@lzehrung/codegraph 2.3.30 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -137
- package/codegraph-skill/codegraph/SKILL.md +5 -3
- package/dist/agent/artifact.d.ts +2 -0
- package/dist/agent/artifact.d.ts.map +1 -1
- package/dist/agent/artifact.js.map +1 -1
- package/dist/agent/search.d.ts.map +1 -1
- package/dist/agent/search.js +3 -0
- package/dist/agent/search.js.map +1 -1
- package/dist/agent/session.d.ts +3 -0
- package/dist/agent/session.d.ts.map +1 -1
- package/dist/agent/session.js +10 -2
- package/dist/agent/session.js.map +1 -1
- package/dist/bin/cli.js +42229 -28876
- package/dist/bin/query-index-worker.js +73 -11
- package/dist/cli/affected.d.ts.map +1 -1
- package/dist/cli/affected.js +4 -1
- package/dist/cli/affected.js.map +1 -1
- package/dist/cli/command-catalog.js +1 -1
- package/dist/cli/command-catalog.js.map +1 -1
- package/dist/cli/graph-queries.d.ts.map +1 -1
- package/dist/cli/graph-queries.js +27 -1
- package/dist/cli/graph-queries.js.map +1 -1
- package/dist/cli/help.d.ts +2 -2
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +4 -2
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/options.js +2 -2
- package/dist/cli/options.js.map +1 -1
- package/dist/drift/artifact.d.ts.map +1 -1
- package/dist/drift/artifact.js +15 -18
- package/dist/drift/artifact.js.map +1 -1
- package/dist/drift/compare.js +1 -1
- package/dist/drift/compare.js.map +1 -1
- package/dist/drift/snapshot.d.ts.map +1 -1
- package/dist/drift/snapshot.js +5 -6
- package/dist/drift/snapshot.js.map +1 -1
- package/dist/drift/types.d.ts +2 -0
- package/dist/drift/types.d.ts.map +1 -1
- package/dist/drift/types.js.map +1 -1
- package/dist/graph-builder.d.ts.map +1 -1
- package/dist/graph-builder.js +4 -6
- package/dist/graph-builder.js.map +1 -1
- package/dist/graph-edge-collector.d.ts +3 -0
- package/dist/graph-edge-collector.d.ts.map +1 -1
- package/dist/graph-edge-collector.js +8 -5
- package/dist/graph-edge-collector.js.map +1 -1
- package/dist/graphs/angularjs.d.ts.map +1 -1
- package/dist/graphs/angularjs.js +2 -1
- package/dist/graphs/angularjs.js.map +1 -1
- package/dist/graphs/edge-resolution.d.ts +4 -1
- package/dist/graphs/edge-resolution.d.ts.map +1 -1
- package/dist/graphs/edge-resolution.js +26 -2
- package/dist/graphs/edge-resolution.js.map +1 -1
- package/dist/graphs/specifiers.d.ts.map +1 -1
- package/dist/graphs/specifiers.js +6 -2
- package/dist/graphs/specifiers.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/ast.d.ts +20 -0
- package/dist/graphs/symbol-graph-detailed/ast.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/ast.js +154 -15
- package/dist/graphs/symbol-graph-detailed/ast.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/edge-passes.d.ts +51 -8
- package/dist/graphs/symbol-graph-detailed/edge-passes.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/edge-passes.js +823 -132
- package/dist/graphs/symbol-graph-detailed/edge-passes.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/import-aliases.d.ts +2 -1
- package/dist/graphs/symbol-graph-detailed/import-aliases.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/import-aliases.js +139 -7
- package/dist/graphs/symbol-graph-detailed/import-aliases.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/member-chains.d.ts +1 -0
- package/dist/graphs/symbol-graph-detailed/member-chains.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/member-chains.js +3 -1
- package/dist/graphs/symbol-graph-detailed/member-chains.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/receiver-calls.d.ts +128 -22
- package/dist/graphs/symbol-graph-detailed/receiver-calls.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/receiver-calls.js +872 -125
- package/dist/graphs/symbol-graph-detailed/receiver-calls.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed.js +434 -40
- package/dist/graphs/symbol-graph-detailed.js.map +1 -1
- package/dist/graphs/symbol-graph.d.ts +2 -0
- package/dist/graphs/symbol-graph.d.ts.map +1 -1
- package/dist/graphs/symbol-graph.js +34 -16
- package/dist/graphs/symbol-graph.js.map +1 -1
- package/dist/impact/call-compatibility/providers/index.d.ts +12 -0
- package/dist/impact/call-compatibility/providers/index.d.ts.map +1 -1
- package/dist/impact/call-compatibility/providers/index.js +16 -22
- package/dist/impact/call-compatibility/providers/index.js.map +1 -1
- package/dist/impact/call-compatibility/providers/types.d.ts +2 -0
- package/dist/impact/call-compatibility/providers/types.d.ts.map +1 -1
- package/dist/impact/call-compatibility/types.d.ts +6 -0
- package/dist/impact/call-compatibility/types.d.ts.map +1 -1
- package/dist/impact/call-compatibility.d.ts.map +1 -1
- package/dist/impact/call-compatibility.js +316 -552
- package/dist/impact/call-compatibility.js.map +1 -1
- package/dist/impact/map.js +1 -0
- package/dist/impact/map.js.map +1 -1
- package/dist/impact/member-resolution-coverage.d.ts.map +1 -1
- package/dist/impact/member-resolution-coverage.js +1 -1
- package/dist/impact/member-resolution-coverage.js.map +1 -1
- package/dist/impact/reference-cache.d.ts.map +1 -1
- package/dist/impact/reference-cache.js +2 -2
- package/dist/impact/reference-cache.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/indexer/ambiguous-resolution.d.ts +11 -0
- package/dist/indexer/ambiguous-resolution.d.ts.map +1 -0
- package/dist/indexer/ambiguous-resolution.js +19 -0
- package/dist/indexer/ambiguous-resolution.js.map +1 -0
- package/dist/indexer/build-cache/module-cache.d.ts +38 -0
- package/dist/indexer/build-cache/module-cache.d.ts.map +1 -1
- package/dist/indexer/build-cache/module-cache.js +151 -9
- package/dist/indexer/build-cache/module-cache.js.map +1 -1
- package/dist/indexer/build-cache/options.d.ts +16 -43
- package/dist/indexer/build-cache/options.d.ts.map +1 -1
- package/dist/indexer/build-cache/options.js +16 -43
- package/dist/indexer/build-cache/options.js.map +1 -1
- package/dist/indexer/build-cache/project-snapshot.d.ts +1 -0
- package/dist/indexer/build-cache/project-snapshot.d.ts.map +1 -1
- package/dist/indexer/build-cache/project-snapshot.js +117 -24
- package/dist/indexer/build-cache/project-snapshot.js.map +1 -1
- package/dist/indexer/build-cache/resolver-environment.d.ts.map +1 -1
- package/dist/indexer/build-cache/resolver-environment.js +32 -2
- package/dist/indexer/build-cache/resolver-environment.js.map +1 -1
- package/dist/indexer/build-cache.d.ts +1 -1
- package/dist/indexer/build-cache.d.ts.map +1 -1
- package/dist/indexer/build-cache.js +1 -1
- package/dist/indexer/build-cache.js.map +1 -1
- package/dist/indexer/build-index.d.ts.map +1 -1
- package/dist/indexer/build-index.js +593 -22
- package/dist/indexer/build-index.js.map +1 -1
- package/dist/indexer/call-hierarchy.js +1 -1
- package/dist/indexer/call-hierarchy.js.map +1 -1
- package/dist/indexer/compilation-units.d.ts +101 -0
- package/dist/indexer/compilation-units.d.ts.map +1 -0
- package/dist/indexer/compilation-units.js +605 -0
- package/dist/indexer/compilation-units.js.map +1 -0
- package/dist/indexer/cpp-callables.d.ts +20 -0
- package/dist/indexer/cpp-callables.d.ts.map +1 -0
- package/dist/indexer/cpp-callables.js +554 -0
- package/dist/indexer/cpp-callables.js.map +1 -0
- package/dist/indexer/declaration-visibility.d.ts +36 -2
- package/dist/indexer/declaration-visibility.d.ts.map +1 -1
- package/dist/indexer/declaration-visibility.js +136 -2
- package/dist/indexer/declaration-visibility.js.map +1 -1
- package/dist/indexer/expand-star-imports.d.ts.map +1 -1
- package/dist/indexer/expand-star-imports.js +28 -6
- package/dist/indexer/expand-star-imports.js.map +1 -1
- package/dist/indexer/import-types.d.ts +37 -0
- package/dist/indexer/import-types.d.ts.map +1 -1
- package/dist/indexer/import-types.js +24 -1
- package/dist/indexer/import-types.js.map +1 -1
- package/dist/indexer/imports/binding-ranges.d.ts +1 -1
- package/dist/indexer/imports/binding-ranges.d.ts.map +1 -1
- package/dist/indexer/imports/context.d.ts +11 -1
- package/dist/indexer/imports/context.d.ts.map +1 -1
- package/dist/indexer/imports/import-binding-tables.d.ts +75 -0
- package/dist/indexer/imports/import-binding-tables.d.ts.map +1 -0
- package/dist/indexer/imports/import-binding-tables.js +544 -0
- package/dist/indexer/imports/import-binding-tables.js.map +1 -0
- package/dist/indexer/imports/js-text-imports.js +4 -4
- package/dist/indexer/imports/js-text-imports.js.map +1 -1
- package/dist/indexer/imports/language-specific.d.ts +3 -22
- package/dist/indexer/imports/language-specific.d.ts.map +1 -1
- package/dist/indexer/imports/language-specific.js +29 -362
- package/dist/indexer/imports/language-specific.js.map +1 -1
- package/dist/indexer/imports/native-captures.d.ts.map +1 -1
- package/dist/indexer/imports/native-captures.js +18 -6
- package/dist/indexer/imports/native-captures.js.map +1 -1
- package/dist/indexer/imports/python.d.ts +10 -2
- package/dist/indexer/imports/python.d.ts.map +1 -1
- package/dist/indexer/imports/python.js +91 -40
- package/dist/indexer/imports/python.js.map +1 -1
- package/dist/indexer/imports/text-import-extractors.d.ts.map +1 -1
- package/dist/indexer/imports/text-import-extractors.js +51 -12
- package/dist/indexer/imports/text-import-extractors.js.map +1 -1
- package/dist/indexer/imports.d.ts.map +1 -1
- package/dist/indexer/imports.js +25 -5
- package/dist/indexer/imports.js.map +1 -1
- package/dist/indexer/incremental-plan.d.ts +33 -0
- package/dist/indexer/incremental-plan.d.ts.map +1 -1
- package/dist/indexer/incremental-plan.js +187 -1
- package/dist/indexer/incremental-plan.js.map +1 -1
- package/dist/indexer/locals-and-exports.d.ts +2 -0
- package/dist/indexer/locals-and-exports.d.ts.map +1 -1
- package/dist/indexer/locals-and-exports.js +565 -54
- package/dist/indexer/locals-and-exports.js.map +1 -1
- package/dist/indexer/name-lookup-policies/c-family.d.ts +7 -0
- package/dist/indexer/name-lookup-policies/c-family.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/c-family.js +117 -0
- package/dist/indexer/name-lookup-policies/c-family.js.map +1 -0
- package/dist/indexer/name-lookup-policies/csharp-using-static.d.ts +11 -0
- package/dist/indexer/name-lookup-policies/csharp-using-static.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/csharp-using-static.js +136 -0
- package/dist/indexer/name-lookup-policies/csharp-using-static.js.map +1 -0
- package/dist/indexer/name-lookup-policies/implicit-self.d.ts +10 -0
- package/dist/indexer/name-lookup-policies/implicit-self.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/implicit-self.js +113 -0
- package/dist/indexer/name-lookup-policies/implicit-self.js.map +1 -0
- package/dist/indexer/name-lookup-policies/index.d.ts +3 -0
- package/dist/indexer/name-lookup-policies/index.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/index.js +106 -0
- package/dist/indexer/name-lookup-policies/index.js.map +1 -0
- package/dist/indexer/name-lookup-policies/php.d.ts +3 -0
- package/dist/indexer/name-lookup-policies/php.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/php.js +75 -0
- package/dist/indexer/name-lookup-policies/php.js.map +1 -0
- package/dist/indexer/name-resolution-types.d.ts +99 -0
- package/dist/indexer/name-resolution-types.d.ts.map +1 -0
- package/dist/indexer/name-resolution-types.js +2 -0
- package/dist/indexer/name-resolution-types.js.map +1 -0
- package/dist/indexer/name-resolution.d.ts +52 -0
- package/dist/indexer/name-resolution.d.ts.map +1 -0
- package/dist/indexer/name-resolution.js +189 -0
- package/dist/indexer/name-resolution.js.map +1 -0
- package/dist/indexer/navigation-cpp.d.ts +50 -0
- package/dist/indexer/navigation-cpp.d.ts.map +1 -0
- package/dist/indexer/navigation-cpp.js +654 -0
- package/dist/indexer/navigation-cpp.js.map +1 -0
- package/dist/indexer/navigation-goto.d.ts +107 -4
- package/dist/indexer/navigation-goto.d.ts.map +1 -1
- package/dist/indexer/navigation-goto.js +2075 -137
- package/dist/indexer/navigation-goto.js.map +1 -1
- package/dist/indexer/navigation-local.d.ts +18 -2
- package/dist/indexer/navigation-local.d.ts.map +1 -1
- package/dist/indexer/navigation-local.js +498 -61
- package/dist/indexer/navigation-local.js.map +1 -1
- package/dist/indexer/navigation-php.d.ts +66 -0
- package/dist/indexer/navigation-php.d.ts.map +1 -1
- package/dist/indexer/navigation-php.js +287 -4
- package/dist/indexer/navigation-php.js.map +1 -1
- package/dist/indexer/navigation-references.d.ts +72 -5
- package/dist/indexer/navigation-references.d.ts.map +1 -1
- package/dist/indexer/navigation-references.js +620 -54
- package/dist/indexer/navigation-references.js.map +1 -1
- package/dist/indexer/navigation-resolve.d.ts +33 -2
- package/dist/indexer/navigation-resolve.d.ts.map +1 -1
- package/dist/indexer/navigation-resolve.js +446 -193
- package/dist/indexer/navigation-resolve.js.map +1 -1
- package/dist/indexer/navigation.d.ts +28 -2
- package/dist/indexer/navigation.d.ts.map +1 -1
- package/dist/indexer/navigation.js +1082 -151
- package/dist/indexer/navigation.js.map +1 -1
- package/dist/indexer/php-namespace-symbols.d.ts +66 -0
- package/dist/indexer/php-namespace-symbols.d.ts.map +1 -0
- package/dist/indexer/php-namespace-symbols.js +342 -0
- package/dist/indexer/php-namespace-symbols.js.map +1 -0
- package/dist/indexer/reference-context.d.ts +2 -0
- package/dist/indexer/reference-context.d.ts.map +1 -1
- package/dist/indexer/reference-context.js +4 -0
- package/dist/indexer/reference-context.js.map +1 -1
- package/dist/indexer/scope-nodes.d.ts +92 -13
- package/dist/indexer/scope-nodes.d.ts.map +1 -1
- package/dist/indexer/scope-nodes.js +212 -20
- package/dist/indexer/scope-nodes.js.map +1 -1
- package/dist/indexer/scope-types.d.ts +25 -1
- package/dist/indexer/scope-types.d.ts.map +1 -1
- package/dist/indexer/scope.d.ts.map +1 -1
- package/dist/indexer/scope.js +894 -45
- package/dist/indexer/scope.js.map +1 -1
- package/dist/indexer/shared-owner-identity.d.ts +53 -0
- package/dist/indexer/shared-owner-identity.d.ts.map +1 -0
- package/dist/indexer/shared-owner-identity.js +460 -0
- package/dist/indexer/shared-owner-identity.js.map +1 -0
- package/dist/indexer/shared.d.ts +1 -1
- package/dist/indexer/shared.d.ts.map +1 -1
- package/dist/indexer/shared.js +1 -1
- package/dist/indexer/shared.js.map +1 -1
- package/dist/indexer/star-import-precedence.d.ts +114 -0
- package/dist/indexer/star-import-precedence.d.ts.map +1 -0
- package/dist/indexer/star-import-precedence.js +396 -0
- package/dist/indexer/star-import-precedence.js.map +1 -0
- package/dist/indexer/symbols.d.ts +3 -1
- package/dist/indexer/symbols.d.ts.map +1 -1
- package/dist/indexer/symbols.js +58 -18
- package/dist/indexer/symbols.js.map +1 -1
- package/dist/indexer/ts-callables.d.ts +51 -0
- package/dist/indexer/ts-callables.d.ts.map +1 -0
- package/dist/indexer/ts-callables.js +264 -0
- package/dist/indexer/ts-callables.js.map +1 -0
- package/dist/indexer/types.d.ts +23 -1
- package/dist/indexer/types.d.ts.map +1 -1
- package/dist/indexer/unused-exports.d.ts +13 -0
- package/dist/indexer/unused-exports.d.ts.map +1 -0
- package/dist/indexer/unused-exports.js +159 -0
- package/dist/indexer/unused-exports.js.map +1 -0
- package/dist/indexer/workspace-symbols.d.ts.map +1 -1
- package/dist/indexer/workspace-symbols.js +3 -2
- package/dist/indexer/workspace-symbols.js.map +1 -1
- package/dist/indexer.d.ts +2 -2
- package/dist/indexer.d.ts.map +1 -1
- package/dist/indexer.js +1 -1
- package/dist/indexer.js.map +1 -1
- package/dist/languages/callable-arity.d.ts +202 -0
- package/dist/languages/callable-arity.d.ts.map +1 -0
- package/dist/languages/callable-arity.js +1182 -0
- package/dist/languages/callable-arity.js.map +1 -0
- package/dist/languages/definitions/c-family.d.ts.map +1 -1
- package/dist/languages/definitions/c-family.js +30 -1
- package/dist/languages/definitions/c-family.js.map +1 -1
- package/dist/languages/definitions/c.d.ts +4 -0
- package/dist/languages/definitions/c.d.ts.map +1 -1
- package/dist/languages/definitions/c.js +19 -0
- package/dist/languages/definitions/c.js.map +1 -1
- package/dist/languages/definitions/cpp.d.ts.map +1 -1
- package/dist/languages/definitions/cpp.js +12 -0
- package/dist/languages/definitions/cpp.js.map +1 -1
- package/dist/languages/definitions/csharp.d.ts.map +1 -1
- package/dist/languages/definitions/csharp.js +2 -8
- package/dist/languages/definitions/csharp.js.map +1 -1
- package/dist/languages/definitions/go.d.ts.map +1 -1
- package/dist/languages/definitions/go.js +3 -0
- package/dist/languages/definitions/go.js.map +1 -1
- package/dist/languages/definitions/javascript.d.ts.map +1 -1
- package/dist/languages/definitions/javascript.js +10 -1
- package/dist/languages/definitions/javascript.js.map +1 -1
- package/dist/languages/definitions/js-family.d.ts +1 -1
- package/dist/languages/definitions/js-family.d.ts.map +1 -1
- package/dist/languages/definitions/js-family.js +7 -1
- package/dist/languages/definitions/js-family.js.map +1 -1
- package/dist/languages/definitions/php.d.ts.map +1 -1
- package/dist/languages/definitions/php.js +5 -1
- package/dist/languages/definitions/php.js.map +1 -1
- package/dist/languages/definitions/typescript.d.ts.map +1 -1
- package/dist/languages/definitions/typescript.js +8 -1
- package/dist/languages/definitions/typescript.js.map +1 -1
- package/dist/languages/definitions/zig.d.ts.map +1 -1
- package/dist/languages/definitions/zig.js +2 -0
- package/dist/languages/definitions/zig.js.map +1 -1
- package/dist/mcp/handlers.js +14 -7
- package/dist/mcp/handlers.js.map +1 -1
- package/dist/review/candidates.d.ts.map +1 -1
- package/dist/review/candidates.js +1 -1
- package/dist/review/candidates.js.map +1 -1
- package/dist/review/deleted.d.ts +3 -3
- package/dist/review/deleted.d.ts.map +1 -1
- package/dist/review/deleted.js +82 -32
- package/dist/review/deleted.js.map +1 -1
- package/dist/review/report.d.ts +1 -0
- package/dist/review/report.d.ts.map +1 -1
- package/dist/review/report.js +1 -1
- package/dist/review/report.js.map +1 -1
- package/dist/review.d.ts.map +1 -1
- package/dist/review.js +1 -0
- package/dist/review.js.map +1 -1
- package/dist/sql/navigation.d.ts.map +1 -1
- package/dist/sql/navigation.js +1 -0
- package/dist/sql/navigation.js.map +1 -1
- package/dist/sql/source-graph.d.ts.map +1 -1
- package/dist/sql/source-graph.js +2 -2
- package/dist/sql/source-graph.js.map +1 -1
- package/dist/sqlite/schema.d.ts +1 -1
- package/dist/sqlite/schema.d.ts.map +1 -1
- package/dist/sqlite/schema.js +8 -2
- package/dist/sqlite/schema.js.map +1 -1
- package/dist/sqlite/write.js +2 -2
- package/dist/sqlite/write.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/util/bloom-filter.d.ts +7 -1
- package/dist/util/bloom-filter.d.ts.map +1 -1
- package/dist/util/bloom-filter.js +12 -2
- package/dist/util/bloom-filter.js.map +1 -1
- package/dist/util/dynamic-import-tables.d.ts +88 -0
- package/dist/util/dynamic-import-tables.d.ts.map +1 -0
- package/dist/util/dynamic-import-tables.js +100 -0
- package/dist/util/dynamic-import-tables.js.map +1 -0
- package/dist/util/graph-edges.d.ts +19 -1
- package/dist/util/graph-edges.d.ts.map +1 -1
- package/dist/util/graph-edges.js +30 -4
- package/dist/util/graph-edges.js.map +1 -1
- package/dist/util/identifiers.d.ts +15 -0
- package/dist/util/identifiers.d.ts.map +1 -1
- package/dist/util/identifiers.js +35 -0
- package/dist/util/identifiers.js.map +1 -1
- package/dist/util/member-access-tables.d.ts +129 -0
- package/dist/util/member-access-tables.d.ts.map +1 -0
- package/dist/util/member-access-tables.js +285 -0
- package/dist/util/member-access-tables.js.map +1 -0
- package/dist/util/member-access.d.ts +17 -0
- package/dist/util/member-access.d.ts.map +1 -1
- package/dist/util/member-access.js +138 -93
- package/dist/util/member-access.js.map +1 -1
- package/dist/util/resolution/cargo-targets.d.ts +11 -0
- package/dist/util/resolution/cargo-targets.d.ts.map +1 -1
- package/dist/util/resolution/cargo-targets.js +174 -13
- package/dist/util/resolution/cargo-targets.js.map +1 -1
- package/dist/util/resolution/csharp.d.ts +33 -1
- package/dist/util/resolution/csharp.d.ts.map +1 -1
- package/dist/util/resolution/csharp.js +145 -30
- package/dist/util/resolution/csharp.js.map +1 -1
- package/dist/util/resolution/php.d.ts.map +1 -1
- package/dist/util/resolution/php.js +35 -8
- package/dist/util/resolution/php.js.map +1 -1
- package/dist/util/resolution/python.d.ts +9 -0
- package/dist/util/resolution/python.d.ts.map +1 -1
- package/dist/util/resolution/python.js +46 -0
- package/dist/util/resolution/python.js.map +1 -1
- package/dist/util/resolution/rust.d.ts.map +1 -1
- package/dist/util/resolution/rust.js +95 -26
- package/dist/util/resolution/rust.js.map +1 -1
- package/dist/util/resolution-candidates.d.ts +5 -0
- package/dist/util/resolution-candidates.d.ts.map +1 -1
- package/dist/util/resolution-candidates.js +16 -3
- package/dist/util/resolution-candidates.js.map +1 -1
- package/dist/util/resolution.d.ts +2 -1
- package/dist/util/resolution.d.ts.map +1 -1
- package/dist/util/resolution.js +96 -5
- package/dist/util/resolution.js.map +1 -1
- package/dist/util/specifiers.d.ts +20 -0
- package/dist/util/specifiers.d.ts.map +1 -1
- package/dist/util/specifiers.js +39 -93
- package/dist/util/specifiers.js.map +1 -1
- package/docs/mcp.md +3 -2
- package/package.json +3 -3
|
@@ -1,29 +1,14 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
|
+
import { findCommentEnd } from "../../impact/call-compatibility/text-scanner.js";
|
|
3
|
+
import { isGoExportedMemberName } from "../../indexer/declaration-visibility.js";
|
|
2
4
|
import { SymbolKind } from "../../indexer/types.js";
|
|
5
|
+
import { isJsTsLanguage } from "../../languages/js-family.js";
|
|
3
6
|
import { sliceText } from "../../util/ast.js";
|
|
4
|
-
import { XID_IDENTIFIER_SOURCE } from "../../util/identifiers.js";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
cpp: { own: ["this"], supertype: [] },
|
|
10
|
-
csharp: { own: ["this"], supertype: ["base"] },
|
|
11
|
-
go: { own: [], supertype: [] },
|
|
12
|
-
java: THIS_SUPER_RECEIVERS,
|
|
13
|
-
js: THIS_SUPER_RECEIVERS,
|
|
14
|
-
kotlin: THIS_SUPER_RECEIVERS,
|
|
15
|
-
php: { own: ["$this", "self", "static"], supertype: ["parent"] },
|
|
16
|
-
python: { own: ["self", "cls"], supertype: [] },
|
|
17
|
-
ruby: { own: ["self"], supertype: [] },
|
|
18
|
-
rust: { own: ["self", "Self"], supertype: [] },
|
|
19
|
-
swift: SELF_RECEIVERS,
|
|
20
|
-
ts: THIS_SUPER_RECEIVERS,
|
|
21
|
-
tsx: THIS_SUPER_RECEIVERS,
|
|
22
|
-
zig: { own: ["self"], supertype: [] },
|
|
23
|
-
};
|
|
24
|
-
const INSTANCE_RECEIVER_KEYWORDS = new Set(["this", "$this"]);
|
|
25
|
-
/** Every language that declares receiver keywords, guarded by the registry-consistency test. */
|
|
26
|
-
export const receiverKeywordLanguageIds = Object.keys(RECEIVER_KEYWORDS);
|
|
7
|
+
import { foldPhpIdentifierCase, XID_IDENTIFIER_SOURCE } from "../../util/identifiers.js";
|
|
8
|
+
import { fileIdentityKey } from "../../util/paths.js";
|
|
9
|
+
import { keywordReceiverKind, ownReceiverMemberScope } from "../../util/member-access-tables.js";
|
|
10
|
+
import { getMemberAccessParts, getNavigationExpressionProperty, isMemberAccessNode, isMemberReferencePropertyIdentifier, isReceiverNameNode, receiverKeywordText, } from "../../util/member-access.js";
|
|
11
|
+
import { declarationMemberArity, findFirstNodeByType, isIdentifierType, PARAMETER_LIST_NODE_TYPES } from "./ast.js";
|
|
27
12
|
/** Languages whose grammar distinguishes static members from instance members. */
|
|
28
13
|
const STATIC_MEMBER_LANGUAGES = {
|
|
29
14
|
cpp: true,
|
|
@@ -35,24 +20,68 @@ const STATIC_MEMBER_LANGUAGES = {
|
|
|
35
20
|
ts: true,
|
|
36
21
|
tsx: true,
|
|
37
22
|
};
|
|
23
|
+
const MEMBER_OVERLOAD_LANGUAGE_IDS = {
|
|
24
|
+
cpp: true,
|
|
25
|
+
csharp: true,
|
|
26
|
+
java: true,
|
|
27
|
+
kotlin: true,
|
|
28
|
+
swift: true,
|
|
29
|
+
ts: true,
|
|
30
|
+
tsx: true,
|
|
31
|
+
};
|
|
32
|
+
/** Bare calls inside members can target a proven member even without an explicit receiver. */
|
|
33
|
+
const IMPLICIT_SELF_MEMBER_CALL_LANGUAGES = {
|
|
34
|
+
csharp: true,
|
|
35
|
+
swift: true,
|
|
36
|
+
};
|
|
37
|
+
/** Whether a bare, receiver-less call inside a member function may target `this`/an inherited member. */
|
|
38
|
+
export function supportsImplicitSelfMemberCalls(languageId) {
|
|
39
|
+
return !!IMPLICIT_SELF_MEMBER_CALL_LANGUAGES[languageId];
|
|
40
|
+
}
|
|
41
|
+
/** Whether member lookup uses call arity to select or reject same-name declarations. */
|
|
42
|
+
export function supportsReceiverMemberOverloads(languageId) {
|
|
43
|
+
return !!MEMBER_OVERLOAD_LANGUAGE_IDS[languageId];
|
|
44
|
+
}
|
|
38
45
|
/** Every language with a static-member distinction, guarded by the registry-consistency test. */
|
|
39
46
|
export const staticMemberLanguageIds = Object.keys(STATIC_MEMBER_LANGUAGES);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return RECEIVER_KEYWORDS[languageId] !== undefined;
|
|
43
|
-
}
|
|
44
|
-
/** Languages where goto and references require proven receivers and do not fall back to a bare name. */
|
|
45
|
-
export function supportsReceiverMemberNavigation(languageId) {
|
|
46
|
-
return RECEIVER_KEYWORDS[languageId] !== undefined;
|
|
47
|
+
export function hasStaticMemberDistinction(languageId) {
|
|
48
|
+
return STATIC_MEMBER_LANGUAGES[languageId] !== undefined;
|
|
47
49
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Languages whose grammar gives members a static-equivalent scope without a `static` keyword:
|
|
52
|
+
* a Kotlin member of a `companion object` or a named `object` declaration is reachable as
|
|
53
|
+
* `Outer.member()`. Member lookup classifies those members "static" and every other member
|
|
54
|
+
* "instance", matching the keyword-`static` languages.
|
|
55
|
+
*/
|
|
56
|
+
const STATIC_EQUIVALENT_MEMBER_CONTAINERS = {
|
|
57
|
+
kotlin: { companion_object: true, object_declaration: true },
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Whether `node` is lexically declared inside its language's static-equivalent member
|
|
61
|
+
* container. The nearest enclosing member container decides: a nested type's members belong
|
|
62
|
+
* to that type, not to an outer `object`.
|
|
63
|
+
*/
|
|
64
|
+
export function declarationIsStaticEquivalent(languageId, node) {
|
|
65
|
+
const containers = STATIC_EQUIVALENT_MEMBER_CONTAINERS[languageId];
|
|
66
|
+
if (!containers)
|
|
51
67
|
return false;
|
|
52
|
-
|
|
68
|
+
let current = node.parent;
|
|
69
|
+
while (current) {
|
|
70
|
+
if (containers[current.type] === true)
|
|
71
|
+
return true;
|
|
72
|
+
if (MEMBER_CONTAINER_TYPES[current.type] === true)
|
|
73
|
+
return false;
|
|
74
|
+
current = current.parent;
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
53
77
|
}
|
|
54
|
-
|
|
55
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Whether member lookup for `languageId` distinguishes static-equivalent members from
|
|
80
|
+
* instance members. A bare type-name receiver restricts lookup to the static-equivalent
|
|
81
|
+
* scope exactly when this holds; otherwise every member must stay reachable.
|
|
82
|
+
*/
|
|
83
|
+
export function supportsStaticMemberScope(languageId) {
|
|
84
|
+
return (STATIC_MEMBER_LANGUAGES[languageId] !== undefined || STATIC_EQUIVALENT_MEMBER_CONTAINERS[languageId] !== undefined);
|
|
56
85
|
}
|
|
57
86
|
/**
|
|
58
87
|
* Call nodes that carry the receiver and the member name on the call node itself
|
|
@@ -84,6 +113,25 @@ const MEMBER_CONTAINER_TYPES = {
|
|
|
84
113
|
struct_specifier: true,
|
|
85
114
|
trait_declaration: true,
|
|
86
115
|
trait_item: true,
|
|
116
|
+
type_alias_declaration: true,
|
|
117
|
+
// C/C++ unions declare members exactly like structs (tree-sitter-cpp captures
|
|
118
|
+
// union names and classifies them as classes).
|
|
119
|
+
union_specifier: true,
|
|
120
|
+
};
|
|
121
|
+
const CPP_MEMBER_CONTAINER_TYPES = {
|
|
122
|
+
class_specifier: true,
|
|
123
|
+
struct_specifier: true,
|
|
124
|
+
union_specifier: true,
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Container nodes whose declared members belong to the *enclosing* member container for
|
|
128
|
+
* direct-member lookup, rather than forming a separate nested type. Kotlin's unnamed
|
|
129
|
+
* `companion object { ... }` block is the language's static-member mechanism: `create()`
|
|
130
|
+
* declared inside one is a member of the enclosing class, reachable as `Outer.create()`,
|
|
131
|
+
* not a member of a distinct "Companion" type the indexer would need to name separately.
|
|
132
|
+
*/
|
|
133
|
+
export const TRANSPARENT_MEMBER_CONTAINER_TYPES = {
|
|
134
|
+
companion_object: true,
|
|
87
135
|
};
|
|
88
136
|
/** Nodes holding a call's argument list across the supported grammars. */
|
|
89
137
|
export const CALL_ARGUMENT_NODE_TYPES = {
|
|
@@ -99,6 +147,16 @@ const HIERARCHY_LABELS = {
|
|
|
99
147
|
mixin: true,
|
|
100
148
|
trait: true,
|
|
101
149
|
};
|
|
150
|
+
const UNPROVEN_HERITAGE_EXPRESSION_TYPES = new Set([
|
|
151
|
+
"binary_expression",
|
|
152
|
+
"call_expression",
|
|
153
|
+
"new_expression",
|
|
154
|
+
"subscript_expression",
|
|
155
|
+
"ternary_expression",
|
|
156
|
+
]);
|
|
157
|
+
export function isUnprovenHeritageExpression(node) {
|
|
158
|
+
return UNPROVEN_HERITAGE_EXPRESSION_TYPES.has(node.type);
|
|
159
|
+
}
|
|
102
160
|
/**
|
|
103
161
|
* Nodes that bind a value name across the supported grammars: locals, parameters,
|
|
104
162
|
* and assignment targets. A receiver naming one of these is a value, not a type.
|
|
@@ -108,6 +166,9 @@ const VALUE_BINDING_TYPES = {
|
|
|
108
166
|
assignment_expression: true,
|
|
109
167
|
assignment_statement: true,
|
|
110
168
|
class_parameter: true,
|
|
169
|
+
// C/C++ local variable statement (`Box b;`, `Box* p = raw;`); the declared type sits
|
|
170
|
+
// beside the declarator, unlike other grammars' dedicated `variable_declaration` node.
|
|
171
|
+
declaration: true,
|
|
111
172
|
formal_parameter: true,
|
|
112
173
|
init_declarator: true,
|
|
113
174
|
let_declaration: true,
|
|
@@ -123,21 +184,6 @@ const VALUE_BINDING_TYPES = {
|
|
|
123
184
|
variable_declaration: true,
|
|
124
185
|
variable_declarator: true,
|
|
125
186
|
};
|
|
126
|
-
/**
|
|
127
|
-
* Parameter lists whose identifier children are value bindings (Ruby has no wrapping param node).
|
|
128
|
-
* Union of the receiver-call and call-compatibility lists so Kotlin `function_value_parameters`
|
|
129
|
-
* and Ruby `block_parameters` are both recognized.
|
|
130
|
-
*/
|
|
131
|
-
export const PARAMETER_LIST_NODE_TYPES = {
|
|
132
|
-
block_parameters: true,
|
|
133
|
-
formal_parameters: true,
|
|
134
|
-
function_parameter_clause: true,
|
|
135
|
-
function_value_parameters: true,
|
|
136
|
-
lambda_parameters: true,
|
|
137
|
-
method_parameters: true,
|
|
138
|
-
parameter_list: true,
|
|
139
|
-
parameters: true,
|
|
140
|
-
};
|
|
141
187
|
/**
|
|
142
188
|
* Access nodes whose syntax is type-scoped (`::`), not dotted member access.
|
|
143
189
|
* A dotted identifier is never itself proof of a type.
|
|
@@ -213,6 +259,8 @@ const BINDING_DECLARATION_TYPES = new Set([
|
|
|
213
259
|
"property_declaration",
|
|
214
260
|
"local_variable_declaration",
|
|
215
261
|
"local_declaration_statement",
|
|
262
|
+
// C/C++ local variable statement, e.g. `Box b;`, `Box* p = raw;`, `Box w{5};`.
|
|
263
|
+
"declaration",
|
|
216
264
|
]);
|
|
217
265
|
const RUBY_CONSTANT_SOURCE = String.raw `(?=\p{Lu})${XID_IDENTIFIER_SOURCE}`;
|
|
218
266
|
/** Proven construction forms that name a receiver's type, keyed by language. */
|
|
@@ -226,7 +274,7 @@ const LANGUAGE_CONSTRUCTION_FORMS = {
|
|
|
226
274
|
php: { newExpression: true },
|
|
227
275
|
python: { capitalizedCall: true },
|
|
228
276
|
ruby: { rubyNew: true },
|
|
229
|
-
rust: { capitalizedCall: true, rustUnitStruct: true },
|
|
277
|
+
rust: { capitalizedCall: true, rustUnitStruct: true, compositeLiteral: true },
|
|
230
278
|
swift: { capitalizedCall: true },
|
|
231
279
|
ts: { newExpression: true },
|
|
232
280
|
tsx: { newExpression: true },
|
|
@@ -270,12 +318,13 @@ export function receiverCallAccess(sup, callNode, callee) {
|
|
|
270
318
|
return { accessNode, receiver, property };
|
|
271
319
|
}
|
|
272
320
|
/**
|
|
273
|
-
* Identifier a binding node declares: a `name` field, a nested C/C++
|
|
274
|
-
* an assignment left-hand side, or the last identifier child (C++
|
|
275
|
-
* the name after the type).
|
|
321
|
+
* Identifier a binding node declares: a `name` or `pattern` field, a nested C/C++
|
|
322
|
+
* declarator, an assignment left-hand side, or the last identifier child (C++
|
|
323
|
+
* parameters hide the name after the type). Rust spells parameter and `let` names in
|
|
324
|
+
* the `pattern` field, where the last-identifier fallback would pick the declared type.
|
|
276
325
|
*/
|
|
277
326
|
function bindingIdentifier(node, sup) {
|
|
278
|
-
const named = node.childForFieldName("name");
|
|
327
|
+
const named = node.childForFieldName("name") ?? node.childForFieldName("pattern");
|
|
279
328
|
if (named && (isReceiverNameNode(sup, named.type) || named.type === "field_identifier")) {
|
|
280
329
|
return named;
|
|
281
330
|
}
|
|
@@ -356,6 +405,17 @@ function containsIndex(node, index) {
|
|
|
356
405
|
return node.startIndex <= index && node.endIndex >= index;
|
|
357
406
|
}
|
|
358
407
|
function constructorNameNode(node, sup) {
|
|
408
|
+
// A dedicated `type` field (C#'s `object_creation_expression`, C/C++'s `new_expression`) can
|
|
409
|
+
// wrap a qualified (`Outer.Inner`) or generic (`Box<int>`) shape the fallback scan below never
|
|
410
|
+
// matches; a language without that field (TS/PHP's `constructor` field, or none) falls through.
|
|
411
|
+
const typeField = node.childForFieldName("type");
|
|
412
|
+
if (typeField) {
|
|
413
|
+
if (isMemberAccessNode(sup, typeField))
|
|
414
|
+
return typeField;
|
|
415
|
+
const unwrapped = unwrapNamedType(typeField, sup);
|
|
416
|
+
if (unwrapped)
|
|
417
|
+
return unwrapped;
|
|
418
|
+
}
|
|
359
419
|
const constructor = node.childForFieldName("constructor") ?? node.child(0);
|
|
360
420
|
if (constructor && isReceiverNameNode(sup, constructor.type)) {
|
|
361
421
|
return constructor;
|
|
@@ -367,14 +427,85 @@ function constructorNameNode(node, sup) {
|
|
|
367
427
|
}
|
|
368
428
|
return null;
|
|
369
429
|
}
|
|
430
|
+
const PHP_OBJECT_CREATION_KEYWORDS = {
|
|
431
|
+
self: "self",
|
|
432
|
+
static: "static",
|
|
433
|
+
parent: "parent",
|
|
434
|
+
};
|
|
435
|
+
export function phpObjectCreationKeyword(node, source, sup) {
|
|
436
|
+
if (sup.id !== "php")
|
|
437
|
+
return null;
|
|
438
|
+
let creation = node;
|
|
439
|
+
while (creation && creation.type !== "object_creation_expression") {
|
|
440
|
+
if (creation.type === "method_declaration" ||
|
|
441
|
+
creation.type === "function_definition" ||
|
|
442
|
+
creation.type === "class_declaration") {
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
creation = creation.parent;
|
|
446
|
+
}
|
|
447
|
+
if (!creation)
|
|
448
|
+
return null;
|
|
449
|
+
const nameNode = constructorNameNode(creation, sup);
|
|
450
|
+
if (!nameNode)
|
|
451
|
+
return null;
|
|
452
|
+
const onName = node === creation || (node.startIndex >= nameNode.startIndex && node.endIndex <= nameNode.endIndex);
|
|
453
|
+
if (!onName)
|
|
454
|
+
return null;
|
|
455
|
+
const keyword = PHP_OBJECT_CREATION_KEYWORDS[foldPhpIdentifierCase(sliceText(nameNode, source))];
|
|
456
|
+
if (!keyword)
|
|
457
|
+
return null;
|
|
458
|
+
const container = nearestMemberContainer(creation);
|
|
459
|
+
const classNode = container?.type === "class_declaration" ? container : null;
|
|
460
|
+
return { keyword, nameNode, classNode };
|
|
461
|
+
}
|
|
462
|
+
/** Members-declaring symbol whose name node is this container's name, when that match is unique. */
|
|
463
|
+
export function memberContainerDef(mod, container) {
|
|
464
|
+
const nameNode = container.childForFieldName("name");
|
|
465
|
+
if (!nameNode)
|
|
466
|
+
return null;
|
|
467
|
+
const matches = mod.locals.filter((local) => declaresMembers(local) && local.range.start.index === nameNode.startIndex);
|
|
468
|
+
return matches.length === 1 ? (matches[0] ?? null) : null;
|
|
469
|
+
}
|
|
370
470
|
function rubyNewReceiverNameNode(node, source, sup) {
|
|
371
471
|
if (!new RegExp(String.raw `^(?:${RUBY_CONSTANT_SOURCE})\.new$`, "u").test(sliceText(node, source)))
|
|
372
472
|
return null;
|
|
373
473
|
return node.namedChildren.find((child) => isReceiverNameNode(sup, child.type) || child.type === "constant") ?? null;
|
|
374
474
|
}
|
|
475
|
+
const NULLISH_TYPE_TEXT = new Set(["undefined", "null", "void"]);
|
|
476
|
+
/**
|
|
477
|
+
* The `typeof import("spec")` type query a TypeScript annotation names, ignoring `| undefined` and
|
|
478
|
+
* `| null`: such a binding holds the module namespace of `spec`.
|
|
479
|
+
*/
|
|
480
|
+
export function typescriptImportTypeQuery(annotation) {
|
|
481
|
+
let current = annotation;
|
|
482
|
+
while (current?.type === "type_annotation")
|
|
483
|
+
current = current.namedChildren[0] ?? null;
|
|
484
|
+
if (current?.type === "union_type") {
|
|
485
|
+
const members = current.namedChildren.filter((member) => !NULLISH_TYPE_TEXT.has(member.text.trim()));
|
|
486
|
+
current = members.length === 1 ? members[0] : null;
|
|
487
|
+
}
|
|
488
|
+
if (current?.type !== "type_query")
|
|
489
|
+
return null;
|
|
490
|
+
const call = current.namedChildren[0];
|
|
491
|
+
return call?.type === "call_expression" && call.childForFieldName("function")?.type === "import" ? current : null;
|
|
492
|
+
}
|
|
493
|
+
/** The module specifier of a `typeof import("spec")` type query. */
|
|
494
|
+
export function importTypeQuerySpecifier(typeQuery) {
|
|
495
|
+
const argument = typeQuery.namedChildren[0]?.childForFieldName("arguments")?.namedChildren[0];
|
|
496
|
+
if (argument?.type !== "string")
|
|
497
|
+
return null;
|
|
498
|
+
return argument.namedChildren.find((child) => child.type === "string_fragment")?.text ?? null;
|
|
499
|
+
}
|
|
375
500
|
export function unwrapNamedType(node, sup) {
|
|
376
501
|
let current = node;
|
|
377
502
|
while (current) {
|
|
503
|
+
if (current.type === "union_type") {
|
|
504
|
+
// `Store | undefined` and `Store | null` still prove `Store` for a member call.
|
|
505
|
+
const members = current.namedChildren.filter((member) => !NULLISH_TYPE_TEXT.has(member.text.trim()));
|
|
506
|
+
current = members.length === 1 ? members[0] : null;
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
378
509
|
if (current.type === "type_annotation" ||
|
|
379
510
|
current.type === "named_type" ||
|
|
380
511
|
current.type === "user_type" ||
|
|
@@ -391,6 +522,16 @@ export function unwrapNamedType(node, sup) {
|
|
|
391
522
|
current = current.childForFieldName("type") ?? current.namedChildren[0] ?? null;
|
|
392
523
|
continue;
|
|
393
524
|
}
|
|
525
|
+
if (current.type === "qualified_name") {
|
|
526
|
+
// C#'s `Outer.Inner` names the nested type by its last segment. PHP also parses a
|
|
527
|
+
// namespaced name as `qualified_name`, but as one flat token with no `name` field, so an
|
|
528
|
+
// absent field leaves `current` as that whole node instead of nulling the result out.
|
|
529
|
+
const segment = current.childForFieldName("name");
|
|
530
|
+
if (!segment)
|
|
531
|
+
break;
|
|
532
|
+
current = segment;
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
394
535
|
break;
|
|
395
536
|
}
|
|
396
537
|
if (!current)
|
|
@@ -400,6 +541,34 @@ export function unwrapNamedType(node, sup) {
|
|
|
400
541
|
}
|
|
401
542
|
return null;
|
|
402
543
|
}
|
|
544
|
+
/**
|
|
545
|
+
* Kotlin extension-function receiver type, e.g. `Widget` in `fun Widget.describe(): String`.
|
|
546
|
+
* The receiver type is an unfielded `user_type` positioned before the function's own `name`;
|
|
547
|
+
* an ordinary function or class member declares no such node.
|
|
548
|
+
*/
|
|
549
|
+
export function kotlinExtensionReceiverTypeNode(node, sup) {
|
|
550
|
+
if (node.type !== "function_declaration")
|
|
551
|
+
return null;
|
|
552
|
+
const nameNode = node.childForFieldName("name");
|
|
553
|
+
if (!nameNode)
|
|
554
|
+
return null;
|
|
555
|
+
const receiverType = node.namedChildren.find((child) => child.startIndex < nameNode.startIndex && (child.type === "user_type" || child.type === "nullable_type"));
|
|
556
|
+
return receiverType ? unwrapNamedType(receiverType, sup) : null;
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Self-type name node of a Rust `impl` block: `Circle` in `impl Circle`, `impl Shape for
|
|
560
|
+
* Circle`, `impl<T> Box<T>`, or `impl Shape for &Circle`. The `type` field always names the
|
|
561
|
+
* self type (the `trait` field names the implemented trait), and `unwrapNamedType` strips
|
|
562
|
+
* generic and reference wrappers down to the base type identifier. Shared by member
|
|
563
|
+
* ownership in the detailed graph and receiver-member navigation so both attribute an
|
|
564
|
+
* impl method to the same owner.
|
|
565
|
+
*/
|
|
566
|
+
export function rustImplSelfTypeNode(implItem, sup) {
|
|
567
|
+
if (implItem.type !== "impl_item")
|
|
568
|
+
return null;
|
|
569
|
+
const typeNode = implItem.childForFieldName("type");
|
|
570
|
+
return typeNode ? unwrapNamedType(typeNode, sup) : null;
|
|
571
|
+
}
|
|
403
572
|
function capitalizedCallTypeName(expr, source, sup) {
|
|
404
573
|
if (expr.type !== "call" && expr.type !== "call_expression")
|
|
405
574
|
return null;
|
|
@@ -428,11 +597,21 @@ function compositeLiteralTypeName(expr, source, sup) {
|
|
|
428
597
|
return null;
|
|
429
598
|
current = operand;
|
|
430
599
|
}
|
|
431
|
-
|
|
600
|
+
// tree-sitter-go composite_literal, tree-sitter-zig struct_initializer, tree-sitter-rust
|
|
601
|
+
// struct_expression: `Type { field: value }` / `Type{ .field = value }`.
|
|
602
|
+
if (current.type !== "composite_literal" &&
|
|
603
|
+
current.type !== "struct_initializer" &&
|
|
604
|
+
current.type !== "struct_expression") {
|
|
432
605
|
return null;
|
|
606
|
+
}
|
|
433
607
|
const typeNode = current.childForFieldName("type") ??
|
|
434
|
-
current.namedChildren.find((child) => child.type === "type_identifier" ||
|
|
608
|
+
current.namedChildren.find((child) => child.type === "type_identifier" ||
|
|
609
|
+
child.type === "identifier" ||
|
|
610
|
+
child.type === sup.nodeTypes.memberExpression) ??
|
|
435
611
|
null;
|
|
612
|
+
// A qualified literal type (Zig `ns.Struct{...}`) has no dedicated `type` field and is not a
|
|
613
|
+
// simple named type either; `unwrapNamedType` rejects it, so the member-access node itself is
|
|
614
|
+
// kept so the caller can resolve it the same way as any other qualified expression.
|
|
436
615
|
return typeNode ? (unwrapNamedType(typeNode, sup) ?? typeNode) : null;
|
|
437
616
|
}
|
|
438
617
|
/**
|
|
@@ -491,13 +670,13 @@ function bindingValueExpression(node) {
|
|
|
491
670
|
function declaredTypeNameNode(node, sup) {
|
|
492
671
|
const typeField = node.childForFieldName("type");
|
|
493
672
|
if (typeField)
|
|
494
|
-
return unwrapNamedType(typeField, sup);
|
|
673
|
+
return unwrapNamedType(typeField, sup) ?? typeField;
|
|
495
674
|
const typedChild = node.namedChildren.find((child) => child.type === "named_type" ||
|
|
496
675
|
child.type === "user_type" ||
|
|
497
676
|
child.type === "type_annotation" ||
|
|
498
677
|
child.type === "type");
|
|
499
678
|
if (typedChild)
|
|
500
|
-
return unwrapNamedType(typedChild, sup);
|
|
679
|
+
return unwrapNamedType(typedChild, sup) ?? typedChild;
|
|
501
680
|
const ids = node.namedChildren.filter((child) => isReceiverNameNode(sup, child.type) || child.type === "type_identifier" || child.type === "name");
|
|
502
681
|
if (ids.length >= 2)
|
|
503
682
|
return unwrapNamedType(ids[0], sup);
|
|
@@ -575,6 +754,10 @@ function bindingProof(node, receiverName, source, sup) {
|
|
|
575
754
|
return { status: "none" };
|
|
576
755
|
if (!bindingDeclaresReceiverName(node, receiverName, source, sup))
|
|
577
756
|
return { status: "none" };
|
|
757
|
+
const annotation = isJsTsLanguage(sup.id) ? node.childForFieldName("type") : null;
|
|
758
|
+
const annotated = annotation ? (unwrapNamedType(annotation, sup) ?? typescriptImportTypeQuery(annotation)) : null;
|
|
759
|
+
if (annotated)
|
|
760
|
+
return { status: "declared", node: annotated };
|
|
578
761
|
const typeNode = constructionTypeFromBinding(node, receiverName, source, sup);
|
|
579
762
|
if (typeNode)
|
|
580
763
|
return { status: "type", node: typeNode };
|
|
@@ -586,12 +769,21 @@ function isSkippableBindingContainer(node, receiver) {
|
|
|
586
769
|
function findPriorConstructorInContainer(node, receiver, receiverName, source, sup) {
|
|
587
770
|
let constructor = null;
|
|
588
771
|
let sawUnproven = false;
|
|
772
|
+
let declared = false;
|
|
589
773
|
const visit = (current) => {
|
|
590
774
|
if (current.startIndex >= receiver.startIndex)
|
|
591
775
|
return true;
|
|
592
776
|
if (current !== node && isSkippableBindingContainer(current, receiver))
|
|
593
777
|
return true;
|
|
594
778
|
const proof = bindingProof(current, receiverName, source, sup);
|
|
779
|
+
if (proof.status === "declared") {
|
|
780
|
+
constructor = proof.node;
|
|
781
|
+
declared = true;
|
|
782
|
+
return true;
|
|
783
|
+
}
|
|
784
|
+
// A later assignment to an annotated binding cannot change its declared type.
|
|
785
|
+
if (declared && proof.status !== "none")
|
|
786
|
+
return true;
|
|
595
787
|
if (proof.status === "unproven") {
|
|
596
788
|
if (constructor) {
|
|
597
789
|
constructor = null;
|
|
@@ -627,7 +819,10 @@ function bindingContainerDeclaresNameBefore(node, receiver, receiverName, source
|
|
|
627
819
|
return false;
|
|
628
820
|
if (current !== node && isSkippableBindingContainer(current, receiver))
|
|
629
821
|
return false;
|
|
630
|
-
|
|
822
|
+
// A JavaScript/TypeScript assignment updates an existing binding; it declares nothing.
|
|
823
|
+
const declaresNewBinding = !(isJsTsLanguage(sup.id) && current.type === "assignment_expression");
|
|
824
|
+
if (declaresNewBinding &&
|
|
825
|
+
BINDING_DECLARATION_TYPES.has(current.type) &&
|
|
631
826
|
bindingDeclaresReceiverName(current, receiverName, source, sup)) {
|
|
632
827
|
return true;
|
|
633
828
|
}
|
|
@@ -662,33 +857,290 @@ function findVisiblePriorConstructor(receiver, receiverName, source, sup) {
|
|
|
662
857
|
* Resolves the node naming the type a receiver expression was constructed from, or
|
|
663
858
|
* null when no constructor is proven for it. Shared with detailed symbol-graph call
|
|
664
859
|
* extraction so `goto` and resolved `calls` edges accept the same receiver forms.
|
|
860
|
+
* A bare name is unit-struct construction only while no local binding of the same
|
|
861
|
+
* name shadows it; a shadowing binding is a value whose type must come from the
|
|
862
|
+
* binding, never from the type the name also spells.
|
|
665
863
|
*/
|
|
666
864
|
export function receiverConstructorExpression(obj, source, sup) {
|
|
667
865
|
const direct = constructionTypeName(obj, source, sup);
|
|
668
|
-
if (direct)
|
|
669
|
-
return direct;
|
|
670
866
|
if (!isReceiverNameNode(sup, obj.type))
|
|
671
|
-
return
|
|
867
|
+
return direct;
|
|
672
868
|
const receiverName = sliceText(obj, source);
|
|
869
|
+
if (direct && !bindsLocalValue(obj, receiverName, source, sup))
|
|
870
|
+
return direct;
|
|
673
871
|
return findVisiblePriorConstructor(obj, receiverName, source, sup);
|
|
674
872
|
}
|
|
675
|
-
|
|
676
|
-
|
|
873
|
+
/** Identifier segments in a C++ qualified name, excluding template arguments. */
|
|
874
|
+
export function cppQualifiedNameSegments(node, source) {
|
|
875
|
+
return cppQualifiedTextSegments(sliceText(node, source));
|
|
876
|
+
}
|
|
877
|
+
/** `::`-separated segments of a C++ qualified name, ignoring `::` inside template arguments and dropping them. */
|
|
878
|
+
export function cppQualifiedTextSegments(text) {
|
|
879
|
+
const segments = [];
|
|
880
|
+
let segmentStart = 0;
|
|
881
|
+
let templateDepth = 0;
|
|
882
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
883
|
+
const char = text[index];
|
|
884
|
+
if (char === "<") {
|
|
885
|
+
templateDepth += 1;
|
|
886
|
+
continue;
|
|
887
|
+
}
|
|
888
|
+
if (char === ">") {
|
|
889
|
+
templateDepth = Math.max(0, templateDepth - 1);
|
|
890
|
+
continue;
|
|
891
|
+
}
|
|
892
|
+
if (char !== ":" || text[index + 1] !== ":" || templateDepth !== 0)
|
|
893
|
+
continue;
|
|
894
|
+
const segment = text.slice(segmentStart, index).trim();
|
|
895
|
+
if (segment)
|
|
896
|
+
segments.push(segment.replace(/<.*$/u, "").trim());
|
|
897
|
+
segmentStart = index + 2;
|
|
898
|
+
index += 1;
|
|
899
|
+
}
|
|
900
|
+
const finalSegment = text.slice(segmentStart).trim();
|
|
901
|
+
if (finalSegment)
|
|
902
|
+
segments.push(finalSegment.replace(/<.*$/u, "").trim());
|
|
903
|
+
return segments.filter(Boolean);
|
|
904
|
+
}
|
|
905
|
+
/** Exact namespace/type path that qualifies a C++ out-of-line function definition. */
|
|
906
|
+
export function cppOutOfLineOwnerPath(node, source, sup) {
|
|
907
|
+
if (sup.id !== "cpp")
|
|
908
|
+
return null;
|
|
909
|
+
let current = node;
|
|
910
|
+
while (current) {
|
|
911
|
+
if (current.type === "function_definition") {
|
|
912
|
+
let declarator = current.childForFieldName("declarator");
|
|
913
|
+
while (declarator) {
|
|
914
|
+
if (declarator.type === "qualified_identifier") {
|
|
915
|
+
const path = cppQualifiedNameSegments(declarator, source);
|
|
916
|
+
path.pop();
|
|
917
|
+
if (!sliceText(declarator, source).trimStart().startsWith("::")) {
|
|
918
|
+
const namespaces = [];
|
|
919
|
+
let parent = current.parent;
|
|
920
|
+
while (parent) {
|
|
921
|
+
if (parent.type === "namespace_definition") {
|
|
922
|
+
const name = parent.childForFieldName("name");
|
|
923
|
+
if (name)
|
|
924
|
+
namespaces.push(cppQualifiedNameSegments(name, source));
|
|
925
|
+
}
|
|
926
|
+
parent = parent.parent;
|
|
927
|
+
}
|
|
928
|
+
for (const namespace of namespaces.reverse())
|
|
929
|
+
path.unshift(...namespace);
|
|
930
|
+
}
|
|
931
|
+
return path.length ? path : null;
|
|
932
|
+
}
|
|
933
|
+
const nested = declarator.childForFieldName("declarator") ?? declarator.namedChildren.at(-1);
|
|
934
|
+
if (!nested || nested.id === declarator.id)
|
|
935
|
+
break;
|
|
936
|
+
declarator = nested;
|
|
937
|
+
}
|
|
938
|
+
return null;
|
|
939
|
+
}
|
|
940
|
+
current = current.parent;
|
|
941
|
+
}
|
|
942
|
+
return null;
|
|
943
|
+
}
|
|
944
|
+
/** Unqualified member name from a C++ out-of-line function definition. */
|
|
945
|
+
export function cppOutOfLineMemberName(node, source, sup) {
|
|
946
|
+
if (sup.id !== "cpp")
|
|
947
|
+
return null;
|
|
948
|
+
let current = node;
|
|
949
|
+
while (current) {
|
|
950
|
+
if (current.type === "function_definition") {
|
|
951
|
+
const nameNode = cppFunctionDeclaratorName(current, sup);
|
|
952
|
+
return nameNode ? sliceText(nameNode, source) : null;
|
|
953
|
+
}
|
|
954
|
+
current = current.parent;
|
|
955
|
+
}
|
|
956
|
+
return null;
|
|
957
|
+
}
|
|
958
|
+
function sameSyntaxNode(left, right) {
|
|
959
|
+
if (!left)
|
|
960
|
+
return false;
|
|
961
|
+
if (left.id !== undefined && right.id !== undefined)
|
|
962
|
+
return left.id === right.id;
|
|
963
|
+
return left.type === right.type && left.startIndex === right.startIndex && left.endIndex === right.endIndex;
|
|
964
|
+
}
|
|
965
|
+
function cppFunctionDeclaratorName(node, sup) {
|
|
966
|
+
const functionDeclarator = node.type === "function_declarator" ? node : findFirstNodeByType(node, "function_declarator");
|
|
967
|
+
let current = functionDeclarator?.childForFieldName("declarator") ?? null;
|
|
968
|
+
while (current) {
|
|
969
|
+
if (isIdentifierType(sup, current.type) || current.type === "operator_name" || current.type === "destructor_name") {
|
|
970
|
+
return current;
|
|
971
|
+
}
|
|
972
|
+
let name = current.childForFieldName("name");
|
|
973
|
+
while (name?.childForFieldName("name"))
|
|
974
|
+
name = name.childForFieldName("name");
|
|
975
|
+
if (name &&
|
|
976
|
+
(isIdentifierType(sup, name.type) || name.type === "operator_name" || name.type === "destructor_name")) {
|
|
977
|
+
return name;
|
|
978
|
+
}
|
|
979
|
+
const nested = current.childForFieldName("declarator");
|
|
980
|
+
if (!nested || nested.id === current.id)
|
|
981
|
+
return null;
|
|
982
|
+
current = nested;
|
|
983
|
+
}
|
|
984
|
+
return null;
|
|
985
|
+
}
|
|
986
|
+
function cppMemberArityFromAncestor(node) {
|
|
987
|
+
let current = node;
|
|
988
|
+
while (current) {
|
|
989
|
+
const arity = declarationMemberArity(current, "cpp");
|
|
990
|
+
if (arity !== undefined)
|
|
991
|
+
return arity;
|
|
992
|
+
current = current.parent;
|
|
993
|
+
}
|
|
994
|
+
return undefined;
|
|
995
|
+
}
|
|
996
|
+
function collectCppMemberDeclarations(node, ownerNode, localName, ownerSource, sup, expectedArity, out) {
|
|
997
|
+
if ((node.type === "field_declaration" || node.type === "declaration") &&
|
|
998
|
+
sameSyntaxNode(nearestMemberContainer(node), ownerNode)) {
|
|
999
|
+
const nameNode = cppFunctionDeclaratorName(node, sup);
|
|
1000
|
+
if (nameNode &&
|
|
1001
|
+
sup.normalizeIdentifier(sliceText(nameNode, ownerSource)) === sup.normalizeIdentifier(localName) &&
|
|
1002
|
+
declarationMemberArity(node, sup.id) === expectedArity) {
|
|
1003
|
+
out.push({ node, nameNode });
|
|
1004
|
+
}
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
for (const child of node.namedChildren) {
|
|
1008
|
+
collectCppMemberDeclarations(child, ownerNode, localName, ownerSource, sup, expectedArity, out);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
/**
|
|
1012
|
+
* Finds the unique in-class declaration corresponding to a C++ out-of-line member
|
|
1013
|
+
* definition. Name and arity must both match so overloads do not collapse.
|
|
1014
|
+
*/
|
|
1015
|
+
export function cppOutOfLineMemberDeclarationNode(definitionNameNode, localName, ownerNameNode, ownerSource, sup) {
|
|
1016
|
+
if (sup.id !== "cpp")
|
|
1017
|
+
return null;
|
|
1018
|
+
const ownerNode = nearestMemberContainer(ownerNameNode);
|
|
1019
|
+
if (!ownerNode)
|
|
1020
|
+
return null;
|
|
1021
|
+
const expectedArity = cppMemberArityFromAncestor(definitionNameNode);
|
|
1022
|
+
const declarations = [];
|
|
1023
|
+
collectCppMemberDeclarations(ownerNode, ownerNode, localName, ownerSource, sup, expectedArity, declarations);
|
|
1024
|
+
return declarations.length === 1 ? declarations[0] : null;
|
|
1025
|
+
}
|
|
1026
|
+
export function nodeDeclaresStatic(node, source) {
|
|
1027
|
+
if (node.type === "static" || node.type === "static_modifier")
|
|
1028
|
+
return true;
|
|
1029
|
+
if (node.type === "storage_class_specifier" || node.type === "modifier" || node.type === "property_modifier") {
|
|
1030
|
+
return sliceText(node, source).trim() === "static";
|
|
1031
|
+
}
|
|
1032
|
+
if (node.type === "class") {
|
|
1033
|
+
const parentType = node.parent?.type;
|
|
1034
|
+
return (parentType === "function_declaration" ||
|
|
1035
|
+
parentType === "property_declaration" ||
|
|
1036
|
+
parentType === "subscript_declaration");
|
|
1037
|
+
}
|
|
1038
|
+
if (node.type === "modifiers") {
|
|
1039
|
+
for (let childIndex = 0;; childIndex += 1) {
|
|
1040
|
+
const child = node.child(childIndex);
|
|
1041
|
+
if (!child)
|
|
1042
|
+
break;
|
|
1043
|
+
if (nodeDeclaresStatic(child, source))
|
|
1044
|
+
return true;
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
return false;
|
|
1048
|
+
}
|
|
1049
|
+
export function declarationNodeIsStatic(node, source) {
|
|
1050
|
+
if (nodeDeclaresStatic(node, source))
|
|
1051
|
+
return true;
|
|
1052
|
+
for (let childIndex = 0;; childIndex += 1) {
|
|
1053
|
+
const child = node.child(childIndex);
|
|
1054
|
+
if (!child)
|
|
1055
|
+
return false;
|
|
1056
|
+
if (nodeDeclaresStatic(child, source))
|
|
1057
|
+
return true;
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
export function nodeInStaticMemberContext(node, source) {
|
|
1061
|
+
const container = nearestMemberContainer(node);
|
|
1062
|
+
if (!container)
|
|
1063
|
+
return false;
|
|
1064
|
+
let current = node;
|
|
1065
|
+
while (current && current !== container) {
|
|
1066
|
+
if (declarationNodeIsStatic(current, source))
|
|
1067
|
+
return true;
|
|
1068
|
+
current = current.parent;
|
|
1069
|
+
}
|
|
1070
|
+
return false;
|
|
1071
|
+
}
|
|
1072
|
+
const JS_TS_DYNAMIC_THIS_FUNCTION_TYPES = {
|
|
1073
|
+
function: true,
|
|
1074
|
+
function_declaration: true,
|
|
1075
|
+
function_expression: true,
|
|
1076
|
+
generator_function: true,
|
|
1077
|
+
generator_function_declaration: true,
|
|
1078
|
+
method_definition: true,
|
|
1079
|
+
};
|
|
1080
|
+
/**
|
|
1081
|
+
* Ordinary JS/TS functions and object-literal methods own `this`/`super`; arrows preserve the
|
|
1082
|
+
* enclosing class member's receiver. Crossing one of those dynamic boundaries makes the class
|
|
1083
|
+
* receiver unproven.
|
|
1084
|
+
*/
|
|
1085
|
+
export function keywordReceiverCrossesDynamicBoundary(sup, node) {
|
|
1086
|
+
if (!isJsTsLanguage(sup.id))
|
|
1087
|
+
return false;
|
|
1088
|
+
const container = nearestMemberContainer(node);
|
|
1089
|
+
if (!container)
|
|
1090
|
+
return false;
|
|
1091
|
+
let current = node.parent;
|
|
1092
|
+
while (current && current !== container) {
|
|
1093
|
+
if (JS_TS_DYNAMIC_THIS_FUNCTION_TYPES[current.type]) {
|
|
1094
|
+
const directlyOwnedClassMethod = current.type === "method_definition" &&
|
|
1095
|
+
current.parent?.type === "class_body" &&
|
|
1096
|
+
current.parent.parent?.startIndex === container.startIndex;
|
|
1097
|
+
return !directlyOwnedClassMethod;
|
|
1098
|
+
}
|
|
1099
|
+
current = current.parent;
|
|
1100
|
+
}
|
|
1101
|
+
return false;
|
|
1102
|
+
}
|
|
1103
|
+
export function keywordReceiverMemberScope(sup, receiverName, node, source) {
|
|
1104
|
+
if (!hasStaticMemberDistinction(sup.id))
|
|
1105
|
+
return "any";
|
|
1106
|
+
if (nodeInStaticMemberContext(node, source))
|
|
1107
|
+
return "static";
|
|
1108
|
+
const kind = keywordReceiverKind(sup.id, receiverName);
|
|
1109
|
+
if (kind === "supertype")
|
|
1110
|
+
return "instance";
|
|
1111
|
+
return ownReceiverMemberScope(sup.id, receiverName) ?? "any";
|
|
1112
|
+
}
|
|
1113
|
+
/**
|
|
1114
|
+
* The leftmost identifier of a C# chain of plain identifiers joined by `.` (`P` in `P.Inner.Mix`),
|
|
1115
|
+
* or `null` when `node` has a call, index, keyword, or type arguments. When that identifier binds
|
|
1116
|
+
* nothing, the chain is a namespace-qualified type name.
|
|
1117
|
+
*/
|
|
1118
|
+
export function csharpDottedNameRoot(node) {
|
|
1119
|
+
if (node.type === "identifier")
|
|
1120
|
+
return node;
|
|
1121
|
+
if (node.type !== "member_access_expression")
|
|
1122
|
+
return null;
|
|
1123
|
+
const object = node.childForFieldName("expression");
|
|
1124
|
+
if (!object || node.childForFieldName("name")?.type !== "identifier")
|
|
1125
|
+
return null;
|
|
1126
|
+
return csharpDottedNameRoot(object);
|
|
677
1127
|
}
|
|
678
1128
|
/**
|
|
679
1129
|
* Classifies a receiver as the declaring type, a supertype, or a named/constructed type.
|
|
680
1130
|
* Returns null when the receiver cannot be proven.
|
|
681
1131
|
* Named-local constructor lookup is memoized per enclosing function and receiver text.
|
|
682
1132
|
*/
|
|
683
|
-
export function classifyReceiver(sup, receiver, source, proofCache, cacheScope, accessNode) {
|
|
684
|
-
const
|
|
685
|
-
const text = sliceText(receiver, source).trim();
|
|
1133
|
+
export function classifyReceiver(sup, receiver, source, proofCache, cacheScope, accessNode, hasLexicalBinding) {
|
|
1134
|
+
const text = receiverKeywordText(sup, receiver, source, hasLexicalBinding);
|
|
686
1135
|
if (!text)
|
|
687
1136
|
return null;
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
1137
|
+
const keywordKind = keywordReceiverKind(sup.id, text);
|
|
1138
|
+
if (keywordKind) {
|
|
1139
|
+
if (keywordReceiverCrossesDynamicBoundary(sup, accessNode))
|
|
1140
|
+
return null;
|
|
1141
|
+
const memberScope = keywordReceiverMemberScope(sup, text, accessNode, source);
|
|
1142
|
+
return keywordKind === "own" ? { kind: "own-type", memberScope } : { kind: "supertype", memberScope };
|
|
1143
|
+
}
|
|
692
1144
|
const receiverIsName = isReceiverNameNode(sup, receiver.type);
|
|
693
1145
|
const cacheKey = `${cacheScope}\u0000${text}`;
|
|
694
1146
|
let proof = proofCache.get(cacheKey);
|
|
@@ -704,7 +1156,9 @@ export function classifyReceiver(sup, receiver, source, proofCache, cacheScope,
|
|
|
704
1156
|
return {
|
|
705
1157
|
kind: "named-type",
|
|
706
1158
|
typeName: sliceText(proof.constructed, source),
|
|
1159
|
+
typeNode: proof.constructed,
|
|
707
1160
|
memberScope: hasStaticMemberDistinction(sup.id) ? "instance" : "any",
|
|
1161
|
+
constructed: true,
|
|
708
1162
|
};
|
|
709
1163
|
}
|
|
710
1164
|
if (!receiverIsName)
|
|
@@ -713,24 +1167,83 @@ export function classifyReceiver(sup, receiver, source, proofCache, cacheScope,
|
|
|
713
1167
|
// `Example::shared()` would still be attributed to a colliding parameter named Example.
|
|
714
1168
|
if (proof.locallyBound)
|
|
715
1169
|
return null;
|
|
716
|
-
// Dotted `Cfg.load()` is not proof
|
|
717
|
-
// is
|
|
718
|
-
//
|
|
1170
|
+
// Dotted `Cfg.load()` is not proof in languages where the identifier may be a
|
|
1171
|
+
// value. Type-scoped `::` is one named-type proof; C# `Box.Left()` is another
|
|
1172
|
+
// because a capitalized unbound name is the static type receiver. Ruby
|
|
1173
|
+
// capitalized names are `constant` tokens even when they name a parameter, so
|
|
1174
|
+
// `constant` is not itself type proof.
|
|
719
1175
|
const property = getMemberAccessParts(sup, accessNode).property;
|
|
720
1176
|
const between = property ? source.slice(receiver.endIndex, property.startIndex) : "";
|
|
721
1177
|
const typeScoped = TYPE_SCOPED_ACCESS_TYPES[accessNode.type] === true || between.includes("::");
|
|
722
|
-
if (receiver.type !== "type_identifier" && !typeScoped)
|
|
723
|
-
|
|
1178
|
+
if (receiver.type !== "type_identifier" && !typeScoped) {
|
|
1179
|
+
// A capitalized bare name is type proof where construction already types
|
|
1180
|
+
// `Box()` as a Box, and for static type-name receivers (`Box.Left()`).
|
|
1181
|
+
// The named type must still resolve to a members-declaring definition
|
|
1182
|
+
// before any call edge is recorded, so a name alone never invents a target.
|
|
1183
|
+
if (!capitalizedTypeReceiverName(sup, receiver, text))
|
|
1184
|
+
return null;
|
|
1185
|
+
return {
|
|
1186
|
+
kind: "named-type",
|
|
1187
|
+
typeName: text,
|
|
1188
|
+
typeNode: receiver,
|
|
1189
|
+
memberScope: UNBOUND_INSTANCE_CALL_LANGUAGE_IDS[sup.id] ? "any" : "static",
|
|
1190
|
+
};
|
|
1191
|
+
}
|
|
724
1192
|
return {
|
|
725
1193
|
kind: "named-type",
|
|
726
1194
|
typeName: text,
|
|
1195
|
+
typeNode: receiver,
|
|
727
1196
|
memberScope: hasStaticMemberDistinction(sup.id) && typeScoped ? "static" : "any",
|
|
728
1197
|
};
|
|
729
1198
|
}
|
|
1199
|
+
/**
|
|
1200
|
+
* Languages whose runtime allows an instance member to be invoked through a type
|
|
1201
|
+
* name (`Box.instanceMethod(args)` is a real unbound call), so a type-named
|
|
1202
|
+
* receiver restricts nothing about static versus instance members.
|
|
1203
|
+
*/
|
|
1204
|
+
const UNBOUND_INSTANCE_CALL_LANGUAGE_IDS = {
|
|
1205
|
+
python: true,
|
|
1206
|
+
};
|
|
1207
|
+
/**
|
|
1208
|
+
* Languages whose dotted type-name receivers (`Box.Left()`) name the type itself.
|
|
1209
|
+
* Distinct from `capitalizedCall`, which also treats `Box()` as construction.
|
|
1210
|
+
*/
|
|
1211
|
+
const STATIC_TYPE_NAME_RECEIVER_LANGUAGE_IDS = {
|
|
1212
|
+
csharp: true,
|
|
1213
|
+
java: true,
|
|
1214
|
+
js: true,
|
|
1215
|
+
kotlin: true,
|
|
1216
|
+
ts: true,
|
|
1217
|
+
tsx: true,
|
|
1218
|
+
};
|
|
1219
|
+
/** Whether a receiver name is capitalized like a type in a capitalized-name language. */
|
|
1220
|
+
function capitalizedTypeReceiverName(sup, receiver, text) {
|
|
1221
|
+
if (LANGUAGE_CONSTRUCTION_FORMS[sup.id]?.capitalizedCall !== true &&
|
|
1222
|
+
!STATIC_TYPE_NAME_RECEIVER_LANGUAGE_IDS[sup.id]) {
|
|
1223
|
+
return false;
|
|
1224
|
+
}
|
|
1225
|
+
if (!isReceiverNameNode(sup, receiver.type))
|
|
1226
|
+
return false;
|
|
1227
|
+
const first = text[0];
|
|
1228
|
+
return !!first && first === first.toUpperCase() && first !== first.toLowerCase();
|
|
1229
|
+
}
|
|
730
1230
|
/** Whether a resolved definition can declare callable members. */
|
|
731
1231
|
export function declaresMembers(def) {
|
|
732
1232
|
return def.kind === SymbolKind.Class || def.kind === SymbolKind.Interface || def.kind === SymbolKind.TypeAlias;
|
|
733
1233
|
}
|
|
1234
|
+
/** Whether a C++ definition denotes a class, struct, or union that can own methods. */
|
|
1235
|
+
export function isCppMemberContainerDefinition(tree, def) {
|
|
1236
|
+
const position = {
|
|
1237
|
+
row: Math.max(0, def.range.start.line - 1),
|
|
1238
|
+
column: Math.max(0, def.range.start.column - 1),
|
|
1239
|
+
};
|
|
1240
|
+
const nameNode = tree.rootNode.descendantForPosition(position, position);
|
|
1241
|
+
const container = nearestMemberContainer(nameNode);
|
|
1242
|
+
if (!container || CPP_MEMBER_CONTAINER_TYPES[container.type] === undefined)
|
|
1243
|
+
return false;
|
|
1244
|
+
const containerName = container.childForFieldName("name");
|
|
1245
|
+
return containerName?.startPosition.row === position.row && containerName.startPosition.column === position.column;
|
|
1246
|
+
}
|
|
734
1247
|
/** Nearest enclosing declaration that lexically owns `node` as a member. */
|
|
735
1248
|
export function nearestMemberContainer(node) {
|
|
736
1249
|
let current = node.parent;
|
|
@@ -741,29 +1254,168 @@ export function nearestMemberContainer(node) {
|
|
|
741
1254
|
}
|
|
742
1255
|
return null;
|
|
743
1256
|
}
|
|
744
|
-
/** Positional argument count of a call,
|
|
745
|
-
export function callArgumentCount(callNode) {
|
|
746
|
-
|
|
747
|
-
|
|
1257
|
+
/** Positional argument count of a call, including Kotlin/Swift trailing lambdas. */
|
|
1258
|
+
export function callArgumentCount(callNode, source) {
|
|
1259
|
+
// Kotlin wraps `pick(1) { }` in an outer call node whose only other child is the
|
|
1260
|
+
// callee call; the trailing lambda belongs to the inner call's argument list.
|
|
1261
|
+
let scope = callNode;
|
|
1262
|
+
for (let wrapper = trailingLambdaWrapper(scope); wrapper; wrapper = trailingLambdaWrapper(scope)) {
|
|
1263
|
+
scope = wrapper;
|
|
1264
|
+
}
|
|
1265
|
+
let argumentNode = callNode.childForFieldName("arguments") ??
|
|
1266
|
+
callNode.childForFieldName("argument_list") ??
|
|
1267
|
+
(callNode.namedChildren ?? []).find((child) => CALL_ARGUMENT_NODE_TYPES[child.type]) ??
|
|
1268
|
+
null;
|
|
748
1269
|
if (argumentNode?.type === "call_suffix") {
|
|
749
1270
|
argumentNode = (argumentNode.namedChildren ?? []).find((child) => child.type === "value_arguments") ?? null;
|
|
750
1271
|
}
|
|
751
|
-
if (!argumentNode)
|
|
752
|
-
|
|
753
|
-
|
|
1272
|
+
if (!argumentNode) {
|
|
1273
|
+
// Swift allows a call suffix with only a trailing lambda (`pick { }`), and Kotlin
|
|
1274
|
+
// `pick { }` keeps the lambda directly on the call node: no argument list exists.
|
|
1275
|
+
const suffix = (callNode.namedChildren ?? []).find((child) => child.type === "call_suffix");
|
|
1276
|
+
return ((suffix ?? scope).namedChildren ?? []).filter((child) => TRAILING_LAMBDA_NODE_TYPES[child.type] && child.startIndex >= callNode.startIndex).length;
|
|
1277
|
+
}
|
|
1278
|
+
let count = (argumentNode.namedChildren ?? []).filter((argument) => argument.type !== "comment").length;
|
|
1279
|
+
const trailingEnd = argumentNode.parent?.type === "call_suffix" ? argumentNode.parent.endIndex : scope.endIndex;
|
|
1280
|
+
if (trailingEnd > argumentNode.endIndex && containsTrailingLambdaNode(scope, argumentNode.endIndex, trailingEnd)) {
|
|
1281
|
+
const trailing = countTrailingClosureArguments(source.slice(argumentNode.endIndex, trailingEnd));
|
|
1282
|
+
// A null scan means the trailing-closure count is unknown, so the whole call
|
|
1283
|
+
// arity is unknown rather than the parenthesized count alone.
|
|
1284
|
+
if (trailing === null)
|
|
1285
|
+
return null;
|
|
1286
|
+
count += trailing;
|
|
1287
|
+
}
|
|
1288
|
+
return count;
|
|
754
1289
|
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
1290
|
+
/** Trailing lambda nodes across the supported grammars. */
|
|
1291
|
+
const TRAILING_LAMBDA_NODE_TYPES = {
|
|
1292
|
+
annotated_lambda: true, // Kotlin wraps the lambda on the outer call node.
|
|
1293
|
+
lambda_literal: true, // Swift keeps trailing lambdas inside `call_suffix`.
|
|
1294
|
+
};
|
|
1295
|
+
/** Call nodes that can wrap a callee call plus a Kotlin trailing lambda. */
|
|
1296
|
+
const TRAILING_LAMBDA_WRAPPER_TYPES = {
|
|
1297
|
+
call: true,
|
|
1298
|
+
call_expression: true,
|
|
1299
|
+
};
|
|
1300
|
+
/**
|
|
1301
|
+
* The outer call node attaching a trailing lambda to `node`, or null. The wrapper
|
|
1302
|
+
* never carries its own argument list, which distinguishes it from a chained call.
|
|
1303
|
+
*/
|
|
1304
|
+
function trailingLambdaWrapper(node) {
|
|
1305
|
+
const parent = node.parent;
|
|
1306
|
+
if (!parent || !TRAILING_LAMBDA_WRAPPER_TYPES[parent.type])
|
|
758
1307
|
return null;
|
|
759
|
-
const
|
|
760
|
-
if (
|
|
1308
|
+
const children = parent.namedChildren ?? [];
|
|
1309
|
+
if (children.some((child) => CALL_ARGUMENT_NODE_TYPES[child.type]))
|
|
761
1310
|
return null;
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
1311
|
+
return children.some((child) => TRAILING_LAMBDA_NODE_TYPES[child.type]) ? parent : null;
|
|
1312
|
+
}
|
|
1313
|
+
/** Whether a trailing lambda node overlaps `[start, end)` anywhere under `node`. */
|
|
1314
|
+
function containsTrailingLambdaNode(node, start, end) {
|
|
1315
|
+
for (const child of node.namedChildren ?? []) {
|
|
1316
|
+
if (child.endIndex <= start || child.startIndex >= end)
|
|
1317
|
+
continue;
|
|
1318
|
+
if (TRAILING_LAMBDA_NODE_TYPES[child.type])
|
|
1319
|
+
return true;
|
|
1320
|
+
if (containsTrailingLambdaNode(child, start, end))
|
|
1321
|
+
return true;
|
|
1322
|
+
}
|
|
1323
|
+
return false;
|
|
1324
|
+
}
|
|
1325
|
+
/** End index after a Swift trailing-closure label and its colon, or null when absent. */
|
|
1326
|
+
function trailingClosureLabelEnd(text, startIndex) {
|
|
1327
|
+
if (text[startIndex] === "`") {
|
|
1328
|
+
const escapedEnd = text.indexOf("`", startIndex + 1);
|
|
1329
|
+
if (escapedEnd < 0 || text[escapedEnd + 1] !== ":")
|
|
1330
|
+
return null;
|
|
1331
|
+
return escapedEnd + 2;
|
|
1332
|
+
}
|
|
1333
|
+
const identifier = /^[_\p{ID_Start}][_\p{ID_Continue}]*/u.exec(text.slice(startIndex));
|
|
1334
|
+
if (!identifier)
|
|
765
1335
|
return null;
|
|
766
|
-
|
|
1336
|
+
const colonIndex = startIndex + identifier[0].length;
|
|
1337
|
+
return text[colonIndex] === ":" ? colonIndex + 1 : null;
|
|
1338
|
+
}
|
|
1339
|
+
/**
|
|
1340
|
+
* Counts the top-level `{ ... }` blocks in trailing call text, one per trailing
|
|
1341
|
+
* closure, or null when the text is not a well-formed trailing-closure run.
|
|
1342
|
+
* Shared with call-compatibility extraction so both paths count identically.
|
|
1343
|
+
*/
|
|
1344
|
+
export function countTrailingClosureArguments(text) {
|
|
1345
|
+
let startIndex = 0;
|
|
1346
|
+
let count = 0;
|
|
1347
|
+
while (startIndex < text.length) {
|
|
1348
|
+
while (/\s/.test(text[startIndex] ?? "")) {
|
|
1349
|
+
startIndex += 1;
|
|
1350
|
+
}
|
|
1351
|
+
if (startIndex === text.length) {
|
|
1352
|
+
return count;
|
|
1353
|
+
}
|
|
1354
|
+
if (text[startIndex] !== "{") {
|
|
1355
|
+
const labelEnd = trailingClosureLabelEnd(text, startIndex);
|
|
1356
|
+
if (labelEnd === null)
|
|
1357
|
+
return null;
|
|
1358
|
+
startIndex = labelEnd;
|
|
1359
|
+
while (/\s/.test(text[startIndex] ?? "")) {
|
|
1360
|
+
startIndex += 1;
|
|
1361
|
+
}
|
|
1362
|
+
if (text[startIndex] !== "{")
|
|
1363
|
+
return null;
|
|
1364
|
+
}
|
|
1365
|
+
let braceDepth = 0;
|
|
1366
|
+
let quote = null;
|
|
1367
|
+
let escaped = false;
|
|
1368
|
+
let closed = false;
|
|
1369
|
+
for (let index = startIndex; index < text.length; index += 1) {
|
|
1370
|
+
const char = text[index];
|
|
1371
|
+
if (quote) {
|
|
1372
|
+
if (escaped) {
|
|
1373
|
+
escaped = false;
|
|
1374
|
+
continue;
|
|
1375
|
+
}
|
|
1376
|
+
if (char === "\\") {
|
|
1377
|
+
escaped = true;
|
|
1378
|
+
continue;
|
|
1379
|
+
}
|
|
1380
|
+
if (char === quote) {
|
|
1381
|
+
quote = null;
|
|
1382
|
+
}
|
|
1383
|
+
continue;
|
|
1384
|
+
}
|
|
1385
|
+
const commentEnd = findCommentEnd(text, index);
|
|
1386
|
+
if (commentEnd !== null) {
|
|
1387
|
+
if (commentEnd < 0) {
|
|
1388
|
+
return null;
|
|
1389
|
+
}
|
|
1390
|
+
index = commentEnd - 1;
|
|
1391
|
+
continue;
|
|
1392
|
+
}
|
|
1393
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
1394
|
+
quote = char;
|
|
1395
|
+
continue;
|
|
1396
|
+
}
|
|
1397
|
+
if (char === "{") {
|
|
1398
|
+
braceDepth += 1;
|
|
1399
|
+
continue;
|
|
1400
|
+
}
|
|
1401
|
+
if (char === "}") {
|
|
1402
|
+
braceDepth -= 1;
|
|
1403
|
+
if (braceDepth < 0) {
|
|
1404
|
+
return null;
|
|
1405
|
+
}
|
|
1406
|
+
if (!braceDepth) {
|
|
1407
|
+
startIndex = index + 1;
|
|
1408
|
+
count += 1;
|
|
1409
|
+
closed = true;
|
|
1410
|
+
break;
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
if (!closed) {
|
|
1415
|
+
return null;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
return count;
|
|
767
1419
|
}
|
|
768
1420
|
function loadSource(file, cache) {
|
|
769
1421
|
const cached = cache.get(file);
|
|
@@ -779,20 +1431,6 @@ function loadSource(file, cache) {
|
|
|
779
1431
|
return "";
|
|
780
1432
|
}
|
|
781
1433
|
}
|
|
782
|
-
function memberIdLooksStatic(memberId, sourceCache) {
|
|
783
|
-
const parsed = parseDefNodeId(memberId);
|
|
784
|
-
if (!parsed)
|
|
785
|
-
return false;
|
|
786
|
-
const source = loadSource(parsed.file, sourceCache);
|
|
787
|
-
if (!source)
|
|
788
|
-
return false;
|
|
789
|
-
const beforeBrace = source.lastIndexOf("{", parsed.index);
|
|
790
|
-
const beforeSemi = source.lastIndexOf(";", parsed.index);
|
|
791
|
-
const beforeClose = source.lastIndexOf("}", parsed.index);
|
|
792
|
-
const declStart = Math.max(beforeBrace, beforeSemi, beforeClose);
|
|
793
|
-
const prefix = source.slice(declStart + 1, parsed.index);
|
|
794
|
-
return /(?:^|[^\w$])static(?:$|[^\w$])/.test(prefix) || /(?:^|[^\w$])def\s+self\s*\./.test(prefix);
|
|
795
|
-
}
|
|
796
1434
|
function inferCallMemberScope(site, sourceCache) {
|
|
797
1435
|
const source = loadSource(site.file, sourceCache);
|
|
798
1436
|
if (!source)
|
|
@@ -807,22 +1445,25 @@ function inferCallMemberScope(site, sourceCache) {
|
|
|
807
1445
|
return "instance";
|
|
808
1446
|
if (/\)\s*\.\s*$/.test(before))
|
|
809
1447
|
return "instance";
|
|
810
|
-
//
|
|
811
|
-
// A lowercase dotted receiver is an instance value (`c.StaticMethod()`), not a type.
|
|
1448
|
+
// Older serialized candidates lack memberScope. Infer the common dotted-receiver cases.
|
|
812
1449
|
if (/(?:^|[^A-Za-z0-9_$])[a-z_][A-Za-z0-9_]*\s*\.\s*$/.test(before))
|
|
813
1450
|
return "instance";
|
|
814
1451
|
if (/(?:^|[^A-Za-z0-9_$])[A-Z][A-Za-z0-9_]*\s*\.\s*$/.test(before))
|
|
815
1452
|
return "static";
|
|
816
1453
|
return "any";
|
|
817
1454
|
}
|
|
1455
|
+
function callSiteKey(callerId, site) {
|
|
1456
|
+
const { start, end } = site.range;
|
|
1457
|
+
return `${callerId}\u0000${site.file}\u0000${start.line}:${start.column}:${start.index ?? ""}-${end.line}:${end.column}:${end.index ?? ""}`;
|
|
1458
|
+
}
|
|
818
1459
|
/**
|
|
819
1460
|
* Records proven `calls` edges for deferred receiver invocations.
|
|
820
1461
|
* Ambiguous names at a level stop the walk.
|
|
821
1462
|
* `super`/`base`/`parent` follow class `extends` ancestors only.
|
|
822
1463
|
*/
|
|
823
|
-
export function emitReceiverCallEdges(graph, candidates, recordEdge) {
|
|
1464
|
+
export function emitReceiverCallEdges(graph, candidates, recordEdge, memberScopes = new Map(), nodeAliases = new Map(), memberArities = new Map(), ownerAnchors = new Map(), accessibleMembers = new Map(), fileHiddenMemberIds = new Set(), acceptsCallTarget) {
|
|
824
1465
|
if (!candidates.length)
|
|
825
|
-
return;
|
|
1466
|
+
return [];
|
|
826
1467
|
const membersByOwner = new Map();
|
|
827
1468
|
const ownerByMember = new Map();
|
|
828
1469
|
const supertypesByOwner = new Map();
|
|
@@ -847,27 +1488,75 @@ export function emitReceiverCallEdges(graph, candidates, recordEdge) {
|
|
|
847
1488
|
if (label === "extends")
|
|
848
1489
|
pushUnique(classAncestorsByOwner, edge.from, edge.to);
|
|
849
1490
|
}
|
|
1491
|
+
// Receiver-local access does not change the nominal type's membership edges.
|
|
1492
|
+
for (const [ownerId, memberIds] of accessibleMembers) {
|
|
1493
|
+
for (const memberId of memberIds)
|
|
1494
|
+
pushUnique(membersByOwner, ownerId, memberId);
|
|
1495
|
+
}
|
|
850
1496
|
const nextOwners = (ownerId, viaSupertypes) => {
|
|
851
1497
|
if (!viaSupertypes)
|
|
852
1498
|
return supertypesByOwner.get(ownerId) ?? [];
|
|
853
1499
|
return (classAncestorsByOwner.get(ownerId) ?? []).filter((id) => graph.nodes.get(id)?.kind === "class");
|
|
854
1500
|
};
|
|
1501
|
+
const callTargetsBySite = new Map();
|
|
1502
|
+
for (const edge of graph.edges) {
|
|
1503
|
+
if (edge.label !== "calls" || !edge.site)
|
|
1504
|
+
continue;
|
|
1505
|
+
const key = callSiteKey(edge.from, edge.site);
|
|
1506
|
+
const targets = callTargetsBySite.get(key);
|
|
1507
|
+
if (targets)
|
|
1508
|
+
targets.add(edge.to);
|
|
1509
|
+
else
|
|
1510
|
+
callTargetsBySite.set(key, new Set([edge.to]));
|
|
1511
|
+
}
|
|
1512
|
+
const rejectedCallSites = new Set();
|
|
855
1513
|
const sourceCache = new Map();
|
|
856
1514
|
for (const candidate of candidates) {
|
|
857
|
-
const
|
|
858
|
-
|
|
1515
|
+
const siteKey = callSiteKey(candidate.callerId, candidate.site);
|
|
1516
|
+
const existingTargets = callTargetsBySite.get(siteKey) ?? new Set();
|
|
1517
|
+
if (existingTargets.size > 1) {
|
|
1518
|
+
rejectedCallSites.add(siteKey);
|
|
1519
|
+
continue;
|
|
1520
|
+
}
|
|
1521
|
+
const rawOwner = candidate.ownerId ?? ownerByMember.get(candidate.callerId);
|
|
1522
|
+
if (!rawOwner)
|
|
859
1523
|
continue;
|
|
1524
|
+
// Shared-owner anchors redirect Swift extension and C# partial owners to the
|
|
1525
|
+
// coalesced type identity so lookup starts on the whole member set.
|
|
1526
|
+
const owner = ownerAnchors.get(rawOwner) ?? rawOwner;
|
|
860
1527
|
const memberScope = candidate.memberScope ?? inferCallMemberScope(candidate.site, sourceCache);
|
|
861
1528
|
let level = candidate.viaSupertypes ? nextOwners(owner, true) : [owner];
|
|
862
1529
|
const visited = new Set(level);
|
|
1530
|
+
let receiverDisposition = "none";
|
|
863
1531
|
for (let depth = 0; depth < MAX_SUPERTYPE_DEPTH && level.length; depth += 1) {
|
|
864
|
-
const lookup = provenMemberTarget(graph, membersByOwner, level, candidate, memberScope,
|
|
1532
|
+
const lookup = provenMemberTarget(graph, membersByOwner, level, candidate, memberScope, memberScopes, nodeAliases, memberArities, fileHiddenMemberIds, candidate.site.file);
|
|
865
1533
|
if (lookup.status === "unique") {
|
|
866
|
-
|
|
1534
|
+
if (acceptsCallTarget && !acceptsCallTarget(lookup.memberId, candidate)) {
|
|
1535
|
+
receiverDisposition = "ambiguous";
|
|
1536
|
+
if (existingTargets.size)
|
|
1537
|
+
rejectedCallSites.add(siteKey);
|
|
1538
|
+
break;
|
|
1539
|
+
}
|
|
1540
|
+
receiverDisposition = "resolved";
|
|
1541
|
+
const combinedTargets = new Set(existingTargets);
|
|
1542
|
+
combinedTargets.add(lookup.memberId);
|
|
1543
|
+
if (combinedTargets.size === 1) {
|
|
1544
|
+
if (existingTargets.size === 0 && recordEdge(candidate.callerId, lookup.memberId, "calls", candidate.site)) {
|
|
1545
|
+
existingTargets.add(lookup.memberId);
|
|
1546
|
+
callTargetsBySite.set(siteKey, existingTargets);
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
else {
|
|
1550
|
+
rejectedCallSites.add(siteKey);
|
|
1551
|
+
}
|
|
867
1552
|
break;
|
|
868
1553
|
}
|
|
869
|
-
if (lookup.status === "ambiguous")
|
|
1554
|
+
if (lookup.status === "ambiguous") {
|
|
1555
|
+
receiverDisposition = "ambiguous";
|
|
1556
|
+
if (existingTargets.size)
|
|
1557
|
+
rejectedCallSites.add(siteKey);
|
|
870
1558
|
break;
|
|
1559
|
+
}
|
|
871
1560
|
const next = [];
|
|
872
1561
|
for (const ownerId of level) {
|
|
873
1562
|
for (const supertype of nextOwners(ownerId, candidate.viaSupertypes)) {
|
|
@@ -879,7 +1568,46 @@ export function emitReceiverCallEdges(graph, candidates, recordEdge) {
|
|
|
879
1568
|
}
|
|
880
1569
|
level = next;
|
|
881
1570
|
}
|
|
1571
|
+
if (receiverDisposition === "none") {
|
|
1572
|
+
if (candidate.fallbackTargetId && !existingTargets.size) {
|
|
1573
|
+
recordEdge(candidate.callerId, candidate.fallbackTargetId, "calls", candidate.site);
|
|
1574
|
+
}
|
|
1575
|
+
else if (existingTargets.size) {
|
|
1576
|
+
rejectedCallSites.add(siteKey);
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
const removed = [];
|
|
1581
|
+
if (rejectedCallSites.size) {
|
|
1582
|
+
let writeIndex = 0;
|
|
1583
|
+
for (const edge of graph.edges) {
|
|
1584
|
+
if (edge.label === "calls" && edge.site && rejectedCallSites.has(callSiteKey(edge.from, edge.site))) {
|
|
1585
|
+
removed.push(edge);
|
|
1586
|
+
continue;
|
|
1587
|
+
}
|
|
1588
|
+
graph.edges[writeIndex] = edge;
|
|
1589
|
+
writeIndex += 1;
|
|
1590
|
+
}
|
|
1591
|
+
graph.edges.length = writeIndex;
|
|
1592
|
+
}
|
|
1593
|
+
return removed;
|
|
1594
|
+
}
|
|
1595
|
+
function canonicalMemberId(id, aliases) {
|
|
1596
|
+
let current = id;
|
|
1597
|
+
const seen = new Set();
|
|
1598
|
+
while (aliases.has(current) && !seen.has(current)) {
|
|
1599
|
+
seen.add(current);
|
|
1600
|
+
current = aliases.get(current);
|
|
882
1601
|
}
|
|
1602
|
+
return current;
|
|
1603
|
+
}
|
|
1604
|
+
function memberArityMatches(memberId, argumentCount, memberArities) {
|
|
1605
|
+
if (argumentCount === null)
|
|
1606
|
+
return true;
|
|
1607
|
+
const range = memberArities.get(memberId);
|
|
1608
|
+
// Declaration parameter counts do not prove required/default/variadic call bounds.
|
|
1609
|
+
// An unknown range cannot reject a unique target or eliminate an overload.
|
|
1610
|
+
return !range || (argumentCount >= range.min && (range.max === null || argumentCount <= range.max));
|
|
883
1611
|
}
|
|
884
1612
|
/**
|
|
885
1613
|
* The single callable member named by `candidate` across `owners`.
|
|
@@ -887,27 +1615,46 @@ export function emitReceiverCallEdges(graph, candidates, recordEdge) {
|
|
|
887
1615
|
* `ambiguous` means this depth matched the name but could not prove one member,
|
|
888
1616
|
* including arity ambiguity, and the walk must stop.
|
|
889
1617
|
*/
|
|
890
|
-
function provenMemberTarget(graph, membersByOwner, owners, candidate, memberScope,
|
|
1618
|
+
function provenMemberTarget(graph, membersByOwner, owners, candidate, memberScope, memberScopes, nodeAliases, memberArities, fileHiddenMemberIds, useFile) {
|
|
891
1619
|
const matches = new Set();
|
|
892
1620
|
for (const ownerId of owners) {
|
|
893
1621
|
for (const memberId of membersByOwner.get(ownerId) ?? []) {
|
|
894
|
-
const
|
|
895
|
-
|
|
1622
|
+
const canonicalId = canonicalMemberId(memberId, nodeAliases);
|
|
1623
|
+
const node = graph.nodes.get(memberId) ?? graph.nodes.get(canonicalId);
|
|
1624
|
+
if (!node || (node.kind !== "function" && !node.callable))
|
|
896
1625
|
continue;
|
|
897
|
-
if (
|
|
1626
|
+
if ((fileHiddenMemberIds.has(memberId) || fileHiddenMemberIds.has(canonicalId)) &&
|
|
1627
|
+
fileIdentityKey(node.file) !== fileIdentityKey(useFile)) {
|
|
898
1628
|
continue;
|
|
899
|
-
|
|
1629
|
+
}
|
|
1630
|
+
const nameMatches = candidate.caseInsensitiveMemberName
|
|
1631
|
+
? foldPhpIdentifierCase(node.name) === foldPhpIdentifierCase(candidate.memberName)
|
|
1632
|
+
: node.name === candidate.memberName;
|
|
1633
|
+
if (!nameMatches)
|
|
900
1634
|
continue;
|
|
901
|
-
|
|
1635
|
+
if (candidate.goPackagePeerFiles &&
|
|
1636
|
+
!isGoExportedMemberName("go", node.name) &&
|
|
1637
|
+
!candidate.goPackagePeerFiles.has(node.file)) {
|
|
1638
|
+
continue;
|
|
1639
|
+
}
|
|
1640
|
+
const scope = memberScopes.get(memberId) ?? memberScopes.get(canonicalId);
|
|
1641
|
+
if (memberScope !== "any" && scope !== memberScope)
|
|
1642
|
+
continue;
|
|
1643
|
+
matches.add(canonicalId);
|
|
902
1644
|
}
|
|
903
1645
|
}
|
|
904
1646
|
if (!matches.size)
|
|
905
1647
|
return { status: "none" };
|
|
906
1648
|
if (matches.size === 1) {
|
|
907
1649
|
const [memberId] = matches;
|
|
908
|
-
|
|
1650
|
+
if (memberArityMatches(memberId, candidate.argumentCount, memberArities)) {
|
|
1651
|
+
return { status: "unique", memberId: memberId };
|
|
1652
|
+
}
|
|
1653
|
+
return { status: "ambiguous" };
|
|
909
1654
|
}
|
|
910
|
-
const byArity =
|
|
1655
|
+
const byArity = candidate.argumentCount === null
|
|
1656
|
+
? []
|
|
1657
|
+
: [...matches].filter((memberId) => memberArityMatches(memberId, candidate.argumentCount, memberArities));
|
|
911
1658
|
if (byArity.length === 1)
|
|
912
1659
|
return { status: "unique", memberId: byArity[0] };
|
|
913
1660
|
return { status: "ambiguous" };
|