@backbencher/brain 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (319) hide show
  1. package/CHANGELOG.md +298 -0
  2. package/EMBEDDINGS.md +355 -0
  3. package/LICENSE +21 -0
  4. package/README.md +415 -2
  5. package/bin/bb +13 -0
  6. package/dist/analysis-exclusions-7ZJDXOVZ.js +35 -0
  7. package/dist/brain-index-health-KY7K552E.js +17 -0
  8. package/dist/brain-relationship-enrichment-QH7OOHUF.js +558 -0
  9. package/dist/brain.sql-EIM4WS4D.js +29 -0
  10. package/dist/chunk-2BSKD7O3.js +40 -0
  11. package/dist/chunk-2DE27DVC.js +1314 -0
  12. package/dist/chunk-2RRBMNBQ.js +322 -0
  13. package/dist/chunk-2RTN2DFU.js +711 -0
  14. package/dist/chunk-2WXYNLBA.js +29 -0
  15. package/dist/chunk-3CTFIJJB.js +17 -0
  16. package/dist/chunk-3LFTSMIV.js +174 -0
  17. package/dist/chunk-47HKX4AJ.js +930 -0
  18. package/dist/chunk-4H22TLOT.js +543 -0
  19. package/dist/chunk-5SD45FLC.js +818 -0
  20. package/dist/chunk-66A5ZFYA.js +1758 -0
  21. package/dist/chunk-6AIRWAO2.js +247 -0
  22. package/dist/chunk-6WNGD3NN.js +196 -0
  23. package/dist/chunk-7AN7U5IZ.js +371 -0
  24. package/dist/chunk-BHB7BYYI.js +626 -0
  25. package/dist/chunk-BQ3OVQ3P.js +125 -0
  26. package/dist/chunk-CW4XXSNN.js +1479 -0
  27. package/dist/chunk-D2Q4Y47A.js +53 -0
  28. package/dist/chunk-DIYAGQJL.js +34 -0
  29. package/dist/chunk-DYM4FQEE.js +90 -0
  30. package/dist/chunk-EWMWK7WN.js +75 -0
  31. package/dist/chunk-FVVQTSIM.js +3427 -0
  32. package/dist/chunk-FWSL4VG6.js +1290 -0
  33. package/dist/chunk-GBI7V7KY.js +69 -0
  34. package/dist/chunk-GEFFO6O2.js +152 -0
  35. package/dist/chunk-HOWAQXJO.js +236 -0
  36. package/dist/chunk-HQSPJDHI.js +613 -0
  37. package/dist/chunk-IFZNR5UJ.js +2863 -0
  38. package/dist/chunk-J4PMBXQQ.js +336 -0
  39. package/dist/chunk-KAET47ZL.js +125 -0
  40. package/dist/chunk-KCDECRJY.js +440 -0
  41. package/dist/chunk-KLK3PBTO.js +1119 -0
  42. package/dist/chunk-L5TDYKYK.js +76 -0
  43. package/dist/chunk-MLKGABMK.js +9 -0
  44. package/dist/chunk-OWISBAOF.js +61 -0
  45. package/dist/chunk-PJTNGD3Z.js +6924 -0
  46. package/dist/chunk-QLK6ED4Q.js +235 -0
  47. package/dist/chunk-R2GW4F6R.js +19 -0
  48. package/dist/chunk-RCMTFSEF.js +87 -0
  49. package/dist/chunk-RH7KC5HU.js +12050 -0
  50. package/dist/chunk-RM3PY6TG.js +376 -0
  51. package/dist/chunk-ROMM6KMC.js +433 -0
  52. package/dist/chunk-SK6YVHFZ.js +211 -0
  53. package/dist/chunk-SM7RFQRO.js +214 -0
  54. package/dist/chunk-UFE77L67.js +782 -0
  55. package/dist/chunk-UGR4QIGS.js +67 -0
  56. package/dist/chunk-UUNHHX4M.js +13 -0
  57. package/dist/chunk-V44PGPA6.js +2104 -0
  58. package/dist/chunk-VL52PL2T.js +71 -0
  59. package/dist/chunk-VURYFIHW.js +201 -0
  60. package/dist/chunk-XJKJYIPP.js +7436 -0
  61. package/dist/chunk-YI6FF2ZR.js +340 -0
  62. package/dist/cli/index.js +37 -0
  63. package/dist/config-HZAAYDP3.js +10 -0
  64. package/dist/content-search-642F7E5V.js +593 -0
  65. package/dist/database-TCTZDLE5.js +9 -0
  66. package/dist/doctor-GXCKSJOX.js +395 -0
  67. package/dist/edit-engine-KM7MPFEC.js +51 -0
  68. package/dist/engine-42TZFNIZ.js +11 -0
  69. package/dist/engine-LO6L4JLQ.js +18 -0
  70. package/dist/fingerprint-XDXFRWO6.js +33 -0
  71. package/dist/framework-intelligence-DWFCKV44.js +860 -0
  72. package/dist/freshness-3K6VU5OT.js +174 -0
  73. package/dist/graph-engine-PL5YA7BA.js +21 -0
  74. package/dist/graph_request-MM4REP6H.js +350 -0
  75. package/dist/impact_request-WDE47IZ5.js +68 -0
  76. package/dist/index-space-IUUN5WYV.js +15 -0
  77. package/dist/index-status-6BD7UBHR.js +34 -0
  78. package/dist/index.js +159 -0
  79. package/dist/instance-FO6QZRLH.js +19 -0
  80. package/dist/language-detection-DFCAU5DZ.js +11 -0
  81. package/dist/lsp/server.js +4301 -0
  82. package/dist/mcp/server.js +132 -0
  83. package/dist/onboard-DIQZV2NW.js +161 -0
  84. package/dist/override-companion-pass-XR7MHTP7.js +117 -0
  85. package/dist/parallel-indexer-4PW3AFXP.js +88 -0
  86. package/dist/pool-worker.js +1153 -0
  87. package/dist/project-health-ZS5TS2Z6.js +94 -0
  88. package/dist/project-index-5SDPHITE.js +57 -0
  89. package/dist/project-registry-DGZBIZS7.js +355 -0
  90. package/dist/real-bash-analyzer-SXSV67E2.js +2201 -0
  91. package/dist/real-c-analyzer-SY7FTZ6W.js +5942 -0
  92. package/dist/real-clean-analyzer-IGDHHLZK.js +3470 -0
  93. package/dist/real-clojure-analyzer-4OH7IWLB.js +2818 -0
  94. package/dist/real-cpp-analyzer-7LTDRJRJ.js +4101 -0
  95. package/dist/real-csharp-analyzer-KICU3TLH.js +3298 -0
  96. package/dist/real-css-analyzer-HPU277Q6.js +15 -0
  97. package/dist/real-dart-analyzer-7LZ5LCMW.js +3944 -0
  98. package/dist/real-elixir-analyzer-BE3MEVZO.js +2192 -0
  99. package/dist/real-erlang-analyzer-BZMAKYBK.js +1731 -0
  100. package/dist/real-fsharp-analyzer-5QLTOV3E.js +2390 -0
  101. package/dist/real-go-analyzer-POD7MZQR.js +4126 -0
  102. package/dist/real-graphql-analyzer-MAQRZ6QW.js +1442 -0
  103. package/dist/real-groovy-analyzer-MEWSUPMJ.js +2285 -0
  104. package/dist/real-haskell-analyzer-I5L2GR3Y.js +2533 -0
  105. package/dist/real-hcl-analyzer-DBOPXSWJ.js +725 -0
  106. package/dist/real-html-analyzer-O63XZLZD.js +1759 -0
  107. package/dist/real-java-analyzer-CHCW3EKA.js +4677 -0
  108. package/dist/real-javascript-analyzer-QAAJQYZE.js +14 -0
  109. package/dist/real-json-analyzer-UGBR7YQN.js +1640 -0
  110. package/dist/real-julia-analyzer-35Q4GHOZ.js +2085 -0
  111. package/dist/real-kotlin-analyzer-GRAYCWW2.js +3956 -0
  112. package/dist/real-lua-analyzer-5BAPDZDF.js +1957 -0
  113. package/dist/real-makefile-analyzer-YJKAHNAS.js +920 -0
  114. package/dist/real-markdown-analyzer-UKUQZO4F.js +1215 -0
  115. package/dist/real-matlab-analyzer-FVIKQZKG.js +480 -0
  116. package/dist/real-nim-analyzer-M7FAIABG.js +3603 -0
  117. package/dist/real-objective-c-analyzer-XMYQIGQI.js +1518 -0
  118. package/dist/real-perl-analyzer-ZLY6M2DZ.js +1865 -0
  119. package/dist/real-php-analyzer-KI2ZAM7X.js +9368 -0
  120. package/dist/real-powershell-analyzer-B3T5ZP6U.js +2915 -0
  121. package/dist/real-protobuf-analyzer-23OZ75XV.js +867 -0
  122. package/dist/real-python-analyzer-I4TIUB6D.js +4880 -0
  123. package/dist/real-r-analyzer-FJZJXXS6.js +2596 -0
  124. package/dist/real-ruby-analyzer-M3UFIVTQ.js +2429 -0
  125. package/dist/real-rust-analyzer-LVSONPAN.js +3359 -0
  126. package/dist/real-scala-analyzer-CWPI33Q2.js +3293 -0
  127. package/dist/real-scss-analyzer-TGRJQ7TH.js +1515 -0
  128. package/dist/real-solidity-analyzer-4P2VBVWA.js +2608 -0
  129. package/dist/real-sql-analyzer-J7DYZOZR.js +3840 -0
  130. package/dist/real-svelte-analyzer-NHEN2SX5.js +2532 -0
  131. package/dist/real-swift-analyzer-2NSYULWI.js +2311 -0
  132. package/dist/real-toml-analyzer-L3C66L7Q.js +929 -0
  133. package/dist/real-typescript-analyzer-FTC6ETRQ.js +14 -0
  134. package/dist/real-vb-analyzer-ORY7LAQS.js +3145 -0
  135. package/dist/real-vba-analyzer-JH2TTUFI.js +466 -0
  136. package/dist/real-vue-analyzer-OO4FUGBV.js +2680 -0
  137. package/dist/real-xml-analyzer-6CQVR477.js +2405 -0
  138. package/dist/real-yaml-analyzer-J6MQG3YX.js +3528 -0
  139. package/dist/real-zig-analyzer-EFKCPJNU.js +2430 -0
  140. package/dist/regex-search-OPTLKUN5.js +239 -0
  141. package/dist/rules-I453JTRC.js +152 -0
  142. package/dist/scoped-path-MJYWJZQX.js +11 -0
  143. package/dist/setup-show-Y5GB5KXA.js +15 -0
  144. package/dist/setup-wizard-G5YYR5T6.js +24 -0
  145. package/dist/symbol-search-KB4MU4SI.js +16 -0
  146. package/dist/taint-YOTN5X55.js +202 -0
  147. package/dist/taint-analyzer-N7YLZU3N.js +163 -0
  148. package/dist/tool-handlers-G4NAGJIH.js +72 -0
  149. package/dist/tool-registry-BPEJNSZG.js +17 -0
  150. package/dist/tool-validation-RF675A3C.js +32 -0
  151. package/dist/type-inference-4Y7WTMFE.js +13 -0
  152. package/dist/vector-binary-LMY4QGAN.js +19 -0
  153. package/dist/version-LXGNFO37.js +9 -0
  154. package/dist/wasm-NMWO3WXY.js +16 -0
  155. package/docs/EMBEDDINGS.md +132 -0
  156. package/docs/README.md +178 -0
  157. package/docs/data/m10-baseline.json +602 -0
  158. package/docs/data/m10-benchmark-baseline.json +57 -0
  159. package/docs/data/m10-benchmark-last-run.json +127 -0
  160. package/docs/data/m10-final-audit.json +215 -0
  161. package/docs/data/m12-final-audit.json +138 -0
  162. package/docs/data/m12-surface-matrix.json +50 -0
  163. package/docs/data/public-tools.json +1902 -0
  164. package/docs/indexing/DEAD-ASSETS.md +88 -0
  165. package/docs/indexing/INDEXER-REVIEW.md +634 -0
  166. package/docs/indexing/PERFORMANCE-FINDINGS.md +106 -0
  167. package/docs/indexing/README.md +427 -0
  168. package/docs/indexing/discovery-matrix.md +41 -0
  169. package/docs/indexing/embeddings.md +325 -0
  170. package/docs/indexing/environment-variables.md +77 -0
  171. package/docs/indexing/performance.md +323 -0
  172. package/docs/indexing/resume.md +345 -0
  173. package/docs/indexing/troubleshooting.md +391 -0
  174. package/docs/man/bb.1 +190 -0
  175. package/docs/tools/bb_analyze.md +147 -0
  176. package/docs/tools/bb_annotate.md +110 -0
  177. package/docs/tools/bb_annotations.md +106 -0
  178. package/docs/tools/bb_ask.md +101 -0
  179. package/docs/tools/bb_cross_language.md +103 -0
  180. package/docs/tools/bb_doc.md +169 -0
  181. package/docs/tools/bb_edit.md +185 -0
  182. package/docs/tools/bb_graph.md +169 -0
  183. package/docs/tools/bb_health.md +156 -0
  184. package/docs/tools/bb_impact.md +117 -0
  185. package/docs/tools/bb_index.md +108 -0
  186. package/docs/tools/bb_migrate.md +135 -0
  187. package/docs/tools/bb_patterns.md +140 -0
  188. package/docs/tools/bb_search.md +150 -0
  189. package/docs/tools/bb_security.md +100 -0
  190. package/docs/tools/bb_visualize.md +184 -0
  191. package/grammars/README.md +57 -0
  192. package/grammars/THIRD-PARTY-NOTICES.md +50 -0
  193. package/grammars/tree-sitter-c/LICENSE +21 -0
  194. package/grammars/tree-sitter-clojure/LICENSE.md +21 -0
  195. package/grammars/tree-sitter-cpp/LICENSE +21 -0
  196. package/grammars/tree-sitter-csharp/LICENSE +21 -0
  197. package/grammars/tree-sitter-css/LICENSE +21 -0
  198. package/grammars/tree-sitter-css/README.md +21 -0
  199. package/grammars/tree-sitter-dart/LICENSE +20 -0
  200. package/grammars/tree-sitter-dart-upstream/LICENSE +20 -0
  201. package/grammars/tree-sitter-dart-upstream/README.md +18 -0
  202. package/grammars/tree-sitter-elixir/LICENSE +176 -0
  203. package/grammars/tree-sitter-elixir/README.md +11 -0
  204. package/grammars/tree-sitter-fsharp/LICENSE +21 -0
  205. package/grammars/tree-sitter-fsharp/README.md +45 -0
  206. package/grammars/tree-sitter-graphql/LICENSE +21 -0
  207. package/grammars/tree-sitter-graphql/README.md +106 -0
  208. package/grammars/tree-sitter-groovy/LICENSE +21 -0
  209. package/grammars/tree-sitter-groovy/README.md +4 -0
  210. package/grammars/tree-sitter-haskell/LICENSE +21 -0
  211. package/grammars/tree-sitter-haskell/README.md +394 -0
  212. package/grammars/tree-sitter-hcl/CHANGELOG.md +102 -0
  213. package/grammars/tree-sitter-hcl/LICENSE +201 -0
  214. package/grammars/tree-sitter-hcl/README.md +39 -0
  215. package/grammars/tree-sitter-html/LICENSE +21 -0
  216. package/grammars/tree-sitter-html/README.md +21 -0
  217. package/grammars/tree-sitter-julia/LICENSE +21 -0
  218. package/grammars/tree-sitter-julia/README.md +24 -0
  219. package/grammars/tree-sitter-kotlin/LICENSE +9 -0
  220. package/grammars/tree-sitter-lua/LICENSE.md +21 -0
  221. package/grammars/tree-sitter-lua/README.md +29 -0
  222. package/grammars/tree-sitter-make/LICENSE +21 -0
  223. package/grammars/tree-sitter-matlab/LICENSE +19 -0
  224. package/grammars/tree-sitter-matlab/README.md +99 -0
  225. package/grammars/tree-sitter-nim/LICENSE +373 -0
  226. package/grammars/tree-sitter-nim/README.md +46 -0
  227. package/grammars/tree-sitter-objc/LICENSE +21 -0
  228. package/grammars/tree-sitter-objc/README.md +20 -0
  229. package/grammars/tree-sitter-perl/LICENSE +7 -0
  230. package/grammars/tree-sitter-perl/README.md +100 -0
  231. package/grammars/tree-sitter-powershell/LICENSE +21 -0
  232. package/grammars/tree-sitter-powershell/README.md +7 -0
  233. package/grammars/tree-sitter-proto/LICENSE +21 -0
  234. package/grammars/tree-sitter-proto/README.md +105 -0
  235. package/grammars/tree-sitter-r/LICENSE +9 -0
  236. package/grammars/tree-sitter-r/README.md +66 -0
  237. package/grammars/tree-sitter-ruby/LICENSE +22 -0
  238. package/grammars/tree-sitter-scss/LICENSE +21 -0
  239. package/grammars/tree-sitter-scss/README.md +17 -0
  240. package/grammars/tree-sitter-solidity/LICENSE +7 -0
  241. package/grammars/tree-sitter-sql/LICENSE +21 -0
  242. package/grammars/tree-sitter-typescript/LICENSE +21 -0
  243. package/grammars/tree-sitter-vba/LICENSE +21 -0
  244. package/grammars/tree-sitter-vba/README.md +738 -0
  245. package/grammars/tree-sitter-vbnet/LICENSE +21 -0
  246. package/grammars/tree-sitter-vbnet/README.md +322 -0
  247. package/grammars/tree-sitter-xml/LICENSE +19 -0
  248. package/grammars/tree-sitter-yaml/LICENSE +20 -0
  249. package/grammars/tree-sitter-yaml/README.md +21 -0
  250. package/package.json +184 -4
  251. package/wasm/dart.wasm +0 -0
  252. package/wasm/tree-sitter-bash.wasm +0 -0
  253. package/wasm/tree-sitter-c.wasm +0 -0
  254. package/wasm/tree-sitter-c_sharp.wasm +0 -0
  255. package/wasm/tree-sitter-clean.wasm +0 -0
  256. package/wasm/tree-sitter-clojure.wasm +0 -0
  257. package/wasm/tree-sitter-core_schema.wasm +0 -0
  258. package/wasm/tree-sitter-cpp.wasm +0 -0
  259. package/wasm/tree-sitter-css.wasm +0 -0
  260. package/wasm/tree-sitter-dart.wasm +0 -0
  261. package/wasm/tree-sitter-elisp.wasm +0 -0
  262. package/wasm/tree-sitter-elixir.wasm +0 -0
  263. package/wasm/tree-sitter-elm.wasm +0 -0
  264. package/wasm/tree-sitter-embedded_template.wasm +0 -0
  265. package/wasm/tree-sitter-erlang.wasm +0 -0
  266. package/wasm/tree-sitter-fsharp.wasm +0 -0
  267. package/wasm/tree-sitter-fsharp_signature.wasm +0 -0
  268. package/wasm/tree-sitter-go.wasm +0 -0
  269. package/wasm/tree-sitter-graphql.wasm +0 -0
  270. package/wasm/tree-sitter-groovy.wasm +0 -0
  271. package/wasm/tree-sitter-haskell.wasm +0 -0
  272. package/wasm/tree-sitter-hcl.wasm +0 -0
  273. package/wasm/tree-sitter-html.wasm +0 -0
  274. package/wasm/tree-sitter-java.wasm +0 -0
  275. package/wasm/tree-sitter-javascript.wasm +0 -0
  276. package/wasm/tree-sitter-json.wasm +0 -0
  277. package/wasm/tree-sitter-json_schema.wasm +0 -0
  278. package/wasm/tree-sitter-julia.wasm +0 -0
  279. package/wasm/tree-sitter-kotlin.wasm +0 -0
  280. package/wasm/tree-sitter-lua.wasm +0 -0
  281. package/wasm/tree-sitter-make.wasm +0 -0
  282. package/wasm/tree-sitter-markdown.wasm +0 -0
  283. package/wasm/tree-sitter-markdown_inline.wasm +0 -0
  284. package/wasm/tree-sitter-matlab.wasm +0 -0
  285. package/wasm/tree-sitter-nim.wasm +0 -0
  286. package/wasm/tree-sitter-objc.wasm +0 -0
  287. package/wasm/tree-sitter-ocaml.wasm +0 -0
  288. package/wasm/tree-sitter-perl.wasm +0 -0
  289. package/wasm/tree-sitter-php.wasm +0 -0
  290. package/wasm/tree-sitter-php_only.wasm +0 -0
  291. package/wasm/tree-sitter-powershell.wasm +0 -0
  292. package/wasm/tree-sitter-protobuf.wasm +0 -0
  293. package/wasm/tree-sitter-python.wasm +0 -0
  294. package/wasm/tree-sitter-ql.wasm +0 -0
  295. package/wasm/tree-sitter-r.wasm +0 -0
  296. package/wasm/tree-sitter-rescript.wasm +0 -0
  297. package/wasm/tree-sitter-ruby.wasm +0 -0
  298. package/wasm/tree-sitter-rust.wasm +0 -0
  299. package/wasm/tree-sitter-scala.wasm +0 -0
  300. package/wasm/tree-sitter-scss.wasm +0 -0
  301. package/wasm/tree-sitter-solidity.wasm +0 -0
  302. package/wasm/tree-sitter-sql.wasm +0 -0
  303. package/wasm/tree-sitter-svelte.wasm +0 -0
  304. package/wasm/tree-sitter-swift.wasm +0 -0
  305. package/wasm/tree-sitter-systemrdl.wasm +0 -0
  306. package/wasm/tree-sitter-terraform.wasm +0 -0
  307. package/wasm/tree-sitter-tlaplus.wasm +0 -0
  308. package/wasm/tree-sitter-toml.wasm +0 -0
  309. package/wasm/tree-sitter-tsx.wasm +0 -0
  310. package/wasm/tree-sitter-typescript.wasm +0 -0
  311. package/wasm/tree-sitter-vba.wasm +0 -0
  312. package/wasm/tree-sitter-vbnet.wasm +0 -0
  313. package/wasm/tree-sitter-vue.wasm +0 -0
  314. package/wasm/tree-sitter-xml.wasm +0 -0
  315. package/wasm/tree-sitter-yaml.wasm +0 -0
  316. package/wasm/tree-sitter-zig.wasm +0 -0
  317. package/wasm/tree-sitter.wasm +0 -0
  318. package/wasm/tsx.wasm +0 -0
  319. package/wasm/typescript.wasm +0 -0
