gitnexus 1.6.8-rc.8 → 1.6.8
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 +3 -3
- package/dist/_shared/graph/types.d.ts +31 -1
- package/dist/_shared/graph/types.d.ts.map +1 -1
- package/dist/_shared/language-detection.d.ts.map +1 -1
- package/dist/_shared/language-detection.js +11 -1
- package/dist/_shared/language-detection.js.map +1 -1
- package/dist/_shared/lbug/schema-constants.d.ts +1 -1
- package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
- package/dist/_shared/lbug/schema-constants.js +6 -0
- package/dist/_shared/lbug/schema-constants.js.map +1 -1
- package/dist/_shared/scope-resolution/parsed-file.d.ts +21 -0
- package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/symbol-definition.d.ts +6 -0
- package/dist/_shared/scope-resolution/symbol-definition.d.ts.map +1 -1
- package/dist/cli/ai-context.d.ts +31 -1
- package/dist/cli/ai-context.js +18 -16
- package/dist/cli/analyze-config.js +11 -0
- package/dist/cli/analyze.d.ts +14 -0
- package/dist/cli/analyze.js +98 -0
- package/dist/cli/embedding-dims.d.ts +33 -0
- package/dist/cli/embedding-dims.js +39 -0
- package/dist/cli/eval-server.d.ts +8 -0
- package/dist/cli/eval-server.js +242 -2
- package/dist/cli/help-i18n.js +19 -0
- package/dist/cli/i18n/en.d.ts +15 -1
- package/dist/cli/i18n/en.js +15 -1
- package/dist/cli/i18n/resources.d.ts +29 -1
- package/dist/cli/i18n/zh-CN.d.ts +14 -0
- package/dist/cli/i18n/zh-CN.js +15 -1
- package/dist/cli/index.js +89 -1
- package/dist/cli/mcp.d.ts +6 -1
- package/dist/cli/mcp.js +26 -1
- package/dist/cli/optional-grammars.d.ts +6 -8
- package/dist/cli/optional-grammars.js +8 -11
- package/dist/cli/setup.d.ts +3 -1
- package/dist/cli/setup.js +67 -17
- package/dist/cli/skill-gen.js +2 -1
- package/dist/cli/tool.d.ts +19 -1
- package/dist/cli/tool.js +96 -3
- package/dist/core/embeddings/hf-env.d.ts +0 -66
- package/dist/core/embeddings/http-client.d.ts +7 -0
- package/dist/core/embeddings/http-client.js +5 -3
- package/dist/core/embeddings/server-mapping.d.ts +1 -1
- package/dist/core/embeddings/server-mapping.js +7 -3
- package/dist/core/graph/import-cycles.d.ts +10 -0
- package/dist/core/graph/import-cycles.js +103 -0
- package/dist/core/group/cross-impact.d.ts +2 -1
- package/dist/core/group/cross-impact.js +5 -1
- package/dist/core/group/extractors/grpc-patterns/proto.js +10 -6
- package/dist/core/group/extractors/http-patterns/kotlin.js +7 -7
- package/dist/core/group/extractors/http-route-extractor.d.ts +24 -0
- package/dist/core/group/extractors/http-route-extractor.js +22 -4
- package/dist/core/group/extractors/include-extractor.js +22 -13
- package/dist/core/group/sync.d.ts +27 -1
- package/dist/core/group/sync.js +0 -0
- package/dist/core/incremental/subgraph-extract.js +18 -1
- package/dist/core/ingestion/cfg/cfg-builder.d.ts +114 -0
- package/dist/core/ingestion/cfg/cfg-builder.js +196 -0
- package/dist/core/ingestion/cfg/collect.d.ts +51 -0
- package/dist/core/ingestion/cfg/collect.js +77 -0
- package/dist/core/ingestion/cfg/control-dependence.d.ts +58 -0
- package/dist/core/ingestion/cfg/control-dependence.js +182 -0
- package/dist/core/ingestion/cfg/control-flow-context.d.ts +106 -0
- package/dist/core/ingestion/cfg/control-flow-context.js +124 -0
- package/dist/core/ingestion/cfg/emit.d.ts +293 -0
- package/dist/core/ingestion/cfg/emit.js +646 -0
- package/dist/core/ingestion/cfg/post-dominators.d.ts +79 -0
- package/dist/core/ingestion/cfg/post-dominators.js +176 -0
- package/dist/core/ingestion/cfg/reaching-def-reason-codec.d.ts +106 -0
- package/dist/core/ingestion/cfg/reaching-def-reason-codec.js +133 -0
- package/dist/core/ingestion/cfg/reaching-defs-graph.d.ts +18 -0
- package/dist/core/ingestion/cfg/reaching-defs-graph.js +312 -0
- package/dist/core/ingestion/cfg/reaching-defs.d.ts +162 -0
- package/dist/core/ingestion/cfg/reaching-defs.js +785 -0
- package/dist/core/ingestion/cfg/synthetic-escape.d.ts +124 -0
- package/dist/core/ingestion/cfg/synthetic-escape.js +300 -0
- package/dist/core/ingestion/cfg/traversal-result.d.ts +20 -0
- package/dist/core/ingestion/cfg/traversal-result.js +2 -0
- package/dist/core/ingestion/cfg/types.d.ts +274 -0
- package/dist/core/ingestion/cfg/types.js +13 -0
- package/dist/core/ingestion/cfg/visitors/c-cpp-harvest.d.ts +111 -0
- package/dist/core/ingestion/cfg/visitors/c-cpp-harvest.js +541 -0
- package/dist/core/ingestion/cfg/visitors/c-cpp.d.ts +74 -0
- package/dist/core/ingestion/cfg/visitors/c-cpp.js +584 -0
- package/dist/core/ingestion/cfg/visitors/call-site-harvest.d.ts +195 -0
- package/dist/core/ingestion/cfg/visitors/call-site-harvest.js +350 -0
- package/dist/core/ingestion/cfg/visitors/csharp-harvest.d.ts +124 -0
- package/dist/core/ingestion/cfg/visitors/csharp-harvest.js +589 -0
- package/dist/core/ingestion/cfg/visitors/csharp.d.ts +87 -0
- package/dist/core/ingestion/cfg/visitors/csharp.js +867 -0
- package/dist/core/ingestion/cfg/visitors/dart-harvest.d.ts +278 -0
- package/dist/core/ingestion/cfg/visitors/dart-harvest.js +870 -0
- package/dist/core/ingestion/cfg/visitors/dart.d.ts +119 -0
- package/dist/core/ingestion/cfg/visitors/dart.js +836 -0
- package/dist/core/ingestion/cfg/visitors/go-harvest.d.ts +160 -0
- package/dist/core/ingestion/cfg/visitors/go-harvest.js +621 -0
- package/dist/core/ingestion/cfg/visitors/go.d.ts +108 -0
- package/dist/core/ingestion/cfg/visitors/go.js +638 -0
- package/dist/core/ingestion/cfg/visitors/java-harvest.d.ts +108 -0
- package/dist/core/ingestion/cfg/visitors/java-harvest.js +513 -0
- package/dist/core/ingestion/cfg/visitors/java.d.ts +101 -0
- package/dist/core/ingestion/cfg/visitors/java.js +812 -0
- package/dist/core/ingestion/cfg/visitors/kotlin-harvest.d.ts +253 -0
- package/dist/core/ingestion/cfg/visitors/kotlin-harvest.js +719 -0
- package/dist/core/ingestion/cfg/visitors/kotlin.d.ts +121 -0
- package/dist/core/ingestion/cfg/visitors/kotlin.js +809 -0
- package/dist/core/ingestion/cfg/visitors/php-harvest.d.ts +172 -0
- package/dist/core/ingestion/cfg/visitors/php-harvest.js +626 -0
- package/dist/core/ingestion/cfg/visitors/php.d.ts +96 -0
- package/dist/core/ingestion/cfg/visitors/php.js +721 -0
- package/dist/core/ingestion/cfg/visitors/python-harvest.d.ts +212 -0
- package/dist/core/ingestion/cfg/visitors/python-harvest.js +739 -0
- package/dist/core/ingestion/cfg/visitors/python.d.ts +103 -0
- package/dist/core/ingestion/cfg/visitors/python.js +558 -0
- package/dist/core/ingestion/cfg/visitors/ruby-harvest.d.ts +223 -0
- package/dist/core/ingestion/cfg/visitors/ruby-harvest.js +587 -0
- package/dist/core/ingestion/cfg/visitors/ruby.d.ts +112 -0
- package/dist/core/ingestion/cfg/visitors/ruby.js +756 -0
- package/dist/core/ingestion/cfg/visitors/rust-harvest.d.ts +324 -0
- package/dist/core/ingestion/cfg/visitors/rust-harvest.js +873 -0
- package/dist/core/ingestion/cfg/visitors/rust.d.ts +95 -0
- package/dist/core/ingestion/cfg/visitors/rust.js +558 -0
- package/dist/core/ingestion/cfg/visitors/scope-tree-harvest.d.ts +87 -0
- package/dist/core/ingestion/cfg/visitors/scope-tree-harvest.js +116 -0
- package/dist/core/ingestion/cfg/visitors/swift-harvest.d.ts +255 -0
- package/dist/core/ingestion/cfg/visitors/swift-harvest.js +679 -0
- package/dist/core/ingestion/cfg/visitors/swift.d.ts +116 -0
- package/dist/core/ingestion/cfg/visitors/swift.js +787 -0
- package/dist/core/ingestion/cfg/visitors/typescript-harvest.d.ts +149 -0
- package/dist/core/ingestion/cfg/visitors/typescript-harvest.js +1056 -0
- package/dist/core/ingestion/cfg/visitors/typescript.d.ts +66 -0
- package/dist/core/ingestion/cfg/visitors/typescript.js +583 -0
- package/dist/core/ingestion/import-resolvers/utils.js +2 -0
- package/dist/core/ingestion/language-provider.d.ts +23 -2
- package/dist/core/ingestion/languages/c-cpp.js +17 -5
- package/dist/core/ingestion/languages/cobol.js +4 -0
- package/dist/core/ingestion/languages/cpp/arity-metadata.js +41 -2
- package/dist/core/ingestion/languages/cpp/captures.js +50 -1
- package/dist/core/ingestion/languages/cpp/conversion-rank.d.ts +2 -0
- package/dist/core/ingestion/languages/cpp/conversion-rank.js +89 -0
- package/dist/core/ingestion/languages/cpp/header-scan.d.ts +1 -1
- package/dist/core/ingestion/languages/cpp/header-scan.js +2 -2
- package/dist/core/ingestion/languages/cpp/inline-namespaces.js +7 -2
- package/dist/core/ingestion/languages/cpp/member-lookup.js +2 -1
- package/dist/core/ingestion/languages/cpp/query.js +23 -0
- package/dist/core/ingestion/languages/cpp/scope-resolver.js +2 -1
- package/dist/core/ingestion/languages/csharp.js +2 -0
- package/dist/core/ingestion/languages/dart.js +2 -0
- package/dist/core/ingestion/languages/go.js +2 -0
- package/dist/core/ingestion/languages/java.js +3 -0
- package/dist/core/ingestion/languages/kotlin/query.js +3 -2
- package/dist/core/ingestion/languages/kotlin.js +8 -1
- package/dist/core/ingestion/languages/php.js +2 -0
- package/dist/core/ingestion/languages/python.js +2 -0
- package/dist/core/ingestion/languages/ruby.js +2 -0
- package/dist/core/ingestion/languages/rust.js +2 -0
- package/dist/core/ingestion/languages/swift.js +2 -0
- package/dist/core/ingestion/languages/typescript.js +5 -0
- package/dist/core/ingestion/languages/vue.js +6 -0
- package/dist/core/ingestion/method-extractors/configs/c-cpp.js +6 -13
- package/dist/core/ingestion/method-extractors/generic.js +1 -0
- package/dist/core/ingestion/method-types.d.ts +2 -0
- package/dist/core/ingestion/model/symbol-table.d.ts +1 -0
- package/dist/core/ingestion/model/symbol-table.js +1 -0
- package/dist/core/ingestion/mro-processor.js +109 -0
- package/dist/core/ingestion/parsing-processor.js +44 -0
- package/dist/core/ingestion/pipeline-phases/call-summaries.d.ts +26 -0
- package/dist/core/ingestion/pipeline-phases/call-summaries.js +46 -0
- package/dist/core/ingestion/pipeline-phases/index.d.ts +2 -0
- package/dist/core/ingestion/pipeline-phases/index.js +2 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +0 -21
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +18 -1
- package/dist/core/ingestion/pipeline-phases/routes.d.ts +10 -0
- package/dist/core/ingestion/pipeline-phases/routes.js +30 -1
- package/dist/core/ingestion/pipeline-phases/taint-summaries.d.ts +30 -0
- package/dist/core/ingestion/pipeline-phases/taint-summaries.js +81 -0
- package/dist/core/ingestion/pipeline.d.ts +92 -10
- package/dist/core/ingestion/pipeline.js +11 -2
- package/dist/core/ingestion/scope-extractor.js +12 -2
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +9 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.d.ts +75 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.js +67 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +12 -2
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +14 -2
- package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +6 -1
- package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +13 -1
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +9 -1
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +28 -2
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +4 -0
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +9 -0
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +6 -1
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +174 -40
- package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +24 -0
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +276 -190
- package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +43 -3
- package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +75 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +349 -1
- package/dist/core/ingestion/scope-resolution/resolution-outcome.d.ts +1 -1
- package/dist/core/ingestion/taint/call-summary-codec.d.ts +75 -0
- package/dist/core/ingestion/taint/call-summary-codec.js +128 -0
- package/dist/core/ingestion/taint/call-summary-emit.d.ts +43 -0
- package/dist/core/ingestion/taint/call-summary-emit.js +70 -0
- package/dist/core/ingestion/taint/call-summary-harvest.d.ts +77 -0
- package/dist/core/ingestion/taint/call-summary-harvest.js +179 -0
- package/dist/core/ingestion/taint/call-summary-model.d.ts +47 -0
- package/dist/core/ingestion/taint/call-summary-model.js +25 -0
- package/dist/core/ingestion/taint/emit.d.ts +125 -0
- package/dist/core/ingestion/taint/emit.js +208 -0
- package/dist/core/ingestion/taint/interproc-emit.d.ts +50 -0
- package/dist/core/ingestion/taint/interproc-emit.js +88 -0
- package/dist/core/ingestion/taint/interproc-solver.d.ts +110 -0
- package/dist/core/ingestion/taint/interproc-solver.js +312 -0
- package/dist/core/ingestion/taint/match.d.ts +153 -0
- package/dist/core/ingestion/taint/match.js +278 -0
- package/dist/core/ingestion/taint/path-codec.d.ts +134 -0
- package/dist/core/ingestion/taint/path-codec.js +190 -0
- package/dist/core/ingestion/taint/propagate.d.ts +216 -0
- package/dist/core/ingestion/taint/propagate.js +659 -0
- package/dist/core/ingestion/taint/site-safety.d.ts +29 -0
- package/dist/core/ingestion/taint/site-safety.js +98 -0
- package/dist/core/ingestion/taint/source-sink-config.d.ts +107 -23
- package/dist/core/ingestion/taint/source-sink-config.js +35 -12
- package/dist/core/ingestion/taint/source-sink-registry.d.ts +6 -4
- package/dist/core/ingestion/taint/source-sink-registry.js +6 -4
- package/dist/core/ingestion/taint/summary-harvest-driver.d.ts +80 -0
- package/dist/core/ingestion/taint/summary-harvest-driver.js +179 -0
- package/dist/core/ingestion/taint/summary-harvest.d.ts +89 -0
- package/dist/core/ingestion/taint/summary-harvest.js +537 -0
- package/dist/core/ingestion/taint/summary-model.d.ts +200 -0
- package/dist/core/ingestion/taint/summary-model.js +86 -0
- package/dist/core/ingestion/taint/typescript-model.d.ts +38 -0
- package/dist/core/ingestion/taint/typescript-model.js +102 -0
- package/dist/core/ingestion/type-extractors/c-cpp.js +21 -0
- package/dist/core/ingestion/utils/env.d.ts +7 -0
- package/dist/core/ingestion/utils/env.js +12 -0
- package/dist/core/ingestion/utils/method-props.js +1 -0
- package/dist/core/ingestion/workers/clone-safety.d.ts +109 -0
- package/dist/core/ingestion/workers/clone-safety.js +465 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +23 -0
- package/dist/core/ingestion/workers/parse-worker.js +81 -53
- package/dist/core/ingestion/workers/post-result.d.ts +22 -0
- package/dist/core/ingestion/workers/post-result.js +87 -0
- package/dist/core/ingestion/workers/result-merge.d.ts +20 -0
- package/dist/core/ingestion/workers/result-merge.js +43 -0
- package/dist/core/ingestion/workers/worker-pool.d.ts +15 -40
- package/dist/core/ingestion/workers/worker-pool.js +43 -16
- package/dist/core/lbug/csv-generator.d.ts +51 -3
- package/dist/core/lbug/csv-generator.js +369 -255
- package/dist/core/lbug/lbug-adapter.d.ts +58 -18
- package/dist/core/lbug/lbug-adapter.js +346 -84
- package/dist/core/lbug/lbug-config.d.ts +19 -0
- package/dist/core/lbug/lbug-config.js +29 -0
- package/dist/core/lbug/pdg-emit-sink.d.ts +127 -0
- package/dist/core/lbug/pdg-emit-sink.js +358 -0
- package/dist/core/lbug/pool-adapter.d.ts +35 -0
- package/dist/core/lbug/pool-adapter.js +90 -1
- package/dist/core/lbug/rel-pair-routing.d.ts +54 -0
- package/dist/core/lbug/rel-pair-routing.js +140 -0
- package/dist/core/lbug/schema.d.ts +2 -2
- package/dist/core/lbug/schema.js +3 -0
- package/dist/core/logger.d.ts +0 -5
- package/dist/core/run-analyze.d.ts +96 -0
- package/dist/core/run-analyze.js +207 -3
- package/dist/core/tree-sitter/parser-loader.js +5 -4
- package/dist/core/tree-sitter/safe-parse.d.ts +0 -5
- package/dist/core/tree-sitter/vendored-grammars.d.ts +39 -0
- package/dist/core/tree-sitter/vendored-grammars.js +57 -0
- package/dist/core/wiki/generator.js +3 -1
- package/dist/core/wiki/graph-queries.d.ts +6 -0
- package/dist/core/wiki/graph-queries.js +9 -1
- package/dist/mcp/http-transport.d.ts +134 -0
- package/dist/mcp/http-transport.js +503 -0
- package/dist/mcp/local/local-backend.d.ts +110 -24
- package/dist/mcp/local/local-backend.js +1225 -19
- package/dist/mcp/local/pdg-impact.d.ts +384 -0
- package/dist/mcp/local/pdg-impact.js +1972 -0
- package/dist/mcp/resources.js +13 -1
- package/dist/mcp/tools.d.ts +12 -0
- package/dist/mcp/tools.js +214 -7
- package/dist/server/analyze-launch.d.ts +29 -0
- package/dist/server/analyze-launch.js +137 -0
- package/dist/server/analyze-upload.d.ts +33 -0
- package/dist/server/analyze-upload.js +123 -0
- package/dist/server/analyze-worker-ipc.d.ts +58 -0
- package/dist/server/analyze-worker-ipc.js +16 -0
- package/dist/server/analyze-worker.d.ts +17 -1
- package/dist/server/analyze-worker.js +7 -1
- package/dist/server/api.d.ts +17 -0
- package/dist/server/api.js +132 -168
- package/dist/server/git-clone.d.ts +43 -2
- package/dist/server/git-clone.js +211 -16
- package/dist/server/mcp-http.d.ts +5 -6
- package/dist/server/mcp-http.js +9 -82
- package/dist/server/middleware.d.ts +40 -0
- package/dist/server/middleware.js +92 -0
- package/dist/server/private-ip.d.ts +1 -0
- package/dist/server/private-ip.js +15 -0
- package/dist/server/upload-ingest.d.ts +56 -0
- package/dist/server/upload-ingest.js +276 -0
- package/dist/server/upload-paths.d.ts +37 -0
- package/dist/server/upload-paths.js +57 -0
- package/dist/server/upload-sweep.d.ts +20 -0
- package/dist/server/upload-sweep.js +57 -0
- package/dist/storage/git.js +5 -8
- package/dist/storage/parse-cache.d.ts +22 -1
- package/dist/storage/parse-cache.js +44 -10
- package/dist/storage/repo-manager.d.ts +121 -10
- package/dist/storage/repo-manager.js +43 -5
- package/dist/types/pipeline.d.ts +9 -0
- package/hooks/antigravity/gitnexus-antigravity-hook.cjs +110 -10
- package/hooks/claude/gitnexus-hook.cjs +110 -13
- package/hooks/claude/hook-db-lock-probe.cjs +513 -26
- package/package.json +8 -2
- package/scripts/assert-publish-grammar-coverage.cjs +31 -0
- package/scripts/build-tree-sitter-grammars.cjs +16 -10
- package/scripts/cross-platform-tests.ts +1 -0
- package/skills/gitnexus-debugging.md +16 -4
- package/skills/gitnexus-exploring.md +3 -3
- package/skills/gitnexus-guide.md +33 -0
- package/skills/gitnexus-pdg-query.md +89 -0
- package/skills/gitnexus-refactoring.md +1 -1
- package/skills/gitnexus-taint-analysis.md +178 -0
- package/vendor/tree-sitter-c/package.json +1 -1
- package/vendor/tree-sitter-dart/package.json +1 -1
- package/vendor/tree-sitter-kotlin/package.json +1 -1
- package/vendor/tree-sitter-proto/package.json +1 -1
- package/vendor/tree-sitter-swift/package.json +1 -1
- package/web/assets/{agent-CKPMqImC.js → agent-D3Xd7zZE.js} +123 -123
- package/web/assets/architecture-7EHR7CIX-6QZW5X65-DxJw65fT.js +1 -0
- package/web/assets/architectureDiagram-UL44E2DR-ChH9xW-N.js +36 -0
- package/web/assets/{blockDiagram-7IZFK4PR-B4niSLvR.js → blockDiagram-7IZFK4PR-BkWhpzWB.js} +1 -1
- package/web/assets/{c4Diagram-Y2BXMSZH-DIzEXalR.js → c4Diagram-Y2BXMSZH-DtBvpzWD.js} +1 -1
- package/web/assets/{chunk-3SSMPTDK-BzGeeheI.js → chunk-3SSMPTDK-B4_etUhr.js} +2 -2
- package/web/assets/{chunk-5IMINLNL-CnTLsXIV.js → chunk-5IMINLNL-DOdTeQri.js} +1 -1
- package/web/assets/{chunk-5VCL7Z4A-w1BCx98Z.js → chunk-5VCL7Z4A-CQ-2dYWj.js} +1 -1
- package/web/assets/chunk-6764PJDD-DpIS7hxD.js +1 -0
- package/web/assets/{chunk-67TQ5CYL-CChC2cAv.js → chunk-67TQ5CYL-4sNPJU1q.js} +3 -3
- package/web/assets/{chunk-7W6UQGC5-CgFyfZ6k.js → chunk-7W6UQGC5-KyEG0HQg.js} +1 -1
- package/web/assets/{chunk-AZZRMDJM-5E9ARdDQ.js → chunk-AZZRMDJM-y-xJBuMz.js} +1 -1
- package/web/assets/{chunk-INKRHTLW-ELpfuk9h.js → chunk-INKRHTLW-Nu4ri9P-.js} +1 -1
- package/web/assets/{chunk-JQRUD6KW-Dcq_Qnyy.js → chunk-JQRUD6KW-Dbva2Z17.js} +1 -1
- package/web/assets/{chunk-KGFNY3KK-W-VR57xb.js → chunk-KGFNY3KK-JGMJvKCK.js} +1 -1
- package/web/assets/chunk-KGYTTC2M-CWC_c3H9.js +161 -0
- package/web/assets/{chunk-KNLZD3CH-BuZBOcso.js → chunk-KNLZD3CH-BGlP6X9R.js} +1 -1
- package/web/assets/{chunk-KRXBNO2N-DH2NPM7I.js → chunk-KRXBNO2N-BW5o0KUN.js} +1 -1
- package/web/assets/{chunk-LCXTWHL2-Dfmux4m1.js → chunk-LCXTWHL2-zoomRtWX.js} +1 -1
- package/web/assets/{chunk-LII3EMHJ-3X33tCpU.js → chunk-LII3EMHJ-D-jm-dLa.js} +1 -1
- package/web/assets/{chunk-QA3QBVWF-D5Ha3GgB.js → chunk-QA3QBVWF-BgRW3SXd.js} +1 -1
- package/web/assets/{chunk-RG4AUYOV-CAkzcoRj.js → chunk-RG4AUYOV-DD0B2N2B.js} +1 -1
- package/web/assets/{chunk-T2UQINTJ-kGYG7ppP.js → chunk-T2UQINTJ-DSBqkzH7.js} +1 -1
- package/web/assets/chunk-T5OCTHI4-DdZvN-9i.js +1 -0
- package/web/assets/{chunk-UY5QBCOK-Du-c56gs.js → chunk-UY5QBCOK-DyF0vyGd.js} +1 -1
- package/web/assets/{chunk-W44A43WB-lrCpuupA.js → chunk-W44A43WB-JqMLIpR0.js} +1 -1
- package/web/assets/{chunk-ZXARS5L4-CSZRiyOf.js → chunk-ZXARS5L4-CxN8oiwI.js} +1 -1
- package/web/assets/classDiagram-KGZ6W3CR-DJm9BgF-.js +1 -0
- package/web/assets/classDiagram-v2-72OJOZXJ-DJm9BgF-.js +1 -0
- package/web/assets/{context-builder-_HS0v2ma.js → context-builder-BHiFUA8O.js} +10 -2
- package/web/assets/{cose-bilkent-UX7MHV2Q-BHv2bbtl.js → cose-bilkent-UX7MHV2Q-D71wNYRJ.js} +1 -1
- package/web/assets/dagre-ND4H6XIP-BIkxmhcu.js +4 -0
- package/web/assets/diagram-3NCE3AQN-BTa6iH0T.js +43 -0
- package/web/assets/diagram-GF46GFSD-DZDV0vBj.js +24 -0
- package/web/assets/{diagram-HNR7UZ2L-fUWreZzx.js → diagram-HNR7UZ2L-DFCfd5LI.js} +2 -2
- package/web/assets/diagram-QXG6HAR7-BzM--Jj0.js +24 -0
- package/web/assets/diagram-WEQXMOUZ-DFFwz0k4.js +10 -0
- package/web/assets/{erDiagram-L5TCEMPS-QN2eEP1e.js → erDiagram-L5TCEMPS-CfHMJJqo.js} +1 -1
- package/web/assets/eventmodeling-FCH6USID-MREXMVOE-BQm9QzEa.js +1 -0
- package/web/assets/{flowDiagram-H6V6AXG4-CXbXImlN.js → flowDiagram-H6V6AXG4-Be805QiJ.js} +3 -3
- package/web/assets/{ganttDiagram-JCBTUEKG-BHQssTz6.js → ganttDiagram-JCBTUEKG-D3iL2Aet.js} +1 -1
- package/web/assets/gitGraph-WXDBUCRP-R675I2BI-Cr_Bm2Nb.js +1 -0
- package/web/assets/gitGraphDiagram-S2ZK5IYY-CPIDgxGm.js +106 -0
- package/web/assets/index-BvYev1UJ.js +635 -0
- package/web/assets/index-_lgn7hs5.css +2 -0
- package/web/assets/info-J43DQDTF-KCYPFFUO-Cs4p2oyk.js +1 -0
- package/web/assets/infoDiagram-3YFTVSEB-Y7BKSoke.js +2 -0
- package/web/assets/{ishikawaDiagram-BNXS4ZKH-6LKOvBfp.js → ishikawaDiagram-BNXS4ZKH-CT4SHFuI.js} +1 -1
- package/web/assets/{journeyDiagram-M6C3CM3L-DNPYCpvq.js → journeyDiagram-M6C3CM3L-CvArszLo.js} +1 -1
- package/web/assets/{kanban-definition-75IXJCU3-DgDi9oJT.js → kanban-definition-75IXJCU3-C6yDxr7N.js} +3 -3
- package/web/assets/{katex-K3KEBU37-CrVLz8l7.js → katex-K3KEBU37-CTc5BslQ.js} +1 -1
- package/web/assets/{mindmap-definition-2TDM6QVE-CwR5sBB-.js → mindmap-definition-2TDM6QVE-BOc2WEne.js} +1 -1
- package/web/assets/packet-YPE3B663-LP52Z2RK-CKCrztD2.js +1 -0
- package/web/assets/pie-LRSECV5Y-TCRJHUBD-BB7pHqoD.js +1 -0
- package/web/assets/{pieDiagram-CU6KROY3-By8g6f6B.js → pieDiagram-CU6KROY3-Cvc0f_Pd.js} +1 -1
- package/web/assets/{quadrantDiagram-VICAPDV7-bAm6Hi_5.js → quadrantDiagram-VICAPDV7-yXSZ2lLz.js} +1 -1
- package/web/assets/radar-GUYGQ44K-RDLRG3WG-DhoTOcuK.js +1 -0
- package/web/assets/{requirementDiagram-JXO7QTGE-7oDcJ1_J.js → requirementDiagram-JXO7QTGE-BRcZvHs4.js} +1 -1
- package/web/assets/{sankeyDiagram-URQDO5SZ-LFJz53u5.js → sankeyDiagram-URQDO5SZ-1qEjlFIB.js} +1 -1
- package/web/assets/{sequenceDiagram-VS2MUI6T-FySeKCUy.js → sequenceDiagram-VS2MUI6T-PcSCuvrB.js} +3 -3
- package/web/assets/stateDiagram-7D4R322I-D6qYSV1E.js +1 -0
- package/web/assets/stateDiagram-v2-36443NZ5-Bg-ImOSX.js +1 -0
- package/web/assets/{timeline-definition-O6YCAMPW-Ds2CnVZK.js → timeline-definition-O6YCAMPW-QWkh01xw.js} +1 -1
- package/web/assets/treeView-BLDUP644-QA4HXRO3-ODehyGKL.js +1 -0
- package/web/assets/treemap-LRROVOQU-LLAWBHMP-BfpgC-7o.js +1 -0
- package/web/assets/{vennDiagram-MWXL3ELB-DUIEwXWp.js → vennDiagram-MWXL3ELB-ak4DfwuM.js} +3 -3
- package/web/assets/wardley-L42UT6IY-5TKZOOLJ-CNeL6VHE.js +1 -0
- package/web/assets/{wardleyDiagram-CUQ6CDDI-DEiFPQih.js → wardleyDiagram-CUQ6CDDI-CTzR4Loq.js} +1 -1
- package/web/assets/{xychartDiagram-N2JHSOCM-BUbayhST.js → xychartDiagram-N2JHSOCM-DA1BEaQz.js} +1 -1
- package/web/index.html +13 -15
- package/scripts/materialize-vendor-grammars.cjs +0 -97
- package/web/assets/architecture-7EHR7CIX-6QZW5X65-BETj2x4g.js +0 -1
- package/web/assets/architectureDiagram-UL44E2DR-D1EXI0zA.js +0 -36
- package/web/assets/chunk-2T2R6R2M-n6s9JZqv.js +0 -4
- package/web/assets/chunk-2UTLFMKG-CMBB1TMN.js +0 -1
- package/web/assets/chunk-4R4BOZG6-mKhL59ul.js +0 -159
- package/web/assets/chunk-6764PJDD-ChwMM2z4.js +0 -1
- package/web/assets/chunk-7J6CGLKN-DT-b53FT.js +0 -10
- package/web/assets/chunk-C62D2QBJ-CDAWj26E.js +0 -1
- package/web/assets/chunk-CEXFNPSA-D68Tk6ls.js +0 -1
- package/web/assets/chunk-CilyBKbf.js +0 -1
- package/web/assets/chunk-J5EP6P6S-DxWW0yvu.js +0 -1
- package/web/assets/chunk-KGYTTC2M-C6PHeuay.js +0 -1
- package/web/assets/chunk-RERM46MO-DQNbXtfw.js +0 -1
- package/web/assets/chunk-RKZBBQEN-BmTPLSyv.js +0 -1
- package/web/assets/chunk-RLI5ZMPA-DExu2DOK.js +0 -1
- package/web/assets/chunk-T5OCTHI4--9wWpVws.js +0 -1
- package/web/assets/chunk-UP6H54XL-DsKdC6jC.js +0 -1
- package/web/assets/chunk-UXSXWOXI-DR81EqLr.js +0 -1
- package/web/assets/classDiagram-KGZ6W3CR-Bv93af_b.js +0 -1
- package/web/assets/classDiagram-v2-72OJOZXJ-CLfEqHUa.js +0 -1
- package/web/assets/dagre-ND4H6XIP-DsIr9MOv.js +0 -4
- package/web/assets/diagram-3NCE3AQN-BqAtKUpW.js +0 -43
- package/web/assets/diagram-GF46GFSD-CaBG6n6o.js +0 -24
- package/web/assets/diagram-QXG6HAR7-CZ-O3rcV.js +0 -24
- package/web/assets/diagram-WEQXMOUZ-P4lSL4GH.js +0 -10
- package/web/assets/eventmodeling-FCH6USID-MREXMVOE-B3myHUst.js +0 -1
- package/web/assets/gitGraph-WXDBUCRP-R675I2BI-DF3rtf4A.js +0 -1
- package/web/assets/gitGraphDiagram-S2ZK5IYY-CCwVLT__.js +0 -106
- package/web/assets/index-BKWA-m7o.css +0 -2
- package/web/assets/index-CG6q8eTs.js +0 -626
- package/web/assets/info-J43DQDTF-KCYPFFUO-R4XV_akJ.js +0 -1
- package/web/assets/infoDiagram-3YFTVSEB-DmLICZx1.js +0 -2
- package/web/assets/packet-YPE3B663-LP52Z2RK-CgQPdNZa.js +0 -1
- package/web/assets/pie-LRSECV5Y-TCRJHUBD-BzWOwc_c.js +0 -1
- package/web/assets/radar-GUYGQ44K-RDLRG3WG-B7KG0FN7.js +0 -1
- package/web/assets/stateDiagram-7D4R322I-CB2nABwH.js +0 -1
- package/web/assets/stateDiagram-v2-36443NZ5-COBGd2RL.js +0 -1
- package/web/assets/treeView-BLDUP644-QA4HXRO3-CRSxEwLO.js +0 -1
- package/web/assets/treemap-LRROVOQU-LLAWBHMP-a0PfKoVn.js +0 -1
- package/web/assets/wardley-L42UT6IY-5TKZOOLJ-BKQ6e5mC.js +0 -1
- /package/web/assets/{chunk-AQ6EADP3-CCFje6lL.js → chunk-AQ6EADP3-CZhslHi-.js} +0 -0
|
@@ -0,0 +1,873 @@
|
|
|
1
|
+
import { CallSiteFactAccumulator as FactAccumulator, finalizeChain } from './call-site-harvest.js';
|
|
2
|
+
/** Node types that own a nested CFG — their subtrees are opaque to harvesting. */
|
|
3
|
+
const NESTED_FUNCTION_TYPES = new Set(['function_item', 'closure_expression']);
|
|
4
|
+
/** Pattern containers whose identifier leaves are binding targets. */
|
|
5
|
+
const PATTERN_CONTAINER_TYPES = new Set([
|
|
6
|
+
'tuple_pattern',
|
|
7
|
+
'slice_pattern',
|
|
8
|
+
'or_pattern',
|
|
9
|
+
'reference_pattern',
|
|
10
|
+
]);
|
|
11
|
+
export class RustHarvester {
|
|
12
|
+
fnNode;
|
|
13
|
+
bindings = [];
|
|
14
|
+
/** Single function-scope name → binding index (v1: no block scope). */
|
|
15
|
+
table = new Map();
|
|
16
|
+
synthetic = new Map();
|
|
17
|
+
fnId;
|
|
18
|
+
/** >0 while walking a conditionally-evaluated subexpression — defs become may-defs. */
|
|
19
|
+
conditionalDepth = 0;
|
|
20
|
+
/**
|
|
21
|
+
* `call_expression` node id → binding indices its single-target result is
|
|
22
|
+
* assigned to (`let x = f()` / `x = g()` ⇒ `[x]`). Populated just before the
|
|
23
|
+
* value walk reaches the call (see {@link registerResultDefs}) and consumed by
|
|
24
|
+
* {@link visitCall}. Mirrors the Swift / Kotlin / Go / Python harvesters' map.
|
|
25
|
+
*/
|
|
26
|
+
resultDefTargets = new Map();
|
|
27
|
+
constructor(fnNode) {
|
|
28
|
+
this.fnNode = fnNode;
|
|
29
|
+
this.fnId = fnNode.id;
|
|
30
|
+
this.declareParams(fnNode);
|
|
31
|
+
const body = this.bodyOf(fnNode);
|
|
32
|
+
if (body)
|
|
33
|
+
this.prescan(body);
|
|
34
|
+
}
|
|
35
|
+
/** The completed binding table — pass to `CfgBuilder.finish`. */
|
|
36
|
+
bindingTable() {
|
|
37
|
+
return this.bindings;
|
|
38
|
+
}
|
|
39
|
+
/** The function/closure body node (a `block` for a fn, block-or-expr for a closure). */
|
|
40
|
+
bodyOf(fnNode) {
|
|
41
|
+
return fnNode.childForFieldName('body') ?? undefined;
|
|
42
|
+
}
|
|
43
|
+
// ── phase 1: declaration pre-scan ────────────────────────────────────────
|
|
44
|
+
declare(nameNode, kind) {
|
|
45
|
+
const name = nameNode.text;
|
|
46
|
+
if (!name || name === '_' || this.table.has(name))
|
|
47
|
+
return;
|
|
48
|
+
this.table.set(name, this.bindings.length);
|
|
49
|
+
this.bindings.push({
|
|
50
|
+
name,
|
|
51
|
+
declLine: nameNode.startPosition.row + 1,
|
|
52
|
+
declColumn: nameNode.startPosition.column,
|
|
53
|
+
kind,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/** Declare every parameter binder of a fn / closure (incl. `mut`, typed). */
|
|
57
|
+
declareParams(fnNode) {
|
|
58
|
+
const params = fnNode.childForFieldName('parameters') ??
|
|
59
|
+
fnNode.namedChildren.find((c) => c.type === 'parameters' || c.type === 'closure_parameters');
|
|
60
|
+
if (!params)
|
|
61
|
+
return;
|
|
62
|
+
for (let i = 0; i < params.namedChildCount; i++) {
|
|
63
|
+
const p = params.namedChild(i);
|
|
64
|
+
if (!p)
|
|
65
|
+
continue;
|
|
66
|
+
if (p.type === 'parameter') {
|
|
67
|
+
const pat = p.childForFieldName('pattern');
|
|
68
|
+
if (pat)
|
|
69
|
+
this.declarePattern(pat, 'param');
|
|
70
|
+
}
|
|
71
|
+
else if (p.type === 'self_parameter') {
|
|
72
|
+
// `&self` / `self` — bind `self` so reads of it resolve to a real
|
|
73
|
+
// binding rather than a synthetic module name.
|
|
74
|
+
const id = p.namedChildren.find((c) => c.type === 'self');
|
|
75
|
+
if (id)
|
|
76
|
+
this.declare(id, 'param');
|
|
77
|
+
}
|
|
78
|
+
else if (p.type === 'identifier') {
|
|
79
|
+
// Bare closure param `|x|`.
|
|
80
|
+
this.declare(p, 'param');
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
// Typed closure param without the `parameter` wrapper, etc.
|
|
84
|
+
this.declarePattern(p, 'param');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Pre-scan the function body once, declaring every bound name. Recurses into
|
|
90
|
+
* compound expressions but NOT into nested `function_item` / `closure_expression`
|
|
91
|
+
* bodies (opaque).
|
|
92
|
+
*/
|
|
93
|
+
prescan(node) {
|
|
94
|
+
const t = node.type;
|
|
95
|
+
if (NESTED_FUNCTION_TYPES.has(t) && node.id !== this.fnId)
|
|
96
|
+
return;
|
|
97
|
+
switch (t) {
|
|
98
|
+
case 'let_declaration': {
|
|
99
|
+
const pat = node.childForFieldName('pattern');
|
|
100
|
+
if (pat)
|
|
101
|
+
this.declarePattern(pat, 'let');
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
case 'for_expression': {
|
|
105
|
+
const pat = node.childForFieldName('pattern');
|
|
106
|
+
if (pat)
|
|
107
|
+
this.declarePattern(pat, 'let');
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
case 'let_condition': {
|
|
111
|
+
// `if let PAT = …` / `while let PAT = …`.
|
|
112
|
+
const pat = node.childForFieldName('pattern');
|
|
113
|
+
if (pat)
|
|
114
|
+
this.declarePattern(pat, 'let');
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
case 'match_arm': {
|
|
118
|
+
const pat = node.childForFieldName('pattern');
|
|
119
|
+
if (pat)
|
|
120
|
+
this.declarePattern(pat, 'let');
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
default:
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
127
|
+
const c = node.namedChild(i);
|
|
128
|
+
if (c)
|
|
129
|
+
this.prescan(c);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Declare every identifier leaf of a binding pattern. Handles the full Rust
|
|
134
|
+
* pattern taxonomy: tuple / slice / struct / tuple-struct / ref / mut /
|
|
135
|
+
* captured (`@`) / or patterns. A `tuple_struct_pattern`'s `type` field is the
|
|
136
|
+
* variant PATH (`Some`, `Ok`) — not a binding; only its inner patterns bind.
|
|
137
|
+
* `_`, literals and range patterns bind nothing.
|
|
138
|
+
*/
|
|
139
|
+
declarePattern(pat, kind) {
|
|
140
|
+
const t = pat.type;
|
|
141
|
+
if (t === 'identifier') {
|
|
142
|
+
this.declare(pat, kind);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (t === '_')
|
|
146
|
+
return; // standalone wildcard pattern is the `_` node
|
|
147
|
+
if (t === 'match_pattern') {
|
|
148
|
+
// The arm pattern wrapper — declare its (non-guard) sub-patterns. The
|
|
149
|
+
// guard `if cond` is a value test, not a binder.
|
|
150
|
+
const guard = pat.childForFieldName('condition');
|
|
151
|
+
for (let i = 0; i < pat.namedChildCount; i++) {
|
|
152
|
+
const c = pat.namedChild(i);
|
|
153
|
+
if (c && c.id !== guard?.id)
|
|
154
|
+
this.declarePattern(c, kind);
|
|
155
|
+
}
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (PATTERN_CONTAINER_TYPES.has(t)) {
|
|
159
|
+
for (let i = 0; i < pat.namedChildCount; i++) {
|
|
160
|
+
const c = pat.namedChild(i);
|
|
161
|
+
if (c)
|
|
162
|
+
this.declarePattern(c, kind);
|
|
163
|
+
}
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (t === 'tuple_struct_pattern') {
|
|
167
|
+
// `Some(n)` / `Ok(v)` — the `type` field is the variant path (not a binder);
|
|
168
|
+
// every other named child is an inner binding pattern.
|
|
169
|
+
const typeNode = pat.childForFieldName('type');
|
|
170
|
+
for (let i = 0; i < pat.namedChildCount; i++) {
|
|
171
|
+
const c = pat.namedChild(i);
|
|
172
|
+
if (c && c.id !== typeNode?.id)
|
|
173
|
+
this.declarePattern(c, kind);
|
|
174
|
+
}
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (t === 'struct_pattern') {
|
|
178
|
+
// `Point { x, y }` — each `field_pattern` binds; shorthand `x` binds `x`,
|
|
179
|
+
// `x: pat` binds `pat`'s leaves. The `type` field is the struct path.
|
|
180
|
+
for (let i = 0; i < pat.namedChildCount; i++) {
|
|
181
|
+
const c = pat.namedChild(i);
|
|
182
|
+
if (!c)
|
|
183
|
+
continue;
|
|
184
|
+
if (c.type === 'field_pattern') {
|
|
185
|
+
this.declareFieldPattern(c, kind);
|
|
186
|
+
}
|
|
187
|
+
else if (c.type === 'shorthand_field_identifier') {
|
|
188
|
+
this.declare(c, kind);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (t === 'ref_pattern' || t === 'mut_pattern' || t === 'reference_pattern') {
|
|
194
|
+
// `ref r` / `mut m` / `&p` — unwrap to the inner pattern.
|
|
195
|
+
for (let i = 0; i < pat.namedChildCount; i++) {
|
|
196
|
+
const c = pat.namedChild(i);
|
|
197
|
+
if (c && c.type !== 'mutable_specifier')
|
|
198
|
+
this.declarePattern(c, kind);
|
|
199
|
+
}
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (t === 'captured_pattern') {
|
|
203
|
+
// `v @ subpat` — `v` binds AND the subpattern's leaves bind.
|
|
204
|
+
for (let i = 0; i < pat.namedChildCount; i++) {
|
|
205
|
+
const c = pat.namedChild(i);
|
|
206
|
+
if (c)
|
|
207
|
+
this.declarePattern(c, kind);
|
|
208
|
+
}
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
// range_pattern / literal patterns / scoped paths bind nothing.
|
|
212
|
+
}
|
|
213
|
+
/** `field_pattern` — shorthand `x` binds `x`; `x: pat` binds `pat`'s leaves. */
|
|
214
|
+
declareFieldPattern(field, kind) {
|
|
215
|
+
const name = field.childForFieldName('name');
|
|
216
|
+
const pattern = field.childForFieldName('pattern');
|
|
217
|
+
if (pattern) {
|
|
218
|
+
this.declarePattern(pattern, kind);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (name && name.type === 'shorthand_field_identifier') {
|
|
222
|
+
this.declare(name, kind);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
// Fallback: declare any identifier / shorthand leaf.
|
|
226
|
+
for (let i = 0; i < field.namedChildCount; i++) {
|
|
227
|
+
const c = field.namedChild(i);
|
|
228
|
+
if (c?.type === 'shorthand_field_identifier' || c?.type === 'identifier')
|
|
229
|
+
this.declare(c, kind);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// ── phase 2: per-statement fact extraction ───────────────────────────────
|
|
233
|
+
/** Def/use facts for one statement (or construct-header expression) node. */
|
|
234
|
+
facts(node) {
|
|
235
|
+
const acc = new FactAccumulator(node.startPosition.row + 1);
|
|
236
|
+
this.walkValue(node, acc);
|
|
237
|
+
return acc.finish();
|
|
238
|
+
}
|
|
239
|
+
/** Facts for an expression whose WHOLE evaluation is conditional (guards/tests). */
|
|
240
|
+
factsConditional(node) {
|
|
241
|
+
const acc = new FactAccumulator(node.startPosition.row + 1);
|
|
242
|
+
this.conditional(() => this.walkValue(node, acc));
|
|
243
|
+
return acc.finish();
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Facts for a `for PAT in ITER` head: the loop pattern's leaves are defs, the
|
|
247
|
+
* iterated expression a use.
|
|
248
|
+
*/
|
|
249
|
+
forHeadFacts(stmt) {
|
|
250
|
+
const acc = new FactAccumulator(stmt.startPosition.row + 1);
|
|
251
|
+
const value = stmt.childForFieldName('value');
|
|
252
|
+
const pat = stmt.childForFieldName('pattern');
|
|
253
|
+
if (value)
|
|
254
|
+
this.walkValue(value, acc);
|
|
255
|
+
if (pat)
|
|
256
|
+
this.defPattern(pat, acc);
|
|
257
|
+
return acc.finish();
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Facts for ONLY a `let_declaration`'s PATTERN bindings (no value walk) — used
|
|
261
|
+
* when the value is a control-flow expression already harvested by the visitor,
|
|
262
|
+
* so the binding defs land on a separate continuation block without
|
|
263
|
+
* double-counting the value's uses.
|
|
264
|
+
*/
|
|
265
|
+
letPatternFacts(stmt) {
|
|
266
|
+
const acc = new FactAccumulator(stmt.startPosition.row + 1);
|
|
267
|
+
const pat = stmt.childForFieldName('pattern');
|
|
268
|
+
if (pat)
|
|
269
|
+
this.defPattern(pat, acc);
|
|
270
|
+
return acc.finish();
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Facts for a `let PAT = VALUE` condition (`if let` / `while let`): the value
|
|
274
|
+
* is a use, the pattern's leaves are defs. When `conditional` is true the defs
|
|
275
|
+
* become may-defs (a `while let` re-test may not bind on the exit iteration).
|
|
276
|
+
*/
|
|
277
|
+
letConditionFacts(cond, conditional) {
|
|
278
|
+
const acc = new FactAccumulator(cond.startPosition.row + 1);
|
|
279
|
+
const run = () => {
|
|
280
|
+
const value = cond.childForFieldName('value');
|
|
281
|
+
const pat = cond.childForFieldName('pattern');
|
|
282
|
+
if (value)
|
|
283
|
+
this.walkValue(value, acc);
|
|
284
|
+
if (pat)
|
|
285
|
+
this.defPattern(pat, acc);
|
|
286
|
+
};
|
|
287
|
+
if (conditional)
|
|
288
|
+
this.conditional(run);
|
|
289
|
+
else
|
|
290
|
+
run();
|
|
291
|
+
return acc.finish();
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Facts for a `match` arm's PATTERN bindings (#2206): `Some(n) => …` binds `n`
|
|
295
|
+
* from the matched subject. The bindings are MAY-defs (only the arm that
|
|
296
|
+
* actually matches binds; a later arm tests only when earlier ones didn't) and
|
|
297
|
+
* are attached to the dispatch block, co-located with the subject's use, so a
|
|
298
|
+
* tainted subject can propagate to the arm binding. The guard is skipped by
|
|
299
|
+
* {@link defPattern}'s `match_pattern` handling. `undefined` when the pattern
|
|
300
|
+
* binds nothing (`_`, a literal, a unit variant).
|
|
301
|
+
*/
|
|
302
|
+
matchArmPatternFacts(arm) {
|
|
303
|
+
const acc = new FactAccumulator(arm.startPosition.row + 1);
|
|
304
|
+
const pat = arm.childForFieldName('pattern');
|
|
305
|
+
if (pat)
|
|
306
|
+
this.conditional(() => this.defPattern(pat, acc));
|
|
307
|
+
return acc.defCount() ? acc.finish() : undefined;
|
|
308
|
+
}
|
|
309
|
+
/** ENTRY-block facts for the parameters (defs only — incl. default-position uses). */
|
|
310
|
+
paramFacts() {
|
|
311
|
+
const params = this.fnNode.childForFieldName('parameters') ??
|
|
312
|
+
this.fnNode.namedChildren.find((c) => c.type === 'parameters' || c.type === 'closure_parameters');
|
|
313
|
+
if (!params)
|
|
314
|
+
return undefined;
|
|
315
|
+
const acc = new FactAccumulator(this.fnNode.startPosition.row + 1);
|
|
316
|
+
for (let i = 0; i < params.namedChildCount; i++) {
|
|
317
|
+
const p = params.namedChild(i);
|
|
318
|
+
if (!p)
|
|
319
|
+
continue;
|
|
320
|
+
if (p.type === 'parameter') {
|
|
321
|
+
const pat = p.childForFieldName('pattern');
|
|
322
|
+
if (pat)
|
|
323
|
+
this.defPattern(pat, acc);
|
|
324
|
+
}
|
|
325
|
+
else if (p.type === 'self_parameter') {
|
|
326
|
+
const id = p.namedChildren.find((c) => c.type === 'self');
|
|
327
|
+
if (id)
|
|
328
|
+
this.def(id, acc);
|
|
329
|
+
}
|
|
330
|
+
else if (p.type === 'identifier') {
|
|
331
|
+
this.def(p, acc);
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
this.defPattern(p, acc);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return acc.defCount() ? acc.finish() : undefined;
|
|
338
|
+
}
|
|
339
|
+
resolve(nameNode) {
|
|
340
|
+
const name = nameNode.text;
|
|
341
|
+
const idx = this.table.get(name);
|
|
342
|
+
if (idx !== undefined)
|
|
343
|
+
return idx;
|
|
344
|
+
let syn = this.synthetic.get(name);
|
|
345
|
+
if (syn === undefined) {
|
|
346
|
+
syn = this.bindings.length;
|
|
347
|
+
this.synthetic.set(name, syn);
|
|
348
|
+
this.bindings.push({ name, declLine: 0, declColumn: 0, kind: 'module', synthetic: true });
|
|
349
|
+
}
|
|
350
|
+
return syn;
|
|
351
|
+
}
|
|
352
|
+
def(nameNode, acc) {
|
|
353
|
+
if (nameNode.text === '_')
|
|
354
|
+
return; // blank target defines nothing
|
|
355
|
+
if (this.conditionalDepth > 0)
|
|
356
|
+
acc.addMayDef(this.resolve(nameNode));
|
|
357
|
+
else
|
|
358
|
+
acc.addDef(this.resolve(nameNode));
|
|
359
|
+
}
|
|
360
|
+
use(nameNode, acc) {
|
|
361
|
+
if (nameNode.text === '_')
|
|
362
|
+
return;
|
|
363
|
+
acc.addUse(this.resolve(nameNode));
|
|
364
|
+
}
|
|
365
|
+
/** Run `fn` with defs demoted to may-defs (conditionally-evaluated context). */
|
|
366
|
+
conditional(fn) {
|
|
367
|
+
this.conditionalDepth++;
|
|
368
|
+
try {
|
|
369
|
+
fn();
|
|
370
|
+
}
|
|
371
|
+
finally {
|
|
372
|
+
this.conditionalDepth--;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Def each identifier leaf of a binding pattern (the def-position analogue of
|
|
377
|
+
* {@link declarePattern}). A `tuple_struct_pattern`'s `type` field path is a
|
|
378
|
+
* variant name, not a def; its inner patterns bind. A struct field shorthand
|
|
379
|
+
* binds; `_` binds nothing.
|
|
380
|
+
*/
|
|
381
|
+
defPattern(pat, acc) {
|
|
382
|
+
const t = pat.type;
|
|
383
|
+
if (t === 'identifier') {
|
|
384
|
+
this.def(pat, acc);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
if (t === '_')
|
|
388
|
+
return; // standalone wildcard pattern is the `_` node
|
|
389
|
+
if (t === 'match_pattern') {
|
|
390
|
+
const guard = pat.childForFieldName('condition');
|
|
391
|
+
for (let i = 0; i < pat.namedChildCount; i++) {
|
|
392
|
+
const c = pat.namedChild(i);
|
|
393
|
+
if (c && c.id !== guard?.id)
|
|
394
|
+
this.defPattern(c, acc);
|
|
395
|
+
}
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
if (PATTERN_CONTAINER_TYPES.has(t)) {
|
|
399
|
+
for (let i = 0; i < pat.namedChildCount; i++) {
|
|
400
|
+
const c = pat.namedChild(i);
|
|
401
|
+
if (c)
|
|
402
|
+
this.defPattern(c, acc);
|
|
403
|
+
}
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
if (t === 'tuple_struct_pattern') {
|
|
407
|
+
const typeNode = pat.childForFieldName('type');
|
|
408
|
+
for (let i = 0; i < pat.namedChildCount; i++) {
|
|
409
|
+
const c = pat.namedChild(i);
|
|
410
|
+
if (c && c.id !== typeNode?.id)
|
|
411
|
+
this.defPattern(c, acc);
|
|
412
|
+
}
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
if (t === 'struct_pattern') {
|
|
416
|
+
for (let i = 0; i < pat.namedChildCount; i++) {
|
|
417
|
+
const c = pat.namedChild(i);
|
|
418
|
+
if (!c)
|
|
419
|
+
continue;
|
|
420
|
+
if (c.type === 'field_pattern')
|
|
421
|
+
this.defFieldPattern(c, acc);
|
|
422
|
+
else if (c.type === 'shorthand_field_identifier')
|
|
423
|
+
this.def(c, acc);
|
|
424
|
+
}
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
if (t === 'ref_pattern' || t === 'mut_pattern' || t === 'reference_pattern') {
|
|
428
|
+
for (let i = 0; i < pat.namedChildCount; i++) {
|
|
429
|
+
const c = pat.namedChild(i);
|
|
430
|
+
if (c && c.type !== 'mutable_specifier')
|
|
431
|
+
this.defPattern(c, acc);
|
|
432
|
+
}
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
if (t === 'captured_pattern') {
|
|
436
|
+
for (let i = 0; i < pat.namedChildCount; i++) {
|
|
437
|
+
const c = pat.namedChild(i);
|
|
438
|
+
if (c)
|
|
439
|
+
this.defPattern(c, acc);
|
|
440
|
+
}
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
// range / literal / scoped path — binds nothing.
|
|
444
|
+
}
|
|
445
|
+
defFieldPattern(field, acc) {
|
|
446
|
+
const name = field.childForFieldName('name');
|
|
447
|
+
const pattern = field.childForFieldName('pattern');
|
|
448
|
+
if (pattern) {
|
|
449
|
+
this.defPattern(pattern, acc);
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
if (name && name.type === 'shorthand_field_identifier') {
|
|
453
|
+
this.def(name, acc);
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
for (let i = 0; i < field.namedChildCount; i++) {
|
|
457
|
+
const c = field.namedChild(i);
|
|
458
|
+
if (c?.type === 'shorthand_field_identifier' || c?.type === 'identifier')
|
|
459
|
+
this.def(c, acc);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
/** Value-position walk: collect uses; route def positions to the pattern handler. */
|
|
463
|
+
walkValue(node, acc) {
|
|
464
|
+
const t = node.type;
|
|
465
|
+
if (NESTED_FUNCTION_TYPES.has(t) && node.id !== this.fnId)
|
|
466
|
+
return; // opaque
|
|
467
|
+
switch (t) {
|
|
468
|
+
case 'identifier':
|
|
469
|
+
this.use(node, acc);
|
|
470
|
+
return;
|
|
471
|
+
case 'let_declaration': {
|
|
472
|
+
const value = node.childForFieldName('value');
|
|
473
|
+
const pat = node.childForFieldName('pattern');
|
|
474
|
+
const alt = node.childForFieldName('alternative'); // `let … else { … }`
|
|
475
|
+
// Register result-defs BEFORE the value walk so the call the walk reaches
|
|
476
|
+
// carries them — single plain-identifier pattern only (`let x = f()`); a
|
|
477
|
+
// destructuring `let (a, b) = …` attaches nothing (ambiguous mapping).
|
|
478
|
+
if (value && pat && pat.type === 'identifier')
|
|
479
|
+
this.registerResultDefs(value, [pat]);
|
|
480
|
+
if (value)
|
|
481
|
+
this.walkValue(value, acc);
|
|
482
|
+
if (alt)
|
|
483
|
+
this.walkValue(alt, acc);
|
|
484
|
+
if (pat)
|
|
485
|
+
this.defPattern(pat, acc);
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
case 'let_condition': {
|
|
489
|
+
const value = node.childForFieldName('value');
|
|
490
|
+
const pat = node.childForFieldName('pattern');
|
|
491
|
+
if (value)
|
|
492
|
+
this.walkValue(value, acc);
|
|
493
|
+
if (pat)
|
|
494
|
+
this.defPattern(pat, acc);
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
case 'assignment_expression': {
|
|
498
|
+
const left = node.childForFieldName('left');
|
|
499
|
+
const right = node.childForFieldName('right');
|
|
500
|
+
// A plain `x = f(a)` attaches `resultDefs: [x]`; a field/index lvalue does
|
|
501
|
+
// not (no scalar target).
|
|
502
|
+
if (right && left && left.type === 'identifier')
|
|
503
|
+
this.registerResultDefs(right, [left]);
|
|
504
|
+
if (right)
|
|
505
|
+
this.walkValue(right, acc);
|
|
506
|
+
if (left) {
|
|
507
|
+
if (left.type === 'identifier') {
|
|
508
|
+
this.def(left, acc);
|
|
509
|
+
}
|
|
510
|
+
else {
|
|
511
|
+
// field / index lvalue (`obj.f = …`, `a[i] = …`) — root is a use only.
|
|
512
|
+
this.walkValue(left, acc);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
case 'compound_assignment_expr': {
|
|
518
|
+
const left = node.childForFieldName('left');
|
|
519
|
+
const right = node.childForFieldName('right');
|
|
520
|
+
if (right)
|
|
521
|
+
this.walkValue(right, acc);
|
|
522
|
+
if (left) {
|
|
523
|
+
if (left.type === 'identifier') {
|
|
524
|
+
this.use(left, acc);
|
|
525
|
+
this.def(left, acc);
|
|
526
|
+
}
|
|
527
|
+
else {
|
|
528
|
+
this.walkValue(left, acc);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
case 'binary_expression': {
|
|
534
|
+
const left = node.childForFieldName('left');
|
|
535
|
+
const right = node.childForFieldName('right');
|
|
536
|
+
const op = node.childForFieldName('operator')?.text ?? '';
|
|
537
|
+
if (left)
|
|
538
|
+
this.walkValue(left, acc);
|
|
539
|
+
if (right) {
|
|
540
|
+
if (op === '&&' || op === '||')
|
|
541
|
+
this.conditional(() => this.walkValue(right, acc));
|
|
542
|
+
else
|
|
543
|
+
this.walkValue(right, acc);
|
|
544
|
+
}
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
case 'call_expression':
|
|
548
|
+
// A Rust call (`foo(a)`, `a.method(x)`, `Foo::bar(x)`, `foo::<T>(x)`).
|
|
549
|
+
// Records a taint site (callee path, receiver, per-arg occurrences, result
|
|
550
|
+
// defs) while reproducing the uses the old default descent recorded. Rust
|
|
551
|
+
// has no `new` — every site is `kind: 'call'`.
|
|
552
|
+
this.visitCall(node, acc);
|
|
553
|
+
return;
|
|
554
|
+
case 'struct_expression':
|
|
555
|
+
// A Rust struct literal (`Point { x: 1 }`, `mymod::Point { .. }`,
|
|
556
|
+
// `Foo::<T> { .. }`). The Rust CALLS query tags it
|
|
557
|
+
// `@reference.call.constructor`, so it resolves to a constructor id — we
|
|
558
|
+
// record a `kind: 'new'` site (callee = the struct type path) so that id
|
|
559
|
+
// joins into `calleeIds`. Field-init VALUES walk for uses/occurrences.
|
|
560
|
+
this.visitStruct(node, acc);
|
|
561
|
+
return;
|
|
562
|
+
case 'field_expression': {
|
|
563
|
+
// `a.b` value read — the chain-root identifier is a use plus at most one
|
|
564
|
+
// member-read site (the innermost access); the field name is not a scalar
|
|
565
|
+
// binding. Mirrors the Swift / Kotlin / Go value-position member-read
|
|
566
|
+
// semantics.
|
|
567
|
+
this.walkChain(node, acc, false);
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
default:
|
|
571
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
572
|
+
const c = node.namedChild(i);
|
|
573
|
+
if (c)
|
|
574
|
+
this.walkValue(c, acc);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
// ── taint-site harvest ───────────────────────────────────────────────────
|
|
579
|
+
/**
|
|
580
|
+
* When `value`'s root (after unwrapping a `try_expression`) is a
|
|
581
|
+
* `call_expression`, remember that call site should carry `resultDefs` — the
|
|
582
|
+
* binding indices of `targets` (def-position identifiers). Consumed by
|
|
583
|
+
* {@link visitCall} once the value walk reaches the node. Single-target only;
|
|
584
|
+
* the blank target (`_`) binds nothing.
|
|
585
|
+
*/
|
|
586
|
+
registerResultDefs(value, targets) {
|
|
587
|
+
const root = this.unwrapValue(value);
|
|
588
|
+
if (root.type !== 'call_expression')
|
|
589
|
+
return;
|
|
590
|
+
const defs = [];
|
|
591
|
+
for (const target of targets) {
|
|
592
|
+
if (target.type !== 'identifier' || target.text === '_')
|
|
593
|
+
continue;
|
|
594
|
+
defs.push(this.resolve(target));
|
|
595
|
+
}
|
|
596
|
+
if (defs.length > 0)
|
|
597
|
+
this.resultDefTargets.set(root.id, defs);
|
|
598
|
+
}
|
|
599
|
+
/** Strip a `try_expression` (`expr?`) / `await_expression` wrapper around a value. */
|
|
600
|
+
unwrapValue(node) {
|
|
601
|
+
let n = node;
|
|
602
|
+
let hops = 4;
|
|
603
|
+
while ((n.type === 'try_expression' || n.type === 'await_expression') && hops-- > 0) {
|
|
604
|
+
const inner = n.namedChild(0);
|
|
605
|
+
if (!inner)
|
|
606
|
+
break;
|
|
607
|
+
n = inner;
|
|
608
|
+
}
|
|
609
|
+
return n;
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Open + populate a call site for a Rust `call_expression`. `node` IS the
|
|
613
|
+
* `call_expression` — the SAME node the scope query anchors `@reference.call.*`
|
|
614
|
+
* on (its `atRange`), so the resolved-id join lands by exact position (see file
|
|
615
|
+
* header ANCHOR ALIGNMENT). A `call_expression` is always `kind: 'call'`; struct
|
|
616
|
+
* literals (`kind: 'new'`) are harvested separately by {@link visitStruct}.
|
|
617
|
+
*/
|
|
618
|
+
visitCall(node, acc) {
|
|
619
|
+
const calleeNode = node.childForFieldName('function');
|
|
620
|
+
const argsNode = node.childForFieldName('arguments');
|
|
621
|
+
const siteIdx = acc.openCallSite('call', [
|
|
622
|
+
node.startPosition.row + 1,
|
|
623
|
+
node.startPosition.column,
|
|
624
|
+
]);
|
|
625
|
+
acc.pushFrame(siteIdx);
|
|
626
|
+
if (calleeNode)
|
|
627
|
+
this.harvestCallee(calleeNode, siteIdx, acc);
|
|
628
|
+
const resultDefs = this.resultDefTargets.get(node.id);
|
|
629
|
+
if (resultDefs !== undefined)
|
|
630
|
+
acc.setSiteResultDefs(siteIdx, resultDefs);
|
|
631
|
+
if (argsNode) {
|
|
632
|
+
let pos = 0;
|
|
633
|
+
for (let i = 0; i < argsNode.namedChildCount; i++) {
|
|
634
|
+
const arg = argsNode.namedChild(i);
|
|
635
|
+
if (!arg || arg.type === 'line_comment' || arg.type === 'block_comment')
|
|
636
|
+
continue;
|
|
637
|
+
acc.setFrameArg(pos);
|
|
638
|
+
this.walkValue(arg, acc);
|
|
639
|
+
pos++;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
acc.popFrame();
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Open + populate a `kind: 'new'` site for a Rust `struct_expression`
|
|
646
|
+
* (`Point { x: 1 }`, `mymod::Point { .. }`, `Foo::<T> { .. }`). `node` IS the
|
|
647
|
+
* `struct_expression` — the SAME node the Rust scope query anchors
|
|
648
|
+
* `@reference.call.constructor` on (its `atRange`), so the resolved
|
|
649
|
+
* constructor-id join lands by exact position. The `name` field of a
|
|
650
|
+
* `struct_expression` is a `type_identifier` (`Point`), a
|
|
651
|
+
* `scoped_type_identifier` (`mymod::Point`), or a `generic_type_with_turbofish`
|
|
652
|
+
* (`Foo::<T>` / `mymod::Bar::<T>`); all three start at the SAME column as the
|
|
653
|
+
* enclosing `struct_expression` (verified by a real parse), so the broadest-span
|
|
654
|
+
* `@reference.call.constructor` anchor == the `struct_expression` start.
|
|
655
|
+
*
|
|
656
|
+
* The callee path joins the `::`-segments of the type with `.` (NOT `::`) so the
|
|
657
|
+
* LEAF after the last separator is the tail (`mymod::Point` ⇒ `mymod.Point` ⇒
|
|
658
|
+
* leaf `Point`), exactly the tail the CALLS query's `@reference.name` capture
|
|
659
|
+
* resolves and the tail {@link import('../emit.js').calleesOfBlock} extracts via
|
|
660
|
+
* `lastIndexOf('.')`. A type/module path head is never a value binding, so no
|
|
661
|
+
* receiver (mirrors {@link harvestScopedCallee}). The field-init VALUES
|
|
662
|
+
* (`field_initializer` `value`, shorthand `y`, base `..rest`) walk for
|
|
663
|
+
* uses/occurrences; field NAMES are not uses.
|
|
664
|
+
*/
|
|
665
|
+
visitStruct(node, acc) {
|
|
666
|
+
const nameNode = node.childForFieldName('name');
|
|
667
|
+
const bodyNode = node.childForFieldName('body');
|
|
668
|
+
const siteIdx = acc.openCallSite('new', [
|
|
669
|
+
node.startPosition.row + 1,
|
|
670
|
+
node.startPosition.column,
|
|
671
|
+
]);
|
|
672
|
+
acc.pushFrame(siteIdx);
|
|
673
|
+
const path = nameNode ? this.structTypePath(nameNode) : undefined;
|
|
674
|
+
if (path !== undefined)
|
|
675
|
+
acc.setSiteCallee(siteIdx, path);
|
|
676
|
+
if (bodyNode) {
|
|
677
|
+
let pos = 0;
|
|
678
|
+
for (let i = 0; i < bodyNode.namedChildCount; i++) {
|
|
679
|
+
const field = bodyNode.namedChild(i);
|
|
680
|
+
if (!field)
|
|
681
|
+
continue;
|
|
682
|
+
if (field.type === 'field_initializer') {
|
|
683
|
+
// `x: VALUE` — the field NAME is not a use; only VALUE is walked.
|
|
684
|
+
const value = field.childForFieldName('value');
|
|
685
|
+
if (value) {
|
|
686
|
+
acc.setFrameArg(pos);
|
|
687
|
+
this.walkValue(value, acc);
|
|
688
|
+
pos++;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
else if (field.type === 'shorthand_field_initializer') {
|
|
692
|
+
// `y` shorthand — the identifier IS a value use of the local `y`.
|
|
693
|
+
const id = field.namedChild(0);
|
|
694
|
+
if (id) {
|
|
695
|
+
acc.setFrameArg(pos);
|
|
696
|
+
this.walkValue(id, acc);
|
|
697
|
+
pos++;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
else if (field.type === 'base_field_initializer') {
|
|
701
|
+
// `..rest` functional-update base — `rest` is a value use.
|
|
702
|
+
const baseExpr = field.namedChild(0);
|
|
703
|
+
if (baseExpr) {
|
|
704
|
+
acc.setFrameArg(pos);
|
|
705
|
+
this.walkValue(baseExpr, acc);
|
|
706
|
+
pos++;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
acc.popFrame();
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Build the dotted type path of a `struct_expression`'s `name` field. The name
|
|
715
|
+
* is a `type_identifier` (`Point`), a `scoped_type_identifier`
|
|
716
|
+
* (`mymod::Point` — `path` + tail `name` type_identifier), or a
|
|
717
|
+
* `generic_type_with_turbofish` (`Foo::<T>` / `mymod::Bar::<T>` — its `type`
|
|
718
|
+
* field is a `type_identifier` or a `scoped_identifier`; the turbofish
|
|
719
|
+
* `type_arguments` are dropped). Segments join with `.` so the leaf is the type
|
|
720
|
+
* tail (matching the CALLS `@reference.name` tail capture). Returns `undefined`
|
|
721
|
+
* when no segments could be read (defensive — keeps a mis-anchored site from
|
|
722
|
+
* carrying a bogus callee).
|
|
723
|
+
*/
|
|
724
|
+
structTypePath(nameNode) {
|
|
725
|
+
const segments = [];
|
|
726
|
+
const collect = (n) => {
|
|
727
|
+
const t = n.type;
|
|
728
|
+
if (t === 'type_identifier' || t === 'identifier') {
|
|
729
|
+
segments.push(n.text);
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
if (t === 'generic_type_with_turbofish') {
|
|
733
|
+
// `Foo::<T>` / `mymod::Bar::<T>` — descend the `type` field; the
|
|
734
|
+
// `type_arguments` are not part of the resolved type path.
|
|
735
|
+
const typeNode = n.childForFieldName('type');
|
|
736
|
+
if (typeNode)
|
|
737
|
+
collect(typeNode);
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
if (t === 'scoped_type_identifier' || t === 'scoped_identifier') {
|
|
741
|
+
// `mymod::Point` / `mymod::Bar` — head `path` then the tail `name`.
|
|
742
|
+
const pathNode = n.childForFieldName('path');
|
|
743
|
+
if (pathNode)
|
|
744
|
+
collect(pathNode);
|
|
745
|
+
const tail = n.childForFieldName('name');
|
|
746
|
+
if (tail)
|
|
747
|
+
collect(tail);
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
collect(nameNode);
|
|
752
|
+
return segments.length > 0 && segments.every((s) => s !== '') ? segments.join('.') : undefined;
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Record the callee path + receiver for a `call_expression`'s `function` node.
|
|
756
|
+
* Free `identifier` (`foo`), method `field_expression` (`a.method`, receiver
|
|
757
|
+
* root `a`), path `scoped_identifier` (`Foo::bar` ⇒ dotted `Foo.bar`, leaf
|
|
758
|
+
* `bar`), and the turbofish `generic_function` (`foo::<T>` — unwrap the
|
|
759
|
+
* `function` field and recurse). Anything else (a call-rooted chain `f()()`,
|
|
760
|
+
* a parenthesized callable) walks for uses with no static callee path.
|
|
761
|
+
*/
|
|
762
|
+
harvestCallee(calleeNode, siteIdx, acc) {
|
|
763
|
+
const callee = this.unwrapValue(calleeNode);
|
|
764
|
+
if (callee.type === 'identifier') {
|
|
765
|
+
// A bare free call — the callee NAME is a statement-level use but NOT a
|
|
766
|
+
// value occurrence in any enclosing argument.
|
|
767
|
+
if (callee.text !== '_')
|
|
768
|
+
acc.addUseWithoutOccurrence(this.resolve(callee));
|
|
769
|
+
acc.setSiteCallee(siteIdx, callee.text);
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
if (callee.type === 'field_expression') {
|
|
773
|
+
// skipFinalRead: the final `.field` IS the callee, carried by the path.
|
|
774
|
+
const chain = this.walkChain(callee, acc, true);
|
|
775
|
+
if (chain.path !== undefined)
|
|
776
|
+
acc.setSiteCallee(siteIdx, chain.path);
|
|
777
|
+
if (chain.rootIdx !== undefined)
|
|
778
|
+
acc.setSiteReceiver(siteIdx, chain.rootIdx);
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
if (callee.type === 'scoped_identifier') {
|
|
782
|
+
const scoped = this.harvestScopedCallee(callee, acc);
|
|
783
|
+
if (scoped.path !== undefined)
|
|
784
|
+
acc.setSiteCallee(siteIdx, scoped.path);
|
|
785
|
+
if (scoped.rootIdx !== undefined)
|
|
786
|
+
acc.setSiteReceiver(siteIdx, scoped.rootIdx);
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
if (callee.type === 'generic_function') {
|
|
790
|
+
// `foo::<T>(x)` — the turbofish wraps the real callee in `function`.
|
|
791
|
+
const inner = callee.childForFieldName('function');
|
|
792
|
+
if (inner)
|
|
793
|
+
this.harvestCallee(inner, siteIdx, acc);
|
|
794
|
+
else
|
|
795
|
+
this.walkValue(callee, acc);
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
// Call-rooted chains (`f()()`), parenthesized callables — walk for uses; no
|
|
799
|
+
// static callee path.
|
|
800
|
+
this.walkValue(callee, acc);
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
* Walk a `scoped_identifier` (`Foo::bar`, `a::b::c`) callee. The `::`-segments
|
|
804
|
+
* are joined with `.` (NOT `::`) so the LEAF after the last separator is the
|
|
805
|
+
* tail (`Foo::bar` ⇒ `Foo.bar` ⇒ leaf `bar`), matching the Rust CALLS query's
|
|
806
|
+
* `@reference.name` tail capture and {@link
|
|
807
|
+
* import('../emit.js').calleesOfBlock}'s `lastIndexOf('.')` leaf rule. The
|
|
808
|
+
* receiver is set only when the head segment is a bound LOCAL (`a::b::c` with
|
|
809
|
+
* `a` a local); a type / module head (`Foo`, `crate`) is no value binding.
|
|
810
|
+
*/
|
|
811
|
+
harvestScopedCallee(node, acc) {
|
|
812
|
+
const segments = [];
|
|
813
|
+
let cur = node;
|
|
814
|
+
for (;;) {
|
|
815
|
+
if (cur.type === 'scoped_identifier') {
|
|
816
|
+
const name = cur.childForFieldName('name');
|
|
817
|
+
segments.unshift(name?.text ?? '');
|
|
818
|
+
const path = cur.childForFieldName('path');
|
|
819
|
+
if (!path)
|
|
820
|
+
break;
|
|
821
|
+
cur = path;
|
|
822
|
+
}
|
|
823
|
+
else {
|
|
824
|
+
// The head segment — a bare `identifier` (`a` / `Foo` / `crate`) or a
|
|
825
|
+
// `crate`/`self`/`super`/`metavariable` keyword node.
|
|
826
|
+
segments.unshift(cur.text);
|
|
827
|
+
break;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
let rootIdx;
|
|
831
|
+
// Only a head segment that is a bound LOCAL is a receiver (taint substrate);
|
|
832
|
+
// a type / module path head launders no value.
|
|
833
|
+
if (cur.type === 'identifier' && cur.text !== '_' && this.table.has(cur.text)) {
|
|
834
|
+
rootIdx = this.resolve(cur);
|
|
835
|
+
acc.addUse(rootIdx);
|
|
836
|
+
}
|
|
837
|
+
const path = segments.every((s) => s !== '') ? segments.join('.') : undefined;
|
|
838
|
+
return { path, rootIdx };
|
|
839
|
+
}
|
|
840
|
+
/**
|
|
841
|
+
* `field_expression` chain walk shared by value position and callee position.
|
|
842
|
+
* Records the chain-root identifier as a use plus at most ONE member-read site
|
|
843
|
+
* — the INNERMOST access — when the root is an identifier; `skipFinalRead`
|
|
844
|
+
* suppresses it when that access is the callee (carried by the dotted path
|
|
845
|
+
* instead). Mirrors the Swift / Kotlin / Go / Python harvesters' walkChain. A
|
|
846
|
+
* non-identifier root (`self`/literal/call) launders no static path/receiver
|
|
847
|
+
* but its uses + nested sites are still walked.
|
|
848
|
+
*/
|
|
849
|
+
walkChain(node, acc, skipFinalRead) {
|
|
850
|
+
const accesses = [];
|
|
851
|
+
let cur = node;
|
|
852
|
+
for (;;) {
|
|
853
|
+
if (cur.type === 'field_expression') {
|
|
854
|
+
const field = cur.childForFieldName('field');
|
|
855
|
+
accesses.unshift(field?.text ?? '');
|
|
856
|
+
const value = cur.childForFieldName('value');
|
|
857
|
+
if (!value)
|
|
858
|
+
break;
|
|
859
|
+
cur = value;
|
|
860
|
+
}
|
|
861
|
+
else {
|
|
862
|
+
break;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
// The shared terminal: root-use record + innermost member-read + path-join.
|
|
866
|
+
// The non-identifier root (`self.x.f()`, `foo().bar`, a tuple index) walks for
|
|
867
|
+
// uses + nested sites.
|
|
868
|
+
return finalizeChain(acc, cur, accesses, skipFinalRead, (t) => t === 'identifier', {
|
|
869
|
+
resolve: (n) => this.resolve(n),
|
|
870
|
+
walkRoot: (n) => this.walkValue(n, acc),
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
}
|