@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,3944 @@
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-dart-analyzer.ts
14
+ import * as path from "path";
15
+ import { existsSync, readFileSync, readdirSync } from "fs";
16
+ var DART_CITATION_ROOTS = /* @__PURE__ */ new Set([
17
+ "override",
18
+ "deprecated",
19
+ "Deprecated",
20
+ "pragma",
21
+ "deprecated_member_use",
22
+ "mustCallSuper",
23
+ "protected",
24
+ "required",
25
+ "immutable",
26
+ "factory",
27
+ "optionalTypeArgs",
28
+ "visibleForTesting",
29
+ "visibleForOverriding",
30
+ "doNotStore",
31
+ "doNotSubmit",
32
+ "mustBeOverridden",
33
+ "redeclare",
34
+ "nonVirtual",
35
+ "recordUse",
36
+ "target",
37
+ "Target",
38
+ "Metadata",
39
+ "Object",
40
+ "String",
41
+ "int",
42
+ "double",
43
+ "bool",
44
+ "List",
45
+ "Map",
46
+ "Set",
47
+ "Future",
48
+ "FutureOr",
49
+ "Stream",
50
+ "Function",
51
+ "Iterable",
52
+ "Duration",
53
+ "DateTime",
54
+ "Exception",
55
+ "Error",
56
+ "StackTrace",
57
+ "RegExp",
58
+ "Uri",
59
+ "BigInt",
60
+ "Symbol",
61
+ "Type",
62
+ "Null",
63
+ "Never",
64
+ "void",
65
+ "dynamic",
66
+ "print",
67
+ "identical",
68
+ "identityHashCode"
69
+ ]);
70
+ var DART_CITATION_TYPE_SHAPED_LABELS = /* @__PURE__ */ new Set([
71
+ "calls",
72
+ "uses",
73
+ "instantiates",
74
+ "extends",
75
+ "implements",
76
+ "overrides",
77
+ "mixes_in",
78
+ "part_of",
79
+ // `renders` names a CLASS (the component a `build` method returns), so its target is a
80
+ // type-shaped claim exactly like `extends`/`implements` are.
81
+ "renders"
82
+ ]);
83
+ function stripWhitespace(text) {
84
+ let out = "";
85
+ for (let i = 0; i < text.length; i++) {
86
+ const c = text.charCodeAt(i);
87
+ const ws = c === 32 || c === 9 || c === 10 || c === 13 || c === 11 || c === 12 || c >= 160 && (c === 160 || c === 5760 || c >= 8192 && c <= 8202 || c === 8232 || c === 8233 || c === 8239 || c === 8287 || c === 12288 || c === 65279);
88
+ if (!ws) out += text[i];
89
+ }
90
+ return out;
91
+ }
92
+ function isDottedIdentifier(s) {
93
+ const identStart = (c) => c >= 97 && c <= 122 || c >= 65 && c <= 90 || c === 95 || c === 36;
94
+ const identPart = (c) => c >= 97 && c <= 122 || c >= 65 && c <= 90 || c >= 48 && c <= 57 || c === 95 || c === 36;
95
+ if (s.length === 0 || !identStart(s.charCodeAt(0))) return false;
96
+ let prevDot = false;
97
+ for (let i = 1; i < s.length; i++) {
98
+ const c = s.charCodeAt(i);
99
+ if (c === 46) {
100
+ if (prevDot) return false;
101
+ prevDot = true;
102
+ continue;
103
+ }
104
+ if (!identPart(c)) return false;
105
+ prevDot = false;
106
+ }
107
+ return !prevDot;
108
+ }
109
+ function isPlainIdentifier(s) {
110
+ if (s.length === 0) return false;
111
+ const start = s.charCodeAt(0);
112
+ if (!(start >= 97 && start <= 122 || start >= 65 && start <= 90 || start === 95 || start === 36)) return false;
113
+ for (let i = 1; i < s.length; i++) {
114
+ const c = s.charCodeAt(i);
115
+ if (!(c >= 97 && c <= 122 || c >= 65 && c <= 90 || c >= 48 && c <= 57 || c === 95 || c === 36)) return false;
116
+ }
117
+ return true;
118
+ }
119
+ function stripNullableSuffix(text) {
120
+ let end = text.length;
121
+ while (end > 0 && text.charCodeAt(end - 1) === 63) end--;
122
+ return end === text.length ? text : text.slice(0, end);
123
+ }
124
+ function typeNameWithoutArguments(text) {
125
+ const lt = text.indexOf("<");
126
+ if (lt <= 0) return text;
127
+ if (text.charCodeAt(text.length - 1) !== 62) return text;
128
+ let depth = 0;
129
+ for (let i = lt; i < text.length; i++) {
130
+ const c = text.charCodeAt(i);
131
+ if (c === 60) depth++;
132
+ else if (c === 62) {
133
+ depth--;
134
+ if (depth === 0 && i !== text.length - 1) return text;
135
+ }
136
+ }
137
+ if (depth !== 0) return text;
138
+ const head = text.slice(0, lt);
139
+ return isPlainIdentifier(head) ? head : text;
140
+ }
141
+ function unquote(text) {
142
+ if (text.length >= 2) {
143
+ const first = text[0];
144
+ if ((first === '"' || first === "'") && text[text.length - 1] === first) {
145
+ return text.slice(1, -1);
146
+ }
147
+ }
148
+ return text;
149
+ }
150
+ function stripCommentMarkers(text) {
151
+ if (text.startsWith("///")) return text.slice(3).trimStart();
152
+ if (text.startsWith("//")) return text.slice(2).trimStart();
153
+ if (text.startsWith("/*")) {
154
+ const end = text.endsWith("*/") ? text.length - 2 : text.length;
155
+ return text.slice(2, end).trim();
156
+ }
157
+ return text.trim();
158
+ }
159
+ function isCapitalized(text) {
160
+ if (text.length === 0) return false;
161
+ const code = text.charCodeAt(0);
162
+ return code >= 65 && code <= 90;
163
+ }
164
+ var IMPLICIT_DYNAMIC_TYPE = "dynamic";
165
+ var DART_UNNAMED_EXTENSION = "unnamed_extension";
166
+ var DART_TYPE_MENTION_DOUBLE_COUNTED = /* @__PURE__ */ new Set([
167
+ // heritage — `extends` / `implements` / `with` (extractClassHeritage)
168
+ "superclass",
169
+ "interfaces",
170
+ "mixins",
171
+ // a mixin `on`-base is an `extends` edge (DARTD14), and a mixin-application
172
+ // class publishes its base and its with-list through the same extractor
173
+ "mixin_declaration",
174
+ "mixin_application",
175
+ // construction is `instantiates`
176
+ "new_expression",
177
+ "const_object_expression",
178
+ "constructor_invocation",
179
+ "redirecting_factory_constructor_signature",
180
+ // `class Box<T>` DECLARES a type variable; it does not mention a type
181
+ "type_parameter",
182
+ // parse damage: not a fact about a well-formed program
183
+ "ERROR"
184
+ ]);
185
+ var R7_PLACEHOLDER_NAME = "unknown";
186
+ var DART_OBJECT_TYPE = "Object";
187
+ var DART_OBJECT_INTERFACE_MEMBERS = /* @__PURE__ */ new Set([
188
+ "toString",
189
+ "hashCode",
190
+ "runtimeType",
191
+ "noSuchMethod",
192
+ "operator =="
193
+ ]);
194
+ function scopeOwnerName(scopes) {
195
+ for (let i = scopes.length - 1; i >= 0; i--) {
196
+ if (scopes[i].kind === "class" /* Class */) return scopes[i].name;
197
+ }
198
+ return void 0;
199
+ }
200
+ function scopeString(scopes) {
201
+ const parts = [];
202
+ for (let i = 1; i < scopes.length; i++) {
203
+ const s = scopes[i];
204
+ if (s.kind !== "global" /* Global */) parts.push(s.name);
205
+ }
206
+ return parts.join(".");
207
+ }
208
+ var DARt_COMPANION_CACHE = /* @__PURE__ */ new WeakMap();
209
+ function dartCompanionFor(projectIndex) {
210
+ if (!projectIndex?.typeMembers) return void 0;
211
+ const key = projectIndex;
212
+ const hit = DARt_COMPANION_CACHE.get(key);
213
+ if (hit) return hit;
214
+ const out = /* @__PURE__ */ new Map();
215
+ const closure = (t, seen) => {
216
+ const cached = out.get(t);
217
+ if (cached) return cached;
218
+ if (seen.has(t)) return /* @__PURE__ */ new Set();
219
+ seen.add(t);
220
+ const members = new Set(projectIndex.typeMembers.get(t) ?? []);
221
+ for (const b of projectIndex.typeBases?.get(t) ?? []) for (const m of closure(b, seen)) members.add(m);
222
+ out.set(t, members);
223
+ return members;
224
+ };
225
+ for (const t of projectIndex.typeKinds?.keys() ?? []) closure(t, /* @__PURE__ */ new Set());
226
+ DARt_COMPANION_CACHE.set(key, out);
227
+ return out;
228
+ }
229
+ var DART_PROJECT_ROOT_CACHE = /* @__PURE__ */ new Map();
230
+ var DART_PROJECT_FACTS_CACHE = /* @__PURE__ */ new Map();
231
+ var DART_WALK_SKIP = /* @__PURE__ */ new Set([
232
+ ".git",
233
+ ".hg",
234
+ ".svn",
235
+ "node_modules",
236
+ ".dart_tool",
237
+ ".symlinks",
238
+ "build",
239
+ "coverage",
240
+ ".pub-cache",
241
+ "out"
242
+ ]);
243
+ function dartProjectRootOf(filePath) {
244
+ const start = path.dirname(path.resolve(filePath));
245
+ const seen = [];
246
+ let dir = start;
247
+ for (let i = 0; i < 64; i++) {
248
+ const cached = DART_PROJECT_ROOT_CACHE.get(dir);
249
+ if (cached !== void 0) {
250
+ for (const d of seen) DART_PROJECT_ROOT_CACHE.set(d, cached);
251
+ return cached;
252
+ }
253
+ seen.push(dir);
254
+ if (existsSync(path.join(dir, ".git"))) {
255
+ if (DART_PROJECT_ROOT_CACHE.size > 2e4) DART_PROJECT_ROOT_CACHE.clear();
256
+ for (const d of seen) DART_PROJECT_ROOT_CACHE.set(d, dir);
257
+ return dir;
258
+ }
259
+ const parent = path.dirname(dir);
260
+ if (parent === dir) break;
261
+ dir = parent;
262
+ }
263
+ if (DART_PROJECT_ROOT_CACHE.size > 2e4) DART_PROJECT_ROOT_CACHE.clear();
264
+ for (const d of seen) DART_PROJECT_ROOT_CACHE.set(d, null);
265
+ return null;
266
+ }
267
+ function isDartLibraryName(text) {
268
+ if (text.length === 0) return false;
269
+ const first = text.charCodeAt(0);
270
+ if (first < 97 || first > 122) return false;
271
+ for (let i = 1; i < text.length; i++) {
272
+ const c = text.charCodeAt(i);
273
+ const ok = c >= 97 && c <= 122 || c >= 48 && c <= 57 || c === 95;
274
+ if (!ok) return false;
275
+ }
276
+ return true;
277
+ }
278
+ function dartPubspecName(file) {
279
+ let text;
280
+ try {
281
+ text = readFileSync(file, "utf8");
282
+ } catch {
283
+ return null;
284
+ }
285
+ let lineStart = 0;
286
+ for (let i = 0; i <= text.length; i++) {
287
+ if (i < text.length && text.charCodeAt(i) !== 10) continue;
288
+ const line = text.slice(lineStart, i);
289
+ lineStart = i + 1;
290
+ if (!line.startsWith("name:")) continue;
291
+ let v = 5;
292
+ while (v < line.length && (line.charCodeAt(v) === 32 || line.charCodeAt(v) === 9)) v++;
293
+ let end = v;
294
+ while (end < line.length) {
295
+ const c = line.charCodeAt(end);
296
+ if (c === 32 || c === 9 || c === 13 || c === 35) break;
297
+ end++;
298
+ }
299
+ const value = line.slice(v, end);
300
+ return isDartLibraryName(value) ? value : null;
301
+ }
302
+ return null;
303
+ }
304
+ function dartProjectFacts(projectRoot) {
305
+ const hit = DART_PROJECT_FACTS_CACHE.get(projectRoot);
306
+ if (hit) return hit;
307
+ const facts = {
308
+ packageRoots: /* @__PURE__ */ new Map(),
309
+ sdkLib: existsSync(path.join(projectRoot, "sdk", "lib")),
310
+ dartLibs: /* @__PURE__ */ new Map()
311
+ };
312
+ const stack = [projectRoot];
313
+ while (stack.length > 0) {
314
+ const dir = stack.pop();
315
+ let entries;
316
+ try {
317
+ entries = readdirSync(dir, { withFileTypes: true });
318
+ } catch {
319
+ continue;
320
+ }
321
+ for (const entry of entries) {
322
+ if (entry.isDirectory()) {
323
+ if (DART_WALK_SKIP.has(entry.name)) continue;
324
+ stack.push(path.join(dir, entry.name));
325
+ continue;
326
+ }
327
+ if (entry.name !== "pubspec.yaml" || !entry.isFile()) continue;
328
+ const name = dartPubspecName(path.join(dir, entry.name));
329
+ if (name === null || facts.packageRoots.has(name)) continue;
330
+ const rel = path.relative(projectRoot, dir);
331
+ facts.packageRoots.set(name, rel.split(path.sep).join("/"));
332
+ }
333
+ }
334
+ if (DART_PROJECT_FACTS_CACHE.size >= 64) DART_PROJECT_FACTS_CACHE.clear();
335
+ DART_PROJECT_FACTS_CACHE.set(projectRoot, facts);
336
+ return facts;
337
+ }
338
+ function dartRepoRelPath(projectRoot, absPath) {
339
+ const rel = path.relative(projectRoot, path.resolve(absPath));
340
+ if (rel.length === 0 || path.isAbsolute(rel)) return null;
341
+ if (rel === ".." || rel.startsWith(`..${path.sep}`)) return null;
342
+ return rel.split(path.sep).join("/");
343
+ }
344
+ function dartJoinInside(baseRel, tail) {
345
+ const base = baseRel.length === 0 ? "" : `${baseRel}/`;
346
+ const norm = path.posix.normalize(`${base}${tail}`);
347
+ if (norm.length === 0 || norm === "." || norm === ".." || norm.startsWith("../")) return null;
348
+ return norm;
349
+ }
350
+ function dartImportTargetPath(projectRoot, filePath, uri) {
351
+ if (uri.length === 0) return null;
352
+ if (uri.startsWith("package:")) {
353
+ const rest = uri.slice(8);
354
+ const slash = rest.indexOf("/");
355
+ if (slash <= 0 || slash === rest.length - 1) return null;
356
+ const pkg = rest.slice(0, slash);
357
+ const sub = rest.slice(slash + 1);
358
+ if (!isDartLibraryName(pkg)) return null;
359
+ const packageRoot = dartProjectFacts(projectRoot).packageRoots.get(pkg);
360
+ if (packageRoot === void 0) return null;
361
+ return dartJoinInside(packageRoot, `lib/${sub}`);
362
+ }
363
+ if (uri.startsWith("dart:")) {
364
+ const lib = uri.slice(5);
365
+ if (!isDartLibraryName(lib)) return null;
366
+ const facts = dartProjectFacts(projectRoot);
367
+ if (!facts.sdkLib) return null;
368
+ const cached = facts.dartLibs.get(lib);
369
+ if (cached !== void 0) return cached;
370
+ const rel = `sdk/lib/${lib}/${lib}.dart`;
371
+ const found = existsSync(path.join(projectRoot, rel)) ? rel : null;
372
+ facts.dartLibs.set(lib, found);
373
+ return found;
374
+ }
375
+ if (uri.indexOf(":") >= 0) return null;
376
+ const abs = path.resolve(path.dirname(path.resolve(filePath)), uri);
377
+ return dartRepoRelPath(projectRoot, abs);
378
+ }
379
+ function isCapitalisedIdentifier(name) {
380
+ if (name.length === 0) return false;
381
+ const first = name.charCodeAt(0);
382
+ if (first < 65 || first > 90) return false;
383
+ for (let i = 1; i < name.length; i++) {
384
+ const c = name.charCodeAt(i);
385
+ if (c >= 65 && c <= 90) continue;
386
+ if (c >= 97 && c <= 122) continue;
387
+ if (c >= 48 && c <= 57) continue;
388
+ if (c === 95 || c === 36) continue;
389
+ return false;
390
+ }
391
+ return true;
392
+ }
393
+ var RealDartAnalyzer = class _RealDartAnalyzer {
394
+ analyze(rootNode, content, filePath, context) {
395
+ this.scopeDeclIndex.clear();
396
+ this.methodComplexityCache.clear();
397
+ const symbols = [];
398
+ const relationships = [];
399
+ const imports = [];
400
+ const exports = [];
401
+ const errors = [];
402
+ const frameworks = { Dart: true };
403
+ const patterns = {};
404
+ const scopes = [{ name: "__global__", kind: "global" /* Global */ }];
405
+ const library = this.libraryName(rootNode, filePath);
406
+ const moduleName = library.name;
407
+ const projectRoot = dartProjectRootOf(filePath);
408
+ const librarySite = {
409
+ moduleName,
410
+ filePath,
411
+ projectRoot,
412
+ moduleIdentity: projectRoot === null ? null : dartRepoRelPath(projectRoot, filePath)
413
+ };
414
+ symbols.push({
415
+ name: moduleName,
416
+ kind: ASTEngine.SymbolKind.Module,
417
+ // THE LIBRARY'S URI. Strategy 1 of the shared resolver matches `qualified_name` exactly,
418
+ // so publishing the path here is what lets an import edge name this file. Unset when the
419
+ // file is not inside a checkout: then the engine's own synthesis (`<scope>.<name>`) is
420
+ // what it always was, and the edges keep their specifier targets (see LIBRARY IDENTITY).
421
+ ...librarySite.moduleIdentity === null ? {} : { qualifiedName: librarySite.moduleIdentity },
422
+ range: library.directive ? this.nodeToRange(library.directive) : { startLine: 1, startColumn: 1, endLine: 1, endColumn: 1, startIndex: 0, endIndex: 0 },
423
+ visibility: "public",
424
+ scope: moduleName,
425
+ metadata: { nodeType: library.directive ? library.directive.type : "implicit_library" }
426
+ });
427
+ if (rootNode.hasError) {
428
+ const errStack = [rootNode];
429
+ while (errStack.length > 0) {
430
+ const n = errStack.pop();
431
+ if (n.type === "ERROR" || n.isMissing) {
432
+ errors.push({
433
+ message: `Syntax ${n.type} detected. The Brain Engine is bypassing this broken node.`,
434
+ range: this.nodeToRange(n),
435
+ severity: "warning"
436
+ });
437
+ }
438
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
439
+ const child = n.namedChild(i);
440
+ if (child) errStack.push(child);
441
+ }
442
+ }
443
+ }
444
+ const stack = [{ node: rootNode, scopes, functionDepth: 0 }];
445
+ let cyclomaticComplexity = 1;
446
+ const seenMembers = /* @__PURE__ */ new Map();
447
+ const nameLines = /* @__PURE__ */ new Map();
448
+ while (stack.length > 0) {
449
+ const { node, scopes: frameScopes, functionDepth } = stack.pop();
450
+ const scopeStack = [...frameScopes];
451
+ let nextFunctionDepth = functionDepth;
452
+ const t = node.type;
453
+ if (t === "if_statement" || t === "for_statement" || t === "while_statement" || t === "do_statement" || t === "switch_statement_case" || t === "switch_expression_case" || t === "catch_clause" || t === "conditional_expression" || t === "logical_and_expression" || t === "logical_or_expression") {
454
+ cyclomaticComplexity++;
455
+ }
456
+ switch (t) {
457
+ case "class_definition": {
458
+ const name = node.childForFieldName("name")?.text || (this.findChildByType(node, "mixin_application_class") ? this.findDescendantByType(node, "identifier")?.text : void 0);
459
+ if (name) {
460
+ const comments = this.collectComments(node);
461
+ const isSealed = this.hasModifier(node, "sealed");
462
+ const isBase = this.hasModifier(node, "base");
463
+ const isInterfaceClass = this.hasModifier(node, "interface");
464
+ const isFinalClass = this.hasModifier(node, "final");
465
+ const isAbstract = this.hasModifier(node, "abstract") || isSealed;
466
+ symbols.push({
467
+ name,
468
+ kind: ASTEngine.SymbolKind.Class,
469
+ range: this.nodeToRange(node),
470
+ scope: scopeString(scopeStack),
471
+ visibility: name.startsWith("_") ? "private" : "public",
472
+ comments,
473
+ isAbstract,
474
+ genericParameters: this.extractTypeParameters(node),
475
+ metadata: { isClass: true, isAbstract, isSealed, isBase, isInterfaceClass, isFinalClass }
476
+ });
477
+ scopeStack.push({ name, kind: "class" /* Class */ });
478
+ this.extractClassHeritage(node, name, relationships, patterns);
479
+ this.extractRenderRelationships(node, name, relationships);
480
+ }
481
+ break;
482
+ }
483
+ case "mixin_declaration": {
484
+ const name = node.childForFieldName("name")?.text || this.findChildByType(node, "identifier")?.text;
485
+ if (name) {
486
+ const comments = this.collectComments(node);
487
+ patterns["Mixins"] = true;
488
+ symbols.push({
489
+ name,
490
+ kind: ASTEngine.SymbolKind.Mixin,
491
+ range: this.nodeToRange(node),
492
+ scope: scopeString(scopeStack),
493
+ visibility: name.startsWith("_") ? "private" : "public",
494
+ comments,
495
+ metadata: { isMixin: true }
496
+ });
497
+ scopeStack.push({ name, kind: "class" /* Class */ });
498
+ this.extractClassHeritage(node, name, relationships, patterns);
499
+ }
500
+ break;
501
+ }
502
+ case "extension_declaration": {
503
+ const targetType = this.extensionTargetType(node);
504
+ const name = this.extensionPublishedName(node);
505
+ if (name) {
506
+ const comments = this.collectComments(node);
507
+ patterns["Extensions"] = true;
508
+ symbols.push({
509
+ name,
510
+ kind: ASTEngine.SymbolKind.Extension,
511
+ range: this.nodeToRange(node),
512
+ scope: scopeString(scopeStack),
513
+ visibility: name.startsWith("_") ? "private" : "public",
514
+ comments,
515
+ metadata: { isExtension: true, targetType, isUnnamedExtension: !node.childForFieldName("name") }
516
+ });
517
+ scopeStack.push({ name, kind: "class" /* Class */ });
518
+ }
519
+ break;
520
+ }
521
+ case "extension_type_declaration": {
522
+ const name = node.childForFieldName("name")?.text;
523
+ if (name) {
524
+ const comments = this.collectComments(node);
525
+ const rep = this.findChildByType(node, "representation_declaration");
526
+ const repType = this.representationTypeName(rep);
527
+ patterns["Extensions"] = true;
528
+ symbols.push({
529
+ name,
530
+ kind: ASTEngine.SymbolKind.Extension,
531
+ range: this.nodeToRange(node),
532
+ scope: scopeString(scopeStack),
533
+ visibility: name.startsWith("_") ? "private" : "public",
534
+ comments,
535
+ metadata: { isExtension: true, isExtensionType: true, representationType: repType }
536
+ });
537
+ scopeStack.push({ name, kind: "class" /* Class */ });
538
+ const repName = rep?.childForFieldName("name")?.text;
539
+ if (rep && repName && repName !== "_") {
540
+ const repRange = this.nodeToRange(rep);
541
+ const repNameNode = rep.childForFieldName("name");
542
+ if (repNameNode) nameLines.set(dartNameLineKey(repName, repRange), this.nodeLine(repNameNode));
543
+ symbols.push({
544
+ name: repName,
545
+ kind: ASTEngine.SymbolKind.Property,
546
+ range: repRange,
547
+ scope: name,
548
+ ownerName: name,
549
+ ownerKind: ASTEngine.SymbolKind.Extension,
550
+ visibility: repName.startsWith("_") ? "private" : "public",
551
+ metadata: { isRepresentationField: true, dataType: repType }
552
+ });
553
+ }
554
+ this.extractClassHeritage(node, name, relationships, patterns);
555
+ }
556
+ break;
557
+ }
558
+ case "enum_declaration": {
559
+ const name = node.childForFieldName("name")?.text;
560
+ if (name) {
561
+ const comments = this.collectComments(node);
562
+ symbols.push({
563
+ name,
564
+ kind: ASTEngine.SymbolKind.Enum,
565
+ range: this.nodeToRange(node),
566
+ scope: scopeString(scopeStack),
567
+ visibility: name.startsWith("_") ? "private" : "public",
568
+ comments,
569
+ metadata: { isEnum: true }
570
+ });
571
+ const body = this.findChildByType(node, "enum_body");
572
+ if (body) {
573
+ for (let i = 0; i < body.namedChildCount; i++) {
574
+ const c = body.namedChild(i);
575
+ if (c && c.type === "enum_constant") {
576
+ const mname = c.childForFieldName("name")?.text;
577
+ if (mname) {
578
+ symbols.push({
579
+ name: mname,
580
+ kind: ASTEngine.SymbolKind.EnumMember,
581
+ range: this.nodeToRange(c),
582
+ scope: name,
583
+ ownerName: name,
584
+ ownerKind: ASTEngine.SymbolKind.Enum,
585
+ visibility: mname.startsWith("_") ? "private" : "public",
586
+ metadata: { isEnumMember: true }
587
+ });
588
+ }
589
+ }
590
+ }
591
+ }
592
+ this.extractClassHeritage(node, name, relationships, patterns);
593
+ scopeStack.push({ name, kind: "class" /* Class */ });
594
+ }
595
+ break;
596
+ }
597
+ case "type_alias": {
598
+ const nameNode = node.childForFieldName("name") || this.findChildByType(node, "type_identifier");
599
+ const name = nameNode?.text;
600
+ if (name && name !== "Function") {
601
+ const comments = this.collectComments(node);
602
+ symbols.push({
603
+ name,
604
+ kind: ASTEngine.SymbolKind.TypeAlias,
605
+ range: this.nodeToRange(node),
606
+ scope: scopeString(scopeStack),
607
+ visibility: name.startsWith("_") ? "private" : "public",
608
+ comments,
609
+ metadata: { isTypeAlias: true }
610
+ });
611
+ }
612
+ break;
613
+ }
614
+ case "declaration": {
615
+ const sig = this.findSignatureIn(node);
616
+ if (sig) {
617
+ if (this.isConstructorSignature(sig.type)) {
618
+ const owner = scopeOwnerName(scopeStack);
619
+ if (owner && this.isRealConstructor(sig, owner)) {
620
+ this.processConstructor(node, sig, owner, symbols, relationships, patterns);
621
+ } else if (owner) {
622
+ this.processFunctionOrMethod(node, sig, symbols, scopeStack, patterns, seenMembers, nameLines);
623
+ }
624
+ } else {
625
+ this.processFunctionOrMethod(node, sig, symbols, scopeStack, patterns, seenMembers, nameLines);
626
+ }
627
+ } else {
628
+ this.processFieldOrVariable(node, symbols, scopeStack, nameLines);
629
+ }
630
+ break;
631
+ }
632
+ case "method_signature":
633
+ case "function_signature":
634
+ case "getter_signature":
635
+ case "setter_signature":
636
+ case "function_declaration": {
637
+ if (node.parent?.type === "declaration") break;
638
+ if (node.parent?.type === "method_signature") break;
639
+ if (t === "function_signature" && node.parent?.type === "lambda_expression") break;
640
+ const owner = scopeOwnerName(scopeStack);
641
+ const inner = this.findInnerSignature(node);
642
+ if (owner && this.isConstructorSignature(inner.type) && this.isRealConstructor(inner, owner)) {
643
+ this.processConstructor(node, inner, owner, symbols, relationships, patterns);
644
+ } else if (owner || t === "function_declaration") {
645
+ this.processFunctionOrMethod(node, node, symbols, scopeStack, patterns, seenMembers, nameLines);
646
+ } else if (t === "function_signature" || t === "getter_signature" || t === "setter_signature") {
647
+ this.processTopLevelFunction(node, symbols, scopeStack, patterns, nameLines, seenMembers);
648
+ }
649
+ break;
650
+ }
651
+ case "constructor_signature":
652
+ case "factory_constructor_signature":
653
+ case "constant_constructor_signature":
654
+ case "redirecting_factory_constructor_signature": {
655
+ const ptype = node.parent?.type;
656
+ if (ptype === "declaration" || ptype === "method_signature") break;
657
+ const owner = scopeOwnerName(scopeStack);
658
+ if (owner && this.isRealConstructor(node, owner)) {
659
+ this.processConstructor(node, node, owner, symbols, relationships, patterns);
660
+ } else if (owner) {
661
+ this.processFunctionOrMethod(node, node, symbols, scopeStack, patterns, seenMembers, nameLines);
662
+ }
663
+ break;
664
+ }
665
+ case "initialized_identifier_list":
666
+ case "static_final_declaration_list": {
667
+ const parent = node.parent;
668
+ if (parent && parent.type === "program") {
669
+ this.processTopLevelVariables(node, symbols, scopeStack, nameLines);
670
+ }
671
+ break;
672
+ }
673
+ case "import_specification":
674
+ case "library_export":
675
+ case "part_directive":
676
+ case "part_of_directive": {
677
+ this.processImportExport(node, imports, exports, frameworks, relationships, librarySite);
678
+ break;
679
+ }
680
+ }
681
+ for (let i = node.namedChildCount - 1; i >= 0; i--) {
682
+ const child = node.namedChild(i);
683
+ if (child) {
684
+ stack.push({ node: child, scopes: scopeStack, functionDepth: nextFunctionDepth });
685
+ }
686
+ }
687
+ }
688
+ this.addContainmentRelationships(symbols, relationships, nameLines);
689
+ this.extractOverrideRelationships(symbols, relationships, context?.overrideCompanion, nameLines, context?.projectIndex);
690
+ this.extractExpressionRelationships(rootNode, relationships, moduleName, symbols, context?.projectIndex);
691
+ {
692
+ const seenRel = /* @__PURE__ */ new Set();
693
+ const uniq = [];
694
+ for (const r of relationships) {
695
+ if (r.source === R7_PLACEHOLDER_NAME || r.target === R7_PLACEHOLDER_NAME || r.from === R7_PLACEHOLDER_NAME || r.to === R7_PLACEHOLDER_NAME) continue;
696
+ const key = `${r.type}|${r.source}|${r.target}|${r.confidence}`;
697
+ const scope = r.metadata?.localBindingScope;
698
+ const dedupKey = scope !== void 0 && scope !== null ? `${key}|${scope}` : key;
699
+ if (seenRel.has(dedupKey)) continue;
700
+ seenRel.add(dedupKey);
701
+ uniq.push(r);
702
+ }
703
+ relationships.splice(0, relationships.length, ...uniq);
704
+ }
705
+ this.classifyEvidenceTier(relationships);
706
+ {
707
+ const fieldTypeByClass = /* @__PURE__ */ new Map();
708
+ for (const s of symbols) {
709
+ if ((s.metadata ?? {}).isField && s.ownerName) {
710
+ const dt = s.metadata.dataType;
711
+ if (dt) fieldTypeByClass.set(`${s.ownerName}.${s.name}`, dt);
712
+ }
713
+ }
714
+ for (const s of symbols) {
715
+ if (s.kind === ASTEngine.SymbolKind.Constructor && s.ownerName) {
716
+ for (const p of s.parameters ?? []) {
717
+ if (p.type === IMPLICIT_DYNAMIC_TYPE) {
718
+ const ft = fieldTypeByClass.get(`${s.ownerName}.${p.name}`);
719
+ if (ft) p.type = ft;
720
+ }
721
+ }
722
+ }
723
+ }
724
+ }
725
+ const architecturalScore = Math.min(100, Math.max(0, 100 - errors.length * 5));
726
+ const detectedPatterns = Object.keys(patterns);
727
+ const detectedFrameworks = Object.keys(frameworks);
728
+ const frameworkPatterns = detectedPatterns.map((p) => ({
729
+ framework: "Dart",
730
+ patterns: [{ type: "usage", detected: true, description: p }]
731
+ }));
732
+ if (process.env.BB_DART_CITATION === "1") {
733
+ for (const r of relationships) {
734
+ const t = String(r.target ?? "");
735
+ if (!DART_CITATION_ROOTS.has(t)) continue;
736
+ const md = r.metadata ?? {};
737
+ const isAnnotation = md.isAnnotation === true;
738
+ const label = String(r.type);
739
+ const typeShaped = DART_CITATION_TYPE_SHAPED_LABELS.has(label) && isCapitalisedIdentifier(t);
740
+ if (!isAnnotation && !typeShaped) continue;
741
+ md.languageNamespace = [t];
742
+ md.languageNamespacePolicy = "strict-absent";
743
+ r.metadata = md;
744
+ }
745
+ }
746
+ for (let i = relationships.length - 1; i >= 0; i--) {
747
+ const r = relationships[i];
748
+ const target = String(r.target ?? "");
749
+ const source = String(r.source ?? "");
750
+ if (target.length === 0 || source.length === 0) relationships.splice(i, 1);
751
+ }
752
+ return {
753
+ language: "dart",
754
+ symbols,
755
+ relationships,
756
+ imports,
757
+ exports,
758
+ errors,
759
+ metadata: {
760
+ complexity: symbols.length + relationships.length,
761
+ linesOfCode: content.split("\n").length,
762
+ cyclomaticComplexity,
763
+ framework: detectedFrameworks.length > 1 ? detectedFrameworks[1] : detectedFrameworks[0],
764
+ patterns: detectedPatterns,
765
+ detectedFrameworks,
766
+ frameworkPatterns,
767
+ architecturalInsights: [],
768
+ architecturalScore,
769
+ codeQuality: errors.length === 0 ? "Excellent" : "Fair",
770
+ hasTypeInference: true,
771
+ hasFrameworkIntelligence: true,
772
+ analysisVersion: "4.0.0-m12.11-family-contract"
773
+ }
774
+ };
775
+ }
776
+ // ============================================================================
777
+ // SYMBOL PROCESSORS
778
+ // ============================================================================
779
+ processConstructor(node, sig, owner, symbols, relationships, patterns) {
780
+ const isFactory = sig.type === "factory_constructor_signature" || sig.type === "redirecting_factory_constructor_signature";
781
+ const isConst = sig.type === "constant_constructor_signature";
782
+ if (isFactory) patterns["Factory"] = true;
783
+ let isNamed = false;
784
+ let namedSegment;
785
+ const directIds = this.directIdentifiers(sig);
786
+ const named = directIds.find((id) => id !== owner);
787
+ if (named) {
788
+ isNamed = true;
789
+ namedSegment = named;
790
+ }
791
+ const params = this.extractParameters(sig);
792
+ const comments = this.collectComments(node);
793
+ const annotations = this.extractAnnotations(node);
794
+ symbols.push({
795
+ name: "constructor",
796
+ kind: ASTEngine.SymbolKind.Constructor,
797
+ range: this.nodeToRange(node),
798
+ scope: owner,
799
+ ownerName: owner,
800
+ ownerKind: ASTEngine.SymbolKind.Class,
801
+ visibility: "public",
802
+ parameters: params,
803
+ comments,
804
+ decorators: annotations,
805
+ metadata: { isFactory, isConst, isNamed, namedSegment, isConstructor: true }
806
+ });
807
+ if (isFactory && sig.type === "redirecting_factory_constructor_signature") {
808
+ const parts = [];
809
+ const partNodes = [];
810
+ let pastEquals = false;
811
+ for (let i = 0; i < sig.childCount; i++) {
812
+ const c = sig.child(i);
813
+ if (!c) continue;
814
+ if (!pastEquals) {
815
+ if (!c.isNamed && c.type === "=") pastEquals = true;
816
+ continue;
817
+ }
818
+ if (c.isNamed && (c.type === "type_identifier" || c.type === "identifier")) {
819
+ parts.push(c.text);
820
+ partNodes.push(c);
821
+ }
822
+ }
823
+ const redirectTarget = parts.length <= 1 ? parts[0] : parts[parts.length - 2];
824
+ const redirectNode = partNodes.length <= 1 ? partNodes[0] : partNodes[partNodes.length - 2];
825
+ if (redirectTarget) {
826
+ relationships.push({
827
+ source: owner,
828
+ target: redirectTarget,
829
+ type: ASTEngine.RelationshipType.Instantiates,
830
+ confidence: 0.95,
831
+ line_number: this.nodeLine(redirectNode),
832
+ metadata: { isFactoryRedirection: true }
833
+ });
834
+ }
835
+ }
836
+ }
837
+ processFunctionOrMethod(node, sig, symbols, scopeStack, patterns, seenMembers, nameLines) {
838
+ const inner = this.findInnerSignature(sig);
839
+ const nameNode = this.signatureNameNode(inner);
840
+ const name = this.signatureName(inner);
841
+ if (!name) return;
842
+ const owner = scopeOwnerName(scopeStack);
843
+ const isGetter0 = inner.type === "getter_signature";
844
+ const isSetter0 = inner.type === "setter_signature";
845
+ const dedupKey = `${owner ?? "module"}:${name}:${isGetter0 ? "get" : isSetter0 ? "set" : "fn"}`;
846
+ const isGetter = isGetter0;
847
+ const isSetter = isSetter0;
848
+ if (isGetter || isSetter) patterns["Getters and Setters"] = true;
849
+ const params = this.extractParameters(inner);
850
+ const returnType = this.extractReturnType(inner);
851
+ const isAsync = this.isAsyncSignature(node, inner);
852
+ const isStatic = this.hasModifier(node, "static") || this.hasModifier(inner, "static");
853
+ const isAbstract = !this.hasBody(node) && (this.hasModifier(node, "abstract") || owner !== void 0);
854
+ const comments = this.collectComments(node);
855
+ const annotations = this.extractAnnotations(node);
856
+ if (isAsync || returnType && returnType.startsWith("Future")) patterns["Async Programming"] = true;
857
+ const kind = isGetter || isSetter ? ASTEngine.SymbolKind.Property : owner ? ASTEngine.SymbolKind.Method : ASTEngine.SymbolKind.Function;
858
+ const symbol = {
859
+ name,
860
+ kind,
861
+ range: this.rangeIncludingBody(node, inner),
862
+ scope: scopeString(scopeStack),
863
+ ownerName: owner,
864
+ ownerKind: owner ? scopeOwnerKind(scopeStack) : void 0,
865
+ visibility: name.startsWith("_") ? "private" : "public",
866
+ parameters: params,
867
+ returnType,
868
+ comments,
869
+ decorators: annotations,
870
+ isStatic,
871
+ isAsync,
872
+ isAbstract,
873
+ complexity: this.methodComplexity(node),
874
+ metadata: { isGetter, isSetter, isStatic, isAsync, isAbstract, hasBody: this.hasBody(node) }
875
+ };
876
+ if (!this.registerCallable(symbols, seenMembers, dedupKey, symbol, nameLines, nameNode)) return;
877
+ scopeStack.push({ name, kind: "function" /* Function */ });
878
+ }
879
+ /**
880
+ * One symbol per (scope,name,kind), LAST declaration wins (DARTD20 — the
881
+ * family dup-key ruling; #753). Both producers of callable symbols use this:
882
+ * a duplicate is a compile error in Dart, so the analyzer's answer for the
883
+ * shape is a deterministic contract, and the family's (settled by python
884
+ * #748) is that the LAST declaration owns the name. Returns false when the
885
+ * symbol REPLACED an earlier one, so the caller can skip its scope push.
886
+ */
887
+ registerCallable(symbols, seenMembers, dedupKey, symbol, nameLines, nameNode) {
888
+ if (nameNode) nameLines.set(dartNameLineKey(symbol.name, symbol.range), this.nodeLine(nameNode));
889
+ const priorIdx = seenMembers.get(dedupKey);
890
+ if (priorIdx !== void 0) {
891
+ symbols[priorIdx] = symbol;
892
+ return false;
893
+ }
894
+ seenMembers.set(dedupKey, symbols.length);
895
+ symbols.push(symbol);
896
+ return true;
897
+ }
898
+ processTopLevelFunction(node, symbols, scopeStack, patterns, nameLines, seenMembers) {
899
+ const nameNode = node.childForFieldName("name") || this.findChildByType(node, "identifier");
900
+ if (!nameNode) return;
901
+ const name = nameNode.text;
902
+ const params = this.extractParameters(node);
903
+ const returnType = this.extractReturnType(node);
904
+ const isAsync = this.isAsyncSignature(node, node);
905
+ if (isAsync || returnType && returnType.startsWith("Future")) patterns["Async Programming"] = true;
906
+ const comments = this.collectComments(node);
907
+ const isAccessor = node.type === "getter_signature" || node.type === "setter_signature";
908
+ const range = this.rangeIncludingBody(node, node);
909
+ const symbol = {
910
+ name,
911
+ kind: isAccessor ? ASTEngine.SymbolKind.Property : ASTEngine.SymbolKind.Function,
912
+ range,
913
+ scope: scopeString(scopeStack),
914
+ visibility: name.startsWith("_") ? "private" : "public",
915
+ parameters: params,
916
+ returnType,
917
+ comments,
918
+ isAsync,
919
+ complexity: this.methodComplexity(node),
920
+ metadata: {
921
+ isAsync,
922
+ isGetter: node.type === "getter_signature",
923
+ isSetter: node.type === "setter_signature",
924
+ isTopLevelFunction: !isAccessor,
925
+ isTopLevelAccessor: isAccessor
926
+ }
927
+ };
928
+ const accessorKind = node.type === "getter_signature" ? "get" : node.type === "setter_signature" ? "set" : "fn";
929
+ if (!this.registerCallable(symbols, seenMembers, `module:${name}:${accessorKind}`, symbol, nameLines, nameNode)) return;
930
+ scopeStack.push({ name, kind: "function" /* Function */ });
931
+ }
932
+ processFieldOrVariable(node, symbols, scopeStack, nameLines) {
933
+ const declList = this.findChildByType(node, "initialized_identifier_list") || this.findChildByType(node, "static_final_declaration_list") || (node.type === "initialized_identifier_list" || node.type === "static_final_declaration_list" ? node : void 0);
934
+ if (!declList) return;
935
+ const owner = scopeOwnerName(scopeStack);
936
+ const isStatic = this.hasModifier(node, "static") || node.type === "static_final_declaration_list";
937
+ const isFinal = this.hasModifier(node, "final") || node.type === "static_final_declaration_list";
938
+ const isConst = this.hasModifier(node, "const");
939
+ const isLate = this.hasModifier(node, "late");
940
+ const dataType = this.extractFieldType(node, declList);
941
+ const declRange = this.nodeToRange(node);
942
+ for (let i = 0; i < declList.namedChildCount; i++) {
943
+ const child = declList.namedChild(i);
944
+ if (!child) continue;
945
+ let name;
946
+ let nameNode = null;
947
+ if (child.type === "initialized_identifier" || child.type === "static_final_declaration") {
948
+ nameNode = this.findChildByType(child, "identifier");
949
+ name = nameNode?.text;
950
+ } else if (child.type === "identifier") {
951
+ name = child.text;
952
+ nameNode = child;
953
+ }
954
+ if (!name) continue;
955
+ nameLines.set(dartNameLineKey(name, declRange), this.nodeLine(nameNode ?? child));
956
+ const kind = owner ? ASTEngine.SymbolKind.Property : isConst || isFinal ? ASTEngine.SymbolKind.Constant : ASTEngine.SymbolKind.Variable;
957
+ symbols.push({
958
+ name,
959
+ kind,
960
+ range: declRange,
961
+ scope: scopeString(scopeStack),
962
+ ownerName: owner,
963
+ ownerKind: owner ? scopeOwnerKind(scopeStack) : void 0,
964
+ visibility: name.startsWith("_") ? "private" : "public",
965
+ isStatic,
966
+ metadata: { isStatic, isFinal, isConst, isLate, dataType, isField: owner !== void 0 }
967
+ });
968
+ }
969
+ }
970
+ processTopLevelVariables(node, symbols, scopeStack, nameLines) {
971
+ const isFinal = this.hasModifier(node, "final") || this.hasModifier(node.parent, "final");
972
+ const isConst = this.hasModifier(node, "const") || this.hasModifier(node.parent, "const");
973
+ const dataType = this.extractFieldType(node, node);
974
+ const declRange = this.nodeToRange(node);
975
+ for (let i = 0; i < node.namedChildCount; i++) {
976
+ const child = node.namedChild(i);
977
+ if (!child) continue;
978
+ let name;
979
+ let nameNode = null;
980
+ if (child.type === "initialized_identifier" || child.type === "static_final_declaration") {
981
+ nameNode = this.findChildByType(child, "identifier");
982
+ name = nameNode?.text;
983
+ } else if (child.type === "identifier") {
984
+ name = child.text;
985
+ nameNode = child;
986
+ }
987
+ if (!name) continue;
988
+ nameLines.set(dartNameLineKey(name, declRange), this.nodeLine(nameNode ?? child));
989
+ symbols.push({
990
+ name,
991
+ kind: isConst || isFinal ? ASTEngine.SymbolKind.Constant : ASTEngine.SymbolKind.Variable,
992
+ range: declRange,
993
+ scope: scopeString(scopeStack),
994
+ visibility: name.startsWith("_") ? "private" : "public",
995
+ metadata: { isStatic: false, isFinal, isConst, dataType, isField: false, isTopLevelVariable: true }
996
+ });
997
+ }
998
+ }
999
+ processImportExport(node, imports, exports, frameworks, relationships, site) {
1000
+ const targetOf = (uri2) => site.projectRoot === null ? null : dartImportTargetPath(site.projectRoot, site.filePath, uri2);
1001
+ const outsideProject = (uri2) => {
1002
+ if (site.projectRoot === null) return false;
1003
+ if (uri2.startsWith("package:")) {
1004
+ const rest = uri2.slice(8);
1005
+ const slash = rest.indexOf("/");
1006
+ if (slash <= 0) return false;
1007
+ const pkg = rest.slice(0, slash);
1008
+ if (!isDartLibraryName(pkg)) return false;
1009
+ return dartProjectFacts(site.projectRoot).packageRoots.get(pkg) === void 0;
1010
+ }
1011
+ if (uri2.startsWith("dart:")) {
1012
+ const lib = uri2.slice(5);
1013
+ if (!isDartLibraryName(lib)) return false;
1014
+ return !dartProjectFacts(site.projectRoot).sdkLib;
1015
+ }
1016
+ return uri2.indexOf(":") >= 0;
1017
+ };
1018
+ const claimMeta = (uri2, resolved) => resolved === null && outsideProject(uri2) ? { importOutsideProject: true } : {};
1019
+ if (node.type === "part_directive" || node.type === "part_of_directive") {
1020
+ const uriNode2 = this.findDescendantByType(node, "string_literal") || this.findDescendantByType(node, "uri");
1021
+ const uri2 = uriNode2 ? unquote(uriNode2.text) : "";
1022
+ if (!uri2 && node.type === "part_of_directive") {
1023
+ const named = this.findChildByType(node, "dotted_identifier_list");
1024
+ if (named) {
1025
+ relationships.push({
1026
+ source: site.moduleName,
1027
+ target: stripWhitespace(named.text),
1028
+ type: ASTEngine.RelationshipType.PartOf,
1029
+ confidence: 1,
1030
+ line_number: this.nodeLine(named),
1031
+ metadata: { partRole: "part", isNamedLibrary: true }
1032
+ });
1033
+ }
1034
+ return;
1035
+ }
1036
+ if (uri2) {
1037
+ const resolved = targetOf(uri2);
1038
+ imports.push({ module: uri2, source: uri2, imported: ["*"], isPart: true, range: this.nodeToRange(node) });
1039
+ if (resolved !== null) {
1040
+ relationships.push({
1041
+ source: site.moduleName,
1042
+ target: resolved,
1043
+ type: ASTEngine.RelationshipType.PartOf,
1044
+ confidence: 1,
1045
+ line_number: this.nodeLine(uriNode2),
1046
+ metadata: { partRole: node.type === "part_of_directive" ? "part" : "library" }
1047
+ });
1048
+ }
1049
+ }
1050
+ return;
1051
+ }
1052
+ const isExport = node.type === "library_export" || node.text.startsWith("export");
1053
+ const uriNode = this.findDescendantByType(node, "string_literal") || this.findDescendantByType(node, "uri");
1054
+ const uri = uriNode ? unquote(uriNode.text) : "";
1055
+ if (!uri) return;
1056
+ if (uri.startsWith("package:flutter/")) frameworks["Flutter"] = true;
1057
+ if (uri.startsWith("package:angular/")) frameworks["AngularDart"] = true;
1058
+ if (isExport) {
1059
+ const resolved = targetOf(uri);
1060
+ relationships.push({
1061
+ source: site.moduleName,
1062
+ target: resolved ?? uri,
1063
+ type: ASTEngine.RelationshipType.Exports,
1064
+ confidence: 1,
1065
+ line_number: this.nodeLine(uriNode),
1066
+ metadata: { ...claimMeta(uri, resolved) }
1067
+ });
1068
+ } else {
1069
+ const resolved = targetOf(uri);
1070
+ imports.push({ module: uri, source: uri, imported: ["*"], isTypeOnly: false, range: this.nodeToRange(node) });
1071
+ relationships.push({
1072
+ source: site.moduleName,
1073
+ target: resolved ?? `${uri}:*`,
1074
+ type: ASTEngine.RelationshipType.Imports,
1075
+ confidence: 1,
1076
+ line_number: this.nodeLine(uriNode),
1077
+ metadata: { ...claimMeta(uri, resolved) }
1078
+ });
1079
+ }
1080
+ }
1081
+ // ============================================================================
1082
+ // RELATIONSHIP PASSES
1083
+ // ============================================================================
1084
+ addContainmentRelationships(symbols, relationships, nameLines) {
1085
+ for (const symbol of symbols) {
1086
+ if (symbol.ownerName) {
1087
+ const qualified = symbol.scope ? `${symbol.scope}.${symbol.name}` : symbol.name;
1088
+ relationships.push({
1089
+ source: symbol.ownerName,
1090
+ target: qualified,
1091
+ type: ASTEngine.RelationshipType.Contains,
1092
+ confidence: 1,
1093
+ // The member's OWN NAME line — the name node when the producers could
1094
+ // hand one over (a wrapped declaration moves it off the start line),
1095
+ // the declaration start otherwise (php stamps contains the same way).
1096
+ line_number: nameLines.get(dartNameLineKey(symbol.name, symbol.range)) ?? symbol.range?.startLine,
1097
+ metadata: {}
1098
+ });
1099
+ }
1100
+ }
1101
+ }
1102
+ extractOverrideRelationships(symbols, relationships, overrideCompanion, nameLines, projectIndex) {
1103
+ const byName = /* @__PURE__ */ new Map();
1104
+ for (const s of symbols) {
1105
+ if (s.kind === ASTEngine.SymbolKind.Constructor) continue;
1106
+ const list = byName.get(s.name) ?? [];
1107
+ list.push(s);
1108
+ byName.set(s.name, list);
1109
+ }
1110
+ const companionForPass2 = overrideCompanion ?? dartCompanionFor(projectIndex);
1111
+ const seesProject = companionForPass2 !== void 0;
1112
+ const heritageByClass = /* @__PURE__ */ new Map();
1113
+ const firstHeritage = /* @__PURE__ */ new Map();
1114
+ for (const r of relationships) {
1115
+ if (!r.source || !r.target) continue;
1116
+ const type = r.type ?? r.relationshipType ?? "";
1117
+ if (type === "extends" || type === "implements" || type === "uses" || type === "mixes_in") {
1118
+ const list = heritageByClass.get(r.source) ?? [];
1119
+ if (!list.includes(r.target)) list.push(r.target);
1120
+ heritageByClass.set(r.source, list);
1121
+ if (!firstHeritage.has(r.source)) firstHeritage.set(r.source, r.target);
1122
+ }
1123
+ }
1124
+ const basesOf = /* @__PURE__ */ new Map();
1125
+ for (const r of relationships) {
1126
+ const type = r.type ?? r.relationshipType ?? "";
1127
+ if (type !== "extends" && type !== "implements") continue;
1128
+ if (!r.source || !r.target) continue;
1129
+ const list = basesOf.get(r.source) ?? [];
1130
+ if (!list.includes(r.target)) list.push(r.target);
1131
+ basesOf.set(r.source, list);
1132
+ }
1133
+ const baseOrder = /* @__PURE__ */ new Map();
1134
+ for (const r of relationships) {
1135
+ const type = r.type ?? r.relationshipType ?? "";
1136
+ const isBase = type === "extends" || type === "implements" || type === "mixes_in";
1137
+ if (!isBase || !r.source || !r.target) continue;
1138
+ const list = baseOrder.get(r.source) ?? [];
1139
+ if (!list.includes(r.target)) list.push(r.target);
1140
+ baseOrder.set(r.source, list);
1141
+ }
1142
+ const exposedCache = /* @__PURE__ */ new Map();
1143
+ const exposing = /* @__PURE__ */ new Set();
1144
+ const exposed = (cls) => {
1145
+ const cached = exposedCache.get(cls);
1146
+ if (cached) return cached;
1147
+ if (exposing.has(cls)) return /* @__PURE__ */ new Set();
1148
+ exposing.add(cls);
1149
+ const out = /* @__PURE__ */ new Set();
1150
+ for (const [name, syms] of byName) if (syms.some((bm) => bm.ownerName === cls)) out.add(name);
1151
+ for (const b of basesOf.get(cls) ?? []) for (const m of exposed(b)) out.add(m);
1152
+ exposing.delete(cls);
1153
+ exposedCache.set(cls, out);
1154
+ return out;
1155
+ };
1156
+ const declares = (base, method) => {
1157
+ if (exposed(base).has(method)) return true;
1158
+ const companion = companionForPass2?.get(base);
1159
+ return !!companion && companion.has(method);
1160
+ };
1161
+ const TYPE_KINDS = /* @__PURE__ */ new Set([
1162
+ ASTEngine.SymbolKind.Class,
1163
+ ASTEngine.SymbolKind.Interface,
1164
+ ASTEngine.SymbolKind.Enum,
1165
+ ASTEngine.SymbolKind.Type
1166
+ ]);
1167
+ const inFileTypes = /* @__PURE__ */ new Set();
1168
+ for (const s of symbols) {
1169
+ if (typeof s.name === "string" && s.name && TYPE_KINDS.has(s.kind)) inFileTypes.add(s.name);
1170
+ }
1171
+ const seenOverride = /* @__PURE__ */ new Set();
1172
+ for (const s of symbols) {
1173
+ if ((s.kind === ASTEngine.SymbolKind.Method || s.kind === ASTEngine.SymbolKind.Property) && s.ownerName) {
1174
+ if (s.isStatic) continue;
1175
+ const heritage = heritageByClass.get(s.ownerName);
1176
+ if (!heritage || heritage.length === 0) continue;
1177
+ let target = null;
1178
+ const seenCls = /* @__PURE__ */ new Set();
1179
+ const queue = [...heritage];
1180
+ while (queue.length > 0 && !target) {
1181
+ const base = queue.shift();
1182
+ if (seenCls.has(base)) continue;
1183
+ seenCls.add(base);
1184
+ if (declares(base, s.name)) {
1185
+ target = base;
1186
+ break;
1187
+ }
1188
+ for (const h of heritageByClass.get(base) ?? []) queue.push(h);
1189
+ }
1190
+ let confidence = 0.95;
1191
+ let metadata = { isOverride: true, relationshipType: "method_override" };
1192
+ if (target) {
1193
+ } else if (companionForPass2 && heritage.length > 0) {
1194
+ const seen2 = /* @__PURE__ */ new Set();
1195
+ const q2 = [...heritage];
1196
+ while (q2.length > 0 && !target) {
1197
+ const base = q2.shift();
1198
+ if (seen2.has(base)) continue;
1199
+ seen2.add(base);
1200
+ if (this.companionHas(companionForPass2, base, s.name)) {
1201
+ target = base;
1202
+ break;
1203
+ }
1204
+ for (const h of heritageByClass.get(base) ?? []) q2.push(h);
1205
+ }
1206
+ }
1207
+ if (!target) {
1208
+ const hasOverrideAnn = (s.decorators ?? []).some((d) => d.includes("@override"));
1209
+ if (hasOverrideAnn) {
1210
+ const rawFirst = firstHeritage.get(s.ownerName) ?? null;
1211
+ const first = rawFirst === DART_OBJECT_TYPE && !DART_OBJECT_INTERFACE_MEMBERS.has(s.name) ? (baseOrder.get(s.ownerName) ?? []).find((b) => b !== DART_OBJECT_TYPE) ?? null : rawFirst;
1212
+ const indexKnowsBase = first !== null && companionForPass2?.has(first) === true;
1213
+ const refuted = first !== null && !declares(first, s.name) && (byName.get(s.name)?.some((bm) => bm.ownerName === first) ?? false) === false && (heritageByClass.has(first) || inFileTypes.has(first) || indexKnowsBase);
1214
+ if (!refuted) {
1215
+ target = first;
1216
+ confidence = 0.8;
1217
+ metadata = seesProject || first === DART_OBJECT_TYPE ? { isOverride: true, relationshipType: "method_override", isExternalBase: true } : { isOverride: true, relationshipType: "method_override", baseBeyondRunScope: String(first) };
1218
+ if (first !== null && first !== DART_OBJECT_TYPE && DART_OBJECT_INTERFACE_MEMBERS.has(s.name)) {
1219
+ target = DART_OBJECT_TYPE;
1220
+ const projectPublishesObject = companionForPass2 !== void 0 && this.companionHas(companionForPass2, DART_OBJECT_TYPE, s.name);
1221
+ metadata = {
1222
+ isOverride: true,
1223
+ relationshipType: "method_override",
1224
+ ...projectPublishesObject ? {} : { isExternalBase: true },
1225
+ isObjectInterfaceBase: true,
1226
+ firstHeritageParent: first
1227
+ };
1228
+ if (projectPublishesObject) confidence = 0.95;
1229
+ }
1230
+ }
1231
+ }
1232
+ }
1233
+ if (!target) continue;
1234
+ const key = `${s.ownerName}.${s.name}->${target}.${s.name}`;
1235
+ if (seenOverride.has(key)) continue;
1236
+ seenOverride.add(key);
1237
+ relationships.push({
1238
+ source: `${s.ownerName}.${s.name}`,
1239
+ target: `${target}.${s.name}`,
1240
+ type: ASTEngine.RelationshipType.Overrides,
1241
+ confidence,
1242
+ // The overriding METHOD's own name line — the node that names the
1243
+ // edge, not the base it shadows (and not a wrapped start line).
1244
+ line_number: nameLines?.get(dartNameLineKey(s.name, s.range)) ?? s.range?.startLine,
1245
+ metadata
1246
+ });
1247
+ }
1248
+ }
1249
+ }
1250
+ /** Companion lookup with leaf-name fallback (bases may arrive qualified). */
1251
+ companionHas(companion, base, method) {
1252
+ if (companion.get(base)?.has(method)) return true;
1253
+ const leaf = base.includes(".") ? base.split(".").pop() : base;
1254
+ return leaf !== base && !!companion.get(leaf)?.has(method);
1255
+ }
1256
+ /**
1257
+ * The whole-tree EXPRESSION pass: `calls`, `uses`, `instantiates` and
1258
+ * `references`.
1259
+ *
1260
+ * Named for what it reads rather than for when it was written. It began as the
1261
+ * call pass, but it is also the only traversal that already knows, at every
1262
+ * node, which declaration encloses it (`current`/`owner`, the family attribution
1263
+ * rule), and a type mention's SOURCE is the same question a call's source is.
1264
+ * A second traversal would have had to re-derive that attribution, and two
1265
+ * copies of one rule are two rules that drift.
1266
+ *
1267
+ * `projectIndex` is LAST and it must stay last — the same hazard
1268
+ * `extractOverrideRelationships` documents: an optional parameter inserted
1269
+ * earlier silently swapped the ones after it.
1270
+ */
1271
+ extractExpressionRelationships(node, relationships, moduleName, symbols, projectIndex) {
1272
+ const importAliases = this.collectImportAliases(node);
1273
+ const visibleTypes = /* @__PURE__ */ new Set();
1274
+ for (const s of symbols) {
1275
+ const kind = s.kind;
1276
+ const isType = kind === ASTEngine.SymbolKind.Class || kind === ASTEngine.SymbolKind.Mixin || kind === ASTEngine.SymbolKind.Extension || kind === ASTEngine.SymbolKind.Enum || kind === ASTEngine.SymbolKind.TypeAlias;
1277
+ if (!isType) continue;
1278
+ if (kind === ASTEngine.SymbolKind.Extension && s.metadata?.isUnnamedExtension) continue;
1279
+ if (s.name) visibleTypes.add(s.name);
1280
+ }
1281
+ const fileTypes = new Set(visibleTypes);
1282
+ const libraryScope = /* @__PURE__ */ new Set();
1283
+ for (const s of symbols) {
1284
+ if (s.ownerName) continue;
1285
+ if (fileTypes.has(s.name)) continue;
1286
+ if (s.kind === ASTEngine.SymbolKind.EnumMember) continue;
1287
+ if (s.name) libraryScope.add(s.name);
1288
+ }
1289
+ for (const t of projectIndex?.typeKinds?.keys?.() ?? []) visibleTypes.add(String(t));
1290
+ const namedCtors = /* @__PURE__ */ new Set();
1291
+ for (const s of symbols) {
1292
+ if (s.kind === ASTEngine.SymbolKind.Constructor && s.ownerName) {
1293
+ const seg = s.metadata?.namedSegment;
1294
+ if (seg) namedCtors.add(`${s.ownerName}.${seg}`);
1295
+ }
1296
+ }
1297
+ const membersByOwner = /* @__PURE__ */ new Map();
1298
+ for (const s of symbols) {
1299
+ if (!s.ownerName) continue;
1300
+ let set = membersByOwner.get(s.ownerName);
1301
+ if (!set) {
1302
+ set = /* @__PURE__ */ new Set();
1303
+ membersByOwner.set(s.ownerName, set);
1304
+ }
1305
+ set.add(s.name);
1306
+ }
1307
+ const topLevelValues = /* @__PURE__ */ new Set();
1308
+ for (const s of symbols) {
1309
+ if (s.ownerName) continue;
1310
+ if (s.kind === ASTEngine.SymbolKind.Variable || s.kind === ASTEngine.SymbolKind.Constant || s.kind === ASTEngine.SymbolKind.Function || s.kind === ASTEngine.SymbolKind.Property) {
1311
+ topLevelValues.add(s.name);
1312
+ }
1313
+ }
1314
+ const superByClass = /* @__PURE__ */ new Map();
1315
+ {
1316
+ const pre = [node];
1317
+ while (pre.length > 0) {
1318
+ const cn = pre.pop();
1319
+ if (!cn) continue;
1320
+ if (cn.type === "class_definition") {
1321
+ const nm = cn.childForFieldName?.("name")?.text;
1322
+ const sup = this.findChildByType(cn, "superclass");
1323
+ if (nm && sup) {
1324
+ const base = this.resolvedHeritageNames(sup)[0];
1325
+ if (base) superByClass.set(nm, base);
1326
+ }
1327
+ }
1328
+ for (let i = cn.namedChildCount - 1; i >= 0; i--) {
1329
+ const ch = cn.namedChild(i);
1330
+ if (ch) pre.push(ch);
1331
+ }
1332
+ }
1333
+ }
1334
+ const fileQualified = /* @__PURE__ */ new Set();
1335
+ for (const s of symbols) {
1336
+ if (s.ownerName) fileQualified.add(`${s.ownerName}.${s.name}`);
1337
+ }
1338
+ const indexDeclares = (typeName, member) => {
1339
+ const set = projectIndex?.typeMembers?.get?.(typeName);
1340
+ if (set instanceof Set) return set.has(member);
1341
+ if (Array.isArray(set)) return set.includes(member);
1342
+ return false;
1343
+ };
1344
+ const declaringOwner = (member, from, memberNodesByOwner) => {
1345
+ const seen = /* @__PURE__ */ new Set();
1346
+ let cur = from;
1347
+ while (cur && !seen.has(cur)) {
1348
+ seen.add(cur);
1349
+ if (memberNodesByOwner.get(cur)?.has(member)) return cur;
1350
+ cur = superByClass.get(cur) ?? null;
1351
+ }
1352
+ return null;
1353
+ };
1354
+ const typeKindByName = /* @__PURE__ */ new Map();
1355
+ const fieldTypeByField = /* @__PURE__ */ new Map();
1356
+ for (const s of symbols) {
1357
+ if (!s.ownerName) continue;
1358
+ const dt = String(s.metadata?.dataType ?? "");
1359
+ if (isPlainIdentifier(dt)) fieldTypeByField.set(`${s.ownerName}.${s.name}`, dt);
1360
+ }
1361
+ const qualifiedTarget = (ownerText, member) => {
1362
+ if (!ownerText || !member) return { target: null, cause: "receiverUnnamed" };
1363
+ if (!visibleTypes.has(ownerText)) {
1364
+ if (!isCapitalized(ownerText)) return { target: null, cause: "receiverTypeUnknown" };
1365
+ return { target: `${ownerText}.${member}`, cause: null, typeName: ownerText, claim: ownerText };
1366
+ }
1367
+ const declared = declaringOwner(member, ownerText, membersByOwner);
1368
+ if (declared) {
1369
+ const spelling = `${declared}.${member}`;
1370
+ if (fileQualified.has(spelling)) return { target: spelling, cause: null };
1371
+ return { target: null, cause: "receiverInFileNotSpelled" };
1372
+ }
1373
+ if (indexDeclares(ownerText, member)) return { target: `${ownerText}.${member}`, cause: null };
1374
+ return { target: null, cause: "receiverMemberOutsideRun" };
1375
+ };
1376
+ const receiverCut = (q) => {
1377
+ if (q.claim !== void 0) return { baseBeyondRunScope: q.claim };
1378
+ return q.cause ? { receiverCause: q.cause, ...q.typeName ? { receiverType: q.typeName } : {} } : {};
1379
+ };
1380
+ const plainTypeName = (text) => {
1381
+ const leaf = stripNullableSuffix(String(text ?? ""));
1382
+ return isPlainIdentifier(leaf) ? leaf : void 0;
1383
+ };
1384
+ const headOwner = (head, enclosing, localTypes) => {
1385
+ const t = head?.type;
1386
+ if (t === "this") return enclosing;
1387
+ if (t === "super") return superByClass.get(enclosing ?? "") ?? null;
1388
+ const text = this.headNameText(head);
1389
+ if (text === null) return null;
1390
+ const localType = localTypes.get(text);
1391
+ if (localType) return localType;
1392
+ const fieldType = fieldTypeByField.get(`${enclosing ?? ""}.${text}`);
1393
+ if (fieldType) return fieldType;
1394
+ return text;
1395
+ };
1396
+ const receiverOwnerMeta = (head, enclosing, localTypes, localEvidence) => {
1397
+ const t = head?.type;
1398
+ if (t === "this" || t === "super") return null;
1399
+ const text = this.headNameText(head);
1400
+ if (text === null) return null;
1401
+ const classLikeType = (name) => {
1402
+ if (name === "dynamic" || name === "void" || name === "Never") return null;
1403
+ const kind = typeKindByName.get(name);
1404
+ if (kind === void 0) return name;
1405
+ if (kind === "class" || kind === "enum") return name;
1406
+ return null;
1407
+ };
1408
+ const localType = localTypes.get(text);
1409
+ if (localType) {
1410
+ const evidence = localEvidence.get(text);
1411
+ const owner2 = classLikeType(localType);
1412
+ return evidence && owner2 ? { owner: owner2, ownerEvidence: evidence } : null;
1413
+ }
1414
+ const fieldType = fieldTypeByField.get(`${enclosing ?? ""}.${text}`);
1415
+ const owner = fieldType ? classLikeType(fieldType) : null;
1416
+ if (owner) return { owner, ownerEvidence: "field" };
1417
+ return null;
1418
+ };
1419
+ for (const s of symbols) {
1420
+ if (s.ownerName) continue;
1421
+ const kind = String(s.kind ?? "");
1422
+ if (kind === "class" || kind === "enum" || kind === "mixin" || kind === "extension" || kind === "extension_type" || kind === "type_alias" || kind === "type") {
1423
+ if (s.name) typeKindByName.set(s.name, kind);
1424
+ }
1425
+ }
1426
+ const resolveCurrent = (n, current) => {
1427
+ if (n.type === "function_signature" && n.parent?.type === "lambda_expression") {
1428
+ return current;
1429
+ }
1430
+ let sig = null;
1431
+ if (this.isSignatureNode(n.type)) {
1432
+ sig = n;
1433
+ } else if (n.type === "declaration") {
1434
+ for (let i = 0; i < n.namedChildCount; i++) {
1435
+ const c = n.namedChild(i);
1436
+ if (c && this.isSignatureNode(c.type)) {
1437
+ sig = c;
1438
+ break;
1439
+ }
1440
+ }
1441
+ }
1442
+ if (sig) {
1443
+ const inner = this.findInnerSignature(sig);
1444
+ if (this.isConstructorSignature(sig.type) || this.isConstructorSignature(inner.type)) {
1445
+ if (this.isRealConstructor(inner, this.enclosingTypeName(inner))) return "constructor";
1446
+ }
1447
+ const nm = this.signatureName(inner);
1448
+ if (nm) return nm;
1449
+ }
1450
+ return current;
1451
+ };
1452
+ const resolveOwner = (n, owner) => {
1453
+ const t = n.type;
1454
+ if (t === "class_definition" || t === "enum_declaration") {
1455
+ const nm = n.childForFieldName?.("name")?.text;
1456
+ return nm || owner;
1457
+ }
1458
+ if (t === "extension_declaration" || t === "extension_type_declaration") {
1459
+ return this.extensionPublishedName(n) || owner;
1460
+ }
1461
+ if (t === "mixin_declaration") {
1462
+ const id = this.findChildByType(n, "identifier");
1463
+ return id ? id.text : owner;
1464
+ }
1465
+ if (t === "type_alias") {
1466
+ const nm = n.childForFieldName?.("name")?.text || this.findChildByType(n, "type_identifier")?.text;
1467
+ return nm || owner;
1468
+ }
1469
+ return owner;
1470
+ };
1471
+ const visitNode = (n, current, owner, locals, localTypes, localEvidence, localElements, insideCollected) => {
1472
+ const prev = current;
1473
+ const prevOwner = owner;
1474
+ const t = n.type;
1475
+ current = resolveCurrent(n, current);
1476
+ owner = resolveOwner(n, owner);
1477
+ let childLocals = locals;
1478
+ let childLocalTypes = localTypes;
1479
+ let childLocalEvidence = localEvidence;
1480
+ let childLocalElements = localElements;
1481
+ let childInsideCollected = insideCollected;
1482
+ if (t === "function_body" || t === "function_expression_body" || t === "block") {
1483
+ let bound = null;
1484
+ let boundTypes = null;
1485
+ if (!insideCollected) {
1486
+ const own = this.collectDartLocals(n);
1487
+ if (own.size > 0) bound = own;
1488
+ const ownTypes = this.collectDartLocalTypes(n);
1489
+ if (ownTypes.size > 0) boundTypes = ownTypes;
1490
+ }
1491
+ const sig = n.previousNamedSibling;
1492
+ if (sig) {
1493
+ const sigNames = this.collectDartLocals(sig);
1494
+ if (sigNames.size > 0) {
1495
+ if (!bound) bound = /* @__PURE__ */ new Set();
1496
+ for (const nm of sigNames) bound.add(nm);
1497
+ }
1498
+ const sigTypes = this.collectDartParameterTypes(sig);
1499
+ if (sigTypes.size > 0) {
1500
+ if (!boundTypes) boundTypes = /* @__PURE__ */ new Map();
1501
+ for (const [nm, ty] of sigTypes) boundTypes.set(nm, ty);
1502
+ }
1503
+ }
1504
+ if (bound) {
1505
+ childLocals = new Set(locals);
1506
+ for (const nm of bound) childLocals.add(nm);
1507
+ }
1508
+ if (boundTypes) {
1509
+ childLocalTypes = new Map(localTypes);
1510
+ childLocalEvidence = new Map(localEvidence);
1511
+ childLocalElements = new Map(localElements);
1512
+ for (const [nm, ty] of boundTypes) {
1513
+ childLocalTypes.set(nm, ty.type);
1514
+ childLocalEvidence.set(nm, ty.evidence);
1515
+ if (ty.element) childLocalElements.set(nm, ty.element);
1516
+ if (ty.keyElement) childLocalElements.set(`${nm}\0key`, ty.keyElement);
1517
+ }
1518
+ }
1519
+ const awaitTypes = this.collectAwaitLocalTypes(n, childLocalElements, childLocalTypes);
1520
+ if (awaitTypes.size > 0) {
1521
+ childLocalTypes = new Map(childLocalTypes);
1522
+ childLocalEvidence = new Map(childLocalEvidence);
1523
+ childLocalElements = new Map(childLocalElements);
1524
+ for (const [nm, ty] of awaitTypes) {
1525
+ childLocalTypes.set(nm, ty);
1526
+ childLocalEvidence.set(nm, "annotation");
1527
+ childLocalElements.set(nm, ty);
1528
+ }
1529
+ }
1530
+ if (!insideCollected) childInsideCollected = true;
1531
+ }
1532
+ if (t === "for_statement") {
1533
+ const parts = this.findChildByType(n, "for_loop_parts");
1534
+ if (parts) {
1535
+ const hasWrittenType = (parts.namedChildren ?? []).some((c) => c?.type === "type_identifier");
1536
+ const nameNode = parts.childForFieldName?.("name");
1537
+ const valueNode = parts.childForFieldName?.("value");
1538
+ if (!hasWrittenType && nameNode && valueNode && !childLocalTypes.has(nameNode.text)) {
1539
+ let element;
1540
+ let link;
1541
+ const sel = valueNode.nextNamedSibling;
1542
+ if (sel && sel.type === "selector") {
1543
+ const ids = [];
1544
+ const linkStack = [sel];
1545
+ while (linkStack.length > 0) {
1546
+ const x = linkStack.pop();
1547
+ if (x.type === "identifier") {
1548
+ ids.push(x.text);
1549
+ continue;
1550
+ }
1551
+ for (const c of x.namedChildren ?? []) if (c) linkStack.push(c);
1552
+ }
1553
+ link = ids.find((t2) => t2 === "values" || t2 === "keys");
1554
+ }
1555
+ if (link === "values") element = localElements.get(valueNode.text);
1556
+ else if (link === "keys") element = localElements.get(`${valueNode.text}\0key`);
1557
+ else element = localElements.get(valueNode.text);
1558
+ if (element) {
1559
+ childLocalTypes = new Map(childLocalTypes);
1560
+ childLocalTypes.set(nameNode.text, element);
1561
+ childLocalEvidence = new Map(childLocalEvidence);
1562
+ childLocalEvidence.set(nameNode.text, "annotation");
1563
+ }
1564
+ }
1565
+ }
1566
+ }
1567
+ if (t === "initializers") {
1568
+ const par = n.parent;
1569
+ if (par) {
1570
+ for (let k = 0; k < par.namedChildCount; k++) {
1571
+ const sib = par.namedChild(k);
1572
+ if (sib && (sib.type === "constructor_signature" || sib.type === "method_signature" || sib.type === "function_signature")) {
1573
+ const bound = this.collectDartLocals(sib);
1574
+ if (bound.size > 0) {
1575
+ childLocals = new Set(locals);
1576
+ for (const nm of bound) childLocals.add(nm);
1577
+ }
1578
+ break;
1579
+ }
1580
+ }
1581
+ }
1582
+ }
1583
+ if ((t === "function_body" || t === "block" || t === "function_expression_body") && current === prev) {
1584
+ const prevSib = n.previousNamedSibling;
1585
+ if (prevSib) {
1586
+ const sigSource = resolveCurrent(prevSib, current);
1587
+ if (sigSource !== current) current = sigSource;
1588
+ }
1589
+ }
1590
+ const callSource = current || owner || moduleName;
1591
+ if (t === "constructor_invocation" || t === "new_expression" || t === "const_object_expression") {
1592
+ const typeNode = this.findChildByType(n, "type_identifier");
1593
+ if (typeNode && isCapitalized(typeNode.text)) {
1594
+ relationships.push({
1595
+ source: callSource,
1596
+ target: typeNode.text,
1597
+ type: ASTEngine.RelationshipType.Instantiates,
1598
+ confidence: 0.95,
1599
+ line_number: this.nodeLine(typeNode),
1600
+ metadata: { isConstructorInvocation: true }
1601
+ });
1602
+ }
1603
+ }
1604
+ if (t === "selector" && this.findChildByType(n, "argument_part")) {
1605
+ const { head, links } = this.invocationChain(n);
1606
+ if (links.length === 0) {
1607
+ const leafNode = this.resolveSelectorCalleeNode(n);
1608
+ const leaf = leafNode?.text ?? null;
1609
+ if (leaf) {
1610
+ if (this.isDartGlobalBuiltin(leaf)) {
1611
+ } else if (isCapitalized(leaf) && this.selectorIsBareCall(n)) {
1612
+ relationships.push({
1613
+ source: callSource,
1614
+ target: leaf,
1615
+ type: ASTEngine.RelationshipType.Instantiates,
1616
+ confidence: 0.95,
1617
+ line_number: this.nodeLine(leafNode),
1618
+ metadata: {}
1619
+ });
1620
+ } else {
1621
+ const isBinding = locals.has(leaf);
1622
+ const q0 = isBinding ? { target: null, cause: null } : qualifiedTarget(owner, leaf);
1623
+ const q = q0.cause === "receiverMemberOutsideRun" && libraryScope.has(leaf) ? { target: null, cause: "libraryScopeCallee" } : q0;
1624
+ relationships.push({
1625
+ source: callSource,
1626
+ target: q.target ?? (isBinding ? `${callSource}${BINDING_SEP}${leaf}` : leaf),
1627
+ type: ASTEngine.RelationshipType.Calls,
1628
+ confidence: 0.9,
1629
+ line_number: this.nodeLine(leafNode),
1630
+ // Metadata only, for the marker half. No row is added or removed and no
1631
+ // confidence moves, so this cannot change WHICH edges exist; it makes an
1632
+ // existing population queryable, which is what M14 §4 asks of a heuristic
1633
+ // edge.
1634
+ metadata: {
1635
+ ...isBinding ? { isLocalRead: true, localBinding: true } : {},
1636
+ ...q.target ? { qualifier: "enclosing" } : {},
1637
+ // THE RECEIVER CUT, on the rows that stayed bare and only there — a row that
1638
+ // resolved already says how it resolved (`qualifier`), and a binding has no
1639
+ // receiver question to answer.
1640
+ ...isBinding ? {} : receiverCut(q)
1641
+ }
1642
+ });
1643
+ }
1644
+ }
1645
+ } else {
1646
+ const method = links[links.length - 1];
1647
+ const before = links.length > 1 ? links[links.length - 2] : this.headNameText(head);
1648
+ const headText = this.headNameText(head);
1649
+ const calleeNode = n.previousNamedSibling ? this.lastIdentifierNode(n.previousNamedSibling) : null;
1650
+ if (this.isDartGlobalBuiltin(method)) {
1651
+ } else if (links.length === 1 && isCapitalized(method) && headText !== null && !isCapitalized(headText) && head !== null && (head.type === "identifier" || head.type === "type_identifier")) {
1652
+ relationships.push({
1653
+ source: callSource,
1654
+ target: method,
1655
+ type: ASTEngine.RelationshipType.Instantiates,
1656
+ confidence: 0.95,
1657
+ line_number: this.nodeLine(calleeNode),
1658
+ metadata: { isNamedConstructor: true, isPrefixQualified: true }
1659
+ });
1660
+ } else if (before !== null && isCapitalized(before) && namedCtors.has(`${before}.${method}`)) {
1661
+ relationships.push({
1662
+ source: callSource,
1663
+ target: before,
1664
+ type: ASTEngine.RelationshipType.Instantiates,
1665
+ confidence: 0.95,
1666
+ line_number: this.nodeLine(calleeNode),
1667
+ metadata: { isNamedConstructor: true }
1668
+ });
1669
+ } else {
1670
+ const ownerCand = links.length > 1 ? before : headOwner(head, owner, localTypes);
1671
+ const ownMeta = links.length > 1 ? null : receiverOwnerMeta(head, owner, localTypes, localEvidence);
1672
+ const q = qualifiedTarget(ownerCand, method);
1673
+ relationships.push({
1674
+ source: callSource,
1675
+ target: q.target ?? method,
1676
+ type: ASTEngine.RelationshipType.Calls,
1677
+ confidence: 0.9,
1678
+ line_number: this.nodeLine(calleeNode),
1679
+ // Same reason as the bare-call arm above: a leaf that the enclosing
1680
+ // function binds is a binding, and the row is labelled so a consumer
1681
+ // can tell it from a call to a declared member.
1682
+ //
1683
+ // 2026-10-08 — DELIBERATELY NOT re-spelled as a binding, and the reason is a
1684
+ // finding rather than an omission. This arm runs for a chain of TWO or more
1685
+ // links (`a.b.c()`), where the leaf is resolved against the RECEIVER, not
1686
+ // against the enclosing function: the `locals` set consulted here is the
1687
+ // enclosing function's, so `isLocalRead` on this arm answers a question about
1688
+ // the WRONG SCOPE whenever `links.length > 1`. The target cannot be re-spelled
1689
+ // to `#` for the same reason — for a chain the leaf may genuinely name a
1690
+ // declaration reachable through the receiver, and `qualifiedTarget` is the rule
1691
+ // that decides it. Named, not fixed: a one-link chain reaches the bare-call arm
1692
+ // above and IS re-spelled; the multi-link case needs its own measurement of how
1693
+ // often `locals.has(method)` is true where no receiver member exists.
1694
+ metadata: {
1695
+ ...locals.has(method) ? { isLocalRead: true } : {},
1696
+ ...q.target ? { qualifier: "chain" } : {},
1697
+ ...receiverCut(q),
1698
+ // THE STORE'S CHANNEL (see `receiverOwnerMeta`): the exactly-known receiver
1699
+ // type plus the evidence class. Additive — the target spelling above is
1700
+ // unchanged, so nothing here can move an edge.
1701
+ ...ownMeta ?? {}
1702
+ }
1703
+ });
1704
+ }
1705
+ }
1706
+ }
1707
+ if (t === "annotation") {
1708
+ this.emitAnnotationReferences(n, relationships, current, owner, moduleName, importAliases);
1709
+ }
1710
+ if (t === "selector" && !this.findChildByType(n, "argument_part")) {
1711
+ const memberNode = this.linkMemberNode(n);
1712
+ const member = memberNode?.text ?? null;
1713
+ if (member && !this.isDartGlobalBuiltin(member)) {
1714
+ const next = n.nextNamedSibling;
1715
+ const nextIsArgs = !!next && next.type === "selector" && !!this.findChildByType(next, "argument_part");
1716
+ if (!nextIsArgs && !this.selectorIsLhsWrite(n)) {
1717
+ const head = n.previousNamedSibling;
1718
+ const headText = this.headNameText(head);
1719
+ if (headText !== null && isCapitalized(headText) && namedCtors.has(`${headText}.${member}`)) {
1720
+ relationships.push({
1721
+ source: callSource,
1722
+ target: headText,
1723
+ type: ASTEngine.RelationshipType.Instantiates,
1724
+ confidence: 0.95,
1725
+ line_number: this.nodeLine(memberNode),
1726
+ metadata: { isNamedConstructor: true, isTearOff: true }
1727
+ });
1728
+ } else {
1729
+ const ownMeta = receiverOwnerMeta(head, owner, localTypes, localEvidence);
1730
+ const q = qualifiedTarget(headOwner(head, owner, localTypes), member);
1731
+ relationships.push({
1732
+ source: callSource,
1733
+ target: q.target ?? member,
1734
+ type: ASTEngine.RelationshipType.Uses,
1735
+ confidence: 0.9,
1736
+ line_number: this.nodeLine(memberNode),
1737
+ metadata: q.target ? { isMemberRead: true, qualifier: "head", ...receiverCut(q), ...ownMeta ?? {} } : { isMemberRead: true, ...receiverCut(q), ...ownMeta ?? {} }
1738
+ });
1739
+ }
1740
+ }
1741
+ }
1742
+ }
1743
+ if (t === "qualified" && this.isPatternTestConstant(n)) {
1744
+ const segs = n.text.split(".");
1745
+ const member = segs[segs.length - 1];
1746
+ const head = segs.length > 1 ? typeNameWithoutArguments(segs[segs.length - 2]) : null;
1747
+ if (member && !this.isDartGlobalBuiltin(member) && member !== head) {
1748
+ if (head !== null && isCapitalized(head) && (namedCtors.has(`${head}.${member}`) || member === "new")) {
1749
+ relationships.push({
1750
+ source: callSource,
1751
+ target: head,
1752
+ type: ASTEngine.RelationshipType.Instantiates,
1753
+ confidence: 0.95,
1754
+ line_number: this.nodeLine(n),
1755
+ metadata: member === "new" ? { isConstructorReference: true, isConstantPattern: true } : { isNamedConstructor: true, isConstantPattern: true }
1756
+ });
1757
+ } else {
1758
+ const q = qualifiedTarget(head, member);
1759
+ relationships.push({
1760
+ source: callSource,
1761
+ target: q.target ?? member,
1762
+ type: ASTEngine.RelationshipType.Uses,
1763
+ confidence: 0.9,
1764
+ line_number: this.nodeLine(n),
1765
+ metadata: q.target ? { isMemberRead: true, isConstantPattern: true, qualifier: "pattern", ...receiverCut(q) } : { isMemberRead: true, isConstantPattern: true, ...receiverCut(q) }
1766
+ });
1767
+ }
1768
+ }
1769
+ }
1770
+ if (t === "identifier") {
1771
+ const txt = n.text;
1772
+ const ptype = n.parent?.type ?? "";
1773
+ const memberLink = ptype === "unconditional_assignable_selector" || ptype === "conditional_assignable_selector" || ptype === "cascade_selector" || ptype === "selector";
1774
+ const next = n.nextNamedSibling;
1775
+ const nextIsCall = !!next && next.type === "selector" && !!this.findChildByType(next, "argument_part");
1776
+ const declName = this.isDartDeclNameParent(ptype) || this.isDartDeclaredName(n);
1777
+ const isWrite = this.selectorIsLhsWrite(n);
1778
+ const isLabel = ptype === "labeled_statement" || ptype === "break_statement" || ptype === "continue_statement";
1779
+ const isFieldInitTarget = ptype === "field_initializer" && (() => {
1780
+ const first = n.parent?.namedChild(0);
1781
+ return !!first && first.startIndex === n.startIndex && first.endIndex === n.endIndex;
1782
+ })();
1783
+ const isAnnotationName = ptype === "annotation" || ptype === "scoped_identifier" && n.parent?.parent?.type === "annotation";
1784
+ if (txt === "this" || txt === "super") {
1785
+ } else if (!memberLink && !nextIsCall && !declName && !isWrite && !isLabel && !isFieldInitTarget && !isAnnotationName) {
1786
+ const isDeclaredMember = !!owner && (membersByOwner.get(owner)?.has(txt) ?? false);
1787
+ if (!locals.has(txt) && isDeclaredMember) {
1788
+ const q = qualifiedTarget(owner, txt);
1789
+ relationships.push({
1790
+ source: callSource,
1791
+ target: q.target ?? txt,
1792
+ type: ASTEngine.RelationshipType.Uses,
1793
+ confidence: 0.6,
1794
+ line_number: this.nodeLine(n),
1795
+ metadata: q.target ? { isBareRead: true, qualifier: "enclosing", ...receiverCut(q) } : { isBareRead: true, ...receiverCut(q) }
1796
+ });
1797
+ } else if (locals.has(txt)) {
1798
+ relationships.push({
1799
+ source: callSource,
1800
+ target: `${callSource}${BINDING_SEP}${txt}`,
1801
+ type: ASTEngine.RelationshipType.Uses,
1802
+ confidence: 0.9,
1803
+ line_number: this.nodeLine(n),
1804
+ // A read of a PARAMETER is a binding too: `locals` holds the function's bound
1805
+ // names (parameters and `var`s alike), which is why both land here.
1806
+ metadata: { isLocalRead: true, localBinding: true, ...this.localBindingScope(n, txt) ?? {} }
1807
+ });
1808
+ } else if (this.isPatternTestConstant(n)) {
1809
+ relationships.push({
1810
+ source: callSource,
1811
+ target: txt,
1812
+ type: ASTEngine.RelationshipType.Uses,
1813
+ confidence: 0.9,
1814
+ line_number: this.nodeLine(n),
1815
+ metadata: { isConstantPattern: true, isPatternTest: true }
1816
+ });
1817
+ } else if (!isDeclaredMember && topLevelValues.has(txt)) {
1818
+ relationships.push({
1819
+ source: callSource,
1820
+ target: txt,
1821
+ type: ASTEngine.RelationshipType.Uses,
1822
+ confidence: 0.9,
1823
+ line_number: this.nodeLine(n),
1824
+ metadata: { isGlobalRead: true }
1825
+ });
1826
+ }
1827
+ }
1828
+ }
1829
+ if (t === "initializer_list_entry") {
1830
+ let headIsSuper = false;
1831
+ let headIsThis = false;
1832
+ let headNode = null;
1833
+ const firstRaw = n.child(0);
1834
+ if (firstRaw && !firstRaw.isNamed && (firstRaw.text === "super" || firstRaw.text === "this")) {
1835
+ headIsSuper = firstRaw.text === "super";
1836
+ headIsThis = firstRaw.text === "this";
1837
+ headNode = firstRaw;
1838
+ } else {
1839
+ const firstNamed = n.namedChild(0);
1840
+ if (firstNamed && (firstNamed.type === "super" || firstNamed.type === "this")) {
1841
+ headIsSuper = firstNamed.type === "super";
1842
+ headIsThis = firstNamed.type === "this";
1843
+ headNode = firstNamed;
1844
+ }
1845
+ }
1846
+ if (headIsSuper && owner) {
1847
+ const base = superByClass.get(owner);
1848
+ if (base) {
1849
+ relationships.push({
1850
+ source: callSource,
1851
+ target: base,
1852
+ type: ASTEngine.RelationshipType.Instantiates,
1853
+ confidence: 0.95,
1854
+ line_number: this.nodeLine(headNode),
1855
+ metadata: { isSuperConstructor: true }
1856
+ });
1857
+ }
1858
+ } else if (headIsThis && owner) {
1859
+ relationships.push({
1860
+ source: callSource,
1861
+ target: owner,
1862
+ type: ASTEngine.RelationshipType.Instantiates,
1863
+ confidence: 0.95,
1864
+ line_number: this.nodeLine(headNode),
1865
+ metadata: { isRedirectingConstructor: true }
1866
+ });
1867
+ }
1868
+ }
1869
+ if (t === "redirection" && owner) {
1870
+ let rSuper = false;
1871
+ let rThis = false;
1872
+ let headTok = null;
1873
+ for (let i = 0; i < n.namedChildCount; i++) {
1874
+ const c = n.namedChild(i);
1875
+ if (!c) continue;
1876
+ if (c.type === "this") {
1877
+ rThis = true;
1878
+ headTok = c;
1879
+ break;
1880
+ }
1881
+ if (c.type === "super") {
1882
+ rSuper = true;
1883
+ headTok = c;
1884
+ break;
1885
+ }
1886
+ if (c.type === "identifier" || c.type === "type_identifier" || c.type === "arguments") continue;
1887
+ break;
1888
+ }
1889
+ if (rSuper) {
1890
+ const base = superByClass.get(owner);
1891
+ if (base) {
1892
+ relationships.push({
1893
+ source: callSource,
1894
+ target: base,
1895
+ type: ASTEngine.RelationshipType.Instantiates,
1896
+ confidence: 0.95,
1897
+ line_number: this.nodeLine(headTok),
1898
+ metadata: { isSuperConstructor: true }
1899
+ });
1900
+ }
1901
+ } else if (rThis) {
1902
+ relationships.push({
1903
+ source: callSource,
1904
+ target: owner,
1905
+ type: ASTEngine.RelationshipType.Instantiates,
1906
+ confidence: 0.95,
1907
+ line_number: this.nodeLine(headTok),
1908
+ metadata: { isRedirectingConstructor: true }
1909
+ });
1910
+ }
1911
+ }
1912
+ if (t === "identifier") {
1913
+ const parent = n.parent;
1914
+ const ptype = parent?.type ?? "";
1915
+ if (!this.isDartDeclNameParent(ptype) && ptype !== "label" && !this.isDartDeclaredName(n)) {
1916
+ const nx = n.nextNamedSibling;
1917
+ if (nx && (nx.type === "selector" && this.isIndexSelector(nx) || this.isIndexAssignable(nx))) {
1918
+ relationships.push({
1919
+ source: callSource,
1920
+ target: n.text,
1921
+ type: ASTEngine.RelationshipType.Uses,
1922
+ confidence: 0.9,
1923
+ line_number: this.nodeLine(n),
1924
+ metadata: { isSubscriptBase: true }
1925
+ });
1926
+ } else if (this.isInsideTemplateSubstitution(n)) {
1927
+ const isLocal = locals.has(n.text);
1928
+ relationships.push({
1929
+ source: callSource,
1930
+ target: isLocal ? `${callSource}${BINDING_SEP}${n.text}` : n.text,
1931
+ type: ASTEngine.RelationshipType.Uses,
1932
+ confidence: 0.9,
1933
+ line_number: this.nodeLine(n),
1934
+ metadata: isLocal ? { isInterpolationRead: true, isLocalRead: true, localBinding: true, ...this.localBindingScope(n, n.text) ?? {} } : { isInterpolationRead: true }
1935
+ });
1936
+ }
1937
+ }
1938
+ }
1939
+ if (t === "identifier_dollar_escaped") {
1940
+ const raw = n.text;
1941
+ const name = raw.startsWith("$") ? raw.slice(1) : raw;
1942
+ if (name) {
1943
+ const isLocal = locals.has(name);
1944
+ relationships.push({
1945
+ source: callSource,
1946
+ target: isLocal ? `${callSource}${BINDING_SEP}${name}` : name,
1947
+ type: ASTEngine.RelationshipType.Uses,
1948
+ confidence: 0.9,
1949
+ line_number: this.nodeLine(n),
1950
+ metadata: isLocal ? { isInterpolationRead: true, isLocalRead: true, localBinding: true, ...this.localBindingScope(n, name) ?? {} } : { isInterpolationRead: true }
1951
+ });
1952
+ }
1953
+ }
1954
+ if (t === "cascade_section") {
1955
+ let sectionAssigns = false;
1956
+ for (let i = 0; i < n.childCount; i++) {
1957
+ const c = n.child(i);
1958
+ if (c && !c.isNamed && c.text === "=") {
1959
+ sectionAssigns = true;
1960
+ break;
1961
+ }
1962
+ }
1963
+ for (let i = 0; i < n.namedChildCount; i++) {
1964
+ const c = n.namedChild(i);
1965
+ if (!c) continue;
1966
+ if (c.type !== "cascade_selector" && c.type !== "unconditional_assignable_selector" && c.type !== "conditional_assignable_selector" && c.type !== "selector") continue;
1967
+ const next = n.namedChild(i + 1);
1968
+ if (next && next.type === "argument_part") {
1969
+ const idNode = this.lastIdentifierNode(c);
1970
+ const id = idNode?.text ?? null;
1971
+ if (id && !this.isDartGlobalBuiltin(id)) {
1972
+ relationships.push({
1973
+ source: callSource,
1974
+ target: id,
1975
+ type: ASTEngine.RelationshipType.Calls,
1976
+ confidence: 0.9,
1977
+ line_number: this.nodeLine(idNode),
1978
+ metadata: { isCascade: true }
1979
+ });
1980
+ }
1981
+ } else if (!sectionAssigns) {
1982
+ const idNode = this.lastIdentifierNode(c);
1983
+ const id = idNode?.text ?? null;
1984
+ if (id && !this.isDartGlobalBuiltin(id)) {
1985
+ relationships.push({
1986
+ source: callSource,
1987
+ target: id,
1988
+ type: ASTEngine.RelationshipType.Uses,
1989
+ confidence: 0.9,
1990
+ line_number: this.nodeLine(idNode),
1991
+ metadata: { isMemberRead: true, isCascade: true }
1992
+ });
1993
+ }
1994
+ }
1995
+ }
1996
+ }
1997
+ if (t === "type_identifier") {
1998
+ this.collectTypeMention(n, callSource, relationships, visibleTypes, fileTypes);
1999
+ }
2000
+ return [current, owner, childLocals, childLocalTypes, childLocalEvidence, childLocalElements, childInsideCollected];
2001
+ };
2002
+ const processNode = (root, current, owner, locals, localTypes, localEvidence, localElements, insideCollected) => {
2003
+ const work = [
2004
+ [root, current, owner, locals, localTypes, localEvidence, localElements, insideCollected]
2005
+ ];
2006
+ while (work.length > 0) {
2007
+ const [n2, cur, own, loc, types, evid, elems, inside] = work.pop();
2008
+ const [cur2, own2, loc2, types2, evid2, elems2, inside2] = visitNode(n2, cur, own, loc, types, evid, elems, inside);
2009
+ const kids = n2.namedChildren ?? [];
2010
+ for (let i = kids.length - 1; i >= 0; i--) {
2011
+ const child = kids[i];
2012
+ if (child) work.push([child, cur2, own2, loc2, types2, evid2, elems2, inside2]);
2013
+ }
2014
+ }
2015
+ };
2016
+ processNode(node, null, null, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Map(), /* @__PURE__ */ new Map(), /* @__PURE__ */ new Map(), false);
2017
+ }
2018
+ // ============================================================================
2019
+ // `references` — A DECLARATION MENTIONS A TYPE
2020
+ // ============================================================================
2021
+ /**
2022
+ * One type mention, read off ONE node. No recursion, no state: everything it
2023
+ * needs (`source`, the type set, the row sink) arrives as an argument, so the
2024
+ * rule below is the whole rule and a reader does not have to reconstruct a walk
2025
+ * to know what a `references` edge claims.
2026
+ *
2027
+ * ## WHAT IS CLAIMED
2028
+ *
2029
+ * `X references T` means: the declaration `X` names the type `T` in a TYPE
2030
+ * POSITION, and `T` binds to a type declaration this run can see.
2031
+ *
2032
+ * Dart is statically typed, and a type position is a grammatical position, not
2033
+ * a convention — the grammar has a dedicated node (`type_identifier`) that never
2034
+ * appears where a value identifier appears. So the mention is a PARSE FACT, not
2035
+ * a name-shape guess. What is not a parse fact is the BINDING of that name to a
2036
+ * declaration, which is why the edge is published only when the name resolves
2037
+ * and carries 0.95 rather than 1.0 — the same derivation `instantiates` is
2038
+ * published at, for the same reason (a syntactic role plus a resolved name).
2039
+ *
2040
+ * ## WHY RESOLUTION IS A GATE AND NOT A REPORT
2041
+ *
2042
+ * Measured over 1,000 corpus files across 5 repositories: 75,769 type mentions
2043
+ * outside heritage clauses, of which 3,460 (4.57%) name a type the same file
2044
+ * declares. The other 95.43% name `String`, `Widget`, `Future` — real types, in
2045
+ * `dart:core` and the Flutter SDK, declared in files this per-file run has not
2046
+ * read. Publishing those would make the label mostly a list of SDK types, at
2047
+ * ~22x the edge count, and every one of them an endpoint no symbol in the index
2048
+ * can bind. A narrow graph that is true beats a wide graph that is not, so the
2049
+ * unresolvable majority is deliberately unread, and the number is stated rather
2050
+ * than left to be discovered downstream.
2051
+ *
2052
+ * A `projectIndex` widens the gate to every type the run can see
2053
+ * (`src/ast/project-index.ts` `typeKinds`), which is how the differential's
2054
+ * `--dart-deps=direct` runs measure more than production does. Production does
2055
+ * not supply one for `.dart`: `parallel-indexer.ts` `PROJECT_INDEX_EXTENSIONS`
2056
+ * names only `.sol`. So the published set is file-local in production and grows
2057
+ * with an index — recorded per row in `isIndexResolved` so the two answers are
2058
+ * distinguishable in the store rather than conflated.
2059
+ *
2060
+ * ## WHAT IS NOT A REFERENCE
2061
+ *
2062
+ * Measured parent-position inventory (the probe that produced it is
2063
+ * `validation/m14/fixtures/dart/type-position-probe.ts`):
2064
+ *
2065
+ * published — formal_parameter, normal_parameter_type, super_formal_parameter,
2066
+ * constructor_param (a `this.x` parameter's type), function_signature /
2067
+ * getter_signature / setter_signature / operator_signature (return types),
2068
+ * type_arguments (`List<T>`, `Map<K,V>`), function_type, record_type_field,
2069
+ * type_cast (`as T`), type_test (`is T`), try_statement (`on T catch`),
2070
+ * initialized_variable_definition, typed_identifier, for_loop_parts,
2071
+ * declaration (a field's type), program (a top-level `const String x`),
2072
+ * qualified (the LEAF of `prefix.Type`), constructor_tearoff (`http.Client`
2073
+ * used as a value), extension_declaration (`on T`), representation_declaration
2074
+ * (an extension type's representation type), type_alias (`typedef A = B`),
2075
+ * type_bound (`<T extends B>`), object_pattern / variable_pattern / cast_pattern
2076
+ * (a Dart 3 pattern's type), record_type.
2077
+ *
2078
+ * NOT published, each for a stated reason:
2079
+ * superclass / interfaces / mixins / mixin_declaration / mixin_application —
2080
+ * heritage already publishes `extends` / `implements` / `uses`, and a second
2081
+ * label for the same fact double-counts it.
2082
+ * new_expression / const_object_expression / constructor_invocation /
2083
+ * redirecting_factory_constructor_signature — construction already publishes
2084
+ * `instantiates`.
2085
+ * type_parameter — `class Box<T>` DECLARES a type variable; it does not
2086
+ * mention a type. A mention of `T` inside that class resolves to the type
2087
+ * VARIABLE, which is what `isTypeParameterShadowed` refuses.
2088
+ * ERROR — nothing inside a parse-error region is a fact about a well-formed
2089
+ * program, and this analyzer's error policy is to publish what survived the
2090
+ * parse, not what the recovery invented.
2091
+ *
2092
+ * The last one is the reason this method checks the PARENT at all: in this
2093
+ * grammar `type_identifier` only ever appears in a type position, so the node
2094
+ * type alone would publish the double-counted positions above.
2095
+ */
2096
+ collectTypeMention(n, source, relationships, visibleTypes, fileTypes) {
2097
+ const parent = n.parent;
2098
+ const position = parent ? parent.type : "";
2099
+ if (DART_TYPE_MENTION_DOUBLE_COUNTED.has(position)) return;
2100
+ const name = n.text;
2101
+ if (!name || name === IMPLICIT_DYNAMIC_TYPE) return;
2102
+ if (name === source) return;
2103
+ if (this.isUnnamedExtensionSubject(n)) return;
2104
+ if (!visibleTypes.has(name)) return;
2105
+ if (this.isTypeParameterShadowed(n, name)) return;
2106
+ relationships.push({
2107
+ source,
2108
+ target: name,
2109
+ type: ASTEngine.RelationshipType.References,
2110
+ confidence: 0.95,
2111
+ line_number: this.nodeLine(n),
2112
+ metadata: {
2113
+ isTypeMention: true,
2114
+ typePosition: position,
2115
+ isIndexResolved: !fileTypes.has(name)
2116
+ }
2117
+ });
2118
+ }
2119
+ /**
2120
+ * Is `name` bound by a TYPE PARAMETER of an enclosing declaration?
2121
+ * `class Box<T> { T value; }` — `T` here is the type variable the class
2122
+ * introduced, so the mention is not a reference to a declared type even when
2123
+ * something in the project is also called `T`.
2124
+ */
2125
+ isTypeParameterShadowed(n, name) {
2126
+ let cur = n.parent;
2127
+ let guard = 0;
2128
+ while (cur && guard++ < 32) {
2129
+ const tp = this.findChildByType(cur, "type_parameters");
2130
+ if (tp) {
2131
+ for (let i = 0; i < tp.namedChildCount; i++) {
2132
+ const c = tp.namedChild(i);
2133
+ if (!c || c.type !== "type_parameter") continue;
2134
+ const declared = c.childForFieldName?.("name")?.text ?? this.findChildByType(c, "type_identifier")?.text;
2135
+ if (declared === name) return true;
2136
+ }
2137
+ }
2138
+ cur = cur.parent;
2139
+ }
2140
+ return false;
2141
+ }
2142
+ // ============================================================================
2143
+ // CLASS HERITAGE
2144
+ // ============================================================================
2145
+ extractClassHeritage(node, className, relationships, patterns) {
2146
+ const mixinAppCls = this.findChildByType(node, "mixin_application_class");
2147
+ if (mixinAppCls) {
2148
+ const app = this.findChildByType(mixinAppCls, "mixin_application");
2149
+ if (app) {
2150
+ const baseNode = this.resolvedHeritageNodes(app)[0] ?? null;
2151
+ if (baseNode) {
2152
+ relationships.push({
2153
+ source: className,
2154
+ target: baseNode.text,
2155
+ type: ASTEngine.RelationshipType.Extends,
2156
+ confidence: 1,
2157
+ line_number: this.nodeLine(baseNode),
2158
+ metadata: {}
2159
+ });
2160
+ }
2161
+ const mx = this.findChildByType(app, "mixins");
2162
+ if (mx) {
2163
+ patterns["Mixins"] = true;
2164
+ for (const ti of this.resolvedHeritageNodes(mx)) {
2165
+ relationships.push({
2166
+ source: className,
2167
+ target: ti.text,
2168
+ type: ASTEngine.RelationshipType.MixesIn,
2169
+ confidence: 1,
2170
+ line_number: this.nodeLine(ti),
2171
+ metadata: { isMixin: true }
2172
+ });
2173
+ }
2174
+ }
2175
+ }
2176
+ }
2177
+ if (node.type === "mixin_declaration") {
2178
+ const onNodes = this.resolvedHeritageNodes(node).filter((n) => n.text !== className);
2179
+ for (const baseNode of onNodes) {
2180
+ relationships.push({
2181
+ source: className,
2182
+ target: baseNode.text,
2183
+ type: ASTEngine.RelationshipType.References,
2184
+ confidence: 1,
2185
+ line_number: this.nodeLine(baseNode),
2186
+ metadata: { isMixinOn: true, isTypeMention: true, typePosition: "mixin_on_clause" }
2187
+ });
2188
+ }
2189
+ }
2190
+ const superclass = this.findChildByType(node, "superclass");
2191
+ if (superclass) {
2192
+ const baseNode = this.resolvedHeritageNodes(superclass)[0] ?? null;
2193
+ if (baseNode) {
2194
+ relationships.push({
2195
+ source: className,
2196
+ target: baseNode.text,
2197
+ type: ASTEngine.RelationshipType.Extends,
2198
+ confidence: 1,
2199
+ line_number: this.nodeLine(baseNode),
2200
+ metadata: {}
2201
+ });
2202
+ }
2203
+ }
2204
+ const interfaces = this.findChildByType(node, "interfaces");
2205
+ if (interfaces) {
2206
+ for (const ti of this.resolvedHeritageNodes(interfaces)) {
2207
+ relationships.push({
2208
+ source: className,
2209
+ target: ti.text,
2210
+ type: ASTEngine.RelationshipType.Implements,
2211
+ confidence: 1,
2212
+ line_number: this.nodeLine(ti),
2213
+ metadata: { isImplements: true }
2214
+ });
2215
+ }
2216
+ }
2217
+ let mixinsNode = superclass ? this.findChildByType(superclass, "mixins") : void 0;
2218
+ if (!mixinsNode) mixinsNode = this.findChildByType(node, "mixins");
2219
+ if (mixinsNode) {
2220
+ patterns["Mixins"] = true;
2221
+ for (const ti of this.resolvedHeritageNodes(mixinsNode)) {
2222
+ relationships.push({
2223
+ source: className,
2224
+ target: ti.text,
2225
+ type: ASTEngine.RelationshipType.MixesIn,
2226
+ confidence: 1,
2227
+ line_number: this.nodeLine(ti),
2228
+ metadata: { isMixin: true }
2229
+ });
2230
+ }
2231
+ }
2232
+ }
2233
+ // ============================================================================
2234
+ // `renders` — A COMPONENT RENDERS ANOTHER COMPONENT
2235
+ // ============================================================================
2236
+ //
2237
+ // THE CONTRACT (`src/ast/types.ts:131-140`): *"A component RENDERS another component: source = the
2238
+ // rendering symbol, target = the rendered component."* The fleet's OTHER lanes publish it in
2239
+ // three different languages and ALL THREE use this shape — the RENDERING SYMBOL as the source and
2240
+ // the RENDERED COMPONENT'S NAME as the target:
2241
+ //
2242
+ // · typescript/js (`real-typescript-analyzer.ts:6758`, `real-javascript-analyzer.ts:2667`):
2243
+ // source = the enclosing component function, target = the JSX tag name, and a LOWERCASE tag is
2244
+ // NOT a component (the language's own capitalization convention).
2245
+ // · clojure (`real-clojure-analyzer.ts:1584`): source = the enclosing fn, target = the component
2246
+ // symbol inside a hiccup vector — a VECTOR LITERAL, an EXISTING node type, no grammar change.
2247
+ // · csharp (`real-csharp-analyzer.ts:2285`): source = the rendering context, target = the
2248
+ // component named by a TYPE ARGUMENT — an EXISTING node type, no grammar change.
2249
+ //
2250
+ // ⇒⇒ THE VALUE IS NOT JSX-SPECIFIC, AND FOR DART THE CONSTRUCT IS FLUTTER'S `build` METHOD:
2251
+ // `class Home extends StatelessWidget { Widget build(BuildContext context) => Scaffold(); }`
2252
+ // `Home` IS the component and `Scaffold` IS the component it renders — the structural analogue of
2253
+ // a React component returning `<Scaffold/>`. Dart has no JSX, so the syntax that carries the
2254
+ // construct is the rendering ENTRY POINT, which is what the `build` method is.
2255
+ //
2256
+ // THE TRIGGER, AND WHY IT IS NOT THE METHOD NAME ALONE:
2257
+ // · the method is named `build` (Flutter's rendering entry point), AND
2258
+ // · its DECLARED RETURN TYPE is `Widget` — the framework's own marker, the same kind of
2259
+ // language-level convention the JS lane reads off capitalization. Without it, any class with
2260
+ // a `build()` method would qualify, including the protobuf-style message builders whose
2261
+ // `build()` returns a message and not a component.
2262
+ // The measured populations behind both halves are in
2263
+ // `validation/m14/measurements/dart-extended-constructs-2026-10-21.json`.
2264
+ //
2265
+ // WHAT DOES NOT FIRE, each for its own reason (and each pinned by the fixture):
2266
+ // · a `build` method whose body returns a VARIABLE or a CALL — no component is NAMED, so there
2267
+ // is nothing to render (the nearest construct, a call, stays a `calls` row);
2268
+ // · a `build` method whose declared return type is not `Widget` — not a rendering host;
2269
+ // · a construction that is NOT the returned expression — it is a `calls`/`instantiates` row,
2270
+ // because the widget tree is not the return value;
2271
+ // · a LOWERCASE name — Dart's class-naming convention, the same test the JS lane applies to a
2272
+ // JSX tag (`<div>` is an intrinsic, not a component).
2273
+ extractRenderRelationships(node, className, relationships) {
2274
+ const classBody = node.childForFieldName("body");
2275
+ if (!classBody) return;
2276
+ for (const member of classBody.namedChildren) {
2277
+ if (!member || member.type !== "method_signature") continue;
2278
+ const fsig = member.namedChildren.find((k) => k && k.type === "function_signature");
2279
+ if (!fsig) continue;
2280
+ if (fsig.childForFieldName("name")?.text !== "build") continue;
2281
+ const first = fsig.namedChild(0);
2282
+ const declared = first && (first.type === "type_identifier" || first.type === "type") ? first.text : "";
2283
+ if (declared !== "Widget") continue;
2284
+ let body = member.nextNamedSibling;
2285
+ while (body && body.type !== "function_body" && body.type !== "method_signature") {
2286
+ body = body.nextNamedSibling;
2287
+ }
2288
+ if (!body || body.type !== "function_body") continue;
2289
+ const block = body.namedChildren.find((k) => k && k.type === "block");
2290
+ const ret = (block ?? body).namedChildren.find((k) => k && k.type === "return_statement");
2291
+ const expr = ret ? ret.namedChild(0) : body.namedChildren.find((k) => k && k.type !== "comment") ?? null;
2292
+ const target = this.renderedComponentName(expr);
2293
+ if (!target) continue;
2294
+ relationships.push({
2295
+ source: className,
2296
+ target,
2297
+ type: ASTEngine.RelationshipType.Renders,
2298
+ confidence: 0.9,
2299
+ line_number: this.nodeLine(expr),
2300
+ metadata: { isRenderHost: true, renderHost: "build", renderTarget: target }
2301
+ });
2302
+ }
2303
+ }
2304
+ /**
2305
+ * THE NAME OF THE COMPONENT A CONSTRUCTION EXPRESSION NAMES — dart spells a construction three
2306
+ * ways and all three name the type: `const T()` is a `const_object_expression`, `new T()` is a
2307
+ * `new_expression`, and the modern `T()` is a BARE `identifier` FOLLOWED BY A `selector` (the
2308
+ * call). A bare identifier with NO selector is a VARIABLE, not a construction — that distinction
2309
+ * is the refusal, and it is why the selector is checked rather than the identifier alone.
2310
+ */
2311
+ renderedComponentName(expr) {
2312
+ if (!expr) return null;
2313
+ let name = null;
2314
+ if (expr.type === "const_object_expression" || expr.type === "new_expression") {
2315
+ const t = expr.childForFieldName("type") ?? expr.namedChildren.find((k) => k && (k.type === "type_identifier" || k.type === "identifier"));
2316
+ name = t ? t.text : null;
2317
+ } else if (expr.type === "identifier") {
2318
+ const sib = expr.nextNamedSibling;
2319
+ name = sib && sib.type === "selector" ? expr.text : null;
2320
+ }
2321
+ if (!name) return null;
2322
+ const first = name[0];
2323
+ if (!first || first === first.toLowerCase()) return null;
2324
+ return name;
2325
+ }
2326
+ // ============================================================================
2327
+ // EVIDENCE TIERS
2328
+ // ============================================================================
2329
+ /**
2330
+ * Evidence-tier classification (family contract — mirrors csharp/java/rust;
2331
+ * M14 section D requires >=2 distinct evidenceClass values). Same rules as
2332
+ * the managed-OO family: structural (grammar clauses, conf 1.0) |
2333
+ * usage_contextual (calls, mixin uses) | import_based (import/export/part
2334
+ * directives) | inferred (confidence < 1 edges).
2335
+ */
2336
+ classifyEvidenceTier(relationships) {
2337
+ for (const r of relationships) {
2338
+ const type = r.type ?? r.relationshipType ?? "";
2339
+ const md = r.metadata ?? {};
2340
+ if (type === "contains") {
2341
+ md.evidenceClass = "structural";
2342
+ continue;
2343
+ }
2344
+ if (type === "imports" || type === "exports") {
2345
+ md.evidenceClass = "import_based";
2346
+ continue;
2347
+ }
2348
+ if (type === "part_of") {
2349
+ md.evidenceClass = "import_based";
2350
+ continue;
2351
+ }
2352
+ if (type === "mixes_in") {
2353
+ md.evidenceClass = "structural";
2354
+ continue;
2355
+ }
2356
+ if (type === "calls" || type === "uses") {
2357
+ md.evidenceClass = (r.confidence ?? 0) >= 0.85 ? "usage_contextual" : "inferred";
2358
+ continue;
2359
+ }
2360
+ if (type === "instantiates" || type === "extends" || type === "implements") {
2361
+ md.evidenceClass = (r.confidence ?? 0) >= 0.9 ? "structural" : "inferred";
2362
+ continue;
2363
+ }
2364
+ if (type === "overrides") {
2365
+ md.evidenceClass = "structural";
2366
+ continue;
2367
+ }
2368
+ if (type === "renders") {
2369
+ md.evidenceClass = "usage_contextual";
2370
+ continue;
2371
+ }
2372
+ if (type === "references") {
2373
+ md.evidenceClass = (r.confidence ?? 0) >= 0.85 ? "usage_contextual" : "inferred";
2374
+ continue;
2375
+ }
2376
+ md.evidenceClass = "usage_contextual";
2377
+ }
2378
+ }
2379
+ // ============================================================================
2380
+ // HELPERS
2381
+ // ============================================================================
2382
+ /**
2383
+ * The library this file declares: its name, the node that declared it, and which of
2384
+ * the three directive forms it was.
2385
+ *
2386
+ * `library X;` names the library X. No `library` directive means the file is an
2387
+ * implicit library named by its stem (Dart spec). `library;` — legal, and 101 of a
2388
+ * 3,000-file sample — declares a library with NO name, so the stem there is this
2389
+ * analyzer's convention and the caller records it as `kind: "unnamed"` rather than
2390
+ * letting a consumer read it as a declared fact.
2391
+ *
2392
+ * The declared name is read from the parse tree, never from a regex over the text,
2393
+ * because it is frequently DOTTED: `_fe_analyzer_shared.scanner.characters`, which a
2394
+ * first-token match truncates to `_fe_analyzer_shared` — a name no other file imports.
2395
+ * The corpus measurement that established the three frequencies and the `annotation`
2396
+ * case is `validation/m14/fixtures/dart/library-directive-census.ts`; the
2397
+ * `annotation` case is why the child must pass an IDENTIFIER shape test rather than
2398
+ * being taken as text.
2399
+ *
2400
+ * Only a DIRECT child of `program` is considered: a `library_name` anywhere else in
2401
+ * the tree is a declaration inside a part or a raw string (52 of 3,000 sampled files
2402
+ * carry `library X;` inside a `'''...'''` test fixture, which is not a directive at
2403
+ * all), not this file's library. Measured: no sampled file has more than one
2404
+ * top-level `library_name`, so "first valid" is not a choice between two candidates.
2405
+ */
2406
+ libraryName(rootNode, filePath) {
2407
+ const stem = path.basename(filePath, path.extname(filePath));
2408
+ for (let i = 0; i < rootNode.namedChildCount; i++) {
2409
+ const child = rootNode.namedChild(i);
2410
+ if (!child || child.type !== "library_name") continue;
2411
+ if (child.namedChildCount === 0) return { name: stem, directive: child, kind: "unnamed" };
2412
+ for (let j = 0; j < child.namedChildCount; j++) {
2413
+ const nameNode = child.namedChild(j);
2414
+ if (!nameNode) continue;
2415
+ const declared = stripWhitespace(nameNode.text);
2416
+ if (declared.length > 0 && isDottedIdentifier(declared)) {
2417
+ return { name: declared, directive: child, kind: "named" };
2418
+ }
2419
+ }
2420
+ }
2421
+ return { name: stem, directive: null, kind: "implicit" };
2422
+ }
2423
+ isDartGlobalBuiltin(name) {
2424
+ if (name === "print" || name === "assert" || name === "identical") return true;
2425
+ if (name === "toString" || name === "hashCode" || name === "runtimeType" || name === "noSuchMethod") return true;
2426
+ return false;
2427
+ }
2428
+ selectorIsBareCall(selector) {
2429
+ const prev = selector.previousNamedSibling;
2430
+ if (!prev) return false;
2431
+ return prev.type === "identifier" || prev.type === "type_identifier";
2432
+ }
2433
+ resolveSelectorCallee(selector) {
2434
+ return this.resolveSelectorCalleeNode(selector)?.text ?? null;
2435
+ }
2436
+ /** Node form of `resolveSelectorCallee` — the CALLEE identifier is the node
2437
+ * that names a call/instantiates edge (M60 line contract). */
2438
+ resolveSelectorCalleeNode(selector) {
2439
+ const prev = selector.previousNamedSibling;
2440
+ if (!prev) return null;
2441
+ if (prev.type === "unconditional_assignable_selector" || prev.type === "conditional_assignable_selector" || prev.type === "selector") {
2442
+ const inner = this.findChildByType(prev, "identifier");
2443
+ if (inner) return inner;
2444
+ return this.lastIdentifierNode(prev);
2445
+ }
2446
+ if (prev.type === "identifier" || prev.type === "type_identifier") {
2447
+ return prev;
2448
+ }
2449
+ return null;
2450
+ }
2451
+ lastIdentifier(node) {
2452
+ return this.lastIdentifierNode(node)?.text ?? null;
2453
+ }
2454
+ /**
2455
+ * Node form of `lastIdentifier`: the identifier that NAMES a cascade link's
2456
+ * edge is also the node whose line the edge carries (M60 line contract), so
2457
+ * both views come from ONE traversal and cannot disagree.
2458
+ */
2459
+ lastIdentifierNode(node) {
2460
+ let result = null;
2461
+ const stack = [node];
2462
+ while (stack.length > 0) {
2463
+ const n = stack.pop();
2464
+ if (n.type === "identifier" || n.type === "type_identifier") result = n;
2465
+ for (let i = 0; i < n.namedChildCount; i++) {
2466
+ const c = n.namedChild(i);
2467
+ if (c) stack.push(c);
2468
+ }
2469
+ }
2470
+ return result;
2471
+ }
2472
+ /**
2473
+ * DT-A/DT-F: the member identifier of a link selector
2474
+ * (`unconditional_assignable_selector` / `conditional_assignable_selector`
2475
+ * child holding `method` in `obj.method`, `?.value`, `..length`).
2476
+ */
2477
+ linkMember(sel) {
2478
+ return this.linkMemberNode(sel)?.text ?? null;
2479
+ }
2480
+ /** Node form of `linkMember`: the member identifier itself is the node that
2481
+ * NAMES a member-read/tear-off edge (M60 line contract). */
2482
+ linkMemberNode(sel) {
2483
+ const probe = sel.type === "selector" ? this.namedChildrenOf(sel) : [sel];
2484
+ for (const c of probe) {
2485
+ if (c.type === "unconditional_assignable_selector" || c.type === "conditional_assignable_selector" || c.type === "cascade_selector") {
2486
+ const id = this.findChildByType(c, "identifier") ?? this.findChildByType(c, "type_identifier");
2487
+ if (id) return id;
2488
+ }
2489
+ }
2490
+ return null;
2491
+ }
2492
+ namedChildrenOf(n) {
2493
+ const out = [];
2494
+ for (let i = 0; i < n.namedChildCount; i++) {
2495
+ const c = n.namedChild(i);
2496
+ if (c) out.push(c);
2497
+ }
2498
+ return out;
2499
+ }
2500
+ /**
2501
+ * DT-A: receiver chain of an invocation, walking back through
2502
+ * link-selector siblings from the argument-carrying selector.
2503
+ * `P.Foo.bar()` -> head `P`, links [`Foo`, `bar`]; bare `foo()` ->
2504
+ * head `foo`, no links.
2505
+ */
2506
+ invocationChain(argsSelector) {
2507
+ const links = [];
2508
+ let c = argsSelector.previousNamedSibling;
2509
+ while (c) {
2510
+ if (c.type === "selector") {
2511
+ const m = this.linkMember(c);
2512
+ if (!m) break;
2513
+ links.unshift(m);
2514
+ c = c.previousNamedSibling;
2515
+ } else if (c.type === "unconditional_assignable_selector" || c.type === "conditional_assignable_selector") {
2516
+ const id = this.findChildByType(c, "identifier") ?? this.findChildByType(c, "type_identifier");
2517
+ if (!id) break;
2518
+ links.unshift(id.text);
2519
+ c = c.previousNamedSibling;
2520
+ } else break;
2521
+ }
2522
+ return { head: c, links };
2523
+ }
2524
+ headNameText(head) {
2525
+ if (head && (head.type === "identifier" || head.type === "type_identifier")) return head.text;
2526
+ return null;
2527
+ }
2528
+ /**
2529
+ * DT-F: true when the selector sits in an assignment LHS write position
2530
+ * with a plain `=` operator (`a.b = c` writes b; `a.b += c` and `a.b ??= c`
2531
+ * read it). Span-matched like the C analyzer's LHS gate.
2532
+ */
2533
+ selectorIsLhsWrite(sel) {
2534
+ let p = sel.parent;
2535
+ let guard = 0;
2536
+ while (p && guard++ < 16) {
2537
+ if (p.type === "assignment_expression") {
2538
+ let opText = null;
2539
+ for (let i = 0; i < p.childCount; i++) {
2540
+ const c = p.child(i);
2541
+ if (c && !c.isNamed && c.text.includes("=")) {
2542
+ opText = c.text;
2543
+ break;
2544
+ }
2545
+ }
2546
+ if (opText !== "=") return false;
2547
+ const lhs = p.namedChild(0);
2548
+ if (lhs && lhs.startIndex <= sel.startIndex && lhs.endIndex >= sel.endIndex) return true;
2549
+ return false;
2550
+ }
2551
+ if (p.type === "function_body" || p.type === "block" || p.type === "class_body" || p.type === "program" || p.type === "declaration") return false;
2552
+ p = p.parent;
2553
+ }
2554
+ return false;
2555
+ }
2556
+ /** DT-F: declaration-name positions whose identifiers are binds, never reads. */
2557
+ isDartDeclNameParent(ptype) {
2558
+ return ptype === "formal_parameter" || ptype === "constructor_param" || ptype === "function_signature" || ptype === "method_signature" || ptype === "getter_signature" || ptype === "setter_signature" || ptype === "type_alias" || ptype === "label" || // #752: field / static-field declaration names (`int count = 0;`,
2559
+ // `static final int x = 1;`) — the declaration is a BIND, not a read.
2560
+ ptype === "initialized_identifier" || ptype === "static_final_declaration" || ptype === "class_definition" || ptype === "enum_declaration" || ptype === "mixin_declaration" || ptype === "extension_declaration" || // M60 (#933 triage): `enum_declaration` covered the enum's OWN name but
2561
+ // not each VALUE's node, so every enum constant was published as a bare
2562
+ // read of its own enum (`uses Shape -> circle` at the declaration line,
2563
+ // confidence 0.6). The M12 harness files sub-threshold facts as
2564
+ // `unknown`, never as false positives, which is how it survived the
2565
+ // lane's whole fixture and mutant program — probe-verified node shape:
2566
+ // `(enum_body (enum_constant (identifier)))`. The constant's ARGUMENTS
2567
+ // are outside this gate and stay ordinary reads.
2568
+ ptype === "enum_constant" || // DARTD21: an extension type's container name AND its representation
2569
+ // field name (`extension type X._(T object$)`) are declarations.
2570
+ ptype === "extension_type_declaration" || ptype === "representation_declaration" || // DARTD22: a signature's OWN name binds, and the constructor-shaped
2571
+ // signatures were the one family missing from this set. A constructor
2572
+ // `Real()` and an UNTYPED METHOD `untypedMethod()` parse into the same
2573
+ // node, and in both the leading `identifier` is the declared name — so
2574
+ // without this row every one of them published a bare read of ITSELF
2575
+ // (`uses untypedMethod -> untypedMethod` at 0.6). Before DARTD22 the same
2576
+ // node was published as a member called `constructor`, so the read was
2577
+ // invisible: the source and the target were two different wrong names and
2578
+ // cancelled out. Fixing only the symbol left the self-loop visible, which
2579
+ // is why the row belongs here and not in the constructor dispatch.
2580
+ ptype === "constructor_signature" || ptype === "factory_constructor_signature" || ptype === "constant_constructor_signature" || ptype === "redirecting_factory_constructor_signature";
2581
+ }
2582
+ /**
2583
+ * DT-F+ (#752): the names a function scope binds locally — its own
2584
+ * declarators plus formal / constructor / catch parameters. Used by the
2585
+ * bare-member-read gate so a name the CURRENT FUNCTION binds suppresses the
2586
+ * member read (CGD15 parity: a local is not a member, #299 — a false edge is
2587
+ * worse than a missing one). O(subtree), no regex.
2588
+ */
2589
+ /**
2590
+ * THE DECLARED TYPE OF EVERY LOCAL A SUBTREE BINDS (`collectDartLocals`' twin).
2591
+ *
2592
+ * The NAME set answers "is this a local", which is what suppresses a false member read.
2593
+ * It cannot answer "what IS this local", which is what makes a call target determinate:
2594
+ * `r.getOrders()` with `void run(OrderRepository r)` names `OrderRepository.getOrders`, and
2595
+ * without the type the edge can only say `getOrders` — the bare-name population the
2596
+ * incoming gate counts as ambiguous whenever any other declaration in the PROJECT shares
2597
+ * the name.
2598
+ *
2599
+ * Two shapes, and both are read off the GRAMMAR rather than guessed from text:
2600
+ *
2601
+ * local_variable_declaration the declared type is a direct child (`extractFieldType`,
2602
+ * the same reader fields use — `Baz y = Baz();`). It is
2603
+ * `undefined` for `var x = Widget();`, so the fallback below
2604
+ * is not a convenience: it is the only reading available.
2605
+ * initialized_variable_definition
2606
+ * no declared type, so the INITIALIZER names it when it is a
2607
+ * construction (`Widget()`), which is a fact about the source
2608
+ * and not an inference about the program.
2609
+ *
2610
+ * `Widget? w` is deliberately NOT reduced (`extractFieldType` does not know the nullable
2611
+ * wrapper) and a generic type is refused by `plainTypeName` — a receiver typed `List<int>`
2612
+ * resolves through a type argument this pass has not read, and naming the wrong declaration
2613
+ * is worse than naming none.
2614
+ */
2615
+ collectDartLocalTypes(root) {
2616
+ const out = /* @__PURE__ */ new Map();
2617
+ const stack = [root];
2618
+ while (stack.length > 0) {
2619
+ const n = stack.pop();
2620
+ const t = n.type;
2621
+ if (t === "for_loop_parts") {
2622
+ const nameNode = n.childForFieldName?.("name");
2623
+ const iterable = n.childForFieldName?.("value");
2624
+ const inferred = (n.namedChildren ?? []).some((c) => c?.type === "inferred_type");
2625
+ if (nameNode && iterable && !inferred) {
2626
+ const nameIdx = (n.namedChildren ?? []).findIndex(
2627
+ (c) => c && c.startIndex === nameNode.startIndex && c.endIndex === nameNode.endIndex
2628
+ );
2629
+ for (let i = 0; i < nameIdx; i++) {
2630
+ const c = n.namedChild(i);
2631
+ if (c && c.type === "type_identifier") {
2632
+ const plain = stripNullableSuffix(c.text);
2633
+ if (isPlainIdentifier(plain)) out.set(nameNode.text, { type: plain, evidence: "annotation" });
2634
+ break;
2635
+ }
2636
+ }
2637
+ }
2638
+ }
2639
+ if (t === "catch_clause") {
2640
+ const prev = n.previousNamedSibling;
2641
+ const typeNode = prev && (prev.type === "type_identifier" || prev.type === "nullable_type") ? prev : void 0;
2642
+ const typeLeaf = typeNode ? typeNode.type === "type_identifier" ? typeNode.text : this.findChildByType(typeNode, "type_identifier")?.text : void 0;
2643
+ const typeName = typeLeaf ? stripNullableSuffix(String(typeLeaf)) : void 0;
2644
+ const params = this.findChildByType(n, "catch_parameters");
2645
+ if (typeName && isPlainIdentifier(typeName) && params) {
2646
+ for (const c of params.namedChildren ?? []) {
2647
+ if (c && c.type === "identifier") out.set(c.text, { type: typeName, evidence: "annotation" });
2648
+ }
2649
+ }
2650
+ }
2651
+ if (t === "local_variable_declaration" || t === "declaration" || t === "initialized_variable_definition") {
2652
+ const name = n.childForFieldName?.("name")?.text ?? this.findChildByType(n, "initialized_variable_definition")?.childForFieldName?.("name")?.text;
2653
+ if (name && !out.has(name)) {
2654
+ const explicit = this.extractFieldType(n, n);
2655
+ const typeName = explicit ? stripNullableSuffix(String(explicit)) : this.constructedTypeName(n);
2656
+ if (typeName && isPlainIdentifier(typeName)) {
2657
+ let element;
2658
+ const args = this.findChildByType(n, "type_arguments");
2659
+ let keyElement;
2660
+ if (args) {
2661
+ const argsIds = [];
2662
+ const argStack = [args];
2663
+ while (argStack.length > 0) {
2664
+ const a = argStack.pop();
2665
+ if (a.type === "type_identifier") {
2666
+ argsIds.push(a.text);
2667
+ continue;
2668
+ }
2669
+ for (const c of a.namedChildren ?? []) if (c) argStack.push(c);
2670
+ }
2671
+ if (_RealDartAnalyzer.DART_ELEMENT_CONTAINERS.has(typeName)) element = argsIds[0];
2672
+ else if (_RealDartAnalyzer.DART_MAP_CONTAINERS.has(typeName)) {
2673
+ keyElement = argsIds[0];
2674
+ element = argsIds[1];
2675
+ }
2676
+ }
2677
+ out.set(name, { type: typeName, evidence: explicit ? "annotation" : "assignment", ...element ? { element } : {}, ...keyElement ? { keyElement } : {} });
2678
+ }
2679
+ }
2680
+ }
2681
+ for (const c of n.namedChildren ?? []) if (c) stack.push(c);
2682
+ }
2683
+ const awaits = [];
2684
+ const awaitStack = [root];
2685
+ while (awaitStack.length > 0) {
2686
+ const n = awaitStack.pop();
2687
+ if (n.type === "initialized_variable_definition") {
2688
+ const nameNode = n.childForFieldName?.("name");
2689
+ const value = n.childForFieldName?.("value");
2690
+ if (nameNode && value && value.type === "await_expression") {
2691
+ const operand = this.findChildByType(value, "identifier");
2692
+ if (operand) awaits.push({ name: nameNode.text, operand: operand.text });
2693
+ }
2694
+ }
2695
+ for (const c of n.namedChildren ?? []) if (c) awaitStack.push(c);
2696
+ }
2697
+ for (let round = 0; round < 3 && awaits.length > 0; round++) {
2698
+ let changed = false;
2699
+ for (const a of awaits) {
2700
+ if (out.has(a.name)) continue;
2701
+ const fut = out.get(a.operand);
2702
+ if (!fut || !fut.element) continue;
2703
+ if (!_RealDartAnalyzer.DART_ELEMENT_CONTAINERS.has(fut.type)) continue;
2704
+ out.set(a.name, { type: fut.element, evidence: "annotation" });
2705
+ changed = true;
2706
+ }
2707
+ if (!changed) break;
2708
+ }
2709
+ return out;
2710
+ }
2711
+ /**
2712
+ * AWAITED LOCALS: `final repo = await pending;` where the awaited variable's type is written as
2713
+ * `Future<Repo>` — the awaited VALUE's type is the future's type argument (written evidence, the
2714
+ * `annotation` class). Returns the declarations to type; the caller applies them to the body scope
2715
+ * so SIBLING statements see them. Grammar note: `await pending` parses as
2716
+ * `unary_expression > await_expression > identifier` (probed), not as a bare await_expression.
2717
+ */
2718
+ collectAwaitLocalTypes(root, localElements, localTypes) {
2719
+ const out = /* @__PURE__ */ new Map();
2720
+ const decls = [];
2721
+ const stack = [root];
2722
+ while (stack.length > 0) {
2723
+ const n = stack.pop();
2724
+ if (n.type === "initialized_variable_definition") {
2725
+ const nameNode = n.childForFieldName?.("name");
2726
+ const valueRaw = n.childForFieldName?.("value");
2727
+ const awaitNode = valueRaw && valueRaw.type === "unary_expression" ? this.findChildByType(valueRaw, "await_expression") : valueRaw;
2728
+ if (nameNode && awaitNode && awaitNode.type === "await_expression") {
2729
+ const operand = this.findChildByType(awaitNode, "identifier");
2730
+ if (operand) decls.push({ name: nameNode.text, operand: operand.text });
2731
+ }
2732
+ }
2733
+ for (const c of n.namedChildren ?? []) if (c) stack.push(c);
2734
+ }
2735
+ for (let round = 0; round < 3 && decls.length > 0; round++) {
2736
+ let changed = false;
2737
+ for (const d of decls) {
2738
+ if (out.has(d.name) || localTypes.has(d.name)) continue;
2739
+ const fut = localTypes.get(d.operand) ?? "";
2740
+ const element = localElements.get(d.operand);
2741
+ if (!element) continue;
2742
+ if (fut !== "Future" && fut !== "FutureOr") continue;
2743
+ out.set(d.name, element);
2744
+ changed = true;
2745
+ }
2746
+ if (!changed) break;
2747
+ }
2748
+ return out;
2749
+ }
2750
+ /** The type a variable's INITIALIZER constructs: `var x = Widget()` -> `Widget`.
2751
+ * `new`/`const` expressions and a bare capitalized callee are all constructions in
2752
+ * Dart, and a lowercase callee is a function call whose RESULT type this pass has not
2753
+ * read — so it returns undefined rather than the callee's name. */
2754
+ constructedTypeName(n) {
2755
+ const value = n.childForFieldName?.("value");
2756
+ if (!value) return void 0;
2757
+ const head = value.type === "constructor_invocation" || value.type === "new_expression" || value.type === "const_object_expression" ? this.findChildByType(value, "type_identifier") : value.type === "identifier" || value.type === "type_identifier" ? value : null;
2758
+ const text = head?.text;
2759
+ if (!text || !isCapitalized(text)) return void 0;
2760
+ return text;
2761
+ }
2762
+ /**
2763
+ * PARAMETER DECLARED TYPES, from the signature node the body follows.
2764
+ *
2765
+ * `extractParameters` is the reader the symbol payload already uses (and its fixture
2766
+ * asserts), so a parameter is typed here exactly as it is typed on the symbol — one
2767
+ * reader, two consumers. A parameter whose type is not a plain identifier contributes
2768
+ * nothing, which is the conservative half of the rule.
2769
+ */
2770
+ /** Container types whose ELEMENT type this lane can read from a written type argument. */
2771
+ static DART_ELEMENT_CONTAINERS = /* @__PURE__ */ new Set([
2772
+ "List",
2773
+ "Iterable",
2774
+ "Set",
2775
+ "Queue",
2776
+ "Stream",
2777
+ "Iterator",
2778
+ "ListQueue",
2779
+ "DoubleLinkedQueue",
2780
+ // `Future<T>`: the AWAITED value is T, and `await f` is written evidence of exactly the same
2781
+ // class as a declared local — it types the receiver of the statements after the await.
2782
+ "Future",
2783
+ "FutureOr"
2784
+ ]);
2785
+ /** Map types whose KEY/VALUE type arguments this lane can read (value = `element`, key = `keyElement`). */
2786
+ static DART_MAP_CONTAINERS = /* @__PURE__ */ new Set([
2787
+ "Map",
2788
+ "HashMap",
2789
+ "LinkedHashMap",
2790
+ "SplayTreeMap",
2791
+ "UnmodifiableMapView"
2792
+ ]);
2793
+ collectDartParameterTypes(sig) {
2794
+ const out = /* @__PURE__ */ new Map();
2795
+ let params = [];
2796
+ try {
2797
+ params = this.extractParameters(sig) ?? [];
2798
+ } catch {
2799
+ return out;
2800
+ }
2801
+ for (const p of params) {
2802
+ const name = String(p?.name ?? "");
2803
+ const typeName = stripNullableSuffix(String(p?.type ?? ""));
2804
+ if (name && isPlainIdentifier(typeName)) out.set(name, { type: typeName, evidence: "param" });
2805
+ }
2806
+ const stack = [sig];
2807
+ while (stack.length > 0) {
2808
+ const n = stack.pop();
2809
+ if (n.type === "formal_parameter" || n.type === "constructor_param" || n.type === "super_formal_parameter") {
2810
+ const nameNode = n.childForFieldName?.("name") ?? this.findChildByType(n, "identifier");
2811
+ const typeNode = this.findChildByType(n, "type_identifier");
2812
+ const args = this.findChildByType(n, "type_arguments");
2813
+ const argsIds = args ? (args.namedChildren ?? []).filter((c) => c && c.type === "type_identifier") : [];
2814
+ const base = typeNode ? stripNullableSuffix(typeNode.text) : "";
2815
+ if (nameNode && argsIds.length > 0 && _RealDartAnalyzer.DART_ELEMENT_CONTAINERS.has(base)) {
2816
+ const prev = out.get(nameNode.text);
2817
+ if (prev) prev.element = argsIds[0].text;
2818
+ else out.set(nameNode.text, { type: base, evidence: "param", element: argsIds[0].text });
2819
+ } else if (nameNode && argsIds.length >= 2 && _RealDartAnalyzer.DART_MAP_CONTAINERS.has(base)) {
2820
+ const prev = out.get(nameNode.text);
2821
+ if (prev) {
2822
+ prev.keyElement = argsIds[0].text;
2823
+ prev.element = argsIds[1].text;
2824
+ } else out.set(nameNode.text, { type: base, evidence: "param", keyElement: argsIds[0].text, element: argsIds[1].text });
2825
+ }
2826
+ continue;
2827
+ }
2828
+ for (const c of n.namedChildren ?? []) if (c) stack.push(c);
2829
+ }
2830
+ return out;
2831
+ }
2832
+ /** The import aliases this file declares (`import 'x.dart' as _i1;`). Only the identifier that
2833
+ * is a DIRECT child of `import_specification` is an alias — the specifier's own identifiers sit
2834
+ * inside `configurable_uri`/`uri`, which is why the walk stops at those nodes. */
2835
+ collectImportAliases(root) {
2836
+ const out = /* @__PURE__ */ new Set();
2837
+ const stack = [root];
2838
+ while (stack.length > 0) {
2839
+ const n = stack.pop();
2840
+ if (n.type === "import_specification") {
2841
+ for (const c of n.namedChildren ?? []) {
2842
+ if (c && c.type === "identifier") out.add(c.text);
2843
+ }
2844
+ continue;
2845
+ }
2846
+ if (n.type === "uri" || n.type === "configurable_uri" || n.type === "string_literal") continue;
2847
+ for (const c of n.namedChildren ?? []) if (c) stack.push(c);
2848
+ }
2849
+ return out;
2850
+ }
2851
+ collectDartLocals(root) {
2852
+ const out = /* @__PURE__ */ new Set();
2853
+ const stack = [root];
2854
+ while (stack.length > 0) {
2855
+ const n = stack.pop();
2856
+ if (n.type === "identifier" && this.isDartDeclaredName(n)) out.add(n.text);
2857
+ if (n.type === "formal_parameter" || n.type === "constructor_param" || n.type === "super_formal_parameter" || n.type === "catch_parameters") {
2858
+ const id = this.findChildByType(n, "identifier");
2859
+ if (id) out.add(id.text);
2860
+ }
2861
+ if (n.type === "for_loop_parts") {
2862
+ for (let i = 0; i < n.childCount; i++) {
2863
+ const c = n.child(i);
2864
+ if (c && !c.isNamed && c.text === "in") {
2865
+ for (let j = 0; j < i; j++) {
2866
+ const pc = n.child(j);
2867
+ if (pc && pc.isNamed && pc.type === "identifier") out.add(pc.text);
2868
+ }
2869
+ break;
2870
+ }
2871
+ }
2872
+ }
2873
+ if (n.type === "variable_pattern") {
2874
+ const id = this.deepIdentifier(n);
2875
+ if (id) out.add(id.text);
2876
+ }
2877
+ if (n.type === "rest_pattern") {
2878
+ const id = this.deepIdentifier(n);
2879
+ if (id) out.add(id.text);
2880
+ }
2881
+ if (n.type === "constant_pattern" && this.isBindingPatternContext(n)) {
2882
+ const id = this.deepIdentifier(n);
2883
+ if (id) out.add(id.text);
2884
+ }
2885
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
2886
+ const c = n.namedChild(i);
2887
+ if (c) stack.push(c);
2888
+ }
2889
+ }
2890
+ return out;
2891
+ }
2892
+ /**
2893
+ * DT2: first document-order `identifier` in a subtree (pattern contents
2894
+ * nest — direct-child lookup misses `var (a, b)`-style bindings).
2895
+ */
2896
+ deepIdentifier(n) {
2897
+ const stack = [n];
2898
+ while (stack.length > 0) {
2899
+ const c = stack.pop();
2900
+ if (c.type === "identifier") return c;
2901
+ for (let i = c.namedChildCount - 1; i >= 0; i--) {
2902
+ const ch = c.namedChild(i);
2903
+ if (ch) stack.push(ch);
2904
+ }
2905
+ }
2906
+ return null;
2907
+ }
2908
+ /**
2909
+ * DARTGAP2: is this node a CONSTANT being TESTED by a pattern — i.e. a position whose
2910
+ * owed relationship is a `uses` read of the constant, not a declaration and not a binding?
2911
+ *
2912
+ * The qualified form (`case M.off:`) is already read by the `qualified`-under-`constant_pattern`
2913
+ * branch (3d4d7fa4). This predicate covers the BARE identifier and the RELATIONAL operand.
2914
+ * The shapes are not inferred from the Dart spec — they were probed against the PINNED grammar
2915
+ * (`validation/m14/fixtures/dart/pattern-forms-probe.ts`):
2916
+ *
2917
+ * case kOne: (switch_statement_case (case_builtin) (constant_pattern (identifier)))
2918
+ * if (x case kOne) (if_statement (identifier) (constant_pattern (identifier)))
2919
+ * if (x case kNested?) (if_statement (identifier) (null_check_pattern (constant_pattern (identifier))))
2920
+ * if (x case kOne!) (if_statement (identifier) (null_assert_pattern (constant_pattern (identifier))))
2921
+ * if (x case >= kOne) (if_statement (identifier) (relational_operator) (identifier))
2922
+ * — tree-sitter-dart has NO `relational_pattern` wrapper, so the
2923
+ * operand is a direct child of the `if_statement` whose previous
2924
+ * NAMED sibling is the operator; the same shape appears under
2925
+ * `switch_statement_case` (probed) and `switch_expression_case`.
2926
+ * case [kOne, 2] (list_pattern (constant_pattern (identifier)) (constant_pattern (decimal_integer_literal)))
2927
+ * case {'a': kOne} (map_pattern (string_literal) (constant_pattern (identifier)))
2928
+ * case (kOne, 2) (record_pattern (constant_pattern (identifier)) (constant_pattern (decimal_integer_literal)))
2929
+ *
2930
+ * THREE EXCLUSIONS, each measured rather than assumed:
2931
+ * * BINDING context. `var (int a, int b) = ...` parses its names as `variable_pattern`, so
2932
+ * they never reach this predicate — but a destructuring declaration can still hold a
2933
+ * `constant_pattern`, and `isBindingPatternContext` is the ONE rule that already decides
2934
+ * that question for the binding collector. Reused rather than re-spelled.
2935
+ * * `...rest` in `final [int f, ...rest] = ...` parses as
2936
+ * `(rest_pattern (constant_pattern (identifier)))` — probed. That identifier is the BINDING
2937
+ * being declared, not a constant being tested, so reading it would fabricate a use of the
2938
+ * very name being bound.
2939
+ * * the `_` wildcard parses as `constant_pattern (identifier "_")` in map patterns (probed:
2940
+ * `{'b': _}`). `_` is not a constant and no declaration is named by it.
2941
+ */
2942
+ isPatternTestConstant(n) {
2943
+ const id = n.type === "identifier" ? n : null;
2944
+ const p = n.parent;
2945
+ if (!p) return false;
2946
+ if (p.type === "constant_pattern") {
2947
+ if (id && id.text === "_") return false;
2948
+ if (p.parent?.type === "rest_pattern") return false;
2949
+ return !this.isBindingPatternContext(p);
2950
+ }
2951
+ if (p.type === "if_statement" || p.type === "switch_statement_case" || p.type === "switch_expression_case") {
2952
+ return n.previousNamedSibling?.type === "relational_operator";
2953
+ }
2954
+ return false;
2955
+ }
2956
+ /**
2957
+ * DT2: is this pattern node inside a BINDING context? Only structural
2958
+ * descent counts: a `var (a, b)` destructuring binds through record /
2959
+ * list / map wrappers to the declaration (or for-in head). A case TEST
2960
+ * (`InvitationStatus.declined => ...` inside `var x = switch ...`)
2961
+ * crosses a switch/if boundary — it tests, never binds, even when the
2962
+ * whole switch initializes a variable.
2963
+ */
2964
+ isBindingPatternContext(n) {
2965
+ let p = n.parent;
2966
+ let guard = 0;
2967
+ while (p && guard++ < 8) {
2968
+ const t = p.type;
2969
+ if (t === "pattern_variable_declaration" || t === "for_loop_parts") return true;
2970
+ if (t !== "record_pattern" && t !== "list_pattern" && t !== "map_pattern" && t !== "constant_pattern" && t !== "variable_pattern") return false;
2971
+ p = p.parent;
2972
+ }
2973
+ return false;
2974
+ }
2975
+ /**
2976
+ * THE DECLARATION A LOCAL READ BINDS TO — the innermost enclosing scope that declares the
2977
+ * name, and the identity that makes two reads of the same name DIFFERENT facts when they
2978
+ * read DIFFERENT declarations.
2979
+ *
2980
+ * WHY IT EXISTS. A local read's spelling is `<callable>\u0000<name>`, and the CALLABLE is the
2981
+ * enclosing FUNCTION — so two reads of two different declarations that share a name inside one
2982
+ * function spell IDENTICALLY. The relationship dedup key (`type|source|target|confidence`)
2983
+ * carried no line, so the two collapsed into one row and the second was LOST: a resolution
2984
+ * loss wearing a dedup's clothes.
2985
+ *
2986
+ * THE SCOPE RULE, measured on the dart lane's own `nested-block-shadows-member` fixture (its
2987
+ * line numbers are the fixture's):
2988
+ *
2989
+ * 44: int value = 0; // the FIELD
2990
+ * 47: var value = 1; // the BODY local
2991
+ * 48: print(value); // -> the body local
2992
+ * 50: var value = 2; // the NESTED declaration, shadowing the body local
2993
+ * 51: print(value); // -> the NESTED local
2994
+ * 53: print(value); // -> the BODY local again
2995
+ *
2996
+ * Dart's block scope: an inner declaration shadows an outer binding of the same name for the
2997
+ * whole of the inner block. So the read at 51 reads a DIFFERENT declaration from the reads at
2998
+ * 48 and 53 — and the read at 53 reads the SAME declaration as the read at 48, so those two
2999
+ * are ONE relationship and must still collapse.
3000
+ *
3001
+ * WHY A BLOCK'S OWN DECLARATIONS, not its whole subtree. The scope of a declaration is the
3002
+ * block that holds it, so the nested block's `value` is NOT a declaration of the outer block.
3003
+ * A subtree scan would find it and attribute the read at 53 to the nested declaration — which
3004
+ * is the same class of mistake as the analyzer's own `heritageByClass` comment records
3005
+ * ("a `uses` edge is evidence that a type is relevant to this file — but it is not a BASE").
3006
+ *
3007
+ * THE RESULT IS A SCOPE IDENTITY, not a line: the enclosing scope's own start position, plus
3008
+ * the declaration's own line. Two reads of the SAME declaration share it; two reads of
3009
+ * DIFFERENT declarations do not. It rides the row as `localBindingScope` and joins the dedup
3010
+ * key ONLY for a `localBinding` row — every other row keeps its key byte for byte, so the
3011
+ * 192,761-row payload-pollution dedup is untouched.
3012
+ */
3013
+ localBindingScope(n, name) {
3014
+ let p = n.parent;
3015
+ let guard = 0;
3016
+ while (p && guard++ < 64) {
3017
+ if (p.type === "block" || p.type === "function_body") {
3018
+ const decl = this.scopeOwnDeclaration(p, name, n.startIndex);
3019
+ if (decl !== null) {
3020
+ return { localBindingScope: `${p.startIndex}:${decl}` };
3021
+ }
3022
+ }
3023
+ p = p.parent;
3024
+ }
3025
+ return null;
3026
+ }
3027
+ // ══ THE SCOPE-DECLARATION INDEX — ONE WALK PER SCOPE, NOT ONE WALK PER READ.
3028
+ //
3029
+ // `scopeOwnDeclaration` used to walk the ENTIRE subtree of the enclosing scope for EVERY local
3030
+ // read. On ordinary files that is invisible; on a file of deeply nested statements it is fatal,
3031
+ // because each of the N reads walks a subtree that contains most of the file, so the pass is
3032
+ // O(N × subtree) — quadratic in DEPTH. Measured on `deep_nesting_statement_test.dart`
3033
+ // (350,435 B, 130,042 nodes): the raw tree walk costs 187 ms (0.0014 ms/node) while the analyzer
3034
+ // spent ~3.8 ms per node-visit, and this function is where that went.
3035
+ //
3036
+ // The fix is the same shape the gate's `addKey` fix had: PAY ONCE. A scope's own declarations are
3037
+ // a property of the SCOPE, so they are indexed on first use and every later read is a lookup.
3038
+ // The key is `${startIndex}:${endIndex}:${type}` and NOT the node object, because web-tree-sitter
3039
+ // hands out FRESH wrappers on every traversal — a memo keyed on the object would never hit and
3040
+ // would grow without bound. A memo that never hits is a leak with extra steps.
3041
+ //
3042
+ // The index is cleared at the top of `analyze()`: an analyzer instance outlives one file, and a
3043
+ // cache that outlived it would serve one file's declarations to the next file's reads.
3044
+ scopeDeclIndex = /* @__PURE__ */ new Map();
3045
+ /**
3046
+ * THE DECLARATION OF `name` THAT BELONGS TO THIS SCOPE — the block's OWN declarations, not
3047
+ * the ones in a nested block (a nested block is a DIFFERENT scope, and a declaration there
3048
+ * does not belong to this one). Returns the declaration's START INDEX, which is all the caller
3049
+ * needs (`<scopeStart>:<declStart>`), so no node is retained.
3050
+ */
3051
+ scopeOwnDeclaration(scope, name, before) {
3052
+ const key = `${scope.startIndex}:${scope.endIndex}:${scope.type}`;
3053
+ let index = this.scopeDeclIndex.get(key);
3054
+ if (!index) {
3055
+ index = /* @__PURE__ */ new Map();
3056
+ const walk = (n, depth) => {
3057
+ if (depth > 64) return;
3058
+ for (let i = 0; i < n.namedChildCount; i++) {
3059
+ const c = n.namedChild(i);
3060
+ if (!c) continue;
3061
+ if (c.type === "block" || c.type === "function_body" || c.type === "class_body") continue;
3062
+ if (c.type === "identifier" && this.isDartDeclaredName(c)) {
3063
+ const arr = index.get(c.text);
3064
+ if (arr) arr.push(c.startIndex);
3065
+ else index.set(c.text, [c.startIndex]);
3066
+ }
3067
+ walk(c, depth + 1);
3068
+ }
3069
+ };
3070
+ walk(scope, 0);
3071
+ this.scopeDeclIndex.set(key, index);
3072
+ }
3073
+ const starts = index.get(name);
3074
+ if (!starts || starts.length === 0) return null;
3075
+ let best = -1;
3076
+ for (const s of starts) if (s < before && s > best) best = s;
3077
+ return best < 0 ? null : best;
3078
+ }
3079
+ /**
3080
+ * DT-F: is this identifier the DECLARED name of its definition
3081
+ * (`var q = ...`, `int xs[3]`, `int xs[3] = ...`)? Names precede the
3082
+ * first `=` child; values follow it. Definitions without `=` bind every
3083
+ * identifier they directly hold.
3084
+ */
3085
+ isDartDeclaredName(n) {
3086
+ const parent = n.parent;
3087
+ if (!parent) return false;
3088
+ const ptype = parent.type;
3089
+ if (ptype !== "initialized_variable_definition" && ptype !== "static_final_declaration") return false;
3090
+ let eqIdx = -1;
3091
+ let myIdx = -1;
3092
+ for (let i = 0; i < parent.childCount; i++) {
3093
+ const c = parent.child(i);
3094
+ if (!c) continue;
3095
+ if (!c.isNamed && c.text === "=" && eqIdx === -1) eqIdx = i;
3096
+ if (c.isNamed && c.startIndex === n.startIndex && c.endIndex === n.endIndex) myIdx = i;
3097
+ }
3098
+ if (myIdx === -1) return false;
3099
+ if (eqIdx === -1) return true;
3100
+ return myIdx < eqIdx;
3101
+ }
3102
+ /** DT-F: is this selector an index operation (`xs[0]` parses the index
3103
+ * as a member-less selector holding brackets)? Member selectors name a
3104
+ * member; argument selectors invoke. Bounded shape scan, never regex. */
3105
+ isIndexSelector(sel) {
3106
+ if (this.linkMember(sel) !== null) return false;
3107
+ if (this.findChildByType(sel, "argument_part")) return false;
3108
+ return this.subtreeHasBracket(sel);
3109
+ }
3110
+ /** DT-F: is this assignable an index target (`xs[3] = 4` wraps the base
3111
+ * and index in assignable_expression)? Same member-less + bracket test
3112
+ * applied to the node itself. */
3113
+ isIndexAssignable(sel) {
3114
+ if (sel.type !== "unconditional_assignable_selector" && sel.type !== "conditional_assignable_selector") return false;
3115
+ if (this.findChildByType(sel, "identifier") ?? this.findChildByType(sel, "type_identifier")) return false;
3116
+ return this.subtreeHasBracket(sel);
3117
+ }
3118
+ subtreeHasBracket(sel) {
3119
+ const stack = [sel];
3120
+ let guard = 0;
3121
+ while (stack.length > 0 && guard++ < 32) {
3122
+ const c = stack.pop();
3123
+ if (!c.isNamed && (c.text === "[" || c.text === "?[")) return true;
3124
+ for (let i = 0; i < c.childCount; i++) {
3125
+ const ch = c.child(i);
3126
+ if (ch) stack.push(ch);
3127
+ }
3128
+ }
3129
+ return false;
3130
+ }
3131
+ /** DT-F: bounded ancestor climb — is this node inside string interpolation? */
3132
+ isInsideTemplateSubstitution(n) {
3133
+ let p = n.parent;
3134
+ let guard = 0;
3135
+ while (p && guard++ < 8) {
3136
+ if (p.type === "template_substitution") return true;
3137
+ if (p.type === "function_body" || p.type === "block" || p.type === "class_body" || p.type === "program" || p.type === "declaration") return false;
3138
+ p = p.parent;
3139
+ }
3140
+ return false;
3141
+ }
3142
+ isSignatureNode(type) {
3143
+ return type === "function_signature" || type === "method_signature" || type === "getter_signature" || type === "setter_signature" || type === "constructor_signature" || type === "factory_constructor_signature" || type === "constant_constructor_signature" || type === "redirecting_factory_constructor_signature" || type === "operator_signature";
3144
+ }
3145
+ isConstructorSignature(type) {
3146
+ return type === "constructor_signature" || type === "factory_constructor_signature" || type === "constant_constructor_signature" || type === "redirecting_factory_constructor_signature";
3147
+ }
3148
+ /**
3149
+ * Is this constructor-shaped signature actually a CONSTRUCTOR, or is it a
3150
+ * METHOD the grammar has no node for?
3151
+ *
3152
+ * ## The shape the grammar cannot distinguish
3153
+ *
3154
+ * `tree-sitter-dart` has no node for a method declared WITHOUT a return
3155
+ * type. `class A { noRetType() { } }` parses as
3156
+ *
3157
+ * method_signature
3158
+ * constructor_signature <- an UNTYPED METHOD
3159
+ * identifier noRetType
3160
+ * formal_parameter_list
3161
+ *
3162
+ * which is structurally identical to a real constructor `A()`. Reading the
3163
+ * node TYPE alone therefore publishes every untyped method in the corpus as a
3164
+ * `constructor` symbol named `constructor`, and the method's own name is never
3165
+ * published at all. MEASURED over a 600-file stride sample of the corpus at
3166
+ * 35,090 `.dart` files: 1,197 constructor-shaped signatures, of which 521
3167
+ * (43.5%) are untyped methods, spread over 56 files — and one file,
3168
+ * `dart-sdk/pkg/analyzer/test/src/summary/elements/type_inference_test.dart`,
3169
+ * holds 91 of them, all collapsed onto the single name
3170
+ * `TypeInferenceElementTest.constructor`. `validation/m14/fixtures/dart/
3171
+ * untyped-method-census.ts` is the census and the discriminator cross-tab.
3172
+ *
3173
+ * ## Why the PARENT is not the test
3174
+ *
3175
+ * "Is the inner signature's parent a `method_signature`?" looks like the
3176
+ * discriminator and is not: the grammar ALSO wraps real constructors that
3177
+ * way (`A.named(int a)`, `factory A.f() = A.named`), so it would misclassify
3178
+ * 559 real constructors as methods. The cross-tab in the census prints both.
3179
+ *
3180
+ * ## The test that IS exact — the language's own naming rule
3181
+ *
3182
+ * A constructor is named after its class, and the unnamed one is spelled
3183
+ * `.new`. A MEMBER may not share its class's name — that is a compile error —
3184
+ * so
3185
+ *
3186
+ * lead === ownerName || lead === "new"
3187
+ *
3188
+ * holds for every constructor and for no method. It is a property of the
3189
+ * declaration's own text, not of the grammar's node choice, so it survives a
3190
+ * grammar upgrade that re-shapes the wrapper.
3191
+ *
3192
+ * The census measures it as exact on both classes: 662 constructors and 535
3193
+ * untyped methods, with no cell of the contingency table requiring a choice.
3194
+ *
3195
+ * ## An ownerless signature is NOT decided here
3196
+ *
3197
+ * An `extension type` declares constructors too (`Ext1.name1(this i);`), and
3198
+ * `scopeOwnerName` resolves no enclosing type for one. Returning true there
3199
+ * would keep the current behaviour — a constructor published without an owner
3200
+ * — which is right, and is not this method's business to change. The 14
3201
+ * ownerless rows in the census are counted, not silently folded into either
3202
+ * class.
3203
+ */
3204
+ isRealConstructor(sig, owner) {
3205
+ if (!owner) return true;
3206
+ const lead = this.signatureName(sig);
3207
+ if (lead === null) return true;
3208
+ return lead === "new" || lead === owner;
3209
+ }
3210
+ /**
3211
+ * The name of the type a signature is declared in, read from the PARENT chain.
3212
+ *
3213
+ * The symbol pass has a `scopeStack` already carrying this, so it passes the
3214
+ * owner in. The relationship passes have no scope stack — they walk with
3215
+ * their own `current`/`owner` cursors — so they need it derived from the tree.
3216
+ * The two must agree or an edge's source and the symbol it names are decided
3217
+ * by different rules, which is how `resolveCurrent` came to publish the
3218
+ * literal `constructor` for every untyped method's body while the symbol pass
3219
+ * published the method under its own name.
3220
+ *
3221
+ * The owner resolution is the analyzer's, not the grammar's: `class_definition`
3222
+ * and `enum_declaration` carry a `name` FIELD, while `mixin_declaration` and
3223
+ * `extension_declaration` carry a direct `identifier` child. A lookup that read
3224
+ * only the name field would resolve no owner for a mixin and send every
3225
+ * untyped mixin method down the ownerless path.
3226
+ */
3227
+ enclosingTypeName(node) {
3228
+ let n = node;
3229
+ let guard = 0;
3230
+ while (n && guard++ < 64) {
3231
+ const t = n.type;
3232
+ if (t === "class_definition" || t === "enum_declaration" || t === "mixin_declaration" || t === "extension_declaration" || t === "extension_type_declaration") {
3233
+ const named = n.childForFieldName("name");
3234
+ if (named) return named.text;
3235
+ const id = this.findChildByType(n, "identifier");
3236
+ if (id) return id.text;
3237
+ return void 0;
3238
+ }
3239
+ n = n.parent;
3240
+ }
3241
+ return void 0;
3242
+ }
3243
+ signatureName(inner) {
3244
+ const nameNode = inner.childForFieldName("name");
3245
+ if (nameNode) return nameNode.text;
3246
+ if (inner.type === "operator_signature") {
3247
+ const opTok = this.operatorTokenNode(inner);
3248
+ return opTok ? `operator ${opTok.text}` : null;
3249
+ }
3250
+ const id = this.findChildByType(inner, "identifier");
3251
+ return id ? id.text : null;
3252
+ }
3253
+ /** The operator symbol token of an `operator_signature`.
3254
+ *
3255
+ * This locates the `operator` KEYWORD and takes the token after it. It must NOT
3256
+ * hardcode a child index: the return type in front of the keyword is not one
3257
+ * node but a variable number of them. A plain `bool` is a single
3258
+ * `type_identifier`, so `child(2)` is the operator token — but a SCOPED return
3259
+ * type (`core$_.bool`, `ns.bool`) is a `scoped_identifier` the grammar splits,
3260
+ * which shifts every later child by one.
3261
+ *
3262
+ * Measured: `extension ByteString$$Methods on ByteString { core$_.bool
3263
+ * operator >(ByteString byteString) { ... } }` in ok_http's JNI bindings, where
3264
+ * `child(2)` then names the member after the return type's last segment and
3265
+ * publishes `operator bool` — a member name that names a TYPE, on a class that
3266
+ * has no such member, while the real `operator >` went missing. Scanning for
3267
+ * the keyword is invariant to how the return type is spelled. */
3268
+ operatorTokenNode(inner) {
3269
+ for (let i = 0; i < inner.childCount; i++) {
3270
+ const c = inner.child(i);
3271
+ if (c && !c.isNamed && c.text === "operator") return inner.child(i + 1);
3272
+ }
3273
+ return null;
3274
+ }
3275
+ /** Node form of `signatureName`: the node that names a member edge is also
3276
+ * the node whose LINE the edge carries (M60 line contract). For an operator
3277
+ * overload the naming node is the operator TOKEN — its line, not its text,
3278
+ * is what the synthesised spelling contributes. */
3279
+ signatureNameNode(inner) {
3280
+ const nameNode = inner.childForFieldName("name");
3281
+ if (nameNode) return nameNode;
3282
+ if (inner.type === "operator_signature") {
3283
+ return this.operatorTokenNode(inner);
3284
+ }
3285
+ return this.findChildByType(inner, "identifier");
3286
+ }
3287
+ findSignatureIn(node) {
3288
+ const stack = [node];
3289
+ let guard = 0;
3290
+ while (stack.length > 0 && guard++ < 64) {
3291
+ const n = stack.pop();
3292
+ if (this.isSignatureNode(n.type)) return n;
3293
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
3294
+ const c = n.namedChild(i);
3295
+ if (c) stack.push(c);
3296
+ }
3297
+ }
3298
+ return null;
3299
+ }
3300
+ findInnerSignature(node) {
3301
+ if (node.type !== "method_signature") return node;
3302
+ const stack = [node];
3303
+ while (stack.length > 0) {
3304
+ const n = stack.pop();
3305
+ if (this.isSignatureNode(n.type) && n.type !== "method_signature") {
3306
+ return n;
3307
+ }
3308
+ for (let i = 0; i < n.namedChildCount; i++) {
3309
+ const c = n.namedChild(i);
3310
+ if (c) stack.push(c);
3311
+ }
3312
+ }
3313
+ return node;
3314
+ }
3315
+ hasBody(node) {
3316
+ const sib = node.nextNamedSibling;
3317
+ if (sib && (sib.type === "function_body" || sib.type === "block")) return true;
3318
+ return this.findChildByType(node, "function_body") !== null;
3319
+ }
3320
+ rangeIncludingBody(node, sig) {
3321
+ const range = this.nodeToRange(node);
3322
+ let sib = node.nextSibling;
3323
+ let guard = 0;
3324
+ while (sib && guard++ < 8) {
3325
+ if (sib.type === "function_body" || sib.type === "block") {
3326
+ range.endLine = sib.endPosition.row + 1;
3327
+ range.endColumn = sib.endPosition.column + 1;
3328
+ range.endIndex = sib.endIndex;
3329
+ break;
3330
+ }
3331
+ if (sib.type === "}" || sib.type === "class_body" || sib.type === "extension_body") break;
3332
+ sib = sib.nextSibling;
3333
+ }
3334
+ return range;
3335
+ }
3336
+ isAsyncSignature(node, sig) {
3337
+ const body = this.findChildByType(node, "function_body");
3338
+ if (body) {
3339
+ if (this.hasToken(body, "async")) return true;
3340
+ } else {
3341
+ const sib = node.nextSibling;
3342
+ if (sib && sib.type === "function_body" && this.hasToken(sib, "async")) return true;
3343
+ }
3344
+ return false;
3345
+ }
3346
+ hasToken(node, token) {
3347
+ for (let i = 0; i < node.childCount; i++) {
3348
+ const c = node.child(i);
3349
+ if (c && (c.type === token || c.text === token)) return true;
3350
+ }
3351
+ return false;
3352
+ }
3353
+ hasModifier(node, modifier) {
3354
+ for (let i = 0; i < node.childCount; i++) {
3355
+ const c = node.child(i);
3356
+ if (c && (c.type === modifier || c.text === modifier)) return true;
3357
+ }
3358
+ if (node.parent && node.parent.type === "declaration") {
3359
+ for (let i = 0; i < node.parent.childCount; i++) {
3360
+ const c = node.parent.child(i);
3361
+ if (c && (c.type === modifier || c.text === modifier)) return true;
3362
+ }
3363
+ }
3364
+ return false;
3365
+ }
3366
+ extractParameters(node) {
3367
+ const out = [];
3368
+ const paramsNode = node.type === "formal_parameter_list" ? node : this.findChildByType(node, "formal_parameter_list");
3369
+ if (!paramsNode) return out;
3370
+ const stack = [paramsNode];
3371
+ while (stack.length > 0) {
3372
+ const n = stack.pop();
3373
+ if (n.type === "formal_parameter" || n.type === "constructor_param" || n.type === "typed_identifier") {
3374
+ const nameNode = n.childForFieldName("name") || this.findChildByType(n, "identifier");
3375
+ if (nameNode && nameNode.text !== "this" && nameNode.text !== "super") {
3376
+ let typeText;
3377
+ if (n.type === "typed_identifier") {
3378
+ typeText = this.firstTypeIdentifier(n, false)?.text;
3379
+ } else {
3380
+ const kids = [];
3381
+ for (let i = 0; i < n.namedChildCount; i++) {
3382
+ const c = n.namedChild(i);
3383
+ if (c) kids.push(c);
3384
+ }
3385
+ const nameIdx = kids.findIndex(
3386
+ (c) => (
3387
+ // web-tree-sitter creates a NEW wrapper object per access, so
3388
+ // reference equality always fails (same trap extractReturnType's
3389
+ // KAD2 sweep fixed) — compare byte spans.
3390
+ nameNode && c.startIndex === nameNode.startIndex && c.endIndex === nameNode.endIndex
3391
+ )
3392
+ );
3393
+ for (let i = 0; i < nameIdx; i++) {
3394
+ const c = kids[i];
3395
+ if (c.type === "type_identifier" || c.type === "function_type" || c.type === "nullable_type" || c.type === "void_type" || c.type === "record_type") {
3396
+ typeText = c.text;
3397
+ break;
3398
+ }
3399
+ }
3400
+ }
3401
+ let group;
3402
+ let p = n.parent;
3403
+ while (p && p.type !== "optional_formal_parameters") p = p.parent;
3404
+ if (p && p.type === "optional_formal_parameters") group = p;
3405
+ const isNamed = group ? group.text.startsWith("{") : false;
3406
+ const isOptional = group !== void 0;
3407
+ const hasDefault = n.text.includes("=");
3408
+ const already = out.some((p2) => p2.name === nameNode.text);
3409
+ if (!already) out.push({ name: nameNode.text, type: typeText ?? IMPLICIT_DYNAMIC_TYPE, isOptional, isNamed });
3410
+ }
3411
+ }
3412
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
3413
+ const c = n.namedChild(i);
3414
+ if (c) stack.push(c);
3415
+ }
3416
+ }
3417
+ return out;
3418
+ }
3419
+ extractReturnType(node) {
3420
+ const voidType = this.findChildByType(node, "void_type");
3421
+ if (voidType) return "void";
3422
+ const nameNode = node.childForFieldName("name");
3423
+ for (let i = 0; i < node.namedChildCount; i++) {
3424
+ const c = node.namedChild(i);
3425
+ if (!c) continue;
3426
+ if (nameNode && c.startIndex === nameNode.startIndex && c.endIndex === nameNode.endIndex) break;
3427
+ if (c.type === "type_identifier" || c.type === "void_type" || c.type === "record_type") return c.text;
3428
+ }
3429
+ return void 0;
3430
+ }
3431
+ extractFieldType(node, declList) {
3432
+ const candidates = [node, declList];
3433
+ for (const cand of candidates) {
3434
+ for (let i = 0; i < cand.namedChildCount; i++) {
3435
+ const c = cand.namedChild(i);
3436
+ if (!c) continue;
3437
+ if (c.type === "type_identifier" || c.type === "void_type" || c.type === "record_type") {
3438
+ return c.text;
3439
+ }
3440
+ }
3441
+ }
3442
+ return void 0;
3443
+ }
3444
+ /** The representation type's leaf name (`extension type X(objc.ObjCObject o)`
3445
+ * -> `ObjCObject`): alias-free like every other type name this lane emits
3446
+ * (DARTD1/DARTD2), since the first identifier is the IMPORT PREFIX. */
3447
+ representationTypeName(rep) {
3448
+ if (!rep) return void 0;
3449
+ const nameNode = rep.childForFieldName("name");
3450
+ let leaf;
3451
+ for (let i = 0; i < rep.namedChildCount; i++) {
3452
+ const c = rep.namedChild(i);
3453
+ if (!c) continue;
3454
+ if (nameNode && c.startIndex === nameNode.startIndex && c.endIndex === nameNode.endIndex) break;
3455
+ if (c.type === "type_identifier" || c.type === "void_type") leaf = c.text;
3456
+ }
3457
+ return leaf;
3458
+ }
3459
+ extractTypeParameters(node) {
3460
+ const tp = this.findChildByType(node, "type_parameters");
3461
+ if (!tp) return void 0;
3462
+ const out = [];
3463
+ for (let i = 0; i < tp.namedChildCount; i++) {
3464
+ const c = tp.namedChild(i);
3465
+ if (c && c.type === "type_parameter") out.push(c.text);
3466
+ }
3467
+ return out.length > 0 ? out : void 0;
3468
+ }
3469
+ extensionTargetType(node) {
3470
+ const name = node.childForFieldName("name")?.text;
3471
+ for (let i = 0; i < node.namedChildCount; i++) {
3472
+ const c = node.namedChild(i);
3473
+ if (!c || c.type === "extension_body" || c.type === "identifier") continue;
3474
+ if (c.type === "type_identifier" || c.type === "void_type") {
3475
+ if (c.text === name) continue;
3476
+ return c.text;
3477
+ }
3478
+ }
3479
+ return void 0;
3480
+ }
3481
+ /**
3482
+ * THE NAME AN EXTENSION IS PUBLISHED UNDER — ONE rule at BOTH sites that need it (the symbol
3483
+ * emitter and `resolveOwner`), because a member whose published scope and whose container's name
3484
+ * disagree is a `contains` edge that names nothing.
3485
+ *
3486
+ * A named extension is published under its own name. An unnamed one has none, so it takes the
3487
+ * family's synthesized spelling — see `DART_UNNAMED_EXTENSION` for why borrowing the target
3488
+ * type's name was a defect rather than untidiness, and what it cost. The `undefined` fallback is
3489
+ * `DART_UNNAMED_EXTENSION` itself, mirroring java's `anonymous_class` (real-java-analyzer.ts):
3490
+ * a declaration that HAS members and no name still needs an owner string, and the alternative is
3491
+ * leaking those members into whatever scope surrounded them.
3492
+ */
3493
+ extensionPublishedName(node) {
3494
+ const declared = node.childForFieldName?.("name")?.text;
3495
+ if (declared) return declared;
3496
+ const target = this.extensionTargetType(node);
3497
+ return target ? `${DART_UNNAMED_EXTENSION}_${target}` : DART_UNNAMED_EXTENSION;
3498
+ }
3499
+ /**
3500
+ * Is this `type_identifier` the `on` SUBJECT of the UNNAMED extension that encloses it?
3501
+ *
3502
+ * The subject is part of the extension's own identity — `extensionPublishedName` derives the
3503
+ * published name FROM it — so it is not a mention the body makes. Only an UNNAMED extension is
3504
+ * covered: `extension M on M2 {}` publishes `M -> M2` and must keep doing so (pinned by
3505
+ * `verify-dart-references.ts`).
3506
+ */
3507
+ isUnnamedExtensionSubject(n) {
3508
+ const parent = n.parent;
3509
+ if (!parent || parent.type !== "extension_declaration") return false;
3510
+ return parent.childForFieldName?.("name")?.text === void 0;
3511
+ }
3512
+ extractAnnotations(node) {
3513
+ const annotations = [];
3514
+ let prev = node.previousNamedSibling;
3515
+ let guard = 0;
3516
+ while (prev && guard++ < 8) {
3517
+ if (prev.type === "annotation") {
3518
+ annotations.unshift(prev.text);
3519
+ prev = prev.previousNamedSibling;
3520
+ } else {
3521
+ break;
3522
+ }
3523
+ }
3524
+ return annotations.length > 0 ? annotations : void 0;
3525
+ }
3526
+ // ============================================================================
3527
+ // ANNOTATION REFERENCES — the type a `@...` names is a `references` edge
3528
+ // ============================================================================
3529
+ /**
3530
+ * Emit one `references` row per (annotation, decorated declaration) pair.
3531
+ *
3532
+ * THE RULES, each from the pinned grammar (see `annotation-shapes-probe.ts`):
3533
+ *
3534
+ * TARGET — the annotation's `name` field, read as written and WITHOUT its `@`, arguments
3535
+ * or type arguments. For `@prefix.Qualified` the target is the LEAF the `name` field names,
3536
+ * not the import alias: this lane strips alias prefixes everywhere heritage and type
3537
+ * mentions are spelled (DT-B/DAD1/DAD2 — `_i2.Mixin` is `Mixin`), and an annotation alias
3538
+ * is the same alias. (Java keeps the full path because Java's qualifier is a package, not
3539
+ * an import alias; the two lanes' conventions differ for a reason.)
3540
+ *
3541
+ * SOURCE — the declaration the annotation DECORATES, because an annotation node has no
3542
+ * name of its own. Three carrier shapes, all probed:
3543
+ * child form `@deprecated class C` -> annotation is a CHILD of `class_definition`;
3544
+ * `enum Kind { @deprecated old }` -> child of `enum_constant`;
3545
+ * `@deprecated final x = 1;` -> child of `local_variable_declaration`.
3546
+ * sibling form inside `class_body` and `program` the annotation is a SIBLING of the
3547
+ * declaration (`class_body (annotation) (method_signature)`), so the
3548
+ * carrier is the next non-annotation sibling.
3549
+ * signature-param form `void f(@required int x)` -> annotation is a CHILD of
3550
+ * `formal_parameter`; the carrier is the enclosing callable, because a
3551
+ * parameter publishes no symbol (DARTD8) and the annotation is a fact
3552
+ * about the declaration that carries it.
3553
+ * Multi-declarator fields (`@JsonKey(...) int a = 1, b = 2;`) publish ONE row per
3554
+ * declarator, matching the symbol model (each declarator is its own property).
3555
+ *
3556
+ * CONFIDENCE 0.95, `evidenceClass: structural`: the annotation's type name is WRITTEN at
3557
+ * the declaration, not inferred. `annotatedKind` is the carrier's grammar node type so a
3558
+ * consumer can tell a class annotation from a parameter annotation — the node type alone
3559
+ * cannot.
3560
+ *
3561
+ * NOT A CLAIM, and not an unresolvable-target exemption: the row is an ordinary `references`
3562
+ * edge and the project's resolver decides whether the annotation type has exactly one
3563
+ * in-project declaration (`@override` is dart:core's, so it will read unresolved in a
3564
+ * per-file run and resolve under an index — the same treatment every out-of-file type
3565
+ * mention gets on this label).
3566
+ */
3567
+ emitAnnotationReferences(ann, relationships, current, owner, moduleName, importAliases) {
3568
+ const nameNode = ann.childForFieldName?.("name") ?? this.findChildByType(ann, "identifier");
3569
+ if (!nameNode) return;
3570
+ const target = this.annotationTargetSpelling(nameNode, importAliases);
3571
+ if (!target) return;
3572
+ const carriers = this.annotationCarriers(ann, current, owner, moduleName);
3573
+ for (const carrier of carriers) {
3574
+ relationships.push({
3575
+ source: carrier.name,
3576
+ target,
3577
+ type: ASTEngine.RelationshipType.References,
3578
+ confidence: 0.95,
3579
+ // The NAME is written on the annotation's line; the declaration's header is another.
3580
+ line_number: this.nodeLine(nameNode),
3581
+ metadata: {
3582
+ relationshipType: "annotation",
3583
+ isAnnotation: true,
3584
+ annotatedKind: carrier.kind
3585
+ }
3586
+ });
3587
+ }
3588
+ }
3589
+ /**
3590
+ * The annotation's TYPE name, as this lane spells types.
3591
+ *
3592
+ * A `scoped_identifier` is ONE grammar shape for two different Dart facts, and the alias table
3593
+ * is what tells them apart:
3594
+ *
3595
+ * `@_i1.TestFor()` the qualifier is an IMPORT ALIAS -> the type is the LEAF
3596
+ * (`TestFor`), and publishing `_i1.TestFor` would name an
3597
+ * alias no declaration has.
3598
+ * `@TestFor.async_value_nullable_pattern`
3599
+ * the qualifier is the TYPE and the member selects a named
3600
+ * CONSTRUCTOR (riverpod's `@TestFor.<lint>()` family) or a
3601
+ * static; the type is the QUALIFIER. Taking the leaf here
3602
+ * published a target named after a constructor, which is
3603
+ * neither a type nor a declaration — and on a file whose
3604
+ * stem matched the constructor it even produced a
3605
+ * self-reference row (`async_value_nullable_pattern ->
3606
+ * async_value_nullable_pattern`), measured by the corpus
3607
+ * gate as UNEXPLAINED.
3608
+ *
3609
+ * Non-scoped names: the name field's own text. Arguments and type arguments are separate
3610
+ * children and never enter the target.
3611
+ */
3612
+ annotationTargetSpelling(nameNode, importAliases) {
3613
+ if (nameNode.type === "identifier" || nameNode.type === "type_identifier") return nameNode.text;
3614
+ if (nameNode.type === "scoped_identifier") {
3615
+ const scope = nameNode.childForFieldName?.("scope")?.text ?? (nameNode.namedChildren ?? []).find((c) => c?.type === "identifier")?.text;
3616
+ const leaf = nameNode.childForFieldName?.("name")?.text ?? [...nameNode.namedChildren ?? []].reverse().find((c) => c?.type === "identifier")?.text;
3617
+ if (scope && importAliases.has(scope)) return leaf ?? null;
3618
+ return scope ?? leaf ?? null;
3619
+ }
3620
+ const id = this.findChildByType(nameNode, "identifier") ?? this.findChildByType(nameNode, "type_identifier");
3621
+ return id ? id.text : null;
3622
+ }
3623
+ /**
3624
+ * The declarations one annotation decorates, with the grammar node type of each carrier.
3625
+ * EMPTY when the position publishes no name this lane can attribute to — a library
3626
+ * directive's annotation (`library @TestOn('vm');`) is deliberately not attributed: the
3627
+ * library node is the module symbol, and an edge from the module to the annotation type
3628
+ * would be a claim about a directive the lane has not placed in this family.
3629
+ */
3630
+ annotationCarriers(ann, current, owner, moduleName) {
3631
+ const parent = ann.parent;
3632
+ if (!parent) return [];
3633
+ const p = parent.type;
3634
+ if (p === "class_definition" || p === "mixin_declaration" || p === "enum_declaration" || p === "extension_declaration" || p === "extension_type_declaration") {
3635
+ const nm = parent.childForFieldName?.("name")?.text ?? this.findChildByType(parent, "identifier")?.text;
3636
+ return nm ? [{ name: nm, kind: p }] : [];
3637
+ }
3638
+ if (p === "enum_constant") {
3639
+ const nm = parent.childForFieldName?.("name")?.text ?? this.findChildByType(parent, "identifier")?.text;
3640
+ return nm ? [{ name: nm, kind: p }] : [];
3641
+ }
3642
+ if (p === "type_alias") {
3643
+ const nm = parent.childForFieldName?.("name")?.text ?? this.findChildByType(parent, "type_identifier")?.text;
3644
+ return nm ? [{ name: nm, kind: p }] : [];
3645
+ }
3646
+ if (p === "library_name") {
3647
+ const declared = this.findChildByType(parent, "dotted_identifier_list");
3648
+ const name = declared ? stripWhitespace(declared.text) : moduleName;
3649
+ return name ? [{ name, kind: "library_annotation" }] : [];
3650
+ }
3651
+ if (p === "local_variable_declaration") {
3652
+ const carrier = current ?? owner;
3653
+ return carrier ? [{ name: carrier, kind: p }] : [];
3654
+ }
3655
+ if (p === "formal_parameter" || p === "constructor_param" || p === "super_formal_parameter") {
3656
+ let sig = parent;
3657
+ let guard = 0;
3658
+ while (sig && guard++ < 8) {
3659
+ if (this.isSignatureNode(sig.type)) break;
3660
+ sig = sig.parent;
3661
+ }
3662
+ if (sig) {
3663
+ if (sig.parent?.type === "lambda_expression" || sig.parent?.type === "function_expression") {
3664
+ return current ? [{ name: current, kind: "lambda_parameter" }] : [];
3665
+ }
3666
+ const inner = this.findInnerSignature(sig);
3667
+ if (this.isConstructorSignature(sig.type) || this.isConstructorSignature(inner.type)) {
3668
+ return this.isRealConstructor(inner, this.enclosingTypeName(inner)) ? [{ name: "constructor", kind: "constructor_param" }] : [];
3669
+ }
3670
+ const nm = this.signatureName(inner);
3671
+ return nm ? [{ name: nm, kind: "parameter_annotation" }] : [];
3672
+ }
3673
+ return current ? [{ name: current, kind: "parameter_annotation" }] : [];
3674
+ }
3675
+ if (p === "class_body" || p === "program" || p === "enum_body") {
3676
+ let sib = ann.nextNamedSibling;
3677
+ let guard = 0;
3678
+ while (sib && sib.type === "annotation" && guard++ < 16) sib = sib.nextNamedSibling;
3679
+ let modGuard = 0;
3680
+ while (sib && sib.type.endsWith("_builtin") && modGuard++ < 8) sib = sib.nextNamedSibling;
3681
+ return sib ? this.carriersOfDeclarationNode(sib) : [];
3682
+ }
3683
+ return [];
3684
+ }
3685
+ /** The names a declaration node publishes, for the annotation sibling form. */
3686
+ carriersOfDeclarationNode(node) {
3687
+ if (node.type === "type_identifier" || node.type === "void_type" || node.type === "nullable_type" || node.type === "function_type" || node.type === "inferred_type") {
3688
+ const after = node.nextNamedSibling;
3689
+ if (after && (after.type === "initialized_identifier_list" || after.type === "static_final_declaration_list")) {
3690
+ return this.carriersOfDeclarationNode(after);
3691
+ }
3692
+ return [];
3693
+ }
3694
+ if (node.type === "initialized_identifier_list" || node.type === "static_final_declaration_list") {
3695
+ const out2 = [];
3696
+ for (let i = 0; i < node.namedChildCount; i++) {
3697
+ const c = node.namedChild(i);
3698
+ if (!c) continue;
3699
+ const id = this.findChildByType(c, "identifier");
3700
+ if (id) out2.push({ name: id.text, kind: node.type });
3701
+ }
3702
+ return out2;
3703
+ }
3704
+ const t = node.type;
3705
+ if (t === "class_definition" || t === "mixin_declaration" || t === "enum_declaration" || t === "extension_declaration" || t === "extension_type_declaration" || t === "type_alias") {
3706
+ const nm = node.childForFieldName?.("name")?.text ?? this.findChildByType(node, "identifier")?.text;
3707
+ return nm ? [{ name: nm, kind: t }] : [];
3708
+ }
3709
+ let decl = node;
3710
+ if (decl.type === "declaration") {
3711
+ for (let i = 0; i < decl.namedChildCount; i++) {
3712
+ const c = decl.namedChild(i);
3713
+ if (c && this.isSignatureNode(c.type)) {
3714
+ decl = c;
3715
+ break;
3716
+ }
3717
+ }
3718
+ }
3719
+ if (this.isSignatureNode(decl.type)) {
3720
+ const inner = this.findInnerSignature(decl);
3721
+ if (this.isConstructorSignature(decl.type) || this.isConstructorSignature(inner.type)) {
3722
+ return this.isRealConstructor(inner, this.enclosingTypeName(inner)) ? [{ name: "constructor", kind: "constructor_signature" }] : [];
3723
+ }
3724
+ const nm = this.signatureName(inner);
3725
+ return nm ? [{ name: nm, kind: inner.type }] : [];
3726
+ }
3727
+ const out = [];
3728
+ const stack = [node];
3729
+ let guard = 0;
3730
+ while (stack.length > 0 && guard++ < 64) {
3731
+ const c = stack.pop();
3732
+ if (c.type === "initialized_identifier" || c.type === "static_final_declaration") {
3733
+ const id = this.findChildByType(c, "identifier");
3734
+ if (id) out.push({ name: id.text, kind: node.type });
3735
+ continue;
3736
+ }
3737
+ for (let i = c.namedChildCount - 1; i >= 0; i--) {
3738
+ const ch = c.namedChild(i);
3739
+ if (ch && (ch.type === "initialized_identifier_list" || ch.type === "static_final_declaration_list" || ch.type === "initialized_identifier" || ch.type === "static_final_declaration")) {
3740
+ stack.push(ch);
3741
+ }
3742
+ }
3743
+ }
3744
+ return out;
3745
+ }
3746
+ collectComments(node) {
3747
+ const comments = [];
3748
+ let sibling = node.previousSibling;
3749
+ let guard = 0;
3750
+ while (sibling && guard++ < 12) {
3751
+ if (sibling.type === "comment" || sibling.type === "documentation_comment") {
3752
+ comments.unshift(stripCommentMarkers(sibling.text));
3753
+ sibling = sibling.previousSibling;
3754
+ } else if (sibling.type === "annotation") {
3755
+ sibling = sibling.previousSibling;
3756
+ } else {
3757
+ break;
3758
+ }
3759
+ }
3760
+ return comments.length > 0 ? comments : void 0;
3761
+ }
3762
+ directIdentifiers(node) {
3763
+ const out = [];
3764
+ for (let i = 0; i < node.namedChildCount; i++) {
3765
+ const c = node.namedChild(i);
3766
+ if (c && (c.type === "identifier" || c.type === "type_identifier")) out.push(c.text);
3767
+ }
3768
+ return out;
3769
+ }
3770
+ // ══ CYCLOMATIC COMPLEXITY IS COMPUTED ONCE PER FUNCTION, NOT ONCE PER CALL.
3771
+ //
3772
+ // `methodComplexity` walks the node's whole subtree, and `processTopLevelFunction` calls it for
3773
+ // every function-like node. In a file of DEEPLY NESTED statements those subtrees OVERLAP — every
3774
+ // nested function's walk includes everything below it — so the pass is O(functions × nodes). The
3775
+ // accessor counter measured it: **103,034,258 `.type` reads** on a 130,042-node file, with
3776
+ // `.text` at 1,596 and 1 MB of characters materialised, and the stack named this function. (That
3777
+ // also FALSIFIED the `.text` hypothesis: the materialisation was never the cost; the re-walking
3778
+ // was, and each visit pays ~10 `.type` reads that cross into WASM.)
3779
+ //
3780
+ // A function's complexity is a pure property of its subtree, so it is cached by the node's stable
3781
+ // SPAN — `startIndex:endIndex:type`, never the node object, because web-tree-sitter hands out
3782
+ // fresh wrappers. Cleared per file in `analyze()`.
3783
+ methodComplexityCache = /* @__PURE__ */ new Map();
3784
+ methodComplexity(node) {
3785
+ const cacheKey = `${node.startIndex}:${node.endIndex}:${node.type}`;
3786
+ const cached = this.methodComplexityCache.get(cacheKey);
3787
+ if (cached !== void 0) return cached;
3788
+ let c = 1;
3789
+ const stack = [node];
3790
+ let sib = node.nextNamedSibling;
3791
+ let guard = 0;
3792
+ while (sib && guard++ < 8) {
3793
+ if (sib.type === "function_body" || sib.type === "block" || sib.type === "function_expression_body") {
3794
+ stack.push(sib);
3795
+ break;
3796
+ }
3797
+ if (sib.type === "class_body" || sib.type === "extension_body" || sib.type === "}" || sib.type === "declaration" || sib.type === "method_signature" || sib.type === "function_signature") break;
3798
+ sib = sib.nextNamedSibling;
3799
+ }
3800
+ guard = 0;
3801
+ while (stack.length > 0 && guard++ < 2e5) {
3802
+ const n = stack.pop();
3803
+ if (n.type === "if_statement" || n.type === "for_statement" || n.type === "while_statement" || n.type === "do_statement" || n.type === "switch_statement_case" || n.type === "switch_expression_case" || n.type === "catch_clause" || n.type === "conditional_expression" || n.type === "logical_and_expression" || n.type === "logical_or_expression") {
3804
+ c++;
3805
+ }
3806
+ for (let i = 0; i < n.namedChildCount; i++) {
3807
+ const child = n.namedChild(i);
3808
+ if (child) stack.push(child);
3809
+ }
3810
+ }
3811
+ this.methodComplexityCache.set(cacheKey, c);
3812
+ return c;
3813
+ }
3814
+ firstTypeIdentifier(node, excludeMixins) {
3815
+ for (let i = 0; i < node.namedChildCount; i++) {
3816
+ const c = node.namedChild(i);
3817
+ if (!c) continue;
3818
+ if (excludeMixins && c.type === "mixins") continue;
3819
+ if (c.type === "type_identifier") {
3820
+ return c;
3821
+ }
3822
+ if (c.type === "identifier") return c;
3823
+ }
3824
+ return null;
3825
+ }
3826
+ /**
3827
+ * Resolved heritage names (DAD1/DAD2 contract): the real class names from a
3828
+ * heritage clause, with import-alias prefixes dropped and generic arguments
3829
+ * excluded.
3830
+ *
3831
+ * tree-sitter-dart parses a qualified supertype FLAT: `extends _i1.SmartFake`
3832
+ * is superclass > [_i1, `.`, SmartFake] — adjacent type_identifiers joined by
3833
+ * an anonymous `.` token where the FIRST is the import-alias container, not a
3834
+ * class name (the mocktail shape: `class _FakeX_0 extends _i1.SmartFake
3835
+ * implements _i2.Database`). Taking the first raw identifier named the base
3836
+ * after the alias (DAD1); enumerating all type_identifiers leaked the alias
3837
+ * into implements targets (DAD2).
3838
+ *
3839
+ * Rule over RAW children (anonymous tokens carry the disambiguation):
3840
+ * keep type_identifier/identifier nodes whose next raw sibling is NOT a `.`
3841
+ * token — a `.` means this name is an alias prefix; `,` or end-of-clause or
3842
+ * type_arguments means it is a real name. type_arguments are generic
3843
+ * arguments, never bases; mixins are handled separately as `uses`.
3844
+ * Probed shapes (dart-heritage-shapes.ts, 2026-09-03):
3845
+ * `_i1.SmartFake` -> [SmartFake]
3846
+ * `Alpha, Beta` -> [Alpha, Beta]
3847
+ * `_i2.Database, _i3.Future` -> [Database, Future]
3848
+ * `generics.Map<String, int>`-> [Map]
3849
+ */
3850
+ resolvedHeritageNames(node) {
3851
+ return this.resolvedHeritageNodes(node).map((n) => n.text);
3852
+ }
3853
+ /** Node form of `resolvedHeritageNames` — the heritage-name node IS the node
3854
+ * that names an extends/implements/uses edge (M60 line contract), so text
3855
+ * and line come from one selection. */
3856
+ resolvedHeritageNodes(node) {
3857
+ const out = [];
3858
+ for (let i = 0; i < node.childCount; i++) {
3859
+ const c = node.child(i);
3860
+ if (!c) continue;
3861
+ if (c.type === "type_arguments" || c.type === "mixins") continue;
3862
+ if (c.type !== "type_identifier" && c.type !== "identifier") continue;
3863
+ const next = node.child(i + 1);
3864
+ if (next && next.type === ".") continue;
3865
+ out.push(c);
3866
+ }
3867
+ return out;
3868
+ }
3869
+ allTypeIdentifiers(node) {
3870
+ const out = [];
3871
+ const stack = [node];
3872
+ let guard = 0;
3873
+ while (stack.length > 0 && guard++ < 64) {
3874
+ const n = stack.pop();
3875
+ if (n.type === "type_identifier" && n.parent && n.parent.type !== "type_arguments") {
3876
+ out.push(n);
3877
+ } else if (n.type === "identifier" && n.parent && n.parent.type === "mixins") {
3878
+ out.push(n);
3879
+ }
3880
+ for (let i = 0; i < n.namedChildCount; i++) {
3881
+ const c = n.namedChild(i);
3882
+ if (c) stack.push(c);
3883
+ }
3884
+ }
3885
+ return out;
3886
+ }
3887
+ findChildByType(node, type) {
3888
+ for (let i = 0; i < node.namedChildCount; i++) {
3889
+ const child = node.namedChild(i);
3890
+ if (child && child.type === type) return child;
3891
+ }
3892
+ return null;
3893
+ }
3894
+ findDescendantByType(node, type) {
3895
+ const stack = [node];
3896
+ while (stack.length > 0) {
3897
+ const n = stack.pop();
3898
+ if (n.type === type) return n;
3899
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
3900
+ const c = n.namedChild(i);
3901
+ if (c) stack.push(c);
3902
+ }
3903
+ }
3904
+ return null;
3905
+ }
3906
+ /**
3907
+ * M60 (#929 lineage, dart lane): the line a relationship CLAIMS is the line
3908
+ * of the node that NAMES its target — the base clause for extends/implements,
3909
+ * the declared member for contains, the callee for calls, the constructed
3910
+ * class for instantiates, the overriding method for overrides, the uri
3911
+ * literal for an import/export/part, the redirect target for a factory
3912
+ * redirection. No node means NO field (absent is honest; `0` is the M59
3913
+ * fabrication).
3914
+ */
3915
+ nodeLine(node) {
3916
+ if (!node) return void 0;
3917
+ return node.startPosition.row + 1;
3918
+ }
3919
+ nodeToRange(node) {
3920
+ return {
3921
+ startLine: node.startPosition.row + 1,
3922
+ startColumn: node.startPosition.column + 1,
3923
+ endLine: node.endPosition.row + 1,
3924
+ endColumn: node.endPosition.column + 1,
3925
+ startIndex: node.startIndex,
3926
+ endIndex: node.endIndex
3927
+ };
3928
+ }
3929
+ };
3930
+ function dartNameLineKey(name, range) {
3931
+ return `${name}|${range?.startIndex ?? -1}|${range?.endIndex ?? -1}`;
3932
+ }
3933
+ function scopeOwnerKind(scopes) {
3934
+ for (let i = scopes.length - 1; i >= 0; i--) {
3935
+ if (scopes[i].kind === "class" /* Class */) {
3936
+ return ASTEngine.SymbolKind.Class;
3937
+ }
3938
+ }
3939
+ return void 0;
3940
+ }
3941
+ export {
3942
+ DART_CITATION_ROOTS,
3943
+ RealDartAnalyzer
3944
+ };