cgraphx 2.0.1 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.claude-template/FLOW-MAP.md +7 -5
- package/dist/.claude-template/commands/end.md +1 -1
- package/dist/.claude-template/skills/cgraphx-guide/SKILL.md +2 -1
- package/dist/.claude-template/skills/cgraphx-guide/how-to-use.html +4 -2
- package/dist/.claude-template/skills/cgraphx-guide/how-to-use.md +5 -2
- package/dist/.claude-template/skills/iboc-check/SKILL.md +116 -0
- package/dist/.claude-template/skills/iboc-check/filler-prompt.md +75 -0
- package/dist/.claude-template/skills/implementation/SKILL.md +7 -0
- package/dist/.claude-template/skills/init-project-guides/SKILL.md +1 -1
- package/dist/.claude-template/skills/run-api-test/references/db-verification.md +2 -2
- package/dist/.claude-template/skills/subagent-implement/SKILL.md +3 -2
- package/dist/.claude-template/skills/write-api/assets/template-request.bru +3 -3
- package/dist/.claude-template/skills/write-api/references/bru-format.md +6 -6
- package/dist/.claude-template/skills/write-delivery-guide/SKILL.md +97 -0
- package/dist/.claude-template/skills/write-delivery-guide/template-change-type.md +120 -0
- package/dist/.claude-template/skills/write-delivery-guide/template-multi-role-flow.md +148 -0
- package/dist/.claude-template/skills/write-unit-test-code/SKILL.md +23 -6
- package/dist/core/code/engine/cli/analyze-config.js +320 -0
- package/dist/core/code/engine/cli/analyze.js +1086 -0
- package/dist/core/code/engine/cli/cli-message.js +83 -0
- package/dist/core/code/engine/cli/detect-changes-format.js +58 -0
- package/dist/core/code/engine/cli/embedding-dims.js +43 -0
- package/dist/core/code/engine/cli/format-elapsed.js +12 -0
- package/dist/core/code/engine/cli/help-i18n.js +144 -0
- package/dist/core/code/engine/cli/i18n/en.js +110 -0
- package/dist/core/code/engine/cli/i18n/index.js +47 -0
- package/dist/core/code/engine/cli/i18n/resources.js +9 -0
- package/dist/core/code/engine/cli/i18n/zh-CN.js +110 -0
- package/dist/core/code/engine/cli/lazy-action.js +67 -0
- package/dist/core/code/engine/cli/optional-grammars.js +136 -0
- package/dist/core/code/engine/cli/resolve-invocation.js +76 -0
- package/dist/core/code/engine/cli/status.js +156 -0
- package/dist/core/code/engine/cli/tool.js +384 -0
- package/dist/core/code/engine/config/ignore-service.js +511 -0
- package/dist/core/code/engine/config/supported-languages.js +17 -0
- package/dist/core/code/engine/core/analysis-features.js +64 -0
- package/dist/core/code/engine/core/analyzer-identity.js +2171 -0
- package/dist/core/code/engine/core/git-staleness.js +180 -0
- package/dist/core/code/engine/core/graph/graph.js +180 -0
- package/dist/core/code/engine/core/graph/import-cycles.js +106 -0
- package/dist/core/code/engine/core/graph/types.js +2 -0
- package/dist/core/code/engine/core/index-freshness.js +12 -0
- package/dist/core/code/engine/core/ingestion/binding-accumulator.js +341 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/c-cpp.js +168 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/csharp.js +9 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/dart.js +8 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/go.js +8 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/jvm.js +54 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/php.js +8 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/python.js +8 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/ruby.js +8 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/rust.js +8 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/swift.js +8 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/configs/typescript-javascript.js +11 -0
- package/dist/core/code/engine/core/ingestion/call-extractors/generic.js +62 -0
- package/dist/core/code/engine/core/ingestion/call-processor.js +503 -0
- package/dist/core/code/engine/core/ingestion/call-routing.js +98 -0
- package/dist/core/code/engine/core/ingestion/call-types.js +3 -0
- package/dist/core/code/engine/core/ingestion/cfg/callee-cell-format.js +45 -0
- package/dist/core/code/engine/core/ingestion/cfg/cfg-builder.js +202 -0
- package/dist/core/code/engine/core/ingestion/cfg/collect.js +81 -0
- package/dist/core/code/engine/core/ingestion/cfg/control-dependence.js +185 -0
- package/dist/core/code/engine/core/ingestion/cfg/control-flow-context.js +130 -0
- package/dist/core/code/engine/core/ingestion/cfg/emit.js +646 -0
- package/dist/core/code/engine/core/ingestion/cfg/post-dominators.js +182 -0
- package/dist/core/code/engine/core/ingestion/cfg/reaching-def-reason-codec.js +139 -0
- package/dist/core/code/engine/core/ingestion/cfg/reaching-defs-graph.js +322 -0
- package/dist/core/code/engine/core/ingestion/cfg/reaching-defs.js +792 -0
- package/dist/core/code/engine/core/ingestion/cfg/synthetic-escape.js +305 -0
- package/dist/core/code/engine/core/ingestion/cfg/traversal-result.js +6 -0
- package/dist/core/code/engine/core/ingestion/cfg/types.js +14 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/c-cpp-harvest.js +545 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/c-cpp.js +590 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/call-site-harvest.js +356 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/csharp-harvest.js +593 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/csharp.js +871 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/dart-harvest.js +874 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/dart.js +840 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/go-harvest.js +625 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/go.js +642 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/java-harvest.js +517 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/java.js +816 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/kotlin-harvest.js +723 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/kotlin.js +813 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/php-harvest.js +630 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/php.js +725 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/python-harvest.js +776 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/python.js +562 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/ruby-harvest.js +591 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/ruby.js +760 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/rust-harvest.js +877 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/rust.js +562 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/scope-tree-harvest.js +120 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/swift-harvest.js +683 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/swift.js +791 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/typescript-harvest.js +1060 -0
- package/dist/core/code/engine/core/ingestion/cfg/visitors/typescript.js +587 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/c-cpp.js +77 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/csharp.js +24 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/dart.js +10 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/go.js +28 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/jvm.js +67 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/php.js +10 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/python.js +10 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/ruby.js +13 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/rust.js +10 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/swift.js +21 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/configs/typescript-javascript.js +31 -0
- package/dist/core/code/engine/core/ingestion/class-extractors/generic.js +144 -0
- package/dist/core/code/engine/core/ingestion/class-types.js +2 -0
- package/dist/core/code/engine/core/ingestion/cluster-enricher.js +174 -0
- package/dist/core/code/engine/core/ingestion/community-processor.js +604 -0
- package/dist/core/code/engine/core/ingestion/constants.js +26 -0
- package/dist/core/code/engine/core/ingestion/cpp-ue-preprocessor.js +263 -0
- package/dist/core/code/engine/core/ingestion/csharp-namespace-gate.js +133 -0
- package/dist/core/code/engine/core/ingestion/di-extractors/index.js +38 -0
- package/dist/core/code/engine/core/ingestion/di-extractors/spring.js +310 -0
- package/dist/core/code/engine/core/ingestion/emit-references.js +244 -0
- package/dist/core/code/engine/core/ingestion/entry-point-scoring.js +201 -0
- package/dist/core/code/engine/core/ingestion/export-detection.js +244 -0
- package/dist/core/code/engine/core/ingestion/field-extractor.js +29 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/c-cpp.js +107 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/csharp.js +124 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/dart.js +99 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/go.js +102 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/helpers.js +198 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/jvm.js +172 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/php.js +67 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/python.js +94 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/ruby.js +79 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/rust.js +55 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/swift.js +93 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/configs/typescript-javascript.js +59 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/generic.js +147 -0
- package/dist/core/code/engine/core/ingestion/field-extractors/typescript.js +266 -0
- package/dist/core/code/engine/core/ingestion/field-types.js +3 -0
- package/dist/core/code/engine/core/ingestion/filesystem-walker.js +136 -0
- package/dist/core/code/engine/core/ingestion/finalize-orchestrator.js +159 -0
- package/dist/core/code/engine/core/ingestion/framework-detection.js +432 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/analysis-features.js +38 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/annotation-arguments.js +234 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/aop-candidates.js +88 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/aop.js +487 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/auto-configuration.js +21 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/bean-candidates.js +189 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/bean-catalog.js +32 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/bean-factories.js +73 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/conditionals.js +323 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/config-bindings.js +119 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/di-metadata.js +385 -0
- package/dist/core/code/engine/core/ingestion/frameworks/spring/resource-injection.js +96 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/c-cpp.js +17 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/csharp.js +46 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/dart.js +59 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/go.js +30 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/jvm.js +73 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/php.js +19 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/python.js +45 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/ruby.js +20 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/rust.js +58 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/swift.js +94 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/configs/typescript-javascript.js +26 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/csharp.js +128 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/go.js +50 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/jvm.js +112 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/php.js +80 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/python.js +75 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/resolver-factory.js +36 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/ruby.js +20 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/rust.js +79 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/standard.js +180 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/types.js +7 -0
- package/dist/core/code/engine/core/ingestion/import-resolvers/utils.js +153 -0
- package/dist/core/code/engine/core/ingestion/import-target-adapter.js +99 -0
- package/dist/core/code/engine/core/ingestion/language-config.js +391 -0
- package/dist/core/code/engine/core/ingestion/language-provider.js +25 -0
- package/dist/core/code/engine/core/ingestion/languages/c/arity-metadata.js +98 -0
- package/dist/core/code/engine/core/ingestion/languages/c/arity.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/c/capture-side-channel.js +69 -0
- package/dist/core/code/engine/core/ingestion/languages/c/captures.js +189 -0
- package/dist/core/code/engine/core/ingestion/languages/c/header-scan.js +58 -0
- package/dist/core/code/engine/core/ingestion/languages/c/import-decomposer.js +68 -0
- package/dist/core/code/engine/core/ingestion/languages/c/import-target.js +103 -0
- package/dist/core/code/engine/core/ingestion/languages/c/index.js +33 -0
- package/dist/core/code/engine/core/ingestion/languages/c/interpret.js +53 -0
- package/dist/core/code/engine/core/ingestion/languages/c/merge-bindings.js +26 -0
- package/dist/core/code/engine/core/ingestion/languages/c/query.js +210 -0
- package/dist/core/code/engine/core/ingestion/languages/c/scope-resolver.js +112 -0
- package/dist/core/code/engine/core/ingestion/languages/c/simple-hooks.js +24 -0
- package/dist/core/code/engine/core/ingestion/languages/c/static-linkage.js +109 -0
- package/dist/core/code/engine/core/ingestion/languages/c-cpp.js +506 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/adl.js +804 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/arity-metadata.js +258 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/arity.js +37 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/capture-side-channel.js +89 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/captures.js +1975 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/constraint-extractor.js +311 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/constraint-filter.js +210 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/conversion-rank.js +163 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/file-local-linkage.js +327 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/header-scan.js +53 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/import-decomposer.js +134 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/import-target.js +16 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/index.js +33 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/inline-namespaces.js +379 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/interpret.js +239 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/member-lookup.js +470 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/merge-bindings.js +32 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/query.js +763 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/range-bindings.js +230 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/scope-resolver.js +354 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/simple-hooks.js +67 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/two-phase-lookup.js +348 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/type-classifier.js +56 -0
- package/dist/core/code/engine/core/ingestion/languages/cpp/user-defined-conversions.js +128 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/accessor-unwrap.js +67 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/arity-metadata.js +49 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/arity.js +40 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/cache-stats.js +32 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/captures.js +557 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/import-decomposer.js +96 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/import-target.js +176 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/index.js +95 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/interpret.js +150 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/merge-bindings.js +58 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/namespace-siblings.js +708 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/qualified-type-names.js +62 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/query.js +578 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/receiver-binding.js +142 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/resolution-config.js +18 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/scope-resolver.js +84 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp/simple-hooks.js +81 -0
- package/dist/core/code/engine/core/ingestion/languages/csharp.js +204 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/arity-metadata.js +38 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/arity.js +34 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/built-ins.js +37 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/cache-stats.js +30 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/captures.js +1096 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/expand-wildcards.js +34 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/extension-type-preprocess.js +33 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/import-target.js +68 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/index.js +45 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/interpret.js +101 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/merge-bindings.js +42 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/query.js +246 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/receiver-binding.js +90 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/scope-resolver.js +197 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/signature-bindings.js +54 -0
- package/dist/core/code/engine/core/ingestion/languages/dart/simple-hooks.js +61 -0
- package/dist/core/code/engine/core/ingestion/languages/dart.js +138 -0
- package/dist/core/code/engine/core/ingestion/languages/go/arity-metadata.js +71 -0
- package/dist/core/code/engine/core/ingestion/languages/go/arity.js +17 -0
- package/dist/core/code/engine/core/ingestion/languages/go/cache-stats.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/go/captures.js +492 -0
- package/dist/core/code/engine/core/ingestion/languages/go/expand-wildcards.js +97 -0
- package/dist/core/code/engine/core/ingestion/languages/go/generic-type-parameters.js +146 -0
- package/dist/core/code/engine/core/ingestion/languages/go/import-decomposer.js +47 -0
- package/dist/core/code/engine/core/ingestion/languages/go/import-target.js +70 -0
- package/dist/core/code/engine/core/ingestion/languages/go/index.js +39 -0
- package/dist/core/code/engine/core/ingestion/languages/go/interface-impls.js +955 -0
- package/dist/core/code/engine/core/ingestion/languages/go/interpret.js +177 -0
- package/dist/core/code/engine/core/ingestion/languages/go/merge-bindings.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/go/method-owners.js +131 -0
- package/dist/core/code/engine/core/ingestion/languages/go/namespace-mirror.js +56 -0
- package/dist/core/code/engine/core/ingestion/languages/go/package-clause.js +79 -0
- package/dist/core/code/engine/core/ingestion/languages/go/package-siblings.js +83 -0
- package/dist/core/code/engine/core/ingestion/languages/go/query.js +298 -0
- package/dist/core/code/engine/core/ingestion/languages/go/range-binding.js +127 -0
- package/dist/core/code/engine/core/ingestion/languages/go/receiver-binding.js +24 -0
- package/dist/core/code/engine/core/ingestion/languages/go/scope-resolver.js +75 -0
- package/dist/core/code/engine/core/ingestion/languages/go/simple-hooks.js +31 -0
- package/dist/core/code/engine/core/ingestion/languages/go/type-binding.js +279 -0
- package/dist/core/code/engine/core/ingestion/languages/go.js +160 -0
- package/dist/core/code/engine/core/ingestion/languages/index.js +66 -0
- package/dist/core/code/engine/core/ingestion/languages/java/analysis-features.js +16 -0
- package/dist/core/code/engine/core/ingestion/languages/java/arity-metadata.js +43 -0
- package/dist/core/code/engine/core/ingestion/languages/java/arity.js +27 -0
- package/dist/core/code/engine/core/ingestion/languages/java/cache-stats.js +32 -0
- package/dist/core/code/engine/core/ingestion/languages/java/capture-side-channel.js +123 -0
- package/dist/core/code/engine/core/ingestion/languages/java/captures.js +791 -0
- package/dist/core/code/engine/core/ingestion/languages/java/import-decomposer.js +88 -0
- package/dist/core/code/engine/core/ingestion/languages/java/import-target.js +103 -0
- package/dist/core/code/engine/core/ingestion/languages/java/index.js +43 -0
- package/dist/core/code/engine/core/ingestion/languages/java/interpret.js +146 -0
- package/dist/core/code/engine/core/ingestion/languages/java/merge-bindings.js +43 -0
- package/dist/core/code/engine/core/ingestion/languages/java/package-facts.js +16 -0
- package/dist/core/code/engine/core/ingestion/languages/java/package-siblings.js +11 -0
- package/dist/core/code/engine/core/ingestion/languages/java/query.js +319 -0
- package/dist/core/code/engine/core/ingestion/languages/java/receiver-binding.js +98 -0
- package/dist/core/code/engine/core/ingestion/languages/java/scope-resolver.js +213 -0
- package/dist/core/code/engine/core/ingestion/languages/java/simple-hooks.js +39 -0
- package/dist/core/code/engine/core/ingestion/languages/java/spring-aop.js +53 -0
- package/dist/core/code/engine/core/ingestion/languages/java/spring-bean-metadata.js +11 -0
- package/dist/core/code/engine/core/ingestion/languages/java/spring-conditionals.js +52 -0
- package/dist/core/code/engine/core/ingestion/languages/java/spring-config-bindings.js +222 -0
- package/dist/core/code/engine/core/ingestion/languages/java/spring-di.js +165 -0
- package/dist/core/code/engine/core/ingestion/languages/java.js +192 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/arity.js +15 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/captures.js +1122 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/import-target.js +56 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/index.js +109 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/interpret.js +45 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/merge-bindings.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/query.js +659 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/scope-resolver.js +78 -0
- package/dist/core/code/engine/core/ingestion/languages/javascript/simple-hooks.js +44 -0
- package/dist/core/code/engine/core/ingestion/languages/jvm/package-facts.js +46 -0
- package/dist/core/code/engine/core/ingestion/languages/jvm/package-siblings.js +200 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/arity-metadata.js +23 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/arity.js +18 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/cache-stats.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/capture-side-channel.js +160 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/captures.js +1262 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/companion-scopes.js +72 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/import-decomposer.js +40 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/import-target.js +135 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/index.js +26 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/interpret.js +75 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/merge-bindings.js +28 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/owners.js +134 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/package-facts.js +16 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/package-siblings.js +11 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/query.js +243 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/receiver-binding.js +103 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/scope-resolver.js +207 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/simple-hooks.js +42 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/spring-aop.js +68 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/spring-bean-metadata.js +11 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/spring-conditionals.js +53 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin/spring-di.js +304 -0
- package/dist/core/code/engine/core/ingestion/languages/kotlin.js +189 -0
- package/dist/core/code/engine/core/ingestion/languages/php/arity-metadata.js +66 -0
- package/dist/core/code/engine/core/ingestion/languages/php/arity.js +43 -0
- package/dist/core/code/engine/core/ingestion/languages/php/cache-stats.js +32 -0
- package/dist/core/code/engine/core/ingestion/languages/php/captures.js +1166 -0
- package/dist/core/code/engine/core/ingestion/languages/php/import-decomposer.js +238 -0
- package/dist/core/code/engine/core/ingestion/languages/php/import-target.js +213 -0
- package/dist/core/code/engine/core/ingestion/languages/php/index.js +85 -0
- package/dist/core/code/engine/core/ingestion/languages/php/interpret.js +256 -0
- package/dist/core/code/engine/core/ingestion/languages/php/merge-bindings.js +50 -0
- package/dist/core/code/engine/core/ingestion/languages/php/namespace-siblings.js +353 -0
- package/dist/core/code/engine/core/ingestion/languages/php/query.js +391 -0
- package/dist/core/code/engine/core/ingestion/languages/php/receiver-binding.js +135 -0
- package/dist/core/code/engine/core/ingestion/languages/php/scope-resolver.js +361 -0
- package/dist/core/code/engine/core/ingestion/languages/php/simple-hooks.js +116 -0
- package/dist/core/code/engine/core/ingestion/languages/php.js +302 -0
- package/dist/core/code/engine/core/ingestion/languages/python/arity-metadata.js +49 -0
- package/dist/core/code/engine/core/ingestion/languages/python/arity.js +41 -0
- package/dist/core/code/engine/core/ingestion/languages/python/cache-stats.js +34 -0
- package/dist/core/code/engine/core/ingestion/languages/python/captures.js +299 -0
- package/dist/core/code/engine/core/ingestion/languages/python/depends-references.js +68 -0
- package/dist/core/code/engine/core/ingestion/languages/python/import-decomposer.js +115 -0
- package/dist/core/code/engine/core/ingestion/languages/python/import-target.js +440 -0
- package/dist/core/code/engine/core/ingestion/languages/python/index-stats.js +30 -0
- package/dist/core/code/engine/core/ingestion/languages/python/index.js +96 -0
- package/dist/core/code/engine/core/ingestion/languages/python/interpret.js +430 -0
- package/dist/core/code/engine/core/ingestion/languages/python/merge-bindings.js +47 -0
- package/dist/core/code/engine/core/ingestion/languages/python/query.js +323 -0
- package/dist/core/code/engine/core/ingestion/languages/python/receiver-binding.js +310 -0
- package/dist/core/code/engine/core/ingestion/languages/python/scope-resolver.js +80 -0
- package/dist/core/code/engine/core/ingestion/languages/python/simple-hooks.js +53 -0
- package/dist/core/code/engine/core/ingestion/languages/python.js +140 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/arity.js +41 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/cache-stats.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/captures.js +864 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/import-target.js +88 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/index.js +29 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/interpret.js +115 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/merge-bindings.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/query.js +349 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/receiver-binding.js +70 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/scope-resolver.js +263 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby/simple-hooks.js +68 -0
- package/dist/core/code/engine/core/ingestion/languages/ruby.js +215 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/arity.js +16 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/cache-stats.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/captures.js +304 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/import-decomposer.js +167 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/import-target.js +108 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/index.js +29 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/interpret.js +201 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/merge-bindings.js +21 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/method-owners.js +76 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/module-path.js +222 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/qualified-call.js +482 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/query.js +280 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/range-binding.js +687 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/receiver-binding.js +148 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/scope-resolver.js +151 -0
- package/dist/core/code/engine/core/ingestion/languages/rust/simple-hooks.js +32 -0
- package/dist/core/code/engine/core/ingestion/languages/rust.js +183 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/arity-metadata.js +44 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/arity.js +45 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/base-type.js +30 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/cache-stats.js +32 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/captures.js +594 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/conditional-directive-preprocess.js +256 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/implicit-imports.js +60 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/import-decomposer.js +87 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/import-target.js +84 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/index.js +56 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/interpret.js +93 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/merge-bindings.js +51 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/query.js +226 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/receiver-binding.js +169 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/scope-resolver.js +192 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/sibling-type-bindings.js +68 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/signature-bindings.js +69 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/simple-hooks.js +65 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/target-grouping.js +97 -0
- package/dist/core/code/engine/core/ingestion/languages/swift/target-siblings.js +74 -0
- package/dist/core/code/engine/core/ingestion/languages/swift.js +246 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/arity-metadata.js +106 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/arity.js +57 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/array-callback.js +58 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/cache-stats.js +34 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/captures.js +956 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/cjs-export-assignment.js +535 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/cjs-module-exports.js +196 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/import-decomposer.js +374 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/import-target.js +65 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/index.js +108 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/interpret.js +344 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/merge-bindings.js +161 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/nuxt-auto-imports.js +325 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/query.js +1328 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/receiver-binding.js +201 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/scope-resolver.js +293 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript/simple-hooks.js +139 -0
- package/dist/core/code/engine/core/ingestion/languages/typescript.js +455 -0
- package/dist/core/code/engine/core/ingestion/languages/vue/captures.js +70 -0
- package/dist/core/code/engine/core/ingestion/languages/vue/import-target.js +61 -0
- package/dist/core/code/engine/core/ingestion/languages/vue/index.js +55 -0
- package/dist/core/code/engine/core/ingestion/languages/vue/scope-resolver.js +295 -0
- package/dist/core/code/engine/core/ingestion/languages/vue.js +96 -0
- package/dist/core/code/engine/core/ingestion/local-symbol-pruner.js +68 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/c-cpp.js +387 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/csharp.js +290 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/dart.js +392 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/go.js +179 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/jvm.js +350 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/php.js +306 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/python.js +312 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/ruby.js +289 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/rust.js +198 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/swift.js +286 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/configs/typescript-javascript.js +341 -0
- package/dist/core/code/engine/core/ingestion/method-extractors/generic.js +209 -0
- package/dist/core/code/engine/core/ingestion/method-types.js +3 -0
- package/dist/core/code/engine/core/ingestion/model/field-registry.js +41 -0
- package/dist/core/code/engine/core/ingestion/model/index.js +52 -0
- package/dist/core/code/engine/core/ingestion/model/method-registry.js +138 -0
- package/dist/core/code/engine/core/ingestion/model/owned-members-lookup.js +46 -0
- package/dist/core/code/engine/core/ingestion/model/registration-table.js +234 -0
- package/dist/core/code/engine/core/ingestion/model/resolve.js +183 -0
- package/dist/core/code/engine/core/ingestion/model/scope-resolution-indexes.js +43 -0
- package/dist/core/code/engine/core/ingestion/model/semantic-model.js +179 -0
- package/dist/core/code/engine/core/ingestion/model/symbol-table.js +216 -0
- package/dist/core/code/engine/core/ingestion/model/type-registry.js +84 -0
- package/dist/core/code/engine/core/ingestion/mro-processor.js +709 -0
- package/dist/core/code/engine/core/ingestion/parsing-processor.js +312 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/communities.js +69 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/cross-file.js +71 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/di.js +338 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/http-api-calls.js +312 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/index.js +54 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/mro.js +40 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/orm.js +78 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/parse-impl.js +1286 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/parse.js +41 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/processes.js +193 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/prune-local-symbols.js +29 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/registry.js +52 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/routes.js +409 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/rpc-edges.js +301 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/runner.js +207 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/scan.js +49 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/spring-aop.js +442 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/spring-auto-configuration.js +264 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/spring-config.js +440 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/structure.js +38 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/tools.js +89 -0
- package/dist/core/code/engine/core/ingestion/pipeline-phases/types.js +40 -0
- package/dist/core/code/engine/core/ingestion/pipeline.js +152 -0
- package/dist/core/code/engine/core/ingestion/process-processor.js +325 -0
- package/dist/core/code/engine/core/ingestion/resolve-references.js +205 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/constant-resolver.js +135 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/django-root-discovery.js +221 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/django.js +428 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/expo.js +39 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/fastapi-router-bindings.js +264 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/laravel.js +501 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/middleware.js +175 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/nextjs.js +81 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/php.js +25 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/python-const-resolver.js +307 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/response-shapes.js +299 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/route-path.js +71 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/spring-shared.js +310 -0
- package/dist/core/code/engine/core/ingestion/route-extractors/spring.js +441 -0
- package/dist/core/code/engine/core/ingestion/scope-extractor-bridge.js +60 -0
- package/dist/core/code/engine/core/ingestion/scope-extractor.js +1373 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/contract/scope-resolver.js +282 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.js +72 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/edges.js +194 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/ids.js +472 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.js +49 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/method-dispatch.js +43 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +274 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +93 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/passes/callable-value-flow.js +1240 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/passes/compound-receiver.js +1174 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/passes/free-call-fallback.js +873 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/passes/imported-return-types.js +226 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/passes/mro.js +107 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/passes/overload-narrowing.js +441 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/passes/property-dispatch.js +122 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +1720 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/phase.js +395 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +208 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/registry.js +49 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/run.js +632 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/validate-bindings-immutability.js +112 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/resolution-outcome.js +41 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/scope/namespace-targets.js +81 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/scope/walkers.js +1835 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/unresolved-receivers.js +242 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/utils/definition-id.js +22 -0
- package/dist/core/code/engine/core/ingestion/scope-resolution/workspace-index.js +151 -0
- package/dist/core/code/engine/core/ingestion/structure-processor.js +40 -0
- package/dist/core/code/engine/core/ingestion/tree-sitter-queries.js +2244 -0
- package/dist/core/code/engine/core/ingestion/ts-js-hoc-utils.js +115 -0
- package/dist/core/code/engine/core/ingestion/type-env.js +1136 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/c-cpp.js +555 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/csharp.js +570 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/dart.js +372 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/go.js +508 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/jvm.js +875 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/php.js +537 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/python.js +477 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/ruby.js +380 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/rust.js +502 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/shared.js +843 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/swift.js +490 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/types.js +2 -0
- package/dist/core/code/engine/core/ingestion/type-extractors/typescript.js +690 -0
- package/dist/core/code/engine/core/ingestion/utils/ast-helpers.js +1693 -0
- package/dist/core/code/engine/core/ingestion/utils/call-analysis.js +779 -0
- package/dist/core/code/engine/core/ingestion/utils/callable-flow-captures.js +932 -0
- package/dist/core/code/engine/core/ingestion/utils/callable-labels.js +49 -0
- package/dist/core/code/engine/core/ingestion/utils/deferred-resolution-profile.js +151 -0
- package/dist/core/code/engine/core/ingestion/utils/effective-ram.js +67 -0
- package/dist/core/code/engine/core/ingestion/utils/env.js +60 -0
- package/dist/core/code/engine/core/ingestion/utils/event-loop.js +9 -0
- package/dist/core/code/engine/core/ingestion/utils/graph-sort.js +103 -0
- package/dist/core/code/engine/core/ingestion/utils/heap-probe.js +45 -0
- package/dist/core/code/engine/core/ingestion/utils/heritage-marker.js +47 -0
- package/dist/core/code/engine/core/ingestion/utils/line-base.js +24 -0
- package/dist/core/code/engine/core/ingestion/utils/max-file-size.js +59 -0
- package/dist/core/code/engine/core/ingestion/utils/method-props.js +198 -0
- package/dist/core/code/engine/core/ingestion/utils/qualified-name.js +73 -0
- package/dist/core/code/engine/core/ingestion/utils/receiver-chain-captures.js +60 -0
- package/dist/core/code/engine/core/ingestion/utils/receiver-chain-codec.js +188 -0
- package/dist/core/code/engine/core/ingestion/utils/scope-tree-walk.js +36 -0
- package/dist/core/code/engine/core/ingestion/utils/symbol-labels.js +48 -0
- package/dist/core/code/engine/core/ingestion/utils/template-arguments.js +187 -0
- package/dist/core/code/engine/core/ingestion/utils/type-parameters.js +209 -0
- package/dist/core/code/engine/core/ingestion/utils/verbose.js +6 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/c-cpp.js +133 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/csharp.js +66 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/dart.js +111 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/go.js +153 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/jvm.js +145 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/php.js +61 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/python.js +104 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/ruby.js +55 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/rust.js +79 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/swift.js +91 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/configs/typescript-javascript.js +86 -0
- package/dist/core/code/engine/core/ingestion/variable-extractors/generic.js +111 -0
- package/dist/core/code/engine/core/ingestion/variable-types.js +3 -0
- package/dist/core/code/engine/core/ingestion/vue-sfc-extractor.js +544 -0
- package/dist/core/code/engine/core/ingestion/workers/callable-id.js +122 -0
- package/dist/core/code/engine/core/ingestion/workers/clone-safety.js +470 -0
- package/dist/core/code/engine/core/ingestion/workers/parse-worker.js +2456 -0
- package/dist/core/code/engine/core/ingestion/workers/post-result.js +90 -0
- package/dist/core/code/engine/core/ingestion/workers/quarantine.js +41 -0
- package/dist/core/code/engine/core/ingestion/workers/result-merge.js +59 -0
- package/dist/core/code/engine/core/ingestion/workers/worker-pool.js +1725 -0
- package/dist/core/code/engine/core/ingestion/workspace-config.js +139 -0
- package/dist/core/code/engine/core/lbug/conn-lock.js +72 -0
- package/dist/core/code/engine/core/lbug/csv-generator.js +710 -0
- package/dist/core/code/engine/core/lbug/cypher-escape.js +24 -0
- package/dist/core/code/engine/core/lbug/extension-load-error.js +339 -0
- package/dist/core/code/engine/core/lbug/extension-loader.js +261 -0
- package/dist/core/code/engine/core/lbug/graph-emit-sink.js +584 -0
- package/dist/core/code/engine/core/lbug/lbug-adapter.js +2703 -0
- package/dist/core/code/engine/core/lbug/lbug-config.js +1029 -0
- package/dist/core/code/engine/core/lbug/native-check.js +435 -0
- package/dist/core/code/engine/core/lbug/pool-adapter.js +973 -0
- package/dist/core/code/engine/core/lbug/query-params.js +25 -0
- package/dist/core/code/engine/core/lbug/query-result-utils.js +31 -0
- package/dist/core/code/engine/core/lbug/rel-pair-routing.js +373 -0
- package/dist/core/code/engine/core/lbug/schema.js +771 -0
- package/dist/core/code/engine/core/lbug/shutdown-helpers.js +40 -0
- package/dist/core/code/engine/core/lbug/sidecar-recovery.js +716 -0
- package/dist/core/code/engine/core/lbug/stdio-capture.js +49 -0
- package/dist/core/code/engine/core/lbug/sync-csv-writer.js +115 -0
- package/dist/core/code/engine/core/lbug/wal-checkpoint-driver.js +215 -0
- package/dist/core/code/engine/core/lbug/wal-driver-state.js +28 -0
- package/dist/core/code/engine/core/logger.js +339 -0
- package/dist/core/code/engine/core/platform/capabilities.js +91 -0
- package/dist/core/code/engine/core/run-analyze.js +1098 -0
- package/dist/core/code/engine/core/tree-sitter/parser-loader.js +281 -0
- package/dist/core/code/engine/core/tree-sitter/safe-parse.js +258 -0
- package/dist/core/code/engine/core/tree-sitter/vendored-grammars.js +64 -0
- package/dist/core/code/engine/lib/utils.js +121 -0
- package/dist/core/code/engine/mcp/core/lbug-adapter.js +27 -0
- package/dist/core/code/engine/mcp/local/aop-metadata.js +230 -0
- package/dist/core/code/engine/mcp/local/bean-metadata.js +49 -0
- package/dist/core/code/engine/mcp/local/limits.js +15 -0
- package/dist/core/code/engine/mcp/local/line-display.js +6 -0
- package/dist/core/code/engine/mcp/local/local-backend.js +4142 -0
- package/dist/core/code/engine/storage/branch-index.js +72 -0
- package/dist/core/code/engine/storage/file-hash.js +95 -0
- package/dist/core/code/engine/storage/fs-atomic.js +34 -0
- package/dist/core/code/engine/storage/git.js +555 -0
- package/dist/core/code/engine/storage/index-lock.js +664 -0
- package/dist/core/code/engine/storage/parse-cache.js +650 -0
- package/dist/core/code/engine/storage/parsedfile-store.js +620 -0
- package/dist/core/code/engine/storage/repo-manager.js +1061 -0
- package/dist/core/code/engine/storage/scope-index-store.js +247 -0
- package/dist/core/code/engine/types/pipeline.js +2 -0
- package/dist/core/code/scripts/install-duckdb-extension.mjs +125 -0
- package/dist/core/code/scripts/resolve-analyze-cmd.cjs +346 -0
- package/dist/core/code/shared/graph/types.js +8 -0
- package/dist/core/code/shared/index.js +105 -0
- package/dist/core/code/shared/integrations/circuit-breaker.js +242 -0
- package/dist/core/code/shared/integrations/resilient-fetch.js +224 -0
- package/dist/core/code/shared/integrations/retry.js +70 -0
- package/dist/core/code/shared/integrations/understand-quickly.js +145 -0
- package/dist/core/code/shared/language-detection.js +162 -0
- package/dist/core/code/shared/languages.js +27 -0
- package/dist/core/code/shared/lbug/schema-constants.js +98 -0
- package/dist/core/code/shared/mro-strategy.js +2 -0
- package/dist/core/code/shared/pipeline.js +5 -0
- package/dist/core/code/shared/scope-resolution/callable-flow-site.js +11 -0
- package/dist/core/code/shared/scope-resolution/def-index.js +53 -0
- package/dist/core/code/shared/scope-resolution/evidence-weights.js +87 -0
- package/dist/core/code/shared/scope-resolution/finalize-algorithm.js +807 -0
- package/dist/core/code/shared/scope-resolution/language-classification.js +46 -0
- package/dist/core/code/shared/scope-resolution/method-dispatch-index.js +100 -0
- package/dist/core/code/shared/scope-resolution/module-scope-index.js +59 -0
- package/dist/core/code/shared/scope-resolution/origin-priority.js +23 -0
- package/dist/core/code/shared/scope-resolution/parsed-file.js +54 -0
- package/dist/core/code/shared/scope-resolution/position-index.js +136 -0
- package/dist/core/code/shared/scope-resolution/qualified-name-index.js +77 -0
- package/dist/core/code/shared/scope-resolution/reference-site.js +24 -0
- package/dist/core/code/shared/scope-resolution/registries/class-registry.js +32 -0
- package/dist/core/code/shared/scope-resolution/registries/context.js +52 -0
- package/dist/core/code/shared/scope-resolution/registries/evidence.js +152 -0
- package/dist/core/code/shared/scope-resolution/registries/field-registry.js +33 -0
- package/dist/core/code/shared/scope-resolution/registries/lookup-core.js +392 -0
- package/dist/core/code/shared/scope-resolution/registries/lookup-qualified.js +58 -0
- package/dist/core/code/shared/scope-resolution/registries/macro-registry.js +34 -0
- package/dist/core/code/shared/scope-resolution/registries/method-registry.js +34 -0
- package/dist/core/code/shared/scope-resolution/registries/tie-breaks.js +63 -0
- package/dist/core/code/shared/scope-resolution/resolve-type-ref.js +128 -0
- package/dist/core/code/shared/scope-resolution/scope-id.js +49 -0
- package/dist/core/code/shared/scope-resolution/scope-tree.js +225 -0
- package/dist/core/code/shared/scope-resolution/symbol-definition.js +12 -0
- package/dist/core/code/shared/scope-resolution/types.js +25 -0
- package/dist/core/code/shared/test-helpers.js +17 -0
- package/dist/core/code/vendor/leiden/index.cjs +355 -0
- package/dist/core/code/vendor/leiden/utils.cjs +419 -0
- package/dist/core/features/scanner.d.ts +2 -0
- package/dist/core/features/scanner.d.ts.map +1 -1
- package/dist/core/features/scanner.js +27 -6
- package/dist/core/features/scanner.js.map +1 -1
- package/dist/core/timeline/cli.d.ts.map +1 -1
- package/dist/core/timeline/cli.js +13 -6
- package/dist/core/timeline/cli.js.map +1 -1
- package/dist/core/timeline/debris.d.ts +20 -0
- package/dist/core/timeline/debris.d.ts.map +1 -0
- package/dist/core/timeline/debris.js +124 -0
- package/dist/core/timeline/debris.js.map +1 -0
- package/dist/core/timeline/hook-runner.d.ts.map +1 -1
- package/dist/core/timeline/hook-runner.js +3 -1
- package/dist/core/timeline/hook-runner.js.map +1 -1
- package/dist/core/timeline/hooks.d.ts.map +1 -1
- package/dist/core/timeline/hooks.js +13 -5
- package/dist/core/timeline/hooks.js.map +1 -1
- package/dist/core/timeline/installer.d.ts +2 -0
- package/dist/core/timeline/installer.d.ts.map +1 -1
- package/dist/core/timeline/installer.js +12 -0
- package/dist/core/timeline/installer.js.map +1 -1
- package/dist/core/timeline/project-root.d.ts +21 -0
- package/dist/core/timeline/project-root.d.ts.map +1 -0
- package/dist/core/timeline/project-root.js +83 -0
- package/dist/core/timeline/project-root.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,1720 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Receiver-bound CALLS / ACCESSES emit pass — generic 8-case
|
|
4
|
+
* dispatcher consuming `ScopeResolver` for the language-specific bits
|
|
5
|
+
* (super recognizer, field-fallback toggle).
|
|
6
|
+
*
|
|
7
|
+
* **Contract Invariant I4 — case order is load-bearing.** The cases
|
|
8
|
+
* are evaluated in this order; the FIRST that emits an edge wins:
|
|
9
|
+
*
|
|
10
|
+
* 1. **super branch** — `provider.isSuperReceiver(receiverName)` →
|
|
11
|
+
* MRO walk skipping self
|
|
12
|
+
* 2. **Case 0 (compound)** — receiver has `.` or `(` → compound resolver.
|
|
13
|
+
* Also emits the interface-dispatch fan-out when the folded receiver type
|
|
14
|
+
* is an Interface (#2829) — see Case 4, which does the same.
|
|
15
|
+
* 3. **Case 0.5 (implicit `this` receiver)** — GATED: fires only when
|
|
16
|
+
* the language sets `resolveThisViaEnclosingClass === true` AND the
|
|
17
|
+
* receiver is literally `this` → enclosing-class + MRO chain walk
|
|
18
|
+
* with C++ member-name-hiding semantics. Languages that leave the
|
|
19
|
+
* toggle unset skip this case entirely; their `this` sites fall
|
|
20
|
+
* through to Case 4 via the synthesized `this` typeBinding (which
|
|
21
|
+
* emits the interface-dispatch fan-out that this case does not —
|
|
22
|
+
* as do Cases 0 since #2829 and 3b since #2832; Case 0.5 remains
|
|
23
|
+
* the only fold-or-walk case without it).
|
|
24
|
+
* 4. **Case 1 (namespace)** — receiver in `namespaceTargets` → exported def
|
|
25
|
+
* 5. **Case 2 (class-name / static receiver)** — receiver resolves to a
|
|
26
|
+
* class-like binding (Class/Interface/Struct/Record/Enum/Trait) → MRO
|
|
27
|
+
* walk on that class. Also handles static-style invocations
|
|
28
|
+
* (`ILogger.Warn(...)`) with kind-aware reason/confidence for
|
|
29
|
+
* read/write ACCESSES.
|
|
30
|
+
* 6. **Case 3 (dotted typeBinding for namespace prefix)** —
|
|
31
|
+
* `typeRef.rawName` like `models.User`
|
|
32
|
+
* 7. **Case 3b (chain-typebinding)** — `typeRef.rawName` has a dot
|
|
33
|
+
* but not a namespace prefix → compound resolver. Also emits the
|
|
34
|
+
* interface-dispatch fan-out when the folded receiver type is an
|
|
35
|
+
* Interface (#2832) — same call Cases 0 and 4 make.
|
|
36
|
+
* 8. **Case 4 (simple typeBinding)** — `typeRef.rawName` has no dot →
|
|
37
|
+
* MRO walk + `findOwnedMember`
|
|
38
|
+
* 9. **Case 5 (value-receiver bridge)** — receiver is a `Const`/`Variable`
|
|
39
|
+
* whose `nodeId` is referenced as an `ownerId` in `model.methods`
|
|
40
|
+
* (object-literal services). Last-resort fallback for lowercase
|
|
41
|
+
* receivers with no class-like or type-binding match. Mirrors
|
|
42
|
+
* the legacy DAG bridge in `call-processor.ts`.
|
|
43
|
+
* 10. **Case 6 (class-level member receiver)** — `Holder.repo.save(u)`,
|
|
44
|
+
* where the receiver's head is a CLASS and the one hop past it is a
|
|
45
|
+
* class-level (`isStatic`) field. Types the receiver from that field
|
|
46
|
+
* DEF's declared type rather than from a `typeBindings` entry, which
|
|
47
|
+
* is the thing a per-scope binding map cannot hold for a class that
|
|
48
|
+
* declares both a static and an instance member of one name. Gated on
|
|
49
|
+
* Case 0 having declined the same receiver, so it only ever adds an
|
|
50
|
+
* edge where there was none. Emits the interface-dispatch fan-out
|
|
51
|
+
* alongside Cases 0, 3b and 4.
|
|
52
|
+
*
|
|
53
|
+
* Reordering or merging cases changes resolution semantics.
|
|
54
|
+
*
|
|
55
|
+
* **Contract Invariant I5 — pre-seeding `seen` is forbidden.** The
|
|
56
|
+
* orchestrator runs this pass FIRST (before `emitReferencesViaLookup`)
|
|
57
|
+
* and consumes the populated `handledSites` set. Pre-seeding `seen`
|
|
58
|
+
* from the shared resolver's emissions (an old optimization) actively
|
|
59
|
+
* suppresses correct emissions for sites the shared resolver also
|
|
60
|
+
* resolved to a wrong target.
|
|
61
|
+
*/
|
|
62
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
63
|
+
exports.OVERLOAD_AMBIGUOUS = exports.MAX_INTERFACE_DISPATCH_FANOUT = void 0;
|
|
64
|
+
exports.classifyReceiverOrigin = classifyReceiverOrigin;
|
|
65
|
+
exports.emitReceiverBoundCalls = emitReceiverBoundCalls;
|
|
66
|
+
const namespace_targets_js_1 = require("../scope/namespace-targets.js");
|
|
67
|
+
const walkers_js_1 = require("../scope/walkers.js");
|
|
68
|
+
const edges_js_1 = require("../graph-bridge/edges.js");
|
|
69
|
+
const compound_receiver_js_1 = require("../passes/compound-receiver.js");
|
|
70
|
+
const template_arguments_js_1 = require("../../utils/template-arguments.js");
|
|
71
|
+
const ids_js_1 = require("../graph-bridge/ids.js");
|
|
72
|
+
const overload_narrowing_js_1 = require("./overload-narrowing.js");
|
|
73
|
+
const resolution_outcome_js_1 = require("../resolution-outcome.js");
|
|
74
|
+
const receiver_chain_codec_js_1 = require("../../utils/receiver-chain-codec.js");
|
|
75
|
+
/** A bare, undecorated identifier and nothing else — see {@link isBareTypeName}. */
|
|
76
|
+
const BARE_TYPE_NAME_RE = /^[A-Za-z_$][\w$]*$/;
|
|
77
|
+
/**
|
|
78
|
+
* A type name a built-in test may be asked about: a bare, undecorated
|
|
79
|
+
* identifier and nothing else.
|
|
80
|
+
*
|
|
81
|
+
* `Promise<User>`, `[]Repo` and `Option<Repo>` all name a built-in CONTAINER
|
|
82
|
+
* whose ELEMENT is very often in-program, and an await/index/unwrap step is
|
|
83
|
+
* exactly how a receiver chain reaches that element. Answering "external"
|
|
84
|
+
* because the outer spelling matched a built-in would relabel a real in-program
|
|
85
|
+
* drop, which is the failure this whole function exists to stop. A decorated or
|
|
86
|
+
* dotted spelling is likewise not a built-in name, it merely contains one.
|
|
87
|
+
*/
|
|
88
|
+
function isBareTypeName(rawName) {
|
|
89
|
+
return BARE_TYPE_NAME_RE.test(rawName);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Is this dropped receiver rooted inside the analyzed program?
|
|
93
|
+
*
|
|
94
|
+
* Asks of the receiver's BASE — the leftmost name the chain hangs off — what
|
|
95
|
+
* this index can DEMONSTRATE. Three answers, and the asymmetry between them is
|
|
96
|
+
* the whole point:
|
|
97
|
+
*
|
|
98
|
+
* - `in-program` — the base's declared type resolves here, or the base itself is
|
|
99
|
+
* a class, a qualified name, or a value this program declares. A real edge was
|
|
100
|
+
* lost; the hedge must fire.
|
|
101
|
+
* - `external` — POSITIVE evidence that the target is outside: the language
|
|
102
|
+
* itself names the base (or its bare declared type) a built-in. `console.log`,
|
|
103
|
+
* `fetch(...)`, `JSON.stringify` reach code no index contains, so there is no
|
|
104
|
+
* node an edge could have pointed at and nothing was lost.
|
|
105
|
+
* - `unknown` — everything else. An absence of evidence is NOT evidence of
|
|
106
|
+
* externality: an unannotated parameter (`function f(svc) { svc.a().b(); }`)
|
|
107
|
+
* is recorded nowhere in the scope model at all, and calling that "external"
|
|
108
|
+
* published `epistemic: 'exact'` over a genuinely missing in-program caller —
|
|
109
|
+
* strictly worse than hedging, because it is a confident wrong answer rather
|
|
110
|
+
* than an admitted gap. `unknown` counts WITH `in-program` in
|
|
111
|
+
* `summarizeUnresolvedReceivers`, which is the safe direction.
|
|
112
|
+
*
|
|
113
|
+
* Uses the AST-derived chain base when one was minted, and falls back to the
|
|
114
|
+
* head of the receiver text otherwise — never a regex over the source line.
|
|
115
|
+
*
|
|
116
|
+
* Exported for the unit tests that pin the three-way split; the pass is its only
|
|
117
|
+
* production caller.
|
|
118
|
+
*/
|
|
119
|
+
function classifyReceiverOrigin(decoded, inScope, receiverName, scopes, options = {}) {
|
|
120
|
+
// The chain's base is authoritative. Without one, take the head of the
|
|
121
|
+
// receiver text up to the first member/call punctuation.
|
|
122
|
+
const base = decoded?.baseReceiverName ?? /^[A-Za-z_$][\w$]*/.exec(receiverName)?.[0];
|
|
123
|
+
if (base === undefined || base.length === 0)
|
|
124
|
+
return 'unknown';
|
|
125
|
+
const strip = options.stripTypePreservingDecoration;
|
|
126
|
+
const isBuiltIn = options.isBuiltInName;
|
|
127
|
+
// The base's declared TYPE, when it has one, is the strongest signal about
|
|
128
|
+
// where the member lives: `inputs.stream()` has an in-program base bound to
|
|
129
|
+
// `List<String>`, whose `stream` is in the JDK.
|
|
130
|
+
const binding = (0, walkers_js_1.findReceiverTypeBinding)(inScope, base, scopes);
|
|
131
|
+
if (binding !== undefined) {
|
|
132
|
+
// `resolveClassBindingForName`, not a bare lookup: it also strips template
|
|
133
|
+
// arguments, so an in-program generic base (`Box<String> b; b.open()`)
|
|
134
|
+
// resolves instead of being mislabelled and dropped from the hedge.
|
|
135
|
+
if ((0, walkers_js_1.resolveClassBindingForName)(binding.declaredAtScope, binding.rawName, scopes, strip) !==
|
|
136
|
+
undefined) {
|
|
137
|
+
return 'in-program';
|
|
138
|
+
}
|
|
139
|
+
// The declared type is not one this index contains. That is only proof of
|
|
140
|
+
// externality when the language itself names it — otherwise the type merely
|
|
141
|
+
// failed to resolve (an alias, an inferred callable, a generic parameter),
|
|
142
|
+
// and we fall through to ask what the index knows about the base itself.
|
|
143
|
+
if (isBareTypeName(binding.rawName) && isBuiltIn?.(binding.rawName) === true) {
|
|
144
|
+
return 'external';
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Anything else this index knows by that name (namespace, module, free fn).
|
|
148
|
+
// O(1), so it goes ahead of the scope-chain walks below: all three checks are
|
|
149
|
+
// arms of the same `in-program` disjunction and none has a side effect, so
|
|
150
|
+
// answering from the index first is free and changes no verdict.
|
|
151
|
+
if (scopes.qualifiedNames.has(base))
|
|
152
|
+
return 'in-program';
|
|
153
|
+
// A type the program declares, used as a static receiver.
|
|
154
|
+
if ((0, walkers_js_1.findClassBindingInScope)(inScope, base, scopes, strip) !== undefined)
|
|
155
|
+
return 'in-program';
|
|
156
|
+
// A VALUE the program declares — an object-literal service, a local whose
|
|
157
|
+
// initializer we could not type (`const loc = makeIt(); loc.getUser().save()`),
|
|
158
|
+
// a field. The type channel had nothing usable to say about these, but the
|
|
159
|
+
// program demonstrably declares the name, so the lost edge is in-program and
|
|
160
|
+
// failing to type it is a resolver defect. This is the channel Case 5 already
|
|
161
|
+
// dispatches on; consulting it here keeps the diagnostic honest about the
|
|
162
|
+
// same population.
|
|
163
|
+
if ((0, walkers_js_1.findValueBindingInScope)(inScope, base, scopes) !== undefined)
|
|
164
|
+
return 'in-program';
|
|
165
|
+
// Positive external evidence, and the only kind reachable from this pass.
|
|
166
|
+
if (isBuiltIn?.(base) === true)
|
|
167
|
+
return 'external';
|
|
168
|
+
return 'unknown';
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Upper bound on how many implementors ONE interface member may fan out to at a
|
|
172
|
+
* single call site (#2829).
|
|
173
|
+
*
|
|
174
|
+
* Mirrors `MAX_PROPERTY_DISPATCH_FANOUT` in `property-dispatch.ts`, deliberately
|
|
175
|
+
* including its reporting half: a bare cap would silently discard valid dispatch
|
|
176
|
+
* targets, which is the same false-safe silence #2813 was filed about. The
|
|
177
|
+
* default matches that sibling's 32 — the fan-out is a per-call-site product, so
|
|
178
|
+
* an interface with hundreds of implementors (mock proliferation is the usual
|
|
179
|
+
* cause) multiplies the graph without adding information a reader can act on.
|
|
180
|
+
*
|
|
181
|
+
* Override with `CGRAPH_MAX_INTERFACE_DISPATCH_FANOUT` for a repo with
|
|
182
|
+
* legitimately high implementor counts.
|
|
183
|
+
*/
|
|
184
|
+
exports.MAX_INTERFACE_DISPATCH_FANOUT = (() => {
|
|
185
|
+
const env = Number(process.env.CGRAPH_MAX_INTERFACE_DISPATCH_FANOUT);
|
|
186
|
+
return Number.isInteger(env) && env >= 1 ? env : 32;
|
|
187
|
+
})();
|
|
188
|
+
/** Bound on the sample of over-cap interface members kept for the warning. */
|
|
189
|
+
const MAX_REPORTED_SKIPPED_INTERFACES = 20;
|
|
190
|
+
function emitReceiverBoundCalls(graph, scopes, parsedFiles, nodeLookup, handledSites, provider, index, model, options = {}) {
|
|
191
|
+
let emitted = 0;
|
|
192
|
+
let dispatchFanoutSkipped = 0;
|
|
193
|
+
const dispatchFanoutSkippedNames = [];
|
|
194
|
+
// Per-pass dedup so the multiple cases don't double-emit if two of
|
|
195
|
+
// them resolve the same site to the same target. NEVER pre-seed
|
|
196
|
+
// from the reference index — see Contract Invariant I5.
|
|
197
|
+
const seen = new Set();
|
|
198
|
+
const fieldFallback = provider.fieldFallbackOnMethodLookup ?? true;
|
|
199
|
+
const collapse = provider.collapseMemberCallsByCallerTarget === true;
|
|
200
|
+
const hoistTypeBindingsToModule = provider.hoistTypeBindingsToModule === true;
|
|
201
|
+
const compoundOpts = {
|
|
202
|
+
fieldFallback,
|
|
203
|
+
elementTypeOf: provider.elementTypeOf,
|
|
204
|
+
hoistTypeBindingsToModule,
|
|
205
|
+
stripReceiverCastExpressions: provider.stripReceiverCastExpressions === true,
|
|
206
|
+
constructionSyntax: provider.constructionSyntax,
|
|
207
|
+
stripTypePreservingDecoration: provider.stripTypePreservingDecoration,
|
|
208
|
+
resolveThisViaEnclosingClass: provider.resolveThisViaEnclosingClass,
|
|
209
|
+
};
|
|
210
|
+
// Loop-invariant: both hooks come off the pass arguments, so the options bag
|
|
211
|
+
// for `classifyReceiverOrigin` is built once here rather than per dropped site.
|
|
212
|
+
const receiverOriginOpts = {
|
|
213
|
+
stripTypePreservingDecoration: provider.stripTypePreservingDecoration,
|
|
214
|
+
isBuiltInName: options.isBuiltInName,
|
|
215
|
+
};
|
|
216
|
+
// Build an interface → implementors map from IMPLEMENTS edges.
|
|
217
|
+
// Maps Interface graph-id → list of implementor class scope-def-ids.
|
|
218
|
+
// We translate graph-ids back to scope-resolution DefIds via
|
|
219
|
+
// `parsedFiles.localDefs` lookup so downstream `findOwnedMember`
|
|
220
|
+
// (which keys by DefId) can find the implementor's members.
|
|
221
|
+
const graphIdToClassDef = new Map();
|
|
222
|
+
for (const parsed of parsedFiles) {
|
|
223
|
+
for (const def of parsed.localDefs) {
|
|
224
|
+
if (def.type !== 'Class' && def.type !== 'Struct' && def.type !== 'Interface')
|
|
225
|
+
continue;
|
|
226
|
+
const graphId = (0, ids_js_1.resolveDefGraphId)(parsed.filePath, def, nodeLookup);
|
|
227
|
+
if (graphId !== undefined)
|
|
228
|
+
graphIdToClassDef.set(graphId, def);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Direct subtypes of a type, keyed by the SUPERtype's def id.
|
|
232
|
+
//
|
|
233
|
+
// Built from IMPLEMENTS **and** EXTENDS (#2829). IMPLEMENTS alone is not the
|
|
234
|
+
// set of implementations: `preEmitInheritanceEdges` classifies heritage by the
|
|
235
|
+
// TARGET's kind, so `interface B extends A` is stored as `B IMPLEMENTS A` and
|
|
236
|
+
// an INTERFACE lands in A's list; and a concrete class reaches its interface
|
|
237
|
+
// through `class C extends AbstractBase` (EXTENDS) + `AbstractBase implements
|
|
238
|
+
// I` (IMPLEMENTS), so it is two hops away and invisible to a depth-1 walk.
|
|
239
|
+
// Both shapes previously ended the fan-out on a bodiless declaration while the
|
|
240
|
+
// only executable target got no edge at all.
|
|
241
|
+
const subtypesBySupertypeDefId = new Map();
|
|
242
|
+
const addSubtype = (superId, sub) => {
|
|
243
|
+
let list = subtypesBySupertypeDefId.get(superId);
|
|
244
|
+
if (list === undefined) {
|
|
245
|
+
list = [];
|
|
246
|
+
subtypesBySupertypeDefId.set(superId, list);
|
|
247
|
+
}
|
|
248
|
+
list.push(sub);
|
|
249
|
+
};
|
|
250
|
+
for (const relType of ['IMPLEMENTS', 'EXTENDS']) {
|
|
251
|
+
for (const rel of graph.iterRelationshipsByType(relType)) {
|
|
252
|
+
const superDef = graphIdToClassDef.get(rel.targetId);
|
|
253
|
+
const subDef = graphIdToClassDef.get(rel.sourceId);
|
|
254
|
+
if (superDef === undefined || subDef === undefined)
|
|
255
|
+
continue;
|
|
256
|
+
addSubtype(superDef.nodeId, subDef);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Is this member a bodiless DECLARATION rather than an implementation?
|
|
261
|
+
*
|
|
262
|
+
* An interface method, and an `abstract` method on an abstract base, are
|
|
263
|
+
* both declarations: dispatching to them names something with no body while
|
|
264
|
+
* the executable target sits further down the hierarchy. `isAbstract` lives
|
|
265
|
+
* on the graph NODE (the structure phase sets it), not on `SymbolDefinition`,
|
|
266
|
+
* so this resolves the def to its node. A def that cannot be resolved is
|
|
267
|
+
* treated as NOT abstract — the fail-open direction, matching how the rest of
|
|
268
|
+
* this pass treats an unresolvable lookup.
|
|
269
|
+
*/
|
|
270
|
+
const isDeclarationOnly = (def) => {
|
|
271
|
+
const graphId = (0, ids_js_1.resolveDefGraphId)(def.filePath, def, nodeLookup);
|
|
272
|
+
if (graphId === undefined)
|
|
273
|
+
return false;
|
|
274
|
+
return graph.getNode(graphId)?.properties.isAbstract === true;
|
|
275
|
+
};
|
|
276
|
+
/**
|
|
277
|
+
* Can an INSTANCE-typed receiver reach this member? A static member cannot be,
|
|
278
|
+
* ever — `class C implements I { static save() {} }` does not satisfy `I`
|
|
279
|
+
* (TypeScript rejects it outright as TS2420, "Property 'save' is missing"), so
|
|
280
|
+
* an edge to it from an `I`-typed receiver names a target no dispatch can
|
|
281
|
+
* produce. Every comparable tool draws the same line: tsserver partitions
|
|
282
|
+
* static from instance results, clangd gates on `isVirtual()` (C++ forbids
|
|
283
|
+
* virtual statics), jdtls filters abstract-or-static, and class-hierarchy
|
|
284
|
+
* analysis expands only VIRTUAL call sites — a static call already has exactly
|
|
285
|
+
* one target and needs no fan-out.
|
|
286
|
+
*
|
|
287
|
+
* Two sources answer this, and the order matters:
|
|
288
|
+
*
|
|
289
|
+
* 1. `provider.isStaticOnly` when the language declares it. It is the
|
|
290
|
+
* precise answer, because a language that needs the distinction defines
|
|
291
|
+
* it exactly — Kotlin marks only COMPANION-promoted defs, so a Kotlin
|
|
292
|
+
* `object Impl : Iface { override fun handle() }` is correctly kept: an
|
|
293
|
+
* `object` is a singleton INSTANCE and its members really are reachable
|
|
294
|
+
* through an `Iface`-typed receiver.
|
|
295
|
+
* 2. Otherwise the graph node's `isStatic`. For every language that does not
|
|
296
|
+
* declare the hook, that flag comes from the member's own modifier (or,
|
|
297
|
+
* for Ruby, from `singleton_class` — `def self.foo`, which is likewise
|
|
298
|
+
* unreachable through an instance), so it means what we need here.
|
|
299
|
+
*
|
|
300
|
+
* Getting that order wrong is a live regression, not a hypothetical: the
|
|
301
|
+
* method extractor derives `isStatic` from the OWNER type as well as the
|
|
302
|
+
* member (`method-extractors/generic.ts`, `staticOwnerTypes`), and the JVM
|
|
303
|
+
* config lists `object_declaration`. Reading the flag first would delete
|
|
304
|
+
* Kotlin object implementations from the fan-out. A language that needs
|
|
305
|
+
* precision declares the hook; that is the upgrade path.
|
|
306
|
+
*
|
|
307
|
+
* Unresolvable defs fail open (treated as reachable), matching
|
|
308
|
+
* `isDeclarationOnly` and the rest of this pass.
|
|
309
|
+
*/
|
|
310
|
+
const isUnreachableByInstanceDispatch = (def) => {
|
|
311
|
+
const staticOnly = provider.isStaticOnly;
|
|
312
|
+
if (staticOnly !== undefined)
|
|
313
|
+
return staticOnly(def) === true;
|
|
314
|
+
const graphId = (0, ids_js_1.resolveDefGraphId)(def.filePath, def, nodeLookup);
|
|
315
|
+
if (graphId === undefined)
|
|
316
|
+
return false;
|
|
317
|
+
return graph.getNode(graphId)?.properties.isStatic === true;
|
|
318
|
+
};
|
|
319
|
+
/**
|
|
320
|
+
* Emit secondary CALLS edges with reason='interface-dispatch' when the primary
|
|
321
|
+
* receiver-typed edge targeted an Interface's method.
|
|
322
|
+
*
|
|
323
|
+
* Walks the SUBTYPE CLOSURE of the interface rather than its direct
|
|
324
|
+
* implementors (#2829). Two shapes made the depth-1 walk both wrong and
|
|
325
|
+
* incomplete, each reproduced in plain Java:
|
|
326
|
+
*
|
|
327
|
+
* interface ReadCloser extends Reader { int read(String p); } // re-declares
|
|
328
|
+
* abstract class AbstractHandler implements Handler { public abstract void handle(String s); }
|
|
329
|
+
*
|
|
330
|
+
* In both, the depth-1 list contains a type whose `read`/`handle` is a bodiless
|
|
331
|
+
* declaration, so the fan-out emitted an edge to *that* — while the only class
|
|
332
|
+
* with a body (`FileRC`, `RealHandler`) is one hop further down and received
|
|
333
|
+
* nothing at all. Descending and skipping declarations fixes both directions
|
|
334
|
+
* at once: the wrong edge disappears and the real implementation gains one.
|
|
335
|
+
*
|
|
336
|
+
* Descent continues THROUGH a type that supplied a concrete member, because an
|
|
337
|
+
* override further down is an equally real runtime target — dispatch is an
|
|
338
|
+
* over-approximation by design, and stopping early would silently prefer the
|
|
339
|
+
* base.
|
|
340
|
+
*/
|
|
341
|
+
const emitInterfaceDispatchFor = (ownerDef, memberName, primaryMemberDef, site, confidence, calleeCapture) => {
|
|
342
|
+
if (ownerDef.type !== 'Interface')
|
|
343
|
+
return 0;
|
|
344
|
+
if (subtypesBySupertypeDefId.get(ownerDef.nodeId) === undefined)
|
|
345
|
+
return 0;
|
|
346
|
+
// Collect concrete targets across the closure first, so the cap below counts
|
|
347
|
+
// real dispatch targets rather than types visited.
|
|
348
|
+
const targets = [];
|
|
349
|
+
const seenTypes = new Set([ownerDef.nodeId]);
|
|
350
|
+
const queue = [ownerDef.nodeId];
|
|
351
|
+
while (queue.length > 0) {
|
|
352
|
+
const superId = queue.shift();
|
|
353
|
+
for (const subDef of subtypesBySupertypeDefId.get(superId) ?? []) {
|
|
354
|
+
if (seenTypes.has(subDef.nodeId))
|
|
355
|
+
continue;
|
|
356
|
+
seenTypes.add(subDef.nodeId);
|
|
357
|
+
queue.push(subDef.nodeId);
|
|
358
|
+
const implMember = pickOverload(subDef.nodeId, memberName, site, model, provider);
|
|
359
|
+
if (implMember === undefined ||
|
|
360
|
+
implMember === exports.OVERLOAD_AMBIGUOUS ||
|
|
361
|
+
implMember.isDeleted === true) {
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
if (implMember.nodeId === primaryMemberDef.nodeId)
|
|
365
|
+
continue;
|
|
366
|
+
// A re-declared interface method or an `abstract` override is not an
|
|
367
|
+
// implementation — keep descending past it rather than emitting to it.
|
|
368
|
+
if (isDeclarationOnly(implMember))
|
|
369
|
+
continue;
|
|
370
|
+
// Nor is a static member: no instance-typed receiver can reach one, so
|
|
371
|
+
// an edge to it is a target dispatch cannot produce (#2842 review).
|
|
372
|
+
if (isUnreachableByInstanceDispatch(implMember))
|
|
373
|
+
continue;
|
|
374
|
+
targets.push(implMember);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
// Bounded, and NEVER silently (#2829). An interface with a very large
|
|
378
|
+
// implementor set multiplies edges by every call site — Go, TypeScript and
|
|
379
|
+
// Kotlin do not set `collapseMemberCallsByCallerTarget`, so the product is
|
|
380
|
+
// per SITE. Truncating without saying so would recreate the false-safe
|
|
381
|
+
// silence this whole issue is about, which is why the sibling
|
|
382
|
+
// `MAX_PROPERTY_DISPATCH_FANOUT` reports its dropped keys too.
|
|
383
|
+
if (targets.length > exports.MAX_INTERFACE_DISPATCH_FANOUT) {
|
|
384
|
+
dispatchFanoutSkipped += targets.length - exports.MAX_INTERFACE_DISPATCH_FANOUT;
|
|
385
|
+
if (dispatchFanoutSkippedNames.length < MAX_REPORTED_SKIPPED_INTERFACES) {
|
|
386
|
+
dispatchFanoutSkippedNames.push(`${ownerDef.qualifiedName ?? ownerDef.nodeId}.${memberName} (${targets.length} targets)`);
|
|
387
|
+
}
|
|
388
|
+
targets.length = exports.MAX_INTERFACE_DISPATCH_FANOUT;
|
|
389
|
+
}
|
|
390
|
+
let n = 0;
|
|
391
|
+
for (const implMember of targets) {
|
|
392
|
+
const ok = (0, edges_js_1.tryEmitEdge)(graph, scopes, nodeLookup, site, implMember, 'interface-dispatch', seen, confidence, collapse, calleeCapture);
|
|
393
|
+
if (ok)
|
|
394
|
+
n++;
|
|
395
|
+
}
|
|
396
|
+
return n;
|
|
397
|
+
};
|
|
398
|
+
/**
|
|
399
|
+
* Declared type of the CLASS-LEVEL field named `fieldName` on `ownerId`, or
|
|
400
|
+
* `undefined` when the owner declares no such field, declares only an
|
|
401
|
+
* instance one, or declares one whose type was never captured.
|
|
402
|
+
*
|
|
403
|
+
* Both facts live on the graph NODE rather than on `SymbolDefinition` —
|
|
404
|
+
* `isStatic` is set by the structure phase and `declaredType` by the field
|
|
405
|
+
* extractor — which is the same place {@link isUnreachableByInstanceDispatch}
|
|
406
|
+
* reads `isStatic` from, so this introduces no new dependency.
|
|
407
|
+
*
|
|
408
|
+
* `isStatic === true` is required, not merely preferred. The receiver that
|
|
409
|
+
* asks this question resolved its head to the CLASS, so an instance field of
|
|
410
|
+
* that name is not reachable through it and answering with the instance
|
|
411
|
+
* field's type would type the receiver as something the source cannot
|
|
412
|
+
* denote. A def that resolves to no node, or a node with no captured type,
|
|
413
|
+
* answers `undefined` — the declining direction, matching how the rest of
|
|
414
|
+
* this pass treats an unresolvable lookup.
|
|
415
|
+
*/
|
|
416
|
+
const declaredTypeOfClassLevelField = (ownerId, fieldName) => {
|
|
417
|
+
for (const candidate of model.fields.lookupAllByOwner(ownerId, fieldName)) {
|
|
418
|
+
const graphId = (0, ids_js_1.resolveDefGraphId)(candidate.filePath, candidate, nodeLookup);
|
|
419
|
+
if (graphId === undefined)
|
|
420
|
+
continue;
|
|
421
|
+
const properties = graph.getNode(graphId)?.properties;
|
|
422
|
+
if (properties?.isStatic !== true)
|
|
423
|
+
continue;
|
|
424
|
+
const declaredType = properties.declaredType;
|
|
425
|
+
if (typeof declaredType !== 'string')
|
|
426
|
+
continue;
|
|
427
|
+
const trimmed = declaredType.trim();
|
|
428
|
+
if (trimmed.length === 0)
|
|
429
|
+
continue;
|
|
430
|
+
return trimmed;
|
|
431
|
+
}
|
|
432
|
+
return undefined;
|
|
433
|
+
};
|
|
434
|
+
for (const parsed of parsedFiles) {
|
|
435
|
+
const namespaceTargets = (0, namespace_targets_js_1.collectNamespaceTargets)(parsed, scopes, {
|
|
436
|
+
receiverPaths: provider.namespaceReceiverPaths,
|
|
437
|
+
moduleFileExists: (filePath) => index.moduleScopeByFile.has(filePath),
|
|
438
|
+
});
|
|
439
|
+
const fileCompoundOpts = { ...compoundOpts, namespaceTargets };
|
|
440
|
+
// Per-file resolved-callee-id capture context (#2227 U2). Built once per
|
|
441
|
+
// file; `undefined` when the sink is absent (pdg off) so the `tryEmitEdge`
|
|
442
|
+
// capture is a no-op and emission stays byte-identical (R4).
|
|
443
|
+
const calleeCapture = options.calleeIdSink !== undefined
|
|
444
|
+
? { sink: options.calleeIdSink, filePath: parsed.filePath }
|
|
445
|
+
: undefined;
|
|
446
|
+
for (const site of parsed.referenceSites) {
|
|
447
|
+
if (site.kind !== 'call' && site.kind !== 'read' && site.kind !== 'write')
|
|
448
|
+
continue;
|
|
449
|
+
if (site.explicitReceiver === undefined)
|
|
450
|
+
continue;
|
|
451
|
+
const receiverName = site.explicitReceiver.name;
|
|
452
|
+
const memberName = site.name;
|
|
453
|
+
const siteKey = `${parsed.filePath}:${site.atRange.startLine}:${site.atRange.startCol}`;
|
|
454
|
+
// ── owned-but-unbound receiver ───────────────────────────────
|
|
455
|
+
// The language declared this scope REBINDS the receiver and gave
|
|
456
|
+
// it no type — a JS/TS ordinary `function`, whose `this` comes
|
|
457
|
+
// from the call site (#2701). No enclosing type can be its type,
|
|
458
|
+
// so this is a definitive negative, not a miss: suppress the site
|
|
459
|
+
// instead of letting the receiver-blind lexical fallback in
|
|
460
|
+
// `lookupCore` match the enclosing class's member by name.
|
|
461
|
+
// No-op for every language that leaves `Scope.ownsReceivers` unset.
|
|
462
|
+
if ((0, walkers_js_1.isReceiverOwnedButUnbound)(site.inScope, receiverName, scopes)) {
|
|
463
|
+
options.recordResolutionOutcome?.({
|
|
464
|
+
kind: 'suppressed',
|
|
465
|
+
phase: 'receiver-bound-calls',
|
|
466
|
+
filePath: parsed.filePath,
|
|
467
|
+
name: site.name,
|
|
468
|
+
range: site.atRange,
|
|
469
|
+
reason: 'receiver-owned-but-unbound',
|
|
470
|
+
candidateIds: [],
|
|
471
|
+
});
|
|
472
|
+
handledSites.add(siteKey);
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
// ── super branch ─────────────────────────────────────────────
|
|
476
|
+
// Languages with caller-context-dependent super classification
|
|
477
|
+
// (C++) define `isSuperReceiverInContext`; we prefer it. Simple
|
|
478
|
+
// text-only languages (Python, Java, PHP) use the plain hook.
|
|
479
|
+
const isSuper = provider.isSuperReceiverInContext !== undefined
|
|
480
|
+
? provider.isSuperReceiverInContext(receiverName, site.inScope, scopes)
|
|
481
|
+
: provider.isSuperReceiver(receiverName);
|
|
482
|
+
if (isSuper) {
|
|
483
|
+
const enclosingClass = (0, walkers_js_1.findEnclosingClassDef)(site.inScope, scopes);
|
|
484
|
+
if (enclosingClass !== undefined) {
|
|
485
|
+
// For super-receiver dispatch (`parent::`, `base.`, `super()`),
|
|
486
|
+
// walk the inheritance-only ancestor chain when the language
|
|
487
|
+
// exposes it. PHP's `parent::` semantically bypasses composed
|
|
488
|
+
// traits; other languages without mixin augmentation have no
|
|
489
|
+
// `extendsOnlyMroFor` and fall back to `mroFor`.
|
|
490
|
+
const extendsOnly = scopes.methodDispatch.extendsOnlyMroFor;
|
|
491
|
+
const ancestors = extendsOnly !== undefined
|
|
492
|
+
? extendsOnly(enclosingClass.nodeId)
|
|
493
|
+
: scopes.methodDispatch.mroFor(enclosingClass.nodeId);
|
|
494
|
+
let memberDef;
|
|
495
|
+
let ambiguousOwnerId;
|
|
496
|
+
for (const ownerId of ancestors) {
|
|
497
|
+
const picked = site.kind === 'call'
|
|
498
|
+
? pickOverload(ownerId, memberName, site, model, provider)
|
|
499
|
+
: (0, walkers_js_1.findOwnedMember)(ownerId, memberName, model);
|
|
500
|
+
if (picked === exports.OVERLOAD_AMBIGUOUS) {
|
|
501
|
+
ambiguousOwnerId = ownerId;
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
if (picked !== undefined) {
|
|
505
|
+
memberDef = picked;
|
|
506
|
+
break;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
if (ambiguousOwnerId !== undefined) {
|
|
510
|
+
recordReceiverOverloadSuppression(options.recordResolutionOutcome, parsed.filePath, site, ambiguousOwnerId, memberName, model, provider);
|
|
511
|
+
handledSites.add(siteKey);
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
if (memberDef !== undefined) {
|
|
515
|
+
if (suppressDeletedCallTarget(options.recordResolutionOutcome, parsed.filePath, site, memberDef)) {
|
|
516
|
+
handledSites.add(siteKey);
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
// Super/base calls resolve through the MRO chain, not
|
|
520
|
+
// through imports — the ancestor method is found by
|
|
521
|
+
// walking `methodDispatch.mroFor(enclosingClass)`, which
|
|
522
|
+
// is independent of whether a `using` / `import` directive
|
|
523
|
+
// brought the ancestor into scope. We emit the canonical
|
|
524
|
+
// `'global'` tier (ARCHITECTURE.md § Scope-Resolution
|
|
525
|
+
// Pipeline — edge vocabulary).
|
|
526
|
+
//
|
|
527
|
+
// Known legacy-path asymmetry: the C# legacy DAG also
|
|
528
|
+
// classifies `base.Save()` as `'global'` (same-graph); the
|
|
529
|
+
// Python legacy DAG classifies `super().save()` as
|
|
530
|
+
// `'import-resolved'` because Python's ancestor lookup
|
|
531
|
+
// flows through `typeEnv.lookup(...)` which resolves the
|
|
532
|
+
// superclass via its `import`/`from … import …` binding.
|
|
533
|
+
// Closing that gap requires realigning the legacy tier
|
|
534
|
+
// classifier and is tracked separately.
|
|
535
|
+
const ok = (0, edges_js_1.tryEmitEdge)(graph, scopes, nodeLookup, site, memberDef, 'global', seen, 0.85, collapse, calleeCapture);
|
|
536
|
+
if (ok)
|
|
537
|
+
emitted++;
|
|
538
|
+
// Always mark handled when the site was resolved, even
|
|
539
|
+
// if the edge was deduplicated (collapse mode), so
|
|
540
|
+
// `emitReferencesViaLookup` doesn't re-emit from the
|
|
541
|
+
// reference index.
|
|
542
|
+
handledSites.add(siteKey);
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
// ── Case 0: compound receiver ────────────────────────────────
|
|
548
|
+
// #2744: remember a compound receiver we could not type. Reported at
|
|
549
|
+
// the end of the site loop, not here — a later case may still resolve
|
|
550
|
+
// the site, and only a site that survives every case is a real drop.
|
|
551
|
+
let compoundReceiverUnresolved = false;
|
|
552
|
+
// The punctuation test is a C-family heuristic and it is the reason
|
|
553
|
+
// `repos[0].save()` is INVISIBLE in all 14 languages: a subscript receiver
|
|
554
|
+
// contains neither `.` nor `(`, so this case never fired, the fold was
|
|
555
|
+
// never consulted, and no drop was recorded either — the call vanished
|
|
556
|
+
// with the instrument blind to it. PHP `->` and `::` receivers are lost
|
|
557
|
+
// the same way.
|
|
558
|
+
//
|
|
559
|
+
// A minted receiver chain is the STRUCTURAL answer to the same question:
|
|
560
|
+
// the capture layer walked the real AST and found the receiver is an
|
|
561
|
+
// expression, whatever punctuation it happens to be spelled with. Trusting
|
|
562
|
+
// that instead of the text is the substitution this whole line of work
|
|
563
|
+
// exists to make.
|
|
564
|
+
if (receiverName.includes('.') ||
|
|
565
|
+
receiverName.includes('(') ||
|
|
566
|
+
site.receiverChain !== undefined) {
|
|
567
|
+
const currentClass = (0, compound_receiver_js_1.resolveCompoundReceiverClass)(receiverName, site.inScope, scopes, index,
|
|
568
|
+
// Group A: the receiver IS this site's expression, so the site's
|
|
569
|
+
// captured chain describes it and the structural fold applies.
|
|
570
|
+
{ ...fileCompoundOpts, receiverChain: site.receiverChain });
|
|
571
|
+
compoundReceiverUnresolved = currentClass === undefined;
|
|
572
|
+
if (currentClass !== undefined) {
|
|
573
|
+
const chain = [currentClass.nodeId, ...scopes.methodDispatch.mroFor(currentClass.nodeId)];
|
|
574
|
+
let memberDef;
|
|
575
|
+
let ambiguousOwnerId;
|
|
576
|
+
// Static-only filter (#1756 / U3): same shape as Case 4's
|
|
577
|
+
// overload-aware chain walk (skip-and-walk-on). When
|
|
578
|
+
// an owner's resolved candidate is static-only (Kotlin
|
|
579
|
+
// companion-promoted), continue to the next ancestor in
|
|
580
|
+
// the MRO chain so a legitimate instance member can bind.
|
|
581
|
+
// If the entire chain is static-only, no edge is emitted —
|
|
582
|
+
// unlike Case 4, Case 0 does NOT mark the site handled in
|
|
583
|
+
// that situation because compound receivers (`a.b.c()`)
|
|
584
|
+
// are not pre-emitted by `emitReferencesViaLookup` (the
|
|
585
|
+
// reference index has no compound-receiver entry for
|
|
586
|
+
// shapes like `Logger.create("a")`), so there's no wrong
|
|
587
|
+
// target to suppress.
|
|
588
|
+
for (const ownerId of chain) {
|
|
589
|
+
const picked = site.kind === 'call'
|
|
590
|
+
? pickFirstNonStaticOnly(ownerId, memberName, site, model, provider)
|
|
591
|
+
: (0, walkers_js_1.findOwnedMember)(ownerId, memberName, model);
|
|
592
|
+
if (picked === exports.OVERLOAD_AMBIGUOUS) {
|
|
593
|
+
ambiguousOwnerId = ownerId;
|
|
594
|
+
break;
|
|
595
|
+
}
|
|
596
|
+
if (picked === STATIC_ONLY_FILTERED || picked === undefined) {
|
|
597
|
+
continue;
|
|
598
|
+
}
|
|
599
|
+
memberDef = picked;
|
|
600
|
+
break;
|
|
601
|
+
}
|
|
602
|
+
if (ambiguousOwnerId !== undefined) {
|
|
603
|
+
recordReceiverOverloadSuppression(options.recordResolutionOutcome, parsed.filePath, site, ambiguousOwnerId, memberName, model, provider);
|
|
604
|
+
handledSites.add(siteKey);
|
|
605
|
+
continue;
|
|
606
|
+
}
|
|
607
|
+
if (memberDef !== undefined) {
|
|
608
|
+
if (suppressDeletedCallTarget(options.recordResolutionOutcome, parsed.filePath, site, memberDef)) {
|
|
609
|
+
handledSites.add(siteKey);
|
|
610
|
+
continue;
|
|
611
|
+
}
|
|
612
|
+
const ok = (0, edges_js_1.tryEmitEdge)(graph, scopes, nodeLookup, site, memberDef, memberDef.filePath !== parsed.filePath ? 'import-resolved' : 'global', seen, 0.85, collapse, calleeCapture);
|
|
613
|
+
if (ok)
|
|
614
|
+
emitted++;
|
|
615
|
+
// Interface dispatch, exactly as Case 4 does it (#2813). When the
|
|
616
|
+
// folded receiver type is an Interface, the primary edge above
|
|
617
|
+
// lands on the interface's own method DECLARATION; these secondary
|
|
618
|
+
// edges are what reach the implementations.
|
|
619
|
+
//
|
|
620
|
+
// Case 4 had this and Case 0 did not, which made the gap a property
|
|
621
|
+
// of receiver SYNTAX rather than of types: a struct-field receiver
|
|
622
|
+
// (`s.orderRepo`) contains a dot, so it always takes Case 0, while
|
|
623
|
+
// the same interface reached through a local or parameter is a bare
|
|
624
|
+
// name and reaches Case 4. Field-held interfaces — dependency
|
|
625
|
+
// injection, in other words — were the half that silently lost every
|
|
626
|
+
// implementation edge.
|
|
627
|
+
//
|
|
628
|
+
// `currentClass` is the receiver's own folded type, matching what
|
|
629
|
+
// Case 4 passes. `emitInterfaceDispatchFor` self-gates on
|
|
630
|
+
// `ownerDef.type !== 'Interface'`, so this is inert for every
|
|
631
|
+
// concrete receiver and needs no language check of its own — a
|
|
632
|
+
// member whose owner resolved to a Struct emits nothing extra.
|
|
633
|
+
//
|
|
634
|
+
// Confidence mirrors THIS case's own primary emit above — the 0.85
|
|
635
|
+
// literal — so a site's dispatch edges never claim more certainty
|
|
636
|
+
// than the edge they hang off.
|
|
637
|
+
//
|
|
638
|
+
// Case 4 passes a site.kind-dependent value instead (1.0 for
|
|
639
|
+
// read/write, `:1399-1405`) because ITS primary varies the same way.
|
|
640
|
+
// Case 0 does branch on `site.kind` when picking the member
|
|
641
|
+
// (`:713-716`), it simply does not vary reason/confidence with it,
|
|
642
|
+
// so there is no 1.0 arm here to mirror. That leaves a read/write
|
|
643
|
+
// ACCESSES through a compound receiver at 0.85 while the same access
|
|
644
|
+
// through a bare name is 1.0 — a PRE-EXISTING difference between the
|
|
645
|
+
// two cases' primaries, not something this fan-out introduces.
|
|
646
|
+
// Deliberately not "fixed" here: changing Case 0's primary
|
|
647
|
+
// confidence is a separate behavioural change affecting every
|
|
648
|
+
// language, and is out of scope for #2813.
|
|
649
|
+
emitted += emitInterfaceDispatchFor(currentClass, memberName, memberDef, site, 0.85, calleeCapture);
|
|
650
|
+
// Always mark handled when the site was resolved, even
|
|
651
|
+
// if the edge was deduplicated (collapse mode), so
|
|
652
|
+
// `emitReferencesViaLookup` doesn't re-emit from the
|
|
653
|
+
// reference index.
|
|
654
|
+
handledSites.add(siteKey);
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
// ── Case 0.5: implicit `this` receiver ───────────────────────
|
|
660
|
+
// C++ `this->member()` (and same-shape receivers in other OO
|
|
661
|
+
// languages) should resolve against the enclosing class + MRO
|
|
662
|
+
// even when there is no explicit `this` typeBinding in scope.
|
|
663
|
+
//
|
|
664
|
+
// **Static-only filter dependency (#1756 / U3):** this case does
|
|
665
|
+
// NOT currently consult `provider.isStaticOnly`. Today it fires
|
|
666
|
+
// only for C++ (the sole `resolveThisViaEnclosingClass === true`
|
|
667
|
+
// language), which has no static-only semantics. Kotlin — the
|
|
668
|
+
// current `isStaticOnly` consumer — leaves `resolveThisVia
|
|
669
|
+
// EnclosingClass` unset, so Case 0.5 is dead code for Kotlin
|
|
670
|
+
// crossover suppression and U3 leaves it untouched. If any
|
|
671
|
+
// future language enables BOTH `resolveThisViaEnclosingClass`
|
|
672
|
+
// AND `isStaticOnly`, the chain-walk below MUST adopt the
|
|
673
|
+
// skip-and-walk-on filter pattern used by Cases 0, 3b, and 4.
|
|
674
|
+
if (provider.resolveThisViaEnclosingClass === true && receiverName === 'this') {
|
|
675
|
+
const enclosingClass = (0, walkers_js_1.findEnclosingClassDef)(site.inScope, scopes);
|
|
676
|
+
if (enclosingClass !== undefined) {
|
|
677
|
+
const languageResolution = provider.resolveReceiverMember?.(enclosingClass, memberName, site, scopes, model);
|
|
678
|
+
if (languageResolution?.kind === 'ambiguous') {
|
|
679
|
+
options.recordResolutionOutcome?.({
|
|
680
|
+
kind: 'suppressed',
|
|
681
|
+
phase: 'receiver-bound-calls',
|
|
682
|
+
filePath: parsed.filePath,
|
|
683
|
+
name: site.name,
|
|
684
|
+
range: site.atRange,
|
|
685
|
+
reason: 'member-lookup-ambiguous',
|
|
686
|
+
candidateIds: languageResolution.candidateIds,
|
|
687
|
+
});
|
|
688
|
+
handledSites.add(siteKey);
|
|
689
|
+
continue;
|
|
690
|
+
}
|
|
691
|
+
if (languageResolution?.kind === 'resolved') {
|
|
692
|
+
const memberDef = languageResolution.definition;
|
|
693
|
+
if (suppressDeletedCallTarget(options.recordResolutionOutcome, parsed.filePath, site, memberDef)) {
|
|
694
|
+
handledSites.add(siteKey);
|
|
695
|
+
continue;
|
|
696
|
+
}
|
|
697
|
+
const reason = site.kind === 'write' || site.kind === 'read'
|
|
698
|
+
? site.kind
|
|
699
|
+
: memberDef.filePath !== parsed.filePath
|
|
700
|
+
? 'import-resolved'
|
|
701
|
+
: 'global';
|
|
702
|
+
const confidence = site.kind === 'write' || site.kind === 'read' ? 1.0 : 0.85;
|
|
703
|
+
const ok = (0, edges_js_1.tryEmitEdge)(graph, scopes, nodeLookup, site, memberDef, reason, seen, confidence, collapse, calleeCapture);
|
|
704
|
+
if (ok)
|
|
705
|
+
emitted++;
|
|
706
|
+
handledSites.add(siteKey);
|
|
707
|
+
continue;
|
|
708
|
+
}
|
|
709
|
+
const chain = [
|
|
710
|
+
enclosingClass.nodeId,
|
|
711
|
+
...scopes.methodDispatch.mroFor(enclosingClass.nodeId),
|
|
712
|
+
];
|
|
713
|
+
let memberDef;
|
|
714
|
+
let ambiguous = false;
|
|
715
|
+
let hiddenByName = false;
|
|
716
|
+
for (const ownerId of chain) {
|
|
717
|
+
const methodOverloads = model.methods.lookupAllByOwner(ownerId, memberName);
|
|
718
|
+
if (methodOverloads.length > 0) {
|
|
719
|
+
const narrowed = (0, overload_narrowing_js_1.narrowOverloadCandidates)(methodOverloads, site.arity, site.argumentTypes, {
|
|
720
|
+
argumentTypeClasses: site.argumentTypeClasses,
|
|
721
|
+
conversionRankFn: provider.conversionRankFn,
|
|
722
|
+
conversionOnlyArgTypePrefixes: provider.conversionOnlyArgTypePrefixes,
|
|
723
|
+
constraintCompatibility: provider.constraintCompatibility,
|
|
724
|
+
});
|
|
725
|
+
if ((0, overload_narrowing_js_1.isOverloadAmbiguousAfterNormalization)(narrowed, site.arity)) {
|
|
726
|
+
ambiguous = true;
|
|
727
|
+
break;
|
|
728
|
+
}
|
|
729
|
+
if (narrowed.length === 0) {
|
|
730
|
+
// C++ name hiding: if the derived class declares `f`, base-class
|
|
731
|
+
// overloads named `f` are hidden for member lookup
|
|
732
|
+
// ([basic.lookup.classref]). A non-viable derived overload set
|
|
733
|
+
// therefore terminates lookup instead of falling through to base.
|
|
734
|
+
hiddenByName = true;
|
|
735
|
+
break;
|
|
736
|
+
}
|
|
737
|
+
// Multiple tied survivors with distinct param types (e.g.
|
|
738
|
+
// h(int,double) vs h(double,int) both scoring 2) → ambiguous.
|
|
739
|
+
if (narrowed.length > 1) {
|
|
740
|
+
ambiguous = true;
|
|
741
|
+
break;
|
|
742
|
+
}
|
|
743
|
+
memberDef = narrowed[0] ?? methodOverloads[0];
|
|
744
|
+
break;
|
|
745
|
+
}
|
|
746
|
+
// Field/property lookup intentionally runs only after the method
|
|
747
|
+
// lookup above: in C++ member-name lookup, functions with this
|
|
748
|
+
// name hide same-named base members; we therefore prefer method
|
|
749
|
+
// candidates first and only target a field when no methods with
|
|
750
|
+
// this name exist on the current owner.
|
|
751
|
+
memberDef = model.fields.lookupFieldByOwner(ownerId, memberName);
|
|
752
|
+
if (memberDef !== undefined) {
|
|
753
|
+
break;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
if (ambiguous) {
|
|
757
|
+
handledSites.add(siteKey);
|
|
758
|
+
continue;
|
|
759
|
+
}
|
|
760
|
+
if (hiddenByName) {
|
|
761
|
+
handledSites.add(siteKey);
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
if (memberDef !== undefined) {
|
|
765
|
+
if (suppressDeletedCallTarget(options.recordResolutionOutcome, parsed.filePath, site, memberDef)) {
|
|
766
|
+
handledSites.add(siteKey);
|
|
767
|
+
continue;
|
|
768
|
+
}
|
|
769
|
+
const reason = site.kind === 'write' || site.kind === 'read'
|
|
770
|
+
? site.kind
|
|
771
|
+
: memberDef.filePath !== parsed.filePath
|
|
772
|
+
? 'import-resolved'
|
|
773
|
+
: 'global';
|
|
774
|
+
const confidence = site.kind === 'write' || site.kind === 'read' ? 1.0 : 0.85;
|
|
775
|
+
const ok = (0, edges_js_1.tryEmitEdge)(graph, scopes, nodeLookup, site, memberDef, reason, seen, confidence, collapse, calleeCapture);
|
|
776
|
+
if (ok)
|
|
777
|
+
emitted++;
|
|
778
|
+
handledSites.add(siteKey);
|
|
779
|
+
continue;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
// ── Case 1: namespace receiver ───────────────────────────────
|
|
784
|
+
// `namespaceTargets` is collected per FILE, so a local declaration that
|
|
785
|
+
// shadows the import must suppress it — `def f(pkg): pkg.db.query()`
|
|
786
|
+
// calls a method on the PARAMETER, and resolving it through the import
|
|
787
|
+
// emits a wrong edge, not a missing one. The compound-receiver
|
|
788
|
+
// construction path has applied this guard since #2770; Case 1 never did,
|
|
789
|
+
// for dotted and single-segment receivers alike.
|
|
790
|
+
// Map lookup FIRST: it is an O(1) miss for almost every site, and the
|
|
791
|
+
// guard is a scope-chain walk (a Set allocation plus a linear `ownedDefs`
|
|
792
|
+
// scan per level). Guarding before looking up would charge that walk to
|
|
793
|
+
// every explicit-receiver site in every language, for a candidate set
|
|
794
|
+
// that is usually empty. Mirrors the order the compound-receiver
|
|
795
|
+
// construction path already uses.
|
|
796
|
+
const namespaceCandidates = namespaceTargets.get(receiverName);
|
|
797
|
+
const targetFiles = namespaceCandidates !== undefined &&
|
|
798
|
+
!(0, walkers_js_1.isNamespaceNameShadowed)(receiverName, site.inScope, scopes)
|
|
799
|
+
? namespaceCandidates
|
|
800
|
+
: undefined;
|
|
801
|
+
if (targetFiles !== undefined && provider.resolveQualifiedReceiverMember === undefined) {
|
|
802
|
+
let found = false;
|
|
803
|
+
for (const targetFile of targetFiles) {
|
|
804
|
+
const memberDef = (0, walkers_js_1.findExportedDef)(targetFile, memberName, index);
|
|
805
|
+
if (memberDef !== undefined) {
|
|
806
|
+
if (suppressDeletedCallTarget(options.recordResolutionOutcome, parsed.filePath, site, memberDef)) {
|
|
807
|
+
handledSites.add(siteKey);
|
|
808
|
+
found = true;
|
|
809
|
+
break;
|
|
810
|
+
}
|
|
811
|
+
const ok = (0, edges_js_1.tryEmitEdge)(graph, scopes, nodeLookup, site, memberDef, memberDef.filePath !== parsed.filePath ? 'import-resolved' : 'global', seen, 0.85, collapse, calleeCapture);
|
|
812
|
+
if (ok)
|
|
813
|
+
emitted++;
|
|
814
|
+
handledSites.add(siteKey);
|
|
815
|
+
found = true;
|
|
816
|
+
break;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
if (found)
|
|
820
|
+
continue;
|
|
821
|
+
}
|
|
822
|
+
// ── Case 1.5: qualified namespace-receiver (language-specific) ───
|
|
823
|
+
// Languages whose qualified-name semantics need workspace-wide
|
|
824
|
+
// namespace-scope walking (C++ `outer::foo()`, including inline-
|
|
825
|
+
// namespace transitive traversal) implement `resolveQualifiedReceiverMember`.
|
|
826
|
+
// Runs before Case 2 so namespace receivers don't accidentally match a
|
|
827
|
+
// class with the same simple name.
|
|
828
|
+
if (provider.resolveQualifiedReceiverMember !== undefined) {
|
|
829
|
+
const memberDef = provider.resolveQualifiedReceiverMember(receiverName, memberName, site.inScope, scopes, parsedFiles, site);
|
|
830
|
+
if (memberDef === 'ambiguous') {
|
|
831
|
+
// Same-name ambiguity across inline-namespace children (#1564):
|
|
832
|
+
// suppress edge emission, mark site handled.
|
|
833
|
+
options.recordResolutionOutcome?.({
|
|
834
|
+
kind: 'suppressed',
|
|
835
|
+
phase: 'receiver-bound-calls',
|
|
836
|
+
filePath: parsed.filePath,
|
|
837
|
+
name: site.name,
|
|
838
|
+
range: site.atRange,
|
|
839
|
+
reason: 'inline-ns-ambiguous',
|
|
840
|
+
candidateIds: [],
|
|
841
|
+
});
|
|
842
|
+
handledSites.add(siteKey);
|
|
843
|
+
continue;
|
|
844
|
+
}
|
|
845
|
+
if (memberDef !== undefined) {
|
|
846
|
+
if (suppressDeletedCallTarget(options.recordResolutionOutcome, parsed.filePath, site, memberDef)) {
|
|
847
|
+
handledSites.add(siteKey);
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
850
|
+
const ok = (0, edges_js_1.tryEmitEdge)(graph, scopes, nodeLookup, site, memberDef, memberDef.filePath !== parsed.filePath ? 'import-resolved' : 'global', seen, 0.85, collapse, calleeCapture);
|
|
851
|
+
if (ok)
|
|
852
|
+
emitted++;
|
|
853
|
+
handledSites.add(siteKey);
|
|
854
|
+
continue;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
// ── Case 2: class-name receiver ──────────────────────────────
|
|
858
|
+
const classDef = (0, walkers_js_1.findClassBindingInScope)(site.inScope, receiverName, scopes);
|
|
859
|
+
if (classDef !== undefined) {
|
|
860
|
+
const chain = [classDef.nodeId, ...scopes.methodDispatch.mroFor(classDef.nodeId)];
|
|
861
|
+
let memberDef;
|
|
862
|
+
let ambiguousOwnerId;
|
|
863
|
+
for (const ownerId of chain) {
|
|
864
|
+
const picked = site.kind === 'call'
|
|
865
|
+
? pickOverload(ownerId, memberName, site, model, provider)
|
|
866
|
+
: (0, walkers_js_1.findOwnedMember)(ownerId, memberName, model);
|
|
867
|
+
if (picked === exports.OVERLOAD_AMBIGUOUS) {
|
|
868
|
+
ambiguousOwnerId = ownerId;
|
|
869
|
+
break;
|
|
870
|
+
}
|
|
871
|
+
if (picked !== undefined) {
|
|
872
|
+
memberDef = picked;
|
|
873
|
+
// The MRO chain is most-derived-first ([classDef, ...ancestors]).
|
|
874
|
+
// If the most-derived definition is arity-incompatible with the
|
|
875
|
+
// call site, PHP throws ArgumentCountError at runtime — it does
|
|
876
|
+
// NOT silently dispatch to an ancestor. Terminate the chain walk
|
|
877
|
+
// so no edge is emitted, rather than falling through to an
|
|
878
|
+
// arity-compatible ancestor (which would be a false positive).
|
|
879
|
+
if ((0, overload_narrowing_js_1.narrowOverloadCandidates)([memberDef], site.arity, site.argumentTypes).length === 0) {
|
|
880
|
+
memberDef = undefined;
|
|
881
|
+
break;
|
|
882
|
+
}
|
|
883
|
+
break;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
if (ambiguousOwnerId !== undefined) {
|
|
887
|
+
recordReceiverOverloadSuppression(options.recordResolutionOutcome, parsed.filePath, site, ambiguousOwnerId, memberName, model, provider);
|
|
888
|
+
handledSites.add(siteKey);
|
|
889
|
+
continue;
|
|
890
|
+
}
|
|
891
|
+
if (memberDef !== undefined) {
|
|
892
|
+
if (suppressDeletedCallTarget(options.recordResolutionOutcome, parsed.filePath, site, memberDef)) {
|
|
893
|
+
handledSites.add(siteKey);
|
|
894
|
+
continue;
|
|
895
|
+
}
|
|
896
|
+
const reason = site.kind === 'write' || site.kind === 'read'
|
|
897
|
+
? site.kind
|
|
898
|
+
: memberDef.filePath !== parsed.filePath
|
|
899
|
+
? 'import-resolved'
|
|
900
|
+
: 'global';
|
|
901
|
+
const confidence = site.kind === 'write' || site.kind === 'read' ? 1.0 : 0.85;
|
|
902
|
+
const ok = (0, edges_js_1.tryEmitEdge)(graph, scopes, nodeLookup, site, memberDef, reason, seen, confidence, collapse, calleeCapture);
|
|
903
|
+
if (ok)
|
|
904
|
+
emitted++;
|
|
905
|
+
handledSites.add(siteKey);
|
|
906
|
+
continue;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
// ── Case 3: dotted typeBinding (`u: models.User`) ────────────
|
|
910
|
+
const typeRef = (0, walkers_js_1.findReceiverTypeBinding)(site.inScope, receiverName, scopes);
|
|
911
|
+
if (typeRef !== undefined && typeRef.rawName.includes('.')) {
|
|
912
|
+
const [nsName, ...classNameParts] = typeRef.rawName.split('.');
|
|
913
|
+
const className = classNameParts.join('.');
|
|
914
|
+
const targetFiles3 = namespaceTargets.get(nsName);
|
|
915
|
+
if (targetFiles3 !== undefined && className.length > 0) {
|
|
916
|
+
let found3 = false;
|
|
917
|
+
for (const targetFile3 of targetFiles3) {
|
|
918
|
+
const classDef3 = (0, walkers_js_1.findExportedDef)(targetFile3, className, index);
|
|
919
|
+
if (classDef3 !== undefined) {
|
|
920
|
+
const picked = site.kind === 'call'
|
|
921
|
+
? pickOverload(classDef3.nodeId, memberName, site, model, provider)
|
|
922
|
+
: (0, walkers_js_1.findOwnedMember)(classDef3.nodeId, memberName, model);
|
|
923
|
+
if (picked === exports.OVERLOAD_AMBIGUOUS) {
|
|
924
|
+
recordReceiverOverloadSuppression(options.recordResolutionOutcome, parsed.filePath, site, classDef3.nodeId, memberName, model, provider);
|
|
925
|
+
handledSites.add(siteKey);
|
|
926
|
+
found3 = true;
|
|
927
|
+
break;
|
|
928
|
+
}
|
|
929
|
+
if (picked !== undefined) {
|
|
930
|
+
const memberDef = picked;
|
|
931
|
+
if (suppressDeletedCallTarget(options.recordResolutionOutcome, parsed.filePath, site, memberDef)) {
|
|
932
|
+
handledSites.add(siteKey);
|
|
933
|
+
found3 = true;
|
|
934
|
+
break;
|
|
935
|
+
}
|
|
936
|
+
const ok = (0, edges_js_1.tryEmitEdge)(graph, scopes, nodeLookup, site, memberDef, memberDef.filePath !== parsed.filePath ? 'import-resolved' : 'global', seen,
|
|
937
|
+
// Explicit defaults so the trailing capture ctx (#2227 U2) can
|
|
938
|
+
// be threaded without changing dedup/confidence behavior.
|
|
939
|
+
0.85, false, calleeCapture);
|
|
940
|
+
if (ok) {
|
|
941
|
+
emitted++;
|
|
942
|
+
handledSites.add(siteKey);
|
|
943
|
+
}
|
|
944
|
+
found3 = true;
|
|
945
|
+
break;
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
if (found3)
|
|
950
|
+
continue;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
// ── Case 3b: chain-typebinding (`city → user.get_city`) ──────
|
|
954
|
+
// Also handles compound member-call rawNames (`city → addr.get_city()`)
|
|
955
|
+
// where the rawName includes both `.` and `()` — Ruby's
|
|
956
|
+
// member-call-return captures produce this shape.
|
|
957
|
+
const chainHead = typeRef !== undefined && typeRef.rawName.includes('.')
|
|
958
|
+
? (typeRef.rawName.split('.', 1)[0] ?? '')
|
|
959
|
+
: undefined;
|
|
960
|
+
if (typeRef !== undefined && chainHead !== undefined && !namespaceTargets.has(chainHead)) {
|
|
961
|
+
// Try the plain dotted-field walk first — covers property /
|
|
962
|
+
// collection-accessor shapes (`.Values`, Kotlin `.size`) and
|
|
963
|
+
// field chains. Fall back to call-form (`x()`) which treats
|
|
964
|
+
// the last segment as a method invocation. For rawNames that
|
|
965
|
+
// already contain `()` (Ruby member-call-return captures),
|
|
966
|
+
// pass through directly — the compound resolver handles the
|
|
967
|
+
// full expression including the call syntax.
|
|
968
|
+
let ownerDef = (0, compound_receiver_js_1.resolveCompoundReceiverClass)(typeRef.rawName, typeRef.declaredAtScope, scopes, index, fileCompoundOpts);
|
|
969
|
+
if (ownerDef === undefined && !typeRef.rawName.includes('(')) {
|
|
970
|
+
ownerDef = (0, compound_receiver_js_1.resolveCompoundReceiverClass)(typeRef.rawName + '()', typeRef.declaredAtScope, scopes, index, fileCompoundOpts);
|
|
971
|
+
}
|
|
972
|
+
if (ownerDef !== undefined) {
|
|
973
|
+
const chain = [ownerDef.nodeId, ...scopes.methodDispatch.mroFor(ownerDef.nodeId)];
|
|
974
|
+
let memberDef;
|
|
975
|
+
let ambiguousOwnerId;
|
|
976
|
+
// Static-only filter (#1756 / U3): mirrors Case 0's
|
|
977
|
+
// overload-aware chain walk. When
|
|
978
|
+
// a static-only candidate is found at an ancestor, walk on
|
|
979
|
+
// so a legitimate instance member can bind. If the entire
|
|
980
|
+
// chain is static-only, no edge is emitted (Case 3b is fed
|
|
981
|
+
// by chain-typebinding receivers, not pre-emitted by
|
|
982
|
+
// `emitReferencesViaLookup` for compound shapes, so no
|
|
983
|
+
// handled-site marker is needed for chain-only-static).
|
|
984
|
+
for (const ownerId of chain) {
|
|
985
|
+
const picked = site.kind === 'call'
|
|
986
|
+
? pickFirstNonStaticOnly(ownerId, memberName, site, model, provider)
|
|
987
|
+
: (0, walkers_js_1.findOwnedMember)(ownerId, memberName, model);
|
|
988
|
+
if (picked === exports.OVERLOAD_AMBIGUOUS) {
|
|
989
|
+
ambiguousOwnerId = ownerId;
|
|
990
|
+
break;
|
|
991
|
+
}
|
|
992
|
+
if (picked === STATIC_ONLY_FILTERED || picked === undefined) {
|
|
993
|
+
continue;
|
|
994
|
+
}
|
|
995
|
+
memberDef = picked;
|
|
996
|
+
break;
|
|
997
|
+
}
|
|
998
|
+
if (ambiguousOwnerId !== undefined) {
|
|
999
|
+
recordReceiverOverloadSuppression(options.recordResolutionOutcome, parsed.filePath, site, ambiguousOwnerId, memberName, model, provider);
|
|
1000
|
+
handledSites.add(siteKey);
|
|
1001
|
+
continue;
|
|
1002
|
+
}
|
|
1003
|
+
if (memberDef !== undefined) {
|
|
1004
|
+
if (suppressDeletedCallTarget(options.recordResolutionOutcome, parsed.filePath, site, memberDef)) {
|
|
1005
|
+
handledSites.add(siteKey);
|
|
1006
|
+
continue;
|
|
1007
|
+
}
|
|
1008
|
+
const ok = (0, edges_js_1.tryEmitEdge)(graph, scopes, nodeLookup, site, memberDef, memberDef.filePath !== parsed.filePath ? 'import-resolved' : 'global', seen, 0.85, collapse, calleeCapture);
|
|
1009
|
+
if (ok)
|
|
1010
|
+
emitted++;
|
|
1011
|
+
// Interface dispatch, exactly as Cases 0 and 4 do it (#2832). Case
|
|
1012
|
+
// 3b folds a chain to a receiver type through the SAME
|
|
1013
|
+
// `resolveCompoundReceiverClass` call and the same MRO walk Case 0
|
|
1014
|
+
// uses, so when that fold lands on an Interface the primary edge
|
|
1015
|
+
// above names the interface's own bodiless DECLARATION and nothing
|
|
1016
|
+
// reaches the implementations.
|
|
1017
|
+
//
|
|
1018
|
+
// Leaving 3b out made the fan-out a property of how the receiver
|
|
1019
|
+
// was SPELLED rather than of what it resolved to: `d.repo.save()`
|
|
1020
|
+
// took Case 0 and fanned out, while binding the identical field to
|
|
1021
|
+
// a local first (`const r = d.repo; r.save()`) took Case 3b and
|
|
1022
|
+
// did not. #2829 closed that gap for Case 0 and left this half of
|
|
1023
|
+
// it open (#2832).
|
|
1024
|
+
//
|
|
1025
|
+
// `ownerDef` is the receiver's own folded type — matching Case 0's
|
|
1026
|
+
// `currentClass` and Case 4's `ownerDef` — NOT the owner of the
|
|
1027
|
+
// member the MRO walk settled on. That distinction matters: a
|
|
1028
|
+
// receiver that folds to a concrete class merely INHERITING an
|
|
1029
|
+
// interface method must not fan out, because its runtime type is
|
|
1030
|
+
// that class. `emitInterfaceDispatchFor` self-gates on
|
|
1031
|
+
// `ownerDef.type !== 'Interface'`, so this is inert for every
|
|
1032
|
+
// concrete receiver and needs no language check of its own.
|
|
1033
|
+
//
|
|
1034
|
+
// That gate is deliberately narrower than "the primary landed on
|
|
1035
|
+
// something bodiless": a chain folding to an ABSTRACT class also
|
|
1036
|
+
// dead-ends on a declaration-only member and does NOT fan out
|
|
1037
|
+
// here. Widening it to `|| isDeclarationOnly(memberDef)` would
|
|
1038
|
+
// cover that, but it changes Cases 0 and 4 identically and for
|
|
1039
|
+
// every language, so it is not #2832's to make.
|
|
1040
|
+
//
|
|
1041
|
+
// Confidence mirrors THIS case's own primary emit above — the 0.85
|
|
1042
|
+
// literal — so a site's dispatch edges never claim more certainty
|
|
1043
|
+
// than the edge they hang off. Case 4 passes a site.kind-dependent
|
|
1044
|
+
// value instead because ITS primary varies that way; Case 3b's
|
|
1045
|
+
// primary, like Case 0's, does not, so there is no 1.0 arm here to
|
|
1046
|
+
// mirror.
|
|
1047
|
+
emitted += emitInterfaceDispatchFor(ownerDef, memberName, memberDef, site, 0.85, calleeCapture);
|
|
1048
|
+
// Always mark handled when the site was resolved, even
|
|
1049
|
+
// if the edge was deduplicated (collapse mode), so
|
|
1050
|
+
// `emitReferencesViaLookup` doesn't re-emit from the
|
|
1051
|
+
// reference index.
|
|
1052
|
+
handledSites.add(siteKey);
|
|
1053
|
+
continue;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
// ── Case 4: simple typeBinding (`u: U`) ──────────────────────
|
|
1058
|
+
if (typeRef !== undefined && !typeRef.rawName.includes('.')) {
|
|
1059
|
+
// A `rawName` the capture layer reduced from a type application is
|
|
1060
|
+
// resolved through the application it was written as, so the erasure
|
|
1061
|
+
// takes the GROUNDED route rather than binding whatever the workspace
|
|
1062
|
+
// declares under that base name — see {@link erasedTypeApplication}.
|
|
1063
|
+
const typeApplication = (0, template_arguments_js_1.erasedTypeApplication)(typeRef);
|
|
1064
|
+
let ownerDef = (0, walkers_js_1.resolveClassBindingForName)(site.inScope, typeApplication ?? typeRef.rawName, scopes);
|
|
1065
|
+
// `findClassBindingInScope(..., typeRef.rawName)` only works when
|
|
1066
|
+
// rawName is itself a class symbol reachable through scope bindings.
|
|
1067
|
+
// For languages with namespace-style imports (Go), imported types
|
|
1068
|
+
// don't create bindings. Fall back to QualifiedNameIndex — single-
|
|
1069
|
+
// match wins; ambiguous/missing falls through.
|
|
1070
|
+
//
|
|
1071
|
+
// NOT for an erased base name. This fallback consults no scope, no
|
|
1072
|
+
// import and no module: it binds any name with exactly one workspace
|
|
1073
|
+
// definition. That is a defensible last resort for a name the source
|
|
1074
|
+
// WROTE — the file named it, so the only question is which declaration
|
|
1075
|
+
// it meant — and is not defensible for a name the capture layer
|
|
1076
|
+
// MANUFACTURED by erasing type arguments, where the file may never
|
|
1077
|
+
// have named it at all. The lookup above already answered that case on
|
|
1078
|
+
// grounds; re-asking it here without any would undo them.
|
|
1079
|
+
if (ownerDef === undefined && typeApplication === undefined) {
|
|
1080
|
+
const qnameIds = scopes.qualifiedNames.get(typeRef.rawName);
|
|
1081
|
+
if (qnameIds.length === 1) {
|
|
1082
|
+
const qdef = scopes.defs.get(qnameIds[0]);
|
|
1083
|
+
if (qdef !== undefined && (0, walkers_js_1.isClassLike)(qdef.type))
|
|
1084
|
+
ownerDef = qdef;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
// Map for-of tuple bindings (`__MAP_TUPLE_i__:mapId`), callable
|
|
1088
|
+
// aliases (`getUser` → User), and other compound-friendly shapes
|
|
1089
|
+
// need the compound resolver keyed by the receiver identifier.
|
|
1090
|
+
//
|
|
1091
|
+
// Not asked for a receiver whose declared type IS an erased type
|
|
1092
|
+
// application the grounded lookup just refused. Those shapes are
|
|
1093
|
+
// alternatives to a declared type, not readings of one: this receiver
|
|
1094
|
+
// HAS a declared type, the question "which class does its base name
|
|
1095
|
+
// denote here" was already put and answered "cannot tell", and the
|
|
1096
|
+
// compound resolver reaches the same base name through its own
|
|
1097
|
+
// scope-free routes (its bare-identifier step re-runs the lookup on
|
|
1098
|
+
// `rawName`; its callable-alias step retries the same name as a
|
|
1099
|
+
// construction). Asking again by a route that cannot see the grounds
|
|
1100
|
+
// would make the refusal decorative.
|
|
1101
|
+
if (ownerDef === undefined && typeApplication === undefined) {
|
|
1102
|
+
ownerDef = (0, compound_receiver_js_1.resolveCompoundReceiverClass)(receiverName, site.inScope, scopes, index,
|
|
1103
|
+
// Group A, same reasoning as Case 0 above.
|
|
1104
|
+
{ ...fileCompoundOpts, receiverChain: site.receiverChain });
|
|
1105
|
+
}
|
|
1106
|
+
// The receiver has a declared type, that type is a type APPLICATION,
|
|
1107
|
+
// and its base name could not be connected to any declaration this
|
|
1108
|
+
// file can see. The site is DROPPED, and dropped deliberately, so it
|
|
1109
|
+
// must be marked handled: `emitReferencesViaLookup` would otherwise
|
|
1110
|
+
// re-emit the very target the grounds refused, because the pre-resolved
|
|
1111
|
+
// reference index answers a name with the single workspace definition
|
|
1112
|
+
// that carries it and knows nothing about erasure. That is exactly why
|
|
1113
|
+
// the static-only filter above marks handled too — a refusal this pass
|
|
1114
|
+
// makes is not a refusal until the fallback emitter is told.
|
|
1115
|
+
//
|
|
1116
|
+
// Recorded as `receiver-unresolved` rather than silently: the receiver's
|
|
1117
|
+
// TYPE could not be established, which is the reason's own definition,
|
|
1118
|
+
// and a consumer counting resolver gaps must see this drop rather than
|
|
1119
|
+
// read the absence as a resolved site. No `receiverOrigin` — the base
|
|
1120
|
+
// name resolving in the index is precisely the evidence just rejected,
|
|
1121
|
+
// so claiming `in-program` from it would relaunder the fabrication as a
|
|
1122
|
+
// diagnostic, and the absent field hedges (the safe direction).
|
|
1123
|
+
if (ownerDef === undefined && typeApplication !== undefined) {
|
|
1124
|
+
options.recordResolutionOutcome?.({
|
|
1125
|
+
kind: 'suppressed',
|
|
1126
|
+
reason: 'receiver-unresolved',
|
|
1127
|
+
candidateIds: [],
|
|
1128
|
+
phase: 'receiver-bound-calls',
|
|
1129
|
+
filePath: parsed.filePath,
|
|
1130
|
+
name: site.name,
|
|
1131
|
+
range: site.atRange,
|
|
1132
|
+
siteKind: site.kind,
|
|
1133
|
+
});
|
|
1134
|
+
handledSites.add(siteKey);
|
|
1135
|
+
continue;
|
|
1136
|
+
}
|
|
1137
|
+
if (ownerDef !== undefined) {
|
|
1138
|
+
const languageResolution = provider.resolveReceiverMember?.(ownerDef, memberName, site, scopes, model);
|
|
1139
|
+
if (languageResolution?.kind === 'ambiguous') {
|
|
1140
|
+
options.recordResolutionOutcome?.({
|
|
1141
|
+
kind: 'suppressed',
|
|
1142
|
+
phase: 'receiver-bound-calls',
|
|
1143
|
+
filePath: parsed.filePath,
|
|
1144
|
+
name: site.name,
|
|
1145
|
+
range: site.atRange,
|
|
1146
|
+
reason: 'member-lookup-ambiguous',
|
|
1147
|
+
candidateIds: languageResolution.candidateIds,
|
|
1148
|
+
});
|
|
1149
|
+
handledSites.add(siteKey);
|
|
1150
|
+
continue;
|
|
1151
|
+
}
|
|
1152
|
+
if (languageResolution?.kind === 'resolved') {
|
|
1153
|
+
const memberDef = languageResolution.definition;
|
|
1154
|
+
if (suppressDeletedCallTarget(options.recordResolutionOutcome, parsed.filePath, site, memberDef)) {
|
|
1155
|
+
handledSites.add(siteKey);
|
|
1156
|
+
continue;
|
|
1157
|
+
}
|
|
1158
|
+
const reason = site.kind === 'write' || site.kind === 'read'
|
|
1159
|
+
? site.kind
|
|
1160
|
+
: memberDef.filePath !== parsed.filePath
|
|
1161
|
+
? 'import-resolved'
|
|
1162
|
+
: 'global';
|
|
1163
|
+
const confidence = site.kind === 'write' || site.kind === 'read' ? 1.0 : 0.85;
|
|
1164
|
+
const ok = (0, edges_js_1.tryEmitEdge)(graph, scopes, nodeLookup, site, memberDef, reason, seen, confidence, collapse, calleeCapture);
|
|
1165
|
+
if (ok)
|
|
1166
|
+
emitted++;
|
|
1167
|
+
handledSites.add(siteKey);
|
|
1168
|
+
continue;
|
|
1169
|
+
}
|
|
1170
|
+
const chain = [ownerDef.nodeId, ...scopes.methodDispatch.mroFor(ownerDef.nodeId)];
|
|
1171
|
+
let memberDef;
|
|
1172
|
+
let ambiguous = false;
|
|
1173
|
+
let ambiguousOwnerId;
|
|
1174
|
+
// Track whether the chain walk filtered out any static-only
|
|
1175
|
+
// candidates. When it did and the chain ended with no
|
|
1176
|
+
// legitimate instance member, we mark the site as handled so
|
|
1177
|
+
// `emitReferencesViaLookup` doesn't re-emit a wrong target
|
|
1178
|
+
// from the pre-resolved reference index (which has no
|
|
1179
|
+
// static-only awareness).
|
|
1180
|
+
let allFilteredStaticOnly = false;
|
|
1181
|
+
// Static-only filter (#1756 / U2): the filter must run INSIDE
|
|
1182
|
+
// the chain walk and BEFORE arity narrowing.
|
|
1183
|
+
//
|
|
1184
|
+
// INSIDE: when a derived owner's only candidates are static-
|
|
1185
|
+
// only (Kotlin companion-promoted), `pickFirstNonStaticOnly`
|
|
1186
|
+
// returns `undefined` and the loop `continue`s to the next
|
|
1187
|
+
// ancestor in the MRO chain — giving a legitimate ancestor
|
|
1188
|
+
// instance method a chance to bind. The earlier after-chain
|
|
1189
|
+
// filter aborted the entire site instead, producing a false
|
|
1190
|
+
// negative whenever the most-derived owner shadowed an
|
|
1191
|
+
// ancestor's instance method with a static-only companion
|
|
1192
|
+
// member.
|
|
1193
|
+
//
|
|
1194
|
+
// BEFORE narrowing: filtering survivors of `lookupAllByOwner`
|
|
1195
|
+
// (rather than survivors of `narrowOverloadCandidates`) means
|
|
1196
|
+
// a same-arity static + instance pair on one owner doesn't
|
|
1197
|
+
// collapse to `OVERLOAD_AMBIGUOUS`. Kotlin compile-resolves
|
|
1198
|
+
// such a pair unambiguously to the instance method because
|
|
1199
|
+
// companion members are not legal instance-dispatch
|
|
1200
|
+
// candidates.
|
|
1201
|
+
for (const ownerId of chain) {
|
|
1202
|
+
const picked = pickFirstNonStaticOnly(ownerId, memberName, site, model, provider);
|
|
1203
|
+
if (picked === exports.OVERLOAD_AMBIGUOUS) {
|
|
1204
|
+
ambiguous = true;
|
|
1205
|
+
ambiguousOwnerId = ownerId;
|
|
1206
|
+
break;
|
|
1207
|
+
}
|
|
1208
|
+
if (picked === STATIC_ONLY_FILTERED) {
|
|
1209
|
+
// At least one static-only candidate was filtered out at
|
|
1210
|
+
// this owner; remember so we can mark handled if the
|
|
1211
|
+
// chain ends with no legitimate match.
|
|
1212
|
+
allFilteredStaticOnly = true;
|
|
1213
|
+
continue;
|
|
1214
|
+
}
|
|
1215
|
+
if (picked !== undefined) {
|
|
1216
|
+
memberDef = picked;
|
|
1217
|
+
break;
|
|
1218
|
+
}
|
|
1219
|
+
// `picked === undefined` means this owner had no member of
|
|
1220
|
+
// this name at all. Walk on to the next ancestor in the
|
|
1221
|
+
// MRO chain.
|
|
1222
|
+
}
|
|
1223
|
+
if (ambiguous) {
|
|
1224
|
+
// Suppress and mark handled so `emitReferencesViaLookup`
|
|
1225
|
+
// doesn't re-emit the pre-resolved reference. See
|
|
1226
|
+
// OVERLOAD_AMBIGUOUS docstring for the upstream cause.
|
|
1227
|
+
recordReceiverOverloadSuppression(options.recordResolutionOutcome, parsed.filePath, site, ambiguousOwnerId ?? ownerDef.nodeId, memberName, model, provider);
|
|
1228
|
+
handledSites.add(siteKey);
|
|
1229
|
+
continue;
|
|
1230
|
+
}
|
|
1231
|
+
if (memberDef === undefined && allFilteredStaticOnly) {
|
|
1232
|
+
// The chain ended with no candidates because every viable
|
|
1233
|
+
// owner had only static-only members. Mark handled so
|
|
1234
|
+
// `emitReferencesViaLookup` doesn't re-emit a wrong target
|
|
1235
|
+
// from the pre-resolved reference index. Parallels the old
|
|
1236
|
+
// after-chain `isStaticOnly` suppression block.
|
|
1237
|
+
handledSites.add(siteKey);
|
|
1238
|
+
continue;
|
|
1239
|
+
}
|
|
1240
|
+
if (memberDef !== undefined) {
|
|
1241
|
+
if (suppressDeletedCallTarget(options.recordResolutionOutcome, parsed.filePath, site, memberDef)) {
|
|
1242
|
+
handledSites.add(siteKey);
|
|
1243
|
+
continue;
|
|
1244
|
+
}
|
|
1245
|
+
// For read/write ACCESSES, mirror the legacy DAG's reason
|
|
1246
|
+
// convention so consumers asserting `reason === 'write'`
|
|
1247
|
+
// keep working.
|
|
1248
|
+
const reason = site.kind === 'write' || site.kind === 'read'
|
|
1249
|
+
? site.kind
|
|
1250
|
+
: memberDef.filePath !== parsed.filePath
|
|
1251
|
+
? 'import-resolved'
|
|
1252
|
+
: 'global';
|
|
1253
|
+
const confidence = site.kind === 'write' || site.kind === 'read' ? 1.0 : 0.85;
|
|
1254
|
+
const ok = (0, edges_js_1.tryEmitEdge)(graph, scopes, nodeLookup, site, memberDef, reason, seen, confidence, collapse, calleeCapture);
|
|
1255
|
+
if (ok)
|
|
1256
|
+
emitted++;
|
|
1257
|
+
// Interface dispatch: when the primary owner is an
|
|
1258
|
+
// Interface, emit secondary CALLS edges to every
|
|
1259
|
+
// implementing class's same-named method.
|
|
1260
|
+
emitted += emitInterfaceDispatchFor(ownerDef, memberName, memberDef, site, confidence, calleeCapture);
|
|
1261
|
+
// Always mark handled when the site was resolved, even
|
|
1262
|
+
// if the edge was deduplicated (collapse mode), so
|
|
1263
|
+
// `emitReferencesViaLookup` doesn't re-emit from the
|
|
1264
|
+
// reference index.
|
|
1265
|
+
handledSites.add(siteKey);
|
|
1266
|
+
continue;
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
// ── Case 5: value-receiver bridge (object-literal services) ──
|
|
1271
|
+
// When prior cases couldn't resolve the receiver as a class or
|
|
1272
|
+
// type binding, fall back to value-binding resolution. Covers:
|
|
1273
|
+
//
|
|
1274
|
+
// export const fooService = { getUser(id) {...} };
|
|
1275
|
+
// import { fooService } from './service';
|
|
1276
|
+
// fooService.getUser(id); // ← resolve here
|
|
1277
|
+
//
|
|
1278
|
+
// `fooService` is a `Const`/`Variable` (not class-like, no typeBinding
|
|
1279
|
+
// for unannotated literals), so Cases 2-4 skip it. Scope-resolution
|
|
1280
|
+
// defs for non-class values carry a synthetic id, so we translate to
|
|
1281
|
+
// the canonical graph node ID via `resolveDefGraphId` before owner-
|
|
1282
|
+
// indexed lookup — the parser writes the graph node ID as `ownerId`
|
|
1283
|
+
// on the method symbol-table entry to match.
|
|
1284
|
+
//
|
|
1285
|
+
// Object-literal methods do not carry a `qualifiedName` (no class
|
|
1286
|
+
// owner to seed it), so the picked def cannot round-trip through
|
|
1287
|
+
// `tryEmitEdge` → `resolveDefGraphId`. We use
|
|
1288
|
+
// `tryEmitEdgeWithExplicitTargetId` instead, passing `picked.nodeId`
|
|
1289
|
+
// directly — same dedup-key shape, collapse-flag honoring, and
|
|
1290
|
+
// caller resolution as `tryEmitEdge`.
|
|
1291
|
+
const valueDef = (0, walkers_js_1.findValueBindingInScope)(site.inScope, receiverName, scopes);
|
|
1292
|
+
if (valueDef !== undefined) {
|
|
1293
|
+
const ownerGraphId = (0, ids_js_1.resolveDefGraphId)(valueDef.filePath, valueDef, nodeLookup) ?? valueDef.nodeId;
|
|
1294
|
+
const picked = pickOverload(ownerGraphId, memberName, site, model, provider);
|
|
1295
|
+
if (picked === exports.OVERLOAD_AMBIGUOUS) {
|
|
1296
|
+
recordReceiverOverloadSuppression(options.recordResolutionOutcome, parsed.filePath, site, ownerGraphId, memberName, model, provider);
|
|
1297
|
+
handledSites.add(siteKey);
|
|
1298
|
+
continue;
|
|
1299
|
+
}
|
|
1300
|
+
if (picked !== undefined) {
|
|
1301
|
+
if (suppressDeletedCallTarget(options.recordResolutionOutcome, parsed.filePath, site, picked)) {
|
|
1302
|
+
handledSites.add(siteKey);
|
|
1303
|
+
continue;
|
|
1304
|
+
}
|
|
1305
|
+
// Static-only filter (#1756 / U3): unlike Case 4 there's no
|
|
1306
|
+
// MRO chain to walk here — Case 5 dispatches on a single
|
|
1307
|
+
// owner via `pickOverload`. When the picked candidate is
|
|
1308
|
+
// static-only (Kotlin companion-promoted), suppress the
|
|
1309
|
+
// edge entirely and mark the site handled so
|
|
1310
|
+
// `emitReferencesViaLookup` doesn't re-emit a wrong target
|
|
1311
|
+
// from the pre-resolved reference index. Matches the after-
|
|
1312
|
+
// chain handled-marker semantic used by Case 4's
|
|
1313
|
+
// all-filtered fall-through.
|
|
1314
|
+
if (provider.isStaticOnly?.(picked) === true) {
|
|
1315
|
+
handledSites.add(siteKey);
|
|
1316
|
+
continue;
|
|
1317
|
+
}
|
|
1318
|
+
const reason = site.kind === 'write' || site.kind === 'read'
|
|
1319
|
+
? site.kind
|
|
1320
|
+
: picked.filePath !== parsed.filePath
|
|
1321
|
+
? 'import-resolved'
|
|
1322
|
+
: 'global';
|
|
1323
|
+
const confidence = site.kind === 'write' || site.kind === 'read' ? 1.0 : 0.85;
|
|
1324
|
+
const ok = (0, edges_js_1.tryEmitEdgeWithExplicitTargetId)(graph, scopes, nodeLookup, site, picked.nodeId, reason, seen, confidence, collapse, calleeCapture);
|
|
1325
|
+
if (ok)
|
|
1326
|
+
emitted++;
|
|
1327
|
+
handledSites.add(siteKey);
|
|
1328
|
+
continue;
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
// ── Case 6: class-level (static) member receiver ─────────────
|
|
1332
|
+
// `Holder.repo.save(u)` — the receiver `Holder.repo` reaches a value
|
|
1333
|
+
// through a CLASS-LEVEL member. Both routes that type a compound
|
|
1334
|
+
// receiver (the structural fold and the text cascade) read the same
|
|
1335
|
+
// place for the `repo` hop: the owning class scope's `typeBindings`.
|
|
1336
|
+
// A scope has ONE `typeBindings` map with no static/instance split, so
|
|
1337
|
+
// a language that declares both `p` and `static p` cannot record both
|
|
1338
|
+
// — and at least two resolve that collision by not recording the
|
|
1339
|
+
// static one at all, leaving `Holder.repo` with nothing to type
|
|
1340
|
+
// against. A language that nests its class-level members in a scope of
|
|
1341
|
+
// their own (a companion/singleton body) lands in the same place from
|
|
1342
|
+
// the other direction: the binding exists, but not in the scope keyed
|
|
1343
|
+
// by the class the receiver names. Both were MEASURED as emitting no
|
|
1344
|
+
// edge at all, generic field and non-generic control alike.
|
|
1345
|
+
//
|
|
1346
|
+
// The definition side does not have that ambiguity: a class-level
|
|
1347
|
+
// member and an instance member of one name are two distinct defs, and
|
|
1348
|
+
// the graph node carries both `isStatic` and the member's declared
|
|
1349
|
+
// type. So this case types the receiver off the DEF rather than off a
|
|
1350
|
+
// typeBinding, and needs no scope-tree change to do it.
|
|
1351
|
+
//
|
|
1352
|
+
// ── WHY THIS CANNOT MINT A STATIC-TARGETED EDGE ────────────────────
|
|
1353
|
+
//
|
|
1354
|
+
// `Holder.repo` is a static FIELD whose TYPE is `Repo`; the value it
|
|
1355
|
+
// holds is an INSTANCE. So "reached through a class-level member" says
|
|
1356
|
+
// nothing about the target: `save` is looked up with the ordinary
|
|
1357
|
+
// `pickFirstNonStaticOnly` instance walk that Cases 0/3b/4 use, and a
|
|
1358
|
+
// static-only `save` is skipped exactly as it is there. A genuine
|
|
1359
|
+
// static CALL (`Repo.create()`) never arrives here — its receiver is a
|
|
1360
|
+
// bare class name with no dot, which Case 2 owns and this case's
|
|
1361
|
+
// two-part receiver requirement excludes.
|
|
1362
|
+
//
|
|
1363
|
+
// The `isStatic === true` requirement on the FIELD is the load-bearing
|
|
1364
|
+
// guard in the other direction: the head resolved to the class itself,
|
|
1365
|
+
// so only a class-level member is reachable through it, and an
|
|
1366
|
+
// instance field of the same name must not be substituted. That is a
|
|
1367
|
+
// POSITIVE selection among the defs that exist, never a filter that
|
|
1368
|
+
// deletes otherwise-valid targets — the distinction that matters for a
|
|
1369
|
+
// language whose singleton/companion members all carry `isStatic` from
|
|
1370
|
+
// their OWNER type, where the flag being set is precisely what makes
|
|
1371
|
+
// reaching them through the type name correct.
|
|
1372
|
+
//
|
|
1373
|
+
// Runs LAST, and only for a receiver Case 0 already declined
|
|
1374
|
+
// (`compoundReceiverUnresolved`): a site any earlier case resolved
|
|
1375
|
+
// keeps that answer, so this can only turn a missing edge into an edge
|
|
1376
|
+
// and never retarget an existing one. Contract Invariant I4 holds —
|
|
1377
|
+
// nothing above moved.
|
|
1378
|
+
if (compoundReceiverUnresolved) {
|
|
1379
|
+
const staticMemberReceiver = splitClassLevelMemberReceiver(receiverName, site.receiverChain);
|
|
1380
|
+
const headClass = staticMemberReceiver === undefined
|
|
1381
|
+
? undefined
|
|
1382
|
+
: (0, walkers_js_1.findClassBindingInScope)(site.inScope, staticMemberReceiver.headName, scopes);
|
|
1383
|
+
// The head must be the CLASS ITSELF, not a value that happens to
|
|
1384
|
+
// share its name — the same `currentIsClassConstant` test the text
|
|
1385
|
+
// cascade makes before it treats a head as a class constant. A head
|
|
1386
|
+
// with a type binding is an instance and its members are typed by
|
|
1387
|
+
// the routes above.
|
|
1388
|
+
if (staticMemberReceiver !== undefined &&
|
|
1389
|
+
headClass !== undefined &&
|
|
1390
|
+
(0, walkers_js_1.findReceiverTypeBinding)(site.inScope, staticMemberReceiver.headName, scopes) === undefined) {
|
|
1391
|
+
// MRO walk, so a class-level member declared on an ancestor is
|
|
1392
|
+
// reachable through a subclass name where the language allows it.
|
|
1393
|
+
// First owner that declares one wins, matching every other walk in
|
|
1394
|
+
// this pass.
|
|
1395
|
+
let fieldOwnerId;
|
|
1396
|
+
let fieldDeclaredType;
|
|
1397
|
+
for (const ownerId of [
|
|
1398
|
+
headClass.nodeId,
|
|
1399
|
+
...scopes.methodDispatch.mroFor(headClass.nodeId),
|
|
1400
|
+
]) {
|
|
1401
|
+
const declared = declaredTypeOfClassLevelField(ownerId, staticMemberReceiver.memberName);
|
|
1402
|
+
if (declared === undefined)
|
|
1403
|
+
continue;
|
|
1404
|
+
fieldOwnerId = ownerId;
|
|
1405
|
+
fieldDeclaredType = declared;
|
|
1406
|
+
break;
|
|
1407
|
+
}
|
|
1408
|
+
// Resolve the declared type from where it was WRITTEN — the
|
|
1409
|
+
// declaring class's own scope — not from the call site. A caller
|
|
1410
|
+
// in another file need not have the field's type in scope at all,
|
|
1411
|
+
// and resolving `Repo` against the caller's bindings would either
|
|
1412
|
+
// miss or, worse, find an unrelated same-named class.
|
|
1413
|
+
const declaringScope = fieldOwnerId === undefined ? undefined : index.classScopeByDefId.get(fieldOwnerId)?.id;
|
|
1414
|
+
const receiverClass = declaringScope === undefined || fieldDeclaredType === undefined
|
|
1415
|
+
? undefined
|
|
1416
|
+
: (0, walkers_js_1.resolveClassBindingForName)(declaringScope, fieldDeclaredType, scopes, provider.stripTypePreservingDecoration);
|
|
1417
|
+
if (receiverClass !== undefined) {
|
|
1418
|
+
const chain = [
|
|
1419
|
+
receiverClass.nodeId,
|
|
1420
|
+
...scopes.methodDispatch.mroFor(receiverClass.nodeId),
|
|
1421
|
+
];
|
|
1422
|
+
let memberDef;
|
|
1423
|
+
let ambiguousOwnerId;
|
|
1424
|
+
for (const ownerId of chain) {
|
|
1425
|
+
const picked = pickFirstNonStaticOnly(ownerId, memberName, site, model, provider);
|
|
1426
|
+
if (picked === exports.OVERLOAD_AMBIGUOUS) {
|
|
1427
|
+
ambiguousOwnerId = ownerId;
|
|
1428
|
+
break;
|
|
1429
|
+
}
|
|
1430
|
+
// Same skip-and-walk-on as Case 4: a static-only candidate at
|
|
1431
|
+
// this owner must not block an ancestor's instance member.
|
|
1432
|
+
if (picked === STATIC_ONLY_FILTERED || picked === undefined)
|
|
1433
|
+
continue;
|
|
1434
|
+
memberDef = picked;
|
|
1435
|
+
break;
|
|
1436
|
+
}
|
|
1437
|
+
if (ambiguousOwnerId !== undefined) {
|
|
1438
|
+
recordReceiverOverloadSuppression(options.recordResolutionOutcome, parsed.filePath, site, ambiguousOwnerId, memberName, model, provider);
|
|
1439
|
+
handledSites.add(siteKey);
|
|
1440
|
+
continue;
|
|
1441
|
+
}
|
|
1442
|
+
if (memberDef !== undefined) {
|
|
1443
|
+
if (suppressDeletedCallTarget(options.recordResolutionOutcome, parsed.filePath, site, memberDef)) {
|
|
1444
|
+
handledSites.add(siteKey);
|
|
1445
|
+
continue;
|
|
1446
|
+
}
|
|
1447
|
+
const reason = site.kind === 'write' || site.kind === 'read'
|
|
1448
|
+
? site.kind
|
|
1449
|
+
: memberDef.filePath !== parsed.filePath
|
|
1450
|
+
? 'import-resolved'
|
|
1451
|
+
: 'global';
|
|
1452
|
+
const confidence = site.kind === 'write' || site.kind === 'read' ? 1.0 : 0.85;
|
|
1453
|
+
const ok = (0, edges_js_1.tryEmitEdge)(graph, scopes, nodeLookup, site, memberDef, reason, seen, confidence, collapse, calleeCapture);
|
|
1454
|
+
if (ok)
|
|
1455
|
+
emitted++;
|
|
1456
|
+
// The receiver's declared type can be an Interface exactly as
|
|
1457
|
+
// in Cases 0/3b/4 — an interface-typed static field is the
|
|
1458
|
+
// canonical service-locator shape — so it fans out the same
|
|
1459
|
+
// way. Omitting it would make the static spelling emit fewer
|
|
1460
|
+
// targets than the identical instance field, which is the very
|
|
1461
|
+
// spelling-dependence #2829/#2842 closed elsewhere.
|
|
1462
|
+
emitted += emitInterfaceDispatchFor(receiverClass, memberName, memberDef, site, confidence, calleeCapture);
|
|
1463
|
+
handledSites.add(siteKey);
|
|
1464
|
+
continue;
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
// #2744: the site survived every case with a compound receiver we could
|
|
1470
|
+
// not type, so the call is dropped with no candidate. Record it here —
|
|
1471
|
+
// after the cases, so a site a later case resolved is never reported —
|
|
1472
|
+
// keyed by the MEMBER name, which is the only thing still known about a
|
|
1473
|
+
// dropped site (its callee is unknown by definition, so the drop cannot
|
|
1474
|
+
// be attributed to any target symbol).
|
|
1475
|
+
if (compoundReceiverUnresolved && !handledSites.has(siteKey)) {
|
|
1476
|
+
// Decoded once: both the shape census and the origin classifier read the
|
|
1477
|
+
// same chain, and this is inside the drop guard so a resolved site pays
|
|
1478
|
+
// nothing.
|
|
1479
|
+
const decodedChain = (0, receiver_chain_codec_js_1.decodeReceiverChain)(site.receiverChain);
|
|
1480
|
+
options.recordResolutionOutcome?.({
|
|
1481
|
+
kind: 'suppressed',
|
|
1482
|
+
reason: 'receiver-unresolved',
|
|
1483
|
+
candidateIds: [],
|
|
1484
|
+
phase: 'receiver-bound-calls',
|
|
1485
|
+
filePath: parsed.filePath,
|
|
1486
|
+
name: site.name,
|
|
1487
|
+
range: site.atRange,
|
|
1488
|
+
// The gate above tests the receiver's punctuation, not the site's
|
|
1489
|
+
// kind, so property reads and writes with a compound receiver are
|
|
1490
|
+
// recorded here too. Carry the kind so a consumer can separate a
|
|
1491
|
+
// dropped CALL from a dropped property access.
|
|
1492
|
+
siteKind: site.kind,
|
|
1493
|
+
// Structural, from the AST-derived chain the emitter minted — never
|
|
1494
|
+
// re-derived from the source line.
|
|
1495
|
+
// `decodeReceiverChain` opens with a non-string guard, so the
|
|
1496
|
+
// undefined case needs no ternary here.
|
|
1497
|
+
receiverShape: (0, resolution_outcome_js_1.classifyReceiverShape)(decodedChain),
|
|
1498
|
+
// Whether anything was actually lost. An external target has no node
|
|
1499
|
+
// to point at, so its absence is completeness, not uncertainty — but
|
|
1500
|
+
// ONLY a positive built-in match may say so. Everything the index
|
|
1501
|
+
// cannot demonstrate stays `unknown` and keeps hedging.
|
|
1502
|
+
receiverOrigin: classifyReceiverOrigin(decodedChain, site.inScope, receiverName, scopes, receiverOriginOpts),
|
|
1503
|
+
});
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
return { emitted, dispatchFanoutSkipped, dispatchFanoutSkippedNames };
|
|
1508
|
+
}
|
|
1509
|
+
/**
|
|
1510
|
+
* Split a receiver into `Head` + one member hop, or decline.
|
|
1511
|
+
*
|
|
1512
|
+
* The STRUCTURE decides when the capture layer minted a chain: exactly one
|
|
1513
|
+
* step, and that step a FIELD. A `call` step is a different shape entirely
|
|
1514
|
+
* (`Holder.make().save()` — the value comes from a return type, which the
|
|
1515
|
+
* routes above already own), and an `await`/`index` step transforms the value
|
|
1516
|
+
* in a way a field's declared type does not describe.
|
|
1517
|
+
*
|
|
1518
|
+
* Without a chain the receiver TEXT answers, and only in the one spelling that
|
|
1519
|
+
* cannot be read two ways: two bare identifiers around a single dot. Anything
|
|
1520
|
+
* carrying a call, a subscript, a second dot or a decoration declines rather
|
|
1521
|
+
* than being parsed here — re-deriving structure from text is what the chain
|
|
1522
|
+
* exists to replace, and a second, looser text parser beside the cascade's own
|
|
1523
|
+
* would drift from it.
|
|
1524
|
+
*/
|
|
1525
|
+
function splitClassLevelMemberReceiver(receiverText, receiverChain) {
|
|
1526
|
+
const decoded = (0, receiver_chain_codec_js_1.decodeReceiverChain)(receiverChain);
|
|
1527
|
+
if (decoded !== undefined) {
|
|
1528
|
+
if (decoded.truncated || decoded.steps.length !== 1)
|
|
1529
|
+
return undefined;
|
|
1530
|
+
const step = decoded.steps[0];
|
|
1531
|
+
if (step === undefined || step.kind !== 'field')
|
|
1532
|
+
return undefined;
|
|
1533
|
+
return { headName: decoded.baseReceiverName, memberName: step.name };
|
|
1534
|
+
}
|
|
1535
|
+
const match = TWO_PART_RECEIVER_RE.exec(receiverText);
|
|
1536
|
+
if (match === null)
|
|
1537
|
+
return undefined;
|
|
1538
|
+
const [, headName, memberName] = match;
|
|
1539
|
+
if (headName === undefined || memberName === undefined)
|
|
1540
|
+
return undefined;
|
|
1541
|
+
return { headName, memberName };
|
|
1542
|
+
}
|
|
1543
|
+
/** `Holder.repo` and nothing looser — see {@link splitClassLevelMemberReceiver}. */
|
|
1544
|
+
const TWO_PART_RECEIVER_RE = /^([A-Za-z_$][\w$]*)\.([A-Za-z_$][\w$]*)$/;
|
|
1545
|
+
/** Resolve a member by name on a class def, narrowing by argument
|
|
1546
|
+
* types when multiple overloads share the name. Falls back to the
|
|
1547
|
+
* first-seen def (legacy `findOwnedMember` semantics) when there's
|
|
1548
|
+
* no narrowing signal or when `argumentTypes` is unavailable. */
|
|
1549
|
+
function pickOverload(ownerId, memberName, site, model, provider) {
|
|
1550
|
+
const overloads = model.methods.lookupAllByOwner(ownerId, memberName);
|
|
1551
|
+
if (overloads.length === 0) {
|
|
1552
|
+
// Non-callable member (field / property / variable) — ACCESSES
|
|
1553
|
+
// write/read sites target these too. Fall back to the field
|
|
1554
|
+
// registry so owner-scoped attribute access resolves.
|
|
1555
|
+
return model.fields.lookupFieldByOwner(ownerId, memberName);
|
|
1556
|
+
}
|
|
1557
|
+
if (overloads.length === 1)
|
|
1558
|
+
return overloads[0];
|
|
1559
|
+
const candidates = (0, overload_narrowing_js_1.narrowOverloadCandidates)(overloads, site.arity, site.argumentTypes, {
|
|
1560
|
+
argumentTypeClasses: site.argumentTypeClasses,
|
|
1561
|
+
conversionRankFn: provider.conversionRankFn,
|
|
1562
|
+
conversionOnlyArgTypePrefixes: provider.conversionOnlyArgTypePrefixes,
|
|
1563
|
+
constraintCompatibility: provider.constraintCompatibility,
|
|
1564
|
+
});
|
|
1565
|
+
// When narrowing leaves >1 candidate that share identical normalized
|
|
1566
|
+
// parameter-types (e.g., C++ `f(int)` vs `f(long)` both collapsed to
|
|
1567
|
+
// `['int']` by `normalizeCppParamType`), suppress the edge entirely.
|
|
1568
|
+
// The graph schema has no ambiguous-target edge model, so emitting one
|
|
1569
|
+
// would arbitrarily pick a candidate and lie about the call's target.
|
|
1570
|
+
// PR #1520 review follow-up plan U2 / Claude review Finding 5.
|
|
1571
|
+
if ((0, overload_narrowing_js_1.isOverloadAmbiguousAfterNormalization)(candidates, site.arity))
|
|
1572
|
+
return exports.OVERLOAD_AMBIGUOUS;
|
|
1573
|
+
// When conversion-rank scoring leaves >1 tied candidate with distinct
|
|
1574
|
+
// parameter types (e.g. h(int,double) vs h(double,int) both scoring 2),
|
|
1575
|
+
// suppress rather than picking arbitrarily — C++ would call this
|
|
1576
|
+
// ambiguous. Mirrors ADL merged-candidate suppression behavior.
|
|
1577
|
+
if (candidates.length > 1)
|
|
1578
|
+
return exports.OVERLOAD_AMBIGUOUS;
|
|
1579
|
+
return candidates[0] ?? overloads[0];
|
|
1580
|
+
}
|
|
1581
|
+
/**
|
|
1582
|
+
* Sentinel returned by `pickOverload` when narrowing leaves >1 candidate
|
|
1583
|
+
* sharing identical normalized parameter-types. Callers should suppress
|
|
1584
|
+
* the CALLS edge AND mark the site as handled so `emitReferencesViaLookup`
|
|
1585
|
+
* does not re-emit from the pre-resolved reference index. See
|
|
1586
|
+
* `pickOverload` JSDoc for the upstream cause (per-language normalizer
|
|
1587
|
+
* collapses distinct types in arity-metadata).
|
|
1588
|
+
*/
|
|
1589
|
+
exports.OVERLOAD_AMBIGUOUS = Symbol('overload-ambiguous');
|
|
1590
|
+
/**
|
|
1591
|
+
* Sentinel returned by `pickFirstNonStaticOnly` when the only candidates
|
|
1592
|
+
* at the queried owner were filtered out by `provider.isStaticOnly`. Lets
|
|
1593
|
+
* the Case 4 chain walk distinguish "owner had no member of this name"
|
|
1594
|
+
* (return `undefined`, continue silently) from "owner had only static-
|
|
1595
|
+
* only members" (return this sentinel, continue and remember so the
|
|
1596
|
+
* post-chain handled-marker logic can suppress wrong-target re-emission
|
|
1597
|
+
* from `emitReferencesViaLookup`). See #1756 / remediation plan U2.
|
|
1598
|
+
*/
|
|
1599
|
+
const STATIC_ONLY_FILTERED = Symbol('static-only-filtered');
|
|
1600
|
+
/**
|
|
1601
|
+
* Receiver-bound member lookup that filters static-only candidates BEFORE
|
|
1602
|
+
* arity narrowing. Wraps the raw `lookupAllByOwner` → `narrowOverloadCandidates`
|
|
1603
|
+
* pipeline so:
|
|
1604
|
+
*
|
|
1605
|
+
* 1. Candidates flagged by `provider.isStaticOnly` (Kotlin companion-
|
|
1606
|
+
* promoted methods today) never enter the narrowing stage. A same-
|
|
1607
|
+
* name same-arity static + instance pair on one owner therefore does
|
|
1608
|
+
* NOT collapse to `OVERLOAD_AMBIGUOUS` — the instance member wins
|
|
1609
|
+
* unambiguously, matching Kotlin's compile-time resolution.
|
|
1610
|
+
* 2. The chain walk in `emitReceiverBoundCalls` Case 4 can fall through
|
|
1611
|
+
* to ancestors when only static-only candidates exist at the
|
|
1612
|
+
* most-derived owner (returns `STATIC_ONLY_FILTERED`), rather than
|
|
1613
|
+
* aborting the site as the previous after-chain filter did.
|
|
1614
|
+
*
|
|
1615
|
+
* Returns:
|
|
1616
|
+
* - `undefined` — no member with this name on this owner; chain walk
|
|
1617
|
+
* continues silently.
|
|
1618
|
+
* - `STATIC_ONLY_FILTERED` — at least one candidate existed but every
|
|
1619
|
+
* one was static-only; chain walk continues and remembers so the
|
|
1620
|
+
* post-chain handled-marker can fire if no ancestor binds.
|
|
1621
|
+
* - `OVERLOAD_AMBIGUOUS` — narrowing on the surviving non-static
|
|
1622
|
+
* candidates left >1 ambiguous match; chain walk aborts and the
|
|
1623
|
+
* site is marked handled (existing sentinel handling preserved).
|
|
1624
|
+
* - `SymbolDefinition` — single survivor (the chosen target).
|
|
1625
|
+
*
|
|
1626
|
+
* See remediation plan `docs/plans/2026-05-22-002-fix-lang-kotlin-1782-
|
|
1627
|
+
* remediation-plan.md` § U2 for the full rationale.
|
|
1628
|
+
*/
|
|
1629
|
+
function pickFirstNonStaticOnly(ownerId, memberName, site, model, provider) {
|
|
1630
|
+
const rawOverloads = model.methods.lookupAllByOwner(ownerId, memberName);
|
|
1631
|
+
if (rawOverloads.length === 0) {
|
|
1632
|
+
// Non-callable member (field / property / variable) — ACCESSES
|
|
1633
|
+
// write/read sites target these too. Static-only filtering doesn't
|
|
1634
|
+
// apply to fields, so delegate straight to `lookupFieldByOwner`.
|
|
1635
|
+
return model.fields.lookupFieldByOwner(ownerId, memberName);
|
|
1636
|
+
}
|
|
1637
|
+
const isStaticOnly = provider.isStaticOnly;
|
|
1638
|
+
let overloads = rawOverloads;
|
|
1639
|
+
let filteredAny = false;
|
|
1640
|
+
if (isStaticOnly !== undefined) {
|
|
1641
|
+
const survivors = [];
|
|
1642
|
+
for (const candidate of rawOverloads) {
|
|
1643
|
+
if (isStaticOnly(candidate) === true) {
|
|
1644
|
+
filteredAny = true;
|
|
1645
|
+
continue;
|
|
1646
|
+
}
|
|
1647
|
+
survivors.push(candidate);
|
|
1648
|
+
}
|
|
1649
|
+
overloads = survivors;
|
|
1650
|
+
}
|
|
1651
|
+
if (overloads.length === 0) {
|
|
1652
|
+
// Every candidate was static-only; the caller (Case 4 chain walk)
|
|
1653
|
+
// should walk on to the next owner AND remember that filtering
|
|
1654
|
+
// happened so it can mark the site handled if the whole chain
|
|
1655
|
+
// ends with no legitimate match.
|
|
1656
|
+
return filteredAny ? STATIC_ONLY_FILTERED : undefined;
|
|
1657
|
+
}
|
|
1658
|
+
if (overloads.length === 1)
|
|
1659
|
+
return overloads[0];
|
|
1660
|
+
const candidates = (0, overload_narrowing_js_1.narrowOverloadCandidates)(overloads, site.arity, site.argumentTypes, {
|
|
1661
|
+
argumentTypeClasses: site.argumentTypeClasses,
|
|
1662
|
+
conversionRankFn: provider.conversionRankFn,
|
|
1663
|
+
conversionOnlyArgTypePrefixes: provider.conversionOnlyArgTypePrefixes,
|
|
1664
|
+
constraintCompatibility: provider.constraintCompatibility,
|
|
1665
|
+
});
|
|
1666
|
+
// Same ambiguity handling as `pickOverload`: when normalization
|
|
1667
|
+
// collapses the surviving overloads into a single bucket (e.g., C++
|
|
1668
|
+
// `f(int)`/`f(long)` normalized to `['int']`), suppress rather than
|
|
1669
|
+
// arbitrarily picking. When narrowing leaves >1 distinct candidate
|
|
1670
|
+
// with no tie-breaker, suppress for the same reason.
|
|
1671
|
+
if ((0, overload_narrowing_js_1.isOverloadAmbiguousAfterNormalization)(candidates, site.arity))
|
|
1672
|
+
return exports.OVERLOAD_AMBIGUOUS;
|
|
1673
|
+
if (candidates.length > 1)
|
|
1674
|
+
return exports.OVERLOAD_AMBIGUOUS;
|
|
1675
|
+
return candidates[0] ?? overloads[0];
|
|
1676
|
+
}
|
|
1677
|
+
function suppressDeletedCallTarget(record, filePath, site, target) {
|
|
1678
|
+
if (site.kind !== 'call' || target.isDeleted !== true)
|
|
1679
|
+
return false;
|
|
1680
|
+
record?.({
|
|
1681
|
+
kind: 'suppressed',
|
|
1682
|
+
phase: 'receiver-bound-calls',
|
|
1683
|
+
filePath,
|
|
1684
|
+
name: site.name,
|
|
1685
|
+
range: site.atRange,
|
|
1686
|
+
reason: 'selected-callable-deleted',
|
|
1687
|
+
candidateIds: [target.nodeId],
|
|
1688
|
+
});
|
|
1689
|
+
return true;
|
|
1690
|
+
}
|
|
1691
|
+
function recordReceiverOverloadSuppression(record, filePath, site, ownerId, memberName, model, provider) {
|
|
1692
|
+
if (record === undefined)
|
|
1693
|
+
return;
|
|
1694
|
+
const overloads = model.methods.lookupAllByOwner(ownerId, memberName);
|
|
1695
|
+
const candidates = (0, overload_narrowing_js_1.narrowOverloadCandidates)(overloads, site.arity, site.argumentTypes, {
|
|
1696
|
+
argumentTypeClasses: site.argumentTypeClasses,
|
|
1697
|
+
conversionRankFn: provider.conversionRankFn,
|
|
1698
|
+
conversionOnlyArgTypePrefixes: provider.conversionOnlyArgTypePrefixes,
|
|
1699
|
+
constraintCompatibility: provider.constraintCompatibility,
|
|
1700
|
+
});
|
|
1701
|
+
const reason = (0, overload_narrowing_js_1.isOverloadAmbiguousAfterNormalization)(candidates, site.arity)
|
|
1702
|
+
? 'overload-ambiguous-normalization'
|
|
1703
|
+
: hasConversionRankingSignal(site, provider)
|
|
1704
|
+
? 'conversion-rank-tied'
|
|
1705
|
+
: 'overload-ambiguous';
|
|
1706
|
+
record({
|
|
1707
|
+
kind: 'suppressed',
|
|
1708
|
+
phase: 'receiver-bound-calls',
|
|
1709
|
+
filePath,
|
|
1710
|
+
name: site.name,
|
|
1711
|
+
range: site.atRange,
|
|
1712
|
+
reason,
|
|
1713
|
+
candidateIds: candidates.map((d) => d.nodeId),
|
|
1714
|
+
});
|
|
1715
|
+
}
|
|
1716
|
+
function hasConversionRankingSignal(site, provider) {
|
|
1717
|
+
return (provider.conversionRankFn !== undefined &&
|
|
1718
|
+
site.argumentTypes !== undefined &&
|
|
1719
|
+
site.argumentTypes.length > 0);
|
|
1720
|
+
}
|