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.
Files changed (439) hide show
  1. package/README.md +3 -3
  2. package/dist/_shared/graph/types.d.ts +31 -1
  3. package/dist/_shared/graph/types.d.ts.map +1 -1
  4. package/dist/_shared/language-detection.d.ts.map +1 -1
  5. package/dist/_shared/language-detection.js +11 -1
  6. package/dist/_shared/language-detection.js.map +1 -1
  7. package/dist/_shared/lbug/schema-constants.d.ts +1 -1
  8. package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
  9. package/dist/_shared/lbug/schema-constants.js +6 -0
  10. package/dist/_shared/lbug/schema-constants.js.map +1 -1
  11. package/dist/_shared/scope-resolution/parsed-file.d.ts +21 -0
  12. package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -1
  13. package/dist/_shared/scope-resolution/symbol-definition.d.ts +6 -0
  14. package/dist/_shared/scope-resolution/symbol-definition.d.ts.map +1 -1
  15. package/dist/cli/ai-context.d.ts +31 -1
  16. package/dist/cli/ai-context.js +18 -16
  17. package/dist/cli/analyze-config.js +11 -0
  18. package/dist/cli/analyze.d.ts +14 -0
  19. package/dist/cli/analyze.js +98 -0
  20. package/dist/cli/embedding-dims.d.ts +33 -0
  21. package/dist/cli/embedding-dims.js +39 -0
  22. package/dist/cli/eval-server.d.ts +8 -0
  23. package/dist/cli/eval-server.js +242 -2
  24. package/dist/cli/help-i18n.js +19 -0
  25. package/dist/cli/i18n/en.d.ts +15 -1
  26. package/dist/cli/i18n/en.js +15 -1
  27. package/dist/cli/i18n/resources.d.ts +29 -1
  28. package/dist/cli/i18n/zh-CN.d.ts +14 -0
  29. package/dist/cli/i18n/zh-CN.js +15 -1
  30. package/dist/cli/index.js +89 -1
  31. package/dist/cli/mcp.d.ts +6 -1
  32. package/dist/cli/mcp.js +26 -1
  33. package/dist/cli/optional-grammars.d.ts +6 -8
  34. package/dist/cli/optional-grammars.js +8 -11
  35. package/dist/cli/setup.d.ts +3 -1
  36. package/dist/cli/setup.js +67 -17
  37. package/dist/cli/skill-gen.js +2 -1
  38. package/dist/cli/tool.d.ts +19 -1
  39. package/dist/cli/tool.js +96 -3
  40. package/dist/core/embeddings/hf-env.d.ts +0 -66
  41. package/dist/core/embeddings/http-client.d.ts +7 -0
  42. package/dist/core/embeddings/http-client.js +5 -3
  43. package/dist/core/embeddings/server-mapping.d.ts +1 -1
  44. package/dist/core/embeddings/server-mapping.js +7 -3
  45. package/dist/core/graph/import-cycles.d.ts +10 -0
  46. package/dist/core/graph/import-cycles.js +103 -0
  47. package/dist/core/group/cross-impact.d.ts +2 -1
  48. package/dist/core/group/cross-impact.js +5 -1
  49. package/dist/core/group/extractors/grpc-patterns/proto.js +10 -6
  50. package/dist/core/group/extractors/http-patterns/kotlin.js +7 -7
  51. package/dist/core/group/extractors/http-route-extractor.d.ts +24 -0
  52. package/dist/core/group/extractors/http-route-extractor.js +22 -4
  53. package/dist/core/group/extractors/include-extractor.js +22 -13
  54. package/dist/core/group/sync.d.ts +27 -1
  55. package/dist/core/group/sync.js +0 -0
  56. package/dist/core/incremental/subgraph-extract.js +18 -1
  57. package/dist/core/ingestion/cfg/cfg-builder.d.ts +114 -0
  58. package/dist/core/ingestion/cfg/cfg-builder.js +196 -0
  59. package/dist/core/ingestion/cfg/collect.d.ts +51 -0
  60. package/dist/core/ingestion/cfg/collect.js +77 -0
  61. package/dist/core/ingestion/cfg/control-dependence.d.ts +58 -0
  62. package/dist/core/ingestion/cfg/control-dependence.js +182 -0
  63. package/dist/core/ingestion/cfg/control-flow-context.d.ts +106 -0
  64. package/dist/core/ingestion/cfg/control-flow-context.js +124 -0
  65. package/dist/core/ingestion/cfg/emit.d.ts +293 -0
  66. package/dist/core/ingestion/cfg/emit.js +646 -0
  67. package/dist/core/ingestion/cfg/post-dominators.d.ts +79 -0
  68. package/dist/core/ingestion/cfg/post-dominators.js +176 -0
  69. package/dist/core/ingestion/cfg/reaching-def-reason-codec.d.ts +106 -0
  70. package/dist/core/ingestion/cfg/reaching-def-reason-codec.js +133 -0
  71. package/dist/core/ingestion/cfg/reaching-defs-graph.d.ts +18 -0
  72. package/dist/core/ingestion/cfg/reaching-defs-graph.js +312 -0
  73. package/dist/core/ingestion/cfg/reaching-defs.d.ts +162 -0
  74. package/dist/core/ingestion/cfg/reaching-defs.js +785 -0
  75. package/dist/core/ingestion/cfg/synthetic-escape.d.ts +124 -0
  76. package/dist/core/ingestion/cfg/synthetic-escape.js +300 -0
  77. package/dist/core/ingestion/cfg/traversal-result.d.ts +20 -0
  78. package/dist/core/ingestion/cfg/traversal-result.js +2 -0
  79. package/dist/core/ingestion/cfg/types.d.ts +274 -0
  80. package/dist/core/ingestion/cfg/types.js +13 -0
  81. package/dist/core/ingestion/cfg/visitors/c-cpp-harvest.d.ts +111 -0
  82. package/dist/core/ingestion/cfg/visitors/c-cpp-harvest.js +541 -0
  83. package/dist/core/ingestion/cfg/visitors/c-cpp.d.ts +74 -0
  84. package/dist/core/ingestion/cfg/visitors/c-cpp.js +584 -0
  85. package/dist/core/ingestion/cfg/visitors/call-site-harvest.d.ts +195 -0
  86. package/dist/core/ingestion/cfg/visitors/call-site-harvest.js +350 -0
  87. package/dist/core/ingestion/cfg/visitors/csharp-harvest.d.ts +124 -0
  88. package/dist/core/ingestion/cfg/visitors/csharp-harvest.js +589 -0
  89. package/dist/core/ingestion/cfg/visitors/csharp.d.ts +87 -0
  90. package/dist/core/ingestion/cfg/visitors/csharp.js +867 -0
  91. package/dist/core/ingestion/cfg/visitors/dart-harvest.d.ts +278 -0
  92. package/dist/core/ingestion/cfg/visitors/dart-harvest.js +870 -0
  93. package/dist/core/ingestion/cfg/visitors/dart.d.ts +119 -0
  94. package/dist/core/ingestion/cfg/visitors/dart.js +836 -0
  95. package/dist/core/ingestion/cfg/visitors/go-harvest.d.ts +160 -0
  96. package/dist/core/ingestion/cfg/visitors/go-harvest.js +621 -0
  97. package/dist/core/ingestion/cfg/visitors/go.d.ts +108 -0
  98. package/dist/core/ingestion/cfg/visitors/go.js +638 -0
  99. package/dist/core/ingestion/cfg/visitors/java-harvest.d.ts +108 -0
  100. package/dist/core/ingestion/cfg/visitors/java-harvest.js +513 -0
  101. package/dist/core/ingestion/cfg/visitors/java.d.ts +101 -0
  102. package/dist/core/ingestion/cfg/visitors/java.js +812 -0
  103. package/dist/core/ingestion/cfg/visitors/kotlin-harvest.d.ts +253 -0
  104. package/dist/core/ingestion/cfg/visitors/kotlin-harvest.js +719 -0
  105. package/dist/core/ingestion/cfg/visitors/kotlin.d.ts +121 -0
  106. package/dist/core/ingestion/cfg/visitors/kotlin.js +809 -0
  107. package/dist/core/ingestion/cfg/visitors/php-harvest.d.ts +172 -0
  108. package/dist/core/ingestion/cfg/visitors/php-harvest.js +626 -0
  109. package/dist/core/ingestion/cfg/visitors/php.d.ts +96 -0
  110. package/dist/core/ingestion/cfg/visitors/php.js +721 -0
  111. package/dist/core/ingestion/cfg/visitors/python-harvest.d.ts +212 -0
  112. package/dist/core/ingestion/cfg/visitors/python-harvest.js +739 -0
  113. package/dist/core/ingestion/cfg/visitors/python.d.ts +103 -0
  114. package/dist/core/ingestion/cfg/visitors/python.js +558 -0
  115. package/dist/core/ingestion/cfg/visitors/ruby-harvest.d.ts +223 -0
  116. package/dist/core/ingestion/cfg/visitors/ruby-harvest.js +587 -0
  117. package/dist/core/ingestion/cfg/visitors/ruby.d.ts +112 -0
  118. package/dist/core/ingestion/cfg/visitors/ruby.js +756 -0
  119. package/dist/core/ingestion/cfg/visitors/rust-harvest.d.ts +324 -0
  120. package/dist/core/ingestion/cfg/visitors/rust-harvest.js +873 -0
  121. package/dist/core/ingestion/cfg/visitors/rust.d.ts +95 -0
  122. package/dist/core/ingestion/cfg/visitors/rust.js +558 -0
  123. package/dist/core/ingestion/cfg/visitors/scope-tree-harvest.d.ts +87 -0
  124. package/dist/core/ingestion/cfg/visitors/scope-tree-harvest.js +116 -0
  125. package/dist/core/ingestion/cfg/visitors/swift-harvest.d.ts +255 -0
  126. package/dist/core/ingestion/cfg/visitors/swift-harvest.js +679 -0
  127. package/dist/core/ingestion/cfg/visitors/swift.d.ts +116 -0
  128. package/dist/core/ingestion/cfg/visitors/swift.js +787 -0
  129. package/dist/core/ingestion/cfg/visitors/typescript-harvest.d.ts +149 -0
  130. package/dist/core/ingestion/cfg/visitors/typescript-harvest.js +1056 -0
  131. package/dist/core/ingestion/cfg/visitors/typescript.d.ts +66 -0
  132. package/dist/core/ingestion/cfg/visitors/typescript.js +583 -0
  133. package/dist/core/ingestion/import-resolvers/utils.js +2 -0
  134. package/dist/core/ingestion/language-provider.d.ts +23 -2
  135. package/dist/core/ingestion/languages/c-cpp.js +17 -5
  136. package/dist/core/ingestion/languages/cobol.js +4 -0
  137. package/dist/core/ingestion/languages/cpp/arity-metadata.js +41 -2
  138. package/dist/core/ingestion/languages/cpp/captures.js +50 -1
  139. package/dist/core/ingestion/languages/cpp/conversion-rank.d.ts +2 -0
  140. package/dist/core/ingestion/languages/cpp/conversion-rank.js +89 -0
  141. package/dist/core/ingestion/languages/cpp/header-scan.d.ts +1 -1
  142. package/dist/core/ingestion/languages/cpp/header-scan.js +2 -2
  143. package/dist/core/ingestion/languages/cpp/inline-namespaces.js +7 -2
  144. package/dist/core/ingestion/languages/cpp/member-lookup.js +2 -1
  145. package/dist/core/ingestion/languages/cpp/query.js +23 -0
  146. package/dist/core/ingestion/languages/cpp/scope-resolver.js +2 -1
  147. package/dist/core/ingestion/languages/csharp.js +2 -0
  148. package/dist/core/ingestion/languages/dart.js +2 -0
  149. package/dist/core/ingestion/languages/go.js +2 -0
  150. package/dist/core/ingestion/languages/java.js +3 -0
  151. package/dist/core/ingestion/languages/kotlin/query.js +3 -2
  152. package/dist/core/ingestion/languages/kotlin.js +8 -1
  153. package/dist/core/ingestion/languages/php.js +2 -0
  154. package/dist/core/ingestion/languages/python.js +2 -0
  155. package/dist/core/ingestion/languages/ruby.js +2 -0
  156. package/dist/core/ingestion/languages/rust.js +2 -0
  157. package/dist/core/ingestion/languages/swift.js +2 -0
  158. package/dist/core/ingestion/languages/typescript.js +5 -0
  159. package/dist/core/ingestion/languages/vue.js +6 -0
  160. package/dist/core/ingestion/method-extractors/configs/c-cpp.js +6 -13
  161. package/dist/core/ingestion/method-extractors/generic.js +1 -0
  162. package/dist/core/ingestion/method-types.d.ts +2 -0
  163. package/dist/core/ingestion/model/symbol-table.d.ts +1 -0
  164. package/dist/core/ingestion/model/symbol-table.js +1 -0
  165. package/dist/core/ingestion/mro-processor.js +109 -0
  166. package/dist/core/ingestion/parsing-processor.js +44 -0
  167. package/dist/core/ingestion/pipeline-phases/call-summaries.d.ts +26 -0
  168. package/dist/core/ingestion/pipeline-phases/call-summaries.js +46 -0
  169. package/dist/core/ingestion/pipeline-phases/index.d.ts +2 -0
  170. package/dist/core/ingestion/pipeline-phases/index.js +2 -0
  171. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +0 -21
  172. package/dist/core/ingestion/pipeline-phases/parse-impl.js +18 -1
  173. package/dist/core/ingestion/pipeline-phases/routes.d.ts +10 -0
  174. package/dist/core/ingestion/pipeline-phases/routes.js +30 -1
  175. package/dist/core/ingestion/pipeline-phases/taint-summaries.d.ts +30 -0
  176. package/dist/core/ingestion/pipeline-phases/taint-summaries.js +81 -0
  177. package/dist/core/ingestion/pipeline.d.ts +92 -10
  178. package/dist/core/ingestion/pipeline.js +11 -2
  179. package/dist/core/ingestion/scope-extractor.js +12 -2
  180. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +9 -0
  181. package/dist/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.d.ts +75 -0
  182. package/dist/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.js +67 -0
  183. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +12 -2
  184. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +14 -2
  185. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +6 -1
  186. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +13 -1
  187. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +9 -1
  188. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +28 -2
  189. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +4 -0
  190. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +9 -0
  191. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +6 -1
  192. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +174 -40
  193. package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +24 -0
  194. package/dist/core/ingestion/scope-resolution/pipeline/phase.js +276 -190
  195. package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +43 -3
  196. package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +75 -0
  197. package/dist/core/ingestion/scope-resolution/pipeline/run.js +349 -1
  198. package/dist/core/ingestion/scope-resolution/resolution-outcome.d.ts +1 -1
  199. package/dist/core/ingestion/taint/call-summary-codec.d.ts +75 -0
  200. package/dist/core/ingestion/taint/call-summary-codec.js +128 -0
  201. package/dist/core/ingestion/taint/call-summary-emit.d.ts +43 -0
  202. package/dist/core/ingestion/taint/call-summary-emit.js +70 -0
  203. package/dist/core/ingestion/taint/call-summary-harvest.d.ts +77 -0
  204. package/dist/core/ingestion/taint/call-summary-harvest.js +179 -0
  205. package/dist/core/ingestion/taint/call-summary-model.d.ts +47 -0
  206. package/dist/core/ingestion/taint/call-summary-model.js +25 -0
  207. package/dist/core/ingestion/taint/emit.d.ts +125 -0
  208. package/dist/core/ingestion/taint/emit.js +208 -0
  209. package/dist/core/ingestion/taint/interproc-emit.d.ts +50 -0
  210. package/dist/core/ingestion/taint/interproc-emit.js +88 -0
  211. package/dist/core/ingestion/taint/interproc-solver.d.ts +110 -0
  212. package/dist/core/ingestion/taint/interproc-solver.js +312 -0
  213. package/dist/core/ingestion/taint/match.d.ts +153 -0
  214. package/dist/core/ingestion/taint/match.js +278 -0
  215. package/dist/core/ingestion/taint/path-codec.d.ts +134 -0
  216. package/dist/core/ingestion/taint/path-codec.js +190 -0
  217. package/dist/core/ingestion/taint/propagate.d.ts +216 -0
  218. package/dist/core/ingestion/taint/propagate.js +659 -0
  219. package/dist/core/ingestion/taint/site-safety.d.ts +29 -0
  220. package/dist/core/ingestion/taint/site-safety.js +98 -0
  221. package/dist/core/ingestion/taint/source-sink-config.d.ts +107 -23
  222. package/dist/core/ingestion/taint/source-sink-config.js +35 -12
  223. package/dist/core/ingestion/taint/source-sink-registry.d.ts +6 -4
  224. package/dist/core/ingestion/taint/source-sink-registry.js +6 -4
  225. package/dist/core/ingestion/taint/summary-harvest-driver.d.ts +80 -0
  226. package/dist/core/ingestion/taint/summary-harvest-driver.js +179 -0
  227. package/dist/core/ingestion/taint/summary-harvest.d.ts +89 -0
  228. package/dist/core/ingestion/taint/summary-harvest.js +537 -0
  229. package/dist/core/ingestion/taint/summary-model.d.ts +200 -0
  230. package/dist/core/ingestion/taint/summary-model.js +86 -0
  231. package/dist/core/ingestion/taint/typescript-model.d.ts +38 -0
  232. package/dist/core/ingestion/taint/typescript-model.js +102 -0
  233. package/dist/core/ingestion/type-extractors/c-cpp.js +21 -0
  234. package/dist/core/ingestion/utils/env.d.ts +7 -0
  235. package/dist/core/ingestion/utils/env.js +12 -0
  236. package/dist/core/ingestion/utils/method-props.js +1 -0
  237. package/dist/core/ingestion/workers/clone-safety.d.ts +109 -0
  238. package/dist/core/ingestion/workers/clone-safety.js +465 -0
  239. package/dist/core/ingestion/workers/parse-worker.d.ts +23 -0
  240. package/dist/core/ingestion/workers/parse-worker.js +81 -53
  241. package/dist/core/ingestion/workers/post-result.d.ts +22 -0
  242. package/dist/core/ingestion/workers/post-result.js +87 -0
  243. package/dist/core/ingestion/workers/result-merge.d.ts +20 -0
  244. package/dist/core/ingestion/workers/result-merge.js +43 -0
  245. package/dist/core/ingestion/workers/worker-pool.d.ts +15 -40
  246. package/dist/core/ingestion/workers/worker-pool.js +43 -16
  247. package/dist/core/lbug/csv-generator.d.ts +51 -3
  248. package/dist/core/lbug/csv-generator.js +369 -255
  249. package/dist/core/lbug/lbug-adapter.d.ts +58 -18
  250. package/dist/core/lbug/lbug-adapter.js +346 -84
  251. package/dist/core/lbug/lbug-config.d.ts +19 -0
  252. package/dist/core/lbug/lbug-config.js +29 -0
  253. package/dist/core/lbug/pdg-emit-sink.d.ts +127 -0
  254. package/dist/core/lbug/pdg-emit-sink.js +358 -0
  255. package/dist/core/lbug/pool-adapter.d.ts +35 -0
  256. package/dist/core/lbug/pool-adapter.js +90 -1
  257. package/dist/core/lbug/rel-pair-routing.d.ts +54 -0
  258. package/dist/core/lbug/rel-pair-routing.js +140 -0
  259. package/dist/core/lbug/schema.d.ts +2 -2
  260. package/dist/core/lbug/schema.js +3 -0
  261. package/dist/core/logger.d.ts +0 -5
  262. package/dist/core/run-analyze.d.ts +96 -0
  263. package/dist/core/run-analyze.js +207 -3
  264. package/dist/core/tree-sitter/parser-loader.js +5 -4
  265. package/dist/core/tree-sitter/safe-parse.d.ts +0 -5
  266. package/dist/core/tree-sitter/vendored-grammars.d.ts +39 -0
  267. package/dist/core/tree-sitter/vendored-grammars.js +57 -0
  268. package/dist/core/wiki/generator.js +3 -1
  269. package/dist/core/wiki/graph-queries.d.ts +6 -0
  270. package/dist/core/wiki/graph-queries.js +9 -1
  271. package/dist/mcp/http-transport.d.ts +134 -0
  272. package/dist/mcp/http-transport.js +503 -0
  273. package/dist/mcp/local/local-backend.d.ts +110 -24
  274. package/dist/mcp/local/local-backend.js +1225 -19
  275. package/dist/mcp/local/pdg-impact.d.ts +384 -0
  276. package/dist/mcp/local/pdg-impact.js +1972 -0
  277. package/dist/mcp/resources.js +13 -1
  278. package/dist/mcp/tools.d.ts +12 -0
  279. package/dist/mcp/tools.js +214 -7
  280. package/dist/server/analyze-launch.d.ts +29 -0
  281. package/dist/server/analyze-launch.js +137 -0
  282. package/dist/server/analyze-upload.d.ts +33 -0
  283. package/dist/server/analyze-upload.js +123 -0
  284. package/dist/server/analyze-worker-ipc.d.ts +58 -0
  285. package/dist/server/analyze-worker-ipc.js +16 -0
  286. package/dist/server/analyze-worker.d.ts +17 -1
  287. package/dist/server/analyze-worker.js +7 -1
  288. package/dist/server/api.d.ts +17 -0
  289. package/dist/server/api.js +132 -168
  290. package/dist/server/git-clone.d.ts +43 -2
  291. package/dist/server/git-clone.js +211 -16
  292. package/dist/server/mcp-http.d.ts +5 -6
  293. package/dist/server/mcp-http.js +9 -82
  294. package/dist/server/middleware.d.ts +40 -0
  295. package/dist/server/middleware.js +92 -0
  296. package/dist/server/private-ip.d.ts +1 -0
  297. package/dist/server/private-ip.js +15 -0
  298. package/dist/server/upload-ingest.d.ts +56 -0
  299. package/dist/server/upload-ingest.js +276 -0
  300. package/dist/server/upload-paths.d.ts +37 -0
  301. package/dist/server/upload-paths.js +57 -0
  302. package/dist/server/upload-sweep.d.ts +20 -0
  303. package/dist/server/upload-sweep.js +57 -0
  304. package/dist/storage/git.js +5 -8
  305. package/dist/storage/parse-cache.d.ts +22 -1
  306. package/dist/storage/parse-cache.js +44 -10
  307. package/dist/storage/repo-manager.d.ts +121 -10
  308. package/dist/storage/repo-manager.js +43 -5
  309. package/dist/types/pipeline.d.ts +9 -0
  310. package/hooks/antigravity/gitnexus-antigravity-hook.cjs +110 -10
  311. package/hooks/claude/gitnexus-hook.cjs +110 -13
  312. package/hooks/claude/hook-db-lock-probe.cjs +513 -26
  313. package/package.json +8 -2
  314. package/scripts/assert-publish-grammar-coverage.cjs +31 -0
  315. package/scripts/build-tree-sitter-grammars.cjs +16 -10
  316. package/scripts/cross-platform-tests.ts +1 -0
  317. package/skills/gitnexus-debugging.md +16 -4
  318. package/skills/gitnexus-exploring.md +3 -3
  319. package/skills/gitnexus-guide.md +33 -0
  320. package/skills/gitnexus-pdg-query.md +89 -0
  321. package/skills/gitnexus-refactoring.md +1 -1
  322. package/skills/gitnexus-taint-analysis.md +178 -0
  323. package/vendor/tree-sitter-c/package.json +1 -1
  324. package/vendor/tree-sitter-dart/package.json +1 -1
  325. package/vendor/tree-sitter-kotlin/package.json +1 -1
  326. package/vendor/tree-sitter-proto/package.json +1 -1
  327. package/vendor/tree-sitter-swift/package.json +1 -1
  328. package/web/assets/{agent-CKPMqImC.js → agent-D3Xd7zZE.js} +123 -123
  329. package/web/assets/architecture-7EHR7CIX-6QZW5X65-DxJw65fT.js +1 -0
  330. package/web/assets/architectureDiagram-UL44E2DR-ChH9xW-N.js +36 -0
  331. package/web/assets/{blockDiagram-7IZFK4PR-B4niSLvR.js → blockDiagram-7IZFK4PR-BkWhpzWB.js} +1 -1
  332. package/web/assets/{c4Diagram-Y2BXMSZH-DIzEXalR.js → c4Diagram-Y2BXMSZH-DtBvpzWD.js} +1 -1
  333. package/web/assets/{chunk-3SSMPTDK-BzGeeheI.js → chunk-3SSMPTDK-B4_etUhr.js} +2 -2
  334. package/web/assets/{chunk-5IMINLNL-CnTLsXIV.js → chunk-5IMINLNL-DOdTeQri.js} +1 -1
  335. package/web/assets/{chunk-5VCL7Z4A-w1BCx98Z.js → chunk-5VCL7Z4A-CQ-2dYWj.js} +1 -1
  336. package/web/assets/chunk-6764PJDD-DpIS7hxD.js +1 -0
  337. package/web/assets/{chunk-67TQ5CYL-CChC2cAv.js → chunk-67TQ5CYL-4sNPJU1q.js} +3 -3
  338. package/web/assets/{chunk-7W6UQGC5-CgFyfZ6k.js → chunk-7W6UQGC5-KyEG0HQg.js} +1 -1
  339. package/web/assets/{chunk-AZZRMDJM-5E9ARdDQ.js → chunk-AZZRMDJM-y-xJBuMz.js} +1 -1
  340. package/web/assets/{chunk-INKRHTLW-ELpfuk9h.js → chunk-INKRHTLW-Nu4ri9P-.js} +1 -1
  341. package/web/assets/{chunk-JQRUD6KW-Dcq_Qnyy.js → chunk-JQRUD6KW-Dbva2Z17.js} +1 -1
  342. package/web/assets/{chunk-KGFNY3KK-W-VR57xb.js → chunk-KGFNY3KK-JGMJvKCK.js} +1 -1
  343. package/web/assets/chunk-KGYTTC2M-CWC_c3H9.js +161 -0
  344. package/web/assets/{chunk-KNLZD3CH-BuZBOcso.js → chunk-KNLZD3CH-BGlP6X9R.js} +1 -1
  345. package/web/assets/{chunk-KRXBNO2N-DH2NPM7I.js → chunk-KRXBNO2N-BW5o0KUN.js} +1 -1
  346. package/web/assets/{chunk-LCXTWHL2-Dfmux4m1.js → chunk-LCXTWHL2-zoomRtWX.js} +1 -1
  347. package/web/assets/{chunk-LII3EMHJ-3X33tCpU.js → chunk-LII3EMHJ-D-jm-dLa.js} +1 -1
  348. package/web/assets/{chunk-QA3QBVWF-D5Ha3GgB.js → chunk-QA3QBVWF-BgRW3SXd.js} +1 -1
  349. package/web/assets/{chunk-RG4AUYOV-CAkzcoRj.js → chunk-RG4AUYOV-DD0B2N2B.js} +1 -1
  350. package/web/assets/{chunk-T2UQINTJ-kGYG7ppP.js → chunk-T2UQINTJ-DSBqkzH7.js} +1 -1
  351. package/web/assets/chunk-T5OCTHI4-DdZvN-9i.js +1 -0
  352. package/web/assets/{chunk-UY5QBCOK-Du-c56gs.js → chunk-UY5QBCOK-DyF0vyGd.js} +1 -1
  353. package/web/assets/{chunk-W44A43WB-lrCpuupA.js → chunk-W44A43WB-JqMLIpR0.js} +1 -1
  354. package/web/assets/{chunk-ZXARS5L4-CSZRiyOf.js → chunk-ZXARS5L4-CxN8oiwI.js} +1 -1
  355. package/web/assets/classDiagram-KGZ6W3CR-DJm9BgF-.js +1 -0
  356. package/web/assets/classDiagram-v2-72OJOZXJ-DJm9BgF-.js +1 -0
  357. package/web/assets/{context-builder-_HS0v2ma.js → context-builder-BHiFUA8O.js} +10 -2
  358. package/web/assets/{cose-bilkent-UX7MHV2Q-BHv2bbtl.js → cose-bilkent-UX7MHV2Q-D71wNYRJ.js} +1 -1
  359. package/web/assets/dagre-ND4H6XIP-BIkxmhcu.js +4 -0
  360. package/web/assets/diagram-3NCE3AQN-BTa6iH0T.js +43 -0
  361. package/web/assets/diagram-GF46GFSD-DZDV0vBj.js +24 -0
  362. package/web/assets/{diagram-HNR7UZ2L-fUWreZzx.js → diagram-HNR7UZ2L-DFCfd5LI.js} +2 -2
  363. package/web/assets/diagram-QXG6HAR7-BzM--Jj0.js +24 -0
  364. package/web/assets/diagram-WEQXMOUZ-DFFwz0k4.js +10 -0
  365. package/web/assets/{erDiagram-L5TCEMPS-QN2eEP1e.js → erDiagram-L5TCEMPS-CfHMJJqo.js} +1 -1
  366. package/web/assets/eventmodeling-FCH6USID-MREXMVOE-BQm9QzEa.js +1 -0
  367. package/web/assets/{flowDiagram-H6V6AXG4-CXbXImlN.js → flowDiagram-H6V6AXG4-Be805QiJ.js} +3 -3
  368. package/web/assets/{ganttDiagram-JCBTUEKG-BHQssTz6.js → ganttDiagram-JCBTUEKG-D3iL2Aet.js} +1 -1
  369. package/web/assets/gitGraph-WXDBUCRP-R675I2BI-Cr_Bm2Nb.js +1 -0
  370. package/web/assets/gitGraphDiagram-S2ZK5IYY-CPIDgxGm.js +106 -0
  371. package/web/assets/index-BvYev1UJ.js +635 -0
  372. package/web/assets/index-_lgn7hs5.css +2 -0
  373. package/web/assets/info-J43DQDTF-KCYPFFUO-Cs4p2oyk.js +1 -0
  374. package/web/assets/infoDiagram-3YFTVSEB-Y7BKSoke.js +2 -0
  375. package/web/assets/{ishikawaDiagram-BNXS4ZKH-6LKOvBfp.js → ishikawaDiagram-BNXS4ZKH-CT4SHFuI.js} +1 -1
  376. package/web/assets/{journeyDiagram-M6C3CM3L-DNPYCpvq.js → journeyDiagram-M6C3CM3L-CvArszLo.js} +1 -1
  377. package/web/assets/{kanban-definition-75IXJCU3-DgDi9oJT.js → kanban-definition-75IXJCU3-C6yDxr7N.js} +3 -3
  378. package/web/assets/{katex-K3KEBU37-CrVLz8l7.js → katex-K3KEBU37-CTc5BslQ.js} +1 -1
  379. package/web/assets/{mindmap-definition-2TDM6QVE-CwR5sBB-.js → mindmap-definition-2TDM6QVE-BOc2WEne.js} +1 -1
  380. package/web/assets/packet-YPE3B663-LP52Z2RK-CKCrztD2.js +1 -0
  381. package/web/assets/pie-LRSECV5Y-TCRJHUBD-BB7pHqoD.js +1 -0
  382. package/web/assets/{pieDiagram-CU6KROY3-By8g6f6B.js → pieDiagram-CU6KROY3-Cvc0f_Pd.js} +1 -1
  383. package/web/assets/{quadrantDiagram-VICAPDV7-bAm6Hi_5.js → quadrantDiagram-VICAPDV7-yXSZ2lLz.js} +1 -1
  384. package/web/assets/radar-GUYGQ44K-RDLRG3WG-DhoTOcuK.js +1 -0
  385. package/web/assets/{requirementDiagram-JXO7QTGE-7oDcJ1_J.js → requirementDiagram-JXO7QTGE-BRcZvHs4.js} +1 -1
  386. package/web/assets/{sankeyDiagram-URQDO5SZ-LFJz53u5.js → sankeyDiagram-URQDO5SZ-1qEjlFIB.js} +1 -1
  387. package/web/assets/{sequenceDiagram-VS2MUI6T-FySeKCUy.js → sequenceDiagram-VS2MUI6T-PcSCuvrB.js} +3 -3
  388. package/web/assets/stateDiagram-7D4R322I-D6qYSV1E.js +1 -0
  389. package/web/assets/stateDiagram-v2-36443NZ5-Bg-ImOSX.js +1 -0
  390. package/web/assets/{timeline-definition-O6YCAMPW-Ds2CnVZK.js → timeline-definition-O6YCAMPW-QWkh01xw.js} +1 -1
  391. package/web/assets/treeView-BLDUP644-QA4HXRO3-ODehyGKL.js +1 -0
  392. package/web/assets/treemap-LRROVOQU-LLAWBHMP-BfpgC-7o.js +1 -0
  393. package/web/assets/{vennDiagram-MWXL3ELB-DUIEwXWp.js → vennDiagram-MWXL3ELB-ak4DfwuM.js} +3 -3
  394. package/web/assets/wardley-L42UT6IY-5TKZOOLJ-CNeL6VHE.js +1 -0
  395. package/web/assets/{wardleyDiagram-CUQ6CDDI-DEiFPQih.js → wardleyDiagram-CUQ6CDDI-CTzR4Loq.js} +1 -1
  396. package/web/assets/{xychartDiagram-N2JHSOCM-BUbayhST.js → xychartDiagram-N2JHSOCM-DA1BEaQz.js} +1 -1
  397. package/web/index.html +13 -15
  398. package/scripts/materialize-vendor-grammars.cjs +0 -97
  399. package/web/assets/architecture-7EHR7CIX-6QZW5X65-BETj2x4g.js +0 -1
  400. package/web/assets/architectureDiagram-UL44E2DR-D1EXI0zA.js +0 -36
  401. package/web/assets/chunk-2T2R6R2M-n6s9JZqv.js +0 -4
  402. package/web/assets/chunk-2UTLFMKG-CMBB1TMN.js +0 -1
  403. package/web/assets/chunk-4R4BOZG6-mKhL59ul.js +0 -159
  404. package/web/assets/chunk-6764PJDD-ChwMM2z4.js +0 -1
  405. package/web/assets/chunk-7J6CGLKN-DT-b53FT.js +0 -10
  406. package/web/assets/chunk-C62D2QBJ-CDAWj26E.js +0 -1
  407. package/web/assets/chunk-CEXFNPSA-D68Tk6ls.js +0 -1
  408. package/web/assets/chunk-CilyBKbf.js +0 -1
  409. package/web/assets/chunk-J5EP6P6S-DxWW0yvu.js +0 -1
  410. package/web/assets/chunk-KGYTTC2M-C6PHeuay.js +0 -1
  411. package/web/assets/chunk-RERM46MO-DQNbXtfw.js +0 -1
  412. package/web/assets/chunk-RKZBBQEN-BmTPLSyv.js +0 -1
  413. package/web/assets/chunk-RLI5ZMPA-DExu2DOK.js +0 -1
  414. package/web/assets/chunk-T5OCTHI4--9wWpVws.js +0 -1
  415. package/web/assets/chunk-UP6H54XL-DsKdC6jC.js +0 -1
  416. package/web/assets/chunk-UXSXWOXI-DR81EqLr.js +0 -1
  417. package/web/assets/classDiagram-KGZ6W3CR-Bv93af_b.js +0 -1
  418. package/web/assets/classDiagram-v2-72OJOZXJ-CLfEqHUa.js +0 -1
  419. package/web/assets/dagre-ND4H6XIP-DsIr9MOv.js +0 -4
  420. package/web/assets/diagram-3NCE3AQN-BqAtKUpW.js +0 -43
  421. package/web/assets/diagram-GF46GFSD-CaBG6n6o.js +0 -24
  422. package/web/assets/diagram-QXG6HAR7-CZ-O3rcV.js +0 -24
  423. package/web/assets/diagram-WEQXMOUZ-P4lSL4GH.js +0 -10
  424. package/web/assets/eventmodeling-FCH6USID-MREXMVOE-B3myHUst.js +0 -1
  425. package/web/assets/gitGraph-WXDBUCRP-R675I2BI-DF3rtf4A.js +0 -1
  426. package/web/assets/gitGraphDiagram-S2ZK5IYY-CCwVLT__.js +0 -106
  427. package/web/assets/index-BKWA-m7o.css +0 -2
  428. package/web/assets/index-CG6q8eTs.js +0 -626
  429. package/web/assets/info-J43DQDTF-KCYPFFUO-R4XV_akJ.js +0 -1
  430. package/web/assets/infoDiagram-3YFTVSEB-DmLICZx1.js +0 -2
  431. package/web/assets/packet-YPE3B663-LP52Z2RK-CgQPdNZa.js +0 -1
  432. package/web/assets/pie-LRSECV5Y-TCRJHUBD-BzWOwc_c.js +0 -1
  433. package/web/assets/radar-GUYGQ44K-RDLRG3WG-B7KG0FN7.js +0 -1
  434. package/web/assets/stateDiagram-7D4R322I-CB2nABwH.js +0 -1
  435. package/web/assets/stateDiagram-v2-36443NZ5-COBGd2RL.js +0 -1
  436. package/web/assets/treeView-BLDUP644-QA4HXRO3-CRSxEwLO.js +0 -1
  437. package/web/assets/treemap-LRROVOQU-LLAWBHMP-a0PfKoVn.js +0 -1
  438. package/web/assets/wardley-L42UT6IY-5TKZOOLJ-BKQ6e5mC.js +0 -1
  439. /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
+ }