@lzehrung/codegraph 2.3.30 → 2.4.0
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/README.md +30 -137
- package/codegraph-skill/codegraph/SKILL.md +5 -3
- package/dist/agent/artifact.d.ts +2 -0
- package/dist/agent/artifact.d.ts.map +1 -1
- package/dist/agent/artifact.js.map +1 -1
- package/dist/agent/search.d.ts.map +1 -1
- package/dist/agent/search.js +3 -0
- package/dist/agent/search.js.map +1 -1
- package/dist/agent/session.d.ts +3 -0
- package/dist/agent/session.d.ts.map +1 -1
- package/dist/agent/session.js +10 -2
- package/dist/agent/session.js.map +1 -1
- package/dist/bin/cli.js +42229 -28876
- package/dist/bin/query-index-worker.js +73 -11
- package/dist/cli/affected.d.ts.map +1 -1
- package/dist/cli/affected.js +4 -1
- package/dist/cli/affected.js.map +1 -1
- package/dist/cli/command-catalog.js +1 -1
- package/dist/cli/command-catalog.js.map +1 -1
- package/dist/cli/graph-queries.d.ts.map +1 -1
- package/dist/cli/graph-queries.js +27 -1
- package/dist/cli/graph-queries.js.map +1 -1
- package/dist/cli/help.d.ts +2 -2
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +4 -2
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/options.js +2 -2
- package/dist/cli/options.js.map +1 -1
- package/dist/drift/artifact.d.ts.map +1 -1
- package/dist/drift/artifact.js +15 -18
- package/dist/drift/artifact.js.map +1 -1
- package/dist/drift/compare.js +1 -1
- package/dist/drift/compare.js.map +1 -1
- package/dist/drift/snapshot.d.ts.map +1 -1
- package/dist/drift/snapshot.js +5 -6
- package/dist/drift/snapshot.js.map +1 -1
- package/dist/drift/types.d.ts +2 -0
- package/dist/drift/types.d.ts.map +1 -1
- package/dist/drift/types.js.map +1 -1
- package/dist/graph-builder.d.ts.map +1 -1
- package/dist/graph-builder.js +4 -6
- package/dist/graph-builder.js.map +1 -1
- package/dist/graph-edge-collector.d.ts +3 -0
- package/dist/graph-edge-collector.d.ts.map +1 -1
- package/dist/graph-edge-collector.js +8 -5
- package/dist/graph-edge-collector.js.map +1 -1
- package/dist/graphs/angularjs.d.ts.map +1 -1
- package/dist/graphs/angularjs.js +2 -1
- package/dist/graphs/angularjs.js.map +1 -1
- package/dist/graphs/edge-resolution.d.ts +4 -1
- package/dist/graphs/edge-resolution.d.ts.map +1 -1
- package/dist/graphs/edge-resolution.js +26 -2
- package/dist/graphs/edge-resolution.js.map +1 -1
- package/dist/graphs/specifiers.d.ts.map +1 -1
- package/dist/graphs/specifiers.js +6 -2
- package/dist/graphs/specifiers.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/ast.d.ts +20 -0
- package/dist/graphs/symbol-graph-detailed/ast.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/ast.js +154 -15
- package/dist/graphs/symbol-graph-detailed/ast.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/edge-passes.d.ts +51 -8
- package/dist/graphs/symbol-graph-detailed/edge-passes.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/edge-passes.js +823 -132
- package/dist/graphs/symbol-graph-detailed/edge-passes.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/import-aliases.d.ts +2 -1
- package/dist/graphs/symbol-graph-detailed/import-aliases.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/import-aliases.js +139 -7
- package/dist/graphs/symbol-graph-detailed/import-aliases.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/member-chains.d.ts +1 -0
- package/dist/graphs/symbol-graph-detailed/member-chains.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/member-chains.js +3 -1
- package/dist/graphs/symbol-graph-detailed/member-chains.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/receiver-calls.d.ts +128 -22
- package/dist/graphs/symbol-graph-detailed/receiver-calls.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/receiver-calls.js +872 -125
- package/dist/graphs/symbol-graph-detailed/receiver-calls.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed.js +434 -40
- package/dist/graphs/symbol-graph-detailed.js.map +1 -1
- package/dist/graphs/symbol-graph.d.ts +2 -0
- package/dist/graphs/symbol-graph.d.ts.map +1 -1
- package/dist/graphs/symbol-graph.js +34 -16
- package/dist/graphs/symbol-graph.js.map +1 -1
- package/dist/impact/call-compatibility/providers/index.d.ts +12 -0
- package/dist/impact/call-compatibility/providers/index.d.ts.map +1 -1
- package/dist/impact/call-compatibility/providers/index.js +16 -22
- package/dist/impact/call-compatibility/providers/index.js.map +1 -1
- package/dist/impact/call-compatibility/providers/types.d.ts +2 -0
- package/dist/impact/call-compatibility/providers/types.d.ts.map +1 -1
- package/dist/impact/call-compatibility/types.d.ts +6 -0
- package/dist/impact/call-compatibility/types.d.ts.map +1 -1
- package/dist/impact/call-compatibility.d.ts.map +1 -1
- package/dist/impact/call-compatibility.js +316 -552
- package/dist/impact/call-compatibility.js.map +1 -1
- package/dist/impact/map.js +1 -0
- package/dist/impact/map.js.map +1 -1
- package/dist/impact/member-resolution-coverage.d.ts.map +1 -1
- package/dist/impact/member-resolution-coverage.js +1 -1
- package/dist/impact/member-resolution-coverage.js.map +1 -1
- package/dist/impact/reference-cache.d.ts.map +1 -1
- package/dist/impact/reference-cache.js +2 -2
- package/dist/impact/reference-cache.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/indexer/ambiguous-resolution.d.ts +11 -0
- package/dist/indexer/ambiguous-resolution.d.ts.map +1 -0
- package/dist/indexer/ambiguous-resolution.js +19 -0
- package/dist/indexer/ambiguous-resolution.js.map +1 -0
- package/dist/indexer/build-cache/module-cache.d.ts +38 -0
- package/dist/indexer/build-cache/module-cache.d.ts.map +1 -1
- package/dist/indexer/build-cache/module-cache.js +151 -9
- package/dist/indexer/build-cache/module-cache.js.map +1 -1
- package/dist/indexer/build-cache/options.d.ts +16 -43
- package/dist/indexer/build-cache/options.d.ts.map +1 -1
- package/dist/indexer/build-cache/options.js +16 -43
- package/dist/indexer/build-cache/options.js.map +1 -1
- package/dist/indexer/build-cache/project-snapshot.d.ts +1 -0
- package/dist/indexer/build-cache/project-snapshot.d.ts.map +1 -1
- package/dist/indexer/build-cache/project-snapshot.js +117 -24
- package/dist/indexer/build-cache/project-snapshot.js.map +1 -1
- package/dist/indexer/build-cache/resolver-environment.d.ts.map +1 -1
- package/dist/indexer/build-cache/resolver-environment.js +32 -2
- package/dist/indexer/build-cache/resolver-environment.js.map +1 -1
- package/dist/indexer/build-cache.d.ts +1 -1
- package/dist/indexer/build-cache.d.ts.map +1 -1
- package/dist/indexer/build-cache.js +1 -1
- package/dist/indexer/build-cache.js.map +1 -1
- package/dist/indexer/build-index.d.ts.map +1 -1
- package/dist/indexer/build-index.js +593 -22
- package/dist/indexer/build-index.js.map +1 -1
- package/dist/indexer/call-hierarchy.js +1 -1
- package/dist/indexer/call-hierarchy.js.map +1 -1
- package/dist/indexer/compilation-units.d.ts +101 -0
- package/dist/indexer/compilation-units.d.ts.map +1 -0
- package/dist/indexer/compilation-units.js +605 -0
- package/dist/indexer/compilation-units.js.map +1 -0
- package/dist/indexer/cpp-callables.d.ts +20 -0
- package/dist/indexer/cpp-callables.d.ts.map +1 -0
- package/dist/indexer/cpp-callables.js +554 -0
- package/dist/indexer/cpp-callables.js.map +1 -0
- package/dist/indexer/declaration-visibility.d.ts +36 -2
- package/dist/indexer/declaration-visibility.d.ts.map +1 -1
- package/dist/indexer/declaration-visibility.js +136 -2
- package/dist/indexer/declaration-visibility.js.map +1 -1
- package/dist/indexer/expand-star-imports.d.ts.map +1 -1
- package/dist/indexer/expand-star-imports.js +28 -6
- package/dist/indexer/expand-star-imports.js.map +1 -1
- package/dist/indexer/import-types.d.ts +37 -0
- package/dist/indexer/import-types.d.ts.map +1 -1
- package/dist/indexer/import-types.js +24 -1
- package/dist/indexer/import-types.js.map +1 -1
- package/dist/indexer/imports/binding-ranges.d.ts +1 -1
- package/dist/indexer/imports/binding-ranges.d.ts.map +1 -1
- package/dist/indexer/imports/context.d.ts +11 -1
- package/dist/indexer/imports/context.d.ts.map +1 -1
- package/dist/indexer/imports/import-binding-tables.d.ts +75 -0
- package/dist/indexer/imports/import-binding-tables.d.ts.map +1 -0
- package/dist/indexer/imports/import-binding-tables.js +544 -0
- package/dist/indexer/imports/import-binding-tables.js.map +1 -0
- package/dist/indexer/imports/js-text-imports.js +4 -4
- package/dist/indexer/imports/js-text-imports.js.map +1 -1
- package/dist/indexer/imports/language-specific.d.ts +3 -22
- package/dist/indexer/imports/language-specific.d.ts.map +1 -1
- package/dist/indexer/imports/language-specific.js +29 -362
- package/dist/indexer/imports/language-specific.js.map +1 -1
- package/dist/indexer/imports/native-captures.d.ts.map +1 -1
- package/dist/indexer/imports/native-captures.js +18 -6
- package/dist/indexer/imports/native-captures.js.map +1 -1
- package/dist/indexer/imports/python.d.ts +10 -2
- package/dist/indexer/imports/python.d.ts.map +1 -1
- package/dist/indexer/imports/python.js +91 -40
- package/dist/indexer/imports/python.js.map +1 -1
- package/dist/indexer/imports/text-import-extractors.d.ts.map +1 -1
- package/dist/indexer/imports/text-import-extractors.js +51 -12
- package/dist/indexer/imports/text-import-extractors.js.map +1 -1
- package/dist/indexer/imports.d.ts.map +1 -1
- package/dist/indexer/imports.js +25 -5
- package/dist/indexer/imports.js.map +1 -1
- package/dist/indexer/incremental-plan.d.ts +33 -0
- package/dist/indexer/incremental-plan.d.ts.map +1 -1
- package/dist/indexer/incremental-plan.js +187 -1
- package/dist/indexer/incremental-plan.js.map +1 -1
- package/dist/indexer/locals-and-exports.d.ts +2 -0
- package/dist/indexer/locals-and-exports.d.ts.map +1 -1
- package/dist/indexer/locals-and-exports.js +565 -54
- package/dist/indexer/locals-and-exports.js.map +1 -1
- package/dist/indexer/name-lookup-policies/c-family.d.ts +7 -0
- package/dist/indexer/name-lookup-policies/c-family.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/c-family.js +117 -0
- package/dist/indexer/name-lookup-policies/c-family.js.map +1 -0
- package/dist/indexer/name-lookup-policies/csharp-using-static.d.ts +11 -0
- package/dist/indexer/name-lookup-policies/csharp-using-static.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/csharp-using-static.js +136 -0
- package/dist/indexer/name-lookup-policies/csharp-using-static.js.map +1 -0
- package/dist/indexer/name-lookup-policies/implicit-self.d.ts +10 -0
- package/dist/indexer/name-lookup-policies/implicit-self.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/implicit-self.js +113 -0
- package/dist/indexer/name-lookup-policies/implicit-self.js.map +1 -0
- package/dist/indexer/name-lookup-policies/index.d.ts +3 -0
- package/dist/indexer/name-lookup-policies/index.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/index.js +106 -0
- package/dist/indexer/name-lookup-policies/index.js.map +1 -0
- package/dist/indexer/name-lookup-policies/php.d.ts +3 -0
- package/dist/indexer/name-lookup-policies/php.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/php.js +75 -0
- package/dist/indexer/name-lookup-policies/php.js.map +1 -0
- package/dist/indexer/name-resolution-types.d.ts +99 -0
- package/dist/indexer/name-resolution-types.d.ts.map +1 -0
- package/dist/indexer/name-resolution-types.js +2 -0
- package/dist/indexer/name-resolution-types.js.map +1 -0
- package/dist/indexer/name-resolution.d.ts +52 -0
- package/dist/indexer/name-resolution.d.ts.map +1 -0
- package/dist/indexer/name-resolution.js +189 -0
- package/dist/indexer/name-resolution.js.map +1 -0
- package/dist/indexer/navigation-cpp.d.ts +50 -0
- package/dist/indexer/navigation-cpp.d.ts.map +1 -0
- package/dist/indexer/navigation-cpp.js +654 -0
- package/dist/indexer/navigation-cpp.js.map +1 -0
- package/dist/indexer/navigation-goto.d.ts +107 -4
- package/dist/indexer/navigation-goto.d.ts.map +1 -1
- package/dist/indexer/navigation-goto.js +2075 -137
- package/dist/indexer/navigation-goto.js.map +1 -1
- package/dist/indexer/navigation-local.d.ts +18 -2
- package/dist/indexer/navigation-local.d.ts.map +1 -1
- package/dist/indexer/navigation-local.js +498 -61
- package/dist/indexer/navigation-local.js.map +1 -1
- package/dist/indexer/navigation-php.d.ts +66 -0
- package/dist/indexer/navigation-php.d.ts.map +1 -1
- package/dist/indexer/navigation-php.js +287 -4
- package/dist/indexer/navigation-php.js.map +1 -1
- package/dist/indexer/navigation-references.d.ts +72 -5
- package/dist/indexer/navigation-references.d.ts.map +1 -1
- package/dist/indexer/navigation-references.js +620 -54
- package/dist/indexer/navigation-references.js.map +1 -1
- package/dist/indexer/navigation-resolve.d.ts +33 -2
- package/dist/indexer/navigation-resolve.d.ts.map +1 -1
- package/dist/indexer/navigation-resolve.js +446 -193
- package/dist/indexer/navigation-resolve.js.map +1 -1
- package/dist/indexer/navigation.d.ts +28 -2
- package/dist/indexer/navigation.d.ts.map +1 -1
- package/dist/indexer/navigation.js +1082 -151
- package/dist/indexer/navigation.js.map +1 -1
- package/dist/indexer/php-namespace-symbols.d.ts +66 -0
- package/dist/indexer/php-namespace-symbols.d.ts.map +1 -0
- package/dist/indexer/php-namespace-symbols.js +342 -0
- package/dist/indexer/php-namespace-symbols.js.map +1 -0
- package/dist/indexer/reference-context.d.ts +2 -0
- package/dist/indexer/reference-context.d.ts.map +1 -1
- package/dist/indexer/reference-context.js +4 -0
- package/dist/indexer/reference-context.js.map +1 -1
- package/dist/indexer/scope-nodes.d.ts +92 -13
- package/dist/indexer/scope-nodes.d.ts.map +1 -1
- package/dist/indexer/scope-nodes.js +212 -20
- package/dist/indexer/scope-nodes.js.map +1 -1
- package/dist/indexer/scope-types.d.ts +25 -1
- package/dist/indexer/scope-types.d.ts.map +1 -1
- package/dist/indexer/scope.d.ts.map +1 -1
- package/dist/indexer/scope.js +894 -45
- package/dist/indexer/scope.js.map +1 -1
- package/dist/indexer/shared-owner-identity.d.ts +53 -0
- package/dist/indexer/shared-owner-identity.d.ts.map +1 -0
- package/dist/indexer/shared-owner-identity.js +460 -0
- package/dist/indexer/shared-owner-identity.js.map +1 -0
- package/dist/indexer/shared.d.ts +1 -1
- package/dist/indexer/shared.d.ts.map +1 -1
- package/dist/indexer/shared.js +1 -1
- package/dist/indexer/shared.js.map +1 -1
- package/dist/indexer/star-import-precedence.d.ts +114 -0
- package/dist/indexer/star-import-precedence.d.ts.map +1 -0
- package/dist/indexer/star-import-precedence.js +396 -0
- package/dist/indexer/star-import-precedence.js.map +1 -0
- package/dist/indexer/symbols.d.ts +3 -1
- package/dist/indexer/symbols.d.ts.map +1 -1
- package/dist/indexer/symbols.js +58 -18
- package/dist/indexer/symbols.js.map +1 -1
- package/dist/indexer/ts-callables.d.ts +51 -0
- package/dist/indexer/ts-callables.d.ts.map +1 -0
- package/dist/indexer/ts-callables.js +264 -0
- package/dist/indexer/ts-callables.js.map +1 -0
- package/dist/indexer/types.d.ts +23 -1
- package/dist/indexer/types.d.ts.map +1 -1
- package/dist/indexer/unused-exports.d.ts +13 -0
- package/dist/indexer/unused-exports.d.ts.map +1 -0
- package/dist/indexer/unused-exports.js +159 -0
- package/dist/indexer/unused-exports.js.map +1 -0
- package/dist/indexer/workspace-symbols.d.ts.map +1 -1
- package/dist/indexer/workspace-symbols.js +3 -2
- package/dist/indexer/workspace-symbols.js.map +1 -1
- package/dist/indexer.d.ts +2 -2
- package/dist/indexer.d.ts.map +1 -1
- package/dist/indexer.js +1 -1
- package/dist/indexer.js.map +1 -1
- package/dist/languages/callable-arity.d.ts +202 -0
- package/dist/languages/callable-arity.d.ts.map +1 -0
- package/dist/languages/callable-arity.js +1182 -0
- package/dist/languages/callable-arity.js.map +1 -0
- package/dist/languages/definitions/c-family.d.ts.map +1 -1
- package/dist/languages/definitions/c-family.js +30 -1
- package/dist/languages/definitions/c-family.js.map +1 -1
- package/dist/languages/definitions/c.d.ts +4 -0
- package/dist/languages/definitions/c.d.ts.map +1 -1
- package/dist/languages/definitions/c.js +19 -0
- package/dist/languages/definitions/c.js.map +1 -1
- package/dist/languages/definitions/cpp.d.ts.map +1 -1
- package/dist/languages/definitions/cpp.js +12 -0
- package/dist/languages/definitions/cpp.js.map +1 -1
- package/dist/languages/definitions/csharp.d.ts.map +1 -1
- package/dist/languages/definitions/csharp.js +2 -8
- package/dist/languages/definitions/csharp.js.map +1 -1
- package/dist/languages/definitions/go.d.ts.map +1 -1
- package/dist/languages/definitions/go.js +3 -0
- package/dist/languages/definitions/go.js.map +1 -1
- package/dist/languages/definitions/javascript.d.ts.map +1 -1
- package/dist/languages/definitions/javascript.js +10 -1
- package/dist/languages/definitions/javascript.js.map +1 -1
- package/dist/languages/definitions/js-family.d.ts +1 -1
- package/dist/languages/definitions/js-family.d.ts.map +1 -1
- package/dist/languages/definitions/js-family.js +7 -1
- package/dist/languages/definitions/js-family.js.map +1 -1
- package/dist/languages/definitions/php.d.ts.map +1 -1
- package/dist/languages/definitions/php.js +5 -1
- package/dist/languages/definitions/php.js.map +1 -1
- package/dist/languages/definitions/typescript.d.ts.map +1 -1
- package/dist/languages/definitions/typescript.js +8 -1
- package/dist/languages/definitions/typescript.js.map +1 -1
- package/dist/languages/definitions/zig.d.ts.map +1 -1
- package/dist/languages/definitions/zig.js +2 -0
- package/dist/languages/definitions/zig.js.map +1 -1
- package/dist/mcp/handlers.js +14 -7
- package/dist/mcp/handlers.js.map +1 -1
- package/dist/review/candidates.d.ts.map +1 -1
- package/dist/review/candidates.js +1 -1
- package/dist/review/candidates.js.map +1 -1
- package/dist/review/deleted.d.ts +3 -3
- package/dist/review/deleted.d.ts.map +1 -1
- package/dist/review/deleted.js +82 -32
- package/dist/review/deleted.js.map +1 -1
- package/dist/review/report.d.ts +1 -0
- package/dist/review/report.d.ts.map +1 -1
- package/dist/review/report.js +1 -1
- package/dist/review/report.js.map +1 -1
- package/dist/review.d.ts.map +1 -1
- package/dist/review.js +1 -0
- package/dist/review.js.map +1 -1
- package/dist/sql/navigation.d.ts.map +1 -1
- package/dist/sql/navigation.js +1 -0
- package/dist/sql/navigation.js.map +1 -1
- package/dist/sql/source-graph.d.ts.map +1 -1
- package/dist/sql/source-graph.js +2 -2
- package/dist/sql/source-graph.js.map +1 -1
- package/dist/sqlite/schema.d.ts +1 -1
- package/dist/sqlite/schema.d.ts.map +1 -1
- package/dist/sqlite/schema.js +8 -2
- package/dist/sqlite/schema.js.map +1 -1
- package/dist/sqlite/write.js +2 -2
- package/dist/sqlite/write.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/util/bloom-filter.d.ts +7 -1
- package/dist/util/bloom-filter.d.ts.map +1 -1
- package/dist/util/bloom-filter.js +12 -2
- package/dist/util/bloom-filter.js.map +1 -1
- package/dist/util/dynamic-import-tables.d.ts +88 -0
- package/dist/util/dynamic-import-tables.d.ts.map +1 -0
- package/dist/util/dynamic-import-tables.js +100 -0
- package/dist/util/dynamic-import-tables.js.map +1 -0
- package/dist/util/graph-edges.d.ts +19 -1
- package/dist/util/graph-edges.d.ts.map +1 -1
- package/dist/util/graph-edges.js +30 -4
- package/dist/util/graph-edges.js.map +1 -1
- package/dist/util/identifiers.d.ts +15 -0
- package/dist/util/identifiers.d.ts.map +1 -1
- package/dist/util/identifiers.js +35 -0
- package/dist/util/identifiers.js.map +1 -1
- package/dist/util/member-access-tables.d.ts +129 -0
- package/dist/util/member-access-tables.d.ts.map +1 -0
- package/dist/util/member-access-tables.js +285 -0
- package/dist/util/member-access-tables.js.map +1 -0
- package/dist/util/member-access.d.ts +17 -0
- package/dist/util/member-access.d.ts.map +1 -1
- package/dist/util/member-access.js +138 -93
- package/dist/util/member-access.js.map +1 -1
- package/dist/util/resolution/cargo-targets.d.ts +11 -0
- package/dist/util/resolution/cargo-targets.d.ts.map +1 -1
- package/dist/util/resolution/cargo-targets.js +174 -13
- package/dist/util/resolution/cargo-targets.js.map +1 -1
- package/dist/util/resolution/csharp.d.ts +33 -1
- package/dist/util/resolution/csharp.d.ts.map +1 -1
- package/dist/util/resolution/csharp.js +145 -30
- package/dist/util/resolution/csharp.js.map +1 -1
- package/dist/util/resolution/php.d.ts.map +1 -1
- package/dist/util/resolution/php.js +35 -8
- package/dist/util/resolution/php.js.map +1 -1
- package/dist/util/resolution/python.d.ts +9 -0
- package/dist/util/resolution/python.d.ts.map +1 -1
- package/dist/util/resolution/python.js +46 -0
- package/dist/util/resolution/python.js.map +1 -1
- package/dist/util/resolution/rust.d.ts.map +1 -1
- package/dist/util/resolution/rust.js +95 -26
- package/dist/util/resolution/rust.js.map +1 -1
- package/dist/util/resolution-candidates.d.ts +5 -0
- package/dist/util/resolution-candidates.d.ts.map +1 -1
- package/dist/util/resolution-candidates.js +16 -3
- package/dist/util/resolution-candidates.js.map +1 -1
- package/dist/util/resolution.d.ts +2 -1
- package/dist/util/resolution.d.ts.map +1 -1
- package/dist/util/resolution.js +96 -5
- package/dist/util/resolution.js.map +1 -1
- package/dist/util/specifiers.d.ts +20 -0
- package/dist/util/specifiers.d.ts.map +1 -1
- package/dist/util/specifiers.js +39 -93
- package/dist/util/specifiers.js.map +1 -1
- package/docs/mcp.md +3 -2
- package/package.json +3 -3
|
@@ -1,16 +1,59 @@
|
|
|
1
|
+
import path from "node:path";
|
|
1
2
|
import { supportForFileWithoutHeaderSample } from "../languages.js";
|
|
2
|
-
import { fileIdentityKey } from "../util/paths.js";
|
|
3
|
+
import { fileIdentityKey, normalizePath } from "../util/paths.js";
|
|
3
4
|
import { sliceText, toRange } from "../util/ast.js";
|
|
5
|
+
import { getMemberAccessParts, isMemberAccessNode, isReceiverNameNode } from "../util/member-access.js";
|
|
6
|
+
import { classifyReceiver, declaresMembers, receiverConstructorExpression, } from "../graphs/symbol-graph-detailed/receiver-calls.js";
|
|
7
|
+
import { provenClassifiedReceiverOmitsMember } from "./navigation-goto.js";
|
|
4
8
|
import { ensureParsedContext } from "./parse-context.js";
|
|
5
|
-
import { sameDef } from "./reference-context.js";
|
|
6
|
-
import { readPhpNamespaceFromRange } from "./navigation-php.js";
|
|
9
|
+
import { definitionIdentityKey, sameDef } from "./reference-context.js";
|
|
10
|
+
import { canonicalPhpReferenceNames, comparePhpReferenceNames, getPhpQualifiedReference, inferPhpQualifiedReferenceImportType, isInsidePhpUseDeclaration, isPhpQualifiedReferenceNode, phpLastIdentifierSegment, readPhpNamespaceFromRange, selectFirstExistingPhpCanonicalName, } from "./navigation-php.js";
|
|
11
|
+
import { isKeywordReceiver, memberSyntaxNamesFreeFunction } from "../util/member-access-tables.js";
|
|
12
|
+
import { getCompilationUnitPeers } from "./compilation-units.js";
|
|
13
|
+
import { findClosestScopeBinding } from "./navigation-local.js";
|
|
14
|
+
import { scopeNodesFor } from "./scope-nodes.js";
|
|
7
15
|
import { candidateFilesImportingTarget } from "./reference-candidates.js";
|
|
8
16
|
import { buildScopeIndexFromSource } from "./scope.js";
|
|
17
|
+
import { bindingKindToSymbolKind } from "./declarations.js";
|
|
9
18
|
import { resolveExport, resolveImported } from "./navigation-resolve.js";
|
|
10
|
-
import {
|
|
19
|
+
import { isAmbiguousResolutionReason } from "./ambiguous-resolution.js";
|
|
20
|
+
import { ensurePhpNamespaceSymbolIndex, phpNamespaceSymbolIndexFor, phpReferenceRoleMatchesKind, } from "./php-namespace-symbols.js";
|
|
21
|
+
import { SymbolKind, } from "./types.js";
|
|
22
|
+
import { ECMASCRIPT_IDENTIFIER_SOURCE, foldPhpIdentifierCase } from "../util/identifiers.js";
|
|
11
23
|
const EXPORT_FROM_PATTERN = new RegExp(String.raw `\bexport\s+(?:type\s+)?\{([^}]*)\}\s*from\s*(["'])([^"']+)\2`, "gu");
|
|
12
24
|
const NAMESPACE_EXPORT_PATTERN = new RegExp(String.raw `\bexport\s*\*\s*as\s*(${ECMASCRIPT_IDENTIFIER_SOURCE})\s*from\s*(["'])([^"']+)\2`, "gu");
|
|
13
25
|
const EXPORT_FROM_SPECIFIER_PATTERN = new RegExp(String.raw `^(${ECMASCRIPT_IDENTIFIER_SOURCE})`, "u");
|
|
26
|
+
const importClosureCache = new WeakMap();
|
|
27
|
+
/** File keys reachable from `file` through resolved imports and includes, including `file`. */
|
|
28
|
+
function importClosure(index, file) {
|
|
29
|
+
let byFile = importClosureCache.get(index);
|
|
30
|
+
if (!byFile) {
|
|
31
|
+
byFile = new Map();
|
|
32
|
+
importClosureCache.set(index, byFile);
|
|
33
|
+
}
|
|
34
|
+
const startKey = fileIdentityKey(file);
|
|
35
|
+
const cached = byFile.get(startKey);
|
|
36
|
+
if (cached)
|
|
37
|
+
return cached;
|
|
38
|
+
const reached = new Set([startKey]);
|
|
39
|
+
const startModule = index.byFile.get(startKey);
|
|
40
|
+
const pending = startModule ? [startModule] : [];
|
|
41
|
+
while (pending.length) {
|
|
42
|
+
for (const imp of pending.pop().imports) {
|
|
43
|
+
if (typeof imp.resolved !== "string")
|
|
44
|
+
continue;
|
|
45
|
+
const key = fileIdentityKey(imp.resolved);
|
|
46
|
+
if (reached.has(key))
|
|
47
|
+
continue;
|
|
48
|
+
reached.add(key);
|
|
49
|
+
const moduleEntry = index.byFile.get(key);
|
|
50
|
+
if (moduleEntry)
|
|
51
|
+
pending.push(moduleEntry);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
byFile.set(startKey, reached);
|
|
55
|
+
return reached;
|
|
56
|
+
}
|
|
14
57
|
export function exportFromIdentifier(index, fileId, range, parsed) {
|
|
15
58
|
if (!parsed.sup.supportsExportFromReferences)
|
|
16
59
|
return null;
|
|
@@ -67,11 +110,25 @@ export function exportFromIdentifier(index, fileId, range, parsed) {
|
|
|
67
110
|
return null;
|
|
68
111
|
}
|
|
69
112
|
export function getCachedScope(index, fileId, moduleIndex, parsedCtx) {
|
|
113
|
+
const keepOccurrence = (binding, occurrence) => {
|
|
114
|
+
if (exportFromIdentifier(index, fileId, occurrence, parsedCtx)?.isExportFrom)
|
|
115
|
+
return false;
|
|
116
|
+
if (parsedCtx.sup.id !== "php" || !binding.def || occurrence.start.index === binding.def.start.index)
|
|
117
|
+
return true;
|
|
118
|
+
const kind = bindingKindToSymbolKind(binding.kind);
|
|
119
|
+
if (kind === SymbolKind.Variable)
|
|
120
|
+
return true;
|
|
121
|
+
const start = occurrence.start.index;
|
|
122
|
+
if (start === undefined)
|
|
123
|
+
return true;
|
|
124
|
+
const node = parsedCtx.tree.rootNode.descendantForIndex(start, start);
|
|
125
|
+
return phpReferenceRoleMatchesKind(node, kind);
|
|
126
|
+
};
|
|
70
127
|
const fileKey = fileIdentityKey(fileId);
|
|
71
128
|
const cachedScope = index.scopeCache.get(fileKey);
|
|
72
129
|
if (cachedScope) {
|
|
73
130
|
for (const binding of cachedScope.all) {
|
|
74
|
-
binding.occurrences = binding.occurrences.filter((occurrence) =>
|
|
131
|
+
binding.occurrences = binding.occurrences.filter((occurrence) => keepOccurrence(binding, occurrence));
|
|
75
132
|
}
|
|
76
133
|
return cachedScope;
|
|
77
134
|
}
|
|
@@ -79,28 +136,182 @@ export function getCachedScope(index, fileId, moduleIndex, parsedCtx) {
|
|
|
79
136
|
tree: parsedCtx.tree,
|
|
80
137
|
});
|
|
81
138
|
for (const binding of scopeIndex.all) {
|
|
82
|
-
binding.occurrences = binding.occurrences.filter((occurrence) =>
|
|
139
|
+
binding.occurrences = binding.occurrences.filter((occurrence) => keepOccurrence(binding, occurrence));
|
|
83
140
|
}
|
|
84
141
|
index.scopeCache.set(fileKey, scopeIndex);
|
|
85
142
|
return scopeIndex;
|
|
86
143
|
}
|
|
87
|
-
|
|
144
|
+
/**
|
|
145
|
+
* The authoritative PHP spellings of a definition: `Namespace\Name`, or the bare `Name` for a
|
|
146
|
+
* global-namespace declaration. These are the names a reference must resolve to, so they keep
|
|
147
|
+
* the declaration's own case. A global-namespace declaration is still addressable by its bare
|
|
148
|
+
* name and by the fully-qualified `\Name` spelling, so the global-name scan must run for it;
|
|
149
|
+
* returning `[]` here left a global PHP symbol on the importer-narrowed path, which never scans
|
|
150
|
+
* a consumer that has no `use` statement.
|
|
151
|
+
*/
|
|
152
|
+
async function readPhpDefinitionNames(index, definitionFile, def) {
|
|
88
153
|
try {
|
|
89
154
|
const definitionParsed = await ensureParsedContext(definitionFile, index.parsed?.get(fileIdentityKey(definitionFile)), index.languageExtensions);
|
|
90
155
|
if (definitionParsed.sup.id !== "php") {
|
|
91
156
|
return [];
|
|
92
157
|
}
|
|
93
158
|
const phpNamespace = readPhpNamespaceFromRange(definitionParsed.tree, definitionParsed.source, def.range);
|
|
94
|
-
|
|
95
|
-
return [];
|
|
96
|
-
const qualifiedName = `${phpNamespace}\\${def.localName}`;
|
|
97
|
-
return [qualifiedName, `\\${qualifiedName}`];
|
|
159
|
+
return [phpNamespace ? `${phpNamespace}\\${def.localName}` : def.localName];
|
|
98
160
|
}
|
|
99
161
|
catch {
|
|
100
162
|
return [];
|
|
101
163
|
}
|
|
102
164
|
}
|
|
103
|
-
async function
|
|
165
|
+
export async function buildPhpQualifiedNames(index, definitionFile, def) {
|
|
166
|
+
// Keep one canonical spelling. Case-variant consumers are admitted during the single AST
|
|
167
|
+
// walk by comparePhpReferenceNames; extra folded probes would multiply whole-file scans.
|
|
168
|
+
return readPhpDefinitionNames(index, definitionFile, def);
|
|
169
|
+
}
|
|
170
|
+
const phpCanonicalNamesCache = new WeakMap();
|
|
171
|
+
const phpNameEquivalenceGaps = new WeakMap();
|
|
172
|
+
async function phpCanonicalDefinitionNames(index, def) {
|
|
173
|
+
let perIndex = phpCanonicalNamesCache.get(index);
|
|
174
|
+
if (!perIndex) {
|
|
175
|
+
perIndex = new Map();
|
|
176
|
+
phpCanonicalNamesCache.set(index, perIndex);
|
|
177
|
+
}
|
|
178
|
+
const key = definitionIdentityKey(def);
|
|
179
|
+
const cached = perIndex.get(key);
|
|
180
|
+
if (cached)
|
|
181
|
+
return cached;
|
|
182
|
+
const indexed = phpNamespaceSymbolIndexFor(index)?.canonicalByDefinition.get(key);
|
|
183
|
+
if (indexed) {
|
|
184
|
+
perIndex.set(key, [indexed]);
|
|
185
|
+
return [indexed];
|
|
186
|
+
}
|
|
187
|
+
const canonicalNames = (await readPhpDefinitionNames(index, def.file, def)).map((name) => name.replace(/^\\+/, ""));
|
|
188
|
+
perIndex.set(key, canonicalNames);
|
|
189
|
+
return canonicalNames;
|
|
190
|
+
}
|
|
191
|
+
function markPhpNameEquivalenceGap(index, def) {
|
|
192
|
+
let gaps = phpNameEquivalenceGaps.get(index);
|
|
193
|
+
if (!gaps) {
|
|
194
|
+
gaps = new Set();
|
|
195
|
+
phpNameEquivalenceGaps.set(index, gaps);
|
|
196
|
+
}
|
|
197
|
+
gaps.add(definitionIdentityKey(def));
|
|
198
|
+
}
|
|
199
|
+
const PHP_OWN_RECEIVER_KEYWORDS = new Set(["$this", "self", "static"]);
|
|
200
|
+
const PHP_TYPE_CONTAINER_TYPES = new Set([
|
|
201
|
+
"class_declaration",
|
|
202
|
+
"interface_declaration",
|
|
203
|
+
"trait_declaration",
|
|
204
|
+
"enum_declaration",
|
|
205
|
+
]);
|
|
206
|
+
async function phpOwnerInfo(index, def) {
|
|
207
|
+
const module = index.byFile.get(fileIdentityKey(def.file));
|
|
208
|
+
if (!module)
|
|
209
|
+
return null;
|
|
210
|
+
const parsed = await ensureParsedContext(def.file, index.parsed?.get(fileIdentityKey(def.file)), index.languageExtensions);
|
|
211
|
+
const start = def.range.start;
|
|
212
|
+
const position = { row: Math.max(0, start.line - 1), column: Math.max(0, start.column - 1) };
|
|
213
|
+
let current = parsed.tree.rootNode.descendantForPosition(position, position);
|
|
214
|
+
while (current) {
|
|
215
|
+
if (PHP_TYPE_CONTAINER_TYPES.has(current.type)) {
|
|
216
|
+
const nameNode = current.childForFieldName("name");
|
|
217
|
+
const className = nameNode ? sliceText(nameNode, parsed.source) : null;
|
|
218
|
+
if (!className)
|
|
219
|
+
return null;
|
|
220
|
+
const owner = module.locals.find((local) => local.localName === className &&
|
|
221
|
+
local.range.start.index === nameNode?.startIndex &&
|
|
222
|
+
(local.kind === SymbolKind.Class ||
|
|
223
|
+
local.kind === SymbolKind.Interface ||
|
|
224
|
+
local.kind === SymbolKind.TypeAlias)) ?? null;
|
|
225
|
+
if (!owner)
|
|
226
|
+
return null;
|
|
227
|
+
return { owner, containerStart: current.startIndex, containerEnd: current.endIndex };
|
|
228
|
+
}
|
|
229
|
+
current = current.parent;
|
|
230
|
+
}
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
function phpMemberAccessProperty(node, parsed) {
|
|
234
|
+
const parent = node.parent;
|
|
235
|
+
if (!parent || !isMemberAccessNode(parsed.sup, parent))
|
|
236
|
+
return null;
|
|
237
|
+
const parts = getMemberAccessParts(parsed.sup, parent);
|
|
238
|
+
if (!parts.property || !parts.object)
|
|
239
|
+
return null;
|
|
240
|
+
const isProperty = parts.property === node ||
|
|
241
|
+
(parts.property.id !== undefined && parts.property.id === node.id) ||
|
|
242
|
+
(parts.property.startIndex === node.startIndex && parts.property.endIndex === node.endIndex);
|
|
243
|
+
if (!isProperty)
|
|
244
|
+
return null;
|
|
245
|
+
if (parts.object.startIndex === parts.property.startIndex)
|
|
246
|
+
return null;
|
|
247
|
+
return { object: parts.object, property: parts.property, parent };
|
|
248
|
+
}
|
|
249
|
+
async function phpCaseInsensitiveReceiverMemberMatch(index, fileId, node, parsed, expectedDef) {
|
|
250
|
+
const access = phpMemberAccessProperty(node, parsed);
|
|
251
|
+
if (!access)
|
|
252
|
+
return "skip";
|
|
253
|
+
const propertyText = sliceText(access.property, parsed.source);
|
|
254
|
+
const comparison = comparePhpReferenceNames(propertyText, expectedDef.localName, {
|
|
255
|
+
symbolKind: expectedDef.kind,
|
|
256
|
+
});
|
|
257
|
+
if (comparison !== "equivalent")
|
|
258
|
+
return "skip";
|
|
259
|
+
const exactSpelling = propertyText === expectedDef.localName;
|
|
260
|
+
const ownerInfo = await phpOwnerInfo(index, expectedDef);
|
|
261
|
+
if (!ownerInfo)
|
|
262
|
+
return exactSpelling ? "skip" : "unverified";
|
|
263
|
+
const owner = ownerInfo.owner;
|
|
264
|
+
const receiverName = sliceText(access.object, parsed.source);
|
|
265
|
+
if (PHP_OWN_RECEIVER_KEYWORDS.has(receiverName)) {
|
|
266
|
+
if (fileIdentityKey(fileId) === fileIdentityKey(expectedDef.file) &&
|
|
267
|
+
node.startIndex >= ownerInfo.containerStart &&
|
|
268
|
+
node.startIndex <= ownerInfo.containerEnd) {
|
|
269
|
+
return "matched";
|
|
270
|
+
}
|
|
271
|
+
return exactSpelling ? "skip" : "unverified";
|
|
272
|
+
}
|
|
273
|
+
if (isKeywordReceiver(parsed.sup.id, receiverName)) {
|
|
274
|
+
return exactSpelling ? "skip" : "unverified";
|
|
275
|
+
}
|
|
276
|
+
const constructor = receiverConstructorExpression(access.object, parsed.source, parsed.sup);
|
|
277
|
+
const typeNode = constructor ?? (access.parent.type === "scoped_call_expression" ? access.object : null);
|
|
278
|
+
if (!typeNode)
|
|
279
|
+
return exactSpelling ? "skip" : "unverified";
|
|
280
|
+
const ownerNames = await phpCanonicalDefinitionNames(index, owner);
|
|
281
|
+
const imports = index.byFile.get(fileIdentityKey(fileId))?.imports;
|
|
282
|
+
const typeNames = canonicalPhpReferenceNames(sliceText(typeNode, parsed.source), parsed.source, parsed.tree, typeNode, {
|
|
283
|
+
...(imports ? { imports } : {}),
|
|
284
|
+
role: "class",
|
|
285
|
+
});
|
|
286
|
+
const matchesOwner = typeNames.some((typeName) => ownerNames.some((ownerName) => comparePhpReferenceNames(typeName, ownerName, { symbolKind: SymbolKind.Class }) === "equivalent"));
|
|
287
|
+
if (matchesOwner)
|
|
288
|
+
return "matched";
|
|
289
|
+
return "skip";
|
|
290
|
+
}
|
|
291
|
+
function matchesPhpFallbackDefinition(node, parsed, expectedDef) {
|
|
292
|
+
if (expectedDef.isMember)
|
|
293
|
+
return false;
|
|
294
|
+
const parent = node.parent;
|
|
295
|
+
if (parent && isMemberAccessNode(parsed.sup, parent)) {
|
|
296
|
+
const property = getMemberAccessParts(parsed.sup, parent).property;
|
|
297
|
+
if (property &&
|
|
298
|
+
(property === node ||
|
|
299
|
+
(property.id !== undefined && property.id === node.id) ||
|
|
300
|
+
(property.startIndex === node.startIndex && property.endIndex === node.endIndex))) {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
const referenceKind = inferPhpQualifiedReferenceImportType(node);
|
|
305
|
+
if (expectedDef.kind === SymbolKind.Function)
|
|
306
|
+
return referenceKind === "function";
|
|
307
|
+
if (expectedDef.kind === SymbolKind.Class ||
|
|
308
|
+
expectedDef.kind === SymbolKind.Interface ||
|
|
309
|
+
expectedDef.kind === SymbolKind.TypeAlias) {
|
|
310
|
+
return referenceKind === "class";
|
|
311
|
+
}
|
|
312
|
+
return false;
|
|
313
|
+
}
|
|
314
|
+
async function collectNamedNodeReferences(index, fileId, symbolName, symbolKind) {
|
|
104
315
|
try {
|
|
105
316
|
const parsedEntry = index.parsed?.get(fileIdentityKey(fileId));
|
|
106
317
|
const parsed = await ensureParsedContext(fileId, parsedEntry, index.languageExtensions);
|
|
@@ -112,15 +323,39 @@ async function collectNamedNodeReferences(index, fileId, symbolName) {
|
|
|
112
323
|
"field_identifier",
|
|
113
324
|
]);
|
|
114
325
|
const canonicalSymbolName = parsed.sup.normalizeIdentifier(symbolName);
|
|
115
|
-
const
|
|
326
|
+
const isPhp = parsed.sup.id === "php";
|
|
327
|
+
const matched = [];
|
|
328
|
+
let nameEquivalenceUnavailable = false;
|
|
116
329
|
const moduleIndex = index.byFile.get(fileIdentityKey(fileId));
|
|
117
330
|
const importDeclarationKeys = importBindingDeclarationRangeKeys(moduleIndex);
|
|
331
|
+
const row = scopeNodesFor(parsed.sup.id);
|
|
118
332
|
const walk = (node) => {
|
|
119
|
-
if (identifierTypes.has(node.type)
|
|
120
|
-
parsed.sup.normalizeIdentifier(sliceText(node, parsed.source))
|
|
121
|
-
|
|
122
|
-
if (!
|
|
123
|
-
|
|
333
|
+
if (identifierTypes.has(node.type)) {
|
|
334
|
+
const text = parsed.sup.normalizeIdentifier(sliceText(node, parsed.source));
|
|
335
|
+
let isMatch;
|
|
336
|
+
if (!isPhp) {
|
|
337
|
+
isMatch = text === canonicalSymbolName;
|
|
338
|
+
}
|
|
339
|
+
else if ((node.type === "name" || node.type === "namespace_name") &&
|
|
340
|
+
(isPhpQualifiedReferenceNode(node.parent) ||
|
|
341
|
+
(node.parent && identifierTypes.has(node.parent.type) && row.childSkipNameTypes?.has(node.type)))) {
|
|
342
|
+
isMatch = false;
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
const comparisonText = node.type === "qualified_name" || node.type === "relative_name" ? phpLastIdentifierSegment(text) : text;
|
|
346
|
+
const comparison = comparePhpReferenceNames(comparisonText, phpLastIdentifierSegment(canonicalSymbolName), {
|
|
347
|
+
caseSensitiveForm: node.type === "variable_name" || node.type === "constant",
|
|
348
|
+
...(symbolKind ? { symbolKind } : {}),
|
|
349
|
+
});
|
|
350
|
+
if (comparison === "unverified")
|
|
351
|
+
nameEquivalenceUnavailable = true;
|
|
352
|
+
isMatch = comparison === "equivalent";
|
|
353
|
+
}
|
|
354
|
+
if (isMatch) {
|
|
355
|
+
const range = toRange(node);
|
|
356
|
+
if (!importDeclarationKeys.has(rangeIdentityKey(range)) && !(isPhp && isInsidePhpUseDeclaration(node))) {
|
|
357
|
+
matched.push({ range, node });
|
|
358
|
+
}
|
|
124
359
|
}
|
|
125
360
|
}
|
|
126
361
|
for (const child of node.namedChildren) {
|
|
@@ -128,31 +363,149 @@ async function collectNamedNodeReferences(index, fileId, symbolName) {
|
|
|
128
363
|
}
|
|
129
364
|
};
|
|
130
365
|
walk(parsed.tree.rootNode);
|
|
131
|
-
return {
|
|
366
|
+
return { matched, parsed, nameEquivalenceUnavailable };
|
|
132
367
|
}
|
|
133
368
|
catch {
|
|
134
369
|
return null;
|
|
135
370
|
}
|
|
136
371
|
}
|
|
137
|
-
|
|
138
|
-
|
|
372
|
+
/** True when `range` is the property of a member-access expression, not a bare call. */
|
|
373
|
+
export function isMemberAccessPropertyRange(parsed, range) {
|
|
374
|
+
const startIndex = range.start.index;
|
|
375
|
+
const node = startIndex !== undefined
|
|
376
|
+
? parsed.tree.rootNode.descendantForIndex(startIndex, range.end.index ?? startIndex)
|
|
377
|
+
: parsed.tree.rootNode.descendantForPosition({ row: range.start.line - 1, column: range.start.column - 1 }, { row: range.start.line - 1, column: range.start.column - 1 });
|
|
378
|
+
const parent = node.parent;
|
|
379
|
+
if (!parent || !isMemberAccessNode(parsed.sup, parent))
|
|
380
|
+
return false;
|
|
381
|
+
const property = getMemberAccessParts(parsed.sup, parent).property;
|
|
382
|
+
return !!property && node.startIndex >= property.startIndex && node.endIndex <= property.endIndex;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* A member call on a local, parameter, field, or `this`/`self` cannot name a free function
|
|
386
|
+
* in a language whose member syntax never calls one. A missing receiver, or a module or
|
|
387
|
+
* namespace binding (`util.helper()`, `mod.thing()`), stays unproven.
|
|
388
|
+
*/
|
|
389
|
+
function freeFunctionMemberSiteIsProvenNonReference(index, fileId, parsed, objectNode, expectedDef) {
|
|
390
|
+
if (expectedDef.isMember || expectedDef.kind !== SymbolKind.Function)
|
|
391
|
+
return false;
|
|
392
|
+
if (memberSyntaxNamesFreeFunction(parsed.sup.id))
|
|
393
|
+
return false;
|
|
394
|
+
const receiverText = sliceText(objectNode, parsed.source).trim();
|
|
395
|
+
if (isKeywordReceiver(parsed.sup.id, receiverText))
|
|
396
|
+
return true;
|
|
397
|
+
if (!isReceiverNameNode(parsed.sup, objectNode.type))
|
|
398
|
+
return false;
|
|
399
|
+
const mod = index.byFile.get(fileIdentityKey(fileId));
|
|
400
|
+
if (!mod)
|
|
401
|
+
return false;
|
|
402
|
+
const binding = findClosestScopeBinding(getCachedScope(index, fileId, mod, parsed), receiverText, objectNode, parsed.sup);
|
|
403
|
+
return binding?.kind === "local" || binding?.kind === "param";
|
|
404
|
+
}
|
|
405
|
+
async function receiverProofUnavailable(index, fileId, parsed, range, resolveDefinition, expectedDef) {
|
|
406
|
+
const position = {
|
|
407
|
+
row: range.start.line - 1,
|
|
408
|
+
column: range.start.column - 1,
|
|
409
|
+
};
|
|
410
|
+
const nameNode = parsed.tree.rootNode.descendantForPosition(position, position);
|
|
411
|
+
let current = nameNode.parent;
|
|
412
|
+
while (current) {
|
|
413
|
+
if (isMemberAccessNode(parsed.sup, current)) {
|
|
414
|
+
const { object, property } = getMemberAccessParts(parsed.sup, current);
|
|
415
|
+
if (!object || !property || property.startIndex !== range.start.index)
|
|
416
|
+
return false;
|
|
417
|
+
const receiver = classifyReceiver(parsed.sup, object, parsed.source, new Map(), current.startIndex, current, (callee) => {
|
|
418
|
+
const mod = index.byFile.get(fileIdentityKey(fileId));
|
|
419
|
+
if (!mod)
|
|
420
|
+
return true;
|
|
421
|
+
const scope = getCachedScope(index, fileId, mod, parsed);
|
|
422
|
+
return !!findClosestScopeBinding(scope, sliceText(callee, parsed.source), callee, parsed.sup);
|
|
423
|
+
});
|
|
424
|
+
// A recognized shape is not proof. Exclude it only when the type is a resolved
|
|
425
|
+
// member-declaring definition, every supertype resolves, and none declare this member.
|
|
426
|
+
let unavailable;
|
|
427
|
+
if (receiver) {
|
|
428
|
+
const omits = await provenClassifiedReceiverOmitsMember(index, fileId, parsed, current, object, sliceText(property, parsed.source));
|
|
429
|
+
unavailable = !omits;
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
const receiverRange = toRange(object);
|
|
433
|
+
const resolvedReceiver = await resolveDefinition({
|
|
434
|
+
file: fileId,
|
|
435
|
+
line: receiverRange.start.line,
|
|
436
|
+
column: receiverRange.start.column,
|
|
437
|
+
}, parsed);
|
|
438
|
+
unavailable = !(resolvedReceiver.status === "ok" &&
|
|
439
|
+
resolvedReceiver.definition &&
|
|
440
|
+
declaresMembers(resolvedReceiver.definition));
|
|
441
|
+
}
|
|
442
|
+
if (!unavailable)
|
|
443
|
+
return false;
|
|
444
|
+
return !freeFunctionMemberSiteIsProvenNonReference(index, fileId, parsed, object, expectedDef);
|
|
445
|
+
}
|
|
446
|
+
current = current.parent;
|
|
447
|
+
}
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
export async function collectVerifiedNamedNodeReferences(index, fileId, symbolName, expectedDef, resolveDefinition, maxVerified, includeReference, onReceiverProofUnavailable, equivalentDefinitions = []) {
|
|
451
|
+
const collected = await collectNamedNodeReferences(index, fileId, symbolName, expectedDef.kind);
|
|
139
452
|
if (!collected)
|
|
140
453
|
return [];
|
|
141
|
-
const {
|
|
454
|
+
const { matched, parsed, nameEquivalenceUnavailable } = collected;
|
|
455
|
+
if (nameEquivalenceUnavailable)
|
|
456
|
+
markPhpNameEquivalenceGap(index, expectedDef);
|
|
457
|
+
const phpCanonicalNames = parsed.sup.id === "php" ? await phpCanonicalDefinitionNames(index, expectedDef) : undefined;
|
|
458
|
+
const phpExistingFunctionNames = phpCanonicalNames && expectedDef.kind === SymbolKind.Function
|
|
459
|
+
? (await ensurePhpNamespaceSymbolIndex(index)).functionNames
|
|
460
|
+
: undefined;
|
|
142
461
|
const verified = [];
|
|
462
|
+
const matchesExpectedDefinition = (definition) => sameDef(definition, expectedDef, index.languageExtensions) ||
|
|
463
|
+
equivalentDefinitions.some((equivalent) => sameDef(definition, equivalent, index.languageExtensions));
|
|
464
|
+
const candidateFileKey = fileIdentityKey(fileId);
|
|
465
|
+
const equivalentDefinitionsInFile = equivalentDefinitions.filter((equivalent) => fileIdentityKey(equivalent.file) === candidateFileKey);
|
|
466
|
+
const isEquivalentDeclarationRange = (range) => equivalentDefinitionsInFile.some((equivalent) => {
|
|
467
|
+
const startMatches = range.start.index !== undefined && equivalent.range.start.index !== undefined
|
|
468
|
+
? range.start.index === equivalent.range.start.index
|
|
469
|
+
: range.start.line === equivalent.range.start.line && range.start.column === equivalent.range.start.column;
|
|
470
|
+
const endMatches = range.end.index !== undefined && equivalent.range.end.index !== undefined
|
|
471
|
+
? range.end.index === equivalent.range.end.index
|
|
472
|
+
: range.end.line === equivalent.range.end.line && range.end.column === equivalent.range.end.column;
|
|
473
|
+
return startMatches && endMatches;
|
|
474
|
+
});
|
|
143
475
|
const pushVerified = (reference) => {
|
|
144
476
|
if (!includeReference || includeReference(reference))
|
|
145
477
|
verified.push(reference);
|
|
146
478
|
};
|
|
147
|
-
|
|
479
|
+
let definitionVisible;
|
|
480
|
+
const definitionVisibleHere = () => {
|
|
481
|
+
if (definitionVisible === undefined) {
|
|
482
|
+
const closure = importClosure(index, fileId);
|
|
483
|
+
definitionVisible = [expectedDef, ...equivalentDefinitions].some((definition) => closure.has(fileIdentityKey(definition.file)));
|
|
484
|
+
}
|
|
485
|
+
return definitionVisible;
|
|
486
|
+
};
|
|
487
|
+
for (const { range, node } of matched) {
|
|
148
488
|
if (maxVerified !== undefined && maxVerified > 0 && verified.length >= maxVerified) {
|
|
149
489
|
break;
|
|
150
490
|
}
|
|
491
|
+
if (isEquivalentDeclarationRange(range)) {
|
|
492
|
+
pushVerified({ range });
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
if (parsed.sup.id === "c" && expectedDef.kind === SymbolKind.Function) {
|
|
496
|
+
let enclosing = node.parent;
|
|
497
|
+
while (enclosing && enclosing.type !== "field_declaration" && enclosing.type !== "translation_unit") {
|
|
498
|
+
enclosing = enclosing.parent;
|
|
499
|
+
}
|
|
500
|
+
// A C struct function-pointer field is not a declaration or use of a free function.
|
|
501
|
+
if (enclosing?.type === "field_declaration")
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
151
504
|
const exportFrom = exportFromIdentifier(index, fileId, range, parsed);
|
|
152
505
|
if (exportFrom?.entry) {
|
|
153
506
|
const reexported = resolveExport(index, exportFrom.entry.fromModule, exportFrom.entry.sourceSpecifier);
|
|
154
507
|
if (reexported?.kind === "resolved") {
|
|
155
|
-
if (
|
|
508
|
+
if (matchesExpectedDefinition(reexported.def)) {
|
|
156
509
|
pushVerified({ range, via: { reexport: true } });
|
|
157
510
|
}
|
|
158
511
|
continue;
|
|
@@ -163,14 +516,71 @@ export async function collectVerifiedNamedNodeReferences(index, fileId, symbolNa
|
|
|
163
516
|
line: range.start.line,
|
|
164
517
|
column: range.start.column,
|
|
165
518
|
}, parsed);
|
|
166
|
-
if (resolved.status
|
|
519
|
+
if (resolved.status === "ok" && resolved.definition) {
|
|
520
|
+
if (matchesExpectedDefinition(resolved.definition)) {
|
|
521
|
+
pushVerified({
|
|
522
|
+
range,
|
|
523
|
+
...(exportFrom?.isExportFrom ? { via: { reexport: true } } : {}),
|
|
524
|
+
...(resolved.provenance ? { provenance: resolved.provenance } : {}),
|
|
525
|
+
});
|
|
526
|
+
}
|
|
167
527
|
continue;
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
528
|
+
}
|
|
529
|
+
let recoveredByLanguageFallback = false;
|
|
530
|
+
if (parsed.sup.id === "php" && expectedDef.isMember) {
|
|
531
|
+
const memberMatch = await phpCaseInsensitiveReceiverMemberMatch(index, fileId, node, parsed, expectedDef);
|
|
532
|
+
if (memberMatch === "matched") {
|
|
533
|
+
pushVerified({ range, ...(exportFrom?.isExportFrom ? { via: { reexport: true } } : {}) });
|
|
534
|
+
recoveredByLanguageFallback = true;
|
|
535
|
+
}
|
|
536
|
+
else if (memberMatch === "unverified") {
|
|
537
|
+
markPhpNameEquivalenceGap(index, expectedDef);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
else if (phpCanonicalNames && matchesPhpFallbackDefinition(node, parsed, expectedDef)) {
|
|
541
|
+
// PHP names are case-insensitive, but a namespace spelling alone cannot prove a member or
|
|
542
|
+
// distinguish a class reference from a function call. Restrict the fallback to syntax whose
|
|
543
|
+
// role matches the namespace-level definition.
|
|
544
|
+
const rawText = getPhpQualifiedReference(node, parsed.source) ?? sliceText(node, parsed.source);
|
|
545
|
+
const imports = index.byFile.get(fileIdentityKey(fileId))?.imports;
|
|
546
|
+
const role = inferPhpQualifiedReferenceImportType(node);
|
|
547
|
+
const candidates = canonicalPhpReferenceNames(rawText, parsed.source, parsed.tree, node, {
|
|
548
|
+
...(imports ? { imports } : {}),
|
|
549
|
+
...(role ? { role } : {}),
|
|
173
550
|
});
|
|
551
|
+
const existingNames = phpExistingFunctionNames ?? phpCanonicalNames;
|
|
552
|
+
const selectedCandidate = selectFirstExistingPhpCanonicalName(candidates, existingNames, expectedDef.kind);
|
|
553
|
+
let matchedCanonical;
|
|
554
|
+
if (selectedCandidate) {
|
|
555
|
+
matchedCanonical = phpCanonicalNames.find((canonicalName) => comparePhpReferenceNames(selectedCandidate, canonicalName, { symbolKind: expectedDef.kind }) ===
|
|
556
|
+
"equivalent");
|
|
557
|
+
}
|
|
558
|
+
if (matchedCanonical) {
|
|
559
|
+
// A qualified path proves the namespace, but a bare case-variant `name` could also be a
|
|
560
|
+
// same-named constant, so the equivalence is unproven for that form and coverage says so
|
|
561
|
+
// instead of silently returning a short list.
|
|
562
|
+
if (!isPhpQualifiedReferenceNode(node)) {
|
|
563
|
+
const lastSegment = matchedCanonical.split("\\").pop() ?? matchedCanonical;
|
|
564
|
+
const bareText = sliceText(node, parsed.source).trim();
|
|
565
|
+
if (bareText !== lastSegment && foldPhpIdentifierCase(bareText) === foldPhpIdentifierCase(lastSegment)) {
|
|
566
|
+
markPhpNameEquivalenceGap(index, expectedDef);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
pushVerified({ range, ...(exportFrom?.isExportFrom ? { via: { reexport: true } } : {}) });
|
|
570
|
+
recoveredByLanguageFallback = true;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
// A same-name node that direct resolution and every language-specific fallback both failed
|
|
574
|
+
// to place is not provably unrelated: report the file so coverage cannot silently claim
|
|
575
|
+
// `complete` while this occurrence's status stays unknown. An ambiguous result (star
|
|
576
|
+
// imports, C++ overloads, or using targets) is the same kind of gap when this definition
|
|
577
|
+
// (or an equivalent declaration) is visible from this file: one of the candidates may be
|
|
578
|
+
// it. A name with no binding at all is proven unrelated by scope and stays out.
|
|
579
|
+
if (!recoveredByLanguageFallback && onReceiverProofUnavailable) {
|
|
580
|
+
if ((isAmbiguousResolutionReason(resolved.reason) && definitionVisibleHere()) ||
|
|
581
|
+
(await receiverProofUnavailable(index, fileId, parsed, range, resolveDefinition, expectedDef))) {
|
|
582
|
+
onReceiverProofUnavailable(fileId);
|
|
583
|
+
}
|
|
174
584
|
}
|
|
175
585
|
}
|
|
176
586
|
return verified;
|
|
@@ -210,31 +620,77 @@ export function hasExpandedNamedImport(moduleIndex, targetFile, symbolName) {
|
|
|
210
620
|
fileIdentityKey(candidate.resolved) === targetKey);
|
|
211
621
|
}
|
|
212
622
|
const referenceCandidateCache = new WeakMap();
|
|
623
|
+
/** A package import may expose a child only along its exact dotted module path. */
|
|
624
|
+
export function pythonParentPackageNamespacePaths(moduleIndex, definitionFile) {
|
|
625
|
+
const file = normalizePath(definitionFile);
|
|
626
|
+
const basename = path.posix.basename(file);
|
|
627
|
+
if (!file.endsWith(".py") && !file.endsWith(".pyi"))
|
|
628
|
+
return [];
|
|
629
|
+
const moduleStem = basename === "__init__.py" || basename === "__init__.pyi"
|
|
630
|
+
? path.posix.dirname(file)
|
|
631
|
+
: file.slice(0, -path.posix.extname(file).length);
|
|
632
|
+
const paths = [];
|
|
633
|
+
for (const imp of moduleIndex.imports) {
|
|
634
|
+
if (imp.kind !== "namespace" || imp.mechanism !== "python" || typeof imp.resolved !== "string")
|
|
635
|
+
continue;
|
|
636
|
+
const target = normalizePath(imp.resolved);
|
|
637
|
+
const targetName = path.posix.basename(target);
|
|
638
|
+
const initializer = targetName === "__init__.py" || targetName === "__init__.pyi";
|
|
639
|
+
if (!initializer && (target.endsWith(".py") || target.endsWith(".pyi")))
|
|
640
|
+
continue;
|
|
641
|
+
const packageDirectory = initializer ? path.posix.dirname(target) : target;
|
|
642
|
+
const child = path.posix.relative(packageDirectory, moduleStem);
|
|
643
|
+
if (!child || child.startsWith("..") || path.posix.isAbsolute(child))
|
|
644
|
+
continue;
|
|
645
|
+
const boundName = imp.from.includes(".") && !imp.explicitAlias ? imp.from : imp.localNS;
|
|
646
|
+
paths.push({ namespace: boundName + "." + child.replaceAll("/", "."), importBinding: imp });
|
|
647
|
+
}
|
|
648
|
+
return paths;
|
|
649
|
+
}
|
|
213
650
|
function referenceCandidateCacheKey(index, def, exportedNames) {
|
|
214
651
|
const normalizeIdentifier = supportForFileWithoutHeaderSample(def.file, index.languageExtensions)?.normalizeIdentifier ?? ((name) => name);
|
|
215
652
|
const sortedNames = exportedNames.map(normalizeIdentifier).sort();
|
|
216
653
|
return `${fileIdentityKey(def.file)}::${def.range.start.index ?? 0}::canonical::${sortedNames.join("\0")}`;
|
|
217
654
|
}
|
|
218
|
-
function importCanReferenceDefinition(index, imp, def, exportedNames) {
|
|
655
|
+
function importCanReferenceDefinition(index, imp, def, exportedNames, languageId) {
|
|
219
656
|
const targetFile = typeof imp.resolved === "string" ? imp.resolved : undefined;
|
|
220
657
|
if (!targetFile)
|
|
221
658
|
return false;
|
|
659
|
+
let cNamespace;
|
|
660
|
+
if (languageId === "c")
|
|
661
|
+
cNamespace = def.cTag ? "tag" : "ordinary";
|
|
662
|
+
const exportOptions = cNamespace ? { cNamespace } : undefined;
|
|
663
|
+
if (cNamespace && imp.kind === "named" && (imp.cNamespace ?? "ordinary") !== cNamespace)
|
|
664
|
+
return false;
|
|
222
665
|
const resolvesToDefinition = (exportedName) => {
|
|
223
|
-
const hit = resolveExport(index, targetFile, exportedName);
|
|
666
|
+
const hit = resolveExport(index, targetFile, exportedName, exportOptions);
|
|
224
667
|
if (hit?.kind === "resolved") {
|
|
225
668
|
return sameDef(hit.def, def, index.languageExtensions);
|
|
226
669
|
}
|
|
670
|
+
if (cppCanonicalStructuralExport(index, targetFile, exportedName, def, languageId)) {
|
|
671
|
+
return true;
|
|
672
|
+
}
|
|
227
673
|
return imp.kind === "namespace" && fileIdentityKey(targetFile) === fileIdentityKey(def.file);
|
|
228
674
|
};
|
|
229
675
|
if (imp.kind === "named") {
|
|
230
|
-
|
|
676
|
+
if (resolvesToDefinition(imp.imported))
|
|
677
|
+
return true;
|
|
678
|
+
// A python `from pkg import name` binds `name` from the package's own namespace; when the
|
|
679
|
+
// package has no such export, Python's own import system falls back to treating `name` as
|
|
680
|
+
// an implicit submodule attribute instead (the same fallback `resolveImported` applies at
|
|
681
|
+
// consumption time). Reusing that fallback here keeps candidate-file discovery in agreement
|
|
682
|
+
// with the goto/reference-collection consumers that already resolve through it.
|
|
683
|
+
if (languageId !== "python")
|
|
684
|
+
return false;
|
|
685
|
+
const result = resolveImported(index, imp, imp.imported, exportOptions);
|
|
686
|
+
return !!result && "namespace" in result && fileIdentityKey(result.namespace) === fileIdentityKey(def.file);
|
|
231
687
|
}
|
|
232
688
|
if (imp.kind === "default") {
|
|
233
689
|
return resolvesToDefinition("default");
|
|
234
690
|
}
|
|
235
691
|
if (imp.kind === "star") {
|
|
236
692
|
return exportedNames.some((exportedName) => {
|
|
237
|
-
const result = resolveImported(index, imp, exportedName);
|
|
693
|
+
const result = resolveImported(index, imp, exportedName, exportOptions);
|
|
238
694
|
return !!result && !("namespace" in result) && sameDef(result, def, index.languageExtensions);
|
|
239
695
|
});
|
|
240
696
|
}
|
|
@@ -259,10 +715,16 @@ function moduleExportProbeNames(index, moduleIndex, exportedNames, visited = new
|
|
|
259
715
|
}
|
|
260
716
|
return [...names];
|
|
261
717
|
}
|
|
262
|
-
function filesExportingDefinition(index, def, exportedNames) {
|
|
718
|
+
function filesExportingDefinition(index, def, exportedNames, languageId) {
|
|
263
719
|
const files = new Map([[fileIdentityKey(def.file), def.file]]);
|
|
720
|
+
const namespaceLinks = [];
|
|
264
721
|
for (const moduleIndex of index.byFile.values()) {
|
|
265
722
|
const fileId = moduleIndex.file;
|
|
723
|
+
for (const entry of moduleIndex.exports) {
|
|
724
|
+
if (entry.type === "namespaceReexport") {
|
|
725
|
+
namespaceLinks.push({ sourceFile: fileId, exportedAs: entry.exportedAs, targetFile: entry.fromModule });
|
|
726
|
+
}
|
|
727
|
+
}
|
|
266
728
|
if (fileIdentityKey(fileId) === fileIdentityKey(def.file) || !moduleIndex.exports.length)
|
|
267
729
|
continue;
|
|
268
730
|
for (const exportedName of moduleExportProbeNames(index, moduleIndex, exportedNames)) {
|
|
@@ -271,6 +733,10 @@ function filesExportingDefinition(index, def, exportedNames) {
|
|
|
271
733
|
files.set(fileIdentityKey(fileId), fileId);
|
|
272
734
|
break;
|
|
273
735
|
}
|
|
736
|
+
if (cppCanonicalStructuralExport(index, fileId, exportedName, def, languageId)) {
|
|
737
|
+
files.set(fileIdentityKey(fileId), fileId);
|
|
738
|
+
break;
|
|
739
|
+
}
|
|
274
740
|
}
|
|
275
741
|
if (moduleIndex.exports.some((entry) => entry.type === "reexport" &&
|
|
276
742
|
exportedNames.includes(entry.sourceSpecifier) &&
|
|
@@ -278,15 +744,31 @@ function filesExportingDefinition(index, def, exportedNames) {
|
|
|
278
744
|
files.set(fileIdentityKey(fileId), fileId);
|
|
279
745
|
}
|
|
280
746
|
}
|
|
747
|
+
// A namespace re-export can expose an inner symbol through a member chain even
|
|
748
|
+
// though it does not export that symbol under its own name. Include consumers
|
|
749
|
+
// only when the visible namespace alias really targets the exporting module.
|
|
750
|
+
let grew = true;
|
|
751
|
+
while (grew) {
|
|
752
|
+
grew = false;
|
|
753
|
+
for (const link of namespaceLinks) {
|
|
754
|
+
const sourceKey = fileIdentityKey(link.sourceFile);
|
|
755
|
+
if (files.has(sourceKey) || !files.has(fileIdentityKey(link.targetFile)))
|
|
756
|
+
continue;
|
|
757
|
+
const visible = resolveExport(index, link.sourceFile, link.exportedAs, { allowLocalFallback: false });
|
|
758
|
+
if (visible?.kind !== "namespace" || fileIdentityKey(visible.file) !== fileIdentityKey(link.targetFile)) {
|
|
759
|
+
continue;
|
|
760
|
+
}
|
|
761
|
+
files.set(sourceKey, link.sourceFile);
|
|
762
|
+
grew = true;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
281
765
|
return [...files.values()];
|
|
282
766
|
}
|
|
283
|
-
function getIndexedReferenceCandidateFiles(index, def, exportedNames) {
|
|
284
|
-
if (def.file.toLowerCase().endsWith(".php"))
|
|
285
|
-
return undefined;
|
|
767
|
+
function getIndexedReferenceCandidateFiles(index, def, exportedNames, languageId) {
|
|
286
768
|
if (!index.referenceCandidates)
|
|
287
769
|
return undefined;
|
|
288
770
|
const files = new Map();
|
|
289
|
-
for (const exportingFile of filesExportingDefinition(index, def, exportedNames)) {
|
|
771
|
+
for (const exportingFile of filesExportingDefinition(index, def, exportedNames, languageId)) {
|
|
290
772
|
for (const importingFile of candidateFilesImportingTarget(index.referenceCandidates, exportingFile) ?? []) {
|
|
291
773
|
files.set(fileIdentityKey(importingFile), importingFile);
|
|
292
774
|
}
|
|
@@ -295,30 +777,49 @@ function getIndexedReferenceCandidateFiles(index, def, exportedNames) {
|
|
|
295
777
|
const fileId = moduleIndex.file;
|
|
296
778
|
if (fileIdentityKey(fileId) === fileIdentityKey(def.file) || files.has(fileIdentityKey(fileId)))
|
|
297
779
|
continue;
|
|
298
|
-
if (moduleIndex.imports.some((imp) => (imp.kind === "star" || imp.kind === "namespace") &&
|
|
299
|
-
importCanReferenceDefinition(index, imp, def, exportedNames))) {
|
|
780
|
+
if (moduleIndex.imports.some((imp) => (imp.kind === "star" || imp.kind === "namespace" || (imp.kind === "named" && imp.mechanism === "python")) &&
|
|
781
|
+
importCanReferenceDefinition(index, imp, def, exportedNames, languageId))) {
|
|
300
782
|
files.set(fileIdentityKey(fileId), fileId);
|
|
301
783
|
}
|
|
302
784
|
}
|
|
303
785
|
return [...files.values()].sort((left, right) => left.localeCompare(right));
|
|
304
786
|
}
|
|
305
|
-
export function getCachedReferenceCandidateFiles(index, def, exportedNames, hasGlobalNameReferences) {
|
|
787
|
+
export function getCachedReferenceCandidateFiles(index, def, exportedNames, hasGlobalNameReferences, languageId) {
|
|
306
788
|
if (hasGlobalNameReferences) {
|
|
307
|
-
|
|
789
|
+
// Callers only set this for a PHP definition (`buildPhpQualifiedNames` returns names for no
|
|
790
|
+
// other language), and PHP's global-namespace fallback can be referenced from any PHP file
|
|
791
|
+
// without an import, so the import-graph narrowing below cannot apply. Every other language
|
|
792
|
+
// is still a sound, source-free rejection: a PHP symbol can never be referenced from a file
|
|
793
|
+
// whose own language a different parser owns.
|
|
794
|
+
return Array.from(index.byFile.values(), (module) => module.file)
|
|
795
|
+
.filter((file) => supportForFileWithoutHeaderSample(file, index.languageExtensions)?.id === "php")
|
|
796
|
+
.sort((left, right) => left.localeCompare(right));
|
|
308
797
|
}
|
|
309
798
|
let cache = referenceCandidateCache.get(index);
|
|
310
799
|
if (!cache) {
|
|
311
800
|
cache = new Map();
|
|
312
801
|
referenceCandidateCache.set(index, cache);
|
|
313
802
|
}
|
|
314
|
-
const key = referenceCandidateCacheKey(index, def, exportedNames)
|
|
803
|
+
const key = `${languageId}:${referenceCandidateCacheKey(index, def, exportedNames)}`;
|
|
315
804
|
const cached = cache.get(key);
|
|
316
805
|
if (cached)
|
|
317
806
|
return cached;
|
|
318
807
|
const candidates = new Map();
|
|
319
|
-
|
|
808
|
+
if (def.isMember)
|
|
809
|
+
candidates.set(fileIdentityKey(def.file), def.file);
|
|
810
|
+
// Files sharing the definition's compilation unit can name it without any import edge (Go and
|
|
811
|
+
// JVM packages, C# namespaces, Swift modules), so they bypass the import filter below. C#
|
|
812
|
+
// also includes same-directory files that can reach the type only through a qualified name,
|
|
813
|
+
// so dotted and `global::` uses are candidates even when the consumer declares unrelated
|
|
814
|
+
// namespaces. Bare-name resolution still uses the related-namespace peer set.
|
|
815
|
+
for (const unitPeer of getCompilationUnitPeers(index, def.file, languageId === "csharp" ? { csharpQualifiedName: true } : undefined).files) {
|
|
816
|
+
if (fileIdentityKey(unitPeer) !== fileIdentityKey(def.file)) {
|
|
817
|
+
candidates.set(fileIdentityKey(unitPeer), unitPeer);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
const candidateFileEntries = getIndexedReferenceCandidateFiles(index, def, exportedNames, languageId) ??
|
|
320
821
|
Array.from(index.byFile.values(), (module) => module.file);
|
|
321
|
-
const exportingFileIds = filesExportingDefinition(index, def, exportedNames);
|
|
822
|
+
const exportingFileIds = filesExportingDefinition(index, def, exportedNames, languageId);
|
|
322
823
|
const exportingFiles = new Set(exportingFileIds.map((file) => fileIdentityKey(file)));
|
|
323
824
|
for (const fileId of exportingFileIds) {
|
|
324
825
|
if (fileIdentityKey(fileId) !== fileIdentityKey(def.file)) {
|
|
@@ -335,16 +836,61 @@ export function getCachedReferenceCandidateFiles(index, def, exportedNames, hasG
|
|
|
335
836
|
if (typeof imp.resolved !== "string")
|
|
336
837
|
return false;
|
|
337
838
|
return (exportingFiles.has(fileIdentityKey(imp.resolved)) ||
|
|
338
|
-
importCanReferenceDefinition(index, imp, def, exportedNames));
|
|
839
|
+
importCanReferenceDefinition(index, imp, def, exportedNames, languageId));
|
|
339
840
|
})) {
|
|
340
841
|
candidates.set(fileIdentityKey(fileId), fileId);
|
|
341
842
|
}
|
|
342
843
|
}
|
|
844
|
+
// A plain package import does not prove its child's attribute, but a same-name use
|
|
845
|
+
// through that package must be checked before coverage can claim completeness.
|
|
846
|
+
if (languageId === "python" && !def.isMember) {
|
|
847
|
+
for (const moduleIndex of index.byFile.values()) {
|
|
848
|
+
if (pythonParentPackageNamespacePaths(moduleIndex, def.file).length) {
|
|
849
|
+
candidates.set(fileIdentityKey(moduleIndex.file), moduleIndex.file);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
}
|
|
343
853
|
const sorted = [...candidates.values()].sort((left, right) => left.localeCompare(right));
|
|
344
854
|
cache.set(key, sorted);
|
|
345
855
|
return sorted;
|
|
346
856
|
}
|
|
347
|
-
|
|
857
|
+
/**
|
|
858
|
+
* The single precedence table for reference-coverage reasons, shared by the direct
|
|
859
|
+
* indexed-candidate builder and the bounded reference lookup cache. Existing reasons keep
|
|
860
|
+
* their established relative order so current expectations stay stable; the two strategy
|
|
861
|
+
* reasons sit after the file-level reasons they refine and before `truncated`, because a
|
|
862
|
+
* skipped collection strategy is a correctness gap while `truncated` only reflects the
|
|
863
|
+
* caller's own requested bound.
|
|
864
|
+
*/
|
|
865
|
+
export const REFERENCE_COVERAGE_REASON_ORDER = [
|
|
866
|
+
"parser_degraded",
|
|
867
|
+
"unresolved_import",
|
|
868
|
+
"strategy_unavailable",
|
|
869
|
+
"name_equivalence_unavailable",
|
|
870
|
+
"truncated",
|
|
871
|
+
];
|
|
872
|
+
export function describeReferenceStrategies(args) {
|
|
873
|
+
const applicable = [];
|
|
874
|
+
const executed = [];
|
|
875
|
+
if (args.sameFileOccurrence?.applicable) {
|
|
876
|
+
applicable.push("same_file_occurrence");
|
|
877
|
+
if (args.sameFileOccurrence.executed)
|
|
878
|
+
executed.push("same_file_occurrence");
|
|
879
|
+
}
|
|
880
|
+
if (args.implicitUnitPeers?.applicable) {
|
|
881
|
+
applicable.push("implicit_unit_peers");
|
|
882
|
+
if (args.implicitUnitPeers.executed)
|
|
883
|
+
executed.push("implicit_unit_peers");
|
|
884
|
+
}
|
|
885
|
+
if (args.languageId === "php") {
|
|
886
|
+
// Every PHP definition is addressable by its global or namespace-qualified spelling, so
|
|
887
|
+
// the qualified-name scan is required; the receiver scan is not the PHP strategy.
|
|
888
|
+
applicable.push("php_qualified_name");
|
|
889
|
+
if (args.phpQualifiedNames.length)
|
|
890
|
+
executed.push("php_qualified_name");
|
|
891
|
+
}
|
|
892
|
+
return { applicable, executed };
|
|
893
|
+
}
|
|
348
894
|
export function rangeIdentityKey(range) {
|
|
349
895
|
return `${range.start.line}:${range.start.column}:${range.start.index ?? ""}:${range.end.line}:${range.end.column}:${range.end.index ?? ""}`;
|
|
350
896
|
}
|
|
@@ -379,7 +925,7 @@ export function importBindingReferenceSites(imp) {
|
|
|
379
925
|
sites.push({ range: localRange, importBinding: "local" });
|
|
380
926
|
}
|
|
381
927
|
}
|
|
382
|
-
else if (imp.kind === "default") {
|
|
928
|
+
else if (imp.kind === "default" || imp.kind === "namespace") {
|
|
383
929
|
const { localRange } = bindingTokenRanges(imp);
|
|
384
930
|
if (localRange) {
|
|
385
931
|
sites.push({ range: localRange, importBinding: "local" });
|
|
@@ -443,7 +989,13 @@ function structurallyExportsDefinition(index, moduleFile, exportedName, def, def
|
|
|
443
989
|
}
|
|
444
990
|
return false;
|
|
445
991
|
}
|
|
446
|
-
|
|
992
|
+
/** Recover C++ overload candidates through real exports, never the unresolved-import name shortcut. */
|
|
993
|
+
export function cppCanonicalStructuralExport(index, targetFile, exportedName, def, languageId) {
|
|
994
|
+
if (languageId !== "cpp")
|
|
995
|
+
return false;
|
|
996
|
+
return structurallyExportsDefinition(index, targetFile, exportedName, def, []);
|
|
997
|
+
}
|
|
998
|
+
function isUnresolvedIndexedImport(index, imp, def, exportedNames, languageId) {
|
|
447
999
|
if (typeof imp.resolved !== "string")
|
|
448
1000
|
return false;
|
|
449
1001
|
let importedName;
|
|
@@ -455,7 +1007,7 @@ function isUnresolvedIndexedImport(index, imp, def, exportedNames) {
|
|
|
455
1007
|
return false;
|
|
456
1008
|
if (!structurallyExportsDefinition(index, imp.resolved, importedName, def, exportedNames))
|
|
457
1009
|
return false;
|
|
458
|
-
return !importCanReferenceDefinition(index, imp, def, exportedNames);
|
|
1010
|
+
return !importCanReferenceDefinition(index, imp, def, exportedNames, languageId);
|
|
459
1011
|
}
|
|
460
1012
|
function parserDegradedCandidateFiles(index, scannedFiles) {
|
|
461
1013
|
const report = index.buildReport?.backend?.parser;
|
|
@@ -484,7 +1036,7 @@ function parserDegradedCandidateFiles(index, scannedFiles) {
|
|
|
484
1036
|
return { files: affected, hasUnlistedCandidate };
|
|
485
1037
|
}
|
|
486
1038
|
export function buildIndexedCandidateCoverage(args) {
|
|
487
|
-
const { index, def, exportedNames, candidateFiles, scannedFiles, truncated } = args;
|
|
1039
|
+
const { index, def, languageId, exportedNames, candidateFiles, scannedFiles, truncated, strategies, strategyUnavailableFiles = [], } = args;
|
|
488
1040
|
const reasons = [];
|
|
489
1041
|
const affectedFiles = [];
|
|
490
1042
|
const affectedSeen = new Set();
|
|
@@ -506,7 +1058,7 @@ export function buildIndexedCandidateCoverage(args) {
|
|
|
506
1058
|
const moduleIndex = index.byFile.get(fileIdentityKey(fileId));
|
|
507
1059
|
if (!moduleIndex)
|
|
508
1060
|
continue;
|
|
509
|
-
if (moduleIndex.imports.some((imp) => isUnresolvedIndexedImport(index, imp, def, exportedNames))) {
|
|
1061
|
+
if (moduleIndex.imports.some((imp) => isUnresolvedIndexedImport(index, imp, def, exportedNames, languageId))) {
|
|
510
1062
|
unresolvedFiles.push(fileId);
|
|
511
1063
|
}
|
|
512
1064
|
}
|
|
@@ -515,6 +1067,20 @@ export function buildIndexedCandidateCoverage(args) {
|
|
|
515
1067
|
for (const file of unresolvedFiles)
|
|
516
1068
|
addAffected(file);
|
|
517
1069
|
}
|
|
1070
|
+
if (strategyUnavailableFiles.length) {
|
|
1071
|
+
reasons.push("strategy_unavailable");
|
|
1072
|
+
for (const file of strategyUnavailableFiles)
|
|
1073
|
+
addAffected(file);
|
|
1074
|
+
}
|
|
1075
|
+
if (strategies) {
|
|
1076
|
+
const executed = new Set(strategies.executed);
|
|
1077
|
+
if (strategies.applicable.some((strategy) => !executed.has(strategy))) {
|
|
1078
|
+
reasons.push("strategy_unavailable");
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
if (phpNameEquivalenceGaps.get(index)?.has(definitionIdentityKey(def))) {
|
|
1082
|
+
reasons.push("name_equivalence_unavailable");
|
|
1083
|
+
}
|
|
518
1084
|
if (truncated)
|
|
519
1085
|
reasons.push("truncated");
|
|
520
1086
|
if (!reasons.length) {
|
|
@@ -523,7 +1089,7 @@ export function buildIndexedCandidateCoverage(args) {
|
|
|
523
1089
|
return {
|
|
524
1090
|
scope: "indexed_candidates",
|
|
525
1091
|
state: "partial",
|
|
526
|
-
reasons:
|
|
1092
|
+
reasons: REFERENCE_COVERAGE_REASON_ORDER.filter((reason) => reasons.includes(reason)),
|
|
527
1093
|
...(affectedFiles.length ? { affectedFiles } : {}),
|
|
528
1094
|
};
|
|
529
1095
|
}
|