@lzehrung/codegraph 2.3.31 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -137
- package/codegraph-skill/codegraph/SKILL.md +5 -3
- package/dist/agent/artifact.d.ts +2 -0
- package/dist/agent/artifact.d.ts.map +1 -1
- package/dist/agent/artifact.js.map +1 -1
- package/dist/agent/search.d.ts.map +1 -1
- package/dist/agent/search.js +3 -0
- package/dist/agent/search.js.map +1 -1
- package/dist/agent/session.d.ts +3 -0
- package/dist/agent/session.d.ts.map +1 -1
- package/dist/agent/session.js +10 -2
- package/dist/agent/session.js.map +1 -1
- package/dist/bin/cli.js +42243 -29202
- package/dist/bin/query-index-worker.js +73 -11
- package/dist/cli/affected.d.ts.map +1 -1
- package/dist/cli/affected.js +4 -1
- package/dist/cli/affected.js.map +1 -1
- package/dist/cli/command-catalog.js +1 -1
- package/dist/cli/command-catalog.js.map +1 -1
- package/dist/cli/graph-queries.d.ts.map +1 -1
- package/dist/cli/graph-queries.js +27 -1
- package/dist/cli/graph-queries.js.map +1 -1
- package/dist/cli/help.d.ts +2 -2
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +4 -2
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/options.js +2 -2
- package/dist/cli/options.js.map +1 -1
- package/dist/drift/artifact.d.ts.map +1 -1
- package/dist/drift/artifact.js +15 -18
- package/dist/drift/artifact.js.map +1 -1
- package/dist/drift/compare.js +1 -1
- package/dist/drift/compare.js.map +1 -1
- package/dist/drift/snapshot.d.ts.map +1 -1
- package/dist/drift/snapshot.js +5 -6
- package/dist/drift/snapshot.js.map +1 -1
- package/dist/drift/types.d.ts +2 -0
- package/dist/drift/types.d.ts.map +1 -1
- package/dist/drift/types.js.map +1 -1
- package/dist/graph-builder.d.ts.map +1 -1
- package/dist/graph-builder.js +4 -6
- package/dist/graph-builder.js.map +1 -1
- package/dist/graph-edge-collector.d.ts +3 -0
- package/dist/graph-edge-collector.d.ts.map +1 -1
- package/dist/graph-edge-collector.js +8 -5
- package/dist/graph-edge-collector.js.map +1 -1
- package/dist/graphs/angularjs.d.ts.map +1 -1
- package/dist/graphs/angularjs.js +2 -1
- package/dist/graphs/angularjs.js.map +1 -1
- package/dist/graphs/edge-resolution.d.ts +4 -1
- package/dist/graphs/edge-resolution.d.ts.map +1 -1
- package/dist/graphs/edge-resolution.js +26 -2
- package/dist/graphs/edge-resolution.js.map +1 -1
- package/dist/graphs/specifiers.d.ts.map +1 -1
- package/dist/graphs/specifiers.js +6 -2
- package/dist/graphs/specifiers.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/ast.d.ts +20 -0
- package/dist/graphs/symbol-graph-detailed/ast.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/ast.js +154 -15
- package/dist/graphs/symbol-graph-detailed/ast.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/edge-passes.d.ts +51 -8
- package/dist/graphs/symbol-graph-detailed/edge-passes.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/edge-passes.js +823 -132
- package/dist/graphs/symbol-graph-detailed/edge-passes.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/import-aliases.d.ts +2 -1
- package/dist/graphs/symbol-graph-detailed/import-aliases.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/import-aliases.js +139 -7
- package/dist/graphs/symbol-graph-detailed/import-aliases.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/member-chains.d.ts +1 -0
- package/dist/graphs/symbol-graph-detailed/member-chains.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/member-chains.js +3 -1
- package/dist/graphs/symbol-graph-detailed/member-chains.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed/receiver-calls.d.ts +128 -15
- package/dist/graphs/symbol-graph-detailed/receiver-calls.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed/receiver-calls.js +873 -93
- package/dist/graphs/symbol-graph-detailed/receiver-calls.js.map +1 -1
- package/dist/graphs/symbol-graph-detailed.d.ts.map +1 -1
- package/dist/graphs/symbol-graph-detailed.js +434 -40
- package/dist/graphs/symbol-graph-detailed.js.map +1 -1
- package/dist/graphs/symbol-graph.d.ts +2 -0
- package/dist/graphs/symbol-graph.d.ts.map +1 -1
- package/dist/graphs/symbol-graph.js +34 -16
- package/dist/graphs/symbol-graph.js.map +1 -1
- package/dist/impact/call-compatibility/providers/index.d.ts +4 -4
- package/dist/impact/call-compatibility/providers/index.d.ts.map +1 -1
- package/dist/impact/call-compatibility/providers/index.js +6 -6
- package/dist/impact/call-compatibility/providers/index.js.map +1 -1
- package/dist/impact/call-compatibility/providers/types.d.ts +2 -0
- package/dist/impact/call-compatibility/providers/types.d.ts.map +1 -1
- package/dist/impact/call-compatibility/types.d.ts +6 -0
- package/dist/impact/call-compatibility/types.d.ts.map +1 -1
- package/dist/impact/call-compatibility.d.ts.map +1 -1
- package/dist/impact/call-compatibility.js +310 -516
- package/dist/impact/call-compatibility.js.map +1 -1
- package/dist/impact/map.js +1 -0
- package/dist/impact/map.js.map +1 -1
- package/dist/impact/reference-cache.d.ts.map +1 -1
- package/dist/impact/reference-cache.js +2 -2
- package/dist/impact/reference-cache.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/indexer/ambiguous-resolution.d.ts +11 -0
- package/dist/indexer/ambiguous-resolution.d.ts.map +1 -0
- package/dist/indexer/ambiguous-resolution.js +19 -0
- package/dist/indexer/ambiguous-resolution.js.map +1 -0
- package/dist/indexer/build-cache/module-cache.d.ts +38 -0
- package/dist/indexer/build-cache/module-cache.d.ts.map +1 -1
- package/dist/indexer/build-cache/module-cache.js +151 -9
- package/dist/indexer/build-cache/module-cache.js.map +1 -1
- package/dist/indexer/build-cache/options.d.ts +16 -43
- package/dist/indexer/build-cache/options.d.ts.map +1 -1
- package/dist/indexer/build-cache/options.js +16 -43
- package/dist/indexer/build-cache/options.js.map +1 -1
- package/dist/indexer/build-cache/project-snapshot.d.ts +1 -0
- package/dist/indexer/build-cache/project-snapshot.d.ts.map +1 -1
- package/dist/indexer/build-cache/project-snapshot.js +117 -24
- package/dist/indexer/build-cache/project-snapshot.js.map +1 -1
- package/dist/indexer/build-cache/resolver-environment.d.ts.map +1 -1
- package/dist/indexer/build-cache/resolver-environment.js +32 -2
- package/dist/indexer/build-cache/resolver-environment.js.map +1 -1
- package/dist/indexer/build-cache.d.ts +1 -1
- package/dist/indexer/build-cache.d.ts.map +1 -1
- package/dist/indexer/build-cache.js +1 -1
- package/dist/indexer/build-cache.js.map +1 -1
- package/dist/indexer/build-index.d.ts.map +1 -1
- package/dist/indexer/build-index.js +593 -22
- package/dist/indexer/build-index.js.map +1 -1
- package/dist/indexer/call-hierarchy.js +1 -1
- package/dist/indexer/call-hierarchy.js.map +1 -1
- package/dist/indexer/compilation-units.d.ts +101 -0
- package/dist/indexer/compilation-units.d.ts.map +1 -0
- package/dist/indexer/compilation-units.js +605 -0
- package/dist/indexer/compilation-units.js.map +1 -0
- package/dist/indexer/cpp-callables.d.ts +20 -0
- package/dist/indexer/cpp-callables.d.ts.map +1 -0
- package/dist/indexer/cpp-callables.js +554 -0
- package/dist/indexer/cpp-callables.js.map +1 -0
- package/dist/indexer/declaration-visibility.d.ts +36 -2
- package/dist/indexer/declaration-visibility.d.ts.map +1 -1
- package/dist/indexer/declaration-visibility.js +136 -2
- package/dist/indexer/declaration-visibility.js.map +1 -1
- package/dist/indexer/expand-star-imports.d.ts.map +1 -1
- package/dist/indexer/expand-star-imports.js +28 -6
- package/dist/indexer/expand-star-imports.js.map +1 -1
- package/dist/indexer/import-types.d.ts +37 -0
- package/dist/indexer/import-types.d.ts.map +1 -1
- package/dist/indexer/import-types.js +24 -1
- package/dist/indexer/import-types.js.map +1 -1
- package/dist/indexer/imports/context.d.ts +11 -1
- package/dist/indexer/imports/context.d.ts.map +1 -1
- package/dist/indexer/imports/import-binding-tables.d.ts +12 -1
- package/dist/indexer/imports/import-binding-tables.d.ts.map +1 -1
- package/dist/indexer/imports/import-binding-tables.js +144 -19
- package/dist/indexer/imports/import-binding-tables.js.map +1 -1
- package/dist/indexer/imports/js-text-imports.js +4 -4
- package/dist/indexer/imports/js-text-imports.js.map +1 -1
- package/dist/indexer/imports/native-captures.d.ts.map +1 -1
- package/dist/indexer/imports/native-captures.js +18 -6
- package/dist/indexer/imports/native-captures.js.map +1 -1
- package/dist/indexer/imports/python.d.ts +10 -2
- package/dist/indexer/imports/python.d.ts.map +1 -1
- package/dist/indexer/imports/python.js +91 -40
- package/dist/indexer/imports/python.js.map +1 -1
- package/dist/indexer/imports/text-import-extractors.d.ts.map +1 -1
- package/dist/indexer/imports/text-import-extractors.js +51 -12
- package/dist/indexer/imports/text-import-extractors.js.map +1 -1
- package/dist/indexer/imports.d.ts.map +1 -1
- package/dist/indexer/imports.js +23 -4
- package/dist/indexer/imports.js.map +1 -1
- package/dist/indexer/incremental-plan.d.ts +33 -0
- package/dist/indexer/incremental-plan.d.ts.map +1 -1
- package/dist/indexer/incremental-plan.js +187 -1
- package/dist/indexer/incremental-plan.js.map +1 -1
- package/dist/indexer/locals-and-exports.d.ts +2 -0
- package/dist/indexer/locals-and-exports.d.ts.map +1 -1
- package/dist/indexer/locals-and-exports.js +565 -54
- package/dist/indexer/locals-and-exports.js.map +1 -1
- package/dist/indexer/name-lookup-policies/c-family.d.ts +7 -0
- package/dist/indexer/name-lookup-policies/c-family.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/c-family.js +117 -0
- package/dist/indexer/name-lookup-policies/c-family.js.map +1 -0
- package/dist/indexer/name-lookup-policies/csharp-using-static.d.ts +11 -0
- package/dist/indexer/name-lookup-policies/csharp-using-static.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/csharp-using-static.js +136 -0
- package/dist/indexer/name-lookup-policies/csharp-using-static.js.map +1 -0
- package/dist/indexer/name-lookup-policies/implicit-self.d.ts +10 -0
- package/dist/indexer/name-lookup-policies/implicit-self.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/implicit-self.js +113 -0
- package/dist/indexer/name-lookup-policies/implicit-self.js.map +1 -0
- package/dist/indexer/name-lookup-policies/index.d.ts +3 -0
- package/dist/indexer/name-lookup-policies/index.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/index.js +106 -0
- package/dist/indexer/name-lookup-policies/index.js.map +1 -0
- package/dist/indexer/name-lookup-policies/php.d.ts +3 -0
- package/dist/indexer/name-lookup-policies/php.d.ts.map +1 -0
- package/dist/indexer/name-lookup-policies/php.js +75 -0
- package/dist/indexer/name-lookup-policies/php.js.map +1 -0
- package/dist/indexer/name-resolution-types.d.ts +99 -0
- package/dist/indexer/name-resolution-types.d.ts.map +1 -0
- package/dist/indexer/name-resolution-types.js +2 -0
- package/dist/indexer/name-resolution-types.js.map +1 -0
- package/dist/indexer/name-resolution.d.ts +52 -0
- package/dist/indexer/name-resolution.d.ts.map +1 -0
- package/dist/indexer/name-resolution.js +189 -0
- package/dist/indexer/name-resolution.js.map +1 -0
- package/dist/indexer/navigation-cpp.d.ts +50 -0
- package/dist/indexer/navigation-cpp.d.ts.map +1 -0
- package/dist/indexer/navigation-cpp.js +654 -0
- package/dist/indexer/navigation-cpp.js.map +1 -0
- package/dist/indexer/navigation-goto.d.ts +106 -3
- package/dist/indexer/navigation-goto.d.ts.map +1 -1
- package/dist/indexer/navigation-goto.js +2074 -137
- package/dist/indexer/navigation-goto.js.map +1 -1
- package/dist/indexer/navigation-local.d.ts +18 -2
- package/dist/indexer/navigation-local.d.ts.map +1 -1
- package/dist/indexer/navigation-local.js +498 -61
- package/dist/indexer/navigation-local.js.map +1 -1
- package/dist/indexer/navigation-php.d.ts +66 -0
- package/dist/indexer/navigation-php.d.ts.map +1 -1
- package/dist/indexer/navigation-php.js +287 -4
- package/dist/indexer/navigation-php.js.map +1 -1
- package/dist/indexer/navigation-references.d.ts +72 -5
- package/dist/indexer/navigation-references.d.ts.map +1 -1
- package/dist/indexer/navigation-references.js +620 -54
- package/dist/indexer/navigation-references.js.map +1 -1
- package/dist/indexer/navigation-resolve.d.ts +33 -2
- package/dist/indexer/navigation-resolve.d.ts.map +1 -1
- package/dist/indexer/navigation-resolve.js +446 -193
- package/dist/indexer/navigation-resolve.js.map +1 -1
- package/dist/indexer/navigation.d.ts +28 -2
- package/dist/indexer/navigation.d.ts.map +1 -1
- package/dist/indexer/navigation.js +1082 -151
- package/dist/indexer/navigation.js.map +1 -1
- package/dist/indexer/php-namespace-symbols.d.ts +66 -0
- package/dist/indexer/php-namespace-symbols.d.ts.map +1 -0
- package/dist/indexer/php-namespace-symbols.js +342 -0
- package/dist/indexer/php-namespace-symbols.js.map +1 -0
- package/dist/indexer/reference-context.d.ts +2 -0
- package/dist/indexer/reference-context.d.ts.map +1 -1
- package/dist/indexer/reference-context.js +4 -0
- package/dist/indexer/reference-context.js.map +1 -1
- package/dist/indexer/scope-nodes.d.ts +92 -13
- package/dist/indexer/scope-nodes.d.ts.map +1 -1
- package/dist/indexer/scope-nodes.js +212 -20
- package/dist/indexer/scope-nodes.js.map +1 -1
- package/dist/indexer/scope-types.d.ts +25 -1
- package/dist/indexer/scope-types.d.ts.map +1 -1
- package/dist/indexer/scope.d.ts.map +1 -1
- package/dist/indexer/scope.js +894 -45
- package/dist/indexer/scope.js.map +1 -1
- package/dist/indexer/shared-owner-identity.d.ts +53 -0
- package/dist/indexer/shared-owner-identity.d.ts.map +1 -0
- package/dist/indexer/shared-owner-identity.js +460 -0
- package/dist/indexer/shared-owner-identity.js.map +1 -0
- package/dist/indexer/shared.d.ts +1 -1
- package/dist/indexer/shared.d.ts.map +1 -1
- package/dist/indexer/shared.js +1 -1
- package/dist/indexer/shared.js.map +1 -1
- package/dist/indexer/star-import-precedence.d.ts +114 -0
- package/dist/indexer/star-import-precedence.d.ts.map +1 -0
- package/dist/indexer/star-import-precedence.js +396 -0
- package/dist/indexer/star-import-precedence.js.map +1 -0
- package/dist/indexer/symbols.d.ts +3 -1
- package/dist/indexer/symbols.d.ts.map +1 -1
- package/dist/indexer/symbols.js +58 -18
- package/dist/indexer/symbols.js.map +1 -1
- package/dist/indexer/ts-callables.d.ts +51 -0
- package/dist/indexer/ts-callables.d.ts.map +1 -0
- package/dist/indexer/ts-callables.js +264 -0
- package/dist/indexer/ts-callables.js.map +1 -0
- package/dist/indexer/types.d.ts +23 -1
- package/dist/indexer/types.d.ts.map +1 -1
- package/dist/indexer/unused-exports.d.ts +13 -0
- package/dist/indexer/unused-exports.d.ts.map +1 -0
- package/dist/indexer/unused-exports.js +159 -0
- package/dist/indexer/unused-exports.js.map +1 -0
- package/dist/indexer/workspace-symbols.d.ts.map +1 -1
- package/dist/indexer/workspace-symbols.js +3 -2
- package/dist/indexer/workspace-symbols.js.map +1 -1
- package/dist/indexer.d.ts +2 -2
- package/dist/indexer.d.ts.map +1 -1
- package/dist/indexer.js +1 -1
- package/dist/indexer.js.map +1 -1
- package/dist/languages/callable-arity.d.ts +202 -0
- package/dist/languages/callable-arity.d.ts.map +1 -0
- package/dist/languages/callable-arity.js +1182 -0
- package/dist/languages/callable-arity.js.map +1 -0
- package/dist/languages/definitions/c-family.d.ts.map +1 -1
- package/dist/languages/definitions/c-family.js +30 -1
- package/dist/languages/definitions/c-family.js.map +1 -1
- package/dist/languages/definitions/c.d.ts +4 -0
- package/dist/languages/definitions/c.d.ts.map +1 -1
- package/dist/languages/definitions/c.js +19 -0
- package/dist/languages/definitions/c.js.map +1 -1
- package/dist/languages/definitions/cpp.d.ts.map +1 -1
- package/dist/languages/definitions/cpp.js +12 -0
- package/dist/languages/definitions/cpp.js.map +1 -1
- package/dist/languages/definitions/csharp.d.ts.map +1 -1
- package/dist/languages/definitions/csharp.js +2 -8
- package/dist/languages/definitions/csharp.js.map +1 -1
- package/dist/languages/definitions/go.d.ts.map +1 -1
- package/dist/languages/definitions/go.js +3 -0
- package/dist/languages/definitions/go.js.map +1 -1
- package/dist/languages/definitions/javascript.d.ts.map +1 -1
- package/dist/languages/definitions/javascript.js +10 -1
- package/dist/languages/definitions/javascript.js.map +1 -1
- package/dist/languages/definitions/js-family.d.ts +1 -1
- package/dist/languages/definitions/js-family.d.ts.map +1 -1
- package/dist/languages/definitions/js-family.js +7 -1
- package/dist/languages/definitions/js-family.js.map +1 -1
- package/dist/languages/definitions/php.d.ts.map +1 -1
- package/dist/languages/definitions/php.js +5 -1
- package/dist/languages/definitions/php.js.map +1 -1
- package/dist/languages/definitions/typescript.d.ts.map +1 -1
- package/dist/languages/definitions/typescript.js +8 -1
- package/dist/languages/definitions/typescript.js.map +1 -1
- package/dist/languages/definitions/zig.d.ts.map +1 -1
- package/dist/languages/definitions/zig.js +2 -0
- package/dist/languages/definitions/zig.js.map +1 -1
- package/dist/mcp/handlers.js +14 -7
- package/dist/mcp/handlers.js.map +1 -1
- package/dist/review/candidates.d.ts.map +1 -1
- package/dist/review/candidates.js +1 -1
- package/dist/review/candidates.js.map +1 -1
- package/dist/review/deleted.d.ts +3 -3
- package/dist/review/deleted.d.ts.map +1 -1
- package/dist/review/deleted.js +82 -32
- package/dist/review/deleted.js.map +1 -1
- package/dist/review/report.d.ts +1 -0
- package/dist/review/report.d.ts.map +1 -1
- package/dist/review/report.js +1 -1
- package/dist/review/report.js.map +1 -1
- package/dist/review.d.ts.map +1 -1
- package/dist/review.js +1 -0
- package/dist/review.js.map +1 -1
- package/dist/sql/navigation.d.ts.map +1 -1
- package/dist/sql/navigation.js +1 -0
- package/dist/sql/navigation.js.map +1 -1
- package/dist/sql/source-graph.d.ts.map +1 -1
- package/dist/sql/source-graph.js +2 -2
- package/dist/sql/source-graph.js.map +1 -1
- package/dist/sqlite/schema.d.ts +1 -1
- package/dist/sqlite/schema.d.ts.map +1 -1
- package/dist/sqlite/schema.js +8 -2
- package/dist/sqlite/schema.js.map +1 -1
- package/dist/sqlite/write.js +2 -2
- package/dist/sqlite/write.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/util/bloom-filter.d.ts +7 -1
- package/dist/util/bloom-filter.d.ts.map +1 -1
- package/dist/util/bloom-filter.js +12 -2
- package/dist/util/bloom-filter.js.map +1 -1
- package/dist/util/graph-edges.d.ts +19 -1
- package/dist/util/graph-edges.d.ts.map +1 -1
- package/dist/util/graph-edges.js +30 -4
- package/dist/util/graph-edges.js.map +1 -1
- package/dist/util/identifiers.d.ts +15 -0
- package/dist/util/identifiers.d.ts.map +1 -1
- package/dist/util/identifiers.js +35 -0
- package/dist/util/identifiers.js.map +1 -1
- package/dist/util/member-access-tables.d.ts +61 -4
- package/dist/util/member-access-tables.d.ts.map +1 -1
- package/dist/util/member-access-tables.js +137 -19
- package/dist/util/member-access-tables.js.map +1 -1
- package/dist/util/member-access.d.ts +17 -0
- package/dist/util/member-access.d.ts.map +1 -1
- package/dist/util/member-access.js +95 -3
- package/dist/util/member-access.js.map +1 -1
- package/dist/util/resolution/cargo-targets.d.ts +11 -0
- package/dist/util/resolution/cargo-targets.d.ts.map +1 -1
- package/dist/util/resolution/cargo-targets.js +174 -13
- package/dist/util/resolution/cargo-targets.js.map +1 -1
- package/dist/util/resolution/csharp.d.ts +33 -1
- package/dist/util/resolution/csharp.d.ts.map +1 -1
- package/dist/util/resolution/csharp.js +145 -30
- package/dist/util/resolution/csharp.js.map +1 -1
- package/dist/util/resolution/php.d.ts.map +1 -1
- package/dist/util/resolution/php.js +35 -8
- package/dist/util/resolution/php.js.map +1 -1
- package/dist/util/resolution/python.d.ts +9 -0
- package/dist/util/resolution/python.d.ts.map +1 -1
- package/dist/util/resolution/python.js +46 -0
- package/dist/util/resolution/python.js.map +1 -1
- package/dist/util/resolution/rust.d.ts.map +1 -1
- package/dist/util/resolution/rust.js +95 -26
- package/dist/util/resolution/rust.js.map +1 -1
- package/dist/util/resolution-candidates.d.ts +5 -0
- package/dist/util/resolution-candidates.d.ts.map +1 -1
- package/dist/util/resolution-candidates.js +16 -3
- package/dist/util/resolution-candidates.js.map +1 -1
- package/dist/util/resolution.d.ts +2 -1
- package/dist/util/resolution.d.ts.map +1 -1
- package/dist/util/resolution.js +96 -5
- package/dist/util/resolution.js.map +1 -1
- package/dist/util/specifiers.d.ts +20 -0
- package/dist/util/specifiers.d.ts.map +1 -1
- package/dist/util/specifiers.js +29 -0
- package/dist/util/specifiers.js.map +1 -1
- package/docs/mcp.md +3 -2
- package/package.json +3 -3
- package/dist/impact/call-compatibility/providers/profiles.d.ts +0 -80
- package/dist/impact/call-compatibility/providers/profiles.d.ts.map +0 -1
- package/dist/impact/call-compatibility/providers/profiles.js +0 -164
- package/dist/impact/call-compatibility/providers/profiles.js.map +0 -1
|
@@ -1,10 +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
|
-
import {
|
|
7
|
-
|
|
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";
|
|
8
12
|
/** Languages whose grammar distinguishes static members from instance members. */
|
|
9
13
|
const STATIC_MEMBER_LANGUAGES = {
|
|
10
14
|
cpp: true,
|
|
@@ -16,11 +20,69 @@ const STATIC_MEMBER_LANGUAGES = {
|
|
|
16
20
|
ts: true,
|
|
17
21
|
tsx: true,
|
|
18
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
|
+
}
|
|
19
45
|
/** Every language with a static-member distinction, guarded by the registry-consistency test. */
|
|
20
46
|
export const staticMemberLanguageIds = Object.keys(STATIC_MEMBER_LANGUAGES);
|
|
21
47
|
export function hasStaticMemberDistinction(languageId) {
|
|
22
48
|
return STATIC_MEMBER_LANGUAGES[languageId] !== undefined;
|
|
23
49
|
}
|
|
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)
|
|
67
|
+
return false;
|
|
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;
|
|
77
|
+
}
|
|
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);
|
|
85
|
+
}
|
|
24
86
|
/**
|
|
25
87
|
* Call nodes that carry the receiver and the member name on the call node itself
|
|
26
88
|
* instead of exposing a member-access callee.
|
|
@@ -51,6 +113,25 @@ const MEMBER_CONTAINER_TYPES = {
|
|
|
51
113
|
struct_specifier: true,
|
|
52
114
|
trait_declaration: true,
|
|
53
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,
|
|
54
135
|
};
|
|
55
136
|
/** Nodes holding a call's argument list across the supported grammars. */
|
|
56
137
|
export const CALL_ARGUMENT_NODE_TYPES = {
|
|
@@ -66,6 +147,16 @@ const HIERARCHY_LABELS = {
|
|
|
66
147
|
mixin: true,
|
|
67
148
|
trait: true,
|
|
68
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
|
+
}
|
|
69
160
|
/**
|
|
70
161
|
* Nodes that bind a value name across the supported grammars: locals, parameters,
|
|
71
162
|
* and assignment targets. A receiver naming one of these is a value, not a type.
|
|
@@ -75,6 +166,9 @@ const VALUE_BINDING_TYPES = {
|
|
|
75
166
|
assignment_expression: true,
|
|
76
167
|
assignment_statement: true,
|
|
77
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,
|
|
78
172
|
formal_parameter: true,
|
|
79
173
|
init_declarator: true,
|
|
80
174
|
let_declaration: true,
|
|
@@ -90,21 +184,6 @@ const VALUE_BINDING_TYPES = {
|
|
|
90
184
|
variable_declaration: true,
|
|
91
185
|
variable_declarator: true,
|
|
92
186
|
};
|
|
93
|
-
/**
|
|
94
|
-
* Parameter lists whose identifier children are value bindings (Ruby has no wrapping param node).
|
|
95
|
-
* Union of the receiver-call and call-compatibility lists so Kotlin `function_value_parameters`
|
|
96
|
-
* and Ruby `block_parameters` are both recognized.
|
|
97
|
-
*/
|
|
98
|
-
export const PARAMETER_LIST_NODE_TYPES = {
|
|
99
|
-
block_parameters: true,
|
|
100
|
-
formal_parameters: true,
|
|
101
|
-
function_parameter_clause: true,
|
|
102
|
-
function_value_parameters: true,
|
|
103
|
-
lambda_parameters: true,
|
|
104
|
-
method_parameters: true,
|
|
105
|
-
parameter_list: true,
|
|
106
|
-
parameters: true,
|
|
107
|
-
};
|
|
108
187
|
/**
|
|
109
188
|
* Access nodes whose syntax is type-scoped (`::`), not dotted member access.
|
|
110
189
|
* A dotted identifier is never itself proof of a type.
|
|
@@ -180,6 +259,8 @@ const BINDING_DECLARATION_TYPES = new Set([
|
|
|
180
259
|
"property_declaration",
|
|
181
260
|
"local_variable_declaration",
|
|
182
261
|
"local_declaration_statement",
|
|
262
|
+
// C/C++ local variable statement, e.g. `Box b;`, `Box* p = raw;`, `Box w{5};`.
|
|
263
|
+
"declaration",
|
|
183
264
|
]);
|
|
184
265
|
const RUBY_CONSTANT_SOURCE = String.raw `(?=\p{Lu})${XID_IDENTIFIER_SOURCE}`;
|
|
185
266
|
/** Proven construction forms that name a receiver's type, keyed by language. */
|
|
@@ -193,7 +274,7 @@ const LANGUAGE_CONSTRUCTION_FORMS = {
|
|
|
193
274
|
php: { newExpression: true },
|
|
194
275
|
python: { capitalizedCall: true },
|
|
195
276
|
ruby: { rubyNew: true },
|
|
196
|
-
rust: { capitalizedCall: true, rustUnitStruct: true },
|
|
277
|
+
rust: { capitalizedCall: true, rustUnitStruct: true, compositeLiteral: true },
|
|
197
278
|
swift: { capitalizedCall: true },
|
|
198
279
|
ts: { newExpression: true },
|
|
199
280
|
tsx: { newExpression: true },
|
|
@@ -237,12 +318,13 @@ export function receiverCallAccess(sup, callNode, callee) {
|
|
|
237
318
|
return { accessNode, receiver, property };
|
|
238
319
|
}
|
|
239
320
|
/**
|
|
240
|
-
* Identifier a binding node declares: a `name` field, a nested C/C++
|
|
241
|
-
* an assignment left-hand side, or the last identifier child (C++
|
|
242
|
-
* 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.
|
|
243
325
|
*/
|
|
244
326
|
function bindingIdentifier(node, sup) {
|
|
245
|
-
const named = node.childForFieldName("name");
|
|
327
|
+
const named = node.childForFieldName("name") ?? node.childForFieldName("pattern");
|
|
246
328
|
if (named && (isReceiverNameNode(sup, named.type) || named.type === "field_identifier")) {
|
|
247
329
|
return named;
|
|
248
330
|
}
|
|
@@ -323,6 +405,17 @@ function containsIndex(node, index) {
|
|
|
323
405
|
return node.startIndex <= index && node.endIndex >= index;
|
|
324
406
|
}
|
|
325
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
|
+
}
|
|
326
419
|
const constructor = node.childForFieldName("constructor") ?? node.child(0);
|
|
327
420
|
if (constructor && isReceiverNameNode(sup, constructor.type)) {
|
|
328
421
|
return constructor;
|
|
@@ -334,14 +427,85 @@ function constructorNameNode(node, sup) {
|
|
|
334
427
|
}
|
|
335
428
|
return null;
|
|
336
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
|
+
}
|
|
337
470
|
function rubyNewReceiverNameNode(node, source, sup) {
|
|
338
471
|
if (!new RegExp(String.raw `^(?:${RUBY_CONSTANT_SOURCE})\.new$`, "u").test(sliceText(node, source)))
|
|
339
472
|
return null;
|
|
340
473
|
return node.namedChildren.find((child) => isReceiverNameNode(sup, child.type) || child.type === "constant") ?? null;
|
|
341
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
|
+
}
|
|
342
500
|
export function unwrapNamedType(node, sup) {
|
|
343
501
|
let current = node;
|
|
344
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
|
+
}
|
|
345
509
|
if (current.type === "type_annotation" ||
|
|
346
510
|
current.type === "named_type" ||
|
|
347
511
|
current.type === "user_type" ||
|
|
@@ -358,6 +522,16 @@ export function unwrapNamedType(node, sup) {
|
|
|
358
522
|
current = current.childForFieldName("type") ?? current.namedChildren[0] ?? null;
|
|
359
523
|
continue;
|
|
360
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
|
+
}
|
|
361
535
|
break;
|
|
362
536
|
}
|
|
363
537
|
if (!current)
|
|
@@ -367,6 +541,34 @@ export function unwrapNamedType(node, sup) {
|
|
|
367
541
|
}
|
|
368
542
|
return null;
|
|
369
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
|
+
}
|
|
370
572
|
function capitalizedCallTypeName(expr, source, sup) {
|
|
371
573
|
if (expr.type !== "call" && expr.type !== "call_expression")
|
|
372
574
|
return null;
|
|
@@ -395,11 +597,21 @@ function compositeLiteralTypeName(expr, source, sup) {
|
|
|
395
597
|
return null;
|
|
396
598
|
current = operand;
|
|
397
599
|
}
|
|
398
|
-
|
|
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") {
|
|
399
605
|
return null;
|
|
606
|
+
}
|
|
400
607
|
const typeNode = current.childForFieldName("type") ??
|
|
401
|
-
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) ??
|
|
402
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.
|
|
403
615
|
return typeNode ? (unwrapNamedType(typeNode, sup) ?? typeNode) : null;
|
|
404
616
|
}
|
|
405
617
|
/**
|
|
@@ -458,13 +670,13 @@ function bindingValueExpression(node) {
|
|
|
458
670
|
function declaredTypeNameNode(node, sup) {
|
|
459
671
|
const typeField = node.childForFieldName("type");
|
|
460
672
|
if (typeField)
|
|
461
|
-
return unwrapNamedType(typeField, sup);
|
|
673
|
+
return unwrapNamedType(typeField, sup) ?? typeField;
|
|
462
674
|
const typedChild = node.namedChildren.find((child) => child.type === "named_type" ||
|
|
463
675
|
child.type === "user_type" ||
|
|
464
676
|
child.type === "type_annotation" ||
|
|
465
677
|
child.type === "type");
|
|
466
678
|
if (typedChild)
|
|
467
|
-
return unwrapNamedType(typedChild, sup);
|
|
679
|
+
return unwrapNamedType(typedChild, sup) ?? typedChild;
|
|
468
680
|
const ids = node.namedChildren.filter((child) => isReceiverNameNode(sup, child.type) || child.type === "type_identifier" || child.type === "name");
|
|
469
681
|
if (ids.length >= 2)
|
|
470
682
|
return unwrapNamedType(ids[0], sup);
|
|
@@ -542,6 +754,10 @@ function bindingProof(node, receiverName, source, sup) {
|
|
|
542
754
|
return { status: "none" };
|
|
543
755
|
if (!bindingDeclaresReceiverName(node, receiverName, source, sup))
|
|
544
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 };
|
|
545
761
|
const typeNode = constructionTypeFromBinding(node, receiverName, source, sup);
|
|
546
762
|
if (typeNode)
|
|
547
763
|
return { status: "type", node: typeNode };
|
|
@@ -553,12 +769,21 @@ function isSkippableBindingContainer(node, receiver) {
|
|
|
553
769
|
function findPriorConstructorInContainer(node, receiver, receiverName, source, sup) {
|
|
554
770
|
let constructor = null;
|
|
555
771
|
let sawUnproven = false;
|
|
772
|
+
let declared = false;
|
|
556
773
|
const visit = (current) => {
|
|
557
774
|
if (current.startIndex >= receiver.startIndex)
|
|
558
775
|
return true;
|
|
559
776
|
if (current !== node && isSkippableBindingContainer(current, receiver))
|
|
560
777
|
return true;
|
|
561
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;
|
|
562
787
|
if (proof.status === "unproven") {
|
|
563
788
|
if (constructor) {
|
|
564
789
|
constructor = null;
|
|
@@ -594,7 +819,10 @@ function bindingContainerDeclaresNameBefore(node, receiver, receiverName, source
|
|
|
594
819
|
return false;
|
|
595
820
|
if (current !== node && isSkippableBindingContainer(current, receiver))
|
|
596
821
|
return false;
|
|
597
|
-
|
|
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) &&
|
|
598
826
|
bindingDeclaresReceiverName(current, receiverName, source, sup)) {
|
|
599
827
|
return true;
|
|
600
828
|
}
|
|
@@ -629,33 +857,290 @@ function findVisiblePriorConstructor(receiver, receiverName, source, sup) {
|
|
|
629
857
|
* Resolves the node naming the type a receiver expression was constructed from, or
|
|
630
858
|
* null when no constructor is proven for it. Shared with detailed symbol-graph call
|
|
631
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.
|
|
632
863
|
*/
|
|
633
864
|
export function receiverConstructorExpression(obj, source, sup) {
|
|
634
865
|
const direct = constructionTypeName(obj, source, sup);
|
|
635
|
-
if (direct)
|
|
636
|
-
return direct;
|
|
637
866
|
if (!isReceiverNameNode(sup, obj.type))
|
|
638
|
-
return
|
|
867
|
+
return direct;
|
|
639
868
|
const receiverName = sliceText(obj, source);
|
|
869
|
+
if (direct && !bindsLocalValue(obj, receiverName, source, sup))
|
|
870
|
+
return direct;
|
|
640
871
|
return findVisiblePriorConstructor(obj, receiverName, source, sup);
|
|
641
872
|
}
|
|
642
|
-
|
|
643
|
-
|
|
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);
|
|
644
1127
|
}
|
|
645
1128
|
/**
|
|
646
1129
|
* Classifies a receiver as the declaring type, a supertype, or a named/constructed type.
|
|
647
1130
|
* Returns null when the receiver cannot be proven.
|
|
648
1131
|
* Named-local constructor lookup is memoized per enclosing function and receiver text.
|
|
649
1132
|
*/
|
|
650
|
-
export function classifyReceiver(sup, receiver, source, proofCache, cacheScope, accessNode) {
|
|
651
|
-
const
|
|
652
|
-
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);
|
|
653
1135
|
if (!text)
|
|
654
1136
|
return null;
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
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
|
+
}
|
|
659
1144
|
const receiverIsName = isReceiverNameNode(sup, receiver.type);
|
|
660
1145
|
const cacheKey = `${cacheScope}\u0000${text}`;
|
|
661
1146
|
let proof = proofCache.get(cacheKey);
|
|
@@ -671,7 +1156,9 @@ export function classifyReceiver(sup, receiver, source, proofCache, cacheScope,
|
|
|
671
1156
|
return {
|
|
672
1157
|
kind: "named-type",
|
|
673
1158
|
typeName: sliceText(proof.constructed, source),
|
|
1159
|
+
typeNode: proof.constructed,
|
|
674
1160
|
memberScope: hasStaticMemberDistinction(sup.id) ? "instance" : "any",
|
|
1161
|
+
constructed: true,
|
|
675
1162
|
};
|
|
676
1163
|
}
|
|
677
1164
|
if (!receiverIsName)
|
|
@@ -680,24 +1167,83 @@ export function classifyReceiver(sup, receiver, source, proofCache, cacheScope,
|
|
|
680
1167
|
// `Example::shared()` would still be attributed to a colliding parameter named Example.
|
|
681
1168
|
if (proof.locallyBound)
|
|
682
1169
|
return null;
|
|
683
|
-
// Dotted `Cfg.load()` is not proof
|
|
684
|
-
// is
|
|
685
|
-
//
|
|
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.
|
|
686
1175
|
const property = getMemberAccessParts(sup, accessNode).property;
|
|
687
1176
|
const between = property ? source.slice(receiver.endIndex, property.startIndex) : "";
|
|
688
1177
|
const typeScoped = TYPE_SCOPED_ACCESS_TYPES[accessNode.type] === true || between.includes("::");
|
|
689
|
-
if (receiver.type !== "type_identifier" && !typeScoped)
|
|
690
|
-
|
|
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
|
+
}
|
|
691
1192
|
return {
|
|
692
1193
|
kind: "named-type",
|
|
693
1194
|
typeName: text,
|
|
1195
|
+
typeNode: receiver,
|
|
694
1196
|
memberScope: hasStaticMemberDistinction(sup.id) && typeScoped ? "static" : "any",
|
|
695
1197
|
};
|
|
696
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
|
+
}
|
|
697
1230
|
/** Whether a resolved definition can declare callable members. */
|
|
698
1231
|
export function declaresMembers(def) {
|
|
699
1232
|
return def.kind === SymbolKind.Class || def.kind === SymbolKind.Interface || def.kind === SymbolKind.TypeAlias;
|
|
700
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
|
+
}
|
|
701
1247
|
/** Nearest enclosing declaration that lexically owns `node` as a member. */
|
|
702
1248
|
export function nearestMemberContainer(node) {
|
|
703
1249
|
let current = node.parent;
|
|
@@ -708,29 +1254,168 @@ export function nearestMemberContainer(node) {
|
|
|
708
1254
|
}
|
|
709
1255
|
return null;
|
|
710
1256
|
}
|
|
711
|
-
/** Positional argument count of a call,
|
|
712
|
-
export function callArgumentCount(callNode) {
|
|
713
|
-
|
|
714
|
-
|
|
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;
|
|
715
1269
|
if (argumentNode?.type === "call_suffix") {
|
|
716
1270
|
argumentNode = (argumentNode.namedChildren ?? []).find((child) => child.type === "value_arguments") ?? null;
|
|
717
1271
|
}
|
|
718
|
-
if (!argumentNode)
|
|
719
|
-
|
|
720
|
-
|
|
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;
|
|
721
1289
|
}
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
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])
|
|
725
1307
|
return null;
|
|
726
|
-
const
|
|
727
|
-
if (
|
|
1308
|
+
const children = parent.namedChildren ?? [];
|
|
1309
|
+
if (children.some((child) => CALL_ARGUMENT_NODE_TYPES[child.type]))
|
|
728
1310
|
return null;
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
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)
|
|
732
1335
|
return null;
|
|
733
|
-
|
|
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;
|
|
734
1419
|
}
|
|
735
1420
|
function loadSource(file, cache) {
|
|
736
1421
|
const cached = cache.get(file);
|
|
@@ -746,20 +1431,6 @@ function loadSource(file, cache) {
|
|
|
746
1431
|
return "";
|
|
747
1432
|
}
|
|
748
1433
|
}
|
|
749
|
-
function memberIdLooksStatic(memberId, sourceCache) {
|
|
750
|
-
const parsed = parseDefNodeId(memberId);
|
|
751
|
-
if (!parsed)
|
|
752
|
-
return false;
|
|
753
|
-
const source = loadSource(parsed.file, sourceCache);
|
|
754
|
-
if (!source)
|
|
755
|
-
return false;
|
|
756
|
-
const beforeBrace = source.lastIndexOf("{", parsed.index);
|
|
757
|
-
const beforeSemi = source.lastIndexOf(";", parsed.index);
|
|
758
|
-
const beforeClose = source.lastIndexOf("}", parsed.index);
|
|
759
|
-
const declStart = Math.max(beforeBrace, beforeSemi, beforeClose);
|
|
760
|
-
const prefix = source.slice(declStart + 1, parsed.index);
|
|
761
|
-
return /(?:^|[^\w$])static(?:$|[^\w$])/.test(prefix) || /(?:^|[^\w$])def\s+self\s*\./.test(prefix);
|
|
762
|
-
}
|
|
763
1434
|
function inferCallMemberScope(site, sourceCache) {
|
|
764
1435
|
const source = loadSource(site.file, sourceCache);
|
|
765
1436
|
if (!source)
|
|
@@ -774,22 +1445,25 @@ function inferCallMemberScope(site, sourceCache) {
|
|
|
774
1445
|
return "instance";
|
|
775
1446
|
if (/\)\s*\.\s*$/.test(before))
|
|
776
1447
|
return "instance";
|
|
777
|
-
//
|
|
778
|
-
// 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.
|
|
779
1449
|
if (/(?:^|[^A-Za-z0-9_$])[a-z_][A-Za-z0-9_]*\s*\.\s*$/.test(before))
|
|
780
1450
|
return "instance";
|
|
781
1451
|
if (/(?:^|[^A-Za-z0-9_$])[A-Z][A-Za-z0-9_]*\s*\.\s*$/.test(before))
|
|
782
1452
|
return "static";
|
|
783
1453
|
return "any";
|
|
784
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
|
+
}
|
|
785
1459
|
/**
|
|
786
1460
|
* Records proven `calls` edges for deferred receiver invocations.
|
|
787
1461
|
* Ambiguous names at a level stop the walk.
|
|
788
1462
|
* `super`/`base`/`parent` follow class `extends` ancestors only.
|
|
789
1463
|
*/
|
|
790
|
-
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) {
|
|
791
1465
|
if (!candidates.length)
|
|
792
|
-
return;
|
|
1466
|
+
return [];
|
|
793
1467
|
const membersByOwner = new Map();
|
|
794
1468
|
const ownerByMember = new Map();
|
|
795
1469
|
const supertypesByOwner = new Map();
|
|
@@ -814,27 +1488,75 @@ export function emitReceiverCallEdges(graph, candidates, recordEdge) {
|
|
|
814
1488
|
if (label === "extends")
|
|
815
1489
|
pushUnique(classAncestorsByOwner, edge.from, edge.to);
|
|
816
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
|
+
}
|
|
817
1496
|
const nextOwners = (ownerId, viaSupertypes) => {
|
|
818
1497
|
if (!viaSupertypes)
|
|
819
1498
|
return supertypesByOwner.get(ownerId) ?? [];
|
|
820
1499
|
return (classAncestorsByOwner.get(ownerId) ?? []).filter((id) => graph.nodes.get(id)?.kind === "class");
|
|
821
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();
|
|
822
1513
|
const sourceCache = new Map();
|
|
823
1514
|
for (const candidate of candidates) {
|
|
824
|
-
const
|
|
825
|
-
|
|
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)
|
|
826
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;
|
|
827
1527
|
const memberScope = candidate.memberScope ?? inferCallMemberScope(candidate.site, sourceCache);
|
|
828
1528
|
let level = candidate.viaSupertypes ? nextOwners(owner, true) : [owner];
|
|
829
1529
|
const visited = new Set(level);
|
|
1530
|
+
let receiverDisposition = "none";
|
|
830
1531
|
for (let depth = 0; depth < MAX_SUPERTYPE_DEPTH && level.length; depth += 1) {
|
|
831
|
-
const lookup = provenMemberTarget(graph, membersByOwner, level, candidate, memberScope,
|
|
1532
|
+
const lookup = provenMemberTarget(graph, membersByOwner, level, candidate, memberScope, memberScopes, nodeAliases, memberArities, fileHiddenMemberIds, candidate.site.file);
|
|
832
1533
|
if (lookup.status === "unique") {
|
|
833
|
-
|
|
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
|
+
}
|
|
834
1552
|
break;
|
|
835
1553
|
}
|
|
836
|
-
if (lookup.status === "ambiguous")
|
|
1554
|
+
if (lookup.status === "ambiguous") {
|
|
1555
|
+
receiverDisposition = "ambiguous";
|
|
1556
|
+
if (existingTargets.size)
|
|
1557
|
+
rejectedCallSites.add(siteKey);
|
|
837
1558
|
break;
|
|
1559
|
+
}
|
|
838
1560
|
const next = [];
|
|
839
1561
|
for (const ownerId of level) {
|
|
840
1562
|
for (const supertype of nextOwners(ownerId, candidate.viaSupertypes)) {
|
|
@@ -846,7 +1568,46 @@ export function emitReceiverCallEdges(graph, candidates, recordEdge) {
|
|
|
846
1568
|
}
|
|
847
1569
|
level = next;
|
|
848
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);
|
|
849
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));
|
|
850
1611
|
}
|
|
851
1612
|
/**
|
|
852
1613
|
* The single callable member named by `candidate` across `owners`.
|
|
@@ -854,27 +1615,46 @@ export function emitReceiverCallEdges(graph, candidates, recordEdge) {
|
|
|
854
1615
|
* `ambiguous` means this depth matched the name but could not prove one member,
|
|
855
1616
|
* including arity ambiguity, and the walk must stop.
|
|
856
1617
|
*/
|
|
857
|
-
function provenMemberTarget(graph, membersByOwner, owners, candidate, memberScope,
|
|
1618
|
+
function provenMemberTarget(graph, membersByOwner, owners, candidate, memberScope, memberScopes, nodeAliases, memberArities, fileHiddenMemberIds, useFile) {
|
|
858
1619
|
const matches = new Set();
|
|
859
1620
|
for (const ownerId of owners) {
|
|
860
1621
|
for (const memberId of membersByOwner.get(ownerId) ?? []) {
|
|
861
|
-
const
|
|
862
|
-
|
|
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))
|
|
863
1625
|
continue;
|
|
864
|
-
if (
|
|
1626
|
+
if ((fileHiddenMemberIds.has(memberId) || fileHiddenMemberIds.has(canonicalId)) &&
|
|
1627
|
+
fileIdentityKey(node.file) !== fileIdentityKey(useFile)) {
|
|
865
1628
|
continue;
|
|
866
|
-
|
|
1629
|
+
}
|
|
1630
|
+
const nameMatches = candidate.caseInsensitiveMemberName
|
|
1631
|
+
? foldPhpIdentifierCase(node.name) === foldPhpIdentifierCase(candidate.memberName)
|
|
1632
|
+
: node.name === candidate.memberName;
|
|
1633
|
+
if (!nameMatches)
|
|
867
1634
|
continue;
|
|
868
|
-
|
|
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);
|
|
869
1644
|
}
|
|
870
1645
|
}
|
|
871
1646
|
if (!matches.size)
|
|
872
1647
|
return { status: "none" };
|
|
873
1648
|
if (matches.size === 1) {
|
|
874
1649
|
const [memberId] = matches;
|
|
875
|
-
|
|
1650
|
+
if (memberArityMatches(memberId, candidate.argumentCount, memberArities)) {
|
|
1651
|
+
return { status: "unique", memberId: memberId };
|
|
1652
|
+
}
|
|
1653
|
+
return { status: "ambiguous" };
|
|
876
1654
|
}
|
|
877
|
-
const byArity =
|
|
1655
|
+
const byArity = candidate.argumentCount === null
|
|
1656
|
+
? []
|
|
1657
|
+
: [...matches].filter((memberId) => memberArityMatches(memberId, candidate.argumentCount, memberArities));
|
|
878
1658
|
if (byArity.length === 1)
|
|
879
1659
|
return { status: "unique", memberId: byArity[0] };
|
|
880
1660
|
return { status: "ambiguous" };
|