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,836 @@
1
+ import { CfgBuilder } from '../cfg-builder.js';
2
+ import { ControlFlowContext, drainFinalizerPending, wireJumpThroughFinalizers, } from '../control-flow-context.js';
3
+ import { DartHarvester } from './dart-harvest.js';
4
+ /** Signature node types whose SIBLING `function_body` owns a CFG-bearing body. */
5
+ const DART_SIGNATURE_TYPES = new Set([
6
+ 'function_signature',
7
+ 'method_signature',
8
+ 'getter_signature',
9
+ 'setter_signature',
10
+ 'constructor_signature',
11
+ 'factory_constructor_signature',
12
+ ]);
13
+ /** Statement node types that break a basic block (everything else coalesces). */
14
+ const CONTROL_FLOW_TYPES = new Set([
15
+ 'if_statement',
16
+ 'for_statement',
17
+ 'while_statement',
18
+ 'do_statement',
19
+ 'switch_statement',
20
+ 'try_statement',
21
+ 'return_statement',
22
+ 'break_statement',
23
+ 'continue_statement',
24
+ 'assert_statement',
25
+ ]);
26
+ /** Comment node types tree-sitter-dart surfaces. */
27
+ const COMMENT_TYPES = new Set(['comment', 'documentation_comment']);
28
+ const startLineOf = (n) => n.startPosition.row + 1;
29
+ const endLineOf = (n) => n.endPosition.row + 1;
30
+ const isComment = (n) => COMMENT_TYPES.has(n.type);
31
+ /** Whether an `expression_statement` is a bare `throw …;`. */
32
+ const isThrowStatement = (n) => n.type === 'expression_statement' && n.namedChildren.some((c) => c.type === 'throw_expression');
33
+ /** Whether an `expression_statement` is a bare `rethrow;`. */
34
+ const isRethrowStatement = (n) => n.type === 'expression_statement' && n.namedChildren.some((c) => c.type === 'rethrow_expression');
35
+ /**
36
+ * Whether a node is a LABELED `continue LABEL;` (a switch fallthrough-spill),
37
+ * vs a bare `continue;` (which targets the enclosing loop). Only the labeled form
38
+ * is stripped from a case body and handled via {@link caseContinueLabel}.
39
+ */
40
+ const isLabeledContinue = (n) => n.type === 'continue_statement' && n.namedChildren.some((c) => c.type === 'identifier');
41
+ /**
42
+ * Per-function Dart walk state. One instance per function so the
43
+ * {@link ControlFlowContext}, exception-handler stack, and labeled-frame
44
+ * bookkeeping are scoped to that function and never leak across functions.
45
+ */
46
+ class DartCfgWalk {
47
+ builder;
48
+ harvest;
49
+ cfc = new ControlFlowContext();
50
+ /** Stack of exception-handler entry blocks (catch/finally) a `throw` jumps to. */
51
+ handlers = [];
52
+ /** Label(s) pending attachment to the NEXT pushed loop/switch frame. */
53
+ pendingLabels = [];
54
+ constructor(builder, harvest) {
55
+ this.builder = builder;
56
+ this.harvest = harvest;
57
+ }
58
+ /** Named statements of a `block`, ignoring comments. */
59
+ statementsOf(block) {
60
+ return block.namedChildren.filter((c) => !isComment(c));
61
+ }
62
+ /** Unwrap a body STMT: a `block` yields its statements; a bare statement is itself. */
63
+ visitBody(node) {
64
+ return this.builder.withNesting(() => {
65
+ if (!node)
66
+ return null;
67
+ if (node.type === 'block')
68
+ return this.visitSeq(this.statementsOf(node));
69
+ if (isComment(node))
70
+ return null;
71
+ return this.visitStmt(node);
72
+ });
73
+ }
74
+ /** Wire a sequence of statements, coalescing straight-line runs into blocks. */
75
+ visitSeq(stmts) {
76
+ return this.builder.withNesting(() => {
77
+ let entry;
78
+ let dangling = [];
79
+ let openSimple;
80
+ for (const stmt of stmts) {
81
+ if (this.isControlFlow(stmt)) {
82
+ openSimple = undefined; // close any open straight-line block
83
+ const res = this.visitStmt(stmt);
84
+ if (res === null)
85
+ continue; // transparent (empty nested block / label-only)
86
+ if (entry === undefined)
87
+ entry = res.entry;
88
+ else
89
+ this.builder.connect(dangling, res.entry, 'seq');
90
+ dangling = [...res.exits];
91
+ }
92
+ else {
93
+ if (openSimple === undefined) {
94
+ const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text, 'normal', this.harvest.facts(stmt));
95
+ if (entry === undefined)
96
+ entry = idx;
97
+ else
98
+ this.builder.connect(dangling, idx, 'seq');
99
+ openSimple = idx;
100
+ dangling = [idx];
101
+ }
102
+ else {
103
+ this.builder.extendBlock(openSimple, endLineOf(stmt), stmt.text, this.harvest.facts(stmt));
104
+ }
105
+ }
106
+ }
107
+ if (entry === undefined)
108
+ return null;
109
+ return { entry, exits: dangling };
110
+ });
111
+ }
112
+ /** Whether a statement node breaks the current straight-line block. */
113
+ isControlFlow(stmt) {
114
+ if (this.isLabelError(stmt))
115
+ return true; // a stray label sibling — queue it
116
+ if (isThrowStatement(stmt) || isRethrowStatement(stmt))
117
+ return true;
118
+ // `var x = switch (v) { … }` (#2207): a value-position switch breaks so
119
+ // `visitStmt` models the arms as control flow instead of coalescing.
120
+ if (stmt.type === 'local_variable_declaration') {
121
+ const v = this.directValue(stmt);
122
+ return v !== undefined && this.isModelableValueBranch(v);
123
+ }
124
+ return CONTROL_FLOW_TYPES.has(stmt.type);
125
+ }
126
+ /**
127
+ * A labeled LOOP outside a switch (`outer: for …`) is mis-parsed by
128
+ * tree-sitter-dart as a stray `ERROR [identifier :]` SIBLING preceding the
129
+ * loop. Recognize that exact shape so the label still resolves a `break outer`.
130
+ */
131
+ isLabelError(stmt) {
132
+ if (stmt.type !== 'ERROR')
133
+ return false;
134
+ const id = stmt.namedChildren.find((c) => c.type === 'identifier');
135
+ return id !== undefined && stmt.children.some((c) => c.type === ':');
136
+ }
137
+ /** Dispatch one statement to its handler. Non-null except for empty / label-only. */
138
+ visitStmt(stmt) {
139
+ if (this.isLabelError(stmt)) {
140
+ const id = stmt.namedChildren.find((c) => c.type === 'identifier');
141
+ if (id?.text)
142
+ this.pendingLabels = [...this.pendingLabels, id.text];
143
+ return null; // emits no block of its own
144
+ }
145
+ if (isThrowStatement(stmt))
146
+ return this.visitThrow(stmt);
147
+ if (isRethrowStatement(stmt))
148
+ return this.visitRethrow(stmt);
149
+ switch (stmt.type) {
150
+ case 'local_variable_declaration': {
151
+ // `var x = switch (v) { … }` (#2207): the value is a value-position
152
+ // branch — model it as control flow and bind the result on the rejoin.
153
+ const value = this.directValue(stmt);
154
+ if (value && this.isModelableValueBranch(value))
155
+ return this.visitBindBranch(stmt, value);
156
+ return this.visitSimple(stmt);
157
+ }
158
+ case 'if_statement':
159
+ return this.visitIf(stmt);
160
+ case 'for_statement':
161
+ return this.visitFor(stmt);
162
+ case 'while_statement':
163
+ return this.visitWhile(stmt);
164
+ case 'do_statement':
165
+ return this.visitDoWhile(stmt);
166
+ case 'switch_statement':
167
+ return this.visitSwitch(stmt);
168
+ case 'try_statement':
169
+ return this.visitTry(stmt);
170
+ case 'return_statement':
171
+ return this.visitReturn(stmt);
172
+ case 'break_statement':
173
+ return this.visitBreak(stmt);
174
+ case 'continue_statement':
175
+ return this.visitContinue(stmt);
176
+ case 'assert_statement':
177
+ return this.visitAssert(stmt);
178
+ case 'block':
179
+ return this.visitSeq(this.statementsOf(stmt));
180
+ default:
181
+ return this.visitSimple(stmt);
182
+ }
183
+ }
184
+ visitSimple(stmt) {
185
+ const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text, 'normal', this.harvest.facts(stmt));
186
+ return { entry: idx, exits: [idx] };
187
+ }
188
+ /** Take and clear the labels queued by a preceding label sibling. */
189
+ takeLabels() {
190
+ const labels = this.pendingLabels;
191
+ this.pendingLabels = [];
192
+ return labels;
193
+ }
194
+ // ── jumps (return / throw / rethrow / break / continue / assert) ──────────
195
+ /** `return [expr];` — threads through every active finalizer before EXIT. */
196
+ visitReturn(stmt) {
197
+ // `return switch (v) { … };` (#2207): the returned value is a value-position
198
+ // branch — model it as control flow, with each arm returning (its value IS
199
+ // the function result), threading every active finalizer per arm.
200
+ const branch = stmt.namedChildren.find((c) => !isComment(c));
201
+ if (branch && this.isModelableValueBranch(branch)) {
202
+ const res = this.visitBranchExpr(branch);
203
+ const finalizers = this.cfc.finalizersForReturn();
204
+ for (const ex of res.exits) {
205
+ wireJumpThroughFinalizers(this.builder, ex, finalizers, this.builder.exitIndex, 'return');
206
+ }
207
+ return { entry: res.entry, exits: [] };
208
+ }
209
+ const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text, 'normal', this.harvest.facts(stmt));
210
+ wireJumpThroughFinalizers(this.builder, idx, this.cfc.finalizersForReturn(), this.builder.exitIndex, 'return');
211
+ return { entry: idx, exits: [] };
212
+ }
213
+ /** `throw e;` — routes to the nearest enclosing handler (catch/finally), else EXIT. */
214
+ visitThrow(stmt) {
215
+ const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text, 'normal', this.harvest.facts(stmt));
216
+ this.builder.edge(idx, this.currentHandler(), 'throw');
217
+ return { entry: idx, exits: [] };
218
+ }
219
+ /**
220
+ * `rethrow;` — re-raises the current exception. Inside a handler it propagates
221
+ * past the CURRENT catch to the next-outer handler / EXIT, which is exactly
222
+ * what `currentHandler()` resolves to (the in-flight catch is not on the
223
+ * handler stack while its own body is walked). Terminates its block.
224
+ */
225
+ visitRethrow(stmt) {
226
+ const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text, 'normal', this.harvest.facts(stmt));
227
+ this.builder.edge(idx, this.currentHandler(), 'throw');
228
+ return { entry: idx, exits: [] };
229
+ }
230
+ visitBreak(stmt) {
231
+ const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text);
232
+ const label = this.jumpLabel(stmt);
233
+ const res = this.cfc.resolveBreak(label);
234
+ const { target, finalizers } = res ?? {
235
+ target: this.builder.exitIndex,
236
+ finalizers: this.cfc.finalizersForReturn(),
237
+ };
238
+ wireJumpThroughFinalizers(this.builder, idx, finalizers, target, 'break');
239
+ return { entry: idx, exits: [] };
240
+ }
241
+ visitContinue(stmt) {
242
+ const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text);
243
+ const label = this.jumpLabel(stmt);
244
+ const res = this.cfc.resolveContinue(label);
245
+ const { target, finalizers } = res ?? {
246
+ target: this.builder.exitIndex,
247
+ finalizers: this.cfc.finalizersForReturn(),
248
+ };
249
+ wireJumpThroughFinalizers(this.builder, idx, finalizers, target, 'continue');
250
+ return { entry: idx, exits: [] };
251
+ }
252
+ /**
253
+ * `assert ( cond [, msg] );` — may throw an `AssertionError`. Modeled as a
254
+ * straight-line block that ALSO edges to the current handler (the assertion
255
+ * may fire), so the error path stays represented while the success path falls
256
+ * through. Conservative; deduped by the builder.
257
+ */
258
+ visitAssert(stmt) {
259
+ const idx = this.builder.newBlock(startLineOf(stmt), endLineOf(stmt), stmt.text, 'normal', this.harvest.facts(stmt));
260
+ this.builder.edge(idx, this.currentHandler(), 'throw');
261
+ return { entry: idx, exits: [idx] };
262
+ }
263
+ /** The `identifier` label of a `break outer;` / `continue outer;`, if any. */
264
+ jumpLabel(stmt) {
265
+ const id = stmt.namedChildren.find((c) => c.type === 'identifier');
266
+ return id?.text || undefined;
267
+ }
268
+ // ── branches (if/else) ─────────────────────────────────────────────────────
269
+ /**
270
+ * `if ( COND ) consequence:STMT [ else alternative:STMT ]`. The consequence /
271
+ * alternative are a `block` or a bare statement; an `else if` is the nested
272
+ * `if_statement` in the `alternative` field.
273
+ */
274
+ visitIf(stmt) {
275
+ const cond = this.parenCondition(stmt);
276
+ const header = this.builder.newBlock(startLineOf(stmt), cond ? endLineOf(cond) : startLineOf(stmt), cond ? `if (${cond.text})` : 'if', 'normal', cond ? this.harvest.facts(cond) : undefined);
277
+ const consequence = stmt.childForFieldName('consequence');
278
+ const alternative = stmt.childForFieldName('alternative');
279
+ const exits = [];
280
+ const thenRes = this.visitBody(consequence);
281
+ if (thenRes) {
282
+ this.builder.edge(header, thenRes.entry, 'cond-true');
283
+ exits.push(...thenRes.exits);
284
+ }
285
+ else {
286
+ exits.push(header); // empty then — true path falls through
287
+ }
288
+ if (alternative) {
289
+ const elseRes = this.visitBody(alternative);
290
+ if (elseRes) {
291
+ this.builder.edge(header, elseRes.entry, 'cond-false');
292
+ exits.push(...elseRes.exits);
293
+ }
294
+ else {
295
+ exits.push(header);
296
+ }
297
+ }
298
+ else {
299
+ exits.push(header); // no else — false path falls through to the join
300
+ }
301
+ return { entry: header, exits: [...new Set(exits)] };
302
+ }
303
+ /** The condition expression of an `if` — the named child between `(` and `)`. */
304
+ parenCondition(stmt) {
305
+ let sawOpen = false;
306
+ for (let i = 0; i < stmt.childCount; i++) {
307
+ const c = stmt.child(i);
308
+ if (!c)
309
+ continue;
310
+ if (c.type === '(') {
311
+ sawOpen = true;
312
+ continue;
313
+ }
314
+ if (c.type === ')')
315
+ return undefined;
316
+ if (sawOpen && c.isNamed && !isComment(c))
317
+ return c;
318
+ }
319
+ return undefined;
320
+ }
321
+ // ── loops ───────────────────────────────────────────────────────────────
322
+ /** `for ( for_loop_parts ) body:STMT` (C-style and for-in). */
323
+ visitFor(stmt) {
324
+ const labels = this.takeLabels();
325
+ const parts = stmt.namedChildren.find((c) => c.type === 'for_loop_parts');
326
+ const header = this.builder.newBlock(startLineOf(stmt), parts ? endLineOf(parts) : startLineOf(stmt), this.forHeaderText(stmt, parts), 'normal', this.harvest.forHeadFacts(parts));
327
+ const loopExit = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), '');
328
+ this.cfc.pushLoop(header, loopExit, labels);
329
+ const body = this.visitBody(stmt.childForFieldName('body'));
330
+ this.cfc.pop();
331
+ if (body) {
332
+ this.builder.edge(header, body.entry, 'cond-true');
333
+ this.builder.connect(body.exits, header, 'loop-back');
334
+ }
335
+ else {
336
+ this.builder.edge(header, header, 'loop-back'); // empty body re-iterates
337
+ }
338
+ // Structural exit edge — even `for (;;) {}` keeps EXIT reverse-reachable.
339
+ this.builder.edge(header, loopExit, 'cond-false');
340
+ return { entry: header, exits: [loopExit] };
341
+ }
342
+ forHeaderText(stmt, parts) {
343
+ return parts ? `for ${parts.text}` : 'for';
344
+ }
345
+ /** `while condition:parenthesized_expression body:STMT`. */
346
+ visitWhile(stmt) {
347
+ const labels = this.takeLabels();
348
+ const cond = stmt.childForFieldName('condition');
349
+ const header = this.builder.newBlock(startLineOf(stmt), cond ? endLineOf(cond) : startLineOf(stmt), cond ? `while ${cond.text}` : 'while', 'normal', cond ? this.harvest.facts(cond) : undefined);
350
+ const loopExit = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), '');
351
+ this.cfc.pushLoop(header, loopExit, labels);
352
+ const body = this.visitBody(stmt.childForFieldName('body'));
353
+ this.cfc.pop();
354
+ if (body) {
355
+ this.builder.edge(header, body.entry, 'cond-true');
356
+ this.builder.connect(body.exits, header, 'loop-back');
357
+ }
358
+ else {
359
+ this.builder.edge(header, header, 'loop-back'); // empty body re-tests
360
+ }
361
+ // Structural exit edge — even `while (true) {}` keeps EXIT reverse-reachable.
362
+ this.builder.edge(header, loopExit, 'cond-false');
363
+ return { entry: header, exits: [loopExit] };
364
+ }
365
+ /**
366
+ * `do body:STMT while condition:… ;` — BOTTOM-TEST: the body runs at least
367
+ * once, THEN the condition decides whether to loop back.
368
+ */
369
+ visitDoWhile(stmt) {
370
+ const labels = this.takeLabels();
371
+ const cond = stmt.childForFieldName('condition');
372
+ const condBlock = this.builder.newBlock(cond ? startLineOf(cond) : endLineOf(stmt), cond ? endLineOf(cond) : endLineOf(stmt), cond ? `while ${cond.text}` : 'while', 'normal', cond ? this.harvest.facts(cond) : undefined);
373
+ const loopExit = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), '');
374
+ // `continue` re-tests the condition; `break` leaves the loop.
375
+ this.cfc.pushLoop(condBlock, loopExit, labels);
376
+ const body = this.visitBody(stmt.childForFieldName('body'));
377
+ this.cfc.pop();
378
+ const backTarget = body ? body.entry : condBlock;
379
+ if (body)
380
+ this.builder.connect(body.exits, condBlock, 'seq');
381
+ this.builder.edge(condBlock, backTarget, 'loop-back'); // cond true → run body again
382
+ // Structural exit edge — even `do {} while (true)` keeps EXIT reachable.
383
+ this.builder.edge(condBlock, loopExit, 'cond-false');
384
+ return { entry: backTarget, exits: [loopExit] };
385
+ }
386
+ // ── switch (no implicit fallthrough; empty-case + continue-label spill) ─────
387
+ /**
388
+ * `switch condition:parenthesized_expression body:switch_block`. A non-empty
389
+ * case rejoins after the switch (no implicit fallthrough). An EMPTY case (no
390
+ * statements) spills into the next case (Dart empty-case fallthrough). An
391
+ * explicit `continue LABEL;` jumps to the labeled case.
392
+ */
393
+ visitSwitch(stmt) {
394
+ const labels = this.takeLabels();
395
+ // The `condition` field is a `parenthesized_expression` (verified) — unwrap it
396
+ // so the dispatch text/discriminant matches the value-position `visitSwitchExpr`
397
+ // form (`switch x`, not `switch (x)`). The harvest walks into the paren either
398
+ // way, so the def/use facts are unchanged — only the block text normalizes.
399
+ const condRaw = stmt.childForFieldName('condition');
400
+ const value = condRaw ? this.unwrapParen(condRaw) : undefined;
401
+ const dispatch = this.builder.newBlock(startLineOf(stmt), value ? endLineOf(value) : startLineOf(stmt), value ? `switch ${value.text}` : 'switch', 'normal', value ? this.harvest.facts(value) : undefined);
402
+ const switchExit = this.builder.newBlock(endLineOf(stmt), endLineOf(stmt), '');
403
+ const block = stmt.childForFieldName('body');
404
+ const cases = block
405
+ ? block.namedChildren.filter((c) => c.type === 'switch_statement_case' || c.type === 'switch_statement_default')
406
+ : [];
407
+ this.cfc.pushSwitch(switchExit, labels);
408
+ // Phase 1: build each case body. Register case labels so `continue LABEL`
409
+ // can target a labeled case's entry.
410
+ const caseBodies = [];
411
+ const caseLabels = [];
412
+ for (const c of cases) {
413
+ caseLabels.push(this.caseLabel(c));
414
+ // A case-pattern test runs conditionally before the body — harvest its
415
+ // uses onto the dispatch block (a later case tests only if earlier ones
416
+ // didn't match; any def there is a may-def).
417
+ for (const pat of this.casePatterns(c)) {
418
+ this.builder.attachFacts(dispatch, this.harvest.factsConditional(pat));
419
+ }
420
+ caseBodies.push(this.visitSeq(this.caseStatements(c)));
421
+ }
422
+ // The entry block of each case (its body, or the NEXT non-empty case's entry
423
+ // for an empty fallthrough case, or switchExit when nothing follows).
424
+ const entryOf = new Array(cases.length);
425
+ let after = switchExit;
426
+ for (let i = cases.length - 1; i >= 0; i--) {
427
+ entryOf[i] = caseBodies[i]?.entry ?? after;
428
+ after = entryOf[i];
429
+ }
430
+ // Resolve a `continue LABEL` target: the entry of the case carrying LABEL.
431
+ const labelTarget = (name) => {
432
+ const idx = caseLabels.findIndex((l) => l === name);
433
+ return idx >= 0 ? entryOf[idx] : undefined;
434
+ };
435
+ const hasDefault = cases.some((c) => c.type === 'switch_statement_default');
436
+ // Dispatch edges: one per case.
437
+ for (let i = 0; i < cases.length; i++) {
438
+ this.builder.edge(dispatch, entryOf[i], 'switch-case');
439
+ }
440
+ if (!hasDefault)
441
+ this.builder.edge(dispatch, switchExit, 'switch-case'); // no-match path
442
+ // Case-body completion: a non-empty case rejoins after the switch UNLESS it
443
+ // ends in an explicit `continue LABEL` (handled as the body's own terminator)
444
+ // — and an EMPTY case spills into the next case (fallthrough edge). A bare
445
+ // `break;` inside a case is a normal break to switchExit (the jump handler).
446
+ for (let i = 0; i < cases.length; i++) {
447
+ const res = caseBodies[i];
448
+ const contLabel = this.caseContinueLabel(cases[i]);
449
+ if (!res) {
450
+ // Empty case — spill to the next case (or switchExit).
451
+ this.builder.edge(dispatch, i + 1 < cases.length ? entryOf[i + 1] : switchExit, 'fallthrough');
452
+ continue;
453
+ }
454
+ if (contLabel) {
455
+ const tgt = labelTarget(contLabel);
456
+ if (tgt !== undefined)
457
+ this.builder.connect(res.exits, tgt, 'fallthrough');
458
+ else
459
+ this.builder.connect(res.exits, switchExit, 'seq');
460
+ }
461
+ else {
462
+ this.builder.connect(res.exits, switchExit, 'seq');
463
+ }
464
+ }
465
+ this.cfc.pop();
466
+ return { entry: dispatch, exits: [switchExit] };
467
+ }
468
+ /** The `label` name of a `switch_statement_case` (`myLabel: case …`), if any. */
469
+ caseLabel(c) {
470
+ const label = c.namedChildren.find((ch) => ch.type === 'label');
471
+ const id = label?.namedChildren.find((ch) => ch.type === 'identifier');
472
+ return id?.text || undefined;
473
+ }
474
+ /**
475
+ * Case-pattern nodes of a `switch_statement_case` whose test evaluates before
476
+ * the body — the only DISTINCT pattern node tree-sitter-dart emits is
477
+ * `constant_pattern` (`case 1:` / `case 1 || 2:`); a relational guard
478
+ * (`case > 5:`) is a bare `relational_operator` + operand, not a wrapper node.
479
+ * Harvesting the `constant_pattern` uses onto the dispatch block covers the
480
+ * common case; the rest fold into the body walk.
481
+ */
482
+ casePatterns(c) {
483
+ return c.namedChildren.filter((ch) => ch.type === 'constant_pattern');
484
+ }
485
+ /** Body statements of a switch case/default (skip the case keyword/pattern/label). */
486
+ caseStatements(c) {
487
+ const NON_BODY = new Set(['case_builtin', 'label', 'constant_pattern']);
488
+ // A LABELED `continue LABEL;` at a case tail is a fallthrough-spill, handled
489
+ // via caseContinueLabel — drop it from the body. But a BARE `continue;` targets
490
+ // the ENCLOSING LOOP (valid Dart) and MUST stay in the body: dropping it
491
+ // silently removes the jump and fabricates a false case→next-statement
492
+ // fall-through path. Only drop the labeled form.
493
+ return c.namedChildren.filter((ch) => !isComment(ch) && !NON_BODY.has(ch.type) && !isLabeledContinue(ch));
494
+ }
495
+ /** A trailing `continue LABEL;` in a case spills to the labeled case. */
496
+ caseContinueLabel(c) {
497
+ const cont = c.namedChildren.find((ch) => isLabeledContinue(ch));
498
+ if (!cont)
499
+ return undefined;
500
+ const id = cont.namedChildren.find((ch) => ch.type === 'identifier');
501
+ return id?.text || undefined;
502
+ }
503
+ // ── value-position switch expression (#2207) ────────────────────────────────
504
+ /**
505
+ * The direct value of a `local_variable_declaration` with a SINGLE
506
+ * `initialized_variable_definition` (`var x = <value>`): its `value` field.
507
+ * Returns undefined for a multi-binding decl (`var a = …, b = …`) — modeling
508
+ * those arm-by-arm is out of scope, so they coalesce inline.
509
+ */
510
+ directValue(stmt) {
511
+ const defs = stmt.namedChildren.filter((c) => c.type === 'initialized_variable_definition');
512
+ if (defs.length !== 1)
513
+ return undefined;
514
+ return defs[0].childForFieldName('value') ?? undefined;
515
+ }
516
+ /**
517
+ * Whether `node` is a value-position branch worth modeling as control flow
518
+ * (#2207): a `switch_expression` (Dart 3) with ≥2 arms — a real dispatch. Dart's
519
+ * value-position `if` does not exist; the ternary `?:` is excluded by design.
520
+ */
521
+ isModelableValueBranch(node) {
522
+ if (node.type !== 'switch_expression')
523
+ return false;
524
+ return node.namedChildren.filter((c) => c.type === 'switch_expression_case').length >= 2;
525
+ }
526
+ /** Model a value-position branch as control flow (only `switch_expression`). */
527
+ visitBranchExpr(node) {
528
+ return this.visitSwitchExpr(node);
529
+ }
530
+ /**
531
+ * Model a value-position `switch (v) { p [when g] => e, _ => e }` (Dart 3) as a
532
+ * CFG dispatch: a discriminant block, each arm's value a block reached by a
533
+ * `switch-case` edge, all arms rejoining at one exit (no fallthrough). The arm
534
+ * PATTERN and any `when` GUARD are harvested as conditional uses on the dispatch
535
+ * (they evaluate before the body, only when earlier arms missed); a Dart call
536
+ * value parses as `identifier` + `selector` (multiple children), so the arm-value
537
+ * facts come from each post-`=>` child. Only an UNGUARDED `_` arm is the
538
+ * exhaustive catch-all — a guarded `_ when …` is NOT (the no-match path still
539
+ * needs the conservative edge), mirroring the C# `visitSwitchExpr`.
540
+ */
541
+ visitSwitchExpr(node) {
542
+ const condRaw = node.childForFieldName('condition');
543
+ const cond = condRaw ? this.unwrapParen(condRaw) : node;
544
+ const dispatch = this.builder.newBlock(startLineOf(node), endLineOf(cond), `switch ${cond.text}`, 'normal', this.harvest.facts(cond));
545
+ const switchExit = this.builder.newBlock(endLineOf(node), endLineOf(node), '');
546
+ const arms = node.namedChildren.filter((c) => c.type === 'switch_expression_case');
547
+ let hasCatchAll = false;
548
+ for (const arm of arms) {
549
+ const { pattern, guards, values } = this.armParts(arm);
550
+ // The pattern + `when` guard are conditional dispatch tests, NOT arm-value
551
+ // uses — harvest them onto the dispatch (mirrors casePatterns for switch_statement).
552
+ if (pattern)
553
+ this.builder.attachFacts(dispatch, this.harvest.factsConditional(pattern));
554
+ for (const g of guards)
555
+ this.builder.attachFacts(dispatch, this.harvest.factsConditional(g));
556
+ if (pattern && pattern.text === '_' && guards.length === 0)
557
+ hasCatchAll = true;
558
+ const first = values[0] ?? arm;
559
+ const last = values[values.length - 1] ?? arm;
560
+ const armBlock = this.builder.newBlock(startLineOf(first), endLineOf(last), values.map((c) => c.text).join('') || arm.text, 'normal', undefined);
561
+ for (const v of values)
562
+ this.builder.attachFacts(armBlock, this.harvest.facts(v));
563
+ this.builder.edge(dispatch, armBlock, 'switch-case');
564
+ this.builder.edge(armBlock, switchExit, 'seq');
565
+ }
566
+ // A non-exhaustive Dart switch expression throws at runtime; conservatively
567
+ // keep EXIT reachable via a no-match edge when no `_` catch-all arm exists.
568
+ if (!hasCatchAll)
569
+ this.builder.edge(dispatch, switchExit, 'switch-case');
570
+ return { entry: dispatch, exits: [switchExit] };
571
+ }
572
+ /**
573
+ * Split a `switch_expression_case` at the `=>` token: the PATTERN (first named
574
+ * child before `=>`), any `when` GUARD (named children between the pattern and
575
+ * `=>` — tree-sitter-dart parses the guard as a bare sibling, not a wrapper),
576
+ * and the VALUE expression (named children after `=>` — a Dart call is split
577
+ * across `identifier` + `selector`, hence an array).
578
+ */
579
+ armParts(arm) {
580
+ const before = [];
581
+ const values = [];
582
+ let seenArrow = false;
583
+ for (let i = 0; i < arm.childCount; i++) {
584
+ const c = arm.child(i);
585
+ if (!c)
586
+ continue;
587
+ if (!c.isNamed) {
588
+ if (c.text === '=>')
589
+ seenArrow = true;
590
+ continue;
591
+ }
592
+ if (isComment(c))
593
+ continue;
594
+ (seenArrow ? values : before).push(c);
595
+ }
596
+ return { pattern: before[0], guards: before.slice(1), values };
597
+ }
598
+ /** Strip a `parenthesized_expression` wrapper (a switch/if condition). */
599
+ unwrapParen(node) {
600
+ if (node.type === 'parenthesized_expression') {
601
+ const inner = node.namedChildren.find((c) => !isComment(c));
602
+ if (inner)
603
+ return inner;
604
+ }
605
+ return node;
606
+ }
607
+ /**
608
+ * `var x = switch (v) { … }` (#2207): visit the switch as control flow, then
609
+ * rejoin its arms at a facts-only continuation carrying ONLY the declared name's
610
+ * def (the subject + arm-value uses are already on the switch's blocks). The
611
+ * arms are now control-dependent on the dispatch — mirrors Java / Kotlin / Rust.
612
+ */
613
+ visitBindBranch(stmt, branch) {
614
+ const res = this.visitBranchExpr(branch);
615
+ const cont = this.builder.newBlock(startLineOf(stmt), startLineOf(stmt), '', 'normal', this.harvest.bindingDefFacts(stmt));
616
+ this.builder.connect(res.exits, cont, 'seq');
617
+ return { entry: res.entry, exits: [cont] };
618
+ }
619
+ // ── try / on / catch / finally ─────────────────────────────────────────────
620
+ /**
621
+ * `try body:block (on TYPE? catch_clause? block)* finally_clause?`. The handler
622
+ * groups are bare siblings: an `on type_identifier`, an optional `catch_clause`
623
+ * (→ `catch_parameters`), and the handler `block`. The `finally` runs on BOTH
624
+ * normal and exception exit — a `return`/`break`/`continue` crossing it threads
625
+ * through (`finally-*` completion edges). Mirrors the Java/Kotlin `visitTry`.
626
+ */
627
+ visitTry(stmt) {
628
+ const bodyNode = stmt.childForFieldName('body');
629
+ const finallyClause = stmt.namedChildren.find((c) => c.type === 'finally_clause');
630
+ const finallyBody = finallyClause?.namedChildren.find((c) => c.type === 'block');
631
+ const handlerGroups = this.handlerGroups(stmt);
632
+ // The explicit finally is a finalizer the whole protected region threads through.
633
+ const finallyRes = finallyBody ? this.visitSeq(this.statementsOf(finallyBody)) : null;
634
+ const finallyFrame = finallyRes ? this.cfc.pushFinalizer(finallyRes.entry) : null;
635
+ const finalizerEntry = finallyRes?.entry;
636
+ const finalizerExits = finallyRes?.exits ?? null;
637
+ // Build each catch/on handler.
638
+ const catchExits = [];
639
+ let firstCatchEntry;
640
+ for (const group of handlerGroups) {
641
+ const clauseBody = group.block;
642
+ if (finalizerEntry !== undefined)
643
+ this.handlers.push(finalizerEntry);
644
+ let res = clauseBody ? this.visitSeq(this.statementsOf(clauseBody)) : null;
645
+ if (finalizerEntry !== undefined)
646
+ this.handlers.pop();
647
+ if (res === null) {
648
+ // Empty `on T { }` / `catch (e) {}` still catches — synthesize one block
649
+ // so exception flow lands somewhere and post-try code stays reachable.
650
+ const anchor = clauseBody ?? group.catchClause ?? stmt;
651
+ const idx = this.builder.newBlock(startLineOf(anchor), endLineOf(anchor), '');
652
+ res = { entry: idx, exits: [idx] };
653
+ }
654
+ const paramFacts = this.harvest.catchParamFacts(group.catchParameters);
655
+ if (paramFacts) {
656
+ const anchor = group.catchParameters ?? group.catchClause ?? stmt;
657
+ const paramBlock = this.builder.newBlock(startLineOf(anchor), startLineOf(anchor), '', 'normal', paramFacts);
658
+ this.builder.edge(paramBlock, res.entry, 'seq');
659
+ res = { entry: paramBlock, exits: res.exits };
660
+ }
661
+ catchExits.push(...res.exits);
662
+ if (firstCatchEntry === undefined)
663
+ firstCatchEntry = res.entry;
664
+ }
665
+ // Handler for the try body: first catch if present, else the finally, else outer.
666
+ const tryHandler = firstCatchEntry ?? finalizerEntry ?? this.currentHandler();
667
+ const protectedStart = this.builder.blockCount;
668
+ this.handlers.push(tryHandler);
669
+ const bodyRes = bodyNode ? this.visitSeq(this.statementsOf(bodyNode)) : null;
670
+ this.handlers.pop();
671
+ if (handlerGroups.length > 0 || finalizerEntry !== undefined) {
672
+ for (let b = protectedStart; b < this.builder.blockCount; b++) {
673
+ this.builder.edge(b, tryHandler, 'throw');
674
+ }
675
+ }
676
+ // Pop the finalizer frame and drain its pending completion legs.
677
+ if (finallyFrame && finallyRes) {
678
+ this.cfc.pop();
679
+ drainFinalizerPending(this.builder, finallyFrame, finallyRes.exits);
680
+ }
681
+ const exits = [];
682
+ if (finalizerEntry !== undefined) {
683
+ if (bodyRes)
684
+ this.builder.connect(bodyRes.exits, finalizerEntry, 'seq');
685
+ for (const e of catchExits)
686
+ this.builder.edge(e, finalizerEntry, 'seq');
687
+ if (finalizerExits)
688
+ exits.push(...finalizerExits);
689
+ // No catch → an exception re-propagates out after the finally runs.
690
+ if (handlerGroups.length === 0 && finalizerExits) {
691
+ this.builder.connect(finalizerExits, this.currentHandler(), 'throw');
692
+ }
693
+ }
694
+ else {
695
+ if (bodyRes)
696
+ exits.push(...bodyRes.exits);
697
+ exits.push(...catchExits);
698
+ }
699
+ const entry = bodyRes?.entry ?? finalizerEntry ?? firstCatchEntry;
700
+ if (entry === undefined)
701
+ return null;
702
+ return { entry, exits: [...new Set(exits)] };
703
+ }
704
+ /**
705
+ * The handler groups of a `try_statement`: each is an `on TYPE` and/or
706
+ * `catch (params)` plus the handler `block` that follows it. The `on`/`type`/
707
+ * `catch_clause`/`block` are bare children in source order, so a group is a
708
+ * `catch_clause` (or an `on` keyword + `type_identifier`) followed by a `block`.
709
+ */
710
+ handlerGroups(stmt) {
711
+ const groups = [];
712
+ const bodyNode = stmt.childForFieldName('body');
713
+ let pendingCatch;
714
+ let sawOn = false;
715
+ for (let i = 0; i < stmt.childCount; i++) {
716
+ const c = stmt.child(i);
717
+ if (!c)
718
+ continue;
719
+ if (c.type === 'finally_clause')
720
+ break; // finally is separate
721
+ if (c.type === 'on') {
722
+ sawOn = true;
723
+ continue;
724
+ }
725
+ if (c.type === 'catch_clause') {
726
+ pendingCatch = c;
727
+ continue;
728
+ }
729
+ if (c.type === 'block' && c !== bodyNode) {
730
+ const catchParameters = pendingCatch?.namedChildren.find((ch) => ch.type === 'catch_parameters');
731
+ groups.push({ catchClause: pendingCatch, catchParameters, block: c });
732
+ pendingCatch = undefined;
733
+ sawOn = false;
734
+ }
735
+ }
736
+ // A trailing `on T catch (e)` with no `{}` body is malformed; ignore here.
737
+ void sawOn;
738
+ return groups;
739
+ }
740
+ /** Nearest enclosing exception handler, or the function EXIT. */
741
+ currentHandler() {
742
+ return this.handlers.length ? this.handlers[this.handlers.length - 1] : this.builder.exitIndex;
743
+ }
744
+ }
745
+ /** The signature node that precedes a `function_body` (params live there). */
746
+ function signatureFor(fnNode) {
747
+ if (fnNode.type !== 'function_body')
748
+ return undefined;
749
+ const prev = fnNode.previousSibling;
750
+ if (!prev)
751
+ return undefined;
752
+ if (DART_SIGNATURE_TYPES.has(prev.type))
753
+ return prev;
754
+ // A `method_signature` wraps a `function_signature` / getter / setter — the
755
+ // params live on the wrapped signature for a plain method but the wrapper still
756
+ // carries them transitively; return the wrapper (paramList searches its named
757
+ // children, which include the `formal_parameter_list`).
758
+ return undefined;
759
+ }
760
+ /** The body STMT of a `function_body` / `function_expression`: a `block` or an arrow expr. */
761
+ function bodyAndArrow(fnNode) {
762
+ let container = fnNode;
763
+ if (fnNode.type === 'function_expression') {
764
+ container = fnNode.childForFieldName('body') ?? undefined; // function_expression_body
765
+ }
766
+ if (!container)
767
+ return {};
768
+ const block = container.namedChildren.find((c) => c.type === 'block');
769
+ if (block)
770
+ return { block };
771
+ // Arrow body `=> expr`: the expression is the first non-comment named child.
772
+ const arrowExpr = container.namedChildren.find((c) => !COMMENT_TYPES.has(c.type));
773
+ return { arrowExpr };
774
+ }
775
+ /** Build the CFG for one Dart function node, or `undefined` if not modelable. */
776
+ function buildFunctionCfg(fnNode, filePath) {
777
+ try {
778
+ if (!isFunction(fnNode))
779
+ return undefined;
780
+ const signature = signatureFor(fnNode);
781
+ // Anchor the span/column to the signature when present (so same-line
782
+ // functions `void a(){} void b(){}` get distinct start columns from the
783
+ // signature, and the span covers the declaration head), else the body node.
784
+ const anchor = signature ?? fnNode;
785
+ const startLine = startLineOf(anchor);
786
+ const endLine = endLineOf(fnNode);
787
+ const startColumn = anchor.startPosition.column;
788
+ const { block, arrowExpr } = bodyAndArrow(fnNode);
789
+ const builder = new CfgBuilder(filePath, startLine, endLine, startColumn);
790
+ const harvest = new DartHarvester(fnNode, signature);
791
+ const paramFacts = harvest.paramFacts();
792
+ if (paramFacts)
793
+ builder.attachFacts(builder.entryIndex, paramFacts);
794
+ // Arrow body (`=> expr`): one block whose value is returned.
795
+ if (!block && arrowExpr) {
796
+ const blk = builder.newBlock(startLineOf(arrowExpr), endLineOf(arrowExpr), arrowExpr.text, 'normal', harvest.facts(arrowExpr));
797
+ builder.edge(builder.entryIndex, blk, 'seq');
798
+ builder.edge(blk, builder.exitIndex, 'return');
799
+ return builder.finish(harvest.bindingTable());
800
+ }
801
+ const walk = new DartCfgWalk(builder, harvest);
802
+ const res = block ? walk.visitSeq(block.namedChildren.filter((c) => !isComment(c))) : null;
803
+ if (!res) {
804
+ builder.edge(builder.entryIndex, builder.exitIndex, 'seq'); // empty body
805
+ return builder.finish(harvest.bindingTable());
806
+ }
807
+ builder.edge(builder.entryIndex, res.entry, 'seq');
808
+ builder.connect(res.exits, builder.exitIndex, 'seq'); // normal fall-off → EXIT
809
+ return builder.finish(harvest.bindingTable());
810
+ }
811
+ catch (err) {
812
+ // Never throw out of buildFunctionCfg — a malformed AST shape must skip only
813
+ // this one function's CFG, never drop the whole file's language group (R4).
814
+ // eslint-disable-next-line no-console
815
+ console.warn(`[cfg] Dart buildFunctionCfg skipped a function in ${filePath}: ${String(err)}`);
816
+ return undefined;
817
+ }
818
+ }
819
+ /**
820
+ * Whether a node is a Dart function this visitor builds a CFG for: a
821
+ * `function_body` whose previous sibling is a signature (top-level fn / method /
822
+ * getter / setter / constructor), or a `function_expression` (a closure).
823
+ */
824
+ function isFunction(node) {
825
+ if (node.type === 'function_expression')
826
+ return true;
827
+ if (node.type !== 'function_body')
828
+ return false;
829
+ const prev = node.previousSibling;
830
+ return prev !== null && DART_SIGNATURE_TYPES.has(prev.type);
831
+ }
832
+ /** The Dart CFG visitor. */
833
+ export function createDartCfgVisitor() {
834
+ return { buildFunctionCfg, isFunction };
835
+ }
836
+ export { DART_SIGNATURE_TYPES };