arceus-s 1.6.4
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 +395 -0
- package/dist/_shared/graph/types.d.ts +81 -0
- package/dist/_shared/graph/types.d.ts.map +1 -0
- package/dist/_shared/graph/types.js +8 -0
- package/dist/_shared/graph/types.js.map +1 -0
- package/dist/_shared/index.d.ts +63 -0
- package/dist/_shared/index.d.ts.map +1 -0
- package/dist/_shared/index.js +48 -0
- package/dist/_shared/index.js.map +1 -0
- package/dist/_shared/integrations/circuit-breaker.d.ts +183 -0
- package/dist/_shared/integrations/circuit-breaker.d.ts.map +1 -0
- package/dist/_shared/integrations/circuit-breaker.js +236 -0
- package/dist/_shared/integrations/circuit-breaker.js.map +1 -0
- package/dist/_shared/integrations/resilient-fetch.d.ts +99 -0
- package/dist/_shared/integrations/resilient-fetch.d.ts.map +1 -0
- package/dist/_shared/integrations/resilient-fetch.js +204 -0
- package/dist/_shared/integrations/resilient-fetch.js.map +1 -0
- package/dist/_shared/integrations/retry.d.ts +60 -0
- package/dist/_shared/integrations/retry.d.ts.map +1 -0
- package/dist/_shared/integrations/retry.js +67 -0
- package/dist/_shared/integrations/retry.js.map +1 -0
- package/dist/_shared/integrations/understand-quickly.d.ts +77 -0
- package/dist/_shared/integrations/understand-quickly.d.ts.map +1 -0
- package/dist/_shared/integrations/understand-quickly.js +176 -0
- package/dist/_shared/integrations/understand-quickly.js.map +1 -0
- package/dist/_shared/language-detection.d.ts +23 -0
- package/dist/_shared/language-detection.d.ts.map +1 -0
- package/dist/_shared/language-detection.js +139 -0
- package/dist/_shared/language-detection.js.map +1 -0
- package/dist/_shared/languages.d.ts +26 -0
- package/dist/_shared/languages.d.ts.map +1 -0
- package/dist/_shared/languages.js +27 -0
- package/dist/_shared/languages.js.map +1 -0
- package/dist/_shared/lbug/schema-constants.d.ts +16 -0
- package/dist/_shared/lbug/schema-constants.d.ts.map +1 -0
- package/dist/_shared/lbug/schema-constants.js +67 -0
- package/dist/_shared/lbug/schema-constants.js.map +1 -0
- package/dist/_shared/mro-strategy.d.ts +41 -0
- package/dist/_shared/mro-strategy.d.ts.map +1 -0
- package/dist/_shared/mro-strategy.js +2 -0
- package/dist/_shared/mro-strategy.js.map +1 -0
- package/dist/_shared/pipeline.d.ts +16 -0
- package/dist/_shared/pipeline.d.ts.map +1 -0
- package/dist/_shared/pipeline.js +5 -0
- package/dist/_shared/pipeline.js.map +1 -0
- package/dist/_shared/scope-resolution/def-index.d.ts +36 -0
- package/dist/_shared/scope-resolution/def-index.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/def-index.js +51 -0
- package/dist/_shared/scope-resolution/def-index.js.map +1 -0
- package/dist/_shared/scope-resolution/evidence-weights.d.ts +69 -0
- package/dist/_shared/scope-resolution/evidence-weights.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/evidence-weights.js +84 -0
- package/dist/_shared/scope-resolution/evidence-weights.js.map +1 -0
- package/dist/_shared/scope-resolution/finalize-algorithm.d.ts +149 -0
- package/dist/_shared/scope-resolution/finalize-algorithm.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/finalize-algorithm.js +795 -0
- package/dist/_shared/scope-resolution/finalize-algorithm.js.map +1 -0
- package/dist/_shared/scope-resolution/language-classification.d.ts +26 -0
- package/dist/_shared/scope-resolution/language-classification.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/language-classification.js +44 -0
- package/dist/_shared/scope-resolution/language-classification.js.map +1 -0
- package/dist/_shared/scope-resolution/method-dispatch-index.d.ts +106 -0
- package/dist/_shared/scope-resolution/method-dispatch-index.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/method-dispatch-index.js +98 -0
- package/dist/_shared/scope-resolution/method-dispatch-index.js.map +1 -0
- package/dist/_shared/scope-resolution/module-scope-index.d.ts +46 -0
- package/dist/_shared/scope-resolution/module-scope-index.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/module-scope-index.js +58 -0
- package/dist/_shared/scope-resolution/module-scope-index.js.map +1 -0
- package/dist/_shared/scope-resolution/origin-priority.d.ts +14 -0
- package/dist/_shared/scope-resolution/origin-priority.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/origin-priority.js +21 -0
- package/dist/_shared/scope-resolution/origin-priority.js.map +1 -0
- package/dist/_shared/scope-resolution/parsed-file.d.ts +76 -0
- package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/parsed-file.js +54 -0
- package/dist/_shared/scope-resolution/parsed-file.js.map +1 -0
- package/dist/_shared/scope-resolution/position-index.d.ts +62 -0
- package/dist/_shared/scope-resolution/position-index.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/position-index.js +134 -0
- package/dist/_shared/scope-resolution/position-index.js.map +1 -0
- package/dist/_shared/scope-resolution/qualified-name-index.d.ts +44 -0
- package/dist/_shared/scope-resolution/qualified-name-index.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/qualified-name-index.js +75 -0
- package/dist/_shared/scope-resolution/qualified-name-index.js.map +1 -0
- package/dist/_shared/scope-resolution/reference-site.d.ts +75 -0
- package/dist/_shared/scope-resolution/reference-site.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/reference-site.js +24 -0
- package/dist/_shared/scope-resolution/reference-site.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/class-registry.d.ts +27 -0
- package/dist/_shared/scope-resolution/registries/class-registry.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/class-registry.js +30 -0
- package/dist/_shared/scope-resolution/registries/class-registry.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/context.d.ts +69 -0
- package/dist/_shared/scope-resolution/registries/context.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/context.js +44 -0
- package/dist/_shared/scope-resolution/registries/context.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/evidence.d.ts +56 -0
- package/dist/_shared/scope-resolution/registries/evidence.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/evidence.js +150 -0
- package/dist/_shared/scope-resolution/registries/evidence.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/field-registry.d.ts +26 -0
- package/dist/_shared/scope-resolution/registries/field-registry.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/field-registry.js +31 -0
- package/dist/_shared/scope-resolution/registries/field-registry.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/lookup-core.d.ts +81 -0
- package/dist/_shared/scope-resolution/registries/lookup-core.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/lookup-core.js +349 -0
- package/dist/_shared/scope-resolution/registries/lookup-core.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/lookup-qualified.d.ts +33 -0
- package/dist/_shared/scope-resolution/registries/lookup-qualified.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/lookup-qualified.js +56 -0
- package/dist/_shared/scope-resolution/registries/lookup-qualified.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/method-registry.d.ts +36 -0
- package/dist/_shared/scope-resolution/registries/method-registry.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/method-registry.js +32 -0
- package/dist/_shared/scope-resolution/registries/method-registry.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/tie-breaks.d.ts +43 -0
- package/dist/_shared/scope-resolution/registries/tie-breaks.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/tie-breaks.js +60 -0
- package/dist/_shared/scope-resolution/registries/tie-breaks.js.map +1 -0
- package/dist/_shared/scope-resolution/resolve-type-ref.d.ts +53 -0
- package/dist/_shared/scope-resolution/resolve-type-ref.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/resolve-type-ref.js +126 -0
- package/dist/_shared/scope-resolution/resolve-type-ref.js.map +1 -0
- package/dist/_shared/scope-resolution/scope-id.d.ts +43 -0
- package/dist/_shared/scope-resolution/scope-id.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/scope-id.js +46 -0
- package/dist/_shared/scope-resolution/scope-id.js.map +1 -0
- package/dist/_shared/scope-resolution/scope-tree.d.ts +83 -0
- package/dist/_shared/scope-resolution/scope-tree.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/scope-tree.js +220 -0
- package/dist/_shared/scope-resolution/scope-tree.js.map +1 -0
- package/dist/_shared/scope-resolution/shadow/aggregate.d.ts +63 -0
- package/dist/_shared/scope-resolution/shadow/aggregate.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/shadow/aggregate.js +122 -0
- package/dist/_shared/scope-resolution/shadow/aggregate.js.map +1 -0
- package/dist/_shared/scope-resolution/shadow/diff.d.ts +59 -0
- package/dist/_shared/scope-resolution/shadow/diff.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/shadow/diff.js +79 -0
- package/dist/_shared/scope-resolution/shadow/diff.js.map +1 -0
- package/dist/_shared/scope-resolution/symbol-definition.d.ts +34 -0
- package/dist/_shared/scope-resolution/symbol-definition.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/symbol-definition.js +12 -0
- package/dist/_shared/scope-resolution/symbol-definition.js.map +1 -0
- package/dist/_shared/scope-resolution/types.d.ts +400 -0
- package/dist/_shared/scope-resolution/types.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/types.js +25 -0
- package/dist/_shared/scope-resolution/types.js.map +1 -0
- package/dist/_shared/test-helpers.d.ts +13 -0
- package/dist/_shared/test-helpers.d.ts.map +1 -0
- package/dist/_shared/test-helpers.js +13 -0
- package/dist/_shared/test-helpers.js.map +1 -0
- package/dist/cli/ai-context.d.ts +28 -0
- package/dist/cli/ai-context.js +289 -0
- package/dist/cli/analyze.d.ts +80 -0
- package/dist/cli/analyze.js +525 -0
- package/dist/cli/augment.d.ts +13 -0
- package/dist/cli/augment.js +33 -0
- package/dist/cli/clean.d.ts +10 -0
- package/dist/cli/clean.js +79 -0
- package/dist/cli/cli-message.d.ts +16 -0
- package/dist/cli/cli-message.js +71 -0
- package/dist/cli/doctor.d.ts +1 -0
- package/dist/cli/doctor.js +31 -0
- package/dist/cli/eval-server.d.ts +37 -0
- package/dist/cli/eval-server.js +423 -0
- package/dist/cli/group.d.ts +1 -0
- package/dist/cli/group.js +323 -0
- package/dist/cli/index-repo.d.ts +15 -0
- package/dist/cli/index-repo.js +120 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +492 -0
- package/dist/cli/lazy-action.d.ts +6 -0
- package/dist/cli/lazy-action.js +18 -0
- package/dist/cli/list.d.ts +6 -0
- package/dist/cli/list.js +40 -0
- package/dist/cli/mcp.d.ts +29 -0
- package/dist/cli/mcp.js +73 -0
- package/dist/cli/optional-grammars.d.ts +47 -0
- package/dist/cli/optional-grammars.js +87 -0
- package/dist/cli/publish.d.ts +29 -0
- package/dist/cli/publish.js +174 -0
- package/dist/cli/remove.d.ts +30 -0
- package/dist/cli/remove.js +102 -0
- package/dist/cli/serve.d.ts +4 -0
- package/dist/cli/serve.js +55 -0
- package/dist/cli/setup.d.ts +8 -0
- package/dist/cli/setup.js +642 -0
- package/dist/cli/skill-gen.d.ts +26 -0
- package/dist/cli/skill-gen.js +555 -0
- package/dist/cli/status.d.ts +6 -0
- package/dist/cli/status.js +36 -0
- package/dist/cli/tool.d.ts +43 -0
- package/dist/cli/tool.js +169 -0
- package/dist/cli/wiki.d.ts +23 -0
- package/dist/cli/wiki.js +616 -0
- package/dist/config/ignore-service.d.ts +35 -0
- package/dist/config/ignore-service.js +439 -0
- package/dist/config/supported-languages.d.ts +13 -0
- package/dist/config/supported-languages.js +13 -0
- package/dist/core/augmentation/engine.d.ts +26 -0
- package/dist/core/augmentation/engine.js +272 -0
- package/dist/core/embedding-mode.d.ts +51 -0
- package/dist/core/embedding-mode.js +48 -0
- package/dist/core/embeddings/ast-utils.d.ts +22 -0
- package/dist/core/embeddings/ast-utils.js +106 -0
- package/dist/core/embeddings/character-chunk.d.ts +12 -0
- package/dist/core/embeddings/character-chunk.js +43 -0
- package/dist/core/embeddings/chunker.d.ts +14 -0
- package/dist/core/embeddings/chunker.js +239 -0
- package/dist/core/embeddings/config.d.ts +2 -0
- package/dist/core/embeddings/config.js +36 -0
- package/dist/core/embeddings/embedder.d.ts +65 -0
- package/dist/core/embeddings/embedder.js +349 -0
- package/dist/core/embeddings/embedding-pipeline.d.ts +68 -0
- package/dist/core/embeddings/embedding-pipeline.js +552 -0
- package/dist/core/embeddings/exact-search.d.ts +15 -0
- package/dist/core/embeddings/exact-search.js +27 -0
- package/dist/core/embeddings/hf-env.d.ts +135 -0
- package/dist/core/embeddings/hf-env.js +232 -0
- package/dist/core/embeddings/http-client.d.ts +37 -0
- package/dist/core/embeddings/http-client.js +199 -0
- package/dist/core/embeddings/index.d.ts +10 -0
- package/dist/core/embeddings/index.js +10 -0
- package/dist/core/embeddings/line-index.d.ts +7 -0
- package/dist/core/embeddings/line-index.js +42 -0
- package/dist/core/embeddings/server-mapping.d.ts +15 -0
- package/dist/core/embeddings/server-mapping.js +33 -0
- package/dist/core/embeddings/structural-extractor.d.ts +15 -0
- package/dist/core/embeddings/structural-extractor.js +58 -0
- package/dist/core/embeddings/text-generator.d.ts +31 -0
- package/dist/core/embeddings/text-generator.js +208 -0
- package/dist/core/embeddings/types.d.ts +211 -0
- package/dist/core/embeddings/types.js +202 -0
- package/dist/core/git-staleness.d.ts +37 -0
- package/dist/core/git-staleness.js +167 -0
- package/dist/core/graph/graph.d.ts +2 -0
- package/dist/core/graph/graph.js +173 -0
- package/dist/core/graph/types.d.ts +36 -0
- package/dist/core/graph/types.js +1 -0
- package/dist/core/group/bridge-db.d.ts +82 -0
- package/dist/core/group/bridge-db.js +609 -0
- package/dist/core/group/bridge-schema.d.ts +27 -0
- package/dist/core/group/bridge-schema.js +55 -0
- package/dist/core/group/config-parser.d.ts +7 -0
- package/dist/core/group/config-parser.js +122 -0
- package/dist/core/group/contract-extractor.d.ts +7 -0
- package/dist/core/group/contract-extractor.js +1 -0
- package/dist/core/group/cross-impact.d.ts +85 -0
- package/dist/core/group/cross-impact.js +515 -0
- package/dist/core/group/extractors/elixir-workspace-extractor.d.ts +15 -0
- package/dist/core/group/extractors/elixir-workspace-extractor.js +204 -0
- package/dist/core/group/extractors/fs-utils.d.ts +10 -0
- package/dist/core/group/extractors/fs-utils.js +24 -0
- package/dist/core/group/extractors/go-workspace-extractor.d.ts +14 -0
- package/dist/core/group/extractors/go-workspace-extractor.js +217 -0
- package/dist/core/group/extractors/grpc-extractor.d.ts +25 -0
- package/dist/core/group/extractors/grpc-extractor.js +414 -0
- package/dist/core/group/extractors/grpc-patterns/go.d.ts +2 -0
- package/dist/core/group/extractors/grpc-patterns/go.js +97 -0
- package/dist/core/group/extractors/grpc-patterns/index.d.ts +19 -0
- package/dist/core/group/extractors/grpc-patterns/index.js +46 -0
- package/dist/core/group/extractors/grpc-patterns/java.d.ts +2 -0
- package/dist/core/group/extractors/grpc-patterns/java.js +173 -0
- package/dist/core/group/extractors/grpc-patterns/node.d.ts +4 -0
- package/dist/core/group/extractors/grpc-patterns/node.js +290 -0
- package/dist/core/group/extractors/grpc-patterns/proto.d.ts +9 -0
- package/dist/core/group/extractors/grpc-patterns/proto.js +134 -0
- package/dist/core/group/extractors/grpc-patterns/python.d.ts +2 -0
- package/dist/core/group/extractors/grpc-patterns/python.js +67 -0
- package/dist/core/group/extractors/grpc-patterns/types.d.ts +50 -0
- package/dist/core/group/extractors/grpc-patterns/types.js +1 -0
- package/dist/core/group/extractors/http-patterns/go.d.ts +2 -0
- package/dist/core/group/extractors/http-patterns/go.js +215 -0
- package/dist/core/group/extractors/http-patterns/index.d.ts +17 -0
- package/dist/core/group/extractors/http-patterns/index.js +44 -0
- package/dist/core/group/extractors/http-patterns/java.d.ts +2 -0
- package/dist/core/group/extractors/http-patterns/java.js +253 -0
- package/dist/core/group/extractors/http-patterns/node.d.ts +4 -0
- package/dist/core/group/extractors/http-patterns/node.js +484 -0
- package/dist/core/group/extractors/http-patterns/php.d.ts +2 -0
- package/dist/core/group/extractors/http-patterns/php.js +178 -0
- package/dist/core/group/extractors/http-patterns/python.d.ts +2 -0
- package/dist/core/group/extractors/http-patterns/python.js +308 -0
- package/dist/core/group/extractors/http-patterns/types.d.ts +61 -0
- package/dist/core/group/extractors/http-patterns/types.js +1 -0
- package/dist/core/group/extractors/http-route-extractor.d.ts +21 -0
- package/dist/core/group/extractors/http-route-extractor.js +430 -0
- package/dist/core/group/extractors/include-extractor.d.ts +39 -0
- package/dist/core/group/extractors/include-extractor.js +566 -0
- package/dist/core/group/extractors/java-workspace-extractor.d.ts +16 -0
- package/dist/core/group/extractors/java-workspace-extractor.js +204 -0
- package/dist/core/group/extractors/manifest-extractor.d.ts +54 -0
- package/dist/core/group/extractors/manifest-extractor.js +320 -0
- package/dist/core/group/extractors/node-workspace-extractor.d.ts +14 -0
- package/dist/core/group/extractors/node-workspace-extractor.js +207 -0
- package/dist/core/group/extractors/python-workspace-extractor.d.ts +15 -0
- package/dist/core/group/extractors/python-workspace-extractor.js +205 -0
- package/dist/core/group/extractors/rust-workspace-extractor.d.ts +44 -0
- package/dist/core/group/extractors/rust-workspace-extractor.js +240 -0
- package/dist/core/group/extractors/thrift-extractor.d.ts +22 -0
- package/dist/core/group/extractors/thrift-extractor.js +283 -0
- package/dist/core/group/extractors/thrift-patterns/index.d.ts +4 -0
- package/dist/core/group/extractors/thrift-patterns/index.js +10 -0
- package/dist/core/group/extractors/thrift-patterns/java.d.ts +2 -0
- package/dist/core/group/extractors/thrift-patterns/java.js +220 -0
- package/dist/core/group/extractors/thrift-patterns/types.d.ts +17 -0
- package/dist/core/group/extractors/thrift-patterns/types.js +1 -0
- package/dist/core/group/extractors/topic-extractor.d.ts +8 -0
- package/dist/core/group/extractors/topic-extractor.js +97 -0
- package/dist/core/group/extractors/topic-patterns/go.d.ts +2 -0
- package/dist/core/group/extractors/topic-patterns/go.js +120 -0
- package/dist/core/group/extractors/topic-patterns/index.d.ts +14 -0
- package/dist/core/group/extractors/topic-patterns/index.js +38 -0
- package/dist/core/group/extractors/topic-patterns/java.d.ts +2 -0
- package/dist/core/group/extractors/topic-patterns/java.js +80 -0
- package/dist/core/group/extractors/topic-patterns/node.d.ts +4 -0
- package/dist/core/group/extractors/topic-patterns/node.js +155 -0
- package/dist/core/group/extractors/topic-patterns/python.d.ts +2 -0
- package/dist/core/group/extractors/topic-patterns/python.js +116 -0
- package/dist/core/group/extractors/topic-patterns/types.d.ts +25 -0
- package/dist/core/group/extractors/topic-patterns/types.js +10 -0
- package/dist/core/group/extractors/tree-sitter-scanner.d.ts +113 -0
- package/dist/core/group/extractors/tree-sitter-scanner.js +95 -0
- package/dist/core/group/extractors/workspace-extractor.d.ts +13 -0
- package/dist/core/group/extractors/workspace-extractor.js +65 -0
- package/dist/core/group/group-path-utils.d.ts +17 -0
- package/dist/core/group/group-path-utils.js +40 -0
- package/dist/core/group/matching.d.ts +13 -0
- package/dist/core/group/matching.js +284 -0
- package/dist/core/group/normalization.d.ts +3 -0
- package/dist/core/group/normalization.js +115 -0
- package/dist/core/group/resolve-at-member.d.ts +10 -0
- package/dist/core/group/resolve-at-member.js +31 -0
- package/dist/core/group/service-boundary-detector.d.ts +8 -0
- package/dist/core/group/service-boundary-detector.js +155 -0
- package/dist/core/group/service.d.ts +56 -0
- package/dist/core/group/service.js +395 -0
- package/dist/core/group/storage.d.ts +9 -0
- package/dist/core/group/storage.js +157 -0
- package/dist/core/group/sync.d.ts +21 -0
- package/dist/core/group/sync.js +267 -0
- package/dist/core/group/types.d.ts +181 -0
- package/dist/core/group/types.js +1 -0
- package/dist/core/incremental/shadow-candidates.d.ts +44 -0
- package/dist/core/incremental/shadow-candidates.js +74 -0
- package/dist/core/incremental/subgraph-extract.d.ts +64 -0
- package/dist/core/incremental/subgraph-extract.js +111 -0
- package/dist/core/ingestion/ast-cache.d.ts +26 -0
- package/dist/core/ingestion/ast-cache.js +48 -0
- package/dist/core/ingestion/binding-accumulator.d.ts +212 -0
- package/dist/core/ingestion/binding-accumulator.js +336 -0
- package/dist/core/ingestion/call-extractors/configs/c-cpp.d.ts +3 -0
- package/dist/core/ingestion/call-extractors/configs/c-cpp.js +8 -0
- package/dist/core/ingestion/call-extractors/configs/csharp.d.ts +2 -0
- package/dist/core/ingestion/call-extractors/configs/csharp.js +6 -0
- package/dist/core/ingestion/call-extractors/configs/dart.d.ts +2 -0
- package/dist/core/ingestion/call-extractors/configs/dart.js +5 -0
- package/dist/core/ingestion/call-extractors/configs/go.d.ts +2 -0
- package/dist/core/ingestion/call-extractors/configs/go.js +5 -0
- package/dist/core/ingestion/call-extractors/configs/jvm.d.ts +3 -0
- package/dist/core/ingestion/call-extractors/configs/jvm.js +51 -0
- package/dist/core/ingestion/call-extractors/configs/php.d.ts +2 -0
- package/dist/core/ingestion/call-extractors/configs/php.js +5 -0
- package/dist/core/ingestion/call-extractors/configs/python.d.ts +2 -0
- package/dist/core/ingestion/call-extractors/configs/python.js +5 -0
- package/dist/core/ingestion/call-extractors/configs/ruby.d.ts +2 -0
- package/dist/core/ingestion/call-extractors/configs/ruby.js +5 -0
- package/dist/core/ingestion/call-extractors/configs/rust.d.ts +2 -0
- package/dist/core/ingestion/call-extractors/configs/rust.js +5 -0
- package/dist/core/ingestion/call-extractors/configs/swift.d.ts +2 -0
- package/dist/core/ingestion/call-extractors/configs/swift.js +5 -0
- package/dist/core/ingestion/call-extractors/configs/typescript-javascript.d.ts +3 -0
- package/dist/core/ingestion/call-extractors/configs/typescript-javascript.js +8 -0
- package/dist/core/ingestion/call-extractors/generic.d.ts +5 -0
- package/dist/core/ingestion/call-extractors/generic.js +59 -0
- package/dist/core/ingestion/call-processor.d.ts +235 -0
- package/dist/core/ingestion/call-processor.js +2754 -0
- package/dist/core/ingestion/call-routing.d.ts +55 -0
- package/dist/core/ingestion/call-routing.js +95 -0
- package/dist/core/ingestion/call-types.d.ts +135 -0
- package/dist/core/ingestion/call-types.js +2 -0
- package/dist/core/ingestion/class-extractors/configs/c-cpp.d.ts +3 -0
- package/dist/core/ingestion/class-extractors/configs/c-cpp.js +11 -0
- package/dist/core/ingestion/class-extractors/configs/csharp.d.ts +2 -0
- package/dist/core/ingestion/class-extractors/configs/csharp.js +21 -0
- package/dist/core/ingestion/class-extractors/configs/dart.d.ts +2 -0
- package/dist/core/ingestion/class-extractors/configs/dart.js +7 -0
- package/dist/core/ingestion/class-extractors/configs/go.d.ts +2 -0
- package/dist/core/ingestion/class-extractors/configs/go.js +20 -0
- package/dist/core/ingestion/class-extractors/configs/jvm.d.ts +3 -0
- package/dist/core/ingestion/class-extractors/configs/jvm.js +35 -0
- package/dist/core/ingestion/class-extractors/configs/php.d.ts +2 -0
- package/dist/core/ingestion/class-extractors/configs/php.js +7 -0
- package/dist/core/ingestion/class-extractors/configs/python.d.ts +2 -0
- package/dist/core/ingestion/class-extractors/configs/python.js +7 -0
- package/dist/core/ingestion/class-extractors/configs/ruby.d.ts +2 -0
- package/dist/core/ingestion/class-extractors/configs/ruby.js +7 -0
- package/dist/core/ingestion/class-extractors/configs/rust.d.ts +2 -0
- package/dist/core/ingestion/class-extractors/configs/rust.js +7 -0
- package/dist/core/ingestion/class-extractors/configs/swift.d.ts +2 -0
- package/dist/core/ingestion/class-extractors/configs/swift.js +18 -0
- package/dist/core/ingestion/class-extractors/configs/typescript-javascript.d.ts +4 -0
- package/dist/core/ingestion/class-extractors/configs/typescript-javascript.js +28 -0
- package/dist/core/ingestion/class-extractors/generic.d.ts +2 -0
- package/dist/core/ingestion/class-extractors/generic.js +135 -0
- package/dist/core/ingestion/class-types.d.ts +34 -0
- package/dist/core/ingestion/class-types.js +1 -0
- package/dist/core/ingestion/cluster-enricher.d.ts +38 -0
- package/dist/core/ingestion/cluster-enricher.js +169 -0
- package/dist/core/ingestion/cobol/cobol-copy-expander.d.ts +57 -0
- package/dist/core/ingestion/cobol/cobol-copy-expander.js +376 -0
- package/dist/core/ingestion/cobol/cobol-preprocessor.d.ts +212 -0
- package/dist/core/ingestion/cobol/cobol-preprocessor.js +1727 -0
- package/dist/core/ingestion/cobol/jcl-parser.d.ts +68 -0
- package/dist/core/ingestion/cobol/jcl-parser.js +217 -0
- package/dist/core/ingestion/cobol/jcl-processor.d.ts +33 -0
- package/dist/core/ingestion/cobol/jcl-processor.js +229 -0
- package/dist/core/ingestion/cobol-processor.d.ts +54 -0
- package/dist/core/ingestion/cobol-processor.js +1232 -0
- package/dist/core/ingestion/community-processor.d.ts +39 -0
- package/dist/core/ingestion/community-processor.js +336 -0
- package/dist/core/ingestion/constants.d.ts +17 -0
- package/dist/core/ingestion/constants.js +21 -0
- package/dist/core/ingestion/cpp-ue-preprocessor.d.ts +12 -0
- package/dist/core/ingestion/cpp-ue-preprocessor.js +260 -0
- package/dist/core/ingestion/emit-references.d.ts +88 -0
- package/dist/core/ingestion/emit-references.js +229 -0
- package/dist/core/ingestion/entry-point-scoring.d.ts +40 -0
- package/dist/core/ingestion/entry-point-scoring.js +196 -0
- package/dist/core/ingestion/export-detection.d.ts +57 -0
- package/dist/core/ingestion/export-detection.js +233 -0
- package/dist/core/ingestion/field-extractor.d.ts +29 -0
- package/dist/core/ingestion/field-extractor.js +25 -0
- package/dist/core/ingestion/field-extractors/configs/c-cpp.d.ts +3 -0
- package/dist/core/ingestion/field-extractors/configs/c-cpp.js +104 -0
- package/dist/core/ingestion/field-extractors/configs/csharp.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/csharp.js +121 -0
- package/dist/core/ingestion/field-extractors/configs/dart.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/dart.js +78 -0
- package/dist/core/ingestion/field-extractors/configs/go.d.ts +11 -0
- package/dist/core/ingestion/field-extractors/configs/go.js +60 -0
- package/dist/core/ingestion/field-extractors/configs/helpers.d.ts +53 -0
- package/dist/core/ingestion/field-extractors/configs/helpers.js +158 -0
- package/dist/core/ingestion/field-extractors/configs/jvm.d.ts +3 -0
- package/dist/core/ingestion/field-extractors/configs/jvm.js +118 -0
- package/dist/core/ingestion/field-extractors/configs/php.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/php.js +65 -0
- package/dist/core/ingestion/field-extractors/configs/python.d.ts +12 -0
- package/dist/core/ingestion/field-extractors/configs/python.js +91 -0
- package/dist/core/ingestion/field-extractors/configs/ruby.d.ts +16 -0
- package/dist/core/ingestion/field-extractors/configs/ruby.js +76 -0
- package/dist/core/ingestion/field-extractors/configs/rust.d.ts +9 -0
- package/dist/core/ingestion/field-extractors/configs/rust.js +52 -0
- package/dist/core/ingestion/field-extractors/configs/swift.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/swift.js +65 -0
- package/dist/core/ingestion/field-extractors/configs/typescript-javascript.d.ts +3 -0
- package/dist/core/ingestion/field-extractors/configs/typescript-javascript.js +56 -0
- package/dist/core/ingestion/field-extractors/generic.d.ts +49 -0
- package/dist/core/ingestion/field-extractors/generic.js +117 -0
- package/dist/core/ingestion/field-extractors/typescript.d.ts +77 -0
- package/dist/core/ingestion/field-extractors/typescript.js +291 -0
- package/dist/core/ingestion/field-types.d.ts +61 -0
- package/dist/core/ingestion/field-types.js +2 -0
- package/dist/core/ingestion/filesystem-walker.d.ts +28 -0
- package/dist/core/ingestion/filesystem-walker.js +92 -0
- package/dist/core/ingestion/finalize-orchestrator.d.ts +63 -0
- package/dist/core/ingestion/finalize-orchestrator.js +142 -0
- package/dist/core/ingestion/framework-detection.d.ts +30 -0
- package/dist/core/ingestion/framework-detection.js +428 -0
- package/dist/core/ingestion/heritage-extractors/configs/go.d.ts +13 -0
- package/dist/core/ingestion/heritage-extractors/configs/go.js +20 -0
- package/dist/core/ingestion/heritage-extractors/configs/ruby.d.ts +18 -0
- package/dist/core/ingestion/heritage-extractors/configs/ruby.js +65 -0
- package/dist/core/ingestion/heritage-extractors/generic.d.ts +23 -0
- package/dist/core/ingestion/heritage-extractors/generic.js +47 -0
- package/dist/core/ingestion/heritage-processor.d.ts +54 -0
- package/dist/core/ingestion/heritage-processor.js +367 -0
- package/dist/core/ingestion/heritage-types.d.ts +73 -0
- package/dist/core/ingestion/heritage-types.js +2 -0
- package/dist/core/ingestion/import-processor.d.ts +23 -0
- package/dist/core/ingestion/import-processor.js +377 -0
- package/dist/core/ingestion/import-resolvers/configs/c-cpp.d.ts +7 -0
- package/dist/core/ingestion/import-resolvers/configs/c-cpp.js +14 -0
- package/dist/core/ingestion/import-resolvers/configs/csharp.d.ts +8 -0
- package/dist/core/ingestion/import-resolvers/configs/csharp.js +27 -0
- package/dist/core/ingestion/import-resolvers/configs/dart.d.ts +17 -0
- package/dist/core/ingestion/import-resolvers/configs/dart.js +54 -0
- package/dist/core/ingestion/import-resolvers/configs/go.d.ts +8 -0
- package/dist/core/ingestion/import-resolvers/configs/go.js +26 -0
- package/dist/core/ingestion/import-resolvers/configs/jvm.d.ts +13 -0
- package/dist/core/ingestion/import-resolvers/configs/jvm.js +68 -0
- package/dist/core/ingestion/import-resolvers/configs/php.d.ts +8 -0
- package/dist/core/ingestion/import-resolvers/configs/php.js +15 -0
- package/dist/core/ingestion/import-resolvers/configs/python.d.ts +12 -0
- package/dist/core/ingestion/import-resolvers/configs/python.js +41 -0
- package/dist/core/ingestion/import-resolvers/configs/ruby.d.ts +8 -0
- package/dist/core/ingestion/import-resolvers/configs/ruby.js +16 -0
- package/dist/core/ingestion/import-resolvers/configs/rust.d.ts +8 -0
- package/dist/core/ingestion/import-resolvers/configs/rust.js +54 -0
- package/dist/core/ingestion/import-resolvers/configs/swift.d.ts +8 -0
- package/dist/core/ingestion/import-resolvers/configs/swift.js +29 -0
- package/dist/core/ingestion/import-resolvers/configs/typescript-javascript.d.ts +9 -0
- package/dist/core/ingestion/import-resolvers/configs/typescript-javascript.js +23 -0
- package/dist/core/ingestion/import-resolvers/csharp.d.ts +18 -0
- package/dist/core/ingestion/import-resolvers/csharp.js +115 -0
- package/dist/core/ingestion/import-resolvers/go.d.ts +17 -0
- package/dist/core/ingestion/import-resolvers/go.js +46 -0
- package/dist/core/ingestion/import-resolvers/jvm.d.ts +27 -0
- package/dist/core/ingestion/import-resolvers/jvm.js +106 -0
- package/dist/core/ingestion/import-resolvers/php.d.ts +24 -0
- package/dist/core/ingestion/import-resolvers/php.js +77 -0
- package/dist/core/ingestion/import-resolvers/python.d.ts +22 -0
- package/dist/core/ingestion/import-resolvers/python.js +72 -0
- package/dist/core/ingestion/import-resolvers/resolver-factory.d.ts +24 -0
- package/dist/core/ingestion/import-resolvers/resolver-factory.js +33 -0
- package/dist/core/ingestion/import-resolvers/ruby.d.ts +14 -0
- package/dist/core/ingestion/import-resolvers/ruby.js +17 -0
- package/dist/core/ingestion/import-resolvers/rust.d.ts +17 -0
- package/dist/core/ingestion/import-resolvers/rust.js +75 -0
- package/dist/core/ingestion/import-resolvers/standard.d.ts +35 -0
- package/dist/core/ingestion/import-resolvers/standard.js +168 -0
- package/dist/core/ingestion/import-resolvers/types.d.ts +68 -0
- package/dist/core/ingestion/import-resolvers/types.js +6 -0
- package/dist/core/ingestion/import-resolvers/utils.d.ts +35 -0
- package/dist/core/ingestion/import-resolvers/utils.js +153 -0
- package/dist/core/ingestion/import-target-adapter.d.ts +73 -0
- package/dist/core/ingestion/import-target-adapter.js +95 -0
- package/dist/core/ingestion/language-config.d.ts +52 -0
- package/dist/core/ingestion/language-config.js +182 -0
- package/dist/core/ingestion/language-provider.d.ts +465 -0
- package/dist/core/ingestion/language-provider.js +24 -0
- package/dist/core/ingestion/languages/c/arity-metadata.d.ts +14 -0
- package/dist/core/ingestion/languages/c/arity-metadata.js +94 -0
- package/dist/core/ingestion/languages/c/arity.d.ts +6 -0
- package/dist/core/ingestion/languages/c/arity.js +18 -0
- package/dist/core/ingestion/languages/c/captures.d.ts +2 -0
- package/dist/core/ingestion/languages/c/captures.js +105 -0
- package/dist/core/ingestion/languages/c/header-scan.d.ts +7 -0
- package/dist/core/ingestion/languages/c/header-scan.js +55 -0
- package/dist/core/ingestion/languages/c/import-decomposer.d.ts +8 -0
- package/dist/core/ingestion/languages/c/import-decomposer.js +50 -0
- package/dist/core/ingestion/languages/c/import-target.d.ts +14 -0
- package/dist/core/ingestion/languages/c/import-target.js +57 -0
- package/dist/core/ingestion/languages/c/index.d.ts +11 -0
- package/dist/core/ingestion/languages/c/index.js +11 -0
- package/dist/core/ingestion/languages/c/interpret.d.ts +14 -0
- package/dist/core/ingestion/languages/c/interpret.js +48 -0
- package/dist/core/ingestion/languages/c/merge-bindings.d.ts +7 -0
- package/dist/core/ingestion/languages/c/merge-bindings.js +23 -0
- package/dist/core/ingestion/languages/c/query.d.ts +3 -0
- package/dist/core/ingestion/languages/c/query.js +161 -0
- package/dist/core/ingestion/languages/c/scope-resolver.d.ts +13 -0
- package/dist/core/ingestion/languages/c/scope-resolver.js +60 -0
- package/dist/core/ingestion/languages/c/simple-hooks.d.ts +14 -0
- package/dist/core/ingestion/languages/c/simple-hooks.js +19 -0
- package/dist/core/ingestion/languages/c/static-linkage.d.ts +13 -0
- package/dist/core/ingestion/languages/c/static-linkage.js +57 -0
- package/dist/core/ingestion/languages/c-cpp.d.ts +12 -0
- package/dist/core/ingestion/languages/c-cpp.js +411 -0
- package/dist/core/ingestion/languages/cobol.d.ts +1 -0
- package/dist/core/ingestion/languages/cobol.js +28 -0
- package/dist/core/ingestion/languages/csharp/accessor-unwrap.d.ts +21 -0
- package/dist/core/ingestion/languages/csharp/accessor-unwrap.js +56 -0
- package/dist/core/ingestion/languages/csharp/arity-metadata.d.ts +26 -0
- package/dist/core/ingestion/languages/csharp/arity-metadata.js +46 -0
- package/dist/core/ingestion/languages/csharp/arity.d.ts +23 -0
- package/dist/core/ingestion/languages/csharp/arity.js +37 -0
- package/dist/core/ingestion/languages/csharp/cache-stats.d.ts +15 -0
- package/dist/core/ingestion/languages/csharp/cache-stats.js +26 -0
- package/dist/core/ingestion/languages/csharp/captures.d.ts +19 -0
- package/dist/core/ingestion/languages/csharp/captures.js +346 -0
- package/dist/core/ingestion/languages/csharp/import-decomposer.d.ts +19 -0
- package/dist/core/ingestion/languages/csharp/import-decomposer.js +93 -0
- package/dist/core/ingestion/languages/csharp/import-target.d.ts +25 -0
- package/dist/core/ingestion/languages/csharp/import-target.js +123 -0
- package/dist/core/ingestion/languages/csharp/index.d.ts +82 -0
- package/dist/core/ingestion/languages/csharp/index.js +82 -0
- package/dist/core/ingestion/languages/csharp/interpret.d.ts +15 -0
- package/dist/core/ingestion/languages/csharp/interpret.js +132 -0
- package/dist/core/ingestion/languages/csharp/merge-bindings.d.ts +27 -0
- package/dist/core/ingestion/languages/csharp/merge-bindings.js +55 -0
- package/dist/core/ingestion/languages/csharp/namespace-siblings.d.ts +51 -0
- package/dist/core/ingestion/languages/csharp/namespace-siblings.js +387 -0
- package/dist/core/ingestion/languages/csharp/query.d.ts +35 -0
- package/dist/core/ingestion/languages/csharp/query.js +521 -0
- package/dist/core/ingestion/languages/csharp/receiver-binding.d.ts +31 -0
- package/dist/core/ingestion/languages/csharp/receiver-binding.js +135 -0
- package/dist/core/ingestion/languages/csharp/scope-resolver.d.ts +10 -0
- package/dist/core/ingestion/languages/csharp/scope-resolver.js +63 -0
- package/dist/core/ingestion/languages/csharp/simple-hooks.d.ts +53 -0
- package/dist/core/ingestion/languages/csharp/simple-hooks.js +76 -0
- package/dist/core/ingestion/languages/csharp.d.ts +8 -0
- package/dist/core/ingestion/languages/csharp.js +201 -0
- package/dist/core/ingestion/languages/dart.d.ts +12 -0
- package/dist/core/ingestion/languages/dart.js +138 -0
- package/dist/core/ingestion/languages/go/arity-metadata.d.ts +8 -0
- package/dist/core/ingestion/languages/go/arity-metadata.js +37 -0
- package/dist/core/ingestion/languages/go/arity.d.ts +2 -0
- package/dist/core/ingestion/languages/go/arity.js +14 -0
- package/dist/core/ingestion/languages/go/cache-stats.d.ts +7 -0
- package/dist/core/ingestion/languages/go/cache-stats.js +15 -0
- package/dist/core/ingestion/languages/go/captures.d.ts +2 -0
- package/dist/core/ingestion/languages/go/captures.js +129 -0
- package/dist/core/ingestion/languages/go/expand-wildcards.d.ts +15 -0
- package/dist/core/ingestion/languages/go/expand-wildcards.js +93 -0
- package/dist/core/ingestion/languages/go/import-decomposer.d.ts +3 -0
- package/dist/core/ingestion/languages/go/import-decomposer.js +44 -0
- package/dist/core/ingestion/languages/go/import-target.d.ts +21 -0
- package/dist/core/ingestion/languages/go/import-target.js +67 -0
- package/dist/core/ingestion/languages/go/index.d.ts +17 -0
- package/dist/core/ingestion/languages/go/index.js +17 -0
- package/dist/core/ingestion/languages/go/interface-impls.d.ts +4 -0
- package/dist/core/ingestion/languages/go/interface-impls.js +72 -0
- package/dist/core/ingestion/languages/go/interpret.d.ts +11 -0
- package/dist/core/ingestion/languages/go/interpret.js +146 -0
- package/dist/core/ingestion/languages/go/merge-bindings.d.ts +2 -0
- package/dist/core/ingestion/languages/go/merge-bindings.js +18 -0
- package/dist/core/ingestion/languages/go/method-owners.d.ts +17 -0
- package/dist/core/ingestion/languages/go/method-owners.js +96 -0
- package/dist/core/ingestion/languages/go/namespace-mirror.d.ts +15 -0
- package/dist/core/ingestion/languages/go/namespace-mirror.js +53 -0
- package/dist/core/ingestion/languages/go/package-siblings.d.ts +11 -0
- package/dist/core/ingestion/languages/go/package-siblings.js +84 -0
- package/dist/core/ingestion/languages/go/query.d.ts +3 -0
- package/dist/core/ingestion/languages/go/query.js +207 -0
- package/dist/core/ingestion/languages/go/range-binding.d.ts +8 -0
- package/dist/core/ingestion/languages/go/range-binding.js +109 -0
- package/dist/core/ingestion/languages/go/receiver-binding.d.ts +3 -0
- package/dist/core/ingestion/languages/go/receiver-binding.js +21 -0
- package/dist/core/ingestion/languages/go/scope-resolver.d.ts +2 -0
- package/dist/core/ingestion/languages/go/scope-resolver.js +33 -0
- package/dist/core/ingestion/languages/go/simple-hooks.d.ts +4 -0
- package/dist/core/ingestion/languages/go/simple-hooks.js +21 -0
- package/dist/core/ingestion/languages/go/type-binding.d.ts +3 -0
- package/dist/core/ingestion/languages/go/type-binding.js +237 -0
- package/dist/core/ingestion/languages/go.d.ts +11 -0
- package/dist/core/ingestion/languages/go.js +94 -0
- package/dist/core/ingestion/languages/index.d.ts +39 -0
- package/dist/core/ingestion/languages/index.js +64 -0
- package/dist/core/ingestion/languages/java/arity-metadata.d.ts +18 -0
- package/dist/core/ingestion/languages/java/arity-metadata.js +40 -0
- package/dist/core/ingestion/languages/java/arity.d.ts +10 -0
- package/dist/core/ingestion/languages/java/arity.js +24 -0
- package/dist/core/ingestion/languages/java/cache-stats.d.ts +15 -0
- package/dist/core/ingestion/languages/java/cache-stats.js +26 -0
- package/dist/core/ingestion/languages/java/captures.d.ts +17 -0
- package/dist/core/ingestion/languages/java/captures.js +187 -0
- package/dist/core/ingestion/languages/java/import-decomposer.d.ts +18 -0
- package/dist/core/ingestion/languages/java/import-decomposer.js +85 -0
- package/dist/core/ingestion/languages/java/import-target.d.ts +17 -0
- package/dist/core/ingestion/languages/java/import-target.js +100 -0
- package/dist/core/ingestion/languages/java/index.d.ts +29 -0
- package/dist/core/ingestion/languages/java/index.js +29 -0
- package/dist/core/ingestion/languages/java/interpret.d.ts +13 -0
- package/dist/core/ingestion/languages/java/interpret.js +131 -0
- package/dist/core/ingestion/languages/java/merge-bindings.d.ts +12 -0
- package/dist/core/ingestion/languages/java/merge-bindings.js +40 -0
- package/dist/core/ingestion/languages/java/query.d.ts +30 -0
- package/dist/core/ingestion/languages/java/query.js +192 -0
- package/dist/core/ingestion/languages/java/receiver-binding.d.ts +11 -0
- package/dist/core/ingestion/languages/java/receiver-binding.js +95 -0
- package/dist/core/ingestion/languages/java/scope-resolver.d.ts +50 -0
- package/dist/core/ingestion/languages/java/scope-resolver.js +74 -0
- package/dist/core/ingestion/languages/java/simple-hooks.d.ts +13 -0
- package/dist/core/ingestion/languages/java/simple-hooks.js +34 -0
- package/dist/core/ingestion/languages/java.d.ts +9 -0
- package/dist/core/ingestion/languages/java.js +76 -0
- package/dist/core/ingestion/languages/kotlin.d.ts +9 -0
- package/dist/core/ingestion/languages/kotlin.js +164 -0
- package/dist/core/ingestion/languages/php/arity-metadata.d.ts +28 -0
- package/dist/core/ingestion/languages/php/arity-metadata.js +63 -0
- package/dist/core/ingestion/languages/php/arity.d.ts +25 -0
- package/dist/core/ingestion/languages/php/arity.js +40 -0
- package/dist/core/ingestion/languages/php/cache-stats.d.ts +15 -0
- package/dist/core/ingestion/languages/php/cache-stats.js +26 -0
- package/dist/core/ingestion/languages/php/captures.d.ts +34 -0
- package/dist/core/ingestion/languages/php/captures.js +739 -0
- package/dist/core/ingestion/languages/php/import-decomposer.d.ts +28 -0
- package/dist/core/ingestion/languages/php/import-decomposer.js +265 -0
- package/dist/core/ingestion/languages/php/import-target.d.ts +47 -0
- package/dist/core/ingestion/languages/php/import-target.js +100 -0
- package/dist/core/ingestion/languages/php/index.d.ts +68 -0
- package/dist/core/ingestion/languages/php/index.js +72 -0
- package/dist/core/ingestion/languages/php/interpret.d.ts +36 -0
- package/dist/core/ingestion/languages/php/interpret.js +241 -0
- package/dist/core/ingestion/languages/php/merge-bindings.d.ts +19 -0
- package/dist/core/ingestion/languages/php/merge-bindings.js +47 -0
- package/dist/core/ingestion/languages/php/namespace-siblings.d.ts +51 -0
- package/dist/core/ingestion/languages/php/namespace-siblings.js +288 -0
- package/dist/core/ingestion/languages/php/query.d.ts +32 -0
- package/dist/core/ingestion/languages/php/query.js +326 -0
- package/dist/core/ingestion/languages/php/receiver-binding.d.ts +36 -0
- package/dist/core/ingestion/languages/php/receiver-binding.js +128 -0
- package/dist/core/ingestion/languages/php/scope-resolver.d.ts +23 -0
- package/dist/core/ingestion/languages/php/scope-resolver.js +358 -0
- package/dist/core/ingestion/languages/php/simple-hooks.d.ts +42 -0
- package/dist/core/ingestion/languages/php/simple-hooks.js +111 -0
- package/dist/core/ingestion/languages/php.d.ts +1 -0
- package/dist/core/ingestion/languages/php.js +290 -0
- package/dist/core/ingestion/languages/python/arity-metadata.d.ts +24 -0
- package/dist/core/ingestion/languages/python/arity-metadata.js +45 -0
- package/dist/core/ingestion/languages/python/arity.d.ts +22 -0
- package/dist/core/ingestion/languages/python/arity.js +38 -0
- package/dist/core/ingestion/languages/python/cache-stats.d.ts +17 -0
- package/dist/core/ingestion/languages/python/cache-stats.js +28 -0
- package/dist/core/ingestion/languages/python/captures.d.ts +19 -0
- package/dist/core/ingestion/languages/python/captures.js +130 -0
- package/dist/core/ingestion/languages/python/import-decomposer.d.ts +15 -0
- package/dist/core/ingestion/languages/python/import-decomposer.js +112 -0
- package/dist/core/ingestion/languages/python/import-target.d.ts +21 -0
- package/dist/core/ingestion/languages/python/import-target.js +195 -0
- package/dist/core/ingestion/languages/python/index.d.ts +80 -0
- package/dist/core/ingestion/languages/python/index.js +80 -0
- package/dist/core/ingestion/languages/python/interpret.d.ts +15 -0
- package/dist/core/ingestion/languages/python/interpret.js +191 -0
- package/dist/core/ingestion/languages/python/merge-bindings.d.ts +16 -0
- package/dist/core/ingestion/languages/python/merge-bindings.js +44 -0
- package/dist/core/ingestion/languages/python/query.d.ts +9 -0
- package/dist/core/ingestion/languages/python/query.js +267 -0
- package/dist/core/ingestion/languages/python/receiver-binding.d.ts +21 -0
- package/dist/core/ingestion/languages/python/receiver-binding.js +116 -0
- package/dist/core/ingestion/languages/python/scope-resolver.d.ts +16 -0
- package/dist/core/ingestion/languages/python/scope-resolver.js +53 -0
- package/dist/core/ingestion/languages/python/simple-hooks.d.ts +25 -0
- package/dist/core/ingestion/languages/python/simple-hooks.js +43 -0
- package/dist/core/ingestion/languages/python.d.ts +12 -0
- package/dist/core/ingestion/languages/python.js +133 -0
- package/dist/core/ingestion/languages/ruby.d.ts +9 -0
- package/dist/core/ingestion/languages/ruby.js +235 -0
- package/dist/core/ingestion/languages/rust.d.ts +12 -0
- package/dist/core/ingestion/languages/rust.js +167 -0
- package/dist/core/ingestion/languages/swift.d.ts +12 -0
- package/dist/core/ingestion/languages/swift.js +312 -0
- package/dist/core/ingestion/languages/typescript/arity-metadata.d.ts +59 -0
- package/dist/core/ingestion/languages/typescript/arity-metadata.js +103 -0
- package/dist/core/ingestion/languages/typescript/arity.d.ts +37 -0
- package/dist/core/ingestion/languages/typescript/arity.js +54 -0
- package/dist/core/ingestion/languages/typescript/cache-stats.d.ts +17 -0
- package/dist/core/ingestion/languages/typescript/cache-stats.js +28 -0
- package/dist/core/ingestion/languages/typescript/captures.d.ts +28 -0
- package/dist/core/ingestion/languages/typescript/captures.js +474 -0
- package/dist/core/ingestion/languages/typescript/import-decomposer.d.ts +49 -0
- package/dist/core/ingestion/languages/typescript/import-decomposer.js +371 -0
- package/dist/core/ingestion/languages/typescript/import-target.d.ts +50 -0
- package/dist/core/ingestion/languages/typescript/import-target.js +61 -0
- package/dist/core/ingestion/languages/typescript/index.d.ts +94 -0
- package/dist/core/ingestion/languages/typescript/index.js +94 -0
- package/dist/core/ingestion/languages/typescript/interpret.d.ts +35 -0
- package/dist/core/ingestion/languages/typescript/interpret.js +317 -0
- package/dist/core/ingestion/languages/typescript/merge-bindings.d.ts +62 -0
- package/dist/core/ingestion/languages/typescript/merge-bindings.js +158 -0
- package/dist/core/ingestion/languages/typescript/query.d.ts +84 -0
- package/dist/core/ingestion/languages/typescript/query.js +978 -0
- package/dist/core/ingestion/languages/typescript/receiver-binding.d.ts +59 -0
- package/dist/core/ingestion/languages/typescript/receiver-binding.js +171 -0
- package/dist/core/ingestion/languages/typescript/scope-resolver.d.ts +16 -0
- package/dist/core/ingestion/languages/typescript/scope-resolver.js +113 -0
- package/dist/core/ingestion/languages/typescript/simple-hooks.d.ts +71 -0
- package/dist/core/ingestion/languages/typescript/simple-hooks.js +131 -0
- package/dist/core/ingestion/languages/typescript.d.ts +11 -0
- package/dist/core/ingestion/languages/typescript.js +324 -0
- package/dist/core/ingestion/languages/vue.d.ts +13 -0
- package/dist/core/ingestion/languages/vue.js +79 -0
- package/dist/core/ingestion/markdown-processor.d.ts +17 -0
- package/dist/core/ingestion/markdown-processor.js +124 -0
- package/dist/core/ingestion/method-extractors/configs/c-cpp.d.ts +3 -0
- package/dist/core/ingestion/method-extractors/configs/c-cpp.js +387 -0
- package/dist/core/ingestion/method-extractors/configs/csharp.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/csharp.js +287 -0
- package/dist/core/ingestion/method-extractors/configs/dart.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/dart.js +376 -0
- package/dist/core/ingestion/method-extractors/configs/go.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/go.js +176 -0
- package/dist/core/ingestion/method-extractors/configs/jvm.d.ts +3 -0
- package/dist/core/ingestion/method-extractors/configs/jvm.js +336 -0
- package/dist/core/ingestion/method-extractors/configs/php.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/php.js +304 -0
- package/dist/core/ingestion/method-extractors/configs/python.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/python.js +309 -0
- package/dist/core/ingestion/method-extractors/configs/ruby.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/ruby.js +286 -0
- package/dist/core/ingestion/method-extractors/configs/rust.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/rust.js +195 -0
- package/dist/core/ingestion/method-extractors/configs/swift.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/swift.js +276 -0
- package/dist/core/ingestion/method-extractors/configs/typescript-javascript.d.ts +3 -0
- package/dist/core/ingestion/method-extractors/configs/typescript-javascript.js +338 -0
- package/dist/core/ingestion/method-extractors/generic.d.ts +11 -0
- package/dist/core/ingestion/method-extractors/generic.js +205 -0
- package/dist/core/ingestion/method-types.d.ts +90 -0
- package/dist/core/ingestion/method-types.js +2 -0
- package/dist/core/ingestion/model/field-registry.d.ts +18 -0
- package/dist/core/ingestion/model/field-registry.js +22 -0
- package/dist/core/ingestion/model/heritage-map.d.ts +105 -0
- package/dist/core/ingestion/model/heritage-map.js +260 -0
- package/dist/core/ingestion/model/index.d.ts +20 -0
- package/dist/core/ingestion/model/index.js +43 -0
- package/dist/core/ingestion/model/method-registry.d.ts +71 -0
- package/dist/core/ingestion/model/method-registry.js +134 -0
- package/dist/core/ingestion/model/registration-table.d.ts +138 -0
- package/dist/core/ingestion/model/registration-table.js +224 -0
- package/dist/core/ingestion/model/resolution-context.d.ts +93 -0
- package/dist/core/ingestion/model/resolution-context.js +337 -0
- package/dist/core/ingestion/model/resolve.d.ts +61 -0
- package/dist/core/ingestion/model/resolve.js +401 -0
- package/dist/core/ingestion/model/scope-resolution-indexes.d.ts +72 -0
- package/dist/core/ingestion/model/scope-resolution-indexes.js +42 -0
- package/dist/core/ingestion/model/semantic-model.d.ts +150 -0
- package/dist/core/ingestion/model/semantic-model.js +175 -0
- package/dist/core/ingestion/model/symbol-table.d.ts +200 -0
- package/dist/core/ingestion/model/symbol-table.js +206 -0
- package/dist/core/ingestion/model/type-registry.d.ts +39 -0
- package/dist/core/ingestion/model/type-registry.js +62 -0
- package/dist/core/ingestion/mro-processor.d.ts +46 -0
- package/dist/core/ingestion/mro-processor.js +597 -0
- package/dist/core/ingestion/named-bindings/csharp.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/csharp.js +37 -0
- package/dist/core/ingestion/named-bindings/java.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/java.js +29 -0
- package/dist/core/ingestion/named-bindings/kotlin.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/kotlin.js +36 -0
- package/dist/core/ingestion/named-bindings/php.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/php.js +61 -0
- package/dist/core/ingestion/named-bindings/python.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/python.js +49 -0
- package/dist/core/ingestion/named-bindings/rust.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/rust.js +66 -0
- package/dist/core/ingestion/named-bindings/types.d.ts +16 -0
- package/dist/core/ingestion/named-bindings/types.js +6 -0
- package/dist/core/ingestion/named-bindings/typescript.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/typescript.js +58 -0
- package/dist/core/ingestion/parsing-processor.d.ts +60 -0
- package/dist/core/ingestion/parsing-processor.js +627 -0
- package/dist/core/ingestion/pipeline-phases/cobol.d.ts +16 -0
- package/dist/core/ingestion/pipeline-phases/cobol.js +46 -0
- package/dist/core/ingestion/pipeline-phases/communities.d.ts +16 -0
- package/dist/core/ingestion/pipeline-phases/communities.js +63 -0
- package/dist/core/ingestion/pipeline-phases/cross-file-impl.d.ts +17 -0
- package/dist/core/ingestion/pipeline-phases/cross-file-impl.js +157 -0
- package/dist/core/ingestion/pipeline-phases/cross-file.d.ts +37 -0
- package/dist/core/ingestion/pipeline-phases/cross-file.js +64 -0
- package/dist/core/ingestion/pipeline-phases/index.d.ts +22 -0
- package/dist/core/ingestion/pipeline-phases/index.js +23 -0
- package/dist/core/ingestion/pipeline-phases/markdown.d.ts +17 -0
- package/dist/core/ingestion/pipeline-phases/markdown.js +34 -0
- package/dist/core/ingestion/pipeline-phases/mro.d.ts +18 -0
- package/dist/core/ingestion/pipeline-phases/mro.js +37 -0
- package/dist/core/ingestion/pipeline-phases/orm-extraction.d.ts +22 -0
- package/dist/core/ingestion/pipeline-phases/orm-extraction.js +92 -0
- package/dist/core/ingestion/pipeline-phases/orm.d.ts +15 -0
- package/dist/core/ingestion/pipeline-phases/orm.js +75 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +63 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +567 -0
- package/dist/core/ingestion/pipeline-phases/parse.d.ts +88 -0
- package/dist/core/ingestion/pipeline-phases/parse.js +33 -0
- package/dist/core/ingestion/pipeline-phases/processes.d.ts +16 -0
- package/dist/core/ingestion/pipeline-phases/processes.js +149 -0
- package/dist/core/ingestion/pipeline-phases/routes.d.ts +21 -0
- package/dist/core/ingestion/pipeline-phases/routes.js +244 -0
- package/dist/core/ingestion/pipeline-phases/runner.d.ts +22 -0
- package/dist/core/ingestion/pipeline-phases/runner.js +204 -0
- package/dist/core/ingestion/pipeline-phases/scan.d.ts +21 -0
- package/dist/core/ingestion/pipeline-phases/scan.js +46 -0
- package/dist/core/ingestion/pipeline-phases/structure.d.ts +27 -0
- package/dist/core/ingestion/pipeline-phases/structure.js +35 -0
- package/dist/core/ingestion/pipeline-phases/tools.d.ts +21 -0
- package/dist/core/ingestion/pipeline-phases/tools.js +86 -0
- package/dist/core/ingestion/pipeline-phases/types.d.ts +79 -0
- package/dist/core/ingestion/pipeline-phases/types.js +37 -0
- package/dist/core/ingestion/pipeline-phases/wildcard-synthesis.d.ts +70 -0
- package/dist/core/ingestion/pipeline-phases/wildcard-synthesis.js +312 -0
- package/dist/core/ingestion/pipeline.d.ts +49 -0
- package/dist/core/ingestion/pipeline.js +89 -0
- package/dist/core/ingestion/process-processor.d.ts +51 -0
- package/dist/core/ingestion/process-processor.js +318 -0
- package/dist/core/ingestion/registry-primary-flag.d.ts +88 -0
- package/dist/core/ingestion/registry-primary-flag.js +117 -0
- package/dist/core/ingestion/resolve-references.d.ts +63 -0
- package/dist/core/ingestion/resolve-references.js +175 -0
- package/dist/core/ingestion/route-extractors/expo.d.ts +1 -0
- package/dist/core/ingestion/route-extractors/expo.js +36 -0
- package/dist/core/ingestion/route-extractors/middleware.d.ts +47 -0
- package/dist/core/ingestion/route-extractors/middleware.js +167 -0
- package/dist/core/ingestion/route-extractors/nextjs.d.ts +3 -0
- package/dist/core/ingestion/route-extractors/nextjs.js +76 -0
- package/dist/core/ingestion/route-extractors/php.d.ts +7 -0
- package/dist/core/ingestion/route-extractors/php.js +22 -0
- package/dist/core/ingestion/route-extractors/response-shapes.d.ts +20 -0
- package/dist/core/ingestion/route-extractors/response-shapes.js +294 -0
- package/dist/core/ingestion/scope-extractor-bridge.d.ts +35 -0
- package/dist/core/ingestion/scope-extractor-bridge.js +49 -0
- package/dist/core/ingestion/scope-extractor.d.ts +86 -0
- package/dist/core/ingestion/scope-extractor.js +772 -0
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +558 -0
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.js +250 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +43 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +79 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/ids.d.ts +57 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +142 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.d.ts +17 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.js +46 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/method-dispatch.d.ts +19 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/method-dispatch.js +40 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.d.ts +37 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +118 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +38 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +73 -0
- package/dist/core/ingestion/scope-resolution/passes/compound-receiver.d.ts +42 -0
- package/dist/core/ingestion/scope-resolution/passes/compound-receiver.js +467 -0
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +53 -0
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +251 -0
- package/dist/core/ingestion/scope-resolution/passes/imported-return-types.d.ts +75 -0
- package/dist/core/ingestion/scope-resolution/passes/imported-return-types.js +202 -0
- package/dist/core/ingestion/scope-resolution/passes/mro.d.ts +42 -0
- package/dist/core/ingestion/scope-resolution/passes/mro.js +102 -0
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +30 -0
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +81 -0
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +46 -0
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +377 -0
- package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +47 -0
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +152 -0
- package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.d.ts +68 -0
- package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +125 -0
- package/dist/core/ingestion/scope-resolution/pipeline/registry.d.ts +17 -0
- package/dist/core/ingestion/scope-resolution/pipeline/registry.js +31 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +91 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +218 -0
- package/dist/core/ingestion/scope-resolution/pipeline/validate-bindings-immutability.d.ts +39 -0
- package/dist/core/ingestion/scope-resolution/pipeline/validate-bindings-immutability.js +65 -0
- package/dist/core/ingestion/scope-resolution/scope/namespace-targets.d.ts +36 -0
- package/dist/core/ingestion/scope-resolution/scope/namespace-targets.js +58 -0
- package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +170 -0
- package/dist/core/ingestion/scope-resolution/scope/walkers.js +447 -0
- package/dist/core/ingestion/scope-resolution/workspace-index.d.ts +52 -0
- package/dist/core/ingestion/scope-resolution/workspace-index.js +61 -0
- package/dist/core/ingestion/shadow-harness.d.ts +113 -0
- package/dist/core/ingestion/shadow-harness.js +148 -0
- package/dist/core/ingestion/structure-processor.d.ts +2 -0
- package/dist/core/ingestion/structure-processor.js +36 -0
- package/dist/core/ingestion/tree-sitter-queries.d.ts +16 -0
- package/dist/core/ingestion/tree-sitter-queries.js +1497 -0
- package/dist/core/ingestion/type-env.d.ts +86 -0
- package/dist/core/ingestion/type-env.js +1129 -0
- package/dist/core/ingestion/type-extractors/c-cpp.d.ts +7 -0
- package/dist/core/ingestion/type-extractors/c-cpp.js +532 -0
- package/dist/core/ingestion/type-extractors/csharp.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/csharp.js +583 -0
- package/dist/core/ingestion/type-extractors/dart.d.ts +15 -0
- package/dist/core/ingestion/type-extractors/dart.js +369 -0
- package/dist/core/ingestion/type-extractors/go.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/go.js +513 -0
- package/dist/core/ingestion/type-extractors/jvm.d.ts +3 -0
- package/dist/core/ingestion/type-extractors/jvm.js +856 -0
- package/dist/core/ingestion/type-extractors/php.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/php.js +534 -0
- package/dist/core/ingestion/type-extractors/python.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/python.js +474 -0
- package/dist/core/ingestion/type-extractors/ruby.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/ruby.js +377 -0
- package/dist/core/ingestion/type-extractors/rust.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/rust.js +515 -0
- package/dist/core/ingestion/type-extractors/shared.d.ts +131 -0
- package/dist/core/ingestion/type-extractors/shared.js +796 -0
- package/dist/core/ingestion/type-extractors/swift.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/swift.js +487 -0
- package/dist/core/ingestion/type-extractors/types.d.ts +172 -0
- package/dist/core/ingestion/type-extractors/types.js +1 -0
- package/dist/core/ingestion/type-extractors/typescript.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/typescript.js +661 -0
- package/dist/core/ingestion/utils/ast-helpers.d.ts +102 -0
- package/dist/core/ingestion/utils/ast-helpers.js +561 -0
- package/dist/core/ingestion/utils/call-analysis.d.ts +75 -0
- package/dist/core/ingestion/utils/call-analysis.js +574 -0
- package/dist/core/ingestion/utils/env.d.ts +20 -0
- package/dist/core/ingestion/utils/env.js +24 -0
- package/dist/core/ingestion/utils/event-loop.d.ts +5 -0
- package/dist/core/ingestion/utils/event-loop.js +5 -0
- package/dist/core/ingestion/utils/graph-sort.d.ts +58 -0
- package/dist/core/ingestion/utils/graph-sort.js +100 -0
- package/dist/core/ingestion/utils/max-file-size.d.ts +20 -0
- package/dist/core/ingestion/utils/max-file-size.js +53 -0
- package/dist/core/ingestion/utils/method-props.d.ts +32 -0
- package/dist/core/ingestion/utils/method-props.js +147 -0
- package/dist/core/ingestion/utils/ruby-self-call.d.ts +52 -0
- package/dist/core/ingestion/utils/ruby-self-call.js +59 -0
- package/dist/core/ingestion/utils/verbose.d.ts +1 -0
- package/dist/core/ingestion/utils/verbose.js +7 -0
- package/dist/core/ingestion/variable-extractors/configs/c-cpp.d.ts +3 -0
- package/dist/core/ingestion/variable-extractors/configs/c-cpp.js +81 -0
- package/dist/core/ingestion/variable-extractors/configs/csharp.d.ts +9 -0
- package/dist/core/ingestion/variable-extractors/configs/csharp.js +63 -0
- package/dist/core/ingestion/variable-extractors/configs/dart.d.ts +2 -0
- package/dist/core/ingestion/variable-extractors/configs/dart.js +94 -0
- package/dist/core/ingestion/variable-extractors/configs/go.d.ts +2 -0
- package/dist/core/ingestion/variable-extractors/configs/go.js +83 -0
- package/dist/core/ingestion/variable-extractors/configs/jvm.d.ts +18 -0
- package/dist/core/ingestion/variable-extractors/configs/jvm.js +115 -0
- package/dist/core/ingestion/variable-extractors/configs/php.d.ts +14 -0
- package/dist/core/ingestion/variable-extractors/configs/php.js +58 -0
- package/dist/core/ingestion/variable-extractors/configs/python.d.ts +2 -0
- package/dist/core/ingestion/variable-extractors/configs/python.js +101 -0
- package/dist/core/ingestion/variable-extractors/configs/ruby.d.ts +11 -0
- package/dist/core/ingestion/variable-extractors/configs/ruby.js +52 -0
- package/dist/core/ingestion/variable-extractors/configs/rust.d.ts +2 -0
- package/dist/core/ingestion/variable-extractors/configs/rust.js +76 -0
- package/dist/core/ingestion/variable-extractors/configs/swift.d.ts +2 -0
- package/dist/core/ingestion/variable-extractors/configs/swift.js +88 -0
- package/dist/core/ingestion/variable-extractors/configs/typescript-javascript.d.ts +3 -0
- package/dist/core/ingestion/variable-extractors/configs/typescript-javascript.js +83 -0
- package/dist/core/ingestion/variable-extractors/generic.d.ts +5 -0
- package/dist/core/ingestion/variable-extractors/generic.js +80 -0
- package/dist/core/ingestion/variable-types.d.ts +82 -0
- package/dist/core/ingestion/variable-types.js +2 -0
- package/dist/core/ingestion/vue-sfc-extractor.d.ts +44 -0
- package/dist/core/ingestion/vue-sfc-extractor.js +111 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +199 -0
- package/dist/core/ingestion/workers/parse-worker.js +1940 -0
- package/dist/core/ingestion/workers/worker-pool.d.ts +23 -0
- package/dist/core/ingestion/workers/worker-pool.js +380 -0
- package/dist/core/lbug/csv-generator.d.ts +33 -0
- package/dist/core/lbug/csv-generator.js +463 -0
- package/dist/core/lbug/extension-loader.d.ts +86 -0
- package/dist/core/lbug/extension-loader.js +186 -0
- package/dist/core/lbug/lbug-adapter.d.ts +243 -0
- package/dist/core/lbug/lbug-adapter.js +1377 -0
- package/dist/core/lbug/lbug-config.d.ts +102 -0
- package/dist/core/lbug/lbug-config.js +303 -0
- package/dist/core/lbug/pool-adapter.d.ts +90 -0
- package/dist/core/lbug/pool-adapter.js +592 -0
- package/dist/core/lbug/schema.d.ts +62 -0
- package/dist/core/lbug/schema.js +495 -0
- package/dist/core/logger.d.ts +125 -0
- package/dist/core/logger.js +323 -0
- package/dist/core/platform/capabilities.d.ts +24 -0
- package/dist/core/platform/capabilities.js +54 -0
- package/dist/core/run-analyze.d.ts +92 -0
- package/dist/core/run-analyze.js +672 -0
- package/dist/core/search/bm25-index.d.ts +29 -0
- package/dist/core/search/bm25-index.js +118 -0
- package/dist/core/search/fts-indexes.d.ts +1 -0
- package/dist/core/search/fts-indexes.js +7 -0
- package/dist/core/search/fts-schema.d.ts +6 -0
- package/dist/core/search/fts-schema.js +7 -0
- package/dist/core/search/hybrid-search.d.ts +53 -0
- package/dist/core/search/hybrid-search.js +136 -0
- package/dist/core/search/phase-timer.d.ts +72 -0
- package/dist/core/search/phase-timer.js +106 -0
- package/dist/core/tree-sitter/parser-loader.d.ts +8 -0
- package/dist/core/tree-sitter/parser-loader.js +189 -0
- package/dist/core/tree-sitter/safe-parse.d.ts +6 -0
- package/dist/core/tree-sitter/safe-parse.js +32 -0
- package/dist/core/wiki/cursor-client.d.ts +31 -0
- package/dist/core/wiki/cursor-client.js +123 -0
- package/dist/core/wiki/generator.d.ts +129 -0
- package/dist/core/wiki/generator.js +899 -0
- package/dist/core/wiki/graph-queries.d.ts +84 -0
- package/dist/core/wiki/graph-queries.js +244 -0
- package/dist/core/wiki/html-viewer.d.ts +10 -0
- package/dist/core/wiki/html-viewer.js +304 -0
- package/dist/core/wiki/llm-client.d.ts +83 -0
- package/dist/core/wiki/llm-client.js +267 -0
- package/dist/core/wiki/mermaid-sanitizer.d.ts +2 -0
- package/dist/core/wiki/mermaid-sanitizer.js +100 -0
- package/dist/core/wiki/prompts.d.ts +53 -0
- package/dist/core/wiki/prompts.js +181 -0
- package/dist/lib/utils.d.ts +1 -0
- package/dist/lib/utils.js +3 -0
- package/dist/mcp/compatible-stdio-transport.d.ts +25 -0
- package/dist/mcp/compatible-stdio-transport.js +206 -0
- package/dist/mcp/core/embedder.d.ts +27 -0
- package/dist/mcp/core/embedder.js +145 -0
- package/dist/mcp/core/lbug-adapter.d.ts +11 -0
- package/dist/mcp/core/lbug-adapter.js +11 -0
- package/dist/mcp/local/local-backend.d.ts +356 -0
- package/dist/mcp/local/local-backend.js +3251 -0
- package/dist/mcp/resources.d.ts +62 -0
- package/dist/mcp/resources.js +512 -0
- package/dist/mcp/server.d.ts +23 -0
- package/dist/mcp/server.js +314 -0
- package/dist/mcp/staleness.d.ts +5 -0
- package/dist/mcp/staleness.js +4 -0
- package/dist/mcp/stdio-capture.d.ts +40 -0
- package/dist/mcp/stdio-capture.js +53 -0
- package/dist/mcp/stdio-context.d.ts +47 -0
- package/dist/mcp/stdio-context.js +145 -0
- package/dist/mcp/tools.d.ts +29 -0
- package/dist/mcp/tools.js +506 -0
- package/dist/server/analyze-job.d.ts +55 -0
- package/dist/server/analyze-job.js +150 -0
- package/dist/server/analyze-worker.d.ts +13 -0
- package/dist/server/analyze-worker.js +59 -0
- package/dist/server/api.d.ts +72 -0
- package/dist/server/api.js +1638 -0
- package/dist/server/git-clone.d.ts +99 -0
- package/dist/server/git-clone.js +397 -0
- package/dist/server/mcp-http.d.ts +13 -0
- package/dist/server/mcp-http.js +101 -0
- package/dist/server/validation.d.ts +98 -0
- package/dist/server/validation.js +142 -0
- package/dist/storage/file-hash.d.ts +47 -0
- package/dist/storage/file-hash.js +86 -0
- package/dist/storage/git.d.ts +148 -0
- package/dist/storage/git.js +346 -0
- package/dist/storage/parse-cache.d.ts +67 -0
- package/dist/storage/parse-cache.js +182 -0
- package/dist/storage/repo-manager.d.ts +467 -0
- package/dist/storage/repo-manager.js +804 -0
- package/dist/types/pipeline.d.ts +18 -0
- package/dist/types/pipeline.js +1 -0
- package/hooks/claude/arc-hook.cjs +334 -0
- package/hooks/claude/hook-lock.cjs +119 -0
- package/hooks/claude/pre-tool-use.sh +79 -0
- package/hooks/claude/session-start.sh +42 -0
- package/package.json +122 -0
- package/scripts/bench-scope-resolution.ts +134 -0
- package/scripts/build-tree-sitter-dart.cjs +53 -0
- package/scripts/build-tree-sitter-proto.cjs +93 -0
- package/scripts/build.js +99 -0
- package/scripts/ci-list-migrated-languages.ts +24 -0
- package/scripts/install-duckdb-extension.mjs +48 -0
- package/skills/arc-cli.md +83 -0
- package/skills/arc-debugging.md +89 -0
- package/skills/arc-exploring.md +78 -0
- package/skills/arc-guide.md +64 -0
- package/skills/arc-impact-analysis.md +97 -0
- package/skills/arc-pr-review.md +163 -0
- package/skills/arc-refactoring.md +121 -0
- package/vendor/leiden/index.cjs +355 -0
- package/vendor/leiden/utils.cjs +392 -0
- package/vendor/tree-sitter-dart/README.md +18 -0
- package/vendor/tree-sitter-dart/binding.gyp +31 -0
- package/vendor/tree-sitter-dart/bindings/node/binding.cc +20 -0
- package/vendor/tree-sitter-dart/bindings/node/index.d.ts +28 -0
- package/vendor/tree-sitter-dart/bindings/node/index.js +7 -0
- package/vendor/tree-sitter-dart/grammar.js +2895 -0
- package/vendor/tree-sitter-dart/package.json +18 -0
- package/vendor/tree-sitter-dart/queries/highlights.scm +246 -0
- package/vendor/tree-sitter-dart/queries/tags.scm +92 -0
- package/vendor/tree-sitter-dart/queries/test.scm +1 -0
- package/vendor/tree-sitter-dart/src/grammar.json +12459 -0
- package/vendor/tree-sitter-dart/src/node-types.json +15055 -0
- package/vendor/tree-sitter-dart/src/parser.c +196127 -0
- package/vendor/tree-sitter-dart/src/scanner.c +130 -0
- package/vendor/tree-sitter-dart/src/tree_sitter/alloc.h +54 -0
- package/vendor/tree-sitter-dart/src/tree_sitter/array.h +290 -0
- package/vendor/tree-sitter-dart/src/tree_sitter/parser.h +265 -0
- package/vendor/tree-sitter-proto/binding.gyp +30 -0
- package/vendor/tree-sitter-proto/bindings/node/binding.cc +20 -0
- package/vendor/tree-sitter-proto/bindings/node/index.d.ts +28 -0
- package/vendor/tree-sitter-proto/bindings/node/index.js +7 -0
- package/vendor/tree-sitter-proto/package.json +12 -0
- package/vendor/tree-sitter-proto/src/node-types.json +1145 -0
- package/vendor/tree-sitter-proto/src/parser.c +10149 -0
- package/vendor/tree-sitter-proto/src/tree_sitter/alloc.h +54 -0
- package/vendor/tree-sitter-proto/src/tree_sitter/array.h +291 -0
- package/vendor/tree-sitter-proto/src/tree_sitter/parser.h +266 -0
- package/vendor/tree-sitter-swift/LICENSE +21 -0
- package/vendor/tree-sitter-swift/README.md +139 -0
- package/vendor/tree-sitter-swift/bindings/node/index.d.ts +28 -0
- package/vendor/tree-sitter-swift/bindings/node/index.js +7 -0
- package/vendor/tree-sitter-swift/package.json +28 -0
- package/vendor/tree-sitter-swift/prebuilds/darwin-arm64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/darwin-x64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/linux-arm64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/linux-x64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/win32-arm64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/win32-x64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/src/node-types.json +30694 -0
- package/web/assets/__vite-browser-external-CLwMvL_q.js +1 -0
- package/web/assets/agent-DaYmiVrk.js +601 -0
- package/web/assets/architecture-7EHR7CIX-6QZW5X65-aGTGQQQG.js +1 -0
- package/web/assets/architectureDiagram-UL44E2DR-613o-OfM.js +36 -0
- package/web/assets/blockDiagram-7IZFK4PR-BBJRt4vF.js +132 -0
- package/web/assets/c4Diagram-Y2BXMSZH-BhR2CErx.js +10 -0
- package/web/assets/chunk-3SSMPTDK-DWfEAoKy.js +321 -0
- package/web/assets/chunk-6764PJDD-NOXEgi3n.js +1 -0
- package/web/assets/chunk-AZZRMDJM-DVvcxwI7.js +15 -0
- package/web/assets/chunk-JQRUD6KW-CQgkrimK.js +1 -0
- package/web/assets/chunk-KGYTTC2M-DxOdSoAJ.js +161 -0
- package/web/assets/chunk-KRXBNO2N-BlnQTnxv.js +1 -0
- package/web/assets/chunk-LCXTWHL2-Dhf_u-1F.js +231 -0
- package/web/assets/chunk-LII3EMHJ-Cb3HLCZX.js +1 -0
- package/web/assets/chunk-RG4AUYOV-DLCfNede.js +206 -0
- package/web/assets/chunk-T5OCTHI4-B0CGAG7q.js +1 -0
- package/web/assets/chunk-W44A43WB-ZyrAMwtT.js +1 -0
- package/web/assets/chunk-ZXARS5L4-B0TJPmj5.js +1 -0
- package/web/assets/classDiagram-KGZ6W3CR-CvSnsfJD.js +1 -0
- package/web/assets/classDiagram-v2-72OJOZXJ-CvSnsfJD.js +1 -0
- package/web/assets/context-builder-BREgwful.js +15 -0
- package/web/assets/cose-bilkent-UX7MHV2Q-BsPIaeag.js +1 -0
- package/web/assets/dagre-ND4H6XIP-CV4l9vOZ.js +4 -0
- package/web/assets/diagram-3NCE3AQN-9kSzEbS8.js +43 -0
- package/web/assets/diagram-GF46GFSD-qRvqbex6.js +24 -0
- package/web/assets/diagram-HNR7UZ2L-Dj_ye4Ua.js +3 -0
- package/web/assets/diagram-QXG6HAR7-COwBV6B0.js +24 -0
- package/web/assets/diagram-WEQXMOUZ-C9xjn5dU.js +10 -0
- package/web/assets/erDiagram-L5TCEMPS-fRp0t1Yd.js +85 -0
- package/web/assets/eventmodeling-FCH6USID-MREXMVOE-BR0Ygfrw.js +1 -0
- package/web/assets/flowDiagram-H6V6AXG4-Ccr8FDLD.js +162 -0
- package/web/assets/ganttDiagram-JCBTUEKG-DfBPqAGN.js +292 -0
- package/web/assets/gitGraph-WXDBUCRP-R675I2BI-CYihBz6Z.js +1 -0
- package/web/assets/gitGraphDiagram-S2ZK5IYY-CHvG_UQ0.js +106 -0
- package/web/assets/index-B7cw1L6-.css +2 -0
- package/web/assets/index-CJJQgfSH.js +886 -0
- package/web/assets/info-J43DQDTF-KCYPFFUO-BmmoeX4D.js +1 -0
- package/web/assets/infoDiagram-3YFTVSEB-C7cMy-GP.js +2 -0
- package/web/assets/ishikawaDiagram-BNXS4ZKH-C80yCPYi.js +70 -0
- package/web/assets/journeyDiagram-M6C3CM3L-BHxH1zjE.js +139 -0
- package/web/assets/kanban-definition-75IXJCU3-DNZo1hOE.js +89 -0
- package/web/assets/katex-K3KEBU37-CbyuvTf1.js +261 -0
- package/web/assets/mindmap-definition-2TDM6QVE-Dpgl3Elt.js +96 -0
- package/web/assets/packet-YPE3B663-LP52Z2RK-7JAqDnUy.js +1 -0
- package/web/assets/pie-LRSECV5Y-TCRJHUBD-Bv9vE7io.js +1 -0
- package/web/assets/pieDiagram-CU6KROY3-BW0mr0ek.js +30 -0
- package/web/assets/quadrantDiagram-VICAPDV7-C1dCMBbk.js +7 -0
- package/web/assets/radar-GUYGQ44K-RDLRG3WG-dtZpcOZd.js +1 -0
- package/web/assets/requirementDiagram-JXO7QTGE-Dyqqny4j.js +84 -0
- package/web/assets/sankeyDiagram-URQDO5SZ-B3FGr5SL.js +40 -0
- package/web/assets/sequenceDiagram-VS2MUI6T-B4LlGP9C.js +162 -0
- package/web/assets/stateDiagram-7D4R322I-V9F-klBP.js +1 -0
- package/web/assets/stateDiagram-v2-36443NZ5-CKDYYzqR.js +1 -0
- package/web/assets/timeline-definition-O6YCAMPW-CX2WjkZA.js +120 -0
- package/web/assets/treeView-BLDUP644-QA4HXRO3-BQaKTdhr.js +1 -0
- package/web/assets/treemap-LRROVOQU-LLAWBHMP-Bqlxdyrq.js +1 -0
- package/web/assets/vennDiagram-MWXL3ELB-BxZPYqOF.js +34 -0
- package/web/assets/wardley-L42UT6IY-5TKZOOLJ-dofeprUr.js +1 -0
- package/web/assets/wardleyDiagram-CUQ6CDDI-BLdJJYkV.js +78 -0
- package/web/assets/xychartDiagram-N2JHSOCM-DqDgigLa.js +7 -0
- package/web/index.html +19 -0
|
@@ -0,0 +1,2754 @@
|
|
|
1
|
+
import { CLASS_TYPES, CALL_TARGET_TYPES, lookupMethodByOwnerWithMRO } from './model/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* DAG stage 4 fallback: used when `selectDispatch` is absent or returns null.
|
|
4
|
+
* Preserves pre-DAG dispatch semantics:
|
|
5
|
+
* - 'constructor' → constructor branch
|
|
6
|
+
* - 'free' → free branch (admits class-target fast path)
|
|
7
|
+
* - 'member' or undefined → owner-scoped branch
|
|
8
|
+
*
|
|
9
|
+
* `undefined` callForm MUST route through owner-scoped (not free) so bare
|
|
10
|
+
* identifiers without a classified shape do NOT trigger `resolveFreeCall`'s
|
|
11
|
+
* class-target fast path. Without a `receiverTypeName`, the owner-scoped
|
|
12
|
+
* branch falls through to `resolveModuleAliasedCall` + `singleCandidate`,
|
|
13
|
+
* matching legacy behavior where non-callable symbols (Class, Interface)
|
|
14
|
+
* null-route instead of producing spurious Constructor edges.
|
|
15
|
+
*/
|
|
16
|
+
const defaultDispatchDecision = (callForm) => {
|
|
17
|
+
if (callForm === 'constructor')
|
|
18
|
+
return { primary: 'constructor' };
|
|
19
|
+
if (callForm === 'free')
|
|
20
|
+
return { primary: 'free' };
|
|
21
|
+
return { primary: 'owner-scoped' };
|
|
22
|
+
};
|
|
23
|
+
import Parser from 'tree-sitter';
|
|
24
|
+
import { TIER_CONFIDENCE } from './model/resolution-context.js';
|
|
25
|
+
import { isLanguageAvailable, loadParser, loadLanguage } from '../tree-sitter/parser-loader.js';
|
|
26
|
+
import { getProvider } from './languages/index.js';
|
|
27
|
+
import { generateId } from '../../lib/utils.js';
|
|
28
|
+
import { getLanguageFromFilename, SupportedLanguages } from '../../_shared/index.js';
|
|
29
|
+
import { isRegistryPrimary } from './registry-primary-flag.js';
|
|
30
|
+
import { isVerboseIngestionEnabled } from './utils/verbose.js';
|
|
31
|
+
import { yieldToEventLoop } from './utils/event-loop.js';
|
|
32
|
+
import { parseSourceSafe } from '../tree-sitter/safe-parse.js';
|
|
33
|
+
import { CLASS_CONTAINER_TYPES, FUNCTION_NODE_TYPES, findEnclosingClassInfo, genericFuncName, inferFunctionLabel, } from './utils/ast-helpers.js';
|
|
34
|
+
import { typeTagForId, constTagForId, buildCollisionGroups } from './utils/method-props.js';
|
|
35
|
+
import { countCallArguments, inferCallForm, extractReceiverName, extractReceiverNode, extractMixedChain, extractCallArgTypes, } from './utils/call-analysis.js';
|
|
36
|
+
import { buildTypeEnv, isSubclassOf } from './type-env.js';
|
|
37
|
+
import { getTreeSitterBufferSize } from './constants.js';
|
|
38
|
+
import { normalizeFetchURL, routeMatches } from './route-extractors/nextjs.js';
|
|
39
|
+
import { extractTemplateComponents } from './vue-sfc-extractor.js';
|
|
40
|
+
import { extractReturnTypeName, stripNullable } from './type-extractors/shared.js';
|
|
41
|
+
import { logger } from '../logger.js';
|
|
42
|
+
// ── Property-prepass helpers (parity with parse-worker.ts) ──
|
|
43
|
+
// These mirror the sequential-path equivalents in parse-worker.ts so the main-
|
|
44
|
+
// thread `processCalls` pre-pass produces byte-identical Property nodes/symbols
|
|
45
|
+
// to the worker pool. Drift between the two paths breaks the
|
|
46
|
+
// `incremental ≡ --force` invariant the moment a repo crosses the worker
|
|
47
|
+
// threshold between runs.
|
|
48
|
+
/** Walk up to the nearest enclosing class/struct/interface AST node. */
|
|
49
|
+
const findEnclosingClassNode = (node) => {
|
|
50
|
+
let current = node.parent;
|
|
51
|
+
while (current) {
|
|
52
|
+
if (CLASS_CONTAINER_TYPES.has(current.type))
|
|
53
|
+
return current;
|
|
54
|
+
current = current.parent;
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
};
|
|
58
|
+
/** No-op SymbolTable stub for FieldExtractorContext — matches parse-worker. */
|
|
59
|
+
const NOOP_SYMBOL_TABLE = {
|
|
60
|
+
lookupExact: () => undefined,
|
|
61
|
+
lookupExactFull: () => undefined,
|
|
62
|
+
lookupExactAll: () => [],
|
|
63
|
+
lookupCallableByName: () => [],
|
|
64
|
+
getFiles: () => [][Symbol.iterator](),
|
|
65
|
+
getStats: () => ({ fileCount: 0 }),
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Extract (and cache) field info for a class node. Cache is passed in so it
|
|
69
|
+
* stays scoped to a single `processCalls` invocation rather than leaking
|
|
70
|
+
* across analyze runs (worker uses module-level caching because each worker
|
|
71
|
+
* process is short-lived; the main thread is not).
|
|
72
|
+
*
|
|
73
|
+
* Cache key is `${filePath}:${classNode.startIndex}` — startIndex alone is a
|
|
74
|
+
* per-file byte offset, so almost every Ruby/Python file's leading class lands
|
|
75
|
+
* at byte 0 and would collide across files in the shared map.
|
|
76
|
+
*/
|
|
77
|
+
const getFieldInfo = (classNode, provider, context, cache) => {
|
|
78
|
+
if (!provider.fieldExtractor)
|
|
79
|
+
return undefined;
|
|
80
|
+
const cacheKey = `${context.filePath}:${classNode.startIndex}`;
|
|
81
|
+
const cached = cache.get(cacheKey);
|
|
82
|
+
if (cached)
|
|
83
|
+
return cached;
|
|
84
|
+
const result = provider.fieldExtractor.extract(classNode, context);
|
|
85
|
+
if (!result?.fields?.length)
|
|
86
|
+
return undefined;
|
|
87
|
+
const map = new Map();
|
|
88
|
+
for (const field of result.fields)
|
|
89
|
+
map.set(field.name, field);
|
|
90
|
+
cache.set(cacheKey, map);
|
|
91
|
+
return map;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Type labels treated as class-like **method-dispatch receivers** by the call
|
|
95
|
+
* resolver — the set walked by the MRO / heritage path for member and static
|
|
96
|
+
* method calls.
|
|
97
|
+
*
|
|
98
|
+
* Derived from `CLASS_TYPES` (the heritage-index set in symbol-table) plus
|
|
99
|
+
* `Impl` — Rust `impl` blocks are the definition site of methods for a struct
|
|
100
|
+
* and must be walkable as receiver-type candidates even though they are not
|
|
101
|
+
* indexed by `lookupClassByName` (which keys off struct/trait names). Keeping
|
|
102
|
+
* this set a strict superset of `CLASS_TYPES` guarantees that anything
|
|
103
|
+
* reachable via `lookupClassByName` also passes this filter, so the two call
|
|
104
|
+
* paths cannot diverge silently.
|
|
105
|
+
*
|
|
106
|
+
* `Interface` is included even though interfaces cannot be directly
|
|
107
|
+
* instantiated in Java/C#/TypeScript: the resolver still needs to reach
|
|
108
|
+
* interface nodes for static-method dispatch (`Interface.staticMethod()`) and
|
|
109
|
+
* default-method resolution via the MRO walker.
|
|
110
|
+
*
|
|
111
|
+
* **Do not reuse this set for constructor-fallback filtering.** Constructors
|
|
112
|
+
* can only instantiate a narrower subset — see `INSTANTIABLE_CLASS_TYPES`
|
|
113
|
+
* below. `resolveStaticCall`'s step-5 class-node fallback uses the narrower
|
|
114
|
+
* set to prevent false `CALLS` edges from constructor-shaped calls to
|
|
115
|
+
* `Interface`, `Trait`, or `Impl` nodes.
|
|
116
|
+
*/
|
|
117
|
+
const CLASS_LIKE_TYPES = new Set([...CLASS_TYPES, 'Impl']);
|
|
118
|
+
/**
|
|
119
|
+
* Type labels that can be the target of a constructor-shaped call when no
|
|
120
|
+
* explicit `Constructor` symbol is indexed — the "return the type itself as
|
|
121
|
+
* the call target" fallback set.
|
|
122
|
+
*
|
|
123
|
+
* Strict subset of both `CLASS_LIKE_TYPES` and `CONSTRUCTOR_TARGET_TYPES`.
|
|
124
|
+
* Excludes:
|
|
125
|
+
* - `Interface` / `Trait` — not instantiable by definition in any
|
|
126
|
+
* supported language.
|
|
127
|
+
* - `Impl` — Rust `impl` blocks are method-definition containers, not
|
|
128
|
+
* the type itself; the owning `Struct` is the correct target.
|
|
129
|
+
* - `Enum` — excluded pending language-specific support with motivating
|
|
130
|
+
* test fixtures (matches `CONSTRUCTOR_TARGET_TYPES`).
|
|
131
|
+
*
|
|
132
|
+
* Used exclusively by `resolveStaticCall`'s step-5 class-node fallback.
|
|
133
|
+
* Keep in sync with `CONSTRUCTOR_TARGET_TYPES` (which additionally contains
|
|
134
|
+
* `'Constructor'` for explicit-constructor-node filtering) when extending.
|
|
135
|
+
*/
|
|
136
|
+
const INSTANTIABLE_CLASS_TYPES = new Set(['Class', 'Struct', 'Record']);
|
|
137
|
+
const MAX_EXPORTS_PER_FILE = 500;
|
|
138
|
+
const MAX_TYPE_NAME_LENGTH = 256;
|
|
139
|
+
/** Build a map of imported callee names → return types for cross-file call-result binding.
|
|
140
|
+
* Consulted ONLY when SymbolTable has no unambiguous local match (local-first principle).
|
|
141
|
+
*
|
|
142
|
+
* Overlapping mechanism (1 of 3): this is the SymbolTable-backed path.
|
|
143
|
+
* See also:
|
|
144
|
+
* 2. collectExportedBindings (~line 168) / enrichExportedTypeMap — TypeEnv + graph isExported
|
|
145
|
+
* 3. Phase 9 fallback in verifyConstructorBindings (~line 563) — namedImportMap + BindingAccumulator
|
|
146
|
+
* A future cleanup should merge these into a single resolution pass. */
|
|
147
|
+
export function buildImportedReturnTypes(filePath, namedImportMap, symbolTable) {
|
|
148
|
+
const result = new Map();
|
|
149
|
+
const fileImports = namedImportMap.get(filePath);
|
|
150
|
+
if (!fileImports)
|
|
151
|
+
return result;
|
|
152
|
+
for (const [localName, binding] of fileImports) {
|
|
153
|
+
const def = symbolTable.lookupExactFull(binding.sourcePath, binding.exportedName);
|
|
154
|
+
if (!def?.returnType)
|
|
155
|
+
continue;
|
|
156
|
+
const simpleReturn = extractReturnTypeName(def.returnType);
|
|
157
|
+
if (simpleReturn)
|
|
158
|
+
result.set(localName, simpleReturn);
|
|
159
|
+
}
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
/** Build cross-file RAW return types for imported callables.
|
|
163
|
+
* Unlike buildImportedReturnTypes (which stores extractReturnTypeName output),
|
|
164
|
+
* this stores the raw declared return type string (e.g., 'User[]', 'List<User>').
|
|
165
|
+
* Used by lookupRawReturnType for for-loop element extraction via extractElementTypeFromString. */
|
|
166
|
+
export function buildImportedRawReturnTypes(filePath, namedImportMap, symbolTable) {
|
|
167
|
+
const result = new Map();
|
|
168
|
+
const fileImports = namedImportMap.get(filePath);
|
|
169
|
+
if (!fileImports)
|
|
170
|
+
return result;
|
|
171
|
+
for (const [localName, binding] of fileImports) {
|
|
172
|
+
const def = symbolTable.lookupExactFull(binding.sourcePath, binding.exportedName);
|
|
173
|
+
if (!def?.returnType)
|
|
174
|
+
continue;
|
|
175
|
+
result.set(localName, def.returnType);
|
|
176
|
+
}
|
|
177
|
+
return result;
|
|
178
|
+
}
|
|
179
|
+
/** Collect resolved type bindings for exported file-scope symbols.
|
|
180
|
+
* Uses graph node isExported flag — does NOT require isExported on SymbolDefinition.
|
|
181
|
+
*
|
|
182
|
+
* **Counterpart**: the worker path populates `exportedTypeMap` via the
|
|
183
|
+
* accumulator enrichment loop in `pipeline.ts` (search for "Worker path
|
|
184
|
+
* quality enrichment"). Both sites populate the same map with subtly
|
|
185
|
+
* different export-check semantics — this site uses SymbolTable +
|
|
186
|
+
* graph lookup, the worker loop uses three-candidate-ID graph lookup.
|
|
187
|
+
* They must stay in sync until unified. If you edit one, check the other.
|
|
188
|
+
*
|
|
189
|
+
* Overlapping mechanism (2 of 3): this is the TypeEnv + graph isExported path.
|
|
190
|
+
* See also:
|
|
191
|
+
* 1. buildImportedReturnTypes (~line 109) — namedImportMap + SymbolTable
|
|
192
|
+
* 3. Phase 9 fallback in verifyConstructorBindings (~line 563) — namedImportMap + BindingAccumulator
|
|
193
|
+
* A future cleanup should merge these into a single resolution pass. */
|
|
194
|
+
function collectExportedBindings(typeEnv, filePath, symbolTable, graph) {
|
|
195
|
+
const fileScope = typeEnv.fileScope();
|
|
196
|
+
if (!fileScope || fileScope.size === 0)
|
|
197
|
+
return null;
|
|
198
|
+
const exported = new Map();
|
|
199
|
+
for (const [varName, typeName] of fileScope) {
|
|
200
|
+
if (exported.size >= MAX_EXPORTS_PER_FILE)
|
|
201
|
+
break;
|
|
202
|
+
if (!typeName || typeName.length > MAX_TYPE_NAME_LENGTH)
|
|
203
|
+
continue;
|
|
204
|
+
const nodeId = symbolTable.lookupExact(filePath, varName);
|
|
205
|
+
if (!nodeId)
|
|
206
|
+
continue;
|
|
207
|
+
const node = graph.getNode(nodeId);
|
|
208
|
+
if (node?.properties?.isExported) {
|
|
209
|
+
exported.set(varName, typeName);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return exported.size > 0 ? exported : null;
|
|
213
|
+
}
|
|
214
|
+
/** Build ExportedTypeMap from graph nodes — used for worker path where TypeEnv
|
|
215
|
+
* is not available in the main thread. Collects returnType/declaredType from
|
|
216
|
+
* exported symbols that have callables with known return types. */
|
|
217
|
+
export function buildExportedTypeMapFromGraph(graph, symbolTable) {
|
|
218
|
+
const result = new Map();
|
|
219
|
+
graph.forEachNode((node) => {
|
|
220
|
+
if (!node.properties?.isExported)
|
|
221
|
+
return;
|
|
222
|
+
if (!node.properties?.filePath || !node.properties?.name)
|
|
223
|
+
return;
|
|
224
|
+
const filePath = node.properties.filePath;
|
|
225
|
+
const name = node.properties.name;
|
|
226
|
+
if (!name || name.length > MAX_TYPE_NAME_LENGTH)
|
|
227
|
+
return;
|
|
228
|
+
// For callable symbols, use returnType; for properties/variables, use declaredType.
|
|
229
|
+
// Use lookupExactAll + nodeId match to handle same-name methods in different classes.
|
|
230
|
+
const defs = symbolTable.lookupExactAll(filePath, name);
|
|
231
|
+
const def = defs.find((d) => d.nodeId === node.id) ?? defs[0];
|
|
232
|
+
if (!def)
|
|
233
|
+
return;
|
|
234
|
+
const typeName = def.returnType ?? def.declaredType;
|
|
235
|
+
if (!typeName || typeName.length > MAX_TYPE_NAME_LENGTH)
|
|
236
|
+
return;
|
|
237
|
+
// Extract simple type name (strip Promise<>, etc.) — reuse shared utility
|
|
238
|
+
const simpleType = extractReturnTypeName(typeName) ?? typeName;
|
|
239
|
+
if (!simpleType)
|
|
240
|
+
return;
|
|
241
|
+
let fileExports = result.get(filePath);
|
|
242
|
+
if (!fileExports) {
|
|
243
|
+
fileExports = new Map();
|
|
244
|
+
result.set(filePath, fileExports);
|
|
245
|
+
}
|
|
246
|
+
if (fileExports.size < MAX_EXPORTS_PER_FILE) {
|
|
247
|
+
fileExports.set(name, simpleType);
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
return result;
|
|
251
|
+
}
|
|
252
|
+
/** Seed cross-file receiver types into pre-extracted call records.
|
|
253
|
+
* Fills missing receiverTypeName for single-hop imported variables
|
|
254
|
+
* using ExportedTypeMap + namedImportMap — zero disk I/O, zero AST re-parsing.
|
|
255
|
+
* Mutates calls in-place. Runs BEFORE processCallsFromExtracted. */
|
|
256
|
+
export function seedCrossFileReceiverTypes(calls, namedImportMap, exportedTypeMap) {
|
|
257
|
+
if (namedImportMap.size === 0 || exportedTypeMap.size === 0) {
|
|
258
|
+
return { enrichedCount: 0 };
|
|
259
|
+
}
|
|
260
|
+
let enrichedCount = 0;
|
|
261
|
+
for (const call of calls) {
|
|
262
|
+
if (call.receiverTypeName || !call.receiverName)
|
|
263
|
+
continue;
|
|
264
|
+
if (call.callForm !== 'member')
|
|
265
|
+
continue;
|
|
266
|
+
const fileImports = namedImportMap.get(call.filePath);
|
|
267
|
+
if (!fileImports)
|
|
268
|
+
continue;
|
|
269
|
+
const binding = fileImports.get(call.receiverName);
|
|
270
|
+
if (!binding)
|
|
271
|
+
continue;
|
|
272
|
+
const upstream = exportedTypeMap.get(binding.sourcePath);
|
|
273
|
+
if (!upstream)
|
|
274
|
+
continue;
|
|
275
|
+
const type = upstream.get(binding.exportedName);
|
|
276
|
+
if (type) {
|
|
277
|
+
call.receiverTypeName = type;
|
|
278
|
+
enrichedCount++;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return { enrichedCount };
|
|
282
|
+
}
|
|
283
|
+
// Stdlib methods that preserve the receiver's type identity. When TypeEnv already
|
|
284
|
+
// strips nullable wrappers (Option<User> → User), these chain steps are no-ops
|
|
285
|
+
// for type resolution — the current type passes through unchanged.
|
|
286
|
+
const TYPE_PRESERVING_METHODS = new Set([
|
|
287
|
+
'unwrap',
|
|
288
|
+
'expect',
|
|
289
|
+
'unwrap_or',
|
|
290
|
+
'unwrap_or_default',
|
|
291
|
+
'unwrap_or_else', // Rust Option/Result
|
|
292
|
+
'clone',
|
|
293
|
+
'to_owned',
|
|
294
|
+
'as_ref',
|
|
295
|
+
'as_mut',
|
|
296
|
+
'borrow',
|
|
297
|
+
'borrow_mut', // Rust clone/borrow
|
|
298
|
+
'get', // Kotlin/Java Optional.get()
|
|
299
|
+
'orElseThrow', // Java Optional
|
|
300
|
+
]);
|
|
301
|
+
/** Cache for method extraction results in findEnclosingFunction fallback path.
|
|
302
|
+
* Keyed by classNode.id to avoid re-extracting the same class body per call site.
|
|
303
|
+
* Cleared between files at line ~611 in the processCalls file loop. */
|
|
304
|
+
const enclosingFnExtractCache = new Map();
|
|
305
|
+
/**
|
|
306
|
+
* Walk up the AST from a node to find the enclosing function/method.
|
|
307
|
+
* Returns null if the call is at module/file level (top-level code).
|
|
308
|
+
*/
|
|
309
|
+
const findEnclosingFunction = (node, filePath, ctx, provider) => {
|
|
310
|
+
let current = node.parent;
|
|
311
|
+
while (current) {
|
|
312
|
+
if (FUNCTION_NODE_TYPES.has(current.type)) {
|
|
313
|
+
const efnResult = provider.methodExtractor?.extractFunctionName?.(current);
|
|
314
|
+
const funcName = efnResult?.funcName ?? genericFuncName(current);
|
|
315
|
+
const label = efnResult?.label ?? inferFunctionLabel(current.type);
|
|
316
|
+
if (funcName) {
|
|
317
|
+
const resolved = ctx.resolve(funcName, filePath);
|
|
318
|
+
if (resolved?.tier === 'same-file' && resolved.candidates.length > 0) {
|
|
319
|
+
// Disambiguate by enclosing class when multiple candidates
|
|
320
|
+
if (resolved.candidates.length === 1) {
|
|
321
|
+
return resolved.candidates[0].nodeId;
|
|
322
|
+
}
|
|
323
|
+
const classInfo = findEnclosingClassInfo(current, filePath);
|
|
324
|
+
if (classInfo) {
|
|
325
|
+
const classMatches = resolved.candidates.filter((c) => c.ownerId === classInfo.classId);
|
|
326
|
+
// Unique class match — return it (no same-arity ambiguity)
|
|
327
|
+
if (classMatches.length === 1)
|
|
328
|
+
return classMatches[0].nodeId;
|
|
329
|
+
// Multiple same-class candidates (same-arity overloads) — fall through
|
|
330
|
+
// to the fallback path which computes the exact ID with type-hash.
|
|
331
|
+
if (classMatches.length > 1) {
|
|
332
|
+
/* fall through to manual ID construction below */
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
// No class match — return first candidate as before
|
|
336
|
+
return resolved.candidates[0].nodeId;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
return resolved.candidates[0].nodeId;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
// Fallback: qualify the generated ID to match definition-phase node IDs
|
|
344
|
+
let finalLabel = label;
|
|
345
|
+
if (provider.labelOverride) {
|
|
346
|
+
const override = provider.labelOverride(current, label);
|
|
347
|
+
if (override !== null)
|
|
348
|
+
finalLabel = override;
|
|
349
|
+
}
|
|
350
|
+
const classInfo2 = findEnclosingClassInfo(current, filePath);
|
|
351
|
+
const qualifiedName = classInfo2 ? `${classInfo2.className}.${funcName}` : funcName;
|
|
352
|
+
// Include #<arity> and ~typeTag suffix to match definition-phase Method/Constructor IDs.
|
|
353
|
+
const language = getLanguageFromFilename(filePath);
|
|
354
|
+
let arity;
|
|
355
|
+
let encTypeTag = '';
|
|
356
|
+
if ((finalLabel === 'Method' || finalLabel === 'Constructor') &&
|
|
357
|
+
provider.methodExtractor &&
|
|
358
|
+
language) {
|
|
359
|
+
// Get class method map (cached per classNode.id) and look up current method
|
|
360
|
+
// by funcName:line. This avoids per-call-site extractFromNode AST walks.
|
|
361
|
+
let classNode = current.parent;
|
|
362
|
+
while (classNode && !provider.methodExtractor.isTypeDeclaration(classNode)) {
|
|
363
|
+
classNode = classNode.parent;
|
|
364
|
+
}
|
|
365
|
+
let info;
|
|
366
|
+
if (classNode) {
|
|
367
|
+
let extracted = enclosingFnExtractCache.get(classNode.id);
|
|
368
|
+
if (extracted === undefined) {
|
|
369
|
+
extracted =
|
|
370
|
+
provider.methodExtractor.extract(classNode, { filePath, language }) ?? null;
|
|
371
|
+
enclosingFnExtractCache.set(classNode.id, extracted);
|
|
372
|
+
}
|
|
373
|
+
if (extracted?.methods?.length) {
|
|
374
|
+
const defLine = current.startPosition.row + 1;
|
|
375
|
+
info = extracted.methods.find((m) => m.name === funcName && m.line === defLine);
|
|
376
|
+
if (info) {
|
|
377
|
+
arity = info.parameters.some((p) => p.isVariadic)
|
|
378
|
+
? undefined
|
|
379
|
+
: info.parameters.length;
|
|
380
|
+
}
|
|
381
|
+
if (arity !== undefined && info) {
|
|
382
|
+
const methodMap = new Map();
|
|
383
|
+
for (const m of extracted.methods)
|
|
384
|
+
methodMap.set(`${m.name}:${m.line}`, m);
|
|
385
|
+
const groups = buildCollisionGroups(methodMap);
|
|
386
|
+
encTypeTag =
|
|
387
|
+
typeTagForId(methodMap, funcName, arity, info, language, groups) +
|
|
388
|
+
constTagForId(methodMap, funcName, arity, info, groups);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
// Fallback: extractFromNode for top-level methods without a class
|
|
393
|
+
if (!info && provider.methodExtractor.extractFromNode) {
|
|
394
|
+
const nodeInfo = provider.methodExtractor.extractFromNode(current, {
|
|
395
|
+
filePath,
|
|
396
|
+
language,
|
|
397
|
+
});
|
|
398
|
+
if (nodeInfo) {
|
|
399
|
+
arity = nodeInfo.parameters.some((p) => p.isVariadic)
|
|
400
|
+
? undefined
|
|
401
|
+
: nodeInfo.parameters.length;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
const arityTag = arity !== undefined ? `#${arity}${encTypeTag}` : '';
|
|
406
|
+
return generateId(finalLabel, `${filePath}:${qualifiedName}${arityTag}`);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
// Language-specific enclosing function resolution (e.g., Dart where
|
|
410
|
+
// function_body is a sibling of function_signature, not a child).
|
|
411
|
+
if (provider.enclosingFunctionFinder) {
|
|
412
|
+
const customResult = provider.enclosingFunctionFinder(current);
|
|
413
|
+
if (customResult) {
|
|
414
|
+
const resolved = ctx.resolve(customResult.funcName, filePath);
|
|
415
|
+
if (resolved?.tier === 'same-file' && resolved.candidates.length > 0) {
|
|
416
|
+
if (resolved.candidates.length === 1) {
|
|
417
|
+
return resolved.candidates[0].nodeId;
|
|
418
|
+
}
|
|
419
|
+
const classInfo = findEnclosingClassInfo(current.previousSibling ?? current, filePath);
|
|
420
|
+
if (classInfo) {
|
|
421
|
+
const classMatches = resolved.candidates.filter((c) => c.ownerId === classInfo.classId);
|
|
422
|
+
if (classMatches.length === 1)
|
|
423
|
+
return classMatches[0].nodeId;
|
|
424
|
+
if (classMatches.length > 1) {
|
|
425
|
+
/* fall through to manual ID construction below */
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
return resolved.candidates[0].nodeId;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
return resolved.candidates[0].nodeId;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
let finalLabel = customResult.label;
|
|
436
|
+
if (provider.labelOverride) {
|
|
437
|
+
const override = provider.labelOverride(current.previousSibling, finalLabel);
|
|
438
|
+
if (override !== null)
|
|
439
|
+
finalLabel = override;
|
|
440
|
+
}
|
|
441
|
+
const classInfo2 = findEnclosingClassInfo(current.previousSibling ?? current, filePath);
|
|
442
|
+
const qualifiedName = classInfo2
|
|
443
|
+
? `${classInfo2.className}.${customResult.funcName}`
|
|
444
|
+
: customResult.funcName;
|
|
445
|
+
// Include #<arity> and ~typeTag suffix to match definition-phase Method/Constructor IDs.
|
|
446
|
+
const sigNode = current.previousSibling ?? current;
|
|
447
|
+
const language2 = getLanguageFromFilename(filePath);
|
|
448
|
+
let arity2;
|
|
449
|
+
let encTypeTag2 = '';
|
|
450
|
+
if ((finalLabel === 'Method' || finalLabel === 'Constructor') &&
|
|
451
|
+
provider.methodExtractor &&
|
|
452
|
+
language2) {
|
|
453
|
+
let classNode2 = (current.previousSibling ?? current).parent;
|
|
454
|
+
while (classNode2 && !provider.methodExtractor.isTypeDeclaration(classNode2)) {
|
|
455
|
+
classNode2 = classNode2.parent;
|
|
456
|
+
}
|
|
457
|
+
let info2;
|
|
458
|
+
if (classNode2) {
|
|
459
|
+
let extracted2 = enclosingFnExtractCache.get(classNode2.id);
|
|
460
|
+
if (extracted2 === undefined) {
|
|
461
|
+
extracted2 =
|
|
462
|
+
provider.methodExtractor.extract(classNode2, { filePath, language: language2 }) ??
|
|
463
|
+
null;
|
|
464
|
+
enclosingFnExtractCache.set(classNode2.id, extracted2);
|
|
465
|
+
}
|
|
466
|
+
if (extracted2?.methods?.length) {
|
|
467
|
+
const defLine2 = sigNode.startPosition.row + 1;
|
|
468
|
+
info2 = extracted2.methods.find((m) => m.name === customResult.funcName && m.line === defLine2);
|
|
469
|
+
if (info2) {
|
|
470
|
+
arity2 = info2.parameters.some((p) => p.isVariadic)
|
|
471
|
+
? undefined
|
|
472
|
+
: info2.parameters.length;
|
|
473
|
+
}
|
|
474
|
+
if (arity2 !== undefined && info2) {
|
|
475
|
+
const methodMap = new Map();
|
|
476
|
+
for (const m of extracted2.methods)
|
|
477
|
+
methodMap.set(`${m.name}:${m.line}`, m);
|
|
478
|
+
const groups2 = buildCollisionGroups(methodMap);
|
|
479
|
+
encTypeTag2 =
|
|
480
|
+
typeTagForId(methodMap, customResult.funcName, arity2, info2, language2, groups2) + constTagForId(methodMap, customResult.funcName, arity2, info2, groups2);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
if (!info2 && provider.methodExtractor.extractFromNode) {
|
|
485
|
+
const nodeInfo = provider.methodExtractor.extractFromNode(sigNode, {
|
|
486
|
+
filePath,
|
|
487
|
+
language: language2,
|
|
488
|
+
});
|
|
489
|
+
if (nodeInfo) {
|
|
490
|
+
arity2 = nodeInfo.parameters.some((p) => p.isVariadic)
|
|
491
|
+
? undefined
|
|
492
|
+
: nodeInfo.parameters.length;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
const arityTag2 = arity2 !== undefined ? `#${arity2}${encTypeTag2}` : '';
|
|
497
|
+
return generateId(finalLabel, `${filePath}:${qualifiedName}${arityTag2}`);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
current = current.parent;
|
|
501
|
+
}
|
|
502
|
+
return null;
|
|
503
|
+
};
|
|
504
|
+
/**
|
|
505
|
+
* Verify constructor bindings against SymbolTable and infer receiver types.
|
|
506
|
+
* Shared between sequential (processCalls) and worker (processCallsFromExtracted) paths.
|
|
507
|
+
*/
|
|
508
|
+
const verifyConstructorBindings = (bindings, filePath, ctx, graph, bindingAccumulator) => {
|
|
509
|
+
const verified = new Map();
|
|
510
|
+
for (const { scope, varName, calleeName, receiverClassName } of bindings) {
|
|
511
|
+
const tiered = ctx.resolve(calleeName, filePath);
|
|
512
|
+
const isClass = tiered?.candidates.some((def) => def.type === 'Class') ?? false;
|
|
513
|
+
if (isClass) {
|
|
514
|
+
verified.set(receiverKey(scope, varName), calleeName);
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
let callableDefs = tiered?.candidates.filter((d) => d.type === 'Function' || d.type === 'Method');
|
|
518
|
+
// When receiver class is known (e.g. $this->method() in PHP), narrow
|
|
519
|
+
// candidates to methods owned by that class to avoid false disambiguation failures.
|
|
520
|
+
if (callableDefs && callableDefs.length > 1 && receiverClassName) {
|
|
521
|
+
if (graph) {
|
|
522
|
+
// Worker path: use graph.getNode (fast, already in-memory)
|
|
523
|
+
const narrowed = callableDefs.filter((d) => {
|
|
524
|
+
if (!d.ownerId)
|
|
525
|
+
return false;
|
|
526
|
+
const owner = graph.getNode(d.ownerId);
|
|
527
|
+
return owner?.properties.name === receiverClassName;
|
|
528
|
+
});
|
|
529
|
+
if (narrowed.length > 0)
|
|
530
|
+
callableDefs = narrowed;
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
// Sequential path: use ctx.resolve (no graph available)
|
|
534
|
+
const classResolved = ctx.resolve(receiverClassName, filePath);
|
|
535
|
+
if (classResolved && classResolved.candidates.length > 0) {
|
|
536
|
+
const classNodeIds = new Set(classResolved.candidates.map((c) => c.nodeId));
|
|
537
|
+
const narrowed = callableDefs.filter((d) => d.ownerId && classNodeIds.has(d.ownerId));
|
|
538
|
+
if (narrowed.length > 0)
|
|
539
|
+
callableDefs = narrowed;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
let typeName;
|
|
544
|
+
if (callableDefs && callableDefs.length === 1 && callableDefs[0].returnType) {
|
|
545
|
+
typeName = extractReturnTypeName(callableDefs[0].returnType);
|
|
546
|
+
}
|
|
547
|
+
// Phase 9: BindingAccumulator fallback for cross-file return types.
|
|
548
|
+
// Used when the SymbolTable has no return type for a cross-file callee
|
|
549
|
+
// (e.g., a return type that TypeEnv resolved via fixpoint in the source
|
|
550
|
+
// file but was not stored as a SymbolTable returnType annotation).
|
|
551
|
+
// namedImportMap tells us which source file exported the callee so we
|
|
552
|
+
// can look up its file-scope binding via the O(1) fileScopeGet method.
|
|
553
|
+
//
|
|
554
|
+
// Tier gating: only fall back to the accumulator when resolution is
|
|
555
|
+
// unambiguously import-scoped or global. When tiered.tier is 'same-file',
|
|
556
|
+
// the local definition is authoritative even without a return type
|
|
557
|
+
// annotation — using the accumulator here would let an imported callee
|
|
558
|
+
// with the same name shadow the local one, producing false CALLS edges.
|
|
559
|
+
// When multiple callable candidates exist, the accumulator would pick
|
|
560
|
+
// arbitrarily — skip to avoid fabricated edges.
|
|
561
|
+
//
|
|
562
|
+
// Quality note: worker-path accumulator entries are Tier 0/1 only
|
|
563
|
+
// (annotation-declared + same-file constructor inference) — see the
|
|
564
|
+
// BindingAccumulator class JSDoc. For large repos where the worker
|
|
565
|
+
// path dominates, Phase 9 binding accuracy is structurally lower
|
|
566
|
+
// than for sequential-path repos where Tier 2 cross-file propagation
|
|
567
|
+
// is available.
|
|
568
|
+
//
|
|
569
|
+
// Overlapping mechanism note: this is one of three cross-file
|
|
570
|
+
// return-type resolution paths in the codebase:
|
|
571
|
+
// 1. buildImportedReturnTypes (~line 109) — namedImportMap +
|
|
572
|
+
// SymbolTable.lookupExactFull (structure-processor captured)
|
|
573
|
+
// 2. collectExportedBindings (~line 168) / enrichExportedTypeMap
|
|
574
|
+
// — TypeEnv + graph isExported flag
|
|
575
|
+
// 3. This fallback — namedImportMap + BindingAccumulator
|
|
576
|
+
// A future cleanup should merge these into a single resolution pass.
|
|
577
|
+
const shouldFallback = tiered?.tier !== 'same-file' && (!callableDefs || callableDefs.length <= 1);
|
|
578
|
+
if (!typeName && bindingAccumulator && shouldFallback) {
|
|
579
|
+
const namedImports = ctx.namedImportMap.get(filePath);
|
|
580
|
+
const importBinding = namedImports?.get(calleeName);
|
|
581
|
+
if (importBinding) {
|
|
582
|
+
const rawType = bindingAccumulator.fileScopeGet(importBinding.sourcePath, importBinding.exportedName);
|
|
583
|
+
if (rawType) {
|
|
584
|
+
typeName = extractReturnTypeName(rawType);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
if (typeName) {
|
|
589
|
+
verified.set(receiverKey(scope, varName), typeName);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return verified;
|
|
594
|
+
};
|
|
595
|
+
/**
|
|
596
|
+
* After resolving a call to an interface method, find additional targets
|
|
597
|
+
* in classes implementing that interface. Returns implementation method
|
|
598
|
+
* results with lower confidence ('interface-dispatch').
|
|
599
|
+
*/
|
|
600
|
+
function findInterfaceDispatchTargets(calledName, receiverTypeName, currentFile, ctx, heritageMap, primaryNodeId) {
|
|
601
|
+
const implFiles = heritageMap.getImplementorFiles(receiverTypeName);
|
|
602
|
+
if (implFiles.size === 0)
|
|
603
|
+
return [];
|
|
604
|
+
const typeResolved = ctx.resolve(receiverTypeName, currentFile);
|
|
605
|
+
if (!typeResolved)
|
|
606
|
+
return [];
|
|
607
|
+
if (!typeResolved.candidates.some((c) => c.type === 'Interface'))
|
|
608
|
+
return [];
|
|
609
|
+
const results = [];
|
|
610
|
+
for (const implFile of implFiles) {
|
|
611
|
+
const methods = ctx.model.symbols.lookupExactAll(implFile, calledName);
|
|
612
|
+
for (const method of methods) {
|
|
613
|
+
if (method.nodeId !== primaryNodeId) {
|
|
614
|
+
results.push({
|
|
615
|
+
nodeId: method.nodeId,
|
|
616
|
+
confidence: 0.7,
|
|
617
|
+
reason: 'interface-dispatch',
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
return results;
|
|
623
|
+
}
|
|
624
|
+
export const processCalls = async (graph, files, astCache, ctx, onProgress, exportedTypeMap,
|
|
625
|
+
/** Phase 14: pre-resolved cross-file bindings to seed into buildTypeEnv. Keyed by filePath → Map<localName, typeName>. */
|
|
626
|
+
importedBindingsMap,
|
|
627
|
+
/** Phase 14 E3: cross-file return types for imported callables. Keyed by filePath → Map<calleeName, returnType>.
|
|
628
|
+
* Consulted ONLY when SymbolTable has no unambiguous match (local-first principle). */
|
|
629
|
+
importedReturnTypesMap,
|
|
630
|
+
/** Phase 14 E3: cross-file RAW return types for for-loop element extraction. Keyed by filePath → Map<calleeName, rawReturnType>. */
|
|
631
|
+
importedRawReturnTypesMap, heritageMap, bindingAccumulator) => {
|
|
632
|
+
const parser = await loadParser();
|
|
633
|
+
const collectedHeritage = [];
|
|
634
|
+
const pendingWrites = [];
|
|
635
|
+
// Phase P cross-file: accumulate heritage across files for cross-file isSubclassOf.
|
|
636
|
+
// Used as a secondary check when per-file parentMap lacks the relationship — helps
|
|
637
|
+
// when the heritage-declaring file is processed before the call site file.
|
|
638
|
+
// For remaining cases (reverse file order), the SymbolTable class-type fallback applies.
|
|
639
|
+
const globalParentMap = new Map();
|
|
640
|
+
const globalParentSeen = new Map();
|
|
641
|
+
const logSkipped = isVerboseIngestionEnabled();
|
|
642
|
+
const skippedByLang = logSkipped ? new Map() : null;
|
|
643
|
+
const prepared = [];
|
|
644
|
+
for (let i = 0; i < files.length; i++) {
|
|
645
|
+
const file = files[i];
|
|
646
|
+
if (i % 20 === 0)
|
|
647
|
+
await yieldToEventLoop();
|
|
648
|
+
const language = getLanguageFromFilename(file.path);
|
|
649
|
+
if (!language)
|
|
650
|
+
continue;
|
|
651
|
+
// Registry-primary gate: scope-based phase owns CALLS for this lang.
|
|
652
|
+
if (isRegistryPrimary(language))
|
|
653
|
+
continue;
|
|
654
|
+
if (!isLanguageAvailable(language)) {
|
|
655
|
+
if (skippedByLang) {
|
|
656
|
+
skippedByLang.set(language, (skippedByLang.get(language) ?? 0) + 1);
|
|
657
|
+
}
|
|
658
|
+
continue;
|
|
659
|
+
}
|
|
660
|
+
const provider = getProvider(language);
|
|
661
|
+
const queryStr = provider.treeSitterQueries;
|
|
662
|
+
if (!queryStr)
|
|
663
|
+
continue;
|
|
664
|
+
await loadLanguage(language, file.path);
|
|
665
|
+
let tree = astCache.get(file.path);
|
|
666
|
+
if (!tree) {
|
|
667
|
+
const parseContent = provider.preprocessSource?.(file.content, file.path) ?? file.content;
|
|
668
|
+
try {
|
|
669
|
+
tree = parseSourceSafe(parser, parseContent, undefined, {
|
|
670
|
+
bufferSize: getTreeSitterBufferSize(parseContent),
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
catch (parseError) {
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
676
|
+
astCache.set(file.path, tree);
|
|
677
|
+
}
|
|
678
|
+
let matches;
|
|
679
|
+
try {
|
|
680
|
+
const lang = parser.getLanguage();
|
|
681
|
+
const query = new Parser.Query(lang, queryStr);
|
|
682
|
+
matches = query.matches(tree.rootNode);
|
|
683
|
+
}
|
|
684
|
+
catch (queryError) {
|
|
685
|
+
logger.warn({ queryError }, `Query error for ${file.path}:`);
|
|
686
|
+
continue;
|
|
687
|
+
}
|
|
688
|
+
// Extract heritage from query matches to build parentMap for buildTypeEnv.
|
|
689
|
+
// Heritage-processor runs in PARALLEL, so graph edges don't exist when buildTypeEnv runs.
|
|
690
|
+
const fileParentMap = new Map();
|
|
691
|
+
if (provider.heritageExtractor) {
|
|
692
|
+
for (const match of matches) {
|
|
693
|
+
const captureMap = {};
|
|
694
|
+
match.captures.forEach((c) => (captureMap[c.name] = c.node));
|
|
695
|
+
if (captureMap['heritage.class']) {
|
|
696
|
+
const heritageItems = provider.heritageExtractor.extract(captureMap, {
|
|
697
|
+
filePath: file.path,
|
|
698
|
+
language,
|
|
699
|
+
});
|
|
700
|
+
for (const item of heritageItems) {
|
|
701
|
+
if (item.kind === 'extends') {
|
|
702
|
+
let parents = fileParentMap.get(item.className);
|
|
703
|
+
if (!parents) {
|
|
704
|
+
parents = [];
|
|
705
|
+
fileParentMap.set(item.className, parents);
|
|
706
|
+
}
|
|
707
|
+
if (!parents.includes(item.parentName))
|
|
708
|
+
parents.push(item.parentName);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
const parentMap = fileParentMap;
|
|
715
|
+
// Merge per-file heritage into globalParentMap for cross-file isSubclassOf lookups.
|
|
716
|
+
for (const [cls, parents] of fileParentMap) {
|
|
717
|
+
let global = globalParentMap.get(cls);
|
|
718
|
+
let seen = globalParentSeen.get(cls);
|
|
719
|
+
if (!global) {
|
|
720
|
+
global = [];
|
|
721
|
+
globalParentMap.set(cls, global);
|
|
722
|
+
}
|
|
723
|
+
if (!seen) {
|
|
724
|
+
seen = new Set();
|
|
725
|
+
globalParentSeen.set(cls, seen);
|
|
726
|
+
}
|
|
727
|
+
for (const p of parents) {
|
|
728
|
+
if (!seen.has(p)) {
|
|
729
|
+
seen.add(p);
|
|
730
|
+
global.push(p);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
const importedBindings = importedBindingsMap?.get(file.path);
|
|
735
|
+
const importedReturnTypes = importedReturnTypesMap?.get(file.path);
|
|
736
|
+
const importedRawReturnTypes = importedRawReturnTypesMap?.get(file.path);
|
|
737
|
+
const typeEnv = buildTypeEnv(tree, language, {
|
|
738
|
+
model: ctx.model,
|
|
739
|
+
parentMap,
|
|
740
|
+
importedBindings,
|
|
741
|
+
importedReturnTypes,
|
|
742
|
+
importedRawReturnTypes,
|
|
743
|
+
enclosingFunctionFinder: provider?.enclosingFunctionFinder,
|
|
744
|
+
extractFunctionName: provider?.methodExtractor?.extractFunctionName,
|
|
745
|
+
});
|
|
746
|
+
if (typeEnv && exportedTypeMap) {
|
|
747
|
+
const fileExports = collectExportedBindings(typeEnv, file.path, ctx.model.symbols, graph);
|
|
748
|
+
if (fileExports)
|
|
749
|
+
exportedTypeMap.set(file.path, fileExports);
|
|
750
|
+
}
|
|
751
|
+
if (bindingAccumulator) {
|
|
752
|
+
typeEnv.flush(file.path, bindingAccumulator);
|
|
753
|
+
}
|
|
754
|
+
prepared.push({ file, language, provider, tree, matches, parentMap, typeEnv });
|
|
755
|
+
}
|
|
756
|
+
// ── Property-registration pre-pass ──
|
|
757
|
+
// Register all routed properties (e.g. Ruby attr_accessor) BEFORE the
|
|
758
|
+
// resolution loop so cross-file field-type lookups (e.g.
|
|
759
|
+
// `user.address.save → Address#save`) succeed regardless of file
|
|
760
|
+
// processing order. This MUST stay in lockstep with the equivalent
|
|
761
|
+
// worker-path block in parse-worker.ts (kind === 'properties') — any
|
|
762
|
+
// divergence between the two paths breaks the `incremental ≡ --force`
|
|
763
|
+
// invariant once a repo crosses the worker threshold between runs.
|
|
764
|
+
const fieldInfoCache = new Map();
|
|
765
|
+
for (const { file, language, provider, matches, typeEnv } of prepared) {
|
|
766
|
+
const callRouter = provider.callRouter;
|
|
767
|
+
if (!callRouter)
|
|
768
|
+
continue;
|
|
769
|
+
matches.forEach((match) => {
|
|
770
|
+
const captureMap = {};
|
|
771
|
+
match.captures.forEach((c) => (captureMap[c.name] = c.node));
|
|
772
|
+
if (!captureMap['call'])
|
|
773
|
+
return;
|
|
774
|
+
const callNameNode = captureMap['call.name'];
|
|
775
|
+
if (!callNameNode)
|
|
776
|
+
return;
|
|
777
|
+
const routed = callRouter(callNameNode.text, captureMap['call']);
|
|
778
|
+
if (!routed || routed.kind !== 'properties')
|
|
779
|
+
return;
|
|
780
|
+
const propEnclosingInfo = findEnclosingClassInfo(captureMap['call'], file.path, provider.resolveEnclosingOwner);
|
|
781
|
+
const propEnclosingClassId = propEnclosingInfo?.classId ?? null;
|
|
782
|
+
// Enrich routed properties with FieldExtractor metadata so types
|
|
783
|
+
// discovered from constructor assignments (e.g. `@address = Address.new`)
|
|
784
|
+
// are propagated even when the routing payload itself lacks declaredType.
|
|
785
|
+
let routedFieldMap;
|
|
786
|
+
if (provider.fieldExtractor && typeEnv) {
|
|
787
|
+
const classNode = findEnclosingClassNode(captureMap['call']);
|
|
788
|
+
if (classNode) {
|
|
789
|
+
routedFieldMap = getFieldInfo(classNode, provider, {
|
|
790
|
+
typeEnv,
|
|
791
|
+
symbolTable: NOOP_SYMBOL_TABLE,
|
|
792
|
+
filePath: file.path,
|
|
793
|
+
language,
|
|
794
|
+
}, fieldInfoCache);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
const fileId = generateId('File', file.path);
|
|
798
|
+
for (const item of routed.items) {
|
|
799
|
+
const routedFieldInfo = routedFieldMap?.get(item.propName);
|
|
800
|
+
const propQualifiedName = propEnclosingInfo
|
|
801
|
+
? `${propEnclosingInfo.className}.${item.propName}`
|
|
802
|
+
: item.propName;
|
|
803
|
+
const nodeId = generateId('Property', `${file.path}:${propQualifiedName}`);
|
|
804
|
+
graph.addNode({
|
|
805
|
+
id: nodeId,
|
|
806
|
+
label: 'Property',
|
|
807
|
+
properties: {
|
|
808
|
+
name: item.propName,
|
|
809
|
+
filePath: file.path,
|
|
810
|
+
startLine: item.startLine,
|
|
811
|
+
endLine: item.endLine,
|
|
812
|
+
language,
|
|
813
|
+
isExported: true,
|
|
814
|
+
description: item.accessorType,
|
|
815
|
+
...(item.declaredType
|
|
816
|
+
? { declaredType: item.declaredType }
|
|
817
|
+
: routedFieldInfo?.type
|
|
818
|
+
? { declaredType: routedFieldInfo.type }
|
|
819
|
+
: {}),
|
|
820
|
+
...(routedFieldInfo?.visibility !== undefined
|
|
821
|
+
? { visibility: routedFieldInfo.visibility }
|
|
822
|
+
: {}),
|
|
823
|
+
...(routedFieldInfo?.isStatic !== undefined
|
|
824
|
+
? { isStatic: routedFieldInfo.isStatic }
|
|
825
|
+
: {}),
|
|
826
|
+
...(routedFieldInfo?.isReadonly !== undefined
|
|
827
|
+
? { isReadonly: routedFieldInfo.isReadonly }
|
|
828
|
+
: {}),
|
|
829
|
+
},
|
|
830
|
+
});
|
|
831
|
+
ctx.model.symbols.add(file.path, item.propName, nodeId, 'Property', {
|
|
832
|
+
...(propEnclosingClassId ? { ownerId: propEnclosingClassId } : {}),
|
|
833
|
+
...(item.declaredType
|
|
834
|
+
? { declaredType: item.declaredType }
|
|
835
|
+
: routedFieldInfo?.type
|
|
836
|
+
? { declaredType: routedFieldInfo.type }
|
|
837
|
+
: {}),
|
|
838
|
+
});
|
|
839
|
+
const relId = generateId('DEFINES', `${fileId}->${nodeId}`);
|
|
840
|
+
graph.addRelationship({
|
|
841
|
+
id: relId,
|
|
842
|
+
sourceId: fileId,
|
|
843
|
+
targetId: nodeId,
|
|
844
|
+
type: 'DEFINES',
|
|
845
|
+
confidence: 1.0,
|
|
846
|
+
reason: '',
|
|
847
|
+
});
|
|
848
|
+
if (propEnclosingClassId) {
|
|
849
|
+
graph.addRelationship({
|
|
850
|
+
id: generateId('HAS_PROPERTY', `${propEnclosingClassId}->${nodeId}`),
|
|
851
|
+
sourceId: propEnclosingClassId,
|
|
852
|
+
targetId: nodeId,
|
|
853
|
+
type: 'HAS_PROPERTY',
|
|
854
|
+
confidence: 1.0,
|
|
855
|
+
reason: '',
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
// ── Resolution loop: verify constructor bindings and resolve calls ──
|
|
862
|
+
// The accumulator (if present) is now fully populated from the preparation
|
|
863
|
+
// loop above, so verifyConstructorBindings sees all provider bindings
|
|
864
|
+
// regardless of file processing order.
|
|
865
|
+
for (let i = 0; i < prepared.length; i++) {
|
|
866
|
+
const { file, language, provider, tree, matches, parentMap, typeEnv } = prepared[i];
|
|
867
|
+
enclosingFnExtractCache.clear();
|
|
868
|
+
onProgress?.(i + 1, files.length);
|
|
869
|
+
if (i % 20 === 0)
|
|
870
|
+
await yieldToEventLoop();
|
|
871
|
+
const callRouter = provider.callRouter;
|
|
872
|
+
const verifiedReceivers = typeEnv.constructorBindings.length > 0
|
|
873
|
+
? verifyConstructorBindings(typeEnv.constructorBindings, file.path, ctx, undefined, // graph not available on the sequential path here
|
|
874
|
+
bindingAccumulator)
|
|
875
|
+
: new Map();
|
|
876
|
+
const receiverIndex = buildReceiverTypeIndex(verifiedReceivers);
|
|
877
|
+
ctx.enableCache(file.path);
|
|
878
|
+
const widenCache = new Map();
|
|
879
|
+
matches.forEach((match) => {
|
|
880
|
+
const captureMap = {};
|
|
881
|
+
match.captures.forEach((c) => (captureMap[c.name] = c.node));
|
|
882
|
+
// ── Write access: emit ACCESSES {reason: 'write'} for assignments to member fields ──
|
|
883
|
+
if (captureMap['assignment'] &&
|
|
884
|
+
captureMap['assignment.receiver'] &&
|
|
885
|
+
captureMap['assignment.property']) {
|
|
886
|
+
const receiverNode = captureMap['assignment.receiver'];
|
|
887
|
+
const propertyName = captureMap['assignment.property'].text;
|
|
888
|
+
// Resolve receiver type: simple identifier → TypeEnv lookup or class resolution
|
|
889
|
+
let receiverTypeName;
|
|
890
|
+
const receiverText = receiverNode.text;
|
|
891
|
+
if (receiverText && typeEnv) {
|
|
892
|
+
receiverTypeName = typeEnv.lookup(receiverText, captureMap['assignment']);
|
|
893
|
+
}
|
|
894
|
+
// Fall back to verified constructor bindings (mirrors CALLS resolution tier 2)
|
|
895
|
+
if (!receiverTypeName && receiverText && receiverIndex.size > 0) {
|
|
896
|
+
const enclosing = findEnclosingFunction(captureMap['assignment'], file.path, ctx, provider);
|
|
897
|
+
const funcName = enclosing ? extractFuncNameFromSourceId(enclosing) : '';
|
|
898
|
+
receiverTypeName = lookupReceiverType(receiverIndex, funcName, receiverText);
|
|
899
|
+
}
|
|
900
|
+
if (!receiverTypeName && receiverText) {
|
|
901
|
+
const resolved = ctx.resolve(receiverText, file.path);
|
|
902
|
+
if (resolved?.candidates.some((d) => CLASS_LIKE_TYPES.has(d.type))) {
|
|
903
|
+
receiverTypeName = receiverText;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
if (receiverTypeName) {
|
|
907
|
+
const enclosing = findEnclosingFunction(captureMap['assignment'], file.path, ctx, provider);
|
|
908
|
+
const srcId = enclosing || generateId('File', file.path);
|
|
909
|
+
// Defer resolution so write-access tracking sees the FINAL graph
|
|
910
|
+
// state — properties from the pre-pass are present, but receiver-type
|
|
911
|
+
// resolution can still depend on inference that completes during the
|
|
912
|
+
// main loop. Resolve after all files have been processed.
|
|
913
|
+
pendingWrites.push({ receiverTypeName, propertyName, filePath: file.path, srcId });
|
|
914
|
+
}
|
|
915
|
+
// Assignment-only capture (no @call sibling): skip the rest of this
|
|
916
|
+
// forEach iteration — this acts as a `continue` in the match loop.
|
|
917
|
+
if (!captureMap['call'])
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
if (!captureMap['call'])
|
|
921
|
+
return;
|
|
922
|
+
const callNode = captureMap['call'];
|
|
923
|
+
const callExtractor = provider.callExtractor;
|
|
924
|
+
// ── Language-specific call site (e.g. Java :: method references) ──
|
|
925
|
+
if (callExtractor) {
|
|
926
|
+
const langCallSite = callExtractor.extract(callNode, undefined);
|
|
927
|
+
if (langCallSite) {
|
|
928
|
+
if (provider.isBuiltInName(langCallSite.calledName))
|
|
929
|
+
return;
|
|
930
|
+
const sourceId = findEnclosingFunction(callNode, file.path, ctx, provider) ||
|
|
931
|
+
generateId('File', file.path);
|
|
932
|
+
const receiverName = langCallSite.callForm === 'member' ? langCallSite.receiverName : undefined;
|
|
933
|
+
let receiverTypeName = receiverName && typeEnv ? typeEnv.lookup(receiverName, callNode) : undefined;
|
|
934
|
+
if (langCallSite.typeAsReceiverHeuristic &&
|
|
935
|
+
receiverName !== undefined &&
|
|
936
|
+
receiverTypeName === undefined &&
|
|
937
|
+
langCallSite.callForm === 'member') {
|
|
938
|
+
const c0 = receiverName.charCodeAt(0);
|
|
939
|
+
if (c0 >= 65 && c0 <= 90)
|
|
940
|
+
receiverTypeName = receiverName;
|
|
941
|
+
}
|
|
942
|
+
const resolved = resolveCallTarget({
|
|
943
|
+
calledName: langCallSite.calledName,
|
|
944
|
+
callForm: langCallSite.callForm,
|
|
945
|
+
...(receiverTypeName !== undefined ? { receiverTypeName } : {}),
|
|
946
|
+
...(receiverName !== undefined ? { receiverName } : {}),
|
|
947
|
+
}, file.path, ctx, undefined, widenCache, undefined, heritageMap);
|
|
948
|
+
if (!resolved)
|
|
949
|
+
return;
|
|
950
|
+
graph.addRelationship({
|
|
951
|
+
id: generateId('CALLS', `${sourceId}:${langCallSite.calledName}->${resolved.nodeId}`),
|
|
952
|
+
sourceId,
|
|
953
|
+
targetId: resolved.nodeId,
|
|
954
|
+
type: 'CALLS',
|
|
955
|
+
confidence: resolved.confidence,
|
|
956
|
+
reason: resolved.reason,
|
|
957
|
+
});
|
|
958
|
+
if (heritageMap && langCallSite.callForm === 'member' && receiverTypeName) {
|
|
959
|
+
const implTargets = findInterfaceDispatchTargets(langCallSite.calledName, receiverTypeName, file.path, ctx, heritageMap, resolved.nodeId);
|
|
960
|
+
for (const impl of implTargets) {
|
|
961
|
+
graph.addRelationship({
|
|
962
|
+
id: generateId('CALLS', `${sourceId}:${langCallSite.calledName}->${impl.nodeId}`),
|
|
963
|
+
sourceId,
|
|
964
|
+
targetId: impl.nodeId,
|
|
965
|
+
type: 'CALLS',
|
|
966
|
+
confidence: impl.confidence,
|
|
967
|
+
reason: impl.reason,
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
const nameNode = captureMap['call.name'];
|
|
975
|
+
if (!nameNode)
|
|
976
|
+
return;
|
|
977
|
+
const calledName = nameNode.text;
|
|
978
|
+
// Check heritage extractor for call-based heritage (e.g., Ruby include/extend/prepend)
|
|
979
|
+
if (provider.heritageExtractor?.extractFromCall) {
|
|
980
|
+
const heritageItems = provider.heritageExtractor.extractFromCall(calledName, captureMap['call'], { filePath: file.path, language });
|
|
981
|
+
if (heritageItems !== null) {
|
|
982
|
+
for (const item of heritageItems) {
|
|
983
|
+
collectedHeritage.push({
|
|
984
|
+
filePath: file.path,
|
|
985
|
+
className: item.className,
|
|
986
|
+
parentName: item.parentName,
|
|
987
|
+
kind: item.kind,
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
// Dispatch: route language-specific calls (properties, imports)
|
|
994
|
+
// Heritage routing is handled by heritageExtractor.extractFromCall above.
|
|
995
|
+
const routed = callRouter?.(calledName, captureMap['call']);
|
|
996
|
+
if (routed) {
|
|
997
|
+
switch (routed.kind) {
|
|
998
|
+
case 'skip':
|
|
999
|
+
case 'import':
|
|
1000
|
+
return;
|
|
1001
|
+
case 'properties': {
|
|
1002
|
+
// Properties already registered in the pre-pass above.
|
|
1003
|
+
// Skip to avoid duplicate nodes/edges.
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
case 'call':
|
|
1007
|
+
break;
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
if (provider.isBuiltInName(calledName))
|
|
1011
|
+
return;
|
|
1012
|
+
// --- DAG stage 2-3: classify-form + infer-receiver (shared defaults) ---
|
|
1013
|
+
// These stages run the shared inference chain. Language providers can
|
|
1014
|
+
// customize infer-receiver (stage 3) via the inferImplicitReceiver hook
|
|
1015
|
+
// which runs AFTER this default chain (typed-binding → constructor-map →
|
|
1016
|
+
// module-alias → class-as-receiver → mixed-chain), and selectDispatch
|
|
1017
|
+
// (stage 4) which picks the resolver branch.
|
|
1018
|
+
let callForm = inferCallForm(callNode, nameNode);
|
|
1019
|
+
let receiverName = callForm === 'member' ? extractReceiverName(nameNode) : undefined;
|
|
1020
|
+
let receiverTypeName = receiverName && typeEnv ? typeEnv.lookup(receiverName, callNode) : undefined;
|
|
1021
|
+
let receiverSource = receiverTypeName ? 'typed-binding' : 'none';
|
|
1022
|
+
// Phase P: virtual dispatch override — when the declared type is a base class but
|
|
1023
|
+
// the constructor created a known subclass, prefer the more specific type.
|
|
1024
|
+
// Checks per-file parentMap first, then falls back to globalParentMap for
|
|
1025
|
+
// cross-file heritage (e.g. Dog extends Animal declared in a different file).
|
|
1026
|
+
// Reconstructs the exact scope key (funcName@startIndex\0varName) from the
|
|
1027
|
+
// enclosing function AST node for a correct, O(1) map lookup.
|
|
1028
|
+
if (receiverTypeName && receiverName && typeEnv && typeEnv.constructorTypeMap.size > 0) {
|
|
1029
|
+
// Reconstruct scope key to match constructorTypeMap's scope\0varName format
|
|
1030
|
+
let scope = '';
|
|
1031
|
+
let p = callNode.parent;
|
|
1032
|
+
while (p) {
|
|
1033
|
+
if (FUNCTION_NODE_TYPES.has(p.type)) {
|
|
1034
|
+
const funcName = provider.methodExtractor?.extractFunctionName?.(p)?.funcName ?? genericFuncName(p);
|
|
1035
|
+
if (funcName) {
|
|
1036
|
+
scope = `${funcName}@${p.startIndex}`;
|
|
1037
|
+
break;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
p = p.parent;
|
|
1041
|
+
}
|
|
1042
|
+
const ctorType = typeEnv.constructorTypeMap.get(`${scope}\0${receiverName}`);
|
|
1043
|
+
if (ctorType && ctorType !== receiverTypeName) {
|
|
1044
|
+
// Verify subclass relationship: per-file parentMap first, then cross-file
|
|
1045
|
+
// globalParentMap, then fall back to SymbolTable class verification.
|
|
1046
|
+
// The SymbolTable fallback handles cross-file cases where heritage is declared
|
|
1047
|
+
// in a file not yet processed (e.g. Dog extends Animal in models/Dog.kt when
|
|
1048
|
+
// processing services/App.kt). Since constructorTypeMap only records entries
|
|
1049
|
+
// when a type annotation AND constructor are both present (val x: Base = Sub()),
|
|
1050
|
+
// confirming both are class-like types is sufficient — the original code would
|
|
1051
|
+
// not compile if Sub didn't extend Base.
|
|
1052
|
+
if (isSubclassOf(ctorType, receiverTypeName, parentMap) ||
|
|
1053
|
+
isSubclassOf(ctorType, receiverTypeName, globalParentMap) ||
|
|
1054
|
+
(ctx.model.types.lookupClassByName(ctorType).length > 0 &&
|
|
1055
|
+
ctx.model.types.lookupClassByName(receiverTypeName).length > 0)) {
|
|
1056
|
+
receiverTypeName = ctorType;
|
|
1057
|
+
receiverSource = 'constructor-map';
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
// Fall back to verified constructor bindings for return type inference
|
|
1062
|
+
if (!receiverTypeName && receiverName && receiverIndex.size > 0) {
|
|
1063
|
+
const enclosingFunc = findEnclosingFunction(callNode, file.path, ctx, provider);
|
|
1064
|
+
const funcName = enclosingFunc ? extractFuncNameFromSourceId(enclosingFunc) : '';
|
|
1065
|
+
receiverTypeName = lookupReceiverType(receiverIndex, funcName, receiverName);
|
|
1066
|
+
if (receiverTypeName)
|
|
1067
|
+
receiverSource = 'constructor-map';
|
|
1068
|
+
}
|
|
1069
|
+
// Fall back to class-as-receiver for static method calls (e.g. UserService.find_user(),
|
|
1070
|
+
// Greetable.format()). When the receiver name is not a variable in TypeEnv but
|
|
1071
|
+
// resolves to a class-like symbol (Class / Interface / Struct / Enum / Trait) via
|
|
1072
|
+
// tiered resolution, use it directly as the receiver type. `Trait` is included so
|
|
1073
|
+
// Ruby module class-method calls flow through the class-as-receiver path and reach
|
|
1074
|
+
// the `selectDispatch` hook's singleton branch.
|
|
1075
|
+
if (!receiverTypeName && receiverName && callForm === 'member') {
|
|
1076
|
+
const typeResolved = ctx.resolve(receiverName, file.path);
|
|
1077
|
+
if (typeResolved &&
|
|
1078
|
+
typeResolved.candidates.some((d) => d.type === 'Class' ||
|
|
1079
|
+
d.type === 'Interface' ||
|
|
1080
|
+
d.type === 'Struct' ||
|
|
1081
|
+
d.type === 'Enum' ||
|
|
1082
|
+
d.type === 'Trait')) {
|
|
1083
|
+
receiverTypeName = receiverName;
|
|
1084
|
+
receiverSource = 'class-as-receiver';
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
// Hoist sourceId so it's available for ACCESSES edge emission during chain walk.
|
|
1088
|
+
const enclosingFuncId = findEnclosingFunction(callNode, file.path, ctx, provider);
|
|
1089
|
+
const sourceId = enclosingFuncId || generateId('File', file.path);
|
|
1090
|
+
// Fall back to mixed chain resolution when the receiver is a complex expression
|
|
1091
|
+
// (field chain, call chain, or interleaved — e.g. user.address.city.save() or
|
|
1092
|
+
// svc.getUser().address.save()). Handles all cases with a single unified walk.
|
|
1093
|
+
if (callForm === 'member' && !receiverTypeName && !receiverName) {
|
|
1094
|
+
const receiverNode = extractReceiverNode(nameNode);
|
|
1095
|
+
if (receiverNode) {
|
|
1096
|
+
const extracted = extractMixedChain(receiverNode);
|
|
1097
|
+
if (extracted && extracted.chain.length > 0) {
|
|
1098
|
+
let currentType = extracted.baseReceiverName && typeEnv
|
|
1099
|
+
? typeEnv.lookup(extracted.baseReceiverName, callNode)
|
|
1100
|
+
: undefined;
|
|
1101
|
+
if (!currentType && extracted.baseReceiverName && receiverIndex.size > 0) {
|
|
1102
|
+
const funcName = enclosingFuncId ? extractFuncNameFromSourceId(enclosingFuncId) : '';
|
|
1103
|
+
currentType = lookupReceiverType(receiverIndex, funcName, extracted.baseReceiverName);
|
|
1104
|
+
}
|
|
1105
|
+
if (!currentType && extracted.baseReceiverName) {
|
|
1106
|
+
const cr = ctx.resolve(extracted.baseReceiverName, file.path);
|
|
1107
|
+
if (cr?.candidates.some((d) => d.type === 'Class' ||
|
|
1108
|
+
d.type === 'Interface' ||
|
|
1109
|
+
d.type === 'Struct' ||
|
|
1110
|
+
d.type === 'Enum')) {
|
|
1111
|
+
currentType = extracted.baseReceiverName;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
if (currentType) {
|
|
1115
|
+
receiverTypeName = walkMixedChain(extracted.chain, currentType, file.path, ctx, makeAccessEmitter(graph, sourceId), heritageMap);
|
|
1116
|
+
if (receiverTypeName)
|
|
1117
|
+
receiverSource = 'mixed-chain';
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
// --- DAG stage 3: infer-receiver (provider hook) ---
|
|
1123
|
+
// Synthesize implicit receivers for languages that omit them (e.g., Ruby bare-call).
|
|
1124
|
+
// This hook runs AFTER the shared inference chain so explicit receivers /
|
|
1125
|
+
// typed bindings always take precedence. Output (if non-null) overlays onto
|
|
1126
|
+
// the ReceiverEnriched for the next stage.
|
|
1127
|
+
let dispatchHint;
|
|
1128
|
+
if (provider.inferImplicitReceiver) {
|
|
1129
|
+
const override = provider.inferImplicitReceiver({
|
|
1130
|
+
calledName,
|
|
1131
|
+
callForm,
|
|
1132
|
+
receiverName,
|
|
1133
|
+
receiverTypeName,
|
|
1134
|
+
callNode,
|
|
1135
|
+
filePath: file.path,
|
|
1136
|
+
});
|
|
1137
|
+
if (override) {
|
|
1138
|
+
callForm = override.callForm;
|
|
1139
|
+
receiverName = override.receiverName;
|
|
1140
|
+
receiverTypeName = override.receiverTypeName;
|
|
1141
|
+
receiverSource = override.receiverSource;
|
|
1142
|
+
dispatchHint = override.hint;
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
// --- DAG stage 4: select-dispatch (provider hook + default fallback) ---
|
|
1146
|
+
// Decide which resolver path to try first (primary) and fallback strategy.
|
|
1147
|
+
// Language providers can customize dispatch via selectDispatch hook; all
|
|
1148
|
+
// others use the shared defaultDispatchDecision. Always non-null after this
|
|
1149
|
+
// block so downstream resolvers are table-driven.
|
|
1150
|
+
const dispatchDecision = provider.selectDispatch?.({
|
|
1151
|
+
calledName,
|
|
1152
|
+
callForm,
|
|
1153
|
+
receiverName,
|
|
1154
|
+
receiverTypeName,
|
|
1155
|
+
receiverSource,
|
|
1156
|
+
hint: dispatchHint,
|
|
1157
|
+
}) ?? defaultDispatchDecision(callForm);
|
|
1158
|
+
// Build overload hints for languages with inferLiteralType (Java/Kotlin/C#/C++).
|
|
1159
|
+
// Only used when multiple candidates survive arity filtering — ~1-3% of calls.
|
|
1160
|
+
const langConfig = provider.typeConfig;
|
|
1161
|
+
const hints = langConfig?.inferLiteralType
|
|
1162
|
+
? { callNode, inferLiteralType: langConfig.inferLiteralType, typeEnv }
|
|
1163
|
+
: undefined;
|
|
1164
|
+
const resolved = resolveCallTarget({
|
|
1165
|
+
calledName,
|
|
1166
|
+
argCount: countCallArguments(callNode),
|
|
1167
|
+
callForm,
|
|
1168
|
+
receiverTypeName,
|
|
1169
|
+
receiverName,
|
|
1170
|
+
}, file.path, ctx, hints, widenCache, undefined, heritageMap, dispatchDecision);
|
|
1171
|
+
if (!resolved)
|
|
1172
|
+
return;
|
|
1173
|
+
const relId = generateId('CALLS', `${sourceId}:${calledName}->${resolved.nodeId}`);
|
|
1174
|
+
graph.addRelationship({
|
|
1175
|
+
id: relId,
|
|
1176
|
+
sourceId,
|
|
1177
|
+
targetId: resolved.nodeId,
|
|
1178
|
+
type: 'CALLS',
|
|
1179
|
+
confidence: resolved.confidence,
|
|
1180
|
+
reason: resolved.reason,
|
|
1181
|
+
});
|
|
1182
|
+
if (heritageMap && callForm === 'member' && receiverTypeName) {
|
|
1183
|
+
const implTargets = findInterfaceDispatchTargets(calledName, receiverTypeName, file.path, ctx, heritageMap, resolved.nodeId);
|
|
1184
|
+
for (const impl of implTargets) {
|
|
1185
|
+
graph.addRelationship({
|
|
1186
|
+
id: generateId('CALLS', `${sourceId}:${calledName}->${impl.nodeId}`),
|
|
1187
|
+
sourceId,
|
|
1188
|
+
targetId: impl.nodeId,
|
|
1189
|
+
type: 'CALLS',
|
|
1190
|
+
confidence: impl.confidence,
|
|
1191
|
+
reason: impl.reason,
|
|
1192
|
+
});
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
});
|
|
1196
|
+
// Vue: emit CALLS edges for PascalCase components used in <template>.
|
|
1197
|
+
// Template components are default-imported (not named), so we match the
|
|
1198
|
+
// component name against imported .vue file basenames via the import map.
|
|
1199
|
+
if (language === SupportedLanguages.Vue) {
|
|
1200
|
+
const templateComponents = extractTemplateComponents(file.content);
|
|
1201
|
+
if (templateComponents.length > 0) {
|
|
1202
|
+
const fileId = generateId('File', file.path);
|
|
1203
|
+
const importedFiles = ctx.importMap.get(file.path);
|
|
1204
|
+
if (importedFiles) {
|
|
1205
|
+
for (const componentName of templateComponents) {
|
|
1206
|
+
for (const importedPath of importedFiles) {
|
|
1207
|
+
if (!importedPath.endsWith('.vue'))
|
|
1208
|
+
continue;
|
|
1209
|
+
const basename = importedPath.slice(importedPath.lastIndexOf('/') + 1, importedPath.lastIndexOf('.'));
|
|
1210
|
+
if (basename !== componentName)
|
|
1211
|
+
continue;
|
|
1212
|
+
const targetFileId = generateId('File', importedPath);
|
|
1213
|
+
if (graph.getNode(targetFileId)) {
|
|
1214
|
+
graph.addRelationship({
|
|
1215
|
+
id: generateId('CALLS', `${fileId}:${componentName}->${targetFileId}`),
|
|
1216
|
+
sourceId: fileId,
|
|
1217
|
+
targetId: targetFileId,
|
|
1218
|
+
type: 'CALLS',
|
|
1219
|
+
confidence: 0.9,
|
|
1220
|
+
reason: 'vue-template-component',
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1223
|
+
break;
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
ctx.clearCache();
|
|
1230
|
+
}
|
|
1231
|
+
// ── Resolve deferred write-access edges ──
|
|
1232
|
+
// All properties (including Ruby attr_accessor) are now registered.
|
|
1233
|
+
for (const pw of pendingWrites) {
|
|
1234
|
+
const fieldOwner = resolveFieldOwnership(pw.receiverTypeName, pw.propertyName, pw.filePath, ctx);
|
|
1235
|
+
if (fieldOwner) {
|
|
1236
|
+
graph.addRelationship({
|
|
1237
|
+
id: generateId('ACCESSES', `${pw.srcId}:${fieldOwner.nodeId}:write`),
|
|
1238
|
+
sourceId: pw.srcId,
|
|
1239
|
+
targetId: fieldOwner.nodeId,
|
|
1240
|
+
type: 'ACCESSES',
|
|
1241
|
+
confidence: 1.0,
|
|
1242
|
+
reason: 'write',
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
if (skippedByLang && skippedByLang.size > 0) {
|
|
1247
|
+
for (const [lang, count] of skippedByLang.entries()) {
|
|
1248
|
+
logger.warn(`[ingestion] Skipped ${count} ${lang} file(s) in call processing — ${lang} parser not available.`);
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
return collectedHeritage;
|
|
1252
|
+
};
|
|
1253
|
+
// FREE_CALLABLE_TYPES imported from symbol-table.ts — single source of truth.
|
|
1254
|
+
const CONSTRUCTOR_TARGET_TYPES = new Set(['Constructor', 'Class', 'Struct', 'Record']);
|
|
1255
|
+
const filterCallableCandidates = (candidates, argCount, callForm) => {
|
|
1256
|
+
let kindFiltered;
|
|
1257
|
+
if (callForm === 'constructor') {
|
|
1258
|
+
const constructors = candidates.filter((c) => c.type === 'Constructor');
|
|
1259
|
+
if (constructors.length > 0) {
|
|
1260
|
+
kindFiltered = constructors;
|
|
1261
|
+
}
|
|
1262
|
+
else {
|
|
1263
|
+
const types = candidates.filter((c) => CONSTRUCTOR_TARGET_TYPES.has(c.type));
|
|
1264
|
+
kindFiltered =
|
|
1265
|
+
types.length > 0 ? types : candidates.filter((c) => CALL_TARGET_TYPES.has(c.type));
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
else {
|
|
1269
|
+
// CALL_TARGET_TYPES (not FREE_CALLABLE_TYPES) — the post-A4 filter must
|
|
1270
|
+
// also admit Method and Constructor candidates, which are now unioned
|
|
1271
|
+
// into the pool from `model.methods.lookupMethodByName` rather than
|
|
1272
|
+
// `symbols.lookupCallableByName`.
|
|
1273
|
+
kindFiltered = candidates.filter((c) => CALL_TARGET_TYPES.has(c.type));
|
|
1274
|
+
}
|
|
1275
|
+
if (kindFiltered.length === 0)
|
|
1276
|
+
return [];
|
|
1277
|
+
if (argCount === undefined)
|
|
1278
|
+
return kindFiltered;
|
|
1279
|
+
const hasParameterMetadata = kindFiltered.some((candidate) => candidate.parameterCount !== undefined);
|
|
1280
|
+
if (!hasParameterMetadata)
|
|
1281
|
+
return kindFiltered;
|
|
1282
|
+
return kindFiltered.filter((candidate) => candidate.parameterCount === undefined ||
|
|
1283
|
+
(argCount >= (candidate.requiredParameterCount ?? candidate.parameterCount) &&
|
|
1284
|
+
argCount <= candidate.parameterCount));
|
|
1285
|
+
};
|
|
1286
|
+
/**
|
|
1287
|
+
* Count callable candidates matching the kind + arity filter without
|
|
1288
|
+
* allocating an intermediate array. Short-circuits once count exceeds
|
|
1289
|
+
* `threshold` (default 1) — used by the dispatcher's `skipMember` check
|
|
1290
|
+
* where we only need to know "more than one survivor".
|
|
1291
|
+
*/
|
|
1292
|
+
const countCallableCandidates = (candidates, argCount, callForm, threshold = 1) => {
|
|
1293
|
+
let count = 0;
|
|
1294
|
+
for (const c of candidates) {
|
|
1295
|
+
// Kind filter (mirrors filterCallableCandidates)
|
|
1296
|
+
const typeOk = callForm === 'constructor'
|
|
1297
|
+
? CONSTRUCTOR_TARGET_TYPES.has(c.type)
|
|
1298
|
+
: CALL_TARGET_TYPES.has(c.type);
|
|
1299
|
+
if (!typeOk)
|
|
1300
|
+
continue;
|
|
1301
|
+
// Arity filter
|
|
1302
|
+
if (argCount !== undefined &&
|
|
1303
|
+
c.parameterCount !== undefined &&
|
|
1304
|
+
(argCount < (c.requiredParameterCount ?? c.parameterCount) || argCount > c.parameterCount)) {
|
|
1305
|
+
continue;
|
|
1306
|
+
}
|
|
1307
|
+
count++;
|
|
1308
|
+
if (count > threshold)
|
|
1309
|
+
return count; // early exit
|
|
1310
|
+
}
|
|
1311
|
+
return count;
|
|
1312
|
+
};
|
|
1313
|
+
const toResolveResult = (definition, tier) => ({
|
|
1314
|
+
nodeId: definition.nodeId,
|
|
1315
|
+
confidence: TIER_CONFIDENCE[tier],
|
|
1316
|
+
reason: tier === 'same-file' ? 'same-file' : tier === 'import-scoped' ? 'import-resolved' : 'global',
|
|
1317
|
+
returnType: definition.returnType,
|
|
1318
|
+
});
|
|
1319
|
+
/**
|
|
1320
|
+
* Kotlin often declares parameters with boxed names (`Int`, `Boolean`, …) while
|
|
1321
|
+
* literal inference yields JVM primitives (`int`, `boolean`). This map aligns
|
|
1322
|
+
* those for overload matching. Java parameter text is usually already primitive
|
|
1323
|
+
* spellings, so lookups here are typically unchanged.
|
|
1324
|
+
*/
|
|
1325
|
+
const KOTLIN_BOXED_TO_PRIMITIVE = {
|
|
1326
|
+
Int: 'int',
|
|
1327
|
+
Long: 'long',
|
|
1328
|
+
Short: 'short',
|
|
1329
|
+
Byte: 'byte',
|
|
1330
|
+
Float: 'float',
|
|
1331
|
+
Double: 'double',
|
|
1332
|
+
Boolean: 'boolean',
|
|
1333
|
+
Char: 'char',
|
|
1334
|
+
};
|
|
1335
|
+
const normalizeJvmTypeName = (name) => KOTLIN_BOXED_TO_PRIMITIVE[name] ?? name;
|
|
1336
|
+
const matchCandidatesByArgTypes = (candidates, argTypes) => {
|
|
1337
|
+
if (!candidates.some((c) => c.parameterTypes))
|
|
1338
|
+
return null;
|
|
1339
|
+
const matched = candidates.filter((c) => {
|
|
1340
|
+
// Keep candidates without type info — conservative: partially-annotated codebases
|
|
1341
|
+
// (e.g. C++ with some missing declarations) may have mixed typed/untyped overloads.
|
|
1342
|
+
// If one typed and one untyped both survive, matched.length > 1 → returns null (no edge).
|
|
1343
|
+
if (!c.parameterTypes)
|
|
1344
|
+
return true;
|
|
1345
|
+
return c.parameterTypes.every((pType, i) => {
|
|
1346
|
+
if (i >= argTypes.length || !argTypes[i])
|
|
1347
|
+
return true;
|
|
1348
|
+
// Normalise Kotlin boxed type names (Int→int, Boolean→boolean, etc.) so
|
|
1349
|
+
// that the stored declaration type matches the inferred literal type.
|
|
1350
|
+
return normalizeJvmTypeName(pType) === argTypes[i];
|
|
1351
|
+
});
|
|
1352
|
+
});
|
|
1353
|
+
if (matched.length === 1)
|
|
1354
|
+
return matched[0];
|
|
1355
|
+
// Multiple survivors may share the same nodeId (e.g. TypeScript overload signatures +
|
|
1356
|
+
// implementation body all collide via generateId). Deduplicate by nodeId — if all
|
|
1357
|
+
// matched candidates resolve to the same graph node, disambiguation succeeded.
|
|
1358
|
+
if (matched.length > 1) {
|
|
1359
|
+
const uniqueIds = new Set(matched.map((c) => c.nodeId));
|
|
1360
|
+
if (uniqueIds.size === 1)
|
|
1361
|
+
return matched[0];
|
|
1362
|
+
}
|
|
1363
|
+
return null;
|
|
1364
|
+
};
|
|
1365
|
+
/**
|
|
1366
|
+
* Try to disambiguate overloaded candidates using argument literal types.
|
|
1367
|
+
* Only invoked when filteredCandidates.length > 1 and at least one has parameterTypes.
|
|
1368
|
+
* Returns the single matching candidate, or null if ambiguous/inconclusive.
|
|
1369
|
+
*/
|
|
1370
|
+
const tryOverloadDisambiguation = (candidates, hints) => {
|
|
1371
|
+
const argTypes = extractCallArgTypes(hints.callNode, hints.inferLiteralType, hints.typeEnv ? (varName, cn) => hints.typeEnv.lookup(varName, cn) : undefined);
|
|
1372
|
+
if (!argTypes)
|
|
1373
|
+
return null;
|
|
1374
|
+
return matchCandidatesByArgTypes(candidates, argTypes);
|
|
1375
|
+
};
|
|
1376
|
+
/**
|
|
1377
|
+
* Apply overload-hint or arg-type disambiguation to a pre-filtered candidate
|
|
1378
|
+
* pool. Returns the unique survivor, or null when neither signal is present,
|
|
1379
|
+
* neither can disambiguate, or the pool remains ambiguous.
|
|
1380
|
+
*
|
|
1381
|
+
* Precedence rule: `overloadHints` wins over `preComputedArgTypes` when both
|
|
1382
|
+
* are supplied. The AST-based disambiguator has access to live type inference
|
|
1383
|
+
* hooks, whereas `preComputedArgTypes` is a worker-path pre-computation that
|
|
1384
|
+
* may be coarser-grained.
|
|
1385
|
+
*
|
|
1386
|
+
* Single source of truth for the narrowing-signal precedence used by member
|
|
1387
|
+
* and constructor resolution paths. Add a new narrowing signal here once, not
|
|
1388
|
+
* at each call site.
|
|
1389
|
+
*/
|
|
1390
|
+
const disambiguateByOverloadOrArgTypes = (pool, overloadHints, preComputedArgTypes) => {
|
|
1391
|
+
if (!overloadHints && !preComputedArgTypes)
|
|
1392
|
+
return null;
|
|
1393
|
+
if (overloadHints)
|
|
1394
|
+
return tryOverloadDisambiguation(pool, overloadHints);
|
|
1395
|
+
if (preComputedArgTypes)
|
|
1396
|
+
return matchCandidatesByArgTypes(pool, preComputedArgTypes);
|
|
1397
|
+
return null;
|
|
1398
|
+
};
|
|
1399
|
+
const orderProviderSameNameTypeCandidates = (candidates, typeName, filePath) => {
|
|
1400
|
+
const language = getLanguageFromFilename(filePath);
|
|
1401
|
+
if (language == null)
|
|
1402
|
+
return null;
|
|
1403
|
+
return (getProvider(language).orderSameNameTypeCandidates?.({
|
|
1404
|
+
typeName,
|
|
1405
|
+
callSiteFilePath: filePath,
|
|
1406
|
+
candidates,
|
|
1407
|
+
}) ?? null);
|
|
1408
|
+
};
|
|
1409
|
+
const resolveProviderPrimaryTypeCandidate = (candidates, tier, typeName, filePath) => {
|
|
1410
|
+
const ordered = orderProviderSameNameTypeCandidates(candidates, typeName, filePath);
|
|
1411
|
+
return ordered && ordered.length > 0 ? toResolveResult(ordered[0], tier) : null;
|
|
1412
|
+
};
|
|
1413
|
+
/**
|
|
1414
|
+
* Thin dispatcher that routes a call to the appropriate specialized resolver.
|
|
1415
|
+
*
|
|
1416
|
+
* - `free` → {@link resolveFreeCall}
|
|
1417
|
+
* - `constructor` → {@link resolveStaticCall} (with pre-resolved tiered pool)
|
|
1418
|
+
* - `member` with a known receiver type → {@link resolveMemberCall}, with
|
|
1419
|
+
* file-based fallback for traits/interfaces
|
|
1420
|
+
* - `member` without receiver type → module-alias check, then tiered lookup
|
|
1421
|
+
*
|
|
1422
|
+
* Replaces the former 200+ line function (SM-19: fuzzy-free call resolution).
|
|
1423
|
+
*/
|
|
1424
|
+
/**
|
|
1425
|
+
* Module-alias resolution for member calls without a receiver type.
|
|
1426
|
+
*
|
|
1427
|
+
* Handles Python/Ruby `import mod; mod.Symbol()` patterns where the receiver
|
|
1428
|
+
* is a module name, not a typed variable. Uses `moduleAliasMap` to scope
|
|
1429
|
+
* candidates to the correct module file.
|
|
1430
|
+
*/
|
|
1431
|
+
const resolveModuleAliasedCall = (call, currentFile, ctx, widenCache, tieredOverride) => {
|
|
1432
|
+
if (!call.receiverName)
|
|
1433
|
+
return null;
|
|
1434
|
+
const aliasMap = ctx.moduleAliasMap?.get(currentFile);
|
|
1435
|
+
if (!aliasMap)
|
|
1436
|
+
return null;
|
|
1437
|
+
const moduleFile = aliasMap.get(call.receiverName);
|
|
1438
|
+
if (!moduleFile)
|
|
1439
|
+
return null;
|
|
1440
|
+
// Reuse the caller's pre-computed tiered result when available —
|
|
1441
|
+
// the dispatcher already called ctx.resolve(call.calledName, currentFile).
|
|
1442
|
+
const tiered = tieredOverride ?? ctx.resolve(call.calledName, currentFile);
|
|
1443
|
+
if (!tiered)
|
|
1444
|
+
return null;
|
|
1445
|
+
// Try member-form, then constructor-form (for `module.ClassName()` patterns)
|
|
1446
|
+
let filtered = filterCallableCandidates(tiered.candidates, call.argCount, call.callForm).filter((c) => c.filePath === moduleFile);
|
|
1447
|
+
if (filtered.length === 0) {
|
|
1448
|
+
filtered = filterCallableCandidates(tiered.candidates, call.argCount, 'constructor').filter((c) => c.filePath === moduleFile);
|
|
1449
|
+
}
|
|
1450
|
+
if (filtered.length === 0) {
|
|
1451
|
+
// Widen to global callable+method indexes scoped to the aliased module
|
|
1452
|
+
// file. Function+ownerId (Python/Rust/Kotlin) is still routed to both
|
|
1453
|
+
// indexes until Unit 5 unblocks, so dedup by nodeId.
|
|
1454
|
+
const cacheKey = `${call.calledName}\0${moduleFile}`;
|
|
1455
|
+
let defs = widenCache?.get(cacheKey);
|
|
1456
|
+
if (!defs) {
|
|
1457
|
+
const rawCallable = ctx.model.symbols.lookupCallableByName(call.calledName);
|
|
1458
|
+
const rawMethods = ctx.model.methods.lookupMethodByName(call.calledName);
|
|
1459
|
+
const widenCombined = [];
|
|
1460
|
+
const widenSeen = new Set();
|
|
1461
|
+
for (const d of rawCallable) {
|
|
1462
|
+
if (widenSeen.has(d.nodeId))
|
|
1463
|
+
continue;
|
|
1464
|
+
widenSeen.add(d.nodeId);
|
|
1465
|
+
widenCombined.push(d);
|
|
1466
|
+
}
|
|
1467
|
+
for (const d of rawMethods) {
|
|
1468
|
+
if (widenSeen.has(d.nodeId))
|
|
1469
|
+
continue;
|
|
1470
|
+
widenSeen.add(d.nodeId);
|
|
1471
|
+
widenCombined.push(d);
|
|
1472
|
+
}
|
|
1473
|
+
defs = widenCombined;
|
|
1474
|
+
widenCache?.set(cacheKey, defs);
|
|
1475
|
+
}
|
|
1476
|
+
filtered = filterCallableCandidates(defs, call.argCount, call.callForm).filter((c) => c.filePath === moduleFile);
|
|
1477
|
+
if (filtered.length === 0) {
|
|
1478
|
+
filtered = filterCallableCandidates(defs, call.argCount, 'constructor').filter((c) => c.filePath === moduleFile);
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
return filtered.length === 1 ? toResolveResult(filtered[0], tiered.tier) : null;
|
|
1482
|
+
};
|
|
1483
|
+
/**
|
|
1484
|
+
* File-based fallback for member calls where owner-scoped resolution fails.
|
|
1485
|
+
*
|
|
1486
|
+
* Resolves the receiver type via `ctx.resolve()` and narrows all callable
|
|
1487
|
+
* symbols with the method name to the receiver type's defining file(s),
|
|
1488
|
+
* then applies ownerId filtering and overload disambiguation.
|
|
1489
|
+
*
|
|
1490
|
+
* Handles Rust trait dispatch (`repo.find()` where `find` is on a trait impl),
|
|
1491
|
+
* cross-file overloaded methods, and similar patterns where ownerId
|
|
1492
|
+
* relationships may not be established on all candidates.
|
|
1493
|
+
*/
|
|
1494
|
+
const resolveMemberCallByFile = (calledName, receiverTypeName, currentFile, ctx, argCount, callForm, overloadHints, preComputedArgTypes) => {
|
|
1495
|
+
const typeResolved = ctx.resolve(receiverTypeName, currentFile);
|
|
1496
|
+
if (!typeResolved || typeResolved.candidates.length === 0)
|
|
1497
|
+
return null;
|
|
1498
|
+
const typeNodeIds = new Set(typeResolved.candidates.map((d) => d.nodeId));
|
|
1499
|
+
const typeFiles = new Set(typeResolved.candidates.map((d) => d.filePath));
|
|
1500
|
+
// A4 (plan 006, Unit 4): consult both indexes. Strictly-labeled
|
|
1501
|
+
// Method/Constructor are disjoint, but Function+ownerId (Python/Rust/
|
|
1502
|
+
// Kotlin) is routed into BOTH indexes by `wrappedAdd` until Unit 5
|
|
1503
|
+
// unblocks — dedup by nodeId so overload disambiguation doesn't see
|
|
1504
|
+
// phantom duplicates.
|
|
1505
|
+
const rawCallablePool = ctx.model.symbols.lookupCallableByName(calledName);
|
|
1506
|
+
const rawMethodPool = ctx.model.methods.lookupMethodByName(calledName);
|
|
1507
|
+
const combinedPool = [];
|
|
1508
|
+
const combinedSeen = new Set();
|
|
1509
|
+
for (const def of rawCallablePool) {
|
|
1510
|
+
if (combinedSeen.has(def.nodeId))
|
|
1511
|
+
continue;
|
|
1512
|
+
combinedSeen.add(def.nodeId);
|
|
1513
|
+
combinedPool.push(def);
|
|
1514
|
+
}
|
|
1515
|
+
for (const def of rawMethodPool) {
|
|
1516
|
+
if (combinedSeen.has(def.nodeId))
|
|
1517
|
+
continue;
|
|
1518
|
+
combinedSeen.add(def.nodeId);
|
|
1519
|
+
combinedPool.push(def);
|
|
1520
|
+
}
|
|
1521
|
+
const methodPool = filterCallableCandidates(combinedPool, argCount, callForm);
|
|
1522
|
+
const fileFiltered = methodPool.filter((c) => typeFiles.has(c.filePath));
|
|
1523
|
+
if (fileFiltered.length === 1) {
|
|
1524
|
+
return toResolveResult(fileFiltered[0], typeResolved.tier);
|
|
1525
|
+
}
|
|
1526
|
+
// ownerId fallback: narrow by ownerId matching the type's nodeId
|
|
1527
|
+
const pool = fileFiltered.length > 0 ? fileFiltered : methodPool;
|
|
1528
|
+
const ownerFiltered = pool.filter((c) => c.ownerId && typeNodeIds.has(c.ownerId));
|
|
1529
|
+
if (ownerFiltered.length === 1)
|
|
1530
|
+
return toResolveResult(ownerFiltered[0], typeResolved.tier);
|
|
1531
|
+
// Overload disambiguation on the narrowed pool
|
|
1532
|
+
if (fileFiltered.length > 1 || ownerFiltered.length > 1) {
|
|
1533
|
+
const overloadPool = ownerFiltered.length > 1 ? ownerFiltered : fileFiltered;
|
|
1534
|
+
const disambiguated = disambiguateByOverloadOrArgTypes(overloadPool, overloadHints, preComputedArgTypes);
|
|
1535
|
+
if (disambiguated)
|
|
1536
|
+
return toResolveResult(disambiguated, typeResolved.tier);
|
|
1537
|
+
}
|
|
1538
|
+
// Zero-match null-route: receiver type resolved but no candidate matched
|
|
1539
|
+
// after file-based and owner-based narrowing. Refuse to emit a CALLS edge
|
|
1540
|
+
// rather than guess — matches the SM-10 R3 null-route contract.
|
|
1541
|
+
return null;
|
|
1542
|
+
};
|
|
1543
|
+
/** Return the sole survivor from a tiered pool after callable + arity filtering, or null. */
|
|
1544
|
+
const singleCandidate = (tiered, argCount, callForm) => {
|
|
1545
|
+
const filtered = filterCallableCandidates(tiered.candidates, argCount, callForm);
|
|
1546
|
+
return filtered.length === 1 ? toResolveResult(filtered[0], tiered.tier) : null;
|
|
1547
|
+
};
|
|
1548
|
+
/** @internal Exported for unit tests. Do not use outside tests. */
|
|
1549
|
+
export const _resolveCallTargetForTesting = (call, currentFile, ctx, opts) => resolveCallTarget(call, currentFile, ctx, opts?.overloadHints, opts?.widenCache, opts?.preComputedArgTypes, opts?.heritageMap);
|
|
1550
|
+
const resolveCallTarget = (call, currentFile, ctx, overloadHints, widenCache, preComputedArgTypes, heritageMap, dispatchDecision) => {
|
|
1551
|
+
const tiered = ctx.resolve(call.calledName, currentFile);
|
|
1552
|
+
if (!tiered)
|
|
1553
|
+
return null;
|
|
1554
|
+
// DAG dispatch: use decision.primary to pick the resolver branch.
|
|
1555
|
+
// Callers that own the DAG (processCalls + crossFile deferred paths)
|
|
1556
|
+
// pass a decision; other callers use the shared default ladder.
|
|
1557
|
+
// Language-specific primary / fallback / ancestryView overrides come from
|
|
1558
|
+
// the provider's `selectDispatch` hook.
|
|
1559
|
+
const decision = dispatchDecision ?? defaultDispatchDecision(call.callForm);
|
|
1560
|
+
const primary = decision.primary;
|
|
1561
|
+
if (primary === 'free') {
|
|
1562
|
+
return resolveFreeCall(call.calledName, currentFile, ctx, call.argCount, tiered, overloadHints, preComputedArgTypes);
|
|
1563
|
+
}
|
|
1564
|
+
if (primary === 'constructor') {
|
|
1565
|
+
return (resolveStaticCall(call.calledName, currentFile, ctx, call.argCount, tiered, overloadHints, preComputedArgTypes) ?? singleCandidate(tiered, call.argCount, 'constructor'));
|
|
1566
|
+
}
|
|
1567
|
+
// primary === 'owner-scoped'
|
|
1568
|
+
if (call.receiverTypeName) {
|
|
1569
|
+
// Skip the owner-scoped MRO path when the tiered pool has genuine
|
|
1570
|
+
// overload ambiguity that needs D1-D4+E handling, not D0.
|
|
1571
|
+
const skipMember = (!!overloadHints || !!preComputedArgTypes) &&
|
|
1572
|
+
countCallableCandidates(tiered.candidates, call.argCount, call.callForm) > 1;
|
|
1573
|
+
// Try owner-scoped (resolveMemberCall) then file-scoped (resolveMemberCallByFile).
|
|
1574
|
+
// DAG: dispatchDecision.ancestryView selects instance vs singleton ancestry
|
|
1575
|
+
// for kind-aware MRO strategies. Ruby `Account.log` flows via 'singleton'.
|
|
1576
|
+
//
|
|
1577
|
+
// Singleton-ancestry miss MUST NOT degrade to the file-scoped fallback:
|
|
1578
|
+
// resolveMemberCallByFile matches by ownerId and would happily pick an
|
|
1579
|
+
// instance method defined on the same class, leaking instance dispatch
|
|
1580
|
+
// onto what was declared a class-method call. For singleton dispatch,
|
|
1581
|
+
// a miss either null-routes or falls through to `decision.fallback`.
|
|
1582
|
+
const singletonDispatch = decision.ancestryView === 'singleton';
|
|
1583
|
+
const memberResult = (!skipMember
|
|
1584
|
+
? resolveMemberCall(call.receiverTypeName, call.calledName, currentFile, ctx, heritageMap, call.argCount, decision.ancestryView)
|
|
1585
|
+
: null) ??
|
|
1586
|
+
(singletonDispatch
|
|
1587
|
+
? null
|
|
1588
|
+
: resolveMemberCallByFile(call.calledName, call.receiverTypeName, currentFile, ctx, call.argCount, call.callForm, overloadHints, preComputedArgTypes));
|
|
1589
|
+
if (memberResult)
|
|
1590
|
+
return memberResult;
|
|
1591
|
+
// Module-alias narrowing runs as a FALLBACK, after owner/file-scoped
|
|
1592
|
+
// resolvers have returned null. This ordering is load-bearing: placing
|
|
1593
|
+
// alias narrowing first would short-circuit unique owner-scoped answers
|
|
1594
|
+
// when a local variable coincidentally matches an alias name, leaking
|
|
1595
|
+
// unrelated homonyms from the aliased file onto the wrong receiver type.
|
|
1596
|
+
//
|
|
1597
|
+
// The type-file verification guard is load-bearing for SM-10 R3: an
|
|
1598
|
+
// alias is only a VALID narrowing signal when the alias target file is
|
|
1599
|
+
// among the receiver type's defining files. If the alias points at a
|
|
1600
|
+
// file that does not hold `receiverTypeName`, any candidate we would
|
|
1601
|
+
// pick from there would belong to an unrelated class — a cross-type
|
|
1602
|
+
// false positive. ctx.resolve is cached per (name, file), so resolving
|
|
1603
|
+
// the receiver type a second time here is free.
|
|
1604
|
+
const typeResolves = ctx.resolve(call.receiverTypeName, currentFile);
|
|
1605
|
+
const aliasMap = ctx.moduleAliasMap?.get(currentFile);
|
|
1606
|
+
const aliasTargetFile = call.receiverName && aliasMap ? aliasMap.get(call.receiverName) : undefined;
|
|
1607
|
+
if (aliasTargetFile &&
|
|
1608
|
+
typeResolves &&
|
|
1609
|
+
typeResolves.candidates.some((c) => c.filePath === aliasTargetFile)) {
|
|
1610
|
+
const aliasResult = resolveModuleAliasedCall(call, currentFile, ctx, widenCache, tiered);
|
|
1611
|
+
if (aliasResult)
|
|
1612
|
+
return aliasResult;
|
|
1613
|
+
}
|
|
1614
|
+
// SM-10 R3 null-route: when the receiver type resolves to indexed types
|
|
1615
|
+
// but no scoped resolver (nor the guarded alias fallback) produced a
|
|
1616
|
+
// match, that's a genuine miss — refuse to emit a CALLS edge rather
|
|
1617
|
+
// than guess via an unscoped singleCandidate that ignores the class
|
|
1618
|
+
// hierarchy. When the type is NOT in the index (PHP `mixed`, dynamic
|
|
1619
|
+
// types, unresolvable aliases), the scoped resolvers had nothing to
|
|
1620
|
+
// work with and singleCandidate is the correct last resort.
|
|
1621
|
+
//
|
|
1622
|
+
// DAG fallback override: when `select-dispatch` returned
|
|
1623
|
+
// `fallback: 'free-arity-narrowed'` (today: Ruby implicit-self bare
|
|
1624
|
+
// calls whose enclosing class doesn't define the method), fall through
|
|
1625
|
+
// to free-call resolution instead of null-routing. This preserves
|
|
1626
|
+
// existing free-call arity-narrowing heuristics for bare calls that
|
|
1627
|
+
// happen to target methods on unrelated classes.
|
|
1628
|
+
if (typeResolves && typeResolves.candidates.length > 0) {
|
|
1629
|
+
if (decision.fallback === 'free-arity-narrowed') {
|
|
1630
|
+
const free = resolveFreeCall(call.calledName, currentFile, ctx, call.argCount, tiered, overloadHints, preComputedArgTypes);
|
|
1631
|
+
if (free)
|
|
1632
|
+
return free;
|
|
1633
|
+
}
|
|
1634
|
+
return null; // null-route: type resolved, no candidate matched
|
|
1635
|
+
}
|
|
1636
|
+
return singleCandidate(tiered, call.argCount, call.callForm);
|
|
1637
|
+
}
|
|
1638
|
+
// Member call with no inferred receiver type — e.g. Python `mod.fn()`
|
|
1639
|
+
// where `mod` is a module alias. Module-alias narrowing is the primary
|
|
1640
|
+
// disambiguation signal here. Also consulted from the typed-member
|
|
1641
|
+
// branch above as a guarded fallback after owner/file-scoped resolvers.
|
|
1642
|
+
return (resolveModuleAliasedCall(call, currentFile, ctx, widenCache, tiered) ??
|
|
1643
|
+
singleCandidate(tiered, call.argCount, call.callForm));
|
|
1644
|
+
};
|
|
1645
|
+
// ── Scope key helpers ────────────────────────────────────────────────────
|
|
1646
|
+
// Scope keys use the format "funcName@startIndex" (produced by type-env.ts).
|
|
1647
|
+
// Source IDs use "Label:filepath:funcName" (produced by parse-worker.ts).
|
|
1648
|
+
// NUL (\0) is used as a composite-key separator because it cannot appear
|
|
1649
|
+
// in source-code identifiers, preventing ambiguous concatenation.
|
|
1650
|
+
//
|
|
1651
|
+
// receiverKey stores the FULL scope (funcName@startIndex) to prevent
|
|
1652
|
+
// collisions between overloaded methods with the same name in different
|
|
1653
|
+
// classes (e.g. User.save@100 and Repo.save@200 are distinct keys).
|
|
1654
|
+
// Lookup uses a secondary funcName-only index built in lookupReceiverType.
|
|
1655
|
+
/** Extract the bare function name from a sourceId.
|
|
1656
|
+
* Handles both unqualified ("Function:filepath:funcName" → "funcName")
|
|
1657
|
+
* and qualified ("Function:filepath:ClassName.funcName" → "funcName").
|
|
1658
|
+
* Strips any trailing #<arity> suffix from Method/Constructor IDs. */
|
|
1659
|
+
const extractFuncNameFromSourceId = (sourceId) => {
|
|
1660
|
+
const lastColon = sourceId.lastIndexOf(':');
|
|
1661
|
+
const segment = lastColon >= 0 ? sourceId.slice(lastColon + 1) : '';
|
|
1662
|
+
const dotIdx = segment.lastIndexOf('.');
|
|
1663
|
+
const raw = dotIdx >= 0 ? segment.slice(dotIdx + 1) : segment;
|
|
1664
|
+
// Strip #<arity> suffix (e.g. "save#2" → "save")
|
|
1665
|
+
const hashIdx = raw.indexOf('#');
|
|
1666
|
+
return hashIdx >= 0 ? raw.slice(0, hashIdx) : raw;
|
|
1667
|
+
};
|
|
1668
|
+
/**
|
|
1669
|
+
* Build a composite key for receiver type storage.
|
|
1670
|
+
* Uses the full scope string (e.g. "save@100") to distinguish overloaded
|
|
1671
|
+
* methods with the same name in different classes.
|
|
1672
|
+
*/
|
|
1673
|
+
const receiverKey = (scope, varName) => `${scope}\0${varName}`;
|
|
1674
|
+
/**
|
|
1675
|
+
* Build a two-level secondary index from the verified receiver map.
|
|
1676
|
+
* The verified map is keyed by `scope\0varName` where scope is either
|
|
1677
|
+
* "funcName@startIndex" (inside a function) or "" (file level).
|
|
1678
|
+
* Index structure: Map<funcName, Map<varName, ReceiverTypeEntry>>
|
|
1679
|
+
*
|
|
1680
|
+
* Known limitation: the index collapses scope keys to bare funcName,
|
|
1681
|
+
* so two same-arity overloads with the same local variable name but
|
|
1682
|
+
* different types will mark that variable as ambiguous. A future
|
|
1683
|
+
* enhancement should key by full scope (funcName@startIndex) and carry
|
|
1684
|
+
* scope keys through findEnclosingFunction's return type.
|
|
1685
|
+
*/
|
|
1686
|
+
const buildReceiverTypeIndex = (map) => {
|
|
1687
|
+
const index = new Map();
|
|
1688
|
+
for (const [key, typeName] of map) {
|
|
1689
|
+
const nul = key.indexOf('\0');
|
|
1690
|
+
if (nul < 0)
|
|
1691
|
+
continue;
|
|
1692
|
+
const scope = key.slice(0, nul);
|
|
1693
|
+
const varName = key.slice(nul + 1);
|
|
1694
|
+
if (!varName)
|
|
1695
|
+
continue;
|
|
1696
|
+
if (scope !== '' && !scope.includes('@'))
|
|
1697
|
+
continue;
|
|
1698
|
+
const funcName = scope === '' ? '' : scope.slice(0, scope.indexOf('@'));
|
|
1699
|
+
let varMap = index.get(funcName);
|
|
1700
|
+
if (!varMap) {
|
|
1701
|
+
varMap = new Map();
|
|
1702
|
+
index.set(funcName, varMap);
|
|
1703
|
+
}
|
|
1704
|
+
const existing = varMap.get(varName);
|
|
1705
|
+
if (existing === undefined) {
|
|
1706
|
+
varMap.set(varName, { kind: 'resolved', value: typeName });
|
|
1707
|
+
}
|
|
1708
|
+
else if (existing.kind === 'resolved' && existing.value !== typeName) {
|
|
1709
|
+
varMap.set(varName, { kind: 'ambiguous' });
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
return index;
|
|
1713
|
+
};
|
|
1714
|
+
/**
|
|
1715
|
+
* O(1) receiver type lookup using the pre-built secondary index.
|
|
1716
|
+
* Returns the unique type name if unambiguous. Falls back to file-level scope.
|
|
1717
|
+
*/
|
|
1718
|
+
const lookupReceiverType = (index, funcName, varName) => {
|
|
1719
|
+
const funcBucket = index.get(funcName);
|
|
1720
|
+
if (funcBucket) {
|
|
1721
|
+
const entry = funcBucket.get(varName);
|
|
1722
|
+
if (entry?.kind === 'resolved')
|
|
1723
|
+
return entry.value;
|
|
1724
|
+
if (entry?.kind === 'ambiguous') {
|
|
1725
|
+
// Ambiguous in this function scope — try file-level fallback
|
|
1726
|
+
const fileEntry = index.get('')?.get(varName);
|
|
1727
|
+
return fileEntry?.kind === 'resolved' ? fileEntry.value : undefined;
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
// Fallback: file-level scope (funcName "")
|
|
1731
|
+
if (funcName !== '') {
|
|
1732
|
+
const fileEntry = index.get('')?.get(varName);
|
|
1733
|
+
if (fileEntry?.kind === 'resolved')
|
|
1734
|
+
return fileEntry.value;
|
|
1735
|
+
}
|
|
1736
|
+
return undefined;
|
|
1737
|
+
};
|
|
1738
|
+
/**
|
|
1739
|
+
* Resolve the type that results from accessing `receiverName.fieldName`.
|
|
1740
|
+
* Requires declaredType on the Property node (needed for chain walking continuation).
|
|
1741
|
+
*/
|
|
1742
|
+
const resolveFieldAccessType = (receiverName, fieldName, filePath, ctx) => {
|
|
1743
|
+
const fieldDef = resolveFieldOwnership(receiverName, fieldName, filePath, ctx);
|
|
1744
|
+
if (!fieldDef?.declaredType)
|
|
1745
|
+
return undefined;
|
|
1746
|
+
// Use stripNullable (not extractReturnTypeName) — field types like List<User>
|
|
1747
|
+
// should be preserved as-is, not unwrapped to User. Only strip nullable wrappers.
|
|
1748
|
+
return {
|
|
1749
|
+
typeName: stripNullable(fieldDef.declaredType),
|
|
1750
|
+
fieldNodeId: fieldDef.nodeId,
|
|
1751
|
+
};
|
|
1752
|
+
};
|
|
1753
|
+
/**
|
|
1754
|
+
* Resolve a field's Property node given a receiver type name and field name.
|
|
1755
|
+
* Does NOT require declaredType — used by write-access tracking where only the
|
|
1756
|
+
* fieldNodeId is needed (no chain continuation).
|
|
1757
|
+
*/
|
|
1758
|
+
const resolveFieldOwnership = (receiverName, fieldName, filePath, ctx) => {
|
|
1759
|
+
const typeResolved = ctx.resolve(receiverName, filePath);
|
|
1760
|
+
if (!typeResolved)
|
|
1761
|
+
return undefined;
|
|
1762
|
+
const classDef = typeResolved.candidates.find((d) => CLASS_LIKE_TYPES.has(d.type));
|
|
1763
|
+
if (!classDef)
|
|
1764
|
+
return undefined;
|
|
1765
|
+
return ctx.model.fields.lookupFieldByOwner(classDef.nodeId, fieldName) ?? undefined;
|
|
1766
|
+
};
|
|
1767
|
+
/**
|
|
1768
|
+
* Resolve a method by owner type name using the eagerly-populated methodByOwner index.
|
|
1769
|
+
* Returns `{ def, tier }` when an unambiguous method is found, `undefined` otherwise.
|
|
1770
|
+
*
|
|
1771
|
+
* **Multi-candidate iteration (homonym disambiguation):** when `ctx.resolve(ownerType)`
|
|
1772
|
+
* returns multiple class-like candidates (e.g. two classes named `User` in different
|
|
1773
|
+
* files reachable from the call site), each is probed with `lookupMethodByOwnerWithMRO`.
|
|
1774
|
+
* Results are deduplicated by `nodeId` so that:
|
|
1775
|
+
*
|
|
1776
|
+
* - homonym classes that both walk up to the SAME ancestor's method collapse to 1 hit
|
|
1777
|
+
* - aliased re-exports that produce two candidates pointing at the same def collapse too
|
|
1778
|
+
*
|
|
1779
|
+
* After deduplication:
|
|
1780
|
+
*
|
|
1781
|
+
* - 0 unique matches → `undefined` (owner-scoped path has no answer)
|
|
1782
|
+
* - 1 unique match → return it
|
|
1783
|
+
* - ≥2 unique matches → `undefined` (genuine homonym ambiguity; don't silently pick one)
|
|
1784
|
+
*
|
|
1785
|
+
* The returned `tier` reflects how the owner TYPE was resolved (not the method name).
|
|
1786
|
+
* Threaded out here so callers don't need a second `ctx.resolve(ownerType, ...)` call —
|
|
1787
|
+
* this decouples callers from `ctx.resolve`'s per-file caching contract.
|
|
1788
|
+
*/
|
|
1789
|
+
const resolveMethodByOwner = (receiverTypeName, methodName, filePath, ctx, heritageMap, argCount,
|
|
1790
|
+
/**
|
|
1791
|
+
* DAG-sourced ancestry selector. `'singleton'` routes through
|
|
1792
|
+
* `heritageMap.getSingletonAncestry(owner)` for class-method dispatch
|
|
1793
|
+
* (Ruby `Account.log` via `extend LoggerMixin`). Default / undefined
|
|
1794
|
+
* uses the walker's instance-dispatch behavior.
|
|
1795
|
+
*/
|
|
1796
|
+
ancestryView) => {
|
|
1797
|
+
const typeResolved = ctx.resolve(receiverTypeName, filePath);
|
|
1798
|
+
if (!typeResolved)
|
|
1799
|
+
return undefined;
|
|
1800
|
+
// MRO walking needs a language hint so we can derive the per-language
|
|
1801
|
+
// strategy; compute it once and reuse for every candidate. Unknown
|
|
1802
|
+
// extension → fall back to plain direct lookup (D1-D4 still runs on miss).
|
|
1803
|
+
const language = heritageMap ? getLanguageFromFilename(filePath) : null;
|
|
1804
|
+
const mroStrategy = language != null ? getProvider(language).mroStrategy : null;
|
|
1805
|
+
const canWalkMRO = heritageMap != null && mroStrategy != null;
|
|
1806
|
+
// Iterate all class-like candidates tracking the first unambiguous hit.
|
|
1807
|
+
// Zero-allocation fast path: the common case is exactly one class candidate,
|
|
1808
|
+
// so we avoid building a Map. A second hit with a different `nodeId` flips
|
|
1809
|
+
// `ambiguous` and short-circuits the loop. Diamond MRO convergence on the
|
|
1810
|
+
// same inherited method collapses to one hit because `nodeId` matches.
|
|
1811
|
+
//
|
|
1812
|
+
// firstDef === undefined → owner-scoped resolution found nothing
|
|
1813
|
+
// firstDef && !ambiguous → unambiguous answer
|
|
1814
|
+
// ambiguous → genuine homonym ambiguity — refuse to pick
|
|
1815
|
+
//
|
|
1816
|
+
// argCount is threaded through so arity-differing overloads
|
|
1817
|
+
// (e.g. C++ `greet()` vs `greet(string)`) are disambiguated inside the
|
|
1818
|
+
// owner-scoped lookup rather than collapsing to an arbitrary first pick.
|
|
1819
|
+
let firstDef;
|
|
1820
|
+
let ambiguous = false;
|
|
1821
|
+
for (const candidate of typeResolved.candidates) {
|
|
1822
|
+
if (!CLASS_LIKE_TYPES.has(candidate.type))
|
|
1823
|
+
continue;
|
|
1824
|
+
// Singleton dispatch: when the DAG decision requested the singleton
|
|
1825
|
+
// ancestry view, pass `heritageMap.getSingletonAncestry` as the walker's
|
|
1826
|
+
// ancestry override. Kind-aware strategies (e.g. MroStrategy 'ruby-mixin')
|
|
1827
|
+
// honor the override by scanning it linearly in place of their default walk.
|
|
1828
|
+
const singletonOverride = ancestryView === 'singleton' && canWalkMRO && heritageMap
|
|
1829
|
+
? heritageMap.getSingletonAncestry(candidate.nodeId).map((e) => e.parentId)
|
|
1830
|
+
: undefined;
|
|
1831
|
+
const def = canWalkMRO
|
|
1832
|
+
? lookupMethodByOwnerWithMRO(candidate.nodeId, methodName, heritageMap, ctx.model, mroStrategy, argCount, singletonOverride)
|
|
1833
|
+
: ctx.model.methods.lookupMethodByOwner(candidate.nodeId, methodName, argCount);
|
|
1834
|
+
if (!def)
|
|
1835
|
+
continue;
|
|
1836
|
+
if (!firstDef) {
|
|
1837
|
+
firstDef = def;
|
|
1838
|
+
}
|
|
1839
|
+
else if (def.nodeId !== firstDef.nodeId) {
|
|
1840
|
+
ambiguous = true;
|
|
1841
|
+
break;
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
if (!firstDef && !ambiguous) {
|
|
1845
|
+
const orderedTypeCandidates = orderProviderSameNameTypeCandidates(ctx.model.types.lookupClassByName(receiverTypeName), receiverTypeName, filePath);
|
|
1846
|
+
if (orderedTypeCandidates) {
|
|
1847
|
+
for (const candidate of orderedTypeCandidates) {
|
|
1848
|
+
const def = canWalkMRO
|
|
1849
|
+
? lookupMethodByOwnerWithMRO(candidate.nodeId, methodName, heritageMap, ctx.model, mroStrategy, argCount)
|
|
1850
|
+
: ctx.model.methods.lookupMethodByOwner(candidate.nodeId, methodName, argCount);
|
|
1851
|
+
if (!def)
|
|
1852
|
+
continue;
|
|
1853
|
+
if (!firstDef) {
|
|
1854
|
+
firstDef = def;
|
|
1855
|
+
}
|
|
1856
|
+
else if (def.nodeId !== firstDef.nodeId) {
|
|
1857
|
+
ambiguous = true;
|
|
1858
|
+
break;
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
if (!firstDef || ambiguous)
|
|
1864
|
+
return undefined;
|
|
1865
|
+
return { def: firstDef, tier: typeResolved.tier };
|
|
1866
|
+
};
|
|
1867
|
+
// ---------------------------------------------------------------------------
|
|
1868
|
+
// SM-11: Owner-scoped + MRO member-call resolution (no fuzzy lookup)
|
|
1869
|
+
// ---------------------------------------------------------------------------
|
|
1870
|
+
/**
|
|
1871
|
+
* Resolve a member call using owner-scoped + MRO resolution only (no fuzzy lookup).
|
|
1872
|
+
* Used for `obj.method()` calls where the receiver type is known.
|
|
1873
|
+
*
|
|
1874
|
+
* Delegates to {@link resolveMethodByOwner} which performs an O(1) owner-scoped
|
|
1875
|
+
* method lookup and, when a {@link HeritageMap} is provided, walks the MRO chain
|
|
1876
|
+
* via {@link lookupMethodByOwnerWithMRO}.
|
|
1877
|
+
*
|
|
1878
|
+
* {@link resolveCallTarget} delegates here for member calls.
|
|
1879
|
+
*
|
|
1880
|
+
* **SEMANTIC CHANGE (2026-04-09):** The confidence tier reflects how the
|
|
1881
|
+
* owner TYPE was resolved, not how the method NAME was resolved globally.
|
|
1882
|
+
* more accurate for owner-scoped resolution (the discriminant IS the class,
|
|
1883
|
+
* not the method name). Downstream consumers that filter CALLS edges by
|
|
1884
|
+
* confidence threshold may see shifted values on otherwise-unchanged code.
|
|
1885
|
+
* See the "returns result with correct confidence tier" tests below for the
|
|
1886
|
+
* locked-in behavior.
|
|
1887
|
+
*
|
|
1888
|
+
* **Performance:** Callers that only need the return type (e.g. `walkMixedChain`)
|
|
1889
|
+
* should call {@link resolveMethodByOwner} directly and use the `.def.returnType`
|
|
1890
|
+
* field instead, to avoid building a throwaway `ResolveResult`.
|
|
1891
|
+
*
|
|
1892
|
+
* @param ownerType - The receiver's type name (e.g. 'User')
|
|
1893
|
+
* @param methodName - The method being called (e.g. 'save')
|
|
1894
|
+
* @param currentFile - File path of the call site
|
|
1895
|
+
* @param ctx - Resolution context
|
|
1896
|
+
* @param heritageMap - Optional heritage map for MRO-aware ancestor walking
|
|
1897
|
+
*/
|
|
1898
|
+
export const resolveMemberCall = (ownerType, methodName, currentFile, ctx, heritageMap, argCount, ancestryView) => {
|
|
1899
|
+
const resolved = resolveMethodByOwner(ownerType, methodName, currentFile, ctx, heritageMap, argCount, ancestryView);
|
|
1900
|
+
if (!resolved)
|
|
1901
|
+
return null;
|
|
1902
|
+
return toResolveResult(resolved.def, resolved.tier);
|
|
1903
|
+
};
|
|
1904
|
+
// ---------------------------------------------------------------------------
|
|
1905
|
+
// SM-13: Free-function call resolution
|
|
1906
|
+
// ---------------------------------------------------------------------------
|
|
1907
|
+
/**
|
|
1908
|
+
* Resolve a free-function call using `lookupExact` (same-file) + import-scoped
|
|
1909
|
+
* resolution via `ctx.resolve()`.
|
|
1910
|
+
*
|
|
1911
|
+
* Used for `foo()`, `doStuff()` — unqualified calls with no receiver.
|
|
1912
|
+
* Also handles implicit constructors (`User()` without `new`) by delegating
|
|
1913
|
+
* to {@link resolveStaticCall} when the tiered pool contains class-like
|
|
1914
|
+
* targets.
|
|
1915
|
+
*
|
|
1916
|
+
* {@link resolveCallTarget} delegates here for `callForm === 'free'`.
|
|
1917
|
+
*
|
|
1918
|
+
* `resolveFreeCall` does not take a `widenCache` parameter. Free calls
|
|
1919
|
+
* have no receiver type and rely exclusively on the tiered pool
|
|
1920
|
+
* from `ctx.resolve()`.
|
|
1921
|
+
*
|
|
1922
|
+
* @param calledName - The called function name (e.g. 'doStuff')
|
|
1923
|
+
* @param filePath - File path of the call site
|
|
1924
|
+
* @param ctx - Resolution context
|
|
1925
|
+
* @param argCount - Optional argument count for arity filtering
|
|
1926
|
+
* @param tieredOverride - Pre-computed tiered candidates from an upstream
|
|
1927
|
+
* `ctx.resolve` call. When provided, skips the redundant
|
|
1928
|
+
* lookup inside this function.
|
|
1929
|
+
* @param overloadHints - Optional AST-based overload disambiguation hints
|
|
1930
|
+
* @param preComputedArgTypes - Optional pre-computed argument types (worker path)
|
|
1931
|
+
*/
|
|
1932
|
+
export const resolveFreeCall = (calledName, filePath, ctx, argCount, tieredOverride, overloadHints, preComputedArgTypes) => {
|
|
1933
|
+
const tiered = tieredOverride ?? ctx.resolve(calledName, filePath);
|
|
1934
|
+
if (!tiered)
|
|
1935
|
+
return null;
|
|
1936
|
+
let filteredCandidates = filterCallableCandidates(tiered.candidates, argCount, 'free');
|
|
1937
|
+
// Class-target fast path: free-form call targeting a class. Delegates to
|
|
1938
|
+
// resolveStaticCall for O(1) class + constructor lookup.
|
|
1939
|
+
// The `.some()` trigger must stay aligned with `INSTANTIABLE_CLASS_TYPES` —
|
|
1940
|
+
// any type admitted here that is not in that set will cause resolveStaticCall
|
|
1941
|
+
// to return null, wasting two lookup passes per call. `Enum` is deliberately
|
|
1942
|
+
// excluded; `Record` is included so record-like class targets reach the fast
|
|
1943
|
+
// path.
|
|
1944
|
+
// Align with INSTANTIABLE_CLASS_TYPES by reusing the set directly rather
|
|
1945
|
+
// than enumerating literal strings. This converts an invariant that was
|
|
1946
|
+
// previously enforced by a comment ("keep this list aligned with
|
|
1947
|
+
// INSTANTIABLE_CLASS_TYPES") into one enforced structurally — any future
|
|
1948
|
+
// extension of the set propagates here automatically.
|
|
1949
|
+
// Language providers can still choose a primary same-name type candidate in
|
|
1950
|
+
// the tail of this function when their grammars index one logical type
|
|
1951
|
+
// multiple times.
|
|
1952
|
+
const hasClassTarget = filteredCandidates.length === 0 &&
|
|
1953
|
+
tiered.candidates.some((c) => INSTANTIABLE_CLASS_TYPES.has(c.type));
|
|
1954
|
+
if (hasClassTarget) {
|
|
1955
|
+
const staticResult = resolveStaticCall(calledName, filePath, ctx, argCount, tiered);
|
|
1956
|
+
if (staticResult)
|
|
1957
|
+
return staticResult;
|
|
1958
|
+
// Retry with constructor form for languages whose constructor calls look
|
|
1959
|
+
// like free function calls. If resolveStaticCall didn't match, re-filter
|
|
1960
|
+
// with constructor form so CONSTRUCTOR_TARGET_TYPES applies.
|
|
1961
|
+
//
|
|
1962
|
+
// The retry fires for every null return from `resolveStaticCall`, which
|
|
1963
|
+
// can happen for three distinct reasons — all three are handled below:
|
|
1964
|
+
//
|
|
1965
|
+
// (a) No explicit `Constructor` node found and zero instantiable
|
|
1966
|
+
// class candidates (e.g. Interface/Trait/Impl only — the SM-12
|
|
1967
|
+
// null-route contract). `filterCallableCandidates` with
|
|
1968
|
+
// `'constructor'` form will also return nothing → we fall
|
|
1969
|
+
// through to the final null return. Correct.
|
|
1970
|
+
//
|
|
1971
|
+
// (b) Homonym ambiguity — two or more instantiable class candidates
|
|
1972
|
+
// share the name (e.g. `User` in two files, same tier). The
|
|
1973
|
+
// retry repopulates `filteredCandidates` with both Classes and
|
|
1974
|
+
// they flow into the provider same-name candidate hook below, which
|
|
1975
|
+
// can pick a primary definition or null-route.
|
|
1976
|
+
//
|
|
1977
|
+
// (c) `resolveStaticCall` step 4 bailed because the tiered pool
|
|
1978
|
+
// contains ownerless `Constructor` nodes (some extractors emit
|
|
1979
|
+
// constructors without `ownerId`). Those `Constructor` nodes
|
|
1980
|
+
// survive the constructor-form filter below and reach overload
|
|
1981
|
+
// disambiguation, giving the existing filter path a chance to
|
|
1982
|
+
// pick the right one. Correct but currently uncovered by a
|
|
1983
|
+
// dedicated test — the R5 `preComputedArgTypes` path exercises
|
|
1984
|
+
// overload disambiguation for Functions, which is structurally
|
|
1985
|
+
// the same code.
|
|
1986
|
+
filteredCandidates = filterCallableCandidates(tiered.candidates, argCount, 'constructor');
|
|
1987
|
+
}
|
|
1988
|
+
// E. Overload disambiguation
|
|
1989
|
+
if (filteredCandidates.length > 1) {
|
|
1990
|
+
const disambiguated = overloadHints
|
|
1991
|
+
? tryOverloadDisambiguation(filteredCandidates, overloadHints)
|
|
1992
|
+
: preComputedArgTypes
|
|
1993
|
+
? matchCandidatesByArgTypes(filteredCandidates, preComputedArgTypes)
|
|
1994
|
+
: null;
|
|
1995
|
+
if (disambiguated)
|
|
1996
|
+
return toResolveResult(disambiguated, tiered.tier);
|
|
1997
|
+
}
|
|
1998
|
+
if (filteredCandidates.length !== 1) {
|
|
1999
|
+
const primary = resolveProviderPrimaryTypeCandidate(filteredCandidates, tiered.tier, calledName, filePath);
|
|
2000
|
+
if (primary)
|
|
2001
|
+
return primary;
|
|
2002
|
+
return null;
|
|
2003
|
+
}
|
|
2004
|
+
return toResolveResult(filteredCandidates[0], tiered.tier);
|
|
2005
|
+
};
|
|
2006
|
+
// ---------------------------------------------------------------------------
|
|
2007
|
+
// SM-12: Constructor/static call resolution (no fuzzy lookup)
|
|
2008
|
+
// ---------------------------------------------------------------------------
|
|
2009
|
+
/**
|
|
2010
|
+
* Resolve a constructor or static call using class-scoped lookup (no fuzzy lookup).
|
|
2011
|
+
* Used for `new User()` / `User()` calls where the calledName targets a class.
|
|
2012
|
+
*
|
|
2013
|
+
* Uses {@link TypeRegistry.lookupClassByName} for O(1) class lookup and
|
|
2014
|
+
* {@link MethodRegistry.lookupMethodByOwner} for constructor resolution.
|
|
2015
|
+
* {@link resolveCallTarget} delegates here for constructor and free-form calls
|
|
2016
|
+
* that target a class.
|
|
2017
|
+
*
|
|
2018
|
+
* Resolution strategy:
|
|
2019
|
+
* 1. `lookupClassByName(className)` — O(1) pre-check; bail early if no class exists.
|
|
2020
|
+
* 2. `ctx.resolve(className, currentFile)` — import-scoped tier for confidence.
|
|
2021
|
+
* 3. Filter to class-like candidates via `CLASS_LIKE_TYPES` and walk each
|
|
2022
|
+
* with `lookupMethodByOwner(classNodeId, className, argCount)` — O(1)
|
|
2023
|
+
* constructor lookup. Only accept results with `type === 'Constructor'`.
|
|
2024
|
+
* 4. If step 3 found nothing and the tiered pool contains ownerless
|
|
2025
|
+
* `Constructor` nodes (common in some extractors), bail out so
|
|
2026
|
+
* `filterCallableCandidates` downstream handles Constructor-vs-Class
|
|
2027
|
+
* preference correctly.
|
|
2028
|
+
* 5. Class-node fallback: filter `classCandidates` through
|
|
2029
|
+
* `INSTANTIABLE_CLASS_TYPES` and return the sole survivor when there is
|
|
2030
|
+
* exactly one. Null-route on zero survivors (Interface / Trait / Impl
|
|
2031
|
+
* stripped) or multiple (homonym ambiguity).
|
|
2032
|
+
*
|
|
2033
|
+
* @param className - The class name (e.g. 'User'). Also used as the method
|
|
2034
|
+
* name for the `lookupMethodByOwner` scan, because the
|
|
2035
|
+
* only constructor-shaped call we handle today is
|
|
2036
|
+
* `ClassName(...)` / `new ClassName(...)`. Named
|
|
2037
|
+
* constructors like Dart `User.fromJson()` arrive as
|
|
2038
|
+
* member calls and route through `resolveMemberCall`,
|
|
2039
|
+
* so this function does not yet need a separate
|
|
2040
|
+
* `methodName` parameter. Revisit if a language surfaces
|
|
2041
|
+
* a static-method-shaped call with a distinct member
|
|
2042
|
+
* name.
|
|
2043
|
+
* @param currentFile - File path of the call site
|
|
2044
|
+
* @param ctx - Resolution context
|
|
2045
|
+
* @param argCount - Optional argument count for arity filtering
|
|
2046
|
+
* @param tieredOverride - Pre-computed tiered candidates for `className` from
|
|
2047
|
+
* an upstream `ctx.resolve` call. When provided, skips
|
|
2048
|
+
* the redundant lookup inside this function. Leave
|
|
2049
|
+
* unset for direct callers without a prior resolution.
|
|
2050
|
+
*/
|
|
2051
|
+
export const resolveStaticCall = (className, currentFile, ctx, argCount, tieredOverride, overloadHints, preComputedArgTypes) => {
|
|
2052
|
+
// 1. Pre-check: does a class with this name exist at all? (O(1))
|
|
2053
|
+
// This guards against the expensive `ctx.resolve` walk when the name
|
|
2054
|
+
// is clearly not class-like (e.g. plain functions). When `tieredOverride`
|
|
2055
|
+
// is supplied, the caller has already paid for the tiered lookup, so this
|
|
2056
|
+
// pre-check still prevents the class-candidate filter + lookupMethodByOwner
|
|
2057
|
+
// loop from running on obviously non-class targets.
|
|
2058
|
+
const allClasses = ctx.model.types.lookupClassByName(className);
|
|
2059
|
+
if (allClasses.length === 0)
|
|
2060
|
+
return null;
|
|
2061
|
+
// 2. Scope via ctx.resolve for import-tier information. Reuse the caller's
|
|
2062
|
+
// tiered result when provided — it is computed from the same name and
|
|
2063
|
+
// file context, so re-running the walk would be a pure waste.
|
|
2064
|
+
const typeResolved = tieredOverride ?? ctx.resolve(className, currentFile);
|
|
2065
|
+
if (!typeResolved)
|
|
2066
|
+
return null;
|
|
2067
|
+
const classCandidates = typeResolved.candidates.filter((c) => CLASS_LIKE_TYPES.has(c.type));
|
|
2068
|
+
if (classCandidates.length === 0)
|
|
2069
|
+
return null;
|
|
2070
|
+
// 3. Try lookupMethodByOwner for explicit Constructor nodes.
|
|
2071
|
+
// Only accept results with type === 'Constructor' — a Method or Function
|
|
2072
|
+
// that happens to share the class name (e.g. C++ methods named after
|
|
2073
|
+
// their class) is not a constructor for resolution purposes.
|
|
2074
|
+
// Same dedup logic as resolveMethodByOwner: diamond inheritance converging
|
|
2075
|
+
// on the same constructor collapses to one hit.
|
|
2076
|
+
//
|
|
2077
|
+
// Same-name assumption: the lookup key is `${candidate.nodeId}\0${className}`,
|
|
2078
|
+
// so this finds Constructor nodes whose symbol name equals the class name
|
|
2079
|
+
// (`class User` with a `Constructor` named `User`). Constructors indexed
|
|
2080
|
+
// under a different name (e.g. Python `__init__`) will not be found here —
|
|
2081
|
+
// but they also won't appear in the tiered pool for `ctx.resolve(className)`
|
|
2082
|
+
// for the same reason, so step 4's Constructor-presence check will not
|
|
2083
|
+
// see them either. The two miss cases are symmetric. If a future extractor
|
|
2084
|
+
// indexes Constructor nodes under an alternative name while still setting
|
|
2085
|
+
// `ownerId`, this assumption will need revisiting.
|
|
2086
|
+
let firstDef;
|
|
2087
|
+
let ambiguous = false;
|
|
2088
|
+
for (const candidate of classCandidates) {
|
|
2089
|
+
const def = ctx.model.methods.lookupMethodByOwner(candidate.nodeId, className, argCount);
|
|
2090
|
+
if (!def || def.type !== 'Constructor')
|
|
2091
|
+
continue;
|
|
2092
|
+
if (!firstDef) {
|
|
2093
|
+
firstDef = def;
|
|
2094
|
+
}
|
|
2095
|
+
else if (def.nodeId !== firstDef.nodeId) {
|
|
2096
|
+
ambiguous = true;
|
|
2097
|
+
break;
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
if (firstDef && !ambiguous) {
|
|
2101
|
+
return toResolveResult(firstDef, typeResolved.tier);
|
|
2102
|
+
}
|
|
2103
|
+
// 4. lookupMethodByOwner found nothing — check whether the tiered pool
|
|
2104
|
+
// contains Constructor nodes that lack ownerId (common in some extractors).
|
|
2105
|
+
// If so, bail out so the existing filterCallableCandidates path handles
|
|
2106
|
+
// Constructor-vs-Class preference correctly.
|
|
2107
|
+
//
|
|
2108
|
+
// This branch also catches the step-3 ambiguous case (`ambiguous = true`
|
|
2109
|
+
// with two distinct Constructor nodes across multiple class candidates):
|
|
2110
|
+
// the same Constructor nodes are indexed under the class name in the
|
|
2111
|
+
// tiered pool, so `.some(Constructor)` is true here and we defer to
|
|
2112
|
+
// step 4.5 (overload/arg-type disambiguation) or the caller's fallback.
|
|
2113
|
+
// Do not remove this check without also handling the ambiguous step-3
|
|
2114
|
+
// path explicitly.
|
|
2115
|
+
if (typeResolved.candidates.some((c) => c.type === 'Constructor')) {
|
|
2116
|
+
// 4.5. Overload / arg-type disambiguation for ambiguous or ownerless
|
|
2117
|
+
// Constructor pools. When the caller supplied a narrowing signal
|
|
2118
|
+
// (AST-based overload hints from the sequential path, or pre-
|
|
2119
|
+
// computed arg types from the worker path), give disambiguation a
|
|
2120
|
+
// chance before null-routing. Symmetric with resolveMemberCallByFile's
|
|
2121
|
+
// disambiguation pass — both resolvers now share the same signal
|
|
2122
|
+
// precedence via disambiguateByOverloadOrArgTypes. Only fires when
|
|
2123
|
+
// at least one narrowing signal is present; preserves SM-10 R3 for
|
|
2124
|
+
// genuinely ambiguous cases with no disambiguating input.
|
|
2125
|
+
if (overloadHints || preComputedArgTypes) {
|
|
2126
|
+
const ctorPool = filterCallableCandidates(typeResolved.candidates, argCount, 'constructor');
|
|
2127
|
+
if (ctorPool.length > 1) {
|
|
2128
|
+
const disambiguated = disambiguateByOverloadOrArgTypes(ctorPool, overloadHints, preComputedArgTypes);
|
|
2129
|
+
if (disambiguated)
|
|
2130
|
+
return toResolveResult(disambiguated, typeResolved.tier);
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
return null;
|
|
2134
|
+
}
|
|
2135
|
+
// 5. No constructor nodes at all — fall back to the class node itself, but
|
|
2136
|
+
// ONLY when it is actually instantiable. Interface / Trait / Impl / Enum
|
|
2137
|
+
// are deliberately excluded via `INSTANTIABLE_CLASS_TYPES` to prevent
|
|
2138
|
+
// false `CALLS` edges from constructor-shaped calls to non-instantiable
|
|
2139
|
+
// nodes. This also disambiguates the Rust same-file shadowing case
|
|
2140
|
+
// (`struct User` + `impl User` both present at same-file tier): the
|
|
2141
|
+
// Impl is stripped, leaving the Struct as the sole instantiable target.
|
|
2142
|
+
// Addresses Codex review finding on PR #754.
|
|
2143
|
+
const instantiableCandidates = classCandidates.filter((c) => INSTANTIABLE_CLASS_TYPES.has(c.type));
|
|
2144
|
+
// Three outcomes below, in order of likelihood after the fix:
|
|
2145
|
+
// length === 0 → all candidates were stripped as non-instantiable (e.g.
|
|
2146
|
+
// Interface / Trait / Impl). Null-route via the fall-through `return
|
|
2147
|
+
// null` — this is the dominant Codex-fix case.
|
|
2148
|
+
// length === 1 → a single instantiable candidate remains, return it.
|
|
2149
|
+
// length > 1 → let the call-site provider choose a primary when it can
|
|
2150
|
+
// prove the candidates are one logical type; otherwise null-route.
|
|
2151
|
+
const primary = resolveProviderPrimaryTypeCandidate(instantiableCandidates, typeResolved.tier, className, currentFile);
|
|
2152
|
+
if (primary)
|
|
2153
|
+
return primary;
|
|
2154
|
+
if (instantiableCandidates.length === 1) {
|
|
2155
|
+
return toResolveResult(instantiableCandidates[0], typeResolved.tier);
|
|
2156
|
+
}
|
|
2157
|
+
return null;
|
|
2158
|
+
};
|
|
2159
|
+
/**
|
|
2160
|
+
* Create a deduplicated ACCESSES edge emitter for a single source node.
|
|
2161
|
+
* Each (sourceId, fieldNodeId) pair is emitted at most once per source.
|
|
2162
|
+
*/
|
|
2163
|
+
const makeAccessEmitter = (graph, sourceId) => {
|
|
2164
|
+
const emitted = new Set();
|
|
2165
|
+
return (fieldNodeId) => {
|
|
2166
|
+
const key = `${sourceId}\0${fieldNodeId}`;
|
|
2167
|
+
if (emitted.has(key))
|
|
2168
|
+
return;
|
|
2169
|
+
emitted.add(key);
|
|
2170
|
+
graph.addRelationship({
|
|
2171
|
+
id: generateId('ACCESSES', `${sourceId}:${fieldNodeId}:read`),
|
|
2172
|
+
sourceId,
|
|
2173
|
+
targetId: fieldNodeId,
|
|
2174
|
+
type: 'ACCESSES',
|
|
2175
|
+
confidence: 1.0,
|
|
2176
|
+
reason: 'read',
|
|
2177
|
+
});
|
|
2178
|
+
};
|
|
2179
|
+
};
|
|
2180
|
+
const walkMixedChain = (chain, startType, filePath, ctx, onFieldResolved, heritageMap) => {
|
|
2181
|
+
let currentType = startType;
|
|
2182
|
+
for (const step of chain) {
|
|
2183
|
+
if (!currentType)
|
|
2184
|
+
break;
|
|
2185
|
+
if (step.kind === 'field') {
|
|
2186
|
+
const resolved = resolveFieldAccessType(currentType, step.name, filePath, ctx);
|
|
2187
|
+
if (!resolved) {
|
|
2188
|
+
currentType = undefined;
|
|
2189
|
+
break;
|
|
2190
|
+
}
|
|
2191
|
+
onFieldResolved?.(resolved.fieldNodeId);
|
|
2192
|
+
currentType = resolved.typeName;
|
|
2193
|
+
}
|
|
2194
|
+
else {
|
|
2195
|
+
// Ruby/Python: property access is syntactically identical to method calls.
|
|
2196
|
+
// Try field resolution first — if the name is a known property with declaredType,
|
|
2197
|
+
// use that type directly. Otherwise fall back to method call resolution.
|
|
2198
|
+
const fieldResolved = resolveFieldAccessType(currentType, step.name, filePath, ctx);
|
|
2199
|
+
if (fieldResolved) {
|
|
2200
|
+
onFieldResolved?.(fieldResolved.fieldNodeId);
|
|
2201
|
+
currentType = fieldResolved.typeName;
|
|
2202
|
+
continue;
|
|
2203
|
+
}
|
|
2204
|
+
// Fast path: O(1) owner-scoped method lookup via methodByOwner index.
|
|
2205
|
+
// Note: CALLS edges for intermediate chain steps are NOT emitted here — walkMixedChain
|
|
2206
|
+
// only threads types. CALLS edges come from the outer per-call-expression loop in processCalls.
|
|
2207
|
+
//
|
|
2208
|
+
// We call `resolveMethodByOwner` directly (NOT `resolveMemberCall`) because this is
|
|
2209
|
+
// a hot path — called per chain step per call expression — and we only need the
|
|
2210
|
+
// return type string. Going through `resolveMemberCall` would allocate a throwaway
|
|
2211
|
+
// `ResolveResult` with confidence/reason that we immediately discard.
|
|
2212
|
+
const owned = resolveMethodByOwner(currentType, step.name, filePath, ctx, heritageMap);
|
|
2213
|
+
if (owned?.def.returnType) {
|
|
2214
|
+
const fastRetType = extractReturnTypeName(owned.def.returnType);
|
|
2215
|
+
if (fastRetType) {
|
|
2216
|
+
currentType = fastRetType;
|
|
2217
|
+
continue;
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
// Fallback: resolve via resolveCallTarget dispatcher (delegates to resolveMemberCall)
|
|
2221
|
+
const resolved = resolveCallTarget({ calledName: step.name, callForm: 'member', receiverTypeName: currentType }, filePath, ctx, undefined, undefined, undefined, heritageMap);
|
|
2222
|
+
if (!resolved) {
|
|
2223
|
+
// Stdlib passthrough: unwrap(), clone(), etc. preserve the receiver type
|
|
2224
|
+
if (TYPE_PRESERVING_METHODS.has(step.name))
|
|
2225
|
+
continue;
|
|
2226
|
+
currentType = undefined;
|
|
2227
|
+
break;
|
|
2228
|
+
}
|
|
2229
|
+
if (!resolved.returnType) {
|
|
2230
|
+
currentType = undefined;
|
|
2231
|
+
break;
|
|
2232
|
+
}
|
|
2233
|
+
const retType = extractReturnTypeName(resolved.returnType);
|
|
2234
|
+
if (!retType) {
|
|
2235
|
+
currentType = undefined;
|
|
2236
|
+
break;
|
|
2237
|
+
}
|
|
2238
|
+
currentType = retType;
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
return currentType;
|
|
2242
|
+
};
|
|
2243
|
+
/**
|
|
2244
|
+
* Fast path: resolve pre-extracted call sites from workers.
|
|
2245
|
+
* No AST parsing — workers already extracted calledName + sourceId.
|
|
2246
|
+
*
|
|
2247
|
+
* @param bindingAccumulator Phase 9: optional accumulator carrying file-scope
|
|
2248
|
+
* TypeEnv bindings from all worker-processed files. When the SymbolTable has
|
|
2249
|
+
* no return type for a cross-file callee, `verifyConstructorBindings` falls
|
|
2250
|
+
* back to the accumulator via `namedImportMap` to bind the variable to the
|
|
2251
|
+
* callee's resolved type (e.g. `var x = getUser()` → `x: User`).
|
|
2252
|
+
*/
|
|
2253
|
+
export const processCallsFromExtracted = async (graph, extractedCalls, ctx, onProgress, constructorBindings, heritageMap, bindingAccumulator) => {
|
|
2254
|
+
// Scope-aware receiver types: keyed by filePath → "funcName\0varName" → typeName.
|
|
2255
|
+
// The scope dimension prevents collisions when two functions in the same file
|
|
2256
|
+
// have same-named locals pointing to different constructor types.
|
|
2257
|
+
const fileReceiverTypes = new Map();
|
|
2258
|
+
if (constructorBindings) {
|
|
2259
|
+
for (const { filePath, bindings } of constructorBindings) {
|
|
2260
|
+
const verified = verifyConstructorBindings(bindings, filePath, ctx, graph, bindingAccumulator);
|
|
2261
|
+
if (verified.size > 0) {
|
|
2262
|
+
fileReceiverTypes.set(filePath, buildReceiverTypeIndex(verified));
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
const byFile = new Map();
|
|
2267
|
+
for (const call of extractedCalls) {
|
|
2268
|
+
let list = byFile.get(call.filePath);
|
|
2269
|
+
if (!list) {
|
|
2270
|
+
list = [];
|
|
2271
|
+
byFile.set(call.filePath, list);
|
|
2272
|
+
}
|
|
2273
|
+
list.push(call);
|
|
2274
|
+
}
|
|
2275
|
+
const totalFiles = byFile.size;
|
|
2276
|
+
let filesProcessed = 0;
|
|
2277
|
+
for (const [filePath, calls] of byFile) {
|
|
2278
|
+
filesProcessed++;
|
|
2279
|
+
if (filesProcessed % 100 === 0) {
|
|
2280
|
+
onProgress?.(filesProcessed, totalFiles);
|
|
2281
|
+
await yieldToEventLoop();
|
|
2282
|
+
}
|
|
2283
|
+
// Registry-primary gate: skip Python (etc.) entirely when the
|
|
2284
|
+
// scope-based phase owns CALLS for this language.
|
|
2285
|
+
const fileLanguage = getLanguageFromFilename(filePath);
|
|
2286
|
+
if (fileLanguage && isRegistryPrimary(fileLanguage))
|
|
2287
|
+
continue;
|
|
2288
|
+
ctx.enableCache(filePath);
|
|
2289
|
+
const widenCache = new Map();
|
|
2290
|
+
const receiverMap = fileReceiverTypes.get(filePath);
|
|
2291
|
+
for (const call of calls) {
|
|
2292
|
+
let effectiveCall = call;
|
|
2293
|
+
// Step 1: resolve receiver type from constructor bindings
|
|
2294
|
+
if (!call.receiverTypeName && call.receiverName && receiverMap) {
|
|
2295
|
+
const callFuncName = extractFuncNameFromSourceId(call.sourceId);
|
|
2296
|
+
const resolvedType = lookupReceiverType(receiverMap, callFuncName, call.receiverName);
|
|
2297
|
+
if (resolvedType) {
|
|
2298
|
+
effectiveCall = { ...call, receiverTypeName: resolvedType };
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
// Step 1b: class-as-receiver for static method calls (e.g. UserService.find_user())
|
|
2302
|
+
if (!effectiveCall.receiverTypeName &&
|
|
2303
|
+
effectiveCall.receiverName &&
|
|
2304
|
+
effectiveCall.callForm === 'member') {
|
|
2305
|
+
const typeResolved = ctx.resolve(effectiveCall.receiverName, effectiveCall.filePath);
|
|
2306
|
+
if (typeResolved &&
|
|
2307
|
+
typeResolved.candidates.some((d) => d.type === 'Class' ||
|
|
2308
|
+
d.type === 'Interface' ||
|
|
2309
|
+
d.type === 'Struct' ||
|
|
2310
|
+
d.type === 'Enum')) {
|
|
2311
|
+
effectiveCall = { ...effectiveCall, receiverTypeName: effectiveCall.receiverName };
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
// Step 1c: mixed chain resolution (field, call, or interleaved — e.g. svc.getUser().address.save()).
|
|
2315
|
+
// Runs whenever receiverMixedChain is present. Steps 1/1b may have resolved the base receiver
|
|
2316
|
+
// type already; that type is used as the chain's starting point.
|
|
2317
|
+
if (effectiveCall.receiverMixedChain?.length) {
|
|
2318
|
+
// Use the already-resolved base type (from Steps 1/1b) or look it up now.
|
|
2319
|
+
let currentType = effectiveCall.receiverTypeName;
|
|
2320
|
+
if (!currentType && effectiveCall.receiverName && receiverMap) {
|
|
2321
|
+
const callFuncName = extractFuncNameFromSourceId(effectiveCall.sourceId);
|
|
2322
|
+
currentType = lookupReceiverType(receiverMap, callFuncName, effectiveCall.receiverName);
|
|
2323
|
+
}
|
|
2324
|
+
if (!currentType && effectiveCall.receiverName) {
|
|
2325
|
+
const typeResolved = ctx.resolve(effectiveCall.receiverName, effectiveCall.filePath);
|
|
2326
|
+
if (typeResolved?.candidates.some((d) => d.type === 'Class' ||
|
|
2327
|
+
d.type === 'Interface' ||
|
|
2328
|
+
d.type === 'Struct' ||
|
|
2329
|
+
d.type === 'Enum')) {
|
|
2330
|
+
currentType = effectiveCall.receiverName;
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
if (currentType) {
|
|
2334
|
+
const walkedType = walkMixedChain(effectiveCall.receiverMixedChain, currentType, effectiveCall.filePath, ctx, makeAccessEmitter(graph, effectiveCall.sourceId), heritageMap);
|
|
2335
|
+
if (walkedType) {
|
|
2336
|
+
effectiveCall = { ...effectiveCall, receiverTypeName: walkedType };
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
const resolved = resolveCallTarget(effectiveCall, effectiveCall.filePath, ctx, undefined, widenCache, effectiveCall.argTypes, heritageMap);
|
|
2341
|
+
if (!resolved) {
|
|
2342
|
+
// Vue template component fallback: match calledName against imported .vue basenames
|
|
2343
|
+
if (effectiveCall.filePath.endsWith('.vue') && effectiveCall.sourceId.startsWith('File:')) {
|
|
2344
|
+
const importedFiles = ctx.importMap.get(effectiveCall.filePath);
|
|
2345
|
+
if (importedFiles) {
|
|
2346
|
+
for (const importedPath of importedFiles) {
|
|
2347
|
+
if (!importedPath.endsWith('.vue'))
|
|
2348
|
+
continue;
|
|
2349
|
+
const basename = importedPath.slice(importedPath.lastIndexOf('/') + 1, importedPath.lastIndexOf('.'));
|
|
2350
|
+
if (basename !== effectiveCall.calledName)
|
|
2351
|
+
continue;
|
|
2352
|
+
const targetFileId = generateId('File', importedPath);
|
|
2353
|
+
if (graph.getNode(targetFileId)) {
|
|
2354
|
+
graph.addRelationship({
|
|
2355
|
+
id: generateId('CALLS', `${effectiveCall.sourceId}:${effectiveCall.calledName}->${targetFileId}`),
|
|
2356
|
+
sourceId: effectiveCall.sourceId,
|
|
2357
|
+
targetId: targetFileId,
|
|
2358
|
+
type: 'CALLS',
|
|
2359
|
+
confidence: 0.9,
|
|
2360
|
+
reason: 'vue-template-component',
|
|
2361
|
+
});
|
|
2362
|
+
}
|
|
2363
|
+
break;
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
continue;
|
|
2368
|
+
}
|
|
2369
|
+
const relId = generateId('CALLS', `${effectiveCall.sourceId}:${effectiveCall.calledName}->${resolved.nodeId}`);
|
|
2370
|
+
graph.addRelationship({
|
|
2371
|
+
id: relId,
|
|
2372
|
+
sourceId: effectiveCall.sourceId,
|
|
2373
|
+
targetId: resolved.nodeId,
|
|
2374
|
+
type: 'CALLS',
|
|
2375
|
+
confidence: resolved.confidence,
|
|
2376
|
+
reason: resolved.reason,
|
|
2377
|
+
});
|
|
2378
|
+
if (heritageMap && effectiveCall.callForm === 'member' && effectiveCall.receiverTypeName) {
|
|
2379
|
+
const implTargets = findInterfaceDispatchTargets(effectiveCall.calledName, effectiveCall.receiverTypeName, effectiveCall.filePath, ctx, heritageMap, resolved.nodeId);
|
|
2380
|
+
for (const impl of implTargets) {
|
|
2381
|
+
graph.addRelationship({
|
|
2382
|
+
id: generateId('CALLS', `${effectiveCall.sourceId}:${effectiveCall.calledName}->${impl.nodeId}`),
|
|
2383
|
+
sourceId: effectiveCall.sourceId,
|
|
2384
|
+
targetId: impl.nodeId,
|
|
2385
|
+
type: 'CALLS',
|
|
2386
|
+
confidence: impl.confidence,
|
|
2387
|
+
reason: impl.reason,
|
|
2388
|
+
});
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
ctx.clearCache();
|
|
2393
|
+
}
|
|
2394
|
+
onProgress?.(totalFiles, totalFiles);
|
|
2395
|
+
};
|
|
2396
|
+
/**
|
|
2397
|
+
* Resolve pre-extracted field write assignments to ACCESSES {reason: 'write'} edges.
|
|
2398
|
+
* Accepts optional constructorBindings for return-type-aware receiver inference,
|
|
2399
|
+
* mirroring processCallsFromExtracted's verified binding lookup.
|
|
2400
|
+
*/
|
|
2401
|
+
export const processAssignmentsFromExtracted = (graph, assignments, ctx, constructorBindings, bindingAccumulator) => {
|
|
2402
|
+
// Build per-file receiver type indexes from verified constructor bindings
|
|
2403
|
+
const fileReceiverTypes = new Map();
|
|
2404
|
+
if (constructorBindings) {
|
|
2405
|
+
for (const { filePath, bindings } of constructorBindings) {
|
|
2406
|
+
const verified = verifyConstructorBindings(bindings, filePath, ctx, graph, bindingAccumulator);
|
|
2407
|
+
if (verified.size > 0) {
|
|
2408
|
+
fileReceiverTypes.set(filePath, buildReceiverTypeIndex(verified));
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
for (const asn of assignments) {
|
|
2413
|
+
// Resolve the receiver type
|
|
2414
|
+
let receiverTypeName = asn.receiverTypeName;
|
|
2415
|
+
// Tier 2: verified constructor bindings (return-type inference)
|
|
2416
|
+
if (!receiverTypeName && fileReceiverTypes.size > 0) {
|
|
2417
|
+
const receiverMap = fileReceiverTypes.get(asn.filePath);
|
|
2418
|
+
if (receiverMap) {
|
|
2419
|
+
const funcName = extractFuncNameFromSourceId(asn.sourceId);
|
|
2420
|
+
receiverTypeName = lookupReceiverType(receiverMap, funcName, asn.receiverText);
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
// Tier 3: static class-as-receiver fallback
|
|
2424
|
+
if (!receiverTypeName) {
|
|
2425
|
+
const resolved = ctx.resolve(asn.receiverText, asn.filePath);
|
|
2426
|
+
if (resolved?.candidates.some((d) => CLASS_LIKE_TYPES.has(d.type))) {
|
|
2427
|
+
receiverTypeName = asn.receiverText;
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
if (!receiverTypeName)
|
|
2431
|
+
continue;
|
|
2432
|
+
const fieldOwner = resolveFieldOwnership(receiverTypeName, asn.propertyName, asn.filePath, ctx);
|
|
2433
|
+
if (!fieldOwner)
|
|
2434
|
+
continue;
|
|
2435
|
+
graph.addRelationship({
|
|
2436
|
+
id: generateId('ACCESSES', `${asn.sourceId}:${fieldOwner.nodeId}:write`),
|
|
2437
|
+
sourceId: asn.sourceId,
|
|
2438
|
+
targetId: fieldOwner.nodeId,
|
|
2439
|
+
type: 'ACCESSES',
|
|
2440
|
+
confidence: 1.0,
|
|
2441
|
+
reason: 'write',
|
|
2442
|
+
});
|
|
2443
|
+
}
|
|
2444
|
+
};
|
|
2445
|
+
/**
|
|
2446
|
+
* Resolve pre-extracted Laravel routes to CALLS edges from route files to controller methods.
|
|
2447
|
+
*/
|
|
2448
|
+
export const processRoutesFromExtracted = async (graph, extractedRoutes, ctx, onProgress) => {
|
|
2449
|
+
for (let i = 0; i < extractedRoutes.length; i++) {
|
|
2450
|
+
const route = extractedRoutes[i];
|
|
2451
|
+
if (i % 50 === 0) {
|
|
2452
|
+
onProgress?.(i, extractedRoutes.length);
|
|
2453
|
+
await yieldToEventLoop();
|
|
2454
|
+
}
|
|
2455
|
+
if (!route.controllerName || !route.methodName)
|
|
2456
|
+
continue;
|
|
2457
|
+
const controllerResolved = ctx.resolve(route.controllerName, route.filePath);
|
|
2458
|
+
if (!controllerResolved || controllerResolved.candidates.length === 0)
|
|
2459
|
+
continue;
|
|
2460
|
+
if (controllerResolved.tier === 'global' && controllerResolved.candidates.length > 1)
|
|
2461
|
+
continue;
|
|
2462
|
+
const controllerDef = controllerResolved.candidates[0];
|
|
2463
|
+
const confidence = TIER_CONFIDENCE[controllerResolved.tier];
|
|
2464
|
+
const methodResolved = ctx.resolve(route.methodName, controllerDef.filePath);
|
|
2465
|
+
const methodId = methodResolved?.tier === 'same-file' ? methodResolved.candidates[0]?.nodeId : undefined;
|
|
2466
|
+
const sourceId = generateId('File', route.filePath);
|
|
2467
|
+
if (!methodId) {
|
|
2468
|
+
const guessedId = generateId('Method', `${controllerDef.filePath}:${route.methodName}`);
|
|
2469
|
+
const relId = generateId('CALLS', `${sourceId}:route->${guessedId}`);
|
|
2470
|
+
graph.addRelationship({
|
|
2471
|
+
id: relId,
|
|
2472
|
+
sourceId,
|
|
2473
|
+
targetId: guessedId,
|
|
2474
|
+
type: 'CALLS',
|
|
2475
|
+
confidence: confidence * 0.8,
|
|
2476
|
+
reason: 'laravel-route',
|
|
2477
|
+
});
|
|
2478
|
+
continue;
|
|
2479
|
+
}
|
|
2480
|
+
const relId = generateId('CALLS', `${sourceId}:route->${methodId}`);
|
|
2481
|
+
graph.addRelationship({
|
|
2482
|
+
id: relId,
|
|
2483
|
+
sourceId,
|
|
2484
|
+
targetId: methodId,
|
|
2485
|
+
type: 'CALLS',
|
|
2486
|
+
confidence,
|
|
2487
|
+
reason: 'laravel-route',
|
|
2488
|
+
});
|
|
2489
|
+
}
|
|
2490
|
+
onProgress?.(extractedRoutes.length, extractedRoutes.length);
|
|
2491
|
+
};
|
|
2492
|
+
/**
|
|
2493
|
+
* Extract property access keys from a consumer file's source code near fetch calls.
|
|
2494
|
+
*
|
|
2495
|
+
* Looks for three patterns after a fetch/response variable assignment:
|
|
2496
|
+
* 1. Destructuring: `const { data, pagination } = await res.json()`
|
|
2497
|
+
* 2. Property access: `response.data`, `result.items`
|
|
2498
|
+
* 3. Optional chaining: `data?.key1?.key2`
|
|
2499
|
+
*
|
|
2500
|
+
* Returns deduplicated top-level property names accessed on the response.
|
|
2501
|
+
*
|
|
2502
|
+
* NOTE: This scans the entire file content, not just code near a specific fetch call.
|
|
2503
|
+
* If a file has multiple fetch calls to different routes, all accessed keys are
|
|
2504
|
+
* attributed to each fetch. This is an acceptable tradeoff for regex-based extraction.
|
|
2505
|
+
*/
|
|
2506
|
+
/** Common method names on response/data objects that are NOT property accesses */
|
|
2507
|
+
// Properties/methods to ignore when extracting consumer accessed keys from `data.X` patterns.
|
|
2508
|
+
// Avoids false positives from Fetch API, Array, Object, Promise, and DOM access on variables
|
|
2509
|
+
// that happen to share names with response variables (data, result, response, etc.).
|
|
2510
|
+
const RESPONSE_ACCESS_BLOCKLIST = new Set([
|
|
2511
|
+
// Fetch/Response API
|
|
2512
|
+
'json',
|
|
2513
|
+
'text',
|
|
2514
|
+
'blob',
|
|
2515
|
+
'arrayBuffer',
|
|
2516
|
+
'formData',
|
|
2517
|
+
'ok',
|
|
2518
|
+
'status',
|
|
2519
|
+
'headers',
|
|
2520
|
+
'clone',
|
|
2521
|
+
// Promise
|
|
2522
|
+
'then',
|
|
2523
|
+
'catch',
|
|
2524
|
+
'finally',
|
|
2525
|
+
// Array
|
|
2526
|
+
'map',
|
|
2527
|
+
'filter',
|
|
2528
|
+
'forEach',
|
|
2529
|
+
'reduce',
|
|
2530
|
+
'find',
|
|
2531
|
+
'some',
|
|
2532
|
+
'every',
|
|
2533
|
+
'push',
|
|
2534
|
+
'pop',
|
|
2535
|
+
'shift',
|
|
2536
|
+
'unshift',
|
|
2537
|
+
'splice',
|
|
2538
|
+
'slice',
|
|
2539
|
+
'concat',
|
|
2540
|
+
'join',
|
|
2541
|
+
'sort',
|
|
2542
|
+
'reverse',
|
|
2543
|
+
'includes',
|
|
2544
|
+
'indexOf',
|
|
2545
|
+
// Object
|
|
2546
|
+
'length',
|
|
2547
|
+
'toString',
|
|
2548
|
+
'valueOf',
|
|
2549
|
+
'keys',
|
|
2550
|
+
'values',
|
|
2551
|
+
'entries',
|
|
2552
|
+
// DOM methods — file-download patterns often reuse `data`/`response` variable names
|
|
2553
|
+
'appendChild',
|
|
2554
|
+
'removeChild',
|
|
2555
|
+
'insertBefore',
|
|
2556
|
+
'replaceChild',
|
|
2557
|
+
'replaceChildren',
|
|
2558
|
+
'createElement',
|
|
2559
|
+
'getElementById',
|
|
2560
|
+
'querySelector',
|
|
2561
|
+
'querySelectorAll',
|
|
2562
|
+
'setAttribute',
|
|
2563
|
+
'getAttribute',
|
|
2564
|
+
'removeAttribute',
|
|
2565
|
+
'hasAttribute',
|
|
2566
|
+
'addEventListener',
|
|
2567
|
+
'removeEventListener',
|
|
2568
|
+
'dispatchEvent',
|
|
2569
|
+
'classList',
|
|
2570
|
+
'className',
|
|
2571
|
+
'parentNode',
|
|
2572
|
+
'parentElement',
|
|
2573
|
+
'childNodes',
|
|
2574
|
+
'children',
|
|
2575
|
+
'nextSibling',
|
|
2576
|
+
'previousSibling',
|
|
2577
|
+
'firstChild',
|
|
2578
|
+
'lastChild',
|
|
2579
|
+
'click',
|
|
2580
|
+
'focus',
|
|
2581
|
+
'blur',
|
|
2582
|
+
'submit',
|
|
2583
|
+
'reset',
|
|
2584
|
+
'innerHTML',
|
|
2585
|
+
'outerHTML',
|
|
2586
|
+
'textContent',
|
|
2587
|
+
'innerText',
|
|
2588
|
+
]);
|
|
2589
|
+
export const extractConsumerAccessedKeys = (content) => {
|
|
2590
|
+
const keys = new Set();
|
|
2591
|
+
// Pattern 1: Destructuring from .json() — const { key1, key2 } = await res.json()
|
|
2592
|
+
// Also matches: const { key1, key2 } = await (await fetch(...)).json()
|
|
2593
|
+
const destructurePattern = /(?:const|let|var)\s+\{([^}]+)\}\s*=\s*(?:await\s+)?(?:\w+\.json\s*\(\)|(?:await\s+)?(?:fetch|axios|got)\s*\([^)]*\)(?:\.then\s*\([^)]*\))?(?:\.json\s*\(\))?)/g;
|
|
2594
|
+
let match;
|
|
2595
|
+
while ((match = destructurePattern.exec(content)) !== null) {
|
|
2596
|
+
const destructuredBody = match[1];
|
|
2597
|
+
// Extract identifiers from destructuring, handling renamed bindings (key: alias)
|
|
2598
|
+
const keyPattern = /(\w+)\s*(?::\s*\w+)?/g;
|
|
2599
|
+
let keyMatch;
|
|
2600
|
+
while ((keyMatch = keyPattern.exec(destructuredBody)) !== null) {
|
|
2601
|
+
keys.add(keyMatch[1]);
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
// Pattern 2: Destructuring from a data/result/response/json variable
|
|
2605
|
+
// e.g., const { items, total } = data; or const { error } = result;
|
|
2606
|
+
const dataVarDestructure = /(?:const|let|var)\s+\{([^}]+)\}\s*=\s*(?:data|result|response|json|body|res)\b/g;
|
|
2607
|
+
while ((match = dataVarDestructure.exec(content)) !== null) {
|
|
2608
|
+
const destructuredBody = match[1];
|
|
2609
|
+
const keyPattern = /(\w+)\s*(?::\s*\w+)?/g;
|
|
2610
|
+
let keyMatch;
|
|
2611
|
+
while ((keyMatch = keyPattern.exec(destructuredBody)) !== null) {
|
|
2612
|
+
keys.add(keyMatch[1]);
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
// Pattern 3: Property access on common response variable names
|
|
2616
|
+
// Matches: data.key, response.key, result.key, json.key, body.key
|
|
2617
|
+
// Also matches optional chaining: data?.key
|
|
2618
|
+
const propAccessPattern = /\b(?:data|response|result|json|body|res)\s*(?:\?\.|\.)(\w+)/g;
|
|
2619
|
+
while ((match = propAccessPattern.exec(content)) !== null) {
|
|
2620
|
+
const key = match[1];
|
|
2621
|
+
// Skip common method calls that aren't property accesses
|
|
2622
|
+
if (!RESPONSE_ACCESS_BLOCKLIST.has(key)) {
|
|
2623
|
+
keys.add(key);
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
return [...keys];
|
|
2627
|
+
};
|
|
2628
|
+
/**
|
|
2629
|
+
* Create FETCHES edges from extracted fetch() calls to matching Route nodes.
|
|
2630
|
+
* When consumerContents is provided, extracts property access patterns from
|
|
2631
|
+
* consumer files and encodes them in the edge reason field.
|
|
2632
|
+
*/
|
|
2633
|
+
export const processNextjsFetchRoutes = (graph, fetchCalls, routeRegistry, // routeURL → handlerFilePath
|
|
2634
|
+
consumerContents) => {
|
|
2635
|
+
// Pre-count how many routes each consumer file matches (for confidence attribution)
|
|
2636
|
+
const routeCountByFile = new Map();
|
|
2637
|
+
for (const call of fetchCalls) {
|
|
2638
|
+
const normalized = normalizeFetchURL(call.fetchURL);
|
|
2639
|
+
if (!normalized)
|
|
2640
|
+
continue;
|
|
2641
|
+
for (const [routeURL] of routeRegistry) {
|
|
2642
|
+
if (routeMatches(normalized, routeURL)) {
|
|
2643
|
+
routeCountByFile.set(call.filePath, (routeCountByFile.get(call.filePath) ?? 0) + 1);
|
|
2644
|
+
break;
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2647
|
+
}
|
|
2648
|
+
for (const call of fetchCalls) {
|
|
2649
|
+
const normalized = normalizeFetchURL(call.fetchURL);
|
|
2650
|
+
if (!normalized)
|
|
2651
|
+
continue;
|
|
2652
|
+
for (const [routeURL] of routeRegistry) {
|
|
2653
|
+
if (routeMatches(normalized, routeURL)) {
|
|
2654
|
+
const sourceId = generateId('File', call.filePath);
|
|
2655
|
+
const routeNodeId = generateId('Route', routeURL);
|
|
2656
|
+
// Extract consumer accessed keys if file content is available
|
|
2657
|
+
let reason = 'fetch-url-match';
|
|
2658
|
+
if (consumerContents) {
|
|
2659
|
+
const content = consumerContents.get(call.filePath);
|
|
2660
|
+
if (content) {
|
|
2661
|
+
const accessedKeys = extractConsumerAccessedKeys(content);
|
|
2662
|
+
if (accessedKeys.length > 0) {
|
|
2663
|
+
reason = `fetch-url-match|keys:${accessedKeys.join(',')}`;
|
|
2664
|
+
}
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
// Encode multi-fetch count so downstream can set confidence
|
|
2668
|
+
const fetchCount = routeCountByFile.get(call.filePath) ?? 1;
|
|
2669
|
+
if (fetchCount > 1) {
|
|
2670
|
+
reason = `${reason}|fetches:${fetchCount}`;
|
|
2671
|
+
}
|
|
2672
|
+
graph.addRelationship({
|
|
2673
|
+
id: generateId('FETCHES', `${sourceId}->${routeNodeId}`),
|
|
2674
|
+
sourceId,
|
|
2675
|
+
targetId: routeNodeId,
|
|
2676
|
+
type: 'FETCHES',
|
|
2677
|
+
confidence: 0.9,
|
|
2678
|
+
reason,
|
|
2679
|
+
});
|
|
2680
|
+
break;
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
};
|
|
2685
|
+
/**
|
|
2686
|
+
* Extract fetch() calls from source files (sequential path).
|
|
2687
|
+
* Workers handle this via tree-sitter captures in parse-worker; this function
|
|
2688
|
+
* provides the same extraction for the sequential fallback path.
|
|
2689
|
+
*/
|
|
2690
|
+
export const extractFetchCallsFromFiles = async (files, astCache) => {
|
|
2691
|
+
const parser = await loadParser();
|
|
2692
|
+
const result = [];
|
|
2693
|
+
for (const file of files) {
|
|
2694
|
+
const language = getLanguageFromFilename(file.path);
|
|
2695
|
+
if (!language)
|
|
2696
|
+
continue;
|
|
2697
|
+
if (!isLanguageAvailable(language))
|
|
2698
|
+
continue;
|
|
2699
|
+
const provider = getProvider(language);
|
|
2700
|
+
const queryStr = provider.treeSitterQueries;
|
|
2701
|
+
if (!queryStr)
|
|
2702
|
+
continue;
|
|
2703
|
+
await loadLanguage(language, file.path);
|
|
2704
|
+
let tree = astCache.get(file.path);
|
|
2705
|
+
if (!tree) {
|
|
2706
|
+
const parseContent = provider.preprocessSource?.(file.content, file.path) ?? file.content;
|
|
2707
|
+
try {
|
|
2708
|
+
tree = parseSourceSafe(parser, parseContent, undefined, {
|
|
2709
|
+
bufferSize: getTreeSitterBufferSize(parseContent),
|
|
2710
|
+
});
|
|
2711
|
+
}
|
|
2712
|
+
catch {
|
|
2713
|
+
continue;
|
|
2714
|
+
}
|
|
2715
|
+
astCache.set(file.path, tree);
|
|
2716
|
+
}
|
|
2717
|
+
let matches;
|
|
2718
|
+
try {
|
|
2719
|
+
const lang = parser.getLanguage();
|
|
2720
|
+
const query = new Parser.Query(lang, queryStr);
|
|
2721
|
+
matches = query.matches(tree.rootNode);
|
|
2722
|
+
}
|
|
2723
|
+
catch {
|
|
2724
|
+
continue;
|
|
2725
|
+
}
|
|
2726
|
+
for (const match of matches) {
|
|
2727
|
+
const captureMap = {};
|
|
2728
|
+
match.captures.forEach((c) => (captureMap[c.name] = c.node));
|
|
2729
|
+
if (captureMap['route.fetch']) {
|
|
2730
|
+
const urlNode = captureMap['route.url'] ?? captureMap['route.template_url'];
|
|
2731
|
+
if (urlNode) {
|
|
2732
|
+
result.push({
|
|
2733
|
+
filePath: file.path,
|
|
2734
|
+
fetchURL: urlNode.text,
|
|
2735
|
+
lineNumber: captureMap['route.fetch'].startPosition.row,
|
|
2736
|
+
});
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2739
|
+
else if (captureMap['http_client'] && captureMap['http_client.url']) {
|
|
2740
|
+
const method = captureMap['http_client.method']?.text;
|
|
2741
|
+
const url = captureMap['http_client.url'].text;
|
|
2742
|
+
const HTTP_CLIENT_ONLY = new Set(['head', 'options', 'request', 'ajax']);
|
|
2743
|
+
if (method && HTTP_CLIENT_ONLY.has(method) && url.startsWith('/')) {
|
|
2744
|
+
result.push({
|
|
2745
|
+
filePath: file.path,
|
|
2746
|
+
fetchURL: url,
|
|
2747
|
+
lineNumber: captureMap['http_client'].startPosition.row,
|
|
2748
|
+
});
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2753
|
+
return result;
|
|
2754
|
+
};
|