@lzehrung/codegraph 2.3.31 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -137
- package/codegraph-skill/codegraph/SKILL.md +5 -3
- package/dist/agent/artifact.d.ts +2 -0
- package/dist/agent/artifact.d.ts.map +1 -1
- package/dist/agent/artifact.js.map +1 -1
- package/dist/agent/search.d.ts.map +1 -1
- package/dist/agent/search.js +3 -0
- package/dist/agent/search.js.map +1 -1
- package/dist/agent/session.d.ts +3 -0
- package/dist/agent/session.d.ts.map +1 -1
- package/dist/agent/session.js +10 -2
- package/dist/agent/session.js.map +1 -1
- package/dist/bin/cli.js +42243 -29202
- package/dist/bin/query-index-worker.js +73 -11
- package/dist/cli/affected.d.ts.map +1 -1
- package/dist/cli/affected.js +4 -1
- package/dist/cli/affected.js.map +1 -1
- package/dist/cli/command-catalog.js +1 -1
- package/dist/cli/command-catalog.js.map +1 -1
- package/dist/cli/graph-queries.d.ts.map +1 -1
- package/dist/cli/graph-queries.js +27 -1
- package/dist/cli/graph-queries.js.map +1 -1
- package/dist/cli/help.d.ts +2 -2
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +4 -2
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/options.js +2 -2
- package/dist/cli/options.js.map +1 -1
- package/dist/drift/artifact.d.ts.map +1 -1
- package/dist/drift/artifact.js +15 -18
- package/dist/drift/artifact.js.map +1 -1
- package/dist/drift/compare.js +1 -1
- package/dist/drift/compare.js.map +1 -1
- package/dist/drift/snapshot.d.ts.map +1 -1
- package/dist/drift/snapshot.js +5 -6
- package/dist/drift/snapshot.js.map +1 -1
- package/dist/drift/types.d.ts +2 -0
- package/dist/drift/types.d.ts.map +1 -1
- package/dist/drift/types.js.map +1 -1
- package/dist/graph-builder.d.ts.map +1 -1
- package/dist/graph-builder.js +4 -6
- package/dist/graph-builder.js.map +1 -1
- package/dist/graph-edge-collector.d.ts +3 -0
- package/dist/graph-edge-collector.d.ts.map +1 -1
- package/dist/graph-edge-collector.js +8 -5
- package/dist/graph-edge-collector.js.map +1 -1
- package/dist/graphs/angularjs.d.ts.map +1 -1
- package/dist/graphs/angularjs.js +2 -1
- package/dist/graphs/angularjs.js.map +1 -1
- package/dist/graphs/edge-resolution.d.ts +4 -1
- package/dist/graphs/edge-resolution.d.ts.map +1 -1
- package/dist/graphs/edge-resolution.js +26 -2
- package/dist/graphs/edge-resolution.js.map +1 -1
- package/dist/graphs/specifiers.d.ts.map +1 -1
- package/dist/graphs/specifiers.js +6 -2
- package/dist/graphs/specifiers.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/ast.d.ts +20 -0
- package/dist/graphs/symbol-graph-detailed/ast.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/ast.js +154 -15
- package/dist/graphs/symbol-graph-detailed/ast.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/edge-passes.d.ts +51 -8
- package/dist/graphs/symbol-graph-detailed/edge-passes.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/edge-passes.js +823 -132
- package/dist/graphs/symbol-graph-detailed/edge-passes.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/import-aliases.d.ts +2 -1
- package/dist/graphs/symbol-graph-detailed/import-aliases.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/import-aliases.js +139 -7
- package/dist/graphs/symbol-graph-detailed/import-aliases.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/member-chains.d.ts +1 -0
- package/dist/graphs/symbol-graph-detailed/member-chains.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/member-chains.js +3 -1
- package/dist/graphs/symbol-graph-detailed/member-chains.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/receiver-calls.d.ts +128 -15
- package/dist/graphs/symbol-graph-detailed/receiver-calls.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/receiver-calls.js +873 -93
- package/dist/graphs/symbol-graph-detailed/receiver-calls.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed.js +434 -40
- package/dist/graphs/symbol-graph-detailed.js.map +1 -1
- package/dist/graphs/symbol-graph.d.ts +2 -0
- package/dist/graphs/symbol-graph.d.ts.map +1 -1
- package/dist/graphs/symbol-graph.js +34 -16
- package/dist/graphs/symbol-graph.js.map +1 -1
- package/dist/impact/call-compatibility/providers/index.d.ts +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 +310 -516
- 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 +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/indexer/ambiguous-resolution.d.ts +11 -0
- package/dist/indexer/ambiguous-resolution.d.ts.map +1 -0
- package/dist/indexer/ambiguous-resolution.js +19 -0
- package/dist/indexer/ambiguous-resolution.js.map +1 -0
- package/dist/indexer/build-cache/module-cache.d.ts +38 -0
- package/dist/indexer/build-cache/module-cache.d.ts.map +1 -1
- package/dist/indexer/build-cache/module-cache.js +151 -9
- package/dist/indexer/build-cache/module-cache.js.map +1 -1
- package/dist/indexer/build-cache/options.d.ts +16 -43
- package/dist/indexer/build-cache/options.d.ts.map +1 -1
- package/dist/indexer/build-cache/options.js +16 -43
- package/dist/indexer/build-cache/options.js.map +1 -1
- package/dist/indexer/build-cache/project-snapshot.d.ts +1 -0
- package/dist/indexer/build-cache/project-snapshot.d.ts.map +1 -1
- package/dist/indexer/build-cache/project-snapshot.js +117 -24
- package/dist/indexer/build-cache/project-snapshot.js.map +1 -1
- package/dist/indexer/build-cache/resolver-environment.d.ts.map +1 -1
- package/dist/indexer/build-cache/resolver-environment.js +32 -2
- package/dist/indexer/build-cache/resolver-environment.js.map +1 -1
- package/dist/indexer/build-cache.d.ts +1 -1
- package/dist/indexer/build-cache.d.ts.map +1 -1
- package/dist/indexer/build-cache.js +1 -1
- package/dist/indexer/build-cache.js.map +1 -1
- package/dist/indexer/build-index.d.ts.map +1 -1
- package/dist/indexer/build-index.js +593 -22
- package/dist/indexer/build-index.js.map +1 -1
- package/dist/indexer/call-hierarchy.js +1 -1
- package/dist/indexer/call-hierarchy.js.map +1 -1
- package/dist/indexer/compilation-units.d.ts +101 -0
- package/dist/indexer/compilation-units.d.ts.map +1 -0
- package/dist/indexer/compilation-units.js +605 -0
- package/dist/indexer/compilation-units.js.map +1 -0
- package/dist/indexer/cpp-callables.d.ts +20 -0
- package/dist/indexer/cpp-callables.d.ts.map +1 -0
- package/dist/indexer/cpp-callables.js +554 -0
- package/dist/indexer/cpp-callables.js.map +1 -0
- package/dist/indexer/declaration-visibility.d.ts +36 -2
- package/dist/indexer/declaration-visibility.d.ts.map +1 -1
- package/dist/indexer/declaration-visibility.js +136 -2
- package/dist/indexer/declaration-visibility.js.map +1 -1
- package/dist/indexer/expand-star-imports.d.ts.map +1 -1
- package/dist/indexer/expand-star-imports.js +28 -6
- package/dist/indexer/expand-star-imports.js.map +1 -1
- package/dist/indexer/import-types.d.ts +37 -0
- package/dist/indexer/import-types.d.ts.map +1 -1
- package/dist/indexer/import-types.js +24 -1
- package/dist/indexer/import-types.js.map +1 -1
- package/dist/indexer/imports/context.d.ts +11 -1
- package/dist/indexer/imports/context.d.ts.map +1 -1
- package/dist/indexer/imports/import-binding-tables.d.ts +12 -1
- package/dist/indexer/imports/import-binding-tables.d.ts.map +1 -1
- package/dist/indexer/imports/import-binding-tables.js +144 -19
- 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 +18 -6
- package/dist/indexer/imports/native-captures.js.map +1 -1
- package/dist/indexer/imports/python.d.ts +10 -2
- package/dist/indexer/imports/python.d.ts.map +1 -1
- package/dist/indexer/imports/python.js +91 -40
- package/dist/indexer/imports/python.js.map +1 -1
- package/dist/indexer/imports/text-import-extractors.d.ts.map +1 -1
- package/dist/indexer/imports/text-import-extractors.js +51 -12
- package/dist/indexer/imports/text-import-extractors.js.map +1 -1
- package/dist/indexer/imports.d.ts.map +1 -1
- package/dist/indexer/imports.js +23 -4
- package/dist/indexer/imports.js.map +1 -1
- package/dist/indexer/incremental-plan.d.ts +33 -0
- package/dist/indexer/incremental-plan.d.ts.map +1 -1
- package/dist/indexer/incremental-plan.js +187 -1
- package/dist/indexer/incremental-plan.js.map +1 -1
- package/dist/indexer/locals-and-exports.d.ts +2 -0
- package/dist/indexer/locals-and-exports.d.ts.map +1 -1
- package/dist/indexer/locals-and-exports.js +565 -54
- package/dist/indexer/locals-and-exports.js.map +1 -1
- package/dist/indexer/name-lookup-policies/c-family.d.ts +7 -0
- package/dist/indexer/name-lookup-policies/c-family.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/c-family.js +117 -0
- package/dist/indexer/name-lookup-policies/c-family.js.map +1 -0
- package/dist/indexer/name-lookup-policies/csharp-using-static.d.ts +11 -0
- package/dist/indexer/name-lookup-policies/csharp-using-static.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/csharp-using-static.js +136 -0
- package/dist/indexer/name-lookup-policies/csharp-using-static.js.map +1 -0
- package/dist/indexer/name-lookup-policies/implicit-self.d.ts +10 -0
- package/dist/indexer/name-lookup-policies/implicit-self.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/implicit-self.js +113 -0
- package/dist/indexer/name-lookup-policies/implicit-self.js.map +1 -0
- package/dist/indexer/name-lookup-policies/index.d.ts +3 -0
- package/dist/indexer/name-lookup-policies/index.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/index.js +106 -0
- package/dist/indexer/name-lookup-policies/index.js.map +1 -0
- package/dist/indexer/name-lookup-policies/php.d.ts +3 -0
- package/dist/indexer/name-lookup-policies/php.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/php.js +75 -0
- package/dist/indexer/name-lookup-policies/php.js.map +1 -0
- package/dist/indexer/name-resolution-types.d.ts +99 -0
- package/dist/indexer/name-resolution-types.d.ts.map +1 -0
- package/dist/indexer/name-resolution-types.js +2 -0
- package/dist/indexer/name-resolution-types.js.map +1 -0
- package/dist/indexer/name-resolution.d.ts +52 -0
- package/dist/indexer/name-resolution.d.ts.map +1 -0
- package/dist/indexer/name-resolution.js +189 -0
- package/dist/indexer/name-resolution.js.map +1 -0
- package/dist/indexer/navigation-cpp.d.ts +50 -0
- package/dist/indexer/navigation-cpp.d.ts.map +1 -0
- package/dist/indexer/navigation-cpp.js +654 -0
- package/dist/indexer/navigation-cpp.js.map +1 -0
- package/dist/indexer/navigation-goto.d.ts +106 -3
- package/dist/indexer/navigation-goto.d.ts.map +1 -1
- package/dist/indexer/navigation-goto.js +2074 -137
- package/dist/indexer/navigation-goto.js.map +1 -1
- package/dist/indexer/navigation-local.d.ts +18 -2
- package/dist/indexer/navigation-local.d.ts.map +1 -1
- package/dist/indexer/navigation-local.js +498 -61
- package/dist/indexer/navigation-local.js.map +1 -1
- package/dist/indexer/navigation-php.d.ts +66 -0
- package/dist/indexer/navigation-php.d.ts.map +1 -1
- package/dist/indexer/navigation-php.js +287 -4
- package/dist/indexer/navigation-php.js.map +1 -1
- package/dist/indexer/navigation-references.d.ts +72 -5
- package/dist/indexer/navigation-references.d.ts.map +1 -1
- package/dist/indexer/navigation-references.js +620 -54
- package/dist/indexer/navigation-references.js.map +1 -1
- package/dist/indexer/navigation-resolve.d.ts +33 -2
- package/dist/indexer/navigation-resolve.d.ts.map +1 -1
- package/dist/indexer/navigation-resolve.js +446 -193
- package/dist/indexer/navigation-resolve.js.map +1 -1
- package/dist/indexer/navigation.d.ts +28 -2
- package/dist/indexer/navigation.d.ts.map +1 -1
- package/dist/indexer/navigation.js +1082 -151
- package/dist/indexer/navigation.js.map +1 -1
- package/dist/indexer/php-namespace-symbols.d.ts +66 -0
- package/dist/indexer/php-namespace-symbols.d.ts.map +1 -0
- package/dist/indexer/php-namespace-symbols.js +342 -0
- package/dist/indexer/php-namespace-symbols.js.map +1 -0
- package/dist/indexer/reference-context.d.ts +2 -0
- package/dist/indexer/reference-context.d.ts.map +1 -1
- package/dist/indexer/reference-context.js +4 -0
- package/dist/indexer/reference-context.js.map +1 -1
- package/dist/indexer/scope-nodes.d.ts +92 -13
- package/dist/indexer/scope-nodes.d.ts.map +1 -1
- package/dist/indexer/scope-nodes.js +212 -20
- package/dist/indexer/scope-nodes.js.map +1 -1
- package/dist/indexer/scope-types.d.ts +25 -1
- package/dist/indexer/scope-types.d.ts.map +1 -1
- package/dist/indexer/scope.d.ts.map +1 -1
- package/dist/indexer/scope.js +894 -45
- package/dist/indexer/scope.js.map +1 -1
- package/dist/indexer/shared-owner-identity.d.ts +53 -0
- package/dist/indexer/shared-owner-identity.d.ts.map +1 -0
- package/dist/indexer/shared-owner-identity.js +460 -0
- package/dist/indexer/shared-owner-identity.js.map +1 -0
- package/dist/indexer/shared.d.ts +1 -1
- package/dist/indexer/shared.d.ts.map +1 -1
- package/dist/indexer/shared.js +1 -1
- package/dist/indexer/shared.js.map +1 -1
- package/dist/indexer/star-import-precedence.d.ts +114 -0
- package/dist/indexer/star-import-precedence.d.ts.map +1 -0
- package/dist/indexer/star-import-precedence.js +396 -0
- package/dist/indexer/star-import-precedence.js.map +1 -0
- package/dist/indexer/symbols.d.ts +3 -1
- package/dist/indexer/symbols.d.ts.map +1 -1
- package/dist/indexer/symbols.js +58 -18
- package/dist/indexer/symbols.js.map +1 -1
- package/dist/indexer/ts-callables.d.ts +51 -0
- package/dist/indexer/ts-callables.d.ts.map +1 -0
- package/dist/indexer/ts-callables.js +264 -0
- package/dist/indexer/ts-callables.js.map +1 -0
- package/dist/indexer/types.d.ts +23 -1
- package/dist/indexer/types.d.ts.map +1 -1
- package/dist/indexer/unused-exports.d.ts +13 -0
- package/dist/indexer/unused-exports.d.ts.map +1 -0
- package/dist/indexer/unused-exports.js +159 -0
- package/dist/indexer/unused-exports.js.map +1 -0
- package/dist/indexer/workspace-symbols.d.ts.map +1 -1
- package/dist/indexer/workspace-symbols.js +3 -2
- package/dist/indexer/workspace-symbols.js.map +1 -1
- package/dist/indexer.d.ts +2 -2
- package/dist/indexer.d.ts.map +1 -1
- package/dist/indexer.js +1 -1
- package/dist/indexer.js.map +1 -1
- package/dist/languages/callable-arity.d.ts +202 -0
- package/dist/languages/callable-arity.d.ts.map +1 -0
- package/dist/languages/callable-arity.js +1182 -0
- package/dist/languages/callable-arity.js.map +1 -0
- package/dist/languages/definitions/c-family.d.ts.map +1 -1
- package/dist/languages/definitions/c-family.js +30 -1
- package/dist/languages/definitions/c-family.js.map +1 -1
- package/dist/languages/definitions/c.d.ts +4 -0
- package/dist/languages/definitions/c.d.ts.map +1 -1
- package/dist/languages/definitions/c.js +19 -0
- package/dist/languages/definitions/c.js.map +1 -1
- package/dist/languages/definitions/cpp.d.ts.map +1 -1
- package/dist/languages/definitions/cpp.js +12 -0
- package/dist/languages/definitions/cpp.js.map +1 -1
- package/dist/languages/definitions/csharp.d.ts.map +1 -1
- package/dist/languages/definitions/csharp.js +2 -8
- package/dist/languages/definitions/csharp.js.map +1 -1
- package/dist/languages/definitions/go.d.ts.map +1 -1
- package/dist/languages/definitions/go.js +3 -0
- package/dist/languages/definitions/go.js.map +1 -1
- package/dist/languages/definitions/javascript.d.ts.map +1 -1
- package/dist/languages/definitions/javascript.js +10 -1
- package/dist/languages/definitions/javascript.js.map +1 -1
- package/dist/languages/definitions/js-family.d.ts +1 -1
- package/dist/languages/definitions/js-family.d.ts.map +1 -1
- package/dist/languages/definitions/js-family.js +7 -1
- package/dist/languages/definitions/js-family.js.map +1 -1
- package/dist/languages/definitions/php.d.ts.map +1 -1
- package/dist/languages/definitions/php.js +5 -1
- package/dist/languages/definitions/php.js.map +1 -1
- package/dist/languages/definitions/typescript.d.ts.map +1 -1
- package/dist/languages/definitions/typescript.js +8 -1
- package/dist/languages/definitions/typescript.js.map +1 -1
- package/dist/languages/definitions/zig.d.ts.map +1 -1
- package/dist/languages/definitions/zig.js +2 -0
- package/dist/languages/definitions/zig.js.map +1 -1
- package/dist/mcp/handlers.js +14 -7
- package/dist/mcp/handlers.js.map +1 -1
- package/dist/review/candidates.d.ts.map +1 -1
- package/dist/review/candidates.js +1 -1
- package/dist/review/candidates.js.map +1 -1
- package/dist/review/deleted.d.ts +3 -3
- package/dist/review/deleted.d.ts.map +1 -1
- package/dist/review/deleted.js +82 -32
- package/dist/review/deleted.js.map +1 -1
- package/dist/review/report.d.ts +1 -0
- package/dist/review/report.d.ts.map +1 -1
- package/dist/review/report.js +1 -1
- package/dist/review/report.js.map +1 -1
- package/dist/review.d.ts.map +1 -1
- package/dist/review.js +1 -0
- package/dist/review.js.map +1 -1
- package/dist/sql/navigation.d.ts.map +1 -1
- package/dist/sql/navigation.js +1 -0
- package/dist/sql/navigation.js.map +1 -1
- package/dist/sql/source-graph.d.ts.map +1 -1
- package/dist/sql/source-graph.js +2 -2
- package/dist/sql/source-graph.js.map +1 -1
- package/dist/sqlite/schema.d.ts +1 -1
- package/dist/sqlite/schema.d.ts.map +1 -1
- package/dist/sqlite/schema.js +8 -2
- package/dist/sqlite/schema.js.map +1 -1
- package/dist/sqlite/write.js +2 -2
- package/dist/sqlite/write.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/util/bloom-filter.d.ts +7 -1
- package/dist/util/bloom-filter.d.ts.map +1 -1
- package/dist/util/bloom-filter.js +12 -2
- package/dist/util/bloom-filter.js.map +1 -1
- package/dist/util/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 +137 -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 +95 -3
- package/dist/util/member-access.js.map +1 -1
- package/dist/util/resolution/cargo-targets.d.ts +11 -0
- package/dist/util/resolution/cargo-targets.d.ts.map +1 -1
- package/dist/util/resolution/cargo-targets.js +174 -13
- package/dist/util/resolution/cargo-targets.js.map +1 -1
- package/dist/util/resolution/csharp.d.ts +33 -1
- package/dist/util/resolution/csharp.d.ts.map +1 -1
- package/dist/util/resolution/csharp.js +145 -30
- package/dist/util/resolution/csharp.js.map +1 -1
- package/dist/util/resolution/php.d.ts.map +1 -1
- package/dist/util/resolution/php.js +35 -8
- package/dist/util/resolution/php.js.map +1 -1
- package/dist/util/resolution/python.d.ts +9 -0
- package/dist/util/resolution/python.d.ts.map +1 -1
- package/dist/util/resolution/python.js +46 -0
- package/dist/util/resolution/python.js.map +1 -1
- package/dist/util/resolution/rust.d.ts.map +1 -1
- package/dist/util/resolution/rust.js +95 -26
- package/dist/util/resolution/rust.js.map +1 -1
- package/dist/util/resolution-candidates.d.ts +5 -0
- package/dist/util/resolution-candidates.d.ts.map +1 -1
- package/dist/util/resolution-candidates.js +16 -3
- package/dist/util/resolution-candidates.js.map +1 -1
- package/dist/util/resolution.d.ts +2 -1
- package/dist/util/resolution.d.ts.map +1 -1
- package/dist/util/resolution.js +96 -5
- package/dist/util/resolution.js.map +1 -1
- package/dist/util/specifiers.d.ts +20 -0
- package/dist/util/specifiers.d.ts.map +1 -1
- package/dist/util/specifiers.js +29 -0
- package/dist/util/specifiers.js.map +1 -1
- package/docs/mcp.md +3 -2
- package/package.json +3 -3
- 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,405 @@
|
|
|
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, receiverKeywordText, } from "../util/member-access.js";
|
|
8
|
+
import { keywordReceiverKind, MEMBER_ACCESS_ROWS, supportsReceiverMemberNavigation, } from "../util/member-access-tables.js";
|
|
9
|
+
import { cppCallableShapeForNode } from "./cpp-callables.js";
|
|
10
|
+
import { typescriptCallableContainerKey, typescriptMergedNamespaceContainers, typescriptSelectOverloadCandidate, } from "./ts-callables.js";
|
|
11
|
+
import { effectiveExplicitBinding, isExpandedStarBinding, resolveStarImportedName, resolveStarImportedNamespace, starImportPrecedence, } from "./star-import-precedence.js";
|
|
12
|
+
import { classifyReceiver, cppOutOfLineOwnerPath, cppQualifiedNameSegments, declarationIsStaticEquivalent, declarationNodeIsStatic, declaresMembers, hasStaticMemberDistinction, memberContainerDef, nearestMemberContainer, keywordReceiverCrossesDynamicBoundary, isUnprovenHeritageExpression, keywordReceiverMemberScope, kotlinExtensionReceiverTypeNode, csharpDottedNameRoot, nodeInStaticMemberContext, importTypeQuerySpecifier, receiverConstructorExpression, rustImplSelfTypeNode, supportsStaticMemberScope, TRANSPARENT_MEMBER_CONTAINER_TYPES, unwrapNamedType, } from "../graphs/symbol-graph-detailed/receiver-calls.js";
|
|
13
|
+
import { CALLABLE_DECLARATION_NODE_TYPES, getCallableArity, getCallArgumentCount, memberLookupBinding, } from "../languages/callable-arity.js";
|
|
14
|
+
import { getCompilationUnitPeers } from "./compilation-units.js";
|
|
15
|
+
import { isExportedDeclaration, isPrivateDeclaration, isGoExportedMemberName, isSwiftCrossFileHiddenSharedOwnerMember, } from "./declaration-visibility.js";
|
|
8
16
|
import { ensureParsedContext } from "./parse-context.js";
|
|
17
|
+
import { csharpLookupName, csharpQualifiedNameNode, findClosestScopeBinding, getOrBuildScopeIndex, resolveNamedDefinition, } from "./navigation-local.js";
|
|
18
|
+
import { resolveCppQualifiedMemberContainer, cppFunctionHasImplicitThis } from "./navigation-cpp.js";
|
|
9
19
|
import { okGoToResult } from "./navigation-provenance.js";
|
|
10
|
-
import {
|
|
20
|
+
import { comparePhpReferenceNames, findPhpImportAlias } from "./navigation-php.js";
|
|
21
|
+
import { resolveIndexedPhpClassReference, resolvePhpNamespaceSymbol } from "./php-namespace-symbols.js";
|
|
22
|
+
import { cjsRequireValueBinding, memberContainerForDefinition, resolveCsharpQualifiedName, resolveExport, resolveImported, resolvePhpExportByImportType, resolveImportTypeMember, } from "./navigation-resolve.js";
|
|
23
|
+
import { CSHARP_PARTIAL_CONTAINER_TYPES, getSharedOwnerIdentity, isSwiftExtensionContainer, sharedOwnerCanUseMembers, } from "./shared-owner-identity.js";
|
|
11
24
|
import { SymbolKind, } from "./types.js";
|
|
25
|
+
/**
|
|
26
|
+
* One bound for every receiver-hierarchy walk in this module: keyword `super`/`parent` lookup,
|
|
27
|
+
* Go struct embedding, and Python base classes. Each walk keeps its own visited set, so this is a
|
|
28
|
+
* resource bound rather than a cycle guard; keeping a single constant stops the three walks from
|
|
29
|
+
* drifting to different semantic cutoffs.
|
|
30
|
+
*/
|
|
31
|
+
const RECEIVER_HIERARCHY_DEPTH = 16;
|
|
32
|
+
function collectSharedOwnerContainers(root, languageId, out) {
|
|
33
|
+
if (languageId === "csharp") {
|
|
34
|
+
if (CSHARP_PARTIAL_CONTAINER_TYPES.has(root.type))
|
|
35
|
+
out.push(root);
|
|
36
|
+
}
|
|
37
|
+
else if (languageId === "swift") {
|
|
38
|
+
if (root.type === "class_declaration")
|
|
39
|
+
out.push(root);
|
|
40
|
+
}
|
|
41
|
+
for (const child of root.namedChildren ?? [])
|
|
42
|
+
collectSharedOwnerContainers(child, languageId, out);
|
|
43
|
+
}
|
|
44
|
+
const sharedOwnerContainersByRoot = new WeakMap();
|
|
45
|
+
function sharedOwnerContainersInTree(root, languageId) {
|
|
46
|
+
let byLanguage = sharedOwnerContainersByRoot.get(root);
|
|
47
|
+
if (!byLanguage) {
|
|
48
|
+
byLanguage = new Map();
|
|
49
|
+
sharedOwnerContainersByRoot.set(root, byLanguage);
|
|
50
|
+
}
|
|
51
|
+
const cached = byLanguage.get(languageId);
|
|
52
|
+
if (cached)
|
|
53
|
+
return cached;
|
|
54
|
+
const collected = [];
|
|
55
|
+
collectSharedOwnerContainers(root, languageId, collected);
|
|
56
|
+
byLanguage.set(languageId, collected);
|
|
57
|
+
return collected;
|
|
58
|
+
}
|
|
59
|
+
export async function resolveSharedOwnerContainers(params) {
|
|
60
|
+
const { index, ownerFile, ownerContainer, ownerSource, languageId } = params;
|
|
61
|
+
if (languageId !== "csharp" && languageId !== "swift")
|
|
62
|
+
return [];
|
|
63
|
+
const ownerIdentity = getSharedOwnerIdentity(ownerContainer, ownerSource, languageId, ownerFile);
|
|
64
|
+
if (!ownerIdentity)
|
|
65
|
+
return [];
|
|
66
|
+
// A C# `file` owner has only same-file parts, so no other file is a candidate peer.
|
|
67
|
+
const peers = ownerIdentity.fileLocalTo
|
|
68
|
+
? { files: new Set([ownerFile]), complete: true }
|
|
69
|
+
: getCompilationUnitPeers(index, ownerFile);
|
|
70
|
+
const out = [];
|
|
71
|
+
const seen = new Set();
|
|
72
|
+
const ownerIsExtension = languageId === "swift" ? isSwiftExtensionContainer(ownerContainer, ownerSource) : false;
|
|
73
|
+
const ownerKey = fileIdentityKey(ownerFile);
|
|
74
|
+
const units = [];
|
|
75
|
+
for (const file of peers.files) {
|
|
76
|
+
const peer = index.byFile.get(fileIdentityKey(file));
|
|
77
|
+
if (peer)
|
|
78
|
+
units.push(peer);
|
|
79
|
+
}
|
|
80
|
+
for (const peer of units) {
|
|
81
|
+
let peerContext;
|
|
82
|
+
try {
|
|
83
|
+
peerContext = await ensureParsedContext(peer.file, index.parsed?.get(fileIdentityKey(peer.file)), index.languageExtensions);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (peerContext.sup.id !== languageId)
|
|
89
|
+
continue;
|
|
90
|
+
const containers = sharedOwnerContainersInTree(peerContext.tree.rootNode, languageId);
|
|
91
|
+
for (const container of containers) {
|
|
92
|
+
if (fileIdentityKey(peer.file) === ownerKey &&
|
|
93
|
+
container.startIndex === ownerContainer.startIndex &&
|
|
94
|
+
container.endIndex === ownerContainer.endIndex) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const identity = getSharedOwnerIdentity(container, peerContext.source, languageId, peer.file);
|
|
98
|
+
if (!identity || !sharedOwnerCanUseMembers(ownerIdentity, identity))
|
|
99
|
+
continue;
|
|
100
|
+
if (languageId === "swift") {
|
|
101
|
+
const peerIsExtension = isSwiftExtensionContainer(container, peerContext.source);
|
|
102
|
+
if (!ownerIsExtension && !peerIsExtension)
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
const key = fileIdentityKey(peer.file) + ":" + container.startIndex + ":" + container.endIndex;
|
|
106
|
+
if (seen.has(key))
|
|
107
|
+
continue;
|
|
108
|
+
seen.add(key);
|
|
109
|
+
out.push({ file: peer.file, container, context: peerContext, module: peer });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Unit-boundary completeness for a C# partial or Swift type/extension member. Its other
|
|
116
|
+
* owner parts, and therefore its uses, are bounded by `getCompilationUnitPeers`, so an
|
|
117
|
+
* unproven unit boundary leaves the member's reference set incomplete. Returns null when
|
|
118
|
+
* the member's owner cannot be shared across files.
|
|
119
|
+
*/
|
|
120
|
+
export async function sharedOwnerMemberUnitComplete(index, def) {
|
|
121
|
+
if (!def.isMember)
|
|
122
|
+
return null;
|
|
123
|
+
let context;
|
|
124
|
+
try {
|
|
125
|
+
context = await ensureParsedContext(def.file, index.parsed?.get(fileIdentityKey(def.file)), index.languageExtensions);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
const languageId = context.sup.id;
|
|
131
|
+
if (languageId !== "csharp" && languageId !== "swift")
|
|
132
|
+
return null;
|
|
133
|
+
const nameNode = nameNodeForDef(context, def);
|
|
134
|
+
const container = nameNode ? nearestMemberContainer(nameNode) : null;
|
|
135
|
+
const identity = container ? getSharedOwnerIdentity(container, context.source, languageId, def.file) : null;
|
|
136
|
+
if (!identity)
|
|
137
|
+
return null;
|
|
138
|
+
// File-local owners have no parts in other files, so the reference set is complete.
|
|
139
|
+
if (identity.fileLocalTo)
|
|
140
|
+
return true;
|
|
141
|
+
// A C# member is reachable through a qualified owner (`P.Box`) from any namespace, so use the
|
|
142
|
+
// same qualified peer relation as reference candidate discovery.
|
|
143
|
+
return getCompilationUnitPeers(index, def.file, languageId === "csharp" ? { csharpQualifiedName: true } : undefined)
|
|
144
|
+
.complete;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Other C# `partial` type parts that share owner identity with `def`.
|
|
148
|
+
* Reference collection treats them as one type so uses that resolve to the
|
|
149
|
+
* coalesced representative still match a query started on any part.
|
|
150
|
+
*/
|
|
151
|
+
export async function findCsharpPartialTypeEquivalents(index, def) {
|
|
152
|
+
if (!declaresMembers(def))
|
|
153
|
+
return [];
|
|
154
|
+
const module = index.byFile.get(fileIdentityKey(def.file));
|
|
155
|
+
if (!module)
|
|
156
|
+
return [];
|
|
157
|
+
let context;
|
|
158
|
+
try {
|
|
159
|
+
context = await ensureParsedContext(def.file, index.parsed?.get(fileIdentityKey(def.file)), index.languageExtensions);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
if (context.sup.id !== "csharp")
|
|
165
|
+
return [];
|
|
166
|
+
const start = def.range.start;
|
|
167
|
+
const nameNode = context.tree.rootNode.descendantForPosition({ row: start.line - 1, column: start.column - 1 }, { row: start.line - 1, column: start.column - 1 });
|
|
168
|
+
const container = nearestMemberContainer(nameNode);
|
|
169
|
+
if (!container)
|
|
170
|
+
return [];
|
|
171
|
+
const peers = await resolveSharedOwnerContainers({
|
|
172
|
+
index,
|
|
173
|
+
ownerFile: def.file,
|
|
174
|
+
ownerContainer: container,
|
|
175
|
+
ownerSource: context.source,
|
|
176
|
+
languageId: "csharp",
|
|
177
|
+
});
|
|
178
|
+
const out = [];
|
|
179
|
+
for (const peer of peers) {
|
|
180
|
+
const peerName = peer.container.childForFieldName("name");
|
|
181
|
+
if (!peerName)
|
|
182
|
+
continue;
|
|
183
|
+
const peerDef = peer.module.locals.find((local) => declaresMembers(local) && local.range.start.index === peerName.startIndex);
|
|
184
|
+
if (peerDef)
|
|
185
|
+
out.push(peerDef);
|
|
186
|
+
}
|
|
187
|
+
return out;
|
|
188
|
+
}
|
|
189
|
+
/** Reusable shared-owner member lookup for keyword navigation and receiver graphs. */
|
|
190
|
+
export async function findSharedOwnerMemberDefinitions(params) {
|
|
191
|
+
const { index, ownerFile, ownerContainer, ownerSource, languageId, member, memberScope } = params;
|
|
192
|
+
const peers = await resolveSharedOwnerContainers({ index, ownerFile, ownerContainer, ownerSource, languageId });
|
|
193
|
+
const out = [];
|
|
194
|
+
for (const peer of peers) {
|
|
195
|
+
const predicate = !memberScope || memberScope === "any"
|
|
196
|
+
? undefined
|
|
197
|
+
: (local) => matchesReceiverMemberScope(local, memberScope, peer.context, peer.container);
|
|
198
|
+
const hits = findDirectLocalsWithinNode(peer.module.locals, member, peer.container, peer.context, peer.context.sup.normalizeIdentifier, predicate);
|
|
199
|
+
for (const hit of hits) {
|
|
200
|
+
const hitNode = nameNodeForDef(peer.context, hit);
|
|
201
|
+
if (languageId === "swift" && fileIdentityKey(ownerFile) !== fileIdentityKey(peer.file)) {
|
|
202
|
+
if (!hitNode || isSwiftCrossFileHiddenSharedOwnerMember(languageId, ownerFile, peer.file, hitNode))
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (!out.includes(hit))
|
|
206
|
+
out.push(hit);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return out;
|
|
210
|
+
}
|
|
211
|
+
function nameNodeForDef(context, def) {
|
|
212
|
+
const start = def.range.start;
|
|
213
|
+
const index = start.index;
|
|
214
|
+
if (index !== undefined)
|
|
215
|
+
return context.tree.rootNode.descendantForIndex(index, index);
|
|
216
|
+
const position = { row: start.line - 1, column: start.column - 1 };
|
|
217
|
+
return context.tree.rootNode.descendantForPosition(position, position);
|
|
218
|
+
}
|
|
219
|
+
function enclosingImportScope(declarationName) {
|
|
220
|
+
let current = declarationName;
|
|
221
|
+
while (current && current.type !== "variable_declaration" && current.type !== "using_directive") {
|
|
222
|
+
current = current.parent;
|
|
223
|
+
}
|
|
224
|
+
return current?.parent ?? null;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Rewrites a valid C# `using X = Namespace; X::Target` lookup into the dotted form
|
|
228
|
+
* export resolution already understands. `global::` and ordinary dotted names keep
|
|
229
|
+
* their source spelling; an unknown alias stays `X::Target` instead of a bare name.
|
|
230
|
+
*/
|
|
231
|
+
export function csharpAliasQualifiedLookupName(node, source, fallback, imports) {
|
|
232
|
+
const raw = csharpLookupName(node, source, fallback);
|
|
233
|
+
if (raw.startsWith("global::"))
|
|
234
|
+
return raw;
|
|
235
|
+
const chainNode = csharpQualifiedNameNode(node);
|
|
236
|
+
if (!chainNode || !raw.includes("::"))
|
|
237
|
+
return raw;
|
|
238
|
+
const names = [];
|
|
239
|
+
let current = chainNode;
|
|
240
|
+
let base = null;
|
|
241
|
+
while (current && (current.type === "qualified_name" || current.type === "alias_qualified_name")) {
|
|
242
|
+
const parts = getMemberAccessParts(CSHARP_SUPPORT, current);
|
|
243
|
+
base = parts.object ?? base;
|
|
244
|
+
let property = parts.property;
|
|
245
|
+
if (property?.type === "generic_name") {
|
|
246
|
+
property = property.childForFieldName("name") ?? property.namedChildren[0] ?? property;
|
|
247
|
+
}
|
|
248
|
+
if (property?.type === "identifier")
|
|
249
|
+
names.push(sliceText(property, source));
|
|
250
|
+
current = base;
|
|
251
|
+
}
|
|
252
|
+
if (!base || !isReceiverNameNode(CSHARP_SUPPORT, base.type) || !names.length)
|
|
253
|
+
return raw;
|
|
254
|
+
const alias = sliceText(base, source);
|
|
255
|
+
if (alias === "global")
|
|
256
|
+
return raw;
|
|
257
|
+
const imported = innermostNamespaceImport(imports, alias, base, CSHARP_SUPPORT.normalizeIdentifier);
|
|
258
|
+
if (!imported)
|
|
259
|
+
return raw;
|
|
260
|
+
return `${imported.from}.${[...names].reverse().join(".")}`;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Innermost in-scope namespace import bound to `alias`. `normalize` applies the language's
|
|
264
|
+
* identifier equality (C# `@X` and `X` are one alias); the default compares exact spelling.
|
|
265
|
+
*/
|
|
266
|
+
export function innermostNamespaceImport(imports, alias, useNode, normalize = (name) => name) {
|
|
267
|
+
const normalizedAlias = normalize(alias);
|
|
268
|
+
const matches = imports.filter((candidate) => candidate.kind === "namespace" && normalize(candidate.localNS) === normalizedAlias);
|
|
269
|
+
if (!matches.length)
|
|
270
|
+
return undefined;
|
|
271
|
+
let root = useNode;
|
|
272
|
+
while (root.parent)
|
|
273
|
+
root = root.parent;
|
|
274
|
+
let best;
|
|
275
|
+
let bestScopeSpan = Number.POSITIVE_INFINITY;
|
|
276
|
+
let bestStart = -1;
|
|
277
|
+
for (const match of matches) {
|
|
278
|
+
const start = match.localRange?.start.index;
|
|
279
|
+
if (start === undefined)
|
|
280
|
+
continue;
|
|
281
|
+
let nameNode = root;
|
|
282
|
+
for (;;) {
|
|
283
|
+
const child = nameNode.namedChildren.find((candidate) => candidate.startIndex <= start && start < candidate.endIndex);
|
|
284
|
+
if (!child)
|
|
285
|
+
break;
|
|
286
|
+
nameNode = child;
|
|
287
|
+
}
|
|
288
|
+
const scope = enclosingImportScope(nameNode);
|
|
289
|
+
if (!scope)
|
|
290
|
+
continue;
|
|
291
|
+
if (start > useNode.startIndex && scope.parent)
|
|
292
|
+
continue;
|
|
293
|
+
if (scope.startIndex > useNode.startIndex || scope.endIndex < useNode.endIndex)
|
|
294
|
+
continue;
|
|
295
|
+
const span = scope.endIndex - scope.startIndex;
|
|
296
|
+
if (span < bestScopeSpan || (span === bestScopeSpan && start >= bestStart)) {
|
|
297
|
+
bestScopeSpan = span;
|
|
298
|
+
bestStart = start;
|
|
299
|
+
best = match;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (best)
|
|
303
|
+
return best;
|
|
304
|
+
const unlocated = matches.filter((match) => match.localRange?.start.index === undefined);
|
|
305
|
+
if (unlocated.length === 1)
|
|
306
|
+
return unlocated[0];
|
|
307
|
+
const files = new Map();
|
|
308
|
+
for (const match of unlocated) {
|
|
309
|
+
if (typeof match.resolved !== "string")
|
|
310
|
+
continue;
|
|
311
|
+
const key = fileIdentityKey(match.resolved);
|
|
312
|
+
if (!files.has(key))
|
|
313
|
+
files.set(key, match);
|
|
314
|
+
}
|
|
315
|
+
if (files.size === 1)
|
|
316
|
+
return [...files.values()][0];
|
|
317
|
+
return undefined;
|
|
318
|
+
}
|
|
319
|
+
function rubyScopeResolutionPath(node, source) {
|
|
320
|
+
if (node.type !== "constant")
|
|
321
|
+
return null;
|
|
322
|
+
const owner = node.parent;
|
|
323
|
+
if (owner?.type !== "scope_resolution")
|
|
324
|
+
return null;
|
|
325
|
+
const ownerName = owner.childForFieldName("name");
|
|
326
|
+
if (!ownerName || ownerName.startIndex !== node.startIndex || ownerName.endIndex !== node.endIndex)
|
|
327
|
+
return null;
|
|
328
|
+
// `Outer::Inner::Tool` nests scope_resolution on `scope`, so flatten from the
|
|
329
|
+
// clicked name instead of stopping at the first nested scope.
|
|
330
|
+
const segments = [];
|
|
331
|
+
let current = owner;
|
|
332
|
+
while (current?.type === "scope_resolution") {
|
|
333
|
+
const name = current.childForFieldName("name");
|
|
334
|
+
const scope = current.childForFieldName("scope");
|
|
335
|
+
if (!name || name.type !== "constant")
|
|
336
|
+
return null;
|
|
337
|
+
const text = sliceText(name, source);
|
|
338
|
+
if (!text)
|
|
339
|
+
return null;
|
|
340
|
+
segments.unshift(text);
|
|
341
|
+
current = scope;
|
|
342
|
+
}
|
|
343
|
+
if (!current || current.type !== "constant" || segments.length === 0)
|
|
344
|
+
return null;
|
|
345
|
+
const root = sliceText(current, source);
|
|
346
|
+
if (!root)
|
|
347
|
+
return null;
|
|
348
|
+
return { path: `${root}::${segments.join("::")}`, root };
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* `Outer::Inner::Tool` is one constant path. Nested classes are exported under that
|
|
352
|
+
* path, not as bare `Tool`, so the chain has to ask for the qualified export.
|
|
353
|
+
*/
|
|
354
|
+
function resolveRubyQualifiedConstantDefinition(index, mod, node, source) {
|
|
355
|
+
const qualified = rubyScopeResolutionPath(node, source);
|
|
356
|
+
if (!qualified)
|
|
357
|
+
return null;
|
|
358
|
+
const files = [];
|
|
359
|
+
const seen = new Set();
|
|
360
|
+
const add = (file) => {
|
|
361
|
+
if (!file)
|
|
362
|
+
return;
|
|
363
|
+
const key = fileIdentityKey(file);
|
|
364
|
+
if (seen.has(key))
|
|
365
|
+
return;
|
|
366
|
+
seen.add(key);
|
|
367
|
+
files.push(file);
|
|
368
|
+
};
|
|
369
|
+
for (const imp of mod.imports) {
|
|
370
|
+
if (imp.kind === "namespace" && imp.localNS === qualified.root && typeof imp.resolved === "string") {
|
|
371
|
+
add(imp.resolved);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
add(mod.file);
|
|
375
|
+
const hits = [];
|
|
376
|
+
for (const file of files) {
|
|
377
|
+
const hit = resolveExport(index, file, qualified.path, { allowLocalFallback: false });
|
|
378
|
+
if (hit?.kind !== "resolved")
|
|
379
|
+
continue;
|
|
380
|
+
if (hits.some((candidate) => fileIdentityKey(candidate.file) === fileIdentityKey(hit.def.file) &&
|
|
381
|
+
candidate.localName === hit.def.localName &&
|
|
382
|
+
candidate.range.start.line === hit.def.range.start.line &&
|
|
383
|
+
candidate.range.start.column === hit.def.range.start.column)) {
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
hits.push(hit.def);
|
|
387
|
+
}
|
|
388
|
+
if (hits.length !== 1)
|
|
389
|
+
return null;
|
|
390
|
+
return okGoToResult(index, hits[0], {
|
|
391
|
+
via: { exportedName: qualified.path },
|
|
392
|
+
resolution: "member-access",
|
|
393
|
+
confidence: "medium",
|
|
394
|
+
});
|
|
395
|
+
}
|
|
12
396
|
export async function resolveMemberAccessDefinition(params) {
|
|
13
|
-
const { index, mod, node, source, sup, resolveLexicalBinding } = params;
|
|
397
|
+
const { index, mod, node, source, tree, sup, resolveLexicalBinding } = params;
|
|
398
|
+
if (sup.id === "ruby") {
|
|
399
|
+
const qualified = resolveRubyQualifiedConstantDefinition(index, mod, node, source);
|
|
400
|
+
if (qualified)
|
|
401
|
+
return qualified;
|
|
402
|
+
}
|
|
14
403
|
const parent = node.parent;
|
|
15
404
|
if (!parent || !sup.supportsCrossModuleSymbols) {
|
|
16
405
|
return null;
|
|
@@ -26,46 +415,143 @@ export async function resolveMemberAccessDefinition(params) {
|
|
|
26
415
|
return null;
|
|
27
416
|
const { object: obj, property: prop } = getMemberAccessParts(sup, memberNode);
|
|
28
417
|
const optionalMemberTypes = memberAccessTraversalTypes(sup);
|
|
418
|
+
// Only the outermost access is the callee; an inner hop (`A.B` in `A.B.C()`) is not called.
|
|
419
|
+
const callArgumentCountFor = (expr) => expr.id === memberNode.id
|
|
420
|
+
? (getCallArgumentCount({ languageId: sup.id, source, call: memberNode.parent ?? memberNode }) ?? undefined)
|
|
421
|
+
: undefined;
|
|
29
422
|
const resolveExpression = async (expr) => {
|
|
30
423
|
const exprIsId = isReceiverNameNode(sup, expr.type) && !isMemberAccessNode(sup, expr);
|
|
31
424
|
if (exprIsId) {
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
425
|
+
const aliasQualifier = sup.id === "csharp" &&
|
|
426
|
+
expr.parent?.type === "alias_qualified_name" &&
|
|
427
|
+
getMemberAccessParts(sup, expr.parent).object?.id === expr.id;
|
|
428
|
+
if (!aliasQualifier) {
|
|
429
|
+
const lexicalBinding = resolveLexicalBinding?.(expr);
|
|
430
|
+
if (lexicalBinding)
|
|
431
|
+
return { kind: "resolved", def: lexicalBinding };
|
|
432
|
+
}
|
|
35
433
|
const exprName = sliceText(expr, source);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
(
|
|
434
|
+
let imp;
|
|
435
|
+
if (sup.id === "php") {
|
|
436
|
+
imp = findPhpImportAlias(mod.imports, exprName, "class") ?? undefined;
|
|
437
|
+
}
|
|
438
|
+
else if (sup.id === "zig") {
|
|
439
|
+
imp = innermostNamespaceImport(mod.imports, exprName, expr);
|
|
440
|
+
if (!imp) {
|
|
441
|
+
imp = mod.imports.find((candidate) => (candidate.kind === "named" || candidate.kind === "default") && candidate.local === exprName);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
else if (sup.id === "csharp") {
|
|
445
|
+
if (aliasQualifier) {
|
|
446
|
+
if (exprName !== "global")
|
|
447
|
+
imp = innermostNamespaceImport(mod.imports, exprName, expr, sup.normalizeIdentifier);
|
|
448
|
+
if (!imp)
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
imp = innermostNamespaceImport(mod.imports, exprName, expr, sup.normalizeIdentifier);
|
|
453
|
+
if (!imp) {
|
|
454
|
+
imp = mod.imports.find((candidate) => (candidate.kind === "named" || candidate.kind === "default") &&
|
|
455
|
+
candidate.local === exprName &&
|
|
456
|
+
!isExpandedStarBinding(candidate, mod.imports));
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
imp = effectiveExplicitBinding(mod.imports, sup.id, (candidate) => ((candidate.kind === "named" || candidate.kind === "default") && candidate.local === exprName) ||
|
|
462
|
+
(candidate.kind === "namespace" && candidate.localNS === exprName));
|
|
463
|
+
if (!imp && isJsTsLanguage(sup.id)) {
|
|
464
|
+
imp = innermostNamespaceImport(mod.imports, exprName, expr);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
39
467
|
if (imp) {
|
|
40
468
|
if (imp.kind === "namespace") {
|
|
469
|
+
if (imp.mechanism === "cjs" && typeof imp.resolved === "string") {
|
|
470
|
+
const classValue = cjsRequireValueBinding(index, imp.resolved);
|
|
471
|
+
if (classValue)
|
|
472
|
+
return { kind: "resolved", def: classValue };
|
|
473
|
+
}
|
|
41
474
|
return {
|
|
42
475
|
kind: "namespace",
|
|
43
476
|
file: typeof imp.resolved === "string" ? imp.resolved.replace(/\\/g, "/") : imp.resolved?.external || "",
|
|
44
477
|
};
|
|
45
478
|
}
|
|
479
|
+
if (sup.id === "php" && imp.kind === "named" && typeof imp.resolved === "string") {
|
|
480
|
+
const result = resolvePhpExportByImportType(index, imp.resolved, imp.imported, "class");
|
|
481
|
+
if (result)
|
|
482
|
+
return result;
|
|
483
|
+
}
|
|
46
484
|
const result = resolveImported(index, imp, imp.kind === "named" ? imp.imported : "default");
|
|
47
485
|
if (result) {
|
|
48
486
|
if ("namespace" in result) {
|
|
49
487
|
return { kind: "namespace", file: result.namespace };
|
|
50
488
|
}
|
|
51
|
-
|
|
489
|
+
const container = asMemberContainer(index, result);
|
|
490
|
+
return { kind: "resolved", def: container ?? result };
|
|
52
491
|
}
|
|
492
|
+
// Last-wins precedence (Python): the explicit import rebound the name, and an
|
|
493
|
+
// unresolved winner stays unresolved instead of falling back to an older star import.
|
|
494
|
+
if (starImportPrecedence(sup.id) === "last-wins")
|
|
495
|
+
return null;
|
|
53
496
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return
|
|
497
|
+
if (sup.id === "csharp") {
|
|
498
|
+
const local = resolveExport(index, mod.file, exprName, { referenceIndex: expr.startIndex });
|
|
499
|
+
if (local)
|
|
500
|
+
return local;
|
|
501
|
+
}
|
|
502
|
+
else {
|
|
503
|
+
const local = mod.locals.find((candidate) => {
|
|
504
|
+
if (candidate.localName === exprName)
|
|
505
|
+
return true;
|
|
506
|
+
return (sup.id === "php" &&
|
|
507
|
+
declaresMembers(candidate) &&
|
|
508
|
+
foldPhpIdentifierCase(candidate.localName) === foldPhpIdentifierCase(exprName));
|
|
509
|
+
});
|
|
510
|
+
if (local)
|
|
511
|
+
return { kind: "resolved", def: local };
|
|
512
|
+
if (sup.id === "java" || sup.id === "kotlin" || sup.id === "swift") {
|
|
513
|
+
const peer = resolveExport(index, mod.file, exprName);
|
|
514
|
+
if (peer)
|
|
515
|
+
return peer;
|
|
516
|
+
}
|
|
517
|
+
else if (sup.id === "php") {
|
|
518
|
+
const phpClass = resolveIndexedPhpClassReference(index, source, tree, expr, exprName, mod.imports);
|
|
519
|
+
if (phpClass)
|
|
520
|
+
return { kind: "resolved", def: phpClass };
|
|
64
521
|
}
|
|
65
522
|
}
|
|
523
|
+
const starDecision = resolveStarImportedName(index, mod, sup.id, exprName);
|
|
524
|
+
if (starDecision.status === "resolved")
|
|
525
|
+
return { kind: "resolved", def: starDecision.definition };
|
|
526
|
+
if (starDecision.status === "ambiguous")
|
|
527
|
+
return null;
|
|
528
|
+
const starNamespaceDecision = resolveStarImportedNamespace(index, mod, sup.id, exprName);
|
|
529
|
+
if (starNamespaceDecision.status === "resolved") {
|
|
530
|
+
return { kind: "namespace", file: starNamespaceDecision.namespace };
|
|
531
|
+
}
|
|
532
|
+
if (starNamespaceDecision.status === "ambiguous")
|
|
533
|
+
return null;
|
|
66
534
|
return null;
|
|
67
535
|
}
|
|
68
536
|
if (optionalMemberTypes.has(expr.type)) {
|
|
537
|
+
// A Python unaliased dotted import (`import a.b`) binds only the first segment `a`,
|
|
538
|
+
// resolved to the leaf module `a.b` names; the source can only ever repeat that whole
|
|
539
|
+
// dotted phrase to reach it again (`a.b.symbol(...)`), never a bare intermediate segment
|
|
540
|
+
// on its own. Recognize that literal phrase up front so the chain walk below lands on the
|
|
541
|
+
// leaf module the import machinery already resolved, instead of treating each dot as an
|
|
542
|
+
// ordinary member-access hop and failing on the segment duplicating the import's spelling.
|
|
543
|
+
if (sup.id === "python") {
|
|
544
|
+
const dottedText = sliceText(expr, source);
|
|
545
|
+
if (dottedText.includes(".")) {
|
|
546
|
+
const dottedImport = mod.imports.find((imp) => imp.kind === "namespace" &&
|
|
547
|
+
imp.mechanism === "python" &&
|
|
548
|
+
imp.from === dottedText &&
|
|
549
|
+
typeof imp.resolved === "string");
|
|
550
|
+
if (dottedImport && typeof dottedImport.resolved === "string") {
|
|
551
|
+
return { kind: "namespace", file: dottedImport.resolved.replace(/\\/g, "/") };
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
69
555
|
const parts = getMemberAccessParts(sup, expr);
|
|
70
556
|
const subObj = parts.object;
|
|
71
557
|
let subProp = parts.property;
|
|
@@ -75,15 +561,24 @@ export async function resolveMemberAccessDefinition(params) {
|
|
|
75
561
|
if (subObj && subProp) {
|
|
76
562
|
const base = await resolveExpression(subObj);
|
|
77
563
|
const memberName = sliceText(subProp, source);
|
|
564
|
+
// `P.Mix` in `P.Mix.M()`: when the leftmost name binds nothing, a C# dotted name is a
|
|
565
|
+
// namespace-qualified type, which qualified export lookup resolves across every file that
|
|
566
|
+
// declares the namespace.
|
|
567
|
+
const dottedRoot = !base && sup.id === "csharp" ? csharpDottedNameRoot(expr) : null;
|
|
568
|
+
if (dottedRoot && !resolveLexicalBinding?.(dottedRoot)) {
|
|
569
|
+
return resolveCsharpQualifiedName(index, mod, sliceText(expr, source).replace(/\s+/gu, ""), expr.startIndex);
|
|
570
|
+
}
|
|
78
571
|
if (base?.kind === "namespace") {
|
|
572
|
+
if (!isGoExportedMemberName(sup.id, memberName))
|
|
573
|
+
return null;
|
|
79
574
|
return resolveExport(index, base.file, memberName, { allowLocalFallback: false });
|
|
80
575
|
}
|
|
81
576
|
if (base?.kind === "resolved") {
|
|
82
|
-
if (sup.id === "java") {
|
|
83
|
-
const memberDef = await resolveMemberDefinitionForBase(index, base.def, memberName);
|
|
577
|
+
if (sup.id === "java" || sup.id === "csharp") {
|
|
578
|
+
const memberDef = await resolveMemberDefinitionForBase(index, base.def, memberName, callArgumentCountFor(expr));
|
|
84
579
|
return memberDef ? { kind: "resolved", def: memberDef } : null;
|
|
85
580
|
}
|
|
86
|
-
if (sup.id === "ruby") {
|
|
581
|
+
if (sup.id === "ruby" && declaresMembers(base.def)) {
|
|
87
582
|
const memberDef = await resolveMemberDefinitionForBase(index, base.def, memberName);
|
|
88
583
|
if (memberDef)
|
|
89
584
|
return { kind: "resolved", def: memberDef };
|
|
@@ -105,7 +600,7 @@ export async function resolveMemberAccessDefinition(params) {
|
|
|
105
600
|
return resolveExport(index, base.file, memberName, { allowLocalFallback: false });
|
|
106
601
|
}
|
|
107
602
|
if (base?.kind === "resolved") {
|
|
108
|
-
const memberDef = await resolveMemberDefinitionForBase(index, base.def, memberName);
|
|
603
|
+
const memberDef = await resolveMemberDefinitionForBase(index, base.def, memberName, callArgumentCountFor(expr));
|
|
109
604
|
return memberDef ? { kind: "resolved", def: memberDef } : null;
|
|
110
605
|
}
|
|
111
606
|
}
|
|
@@ -134,11 +629,11 @@ export async function resolveMemberAccessDefinition(params) {
|
|
|
134
629
|
}
|
|
135
630
|
}
|
|
136
631
|
const receiverName = obj ? sliceText(obj, source) : "";
|
|
137
|
-
const
|
|
138
|
-
if (obj && prop && node.id === prop.id &&
|
|
632
|
+
const receiverKind = keywordReceiverKind(sup.id, receiverName);
|
|
633
|
+
if (obj && prop && node.id === prop.id && supportsReceiverMemberNavigation(sup.id)) {
|
|
139
634
|
const member = sliceText(prop, source);
|
|
140
635
|
if (sup.id === "python") {
|
|
141
|
-
const memberDef = await resolvePythonReceiverMember(index, mod, node, obj, member, source, sup, resolveExpression);
|
|
636
|
+
const memberDef = await resolvePythonReceiverMember(index, mod, node, obj, member, source, tree, sup, resolveExpression);
|
|
142
637
|
if (!memberDef)
|
|
143
638
|
return null;
|
|
144
639
|
return okGoToResult(index, memberDef, {
|
|
@@ -147,11 +642,12 @@ export async function resolveMemberAccessDefinition(params) {
|
|
|
147
642
|
confidence: "medium",
|
|
148
643
|
});
|
|
149
644
|
}
|
|
150
|
-
if (
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
645
|
+
if (receiverKind && keywordReceiverCrossesDynamicBoundary(sup, node)) {
|
|
646
|
+
return null;
|
|
647
|
+
}
|
|
648
|
+
const keywordScope = receiverKind ? keywordReceiverMemberScope(sup, receiverName, node, source) : "any";
|
|
649
|
+
if (receiverKind === "own") {
|
|
650
|
+
const memberDef = await resolveKeywordReceiverMember(index, mod, node, member, keywordScope, false, getCallArgumentCount({ languageId: sup.id, source, call: memberNode.parent ?? memberNode }) ?? undefined);
|
|
155
651
|
if (memberDef) {
|
|
156
652
|
return okGoToResult(index, memberDef, {
|
|
157
653
|
via: { exportedName: member },
|
|
@@ -159,8 +655,74 @@ export async function resolveMemberAccessDefinition(params) {
|
|
|
159
655
|
confidence: "medium",
|
|
160
656
|
});
|
|
161
657
|
}
|
|
658
|
+
const outOfLineOwnerPath = cppOutOfLineOwnerPath(node, source, sup);
|
|
659
|
+
const outOfLineOwner = outOfLineOwnerPath
|
|
660
|
+
? await resolveCppQualifiedMemberContainer(index, mod, outOfLineOwnerPath)
|
|
661
|
+
: null;
|
|
662
|
+
if (outOfLineOwner) {
|
|
663
|
+
const outOfLineMember = await resolveKeywordReceiverMember(index, mod, node, member, keywordScope, false, getCallArgumentCount({ languageId: sup.id, source, call: memberNode.parent ?? memberNode }) ?? undefined, outOfLineOwner);
|
|
664
|
+
if (outOfLineMember) {
|
|
665
|
+
return okGoToResult(index, outOfLineMember, {
|
|
666
|
+
resolution: "exact",
|
|
667
|
+
confidence: "high",
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
// A failed `self` lookup is not a license to bind an unrelated same-file
|
|
672
|
+
// extension member by its bare name (including an unproven where clause).
|
|
673
|
+
if (sup.id === "swift")
|
|
674
|
+
return { status: "not_found", reason: "No matching Swift member definition" };
|
|
162
675
|
}
|
|
163
|
-
|
|
676
|
+
else if (receiverKind === "supertype") {
|
|
677
|
+
const memberDef = await resolveKeywordReceiverMember(index, mod, node, member, keywordScope, true, getCallArgumentCount({ languageId: sup.id, source, call: memberNode.parent ?? memberNode }) ?? undefined);
|
|
678
|
+
if (!memberDef)
|
|
679
|
+
return { status: "not_found", reason: "No matching supertype member definition" };
|
|
680
|
+
return okGoToResult(index, memberDef, {
|
|
681
|
+
via: { exportedName: member },
|
|
682
|
+
resolution: "member-access",
|
|
683
|
+
confidence: "medium",
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
if (sup.id === "cpp" && memberNode.type === "qualified_identifier") {
|
|
687
|
+
// `a::C::run` nests `C::run` inside `a::(...)`; only the outermost node carries the full owner.
|
|
688
|
+
let outermost = memberNode;
|
|
689
|
+
while (outermost.parent?.type === "qualified_identifier")
|
|
690
|
+
outermost = outermost.parent;
|
|
691
|
+
// The declarator of an out-of-line definition (`int Box<T>::f() {}`) names the declaration
|
|
692
|
+
// itself, not a member call; the declaration lookup below resolves it.
|
|
693
|
+
if (outermost.parent?.type === "function_declarator")
|
|
694
|
+
return null;
|
|
695
|
+
const qualified = cppQualifiedNameSegments(outermost, source);
|
|
696
|
+
const ownerPath = qualified.slice(0, -1);
|
|
697
|
+
const owner = await resolveCppQualifiedMemberContainer(index, mod, ownerPath);
|
|
698
|
+
if (owner) {
|
|
699
|
+
const knownArgumentCount = getCallArgumentCount({ languageId: sup.id, source, call: memberNode.parent ?? memberNode }) ?? undefined;
|
|
700
|
+
// Inside a member function, `Owner::member()` can name an instance member of the class or a
|
|
701
|
+
// base through the implicit `this`; elsewhere only static members qualify.
|
|
702
|
+
const implicitThis = await cppQualifiedOwnerHasImplicitThis(index, mod, memberNode, source, sup, owner);
|
|
703
|
+
const memberScope = implicitThis ? "any" : "static";
|
|
704
|
+
const memberDef = await resolveKeywordReceiverMember(index, mod, node, member, memberScope, false, knownArgumentCount, owner);
|
|
705
|
+
return memberDef
|
|
706
|
+
? okGoToResult(index, memberDef, { resolution: "member-access", confidence: "high" })
|
|
707
|
+
: { status: "not_found", reason: "No matching C++ static member definition" };
|
|
708
|
+
}
|
|
709
|
+
// A qualifier that is not a class (a namespace) names a free function, which the qualified
|
|
710
|
+
// function lookup resolves; the short name must not be read as a same-named class receiver.
|
|
711
|
+
return null;
|
|
712
|
+
}
|
|
713
|
+
const importType = isJsTsLanguage(sup.id) ? receiverConstructorExpression(obj, source, sup) : null;
|
|
714
|
+
const importSpecifier = importType?.type === "type_query" ? importTypeQuerySpecifier(importType) : null;
|
|
715
|
+
if (importSpecifier) {
|
|
716
|
+
const memberDef = resolveImportTypeMember(index, mod.file, importSpecifier, member);
|
|
717
|
+
return memberDef
|
|
718
|
+
? okGoToResult(index, memberDef, {
|
|
719
|
+
via: { exportedName: member },
|
|
720
|
+
resolution: "member-access",
|
|
721
|
+
confidence: "medium",
|
|
722
|
+
})
|
|
723
|
+
: null;
|
|
724
|
+
}
|
|
725
|
+
const receiver = await resolveReceiverDefinition(index, obj, source, sup, resolveExpression, mod);
|
|
164
726
|
if (receiver) {
|
|
165
727
|
const objDef = receiver.def;
|
|
166
728
|
const targetContext = await ensureParsedContext(objDef.file, undefined, index.languageExtensions);
|
|
@@ -185,37 +747,1030 @@ export async function resolveMemberAccessDefinition(params) {
|
|
|
185
747
|
const memberPredicate = receiver.memberScope === "any"
|
|
186
748
|
? undefined
|
|
187
749
|
: (local) => matchesReceiverMemberScope(local, receiver.memberScope, targetContext, container);
|
|
750
|
+
const knownArgumentCount = getCallArgumentCount({ languageId: sup.id, source, call: memberNode.parent ?? memberNode }) ?? undefined;
|
|
188
751
|
let memberDef;
|
|
189
|
-
if (
|
|
190
|
-
|
|
752
|
+
if ((targetContext.sup.id === "ts" || targetContext.sup.id === "tsx") &&
|
|
753
|
+
(container.type === "internal_module" || container.type === "module")) {
|
|
754
|
+
memberDef = await findReceiverMemberDefinition(index, targetModule.locals, member, objDef, container, targetContext, normalizeIdentifier, receiver.memberScope, knownArgumentCount);
|
|
755
|
+
}
|
|
756
|
+
else if (receiver.runtimeTypeOnly || targetContext.sup.id === "java") {
|
|
757
|
+
const candidates = findDirectLocalsWithinNode(targetModule.locals, member, container, targetContext, normalizeIdentifier, memberPredicate);
|
|
758
|
+
memberDef = await selectReceiverMemberCandidates(index, candidates, knownArgumentCount);
|
|
191
759
|
}
|
|
192
760
|
else {
|
|
193
|
-
memberDef = findReceiverMemberDefinition(targetModule.locals, member, objDef, container, targetContext, normalizeIdentifier, receiver.memberScope);
|
|
761
|
+
memberDef = await findReceiverMemberDefinition(index, targetModule.locals, member, objDef, container, targetContext, normalizeIdentifier, receiver.memberScope, knownArgumentCount);
|
|
762
|
+
}
|
|
763
|
+
if (memberDef &&
|
|
764
|
+
(sup.id === "cpp" ||
|
|
765
|
+
isGoExportedMemberName(sup.id, member) ||
|
|
766
|
+
getCompilationUnitPeers(index, mod.file).files.has(memberDef.file)) &&
|
|
767
|
+
!crossFilePeerMemberHidden(mod.file, memberDef, targetContext)) {
|
|
768
|
+
return okGoToResult(index, memberDef, {
|
|
769
|
+
via: { exportedName: member },
|
|
770
|
+
resolution: "member-access",
|
|
771
|
+
confidence: "medium",
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
return null;
|
|
779
|
+
}
|
|
780
|
+
function findEnclosingClassContainer(node) {
|
|
781
|
+
return nearestMemberContainer(node);
|
|
782
|
+
}
|
|
783
|
+
function keywordClassKey(def) {
|
|
784
|
+
const start = def.range.start;
|
|
785
|
+
return `${fileIdentityKey(def.file)}:${start.index ?? `${start.line}:${start.column}`}`;
|
|
786
|
+
}
|
|
787
|
+
function keywordContainerKey(file, container) {
|
|
788
|
+
return `${fileIdentityKey(file)}:${container.startIndex}:${container.endIndex}`;
|
|
789
|
+
}
|
|
790
|
+
function peelTypeWrappers(node) {
|
|
791
|
+
let current = node;
|
|
792
|
+
while (current) {
|
|
793
|
+
if (current.type === "type_annotation" ||
|
|
794
|
+
current.type === "named_type" ||
|
|
795
|
+
current.type === "user_type" ||
|
|
796
|
+
current.type === "type" ||
|
|
797
|
+
current.type === "parenthesized_type" ||
|
|
798
|
+
current.type === "pointer_type" ||
|
|
799
|
+
current.type === "reference_type" ||
|
|
800
|
+
current.type === "optional_type" ||
|
|
801
|
+
current.type === "nullable_type" ||
|
|
802
|
+
current.type === "generic_type" ||
|
|
803
|
+
current.type === "generic_name") {
|
|
804
|
+
current = current.childForFieldName("type") ?? current.namedChildren[0] ?? null;
|
|
805
|
+
continue;
|
|
806
|
+
}
|
|
807
|
+
break;
|
|
808
|
+
}
|
|
809
|
+
return current ?? node;
|
|
810
|
+
}
|
|
811
|
+
function isSimpleTypeNameNode(node, sup) {
|
|
812
|
+
return isReceiverNameNode(sup, node.type) || node.type === "type_identifier" || node.type === "name";
|
|
813
|
+
}
|
|
814
|
+
function parseCppBaseType(node, source) {
|
|
815
|
+
let current = node;
|
|
816
|
+
while (current.type === "base_class_clause" ||
|
|
817
|
+
current.type === "access_specifier" ||
|
|
818
|
+
current.type === "virtual_specifier" ||
|
|
819
|
+
current.type === "type_descriptor") {
|
|
820
|
+
const nested = current.namedChildren.at(-1);
|
|
821
|
+
if (!nested || nested.id === current.id)
|
|
822
|
+
break;
|
|
823
|
+
current = nested;
|
|
824
|
+
}
|
|
825
|
+
const names = cppQualifiedNameSegments(current, source);
|
|
826
|
+
return names.length ? { base: names[0], path: names.slice(1) } : null;
|
|
827
|
+
}
|
|
828
|
+
function collectDeclaredBaseTypes(container, source, sup, superclassOnly = false) {
|
|
829
|
+
const ancestry = MEMBER_ACCESS_ROWS[sup.id]?.receiverAncestry;
|
|
830
|
+
if (!ancestry || (ancestry.clauses.length === 0 && !ancestry.mixinCalls?.length))
|
|
831
|
+
return [];
|
|
832
|
+
const bases = [];
|
|
833
|
+
const seen = new Set();
|
|
834
|
+
let usedFirstMatch = false;
|
|
835
|
+
const addSimple = (name, invoked) => {
|
|
836
|
+
if (!name || seen.has(name))
|
|
837
|
+
return;
|
|
838
|
+
seen.add(name);
|
|
839
|
+
bases.push(invoked ? { kind: "simple", name, invoked } : { kind: "simple", name });
|
|
840
|
+
};
|
|
841
|
+
const addQualified = (base, path, invoked) => {
|
|
842
|
+
if (!base || path.length === 0)
|
|
843
|
+
return;
|
|
844
|
+
const key = `${base}.${path.join(".")}`;
|
|
845
|
+
if (seen.has(key))
|
|
846
|
+
return;
|
|
847
|
+
seen.add(key);
|
|
848
|
+
bases.push(invoked ? { kind: "qualified", base, path, invoked } : { kind: "qualified", base, path });
|
|
849
|
+
};
|
|
850
|
+
const collectType = (node, invoked) => {
|
|
851
|
+
const core = peelTypeWrappers(node);
|
|
852
|
+
if (core.type === "constructor_invocation") {
|
|
853
|
+
// Kotlin writes the superclass as a constructor invocation (`Base()`); its callees are
|
|
854
|
+
// superclass entries, while bare types in the same base list are interfaces.
|
|
855
|
+
for (const child of core.namedChildren) {
|
|
856
|
+
if (child.type === "value_arguments")
|
|
857
|
+
continue;
|
|
858
|
+
collectType(child, true);
|
|
859
|
+
}
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
// A computed heritage expression can name the runtime base factory rather than a class.
|
|
863
|
+
// Its descendants do not prove an inheritance edge.
|
|
864
|
+
if (isUnprovenHeritageExpression(core))
|
|
865
|
+
return;
|
|
866
|
+
if (sup.id === "cpp" && core.type === "base_class_clause") {
|
|
867
|
+
for (const child of core.namedChildren) {
|
|
868
|
+
if (child.type === "access_specifier" || child.type === "virtual_specifier")
|
|
869
|
+
continue;
|
|
870
|
+
collectType(child, invoked);
|
|
871
|
+
}
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
if (sup.id === "cpp") {
|
|
875
|
+
const qualified = parseCppBaseType(core, source);
|
|
876
|
+
if (qualified) {
|
|
877
|
+
if (qualified.path.length)
|
|
878
|
+
addQualified(qualified.base, qualified.path, invoked);
|
|
879
|
+
else
|
|
880
|
+
addSimple(qualified.base, invoked);
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
const unwrapped = unwrapNamedType(core, sup);
|
|
885
|
+
if (unwrapped) {
|
|
886
|
+
addSimple(sliceText(unwrapped, source), invoked);
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
if (isMemberAccessNode(sup, core)) {
|
|
890
|
+
const chain = collectMemberAccessChain({ sup, source, chainNode: core });
|
|
891
|
+
if (chain && isSimpleTypeNameNode(chain.base, sup) && chain.names.length) {
|
|
892
|
+
addQualified(sliceText(chain.base, source), [...chain.names].reverse(), invoked);
|
|
893
|
+
}
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
if (memberAccessTraversalTypes(sup).has(core.type))
|
|
897
|
+
return;
|
|
898
|
+
for (const child of core.namedChildren) {
|
|
899
|
+
if (child.type === "type_arguments")
|
|
900
|
+
continue;
|
|
901
|
+
collectType(child, invoked);
|
|
902
|
+
}
|
|
903
|
+
};
|
|
904
|
+
const consider = (node) => {
|
|
905
|
+
for (const rule of ancestry.clauses) {
|
|
906
|
+
if (node.type !== rule.nodeType)
|
|
907
|
+
continue;
|
|
908
|
+
if (superclassOnly && !rule.supertype)
|
|
909
|
+
continue;
|
|
910
|
+
if (superclassOnly && rule.supertype === "first-match") {
|
|
911
|
+
if (usedFirstMatch)
|
|
912
|
+
continue;
|
|
913
|
+
usedFirstMatch = true;
|
|
914
|
+
}
|
|
915
|
+
let target = node;
|
|
916
|
+
if (superclassOnly && rule.supertype === "first-child") {
|
|
917
|
+
target = target.namedChildren[0] ?? target;
|
|
918
|
+
}
|
|
919
|
+
collectType(target, false);
|
|
920
|
+
}
|
|
921
|
+
if (superclassOnly || node.type !== "call" || !ancestry.mixinCalls?.length)
|
|
922
|
+
return;
|
|
923
|
+
if (node.childForFieldName("receiver"))
|
|
924
|
+
return;
|
|
925
|
+
const methodNode = node.childForFieldName("method");
|
|
926
|
+
const methodName = methodNode ? sliceText(methodNode, source) : undefined;
|
|
927
|
+
if (!methodName || !ancestry.mixinCalls.includes(methodName))
|
|
928
|
+
return;
|
|
929
|
+
const args = node.childForFieldName("arguments");
|
|
930
|
+
if (args)
|
|
931
|
+
collectType(args, false);
|
|
932
|
+
};
|
|
933
|
+
for (const child of container.namedChildren) {
|
|
934
|
+
consider(child);
|
|
935
|
+
for (const grand of child.namedChildren)
|
|
936
|
+
consider(grand);
|
|
937
|
+
}
|
|
938
|
+
return bases;
|
|
939
|
+
}
|
|
940
|
+
function asMemberContainer(index, def) {
|
|
941
|
+
return memberContainerForDefinition(index, def);
|
|
942
|
+
}
|
|
943
|
+
function importedMemberContainer(index, result) {
|
|
944
|
+
if (!result || "namespace" in result)
|
|
945
|
+
return undefined;
|
|
946
|
+
return asMemberContainer(index, result);
|
|
947
|
+
}
|
|
948
|
+
function resolveNamedMemberContainer(index, mod, name, normalize) {
|
|
949
|
+
const typedLocals = memberDeclaringLocals(mod, name, normalize);
|
|
950
|
+
const topLevel = typedLocals.filter((local) => !local.isMember);
|
|
951
|
+
const candidates = topLevel.length ? topLevel : typedLocals;
|
|
952
|
+
if (candidates.length === 1)
|
|
953
|
+
return candidates[0];
|
|
954
|
+
if (candidates.length > 1)
|
|
955
|
+
return undefined;
|
|
956
|
+
const normalizedName = normalize(name);
|
|
957
|
+
const languageId = supportForFileWithoutHeaderSample(mod.file, index.languageExtensions)?.id;
|
|
958
|
+
const explicitBinding = effectiveExplicitBinding(mod.imports, languageId ?? "", (binding) => (binding.kind === "named" || binding.kind === "default") && normalize(binding.local) === normalizedName);
|
|
959
|
+
if (explicitBinding?.kind === "named") {
|
|
960
|
+
const container = importedMemberContainer(index, resolveImported(index, explicitBinding, explicitBinding.imported));
|
|
961
|
+
if (container)
|
|
962
|
+
return container;
|
|
963
|
+
}
|
|
964
|
+
else if (explicitBinding?.kind === "default") {
|
|
965
|
+
const container = importedMemberContainer(index, resolveImported(index, explicitBinding, "default"));
|
|
966
|
+
if (container)
|
|
967
|
+
return container;
|
|
968
|
+
}
|
|
969
|
+
if (languageId) {
|
|
970
|
+
const starDecision = resolveStarImportedName(index, mod, languageId, name);
|
|
971
|
+
if (starDecision.status === "ambiguous")
|
|
972
|
+
return undefined;
|
|
973
|
+
if (starDecision.status === "resolved") {
|
|
974
|
+
const container = asMemberContainer(index, starDecision.definition);
|
|
975
|
+
if (container)
|
|
976
|
+
return container;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
const exported = resolveExport(index, mod.file, name, { allowLocalFallback: false });
|
|
980
|
+
if (exported?.kind === "resolved")
|
|
981
|
+
return asMemberContainer(index, exported.def);
|
|
982
|
+
return undefined;
|
|
983
|
+
}
|
|
984
|
+
async function resolveQualifiedMemberContainer(index, mod, baseName, path, normalize, sup) {
|
|
985
|
+
if (path.length === 0)
|
|
986
|
+
return undefined;
|
|
987
|
+
if (sup.id === "cpp") {
|
|
988
|
+
return (await resolveCppQualifiedMemberContainer(index, mod, [baseName, ...path])) ?? undefined;
|
|
989
|
+
}
|
|
990
|
+
const namespaceImports = mod.imports.filter((imp) => imp.kind === "namespace" && normalize(imp.localNS) === normalize(baseName));
|
|
991
|
+
if (namespaceImports.length !== 1)
|
|
992
|
+
return undefined;
|
|
993
|
+
const resolved = namespaceImports[0].resolved;
|
|
994
|
+
let file = typeof resolved === "string" ? resolved : undefined;
|
|
995
|
+
if (!file)
|
|
996
|
+
return undefined;
|
|
997
|
+
for (let partIndex = 0; partIndex < path.length; partIndex += 1) {
|
|
998
|
+
const part = path[partIndex];
|
|
999
|
+
const last = partIndex === path.length - 1;
|
|
1000
|
+
const hit = resolveExport(index, file, part, { allowLocalFallback: false });
|
|
1001
|
+
if (!hit)
|
|
1002
|
+
return undefined;
|
|
1003
|
+
if (hit.kind === "namespace") {
|
|
1004
|
+
if (last)
|
|
1005
|
+
return undefined;
|
|
1006
|
+
file = hit.file;
|
|
1007
|
+
continue;
|
|
1008
|
+
}
|
|
1009
|
+
if (!last)
|
|
1010
|
+
return undefined;
|
|
1011
|
+
return asMemberContainer(index, hit.def);
|
|
1012
|
+
}
|
|
1013
|
+
return undefined;
|
|
1014
|
+
}
|
|
1015
|
+
function nodeContainsUnprovenHeritage(node) {
|
|
1016
|
+
if (isUnprovenHeritageExpression(node))
|
|
1017
|
+
return true;
|
|
1018
|
+
return node.namedChildren.some((child) => nodeContainsUnprovenHeritage(child));
|
|
1019
|
+
}
|
|
1020
|
+
/** A heritage or mixin clause whose type expression is not a resolvable name. */
|
|
1021
|
+
function containerHeritageIsUnproven(container, source, sup) {
|
|
1022
|
+
const ancestry = MEMBER_ACCESS_ROWS[sup.id]?.receiverAncestry;
|
|
1023
|
+
if (!ancestry)
|
|
1024
|
+
return false;
|
|
1025
|
+
const clauseTypes = new Set(ancestry.clauses.map((rule) => rule.nodeType));
|
|
1026
|
+
const nodes = [];
|
|
1027
|
+
for (const child of container.namedChildren) {
|
|
1028
|
+
nodes.push(child);
|
|
1029
|
+
for (const grand of child.namedChildren)
|
|
1030
|
+
nodes.push(grand);
|
|
1031
|
+
}
|
|
1032
|
+
if (nodes.some((node) => clauseTypes.has(node.type) && nodeContainsUnprovenHeritage(node)))
|
|
1033
|
+
return true;
|
|
1034
|
+
const mixinCalls = ancestry.mixinCalls;
|
|
1035
|
+
if (!mixinCalls?.length)
|
|
1036
|
+
return false;
|
|
1037
|
+
return nodes.some((node) => {
|
|
1038
|
+
if (node.type !== "call" || node.childForFieldName("receiver"))
|
|
1039
|
+
return false;
|
|
1040
|
+
const methodNode = node.childForFieldName("method");
|
|
1041
|
+
const methodName = methodNode ? sliceText(methodNode, source) : undefined;
|
|
1042
|
+
if (!methodName || !mixinCalls.includes(methodName))
|
|
1043
|
+
return false;
|
|
1044
|
+
const args = node.childForFieldName("arguments");
|
|
1045
|
+
return !!args && nodeContainsUnprovenHeritage(args);
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
function definitionForContainer(mod, container) {
|
|
1049
|
+
const nameNode = container.childForFieldName("name");
|
|
1050
|
+
if (!nameNode)
|
|
1051
|
+
return undefined;
|
|
1052
|
+
const matches = mod.locals.filter((local) => declaresMembers(local) && local.range.start.index === nameNode.startIndex);
|
|
1053
|
+
return matches.length === 1 ? matches[0] : undefined;
|
|
1054
|
+
}
|
|
1055
|
+
async function resolveReceiverTypeName(index, mod, sup, typeName) {
|
|
1056
|
+
const trimmed = typeName.trim().replace(/^\\+/, "");
|
|
1057
|
+
if (!trimmed)
|
|
1058
|
+
return undefined;
|
|
1059
|
+
const normalize = sup.normalizeIdentifier;
|
|
1060
|
+
for (const separator of ["::", "\\", "."]) {
|
|
1061
|
+
if (!trimmed.includes(separator))
|
|
1062
|
+
continue;
|
|
1063
|
+
const parts = trimmed.split(separator).filter(Boolean);
|
|
1064
|
+
if (parts.length < 2)
|
|
1065
|
+
return undefined;
|
|
1066
|
+
return resolveQualifiedMemberContainer(index, mod, parts[0], parts.slice(1), normalize, sup);
|
|
1067
|
+
}
|
|
1068
|
+
return resolveNamedMemberContainer(index, mod, trimmed, normalize);
|
|
1069
|
+
}
|
|
1070
|
+
async function resolveDeclaredBase(index, mod, sup, base) {
|
|
1071
|
+
const normalize = sup.normalizeIdentifier;
|
|
1072
|
+
if (base.kind === "simple")
|
|
1073
|
+
return resolveNamedMemberContainer(index, mod, base.name, normalize);
|
|
1074
|
+
return resolveQualifiedMemberContainer(index, mod, base.base, base.path, normalize, sup);
|
|
1075
|
+
}
|
|
1076
|
+
/**
|
|
1077
|
+
* True when `def` and every resolved supertype lack a direct `memberName`. An unresolved
|
|
1078
|
+
* supertype, an unreadable declaration, or a declared member returns false.
|
|
1079
|
+
*/
|
|
1080
|
+
async function hierarchyOmitsMember(index, def, memberName, memberScope, seen) {
|
|
1081
|
+
const containerDef = asMemberContainer(index, def);
|
|
1082
|
+
if (!containerDef)
|
|
1083
|
+
return false;
|
|
1084
|
+
const start = containerDef.range.start;
|
|
1085
|
+
const key = `${fileIdentityKey(containerDef.file)}:${start.index ?? `${start.line}:${start.column}`}`;
|
|
1086
|
+
if (seen.has(key))
|
|
1087
|
+
return true;
|
|
1088
|
+
seen.add(key);
|
|
1089
|
+
const ref = await keywordClassRefFromDef(index, containerDef);
|
|
1090
|
+
if (!ref)
|
|
1091
|
+
return false;
|
|
1092
|
+
const normalize = ref.context.sup.normalizeIdentifier;
|
|
1093
|
+
const memberPredicate = memberScope === "any"
|
|
1094
|
+
? undefined
|
|
1095
|
+
: (local) => matchesReceiverMemberScope(local, memberScope, ref.context, ref.container);
|
|
1096
|
+
if (findDirectLocalsWithinNode(ref.module.locals, memberName, ref.container, ref.context, normalize, memberPredicate)
|
|
1097
|
+
.length > 0) {
|
|
1098
|
+
return false;
|
|
1099
|
+
}
|
|
1100
|
+
if (ref.context.sup.id === "csharp" || ref.context.sup.id === "swift") {
|
|
1101
|
+
const shared = await findSharedOwnerMemberDefinitions({
|
|
1102
|
+
index,
|
|
1103
|
+
ownerFile: containerDef.file,
|
|
1104
|
+
ownerContainer: ref.container,
|
|
1105
|
+
ownerSource: ref.context.source,
|
|
1106
|
+
languageId: ref.context.sup.id,
|
|
1107
|
+
member: memberName,
|
|
1108
|
+
memberScope,
|
|
1109
|
+
});
|
|
1110
|
+
if (shared.length > 0)
|
|
1111
|
+
return false;
|
|
1112
|
+
}
|
|
1113
|
+
if (containerHeritageIsUnproven(ref.container, ref.context.source, ref.context.sup))
|
|
1114
|
+
return false;
|
|
1115
|
+
const bases = collectDeclaredBaseTypes(ref.container, ref.context.source, ref.context.sup, false);
|
|
1116
|
+
for (const base of bases) {
|
|
1117
|
+
const baseDef = await resolveDeclaredBase(index, ref.module, ref.context.sup, base);
|
|
1118
|
+
if (!baseDef)
|
|
1119
|
+
return false;
|
|
1120
|
+
if (!(await hierarchyOmitsMember(index, baseDef, memberName, memberScope, seen)))
|
|
1121
|
+
return false;
|
|
1122
|
+
}
|
|
1123
|
+
return true;
|
|
1124
|
+
}
|
|
1125
|
+
/**
|
|
1126
|
+
* A classified receiver (`new Widget()`, a named type, `this`, or `super`) excludes a failed
|
|
1127
|
+
* member lookup only when its type resolves to a member-declaring definition, no supertype is
|
|
1128
|
+
* unresolved, and that type does not declare `memberName`. Otherwise the site stays unproven.
|
|
1129
|
+
*/
|
|
1130
|
+
export async function provenClassifiedReceiverOmitsMember(index, fileId, parsed, accessNode, objectNode, memberName) {
|
|
1131
|
+
const mod = index.byFile.get(fileIdentityKey(fileId));
|
|
1132
|
+
if (!mod)
|
|
1133
|
+
return false;
|
|
1134
|
+
const receiver = classifyReceiver(parsed.sup, objectNode, parsed.source, new Map(), accessNode.startIndex, accessNode, (callee) => {
|
|
1135
|
+
const scope = getOrBuildScopeIndex(index, fileId, parsed.source, parsed.sup, mod, parsed.tree);
|
|
1136
|
+
return !!findClosestScopeBinding(scope, sliceText(callee, parsed.source), callee, parsed.sup);
|
|
1137
|
+
});
|
|
1138
|
+
if (!receiver)
|
|
1139
|
+
return false;
|
|
1140
|
+
if (receiver.kind === "named-type") {
|
|
1141
|
+
const def = await resolveReceiverTypeName(index, mod, parsed.sup, receiver.typeName);
|
|
1142
|
+
if (!def)
|
|
1143
|
+
return false;
|
|
1144
|
+
return hierarchyOmitsMember(index, def, memberName, receiver.memberScope, new Set());
|
|
1145
|
+
}
|
|
1146
|
+
const container = nearestMemberContainer(accessNode);
|
|
1147
|
+
if (!container)
|
|
1148
|
+
return false;
|
|
1149
|
+
if (receiver.kind === "supertype") {
|
|
1150
|
+
if (containerHeritageIsUnproven(container, parsed.source, parsed.sup))
|
|
1151
|
+
return false;
|
|
1152
|
+
const bases = collectDeclaredBaseTypes(container, parsed.source, parsed.sup, true);
|
|
1153
|
+
if (!bases.length)
|
|
1154
|
+
return false;
|
|
1155
|
+
const seen = new Set();
|
|
1156
|
+
for (const base of bases) {
|
|
1157
|
+
const baseDef = await resolveDeclaredBase(index, mod, parsed.sup, base);
|
|
1158
|
+
if (!baseDef)
|
|
1159
|
+
return false;
|
|
1160
|
+
if (!(await hierarchyOmitsMember(index, baseDef, memberName, receiver.memberScope, seen)))
|
|
1161
|
+
return false;
|
|
1162
|
+
}
|
|
1163
|
+
return true;
|
|
1164
|
+
}
|
|
1165
|
+
const def = definitionForContainer(mod, container);
|
|
1166
|
+
if (!def)
|
|
1167
|
+
return false;
|
|
1168
|
+
return hierarchyOmitsMember(index, def, memberName, receiver.memberScope, new Set());
|
|
1169
|
+
}
|
|
1170
|
+
async function keywordClassRefFromDef(index, def) {
|
|
1171
|
+
const module = index.byFile.get(fileIdentityKey(def.file));
|
|
1172
|
+
if (!module)
|
|
1173
|
+
return null;
|
|
1174
|
+
const context = await ensureParsedContext(def.file, undefined, index.languageExtensions);
|
|
1175
|
+
const start = def.range.start;
|
|
1176
|
+
const position = {
|
|
1177
|
+
row: start.line - 1,
|
|
1178
|
+
column: start.column - 1,
|
|
1179
|
+
};
|
|
1180
|
+
const nameNode = context.tree.rootNode.descendantForPosition(position, position);
|
|
1181
|
+
const container = nearestMemberContainer(nameNode);
|
|
1182
|
+
if (!container)
|
|
1183
|
+
return null;
|
|
1184
|
+
return { file: def.file, container, context, module };
|
|
1185
|
+
}
|
|
1186
|
+
async function keywordClassRefFromNode(index, mod, node) {
|
|
1187
|
+
const context = await ensureParsedContext(mod.file, undefined, index.languageExtensions);
|
|
1188
|
+
const memberNode = context.tree.rootNode.descendantForPosition(node.startPosition, node.startPosition);
|
|
1189
|
+
const container = findEnclosingClassContainer(memberNode);
|
|
1190
|
+
if (!container)
|
|
1191
|
+
return null;
|
|
1192
|
+
return { file: mod.file, container, context, module: mod };
|
|
1193
|
+
}
|
|
1194
|
+
function crossFilePeerMemberHidden(useFile, memberDef, targetContext) {
|
|
1195
|
+
const languageId = targetContext.sup.id;
|
|
1196
|
+
if (languageId !== "java" && languageId !== "kotlin" && languageId !== "swift")
|
|
1197
|
+
return false;
|
|
1198
|
+
const start = memberDef.range.start;
|
|
1199
|
+
const position = { row: Math.max(0, start.line - 1), column: Math.max(0, start.column - 1) };
|
|
1200
|
+
const nameNode = targetContext.tree.rootNode.descendantForPosition(position, position);
|
|
1201
|
+
if (!nameNode)
|
|
1202
|
+
return false;
|
|
1203
|
+
if (languageId === "swift") {
|
|
1204
|
+
return isSwiftCrossFileHiddenSharedOwnerMember(languageId, useFile, memberDef.file, nameNode);
|
|
1205
|
+
}
|
|
1206
|
+
if (fileIdentityKey(useFile) === fileIdentityKey(memberDef.file))
|
|
1207
|
+
return false;
|
|
1208
|
+
return !isExportedDeclaration(languageId, nameNode);
|
|
1209
|
+
}
|
|
1210
|
+
async function baseRefsFromContainer(index, mod, container, source, sup, superclassOnly, tree) {
|
|
1211
|
+
const bases = collectDeclaredBaseTypes(container, source, sup, superclassOnly);
|
|
1212
|
+
const refs = [];
|
|
1213
|
+
const seen = new Set();
|
|
1214
|
+
const normalize = (name) => {
|
|
1215
|
+
const normalized = sup.normalizeIdentifier(name);
|
|
1216
|
+
return sup.id === "php" ? foldPhpIdentifierCase(normalized) : normalized;
|
|
1217
|
+
};
|
|
1218
|
+
for (const base of bases) {
|
|
1219
|
+
let def = base.kind === "simple"
|
|
1220
|
+
? resolveNamedMemberContainer(index, mod, base.name, normalize)
|
|
1221
|
+
: await resolveQualifiedMemberContainer(index, mod, base.base, base.path, normalize, sup);
|
|
1222
|
+
if (!def && sup.id === "php" && base.kind === "simple") {
|
|
1223
|
+
def = resolvePhpNamespaceSymbol(index, source, tree, container, base.name, mod.imports, "class") ?? undefined;
|
|
1224
|
+
}
|
|
1225
|
+
if (!def)
|
|
1226
|
+
continue;
|
|
1227
|
+
const ref = await keywordClassRefFromDef(index, def);
|
|
1228
|
+
if (!ref)
|
|
1229
|
+
continue;
|
|
1230
|
+
// `super`, `base`, and `parent` follow class ancestors only. An own receiver also inherits
|
|
1231
|
+
// interface or protocol members, so apply the class-only filter only to supertype lookup.
|
|
1232
|
+
const interfaceLike = ref.container.type === "interface_declaration" ||
|
|
1233
|
+
ref.container.type === "protocol_declaration" ||
|
|
1234
|
+
ref.container.type === "trait_item" ||
|
|
1235
|
+
/^(?:interface|protocol|trait)\b/.test(sliceText(ref.container, ref.context.source).trimStart());
|
|
1236
|
+
if (superclassOnly && (def.kind !== SymbolKind.Class || interfaceLike))
|
|
1237
|
+
continue;
|
|
1238
|
+
// Kotlin classifies interfaces as SymbolKind.Class too, so the superclass is identified
|
|
1239
|
+
// syntactically: `Base()` is a constructor invocation, while bare delegation-specifier
|
|
1240
|
+
// entries (`Face`, `by` delegations) are interfaces. Interface-only super lookup stays
|
|
1241
|
+
// unresolved, while an own receiver can inherit their default members.
|
|
1242
|
+
if (superclassOnly && sup.id === "kotlin" && !base.invoked)
|
|
1243
|
+
continue;
|
|
1244
|
+
const key = keywordClassKey(def);
|
|
1245
|
+
if (seen.has(key))
|
|
1246
|
+
continue;
|
|
1247
|
+
seen.add(key);
|
|
1248
|
+
refs.push(ref);
|
|
1249
|
+
}
|
|
1250
|
+
return refs;
|
|
1251
|
+
}
|
|
1252
|
+
const HIERARCHY_OVERLOAD_LANGUAGES = new Set(["java", "kotlin", "csharp"]);
|
|
1253
|
+
async function resolveKeywordReceiverMember(index, mod, node, member, memberScope, startAtAncestor, knownArgumentCount, explicitClassDef, report) {
|
|
1254
|
+
const current = explicitClassDef
|
|
1255
|
+
? await keywordClassRefFromDef(index, explicitClassDef)
|
|
1256
|
+
: await keywordClassRefFromNode(index, mod, node);
|
|
1257
|
+
if (!current)
|
|
1258
|
+
return undefined;
|
|
1259
|
+
let level = startAtAncestor
|
|
1260
|
+
? await baseRefsFromContainer(index, current.module, current.container, current.context.source, current.context.sup, true, current.context.tree)
|
|
1261
|
+
: [current];
|
|
1262
|
+
if (level.length === 0)
|
|
1263
|
+
return undefined;
|
|
1264
|
+
// Java, Kotlin, and C# overload across the hierarchy: when no member at one level accepts the
|
|
1265
|
+
// call, a base level may. C++ and Swift hide a base name behind any same-named member.
|
|
1266
|
+
// An unknown count (a Kotlin spread) keeps every non-overridden overload as a candidate.
|
|
1267
|
+
const spansHierarchy = HIERARCHY_OVERLOAD_LANGUAGES.has(current.context.sup.id);
|
|
1268
|
+
// Java, Kotlin, and C# base members that are private are not accessible from a subclass.
|
|
1269
|
+
const filtersPrivateBases = HIERARCHY_OVERLOAD_LANGUAGES.has(current.context.sup.id);
|
|
1270
|
+
let lenient;
|
|
1271
|
+
// Accepted overloads across levels. A deeper one is overridden only by an accepted method with
|
|
1272
|
+
// the same parameter types in a class proven to derive from its owner on the walked path;
|
|
1273
|
+
// unrelated owners with one signature (two interfaces) stay separate and ambiguous.
|
|
1274
|
+
const accepted = [];
|
|
1275
|
+
const memberKey = (def) => `${fileIdentityKey(def.file)}:${def.range.start.index}`;
|
|
1276
|
+
const ownerOf = new Map();
|
|
1277
|
+
const subclassesOf = new Map();
|
|
1278
|
+
const recordOwner = (from, owner) => {
|
|
1279
|
+
for (let at = from; at < matches.length; at += 1)
|
|
1280
|
+
ownerOf.set(memberKey(matches[at]), owner);
|
|
1281
|
+
};
|
|
1282
|
+
let matches = [];
|
|
1283
|
+
const visited = new Set([
|
|
1284
|
+
keywordContainerKey(current.file, current.container),
|
|
1285
|
+
...level.map((candidate) => keywordContainerKey(candidate.file, candidate.container)),
|
|
1286
|
+
]);
|
|
1287
|
+
for (let depth = 0; depth < RECEIVER_HIERARCHY_DEPTH && level.length; depth += 1) {
|
|
1288
|
+
matches = [];
|
|
1289
|
+
for (const candidate of level) {
|
|
1290
|
+
const ownerKey = keywordContainerKey(candidate.file, candidate.container);
|
|
1291
|
+
const before = matches.length;
|
|
1292
|
+
const memberPredicate = memberScope === "any"
|
|
1293
|
+
? undefined
|
|
1294
|
+
: (local) => matchesReceiverMemberScope(local, memberScope, candidate.context, candidate.container);
|
|
1295
|
+
appendDirectKeywordMembers(candidate.module.locals, member, candidate.container, candidate.context, candidate.context.sup.normalizeIdentifier, memberPredicate, matches);
|
|
1296
|
+
recordOwner(before, ownerKey);
|
|
1297
|
+
}
|
|
1298
|
+
for (const candidate of level) {
|
|
1299
|
+
if (candidate.context.sup.id !== "csharp" && candidate.context.sup.id !== "swift")
|
|
1300
|
+
continue;
|
|
1301
|
+
const ownerKey = keywordContainerKey(candidate.file, candidate.container);
|
|
1302
|
+
const before = matches.length;
|
|
1303
|
+
const sharedContainers = await resolveSharedOwnerContainers({
|
|
1304
|
+
index,
|
|
1305
|
+
ownerFile: candidate.file,
|
|
1306
|
+
ownerContainer: candidate.container,
|
|
1307
|
+
ownerSource: candidate.context.source,
|
|
1308
|
+
languageId: candidate.context.sup.id,
|
|
1309
|
+
});
|
|
1310
|
+
for (const shared of sharedContainers) {
|
|
1311
|
+
const crossFileSwift = candidate.context.sup.id === "swift" && fileIdentityKey(candidate.file) !== fileIdentityKey(shared.file);
|
|
1312
|
+
const sharedPredicate = (local) => {
|
|
1313
|
+
if (memberScope !== "any" &&
|
|
1314
|
+
!matchesReceiverMemberScope(local, memberScope, shared.context, shared.container)) {
|
|
1315
|
+
return false;
|
|
194
1316
|
}
|
|
195
|
-
if (
|
|
196
|
-
return
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
1317
|
+
if (!crossFileSwift)
|
|
1318
|
+
return true;
|
|
1319
|
+
const nameNode = nameNodeForDef(shared.context, local);
|
|
1320
|
+
return !!nameNode && !isSwiftCrossFileHiddenSharedOwnerMember("swift", candidate.file, shared.file, nameNode);
|
|
1321
|
+
};
|
|
1322
|
+
appendDirectKeywordMembers(shared.module.locals, member, shared.container, shared.context, shared.context.sup.normalizeIdentifier, sharedPredicate, matches);
|
|
1323
|
+
}
|
|
1324
|
+
// Partial parts of one type share its identity.
|
|
1325
|
+
recordOwner(before, ownerKey);
|
|
1326
|
+
}
|
|
1327
|
+
let uniqueMatches = uniqueReceiverMemberCandidates(matches);
|
|
1328
|
+
// A private member of a base class is not accessible: it neither answers nor stops the walk.
|
|
1329
|
+
// The first level of a `this` lookup is the class itself, including C# partial parts.
|
|
1330
|
+
if (filtersPrivateBases && (startAtAncestor || depth > 0) && uniqueMatches.length) {
|
|
1331
|
+
const inherited = [];
|
|
1332
|
+
for (const candidate of uniqueMatches) {
|
|
1333
|
+
if (!(await isUninheritedPrivateMember(index, mod, node, candidate)))
|
|
1334
|
+
inherited.push(candidate);
|
|
1335
|
+
}
|
|
1336
|
+
uniqueMatches = inherited;
|
|
1337
|
+
}
|
|
1338
|
+
if (uniqueMatches.length && report)
|
|
1339
|
+
report.named = true;
|
|
1340
|
+
if (uniqueMatches.length) {
|
|
1341
|
+
const allowUniqueArityMismatch = !startAtAncestor && depth === 0;
|
|
1342
|
+
if (!spansHierarchy) {
|
|
1343
|
+
return await selectReceiverMemberCandidates(index, uniqueMatches, knownArgumentCount, allowUniqueArityMismatch);
|
|
1344
|
+
}
|
|
1345
|
+
for (const candidate of uniqueMatches) {
|
|
1346
|
+
if (knownArgumentCount !== undefined &&
|
|
1347
|
+
(await receiverMemberAcceptsArgumentCount(index, candidate, knownArgumentCount)) === false) {
|
|
1348
|
+
continue;
|
|
1349
|
+
}
|
|
1350
|
+
const owner = ownerOf.get(memberKey(candidate)) ?? "";
|
|
1351
|
+
const subclasses = subclassesOf.get(owner);
|
|
1352
|
+
let overridden = false;
|
|
1353
|
+
for (const entry of accepted) {
|
|
1354
|
+
if (subclasses?.has(entry.owner) && (await sameParameterTypes(index, entry.def, candidate))) {
|
|
1355
|
+
overridden = true;
|
|
1356
|
+
break;
|
|
201
1357
|
}
|
|
202
1358
|
}
|
|
1359
|
+
if (!overridden)
|
|
1360
|
+
accepted.push({ def: candidate, owner });
|
|
1361
|
+
}
|
|
1362
|
+
// Navigation still names the only incompatible candidate when no ancestor accepts the call.
|
|
1363
|
+
if (allowUniqueArityMismatch && !lenient) {
|
|
1364
|
+
lenient = await selectReceiverMemberCandidates(index, uniqueMatches, knownArgumentCount, true);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
const next = [];
|
|
1368
|
+
for (const candidate of level) {
|
|
1369
|
+
const childKey = keywordContainerKey(candidate.file, candidate.container);
|
|
1370
|
+
const childSubclasses = subclassesOf.get(childKey);
|
|
1371
|
+
for (const parent of await baseRefsFromContainer(index, candidate.module, candidate.container, candidate.context.source, candidate.context.sup, startAtAncestor, candidate.context.tree)) {
|
|
1372
|
+
const key = keywordContainerKey(parent.file, parent.container);
|
|
1373
|
+
// Every class on a path to this ancestor derives from it, including through a diamond.
|
|
1374
|
+
const subclasses = subclassesOf.get(key) ?? new Set();
|
|
1375
|
+
subclasses.add(childKey);
|
|
1376
|
+
for (const subclass of childSubclasses ?? [])
|
|
1377
|
+
subclasses.add(subclass);
|
|
1378
|
+
subclassesOf.set(key, subclasses);
|
|
1379
|
+
if (visited.has(key))
|
|
1380
|
+
continue;
|
|
1381
|
+
visited.add(key);
|
|
1382
|
+
next.push(parent);
|
|
203
1383
|
}
|
|
204
1384
|
}
|
|
1385
|
+
level = next;
|
|
1386
|
+
}
|
|
1387
|
+
if (!spansHierarchy)
|
|
1388
|
+
return undefined;
|
|
1389
|
+
// Without type ranking, two surviving overloads are ambiguous, including a fixed-arity and a
|
|
1390
|
+
// variable-arity one: `hit(String)` and `hit(int...)` both take one argument, and only argument
|
|
1391
|
+
// types decide.
|
|
1392
|
+
if (accepted.length > 1) {
|
|
1393
|
+
if (report)
|
|
1394
|
+
report.ambiguous = true;
|
|
1395
|
+
return undefined;
|
|
1396
|
+
}
|
|
1397
|
+
return accepted[0]?.def ?? lenient;
|
|
1398
|
+
}
|
|
1399
|
+
/** The parameter list of a Java, Kotlin, or C# method declaration. */
|
|
1400
|
+
async function memberParameterList(index, def) {
|
|
1401
|
+
const context = await ensureParsedContext(def.file, index.parsed?.get(fileIdentityKey(def.file)), index.languageExtensions).catch(() => null);
|
|
1402
|
+
const start = def.range.start.index;
|
|
1403
|
+
if (!context || start === undefined)
|
|
1404
|
+
return null;
|
|
1405
|
+
const declaration = context.tree.rootNode.descendantForIndex(start, def.range.end.index ?? start).parent;
|
|
1406
|
+
return (declaration?.childForFieldName("parameters") ??
|
|
1407
|
+
declaration?.namedChildren.find((child) => child.type === "function_value_parameters") ??
|
|
1408
|
+
null);
|
|
1409
|
+
}
|
|
1410
|
+
/** Parameter type texts of a Java, Kotlin, or C# method; null when a type cannot be read. */
|
|
1411
|
+
async function memberParameterTypes(index, def) {
|
|
1412
|
+
const parameters = await memberParameterList(index, def);
|
|
1413
|
+
if (!parameters)
|
|
1414
|
+
return null;
|
|
1415
|
+
const types = [];
|
|
1416
|
+
for (const parameter of parameters.namedChildren) {
|
|
1417
|
+
const type = parameter.childForFieldName("type") ?? parameter.namedChildren.at(-1);
|
|
1418
|
+
if (!type)
|
|
1419
|
+
return null;
|
|
1420
|
+
types.push(type.text.replace(/\s+/g, ""));
|
|
1421
|
+
}
|
|
1422
|
+
return types;
|
|
1423
|
+
}
|
|
1424
|
+
/** The import that binds a type name in a module, as text; empty when no import binds it. */
|
|
1425
|
+
function importedTypeSource(module, name) {
|
|
1426
|
+
for (const imp of module.imports) {
|
|
1427
|
+
if (imp.kind !== "named" || imp.local !== name)
|
|
1428
|
+
continue;
|
|
1429
|
+
const target = typeof imp.resolved === "string" ? imp.resolved : (imp.resolved?.external ?? "");
|
|
1430
|
+
return `${target}:${imp.imported}`;
|
|
1431
|
+
}
|
|
1432
|
+
return "";
|
|
1433
|
+
}
|
|
1434
|
+
/** Type parameter names declared by a method or any type enclosing it. */
|
|
1435
|
+
async function typeParameterNamesAround(index, def) {
|
|
1436
|
+
const names = new Set();
|
|
1437
|
+
const context = await ensureParsedContext(def.file, index.parsed?.get(fileIdentityKey(def.file)), index.languageExtensions).catch(() => null);
|
|
1438
|
+
const start = def.range.start.index;
|
|
1439
|
+
if (!context || start === undefined)
|
|
1440
|
+
return names;
|
|
1441
|
+
let current = context.tree.rootNode.descendantForIndex(start, def.range.end.index ?? start);
|
|
1442
|
+
for (; current; current = current.parent) {
|
|
1443
|
+
const parameters = current.childForFieldName("type_parameters") ??
|
|
1444
|
+
current.namedChildren.find((child) => child.type === "type_parameters" || child.type === "type_parameter_list");
|
|
1445
|
+
if (!parameters)
|
|
1446
|
+
continue;
|
|
1447
|
+
for (const parameter of parameters.namedChildren) {
|
|
1448
|
+
const name = parameter.childForFieldName("name") ??
|
|
1449
|
+
parameter.namedChildren.find((child) => /identifier/u.test(child.type)) ??
|
|
1450
|
+
(/identifier/u.test(parameter.type) ? parameter : null);
|
|
1451
|
+
if (name)
|
|
1452
|
+
names.add(name.text);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
return names;
|
|
1456
|
+
}
|
|
1457
|
+
/** Built-in parameter types whose spelling names one type in every file. */
|
|
1458
|
+
const BUILTIN_PARAMETER_TYPES = {
|
|
1459
|
+
java: new Set([
|
|
1460
|
+
"int",
|
|
1461
|
+
"long",
|
|
1462
|
+
"short",
|
|
1463
|
+
"byte",
|
|
1464
|
+
"char",
|
|
1465
|
+
"boolean",
|
|
1466
|
+
"float",
|
|
1467
|
+
"double",
|
|
1468
|
+
"String",
|
|
1469
|
+
"Object",
|
|
1470
|
+
"Integer",
|
|
1471
|
+
"Long",
|
|
1472
|
+
"Boolean",
|
|
1473
|
+
"Double",
|
|
1474
|
+
"Float",
|
|
1475
|
+
"Short",
|
|
1476
|
+
"Byte",
|
|
1477
|
+
"Character",
|
|
1478
|
+
]),
|
|
1479
|
+
kotlin: new Set(["Int", "Long", "Short", "Byte", "Char", "Boolean", "Float", "Double", "String", "Any", "Unit"]),
|
|
1480
|
+
csharp: new Set([
|
|
1481
|
+
"int",
|
|
1482
|
+
"long",
|
|
1483
|
+
"short",
|
|
1484
|
+
"byte",
|
|
1485
|
+
"char",
|
|
1486
|
+
"bool",
|
|
1487
|
+
"float",
|
|
1488
|
+
"double",
|
|
1489
|
+
"decimal",
|
|
1490
|
+
"string",
|
|
1491
|
+
"object",
|
|
1492
|
+
"uint",
|
|
1493
|
+
"ulong",
|
|
1494
|
+
"ushort",
|
|
1495
|
+
"sbyte",
|
|
1496
|
+
]),
|
|
1497
|
+
};
|
|
1498
|
+
/**
|
|
1499
|
+
* Whether two methods take the same parameter types, so the one in a derived class overrides the
|
|
1500
|
+
* other. Equal spelling is proof only within one file or for built-in types; elsewhere each
|
|
1501
|
+
* spelling must resolve to the same declaration from its own file.
|
|
1502
|
+
*/
|
|
1503
|
+
async function sameParameterTypes(index, left, right) {
|
|
1504
|
+
// A type parameter (`T`) names a different type in each declaration after substitution, so
|
|
1505
|
+
// equal spelling proves nothing.
|
|
1506
|
+
const [leftGenerics, rightGenerics] = await Promise.all([
|
|
1507
|
+
typeParameterNamesAround(index, left),
|
|
1508
|
+
typeParameterNamesAround(index, right),
|
|
1509
|
+
]);
|
|
1510
|
+
const [leftTypes, rightTypes] = await Promise.all([
|
|
1511
|
+
memberParameterTypes(index, left),
|
|
1512
|
+
memberParameterTypes(index, right),
|
|
1513
|
+
]);
|
|
1514
|
+
if (!leftTypes || !rightTypes || leftTypes.length !== rightTypes.length)
|
|
1515
|
+
return false;
|
|
1516
|
+
const sameFile = fileIdentityKey(left.file) === fileIdentityKey(right.file);
|
|
1517
|
+
const leftContext = await ensureParsedContext(left.file, index.parsed?.get(fileIdentityKey(left.file)), index.languageExtensions).catch(() => null);
|
|
1518
|
+
if (!leftContext)
|
|
1519
|
+
return false;
|
|
1520
|
+
for (let at = 0; at < leftTypes.length; at += 1) {
|
|
1521
|
+
const text = leftTypes[at];
|
|
1522
|
+
if (text !== rightTypes[at])
|
|
1523
|
+
return false;
|
|
1524
|
+
const typeNames = text.match(/[\p{L}_][\p{L}\p{N}_]*/gu) ?? [];
|
|
1525
|
+
if (typeNames.some((typeName) => leftGenerics.has(typeName) || rightGenerics.has(typeName)))
|
|
1526
|
+
return false;
|
|
1527
|
+
// One Java or Kotlin file declares one package; a C# file can open several namespaces, so the
|
|
1528
|
+
// same spelling there still needs resolution.
|
|
1529
|
+
if (sameFile && leftContext.sup.id !== "csharp")
|
|
1530
|
+
continue;
|
|
1531
|
+
const base = text
|
|
1532
|
+
.replace(/<.*$/su, "")
|
|
1533
|
+
.replace(/[?\[\].]+$/u, "")
|
|
1534
|
+
.split(".")
|
|
1535
|
+
.pop() ?? text;
|
|
1536
|
+
if (BUILTIN_PARAMETER_TYPES[leftContext.sup.id]?.has(base))
|
|
1537
|
+
continue;
|
|
1538
|
+
const rightContext = await ensureParsedContext(right.file, index.parsed?.get(fileIdentityKey(right.file)), index.languageExtensions).catch(() => null);
|
|
1539
|
+
const leftModule = index.byFile.get(fileIdentityKey(left.file));
|
|
1540
|
+
const rightModule = index.byFile.get(fileIdentityKey(right.file));
|
|
1541
|
+
if (!rightContext || !leftModule || !rightModule)
|
|
1542
|
+
return false;
|
|
1543
|
+
// Resolve at each declaration so a C# spelling reads its own namespace region.
|
|
1544
|
+
const leftType = resolveNamedDefinition(index, leftModule, left.file, leftContext.sup, base, undefined, left.range.start.index);
|
|
1545
|
+
const rightType = resolveNamedDefinition(index, rightModule, right.file, rightContext.sup, base, undefined, right.range.start.index);
|
|
1546
|
+
if (leftType?.status !== "ok" && rightType?.status !== "ok") {
|
|
1547
|
+
// Neither names a project type: both files must name it through the same import (or none).
|
|
1548
|
+
if (importedTypeSource(leftModule, base) !== importedTypeSource(rightModule, base))
|
|
1549
|
+
return false;
|
|
1550
|
+
continue;
|
|
1551
|
+
}
|
|
1552
|
+
if (leftType?.status !== "ok" || rightType?.status !== "ok")
|
|
1553
|
+
return false;
|
|
1554
|
+
if (fileIdentityKey(leftType.definition.file) !== fileIdentityKey(rightType.definition.file) ||
|
|
1555
|
+
leftType.definition.range.start.index !== rightType.definition.range.start.index) {
|
|
1556
|
+
return false;
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
return true;
|
|
1560
|
+
}
|
|
1561
|
+
/**
|
|
1562
|
+
* Validate an unqualified member use against its actual lexical owner, including shared
|
|
1563
|
+
* owner parts in other files and inherited members. Swift checks every bare name; C#, Java, and
|
|
1564
|
+
* Kotlin check only bare call callees. A static context reaches only static members.
|
|
1565
|
+
*/
|
|
1566
|
+
/** The callee of a receiverless call (`f()`), which may name a member through the implicit `this`. */
|
|
1567
|
+
export function implicitSelfCallee(languageId, call) {
|
|
1568
|
+
if (languageId === "csharp")
|
|
1569
|
+
return call.type === "invocation_expression" ? call.childForFieldName("function") : null;
|
|
1570
|
+
if (languageId === "java") {
|
|
1571
|
+
if (call.type !== "method_invocation" || call.childForFieldName("object"))
|
|
1572
|
+
return null;
|
|
1573
|
+
return call.childForFieldName("name");
|
|
205
1574
|
}
|
|
1575
|
+
if (languageId === "kotlin")
|
|
1576
|
+
return call.type === "call_expression" ? (call.namedChildren[0] ?? null) : null;
|
|
206
1577
|
return null;
|
|
207
1578
|
}
|
|
208
|
-
function
|
|
209
|
-
|
|
1579
|
+
export async function resolveImplicitSelfMember(index, mod, node, name, source, languageId) {
|
|
1580
|
+
const call = node.parent ?? node;
|
|
1581
|
+
if (languageId !== "swift" && implicitSelfCallee(languageId, call)?.id !== node.id)
|
|
1582
|
+
return undefined;
|
|
1583
|
+
const jvm = languageId === "java" || languageId === "kotlin";
|
|
1584
|
+
const staticScoped = languageId === "csharp" || jvm;
|
|
1585
|
+
const memberScope = staticScoped && nodeInStaticMemberContext(node, source) ? "static" : "any";
|
|
1586
|
+
const argumentCount = getCallArgumentCount({ languageId, source, call }) ?? undefined;
|
|
1587
|
+
const report = { named: false, ambiguous: false };
|
|
1588
|
+
const member = await resolveKeywordReceiverMember(index, mod, node, name, memberScope, false, argumentCount, undefined, report);
|
|
1589
|
+
if (member)
|
|
1590
|
+
return member;
|
|
1591
|
+
// Two inherited overloads of one arity are ambiguous without type ranking; in Java and C# any
|
|
1592
|
+
// inherited method of that name hides imports even when none accepts the call.
|
|
1593
|
+
if (report.ambiguous || (report.named && (languageId === "java" || languageId === "csharp")))
|
|
1594
|
+
return null;
|
|
1595
|
+
if (memberScope !== "static" || !jvm)
|
|
1596
|
+
return undefined;
|
|
1597
|
+
// In a static context an inherited instance method still shadows a static import or package
|
|
1598
|
+
// function; the call is invalid, so it has no target.
|
|
1599
|
+
const instanceReport = { named: false, ambiguous: false };
|
|
1600
|
+
const instance = await resolveKeywordReceiverMember(index, mod, node, name, "any", false, argumentCount, undefined, instanceReport);
|
|
1601
|
+
// Any inherited instance method of that name hides the import, even when none applies.
|
|
1602
|
+
return instance || instanceReport.named ? null : undefined;
|
|
1603
|
+
}
|
|
1604
|
+
/** Whether a Java or Kotlin member is `private` and declared outside the class enclosing the use. */
|
|
1605
|
+
async function isUninheritedPrivateMember(index, mod, use, member) {
|
|
1606
|
+
const container = findEnclosingClassContainer(use);
|
|
1607
|
+
const start = member.range.start.index ?? -1;
|
|
1608
|
+
const ownMember = !!container &&
|
|
1609
|
+
fileIdentityKey(member.file) === fileIdentityKey(mod.file) &&
|
|
1610
|
+
start >= container.startIndex &&
|
|
1611
|
+
start < container.endIndex;
|
|
1612
|
+
if (ownMember)
|
|
1613
|
+
return false;
|
|
1614
|
+
const parsed = await ensureParsedContext(member.file, index.parsed?.get(fileIdentityKey(member.file)), index.languageExtensions).catch(() => null);
|
|
1615
|
+
const nameNode = parsed?.tree.rootNode.descendantForIndex(start, member.range.end.index ?? start);
|
|
1616
|
+
return !!parsed && !!nameNode && isPrivateDeclaration(parsed.sup.id, nameNode);
|
|
1617
|
+
}
|
|
1618
|
+
/**
|
|
1619
|
+
* Whether `Owner::member()` at `use` can reach an instance member of `owner` through an implicit
|
|
1620
|
+
* `this`: the enclosing function is a non-static member function of `owner` or of a class that
|
|
1621
|
+
* derives from `owner` through proven bases. Any other context can only name a static member.
|
|
1622
|
+
*/
|
|
1623
|
+
export async function cppQualifiedOwnerHasImplicitThis(index, mod, use, source, sup, owner) {
|
|
1624
|
+
const definition = cppEnclosingFunctionDefinition(use);
|
|
1625
|
+
if (!definition || !(await cppFunctionHasImplicitThis(index, mod, definition, source, sup)))
|
|
1626
|
+
return false;
|
|
1627
|
+
const outOfLinePath = cppOutOfLineOwnerPath(definition, source, sup);
|
|
1628
|
+
const enclosingDef = outOfLinePath ? await resolveCppQualifiedMemberContainer(index, mod, outOfLinePath) : null;
|
|
1629
|
+
const enclosing = enclosingDef
|
|
1630
|
+
? await keywordClassRefFromDef(index, enclosingDef)
|
|
1631
|
+
: await keywordClassRefFromNode(index, mod, definition);
|
|
1632
|
+
const target = await keywordClassRefFromDef(index, owner);
|
|
1633
|
+
if (!enclosing || !target)
|
|
1634
|
+
return false;
|
|
1635
|
+
const targetKey = keywordContainerKey(target.file, target.container);
|
|
1636
|
+
const visited = new Set();
|
|
1637
|
+
let level = [enclosing];
|
|
1638
|
+
for (let depth = 0; depth < RECEIVER_HIERARCHY_DEPTH && level.length; depth += 1) {
|
|
1639
|
+
const next = [];
|
|
1640
|
+
for (const candidate of level) {
|
|
1641
|
+
const key = keywordContainerKey(candidate.file, candidate.container);
|
|
1642
|
+
if (key === targetKey)
|
|
1643
|
+
return true;
|
|
1644
|
+
if (visited.has(key))
|
|
1645
|
+
continue;
|
|
1646
|
+
visited.add(key);
|
|
1647
|
+
next.push(...(await baseRefsFromContainer(index, candidate.module, candidate.container, candidate.context.source, candidate.context.sup, false, candidate.context.tree)));
|
|
1648
|
+
}
|
|
1649
|
+
level = next;
|
|
1650
|
+
}
|
|
1651
|
+
return false;
|
|
1652
|
+
}
|
|
1653
|
+
/** The C++ function definition enclosing a use, if any. */
|
|
1654
|
+
export function cppEnclosingFunctionDefinition(node) {
|
|
1655
|
+
for (let current = node.parent; current; current = current.parent) {
|
|
1656
|
+
if (current.type === "function_definition")
|
|
1657
|
+
return current;
|
|
1658
|
+
}
|
|
1659
|
+
return null;
|
|
1660
|
+
}
|
|
1661
|
+
/** Proven unqualified member call from an out-of-line C++ method definition. */
|
|
1662
|
+
export async function resolveCppOutOfLineImplicitMember(index, mod, node, name, source, sup, requireAcceptedArity = false) {
|
|
1663
|
+
if (node.parent?.type !== "call_expression" || node.parent.childForFieldName("function")?.id !== node.id) {
|
|
1664
|
+
return undefined;
|
|
1665
|
+
}
|
|
1666
|
+
const ownerPath = cppOutOfLineOwnerPath(node, source, sup);
|
|
1667
|
+
const owner = ownerPath ? await resolveCppQualifiedMemberContainer(index, mod, ownerPath) : null;
|
|
1668
|
+
if (!owner)
|
|
1669
|
+
return undefined;
|
|
1670
|
+
const argumentCount = getCallArgumentCount({ languageId: "cpp", source, call: node.parent }) ?? undefined;
|
|
1671
|
+
const enclosingFunction = cppEnclosingFunctionDefinition(node);
|
|
1672
|
+
const memberScope = enclosingFunction && (await cppFunctionHasImplicitThis(index, mod, enclosingFunction, source, sup))
|
|
1673
|
+
? "any"
|
|
1674
|
+
: "static";
|
|
1675
|
+
const matching = await resolveKeywordReceiverMember(index, mod, node, name, memberScope, false, argumentCount, owner);
|
|
1676
|
+
// A class member hides a same-named global even when this call cannot accept its arity. The
|
|
1677
|
+
// only member is still the call's target for navigation; an ambiguous overload set is not.
|
|
1678
|
+
const member = matching ??
|
|
1679
|
+
(argumentCount === undefined
|
|
1680
|
+
? undefined
|
|
1681
|
+
: await resolveKeywordReceiverMember(index, mod, node, name, memberScope, false, undefined, owner));
|
|
1682
|
+
if (!member) {
|
|
1683
|
+
// In a static member function, an instance member still hides a same-named global: the call
|
|
1684
|
+
// is ill-formed, so neither consumer may fall back to the global.
|
|
1685
|
+
const hidden = memberScope === "static" &&
|
|
1686
|
+
(await resolveKeywordReceiverMember(index, mod, node, name, "any", false, undefined, owner));
|
|
1687
|
+
return hidden ? null : undefined;
|
|
1688
|
+
}
|
|
1689
|
+
// The call graph records no edge for a call its target cannot accept. `null` means a member hides
|
|
1690
|
+
// the name but is not the call's target, so no consumer may fall back to a same-named free function.
|
|
1691
|
+
const rejected = requireAcceptedArity &&
|
|
1692
|
+
argumentCount !== undefined &&
|
|
1693
|
+
!(await receiverMemberAcceptsArgumentCount(index, member, argumentCount));
|
|
1694
|
+
return rejected ? null : member;
|
|
210
1695
|
}
|
|
211
1696
|
export { supportsReceiverCallEdges, supportsReceiverMemberNavigation } from "../util/member-access-tables.js";
|
|
212
1697
|
function memberDeclaringLocals(mod, typeName, normalize) {
|
|
213
1698
|
const normalized = normalize(typeName);
|
|
214
1699
|
return mod.locals.filter((local) => normalize(local.localName) === normalized && declaresMembers(local));
|
|
215
1700
|
}
|
|
216
|
-
|
|
1701
|
+
/**
|
|
1702
|
+
* Ruby constant visible the same way go-to-definition resolves a bare `Widget`:
|
|
1703
|
+
* a same-file declaration, or the class a `require` brought in. Not project-wide.
|
|
1704
|
+
*/
|
|
1705
|
+
export function resolveRubyVisibleConstant(index, mod, sup, typeName) {
|
|
1706
|
+
if (sup.id !== "ruby")
|
|
1707
|
+
return null;
|
|
1708
|
+
const resolved = resolveNamedDefinition(index, mod, mod.file, sup, typeName);
|
|
1709
|
+
if (resolved?.status !== "ok")
|
|
1710
|
+
return null;
|
|
1711
|
+
return declaresMembers(resolved.definition) ? resolved.definition : null;
|
|
1712
|
+
}
|
|
1713
|
+
/** Proven PHP `extends` base, using the same simple-name lookup as `parent::`. */
|
|
1714
|
+
function resolvePhpProvenBaseClass(index, mod, classNode, source, sup) {
|
|
1715
|
+
const bases = collectDeclaredBaseTypes(classNode, source, sup, true);
|
|
1716
|
+
if (bases.length !== 1)
|
|
1717
|
+
return null;
|
|
1718
|
+
const base = bases[0];
|
|
1719
|
+
if (!base || base.kind !== "simple")
|
|
1720
|
+
return null;
|
|
1721
|
+
const normalize = (name) => foldPhpIdentifierCase(sup.normalizeIdentifier(name));
|
|
1722
|
+
return resolveNamedMemberContainer(index, mod, base.name, normalize) ?? null;
|
|
1723
|
+
}
|
|
1724
|
+
/**
|
|
1725
|
+
* Class named by `new self()`, `new static()`, or `new parent()`.
|
|
1726
|
+
* `self` and `static` are the enclosing class. `parent` is the proven base.
|
|
1727
|
+
* Returns null when the keyword is not proven, so a class named `Parent` cannot win.
|
|
1728
|
+
*/
|
|
1729
|
+
export function resolvePhpObjectCreationTarget(index, mod, keyword, source, sup) {
|
|
1730
|
+
if (!keyword.classNode)
|
|
1731
|
+
return null;
|
|
1732
|
+
if (keyword.keyword === "self" || keyword.keyword === "static") {
|
|
1733
|
+
return memberContainerDef(mod, keyword.classNode);
|
|
1734
|
+
}
|
|
1735
|
+
return resolvePhpProvenBaseClass(index, mod, keyword.classNode, source, sup);
|
|
1736
|
+
}
|
|
1737
|
+
function enclosingRubyMethod(node) {
|
|
1738
|
+
let current = node.parent;
|
|
1739
|
+
while (current) {
|
|
1740
|
+
if (current.type === "method" || current.type === "singleton_method")
|
|
1741
|
+
return current;
|
|
1742
|
+
current = current.parent;
|
|
1743
|
+
}
|
|
1744
|
+
return null;
|
|
1745
|
+
}
|
|
1746
|
+
/** Bare `super` / `super()` targets the same-named method on a proven superclass. */
|
|
1747
|
+
export async function resolveRubySuperDefinition(index, mod, node, source, sup) {
|
|
1748
|
+
if (sup.id !== "ruby" || node.type !== "super")
|
|
1749
|
+
return undefined;
|
|
1750
|
+
const method = enclosingRubyMethod(node);
|
|
1751
|
+
const nameNode = method?.childForFieldName("name") ?? null;
|
|
1752
|
+
if (!nameNode)
|
|
1753
|
+
return undefined;
|
|
1754
|
+
const member = sliceText(nameNode, source);
|
|
1755
|
+
if (!member)
|
|
1756
|
+
return undefined;
|
|
1757
|
+
return resolveKeywordReceiverMember(index, mod, node, member, "any", true);
|
|
1758
|
+
}
|
|
1759
|
+
async function resolveReceiverDefinition(index, obj, source, sup, resolveExpression, mod) {
|
|
217
1760
|
const constructor = receiverConstructorExpression(obj, source, sup);
|
|
218
1761
|
if (constructor) {
|
|
1762
|
+
if (sup.id === "cpp") {
|
|
1763
|
+
const qualifiedType = cppQualifiedNameSegments(constructor, source);
|
|
1764
|
+
if (qualifiedType.length > 1) {
|
|
1765
|
+
const def = await resolveCppQualifiedMemberContainer(index, mod, qualifiedType);
|
|
1766
|
+
if (def) {
|
|
1767
|
+
return {
|
|
1768
|
+
def,
|
|
1769
|
+
memberScope: hasStaticMemberDistinction(sup.id) ? "instance" : "any",
|
|
1770
|
+
};
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
219
1774
|
const typeName = sliceText(constructor, source);
|
|
220
1775
|
const typedLocals = memberDeclaringLocals(mod, typeName, sup.normalizeIdentifier);
|
|
221
1776
|
if (typedLocals.length === 1) {
|
|
@@ -224,13 +1779,36 @@ async function resolveReceiverDefinition(obj, source, sup, resolveExpression, mo
|
|
|
224
1779
|
memberScope: hasStaticMemberDistinction(sup.id) ? "instance" : "any",
|
|
225
1780
|
};
|
|
226
1781
|
}
|
|
1782
|
+
if (sup.id === "php") {
|
|
1783
|
+
const normalizeTypeName = (name) => foldPhpIdentifierCase(sup.normalizeIdentifier(name));
|
|
1784
|
+
const namedContainer = resolveNamedMemberContainer(index, mod, typeName, normalizeTypeName);
|
|
1785
|
+
if (namedContainer) {
|
|
1786
|
+
return {
|
|
1787
|
+
def: namedContainer,
|
|
1788
|
+
memberScope: hasStaticMemberDistinction(sup.id) ? "instance" : "any",
|
|
1789
|
+
};
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
227
1792
|
const result = await resolveExpression(constructor);
|
|
228
|
-
|
|
1793
|
+
const constructed = result?.kind === "resolved" ? asMemberContainer(index, result.def) : undefined;
|
|
1794
|
+
if (constructed) {
|
|
229
1795
|
return {
|
|
230
|
-
def:
|
|
1796
|
+
def: constructed,
|
|
231
1797
|
memberScope: hasStaticMemberDistinction(sup.id) ? "instance" : "any",
|
|
232
1798
|
};
|
|
233
1799
|
}
|
|
1800
|
+
// Ruby `require` exposes a class as a namespace import. A bare constant already
|
|
1801
|
+
// resolves through resolveNamedDefinition; `Klass.new` must use that same path
|
|
1802
|
+
// so goto, references, and call edges agree. Same-file classes returned above.
|
|
1803
|
+
if (sup.id === "ruby" && typedLocals.length === 0) {
|
|
1804
|
+
const rubyType = resolveRubyVisibleConstant(index, mod, sup, typeName);
|
|
1805
|
+
if (rubyType) {
|
|
1806
|
+
return {
|
|
1807
|
+
def: rubyType,
|
|
1808
|
+
memberScope: hasStaticMemberDistinction(sup.id) ? "instance" : "any",
|
|
1809
|
+
};
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
234
1812
|
if (typedLocals[0]) {
|
|
235
1813
|
return {
|
|
236
1814
|
def: typedLocals[0],
|
|
@@ -239,12 +1817,13 @@ async function resolveReceiverDefinition(obj, source, sup, resolveExpression, mo
|
|
|
239
1817
|
}
|
|
240
1818
|
}
|
|
241
1819
|
const direct = await resolveExpression(obj);
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
1820
|
+
const directContainer = direct?.kind === "resolved" ? asMemberContainer(index, direct.def) : undefined;
|
|
1821
|
+
if (directContainer) {
|
|
1822
|
+
if (isJsTsLanguage(sup.id) && directContainer.kind === SymbolKind.TypeAlias) {
|
|
1823
|
+
return { def: directContainer, memberScope: "any", runtimeTypeOnly: true };
|
|
245
1824
|
}
|
|
246
|
-
const memberScope =
|
|
247
|
-
return { def:
|
|
1825
|
+
const memberScope = supportsStaticMemberScope(sup.id) ? "static" : "any";
|
|
1826
|
+
return { def: directContainer, memberScope };
|
|
248
1827
|
}
|
|
249
1828
|
if (isJsTsLanguage(sup.id) && isReceiverNameNode(sup, obj.type)) {
|
|
250
1829
|
return null;
|
|
@@ -254,7 +1833,25 @@ async function resolveReceiverDefinition(obj, source, sup, resolveExpression, mo
|
|
|
254
1833
|
}
|
|
255
1834
|
return null;
|
|
256
1835
|
}
|
|
257
|
-
|
|
1836
|
+
/** Whether a type-qualified access (`Util.M`) can name `def`: a nested type or a static member. */
|
|
1837
|
+
async function reachableThroughTypeName(index, def) {
|
|
1838
|
+
if (declaresMembers(def))
|
|
1839
|
+
return true;
|
|
1840
|
+
const context = await ensureParsedContext(def.file, index.parsed?.get(fileIdentityKey(def.file)), index.languageExtensions);
|
|
1841
|
+
const nameNode = nameNodeForDef(context, def);
|
|
1842
|
+
if (!nameNode)
|
|
1843
|
+
return false;
|
|
1844
|
+
return hasStaticModifier(def, context, nearestMemberContainer(nameNode) ?? context.tree.rootNode);
|
|
1845
|
+
}
|
|
1846
|
+
/**
|
|
1847
|
+
* A member declared directly in the type `baseDef` names (`Util.Two`), including the other parts
|
|
1848
|
+
* of a C# partial type, or inherited through it. Same-named overloads are narrowed to members a
|
|
1849
|
+
* type name reaches (static members and nested types) when the base is a type, then chosen by
|
|
1850
|
+
* the call's argument count; without a count, or when several accept it, the lookup is
|
|
1851
|
+
* ambiguous. A sole reachable candidate is kept even when the count does not fit, as for other
|
|
1852
|
+
* calls; the graph records no edge for it. Ruby reopens a method by redefining it, so it keeps the first match.
|
|
1853
|
+
*/
|
|
1854
|
+
async function resolveMemberDefinitionForBase(index, baseDef, member, knownArgumentCount) {
|
|
258
1855
|
const targetContext = await ensureParsedContext(baseDef.file, undefined, index.languageExtensions);
|
|
259
1856
|
const start = baseDef.range.start;
|
|
260
1857
|
const targetPosition = {
|
|
@@ -268,37 +1865,141 @@ async function resolveMemberDefinitionForBase(index, baseDef, member) {
|
|
|
268
1865
|
const targetModule = index.byFile.get(fileIdentityKey(baseDef.file));
|
|
269
1866
|
if (!targetModule)
|
|
270
1867
|
return undefined;
|
|
1868
|
+
const languageId = targetContext.sup.id;
|
|
271
1869
|
const normalizeIdentifier = targetContext.sup.normalizeIdentifier;
|
|
272
|
-
const
|
|
273
|
-
if (
|
|
274
|
-
|
|
275
|
-
|
|
1870
|
+
const candidates = findDirectLocalsWithinNode(targetModule.locals, member, container, targetContext, normalizeIdentifier);
|
|
1871
|
+
if (languageId === "csharp") {
|
|
1872
|
+
const partialParts = await findSharedOwnerMemberDefinitions({
|
|
1873
|
+
index,
|
|
1874
|
+
ownerFile: baseDef.file,
|
|
1875
|
+
ownerContainer: container,
|
|
1876
|
+
ownerSource: targetContext.source,
|
|
1877
|
+
languageId,
|
|
1878
|
+
member,
|
|
1879
|
+
});
|
|
1880
|
+
for (const hit of partialParts) {
|
|
1881
|
+
if (!candidates.includes(hit))
|
|
1882
|
+
candidates.push(hit);
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
if (candidates.length > 1 && languageId !== "ruby") {
|
|
1886
|
+
const reachable = [];
|
|
1887
|
+
for (const candidate of candidates) {
|
|
1888
|
+
if (!declaresMembers(baseDef) || (await reachableThroughTypeName(index, candidate)))
|
|
1889
|
+
reachable.push(candidate);
|
|
1890
|
+
}
|
|
1891
|
+
return await selectReceiverMemberCandidates(index, reachable, knownArgumentCount);
|
|
1892
|
+
}
|
|
1893
|
+
if (candidates[0])
|
|
1894
|
+
return candidates[0];
|
|
1895
|
+
if (languageId === "java")
|
|
276
1896
|
return undefined;
|
|
277
|
-
return findReceiverMemberDefinition(targetModule.locals, member, baseDef, container, targetContext, normalizeIdentifier);
|
|
1897
|
+
return await findReceiverMemberDefinition(index, targetModule.locals, member, baseDef, container, targetContext, normalizeIdentifier);
|
|
1898
|
+
}
|
|
1899
|
+
function typescriptMergedInterfaceMemberCandidates(locals, member, receiverDef, container, targetContext, normalizeIdentifier, predicate) {
|
|
1900
|
+
const matches = findDirectLocalsWithinNode(locals, member, container, targetContext, normalizeIdentifier, predicate);
|
|
1901
|
+
if (!isJsTsLanguage(targetContext.sup.id) || container.type !== "interface_declaration")
|
|
1902
|
+
return matches;
|
|
1903
|
+
const receiverStart = receiverDef.range.start.index ?? 0;
|
|
1904
|
+
const receiverEnd = receiverDef.range.end.index ?? receiverStart;
|
|
1905
|
+
const receiverContainerKey = typescriptCallableContainerKey(targetContext.tree, receiverStart, receiverEnd);
|
|
1906
|
+
const seenBodies = new Set();
|
|
1907
|
+
const appendMembers = (interfaceDeclaration) => {
|
|
1908
|
+
const body = interfaceDeclaration.childForFieldName("body");
|
|
1909
|
+
if (!body)
|
|
1910
|
+
return;
|
|
1911
|
+
const bodyKey = typescriptCallableContainerKey(targetContext.tree, body.startIndex, body.endIndex);
|
|
1912
|
+
if (seenBodies.has(bodyKey))
|
|
1913
|
+
return;
|
|
1914
|
+
seenBodies.add(bodyKey);
|
|
1915
|
+
for (const candidate of findDirectLocalsWithinNode(locals, member, interfaceDeclaration, targetContext, normalizeIdentifier, predicate)) {
|
|
1916
|
+
if (!matches.includes(candidate))
|
|
1917
|
+
matches.push(candidate);
|
|
1918
|
+
}
|
|
1919
|
+
};
|
|
1920
|
+
const normalizedReceiverName = normalizeIdentifier(receiverDef.localName);
|
|
1921
|
+
for (const local of locals) {
|
|
1922
|
+
if (normalizeIdentifier(local.localName) !== normalizedReceiverName)
|
|
1923
|
+
continue;
|
|
1924
|
+
const start = local.range.start.index;
|
|
1925
|
+
if (start === undefined)
|
|
1926
|
+
continue;
|
|
1927
|
+
const end = local.range.end.index ?? start;
|
|
1928
|
+
if (typescriptCallableContainerKey(targetContext.tree, start, end) !== receiverContainerKey)
|
|
1929
|
+
continue;
|
|
1930
|
+
const nameNode = targetContext.tree.rootNode.descendantForIndex(start, end);
|
|
1931
|
+
const interfaceDeclaration = nameNode.parent;
|
|
1932
|
+
if (interfaceDeclaration?.type !== "interface_declaration")
|
|
1933
|
+
continue;
|
|
1934
|
+
appendMembers(interfaceDeclaration);
|
|
1935
|
+
}
|
|
1936
|
+
return matches;
|
|
1937
|
+
}
|
|
1938
|
+
/** Same-name namespace reopenings, excluding members of a decoy namespace or nested owner. */
|
|
1939
|
+
export function findTypeScriptNamespaceMemberCandidates(locals, member, receiverDef, context) {
|
|
1940
|
+
if (!isJsTsLanguage(context.sup.id))
|
|
1941
|
+
return [];
|
|
1942
|
+
const containers = typescriptMergedNamespaceContainers(context.tree, locals, receiverDef);
|
|
1943
|
+
const matches = [];
|
|
1944
|
+
for (const container of containers) {
|
|
1945
|
+
for (const candidate of findDirectLocalsWithinNode(locals, member, container, context, context.sup.normalizeIdentifier)) {
|
|
1946
|
+
if (!matches.includes(candidate))
|
|
1947
|
+
matches.push(candidate);
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
return matches;
|
|
278
1951
|
}
|
|
279
|
-
function findReceiverMemberDefinition(locals, member, receiverDef, container, targetContext, normalizeIdentifier, memberScope = "any") {
|
|
1952
|
+
async function findReceiverMemberDefinition(index, locals, member, receiverDef, container, targetContext, normalizeIdentifier, memberScope = "any", knownArgumentCount) {
|
|
280
1953
|
const memberPredicate = memberScope === "any"
|
|
281
1954
|
? undefined
|
|
282
1955
|
: (local) => matchesReceiverMemberScope(local, memberScope, targetContext, container);
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
1956
|
+
if ((targetContext.sup.id === "ts" || targetContext.sup.id === "tsx") &&
|
|
1957
|
+
(container.type === "internal_module" || container.type === "module")) {
|
|
1958
|
+
const candidates = findTypeScriptNamespaceMemberCandidates(locals, member, receiverDef, targetContext);
|
|
1959
|
+
return await selectReceiverMemberCandidates(index, candidates, knownArgumentCount);
|
|
1960
|
+
}
|
|
1961
|
+
const allReceiverMatches = typescriptMergedInterfaceMemberCandidates(locals, member, receiverDef, container, targetContext, normalizeIdentifier, memberPredicate);
|
|
1962
|
+
if (targetContext.sup.id === "csharp" || targetContext.sup.id === "swift") {
|
|
1963
|
+
const shared = await findSharedOwnerMemberDefinitions({
|
|
1964
|
+
index,
|
|
1965
|
+
ownerFile: receiverDef.file,
|
|
1966
|
+
ownerContainer: container,
|
|
1967
|
+
ownerSource: targetContext.source,
|
|
1968
|
+
languageId: targetContext.sup.id,
|
|
1969
|
+
member,
|
|
1970
|
+
memberScope,
|
|
1971
|
+
});
|
|
1972
|
+
for (const hit of shared) {
|
|
1973
|
+
if (!allReceiverMatches.includes(hit))
|
|
1974
|
+
allReceiverMatches.push(hit);
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
if (allReceiverMatches.length) {
|
|
1978
|
+
return await selectReceiverMemberCandidates(index, allReceiverMatches, knownArgumentCount);
|
|
1979
|
+
}
|
|
288
1980
|
if (targetContext.sup.id === "rust") {
|
|
289
|
-
const
|
|
290
|
-
|
|
1981
|
+
const matches = [];
|
|
1982
|
+
for (const implNode of findRustImplsForType(targetContext.tree.rootNode, receiverDef.localName, targetContext.source, targetContext.sup)) {
|
|
1983
|
+
appendDirectKeywordMembers(locals, member, implNode, targetContext, normalizeIdentifier, undefined, matches);
|
|
1984
|
+
}
|
|
1985
|
+
return await selectReceiverMemberCandidates(index, matches, knownArgumentCount);
|
|
291
1986
|
}
|
|
292
1987
|
if (targetContext.sup.id === "go") {
|
|
293
1988
|
return findGoReceiverMember(locals, member, receiverDef.localName, targetContext, normalizeIdentifier);
|
|
294
1989
|
}
|
|
1990
|
+
if (targetContext.sup.id === "kotlin") {
|
|
1991
|
+
const extensionMatches = kotlinExtensionFunctionsNamedOnType(locals, member, receiverDef.localName, targetContext, normalizeIdentifier);
|
|
1992
|
+
if (extensionMatches.length) {
|
|
1993
|
+
return await selectReceiverMemberCandidates(index, extensionMatches, knownArgumentCount);
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
295
1996
|
return undefined;
|
|
296
1997
|
}
|
|
297
|
-
function
|
|
1998
|
+
function findLocalsWithinNode(locals, member, node, normalizeIdentifier = (name) => name, predicate) {
|
|
298
1999
|
const containerStart = node.startIndex;
|
|
299
2000
|
const containerEnd = node.endIndex;
|
|
300
2001
|
const normalizedMember = normalizeIdentifier(member);
|
|
301
|
-
return locals.
|
|
2002
|
+
return locals.filter((local) => {
|
|
302
2003
|
const startIndex = local.range.start.index;
|
|
303
2004
|
const endIndex = local.range.end.index;
|
|
304
2005
|
return (normalizeIdentifier(local.localName) === normalizedMember &&
|
|
@@ -309,27 +2010,108 @@ function findLocalWithinNode(locals, member, node, normalizeIdentifier = (name)
|
|
|
309
2010
|
(!predicate || predicate(local)));
|
|
310
2011
|
});
|
|
311
2012
|
}
|
|
2013
|
+
function findLocalWithinNode(locals, member, node, normalizeIdentifier = (name) => name, predicate) {
|
|
2014
|
+
return findLocalsWithinNode(locals, member, node, normalizeIdentifier, predicate)[0];
|
|
2015
|
+
}
|
|
312
2016
|
function matchesReceiverMemberScope(local, memberScope, targetContext, container) {
|
|
313
2017
|
if (memberScope === "any")
|
|
314
2018
|
return true;
|
|
315
2019
|
return hasStaticModifier(local, targetContext, container) === (memberScope === "static");
|
|
316
2020
|
}
|
|
317
|
-
function
|
|
318
|
-
|
|
2021
|
+
async function getCallableArityForDef(index, def) {
|
|
2022
|
+
const context = await ensureParsedContext(def.file, undefined, index.languageExtensions);
|
|
2023
|
+
const start = def.range.start;
|
|
2024
|
+
const position = { row: start.line - 1, column: start.column - 1 };
|
|
2025
|
+
const nameNode = context.tree.rootNode.descendantForPosition(position, position);
|
|
2026
|
+
const container = nearestMemberContainer(nameNode);
|
|
2027
|
+
let current = nameNode;
|
|
2028
|
+
while (current && current !== container) {
|
|
2029
|
+
if (CALLABLE_DECLARATION_NODE_TYPES[current.type]) {
|
|
2030
|
+
const range = getCallableArity({
|
|
2031
|
+
languageId: context.sup.id,
|
|
2032
|
+
source: context.source,
|
|
2033
|
+
declaration: current,
|
|
2034
|
+
binding: memberLookupBinding(context.sup.id),
|
|
2035
|
+
});
|
|
2036
|
+
if (range)
|
|
2037
|
+
return range;
|
|
2038
|
+
}
|
|
2039
|
+
current = current.parent;
|
|
2040
|
+
}
|
|
2041
|
+
return undefined;
|
|
2042
|
+
}
|
|
2043
|
+
/**
|
|
2044
|
+
* Whether a member found for a receiverless call can accept that call's argument count. Unknown
|
|
2045
|
+
* counts and unknown arities accept.
|
|
2046
|
+
*/
|
|
2047
|
+
export async function memberAcceptsCallAt(index, member, callee, source, languageId) {
|
|
2048
|
+
const argumentCount = getCallArgumentCount({ languageId, source, call: callee.parent ?? callee });
|
|
2049
|
+
if (argumentCount === null)
|
|
319
2050
|
return true;
|
|
320
|
-
|
|
321
|
-
|
|
2051
|
+
return (await receiverMemberAcceptsArgumentCount(index, member, argumentCount)) !== false;
|
|
2052
|
+
}
|
|
2053
|
+
async function receiverMemberAcceptsArgumentCount(index, def, argumentCount) {
|
|
2054
|
+
const context = await ensureParsedContext(def.file, undefined, index.languageExtensions);
|
|
2055
|
+
const start = def.range.start;
|
|
2056
|
+
const position = {
|
|
2057
|
+
row: start.line - 1,
|
|
2058
|
+
column: start.column - 1,
|
|
2059
|
+
};
|
|
2060
|
+
const nameNode = context.tree.rootNode.descendantForPosition(position, position);
|
|
2061
|
+
if (context.sup.id === "cpp") {
|
|
2062
|
+
const shape = cppCallableShapeForNode(nameNode);
|
|
2063
|
+
return shape
|
|
2064
|
+
? argumentCount >= shape.minArity && (shape.maxArity === null || argumentCount <= shape.maxArity)
|
|
2065
|
+
: undefined;
|
|
322
2066
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
2067
|
+
const range = await getCallableArityForDef(index, def);
|
|
2068
|
+
if (!range)
|
|
2069
|
+
return undefined;
|
|
2070
|
+
return argumentCount >= range.minArgs && (range.maxArgs === null || argumentCount <= range.maxArgs);
|
|
2071
|
+
}
|
|
2072
|
+
function uniqueReceiverMemberCandidates(candidates) {
|
|
2073
|
+
const seen = new Set();
|
|
2074
|
+
const unique = [];
|
|
2075
|
+
for (const candidate of candidates) {
|
|
2076
|
+
const key = keywordClassKey(candidate);
|
|
2077
|
+
if (seen.has(key))
|
|
2078
|
+
continue;
|
|
2079
|
+
seen.add(key);
|
|
2080
|
+
unique.push(candidate);
|
|
2081
|
+
}
|
|
2082
|
+
return unique;
|
|
2083
|
+
}
|
|
2084
|
+
async function selectReceiverMemberCandidates(index, candidates, knownArgumentCount, allowUniqueArityMismatch = true) {
|
|
2085
|
+
if (candidates.length > 1) {
|
|
2086
|
+
const file = candidates[0]?.file;
|
|
2087
|
+
if (file && candidates.every((candidate) => candidate.file === file)) {
|
|
2088
|
+
const context = await ensureParsedContext(file, undefined, index.languageExtensions);
|
|
2089
|
+
if (isJsTsLanguage(context.sup.id)) {
|
|
2090
|
+
// Signature-only overloads still need arity selection before generic member deduplication.
|
|
2091
|
+
return typescriptSelectOverloadCandidate({
|
|
2092
|
+
group: candidates,
|
|
2093
|
+
tree: context.tree,
|
|
2094
|
+
definitionOf: (candidate) => candidate,
|
|
2095
|
+
declarationOf: (candidate) => nameNodeForDef(context, candidate)?.parent,
|
|
2096
|
+
source: context.source,
|
|
2097
|
+
languageId: context.sup.id,
|
|
2098
|
+
argumentCount: knownArgumentCount ?? null,
|
|
2099
|
+
});
|
|
2100
|
+
}
|
|
330
2101
|
}
|
|
331
2102
|
}
|
|
332
|
-
|
|
2103
|
+
const unique = uniqueReceiverMemberCandidates(candidates);
|
|
2104
|
+
if (unique.length === 1 && (allowUniqueArityMismatch || knownArgumentCount === undefined))
|
|
2105
|
+
return unique[0];
|
|
2106
|
+
if (knownArgumentCount === undefined)
|
|
2107
|
+
return undefined;
|
|
2108
|
+
const matches = [];
|
|
2109
|
+
for (const candidate of unique) {
|
|
2110
|
+
if ((await receiverMemberAcceptsArgumentCount(index, candidate, knownArgumentCount)) !== false) {
|
|
2111
|
+
matches.push(candidate);
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
return matches.length === 1 ? matches[0] : undefined;
|
|
333
2115
|
}
|
|
334
2116
|
function hasStaticModifier(local, targetContext, container) {
|
|
335
2117
|
const position = {
|
|
@@ -337,19 +2119,21 @@ function hasStaticModifier(local, targetContext, container) {
|
|
|
337
2119
|
column: local.range.start.column - 1,
|
|
338
2120
|
};
|
|
339
2121
|
let current = targetContext.tree.rootNode.descendantForPosition(position, position);
|
|
2122
|
+
if (declarationIsStaticEquivalent(targetContext.sup.id, current))
|
|
2123
|
+
return true;
|
|
340
2124
|
while (current && current !== container) {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
if (nodeDeclaresStatic(child, targetContext.source))
|
|
346
|
-
return true;
|
|
347
|
-
}
|
|
2125
|
+
if (targetContext.sup.id === "php" && (current.type === "const_declaration" || current.type === "enum_case"))
|
|
2126
|
+
return true;
|
|
2127
|
+
if (declarationNodeIsStatic(current, targetContext.source))
|
|
2128
|
+
return true;
|
|
348
2129
|
current = current.parent;
|
|
349
2130
|
}
|
|
350
2131
|
return false;
|
|
351
2132
|
}
|
|
352
2133
|
const NESTED_MEMBER_LOCAL_CONTAINERS = new Set([
|
|
2134
|
+
"formal_parameters",
|
|
2135
|
+
"parameter_list",
|
|
2136
|
+
"parameters",
|
|
353
2137
|
"block",
|
|
354
2138
|
"class",
|
|
355
2139
|
"class_declaration",
|
|
@@ -368,14 +2152,22 @@ const NESTED_MEMBER_LOCAL_CONTAINERS = new Set([
|
|
|
368
2152
|
"module",
|
|
369
2153
|
"statement_block",
|
|
370
2154
|
]);
|
|
371
|
-
function
|
|
2155
|
+
function matchesReceiverMemberName(local, normalizedMember, targetContext, normalizeIdentifier) {
|
|
2156
|
+
const localName = normalizeIdentifier(local.localName);
|
|
2157
|
+
if (targetContext.sup.id === "php") {
|
|
2158
|
+
return comparePhpReferenceNames(normalizedMember, localName, { symbolKind: local.kind }) === "equivalent";
|
|
2159
|
+
}
|
|
2160
|
+
return localName === normalizedMember;
|
|
2161
|
+
}
|
|
2162
|
+
function findDirectLocalsWithinNode(locals, member, container, targetContext, normalizeIdentifier, predicate) {
|
|
2163
|
+
const matches = [];
|
|
372
2164
|
const containerStart = container.startIndex;
|
|
373
2165
|
const containerEnd = container.endIndex;
|
|
374
2166
|
const normalizedMember = normalizeIdentifier(member);
|
|
375
2167
|
for (const local of locals) {
|
|
376
2168
|
const startIndex = local.range.start.index;
|
|
377
2169
|
const endIndex = local.range.end.index;
|
|
378
|
-
if (
|
|
2170
|
+
if (!matchesReceiverMemberName(local, normalizedMember, targetContext, normalizeIdentifier) ||
|
|
379
2171
|
startIndex === undefined ||
|
|
380
2172
|
endIndex === undefined ||
|
|
381
2173
|
startIndex < containerStart ||
|
|
@@ -391,8 +2183,18 @@ function findDirectLocalWithinNode(locals, member, container, targetContext, nor
|
|
|
391
2183
|
let isDeclarationParent = true;
|
|
392
2184
|
while (current && current !== container) {
|
|
393
2185
|
const isDirectBody = (current.type === "statement_block" || current.type === "block") && current.parent === container;
|
|
2186
|
+
// A transparent container (Kotlin's `companion object`) donates its body's members to its
|
|
2187
|
+
// own enclosing class; the grammar never lets one nest under any other declaration, so its
|
|
2188
|
+
// body never counts as evidence of a foreign nested class here.
|
|
2189
|
+
const isTransparentBody = current.type === "class_body" &&
|
|
2190
|
+
!!current.parent &&
|
|
2191
|
+
TRANSPARENT_MEMBER_CONTAINER_TYPES[current.parent.type] === true;
|
|
2192
|
+
const definingDeclarator = current.type === "function_definition" ? current.childForFieldName("declarator") : null;
|
|
2193
|
+
const isOwnFunctionDeclaration = !!definingDeclarator && definingDeclarator.startIndex <= startIndex && definingDeclarator.endIndex >= endIndex;
|
|
394
2194
|
if (!isDeclarationParent &&
|
|
395
2195
|
!isDirectBody &&
|
|
2196
|
+
!isOwnFunctionDeclaration &&
|
|
2197
|
+
!isTransparentBody &&
|
|
396
2198
|
((current.type === "class_body" && current.parent !== container) ||
|
|
397
2199
|
NESTED_MEMBER_LOCAL_CONTAINERS.has(current.type))) {
|
|
398
2200
|
current = null;
|
|
@@ -402,30 +2204,138 @@ function findDirectLocalWithinNode(locals, member, container, targetContext, nor
|
|
|
402
2204
|
current = current.parent;
|
|
403
2205
|
}
|
|
404
2206
|
if (current && (!predicate || predicate(local)))
|
|
405
|
-
|
|
2207
|
+
matches.push(local);
|
|
406
2208
|
}
|
|
407
|
-
return
|
|
2209
|
+
return matches;
|
|
408
2210
|
}
|
|
409
|
-
function
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
2211
|
+
function findDirectLocalWithinNode(locals, member, container, targetContext, normalizeIdentifier, predicate) {
|
|
2212
|
+
return findDirectLocalsWithinNode(locals, member, container, targetContext, normalizeIdentifier, predicate)[0];
|
|
2213
|
+
}
|
|
2214
|
+
function appendDirectKeywordMembers(locals, member, container, targetContext, normalizeIdentifier, predicate, matches) {
|
|
2215
|
+
const containerStart = container.startIndex;
|
|
2216
|
+
const containerEnd = container.endIndex;
|
|
2217
|
+
const normalizedMember = normalizeIdentifier(member);
|
|
2218
|
+
for (const local of locals) {
|
|
2219
|
+
const startIndex = local.range.start.index;
|
|
2220
|
+
const endIndex = local.range.end.index;
|
|
2221
|
+
if (!matchesReceiverMemberName(local, normalizedMember, targetContext, normalizeIdentifier) ||
|
|
2222
|
+
startIndex === undefined ||
|
|
2223
|
+
endIndex === undefined ||
|
|
2224
|
+
startIndex < containerStart ||
|
|
2225
|
+
endIndex > containerEnd) {
|
|
2226
|
+
continue;
|
|
418
2227
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
2228
|
+
const start = local.range.start;
|
|
2229
|
+
const position = {
|
|
2230
|
+
row: start.line - 1,
|
|
2231
|
+
column: start.column - 1,
|
|
2232
|
+
};
|
|
2233
|
+
const declarationNode = targetContext.tree.rootNode.descendantForPosition(position, position);
|
|
2234
|
+
if (!isDirectKeywordMemberDeclaration(declarationNode, container) || (predicate && !predicate(local))) {
|
|
2235
|
+
continue;
|
|
2236
|
+
}
|
|
2237
|
+
matches.push(local);
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
const SWIFT_PARAMETER_OWNER_TYPES = {
|
|
2241
|
+
init_declaration: true,
|
|
2242
|
+
function_declaration: true,
|
|
2243
|
+
deinit_declaration: true,
|
|
2244
|
+
subscript_declaration: true,
|
|
2245
|
+
lambda_literal: true,
|
|
2246
|
+
protocol_function_declaration: true,
|
|
2247
|
+
};
|
|
2248
|
+
/**
|
|
2249
|
+
* Swift parameters and function-body locals share the type's byte span, but
|
|
2250
|
+
* `self.name` names the member. A parameter or local that shadows the bare name
|
|
2251
|
+
* is not a member candidate. Only `simple_identifier` names are Swift.
|
|
2252
|
+
*/
|
|
2253
|
+
function isSwiftShadowingNonMember(declarationNode) {
|
|
2254
|
+
if (declarationNode.type !== "simple_identifier")
|
|
2255
|
+
return false;
|
|
2256
|
+
let current = declarationNode.parent;
|
|
2257
|
+
while (current) {
|
|
2258
|
+
if (current.type === "parameter" && current.parent && SWIFT_PARAMETER_OWNER_TYPES[current.parent.type]) {
|
|
2259
|
+
return true;
|
|
2260
|
+
}
|
|
2261
|
+
if (current.type === "function_body" ||
|
|
2262
|
+
current.type === "lambda_literal" ||
|
|
2263
|
+
current.type === "computed_property" ||
|
|
2264
|
+
current.type === "willset_didset_block") {
|
|
2265
|
+
return true;
|
|
422
2266
|
}
|
|
2267
|
+
if (current.type === "class_declaration" ||
|
|
2268
|
+
current.type === "protocol_declaration" ||
|
|
2269
|
+
current.type === "enum_class_body") {
|
|
2270
|
+
return false;
|
|
2271
|
+
}
|
|
2272
|
+
current = current.parent;
|
|
2273
|
+
}
|
|
2274
|
+
return false;
|
|
2275
|
+
}
|
|
2276
|
+
/** Parameter lists whose entries are locals of the function, not members of its type. */
|
|
2277
|
+
const PARAMETER_LIST_TYPES = new Set([
|
|
2278
|
+
"formal_parameters",
|
|
2279
|
+
"parameter_list",
|
|
2280
|
+
"parameters",
|
|
2281
|
+
"function_value_parameters",
|
|
2282
|
+
]);
|
|
2283
|
+
/**
|
|
2284
|
+
* A parameter that also declares a property of the enclosing type: a TypeScript parameter
|
|
2285
|
+
* property (`constructor(readonly path: string)`) or a PHP promoted constructor property.
|
|
2286
|
+
*/
|
|
2287
|
+
function isPropertyParameter(declarationNode, list) {
|
|
2288
|
+
let parameter = declarationNode;
|
|
2289
|
+
while (parameter && parameter.parent !== list)
|
|
2290
|
+
parameter = parameter.parent;
|
|
2291
|
+
if (!parameter)
|
|
2292
|
+
return false;
|
|
2293
|
+
if (parameter.type === "property_promotion_parameter")
|
|
423
2294
|
return true;
|
|
2295
|
+
return parameter.namedChildren.some((child) => child.type === "accessibility_modifier" || child.type === "override_modifier" || child.text === "readonly");
|
|
2296
|
+
}
|
|
2297
|
+
export function isDirectKeywordMemberDeclaration(declarationNode, container) {
|
|
2298
|
+
if (isSwiftShadowingNonMember(declarationNode))
|
|
2299
|
+
return false;
|
|
2300
|
+
if (nearestMemberContainer(declarationNode) !== container)
|
|
2301
|
+
return false;
|
|
2302
|
+
let functionDepth = 0;
|
|
2303
|
+
let current = declarationNode;
|
|
2304
|
+
while (current && current !== container) {
|
|
2305
|
+
// Swift local functions can nest inside a member; only the outer function is a member.
|
|
2306
|
+
if (current.type === "function_declaration" && ++functionDepth > 1)
|
|
2307
|
+
return false;
|
|
2308
|
+
const isMethodBody = (current.type === "block" || current.type === "compound_statement" || current.type === "statement_block") &&
|
|
2309
|
+
current.parent !== container;
|
|
2310
|
+
if (isMethodBody)
|
|
2311
|
+
return false;
|
|
2312
|
+
// A method parameter is a local of that method, unless it also declares a property.
|
|
2313
|
+
if (PARAMETER_LIST_TYPES.has(current.type))
|
|
2314
|
+
return isPropertyParameter(declarationNode, current);
|
|
2315
|
+
current = current.parent;
|
|
2316
|
+
}
|
|
2317
|
+
return current === container;
|
|
2318
|
+
}
|
|
2319
|
+
/**
|
|
2320
|
+
* Every Rust `impl` block whose self type is `typeName`, across inherent (`impl Circle`),
|
|
2321
|
+
* trait (`impl Shape for Circle`), and generic (`impl<T> Box<T>`) forms. The shared
|
|
2322
|
+
* self-type extraction keeps navigation and detailed-graph member ownership in agreement.
|
|
2323
|
+
*/
|
|
2324
|
+
function findRustImplsForType(root, typeName, source, sup) {
|
|
2325
|
+
const normalized = sup.normalizeIdentifier(typeName);
|
|
2326
|
+
const found = [];
|
|
2327
|
+
const visit = (node) => {
|
|
2328
|
+
if (node.type === "impl_item") {
|
|
2329
|
+
const selfType = rustImplSelfTypeNode(node, sup);
|
|
2330
|
+
if (selfType && sup.normalizeIdentifier(sliceText(selfType, source)) === normalized)
|
|
2331
|
+
found.push(node);
|
|
2332
|
+
}
|
|
2333
|
+
for (const child of node.namedChildren)
|
|
2334
|
+
visit(child);
|
|
424
2335
|
};
|
|
425
2336
|
visit(root);
|
|
426
2337
|
return found;
|
|
427
2338
|
}
|
|
428
|
-
const GO_EMBED_DEPTH = 16;
|
|
429
2339
|
function goMethodReceiverTypeName(methodNode, source, sup) {
|
|
430
2340
|
const receiver = methodNode.childForFieldName("receiver");
|
|
431
2341
|
if (!receiver)
|
|
@@ -499,10 +2409,35 @@ function goMethodsNamedOnType(locals, member, typeName, targetContext, normalize
|
|
|
499
2409
|
visit(targetContext.tree.rootNode);
|
|
500
2410
|
return matches;
|
|
501
2411
|
}
|
|
2412
|
+
/**
|
|
2413
|
+
* Kotlin extension functions declared `fun Type.member()` for a receiver type, searched across
|
|
2414
|
+
* the receiver type's own declaring file the same way `goMethodsNamedOnType` searches Go's file
|
|
2415
|
+
* for a receiver method. An ordinary class member never carries a receiver-type prefix, so this
|
|
2416
|
+
* never matches a real member and cannot shadow one.
|
|
2417
|
+
*/
|
|
2418
|
+
function kotlinExtensionFunctionsNamedOnType(locals, member, typeName, targetContext, normalizeIdentifier) {
|
|
2419
|
+
const matches = [];
|
|
2420
|
+
const normalizedType = normalizeIdentifier(typeName);
|
|
2421
|
+
const visit = (node) => {
|
|
2422
|
+
if (node.type === "function_declaration") {
|
|
2423
|
+
const receiverType = kotlinExtensionReceiverTypeNode(node, targetContext.sup);
|
|
2424
|
+
if (receiverType && normalizeIdentifier(sliceText(receiverType, targetContext.source)) === normalizedType) {
|
|
2425
|
+
const local = findLocalWithinNode(locals, member, node, normalizeIdentifier);
|
|
2426
|
+
if (local && !matches.includes(local))
|
|
2427
|
+
matches.push(local);
|
|
2428
|
+
}
|
|
2429
|
+
return;
|
|
2430
|
+
}
|
|
2431
|
+
for (const child of node.namedChildren)
|
|
2432
|
+
visit(child);
|
|
2433
|
+
};
|
|
2434
|
+
visit(targetContext.tree.rootNode);
|
|
2435
|
+
return matches;
|
|
2436
|
+
}
|
|
502
2437
|
function findGoReceiverMember(locals, member, typeName, targetContext, normalizeIdentifier) {
|
|
503
2438
|
const visited = new Set();
|
|
504
2439
|
let level = [typeName];
|
|
505
|
-
for (let depth = 0; depth <
|
|
2440
|
+
for (let depth = 0; depth < RECEIVER_HIERARCHY_DEPTH && level.length; depth += 1) {
|
|
506
2441
|
const matches = [];
|
|
507
2442
|
const next = [];
|
|
508
2443
|
for (const currentType of level) {
|
|
@@ -526,20 +2461,23 @@ function findGoReceiverMember(locals, member, typeName, targetContext, normalize
|
|
|
526
2461
|
}
|
|
527
2462
|
return undefined;
|
|
528
2463
|
}
|
|
529
|
-
const PYTHON_SUPERTYPE_DEPTH = 16;
|
|
530
2464
|
function pythonClassKey(def) {
|
|
531
2465
|
const start = def.range.start;
|
|
532
2466
|
return `${fileIdentityKey(def.file)}:${start.index ?? `${start.line}:${start.column}`}`;
|
|
533
2467
|
}
|
|
534
|
-
async function resolvePythonReceiverMember(index, mod, node, obj, member, source, sup, resolveExpression) {
|
|
535
|
-
const classRef = await pythonReceiverClassRef(index, mod, node, obj, source, sup, resolveExpression);
|
|
2468
|
+
async function resolvePythonReceiverMember(index, mod, node, obj, member, source, tree, sup, resolveExpression) {
|
|
2469
|
+
const classRef = await pythonReceiverClassRef(index, mod, node, obj, source, tree, sup, resolveExpression);
|
|
536
2470
|
if (!classRef)
|
|
537
2471
|
return undefined;
|
|
538
|
-
return lookupPythonClassMember(index, classRef, member);
|
|
2472
|
+
return lookupPythonClassMember(index, classRef.ref, member, classRef.startAtSupertype);
|
|
539
2473
|
}
|
|
540
|
-
async function pythonReceiverClassRef(index, mod, node, obj, source, sup, resolveExpression) {
|
|
541
|
-
const receiverName =
|
|
542
|
-
|
|
2474
|
+
async function pythonReceiverClassRef(index, mod, node, obj, source, tree, sup, resolveExpression) {
|
|
2475
|
+
const receiverName = receiverKeywordText(sup, obj, source, (callee) => {
|
|
2476
|
+
const scope = getOrBuildScopeIndex(index, mod.file, source, sup, mod, tree);
|
|
2477
|
+
return !!findClosestScopeBinding(scope, sliceText(callee, source), callee, sup);
|
|
2478
|
+
});
|
|
2479
|
+
const keywordKind = keywordReceiverKind(sup.id, receiverName);
|
|
2480
|
+
if (keywordKind) {
|
|
543
2481
|
const container = findEnclosingClassContainer(node);
|
|
544
2482
|
if (!container)
|
|
545
2483
|
return null;
|
|
@@ -559,7 +2497,8 @@ async function pythonReceiverClassRef(index, mod, node, obj, source, sup, resolv
|
|
|
559
2497
|
});
|
|
560
2498
|
if (!def)
|
|
561
2499
|
return null;
|
|
562
|
-
|
|
2500
|
+
const ref = await pythonClassRefFromDef(index, def);
|
|
2501
|
+
return ref ? { ref, startAtSupertype: keywordKind === "supertype" } : null;
|
|
563
2502
|
}
|
|
564
2503
|
let classDef;
|
|
565
2504
|
const constructor = receiverConstructorExpression(obj, source, sup);
|
|
@@ -577,7 +2516,8 @@ async function pythonReceiverClassRef(index, mod, node, obj, source, sup, resolv
|
|
|
577
2516
|
}
|
|
578
2517
|
if (!classDef)
|
|
579
2518
|
return null;
|
|
580
|
-
|
|
2519
|
+
const ref = await pythonClassRefFromDef(index, classDef);
|
|
2520
|
+
return ref ? { ref, startAtSupertype: false } : null;
|
|
581
2521
|
}
|
|
582
2522
|
async function pythonClassRefFromDef(index, def) {
|
|
583
2523
|
const module = index.byFile.get(fileIdentityKey(def.file));
|
|
@@ -664,17 +2604,15 @@ function resolvePythonNamedClass(index, mod, name) {
|
|
|
664
2604
|
return candidates[0];
|
|
665
2605
|
if (candidates.length > 1)
|
|
666
2606
|
return undefined;
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
return result;
|
|
677
|
-
}
|
|
2607
|
+
const explicitBinding = effectiveExplicitBinding(mod.imports, "python", (binding) => binding.kind === "named" && binding.local === name);
|
|
2608
|
+
if (explicitBinding?.kind === "named") {
|
|
2609
|
+
const result = resolveImported(index, explicitBinding, explicitBinding.imported);
|
|
2610
|
+
if (result && !("namespace" in result) && result.kind === SymbolKind.Class)
|
|
2611
|
+
return result;
|
|
2612
|
+
}
|
|
2613
|
+
const starDecision = resolveStarImportedName(index, mod, "python", name);
|
|
2614
|
+
if (starDecision.status === "resolved") {
|
|
2615
|
+
return starDecision.definition.kind === SymbolKind.Class ? starDecision.definition : undefined;
|
|
678
2616
|
}
|
|
679
2617
|
const exported = resolveExport(index, mod.file, name, { preferredKind: SymbolKind.Class, allowLocalFallback: false });
|
|
680
2618
|
if (exported?.kind === "resolved" && exported.def.kind === SymbolKind.Class)
|
|
@@ -699,15 +2637,17 @@ async function pythonBaseClassRefs(index, classRef) {
|
|
|
699
2637
|
}
|
|
700
2638
|
return refs;
|
|
701
2639
|
}
|
|
702
|
-
async function lookupPythonClassMember(index, start, member) {
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
2640
|
+
async function lookupPythonClassMember(index, start, member, startAtSupertype = false) {
|
|
2641
|
+
if (!startAtSupertype) {
|
|
2642
|
+
const own = pythonMembersOnClass(start, member);
|
|
2643
|
+
if (own.length === 1)
|
|
2644
|
+
return own[0];
|
|
2645
|
+
if (own.length > 1)
|
|
2646
|
+
return undefined;
|
|
2647
|
+
}
|
|
708
2648
|
let level = await pythonBaseClassRefs(index, start);
|
|
709
2649
|
const visited = new Set([pythonClassKey(start.def), ...level.map((base) => pythonClassKey(base.def))]);
|
|
710
|
-
for (let depth = 0; depth <
|
|
2650
|
+
for (let depth = 0; depth < RECEIVER_HIERARCHY_DEPTH && level.length; depth += 1) {
|
|
711
2651
|
const matches = [];
|
|
712
2652
|
const seenMatch = new Set();
|
|
713
2653
|
for (const base of level) {
|
|
@@ -737,7 +2677,4 @@ async function lookupPythonClassMember(index, start, member) {
|
|
|
737
2677
|
}
|
|
738
2678
|
return undefined;
|
|
739
2679
|
}
|
|
740
|
-
function escapeRegExp(value) {
|
|
741
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
742
|
-
}
|
|
743
2680
|
//# sourceMappingURL=navigation-goto.js.map
|