@lzehrung/codegraph 2.3.31 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -141
- package/codegraph-skill/codegraph/SKILL.md +8 -6
- package/dist/agent/artifact.d.ts +2 -0
- package/dist/agent/artifact.d.ts.map +1 -1
- package/dist/agent/artifact.js.map +1 -1
- package/dist/agent/call-hierarchy.js +2 -4
- package/dist/agent/call-hierarchy.js.map +1 -1
- package/dist/agent/explore.d.ts +1 -1
- package/dist/agent/explore.d.ts.map +1 -1
- package/dist/agent/explore.js +3 -2
- package/dist/agent/explore.js.map +1 -1
- package/dist/agent/refactor-plan.js +2 -4
- package/dist/agent/refactor-plan.js.map +1 -1
- package/dist/agent/rename-preview.d.ts.map +1 -1
- package/dist/agent/rename-preview.js +4 -3
- package/dist/agent/rename-preview.js.map +1 -1
- package/dist/agent/search.d.ts.map +1 -1
- package/dist/agent/search.js +5 -3
- package/dist/agent/search.js.map +1 -1
- package/dist/agent/semantic-symbols.d.ts.map +1 -1
- package/dist/agent/semantic-symbols.js +2 -3
- package/dist/agent/semantic-symbols.js.map +1 -1
- package/dist/agent/session.d.ts +3 -0
- package/dist/agent/session.d.ts.map +1 -1
- package/dist/agent/session.js +10 -2
- package/dist/agent/session.js.map +1 -1
- package/dist/agent/type-hierarchy.js +2 -4
- package/dist/agent/type-hierarchy.js.map +1 -1
- package/dist/agent/workspace-symbols.d.ts.map +1 -1
- package/dist/agent/workspace-symbols.js +2 -3
- package/dist/agent/workspace-symbols.js.map +1 -1
- package/dist/agent-tools.d.ts +0 -4
- package/dist/agent-tools.d.ts.map +1 -1
- package/dist/agent-tools.js +3 -6
- package/dist/agent-tools.js.map +1 -1
- package/dist/analysis-summary.d.ts +8 -4
- package/dist/analysis-summary.d.ts.map +1 -1
- package/dist/analysis-summary.js +15 -66
- package/dist/analysis-summary.js.map +1 -1
- package/dist/bin/cli.js +51145 -41770
- package/dist/bin/query-index-worker.js +122 -82
- package/dist/cli/affected.d.ts.map +1 -1
- package/dist/cli/affected.js +4 -1
- package/dist/cli/affected.js.map +1 -1
- package/dist/cli/command-catalog.js +1 -1
- package/dist/cli/command-catalog.js.map +1 -1
- package/dist/cli/command-table.d.ts.map +1 -1
- package/dist/cli/command-table.js +9 -17
- package/dist/cli/command-table.js.map +1 -1
- package/dist/cli/context.js +10 -10
- package/dist/cli/context.js.map +1 -1
- package/dist/cli/drift.d.ts +0 -2
- package/dist/cli/drift.d.ts.map +1 -1
- package/dist/cli/drift.js +0 -1
- package/dist/cli/drift.js.map +1 -1
- package/dist/cli/graph-delta.d.ts +0 -2
- package/dist/cli/graph-delta.d.ts.map +1 -1
- package/dist/cli/graph-delta.js +0 -4
- package/dist/cli/graph-delta.js.map +1 -1
- package/dist/cli/graph-queries.d.ts.map +1 -1
- package/dist/cli/graph-queries.js +27 -1
- package/dist/cli/graph-queries.js.map +1 -1
- package/dist/cli/graph.d.ts +0 -3
- package/dist/cli/graph.d.ts.map +1 -1
- package/dist/cli/graph.js +4 -14
- package/dist/cli/graph.js.map +1 -1
- package/dist/cli/help.d.ts +34 -34
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +9 -14
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/impact.d.ts +0 -2
- package/dist/cli/impact.d.ts.map +1 -1
- package/dist/cli/impact.js +0 -1
- package/dist/cli/impact.js.map +1 -1
- package/dist/cli/index.d.ts +0 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +5 -6
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/inspect.d.ts +0 -2
- package/dist/cli/inspect.d.ts.map +1 -1
- package/dist/cli/inspect.js +5 -8
- package/dist/cli/inspect.js.map +1 -1
- package/dist/cli/invocation-context.d.ts +0 -3
- package/dist/cli/invocation-context.d.ts.map +1 -1
- package/dist/cli/invocation-context.js +4 -16
- package/dist/cli/invocation-context.js.map +1 -1
- package/dist/cli/navigation.d.ts +0 -2
- package/dist/cli/navigation.d.ts.map +1 -1
- package/dist/cli/navigation.js +0 -1
- package/dist/cli/navigation.js.map +1 -1
- package/dist/cli/options.d.ts.map +1 -1
- package/dist/cli/options.js +4 -14
- package/dist/cli/options.js.map +1 -1
- package/dist/cli/review.d.ts +0 -2
- package/dist/cli/review.d.ts.map +1 -1
- package/dist/cli/review.js +0 -5
- package/dist/cli/review.js.map +1 -1
- package/dist/cli/root-policy.d.ts +0 -2
- package/dist/cli/root-policy.d.ts.map +1 -1
- package/dist/cli/root-policy.js +0 -8
- package/dist/cli/root-policy.js.map +1 -1
- package/dist/cli/server.d.ts.map +1 -1
- package/dist/cli/server.js +0 -2
- package/dist/cli/server.js.map +1 -1
- package/dist/drift/artifact.d.ts.map +1 -1
- package/dist/drift/artifact.js +15 -18
- package/dist/drift/artifact.js.map +1 -1
- package/dist/drift/compare.js +1 -1
- package/dist/drift/compare.js.map +1 -1
- package/dist/drift/git.d.ts.map +1 -1
- package/dist/drift/git.js +0 -1
- package/dist/drift/git.js.map +1 -1
- package/dist/drift/snapshot.d.ts.map +1 -1
- package/dist/drift/snapshot.js +5 -7
- package/dist/drift/snapshot.js.map +1 -1
- package/dist/drift/types.d.ts +2 -2
- package/dist/drift/types.d.ts.map +1 -1
- package/dist/drift/types.js.map +1 -1
- package/dist/duplicates/unit-cache.d.ts +0 -1
- package/dist/duplicates/unit-cache.d.ts.map +1 -1
- package/dist/duplicates/unit-cache.js +3 -11
- package/dist/duplicates/unit-cache.js.map +1 -1
- package/dist/duplicates/units.d.ts +5 -5
- package/dist/duplicates/units.d.ts.map +1 -1
- package/dist/duplicates/units.js +25 -88
- package/dist/duplicates/units.js.map +1 -1
- package/dist/graph-builder.d.ts +0 -6
- package/dist/graph-builder.d.ts.map +1 -1
- package/dist/graph-builder.js +6 -11
- package/dist/graph-builder.js.map +1 -1
- package/dist/graph-edge-collector.d.ts +4 -6
- package/dist/graph-edge-collector.d.ts.map +1 -1
- package/dist/graph-edge-collector.js +31 -26
- package/dist/graph-edge-collector.js.map +1 -1
- package/dist/graphs/angularjs.d.ts.map +1 -1
- package/dist/graphs/angularjs.js +2 -1
- package/dist/graphs/angularjs.js.map +1 -1
- package/dist/graphs/edge-resolution.d.ts +4 -1
- package/dist/graphs/edge-resolution.d.ts.map +1 -1
- package/dist/graphs/edge-resolution.js +24 -76
- package/dist/graphs/edge-resolution.js.map +1 -1
- package/dist/graphs/specifiers.d.ts +4 -13
- package/dist/graphs/specifiers.d.ts.map +1 -1
- package/dist/graphs/specifiers.js +80 -173
- package/dist/graphs/specifiers.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/ast.d.ts +20 -0
- package/dist/graphs/symbol-graph-detailed/ast.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/ast.js +154 -15
- package/dist/graphs/symbol-graph-detailed/ast.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/edge-passes.d.ts +60 -8
- package/dist/graphs/symbol-graph-detailed/edge-passes.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/edge-passes.js +997 -168
- package/dist/graphs/symbol-graph-detailed/edge-passes.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/import-aliases.d.ts +2 -1
- package/dist/graphs/symbol-graph-detailed/import-aliases.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/import-aliases.js +150 -7
- package/dist/graphs/symbol-graph-detailed/import-aliases.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/member-chains.d.ts +1 -0
- package/dist/graphs/symbol-graph-detailed/member-chains.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/member-chains.js +3 -1
- package/dist/graphs/symbol-graph-detailed/member-chains.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/receiver-calls.d.ts +141 -25
- package/dist/graphs/symbol-graph-detailed/receiver-calls.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/receiver-calls.js +1015 -166
- package/dist/graphs/symbol-graph-detailed/receiver-calls.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed.js +449 -46
- package/dist/graphs/symbol-graph-detailed.js.map +1 -1
- package/dist/graphs/symbol-graph.d.ts +2 -0
- package/dist/graphs/symbol-graph.d.ts.map +1 -1
- package/dist/graphs/symbol-graph.js +34 -16
- package/dist/graphs/symbol-graph.js.map +1 -1
- package/dist/graphs/types.d.ts +0 -4
- package/dist/graphs/types.d.ts.map +1 -1
- package/dist/graphs.d.ts +1 -1
- package/dist/graphs.d.ts.map +1 -1
- package/dist/impact/call-compatibility/providers/index.d.ts +4 -4
- package/dist/impact/call-compatibility/providers/index.d.ts.map +1 -1
- package/dist/impact/call-compatibility/providers/index.js +6 -6
- package/dist/impact/call-compatibility/providers/index.js.map +1 -1
- package/dist/impact/call-compatibility/providers/types.d.ts +2 -0
- package/dist/impact/call-compatibility/providers/types.d.ts.map +1 -1
- package/dist/impact/call-compatibility/types.d.ts +6 -0
- package/dist/impact/call-compatibility/types.d.ts.map +1 -1
- package/dist/impact/call-compatibility.d.ts.map +1 -1
- package/dist/impact/call-compatibility.js +316 -526
- package/dist/impact/call-compatibility.js.map +1 -1
- package/dist/impact/map.js +1 -0
- package/dist/impact/map.js.map +1 -1
- package/dist/impact/reference-cache.d.ts.map +1 -1
- package/dist/impact/reference-cache.js +2 -2
- package/dist/impact/reference-cache.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/indexer/ambiguous-resolution.d.ts +11 -0
- package/dist/indexer/ambiguous-resolution.d.ts.map +1 -0
- package/dist/indexer/ambiguous-resolution.js +19 -0
- package/dist/indexer/ambiguous-resolution.js.map +1 -0
- package/dist/indexer/build-cache/module-cache.d.ts +39 -1
- package/dist/indexer/build-cache/module-cache.d.ts.map +1 -1
- package/dist/indexer/build-cache/module-cache.js +218 -21
- package/dist/indexer/build-cache/module-cache.js.map +1 -1
- package/dist/indexer/build-cache/options.d.ts +30 -44
- package/dist/indexer/build-cache/options.d.ts.map +1 -1
- package/dist/indexer/build-cache/options.js +32 -71
- package/dist/indexer/build-cache/options.js.map +1 -1
- package/dist/indexer/build-cache/project-snapshot.d.ts +3 -3
- package/dist/indexer/build-cache/project-snapshot.d.ts.map +1 -1
- package/dist/indexer/build-cache/project-snapshot.js +164 -84
- package/dist/indexer/build-cache/project-snapshot.js.map +1 -1
- package/dist/indexer/build-cache/reports.d.ts +0 -2
- package/dist/indexer/build-cache/reports.d.ts.map +1 -1
- package/dist/indexer/build-cache/reports.js +0 -97
- package/dist/indexer/build-cache/reports.js.map +1 -1
- package/dist/indexer/build-cache/resolver-environment.d.ts.map +1 -1
- package/dist/indexer/build-cache/resolver-environment.js +32 -2
- package/dist/indexer/build-cache/resolver-environment.js.map +1 -1
- package/dist/indexer/build-cache.d.ts +2 -2
- package/dist/indexer/build-cache.d.ts.map +1 -1
- package/dist/indexer/build-cache.js +2 -2
- package/dist/indexer/build-cache.js.map +1 -1
- package/dist/indexer/build-index.d.ts.map +1 -1
- package/dist/indexer/build-index.js +656 -71
- package/dist/indexer/build-index.js.map +1 -1
- package/dist/indexer/build-workers.d.ts.map +1 -1
- package/dist/indexer/build-workers.js +9 -9
- package/dist/indexer/build-workers.js.map +1 -1
- package/dist/indexer/call-hierarchy.js +1 -1
- package/dist/indexer/call-hierarchy.js.map +1 -1
- package/dist/indexer/callable-identity.d.ts +24 -0
- package/dist/indexer/callable-identity.d.ts.map +1 -0
- package/dist/indexer/callable-identity.js +111 -0
- package/dist/indexer/callable-identity.js.map +1 -0
- package/dist/indexer/compilation-units.d.ts +103 -0
- package/dist/indexer/compilation-units.d.ts.map +1 -0
- package/dist/indexer/compilation-units.js +607 -0
- package/dist/indexer/compilation-units.js.map +1 -0
- package/dist/indexer/cpp-callables.d.ts +25 -0
- package/dist/indexer/cpp-callables.d.ts.map +1 -0
- package/dist/indexer/cpp-callables.js +562 -0
- package/dist/indexer/cpp-callables.js.map +1 -0
- package/dist/indexer/declaration-visibility.d.ts +43 -2
- package/dist/indexer/declaration-visibility.d.ts.map +1 -1
- package/dist/indexer/declaration-visibility.js +158 -2
- package/dist/indexer/declaration-visibility.js.map +1 -1
- package/dist/indexer/expand-star-imports.d.ts +6 -1
- package/dist/indexer/expand-star-imports.d.ts.map +1 -1
- package/dist/indexer/expand-star-imports.js +131 -37
- package/dist/indexer/expand-star-imports.js.map +1 -1
- package/dist/indexer/finalize.d.ts.map +1 -1
- package/dist/indexer/finalize.js +0 -1
- package/dist/indexer/finalize.js.map +1 -1
- package/dist/indexer/import-resolution-tables.d.ts +72 -0
- package/dist/indexer/import-resolution-tables.d.ts.map +1 -0
- package/dist/indexer/import-resolution-tables.js +137 -0
- package/dist/indexer/import-resolution-tables.js.map +1 -0
- package/dist/indexer/import-types.d.ts +54 -0
- package/dist/indexer/import-types.d.ts.map +1 -1
- package/dist/indexer/import-types.js +24 -1
- package/dist/indexer/import-types.js.map +1 -1
- package/dist/indexer/imports/context.d.ts +16 -1
- package/dist/indexer/imports/context.d.ts.map +1 -1
- package/dist/indexer/imports/import-binding-tables.d.ts +13 -1
- package/dist/indexer/imports/import-binding-tables.d.ts.map +1 -1
- package/dist/indexer/imports/import-binding-tables.js +149 -30
- package/dist/indexer/imports/import-binding-tables.js.map +1 -1
- package/dist/indexer/imports/js-text-imports.js +4 -4
- package/dist/indexer/imports/js-text-imports.js.map +1 -1
- package/dist/indexer/imports/native-captures.d.ts.map +1 -1
- package/dist/indexer/imports/native-captures.js +24 -6
- package/dist/indexer/imports/native-captures.js.map +1 -1
- package/dist/indexer/imports/python.d.ts +10 -2
- package/dist/indexer/imports/python.d.ts.map +1 -1
- package/dist/indexer/imports/python.js +128 -112
- package/dist/indexer/imports/python.js.map +1 -1
- package/dist/indexer/imports/text-import-extractors.d.ts.map +1 -1
- package/dist/indexer/imports/text-import-extractors.js +68 -19
- package/dist/indexer/imports/text-import-extractors.js.map +1 -1
- package/dist/indexer/imports.d.ts +3 -4
- package/dist/indexer/imports.d.ts.map +1 -1
- package/dist/indexer/imports.js +116 -72
- package/dist/indexer/imports.js.map +1 -1
- package/dist/indexer/incremental-plan.d.ts +33 -0
- package/dist/indexer/incremental-plan.d.ts.map +1 -1
- package/dist/indexer/incremental-plan.js +203 -1
- package/dist/indexer/incremental-plan.js.map +1 -1
- package/dist/indexer/locals-and-exports.d.ts +3 -2
- package/dist/indexer/locals-and-exports.d.ts.map +1 -1
- package/dist/indexer/locals-and-exports.js +659 -78
- package/dist/indexer/locals-and-exports.js.map +1 -1
- package/dist/indexer/member-selection.d.ts +46 -0
- package/dist/indexer/member-selection.d.ts.map +1 -0
- package/dist/indexer/member-selection.js +169 -0
- package/dist/indexer/member-selection.js.map +1 -0
- package/dist/indexer/name-lookup-policies/c-family.d.ts +7 -0
- package/dist/indexer/name-lookup-policies/c-family.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/c-family.js +137 -0
- package/dist/indexer/name-lookup-policies/c-family.js.map +1 -0
- package/dist/indexer/name-lookup-policies/csharp-using-static.d.ts +11 -0
- package/dist/indexer/name-lookup-policies/csharp-using-static.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/csharp-using-static.js +136 -0
- package/dist/indexer/name-lookup-policies/csharp-using-static.js.map +1 -0
- package/dist/indexer/name-lookup-policies/implicit-self.d.ts +10 -0
- package/dist/indexer/name-lookup-policies/implicit-self.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/implicit-self.js +119 -0
- package/dist/indexer/name-lookup-policies/implicit-self.js.map +1 -0
- package/dist/indexer/name-lookup-policies/index.d.ts +3 -0
- package/dist/indexer/name-lookup-policies/index.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/index.js +132 -0
- package/dist/indexer/name-lookup-policies/index.js.map +1 -0
- package/dist/indexer/name-lookup-policies/php.d.ts +3 -0
- package/dist/indexer/name-lookup-policies/php.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/php.js +82 -0
- package/dist/indexer/name-lookup-policies/php.js.map +1 -0
- package/dist/indexer/name-resolution-types.d.ts +101 -0
- package/dist/indexer/name-resolution-types.d.ts.map +1 -0
- package/dist/indexer/name-resolution-types.js +2 -0
- package/dist/indexer/name-resolution-types.js.map +1 -0
- package/dist/indexer/name-resolution.d.ts +57 -0
- package/dist/indexer/name-resolution.d.ts.map +1 -0
- package/dist/indexer/name-resolution.js +204 -0
- package/dist/indexer/name-resolution.js.map +1 -0
- package/dist/indexer/navigation-cpp.d.ts +58 -0
- package/dist/indexer/navigation-cpp.d.ts.map +1 -0
- package/dist/indexer/navigation-cpp.js +935 -0
- package/dist/indexer/navigation-cpp.js.map +1 -0
- package/dist/indexer/navigation-goto.d.ts +115 -3
- package/dist/indexer/navigation-goto.d.ts.map +1 -1
- package/dist/indexer/navigation-goto.js +2469 -190
- package/dist/indexer/navigation-goto.js.map +1 -1
- package/dist/indexer/navigation-local.d.ts +21 -2
- package/dist/indexer/navigation-local.d.ts.map +1 -1
- package/dist/indexer/navigation-local.js +520 -63
- package/dist/indexer/navigation-local.js.map +1 -1
- package/dist/indexer/navigation-php.d.ts +66 -0
- package/dist/indexer/navigation-php.d.ts.map +1 -1
- package/dist/indexer/navigation-php.js +287 -4
- package/dist/indexer/navigation-php.js.map +1 -1
- package/dist/indexer/navigation-provenance.d.ts +1 -1
- package/dist/indexer/navigation-provenance.d.ts.map +1 -1
- package/dist/indexer/navigation-provenance.js +4 -13
- package/dist/indexer/navigation-provenance.js.map +1 -1
- package/dist/indexer/navigation-references.d.ts +73 -6
- package/dist/indexer/navigation-references.d.ts.map +1 -1
- package/dist/indexer/navigation-references.js +661 -67
- package/dist/indexer/navigation-references.js.map +1 -1
- package/dist/indexer/navigation-resolve.d.ts +46 -2
- package/dist/indexer/navigation-resolve.d.ts.map +1 -1
- package/dist/indexer/navigation-resolve.js +599 -193
- package/dist/indexer/navigation-resolve.js.map +1 -1
- package/dist/indexer/navigation.d.ts +29 -2
- package/dist/indexer/navigation.d.ts.map +1 -1
- package/dist/indexer/navigation.js +1092 -153
- package/dist/indexer/navigation.js.map +1 -1
- package/dist/indexer/parse-context.d.ts +2 -3
- package/dist/indexer/parse-context.d.ts.map +1 -1
- package/dist/indexer/parse-context.js +8 -21
- package/dist/indexer/parse-context.js.map +1 -1
- package/dist/indexer/php-namespace-symbols.d.ts +66 -0
- package/dist/indexer/php-namespace-symbols.d.ts.map +1 -0
- package/dist/indexer/php-namespace-symbols.js +342 -0
- package/dist/indexer/php-namespace-symbols.js.map +1 -0
- package/dist/indexer/reference-candidates.d.ts.map +1 -1
- package/dist/indexer/reference-candidates.js +13 -7
- package/dist/indexer/reference-candidates.js.map +1 -1
- package/dist/indexer/reference-context.d.ts +2 -0
- package/dist/indexer/reference-context.d.ts.map +1 -1
- package/dist/indexer/reference-context.js +4 -0
- package/dist/indexer/reference-context.js.map +1 -1
- package/dist/indexer/scope-nodes.d.ts +92 -13
- package/dist/indexer/scope-nodes.d.ts.map +1 -1
- package/dist/indexer/scope-nodes.js +212 -20
- package/dist/indexer/scope-nodes.js.map +1 -1
- package/dist/indexer/scope-types.d.ts +29 -1
- package/dist/indexer/scope-types.d.ts.map +1 -1
- package/dist/indexer/scope.d.ts +2 -3
- package/dist/indexer/scope.d.ts.map +1 -1
- package/dist/indexer/scope.js +968 -47
- package/dist/indexer/scope.js.map +1 -1
- package/dist/indexer/shared-owner-identity.d.ts +53 -0
- package/dist/indexer/shared-owner-identity.d.ts.map +1 -0
- package/dist/indexer/shared-owner-identity.js +460 -0
- package/dist/indexer/shared-owner-identity.js.map +1 -0
- package/dist/indexer/shared.d.ts +1 -1
- package/dist/indexer/shared.d.ts.map +1 -1
- package/dist/indexer/shared.js +1 -1
- package/dist/indexer/shared.js.map +1 -1
- package/dist/indexer/star-import-precedence.d.ts +113 -0
- package/dist/indexer/star-import-precedence.d.ts.map +1 -0
- package/dist/indexer/star-import-precedence.js +402 -0
- package/dist/indexer/star-import-precedence.js.map +1 -0
- package/dist/indexer/symbols.d.ts +3 -1
- package/dist/indexer/symbols.d.ts.map +1 -1
- package/dist/indexer/symbols.js +58 -18
- package/dist/indexer/symbols.js.map +1 -1
- package/dist/indexer/ts-callables.d.ts +42 -0
- package/dist/indexer/ts-callables.d.ts.map +1 -0
- package/dist/indexer/ts-callables.js +233 -0
- package/dist/indexer/ts-callables.js.map +1 -0
- package/dist/indexer/types.d.ts +41 -26
- package/dist/indexer/types.d.ts.map +1 -1
- package/dist/indexer/unused-exports.d.ts +13 -0
- package/dist/indexer/unused-exports.d.ts.map +1 -0
- package/dist/indexer/unused-exports.js +159 -0
- package/dist/indexer/unused-exports.js.map +1 -0
- package/dist/indexer/workspace-symbols.d.ts.map +1 -1
- package/dist/indexer/workspace-symbols.js +3 -2
- package/dist/indexer/workspace-symbols.js.map +1 -1
- package/dist/indexer.d.ts +3 -8
- package/dist/indexer.d.ts.map +1 -1
- package/dist/indexer.js +1 -1
- package/dist/indexer.js.map +1 -1
- package/dist/languages/callable-arity.d.ts +223 -0
- package/dist/languages/callable-arity.d.ts.map +1 -0
- package/dist/languages/callable-arity.js +1199 -0
- package/dist/languages/callable-arity.js.map +1 -0
- package/dist/languages/definitions/c-family.d.ts.map +1 -1
- package/dist/languages/definitions/c-family.js +30 -1
- package/dist/languages/definitions/c-family.js.map +1 -1
- package/dist/languages/definitions/c.d.ts +4 -0
- package/dist/languages/definitions/c.d.ts.map +1 -1
- package/dist/languages/definitions/c.js +19 -0
- package/dist/languages/definitions/c.js.map +1 -1
- package/dist/languages/definitions/cpp.d.ts.map +1 -1
- package/dist/languages/definitions/cpp.js +12 -0
- package/dist/languages/definitions/cpp.js.map +1 -1
- package/dist/languages/definitions/csharp.d.ts.map +1 -1
- package/dist/languages/definitions/csharp.js +2 -8
- package/dist/languages/definitions/csharp.js.map +1 -1
- package/dist/languages/definitions/go.d.ts.map +1 -1
- package/dist/languages/definitions/go.js +3 -0
- package/dist/languages/definitions/go.js.map +1 -1
- package/dist/languages/definitions/javascript.d.ts.map +1 -1
- package/dist/languages/definitions/javascript.js +10 -1
- package/dist/languages/definitions/javascript.js.map +1 -1
- package/dist/languages/definitions/js-family.d.ts +1 -1
- package/dist/languages/definitions/js-family.d.ts.map +1 -1
- package/dist/languages/definitions/js-family.js +7 -1
- package/dist/languages/definitions/js-family.js.map +1 -1
- package/dist/languages/definitions/php.d.ts.map +1 -1
- package/dist/languages/definitions/php.js +5 -1
- package/dist/languages/definitions/php.js.map +1 -1
- package/dist/languages/definitions/typescript.d.ts.map +1 -1
- package/dist/languages/definitions/typescript.js +8 -1
- package/dist/languages/definitions/typescript.js.map +1 -1
- package/dist/languages/definitions/zig.d.ts.map +1 -1
- package/dist/languages/definitions/zig.js +8 -0
- package/dist/languages/definitions/zig.js.map +1 -1
- package/dist/lifecycle/manifest.d.ts.map +1 -1
- package/dist/lifecycle/manifest.js +0 -1
- package/dist/lifecycle/manifest.js.map +1 -1
- package/dist/mcp/handlers.js +14 -7
- package/dist/mcp/handlers.js.map +1 -1
- package/dist/native/byte-index.d.ts +6 -0
- package/dist/native/byte-index.d.ts.map +1 -1
- package/dist/native/byte-index.js +11 -6
- package/dist/native/byte-index.js.map +1 -1
- package/dist/native/contracts.d.ts +2 -3
- package/dist/native/contracts.d.ts.map +1 -1
- package/dist/native/execution.d.ts +9 -9
- package/dist/native/execution.d.ts.map +1 -1
- package/dist/native/execution.js +37 -77
- package/dist/native/execution.js.map +1 -1
- package/dist/native/js-bridge.d.ts +2 -15
- package/dist/native/js-bridge.d.ts.map +1 -1
- package/dist/native/js-bridge.js +2 -15
- package/dist/native/js-bridge.js.map +1 -1
- package/dist/native/query-results.js +2 -2
- package/dist/native/query-results.js.map +1 -1
- package/dist/native/runtime.d.ts +12 -12
- package/dist/native/runtime.d.ts.map +1 -1
- package/dist/native/runtime.js +24 -69
- package/dist/native/runtime.js.map +1 -1
- package/dist/native/tree-sitter-native.d.ts +3 -3
- package/dist/native/tree-sitter-native.d.ts.map +1 -1
- package/dist/native/tree-sitter-native.js +2 -2
- package/dist/native/tree-sitter-native.js.map +1 -1
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +0 -4
- package/dist/presets.js.map +1 -1
- package/dist/review/candidates.d.ts.map +1 -1
- package/dist/review/candidates.js +1 -1
- package/dist/review/candidates.js.map +1 -1
- package/dist/review/deleted.d.ts +3 -3
- package/dist/review/deleted.d.ts.map +1 -1
- package/dist/review/deleted.js +82 -32
- package/dist/review/deleted.js.map +1 -1
- package/dist/review/report.d.ts +1 -0
- package/dist/review/report.d.ts.map +1 -1
- package/dist/review/report.js +1 -1
- package/dist/review/report.js.map +1 -1
- package/dist/review.d.ts.map +1 -1
- package/dist/review.js +3 -20
- package/dist/review.js.map +1 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +0 -5
- package/dist/session.js.map +1 -1
- package/dist/sql/navigation.d.ts.map +1 -1
- package/dist/sql/navigation.js +2 -1
- package/dist/sql/navigation.js.map +1 -1
- package/dist/sql/source-graph.d.ts.map +1 -1
- package/dist/sql/source-graph.js +2 -2
- package/dist/sql/source-graph.js.map +1 -1
- package/dist/sqlite/schema.d.ts +1 -1
- package/dist/sqlite/schema.d.ts.map +1 -1
- package/dist/sqlite/schema.js +8 -2
- package/dist/sqlite/schema.js.map +1 -1
- package/dist/sqlite/write.js +2 -2
- package/dist/sqlite/write.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/util/bloom-filter.d.ts +8 -1
- package/dist/util/bloom-filter.d.ts.map +1 -1
- package/dist/util/bloom-filter.js +15 -2
- package/dist/util/bloom-filter.js.map +1 -1
- package/dist/util/glob.d.ts +32 -0
- package/dist/util/glob.d.ts.map +1 -0
- package/dist/util/glob.js +137 -0
- package/dist/util/glob.js.map +1 -0
- package/dist/util/graph-edges.d.ts +19 -1
- package/dist/util/graph-edges.d.ts.map +1 -1
- package/dist/util/graph-edges.js +30 -4
- package/dist/util/graph-edges.js.map +1 -1
- package/dist/util/identifiers.d.ts +15 -0
- package/dist/util/identifiers.d.ts.map +1 -1
- package/dist/util/identifiers.js +35 -0
- package/dist/util/identifiers.js.map +1 -1
- package/dist/util/member-access-tables.d.ts +61 -4
- package/dist/util/member-access-tables.d.ts.map +1 -1
- package/dist/util/member-access-tables.js +139 -19
- package/dist/util/member-access-tables.js.map +1 -1
- package/dist/util/member-access.d.ts +17 -0
- package/dist/util/member-access.d.ts.map +1 -1
- package/dist/util/member-access.js +98 -3
- package/dist/util/member-access.js.map +1 -1
- package/dist/util/project-files.d.ts +2 -10
- package/dist/util/project-files.d.ts.map +1 -1
- package/dist/util/project-files.js +29 -51
- package/dist/util/project-files.js.map +1 -1
- package/dist/util/python-type-checking.d.ts +18 -0
- package/dist/util/python-type-checking.d.ts.map +1 -0
- package/dist/util/python-type-checking.js +160 -0
- package/dist/util/python-type-checking.js.map +1 -0
- package/dist/util/resolution/cargo-targets.d.ts +11 -0
- package/dist/util/resolution/cargo-targets.d.ts.map +1 -1
- package/dist/util/resolution/cargo-targets.js +174 -13
- package/dist/util/resolution/cargo-targets.js.map +1 -1
- package/dist/util/resolution/csharp.d.ts +33 -1
- package/dist/util/resolution/csharp.d.ts.map +1 -1
- package/dist/util/resolution/csharp.js +145 -30
- package/dist/util/resolution/csharp.js.map +1 -1
- package/dist/util/resolution/jvm.d.ts +7 -3
- package/dist/util/resolution/jvm.d.ts.map +1 -1
- package/dist/util/resolution/jvm.js +56 -38
- package/dist/util/resolution/jvm.js.map +1 -1
- package/dist/util/resolution/php.d.ts.map +1 -1
- package/dist/util/resolution/php.js +35 -8
- package/dist/util/resolution/php.js.map +1 -1
- package/dist/util/resolution/python.d.ts +9 -0
- package/dist/util/resolution/python.d.ts.map +1 -1
- package/dist/util/resolution/python.js +46 -0
- package/dist/util/resolution/python.js.map +1 -1
- package/dist/util/resolution/rust.d.ts +0 -1
- package/dist/util/resolution/rust.d.ts.map +1 -1
- package/dist/util/resolution/rust.js +95 -35
- package/dist/util/resolution/rust.js.map +1 -1
- package/dist/util/resolution/specifier-targets.d.ts +44 -0
- package/dist/util/resolution/specifier-targets.d.ts.map +1 -0
- package/dist/util/resolution/specifier-targets.js +152 -0
- package/dist/util/resolution/specifier-targets.js.map +1 -0
- package/dist/util/resolution-candidates.d.ts +5 -0
- package/dist/util/resolution-candidates.d.ts.map +1 -1
- package/dist/util/resolution-candidates.js +16 -3
- package/dist/util/resolution-candidates.js.map +1 -1
- package/dist/util/resolution.d.ts +2 -1
- package/dist/util/resolution.d.ts.map +1 -1
- package/dist/util/resolution.js +101 -15
- package/dist/util/resolution.js.map +1 -1
- package/dist/util/specifiers.d.ts +29 -1
- package/dist/util/specifiers.d.ts.map +1 -1
- package/dist/util/specifiers.js +56 -8
- package/dist/util/specifiers.js.map +1 -1
- package/dist/util/trivia.d.ts.map +1 -1
- package/dist/util/trivia.js +49 -30
- package/dist/util/trivia.js.map +1 -1
- package/dist/util/workspace.d.ts.map +1 -1
- package/dist/util/workspace.js +32 -4
- package/dist/util/workspace.js.map +1 -1
- package/docs/mcp.md +3 -2
- package/package.json +5 -7
- package/dist/impact/call-compatibility/providers/profiles.d.ts +0 -80
- package/dist/impact/call-compatibility/providers/profiles.d.ts.map +0 -1
- package/dist/impact/call-compatibility/providers/profiles.js +0 -164
- package/dist/impact/call-compatibility/providers/profiles.js.map +0 -1
|
@@ -1,17 +1,32 @@
|
|
|
1
|
+
import { SymbolKind } from "../../indexer/types.js";
|
|
2
|
+
import { getCompilationUnitPeers } from "../../indexer/compilation-units.js";
|
|
3
|
+
import { isExportedDeclaration, isGoExportedMemberName, isSwiftCrossFileHiddenSharedOwnerMember, isSwiftFileHiddenSharedOwnerMember, } from "../../indexer/declaration-visibility.js";
|
|
4
|
+
import { resolveCppQualifiedMemberContainer } from "../../indexer/navigation-cpp.js";
|
|
5
|
+
import { cppQualifiedOwnerHasImplicitThis, findTypeScriptNamespaceMemberCandidates, innermostNamespaceImport, isDirectKeywordMemberDeclaration, memberAcceptsCallAt, resolvePhpObjectCreationTarget, resolveRubyVisibleConstant, resolveSharedOwnerContainers, supportsDeclaredFieldChains, } from "../../indexer/navigation-goto.js";
|
|
6
|
+
import { findClosestScopeBinding } from "../../indexer/navigation-local.js";
|
|
7
|
+
import { findPhpImportAlias, inferPhpQualifiedReferenceImportType } from "../../indexer/navigation-php.js";
|
|
8
|
+
import { cjsRequireValueBinding, resolveImportTypeMember, resolvePhpExportByImportType, } from "../../indexer/navigation-resolve.js";
|
|
9
|
+
import { selectMember } from "../../indexer/member-selection.js";
|
|
10
|
+
import { effectiveExplicitBinding } from "../../indexer/star-import-precedence.js";
|
|
11
|
+
import { isSwiftConstrainedExtension, isSwiftExtensionContainer, selectCsharpPartialRepresentative, } from "../../indexer/shared-owner-identity.js";
|
|
12
|
+
import { isJsTsLanguage } from "../../languages/js-family.js";
|
|
13
|
+
import { getCallArgumentCount, memberLookupBinding } from "../../languages/callable-arity.js";
|
|
1
14
|
import { sliceText, toRange } from "../../util/ast.js";
|
|
2
|
-
import { getMemberAccessParts } from "../../util/member-access.js";
|
|
15
|
+
import { getMemberAccessParts, isMemberAccessNode, rustTokenTreeHoldsExpressions, rustTokenTreeNameFollowsSeparator, } from "../../util/member-access.js";
|
|
16
|
+
import { foldPhpIdentifierCase } from "../../util/identifiers.js";
|
|
17
|
+
import { MEMBER_ACCESS_ROWS, } from "../../util/member-access-tables.js";
|
|
3
18
|
import { fileIdentityKey } from "../../util/paths.js";
|
|
4
19
|
import { defNodeId, nodeForDef } from "../symbol-graph.js";
|
|
5
|
-
import { collectNodesByType, findFirstNodeByType, isIdentifierType } from "./ast.js";
|
|
6
|
-
import { CALL_ARGUMENT_NODE_TYPES,
|
|
20
|
+
import { collectNodesByType, declarationMemberArity, findFirstNodeByType, isIdentifierType } from "./ast.js";
|
|
21
|
+
import { CALL_ARGUMENT_NODE_TYPES, classifyReceiver, constructionTypeName, declarationIsStaticEquivalent, declarationNodeIsStatic, cppOutOfLineOwnerPath, cppOutOfLineMemberDeclarationNode, cppQualifiedNameSegments, declaresMembers, isUnprovenHeritageExpression, kotlinExtensionReceiverTypeNode, memberContainerDef, nearestMemberContainer, nodeInStaticMemberContext, phpObjectCreationKeyword, receiverCallAccess, rustImplSelfTypeNode, supportsImplicitSelfMemberCalls, supportsReceiverMemberOverloads, importTypeQuerySpecifier, } from "./receiver-calls.js";
|
|
7
22
|
function ensureNode(context, def) {
|
|
8
23
|
const id = defNodeId(def);
|
|
9
24
|
if (!context.nodes.has(id))
|
|
10
25
|
context.nodes.set(id, nodeForDef(def));
|
|
11
26
|
return id;
|
|
12
27
|
}
|
|
13
|
-
function markImplementationTarget(context, id, declarationNode, def) {
|
|
14
|
-
const declaration = sliceText(declarationNode,
|
|
28
|
+
function markImplementationTarget(context, id, declarationNode, declarationSource, def) {
|
|
29
|
+
const declaration = sliceText(declarationNode, declarationSource);
|
|
15
30
|
const nameIndex = declaration.indexOf(def.localName);
|
|
16
31
|
const prefix = nameIndex >= 0 ? declaration.slice(0, nameIndex) : declaration;
|
|
17
32
|
if (!/\b(?:abstract|virtual|override)\b/.test(prefix))
|
|
@@ -21,27 +36,49 @@ function markImplementationTarget(context, id, declarationNode, def) {
|
|
|
21
36
|
node.implementationTarget = true;
|
|
22
37
|
}
|
|
23
38
|
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)
|
|
39
|
+
const arity = declarationMemberArity(declarationNode, context.sup.id);
|
|
40
|
+
if (arity === undefined)
|
|
39
41
|
return;
|
|
40
|
-
const arity = (parameters.namedChildren ?? []).filter((child) => child.type !== "comment").length;
|
|
41
42
|
const node = context.nodes.get(id);
|
|
42
43
|
if (node)
|
|
43
44
|
node.memberArity = arity;
|
|
44
45
|
}
|
|
46
|
+
function mergeCppCallableShapes(...defs) {
|
|
47
|
+
let merged;
|
|
48
|
+
for (const def of defs) {
|
|
49
|
+
const arity = def?.callable?.arity;
|
|
50
|
+
if (!arity)
|
|
51
|
+
continue;
|
|
52
|
+
if (!merged) {
|
|
53
|
+
merged = { min: arity.minArgs, max: arity.maxArgs };
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
merged.min = Math.min(merged.min, arity.minArgs);
|
|
57
|
+
if (merged.max === null || arity.maxArgs === null)
|
|
58
|
+
merged.max = null;
|
|
59
|
+
else
|
|
60
|
+
merged.max = Math.max(merged.max, arity.maxArgs);
|
|
61
|
+
}
|
|
62
|
+
return merged;
|
|
63
|
+
}
|
|
64
|
+
/** The member call form determines whether a declared receiver takes an argument. */
|
|
65
|
+
function acceptedMemberArityRange(context, def) {
|
|
66
|
+
if (!supportsReceiverMemberOverloads(context.sup.id))
|
|
67
|
+
return undefined;
|
|
68
|
+
const callable = def.callable;
|
|
69
|
+
const range = memberLookupBinding(context.sup.id) === "unbound" ? (callable?.unboundArity ?? callable?.arity) : callable?.arity;
|
|
70
|
+
return range ? { min: range.minArgs, max: range.maxArgs } : undefined;
|
|
71
|
+
}
|
|
72
|
+
function recordMemberLookupIdentity(context, definitionId, memberId, memberScope, arityRange) {
|
|
73
|
+
context.receiverMemberScopes.set(definitionId, memberScope);
|
|
74
|
+
if (memberId !== definitionId)
|
|
75
|
+
context.receiverMemberScopes.set(memberId, memberScope);
|
|
76
|
+
if (!arityRange)
|
|
77
|
+
return;
|
|
78
|
+
context.receiverMemberArities.set(definitionId, arityRange);
|
|
79
|
+
if (memberId !== definitionId)
|
|
80
|
+
context.receiverMemberArities.set(memberId, arityRange);
|
|
81
|
+
}
|
|
45
82
|
function recordDefEdge(context, fromId, target, label, siteNode) {
|
|
46
83
|
const toId = ensureNode(context, target);
|
|
47
84
|
return context.recordEdge(fromId, toId, label, siteNode ? { file: context.moduleEntry.file, range: toRange(siteNode) } : undefined);
|
|
@@ -56,10 +93,27 @@ function tryResolveChain(context, node, fromId, label = "uses") {
|
|
|
56
93
|
}
|
|
57
94
|
/** Records an edge for a resolvable target node. Returns whether a target was resolved. */
|
|
58
95
|
function tryResolveNode(context, node, fromId, label) {
|
|
59
|
-
if (
|
|
96
|
+
if (context.sup.id === "cpp" && node.type === "qualified_identifier") {
|
|
97
|
+
const name = cppQualifiedNameSegments(node, context.source).join("::");
|
|
98
|
+
const target = context.resolveIdentifier(name, node);
|
|
99
|
+
if (target) {
|
|
100
|
+
recordDefEdge(context, fromId, target, label, node);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
if (isIdentifierType(context.sup, node.type) ||
|
|
106
|
+
node.type === "type_identifier" ||
|
|
107
|
+
(context.sup.id === "csharp" && (node.type === "qualified_name" || node.type === "alias_qualified_name")) ||
|
|
108
|
+
(context.sup.id === "php" && (node.type === "qualified_name" || node.type === "relative_name")) ||
|
|
109
|
+
(context.sup.id === "cpp" && (node.type === "operator_name" || node.type === "destructor_name"))) {
|
|
60
110
|
const name = sliceText(node, context.source);
|
|
61
111
|
const target = context.resolveIdentifier(name, node);
|
|
62
112
|
if (target) {
|
|
113
|
+
// Zig struct declarations require an explicit type receiver (`Self.helper()`).
|
|
114
|
+
// An unqualified identifier is not a call to a member of its enclosing struct.
|
|
115
|
+
if (label === "calls" && context.sup.id === "zig" && target.isMember)
|
|
116
|
+
return false;
|
|
63
117
|
recordDefEdge(context, fromId, target, label, node);
|
|
64
118
|
return true;
|
|
65
119
|
}
|
|
@@ -69,6 +123,32 @@ function tryResolveNode(context, node, fromId, label) {
|
|
|
69
123
|
}
|
|
70
124
|
return false;
|
|
71
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* Rust macro arguments stay unparsed token trees, so `println!("{}", greet())` never forms a
|
|
128
|
+
* call node even though goto resolves the identifier. An `identifier` directly followed by a
|
|
129
|
+
* `(` group is a call in the argument list, resolved like a bare call. The macro's own name
|
|
130
|
+
* sits outside the token tree and is never a call; a `!` gap marks a nested macro invocation,
|
|
131
|
+
* and a `.` or `::` gap marks a member or path receiver that raw tokens cannot prove, matching
|
|
132
|
+
* navigation's refusals inside token trees.
|
|
133
|
+
*/
|
|
134
|
+
function recordRustMacroArgumentCalls(context, tokenTree, fromId) {
|
|
135
|
+
if (!rustTokenTreeHoldsExpressions(tokenTree))
|
|
136
|
+
return;
|
|
137
|
+
const children = tokenTree.namedChildren;
|
|
138
|
+
for (let index = 0; index + 1 < children.length; index += 1) {
|
|
139
|
+
const name = children[index];
|
|
140
|
+
const args = children[index + 1];
|
|
141
|
+
if (name.type !== "identifier" || args.type !== "token_tree")
|
|
142
|
+
continue;
|
|
143
|
+
if (!args.text.startsWith("("))
|
|
144
|
+
continue;
|
|
145
|
+
if (context.source.slice(name.endIndex, args.startIndex).trim())
|
|
146
|
+
continue;
|
|
147
|
+
if (rustTokenTreeNameFollowsSeparator(name))
|
|
148
|
+
continue;
|
|
149
|
+
tryResolveNode(context, name, fromId, "calls");
|
|
150
|
+
}
|
|
151
|
+
}
|
|
72
152
|
function getCallTarget(node) {
|
|
73
153
|
const explicitTarget = node.childForFieldName("function") ??
|
|
74
154
|
node.childForFieldName("callee") ??
|
|
@@ -76,6 +156,11 @@ function getCallTarget(node) {
|
|
|
76
156
|
node.childForFieldName("method") ??
|
|
77
157
|
node.childForFieldName("member") ??
|
|
78
158
|
node.childForFieldName("expression");
|
|
159
|
+
// tree-sitter-typescript parses `await f<T>(x)` as a call whose callee is `await f`; the awaited
|
|
160
|
+
// value is the call's result, so the callee is `f`.
|
|
161
|
+
if (explicitTarget?.type === "await_expression" && node.childForFieldName("type_arguments")) {
|
|
162
|
+
return explicitTarget.namedChildren[0] ?? null;
|
|
163
|
+
}
|
|
79
164
|
if (explicitTarget)
|
|
80
165
|
return explicitTarget;
|
|
81
166
|
// Kotlin and Swift calls name no callee field, so the sole non-argument child is it.
|
|
@@ -83,15 +168,18 @@ function getCallTarget(node) {
|
|
|
83
168
|
return nonArgumentChildren.length === 1 ? (nonArgumentChildren[0] ?? null) : null;
|
|
84
169
|
}
|
|
85
170
|
function getNewTarget(node) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
171
|
+
const field = node.childForFieldName("constructor") ?? node.childForFieldName("type") ?? node.childForFieldName("name");
|
|
172
|
+
if (field)
|
|
173
|
+
return field;
|
|
174
|
+
// PHP `new Base()` has no type field; the type is a `name` / `qualified_name` child and
|
|
175
|
+
// `child(0)` is the `new` keyword, which is not a type.
|
|
176
|
+
const namedType = node.namedChildren.find((child) => child.type === "type_identifier" ||
|
|
177
|
+
child.type === "name" ||
|
|
178
|
+
child.type === "qualified_name" ||
|
|
179
|
+
child.type === "relative_name");
|
|
180
|
+
return namedType ?? node.child(0);
|
|
91
181
|
}
|
|
92
182
|
export function emitPythonDecoratorEdges(context, rootNode) {
|
|
93
|
-
if (context.sup.id !== "python")
|
|
94
|
-
return;
|
|
95
183
|
const addDecoratorUses = (node) => {
|
|
96
184
|
if (node.type === "decorated_definition") {
|
|
97
185
|
const fn = node.namedChildren.find((child) => child.type === "function_definition");
|
|
@@ -151,34 +239,220 @@ export function emitPythonDecoratorEdges(context, rootNode) {
|
|
|
151
239
|
};
|
|
152
240
|
addDecoratorUses(rootNode);
|
|
153
241
|
}
|
|
154
|
-
|
|
242
|
+
/** Whether a function declaration can participate in class member lookup and ownership. */
|
|
243
|
+
function isClassMemberFunction(fn) {
|
|
244
|
+
return fn.node.type !== "local_function_statement";
|
|
245
|
+
}
|
|
246
|
+
export async function emitMemberOwnershipEdges(context, functionNodes, classNodes) {
|
|
155
247
|
for (const fn of functionNodes) {
|
|
156
|
-
const
|
|
157
|
-
if (!
|
|
248
|
+
const owner = await memberOwner(context, fn, classNodes);
|
|
249
|
+
if (!owner)
|
|
158
250
|
continue;
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
251
|
+
const outOfLineDeclaration = owner.cppOutOfLine
|
|
252
|
+
? await cppOutOfLineMemberDeclaration(context, fn, owner.def)
|
|
253
|
+
: null;
|
|
254
|
+
const definitionId = ensureNode(context, fn.def);
|
|
255
|
+
let memberDef = fn.def;
|
|
256
|
+
if (outOfLineDeclaration) {
|
|
257
|
+
const declarationModule = context.index.byFile.get(fileIdentityKey(owner.def.file));
|
|
258
|
+
const declarationDef = declarationModule?.locals.find((candidate) => candidate.localName === fn.def.localName &&
|
|
259
|
+
candidate.kind === fn.def.kind &&
|
|
260
|
+
candidate.range.start.index === outOfLineDeclaration.nameNode.startIndex &&
|
|
261
|
+
candidate.range.end.index === outOfLineDeclaration.nameNode.endIndex);
|
|
262
|
+
if (declarationDef)
|
|
263
|
+
memberDef = declarationDef;
|
|
264
|
+
}
|
|
265
|
+
const memberId = ensureNode(context, memberDef);
|
|
266
|
+
if (definitionId !== memberId)
|
|
267
|
+
context.nodeAliases.set(definitionId, memberId);
|
|
268
|
+
if (context.sup.id === "java" || context.sup.id === "kotlin" || context.sup.id === "swift") {
|
|
269
|
+
const declarationNode = outOfLineDeclaration?.node ?? fn.node;
|
|
270
|
+
const hidden = context.sup.id === "swift"
|
|
271
|
+
? isSwiftFileHiddenSharedOwnerMember(context.sup.id, declarationNode)
|
|
272
|
+
: !isExportedDeclaration(context.sup.id, declarationNode);
|
|
273
|
+
if (hidden) {
|
|
274
|
+
context.fileHiddenMemberIds.add(memberId);
|
|
275
|
+
if (definitionId !== memberId)
|
|
276
|
+
context.fileHiddenMemberIds.add(definitionId);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
markImplementationTarget(context, memberId, outOfLineDeclaration?.node ?? fn.node, outOfLineDeclaration?.source ?? context.source, fn.def);
|
|
280
|
+
const arityNode = outOfLineDeclaration?.node ?? fn.node;
|
|
281
|
+
markMemberArity(context, definitionId, arityNode);
|
|
282
|
+
if (memberId !== definitionId)
|
|
283
|
+
markMemberArity(context, memberId, arityNode);
|
|
284
|
+
const memberScope = memberScopeForDefinition(context, fn, owner.cppOutOfLine, outOfLineDeclaration);
|
|
285
|
+
const outOfLineMember = outOfLineDeclaration ? memberDef : undefined;
|
|
286
|
+
const arityRange = context.sup.id === "cpp"
|
|
287
|
+
? mergeCppCallableShapes(fn.def, outOfLineMember)
|
|
288
|
+
: acceptedMemberArityRange(context, memberDef);
|
|
289
|
+
recordMemberLookupIdentity(context, definitionId, memberId, memberScope, arityRange);
|
|
290
|
+
recordDefEdge(context, definitionId, owner.def, "member_of");
|
|
291
|
+
await emitSharedOwnerMembershipEdges(context, owner, definitionId, fn.node);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/** Owner def of a shared-owner container, matched by its declaration name range. */
|
|
295
|
+
function sharedOwnerPeerDef(peer, callerFile) {
|
|
296
|
+
const nameNode = peer.container.childForFieldName("name");
|
|
297
|
+
if (!nameNode)
|
|
298
|
+
return null;
|
|
299
|
+
const def = peer.module.locals.find((local) => declaresMembers(local) && local.range.start.index === nameNode.startIndex);
|
|
300
|
+
if (!def)
|
|
301
|
+
return null;
|
|
302
|
+
if (!callerFile)
|
|
303
|
+
return { def, isExtension: isSwiftExtensionContainer(peer.container, peer.context.source) };
|
|
304
|
+
const visibleMemberIds = [];
|
|
305
|
+
for (const local of peer.module.locals) {
|
|
306
|
+
const start = local.range.start.index;
|
|
307
|
+
if (start === undefined || start < peer.container.startIndex || start >= peer.container.endIndex)
|
|
308
|
+
continue;
|
|
309
|
+
const name = peer.context.tree.rootNode.descendantForIndex(start, start);
|
|
310
|
+
if (!isDirectKeywordMemberDeclaration(name, peer.container))
|
|
311
|
+
continue;
|
|
312
|
+
if (isSwiftCrossFileHiddenSharedOwnerMember("swift", callerFile, peer.file, name))
|
|
313
|
+
continue;
|
|
314
|
+
visibleMemberIds.push(defNodeId(local));
|
|
315
|
+
}
|
|
316
|
+
return { def, isExtension: isSwiftExtensionContainer(peer.container, peer.context.source), visibleMemberIds };
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* C# partial declarations and unconstrained Swift extensions share type membership
|
|
320
|
+
* across proven peers. Constrained Swift extensions instead retain their own members
|
|
321
|
+
* and record only the peer members their lexical owner can use. This keeps unproven
|
|
322
|
+
* receivers from inheriting constrained members while preserving calls inside the
|
|
323
|
+
* extension. Same-named types in other namespaces or paths remain separate.
|
|
324
|
+
*/
|
|
325
|
+
async function emitSharedOwnerMembershipEdges(context, owner, definitionId, memberNode) {
|
|
326
|
+
const container = owner.container;
|
|
327
|
+
if (!container)
|
|
328
|
+
return;
|
|
329
|
+
if (context.sup.id !== "csharp" && context.sup.id !== "swift")
|
|
330
|
+
return;
|
|
331
|
+
const isExtension = context.sup.id === "swift" && isSwiftExtensionContainer(container, context.source);
|
|
332
|
+
// Swift base types already own their full member set; only extensions and C#
|
|
333
|
+
// partials reach across owner declarations. Non-partial C# containers resolve
|
|
334
|
+
// to no peers inside the shared-owner relation.
|
|
335
|
+
// Constrained extensions can use nominal members but cannot donate their own
|
|
336
|
+
// members to every instance of the nominal type without proving the where clause.
|
|
337
|
+
const constrained = context.sup.id === "swift" && isSwiftConstrainedExtension(container, context.source);
|
|
338
|
+
if (constrained && context.sharedOwnerAccessibleMembers.has(defNodeId(owner.def)))
|
|
339
|
+
return;
|
|
340
|
+
const cacheKey = `${fileIdentityKey(context.moduleEntry.file)}\u0000${container.startIndex}\u0000${container.endIndex}`;
|
|
341
|
+
let peers = context.sharedOwnerPeers.get(cacheKey);
|
|
342
|
+
if (!peers) {
|
|
343
|
+
peers = resolveSharedOwnerContainers({
|
|
344
|
+
index: context.index,
|
|
345
|
+
ownerFile: context.moduleEntry.file,
|
|
346
|
+
ownerContainer: container,
|
|
347
|
+
ownerSource: context.source,
|
|
348
|
+
languageId: context.sup.id,
|
|
349
|
+
}).then((resolved) => resolved
|
|
350
|
+
.map((peer) => sharedOwnerPeerDef(peer, constrained ? context.moduleEntry.file : undefined))
|
|
351
|
+
.filter((peer) => !!peer));
|
|
352
|
+
context.sharedOwnerPeers.set(cacheKey, peers);
|
|
353
|
+
}
|
|
354
|
+
const peerDefs = await peers;
|
|
355
|
+
if (constrained) {
|
|
356
|
+
const ownerId = defNodeId(owner.def);
|
|
357
|
+
const accessible = context.sharedOwnerAccessibleMembers.get(ownerId) ?? new Set();
|
|
358
|
+
for (const peer of peerDefs)
|
|
359
|
+
for (const memberId of peer.visibleMemberIds ?? [])
|
|
360
|
+
accessible.add(memberId);
|
|
361
|
+
context.sharedOwnerAccessibleMembers.set(ownerId, accessible);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
for (const peer of peerDefs) {
|
|
365
|
+
if (isSwiftCrossFileHiddenSharedOwnerMember(context.sup.id, peer.def.file, context.moduleEntry.file, memberNode)) {
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
recordDefEdge(context, definitionId, peer.def, "member_of");
|
|
369
|
+
}
|
|
370
|
+
if (isExtension) {
|
|
371
|
+
const anchor = peerDefs.find((peer) => !peer.isExtension);
|
|
372
|
+
context.sharedOwnerAnchors.set(defNodeId(owner.def), defNodeId(anchor ? anchor.def : owner.def));
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
if (context.sup.id === "csharp" && peerDefs.length) {
|
|
376
|
+
const representative = selectCsharpPartialRepresentative([owner.def, ...peerDefs.map((peer) => peer.def)]);
|
|
377
|
+
context.sharedOwnerAnchors.set(defNodeId(owner.def), defNodeId(representative));
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
function memberScopeForDefinition(context, fn, cppOutOfLine, outOfLineDeclaration) {
|
|
381
|
+
if (outOfLineDeclaration) {
|
|
382
|
+
return declarationNodeIsStatic(outOfLineDeclaration.node, outOfLineDeclaration.source) ? "static" : "instance";
|
|
163
383
|
}
|
|
384
|
+
if (cppOutOfLine)
|
|
385
|
+
return "any";
|
|
386
|
+
// Kotlin has no `static` keyword; a member of a `companion object` or a named `object`
|
|
387
|
+
// declaration is the language's static-equivalent mechanism (reachable as `Outer.member()`).
|
|
388
|
+
if (declarationIsStaticEquivalent(context.sup.id, fn.node))
|
|
389
|
+
return "static";
|
|
390
|
+
const declarationNode = fn.node.parent?.type === "public_field_definition" || fn.node.parent?.type === "field_definition"
|
|
391
|
+
? fn.node.parent
|
|
392
|
+
: fn.node;
|
|
393
|
+
return declarationNodeIsStatic(declarationNode, context.source) ? "static" : "instance";
|
|
164
394
|
}
|
|
165
|
-
/** Lexical
|
|
166
|
-
function
|
|
395
|
+
/** Lexical type body, named Go receiver type, or named C++ out-of-line owner. */
|
|
396
|
+
async function memberOwner(context, fn, classNodes) {
|
|
397
|
+
if (!isClassMemberFunction(fn))
|
|
398
|
+
return null;
|
|
167
399
|
if (context.sup.id === "go" && fn.node.type === "method_declaration") {
|
|
168
|
-
|
|
400
|
+
const def = goMethodReceiverTypeDef(context, fn.node);
|
|
401
|
+
return def ? { def, container: null, cppOutOfLine: false } : null;
|
|
169
402
|
}
|
|
170
403
|
const owners = classNodes
|
|
171
404
|
.filter((candidate) => candidate.node.startIndex <= fn.node.startIndex && candidate.node.endIndex >= fn.node.endIndex)
|
|
172
405
|
.sort((left, right) => left.node.endIndex - left.node.startIndex - (right.node.endIndex - right.node.startIndex));
|
|
173
|
-
if (owners[0]?.def)
|
|
174
|
-
|
|
406
|
+
if (owners[0]?.def) {
|
|
407
|
+
if (!isDirectKeywordMemberDeclaration(fn.node, owners[0].node))
|
|
408
|
+
return null;
|
|
409
|
+
return { def: owners[0].def, container: owners[0].node, cppOutOfLine: false };
|
|
410
|
+
}
|
|
411
|
+
if (context.sup.id === "cpp") {
|
|
412
|
+
const ownerPath = cppOutOfLineOwnerPath(fn.node, context.source, context.sup);
|
|
413
|
+
const def = ownerPath
|
|
414
|
+
? await resolveCppQualifiedMemberContainer(context.index, context.moduleEntry, ownerPath, context.loadParsedFile)
|
|
415
|
+
: null;
|
|
416
|
+
return def ? { def, container: null, cppOutOfLine: true } : null;
|
|
417
|
+
}
|
|
418
|
+
if (context.sup.id === "kotlin") {
|
|
419
|
+
// `fun Widget.describe()` is a top-level declaration outside Widget's own body, so it is
|
|
420
|
+
// never one of `owners` above; its receiver-type prefix is the only proof of ownership.
|
|
421
|
+
const receiverTypeNode = kotlinExtensionReceiverTypeNode(fn.node, context.sup);
|
|
422
|
+
if (!receiverTypeNode)
|
|
423
|
+
return null;
|
|
424
|
+
const def = resolveNamedType(context, sliceText(receiverTypeNode, context.source), receiverTypeNode);
|
|
425
|
+
return def ? { def, container: null, cppOutOfLine: false } : null;
|
|
426
|
+
}
|
|
427
|
+
if (context.sup.id === "rust") {
|
|
428
|
+
// A Rust impl method sits beside its self type, not inside it (like Go's receiver type);
|
|
429
|
+
// the `impl` block's self type is the owner, and only direct impl members count.
|
|
430
|
+
const container = nearestMemberContainer(fn.node);
|
|
431
|
+
if (!container || container.type !== "impl_item" || !isDirectKeywordMemberDeclaration(fn.node, container)) {
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
const nameNode = rustImplSelfTypeNode(container, context.sup);
|
|
435
|
+
const def = nameNode ? resolveNamedType(context, sliceText(nameNode, context.source), nameNode) : null;
|
|
436
|
+
return def ? { def, container, cppOutOfLine: false } : null;
|
|
437
|
+
}
|
|
175
438
|
if (context.sup.id !== "zig")
|
|
176
439
|
return null;
|
|
177
440
|
const container = nearestMemberContainer(fn.node);
|
|
178
441
|
if (container?.type !== "struct_declaration")
|
|
179
442
|
return null;
|
|
180
443
|
const name = container.parent?.namedChildren.find((child) => child.type === "identifier");
|
|
181
|
-
|
|
444
|
+
const def = name ? resolveNamedType(context, sliceText(name, context.source), name) : null;
|
|
445
|
+
return def ? { def, container, cppOutOfLine: false } : null;
|
|
446
|
+
}
|
|
447
|
+
async function cppOutOfLineMemberDeclaration(context, fn, ownerDef) {
|
|
448
|
+
const parsed = await context.loadParsedFile(ownerDef.file);
|
|
449
|
+
const startIndex = ownerDef.range.start.index;
|
|
450
|
+
const endIndex = ownerDef.range.end.index;
|
|
451
|
+
if (!parsed || startIndex === undefined || endIndex === undefined)
|
|
452
|
+
return null;
|
|
453
|
+
const ownerNameNode = parsed.tree.rootNode.descendantForIndex(startIndex, endIndex);
|
|
454
|
+
const declaration = cppOutOfLineMemberDeclarationNode(fn.node, fn.def.localName, ownerNameNode, parsed.source, context.sup);
|
|
455
|
+
return declaration ? { node: declaration.node, nameNode: declaration.nameNode, source: parsed.source } : null;
|
|
182
456
|
}
|
|
183
457
|
/** Receiver type of `func (b *T) M()` / `func (b T) M()`, unwrapped through pointers. */
|
|
184
458
|
function goMethodReceiverTypeDef(context, methodNode) {
|
|
@@ -208,16 +482,347 @@ function unwrapGoNamedType(node) {
|
|
|
208
482
|
return current?.type === "type_identifier" ? current : null;
|
|
209
483
|
}
|
|
210
484
|
/** Type-like defs only, so a PHP `use function` alias cannot steal `Example::m()`. */
|
|
211
|
-
function resolveNamedType(context, name, node) {
|
|
485
|
+
function resolveNamedType(context, name, node, rubyConstructed = false) {
|
|
486
|
+
// A Go composite literal or Zig struct initializer can name a qualified cross-file type
|
|
487
|
+
// (`pkg.Type{}` / `ns.Type{}`); the receiver's typeName is then dotted text, not one
|
|
488
|
+
// identifier, so resolve the chain directly instead of treating it as a plain name.
|
|
489
|
+
if ((context.sup.id === "go" || context.sup.id === "zig") && context.optionalMemberTypes.has(node.type)) {
|
|
490
|
+
const qualified = context.resolveMemberChainTarget(node);
|
|
491
|
+
return qualified && declaresMembers(qualified) ? qualified : null;
|
|
492
|
+
}
|
|
212
493
|
const target = context.resolveIdentifier(name, node);
|
|
213
494
|
if (target && declaresMembers(target))
|
|
214
495
|
return target;
|
|
215
496
|
// A parameter/annotation type name is a closer scope binding than the class it names.
|
|
216
497
|
const normalized = context.sup.normalizeIdentifier(name);
|
|
217
498
|
const typed = context.moduleEntry.locals.filter((local) => context.sup.normalizeIdentifier(local.localName) === normalized && declaresMembers(local));
|
|
218
|
-
|
|
499
|
+
if (typed.length === 1)
|
|
500
|
+
return typed[0];
|
|
501
|
+
const imported = context.aliasToTargetDef.get(name);
|
|
502
|
+
if (imported && declaresMembers(imported))
|
|
503
|
+
return imported;
|
|
504
|
+
// A constructed `Klass.new` type, or (Ruby only) a bare capitalized constant receiver
|
|
505
|
+
// (`Calc.add(1, 2)`), uses Ruby's bare-constant visibility, and only when this file does not
|
|
506
|
+
// already have several classes of that name. Inheritance names stay on resolveIdentifier so
|
|
507
|
+
// an unproven superclass is not invented.
|
|
508
|
+
const triesRubyVisibleConstant = rubyConstructed || context.sup.id === "ruby";
|
|
509
|
+
if (!triesRubyVisibleConstant || typed.length > 1)
|
|
510
|
+
return null;
|
|
511
|
+
return resolveRubyVisibleConstant(context.index, context.moduleEntry, context.sup, name);
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Zig's static-receiver call form (`module.Type.member()`): the receiver is itself a qualified
|
|
515
|
+
* member-access node naming a type through another file's export (`counter.Counter` in
|
|
516
|
+
* `counter.Counter.zero()`). `classifyReceiver` only proves an identifier or keyword receiver,
|
|
517
|
+
* so this resolves the receiver's own chain directly and defers the member lookup through the
|
|
518
|
+
* same cross-file receiver-call pipeline every other member call uses.
|
|
519
|
+
*/
|
|
520
|
+
function recordZigQualifiedTypeReceiverCall(context, node, access, fromId) {
|
|
521
|
+
const typeDef = context.resolveMemberChainTarget(access.receiver);
|
|
522
|
+
if (!typeDef || !declaresMembers(typeDef))
|
|
523
|
+
return false;
|
|
524
|
+
const memberName = sliceText(access.property, context.source);
|
|
525
|
+
if (!memberName)
|
|
526
|
+
return false;
|
|
527
|
+
const argumentCount = supportsReceiverMemberOverloads(context.sup.id)
|
|
528
|
+
? getCallArgumentCount({ languageId: context.sup.id, source: context.source, call: node })
|
|
529
|
+
: null;
|
|
530
|
+
context.receiverCalls.push({
|
|
531
|
+
callerId: fromId,
|
|
532
|
+
ownerId: ensureNode(context, typeDef),
|
|
533
|
+
viaSupertypes: false,
|
|
534
|
+
memberName,
|
|
535
|
+
argumentCount,
|
|
536
|
+
site: { file: context.moduleEntry.file, range: toRange(access.property) },
|
|
537
|
+
memberScope: "any",
|
|
538
|
+
});
|
|
539
|
+
return true;
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Go's `d.Base.Run()`: `d.Base` explicitly names an embedded field to reach a promoted method
|
|
543
|
+
* that a derived type's own same-named method would otherwise shadow, Go's equivalent of
|
|
544
|
+
* `super`/`base`. `classifyReceiver` only proves an identifier or keyword receiver, so a
|
|
545
|
+
* dotted `d.Base` receiver is never classified. This resolves `d`'s own type, confirms `Base`
|
|
546
|
+
* is really a nameless embedded field declared on it (not an unrelated named field that merely
|
|
547
|
+
* shares the name), then defers the member lookup through the same cross-file receiver-call
|
|
548
|
+
* pipeline every other member call uses. Limited to a same-file owner declaration: the AST
|
|
549
|
+
* needed to read the field list is only synchronously available there.
|
|
550
|
+
*/
|
|
551
|
+
function recordGoEmbeddedFieldReceiverCall(context, access, fromId, hasLexicalBinding) {
|
|
552
|
+
const parts = getMemberAccessParts(context.sup, access.receiver);
|
|
553
|
+
const objectNode = parts.object;
|
|
554
|
+
const fieldNode = parts.property;
|
|
555
|
+
if (!objectNode || !fieldNode || !isIdentifierType(context.sup, fieldNode.type))
|
|
556
|
+
return false;
|
|
557
|
+
const ownerBinding = classifyReceiver(context.sup, objectNode, context.source, null, 0, access.receiver, hasLexicalBinding);
|
|
558
|
+
if (ownerBinding?.kind !== "named-type")
|
|
559
|
+
return false;
|
|
560
|
+
const ownerDef = resolveNamedType(context, ownerBinding.typeName, ownerBinding.typeNode, ownerBinding.constructed);
|
|
561
|
+
if (!ownerDef || fileIdentityKey(ownerDef.file) !== fileIdentityKey(context.moduleEntry.file))
|
|
562
|
+
return false;
|
|
563
|
+
const embeds = MEMBER_ACCESS_ROWS.go?.receiverAncestry?.embeds;
|
|
564
|
+
if (!embeds)
|
|
565
|
+
return false;
|
|
566
|
+
const start = ownerDef.range.start.index ?? 0;
|
|
567
|
+
const nameNode = context.tree.rootNode.descendantForIndex(start, ownerDef.range.end.index ?? start);
|
|
568
|
+
const declaration = nameNode.parent;
|
|
569
|
+
const declaredType = declaration?.childForFieldName("type");
|
|
570
|
+
if (!declaration || !declaredType)
|
|
571
|
+
return false;
|
|
572
|
+
const fieldName = sliceText(fieldNode, context.source);
|
|
573
|
+
for (const rule of embeds) {
|
|
574
|
+
if (declaredType.type !== rule.body)
|
|
575
|
+
continue;
|
|
576
|
+
const list = rule.memberList ? findFirstNodeByType(declaredType, rule.memberList) : declaredType;
|
|
577
|
+
if (!list)
|
|
578
|
+
continue;
|
|
579
|
+
for (const member of list.namedChildren ?? []) {
|
|
580
|
+
if (member.type !== rule.member)
|
|
581
|
+
continue;
|
|
582
|
+
if (rule.nameless && member.childForFieldName("name"))
|
|
583
|
+
continue;
|
|
584
|
+
const specifier = rule.typeField ? member.childForFieldName(rule.typeField) : member;
|
|
585
|
+
if (!specifier || sliceText(specifier, context.source) !== fieldName)
|
|
586
|
+
continue;
|
|
587
|
+
const embeddedDef = context.resolveIdentifier(fieldName, specifier);
|
|
588
|
+
if (!embeddedDef || !declaresMembers(embeddedDef))
|
|
589
|
+
return false;
|
|
590
|
+
const memberName = sliceText(access.property, context.source);
|
|
591
|
+
if (!memberName)
|
|
592
|
+
return false;
|
|
593
|
+
context.receiverCalls.push({
|
|
594
|
+
callerId: fromId,
|
|
595
|
+
ownerId: ensureNode(context, embeddedDef),
|
|
596
|
+
viaSupertypes: false,
|
|
597
|
+
memberName,
|
|
598
|
+
argumentCount: null,
|
|
599
|
+
site: { file: context.moduleEntry.file, range: toRange(access.property) },
|
|
600
|
+
memberScope: "any",
|
|
601
|
+
});
|
|
602
|
+
return true;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
return false;
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Collection can attach an arrow to any same-name local when the binding site is
|
|
609
|
+
* not that local (`obj.helper = () => 1` beside `const helper = 1`). Prove that
|
|
610
|
+
* this function is the value of `fn.def` itself: the declarator name is that
|
|
611
|
+
* definition, or an identifier assignment resolves to it. Member and pattern
|
|
612
|
+
* left-hand sides do not prove a local binding.
|
|
613
|
+
*/
|
|
614
|
+
function provesCallableBinding(context, fn) {
|
|
615
|
+
const parent = fn.node.parent;
|
|
616
|
+
if (!parent)
|
|
617
|
+
return false;
|
|
618
|
+
const start = fn.def.range.start.index;
|
|
619
|
+
const end = fn.def.range.end.index;
|
|
620
|
+
if (start === undefined || end === undefined)
|
|
621
|
+
return false;
|
|
622
|
+
const sameNodeRange = (candidate) => !!candidate && candidate.startIndex === fn.node.startIndex && candidate.endIndex === fn.node.endIndex;
|
|
623
|
+
if ((parent.type === "variable_declarator" ||
|
|
624
|
+
parent.type === "public_field_definition" ||
|
|
625
|
+
parent.type === "field_definition") &&
|
|
626
|
+
sameNodeRange(parent.childForFieldName("value"))) {
|
|
627
|
+
const bindingName = parent.childForFieldName("name") ?? parent.childForFieldName("property");
|
|
628
|
+
return (!!bindingName &&
|
|
629
|
+
(isIdentifierType(context.sup, bindingName.type) || context.propertyIdentifierTypes.includes(bindingName.type)) &&
|
|
630
|
+
bindingName.startIndex === start &&
|
|
631
|
+
bindingName.endIndex === end);
|
|
632
|
+
}
|
|
633
|
+
if (parent.type === "assignment_expression" && sameNodeRange(parent.childForFieldName("right"))) {
|
|
634
|
+
const left = parent.childForFieldName("left");
|
|
635
|
+
if (!left || !isIdentifierType(context.sup, left.type))
|
|
636
|
+
return false;
|
|
637
|
+
const resolved = context.resolveIdentifier(sliceText(left, context.source), left);
|
|
638
|
+
return !!resolved && defNodeId(resolved) === defNodeId(fn.def);
|
|
639
|
+
}
|
|
640
|
+
return false;
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Whether a resolved member can be a `calls` target: a function, a class, or a binding the
|
|
644
|
+
* declaration pass proved holds a function. A plain value (`export const value = 1`) is not.
|
|
645
|
+
*/
|
|
646
|
+
/** A JS/TS class is not callable without `new`; construction is an `instantiates` edge instead. */
|
|
647
|
+
function isCallTarget(context, def) {
|
|
648
|
+
return def.kind === SymbolKind.Function || !!context.nodes.get(defNodeId(def))?.callable;
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* A member of a proven same-file TS namespace receiver, selected with navigation's container and
|
|
652
|
+
* overload rules. `undefined` means the receiver is not such a namespace.
|
|
653
|
+
*/
|
|
654
|
+
function resolveTypeScriptNamespaceMember(context, call, receiverNode, property) {
|
|
655
|
+
if ((context.sup.id !== "ts" && context.sup.id !== "tsx") || !isIdentifierType(context.sup, receiverNode.type))
|
|
656
|
+
return undefined;
|
|
657
|
+
const receiver = context.resolveIdentifier(sliceText(receiverNode, context.source), receiverNode);
|
|
658
|
+
if (!receiver || fileIdentityKey(receiver.file) !== fileIdentityKey(context.moduleEntry.file))
|
|
659
|
+
return undefined;
|
|
660
|
+
const member = sliceText(property, context.source);
|
|
661
|
+
const candidates = findTypeScriptNamespaceMemberCandidates(context.moduleEntry.locals, member, receiver, context);
|
|
662
|
+
if (!candidates.length)
|
|
663
|
+
return undefined;
|
|
664
|
+
const selected = selectMember([receiver], {
|
|
665
|
+
ownerKey: (def) => def.callable?.owner ?? fileIdentityKey(def.file),
|
|
666
|
+
members: () => candidates,
|
|
667
|
+
supertypes: () => [],
|
|
668
|
+
name: (def) => def.localName,
|
|
669
|
+
key: (def) => def.callable?.key ?? `${fileIdentityKey(def.file)}:${def.range.start.index}`,
|
|
670
|
+
callable: (def) => def.callable,
|
|
671
|
+
scope: () => "any",
|
|
672
|
+
visible: () => true,
|
|
673
|
+
}, {
|
|
674
|
+
name: member,
|
|
675
|
+
argumentCount: getCallArgumentCount({ languageId: context.sup.id, source: context.source, call }),
|
|
676
|
+
scope: "any",
|
|
677
|
+
useFile: context.moduleEntry.file,
|
|
678
|
+
});
|
|
679
|
+
return selected.status === "unique" ? selected.member : null;
|
|
680
|
+
}
|
|
681
|
+
function recordTypeScriptNamespaceCall(context, call, access, fromId) {
|
|
682
|
+
const selected = resolveTypeScriptNamespaceMember(context, call, access.receiver, access.property);
|
|
683
|
+
if (selected === undefined)
|
|
684
|
+
return false;
|
|
685
|
+
if (selected && isCallTarget(context, selected))
|
|
686
|
+
recordDefEdge(context, fromId, selected, "calls", access.property);
|
|
687
|
+
return true;
|
|
688
|
+
}
|
|
689
|
+
/** `new N.C()` on a same-file TS namespace constructs the namespace's class. */
|
|
690
|
+
function recordTypeScriptNamespaceConstruction(context, node, target, fromId) {
|
|
691
|
+
if (!isMemberAccessNode(context.sup, target))
|
|
692
|
+
return false;
|
|
693
|
+
const { object, property } = getMemberAccessParts(context.sup, target);
|
|
694
|
+
if (!object || !property)
|
|
695
|
+
return false;
|
|
696
|
+
const selected = resolveTypeScriptNamespaceMember(context, node, object, property);
|
|
697
|
+
if (selected === undefined)
|
|
698
|
+
return false;
|
|
699
|
+
if (selected?.kind === SymbolKind.Class)
|
|
700
|
+
recordDefEdge(context, fromId, selected, "instantiates", property);
|
|
701
|
+
return true;
|
|
702
|
+
}
|
|
703
|
+
/** A member call on a `typeof import("spec")` binding names that module's export. */
|
|
704
|
+
function recordImportTypeCall(context, access, fromId, proof) {
|
|
705
|
+
if (!isJsTsLanguage(context.sup.id))
|
|
706
|
+
return false;
|
|
707
|
+
const importType = proof.kind === "named-type" ? proof.typeNode : null;
|
|
708
|
+
const specifier = importType?.type === "type_query" ? importTypeQuerySpecifier(importType) : null;
|
|
709
|
+
if (!specifier)
|
|
710
|
+
return false;
|
|
711
|
+
const member = sliceText(access.property, context.source);
|
|
712
|
+
const target = resolveImportTypeMember(context.index, context.moduleEntry.file, specifier, member);
|
|
713
|
+
if (target && isCallTarget(context, target))
|
|
714
|
+
recordDefEdge(context, fromId, target, "calls", access.property);
|
|
715
|
+
return true;
|
|
716
|
+
}
|
|
717
|
+
/** Swift `Foo()` / `Worker(name:)` is construction. Record `instantiates` and skip the call path. */
|
|
718
|
+
function recordSwiftCapitalizedConstruction(context, node, fromId) {
|
|
719
|
+
const constructed = constructionTypeName(node, context.source, context.sup);
|
|
720
|
+
if (!constructed)
|
|
721
|
+
return false;
|
|
722
|
+
const name = sliceText(constructed, context.source);
|
|
723
|
+
// An inherited method can share a type's name; a deferred member lookup decides after the walk.
|
|
724
|
+
if (context.resolveName(name, constructed)?.status === "deferred")
|
|
725
|
+
return false;
|
|
726
|
+
const target = context.resolveIdentifier(name, constructed);
|
|
727
|
+
if (!target || !declaresMembers(target))
|
|
728
|
+
return false;
|
|
729
|
+
recordDefEdge(context, fromId, target, "instantiates", constructed);
|
|
730
|
+
return true;
|
|
731
|
+
}
|
|
732
|
+
function recordGoBuiltinConstruction(context, node, fromId) {
|
|
733
|
+
const callTarget = getCallTarget(node);
|
|
734
|
+
const calleeName = callTarget && isIdentifierType(context.sup, callTarget.type) ? sliceText(callTarget, context.source) : null;
|
|
735
|
+
if (calleeName !== "new" && calleeName !== "make")
|
|
736
|
+
return undefined;
|
|
737
|
+
const argList = node.childForFieldName("arguments") ?? node.childForFieldName("argument_list");
|
|
738
|
+
const typeNode = argList?.namedChildren?.find((child) => child.type === "type_identifier") ?? null;
|
|
739
|
+
if (typeNode)
|
|
740
|
+
tryResolveNode(context, typeNode, fromId, "instantiates");
|
|
741
|
+
return false;
|
|
742
|
+
}
|
|
743
|
+
function recordRubySuper(context, fn, superNode, fromId) {
|
|
744
|
+
const container = nearestMemberContainer(fn.node);
|
|
745
|
+
if (!container || container.type !== "class")
|
|
746
|
+
return;
|
|
747
|
+
const owner = memberContainerDef(context.moduleEntry, container);
|
|
748
|
+
if (!owner)
|
|
749
|
+
return;
|
|
750
|
+
context.receiverCalls.push({
|
|
751
|
+
callerId: fromId,
|
|
752
|
+
ownerId: ensureNode(context, owner),
|
|
753
|
+
viaSupertypes: true,
|
|
754
|
+
memberName: fn.def.localName,
|
|
755
|
+
argumentCount: null,
|
|
756
|
+
site: { file: context.moduleEntry.file, range: toRange(superNode) },
|
|
757
|
+
memberScope: "any",
|
|
758
|
+
});
|
|
219
759
|
}
|
|
220
|
-
|
|
760
|
+
function recordRubyCall(context, node, fromId, resolveCallTarget) {
|
|
761
|
+
if (node.type !== "call")
|
|
762
|
+
return undefined;
|
|
763
|
+
const methodNode = node.childForFieldName("method");
|
|
764
|
+
const receiverNode = node.childForFieldName("receiver");
|
|
765
|
+
const methodName = methodNode ? sliceText(methodNode, context.source) : null;
|
|
766
|
+
if (methodName === "new" && receiverNode) {
|
|
767
|
+
const recorded = tryResolveNode(context, receiverNode, fromId, "instantiates");
|
|
768
|
+
if (!recorded) {
|
|
769
|
+
const rubyType = resolveRubyVisibleConstant(context.index, context.moduleEntry, context.sup, sliceText(receiverNode, context.source));
|
|
770
|
+
if (rubyType)
|
|
771
|
+
recordDefEdge(context, fromId, rubyType, "instantiates", receiverNode);
|
|
772
|
+
}
|
|
773
|
+
return false;
|
|
774
|
+
}
|
|
775
|
+
if (methodNode?.type === "super")
|
|
776
|
+
return true;
|
|
777
|
+
if (methodNode) {
|
|
778
|
+
resolveCallTarget(node, methodNode);
|
|
779
|
+
return false;
|
|
780
|
+
}
|
|
781
|
+
const callee = getCallTarget(node);
|
|
782
|
+
if (callee?.type === "super")
|
|
783
|
+
return true;
|
|
784
|
+
resolveCallTarget(node, callee);
|
|
785
|
+
return false;
|
|
786
|
+
}
|
|
787
|
+
/** Specialized syntax stays with its language; the common walker only dispatches these hooks. */
|
|
788
|
+
export const SPECIALIZED_EDGE_PASSES = {
|
|
789
|
+
python: { beforeModule: emitPythonDecoratorEdges },
|
|
790
|
+
rust: {
|
|
791
|
+
afterModule: emitRustImplEdges,
|
|
792
|
+
onNode(context, _fn, node, fromId) {
|
|
793
|
+
if (node.type === "token_tree")
|
|
794
|
+
recordRustMacroArgumentCalls(context, node, fromId);
|
|
795
|
+
return undefined;
|
|
796
|
+
},
|
|
797
|
+
},
|
|
798
|
+
go: {
|
|
799
|
+
onReceiverCall(context, access, fromId, hasLexicalBinding) {
|
|
800
|
+
return (context.optionalMemberTypes.has(access.receiver.type) &&
|
|
801
|
+
recordGoEmbeddedFieldReceiverCall(context, access, fromId, hasLexicalBinding));
|
|
802
|
+
},
|
|
803
|
+
onCall: recordGoBuiltinConstruction,
|
|
804
|
+
},
|
|
805
|
+
ruby: {
|
|
806
|
+
onNode(context, fn, node, fromId) {
|
|
807
|
+
if (node.type !== "super")
|
|
808
|
+
return undefined;
|
|
809
|
+
recordRubySuper(context, fn, node, fromId);
|
|
810
|
+
return true;
|
|
811
|
+
},
|
|
812
|
+
onCall: recordRubyCall,
|
|
813
|
+
},
|
|
814
|
+
swift: {
|
|
815
|
+
onCall(context, node, fromId) {
|
|
816
|
+
return recordSwiftCapitalizedConstruction(context, node, fromId) ? true : undefined;
|
|
817
|
+
},
|
|
818
|
+
},
|
|
819
|
+
};
|
|
820
|
+
export async function emitFunctionBodyEdges(context, functionNodes) {
|
|
821
|
+
const specialized = SPECIALIZED_EDGE_PASSES[context.sup.id];
|
|
822
|
+
const qualifiedConstructionTargets = [];
|
|
823
|
+
// C# calls through a dotted receiver (`P.Mix.M()`, `Imported.Outer.Inner.M()`): navigation's
|
|
824
|
+
// member-access resolution decides the target after the walk, so both consumers agree.
|
|
825
|
+
const dottedReceiverCalls = [];
|
|
221
826
|
const callNodeTypes = new Set([
|
|
222
827
|
"call_expression",
|
|
223
828
|
"call",
|
|
@@ -229,21 +834,29 @@ export function emitFunctionBodyEdges(context, functionNodes) {
|
|
|
229
834
|
"nullsafe_member_call_expression",
|
|
230
835
|
"scoped_call_expression",
|
|
231
836
|
]);
|
|
232
|
-
const newNodeTypes =
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
837
|
+
const newNodeTypes = {
|
|
838
|
+
new_expression: true,
|
|
839
|
+
object_creation_expression: true,
|
|
840
|
+
struct_expression: true,
|
|
841
|
+
composite_literal: true,
|
|
842
|
+
// tree-sitter-zig's own struct-literal node, e.g. `Widget{}` or a qualified `ns.Widget{}`.
|
|
843
|
+
struct_initializer: true,
|
|
844
|
+
};
|
|
845
|
+
// Receiver typing and lexical member lookup are initialized only for receiver calls.
|
|
846
|
+
const receiverProofs = new Map();
|
|
847
|
+
const hasLexicalBinding = (callee) => {
|
|
848
|
+
const scope = context.index.scopeCache.get(fileIdentityKey(context.moduleEntry.file));
|
|
849
|
+
return !scope || !!findClosestScopeBinding(scope, sliceText(callee, context.source), callee, context.sup);
|
|
850
|
+
};
|
|
240
851
|
let membersByContainer;
|
|
241
852
|
const lexicalMembers = (container) => {
|
|
242
853
|
if (!membersByContainer) {
|
|
243
854
|
membersByContainer = new Map();
|
|
244
855
|
for (const candidate of functionNodes) {
|
|
856
|
+
if (!isClassMemberFunction(candidate))
|
|
857
|
+
continue;
|
|
245
858
|
const owner = nearestMemberContainer(candidate.node);
|
|
246
|
-
if (!owner)
|
|
859
|
+
if (!owner || !isDirectKeywordMemberDeclaration(candidate.node, owner))
|
|
247
860
|
continue;
|
|
248
861
|
const members = membersByContainer.get(owner.startIndex);
|
|
249
862
|
if (members)
|
|
@@ -254,10 +867,21 @@ export function emitFunctionBodyEdges(context, functionNodes) {
|
|
|
254
867
|
}
|
|
255
868
|
return membersByContainer.get(container.startIndex) ?? [];
|
|
256
869
|
};
|
|
257
|
-
const receiverProofs = new Map();
|
|
258
870
|
for (const fn of functionNodes) {
|
|
871
|
+
const phpCaseInsensitive = context.sup.id === "php";
|
|
259
872
|
const fromId = ensureNode(context, fn.def);
|
|
873
|
+
const provenNode = context.nodes.get(fromId);
|
|
874
|
+
// Function-valued bindings (`const helper = () => 1`) keep their `variable` kind;
|
|
875
|
+
// the callable metadata records that this binding was proven to hold a function.
|
|
876
|
+
if (provenNode && provenNode.kind !== "function" && provesCallableBinding(context, fn)) {
|
|
877
|
+
provenNode.callable = true;
|
|
878
|
+
context.noteCallableName(fn.name, context.sup.id === "php");
|
|
879
|
+
}
|
|
260
880
|
const seenAliases = new Set();
|
|
881
|
+
// Calls whose target needs an async member lookup (C++ out-of-line owner members, included
|
|
882
|
+
// C/C++ declarations of one callable); settled after the walk.
|
|
883
|
+
const deferredCalls = [];
|
|
884
|
+
const qualifiedCppCalls = [];
|
|
261
885
|
const nestedFunctions = new Set(functionNodes
|
|
262
886
|
.filter((candidate) => candidate.node !== fn.node &&
|
|
263
887
|
candidate.node.startIndex >= fn.node.startIndex &&
|
|
@@ -267,12 +891,28 @@ export function emitFunctionBodyEdges(context, functionNodes) {
|
|
|
267
891
|
if (context.membersOnly || !isIdentifierType(context.sup, node.type))
|
|
268
892
|
return;
|
|
269
893
|
const name = sliceText(node, context.source);
|
|
894
|
+
if (context.sup.id === "php") {
|
|
895
|
+
// Resolve the occurrence's namespace; a plain-name map cannot distinguish PHP roles.
|
|
896
|
+
const importType = inferPhpQualifiedReferenceImportType(node) ?? "const";
|
|
897
|
+
const aliasName = importType === "const" ? name : foldPhpIdentifierCase(name);
|
|
898
|
+
const seenKey = `${importType}:${aliasName}`;
|
|
899
|
+
if (seenAliases.has(seenKey))
|
|
900
|
+
return;
|
|
901
|
+
seenAliases.add(seenKey);
|
|
902
|
+
const phpImport = findPhpImportAlias(context.moduleEntry.imports, name, importType);
|
|
903
|
+
if (!phpImport || typeof phpImport.resolved !== "string")
|
|
904
|
+
return;
|
|
905
|
+
const resolved = resolvePhpExportByImportType(context.index, phpImport.resolved, phpImport.imported, importType);
|
|
906
|
+
if (resolved?.kind === "resolved")
|
|
907
|
+
recordDefEdge(context, fromId, resolved.def, "uses");
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
270
910
|
if (seenAliases.has(name))
|
|
271
911
|
return;
|
|
272
912
|
let target = context.aliasToTargetDef.get(name) ?? null;
|
|
273
913
|
if (!target) {
|
|
274
914
|
const modFile = context.aliasToTargetModule.get(name);
|
|
275
|
-
if (modFile) {
|
|
915
|
+
if (modFile && context.moduleAliasIsUnshadowed(name, node)) {
|
|
276
916
|
let exportedName = null;
|
|
277
917
|
const parent = node.parent;
|
|
278
918
|
if (parent &&
|
|
@@ -284,7 +924,7 @@ export function emitFunctionBodyEdges(context, functionNodes) {
|
|
|
284
924
|
}
|
|
285
925
|
if (exportedName) {
|
|
286
926
|
target = context.resolveExportFrom(modFile, exportedName);
|
|
287
|
-
if (!target) {
|
|
927
|
+
if (!target && (context.sup.id !== "go" || isGoExportedMemberName(context.sup.id, exportedName))) {
|
|
288
928
|
const targetModule = context.index.byFile.get(fileIdentityKey(modFile));
|
|
289
929
|
target = (targetModule?.locals ?? []).find((local) => local.localName === exportedName) ?? null;
|
|
290
930
|
}
|
|
@@ -304,22 +944,44 @@ export function emitFunctionBodyEdges(context, functionNodes) {
|
|
|
304
944
|
recordDefEdge(context, fromId, targetDef, "uses");
|
|
305
945
|
}
|
|
306
946
|
};
|
|
947
|
+
const classifyImport = (name, node) => {
|
|
948
|
+
if (isJsTsLanguage(context.sup.id) && isIdentifierType(context.sup, node.type)) {
|
|
949
|
+
const imported = effectiveExplicitBinding(context.moduleEntry.imports, context.sup.id, (candidate) => candidate.kind === "namespace" && candidate.localNS === name, node.startIndex) ?? innermostNamespaceImport(context.moduleEntry.imports, name, node);
|
|
950
|
+
if (imported?.mechanism === "cjs" && typeof imported.resolved === "string") {
|
|
951
|
+
const value = cjsRequireValueBinding(context.index, imported.resolved);
|
|
952
|
+
const lexical = value ? context.resolveIdentifier(name, node) : null;
|
|
953
|
+
if (value && declaresMembers(value) && lexical && defNodeId(value) === defNodeId(lexical)) {
|
|
954
|
+
return "static-type";
|
|
955
|
+
}
|
|
956
|
+
return "module-import";
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
if (context.aliasToTargetModule.has(name) && context.moduleAliasIsUnshadowed(name, node)) {
|
|
960
|
+
return "module-import";
|
|
961
|
+
}
|
|
962
|
+
return null;
|
|
963
|
+
};
|
|
307
964
|
/**
|
|
308
965
|
* Resolves a receiver method call against the receiver's type. Members declared
|
|
309
966
|
* alongside the caller resolve here; anything needing another module's members
|
|
310
967
|
* becomes a deferred candidate.
|
|
311
968
|
*/
|
|
312
|
-
const recordReceiverCall = (node, access) => {
|
|
969
|
+
const recordReceiverCall = (node, access, forcedMemberScope, proof) => {
|
|
313
970
|
const memberName = sliceText(access.property, context.source);
|
|
314
|
-
if (!memberName
|
|
971
|
+
if (!memberName)
|
|
315
972
|
return;
|
|
316
|
-
const binding =
|
|
317
|
-
|
|
973
|
+
const binding = proof ??
|
|
974
|
+
classifyReceiver(context.sup, access.receiver, context.source, receiverProofs, fn.node.startIndex, access.accessNode, hasLexicalBinding, classifyImport);
|
|
975
|
+
if (binding.kind === "module-import" || binding.kind === "unknown")
|
|
318
976
|
return;
|
|
319
977
|
const site = { file: context.moduleEntry.file, range: toRange(access.property) };
|
|
320
|
-
|
|
978
|
+
// Shared call-count facts treat unproven spread expansions as unknown (null),
|
|
979
|
+
// so overload selection never fabricates an argument count.
|
|
980
|
+
const argumentCount = supportsReceiverMemberOverloads(context.sup.id)
|
|
981
|
+
? getCallArgumentCount({ languageId: context.sup.id, source: context.source, call: node })
|
|
982
|
+
: null;
|
|
321
983
|
if (binding.kind === "named-type") {
|
|
322
|
-
const typeDef = resolveNamedType(context, binding.typeName,
|
|
984
|
+
const typeDef = resolveNamedType(context, binding.typeName, binding.typeNode, binding.constructed);
|
|
323
985
|
if (!typeDef)
|
|
324
986
|
return;
|
|
325
987
|
context.receiverCalls.push({
|
|
@@ -329,26 +991,89 @@ export function emitFunctionBodyEdges(context, functionNodes) {
|
|
|
329
991
|
memberName,
|
|
330
992
|
argumentCount,
|
|
331
993
|
site,
|
|
994
|
+
memberScope: forcedMemberScope ?? binding.memberScope,
|
|
995
|
+
...(context.sup.id === "go" && !isGoExportedMemberName(context.sup.id, memberName)
|
|
996
|
+
? { goPackagePeerFiles: getCompilationUnitPeers(context.index, context.moduleEntry.file).files }
|
|
997
|
+
: {}),
|
|
998
|
+
caseInsensitiveMemberName: phpCaseInsensitive,
|
|
332
999
|
});
|
|
333
1000
|
return;
|
|
334
1001
|
}
|
|
335
1002
|
if (binding.kind === "own-type") {
|
|
336
1003
|
const container = nearestMemberContainer(fn.node);
|
|
337
1004
|
const declared = container
|
|
338
|
-
? lexicalMembers(container).filter((candidate) =>
|
|
1005
|
+
? lexicalMembers(container).filter((candidate) => {
|
|
1006
|
+
const candidateName = candidate.def.localName;
|
|
1007
|
+
const nameMatches = phpCaseInsensitive
|
|
1008
|
+
? foldPhpIdentifierCase(candidateName) === foldPhpIdentifierCase(memberName)
|
|
1009
|
+
: candidateName === memberName;
|
|
1010
|
+
if (!nameMatches)
|
|
1011
|
+
return false;
|
|
1012
|
+
if (binding.memberScope === "any")
|
|
1013
|
+
return true;
|
|
1014
|
+
return declarationNodeIsStatic(candidate.node, context.source) === (binding.memberScope === "static");
|
|
1015
|
+
})
|
|
339
1016
|
: [];
|
|
340
1017
|
if (declared.length === 1) {
|
|
341
|
-
|
|
342
|
-
|
|
1018
|
+
const unique = declared[0];
|
|
1019
|
+
recordDefEdge(context, fromId, unique.def, "calls", access.property);
|
|
1020
|
+
// File-hidden Swift members stay off cross-file shared owners. Skipping the
|
|
1021
|
+
// coalesced deferred lookup keeps a same-container private call from being stripped.
|
|
1022
|
+
if (isSwiftFileHiddenSharedOwnerMember(context.sup.id, unique.node))
|
|
1023
|
+
return;
|
|
343
1024
|
}
|
|
344
1025
|
}
|
|
1026
|
+
const receiverContainer = nearestMemberContainer(access.accessNode);
|
|
1027
|
+
const receiverContainerName = receiverContainer?.childForFieldName("name");
|
|
1028
|
+
const receiverOwnerDef = receiverContainerName
|
|
1029
|
+
? context.moduleEntry.locals.find((local) => local.range.start.index === receiverContainerName.startIndex && declaresMembers(local))
|
|
1030
|
+
: undefined;
|
|
345
1031
|
context.receiverCalls.push({
|
|
346
1032
|
callerId: fromId,
|
|
347
|
-
ownerId: null,
|
|
1033
|
+
ownerId: receiverOwnerDef ? ensureNode(context, receiverOwnerDef) : null,
|
|
348
1034
|
viaSupertypes: binding.kind === "supertype",
|
|
349
1035
|
memberName,
|
|
350
1036
|
argumentCount,
|
|
351
1037
|
site,
|
|
1038
|
+
memberScope: binding.memberScope,
|
|
1039
|
+
caseInsensitiveMemberName: phpCaseInsensitive,
|
|
1040
|
+
});
|
|
1041
|
+
};
|
|
1042
|
+
/**
|
|
1043
|
+
* A bare call inside a member function carries no receiver syntax, but in
|
|
1044
|
+
* languages where that implicitly targets `this` it can still name an
|
|
1045
|
+
* inherited member. Deferred like `recordReceiverCall`'s own-type/supertype
|
|
1046
|
+
* candidates so cross-file base-type members resolve once every module's
|
|
1047
|
+
* `member_of` and hierarchy edges are known.
|
|
1048
|
+
*/
|
|
1049
|
+
const recordImplicitSelfMemberCall = (node, callee, fallbackDef = null) => {
|
|
1050
|
+
if (!supportsImplicitSelfMemberCalls(context.sup.id) || !isIdentifierType(context.sup, callee.type))
|
|
1051
|
+
return;
|
|
1052
|
+
const container = nearestMemberContainer(fn.node);
|
|
1053
|
+
const containerName = container?.childForFieldName("name");
|
|
1054
|
+
const ownerDef = containerName
|
|
1055
|
+
? context.moduleEntry.locals.find((local) => local.range.start.index === containerName.startIndex && declaresMembers(local))
|
|
1056
|
+
: undefined;
|
|
1057
|
+
if (!ownerDef)
|
|
1058
|
+
return;
|
|
1059
|
+
const memberName = sliceText(callee, context.source);
|
|
1060
|
+
if (!memberName)
|
|
1061
|
+
return;
|
|
1062
|
+
const argumentCount = supportsReceiverMemberOverloads(context.sup.id)
|
|
1063
|
+
? getCallArgumentCount({ languageId: context.sup.id, source: context.source, call: node })
|
|
1064
|
+
: null;
|
|
1065
|
+
context.receiverCalls.push({
|
|
1066
|
+
callerId: fromId,
|
|
1067
|
+
ownerId: ensureNode(context, ownerDef),
|
|
1068
|
+
viaSupertypes: false,
|
|
1069
|
+
memberName,
|
|
1070
|
+
argumentCount,
|
|
1071
|
+
site: { file: context.moduleEntry.file, range: toRange(callee) },
|
|
1072
|
+
// A static caller can only reach a static bare member; an instance caller
|
|
1073
|
+
// may bare-call either, matching C#'s unqualified invocation rules.
|
|
1074
|
+
memberScope: nodeInStaticMemberContext(fn.node, context.source) ? "static" : "any",
|
|
1075
|
+
caseInsensitiveMemberName: phpCaseInsensitive,
|
|
1076
|
+
fallbackTargetId: fallbackDef ? ensureNode(context, fallbackDef) : undefined,
|
|
352
1077
|
});
|
|
353
1078
|
};
|
|
354
1079
|
/**
|
|
@@ -360,46 +1085,116 @@ export function emitFunctionBodyEdges(context, functionNodes) {
|
|
|
360
1085
|
const resolveCallTarget = (node, callee) => {
|
|
361
1086
|
const access = receiverCallAccess(context.sup, node, callee);
|
|
362
1087
|
if (access) {
|
|
363
|
-
if (
|
|
364
|
-
|
|
1088
|
+
if (recordTypeScriptNamespaceCall(context, node, access, fromId))
|
|
1089
|
+
return;
|
|
1090
|
+
const typeScopedCppCall = context.sup.id === "cpp" &&
|
|
1091
|
+
context.source.slice(access.receiver.endIndex, access.property.startIndex).includes("::");
|
|
1092
|
+
if (typeScopedCppCall) {
|
|
1093
|
+
const qualifiedName = cppQualifiedNameSegments(access.accessNode, context.source).join("::");
|
|
1094
|
+
const qualifiedTarget = context.resolveIdentifier(qualifiedName, access.property);
|
|
1095
|
+
// Whether the owner is a class, and whether the caller has an implicit `this` of that class,
|
|
1096
|
+
// needs the exact qualified path, so the decision waits until the function body is walked.
|
|
1097
|
+
qualifiedCppCalls.push({
|
|
1098
|
+
node,
|
|
1099
|
+
access,
|
|
1100
|
+
target: qualifiedTarget,
|
|
1101
|
+
ownerPath: qualifiedName.split("::").slice(0, -1),
|
|
1102
|
+
});
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
const receiverProof = classifyReceiver(context.sup, access.receiver, context.source, receiverProofs, fn.node.startIndex, access.accessNode, hasLexicalBinding, classifyImport);
|
|
1106
|
+
if (recordImportTypeCall(context, access, fromId, receiverProof))
|
|
1107
|
+
return;
|
|
1108
|
+
if (supportsDeclaredFieldChains(context.sup.id) && isMemberAccessNode(context.sup, access.receiver)) {
|
|
1109
|
+
dottedReceiverCalls.push({ fromId, access });
|
|
1110
|
+
return;
|
|
1111
|
+
}
|
|
1112
|
+
if (receiverProof.kind === "own-type" || receiverProof.kind === "supertype") {
|
|
1113
|
+
recordReceiverCall(node, access, undefined, receiverProof);
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1116
|
+
if (context.sup.id === "swift" &&
|
|
1117
|
+
isIdentifierType(context.sup, access.receiver.type) &&
|
|
1118
|
+
context.resolveIdentifier(sliceText(access.receiver, context.source), access.receiver)?.kind ===
|
|
1119
|
+
SymbolKind.TypeAlias) {
|
|
1120
|
+
dottedReceiverCalls.push({ fromId, access });
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
if (tryResolveChain(context, access.accessNode, fromId, "calls"))
|
|
1124
|
+
return;
|
|
1125
|
+
if (context.sup.id === "zig" &&
|
|
1126
|
+
context.optionalMemberTypes.has(access.receiver.type) &&
|
|
1127
|
+
recordZigQualifiedTypeReceiverCall(context, node, access, fromId)) {
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
if (specialized?.onReceiverCall?.(context, access, fromId, hasLexicalBinding))
|
|
1131
|
+
return;
|
|
1132
|
+
const javaConstructionType = context.sup.id === "java" && access.receiver.type === "object_creation_expression"
|
|
1133
|
+
? constructionTypeName(access.receiver, context.source, context.sup)
|
|
1134
|
+
: null;
|
|
1135
|
+
if (context.sup.id === "java" &&
|
|
1136
|
+
javaConstructionType &&
|
|
1137
|
+
isMemberAccessNode(context.sup, javaConstructionType)) {
|
|
1138
|
+
dottedReceiverCalls.push({ fromId, access });
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
if (context.sup.id === "kotlin" &&
|
|
1142
|
+
(isIdentifierType(context.sup, access.receiver.type) || access.receiver.type === "navigation_expression") &&
|
|
1143
|
+
!context.resolveIdentifier(sliceText(access.receiver, context.source), access.receiver)) {
|
|
1144
|
+
dottedReceiverCalls.push({ fromId, access });
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
recordReceiverCall(node, access, undefined, receiverProof);
|
|
365
1148
|
return;
|
|
366
1149
|
}
|
|
367
|
-
if (callee)
|
|
368
|
-
|
|
1150
|
+
if (!callee)
|
|
1151
|
+
return;
|
|
1152
|
+
// A name whose lookup waits on an async member step (implicit `this`/`self`, C++ out-of-line
|
|
1153
|
+
// owners, included C/C++ declarations) settles after the walk exactly as navigation does.
|
|
1154
|
+
if (isIdentifierType(context.sup, callee.type)) {
|
|
1155
|
+
const name = sliceText(callee, context.source);
|
|
1156
|
+
const resolution = context.resolveName(name, callee);
|
|
1157
|
+
if (resolution?.status === "deferred") {
|
|
1158
|
+
deferredCalls.push({ callee, name, resolution });
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
if (!tryResolveNode(context, callee, fromId, "calls"))
|
|
1163
|
+
recordImplicitSelfMemberCall(node, callee);
|
|
369
1164
|
};
|
|
370
1165
|
const recordCallOrInstantiation = (node) => {
|
|
1166
|
+
const nodeResult = specialized?.onNode?.(context, fn, node, fromId);
|
|
1167
|
+
if (nodeResult !== undefined)
|
|
1168
|
+
return nodeResult;
|
|
371
1169
|
if (callNodeTypes.has(node.type)) {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
1170
|
+
const callResult = specialized?.onCall?.(context, node, fromId, resolveCallTarget);
|
|
1171
|
+
if (callResult !== undefined)
|
|
1172
|
+
return callResult;
|
|
1173
|
+
resolveCallTarget(node, getCallTarget(node));
|
|
1174
|
+
}
|
|
1175
|
+
if (newNodeTypes[node.type]) {
|
|
1176
|
+
const keyword = phpObjectCreationKeyword(node, context.source, context.sup);
|
|
1177
|
+
if (keyword) {
|
|
1178
|
+
const created = resolvePhpObjectCreationTarget(context.index, context.moduleEntry, keyword, context.source, context.sup);
|
|
1179
|
+
if (created)
|
|
1180
|
+
recordDefEdge(context, fromId, created, "instantiates", keyword.nameNode);
|
|
383
1181
|
}
|
|
384
|
-
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
1182
|
+
else {
|
|
1183
|
+
const target = constructionTypeName(node, context.source, context.sup) ?? getNewTarget(node);
|
|
1184
|
+
if (target) {
|
|
1185
|
+
if (recordTypeScriptNamespaceConstruction(context, node, target, fromId))
|
|
1186
|
+
return true;
|
|
1187
|
+
const property = isMemberAccessNode(context.sup, target)
|
|
1188
|
+
? getMemberAccessParts(context.sup, target).property
|
|
1189
|
+
: null;
|
|
1190
|
+
if ((context.sup.id === "java" || context.sup.id === "csharp") && property) {
|
|
1191
|
+
qualifiedConstructionTargets.push({ fromId, member: property, target });
|
|
1192
|
+
}
|
|
1193
|
+
else {
|
|
1194
|
+
tryResolveNode(context, target, fromId, "instantiates");
|
|
1195
|
+
}
|
|
395
1196
|
}
|
|
396
1197
|
}
|
|
397
|
-
resolveCallTarget(node, getCallTarget(node));
|
|
398
|
-
}
|
|
399
|
-
if (newNodeTypes.has(node.type)) {
|
|
400
|
-
const target = getNewTarget(node);
|
|
401
|
-
if (target)
|
|
402
|
-
tryResolveNode(context, target, fromId, "instantiates");
|
|
403
1198
|
}
|
|
404
1199
|
return true;
|
|
405
1200
|
};
|
|
@@ -413,11 +1208,54 @@ export function emitFunctionBodyEdges(context, functionNodes) {
|
|
|
413
1208
|
walkFunctionBody(child, allowChildCallProcessing);
|
|
414
1209
|
};
|
|
415
1210
|
walkFunctionBody(fn.node, true);
|
|
1211
|
+
for (const call of deferredCalls) {
|
|
1212
|
+
const target = await context.settleName(call.name, call.callee, call.resolution);
|
|
1213
|
+
// Swift `Foo()` whose deferred lookup settles on a type is construction.
|
|
1214
|
+
const label = context.sup.id === "swift" && target && declaresMembers(target) ? "instantiates" : "calls";
|
|
1215
|
+
if (target)
|
|
1216
|
+
recordDefEdge(context, fromId, target, label, call.callee);
|
|
1217
|
+
}
|
|
1218
|
+
for (const call of qualifiedCppCalls) {
|
|
1219
|
+
const owner = call.ownerPath.length
|
|
1220
|
+
? await resolveCppQualifiedMemberContainer(context.index, context.moduleEntry, call.ownerPath, context.loadParsedFile)
|
|
1221
|
+
: null;
|
|
1222
|
+
if (!owner) {
|
|
1223
|
+
// A namespace (or no) qualifier names the free function directly.
|
|
1224
|
+
if (call.target)
|
|
1225
|
+
recordDefEdge(context, fromId, call.target, "calls", call.access.property);
|
|
1226
|
+
continue;
|
|
1227
|
+
}
|
|
1228
|
+
const implicitThis = await cppQualifiedOwnerHasImplicitThis(context.index, context.moduleEntry, call.access.accessNode, context.source, context.sup, owner);
|
|
1229
|
+
if (implicitThis && call.target) {
|
|
1230
|
+
recordDefEdge(context, fromId, call.target, "calls", call.access.property);
|
|
1231
|
+
}
|
|
1232
|
+
else {
|
|
1233
|
+
// Without an implicit `this` of the owner, `Owner::member()` can only name a static member.
|
|
1234
|
+
recordReceiverCall(call.node, call.access, implicitThis ? "any" : "static");
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
for (const { fromId, access } of dottedReceiverCalls) {
|
|
1239
|
+
const member = await context.resolveMemberAccessTarget(access.property);
|
|
1240
|
+
// The graph records no edge for a call its target cannot accept, as for other member calls.
|
|
1241
|
+
if (member && memberAcceptsCallAt(context.index, member, access.accessNode, context.source, context.sup.id)) {
|
|
1242
|
+
recordDefEdge(context, fromId, member, "calls", access.property);
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
for (const target of qualifiedConstructionTargets) {
|
|
1246
|
+
const resolved = await context.resolveMemberAccessTarget(target.member);
|
|
1247
|
+
if (resolved) {
|
|
1248
|
+
recordDefEdge(context, target.fromId, resolved, "instantiates", target.member);
|
|
1249
|
+
}
|
|
1250
|
+
else {
|
|
1251
|
+
tryResolveNode(context, target.target, target.fromId, "instantiates");
|
|
1252
|
+
}
|
|
416
1253
|
}
|
|
417
1254
|
}
|
|
418
1255
|
/** Qualifiers name a container, not another base type. */
|
|
419
1256
|
const QUALIFIER_NAME_FIELD = {
|
|
420
1257
|
qualified_name: "name", // C#: Namespace.Base, Outer.Inner
|
|
1258
|
+
alias_qualified_name: "name", // C#: X::Base, global::Namespace.Base
|
|
421
1259
|
qualified_identifier: "name", // C++: ns::Base
|
|
422
1260
|
scope_resolution: "name", // Ruby: Module::Base
|
|
423
1261
|
qualified_type: "name", // Go: pkg.Base
|
|
@@ -504,9 +1342,19 @@ function narrowBaseSpecifierNode(node) {
|
|
|
504
1342
|
}
|
|
505
1343
|
/** Collect one type identifier per direct base or interface specifier. */
|
|
506
1344
|
function collectBaseSpecifierIdentifiers(node, sup, out) {
|
|
1345
|
+
if (isUnprovenHeritageExpression(node))
|
|
1346
|
+
return;
|
|
507
1347
|
if (BASE_TYPE_IGNORED_TYPES[node.type])
|
|
508
1348
|
return;
|
|
1349
|
+
if (sup.id === "cpp" && node.type === "qualified_identifier") {
|
|
1350
|
+
out.push(node);
|
|
1351
|
+
return;
|
|
1352
|
+
}
|
|
509
1353
|
const narrowed = narrowBaseSpecifierNode(node);
|
|
1354
|
+
if ((sup.id === "ts" || sup.id === "tsx") && narrowed.type === "member_expression") {
|
|
1355
|
+
out.push(narrowed);
|
|
1356
|
+
return;
|
|
1357
|
+
}
|
|
510
1358
|
if (isIdentifierType(sup, narrowed.type) || narrowed.type === "type_identifier") {
|
|
511
1359
|
out.push(narrowed);
|
|
512
1360
|
return;
|
|
@@ -514,19 +1362,39 @@ function collectBaseSpecifierIdentifiers(node, sup, out) {
|
|
|
514
1362
|
for (const child of narrowed.namedChildren ?? [])
|
|
515
1363
|
collectBaseSpecifierIdentifiers(child, sup, out);
|
|
516
1364
|
}
|
|
517
|
-
function recordIdentifierRelations(context, fromId, container, relationForTarget) {
|
|
1365
|
+
async function recordIdentifierRelations(context, fromId, container, relationForTarget) {
|
|
518
1366
|
const identifiers = [];
|
|
519
1367
|
collectBaseSpecifierIdentifiers(container, context.sup, identifiers);
|
|
520
1368
|
const seen = new Set();
|
|
521
1369
|
for (const [index, identifier] of identifiers.entries()) {
|
|
522
|
-
|
|
1370
|
+
let target;
|
|
1371
|
+
if (context.sup.id === "cpp" && identifier.type === "qualified_identifier") {
|
|
1372
|
+
const qualifiedPath = cppQualifiedNameSegments(identifier, context.source);
|
|
1373
|
+
target = await resolveCppQualifiedMemberContainer(context.index, context.moduleEntry, qualifiedPath, context.loadParsedFile);
|
|
1374
|
+
if (!target && qualifiedPath.length > 1) {
|
|
1375
|
+
target = context.resolveIdentifier(qualifiedPath.join("::"), identifier);
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
else if ((context.sup.id === "ts" || context.sup.id === "tsx") && identifier.type === "member_expression") {
|
|
1379
|
+
const qualified = context.resolveMemberChainTarget(identifier);
|
|
1380
|
+
target = qualified && declaresMembers(qualified) ? qualified : null;
|
|
1381
|
+
}
|
|
1382
|
+
else {
|
|
1383
|
+
const name = sliceText(identifier, context.source);
|
|
1384
|
+
target = context.resolveIdentifier(name, identifier);
|
|
1385
|
+
// Ruby `require` publishes a top-level constant, not a nested one. Reuse the
|
|
1386
|
+
// same visibility as goto so `class Worker < Base` follows the required class.
|
|
1387
|
+
if (!target && context.sup.id === "ruby") {
|
|
1388
|
+
target = resolveRubyVisibleConstant(context.index, context.moduleEntry, context.sup, name);
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
523
1391
|
if (!target)
|
|
524
1392
|
continue;
|
|
525
1393
|
const targetId = defNodeId(target);
|
|
526
1394
|
if (seen.has(targetId))
|
|
527
1395
|
continue;
|
|
528
1396
|
seen.add(targetId);
|
|
529
|
-
recordDefEdge(context, fromId, target, relationForTarget(target, index), identifier);
|
|
1397
|
+
recordDefEdge(context, fromId, target, relationForTarget(target, index, identifier), identifier);
|
|
530
1398
|
}
|
|
531
1399
|
}
|
|
532
1400
|
const RUBY_NESTED_SCOPE_TYPES = new Set(["class", "module", "method", "singleton_method", "block", "do_block"]);
|
|
@@ -540,68 +1408,24 @@ function collectDirectCallsExcludingNestedScopes(node, out) {
|
|
|
540
1408
|
collectDirectCallsExcludingNestedScopes(child, out);
|
|
541
1409
|
}
|
|
542
1410
|
}
|
|
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) {
|
|
1411
|
+
function baseClauseRelation(label, target, index, interfaceIds, identifier, kotlin) {
|
|
597
1412
|
if (label !== "superclass-first")
|
|
598
1413
|
return label;
|
|
1414
|
+
if (kotlin) {
|
|
1415
|
+
let current = identifier.parent;
|
|
1416
|
+
while (current && current.type !== "delegation_specifiers") {
|
|
1417
|
+
if (current.type === "constructor_invocation")
|
|
1418
|
+
return "extends";
|
|
1419
|
+
current = current.parent;
|
|
1420
|
+
}
|
|
1421
|
+
return "implements";
|
|
1422
|
+
}
|
|
599
1423
|
if (interfaceIds.has(defNodeId(target)) || index > 0)
|
|
600
1424
|
return "implements";
|
|
601
1425
|
return "extends";
|
|
602
1426
|
}
|
|
603
1427
|
/** Records `implements` for every embedded type of a Go interface or struct declaration. */
|
|
604
|
-
function recordEmbedRelations(context, fromId, declaration, embeds) {
|
|
1428
|
+
async function recordEmbedRelations(context, fromId, declaration, embeds) {
|
|
605
1429
|
const declaredType = declaration.childForFieldName("type");
|
|
606
1430
|
if (!declaredType)
|
|
607
1431
|
return;
|
|
@@ -619,12 +1443,12 @@ function recordEmbedRelations(context, fromId, declaration, embeds) {
|
|
|
619
1443
|
const specifier = rule.typeField ? member.childForFieldName(rule.typeField) : member;
|
|
620
1444
|
if (!specifier)
|
|
621
1445
|
continue;
|
|
622
|
-
recordIdentifierRelations(context, fromId, specifier, () => "implements");
|
|
1446
|
+
await recordIdentifierRelations(context, fromId, specifier, () => "implements");
|
|
623
1447
|
}
|
|
624
1448
|
}
|
|
625
1449
|
}
|
|
626
|
-
export function emitClassInheritanceEdges(context, classNodes) {
|
|
627
|
-
const rules =
|
|
1450
|
+
export async function emitClassInheritanceEdges(context, classNodes) {
|
|
1451
|
+
const rules = MEMBER_ACCESS_ROWS[context.sup.id]?.receiverAncestry;
|
|
628
1452
|
if (!rules)
|
|
629
1453
|
return;
|
|
630
1454
|
const interfaceIds = new Set(classNodes
|
|
@@ -635,20 +1459,27 @@ export function emitClassInheritanceEdges(context, classNodes) {
|
|
|
635
1459
|
.map((candidate) => defNodeId(candidate.def)));
|
|
636
1460
|
for (const cls of classNodes) {
|
|
637
1461
|
const fromId = ensureNode(context, cls.def);
|
|
638
|
-
markImplementationTarget(context, fromId, cls.node, cls.def);
|
|
1462
|
+
markImplementationTarget(context, fromId, cls.node, context.source, cls.def);
|
|
1463
|
+
if ((context.sup.id === "ts" || context.sup.id === "tsx") && cls.node.type === "class_declaration") {
|
|
1464
|
+
const heritage = cls.node.namedChildren.find((child) => child.type === "class_heritage");
|
|
1465
|
+
const superclass = heritage?.namedChildren.find((child) => child.type === "extends_clause");
|
|
1466
|
+
// TypeScript classes have one superclass. Invalid multi-base syntax proves neither target.
|
|
1467
|
+
if (superclass && superclass.namedChildren.length > 1)
|
|
1468
|
+
continue;
|
|
1469
|
+
}
|
|
639
1470
|
for (const rule of rules.clauses) {
|
|
640
1471
|
const clauses = [];
|
|
641
1472
|
if (rule.each) {
|
|
642
|
-
collectNodesByType(cls.node, rule.
|
|
1473
|
+
collectNodesByType(cls.node, rule.nodeType, clauses);
|
|
643
1474
|
}
|
|
644
1475
|
else {
|
|
645
|
-
const found = findFirstNodeByType(cls.node, rule.
|
|
1476
|
+
const found = findFirstNodeByType(cls.node, rule.nodeType);
|
|
646
1477
|
if (found)
|
|
647
1478
|
clauses.push(found);
|
|
648
1479
|
}
|
|
649
1480
|
for (const clause of clauses) {
|
|
650
1481
|
const specifiers = rule.field ? (clause.childForFieldName(rule.field) ?? clause) : clause;
|
|
651
|
-
recordIdentifierRelations(context, fromId, specifiers, (target, index) => baseClauseRelation(rule.
|
|
1482
|
+
await recordIdentifierRelations(context, fromId, specifiers, (target, index, identifier) => baseClauseRelation(rule.relation, target, index, interfaceIds, identifier, context.sup.id === "kotlin"));
|
|
652
1483
|
}
|
|
653
1484
|
}
|
|
654
1485
|
if (rules.mixinCalls) {
|
|
@@ -663,16 +1494,14 @@ export function emitClassInheritanceEdges(context, classNodes) {
|
|
|
663
1494
|
continue;
|
|
664
1495
|
const args = call.childForFieldName("arguments");
|
|
665
1496
|
if (args)
|
|
666
|
-
recordIdentifierRelations(context, fromId, args, () => "mixin");
|
|
1497
|
+
await recordIdentifierRelations(context, fromId, args, () => "mixin");
|
|
667
1498
|
}
|
|
668
1499
|
}
|
|
669
1500
|
if (rules.embeds)
|
|
670
|
-
recordEmbedRelations(context, fromId, cls.node, rules.embeds);
|
|
1501
|
+
await recordEmbedRelations(context, fromId, cls.node, rules.embeds);
|
|
671
1502
|
}
|
|
672
1503
|
}
|
|
673
1504
|
export function emitRustImplEdges(context, rootNode) {
|
|
674
|
-
if (context.sup.id !== "rust")
|
|
675
|
-
return;
|
|
676
1505
|
const walkImpls = (node) => {
|
|
677
1506
|
if (node.type === "impl_item") {
|
|
678
1507
|
const typeIdentifiers = node.namedChildren?.filter((child) => child.type === "type_identifier") ?? [];
|