@backbencher/brain 0.0.0-stage → 0.1.1

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-Q5APTJQL.js +17 -0
  8. package/dist/brain-relationship-enrichment-MYR2AM4F.js +558 -0
  9. package/dist/brain.sql-EIM4WS4D.js +29 -0
  10. package/dist/chunk-22V37YTK.js +1758 -0
  11. package/dist/chunk-2BSKD7O3.js +40 -0
  12. package/dist/chunk-2DE27DVC.js +1314 -0
  13. package/dist/chunk-2RRBMNBQ.js +322 -0
  14. package/dist/chunk-2WXYNLBA.js +29 -0
  15. package/dist/chunk-3CTFIJJB.js +17 -0
  16. package/dist/chunk-3ZTOHHSW.js +2104 -0
  17. package/dist/chunk-4H22TLOT.js +543 -0
  18. package/dist/chunk-5BVAH26D.js +340 -0
  19. package/dist/chunk-62DHLYW6.js +3427 -0
  20. package/dist/chunk-7AN7U5IZ.js +371 -0
  21. package/dist/chunk-7DRPNVIN.js +782 -0
  22. package/dist/chunk-7MBYT777.js +174 -0
  23. package/dist/chunk-AHDWGM7Q.js +236 -0
  24. package/dist/chunk-B5GEAAYF.js +818 -0
  25. package/dist/chunk-BEIZZ73B.js +1479 -0
  26. package/dist/chunk-BQ3OVQ3P.js +125 -0
  27. package/dist/chunk-D2Q4Y47A.js +53 -0
  28. package/dist/chunk-DIYAGQJL.js +34 -0
  29. package/dist/chunk-DWKXZ6IF.js +336 -0
  30. package/dist/chunk-DYM4FQEE.js +90 -0
  31. package/dist/chunk-ENW4BTBA.js +930 -0
  32. package/dist/chunk-EWMWK7WN.js +75 -0
  33. package/dist/chunk-EY5ZZUUA.js +626 -0
  34. package/dist/chunk-GBI7V7KY.js +69 -0
  35. package/dist/chunk-GEFFO6O2.js +152 -0
  36. package/dist/chunk-HJEWO5EV.js +247 -0
  37. package/dist/chunk-HQSPJDHI.js +613 -0
  38. package/dist/chunk-KAET47ZL.js +125 -0
  39. package/dist/chunk-KCDECRJY.js +440 -0
  40. package/dist/chunk-L5TDYKYK.js +76 -0
  41. package/dist/chunk-MLKGABMK.js +9 -0
  42. package/dist/chunk-NO3U3FJQ.js +201 -0
  43. package/dist/chunk-OWISBAOF.js +61 -0
  44. package/dist/chunk-QKTRECYS.js +1119 -0
  45. package/dist/chunk-QLK6ED4Q.js +235 -0
  46. package/dist/chunk-R2GW4F6R.js +19 -0
  47. package/dist/chunk-RCMTFSEF.js +87 -0
  48. package/dist/chunk-RM3PY6TG.js +376 -0
  49. package/dist/chunk-ROMM6KMC.js +433 -0
  50. package/dist/chunk-SGGCSHAX.js +12050 -0
  51. package/dist/chunk-SK6YVHFZ.js +211 -0
  52. package/dist/chunk-SM7RFQRO.js +214 -0
  53. package/dist/chunk-UGR4QIGS.js +67 -0
  54. package/dist/chunk-UQNQZXHY.js +711 -0
  55. package/dist/chunk-UUNHHX4M.js +13 -0
  56. package/dist/chunk-VL52PL2T.js +71 -0
  57. package/dist/chunk-WIG36E67.js +196 -0
  58. package/dist/chunk-XHK3WWGB.js +7436 -0
  59. package/dist/chunk-XTQM2IP3.js +2863 -0
  60. package/dist/chunk-ZE2LG76D.js +1296 -0
  61. package/dist/chunk-ZETTGPAW.js +6950 -0
  62. package/dist/cli/index.js +37 -0
  63. package/dist/config-HZAAYDP3.js +10 -0
  64. package/dist/content-search-7XIP555Q.js +593 -0
  65. package/dist/database-76RP44JG.js +9 -0
  66. package/dist/doctor-ANGETYMN.js +395 -0
  67. package/dist/edit-engine-CG72LDZD.js +51 -0
  68. package/dist/engine-BKEOGN7N.js +11 -0
  69. package/dist/engine-VIW6NSKO.js +18 -0
  70. package/dist/fingerprint-OBLIMZLP.js +33 -0
  71. package/dist/framework-intelligence-3LORTPOX.js +860 -0
  72. package/dist/freshness-3K6VU5OT.js +174 -0
  73. package/dist/graph-engine-HBSQNPTB.js +21 -0
  74. package/dist/graph_request-Q6NKB6N2.js +350 -0
  75. package/dist/impact_request-LYP672WE.js +68 -0
  76. package/dist/index-space-IUUN5WYV.js +15 -0
  77. package/dist/index-status-7OVGYSGW.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-FK2XKZXY.js +88 -0
  86. package/dist/pool-worker.js +1153 -0
  87. package/dist/project-health-6XPENVAA.js +94 -0
  88. package/dist/project-index-5SDPHITE.js +57 -0
  89. package/dist/project-registry-ZJ3DVUGO.js +355 -0
  90. package/dist/real-bash-analyzer-EJAU5LW5.js +2201 -0
  91. package/dist/real-c-analyzer-HBD3FNNH.js +5942 -0
  92. package/dist/real-clean-analyzer-4EEZLPV5.js +3470 -0
  93. package/dist/real-clojure-analyzer-Q55EBOO2.js +2818 -0
  94. package/dist/real-cpp-analyzer-IVVOD25R.js +4101 -0
  95. package/dist/real-csharp-analyzer-ZBNLNJZJ.js +3298 -0
  96. package/dist/real-css-analyzer-6XRQLUN7.js +15 -0
  97. package/dist/real-dart-analyzer-OLR72EFW.js +3944 -0
  98. package/dist/real-elixir-analyzer-MQWGAH2I.js +2192 -0
  99. package/dist/real-erlang-analyzer-7BVPQEHO.js +1731 -0
  100. package/dist/real-fsharp-analyzer-C2UPFNOA.js +2390 -0
  101. package/dist/real-go-analyzer-UKGI43YS.js +4126 -0
  102. package/dist/real-graphql-analyzer-VE2EEFII.js +1442 -0
  103. package/dist/real-groovy-analyzer-W26VACFW.js +2285 -0
  104. package/dist/real-haskell-analyzer-5CVVAJRX.js +2533 -0
  105. package/dist/real-hcl-analyzer-5ZT4U43N.js +725 -0
  106. package/dist/real-html-analyzer-JWNX3C6I.js +1759 -0
  107. package/dist/real-java-analyzer-GU42VBGQ.js +4677 -0
  108. package/dist/real-javascript-analyzer-QA4QOJ6W.js +14 -0
  109. package/dist/real-json-analyzer-MPA6PRXL.js +1640 -0
  110. package/dist/real-julia-analyzer-22CWYQMN.js +2085 -0
  111. package/dist/real-kotlin-analyzer-QDZJLXXU.js +3956 -0
  112. package/dist/real-lua-analyzer-G7NHLYKS.js +1957 -0
  113. package/dist/real-makefile-analyzer-OCY3GT5N.js +920 -0
  114. package/dist/real-markdown-analyzer-TEJK2JXJ.js +1215 -0
  115. package/dist/real-matlab-analyzer-Q5SWLN7Z.js +480 -0
  116. package/dist/real-nim-analyzer-YLXSK563.js +3603 -0
  117. package/dist/real-objective-c-analyzer-D23ACC2R.js +1518 -0
  118. package/dist/real-perl-analyzer-WWTRHYDT.js +1865 -0
  119. package/dist/real-php-analyzer-JNKJHTCL.js +9368 -0
  120. package/dist/real-powershell-analyzer-XRSNQERV.js +2915 -0
  121. package/dist/real-protobuf-analyzer-3OKUCZ7T.js +867 -0
  122. package/dist/real-python-analyzer-VXAFWI72.js +4880 -0
  123. package/dist/real-r-analyzer-ZGAIDVI4.js +2596 -0
  124. package/dist/real-ruby-analyzer-X2KTLVBH.js +2429 -0
  125. package/dist/real-rust-analyzer-OFFHPL2A.js +3359 -0
  126. package/dist/real-scala-analyzer-TSTW27J6.js +3293 -0
  127. package/dist/real-scss-analyzer-AVWYPSOQ.js +1515 -0
  128. package/dist/real-solidity-analyzer-UCI2KWUB.js +2608 -0
  129. package/dist/real-sql-analyzer-F77ZMQXR.js +3840 -0
  130. package/dist/real-svelte-analyzer-SF7NRUO5.js +2532 -0
  131. package/dist/real-swift-analyzer-MN5QT3QN.js +2311 -0
  132. package/dist/real-toml-analyzer-66L7BDCQ.js +929 -0
  133. package/dist/real-typescript-analyzer-6KEMVDWQ.js +14 -0
  134. package/dist/real-vb-analyzer-VICVPOP7.js +3145 -0
  135. package/dist/real-vba-analyzer-HXLTTHKM.js +466 -0
  136. package/dist/real-vue-analyzer-QXR56CFV.js +2680 -0
  137. package/dist/real-xml-analyzer-YL465ZJW.js +2405 -0
  138. package/dist/real-yaml-analyzer-SUBCBNSI.js +3528 -0
  139. package/dist/real-zig-analyzer-HIQLZMGY.js +2430 -0
  140. package/dist/regex-search-NJKHMZNO.js +239 -0
  141. package/dist/rules-ACHPB7QU.js +152 -0
  142. package/dist/scoped-path-MJYWJZQX.js +11 -0
  143. package/dist/setup-show-NT5ZHYNE.js +15 -0
  144. package/dist/setup-wizard-G5YYR5T6.js +24 -0
  145. package/dist/symbol-search-GZJIM2J2.js +16 -0
  146. package/dist/taint-KXUHHPIE.js +202 -0
  147. package/dist/taint-analyzer-N7YLZU3N.js +163 -0
  148. package/dist/tool-handlers-OJDC35DI.js +72 -0
  149. package/dist/tool-registry-BPEJNSZG.js +17 -0
  150. package/dist/tool-validation-Q57YMMBX.js +32 -0
  151. package/dist/type-inference-QMT2RMLV.js +13 -0
  152. package/dist/vector-binary-LMY4QGAN.js +19 -0
  153. package/dist/version-ABP2TUL2.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,1518 @@
