@lzehrung/codegraph 2.3.31 → 3.0.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 +33 -141
- package/codegraph-skill/codegraph/SKILL.md +8 -6
- 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/call-hierarchy.js +2 -4
- package/dist/agent/call-hierarchy.js.map +1 -1
- package/dist/agent/explore.d.ts +1 -1
- package/dist/agent/explore.d.ts.map +1 -1
- package/dist/agent/explore.js +3 -2
- package/dist/agent/explore.js.map +1 -1
- package/dist/agent/refactor-plan.js +2 -4
- package/dist/agent/refactor-plan.js.map +1 -1
- package/dist/agent/rename-preview.d.ts.map +1 -1
- package/dist/agent/rename-preview.js +4 -3
- package/dist/agent/rename-preview.js.map +1 -1
- package/dist/agent/search.d.ts.map +1 -1
- package/dist/agent/search.js +5 -3
- package/dist/agent/search.js.map +1 -1
- package/dist/agent/semantic-symbols.d.ts.map +1 -1
- package/dist/agent/semantic-symbols.js +2 -3
- package/dist/agent/semantic-symbols.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/agent/type-hierarchy.js +2 -4
- package/dist/agent/type-hierarchy.js.map +1 -1
- package/dist/agent/workspace-symbols.d.ts.map +1 -1
- package/dist/agent/workspace-symbols.js +2 -3
- package/dist/agent/workspace-symbols.js.map +1 -1
- package/dist/agent-tools.d.ts +0 -4
- package/dist/agent-tools.d.ts.map +1 -1
- package/dist/agent-tools.js +3 -6
- package/dist/agent-tools.js.map +1 -1
- package/dist/analysis-summary.d.ts +8 -4
- package/dist/analysis-summary.d.ts.map +1 -1
- package/dist/analysis-summary.js +15 -66
- package/dist/analysis-summary.js.map +1 -1
- package/dist/bin/cli.js +51145 -41770
- package/dist/bin/query-index-worker.js +122 -82
- 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/command-table.d.ts.map +1 -1
- package/dist/cli/command-table.js +9 -17
- package/dist/cli/command-table.js.map +1 -1
- package/dist/cli/context.js +10 -10
- package/dist/cli/context.js.map +1 -1
- package/dist/cli/drift.d.ts +0 -2
- package/dist/cli/drift.d.ts.map +1 -1
- package/dist/cli/drift.js +0 -1
- package/dist/cli/drift.js.map +1 -1
- package/dist/cli/graph-delta.d.ts +0 -2
- package/dist/cli/graph-delta.d.ts.map +1 -1
- package/dist/cli/graph-delta.js +0 -4
- package/dist/cli/graph-delta.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/graph.d.ts +0 -3
- package/dist/cli/graph.d.ts.map +1 -1
- package/dist/cli/graph.js +4 -14
- package/dist/cli/graph.js.map +1 -1
- package/dist/cli/help.d.ts +34 -34
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +9 -14
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/impact.d.ts +0 -2
- package/dist/cli/impact.d.ts.map +1 -1
- package/dist/cli/impact.js +0 -1
- package/dist/cli/impact.js.map +1 -1
- package/dist/cli/index.d.ts +0 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +5 -6
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/inspect.d.ts +0 -2
- package/dist/cli/inspect.d.ts.map +1 -1
- package/dist/cli/inspect.js +5 -8
- package/dist/cli/inspect.js.map +1 -1
- package/dist/cli/invocation-context.d.ts +0 -3
- package/dist/cli/invocation-context.d.ts.map +1 -1
- package/dist/cli/invocation-context.js +4 -16
- package/dist/cli/invocation-context.js.map +1 -1
- package/dist/cli/navigation.d.ts +0 -2
- package/dist/cli/navigation.d.ts.map +1 -1
- package/dist/cli/navigation.js +0 -1
- package/dist/cli/navigation.js.map +1 -1
- package/dist/cli/options.d.ts.map +1 -1
- package/dist/cli/options.js +4 -14
- package/dist/cli/options.js.map +1 -1
- package/dist/cli/review.d.ts +0 -2
- package/dist/cli/review.d.ts.map +1 -1
- package/dist/cli/review.js +0 -5
- package/dist/cli/review.js.map +1 -1
- package/dist/cli/root-policy.d.ts +0 -2
- package/dist/cli/root-policy.d.ts.map +1 -1
- package/dist/cli/root-policy.js +0 -8
- package/dist/cli/root-policy.js.map +1 -1
- package/dist/cli/server.d.ts.map +1 -1
- package/dist/cli/server.js +0 -2
- package/dist/cli/server.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/git.d.ts.map +1 -1
- package/dist/drift/git.js +0 -1
- package/dist/drift/git.js.map +1 -1
- package/dist/drift/snapshot.d.ts.map +1 -1
- package/dist/drift/snapshot.js +5 -7
- package/dist/drift/snapshot.js.map +1 -1
- package/dist/drift/types.d.ts +2 -2
- package/dist/drift/types.d.ts.map +1 -1
- package/dist/drift/types.js.map +1 -1
- package/dist/duplicates/unit-cache.d.ts +0 -1
- package/dist/duplicates/unit-cache.d.ts.map +1 -1
- package/dist/duplicates/unit-cache.js +3 -11
- package/dist/duplicates/unit-cache.js.map +1 -1
- package/dist/duplicates/units.d.ts +5 -5
- package/dist/duplicates/units.d.ts.map +1 -1
- package/dist/duplicates/units.js +25 -88
- package/dist/duplicates/units.js.map +1 -1
- package/dist/graph-builder.d.ts +0 -6
- package/dist/graph-builder.d.ts.map +1 -1
- package/dist/graph-builder.js +6 -11
- package/dist/graph-builder.js.map +1 -1
- package/dist/graph-edge-collector.d.ts +4 -6
- package/dist/graph-edge-collector.d.ts.map +1 -1
- package/dist/graph-edge-collector.js +31 -26
- 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 +24 -76
- package/dist/graphs/edge-resolution.js.map +1 -1
- package/dist/graphs/specifiers.d.ts +4 -13
- package/dist/graphs/specifiers.d.ts.map +1 -1
- package/dist/graphs/specifiers.js +80 -173
- 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 +60 -8
- package/dist/graphs/symbol-graph-detailed/edge-passes.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/edge-passes.js +997 -168
- 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 +150 -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 +141 -25
- package/dist/graphs/symbol-graph-detailed/receiver-calls.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/receiver-calls.js +1015 -166
- 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 +449 -46
- 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/graphs/types.d.ts +0 -4
- package/dist/graphs/types.d.ts.map +1 -1
- package/dist/graphs.d.ts +1 -1
- package/dist/graphs.d.ts.map +1 -1
- package/dist/impact/call-compatibility/providers/index.d.ts +4 -4
- package/dist/impact/call-compatibility/providers/index.d.ts.map +1 -1
- package/dist/impact/call-compatibility/providers/index.js +6 -6
- 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 -526
- 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/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 +2 -2
- 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 +39 -1
- package/dist/indexer/build-cache/module-cache.d.ts.map +1 -1
- package/dist/indexer/build-cache/module-cache.js +218 -21
- package/dist/indexer/build-cache/module-cache.js.map +1 -1
- package/dist/indexer/build-cache/options.d.ts +30 -44
- package/dist/indexer/build-cache/options.d.ts.map +1 -1
- package/dist/indexer/build-cache/options.js +32 -71
- package/dist/indexer/build-cache/options.js.map +1 -1
- package/dist/indexer/build-cache/project-snapshot.d.ts +3 -3
- package/dist/indexer/build-cache/project-snapshot.d.ts.map +1 -1
- package/dist/indexer/build-cache/project-snapshot.js +164 -84
- package/dist/indexer/build-cache/project-snapshot.js.map +1 -1
- package/dist/indexer/build-cache/reports.d.ts +0 -2
- package/dist/indexer/build-cache/reports.d.ts.map +1 -1
- package/dist/indexer/build-cache/reports.js +0 -97
- package/dist/indexer/build-cache/reports.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 +2 -2
- package/dist/indexer/build-cache.d.ts.map +1 -1
- package/dist/indexer/build-cache.js +2 -2
- 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 +656 -71
- package/dist/indexer/build-index.js.map +1 -1
- package/dist/indexer/build-workers.d.ts.map +1 -1
- package/dist/indexer/build-workers.js +9 -9
- package/dist/indexer/build-workers.js.map +1 -1
- package/dist/indexer/call-hierarchy.js +1 -1
- package/dist/indexer/call-hierarchy.js.map +1 -1
- package/dist/indexer/callable-identity.d.ts +24 -0
- package/dist/indexer/callable-identity.d.ts.map +1 -0
- package/dist/indexer/callable-identity.js +111 -0
- package/dist/indexer/callable-identity.js.map +1 -0
- package/dist/indexer/compilation-units.d.ts +103 -0
- package/dist/indexer/compilation-units.d.ts.map +1 -0
- package/dist/indexer/compilation-units.js +607 -0
- package/dist/indexer/compilation-units.js.map +1 -0
- package/dist/indexer/cpp-callables.d.ts +25 -0
- package/dist/indexer/cpp-callables.d.ts.map +1 -0
- package/dist/indexer/cpp-callables.js +562 -0
- package/dist/indexer/cpp-callables.js.map +1 -0
- package/dist/indexer/declaration-visibility.d.ts +43 -2
- package/dist/indexer/declaration-visibility.d.ts.map +1 -1
- package/dist/indexer/declaration-visibility.js +158 -2
- package/dist/indexer/declaration-visibility.js.map +1 -1
- package/dist/indexer/expand-star-imports.d.ts +6 -1
- package/dist/indexer/expand-star-imports.d.ts.map +1 -1
- package/dist/indexer/expand-star-imports.js +131 -37
- package/dist/indexer/expand-star-imports.js.map +1 -1
- package/dist/indexer/finalize.d.ts.map +1 -1
- package/dist/indexer/finalize.js +0 -1
- package/dist/indexer/finalize.js.map +1 -1
- package/dist/indexer/import-resolution-tables.d.ts +72 -0
- package/dist/indexer/import-resolution-tables.d.ts.map +1 -0
- package/dist/indexer/import-resolution-tables.js +137 -0
- package/dist/indexer/import-resolution-tables.js.map +1 -0
- package/dist/indexer/import-types.d.ts +54 -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/context.d.ts +16 -1
- package/dist/indexer/imports/context.d.ts.map +1 -1
- package/dist/indexer/imports/import-binding-tables.d.ts +13 -1
- package/dist/indexer/imports/import-binding-tables.d.ts.map +1 -1
- package/dist/indexer/imports/import-binding-tables.js +149 -30
- package/dist/indexer/imports/import-binding-tables.js.map +1 -1
- 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/native-captures.d.ts.map +1 -1
- package/dist/indexer/imports/native-captures.js +24 -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 +128 -112
- 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 +68 -19
- package/dist/indexer/imports/text-import-extractors.js.map +1 -1
- package/dist/indexer/imports.d.ts +3 -4
- package/dist/indexer/imports.d.ts.map +1 -1
- package/dist/indexer/imports.js +116 -72
- 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 +203 -1
- package/dist/indexer/incremental-plan.js.map +1 -1
- package/dist/indexer/locals-and-exports.d.ts +3 -2
- package/dist/indexer/locals-and-exports.d.ts.map +1 -1
- package/dist/indexer/locals-and-exports.js +659 -78
- package/dist/indexer/locals-and-exports.js.map +1 -1
- package/dist/indexer/member-selection.d.ts +46 -0
- package/dist/indexer/member-selection.d.ts.map +1 -0
- package/dist/indexer/member-selection.js +169 -0
- package/dist/indexer/member-selection.js.map +1 -0
- 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 +137 -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 +119 -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 +132 -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 +82 -0
- package/dist/indexer/name-lookup-policies/php.js.map +1 -0
- package/dist/indexer/name-resolution-types.d.ts +101 -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 +57 -0
- package/dist/indexer/name-resolution.d.ts.map +1 -0
- package/dist/indexer/name-resolution.js +204 -0
- package/dist/indexer/name-resolution.js.map +1 -0
- package/dist/indexer/navigation-cpp.d.ts +58 -0
- package/dist/indexer/navigation-cpp.d.ts.map +1 -0
- package/dist/indexer/navigation-cpp.js +935 -0
- package/dist/indexer/navigation-cpp.js.map +1 -0
- package/dist/indexer/navigation-goto.d.ts +115 -3
- package/dist/indexer/navigation-goto.d.ts.map +1 -1
- package/dist/indexer/navigation-goto.js +2469 -190
- package/dist/indexer/navigation-goto.js.map +1 -1
- package/dist/indexer/navigation-local.d.ts +21 -2
- package/dist/indexer/navigation-local.d.ts.map +1 -1
- package/dist/indexer/navigation-local.js +520 -63
- 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-provenance.d.ts +1 -1
- package/dist/indexer/navigation-provenance.d.ts.map +1 -1
- package/dist/indexer/navigation-provenance.js +4 -13
- package/dist/indexer/navigation-provenance.js.map +1 -1
- package/dist/indexer/navigation-references.d.ts +73 -6
- package/dist/indexer/navigation-references.d.ts.map +1 -1
- package/dist/indexer/navigation-references.js +661 -67
- package/dist/indexer/navigation-references.js.map +1 -1
- package/dist/indexer/navigation-resolve.d.ts +46 -2
- package/dist/indexer/navigation-resolve.d.ts.map +1 -1
- package/dist/indexer/navigation-resolve.js +599 -193
- package/dist/indexer/navigation-resolve.js.map +1 -1
- package/dist/indexer/navigation.d.ts +29 -2
- package/dist/indexer/navigation.d.ts.map +1 -1
- package/dist/indexer/navigation.js +1092 -153
- package/dist/indexer/navigation.js.map +1 -1
- package/dist/indexer/parse-context.d.ts +2 -3
- package/dist/indexer/parse-context.d.ts.map +1 -1
- package/dist/indexer/parse-context.js +8 -21
- package/dist/indexer/parse-context.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-candidates.d.ts.map +1 -1
- package/dist/indexer/reference-candidates.js +13 -7
- package/dist/indexer/reference-candidates.js.map +1 -1
- 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 +29 -1
- package/dist/indexer/scope-types.d.ts.map +1 -1
- package/dist/indexer/scope.d.ts +2 -3
- package/dist/indexer/scope.d.ts.map +1 -1
- package/dist/indexer/scope.js +968 -47
- 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 +113 -0
- package/dist/indexer/star-import-precedence.d.ts.map +1 -0
- package/dist/indexer/star-import-precedence.js +402 -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 +42 -0
- package/dist/indexer/ts-callables.d.ts.map +1 -0
- package/dist/indexer/ts-callables.js +233 -0
- package/dist/indexer/ts-callables.js.map +1 -0
- package/dist/indexer/types.d.ts +41 -26
- 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 +3 -8
- 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 +223 -0
- package/dist/languages/callable-arity.d.ts.map +1 -0
- package/dist/languages/callable-arity.js +1199 -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 +8 -0
- package/dist/languages/definitions/zig.js.map +1 -1
- package/dist/lifecycle/manifest.d.ts.map +1 -1
- package/dist/lifecycle/manifest.js +0 -1
- package/dist/lifecycle/manifest.js.map +1 -1
- package/dist/mcp/handlers.js +14 -7
- package/dist/mcp/handlers.js.map +1 -1
- package/dist/native/byte-index.d.ts +6 -0
- package/dist/native/byte-index.d.ts.map +1 -1
- package/dist/native/byte-index.js +11 -6
- package/dist/native/byte-index.js.map +1 -1
- package/dist/native/contracts.d.ts +2 -3
- package/dist/native/contracts.d.ts.map +1 -1
- package/dist/native/execution.d.ts +9 -9
- package/dist/native/execution.d.ts.map +1 -1
- package/dist/native/execution.js +37 -77
- package/dist/native/execution.js.map +1 -1
- package/dist/native/js-bridge.d.ts +2 -15
- package/dist/native/js-bridge.d.ts.map +1 -1
- package/dist/native/js-bridge.js +2 -15
- package/dist/native/js-bridge.js.map +1 -1
- package/dist/native/query-results.js +2 -2
- package/dist/native/query-results.js.map +1 -1
- package/dist/native/runtime.d.ts +12 -12
- package/dist/native/runtime.d.ts.map +1 -1
- package/dist/native/runtime.js +24 -69
- package/dist/native/runtime.js.map +1 -1
- package/dist/native/tree-sitter-native.d.ts +3 -3
- package/dist/native/tree-sitter-native.d.ts.map +1 -1
- package/dist/native/tree-sitter-native.js +2 -2
- package/dist/native/tree-sitter-native.js.map +1 -1
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +0 -4
- package/dist/presets.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 +3 -20
- package/dist/review.js.map +1 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +0 -5
- package/dist/session.js.map +1 -1
- package/dist/sql/navigation.d.ts.map +1 -1
- package/dist/sql/navigation.js +2 -1
- 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 +8 -1
- package/dist/util/bloom-filter.d.ts.map +1 -1
- package/dist/util/bloom-filter.js +15 -2
- package/dist/util/bloom-filter.js.map +1 -1
- package/dist/util/glob.d.ts +32 -0
- package/dist/util/glob.d.ts.map +1 -0
- package/dist/util/glob.js +137 -0
- package/dist/util/glob.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 +61 -4
- package/dist/util/member-access-tables.d.ts.map +1 -1
- package/dist/util/member-access-tables.js +139 -19
- package/dist/util/member-access-tables.js.map +1 -1
- 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 +98 -3
- package/dist/util/member-access.js.map +1 -1
- package/dist/util/project-files.d.ts +2 -10
- package/dist/util/project-files.d.ts.map +1 -1
- package/dist/util/project-files.js +29 -51
- package/dist/util/project-files.js.map +1 -1
- package/dist/util/python-type-checking.d.ts +18 -0
- package/dist/util/python-type-checking.d.ts.map +1 -0
- package/dist/util/python-type-checking.js +160 -0
- package/dist/util/python-type-checking.js.map +1 -0
- 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/jvm.d.ts +7 -3
- package/dist/util/resolution/jvm.d.ts.map +1 -1
- package/dist/util/resolution/jvm.js +56 -38
- package/dist/util/resolution/jvm.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 +0 -1
- package/dist/util/resolution/rust.d.ts.map +1 -1
- package/dist/util/resolution/rust.js +95 -35
- package/dist/util/resolution/rust.js.map +1 -1
- package/dist/util/resolution/specifier-targets.d.ts +44 -0
- package/dist/util/resolution/specifier-targets.d.ts.map +1 -0
- package/dist/util/resolution/specifier-targets.js +152 -0
- package/dist/util/resolution/specifier-targets.js.map +1 -0
- 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 +101 -15
- package/dist/util/resolution.js.map +1 -1
- package/dist/util/specifiers.d.ts +29 -1
- package/dist/util/specifiers.d.ts.map +1 -1
- package/dist/util/specifiers.js +56 -8
- package/dist/util/specifiers.js.map +1 -1
- package/dist/util/trivia.d.ts.map +1 -1
- package/dist/util/trivia.js +49 -30
- package/dist/util/trivia.js.map +1 -1
- package/dist/util/workspace.d.ts.map +1 -1
- package/dist/util/workspace.js +32 -4
- package/dist/util/workspace.js.map +1 -1
- package/docs/mcp.md +3 -2
- package/package.json +5 -7
- package/dist/impact/call-compatibility/providers/profiles.d.ts +0 -80
- package/dist/impact/call-compatibility/providers/profiles.d.ts.map +0 -1
- package/dist/impact/call-compatibility/providers/profiles.js +0 -164
- package/dist/impact/call-compatibility/providers/profiles.js.map +0 -1
|
@@ -1,16 +1,517 @@
|
|
|
1
|
+
import { CSHARP_SUPPORT, supportForFileWithoutHeaderSample } from "../languages.js";
|
|
1
2
|
import { isJsTsLanguage } from "../languages/js-family.js";
|
|
2
3
|
import { isPythonReceiverAttributeAssignmentName } from "../languages/definitions/python.js";
|
|
3
4
|
import { sliceText } from "../util/ast.js";
|
|
5
|
+
import { foldPhpIdentifierCase } from "../util/identifiers.js";
|
|
4
6
|
import { fileIdentityKey } from "../util/paths.js";
|
|
5
|
-
import { getMemberAccessParts, getNavigationExpressionProperty, isMemberAccessNode, isReceiverNameNode, memberAccessTraversalTypes, } from "../util/member-access.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
7
|
+
import { collectMemberAccessChain, getMemberAccessParts, getNavigationExpressionProperty, isMemberAccessNode, isReceiverNameNode, memberAccessTraversalTypes, } from "../util/member-access.js";
|
|
8
|
+
import { MEMBER_ACCESS_ROWS, supportsReceiverMemberNavigation } from "../util/member-access-tables.js";
|
|
9
|
+
import { typescriptCallableContainerKey, typescriptMergedNamespaceContainers } from "./ts-callables.js";
|
|
10
|
+
import { effectiveExplicitBinding, isExpandedStarBinding, resolveStarImportedName, resolveStarImportedNamespace, starImportPrecedence, } from "./star-import-precedence.js";
|
|
11
|
+
import { classifyReceiver, cppOutOfLineOwnerPath, cppQualifiedNameSegments, declarationIsStaticEquivalent, declarationNodeIsStatic, declaredTypeNameNode, declaresMembers, hasStaticMemberDistinction, memberContainerDef, nearestMemberContainer, isUnprovenHeritageExpression, kotlinExtensionReceiverTypeNode, csharpDottedNameRoot, nodeInStaticMemberContext, importTypeQuerySpecifier, rustImplSelfTypeNode, supportsStaticMemberScope, TRANSPARENT_MEMBER_CONTAINER_TYPES, unwrapNamedType, } from "../graphs/symbol-graph-detailed/receiver-calls.js";
|
|
12
|
+
import { getCallArgumentCount, memberLookupBinding } from "../languages/callable-arity.js";
|
|
13
|
+
import { getCompilationUnitPeers, getPackageDeclarationName } from "./compilation-units.js";
|
|
14
|
+
import { isExportedDeclaration, isJvmPackageSymbolVisible, isPrivateDeclaration, isGoExportedMemberName, isSwiftCrossFileHiddenSharedOwnerMember, } from "./declaration-visibility.js";
|
|
8
15
|
import { ensureParsedContext } from "./parse-context.js";
|
|
16
|
+
import { csharpLookupName, csharpQualifiedNameNode, findClosestScopeBinding, getOrBuildScopeIndex, resolveNamedDefinition, } from "./navigation-local.js";
|
|
17
|
+
import { resolveCppQualifiedMemberContainer, cppFunctionHasImplicitThis } from "./navigation-cpp.js";
|
|
9
18
|
import { okGoToResult } from "./navigation-provenance.js";
|
|
10
|
-
import {
|
|
19
|
+
import { comparePhpReferenceNames, findPhpImportAlias } from "./navigation-php.js";
|
|
20
|
+
import { resolveIndexedPhpClassReference, resolvePhpNamespaceSymbol } from "./php-namespace-symbols.js";
|
|
21
|
+
import { cjsRequireValueBinding, memberContainerForDefinition, resolveCsharpQualifiedName, resolveExport, resolveImported, resolvePhpExportByImportType, resolveImportTypeMember, } from "./navigation-resolve.js";
|
|
22
|
+
import { CSHARP_PARTIAL_CONTAINER_TYPES, getSharedOwnerIdentity, isSwiftExtensionContainer, sharedOwnerCanUseMembers, } from "./shared-owner-identity.js";
|
|
23
|
+
import { hasSeparateMemberScopes, inheritsMemberOverloads, selectMember, } from "./member-selection.js";
|
|
11
24
|
import { SymbolKind, } from "./types.js";
|
|
25
|
+
const DECLARED_FIELD_CHAIN_LANGUAGES = {
|
|
26
|
+
ts: true,
|
|
27
|
+
tsx: true,
|
|
28
|
+
java: true,
|
|
29
|
+
csharp: true,
|
|
30
|
+
kotlin: true,
|
|
31
|
+
};
|
|
32
|
+
/** Type-syntax nodes that wrap a declared type name together with its qualifier. */
|
|
33
|
+
const QUALIFIED_TYPE_NODE_TYPES = {
|
|
34
|
+
qualified_name: true,
|
|
35
|
+
scoped_type_identifier: true,
|
|
36
|
+
nested_type_identifier: true,
|
|
37
|
+
user_type: true,
|
|
38
|
+
generic_name: true,
|
|
39
|
+
generic_type: true,
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* The full spelling of a declared type, e.g. `B.Repo` for `B.Repo<T>`: the last segment alone
|
|
43
|
+
* would resolve to a same-named type of another namespace.
|
|
44
|
+
*/
|
|
45
|
+
function qualifiedDeclaredTypeText(typeName, source) {
|
|
46
|
+
let top = typeName;
|
|
47
|
+
for (let parent = typeName.parent; parent && QUALIFIED_TYPE_NODE_TYPES[parent.type]; parent = parent.parent) {
|
|
48
|
+
top = parent;
|
|
49
|
+
}
|
|
50
|
+
let text = sliceText(top, source).replace(/\s+/g, "");
|
|
51
|
+
for (let previous = ""; previous !== text;) {
|
|
52
|
+
previous = text;
|
|
53
|
+
text = text.replace(/<[^<>]*>/g, "");
|
|
54
|
+
}
|
|
55
|
+
return text;
|
|
56
|
+
}
|
|
57
|
+
/** Type-parameter list nodes across the TypeScript, Java, C#, and Kotlin grammars. */
|
|
58
|
+
const TYPE_PARAMETER_LIST_TYPES = {
|
|
59
|
+
type_parameters: true,
|
|
60
|
+
type_parameter_list: true,
|
|
61
|
+
};
|
|
62
|
+
/** Names of the generic type parameters declared by `node` and its enclosing declarations. */
|
|
63
|
+
function typeParameterNamesInScope(node, source) {
|
|
64
|
+
const names = new Set();
|
|
65
|
+
for (let current = node; current; current = current.parent) {
|
|
66
|
+
for (const list of current.namedChildren) {
|
|
67
|
+
if (!TYPE_PARAMETER_LIST_TYPES[list.type])
|
|
68
|
+
continue;
|
|
69
|
+
for (const parameter of list.namedChildren) {
|
|
70
|
+
const name = parameter.childForFieldName("name") ??
|
|
71
|
+
parameter.namedChildren.find((child) => child.type === "identifier" || child.type === "type_identifier");
|
|
72
|
+
if (name)
|
|
73
|
+
names.add(sliceText(name, source));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return names;
|
|
78
|
+
}
|
|
79
|
+
export function supportsDeclaredFieldChains(languageId) {
|
|
80
|
+
return !!DECLARED_FIELD_CHAIN_LANGUAGES[languageId];
|
|
81
|
+
}
|
|
82
|
+
function noLexicalBinding() {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
/** A Rust named `use crate::mod` binding names the indexed module, not a class export. */
|
|
86
|
+
export function resolveRustModuleNamespace(index, binding) {
|
|
87
|
+
if (binding.kind !== "named" || typeof binding.resolved !== "string")
|
|
88
|
+
return null;
|
|
89
|
+
const owner = index.byFile.get(fileIdentityKey(binding.resolved));
|
|
90
|
+
if (!owner)
|
|
91
|
+
return null;
|
|
92
|
+
const modules = owner.imports.filter((candidate) => candidate.kind === "namespace" && candidate.localNS === binding.imported);
|
|
93
|
+
if (modules.length !== 1)
|
|
94
|
+
return null;
|
|
95
|
+
const resolved = modules[0]?.resolved;
|
|
96
|
+
return typeof resolved === "string" ? resolved : null;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* One bound for every receiver-hierarchy walk in this module: keyword `super`/`parent` lookup,
|
|
100
|
+
* Go struct embedding, and Python base classes. Each walk keeps its own visited set, so this is a
|
|
101
|
+
* resource bound rather than a cycle guard; keeping a single constant stops the three walks from
|
|
102
|
+
* drifting to different semantic cutoffs.
|
|
103
|
+
*/
|
|
104
|
+
const RECEIVER_HIERARCHY_DEPTH = 16;
|
|
105
|
+
function collectSharedOwnerContainers(root, languageId, out) {
|
|
106
|
+
if (languageId === "csharp") {
|
|
107
|
+
if (CSHARP_PARTIAL_CONTAINER_TYPES.has(root.type))
|
|
108
|
+
out.push(root);
|
|
109
|
+
}
|
|
110
|
+
else if (languageId === "swift") {
|
|
111
|
+
if (root.type === "class_declaration")
|
|
112
|
+
out.push(root);
|
|
113
|
+
}
|
|
114
|
+
for (const child of root.namedChildren ?? [])
|
|
115
|
+
collectSharedOwnerContainers(child, languageId, out);
|
|
116
|
+
}
|
|
117
|
+
const sharedOwnerContainersByRoot = new WeakMap();
|
|
118
|
+
function sharedOwnerContainersInTree(root, languageId) {
|
|
119
|
+
let byLanguage = sharedOwnerContainersByRoot.get(root);
|
|
120
|
+
if (!byLanguage) {
|
|
121
|
+
byLanguage = new Map();
|
|
122
|
+
sharedOwnerContainersByRoot.set(root, byLanguage);
|
|
123
|
+
}
|
|
124
|
+
const cached = byLanguage.get(languageId);
|
|
125
|
+
if (cached)
|
|
126
|
+
return cached;
|
|
127
|
+
const collected = [];
|
|
128
|
+
collectSharedOwnerContainers(root, languageId, collected);
|
|
129
|
+
byLanguage.set(languageId, collected);
|
|
130
|
+
return collected;
|
|
131
|
+
}
|
|
132
|
+
export async function resolveSharedOwnerContainers(params) {
|
|
133
|
+
const { index, ownerFile, ownerContainer, ownerSource, languageId } = params;
|
|
134
|
+
if (languageId !== "csharp" && languageId !== "swift")
|
|
135
|
+
return [];
|
|
136
|
+
const ownerIdentity = getSharedOwnerIdentity(ownerContainer, ownerSource, languageId, ownerFile);
|
|
137
|
+
if (!ownerIdentity)
|
|
138
|
+
return [];
|
|
139
|
+
// A C# `file` owner has only same-file parts, so no other file is a candidate peer.
|
|
140
|
+
const peers = ownerIdentity.fileLocalTo
|
|
141
|
+
? { files: new Set([ownerFile]), complete: true }
|
|
142
|
+
: getCompilationUnitPeers(index, ownerFile);
|
|
143
|
+
const out = [];
|
|
144
|
+
const seen = new Set();
|
|
145
|
+
const ownerIsExtension = languageId === "swift" ? isSwiftExtensionContainer(ownerContainer, ownerSource) : false;
|
|
146
|
+
const ownerKey = fileIdentityKey(ownerFile);
|
|
147
|
+
const units = [];
|
|
148
|
+
for (const file of peers.files) {
|
|
149
|
+
const peer = index.byFile.get(fileIdentityKey(file));
|
|
150
|
+
if (peer)
|
|
151
|
+
units.push(peer);
|
|
152
|
+
}
|
|
153
|
+
for (const peer of units) {
|
|
154
|
+
let peerContext;
|
|
155
|
+
try {
|
|
156
|
+
peerContext = await ensureParsedContext(peer.file, index.parsed?.get(fileIdentityKey(peer.file)), index.languageExtensions);
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (peerContext.sup.id !== languageId)
|
|
162
|
+
continue;
|
|
163
|
+
const containers = sharedOwnerContainersInTree(peerContext.tree.rootNode, languageId);
|
|
164
|
+
for (const container of containers) {
|
|
165
|
+
if (fileIdentityKey(peer.file) === ownerKey &&
|
|
166
|
+
container.startIndex === ownerContainer.startIndex &&
|
|
167
|
+
container.endIndex === ownerContainer.endIndex) {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
const identity = getSharedOwnerIdentity(container, peerContext.source, languageId, peer.file);
|
|
171
|
+
if (!identity || !sharedOwnerCanUseMembers(ownerIdentity, identity))
|
|
172
|
+
continue;
|
|
173
|
+
if (languageId === "swift") {
|
|
174
|
+
const peerIsExtension = isSwiftExtensionContainer(container, peerContext.source);
|
|
175
|
+
if (!ownerIsExtension && !peerIsExtension)
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
const key = fileIdentityKey(peer.file) + ":" + container.startIndex + ":" + container.endIndex;
|
|
179
|
+
if (seen.has(key))
|
|
180
|
+
continue;
|
|
181
|
+
seen.add(key);
|
|
182
|
+
out.push({ file: peer.file, container, context: peerContext, module: peer });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return out;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Unit-boundary completeness for a C# partial or Swift type/extension member. Its other
|
|
189
|
+
* owner parts, and therefore its uses, are bounded by `getCompilationUnitPeers`, so an
|
|
190
|
+
* unproven unit boundary leaves the member's reference set incomplete. Returns null when
|
|
191
|
+
* the member's owner cannot be shared across files.
|
|
192
|
+
*/
|
|
193
|
+
export async function sharedOwnerMemberUnitComplete(index, def) {
|
|
194
|
+
if (!def.isMember)
|
|
195
|
+
return null;
|
|
196
|
+
let context;
|
|
197
|
+
try {
|
|
198
|
+
context = await ensureParsedContext(def.file, index.parsed?.get(fileIdentityKey(def.file)), index.languageExtensions);
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
const languageId = context.sup.id;
|
|
204
|
+
if (languageId !== "csharp" && languageId !== "swift")
|
|
205
|
+
return null;
|
|
206
|
+
const nameNode = nameNodeForDef(context, def);
|
|
207
|
+
const container = nameNode ? nearestMemberContainer(nameNode) : null;
|
|
208
|
+
const identity = container ? getSharedOwnerIdentity(container, context.source, languageId, def.file) : null;
|
|
209
|
+
if (!identity)
|
|
210
|
+
return null;
|
|
211
|
+
// File-local owners have no parts in other files, so the reference set is complete.
|
|
212
|
+
if (identity.fileLocalTo)
|
|
213
|
+
return true;
|
|
214
|
+
// A C# member is reachable through a qualified owner (`P.Box`) from any namespace, so use the
|
|
215
|
+
// same qualified peer relation as reference candidate discovery.
|
|
216
|
+
return getCompilationUnitPeers(index, def.file, languageId === "csharp" ? { csharpQualifiedName: true } : undefined)
|
|
217
|
+
.complete;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Other C# `partial` type parts that share owner identity with `def`.
|
|
221
|
+
* Reference collection treats them as one type so uses that resolve to the
|
|
222
|
+
* coalesced representative still match a query started on any part.
|
|
223
|
+
*/
|
|
224
|
+
export async function findCsharpPartialTypeEquivalents(index, def) {
|
|
225
|
+
if (!declaresMembers(def))
|
|
226
|
+
return [];
|
|
227
|
+
const module = index.byFile.get(fileIdentityKey(def.file));
|
|
228
|
+
if (!module)
|
|
229
|
+
return [];
|
|
230
|
+
let context;
|
|
231
|
+
try {
|
|
232
|
+
context = await ensureParsedContext(def.file, index.parsed?.get(fileIdentityKey(def.file)), index.languageExtensions);
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
return [];
|
|
236
|
+
}
|
|
237
|
+
if (context.sup.id !== "csharp")
|
|
238
|
+
return [];
|
|
239
|
+
const start = def.range.start;
|
|
240
|
+
const nameNode = context.tree.rootNode.descendantForPosition({ row: start.line - 1, column: start.column - 1 }, { row: start.line - 1, column: start.column - 1 });
|
|
241
|
+
const container = nearestMemberContainer(nameNode);
|
|
242
|
+
if (!container)
|
|
243
|
+
return [];
|
|
244
|
+
const peers = await resolveSharedOwnerContainers({
|
|
245
|
+
index,
|
|
246
|
+
ownerFile: def.file,
|
|
247
|
+
ownerContainer: container,
|
|
248
|
+
ownerSource: context.source,
|
|
249
|
+
languageId: "csharp",
|
|
250
|
+
});
|
|
251
|
+
const out = [];
|
|
252
|
+
for (const peer of peers) {
|
|
253
|
+
const peerName = peer.container.childForFieldName("name");
|
|
254
|
+
if (!peerName)
|
|
255
|
+
continue;
|
|
256
|
+
const peerDef = peer.module.locals.find((local) => declaresMembers(local) && local.range.start.index === peerName.startIndex);
|
|
257
|
+
if (peerDef)
|
|
258
|
+
out.push(peerDef);
|
|
259
|
+
}
|
|
260
|
+
return out;
|
|
261
|
+
}
|
|
262
|
+
/** Reusable shared-owner member lookup for keyword navigation and receiver graphs. */
|
|
263
|
+
export async function findSharedOwnerMemberDefinitions(params) {
|
|
264
|
+
const { index, ownerFile, ownerContainer, ownerSource, languageId, member, memberScope } = params;
|
|
265
|
+
const peers = await resolveSharedOwnerContainers({ index, ownerFile, ownerContainer, ownerSource, languageId });
|
|
266
|
+
const out = [];
|
|
267
|
+
for (const peer of peers) {
|
|
268
|
+
const predicate = !memberScope || memberScope === "any"
|
|
269
|
+
? undefined
|
|
270
|
+
: (local) => matchesReceiverMemberScope(local, memberScope, peer.context, peer.container);
|
|
271
|
+
const hits = findDirectLocalsWithinNode(peer.module.locals, member, peer.container, peer.context, peer.context.sup.normalizeIdentifier, predicate);
|
|
272
|
+
for (const hit of hits) {
|
|
273
|
+
const hitNode = nameNodeForDef(peer.context, hit);
|
|
274
|
+
if (languageId === "swift" && fileIdentityKey(ownerFile) !== fileIdentityKey(peer.file)) {
|
|
275
|
+
if (!hitNode || isSwiftCrossFileHiddenSharedOwnerMember(languageId, ownerFile, peer.file, hitNode))
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
if (!out.includes(hit))
|
|
279
|
+
out.push(hit);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return out;
|
|
283
|
+
}
|
|
284
|
+
function nameNodeForDef(context, def) {
|
|
285
|
+
const start = def.range.start;
|
|
286
|
+
const index = start.index;
|
|
287
|
+
if (index !== undefined)
|
|
288
|
+
return context.tree.rootNode.descendantForIndex(index, index);
|
|
289
|
+
const position = { row: start.line - 1, column: start.column - 1 };
|
|
290
|
+
return context.tree.rootNode.descendantForPosition(position, position);
|
|
291
|
+
}
|
|
292
|
+
function enclosingImportScope(declarationName) {
|
|
293
|
+
let current = declarationName;
|
|
294
|
+
while (current && current.type !== "variable_declaration" && current.type !== "using_directive") {
|
|
295
|
+
current = current.parent;
|
|
296
|
+
}
|
|
297
|
+
return current?.parent ?? null;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Rewrites a valid C# `using X = Namespace; X::Target` lookup into the dotted form
|
|
301
|
+
* export resolution already understands. `global::` and ordinary dotted names keep
|
|
302
|
+
* their source spelling; an unknown alias stays `X::Target` instead of a bare name.
|
|
303
|
+
*/
|
|
304
|
+
export function csharpAliasQualifiedLookupName(node, source, fallback, imports) {
|
|
305
|
+
const raw = csharpLookupName(node, source, fallback);
|
|
306
|
+
if (raw.startsWith("global::"))
|
|
307
|
+
return raw;
|
|
308
|
+
const chainNode = csharpQualifiedNameNode(node);
|
|
309
|
+
if (!chainNode || !raw.includes("::"))
|
|
310
|
+
return raw;
|
|
311
|
+
const names = [];
|
|
312
|
+
let current = chainNode;
|
|
313
|
+
let base = null;
|
|
314
|
+
while (current && (current.type === "qualified_name" || current.type === "alias_qualified_name")) {
|
|
315
|
+
const parts = getMemberAccessParts(CSHARP_SUPPORT, current);
|
|
316
|
+
base = parts.object ?? base;
|
|
317
|
+
let property = parts.property;
|
|
318
|
+
if (property?.type === "generic_name") {
|
|
319
|
+
property = property.childForFieldName("name") ?? property.namedChildren[0] ?? property;
|
|
320
|
+
}
|
|
321
|
+
if (property?.type === "identifier")
|
|
322
|
+
names.push(sliceText(property, source));
|
|
323
|
+
current = base;
|
|
324
|
+
}
|
|
325
|
+
if (!base || !isReceiverNameNode(CSHARP_SUPPORT, base.type) || !names.length)
|
|
326
|
+
return raw;
|
|
327
|
+
const alias = sliceText(base, source);
|
|
328
|
+
if (alias === "global")
|
|
329
|
+
return raw;
|
|
330
|
+
const imported = innermostNamespaceImport(imports, alias, base, CSHARP_SUPPORT.normalizeIdentifier);
|
|
331
|
+
if (!imported)
|
|
332
|
+
return raw;
|
|
333
|
+
return `${imported.from}.${[...names].reverse().join(".")}`;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Innermost in-scope namespace import bound to `alias`. `normalize` applies the language's
|
|
337
|
+
* identifier equality (C# `@X` and `X` are one alias); the default compares exact spelling.
|
|
338
|
+
*/
|
|
339
|
+
export function innermostNamespaceImport(imports, alias, useNode, normalize = (name) => name) {
|
|
340
|
+
const normalizedAlias = normalize(alias);
|
|
341
|
+
const matches = imports.filter((candidate) => candidate.kind === "namespace" && normalize(candidate.localNS) === normalizedAlias);
|
|
342
|
+
if (!matches.length)
|
|
343
|
+
return undefined;
|
|
344
|
+
let root = useNode;
|
|
345
|
+
while (root.parent)
|
|
346
|
+
root = root.parent;
|
|
347
|
+
let best;
|
|
348
|
+
let bestScopeSpan = Number.POSITIVE_INFINITY;
|
|
349
|
+
let bestStart = -1;
|
|
350
|
+
for (const match of matches) {
|
|
351
|
+
const start = match.localRange?.start.index;
|
|
352
|
+
if (start === undefined)
|
|
353
|
+
continue;
|
|
354
|
+
let nameNode = root;
|
|
355
|
+
for (;;) {
|
|
356
|
+
const child = nameNode.namedChildren.find((candidate) => candidate.startIndex <= start && start < candidate.endIndex);
|
|
357
|
+
if (!child)
|
|
358
|
+
break;
|
|
359
|
+
nameNode = child;
|
|
360
|
+
}
|
|
361
|
+
const scope = enclosingImportScope(nameNode);
|
|
362
|
+
if (!scope)
|
|
363
|
+
continue;
|
|
364
|
+
if (start > useNode.startIndex && scope.parent)
|
|
365
|
+
continue;
|
|
366
|
+
if (scope.startIndex > useNode.startIndex || scope.endIndex < useNode.endIndex)
|
|
367
|
+
continue;
|
|
368
|
+
const span = scope.endIndex - scope.startIndex;
|
|
369
|
+
if (span < bestScopeSpan || (span === bestScopeSpan && start >= bestStart)) {
|
|
370
|
+
bestScopeSpan = span;
|
|
371
|
+
bestStart = start;
|
|
372
|
+
best = match;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (best)
|
|
376
|
+
return best;
|
|
377
|
+
const unlocated = matches.filter((match) => match.localRange?.start.index === undefined);
|
|
378
|
+
if (unlocated.length === 1)
|
|
379
|
+
return unlocated[0];
|
|
380
|
+
const files = new Map();
|
|
381
|
+
for (const match of unlocated) {
|
|
382
|
+
if (typeof match.resolved !== "string")
|
|
383
|
+
continue;
|
|
384
|
+
const key = fileIdentityKey(match.resolved);
|
|
385
|
+
if (!files.has(key))
|
|
386
|
+
files.set(key, match);
|
|
387
|
+
}
|
|
388
|
+
if (files.size === 1)
|
|
389
|
+
return [...files.values()][0];
|
|
390
|
+
return undefined;
|
|
391
|
+
}
|
|
392
|
+
function rubyScopeResolutionPath(node, source) {
|
|
393
|
+
if (node.type !== "constant")
|
|
394
|
+
return null;
|
|
395
|
+
const owner = node.parent;
|
|
396
|
+
if (owner?.type !== "scope_resolution")
|
|
397
|
+
return null;
|
|
398
|
+
const ownerName = owner.childForFieldName("name");
|
|
399
|
+
if (!ownerName || ownerName.startIndex !== node.startIndex || ownerName.endIndex !== node.endIndex)
|
|
400
|
+
return null;
|
|
401
|
+
// `Outer::Inner::Tool` nests scope_resolution on `scope`, so flatten from the
|
|
402
|
+
// clicked name instead of stopping at the first nested scope.
|
|
403
|
+
const segments = [];
|
|
404
|
+
let current = owner;
|
|
405
|
+
while (current?.type === "scope_resolution") {
|
|
406
|
+
const name = current.childForFieldName("name");
|
|
407
|
+
const scope = current.childForFieldName("scope");
|
|
408
|
+
if (!name || name.type !== "constant")
|
|
409
|
+
return null;
|
|
410
|
+
const text = sliceText(name, source);
|
|
411
|
+
if (!text)
|
|
412
|
+
return null;
|
|
413
|
+
segments.unshift(text);
|
|
414
|
+
current = scope;
|
|
415
|
+
}
|
|
416
|
+
if (!current || current.type !== "constant" || segments.length === 0)
|
|
417
|
+
return null;
|
|
418
|
+
const root = sliceText(current, source);
|
|
419
|
+
if (!root)
|
|
420
|
+
return null;
|
|
421
|
+
return { path: `${root}::${segments.join("::")}`, root };
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* `Outer::Inner::Tool` is one constant path. Nested classes are exported under that
|
|
425
|
+
* path, not as bare `Tool`, so the chain has to ask for the qualified export.
|
|
426
|
+
*/
|
|
427
|
+
function resolveRubyQualifiedConstantDefinition(index, mod, node, source) {
|
|
428
|
+
const qualified = rubyScopeResolutionPath(node, source);
|
|
429
|
+
if (!qualified)
|
|
430
|
+
return null;
|
|
431
|
+
const files = [];
|
|
432
|
+
const seen = new Set();
|
|
433
|
+
const add = (file) => {
|
|
434
|
+
if (!file)
|
|
435
|
+
return;
|
|
436
|
+
const key = fileIdentityKey(file);
|
|
437
|
+
if (seen.has(key))
|
|
438
|
+
return;
|
|
439
|
+
seen.add(key);
|
|
440
|
+
files.push(file);
|
|
441
|
+
};
|
|
442
|
+
for (const imp of mod.imports) {
|
|
443
|
+
if (imp.kind === "namespace" && imp.localNS === qualified.root && typeof imp.resolved === "string") {
|
|
444
|
+
add(imp.resolved);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
add(mod.file);
|
|
448
|
+
const hits = [];
|
|
449
|
+
for (const file of files) {
|
|
450
|
+
const hit = resolveExport(index, file, qualified.path, { allowLocalFallback: false });
|
|
451
|
+
if (hit?.kind !== "resolved")
|
|
452
|
+
continue;
|
|
453
|
+
if (hits.some((candidate) => fileIdentityKey(candidate.file) === fileIdentityKey(hit.def.file) &&
|
|
454
|
+
candidate.localName === hit.def.localName &&
|
|
455
|
+
candidate.range.start.line === hit.def.range.start.line &&
|
|
456
|
+
candidate.range.start.column === hit.def.range.start.column)) {
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
hits.push(hit.def);
|
|
460
|
+
}
|
|
461
|
+
if (hits.length !== 1)
|
|
462
|
+
return null;
|
|
463
|
+
return okGoToResult(index, hits[0], {
|
|
464
|
+
via: { exportedName: qualified.path },
|
|
465
|
+
resolution: "member-access",
|
|
466
|
+
confidence: "medium",
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
/** A fully qualified JVM package symbol, never a same-spelled type from another package. */
|
|
470
|
+
function resolveJvmPackageSymbol(index, packageName, symbolName, languageId, useFile) {
|
|
471
|
+
const samePackage = getPackageDeclarationName(index, useFile, languageId) === packageName;
|
|
472
|
+
let target;
|
|
473
|
+
for (const candidate of index.byFile.values()) {
|
|
474
|
+
const targetLanguage = supportForFileWithoutHeaderSample(candidate.file, index.languageExtensions)?.id;
|
|
475
|
+
if (targetLanguage !== "java" && targetLanguage !== "kotlin")
|
|
476
|
+
continue;
|
|
477
|
+
if (getPackageDeclarationName(index, candidate.file, targetLanguage) !== packageName)
|
|
478
|
+
continue;
|
|
479
|
+
for (const entry of candidate.exports) {
|
|
480
|
+
if (entry.type !== "local" ||
|
|
481
|
+
entry.exportedAs !== symbolName ||
|
|
482
|
+
entry.target.isMember ||
|
|
483
|
+
!isJvmPackageSymbolVisible(entry.target, targetLanguage, languageId, samePackage))
|
|
484
|
+
continue;
|
|
485
|
+
if (target &&
|
|
486
|
+
(target.file !== entry.target.file || target.range.start.index !== entry.target.range.start.index)) {
|
|
487
|
+
return undefined;
|
|
488
|
+
}
|
|
489
|
+
target = entry.target;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return target;
|
|
493
|
+
}
|
|
494
|
+
/** Package paths only bind when the leftmost name is not a lexical value or imported alias. */
|
|
495
|
+
function resolveJvmPackageExpression(index, sup, source, expr, resolveLexicalBinding, useFile) {
|
|
496
|
+
if (sup.id !== "java" && sup.id !== "kotlin")
|
|
497
|
+
return null;
|
|
498
|
+
const chain = collectMemberAccessChain({ sup, source, chainNode: expr });
|
|
499
|
+
if (!chain || !isReceiverNameNode(sup, chain.base.type) || resolveLexicalBinding?.(chain.base))
|
|
500
|
+
return null;
|
|
501
|
+
const parts = [sliceText(chain.base, source), ...chain.names.toReversed()];
|
|
502
|
+
const member = parts.pop();
|
|
503
|
+
if (!member || !parts.every((part) => /^[\p{L}_][\p{L}\p{N}_]*$/u.test(part)))
|
|
504
|
+
return null;
|
|
505
|
+
const target = resolveJvmPackageSymbol(index, parts.join("."), member, sup.id, useFile);
|
|
506
|
+
return target ? { kind: "resolved", def: target } : null;
|
|
507
|
+
}
|
|
12
508
|
export async function resolveMemberAccessDefinition(params) {
|
|
13
|
-
const { index, mod, node, source, sup, resolveLexicalBinding } = params;
|
|
509
|
+
const { index, mod, node, source, tree, sup, resolveLexicalBinding } = params;
|
|
510
|
+
if (sup.id === "ruby") {
|
|
511
|
+
const qualified = resolveRubyQualifiedConstantDefinition(index, mod, node, source);
|
|
512
|
+
if (qualified)
|
|
513
|
+
return qualified;
|
|
514
|
+
}
|
|
14
515
|
const parent = node.parent;
|
|
15
516
|
if (!parent || !sup.supportsCrossModuleSymbols) {
|
|
16
517
|
return null;
|
|
@@ -26,46 +527,148 @@ export async function resolveMemberAccessDefinition(params) {
|
|
|
26
527
|
return null;
|
|
27
528
|
const { object: obj, property: prop } = getMemberAccessParts(sup, memberNode);
|
|
28
529
|
const optionalMemberTypes = memberAccessTraversalTypes(sup);
|
|
530
|
+
// Only the outermost access is the callee; an inner hop (`A.B` in `A.B.C()`) is not called.
|
|
531
|
+
const callArgumentCountFor = (expr) => expr.id === memberNode.id
|
|
532
|
+
? (getCallArgumentCount({ languageId: sup.id, source, call: memberNode.parent ?? memberNode }) ?? undefined)
|
|
533
|
+
: undefined;
|
|
29
534
|
const resolveExpression = async (expr) => {
|
|
30
535
|
const exprIsId = isReceiverNameNode(sup, expr.type) && !isMemberAccessNode(sup, expr);
|
|
31
536
|
if (exprIsId) {
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
537
|
+
const aliasQualifier = sup.id === "csharp" &&
|
|
538
|
+
expr.parent?.type === "alias_qualified_name" &&
|
|
539
|
+
getMemberAccessParts(sup, expr.parent).object?.id === expr.id;
|
|
540
|
+
if (!aliasQualifier) {
|
|
541
|
+
const lexicalBinding = resolveLexicalBinding?.(expr);
|
|
542
|
+
if (lexicalBinding)
|
|
543
|
+
return { kind: "resolved", def: lexicalBinding };
|
|
544
|
+
}
|
|
35
545
|
const exprName = sliceText(expr, source);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
(
|
|
546
|
+
let imp;
|
|
547
|
+
if (sup.id === "php") {
|
|
548
|
+
imp = findPhpImportAlias(mod.imports, exprName, "class") ?? undefined;
|
|
549
|
+
}
|
|
550
|
+
else if (sup.id === "zig") {
|
|
551
|
+
imp = innermostNamespaceImport(mod.imports, exprName, expr);
|
|
552
|
+
if (!imp) {
|
|
553
|
+
imp = mod.imports.find((candidate) => (candidate.kind === "named" || candidate.kind === "default") && candidate.local === exprName);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
else if (sup.id === "csharp") {
|
|
557
|
+
if (aliasQualifier) {
|
|
558
|
+
if (exprName !== "global")
|
|
559
|
+
imp = innermostNamespaceImport(mod.imports, exprName, expr, sup.normalizeIdentifier);
|
|
560
|
+
if (!imp)
|
|
561
|
+
return null;
|
|
562
|
+
}
|
|
563
|
+
else {
|
|
564
|
+
imp = innermostNamespaceImport(mod.imports, exprName, expr, sup.normalizeIdentifier);
|
|
565
|
+
if (!imp) {
|
|
566
|
+
imp = mod.imports.find((candidate) => (candidate.kind === "named" || candidate.kind === "default") &&
|
|
567
|
+
candidate.local === exprName &&
|
|
568
|
+
!isExpandedStarBinding(candidate, mod.imports));
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
else {
|
|
573
|
+
imp = effectiveExplicitBinding(mod.imports, sup.id, (candidate) => ((candidate.kind === "named" || candidate.kind === "default") && candidate.local === exprName) ||
|
|
574
|
+
(candidate.kind === "namespace" && candidate.localNS === exprName));
|
|
575
|
+
if (!imp && isJsTsLanguage(sup.id)) {
|
|
576
|
+
imp = innermostNamespaceImport(mod.imports, exprName, expr);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
39
579
|
if (imp) {
|
|
40
580
|
if (imp.kind === "namespace") {
|
|
581
|
+
if (imp.mechanism === "cjs" && typeof imp.resolved === "string") {
|
|
582
|
+
const classValue = cjsRequireValueBinding(index, imp.resolved);
|
|
583
|
+
if (classValue)
|
|
584
|
+
return { kind: "resolved", def: classValue };
|
|
585
|
+
}
|
|
41
586
|
return {
|
|
42
587
|
kind: "namespace",
|
|
43
588
|
file: typeof imp.resolved === "string" ? imp.resolved.replace(/\\/g, "/") : imp.resolved?.external || "",
|
|
44
589
|
};
|
|
45
590
|
}
|
|
591
|
+
if (sup.id === "php" && imp.kind === "named" && typeof imp.resolved === "string") {
|
|
592
|
+
const result = resolvePhpExportByImportType(index, imp.resolved, imp.imported, "class");
|
|
593
|
+
if (result)
|
|
594
|
+
return result;
|
|
595
|
+
}
|
|
596
|
+
if (sup.id === "rust") {
|
|
597
|
+
const moduleFile = resolveRustModuleNamespace(index, imp);
|
|
598
|
+
if (moduleFile)
|
|
599
|
+
return { kind: "namespace", file: moduleFile };
|
|
600
|
+
}
|
|
46
601
|
const result = resolveImported(index, imp, imp.kind === "named" ? imp.imported : "default");
|
|
47
602
|
if (result) {
|
|
48
603
|
if ("namespace" in result) {
|
|
49
604
|
return { kind: "namespace", file: result.namespace };
|
|
50
605
|
}
|
|
51
|
-
|
|
606
|
+
const container = asMemberContainer(index, result);
|
|
607
|
+
return { kind: "resolved", def: container ?? result };
|
|
52
608
|
}
|
|
609
|
+
// Last-wins precedence (Python): the explicit import rebound the name, and an
|
|
610
|
+
// unresolved winner stays unresolved instead of falling back to an older star import.
|
|
611
|
+
if (starImportPrecedence(sup.id) === "last-wins")
|
|
612
|
+
return null;
|
|
53
613
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return
|
|
614
|
+
if (sup.id === "csharp") {
|
|
615
|
+
const local = resolveExport(index, mod.file, exprName, { referenceIndex: expr.startIndex });
|
|
616
|
+
if (local)
|
|
617
|
+
return local;
|
|
618
|
+
}
|
|
619
|
+
else {
|
|
620
|
+
const local = mod.locals.find((candidate) => {
|
|
621
|
+
if (candidate.localName === exprName)
|
|
622
|
+
return true;
|
|
623
|
+
return (sup.id === "php" &&
|
|
624
|
+
declaresMembers(candidate) &&
|
|
625
|
+
foldPhpIdentifierCase(candidate.localName) === foldPhpIdentifierCase(exprName));
|
|
626
|
+
});
|
|
627
|
+
if (local)
|
|
628
|
+
return { kind: "resolved", def: local };
|
|
629
|
+
if (sup.id === "java" || sup.id === "kotlin" || sup.id === "swift") {
|
|
630
|
+
const peer = resolveExport(index, mod.file, exprName);
|
|
631
|
+
if (peer)
|
|
632
|
+
return peer;
|
|
633
|
+
}
|
|
634
|
+
else if (sup.id === "php") {
|
|
635
|
+
const phpClass = resolveIndexedPhpClassReference(index, source, tree, expr, exprName, mod.imports);
|
|
636
|
+
if (phpClass)
|
|
637
|
+
return { kind: "resolved", def: phpClass };
|
|
64
638
|
}
|
|
65
639
|
}
|
|
640
|
+
const starDecision = resolveStarImportedName(index, mod, sup.id, exprName);
|
|
641
|
+
if (starDecision.status === "resolved")
|
|
642
|
+
return { kind: "resolved", def: starDecision.definition };
|
|
643
|
+
if (starDecision.status === "ambiguous")
|
|
644
|
+
return null;
|
|
645
|
+
const starNamespaceDecision = resolveStarImportedNamespace(index, mod, sup.id, exprName);
|
|
646
|
+
if (starNamespaceDecision.status === "resolved") {
|
|
647
|
+
return { kind: "namespace", file: starNamespaceDecision.namespace };
|
|
648
|
+
}
|
|
649
|
+
if (starNamespaceDecision.status === "ambiguous")
|
|
650
|
+
return null;
|
|
66
651
|
return null;
|
|
67
652
|
}
|
|
68
653
|
if (optionalMemberTypes.has(expr.type)) {
|
|
654
|
+
// A Python unaliased dotted import (`import a.b`) binds only the first segment `a`,
|
|
655
|
+
// resolved to the leaf module `a.b` names; the source can only ever repeat that whole
|
|
656
|
+
// dotted phrase to reach it again (`a.b.symbol(...)`), never a bare intermediate segment
|
|
657
|
+
// on its own. Recognize that literal phrase up front so the chain walk below lands on the
|
|
658
|
+
// leaf module the import machinery already resolved, instead of treating each dot as an
|
|
659
|
+
// ordinary member-access hop and failing on the segment duplicating the import's spelling.
|
|
660
|
+
if (sup.id === "python") {
|
|
661
|
+
const dottedText = sliceText(expr, source);
|
|
662
|
+
if (dottedText.includes(".")) {
|
|
663
|
+
const dottedImport = mod.imports.find((imp) => imp.kind === "namespace" &&
|
|
664
|
+
imp.mechanism === "python" &&
|
|
665
|
+
imp.from === dottedText &&
|
|
666
|
+
typeof imp.resolved === "string");
|
|
667
|
+
if (dottedImport && typeof dottedImport.resolved === "string") {
|
|
668
|
+
return { kind: "namespace", file: dottedImport.resolved.replace(/\\/g, "/") };
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
69
672
|
const parts = getMemberAccessParts(sup, expr);
|
|
70
673
|
const subObj = parts.object;
|
|
71
674
|
let subProp = parts.property;
|
|
@@ -74,16 +677,48 @@ export async function resolveMemberAccessDefinition(params) {
|
|
|
74
677
|
}
|
|
75
678
|
if (subObj && subProp) {
|
|
76
679
|
const base = await resolveExpression(subObj);
|
|
680
|
+
if (!base) {
|
|
681
|
+
const qualified = resolveJvmPackageExpression(index, sup, source, expr, resolveLexicalBinding, mod.file);
|
|
682
|
+
if (qualified)
|
|
683
|
+
return qualified;
|
|
684
|
+
}
|
|
77
685
|
const memberName = sliceText(subProp, source);
|
|
686
|
+
// `P.Mix` in `P.Mix.M()`: when the leftmost name binds nothing, a C# dotted name is a
|
|
687
|
+
// namespace-qualified type, which qualified export lookup resolves across every file that
|
|
688
|
+
// declares the namespace.
|
|
689
|
+
const dottedRoot = !base && sup.id === "csharp" ? csharpDottedNameRoot(expr) : null;
|
|
690
|
+
if (dottedRoot && !resolveLexicalBinding?.(dottedRoot)) {
|
|
691
|
+
return resolveCsharpQualifiedName(index, mod, sliceText(expr, source).replace(/\s+/gu, ""), expr.startIndex);
|
|
692
|
+
}
|
|
78
693
|
if (base?.kind === "namespace") {
|
|
694
|
+
if (!isGoExportedMemberName(sup.id, memberName))
|
|
695
|
+
return null;
|
|
79
696
|
return resolveExport(index, base.file, memberName, { allowLocalFallback: false });
|
|
80
697
|
}
|
|
81
698
|
if (base?.kind === "resolved") {
|
|
82
|
-
if (sup.id === "
|
|
83
|
-
const
|
|
699
|
+
if (sup.id === "go") {
|
|
700
|
+
const receiver = await resolveReceiverDefinition(index, subObj, source, sup, resolveExpression, mod, undefined, base);
|
|
701
|
+
if (receiver && declaresMembers(receiver.def)) {
|
|
702
|
+
const context = await ensureParsedContext(receiver.def.file, undefined, index.languageExtensions);
|
|
703
|
+
const embedded = goEmbeddedTypeNames(receiver.def.localName, context, context.sup.normalizeIdentifier);
|
|
704
|
+
if (embedded.includes(memberName)) {
|
|
705
|
+
const ownerModule = index.byFile.get(fileIdentityKey(receiver.def.file));
|
|
706
|
+
const named = ownerModule
|
|
707
|
+
? resolveNamedDefinition(index, ownerModule, ownerModule.file, context.sup, memberName)
|
|
708
|
+
: null;
|
|
709
|
+
if (named?.status === "ok") {
|
|
710
|
+
const type = asMemberContainer(index, named.definition);
|
|
711
|
+
if (type)
|
|
712
|
+
return { kind: "resolved", def: type };
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
if (sup.id === "java" || sup.id === "csharp") {
|
|
718
|
+
const memberDef = await resolveMemberDefinitionForBase(index, base.def, memberName, callArgumentCountFor(expr));
|
|
84
719
|
return memberDef ? { kind: "resolved", def: memberDef } : null;
|
|
85
720
|
}
|
|
86
|
-
if (sup.id === "ruby") {
|
|
721
|
+
if (sup.id === "ruby" && declaresMembers(base.def)) {
|
|
87
722
|
const memberDef = await resolveMemberDefinitionForBase(index, base.def, memberName);
|
|
88
723
|
if (memberDef)
|
|
89
724
|
return { kind: "resolved", def: memberDef };
|
|
@@ -100,18 +735,39 @@ export async function resolveMemberAccessDefinition(params) {
|
|
|
100
735
|
const subProp = expr.childForFieldName("name") ?? expr.child(2);
|
|
101
736
|
if (subObj && subProp) {
|
|
102
737
|
const base = await resolveExpression(subObj);
|
|
738
|
+
if (!base) {
|
|
739
|
+
const qualified = resolveJvmPackageExpression(index, sup, source, expr, resolveLexicalBinding, mod.file);
|
|
740
|
+
if (qualified)
|
|
741
|
+
return qualified;
|
|
742
|
+
}
|
|
103
743
|
const memberName = sliceText(subProp, source);
|
|
104
744
|
if (base?.kind === "namespace") {
|
|
105
745
|
return resolveExport(index, base.file, memberName, { allowLocalFallback: false });
|
|
106
746
|
}
|
|
107
747
|
if (base?.kind === "resolved") {
|
|
108
|
-
const memberDef = await resolveMemberDefinitionForBase(index, base.def, memberName);
|
|
748
|
+
const memberDef = await resolveMemberDefinitionForBase(index, base.def, memberName, callArgumentCountFor(expr));
|
|
109
749
|
return memberDef ? { kind: "resolved", def: memberDef } : null;
|
|
110
750
|
}
|
|
111
751
|
}
|
|
112
752
|
}
|
|
113
753
|
return null;
|
|
114
754
|
};
|
|
755
|
+
const receiverHasLexicalBinding = sup.id === "python"
|
|
756
|
+
? (callee) => {
|
|
757
|
+
const scope = getOrBuildScopeIndex(index, mod.file, source, sup, mod, tree);
|
|
758
|
+
return !!findClosestScopeBinding(scope, sliceText(callee, source), callee, sup);
|
|
759
|
+
}
|
|
760
|
+
: noLexicalBinding;
|
|
761
|
+
const resolvedReceiver = obj && prop && node.id === prop.id ? await resolveExpression(obj) : null;
|
|
762
|
+
const receiverProof = obj && prop && node.id === prop.id
|
|
763
|
+
? classifyReceiver(sup, obj, source, null, 0, memberNode, receiverHasLexicalBinding, () => {
|
|
764
|
+
if (resolvedReceiver?.kind === "namespace")
|
|
765
|
+
return "module-import";
|
|
766
|
+
if (resolvedReceiver?.kind === "resolved" && declaresMembers(resolvedReceiver.def))
|
|
767
|
+
return "static-type";
|
|
768
|
+
return null;
|
|
769
|
+
})
|
|
770
|
+
: null;
|
|
115
771
|
const chain = await resolveExpression(memberNode);
|
|
116
772
|
if (chain && prop && node.id === prop.id) {
|
|
117
773
|
if (chain.kind === "resolved") {
|
|
@@ -133,12 +789,10 @@ export async function resolveMemberAccessDefinition(params) {
|
|
|
133
789
|
}
|
|
134
790
|
}
|
|
135
791
|
}
|
|
136
|
-
|
|
137
|
-
const implicitClassReceiver = isKeywordReceiver(sup.id, receiverName);
|
|
138
|
-
if (obj && prop && node.id === prop.id && (supportsReceiverMemberNavigation(sup.id) || implicitClassReceiver)) {
|
|
792
|
+
if (obj && prop && node.id === prop.id && supportsReceiverMemberNavigation(sup.id)) {
|
|
139
793
|
const member = sliceText(prop, source);
|
|
140
794
|
if (sup.id === "python") {
|
|
141
|
-
const memberDef = await resolvePythonReceiverMember(index, mod, node, obj, member, source, sup, resolveExpression);
|
|
795
|
+
const memberDef = await resolvePythonReceiverMember(index, mod, node, obj, member, source, sup, resolveExpression, receiverProof);
|
|
142
796
|
if (!memberDef)
|
|
143
797
|
return null;
|
|
144
798
|
return okGoToResult(index, memberDef, {
|
|
@@ -147,11 +801,9 @@ export async function resolveMemberAccessDefinition(params) {
|
|
|
147
801
|
confidence: "medium",
|
|
148
802
|
});
|
|
149
803
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const memberDef =
|
|
153
|
-
? findLocalWithinNode(mod.locals, member, classContainer, sup.normalizeIdentifier)
|
|
154
|
-
: undefined;
|
|
804
|
+
const keywordScope = receiverProof?.kind === "own-type" || receiverProof?.kind === "supertype" ? receiverProof.memberScope : "any";
|
|
805
|
+
if (receiverProof?.kind === "own-type") {
|
|
806
|
+
const memberDef = await resolveKeywordReceiverMember(index, mod, node, member, keywordScope, false, getCallArgumentCount({ languageId: sup.id, source, call: memberNode.parent ?? memberNode }) ?? undefined);
|
|
155
807
|
if (memberDef) {
|
|
156
808
|
return okGoToResult(index, memberDef, {
|
|
157
809
|
via: { exportedName: member },
|
|
@@ -159,8 +811,74 @@ export async function resolveMemberAccessDefinition(params) {
|
|
|
159
811
|
confidence: "medium",
|
|
160
812
|
});
|
|
161
813
|
}
|
|
814
|
+
const outOfLineOwnerPath = cppOutOfLineOwnerPath(node, source, sup);
|
|
815
|
+
const outOfLineOwner = outOfLineOwnerPath
|
|
816
|
+
? await resolveCppQualifiedMemberContainer(index, mod, outOfLineOwnerPath)
|
|
817
|
+
: null;
|
|
818
|
+
if (outOfLineOwner) {
|
|
819
|
+
const outOfLineMember = await resolveKeywordReceiverMember(index, mod, node, member, keywordScope, false, getCallArgumentCount({ languageId: sup.id, source, call: memberNode.parent ?? memberNode }) ?? undefined, outOfLineOwner);
|
|
820
|
+
if (outOfLineMember) {
|
|
821
|
+
return okGoToResult(index, outOfLineMember, {
|
|
822
|
+
resolution: "exact",
|
|
823
|
+
confidence: "high",
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
// A failed `self` lookup is not a license to bind an unrelated same-file
|
|
828
|
+
// extension member by its bare name (including an unproven where clause).
|
|
829
|
+
if (sup.id === "swift")
|
|
830
|
+
return { status: "not_found", reason: "No matching Swift member definition" };
|
|
831
|
+
}
|
|
832
|
+
else if (receiverProof?.kind === "supertype") {
|
|
833
|
+
const memberDef = await resolveKeywordReceiverMember(index, mod, node, member, keywordScope, true, getCallArgumentCount({ languageId: sup.id, source, call: memberNode.parent ?? memberNode }) ?? undefined);
|
|
834
|
+
if (!memberDef)
|
|
835
|
+
return { status: "not_found", reason: "No matching supertype member definition" };
|
|
836
|
+
return okGoToResult(index, memberDef, {
|
|
837
|
+
via: { exportedName: member },
|
|
838
|
+
resolution: "member-access",
|
|
839
|
+
confidence: "medium",
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
if (sup.id === "cpp" && memberNode.type === "qualified_identifier") {
|
|
843
|
+
// `a::C::run` nests `C::run` inside `a::(...)`; only the outermost node carries the full owner.
|
|
844
|
+
let outermost = memberNode;
|
|
845
|
+
while (outermost.parent?.type === "qualified_identifier")
|
|
846
|
+
outermost = outermost.parent;
|
|
847
|
+
// The declarator of an out-of-line definition (`int Box<T>::f() {}`) names the declaration
|
|
848
|
+
// itself, not a member call; the declaration lookup below resolves it.
|
|
849
|
+
if (outermost.parent?.type === "function_declarator")
|
|
850
|
+
return null;
|
|
851
|
+
const qualified = cppQualifiedNameSegments(outermost, source);
|
|
852
|
+
const ownerPath = qualified.slice(0, -1);
|
|
853
|
+
const owner = await resolveCppQualifiedMemberContainer(index, mod, ownerPath);
|
|
854
|
+
if (owner) {
|
|
855
|
+
const knownArgumentCount = getCallArgumentCount({ languageId: sup.id, source, call: memberNode.parent ?? memberNode }) ?? undefined;
|
|
856
|
+
// Inside a member function, `Owner::member()` can name an instance member of the class or a
|
|
857
|
+
// base through the implicit `this`; elsewhere only static members qualify.
|
|
858
|
+
const implicitThis = await cppQualifiedOwnerHasImplicitThis(index, mod, memberNode, source, sup, owner);
|
|
859
|
+
const memberScope = implicitThis ? "any" : "static";
|
|
860
|
+
const memberDef = await resolveKeywordReceiverMember(index, mod, node, member, memberScope, false, knownArgumentCount, owner);
|
|
861
|
+
return memberDef
|
|
862
|
+
? okGoToResult(index, memberDef, { resolution: "member-access", confidence: "high" })
|
|
863
|
+
: { status: "not_found", reason: "No matching C++ static member definition" };
|
|
864
|
+
}
|
|
865
|
+
// A qualifier that is not a class (a namespace) names a free function, which the qualified
|
|
866
|
+
// function lookup resolves; the short name must not be read as a same-named class receiver.
|
|
867
|
+
return null;
|
|
162
868
|
}
|
|
163
|
-
const
|
|
869
|
+
const importType = isJsTsLanguage(sup.id) && receiverProof?.kind === "named-type" ? receiverProof.typeNode : null;
|
|
870
|
+
const importSpecifier = importType?.type === "type_query" ? importTypeQuerySpecifier(importType) : null;
|
|
871
|
+
if (importSpecifier) {
|
|
872
|
+
const memberDef = resolveImportTypeMember(index, mod.file, importSpecifier, member);
|
|
873
|
+
return memberDef
|
|
874
|
+
? okGoToResult(index, memberDef, {
|
|
875
|
+
via: { exportedName: member },
|
|
876
|
+
resolution: "member-access",
|
|
877
|
+
confidence: "medium",
|
|
878
|
+
})
|
|
879
|
+
: null;
|
|
880
|
+
}
|
|
881
|
+
const receiver = await resolveReceiverDefinition(index, obj, source, sup, resolveExpression, mod, receiverProof, resolvedReceiver);
|
|
164
882
|
if (receiver) {
|
|
165
883
|
const objDef = receiver.def;
|
|
166
884
|
const targetContext = await ensureParsedContext(objDef.file, undefined, index.languageExtensions);
|
|
@@ -185,14 +903,50 @@ export async function resolveMemberAccessDefinition(params) {
|
|
|
185
903
|
const memberPredicate = receiver.memberScope === "any"
|
|
186
904
|
? undefined
|
|
187
905
|
: (local) => matchesReceiverMemberScope(local, receiver.memberScope, targetContext, container);
|
|
906
|
+
const knownArgumentCount = getCallArgumentCount({ languageId: sup.id, source, call: memberNode.parent ?? memberNode }) ?? undefined;
|
|
188
907
|
let memberDef;
|
|
189
|
-
|
|
190
|
-
|
|
908
|
+
const hierarchyLanguage = isJsTsLanguage(targetContext.sup.id) ||
|
|
909
|
+
targetContext.sup.id === "ruby" ||
|
|
910
|
+
targetContext.sup.id === "cpp" ||
|
|
911
|
+
targetContext.sup.id === "csharp" ||
|
|
912
|
+
targetContext.sup.id === "java" ||
|
|
913
|
+
targetContext.sup.id === "kotlin" ||
|
|
914
|
+
targetContext.sup.id === "php" ||
|
|
915
|
+
targetContext.sup.id === "swift";
|
|
916
|
+
if (hierarchyLanguage && declaresMembers(objDef) && !receiver.runtimeTypeOnly) {
|
|
917
|
+
const report = { named: false, ambiguous: false };
|
|
918
|
+
memberDef = await resolveKeywordReceiverMember(index, mod, node, member, receiver.memberScope, false, knownArgumentCount, objDef, report);
|
|
919
|
+
if (!memberDef && report.named)
|
|
920
|
+
return { status: "not_found", reason: "No unique receiver member" };
|
|
921
|
+
}
|
|
922
|
+
if (!memberDef &&
|
|
923
|
+
(targetContext.sup.id === "ts" || targetContext.sup.id === "tsx") &&
|
|
924
|
+
(container.type === "internal_module" || container.type === "module")) {
|
|
925
|
+
memberDef = await findReceiverMemberDefinition(index, targetModule.locals, member, objDef, container, targetContext, normalizeIdentifier, receiver.memberScope, knownArgumentCount);
|
|
191
926
|
}
|
|
192
|
-
else {
|
|
193
|
-
|
|
927
|
+
else if (!memberDef && (receiver.runtimeTypeOnly || targetContext.sup.id === "java")) {
|
|
928
|
+
const candidates = findDirectLocalsWithinNode(targetModule.locals, member, container, targetContext, normalizeIdentifier, memberPredicate);
|
|
929
|
+
const selected = selectMember([objDef], indexedMemberModel(index, candidates, targetContext.sup.id), {
|
|
930
|
+
name: member,
|
|
931
|
+
argumentCount: knownArgumentCount ?? null,
|
|
932
|
+
scope: "any",
|
|
933
|
+
useFile: mod.file,
|
|
934
|
+
phpCaseInsensitive: targetContext.sup.id === "php",
|
|
935
|
+
keepUniqueArityMismatch: true,
|
|
936
|
+
});
|
|
937
|
+
memberDef = selected.status === "unique" ? selected.member : undefined;
|
|
938
|
+
}
|
|
939
|
+
else if (!memberDef) {
|
|
940
|
+
memberDef = await findReceiverMemberDefinition(index, targetModule.locals, member, objDef, container, targetContext, normalizeIdentifier, receiver.memberScope, knownArgumentCount);
|
|
194
941
|
}
|
|
195
|
-
|
|
942
|
+
const memberContext = memberDef && fileIdentityKey(memberDef.file) !== fileIdentityKey(objDef.file)
|
|
943
|
+
? await ensureParsedContext(memberDef.file, undefined, index.languageExtensions)
|
|
944
|
+
: targetContext;
|
|
945
|
+
if (memberDef &&
|
|
946
|
+
(sup.id === "cpp" ||
|
|
947
|
+
isGoExportedMemberName(sup.id, member) ||
|
|
948
|
+
getCompilationUnitPeers(index, mod.file).files.has(memberDef.file)) &&
|
|
949
|
+
!crossFilePeerMemberHidden(mod.file, memberDef, memberContext)) {
|
|
196
950
|
return okGoToResult(index, memberDef, {
|
|
197
951
|
via: { exportedName: member },
|
|
198
952
|
resolution: "member-access",
|
|
@@ -205,17 +959,1077 @@ export async function resolveMemberAccessDefinition(params) {
|
|
|
205
959
|
}
|
|
206
960
|
return null;
|
|
207
961
|
}
|
|
208
|
-
function findEnclosingClassContainer(node) {
|
|
209
|
-
return nearestMemberContainer(node);
|
|
962
|
+
function findEnclosingClassContainer(node) {
|
|
963
|
+
return nearestMemberContainer(node);
|
|
964
|
+
}
|
|
965
|
+
function keywordClassKey(def) {
|
|
966
|
+
const start = def.range.start;
|
|
967
|
+
return `${fileIdentityKey(def.file)}:${start.index ?? `${start.line}:${start.column}`}`;
|
|
968
|
+
}
|
|
969
|
+
function keywordContainerKey(file, container) {
|
|
970
|
+
return `${fileIdentityKey(file)}:${container.startIndex}:${container.endIndex}`;
|
|
971
|
+
}
|
|
972
|
+
function peelTypeWrappers(node) {
|
|
973
|
+
let current = node;
|
|
974
|
+
while (current) {
|
|
975
|
+
if (current.type === "type_annotation" ||
|
|
976
|
+
current.type === "named_type" ||
|
|
977
|
+
current.type === "user_type" ||
|
|
978
|
+
current.type === "type" ||
|
|
979
|
+
current.type === "parenthesized_type" ||
|
|
980
|
+
current.type === "pointer_type" ||
|
|
981
|
+
current.type === "reference_type" ||
|
|
982
|
+
current.type === "optional_type" ||
|
|
983
|
+
current.type === "nullable_type" ||
|
|
984
|
+
current.type === "generic_type" ||
|
|
985
|
+
current.type === "generic_name") {
|
|
986
|
+
current = current.childForFieldName("type") ?? current.namedChildren[0] ?? null;
|
|
987
|
+
continue;
|
|
988
|
+
}
|
|
989
|
+
break;
|
|
990
|
+
}
|
|
991
|
+
return current ?? node;
|
|
992
|
+
}
|
|
993
|
+
function isSimpleTypeNameNode(node, sup) {
|
|
994
|
+
return isReceiverNameNode(sup, node.type) || node.type === "type_identifier" || node.type === "name";
|
|
995
|
+
}
|
|
996
|
+
function parseCppBaseType(node, source) {
|
|
997
|
+
let current = node;
|
|
998
|
+
while (current.type === "base_class_clause" ||
|
|
999
|
+
current.type === "access_specifier" ||
|
|
1000
|
+
current.type === "virtual_specifier" ||
|
|
1001
|
+
current.type === "type_descriptor") {
|
|
1002
|
+
const nested = current.namedChildren.at(-1);
|
|
1003
|
+
if (!nested || nested.id === current.id)
|
|
1004
|
+
break;
|
|
1005
|
+
current = nested;
|
|
1006
|
+
}
|
|
1007
|
+
const names = cppQualifiedNameSegments(current, source);
|
|
1008
|
+
return names.length ? { base: names[0], path: names.slice(1) } : null;
|
|
1009
|
+
}
|
|
1010
|
+
function collectDeclaredBaseTypes(container, source, sup, superclassOnly = false) {
|
|
1011
|
+
const ancestry = MEMBER_ACCESS_ROWS[sup.id]?.receiverAncestry;
|
|
1012
|
+
if (!ancestry || (ancestry.clauses.length === 0 && !ancestry.mixinCalls?.length))
|
|
1013
|
+
return [];
|
|
1014
|
+
const bases = [];
|
|
1015
|
+
const seen = new Set();
|
|
1016
|
+
let usedFirstMatch = false;
|
|
1017
|
+
const addSimple = (name, invoked) => {
|
|
1018
|
+
if (!name || seen.has(name))
|
|
1019
|
+
return;
|
|
1020
|
+
seen.add(name);
|
|
1021
|
+
bases.push(invoked ? { kind: "simple", name, invoked } : { kind: "simple", name });
|
|
1022
|
+
};
|
|
1023
|
+
const addQualified = (base, path, invoked) => {
|
|
1024
|
+
if (!base || path.length === 0)
|
|
1025
|
+
return;
|
|
1026
|
+
const key = `${base}.${path.join(".")}`;
|
|
1027
|
+
if (seen.has(key))
|
|
1028
|
+
return;
|
|
1029
|
+
seen.add(key);
|
|
1030
|
+
bases.push(invoked ? { kind: "qualified", base, path, invoked } : { kind: "qualified", base, path });
|
|
1031
|
+
};
|
|
1032
|
+
const collectType = (node, invoked) => {
|
|
1033
|
+
const core = peelTypeWrappers(node);
|
|
1034
|
+
if (core.type === "constructor_invocation") {
|
|
1035
|
+
// Kotlin writes the superclass as a constructor invocation (`Base()`); its callees are
|
|
1036
|
+
// superclass entries, while bare types in the same base list are interfaces.
|
|
1037
|
+
for (const child of core.namedChildren) {
|
|
1038
|
+
if (child.type === "value_arguments")
|
|
1039
|
+
continue;
|
|
1040
|
+
collectType(child, true);
|
|
1041
|
+
}
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
// A computed heritage expression can name the runtime base factory rather than a class.
|
|
1045
|
+
// Its descendants do not prove an inheritance edge.
|
|
1046
|
+
if (isUnprovenHeritageExpression(core))
|
|
1047
|
+
return;
|
|
1048
|
+
if (sup.id === "cpp" && core.type === "base_class_clause") {
|
|
1049
|
+
for (const child of core.namedChildren) {
|
|
1050
|
+
if (child.type === "access_specifier" || child.type === "virtual_specifier")
|
|
1051
|
+
continue;
|
|
1052
|
+
collectType(child, invoked);
|
|
1053
|
+
}
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
if (sup.id === "cpp") {
|
|
1057
|
+
const qualified = parseCppBaseType(core, source);
|
|
1058
|
+
if (qualified) {
|
|
1059
|
+
if (qualified.path.length)
|
|
1060
|
+
addQualified(qualified.base, qualified.path, invoked);
|
|
1061
|
+
else
|
|
1062
|
+
addSimple(qualified.base, invoked);
|
|
1063
|
+
return;
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
const unwrapped = unwrapNamedType(core, sup);
|
|
1067
|
+
if (unwrapped) {
|
|
1068
|
+
addSimple(sliceText(unwrapped, source), invoked);
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
if (isMemberAccessNode(sup, core)) {
|
|
1072
|
+
const chain = collectMemberAccessChain({ sup, source, chainNode: core });
|
|
1073
|
+
if (chain && isSimpleTypeNameNode(chain.base, sup) && chain.names.length) {
|
|
1074
|
+
addQualified(sliceText(chain.base, source), [...chain.names].reverse(), invoked);
|
|
1075
|
+
}
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
if (memberAccessTraversalTypes(sup).has(core.type))
|
|
1079
|
+
return;
|
|
1080
|
+
for (const child of core.namedChildren) {
|
|
1081
|
+
if (child.type === "type_arguments")
|
|
1082
|
+
continue;
|
|
1083
|
+
collectType(child, invoked);
|
|
1084
|
+
}
|
|
1085
|
+
};
|
|
1086
|
+
const consider = (node) => {
|
|
1087
|
+
for (const rule of ancestry.clauses) {
|
|
1088
|
+
if (node.type !== rule.nodeType)
|
|
1089
|
+
continue;
|
|
1090
|
+
if (superclassOnly && !rule.supertype)
|
|
1091
|
+
continue;
|
|
1092
|
+
if (superclassOnly && rule.supertype === "first-match") {
|
|
1093
|
+
if (usedFirstMatch)
|
|
1094
|
+
continue;
|
|
1095
|
+
usedFirstMatch = true;
|
|
1096
|
+
}
|
|
1097
|
+
let target = node;
|
|
1098
|
+
if (superclassOnly && rule.supertype === "first-child") {
|
|
1099
|
+
target = target.namedChildren[0] ?? target;
|
|
1100
|
+
}
|
|
1101
|
+
collectType(target, false);
|
|
1102
|
+
}
|
|
1103
|
+
if (superclassOnly || node.type !== "call" || !ancestry.mixinCalls?.length)
|
|
1104
|
+
return;
|
|
1105
|
+
if (node.childForFieldName("receiver"))
|
|
1106
|
+
return;
|
|
1107
|
+
const methodNode = node.childForFieldName("method");
|
|
1108
|
+
const methodName = methodNode ? sliceText(methodNode, source) : undefined;
|
|
1109
|
+
if (!methodName || !ancestry.mixinCalls.includes(methodName))
|
|
1110
|
+
return;
|
|
1111
|
+
const args = node.childForFieldName("arguments");
|
|
1112
|
+
if (args)
|
|
1113
|
+
collectType(args, false);
|
|
1114
|
+
};
|
|
1115
|
+
for (const child of container.namedChildren) {
|
|
1116
|
+
consider(child);
|
|
1117
|
+
for (const grand of child.namedChildren)
|
|
1118
|
+
consider(grand);
|
|
1119
|
+
}
|
|
1120
|
+
return bases;
|
|
1121
|
+
}
|
|
1122
|
+
function asMemberContainer(index, def) {
|
|
1123
|
+
return memberContainerForDefinition(index, def);
|
|
1124
|
+
}
|
|
1125
|
+
function importedMemberContainer(index, result) {
|
|
1126
|
+
if (!result || "namespace" in result)
|
|
1127
|
+
return undefined;
|
|
1128
|
+
return asMemberContainer(index, result);
|
|
1129
|
+
}
|
|
1130
|
+
function resolveNamedMemberContainer(index, mod, name, normalize) {
|
|
1131
|
+
const typedLocals = memberDeclaringLocals(mod, name, normalize);
|
|
1132
|
+
const topLevel = typedLocals.filter((local) => !local.isMember);
|
|
1133
|
+
const candidates = topLevel.length ? topLevel : typedLocals;
|
|
1134
|
+
if (candidates.length === 1)
|
|
1135
|
+
return candidates[0];
|
|
1136
|
+
if (candidates.length > 1)
|
|
1137
|
+
return undefined;
|
|
1138
|
+
const normalizedName = normalize(name);
|
|
1139
|
+
const languageId = supportForFileWithoutHeaderSample(mod.file, index.languageExtensions)?.id;
|
|
1140
|
+
const explicitBinding = effectiveExplicitBinding(mod.imports, languageId ?? "", (binding) => (binding.kind === "named" || binding.kind === "default") && normalize(binding.local) === normalizedName);
|
|
1141
|
+
if (explicitBinding?.kind === "named") {
|
|
1142
|
+
const container = importedMemberContainer(index, resolveImported(index, explicitBinding, explicitBinding.imported));
|
|
1143
|
+
if (container)
|
|
1144
|
+
return container;
|
|
1145
|
+
}
|
|
1146
|
+
else if (explicitBinding?.kind === "default") {
|
|
1147
|
+
const container = importedMemberContainer(index, resolveImported(index, explicitBinding, "default"));
|
|
1148
|
+
if (container)
|
|
1149
|
+
return container;
|
|
1150
|
+
}
|
|
1151
|
+
if (languageId) {
|
|
1152
|
+
const starDecision = resolveStarImportedName(index, mod, languageId, name);
|
|
1153
|
+
if (starDecision.status === "ambiguous")
|
|
1154
|
+
return undefined;
|
|
1155
|
+
if (starDecision.status === "resolved") {
|
|
1156
|
+
const container = asMemberContainer(index, starDecision.definition);
|
|
1157
|
+
if (container)
|
|
1158
|
+
return container;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
const exported = resolveExport(index, mod.file, name, { allowLocalFallback: false });
|
|
1162
|
+
if (exported?.kind === "resolved")
|
|
1163
|
+
return asMemberContainer(index, exported.def);
|
|
1164
|
+
return undefined;
|
|
1165
|
+
}
|
|
1166
|
+
async function resolveQualifiedMemberContainer(index, mod, baseName, path, normalize, sup) {
|
|
1167
|
+
if (path.length === 0)
|
|
1168
|
+
return undefined;
|
|
1169
|
+
if (sup.id === "cpp") {
|
|
1170
|
+
return (await resolveCppQualifiedMemberContainer(index, mod, [baseName, ...path])) ?? undefined;
|
|
1171
|
+
}
|
|
1172
|
+
const namespaceImports = mod.imports.filter((imp) => imp.kind === "namespace" && normalize(imp.localNS) === normalize(baseName));
|
|
1173
|
+
if (namespaceImports.length !== 1)
|
|
1174
|
+
return undefined;
|
|
1175
|
+
const resolved = namespaceImports[0].resolved;
|
|
1176
|
+
let file = typeof resolved === "string" ? resolved : undefined;
|
|
1177
|
+
if (!file)
|
|
1178
|
+
return undefined;
|
|
1179
|
+
for (let partIndex = 0; partIndex < path.length; partIndex += 1) {
|
|
1180
|
+
const part = path[partIndex];
|
|
1181
|
+
const last = partIndex === path.length - 1;
|
|
1182
|
+
const hit = resolveExport(index, file, part, { allowLocalFallback: false });
|
|
1183
|
+
if (!hit)
|
|
1184
|
+
return undefined;
|
|
1185
|
+
if (hit.kind === "namespace") {
|
|
1186
|
+
if (last)
|
|
1187
|
+
return undefined;
|
|
1188
|
+
file = hit.file;
|
|
1189
|
+
continue;
|
|
1190
|
+
}
|
|
1191
|
+
if (!last)
|
|
1192
|
+
return undefined;
|
|
1193
|
+
return asMemberContainer(index, hit.def);
|
|
1194
|
+
}
|
|
1195
|
+
return undefined;
|
|
1196
|
+
}
|
|
1197
|
+
function nodeContainsUnprovenHeritage(node) {
|
|
1198
|
+
if (isUnprovenHeritageExpression(node))
|
|
1199
|
+
return true;
|
|
1200
|
+
return node.namedChildren.some((child) => nodeContainsUnprovenHeritage(child));
|
|
1201
|
+
}
|
|
1202
|
+
/** A heritage or mixin clause whose type expression is not a resolvable name. */
|
|
1203
|
+
function containerHeritageIsUnproven(container, source, sup) {
|
|
1204
|
+
const ancestry = MEMBER_ACCESS_ROWS[sup.id]?.receiverAncestry;
|
|
1205
|
+
if (!ancestry)
|
|
1206
|
+
return false;
|
|
1207
|
+
const clauseTypes = new Set(ancestry.clauses.map((rule) => rule.nodeType));
|
|
1208
|
+
const nodes = [];
|
|
1209
|
+
for (const child of container.namedChildren) {
|
|
1210
|
+
nodes.push(child);
|
|
1211
|
+
for (const grand of child.namedChildren)
|
|
1212
|
+
nodes.push(grand);
|
|
1213
|
+
}
|
|
1214
|
+
if (nodes.some((node) => clauseTypes.has(node.type) && nodeContainsUnprovenHeritage(node)))
|
|
1215
|
+
return true;
|
|
1216
|
+
const mixinCalls = ancestry.mixinCalls;
|
|
1217
|
+
if (!mixinCalls?.length)
|
|
1218
|
+
return false;
|
|
1219
|
+
return nodes.some((node) => {
|
|
1220
|
+
if (node.type !== "call" || node.childForFieldName("receiver"))
|
|
1221
|
+
return false;
|
|
1222
|
+
const methodNode = node.childForFieldName("method");
|
|
1223
|
+
const methodName = methodNode ? sliceText(methodNode, source) : undefined;
|
|
1224
|
+
if (!methodName || !mixinCalls.includes(methodName))
|
|
1225
|
+
return false;
|
|
1226
|
+
const args = node.childForFieldName("arguments");
|
|
1227
|
+
return !!args && nodeContainsUnprovenHeritage(args);
|
|
1228
|
+
});
|
|
1229
|
+
}
|
|
1230
|
+
function definitionForContainer(mod, container) {
|
|
1231
|
+
const nameNode = container.childForFieldName("name");
|
|
1232
|
+
if (!nameNode)
|
|
1233
|
+
return undefined;
|
|
1234
|
+
const matches = mod.locals.filter((local) => declaresMembers(local) && local.range.start.index === nameNode.startIndex);
|
|
1235
|
+
return matches.length === 1 ? matches[0] : undefined;
|
|
1236
|
+
}
|
|
1237
|
+
async function resolveReceiverTypeName(index, mod, sup, typeName) {
|
|
1238
|
+
const trimmed = typeName.trim().replace(/^\\+/, "");
|
|
1239
|
+
if (!trimmed)
|
|
1240
|
+
return undefined;
|
|
1241
|
+
const normalize = sup.normalizeIdentifier;
|
|
1242
|
+
for (const separator of ["::", "\\", "."]) {
|
|
1243
|
+
if (!trimmed.includes(separator))
|
|
1244
|
+
continue;
|
|
1245
|
+
const parts = trimmed.split(separator).filter(Boolean);
|
|
1246
|
+
if (parts.length < 2)
|
|
1247
|
+
return undefined;
|
|
1248
|
+
return resolveQualifiedMemberContainer(index, mod, parts[0], parts.slice(1), normalize, sup);
|
|
1249
|
+
}
|
|
1250
|
+
return resolveNamedMemberContainer(index, mod, trimmed, normalize);
|
|
1251
|
+
}
|
|
1252
|
+
async function resolveDeclaredBase(index, mod, sup, base) {
|
|
1253
|
+
const normalize = sup.normalizeIdentifier;
|
|
1254
|
+
if (base.kind === "simple")
|
|
1255
|
+
return resolveNamedMemberContainer(index, mod, base.name, normalize);
|
|
1256
|
+
return resolveQualifiedMemberContainer(index, mod, base.base, base.path, normalize, sup);
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
1259
|
+
* True when `def` and every resolved supertype lack a direct `memberName`. An unresolved
|
|
1260
|
+
* supertype, an unreadable declaration, or a declared member returns false.
|
|
1261
|
+
*/
|
|
1262
|
+
async function hierarchyOmitsMember(index, def, memberName, memberScope, seen) {
|
|
1263
|
+
const containerDef = asMemberContainer(index, def);
|
|
1264
|
+
if (!containerDef)
|
|
1265
|
+
return false;
|
|
1266
|
+
const start = containerDef.range.start;
|
|
1267
|
+
const key = `${fileIdentityKey(containerDef.file)}:${start.index ?? `${start.line}:${start.column}`}`;
|
|
1268
|
+
if (seen.has(key))
|
|
1269
|
+
return true;
|
|
1270
|
+
seen.add(key);
|
|
1271
|
+
const ref = await keywordClassRefFromDef(index, containerDef);
|
|
1272
|
+
if (!ref)
|
|
1273
|
+
return false;
|
|
1274
|
+
const normalize = ref.context.sup.normalizeIdentifier;
|
|
1275
|
+
const memberPredicate = memberScope === "any"
|
|
1276
|
+
? undefined
|
|
1277
|
+
: (local) => matchesReceiverMemberScope(local, memberScope, ref.context, ref.container);
|
|
1278
|
+
if (findDirectLocalsWithinNode(ref.module.locals, memberName, ref.container, ref.context, normalize, memberPredicate)
|
|
1279
|
+
.length > 0) {
|
|
1280
|
+
return false;
|
|
1281
|
+
}
|
|
1282
|
+
if (ref.context.sup.id === "csharp" || ref.context.sup.id === "swift") {
|
|
1283
|
+
const shared = await findSharedOwnerMemberDefinitions({
|
|
1284
|
+
index,
|
|
1285
|
+
ownerFile: containerDef.file,
|
|
1286
|
+
ownerContainer: ref.container,
|
|
1287
|
+
ownerSource: ref.context.source,
|
|
1288
|
+
languageId: ref.context.sup.id,
|
|
1289
|
+
member: memberName,
|
|
1290
|
+
memberScope,
|
|
1291
|
+
});
|
|
1292
|
+
if (shared.length > 0)
|
|
1293
|
+
return false;
|
|
1294
|
+
}
|
|
1295
|
+
if (containerHeritageIsUnproven(ref.container, ref.context.source, ref.context.sup))
|
|
1296
|
+
return false;
|
|
1297
|
+
const bases = collectDeclaredBaseTypes(ref.container, ref.context.source, ref.context.sup, false);
|
|
1298
|
+
for (const base of bases) {
|
|
1299
|
+
const baseDef = await resolveDeclaredBase(index, ref.module, ref.context.sup, base);
|
|
1300
|
+
if (!baseDef)
|
|
1301
|
+
return false;
|
|
1302
|
+
if (!(await hierarchyOmitsMember(index, baseDef, memberName, memberScope, seen)))
|
|
1303
|
+
return false;
|
|
1304
|
+
}
|
|
1305
|
+
return true;
|
|
1306
|
+
}
|
|
1307
|
+
/**
|
|
1308
|
+
* A classified receiver (`new Widget()`, a named type, `this`, or `super`) excludes a failed
|
|
1309
|
+
* member lookup only when its type resolves to a member-declaring definition, no supertype is
|
|
1310
|
+
* unresolved, and that type does not declare `memberName`. Otherwise the site stays unproven.
|
|
1311
|
+
*/
|
|
1312
|
+
export async function provenClassifiedReceiverOmitsMember(index, fileId, parsed, accessNode, objectNode, memberName) {
|
|
1313
|
+
const mod = index.byFile.get(fileIdentityKey(fileId));
|
|
1314
|
+
if (!mod)
|
|
1315
|
+
return false;
|
|
1316
|
+
const receiver = classifyReceiver(parsed.sup, objectNode, parsed.source, new Map(), accessNode.startIndex, accessNode, (callee) => {
|
|
1317
|
+
const scope = getOrBuildScopeIndex(index, fileId, parsed.source, parsed.sup, mod, parsed.tree);
|
|
1318
|
+
return !!findClosestScopeBinding(scope, sliceText(callee, parsed.source), callee, parsed.sup);
|
|
1319
|
+
});
|
|
1320
|
+
if (receiver.kind === "unknown" || receiver.kind === "module-import")
|
|
1321
|
+
return false;
|
|
1322
|
+
if (receiver.kind === "named-type") {
|
|
1323
|
+
const def = await resolveReceiverTypeName(index, mod, parsed.sup, receiver.typeName);
|
|
1324
|
+
if (!def)
|
|
1325
|
+
return false;
|
|
1326
|
+
return hierarchyOmitsMember(index, def, memberName, receiver.memberScope, new Set());
|
|
1327
|
+
}
|
|
1328
|
+
const container = nearestMemberContainer(accessNode);
|
|
1329
|
+
if (!container)
|
|
1330
|
+
return false;
|
|
1331
|
+
if (receiver.kind === "supertype") {
|
|
1332
|
+
if (containerHeritageIsUnproven(container, parsed.source, parsed.sup))
|
|
1333
|
+
return false;
|
|
1334
|
+
const bases = collectDeclaredBaseTypes(container, parsed.source, parsed.sup, true);
|
|
1335
|
+
if (!bases.length)
|
|
1336
|
+
return false;
|
|
1337
|
+
const seen = new Set();
|
|
1338
|
+
for (const base of bases) {
|
|
1339
|
+
const baseDef = await resolveDeclaredBase(index, mod, parsed.sup, base);
|
|
1340
|
+
if (!baseDef)
|
|
1341
|
+
return false;
|
|
1342
|
+
if (!(await hierarchyOmitsMember(index, baseDef, memberName, receiver.memberScope, seen)))
|
|
1343
|
+
return false;
|
|
1344
|
+
}
|
|
1345
|
+
return true;
|
|
1346
|
+
}
|
|
1347
|
+
const def = definitionForContainer(mod, container);
|
|
1348
|
+
if (!def)
|
|
1349
|
+
return false;
|
|
1350
|
+
return hierarchyOmitsMember(index, def, memberName, receiver.memberScope, new Set());
|
|
1351
|
+
}
|
|
1352
|
+
async function keywordClassRefFromDef(index, def) {
|
|
1353
|
+
const module = index.byFile.get(fileIdentityKey(def.file));
|
|
1354
|
+
if (!module)
|
|
1355
|
+
return null;
|
|
1356
|
+
const context = await ensureParsedContext(def.file, undefined, index.languageExtensions);
|
|
1357
|
+
const start = def.range.start;
|
|
1358
|
+
const position = {
|
|
1359
|
+
row: start.line - 1,
|
|
1360
|
+
column: start.column - 1,
|
|
1361
|
+
};
|
|
1362
|
+
const nameNode = context.tree.rootNode.descendantForPosition(position, position);
|
|
1363
|
+
const container = nearestMemberContainer(nameNode);
|
|
1364
|
+
if (!container)
|
|
1365
|
+
return null;
|
|
1366
|
+
return { file: def.file, container, context, module };
|
|
1367
|
+
}
|
|
1368
|
+
async function keywordClassRefFromNode(index, mod, node) {
|
|
1369
|
+
const context = await ensureParsedContext(mod.file, undefined, index.languageExtensions);
|
|
1370
|
+
const memberNode = context.tree.rootNode.descendantForPosition(node.startPosition, node.startPosition);
|
|
1371
|
+
const container = findEnclosingClassContainer(memberNode);
|
|
1372
|
+
if (!container)
|
|
1373
|
+
return null;
|
|
1374
|
+
return { file: mod.file, container, context, module: mod };
|
|
1375
|
+
}
|
|
1376
|
+
function crossFilePeerMemberHidden(useFile, memberDef, targetContext) {
|
|
1377
|
+
const languageId = targetContext.sup.id;
|
|
1378
|
+
if (languageId !== "java" && languageId !== "kotlin" && languageId !== "swift")
|
|
1379
|
+
return false;
|
|
1380
|
+
const start = memberDef.range.start;
|
|
1381
|
+
const position = { row: Math.max(0, start.line - 1), column: Math.max(0, start.column - 1) };
|
|
1382
|
+
const nameNode = targetContext.tree.rootNode.descendantForPosition(position, position);
|
|
1383
|
+
if (!nameNode)
|
|
1384
|
+
return false;
|
|
1385
|
+
if (languageId === "swift") {
|
|
1386
|
+
return isSwiftCrossFileHiddenSharedOwnerMember(languageId, useFile, memberDef.file, nameNode);
|
|
1387
|
+
}
|
|
1388
|
+
if (fileIdentityKey(useFile) === fileIdentityKey(memberDef.file))
|
|
1389
|
+
return false;
|
|
1390
|
+
return !isExportedDeclaration(languageId, nameNode);
|
|
1391
|
+
}
|
|
1392
|
+
async function baseRefsFromContainer(index, mod, container, source, sup, superclassOnly, tree) {
|
|
1393
|
+
const bases = collectDeclaredBaseTypes(container, source, sup, superclassOnly);
|
|
1394
|
+
const refs = [];
|
|
1395
|
+
const seen = new Set();
|
|
1396
|
+
const normalize = (name) => {
|
|
1397
|
+
const normalized = sup.normalizeIdentifier(name);
|
|
1398
|
+
return sup.id === "php" ? foldPhpIdentifierCase(normalized) : normalized;
|
|
1399
|
+
};
|
|
1400
|
+
for (const base of bases) {
|
|
1401
|
+
let def = base.kind === "simple"
|
|
1402
|
+
? resolveNamedMemberContainer(index, mod, base.name, normalize)
|
|
1403
|
+
: await resolveQualifiedMemberContainer(index, mod, base.base, base.path, normalize, sup);
|
|
1404
|
+
if (!def && base.kind === "simple" && sup.id !== "php") {
|
|
1405
|
+
const named = resolveNamedDefinition(index, mod, mod.file, sup, base.name, undefined, container.startIndex);
|
|
1406
|
+
if (named?.status === "ok")
|
|
1407
|
+
def = asMemberContainer(index, named.definition);
|
|
1408
|
+
}
|
|
1409
|
+
if (!def && sup.id === "php" && base.kind === "simple") {
|
|
1410
|
+
def = resolvePhpNamespaceSymbol(index, source, tree, container, base.name, mod.imports, "class") ?? undefined;
|
|
1411
|
+
}
|
|
1412
|
+
if (!def)
|
|
1413
|
+
continue;
|
|
1414
|
+
const ref = await keywordClassRefFromDef(index, def);
|
|
1415
|
+
if (!ref)
|
|
1416
|
+
continue;
|
|
1417
|
+
// `super`, `base`, and `parent` follow class ancestors only. An own receiver also inherits
|
|
1418
|
+
// interface or protocol members, so apply the class-only filter only to supertype lookup.
|
|
1419
|
+
const interfaceLike = ref.container.type === "interface_declaration" ||
|
|
1420
|
+
ref.container.type === "protocol_declaration" ||
|
|
1421
|
+
ref.container.type === "trait_item" ||
|
|
1422
|
+
/^(?:interface|protocol|trait)\b/.test(sliceText(ref.container, ref.context.source).trimStart());
|
|
1423
|
+
if (superclassOnly && (def.kind !== SymbolKind.Class || interfaceLike))
|
|
1424
|
+
continue;
|
|
1425
|
+
// Kotlin classifies interfaces as SymbolKind.Class too, so the superclass is identified
|
|
1426
|
+
// syntactically: `Base()` is a constructor invocation, while bare delegation-specifier
|
|
1427
|
+
// entries (`Face`, `by` delegations) are interfaces. Interface-only super lookup stays
|
|
1428
|
+
// unresolved, while an own receiver can inherit their default members.
|
|
1429
|
+
if (superclassOnly && sup.id === "kotlin" && !base.invoked)
|
|
1430
|
+
continue;
|
|
1431
|
+
const key = keywordClassKey(def);
|
|
1432
|
+
if (seen.has(key))
|
|
1433
|
+
continue;
|
|
1434
|
+
seen.add(key);
|
|
1435
|
+
refs.push(ref);
|
|
1436
|
+
}
|
|
1437
|
+
return refs;
|
|
1438
|
+
}
|
|
1439
|
+
async function resolveKeywordReceiverMember(index, mod, node, member, memberScope, startAtAncestor, knownArgumentCount, explicitClassDef, report) {
|
|
1440
|
+
const current = explicitClassDef
|
|
1441
|
+
? await keywordClassRefFromDef(index, explicitClassDef)
|
|
1442
|
+
: await keywordClassRefFromNode(index, mod, node);
|
|
1443
|
+
if (!current)
|
|
1444
|
+
return undefined;
|
|
1445
|
+
const spansHierarchy = inheritsMemberOverloads(current.context.sup.id);
|
|
1446
|
+
const ownerKey = (ref) => keywordContainerKey(ref.file, ref.container);
|
|
1447
|
+
const preloadHiddenNames = isJsTsLanguage(current.context.sup.id) && knownArgumentCount !== undefined;
|
|
1448
|
+
const memberKey = (def) => `${fileIdentityKey(def.file)}:${def.range.start.index}`;
|
|
1449
|
+
const parentsByKey = new Map();
|
|
1450
|
+
const membersByKey = new Map();
|
|
1451
|
+
const scopeByMember = new Map();
|
|
1452
|
+
const visible = new Set();
|
|
1453
|
+
let level = [current];
|
|
1454
|
+
const seen = new Set([ownerKey(current)]);
|
|
1455
|
+
for (let depth = 0; depth < RECEIVER_HIERARCHY_DEPTH && level.length; depth += 1) {
|
|
1456
|
+
const next = [];
|
|
1457
|
+
let nameFound = false;
|
|
1458
|
+
for (const ref of level) {
|
|
1459
|
+
const key = ownerKey(ref);
|
|
1460
|
+
const matches = [];
|
|
1461
|
+
if (!startAtAncestor || depth) {
|
|
1462
|
+
appendDirectKeywordMembers(ref.module.locals, member, ref.container, ref.context, ref.context.sup.normalizeIdentifier, undefined, matches);
|
|
1463
|
+
if (isJsTsLanguage(ref.context.sup.id) && ref.container.type === "interface_declaration") {
|
|
1464
|
+
const owner = memberContainerDef(ref.module, ref.container);
|
|
1465
|
+
if (owner) {
|
|
1466
|
+
for (const hit of typescriptMergedInterfaceMemberCandidates(ref.module.locals, member, owner, ref.container, ref.context, ref.context.sup.normalizeIdentifier, undefined)) {
|
|
1467
|
+
if (!matches.includes(hit))
|
|
1468
|
+
matches.push(hit);
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
if (ref.context.sup.id === "csharp" || ref.context.sup.id === "swift") {
|
|
1473
|
+
const shared = await resolveSharedOwnerContainers({
|
|
1474
|
+
index,
|
|
1475
|
+
ownerFile: ref.file,
|
|
1476
|
+
ownerContainer: ref.container,
|
|
1477
|
+
ownerSource: ref.context.source,
|
|
1478
|
+
languageId: ref.context.sup.id,
|
|
1479
|
+
});
|
|
1480
|
+
for (const peer of shared) {
|
|
1481
|
+
const peerMatches = [];
|
|
1482
|
+
appendDirectKeywordMembers(peer.module.locals, member, peer.container, peer.context, peer.context.sup.normalizeIdentifier, undefined, peerMatches);
|
|
1483
|
+
for (const hit of peerMatches) {
|
|
1484
|
+
if (peer.context.sup.id === "swift" && crossFilePeerMemberHidden(mod.file, hit, peer.context))
|
|
1485
|
+
continue;
|
|
1486
|
+
matches.push(hit);
|
|
1487
|
+
scopeByMember.set(hit, hasStaticModifier(hit, peer.context, peer.container) ? "static" : "instance");
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
membersByKey.set(key, matches);
|
|
1493
|
+
for (const hit of matches) {
|
|
1494
|
+
if (!scopeByMember.has(hit)) {
|
|
1495
|
+
scopeByMember.set(hit, hasStaticModifier(hit, ref.context, ref.container) ? "static" : "instance");
|
|
1496
|
+
}
|
|
1497
|
+
if (spansHierarchy && (startAtAncestor || depth) && (await isUninheritedPrivateMember(index, mod, node, hit))) {
|
|
1498
|
+
continue;
|
|
1499
|
+
}
|
|
1500
|
+
visible.add(hit);
|
|
1501
|
+
nameFound = true;
|
|
1502
|
+
}
|
|
1503
|
+
if (nameFound && !spansHierarchy && !preloadHiddenNames)
|
|
1504
|
+
continue;
|
|
1505
|
+
const bases = await baseRefsFromContainer(index, ref.module, ref.container, ref.context.source, ref.context.sup, startAtAncestor, ref.context.tree);
|
|
1506
|
+
parentsByKey.set(key, bases);
|
|
1507
|
+
for (const base of bases) {
|
|
1508
|
+
const baseKey = ownerKey(base);
|
|
1509
|
+
if (seen.has(baseKey))
|
|
1510
|
+
continue;
|
|
1511
|
+
seen.add(baseKey);
|
|
1512
|
+
next.push(base);
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
if (nameFound && !spansHierarchy && !preloadHiddenNames)
|
|
1516
|
+
break;
|
|
1517
|
+
level = next;
|
|
1518
|
+
}
|
|
1519
|
+
const sameSignatures = new Map();
|
|
1520
|
+
if (spansHierarchy) {
|
|
1521
|
+
const all = [...membersByKey.values()].flat().filter((candidate) => visible.has(candidate));
|
|
1522
|
+
for (let left = 0; left < all.length; left += 1) {
|
|
1523
|
+
for (let right = left + 1; right < all.length; right += 1) {
|
|
1524
|
+
const a = all[left];
|
|
1525
|
+
const b = all[right];
|
|
1526
|
+
if (!(await sameParameterTypes(index, a, b)))
|
|
1527
|
+
continue;
|
|
1528
|
+
const aKey = memberKey(a);
|
|
1529
|
+
const bKey = memberKey(b);
|
|
1530
|
+
const identical = sameSignatures.get(aKey) ?? new Set();
|
|
1531
|
+
identical.add(bKey);
|
|
1532
|
+
sameSignatures.set(aKey, identical);
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
const model = {
|
|
1537
|
+
ownerKey,
|
|
1538
|
+
members: (ref) => membersByKey.get(ownerKey(ref)) ?? [],
|
|
1539
|
+
supertypes: (ref) => parentsByKey.get(ownerKey(ref)) ?? [],
|
|
1540
|
+
name: (def) => def.localName,
|
|
1541
|
+
key: (def) => def.callable?.key ?? memberKey(def),
|
|
1542
|
+
callable: (def) => {
|
|
1543
|
+
const facts = def.callable;
|
|
1544
|
+
if (!facts || memberLookupBinding(current.context.sup.id) !== "unbound")
|
|
1545
|
+
return facts;
|
|
1546
|
+
return facts.unboundArity ? { ...facts, arity: facts.unboundArity } : facts;
|
|
1547
|
+
},
|
|
1548
|
+
scope: (def) => scopeByMember.get(def) ?? "any",
|
|
1549
|
+
visible: (def) => visible.has(def),
|
|
1550
|
+
sameSignature: (a, b) => sameSignatures.get(memberKey(a))?.has(memberKey(b)) ?? false,
|
|
1551
|
+
};
|
|
1552
|
+
const selected = selectMember([current], model, {
|
|
1553
|
+
name: member,
|
|
1554
|
+
argumentCount: knownArgumentCount ?? null,
|
|
1555
|
+
scope: memberScope,
|
|
1556
|
+
useFile: mod.file,
|
|
1557
|
+
startAtAncestor,
|
|
1558
|
+
keepUniqueArityMismatch: !startAtAncestor,
|
|
1559
|
+
inheritOverloads: spansHierarchy,
|
|
1560
|
+
phpCaseInsensitive: current.context.sup.id === "php",
|
|
1561
|
+
separateMemberScopes: hasSeparateMemberScopes(current.context.sup.id),
|
|
1562
|
+
});
|
|
1563
|
+
if (report) {
|
|
1564
|
+
report.named = selected.named;
|
|
1565
|
+
report.ambiguous = selected.status === "ambiguous";
|
|
1566
|
+
}
|
|
1567
|
+
return selected.status === "unique" ? selected.member : undefined;
|
|
1568
|
+
}
|
|
1569
|
+
/** The parameter list of a Java, Kotlin, or C# method declaration. */
|
|
1570
|
+
async function memberParameterList(index, def) {
|
|
1571
|
+
const context = await ensureParsedContext(def.file, index.parsed?.get(fileIdentityKey(def.file)), index.languageExtensions).catch(() => null);
|
|
1572
|
+
const start = def.range.start.index;
|
|
1573
|
+
if (!context || start === undefined)
|
|
1574
|
+
return null;
|
|
1575
|
+
const declaration = context.tree.rootNode.descendantForIndex(start, def.range.end.index ?? start).parent;
|
|
1576
|
+
return (declaration?.childForFieldName("parameters") ??
|
|
1577
|
+
declaration?.namedChildren.find((child) => child.type === "function_value_parameters") ??
|
|
1578
|
+
null);
|
|
1579
|
+
}
|
|
1580
|
+
/** Parameter type texts of a Java, Kotlin, or C# method; null when a type cannot be read. */
|
|
1581
|
+
async function memberParameterTypes(index, def) {
|
|
1582
|
+
const parameters = await memberParameterList(index, def);
|
|
1583
|
+
if (!parameters)
|
|
1584
|
+
return null;
|
|
1585
|
+
const types = [];
|
|
1586
|
+
for (const parameter of parameters.namedChildren) {
|
|
1587
|
+
const type = parameter.childForFieldName("type") ?? parameter.namedChildren.at(-1);
|
|
1588
|
+
if (!type)
|
|
1589
|
+
return null;
|
|
1590
|
+
types.push(type.text.replace(/\s+/g, ""));
|
|
1591
|
+
}
|
|
1592
|
+
return types;
|
|
1593
|
+
}
|
|
1594
|
+
/** The import that binds a type name in a module, as text; empty when no import binds it. */
|
|
1595
|
+
function importedTypeSource(module, name) {
|
|
1596
|
+
for (const imp of module.imports) {
|
|
1597
|
+
if (imp.kind !== "named" || imp.local !== name)
|
|
1598
|
+
continue;
|
|
1599
|
+
const target = typeof imp.resolved === "string" ? imp.resolved : (imp.resolved?.external ?? "");
|
|
1600
|
+
return `${target}:${imp.imported}`;
|
|
1601
|
+
}
|
|
1602
|
+
return "";
|
|
1603
|
+
}
|
|
1604
|
+
/** Type parameter names declared by a method or any type enclosing it. */
|
|
1605
|
+
async function typeParameterNamesAround(index, def) {
|
|
1606
|
+
const names = new Set();
|
|
1607
|
+
const context = await ensureParsedContext(def.file, index.parsed?.get(fileIdentityKey(def.file)), index.languageExtensions).catch(() => null);
|
|
1608
|
+
const start = def.range.start.index;
|
|
1609
|
+
if (!context || start === undefined)
|
|
1610
|
+
return names;
|
|
1611
|
+
let current = context.tree.rootNode.descendantForIndex(start, def.range.end.index ?? start);
|
|
1612
|
+
for (; current; current = current.parent) {
|
|
1613
|
+
const parameters = current.childForFieldName("type_parameters") ??
|
|
1614
|
+
current.namedChildren.find((child) => child.type === "type_parameters" || child.type === "type_parameter_list");
|
|
1615
|
+
if (!parameters)
|
|
1616
|
+
continue;
|
|
1617
|
+
for (const parameter of parameters.namedChildren) {
|
|
1618
|
+
const name = parameter.childForFieldName("name") ??
|
|
1619
|
+
parameter.namedChildren.find((child) => /identifier/u.test(child.type)) ??
|
|
1620
|
+
(/identifier/u.test(parameter.type) ? parameter : null);
|
|
1621
|
+
if (name)
|
|
1622
|
+
names.add(name.text);
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
return names;
|
|
1626
|
+
}
|
|
1627
|
+
/** Built-in parameter types whose spelling names one type in every file. */
|
|
1628
|
+
const BUILTIN_PARAMETER_TYPES = {
|
|
1629
|
+
java: new Set([
|
|
1630
|
+
"int",
|
|
1631
|
+
"long",
|
|
1632
|
+
"short",
|
|
1633
|
+
"byte",
|
|
1634
|
+
"char",
|
|
1635
|
+
"boolean",
|
|
1636
|
+
"float",
|
|
1637
|
+
"double",
|
|
1638
|
+
"String",
|
|
1639
|
+
"Object",
|
|
1640
|
+
"Integer",
|
|
1641
|
+
"Long",
|
|
1642
|
+
"Boolean",
|
|
1643
|
+
"Double",
|
|
1644
|
+
"Float",
|
|
1645
|
+
"Short",
|
|
1646
|
+
"Byte",
|
|
1647
|
+
"Character",
|
|
1648
|
+
]),
|
|
1649
|
+
kotlin: new Set(["Int", "Long", "Short", "Byte", "Char", "Boolean", "Float", "Double", "String", "Any", "Unit"]),
|
|
1650
|
+
csharp: new Set([
|
|
1651
|
+
"int",
|
|
1652
|
+
"long",
|
|
1653
|
+
"short",
|
|
1654
|
+
"byte",
|
|
1655
|
+
"char",
|
|
1656
|
+
"bool",
|
|
1657
|
+
"float",
|
|
1658
|
+
"double",
|
|
1659
|
+
"decimal",
|
|
1660
|
+
"string",
|
|
1661
|
+
"object",
|
|
1662
|
+
"uint",
|
|
1663
|
+
"ulong",
|
|
1664
|
+
"ushort",
|
|
1665
|
+
"sbyte",
|
|
1666
|
+
]),
|
|
1667
|
+
};
|
|
1668
|
+
/**
|
|
1669
|
+
* Whether two methods take the same parameter types, so the one in a derived class overrides the
|
|
1670
|
+
* other. Equal spelling is proof only within one file or for built-in types; elsewhere each
|
|
1671
|
+
* spelling must resolve to the same declaration from its own file.
|
|
1672
|
+
*/
|
|
1673
|
+
export async function sameParameterTypes(index, left, right) {
|
|
1674
|
+
// A type parameter (`T`) names a different type in each declaration after substitution, so
|
|
1675
|
+
// equal spelling proves nothing.
|
|
1676
|
+
const [leftGenerics, rightGenerics] = await Promise.all([
|
|
1677
|
+
typeParameterNamesAround(index, left),
|
|
1678
|
+
typeParameterNamesAround(index, right),
|
|
1679
|
+
]);
|
|
1680
|
+
const [leftTypes, rightTypes] = await Promise.all([
|
|
1681
|
+
memberParameterTypes(index, left),
|
|
1682
|
+
memberParameterTypes(index, right),
|
|
1683
|
+
]);
|
|
1684
|
+
if (!leftTypes || !rightTypes || leftTypes.length !== rightTypes.length)
|
|
1685
|
+
return false;
|
|
1686
|
+
const sameFile = fileIdentityKey(left.file) === fileIdentityKey(right.file);
|
|
1687
|
+
const leftContext = await ensureParsedContext(left.file, index.parsed?.get(fileIdentityKey(left.file)), index.languageExtensions).catch(() => null);
|
|
1688
|
+
if (!leftContext)
|
|
1689
|
+
return false;
|
|
1690
|
+
for (let at = 0; at < leftTypes.length; at += 1) {
|
|
1691
|
+
const text = leftTypes[at];
|
|
1692
|
+
if (text !== rightTypes[at])
|
|
1693
|
+
return false;
|
|
1694
|
+
const typeNames = text.match(/[\p{L}_][\p{L}\p{N}_]*/gu) ?? [];
|
|
1695
|
+
if (typeNames.some((typeName) => leftGenerics.has(typeName) || rightGenerics.has(typeName)))
|
|
1696
|
+
return false;
|
|
1697
|
+
// One Java or Kotlin file declares one package; a C# file can open several namespaces, so the
|
|
1698
|
+
// same spelling there still needs resolution.
|
|
1699
|
+
if (sameFile && leftContext.sup.id !== "csharp")
|
|
1700
|
+
continue;
|
|
1701
|
+
const base = text
|
|
1702
|
+
.replace(/<.*$/su, "")
|
|
1703
|
+
.replace(/[?\[\].]+$/u, "")
|
|
1704
|
+
.split(".")
|
|
1705
|
+
.pop() ?? text;
|
|
1706
|
+
if (BUILTIN_PARAMETER_TYPES[leftContext.sup.id]?.has(base))
|
|
1707
|
+
continue;
|
|
1708
|
+
const rightContext = await ensureParsedContext(right.file, index.parsed?.get(fileIdentityKey(right.file)), index.languageExtensions).catch(() => null);
|
|
1709
|
+
const leftModule = index.byFile.get(fileIdentityKey(left.file));
|
|
1710
|
+
const rightModule = index.byFile.get(fileIdentityKey(right.file));
|
|
1711
|
+
if (!rightContext || !leftModule || !rightModule)
|
|
1712
|
+
return false;
|
|
1713
|
+
// Resolve at each declaration so a C# spelling reads its own namespace region.
|
|
1714
|
+
const leftType = resolveNamedDefinition(index, leftModule, left.file, leftContext.sup, base, undefined, left.range.start.index);
|
|
1715
|
+
const rightType = resolveNamedDefinition(index, rightModule, right.file, rightContext.sup, base, undefined, right.range.start.index);
|
|
1716
|
+
if (leftType?.status !== "ok" && rightType?.status !== "ok") {
|
|
1717
|
+
// Neither names a project type: both files must name it through the same import (or none).
|
|
1718
|
+
if (importedTypeSource(leftModule, base) !== importedTypeSource(rightModule, base))
|
|
1719
|
+
return false;
|
|
1720
|
+
continue;
|
|
1721
|
+
}
|
|
1722
|
+
if (leftType?.status !== "ok" || rightType?.status !== "ok")
|
|
1723
|
+
return false;
|
|
1724
|
+
if (fileIdentityKey(leftType.definition.file) !== fileIdentityKey(rightType.definition.file) ||
|
|
1725
|
+
leftType.definition.range.start.index !== rightType.definition.range.start.index) {
|
|
1726
|
+
return false;
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
return true;
|
|
1730
|
+
}
|
|
1731
|
+
/**
|
|
1732
|
+
* Validate an unqualified member use against its actual lexical owner, including shared
|
|
1733
|
+
* owner parts in other files and inherited members. Swift checks every bare name; C#, Java, and
|
|
1734
|
+
* Kotlin check only bare call callees. A static context reaches only static members.
|
|
1735
|
+
*/
|
|
1736
|
+
/** The callee of a receiverless call (`f()`), which may name a member through the implicit `this`. */
|
|
1737
|
+
export function implicitSelfCallee(languageId, call) {
|
|
1738
|
+
if (languageId === "csharp")
|
|
1739
|
+
return call.type === "invocation_expression" ? call.childForFieldName("function") : null;
|
|
1740
|
+
if (languageId === "java") {
|
|
1741
|
+
if (call.type !== "method_invocation" || call.childForFieldName("object"))
|
|
1742
|
+
return null;
|
|
1743
|
+
return call.childForFieldName("name");
|
|
1744
|
+
}
|
|
1745
|
+
if (languageId === "kotlin")
|
|
1746
|
+
return call.type === "call_expression" ? (call.namedChildren[0] ?? null) : null;
|
|
1747
|
+
return null;
|
|
1748
|
+
}
|
|
1749
|
+
export async function resolveImplicitSelfMember(index, mod, node, name, source, languageId) {
|
|
1750
|
+
const call = node.parent ?? node;
|
|
1751
|
+
if (languageId !== "swift" && implicitSelfCallee(languageId, call)?.id !== node.id)
|
|
1752
|
+
return undefined;
|
|
1753
|
+
const jvm = languageId === "java" || languageId === "kotlin";
|
|
1754
|
+
const staticScoped = languageId === "csharp" || jvm;
|
|
1755
|
+
const memberScope = staticScoped && nodeInStaticMemberContext(node, source) ? "static" : "any";
|
|
1756
|
+
const argumentCount = getCallArgumentCount({ languageId, source, call }) ?? undefined;
|
|
1757
|
+
const report = { named: false, ambiguous: false };
|
|
1758
|
+
const member = await resolveKeywordReceiverMember(index, mod, node, name, memberScope, false, argumentCount, undefined, report);
|
|
1759
|
+
if (member)
|
|
1760
|
+
return member;
|
|
1761
|
+
// Two inherited overloads of one arity are ambiguous without type ranking; in Java and C# any
|
|
1762
|
+
// inherited method of that name hides imports even when none accepts the call.
|
|
1763
|
+
if (report.ambiguous || (report.named && (languageId === "java" || languageId === "csharp")))
|
|
1764
|
+
return null;
|
|
1765
|
+
if (memberScope !== "static" || !jvm)
|
|
1766
|
+
return undefined;
|
|
1767
|
+
// In a static context an inherited instance method still shadows a static import or package
|
|
1768
|
+
// function; the call is invalid, so it has no target.
|
|
1769
|
+
const instanceReport = { named: false, ambiguous: false };
|
|
1770
|
+
const instance = await resolveKeywordReceiverMember(index, mod, node, name, "any", false, argumentCount, undefined, instanceReport);
|
|
1771
|
+
// Any inherited instance method of that name hides the import, even when none applies.
|
|
1772
|
+
return instance || instanceReport.named ? null : undefined;
|
|
1773
|
+
}
|
|
1774
|
+
/** Whether an inherited JVM member is inaccessible through this receiver. */
|
|
1775
|
+
async function isUninheritedPrivateMember(index, mod, use, member) {
|
|
1776
|
+
const container = findEnclosingClassContainer(use);
|
|
1777
|
+
const start = member.range.start.index ?? -1;
|
|
1778
|
+
const ownMember = !!container &&
|
|
1779
|
+
fileIdentityKey(member.file) === fileIdentityKey(mod.file) &&
|
|
1780
|
+
start >= container.startIndex &&
|
|
1781
|
+
start < container.endIndex;
|
|
1782
|
+
if (ownMember)
|
|
1783
|
+
return false;
|
|
1784
|
+
if (member.javaPackagePrivate && !member.javaProtectedMember) {
|
|
1785
|
+
const memberPackage = getPackageDeclarationName(index, member.file, "java");
|
|
1786
|
+
const useLanguage = supportForFileWithoutHeaderSample(mod.file, index.languageExtensions)?.id;
|
|
1787
|
+
const usePackage = getPackageDeclarationName(index, mod.file, useLanguage === "kotlin" ? "kotlin" : "java");
|
|
1788
|
+
if (memberPackage !== usePackage)
|
|
1789
|
+
return true;
|
|
1790
|
+
}
|
|
1791
|
+
const parsed = await ensureParsedContext(member.file, index.parsed?.get(fileIdentityKey(member.file)), index.languageExtensions).catch(() => null);
|
|
1792
|
+
const nameNode = parsed?.tree.rootNode.descendantForIndex(start, member.range.end.index ?? start);
|
|
1793
|
+
return !!parsed && !!nameNode && isPrivateDeclaration(parsed.sup.id, nameNode);
|
|
1794
|
+
}
|
|
1795
|
+
/**
|
|
1796
|
+
* Whether `Owner::member()` at `use` can reach an instance member of `owner` through an implicit
|
|
1797
|
+
* `this`: the enclosing function is a non-static member function of `owner` or of a class that
|
|
1798
|
+
* derives from `owner` through proven bases. Any other context can only name a static member.
|
|
1799
|
+
*/
|
|
1800
|
+
export async function cppQualifiedOwnerHasImplicitThis(index, mod, use, source, sup, owner) {
|
|
1801
|
+
const definition = cppEnclosingFunctionDefinition(use);
|
|
1802
|
+
if (!definition || !(await cppFunctionHasImplicitThis(index, mod, definition, source, sup)))
|
|
1803
|
+
return false;
|
|
1804
|
+
const outOfLinePath = cppOutOfLineOwnerPath(definition, source, sup);
|
|
1805
|
+
const enclosingDef = outOfLinePath ? await resolveCppQualifiedMemberContainer(index, mod, outOfLinePath) : null;
|
|
1806
|
+
const enclosing = enclosingDef
|
|
1807
|
+
? await keywordClassRefFromDef(index, enclosingDef)
|
|
1808
|
+
: await keywordClassRefFromNode(index, mod, definition);
|
|
1809
|
+
const target = await keywordClassRefFromDef(index, owner);
|
|
1810
|
+
if (!enclosing || !target)
|
|
1811
|
+
return false;
|
|
1812
|
+
const targetKey = keywordContainerKey(target.file, target.container);
|
|
1813
|
+
const visited = new Set();
|
|
1814
|
+
let level = [enclosing];
|
|
1815
|
+
for (let depth = 0; depth < RECEIVER_HIERARCHY_DEPTH && level.length; depth += 1) {
|
|
1816
|
+
const next = [];
|
|
1817
|
+
for (const candidate of level) {
|
|
1818
|
+
const key = keywordContainerKey(candidate.file, candidate.container);
|
|
1819
|
+
if (key === targetKey)
|
|
1820
|
+
return true;
|
|
1821
|
+
if (visited.has(key))
|
|
1822
|
+
continue;
|
|
1823
|
+
visited.add(key);
|
|
1824
|
+
next.push(...(await baseRefsFromContainer(index, candidate.module, candidate.container, candidate.context.source, candidate.context.sup, false, candidate.context.tree)));
|
|
1825
|
+
}
|
|
1826
|
+
level = next;
|
|
1827
|
+
}
|
|
1828
|
+
return false;
|
|
1829
|
+
}
|
|
1830
|
+
/** The C++ function definition enclosing a use, if any. */
|
|
1831
|
+
export function cppEnclosingFunctionDefinition(node) {
|
|
1832
|
+
for (let current = node.parent; current; current = current.parent) {
|
|
1833
|
+
if (current.type === "function_definition")
|
|
1834
|
+
return current;
|
|
1835
|
+
}
|
|
1836
|
+
return null;
|
|
1837
|
+
}
|
|
1838
|
+
/** Proven unqualified member call from an out-of-line C++ method definition. */
|
|
1839
|
+
export async function resolveCppOutOfLineImplicitMember(index, mod, node, name, source, sup, requireAcceptedArity = false) {
|
|
1840
|
+
if (node.parent?.type !== "call_expression" || node.parent.childForFieldName("function")?.id !== node.id) {
|
|
1841
|
+
return undefined;
|
|
1842
|
+
}
|
|
1843
|
+
const ownerPath = cppOutOfLineOwnerPath(node, source, sup);
|
|
1844
|
+
const owner = ownerPath ? await resolveCppQualifiedMemberContainer(index, mod, ownerPath) : null;
|
|
1845
|
+
if (!owner)
|
|
1846
|
+
return undefined;
|
|
1847
|
+
const argumentCount = getCallArgumentCount({ languageId: "cpp", source, call: node.parent }) ?? undefined;
|
|
1848
|
+
const enclosingFunction = cppEnclosingFunctionDefinition(node);
|
|
1849
|
+
const memberScope = enclosingFunction && (await cppFunctionHasImplicitThis(index, mod, enclosingFunction, source, sup))
|
|
1850
|
+
? "any"
|
|
1851
|
+
: "static";
|
|
1852
|
+
const matching = await resolveKeywordReceiverMember(index, mod, node, name, memberScope, false, argumentCount, owner);
|
|
1853
|
+
// A class member hides a same-named global even when this call cannot accept its arity. The
|
|
1854
|
+
// only member is still the call's target for navigation; an ambiguous overload set is not.
|
|
1855
|
+
const member = matching ??
|
|
1856
|
+
(argumentCount === undefined
|
|
1857
|
+
? undefined
|
|
1858
|
+
: await resolveKeywordReceiverMember(index, mod, node, name, memberScope, false, undefined, owner));
|
|
1859
|
+
if (!member) {
|
|
1860
|
+
// In a static member function, an instance member still hides a same-named global: the call
|
|
1861
|
+
// is ill-formed, so neither consumer may fall back to the global.
|
|
1862
|
+
const hidden = memberScope === "static" &&
|
|
1863
|
+
(await resolveKeywordReceiverMember(index, mod, node, name, "any", false, undefined, owner));
|
|
1864
|
+
return hidden ? null : undefined;
|
|
1865
|
+
}
|
|
1866
|
+
// The call graph records no edge for a call its target cannot accept. `null` means a member hides
|
|
1867
|
+
// the name but is not the call's target, so no consumer may fall back to a same-named free function.
|
|
1868
|
+
const rejected = requireAcceptedArity &&
|
|
1869
|
+
argumentCount !== undefined &&
|
|
1870
|
+
!receiverMemberAcceptsArgumentCount(index, member, argumentCount);
|
|
1871
|
+
return rejected ? null : member;
|
|
210
1872
|
}
|
|
211
1873
|
export { supportsReceiverCallEdges, supportsReceiverMemberNavigation } from "../util/member-access-tables.js";
|
|
212
1874
|
function memberDeclaringLocals(mod, typeName, normalize) {
|
|
213
1875
|
const normalized = normalize(typeName);
|
|
214
1876
|
return mod.locals.filter((local) => normalize(local.localName) === normalized && declaresMembers(local));
|
|
215
1877
|
}
|
|
216
|
-
|
|
217
|
-
|
|
1878
|
+
/**
|
|
1879
|
+
* Ruby constant visible the same way go-to-definition resolves a bare `Widget`:
|
|
1880
|
+
* a same-file declaration, or the class a `require` brought in. Not project-wide.
|
|
1881
|
+
*/
|
|
1882
|
+
export function resolveRubyVisibleConstant(index, mod, sup, typeName) {
|
|
1883
|
+
if (sup.id !== "ruby")
|
|
1884
|
+
return null;
|
|
1885
|
+
const resolved = resolveNamedDefinition(index, mod, mod.file, sup, typeName);
|
|
1886
|
+
if (resolved?.status !== "ok")
|
|
1887
|
+
return null;
|
|
1888
|
+
return declaresMembers(resolved.definition) ? resolved.definition : null;
|
|
1889
|
+
}
|
|
1890
|
+
/** Proven PHP `extends` base, using the same simple-name lookup as `parent::`. */
|
|
1891
|
+
function resolvePhpProvenBaseClass(index, mod, classNode, source, sup) {
|
|
1892
|
+
const bases = collectDeclaredBaseTypes(classNode, source, sup, true);
|
|
1893
|
+
if (bases.length !== 1)
|
|
1894
|
+
return null;
|
|
1895
|
+
const base = bases[0];
|
|
1896
|
+
if (!base || base.kind !== "simple")
|
|
1897
|
+
return null;
|
|
1898
|
+
const normalize = (name) => foldPhpIdentifierCase(sup.normalizeIdentifier(name));
|
|
1899
|
+
return resolveNamedMemberContainer(index, mod, base.name, normalize) ?? null;
|
|
1900
|
+
}
|
|
1901
|
+
/**
|
|
1902
|
+
* Class named by `new self()`, `new static()`, or `new parent()`.
|
|
1903
|
+
* `self` and `static` are the enclosing class. `parent` is the proven base.
|
|
1904
|
+
* Returns null when the keyword is not proven, so a class named `Parent` cannot win.
|
|
1905
|
+
*/
|
|
1906
|
+
export function resolvePhpObjectCreationTarget(index, mod, keyword, source, sup) {
|
|
1907
|
+
if (!keyword.classNode)
|
|
1908
|
+
return null;
|
|
1909
|
+
if (keyword.keyword === "self" || keyword.keyword === "static") {
|
|
1910
|
+
return memberContainerDef(mod, keyword.classNode);
|
|
1911
|
+
}
|
|
1912
|
+
return resolvePhpProvenBaseClass(index, mod, keyword.classNode, source, sup);
|
|
1913
|
+
}
|
|
1914
|
+
function enclosingRubyMethod(node) {
|
|
1915
|
+
let current = node.parent;
|
|
1916
|
+
while (current) {
|
|
1917
|
+
if (current.type === "method" || current.type === "singleton_method")
|
|
1918
|
+
return current;
|
|
1919
|
+
current = current.parent;
|
|
1920
|
+
}
|
|
1921
|
+
return null;
|
|
1922
|
+
}
|
|
1923
|
+
/** Bare `super` / `super()` targets the same-named method on a proven superclass. */
|
|
1924
|
+
export async function resolveRubySuperDefinition(index, mod, node, source, sup) {
|
|
1925
|
+
if (sup.id !== "ruby" || node.type !== "super")
|
|
1926
|
+
return undefined;
|
|
1927
|
+
const method = enclosingRubyMethod(node);
|
|
1928
|
+
const nameNode = method?.childForFieldName("name") ?? null;
|
|
1929
|
+
if (!nameNode)
|
|
1930
|
+
return undefined;
|
|
1931
|
+
const member = sliceText(nameNode, source);
|
|
1932
|
+
if (!member)
|
|
1933
|
+
return undefined;
|
|
1934
|
+
return resolveKeywordReceiverMember(index, mod, node, member, "any", true);
|
|
1935
|
+
}
|
|
1936
|
+
/** Follow a Swift typealias only when its right-hand side is a single indexed nominal type. */
|
|
1937
|
+
async function resolveSwiftAliasContainer(index, alias) {
|
|
1938
|
+
let current = alias;
|
|
1939
|
+
const seen = new Set();
|
|
1940
|
+
while (current.kind === SymbolKind.TypeAlias) {
|
|
1941
|
+
const key = `${fileIdentityKey(current.file)}:${current.range.start.index}`;
|
|
1942
|
+
if (seen.has(key))
|
|
1943
|
+
return undefined;
|
|
1944
|
+
seen.add(key);
|
|
1945
|
+
const mod = index.byFile.get(fileIdentityKey(current.file));
|
|
1946
|
+
if (!mod)
|
|
1947
|
+
return undefined;
|
|
1948
|
+
const context = await ensureParsedContext(current.file, index.parsed?.get(fileIdentityKey(current.file)), index.languageExtensions);
|
|
1949
|
+
if (context.sup.id !== "swift")
|
|
1950
|
+
return undefined;
|
|
1951
|
+
const declaration = nameNodeForDef(context, current)?.parent;
|
|
1952
|
+
if (declaration?.type !== "typealias_declaration") {
|
|
1953
|
+
return declaration?.type === "class_declaration" && declaresMembers(current) ? current : undefined;
|
|
1954
|
+
}
|
|
1955
|
+
const target = declaration.namedChildren.find((child) => child.type === "user_type");
|
|
1956
|
+
const named = target ? unwrapNamedType(target, context.sup) : null;
|
|
1957
|
+
if (!named || target?.namedChildren.length !== 1)
|
|
1958
|
+
return undefined;
|
|
1959
|
+
const targetName = sliceText(named, context.source);
|
|
1960
|
+
const namedTarget = resolveNamedDefinition(index, mod, mod.file, context.sup, targetName, undefined, declaration.startIndex);
|
|
1961
|
+
const next = resolveNamedMemberContainer(index, mod, targetName, context.sup.normalizeIdentifier) ??
|
|
1962
|
+
(namedTarget?.status === "ok" ? asMemberContainer(index, namedTarget.definition) : undefined);
|
|
1963
|
+
if (!next)
|
|
1964
|
+
return undefined;
|
|
1965
|
+
current = next;
|
|
1966
|
+
}
|
|
1967
|
+
return declaresMembers(current) ? current : undefined;
|
|
1968
|
+
}
|
|
1969
|
+
async function resolveReceiverDefinition(index, obj, source, sup, resolveExpression, mod, proof, resolvedDirect) {
|
|
1970
|
+
const direct = resolvedDirect === undefined ? await resolveExpression(obj) : resolvedDirect;
|
|
1971
|
+
const receiver = proof ??
|
|
1972
|
+
classifyReceiver(sup, obj, source, null, 0, obj.parent ?? obj, noLexicalBinding, () => {
|
|
1973
|
+
if (direct?.kind === "namespace")
|
|
1974
|
+
return "module-import";
|
|
1975
|
+
if (direct?.kind === "resolved" && declaresMembers(direct.def))
|
|
1976
|
+
return "static-type";
|
|
1977
|
+
return null;
|
|
1978
|
+
});
|
|
1979
|
+
if (receiver.kind === "module-import")
|
|
1980
|
+
return null;
|
|
1981
|
+
if (supportsDeclaredFieldChains(sup.id) && isMemberAccessNode(sup, obj)) {
|
|
1982
|
+
const directType = direct?.kind === "resolved" ? asMemberContainer(index, direct.def) : undefined;
|
|
1983
|
+
if (!directType) {
|
|
1984
|
+
const { object, property } = getMemberAccessParts(sup, obj);
|
|
1985
|
+
if (!object || !property)
|
|
1986
|
+
return null;
|
|
1987
|
+
const owner = await resolveReceiverDefinition(index, object, source, sup, resolveExpression, mod);
|
|
1988
|
+
if (!owner || owner.runtimeTypeOnly)
|
|
1989
|
+
return null;
|
|
1990
|
+
const field = await resolveKeywordReceiverMember(index, mod, property, sliceText(property, source), owner.memberScope, false, undefined, owner.def);
|
|
1991
|
+
if (!field || field.kind !== SymbolKind.Variable || !field.isMember)
|
|
1992
|
+
return null;
|
|
1993
|
+
const fieldContext = await ensureParsedContext(field.file, undefined, index.languageExtensions);
|
|
1994
|
+
const name = nameNodeForDef(fieldContext, field);
|
|
1995
|
+
let declaration = name?.parent ?? null;
|
|
1996
|
+
if (declaration?.type === "variable_declarator")
|
|
1997
|
+
declaration = declaration.parent;
|
|
1998
|
+
if (!declaration)
|
|
1999
|
+
return null;
|
|
2000
|
+
const annotatedType = declaredTypeNameNode(declaration, fieldContext.sup);
|
|
2001
|
+
if (!annotatedType)
|
|
2002
|
+
return null;
|
|
2003
|
+
const fieldModule = index.byFile.get(fileIdentityKey(field.file));
|
|
2004
|
+
if (!fieldModule)
|
|
2005
|
+
return null;
|
|
2006
|
+
const typeText = qualifiedDeclaredTypeText(annotatedType, fieldContext.source);
|
|
2007
|
+
// A type parameter in scope (`class Host<Repo>`) is not a nominal type; bounds are not solved.
|
|
2008
|
+
if (typeParameterNamesInScope(declaration, fieldContext.source).has(typeText.split(/[.:]/)[0]))
|
|
2009
|
+
return null;
|
|
2010
|
+
const type = await resolveReceiverTypeName(index, fieldModule, fieldContext.sup, typeText);
|
|
2011
|
+
return type ? { def: type, memberScope: "instance" } : null;
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
if (receiver.kind === "own-type") {
|
|
2015
|
+
const container = nearestMemberContainer(obj);
|
|
2016
|
+
const owner = container ? definitionForContainer(mod, container) : undefined;
|
|
2017
|
+
return owner ? { def: owner, memberScope: receiver.memberScope } : null;
|
|
2018
|
+
}
|
|
2019
|
+
const constructor = receiver.kind === "named-type" && receiver.proof !== "static-type" ? receiver.typeNode : null;
|
|
218
2020
|
if (constructor) {
|
|
2021
|
+
if (sup.id === "cpp") {
|
|
2022
|
+
const qualifiedType = cppQualifiedNameSegments(constructor, source);
|
|
2023
|
+
if (qualifiedType.length > 1) {
|
|
2024
|
+
const def = await resolveCppQualifiedMemberContainer(index, mod, qualifiedType);
|
|
2025
|
+
if (def) {
|
|
2026
|
+
return {
|
|
2027
|
+
def,
|
|
2028
|
+
memberScope: hasStaticMemberDistinction(sup.id) ? "instance" : "any",
|
|
2029
|
+
};
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
219
2033
|
const typeName = sliceText(constructor, source);
|
|
220
2034
|
const typedLocals = memberDeclaringLocals(mod, typeName, sup.normalizeIdentifier);
|
|
221
2035
|
if (typedLocals.length === 1) {
|
|
@@ -224,13 +2038,42 @@ async function resolveReceiverDefinition(obj, source, sup, resolveExpression, mo
|
|
|
224
2038
|
memberScope: hasStaticMemberDistinction(sup.id) ? "instance" : "any",
|
|
225
2039
|
};
|
|
226
2040
|
}
|
|
2041
|
+
if (sup.id === "go") {
|
|
2042
|
+
const named = resolveNamedDefinition(index, mod, mod.file, sup, typeName, undefined, constructor.startIndex);
|
|
2043
|
+
const peer = named?.status === "ok" ? asMemberContainer(index, named.definition) : undefined;
|
|
2044
|
+
if (peer)
|
|
2045
|
+
return { def: peer, memberScope: "any" };
|
|
2046
|
+
}
|
|
2047
|
+
if (sup.id === "php") {
|
|
2048
|
+
const normalizeTypeName = (name) => foldPhpIdentifierCase(sup.normalizeIdentifier(name));
|
|
2049
|
+
const namedContainer = resolveNamedMemberContainer(index, mod, typeName, normalizeTypeName);
|
|
2050
|
+
if (namedContainer) {
|
|
2051
|
+
return {
|
|
2052
|
+
def: namedContainer,
|
|
2053
|
+
memberScope: hasStaticMemberDistinction(sup.id) ? "instance" : "any",
|
|
2054
|
+
};
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
227
2057
|
const result = await resolveExpression(constructor);
|
|
228
|
-
|
|
2058
|
+
const constructed = result?.kind === "resolved" ? asMemberContainer(index, result.def) : undefined;
|
|
2059
|
+
if (constructed) {
|
|
229
2060
|
return {
|
|
230
|
-
def:
|
|
2061
|
+
def: constructed,
|
|
231
2062
|
memberScope: hasStaticMemberDistinction(sup.id) ? "instance" : "any",
|
|
232
2063
|
};
|
|
233
2064
|
}
|
|
2065
|
+
// Ruby `require` exposes a class as a namespace import. A bare constant already
|
|
2066
|
+
// resolves through resolveNamedDefinition; `Klass.new` must use that same path
|
|
2067
|
+
// so goto, references, and call edges agree. Same-file classes returned above.
|
|
2068
|
+
if (sup.id === "ruby" && typedLocals.length === 0) {
|
|
2069
|
+
const rubyType = resolveRubyVisibleConstant(index, mod, sup, typeName);
|
|
2070
|
+
if (rubyType) {
|
|
2071
|
+
return {
|
|
2072
|
+
def: rubyType,
|
|
2073
|
+
memberScope: hasStaticMemberDistinction(sup.id) ? "instance" : "any",
|
|
2074
|
+
};
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
234
2077
|
if (typedLocals[0]) {
|
|
235
2078
|
return {
|
|
236
2079
|
def: typedLocals[0],
|
|
@@ -238,13 +2081,19 @@ async function resolveReceiverDefinition(obj, source, sup, resolveExpression, mo
|
|
|
238
2081
|
};
|
|
239
2082
|
}
|
|
240
2083
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
if (
|
|
244
|
-
return { def:
|
|
2084
|
+
if (sup.id === "swift" && direct?.kind === "resolved" && direct.def.kind === SymbolKind.TypeAlias) {
|
|
2085
|
+
const aliased = await resolveSwiftAliasContainer(index, direct.def);
|
|
2086
|
+
if (aliased)
|
|
2087
|
+
return { def: aliased, memberScope: "static" };
|
|
2088
|
+
return null;
|
|
2089
|
+
}
|
|
2090
|
+
const directContainer = direct?.kind === "resolved" ? asMemberContainer(index, direct.def) : undefined;
|
|
2091
|
+
if (directContainer) {
|
|
2092
|
+
if (isJsTsLanguage(sup.id) && directContainer.kind === SymbolKind.TypeAlias) {
|
|
2093
|
+
return { def: directContainer, memberScope: "any", runtimeTypeOnly: true };
|
|
245
2094
|
}
|
|
246
|
-
const memberScope =
|
|
247
|
-
return { def:
|
|
2095
|
+
const memberScope = supportsStaticMemberScope(sup.id) ? "static" : "any";
|
|
2096
|
+
return { def: directContainer, memberScope };
|
|
248
2097
|
}
|
|
249
2098
|
if (isJsTsLanguage(sup.id) && isReceiverNameNode(sup, obj.type)) {
|
|
250
2099
|
return null;
|
|
@@ -254,7 +2103,25 @@ async function resolveReceiverDefinition(obj, source, sup, resolveExpression, mo
|
|
|
254
2103
|
}
|
|
255
2104
|
return null;
|
|
256
2105
|
}
|
|
257
|
-
|
|
2106
|
+
/** Whether a type-qualified access (`Util.M`) can name `def`: a nested type or a static member. */
|
|
2107
|
+
async function reachableThroughTypeName(index, def) {
|
|
2108
|
+
if (declaresMembers(def))
|
|
2109
|
+
return true;
|
|
2110
|
+
const context = await ensureParsedContext(def.file, index.parsed?.get(fileIdentityKey(def.file)), index.languageExtensions);
|
|
2111
|
+
const nameNode = nameNodeForDef(context, def);
|
|
2112
|
+
if (!nameNode)
|
|
2113
|
+
return false;
|
|
2114
|
+
return hasStaticModifier(def, context, nearestMemberContainer(nameNode) ?? context.tree.rootNode);
|
|
2115
|
+
}
|
|
2116
|
+
/**
|
|
2117
|
+
* A member declared directly in the type `baseDef` names (`Util.Two`), including the other parts
|
|
2118
|
+
* of a C# partial type, or inherited through it. Same-named overloads are narrowed to members a
|
|
2119
|
+
* type name reaches (static members and nested types) when the base is a type, then chosen by
|
|
2120
|
+
* the call's argument count; without a count, or when several accept it, the lookup is
|
|
2121
|
+
* ambiguous. A sole reachable candidate is kept even when the count does not fit, as for other
|
|
2122
|
+
* calls; the graph records no edge for it. Ruby reopens a method by redefining it, so it keeps the first match.
|
|
2123
|
+
*/
|
|
2124
|
+
async function resolveMemberDefinitionForBase(index, baseDef, member, knownArgumentCount) {
|
|
258
2125
|
const targetContext = await ensureParsedContext(baseDef.file, undefined, index.languageExtensions);
|
|
259
2126
|
const start = baseDef.range.start;
|
|
260
2127
|
const targetPosition = {
|
|
@@ -268,37 +2135,169 @@ async function resolveMemberDefinitionForBase(index, baseDef, member) {
|
|
|
268
2135
|
const targetModule = index.byFile.get(fileIdentityKey(baseDef.file));
|
|
269
2136
|
if (!targetModule)
|
|
270
2137
|
return undefined;
|
|
2138
|
+
const languageId = targetContext.sup.id;
|
|
271
2139
|
const normalizeIdentifier = targetContext.sup.normalizeIdentifier;
|
|
272
|
-
const
|
|
273
|
-
if (
|
|
274
|
-
|
|
275
|
-
|
|
2140
|
+
const candidates = findDirectLocalsWithinNode(targetModule.locals, member, container, targetContext, normalizeIdentifier);
|
|
2141
|
+
if (languageId === "csharp") {
|
|
2142
|
+
const partialParts = await findSharedOwnerMemberDefinitions({
|
|
2143
|
+
index,
|
|
2144
|
+
ownerFile: baseDef.file,
|
|
2145
|
+
ownerContainer: container,
|
|
2146
|
+
ownerSource: targetContext.source,
|
|
2147
|
+
languageId,
|
|
2148
|
+
member,
|
|
2149
|
+
});
|
|
2150
|
+
for (const hit of partialParts) {
|
|
2151
|
+
if (!candidates.includes(hit))
|
|
2152
|
+
candidates.push(hit);
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
if (candidates.length) {
|
|
2156
|
+
let reachable = candidates;
|
|
2157
|
+
if (languageId !== "ruby" && candidates.length > 1) {
|
|
2158
|
+
reachable = [];
|
|
2159
|
+
for (const candidate of candidates) {
|
|
2160
|
+
if (!declaresMembers(baseDef) || (await reachableThroughTypeName(index, candidate)))
|
|
2161
|
+
reachable.push(candidate);
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
const selected = selectMember([baseDef], indexedMemberModel(index, reachable, languageId), {
|
|
2165
|
+
name: member,
|
|
2166
|
+
argumentCount: knownArgumentCount ?? null,
|
|
2167
|
+
scope: "any",
|
|
2168
|
+
useFile: baseDef.file,
|
|
2169
|
+
phpCaseInsensitive: languageId === "php",
|
|
2170
|
+
firstDeclarationWins: languageId === "ruby",
|
|
2171
|
+
keepUniqueArityMismatch: true,
|
|
2172
|
+
});
|
|
2173
|
+
return selected.status === "unique" ? selected.member : undefined;
|
|
2174
|
+
}
|
|
2175
|
+
if (languageId === "java")
|
|
276
2176
|
return undefined;
|
|
277
|
-
return findReceiverMemberDefinition(targetModule.locals, member, baseDef, container, targetContext, normalizeIdentifier);
|
|
2177
|
+
return await findReceiverMemberDefinition(index, targetModule.locals, member, baseDef, container, targetContext, normalizeIdentifier);
|
|
2178
|
+
}
|
|
2179
|
+
function typescriptMergedInterfaceMemberCandidates(locals, member, receiverDef, container, targetContext, normalizeIdentifier, predicate) {
|
|
2180
|
+
const matches = findDirectLocalsWithinNode(locals, member, container, targetContext, normalizeIdentifier, predicate);
|
|
2181
|
+
if (!isJsTsLanguage(targetContext.sup.id) || container.type !== "interface_declaration")
|
|
2182
|
+
return matches;
|
|
2183
|
+
const receiverStart = receiverDef.range.start.index ?? 0;
|
|
2184
|
+
const receiverEnd = receiverDef.range.end.index ?? receiverStart;
|
|
2185
|
+
const receiverContainerKey = typescriptCallableContainerKey(targetContext.tree, receiverStart, receiverEnd);
|
|
2186
|
+
const seenBodies = new Set();
|
|
2187
|
+
const appendMembers = (interfaceDeclaration) => {
|
|
2188
|
+
const body = interfaceDeclaration.childForFieldName("body");
|
|
2189
|
+
if (!body)
|
|
2190
|
+
return;
|
|
2191
|
+
const bodyKey = typescriptCallableContainerKey(targetContext.tree, body.startIndex, body.endIndex);
|
|
2192
|
+
if (seenBodies.has(bodyKey))
|
|
2193
|
+
return;
|
|
2194
|
+
seenBodies.add(bodyKey);
|
|
2195
|
+
for (const candidate of findDirectLocalsWithinNode(locals, member, interfaceDeclaration, targetContext, normalizeIdentifier, predicate)) {
|
|
2196
|
+
if (!matches.includes(candidate))
|
|
2197
|
+
matches.push(candidate);
|
|
2198
|
+
}
|
|
2199
|
+
};
|
|
2200
|
+
const normalizedReceiverName = normalizeIdentifier(receiverDef.localName);
|
|
2201
|
+
for (const local of locals) {
|
|
2202
|
+
if (normalizeIdentifier(local.localName) !== normalizedReceiverName)
|
|
2203
|
+
continue;
|
|
2204
|
+
const start = local.range.start.index;
|
|
2205
|
+
if (start === undefined)
|
|
2206
|
+
continue;
|
|
2207
|
+
const end = local.range.end.index ?? start;
|
|
2208
|
+
if (typescriptCallableContainerKey(targetContext.tree, start, end) !== receiverContainerKey)
|
|
2209
|
+
continue;
|
|
2210
|
+
const nameNode = targetContext.tree.rootNode.descendantForIndex(start, end);
|
|
2211
|
+
const interfaceDeclaration = nameNode.parent;
|
|
2212
|
+
if (interfaceDeclaration?.type !== "interface_declaration")
|
|
2213
|
+
continue;
|
|
2214
|
+
appendMembers(interfaceDeclaration);
|
|
2215
|
+
}
|
|
2216
|
+
return matches;
|
|
2217
|
+
}
|
|
2218
|
+
/** Same-name namespace reopenings, excluding members of a decoy namespace or nested owner. */
|
|
2219
|
+
export function findTypeScriptNamespaceMemberCandidates(locals, member, receiverDef, context) {
|
|
2220
|
+
if (!isJsTsLanguage(context.sup.id))
|
|
2221
|
+
return [];
|
|
2222
|
+
const containers = typescriptMergedNamespaceContainers(context.tree, locals, receiverDef);
|
|
2223
|
+
const matches = [];
|
|
2224
|
+
for (const container of containers) {
|
|
2225
|
+
for (const candidate of findDirectLocalsWithinNode(locals, member, container, context, context.sup.normalizeIdentifier)) {
|
|
2226
|
+
if (!matches.includes(candidate))
|
|
2227
|
+
matches.push(candidate);
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
return matches;
|
|
278
2231
|
}
|
|
279
|
-
function findReceiverMemberDefinition(locals, member, receiverDef, container, targetContext, normalizeIdentifier, memberScope = "any") {
|
|
2232
|
+
async function findReceiverMemberDefinition(index, locals, member, receiverDef, container, targetContext, normalizeIdentifier, memberScope = "any", knownArgumentCount) {
|
|
280
2233
|
const memberPredicate = memberScope === "any"
|
|
281
2234
|
? undefined
|
|
282
2235
|
: (local) => matchesReceiverMemberScope(local, memberScope, targetContext, container);
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
2236
|
+
if ((targetContext.sup.id === "ts" || targetContext.sup.id === "tsx") &&
|
|
2237
|
+
(container.type === "internal_module" || container.type === "module")) {
|
|
2238
|
+
const candidates = findTypeScriptNamespaceMemberCandidates(locals, member, receiverDef, targetContext);
|
|
2239
|
+
const selected = selectMember([receiverDef], indexedMemberModel(index, candidates, targetContext.sup.id), {
|
|
2240
|
+
name: member,
|
|
2241
|
+
argumentCount: knownArgumentCount ?? null,
|
|
2242
|
+
scope: "any",
|
|
2243
|
+
useFile: receiverDef.file,
|
|
2244
|
+
keepUniqueArityMismatch: true,
|
|
2245
|
+
});
|
|
2246
|
+
return selected.status === "unique" ? selected.member : undefined;
|
|
2247
|
+
}
|
|
2248
|
+
const allReceiverMatches = typescriptMergedInterfaceMemberCandidates(locals, member, receiverDef, container, targetContext, normalizeIdentifier, memberPredicate);
|
|
2249
|
+
if (targetContext.sup.id === "csharp" || targetContext.sup.id === "swift") {
|
|
2250
|
+
const shared = await findSharedOwnerMemberDefinitions({
|
|
2251
|
+
index,
|
|
2252
|
+
ownerFile: receiverDef.file,
|
|
2253
|
+
ownerContainer: container,
|
|
2254
|
+
ownerSource: targetContext.source,
|
|
2255
|
+
languageId: targetContext.sup.id,
|
|
2256
|
+
member,
|
|
2257
|
+
memberScope,
|
|
2258
|
+
});
|
|
2259
|
+
for (const hit of shared) {
|
|
2260
|
+
if (!allReceiverMatches.includes(hit))
|
|
2261
|
+
allReceiverMatches.push(hit);
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
if (allReceiverMatches.length) {
|
|
2265
|
+
const selected = selectMember([receiverDef], indexedMemberModel(index, allReceiverMatches, targetContext.sup.id), {
|
|
2266
|
+
name: member,
|
|
2267
|
+
argumentCount: knownArgumentCount ?? null,
|
|
2268
|
+
scope: "any",
|
|
2269
|
+
useFile: receiverDef.file,
|
|
2270
|
+
phpCaseInsensitive: targetContext.sup.id === "php",
|
|
2271
|
+
keepUniqueArityMismatch: true,
|
|
2272
|
+
});
|
|
2273
|
+
return selected.status === "unique" ? selected.member : undefined;
|
|
2274
|
+
}
|
|
288
2275
|
if (targetContext.sup.id === "rust") {
|
|
289
|
-
|
|
290
|
-
return implNode ? findLocalWithinNode(locals, member, implNode, normalizeIdentifier) : undefined;
|
|
2276
|
+
return selectRustReceiverMember(index, receiverDef, member, targetContext, locals, knownArgumentCount);
|
|
291
2277
|
}
|
|
292
2278
|
if (targetContext.sup.id === "go") {
|
|
293
|
-
return
|
|
2279
|
+
return selectGoReceiverMember(index, receiverDef, member, targetContext.sup.normalizeIdentifier, knownArgumentCount);
|
|
2280
|
+
}
|
|
2281
|
+
if (targetContext.sup.id === "kotlin") {
|
|
2282
|
+
const extensionMatches = kotlinExtensionFunctionsNamedOnType(locals, member, receiverDef.localName, targetContext, normalizeIdentifier);
|
|
2283
|
+
if (extensionMatches.length) {
|
|
2284
|
+
const selected = selectMember([receiverDef], indexedMemberModel(index, extensionMatches, targetContext.sup.id), {
|
|
2285
|
+
name: member,
|
|
2286
|
+
argumentCount: knownArgumentCount ?? null,
|
|
2287
|
+
scope: "any",
|
|
2288
|
+
useFile: receiverDef.file,
|
|
2289
|
+
keepUniqueArityMismatch: true,
|
|
2290
|
+
});
|
|
2291
|
+
return selected.status === "unique" ? selected.member : undefined;
|
|
2292
|
+
}
|
|
294
2293
|
}
|
|
295
2294
|
return undefined;
|
|
296
2295
|
}
|
|
297
|
-
function
|
|
2296
|
+
function findLocalsWithinNode(locals, member, node, normalizeIdentifier = (name) => name, predicate) {
|
|
298
2297
|
const containerStart = node.startIndex;
|
|
299
2298
|
const containerEnd = node.endIndex;
|
|
300
2299
|
const normalizedMember = normalizeIdentifier(member);
|
|
301
|
-
return locals.
|
|
2300
|
+
return locals.filter((local) => {
|
|
302
2301
|
const startIndex = local.range.start.index;
|
|
303
2302
|
const endIndex = local.range.end.index;
|
|
304
2303
|
return (normalizeIdentifier(local.localName) === normalizedMember &&
|
|
@@ -314,22 +2313,50 @@ function matchesReceiverMemberScope(local, memberScope, targetContext, container
|
|
|
314
2313
|
return true;
|
|
315
2314
|
return hasStaticModifier(local, targetContext, container) === (memberScope === "static");
|
|
316
2315
|
}
|
|
317
|
-
|
|
318
|
-
|
|
2316
|
+
/**
|
|
2317
|
+
* Whether a member found for a receiverless call can accept that call's argument count. Unknown
|
|
2318
|
+
* counts and unknown arities accept.
|
|
2319
|
+
*/
|
|
2320
|
+
export function memberAcceptsCallAt(index, member, callee, source, languageId) {
|
|
2321
|
+
const argumentCount = getCallArgumentCount({ languageId, source, call: callee.parent ?? callee });
|
|
2322
|
+
if (argumentCount === null)
|
|
319
2323
|
return true;
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
2324
|
+
return receiverMemberAcceptsArgumentCount(index, member, argumentCount) !== false;
|
|
2325
|
+
}
|
|
2326
|
+
function receiverMemberAcceptsArgumentCount(index, def, argumentCount) {
|
|
2327
|
+
const languageId = supportForFileWithoutHeaderSample(def.file, index.languageExtensions)?.id;
|
|
2328
|
+
if (!languageId)
|
|
2329
|
+
return undefined;
|
|
2330
|
+
const callable = def.callable ??
|
|
2331
|
+
index.byFile
|
|
2332
|
+
.get(fileIdentityKey(def.file))
|
|
2333
|
+
?.locals.find((local) => local.range.start.index === def.range.start.index && local.localName === def.localName)
|
|
2334
|
+
?.callable;
|
|
2335
|
+
const range = memberLookupBinding(languageId) === "unbound" ? (callable?.unboundArity ?? callable?.arity) : callable?.arity;
|
|
2336
|
+
if (!range)
|
|
2337
|
+
return undefined;
|
|
2338
|
+
return argumentCount >= range.minArgs && (range.maxArgs === null || argumentCount <= range.maxArgs);
|
|
2339
|
+
}
|
|
2340
|
+
/** Indexed owner membership and callable facts for direct and merged-owner lookup. */
|
|
2341
|
+
function indexedMemberModel(index, members, languageId) {
|
|
2342
|
+
return {
|
|
2343
|
+
ownerKey: keywordClassKey,
|
|
2344
|
+
members: () => members,
|
|
2345
|
+
supertypes: () => [],
|
|
2346
|
+
name: (def) => def.localName,
|
|
2347
|
+
key: (def) => def.callable?.key ?? keywordClassKey(def),
|
|
2348
|
+
callable: (def) => {
|
|
2349
|
+
const facts = def.callable ??
|
|
2350
|
+
index.byFile
|
|
2351
|
+
.get(fileIdentityKey(def.file))
|
|
2352
|
+
?.locals.find((local) => local.range.start.index === def.range.start.index && local.localName === def.localName)?.callable;
|
|
2353
|
+
if (!facts || memberLookupBinding(languageId) !== "unbound" || !facts.unboundArity)
|
|
2354
|
+
return facts;
|
|
2355
|
+
return { ...facts, arity: facts.unboundArity };
|
|
2356
|
+
},
|
|
2357
|
+
scope: () => "any",
|
|
2358
|
+
visible: () => true,
|
|
2359
|
+
};
|
|
333
2360
|
}
|
|
334
2361
|
function hasStaticModifier(local, targetContext, container) {
|
|
335
2362
|
const position = {
|
|
@@ -337,19 +2364,21 @@ function hasStaticModifier(local, targetContext, container) {
|
|
|
337
2364
|
column: local.range.start.column - 1,
|
|
338
2365
|
};
|
|
339
2366
|
let current = targetContext.tree.rootNode.descendantForPosition(position, position);
|
|
2367
|
+
if (declarationIsStaticEquivalent(targetContext.sup.id, current))
|
|
2368
|
+
return true;
|
|
340
2369
|
while (current && current !== container) {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
if (nodeDeclaresStatic(child, targetContext.source))
|
|
346
|
-
return true;
|
|
347
|
-
}
|
|
2370
|
+
if (targetContext.sup.id === "php" && (current.type === "const_declaration" || current.type === "enum_case"))
|
|
2371
|
+
return true;
|
|
2372
|
+
if (declarationNodeIsStatic(current, targetContext.source))
|
|
2373
|
+
return true;
|
|
348
2374
|
current = current.parent;
|
|
349
2375
|
}
|
|
350
2376
|
return false;
|
|
351
2377
|
}
|
|
352
2378
|
const NESTED_MEMBER_LOCAL_CONTAINERS = new Set([
|
|
2379
|
+
"formal_parameters",
|
|
2380
|
+
"parameter_list",
|
|
2381
|
+
"parameters",
|
|
353
2382
|
"block",
|
|
354
2383
|
"class",
|
|
355
2384
|
"class_declaration",
|
|
@@ -368,14 +2397,22 @@ const NESTED_MEMBER_LOCAL_CONTAINERS = new Set([
|
|
|
368
2397
|
"module",
|
|
369
2398
|
"statement_block",
|
|
370
2399
|
]);
|
|
371
|
-
function
|
|
2400
|
+
function matchesReceiverMemberName(local, normalizedMember, targetContext, normalizeIdentifier) {
|
|
2401
|
+
const localName = normalizeIdentifier(local.localName);
|
|
2402
|
+
if (targetContext.sup.id === "php") {
|
|
2403
|
+
return comparePhpReferenceNames(normalizedMember, localName, { symbolKind: local.kind }) === "equivalent";
|
|
2404
|
+
}
|
|
2405
|
+
return localName === normalizedMember;
|
|
2406
|
+
}
|
|
2407
|
+
function findDirectLocalsWithinNode(locals, member, container, targetContext, normalizeIdentifier, predicate) {
|
|
2408
|
+
const matches = [];
|
|
372
2409
|
const containerStart = container.startIndex;
|
|
373
2410
|
const containerEnd = container.endIndex;
|
|
374
2411
|
const normalizedMember = normalizeIdentifier(member);
|
|
375
2412
|
for (const local of locals) {
|
|
376
2413
|
const startIndex = local.range.start.index;
|
|
377
2414
|
const endIndex = local.range.end.index;
|
|
378
|
-
if (
|
|
2415
|
+
if (!matchesReceiverMemberName(local, normalizedMember, targetContext, normalizeIdentifier) ||
|
|
379
2416
|
startIndex === undefined ||
|
|
380
2417
|
endIndex === undefined ||
|
|
381
2418
|
startIndex < containerStart ||
|
|
@@ -391,8 +2428,18 @@ function findDirectLocalWithinNode(locals, member, container, targetContext, nor
|
|
|
391
2428
|
let isDeclarationParent = true;
|
|
392
2429
|
while (current && current !== container) {
|
|
393
2430
|
const isDirectBody = (current.type === "statement_block" || current.type === "block") && current.parent === container;
|
|
2431
|
+
// A transparent container (Kotlin's `companion object`) donates its body's members to its
|
|
2432
|
+
// own enclosing class; the grammar never lets one nest under any other declaration, so its
|
|
2433
|
+
// body never counts as evidence of a foreign nested class here.
|
|
2434
|
+
const isTransparentBody = current.type === "class_body" &&
|
|
2435
|
+
!!current.parent &&
|
|
2436
|
+
TRANSPARENT_MEMBER_CONTAINER_TYPES[current.parent.type] === true;
|
|
2437
|
+
const definingDeclarator = current.type === "function_definition" ? current.childForFieldName("declarator") : null;
|
|
2438
|
+
const isOwnFunctionDeclaration = !!definingDeclarator && definingDeclarator.startIndex <= startIndex && definingDeclarator.endIndex >= endIndex;
|
|
394
2439
|
if (!isDeclarationParent &&
|
|
395
2440
|
!isDirectBody &&
|
|
2441
|
+
!isOwnFunctionDeclaration &&
|
|
2442
|
+
!isTransparentBody &&
|
|
396
2443
|
((current.type === "class_body" && current.parent !== container) ||
|
|
397
2444
|
NESTED_MEMBER_LOCAL_CONTAINERS.has(current.type))) {
|
|
398
2445
|
current = null;
|
|
@@ -402,30 +2449,196 @@ function findDirectLocalWithinNode(locals, member, container, targetContext, nor
|
|
|
402
2449
|
current = current.parent;
|
|
403
2450
|
}
|
|
404
2451
|
if (current && (!predicate || predicate(local)))
|
|
405
|
-
|
|
2452
|
+
matches.push(local);
|
|
406
2453
|
}
|
|
407
|
-
return
|
|
2454
|
+
return matches;
|
|
408
2455
|
}
|
|
409
|
-
function
|
|
410
|
-
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
2456
|
+
function appendDirectKeywordMembers(locals, member, container, targetContext, normalizeIdentifier, predicate, matches) {
|
|
2457
|
+
const containerStart = container.startIndex;
|
|
2458
|
+
const containerEnd = container.endIndex;
|
|
2459
|
+
const normalizedMember = normalizeIdentifier(member);
|
|
2460
|
+
for (const local of locals) {
|
|
2461
|
+
const startIndex = local.range.start.index;
|
|
2462
|
+
const endIndex = local.range.end.index;
|
|
2463
|
+
if (!matchesReceiverMemberName(local, normalizedMember, targetContext, normalizeIdentifier) ||
|
|
2464
|
+
startIndex === undefined ||
|
|
2465
|
+
endIndex === undefined ||
|
|
2466
|
+
startIndex < containerStart ||
|
|
2467
|
+
endIndex > containerEnd) {
|
|
2468
|
+
continue;
|
|
418
2469
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
2470
|
+
const start = local.range.start;
|
|
2471
|
+
const position = {
|
|
2472
|
+
row: start.line - 1,
|
|
2473
|
+
column: start.column - 1,
|
|
2474
|
+
};
|
|
2475
|
+
const declarationNode = targetContext.tree.rootNode.descendantForPosition(position, position);
|
|
2476
|
+
if (!isDirectKeywordMemberDeclaration(declarationNode, container) || (predicate && !predicate(local))) {
|
|
2477
|
+
continue;
|
|
422
2478
|
}
|
|
2479
|
+
matches.push(local);
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
const SWIFT_PARAMETER_OWNER_TYPES = {
|
|
2483
|
+
init_declaration: true,
|
|
2484
|
+
function_declaration: true,
|
|
2485
|
+
deinit_declaration: true,
|
|
2486
|
+
subscript_declaration: true,
|
|
2487
|
+
lambda_literal: true,
|
|
2488
|
+
protocol_function_declaration: true,
|
|
2489
|
+
};
|
|
2490
|
+
/**
|
|
2491
|
+
* Swift parameters and function-body locals share the type's byte span, but
|
|
2492
|
+
* `self.name` names the member. A parameter or local that shadows the bare name
|
|
2493
|
+
* is not a member candidate. Only `simple_identifier` names are Swift.
|
|
2494
|
+
*/
|
|
2495
|
+
function isSwiftShadowingNonMember(declarationNode) {
|
|
2496
|
+
if (declarationNode.type !== "simple_identifier")
|
|
2497
|
+
return false;
|
|
2498
|
+
let current = declarationNode.parent;
|
|
2499
|
+
while (current) {
|
|
2500
|
+
if (current.type === "parameter" && current.parent && SWIFT_PARAMETER_OWNER_TYPES[current.parent.type]) {
|
|
2501
|
+
return true;
|
|
2502
|
+
}
|
|
2503
|
+
if (current.type === "function_body" ||
|
|
2504
|
+
current.type === "lambda_literal" ||
|
|
2505
|
+
current.type === "computed_property" ||
|
|
2506
|
+
current.type === "willset_didset_block") {
|
|
2507
|
+
return true;
|
|
2508
|
+
}
|
|
2509
|
+
if (current.type === "class_declaration" ||
|
|
2510
|
+
current.type === "protocol_declaration" ||
|
|
2511
|
+
current.type === "enum_class_body") {
|
|
2512
|
+
return false;
|
|
2513
|
+
}
|
|
2514
|
+
current = current.parent;
|
|
2515
|
+
}
|
|
2516
|
+
return false;
|
|
2517
|
+
}
|
|
2518
|
+
/** Parameter lists whose entries are locals of the function, not members of its type. */
|
|
2519
|
+
const PARAMETER_LIST_TYPES = new Set([
|
|
2520
|
+
"formal_parameters",
|
|
2521
|
+
"parameter_list",
|
|
2522
|
+
"parameters",
|
|
2523
|
+
"function_value_parameters",
|
|
2524
|
+
]);
|
|
2525
|
+
/**
|
|
2526
|
+
* A parameter that also declares a property of the enclosing type: a TypeScript parameter
|
|
2527
|
+
* property (`constructor(readonly path: string)`) or a PHP promoted constructor property.
|
|
2528
|
+
*/
|
|
2529
|
+
function isPropertyParameter(declarationNode, list) {
|
|
2530
|
+
let parameter = declarationNode;
|
|
2531
|
+
while (parameter && parameter.parent !== list)
|
|
2532
|
+
parameter = parameter.parent;
|
|
2533
|
+
if (!parameter)
|
|
2534
|
+
return false;
|
|
2535
|
+
if (parameter.type === "property_promotion_parameter")
|
|
423
2536
|
return true;
|
|
2537
|
+
return parameter.namedChildren.some((child) => child.type === "accessibility_modifier" || child.type === "override_modifier" || child.text === "readonly");
|
|
2538
|
+
}
|
|
2539
|
+
export function isDirectKeywordMemberDeclaration(declarationNode, container) {
|
|
2540
|
+
if (isSwiftShadowingNonMember(declarationNode))
|
|
2541
|
+
return false;
|
|
2542
|
+
if (nearestMemberContainer(declarationNode) !== container)
|
|
2543
|
+
return false;
|
|
2544
|
+
let functionDepth = 0;
|
|
2545
|
+
let current = declarationNode;
|
|
2546
|
+
while (current && current !== container) {
|
|
2547
|
+
// Swift local functions can nest inside a member; only the outer function is a member.
|
|
2548
|
+
if (current.type === "function_declaration" && ++functionDepth > 1)
|
|
2549
|
+
return false;
|
|
2550
|
+
const isMethodBody = (current.type === "block" || current.type === "compound_statement" || current.type === "statement_block") &&
|
|
2551
|
+
current.parent !== container;
|
|
2552
|
+
if (isMethodBody)
|
|
2553
|
+
return false;
|
|
2554
|
+
// A method parameter is a local of that method, unless it also declares a property.
|
|
2555
|
+
if (PARAMETER_LIST_TYPES.has(current.type))
|
|
2556
|
+
return isPropertyParameter(declarationNode, current);
|
|
2557
|
+
current = current.parent;
|
|
2558
|
+
}
|
|
2559
|
+
return current === container;
|
|
2560
|
+
}
|
|
2561
|
+
/**
|
|
2562
|
+
* Every Rust `impl` block whose self type is `typeName`, across inherent (`impl Circle`),
|
|
2563
|
+
* trait (`impl Shape for Circle`), and generic (`impl<T> Box<T>`) forms. The shared
|
|
2564
|
+
* self-type extraction keeps navigation and detailed-graph member ownership in agreement.
|
|
2565
|
+
*/
|
|
2566
|
+
function findRustImplsForType(root, typeName, source, sup) {
|
|
2567
|
+
const normalized = sup.normalizeIdentifier(typeName);
|
|
2568
|
+
const found = [];
|
|
2569
|
+
const visit = (node) => {
|
|
2570
|
+
if (node.type === "impl_item") {
|
|
2571
|
+
const selfType = rustImplSelfTypeNode(node, sup);
|
|
2572
|
+
if (selfType && sup.normalizeIdentifier(sliceText(selfType, source)) === normalized)
|
|
2573
|
+
found.push(node);
|
|
2574
|
+
}
|
|
2575
|
+
for (const child of node.namedChildren)
|
|
2576
|
+
visit(child);
|
|
424
2577
|
};
|
|
425
2578
|
visit(root);
|
|
426
2579
|
return found;
|
|
427
2580
|
}
|
|
428
|
-
|
|
2581
|
+
async function selectRustReceiverMember(index, receiverDef, member, context, locals, knownArgumentCount) {
|
|
2582
|
+
const inherent = [];
|
|
2583
|
+
const implemented = [];
|
|
2584
|
+
const traitMembers = new Map();
|
|
2585
|
+
const traitOwners = [];
|
|
2586
|
+
const module = index.byFile.get(fileIdentityKey(receiverDef.file));
|
|
2587
|
+
if (!module)
|
|
2588
|
+
return undefined;
|
|
2589
|
+
for (const impl of findRustImplsForType(context.tree.rootNode, receiverDef.localName, context.source, context.sup)) {
|
|
2590
|
+
const self = rustImplSelfTypeNode(impl, context.sup);
|
|
2591
|
+
const trait = impl.namedChildren.find((child) => child.type === "type_identifier" && child.id !== self?.id);
|
|
2592
|
+
const target = trait ? implemented : inherent;
|
|
2593
|
+
appendDirectKeywordMembers(locals, member, impl, context, context.sup.normalizeIdentifier, undefined, target);
|
|
2594
|
+
if (!trait)
|
|
2595
|
+
continue;
|
|
2596
|
+
const traitName = sliceText(trait, context.source);
|
|
2597
|
+
const traitDef = resolveNamedMemberContainer(index, module, traitName, context.sup.normalizeIdentifier);
|
|
2598
|
+
if (!traitDef)
|
|
2599
|
+
continue;
|
|
2600
|
+
const ref = await keywordClassRefFromDef(index, traitDef);
|
|
2601
|
+
if (!ref || ref.container.type !== "trait_item")
|
|
2602
|
+
continue;
|
|
2603
|
+
const key = keywordClassKey(traitDef);
|
|
2604
|
+
if (traitMembers.has(key))
|
|
2605
|
+
continue;
|
|
2606
|
+
const matches = [];
|
|
2607
|
+
appendDirectKeywordMembers(ref.module.locals, member, ref.container, ref.context, ref.context.sup.normalizeIdentifier, undefined, matches);
|
|
2608
|
+
traitMembers.set(key, matches);
|
|
2609
|
+
traitOwners.push(key);
|
|
2610
|
+
}
|
|
2611
|
+
const model = {
|
|
2612
|
+
ownerKey: (owner) => owner,
|
|
2613
|
+
members: (owner) => {
|
|
2614
|
+
if (owner === "inherent")
|
|
2615
|
+
return inherent;
|
|
2616
|
+
if (owner === "implemented")
|
|
2617
|
+
return implemented;
|
|
2618
|
+
return traitMembers.get(owner) ?? [];
|
|
2619
|
+
},
|
|
2620
|
+
supertypes: (owner) => {
|
|
2621
|
+
if (owner === "type")
|
|
2622
|
+
return ["inherent"];
|
|
2623
|
+
if (owner === "inherent")
|
|
2624
|
+
return ["implemented"];
|
|
2625
|
+
return owner === "implemented" ? traitOwners : [];
|
|
2626
|
+
},
|
|
2627
|
+
name: (def) => context.sup.normalizeIdentifier(def.localName),
|
|
2628
|
+
key: (def) => def.callable?.key ?? keywordClassKey(def),
|
|
2629
|
+
callable: (def) => def.callable,
|
|
2630
|
+
scope: () => "any",
|
|
2631
|
+
visible: () => true,
|
|
2632
|
+
};
|
|
2633
|
+
const selected = selectMember(["type"], model, {
|
|
2634
|
+
name: context.sup.normalizeIdentifier(member),
|
|
2635
|
+
argumentCount: knownArgumentCount ?? null,
|
|
2636
|
+
scope: "any",
|
|
2637
|
+
useFile: receiverDef.file,
|
|
2638
|
+
keepUniqueArityMismatch: true,
|
|
2639
|
+
});
|
|
2640
|
+
return selected.status === "unique" ? selected.member : undefined;
|
|
2641
|
+
}
|
|
429
2642
|
function goMethodReceiverTypeName(methodNode, source, sup) {
|
|
430
2643
|
const receiver = methodNode.childForFieldName("receiver");
|
|
431
2644
|
if (!receiver)
|
|
@@ -487,9 +2700,10 @@ function goMethodsNamedOnType(locals, member, typeName, targetContext, normalize
|
|
|
487
2700
|
if (node.type === "method_declaration") {
|
|
488
2701
|
const receiverType = goMethodReceiverTypeName(node, targetContext.source, targetContext.sup);
|
|
489
2702
|
if (receiverType && normalizeIdentifier(receiverType) === normalizeIdentifier(typeName)) {
|
|
490
|
-
const local
|
|
491
|
-
|
|
492
|
-
|
|
2703
|
+
for (const local of findLocalsWithinNode(locals, member, node, normalizeIdentifier)) {
|
|
2704
|
+
if (!matches.includes(local))
|
|
2705
|
+
matches.push(local);
|
|
2706
|
+
}
|
|
493
2707
|
}
|
|
494
2708
|
return;
|
|
495
2709
|
}
|
|
@@ -499,47 +2713,105 @@ function goMethodsNamedOnType(locals, member, typeName, targetContext, normalize
|
|
|
499
2713
|
visit(targetContext.tree.rootNode);
|
|
500
2714
|
return matches;
|
|
501
2715
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
2716
|
+
/**
|
|
2717
|
+
* Kotlin extension functions declared `fun Type.member()` for a receiver type, searched across
|
|
2718
|
+
* the receiver type's own declaring file the same way `goMethodsNamedOnType` searches Go's file
|
|
2719
|
+
* for a receiver method. An ordinary class member never carries a receiver-type prefix, so this
|
|
2720
|
+
* never matches a real member and cannot shadow one.
|
|
2721
|
+
*/
|
|
2722
|
+
function kotlinExtensionFunctionsNamedOnType(locals, member, typeName, targetContext, normalizeIdentifier) {
|
|
2723
|
+
const matches = [];
|
|
2724
|
+
const normalizedType = normalizeIdentifier(typeName);
|
|
2725
|
+
const visit = (node) => {
|
|
2726
|
+
if (node.type === "function_declaration") {
|
|
2727
|
+
const receiverType = kotlinExtensionReceiverTypeNode(node, targetContext.sup);
|
|
2728
|
+
if (receiverType && normalizeIdentifier(sliceText(receiverType, targetContext.source)) === normalizedType) {
|
|
2729
|
+
for (const local of findLocalsWithinNode(locals, member, node, normalizeIdentifier)) {
|
|
2730
|
+
if (!matches.includes(local))
|
|
2731
|
+
matches.push(local);
|
|
2732
|
+
}
|
|
2733
|
+
}
|
|
2734
|
+
return;
|
|
2735
|
+
}
|
|
2736
|
+
for (const child of node.namedChildren)
|
|
2737
|
+
visit(child);
|
|
2738
|
+
};
|
|
2739
|
+
visit(targetContext.tree.rootNode);
|
|
2740
|
+
return matches;
|
|
2741
|
+
}
|
|
2742
|
+
async function selectGoReceiverMember(index, receiverDef, member, normalizeIdentifier, knownArgumentCount) {
|
|
2743
|
+
const peers = getCompilationUnitPeers(index, receiverDef.file).files;
|
|
2744
|
+
const files = await Promise.all([...peers].map(async (file) => {
|
|
2745
|
+
const module = index.byFile.get(fileIdentityKey(file));
|
|
2746
|
+
if (!module)
|
|
2747
|
+
return null;
|
|
2748
|
+
const context = await ensureParsedContext(file, index.parsed?.get(fileIdentityKey(file)), index.languageExtensions);
|
|
2749
|
+
return context.sup.id === "go" ? { module, context } : null;
|
|
2750
|
+
}));
|
|
2751
|
+
const contexts = files.filter((file) => file !== null);
|
|
2752
|
+
const members = new Map();
|
|
2753
|
+
const embedded = new Map();
|
|
2754
|
+
const seen = new Set();
|
|
2755
|
+
let level = [receiverDef.localName];
|
|
2756
|
+
for (let depth = 0; depth < RECEIVER_HIERARCHY_DEPTH && level.length; depth += 1) {
|
|
507
2757
|
const next = [];
|
|
508
|
-
for (const
|
|
509
|
-
if (
|
|
2758
|
+
for (const typeName of level) {
|
|
2759
|
+
if (seen.has(typeName))
|
|
510
2760
|
continue;
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
2761
|
+
seen.add(typeName);
|
|
2762
|
+
const matches = [];
|
|
2763
|
+
const bases = [];
|
|
2764
|
+
for (const { module, context } of contexts) {
|
|
2765
|
+
const normalize = context.sup.normalizeIdentifier;
|
|
2766
|
+
matches.push(...goMethodsNamedOnType(module.locals, member, typeName, context, normalize));
|
|
2767
|
+
const spec = goTypeSpecNamed(context.tree.rootNode, typeName, context.source, normalize);
|
|
2768
|
+
if (!spec)
|
|
2769
|
+
continue;
|
|
2770
|
+
matches.push(...findDirectLocalsWithinNode(module.locals, member, spec, context, normalize).filter((def) => def.isMember));
|
|
2771
|
+
for (const base of goEmbeddedTypeNames(typeName, context, normalize)) {
|
|
2772
|
+
if (!bases.includes(base))
|
|
2773
|
+
bases.push(base);
|
|
2774
|
+
if (!seen.has(base) && !next.includes(base))
|
|
2775
|
+
next.push(base);
|
|
2776
|
+
}
|
|
519
2777
|
}
|
|
2778
|
+
members.set(typeName, matches);
|
|
2779
|
+
embedded.set(typeName, bases);
|
|
520
2780
|
}
|
|
521
|
-
if (matches.length === 1)
|
|
522
|
-
return matches[0];
|
|
523
|
-
if (matches.length > 1)
|
|
524
|
-
return undefined;
|
|
525
2781
|
level = next;
|
|
526
2782
|
}
|
|
527
|
-
|
|
2783
|
+
const model = {
|
|
2784
|
+
ownerKey: (owner) => owner,
|
|
2785
|
+
members: (owner) => members.get(owner) ?? [],
|
|
2786
|
+
supertypes: (owner) => embedded.get(owner) ?? [],
|
|
2787
|
+
name: (def) => normalizeIdentifier(def.localName),
|
|
2788
|
+
key: (def) => def.callable?.key ?? keywordClassKey(def),
|
|
2789
|
+
callable: (def) => def.callable,
|
|
2790
|
+
scope: () => "any",
|
|
2791
|
+
visible: () => true,
|
|
2792
|
+
};
|
|
2793
|
+
const selected = selectMember([receiverDef.localName], model, {
|
|
2794
|
+
name: normalizeIdentifier(member),
|
|
2795
|
+
argumentCount: knownArgumentCount ?? null,
|
|
2796
|
+
scope: "any",
|
|
2797
|
+
useFile: receiverDef.file,
|
|
2798
|
+
keepUniqueArityMismatch: true,
|
|
2799
|
+
});
|
|
2800
|
+
return selected.status === "unique" ? selected.member : undefined;
|
|
528
2801
|
}
|
|
529
|
-
const PYTHON_SUPERTYPE_DEPTH = 16;
|
|
530
2802
|
function pythonClassKey(def) {
|
|
531
2803
|
const start = def.range.start;
|
|
532
2804
|
return `${fileIdentityKey(def.file)}:${start.index ?? `${start.line}:${start.column}`}`;
|
|
533
2805
|
}
|
|
534
|
-
async function resolvePythonReceiverMember(index, mod, node, obj, member, source, sup, resolveExpression) {
|
|
535
|
-
const classRef = await pythonReceiverClassRef(index, mod, node, obj, source, sup, resolveExpression);
|
|
2806
|
+
async function resolvePythonReceiverMember(index, mod, node, obj, member, source, sup, resolveExpression, proof) {
|
|
2807
|
+
const classRef = await pythonReceiverClassRef(index, mod, node, obj, source, sup, resolveExpression, proof);
|
|
536
2808
|
if (!classRef)
|
|
537
2809
|
return undefined;
|
|
538
|
-
return lookupPythonClassMember(index, classRef, member);
|
|
2810
|
+
return lookupPythonClassMember(index, classRef.ref, member, classRef.startAtSupertype);
|
|
539
2811
|
}
|
|
540
|
-
async function pythonReceiverClassRef(index, mod, node, obj, source, sup, resolveExpression) {
|
|
541
|
-
const
|
|
542
|
-
if (
|
|
2812
|
+
async function pythonReceiverClassRef(index, mod, node, obj, source, sup, resolveExpression, proof) {
|
|
2813
|
+
const receiver = proof ?? classifyReceiver(sup, obj, source, null, 0, obj.parent ?? obj, noLexicalBinding);
|
|
2814
|
+
if (receiver.kind === "own-type" || receiver.kind === "supertype") {
|
|
543
2815
|
const container = findEnclosingClassContainer(node);
|
|
544
2816
|
if (!container)
|
|
545
2817
|
return null;
|
|
@@ -559,10 +2831,11 @@ async function pythonReceiverClassRef(index, mod, node, obj, source, sup, resolv
|
|
|
559
2831
|
});
|
|
560
2832
|
if (!def)
|
|
561
2833
|
return null;
|
|
562
|
-
|
|
2834
|
+
const ref = await pythonClassRefFromDef(index, def);
|
|
2835
|
+
return ref ? { ref, startAtSupertype: receiver.kind === "supertype" } : null;
|
|
563
2836
|
}
|
|
564
2837
|
let classDef;
|
|
565
|
-
const constructor =
|
|
2838
|
+
const constructor = receiver.kind === "named-type" && receiver.proof !== "static-type" ? receiver.typeNode : null;
|
|
566
2839
|
if (constructor) {
|
|
567
2840
|
const result = await resolveExpression(constructor);
|
|
568
2841
|
if (result?.kind === "resolved" && result.def.kind === SymbolKind.Class) {
|
|
@@ -577,7 +2850,8 @@ async function pythonReceiverClassRef(index, mod, node, obj, source, sup, resolv
|
|
|
577
2850
|
}
|
|
578
2851
|
if (!classDef)
|
|
579
2852
|
return null;
|
|
580
|
-
|
|
2853
|
+
const ref = await pythonClassRefFromDef(index, classDef);
|
|
2854
|
+
return ref ? { ref, startAtSupertype: false } : null;
|
|
581
2855
|
}
|
|
582
2856
|
async function pythonClassRefFromDef(index, def) {
|
|
583
2857
|
const module = index.byFile.get(fileIdentityKey(def.file));
|
|
@@ -599,13 +2873,13 @@ async function pythonClassRefFromDef(index, def) {
|
|
|
599
2873
|
}
|
|
600
2874
|
function pythonMembersOnClass(classRef, member) {
|
|
601
2875
|
const normalizeIdentifier = classRef.context.sup.normalizeIdentifier;
|
|
602
|
-
const direct =
|
|
603
|
-
if (direct)
|
|
604
|
-
return
|
|
605
|
-
|
|
606
|
-
return attribute ? [attribute] : [];
|
|
2876
|
+
const direct = findDirectLocalsWithinNode(classRef.module.locals, member, classRef.container, classRef.context, normalizeIdentifier);
|
|
2877
|
+
if (direct.length)
|
|
2878
|
+
return direct;
|
|
2879
|
+
return findPythonInstanceAttributesWithinClass(classRef.module.locals, member, classRef.container, classRef.context, normalizeIdentifier);
|
|
607
2880
|
}
|
|
608
|
-
function
|
|
2881
|
+
function findPythonInstanceAttributesWithinClass(locals, member, container, targetContext, normalizeIdentifier) {
|
|
2882
|
+
const matches = [];
|
|
609
2883
|
const containerStart = container.startIndex;
|
|
610
2884
|
const containerEnd = container.endIndex;
|
|
611
2885
|
const normalizedMember = normalizeIdentifier(member);
|
|
@@ -626,9 +2900,9 @@ function findPythonInstanceAttributeWithinClass(locals, member, container, targe
|
|
|
626
2900
|
};
|
|
627
2901
|
const nameNode = targetContext.tree.rootNode.descendantForPosition(position, position);
|
|
628
2902
|
if (isPythonReceiverAttributeAssignmentName(nameNode))
|
|
629
|
-
|
|
2903
|
+
matches.push(local);
|
|
630
2904
|
}
|
|
631
|
-
return
|
|
2905
|
+
return matches;
|
|
632
2906
|
}
|
|
633
2907
|
function pythonBaseIdentifierNodes(classNode) {
|
|
634
2908
|
const bases = classNode.childForFieldName("superclasses") ??
|
|
@@ -664,17 +2938,15 @@ function resolvePythonNamedClass(index, mod, name) {
|
|
|
664
2938
|
return candidates[0];
|
|
665
2939
|
if (candidates.length > 1)
|
|
666
2940
|
return undefined;
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
return result;
|
|
677
|
-
}
|
|
2941
|
+
const explicitBinding = effectiveExplicitBinding(mod.imports, "python", (binding) => binding.kind === "named" && binding.local === name);
|
|
2942
|
+
if (explicitBinding?.kind === "named") {
|
|
2943
|
+
const result = resolveImported(index, explicitBinding, explicitBinding.imported);
|
|
2944
|
+
if (result && !("namespace" in result) && result.kind === SymbolKind.Class)
|
|
2945
|
+
return result;
|
|
2946
|
+
}
|
|
2947
|
+
const starDecision = resolveStarImportedName(index, mod, "python", name);
|
|
2948
|
+
if (starDecision.status === "resolved") {
|
|
2949
|
+
return starDecision.definition.kind === SymbolKind.Class ? starDecision.definition : undefined;
|
|
678
2950
|
}
|
|
679
2951
|
const exported = resolveExport(index, mod.file, name, { preferredKind: SymbolKind.Class, allowLocalFallback: false });
|
|
680
2952
|
if (exported?.kind === "resolved" && exported.def.kind === SymbolKind.Class)
|
|
@@ -699,33 +2971,26 @@ async function pythonBaseClassRefs(index, classRef) {
|
|
|
699
2971
|
}
|
|
700
2972
|
return refs;
|
|
701
2973
|
}
|
|
702
|
-
async function lookupPythonClassMember(index, start, member) {
|
|
703
|
-
const
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
for (const hit of pythonMembersOnClass(base, member)) {
|
|
715
|
-
const key = pythonClassKey(hit);
|
|
716
|
-
if (seenMatch.has(key))
|
|
717
|
-
continue;
|
|
718
|
-
seenMatch.add(key);
|
|
719
|
-
matches.push(hit);
|
|
720
|
-
}
|
|
2974
|
+
async function lookupPythonClassMember(index, start, member, startAtSupertype = false) {
|
|
2975
|
+
const membersByOwner = new Map();
|
|
2976
|
+
const parentsByOwner = new Map();
|
|
2977
|
+
const visited = new Set([pythonClassKey(start.def)]);
|
|
2978
|
+
let level = [start];
|
|
2979
|
+
for (let depth = 0; depth < RECEIVER_HIERARCHY_DEPTH && level.length; depth += 1) {
|
|
2980
|
+
let named = false;
|
|
2981
|
+
for (const ref of level) {
|
|
2982
|
+
const members = startAtSupertype && !depth ? [] : pythonMembersOnClass(ref, member);
|
|
2983
|
+
membersByOwner.set(pythonClassKey(ref.def), members);
|
|
2984
|
+
if (members.length)
|
|
2985
|
+
named = true;
|
|
721
2986
|
}
|
|
722
|
-
if (
|
|
723
|
-
|
|
724
|
-
if (matches.length > 1)
|
|
725
|
-
return undefined;
|
|
2987
|
+
if (named)
|
|
2988
|
+
break;
|
|
726
2989
|
const next = [];
|
|
727
|
-
for (const
|
|
728
|
-
|
|
2990
|
+
for (const ref of level) {
|
|
2991
|
+
const parents = await pythonBaseClassRefs(index, ref);
|
|
2992
|
+
parentsByOwner.set(pythonClassKey(ref.def), parents);
|
|
2993
|
+
for (const parent of parents) {
|
|
729
2994
|
const key = pythonClassKey(parent.def);
|
|
730
2995
|
if (visited.has(key))
|
|
731
2996
|
continue;
|
|
@@ -735,9 +3000,23 @@ async function lookupPythonClassMember(index, start, member) {
|
|
|
735
3000
|
}
|
|
736
3001
|
level = next;
|
|
737
3002
|
}
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
3003
|
+
const model = {
|
|
3004
|
+
ownerKey: (ref) => pythonClassKey(ref.def),
|
|
3005
|
+
members: (ref) => membersByOwner.get(pythonClassKey(ref.def)) ?? [],
|
|
3006
|
+
supertypes: (ref) => parentsByOwner.get(pythonClassKey(ref.def)) ?? [],
|
|
3007
|
+
name: (def) => def.localName,
|
|
3008
|
+
key: (def) => def.callable?.key ?? pythonClassKey(def),
|
|
3009
|
+
callable: (def) => def.callable,
|
|
3010
|
+
scope: () => "any",
|
|
3011
|
+
visible: () => true,
|
|
3012
|
+
};
|
|
3013
|
+
const selected = selectMember([start], model, {
|
|
3014
|
+
name: member,
|
|
3015
|
+
argumentCount: null,
|
|
3016
|
+
scope: "any",
|
|
3017
|
+
useFile: start.def.file,
|
|
3018
|
+
startAtAncestor: startAtSupertype,
|
|
3019
|
+
});
|
|
3020
|
+
return selected.status === "unique" ? selected.member : undefined;
|
|
742
3021
|
}
|
|
743
3022
|
//# sourceMappingURL=navigation-goto.js.map
|