@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,2311 @@
1
+ import {
2
+ findDeclaringParent
3
+ } from "./chunk-OWISBAOF.js";
4
+ import {
5
+ ASTEngine
6
+ } from "./chunk-V44PGPA6.js";
7
+ import "./chunk-BQ3OVQ3P.js";
8
+ import "./chunk-SK6YVHFZ.js";
9
+ import "./chunk-2BSKD7O3.js";
10
+ import "./chunk-KAET47ZL.js";
11
+ import "./chunk-KCDECRJY.js";
12
+ import "./chunk-MLKGABMK.js";
13
+
14
+ // src/ast/real-swift-analyzer.ts
15
+ var ENUM_RAW_VALUE_TYPES = /* @__PURE__ */ new Set([
16
+ "String",
17
+ "Character",
18
+ "Int",
19
+ "Int8",
20
+ "Int16",
21
+ "Int32",
22
+ "Int64",
23
+ "UInt",
24
+ "UInt8",
25
+ "UInt16",
26
+ "UInt32",
27
+ "UInt64",
28
+ "Float",
29
+ "Float32",
30
+ "Float64",
31
+ "Double"
32
+ ]);
33
+ var MEMBER_OWNER_KINDS = /* @__PURE__ */ new Set([
34
+ ASTEngine.SymbolKind.Class,
35
+ ASTEngine.SymbolKind.Struct,
36
+ ASTEngine.SymbolKind.Interface,
37
+ ASTEngine.SymbolKind.Enum,
38
+ ASTEngine.SymbolKind.Actor,
39
+ ASTEngine.SymbolKind.Extension
40
+ ]);
41
+ var LOCAL_SCOPE_BODIES = /* @__PURE__ */ new Set([
42
+ "function_body",
43
+ "computed_property",
44
+ "computed_getter",
45
+ "computed_setter",
46
+ "willset_didset_block",
47
+ "willset_clause",
48
+ "didset_clause",
49
+ "lambda_literal"
50
+ ]);
51
+ var SCOPE_RESET_DECLARATIONS = /* @__PURE__ */ new Set([
52
+ "class_declaration",
53
+ "protocol_declaration",
54
+ "function_declaration",
55
+ "protocol_function_declaration",
56
+ "init_declaration"
57
+ ]);
58
+ var BUILTIN_BARE_CALLS = /* @__PURE__ */ new Set([
59
+ "print",
60
+ "debugPrint",
61
+ "assert",
62
+ "precondition",
63
+ "fatalError",
64
+ "String",
65
+ "Int",
66
+ "Double",
67
+ "Float",
68
+ "Bool",
69
+ "Array",
70
+ "Dictionary",
71
+ "Set",
72
+ "Optional",
73
+ "Any",
74
+ "Void",
75
+ "Character",
76
+ "Data",
77
+ "Date",
78
+ // Fixed-width stdlib numerics (SWIFTD16): trivial value construction, same
79
+ // class as Int/Double — filtered for consistency (Int8-64/UInt8-64 were
80
+ // previously missed while Int was filtered; corpus: UInt8 244 / Int64 72 /
81
+ // UInt16 47 bare constructions per swift-nio 200-file census).
82
+ "UInt",
83
+ "UInt8",
84
+ "UInt16",
85
+ "UInt32",
86
+ "UInt64",
87
+ "Int8",
88
+ "Int16",
89
+ "Int32",
90
+ "Int64",
91
+ // `defer { }` parses as call_expression(simple_identifier "defer", lambda_literal)
92
+ // in the pinned grammar — it is a control keyword, never a real function call.
93
+ "defer",
94
+ // SW-E (ultimate review): `type(of:)` is a stdlib free function like
95
+ // print/assert — a bare `type` call is the metatype query, never user
96
+ // code. Member reads (`x.type` is invalid Swift) are unaffected.
97
+ "type"
98
+ ]);
99
+ var SWIFT_FRAMEWORK_ORDER = ["SwiftUI", "Combine", "UIKit", "Foundation"];
100
+ var SWIFT_FRAMEWORK_PATTERNS = {
101
+ SwiftUI: ["declarative_ui"],
102
+ Combine: ["reactive_streams"],
103
+ UIKit: ["ui_framework"],
104
+ Foundation: ["base_library"]
105
+ };
106
+ var RealSwiftAnalyzer = class {
107
+ analyze(rootNode, content, filePath, context) {
108
+ const symbols = [];
109
+ const imports = [];
110
+ const relationships = [];
111
+ const exports = [];
112
+ const patternsObj = {};
113
+ const frameworksObj = {};
114
+ const metadata = {
115
+ linesOfCode: 0,
116
+ performanceScore: 100,
117
+ architecturalInsights: [],
118
+ detectedFrameworks: [],
119
+ patterns: []
120
+ };
121
+ if (!rootNode) return { symbols, imports, relationships, exports, metadata, errors: [] };
122
+ if (rootNode.hasError) {
123
+ const errStack = [rootNode];
124
+ while (errStack.length > 0) {
125
+ const n = errStack.pop();
126
+ if (n.type === "ERROR" || n.isMissing) {
127
+ ;
128
+ metadata.errors = metadata.errors || [];
129
+ metadata.errors.push({
130
+ message: `Syntax ${n.type} detected. The Brain Engine is bypassing this broken node.`,
131
+ range: {
132
+ startLine: n.startPosition.row + 1,
133
+ startColumn: n.startPosition.column + 1,
134
+ endLine: n.endPosition.row + 1,
135
+ endColumn: n.endPosition.column + 1,
136
+ startIndex: n.startIndex,
137
+ endIndex: n.endIndex
138
+ },
139
+ severity: "warning"
140
+ });
141
+ }
142
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
143
+ const child = n.namedChild(i);
144
+ if (child) errStack.push(child);
145
+ }
146
+ }
147
+ }
148
+ let loc = 1;
149
+ for (let i = 0; i < content.length; i++) {
150
+ if (content[i] === "\n") loc++;
151
+ }
152
+ metadata.linesOfCode = loc;
153
+ const overrideTasks = [];
154
+ const classHierarchy = /* @__PURE__ */ new Map();
155
+ const symbolMap = /* @__PURE__ */ new Map();
156
+ const inFileKinds = /* @__PURE__ */ new Map();
157
+ this.collectDeclaredKinds(rootNode, inFileKinds);
158
+ const cross = this.crossFileTables(context, classHierarchy);
159
+ const emittedCalls = /* @__PURE__ */ new Set();
160
+ const callSiteCounts = /* @__PURE__ */ new Map();
161
+ const callSiteLines = /* @__PURE__ */ new Map();
162
+ const emittedUses = /* @__PURE__ */ new Set();
163
+ const emittedContains = /* @__PURE__ */ new Set();
164
+ const enumCtorCandidates = [];
165
+ const superByClass = /* @__PURE__ */ new Map();
166
+ const reExports = [];
167
+ const moduleSource = this.moduleLeafName(filePath);
168
+ const emittedExportNames = /* @__PURE__ */ new Set();
169
+ const stack = [
170
+ { node: rootNode, parentSymbol: null, typeName: null, localScope: false }
171
+ ];
172
+ while (stack.length > 0) {
173
+ const popped = stack.pop();
174
+ if (!popped) continue;
175
+ const { node: curr, parentSymbol, localScope } = popped;
176
+ let newParentSymbol = parentSymbol;
177
+ let newTypeName = popped.typeName;
178
+ const nominalRecovery = curr.type === "ERROR" && this.isNominalRecovery(curr);
179
+ const childLocalScope = SCOPE_RESET_DECLARATIONS.has(curr.type) || nominalRecovery ? false : LOCAL_SCOPE_BODIES.has(curr.type) ? true : localScope;
180
+ if (curr.type === "import_declaration") {
181
+ this.handleImport(curr, imports, frameworksObj, reExports);
182
+ } else if (curr.type === "protocol_declaration") {
183
+ newParentSymbol = this.handleProtocol(curr, symbols, relationships, parentSymbol, patternsObj, classHierarchy, localScope);
184
+ if (newParentSymbol) newTypeName = newParentSymbol.name;
185
+ } else if (curr.type === "class_declaration") {
186
+ newParentSymbol = this.handleDeclaration(curr, symbols, relationships, parentSymbol, patternsObj, classHierarchy, inFileKinds, cross.kinds, superByClass, localScope);
187
+ if (newParentSymbol) newTypeName = newParentSymbol.name;
188
+ } else if (nominalRecovery) {
189
+ newParentSymbol = this.handleDeclaration(curr, symbols, relationships, parentSymbol, patternsObj, classHierarchy, inFileKinds, cross.kinds, superByClass, localScope);
190
+ if (newParentSymbol) newTypeName = newParentSymbol.name;
191
+ } else if (curr.type === "function_declaration" || curr.type === "protocol_function_declaration") {
192
+ newParentSymbol = this.handleFunction(curr, symbols, relationships, parentSymbol, overrideTasks, context?.overrideCompanion, localScope);
193
+ } else if (curr.type === "init_declaration") {
194
+ const vis = this.getVisibility(curr);
195
+ const meta = {};
196
+ const funcAttrs = this.extractAttributes(curr);
197
+ if (funcAttrs.length > 0) meta.annotations = funcAttrs;
198
+ const sym = this.createSymbol("constructor", ASTEngine.SymbolKind.Constructor, curr, parentSymbol, symbols, meta, vis, this.getDeclaredVisibility(curr), localScope);
199
+ this.collectFunctionMeta(curr, meta, sym);
200
+ newParentSymbol = sym;
201
+ } else if (curr.type === "deinit_declaration" || curr.type === "subscript_declaration") {
202
+ } else if (curr.type === "property_declaration" || curr.type === "protocol_property_declaration") {
203
+ this.handleVariable(curr, symbols, parentSymbol, newTypeName, frameworksObj, patternsObj, localScope);
204
+ } else if (curr.type === "enum_entry") {
205
+ if (!localScope && parentSymbol && parentSymbol.kind === ASTEngine.SymbolKind.Enum) {
206
+ let caseNode = null;
207
+ for (let i = 0; i < curr.namedChildCount; i++) {
208
+ const c = curr.namedChild(i);
209
+ if (!c || c.type === "modifiers" || c.type === "attribute") continue;
210
+ caseNode = c;
211
+ if (c.type === "simple_identifier" || c.type === "identifier") break;
212
+ }
213
+ const caseName = caseNode ? caseNode.text : curr.text;
214
+ if (caseName && caseName !== "case") {
215
+ this.createSymbol(caseName, ASTEngine.SymbolKind.EnumMember, caseNode ?? curr, parentSymbol, symbols, { enumCase: true });
216
+ }
217
+ }
218
+ } else if (curr.type === "typealias_declaration") {
219
+ const nameNode = this.findChildByType(curr, "type_identifier") || this.firstNamedChild(curr);
220
+ if (nameNode && (nameNode.type === "type_identifier" || nameNode.type === "simple_identifier" || nameNode.type === "identifier")) {
221
+ this.createSymbol(nameNode.text, ASTEngine.SymbolKind.Type, curr, parentSymbol, symbols, { isTypeAlias: true }, this.getVisibility(curr), this.getDeclaredVisibility(curr), localScope);
222
+ }
223
+ } else if (curr.type === "associatedtype_declaration") {
224
+ const nameNode = this.findChildByType(curr, "type_identifier") || this.firstNamedChild(curr);
225
+ if (nameNode && (nameNode.type === "type_identifier" || nameNode.type === "simple_identifier" || nameNode.type === "identifier")) {
226
+ this.createSymbol(nameNode.text, ASTEngine.SymbolKind.Type, curr, parentSymbol, symbols, { isAssociatedType: true }, this.getVisibility(curr), this.getDeclaredVisibility(curr), localScope);
227
+ }
228
+ } else if (curr.type === "call_expression") {
229
+ this.handleCall(curr, relationships, parentSymbol, emittedCalls, enumCtorCandidates, superByClass, callSiteCounts, callSiteLines);
230
+ } else if (curr.type === "navigation_expression") {
231
+ this.handleSelfUse(curr, relationships, newTypeName, parentSymbol, emittedUses);
232
+ this.handleMemberUses(curr, relationships, parentSymbol, newTypeName, emittedUses);
233
+ } else if (curr.type === "key_path_expression") {
234
+ this.handleKeyPathUses(curr, relationships, parentSymbol, newTypeName, emittedUses);
235
+ }
236
+ if (parentSymbol === null && !localScope && this.isExportingDeclaration(curr)) {
237
+ this.emitDeclarationExport(curr, moduleSource, relationships, exports, emittedExportNames);
238
+ }
239
+ if (curr.type === "type_constraints") {
240
+ patternsObj["Generics with Where Clauses"] = true;
241
+ } else if (curr.type === "willset_clause" || curr.type === "didset_clause" || curr.type === "willset_didset_block") {
242
+ patternsObj["Property Observers (willSet/didSet)"] = true;
243
+ } else if (curr.type === "call_expression" && this.isDeferCall(curr)) {
244
+ patternsObj["Defer (Cleanup)"] = true;
245
+ } else if (curr.type === "do_statement") {
246
+ patternsObj["Do-Catch Blocks"] = true;
247
+ } else if (curr.type === "getter_specifier" || curr.type === "setter_specifier") {
248
+ patternsObj["Computed Properties"] = true;
249
+ } else if (curr.type === "availability_condition") {
250
+ patternsObj["Platform Availability Checks"] = true;
251
+ } else if (curr.type === "macro_declaration") {
252
+ patternsObj["Swift Macros"] = true;
253
+ } else if (curr.type === "lambda_literal") {
254
+ patternsObj["Closures"] = true;
255
+ }
256
+ if (curr.type === "try_operator" || curr.type === "catch_block" || curr.type === "control_transfer_statement" && this.hasChildType(curr, "throw_keyword")) {
257
+ patternsObj["Error Handling"] = true;
258
+ }
259
+ if (curr.type === "await_expression" || curr.type === "async") {
260
+ patternsObj["Async/Await"] = true;
261
+ }
262
+ if (curr.type === "optional_type" || curr.type === "guard_statement" || curr.type === "navigation_expression" && this.hasAnonToken(curr, "?") || (curr.type === "if_statement" || curr.type === "while_statement") && this.hasChildType(curr, "value_binding_pattern")) {
263
+ patternsObj["Optional Handling"] = true;
264
+ }
265
+ if (curr.type === "type_parameters") {
266
+ patternsObj["Generics"] = true;
267
+ }
268
+ if (curr.type === "if_statement" || curr.type === "for_statement" || curr.type === "while_statement" || curr.type === "repeat_while_statement" || curr.type === "switch_statement") {
269
+ if (!metadata.cyclomaticComplexity) metadata.cyclomaticComplexity = 1;
270
+ metadata.cyclomaticComplexity++;
271
+ }
272
+ if (curr.type === "modifiers") {
273
+ for (let i = 0; i < curr.namedChildCount; i++) {
274
+ const mod = curr.namedChild(i);
275
+ if (!mod) continue;
276
+ if (mod.type === "ownership_modifier")
277
+ patternsObj["ARC Memory Management"] = true;
278
+ if (mod.type === "property_behavior_modifier" || mod.text === "lazy")
279
+ patternsObj["Lazy Initialization"] = true;
280
+ if (mod.type === "inheritance_modifier" || mod.text === "final")
281
+ patternsObj["Final/Sealed"] = true;
282
+ }
283
+ }
284
+ for (let i = curr.childCount - 1; i >= 0; i--) {
285
+ const child = curr.child(i);
286
+ if (child) stack.push({ node: child, parentSymbol: newParentSymbol, typeName: newTypeName, localScope: childLocalScope });
287
+ }
288
+ }
289
+ this.handleBareUses(rootNode, relationships, symbols, emittedUses);
290
+ if (enumCtorCandidates.length > 0) {
291
+ const enumCases = /* @__PURE__ */ new Set();
292
+ for (const s of symbols) {
293
+ if (s.kind === ASTEngine.SymbolKind.EnumMember && s.ownerName) {
294
+ enumCases.add(`${s.ownerName}.${s.name}`);
295
+ }
296
+ }
297
+ if (enumCases.size > 0) {
298
+ const retiredSites = /* @__PURE__ */ new Set();
299
+ const retiredLines = /* @__PURE__ */ new Map();
300
+ const constructions = [];
301
+ for (const cand of enumCtorCandidates) {
302
+ const key = cand.head ?? cand.owner;
303
+ if (!key || !enumCases.has(`${key}.${cand.leaf}`)) continue;
304
+ if (cand.removeCalls) {
305
+ const siteKey = `${cand.source}\0${cand.leaf}`;
306
+ const left = (callSiteCounts.get(siteKey) ?? 0) - 1;
307
+ callSiteCounts.set(siteKey, left);
308
+ if (typeof cand.line === "number") {
309
+ const set = retiredLines.get(siteKey);
310
+ if (set) set.add(cand.line);
311
+ else retiredLines.set(siteKey, /* @__PURE__ */ new Set([cand.line]));
312
+ }
313
+ if (left <= 0) retiredSites.add(siteKey);
314
+ }
315
+ constructions.push({ source: cand.source, target: key, line: cand.line });
316
+ }
317
+ if (retiredSites.size > 0) {
318
+ for (let i = relationships.length - 1; i >= 0; i--) {
319
+ const r = relationships[i];
320
+ if (r.type !== ASTEngine.RelationshipType.Calls) continue;
321
+ if (!retiredSites.has(`${r.source}\0${String(r.target)}`)) continue;
322
+ relationships.splice(i, 1);
323
+ }
324
+ }
325
+ if (callSiteLines.size > 0) {
326
+ for (const r of relationships) {
327
+ if (r.type !== ASTEngine.RelationshipType.Calls) continue;
328
+ const key = `${r.source}\0${String(r.target)}`;
329
+ const lines = callSiteLines.get(key);
330
+ if (!lines) continue;
331
+ const retired = retiredLines.get(key);
332
+ if (!retired) continue;
333
+ let kept;
334
+ for (const l of lines) {
335
+ if (!retired.has(l)) {
336
+ kept = l;
337
+ break;
338
+ }
339
+ }
340
+ if (typeof kept === "number") r.line_number = kept;
341
+ }
342
+ }
343
+ for (const c of constructions) {
344
+ const ekey = `${c.source}\0instantiates\0${c.target}`;
345
+ if (emittedCalls.has(ekey)) continue;
346
+ emittedCalls.add(ekey);
347
+ relationships.push({
348
+ type: ASTEngine.RelationshipType.Instantiates,
349
+ source: c.source,
350
+ target: c.target,
351
+ isExternal: true,
352
+ weight: 0.7,
353
+ confidence: 0.95,
354
+ // The CONVERTED call site's line, carried on the candidate.
355
+ ...typeof c.line === "number" ? { line_number: c.line } : {},
356
+ metadata: { isEnumConstruction: true }
357
+ });
358
+ }
359
+ }
360
+ }
361
+ for (const p in patternsObj) {
362
+ metadata.patterns.push(p);
363
+ }
364
+ const emittedFrameworks = SWIFT_FRAMEWORK_ORDER.filter((f) => frameworksObj[f] === true);
365
+ const unlistedFrameworks = [];
366
+ for (const f in frameworksObj) {
367
+ if (frameworksObj[f] === true && SWIFT_FRAMEWORK_ORDER.indexOf(f) === -1) unlistedFrameworks.push(f);
368
+ }
369
+ unlistedFrameworks.sort();
370
+ for (let i = 0; i < unlistedFrameworks.length; i++) emittedFrameworks.push(unlistedFrameworks[i]);
371
+ metadata.detectedFrameworks = emittedFrameworks;
372
+ metadata.framework = emittedFrameworks.length > 0 ? emittedFrameworks[0] : void 0;
373
+ metadata.frameworkPatterns = emittedFrameworks.map((f) => ({
374
+ framework: f,
375
+ patterns: SWIFT_FRAMEWORK_PATTERNS[f] ?? ["usage"]
376
+ }));
377
+ for (const s of symbols) {
378
+ if (s.kind === ASTEngine.SymbolKind.Method || s.kind === ASTEngine.SymbolKind.Constructor || s.kind === ASTEngine.SymbolKind.Function) {
379
+ const owner = s.ownerName || "root";
380
+ symbolMap.set(`${owner}.${s.name}`, s);
381
+ }
382
+ }
383
+ for (const s of symbols) {
384
+ const _owner = s.ownerName || s.scope;
385
+ if (_owner && s.name && s.kind !== ASTEngine.SymbolKind.Extension) {
386
+ const key = `${_owner}\0${s.name}`;
387
+ if (!emittedContains.has(key)) {
388
+ emittedContains.add(key);
389
+ relationships.push({
390
+ type: ASTEngine.RelationshipType.Contains,
391
+ source: _owner,
392
+ target: s.name,
393
+ isExternal: false,
394
+ weight: 1,
395
+ confidence: 1,
396
+ // The DECLARATION's own line (the member's range start).
397
+ ...typeof s.range?.startLine === "number" ? { line_number: s.range.startLine } : {},
398
+ metadata: {}
399
+ });
400
+ }
401
+ }
402
+ }
403
+ for (const imp of imports) {
404
+ if (imp.module) {
405
+ relationships.push({
406
+ type: ASTEngine.RelationshipType.References,
407
+ source: "root",
408
+ target: imp.module,
409
+ isExternal: true,
410
+ weight: 0.9,
411
+ confidence: 1,
412
+ // The import statement's own line (handleImport records the range).
413
+ ...typeof imp.range?.startLine === "number" ? { line_number: imp.range.startLine } : {},
414
+ // `isImportNameReference` is the marker the `references` family uses to
415
+ // say this edge names a MODULE brought into scope rather than a symbol
416
+ // the graph holds (INCOMING_SWIFT JOB 2, preserved by DERIV_EXPORTS_SWIFT4).
417
+ // It is why leaving `references` in place beside `imports` is not a
418
+ // duplicate claim: the two carry different markers and answer different
419
+ // questions ("what names came into scope" vs "what does this file depend on").
420
+ //
421
+ // M14 SWIFT RESIDUAL — THE CLAIM MOVES ONTO THE KEY THE ARBITER READS.
422
+ // `isExternal: true` has been set on this RELATIONSHIP since INCOMING_SWIFT
423
+ // JOB 2, and `externalClaimOf` (script/m14/incoming-rule.ts:396) reads
424
+ // `rel.metadata` ONLY — so the statement was invisible and the lane's `extOK`
425
+ // column read 0 while its own sentence above said "a MODULE brought into scope
426
+ // rather than a symbol the graph holds". Measured on the 400-file bounded run
427
+ // (`m14-swift-cause-probe.mts`): 916 `references` + 900 `imports` edges are
428
+ // unresolvable, every one of them naming a module no file in the run declares
429
+ // (Foundation, UIKit, XCTest, Flutter, NIOCore …). Publishing the SAME claim
430
+ // inside `metadata` is ROUTING, not a new judgement. It cannot move a
431
+ // resolution: `classifyEdge` consults a claim only where the run declares no
432
+ // symbol of that name (`:1147`), and with a declaration present it reads the
433
+ // claim "to RECORD it, never to move the outcome" (`:1188-1194`) — so
434
+ // `resolved`, `collRes` and `amb(decl)` are untouched in both directions.
435
+ metadata: { importModule: imp.module, isImportNameReference: true, isExternal: true }
436
+ });
437
+ }
438
+ }
439
+ {
440
+ const seen = /* @__PURE__ */ new Set();
441
+ for (const re of reExports) {
442
+ const key = `${moduleSource}\0${re.module}`;
443
+ if (seen.has(key)) continue;
444
+ seen.add(key);
445
+ const reLine = this.nodeLine(re.node);
446
+ relationships.push({
447
+ type: ASTEngine.RelationshipType.Exports,
448
+ source: moduleSource,
449
+ target: re.module,
450
+ isExternal: true,
451
+ weight: 0.9,
452
+ confidence: 1,
453
+ // The MODULE NAME's own line — the site a reader checks, the same node
454
+ // the ImportInfo row and the references edge are lined at (
455
+ // `@_exported\nimport Basic` lines the edge at the module name, 11,
456
+ // never the attribute's line, 10).
457
+ ...typeof reLine === "number" ? { line_number: reLine } : {},
458
+ // M14 SWIFT RESIDUAL: `@_exported import M` re-exports a MODULE, and the
459
+ // relationship already says `isExternal: true` (`:768`) — but the arbiter
460
+ // reads `metadata`. Same routing, same safety, as the `references`/`imports`
461
+ // edges: a claim is consulted only where the run declares no symbol of that
462
+ // name, so no resolution can move. The `exports` INFO row below keeps its
463
+ // metadata unchanged: it is a declaration, not an external claim.
464
+ metadata: { exportForm: re.form, reExport: true, reexportsModule: re.module, isExternal: true }
465
+ });
466
+ if (!emittedExportNames.has(re.module)) {
467
+ emittedExportNames.add(re.module);
468
+ exports.push({
469
+ name: re.module,
470
+ type: "module",
471
+ range: {
472
+ startLine: re.node.startPosition.row + 1,
473
+ startColumn: re.node.startPosition.column + 1,
474
+ endLine: re.node.endPosition.row + 1,
475
+ endColumn: re.node.endPosition.column + 1,
476
+ startIndex: re.node.startIndex,
477
+ endIndex: re.node.endIndex
478
+ },
479
+ metadata: { exportForm: re.form, reExport: true, reexportsModule: re.module }
480
+ });
481
+ }
482
+ }
483
+ }
484
+ const emittedImports = /* @__PURE__ */ new Set();
485
+ const scopedImportsPerModule = /* @__PURE__ */ new Map();
486
+ for (const imp of imports) {
487
+ const imd = imp.metadata ?? {};
488
+ const im = typeof imd.module === "string" ? imd.module : null;
489
+ if (!im || imd.isScopedImport !== true) continue;
490
+ scopedImportsPerModule.set(im, (scopedImportsPerModule.get(im) ?? 0) + 1);
491
+ }
492
+ for (const imp of imports) {
493
+ const md = imp.metadata ?? {};
494
+ const spelled = typeof imp.module === "string" ? imp.module : "";
495
+ const moduleName = typeof md.module === "string" && md.module ? md.module : spelled.split(".")[0] || null;
496
+ if (!moduleName || emittedImports.has(moduleName)) continue;
497
+ emittedImports.add(moduleName);
498
+ const scopedHere = scopedImportsPerModule.get(moduleName) ?? 0;
499
+ const edgeMd = {
500
+ ...md,
501
+ // `isImport` is the MARKER the completeness section-B inventory reads,
502
+ // so the kind is claimed twice over (go GO precedent) and the evidence
503
+ // tier stays import_based.
504
+ isImport: true,
505
+ isModuleDependency: true,
506
+ // #910: the importer is this FILE, so the indexer must be told this is
507
+ // a file-level edge rather than a scope placeholder named `root`.
508
+ fileLevel: true,
509
+ // M14 SWIFT RESIDUAL: the same module-external claim the `references` edge
510
+ // carries, on the key the arbiter reads — see the block at that edge above.
511
+ // `isExternal` was already `true` on this relationship (`:893`); `metadata` is
512
+ // the only place `externalClaimOf` looks.
513
+ isExternal: true
514
+ };
515
+ if (scopedHere > 1) {
516
+ delete edgeMd.scopedItem;
517
+ edgeMd.scopedItemCount = scopedHere;
518
+ }
519
+ relationships.push({
520
+ type: ASTEngine.RelationshipType.Imports,
521
+ source: "root",
522
+ target: moduleName,
523
+ isExternal: true,
524
+ weight: 0.9,
525
+ confidence: 1,
526
+ // The MODULE NAME's own line (handleImport records the name node's
527
+ // range), the same site the references edge names.
528
+ ...typeof imp.range?.startLine === "number" ? { line_number: imp.range.startLine } : {},
529
+ metadata: edgeMd
530
+ });
531
+ }
532
+ this.resolveOverrides(
533
+ overrideTasks,
534
+ classHierarchy,
535
+ symbolMap,
536
+ relationships,
537
+ context?.overrideCompanion ?? cross.companion,
538
+ cross.getParents(classHierarchy)
539
+ );
540
+ this.classifyEvidenceTier(relationships);
541
+ return {
542
+ symbols,
543
+ relationships,
544
+ imports,
545
+ exports,
546
+ metadata,
547
+ errors: metadata.errors || []
548
+ };
549
+ }
550
+ /** Pre-pass: record every protocol/class/struct/actor/enum/extension name. */
551
+ collectDeclaredKinds(rootNode, out) {
552
+ const stack = [rootNode];
553
+ let guard = 0;
554
+ while (stack.length > 0 && guard++ < 5e5) {
555
+ const n = stack.pop();
556
+ if (n.type === "protocol_declaration") {
557
+ const nm = this.findChildByType(n, "type_identifier");
558
+ if (nm) out.set(nm.text, "interface");
559
+ } else if (n.type === "class_declaration") {
560
+ const nm = this.findChildByType(n, "type_identifier") || this.findChildByType(n, "user_type");
561
+ if (nm) {
562
+ const leaf = nm.type === "user_type" && nm.namedChildCount > 0 ? nm.namedChild(0).text : nm.text;
563
+ if (this.hasAnonToken(n, "class")) out.set(leaf, "class");
564
+ else if (this.hasAnonToken(n, "struct")) out.set(leaf, "struct");
565
+ else if (this.hasAnonToken(n, "actor")) out.set(leaf, "actor");
566
+ }
567
+ }
568
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
569
+ const child = n.namedChild(i);
570
+ if (child) stack.push(child);
571
+ }
572
+ }
573
+ }
574
+ // ============================================================================
575
+ // EVIDENCE TIERS
576
+ // ============================================================================
577
+ /**
578
+ * Evidence-tier classification (managed-OO family contract — mirrors
579
+ * dart/csharp/java; M14 section D requires >=2 distinct evidenceClass
580
+ * values). Same rules as the family: structural (grammar clauses, conf
581
+ * 1.0) | usage_contextual (calls, mixin uses) | import_based (import
582
+ * directives) | inferred (confidence < 1 edges).
583
+ */
584
+ classifyEvidenceTier(relationships) {
585
+ for (const r of relationships) {
586
+ const type = r.type ?? r.relationshipType ?? "";
587
+ const md = r.metadata ?? {};
588
+ if (type === "contains") {
589
+ md.evidenceClass = "structural";
590
+ continue;
591
+ }
592
+ if (type === "imports" || type === "references") {
593
+ md.evidenceClass = "import_based";
594
+ continue;
595
+ }
596
+ if (type === "exports") {
597
+ md.evidenceClass = md.reExport === true ? "import_based" : "structural";
598
+ continue;
599
+ }
600
+ if (type === "calls" || type === "uses") {
601
+ md.evidenceClass = (r.confidence ?? 0) >= 0.85 ? "usage_contextual" : "inferred";
602
+ continue;
603
+ }
604
+ if (type === "instantiates" || type === "extends" || type === "implements") {
605
+ md.evidenceClass = (r.confidence ?? 0) >= 0.9 ? "structural" : "inferred";
606
+ continue;
607
+ }
608
+ if (type === "overrides") {
609
+ md.evidenceClass = (r.confidence ?? 0) >= 0.9 ? "structural" : "inferred";
610
+ continue;
611
+ }
612
+ md.evidenceClass = "usage_contextual";
613
+ }
614
+ }
615
+ // ==========================================================================
616
+ // Declaration handlers
617
+ // ==========================================================================
618
+ handleProtocol(node, symbols, relationships, parent, patternsObj, classHierarchy, localScope = false) {
619
+ patternsObj["Protocol-Oriented Programming"] = true;
620
+ const nameNode = this.findChildByType(node, "type_identifier") || this.firstNamedChild(node);
621
+ if (!nameNode) return parent;
622
+ const meta = { isProtocol: true, hasGenerics: this.hasChildType(node, "type_parameters") };
623
+ if (meta.hasGenerics) patternsObj["Generics"] = true;
624
+ const vis = this.getVisibility(node);
625
+ const symbol = this.createSymbol(nameNode.text, ASTEngine.SymbolKind.Interface, nameNode, parent, symbols, meta, vis, this.getDeclaredVisibility(node), localScope);
626
+ const baseSites = this.extractBaseSites(node);
627
+ const parents = [];
628
+ for (let i = 0; i < baseSites.length; i++) {
629
+ const base = baseSites[i].name;
630
+ if (base === "AnyObject" || base === "Any") continue;
631
+ parents.push(base);
632
+ relationships.push({
633
+ type: ASTEngine.RelationshipType.Extends,
634
+ source: symbol.name,
635
+ target: base,
636
+ isExternal: true,
637
+ weight: 1,
638
+ confidence: 1,
639
+ // The base SPECIFIER's own line in the inheritance clause.
640
+ line_number: this.nodeLine(baseSites[i].node),
641
+ metadata: { isImplements: false }
642
+ });
643
+ }
644
+ if (parents.length > 0) classHierarchy.set(symbol.name, parents);
645
+ return symbol;
646
+ }
647
+ handleDeclaration(node, symbols, relationships, parent, patternsObj, classHierarchy, inFileKinds, crossFileKinds, superByClass, localScope = false) {
648
+ let kind = ASTEngine.SymbolKind.Class;
649
+ const recoveredKind = node.type === "ERROR" ? this.recoveryKind(node) : null;
650
+ if (recoveredKind) {
651
+ kind = recoveredKind;
652
+ } else if (this.hasAnonToken(node, "actor")) {
653
+ kind = ASTEngine.SymbolKind.Actor;
654
+ } else if (this.hasAnonToken(node, "struct")) {
655
+ kind = ASTEngine.SymbolKind.Struct;
656
+ } else if (this.hasAnonToken(node, "enum")) {
657
+ kind = ASTEngine.SymbolKind.Enum;
658
+ } else if (this.hasAnonToken(node, "extension")) {
659
+ kind = ASTEngine.SymbolKind.Extension;
660
+ }
661
+ let nameNode = null;
662
+ const recoveredName = node.type === "ERROR" ? this.recoveryNameNode(node) : null;
663
+ if (recoveredName) {
664
+ nameNode = recoveredName;
665
+ } else if (kind === ASTEngine.SymbolKind.Extension) {
666
+ const ut = this.findChildByType(node, "user_type");
667
+ if (ut) {
668
+ nameNode = this.lastTypeIdentifier(ut);
669
+ } else {
670
+ nameNode = this.findDescendantTypeIdentifier(node);
671
+ }
672
+ } else {
673
+ nameNode = this.findChildByType(node, "type_identifier") || this.findChildByType(node, "user_type") || this.firstNamedChild(node);
674
+ }
675
+ if (!nameNode) return parent;
676
+ let rawName = nameNode.type === "user_type" && nameNode.namedChildCount > 0 ? nameNode.namedChild(0).text : nameNode.text;
677
+ if (kind === ASTEngine.SymbolKind.Extension && nameNode.type === "user_type") {
678
+ const leaf = this.lastTypeIdentifier(nameNode);
679
+ if (leaf) rawName = leaf.text;
680
+ }
681
+ const meta = {
682
+ hasGenerics: this.hasChildType(node, "type_parameters")
683
+ };
684
+ if (meta.hasGenerics) patternsObj["Generics"] = true;
685
+ if (kind === ASTEngine.SymbolKind.Struct) meta.isValueType = true;
686
+ if (kind === ASTEngine.SymbolKind.Enum) meta.isValueType = true;
687
+ const vis = this.getVisibility(node);
688
+ if (kind === ASTEngine.SymbolKind.Enum) {
689
+ const rawType = this.findEnumRawType(node);
690
+ if (rawType && ENUM_RAW_VALUE_TYPES.has(rawType)) meta.rawType = rawType;
691
+ }
692
+ const symbol = this.createSymbol(rawName, kind, nameNode, parent, symbols, meta, vis, this.getDeclaredVisibility(node), localScope);
693
+ const baseSites = this.extractBaseSites(node);
694
+ const bases = baseSites.map((b) => b.name);
695
+ const parents = [];
696
+ if (kind === ASTEngine.SymbolKind.Class || kind === ASTEngine.SymbolKind.Struct || kind === ASTEngine.SymbolKind.Actor) {
697
+ const firstIsSuperclass = kind === ASTEngine.SymbolKind.Class && (bases.length > 0 ? (inFileKinds.get(bases[0]) ?? crossFileKinds?.get(bases[0]) ?? "class") === "class" : true);
698
+ for (let i = 0; i < bases.length; i++) {
699
+ const base = bases[i];
700
+ parents.push(base);
701
+ const isExtends = firstIsSuperclass && i === 0;
702
+ if (isExtends && superByClass) superByClass.set(symbol.name, base);
703
+ relationships.push({
704
+ type: isExtends ? ASTEngine.RelationshipType.Extends : ASTEngine.RelationshipType.Implements,
705
+ source: symbol.name,
706
+ target: base,
707
+ isExternal: true,
708
+ weight: isExtends ? 1 : 0.9,
709
+ confidence: 1,
710
+ line_number: this.nodeLine(baseSites[i].node),
711
+ metadata: { isImplements: !isExtends }
712
+ });
713
+ }
714
+ if (parents.length > 0) classHierarchy.set(symbol.name, parents);
715
+ } else if (kind === ASTEngine.SymbolKind.Extension) {
716
+ for (let i = 0; i < baseSites.length; i++) {
717
+ const base = baseSites[i].name;
718
+ parents.push(base);
719
+ relationships.push({
720
+ type: ASTEngine.RelationshipType.Implements,
721
+ source: symbol.name,
722
+ target: base,
723
+ isExternal: true,
724
+ weight: 0.9,
725
+ confidence: 1,
726
+ line_number: this.nodeLine(baseSites[i].node),
727
+ metadata: { isImplements: true }
728
+ });
729
+ }
730
+ } else if (kind === ASTEngine.SymbolKind.Enum) {
731
+ const skipFirst = bases.length > 0 && ENUM_RAW_VALUE_TYPES.has(bases[0]);
732
+ for (let i = skipFirst ? 1 : 0; i < bases.length; i++) {
733
+ const base = bases[i];
734
+ relationships.push({
735
+ type: ASTEngine.RelationshipType.Implements,
736
+ source: symbol.name,
737
+ target: base,
738
+ isExternal: true,
739
+ weight: 0.9,
740
+ confidence: 1,
741
+ line_number: this.nodeLine(baseSites[i].node),
742
+ metadata: { isImplements: true }
743
+ });
744
+ }
745
+ }
746
+ if (kind === ASTEngine.SymbolKind.Class || kind === ASTEngine.SymbolKind.Struct || kind === ASTEngine.SymbolKind.Extension) {
747
+ if (bases.includes("View")) {
748
+ this.extractRenderRelationships(node, symbol.name, relationships);
749
+ }
750
+ }
751
+ return symbol;
752
+ }
753
+ /**
754
+ * `renders` — the rendered component named by a View body's TOP-LEVEL DSL
755
+ * call. Swift's rendering entry point is `var body: some View { … }` on a
756
+ * type conforming to `View`; the component the body's outermost
757
+ * `call_expression` names is what the type renders (the dart lane's
758
+ * `build`-returns-`Widget` shape, `real-dart-analyzer.ts:4274`). Deeper
759
+ * constructions stay their existing `calls`/`instantiates` rows — only the
760
+ * top-level expression is the rendered tree's root, and every other
761
+ * construction inside it is an ARGUMENT or a CHILD, not the render.
762
+ */
763
+ extractRenderRelationships(node, className, relationships) {
764
+ const classBody = this.findChildByType(node, "class_body");
765
+ if (!classBody) return;
766
+ for (let i = 0; i < classBody.namedChildCount; i++) {
767
+ const member = classBody.namedChild(i);
768
+ if (!member || member.type !== "property_declaration") continue;
769
+ const pattern = this.findChildByType(member, "pattern");
770
+ if (!pattern || pattern.text !== "body") continue;
771
+ const typeAnno = this.findChildByType(member, "type_annotation");
772
+ if (!typeAnno || typeAnno.text.trim() !== ": some View") continue;
773
+ const computed = this.findChildByType(member, "computed_property");
774
+ if (!computed) continue;
775
+ const statements = this.findChildByType(computed, "statements");
776
+ if (!statements) continue;
777
+ const expr = statements.namedChild(0);
778
+ if (!expr || expr.type !== "call_expression") continue;
779
+ const callee = this.findChildByType(expr, "simple_identifier") || this.findChildByType(expr, "identifier");
780
+ if (!callee) continue;
781
+ const target = callee.text;
782
+ const firstCode = target.length > 0 ? target.charCodeAt(0) : 0;
783
+ if (!(firstCode >= 65 && firstCode <= 90) || target === "View" || target === "Self") continue;
784
+ relationships.push({
785
+ source: className,
786
+ target,
787
+ type: ASTEngine.RelationshipType.Renders,
788
+ confidence: 0.9,
789
+ line_number: this.nodeLine(callee),
790
+ metadata: { isRenderHost: true, renderHost: "body", renderTarget: target }
791
+ });
792
+ }
793
+ }
794
+ handleFunction(node, symbols, relationships, parent, overrideTasks, companion, localScope = false) {
795
+ const nameNode = this.findChildByType(node, "simple_identifier") || this.findChildByType(node, "identifier");
796
+ const opName = nameNode ? null : this.operatorName(node);
797
+ if (!nameNode && !opName) return parent;
798
+ const isMethod = !!parent && MEMBER_OWNER_KINDS.has(parent.kind);
799
+ const kind = isMethod ? ASTEngine.SymbolKind.Method : ASTEngine.SymbolKind.Function;
800
+ const vis = this.getVisibility(node);
801
+ const meta = {
802
+ hasGenerics: this.hasChildType(node, "type_parameters")
803
+ };
804
+ const funcAttrs = this.extractAttributes(node);
805
+ if (funcAttrs.length > 0) meta.annotations = funcAttrs;
806
+ const symbol = this.createSymbol(opName ?? nameNode.text, kind, opName ? node : nameNode, parent, symbols, meta, vis, this.getDeclaredVisibility(node), localScope);
807
+ this.collectFunctionMeta(node, meta, symbol);
808
+ symbol.complexity = this.calculateCyclomaticComplexity(node);
809
+ if (this.hasDescendantAnonToken(node, "override") && parent) {
810
+ overrideTasks.push({
811
+ fqn: `${parent.name}.${symbol.name}`,
812
+ methodName: symbol.name,
813
+ ownerFqn: parent.name,
814
+ line: symbol.range?.startLine
815
+ });
816
+ }
817
+ const returnNode = this.findReturnType(node);
818
+ if (returnNode) symbol.returnType = returnNode.text;
819
+ return symbol;
820
+ }
821
+ // ==========================================================================
822
+ // Variable / call / uses handlers
823
+ // ==========================================================================
824
+ handleVariable(node, symbols, parent, typeName, frameworksObj, patternsObj, localScope = false) {
825
+ if (localScope) return;
826
+ if (parent && !MEMBER_OWNER_KINDS.has(parent.kind)) return;
827
+ const vis = this.getVisibility(node);
828
+ const annotations = [];
829
+ for (let i = 0; i < node.childCount; i++) {
830
+ const c = node.child(i);
831
+ if (c && (c.type === "modifiers" || c.type === "attribute")) {
832
+ for (let j = 0; j < c.namedChildCount; j++) {
833
+ const mod = c.namedChild(j);
834
+ if (!mod) continue;
835
+ if (mod.type === "attribute") {
836
+ const text = mod.text;
837
+ annotations.push(text);
838
+ if (text.indexOf("@State") !== -1 || text.indexOf("@Binding") !== -1 || text.indexOf("@Environment") !== -1 || text.indexOf("@ObservedObject") !== -1) {
839
+ frameworksObj["SwiftUI"] = true;
840
+ patternsObj["Property Wrappers"] = true;
841
+ }
842
+ if (text.indexOf("@Published") !== -1) {
843
+ frameworksObj["Combine"] = true;
844
+ patternsObj["Property Wrappers"] = true;
845
+ }
846
+ if (text.indexOf("@MainActor") !== -1 || text.indexOf("@Sendable") !== -1 || text.indexOf("@globalActor") !== -1) {
847
+ patternsObj["Swift Concurrency"] = true;
848
+ }
849
+ if (text.startsWith("@")) patternsObj["Property Wrappers"] = true;
850
+ }
851
+ }
852
+ }
853
+ }
854
+ const meta = {};
855
+ if (annotations.length > 0) meta.annotations = annotations;
856
+ const kind = parent ? ASTEngine.SymbolKind.Property : ASTEngine.SymbolKind.Variable;
857
+ const patternNodes = [];
858
+ for (let i = 0; i < node.namedChildCount; i++) {
859
+ const c = node.namedChild(i);
860
+ if (c && c.type === "pattern") patternNodes.push(c);
861
+ }
862
+ if (patternNodes.length > 0) {
863
+ for (const pn of patternNodes) {
864
+ const idNode = this.findChildByType(pn, "simple_identifier") || this.findChildByType(pn, "identifier") || this.firstNamedChild(pn);
865
+ if (idNode) {
866
+ this.createSymbol(idNode.text, kind, idNode, parent, symbols, meta, vis, this.getDeclaredVisibility(node), localScope);
867
+ }
868
+ }
869
+ } else {
870
+ const idNode = this.findChildByType(node, "simple_identifier") || this.findChildByType(node, "identifier");
871
+ if (idNode) {
872
+ this.createSymbol(idNode.text, kind, idNode, parent, symbols, meta, vis, this.getDeclaredVisibility(node));
873
+ }
874
+ }
875
+ }
876
+ handleCall(node, relationships, parent, emittedCalls, enumCtorCandidates, superByClass, callSiteCounts, callSiteLines) {
877
+ if (this.isCasePatternRecovery(node)) return;
878
+ const source = parent ? parent.name : "root";
879
+ const funcNode = this.findChildByType(node, "simple_identifier") || this.findChildByType(node, "identifier") || this.findChildByType(node, "navigation_expression");
880
+ if (!funcNode) {
881
+ if (enumCtorCandidates) {
882
+ const pre = this.findChildByType(node, "prefix_expression");
883
+ const dotFirst = pre ? pre.child(0) : null;
884
+ if (pre && dotFirst && !dotFirst.isNamed && dotFirst.type === ".") {
885
+ let leaf = null;
886
+ for (let i = 0; i < pre.namedChildCount; i++) {
887
+ const c = pre.namedChild(i);
888
+ if (c && (c.type === "simple_identifier" || c.type === "identifier")) {
889
+ leaf = c.text;
890
+ break;
891
+ }
892
+ }
893
+ const owner = parent?.ownerName ?? null;
894
+ if (leaf && owner) {
895
+ enumCtorCandidates.push({ source, head: null, owner, leaf, removeCalls: false, line: this.nodeLine(node) });
896
+ }
897
+ }
898
+ }
899
+ return;
900
+ }
901
+ if (this.isSubscriptRead(node)) return;
902
+ if (funcNode.type === "navigation_expression") {
903
+ const leaf = this.navigationLeaf(funcNode);
904
+ if (!leaf || leaf === "$0") return;
905
+ if (leaf === "init") {
906
+ const recv = this.navigationReceiver(funcNode);
907
+ if (recv === "super" || recv === "self" || recv === "Self") {
908
+ const owner = parent ? MEMBER_OWNER_KINDS.has(parent.kind) ? parent.name : parent.ownerName ?? null : null;
909
+ let target2 = null;
910
+ if ((recv === "self" || recv === "Self") && owner) target2 = owner;
911
+ else if (recv === "super" && owner && superByClass) target2 = superByClass.get(owner) ?? null;
912
+ if (target2) {
913
+ const key = `${source}\0instantiates\0${target2}`;
914
+ if (!emittedCalls.has(key)) {
915
+ emittedCalls.add(key);
916
+ relationships.push({
917
+ type: ASTEngine.RelationshipType.Instantiates,
918
+ source,
919
+ target: target2,
920
+ isExternal: true,
921
+ weight: 0.7,
922
+ confidence: 0.95,
923
+ // The DELEGATION call site's line (`self.init()` / `super.init()`).
924
+ ...typeof this.nodeLine(node) === "number" ? { line_number: this.nodeLine(node) } : {},
925
+ metadata: { isSuperInit: recv === "super", isSelfInit: recv !== "super" }
926
+ });
927
+ }
928
+ }
929
+ return;
930
+ }
931
+ }
932
+ this.emitCall(relationships, source, leaf, emittedCalls, callSiteCounts, this.nodeLine(this.navigationLeafNode(funcNode)), callSiteLines);
933
+ if (enumCtorCandidates) {
934
+ const chain = this.navigationChain(funcNode);
935
+ const receiver = chain.length >= 2 ? chain[chain.length - 2] : null;
936
+ if (receiver && this.isTypeName(receiver) && receiver !== "Self" && receiver !== "super" && receiver !== "this") {
937
+ enumCtorCandidates.push({ source, head: receiver, owner: null, leaf, removeCalls: true, line: this.nodeLine(this.navigationLeafNode(funcNode)) });
938
+ } else if (receiver === "Self") {
939
+ const owner = parent?.ownerName ?? null;
940
+ if (owner) enumCtorCandidates.push({ source, head: null, owner, leaf, removeCalls: true, line: this.nodeLine(this.navigationLeafNode(funcNode)) });
941
+ }
942
+ }
943
+ return;
944
+ }
945
+ const target = this.stripNameQuoting(funcNode.text);
946
+ if (!target || BUILTIN_BARE_CALLS.has(target)) return;
947
+ if (target.startsWith("XCT")) return;
948
+ const firstChar = target.charCodeAt(0);
949
+ const isCapitalized = firstChar >= 65 && firstChar <= 90;
950
+ if (isCapitalized) {
951
+ const key = `${source}\0instantiates\0${target}`;
952
+ if (!emittedCalls.has(key)) {
953
+ emittedCalls.add(key);
954
+ relationships.push({
955
+ type: ASTEngine.RelationshipType.Instantiates,
956
+ source,
957
+ target,
958
+ isExternal: true,
959
+ weight: 0.7,
960
+ confidence: 1,
961
+ // The construction site's callee node line.
962
+ ...typeof this.nodeLine(funcNode) === "number" ? { line_number: this.nodeLine(funcNode) } : {},
963
+ metadata: { isInstantiation: true }
964
+ });
965
+ }
966
+ } else {
967
+ this.emitCall(relationships, source, target, emittedCalls, callSiteCounts, this.nodeLine(funcNode), callSiteLines);
968
+ }
969
+ }
970
+ emitCall(relationships, source, target, emittedCalls, callSiteCounts, line, callSiteLines) {
971
+ if (callSiteCounts) {
972
+ const siteKey = `${source}\0${target}`;
973
+ callSiteCounts.set(siteKey, (callSiteCounts.get(siteKey) ?? 0) + 1);
974
+ if (typeof line === "number") {
975
+ const arr = callSiteLines?.get(siteKey);
976
+ if (arr) arr.push(line);
977
+ else callSiteLines?.set(siteKey, [line]);
978
+ }
979
+ }
980
+ const key = `${source}\0calls\0${target}`;
981
+ if (emittedCalls.has(key)) return;
982
+ emittedCalls.add(key);
983
+ relationships.push({
984
+ type: ASTEngine.RelationshipType.Calls,
985
+ source,
986
+ target,
987
+ isExternal: true,
988
+ weight: 1,
989
+ confidence: 0.95,
990
+ // The CALLEE node's own line — the site a reader checks.
991
+ ...typeof line === "number" ? { line_number: line } : {},
992
+ metadata: {}
993
+ });
994
+ }
995
+ handleSelfUse(node, relationships, typeName, parent, emittedUses) {
996
+ {
997
+ let a = node.parent;
998
+ while (a && (a.type === "directly_assignable_expression" || a.type === "postfix_expression" || a.type === "navigation_expression" || a.type === "navigation_suffix")) a = a.parent;
999
+ if (a && a.type === "assignment") {
1000
+ const lhs = a.namedChild(0);
1001
+ if (lhs && lhs.startIndex <= node.startIndex && lhs.endIndex >= node.endIndex) return;
1002
+ }
1003
+ }
1004
+ const hasSelf = this.hasSelfReceiver(node);
1005
+ if (!hasSelf) return;
1006
+ const par = node.parent;
1007
+ if (par && par.type === "call_expression" && !this.isSubscriptRead(par)) return;
1008
+ const leaf = this.navigationLeaf(node);
1009
+ if (!leaf) return;
1010
+ const source = (parent && (parent.kind === ASTEngine.SymbolKind.Method || parent.kind === ASTEngine.SymbolKind.Constructor) ? parent.name : null) || typeName || (parent?.ownerName ? parent.ownerName : null) || parent?.name || "root";
1011
+ const key = `${source}\0uses\0${leaf}`;
1012
+ if (emittedUses.has(key)) return;
1013
+ emittedUses.add(key);
1014
+ relationships.push({
1015
+ type: ASTEngine.RelationshipType.Uses,
1016
+ source,
1017
+ target: leaf,
1018
+ isExternal: false,
1019
+ weight: 0.7,
1020
+ confidence: 0.95,
1021
+ // The leaf segment's own node line (`self.a.b` -> the `b` node).
1022
+ ...typeof this.nodeLine(this.navigationLeafNode(node)) === "number" ? { line_number: this.nodeLine(this.navigationLeafNode(node)) } : {},
1023
+ metadata: { isFieldAccess: true }
1024
+ });
1025
+ }
1026
+ /**
1027
+ * M49 #754 (CONV-SW-1): BARE member reads. The main walk only emits uses
1028
+ * for `self.x` navigation reads, so a plain `return count` on a declared
1029
+ * property emitted NOTHING while `return self.count` did (probe-confirmed
1030
+ * live: `uses edges on x: 0`). Mirrors java #739 / dart #752. Gate A: the
1031
+ * name must resolve to a DECLARED MEMBER of the enclosing type (the symbol
1032
+ * table's ownerName IS the enclosing type); gate B (CGD15): a name the
1033
+ * CURRENT function binds locally is not a member read — without it every
1034
+ * local/loop variable fabricates an edge.
1035
+ */
1036
+ handleBareUses(rootNode, relationships, symbols, emittedUses) {
1037
+ const membersByOwner = /* @__PURE__ */ new Map();
1038
+ for (const s of symbols) {
1039
+ if (!s.ownerName) continue;
1040
+ let set = membersByOwner.get(s.ownerName);
1041
+ if (!set) {
1042
+ set = /* @__PURE__ */ new Set();
1043
+ membersByOwner.set(s.ownerName, set);
1044
+ }
1045
+ set.add(s.name);
1046
+ }
1047
+ const NOT_A_READ = /* @__PURE__ */ new Set([
1048
+ "pattern",
1049
+ "parameter",
1050
+ "function_declaration",
1051
+ "protocol_function_declaration",
1052
+ "class_declaration",
1053
+ "protocol_declaration",
1054
+ "struct_declaration",
1055
+ "enum_declaration",
1056
+ "extension_declaration",
1057
+ "actor_declaration",
1058
+ "typealias_declaration",
1059
+ "associatedtype_declaration",
1060
+ "type_annotation",
1061
+ "user_type",
1062
+ "import_declaration",
1063
+ "attribute",
1064
+ "enum_entry",
1065
+ "value_argument_label"
1066
+ ]);
1067
+ const TYPE_DECLS = /* @__PURE__ */ new Set([
1068
+ "class_declaration",
1069
+ "protocol_declaration",
1070
+ "struct_declaration",
1071
+ "enum_declaration",
1072
+ "extension_declaration",
1073
+ "actor_declaration"
1074
+ ]);
1075
+ const FUNC_SCOPES = /* @__PURE__ */ new Set([
1076
+ "function_declaration",
1077
+ "protocol_function_declaration",
1078
+ "init_declaration",
1079
+ "deinit_declaration"
1080
+ ]);
1081
+ const VALUE_SCOPES = /* @__PURE__ */ new Set(["lambda_literal", "property_declaration"]);
1082
+ const isWrite = (n) => {
1083
+ let a = n.parent;
1084
+ while (a && (a.type === "directly_assignable_expression" || a.type === "postfix_expression" || a.type === "navigation_expression" || a.type === "navigation_suffix")) a = a.parent;
1085
+ if (a && a.type === "assignment") {
1086
+ const lhs = a.namedChild(0);
1087
+ return !!lhs && lhs.startIndex <= n.startIndex && lhs.endIndex >= n.endIndex;
1088
+ }
1089
+ return false;
1090
+ };
1091
+ const ownNameSpan = (scope) => {
1092
+ if (scope.type === "property_declaration") {
1093
+ const pat = this.findChildByType(scope, "pattern");
1094
+ const id = pat ? this.findChildByType(pat, "simple_identifier") : null;
1095
+ return id ? id.startIndex : -1;
1096
+ }
1097
+ return -1;
1098
+ };
1099
+ const collectBound = (node, out) => {
1100
+ const own = ownNameSpan(node);
1101
+ const w = [node];
1102
+ while (w.length > 0) {
1103
+ const m = w.pop();
1104
+ if (m.type === "simple_identifier" && m.startIndex !== own) {
1105
+ const pt = m.parent?.type ?? "";
1106
+ if (pt === "parameter" || pt === "pattern") out.add(m.text);
1107
+ }
1108
+ for (let i = m.namedChildCount - 1; i >= 0; i--) {
1109
+ const c = m.namedChild(i);
1110
+ if (c) w.push(c);
1111
+ }
1112
+ }
1113
+ };
1114
+ const extendLocals = (locals, scope) => {
1115
+ const bound = /* @__PURE__ */ new Set();
1116
+ collectBound(scope, bound);
1117
+ if (bound.size === 0) return locals;
1118
+ const next = new Set(locals);
1119
+ for (const nm of bound) next.add(nm);
1120
+ return next;
1121
+ };
1122
+ const walk = (n, ty, fn, locals) => {
1123
+ const t = n.type;
1124
+ let cTy = ty;
1125
+ let cFn = fn;
1126
+ let cLocals = locals;
1127
+ if (TYPE_DECLS.has(t)) {
1128
+ const nameNode = this.firstDescendantOfType(n, "type_identifier") || this.firstDescendantOfType(n, "simple_identifier");
1129
+ if (nameNode) {
1130
+ cTy = nameNode.text;
1131
+ cFn = null;
1132
+ }
1133
+ } else if (this.isNominalRecovery(n)) {
1134
+ const nm = this.recoveryName(n);
1135
+ if (nm) {
1136
+ cTy = nm;
1137
+ cFn = null;
1138
+ }
1139
+ } else if (FUNC_SCOPES.has(t)) {
1140
+ cFn = t === "init_declaration" ? "constructor" : this.findChildByType(n, "simple_identifier")?.text ?? cFn;
1141
+ cLocals = extendLocals(locals, n);
1142
+ } else if (VALUE_SCOPES.has(t)) {
1143
+ cLocals = extendLocals(locals, n);
1144
+ }
1145
+ if (t === "simple_identifier") {
1146
+ const txt = n.text;
1147
+ const pt = n.parent?.type ?? "";
1148
+ let lookup = txt;
1149
+ if (lookup.startsWith("$")) {
1150
+ const rest = lookup.slice(1);
1151
+ if (!rest || this.isAllDigits(rest)) {
1152
+ for (let i = 0; i < n.namedChildCount; i++) {
1153
+ const c = n.namedChild(i);
1154
+ if (c) walk(c, cTy, cFn, cLocals);
1155
+ }
1156
+ return;
1157
+ }
1158
+ lookup = rest;
1159
+ }
1160
+ lookup = this.stripNameQuoting(lookup);
1161
+ const isMember = !!cTy && (membersByOwner.get(cTy)?.has(lookup) ?? false);
1162
+ const isImplicitMember = pt === "prefix_expression" && (n.parent?.text ?? "").startsWith(".");
1163
+ if (!NOT_A_READ.has(pt) && !isImplicitMember && pt !== "navigation_suffix" && pt !== "call_expression" && !isWrite(n) && !cLocals.has(txt) && !cLocals.has(lookup) && isMember) {
1164
+ const source = cFn || cTy || "root";
1165
+ const key = `${source}\0uses\0${lookup}`;
1166
+ if (!emittedUses.has(key)) {
1167
+ emittedUses.add(key);
1168
+ relationships.push({
1169
+ type: ASTEngine.RelationshipType.Uses,
1170
+ source,
1171
+ target: lookup,
1172
+ isExternal: false,
1173
+ weight: 0.7,
1174
+ confidence: 0.6,
1175
+ line_number: this.nodeLine(n),
1176
+ metadata: { isBareRead: true }
1177
+ });
1178
+ }
1179
+ }
1180
+ }
1181
+ for (let i = 0; i < n.namedChildCount; i++) {
1182
+ const c = n.namedChild(i);
1183
+ if (c) walk(c, cTy, cFn, cLocals);
1184
+ }
1185
+ };
1186
+ walk(rootNode, null, null, /* @__PURE__ */ new Set());
1187
+ }
1188
+ // ==========================================================================
1189
+ // Override resolution (Java/C# shared machinery)
1190
+ // ==========================================================================
1191
+ /**
1192
+ * The PROJECT's cross-file declaration tables, read from
1193
+ * `AnalyzeContext.projectIndex` when a caller supplied one.
1194
+ *
1195
+ * WHY THIS EXISTS, WITH THE NUMBER. `overrides` resolves in three passes
1196
+ * (exact in-file BFS -> companion-verified declaring parent -> first-parent
1197
+ * guess at confidence 0.5). Pass 2 needs BOTH a companion (owner -> declared
1198
+ * members) and a way to walk bases it cannot see in this file. Measured over
1199
+ * the 1,256-file draw with `script/m14/swift-overrides-premise-probe.ts`:
1200
+ *
1201
+ * tier edges parent declares the method
1202
+ * pass1 in-file (1.0) 71 71 (100.0%)
1203
+ * pass3 first-parent (0.5) 210 87 ( 41.4%)
1204
+ *
1205
+ * so the guess's premise — "the first hierarchy parent declares the method" —
1206
+ * fails for 123 of 210 edges (58.6%). swift-nio shows the mechanism:
1207
+ * `final class PipeChannel: BaseStreamSocketChannel<PipePair>` overriding
1208
+ * `connectSocket`, where `connectSocket` is declared two levels up, on
1209
+ * `BaseSocketChannel` (Sources/NIOPosix/BaseSocketChannel.swift:479). The
1210
+ * guess names the DIRECT base, which does not declare it.
1211
+ *
1212
+ * Pass 2 could not fix that, and the reason is a wiring fact rather than a
1213
+ * logic one: Pass 2 reads `context.overrideCompanion`, and
1214
+ * `src/ast/parallel-indexer.ts` builds the batch companion only for the
1215
+ * languages its `PROJECT_INDEX_EXTENSIONS` map lists — today `".sol"` — so no
1216
+ * Swift caller has ever supplied one. `src/ast/pool-worker.ts`'s `parse_batch`
1217
+ * supplies `context.projectIndex` instead, and the Dart lane already reads
1218
+ * that channel (`dartCompanionFor`). This method is the Swift equivalent: it
1219
+ * reads the channel production is designed to fill, so the moment the indexer
1220
+ * is told to build an index for `.swift`, this lane needs no further change.
1221
+ *
1222
+ * ADDITIVE AND INERT WHEN ABSENT. With no `projectIndex` — a single-file
1223
+ * parse, every fixture, the differential — `companion` is undefined, `kinds`
1224
+ * is `context.typeKinds` exactly as before, and `getParents` returns the
1225
+ * per-file hierarchy, so output is byte-identical to before this method
1226
+ * existed. Nothing here decides anything on its own: it only supplies the
1227
+ * cross-file answers the file cannot see.
1228
+ */
1229
+ crossFileTables(context, classHierarchy) {
1230
+ const pi = context?.projectIndex;
1231
+ const kinds = context?.typeKinds ?? (pi?.typeKinds instanceof Map ? pi.typeKinds : void 0);
1232
+ if (!pi || !(pi.typeMembers instanceof Map) || !(pi.typeBases instanceof Map)) {
1233
+ return { companion: void 0, kinds, getParents: (local) => (t) => local.get(t) ?? [] };
1234
+ }
1235
+ const cache = /* @__PURE__ */ new Map();
1236
+ const closure = (owner, seen = /* @__PURE__ */ new Set()) => {
1237
+ const hit = cache.get(owner);
1238
+ if (hit) return hit;
1239
+ const out = /* @__PURE__ */ new Set();
1240
+ if (seen.has(owner)) return out;
1241
+ seen.add(owner);
1242
+ for (const m of pi.typeMembers.get(owner) ?? []) out.add(m);
1243
+ for (const b of pi.typeBases.get(owner) ?? []) {
1244
+ for (const m of closure(b, seen)) out.add(m);
1245
+ }
1246
+ cache.set(owner, out);
1247
+ return out;
1248
+ };
1249
+ const companion = /* @__PURE__ */ new Map();
1250
+ for (const owner of pi.typeMembers.keys()) companion.set(owner, closure(owner));
1251
+ return {
1252
+ companion,
1253
+ kinds,
1254
+ // The project's bases first, the file's own second: a type declared in
1255
+ // THIS file is authoritative here, and a type the file never mentions can
1256
+ // only come from the index.
1257
+ getParents: (local) => (t) => {
1258
+ const localParents = local.get(t);
1259
+ if (localParents && localParents.length > 0) return localParents;
1260
+ const projectParents = pi.typeBases.get(t);
1261
+ return projectParents && projectParents.length > 0 ? projectParents : [];
1262
+ }
1263
+ };
1264
+ }
1265
+ resolveOverrides(tasks, classHierarchy, symbolMap, relationships, companion, getParents) {
1266
+ const parentsOf = getParents ?? ((t) => classHierarchy.get(t) ?? []);
1267
+ for (const task of tasks) {
1268
+ let targetFqn = null;
1269
+ let confidence = 1;
1270
+ let reasonCode;
1271
+ const queue = [];
1272
+ const ownerH = classHierarchy.get(task.ownerFqn);
1273
+ if (ownerH) {
1274
+ for (let i = 0; i < ownerH.length; i++) queue.push(ownerH[i]);
1275
+ }
1276
+ const seen = /* @__PURE__ */ new Set();
1277
+ while (queue.length > 0) {
1278
+ const parentName = queue.shift();
1279
+ if (seen.has(parentName)) continue;
1280
+ seen.add(parentName);
1281
+ const potentialTargetFqn = `${parentName}.${task.methodName}`;
1282
+ if (symbolMap.has(potentialTargetFqn)) {
1283
+ targetFqn = potentialTargetFqn;
1284
+ break;
1285
+ }
1286
+ for (const next of parentsOf(parentName)) queue.push(next);
1287
+ }
1288
+ if (!targetFqn && companion && ownerH && ownerH.length > 0) {
1289
+ const declaring = findDeclaringParent(task.methodName, ownerH, companion, parentsOf);
1290
+ if (declaring) {
1291
+ targetFqn = `${declaring}.${task.methodName}`;
1292
+ confidence = 0.9;
1293
+ reasonCode = "companion-index";
1294
+ }
1295
+ }
1296
+ if (!targetFqn && ownerH && ownerH.length > 0) {
1297
+ targetFqn = `${ownerH[0]}.${task.methodName}`;
1298
+ confidence = 0.5;
1299
+ reasonCode = "first-parent-guess";
1300
+ }
1301
+ if (targetFqn) {
1302
+ relationships.push({
1303
+ type: ASTEngine.RelationshipType.Overrides,
1304
+ source: task.fqn,
1305
+ target: targetFqn,
1306
+ isExternal: true,
1307
+ weight: 0.8,
1308
+ confidence,
1309
+ ...typeof task.line === "number" ? { line_number: task.line } : {},
1310
+ metadata: { relationshipType: "method_override", isSwiftOverride: true, ...reasonCode ? { reasonCode } : {} }
1311
+ });
1312
+ }
1313
+ }
1314
+ }
1315
+ // ==========================================================================
1316
+ // Symbol creation / helpers
1317
+ // ==========================================================================
1318
+ /**
1319
+ * #924 (swift half): Swift's keyword-escape quoting is NOT part of a name.
1320
+ * ``case `continue` `` declares the identifier `continue`; ``static var
1321
+ * `default` `` declares `default` — and every REFERENCE in the corpus spells
1322
+ * them without backticks (`flags: Flags = .default`), so a published name that
1323
+ * keeps the syntax is a name no reference can ever match. Measured at full
1324
+ * extent (`script/m14/tmp/sw-name-census.ts`): 29 symbols and 40 targets
1325
+ * carried the backticks. Exactly one surrounding pair is stripped; a name
1326
+ * that merely contains a backtick keeps it.
1327
+ */
1328
+ stripNameQuoting(text) {
1329
+ if (text.length >= 2 && text.charCodeAt(0) === 96 && text.charCodeAt(text.length - 1) === 96) {
1330
+ return text.slice(1, -1);
1331
+ }
1332
+ return text;
1333
+ }
1334
+ createSymbol(name, kind, node, parent, symbols, meta = {}, visibility = "internal", declaredVisibility = null, localScope = false) {
1335
+ name = this.stripNameQuoting(name);
1336
+ if (parent && parent.kind === ASTEngine.SymbolKind.Extension && declaredVisibility === null && parent.visibility) {
1337
+ visibility = parent.visibility;
1338
+ }
1339
+ const functionScoped = localScope || !!parent && (parent.kind === ASTEngine.SymbolKind.Function || parent.kind === ASTEngine.SymbolKind.Method || parent.kind === ASTEngine.SymbolKind.Constructor);
1340
+ const owner = functionScoped ? null : parent;
1341
+ const symbol = {
1342
+ id: parent ? `${parent.id}.${name}` : name,
1343
+ name,
1344
+ kind,
1345
+ range: {
1346
+ startLine: node.startPosition.row + 1,
1347
+ startColumn: node.startPosition.column + 1,
1348
+ endLine: node.endPosition.row + 1,
1349
+ endColumn: node.endPosition.column + 1,
1350
+ startIndex: node.startIndex,
1351
+ endIndex: node.endIndex
1352
+ },
1353
+ parentId: owner ? owner.id : void 0,
1354
+ ownerName: owner ? owner.name : void 0,
1355
+ parent: owner ? owner.name : void 0,
1356
+ metadata: meta,
1357
+ visibility
1358
+ };
1359
+ if (meta.isAsync) symbol.isAsync = true;
1360
+ if (meta.throws) symbol.throws = "true";
1361
+ if (meta.annotations) symbol.annotations = JSON.stringify(meta.annotations);
1362
+ symbols.push(symbol);
1363
+ return symbol;
1364
+ }
1365
+ collectFunctionMeta(node, meta, symbol) {
1366
+ let isAsync = false;
1367
+ let throws = false;
1368
+ const nodeQueue = [node];
1369
+ while (nodeQueue.length > 0) {
1370
+ const c = nodeQueue.pop();
1371
+ if (c.text === "async" || c.type === "async") isAsync = true;
1372
+ if (c.type === "throw_keyword" || c.text === "throws" || c.type === "throws") throws = true;
1373
+ for (let i = c.childCount - 1; i >= 0; i--) {
1374
+ if (c.child(i)) nodeQueue.push(c.child(i));
1375
+ }
1376
+ }
1377
+ if (isAsync) {
1378
+ meta.isAsync = true;
1379
+ symbol.isAsync = true;
1380
+ }
1381
+ if (throws) {
1382
+ meta.throws = true;
1383
+ symbol.throws = "true";
1384
+ }
1385
+ }
1386
+ /**
1387
+ * The declaration's OWN access-level modifier, or null when it states none.
1388
+ *
1389
+ * The null case is load-bearing (DERIV_EXPORTS_SWIFT4): a member of an
1390
+ * extension INHERITS the extension's level only when it declares none of its
1391
+ * own, so "no modifier" and "explicitly internal" must be distinguishable —
1392
+ * `getVisibility` returns "internal" for both, which is right for the symbol
1393
+ * field and wrong for the inheritance rule.
1394
+ */
1395
+ getDeclaredVisibility(node) {
1396
+ for (let i = 0; i < node.namedChildCount; i++) {
1397
+ const child = node.namedChild(i);
1398
+ if (child && child.type === "modifiers") {
1399
+ for (let j = 0; j < child.namedChildCount; j++) {
1400
+ const mod = child.namedChild(j);
1401
+ if (mod) {
1402
+ if (mod.text === "public") return "public";
1403
+ if (mod.text === "private") return "private";
1404
+ if (mod.text === "fileprivate") return "fileprivate";
1405
+ if (mod.text === "internal") return "internal";
1406
+ if (mod.text === "open") return "open";
1407
+ if (mod.text === "package") return "package";
1408
+ }
1409
+ }
1410
+ }
1411
+ }
1412
+ return null;
1413
+ }
1414
+ getVisibility(node) {
1415
+ return this.getDeclaredVisibility(node) ?? "internal";
1416
+ }
1417
+ /**
1418
+ * The EXPORTING access levels, or null when the declaration is not part of the
1419
+ * module's interface. Swift has no `export` keyword (measured: 150 node types
1420
+ * over 400 corpus files, none an export declaration, 0 anonymous `export`
1421
+ * tokens) — its export construct IS access control. What counts as an export
1422
+ * is measured, over 3,656 files (script/m14/swift-import-export-probe.ts) with
1423
+ * 52,113 file-scope declarations as the denominator: 5,708 `public`, 43
1424
+ * `open`, against 1,325 explicit `internal`, 1,470 `private`, 195
1425
+ * `fileprivate` and 43,368 with no modifier at all — 5,751 of 52,113 (11.04%).
1426
+ * `internal`, `private`, `fileprivate` and the no-modifier default are the
1427
+ * ABSENCE of an export, so they are not in this set. `package` IS in the
1428
+ * condition below as a declared grammar-version boundary, not a measured one:
1429
+ * the pinned grammar (npm tree-sitter-swift 0.7.1) cannot reduce it — all six
1430
+ * synthetic shapes parse to an ERROR while `public func` reduces cleanly, and
1431
+ * the corpus probe finds 0 `package` visibility_modifier nodes in 3,656 files
1432
+ * — so it cannot fire on this corpus and this lane does not claim it does.
1433
+ */
1434
+ exportingVisibility(node) {
1435
+ const vis = this.getDeclaredVisibility(node);
1436
+ if (vis === "public" || vis === "open" || vis === "package") return vis;
1437
+ return null;
1438
+ }
1439
+ /**
1440
+ * The MODULE-LEVEL SOURCE of an `exports` edge: the file's own leaf name
1441
+ * (typescript's moduleLeafName() convention), e.g.
1442
+ * `…/fixtures/swift/exports-visibility.swift` -> `exports-visibility`.
1443
+ *
1444
+ * FOREIGN ON PURPOSE: never `root`. `root` is in parallel-indexer's
1445
+ * SYMBOL_NAME_DENYLIST, so an edge sourced there can never bind to a stored
1446
+ * symbol; the leaf name is what every other lane spells for a file-as-module
1447
+ * endpoint (go's packageName, python's moduleName). This is a NAME for the
1448
+ * file the edge was read from, not a claim about SwiftPM's module name, which
1449
+ * no Swift source file contains.
1450
+ */
1451
+ moduleLeafName(filePath) {
1452
+ let start = 0;
1453
+ for (let i = filePath.length - 1; i >= 0; i--) {
1454
+ if (filePath[i] === "/") {
1455
+ start = i + 1;
1456
+ break;
1457
+ }
1458
+ }
1459
+ let end = filePath.length;
1460
+ for (let i = start; i < filePath.length; i++) {
1461
+ if (filePath[i] === ".") {
1462
+ end = i;
1463
+ break;
1464
+ }
1465
+ }
1466
+ return filePath.slice(start, end);
1467
+ }
1468
+ /**
1469
+ * DERIV_EXPORTS_SWIFT4 — does this declaration publish a name for the module?
1470
+ *
1471
+ * Returns the kind the export row carries, or null when the declaration
1472
+ * exports nothing. Three gates, in order:
1473
+ *
1474
+ * 1. an EXPORTING access level (`exportingVisibility`): `public`/`open`/
1475
+ * `package` and nothing else;
1476
+ * 2. NOT an extension: an extension symbol is named after the EXTENDED type
1477
+ * (`public extension Array`), so exporting it would name a declaration
1478
+ * this file does not contain — measured at 54 file-scope sites in a
1479
+ * 3,656-file draw, all of which would be fabrications;
1480
+ * 3. the declaration KINDS that can carry a level: a nominal
1481
+ * (`class`/`struct`/`enum`/`actor`), a protocol, a function, a
1482
+ * property binding and a typealias.
1483
+ *
1484
+ * The caller gates on `parentSymbol === null` (file scope) — a member and a
1485
+ * function-local type never reach here.
1486
+ */
1487
+ isExportingDeclaration(node) {
1488
+ if (this.exportingVisibility(node) === null) return null;
1489
+ if (node.type === "class_declaration" && this.hasAnonToken(node, "extension")) return null;
1490
+ if (node.type === "ERROR") {
1491
+ if (!this.isNominalRecovery(node)) return null;
1492
+ return this.recoveryKind(node);
1493
+ }
1494
+ if (node.type === "class_declaration") {
1495
+ const valueType = this.hasAnonToken(node, "struct") || this.hasAnonToken(node, "enum");
1496
+ if (valueType) {
1497
+ if (this.hasAnonToken(node, "struct")) return ASTEngine.SymbolKind.Struct;
1498
+ if (this.hasAnonToken(node, "enum")) return ASTEngine.SymbolKind.Enum;
1499
+ }
1500
+ if (this.hasAnonToken(node, "actor")) return ASTEngine.SymbolKind.Actor;
1501
+ return ASTEngine.SymbolKind.Class;
1502
+ }
1503
+ if (node.type === "protocol_declaration") return ASTEngine.SymbolKind.Interface;
1504
+ if (node.type === "function_declaration" || node.type === "protocol_function_declaration") {
1505
+ return ASTEngine.SymbolKind.Function;
1506
+ }
1507
+ if (node.type === "property_declaration" || node.type === "protocol_property_declaration") {
1508
+ return ASTEngine.SymbolKind.Variable;
1509
+ }
1510
+ if (node.type === "typealias_declaration") return ASTEngine.SymbolKind.Type;
1511
+ return null;
1512
+ }
1513
+ /**
1514
+ * The NAME (and its node, for the line rule) a declaration publishes — one
1515
+ * entry per binding, because a multi-binding declaration (`var a = 1, b = 2`)
1516
+ * carries a sibling `pattern` per binding (SWIFTD18) and publishing only the
1517
+ * first would silently drop the rest.
1518
+ *
1519
+ * Polled by the export emission only: the symbol table already carries these
1520
+ * names, but the export LINE must come from the declaration's own name node
1521
+ * (a decorated declaration's line is its NAME's line, never the
1522
+ * decorator's — pinned by the `Decorated` row of swift/exports-visibility and
1523
+ * mutant SW-EXP-14).
1524
+ */
1525
+ exportedDeclarationNames(node) {
1526
+ const out = [];
1527
+ if (node.type === "property_declaration" || node.type === "protocol_property_declaration") {
1528
+ const pat = this.findChildByType(node, "pattern");
1529
+ if (pat) {
1530
+ const pats = [];
1531
+ for (let i = 0; i < node.namedChildCount; i++) {
1532
+ const c = node.namedChild(i);
1533
+ if (c && c.type === "pattern") pats.push(c);
1534
+ }
1535
+ for (const pn of pats) {
1536
+ const idNode = this.findChildByType(pn, "simple_identifier") || this.findChildByType(pn, "identifier") || this.firstNamedChild(pn);
1537
+ if (idNode) out.push({ name: idNode.text, node: idNode });
1538
+ }
1539
+ }
1540
+ return out;
1541
+ }
1542
+ if (node.type === "protocol_declaration") {
1543
+ const nm = this.findChildByType(node, "type_identifier");
1544
+ if (nm) out.push({ name: nm.text, node: nm });
1545
+ return out;
1546
+ }
1547
+ if (node.type === "class_declaration") {
1548
+ const nm = this.findChildByType(node, "type_identifier") || this.findChildByType(node, "user_type");
1549
+ if (nm) {
1550
+ const leaf = nm.type === "user_type" ? this.lastTypeIdentifier(nm) : nm;
1551
+ if (leaf) out.push({ name: leaf.text, node: leaf });
1552
+ }
1553
+ return out;
1554
+ }
1555
+ if (node.type === "function_declaration" || node.type === "protocol_function_declaration") {
1556
+ const nm = this.findChildByType(node, "simple_identifier") || this.findChildByType(node, "identifier");
1557
+ if (nm) out.push({ name: nm.text, node: nm });
1558
+ else {
1559
+ const opName = this.operatorName(node);
1560
+ if (opName) out.push({ name: opName, node });
1561
+ }
1562
+ return out;
1563
+ }
1564
+ if (node.type === "typealias_declaration") {
1565
+ const nm = this.findChildByType(node, "type_identifier") || this.firstNamedChild(node);
1566
+ if (nm) out.push({ name: nm.text, node: nm });
1567
+ return out;
1568
+ }
1569
+ if (node.type === "ERROR") {
1570
+ const nm = this.recoveryNameNode(node);
1571
+ if (nm) out.push({ name: nm.text, node: nm });
1572
+ return out;
1573
+ }
1574
+ return out;
1575
+ }
1576
+ /**
1577
+ * DERIV_EXPORTS_SWIFT4 — publish ONE file-scope declaration as an `exports`
1578
+ * edge and an `ExportInfo` row.
1579
+ *
1580
+ * `swiftExport: true` is the marker the CLASSIFICATION keys on indirectly
1581
+ * (`reExport` absent -> structural): the access level is a grammar clause read
1582
+ * off the declaration's own `modifiers` node, so nothing here is inferred and
1583
+ * no reason code is owed (honesty contract ).
1584
+ */
1585
+ emitDeclarationExport(node, moduleSource, relationships, exports, emitted) {
1586
+ const vis = this.exportingVisibility(node);
1587
+ if (!vis) return;
1588
+ const kind = this.isExportingDeclaration(node) ?? ASTEngine.SymbolKind.Class;
1589
+ for (const resolved of this.exportedDeclarationNames(node)) {
1590
+ this.publishDeclarationExport(node, resolved, kind, vis, moduleSource, relationships, exports, emitted);
1591
+ }
1592
+ }
1593
+ /** One published NAME: the row, the edge, and the (source, name) dedupe. */
1594
+ publishDeclarationExport(node, resolved, kind, vis, moduleSource, relationships, exports, emitted) {
1595
+ const name = this.stripNameQuoting(resolved.name);
1596
+ if (!name || emitted.has(name)) return;
1597
+ const nameNode = resolved.node;
1598
+ if (nameNode.startIndex < node.startIndex || nameNode.endIndex > node.endIndex) return;
1599
+ emitted.add(name);
1600
+ exports.push({
1601
+ name,
1602
+ kind,
1603
+ range: {
1604
+ startLine: nameNode.startPosition.row + 1,
1605
+ startColumn: nameNode.startPosition.column + 1,
1606
+ endLine: nameNode.endPosition.row + 1,
1607
+ endColumn: nameNode.endPosition.column + 1,
1608
+ startIndex: nameNode.startIndex,
1609
+ endIndex: nameNode.endIndex
1610
+ },
1611
+ visibility: vis,
1612
+ metadata: { exportForm: "visibility_modifier", accessLevel: vis }
1613
+ });
1614
+ relationships.push({
1615
+ type: ASTEngine.RelationshipType.Exports,
1616
+ source: moduleSource,
1617
+ target: name,
1618
+ isExternal: false,
1619
+ weight: 1,
1620
+ confidence: 1,
1621
+ ...typeof this.nodeLine(nameNode) === "number" ? { line_number: this.nodeLine(nameNode) } : {},
1622
+ metadata: { exportForm: "visibility_modifier", accessLevel: vis, swiftExport: true }
1623
+ });
1624
+ }
1625
+ /** `@_exported` — the underscored re-export attribute, by its NAME node. */
1626
+ hasExportedAttribute(node) {
1627
+ return this.hasAttributeNamed(node, "_exported");
1628
+ }
1629
+ /**
1630
+ * The form in which an import RE-PUBLISHES its module, or null for an
1631
+ * ordinary import (which is a dependency and nothing else).
1632
+ *
1633
+ * Two forms, both pinned by swift/exports-visibility:
1634
+ * - `public import M` (SE-0409) — the `public` modifier on the import;
1635
+ * - `@_exported import M` — Swift's literal re-export attribute.
1636
+ * `public` is checked first so a hypothetical `@_exported public import M`
1637
+ * keeps the access-controlled form; both publish the same fact.
1638
+ */
1639
+ importReExportForm(node) {
1640
+ let reExportForm = null;
1641
+ if (node.type === "import_declaration") {
1642
+ if (this.exportingVisibility(node) === "public") reExportForm = "public_import";
1643
+ else if (this.hasExportedAttribute(node)) reExportForm = "underscored_exported_import";
1644
+ }
1645
+ return reExportForm;
1646
+ }
1647
+ extractAttributes(node) {
1648
+ const attrs = [];
1649
+ const queue = [node];
1650
+ while (queue.length > 0) {
1651
+ const c = queue.pop();
1652
+ if (c.type === "attribute") {
1653
+ const text = c.text.trim();
1654
+ if (text.startsWith("@")) attrs.push(text);
1655
+ }
1656
+ for (let i = c.childCount - 1; i >= 0; i--) {
1657
+ if (c.child(i)) queue.push(c.child(i));
1658
+ }
1659
+ }
1660
+ return attrs;
1661
+ }
1662
+ handleImport(node, imports, frameworksObj, reExports) {
1663
+ let spelled = "";
1664
+ let moduleNode = null;
1665
+ for (let i = 0; i < node.childCount; i++) {
1666
+ const c = node.child(i);
1667
+ if (c && (c.type === "simple_identifier" || c.type === "type_identifier" || c.type === "identifier")) {
1668
+ spelled = c.text;
1669
+ moduleNode = c;
1670
+ break;
1671
+ }
1672
+ }
1673
+ const moduleName = spelled.split(".")[0] ?? "";
1674
+ let scopeKind = "";
1675
+ for (let i = 0; i < node.childCount; i++) {
1676
+ const c = node.child(i);
1677
+ if (!c || c.isNamed) continue;
1678
+ if (c.type === "import") continue;
1679
+ scopeKind = c.type;
1680
+ break;
1681
+ }
1682
+ if (moduleName) {
1683
+ const site = moduleNode ?? node;
1684
+ const range = {
1685
+ startLine: site.startPosition.row + 1,
1686
+ startColumn: site.startPosition.column + 1,
1687
+ endLine: site.endPosition.row + 1,
1688
+ endColumn: site.endPosition.column + 1,
1689
+ startIndex: site.startIndex,
1690
+ endIndex: site.endIndex
1691
+ };
1692
+ const importMd = {
1693
+ module: moduleName,
1694
+ moduleSpecifier: spelled,
1695
+ isModuleDependency: true
1696
+ };
1697
+ if (spelled !== moduleName) importMd.submodulePath = spelled;
1698
+ if (scopeKind.length > 0) {
1699
+ importMd.isScopedImport = true;
1700
+ importMd.scopedKind = scopeKind;
1701
+ const item = spelled.split(".").slice(1).join(".");
1702
+ if (item.length > 0) importMd.scopedItem = item;
1703
+ }
1704
+ const reExportForm = this.importReExportForm(node);
1705
+ if (reExportForm) {
1706
+ importMd.isModuleReExport = true;
1707
+ importMd.reExportForm = reExportForm;
1708
+ importMd.reExport = true;
1709
+ }
1710
+ imports.push({ module: spelled, isExternal: true, range, metadata: importMd });
1711
+ if (reExportForm) {
1712
+ reExports.push({ module: spelled, node: site, form: reExportForm });
1713
+ }
1714
+ if (moduleName === "SwiftUI") frameworksObj["SwiftUI"] = true;
1715
+ if (moduleName === "Combine") frameworksObj["Combine"] = true;
1716
+ if (moduleName === "UIKit") frameworksObj["UIKit"] = true;
1717
+ if (moduleName === "Foundation") frameworksObj["Foundation"] = true;
1718
+ }
1719
+ }
1720
+ /**
1721
+ * The NAME of an `attribute` node: its first named child.
1722
+ *
1723
+ * The attribute arrives as `import_declaration > modifiers > attribute`, and
1724
+ * the pinned grammar spells it `('@' (user_type (type_identifier)))` — so the
1725
+ * attribute's NAME is its first named child's text, never the attribute node's
1726
+ * own text and never the file's text. Reading the name node is what stops
1727
+ * `@objc(name: "_exported")` and a comment from producing a hit, and it is the
1728
+ * same rule the census's probe P2 uses on the other side of the comparison.
1729
+ */
1730
+ attributeName(node) {
1731
+ for (let i = 0; i < node.namedChildCount; i++) {
1732
+ const c = node.namedChild(i);
1733
+ if (c) return c.text;
1734
+ }
1735
+ return null;
1736
+ }
1737
+ hasAttributeNamed(node, want) {
1738
+ const stack = [node];
1739
+ while (stack.length > 0) {
1740
+ const n = stack.pop();
1741
+ if (n.type === "attribute" && this.attributeName(n) === want) return true;
1742
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
1743
+ const c = n.namedChild(i);
1744
+ if (c) stack.push(c);
1745
+ }
1746
+ }
1747
+ return false;
1748
+ }
1749
+ // ---- tree helpers (pure AST, no source scanning) ----
1750
+ findChildByType(node, type) {
1751
+ for (let i = 0; i < node.namedChildCount; i++) {
1752
+ const c = node.namedChild(i);
1753
+ if (c && c.type === type) return c;
1754
+ }
1755
+ return null;
1756
+ }
1757
+ /**
1758
+ * Depth-first (pre-order) first named descendant of the given type. Needed
1759
+ * where the node of interest is wrapped: a type declaration's name sits in a
1760
+ * direct `type_identifier` child for `struct Foo`, but under `user_type` for
1761
+ * `extension Foo` — so a shallow search finds nothing and the declaration
1762
+ * reads as nameless (#754 corpus finding).
1763
+ */
1764
+ firstDescendantOfType(node, type) {
1765
+ const stack = [node];
1766
+ while (stack.length > 0) {
1767
+ const x = stack.pop();
1768
+ if (x.type === type) return x;
1769
+ for (let i = x.namedChildCount - 1; i >= 0; i--) {
1770
+ const c = x.namedChild(i);
1771
+ if (c) stack.push(c);
1772
+ }
1773
+ }
1774
+ return null;
1775
+ }
1776
+ hasChildType(node, type) {
1777
+ for (let i = 0; i < node.namedChildCount; i++) {
1778
+ const c = node.namedChild(i);
1779
+ if (c && c.type === type) return true;
1780
+ }
1781
+ return false;
1782
+ }
1783
+ firstNamedChild(node) {
1784
+ return node.namedChildCount > 0 ? node.namedChild(0) : null;
1785
+ }
1786
+ /**
1787
+ * Per-symbol cyclomatic complexity (managed-OO family contract — csharp
1788
+ * #264 wave precedent). Mirrors the file-level counter's node set, adapted
1789
+ * to the pinned swift grammar (switch_entry per case, guard_statement,
1790
+ * catch_block, conjunction/disjunction/ternary expressions); base 1.
1791
+ */
1792
+ calculateCyclomaticComplexity(node) {
1793
+ let complexity = 1;
1794
+ const complexityNodes = /* @__PURE__ */ new Set([
1795
+ "if_statement",
1796
+ "for_statement",
1797
+ "while_statement",
1798
+ "repeat_while_statement",
1799
+ "switch_entry",
1800
+ "guard_statement",
1801
+ "catch_block",
1802
+ "conjunction_expression",
1803
+ "disjunction_expression",
1804
+ "ternary_expression"
1805
+ ]);
1806
+ const stack = [node];
1807
+ let guard = 0;
1808
+ while (stack.length > 0 && guard++ < 2e5) {
1809
+ const n = stack.pop();
1810
+ if (complexityNodes.has(n.type)) complexity++;
1811
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
1812
+ const c = n.namedChild(i);
1813
+ if (c) stack.push(c);
1814
+ }
1815
+ }
1816
+ return complexity;
1817
+ }
1818
+ /** `x[key]` parses as call_expression with a `[key]` call_suffix. */
1819
+ isSubscriptRead(node) {
1820
+ const suffix = this.findChildByType(node, "call_suffix");
1821
+ if (!suffix) return false;
1822
+ const args = this.findChildByType(suffix, "value_arguments");
1823
+ return !!args && this.hasAnonToken(args, "[");
1824
+ }
1825
+ /**
1826
+ * Operator overload name: the token immediately after `func`.
1827
+ *
1828
+ * The grammar reads most operator tokens as ANONYMOUS children (`==`, `+`,
1829
+ * `<`, `===`) but falls back to a NAMED `custom_operator` node for sequences
1830
+ * it has no reserved token for — measured on the pinned wasm: `func !==` and
1831
+ * `func ~=` both parse as `custom_operator`, `func ===`/`func <`/`func ==` as
1832
+ * anonymous tokens. The pre-fix loop accepted only anonymous children, so the
1833
+ * named forms fell through to the first anonymous child that was not a paren
1834
+ * — the parameter comma — and minted `operator ,`: measured in the corpus as
1835
+ * a symbol and an `exports` edge naming `operator ,` in
1836
+ * swift-corpus/SwiftCompilerSources/Sources/SIL/ApplySite.swift (the `~=`
1837
+ * overload), SIL/Instruction.swift and COM/IUnknown+Identity.swift (the `!==`
1838
+ * overloads). A comma is not an operator Swift can declare, so that endpoint
1839
+ * was FABRICATED, the defect class INCOMING_SWIFT JOB 2 forbids.
1840
+ *
1841
+ * `custom_operator` is the only named node accepted here; a named
1842
+ * `simple_identifier` is a normal function name (return null), and anything
1843
+ * else before the first paren is not a name token.
1844
+ */
1845
+ operatorName(node) {
1846
+ let sawFunc = false;
1847
+ for (let i = 0; i < node.childCount; i++) {
1848
+ const c = node.child(i);
1849
+ if (!c) continue;
1850
+ if (!c.isNamed && c.type === "func") {
1851
+ sawFunc = true;
1852
+ continue;
1853
+ }
1854
+ if (sawFunc) {
1855
+ if (c.isNamed && c.type === "simple_identifier") return null;
1856
+ if (c.isNamed && c.type === "custom_operator") return `operator ${c.text}`;
1857
+ if (!c.isNamed && c.type !== "(" && c.type !== ")" && c.type !== "{") {
1858
+ return `operator ${c.text}`;
1859
+ }
1860
+ }
1861
+ }
1862
+ return null;
1863
+ }
1864
+ /** `defer { ... }` parses as call_expression(simple_identifier "defer", call_suffix > lambda_literal). */
1865
+ isDeferCall(node) {
1866
+ const fn = this.findChildByType(node, "simple_identifier") || this.findChildByType(node, "identifier");
1867
+ if (!fn || fn.text !== "defer") return false;
1868
+ const suffix = this.findChildByType(node, "call_suffix");
1869
+ return !!suffix && this.findChildByType(suffix, "lambda_literal") !== null;
1870
+ }
1871
+ /** Match an anonymous keyword token (`struct`, `enum`, `extension`, `actor`, `override`, `self`). */
1872
+ hasAnonToken(node, tokenType) {
1873
+ for (let i = 0; i < node.childCount; i++) {
1874
+ const c = node.child(i);
1875
+ if (c && !c.isNamed && c.type === tokenType) return true;
1876
+ }
1877
+ return false;
1878
+ }
1879
+ /**
1880
+ * Walk the navigation RECEIVER chain: navigation_expression > (nested
1881
+ * navigation_expression | self_expression | anonymous self token). Stops at
1882
+ * the first non-receiver child (a call_expression/try_expression) — the
1883
+ * suffix then applies to that call's RESULT, not to self.
1884
+ */
1885
+ hasSelfReceiver(node) {
1886
+ let cur = node;
1887
+ let guard = 0;
1888
+ while (cur && guard++ < 64) {
1889
+ if (cur.type === "self_expression" || cur.type === "self" && !cur.isNamed) return true;
1890
+ let next = null;
1891
+ for (let i = 0; i < cur.namedChildCount; i++) {
1892
+ const c = cur.namedChild(i);
1893
+ if (!c) continue;
1894
+ if (c.type === "navigation_expression" || c.type === "self_expression") {
1895
+ next = c;
1896
+ break;
1897
+ }
1898
+ }
1899
+ if (!next && cur.type === "navigation_expression") {
1900
+ const spine = cur.namedChild(0);
1901
+ if (spine && (spine.type === "additive_expression" || spine.type === "prefix_expression")) {
1902
+ let selfChild = null;
1903
+ for (let i = 0; i < spine.namedChildCount; i++) {
1904
+ const c = spine.namedChild(i);
1905
+ if (c?.type === "self_expression") {
1906
+ selfChild = c;
1907
+ break;
1908
+ }
1909
+ }
1910
+ if (selfChild) return true;
1911
+ next = spine;
1912
+ }
1913
+ }
1914
+ if (!next) return false;
1915
+ cur = next;
1916
+ }
1917
+ return false;
1918
+ }
1919
+ /** Match an anonymous keyword token anywhere in the subtree (nested modifier tokens). */
1920
+ hasDescendantAnonToken(node, tokenType) {
1921
+ const stack = [node];
1922
+ while (stack.length > 0) {
1923
+ const c = stack.pop();
1924
+ if (c.type === tokenType && !c.isNamed) return true;
1925
+ for (let i = c.childCount - 1; i >= 0; i--) {
1926
+ const child = c.child(i);
1927
+ if (child) stack.push(child);
1928
+ }
1929
+ }
1930
+ return false;
1931
+ }
1932
+ /** For extensions: the type_identifier inside the user_type (the extended type). */
1933
+ findDescendantTypeIdentifier(node) {
1934
+ const stack = [node];
1935
+ while (stack.length > 0) {
1936
+ const c = stack.pop();
1937
+ if (c.type === "type_identifier") return c;
1938
+ for (let i = c.namedChildCount - 1; i >= 0; i--) {
1939
+ const child = c.namedChild(i);
1940
+ if (child) stack.push(child);
1941
+ }
1942
+ }
1943
+ return null;
1944
+ }
1945
+ findEnumRawType(node) {
1946
+ for (let i = 0; i < node.namedChildCount; i++) {
1947
+ const c = node.namedChild(i);
1948
+ if (c && c.type === "inheritance_specifier") {
1949
+ const inner = this.findChildByType(c, "user_type") || this.findChildByType(c, "type_identifier");
1950
+ return inner ? inner.text : c.text;
1951
+ }
1952
+ }
1953
+ return null;
1954
+ }
1955
+ /** Base type names from the inheritance specifier (class/struct/actor). */
1956
+ /**
1957
+ * SW-D21 (#938): `case .X(let y)` recovery — the callee navigation's first
1958
+ * token is the reserved `case` keyword (parsed as a simple_identifier).
1959
+ * Swift never lets `case` name a value, so this is always grammar recovery.
1960
+ */
1961
+ isCasePatternRecovery(callNode) {
1962
+ for (let i = 0; i < callNode.namedChildCount; i++) {
1963
+ const c = callNode.namedChild(i);
1964
+ if (!c || c.type !== "navigation_expression") continue;
1965
+ const head = this.firstNamedChild(c);
1966
+ if (head && (head.type === "simple_identifier" || head.type === "identifier") && head.text === "case") return true;
1967
+ }
1968
+ return false;
1969
+ }
1970
+ /**
1971
+ * SW-D23 (#940): is this ERROR node a nominal DECLARATION the pinned grammar
1972
+ * failed to reduce? True when its FIRST child is the anonymous nominal
1973
+ * keyword token and its own next named child is the type name (`user_type`
1974
+ * for an extension, an identifier otherwise). The name must be the
1975
+ * recovery's own name node — never a nested declaration's — so a top-level
1976
+ * `ERROR` that merely contains declarations somewhere below is not treated
1977
+ * as one itself.
1978
+ */
1979
+ /**
1980
+ * The nominal kind of an ERROR-recovered header, read from the TOKENS: the
1981
+ * first anonymous nominal keyword child, provided nothing but header material
1982
+ * (`modifiers` / `attribute` / `comment`) precedes it. Declaration content
1983
+ * before a keyword means the keyword belongs to a nested declaration that the
1984
+ * recovery left as a SIBLING (measured: `extension SocketAddressError { ...
1985
+ * struct UnknownHost ... }` carries the nested `struct` token as a direct
1986
+ * child of the recovery, so a plain "does any child token say struct" test
1987
+ * names the extension a struct).
1988
+ */
1989
+ recoveryKind(node) {
1990
+ for (let i = 0; i < node.childCount; i++) {
1991
+ const c = node.child(i);
1992
+ if (!c) continue;
1993
+ if (!c.isNamed) {
1994
+ if (c.type === "class") return ASTEngine.SymbolKind.Class;
1995
+ if (c.type === "struct") return ASTEngine.SymbolKind.Struct;
1996
+ if (c.type === "enum") return ASTEngine.SymbolKind.Enum;
1997
+ if (c.type === "extension") return ASTEngine.SymbolKind.Extension;
1998
+ if (c.type === "actor") return ASTEngine.SymbolKind.Actor;
1999
+ continue;
2000
+ }
2001
+ if (c.type === "modifiers" || c.type === "attribute" || c.type === "comment") continue;
2002
+ return null;
2003
+ }
2004
+ return null;
2005
+ }
2006
+ /**
2007
+ * The recovery's OWN name node: the first non-header child, which is the
2008
+ * extended `user_type` for an extension and an identifier otherwise. Anything
2009
+ * else (a nested declaration's type_identifier, a parameter type) is not this
2010
+ * header's name.
2011
+ */
2012
+ recoveryNameNode(node) {
2013
+ for (let i = 0; i < node.namedChildCount; i++) {
2014
+ const c = node.namedChild(i);
2015
+ if (!c) continue;
2016
+ if (c.type === "modifiers" || c.type === "attribute" || c.type === "comment") continue;
2017
+ if (c.type === "user_type" || c.type === "type_identifier" || c.type === "simple_identifier" || c.type === "identifier") return c;
2018
+ return null;
2019
+ }
2020
+ return null;
2021
+ }
2022
+ recoveryName(node) {
2023
+ const n = this.recoveryNameNode(node);
2024
+ if (!n) return null;
2025
+ if (n.type === "user_type") {
2026
+ const leaf = this.lastTypeIdentifier(n);
2027
+ return leaf ? leaf.text : null;
2028
+ }
2029
+ return n.text;
2030
+ }
2031
+ isNominalRecovery(node) {
2032
+ if (node.type !== "ERROR") return false;
2033
+ return this.recoveryKind(node) !== null && this.recoveryNameNode(node) !== null;
2034
+ }
2035
+ /**
2036
+ * (#825 lineage): base sites WITH their name nodes, because a heritage
2037
+ * edge must name the base specifier's own line — the declaration's line is a
2038
+ * different site, and borrowing it is the fabrication the line rule forbids.
2039
+ */
2040
+ extractBaseSites(node) {
2041
+ const sites = [];
2042
+ for (let i = 0; i < node.namedChildCount; i++) {
2043
+ const c = node.namedChild(i);
2044
+ if (c && c.type === "inheritance_specifier") {
2045
+ const site = this.extractBaseSite(c);
2046
+ if (site) sites.push(site);
2047
+ }
2048
+ }
2049
+ return sites;
2050
+ }
2051
+ /**
2052
+ * The store's `line_number` for a node: its 1-based start row.
2053
+ * `undefined` when there is no node — the field's ruled absence state, never
2054
+ * a fabricated 0 and never an enclosing declaration's line (/ R6).
2055
+ */
2056
+ nodeLine(node) {
2057
+ if (!node) return void 0;
2058
+ return node.startPosition.row + 1;
2059
+ }
2060
+ /**
2061
+ * Leaf of a (possibly dotted) user_type: the LAST sibling type_identifier
2062
+ * (`Outer.Inner` -> Inner, `Foundation.URLSession` -> URLSession). The
2063
+ * user_type node holds its dotted segments as sibling type_identifier
2064
+ * children in source order (probe-verified 2026-09), so the leaf is simply
2065
+ * the last one. SWIFTD19: the pre-fix code returned the FIRST segment,
2066
+ * fabricating extends/implements edges to the wrong (module/container)
2067
+ * name — e.g. `class Sub2: Foundation.URLSession` became extends Sub2 ->
2068
+ * Foundation, and `extension Foo: SomeMod.Proto` became implements Foo ->
2069
+ * SomeMod.
2070
+ */
2071
+ lastTypeIdentifier(ut) {
2072
+ let last = null;
2073
+ for (let i = 0; i < ut.namedChildCount; i++) {
2074
+ const c = ut.namedChild(i);
2075
+ if (c && c.type === "type_identifier") last = c;
2076
+ }
2077
+ if (last) return last;
2078
+ const stack = [ut];
2079
+ let best = null;
2080
+ while (stack.length > 0) {
2081
+ const c = stack.pop();
2082
+ if (c.type === "type_identifier") {
2083
+ if (!best || c.startIndex >= best.startIndex) best = c;
2084
+ }
2085
+ for (let i = c.namedChildCount - 1; i >= 0; i--) {
2086
+ const child = c.namedChild(i);
2087
+ if (child) stack.push(child);
2088
+ }
2089
+ }
2090
+ return best;
2091
+ }
2092
+ extractBaseSite(spec) {
2093
+ for (let i = 0; i < spec.namedChildCount; i++) {
2094
+ const c = spec.namedChild(i);
2095
+ if (!c) continue;
2096
+ if (c.type === "type_identifier") return { name: c.text, node: c };
2097
+ if (c.type === "user_type") {
2098
+ const leaf = this.lastTypeIdentifier(c);
2099
+ if (leaf) return { name: leaf.text, node: leaf };
2100
+ }
2101
+ }
2102
+ const txt = spec.text.trim();
2103
+ return txt && !txt.includes(" ") ? { name: txt, node: spec } : null;
2104
+ }
2105
+ /** SW-F: all-ASCII-digits check without regex (family contract). */
2106
+ isAllDigits(text) {
2107
+ if (text.length === 0) return false;
2108
+ for (let i = 0; i < text.length; i++) {
2109
+ const c = text.charCodeAt(i);
2110
+ if (c < 48 || c > 57) return false;
2111
+ }
2112
+ return true;
2113
+ }
2114
+ /** SW-G: receiver head of a navigation (`super`/`self`/`Self`/identifier). */
2115
+ navigationReceiver(node) {
2116
+ for (let i = 0; i < node.namedChildCount; i++) {
2117
+ const c = node.namedChild(i);
2118
+ if (!c) continue;
2119
+ if (c.type === "self_expression") return "self";
2120
+ if (c.type === "super_expression") return "super";
2121
+ if (c.type === "simple_identifier" || c.type === "identifier" || c.type === "type_identifier") return c.text;
2122
+ if (c.type === "navigation_expression") return this.navigationReceiver(c);
2123
+ return null;
2124
+ }
2125
+ return null;
2126
+ }
2127
+ /**
2128
+ * SW-B: document-order member texts of a navigation chain
2129
+ * (`P.Foo.bar` -> [P, Foo, bar]). The DIRECT receiver of the leaf method
2130
+ * is second-to-last — the ultimate head is wrong for 3+ chains. Never
2131
+ * descends into nested call_expressions (arguments are not chain
2132
+ * members: `a.b[x]` must yield [a, b], never [a, b, x]).
2133
+ */
2134
+ navigationChain(node) {
2135
+ const out = [];
2136
+ const stack = [node];
2137
+ const seen = [];
2138
+ while (stack.length > 0) {
2139
+ const n = stack.pop();
2140
+ if (n !== node && n.type === "call_expression") continue;
2141
+ seen.push(n);
2142
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
2143
+ const c = n.namedChild(i);
2144
+ if (c) stack.push(c);
2145
+ }
2146
+ }
2147
+ seen.sort((a, b) => a.startIndex - b.startIndex);
2148
+ for (const n of seen) {
2149
+ if (n.type === "simple_identifier" || n.type === "identifier" || n.type === "type_identifier") {
2150
+ out.push(n.text);
2151
+ }
2152
+ }
2153
+ return out;
2154
+ }
2155
+ /** SW-B: capitalized type-name signal (SWIFTD17 rule, shared). */
2156
+ isTypeName(text) {
2157
+ if (text.length === 0) return false;
2158
+ const code = text.charCodeAt(0);
2159
+ return code >= 65 && code <= 90;
2160
+ }
2161
+ /**
2162
+ * SW-A (ultimate review): NON-self navigation member reads. Every
2163
+ * navigation_suffix member reads unless (a) it is the invoked callee (its
2164
+ * navigation is the func of a non-subscript call and it is the last
2165
+ * suffix), (b) the chain is self-rooted (handleSelfUse owns those),
2166
+ * (c) it sits in an assignment-LHS span (SCD1), (d) it is an implicit-dot
2167
+ * member (`.regular` — base inferred, bare-uses parity), or (e) it names
2168
+ * self/Self/$0. Shares emittedUses with handleSelfUse (first metadata
2169
+ * wins) and sources identically (method > type > owner > name > root).
2170
+ */
2171
+ handleMemberUses(node, relationships, parent, typeName, emittedUses) {
2172
+ if (this.hasSelfReceiver(node)) return;
2173
+ const source = (parent && (parent.kind === ASTEngine.SymbolKind.Method || parent.kind === ASTEngine.SymbolKind.Constructor) ? parent.name : null) || typeName || (parent?.ownerName ? parent.ownerName : null) || parent?.name || "root";
2174
+ for (let i = 0; i < node.namedChildCount; i++) {
2175
+ const c = node.namedChild(i);
2176
+ if (!c || c.type !== "navigation_suffix") continue;
2177
+ const m = this.findChildByType(c, "simple_identifier") || this.findChildByType(c, "identifier");
2178
+ if (!m || !m.text) continue;
2179
+ const member = this.stripNameQuoting(m.text);
2180
+ if (member === "$0" || member === "self" || member === "Self") continue;
2181
+ const par = node.parent;
2182
+ if (par && par.type === "call_expression" && !this.isSubscriptRead(par)) {
2183
+ let last = false;
2184
+ for (let j = node.namedChildCount - 1; j >= 0; j--) {
2185
+ const s = node.namedChild(j);
2186
+ if (s && s.type === "navigation_suffix") {
2187
+ last = s.startIndex === c.startIndex && s.endIndex === c.endIndex;
2188
+ break;
2189
+ }
2190
+ }
2191
+ if (last) continue;
2192
+ }
2193
+ let outer = node;
2194
+ while (outer && outer.parent && outer.parent.type === "navigation_expression") outer = outer.parent;
2195
+ const firstTok = outer ? outer.child(0) : null;
2196
+ if (firstTok && !firstTok.isNamed && firstTok.type === ".") continue;
2197
+ let a = node.parent;
2198
+ while (a && (a.type === "directly_assignable_expression" || a.type === "postfix_expression" || a.type === "navigation_expression" || a.type === "navigation_suffix")) a = a.parent;
2199
+ if (a && a.type === "assignment") {
2200
+ const lhs = a.namedChild(0);
2201
+ if (lhs && lhs.startIndex <= c.startIndex && lhs.endIndex >= c.endIndex) continue;
2202
+ }
2203
+ const key = `${source}\0uses\0${member}`;
2204
+ if (emittedUses.has(key)) continue;
2205
+ emittedUses.add(key);
2206
+ relationships.push({
2207
+ type: ASTEngine.RelationshipType.Uses,
2208
+ source,
2209
+ target: member,
2210
+ isExternal: false,
2211
+ weight: 0.7,
2212
+ confidence: 0.9,
2213
+ // The member identifier node inside this suffix.
2214
+ line_number: this.nodeLine(m),
2215
+ metadata: { isMemberRead: true }
2216
+ });
2217
+ }
2218
+ }
2219
+ /**
2220
+ * SW-D (ultimate review): key-path members (`\.name`, `\C.name`) always
2221
+ * read the member identity.
2222
+ */
2223
+ handleKeyPathUses(node, relationships, parent, typeName, emittedUses) {
2224
+ const source = (parent && (parent.kind === ASTEngine.SymbolKind.Method || parent.kind === ASTEngine.SymbolKind.Constructor) ? parent.name : null) || typeName || (parent?.ownerName ? parent.ownerName : null) || parent?.name || "root";
2225
+ const stack = [node];
2226
+ while (stack.length > 0) {
2227
+ const n = stack.pop();
2228
+ if (n.type === "navigation_suffix") {
2229
+ const m = this.findChildByType(n, "simple_identifier") || this.findChildByType(n, "identifier");
2230
+ if (m && m.text && m.text !== "self" && m.text !== "Self") {
2231
+ const keyPathMember = this.stripNameQuoting(m.text);
2232
+ const key = `${source}\0uses\0${keyPathMember}`;
2233
+ if (!emittedUses.has(key)) {
2234
+ emittedUses.add(key);
2235
+ relationships.push({
2236
+ type: ASTEngine.RelationshipType.Uses,
2237
+ source,
2238
+ target: keyPathMember,
2239
+ isExternal: false,
2240
+ weight: 0.7,
2241
+ confidence: 0.9,
2242
+ line_number: this.nodeLine(m),
2243
+ metadata: { isMemberRead: true, isKeyPath: true }
2244
+ });
2245
+ }
2246
+ }
2247
+ continue;
2248
+ }
2249
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
2250
+ const c = n.namedChild(i);
2251
+ if (c) stack.push(c);
2252
+ }
2253
+ }
2254
+ }
2255
+ /**
2256
+ * The leaf IDENTIFIER NODE of a navigation (`users.first` -> the `first`
2257
+ * node) — the site a `uses`/`calls` edge's line must name. Paired with
2258
+ * `navigationLeaf` (the string form) so no line is ever taken from a
2259
+ * different segment of the chain.
2260
+ */
2261
+ navigationLeafNode(node) {
2262
+ let leaf = null;
2263
+ for (let i = 0; i < node.namedChildCount; i++) {
2264
+ const c = node.namedChild(i);
2265
+ if (!c) continue;
2266
+ if (c.type === "navigation_suffix") {
2267
+ leaf = this.findChildByType(c, "simple_identifier") || this.findChildByType(c, "identifier") || c;
2268
+ }
2269
+ }
2270
+ if (leaf) return leaf;
2271
+ for (let i = 0; i < node.namedChildCount; i++) {
2272
+ const c = node.namedChild(i);
2273
+ if (c && c.type === "simple_identifier") leaf = c;
2274
+ }
2275
+ return leaf;
2276
+ }
2277
+ /** Leaf name of a navigation_expression: `users.first` -> `first`. */
2278
+ navigationLeaf(node) {
2279
+ let leaf = null;
2280
+ let sawSuffix = false;
2281
+ for (let i = 0; i < node.namedChildCount; i++) {
2282
+ const c = node.namedChild(i);
2283
+ if (!c) continue;
2284
+ if (c.type === "navigation_suffix") {
2285
+ sawSuffix = true;
2286
+ leaf = this.findChildByType(c, "simple_identifier") || this.findChildByType(c, "identifier");
2287
+ }
2288
+ }
2289
+ if (leaf) return this.stripNameQuoting(leaf.text);
2290
+ if (sawSuffix) return null;
2291
+ for (let i = 0; i < node.namedChildCount; i++) {
2292
+ const c = node.namedChild(i);
2293
+ if (c && c.type === "simple_identifier") leaf = c;
2294
+ }
2295
+ return leaf ? this.stripNameQuoting(leaf.text) : null;
2296
+ }
2297
+ /** Return type node after the `->` anonymous token (lightweight). */
2298
+ findReturnType(node) {
2299
+ for (let i = 0; i < node.childCount; i++) {
2300
+ const c = node.child(i);
2301
+ if (c && !c.isNamed && c.type === "->") {
2302
+ const nxt = node.child(i + 1);
2303
+ if (nxt && nxt.isNamed) return nxt;
2304
+ }
2305
+ }
2306
+ return null;
2307
+ }
2308
+ };
2309
+ export {
2310
+ RealSwiftAnalyzer
2311
+ };