@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,17 +1,33 @@
|
|
|
1
|
+
import { SymbolKind } from "../../indexer/types.js";
|
|
2
|
+
import { cppCallableShapeForNode } from "../../indexer/cpp-callables.js";
|
|
3
|
+
import { getCompilationUnitPeers } from "../../indexer/compilation-units.js";
|
|
4
|
+
import { isExportedDeclaration, isGoExportedMemberName, isSwiftCrossFileHiddenSharedOwnerMember, isSwiftFileHiddenSharedOwnerMember, } from "../../indexer/declaration-visibility.js";
|
|
5
|
+
import { resolveCppQualifiedMemberContainer } from "../../indexer/navigation-cpp.js";
|
|
6
|
+
import { cppQualifiedOwnerHasImplicitThis, findTypeScriptNamespaceMemberCandidates, innermostNamespaceImport, isDirectKeywordMemberDeclaration, memberAcceptsCallAt, resolvePhpObjectCreationTarget, resolveRubyVisibleConstant, resolveSharedOwnerContainers, } from "../../indexer/navigation-goto.js";
|
|
7
|
+
import { findClosestScopeBinding } from "../../indexer/navigation-local.js";
|
|
8
|
+
import { findPhpImportAlias, inferPhpQualifiedReferenceImportType } from "../../indexer/navigation-php.js";
|
|
9
|
+
import { cjsRequireValueBinding, resolveImportTypeMember, resolvePhpExportByImportType, } from "../../indexer/navigation-resolve.js";
|
|
10
|
+
import { effectiveExplicitBinding } from "../../indexer/star-import-precedence.js";
|
|
11
|
+
import { typescriptSelectOverloadCandidate } from "../../indexer/ts-callables.js";
|
|
12
|
+
import { isSwiftConstrainedExtension, isSwiftExtensionContainer, selectCsharpPartialRepresentative, } from "../../indexer/shared-owner-identity.js";
|
|
13
|
+
import { isJsTsLanguage } from "../../languages/js-family.js";
|
|
14
|
+
import { getCallableArity, getCallArgumentCount, memberLookupBinding } from "../../languages/callable-arity.js";
|
|
1
15
|
import { sliceText, toRange } from "../../util/ast.js";
|
|
2
|
-
import { getMemberAccessParts } from "../../util/member-access.js";
|
|
16
|
+
import { getMemberAccessParts, isMemberAccessNode, rustTokenTreeHoldsExpressions, rustTokenTreeNameFollowsSeparator, } from "../../util/member-access.js";
|
|
17
|
+
import { foldPhpIdentifierCase } from "../../util/identifiers.js";
|
|
18
|
+
import { MEMBER_ACCESS_ROWS, keywordReceiverKind, } from "../../util/member-access-tables.js";
|
|
3
19
|
import { fileIdentityKey } from "../../util/paths.js";
|
|
4
20
|
import { defNodeId, nodeForDef } from "../symbol-graph.js";
|
|
5
|
-
import { collectNodesByType, findFirstNodeByType, isIdentifierType } from "./ast.js";
|
|
6
|
-
import { CALL_ARGUMENT_NODE_TYPES,
|
|
21
|
+
import { collectNodesByType, declarationMemberArity, findFirstNodeByType, isIdentifierType } from "./ast.js";
|
|
22
|
+
import { CALL_ARGUMENT_NODE_TYPES, classifyReceiver, constructionTypeName, declarationIsStaticEquivalent, declarationNodeIsStatic, cppOutOfLineOwnerPath, cppOutOfLineMemberDeclarationNode, cppQualifiedNameSegments, csharpDottedNameRoot, declaresMembers, isUnprovenHeritageExpression, kotlinExtensionReceiverTypeNode, memberContainerDef, nearestMemberContainer, nodeInStaticMemberContext, phpObjectCreationKeyword, receiverCallAccess, rustImplSelfTypeNode, supportsImplicitSelfMemberCalls, supportsReceiverMemberOverloads, receiverConstructorExpression, importTypeQuerySpecifier, } from "./receiver-calls.js";
|
|
7
23
|
function ensureNode(context, def) {
|
|
8
24
|
const id = defNodeId(def);
|
|
9
25
|
if (!context.nodes.has(id))
|
|
10
26
|
context.nodes.set(id, nodeForDef(def));
|
|
11
27
|
return id;
|
|
12
28
|
}
|
|
13
|
-
function markImplementationTarget(context, id, declarationNode, def) {
|
|
14
|
-
const declaration = sliceText(declarationNode,
|
|
29
|
+
function markImplementationTarget(context, id, declarationNode, declarationSource, def) {
|
|
30
|
+
const declaration = sliceText(declarationNode, declarationSource);
|
|
15
31
|
const nameIndex = declaration.indexOf(def.localName);
|
|
16
32
|
const prefix = nameIndex >= 0 ? declaration.slice(0, nameIndex) : declaration;
|
|
17
33
|
if (!/\b(?:abstract|virtual|override)\b/.test(prefix))
|
|
@@ -21,27 +37,58 @@ function markImplementationTarget(context, id, declarationNode, def) {
|
|
|
21
37
|
node.implementationTarget = true;
|
|
22
38
|
}
|
|
23
39
|
function markMemberArity(context, id, declarationNode) {
|
|
24
|
-
|
|
25
|
-
if (
|
|
26
|
-
for (const type of [
|
|
27
|
-
"formal_parameters",
|
|
28
|
-
"parameter_list",
|
|
29
|
-
"parameters",
|
|
30
|
-
"method_parameters",
|
|
31
|
-
"function_parameter_clause",
|
|
32
|
-
]) {
|
|
33
|
-
parameters = findFirstNodeByType(declarationNode, type);
|
|
34
|
-
if (parameters)
|
|
35
|
-
break;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
if (!parameters)
|
|
40
|
+
const arity = declarationMemberArity(declarationNode, context.sup.id);
|
|
41
|
+
if (arity === undefined)
|
|
39
42
|
return;
|
|
40
|
-
const arity = (parameters.namedChildren ?? []).filter((child) => child.type !== "comment").length;
|
|
41
43
|
const node = context.nodes.get(id);
|
|
42
44
|
if (node)
|
|
43
45
|
node.memberArity = arity;
|
|
44
46
|
}
|
|
47
|
+
function cppShapeNode(node) {
|
|
48
|
+
return node.type === "function_declarator" ? node : (findFirstNodeByType(node, "function_declarator") ?? node);
|
|
49
|
+
}
|
|
50
|
+
function mergeCppCallableShapes(...shapes) {
|
|
51
|
+
const present = shapes.filter((shape) => !!shape);
|
|
52
|
+
if (!present.length)
|
|
53
|
+
return undefined;
|
|
54
|
+
let min = present[0].minArity;
|
|
55
|
+
let max = present[0].maxArity;
|
|
56
|
+
for (const shape of present.slice(1)) {
|
|
57
|
+
min = Math.min(min, shape.minArity);
|
|
58
|
+
if (max === null || shape.maxArity === null)
|
|
59
|
+
max = null;
|
|
60
|
+
else
|
|
61
|
+
max = Math.max(max, shape.maxArity);
|
|
62
|
+
}
|
|
63
|
+
return { min, max };
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Accepted explicit-argument range of a member declaration from the shared callable
|
|
67
|
+
* facts (default parameters, varargs, explicit receivers), or undefined when the
|
|
68
|
+
* declaration shape is unknown or the language does not select members by call arity.
|
|
69
|
+
* The binding follows the call form member lookup resolves (see `memberLookupBinding`).
|
|
70
|
+
*/
|
|
71
|
+
function acceptedMemberArityRange(context, declarationNode, source) {
|
|
72
|
+
if (!supportsReceiverMemberOverloads(context.sup.id))
|
|
73
|
+
return undefined;
|
|
74
|
+
const arity = getCallableArity({
|
|
75
|
+
languageId: context.sup.id,
|
|
76
|
+
source,
|
|
77
|
+
declaration: declarationNode,
|
|
78
|
+
binding: memberLookupBinding(context.sup.id),
|
|
79
|
+
});
|
|
80
|
+
return arity ? { min: arity.minArgs, max: arity.maxArgs } : undefined;
|
|
81
|
+
}
|
|
82
|
+
function recordMemberLookupIdentity(context, definitionId, memberId, memberScope, arityRange) {
|
|
83
|
+
context.receiverMemberScopes.set(definitionId, memberScope);
|
|
84
|
+
if (memberId !== definitionId)
|
|
85
|
+
context.receiverMemberScopes.set(memberId, memberScope);
|
|
86
|
+
if (!arityRange)
|
|
87
|
+
return;
|
|
88
|
+
context.receiverMemberArities.set(definitionId, arityRange);
|
|
89
|
+
if (memberId !== definitionId)
|
|
90
|
+
context.receiverMemberArities.set(memberId, arityRange);
|
|
91
|
+
}
|
|
45
92
|
function recordDefEdge(context, fromId, target, label, siteNode) {
|
|
46
93
|
const toId = ensureNode(context, target);
|
|
47
94
|
return context.recordEdge(fromId, toId, label, siteNode ? { file: context.moduleEntry.file, range: toRange(siteNode) } : undefined);
|
|
@@ -56,10 +103,26 @@ function tryResolveChain(context, node, fromId, label = "uses") {
|
|
|
56
103
|
}
|
|
57
104
|
/** Records an edge for a resolvable target node. Returns whether a target was resolved. */
|
|
58
105
|
function tryResolveNode(context, node, fromId, label) {
|
|
59
|
-
if (
|
|
106
|
+
if (context.sup.id === "cpp" && node.type === "qualified_identifier") {
|
|
107
|
+
const name = cppQualifiedNameSegments(node, context.source).join("::");
|
|
108
|
+
const target = context.resolveIdentifier(name, node);
|
|
109
|
+
if (target) {
|
|
110
|
+
recordDefEdge(context, fromId, target, label, node);
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
if (isIdentifierType(context.sup, node.type) ||
|
|
116
|
+
node.type === "type_identifier" ||
|
|
117
|
+
(context.sup.id === "csharp" && (node.type === "qualified_name" || node.type === "alias_qualified_name")) ||
|
|
118
|
+
(context.sup.id === "cpp" && (node.type === "operator_name" || node.type === "destructor_name"))) {
|
|
60
119
|
const name = sliceText(node, context.source);
|
|
61
120
|
const target = context.resolveIdentifier(name, node);
|
|
62
121
|
if (target) {
|
|
122
|
+
// Zig struct declarations require an explicit type receiver (`Self.helper()`).
|
|
123
|
+
// An unqualified identifier is not a call to a member of its enclosing struct.
|
|
124
|
+
if (label === "calls" && context.sup.id === "zig" && target.isMember)
|
|
125
|
+
return false;
|
|
63
126
|
recordDefEdge(context, fromId, target, label, node);
|
|
64
127
|
return true;
|
|
65
128
|
}
|
|
@@ -69,6 +132,32 @@ function tryResolveNode(context, node, fromId, label) {
|
|
|
69
132
|
}
|
|
70
133
|
return false;
|
|
71
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Rust macro arguments stay unparsed token trees, so `println!("{}", greet())` never forms a
|
|
137
|
+
* call node even though goto resolves the identifier. An `identifier` directly followed by a
|
|
138
|
+
* `(` group is a call in the argument list, resolved like a bare call. The macro's own name
|
|
139
|
+
* sits outside the token tree and is never a call; a `!` gap marks a nested macro invocation,
|
|
140
|
+
* and a `.` or `::` gap marks a member or path receiver that raw tokens cannot prove, matching
|
|
141
|
+
* navigation's refusals inside token trees.
|
|
142
|
+
*/
|
|
143
|
+
function recordRustMacroArgumentCalls(context, tokenTree, fromId) {
|
|
144
|
+
if (context.sup.id !== "rust" || !rustTokenTreeHoldsExpressions(tokenTree))
|
|
145
|
+
return;
|
|
146
|
+
const children = tokenTree.namedChildren;
|
|
147
|
+
for (let index = 0; index + 1 < children.length; index += 1) {
|
|
148
|
+
const name = children[index];
|
|
149
|
+
const args = children[index + 1];
|
|
150
|
+
if (name.type !== "identifier" || args.type !== "token_tree")
|
|
151
|
+
continue;
|
|
152
|
+
if (!args.text.startsWith("("))
|
|
153
|
+
continue;
|
|
154
|
+
if (context.source.slice(name.endIndex, args.startIndex).trim())
|
|
155
|
+
continue;
|
|
156
|
+
if (rustTokenTreeNameFollowsSeparator(name))
|
|
157
|
+
continue;
|
|
158
|
+
tryResolveNode(context, name, fromId, "calls");
|
|
159
|
+
}
|
|
160
|
+
}
|
|
72
161
|
function getCallTarget(node) {
|
|
73
162
|
const explicitTarget = node.childForFieldName("function") ??
|
|
74
163
|
node.childForFieldName("callee") ??
|
|
@@ -76,6 +165,11 @@ function getCallTarget(node) {
|
|
|
76
165
|
node.childForFieldName("method") ??
|
|
77
166
|
node.childForFieldName("member") ??
|
|
78
167
|
node.childForFieldName("expression");
|
|
168
|
+
// tree-sitter-typescript parses `await f<T>(x)` as a call whose callee is `await f`; the awaited
|
|
169
|
+
// value is the call's result, so the callee is `f`.
|
|
170
|
+
if (explicitTarget?.type === "await_expression" && node.childForFieldName("type_arguments")) {
|
|
171
|
+
return explicitTarget.namedChildren[0] ?? null;
|
|
172
|
+
}
|
|
79
173
|
if (explicitTarget)
|
|
80
174
|
return explicitTarget;
|
|
81
175
|
// Kotlin and Swift calls name no callee field, so the sole non-argument child is it.
|
|
@@ -83,11 +177,16 @@ function getCallTarget(node) {
|
|
|
83
177
|
return nonArgumentChildren.length === 1 ? (nonArgumentChildren[0] ?? null) : null;
|
|
84
178
|
}
|
|
85
179
|
function getNewTarget(node) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
180
|
+
const field = node.childForFieldName("constructor") ?? node.childForFieldName("type") ?? node.childForFieldName("name");
|
|
181
|
+
if (field)
|
|
182
|
+
return field;
|
|
183
|
+
// PHP `new Base()` has no type field; the type is a `name` / `qualified_name` child and
|
|
184
|
+
// `child(0)` is the `new` keyword, which is not a type.
|
|
185
|
+
const namedType = node.namedChildren.find((child) => child.type === "type_identifier" ||
|
|
186
|
+
child.type === "name" ||
|
|
187
|
+
child.type === "qualified_name" ||
|
|
188
|
+
child.type === "relative_name");
|
|
189
|
+
return namedType ?? node.child(0);
|
|
91
190
|
}
|
|
92
191
|
export function emitPythonDecoratorEdges(context, rootNode) {
|
|
93
192
|
if (context.sup.id !== "python")
|
|
@@ -151,34 +250,219 @@ export function emitPythonDecoratorEdges(context, rootNode) {
|
|
|
151
250
|
};
|
|
152
251
|
addDecoratorUses(rootNode);
|
|
153
252
|
}
|
|
154
|
-
|
|
253
|
+
/** Whether a function declaration can participate in class member lookup and ownership. */
|
|
254
|
+
function isClassMemberFunction(fn) {
|
|
255
|
+
return fn.node.type !== "local_function_statement";
|
|
256
|
+
}
|
|
257
|
+
export async function emitMemberOwnershipEdges(context, functionNodes, classNodes) {
|
|
155
258
|
for (const fn of functionNodes) {
|
|
156
|
-
const
|
|
157
|
-
if (!
|
|
259
|
+
const owner = await memberOwner(context, fn, classNodes);
|
|
260
|
+
if (!owner)
|
|
261
|
+
continue;
|
|
262
|
+
const outOfLineDeclaration = owner.cppOutOfLine
|
|
263
|
+
? await cppOutOfLineMemberDeclaration(context, fn, owner.def)
|
|
264
|
+
: null;
|
|
265
|
+
const definitionId = ensureNode(context, fn.def);
|
|
266
|
+
let memberDef = fn.def;
|
|
267
|
+
if (outOfLineDeclaration) {
|
|
268
|
+
const declarationModule = context.index.byFile.get(fileIdentityKey(owner.def.file));
|
|
269
|
+
const declarationDef = declarationModule?.locals.find((candidate) => candidate.localName === fn.def.localName &&
|
|
270
|
+
candidate.kind === fn.def.kind &&
|
|
271
|
+
candidate.range.start.index === outOfLineDeclaration.nameNode.startIndex &&
|
|
272
|
+
candidate.range.end.index === outOfLineDeclaration.nameNode.endIndex);
|
|
273
|
+
if (declarationDef)
|
|
274
|
+
memberDef = declarationDef;
|
|
275
|
+
}
|
|
276
|
+
const memberId = ensureNode(context, memberDef);
|
|
277
|
+
if (definitionId !== memberId)
|
|
278
|
+
context.nodeAliases.set(definitionId, memberId);
|
|
279
|
+
if (context.sup.id === "java" || context.sup.id === "kotlin" || context.sup.id === "swift") {
|
|
280
|
+
const declarationNode = outOfLineDeclaration?.node ?? fn.node;
|
|
281
|
+
const hidden = context.sup.id === "swift"
|
|
282
|
+
? isSwiftFileHiddenSharedOwnerMember(context.sup.id, declarationNode)
|
|
283
|
+
: !isExportedDeclaration(context.sup.id, declarationNode);
|
|
284
|
+
if (hidden) {
|
|
285
|
+
context.fileHiddenMemberIds.add(memberId);
|
|
286
|
+
if (definitionId !== memberId)
|
|
287
|
+
context.fileHiddenMemberIds.add(definitionId);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
markImplementationTarget(context, memberId, outOfLineDeclaration?.node ?? fn.node, outOfLineDeclaration?.source ?? context.source, fn.def);
|
|
291
|
+
const arityNode = outOfLineDeclaration?.node ?? fn.node;
|
|
292
|
+
markMemberArity(context, definitionId, arityNode);
|
|
293
|
+
if (memberId !== definitionId)
|
|
294
|
+
markMemberArity(context, memberId, arityNode);
|
|
295
|
+
const memberScope = memberScopeForDefinition(context, fn, owner.cppOutOfLine, outOfLineDeclaration);
|
|
296
|
+
const arityRange = context.sup.id === "cpp"
|
|
297
|
+
? mergeCppCallableShapes(cppCallableShapeForNode(cppShapeNode(fn.node)), outOfLineDeclaration ? cppCallableShapeForNode(cppShapeNode(outOfLineDeclaration.node)) : undefined)
|
|
298
|
+
: acceptedMemberArityRange(context, arityNode, outOfLineDeclaration?.source ?? context.source);
|
|
299
|
+
recordMemberLookupIdentity(context, definitionId, memberId, memberScope, arityRange);
|
|
300
|
+
recordDefEdge(context, definitionId, owner.def, "member_of");
|
|
301
|
+
await emitSharedOwnerMembershipEdges(context, owner, definitionId, fn.node);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
/** Owner def of a shared-owner container, matched by its declaration name range. */
|
|
305
|
+
function sharedOwnerPeerDef(peer, callerFile) {
|
|
306
|
+
const nameNode = peer.container.childForFieldName("name");
|
|
307
|
+
if (!nameNode)
|
|
308
|
+
return null;
|
|
309
|
+
const def = peer.module.locals.find((local) => declaresMembers(local) && local.range.start.index === nameNode.startIndex);
|
|
310
|
+
if (!def)
|
|
311
|
+
return null;
|
|
312
|
+
if (!callerFile)
|
|
313
|
+
return { def, isExtension: isSwiftExtensionContainer(peer.container, peer.context.source) };
|
|
314
|
+
const visibleMemberIds = [];
|
|
315
|
+
for (const local of peer.module.locals) {
|
|
316
|
+
const start = local.range.start.index;
|
|
317
|
+
if (start === undefined || start < peer.container.startIndex || start >= peer.container.endIndex)
|
|
318
|
+
continue;
|
|
319
|
+
const name = peer.context.tree.rootNode.descendantForIndex(start, start);
|
|
320
|
+
if (!isDirectKeywordMemberDeclaration(name, peer.container))
|
|
321
|
+
continue;
|
|
322
|
+
if (isSwiftCrossFileHiddenSharedOwnerMember("swift", callerFile, peer.file, name))
|
|
323
|
+
continue;
|
|
324
|
+
visibleMemberIds.push(defNodeId(local));
|
|
325
|
+
}
|
|
326
|
+
return { def, isExtension: isSwiftExtensionContainer(peer.container, peer.context.source), visibleMemberIds };
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* C# partial declarations and unconstrained Swift extensions share type membership
|
|
330
|
+
* across proven peers. Constrained Swift extensions instead retain their own members
|
|
331
|
+
* and record only the peer members their lexical owner can use. This keeps unproven
|
|
332
|
+
* receivers from inheriting constrained members while preserving calls inside the
|
|
333
|
+
* extension. Same-named types in other namespaces or paths remain separate.
|
|
334
|
+
*/
|
|
335
|
+
async function emitSharedOwnerMembershipEdges(context, owner, definitionId, memberNode) {
|
|
336
|
+
const container = owner.container;
|
|
337
|
+
if (!container)
|
|
338
|
+
return;
|
|
339
|
+
if (context.sup.id !== "csharp" && context.sup.id !== "swift")
|
|
340
|
+
return;
|
|
341
|
+
const isExtension = context.sup.id === "swift" && isSwiftExtensionContainer(container, context.source);
|
|
342
|
+
// Swift base types already own their full member set; only extensions and C#
|
|
343
|
+
// partials reach across owner declarations. Non-partial C# containers resolve
|
|
344
|
+
// to no peers inside the shared-owner relation.
|
|
345
|
+
// Constrained extensions can use nominal members but cannot donate their own
|
|
346
|
+
// members to every instance of the nominal type without proving the where clause.
|
|
347
|
+
const constrained = context.sup.id === "swift" && isSwiftConstrainedExtension(container, context.source);
|
|
348
|
+
if (constrained && context.sharedOwnerAccessibleMembers.has(defNodeId(owner.def)))
|
|
349
|
+
return;
|
|
350
|
+
const cacheKey = `${fileIdentityKey(context.moduleEntry.file)}\u0000${container.startIndex}\u0000${container.endIndex}`;
|
|
351
|
+
let peers = context.sharedOwnerPeers.get(cacheKey);
|
|
352
|
+
if (!peers) {
|
|
353
|
+
peers = resolveSharedOwnerContainers({
|
|
354
|
+
index: context.index,
|
|
355
|
+
ownerFile: context.moduleEntry.file,
|
|
356
|
+
ownerContainer: container,
|
|
357
|
+
ownerSource: context.source,
|
|
358
|
+
languageId: context.sup.id,
|
|
359
|
+
}).then((resolved) => resolved
|
|
360
|
+
.map((peer) => sharedOwnerPeerDef(peer, constrained ? context.moduleEntry.file : undefined))
|
|
361
|
+
.filter((peer) => !!peer));
|
|
362
|
+
context.sharedOwnerPeers.set(cacheKey, peers);
|
|
363
|
+
}
|
|
364
|
+
const peerDefs = await peers;
|
|
365
|
+
if (constrained) {
|
|
366
|
+
const ownerId = defNodeId(owner.def);
|
|
367
|
+
const accessible = context.sharedOwnerAccessibleMembers.get(ownerId) ?? new Set();
|
|
368
|
+
for (const peer of peerDefs)
|
|
369
|
+
for (const memberId of peer.visibleMemberIds ?? [])
|
|
370
|
+
accessible.add(memberId);
|
|
371
|
+
context.sharedOwnerAccessibleMembers.set(ownerId, accessible);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
for (const peer of peerDefs) {
|
|
375
|
+
if (isSwiftCrossFileHiddenSharedOwnerMember(context.sup.id, peer.def.file, context.moduleEntry.file, memberNode)) {
|
|
158
376
|
continue;
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
377
|
+
}
|
|
378
|
+
recordDefEdge(context, definitionId, peer.def, "member_of");
|
|
379
|
+
}
|
|
380
|
+
if (isExtension) {
|
|
381
|
+
const anchor = peerDefs.find((peer) => !peer.isExtension);
|
|
382
|
+
context.sharedOwnerAnchors.set(defNodeId(owner.def), defNodeId(anchor ? anchor.def : owner.def));
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
if (context.sup.id === "csharp" && peerDefs.length) {
|
|
386
|
+
const representative = selectCsharpPartialRepresentative([owner.def, ...peerDefs.map((peer) => peer.def)]);
|
|
387
|
+
context.sharedOwnerAnchors.set(defNodeId(owner.def), defNodeId(representative));
|
|
163
388
|
}
|
|
164
389
|
}
|
|
165
|
-
|
|
166
|
-
|
|
390
|
+
function memberScopeForDefinition(context, fn, cppOutOfLine, outOfLineDeclaration) {
|
|
391
|
+
if (outOfLineDeclaration) {
|
|
392
|
+
return declarationNodeIsStatic(outOfLineDeclaration.node, outOfLineDeclaration.source) ? "static" : "instance";
|
|
393
|
+
}
|
|
394
|
+
if (cppOutOfLine)
|
|
395
|
+
return "any";
|
|
396
|
+
// Kotlin has no `static` keyword; a member of a `companion object` or a named `object`
|
|
397
|
+
// declaration is the language's static-equivalent mechanism (reachable as `Outer.member()`).
|
|
398
|
+
if (declarationIsStaticEquivalent(context.sup.id, fn.node))
|
|
399
|
+
return "static";
|
|
400
|
+
const declarationNode = fn.node.parent?.type === "public_field_definition" || fn.node.parent?.type === "field_definition"
|
|
401
|
+
? fn.node.parent
|
|
402
|
+
: fn.node;
|
|
403
|
+
return declarationNodeIsStatic(declarationNode, context.source) ? "static" : "instance";
|
|
404
|
+
}
|
|
405
|
+
/** Lexical type body, named Go receiver type, or named C++ out-of-line owner. */
|
|
406
|
+
async function memberOwner(context, fn, classNodes) {
|
|
407
|
+
if (!isClassMemberFunction(fn))
|
|
408
|
+
return null;
|
|
167
409
|
if (context.sup.id === "go" && fn.node.type === "method_declaration") {
|
|
168
|
-
|
|
410
|
+
const def = goMethodReceiverTypeDef(context, fn.node);
|
|
411
|
+
return def ? { def, container: null, cppOutOfLine: false } : null;
|
|
169
412
|
}
|
|
170
413
|
const owners = classNodes
|
|
171
414
|
.filter((candidate) => candidate.node.startIndex <= fn.node.startIndex && candidate.node.endIndex >= fn.node.endIndex)
|
|
172
415
|
.sort((left, right) => left.node.endIndex - left.node.startIndex - (right.node.endIndex - right.node.startIndex));
|
|
173
|
-
if (owners[0]?.def)
|
|
174
|
-
|
|
416
|
+
if (owners[0]?.def) {
|
|
417
|
+
if (!isDirectKeywordMemberDeclaration(fn.node, owners[0].node))
|
|
418
|
+
return null;
|
|
419
|
+
return { def: owners[0].def, container: owners[0].node, cppOutOfLine: false };
|
|
420
|
+
}
|
|
421
|
+
if (context.sup.id === "cpp") {
|
|
422
|
+
const ownerPath = cppOutOfLineOwnerPath(fn.node, context.source, context.sup);
|
|
423
|
+
const def = ownerPath
|
|
424
|
+
? await resolveCppQualifiedMemberContainer(context.index, context.moduleEntry, ownerPath, context.loadParsedFile)
|
|
425
|
+
: null;
|
|
426
|
+
return def ? { def, container: null, cppOutOfLine: true } : null;
|
|
427
|
+
}
|
|
428
|
+
if (context.sup.id === "kotlin") {
|
|
429
|
+
// `fun Widget.describe()` is a top-level declaration outside Widget's own body, so it is
|
|
430
|
+
// never one of `owners` above; its receiver-type prefix is the only proof of ownership.
|
|
431
|
+
const receiverTypeNode = kotlinExtensionReceiverTypeNode(fn.node, context.sup);
|
|
432
|
+
if (!receiverTypeNode)
|
|
433
|
+
return null;
|
|
434
|
+
const def = resolveNamedType(context, sliceText(receiverTypeNode, context.source), receiverTypeNode);
|
|
435
|
+
return def ? { def, container: null, cppOutOfLine: false } : null;
|
|
436
|
+
}
|
|
437
|
+
if (context.sup.id === "rust") {
|
|
438
|
+
// A Rust impl method sits beside its self type, not inside it (like Go's receiver type);
|
|
439
|
+
// the `impl` block's self type is the owner, and only direct impl members count.
|
|
440
|
+
const container = nearestMemberContainer(fn.node);
|
|
441
|
+
if (!container || container.type !== "impl_item" || !isDirectKeywordMemberDeclaration(fn.node, container)) {
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
const nameNode = rustImplSelfTypeNode(container, context.sup);
|
|
445
|
+
const def = nameNode ? resolveNamedType(context, sliceText(nameNode, context.source), nameNode) : null;
|
|
446
|
+
return def ? { def, container, cppOutOfLine: false } : null;
|
|
447
|
+
}
|
|
175
448
|
if (context.sup.id !== "zig")
|
|
176
449
|
return null;
|
|
177
450
|
const container = nearestMemberContainer(fn.node);
|
|
178
451
|
if (container?.type !== "struct_declaration")
|
|
179
452
|
return null;
|
|
180
453
|
const name = container.parent?.namedChildren.find((child) => child.type === "identifier");
|
|
181
|
-
|
|
454
|
+
const def = name ? resolveNamedType(context, sliceText(name, context.source), name) : null;
|
|
455
|
+
return def ? { def, container, cppOutOfLine: false } : null;
|
|
456
|
+
}
|
|
457
|
+
async function cppOutOfLineMemberDeclaration(context, fn, ownerDef) {
|
|
458
|
+
const parsed = await context.loadParsedFile(ownerDef.file);
|
|
459
|
+
const startIndex = ownerDef.range.start.index;
|
|
460
|
+
const endIndex = ownerDef.range.end.index;
|
|
461
|
+
if (!parsed || startIndex === undefined || endIndex === undefined)
|
|
462
|
+
return null;
|
|
463
|
+
const ownerNameNode = parsed.tree.rootNode.descendantForIndex(startIndex, endIndex);
|
|
464
|
+
const declaration = cppOutOfLineMemberDeclarationNode(fn.node, fn.def.localName, ownerNameNode, parsed.source, context.sup);
|
|
465
|
+
return declaration ? { node: declaration.node, nameNode: declaration.nameNode, source: parsed.source } : null;
|
|
182
466
|
}
|
|
183
467
|
/** Receiver type of `func (b *T) M()` / `func (b T) M()`, unwrapped through pointers. */
|
|
184
468
|
function goMethodReceiverTypeDef(context, methodNode) {
|
|
@@ -208,16 +492,157 @@ function unwrapGoNamedType(node) {
|
|
|
208
492
|
return current?.type === "type_identifier" ? current : null;
|
|
209
493
|
}
|
|
210
494
|
/** Type-like defs only, so a PHP `use function` alias cannot steal `Example::m()`. */
|
|
211
|
-
function resolveNamedType(context, name, node) {
|
|
495
|
+
function resolveNamedType(context, name, node, rubyConstructed = false) {
|
|
496
|
+
if (context.sup.id === "go" && context.optionalMemberTypes.has(node.type)) {
|
|
497
|
+
const qualified = context.resolveMemberChainTarget(node);
|
|
498
|
+
return qualified && declaresMembers(qualified) ? qualified : null;
|
|
499
|
+
}
|
|
212
500
|
const target = context.resolveIdentifier(name, node);
|
|
213
501
|
if (target && declaresMembers(target))
|
|
214
502
|
return target;
|
|
215
503
|
// A parameter/annotation type name is a closer scope binding than the class it names.
|
|
216
504
|
const normalized = context.sup.normalizeIdentifier(name);
|
|
217
505
|
const typed = context.moduleEntry.locals.filter((local) => context.sup.normalizeIdentifier(local.localName) === normalized && declaresMembers(local));
|
|
218
|
-
|
|
506
|
+
if (typed.length === 1)
|
|
507
|
+
return typed[0];
|
|
508
|
+
const imported = context.aliasToTargetDef.get(name);
|
|
509
|
+
if (imported && declaresMembers(imported))
|
|
510
|
+
return imported;
|
|
511
|
+
// Only a constructed `Klass.new` type uses Ruby's bare-constant visibility, and
|
|
512
|
+
// only when this file does not already have several classes of that name.
|
|
513
|
+
// Inheritance names stay on resolveIdentifier so an unproven superclass is not invented.
|
|
514
|
+
if (!rubyConstructed || typed.length > 1)
|
|
515
|
+
return null;
|
|
516
|
+
return resolveRubyVisibleConstant(context.index, context.moduleEntry, context.sup, name);
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Collection can attach an arrow to any same-name local when the binding site is
|
|
520
|
+
* not that local (`obj.helper = () => 1` beside `const helper = 1`). Prove that
|
|
521
|
+
* this function is the value of `fn.def` itself: the declarator name is that
|
|
522
|
+
* definition, or an identifier assignment resolves to it. Member and pattern
|
|
523
|
+
* left-hand sides do not prove a local binding.
|
|
524
|
+
*/
|
|
525
|
+
function provesCallableBinding(context, fn) {
|
|
526
|
+
const parent = fn.node.parent;
|
|
527
|
+
if (!parent)
|
|
528
|
+
return false;
|
|
529
|
+
const start = fn.def.range.start.index;
|
|
530
|
+
const end = fn.def.range.end.index;
|
|
531
|
+
if (start === undefined || end === undefined)
|
|
532
|
+
return false;
|
|
533
|
+
const sameNodeRange = (candidate) => !!candidate && candidate.startIndex === fn.node.startIndex && candidate.endIndex === fn.node.endIndex;
|
|
534
|
+
if ((parent.type === "variable_declarator" ||
|
|
535
|
+
parent.type === "public_field_definition" ||
|
|
536
|
+
parent.type === "field_definition") &&
|
|
537
|
+
sameNodeRange(parent.childForFieldName("value"))) {
|
|
538
|
+
const bindingName = parent.childForFieldName("name") ?? parent.childForFieldName("property");
|
|
539
|
+
return (!!bindingName &&
|
|
540
|
+
(isIdentifierType(context.sup, bindingName.type) || context.propertyIdentifierTypes.includes(bindingName.type)) &&
|
|
541
|
+
bindingName.startIndex === start &&
|
|
542
|
+
bindingName.endIndex === end);
|
|
543
|
+
}
|
|
544
|
+
if (parent.type === "assignment_expression" && sameNodeRange(parent.childForFieldName("right"))) {
|
|
545
|
+
const left = parent.childForFieldName("left");
|
|
546
|
+
if (!left || !isIdentifierType(context.sup, left.type))
|
|
547
|
+
return false;
|
|
548
|
+
const resolved = context.resolveIdentifier(sliceText(left, context.source), left);
|
|
549
|
+
return !!resolved && defNodeId(resolved) === defNodeId(fn.def);
|
|
550
|
+
}
|
|
551
|
+
return false;
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Whether a resolved member can be a `calls` target: a function, a class, or a binding the
|
|
555
|
+
* declaration pass proved holds a function. A plain value (`export const value = 1`) is not.
|
|
556
|
+
*/
|
|
557
|
+
/** A JS/TS class is not callable without `new`; construction is an `instantiates` edge instead. */
|
|
558
|
+
function isCallTarget(context, def) {
|
|
559
|
+
return def.kind === SymbolKind.Function || !!context.nodes.get(defNodeId(def))?.callable;
|
|
219
560
|
}
|
|
220
|
-
|
|
561
|
+
/**
|
|
562
|
+
* A member of a proven same-file TS namespace receiver, selected with navigation's container and
|
|
563
|
+
* overload rules. `undefined` means the receiver is not such a namespace.
|
|
564
|
+
*/
|
|
565
|
+
function resolveTypeScriptNamespaceMember(context, call, receiverNode, property) {
|
|
566
|
+
if ((context.sup.id !== "ts" && context.sup.id !== "tsx") || !isIdentifierType(context.sup, receiverNode.type))
|
|
567
|
+
return undefined;
|
|
568
|
+
const receiver = context.resolveIdentifier(sliceText(receiverNode, context.source), receiverNode);
|
|
569
|
+
if (!receiver || fileIdentityKey(receiver.file) !== fileIdentityKey(context.moduleEntry.file))
|
|
570
|
+
return undefined;
|
|
571
|
+
const member = sliceText(property, context.source);
|
|
572
|
+
const candidates = findTypeScriptNamespaceMemberCandidates(context.moduleEntry.locals, member, receiver, context);
|
|
573
|
+
if (!candidates.length)
|
|
574
|
+
return undefined;
|
|
575
|
+
return typescriptSelectOverloadCandidate({
|
|
576
|
+
group: candidates,
|
|
577
|
+
tree: context.tree,
|
|
578
|
+
definitionOf: (candidate) => candidate,
|
|
579
|
+
declarationOf: (candidate) => {
|
|
580
|
+
const start = candidate.range.start.index ?? 0;
|
|
581
|
+
return context.tree.rootNode.descendantForIndex(start, candidate.range.end.index ?? start).parent;
|
|
582
|
+
},
|
|
583
|
+
source: context.source,
|
|
584
|
+
languageId: context.sup.id,
|
|
585
|
+
argumentCount: getCallArgumentCount({ languageId: context.sup.id, source: context.source, call }),
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
function recordTypeScriptNamespaceCall(context, call, access, fromId) {
|
|
589
|
+
const selected = resolveTypeScriptNamespaceMember(context, call, access.receiver, access.property);
|
|
590
|
+
if (selected === undefined)
|
|
591
|
+
return false;
|
|
592
|
+
if (selected && isCallTarget(context, selected))
|
|
593
|
+
recordDefEdge(context, fromId, selected, "calls", access.property);
|
|
594
|
+
return true;
|
|
595
|
+
}
|
|
596
|
+
/** `new N.C()` on a same-file TS namespace constructs the namespace's class. */
|
|
597
|
+
function recordTypeScriptNamespaceConstruction(context, node, target, fromId) {
|
|
598
|
+
if (!isMemberAccessNode(context.sup, target))
|
|
599
|
+
return false;
|
|
600
|
+
const { object, property } = getMemberAccessParts(context.sup, target);
|
|
601
|
+
if (!object || !property)
|
|
602
|
+
return false;
|
|
603
|
+
const selected = resolveTypeScriptNamespaceMember(context, node, object, property);
|
|
604
|
+
if (selected === undefined)
|
|
605
|
+
return false;
|
|
606
|
+
if (selected?.kind === SymbolKind.Class)
|
|
607
|
+
recordDefEdge(context, fromId, selected, "instantiates", property);
|
|
608
|
+
return true;
|
|
609
|
+
}
|
|
610
|
+
/** A member call on a `typeof import("spec")` binding names that module's export. */
|
|
611
|
+
function recordImportTypeCall(context, access, fromId) {
|
|
612
|
+
if (!isJsTsLanguage(context.sup.id))
|
|
613
|
+
return false;
|
|
614
|
+
const importType = receiverConstructorExpression(access.receiver, context.source, context.sup);
|
|
615
|
+
const specifier = importType?.type === "type_query" ? importTypeQuerySpecifier(importType) : null;
|
|
616
|
+
if (!specifier)
|
|
617
|
+
return false;
|
|
618
|
+
const member = sliceText(access.property, context.source);
|
|
619
|
+
const target = resolveImportTypeMember(context.index, context.moduleEntry.file, specifier, member);
|
|
620
|
+
if (target && isCallTarget(context, target))
|
|
621
|
+
recordDefEdge(context, fromId, target, "calls", access.property);
|
|
622
|
+
return true;
|
|
623
|
+
}
|
|
624
|
+
/** Swift `Foo()` / `Worker(name:)` is construction. Record `instantiates` and skip the call path. */
|
|
625
|
+
function recordSwiftCapitalizedConstruction(context, node, fromId) {
|
|
626
|
+
if (context.sup.id !== "swift")
|
|
627
|
+
return false;
|
|
628
|
+
const constructed = constructionTypeName(node, context.source, context.sup);
|
|
629
|
+
if (!constructed)
|
|
630
|
+
return false;
|
|
631
|
+
const name = sliceText(constructed, context.source);
|
|
632
|
+
// An inherited method can share a type's name; a deferred member lookup decides after the walk.
|
|
633
|
+
if (context.resolveName(name, constructed)?.status === "deferred")
|
|
634
|
+
return false;
|
|
635
|
+
const target = context.resolveIdentifier(name, constructed);
|
|
636
|
+
if (!target || !declaresMembers(target))
|
|
637
|
+
return false;
|
|
638
|
+
recordDefEdge(context, fromId, target, "instantiates", constructed);
|
|
639
|
+
return true;
|
|
640
|
+
}
|
|
641
|
+
export async function emitFunctionBodyEdges(context, functionNodes) {
|
|
642
|
+
const qualifiedConstructionTargets = [];
|
|
643
|
+
// C# calls through a dotted receiver (`P.Mix.M()`, `Imported.Outer.Inner.M()`): navigation's
|
|
644
|
+
// member-access resolution decides the target after the walk, so both consumers agree.
|
|
645
|
+
const dottedReceiverCalls = [];
|
|
221
646
|
const callNodeTypes = new Set([
|
|
222
647
|
"call_expression",
|
|
223
648
|
"call",
|
|
@@ -235,15 +660,21 @@ export function emitFunctionBodyEdges(context, functionNodes) {
|
|
|
235
660
|
"struct_expression",
|
|
236
661
|
"composite_literal",
|
|
237
662
|
]);
|
|
238
|
-
// Receiver typing and lexical member lookup are only
|
|
239
|
-
|
|
663
|
+
// Receiver typing and lexical member lookup are initialized only for receiver calls.
|
|
664
|
+
const receiverProofs = new Map();
|
|
665
|
+
const hasLexicalBinding = (callee) => {
|
|
666
|
+
const scope = context.index.scopeCache.get(fileIdentityKey(context.moduleEntry.file));
|
|
667
|
+
return !scope || !!findClosestScopeBinding(scope, sliceText(callee, context.source), callee, context.sup);
|
|
668
|
+
};
|
|
240
669
|
let membersByContainer;
|
|
241
670
|
const lexicalMembers = (container) => {
|
|
242
671
|
if (!membersByContainer) {
|
|
243
672
|
membersByContainer = new Map();
|
|
244
673
|
for (const candidate of functionNodes) {
|
|
674
|
+
if (!isClassMemberFunction(candidate))
|
|
675
|
+
continue;
|
|
245
676
|
const owner = nearestMemberContainer(candidate.node);
|
|
246
|
-
if (!owner)
|
|
677
|
+
if (!owner || !isDirectKeywordMemberDeclaration(candidate.node, owner))
|
|
247
678
|
continue;
|
|
248
679
|
const members = membersByContainer.get(owner.startIndex);
|
|
249
680
|
if (members)
|
|
@@ -254,10 +685,21 @@ export function emitFunctionBodyEdges(context, functionNodes) {
|
|
|
254
685
|
}
|
|
255
686
|
return membersByContainer.get(container.startIndex) ?? [];
|
|
256
687
|
};
|
|
257
|
-
const receiverProofs = new Map();
|
|
258
688
|
for (const fn of functionNodes) {
|
|
689
|
+
const phpCaseInsensitive = context.sup.id === "php";
|
|
259
690
|
const fromId = ensureNode(context, fn.def);
|
|
691
|
+
const provenNode = context.nodes.get(fromId);
|
|
692
|
+
// Function-valued bindings (`const helper = () => 1`) keep their `variable` kind;
|
|
693
|
+
// the callable metadata records that this binding was proven to hold a function.
|
|
694
|
+
if (provenNode && provenNode.kind !== "function" && provesCallableBinding(context, fn)) {
|
|
695
|
+
provenNode.callable = true;
|
|
696
|
+
context.noteCallableName(fn.name, context.sup.id === "php");
|
|
697
|
+
}
|
|
260
698
|
const seenAliases = new Set();
|
|
699
|
+
// Calls whose target needs an async member lookup (C++ out-of-line owner members, included
|
|
700
|
+
// C/C++ declarations of one callable); settled after the walk.
|
|
701
|
+
const deferredCalls = [];
|
|
702
|
+
const qualifiedCppCalls = [];
|
|
261
703
|
const nestedFunctions = new Set(functionNodes
|
|
262
704
|
.filter((candidate) => candidate.node !== fn.node &&
|
|
263
705
|
candidate.node.startIndex >= fn.node.startIndex &&
|
|
@@ -267,11 +709,32 @@ export function emitFunctionBodyEdges(context, functionNodes) {
|
|
|
267
709
|
if (context.membersOnly || !isIdentifierType(context.sup, node.type))
|
|
268
710
|
return;
|
|
269
711
|
const name = sliceText(node, context.source);
|
|
712
|
+
if (context.sup.id === "php") {
|
|
713
|
+
// Resolve the occurrence's namespace; a plain-name map cannot distinguish PHP roles.
|
|
714
|
+
const importType = inferPhpQualifiedReferenceImportType(node) ?? "const";
|
|
715
|
+
const aliasName = importType === "const" ? name : foldPhpIdentifierCase(name);
|
|
716
|
+
const seenKey = `${importType}:${aliasName}`;
|
|
717
|
+
if (seenAliases.has(seenKey))
|
|
718
|
+
return;
|
|
719
|
+
seenAliases.add(seenKey);
|
|
720
|
+
const phpImport = findPhpImportAlias(context.moduleEntry.imports, name, importType);
|
|
721
|
+
if (!phpImport || typeof phpImport.resolved !== "string")
|
|
722
|
+
return;
|
|
723
|
+
const resolved = resolvePhpExportByImportType(context.index, phpImport.resolved, phpImport.imported, importType);
|
|
724
|
+
if (resolved?.kind === "resolved")
|
|
725
|
+
recordDefEdge(context, fromId, resolved.def, "uses");
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
270
728
|
if (seenAliases.has(name))
|
|
271
729
|
return;
|
|
272
730
|
let target = context.aliasToTargetDef.get(name) ?? null;
|
|
273
731
|
if (!target) {
|
|
274
|
-
|
|
732
|
+
// A local variable can shadow a Go package alias (`u := LocalU{}` alongside
|
|
733
|
+
// `import u "pkg"`); aliasToTargetModule is a blind per-file text map, so refuse it
|
|
734
|
+
// here too whenever a closer, non-namespace scope binding owns the name.
|
|
735
|
+
const modFile = context.sup.id === "go" && context.hasNonModuleBinding(name, node)
|
|
736
|
+
? undefined
|
|
737
|
+
: context.aliasToTargetModule.get(name);
|
|
275
738
|
if (modFile) {
|
|
276
739
|
let exportedName = null;
|
|
277
740
|
const parent = node.parent;
|
|
@@ -284,7 +747,7 @@ export function emitFunctionBodyEdges(context, functionNodes) {
|
|
|
284
747
|
}
|
|
285
748
|
if (exportedName) {
|
|
286
749
|
target = context.resolveExportFrom(modFile, exportedName);
|
|
287
|
-
if (!target) {
|
|
750
|
+
if (!target && (context.sup.id !== "go" || isGoExportedMemberName(context.sup.id, exportedName))) {
|
|
288
751
|
const targetModule = context.index.byFile.get(fileIdentityKey(modFile));
|
|
289
752
|
target = (targetModule?.locals ?? []).find((local) => local.localName === exportedName) ?? null;
|
|
290
753
|
}
|
|
@@ -309,17 +772,32 @@ export function emitFunctionBodyEdges(context, functionNodes) {
|
|
|
309
772
|
* alongside the caller resolve here; anything needing another module's members
|
|
310
773
|
* becomes a deferred candidate.
|
|
311
774
|
*/
|
|
312
|
-
const recordReceiverCall = (node, access) => {
|
|
775
|
+
const recordReceiverCall = (node, access, forcedMemberScope) => {
|
|
313
776
|
const memberName = sliceText(access.property, context.source);
|
|
314
|
-
if (!memberName
|
|
777
|
+
if (!memberName)
|
|
315
778
|
return;
|
|
316
|
-
|
|
779
|
+
let binding = classifyReceiver(context.sup, access.receiver, context.source, receiverProofs, fn.node.startIndex, access.accessNode, hasLexicalBinding);
|
|
780
|
+
if (isJsTsLanguage(context.sup.id) && isIdentifierType(context.sup, access.receiver.type)) {
|
|
781
|
+
const receiverName = sliceText(access.receiver, context.source);
|
|
782
|
+
const imported = effectiveExplicitBinding(context.moduleEntry.imports, context.sup.id, (candidate) => candidate.kind === "namespace" && candidate.localNS === receiverName, access.receiver.startIndex) ?? innermostNamespaceImport(context.moduleEntry.imports, receiverName, access.receiver);
|
|
783
|
+
if (imported?.mechanism === "cjs" && typeof imported.resolved === "string") {
|
|
784
|
+
const value = cjsRequireValueBinding(context.index, imported.resolved);
|
|
785
|
+
const lexical = value ? context.resolveIdentifier(receiverName, access.receiver) : null;
|
|
786
|
+
if (!value || !declaresMembers(value) || !lexical || defNodeId(value) !== defNodeId(lexical))
|
|
787
|
+
return;
|
|
788
|
+
binding = { kind: "named-type", typeName: receiverName, typeNode: access.receiver, memberScope: "static" };
|
|
789
|
+
}
|
|
790
|
+
}
|
|
317
791
|
if (!binding)
|
|
318
792
|
return;
|
|
319
793
|
const site = { file: context.moduleEntry.file, range: toRange(access.property) };
|
|
320
|
-
|
|
794
|
+
// Shared call-count facts treat unproven spread expansions as unknown (null),
|
|
795
|
+
// so overload selection never fabricates an argument count.
|
|
796
|
+
const argumentCount = supportsReceiverMemberOverloads(context.sup.id)
|
|
797
|
+
? getCallArgumentCount({ languageId: context.sup.id, source: context.source, call: node })
|
|
798
|
+
: null;
|
|
321
799
|
if (binding.kind === "named-type") {
|
|
322
|
-
const typeDef = resolveNamedType(context, binding.typeName,
|
|
800
|
+
const typeDef = resolveNamedType(context, binding.typeName, binding.typeNode, binding.constructed);
|
|
323
801
|
if (!typeDef)
|
|
324
802
|
return;
|
|
325
803
|
context.receiverCalls.push({
|
|
@@ -329,26 +807,89 @@ export function emitFunctionBodyEdges(context, functionNodes) {
|
|
|
329
807
|
memberName,
|
|
330
808
|
argumentCount,
|
|
331
809
|
site,
|
|
810
|
+
memberScope: forcedMemberScope ?? binding.memberScope,
|
|
811
|
+
...(context.sup.id === "go" && !isGoExportedMemberName(context.sup.id, memberName)
|
|
812
|
+
? { goPackagePeerFiles: getCompilationUnitPeers(context.index, context.moduleEntry.file).files }
|
|
813
|
+
: {}),
|
|
814
|
+
caseInsensitiveMemberName: phpCaseInsensitive,
|
|
332
815
|
});
|
|
333
816
|
return;
|
|
334
817
|
}
|
|
335
818
|
if (binding.kind === "own-type") {
|
|
336
819
|
const container = nearestMemberContainer(fn.node);
|
|
337
820
|
const declared = container
|
|
338
|
-
? lexicalMembers(container).filter((candidate) =>
|
|
821
|
+
? lexicalMembers(container).filter((candidate) => {
|
|
822
|
+
const candidateName = candidate.def.localName;
|
|
823
|
+
const nameMatches = phpCaseInsensitive
|
|
824
|
+
? foldPhpIdentifierCase(candidateName) === foldPhpIdentifierCase(memberName)
|
|
825
|
+
: candidateName === memberName;
|
|
826
|
+
if (!nameMatches)
|
|
827
|
+
return false;
|
|
828
|
+
if (binding.memberScope === "any")
|
|
829
|
+
return true;
|
|
830
|
+
return declarationNodeIsStatic(candidate.node, context.source) === (binding.memberScope === "static");
|
|
831
|
+
})
|
|
339
832
|
: [];
|
|
340
833
|
if (declared.length === 1) {
|
|
341
|
-
|
|
342
|
-
|
|
834
|
+
const unique = declared[0];
|
|
835
|
+
recordDefEdge(context, fromId, unique.def, "calls", access.property);
|
|
836
|
+
// File-hidden Swift members stay off cross-file shared owners. Skipping the
|
|
837
|
+
// coalesced deferred lookup keeps a same-container private call from being stripped.
|
|
838
|
+
if (isSwiftFileHiddenSharedOwnerMember(context.sup.id, unique.node))
|
|
839
|
+
return;
|
|
343
840
|
}
|
|
344
841
|
}
|
|
842
|
+
const receiverContainer = nearestMemberContainer(access.accessNode);
|
|
843
|
+
const receiverContainerName = receiverContainer?.childForFieldName("name");
|
|
844
|
+
const receiverOwnerDef = receiverContainerName
|
|
845
|
+
? context.moduleEntry.locals.find((local) => local.range.start.index === receiverContainerName.startIndex && declaresMembers(local))
|
|
846
|
+
: undefined;
|
|
345
847
|
context.receiverCalls.push({
|
|
346
848
|
callerId: fromId,
|
|
347
|
-
ownerId: null,
|
|
849
|
+
ownerId: receiverOwnerDef ? ensureNode(context, receiverOwnerDef) : null,
|
|
348
850
|
viaSupertypes: binding.kind === "supertype",
|
|
349
851
|
memberName,
|
|
350
852
|
argumentCount,
|
|
351
853
|
site,
|
|
854
|
+
memberScope: binding.memberScope,
|
|
855
|
+
caseInsensitiveMemberName: phpCaseInsensitive,
|
|
856
|
+
});
|
|
857
|
+
};
|
|
858
|
+
/**
|
|
859
|
+
* A bare call inside a member function carries no receiver syntax, but in
|
|
860
|
+
* languages where that implicitly targets `this` it can still name an
|
|
861
|
+
* inherited member. Deferred like `recordReceiverCall`'s own-type/supertype
|
|
862
|
+
* candidates so cross-file base-type members resolve once every module's
|
|
863
|
+
* `member_of` and hierarchy edges are known.
|
|
864
|
+
*/
|
|
865
|
+
const recordImplicitSelfMemberCall = (node, callee, fallbackDef = null) => {
|
|
866
|
+
if (!supportsImplicitSelfMemberCalls(context.sup.id) || !isIdentifierType(context.sup, callee.type))
|
|
867
|
+
return;
|
|
868
|
+
const container = nearestMemberContainer(fn.node);
|
|
869
|
+
const containerName = container?.childForFieldName("name");
|
|
870
|
+
const ownerDef = containerName
|
|
871
|
+
? context.moduleEntry.locals.find((local) => local.range.start.index === containerName.startIndex && declaresMembers(local))
|
|
872
|
+
: undefined;
|
|
873
|
+
if (!ownerDef)
|
|
874
|
+
return;
|
|
875
|
+
const memberName = sliceText(callee, context.source);
|
|
876
|
+
if (!memberName)
|
|
877
|
+
return;
|
|
878
|
+
const argumentCount = supportsReceiverMemberOverloads(context.sup.id)
|
|
879
|
+
? getCallArgumentCount({ languageId: context.sup.id, source: context.source, call: node })
|
|
880
|
+
: null;
|
|
881
|
+
context.receiverCalls.push({
|
|
882
|
+
callerId: fromId,
|
|
883
|
+
ownerId: ensureNode(context, ownerDef),
|
|
884
|
+
viaSupertypes: false,
|
|
885
|
+
memberName,
|
|
886
|
+
argumentCount,
|
|
887
|
+
site: { file: context.moduleEntry.file, range: toRange(callee) },
|
|
888
|
+
// A static caller can only reach a static bare member; an instance caller
|
|
889
|
+
// may bare-call either, matching C#'s unqualified invocation rules.
|
|
890
|
+
memberScope: nodeInStaticMemberContext(fn.node, context.source) ? "static" : "any",
|
|
891
|
+
caseInsensitiveMemberName: phpCaseInsensitive,
|
|
892
|
+
fallbackTargetId: fallbackDef ? ensureNode(context, fallbackDef) : undefined,
|
|
352
893
|
});
|
|
353
894
|
};
|
|
354
895
|
/**
|
|
@@ -360,14 +901,94 @@ export function emitFunctionBodyEdges(context, functionNodes) {
|
|
|
360
901
|
const resolveCallTarget = (node, callee) => {
|
|
361
902
|
const access = receiverCallAccess(context.sup, node, callee);
|
|
362
903
|
if (access) {
|
|
363
|
-
if (
|
|
904
|
+
if (recordTypeScriptNamespaceCall(context, node, access, fromId))
|
|
905
|
+
return;
|
|
906
|
+
if (recordImportTypeCall(context, access, fromId))
|
|
907
|
+
return;
|
|
908
|
+
const receiverName = sliceText(access.receiver, context.source);
|
|
909
|
+
const typeScopedCppCall = context.sup.id === "cpp" &&
|
|
910
|
+
context.source.slice(access.receiver.endIndex, access.property.startIndex).includes("::");
|
|
911
|
+
if (typeScopedCppCall) {
|
|
912
|
+
const qualifiedName = cppQualifiedNameSegments(access.accessNode, context.source).join("::");
|
|
913
|
+
const qualifiedTarget = context.resolveIdentifier(qualifiedName, access.property);
|
|
914
|
+
// Whether the owner is a class, and whether the caller has an implicit `this` of that class,
|
|
915
|
+
// needs the exact qualified path, so the decision waits until the function body is walked.
|
|
916
|
+
qualifiedCppCalls.push({
|
|
917
|
+
node,
|
|
918
|
+
access,
|
|
919
|
+
target: qualifiedTarget,
|
|
920
|
+
ownerPath: qualifiedName.split("::").slice(0, -1),
|
|
921
|
+
});
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
if (keywordReceiverKind(context.sup.id, receiverName)) {
|
|
364
925
|
recordReceiverCall(node, access);
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
if (tryResolveChain(context, access.accessNode, fromId, "calls"))
|
|
929
|
+
return;
|
|
930
|
+
if (context.sup.id === "csharp" &&
|
|
931
|
+
access.receiver.type === "member_access_expression" &&
|
|
932
|
+
csharpDottedNameRoot(access.receiver)) {
|
|
933
|
+
dottedReceiverCalls.push({ fromId, access });
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
recordReceiverCall(node, access);
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
if (!callee)
|
|
365
940
|
return;
|
|
941
|
+
// A name whose lookup waits on an async member step (implicit `this`/`self`, C++ out-of-line
|
|
942
|
+
// owners, included C/C++ declarations) settles after the walk exactly as navigation does.
|
|
943
|
+
if (isIdentifierType(context.sup, callee.type)) {
|
|
944
|
+
const name = sliceText(callee, context.source);
|
|
945
|
+
const resolution = context.resolveName(name, callee);
|
|
946
|
+
if (resolution?.status === "deferred") {
|
|
947
|
+
deferredCalls.push({ callee, name, resolution });
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
const implicitOwnerLanguage = context.sup.id === "swift" || context.sup.id === "csharp";
|
|
952
|
+
if (implicitOwnerLanguage && isIdentifierType(context.sup, callee.type) && nearestMemberContainer(fn.node)) {
|
|
953
|
+
const name = sliceText(callee, context.source);
|
|
954
|
+
const lexical = context.resolveIdentifier(name, callee);
|
|
955
|
+
const importedCsharpMember = context.sup.id === "csharp" &&
|
|
956
|
+
!!lexical?.isMember &&
|
|
957
|
+
fileIdentityKey(lexical.file) !== fileIdentityKey(context.moduleEntry.file);
|
|
958
|
+
if (!importedCsharpMember && (!lexical || lexical.isMember || !context.hasNonModuleBinding(name, callee))) {
|
|
959
|
+
// A method's local binding wins. Type members require receiver ownership
|
|
960
|
+
// and static-scope proof; only a free function can be a fallback.
|
|
961
|
+
recordImplicitSelfMemberCall(node, callee, lexical?.isMember ? null : lexical);
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
366
964
|
}
|
|
367
|
-
if (callee)
|
|
368
|
-
|
|
965
|
+
if (!tryResolveNode(context, callee, fromId, "calls"))
|
|
966
|
+
recordImplicitSelfMemberCall(node, callee);
|
|
967
|
+
};
|
|
968
|
+
const recordRubySuper = (superNode) => {
|
|
969
|
+
const container = nearestMemberContainer(fn.node);
|
|
970
|
+
if (!container || container.type !== "class")
|
|
971
|
+
return;
|
|
972
|
+
const owner = memberContainerDef(context.moduleEntry, container);
|
|
973
|
+
if (!owner)
|
|
974
|
+
return;
|
|
975
|
+
context.receiverCalls.push({
|
|
976
|
+
callerId: fromId,
|
|
977
|
+
ownerId: ensureNode(context, owner),
|
|
978
|
+
viaSupertypes: true,
|
|
979
|
+
memberName: fn.def.localName,
|
|
980
|
+
argumentCount: null,
|
|
981
|
+
site: { file: context.moduleEntry.file, range: toRange(superNode) },
|
|
982
|
+
memberScope: "any",
|
|
983
|
+
});
|
|
369
984
|
};
|
|
370
985
|
const recordCallOrInstantiation = (node) => {
|
|
986
|
+
if (context.sup.id === "ruby" && node.type === "super") {
|
|
987
|
+
recordRubySuper(node);
|
|
988
|
+
return true;
|
|
989
|
+
}
|
|
990
|
+
if (node.type === "token_tree")
|
|
991
|
+
recordRustMacroArgumentCalls(context, node, fromId);
|
|
371
992
|
if (callNodeTypes.has(node.type)) {
|
|
372
993
|
if (context.sup.id === "go") {
|
|
373
994
|
const callTarget = getCallTarget(node);
|
|
@@ -386,20 +1007,53 @@ export function emitFunctionBodyEdges(context, functionNodes) {
|
|
|
386
1007
|
const receiverNode = node.childForFieldName("receiver");
|
|
387
1008
|
const methodName = methodNode ? sliceText(methodNode, context.source) : null;
|
|
388
1009
|
if (methodName === "new" && receiverNode) {
|
|
389
|
-
tryResolveNode(context, receiverNode, fromId, "instantiates");
|
|
1010
|
+
const recorded = tryResolveNode(context, receiverNode, fromId, "instantiates");
|
|
1011
|
+
if (!recorded) {
|
|
1012
|
+
const rubyType = resolveRubyVisibleConstant(context.index, context.moduleEntry, context.sup, sliceText(receiverNode, context.source));
|
|
1013
|
+
if (rubyType)
|
|
1014
|
+
recordDefEdge(context, fromId, rubyType, "instantiates", receiverNode);
|
|
1015
|
+
}
|
|
390
1016
|
return false;
|
|
391
1017
|
}
|
|
1018
|
+
if (methodNode?.type === "super")
|
|
1019
|
+
return true;
|
|
392
1020
|
if (methodNode) {
|
|
393
1021
|
resolveCallTarget(node, methodNode);
|
|
394
1022
|
return false;
|
|
395
1023
|
}
|
|
1024
|
+
const callee = getCallTarget(node);
|
|
1025
|
+
if (callee?.type === "super")
|
|
1026
|
+
return true;
|
|
1027
|
+
resolveCallTarget(node, callee);
|
|
1028
|
+
return false;
|
|
396
1029
|
}
|
|
1030
|
+
if (recordSwiftCapitalizedConstruction(context, node, fromId))
|
|
1031
|
+
return true;
|
|
397
1032
|
resolveCallTarget(node, getCallTarget(node));
|
|
398
1033
|
}
|
|
399
1034
|
if (newNodeTypes.has(node.type)) {
|
|
400
|
-
const
|
|
401
|
-
if (
|
|
402
|
-
|
|
1035
|
+
const keyword = phpObjectCreationKeyword(node, context.source, context.sup);
|
|
1036
|
+
if (keyword) {
|
|
1037
|
+
const created = resolvePhpObjectCreationTarget(context.index, context.moduleEntry, keyword, context.source, context.sup);
|
|
1038
|
+
if (created)
|
|
1039
|
+
recordDefEdge(context, fromId, created, "instantiates", keyword.nameNode);
|
|
1040
|
+
}
|
|
1041
|
+
else {
|
|
1042
|
+
const target = constructionTypeName(node, context.source, context.sup) ?? getNewTarget(node);
|
|
1043
|
+
if (target) {
|
|
1044
|
+
if (recordTypeScriptNamespaceConstruction(context, node, target, fromId))
|
|
1045
|
+
return true;
|
|
1046
|
+
const property = isMemberAccessNode(context.sup, target)
|
|
1047
|
+
? getMemberAccessParts(context.sup, target).property
|
|
1048
|
+
: null;
|
|
1049
|
+
if ((context.sup.id === "java" || context.sup.id === "csharp") && property) {
|
|
1050
|
+
qualifiedConstructionTargets.push({ fromId, member: property, target });
|
|
1051
|
+
}
|
|
1052
|
+
else {
|
|
1053
|
+
tryResolveNode(context, target, fromId, "instantiates");
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
403
1057
|
}
|
|
404
1058
|
return true;
|
|
405
1059
|
};
|
|
@@ -413,11 +1067,55 @@ export function emitFunctionBodyEdges(context, functionNodes) {
|
|
|
413
1067
|
walkFunctionBody(child, allowChildCallProcessing);
|
|
414
1068
|
};
|
|
415
1069
|
walkFunctionBody(fn.node, true);
|
|
1070
|
+
for (const call of deferredCalls) {
|
|
1071
|
+
const target = await context.settleName(call.name, call.callee, call.resolution);
|
|
1072
|
+
// Swift `Foo()` whose deferred lookup settles on a type is construction.
|
|
1073
|
+
const label = context.sup.id === "swift" && target && declaresMembers(target) ? "instantiates" : "calls";
|
|
1074
|
+
if (target)
|
|
1075
|
+
recordDefEdge(context, fromId, target, label, call.callee);
|
|
1076
|
+
}
|
|
1077
|
+
for (const call of qualifiedCppCalls) {
|
|
1078
|
+
const owner = call.ownerPath.length
|
|
1079
|
+
? await resolveCppQualifiedMemberContainer(context.index, context.moduleEntry, call.ownerPath, context.loadParsedFile)
|
|
1080
|
+
: null;
|
|
1081
|
+
if (!owner) {
|
|
1082
|
+
// A namespace (or no) qualifier names the free function directly.
|
|
1083
|
+
if (call.target)
|
|
1084
|
+
recordDefEdge(context, fromId, call.target, "calls", call.access.property);
|
|
1085
|
+
continue;
|
|
1086
|
+
}
|
|
1087
|
+
const implicitThis = await cppQualifiedOwnerHasImplicitThis(context.index, context.moduleEntry, call.access.accessNode, context.source, context.sup, owner);
|
|
1088
|
+
if (implicitThis && call.target) {
|
|
1089
|
+
recordDefEdge(context, fromId, call.target, "calls", call.access.property);
|
|
1090
|
+
}
|
|
1091
|
+
else {
|
|
1092
|
+
// Without an implicit `this` of the owner, `Owner::member()` can only name a static member.
|
|
1093
|
+
recordReceiverCall(call.node, call.access, implicitThis ? undefined : "static");
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
for (const { fromId, access } of dottedReceiverCalls) {
|
|
1098
|
+
const member = await context.resolveMemberAccessTarget(access.property);
|
|
1099
|
+
// The graph records no edge for a call its target cannot accept, as for other member calls.
|
|
1100
|
+
if (member &&
|
|
1101
|
+
(await memberAcceptsCallAt(context.index, member, access.accessNode, context.source, context.sup.id))) {
|
|
1102
|
+
recordDefEdge(context, fromId, member, "calls", access.property);
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
for (const target of qualifiedConstructionTargets) {
|
|
1106
|
+
const resolved = await context.resolveMemberAccessTarget(target.member);
|
|
1107
|
+
if (resolved) {
|
|
1108
|
+
recordDefEdge(context, target.fromId, resolved, "instantiates", target.member);
|
|
1109
|
+
}
|
|
1110
|
+
else {
|
|
1111
|
+
tryResolveNode(context, target.target, target.fromId, "instantiates");
|
|
1112
|
+
}
|
|
416
1113
|
}
|
|
417
1114
|
}
|
|
418
1115
|
/** Qualifiers name a container, not another base type. */
|
|
419
1116
|
const QUALIFIER_NAME_FIELD = {
|
|
420
1117
|
qualified_name: "name", // C#: Namespace.Base, Outer.Inner
|
|
1118
|
+
alias_qualified_name: "name", // C#: X::Base, global::Namespace.Base
|
|
421
1119
|
qualified_identifier: "name", // C++: ns::Base
|
|
422
1120
|
scope_resolution: "name", // Ruby: Module::Base
|
|
423
1121
|
qualified_type: "name", // Go: pkg.Base
|
|
@@ -504,9 +1202,19 @@ function narrowBaseSpecifierNode(node) {
|
|
|
504
1202
|
}
|
|
505
1203
|
/** Collect one type identifier per direct base or interface specifier. */
|
|
506
1204
|
function collectBaseSpecifierIdentifiers(node, sup, out) {
|
|
1205
|
+
if (isUnprovenHeritageExpression(node))
|
|
1206
|
+
return;
|
|
507
1207
|
if (BASE_TYPE_IGNORED_TYPES[node.type])
|
|
508
1208
|
return;
|
|
1209
|
+
if (sup.id === "cpp" && node.type === "qualified_identifier") {
|
|
1210
|
+
out.push(node);
|
|
1211
|
+
return;
|
|
1212
|
+
}
|
|
509
1213
|
const narrowed = narrowBaseSpecifierNode(node);
|
|
1214
|
+
if ((sup.id === "ts" || sup.id === "tsx") && narrowed.type === "member_expression") {
|
|
1215
|
+
out.push(narrowed);
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
510
1218
|
if (isIdentifierType(sup, narrowed.type) || narrowed.type === "type_identifier") {
|
|
511
1219
|
out.push(narrowed);
|
|
512
1220
|
return;
|
|
@@ -514,19 +1222,39 @@ function collectBaseSpecifierIdentifiers(node, sup, out) {
|
|
|
514
1222
|
for (const child of narrowed.namedChildren ?? [])
|
|
515
1223
|
collectBaseSpecifierIdentifiers(child, sup, out);
|
|
516
1224
|
}
|
|
517
|
-
function recordIdentifierRelations(context, fromId, container, relationForTarget) {
|
|
1225
|
+
async function recordIdentifierRelations(context, fromId, container, relationForTarget) {
|
|
518
1226
|
const identifiers = [];
|
|
519
1227
|
collectBaseSpecifierIdentifiers(container, context.sup, identifiers);
|
|
520
1228
|
const seen = new Set();
|
|
521
1229
|
for (const [index, identifier] of identifiers.entries()) {
|
|
522
|
-
|
|
1230
|
+
let target;
|
|
1231
|
+
if (context.sup.id === "cpp" && identifier.type === "qualified_identifier") {
|
|
1232
|
+
const qualifiedPath = cppQualifiedNameSegments(identifier, context.source);
|
|
1233
|
+
target = await resolveCppQualifiedMemberContainer(context.index, context.moduleEntry, qualifiedPath, context.loadParsedFile);
|
|
1234
|
+
if (!target && qualifiedPath.length > 1) {
|
|
1235
|
+
target = context.resolveIdentifier(qualifiedPath.join("::"), identifier);
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
else if ((context.sup.id === "ts" || context.sup.id === "tsx") && identifier.type === "member_expression") {
|
|
1239
|
+
const qualified = context.resolveMemberChainTarget(identifier);
|
|
1240
|
+
target = qualified && declaresMembers(qualified) ? qualified : null;
|
|
1241
|
+
}
|
|
1242
|
+
else {
|
|
1243
|
+
const name = sliceText(identifier, context.source);
|
|
1244
|
+
target = context.resolveIdentifier(name, identifier);
|
|
1245
|
+
// Ruby `require` publishes a top-level constant, not a nested one. Reuse the
|
|
1246
|
+
// same visibility as goto so `class Worker < Base` follows the required class.
|
|
1247
|
+
if (!target && context.sup.id === "ruby") {
|
|
1248
|
+
target = resolveRubyVisibleConstant(context.index, context.moduleEntry, context.sup, name);
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
523
1251
|
if (!target)
|
|
524
1252
|
continue;
|
|
525
1253
|
const targetId = defNodeId(target);
|
|
526
1254
|
if (seen.has(targetId))
|
|
527
1255
|
continue;
|
|
528
1256
|
seen.add(targetId);
|
|
529
|
-
recordDefEdge(context, fromId, target, relationForTarget(target, index), identifier);
|
|
1257
|
+
recordDefEdge(context, fromId, target, relationForTarget(target, index, identifier), identifier);
|
|
530
1258
|
}
|
|
531
1259
|
}
|
|
532
1260
|
const RUBY_NESTED_SCOPE_TYPES = new Set(["class", "module", "method", "singleton_method", "block", "do_block"]);
|
|
@@ -540,68 +1268,24 @@ function collectDirectCallsExcludingNestedScopes(node, out) {
|
|
|
540
1268
|
collectDirectCallsExcludingNestedScopes(child, out);
|
|
541
1269
|
}
|
|
542
1270
|
}
|
|
543
|
-
|
|
544
|
-
clauses: [
|
|
545
|
-
// The value field is the superclass expression; the sibling type_arguments
|
|
546
|
-
// field holds super-call type arguments, which are not base types.
|
|
547
|
-
{ type: "extends_clause", label: "extends", field: "value" },
|
|
548
|
-
{ type: "implements_clause", label: "implements" },
|
|
549
|
-
],
|
|
550
|
-
};
|
|
551
|
-
/**
|
|
552
|
-
* Per-language class hierarchy forms. The grammar and the language runtime are
|
|
553
|
-
* the only sources of these node names: JavaScript emits `class_heritage` for
|
|
554
|
-
* `extends`, while TypeScript emits `extends_clause` and `implements_clause`.
|
|
555
|
-
*/
|
|
556
|
-
const INHERITANCE_RULES = {
|
|
557
|
-
js: { clauses: [{ type: "class_heritage", label: "extends" }] },
|
|
558
|
-
ts: TYPESCRIPT_INHERITANCE_RULES,
|
|
559
|
-
tsx: TYPESCRIPT_INHERITANCE_RULES,
|
|
560
|
-
java: {
|
|
561
|
-
clauses: [
|
|
562
|
-
{ type: "superclass", label: "extends" },
|
|
563
|
-
{ type: "super_interfaces", label: "implements" },
|
|
564
|
-
],
|
|
565
|
-
},
|
|
566
|
-
csharp: { clauses: [{ type: "base_list", label: "superclass-first" }] },
|
|
567
|
-
kotlin: { clauses: [{ type: "delegation_specifiers", label: "superclass-first" }] },
|
|
568
|
-
swift: { clauses: [{ type: "inheritance_specifier", label: "superclass-first", each: true }] },
|
|
569
|
-
python: { clauses: [{ type: "argument_list", label: "extends" }] },
|
|
570
|
-
php: {
|
|
571
|
-
clauses: [
|
|
572
|
-
{ type: "base_clause", label: "extends" },
|
|
573
|
-
{ type: "class_interface_clause", label: "implements" },
|
|
574
|
-
{ type: "use_declaration", label: "trait", each: true },
|
|
575
|
-
],
|
|
576
|
-
},
|
|
577
|
-
ruby: {
|
|
578
|
-
clauses: [{ type: "superclass", label: "extends" }],
|
|
579
|
-
mixinCalls: ["include", "extend", "prepend"],
|
|
580
|
-
},
|
|
581
|
-
cpp: { clauses: [{ type: "base_class_clause", label: "extends" }] },
|
|
582
|
-
go: {
|
|
583
|
-
clauses: [],
|
|
584
|
-
embeds: [
|
|
585
|
-
{ body: "interface_type", member: "type_elem" },
|
|
586
|
-
{
|
|
587
|
-
body: "struct_type",
|
|
588
|
-
memberList: "field_declaration_list",
|
|
589
|
-
member: "field_declaration",
|
|
590
|
-
typeField: "type",
|
|
591
|
-
nameless: true,
|
|
592
|
-
},
|
|
593
|
-
],
|
|
594
|
-
},
|
|
595
|
-
};
|
|
596
|
-
function baseClauseRelation(label, target, index, interfaceIds) {
|
|
1271
|
+
function baseClauseRelation(label, target, index, interfaceIds, identifier, kotlin) {
|
|
597
1272
|
if (label !== "superclass-first")
|
|
598
1273
|
return label;
|
|
1274
|
+
if (kotlin) {
|
|
1275
|
+
let current = identifier.parent;
|
|
1276
|
+
while (current && current.type !== "delegation_specifiers") {
|
|
1277
|
+
if (current.type === "constructor_invocation")
|
|
1278
|
+
return "extends";
|
|
1279
|
+
current = current.parent;
|
|
1280
|
+
}
|
|
1281
|
+
return "implements";
|
|
1282
|
+
}
|
|
599
1283
|
if (interfaceIds.has(defNodeId(target)) || index > 0)
|
|
600
1284
|
return "implements";
|
|
601
1285
|
return "extends";
|
|
602
1286
|
}
|
|
603
1287
|
/** Records `implements` for every embedded type of a Go interface or struct declaration. */
|
|
604
|
-
function recordEmbedRelations(context, fromId, declaration, embeds) {
|
|
1288
|
+
async function recordEmbedRelations(context, fromId, declaration, embeds) {
|
|
605
1289
|
const declaredType = declaration.childForFieldName("type");
|
|
606
1290
|
if (!declaredType)
|
|
607
1291
|
return;
|
|
@@ -619,12 +1303,12 @@ function recordEmbedRelations(context, fromId, declaration, embeds) {
|
|
|
619
1303
|
const specifier = rule.typeField ? member.childForFieldName(rule.typeField) : member;
|
|
620
1304
|
if (!specifier)
|
|
621
1305
|
continue;
|
|
622
|
-
recordIdentifierRelations(context, fromId, specifier, () => "implements");
|
|
1306
|
+
await recordIdentifierRelations(context, fromId, specifier, () => "implements");
|
|
623
1307
|
}
|
|
624
1308
|
}
|
|
625
1309
|
}
|
|
626
|
-
export function emitClassInheritanceEdges(context, classNodes) {
|
|
627
|
-
const rules =
|
|
1310
|
+
export async function emitClassInheritanceEdges(context, classNodes) {
|
|
1311
|
+
const rules = MEMBER_ACCESS_ROWS[context.sup.id]?.receiverAncestry;
|
|
628
1312
|
if (!rules)
|
|
629
1313
|
return;
|
|
630
1314
|
const interfaceIds = new Set(classNodes
|
|
@@ -635,20 +1319,27 @@ export function emitClassInheritanceEdges(context, classNodes) {
|
|
|
635
1319
|
.map((candidate) => defNodeId(candidate.def)));
|
|
636
1320
|
for (const cls of classNodes) {
|
|
637
1321
|
const fromId = ensureNode(context, cls.def);
|
|
638
|
-
markImplementationTarget(context, fromId, cls.node, cls.def);
|
|
1322
|
+
markImplementationTarget(context, fromId, cls.node, context.source, cls.def);
|
|
1323
|
+
if ((context.sup.id === "ts" || context.sup.id === "tsx") && cls.node.type === "class_declaration") {
|
|
1324
|
+
const heritage = cls.node.namedChildren.find((child) => child.type === "class_heritage");
|
|
1325
|
+
const superclass = heritage?.namedChildren.find((child) => child.type === "extends_clause");
|
|
1326
|
+
// TypeScript classes have one superclass. Invalid multi-base syntax proves neither target.
|
|
1327
|
+
if (superclass && superclass.namedChildren.length > 1)
|
|
1328
|
+
continue;
|
|
1329
|
+
}
|
|
639
1330
|
for (const rule of rules.clauses) {
|
|
640
1331
|
const clauses = [];
|
|
641
1332
|
if (rule.each) {
|
|
642
|
-
collectNodesByType(cls.node, rule.
|
|
1333
|
+
collectNodesByType(cls.node, rule.nodeType, clauses);
|
|
643
1334
|
}
|
|
644
1335
|
else {
|
|
645
|
-
const found = findFirstNodeByType(cls.node, rule.
|
|
1336
|
+
const found = findFirstNodeByType(cls.node, rule.nodeType);
|
|
646
1337
|
if (found)
|
|
647
1338
|
clauses.push(found);
|
|
648
1339
|
}
|
|
649
1340
|
for (const clause of clauses) {
|
|
650
1341
|
const specifiers = rule.field ? (clause.childForFieldName(rule.field) ?? clause) : clause;
|
|
651
|
-
recordIdentifierRelations(context, fromId, specifiers, (target, index) => baseClauseRelation(rule.
|
|
1342
|
+
await recordIdentifierRelations(context, fromId, specifiers, (target, index, identifier) => baseClauseRelation(rule.relation, target, index, interfaceIds, identifier, context.sup.id === "kotlin"));
|
|
652
1343
|
}
|
|
653
1344
|
}
|
|
654
1345
|
if (rules.mixinCalls) {
|
|
@@ -663,11 +1354,11 @@ export function emitClassInheritanceEdges(context, classNodes) {
|
|
|
663
1354
|
continue;
|
|
664
1355
|
const args = call.childForFieldName("arguments");
|
|
665
1356
|
if (args)
|
|
666
|
-
recordIdentifierRelations(context, fromId, args, () => "mixin");
|
|
1357
|
+
await recordIdentifierRelations(context, fromId, args, () => "mixin");
|
|
667
1358
|
}
|
|
668
1359
|
}
|
|
669
1360
|
if (rules.embeds)
|
|
670
|
-
recordEmbedRelations(context, fromId, cls.node, rules.embeds);
|
|
1361
|
+
await recordEmbedRelations(context, fromId, cls.node, rules.embeds);
|
|
671
1362
|
}
|
|
672
1363
|
}
|
|
673
1364
|
export function emitRustImplEdges(context, rootNode) {
|