cgraphx 2.0.2 → 2.0.6
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 +13 -3
- package/dist/.claude-template/agents/task-history.md +186 -0
- package/dist/.claude-template/commands/end.md +1 -0
- package/dist/.claude-template/skills/cgraphx-guide/SKILL.md +5 -3
- package/dist/.claude-template/skills/cgraphx-guide/how-to-use.html +8 -8
- package/dist/.claude-template/skills/cgraphx-guide/how-to-use.md +8 -8
- package/dist/.claude-template/skills/clarify-requirements/SKILL.md +13 -9
- package/dist/.claude-template/skills/db-query/SKILL.md +19 -2
- package/dist/.claude-template/skills/iboc-check/SKILL.md +51 -23
- package/dist/.claude-template/skills/iboc-check/filler-prompt.md +3 -4
- package/dist/.claude-template/skills/run-api-test/SKILL.md +15 -15
- package/dist/.claude-template/skills/run-api-test/assets/{template-test-report.md → template-/346/216/245/345/217/243/346/265/213/350/257/225/346/212/245/345/221/212.md} +4 -4
- package/dist/.claude-template/skills/run-api-test/references/bru-run.md +1 -1
- package/dist/.claude-template/skills/run-api-test/references/db-verification.md +2 -2
- package/dist/.claude-template/skills/run-api-test/references/report-format.md +6 -6
- package/dist/.claude-template/skills/run-api-test/references/service-readiness.md +1 -1
- package/dist/.claude-template/skills/run-api-test/references/test-scope.md +6 -6
- package/dist/.claude-template/skills/run-ui-test/SKILL.md +186 -0
- package/dist/.claude-template/skills/run-ui-test/references/report-format.md +77 -0
- package/dist/.claude-template/skills/subagent-implement/SKILL.md +3 -2
- package/dist/.claude-template/skills/write-api/SKILL.md +18 -18
- package/dist/.claude-template/skills/write-api/assets/{template-api-spec.md → template-/346/216/245/345/217/243/346/265/213/350/257/225-spec.md} +1 -1
- package/dist/.claude-template/skills/write-api/references/{api-spec-format.md → /346/216/245/345/217/243/346/265/213/350/257/225-spec-format.md} +3 -3
- package/dist/.claude-template/skills/write-plan/SKILL.md +4 -2
- package/dist/.claude-template/skills/write-ui-test/SKILL.md +125 -0
- package/dist/.claude-template/skills/write-ui-test/references//347/224/250/344/276/213/350/247/204/346/240/274-format.md +94 -0
- 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/dbquery/cli.d.ts.map +1 -1
- package/dist/core/dbquery/cli.js +5 -4
- package/dist/core/dbquery/cli.js.map +1 -1
- package/dist/core/dbquery/config.d.ts.map +1 -1
- package/dist/core/dbquery/config.js +39 -3
- package/dist/core/dbquery/config.js.map +1 -1
- package/dist/core/dbquery/drivers/http.d.ts +23 -0
- package/dist/core/dbquery/drivers/http.d.ts.map +1 -0
- package/dist/core/dbquery/drivers/http.js +125 -0
- package/dist/core/dbquery/drivers/http.js.map +1 -0
- package/dist/core/dbquery/drivers/mysql.js +1 -1
- package/dist/core/dbquery/drivers/mysql.js.map +1 -1
- package/dist/core/dbquery/drivers/postgres.js +1 -1
- package/dist/core/dbquery/drivers/postgres.js.map +1 -1
- package/dist/core/dbquery/executor.d.ts.map +1 -1
- package/dist/core/dbquery/executor.js +46 -12
- package/dist/core/dbquery/executor.js.map +1 -1
- package/dist/core/dbquery/format.d.ts.map +1 -1
- package/dist/core/dbquery/format.js +2 -1
- package/dist/core/dbquery/format.js.map +1 -1
- package/dist/core/dbquery/init.d.ts.map +1 -1
- package/dist/core/dbquery/init.js +7 -0
- package/dist/core/dbquery/init.js.map +1 -1
- package/dist/core/dbquery/types.d.ts +55 -7
- package/dist/core/dbquery/types.d.ts.map +1 -1
- package/dist/core/directory.d.ts +17 -0
- package/dist/core/directory.d.ts.map +1 -1
- package/dist/core/directory.js +37 -0
- package/dist/core/directory.js.map +1 -1
- package/dist/core/features/cli.d.ts.map +1 -1
- package/dist/core/features/cli.js +3 -2
- package/dist/core/features/cli.js.map +1 -1
- package/dist/core/features/scanner.d.ts +1 -1
- package/dist/core/features/scanner.d.ts.map +1 -1
- package/dist/core/features/scanner.js +20 -4
- package/dist/core/features/scanner.js.map +1 -1
- package/dist/core/markdown/cli.d.ts.map +1 -1
- package/dist/core/markdown/cli.js +9 -8
- package/dist/core/markdown/cli.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/dist/web/register.d.ts.map +1 -1
- package/dist/web/register.js +2 -1
- package/dist/web/register.js.map +1 -1
- package/package.json +1 -1
- package/dist/.claude-template/.cgraphx/timeline-current-turn.json +0 -1
- package/dist/.claude-template/skills/.cgraphx/timeline-current-turn.json +0 -1
- package/dist/.claude-template/skills/handdrawn-deck/.cgraphx/timeline-current-turn.json +0 -1
- package/dist/.claude-template/skills/handdrawn-deck/references/.cgraphx/timeline-current-turn.json +0 -1
- package/dist/.claude-template/skills/handdrawn-sketch-engine/.cgraphx/timeline-current-turn.json +0 -1
- /package/dist/.claude-template/skills/run-api-test/assets/{template-test-verify.jsonl → template-/346/216/245/345/217/243/346/265/213/350/257/225/351/252/214/350/257/201.jsonl"} +0 -0
|
@@ -0,0 +1,1835 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Scope-chain lookup primitives shared across language providers.
|
|
4
|
+
*
|
|
5
|
+
* Five functions:
|
|
6
|
+
* - `findReceiverTypeBinding` — walk scope.typeBindings up the chain
|
|
7
|
+
* for a receiver name.
|
|
8
|
+
* - `lookupBindingsAt` — read finalized + augmented binding refs at
|
|
9
|
+
* one scope, deduped by `def.nodeId`. The dual-source-aware
|
|
10
|
+
* primitive every other binding lookup composes with.
|
|
11
|
+
* - `findClassBindingInScope` — walk scope.bindings + the indexes via
|
|
12
|
+
* `lookupBindingsAt` for a class-kind binding.
|
|
13
|
+
* - `findOwnedMember` — find a method/field owned by a class def
|
|
14
|
+
* across all parsed files by (ownerId, simpleName).
|
|
15
|
+
* - `findExportedDef` — find a file-level exported def (top-of-module
|
|
16
|
+
* class / function) by simpleName.
|
|
17
|
+
*
|
|
18
|
+
* Next-consumer contract: every OO or module-capable language hits the
|
|
19
|
+
* same pre-finalize / post-finalize binding split and the same
|
|
20
|
+
* "resolve member on owner with MRO" pattern. All four are reusable
|
|
21
|
+
* as-is for TypeScript, Java, Kotlin, Ruby, etc.
|
|
22
|
+
*/
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.lookupBindingsAt = lookupBindingsAt;
|
|
25
|
+
exports.namesAtScope = namesAtScope;
|
|
26
|
+
exports.isClassLike = isClassLike;
|
|
27
|
+
exports.isReceiverOwnedButUnbound = isReceiverOwnedButUnbound;
|
|
28
|
+
exports.isNamespaceNameShadowed = isNamespaceNameShadowed;
|
|
29
|
+
exports.findReceiverTypeBinding = findReceiverTypeBinding;
|
|
30
|
+
exports.namespaceTypeBindingFor = namespaceTypeBindingFor;
|
|
31
|
+
exports.moduleScopeIdOf = moduleScopeIdOf;
|
|
32
|
+
exports.findAllClassBindingsInScope = findAllClassBindingsInScope;
|
|
33
|
+
exports.findClassBindingInScope = findClassBindingInScope;
|
|
34
|
+
exports.resolveClassBindingForName = resolveClassBindingForName;
|
|
35
|
+
exports.resolveInheritanceBaseInScope = resolveInheritanceBaseInScope;
|
|
36
|
+
exports.resolveAmbiguousInheritanceBaseViaImports = resolveAmbiguousInheritanceBaseViaImports;
|
|
37
|
+
exports.isOwnableValueLabel = isOwnableValueLabel;
|
|
38
|
+
exports.findValueBindingInScope = findValueBindingInScope;
|
|
39
|
+
exports.findCallableBindingInScope = findCallableBindingInScope;
|
|
40
|
+
exports.findAllCallableBindingCandidatesInScope = findAllCallableBindingCandidatesInScope;
|
|
41
|
+
exports.findAllCallableBindingsInScope = findAllCallableBindingsInScope;
|
|
42
|
+
exports.findCallableBindingsAndAdlBlocker = findCallableBindingsAndAdlBlocker;
|
|
43
|
+
exports.populateClassOwnedMembers = populateClassOwnedMembers;
|
|
44
|
+
exports.tagNamespacePrefixes = tagNamespacePrefixes;
|
|
45
|
+
exports.findEnclosingClassDef = findEnclosingClassDef;
|
|
46
|
+
exports.findExportedDefByName = findExportedDefByName;
|
|
47
|
+
exports.findOwnedMember = findOwnedMember;
|
|
48
|
+
exports.findExportedDef = findExportedDef;
|
|
49
|
+
const qualified_name_js_1 = require("../../utils/qualified-name.js");
|
|
50
|
+
const template_arguments_js_1 = require("../../utils/template-arguments.js");
|
|
51
|
+
const EMPTY_BINDINGS = Object.freeze([]);
|
|
52
|
+
/**
|
|
53
|
+
* Look up binding refs at `scopeId` for `name`, consulting both the
|
|
54
|
+
* finalize-owned `bindings` channel and the post-finalize
|
|
55
|
+
* `bindingAugmentations` channel (see invariant I8 in
|
|
56
|
+
* `contract/scope-resolver.ts`). Finalized refs come first; augmented
|
|
57
|
+
* refs append, deduped by `def.nodeId` so a sibling that's also
|
|
58
|
+
* explicitly imported doesn't double-emit.
|
|
59
|
+
*
|
|
60
|
+
* Returns a shared frozen empty array when neither channel has the
|
|
61
|
+
* name — callers can compare against `=== EMPTY_BINDINGS` if they
|
|
62
|
+
* want a fast-path miss check. The bucket arrays are returned by
|
|
63
|
+
* reference when only one channel populates them; the merged path
|
|
64
|
+
* allocates a fresh array.
|
|
65
|
+
*
|
|
66
|
+
* Walker primitives (`findClassBindingInScope`,
|
|
67
|
+
* `findCallableBindingInScope`, `findExportedDefByName`) and
|
|
68
|
+
* post-finalize passes that read finalized bindings (e.g.
|
|
69
|
+
* `propagateImportedReturnTypes`, `namespace-targets`) MUST go
|
|
70
|
+
* through this helper instead of `scopes.bindings.get(...)` directly,
|
|
71
|
+
* so the augmentation channel is always visible.
|
|
72
|
+
*/
|
|
73
|
+
function lookupBindingsAt(scopeId, name, scopes) {
|
|
74
|
+
const finalized = scopes.bindings.get(scopeId)?.get(name);
|
|
75
|
+
const augmented = scopes.bindingAugmentations.get(scopeId)?.get(name);
|
|
76
|
+
const workspace = scopes.workspaceFqnBindings?.get(name);
|
|
77
|
+
// Per-namespace channel (#1871 named-namespace generalization). Gated by
|
|
78
|
+
// accessibility: only a *module* scope carries an `accessibleNamespacesByScope`
|
|
79
|
+
// entry, so this collects nothing at child scopes and at module scopes only for
|
|
80
|
+
// the namespaces that file can see. Empty (no entry) for every non-C# bundle,
|
|
81
|
+
// so the behavior of the three pre-existing channels is unchanged.
|
|
82
|
+
const namespaceRefs = collectNamespaceFqnBindings(scopeId, name, scopes);
|
|
83
|
+
const fLen = finalized?.length ?? 0;
|
|
84
|
+
const aLen = augmented?.length ?? 0;
|
|
85
|
+
const wLen = workspace?.length ?? 0;
|
|
86
|
+
const nLen = namespaceRefs?.length ?? 0;
|
|
87
|
+
if (fLen === 0 && aLen === 0 && wLen === 0 && nLen === 0)
|
|
88
|
+
return EMPTY_BINDINGS;
|
|
89
|
+
if (aLen === 0 && wLen === 0 && nLen === 0)
|
|
90
|
+
return finalized;
|
|
91
|
+
if (fLen === 0 && wLen === 0 && nLen === 0)
|
|
92
|
+
return augmented;
|
|
93
|
+
if (fLen === 0 && aLen === 0 && nLen === 0)
|
|
94
|
+
return workspace;
|
|
95
|
+
if (fLen === 0 && aLen === 0 && wLen === 0)
|
|
96
|
+
return namespaceRefs;
|
|
97
|
+
// Merge in precedence order, deduped by `def.nodeId` so the strongest source
|
|
98
|
+
// wins duplicate metadata. Named-namespace refs come BEFORE the flat global
|
|
99
|
+
// `workspace` channel: pre-#1871 these lived in `bindingAugmentations` (which
|
|
100
|
+
// `lookupBindingsAt` already ranks above `workspaceFqnBindings`), so a name in
|
|
101
|
+
// both an accessible named namespace and the global namespace must still
|
|
102
|
+
// resolve named-first. Order: finalized > augmented > namespace > workspace.
|
|
103
|
+
const seen = new Set();
|
|
104
|
+
const out = [];
|
|
105
|
+
for (const src of [finalized, augmented, namespaceRefs, workspace]) {
|
|
106
|
+
if (src === undefined)
|
|
107
|
+
continue;
|
|
108
|
+
for (const r of src) {
|
|
109
|
+
if (seen.has(r.def.nodeId))
|
|
110
|
+
continue;
|
|
111
|
+
seen.add(r.def.nodeId);
|
|
112
|
+
out.push(r);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Collect `BindingRef`s for `name` from the per-namespace channel
|
|
119
|
+
* (`namespaceFqnBindings`) across every namespace accessible from `scopeId`.
|
|
120
|
+
* Accessibility comes from `accessibleNamespacesByScope`, which is keyed by
|
|
121
|
+
* *module* scope id — so this returns `undefined` at non-module scopes and at
|
|
122
|
+
* every scope in a bundle that didn't populate the channel (all non-C# today).
|
|
123
|
+
* Language-neutral: keyed only by namespace strings and the index.
|
|
124
|
+
*/
|
|
125
|
+
function collectNamespaceFqnBindings(scopeId, name, scopes) {
|
|
126
|
+
const namespaces = scopes.accessibleNamespacesByScope?.get(scopeId);
|
|
127
|
+
if (namespaces === undefined || namespaces.length === 0)
|
|
128
|
+
return undefined;
|
|
129
|
+
let collected;
|
|
130
|
+
for (const ns of namespaces) {
|
|
131
|
+
const bucket = scopes.namespaceFqnBindings?.get(ns)?.get(name);
|
|
132
|
+
if (bucket !== undefined && bucket.length > 0) {
|
|
133
|
+
if (collected === undefined)
|
|
134
|
+
collected = [];
|
|
135
|
+
for (const r of bucket)
|
|
136
|
+
collected.push(r);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return collected;
|
|
140
|
+
}
|
|
141
|
+
const EMPTY_NAMES = Object.freeze([]);
|
|
142
|
+
/**
|
|
143
|
+
* Return the union of bound names at `scopeId` across both the
|
|
144
|
+
* finalized and augmented channels. Companion to `lookupBindingsAt`
|
|
145
|
+
* for callers that need to iterate every name at a scope (e.g.
|
|
146
|
+
* `propagateImportedReturnTypes`). Order is not guaranteed; callers
|
|
147
|
+
* that need stable iteration should sort externally.
|
|
148
|
+
*
|
|
149
|
+
* Fast paths (zero allocation) when at most one channel is populated:
|
|
150
|
+
* returns the underlying `Map.keys()` iterator directly. Only when both
|
|
151
|
+
* channels carry names do we materialize a `Set` for deduplication.
|
|
152
|
+
*
|
|
153
|
+
* Scope: enumerates only the per-scope `bindings` and `bindingAugmentations`
|
|
154
|
+
* channels. It deliberately EXCLUDES the scope-independent
|
|
155
|
+
* `workspaceFqnBindings` channel (PHP FQN keys, C# global-namespace simple
|
|
156
|
+
* names). `lookupBindingsAt` consults that third channel when resolving a
|
|
157
|
+
* specific name, but name *enumeration* here does not — those names apply at
|
|
158
|
+
* every scope and would flood per-scope callers. Callers that need
|
|
159
|
+
* workspace-level names must read `workspaceFqnBindings` directly.
|
|
160
|
+
*/
|
|
161
|
+
function namesAtScope(scopeId, scopes) {
|
|
162
|
+
const finalized = scopes.bindings.get(scopeId);
|
|
163
|
+
const augmented = scopes.bindingAugmentations.get(scopeId);
|
|
164
|
+
const fSize = finalized?.size ?? 0;
|
|
165
|
+
const aSize = augmented?.size ?? 0;
|
|
166
|
+
if (fSize === 0 && aSize === 0)
|
|
167
|
+
return EMPTY_NAMES;
|
|
168
|
+
if (aSize === 0)
|
|
169
|
+
return finalized.keys();
|
|
170
|
+
if (fSize === 0)
|
|
171
|
+
return augmented.keys();
|
|
172
|
+
const out = new Set(finalized.keys());
|
|
173
|
+
for (const name of augmented.keys())
|
|
174
|
+
out.add(name);
|
|
175
|
+
return out;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* True when a def's `type` names a class-like declaration — every kind
|
|
179
|
+
* that collapses to `@scope.class` in the scope-extractor query contract.
|
|
180
|
+
*
|
|
181
|
+
* Semantics widened historically from `'Class' | 'Interface'` to cover
|
|
182
|
+
* C#-shape languages (struct, record, enum, trait). Languages that emit
|
|
183
|
+
* only `'Class'` are unaffected — the extra kinds never appear in their
|
|
184
|
+
* parsed output.
|
|
185
|
+
*/
|
|
186
|
+
function isClassLike(t) {
|
|
187
|
+
return (t === 'Class' ||
|
|
188
|
+
t === 'Interface' ||
|
|
189
|
+
t === 'Struct' ||
|
|
190
|
+
t === 'Record' ||
|
|
191
|
+
t === 'Enum' ||
|
|
192
|
+
t === 'Trait');
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Walk the scope chain from `startScope` looking for a typeBinding
|
|
196
|
+
* named `receiverName`. Returns the TypeRef or undefined if no binding
|
|
197
|
+
* exists in the chain.
|
|
198
|
+
*
|
|
199
|
+
* A scope that declares `ownsReceivers.has(receiverName)` terminates the
|
|
200
|
+
* walk with `undefined` (#2701): it binds that receiver itself, so an
|
|
201
|
+
* enclosing scope's binding is not visible through it. The check runs
|
|
202
|
+
* AFTER this scope's own `typeBindings`, so a scope that both owns and
|
|
203
|
+
* binds the receiver — a class method, which is where `this` is bound TO
|
|
204
|
+
* the class — still resolves normally. The namespace/global fallbacks
|
|
205
|
+
* below are also skipped: they answer "which type is named X", which is a
|
|
206
|
+
* different question from "what is this scope's receiver", and reaching
|
|
207
|
+
* them for an owned-but-unbound receiver is how a static method or a
|
|
208
|
+
* detached callback acquires a fabricated one.
|
|
209
|
+
*/
|
|
210
|
+
/**
|
|
211
|
+
* True when `receiverName` is DEFINITIVELY unresolvable at `startScope`:
|
|
212
|
+
* a scope on the chain declares it owns that receiver (`Scope.ownsReceivers`)
|
|
213
|
+
* and carries no type binding for it (#2701).
|
|
214
|
+
*
|
|
215
|
+
* This is a stronger statement than `findReceiverTypeBinding` returning
|
|
216
|
+
* `undefined`, which only means "no type found" — an ordinary miss that later
|
|
217
|
+
* passes are free to resolve by other means. Here the language has said the
|
|
218
|
+
* receiver is REBOUND at this scope, so no enclosing type can be its type:
|
|
219
|
+
* `this.m()` inside a nested JS/TS `function` is a call on whatever the
|
|
220
|
+
* function is invoked with, which the graph does not model. A member call
|
|
221
|
+
* whose receiver is unresolvable in this sense must be suppressed rather
|
|
222
|
+
* than left to the receiver-blind lexical fallback in `lookupCore`, which
|
|
223
|
+
* would find the enclosing class's member by name alone.
|
|
224
|
+
*
|
|
225
|
+
* Returns false for every language that leaves `ownsReceivers` unset.
|
|
226
|
+
*/
|
|
227
|
+
function isReceiverOwnedButUnbound(startScope, receiverName, scopes) {
|
|
228
|
+
let currentId = startScope;
|
|
229
|
+
const visited = new Set();
|
|
230
|
+
while (currentId !== null) {
|
|
231
|
+
if (visited.has(currentId))
|
|
232
|
+
return false;
|
|
233
|
+
visited.add(currentId);
|
|
234
|
+
const scope = scopes.scopeTree.getScope(currentId);
|
|
235
|
+
if (scope === undefined)
|
|
236
|
+
return false;
|
|
237
|
+
if (scope.typeBindings.has(receiverName))
|
|
238
|
+
return false;
|
|
239
|
+
if (scope.ownsReceivers?.has(receiverName) === true)
|
|
240
|
+
return true;
|
|
241
|
+
currentId = scope.parent;
|
|
242
|
+
}
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* True when a declaration between the call site and its module scope shadows a
|
|
247
|
+
* file-level namespace import of the same name. Namespace targets are collected
|
|
248
|
+
* per FILE, so every consumer of that map must apply this lexical guard before
|
|
249
|
+
* trusting it at an inner scope — otherwise `def f(pkg): pkg.db.query()`
|
|
250
|
+
* resolves through the import that the parameter shadows, producing a wrong
|
|
251
|
+
* edge rather than a missing one.
|
|
252
|
+
*
|
|
253
|
+
* A namespace key may itself be a dotted import path (`pkg.db`, #2826), but the
|
|
254
|
+
* name a declaration can shadow is always the ROOT identifier — `pkg = Decoy()`
|
|
255
|
+
* shadows `pkg.db` too. Testing the whole dotted string would never match a
|
|
256
|
+
* binding, so the guard would silently stop guarding for exactly the keys it
|
|
257
|
+
* was extended to cover. Single-segment names are unaffected: their root is
|
|
258
|
+
* themselves.
|
|
259
|
+
*
|
|
260
|
+
* Fails closed (returns `true`) on a missing scope or a parent cycle: for every
|
|
261
|
+
* caller, suppressing a resolution costs a missing edge, while trusting a
|
|
262
|
+
* corrupt scope chain costs a wrong one.
|
|
263
|
+
*
|
|
264
|
+
* Reads `scope.bindings` DIRECTLY rather than through `lookupBindingsAt`, and
|
|
265
|
+
* that is deliberate — the opposite of the fix #2745 applied to Rust's
|
|
266
|
+
* `headBoundLocally`. There the question was "is this name bound at all?", so
|
|
267
|
+
* missing the finalized/augmented import channels lost real bindings. Here the
|
|
268
|
+
* question is "does something LOCAL shadow the import?", and the import's own
|
|
269
|
+
* finalized binding is the one thing that must NOT count: routing this through
|
|
270
|
+
* `lookupBindingsAt` would find the namespace import shadowing itself and
|
|
271
|
+
* suppress every namespace receiver in the workspace. Locals, parameters and
|
|
272
|
+
* lexical names all live in the scope's own tables, which is exactly the set
|
|
273
|
+
* this walk wants.
|
|
274
|
+
*/
|
|
275
|
+
function isNamespaceNameShadowed(namespaceName, inScope, scopes) {
|
|
276
|
+
const firstDot = namespaceName.indexOf('.');
|
|
277
|
+
const rootName = firstDot === -1 ? namespaceName : namespaceName.slice(0, firstDot);
|
|
278
|
+
let currentId = inScope;
|
|
279
|
+
const visited = new Set();
|
|
280
|
+
while (currentId !== null) {
|
|
281
|
+
if (visited.has(currentId))
|
|
282
|
+
return true;
|
|
283
|
+
visited.add(currentId);
|
|
284
|
+
const scope = scopes.scopeTree.getScope(currentId);
|
|
285
|
+
if (scope === undefined)
|
|
286
|
+
return true;
|
|
287
|
+
// Stop AT the module scope without inspecting it. In languages where a
|
|
288
|
+
// namespace import IS a variable declaration — CommonJS
|
|
289
|
+
// `const svc = require('./svc')` — the import puts its own name into the
|
|
290
|
+
// module scope's tables, so inspecting them reads the import as its own
|
|
291
|
+
// shadow and suppresses every receiver it was meant to enable (#2723).
|
|
292
|
+
// The contract is "a declaration BETWEEN the call site and its module
|
|
293
|
+
// scope", and the module scope is the floor, not a rung.
|
|
294
|
+
if (scope.kind === 'Module')
|
|
295
|
+
return false;
|
|
296
|
+
if (scope.kind !== 'Object' &&
|
|
297
|
+
(scope.bindings.has(rootName) ||
|
|
298
|
+
scope.typeBindings.has(rootName) ||
|
|
299
|
+
scope.lexicalNames?.has(rootName) === true ||
|
|
300
|
+
scope.ownedDefs.some((def) => {
|
|
301
|
+
const qualifiedName = def.qualifiedName;
|
|
302
|
+
if (qualifiedName === undefined)
|
|
303
|
+
return false;
|
|
304
|
+
const dot = qualifiedName.lastIndexOf('.');
|
|
305
|
+
return (dot === -1 ? qualifiedName : qualifiedName.slice(dot + 1)) === rootName;
|
|
306
|
+
}))) {
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
currentId = scope.parent;
|
|
310
|
+
}
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
function findReceiverTypeBinding(startScope, receiverName, scopes) {
|
|
314
|
+
let currentId = startScope;
|
|
315
|
+
const visited = new Set();
|
|
316
|
+
let moduleScopeId = null;
|
|
317
|
+
while (currentId !== null) {
|
|
318
|
+
if (visited.has(currentId))
|
|
319
|
+
return undefined;
|
|
320
|
+
visited.add(currentId);
|
|
321
|
+
const scope = scopes.scopeTree.getScope(currentId);
|
|
322
|
+
if (scope === undefined)
|
|
323
|
+
return undefined;
|
|
324
|
+
const typeRef = scope.typeBindings.get(receiverName);
|
|
325
|
+
if (typeRef !== undefined)
|
|
326
|
+
return typeRef;
|
|
327
|
+
if (scope.ownsReceivers?.has(receiverName) === true)
|
|
328
|
+
return undefined;
|
|
329
|
+
if (scope.kind === 'Module')
|
|
330
|
+
moduleScopeId = currentId;
|
|
331
|
+
currentId = scope.parent;
|
|
332
|
+
}
|
|
333
|
+
// Fallback 1 — named namespaces accessible from this file (own + `using`d),
|
|
334
|
+
// gated by `accessibleNamespacesByScope`. Consulted BEFORE the global channel
|
|
335
|
+
// so a more-specific named binding wins, matching the pre-#1871 order where
|
|
336
|
+
// these lived in the file's own `Scope.typeBindings` (the chain, above the
|
|
337
|
+
// global fallback). Shared-channel routing avoids the O(files × names) blow-up.
|
|
338
|
+
const named = namespaceTypeBindingFor(moduleScopeId, receiverName, scopes);
|
|
339
|
+
if (named !== undefined)
|
|
340
|
+
return named;
|
|
341
|
+
// Fallback 2 — global/default namespace: C# global types are visible from
|
|
342
|
+
// every file (see `workspaceTypeBindings` doc), so this flat channel is the
|
|
343
|
+
// final, unconditional fallback (#1871).
|
|
344
|
+
return scopes.workspaceTypeBindings?.get(receiverName);
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Resolve a typeBinding for `name` from the per-namespace channel
|
|
348
|
+
* (`namespaceTypeBindings`) across the namespaces accessible from `moduleScopeId`.
|
|
349
|
+
* First accessible-namespace hit wins. Returns `undefined` when the module has no
|
|
350
|
+
* accessibility entry (non-module scope id, or a bundle that didn't populate the
|
|
351
|
+
* channel — all non-C# today). Shared by the two typeBindings chain-walkers so
|
|
352
|
+
* the named-namespace fallback stays identical between them.
|
|
353
|
+
*/
|
|
354
|
+
function namespaceTypeBindingFor(moduleScopeId, name, scopes) {
|
|
355
|
+
if (moduleScopeId === null)
|
|
356
|
+
return undefined;
|
|
357
|
+
const namespaces = scopes.accessibleNamespacesByScope?.get(moduleScopeId);
|
|
358
|
+
if (namespaces === undefined)
|
|
359
|
+
return undefined;
|
|
360
|
+
for (const ns of namespaces) {
|
|
361
|
+
const hit = scopes.namespaceTypeBindings?.get(ns)?.get(name);
|
|
362
|
+
if (hit !== undefined)
|
|
363
|
+
return hit;
|
|
364
|
+
}
|
|
365
|
+
return undefined;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Walk the scope chain from `startScope` to its enclosing Module scope id, or
|
|
369
|
+
* `null` if none is found. Used by chain-followers that need the module scope to
|
|
370
|
+
* consult the accessibility-gated per-namespace channels.
|
|
371
|
+
*/
|
|
372
|
+
function moduleScopeIdOf(startScope, scopes) {
|
|
373
|
+
let currentId = startScope;
|
|
374
|
+
const visited = new Set();
|
|
375
|
+
while (currentId !== null) {
|
|
376
|
+
if (visited.has(currentId))
|
|
377
|
+
return null;
|
|
378
|
+
visited.add(currentId);
|
|
379
|
+
const scope = scopes.scopeTree.getScope(currentId);
|
|
380
|
+
if (scope === undefined)
|
|
381
|
+
return null;
|
|
382
|
+
if (scope.kind === 'Module')
|
|
383
|
+
return currentId;
|
|
384
|
+
currentId = scope.parent;
|
|
385
|
+
}
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Look up a class-like binding by name in the given scope's chain.
|
|
390
|
+
*
|
|
391
|
+
* "Class-like" covers `Class | Interface | Struct | Record | Enum |
|
|
392
|
+
* Trait` via the shared `isClassLike` predicate — every kind that
|
|
393
|
+
* collapses to `@scope.class` in the scope-extractor query contract.
|
|
394
|
+
*
|
|
395
|
+
* Walks the scope chain upward and consults TWO sources at each step:
|
|
396
|
+
* 1. `scope.bindings` — populated during scope-extraction Pass 2 with
|
|
397
|
+
* local declarations (`origin: 'local'`).
|
|
398
|
+
* 2. The cross-file finalized + augmented bindings, via
|
|
399
|
+
* `lookupBindingsAt` (per I8: finalized = canonical immutable
|
|
400
|
+
* output; augmented = post-finalize hooks like
|
|
401
|
+
* `populateNamespaceSiblings`).
|
|
402
|
+
*
|
|
403
|
+
* Without (2) we'd miss every cross-file class-receiver call.
|
|
404
|
+
*/
|
|
405
|
+
/**
|
|
406
|
+
* Every class-like definition visible for `name`, from the scope chain AND the
|
|
407
|
+
* qualified-name index, deduped by `nodeId`.
|
|
408
|
+
*
|
|
409
|
+
* Exists because `walkScopeChain` returns the FIRST match and cannot report a
|
|
410
|
+
* collision, so a caller that widens what a name can match (the decoration
|
|
411
|
+
* normalizer below) has no way to tell "one answer" from "picked the nearest of
|
|
412
|
+
* several". Mirrors `findAllCallableBindingsInScope`, which solved the same
|
|
413
|
+
* problem for callables.
|
|
414
|
+
*/
|
|
415
|
+
function findAllClassBindingsInScope(startScope, name, scopes) {
|
|
416
|
+
return classBindingsVisibleFrom(lexicalClassBindingsInScope(startScope, name, scopes), name, scopes);
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* {@link findAllClassBindingsInScope} for a caller that already holds the
|
|
420
|
+
* scope-chain half (see {@link lexicalClassBindingsInScope}), so the chain is
|
|
421
|
+
* walked once rather than once per question asked about the same name.
|
|
422
|
+
*
|
|
423
|
+
* The chain wins outright when it binds the name: an inner binding shadows
|
|
424
|
+
* anything the qualified-name index would contribute.
|
|
425
|
+
*/
|
|
426
|
+
function classBindingsVisibleFrom(lexical, name, scopes) {
|
|
427
|
+
if (lexical.length > 0)
|
|
428
|
+
return lexical;
|
|
429
|
+
const byNodeId = new Map();
|
|
430
|
+
for (const def of classDefsByQualifiedName(name, scopes))
|
|
431
|
+
byNodeId.set(def.nodeId, def);
|
|
432
|
+
return [...byNodeId.values()];
|
|
433
|
+
}
|
|
434
|
+
/** Bounded so a pathological stripper cannot spin. Real decoration nests
|
|
435
|
+
* shallowly (`*[]T`, `const T&`); three layers is generous. */
|
|
436
|
+
const MAX_DECORATION_LAYERS = 3;
|
|
437
|
+
/** Memo for {@link typeParameterNamesInScope}, keyed by index bundle then
|
|
438
|
+
* scope. One bundle per model, so the outer WeakMap releases with it. */
|
|
439
|
+
const typeParameterNamesByBundle = new WeakMap();
|
|
440
|
+
const NO_TYPE_PARAMETERS = Object.freeze(new Set());
|
|
441
|
+
/**
|
|
442
|
+
* Every name the scope chain above `scopeId` (inclusive) binds as a declared
|
|
443
|
+
* TYPE PARAMETER.
|
|
444
|
+
*
|
|
445
|
+
* Memoized per scope, and each scope's answer is built from its PARENT's, so a
|
|
446
|
+
* chain is walked once and every scope on it is O(own defs) rather than
|
|
447
|
+
* O(depth × defs). That matters because the caller runs on every class-binding
|
|
448
|
+
* lookup, and a module scope's `ownedDefs` is the whole file.
|
|
449
|
+
*/
|
|
450
|
+
function typeParameterNamesInScope(scopeId, scopes) {
|
|
451
|
+
let byScope = typeParameterNamesByBundle.get(scopes);
|
|
452
|
+
if (byScope === undefined) {
|
|
453
|
+
byScope = new Map();
|
|
454
|
+
typeParameterNamesByBundle.set(scopes, byScope);
|
|
455
|
+
}
|
|
456
|
+
const memo = byScope.get(scopeId);
|
|
457
|
+
if (memo !== undefined)
|
|
458
|
+
return memo;
|
|
459
|
+
// Collect the chain first, then fold from the top down, so the recursion is
|
|
460
|
+
// an explicit loop (a deep scope chain must not risk the call stack) and
|
|
461
|
+
// every scope passed through is memoized on the way back.
|
|
462
|
+
const chain = [];
|
|
463
|
+
const seen = new Set();
|
|
464
|
+
let cursor = scopeId;
|
|
465
|
+
let inherited = NO_TYPE_PARAMETERS;
|
|
466
|
+
while (cursor !== null && !seen.has(cursor)) {
|
|
467
|
+
seen.add(cursor);
|
|
468
|
+
const cached = byScope.get(cursor);
|
|
469
|
+
if (cached !== undefined) {
|
|
470
|
+
inherited = cached;
|
|
471
|
+
break;
|
|
472
|
+
}
|
|
473
|
+
chain.push(cursor);
|
|
474
|
+
cursor = scopes.scopeTree.getScope(cursor)?.parent ?? null;
|
|
475
|
+
}
|
|
476
|
+
for (let i = chain.length - 1; i >= 0; i -= 1) {
|
|
477
|
+
const id = chain[i];
|
|
478
|
+
const scope = scopes.scopeTree.getScope(id);
|
|
479
|
+
let own;
|
|
480
|
+
for (const def of scope?.ownedDefs ?? []) {
|
|
481
|
+
for (const parameter of def.typeParameters ?? []) {
|
|
482
|
+
if (parameter.name.length === 0)
|
|
483
|
+
continue;
|
|
484
|
+
own ??= new Set(inherited);
|
|
485
|
+
own.add(parameter.name);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
inherited = own ?? inherited;
|
|
489
|
+
byScope.set(id, inherited);
|
|
490
|
+
}
|
|
491
|
+
return inherited;
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Does the scope chain at `scopeId` bind `name` as a declared TYPE PARAMETER?
|
|
495
|
+
*
|
|
496
|
+
* The question a class-binding lookup has to ask before it answers, because a
|
|
497
|
+
* type parameter and a class are spelled identically and only the declaration
|
|
498
|
+
* says which one a name is. `class Box<T> { t: T }` beside a workspace
|
|
499
|
+
* `export class T` resolved `t` to the CLASS and emitted a confident wrong edge
|
|
500
|
+
* from every member call on `t` — the exact failure mode this subsystem treats
|
|
501
|
+
* as worse than a missing edge.
|
|
502
|
+
*
|
|
503
|
+
* WHY LEXICAL GROUNDING CANNOT SUBSTITUTE. The erasure grounds in
|
|
504
|
+
* `resolveErasedBaseName` all ask "can the file SEE a declaration by this
|
|
505
|
+
* name", and here it plainly can: `export class T` is imported, bound, and
|
|
506
|
+
* lexically visible. Visibility is not the defect — the name means something
|
|
507
|
+
* else at this site regardless of what else is visible, and only the enclosing
|
|
508
|
+
* declaration's parameter list records that. Measured: with the grounding rule
|
|
509
|
+
* in place the false edge still emitted.
|
|
510
|
+
*
|
|
511
|
+
* ABSENCE IS NOT EVIDENCE. `typeParameters` is populated only by the languages
|
|
512
|
+
* whose captures were extended for it, and is absent both for a non-generic
|
|
513
|
+
* declaration and for every declaration in a language that does not populate it
|
|
514
|
+
* yet. So only a POSITIVE match declines; an absent list changes nothing, which
|
|
515
|
+
* is what keeps every unconverted language behaving exactly as it does today.
|
|
516
|
+
*/
|
|
517
|
+
function bindsTypeParameter(scopeId, name, scopes) {
|
|
518
|
+
if (name.length === 0)
|
|
519
|
+
return false;
|
|
520
|
+
return typeParameterNamesInScope(scopeId, scopes).has(name);
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* The declared parameter `name` refers to at `scopeId`, nearest declaration
|
|
524
|
+
* first, or `undefined` when `name` is not a type parameter here.
|
|
525
|
+
*
|
|
526
|
+
* Separate from {@link bindsTypeParameter} because the guard only needs to know
|
|
527
|
+
* THAT a name is a parameter, while resolving through a bound needs the
|
|
528
|
+
* parameter itself — and the memoized name set deliberately keeps no payload so
|
|
529
|
+
* that the guard, which runs on every lookup, stays a single hash probe.
|
|
530
|
+
*/
|
|
531
|
+
function typeParameterAt(scopeId, name, scopes) {
|
|
532
|
+
let cursor = scopeId;
|
|
533
|
+
const seen = new Set();
|
|
534
|
+
while (cursor !== null && !seen.has(cursor)) {
|
|
535
|
+
seen.add(cursor);
|
|
536
|
+
const scope = scopes.scopeTree.getScope(cursor);
|
|
537
|
+
for (const def of scope?.ownedDefs ?? []) {
|
|
538
|
+
const hit = def.typeParameters?.find((parameter) => parameter.name === name);
|
|
539
|
+
if (hit !== undefined)
|
|
540
|
+
return hit;
|
|
541
|
+
}
|
|
542
|
+
cursor = scope?.parent ?? null;
|
|
543
|
+
}
|
|
544
|
+
return undefined;
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* The single class-like name a declared bound names, or `undefined` when the
|
|
548
|
+
* bound names none or names more than one.
|
|
549
|
+
*
|
|
550
|
+
* DECLINING ON AN INTERSECTION is the point. `T extends Repo & Closeable` and
|
|
551
|
+
* `T: Repo + Clone` make a member reachable through EITHER bound, so picking one
|
|
552
|
+
* — the first, as erasure would — mints a confidently-attributed edge to a
|
|
553
|
+
* declaration that may not own the member at all. Two candidates and no way to
|
|
554
|
+
* choose is exactly the case this file already answers with `undefined` in
|
|
555
|
+
* `findClassBindingInScope`'s decoration fallback: a missing edge is
|
|
556
|
+
* recoverable, a wrong one is not.
|
|
557
|
+
*
|
|
558
|
+
* Type ARGUMENTS on the bound are erased (`T extends Repo<User>` → `Repo`),
|
|
559
|
+
* which is sound here for the same reason the erased base-name route exists: the
|
|
560
|
+
* members are declared once, on the declaration written against its parameters.
|
|
561
|
+
*/
|
|
562
|
+
function soleBoundBaseName(bound) {
|
|
563
|
+
// `&` (Java, TypeScript) and `+` (Rust, Kotlin) both compose bounds. Split on
|
|
564
|
+
// whichever appears OUTSIDE brackets, so `Repo<A & B>` stays one bound.
|
|
565
|
+
let depth = 0;
|
|
566
|
+
for (let i = 0; i < bound.length; i += 1) {
|
|
567
|
+
const ch = bound[i];
|
|
568
|
+
if (ch === '<' || ch === '(' || ch === '[' || ch === '{')
|
|
569
|
+
depth += 1;
|
|
570
|
+
else if (ch === '>' || ch === ')' || ch === ']' || ch === '}')
|
|
571
|
+
depth -= 1;
|
|
572
|
+
else if (depth === 0 && (ch === '&' || ch === '+'))
|
|
573
|
+
return undefined;
|
|
574
|
+
}
|
|
575
|
+
const base = (0, template_arguments_js_1.stripTemplateArguments)(bound).trim();
|
|
576
|
+
return base.length === 0 ? undefined : base;
|
|
577
|
+
}
|
|
578
|
+
function findClassBindingInScope(startScope, receiverName, scopes,
|
|
579
|
+
/**
|
|
580
|
+
* OPT-IN. When supplied, a name that binds nothing is retried with decoration
|
|
581
|
+
* stripped one layer at a time, and each retry must resolve to exactly ONE
|
|
582
|
+
* class-like definition or it declines.
|
|
583
|
+
*
|
|
584
|
+
* Opt-in rather than global because roughly two dozen call sites use the shape
|
|
585
|
+
* `findClassBindingInScope(...) ?? otherResolver(...)`: turning a former
|
|
586
|
+
* `undefined` into a hit SUPPRESSES the fallback that used to answer, which
|
|
587
|
+
* would retarget inheritance edges and bypass generic-specialization
|
|
588
|
+
* selection. Only receiver-chain base and step resolution passes this.
|
|
589
|
+
*/
|
|
590
|
+
stripDecoration) {
|
|
591
|
+
// A TYPE PARAMETER is not a class, and it is checked before every route below
|
|
592
|
+
// rather than inside one of them because each route would otherwise reach a
|
|
593
|
+
// same-named class by its own channel: the scope chain when the class is
|
|
594
|
+
// imported, the qualified-name index when it is not, and the decoration
|
|
595
|
+
// fallback after stripping. The declaration that introduced the parameter is
|
|
596
|
+
// the only thing that knows, and it knows for all three.
|
|
597
|
+
if (bindsTypeParameter(startScope, receiverName, scopes)) {
|
|
598
|
+
return resolveThroughTypeParameterBound(startScope, receiverName, scopes, stripDecoration);
|
|
599
|
+
}
|
|
600
|
+
const local = walkScopeChain(startScope, receiverName, scopes, (def) => isClassLike(def.type));
|
|
601
|
+
if (local !== undefined)
|
|
602
|
+
return local;
|
|
603
|
+
// Fallback for languages (Go) where namespace-style imports don't
|
|
604
|
+
// create scope bindings: resolve via QualifiedNameIndex. Only fires
|
|
605
|
+
// when the scope-chain walk found nothing; single-match wins.
|
|
606
|
+
const qnames = scopes.qualifiedNames.get(receiverName);
|
|
607
|
+
if (qnames.length === 1) {
|
|
608
|
+
const def = scopes.defs.get(qnames[0]);
|
|
609
|
+
if (def !== undefined && isClassLike(def.type))
|
|
610
|
+
return def;
|
|
611
|
+
}
|
|
612
|
+
// Second fallback: dotted names like "models.User" — try the simple
|
|
613
|
+
// name (tail after last dot) for languages where defs are indexed by
|
|
614
|
+
// simple name (Go). Only when the dotted lookup fails.
|
|
615
|
+
if (receiverName.includes('.')) {
|
|
616
|
+
const simple = receiverName.slice(receiverName.lastIndexOf('.') + 1);
|
|
617
|
+
if (simple.length > 0 && simple !== receiverName) {
|
|
618
|
+
const simpleIds = scopes.qualifiedNames.get(simple);
|
|
619
|
+
if (simpleIds.length === 1) {
|
|
620
|
+
const def = scopes.defs.get(simpleIds[0]);
|
|
621
|
+
if (def !== undefined && isClassLike(def.type))
|
|
622
|
+
return def;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
// Decoration fallback (opt-in). Every branch above works on the name exactly
|
|
627
|
+
// as written; only when none of them bound anything do we consider that the
|
|
628
|
+
// name may be a decorated spelling of one that would.
|
|
629
|
+
if (stripDecoration !== undefined) {
|
|
630
|
+
let current = receiverName;
|
|
631
|
+
for (let layer = 0; layer < MAX_DECORATION_LAYERS; layer++) {
|
|
632
|
+
const stripped = stripDecoration(current);
|
|
633
|
+
if (stripped === undefined || stripped === current || stripped.length === 0)
|
|
634
|
+
break;
|
|
635
|
+
current = stripped;
|
|
636
|
+
const candidates = findAllClassBindingsInScope(startScope, current, scopes);
|
|
637
|
+
// Exactly one, or decline. Two same-named classes reachable from here mean
|
|
638
|
+
// the decoration was carrying the only disambiguating information, and
|
|
639
|
+
// picking the nearest would mint a confident wrong edge — the failure this
|
|
640
|
+
// whole line of work exists to avoid. A missing edge is recoverable.
|
|
641
|
+
if (candidates.length === 1)
|
|
642
|
+
return candidates[0];
|
|
643
|
+
if (candidates.length > 1)
|
|
644
|
+
return undefined;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
return undefined;
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* What a TYPE PARAMETER used in type position resolves to — its declared BOUND
|
|
651
|
+
* when it states exactly one, and nothing when it is unbounded.
|
|
652
|
+
*
|
|
653
|
+
* `class Box<T extends Repo> { t: T; run() { this.t.save(); } }` has one sound
|
|
654
|
+
* answer for `this.t.save()`: the member set a `T` is GUARANTEED to have is its
|
|
655
|
+
* bound's, so `Repo.save` is the target the declaration itself licenses. An
|
|
656
|
+
* unbounded `class Box2<T>` licenses nothing — `T` has no members — and gets
|
|
657
|
+
* `undefined`, which is the whole of the Gap-C fix.
|
|
658
|
+
*
|
|
659
|
+
* ONE HOP ONLY. The retry is guarded against a bound that is itself a parameter
|
|
660
|
+
* (`class Box<T extends U, U extends Repo>`), so the recursion cannot chain or
|
|
661
|
+
* cycle. Following such a chain is sound in principle but has no measured case
|
|
662
|
+
* behind it, and an unbounded step in the middle would have to decline anyway.
|
|
663
|
+
*/
|
|
664
|
+
function resolveThroughTypeParameterBound(startScope, parameterName, scopes, stripDecoration) {
|
|
665
|
+
const bound = typeParameterAt(startScope, parameterName, scopes)?.bound;
|
|
666
|
+
if (bound === undefined)
|
|
667
|
+
return undefined;
|
|
668
|
+
const baseName = soleBoundBaseName(bound);
|
|
669
|
+
if (baseName === undefined || baseName === parameterName)
|
|
670
|
+
return undefined;
|
|
671
|
+
// A bound naming another parameter terminates here rather than recursing.
|
|
672
|
+
if (bindsTypeParameter(startScope, baseName, scopes))
|
|
673
|
+
return undefined;
|
|
674
|
+
return findClassBindingInScope(startScope, baseName, scopes, stripDecoration);
|
|
675
|
+
}
|
|
676
|
+
function normalizeTemplateArgToken(value) {
|
|
677
|
+
return value.replace(/\s+/g, '');
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* A definition that pins its OWN concrete type arguments (`templateArguments`
|
|
681
|
+
* is set) — the shape a scope extractor records for a declaration written
|
|
682
|
+
* against particular arguments rather than against its parameters, e.g. C++
|
|
683
|
+
* `template <> struct Vec<bool>` (`['bool']`) or `template <class T> struct
|
|
684
|
+
* Vec<T*>` (`['T*']`).
|
|
685
|
+
*
|
|
686
|
+
* The distinction that matters to the lookup below: such a definition serves
|
|
687
|
+
* exactly ONE family of instantiations, so the only sound way to select it is
|
|
688
|
+
* the exact-argument match. A declaration written against its parameters —
|
|
689
|
+
* `template <class T> struct Vec`, `class Repo<T>` in TypeScript, C# and every
|
|
690
|
+
* other language measured — carries NOTHING here (the extractor reads arguments
|
|
691
|
+
* off the declared name, and the name is bare), which is precisely why it can
|
|
692
|
+
* never win that match and must be reachable by the base-name route instead.
|
|
693
|
+
*/
|
|
694
|
+
function carriesOwnTemplateArguments(def) {
|
|
695
|
+
return def.templateArguments !== undefined && def.templateArguments.length > 0;
|
|
696
|
+
}
|
|
697
|
+
/** Class-like defs registered in the workspace-wide qualified-name index under
|
|
698
|
+
* `name`. Workspace-WIDE: no scope filtering, so a caller must treat this as
|
|
699
|
+
* the weaker source and prefer lexically visible candidates. */
|
|
700
|
+
function classDefsByQualifiedName(name, scopes) {
|
|
701
|
+
const out = [];
|
|
702
|
+
for (const id of scopes.qualifiedNames.get(name)) {
|
|
703
|
+
const def = scopes.defs.get(id);
|
|
704
|
+
if (def !== undefined && isClassLike(def.type))
|
|
705
|
+
out.push(def);
|
|
706
|
+
}
|
|
707
|
+
return out;
|
|
708
|
+
}
|
|
709
|
+
/** Defs from `candidates` whose own template arguments equal `wantedArgs`
|
|
710
|
+
* token-for-token (whitespace already squeezed on both sides). */
|
|
711
|
+
function matchingTemplateArguments(candidates, wantedArgs) {
|
|
712
|
+
return candidates.filter((def) => {
|
|
713
|
+
const defArgs = def.templateArguments?.map(normalizeTemplateArgToken);
|
|
714
|
+
return (defArgs !== undefined &&
|
|
715
|
+
defArgs.length === wantedArgs.length &&
|
|
716
|
+
defArgs.every((value, i) => value === wantedArgs[i]));
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* Class-like defs the SCOPE CHAIN binds for `name` — locals, imports, wildcards,
|
|
721
|
+
* namespace siblings; everything `findAllBindingsInScope` reaches. No
|
|
722
|
+
* workspace-index fallback, which is the entire point: this is the set that
|
|
723
|
+
* answers "can the file see a declaration by this name", and
|
|
724
|
+
* `findAllClassBindingsInScope` deliberately cannot answer it because it falls
|
|
725
|
+
* through to the scope-free index when the chain is silent.
|
|
726
|
+
*/
|
|
727
|
+
function lexicalClassBindingsInScope(startScope, name, scopes) {
|
|
728
|
+
return findAllBindingsInScope(startScope, name, scopes, (def) => isClassLike(def.type));
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* The one declaration among `candidates` written against its PARAMETERS rather
|
|
732
|
+
* than against particular arguments — or `undefined` when there is not exactly
|
|
733
|
+
* one.
|
|
734
|
+
*
|
|
735
|
+
* ORDER-INDEPENDENT by construction, and that is why it exists separately from
|
|
736
|
+
* "take the first": an unordered candidate set (the workspace index, whose order
|
|
737
|
+
* is insertion order) must never let source order decide a call target. The
|
|
738
|
+
* scope-chain route keeps its nearest-first answer; only the index routes use
|
|
739
|
+
* this.
|
|
740
|
+
*/
|
|
741
|
+
function theInstantiationAgnosticDeclaration(candidates) {
|
|
742
|
+
const parameterized = candidates.filter((def) => !carriesOwnTemplateArguments(def));
|
|
743
|
+
return parameterized.length === 1 ? parameterized[0] : undefined;
|
|
744
|
+
}
|
|
745
|
+
/** Memo for {@link bindsAnyCrossFileClass}, keyed by index bundle then module
|
|
746
|
+
* scope. One bundle per model, so the outer WeakMap releases with it. */
|
|
747
|
+
const crossFileClassChannelByBundle = new WeakMap();
|
|
748
|
+
/**
|
|
749
|
+
* Does the FILE containing `scopeId` bind, at its module scope, any class-like
|
|
750
|
+
* definition declared in a DIFFERENT file?
|
|
751
|
+
*
|
|
752
|
+
* This is the question "is a name's absence from this file's scope chain
|
|
753
|
+
* evidence of anything", and it has to be asked of the data because the answer
|
|
754
|
+
* differs per language while the scope model records no fact that says which.
|
|
755
|
+
* Both halves were MEASURED on this pipeline, not assumed:
|
|
756
|
+
*
|
|
757
|
+
* - A C++ `#include` materializes NO binding. Two files declaring `Repo`, one
|
|
758
|
+
* of them `#include`d by the referencing file, resolves to NEITHER — the
|
|
759
|
+
* include contributed nothing and the ambiguity was decided by the
|
|
760
|
+
* workspace-wide index alone. So a C++ file's chain binds nothing
|
|
761
|
+
* cross-file, and the index is the only channel it has.
|
|
762
|
+
* - A TypeScript `import` does bind, and so does a C# `using` (through the
|
|
763
|
+
* accessible-namespace channel).
|
|
764
|
+
*
|
|
765
|
+
* So "the chain does not bind `Map`" is real evidence in a TypeScript file and
|
|
766
|
+
* no evidence at all in a C++ one. Asking the data which kind of file this is
|
|
767
|
+
* keeps the rule out of the business of naming languages (AGENTS.md R6).
|
|
768
|
+
*
|
|
769
|
+
* FAILS TOWARD PERMISSIVE. `false` — no module scope, no file path, nothing
|
|
770
|
+
* cross-file bound — restores exactly the import-blind behaviour that predates
|
|
771
|
+
* this check, so every way it can be wrong costs a wrong edge that already
|
|
772
|
+
* existed rather than a working edge that did not.
|
|
773
|
+
*/
|
|
774
|
+
function bindsAnyCrossFileClass(scopeId, scopes) {
|
|
775
|
+
const moduleScopeId = moduleScopeIdOf(scopeId, scopes);
|
|
776
|
+
if (moduleScopeId === null)
|
|
777
|
+
return false;
|
|
778
|
+
let byScope = crossFileClassChannelByBundle.get(scopes);
|
|
779
|
+
if (byScope === undefined) {
|
|
780
|
+
byScope = new Map();
|
|
781
|
+
crossFileClassChannelByBundle.set(scopes, byScope);
|
|
782
|
+
}
|
|
783
|
+
const memo = byScope.get(moduleScopeId);
|
|
784
|
+
if (memo !== undefined)
|
|
785
|
+
return memo;
|
|
786
|
+
const answer = scanForCrossFileClass(moduleScopeId, scopes);
|
|
787
|
+
byScope.set(moduleScopeId, answer);
|
|
788
|
+
return answer;
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* The uncached scan behind {@link bindsAnyCrossFileClass}. Answers on the FIRST
|
|
792
|
+
* hit, so a file with a wide `export *` surface stops at its first imported
|
|
793
|
+
* class rather than walking the surface; a file with none is walked in full, but
|
|
794
|
+
* its module scope then holds only its own declarations.
|
|
795
|
+
*
|
|
796
|
+
* Reads the binding CHANNELS rather than asking `lookupBindingsAt` once per
|
|
797
|
+
* name, because the question is existential and the per-name route answers a
|
|
798
|
+
* question it does not need: a module scope activates the accessibility-gated
|
|
799
|
+
* namespace channel, so every one of N bound names re-probed all K accessible
|
|
800
|
+
* namespaces (75.6 ms for one C#-shaped file at N=5,000, K=1,000) and paid
|
|
801
|
+
* `lookupBindingsAt`'s merge allocation each time. The population considered is
|
|
802
|
+
* identical — the two per-scope channels' own buckets, plus the namespace and
|
|
803
|
+
* workspace channels under exactly the names those two bind.
|
|
804
|
+
*/
|
|
805
|
+
function scanForCrossFileClass(moduleScopeId, scopes) {
|
|
806
|
+
const filePath = scopes.scopeTree.getScope(moduleScopeId)?.filePath;
|
|
807
|
+
if (filePath === undefined)
|
|
808
|
+
return false;
|
|
809
|
+
const bindsCrossFileClass = (refs) => refs !== undefined &&
|
|
810
|
+
refs.some((ref) => isClassLike(ref.def.type) && ref.def.filePath !== filePath);
|
|
811
|
+
// The two per-scope channels, read as whole buckets. An ordinary import lands
|
|
812
|
+
// here, so this is where the early exit usually fires.
|
|
813
|
+
const finalized = scopes.bindings.get(moduleScopeId);
|
|
814
|
+
const augmented = scopes.bindingAugmentations.get(moduleScopeId);
|
|
815
|
+
for (const channel of [finalized, augmented]) {
|
|
816
|
+
for (const refs of channel?.values() ?? []) {
|
|
817
|
+
if (bindsCrossFileClass(refs))
|
|
818
|
+
return true;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
const boundNameCount = (finalized?.size ?? 0) + (augmented?.size ?? 0);
|
|
822
|
+
if (boundNameCount === 0)
|
|
823
|
+
return false;
|
|
824
|
+
const bindsName = (name) => finalized?.has(name) === true || augmented?.has(name) === true;
|
|
825
|
+
// Materialized once, not per channel — `namesAtScope` allocates when both
|
|
826
|
+
// per-scope channels are populated.
|
|
827
|
+
let boundNames;
|
|
828
|
+
const namesBoundHere = () => (boundNames ??= [...namesAtScope(moduleScopeId, scopes)]);
|
|
829
|
+
// The accessibility-gated namespace channel: ONE lookup per accessible
|
|
830
|
+
// namespace, then whichever of the two sides is smaller is the one iterated —
|
|
831
|
+
// so neither a namespace with a large type table nor a file with many bound
|
|
832
|
+
// names can reintroduce the product.
|
|
833
|
+
for (const ns of scopes.accessibleNamespacesByScope?.get(moduleScopeId) ?? []) {
|
|
834
|
+
const inNamespace = scopes.namespaceFqnBindings?.get(ns);
|
|
835
|
+
if (inNamespace === undefined || inNamespace.size === 0)
|
|
836
|
+
continue;
|
|
837
|
+
if (inNamespace.size <= boundNameCount) {
|
|
838
|
+
for (const [name, refs] of inNamespace) {
|
|
839
|
+
if (bindsName(name) && bindsCrossFileClass(refs))
|
|
840
|
+
return true;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
else {
|
|
844
|
+
for (const name of namesBoundHere()) {
|
|
845
|
+
if (bindsCrossFileClass(inNamespace.get(name)))
|
|
846
|
+
return true;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
// The scope-independent workspace channel is keyed by name alone and has no
|
|
851
|
+
// per-scope bucket to walk, so it stays a probe per bound name.
|
|
852
|
+
const workspace = scopes.workspaceFqnBindings;
|
|
853
|
+
if (workspace !== undefined && workspace.size > 0) {
|
|
854
|
+
for (const name of namesBoundHere()) {
|
|
855
|
+
if (bindsCrossFileClass(workspace.get(name)))
|
|
856
|
+
return true;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
return false;
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Resolve a class-like binding for a declared type name, tolerating a spelling
|
|
863
|
+
* that carries TYPE ARGUMENTS (`Repo<User>`, `Vec<int>`) where the declaration
|
|
864
|
+
* itself is registered under the bare base name.
|
|
865
|
+
*
|
|
866
|
+
* Two normalizations, and they are not the same thing:
|
|
867
|
+
*
|
|
868
|
+
* 1. DECORATION stripping (`stripDecoration`, opt-in — see the parameter).
|
|
869
|
+
* Peels type-PRESERVING wrappers (`*T`, `const T&`) off the name.
|
|
870
|
+
* 2. Type-argument ERASURE (unconditional, and the wider of the two).
|
|
871
|
+
* `Repo<User>` → `Repo`. This is what actually widens what binds, because
|
|
872
|
+
* it makes one declaration answer for EVERY instantiation of it — right
|
|
873
|
+
* for a language where a generic class has a single declaration, and a
|
|
874
|
+
* hazard where it does not, which is why the exact-argument match runs
|
|
875
|
+
* first and why the base-name route below refuses to return a
|
|
876
|
+
* declaration that pinned its own arguments.
|
|
877
|
+
*
|
|
878
|
+
* Order: exact spelling → exact type-argument match (lexically visible
|
|
879
|
+
* candidates first, workspace-wide index second) → base name.
|
|
880
|
+
*/
|
|
881
|
+
function resolveClassBindingForName(scopeId, rawClassName, scopes,
|
|
882
|
+
/**
|
|
883
|
+
* OPT-IN, and it governs (1) only — argument erasure happens either way.
|
|
884
|
+
* `findClassBindingInScope`'s own docstring explains the opt-in: a name that
|
|
885
|
+
* previously bound nothing starts binding, which SUPPRESSES the
|
|
886
|
+
* `?? otherResolver(...)` fallbacks several callers rely on.
|
|
887
|
+
*
|
|
888
|
+
* THE RULE, not a roll-call of who currently passes it (that list has been
|
|
889
|
+
* appended to once per round of this work and is stale the moment it is
|
|
890
|
+
* written): pass it from a receiver-TYPING site, and only where the site
|
|
891
|
+
* already forwarded the same `stripTypePreservingDecoration` to the bare
|
|
892
|
+
* lookup — so a Go pointer receiver keeps resolving exactly as it did. A site
|
|
893
|
+
* that has never stripped must keep calling without it, because starting to
|
|
894
|
+
* strip is what suppresses its fallback.
|
|
895
|
+
*/
|
|
896
|
+
stripDecoration) {
|
|
897
|
+
const direct = findClassBindingInScope(scopeId, rawClassName, scopes, stripDecoration);
|
|
898
|
+
if (direct !== undefined)
|
|
899
|
+
return direct;
|
|
900
|
+
if (!rawClassName.includes('<'))
|
|
901
|
+
return undefined;
|
|
902
|
+
const baseName = (0, template_arguments_js_1.stripTemplateArguments)(rawClassName).replace(/\s+/g, '');
|
|
903
|
+
if (baseName.length === 0)
|
|
904
|
+
return undefined;
|
|
905
|
+
// The class-like defs the SCOPE CHAIN binds for the base name. Computed once
|
|
906
|
+
// and used twice — it is the lexical half of "what can the base name see from
|
|
907
|
+
// here" AND ground (1) of the erasure rule below, and the two asked for it
|
|
908
|
+
// separately, bottoming out in the same walk for a third of the cost of every
|
|
909
|
+
// lookup whose declared type carries type arguments.
|
|
910
|
+
const lexical = lexicalClassBindingsInScope(scopeId, baseName, scopes);
|
|
911
|
+
const wantedArgs = (0, template_arguments_js_1.extractTemplateArguments)(rawClassName)?.map(normalizeTemplateArgToken);
|
|
912
|
+
if (wantedArgs !== undefined && wantedArgs.length > 0) {
|
|
913
|
+
// LEXICAL FIRST. The workspace-wide index is not scoped, so matching against
|
|
914
|
+
// it up front let a field inside `namespace N` be answered by the GLOBAL
|
|
915
|
+
// `Box<bool>` — or, when both namespaces declare one, by neither: two
|
|
916
|
+
// matches, a decline, and a fall through to whatever base-name declaration
|
|
917
|
+
// the walk reached first. Candidates the scope chain actually offers are
|
|
918
|
+
// ranked ahead of it, exactly as every other lookup in this file does.
|
|
919
|
+
const lexicalMatches = matchingTemplateArguments(classBindingsVisibleFrom(lexical, baseName, scopes), wantedArgs);
|
|
920
|
+
if (lexicalMatches.length === 1)
|
|
921
|
+
return lexicalMatches[0];
|
|
922
|
+
if (lexicalMatches.length === 0) {
|
|
923
|
+
// Workspace-wide fallback — consulted ONLY when the scope chain offered no
|
|
924
|
+
// exact match, which is how a declaration specialized in a different file
|
|
925
|
+
// than the one instantiating it still binds.
|
|
926
|
+
const indexMatches = matchingTemplateArguments(classDefsByQualifiedName(baseName, scopes), wantedArgs);
|
|
927
|
+
if (indexMatches.length === 1)
|
|
928
|
+
return indexMatches[0];
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
// ── Base-name route ────────────────────────────────────────────────────────
|
|
932
|
+
// Nothing matched the arguments as written, so what is left to find is the
|
|
933
|
+
// declaration written against its PARAMETERS — the one instantiation-agnostic
|
|
934
|
+
// declaration the erasure is entitled to reach.
|
|
935
|
+
return resolveErasedBaseName(scopeId, baseName, scopes, lexical);
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* The declaration an ERASED base name is entitled to reach — the counterpart of
|
|
939
|
+
* `findClassBindingInScope` for a name that lost its type arguments, and the one
|
|
940
|
+
* place the grounding rule for that erasure lives.
|
|
941
|
+
*
|
|
942
|
+
* GROUNDING is the whole difference between a fix and a fabrication. Erasure
|
|
943
|
+
* makes ONE declaration answer for EVERY instantiation of a name, so reaching it
|
|
944
|
+
* by NAME ALONE is the widest step in this file: it is why `Map<string, User>`
|
|
945
|
+
* bound a workspace `class Map` the file cannot see, and why a third-party
|
|
946
|
+
* `Mapped[User]` bound an unrelated workspace `class Mapped` — a family of
|
|
947
|
+
* confident wrong edges the language interpreters have been holding back with
|
|
948
|
+
* deny-lists over an open universe of names. The name is not evidence. One of
|
|
949
|
+
* four grounds must connect the site to the declaration, strongest first.
|
|
950
|
+
*/
|
|
951
|
+
function resolveErasedBaseName(scopeId, baseName, scopes,
|
|
952
|
+
/**
|
|
953
|
+
* Ground (1) below, already computed: {@link lexicalClassBindingsInScope} for
|
|
954
|
+
* `baseName` at `scopeId`. A parameter rather than a call because the only
|
|
955
|
+
* caller needs the same list for its exact-argument match, and computing it
|
|
956
|
+
* twice walked the scope chain twice.
|
|
957
|
+
*/
|
|
958
|
+
lexical) {
|
|
959
|
+
// (1) THE SCOPE CHAIN binds the base name — a local, an import, a wildcard, a
|
|
960
|
+
// namespace sibling. The file demonstrably sees a declaration by that name, so
|
|
961
|
+
// erasing to it is what the source meant.
|
|
962
|
+
if (lexical.length > 0) {
|
|
963
|
+
const nearest = lexical[0];
|
|
964
|
+
// The walk landed on a declaration that pinned its own arguments — arguments
|
|
965
|
+
// the branch above just proved are NOT the ones written. It won on nothing
|
|
966
|
+
// but being reached first: `Vec<int> vi` bound the `Vec<bool>`
|
|
967
|
+
// specialization when the specialization happened to be declared above the
|
|
968
|
+
// primary template, and the primary when it did not. Source order deciding a
|
|
969
|
+
// call target is a wrong edge, not a missing one. Re-decide over the same
|
|
970
|
+
// visible candidates with those declarations removed.
|
|
971
|
+
return carriesOwnTemplateArguments(nearest)
|
|
972
|
+
? theInstantiationAgnosticDeclaration(lexical)
|
|
973
|
+
: nearest;
|
|
974
|
+
}
|
|
975
|
+
// Nothing lexical. Both remaining grounds read the workspace-wide qualified-
|
|
976
|
+
// name index, which consults no scope, no import and no module — so each one
|
|
977
|
+
// has to supply the connection the index itself cannot.
|
|
978
|
+
const indexed = classDefsByQualifiedName(baseName, scopes);
|
|
979
|
+
// (2) THE DECLARATION IS IN THIS VERY FILE. A same-file declaration is visible
|
|
980
|
+
// to the site in every language — no import, no `using`, no `#include` — which
|
|
981
|
+
// is exactly what makes this ground language-neutral rather than a guess. It
|
|
982
|
+
// is also load-bearing rather than theoretical: a member typed `ns::Repo<User>`
|
|
983
|
+
// resolves through here, because the qualifier is dropped at capture and a
|
|
984
|
+
// sibling NAMESPACE is not on the file's scope chain.
|
|
985
|
+
const siteFile = scopes.scopeTree.getScope(scopeId)?.filePath;
|
|
986
|
+
const sameFile = siteFile === undefined ? [] : indexed.filter((def) => def.filePath === siteFile);
|
|
987
|
+
if (sameFile.length > 0)
|
|
988
|
+
return theInstantiationAgnosticDeclaration(sameFile);
|
|
989
|
+
// (3) THE INDEX PROVES THE NAME IS A TEMPLATE FAMILY — some declaration under
|
|
990
|
+
// it pins its own arguments. That is the same evidence the exact-argument
|
|
991
|
+
// index match above already acts on, and acting on it in only one direction
|
|
992
|
+
// was incoherent: in one measured fixture `Vec<bool>` bound the cross-file
|
|
993
|
+
// SPECIALIZATION through the index while `Vec<int>` bound nothing, though both
|
|
994
|
+
// are equally import-blind and the primary template is the only declaration
|
|
995
|
+
// that can answer `int`.
|
|
996
|
+
//
|
|
997
|
+
// (4) …or THE FILE HAS NO CROSS-FILE CHANNEL to be absent from, in which case
|
|
998
|
+
// the index is not a shortcut around the scope chain — it is the only channel
|
|
999
|
+
// that file has, and refusing it deletes every cross-file generic in the
|
|
1000
|
+
// languages whose visibility is not lexical. Measured, both directions: a C++
|
|
1001
|
+
// `#include` binds nothing, so `Repo<User>` in a `.cpp` reaches its header
|
|
1002
|
+
// declaration ONLY here; a TypeScript `import` binds, so a file that imports
|
|
1003
|
+
// anything and still cannot see `Map` genuinely cannot see it.
|
|
1004
|
+
//
|
|
1005
|
+
// Between them these two grounds are what separates the fix from the
|
|
1006
|
+
// fabrication: `Map`, `Queue`, `Deque` in a file with a working import channel
|
|
1007
|
+
// offer nothing but a spelling, and now get nothing.
|
|
1008
|
+
if (indexed.some(carriesOwnTemplateArguments) || !bindsAnyCrossFileClass(scopeId, scopes)) {
|
|
1009
|
+
return theInstantiationAgnosticDeclaration(indexed);
|
|
1010
|
+
}
|
|
1011
|
+
return undefined;
|
|
1012
|
+
}
|
|
1013
|
+
/**
|
|
1014
|
+
* Resolve a class-like inheritance target using the shared inheritance
|
|
1015
|
+
* resolution chain. Keeps pre-emitted heritage edges and language-specific
|
|
1016
|
+
* consumers of `inherits` sites aligned.
|
|
1017
|
+
*/
|
|
1018
|
+
function resolveInheritanceBaseInScope(startScope, baseName, scopes, rawQualifiedName, enclosingClassDef) {
|
|
1019
|
+
// #1982: when the source wrote a qualified base (`Other::Inner`), resolve it
|
|
1020
|
+
// against the full-path QualifiedNameIndex FIRST, so a same-tail nested base
|
|
1021
|
+
// binds to the matching sibling instead of the first-inserted one that the
|
|
1022
|
+
// simple-tail scope walk picks. Falls through to the existing walk when the
|
|
1023
|
+
// base is unqualified, unknown, or the qualified lookup can't pick a unique
|
|
1024
|
+
// winner — so unqualified bases and the cross-file single-candidate case are
|
|
1025
|
+
// unchanged. `enclosingClassDef` (the deriving class) is threaded from the
|
|
1026
|
+
// caller to skip a redundant enclosing-class walk (#1982 perf).
|
|
1027
|
+
if (rawQualifiedName !== undefined) {
|
|
1028
|
+
const qualified = resolveQualifiedInheritanceBase(startScope, rawQualifiedName, scopes, enclosingClassDef);
|
|
1029
|
+
if (qualified !== undefined)
|
|
1030
|
+
return qualified;
|
|
1031
|
+
}
|
|
1032
|
+
return (findClassBindingInScope(startScope, baseName, scopes) ??
|
|
1033
|
+
resolveAmbiguousInheritanceBaseViaImports(startScope, baseName, scopes));
|
|
1034
|
+
}
|
|
1035
|
+
/**
|
|
1036
|
+
* Resolve a qualified inheritance base (`Other::Inner`, `ns::Base`) against the
|
|
1037
|
+
* full-path `QualifiedNameIndex` (keyed by `def.qualifiedName`, which carries
|
|
1038
|
+
* the promoted dotted path post-`populateOwners`). Tries the referencing site's
|
|
1039
|
+
* enclosing-scope segments as progressive prefixes (longest first) before the
|
|
1040
|
+
* root-anchored qualifier, so a *relative* base like `Outer::Inner` written
|
|
1041
|
+
* inside `namespace NS` resolves to the root-anchored key `NS.Outer.Inner`.
|
|
1042
|
+
* Returns a unique class-like def, or `undefined` when the base is unqualified,
|
|
1043
|
+
* unknown, or genuinely ambiguous at a key (refuse-on-tie — never guess; a
|
|
1044
|
+
* wrong EXTENDS edge silently corrupts impact analysis).
|
|
1045
|
+
*/
|
|
1046
|
+
function resolveQualifiedInheritanceBase(startScope, rawQualifiedName, scopes, enclosingClassDef) {
|
|
1047
|
+
const normalized = (0, qualified_name_js_1.stripTrailingTypeArguments)((0, qualified_name_js_1.normalizeQualifiedName)(rawQualifiedName));
|
|
1048
|
+
// No qualifier after normalization → nothing the simple-tail walk doesn't do.
|
|
1049
|
+
if (normalized.length === 0 || !normalized.includes('.'))
|
|
1050
|
+
return undefined;
|
|
1051
|
+
// #1982: a root-anchored base (`::Net::X`) names the GLOBAL scope, so it must
|
|
1052
|
+
// NOT be prefixed with the referencing site's enclosing segments — try only
|
|
1053
|
+
// the root-anchored key. normalizeQualifiedName strips the leading `::`, so
|
|
1054
|
+
// detect the anchor on the raw text (after leading whitespace).
|
|
1055
|
+
const isRootAnchored = /^\s*::/.test(rawQualifiedName);
|
|
1056
|
+
const enclosing = isRootAnchored
|
|
1057
|
+
? []
|
|
1058
|
+
: enclosingScopeSegments(startScope, scopes, enclosingClassDef);
|
|
1059
|
+
// Candidate keys: longest enclosing prefix first for *relative* qualified
|
|
1060
|
+
// bases (`Outer.Inner` inside `NS.Outer.Derived` → `NS.Outer.Inner`). When the
|
|
1061
|
+
// qualifier names a *different* namespace than the enclosing scope (`new B.Foo()`
|
|
1062
|
+
// inside `namespace A` → `B.Foo`, not `A.Foo`), try the raw normalized key
|
|
1063
|
+
// FIRST so same-tail local bindings don't win (#2046 / #1991).
|
|
1064
|
+
const normParts = (0, qualified_name_js_1.splitQualifiedName)(normalized);
|
|
1065
|
+
const isRelativeToEnclosing = enclosing.length > 0 &&
|
|
1066
|
+
normParts.length > 0 &&
|
|
1067
|
+
normParts[0] === enclosing[enclosing.length - 1];
|
|
1068
|
+
const keys = [];
|
|
1069
|
+
if (!isRelativeToEnclosing) {
|
|
1070
|
+
keys.push(normalized);
|
|
1071
|
+
}
|
|
1072
|
+
for (let i = enclosing.length; i >= 1; i--) {
|
|
1073
|
+
keys.push([...enclosing.slice(0, i), normalized].join('.'));
|
|
1074
|
+
}
|
|
1075
|
+
if (!keys.includes(normalized)) {
|
|
1076
|
+
keys.push(normalized);
|
|
1077
|
+
}
|
|
1078
|
+
for (const key of keys) {
|
|
1079
|
+
const ids = scopes.qualifiedNames.get(key);
|
|
1080
|
+
if (ids.length === 0)
|
|
1081
|
+
continue;
|
|
1082
|
+
let unique;
|
|
1083
|
+
let count = 0;
|
|
1084
|
+
for (const id of ids) {
|
|
1085
|
+
const def = scopes.defs.get(id);
|
|
1086
|
+
if (def !== undefined && isClassLike(def.type)) {
|
|
1087
|
+
unique = def;
|
|
1088
|
+
count++;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
if (count === 1)
|
|
1092
|
+
return unique;
|
|
1093
|
+
if (count > 1) {
|
|
1094
|
+
// #1993: same-tail bases collide at this namespace-omitted key (`NS1::A::Inner`
|
|
1095
|
+
// and `NS2::A::Inner` both key `A.Inner`). Break the tie with the bridge's
|
|
1096
|
+
// `namespacePrefix` sidecar — prefer the candidate in the SAME enclosing
|
|
1097
|
+
// namespace as the deriving class. Bridge-held: `def.qualifiedName` and the
|
|
1098
|
+
// index keys are untouched; still refuse when the sidecar can't pick a unique.
|
|
1099
|
+
const childPrefix = enclosingClassDef?.namespacePrefix;
|
|
1100
|
+
if (childPrefix !== undefined && childPrefix.length > 0) {
|
|
1101
|
+
let nsUnique;
|
|
1102
|
+
let nsCount = 0;
|
|
1103
|
+
for (const id of ids) {
|
|
1104
|
+
const def = scopes.defs.get(id);
|
|
1105
|
+
if (def !== undefined && isClassLike(def.type) && def.namespacePrefix === childPrefix) {
|
|
1106
|
+
nsUnique = def;
|
|
1107
|
+
nsCount++;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
if (nsCount === 1)
|
|
1111
|
+
return nsUnique;
|
|
1112
|
+
}
|
|
1113
|
+
return undefined; // genuine tie → refuse, don't guess
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
// Qualifier-vs-sidecar fallback (#2046). Languages whose class `qualifiedName`
|
|
1117
|
+
// is the SIMPLE name (C#) never populate a qualified key in the index, so the
|
|
1118
|
+
// keyed loop above can't see `B.Foo`. Resolve the simple TAIL and break the
|
|
1119
|
+
// same-tail collision by matching the explicit qualifier (`B`) against each
|
|
1120
|
+
// candidate's `namespacePrefix` sidecar. Commit only on a unique match — a
|
|
1121
|
+
// still-ambiguous qualifier refuses (never guesses a wrong EXTENDS/CALLS edge).
|
|
1122
|
+
const tail = normParts[normParts.length - 1];
|
|
1123
|
+
const qualifier = normParts.slice(0, -1).join('.');
|
|
1124
|
+
if (tail !== undefined && qualifier.length > 0) {
|
|
1125
|
+
const tailIds = scopes.qualifiedNames.get(tail);
|
|
1126
|
+
let qUnique;
|
|
1127
|
+
let qCount = 0;
|
|
1128
|
+
for (const id of tailIds) {
|
|
1129
|
+
const def = scopes.defs.get(id);
|
|
1130
|
+
if (def === undefined || !isClassLike(def.type))
|
|
1131
|
+
continue;
|
|
1132
|
+
const np = def.namespacePrefix;
|
|
1133
|
+
if (np === undefined || np.length === 0)
|
|
1134
|
+
continue;
|
|
1135
|
+
if (np === qualifier || np.endsWith(`.${qualifier}`)) {
|
|
1136
|
+
qUnique = def;
|
|
1137
|
+
qCount++;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
if (qCount === 1)
|
|
1141
|
+
return qUnique;
|
|
1142
|
+
}
|
|
1143
|
+
return undefined;
|
|
1144
|
+
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Enclosing scope segments of an inheritance site, derived from the deriving
|
|
1147
|
+
* (child) class def's `qualifiedName` minus its own tail. For child
|
|
1148
|
+
* `NS.Other.Derived` this is `['NS', 'Other']`; empty for a file-scope child.
|
|
1149
|
+
* Used to build progressive-prefix lookup keys for relative qualified bases.
|
|
1150
|
+
*/
|
|
1151
|
+
function enclosingScopeSegments(startScope, scopes, enclosingClassDef) {
|
|
1152
|
+
// Reuse the caller-provided deriving class when available (#1982 perf); only
|
|
1153
|
+
// walk the scope chain when it wasn't threaded in.
|
|
1154
|
+
const child = enclosingClassDef ?? findEnclosingClassDef(startScope, scopes);
|
|
1155
|
+
const q = child?.qualifiedName;
|
|
1156
|
+
if (q === undefined || q.length === 0)
|
|
1157
|
+
return [];
|
|
1158
|
+
const segs = q.split('.').filter(Boolean);
|
|
1159
|
+
return segs.slice(0, -1);
|
|
1160
|
+
}
|
|
1161
|
+
/**
|
|
1162
|
+
* Import/include-aware disambiguation for an *ambiguous* class-like base
|
|
1163
|
+
* name. Engages ONLY as a fallback after `findClassBindingInScope` has
|
|
1164
|
+
* already returned `undefined` — i.e. the scope-chain walk and the
|
|
1165
|
+
* single-match `qualifiedNames` fast paths could not pick a winner because
|
|
1166
|
+
* several same-named class-like defs exist (e.g. two `class Handler`s in
|
|
1167
|
+
* different headers/namespaces).
|
|
1168
|
+
*
|
|
1169
|
+
* Disambiguates by the referencing file's import graph: the enclosing
|
|
1170
|
+
* module scope's finalized `ImportEdge[]` (C++ `#include`, C# `using`, etc.)
|
|
1171
|
+
* each carry the exporting file in `targetFile`. A candidate whose defining
|
|
1172
|
+
* file is brought in by one of those edges is preferred. Resolution is
|
|
1173
|
+
* tiered, strictest first, and only commits when EXACTLY ONE candidate
|
|
1174
|
+
* survives a tier — so a still-ambiguous name keeps the historical
|
|
1175
|
+
* "return undefined" refusal:
|
|
1176
|
+
*
|
|
1177
|
+
* 1. Exact file match — candidate.filePath === an import's `targetFile`
|
|
1178
|
+
* (covers C++ `#include "handler_a.h"` → that header's class).
|
|
1179
|
+
* 2. Same-directory match — candidate.filePath sits in the same directory
|
|
1180
|
+
* as some import target file (covers C# `using MyApp.Models;`, where the
|
|
1181
|
+
* namespace import resolves to ONE representative file in the namespace's
|
|
1182
|
+
* directory, not necessarily the file declaring the referenced type).
|
|
1183
|
+
*
|
|
1184
|
+
* Language-neutral: keyed only on the finalized import edges and the
|
|
1185
|
+
* candidate defs' `filePath`. Returns `undefined` (preserving refusal) when
|
|
1186
|
+
* the name is single-match-resolvable already (never reached — caller gates
|
|
1187
|
+
* on `findClassBindingInScope` miss), when no import disambiguates, or when
|
|
1188
|
+
* a tier leaves more than one survivor.
|
|
1189
|
+
*/
|
|
1190
|
+
function resolveAmbiguousInheritanceBaseViaImports(startScope, baseName, scopes) {
|
|
1191
|
+
// Gather the class-like candidates that share this simple name. Defs are
|
|
1192
|
+
// indexed by their `qualifiedName` in `qualifiedNames`; for languages whose
|
|
1193
|
+
// class qualifiedName IS the simple name (C++, C#, etc.) this is the full
|
|
1194
|
+
// candidate set. A single candidate is not "ambiguous" — leave it to the
|
|
1195
|
+
// existing single-match fast path (this fallback shouldn't have been called).
|
|
1196
|
+
const candidateIds = scopes.qualifiedNames.get(baseName);
|
|
1197
|
+
if (candidateIds.length < 2)
|
|
1198
|
+
return undefined;
|
|
1199
|
+
const candidates = [];
|
|
1200
|
+
for (const id of candidateIds) {
|
|
1201
|
+
const def = scopes.defs.get(id);
|
|
1202
|
+
if (def !== undefined && isClassLike(def.type))
|
|
1203
|
+
candidates.push(def);
|
|
1204
|
+
}
|
|
1205
|
+
if (candidates.length < 2)
|
|
1206
|
+
return undefined;
|
|
1207
|
+
// Collect the exporting files imported by the referencing file's enclosing
|
|
1208
|
+
// module scope (the chain may carry function-local imports too, but the
|
|
1209
|
+
// module scope is where `#include` / `using` land).
|
|
1210
|
+
const moduleScopeId = moduleScopeIdOf(startScope, scopes);
|
|
1211
|
+
if (moduleScopeId === null)
|
|
1212
|
+
return undefined;
|
|
1213
|
+
const importEdges = scopes.imports.get(moduleScopeId);
|
|
1214
|
+
if (importEdges === undefined || importEdges.length === 0)
|
|
1215
|
+
return undefined;
|
|
1216
|
+
const importedFiles = new Set();
|
|
1217
|
+
const importedDirs = new Set();
|
|
1218
|
+
for (const edge of importEdges) {
|
|
1219
|
+
if (edge.targetFile === null)
|
|
1220
|
+
continue;
|
|
1221
|
+
importedFiles.add(edge.targetFile);
|
|
1222
|
+
importedDirs.add(dirnameOf(edge.targetFile));
|
|
1223
|
+
}
|
|
1224
|
+
if (importedFiles.size === 0)
|
|
1225
|
+
return undefined;
|
|
1226
|
+
// Tier 1 — exact file match (C++ `#include "handler_a.h"`).
|
|
1227
|
+
const exact = candidates.filter((c) => importedFiles.has(c.filePath));
|
|
1228
|
+
if (exact.length === 1)
|
|
1229
|
+
return exact[0];
|
|
1230
|
+
if (exact.length > 1)
|
|
1231
|
+
return undefined; // still ambiguous → refuse
|
|
1232
|
+
// Tier 2 — same-directory match (C# namespace `using`, where the namespace
|
|
1233
|
+
// import resolves to one representative file in the namespace's directory).
|
|
1234
|
+
const sameDir = candidates.filter((c) => importedDirs.has(dirnameOf(c.filePath)));
|
|
1235
|
+
if (sameDir.length === 1)
|
|
1236
|
+
return sameDir[0];
|
|
1237
|
+
return undefined;
|
|
1238
|
+
}
|
|
1239
|
+
/**
|
|
1240
|
+
* Directory portion of a forward-slash workspace-relative path. Returns `''`
|
|
1241
|
+
* for a bare filename (no directory). Workspace paths are always normalized to
|
|
1242
|
+
* `/` separators upstream, so a simple `lastIndexOf('/')` is sufficient and
|
|
1243
|
+
* keeps this dependency-free.
|
|
1244
|
+
*/
|
|
1245
|
+
function dirnameOf(filePath) {
|
|
1246
|
+
const idx = filePath.lastIndexOf('/');
|
|
1247
|
+
return idx === -1 ? '' : filePath.slice(0, idx);
|
|
1248
|
+
}
|
|
1249
|
+
/**
|
|
1250
|
+
* Predicate for value-receiver bridge: the labels for which
|
|
1251
|
+
* `reconcileOwnership` registers methods/fields under the def's
|
|
1252
|
+
* `nodeId` as the `ownerId`. Explicit allowlist so future NodeLabel
|
|
1253
|
+
* additions (Module, Namespace, TypeAlias, EnumMember, etc.) do NOT
|
|
1254
|
+
* silently widen the bridge — adding a new ownerable label requires
|
|
1255
|
+
* touching both this predicate and `reconcileOwnership`.
|
|
1256
|
+
*
|
|
1257
|
+
* See: `scope-resolution/pipeline/reconcile-ownership.ts` Property /
|
|
1258
|
+
* Variable / Const / Static registration block.
|
|
1259
|
+
*/
|
|
1260
|
+
function isOwnableValueLabel(t) {
|
|
1261
|
+
return t === 'Const' || t === 'Variable' || t === 'Property' || t === 'Static';
|
|
1262
|
+
}
|
|
1263
|
+
/**
|
|
1264
|
+
* Look up a value-binding (Const/Variable/Property/Static) by name in
|
|
1265
|
+
* the given scope's chain. Used by the value-receiver-owner bridge
|
|
1266
|
+
* for object-literal services such as:
|
|
1267
|
+
*
|
|
1268
|
+
* export const fooService = { getUser(id) {...} };
|
|
1269
|
+
*
|
|
1270
|
+
* where `fooService` is a `Const`/`Variable` whose `nodeId` is the
|
|
1271
|
+
* `ownerId` of the member method. Neither `findClassBindingInScope`
|
|
1272
|
+
* (rejects non-class-like) nor `findReceiverTypeBinding` (no typeBinding
|
|
1273
|
+
* for an unannotated literal) finds it.
|
|
1274
|
+
*
|
|
1275
|
+
* Mirrors `findClassBindingInScope` exactly; only the accepted def-type
|
|
1276
|
+
* predicate differs.
|
|
1277
|
+
*/
|
|
1278
|
+
function findValueBindingInScope(startScope, receiverName, scopes) {
|
|
1279
|
+
return walkScopeChain(startScope, receiverName, scopes, (def) => isOwnableValueLabel(def.type));
|
|
1280
|
+
}
|
|
1281
|
+
/**
|
|
1282
|
+
* Generic scope-chain walker. Walks from `startScope` toward the root,
|
|
1283
|
+
* consulting both the local `scope.bindings` channel and the dual-source
|
|
1284
|
+
* `lookupBindingsAt` view (finalized + augmented). At each scope, local
|
|
1285
|
+
* bindings are exhausted BEFORE imported/augmented bindings — preserves
|
|
1286
|
+
* JavaScript-style lexical scoping where a local `const x` shadows an
|
|
1287
|
+
* imported `x` of the same name.
|
|
1288
|
+
*
|
|
1289
|
+
* Returns the first binding `def` matching `predicate`. Cycles in the
|
|
1290
|
+
* scope graph terminate the walk (defensive — should not occur in
|
|
1291
|
+
* well-formed inputs).
|
|
1292
|
+
*/
|
|
1293
|
+
function walkScopeChain(startScope, name, scopes, predicate) {
|
|
1294
|
+
let currentId = startScope;
|
|
1295
|
+
const visited = new Set();
|
|
1296
|
+
while (currentId !== null) {
|
|
1297
|
+
if (visited.has(currentId))
|
|
1298
|
+
return undefined;
|
|
1299
|
+
visited.add(currentId);
|
|
1300
|
+
const scope = scopes.scopeTree.getScope(currentId);
|
|
1301
|
+
if (scope === undefined)
|
|
1302
|
+
return undefined;
|
|
1303
|
+
// `Object` scopes (object/record literal bodies) are a hoist
|
|
1304
|
+
// boundary only -- their members are reachable via property access,
|
|
1305
|
+
// never bare identifiers, so they contribute nothing to lookup
|
|
1306
|
+
// (#2545/#2551). Still traverse past to the parent.
|
|
1307
|
+
if (scope.kind !== 'Object') {
|
|
1308
|
+
// Local first: a `const x` in this scope shadows any imported `x`.
|
|
1309
|
+
const localBindings = scope.bindings.get(name);
|
|
1310
|
+
if (localBindings !== undefined) {
|
|
1311
|
+
for (const b of localBindings) {
|
|
1312
|
+
if (predicate(b.def))
|
|
1313
|
+
return b.def;
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
// Then imported/augmented bindings — only consulted when no local match.
|
|
1317
|
+
const importedBindings = lookupBindingsAt(currentId, name, scopes);
|
|
1318
|
+
for (const b of importedBindings) {
|
|
1319
|
+
if (predicate(b.def))
|
|
1320
|
+
return b.def;
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
currentId = scope.parent;
|
|
1324
|
+
}
|
|
1325
|
+
return undefined;
|
|
1326
|
+
}
|
|
1327
|
+
/**
|
|
1328
|
+
* Look up a callable (Function/Method/Constructor) by name in the
|
|
1329
|
+
* given scope's chain. Uses the dual-source pattern (scope.bindings +
|
|
1330
|
+
* `lookupBindingsAt` for finalized + augmented) so cross-file
|
|
1331
|
+
* imports are visible — without it free calls to imported functions
|
|
1332
|
+
* never resolve via the post-pass.
|
|
1333
|
+
*
|
|
1334
|
+
* Mirrors `findClassBindingInScope` exactly; only the accepted
|
|
1335
|
+
* def-type predicate differs.
|
|
1336
|
+
*/
|
|
1337
|
+
function findCallableBindingInScope(startScope, callableName, scopes) {
|
|
1338
|
+
return findAllCallableBindingsInScope(startScope, callableName, scopes)[0];
|
|
1339
|
+
}
|
|
1340
|
+
function collectCallableBindingCandidates(sources) {
|
|
1341
|
+
const byNodeId = new Map();
|
|
1342
|
+
for (const source of sources) {
|
|
1343
|
+
if (source === undefined)
|
|
1344
|
+
continue;
|
|
1345
|
+
for (const binding of source) {
|
|
1346
|
+
const def = binding.def;
|
|
1347
|
+
if (def.type !== 'Function' && def.type !== 'Method' && def.type !== 'Constructor')
|
|
1348
|
+
continue;
|
|
1349
|
+
const existing = byNodeId.get(def.nodeId);
|
|
1350
|
+
if (existing === undefined) {
|
|
1351
|
+
byNodeId.set(def.nodeId, { def, bindings: [binding] });
|
|
1352
|
+
}
|
|
1353
|
+
else {
|
|
1354
|
+
existing.bindings.push(binding);
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
return [...byNodeId.values()];
|
|
1359
|
+
}
|
|
1360
|
+
/**
|
|
1361
|
+
* Binding-aware callable lookup for consumers that need visibility evidence.
|
|
1362
|
+
* Unlike `lookupBindingsAt`, duplicate definitions retain every binding path,
|
|
1363
|
+
* so a weaker augmentation can contribute provenance even when a finalized
|
|
1364
|
+
* binding remains the candidate's canonical definition.
|
|
1365
|
+
*/
|
|
1366
|
+
function findAllCallableBindingCandidatesInScope(startScope, callableName, scopes) {
|
|
1367
|
+
let currentId = startScope;
|
|
1368
|
+
const visited = new Set();
|
|
1369
|
+
while (currentId !== null) {
|
|
1370
|
+
if (visited.has(currentId))
|
|
1371
|
+
return [];
|
|
1372
|
+
visited.add(currentId);
|
|
1373
|
+
const scope = scopes.scopeTree.getScope(currentId);
|
|
1374
|
+
if (scope === undefined)
|
|
1375
|
+
return [];
|
|
1376
|
+
if (scope.kind !== 'Object') {
|
|
1377
|
+
const lexical = collectCallableBindingCandidates([scope.bindings.get(callableName)]);
|
|
1378
|
+
if (lexical.length > 0)
|
|
1379
|
+
return lexical;
|
|
1380
|
+
const candidates = collectCallableBindingCandidates([
|
|
1381
|
+
scopes.bindings.get(currentId)?.get(callableName),
|
|
1382
|
+
scopes.bindingAugmentations.get(currentId)?.get(callableName),
|
|
1383
|
+
collectNamespaceFqnBindings(currentId, callableName, scopes),
|
|
1384
|
+
scopes.workspaceFqnBindings?.get(callableName),
|
|
1385
|
+
]);
|
|
1386
|
+
if (candidates.length > 0)
|
|
1387
|
+
return candidates;
|
|
1388
|
+
}
|
|
1389
|
+
currentId = scope.parent;
|
|
1390
|
+
}
|
|
1391
|
+
return [];
|
|
1392
|
+
}
|
|
1393
|
+
/**
|
|
1394
|
+
* Look up all callable bindings (Function/Method/Constructor) by name
|
|
1395
|
+
* from the nearest scope in the chain that binds `callableName`.
|
|
1396
|
+
*
|
|
1397
|
+
* Preserves the original scope-walk boundary used by
|
|
1398
|
+
* `findCallableBindingInScope`: once any callable binding is found in a
|
|
1399
|
+
* scope, outer scopes are not consulted.
|
|
1400
|
+
*/
|
|
1401
|
+
/**
|
|
1402
|
+
* Every definition visible for `name` at the NEAREST scope that binds it,
|
|
1403
|
+
* filtered by `predicate` and deduped by `nodeId`.
|
|
1404
|
+
*
|
|
1405
|
+
* THE shared "collect all at the nearest binding scope" walk. `walkScopeChain`
|
|
1406
|
+
* answers the first-match question; this answers the how-many question, which is
|
|
1407
|
+
* what a caller needs before it can decline on ambiguity.
|
|
1408
|
+
*
|
|
1409
|
+
* Stops at the first scope that binds the name at all: an inner binding SHADOWS
|
|
1410
|
+
* an outer one, so continuing would report a shadowed outer definition as a
|
|
1411
|
+
* competing candidate and decline a name that is unambiguous at this point.
|
|
1412
|
+
*
|
|
1413
|
+
* Returns `[]` on a cycle or a missing scope. That is deliberate and matters:
|
|
1414
|
+
* an earlier copy of this walk `break`-ed instead and fell through to a
|
|
1415
|
+
* qualified-name fallback, so the same malformed input produced a different
|
|
1416
|
+
* answer depending on which copy the caller happened to reach.
|
|
1417
|
+
*/
|
|
1418
|
+
function findAllBindingsInScope(startScope, name, scopes, predicate) {
|
|
1419
|
+
let currentId = startScope;
|
|
1420
|
+
const visited = new Set();
|
|
1421
|
+
while (currentId !== null) {
|
|
1422
|
+
if (visited.has(currentId))
|
|
1423
|
+
return [];
|
|
1424
|
+
visited.add(currentId);
|
|
1425
|
+
const scope = scopes.scopeTree.getScope(currentId);
|
|
1426
|
+
if (scope === undefined)
|
|
1427
|
+
return [];
|
|
1428
|
+
// `Object` scopes are a hoist boundary only -- see walkScopeChain's
|
|
1429
|
+
// comment (#2545/#2551). Skip lookup here, still traverse to parent.
|
|
1430
|
+
if (scope.kind !== 'Object') {
|
|
1431
|
+
const out = [];
|
|
1432
|
+
const seen = new Set();
|
|
1433
|
+
const push = (def) => {
|
|
1434
|
+
if (!predicate(def))
|
|
1435
|
+
return;
|
|
1436
|
+
if (seen.has(def.nodeId))
|
|
1437
|
+
return;
|
|
1438
|
+
seen.add(def.nodeId);
|
|
1439
|
+
out.push(def);
|
|
1440
|
+
};
|
|
1441
|
+
// Local first: a binding in this scope shadows an imported one.
|
|
1442
|
+
for (const b of scope.bindings.get(name) ?? [])
|
|
1443
|
+
push(b.def);
|
|
1444
|
+
for (const b of lookupBindingsAt(currentId, name, scopes))
|
|
1445
|
+
push(b.def);
|
|
1446
|
+
if (out.length > 0)
|
|
1447
|
+
return out;
|
|
1448
|
+
}
|
|
1449
|
+
currentId = scope.parent;
|
|
1450
|
+
}
|
|
1451
|
+
return [];
|
|
1452
|
+
}
|
|
1453
|
+
function findAllCallableBindingsInScope(startScope, callableName, scopes) {
|
|
1454
|
+
return findAllBindingsInScope(startScope, callableName, scopes, (def) => def.type === 'Function' || def.type === 'Method' || def.type === 'Constructor');
|
|
1455
|
+
}
|
|
1456
|
+
/**
|
|
1457
|
+
* ISO C++ `[basic.lookup.unqual]` §7: ADL is suppressed when ordinary
|
|
1458
|
+
* unqualified lookup finds:
|
|
1459
|
+
* - a name that is NOT a function or function template, OR
|
|
1460
|
+
* - a block-scope function declaration that is NOT a using-declaration.
|
|
1461
|
+
*
|
|
1462
|
+
* Combined walker that stops at the **nearest scope** where `name` has any
|
|
1463
|
+
* binding (callable or non-callable) and returns:
|
|
1464
|
+
* - `callables`: Function/Method/Constructor defs found at that scope
|
|
1465
|
+
* - `nonCallableFound`: a non-function binding was present (variable, class, etc.)
|
|
1466
|
+
* - `blockScopeDeclFound`: a callable was found at a Function or Block scope
|
|
1467
|
+
* (block-scope function declaration that blocks ADL)
|
|
1468
|
+
*
|
|
1469
|
+
* One pass, one stop — no divergence between callable collection and blocker
|
|
1470
|
+
* detection.
|
|
1471
|
+
*/
|
|
1472
|
+
function findCallableBindingsAndAdlBlocker(startScope, name, scopes) {
|
|
1473
|
+
let currentId = startScope;
|
|
1474
|
+
const visited = new Set();
|
|
1475
|
+
while (currentId !== null) {
|
|
1476
|
+
if (visited.has(currentId))
|
|
1477
|
+
return { callables: [], nonCallableFound: false, blockScopeDeclFound: false };
|
|
1478
|
+
visited.add(currentId);
|
|
1479
|
+
const scope = scopes.scopeTree.getScope(currentId);
|
|
1480
|
+
if (scope === undefined)
|
|
1481
|
+
return { callables: [], nonCallableFound: false, blockScopeDeclFound: false };
|
|
1482
|
+
const callables = [];
|
|
1483
|
+
const seen = new Set();
|
|
1484
|
+
let nonCallableFound = false;
|
|
1485
|
+
let anyBinding = false;
|
|
1486
|
+
const process = (def) => {
|
|
1487
|
+
anyBinding = true;
|
|
1488
|
+
if (def.type === 'Function' || def.type === 'Method' || def.type === 'Constructor') {
|
|
1489
|
+
if (!seen.has(def.nodeId)) {
|
|
1490
|
+
seen.add(def.nodeId);
|
|
1491
|
+
callables.push(def);
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
else {
|
|
1495
|
+
nonCallableFound = true;
|
|
1496
|
+
}
|
|
1497
|
+
};
|
|
1498
|
+
// `Object` scopes are a hoist boundary only (#2545/#2551) -- never
|
|
1499
|
+
// reached by C++'s ADL path in practice (no language reusing this
|
|
1500
|
+
// function emits `@scope.object`), guarded for consistency with the
|
|
1501
|
+
// other scope-chain walkers in this file.
|
|
1502
|
+
if (scope.kind !== 'Object') {
|
|
1503
|
+
const localBindings = scope.bindings.get(name);
|
|
1504
|
+
if (localBindings !== undefined) {
|
|
1505
|
+
for (const b of localBindings) {
|
|
1506
|
+
process(b.def);
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
const importedBindings = lookupBindingsAt(currentId, name, scopes);
|
|
1510
|
+
for (const b of importedBindings) {
|
|
1511
|
+
process(b.def);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
if (anyBinding) {
|
|
1515
|
+
// ISO C++: a block-scope function declaration (Function or Block scope)
|
|
1516
|
+
// that is NOT a using-declaration blocks ADL. If we found callables at
|
|
1517
|
+
// a function/block scope, ADL must be suppressed.
|
|
1518
|
+
const blockScopeDeclFound = callables.length > 0 && (scope.kind === 'Function' || scope.kind === 'Block');
|
|
1519
|
+
return { callables, nonCallableFound, blockScopeDeclFound };
|
|
1520
|
+
}
|
|
1521
|
+
currentId = scope.parent;
|
|
1522
|
+
}
|
|
1523
|
+
return { callables: [], nonCallableFound: false, blockScopeDeclFound: false };
|
|
1524
|
+
}
|
|
1525
|
+
/**
|
|
1526
|
+
* Populate `ownerId` on every def structurally owned by a Class
|
|
1527
|
+
* scope — methods (defs in Function scopes whose parent is Class)
|
|
1528
|
+
* and class-body fields (defs directly in Class scopes).
|
|
1529
|
+
*
|
|
1530
|
+
* Generic OO ownership rule. Languages that want richer ownership
|
|
1531
|
+
* (e.g. inner-class qualification) can compose with this as a base
|
|
1532
|
+
* step.
|
|
1533
|
+
*
|
|
1534
|
+
* Mutates `parsed.localDefs` in place via type cast — `SymbolDefinition`
|
|
1535
|
+
* is `readonly` for consumers but the extractor returns plain objects.
|
|
1536
|
+
* Defs are shared by reference between `localDefs` and `Scope.ownedDefs`,
|
|
1537
|
+
* so this single mutation is visible from both sides.
|
|
1538
|
+
*/
|
|
1539
|
+
function populateClassOwnedMembers(parsed) {
|
|
1540
|
+
const scopesById = new Map();
|
|
1541
|
+
for (const scope of parsed.scopes)
|
|
1542
|
+
scopesById.set(scope.id, scope);
|
|
1543
|
+
// Promote a def's qualifiedName from `methodName` to `ClassName.methodName`
|
|
1544
|
+
// when the def sits inside a class. Without this, two classes in the
|
|
1545
|
+
// same file that share a method name collide at the graph-bridge lookup
|
|
1546
|
+
// (`node-lookup.ts` keys by (filePath, qualifiedName) and falls back to
|
|
1547
|
+
// simple name only). Python's scope query doesn't emit
|
|
1548
|
+
// `@declaration.qualified_name` for nested methods, so the finalized
|
|
1549
|
+
// defs arrive here with simple names — we stamp the qualifier while
|
|
1550
|
+
// we're already walking class scopes for ownerId.
|
|
1551
|
+
const qualify = (def, classDef) => {
|
|
1552
|
+
const q = def.qualifiedName;
|
|
1553
|
+
if (q === undefined || q.length === 0)
|
|
1554
|
+
return;
|
|
1555
|
+
if (q.includes('.'))
|
|
1556
|
+
return; // already qualified (dotted)
|
|
1557
|
+
// A synthesized anonymous-class def (Java `$`-chain binary name,
|
|
1558
|
+
// #2550/#2555 — `M3$2`, `EnumWrap$Mode$1`) already carries its
|
|
1559
|
+
// COMPLETE name. Prefixing it (`M3.M3$2`) desyncs from the
|
|
1560
|
+
// structure-phase node id (`M3$2.hook`), so same-named methods
|
|
1561
|
+
// across sibling enum-constant bodies collapse onto the first
|
|
1562
|
+
// body's node via the simple-name fallback (empirically caught in
|
|
1563
|
+
// review). Class-like only: `$`-named MEMBERS (legal in JS/TS)
|
|
1564
|
+
// still qualify normally against their class.
|
|
1565
|
+
if (isClassLike(def.type) && q.includes('$'))
|
|
1566
|
+
return;
|
|
1567
|
+
const classQ = classDef.qualifiedName;
|
|
1568
|
+
if (classQ === undefined || classQ.length === 0)
|
|
1569
|
+
return;
|
|
1570
|
+
def.qualifiedName = `${classQ}.${q}`;
|
|
1571
|
+
};
|
|
1572
|
+
// Depth invariant (verified empirically against Python scope-extractor
|
|
1573
|
+
// 2026-04-21): a nested `def helper` declared inside a method body
|
|
1574
|
+
// lives in its OWN Function scope whose parent is the method's Function
|
|
1575
|
+
// scope (not the Class scope). That means the `parentScope.kind ===
|
|
1576
|
+
// 'Class'` branch below only matches DIRECT class-scope children —
|
|
1577
|
+
// method defs themselves — and never stamps arbitrary nested defs with
|
|
1578
|
+
// `ownerId = classDef.nodeId`. If an adversarial reviewer raises this
|
|
1579
|
+
// as a potential false-attribution bug, verify first with a scope dump
|
|
1580
|
+
// on `class U: def save(self): def helper(): ...` — helper.ownerId will
|
|
1581
|
+
// remain undefined. The theoretical concern is real only if the
|
|
1582
|
+
// extractor ever stops creating scopes for inner defs.
|
|
1583
|
+
for (const scope of parsed.scopes) {
|
|
1584
|
+
// Methods: function scope whose parent is a Class scope. Owner is
|
|
1585
|
+
// the parent's class-like def.
|
|
1586
|
+
if (scope.parent !== null) {
|
|
1587
|
+
const parentScope = scopesById.get(scope.parent);
|
|
1588
|
+
if (parentScope !== undefined && parentScope.kind === 'Class') {
|
|
1589
|
+
const classDef = parentScope.ownedDefs.find((d) => isClassLike(d.type));
|
|
1590
|
+
if (classDef !== undefined) {
|
|
1591
|
+
for (const def of scope.ownedDefs) {
|
|
1592
|
+
def.ownerId = classDef.nodeId;
|
|
1593
|
+
qualify(def, classDef);
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
// Class-body fields: defs directly owned by a Class scope (the
|
|
1599
|
+
// class-like def itself excluded).
|
|
1600
|
+
if (scope.kind === 'Class') {
|
|
1601
|
+
const classDef = scope.ownedDefs.find((d) => isClassLike(d.type));
|
|
1602
|
+
if (classDef !== undefined) {
|
|
1603
|
+
for (const def of scope.ownedDefs) {
|
|
1604
|
+
if (def === classDef)
|
|
1605
|
+
continue;
|
|
1606
|
+
def.ownerId = classDef.nodeId;
|
|
1607
|
+
qualify(def, classDef);
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
/**
|
|
1614
|
+
* Tag every def declared inside one or more `Namespace` scopes with its
|
|
1615
|
+
* enclosing-namespace path (`NS`, `Outer.Inner`) on a sidecar `namespacePrefix`
|
|
1616
|
+
* field — WITHOUT touching `qualifiedName`.
|
|
1617
|
+
*
|
|
1618
|
+
* Some scope-extractors qualify a nested type by its enclosing CLASS chain
|
|
1619
|
+
* (`A.Inner`) but drop the enclosing NAMESPACE, while the structure phase keys
|
|
1620
|
+
* the graph node by the full path (`NS.A.Inner`). `resolveDefGraphId` reads this
|
|
1621
|
+
* tag to retry the node lookup with the namespace-prefixed key before the
|
|
1622
|
+
* simple-name fallback, so same-tail nested bases don't collapse across sibling
|
|
1623
|
+
* namespace members (#1982). `qualifiedName` is deliberately left unchanged, so
|
|
1624
|
+
* the `qualifiedName`-keyed resolution index and existing namespace resolution
|
|
1625
|
+
* (brace-init, UDC ranking, two-phase lookup) are untouched.
|
|
1626
|
+
*
|
|
1627
|
+
* Language-agnostic: it acts only on `Namespace`-kind scopes (a namespace-free
|
|
1628
|
+
* language is a no-op) and is opt-in per provider (call after `populateOwners`).
|
|
1629
|
+
* Namespace segments are taken as each namespace def's own tail, so it composes
|
|
1630
|
+
* for nested namespaces regardless of whether the inner namespace's name is
|
|
1631
|
+
* stored simple or already dotted. Skips defs already carrying the prefix.
|
|
1632
|
+
*/
|
|
1633
|
+
function tagNamespacePrefixes(parsed, options = {}) {
|
|
1634
|
+
// Whether a def's `qualifiedName` may ALREADY encode its enclosing namespace.
|
|
1635
|
+
// Where it can (C++, C#), a name equal to — or prefixed by — the namespace path
|
|
1636
|
+
// must not be tagged again. Where it cannot, that guard misreads a coincidence:
|
|
1637
|
+
// a Rust `mod a { pub fn a() }` has `qualifiedName === 'a'` purely because the
|
|
1638
|
+
// item and its module share a name, and skipping it leaves the member looking
|
|
1639
|
+
// like it belongs to the PARENT module.
|
|
1640
|
+
const alreadyQualified = options.qualifiedNamesCarryNamespace === undefined
|
|
1641
|
+
? true
|
|
1642
|
+
: options.qualifiedNamesCarryNamespace;
|
|
1643
|
+
const scopesById = new Map();
|
|
1644
|
+
for (const scope of parsed.scopes)
|
|
1645
|
+
scopesById.set(scope.id, scope);
|
|
1646
|
+
// Enclosing-namespace prefix for a scope: the dotted path of each ancestor
|
|
1647
|
+
// Namespace scope's name, outermost-first (`['Outer','Inner'] → 'Outer.Inner'`).
|
|
1648
|
+
const namespacePrefixOf = (scope) => {
|
|
1649
|
+
const segments = [];
|
|
1650
|
+
let parentId = scope.parent;
|
|
1651
|
+
while (parentId !== null) {
|
|
1652
|
+
const parent = scopesById.get(parentId);
|
|
1653
|
+
if (parent === undefined)
|
|
1654
|
+
break;
|
|
1655
|
+
if (parent.kind === 'Namespace') {
|
|
1656
|
+
const nsDef = parent.ownedDefs.find((d) => d.type === 'Namespace');
|
|
1657
|
+
const nsQ = nsDef?.qualifiedName;
|
|
1658
|
+
if (nsQ !== undefined && nsQ.length > 0) {
|
|
1659
|
+
const dot = nsQ.lastIndexOf('.');
|
|
1660
|
+
segments.unshift(dot === -1 ? nsQ : nsQ.slice(dot + 1));
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
parentId = parent.parent;
|
|
1664
|
+
}
|
|
1665
|
+
return segments.join('.');
|
|
1666
|
+
};
|
|
1667
|
+
for (const scope of parsed.scopes) {
|
|
1668
|
+
if (scope.kind === 'Namespace')
|
|
1669
|
+
continue;
|
|
1670
|
+
const prefix = namespacePrefixOf(scope);
|
|
1671
|
+
if (prefix.length === 0)
|
|
1672
|
+
continue;
|
|
1673
|
+
for (const def of scope.ownedDefs) {
|
|
1674
|
+
const q = def.qualifiedName;
|
|
1675
|
+
if (q === undefined || q.length === 0)
|
|
1676
|
+
continue;
|
|
1677
|
+
if (alreadyQualified && (q === prefix || q.startsWith(`${prefix}.`)))
|
|
1678
|
+
continue;
|
|
1679
|
+
def.namespacePrefix = prefix;
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
// #1993: also tag defs declared DIRECTLY in a Namespace scope with that
|
|
1683
|
+
// namespace's OWN full path. The loop above only reaches class-nested defs
|
|
1684
|
+
// (`A::Inner`); a deriving class like `NS1::DA` lives in the namespace scope and
|
|
1685
|
+
// is skipped, so it would carry no prefix and a same-tail cross-namespace base
|
|
1686
|
+
// tie (`NS1::A::Inner` vs `NS2::A::Inner`) could not be broken by the deriving
|
|
1687
|
+
// side. Composed identically to the class-nested path (enclosing tails + own
|
|
1688
|
+
// tail) so the two agree; still sidecar-only (`qualifiedName` untouched).
|
|
1689
|
+
for (const scope of parsed.scopes) {
|
|
1690
|
+
if (scope.kind !== 'Namespace')
|
|
1691
|
+
continue;
|
|
1692
|
+
const ownNsDef = scope.ownedDefs.find((d) => d.type === 'Namespace');
|
|
1693
|
+
const ownQ = ownNsDef?.qualifiedName;
|
|
1694
|
+
if (ownQ === undefined || ownQ.length === 0)
|
|
1695
|
+
continue;
|
|
1696
|
+
const ownTail = ownQ.slice(ownQ.lastIndexOf('.') + 1);
|
|
1697
|
+
const parentPrefix = namespacePrefixOf(scope);
|
|
1698
|
+
const fullPrefix = parentPrefix.length > 0 ? `${parentPrefix}.${ownTail}` : ownTail;
|
|
1699
|
+
for (const def of scope.ownedDefs) {
|
|
1700
|
+
if (def.type === 'Namespace')
|
|
1701
|
+
continue;
|
|
1702
|
+
const q = def.qualifiedName;
|
|
1703
|
+
if (q === undefined || q.length === 0)
|
|
1704
|
+
continue;
|
|
1705
|
+
if (alreadyQualified && (q === fullPrefix || q.startsWith(`${fullPrefix}.`)))
|
|
1706
|
+
continue;
|
|
1707
|
+
if (def.namespacePrefix !== undefined)
|
|
1708
|
+
continue;
|
|
1709
|
+
def.namespacePrefix = fullPrefix;
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
/**
|
|
1714
|
+
* Walk a scope chain upward looking for the innermost enclosing
|
|
1715
|
+
* Class scope and return that class's def. Used by per-language
|
|
1716
|
+
* `super` receiver branches to discover the dispatch base.
|
|
1717
|
+
*/
|
|
1718
|
+
function findEnclosingClassDef(startScope, scopes) {
|
|
1719
|
+
let currentId = startScope;
|
|
1720
|
+
const visited = new Set();
|
|
1721
|
+
while (currentId !== null) {
|
|
1722
|
+
if (visited.has(currentId))
|
|
1723
|
+
return undefined;
|
|
1724
|
+
visited.add(currentId);
|
|
1725
|
+
const scope = scopes.scopeTree.getScope(currentId);
|
|
1726
|
+
if (scope === undefined)
|
|
1727
|
+
return undefined;
|
|
1728
|
+
if (scope.kind === 'Class') {
|
|
1729
|
+
const cd = scope.ownedDefs.find((d) => isClassLike(d.type));
|
|
1730
|
+
if (cd !== undefined)
|
|
1731
|
+
return cd;
|
|
1732
|
+
}
|
|
1733
|
+
currentId = scope.parent;
|
|
1734
|
+
}
|
|
1735
|
+
return undefined;
|
|
1736
|
+
}
|
|
1737
|
+
/**
|
|
1738
|
+
* Find a free-function def by simple name across all parsed files,
|
|
1739
|
+
* preferring scope-chain-visible bindings (import + finalized scope
|
|
1740
|
+
* bindings) before falling back to a workspace-wide simple-name scan.
|
|
1741
|
+
*
|
|
1742
|
+
* The fallback scan is intentionally loose so per-language compound
|
|
1743
|
+
* resolvers can find a callable target even when the binding chain
|
|
1744
|
+
* doesn't surface it (e.g. cross-package re-exports the finalize
|
|
1745
|
+
* pass missed). Strictly-typed languages may want to disable the
|
|
1746
|
+
* fallback by simply not calling this helper from their compound
|
|
1747
|
+
* resolver.
|
|
1748
|
+
*/
|
|
1749
|
+
function findExportedDefByName(name, inScope, scopes, index) {
|
|
1750
|
+
let currentId = inScope;
|
|
1751
|
+
const visited = new Set();
|
|
1752
|
+
while (currentId !== null) {
|
|
1753
|
+
if (visited.has(currentId))
|
|
1754
|
+
break;
|
|
1755
|
+
visited.add(currentId);
|
|
1756
|
+
const scope = scopes.scopeTree.getScope(currentId);
|
|
1757
|
+
if (scope === undefined)
|
|
1758
|
+
break;
|
|
1759
|
+
// `Object` scopes are a hoist boundary only (#2545/#2551).
|
|
1760
|
+
if (scope.kind !== 'Object') {
|
|
1761
|
+
const local = scope.bindings.get(name);
|
|
1762
|
+
if (local !== undefined) {
|
|
1763
|
+
for (const b of local) {
|
|
1764
|
+
if (b.def.type === 'Function' || b.def.type === 'Method')
|
|
1765
|
+
return b.def;
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
const finalized = lookupBindingsAt(currentId, name, scopes);
|
|
1769
|
+
for (const b of finalized) {
|
|
1770
|
+
if (b.def.type === 'Function' || b.def.type === 'Method')
|
|
1771
|
+
return b.def;
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
currentId = scope.parent;
|
|
1775
|
+
}
|
|
1776
|
+
// Workspace-wide fallback: the first locally-declared callable binding
|
|
1777
|
+
// matching `name` across every file's Module scope (first-seen-by-file wins;
|
|
1778
|
+
// `origin === 'local'`, callable types Function/Method/Constructor). This is
|
|
1779
|
+
// precomputed ONCE into `index.exportedCallableByName` — byte-identical to the
|
|
1780
|
+
// old per-call scan over `moduleScopeByFile`, but O(1) and disk-read-free
|
|
1781
|
+
// (the old scan faulted every module scope in from disk under the out-of-core scope index). We use
|
|
1782
|
+
// this scope-derived index rather than `SemanticModel.symbols.lookupCallableByName`
|
|
1783
|
+
// because the `origin === 'local'` module-export-visibility filter is a scope
|
|
1784
|
+
// concept the raw symbol index doesn't express.
|
|
1785
|
+
return index.exportedCallableByName.get(name);
|
|
1786
|
+
}
|
|
1787
|
+
/**
|
|
1788
|
+
* Find a member of a class by simple name — delegates to
|
|
1789
|
+
* `SemanticModel.methods` (methods / functions / constructors) with a
|
|
1790
|
+
* fallback to `SemanticModel.fields` (properties / fields /
|
|
1791
|
+
* variables). After `runScopeResolution`'s reconciliation pass
|
|
1792
|
+
* populates both registries from `parsed.localDefs[i].ownerId`
|
|
1793
|
+
* (post-`populateOwners`), this is the single authoritative view of
|
|
1794
|
+
* class membership — no parallel scope-resolution index needed.
|
|
1795
|
+
*
|
|
1796
|
+
* Returns the first-seen overload for methods without arity or
|
|
1797
|
+
* return-type narrowing. Callers that need arity-aware dispatch use
|
|
1798
|
+
* `lookupMethodByOwner(owner, name, argCount)` directly.
|
|
1799
|
+
*/
|
|
1800
|
+
function findOwnedMember(ownerDefId, memberName, model) {
|
|
1801
|
+
const method = model.methods.lookupAllByOwner(ownerDefId, memberName)[0];
|
|
1802
|
+
if (method !== undefined)
|
|
1803
|
+
return method;
|
|
1804
|
+
return model.fields.lookupFieldByOwner(ownerDefId, memberName);
|
|
1805
|
+
}
|
|
1806
|
+
/**
|
|
1807
|
+
* Find a file-level def (top-of-module class / function / variable)
|
|
1808
|
+
* by simple name — consults the target file's Module scope's
|
|
1809
|
+
* finalized bindings. Only defs bound at module-scope with
|
|
1810
|
+
* `origin === 'local'` qualify, matching the historical
|
|
1811
|
+
* "module-export-visible" semantics. Class methods and class-body
|
|
1812
|
+
* fields bind at their containing class scope and are naturally
|
|
1813
|
+
* excluded.
|
|
1814
|
+
*
|
|
1815
|
+
* Reads from `WorkspaceResolutionIndex.moduleScopeByFile` (scope-tied
|
|
1816
|
+
* lookup that doesn't live on `SemanticModel`). This intentionally
|
|
1817
|
+
* does NOT call `lookupBindingsAt`: `findExportedDef` answers "what
|
|
1818
|
+
* did the target file declare locally at module scope?", while
|
|
1819
|
+
* `bindingAugmentations` models importer-side visibility created by
|
|
1820
|
+
* post-finalize hooks. Callers that need importer-visible exports use
|
|
1821
|
+
* `findExportedDefByName`, which is dual-channel aware.
|
|
1822
|
+
*/
|
|
1823
|
+
function findExportedDef(targetFile, memberName, index) {
|
|
1824
|
+
const moduleScope = index.moduleScopeByFile.get(targetFile);
|
|
1825
|
+
if (moduleScope === undefined)
|
|
1826
|
+
return undefined;
|
|
1827
|
+
const refs = moduleScope.bindings.get(memberName);
|
|
1828
|
+
if (refs === undefined)
|
|
1829
|
+
return undefined;
|
|
1830
|
+
for (const ref of refs) {
|
|
1831
|
+
if (ref.origin === 'local')
|
|
1832
|
+
return ref.def;
|
|
1833
|
+
}
|
|
1834
|
+
return undefined;
|
|
1835
|
+
}
|