@lzehrung/codegraph 2.3.30 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -137
- package/codegraph-skill/codegraph/SKILL.md +5 -3
- package/dist/agent/artifact.d.ts +2 -0
- package/dist/agent/artifact.d.ts.map +1 -1
- package/dist/agent/artifact.js.map +1 -1
- package/dist/agent/search.d.ts.map +1 -1
- package/dist/agent/search.js +3 -0
- package/dist/agent/search.js.map +1 -1
- package/dist/agent/session.d.ts +3 -0
- package/dist/agent/session.d.ts.map +1 -1
- package/dist/agent/session.js +10 -2
- package/dist/agent/session.js.map +1 -1
- package/dist/bin/cli.js +42229 -28876
- package/dist/bin/query-index-worker.js +73 -11
- package/dist/cli/affected.d.ts.map +1 -1
- package/dist/cli/affected.js +4 -1
- package/dist/cli/affected.js.map +1 -1
- package/dist/cli/command-catalog.js +1 -1
- package/dist/cli/command-catalog.js.map +1 -1
- package/dist/cli/graph-queries.d.ts.map +1 -1
- package/dist/cli/graph-queries.js +27 -1
- package/dist/cli/graph-queries.js.map +1 -1
- package/dist/cli/help.d.ts +2 -2
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +4 -2
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/options.js +2 -2
- package/dist/cli/options.js.map +1 -1
- package/dist/drift/artifact.d.ts.map +1 -1
- package/dist/drift/artifact.js +15 -18
- package/dist/drift/artifact.js.map +1 -1
- package/dist/drift/compare.js +1 -1
- package/dist/drift/compare.js.map +1 -1
- package/dist/drift/snapshot.d.ts.map +1 -1
- package/dist/drift/snapshot.js +5 -6
- package/dist/drift/snapshot.js.map +1 -1
- package/dist/drift/types.d.ts +2 -0
- package/dist/drift/types.d.ts.map +1 -1
- package/dist/drift/types.js.map +1 -1
- package/dist/graph-builder.d.ts.map +1 -1
- package/dist/graph-builder.js +4 -6
- package/dist/graph-builder.js.map +1 -1
- package/dist/graph-edge-collector.d.ts +3 -0
- package/dist/graph-edge-collector.d.ts.map +1 -1
- package/dist/graph-edge-collector.js +8 -5
- package/dist/graph-edge-collector.js.map +1 -1
- package/dist/graphs/angularjs.d.ts.map +1 -1
- package/dist/graphs/angularjs.js +2 -1
- package/dist/graphs/angularjs.js.map +1 -1
- package/dist/graphs/edge-resolution.d.ts +4 -1
- package/dist/graphs/edge-resolution.d.ts.map +1 -1
- package/dist/graphs/edge-resolution.js +26 -2
- package/dist/graphs/edge-resolution.js.map +1 -1
- package/dist/graphs/specifiers.d.ts.map +1 -1
- package/dist/graphs/specifiers.js +6 -2
- package/dist/graphs/specifiers.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/ast.d.ts +20 -0
- package/dist/graphs/symbol-graph-detailed/ast.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/ast.js +154 -15
- package/dist/graphs/symbol-graph-detailed/ast.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/edge-passes.d.ts +51 -8
- package/dist/graphs/symbol-graph-detailed/edge-passes.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/edge-passes.js +823 -132
- package/dist/graphs/symbol-graph-detailed/edge-passes.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/import-aliases.d.ts +2 -1
- package/dist/graphs/symbol-graph-detailed/import-aliases.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/import-aliases.js +139 -7
- package/dist/graphs/symbol-graph-detailed/import-aliases.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/member-chains.d.ts +1 -0
- package/dist/graphs/symbol-graph-detailed/member-chains.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/member-chains.js +3 -1
- package/dist/graphs/symbol-graph-detailed/member-chains.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/receiver-calls.d.ts +128 -22
- package/dist/graphs/symbol-graph-detailed/receiver-calls.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/receiver-calls.js +872 -125
- package/dist/graphs/symbol-graph-detailed/receiver-calls.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed.js +434 -40
- package/dist/graphs/symbol-graph-detailed.js.map +1 -1
- package/dist/graphs/symbol-graph.d.ts +2 -0
- package/dist/graphs/symbol-graph.d.ts.map +1 -1
- package/dist/graphs/symbol-graph.js +34 -16
- package/dist/graphs/symbol-graph.js.map +1 -1
- package/dist/impact/call-compatibility/providers/index.d.ts +12 -0
- package/dist/impact/call-compatibility/providers/index.d.ts.map +1 -1
- package/dist/impact/call-compatibility/providers/index.js +16 -22
- package/dist/impact/call-compatibility/providers/index.js.map +1 -1
- package/dist/impact/call-compatibility/providers/types.d.ts +2 -0
- package/dist/impact/call-compatibility/providers/types.d.ts.map +1 -1
- package/dist/impact/call-compatibility/types.d.ts +6 -0
- package/dist/impact/call-compatibility/types.d.ts.map +1 -1
- package/dist/impact/call-compatibility.d.ts.map +1 -1
- package/dist/impact/call-compatibility.js +316 -552
- package/dist/impact/call-compatibility.js.map +1 -1
- package/dist/impact/map.js +1 -0
- package/dist/impact/map.js.map +1 -1
- package/dist/impact/member-resolution-coverage.d.ts.map +1 -1
- package/dist/impact/member-resolution-coverage.js +1 -1
- package/dist/impact/member-resolution-coverage.js.map +1 -1
- package/dist/impact/reference-cache.d.ts.map +1 -1
- package/dist/impact/reference-cache.js +2 -2
- package/dist/impact/reference-cache.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/indexer/ambiguous-resolution.d.ts +11 -0
- package/dist/indexer/ambiguous-resolution.d.ts.map +1 -0
- package/dist/indexer/ambiguous-resolution.js +19 -0
- package/dist/indexer/ambiguous-resolution.js.map +1 -0
- package/dist/indexer/build-cache/module-cache.d.ts +38 -0
- package/dist/indexer/build-cache/module-cache.d.ts.map +1 -1
- package/dist/indexer/build-cache/module-cache.js +151 -9
- package/dist/indexer/build-cache/module-cache.js.map +1 -1
- package/dist/indexer/build-cache/options.d.ts +16 -43
- package/dist/indexer/build-cache/options.d.ts.map +1 -1
- package/dist/indexer/build-cache/options.js +16 -43
- package/dist/indexer/build-cache/options.js.map +1 -1
- package/dist/indexer/build-cache/project-snapshot.d.ts +1 -0
- package/dist/indexer/build-cache/project-snapshot.d.ts.map +1 -1
- package/dist/indexer/build-cache/project-snapshot.js +117 -24
- package/dist/indexer/build-cache/project-snapshot.js.map +1 -1
- package/dist/indexer/build-cache/resolver-environment.d.ts.map +1 -1
- package/dist/indexer/build-cache/resolver-environment.js +32 -2
- package/dist/indexer/build-cache/resolver-environment.js.map +1 -1
- package/dist/indexer/build-cache.d.ts +1 -1
- package/dist/indexer/build-cache.d.ts.map +1 -1
- package/dist/indexer/build-cache.js +1 -1
- package/dist/indexer/build-cache.js.map +1 -1
- package/dist/indexer/build-index.d.ts.map +1 -1
- package/dist/indexer/build-index.js +593 -22
- package/dist/indexer/build-index.js.map +1 -1
- package/dist/indexer/call-hierarchy.js +1 -1
- package/dist/indexer/call-hierarchy.js.map +1 -1
- package/dist/indexer/compilation-units.d.ts +101 -0
- package/dist/indexer/compilation-units.d.ts.map +1 -0
- package/dist/indexer/compilation-units.js +605 -0
- package/dist/indexer/compilation-units.js.map +1 -0
- package/dist/indexer/cpp-callables.d.ts +20 -0
- package/dist/indexer/cpp-callables.d.ts.map +1 -0
- package/dist/indexer/cpp-callables.js +554 -0
- package/dist/indexer/cpp-callables.js.map +1 -0
- package/dist/indexer/declaration-visibility.d.ts +36 -2
- package/dist/indexer/declaration-visibility.d.ts.map +1 -1
- package/dist/indexer/declaration-visibility.js +136 -2
- package/dist/indexer/declaration-visibility.js.map +1 -1
- package/dist/indexer/expand-star-imports.d.ts.map +1 -1
- package/dist/indexer/expand-star-imports.js +28 -6
- package/dist/indexer/expand-star-imports.js.map +1 -1
- package/dist/indexer/import-types.d.ts +37 -0
- package/dist/indexer/import-types.d.ts.map +1 -1
- package/dist/indexer/import-types.js +24 -1
- package/dist/indexer/import-types.js.map +1 -1
- package/dist/indexer/imports/binding-ranges.d.ts +1 -1
- package/dist/indexer/imports/binding-ranges.d.ts.map +1 -1
- package/dist/indexer/imports/context.d.ts +11 -1
- package/dist/indexer/imports/context.d.ts.map +1 -1
- package/dist/indexer/imports/import-binding-tables.d.ts +75 -0
- package/dist/indexer/imports/import-binding-tables.d.ts.map +1 -0
- package/dist/indexer/imports/import-binding-tables.js +544 -0
- package/dist/indexer/imports/import-binding-tables.js.map +1 -0
- package/dist/indexer/imports/js-text-imports.js +4 -4
- package/dist/indexer/imports/js-text-imports.js.map +1 -1
- package/dist/indexer/imports/language-specific.d.ts +3 -22
- package/dist/indexer/imports/language-specific.d.ts.map +1 -1
- package/dist/indexer/imports/language-specific.js +29 -362
- package/dist/indexer/imports/language-specific.js.map +1 -1
- package/dist/indexer/imports/native-captures.d.ts.map +1 -1
- package/dist/indexer/imports/native-captures.js +18 -6
- package/dist/indexer/imports/native-captures.js.map +1 -1
- package/dist/indexer/imports/python.d.ts +10 -2
- package/dist/indexer/imports/python.d.ts.map +1 -1
- package/dist/indexer/imports/python.js +91 -40
- package/dist/indexer/imports/python.js.map +1 -1
- package/dist/indexer/imports/text-import-extractors.d.ts.map +1 -1
- package/dist/indexer/imports/text-import-extractors.js +51 -12
- package/dist/indexer/imports/text-import-extractors.js.map +1 -1
- package/dist/indexer/imports.d.ts.map +1 -1
- package/dist/indexer/imports.js +25 -5
- package/dist/indexer/imports.js.map +1 -1
- package/dist/indexer/incremental-plan.d.ts +33 -0
- package/dist/indexer/incremental-plan.d.ts.map +1 -1
- package/dist/indexer/incremental-plan.js +187 -1
- package/dist/indexer/incremental-plan.js.map +1 -1
- package/dist/indexer/locals-and-exports.d.ts +2 -0
- package/dist/indexer/locals-and-exports.d.ts.map +1 -1
- package/dist/indexer/locals-and-exports.js +565 -54
- package/dist/indexer/locals-and-exports.js.map +1 -1
- package/dist/indexer/name-lookup-policies/c-family.d.ts +7 -0
- package/dist/indexer/name-lookup-policies/c-family.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/c-family.js +117 -0
- package/dist/indexer/name-lookup-policies/c-family.js.map +1 -0
- package/dist/indexer/name-lookup-policies/csharp-using-static.d.ts +11 -0
- package/dist/indexer/name-lookup-policies/csharp-using-static.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/csharp-using-static.js +136 -0
- package/dist/indexer/name-lookup-policies/csharp-using-static.js.map +1 -0
- package/dist/indexer/name-lookup-policies/implicit-self.d.ts +10 -0
- package/dist/indexer/name-lookup-policies/implicit-self.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/implicit-self.js +113 -0
- package/dist/indexer/name-lookup-policies/implicit-self.js.map +1 -0
- package/dist/indexer/name-lookup-policies/index.d.ts +3 -0
- package/dist/indexer/name-lookup-policies/index.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/index.js +106 -0
- package/dist/indexer/name-lookup-policies/index.js.map +1 -0
- package/dist/indexer/name-lookup-policies/php.d.ts +3 -0
- package/dist/indexer/name-lookup-policies/php.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/php.js +75 -0
- package/dist/indexer/name-lookup-policies/php.js.map +1 -0
- package/dist/indexer/name-resolution-types.d.ts +99 -0
- package/dist/indexer/name-resolution-types.d.ts.map +1 -0
- package/dist/indexer/name-resolution-types.js +2 -0
- package/dist/indexer/name-resolution-types.js.map +1 -0
- package/dist/indexer/name-resolution.d.ts +52 -0
- package/dist/indexer/name-resolution.d.ts.map +1 -0
- package/dist/indexer/name-resolution.js +189 -0
- package/dist/indexer/name-resolution.js.map +1 -0
- package/dist/indexer/navigation-cpp.d.ts +50 -0
- package/dist/indexer/navigation-cpp.d.ts.map +1 -0
- package/dist/indexer/navigation-cpp.js +654 -0
- package/dist/indexer/navigation-cpp.js.map +1 -0
- package/dist/indexer/navigation-goto.d.ts +107 -4
- package/dist/indexer/navigation-goto.d.ts.map +1 -1
- package/dist/indexer/navigation-goto.js +2075 -137
- package/dist/indexer/navigation-goto.js.map +1 -1
- package/dist/indexer/navigation-local.d.ts +18 -2
- package/dist/indexer/navigation-local.d.ts.map +1 -1
- package/dist/indexer/navigation-local.js +498 -61
- package/dist/indexer/navigation-local.js.map +1 -1
- package/dist/indexer/navigation-php.d.ts +66 -0
- package/dist/indexer/navigation-php.d.ts.map +1 -1
- package/dist/indexer/navigation-php.js +287 -4
- package/dist/indexer/navigation-php.js.map +1 -1
- package/dist/indexer/navigation-references.d.ts +72 -5
- package/dist/indexer/navigation-references.d.ts.map +1 -1
- package/dist/indexer/navigation-references.js +620 -54
- package/dist/indexer/navigation-references.js.map +1 -1
- package/dist/indexer/navigation-resolve.d.ts +33 -2
- package/dist/indexer/navigation-resolve.d.ts.map +1 -1
- package/dist/indexer/navigation-resolve.js +446 -193
- package/dist/indexer/navigation-resolve.js.map +1 -1
- package/dist/indexer/navigation.d.ts +28 -2
- package/dist/indexer/navigation.d.ts.map +1 -1
- package/dist/indexer/navigation.js +1082 -151
- package/dist/indexer/navigation.js.map +1 -1
- package/dist/indexer/php-namespace-symbols.d.ts +66 -0
- package/dist/indexer/php-namespace-symbols.d.ts.map +1 -0
- package/dist/indexer/php-namespace-symbols.js +342 -0
- package/dist/indexer/php-namespace-symbols.js.map +1 -0
- package/dist/indexer/reference-context.d.ts +2 -0
- package/dist/indexer/reference-context.d.ts.map +1 -1
- package/dist/indexer/reference-context.js +4 -0
- package/dist/indexer/reference-context.js.map +1 -1
- package/dist/indexer/scope-nodes.d.ts +92 -13
- package/dist/indexer/scope-nodes.d.ts.map +1 -1
- package/dist/indexer/scope-nodes.js +212 -20
- package/dist/indexer/scope-nodes.js.map +1 -1
- package/dist/indexer/scope-types.d.ts +25 -1
- package/dist/indexer/scope-types.d.ts.map +1 -1
- package/dist/indexer/scope.d.ts.map +1 -1
- package/dist/indexer/scope.js +894 -45
- package/dist/indexer/scope.js.map +1 -1
- package/dist/indexer/shared-owner-identity.d.ts +53 -0
- package/dist/indexer/shared-owner-identity.d.ts.map +1 -0
- package/dist/indexer/shared-owner-identity.js +460 -0
- package/dist/indexer/shared-owner-identity.js.map +1 -0
- package/dist/indexer/shared.d.ts +1 -1
- package/dist/indexer/shared.d.ts.map +1 -1
- package/dist/indexer/shared.js +1 -1
- package/dist/indexer/shared.js.map +1 -1
- package/dist/indexer/star-import-precedence.d.ts +114 -0
- package/dist/indexer/star-import-precedence.d.ts.map +1 -0
- package/dist/indexer/star-import-precedence.js +396 -0
- package/dist/indexer/star-import-precedence.js.map +1 -0
- package/dist/indexer/symbols.d.ts +3 -1
- package/dist/indexer/symbols.d.ts.map +1 -1
- package/dist/indexer/symbols.js +58 -18
- package/dist/indexer/symbols.js.map +1 -1
- package/dist/indexer/ts-callables.d.ts +51 -0
- package/dist/indexer/ts-callables.d.ts.map +1 -0
- package/dist/indexer/ts-callables.js +264 -0
- package/dist/indexer/ts-callables.js.map +1 -0
- package/dist/indexer/types.d.ts +23 -1
- package/dist/indexer/types.d.ts.map +1 -1
- package/dist/indexer/unused-exports.d.ts +13 -0
- package/dist/indexer/unused-exports.d.ts.map +1 -0
- package/dist/indexer/unused-exports.js +159 -0
- package/dist/indexer/unused-exports.js.map +1 -0
- package/dist/indexer/workspace-symbols.d.ts.map +1 -1
- package/dist/indexer/workspace-symbols.js +3 -2
- package/dist/indexer/workspace-symbols.js.map +1 -1
- package/dist/indexer.d.ts +2 -2
- package/dist/indexer.d.ts.map +1 -1
- package/dist/indexer.js +1 -1
- package/dist/indexer.js.map +1 -1
- package/dist/languages/callable-arity.d.ts +202 -0
- package/dist/languages/callable-arity.d.ts.map +1 -0
- package/dist/languages/callable-arity.js +1182 -0
- package/dist/languages/callable-arity.js.map +1 -0
- package/dist/languages/definitions/c-family.d.ts.map +1 -1
- package/dist/languages/definitions/c-family.js +30 -1
- package/dist/languages/definitions/c-family.js.map +1 -1
- package/dist/languages/definitions/c.d.ts +4 -0
- package/dist/languages/definitions/c.d.ts.map +1 -1
- package/dist/languages/definitions/c.js +19 -0
- package/dist/languages/definitions/c.js.map +1 -1
- package/dist/languages/definitions/cpp.d.ts.map +1 -1
- package/dist/languages/definitions/cpp.js +12 -0
- package/dist/languages/definitions/cpp.js.map +1 -1
- package/dist/languages/definitions/csharp.d.ts.map +1 -1
- package/dist/languages/definitions/csharp.js +2 -8
- package/dist/languages/definitions/csharp.js.map +1 -1
- package/dist/languages/definitions/go.d.ts.map +1 -1
- package/dist/languages/definitions/go.js +3 -0
- package/dist/languages/definitions/go.js.map +1 -1
- package/dist/languages/definitions/javascript.d.ts.map +1 -1
- package/dist/languages/definitions/javascript.js +10 -1
- package/dist/languages/definitions/javascript.js.map +1 -1
- package/dist/languages/definitions/js-family.d.ts +1 -1
- package/dist/languages/definitions/js-family.d.ts.map +1 -1
- package/dist/languages/definitions/js-family.js +7 -1
- package/dist/languages/definitions/js-family.js.map +1 -1
- package/dist/languages/definitions/php.d.ts.map +1 -1
- package/dist/languages/definitions/php.js +5 -1
- package/dist/languages/definitions/php.js.map +1 -1
- package/dist/languages/definitions/typescript.d.ts.map +1 -1
- package/dist/languages/definitions/typescript.js +8 -1
- package/dist/languages/definitions/typescript.js.map +1 -1
- package/dist/languages/definitions/zig.d.ts.map +1 -1
- package/dist/languages/definitions/zig.js +2 -0
- package/dist/languages/definitions/zig.js.map +1 -1
- package/dist/mcp/handlers.js +14 -7
- package/dist/mcp/handlers.js.map +1 -1
- package/dist/review/candidates.d.ts.map +1 -1
- package/dist/review/candidates.js +1 -1
- package/dist/review/candidates.js.map +1 -1
- package/dist/review/deleted.d.ts +3 -3
- package/dist/review/deleted.d.ts.map +1 -1
- package/dist/review/deleted.js +82 -32
- package/dist/review/deleted.js.map +1 -1
- package/dist/review/report.d.ts +1 -0
- package/dist/review/report.d.ts.map +1 -1
- package/dist/review/report.js +1 -1
- package/dist/review/report.js.map +1 -1
- package/dist/review.d.ts.map +1 -1
- package/dist/review.js +1 -0
- package/dist/review.js.map +1 -1
- package/dist/sql/navigation.d.ts.map +1 -1
- package/dist/sql/navigation.js +1 -0
- package/dist/sql/navigation.js.map +1 -1
- package/dist/sql/source-graph.d.ts.map +1 -1
- package/dist/sql/source-graph.js +2 -2
- package/dist/sql/source-graph.js.map +1 -1
- package/dist/sqlite/schema.d.ts +1 -1
- package/dist/sqlite/schema.d.ts.map +1 -1
- package/dist/sqlite/schema.js +8 -2
- package/dist/sqlite/schema.js.map +1 -1
- package/dist/sqlite/write.js +2 -2
- package/dist/sqlite/write.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/util/bloom-filter.d.ts +7 -1
- package/dist/util/bloom-filter.d.ts.map +1 -1
- package/dist/util/bloom-filter.js +12 -2
- package/dist/util/bloom-filter.js.map +1 -1
- package/dist/util/dynamic-import-tables.d.ts +88 -0
- package/dist/util/dynamic-import-tables.d.ts.map +1 -0
- package/dist/util/dynamic-import-tables.js +100 -0
- package/dist/util/dynamic-import-tables.js.map +1 -0
- package/dist/util/graph-edges.d.ts +19 -1
- package/dist/util/graph-edges.d.ts.map +1 -1
- package/dist/util/graph-edges.js +30 -4
- package/dist/util/graph-edges.js.map +1 -1
- package/dist/util/identifiers.d.ts +15 -0
- package/dist/util/identifiers.d.ts.map +1 -1
- package/dist/util/identifiers.js +35 -0
- package/dist/util/identifiers.js.map +1 -1
- package/dist/util/member-access-tables.d.ts +129 -0
- package/dist/util/member-access-tables.d.ts.map +1 -0
- package/dist/util/member-access-tables.js +285 -0
- package/dist/util/member-access-tables.js.map +1 -0
- package/dist/util/member-access.d.ts +17 -0
- package/dist/util/member-access.d.ts.map +1 -1
- package/dist/util/member-access.js +138 -93
- package/dist/util/member-access.js.map +1 -1
- package/dist/util/resolution/cargo-targets.d.ts +11 -0
- package/dist/util/resolution/cargo-targets.d.ts.map +1 -1
- package/dist/util/resolution/cargo-targets.js +174 -13
- package/dist/util/resolution/cargo-targets.js.map +1 -1
- package/dist/util/resolution/csharp.d.ts +33 -1
- package/dist/util/resolution/csharp.d.ts.map +1 -1
- package/dist/util/resolution/csharp.js +145 -30
- package/dist/util/resolution/csharp.js.map +1 -1
- package/dist/util/resolution/php.d.ts.map +1 -1
- package/dist/util/resolution/php.js +35 -8
- package/dist/util/resolution/php.js.map +1 -1
- package/dist/util/resolution/python.d.ts +9 -0
- package/dist/util/resolution/python.d.ts.map +1 -1
- package/dist/util/resolution/python.js +46 -0
- package/dist/util/resolution/python.js.map +1 -1
- package/dist/util/resolution/rust.d.ts.map +1 -1
- package/dist/util/resolution/rust.js +95 -26
- package/dist/util/resolution/rust.js.map +1 -1
- package/dist/util/resolution-candidates.d.ts +5 -0
- package/dist/util/resolution-candidates.d.ts.map +1 -1
- package/dist/util/resolution-candidates.js +16 -3
- package/dist/util/resolution-candidates.js.map +1 -1
- package/dist/util/resolution.d.ts +2 -1
- package/dist/util/resolution.d.ts.map +1 -1
- package/dist/util/resolution.js +96 -5
- package/dist/util/resolution.js.map +1 -1
- package/dist/util/specifiers.d.ts +20 -0
- package/dist/util/specifiers.d.ts.map +1 -1
- package/dist/util/specifiers.js +39 -93
- package/dist/util/specifiers.js.map +1 -1
- package/docs/mcp.md +3 -2
- package/package.json +3 -3
|
@@ -1,15 +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 { 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";
|
|
7
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";
|
|
8
19
|
import { okGoToResult } from "./navigation-provenance.js";
|
|
9
|
-
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";
|
|
10
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
|
+
}
|
|
11
396
|
export async function resolveMemberAccessDefinition(params) {
|
|
12
|
-
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
|
+
}
|
|
13
403
|
const parent = node.parent;
|
|
14
404
|
if (!parent || !sup.supportsCrossModuleSymbols) {
|
|
15
405
|
return null;
|
|
@@ -25,46 +415,143 @@ export async function resolveMemberAccessDefinition(params) {
|
|
|
25
415
|
return null;
|
|
26
416
|
const { object: obj, property: prop } = getMemberAccessParts(sup, memberNode);
|
|
27
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;
|
|
28
422
|
const resolveExpression = async (expr) => {
|
|
29
423
|
const exprIsId = isReceiverNameNode(sup, expr.type) && !isMemberAccessNode(sup, expr);
|
|
30
424
|
if (exprIsId) {
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
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
|
+
}
|
|
34
433
|
const exprName = sliceText(expr, source);
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
(
|
|
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
|
+
}
|
|
38
467
|
if (imp) {
|
|
39
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
|
+
}
|
|
40
474
|
return {
|
|
41
475
|
kind: "namespace",
|
|
42
476
|
file: typeof imp.resolved === "string" ? imp.resolved.replace(/\\/g, "/") : imp.resolved?.external || "",
|
|
43
477
|
};
|
|
44
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
|
+
}
|
|
45
484
|
const result = resolveImported(index, imp, imp.kind === "named" ? imp.imported : "default");
|
|
46
485
|
if (result) {
|
|
47
486
|
if ("namespace" in result) {
|
|
48
487
|
return { kind: "namespace", file: result.namespace };
|
|
49
488
|
}
|
|
50
|
-
|
|
489
|
+
const container = asMemberContainer(index, result);
|
|
490
|
+
return { kind: "resolved", def: container ?? result };
|
|
51
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;
|
|
52
496
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
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 };
|
|
63
521
|
}
|
|
64
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;
|
|
65
534
|
return null;
|
|
66
535
|
}
|
|
67
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
|
+
}
|
|
68
555
|
const parts = getMemberAccessParts(sup, expr);
|
|
69
556
|
const subObj = parts.object;
|
|
70
557
|
let subProp = parts.property;
|
|
@@ -74,15 +561,24 @@ export async function resolveMemberAccessDefinition(params) {
|
|
|
74
561
|
if (subObj && subProp) {
|
|
75
562
|
const base = await resolveExpression(subObj);
|
|
76
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
|
+
}
|
|
77
571
|
if (base?.kind === "namespace") {
|
|
572
|
+
if (!isGoExportedMemberName(sup.id, memberName))
|
|
573
|
+
return null;
|
|
78
574
|
return resolveExport(index, base.file, memberName, { allowLocalFallback: false });
|
|
79
575
|
}
|
|
80
576
|
if (base?.kind === "resolved") {
|
|
81
|
-
if (sup.id === "java") {
|
|
82
|
-
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));
|
|
83
579
|
return memberDef ? { kind: "resolved", def: memberDef } : null;
|
|
84
580
|
}
|
|
85
|
-
if (sup.id === "ruby") {
|
|
581
|
+
if (sup.id === "ruby" && declaresMembers(base.def)) {
|
|
86
582
|
const memberDef = await resolveMemberDefinitionForBase(index, base.def, memberName);
|
|
87
583
|
if (memberDef)
|
|
88
584
|
return { kind: "resolved", def: memberDef };
|
|
@@ -104,7 +600,7 @@ export async function resolveMemberAccessDefinition(params) {
|
|
|
104
600
|
return resolveExport(index, base.file, memberName, { allowLocalFallback: false });
|
|
105
601
|
}
|
|
106
602
|
if (base?.kind === "resolved") {
|
|
107
|
-
const memberDef = await resolveMemberDefinitionForBase(index, base.def, memberName);
|
|
603
|
+
const memberDef = await resolveMemberDefinitionForBase(index, base.def, memberName, callArgumentCountFor(expr));
|
|
108
604
|
return memberDef ? { kind: "resolved", def: memberDef } : null;
|
|
109
605
|
}
|
|
110
606
|
}
|
|
@@ -133,11 +629,11 @@ export async function resolveMemberAccessDefinition(params) {
|
|
|
133
629
|
}
|
|
134
630
|
}
|
|
135
631
|
const receiverName = obj ? sliceText(obj, source) : "";
|
|
136
|
-
const
|
|
137
|
-
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)) {
|
|
138
634
|
const member = sliceText(prop, source);
|
|
139
635
|
if (sup.id === "python") {
|
|
140
|
-
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);
|
|
141
637
|
if (!memberDef)
|
|
142
638
|
return null;
|
|
143
639
|
return okGoToResult(index, memberDef, {
|
|
@@ -146,11 +642,12 @@ export async function resolveMemberAccessDefinition(params) {
|
|
|
146
642
|
confidence: "medium",
|
|
147
643
|
});
|
|
148
644
|
}
|
|
149
|
-
if (
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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);
|
|
154
651
|
if (memberDef) {
|
|
155
652
|
return okGoToResult(index, memberDef, {
|
|
156
653
|
via: { exportedName: member },
|
|
@@ -158,8 +655,74 @@ export async function resolveMemberAccessDefinition(params) {
|
|
|
158
655
|
confidence: "medium",
|
|
159
656
|
});
|
|
160
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" };
|
|
161
675
|
}
|
|
162
|
-
|
|
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);
|
|
163
726
|
if (receiver) {
|
|
164
727
|
const objDef = receiver.def;
|
|
165
728
|
const targetContext = await ensureParsedContext(objDef.file, undefined, index.languageExtensions);
|
|
@@ -184,37 +747,1030 @@ export async function resolveMemberAccessDefinition(params) {
|
|
|
184
747
|
const memberPredicate = receiver.memberScope === "any"
|
|
185
748
|
? undefined
|
|
186
749
|
: (local) => matchesReceiverMemberScope(local, receiver.memberScope, targetContext, container);
|
|
750
|
+
const knownArgumentCount = getCallArgumentCount({ languageId: sup.id, source, call: memberNode.parent ?? memberNode }) ?? undefined;
|
|
187
751
|
let memberDef;
|
|
188
|
-
if (
|
|
189
|
-
|
|
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);
|
|
190
759
|
}
|
|
191
760
|
else {
|
|
192
|
-
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;
|
|
193
1316
|
}
|
|
194
|
-
if (
|
|
195
|
-
return
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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;
|
|
200
1357
|
}
|
|
201
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);
|
|
202
1383
|
}
|
|
203
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");
|
|
204
1574
|
}
|
|
1575
|
+
if (languageId === "kotlin")
|
|
1576
|
+
return call.type === "call_expression" ? (call.namedChildren[0] ?? null) : null;
|
|
205
1577
|
return null;
|
|
206
1578
|
}
|
|
207
|
-
function
|
|
208
|
-
|
|
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;
|
|
209
1695
|
}
|
|
210
|
-
export { supportsReceiverCallEdges, supportsReceiverMemberNavigation
|
|
1696
|
+
export { supportsReceiverCallEdges, supportsReceiverMemberNavigation } from "../util/member-access-tables.js";
|
|
211
1697
|
function memberDeclaringLocals(mod, typeName, normalize) {
|
|
212
1698
|
const normalized = normalize(typeName);
|
|
213
1699
|
return mod.locals.filter((local) => normalize(local.localName) === normalized && declaresMembers(local));
|
|
214
1700
|
}
|
|
215
|
-
|
|
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) {
|
|
216
1760
|
const constructor = receiverConstructorExpression(obj, source, sup);
|
|
217
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
|
+
}
|
|
218
1774
|
const typeName = sliceText(constructor, source);
|
|
219
1775
|
const typedLocals = memberDeclaringLocals(mod, typeName, sup.normalizeIdentifier);
|
|
220
1776
|
if (typedLocals.length === 1) {
|
|
@@ -223,13 +1779,36 @@ async function resolveReceiverDefinition(obj, source, sup, resolveExpression, mo
|
|
|
223
1779
|
memberScope: hasStaticMemberDistinction(sup.id) ? "instance" : "any",
|
|
224
1780
|
};
|
|
225
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
|
+
}
|
|
226
1792
|
const result = await resolveExpression(constructor);
|
|
227
|
-
|
|
1793
|
+
const constructed = result?.kind === "resolved" ? asMemberContainer(index, result.def) : undefined;
|
|
1794
|
+
if (constructed) {
|
|
228
1795
|
return {
|
|
229
|
-
def:
|
|
1796
|
+
def: constructed,
|
|
230
1797
|
memberScope: hasStaticMemberDistinction(sup.id) ? "instance" : "any",
|
|
231
1798
|
};
|
|
232
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
|
+
}
|
|
233
1812
|
if (typedLocals[0]) {
|
|
234
1813
|
return {
|
|
235
1814
|
def: typedLocals[0],
|
|
@@ -238,12 +1817,13 @@ async function resolveReceiverDefinition(obj, source, sup, resolveExpression, mo
|
|
|
238
1817
|
}
|
|
239
1818
|
}
|
|
240
1819
|
const direct = await resolveExpression(obj);
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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 };
|
|
244
1824
|
}
|
|
245
|
-
const memberScope =
|
|
246
|
-
return { def:
|
|
1825
|
+
const memberScope = supportsStaticMemberScope(sup.id) ? "static" : "any";
|
|
1826
|
+
return { def: directContainer, memberScope };
|
|
247
1827
|
}
|
|
248
1828
|
if (isJsTsLanguage(sup.id) && isReceiverNameNode(sup, obj.type)) {
|
|
249
1829
|
return null;
|
|
@@ -253,7 +1833,25 @@ async function resolveReceiverDefinition(obj, source, sup, resolveExpression, mo
|
|
|
253
1833
|
}
|
|
254
1834
|
return null;
|
|
255
1835
|
}
|
|
256
|
-
|
|
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) {
|
|
257
1855
|
const targetContext = await ensureParsedContext(baseDef.file, undefined, index.languageExtensions);
|
|
258
1856
|
const start = baseDef.range.start;
|
|
259
1857
|
const targetPosition = {
|
|
@@ -267,37 +1865,141 @@ async function resolveMemberDefinitionForBase(index, baseDef, member) {
|
|
|
267
1865
|
const targetModule = index.byFile.get(fileIdentityKey(baseDef.file));
|
|
268
1866
|
if (!targetModule)
|
|
269
1867
|
return undefined;
|
|
1868
|
+
const languageId = targetContext.sup.id;
|
|
270
1869
|
const normalizeIdentifier = targetContext.sup.normalizeIdentifier;
|
|
271
|
-
const
|
|
272
|
-
if (
|
|
273
|
-
|
|
274
|
-
|
|
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")
|
|
275
1896
|
return undefined;
|
|
276
|
-
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;
|
|
277
1951
|
}
|
|
278
|
-
function findReceiverMemberDefinition(locals, member, receiverDef, container, targetContext, normalizeIdentifier, memberScope = "any") {
|
|
1952
|
+
async function findReceiverMemberDefinition(index, locals, member, receiverDef, container, targetContext, normalizeIdentifier, memberScope = "any", knownArgumentCount) {
|
|
279
1953
|
const memberPredicate = memberScope === "any"
|
|
280
1954
|
? undefined
|
|
281
1955
|
: (local) => matchesReceiverMemberScope(local, memberScope, targetContext, container);
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
+
}
|
|
287
1980
|
if (targetContext.sup.id === "rust") {
|
|
288
|
-
const
|
|
289
|
-
|
|
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);
|
|
290
1986
|
}
|
|
291
1987
|
if (targetContext.sup.id === "go") {
|
|
292
1988
|
return findGoReceiverMember(locals, member, receiverDef.localName, targetContext, normalizeIdentifier);
|
|
293
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
|
+
}
|
|
294
1996
|
return undefined;
|
|
295
1997
|
}
|
|
296
|
-
function
|
|
1998
|
+
function findLocalsWithinNode(locals, member, node, normalizeIdentifier = (name) => name, predicate) {
|
|
297
1999
|
const containerStart = node.startIndex;
|
|
298
2000
|
const containerEnd = node.endIndex;
|
|
299
2001
|
const normalizedMember = normalizeIdentifier(member);
|
|
300
|
-
return locals.
|
|
2002
|
+
return locals.filter((local) => {
|
|
301
2003
|
const startIndex = local.range.start.index;
|
|
302
2004
|
const endIndex = local.range.end.index;
|
|
303
2005
|
return (normalizeIdentifier(local.localName) === normalizedMember &&
|
|
@@ -308,27 +2010,108 @@ function findLocalWithinNode(locals, member, node, normalizeIdentifier = (name)
|
|
|
308
2010
|
(!predicate || predicate(local)));
|
|
309
2011
|
});
|
|
310
2012
|
}
|
|
2013
|
+
function findLocalWithinNode(locals, member, node, normalizeIdentifier = (name) => name, predicate) {
|
|
2014
|
+
return findLocalsWithinNode(locals, member, node, normalizeIdentifier, predicate)[0];
|
|
2015
|
+
}
|
|
311
2016
|
function matchesReceiverMemberScope(local, memberScope, targetContext, container) {
|
|
312
2017
|
if (memberScope === "any")
|
|
313
2018
|
return true;
|
|
314
2019
|
return hasStaticModifier(local, targetContext, container) === (memberScope === "static");
|
|
315
2020
|
}
|
|
316
|
-
function
|
|
317
|
-
|
|
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)
|
|
318
2050
|
return true;
|
|
319
|
-
|
|
320
|
-
|
|
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;
|
|
321
2066
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
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
|
+
}
|
|
329
2101
|
}
|
|
330
2102
|
}
|
|
331
|
-
|
|
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;
|
|
332
2115
|
}
|
|
333
2116
|
function hasStaticModifier(local, targetContext, container) {
|
|
334
2117
|
const position = {
|
|
@@ -336,19 +2119,21 @@ function hasStaticModifier(local, targetContext, container) {
|
|
|
336
2119
|
column: local.range.start.column - 1,
|
|
337
2120
|
};
|
|
338
2121
|
let current = targetContext.tree.rootNode.descendantForPosition(position, position);
|
|
2122
|
+
if (declarationIsStaticEquivalent(targetContext.sup.id, current))
|
|
2123
|
+
return true;
|
|
339
2124
|
while (current && current !== container) {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
if (nodeDeclaresStatic(child, targetContext.source))
|
|
345
|
-
return true;
|
|
346
|
-
}
|
|
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;
|
|
347
2129
|
current = current.parent;
|
|
348
2130
|
}
|
|
349
2131
|
return false;
|
|
350
2132
|
}
|
|
351
2133
|
const NESTED_MEMBER_LOCAL_CONTAINERS = new Set([
|
|
2134
|
+
"formal_parameters",
|
|
2135
|
+
"parameter_list",
|
|
2136
|
+
"parameters",
|
|
352
2137
|
"block",
|
|
353
2138
|
"class",
|
|
354
2139
|
"class_declaration",
|
|
@@ -367,14 +2152,22 @@ const NESTED_MEMBER_LOCAL_CONTAINERS = new Set([
|
|
|
367
2152
|
"module",
|
|
368
2153
|
"statement_block",
|
|
369
2154
|
]);
|
|
370
|
-
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 = [];
|
|
371
2164
|
const containerStart = container.startIndex;
|
|
372
2165
|
const containerEnd = container.endIndex;
|
|
373
2166
|
const normalizedMember = normalizeIdentifier(member);
|
|
374
2167
|
for (const local of locals) {
|
|
375
2168
|
const startIndex = local.range.start.index;
|
|
376
2169
|
const endIndex = local.range.end.index;
|
|
377
|
-
if (
|
|
2170
|
+
if (!matchesReceiverMemberName(local, normalizedMember, targetContext, normalizeIdentifier) ||
|
|
378
2171
|
startIndex === undefined ||
|
|
379
2172
|
endIndex === undefined ||
|
|
380
2173
|
startIndex < containerStart ||
|
|
@@ -390,8 +2183,18 @@ function findDirectLocalWithinNode(locals, member, container, targetContext, nor
|
|
|
390
2183
|
let isDeclarationParent = true;
|
|
391
2184
|
while (current && current !== container) {
|
|
392
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;
|
|
393
2194
|
if (!isDeclarationParent &&
|
|
394
2195
|
!isDirectBody &&
|
|
2196
|
+
!isOwnFunctionDeclaration &&
|
|
2197
|
+
!isTransparentBody &&
|
|
395
2198
|
((current.type === "class_body" && current.parent !== container) ||
|
|
396
2199
|
NESTED_MEMBER_LOCAL_CONTAINERS.has(current.type))) {
|
|
397
2200
|
current = null;
|
|
@@ -401,30 +2204,138 @@ function findDirectLocalWithinNode(locals, member, container, targetContext, nor
|
|
|
401
2204
|
current = current.parent;
|
|
402
2205
|
}
|
|
403
2206
|
if (current && (!predicate || predicate(local)))
|
|
404
|
-
|
|
2207
|
+
matches.push(local);
|
|
405
2208
|
}
|
|
406
|
-
return
|
|
2209
|
+
return matches;
|
|
407
2210
|
}
|
|
408
|
-
function
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
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;
|
|
417
2227
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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;
|
|
421
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")
|
|
422
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);
|
|
423
2335
|
};
|
|
424
2336
|
visit(root);
|
|
425
2337
|
return found;
|
|
426
2338
|
}
|
|
427
|
-
const GO_EMBED_DEPTH = 16;
|
|
428
2339
|
function goMethodReceiverTypeName(methodNode, source, sup) {
|
|
429
2340
|
const receiver = methodNode.childForFieldName("receiver");
|
|
430
2341
|
if (!receiver)
|
|
@@ -498,10 +2409,35 @@ function goMethodsNamedOnType(locals, member, typeName, targetContext, normalize
|
|
|
498
2409
|
visit(targetContext.tree.rootNode);
|
|
499
2410
|
return matches;
|
|
500
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
|
+
}
|
|
501
2437
|
function findGoReceiverMember(locals, member, typeName, targetContext, normalizeIdentifier) {
|
|
502
2438
|
const visited = new Set();
|
|
503
2439
|
let level = [typeName];
|
|
504
|
-
for (let depth = 0; depth <
|
|
2440
|
+
for (let depth = 0; depth < RECEIVER_HIERARCHY_DEPTH && level.length; depth += 1) {
|
|
505
2441
|
const matches = [];
|
|
506
2442
|
const next = [];
|
|
507
2443
|
for (const currentType of level) {
|
|
@@ -525,20 +2461,23 @@ function findGoReceiverMember(locals, member, typeName, targetContext, normalize
|
|
|
525
2461
|
}
|
|
526
2462
|
return undefined;
|
|
527
2463
|
}
|
|
528
|
-
const PYTHON_SUPERTYPE_DEPTH = 16;
|
|
529
2464
|
function pythonClassKey(def) {
|
|
530
2465
|
const start = def.range.start;
|
|
531
2466
|
return `${fileIdentityKey(def.file)}:${start.index ?? `${start.line}:${start.column}`}`;
|
|
532
2467
|
}
|
|
533
|
-
async function resolvePythonReceiverMember(index, mod, node, obj, member, source, sup, resolveExpression) {
|
|
534
|
-
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);
|
|
535
2470
|
if (!classRef)
|
|
536
2471
|
return undefined;
|
|
537
|
-
return lookupPythonClassMember(index, classRef, member);
|
|
2472
|
+
return lookupPythonClassMember(index, classRef.ref, member, classRef.startAtSupertype);
|
|
538
2473
|
}
|
|
539
|
-
async function pythonReceiverClassRef(index, mod, node, obj, source, sup, resolveExpression) {
|
|
540
|
-
const receiverName =
|
|
541
|
-
|
|
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) {
|
|
542
2481
|
const container = findEnclosingClassContainer(node);
|
|
543
2482
|
if (!container)
|
|
544
2483
|
return null;
|
|
@@ -558,7 +2497,8 @@ async function pythonReceiverClassRef(index, mod, node, obj, source, sup, resolv
|
|
|
558
2497
|
});
|
|
559
2498
|
if (!def)
|
|
560
2499
|
return null;
|
|
561
|
-
|
|
2500
|
+
const ref = await pythonClassRefFromDef(index, def);
|
|
2501
|
+
return ref ? { ref, startAtSupertype: keywordKind === "supertype" } : null;
|
|
562
2502
|
}
|
|
563
2503
|
let classDef;
|
|
564
2504
|
const constructor = receiverConstructorExpression(obj, source, sup);
|
|
@@ -576,7 +2516,8 @@ async function pythonReceiverClassRef(index, mod, node, obj, source, sup, resolv
|
|
|
576
2516
|
}
|
|
577
2517
|
if (!classDef)
|
|
578
2518
|
return null;
|
|
579
|
-
|
|
2519
|
+
const ref = await pythonClassRefFromDef(index, classDef);
|
|
2520
|
+
return ref ? { ref, startAtSupertype: false } : null;
|
|
580
2521
|
}
|
|
581
2522
|
async function pythonClassRefFromDef(index, def) {
|
|
582
2523
|
const module = index.byFile.get(fileIdentityKey(def.file));
|
|
@@ -663,17 +2604,15 @@ function resolvePythonNamedClass(index, mod, name) {
|
|
|
663
2604
|
return candidates[0];
|
|
664
2605
|
if (candidates.length > 1)
|
|
665
2606
|
return undefined;
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
return result;
|
|
676
|
-
}
|
|
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;
|
|
677
2616
|
}
|
|
678
2617
|
const exported = resolveExport(index, mod.file, name, { preferredKind: SymbolKind.Class, allowLocalFallback: false });
|
|
679
2618
|
if (exported?.kind === "resolved" && exported.def.kind === SymbolKind.Class)
|
|
@@ -698,15 +2637,17 @@ async function pythonBaseClassRefs(index, classRef) {
|
|
|
698
2637
|
}
|
|
699
2638
|
return refs;
|
|
700
2639
|
}
|
|
701
|
-
async function lookupPythonClassMember(index, start, member) {
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
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
|
+
}
|
|
707
2648
|
let level = await pythonBaseClassRefs(index, start);
|
|
708
2649
|
const visited = new Set([pythonClassKey(start.def), ...level.map((base) => pythonClassKey(base.def))]);
|
|
709
|
-
for (let depth = 0; depth <
|
|
2650
|
+
for (let depth = 0; depth < RECEIVER_HIERARCHY_DEPTH && level.length; depth += 1) {
|
|
710
2651
|
const matches = [];
|
|
711
2652
|
const seenMatch = new Set();
|
|
712
2653
|
for (const base of level) {
|
|
@@ -736,7 +2677,4 @@ async function lookupPythonClassMember(index, start, member) {
|
|
|
736
2677
|
}
|
|
737
2678
|
return undefined;
|
|
738
2679
|
}
|
|
739
|
-
function escapeRegExp(value) {
|
|
740
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
741
|
-
}
|
|
742
2680
|
//# sourceMappingURL=navigation-goto.js.map
|