@@ -0,0 +1,2430 @@
1
+ import {
2
+ FILE_CLAIM_MAX_BYTES,
3
+ fileClaimForTarget
4
+ } from "./chunk-EWMWK7WN.js";
5
+ import {
6
+ ASTEngine
7
+ } from "./chunk-V44PGPA6.js";
8
+ import "./chunk-BQ3OVQ3P.js";
9
+ import "./chunk-SK6YVHFZ.js";
10
+ import "./chunk-2BSKD7O3.js";
11
+ import "./chunk-KAET47ZL.js";
12
+ import "./chunk-KCDECRJY.js";
13
+ import "./chunk-MLKGABMK.js";
14
+
15
+ // src/ast/real-zig-analyzer.ts
16
+ var ZIG_ADMITTED_EXTENSIONS = ["zig"];
17
+ var ZIG_TYPE_RHS = /* @__PURE__ */ new Set([
18
+ "builtin_type",
19
+ "pointer_type",
20
+ "slice_type",
21
+ "array_type",
22
+ "nullable_type",
23
+ "error_union_type",
24
+ "anyframe_type",
25
+ "opaque_declaration",
26
+ "function_signature"
27
+ ]);
28
+ var ANONYMOUS_CONTAINER_NODES = /* @__PURE__ */ new Set([
29
+ "struct_declaration",
30
+ "union_declaration",
31
+ "enum_declaration",
32
+ "error_set_declaration",
33
+ "opaque_declaration"
34
+ ]);
35
+ var ENUM_MEMBER_NAME_NODE_TYPES = /* @__PURE__ */ new Set([
36
+ "builtin_type",
37
+ "boolean",
38
+ "null",
39
+ "undefined"
40
+ ]);
41
+ var ZIG_VALUE_RHS = /* @__PURE__ */ new Set([
42
+ "integer",
43
+ "float",
44
+ "string",
45
+ "multiline_string",
46
+ "boolean",
47
+ "character",
48
+ "binary_expression",
49
+ "unary_expression",
50
+ "null_coercion_expression",
51
+ "index_expression",
52
+ "catch_expression",
53
+ "block",
54
+ "initializer_list",
55
+ "range_expression",
56
+ "assignment_expression",
57
+ "error_type",
58
+ "anonymous_struct_initializer",
59
+ "dereference_expression"
60
+ ]);
61
+ var ZIG_FILE_SCOPE_NAMES = /* @__PURE__ */ new WeakMap();
62
+ function zigFileScopeNames(root) {
63
+ const cached = ZIG_FILE_SCOPE_NAMES.get(root);
64
+ if (cached) return cached;
65
+ const BARRIERS = /* @__PURE__ */ new Set([
66
+ "function_declaration",
67
+ "test_declaration",
68
+ "block",
69
+ "if_statement",
70
+ "if_expression",
71
+ "for_statement",
72
+ "for_expression",
73
+ "while_statement",
74
+ "while_expression",
75
+ "switch_statement",
76
+ "switch_expression",
77
+ "catch_expression",
78
+ "defer_statement",
79
+ "expression_statement",
80
+ "comptime_statement",
81
+ "errdefer_statement"
82
+ ]);
83
+ const out = /* @__PURE__ */ new Set();
84
+ const stack = [root];
85
+ let guard = 0;
86
+ while (stack.length > 0 && guard++ < 4e5) {
87
+ const n = stack.pop();
88
+ if (n.type === "variable_declaration" || n.type === "function_declaration" || n.type === "container_field") {
89
+ const c0 = n.namedChildCount > 0 ? n.namedChild(0) : null;
90
+ if (c0 && c0.type === "identifier") {
91
+ let q = n.parent;
92
+ let fileScope = true;
93
+ let hops = 0;
94
+ while (q && hops++ < 40) {
95
+ if (BARRIERS.has(q.type)) {
96
+ fileScope = false;
97
+ break;
98
+ }
99
+ q = q.parent;
100
+ }
101
+ if (fileScope) out.add(stripZigQuoting(c0.text));
102
+ }
103
+ }
104
+ for (let i = 0; i < n.namedChildCount; i++) {
105
+ const c = n.namedChild(i);
106
+ if (c) stack.push(c);
107
+ }
108
+ }
109
+ ZIG_FILE_SCOPE_NAMES.set(root, out);
110
+ return out;
111
+ }
112
+ var ZIG_IMPORT_ALIASES = /* @__PURE__ */ new WeakMap();
113
+ function zigImportAliases(root) {
114
+ const cached = ZIG_IMPORT_ALIASES.get(root);
115
+ if (cached) return cached;
116
+ const imports = /* @__PURE__ */ new Map();
117
+ const shadowed = /* @__PURE__ */ new Set();
118
+ const stack = [root];
119
+ let guard = 0;
120
+ while (stack.length > 0 && guard++ < 4e5) {
121
+ const n = stack.pop();
122
+ if (n.type === "variable_declaration") {
123
+ const kids = zigNamedChildren(n);
124
+ const nameNode = kids.length > 0 && kids[0].type === "identifier" ? kids[0] : null;
125
+ const rhs = kids.length > 0 ? kids[kids.length - 1] : null;
126
+ const bound = nameNode ? stripZigQuoting(nameNode.text) : "";
127
+ if (bound) {
128
+ const lit = zigImportModuleLiteral(rhs);
129
+ if (lit) imports.set(bound, { module: lit.module, external: zigModuleIsExternal(lit.module) });
130
+ else shadowed.add(bound);
131
+ }
132
+ }
133
+ for (let i = 0; i < n.namedChildCount; i++) {
134
+ const c = n.namedChild(i);
135
+ if (c) stack.push(c);
136
+ }
137
+ }
138
+ for (const s of shadowed) imports.delete(s);
139
+ ZIG_IMPORT_ALIASES.set(root, imports);
140
+ return imports;
141
+ }
142
+ function zigModuleIsExternal(modulePath) {
143
+ if (modulePath.startsWith(".") || modulePath.startsWith("/")) return false;
144
+ if (modulePath.includes("/") || modulePath.includes("\\")) return false;
145
+ if (modulePath.endsWith(".zig") || modulePath.endsWith(".zon")) return false;
146
+ return true;
147
+ }
148
+ function zigImportModuleLiteral(rhs) {
149
+ if (!rhs) return null;
150
+ const host = rhs.type === "call_expression" || rhs.type === "builtin_function" ? rhs : null;
151
+ if (!host) return null;
152
+ let isImport = false;
153
+ for (let i = 0; i < host.namedChildCount; i++) {
154
+ const c = host.namedChild(i);
155
+ if (c && (c.type === "builtin_identifier" || c.type === "identifier")) {
156
+ const t = String(c.text ?? "");
157
+ if (t === "@import" || t === "@cImport" || t === "import") {
158
+ isImport = true;
159
+ break;
160
+ }
161
+ }
162
+ }
163
+ if (!isImport) return null;
164
+ const firstString = (h) => {
165
+ if (!h) return null;
166
+ for (let i = 0; i < h.namedChildCount; i++) {
167
+ const c = h.namedChild(i);
168
+ if (c && (c.type === "string" || c.type === "string_content")) return c;
169
+ }
170
+ return null;
171
+ };
172
+ let args = null;
173
+ for (let i = 0; i < host.namedChildCount; i++) {
174
+ const c = host.namedChild(i);
175
+ if (c && c.type === "arguments") {
176
+ args = c;
177
+ break;
178
+ }
179
+ }
180
+ let strNode = firstString(host) ?? firstString(args);
181
+ if (strNode && strNode.type === "string") {
182
+ for (let i = 0; i < strNode.namedChildCount; i++) {
183
+ const sc = strNode.namedChild(i);
184
+ if (sc && sc.type === "string_content") {
185
+ strNode = sc;
186
+ break;
187
+ }
188
+ }
189
+ }
190
+ if (!strNode) return null;
191
+ let out = String(strNode.text ?? "");
192
+ if (out.startsWith('"') && out.endsWith('"') || out.startsWith("'") && out.endsWith("'")) out = out.substring(1, out.length - 1);
193
+ return out.length > 0 ? { module: out, node: strNode } : null;
194
+ }
195
+ function zigNamedChildren(n) {
196
+ const out = [];
197
+ for (let i = 0; i < n.namedChildCount; i++) {
198
+ const c = n.namedChild(i);
199
+ if (c) out.push(c);
200
+ }
201
+ return out;
202
+ }
203
+ function zigModuleSymbol(specifier) {
204
+ const slash = specifier.lastIndexOf("/");
205
+ const base = slash >= 0 ? specifier.slice(slash + 1) : specifier;
206
+ const dot = base.lastIndexOf(".");
207
+ const stem = dot > 0 ? base.slice(0, dot) : base;
208
+ return stem.trim();
209
+ }
210
+ function zigImportModuleInSubtree(node) {
211
+ let found = null;
212
+ const walk = (n) => {
213
+ if (found !== null) return;
214
+ if (n.type === "builtin_function") {
215
+ const lit = zigImportModuleLiteral(n);
216
+ if (lit) {
217
+ found = lit.module;
218
+ return;
219
+ }
220
+ }
221
+ for (let i = 0; i < n.namedChildCount; i++) {
222
+ const c = n.namedChild(i);
223
+ if (c) walk(c);
224
+ }
225
+ };
226
+ walk(node);
227
+ return found;
228
+ }
229
+ function zigMergedMemberLeaf(expr) {
230
+ if (expr.type === "identifier") return stripZigQuoting(String(expr.text ?? ""));
231
+ if (expr.type === "field_expression") {
232
+ const last = expr.namedChildCount > 0 ? expr.namedChild(expr.namedChildCount - 1) : null;
233
+ return last ? zigMergedMemberLeaf(last) : null;
234
+ }
235
+ if (expr.type === "parenthesized_expression") {
236
+ for (let i = expr.namedChildCount - 1; i >= 0; i--) {
237
+ const c = expr.namedChild(i);
238
+ const leaf = c ? zigMergedMemberLeaf(c) : null;
239
+ if (leaf) return leaf;
240
+ }
241
+ }
242
+ return null;
243
+ }
244
+ function zigUsingNamespaceMergedUnit(node) {
245
+ let arg = null;
246
+ for (let i = 0; i < node.namedChildCount; i++) {
247
+ const c = node.namedChild(i);
248
+ if (c && c.type !== "comment") {
249
+ arg = c;
250
+ break;
251
+ }
252
+ }
253
+ if (!arg) return null;
254
+ if (arg.type === "builtin_function") {
255
+ let builtin = "";
256
+ for (let i = 0; i < arg.namedChildCount; i++) {
257
+ const c = arg.namedChild(i);
258
+ if (c && c.type === "builtin_identifier") {
259
+ builtin = String(c.text ?? "");
260
+ break;
261
+ }
262
+ }
263
+ if (builtin !== "@import") return null;
264
+ const lit = zigImportModuleLiteral(arg);
265
+ if (!lit) return null;
266
+ const target2 = zigModuleSymbol(lit.module);
267
+ return target2 ? { target: target2, form: "import", module: lit.module } : null;
268
+ }
269
+ if (arg.type === "identifier") {
270
+ const target2 = stripZigQuoting(String(arg.text ?? ""));
271
+ return target2 ? { target: target2, form: "identifier" } : null;
272
+ }
273
+ const target = zigMergedMemberLeaf(arg);
274
+ if (!target) return null;
275
+ const module = zigImportModuleInSubtree(arg);
276
+ return { target, form: module ? "import_member" : "identifier", ...module ? { module } : {} };
277
+ }
278
+ var ZIG_DECLARED_TYPE_BUILTINS = /* @__PURE__ */ new Set([
279
+ "void",
280
+ "bool",
281
+ "f16",
282
+ "f32",
283
+ "f64",
284
+ "f80",
285
+ "f128",
286
+ "c_longdouble",
287
+ "i8",
288
+ "i16",
289
+ "i32",
290
+ "i64",
291
+ "i128",
292
+ "isize",
293
+ "u8",
294
+ "u16",
295
+ "u32",
296
+ "u64",
297
+ "u128",
298
+ "usize",
299
+ "anytype",
300
+ "comptime_int",
301
+ "comptime_float",
302
+ "noreturn",
303
+ "type",
304
+ "anyerror",
305
+ "c_char",
306
+ "c_short",
307
+ "c_int",
308
+ "c_long",
309
+ "c_longlong",
310
+ "c_uint",
311
+ "c_ulong",
312
+ "c_ulonglong",
313
+ "c_void",
314
+ "error",
315
+ "test"
316
+ ]);
317
+ function stripZigQuoting(text) {
318
+ if (!text.includes('@"')) return text;
319
+ let out = "";
320
+ let i = 0;
321
+ while (i < text.length) {
322
+ if (text.charCodeAt(i) === 64 && text[i + 1] === '"') {
323
+ const close = text.indexOf('"', i + 2);
324
+ if (close === -1) {
325
+ out += text.slice(i);
326
+ break;
327
+ }
328
+ out += text.slice(i + 2, close);
329
+ i = close + 1;
330
+ } else {
331
+ out += text[i];
332
+ i++;
333
+ }
334
+ }
335
+ return out;
336
+ }
337
+ function ownerPathOf(container) {
338
+ if (!container) return void 0;
339
+ return container.zigOwnerPath ?? container.name;
340
+ }
341
+ function publishedSpelling(s) {
342
+ const name = String(s.name ?? "");
343
+ const owner = s.ownerName;
344
+ return owner ? `${owner}.${name}` : name;
345
+ }
346
+ var RealZigAnalyzer = class {
347
+ analyze(rootNode, content, filePath, context) {
348
+ const symbols = [];
349
+ const relationships = [];
350
+ const imports = [];
351
+ const exports = [];
352
+ const errors = [];
353
+ const projectRoot = typeof context?.projectRoot === "string" ? context.projectRoot : void 0;
354
+ const walkStack = [
355
+ { node: rootNode, container: null, inFunction: false, anonContainer: false }
356
+ ];
357
+ const fileContainerName = this.zigThisBindingName(rootNode);
358
+ while (walkStack.length > 0) {
359
+ const item = walkStack.pop();
360
+ const { node, container, inFunction, anonContainer } = item;
361
+ const type = node.type;
362
+ const nodeText = this.nodeText(node);
363
+ if (type === "variable_declaration") {
364
+ if (inFunction || anonContainer) {
365
+ const lchildren = this.getAllNamedChildren(node);
366
+ const lrhs = lchildren[lchildren.length - 1];
367
+ const lrhsType = lrhs?.type || "";
368
+ const lbuiltin = lrhsType === "call_expression" || lrhsType === "builtin_function" ? this.anyChild(lrhs, (n) => n.type === "builtin_identifier") : null;
369
+ if (lbuiltin && (lbuiltin.text === "@import" || lbuiltin.text === "@cImport")) {
370
+ const largs = this.anyChild(lrhs, (n) => n.type === "arguments");
371
+ const lstr = largs ? this.anyChild(largs, (n) => n.type === "string" || n.type === "string_content") : null;
372
+ if (lstr) {
373
+ let ltarget = lstr.text;
374
+ if (ltarget.startsWith('"') && ltarget.endsWith('"') || ltarget.startsWith("'") && ltarget.endsWith("'")) {
375
+ ltarget = ltarget.substring(1, ltarget.length - 1);
376
+ }
377
+ imports.push({
378
+ module: ltarget,
379
+ isDefaultImport: false,
380
+ range: this.nodeRange(node),
381
+ metadata: { zigImport: true, builtin: lbuiltin.text }
382
+ });
383
+ relationships.push({
384
+ source: "(global)",
385
+ target: ltarget,
386
+ type: ASTEngine.RelationshipType.References,
387
+ confidence: 1,
388
+ // M59.A7 (#825): an import dependency occurs where its TARGET is
389
+ // written — the path literal, not the declaration statement.
390
+ ...this.nodeLine(lstr) ? { line_number: this.nodeLine(lstr) } : {},
391
+ metadata: { relationshipType: "zig_import", isImport: true }
392
+ });
393
+ }
394
+ }
395
+ const localKids = this.getAllNamedChildren(node);
396
+ const localRhs = localKids[localKids.length - 1];
397
+ this.pushChildren(
398
+ node,
399
+ walkStack,
400
+ container,
401
+ inFunction,
402
+ anonContainer || !!localRhs && ANONYMOUS_CONTAINER_NODES.has(localRhs.type)
403
+ );
404
+ continue;
405
+ }
406
+ const identNode = this.anyChild(node, (n) => n.type === "identifier");
407
+ const varName = identNode ? identNode.text : "";
408
+ if (!varName || varName.length > 80) {
409
+ this.pushChildren(node, walkStack, container, inFunction, anonContainer);
410
+ continue;
411
+ }
412
+ if (varName === "_") {
413
+ this.pushChildren(node, walkStack, container, inFunction, anonContainer);
414
+ continue;
415
+ }
416
+ const isExported = this.hasToken(node, "pub") || this.hasToken(node, "export");
417
+ const children = this.getAllNamedChildren(node);
418
+ const rhs = children[children.length - 1];
419
+ const rhsType = rhs?.type || "";
420
+ const rhsText = rhs ? this.nodeText(rhs) : "";
421
+ const builtin = rhsType === "call_expression" || rhsType === "builtin_function" ? this.anyChild(rhs, (n) => n.type === "builtin_identifier") : null;
422
+ if (builtin && (builtin.text === "@import" || builtin.text === "@cImport")) {
423
+ const argsNode = this.anyChild(rhs, (n) => n.type === "arguments");
424
+ const strNode = argsNode ? this.anyChild(argsNode, (n) => n.type === "string" || n.type === "string_content") : null;
425
+ if (strNode) {
426
+ let importTarget = strNode.text;
427
+ if (importTarget.startsWith('"') && importTarget.endsWith('"') || importTarget.startsWith("'") && importTarget.endsWith("'")) {
428
+ importTarget = importTarget.substring(1, importTarget.length - 1);
429
+ }
430
+ if (!importTarget) {
431
+ this.pushChildren(node, walkStack, container, inFunction, anonContainer);
432
+ continue;
433
+ }
434
+ imports.push({
435
+ module: importTarget,
436
+ isDefaultImport: false,
437
+ range: this.nodeRange(node),
438
+ metadata: { zigImport: true, builtin: builtin.text }
439
+ });
440
+ relationships.push({
441
+ source: "(global)",
442
+ target: importTarget,
443
+ type: ASTEngine.RelationshipType.References,
444
+ confidence: 1,
445
+ // M59.A7 (#825): the line of the module path as written.
446
+ ...this.nodeLine(strNode) ? { line_number: this.nodeLine(strNode) } : {},
447
+ metadata: { relationshipType: "zig_import", isImport: true }
448
+ });
449
+ }
450
+ if (inFunction) {
451
+ this.pushChildren(node, walkStack, container, inFunction, anonContainer);
452
+ continue;
453
+ }
454
+ }
455
+ const isVar = this.hasToken(node, "var");
456
+ let kind = isVar ? ASTEngine.SymbolKind.Variable : ASTEngine.SymbolKind.Constant;
457
+ const isContainer = rhsType === "struct_declaration" || rhsType === "enum_declaration" || rhsType === "union_declaration" || rhsType === "error_set_declaration" || rhsType === "opaque_declaration";
458
+ if (rhsType === "struct_declaration") kind = ASTEngine.SymbolKind.Struct;
459
+ else if (rhsType === "enum_declaration") kind = ASTEngine.SymbolKind.Enum;
460
+ else if (rhsType === "union_declaration") kind = ASTEngine.SymbolKind.Union;
461
+ else if (rhsType === "error_set_declaration") kind = ASTEngine.SymbolKind.Enum;
462
+ else if (rhsType === "opaque_declaration") kind = ASTEngine.SymbolKind.Type;
463
+ else if (ZIG_TYPE_RHS.has(rhsType)) {
464
+ kind = ASTEngine.SymbolKind.Type;
465
+ } else if (ZIG_VALUE_RHS.has(rhsType) || rhsType === "labeled_type_expression" && this.isLabeledBlockValue(rhs)) {
466
+ if (!isVar) kind = ASTEngine.SymbolKind.Constant;
467
+ } else if (rhsType === "struct_initializer" && !this.isTypeValuedInitializer(rhs)) {
468
+ if (!isVar) kind = ASTEngine.SymbolKind.Constant;
469
+ } else if (varName[0] === varName[0].toUpperCase() && varName[0] !== varName[0].toLowerCase()) {
470
+ kind = ASTEngine.SymbolKind.Type;
471
+ }
472
+ const sym = {
473
+ name: varName,
474
+ kind,
475
+ visibility: isExported ? "public" : "private",
476
+ range: this.nodeRange(node),
477
+ scope: ownerPathOf(container),
478
+ ownerName: ownerPathOf(container),
479
+ ownerKind: container?.kind,
480
+ metadata: {
481
+ zigDeclaration: kind,
482
+ ...rhsType === "error_set_declaration" ? { zigErrorSet: true } : {},
483
+ // Z35: a NAMESPACE IMPORT BINDING is a distinct fact from any other
484
+ // const, and a consumer asking "which names in this file are modules"
485
+ // cannot ask without it. It is also what makes the regression
486
+ // reconstructible by a MARKER rather than by a list of names.
487
+ ...this.isZigImportBinding(rhs) ? { zigImportBinding: true } : {}
488
+ }
489
+ };
490
+ symbols.push(sym);
491
+ if (isExported && !container) exports.push({ name: varName, kind, range: this.nodeRange(node) });
492
+ if (isContainer) {
493
+ const own = stripZigQuoting(varName);
494
+ const parentPath = ownerPathOf(container);
495
+ sym.zigOwnerPath = parentPath ? `${parentPath}.${own}` : own;
496
+ }
497
+ const newContainer = isContainer ? sym : container;
498
+ this.pushChildren(node, walkStack, newContainer, inFunction, anonContainer);
499
+ continue;
500
+ }
501
+ if (type === "test_declaration" || type === "comptime_declaration") {
502
+ this.pushChildren(node, walkStack, container, true, anonContainer);
503
+ continue;
504
+ }
505
+ if (type === "using_namespace_declaration") {
506
+ {
507
+ const merged = zigUsingNamespaceMergedUnit(node);
508
+ const mixinSource = !inFunction && !anonContainer ? container ? container.name : "(global)" : null;
509
+ if (merged && mixinSource) {
510
+ relationships.push({
511
+ source: mixinSource,
512
+ target: merged.target,
513
+ type: ASTEngine.RelationshipType.MixesIn,
514
+ confidence: 1,
515
+ ...this.nodeLine(node) ? { line_number: this.nodeLine(node) } : {},
516
+ metadata: {
517
+ relationshipType: "zig_usingnamespace",
518
+ isMixin: true,
519
+ mixinForm: merged.form,
520
+ ...merged.module ? { module: merged.module } : {},
521
+ ...mixinSource === "(global)" ? { fileLevel: true } : {}
522
+ }
523
+ });
524
+ }
525
+ }
526
+ const bf = this.anyChild(node, (n) => n.type === "builtin_function");
527
+ const bi = bf ? this.anyChild(bf, (n) => n.type === "builtin_identifier") : null;
528
+ if (bf && bi && (bi.text === "@import" || bi.text === "@cImport")) {
529
+ const argsNode = this.anyChild(bf, (n) => n.type === "arguments");
530
+ const strNode = argsNode ? this.anyChild(argsNode, (n) => n.type === "string" || n.type === "string_content") : null;
531
+ if (strNode) {
532
+ let importTarget = strNode.text;
533
+ if (importTarget.startsWith('"') && importTarget.endsWith('"') || importTarget.startsWith("'") && importTarget.endsWith("'")) {
534
+ importTarget = importTarget.substring(1, importTarget.length - 1);
535
+ }
536
+ if (!importTarget) {
537
+ relationships.push({
538
+ source: "(global)",
539
+ target: "@import",
540
+ type: ASTEngine.RelationshipType.References,
541
+ confidence: 1,
542
+ metadata: { relationshipType: "zig_import", isImport: true, emptyImportPath: true }
543
+ });
544
+ continue;
545
+ }
546
+ imports.push({
547
+ module: importTarget,
548
+ isDefaultImport: false,
549
+ range: this.nodeRange(node),
550
+ metadata: { zigImport: true, builtin: bi.text, usingnamespace: true }
551
+ });
552
+ relationships.push({
553
+ source: "(global)",
554
+ target: importTarget,
555
+ type: ASTEngine.RelationshipType.References,
556
+ confidence: 1,
557
+ // M59.A7 (#825): the module path as written.
558
+ ...this.nodeLine(strNode) ? { line_number: this.nodeLine(strNode) } : {},
559
+ metadata: { relationshipType: "zig_import", isImport: true }
560
+ });
561
+ }
562
+ }
563
+ this.pushChildren(node, walkStack, container, inFunction, anonContainer);
564
+ continue;
565
+ }
566
+ if (type === "function_signature") {
567
+ this.pushChildren(node, walkStack, container, inFunction, anonContainer);
568
+ continue;
569
+ }
570
+ if (type === "function_declaration") {
571
+ const firstNamed = node.namedChildCount > 0 ? node.namedChild(0) : null;
572
+ const identNode = firstNamed && firstNamed.type === "identifier" ? firstNamed : this.anyChild(node, (n) => n.type === "identifier");
573
+ const fnName = identNode ? identNode.text : "";
574
+ const isExported = this.hasToken(node, "pub") || this.hasToken(node, "export");
575
+ const isInline = this.hasToken(node, "inline");
576
+ const isAnonymousContainerMember = anonContainer || !container && inFunction;
577
+ if (fnName && fnName.length < 100 && !isAnonymousContainerMember) {
578
+ const params = [];
579
+ const paramsNode = this.anyChild(node, (n) => n.type === "parameters");
580
+ if (paramsNode) {
581
+ for (let i = 0; i < paramsNode.namedChildCount; i++) {
582
+ const child = paramsNode.namedChild(i);
583
+ if (!child || child.type !== "parameter") continue;
584
+ const pNameNode = this.namedFieldOf(child, "name") ?? this.anyChild(child, (n) => n.type === "identifier");
585
+ const pTypeNode = this.typeFieldOf(child);
586
+ if (pNameNode) {
587
+ params.push({
588
+ name: pNameNode.text,
589
+ type: pTypeNode ? this.nodeText(pTypeNode) : void 0
590
+ });
591
+ }
592
+ }
593
+ }
594
+ const isMethod = container !== null;
595
+ const sym = {
596
+ name: fnName,
597
+ kind: isMethod ? ASTEngine.SymbolKind.Method : ASTEngine.SymbolKind.Function,
598
+ visibility: isExported ? "public" : "private",
599
+ range: this.nodeRange(node),
600
+ scope: ownerPathOf(container),
601
+ parameters: params,
602
+ ownerName: ownerPathOf(container),
603
+ ownerKind: container?.kind,
604
+ // Per-symbol cyclomatic complexity (family standard — go/c/rust
605
+ // emit symbol.complexity; zig did NOT, so completeness section C
606
+ // was impossible). Added zig wave 2026-08-29.
607
+ complexity: this.zigBodyComplexity(node),
608
+ metadata: {
609
+ zigFunction: true,
610
+ inline: isInline,
611
+ paramCount: params.length,
612
+ // #612 store-phase: DECLARATION vs DEFINITION. Measured against
613
+ // this grammar (tree dump, 2026-09-25): `extern fn run_engine()
614
+ // c_int;` and `pub fn call_it() c_int { … }` BOTH parse as
615
+ // `function_declaration`, and the grammar hands the difference
616
+ // over as a FIELD — 35,692 of the corpus's 37,129 functions carry
617
+ // a `body` child under the `body` field name, the rest declare
618
+ // nothing. So the discriminator is read off the field, with the
619
+ // node-type test kept as the fallback for a grammar variant that
620
+ // does not name it. Without the flag an `extern` declaration
621
+ // competed on equal footing with the definition implementing it in
622
+ // another file.
623
+ ...this.functionBody(node) === null ? { isDeclaration: true } : {}
624
+ }
625
+ };
626
+ symbols.push(sym);
627
+ if (isExported && !isMethod) exports.push({ name: fnName, kind: ASTEngine.SymbolKind.Function, range: this.nodeRange(node) });
628
+ }
629
+ this.pushChildren(node, walkStack, container, true, anonContainer);
630
+ continue;
631
+ }
632
+ if (ANONYMOUS_CONTAINER_NODES.has(type)) {
633
+ const parent = node.parent;
634
+ const isDeclRhs = !!parent && parent.type === "variable_declaration" && parent.namedChildCount > 0 && parent.namedChild(parent.namedChildCount - 1)?.id === node.id;
635
+ if (!isDeclRhs) {
636
+ this.pushChildren(node, walkStack, container, inFunction, true);
637
+ continue;
638
+ }
639
+ }
640
+ if (type === "container_field") {
641
+ if (!container && !anonContainer && node.parent?.type === "source_file") {
642
+ this.zigFileLevelField(node, fileContainerName, symbols);
643
+ this.pushChildren(node, walkStack, container, inFunction, anonContainer);
644
+ continue;
645
+ }
646
+ if (!container || anonContainer) {
647
+ this.pushChildren(node, walkStack, container, inFunction, anonContainer);
648
+ continue;
649
+ }
650
+ const nameField = node.childForFieldName?.("name");
651
+ const isEnumMember = container?.kind === ASTEngine.SymbolKind.Enum;
652
+ const enumPrimitiveName = isEnumMember && nameField && ENUM_MEMBER_NAME_NODE_TYPES.has(nameField.type) ? nameField : null;
653
+ const nameNode = nameField && nameField.type === "identifier" ? nameField : enumPrimitiveName ?? this.anyChild(node, (n) => n.type === "identifier");
654
+ const fieldName = nameNode ? nameNode.text : "";
655
+ if (fieldName && fieldName !== "_" && fieldName.length < 200) {
656
+ const typeField = this.typeFieldOf(node);
657
+ symbols.push({
658
+ name: fieldName,
659
+ kind: isEnumMember ? ASTEngine.SymbolKind.EnumMember : ASTEngine.SymbolKind.Property,
660
+ visibility: "public",
661
+ range: this.nodeRange(node),
662
+ scope: ownerPathOf(container),
663
+ ownerName: ownerPathOf(container),
664
+ ownerKind: container?.kind,
665
+ returnType: typeField ? this.nodeText(typeField) : void 0,
666
+ metadata: { zigField: true, ...isEnumMember ? { zigEnumMember: true } : {} }
667
+ });
668
+ }
669
+ this.pushChildren(node, walkStack, container, inFunction, anonContainer);
670
+ continue;
671
+ }
672
+ if (type === "identifier" && container && container.metadata?.zigErrorSet && node.parent?.type === "error_set_declaration") {
673
+ const identText = node.text;
674
+ if (identText && identText.length < 50) {
675
+ symbols.push({
676
+ name: identText,
677
+ kind: ASTEngine.SymbolKind.EnumMember,
678
+ visibility: "public",
679
+ range: this.nodeRange(node),
680
+ scope: ownerPathOf(container),
681
+ ownerName: ownerPathOf(container),
682
+ ownerKind: container.kind,
683
+ metadata: { zigErrorSetMember: true }
684
+ });
685
+ }
686
+ continue;
687
+ }
688
+ if (node.type === "ERROR" || node.isMissing) {
689
+ const snippet = nodeText.substring(0, 50);
690
+ errors.push({
691
+ message: `Zig syntax error: ${snippet}`,
692
+ range: {
693
+ // Z2 (zig review): 1-based columns like every other range in
694
+ // this file (nodeRange +1s) — the raw 0-based columns leaked
695
+ // through here (svelte-wave class).
696
+ startLine: node.startPosition.row + 1,
697
+ startColumn: node.startPosition.column + 1,
698
+ endLine: node.endPosition.row + 1,
699
+ endColumn: node.endPosition.column + 1,
700
+ startIndex: node.startIndex,
701
+ endIndex: node.endIndex
702
+ },
703
+ severity: "error"
704
+ });
705
+ }
706
+ this.pushChildren(node, walkStack, container, inFunction || type === "block", anonContainer);
707
+ }
708
+ for (const s of symbols) {
709
+ if (s.ownerName) {
710
+ relationships.push({
711
+ source: s.ownerName,
712
+ // M60 cause A — the target is the member's own PUBLISHED SPELLING, which
713
+ // is what Z-p22 above already established for the SOURCE and this edge
714
+ // never followed on the target half.
715
+ //
716
+ // The projection spells a symbol `scope ? scope + "." + name : name`
717
+ // (pool-worker.ts:997) and `scope` is this symbol's `ownerName`
718
+ // (see `mkSymbol`'s `scope: ownerName ?? undefined`). So the string this
719
+ // edge targets IS the member's published `qualified_name`, and the store
720
+ // resolves it on its FIRST strategy: `target_external = qualified_name`
721
+ // is an exact JOIN (parallel-indexer.ts:4483-4495, `_res_qname` PRIMARY
722
+ // KEY), which binds one declaration by construction.
723
+ //
724
+ // A BARE member name reaches only strategy 2, which fires
725
+ // `_res_name.cnt = 1` — the name must be unique across the WHOLE project
726
+ // (parallel-indexer.ts:4506). No container member name ever is: the
727
+ // fleet's own corpus declares `init`, `deinit`, `assert` and `release`
728
+ // in dozens of files each. So the bare target was not merely imprecise,
729
+ // it was the one spelling guaranteed to fail the only strategy that
730
+ // could have carried it.
731
+ //
732
+ // MEASURED, 600-file stride sample, production `parseFile`, incoming-gate
733
+ // (which counts ambiguity by DECLARATION and indexes name + qualifiedName
734
+ // aliases): `contains` 13,690 edges — 8,993 resolved, 4,697 AMBIGUOUS,
735
+ // 0 unresolved. That 4,697 was the largest single ambiguous bucket in the
736
+ // lane, 24.6% of the 19,084 total. Priced over the FULL population first
737
+ // (3,834 files, 80,876 contains edges, `contains-qualify-control.ts`):
738
+ // FIX 31,800 / RISK 5 / LOST 797 / NEUTRAL 48,274 — i.e. +31,800 edges
739
+ // that resolve to exactly one declaration against 5 that stop doing so.
740
+ //
741
+ // The name SURFACE is untouched: `symbols.push({ name: s.name })` still
742
+ // publishes the bare member, so hover, navigation and every expectation
743
+ // file that reads `name` are unaffected. Only the edge endpoint changes.
744
+ // `source` still carries the owner, so containment survives independently
745
+ // of the target — the two halves are redundant by design, and this makes
746
+ // them agree.
747
+ target: publishedSpelling(s),
748
+ type: ASTEngine.RelationshipType.Contains,
749
+ confidence: 1,
750
+ // M59.A7 (#825): a containment edge has no call site, but it has a
751
+ // navigable one — the member's own declaration line. Omitted, never
752
+ // written as 0, when the symbol carries no range (#820).
753
+ ...s.range?.startLine ? { line_number: s.range.startLine } : {},
754
+ // Z44: a reason code, so every class in this analyzer answers the same
755
+ // question — "why does this edge exist?" — the same way. `contains` is
756
+ // built from the SYMBOL TABLE at the end of the walk rather than at the
757
+ // member's site, which is why it carried an EMPTY metadata object and
758
+ // was invisible to any consumer that reads the reason. It is the
759
+ // largest class in the corpus (261 of the 300-file draw) and it was the
760
+ // one that said nothing. `zigField`/`zigEnumMember` are already on the
761
+ // SYMBOL; here the reason is that the edge is ownership.
762
+ metadata: { relationshipType: "zig_ownership" }
763
+ });
764
+ }
765
+ }
766
+ this.analyzeZigCodeRelationships(rootNode, relationships, symbols);
767
+ this.collectZigImportSites(rootNode, imports, relationships, projectRoot, filePath);
768
+ this.analyzeZigInstantiation(rootNode, relationships);
769
+ this.emitZigExportEdges(relationships, exports);
770
+ this.analyzeZigBareIdentifierUses(rootNode, relationships, symbols);
771
+ {
772
+ const bestUses = /* @__PURE__ */ new Map();
773
+ const order = [];
774
+ for (const r of relationships) {
775
+ if (r.type !== ASTEngine.RelationshipType.Uses) {
776
+ order.push(r);
777
+ continue;
778
+ }
779
+ const key = `${r.source}|${r.target}`;
780
+ const prior = bestUses.get(key);
781
+ if (!prior) {
782
+ bestUses.set(key, r);
783
+ order.push(r);
784
+ } else if ((r.confidence ?? 0) > (prior.confidence ?? 0)) {
785
+ order[order.indexOf(prior)] = r;
786
+ bestUses.set(key, r);
787
+ }
788
+ }
789
+ relationships.length = 0;
790
+ relationships.push(...order);
791
+ }
792
+ this.extractZigDeclaredTypeRefs(rootNode, relationships);
793
+ this.classifyEvidenceTier(relationships);
794
+ this.annotateTargetScope(symbols, relationships);
795
+ this.canonicalizePublishedNames(symbols, relationships, exports);
796
+ const fileComplexity = this.zigBodyComplexity(rootNode);
797
+ const zigDeclaredHere = /* @__PURE__ */ new Set();
798
+ for (const sym of symbols) {
799
+ if (sym.name) zigDeclaredHere.add(sym.name);
800
+ const q = sym.qualifiedName;
801
+ if (typeof q === "string" && q.length > 0) zigDeclaredHere.add(q);
802
+ }
803
+ let zigNamespaceClaims = 0;
804
+ for (const rel of relationships) {
805
+ if (rel.type !== ASTEngine.RelationshipType.Uses) continue;
806
+ const tgt = String(rel.target ?? "");
807
+ if (!tgt.startsWith("std.")) continue;
808
+ if (zigDeclaredHere.has(tgt)) continue;
809
+ const md = rel.metadata;
810
+ if (!md || typeof md !== "object") continue;
811
+ if (md.zigExternalRef === true || md.isExternal === true) continue;
812
+ md.languageNamespace = ["std"];
813
+ zigNamespaceClaims++;
814
+ }
815
+ return {
816
+ language: "zig",
817
+ symbols,
818
+ relationships,
819
+ imports,
820
+ exports,
821
+ errors,
822
+ metadata: {
823
+ namespaceClaims: zigNamespaceClaims,
824
+ complexity: fileComplexity,
825
+ cyclomaticComplexity: fileComplexity,
826
+ linesOfCode: content.split("\n").length,
827
+ patterns: [],
828
+ framework: "Zig",
829
+ hasFrameworkIntelligence: true,
830
+ hasTypeInference: true
831
+ }
832
+ };
833
+ }
834
+ /**
835
+ * Calls + Uses (family contract, zig wave 2026-08-29).
836
+ * Walks the tree tracking the enclosing function/method name; a
837
+ * call_expression whose callee is an identifier or a field_expression emits
838
+ * a `calls` edge to the callee LEAF (Rust/Go contract — shared callee
839
+ * `self.repository.add` -> `add`); a field_expression that is NOT a call
840
+ * callee emits a `uses` edge to its field leaf. Builtin calls (@import,
841
+ * @intCast, ...) are compile-time intrinsics, NOT user symbols — skipped.
842
+ */
843
+ /**
844
+ * Z59 — does this analyzer publish a SYMBOL for `fnNode`, so that naming it as
845
+ * an edge SOURCE resolves?
846
+ *
847
+ * ## The defect this answers
848
+ *
849
+ * A `function_declaration` can be a MEMBER of a container the analyzer
850
+ * deliberately publishes nothing for: a container written inside a function or
851
+ * test body, a container with no name (`fn GenericStack(…) type { return
852
+ * struct { fn push(…) } }`), or a container nested in an anonymous one. That is
853
+ * the Z22 / C-negative contract and it is correct — such a type has no
854
+ * file-scope identity.
855
+ *
856
+ * The three edge passes each took the nearest `function_declaration` /
857
+ * `test_declaration` identifier as the source name WITHOUT asking whether a
858
+ * symbol was published for it. So 5.05% of `calls` + `uses` + `instantiates`
859
+ * edges over the full corpus (11,332 of 224,599) carried a source naming a
860
+ * declaration the index does not hold: a caller that resolves to nothing.
861
+ * Measured by `validation/m14/fixtures/zig/dangling-source-census.ts`, whose
862
+ * residual class is 0, so this is the whole population and not a sample of it.
863
+ *
864
+ * ## The rule
865
+ *
866
+ * A function is published when it has NO container ancestor (a top-level `fn`),
867
+ * or when its nearest container ancestor is bound to an identifier at FILE
868
+ * SCOPE. Named is not sufficient: `const S = struct { … }` inside a test body is
869
+ * a named LOCAL container and is not published either.
870
+ *
871
+ * `test_declaration` is answered separately by the callers: a named test is a
872
+ * scope with a name (Z39) and NO symbol is published for one, so that class is
873
+ * still dangling and is declared as a limitation rather than silently reverted.
874
+ */
875
+ zigFunctionScopeIsPublished(fnNode) {
876
+ let cur = fnNode.parent;
877
+ while (cur) {
878
+ if (ANONYMOUS_CONTAINER_NODES.has(cur.type)) {
879
+ const binding = cur.parent;
880
+ if (!binding || binding.type !== "variable_declaration") return false;
881
+ let named = false;
882
+ for (let i = 0; i < binding.namedChildCount; i++) {
883
+ const c = binding.namedChild(i);
884
+ if (c && c.type === "identifier") {
885
+ named = true;
886
+ break;
887
+ }
888
+ }
889
+ if (!named) return false;
890
+ let up = binding.parent;
891
+ while (up) {
892
+ if (up.type === "function_declaration" || up.type === "test_declaration" || up.type === "comptime_declaration") return false;
893
+ if (ANONYMOUS_CONTAINER_NODES.has(up.type)) return false;
894
+ up = up.parent;
895
+ }
896
+ return true;
897
+ }
898
+ cur = cur.parent;
899
+ }
900
+ return true;
901
+ }
902
+ analyzeZigCodeRelationships(rootNode, relationships, symbols) {
903
+ const emittedCalls = /* @__PURE__ */ new Set();
904
+ const emittedUses = /* @__PURE__ */ new Set();
905
+ const importAliases = zigImportAliases(rootNode);
906
+ const stack = [{ node: rootNode, fn: null, inFn: false }];
907
+ while (stack.length > 0) {
908
+ const { node: n, fn, inFn } = stack.pop();
909
+ let nextFn = fn;
910
+ let nextInFn = inFn;
911
+ if (n.type === "function_declaration" || n.type === "test_declaration") {
912
+ const nameNode = this.anyChild(n, (c) => c.type === "identifier");
913
+ if (nameNode) {
914
+ nextInFn = true;
915
+ if (n.type === "test_declaration" || this.zigFunctionScopeIsPublished(n)) {
916
+ nextFn = nameNode.text;
917
+ }
918
+ }
919
+ }
920
+ if (n.type === "call_expression") {
921
+ const leafNode = this.zigCalleeLeafNode(n);
922
+ const leaf = leafNode?.text;
923
+ if (leaf) {
924
+ const source = nextInFn && nextFn ? nextFn : "(global)";
925
+ const calleeExpr = n.namedChildCount > 0 ? n.namedChild(0) : null;
926
+ const ext = this.zigExternalReceiverPath(
927
+ calleeExpr?.type === "field_expression" ? calleeExpr : void 0,
928
+ importAliases
929
+ );
930
+ const target = ext ? ext.target : leaf;
931
+ const k = `${source}->${target}`;
932
+ if (!emittedCalls.has(k)) {
933
+ emittedCalls.add(k);
934
+ relationships.push({
935
+ source,
936
+ target,
937
+ type: ASTEngine.RelationshipType.Calls,
938
+ confidence: 0.85,
939
+ // M59.A7 (#825): the callee LEAF as written — the name-bearing
940
+ // node, not the call expression (which a receiver can precede).
941
+ ...this.nodeLine(leafNode) ? { line_number: this.nodeLine(leafNode) } : {},
942
+ metadata: {
943
+ relationshipType: "zig_function_call",
944
+ callExpression: n.text.slice(0, 60),
945
+ ...this.zigExternalMetadata(ext)
946
+ }
947
+ });
948
+ }
949
+ }
950
+ }
951
+ if (n.type === "field_expression" && !this.isCallCallee(n)) {
952
+ const fieldNode = this.zigFieldLeafNode(n);
953
+ const field = fieldNode?.text;
954
+ const notBuiltinAccess = this.anyChild(n, (c) => c.type === "identifier") !== null;
955
+ const isAssignLhs = this.isWriteLhs(n);
956
+ const isEnumLiteral = n.namedChildCount < 2;
957
+ const p = n.parent;
958
+ const isTypePosition = (() => {
959
+ const VALUE_BOUNDARY = /* @__PURE__ */ new Set([
960
+ "call_expression",
961
+ "block",
962
+ "block_expression",
963
+ "arguments",
964
+ "assignment_expression",
965
+ "assignment_statement",
966
+ "compound_assignment_statement",
967
+ "struct_initializer",
968
+ "anonymous_struct_initializer",
969
+ "initializer_list",
970
+ "composite_literal",
971
+ "variable_declaration",
972
+ "return_expression",
973
+ "if_expression",
974
+ "switch_expression",
975
+ "while_expression",
976
+ "for_expression"
977
+ ]);
978
+ let cur = p;
979
+ let arrivedFrom = n;
980
+ let hops = 0;
981
+ while (cur && hops < 8) {
982
+ if (cur.type === "parameter" || cur.type === "container_field" || cur.type === "function_declaration" || cur.type === "function_signature") return true;
983
+ if (cur.type === "variable_declaration") {
984
+ return this.zigArrivedAtTypeAnnotation(cur, arrivedFrom);
985
+ }
986
+ if (VALUE_BOUNDARY.has(cur.type)) return false;
987
+ arrivedFrom = cur;
988
+ cur = cur.parent;
989
+ hops++;
990
+ }
991
+ return false;
992
+ })();
993
+ if (field && notBuiltinAccess && !isAssignLhs && !isEnumLiteral && !isTypePosition) {
994
+ const source = nextInFn && nextFn ? nextFn : "(global)";
995
+ const ext = this.zigExternalReceiverPath(n, importAliases);
996
+ const target = ext ? ext.target : field;
997
+ const k = `${source}->${target}`;
998
+ if (!emittedUses.has(k)) {
999
+ emittedUses.add(k);
1000
+ relationships.push({
1001
+ source,
1002
+ target,
1003
+ type: ASTEngine.RelationshipType.Uses,
1004
+ confidence: 0.75,
1005
+ // M59.A7 (#825): the field LEAF as written.
1006
+ ...this.nodeLine(fieldNode) ? { line_number: this.nodeLine(fieldNode) } : {},
1007
+ metadata: {
1008
+ relationshipType: "zig_field_access",
1009
+ fieldAccessExpression: n.text.slice(0, 60),
1010
+ ...this.zigExternalMetadata(ext)
1011
+ }
1012
+ });
1013
+ }
1014
+ }
1015
+ }
1016
+ const reflectedName = this.zigReflectionFieldName(n);
1017
+ if (reflectedName) {
1018
+ const source = nextInFn && nextFn ? nextFn : "(global)";
1019
+ const k = `${source}->${reflectedName.text}`;
1020
+ if (!emittedUses.has(k)) {
1021
+ emittedUses.add(k);
1022
+ relationships.push({
1023
+ source,
1024
+ target: reflectedName.text,
1025
+ type: ASTEngine.RelationshipType.Uses,
1026
+ confidence: 0.75,
1027
+ // M59.A7 (#825): the line of the NAME, matching `zig_field_access`.
1028
+ ...this.nodeLine(reflectedName) ? { line_number: this.nodeLine(reflectedName) } : {},
1029
+ metadata: {
1030
+ relationshipType: "zig_field_reflection",
1031
+ reflectedMember: reflectedName.text
1032
+ }
1033
+ });
1034
+ }
1035
+ }
1036
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
1037
+ const c = n.namedChild(i);
1038
+ if (c) stack.push({ node: c, fn: nextFn, inFn: nextInFn });
1039
+ }
1040
+ }
1041
+ }
1042
+ /**
1043
+ * Z3: bare-identifier value reads (`return MAX`, `g(D)`, `m[k]` keys,
1044
+ * return operands). Every bare `identifier` naming a file-visible VALUE
1045
+ * (file-scope const/var/fn/enum-member) is a Uses edge, with:
1046
+ * - a lexical scope stack: function params bind at entry; block locals
1047
+ * (variable_declaration names), payload captures (`|x|`), for/if/while
1048
+ * payloads bind in walk order (reads before any binding still resolve
1049
+ * outward — C-family flow discipline);
1050
+ * - declaration positions never read (first-identifier rule for
1051
+ * variable_declaration; entity names; composite-literal keys are
1052
+ * field_identifier-typed and auto-skip; labels/package/import paths/
1053
+ * blank `_` excluded);
1054
+ * - call callees (span-matched; the calls branch owns them), member
1055
+ * leaves (field path owns them; objects stay readable), qualified
1056
+ * non-leaves, assignment LHS writes, test/comptime-exempt contexts all
1057
+ * excluded;
1058
+ * - resolvability required: unknown names (std/dot-imported/external,
1059
+ * macro-template `$x`) stay silent — no junk edges.
1060
+ */
1061
+ /**
1062
+ * M60 (#924, zig half): canonicalize the PUBLISHED name surface.
1063
+ *
1064
+ * Zig builds its symbols inline (no factory), so the strip lands here, at the
1065
+ * publish boundary — after every analysis pass, so nothing internal ever sees a
1066
+ * canonical spelling mid-walk (the bare-use inventory and the shadowing frames
1067
+ * are keyed on the SOURCE's own spellings and stay consistent with the tree),
1068
+ * and before the result leaves the analyzer.
1069
+ */
1070
+ /** Z35: is this right-hand side a module import? The two shapes the import
1071
+ * branches recognise, in one place so three sites cannot disagree. */
1072
+ isZigImportBinding(rhs) {
1073
+ if (!rhs) return false;
1074
+ const host = rhs.type === "call_expression" || rhs.type === "builtin_function" ? rhs : null;
1075
+ if (!host) return false;
1076
+ for (let i = 0; i < host.namedChildCount; i++) {
1077
+ const c = host.namedChild(i);
1078
+ if (c && (c.type === "builtin_identifier" || c.type === "identifier")) {
1079
+ const t = String(c.text ?? "");
1080
+ return t === "@import" || t === "@cImport" || t === "import";
1081
+ }
1082
+ }
1083
+ return false;
1084
+ }
1085
+ /**
1086
+ * Z30: every `@import("path")` / `@cImport(…)` SITE, whether or not the import
1087
+ * is the whole right-hand side.
1088
+ *
1089
+ * The three shape branches each ask "is the RHS exactly an @import?", so
1090
+ * `const v = @import("build.zig.zon").version;` (a .zon manifest read),
1091
+ * `const c = @import("c.zig").c;` (a module's C bindings) and
1092
+ * `try @import("apple_sdk").addPaths(b, lib);` (a call ON a module, not even a
1093
+ * declaration) fell through all of them: no fact, no edge, no symbol — 460 of
1094
+ * 1,918 sites, 24% of the class. It is a SITE PASS rather than a fourth shape
1095
+ * test, which is the lesson of this wave written down: a rule that enumerates
1096
+ * the shapes it has seen keeps losing the one it has not.
1097
+ *
1098
+ * Measured shape: `builtin_function[builtin_identifier, arguments]`, the path in
1099
+ * `arguments[string[string_content]]`. `@cImport({ @cInclude(…) })` nests its
1100
+ * include strings and has no module path of its own, so it records the fact and
1101
+ * publishes no edge — the same asymmetry the shape branches have.
1102
+ *
1103
+ * Dedupe key `(line, target)` against what the branches already pushed, so a
1104
+ * site they handled is not recorded twice.
1105
+ */
1106
+ collectZigImportSites(rootNode, imports, relationships, projectRoot, filePath) {
1107
+ const stack = [rootNode];
1108
+ const seenFacts = /* @__PURE__ */ new Set();
1109
+ for (const f of imports) seenFacts.add(`${f.range?.startLine ?? 0}|${f.module ?? ""}`);
1110
+ const refEdgesAt = /* @__PURE__ */ new Map();
1111
+ const importEdgesAt = /* @__PURE__ */ new Map();
1112
+ for (const r of relationships) {
1113
+ if (r.metadata?.isImport && typeof r.line_number === "number") {
1114
+ const k = `${r.line_number}|${r.target}`;
1115
+ if (r.type === ASTEngine.RelationshipType.Imports) importEdgesAt.set(k, r);
1116
+ else refEdgesAt.set(k, r);
1117
+ }
1118
+ }
1119
+ const attachFileClaim = (edge, claim) => {
1120
+ if (claim === null) return;
1121
+ const md = edge.metadata ?? (edge.metadata = {});
1122
+ if (md.importProjectFile === void 0) md.importProjectFile = claim;
1123
+ };
1124
+ while (stack.length > 0) {
1125
+ const n = stack.pop();
1126
+ if (n.type === "builtin_function" || n.type === "call_expression") {
1127
+ let id = null;
1128
+ if (n.type === "builtin_function") {
1129
+ for (let i = 0; i < n.namedChildCount; i++) {
1130
+ const c = n.namedChild(i);
1131
+ if (c && (c.type === "builtin_identifier" || c.type === "identifier")) {
1132
+ id = c;
1133
+ break;
1134
+ }
1135
+ }
1136
+ } else {
1137
+ id = n.namedChildCount > 0 ? n.namedChild(0) : null;
1138
+ }
1139
+ const text = String(id?.text ?? "");
1140
+ if (text === "@import" || text === "@cImport" || text === "import") {
1141
+ const lit = zigImportModuleLiteral(n);
1142
+ if (lit) {
1143
+ const target = lit.module;
1144
+ const line = this.nodeLine(lit.node) ?? 0;
1145
+ const isUsingNamespace = n.parent?.type === "using_namespace_declaration";
1146
+ if (target && line > 0) {
1147
+ const k = `${line}|${target}`;
1148
+ const fileClaim = projectRoot === void 0 ? null : fileClaimForTarget({
1149
+ projectRoot,
1150
+ filePath,
1151
+ written: target,
1152
+ admittedExtensions: ZIG_ADMITTED_EXTENSIONS,
1153
+ // THE SIZE CLAUSE IS THE GUARD'S NOW (`ba04c6b4c`), not this lane's: a file over
1154
+ // the indexer's own limit is never admitted, so a claim naming it cannot verify
1155
+ // while a miss still REFUTES. Measured before it existed: adopting the claim
1156
+ // without it took whole-population `extFAIL` 0 -> 17, all of them oversized
1157
+ // `.zig` files, and all 15 of the corpus's `@import("….zig")` cap cases.
1158
+ maxFileSizeBytes: FILE_CLAIM_MAX_BYTES
1159
+ });
1160
+ if (!seenFacts.has(k)) {
1161
+ seenFacts.add(k);
1162
+ imports.push({
1163
+ module: target,
1164
+ isDefaultImport: false,
1165
+ range: this.nodeRange(n),
1166
+ metadata: { zigImport: true, builtin: text, ...isUsingNamespace ? { usingnamespace: true } : {} }
1167
+ });
1168
+ }
1169
+ const existingImport = importEdgesAt.get(k);
1170
+ if (existingImport) {
1171
+ attachFileClaim(existingImport, fileClaim);
1172
+ } else {
1173
+ const edge = {
1174
+ source: "(global)",
1175
+ target,
1176
+ type: ASTEngine.RelationshipType.Imports,
1177
+ confidence: 0.9,
1178
+ line_number: line,
1179
+ metadata: {
1180
+ relationshipType: "zig_import",
1181
+ isImport: true,
1182
+ zigImportDep: true,
1183
+ // THE FILE CLAIM, ONCE PER EDGE AND ON BOTH LABELS. See the block comment
1184
+ // above `collectZigImportSites`' call site for why it is on both.
1185
+ ...fileClaim ? { importProjectFile: fileClaim } : {}
1186
+ }
1187
+ };
1188
+ relationships.push(edge);
1189
+ importEdgesAt.set(k, edge);
1190
+ }
1191
+ const existingRef = refEdgesAt.get(k);
1192
+ if (existingRef) {
1193
+ attachFileClaim(existingRef, fileClaim);
1194
+ } else {
1195
+ const edge = {
1196
+ source: "(global)",
1197
+ target,
1198
+ type: ASTEngine.RelationshipType.References,
1199
+ confidence: 1,
1200
+ line_number: line,
1201
+ metadata: {
1202
+ relationshipType: "zig_import",
1203
+ isImport: true,
1204
+ ...fileClaim ? { importProjectFile: fileClaim } : {}
1205
+ }
1206
+ };
1207
+ relationships.push(edge);
1208
+ refEdgesAt.set(k, edge);
1209
+ }
1210
+ }
1211
+ }
1212
+ }
1213
+ }
1214
+ for (let i = 0; i < n.namedChildCount; i++) {
1215
+ const c = n.namedChild(i);
1216
+ if (c) stack.push(c);
1217
+ }
1218
+ }
1219
+ }
1220
+ /**
1221
+ * Z32: `instantiates` for the two statically derivable classes — a call to an
1222
+ * in-file `fn … type`, and a construction of an in-file type (`TypeName{ … }`,
1223
+ * bare or QUALIFIED: `Color.C{ .r = 1 }`). The inventory is the tree's own
1224
+ * declarations, so the class needs no inference. `.{ … }` is an
1225
+ * anonymous_struct_initializer with no name and instantiates nothing (Z21).
1226
+ */
1227
+ analyzeZigInstantiation(rootNode, relationships) {
1228
+ const typeNames = /* @__PURE__ */ new Set();
1229
+ const typeFns = /* @__PURE__ */ new Set();
1230
+ const inventory = (n) => {
1231
+ if (n.type === "function_declaration" || n.type === "function_signature") {
1232
+ const t = n.childForFieldName?.("type");
1233
+ if (t && (t.type === "builtin_type" || t.type === "identifier") && String(t.text) === "type") {
1234
+ for (let i = 0; i < n.namedChildCount; i++) {
1235
+ const c = n.namedChild(i);
1236
+ if (c && c.type === "identifier") {
1237
+ typeFns.add(String(c.text));
1238
+ break;
1239
+ }
1240
+ }
1241
+ }
1242
+ }
1243
+ if (n.type === "struct_declaration" || n.type === "enum_declaration" || n.type === "union_declaration" || n.type === "opaque_declaration") {
1244
+ const p = n.parent;
1245
+ const p0 = p?.namedChild(0);
1246
+ if (p && p.type === "variable_declaration" && p.namedChildCount > 0 && p0?.type === "identifier") {
1247
+ typeNames.add(String(p0.text));
1248
+ }
1249
+ }
1250
+ for (let i = 0; i < n.namedChildCount; i++) {
1251
+ const c = n.namedChild(i);
1252
+ if (c) inventory(c);
1253
+ }
1254
+ };
1255
+ inventory(rootNode);
1256
+ if (typeNames.size === 0 && typeFns.size === 0) return;
1257
+ const seen = /* @__PURE__ */ new Set();
1258
+ const enclosingName = (n) => {
1259
+ let cur = n.parent;
1260
+ while (cur) {
1261
+ if (cur.type === "function_declaration" || cur.type === "test_declaration") {
1262
+ for (let i = 0; i < cur.namedChildCount; i++) {
1263
+ const c = cur.namedChild(i);
1264
+ if (c && c.type === "identifier") {
1265
+ if (cur.type === "test_declaration" || this.zigFunctionScopeIsPublished(cur)) {
1266
+ return String(c.text);
1267
+ }
1268
+ break;
1269
+ }
1270
+ }
1271
+ }
1272
+ cur = cur.parent;
1273
+ }
1274
+ return "(global)";
1275
+ };
1276
+ const push = (source, target, line, md) => {
1277
+ if (!target || line <= 0) return;
1278
+ const k = `${source}|${target}|${line}`;
1279
+ if (seen.has(k)) return;
1280
+ seen.add(k);
1281
+ relationships.push({
1282
+ source,
1283
+ target,
1284
+ type: ASTEngine.RelationshipType.Instantiates,
1285
+ confidence: 1,
1286
+ line_number: line,
1287
+ metadata: { relationshipType: "zig_instantiation", zigSpecific: true, isExternalTarget: false, ...md }
1288
+ });
1289
+ };
1290
+ const sites = (n) => {
1291
+ if (n.type === "call_expression") {
1292
+ const callee = n.namedChildCount > 0 ? n.namedChild(0) : null;
1293
+ if (callee && callee.type === "identifier" && typeFns.has(String(callee.text))) {
1294
+ push(enclosingName(n), String(callee.text), this.nodeLine(callee) ?? 0, { isGenericTypeApplication: true });
1295
+ }
1296
+ }
1297
+ if (n.type === "struct_initializer") {
1298
+ const first = n.namedChildCount > 0 ? n.namedChild(0) : null;
1299
+ if (first) {
1300
+ const raw = String(first.text ?? "");
1301
+ const nm = raw.includes(".") ? raw.slice(raw.lastIndexOf(".") + 1) : raw;
1302
+ if (first.type !== "call_expression" && typeNames.has(nm)) {
1303
+ push(enclosingName(n), nm, this.nodeLine(first) ?? 0, { isConstructorApplication: true });
1304
+ }
1305
+ }
1306
+ }
1307
+ for (let i = 0; i < n.namedChildCount; i++) {
1308
+ const c = n.namedChild(i);
1309
+ if (c) sites(c);
1310
+ }
1311
+ };
1312
+ sites(rootNode);
1313
+ }
1314
+ /**
1315
+ * Z33: one `exports` relationship per exported declaration, GENERATED FROM the
1316
+ * export list so the array and the edges cannot drift — which is the failure the
1317
+ * `imports` twins already paid for once, when they were added to three shapes and
1318
+ * not the fourth and only a bidirectional gate caught it.
1319
+ *
1320
+ * Source is `(file)`, not `(global)`: the exporting entity is the compilation
1321
+ * unit, and `(global)` is this lane's marker for the EXPRESSION scope that reads
1322
+ * and imports are attributed to. Keeping them apart lets a consumer group an API
1323
+ * surface without guessing which convention a row used. The line is omitted —
1324
+ * never written as 0 — when a declaration carries none (#820).
1325
+ */
1326
+ emitZigExportEdges(relationships, exports) {
1327
+ const seen = /* @__PURE__ */ new Set();
1328
+ for (const e of exports) {
1329
+ const name = String(e.name ?? "");
1330
+ if (!name) continue;
1331
+ const line = e.range?.startLine ?? 0;
1332
+ const k = `${name}|${line}`;
1333
+ if (seen.has(k)) continue;
1334
+ seen.add(k);
1335
+ relationships.push({
1336
+ source: "(file)",
1337
+ target: name,
1338
+ type: ASTEngine.RelationshipType.Exports,
1339
+ confidence: 1,
1340
+ ...line > 0 ? { line_number: line } : {},
1341
+ metadata: { relationshipType: "zig_export", zigExport: true, exportKind: String(e.kind ?? "") }
1342
+ });
1343
+ }
1344
+ }
1345
+ canonicalizePublishedNames(symbols, relationships, exports) {
1346
+ for (const s of symbols) {
1347
+ if (typeof s.name === "string") s.name = stripZigQuoting(s.name);
1348
+ if (typeof s.ownerName === "string") s.ownerName = stripZigQuoting(s.ownerName);
1349
+ if (typeof s.id === "string") s.id = stripZigQuoting(s.id);
1350
+ if (typeof s.returnType === "string") s.returnType = stripZigQuoting(s.returnType);
1351
+ for (const p of s.parameters ?? []) {
1352
+ if (typeof p?.type === "string") p.type = stripZigQuoting(p.type);
1353
+ if (typeof p?.name === "string") p.name = stripZigQuoting(p.name);
1354
+ }
1355
+ for (const key of ["genericParameters", "decorators", "comments"]) {
1356
+ const arr = s[key];
1357
+ if (Array.isArray(arr)) s[key] = arr.map((v) => typeof v === "string" ? stripZigQuoting(v) : v);
1358
+ }
1359
+ }
1360
+ for (const r of relationships) {
1361
+ if (typeof r.source === "string") r.source = stripZigQuoting(r.source);
1362
+ if (typeof r.target === "string") r.target = stripZigQuoting(r.target);
1363
+ }
1364
+ for (const e of exports) {
1365
+ if (typeof e.name === "string") e.name = stripZigQuoting(e.name);
1366
+ }
1367
+ }
1368
+ analyzeZigBareIdentifierUses(rootNode, relationships, symbols) {
1369
+ const fileValues = /* @__PURE__ */ new Set();
1370
+ const enumMemberNames = /* @__PURE__ */ new Set();
1371
+ for (const s of symbols) {
1372
+ if (s.kind === ASTEngine.SymbolKind.EnumMember) enumMemberNames.add(s.name);
1373
+ if (!s.ownerName && (s.kind === ASTEngine.SymbolKind.Constant || s.kind === ASTEngine.SymbolKind.Variable || s.kind === ASTEngine.SymbolKind.Function || s.kind === ASTEngine.SymbolKind.EnumMember)) {
1374
+ fileValues.add(s.name);
1375
+ }
1376
+ }
1377
+ const emitted = /* @__PURE__ */ new Set();
1378
+ const scopeStack = [];
1379
+ const isShadowed = (name) => {
1380
+ for (let i = scopeStack.length - 1; i >= 0; i--) {
1381
+ if (scopeStack[i].names.has(name)) return true;
1382
+ }
1383
+ return false;
1384
+ };
1385
+ const bindParamName = (node, frame) => {
1386
+ for (let i = 0; i < node.namedChildCount; i++) {
1387
+ const c = node.namedChild(i);
1388
+ if (c && c.type === "identifier") {
1389
+ if (c.text !== "_") frame.names.add(c.text);
1390
+ return;
1391
+ }
1392
+ }
1393
+ const any = this.anyChild(node, (c) => c.type === "identifier");
1394
+ if (any && any.text !== "_") frame.names.add(any.text);
1395
+ };
1396
+ const bindIdentifiersUnder = (node, frame) => {
1397
+ const walk = [node];
1398
+ while (walk.length > 0) {
1399
+ const m = walk.pop();
1400
+ if (m !== node && (m.type === "call_expression" || m.type === "block" || m.type === "block_expression")) {
1401
+ continue;
1402
+ }
1403
+ if (m.type === "identifier" && m.text !== "_") frame.names.add(m.text);
1404
+ for (let i = m.namedChildCount - 1; i >= 0; i--) {
1405
+ const c = m.namedChild(i);
1406
+ if (c) walk.push(c);
1407
+ }
1408
+ }
1409
+ };
1410
+ const isDefiningOccurrence = (node) => {
1411
+ const p = node.parent;
1412
+ if (!p) return false;
1413
+ if (p.type === "variable_declaration" || p.type === "assignment_expression") {
1414
+ if (!this.isPlainAssignment(p)) return false;
1415
+ const lhs = p.namedChildCount > 0 ? p.namedChild(0) : null;
1416
+ return !!lhs && lhs.id === node.id;
1417
+ }
1418
+ if (p.type === "error_set_declaration" && node.type === "identifier") return true;
1419
+ if (p.type === "function_declaration" || p.type === "test_declaration" || p.type === "container_field" || p.type === "error_set_declaration") {
1420
+ for (let i = 0; i < p.namedChildCount; i++) {
1421
+ const c = p.namedChild(i);
1422
+ if (c && c.type === "identifier") {
1423
+ return c.startIndex === node.startIndex && c.endIndex === node.endIndex;
1424
+ }
1425
+ }
1426
+ }
1427
+ return false;
1428
+ };
1429
+ const isCallCallee = (node) => {
1430
+ let top = node;
1431
+ let p = node.parent;
1432
+ while (p && p.type !== "call_expression" && p.type !== "block" && p.type !== "function_declaration") {
1433
+ top = p;
1434
+ p = p.parent;
1435
+ }
1436
+ if (!p || p.type !== "call_expression") return false;
1437
+ const first = p.namedChildCount > 0 ? p.namedChild(0) : null;
1438
+ return !!first && top.startIndex >= first.startIndex && top.endIndex <= first.endIndex;
1439
+ };
1440
+ const stack = [{ node: rootNode, exit: false, pushed: false }];
1441
+ while (stack.length > 0) {
1442
+ const { node, exit, pushed } = stack.pop();
1443
+ if (exit) {
1444
+ if (pushed) scopeStack.pop();
1445
+ continue;
1446
+ }
1447
+ const t = node.type;
1448
+ let pushedScope = false;
1449
+ if (t === "function_declaration" || t === "test_declaration") {
1450
+ const frame = { names: /* @__PURE__ */ new Set(), fnName: null };
1451
+ const params = [node];
1452
+ while (params.length > 0) {
1453
+ const m = params.pop();
1454
+ if (m.type !== "function_declaration" || m === node) {
1455
+ if (m.type === "parameter") bindParamName(m, frame);
1456
+ for (let i = m.namedChildCount - 1; i >= 0; i--) {
1457
+ const c = m.namedChild(i);
1458
+ if (c && c.type !== "block" && c.type !== "block_expression" && c.type !== "function_declaration") params.push(c);
1459
+ }
1460
+ }
1461
+ }
1462
+ const nm = this.anyChild(node, (c) => c.type === "identifier");
1463
+ frame.fnName = nm && (node.type === "test_declaration" || this.zigFunctionScopeIsPublished(node)) ? nm.text : null;
1464
+ scopeStack.push(frame);
1465
+ pushedScope = true;
1466
+ } else if (t === "block" || t === "block_expression") {
1467
+ scopeStack.push({ names: /* @__PURE__ */ new Set(), fnName: null });
1468
+ pushedScope = true;
1469
+ } else if (t === "switch_case") {
1470
+ scopeStack.push({ names: /* @__PURE__ */ new Set(), fnName: null });
1471
+ pushedScope = true;
1472
+ } else if (t === "payload") {
1473
+ const top = scopeStack.length > 0 ? scopeStack[scopeStack.length - 1] : null;
1474
+ if (top) bindIdentifiersUnder(node, top);
1475
+ } else if (t === "variable_declaration") {
1476
+ const top = scopeStack.length > 0 ? scopeStack[scopeStack.length - 1] : null;
1477
+ if (top && (this.hasToken(node, "var") || this.hasToken(node, "const"))) {
1478
+ for (let i = 0; i < node.namedChildCount; i++) {
1479
+ const c = node.namedChild(i);
1480
+ if (!c) continue;
1481
+ if (c.type === "identifier") {
1482
+ if (c.text !== "_") top.names.add(c.text);
1483
+ break;
1484
+ }
1485
+ break;
1486
+ }
1487
+ }
1488
+ } else if (t === "identifier") {
1489
+ const name = node.text;
1490
+ const p = node.parent;
1491
+ let inError = false;
1492
+ if (p && p.type === "error_type" && p.namedChildCount > 0) {
1493
+ const last = p.namedChild(p.namedChildCount - 1);
1494
+ if (last && last.startIndex === node.startIndex && last.endIndex === node.endIndex) {
1495
+ let q = p.parent;
1496
+ let typePos = false;
1497
+ while (q) {
1498
+ if (q.type === "parameter" || q.type === "container_field") {
1499
+ typePos = true;
1500
+ break;
1501
+ }
1502
+ if (q.type === "variable_declaration" || q.type === "arguments" || q.type === "return_expression" || q.type === "call_expression" || q.type === "function_declaration" || q.type === "block" || q.type === "block_expression") break;
1503
+ q = q.parent;
1504
+ }
1505
+ if (!typePos) inError = true;
1506
+ }
1507
+ }
1508
+ if (name && name !== "_" && !name.startsWith("$") && !isDefiningOccurrence(node) && !isCallCallee(node) && !(p && p.type === "field_expression") && // F6 audit: the former `label_name` / `continue_label` disjuncts are
1509
+ // rust vocabulary — tree-sitter-zig labels are `block_label` /
1510
+ // `break_label` (both live here) and a `continue :outer` label is an
1511
+ // anonymous token with no identifier child to suppress.
1512
+ !(p && p.type === "labeled_statement" || p && p.type === "block_label" || p && p.type === "break_label") && !isShadowed(name) && (fileValues.has(name) || inError && enumMemberNames.has(name))) {
1513
+ let fnName = null;
1514
+ for (let i = scopeStack.length - 1; i >= 0; i--) {
1515
+ if (scopeStack[i].fnName) {
1516
+ fnName = scopeStack[i].fnName;
1517
+ break;
1518
+ }
1519
+ }
1520
+ const src = fnName ?? "(global)";
1521
+ const key = `${src}::uses::${name}`;
1522
+ if (!emitted.has(key)) {
1523
+ emitted.add(key);
1524
+ relationships.push({
1525
+ source: src,
1526
+ target: name,
1527
+ type: ASTEngine.RelationshipType.Uses,
1528
+ confidence: 1,
1529
+ // M59.A7 (#825): the read occurs on the identifier's own line.
1530
+ ...this.nodeLine(node) ? { line_number: this.nodeLine(node) } : {},
1531
+ metadata: { relationshipType: "identifier_usage", nodeType: node.type }
1532
+ });
1533
+ }
1534
+ }
1535
+ }
1536
+ stack.push({ node, exit: true, pushed: pushedScope });
1537
+ for (let i = node.namedChildCount - 1; i >= 0; i--) {
1538
+ const child = node.namedChild(i);
1539
+ if (child) stack.push({ node: child, exit: false, pushed: false });
1540
+ }
1541
+ }
1542
+ }
1543
+ // F6 audit — REMOVED: `hasZigQualifiedAncestor`. Its only `true` return was
1544
+ // `cur.type === "qualified_type"`, and tree-sitter-zig has no such node (a
1545
+ // qualified type `std.mem.Allocator` is a nested `field_expression`,
1546
+ // probe-verified), so the function returned false for every input and the
1547
+ // `!this.hasZigQualifiedAncestor(node)` guard it fed was a no-op. Its
1548
+ // boundary names were equally dead (`field_declaration`, `composite_literal`,
1549
+ // `assignment_statement` are rust/go names; zig uses `container_field`,
1550
+ // `struct_initializer`, `variable_declaration`). The intended suppression of
1551
+ // type-position reads is already done by the direct `field_expression`
1552
+ // parent check at the call site; re-pointing this at `field_expression`
1553
+ // would have dropped genuine receiver reads instead.
1554
+ /** Builtin @-prefixed calls and generic type applications are not user
1555
+ * call targets. Returns the leaf name for a real user call site, else
1556
+ * undefined. */
1557
+ zigCalleeLeafNode(call) {
1558
+ const callee = call.namedChildCount > 0 ? call.namedChild(0) : null;
1559
+ if (!callee) return void 0;
1560
+ if (callee.type === "identifier") {
1561
+ if (callee.text === "usingnamespace") return void 0;
1562
+ return callee;
1563
+ }
1564
+ if (callee.type === "field_expression") {
1565
+ return this.zigFieldLeafNode(callee);
1566
+ }
1567
+ return void 0;
1568
+ }
1569
+ // Z17 (deep review) — REMOVED as dead: `zigCalleeLeaf` and `zigFieldLeaf`,
1570
+ // the string-returning wrappers around the two `*Node` accessors below. Every
1571
+ // call site uses the NODE form, because every emitted edge needs the site line
1572
+ // (M59.A7 #825); the string forms had no caller. `zigTypeLeaf` (the same
1573
+ // wrapper for the type leaf) went the same way, and `nameLine` — a helper for
1574
+ // the C/C++ lanes' attribute-line-base rule, never called here, so the zig lane
1575
+ // has no `#920` line rule to claim. Dead code in a review wave is a claim
1576
+ // about a contract that is not implemented, and four of them were sitting in
1577
+ // the file.
1578
+ /** The name-bearing field LEAF of a field_expression (`std.math.abs` ->
1579
+ * `abs`, `list.items` -> `items`) — the node whose text is the edge's target
1580
+ * and whose line is the edge's site (M59.A7 #825). */
1581
+ zigFieldLeafNode(expr) {
1582
+ const last = expr.namedChildCount > 0 ? expr.namedChild(expr.namedChildCount - 1) : null;
1583
+ if (last && last.type === "identifier") return last;
1584
+ return void 0;
1585
+ }
1586
+ /** True when this field_expression is the direct callee of a call_expression
1587
+ * (list.append in `list.append()`) — then the calls edge is the fact, not
1588
+ * a uses edge. Also skips builtin access `std.foo`. */
1589
+ isCallCallee(expr) {
1590
+ const p = expr.parent;
1591
+ return !!p && p.type === "call_expression";
1592
+ }
1593
+ /**
1594
+ * Z66 — the dotted path a read's receiver spells, when the receiver is an
1595
+ * EXTERNAL `@import` alias, else null. The path is written out of the tree,
1596
+ * segment by segment, so it is a parse fact and not a guess.
1597
+ *
1598
+ * `std.mem.Allocator` reaches the edge layer as the leaf `Allocator` alone; this
1599
+ * walks up the `field_expression` chain to the root identifier and, if that root
1600
+ * is a name this file binds by importing a BARE module path, returns
1601
+ * `std.mem.Allocator`. A chain that breaks anywhere — a call result receiver
1602
+ * (`std.io.getStdOut().writer()`), an anonymous enum literal (`.bad`, one named
1603
+ * child and no base), a computed base — returns null and the edge stays as it
1604
+ * was, because a path this analyzer cannot spell out of the tree is a path it
1605
+ * must not publish.
1606
+ *
1607
+ * The name is emitted in place of the leaf and the receiver is recorded in
1608
+ * metadata, so the edge says which module it names (`zigImportAlias`,
1609
+ * `zigImportModule`) and a consumer can EXCLUDE it from its own unresolved count
1610
+ * with a declared reason instead of inferring one. That is the whole of the
1611
+ * `imports`-module-path defect's cost: `diagnose-incoming` reads 404 / 1,024 /
1612
+ * 2,585 unresolved targets for zig with 0 module-wildcard shaped, because the
1613
+ * target was the leaf, so nothing about the edge said `std`.
1614
+ */
1615
+ zigExternalReceiverPath(expr, aliases) {
1616
+ if (!expr || expr.type !== "field_expression" || aliases.size === 0) return null;
1617
+ if (expr.namedChildCount < 2) return null;
1618
+ const segments = [];
1619
+ let node = expr;
1620
+ let root = "";
1621
+ let depth = 0;
1622
+ while (depth++ < 64) {
1623
+ const fieldNode = node.namedChild(node.namedChildCount - 1);
1624
+ if (!fieldNode || fieldNode.type !== "identifier") return null;
1625
+ segments.push(stripZigQuoting(String(fieldNode.text ?? "")));
1626
+ const base = node.namedChild(0);
1627
+ if (!base) return null;
1628
+ if (base.type === "identifier") {
1629
+ root = stripZigQuoting(String(base.text ?? ""));
1630
+ break;
1631
+ }
1632
+ if (base.type !== "field_expression") return null;
1633
+ node = base;
1634
+ }
1635
+ if (root === "") return null;
1636
+ const hit = aliases.get(root);
1637
+ if (!hit || !hit.external) return null;
1638
+ segments.reverse();
1639
+ return { target: `${root}.${segments.join(".")}`, alias: root, module: hit.module };
1640
+ }
1641
+ /**
1642
+ * THE EXTERNAL CLAIM'S METADATA, and the ONE place the `zigExternalRef` KEY is decided.
1643
+ * Three emission sites (calls, field reads, declared-type references) all call this,
1644
+ * because three copies of "may I claim this" is three rules.
1645
+ *
1646
+ * WHY THE CLAIM IS NOT ALWAYS ADMISSIBLE, measured rather than argued. The incoming
1647
+ * arbiter reads `zigExternalRef` as an OWNER claim — "the target names a declaration
1648
+ * whose OWNER the project does not publish" — and verifies it by looking the owner up
1649
+ * (`script/m14/incoming-rule.ts`, `verifyExternalClaim`). For a target the receiver walk
1650
+ * produced, the owner is everything before the LAST dot, and when the receiver path is
1651
+ * exactly ONE segment long, that owner IS the alias:
1652
+ *
1653
+ * `std.mem` owner `std` -> the file's own `const std = @import("std")`
1654
+ * `std.mem.Allocator` owner `std.mem` -> no declaration carries that name
1655
+ *
1656
+ * Z35 publishes that import binding as a `constant` named for the alias, so the owner of
1657
+ * a one-segment path is ALWAYS a published declaration and the claim is ALWAYS refuted.
1658
+ * MEASURED on the gate's own 60-file stride sample (565 marked edges, via
1659
+ * `validation/m14/fixtures/zig/external-claim-audit.ts`), cross-tabulated by target depth
1660
+ * because `owner === alias` is exactly the one-dot case:
1661
+ *
1662
+ * 1 dot: extOK 1 extFAIL 259
1663
+ * 2 dots: extOK 223 extFAIL 0
1664
+ * 3 dots: extOK 60 extFAIL 0
1665
+ * 4 dots: extOK 21 extFAIL 0
1666
+ * 5 dots: extOK 1 extFAIL 0
1667
+ *
1668
+ * and all 259 were answered by a `constant` named for the alias, in the edge's own file.
1669
+ * The key was asserting a claim the project can check and does refute — this lane's own
1670
+ * defect, not a marker-spelling accident — and the fix is to stop asserting it where it
1671
+ * cannot survive the lookup.
1672
+ *
1673
+ * WHAT IS NOT LOST. The QUALIFICATION stays: the target is still `std.mem`, not the bare
1674
+ * leaf `mem`, which is the Z66 defect fix and is untouched by this. The receiver is still
1675
+ * identified on the edge — `zigImportAlias`, `zigImportModule` and the post-pass's
1676
+ * `targetScope: "file-external"` are all still written — so a consumer can still exclude
1677
+ * the edge by a rule instead of by a guess. What is dropped is one key whose meaning the
1678
+ * family defines as "the owner is not published", which is false here.
1679
+ *
1680
+ * THE RESIDUAL RISK, stated rather than hidden: a project that declares a symbol literally
1681
+ * named `std.mem` (a quoted identifier, `@"std.mem"`) would refute a two-segment claim too.
1682
+ * It is not zero, and it is what the arbiter's refutation is FOR — a deep-path claim that
1683
+ * ever reads refuted is a defect this lane can see.
1684
+ */
1685
+ zigExternalMetadata(ext) {
1686
+ if (!ext) return {};
1687
+ const receiver = { zigImportAlias: ext.alias, zigImportModule: ext.module };
1688
+ const dot = ext.target.lastIndexOf(".");
1689
+ if (dot <= 0 || ext.target.slice(0, dot) === ext.alias) return receiver;
1690
+ return { zigExternalRef: true, ...receiver };
1691
+ }
1692
+ /**
1693
+ * Did `from` reach `decl` (a `variable_declaration`) THROUGH its type annotation?
1694
+ *
1695
+ * True for `var x: c.Foo = ...` and `var x: ?c.Bar = null`, false for the two
1696
+ * positions that are value reads: the initializer (`var x = v.baz`) and the
1697
+ * assignment LHS that also parses as a declaration. The test is an ANCESTRY test
1698
+ * rather than an identity test because the grammar's `type` field is the whole
1699
+ * wrapped path — `?c.Bar` is one `field_expression` — so an inner segment of the
1700
+ * chain reaches the declaration from a DESCENDANT of that node.
1701
+ *
1702
+ * It reads the same `type` field the declared-type collector reads, so the pass
1703
+ * that suppresses the duplicate `uses` and the pass that carries the fact as
1704
+ * `references (declared_type)` are keyed off one node by construction. `var d =
1705
+ * v.baz` has no `type` field at all, which is why the initializer is excluded
1706
+ * here and not by a name test.
1707
+ */
1708
+ zigArrivedAtTypeAnnotation(decl, from) {
1709
+ const annotation = this.typeFieldOf(decl);
1710
+ if (!annotation || !from) return false;
1711
+ let cur = from;
1712
+ let hops = 0;
1713
+ while (cur && hops++ < 8) {
1714
+ if (cur.id === annotation.id) return true;
1715
+ const parent = cur.parent;
1716
+ if (!parent || parent.id === decl.id) return false;
1717
+ cur = parent;
1718
+ }
1719
+ return false;
1720
+ }
1721
+ /**
1722
+ * The field_expression a leaf NAME belongs to, when the leaf is the LAST named
1723
+ * child of one — i.e. when the leaf really is the final segment of that access
1724
+ * rather than an identifier found somewhere under it. The declared-type pass reads
1725
+ * leaves by a breadth-first walk (Z19), so for an unnamed fn-TYPE parameter the leaf
1726
+ * can be a BASE segment; qualifying from there would name a path the annotation
1727
+ * does not end at, so that case is refused instead.
1728
+ */
1729
+ zigTrailingFieldExpr(leaf) {
1730
+ if (!leaf) return void 0;
1731
+ const parent = leaf.parent;
1732
+ if (!parent || parent.type !== "field_expression") return void 0;
1733
+ if (parent.namedChild(parent.namedChildCount - 1)?.id !== leaf.id) return void 0;
1734
+ return parent;
1735
+ }
1736
+ /**
1737
+ * Declared-type reference graph (family standard — go/c/rust M14). Collects
1738
+ * the leaf names of parameter types, return types, and struct/union/enum/
1739
+ * error-set field types, emitting `references` edges. Builtins and the bare
1740
+ * `void`/`!void` wrappers are excluded; generic type applications on a var
1741
+ * RHS (std.StringHashMap(usize)) are NOT declared-type deps.
1742
+ */
1743
+ extractZigDeclaredTypeRefs(rootNode, relationships) {
1744
+ const emitted = /* @__PURE__ */ new Set();
1745
+ const importAliases = zigImportAliases(rootNode);
1746
+ const push = (leaf, site) => {
1747
+ if (!leaf) return;
1748
+ const head = leaf;
1749
+ if (!head || ZIG_DECLARED_TYPE_BUILTINS.has(head) && !zigFileScopeNames(rootNode).has(head)) return;
1750
+ const c0 = head.charCodeAt(0);
1751
+ if (c0 >= 48 && c0 <= 57) return;
1752
+ const ext = this.zigExternalReceiverPath(this.zigTrailingFieldExpr(site), importAliases);
1753
+ const target = ext ? ext.target : head;
1754
+ if (!emitted.has(target)) {
1755
+ emitted.add(target);
1756
+ relationships.push({
1757
+ source: "(declared-type)",
1758
+ target,
1759
+ type: ASTEngine.RelationshipType.References,
1760
+ confidence: 0.9,
1761
+ // M59.A7 (#825): the type as written at this site.
1762
+ ...this.nodeLine(site) ? { line_number: this.nodeLine(site) } : {},
1763
+ metadata: {
1764
+ relationshipType: "declared_type",
1765
+ ...this.zigExternalMetadata(ext)
1766
+ }
1767
+ });
1768
+ }
1769
+ };
1770
+ const stack = [rootNode];
1771
+ while (stack.length > 0) {
1772
+ const n = stack.pop();
1773
+ if (n.type === "function_declaration" || n.type === "function_signature") {
1774
+ const paramsNode = this.anyChild(n, (c) => c.type === "parameters");
1775
+ if (paramsNode) {
1776
+ for (let i = 0; i < paramsNode.namedChildCount; i++) {
1777
+ const param = paramsNode.namedChild(i);
1778
+ if (param?.type !== "parameter") continue;
1779
+ const tNode = this.zigTypeLeafNode(param);
1780
+ if (tNode) push(tNode.text, tNode);
1781
+ }
1782
+ }
1783
+ const retNode = this.typeFieldOf(n) ?? this.zigFallbackReturnType(n);
1784
+ const retLeafNode = retNode ? this.leafOfTypeNode(retNode) : void 0;
1785
+ if (retLeafNode) push(retLeafNode.text, retLeafNode);
1786
+ }
1787
+ if (n.type === "container_field") {
1788
+ const tNode = this.zigTypeLeafNode(n);
1789
+ if (tNode) push(tNode.text, tNode);
1790
+ }
1791
+ if (n.type === "variable_declaration") {
1792
+ const tField = this.typeFieldOf(n);
1793
+ if (tField) {
1794
+ const leaf = this.leafOfTypeNode(tField);
1795
+ if (leaf) push(leaf.text, leaf);
1796
+ }
1797
+ }
1798
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
1799
+ const c = n.namedChild(i);
1800
+ if (c) stack.push(c);
1801
+ }
1802
+ }
1803
+ }
1804
+ /**
1805
+ * The history this replaced, kept because the field-based version is only
1806
+ * better if the field is really there (deep-review fixes, 2026-08-29):
1807
+ * (1) builtin_type nodes were dropped because they shadowed the real leaf —
1808
+ * `map: *std.StringHashMap(usize)` returned `usize`. Z13 keeps that
1809
+ * exclusion (it is a value filter, in `push`) and fixes the cause: the
1810
+ * leaf is read from the `type` FIELD, so a generic ARGUMENT can no
1811
+ * longer be the answer because it is no longer in scope.
1812
+ * (2) the first identifier was dropped as the declared name. Z13 drops the
1813
+ * whole guess — the field is the name's sibling, not its predecessor.
1814
+ * (3) enum members yielded undefined, and still do: a `container_field`
1815
+ * with no `type` field IS the enum member, which is now the test
1816
+ * instead of a consequence.
1817
+ */
1818
+ /**
1819
+ * True when a `struct_initializer` initializes an array/slice OF TYPES —
1820
+ * `pub const Implements = [_]type{ gtk.Scrollable };` — so the declaration is
1821
+ * a type, not a value. Measured on the pinned corpora: every one of the 32
1822
+ * upper-case `struct_initializer` declarations is of this form, while the
1823
+ * 467 lower-case ones are ordinary values (`[_]u8{ 1, 2, 3 }`, `Point{}`), and
1824
+ * the element type is a direct child (`array_type` / `slice_type` carrying the
1825
+ * `type`, `comptime_int` or `comptime_float` builtin).
1826
+ */
1827
+ isTypeValuedInitializer(init) {
1828
+ for (let i = 0; i < init.namedChildCount; i++) {
1829
+ const c = init.namedChild(i);
1830
+ if (!c) continue;
1831
+ if (c.type !== "array_type" && c.type !== "slice_type") continue;
1832
+ for (let j = 0; j < c.namedChildCount; j++) {
1833
+ const e = c.namedChild(j);
1834
+ if (e && e.type === "builtin_type" && (e.text === "type" || e.text === "comptime_int" || e.text === "comptime_float")) return true;
1835
+ }
1836
+ }
1837
+ return false;
1838
+ }
1839
+ /**
1840
+ * A `labeled_type_expression` that is really a labeled BLOCK — `const V =
1841
+ * cvalue: { … break :cvalue v; }` — yields a VALUE, whatever the name's case.
1842
+ *
1843
+ * The grammar has ONE node type for both Zig forms (3,900 occurrences in the
1844
+ * pinned corpora): `L: { … break :L v; }` is a labeled block and
1845
+ * `L: T { … return v; }` is a labeled type, and the two differ in the block
1846
+ * they carry — a block leaves by `break :label`, a type by `return`. Measured
1847
+ * over 400 corpus files: 9 of 9 labeled_type_expression right-hand sides
1848
+ * break and none returns, and the six with an upper-case name
1849
+ * (`pub const CValue = cvalue: { … }`, `pub const Key = key: { … }`) were
1850
+ * published as `Type` for what are comptime-computed struct values.
1851
+ */
1852
+ isLabeledBlockValue(rhs) {
1853
+ if (rhs.type !== "labeled_type_expression") return false;
1854
+ let hasBreak = false;
1855
+ let hasReturn = false;
1856
+ const stack = [rhs];
1857
+ let guard = 0;
1858
+ while (stack.length > 0 && guard++ < 500) {
1859
+ const n = stack.pop();
1860
+ if (n.type === "break_label") hasBreak = true;
1861
+ if (n.type === "return_expression") hasReturn = true;
1862
+ if (hasReturn) return false;
1863
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
1864
+ const c = n.namedChild(i);
1865
+ if (c) stack.push(c);
1866
+ }
1867
+ }
1868
+ return hasBreak;
1869
+ }
1870
+ /**
1871
+ * The identifier NODE that names a declaration's type, so a declared-type
1872
+ * reference can carry the line the type is written on (M59.A7 #825).
1873
+ *
1874
+ * Z13 (deep review, 2026-09-28) — this used to walk EVERY `identifier` under
1875
+ * the declaration node, drop the first (assumed to be the declared name) and
1876
+ * take the last. The grammar does not require that guess: it hands the type
1877
+ * over as a FIELD (`parameter.type` 78,895, `container_field.type` 49,424,
1878
+ * `variable_declaration.type` 43,949, `function_declaration.type` 37,129 in
1879
+ * the pinned corpora), and no TYPE node carries a field of that name, so
1880
+ * preferring it can never pick the wrong side of an `!` or a `.*`.
1881
+ *
1882
+ * The walk was wrong wherever a declaration has a DEFAULT VALUE: the value
1883
+ * contributes its own identifiers, and the LAST one won.
1884
+ * `size: Size = .byte` published `byte` — an enum member — and never `Size`
1885
+ * `mode: Mode = .a` published `a`
1886
+ * `normal = @sizeOf(u8)` published `u8`
1887
+ * Measured over the three corpora, the sites where the emitted target changes
1888
+ * are dominated by exactly these shapes (`script/m14/tmp/zigw-decltype.ts`).
1889
+ *
1890
+ * With no `type` field the node is an ENUM MEMBER (`container_field` is the
1891
+ * same node type for `a: u8` and for `north` / `normal = 0`; only the former
1892
+ * has a type), and a member's value is not a type — so that case yields
1893
+ * `undefined` rather than falling back to the walk. The walk survives only
1894
+ * where nothing else exists: an already-extracted type node.
1895
+ */
1896
+ zigTypeLeafNode(node) {
1897
+ const typeField = this.typeFieldOf(node);
1898
+ if (typeField) return this.leafOfTypeNode(typeField);
1899
+ if (node.type === "container_field") return void 0;
1900
+ if (node.type === "variable_declaration") return void 0;
1901
+ const nameField = this.namedFieldOf(node, "name");
1902
+ if (!nameField) return this.leafOfTypeNode(node);
1903
+ const stack = [];
1904
+ for (let i = node.namedChildCount - 1; i >= 0; i--) {
1905
+ const c = node.namedChild(i);
1906
+ if (c && c.id !== nameField.id) stack.push(c);
1907
+ }
1908
+ while (stack.length > 0) {
1909
+ const n = stack.pop();
1910
+ if (n.type === "identifier") return n;
1911
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
1912
+ const c = n.namedChild(i);
1913
+ if (c) stack.push(c);
1914
+ }
1915
+ }
1916
+ return void 0;
1917
+ }
1918
+ /** The `name` FIELD of a node, or null. `function_declaration` carries one
1919
+ * 37,126 times in the corpus, `container_field` 104,090, `parameter` 77,594
1920
+ * (the 1,346 that do not are fn-TYPE parameters, which is what the
1921
+ * name/no-name split above turns on). */
1922
+ namedFieldOf(node, field) {
1923
+ if (typeof node.childForFieldName !== "function") return null;
1924
+ return node.childForFieldName(field) ?? null;
1925
+ }
1926
+ /** The name a TYPE NODE carries, or `undefined` when it carries none.
1927
+ *
1928
+ * - a container literal in a type position is ANONYMOUS (`fn f(s: struct {
1929
+ * FLAG: u8 }) void`): it has fields, not a name, and the last identifier
1930
+ * under it is a FIELD name — the shape that published
1931
+ * `references (declared-type) -> FLAG` for a type that names nothing;
1932
+ * - `!T` carries its dependency in the `ok` FIELD (21,353 in the corpus).
1933
+ * The walk took the LAST identifier under the whole node, which is a
1934
+ * member of the ERROR set: `fn f() error{A}!u32` published `-> A`, a
1935
+ * phantom, and never `u32` (correctly, as a builtin);
1936
+ * - an `error_type` is an unnamed set, for the same reason;
1937
+ * - a `labeled_type_expression` (`const V = blk: { … break :blk v; }`) has
1938
+ * no statically knowable name at all: its label and its value are both
1939
+ * identifiers in the tree, and the last one is the VALUE, not a type.
1940
+ */
1941
+ leafOfTypeNode(t) {
1942
+ if (t.type === "identifier") return t;
1943
+ switch (t.type) {
1944
+ case "struct_declaration":
1945
+ case "union_declaration":
1946
+ case "enum_declaration":
1947
+ case "error_set_declaration":
1948
+ case "error_type":
1949
+ case "labeled_type_expression":
1950
+ return void 0;
1951
+ case "error_union_type": {
1952
+ const ok = this.namedFieldOf(t, "ok");
1953
+ return ok ? this.leafOfTypeNode(ok) : void 0;
1954
+ }
1955
+ default:
1956
+ return this.leafIdentifierUnder(t);
1957
+ }
1958
+ }
1959
+ /** The `type` FIELD of a node, or null when the grammar does not name one.
1960
+ * `childForFieldName` is guarded because a web-tree-sitter build without
1961
+ * field access would otherwise throw on the first declaration. */
1962
+ typeFieldOf(node) {
1963
+ if (typeof node.childForFieldName !== "function") return null;
1964
+ return node.childForFieldName("type") ?? null;
1965
+ }
1966
+ /** The LAST `identifier` at or under `node` — the leaf of a qualified type
1967
+ * (`std.mem.Allocator` → `Allocator`, `MyList(u32)` → `MyList`), the same
1968
+ * rule the old walk applied, now scoped to the type. Guarded at 200 nodes,
1969
+ * which the corpus census shows is far above the deepest real type
1970
+ * (`parameter.type` trees are a handful of nodes). */
1971
+ leafIdentifierUnder(node) {
1972
+ if (node.type === "identifier") return node;
1973
+ let found;
1974
+ let seen = 0;
1975
+ const stack = [node];
1976
+ while (stack.length > 0 && seen++ < 200) {
1977
+ const n = stack.pop();
1978
+ if (n.type === "identifier") found = n;
1979
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
1980
+ const c = n.namedChild(i);
1981
+ if (c) stack.push(c);
1982
+ }
1983
+ }
1984
+ return found;
1985
+ }
1986
+ /**
1987
+ * The return-type node of a function, WITHOUT the `type` field — the
1988
+ * fallback for a grammar variant that does not name it. Zig's return type is
1989
+ * the last child that is not the body and not the parameter list; a
1990
+ * `callconv(.C)` between them is not the return type, so the node-type list
1991
+ * that used to sit here is replaced by position.
1992
+ */
1993
+ zigFallbackReturnType(fn) {
1994
+ const kids = this.getAllNamedChildren(fn);
1995
+ for (let i = kids.length - 1; i >= 0; i--) {
1996
+ const c = kids[i];
1997
+ if (c.type === "block" || c.type === "parameters" || c.type === "block_expression") continue;
1998
+ if (i === 0 && c.type === "identifier") continue;
1999
+ if (c.type === "calling_convention") continue;
2000
+ return c;
2001
+ }
2002
+ return void 0;
2003
+ }
2004
+ /**
2005
+ * True when `node` is the left-hand side of a WRITE. Zig assignments parse as
2006
+ * `variable_declaration` with the LHS as the first named child, and the
2007
+ * OPERATOR is the discriminator: a plain `=` writes without reading, while
2008
+ * `+=`, `|=`, `*%=` and the rest read their left side as well (Z15).
2009
+ */
2010
+ isWriteLhs(node) {
2011
+ const p = node.parent;
2012
+ if (!p) return false;
2013
+ if (p.type !== "variable_declaration" && p.type !== "assignment_expression") return false;
2014
+ if (p.namedChildCount > 0 && p.namedChild(0)?.id !== node.id) return false;
2015
+ return this.isPlainAssignment(p);
2016
+ }
2017
+ /** A plain `=` assignment: the operator is the anonymous token `=`, which is
2018
+ * a node type of its own, distinct from `+=`/`-=`/`|=`/`<<=` (140 distinct
2019
+ * anonymous token types in the corpus census). */
2020
+ isPlainAssignment(node) {
2021
+ return this.hasToken(node, "=");
2022
+ }
2023
+ /** A function's body node, read off the grammar's `body` field, falling back
2024
+ * to a `block` child for a grammar variant that does not name it. */
2025
+ functionBody(fn) {
2026
+ if (typeof fn.childForFieldName === "function") {
2027
+ const b = fn.childForFieldName("body");
2028
+ if (b) return b;
2029
+ if (fn.namedChildCount > 0) {
2030
+ return null;
2031
+ }
2032
+ return null;
2033
+ }
2034
+ return this.anyChild(fn, (c) => c.type === "block");
2035
+ }
2036
+ /**
2037
+ * M59.A7 (#825): the 1-based line of a syntax node, or `undefined` when the
2038
+ * node is absent. Never 0 — a missing site is OMITTED from the edge (the
2039
+ * #820 rule), so a reader can tell "no line" from "line 1".
2040
+ */
2041
+ nodeLine(node) {
2042
+ if (!node || !node.startPosition || typeof node.startPosition.row !== "number") return void 0;
2043
+ return node.startPosition.row + 1;
2044
+ }
2045
+ /** Per-symbol cyclomatic complexity of a function/method body (family
2046
+ * standard — go/c/rust all emit symbol.complexity; zig did NOT).
2047
+ *
2048
+ * Deep-review fixes (2026-08-29, final zig pass):
2049
+ * (1) Expression forms if_expression / for_expression / while_expression
2050
+ * are Zig's DOMINANT forms (`const a = if (c) 1 else 2;`) and were
2051
+ * never counted — only the statement forms were. All six now count.
2052
+ * (2) `else` in expression forms is an ANONYMOUS token inside the
2053
+ * if_expression (probe-verified), not an else_clause — both are
2054
+ * counted now (else_clause node OR hasToken "else").
2055
+ * (3) binary_expression used a substring trap: `t.indexOf("and")`
2056
+ * falsely matched identifiers (`x + standard + order` -> +2). The
2057
+ * operator is an anonymous child token (`and`/`or`/`orelse`,
2058
+ * probe-verified) — we now match the token type exactly. */
2059
+ zigBodyComplexity(node) {
2060
+ let c = 1;
2061
+ const stack = [node];
2062
+ while (stack.length > 0) {
2063
+ const n = stack.pop();
2064
+ if (n.type === "if_statement" || n.type === "if_expression" || n.type === "for_statement" || n.type === "for_expression" || n.type === "while_statement" || n.type === "while_expression" || n.type === "switch_expression" || n.type === "else_clause" || n.type === "catch_expression") {
2065
+ c++;
2066
+ }
2067
+ if ((n.type === "if_expression" || n.type === "for_expression" || n.type === "while_expression") && this.hasToken(n, "else")) {
2068
+ c++;
2069
+ }
2070
+ if (n.type === "binary_expression") {
2071
+ for (let i = 0; i < n.childCount; i++) {
2072
+ const op = n.child(i);
2073
+ if (op && !op.isNamed && (op.type === "and" || op.type === "or" || op.type === "orelse")) {
2074
+ c++;
2075
+ break;
2076
+ }
2077
+ }
2078
+ }
2079
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
2080
+ const c2 = n.namedChild(i);
2081
+ if (c2) stack.push(c2);
2082
+ }
2083
+ }
2084
+ return c;
2085
+ }
2086
+ /** #419-D evidence-tier classification for every emitted relationship
2087
+ * (family rules — rust/c/cpp/go all classify; zig did NOT). */
2088
+ classifyEvidenceTier(relationships) {
2089
+ for (const r of relationships) {
2090
+ const md = r.metadata ?? (r.metadata = {});
2091
+ if (md.evidenceClass) continue;
2092
+ const t = String(r.type ?? "");
2093
+ const conf = typeof r.confidence === "number" ? r.confidence : 1;
2094
+ if (t === "contains") {
2095
+ md.evidenceClass = "structural";
2096
+ continue;
2097
+ }
2098
+ if (t === "references") {
2099
+ md.evidenceClass = md.relationshipType === "declared_type" ? "reference_qualified" : "import_based";
2100
+ continue;
2101
+ }
2102
+ if (t === "instantiates") {
2103
+ md.evidenceClass = conf >= 0.9 && md.isExternalTarget === false ? "structural" : "inferred";
2104
+ continue;
2105
+ }
2106
+ if (t === "imports" || t === "exports") {
2107
+ md.evidenceClass = "import_based";
2108
+ continue;
2109
+ }
2110
+ if (t === "mixes_in") {
2111
+ md.evidenceClass = "structural";
2112
+ continue;
2113
+ }
2114
+ md.evidenceClass = "usage_contextual";
2115
+ }
2116
+ }
2117
+ /**
2118
+ * Z65 — TARGET SCOPE: state, per edge, whether the target name is a symbol THIS
2119
+ * file publishes, and with which kind.
2120
+ *
2121
+ * WHY. A zig relationship carries six keys and both endpoints are NAME STRINGS.
2122
+ * `src/lsp/impact.ts` obtains inbound edges by scanning outgoing edges and
2123
+ * MATCHING TARGETS — no inbound edge is stored — so an edge's name is all a
2124
+ * reverse walk has, and a name two declarations share is attributed to one of
2125
+ * them by a rule nobody chose. Measured over the whole 3,834-file zig corpus
2126
+ * (draw `sha256:2f3545f9…`, production `parseFile`, 392,550 edges):
2127
+ * **265,887 edges (67.73%) name a target 2+ published symbols share**, and of
2128
+ * those **151,004 (56.79%) have a candidate in the edge's OWN file** — a tie a
2129
+ * per-file rule breaks soundly, because within one file the analyzer knows which
2130
+ * declaration it published.
2131
+ *
2132
+ * WHAT IT ADDS. `metadata.targetScope`, plus `targetKind` when the file
2133
+ * publishes exactly one kind under that name:
2134
+ * `file-local` the file publishes a symbol with this exact name. A
2135
+ * consumer resolves the edge to THAT symbol and needs no
2136
+ * cross-file guess.
2137
+ * `file-external` the file publishes none. The edge names something outside
2138
+ * it — a `std.*` declaration, a builtin, an import path — and
2139
+ * no per-file rule can say which. Stated, not guessed.
2140
+ * `scope-marker` the endpoint is one of the literal scope markers
2141
+ * (`(global)`, `(file)`, `(declared-type)`), which is a fact
2142
+ * about the edge's provenance and not a symbol name.
2143
+ *
2144
+ * It adds NO edge, removes NO edge, and changes NO count — it is a label on
2145
+ * edges that already exist, derived from this analyzer's own output rather than
2146
+ * from a guess about the rest of the repository. A consumer that ignores it
2147
+ * behaves exactly as before, which is why it cannot regress a budgeted lane.
2148
+ *
2149
+ * THE LIMIT, stated because it is the part that cannot be fixed here. This says
2150
+ * whether the target is IN this file, not whether it is the declaration the
2151
+ * author meant: `const Self = @This();` is a file-local name, and 240 edges
2152
+ * resolve against it, while `keepSymbol`'s UNIVERSAL denylist
2153
+ * (`src/ast/pool-worker.ts:449`, shared, §8) refuses to publish ANY symbol
2154
+ * named `Self` — so those edges are labelled `file-external` even though the
2155
+ * name is right there in the file. The denylist is the defect, and it is not
2156
+ * this lane's file.
2157
+ */
2158
+ annotateTargetScope(symbols, relationships) {
2159
+ const kindsByName = /* @__PURE__ */ new Map();
2160
+ for (const s of symbols) {
2161
+ const n = String(s.name ?? "");
2162
+ if (!n) continue;
2163
+ const addKey = (k) => {
2164
+ const set = kindsByName.get(k) ?? /* @__PURE__ */ new Set();
2165
+ set.add(String(s.kind ?? ""));
2166
+ kindsByName.set(k, set);
2167
+ };
2168
+ addKey(n);
2169
+ const qualified = publishedSpelling(s);
2170
+ if (qualified !== n) addKey(qualified);
2171
+ }
2172
+ const MARKERS = /* @__PURE__ */ new Set(["(global)", "(file)", "(declared-type)", "(import)", "(local)"]);
2173
+ for (const r of relationships) {
2174
+ const md = r.metadata ?? (r.metadata = {});
2175
+ if (md.targetScope) continue;
2176
+ const t = String(r.target ?? "");
2177
+ if (MARKERS.has(t)) {
2178
+ md.targetScope = "scope-marker";
2179
+ continue;
2180
+ }
2181
+ const kinds = kindsByName.get(t);
2182
+ if (!kinds) {
2183
+ md.targetScope = "file-external";
2184
+ continue;
2185
+ }
2186
+ md.targetScope = "file-local";
2187
+ if (kinds.size === 1) md.targetKind = [...kinds][0];
2188
+ else md.targetKindCount = kinds.size;
2189
+ }
2190
+ }
2191
+ /** The `const X = @This();` binding of a file, or null when the file does not
2192
+ * name its own container. Read off node types only: a direct
2193
+ * `variable_declaration` child of the root whose value is a `builtin_function`
2194
+ * carrying the `builtin_identifier` `@This`.
2195
+ *
2196
+ * The NAME is `namedChild(0)` when that is an `identifier`, NOT
2197
+ * `childForFieldName("name")`: this grammar declares no `name` field on
2198
+ * `variable_declaration` — the walk reads a variable declaration's name the
2199
+ * same way, and a probe that used the field lookup reported 0 bindings on a
2200
+ * source that carries one. `container_field` DOES declare `name`, which is
2201
+ * why `zigFileLevelField` can use the field.
2202
+ *
2203
+ * Null is a real answer and not a fallback: 10 of the 411 corpus files that
2204
+ * declare file-level fields never bind `@This()`, and for those the owner of
2205
+ * those fields is not nameable from the file. */
2206
+ zigThisBindingName(root) {
2207
+ for (let i = 0; i < root.namedChildCount; i++) {
2208
+ const decl = root.namedChild(i);
2209
+ if (!decl || decl.type !== "variable_declaration") continue;
2210
+ for (let j = 0; j < decl.namedChildCount; j++) {
2211
+ const value = decl.namedChild(j);
2212
+ if (!value || value.type !== "builtin_function") continue;
2213
+ const isThis = this.anyChild(value, (n) => n.type === "builtin_identifier" && n.text === "@This");
2214
+ if (!isThis) continue;
2215
+ const name = decl.namedChildCount > 0 ? decl.namedChild(0) : null;
2216
+ if (name && name.type === "identifier") return name.text;
2217
+ }
2218
+ }
2219
+ return null;
2220
+ }
2221
+ /** Publish one `container_field` whose direct parent is the file's root — a
2222
+ * member of the struct the FILE is, per `zigThisBindingName`.
2223
+ *
2224
+ * The kind is decided by the field's own `type` FIELD, which is this
2225
+ * analyzer's discriminator between a struct field and an enum member
2226
+ * (`:2180`): the grammar uses one node type for `a: u8` and for `north`, and
2227
+ * only the former carries a type. A named container gets that answer from
2228
+ * its kind instead; a file-level container has no published kind, because
2229
+ * `const X = @This()` publishes `X` as a `Type` whether the body is a struct
2230
+ * or an enum, so the field's own field is the only evidence there is.
2231
+ *
2232
+ * The owner is set ONLY when the file names it. The 10 corpus files that
2233
+ * declare file-level fields without a `@This()` binding publish the field
2234
+ * with no owner: the declaration is real and its reads then resolve, while
2235
+ * no `contains` edge is emitted for it, because that edge would need a
2236
+ * source symbol and asserting one would be inventing the container's name.
2237
+ *
2238
+ * The name guards are the SAME three the named-container branch applies, and
2239
+ * they are load-bearing here rather than defensive: the corpus's three
2240
+ * untyped file-level fields are all in `zig/test/cases/compile_errors/`
2241
+ * (`file_level_tuple.zig` — a file-level tuple, which Zig rejects), and their
2242
+ * text holds a TYPE where a name belongs (`u32`, `comptime u8 = 123`). A
2243
+ * symbol named `u32` is a fabrication, so they are refused exactly as an
2244
+ * unnamed `extern struct` member is. */
2245
+ zigFileLevelField(node, ownerName, symbols) {
2246
+ const nameField = this.namedFieldOf(node, "name");
2247
+ const nameNode = nameField && nameField.type === "identifier" ? nameField : this.anyChild(node, (n) => n.type === "identifier");
2248
+ const fieldName = nameNode ? nameNode.text : "";
2249
+ if (!fieldName || fieldName === "_" || fieldName.length >= 200) return;
2250
+ const typeField = this.typeFieldOf(node);
2251
+ symbols.push({
2252
+ name: fieldName,
2253
+ kind: typeField ? ASTEngine.SymbolKind.Property : ASTEngine.SymbolKind.EnumMember,
2254
+ visibility: "public",
2255
+ range: this.nodeRange(node),
2256
+ scope: ownerName ?? void 0,
2257
+ ownerName: ownerName ?? void 0,
2258
+ ownerKind: ownerName ? ASTEngine.SymbolKind.Type : void 0,
2259
+ returnType: typeField ? this.nodeText(typeField) : void 0,
2260
+ metadata: {
2261
+ zigField: true,
2262
+ zigFileLevelField: true,
2263
+ ...typeField ? {} : { zigEnumMember: true }
2264
+ }
2265
+ });
2266
+ }
2267
+ pushChildren(node, walkStack, container, inFunction, anonContainer) {
2268
+ for (let i = node.namedChildCount - 1; i >= 0; i--) {
2269
+ const child = node.namedChild(i);
2270
+ if (child) walkStack.push({ node: child, container, inFunction, anonContainer });
2271
+ }
2272
+ }
2273
+ // ─── PURE HELPER FUNCTIONS ───
2274
+ nodeRange(node) {
2275
+ return {
2276
+ startLine: node.startPosition.row + 1,
2277
+ startColumn: node.startPosition.column + 1,
2278
+ endLine: node.endPosition.row + 1,
2279
+ endColumn: node.endPosition.column + 1,
2280
+ startIndex: node.startIndex,
2281
+ endIndex: node.endIndex
2282
+ };
2283
+ }
2284
+ nodeText(node) {
2285
+ if (!node) return "";
2286
+ return node.text;
2287
+ }
2288
+ anyChild(node, predicate) {
2289
+ for (let i = 0; i < node.namedChildCount; i++) {
2290
+ const child = node.namedChild(i);
2291
+ if (child && predicate(child)) return child;
2292
+ }
2293
+ return null;
2294
+ }
2295
+ /**
2296
+ * Z24 (pass 24): the DECLARATION NAME `@field` reads, when it is spelled as a string.
2297
+ *
2298
+ * ## The construct
2299
+ *
2300
+ * Zig can read a container's member by name-as-text rather than by identifier:
2301
+ *
2302
+ * @field(p, "x") // the field `x` of whatever `p` holds
2303
+ * @field(Point, "z") // the field `z` of the type `Point`
2304
+ *
2305
+ * Measured over the whole zig corpus (`validation/m14/fixtures/zig/reflection-builtin-probe.mts`,
2306
+ * 8 shards, 3,848/3,848 files, 0 parse traps): **779 `@field` and 210 `@hasDecl` call
2307
+ * sites over 242 files**, of which **206 name a declaration as a string**. Before this
2308
+ * pass the analyzer read none of them — `reflection-firing.mts` drove the production
2309
+ * `parseFile` over all 70 files carrying such a site and found **0** edges attributed to
2310
+ * a reflection reason code. The 52 sites whose line carried an edge naming the same
2311
+ * declaration decompose, by the analyzer's own reason codes, into `zig_field_access` 39,
2312
+ * `zig_function_call` 10, `zig_export` 3 and `identifier_usage` 1 — all different
2313
+ * constructs that share a line, since a relationship carries `line_number` and no range.
2314
+ *
2315
+ * Measured AFTER this pass, same instrument, same 70 files: **18 of the 22 named `@field`
2316
+ * sites are represented by a `zig_field_reflection` edge**, and 7 of those 18 have the edge
2317
+ * on their OWN line. The gap between 18 and 7 is Z52's post-pass, which collapses one `uses`
2318
+ * edge per (source, target) keeping the higher confidence: `zig/test/behavior/member_func.zig:77,78`
2319
+ * name `one` and `two`, the reflection edge for each is emitted, and the dot-access edge the
2320
+ * same file already published at lines 39 and 62 wins the tie at the same 0.75. The
2321
+ * site-level number alone would have read 7 and filed a working derivation as 11 misses.
2322
+ *
2323
+ * ## Why `@field` and NOT `@hasDecl`
2324
+ *
2325
+ * `@field(S, "a")` READS `S.a`: the corpus compiles, so `S` has that member and the code
2326
+ * consumes its value. That is exactly the claim the `zig_field_access` pass already makes
2327
+ * for `S.a` written with dots, so it is emitted as `uses` at the same confidence, with a
2328
+ * distinct reason code so the two derivations stay auditable apart.
2329
+ *
2330
+ * `@hasDecl(S, "a")` is an EXISTENCE TEST. Its whole purpose is to ask whether `a` is
2331
+ * there, and the surrounding code handles both answers (`if (@hasDecl(T, "opt")) … else
2332
+ * …`). A hard `uses`/`references` edge would assert a dependency the source deliberately
2333
+ * does not take, which is the fabrication this workstream exists to prevent, so it is NOT
2334
+ * emitted. Its measured population is published instead: 184 named sites, and 128 of the
2335
+ * 184 name a declaration this corpus does not publish at all (a foreign container, a type
2336
+ * parameter, a translate-c struct), so even a permissive reading would have produced 128
2337
+ * edges the graph cannot walk to.
2338
+ *
2339
+ * ## Why the name must be identifier-shaped
2340
+ *
2341
+ * `@field(anyerror, "Expected closing ]")` is a legal Zig read of an error-set member, and
2342
+ * Z18 already recorded what publishing such a text as a bare target does: it produced
2343
+ * targets like `@"ziglib"` for names that were legitimate. The same guard applies — a
2344
+ * `string_content` carrying a character outside `[A-Za-z0-9_]` is an error name, not a
2345
+ * member this file declares, and publishing it would add an edge whose target is junk.
2346
+ * Measured: **2 of the 22 named `@field` sites** carry a non-identifier-shaped name
2347
+ * (`zig/lib/std/fmt.zig:1550,1557`, both `"Expected closing ]"`) and **both are declined**.
2348
+ *
2349
+ * ## WHAT THE SHAPE GUARD DOES NOT DO, because a comment that claimed otherwise was wrong
2350
+ *
2351
+ * An earlier revision of this header read "3 of the 22 resolve to nothing and all 3 are
2352
+ * declined, which is what the arity measurement independently found". The two numbers are
2353
+ * 2 and 3 and they are not the same set. The third zero-resolving site is `NotFound` at
2354
+ * `zig/test/behavior/error.zig:897`, `@field(anyerror, "NotFound")`, and its name IS
2355
+ * identifier-shaped, so it is EMITTED — with `targetScope: file-external`, because nothing in
2356
+ * that 3,000-line file publishes `NotFound`. So the guard is not an error-set detector: it
2357
+ * declines error names containing punctuation and lets identifier-shaped error names
2358
+ * through. That is the intended behaviour and it is stated rather than papered over:
2359
+ *
2360
+ * - `error.NotFound` IS a legitimate declaration name in Zig, so the edge states a TRUE
2361
+ * dependency — on a name this particular file's corpus does not publish.
2362
+ * - `anyerror` is a `builtin_type` node, not an `identifier`, so its member namespace is
2363
+ * the inferred error set of the whole PROGRAM: unbounded, and bindable by no per-file
2364
+ * rule.
2365
+ * - Suppressing it instead would be inconsistent with the sibling pass, which publishes
2366
+ * `@typeInfo(u8).Int` → `Int` under the same `file-external` label, and `Int` equally
2367
+ * resolves to nothing in its own file. Dropping one unresolvable read and keeping the
2368
+ * other would be a fit to a single test line, not a rule.
2369
+ *
2370
+ * So the honest count is: of the 7 `zig_field_reflection` edges the corpus produces, **6
2371
+ * carry a target published in their own file and exactly 1 does not**
2372
+ * (`zig/test/behavior/error.zig:897`), and it says so on the edge.
2373
+ *
2374
+ * No regex: the shape test is a Set of node types and the character test is a
2375
+ * character-code range, both the file's existing zero-regex discipline.
2376
+ */
2377
+ zigReflectionFieldName(node) {
2378
+ if (node.type !== "builtin_function") return null;
2379
+ const bid = this.anyChild(node, (c) => c.type === "builtin_identifier");
2380
+ if (!bid || bid.text !== "@field") return null;
2381
+ const args = this.anyChild(node, (c) => c.type === "arguments");
2382
+ if (!args) return null;
2383
+ let stringArg = null;
2384
+ let seen = 0;
2385
+ for (let i = 0; i < args.namedChildCount; i++) {
2386
+ const a = args.namedChild(i);
2387
+ if (!a) continue;
2388
+ seen++;
2389
+ if (seen === 2 && a.type === "string") stringArg = a;
2390
+ }
2391
+ if (!stringArg) return null;
2392
+ const content = this.anyChild(stringArg, (c) => c.type === "string_content");
2393
+ if (!content) return null;
2394
+ const text = content.text;
2395
+ if (!text) return null;
2396
+ const isStart = (c) => c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 95;
2397
+ const isRest = (c) => isStart(c) || c >= 48 && c <= 57;
2398
+ if (!isStart(text.charCodeAt(0))) return null;
2399
+ for (let i = 1; i < text.length; i++) {
2400
+ if (!isRest(text.charCodeAt(i))) return null;
2401
+ }
2402
+ return content;
2403
+ }
2404
+ // The `pub`/`export`/`inline` modifiers are anonymous token children
2405
+ // (`variable_declaration [pub:"pub"] [const:"const"]`) — pure AST, never
2406
+ // source-text scanning.
2407
+ hasToken(node, token) {
2408
+ const count = node.childCount;
2409
+ for (let i = 0; i < count; i++) {
2410
+ const c = node.child(i);
2411
+ if (c && !c.isNamed && c.type === token) return true;
2412
+ }
2413
+ return false;
2414
+ }
2415
+ getAllNamedChildren(node) {
2416
+ const children = [];
2417
+ for (let i = 0; i < node.namedChildCount; i++) {
2418
+ const child = node.namedChild(i);
2419
+ if (child) children.push(child);
2420
+ }
2421
+ return children;
2422
+ }
2423
+ };
2424
+ export {
2425
+ RealZigAnalyzer,
2426
+ ZIG_DECLARED_TYPE_BUILTINS,
2427
+ stripZigQuoting,
2428
+ zigFileScopeNames,
2429
+ zigImportAliases
2430
+ };