1
+ import {
2
+ ASTEngine
3
+ } from "./chunk-3ZTOHHSW.js";
4
+ import "./chunk-BQ3OVQ3P.js";
5
+ import "./chunk-SK6YVHFZ.js";
6
+ import "./chunk-2BSKD7O3.js";
7
+ import {
8
+ BINDING_SEP
9
+ } from "./chunk-KAET47ZL.js";
10
+ import "./chunk-KCDECRJY.js";
11
+ import "./chunk-MLKGABMK.js";
12
+
13
+ // src/ast/real-objective-c-analyzer.ts
14
+ var BARE_READ_SKIP = /* @__PURE__ */ new Set([
15
+ "self",
16
+ "super",
17
+ "nil",
18
+ "Nil",
19
+ "NULL",
20
+ "YES",
21
+ "NO",
22
+ "true",
23
+ "false",
24
+ "_cmd"
25
+ ]);
26
+ var DECLARATOR_WRAPPERS = /* @__PURE__ */ new Set([
27
+ "array_declarator",
28
+ "pointer_declarator",
29
+ "parenthesized_declarator",
30
+ "attributed_declarator",
31
+ "abstract_pointer_declarator",
32
+ "abstract_block_pointer_declarator",
33
+ "block_pointer_declarator",
34
+ "function_declarator"
35
+ ]);
36
+ function objcPartTarget(writtenPath) {
37
+ const segments = writtenPath.split("/").filter((segment) => segment.length > 0);
38
+ if (segments.length === 0) return writtenPath;
39
+ if (segments.length > 1) {
40
+ const module = segments[0];
41
+ const moduleDot = module.lastIndexOf(".");
42
+ return moduleDot > 0 ? module.slice(0, moduleDot) : module;
43
+ }
44
+ const file = segments[0];
45
+ const dot = file.lastIndexOf(".");
46
+ return dot > 0 ? file.slice(0, dot) : file;
47
+ }
48
+ function isUnterminatedDeclaration(node) {
49
+ for (let i = 0; i < node.childCount; i++) {
50
+ const child = node.child(i);
51
+ if (child && child.isMissing && child.type === ";") return true;
52
+ }
53
+ return false;
54
+ }
55
+ function hasParseError(node) {
56
+ const stack = [node];
57
+ let guard = 0;
58
+ while (stack.length > 0 && guard++ < 2e4) {
59
+ const n = stack.pop();
60
+ if (!n) continue;
61
+ if (n.type === "ERROR") return true;
62
+ for (let i = 0; i < n.childCount; i++) {
63
+ const c = n.child(i);
64
+ if (c) stack.push(c);
65
+ }
66
+ }
67
+ return false;
68
+ }
69
+ var OBJC_FRAMEWORK_ORDER = ["UIKit", "AppKit", "Cocoa", "Foundation"];
70
+ var OBJC_FRAMEWORK_PATTERNS = {
71
+ UIKit: ["ui_framework"],
72
+ AppKit: ["ui_framework"],
73
+ Cocoa: ["app_umbrella"],
74
+ Foundation: ["base_library"]
75
+ };
76
+ var OBJC_LINKAGE_UNIT_KINDS = /* @__PURE__ */ new Set([
77
+ ASTEngine.SymbolKind.Class,
78
+ ASTEngine.SymbolKind.Interface,
79
+ ASTEngine.SymbolKind.Struct,
80
+ ASTEngine.SymbolKind.Union,
81
+ ASTEngine.SymbolKind.Enum,
82
+ ASTEngine.SymbolKind.Type,
83
+ ASTEngine.SymbolKind.Function,
84
+ ASTEngine.SymbolKind.Variable,
85
+ ASTEngine.SymbolKind.Constant
86
+ ]);
87
+ var OBJC_EXPORT_MARKERS = /* @__PURE__ */ new Set([
88
+ // Foundation's spelling of `extern __attribute__((visibility("default")))` —
89
+ // the marker every framework header uses for its published surface.
90
+ "FOUNDATION_EXPORT",
91
+ // C's own keyword for external linkage. One occurrence in the corpus, and the
92
+ // grammar has a node type for it, so leaving it out would be the arbitrary
93
+ // choice rather than the principled one.
94
+ "extern"
95
+ ]);
96
+ var RealObjectiveCAnalyzer = class {
97
+ analyze(rootNode, content, filePath) {
98
+ const symbols = [];
99
+ const relationships = [];
100
+ const imports = [];
101
+ const exports = [];
102
+ const frameworks = /* @__PURE__ */ new Set();
103
+ const errors = [];
104
+ const walkStack = [
105
+ { node: rootNode, isExit: false, activeScope: "global", parentType: "", isInBody: false, owner: "", bindable: false }
106
+ ];
107
+ let valueFrame = null;
108
+ const blockFrameStack = [];
109
+ const globals = /* @__PURE__ */ new Set();
110
+ const classMembers = /* @__PURE__ */ new Map();
111
+ const classSuper = /* @__PURE__ */ new Map();
112
+ const internalLinkage = /* @__PURE__ */ new Set();
113
+ const exportMarkers = /* @__PURE__ */ new Map();
114
+ const exportLineByName = /* @__PURE__ */ new Map();
115
+ const enumCases = /* @__PURE__ */ new Set();
116
+ {
117
+ const preStack = [rootNode];
118
+ while (preStack.length > 0) {
119
+ const pn = preStack.pop();
120
+ if (!pn || pn.isMissing) continue;
121
+ if (pn.type === "enum_specifier") {
122
+ const elist = this.findChild(pn, (n) => n.type === "enumerator_list");
123
+ if (elist) {
124
+ for (let ei = 0; ei < elist.namedChildCount; ei++) {
125
+ const en = elist.namedChild(ei);
126
+ if (!en || en.type !== "enumerator") continue;
127
+ const nm = this.findChild(en, (n) => n.type === "identifier");
128
+ if (nm && nm.text.trim()) enumCases.add(nm.text);
129
+ }
130
+ }
131
+ }
132
+ if (pn.type === "class_interface" || pn.type === "class_implementation") {
133
+ const cn = this.findChild(pn, (n) => n.type === "identifier" || n.type === "type_identifier");
134
+ if (cn) {
135
+ let members = classMembers.get(cn.text);
136
+ if (!members) {
137
+ members = /* @__PURE__ */ new Set();
138
+ classMembers.set(cn.text, members);
139
+ }
140
+ const memStack = [pn];
141
+ while (memStack.length > 0) {
142
+ const mn = memStack.pop();
143
+ if (!mn) continue;
144
+ if (mn !== pn && (mn.type === "class_interface" || mn.type === "class_implementation" || mn.type === "protocol_declaration")) continue;
145
+ if (mn.type === "property_declaration" || mn.type === "instance_variable") {
146
+ const mname = this.findDeclaredName(mn);
147
+ if (mname) members.add(mname);
148
+ }
149
+ for (let mi = mn.namedChildCount - 1; mi >= 0; mi--) {
150
+ const mc = mn.namedChild(mi);
151
+ if (mc) memStack.push(mc);
152
+ }
153
+ }
154
+ }
155
+ }
156
+ for (let pi = pn.namedChildCount - 1; pi >= 0; pi--) {
157
+ const pc = pn.namedChild(pi);
158
+ if (pc) preStack.push(pc);
159
+ }
160
+ }
161
+ }
162
+ const bindValue = (name) => {
163
+ if (!name) return;
164
+ if (blockFrameStack.length > 0) blockFrameStack[blockFrameStack.length - 1].add(name);
165
+ else if (valueFrame) valueFrame.add(name);
166
+ else globals.add(name);
167
+ };
168
+ const isBoundValue = (name, owner) => {
169
+ for (let bi = blockFrameStack.length - 1; bi >= 0; bi--) {
170
+ if (blockFrameStack[bi].has(name)) return true;
171
+ }
172
+ if (valueFrame && valueFrame.has(name)) return true;
173
+ const cm = classMembers.get(owner);
174
+ if (cm && cm.has(name)) return true;
175
+ if (globals.has(name)) return true;
176
+ if (enumCases.has(name)) return true;
177
+ return false;
178
+ };
179
+ while (walkStack.length > 0) {
180
+ const state = walkStack.pop();
181
+ if (!state) continue;
182
+ if (state.isExit) {
183
+ blockFrameStack.pop();
184
+ continue;
185
+ }
186
+ const { node, isExit, activeScope, parentType, isInBody, owner, bindable } = state;
187
+ if (isExit || !node || node.isMissing) continue;
188
+ let nextActiveScope = activeScope;
189
+ let nextIsInBody = isInBody;
190
+ let nextOwner = owner;
191
+ let nextBindable = bindable;
192
+ if (node.type === "preproc_include") {
193
+ let modName = null;
194
+ let modNode = null;
195
+ let isSystem = false;
196
+ for (let ci = 0; ci < node.namedChildCount; ci++) {
197
+ const cn = node.namedChild(ci);
198
+ if (!cn) continue;
199
+ if (cn.type === "system_lib_string") {
200
+ isSystem = true;
201
+ const txt = cn.text;
202
+ if (txt.length >= 2) {
203
+ modName = txt.slice(1, txt.length - 1);
204
+ modNode = cn;
205
+ }
206
+ } else if (cn.type === "string_literal") {
207
+ const content2 = this.findChild(cn, (n) => n.type === "string_content");
208
+ modName = content2 ? content2.text : cn.text;
209
+ modNode = content2 ?? cn;
210
+ }
211
+ if (modName) break;
212
+ }
213
+ if (modName) {
214
+ if (modName.includes("UIKit")) frameworks.add("UIKit");
215
+ if (modName.includes("Foundation")) frameworks.add("Foundation");
216
+ if (modName.includes("AppKit")) frameworks.add("AppKit");
217
+ if (modName.includes("Cocoa")) frameworks.add("Cocoa");
218
+ imports.push({
219
+ module: modName,
220
+ items: [],
221
+ importType: "include",
222
+ lineNumber: node.startPosition.row + 1,
223
+ isExternal: isSystem || !modName.startsWith(".")
224
+ });
225
+ relationships.push({
226
+ source: "root",
227
+ target: modName,
228
+ type: ASTEngine.RelationshipType.Imports,
229
+ confidence: 1,
230
+ line_number: this.nodeLine(modNode ?? node),
231
+ metadata: {
232
+ relationshipType: "include_dependency",
233
+ isObjCImport: true,
234
+ isObjCModuleImport: false,
235
+ isImport: true,
236
+ isSystem,
237
+ module: modName,
238
+ fileLevel: true
239
+ }
240
+ });
241
+ relationships.push({
242
+ source: "root",
243
+ target: objcPartTarget(modName),
244
+ type: ASTEngine.RelationshipType.PartOf,
245
+ confidence: 1,
246
+ line_number: this.nodeLine(modNode ?? node),
247
+ metadata: {
248
+ partRole: "library",
249
+ partPath: modName,
250
+ isFileInclusion: true,
251
+ isObjCImport: true,
252
+ isObjCModuleImport: false,
253
+ isSystem,
254
+ fileLevel: true,
255
+ evidenceClass: "import_based"
256
+ }
257
+ });
258
+ relationships.push({
259
+ source: "root",
260
+ target: modName,
261
+ type: ASTEngine.RelationshipType.References,
262
+ confidence: 0.9,
263
+ line_number: this.nodeLine(modNode ?? node),
264
+ metadata: { isObjCImport: true, isObjCModuleImport: false, reasonCode: "include-target-not-resolved" }
265
+ });
266
+ }
267
+ }
268
+ if (node.type === "module_import") {
269
+ const nameNode = this.findChild(node, (n) => n.type === "identifier" || n.type === "type_identifier");
270
+ const modName = nameNode ? nameNode.text.trim() : "";
271
+ if (modName) {
272
+ imports.push({
273
+ module: modName,
274
+ items: [],
275
+ importType: "module",
276
+ lineNumber: node.startPosition.row + 1,
277
+ isExternal: true
278
+ });
279
+ relationships.push({
280
+ source: "root",
281
+ target: modName,
282
+ type: ASTEngine.RelationshipType.References,
283
+ confidence: 0.9,
284
+ line_number: this.nodeLine(nameNode ?? node),
285
+ // Its OWN code, not the include's: the target names a MODULE and nothing in this
286
+ // walk knows whether the build defines a module under that name. Pinned by
287
+ // objc/module-imports ("the sub-1.0 twin carries its OWN reason code").
288
+ metadata: { isObjCImport: true, isObjCModuleImport: true, reasonCode: "clang-module-name-not-resolved" }
289
+ });
290
+ relationships.push({
291
+ source: "root",
292
+ target: modName,
293
+ type: ASTEngine.RelationshipType.Imports,
294
+ confidence: 1,
295
+ line_number: this.nodeLine(nameNode ?? node),
296
+ metadata: {
297
+ relationshipType: "module_import",
298
+ isObjCImport: true,
299
+ isObjCModuleImport: true,
300
+ isImport: true,
301
+ isSystem: false,
302
+ module: modName,
303
+ fileLevel: true
304
+ }
305
+ });
306
+ }
307
+ }
308
+ if (node.type === "class_interface" || node.type === "class_implementation") {
309
+ const identNodes = [];
310
+ for (let ci = 0; ci < node.namedChildCount; ci++) {
311
+ const cn = node.namedChild(ci);
312
+ if (cn && cn.type === "identifier") identNodes.push(cn.text);
313
+ }
314
+ const nameNode = this.findChild(node, (n) => n.type === "identifier" || n.type === "type_identifier");
315
+ if (nameNode) {
316
+ nextActiveScope = nameNode.text;
317
+ nextOwner = nameNode.text;
318
+ let hasParen = false;
319
+ for (let ci = 0; ci < node.childCount; ci++) {
320
+ const c = node.child(ci);
321
+ if (c && !c.isNamed && c.type === "(") {
322
+ hasParen = true;
323
+ break;
324
+ }
325
+ }
326
+ const isCategory = hasParen;
327
+ if (isCategory) {
328
+ symbols.push({
329
+ name: nameNode.text,
330
+ kind: ASTEngine.SymbolKind.Extension,
331
+ range: this.getRange(node),
332
+ ownerName: activeScope !== "global" ? activeScope : void 0,
333
+ metadata: { objcSpecific: true, isCategory: true, categoryName: identNodes[1] }
334
+ });
335
+ for (const p of this.objcInheritanceLists(node).protocols) {
336
+ relationships.push({ source: nameNode.text, target: p.name, type: ASTEngine.RelationshipType.Implements, confidence: 1, line_number: this.nodeLine(p.node), metadata: { objcSpecific: true, isProtocol: true, isCategoryProtocol: true } });
337
+ }
338
+ } else {
339
+ const { superClass: superName, superNode, protocols: protocolList } = this.objcInheritanceLists(node);
340
+ const protocols = protocolList.map((p) => p.name);
341
+ if (superName) classSuper.set(nameNode.text, superName);
342
+ if (superName) {
343
+ relationships.push({ source: nameNode.text, target: superName, type: ASTEngine.RelationshipType.Extends, confidence: 1, line_number: this.nodeLine(superNode ?? nameNode), metadata: { objcSpecific: true } });
344
+ }
345
+ for (const p of protocolList) {
346
+ relationships.push({ source: nameNode.text, target: p.name, type: ASTEngine.RelationshipType.Implements, confidence: 1, line_number: this.nodeLine(p.node ?? nameNode), metadata: { objcSpecific: true, isProtocol: true } });
347
+ }
348
+ symbols.push({
349
+ name: nameNode.text,
350
+ kind: ASTEngine.SymbolKind.Class,
351
+ range: this.getRange(node),
352
+ ownerName: activeScope !== "global" ? activeScope : void 0,
353
+ metadata: { objcSpecific: true, type: node.type, superClass: superName, protocols }
354
+ });
355
+ }
356
+ }
357
+ } else if (node.type === "protocol_declaration") {
358
+ const nameNode = this.findChild(node, (n) => n.type === "identifier");
359
+ if (nameNode) {
360
+ nextActiveScope = nameNode.text;
361
+ symbols.push({
362
+ name: nameNode.text,
363
+ kind: ASTEngine.SymbolKind.Interface,
364
+ range: this.getRange(node),
365
+ metadata: { objcSpecific: true, isProtocol: true }
366
+ });
367
+ const refList = this.findChild(node, (n) => n.type === "protocol_reference_list");
368
+ if (refList) {
369
+ for (let ci = 0; ci < refList.namedChildCount; ci++) {
370
+ const cn = refList.namedChild(ci);
371
+ if (cn && cn.type === "identifier") {
372
+ relationships.push({ source: nameNode.text, target: cn.text, type: ASTEngine.RelationshipType.Extends, confidence: 1, line_number: this.nodeLine(cn), metadata: { objcSpecific: true, isProtocolInheritance: true } });
373
+ }
374
+ }
375
+ }
376
+ }
377
+ } else if (node.type === "method_declaration" || node.type === "method_definition") {
378
+ const sel = this.fullMethodSelector(node);
379
+ if (sel) {
380
+ nextActiveScope = sel;
381
+ if (node.type === "method_definition") {
382
+ valueFrame = /* @__PURE__ */ new Set();
383
+ nextBindable = true;
384
+ }
385
+ const isClassMethod = this.isClassMethodToken(node);
386
+ symbols.push({
387
+ name: sel,
388
+ kind: ASTEngine.SymbolKind.Method,
389
+ range: this.getRange(node),
390
+ ownerName: activeScope !== "global" ? activeScope : void 0,
391
+ complexity: this.calculateCyclomaticComplexity(node),
392
+ metadata: { objcSpecific: true, isClassMethod, isInstanceMethod: !isClassMethod, selector: sel }
393
+ });
394
+ }
395
+ } else if (node.type === "function_definition") {
396
+ const nameNode = this.findFunctionName(node);
397
+ if (nameNode) {
398
+ for (let si = 0; si < node.namedChildCount; si++) {
399
+ const sc = node.namedChild(si);
400
+ if (sc && sc.type === "storage_class_specifier" && sc.text.trim() === "static") {
401
+ internalLinkage.add(nameNode.text);
402
+ break;
403
+ }
404
+ }
405
+ nextActiveScope = nameNode.text;
406
+ valueFrame = /* @__PURE__ */ new Set();
407
+ nextOwner = "";
408
+ nextBindable = true;
409
+ globals.add(nameNode.text);
410
+ symbols.push({
411
+ name: nameNode.text,
412
+ kind: ASTEngine.SymbolKind.Function,
413
+ range: this.getRange(node),
414
+ complexity: this.calculateCyclomaticComplexity(node),
415
+ metadata: { objcSpecific: true }
416
+ });
417
+ }
418
+ } else if (node.type === "property_declaration") {
419
+ const nameNode = this.findDeclaredName(node);
420
+ if (nameNode) {
421
+ symbols.push({
422
+ name: nameNode,
423
+ kind: ASTEngine.SymbolKind.Property,
424
+ range: this.getRange(node),
425
+ ownerName: activeScope !== "global" ? activeScope : void 0,
426
+ metadata: { objcSpecific: true }
427
+ });
428
+ }
429
+ } else if (node.type === "instance_variable") {
430
+ const nameNode = this.findDeclaredName(node);
431
+ if (nameNode) {
432
+ symbols.push({
433
+ name: nameNode,
434
+ kind: ASTEngine.SymbolKind.Property,
435
+ range: this.getRange(node),
436
+ ownerName: activeScope !== "global" ? activeScope : void 0,
437
+ metadata: { objcSpecific: true, isInstanceVariable: true }
438
+ });
439
+ }
440
+ } else if (node.type === "struct_specifier" || node.type === "union_specifier") {
441
+ const sname = this.findChild(node, (n) => n.type === "type_identifier");
442
+ const tname = sname ? sname.text : this.typedefName(node);
443
+ const defines = !!this.findChild(node, (n) => n.type === "field_declaration_list") || node.parent != null && node.parent.type === "type_definition";
444
+ if (tname && defines) {
445
+ symbols.push({
446
+ name: tname,
447
+ kind: ASTEngine.SymbolKind.Struct,
448
+ range: this.getRange(node),
449
+ ownerName: activeScope !== "global" ? activeScope : void 0,
450
+ metadata: { objcSpecific: true, isCStruct: true }
451
+ });
452
+ const flist = this.findChild(node, (n) => n.type === "field_declaration_list");
453
+ if (flist) {
454
+ for (let fi = 0; fi < flist.namedChildCount; fi++) {
455
+ const fd = flist.namedChild(fi);
456
+ if (!fd || fd.type !== "field_declaration") continue;
457
+ const fname = this.deepFind(fd, (n) => n.type === "field_identifier");
458
+ if (fname && fname.text.trim()) {
459
+ symbols.push({
460
+ name: fname.text,
461
+ kind: ASTEngine.SymbolKind.Property,
462
+ range: this.getRange(fd),
463
+ ownerName: tname,
464
+ metadata: { objcSpecific: true, isStructField: true }
465
+ });
466
+ }
467
+ }
468
+ }
469
+ }
470
+ } else if (node.type === "enum_specifier") {
471
+ const ename = this.findChild(node, (n) => n.type === "type_identifier");
472
+ const macroArgs = this.findChild(node, (n) => n.type === "macro_enum_arguments");
473
+ let macroName = null;
474
+ if (macroArgs) {
475
+ for (let i = macroArgs.namedChildCount - 1; i >= 0; i--) {
476
+ const c = macroArgs.namedChild(i);
477
+ if (c && c.text && c.text.trim()) {
478
+ macroName = c.text.trim();
479
+ break;
480
+ }
481
+ }
482
+ }
483
+ const tname = macroName ?? (ename ? ename.text : this.typedefName(node));
484
+ const defines = !!this.findChild(node, (n) => n.type === "enumerator_list") || node.parent != null && node.parent.type === "type_definition";
485
+ if (tname && defines) {
486
+ symbols.push({
487
+ name: tname,
488
+ kind: ASTEngine.SymbolKind.Enum,
489
+ range: this.getRange(node),
490
+ ownerName: activeScope !== "global" ? activeScope : void 0,
491
+ metadata: { objcSpecific: true, isCEnum: true }
492
+ });
493
+ const elist = this.findChild(node, (n) => n.type === "enumerator_list");
494
+ if (elist) {
495
+ for (let ei = 0; ei < elist.namedChildCount; ei++) {
496
+ const en = elist.namedChild(ei);
497
+ if (!en || en.type !== "enumerator") continue;
498
+ const mname = this.findChild(en, (n) => n.type === "identifier");
499
+ if (mname && mname.text.trim()) {
500
+ symbols.push({
501
+ name: mname.text,
502
+ kind: ASTEngine.SymbolKind.EnumMember,
503
+ range: this.getRange(en),
504
+ ownerName: tname,
505
+ metadata: { objcSpecific: true }
506
+ });
507
+ }
508
+ }
509
+ }
510
+ }
511
+ } else if (node.type === "type_definition") {
512
+ let alias = null;
513
+ for (let ti = 0; ti < node.namedChildCount; ti++) {
514
+ const tc = node.namedChild(ti);
515
+ if (tc && (tc.type === "type_identifier" || tc.type === "identifier")) alias = tc;
516
+ }
517
+ const hasAggregate = !!this.findChild(node, (n) => n.type === "struct_specifier" || n.type === "union_specifier" || n.type === "enum_specifier");
518
+ if (alias && !hasAggregate) {
519
+ symbols.push({
520
+ name: alias.text,
521
+ kind: ASTEngine.SymbolKind.Type,
522
+ range: this.getRange(node),
523
+ ownerName: activeScope !== "global" ? activeScope : void 0,
524
+ metadata: { objcSpecific: true, isTypedef: true }
525
+ });
526
+ }
527
+ } else if (node.type === "block_literal") {
528
+ if (nextBindable) blockFrameStack.push(/* @__PURE__ */ new Set());
529
+ } else if (node.type === "declaration" && activeScope === "global") {
530
+ if (!isUnterminatedDeclaration(node) && !hasParseError(node)) {
531
+ let exportMarker = null;
532
+ let storageStatic = false;
533
+ for (let si = 0; si < node.namedChildCount; si++) {
534
+ const sc = node.namedChild(si);
535
+ if (!sc || sc.type !== "storage_class_specifier") continue;
536
+ const st = sc.text.trim();
537
+ if (st === "static") storageStatic = true;
538
+ if (!exportMarker && OBJC_EXPORT_MARKERS.has(st)) exportMarker = st;
539
+ }
540
+ for (let di = 0; di < node.namedChildCount; di++) {
541
+ const dc = node.namedChild(di);
542
+ if (!dc) continue;
543
+ let dname = null;
544
+ const isPrototype = dc.type === "function_declarator" || (dc.type === "pointer_declarator" || dc.type === "array_declarator" || dc.type === "parenthesized_declarator" || dc.type === "attributed_declarator") && this.declaratorChainHasFunctionDeclarator(dc);
545
+ if (dc.type === "init_declarator") {
546
+ dname = this.declaratorName(dc);
547
+ } else if (dc.type === "identifier") {
548
+ dname = dc.text;
549
+ } else if (isPrototype || dc.type === "pointer_declarator" || dc.type === "array_declarator" || dc.type === "parenthesized_declarator" || dc.type === "attributed_declarator") {
550
+ dname = this.declaratorName(dc);
551
+ }
552
+ if (dname) {
553
+ symbols.push({
554
+ name: dname,
555
+ kind: isPrototype ? ASTEngine.SymbolKind.Function : ASTEngine.SymbolKind.Variable,
556
+ range: this.getRange(node),
557
+ metadata: isPrototype ? { objcSpecific: true, isCFunctionPrototype: true } : { objcSpecific: true }
558
+ });
559
+ globals.add(dname);
560
+ if (storageStatic) internalLinkage.add(dname);
561
+ if (exportMarker) {
562
+ exportMarkers.set(dname, exportMarker);
563
+ exportLineByName.set(dname, this.nodeLine(node));
564
+ }
565
+ }
566
+ }
567
+ }
568
+ } else if (node.type === "declaration" && nextBindable) {
569
+ for (let di = 0; di < node.namedChildCount; di++) {
570
+ const dc = node.namedChild(di);
571
+ if (!dc) continue;
572
+ if (dc.type === "init_declarator") {
573
+ const dname = this.declaratorName(dc);
574
+ if (dname) bindValue(dname);
575
+ } else if (dc.type === "identifier") {
576
+ bindValue(dc.text);
577
+ } else if (dc.type === "pointer_declarator" || dc.type === "array_declarator" || dc.type === "parenthesized_declarator" || dc.type === "attributed_declarator") {
578
+ const dname = this.declaratorName(dc);
579
+ if (dname) bindValue(dname);
580
+ }
581
+ }
582
+ } else if ((node.type === "method_parameter" || node.type === "parameter_declaration") && nextBindable) {
583
+ const pname = this.declaratorName(node);
584
+ if (pname) bindValue(pname);
585
+ } else if (node.type === "for_statement" && nextBindable) {
586
+ const cc = node.childCount;
587
+ for (let fi = 0; fi < cc; fi++) {
588
+ const fc = node.child(fi);
589
+ if (fc && !fc.isNamed && fc.type === "in" && fi > 0) {
590
+ for (let fj = fi - 1; fj >= 0; fj--) {
591
+ const pc = node.child(fj);
592
+ if (pc && pc.isNamed) {
593
+ if (pc.type === "identifier") bindValue(pc.text);
594
+ break;
595
+ }
596
+ }
597
+ break;
598
+ }
599
+ }
600
+ } else if (node.type === "message_expression") {
601
+ const msgSel = this.fullMessageSelector(node);
602
+ if (msgSel && activeScope !== "global" && isInBody) {
603
+ const firstKw = msgSel.split(":")[0];
604
+ if (firstKw === "alloc" || firstKw === "new") {
605
+ let receiverLeaf = null;
606
+ let recvTargetNode = null;
607
+ const receiver = node.namedChild(0);
608
+ if (receiver) {
609
+ if (receiver.type === "identifier" || receiver.type === "type_identifier") {
610
+ const keyword = receiver.text === "self" || receiver.text === "super" ? receiver.text : null;
611
+ if (keyword) {
612
+ const resolved = keyword === "self" ? owner : classSuper.get(owner);
613
+ if (resolved && resolved !== "global") {
614
+ receiverLeaf = resolved;
615
+ recvTargetNode = receiver;
616
+ }
617
+ } else {
618
+ receiverLeaf = receiver.text;
619
+ recvTargetNode = receiver;
620
+ }
621
+ } else if (receiver.type === "message_expression") {
622
+ const innerSel = this.findChild(receiver, (n) => n.type === "identifier");
623
+ if (innerSel && (innerSel.text === "alloc" || innerSel.text === "new")) {
624
+ const innerRecv = receiver.namedChild(0);
625
+ if (innerRecv && (innerRecv.type === "identifier" || innerRecv.type === "type_identifier")) {
626
+ receiverLeaf = innerRecv.text;
627
+ recvTargetNode = innerRecv;
628
+ }
629
+ }
630
+ }
631
+ }
632
+ if (receiverLeaf) {
633
+ relationships.push({ source: activeScope, target: receiverLeaf, type: ASTEngine.RelationshipType.Instantiates, confidence: 0.9, line_number: this.nodeLine(recvTargetNode ?? node), metadata: { isObjCAlloc: true, reasonCode: "allocator-receiver-not-cross-file-checked" } });
634
+ }
635
+ } else {
636
+ relationships.push({
637
+ source: activeScope,
638
+ target: msgSel,
639
+ type: ASTEngine.RelationshipType.Calls,
640
+ confidence: 0.95,
641
+ line_number: this.nodeLine(this.fullMessageSelectorNode(node) ?? node),
642
+ metadata: { isMessageExpression: true, reasonCode: "dynamic-dispatch-selector" }
643
+ });
644
+ }
645
+ const msgKids = [];
646
+ for (let mi = 0; mi < node.childCount; mi++) {
647
+ const mc = node.child(mi);
648
+ if (!mc || !mc.isNamed || mc.type !== "identifier") continue;
649
+ const nxt = mi + 1 < node.childCount ? node.child(mi + 1) : null;
650
+ msgKids.push({ n: mc, isKeyword: !!(nxt && !nxt.isNamed && nxt.type === ":") });
651
+ }
652
+ const recvNode = node.namedChild(0);
653
+ const hasKeyword = msgKids.some((k) => k.isKeyword);
654
+ let lastNonRecv = -1;
655
+ if (!hasKeyword) {
656
+ for (let ki = msgKids.length - 1; ki >= 0; ki--) {
657
+ if (!this.sameNode(msgKids[ki].n, recvNode)) {
658
+ lastNonRecv = ki;
659
+ break;
660
+ }
661
+ }
662
+ }
663
+ for (let ki = 0; ki < msgKids.length; ki++) {
664
+ const kk = msgKids[ki];
665
+ if (kk.isKeyword || ki === lastNonRecv) continue;
666
+ const ktext = kk.n.text.trim();
667
+ if (!ktext || BARE_READ_SKIP.has(ktext)) continue;
668
+ if (this.sameNode(kk.n, recvNode)) {
669
+ if (ktext === "self" || ktext === "super") continue;
670
+ }
671
+ if (isBoundValue(ktext, nextOwner)) {
672
+ relationships.push({
673
+ source: activeScope,
674
+ target: ktext,
675
+ type: ASTEngine.RelationshipType.Uses,
676
+ confidence: 0.9,
677
+ line_number: this.nodeLine(kk.n),
678
+ metadata: { isBareRead: true, isMessageArgument: !this.sameNode(kk.n, recvNode), reasonCode: "value-read-by-scope-inference" }
679
+ });
680
+ }
681
+ }
682
+ }
683
+ } else if (node.type === "call_expression") {
684
+ const funcNode = node.childForFieldName("function") ?? this.findChild(node, (n) => n.type === "identifier");
685
+ const inAtDirective = (() => {
686
+ let p = node.parent;
687
+ let g = 0;
688
+ while (p && g++ < 3) {
689
+ if (p.type === "at_expression") return true;
690
+ if (p.type === "message_expression" || p.type === "call_expression") return false;
691
+ p = p.parent;
692
+ }
693
+ return false;
694
+ })();
695
+ if (funcNode && activeScope !== "global" && isInBody && !(inAtDirective && funcNode.text === "protocol")) {
696
+ if (funcNode.type === "field_expression") {
697
+ const fieldNode = funcNode.childForFieldName("field") ?? this.findChild(funcNode, (n) => n.type === "field_identifier");
698
+ if (fieldNode && fieldNode.text.trim()) {
699
+ relationships.push({
700
+ source: activeScope,
701
+ target: fieldNode.text,
702
+ type: ASTEngine.RelationshipType.Calls,
703
+ confidence: 0.95,
704
+ line_number: this.nodeLine(fieldNode),
705
+ metadata: { isFieldInvocation: true, reasonCode: "field-callee-kind-not-determined" }
706
+ });
707
+ }
708
+ } else {
709
+ relationships.push({
710
+ source: activeScope,
711
+ target: funcNode.text,
712
+ type: ASTEngine.RelationshipType.Calls,
713
+ confidence: 0.95,
714
+ line_number: this.nodeLine(funcNode),
715
+ metadata: { isCFunctionCall: true }
716
+ });
717
+ }
718
+ }
719
+ } else if (node.type === "field_expression" && activeScope !== "global" && isInBody) {
720
+ const field = node.childForFieldName("field") ?? this.findChild(node, (n) => n.type === "field_identifier");
721
+ if (field && field.text.trim()) {
722
+ if (parentType !== "assignment_expression") {
723
+ relationships.push({
724
+ source: activeScope,
725
+ target: field.text,
726
+ type: ASTEngine.RelationshipType.Uses,
727
+ confidence: 0.9,
728
+ line_number: this.nodeLine(field),
729
+ // Honesty §4.1: sub-1.0 rows carry their own machine-readable code, and
730
+ // `uses` has ONE — the read is exact (the field name is in the source) and
731
+ // what the row infers is WHICH binding the name denotes, resolved against
732
+ // the enclosing class's members. The `calls` rows for the same node keep a
733
+ // different word (`field-callee-kind-not-determined`), because the call is
734
+ // about the callable's KIND and the read is about the binding
735
+ // (objc/generics-and-reason-codes pins the family, objc/field-invocation
736
+ // pins that the two labels on one node are told apart by their flags).
737
+ metadata: { isFieldRead: true, relationshipType: "field_access", reasonCode: "value-read-by-scope-inference" }
738
+ });
739
+ }
740
+ }
741
+ } else if (node.type === "identifier" && nextBindable && activeScope !== "global") {
742
+ const text = node.text.trim();
743
+ if (text && !BARE_READ_SKIP.has(text)) {
744
+ const parent = node.parent;
745
+ const ptype = parent ? parent.type : "";
746
+ let isName = false;
747
+ if (ptype === "method_declaration" || ptype === "method_definition" || ptype === "method_parameter" || ptype === "parameter_declaration" || ptype === "property_implementation" || ptype === "selector_expression" || ptype === "function_declarator" || ptype === "message_expression" || ptype === "declaration") {
748
+ isName = true;
749
+ } else if (ptype === "call_expression" && parent && parent.namedChild(0) && this.sameNode(parent.namedChild(0), node)) {
750
+ isName = true;
751
+ } else if (ptype === "assignment_expression" && parent && parent.namedChild(0) && this.spanContains(parent.namedChild(0), node)) {
752
+ isName = true;
753
+ } else if (ptype === "for_statement" && parent && this.isForInVariable(parent, node)) {
754
+ isName = true;
755
+ } else if (this.isDeclaratorName(node) || this.isParameterDeclaredName(node)) {
756
+ isName = true;
757
+ }
758
+ if (!isName && isBoundValue(text, nextOwner)) {
759
+ relationships.push({
760
+ source: activeScope,
761
+ target: text,
762
+ type: ASTEngine.RelationshipType.Uses,
763
+ confidence: 0.9,
764
+ line_number: this.nodeLine(node),
765
+ // Honesty §4.1: the read is exact — the identifier is in the source — and what
766
+ // the row INFERS is which binding the name denotes (parameter, body local,
767
+ // same-file ivar/property, file global). That is the whole of the doubt, so it
768
+ // is the whole of the code (objc/generics-and-reason-codes pins the word).
769
+ metadata: { isBareRead: true, reasonCode: "value-read-by-scope-inference" }
770
+ });
771
+ }
772
+ }
773
+ }
774
+ const childCount = node.namedChildCount;
775
+ nextIsInBody = nextIsInBody || node.type === "method_definition" || node.type === "method_declaration" || node.type === "function_definition" || node.type === "block_literal";
776
+ if (node.type === "block_literal" && nextBindable) {
777
+ walkStack.push({ node, isExit: true, activeScope: nextActiveScope, parentType: node.type, isInBody: nextIsInBody, owner: nextOwner, bindable: nextBindable });
778
+ }
779
+ for (let i = childCount - 1; i >= 0; i--) {
780
+ const child = node.namedChild(i);
781
+ if (child) {
782
+ walkStack.push({ node: child, isExit: false, activeScope: nextActiveScope, parentType: node.type, isInBody: nextIsInBody, owner: nextOwner, bindable: nextBindable });
783
+ }
784
+ }
785
+ }
786
+ const uniqueSymbolsMap = /* @__PURE__ */ new Map();
787
+ for (let i = 0; i < symbols.length; i++) {
788
+ const s = symbols[i];
789
+ const catKey = s.kind === ASTEngine.SymbolKind.Extension && s.metadata?.isCategory ? `:${s.metadata?.categoryName ?? ""}` : "";
790
+ uniqueSymbolsMap.set(`${s.kind}:${s.ownerName || ""}:${s.name}${catKey}`, s);
791
+ }
792
+ const uniqueSymbols = Array.from(uniqueSymbolsMap.values());
793
+ const uniqueRelMap = /* @__PURE__ */ new Map();
794
+ for (let i = 0; i < relationships.length; i++) {
795
+ const rk = `${relationships[i].type}|${relationships[i].source}->${relationships[i].target}`;
796
+ const prev = uniqueRelMap.get(rk);
797
+ if (prev) {
798
+ const pm = prev.metadata ?? {};
799
+ const nm = relationships[i].metadata ?? {};
800
+ if (nm.isFieldRead) pm.isFieldRead = true;
801
+ if (nm.isBareRead) pm.isBareRead = true;
802
+ prev.metadata = pm;
803
+ if (prev.line_number === void 0 && relationships[i].line_number !== void 0) {
804
+ prev.line_number = relationships[i].line_number;
805
+ }
806
+ } else {
807
+ uniqueRelMap.set(rk, relationships[i]);
808
+ }
809
+ }
810
+ const uniqueRelationships = Array.from(uniqueRelMap.values());
811
+ const relKeySet = new Set(uniqueRelMap.keys());
812
+ for (const s of uniqueSymbols) {
813
+ if (s.ownerName || s.scope) continue;
814
+ if (s.kind === ASTEngine.SymbolKind.Extension) continue;
815
+ if (!OBJC_LINKAGE_UNIT_KINDS.has(s.kind)) continue;
816
+ if (internalLinkage.has(s.name)) continue;
817
+ const key = `${ASTEngine.RelationshipType.Exports}|root->${s.name}`;
818
+ if (relKeySet.has(key)) continue;
819
+ relKeySet.add(key);
820
+ const marker = exportMarkers.get(s.name);
821
+ uniqueRelationships.push({
822
+ source: "root",
823
+ target: s.name,
824
+ type: ASTEngine.RelationshipType.Exports,
825
+ confidence: 1,
826
+ ...exportLineByName.has(s.name) ? { line_number: exportLineByName.get(s.name) } : s.range?.startLine ? { line_number: s.range.startLine } : {},
827
+ metadata: {
828
+ relationshipType: "module_export",
829
+ isExport: true,
830
+ isObjCExport: true,
831
+ fileLevel: true,
832
+ ...marker ? { exportMarker: marker } : {}
833
+ }
834
+ });
835
+ }
836
+ for (const s of uniqueSymbols) {
837
+ const _owner = s.ownerName || s.scope;
838
+ if (_owner) {
839
+ const key = `${ASTEngine.RelationshipType.Contains}|${_owner}->${s.name}`;
840
+ if (!relKeySet.has(key)) {
841
+ relKeySet.add(key);
842
+ uniqueRelationships.push({
843
+ source: _owner,
844
+ target: s.name,
845
+ type: ASTEngine.RelationshipType.Contains,
846
+ confidence: 1,
847
+ // M59.A7: the line of the name this edge NAMES — the member's own declared
848
+ // name line, taken from the symbol's range (the symbol and the edge come from
849
+ // one selection: the same node named both).
850
+ ...s.range?.startLine ? { line_number: s.range.startLine } : {},
851
+ metadata: { isContained: true }
852
+ });
853
+ }
854
+ }
855
+ }
856
+ const classNameSet = /* @__PURE__ */ new Set();
857
+ for (const s of uniqueSymbols) {
858
+ if (s.kind === ASTEngine.SymbolKind.Class) classNameSet.add(s.name);
859
+ }
860
+ const baseByClass = /* @__PURE__ */ new Map();
861
+ for (const r of uniqueRelationships) {
862
+ if (r.type === ASTEngine.RelationshipType.Extends && classNameSet.has(String(r.source)) && !r.metadata?.isProtocolInheritance) {
863
+ const t = String(r.target);
864
+ if (t && t !== "NSObject" && t !== "NSProxy") baseByClass.set(String(r.source), t);
865
+ }
866
+ }
867
+ const methodsByOwner = /* @__PURE__ */ new Map();
868
+ for (const s of uniqueSymbols) {
869
+ if (s.kind === ASTEngine.SymbolKind.Method && s.ownerName) {
870
+ let names = methodsByOwner.get(s.ownerName);
871
+ if (!names) {
872
+ names = /* @__PURE__ */ new Set();
873
+ methodsByOwner.set(s.ownerName, names);
874
+ }
875
+ names.add(s.name);
876
+ }
877
+ }
878
+ for (const s of uniqueSymbols) {
879
+ if (s.kind !== ASTEngine.SymbolKind.Method || !s.ownerName) continue;
880
+ const base = baseByClass.get(s.ownerName);
881
+ if (!base) continue;
882
+ const inFileBaseMethods = classNameSet.has(base) ? methodsByOwner.get(base) : void 0;
883
+ if (inFileBaseMethods?.has(s.name)) {
884
+ const key = `${ASTEngine.RelationshipType.Overrides}|${s.ownerName}.${s.name}->${base}.${s.name}`;
885
+ if (!relKeySet.has(key)) {
886
+ relKeySet.add(key);
887
+ uniqueRelationships.push({
888
+ source: `${s.ownerName}.${s.name}`,
889
+ target: `${base}.${s.name}`,
890
+ type: ASTEngine.RelationshipType.Overrides,
891
+ confidence: 0.95,
892
+ // M59.A7: the OVERRIDING member's own declaration line (the base's copy lives
893
+ // in another translation unit, and the row is written here).
894
+ ...s.range?.startLine ? { line_number: s.range.startLine } : {},
895
+ // The VERIFIED branch names its own evidence: the superclass is declared in THIS
896
+ // file and declares the same full selector, so this row is a verification. The
897
+ // guessed branch below keeps `first-parent-guess` — one code for both would call
898
+ // a verification a guess (objc/selector-attributes pins the pair).
899
+ metadata: { isOverride: true, reasonCode: "verified-base-declared-in-file" }
900
+ });
901
+ }
902
+ } else if (!classNameSet.has(base)) {
903
+ const key = `${ASTEngine.RelationshipType.Overrides}|${s.ownerName}.${s.name}->${base}.${s.name}`;
904
+ if (!relKeySet.has(key)) {
905
+ relKeySet.add(key);
906
+ uniqueRelationships.push({
907
+ source: `${s.ownerName}.${s.name}`,
908
+ target: `${base}.${s.name}`,
909
+ type: ASTEngine.RelationshipType.Overrides,
910
+ confidence: 0.5,
911
+ ...s.range?.startLine ? { line_number: s.range.startLine } : {},
912
+ metadata: { isOverride: true, reasonCode: "first-parent-guess" }
913
+ });
914
+ }
915
+ }
916
+ }
917
+ const fileFunctions = /* @__PURE__ */ new Set();
918
+ for (const s of uniqueSymbols) {
919
+ if (s.kind === ASTEngine.SymbolKind.Function) fileFunctions.add(s.name);
920
+ }
921
+ for (const r of uniqueRelationships) {
922
+ if (r.type !== ASTEngine.RelationshipType.Calls) continue;
923
+ const md = r.metadata ?? {};
924
+ if (!md.isCFunctionCall || md.reasonCode) continue;
925
+ if (!fileFunctions.has(String(r.target))) md.reasonCode = "callee-not-resolved-in-unit";
926
+ }
927
+ const publishedNames = /* @__PURE__ */ new Set();
928
+ for (const s of uniqueSymbols) if (s.name) publishedNames.add(s.name);
929
+ for (const r of uniqueRelationships) {
930
+ if (r.type !== ASTEngine.RelationshipType.Uses) continue;
931
+ const md = r.metadata ?? {};
932
+ if (md.isBareRead !== true || md.isFieldRead === true) continue;
933
+ const leaf = String(r.target ?? "");
934
+ if (!leaf || !r.source || publishedNames.has(leaf)) continue;
935
+ r.target = `${r.source}${BINDING_SEP}${leaf}`;
936
+ md.localBinding = true;
937
+ }
938
+ this.classifyEvidenceTier(uniqueRelationships);
939
+ {
940
+ const seenExportNames = /* @__PURE__ */ new Set();
941
+ for (let i = 0; i < uniqueSymbols.length; i++) {
942
+ const s = uniqueSymbols[i];
943
+ if (s.ownerName) continue;
944
+ if (s.kind === ASTEngine.SymbolKind.Extension) continue;
945
+ if (seenExportNames.has(s.name)) continue;
946
+ seenExportNames.add(s.name);
947
+ exports.push({ name: s.name, kind: s.kind, range: s.range, visibility: internalLinkage.has(s.name) ? "private" : "public" });
948
+ }
949
+ }
950
+ const detectedFrameworks = [...frameworks];
951
+ detectedFrameworks.sort((a, b) => {
952
+ const ra = OBJC_FRAMEWORK_ORDER.indexOf(a);
953
+ const rb = OBJC_FRAMEWORK_ORDER.indexOf(b);
954
+ if (ra === -1 && rb === -1) return a < b ? -1 : a > b ? 1 : 0;
955
+ if (ra === -1) return 1;
956
+ if (rb === -1) return -1;
957
+ return ra - rb;
958
+ });
959
+ const frameworkPatterns = detectedFrameworks.map((f) => ({
960
+ framework: f,
961
+ patterns: OBJC_FRAMEWORK_PATTERNS[f] ?? ["usage"]
962
+ }));
963
+ return {
964
+ language: "objective-c",
965
+ symbols: uniqueSymbols,
966
+ relationships: uniqueRelationships,
967
+ imports,
968
+ exports,
969
+ errors,
970
+ metadata: {
971
+ complexity: uniqueSymbols.length,
972
+ linesOfCode: content.split("\n").length,
973
+ detectedFrameworks,
974
+ framework: detectedFrameworks.length > 0 ? detectedFrameworks[0] : void 0,
975
+ frameworkPatterns
976
+ }
977
+ };
978
+ }
979
+ /**
980
+ * Evidence-tier classification — same rules as the swift/dart/csharp
981
+ * family (see swift wave #266 for the full rationale).
982
+ */
983
+ classifyEvidenceTier(relationships) {
984
+ for (const r of relationships) {
985
+ const type = r.type ?? r.relationshipType ?? "";
986
+ const md = r.metadata ?? {};
987
+ if (type === "contains") {
988
+ md.evidenceClass = "structural";
989
+ continue;
990
+ }
991
+ if (type === "exports") {
992
+ md.evidenceClass = "structural";
993
+ continue;
994
+ }
995
+ if (type === "imports" || type === "references") {
996
+ md.evidenceClass = "import_based";
997
+ continue;
998
+ }
999
+ if (type === "part_of") {
1000
+ md.evidenceClass = "import_based";
1001
+ continue;
1002
+ }
1003
+ if (type === "calls" || type === "uses") {
1004
+ md.evidenceClass = (r.confidence ?? 0) >= 0.85 ? "usage_contextual" : "inferred";
1005
+ continue;
1006
+ }
1007
+ if (type === "instantiates" || type === "extends" || type === "implements") {
1008
+ md.evidenceClass = (r.confidence ?? 0) >= 0.9 ? "structural" : "inferred";
1009
+ continue;
1010
+ }
1011
+ if (type === "overrides") {
1012
+ md.evidenceClass = (r.confidence ?? 0) >= 0.9 ? "structural" : "inferred";
1013
+ continue;
1014
+ }
1015
+ md.evidenceClass = "usage_contextual";
1016
+ }
1017
+ }
1018
+ /**
1019
+ * Per-symbol cyclomatic complexity (managed-OO family contract — csharp
1020
+ * #264 wave precedent). The objc grammar is a C superset, so the node set
1021
+ * mirrors the C analyzer's exactly: branch constructs + short-circuit
1022
+ * &&/|| operators only; base 1. Probe-verified against the pinned wasm
1023
+ * (do_statement confirmed present, 2026-08-30).
1024
+ */
1025
+ calculateCyclomaticComplexity(node) {
1026
+ let complexity = 1;
1027
+ const complexityNodes = /* @__PURE__ */ new Set([
1028
+ "if_statement",
1029
+ "for_statement",
1030
+ "while_statement",
1031
+ "do_statement",
1032
+ "case_statement",
1033
+ "conditional_expression"
1034
+ ]);
1035
+ const stack = [node];
1036
+ let guard = 0;
1037
+ while (stack.length > 0 && guard++ < 2e5) {
1038
+ const n = stack.pop();
1039
+ if (complexityNodes.has(n.type)) complexity++;
1040
+ else if (n.type === "binary_expression") {
1041
+ const op = n.childForFieldName("operator");
1042
+ if (op && (op.text === "&&" || op.text === "||")) complexity++;
1043
+ }
1044
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
1045
+ const c = n.namedChild(i);
1046
+ if (c) stack.push(c);
1047
+ }
1048
+ }
1049
+ return complexity;
1050
+ }
1051
+ /**
1052
+ * OC-E helpers: bare-identifier read classification. web-tree-sitter
1053
+ * returns fresh wrappers per accessor call, so node identity is compared
1054
+ * by span (start/end offsets), never by reference.
1055
+ */
1056
+ sameNode(a, b) {
1057
+ if (!a || !b) return false;
1058
+ return a.startIndex === b.startIndex && a.endIndex === b.endIndex;
1059
+ }
1060
+ spanContains(outer, inner) {
1061
+ if (!outer || !inner) return false;
1062
+ return outer.startIndex <= inner.startIndex && outer.endIndex >= inner.endIndex;
1063
+ }
1064
+ /**
1065
+ * True when the identifier is the DECLARED name of an init_declarator
1066
+ * (`int y = ...`, `int arr[10] = ...`, `Counter g = ...`): climb through
1067
+ * name-wrapping declarators (array/pointer/parenthesized); the name is
1068
+ * the declarator subtree under the init_declarator's FIRST named child.
1069
+ * Initializer values live outside that subtree and stay reads.
1070
+ */
1071
+ isDeclaratorName(node) {
1072
+ let cur = node;
1073
+ let p = node.parent ?? null;
1074
+ while (p && DECLARATOR_WRAPPERS.has(p.type) && cur) {
1075
+ cur = p;
1076
+ p = p.parent ?? null;
1077
+ }
1078
+ if (!p || p.type !== "init_declarator" || !cur) return false;
1079
+ const head = p.namedChild(0);
1080
+ return !!head && this.spanContains(head, cur);
1081
+ }
1082
+ /**
1083
+ * OC-E: true when the identifier is the DECLARED NAME of a parameter
1084
+ * (`void (^handler)(NSString *)`, `NSString *name`, `int n`). Structural, not
1085
+ * textual: climb the name-wrapping declarators and require that the node we
1086
+ * land on IS the enclosing parameter's own `declarator` field — a declarator
1087
+ * subtree holds no expressions, so nothing inside it can be a value read, and
1088
+ * OC-E's rule is that "declaration names" are names, never reads.
1089
+ *
1090
+ * The direct `identifier < parameter_declaration` shape was already covered by
1091
+ * the parent-type test. The BLOCK-POINTER parameter is the shape that leaked:
1092
+ * its name nests `identifier < block_pointer_declarator <
1093
+ * parenthesized_declarator < function_declarator < parameter_declaration`, so
1094
+ * neither the parent-type test nor isDeclaratorName (which needs an
1095
+ * init_declarator) could see it, and the declaration line was published as a
1096
+ * `uses` read. Measured before the fix (script/m14/tmp/oc-callee-uses.ts):
1097
+ * `void register_block(void (^handler)(NSString *)) { int x = 0; }` emitted
1098
+ * `uses register_block -> handler` with NO read anywhere in the file — while
1099
+ * the C analyzer, handed the same function-pointer parameter, emits nothing
1100
+ * (family parity), and the M12 fixture read it as a false positive against a
1101
+ * truth that only counts the call.
1102
+ */
1103
+ isParameterDeclaredName(node) {
1104
+ let cur = node;
1105
+ let p = node.parent ?? null;
1106
+ while (p && DECLARATOR_WRAPPERS.has(p.type) && cur) {
1107
+ cur = p;
1108
+ p = p.parent ?? null;
1109
+ }
1110
+ if (!p || !cur) return false;
1111
+ if (p.type !== "parameter_declaration" && p.type !== "method_parameter") return false;
1112
+ const declared = p.childForFieldName("declarator");
1113
+ if (!declared) return p.type === "method_parameter";
1114
+ return this.spanContains(declared, cur);
1115
+ }
1116
+ /**
1117
+ * True when the identifier is a for-in loop variable
1118
+ * (`for (id x in items)`): the named sibling directly preceding the
1119
+ * anonymous `in` token.
1120
+ */
1121
+ isForInVariable(forNode, node) {
1122
+ const cc = forNode.childCount;
1123
+ for (let i = 0; i < cc; i++) {
1124
+ const c = forNode.child(i);
1125
+ if (c && !c.isNamed && c.type === "in" && i > 0) {
1126
+ for (let j = i - 1; j >= 0; j--) {
1127
+ const pc = forNode.child(j);
1128
+ if (pc && pc.isNamed) return this.sameNode(pc, node);
1129
+ }
1130
+ return false;
1131
+ }
1132
+ }
1133
+ return false;
1134
+ }
1135
+ /**
1136
+ * The bound name declared by an init_declarator, method_parameter, or
1137
+ * parameter_declaration: the declarator identifier (through wrappers for
1138
+ * arrays/pointers; first document-order identifier for parameters, where
1139
+ * type names are type_identifier and `id` is a typedefed_specifier).
1140
+ */
1141
+ declaratorName(node) {
1142
+ if (node.type === "init_declarator") {
1143
+ const head = node.namedChild(0);
1144
+ if (!head) return null;
1145
+ const stack2 = [head];
1146
+ while (stack2.length > 0) {
1147
+ const c = stack2.pop();
1148
+ if (c.type === "identifier") return c.text;
1149
+ for (let i = c.namedChildCount - 1; i >= 0; i--) {
1150
+ const ch = c.namedChild(i);
1151
+ if (ch) stack2.push(ch);
1152
+ }
1153
+ }
1154
+ return null;
1155
+ }
1156
+ const direct = this.findChild(node, (n) => n.type === "identifier");
1157
+ if (direct) return direct.text;
1158
+ const stack = [node];
1159
+ while (stack.length > 0) {
1160
+ const c = stack.pop();
1161
+ if (c.type === "identifier") return c.text;
1162
+ for (let i = c.namedChildCount - 1; i >= 0; i--) {
1163
+ const ch = c.namedChild(i);
1164
+ if (ch) stack.push(ch);
1165
+ }
1166
+ }
1167
+ return null;
1168
+ }
1169
+ /**
1170
+ * OC-D: the typedef alias naming an anonymous aggregate
1171
+ * (`typedef struct {...} Size` -> "Size"): the LAST type_identifier
1172
+ * child directly under the enclosing type_definition.
1173
+ */
1174
+ typedefName(node) {
1175
+ const p = node.parent;
1176
+ if (!p || p.type !== "type_definition") return null;
1177
+ let alias = null;
1178
+ for (let i = 0; i < p.namedChildCount; i++) {
1179
+ const c = p.namedChild(i);
1180
+ if (c && (c.type === "type_identifier" || c.type === "identifier")) alias = c;
1181
+ }
1182
+ return alias ? alias.text : null;
1183
+ }
1184
+ // Walk struct_declaration → struct_declarator → (pointer/reference) →
1185
+ // identifier to find the declared property/ivar name.
1186
+ findDeclaredName(node) {
1187
+ const walkForIdentifier = (n) => {
1188
+ if (!n) return null;
1189
+ if (n.type === "identifier") return n;
1190
+ for (let i = 0; i < n.namedChildCount; i++) {
1191
+ const found = walkForIdentifier(n.namedChild(i));
1192
+ if (found) return found;
1193
+ }
1194
+ return null;
1195
+ };
1196
+ const idNode = walkForIdentifier(node);
1197
+ if (!idNode) return null;
1198
+ const structDecl = this.findChild(node, (n) => n.type === "struct_declaration");
1199
+ if (structDecl) {
1200
+ const sd = this.findChild(structDecl, (n) => n.type === "struct_declarator");
1201
+ if (sd) {
1202
+ const deep = walkForIdentifier(sd);
1203
+ if (deep) return deep.text;
1204
+ }
1205
+ }
1206
+ return idNode.text;
1207
+ }
1208
+ /**
1209
+ * OBJCD9: does this declarator subtree reach a `function_declarator`?
1210
+ *
1211
+ * A declaration is a function PROTOTYPE when its declarator chain contains one,
1212
+ * whatever wraps it: `void f(int)` nests the declarator bare, `NSString * f(d)`
1213
+ * nests it under a `pointer_declarator`. The wrapper is a return type, not a
1214
+ * different kind of thing, and the pre-fix arm list read the wrapper as if it
1215
+ * were. Structural — `node.type` and a pre-order walk — with no text test and
1216
+ * no regex.
1217
+ */
1218
+ declaratorChainHasFunctionDeclarator(node) {
1219
+ const stack = [node];
1220
+ let guard = 0;
1221
+ while (stack.length > 0 && guard++ < 1e4) {
1222
+ const c = stack.pop();
1223
+ if (c.type === "function_declarator") return true;
1224
+ if (c.type === "parameter_list") continue;
1225
+ for (let i = c.namedChildCount - 1; i >= 0; i--) {
1226
+ const ch = c.namedChild(i);
1227
+ if (ch) stack.push(ch);
1228
+ }
1229
+ }
1230
+ return false;
1231
+ }
1232
+ /**
1233
+ * OBJCD5: the defining name of a function_definition — the first
1234
+ * function_declarator in document order that is NOT inside the body
1235
+ * (compound_statement), then its first identifier child. Handles the
1236
+ * pointer_declarator > function_declarator nesting of pointer-return C
1237
+ * helpers. The head always precedes any parameter-nested function-pointer
1238
+ * declarator in the tree, so pre-order first-hit is exact (probe-verified
1239
+ * against the pinned tree-sitter-objc, 2026-09).
1240
+ */
1241
+ findFunctionName(node) {
1242
+ const stack = [node];
1243
+ while (stack.length > 0) {
1244
+ const c = stack.pop();
1245
+ if (c.type === "compound_statement") continue;
1246
+ if (c.type === "function_declarator") {
1247
+ const id = this.findChild(c, (n) => n.type === "identifier");
1248
+ return id || c;
1249
+ }
1250
+ for (let i = c.namedChildCount - 1; i >= 0; i--) {
1251
+ const child = c.namedChild(i);
1252
+ if (child) stack.push(child);
1253
+ }
1254
+ }
1255
+ return null;
1256
+ }
1257
+ // The `+`/`-` sign is an anonymous token child of a method node
1258
+ // (`method_declaration [-:"-"] [;:";"]`) — scan the non-named children for
1259
+ // the `+` token. Pure AST, never source-text scanning.
1260
+ isClassMethodToken(node) {
1261
+ const count = node.childCount;
1262
+ for (let i = 0; i < count; i++) {
1263
+ const c = node.child(i);
1264
+ if (c && !c.isNamed && c.type === "+") return true;
1265
+ }
1266
+ return false;
1267
+ }
1268
+ /**
1269
+ * OBJCD7: full ObjC selector of a method_declaration/method_definition.
1270
+ * The pinned grammar nests keyword identifiers at the TOP level of the
1271
+ * method node (parameter locals live INSIDE the method_parameter nodes,
1272
+ * probe-verified 2026-09): `- (void)storeImage:(UIImage*)image
1273
+ * forName:(NSString*)name` -> top-level children are identifier
1274
+ * "storeImage", method_parameter, identifier "forName", method_parameter.
1275
+ * A keyword gets ':' when the NEXT named sibling is a method_parameter.
1276
+ *
1277
+ * A selector is that keyword chain and NOTHING ELSE. The grammar also puts a
1278
+ * method's TRAILING ANNOTATION MACROS at the method node's top level as bare
1279
+ * identifiers with no `method_parameter` after them:
1280
+ *
1281
+ * method_declaration "- (instancetype)initWithBaseURL:(NSURL *)url
1282
+ * sessionConfiguration:(NSURLSessionConfiguration *)c
1283
+ * NS_DESIGNATED_INITIALIZER;"
1284
+ * identifier "initWithBaseURL"
1285
+ * method_parameter ":(NSURL *)url"
1286
+ * identifier "sessionConfiguration"
1287
+ * method_parameter ":(NSURLSessionConfiguration *)c"
1288
+ * identifier "NS_DESIGNATED_INITIALIZER" <- an annotation, not a keyword
1289
+ *
1290
+ * Concatenating every top-level identifier published
1291
+ * `initWithBaseURL:sessionConfiguration:NS_DESIGNATED_INITIALIZER` — a symbol
1292
+ * no class in the world declares, and an `overrides` TARGET
1293
+ * (`AFURLSessionManager.<that name>`) that resolves to nothing. Measured over
1294
+ * the admitted universe by script/m60/objc-selector-integrity.ts before this
1295
+ * fix: 79 method nodes in 29 files (NS_REFINED_FOR_SWIFT 62,
1296
+ * NS_DESIGNATED_INITIALIZER 15, NS_SWIFT_NOTHROW 2) and 109 fabricated
1297
+ * `overrides` targets.
1298
+ *
1299
+ * So the chain STOPS at the first bare identifier: after a keyword it is an
1300
+ * annotation; with no keyword seen it is the zero-argument selector and
1301
+ * anything after it is an annotation too. The alternative reading — a bare
1302
+ * identifier BEFORE the keywords is the selector — is not a guess: the same
1303
+ * probe counted the shapes of all 2,891 method nodes and `bareThenKeyword` is
1304
+ * 0, `bareOnlyMulti` 0, `keywordChain` 1,595, `singleBare` 1,217,
1305
+ * `bareAfterKeyword` 79. The rule is the grammar's, and the census is what
1306
+ * would refute it.
1307
+ */
1308
+ fullMethodSelector(node) {
1309
+ let sel = "";
1310
+ const count = node.namedChildCount;
1311
+ for (let i = 0; i < count; i++) {
1312
+ const c = node.namedChild(i);
1313
+ if (!c) continue;
1314
+ if (c.type === "identifier") {
1315
+ const next = i + 1 < count ? node.namedChild(i + 1) : null;
1316
+ if (next && next.type === "method_parameter") {
1317
+ sel += c.text + ":";
1318
+ continue;
1319
+ }
1320
+ if (sel === "") sel = c.text;
1321
+ break;
1322
+ }
1323
+ }
1324
+ return sel;
1325
+ }
1326
+ /**
1327
+ * OBJCD7: full selector of a message_expression. The grammar flattens
1328
+ * keywords and arguments as SIBLINGS; a keyword is an identifier whose
1329
+ * NEXT child is an anonymous ':' token (the colons are direct anonymous
1330
+ * children of message_expression, probe-verified 2026-09). Zero-argument
1331
+ * messages have no ':' — the selector is then the LAST identifier child
1332
+ * (receiver identifiers live inside nested nodes for complex receivers;
1333
+ * `[foo bar]` -> bar, `[self.imageTable removeAllObjects]` ->
1334
+ * removeAllObjects, `[[Foo alloc] init]` -> init).
1335
+ */
1336
+ /**
1337
+ * The FIRST selector node of a message send — the same selection `fullMessageSelector`
1338
+ * builds its text from, returned as a NODE.
1339
+ *
1340
+ * The M59.A7 contract is that a relationship's `line_number` is the line of the NAME it
1341
+ * names, and a name captured as a string has no line. swift's round paid for this lesson
1342
+ * with a surviving `calls Endpoint -> method` edge that claimed the CONVERTED site's line,
1343
+ * because the text and the line came from two different selections; here they come from one
1344
+ * walk, and the equivalence probe re-checks that the node's own text is a prefix of the
1345
+ * published selector (a keyword send) or equal to it (a zero-argument send).
1346
+ */
1347
+ fullMessageSelectorNode(node) {
1348
+ let firstKeyword = null;
1349
+ let lastIdentifier = null;
1350
+ const count = node.childCount;
1351
+ for (let i = 0; i < count; i++) {
1352
+ const c = node.child(i);
1353
+ if (!c) continue;
1354
+ if (c.isNamed && c.type === "identifier") {
1355
+ const nxt = i + 1 < count ? node.child(i + 1) : null;
1356
+ if (nxt && !nxt.isNamed && nxt.type === ":") {
1357
+ if (!firstKeyword) firstKeyword = c;
1358
+ } else {
1359
+ lastIdentifier = c;
1360
+ }
1361
+ }
1362
+ }
1363
+ return firstKeyword ?? lastIdentifier;
1364
+ }
1365
+ /**
1366
+ * The 1-based line a node is WRITTEN on. The unit of the M59.A7 contract, and deliberately
1367
+ * computed from the node itself: two runs of the same file must produce the same number.
1368
+ */
1369
+ nodeLine(n) {
1370
+ return n.startPosition.row + 1;
1371
+ }
1372
+ /**
1373
+ * OC-ONT-6 — the three lists Objective-C spells with ONE token sequence.
1374
+ *
1375
+ * `@interface Cache <KeyType, ObjectType> : Store <KeyType, ObjectType> <CacheProtocol>`
1376
+ * has three `parameterized_arguments` children, and the pinned grammar names all three
1377
+ * identically: the class's own lightweight-generic TYPE PARAMETERS, its superclass's TYPE
1378
+ * ARGUMENTS, and its ADOPTED PROTOCOLS. Reading every list as an adoption list published
1379
+ * `implements Cache -> KeyType` / `-> ObjectType` — the class's own type parameters as
1380
+ * protocols, names no file declares (objc/generics-and-reason-codes).
1381
+ *
1382
+ * The lists are separated by POSITION, and position is exact here rather than heuristic
1383
+ * because the pinned grammar keeps the `:` and `(` tokens as direct children:
1384
+ *
1385
+ * 1. a list BEFORE the `:` is part of the DECLARATION — the class's own type parameters,
1386
+ * never a protocol;
1387
+ * 2. the FIRST list after the `:` is the superclass's type ARGUMENTS exactly when every
1388
+ * name in it is one of those parameters, and is otherwise the adoption list: the
1389
+ * ordinary `@interface Plain : NSObject <NSCopying>` declares no parameters, so its
1390
+ * single list adopts;
1391
+ * 3. every later list is an adoption list.
1392
+ *
1393
+ * A class EXTENSION re-declares the enclosing class's parameters in front of its `()`
1394
+ * (`@interface Cache <KeyType, ObjectType> ()`), so its lists are all declaration-region
1395
+ * and none adopts; a CATEGORY is the mirror image — `@interface Plain (Trim)
1396
+ * <NSFastEnumeration>` — and its list sits AFTER the `(`, so a category's lists all adopt.
1397
+ * The two positions are mirrors, which is why the rule cannot be "a list before the paren"
1398
+ * alone (objc/generics-and-reason-codes pins both).
1399
+ *
1400
+ * The same partition is re-derived independently in
1401
+ * script/m60/objc-construct-site-probe.ts so the F5 differential compares two derivations
1402
+ * rather than one against itself.
1403
+ */
1404
+ objcInheritanceLists(node) {
1405
+ const membersOf = (pa) => {
1406
+ const out = [];
1407
+ for (let i = 0; i < pa.namedChildCount; i++) {
1408
+ const m = pa.namedChild(i);
1409
+ if (m && (m.type === "type_identifier" || m.type === "identifier" || m.type === "type_name")) {
1410
+ out.push({ name: m.text, node: m });
1411
+ }
1412
+ }
1413
+ return out;
1414
+ };
1415
+ let colonIdx = -1;
1416
+ let parenIdx = -1;
1417
+ const lists = [];
1418
+ for (let ci = 0; ci < node.childCount; ci++) {
1419
+ const c = node.child(ci);
1420
+ if (!c) continue;
1421
+ if (!c.isNamed && c.type === ":" && colonIdx < 0) {
1422
+ colonIdx = ci;
1423
+ continue;
1424
+ }
1425
+ if (!c.isNamed && c.type === "(" && parenIdx < 0) {
1426
+ parenIdx = ci;
1427
+ continue;
1428
+ }
1429
+ if (c.isNamed && c.type === "parameterized_arguments") lists.push({ node: c, idx: ci });
1430
+ }
1431
+ const isCategory = parenIdx >= 0 && (colonIdx < 0 || parenIdx < colonIdx);
1432
+ const isDeclarationRegion = (idx) => colonIdx >= 0 ? idx < colonIdx : isCategory && idx < parenIdx;
1433
+ const typeParams = [];
1434
+ for (const l of lists) {
1435
+ if (!isDeclarationRegion(l.idx)) continue;
1436
+ for (const m of membersOf(l.node)) typeParams.push(m.name);
1437
+ }
1438
+ const paramSet = new Set(typeParams);
1439
+ let superClass = null;
1440
+ let superNode = null;
1441
+ const protocols = [];
1442
+ let argsConsumed = false;
1443
+ for (let ci = 0; ci < node.childCount; ci++) {
1444
+ const c = node.child(ci);
1445
+ if (!c) continue;
1446
+ if (colonIdx >= 0 && ci > colonIdx && superClass === null && c.isNamed && (c.type === "identifier" || c.type === "type_identifier")) {
1447
+ superClass = c.text;
1448
+ superNode = c;
1449
+ continue;
1450
+ }
1451
+ if (!c.isNamed || c.type !== "parameterized_arguments") continue;
1452
+ if (isDeclarationRegion(ci)) continue;
1453
+ const mem = membersOf(c);
1454
+ if (colonIdx >= 0 && !argsConsumed && mem.length > 0 && mem.every((m) => paramSet.has(m.name))) {
1455
+ argsConsumed = true;
1456
+ continue;
1457
+ }
1458
+ for (const m of mem) protocols.push(m);
1459
+ if (colonIdx >= 0) argsConsumed = true;
1460
+ }
1461
+ return { superClass, superNode, typeParams, protocols };
1462
+ }
1463
+ fullMessageSelector(node) {
1464
+ let sel = "";
1465
+ let lastId = null;
1466
+ const count = node.childCount;
1467
+ for (let i = 0; i < count; i++) {
1468
+ const c = node.child(i);
1469
+ if (!c) continue;
1470
+ if (c.isNamed && c.type === "identifier") {
1471
+ const nxt = i + 1 < count ? node.child(i + 1) : null;
1472
+ if (nxt && !nxt.isNamed && nxt.type === ":") {
1473
+ sel += c.text + ":";
1474
+ } else {
1475
+ lastId = c.text;
1476
+ }
1477
+ }
1478
+ }
1479
+ if (sel) return sel;
1480
+ return lastId ?? "";
1481
+ }
1482
+ // Pure cursor helper protecting V8 from GC Thrashing
1483
+ findChild(node, predicate) {
1484
+ const count = node.namedChildCount;
1485
+ for (let i = 0; i < count; i++) {
1486
+ const child = node.namedChild(i);
1487
+ if (child && predicate(child)) return child;
1488
+ }
1489
+ return null;
1490
+ }
1491
+ // Deep pre-order variant of findChild (first document-order match in the
1492
+ // subtree, including the node itself).
1493
+ deepFind(node, predicate) {
1494
+ const stack = [node];
1495
+ while (stack.length > 0) {
1496
+ const c = stack.pop();
1497
+ if (predicate(c)) return c;
1498
+ for (let i = c.namedChildCount - 1; i >= 0; i--) {
1499
+ const ch = c.namedChild(i);
1500
+ if (ch) stack.push(ch);
1501
+ }
1502
+ }
1503
+ return null;
1504
+ }
1505
+ getRange(node) {
1506
+ return {
1507
+ startLine: node.startPosition.row + 1,
1508
+ startColumn: node.startPosition.column + 1,
1509
+ endLine: node.endPosition.row + 1,
1510
+ endColumn: node.endPosition.column + 1,
1511
+ startIndex: node.startIndex,
1512
+ endIndex: node.endIndex
1513
+ };
1514
+ }
1515
+ };
1516
+ export {
1517
+ RealObjectiveCAnalyzer
1518
+ };