@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,2608 @@
1
+ import {
2
+ buildResolutionTables
3
+ } from "./chunk-2DE27DVC.js";
4
+ import {
5
+ fileClaimForTarget
6
+ } from "./chunk-EWMWK7WN.js";
7
+ import {
8
+ ASTEngine
9
+ } from "./chunk-3ZTOHHSW.js";
10
+ import "./chunk-BQ3OVQ3P.js";
11
+ import "./chunk-SK6YVHFZ.js";
12
+ import "./chunk-2BSKD7O3.js";
13
+ import {
14
+ BINDING_SEP
15
+ } from "./chunk-KAET47ZL.js";
16
+ import "./chunk-KCDECRJY.js";
17
+ import "./chunk-MLKGABMK.js";
18
+
19
+ // src/ast/solidity-reserved-tokens.ts
20
+ var RESERVED_TOKENS = /* @__PURE__ */ new Set([
21
+ "_",
22
+ "abstract",
23
+ "add",
24
+ "addmod",
25
+ "address",
26
+ "and",
27
+ "anonymous",
28
+ "as",
29
+ "assembly",
30
+ "at",
31
+ "balance",
32
+ "basefee",
33
+ "blobbasefee",
34
+ "blobfee",
35
+ "blobhash",
36
+ "blockhash",
37
+ "bool",
38
+ "break",
39
+ "byte",
40
+ "bytes",
41
+ "bytes1",
42
+ "bytes10",
43
+ "bytes11",
44
+ "bytes12",
45
+ "bytes13",
46
+ "bytes14",
47
+ "bytes15",
48
+ "bytes16",
49
+ "bytes17",
50
+ "bytes18",
51
+ "bytes19",
52
+ "bytes2",
53
+ "bytes20",
54
+ "bytes21",
55
+ "bytes22",
56
+ "bytes23",
57
+ "bytes24",
58
+ "bytes25",
59
+ "bytes26",
60
+ "bytes27",
61
+ "bytes28",
62
+ "bytes29",
63
+ "bytes3",
64
+ "bytes30",
65
+ "bytes31",
66
+ "bytes32",
67
+ "bytes4",
68
+ "bytes5",
69
+ "bytes6",
70
+ "bytes7",
71
+ "bytes8",
72
+ "bytes9",
73
+ "call",
74
+ "callcode",
75
+ "calldata",
76
+ "calldatacopy",
77
+ "calldataload",
78
+ "calldatasize",
79
+ "caller",
80
+ "callvalue",
81
+ "case",
82
+ "catch",
83
+ "chainid",
84
+ "coinbase",
85
+ "constant",
86
+ "constructor",
87
+ "continue",
88
+ "contract",
89
+ "create",
90
+ "create2",
91
+ "days",
92
+ "default",
93
+ "delegatecall",
94
+ "delete",
95
+ "difficulty",
96
+ "div",
97
+ "do",
98
+ "else",
99
+ "emit",
100
+ "enum",
101
+ "eq",
102
+ "error",
103
+ "ether",
104
+ "event",
105
+ "exp",
106
+ "extcodecopy",
107
+ "extcodehash",
108
+ "extcodesize",
109
+ "external",
110
+ "fallback",
111
+ "false",
112
+ "finney",
113
+ "fixed",
114
+ "for",
115
+ "from",
116
+ "function",
117
+ "gas",
118
+ "gaslimit",
119
+ "gasprice",
120
+ "global",
121
+ "gt",
122
+ "gwei",
123
+ "hex",
124
+ "hours",
125
+ "if",
126
+ "immutable",
127
+ "import",
128
+ "indexed",
129
+ "int",
130
+ "int104",
131
+ "int112",
132
+ "int120",
133
+ "int128",
134
+ "int136",
135
+ "int144",
136
+ "int152",
137
+ "int16",
138
+ "int160",
139
+ "int168",
140
+ "int176",
141
+ "int184",
142
+ "int192",
143
+ "int200",
144
+ "int208",
145
+ "int216",
146
+ "int224",
147
+ "int232",
148
+ "int24",
149
+ "int240",
150
+ "int248",
151
+ "int256",
152
+ "int32",
153
+ "int40",
154
+ "int48",
155
+ "int56",
156
+ "int64",
157
+ "int72",
158
+ "int8",
159
+ "int80",
160
+ "int88",
161
+ "int96",
162
+ "interface",
163
+ "internal",
164
+ "invalid",
165
+ "is",
166
+ "iszero",
167
+ "keccak256",
168
+ "layout",
169
+ "leave",
170
+ "let",
171
+ "library",
172
+ "log0",
173
+ "log1",
174
+ "log2",
175
+ "log3",
176
+ "log4",
177
+ "lt",
178
+ "mapping",
179
+ "mcopy",
180
+ "memory",
181
+ "minutes",
182
+ "mload",
183
+ "mod",
184
+ "modifier",
185
+ "msize",
186
+ "mstore",
187
+ "mstore8",
188
+ "mul",
189
+ "mulmod",
190
+ "new",
191
+ "not",
192
+ "number",
193
+ "or",
194
+ "origin",
195
+ "override",
196
+ "payable",
197
+ "pop",
198
+ "pragma",
199
+ "prevrandao",
200
+ "private",
201
+ "public",
202
+ "pure",
203
+ "receive",
204
+ "return",
205
+ "returndatacopy",
206
+ "returndatasize",
207
+ "returns",
208
+ "revert",
209
+ "sar",
210
+ "sdiv",
211
+ "seconds",
212
+ "selfbalance",
213
+ "selfdestruct",
214
+ "sgt",
215
+ "shl",
216
+ "shr",
217
+ "signextend",
218
+ "sload",
219
+ "slt",
220
+ "smod",
221
+ "solidity",
222
+ "sstore",
223
+ "staticcall",
224
+ "stop",
225
+ "storage",
226
+ "string",
227
+ "struct",
228
+ "sub",
229
+ "switch",
230
+ "szabo",
231
+ "timestamp",
232
+ "tload",
233
+ "transient",
234
+ "true",
235
+ "try",
236
+ "tstore",
237
+ "type",
238
+ "ufixed",
239
+ "uint",
240
+ "uint104",
241
+ "uint112",
242
+ "uint120",
243
+ "uint128",
244
+ "uint136",
245
+ "uint144",
246
+ "uint152",
247
+ "uint16",
248
+ "uint160",
249
+ "uint168",
250
+ "uint176",
251
+ "uint184",
252
+ "uint192",
253
+ "uint200",
254
+ "uint208",
255
+ "uint216",
256
+ "uint224",
257
+ "uint232",
258
+ "uint24",
259
+ "uint240",
260
+ "uint248",
261
+ "uint256",
262
+ "uint32",
263
+ "uint40",
264
+ "uint48",
265
+ "uint56",
266
+ "uint64",
267
+ "uint72",
268
+ "uint8",
269
+ "uint80",
270
+ "uint88",
271
+ "uint96",
272
+ "unchecked",
273
+ "unicode",
274
+ "using",
275
+ "var",
276
+ "view",
277
+ "virtual",
278
+ "weeks",
279
+ "wei",
280
+ "while",
281
+ "xor",
282
+ "years"
283
+ ]);
284
+ var isPrimitiveTypeToken = (name) => {
285
+ const has = (prefix) => name.length > prefix.length && name.startsWith(prefix) && isAllDigits(name.slice(prefix.length));
286
+ if (name === "bool" || name === "string" || name === "address" || name === "byte" || name === "bytes" || name === "fixed" || name === "ufixed") return true;
287
+ return has("uint") || has("int") || has("bytes");
288
+ };
289
+ var isAllDigits = (text) => {
290
+ if (text.length === 0) return false;
291
+ for (let i = 0; i < text.length; i++) {
292
+ const c = text.charCodeAt(i);
293
+ if (c < 48 || c > 57) return false;
294
+ }
295
+ return true;
296
+ };
297
+ var isReservedTarget = (type, target, metadata) => {
298
+ if (target.length === 0) return false;
299
+ const meta = metadata ?? {};
300
+ const viaMember = meta.isMemberCall === true || meta.isMemberReference === true || meta.isTypeReference === true;
301
+ if (type === "calls") {
302
+ if (viaMember) return false;
303
+ return RESERVED_TOKENS.has(target);
304
+ }
305
+ if (type === "instantiates") return RESERVED_TOKENS.has(target);
306
+ if (type === "uses") {
307
+ if (viaMember) return false;
308
+ return RESERVED_TOKENS.has(target);
309
+ }
310
+ return false;
311
+ };
312
+ var ENGINE_FUNCTION_NAMES = /* @__PURE__ */ new Set([
313
+ "require",
314
+ "assert",
315
+ "gasleft",
316
+ "sha256",
317
+ "ripemd160",
318
+ "ecrecover",
319
+ "suicide",
320
+ "keccak256",
321
+ "sha3",
322
+ "gas",
323
+ "blockhash",
324
+ "blobhash",
325
+ "selfdestruct",
326
+ "addmod",
327
+ "mulmod"
328
+ ]);
329
+ var EVM_BUILTIN_NAMES = /* @__PURE__ */ new Set([
330
+ "stop",
331
+ "add",
332
+ "sub",
333
+ "mul",
334
+ "div",
335
+ "sdiv",
336
+ "mod",
337
+ "smod",
338
+ "exp",
339
+ "not",
340
+ "lt",
341
+ "gt",
342
+ "slt",
343
+ "sgt",
344
+ "eq",
345
+ "iszero",
346
+ "and",
347
+ "or",
348
+ "xor",
349
+ "byte",
350
+ "shl",
351
+ "shr",
352
+ "sar",
353
+ "addmod",
354
+ "mulmod",
355
+ "signextend",
356
+ "keccak256",
357
+ "pop",
358
+ "mload",
359
+ "mstore",
360
+ "mstore8",
361
+ "sload",
362
+ "sstore",
363
+ "mcopy",
364
+ "tload",
365
+ "tstore",
366
+ "msize",
367
+ "gas",
368
+ "address",
369
+ "balance",
370
+ "selfbalance",
371
+ "caller",
372
+ "callvalue",
373
+ "calldataload",
374
+ "calldatasize",
375
+ "calldatacopy",
376
+ "extcodesize",
377
+ "extcodecopy",
378
+ "extcodehash",
379
+ "returndatasize",
380
+ "returndatacopy",
381
+ "create",
382
+ "create2",
383
+ "call",
384
+ "callcode",
385
+ "delegatecall",
386
+ "staticcall",
387
+ "return",
388
+ "revert",
389
+ "selfdestruct",
390
+ "invalid",
391
+ "log0",
392
+ "log1",
393
+ "log2",
394
+ "log3",
395
+ "log4",
396
+ "datasize",
397
+ "dataoffset",
398
+ "dataload",
399
+ "setimmutable",
400
+ "loadimmutable",
401
+ "linkersymbol",
402
+ "memoryguard",
403
+ "verbatim",
404
+ "chainid",
405
+ "basefee",
406
+ "blobbasefee",
407
+ "blobhash",
408
+ "origin",
409
+ "gasprice",
410
+ "blockhash",
411
+ "coinbase",
412
+ "timestamp",
413
+ "number",
414
+ "difficulty",
415
+ "prevrandao",
416
+ "gaslimit"
417
+ ]);
418
+ var STORE_REFUSED_SYMBOL_NAMES = /* @__PURE__ */ new Set([
419
+ "this",
420
+ "super",
421
+ "self",
422
+ "Self",
423
+ "me",
424
+ "My",
425
+ "destructor",
426
+ "root",
427
+ "program",
428
+ "source_file",
429
+ "module_declaration",
430
+ "package_manifest",
431
+ "GlobalScope",
432
+ "SCSS_FILE_SCOPE",
433
+ "unknown"
434
+ ]);
435
+
436
+ // src/ast/real-solidity-analyzer.ts
437
+ var isSpaceCode = (code) => code === 32 || code >= 9 && code <= 13 || code === 160 || code === 5760 || code >= 8192 && code <= 8202 || code === 8232 || code === 8233 || code === 8239 || code === 8287 || code === 12288 || code === 65279;
438
+ var isPlausibleSymbolName = (name) => {
439
+ if (name.length === 0) return false;
440
+ for (let i = 0; i < name.length; i++) {
441
+ const c = name.charCodeAt(i);
442
+ const ok = c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 95 || c === 36 || c === 46 || c === 40 || c === 41;
443
+ if (!ok) return false;
444
+ }
445
+ return true;
446
+ };
447
+ var skipLeadingSpace = (text) => {
448
+ let i = 0;
449
+ while (i < text.length && isSpaceCode(text.charCodeAt(i))) i++;
450
+ return text.slice(i);
451
+ };
452
+ var unquote = (text) => {
453
+ if (text.length < 2) return text;
454
+ const first = text.charCodeAt(0);
455
+ if (first !== 34 && first !== 39) return text;
456
+ if (text.charCodeAt(text.length - 1) !== first) return text;
457
+ return text.slice(1, -1);
458
+ };
459
+ var GLOBAL_OBJECTS = /* @__PURE__ */ new Set(["msg", "block", "tx", "super", "this"]);
460
+ var RealSolidityAnalyzer = class {
461
+ analyze(rootNode, content, filePath, context) {
462
+ const projectIndex = context?.projectIndex;
463
+ let inheritanceKindsResolvedProjectWide = 0;
464
+ const typeKindByName = /* @__PURE__ */ new Map();
465
+ const DECLARATION_NODE_TYPES = /* @__PURE__ */ new Set([
466
+ "contract_declaration",
467
+ "interface_declaration",
468
+ "library_declaration",
469
+ "function_definition",
470
+ "modifier_definition",
471
+ "constructor_definition",
472
+ "fallback_receive_definition",
473
+ "struct_declaration",
474
+ "enum_declaration",
475
+ "event_definition",
476
+ "error_declaration",
477
+ "user_defined_type_definition",
478
+ "state_variable_declaration",
479
+ "variable_declaration"
480
+ ]);
481
+ const natSpecParamCheck = (doc, paramNames, hasReturn) => {
482
+ const undocumented = paramNames.filter((n) => n !== "");
483
+ const overDocumented = [];
484
+ let returnWithoutReturn = false;
485
+ const documented = /* @__PURE__ */ new Set();
486
+ for (const line of doc.split("\n")) {
487
+ const t = line.trim();
488
+ if (!t.startsWith("@")) continue;
489
+ let end = 1;
490
+ while (end < t.length) {
491
+ const c = t.charAt(end);
492
+ if (c === " " || c === " ") break;
493
+ end++;
494
+ }
495
+ const tag = t.slice(0, end);
496
+ if (tag === "@param") {
497
+ let rest = end;
498
+ while (rest < t.length && (t.charAt(rest) === " " || t.charAt(rest) === " ")) rest++;
499
+ let nameEnd = rest;
500
+ while (nameEnd < t.length && t.charAt(nameEnd) !== " " && t.charAt(nameEnd) !== " ") nameEnd++;
501
+ const nm = t.slice(rest, nameEnd);
502
+ if (nm === "") continue;
503
+ documented.add(nm);
504
+ if (!paramNames.includes(nm)) overDocumented.push(nm);
505
+ } else if (tag === "@return") {
506
+ if (!hasReturn) returnWithoutReturn = true;
507
+ }
508
+ }
509
+ for (const n of documented) {
510
+ const at = undocumented.indexOf(n);
511
+ if (at >= 0) undocumented.splice(at, 1);
512
+ }
513
+ return { undocumented, overDocumented, returnWithoutReturn };
514
+ };
515
+ const readSolcLicence = (root) => {
516
+ const run = [];
517
+ for (let i = 0; i < root.namedChildCount; i++) {
518
+ const c = root.namedChild(i);
519
+ if (!c) continue;
520
+ if (c.type === "comment") {
521
+ run.push(c);
522
+ continue;
523
+ }
524
+ break;
525
+ }
526
+ for (const c of run) {
527
+ const text = readNatSpec(c.text).text;
528
+ if (text === "") continue;
529
+ for (const line of text.split("\n")) {
530
+ const t = line.trim();
531
+ if (!t.startsWith("SPDX-License-Identifier:")) continue;
532
+ const id = t.slice("SPDX-License-Identifier:".length).trim();
533
+ if (id !== "") return id;
534
+ }
535
+ }
536
+ return null;
537
+ };
538
+ const readNatSpec = (raw) => {
539
+ const lines2 = raw.split("\n");
540
+ const out = [];
541
+ let sawNatspec = false;
542
+ for (const line of lines2) {
543
+ let t = line;
544
+ while (t.length > 0 && t.charAt(0) === " ") t = t.slice(1);
545
+ if (t.startsWith("*/")) t = t.slice(2);
546
+ else if (t.startsWith("/**")) {
547
+ t = t.slice(3);
548
+ sawNatspec = true;
549
+ } else if (t.startsWith("///")) {
550
+ t = t.slice(3);
551
+ sawNatspec = true;
552
+ } else if (t.startsWith("//")) t = t.slice(2);
553
+ else if (t.startsWith("*")) t = t.slice(1);
554
+ if (t.startsWith(" ")) t = t.slice(1);
555
+ out.push(t);
556
+ }
557
+ while (out.length > 0 && out[out.length - 1].trim() === "") out.pop();
558
+ while (out.length > 0 && out[0].trim() === "") out.shift();
559
+ return {
560
+ text: out.join("\n").trim(),
561
+ kind: sawNatspec ? raw.indexOf("/**") === 0 ? "natspec-block" : "natspec-line" : "line"
562
+ };
563
+ };
564
+ const natSpecTags = (text) => {
565
+ const tags = [];
566
+ for (const line of text.split("\n")) {
567
+ const t = line.trim();
568
+ if (!t.startsWith("@")) continue;
569
+ let end = 1;
570
+ while (end < t.length) {
571
+ const c = t.charAt(end);
572
+ if (c === " " || c === " ") break;
573
+ end++;
574
+ }
575
+ tags.push(t.slice(0, end));
576
+ }
577
+ return tags;
578
+ };
579
+ const docByNode = /* @__PURE__ */ new Map();
580
+ let commentsRead = 0;
581
+ let commentsAttached = 0;
582
+ let documentedSymbols = 0;
583
+ let symbolsWithComplexity = 0;
584
+ let natSpecUndocumentedParams = 0;
585
+ let natSpecOverDocumentedParams = 0;
586
+ let natSpecReturnWithoutReturn = 0;
587
+ let symbolsWithUndocumentedParams = 0;
588
+ let symbolsWithOverDocumentedParams = 0;
589
+ const overDocumentedSamples = [];
590
+ const returnWithoutReturnSamples = [];
591
+ let symbolsWithQualifiedCalls = 0;
592
+ {
593
+ const attachRun = (run, anchor) => {
594
+ if (run.length === 0 || anchor === null) return;
595
+ if (!DECLARATION_NODE_TYPES.has(anchor.type)) return;
596
+ const parts = [];
597
+ let kind = "line";
598
+ for (const c of run) {
599
+ const read = readNatSpec(c.text);
600
+ if (read.text === "") continue;
601
+ parts.push(read.text);
602
+ if (read.kind !== "line") kind = read.kind;
603
+ commentsAttached++;
604
+ }
605
+ if (parts.length === 0) return;
606
+ const prior = docByNode.get(anchor.id);
607
+ if (prior === void 0) {
608
+ docByNode.set(anchor.id, { text: parts.join("\n"), kind, parts });
609
+ } else {
610
+ prior.parts.push(...parts);
611
+ prior.text = prior.parts.join("\n");
612
+ if (kind !== "line") prior.kind = kind;
613
+ }
614
+ };
615
+ const stack = [rootNode];
616
+ while (stack.length > 0) {
617
+ const p = stack.pop();
618
+ for (let i = 0; i < p.childCount; i++) {
619
+ const c = p.child(i);
620
+ if (c) stack.push(c);
621
+ }
622
+ let run = [];
623
+ let anchor = null;
624
+ const flush = () => {
625
+ if (run.length > 0) attachRun(run, anchor);
626
+ run = [];
627
+ anchor = null;
628
+ };
629
+ for (let i = 0; i < p.namedChildCount; i++) {
630
+ const c = p.namedChild(i);
631
+ if (!c) continue;
632
+ if (c.type === "comment") {
633
+ commentsRead++;
634
+ const last = run.length > 0 ? run[run.length - 1] : null;
635
+ if (last !== null && c.startPosition.row > last.endPosition.row + 1) flush();
636
+ run.push(c);
637
+ continue;
638
+ }
639
+ anchor = c;
640
+ flush();
641
+ }
642
+ flush();
643
+ }
644
+ }
645
+ const recoveryLines = /* @__PURE__ */ new Set();
646
+ {
647
+ const stack = [rootNode];
648
+ while (stack.length > 0) {
649
+ const n = stack.pop();
650
+ if (n.type === "ERROR" || n.isMissing) {
651
+ for (let r = n.startPosition.row + 1; r <= n.endPosition.row + 1; r++) recoveryLines.add(r);
652
+ }
653
+ for (let i = 0; i < n.childCount; i++) {
654
+ const c = n.child(i);
655
+ if (c) stack.push(c);
656
+ }
657
+ }
658
+ }
659
+ const symbols = [];
660
+ const relationships = [];
661
+ const imports = [];
662
+ const exports = [];
663
+ const relKeys = /* @__PURE__ */ new Set();
664
+ const frameworks = /* @__PURE__ */ new Set(["Solidity"]);
665
+ const patterns = /* @__PURE__ */ new Set();
666
+ const fileName = this.basenameOf(filePath);
667
+ const lines = content.split("\n");
668
+ const licence = readSolcLicence(rootNode);
669
+ symbols.push({
670
+ name: fileName,
671
+ kind: ASTEngine.SymbolKind.Module,
672
+ range: this.nodeToRange(rootNode),
673
+ visibility: "public",
674
+ ...licence !== null ? { license: licence } : {},
675
+ metadata: {
676
+ soliditySpecific: true,
677
+ isCompilationUnit: true,
678
+ ...licence !== null ? { license: licence } : {}
679
+ }
680
+ });
681
+ let cyclomaticComplexity = 1;
682
+ const frames = [{ owner: fileName, scope: fileName, containerLine: 1, yul: false, branches: 0, sym: null, qualifiedCalls: [], bindings: /* @__PURE__ */ new Set() }];
683
+ const top = () => frames[frames.length - 1];
684
+ const atFileScope = () => frames.length === 1;
685
+ const nodeKey = (n) => `${n.startIndex}:${n.endIndex}`;
686
+ const openedFrames = /* @__PURE__ */ new Set();
687
+ const interfaceNames = /* @__PURE__ */ new Set();
688
+ const typeNamesInFile = /* @__PURE__ */ new Set();
689
+ const membersByType = /* @__PURE__ */ new Map();
690
+ const basesByContract = /* @__PURE__ */ new Map();
691
+ const modifierNamesInFile = /* @__PURE__ */ new Set();
692
+ const declaredTypeNamesForCalls = /* @__PURE__ */ new Set();
693
+ const declaredVariableTypes = /* @__PURE__ */ new Map();
694
+ const noteVariableType = (name, type) => {
695
+ if (name === "" || type === void 0 || type === "") return;
696
+ const set = declaredVariableTypes.get(name);
697
+ if (set === void 0) declaredVariableTypes.set(name, /* @__PURE__ */ new Set([type]));
698
+ else set.add(type);
699
+ };
700
+ const containerElementTypes = /* @__PURE__ */ new Map();
701
+ const noteContainerElementType = (name, typeNode) => {
702
+ if (!typeNode || name === "") return;
703
+ const text = skipLeadingSpace(typeNode.text).trim();
704
+ if (!text.startsWith("mapping") && !text.endsWith("]")) return;
705
+ const udts = findDescendants(typeNode, "user_defined_type");
706
+ const last = udts[udts.length - 1];
707
+ const head = udtName(last);
708
+ if (head !== "") containerElementTypes.set(name, head);
709
+ };
710
+ let projectResolutionTables = null;
711
+ const projectTables = () => {
712
+ if (projectResolutionTables === null) projectResolutionTables = buildResolutionTables(projectIndex);
713
+ return projectResolutionTables;
714
+ };
715
+ const overrideCandidates = [];
716
+ const topLevelExports = [];
717
+ const importEdges = [];
718
+ let engineCallsSuppressed = 0;
719
+ const storeRefusedNames = /* @__PURE__ */ new Set();
720
+ let storeRefusedDeclarations = 0;
721
+ let symbolsSuppressedOnRecoveryLines = 0;
722
+ let getterSymbols = 0;
723
+ let extendsUnresolvedAsInterface = 0;
724
+ const evidenceClassFor = (type, isImport = false, confidence = 1) => {
725
+ if (isImport) return "import_based";
726
+ switch (type) {
727
+ case ASTEngine.RelationshipType.Contains:
728
+ case ASTEngine.RelationshipType.Extends:
729
+ case ASTEngine.RelationshipType.Implements:
730
+ case ASTEngine.RelationshipType.Exports:
731
+ case ASTEngine.RelationshipType.Instantiates:
732
+ return "structural";
733
+ case ASTEngine.RelationshipType.Overrides:
734
+ return "inferred";
735
+ default:
736
+ return "usage_contextual";
737
+ }
738
+ };
739
+ const caseAudit = process.env.BB_SOLIDITY_CASE_AUDIT === "1";
740
+ const __hits = {};
741
+ const __emits = {};
742
+ let __cur = "(pre)";
743
+ const addRel = (source, target, type, confidence, line, metadata = {}, opts = {}) => {
744
+ if (caseAudit) __emits[__cur] = (__emits[__cur] ?? 0) + 1;
745
+ if (!source || !target) return;
746
+ if (opts.bindingTarget) {
747
+ const at = target.lastIndexOf(BINDING_SEP);
748
+ if (at <= 0 || at + BINDING_SEP.length >= target.length) return;
749
+ if (target.indexOf(BINDING_SEP) !== at) return;
750
+ const ownerPart = target.slice(0, at);
751
+ const leafPart = target.slice(at + BINDING_SEP.length);
752
+ if (!isPlausibleSymbolName(ownerPart) || !isPlausibleSymbolName(leafPart)) return;
753
+ } else if (!opts.pathTarget && !isPlausibleSymbolName(target)) return;
754
+ if (!Number.isFinite(line) || line < 1) return;
755
+ if (recoveryLines.has(line)) return;
756
+ if (isReservedTarget(type, target, metadata)) return;
757
+ if (type === ASTEngine.RelationshipType.Calls && ENGINE_FUNCTION_NAMES.has(target)) {
758
+ engineCallsSuppressed++;
759
+ return;
760
+ }
761
+ const key = `${source}\0${type}\0${opts.dedupeAs ?? target}\0${line}`;
762
+ if (relKeys.has(key)) return;
763
+ relKeys.add(key);
764
+ const isImport = metadata.import === true;
765
+ relationships.push({
766
+ source,
767
+ target,
768
+ type,
769
+ confidence,
770
+ line_number: line,
771
+ // Caller metadata LAST so an explicit `evidenceClass` at a call site wins.
772
+ metadata: { evidenceClass: evidenceClassFor(type, isImport, confidence), soliditySpecific: true, ...metadata }
773
+ });
774
+ };
775
+ const symbolMap = /* @__PURE__ */ new Map();
776
+ const pushSymbol = (name, kind, node, meta = {}, parameters) => {
777
+ const clean = name.trim();
778
+ if (clean === "") return null;
779
+ const line = node.startPosition.row + 1;
780
+ const doc = docByNode.get(node.id);
781
+ if (doc !== void 0 && meta.docstring === void 0) {
782
+ meta.docstring = doc.text;
783
+ meta.docstringKind = doc.kind;
784
+ meta.comments = doc.parts;
785
+ const tags = natSpecTags(doc.text);
786
+ if (tags.length > 0) meta.docTags = tags;
787
+ documentedSymbols++;
788
+ if (parameters !== void 0 && Array.isArray(parameters)) {
789
+ const check = natSpecParamCheck(
790
+ doc.text,
791
+ parameters.map((pp) => pp.name).filter((n) => typeof n === "string"),
792
+ typeof meta.returnType === "string" && meta.returnType !== ""
793
+ );
794
+ if (check.undocumented.length > 0) {
795
+ meta.undocumentedParameters = check.undocumented;
796
+ natSpecUndocumentedParams += check.undocumented.length;
797
+ symbolsWithUndocumentedParams++;
798
+ }
799
+ if (check.overDocumented.length > 0) {
800
+ meta.overDocumentedParameters = check.overDocumented;
801
+ natSpecOverDocumentedParams += check.overDocumented.length;
802
+ symbolsWithOverDocumentedParams++;
803
+ overDocumentedSamples.push(`${fileName}:${line} ${clean} documents @param ${check.overDocumented.join(", ")}`);
804
+ }
805
+ meta.returnDocumentedWithoutReturn = check.returnWithoutReturn;
806
+ if (check.returnWithoutReturn) {
807
+ natSpecReturnWithoutReturn++;
808
+ returnWithoutReturnSamples.push(`${fileName}:${line} ${clean} has @return but returns nothing`);
809
+ }
810
+ }
811
+ }
812
+ const owner = top().owner;
813
+ const key = `${owner}\0${clean}\0${kind}`;
814
+ const existing = symbolMap.get(key);
815
+ if (existing) return existing;
816
+ const sym = {
817
+ name: clean,
818
+ kind,
819
+ range: this.nodeToRange(node),
820
+ scope: owner,
821
+ ownerName: owner,
822
+ visibility: meta.visibility ?? "public",
823
+ parameters,
824
+ // SOL-16 — top-level as well as in metadata, because `docstring` is a
825
+ // FIRST-CLASS field on UniversalSymbol and the store reads `s.docstring`
826
+ // (pool-worker.ts:673). Metadata alone left `brain_symbol.docstring` NULL for
827
+ // every Solidity symbol while the analyzer believed it had published
828
+ // documentation — the field existed in the payload and nothing read it.
829
+ // SOL-17 — first-class symbol field, so it crosses the worker boundary and
830
+ // reaches the store rather than living only in metadata.
831
+ ...Array.isArray(meta.decorators) ? { decorators: meta.decorators } : {},
832
+ // SOL-18 — `ownerKind` is a first-class symbol field (`pool-worker` forwards
833
+ // it), so it is set top-level as well as in metadata, for the same reason
834
+ // `docstring` is.
835
+ ...typeof meta.ownerKind === "string" ? { ownerKind: meta.ownerKind } : {},
836
+ // SOL-23 — top-level as well, because `parseFile` returns every symbol with
837
+ // `metadata: undefined` (pool-worker drops the object at the worker boundary),
838
+ // so a value only in metadata is invisible to a fixture — which is exactly how
839
+ // the first version of this feature looked like it had never run.
840
+ ...typeof meta.stateLocation === "string" ? { stateLocation: meta.stateLocation } : {},
841
+ ...Array.isArray(meta.undocumentedParameters) ? { undocumentedParameters: meta.undocumentedParameters } : {},
842
+ ...Array.isArray(meta.overDocumentedParameters) ? { overDocumentedParameters: meta.overDocumentedParameters } : {},
843
+ ...typeof meta.returnDocumentedWithoutReturn === "boolean" ? { returnDocumentedWithoutReturn: meta.returnDocumentedWithoutReturn } : {},
844
+ ...typeof meta.docstring === "string" ? { docstring: meta.docstring } : {},
845
+ // Each of these is read out of `meta`, which is `Record<string, unknown>`, so
846
+ // each is narrowed on its own rather than carried along by the docstring check:
847
+ // `meta.docstringKind` being a string says nothing about `meta.docTags`. The
848
+ // runtime shapes are unchanged -- only the proof that they hold is now stated.
849
+ ...typeof meta.docstringKind === "string" ? { docstringKind: meta.docstringKind } : {},
850
+ ...Array.isArray(meta.docTags) ? { docTags: meta.docTags } : {},
851
+ // Also top-level: pool-worker's parseFile return shape does not carry
852
+ // `metadata` on symbols at all, so anything only in metadata is invisible
853
+ // to every consumer of this API — which is how `docstringKind` read `-`
854
+ // on the first successful run of this feature.
855
+ ...Array.isArray(meta.comments) ? { comments: meta.comments } : {},
856
+ metadata: { soliditySpecific: true, ...meta }
857
+ };
858
+ if (typeof meta.isAbstract === "boolean") sym.isAbstract = meta.isAbstract;
859
+ if (typeof meta.returnType === "string" && meta.returnType) sym.returnType = meta.returnType;
860
+ symbols.push(sym);
861
+ symbolMap.set(key, sym);
862
+ return sym;
863
+ };
864
+ const declareSymbol = (name, kind, node, line, meta = {}, parameters, containsLine = line) => {
865
+ const clean = name.trim();
866
+ if (clean === "") return null;
867
+ const doc = docByNode.get(node.id);
868
+ if (doc !== void 0 && meta.docstring === void 0) {
869
+ meta.docstring = doc.text;
870
+ meta.docstringKind = doc.kind;
871
+ meta.comments = doc.parts;
872
+ const tags = natSpecTags(doc.text);
873
+ if (tags.length > 0) meta.docTags = tags;
874
+ documentedSymbols++;
875
+ if (parameters !== void 0 && Array.isArray(parameters)) {
876
+ const check = natSpecParamCheck(
877
+ doc.text,
878
+ parameters.map((pp) => pp.name).filter((n) => typeof n === "string"),
879
+ typeof meta.returnType === "string" && meta.returnType !== ""
880
+ );
881
+ if (check.undocumented.length > 0) {
882
+ meta.undocumentedParameters = check.undocumented;
883
+ natSpecUndocumentedParams += check.undocumented.length;
884
+ symbolsWithUndocumentedParams++;
885
+ }
886
+ if (check.overDocumented.length > 0) {
887
+ meta.overDocumentedParameters = check.overDocumented;
888
+ natSpecOverDocumentedParams += check.overDocumented.length;
889
+ symbolsWithOverDocumentedParams++;
890
+ overDocumentedSamples.push(`${fileName}:${line} ${clean} documents @param ${check.overDocumented.join(", ")}`);
891
+ }
892
+ meta.returnDocumentedWithoutReturn = check.returnWithoutReturn;
893
+ if (check.returnWithoutReturn) {
894
+ natSpecReturnWithoutReturn++;
895
+ returnWithoutReturnSamples.push(`${fileName}:${line} ${clean} has @return but returns nothing`);
896
+ }
897
+ }
898
+ }
899
+ if (STORE_REFUSED_SYMBOL_NAMES.has(clean)) {
900
+ storeRefusedDeclarations++;
901
+ storeRefusedNames.add(clean);
902
+ return null;
903
+ }
904
+ if (recoveryLines.has(line)) {
905
+ symbolsSuppressedOnRecoveryLines++;
906
+ return null;
907
+ }
908
+ const ownerKindName = typeKindByName.get(top().owner);
909
+ if (ownerKindName !== void 0 && meta.ownerKind === void 0) {
910
+ meta.ownerKind = ownerKindName;
911
+ }
912
+ if (kind === ASTEngine.SymbolKind.Class || kind === ASTEngine.SymbolKind.Interface) {
913
+ const kw = typeof meta.declarationKeyword === "string" ? meta.declarationKeyword : "";
914
+ typeKindByName.set(
915
+ clean,
916
+ kind === ASTEngine.SymbolKind.Interface ? "interface" : kw === "library" ? "library" : "contract"
917
+ );
918
+ }
919
+ const key = `${top().owner}\0${clean}\0${kind}`;
920
+ if (symbolMap.has(key)) return null;
921
+ const pushed = pushSymbol(clean, kind, node, meta, parameters);
922
+ const owner = top().owner;
923
+ if (owner && owner !== clean) {
924
+ addRel(owner, clean, ASTEngine.RelationshipType.Contains, 1, containsLine ?? line, {});
925
+ }
926
+ return pushed;
927
+ };
928
+ const findChildren = (node, type) => {
929
+ const out = [];
930
+ if (!node) return out;
931
+ for (let i = 0; i < node.namedChildCount; i++) {
932
+ const c = node.namedChild(i);
933
+ if (c && c.type === type) out.push(c);
934
+ }
935
+ return out;
936
+ };
937
+ const findFirstChild = (node, type) => {
938
+ const found = findChildren(node, type);
939
+ return found.length > 0 ? found[0] : null;
940
+ };
941
+ const findDescendant = (node, type) => {
942
+ if (!node) return null;
943
+ const work2 = [node];
944
+ while (work2.length > 0) {
945
+ const n = work2.shift();
946
+ if (n.type === type) return n;
947
+ for (let i = 0; i < n.namedChildCount; i++) {
948
+ const c = n.namedChild(i);
949
+ if (c) work2.push(c);
950
+ }
951
+ }
952
+ return null;
953
+ };
954
+ const findDescendants = (root, type) => {
955
+ const out = [];
956
+ const work2 = [root];
957
+ while (work2.length > 0) {
958
+ const n = work2.pop();
959
+ if (n !== root && n.type === type) out.push(n);
960
+ for (let i = 0; i < n.namedChildCount; i++) {
961
+ const c = n.namedChild(i);
962
+ if (c) work2.push(c);
963
+ }
964
+ }
965
+ return out;
966
+ };
967
+ const udtName = (node) => {
968
+ if (!node) return "";
969
+ if (node.type === "identifier") return node.text;
970
+ if (node.type !== "user_defined_type") return "";
971
+ for (let i = 0; i < node.namedChildCount; i++) {
972
+ const c = node.namedChild(i);
973
+ if (c && c.type === "identifier") return c.text;
974
+ }
975
+ return "";
976
+ };
977
+ const typeTextOf = (node) => {
978
+ if (!node) return "";
979
+ const udt = findFirstChild(node, "user_defined_type");
980
+ if (udt) return udtName(udt);
981
+ return skipLeadingSpace(node.text).trim();
982
+ };
983
+ const calleeNameOf = (node, depth = 0, viaMember = false) => {
984
+ if (!node || depth > 6) return null;
985
+ const line = node.startPosition.row + 1;
986
+ switch (node.type) {
987
+ case "identifier":
988
+ return { name: node.text, line, viaMember };
989
+ case "member_expression": {
990
+ const prop = node.childForFieldName("property");
991
+ return prop ? calleeNameOf(prop, depth + 1, true) : null;
992
+ }
993
+ case "struct_expression":
994
+ return calleeNameOf(node.childForFieldName("type"), depth + 1, viaMember);
995
+ case "user_defined_type": {
996
+ const n = udtName(node);
997
+ return n === "" ? null : { name: n, line, viaMember };
998
+ }
999
+ case "array_access":
1000
+ return calleeNameOf(node.childForFieldName("base"), depth + 1, viaMember);
1001
+ case "expression":
1002
+ case "payable_conversion_expression":
1003
+ case "parenthesized_expression": {
1004
+ for (let i = 0; i < node.namedChildCount; i++) {
1005
+ const c = node.namedChild(i);
1006
+ if (!c) continue;
1007
+ const got = calleeNameOf(c, depth + 1, viaMember);
1008
+ if (got) return got;
1009
+ }
1010
+ return null;
1011
+ }
1012
+ default:
1013
+ return null;
1014
+ }
1015
+ };
1016
+ const innerCalleeOf = (node, depth = 0) => {
1017
+ if (!node || depth > 6) return null;
1018
+ if (node.type === "identifier" || node.type === "member_expression" || node.type === "user_defined_type") return node;
1019
+ if (node.type === "struct_expression") {
1020
+ return innerCalleeOf(node.childForFieldName("type"), depth + 1);
1021
+ }
1022
+ for (let i = 0; i < node.namedChildCount; i++) {
1023
+ const c = node.namedChild(i);
1024
+ if (!c) continue;
1025
+ const got = innerCalleeOf(c, depth + 1);
1026
+ if (got) return got;
1027
+ }
1028
+ return null;
1029
+ };
1030
+ const memberObjectName = (node) => {
1031
+ const obj = node.childForFieldName("object");
1032
+ if (!obj) return "";
1033
+ if (obj.type === "identifier") return obj.text;
1034
+ if (obj.type === "expression" && obj.namedChildCount > 0) {
1035
+ const c = obj.namedChild(0);
1036
+ if (c && c.type === "identifier") return c.text;
1037
+ }
1038
+ return "";
1039
+ };
1040
+ const conversionTypeOf = (node) => {
1041
+ if (!node) return "";
1042
+ if (node.type === "expression" && node.namedChildCount > 0) return conversionTypeOf(node.namedChild(0));
1043
+ if (node.type !== "call_expression") return "";
1044
+ const fn = node.childForFieldName("function") ?? node.namedChild(0);
1045
+ const inner = fn && fn.type === "expression" && fn.namedChildCount > 0 ? fn.namedChild(0) : fn;
1046
+ if (!inner) return "";
1047
+ if (inner.type === "identifier") return inner.text;
1048
+ if (inner.type === "user_defined_type") return udtName(inner);
1049
+ return "";
1050
+ };
1051
+ const indexedBaseOf = (node) => {
1052
+ if (!node) return "";
1053
+ if (node.type === "expression" && node.namedChildCount > 0) return indexedBaseOf(node.namedChild(0));
1054
+ if (node.type !== "array_access") return "";
1055
+ const base = node.namedChild(0);
1056
+ if (!base) return "";
1057
+ if (base.type === "identifier") return base.text;
1058
+ if (base.type === "expression" && base.namedChildCount > 0) {
1059
+ const c = base.namedChild(0);
1060
+ if (c && c.type === "identifier") return c.text;
1061
+ }
1062
+ return "";
1063
+ };
1064
+ const isClaimedCallee = (node) => {
1065
+ let cur = node;
1066
+ for (let hop = 0; hop < 6 && cur; hop++) {
1067
+ if (claimedCallees.has(cur.id)) return true;
1068
+ cur = cur.parent;
1069
+ }
1070
+ return false;
1071
+ };
1072
+ const isClaimedCalleeDirect = (node) => {
1073
+ const parent = node.parent;
1074
+ return parent !== null && claimedCallees.has(parent.id);
1075
+ };
1076
+ const isEngineMemberObject = (object) => {
1077
+ if (!object) return false;
1078
+ if (object.type === "identifier" && GLOBAL_OBJECTS.has(object.text)) return true;
1079
+ if (object.type === "meta_type_expression") return true;
1080
+ if (object.type === "expression" && object.namedChildCount > 0) {
1081
+ const c = object.namedChild(0);
1082
+ if (c && c.type === "meta_type_expression") return true;
1083
+ if (c && c.type === "identifier" && GLOBAL_OBJECTS.has(c.text)) return true;
1084
+ }
1085
+ return false;
1086
+ };
1087
+ const collectParameters = (node) => {
1088
+ const lists = [];
1089
+ for (let i = 0; i < node.namedChildCount; i++) {
1090
+ const c = node.namedChild(i);
1091
+ if (!c) continue;
1092
+ if (c.type === "parameter") lists.push(c);
1093
+ else if (c.type === "return_type_definition") lists.push(...findChildren(c, "parameter"));
1094
+ }
1095
+ const out = [];
1096
+ for (const p of lists) {
1097
+ const nameNode = p.childForFieldName("name");
1098
+ if (!nameNode) continue;
1099
+ out.push({ name: nameNode.text, type: typeTextOf(p.childForFieldName("type")) });
1100
+ }
1101
+ return out;
1102
+ };
1103
+ const modifiersOf = (node) => {
1104
+ const meta = {};
1105
+ const hasChildType = (t) => {
1106
+ for (let i = 0; i < node.childCount; i++) {
1107
+ const c = node.child(i);
1108
+ if (c && c.type === t) return true;
1109
+ }
1110
+ return false;
1111
+ };
1112
+ for (let i = 0; i < node.childCount; i++) {
1113
+ const c = node.child(i);
1114
+ if (!c) continue;
1115
+ if (c.type === "state_mutability") meta.stateMutability = c.text;
1116
+ else if (c.type === "visibility") meta.visibility = c.text;
1117
+ else if (c.type === "virtual") meta.isVirtual = true;
1118
+ else if (c.type === "override_specifier") meta.hasOverrideSpecifier = true;
1119
+ else if (c.type === "constant") meta.isConstant = true;
1120
+ else if (c.type === "immutable") meta.isImmutable = true;
1121
+ }
1122
+ const first = node.childCount > 0 ? node.child(0) : null;
1123
+ meta.isAbstract = first !== null && first.type === "abstract";
1124
+ const ret = node.childForFieldName("return_type");
1125
+ if (ret) {
1126
+ const types = findChildren(ret, "parameter").map((p) => typeTextOf(p.childForFieldName("type")));
1127
+ if (types.length === 1) meta.returnType = types[0];
1128
+ else if (types.length > 1) meta.returnType = `(${types.join(", ")})`;
1129
+ }
1130
+ meta.hasBody = findFirstChild(node, "function_body") !== null;
1131
+ const applied = [];
1132
+ for (const inv of findChildren(node, "modifier_invocation")) {
1133
+ const id = findFirstChild(inv, "identifier");
1134
+ if (id) applied.push(id.text);
1135
+ }
1136
+ if (applied.length > 0) meta.decorators = applied;
1137
+ return meta;
1138
+ };
1139
+ const noteMember = (owner, name) => {
1140
+ if (owner === fileName || owner === "") return;
1141
+ let set = membersByType.get(owner);
1142
+ if (!set) {
1143
+ set = /* @__PURE__ */ new Set();
1144
+ membersByType.set(owner, set);
1145
+ }
1146
+ set.add(name);
1147
+ };
1148
+ const isBranchNode = (node) => {
1149
+ switch (node.type) {
1150
+ case "if_statement":
1151
+ case "for_statement":
1152
+ case "while_statement":
1153
+ case "do_while_statement":
1154
+ case "ternary_expression":
1155
+ case "catch_clause":
1156
+ case "yul_if_statement":
1157
+ case "yul_switch_statement":
1158
+ return true;
1159
+ case "binary_expression": {
1160
+ for (let i = 0; i < node.childCount; i++) {
1161
+ const c = node.child(i);
1162
+ if (c && (c.type === "&&" || c.type === "||")) return true;
1163
+ }
1164
+ return false;
1165
+ }
1166
+ default:
1167
+ return false;
1168
+ }
1169
+ };
1170
+ const openFrame = (node, owner, scope, line, yul = false, isType = false, sym = null) => {
1171
+ openedFrames.add(nodeKey(node));
1172
+ const CALLABLE_DEFINITION_TYPES = /* @__PURE__ */ new Set([
1173
+ "function_definition",
1174
+ "constructor_definition",
1175
+ "fallback_receive_definition",
1176
+ "modifier_definition"
1177
+ ]);
1178
+ let bindings = /* @__PURE__ */ new Set();
1179
+ if (CALLABLE_DEFINITION_TYPES.has(node.type)) {
1180
+ for (const p of collectParameters(node)) {
1181
+ bindings.add(p.name);
1182
+ noteVariableType(p.name, p.type);
1183
+ }
1184
+ }
1185
+ frames.push({ owner, scope, containerLine: line, yul, isType, branches: 1, sym, qualifiedCalls: [], bindings });
1186
+ };
1187
+ const bindingOwnerOf = (name) => {
1188
+ for (let i = frames.length - 1; i >= 1; i--) {
1189
+ if (frames[i].bindings.has(name)) return frames[i].scope;
1190
+ }
1191
+ return null;
1192
+ };
1193
+ const enclosingTypeOf = () => {
1194
+ for (let i = frames.length - 1; i >= 1; i--) if (frames[i].isType) return frames[i].owner;
1195
+ return "";
1196
+ };
1197
+ {
1198
+ const prepass = [rootNode];
1199
+ while (prepass.length > 0) {
1200
+ const n = prepass.pop();
1201
+ if (n.type === "ERROR" || n.isMissing) continue;
1202
+ switch (n.type) {
1203
+ case "modifier_definition": {
1204
+ const nameNode = n.childForFieldName("name");
1205
+ if (nameNode) modifierNamesInFile.add(nameNode.text);
1206
+ break;
1207
+ }
1208
+ case "contract_declaration":
1209
+ case "interface_declaration":
1210
+ case "library_declaration":
1211
+ case "struct_declaration":
1212
+ case "enum_declaration":
1213
+ case "user_defined_type_definition": {
1214
+ const nameNode = n.childForFieldName("name");
1215
+ if (nameNode) declaredTypeNamesForCalls.add(nameNode.text);
1216
+ break;
1217
+ }
1218
+ }
1219
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
1220
+ const c = n.namedChild(i);
1221
+ if (c) prepass.push(c);
1222
+ }
1223
+ }
1224
+ }
1225
+ const work = [{ node: rootNode, exit: false }];
1226
+ const claimedCallees = /* @__PURE__ */ new Set();
1227
+ while (work.length > 0) {
1228
+ const { node, exit } = work.pop();
1229
+ if (exit) {
1230
+ if (openedFrames.has(nodeKey(node))) {
1231
+ const done = frames.pop();
1232
+ if (done.sym !== null) {
1233
+ done.sym.metadata.complexity = done.branches;
1234
+ done.sym.complexity = done.branches;
1235
+ symbolsWithComplexity++;
1236
+ if (done.qualifiedCalls.length > 0) {
1237
+ const uniq = [...new Set(done.qualifiedCalls)].sort();
1238
+ done.sym.qualified_calls = uniq;
1239
+ done.sym.qualifiedCalls = uniq;
1240
+ done.sym.metadata.qualifiedCalls = uniq;
1241
+ symbolsWithQualifiedCalls++;
1242
+ }
1243
+ }
1244
+ openedFrames.delete(nodeKey(node));
1245
+ }
1246
+ continue;
1247
+ }
1248
+ if (node.type === "ERROR" || node.isMissing) continue;
1249
+ if (isBranchNode(node)) {
1250
+ cyclomaticComplexity++;
1251
+ top().branches++;
1252
+ }
1253
+ let descend = true;
1254
+ if (caseAudit) {
1255
+ __hits[node.type] = (__hits[node.type] ?? 0) + 1;
1256
+ __cur = node.type;
1257
+ }
1258
+ switch (node.type) {
1259
+ // ----------------------------------------------------- file structure
1260
+ case "source_file":
1261
+ break;
1262
+ case "pragma_directive": {
1263
+ patterns.add(`pragma:${skipLeadingSpace(node.text).slice(0, 40)}`);
1264
+ break;
1265
+ }
1266
+ case "import_directive": {
1267
+ const srcNode = node.childForFieldName("source");
1268
+ const rawPath = srcNode ? unquote(skipLeadingSpace(srcNode.text)) : "";
1269
+ const alias = node.childForFieldName("alias");
1270
+ const braced = [];
1271
+ let braceDepth = 0;
1272
+ for (let i = 0; i < node.childCount; i++) {
1273
+ const c = node.child(i);
1274
+ if (!c) continue;
1275
+ if (c.type === "{") braceDepth++;
1276
+ else if (c.type === "}") braceDepth--;
1277
+ else if (braceDepth === 1 && c.type === "identifier") braced.push(c.text);
1278
+ }
1279
+ const anySource = findFirstChild(node, "any_source_type") !== null;
1280
+ const plainName = node.childForFieldName("import_name");
1281
+ const line = node.startPosition.row + 1;
1282
+ const names = braced.length > 0 ? braced : plainName ? [plainName.text] : [];
1283
+ for (const n of names) importEdges.push({ name: n, path: rawPath, line });
1284
+ if (names.length === 0 && alias) {
1285
+ importEdges.push({ name: alias.text, path: rawPath, line, isNamespace: true });
1286
+ } else if (names.length === 0 && !alias) {
1287
+ importEdges.push({ name: "*", path: rawPath, line });
1288
+ }
1289
+ imports.push({
1290
+ source: rawPath,
1291
+ imported: alias ? alias.text : anySource ? "*" : names[0] ?? rawPath,
1292
+ line,
1293
+ isDefault: names.length === 0 && !alias,
1294
+ metadata: { soliditySpecific: true, namedImports: names, isNamespaceImport: anySource }
1295
+ });
1296
+ break;
1297
+ }
1298
+ // ------------------------------------------------------ SOL-24: WITHDRAWN
1299
+ //
1300
+ // `using L for T` was published as `mixes_in` for part of 2026-10-27 and the
1301
+ // emission is WITHDRAWN. Why it looked right: the directive names a member set
1302
+ // and declares no conformance, so `extends`/`implements` are false and `uses`
1303
+ // (a name READ) seemed too weak.
1304
+ //
1305
+ // WHY IT IS FALSE, AND THE VOCABULARY'S OWN WORDS ARE THE TEST. `mixes_in`
1306
+ // (src/ast/types.ts) is "a class COMPOSES a mixin into its SUPERTYPE CHAIN …
1307
+ // the mixin is a supertype member" — the members enter the composing type, and
1308
+ // the whole fleet uses it that way: PHP trait `use` flattens the trait's
1309
+ // members into the class's method table (real-php-analyzer.ts isTraitUse), Go
1310
+ // embedding promotes the embedded type's methods and fields into the struct
1311
+ // (real-go-analyzer.ts), and the consumers of this value say so out loud
1312
+ // (src/intelligence/graph/graph-engine.ts ranks `mixes_in` as a supertype
1313
+ // composition, between `implements` and `renders`).
1314
+ //
1315
+ // SOLIDITY'S DIRECTIVE DOES NOT DO THAT. L's functions are never members of T:
1316
+ // no member is added to T's declared surface, T's ABI and inheritance chain are
1317
+ // untouched, and `t.f()` compiles as `L.f(t)` only where the directive is in
1318
+ // scope (`global` widening the scope, not the type). The composing ACTOR is the
1319
+ // SCOPE, not the type, so the row's source would be the wrong actor as well as
1320
+ // the wrong relation. Publishing it would have put `Price -> MathLib` in a
1321
+ // bucket two instruments read as "Price composes a supertype".
1322
+ //
1323
+ // THE DEPENDENCY IS NOT LOST: the `type_alias` case above already publishes the
1324
+ // library as a `references` edge, which is the true relation (the file read the
1325
+ // name). The construct population is measured, not dropped: 124 `using_directive`
1326
+ // sites in 76 files, of which 27 have a declared host and one library, 0 of which
1327
+ // add a member to the type — validation/m14/measurements/solidity-extended-census.json
1328
+ // and script/m14/tmp/sol24-mixes-counters.mts. The ruling and its falsifier live
1329
+ // in script/m14/spec/solidity-completeness.json (coverage.excluded.mixes_in).
1330
+ //
1331
+ // ⇒ A NODE TYPE WITH NO CASE HERE IS THE HONEST SHAPE OF THIS REFUSAL: an empty
1332
+ // `case` would look like a counter that was meant to fire.
1333
+ // ------------------------------------------------------ declarations
1334
+ case "contract_declaration":
1335
+ case "interface_declaration":
1336
+ case "library_declaration": {
1337
+ const nameNode = node.childForFieldName("name");
1338
+ if (!nameNode) break;
1339
+ const name = nameNode.text;
1340
+ const line = nameNode.startPosition.row + 1;
1341
+ const kind = node.type === "interface_declaration" ? ASTEngine.SymbolKind.Interface : ASTEngine.SymbolKind.Class;
1342
+ if (node.type === "interface_declaration") interfaceNames.add(name);
1343
+ typeNamesInFile.add(name);
1344
+ declareSymbol(name, kind, node, line, {
1345
+ ...modifiersOf(node),
1346
+ declarationKeyword: node.type.slice(0, -"_declaration".length)
1347
+ });
1348
+ if (atFileScope()) topLevelExports.push({ name, line });
1349
+ const bases = [];
1350
+ for (const spec of findChildren(node, "inheritance_specifier")) {
1351
+ const anc = spec.childForFieldName("ancestor");
1352
+ const baseName = udtName(anc);
1353
+ if (baseName === "") continue;
1354
+ bases.push(baseName);
1355
+ const isInterface = interfaceNames.has(baseName);
1356
+ if (!isInterface && typeNamesInFile.has(baseName)) {
1357
+ } else if (!isInterface && !typeNamesInFile.has(baseName) && projectIndex?.typeKinds.get(baseName) === void 0) {
1358
+ extendsUnresolvedAsInterface++;
1359
+ }
1360
+ const projectKind = isInterface || typeNamesInFile.has(baseName) ? void 0 : projectIndex?.typeKinds.get(baseName);
1361
+ if (projectKind !== void 0) inheritanceKindsResolvedProjectWide++;
1362
+ const projectIsInterface = projectKind === "interface";
1363
+ const kindUnresolved = !isInterface && !typeNamesInFile.has(baseName) && projectKind === void 0;
1364
+ addRel(
1365
+ name,
1366
+ baseName,
1367
+ isInterface || projectIsInterface ? ASTEngine.RelationshipType.Implements : ASTEngine.RelationshipType.Extends,
1368
+ kindUnresolved ? 0.9 : 1,
1369
+ (anc ?? spec).startPosition.row + 1,
1370
+ {
1371
+ isSolidityInheritance: true,
1372
+ interfaceResolvedInFile: isInterface,
1373
+ ...kindUnresolved ? {
1374
+ // The tier, stated at the site that knows the claim is undecided.
1375
+ evidenceClass: "inferred",
1376
+ kindUnresolved: true,
1377
+ assumedKind: ASTEngine.RelationshipType.Extends,
1378
+ reasonCode: "base-not-declared-in-file",
1379
+ // SOL-27 — AND THE CLAIM THE PROJECT CAN ANSWER. `kindUnresolved` means NEITHER
1380
+ // this file NOR the project index declares the base — the fact the arbiter checks
1381
+ // (a claim is consulted only where `decls === 0`). MEASURED: 38 unresolved
1382
+ // `extends`, led by `Test` 33 (forge-std's base contract, outside the corpus).
1383
+ externalTarget: true,
1384
+ externalVia: "base"
1385
+ } : {},
1386
+ // SOL-21 — how the kind was settled, so a consumer can tell a settled
1387
+ // cross-file kind from an in-file one. `undefined` for the in-file case,
1388
+ // which is the pre-existing shape and needs no new key.
1389
+ ...projectKind !== void 0 ? {
1390
+ kindResolvedProjectWide: true,
1391
+ baseDeclarationKind: projectKind
1392
+ } : {},
1393
+ ...spec.childForFieldName("ancestor_arguments") ? { hasBaseConstructorArgs: true } : {}
1394
+ }
1395
+ );
1396
+ }
1397
+ basesByContract.set(name, bases);
1398
+ openFrame(node, name, name, line, false, true);
1399
+ break;
1400
+ }
1401
+ case "struct_declaration": {
1402
+ const nameNode = node.childForFieldName("name");
1403
+ if (!nameNode) break;
1404
+ const name = nameNode.text;
1405
+ const line = nameNode.startPosition.row + 1;
1406
+ declareSymbol(name, ASTEngine.SymbolKind.Struct, node, line, { declarationKeyword: "struct" });
1407
+ if (atFileScope()) topLevelExports.push({ name, line });
1408
+ openFrame(node, name, top().scope, line);
1409
+ break;
1410
+ }
1411
+ case "enum_declaration": {
1412
+ const nameNode = node.childForFieldName("name");
1413
+ if (!nameNode) break;
1414
+ const name = nameNode.text;
1415
+ const line = nameNode.startPosition.row + 1;
1416
+ declareSymbol(name, ASTEngine.SymbolKind.Enum, node, line, { declarationKeyword: "enum" });
1417
+ if (atFileScope()) topLevelExports.push({ name, line });
1418
+ openFrame(node, name, top().scope, line);
1419
+ break;
1420
+ }
1421
+ // grammar.js:285 — `enum_body: commaSep(alias($.identifier, $.enum_value))`.
1422
+ // An `enum_value` has NO fields: the name IS the node's own text, because
1423
+ // the grammar aliased the identifier to the value node. Reading a `name:`
1424
+ // field here returns null and silently publishes nothing.
1425
+ case "enum_value": {
1426
+ const name = node.text.trim();
1427
+ if (name === "") break;
1428
+ const line = node.startPosition.row + 1;
1429
+ declareSymbol(name, ASTEngine.SymbolKind.EnumMember, node, line, { declarationKeyword: "enum value" });
1430
+ break;
1431
+ }
1432
+ case "struct_member": {
1433
+ const nameNode = node.childForFieldName("name");
1434
+ if (!nameNode) break;
1435
+ const line = nameNode.startPosition.row + 1;
1436
+ declareSymbol(nameNode.text, ASTEngine.SymbolKind.Property, node, line, {
1437
+ declarationKeyword: "struct member",
1438
+ declaredType: typeTextOf(node.childForFieldName("type"))
1439
+ });
1440
+ break;
1441
+ }
1442
+ case "function_definition": {
1443
+ const nameNode = node.childForFieldName("name");
1444
+ if (!nameNode) break;
1445
+ const name = nameNode.text;
1446
+ const line = nameNode.startPosition.row + 1;
1447
+ const kind = atFileScope() ? ASTEngine.SymbolKind.Function : ASTEngine.SymbolKind.Method;
1448
+ const meta = modifiersOf(node);
1449
+ const overrideSpec = findFirstChild(node, "override_specifier");
1450
+ if (overrideSpec) {
1451
+ const explicit = findChildren(overrideSpec, "user_defined_type").map((u) => udtName(u)).filter((s) => s !== "");
1452
+ const anyBase = basesByContract.get(top().owner) ?? [];
1453
+ const inFile = explicit.filter((b) => typeNamesInFile.has(b));
1454
+ meta.overrideExternal = inFile.length === 0;
1455
+ overrideCandidates.push({ memberName: name, owner: top().owner, line, explicit, anyBase });
1456
+ for (const b of explicit) {
1457
+ if (!typeNamesInFile.has(b)) {
1458
+ addRel(
1459
+ top().scope,
1460
+ b,
1461
+ ASTEngine.RelationshipType.References,
1462
+ 0.8,
1463
+ line,
1464
+ { isOverrideBase: true, overrideBaseExternal: true }
1465
+ );
1466
+ }
1467
+ }
1468
+ }
1469
+ const modSym = declareSymbol(name, kind, node, line, meta, collectParameters(node));
1470
+ noteMember(top().owner, name);
1471
+ openFrame(node, name, name, line, false, false, modSym);
1472
+ break;
1473
+ }
1474
+ case "modifier_definition": {
1475
+ const nameNode = node.childForFieldName("name");
1476
+ if (!nameNode) break;
1477
+ const name = nameNode.text;
1478
+ const line = nameNode.startPosition.row + 1;
1479
+ modifierNamesInFile.add(name);
1480
+ const meta = modifiersOf(node);
1481
+ if (findFirstChild(node, "override_specifier")) {
1482
+ meta.overrideExternal = true;
1483
+ overrideCandidates.push({
1484
+ memberName: name,
1485
+ owner: top().owner,
1486
+ line,
1487
+ explicit: [],
1488
+ anyBase: basesByContract.get(top().owner) ?? []
1489
+ });
1490
+ }
1491
+ const fnSym = declareSymbol(name, ASTEngine.SymbolKind.Method, node, line, meta, collectParameters(node));
1492
+ noteMember(top().owner, name);
1493
+ openFrame(node, name, name, line, false, false, fnSym);
1494
+ break;
1495
+ }
1496
+ // SOL-4 — the name is READ off the `constructor` keyword token. Unlike
1497
+ // PowerShell's `Deployment(...)`, Solidity writes the word, so this is not
1498
+ // a synthesised name and the line is the real name line.
1499
+ case "constructor_definition": {
1500
+ const line = node.startPosition.row + 1;
1501
+ const name = "constructor";
1502
+ const ctorSym = declareSymbol(
1503
+ name,
1504
+ ASTEngine.SymbolKind.Constructor,
1505
+ node,
1506
+ line,
1507
+ modifiersOf(node),
1508
+ collectParameters(node)
1509
+ );
1510
+ noteMember(top().owner, name);
1511
+ openFrame(node, name, name, line, false, false, ctorSym);
1512
+ break;
1513
+ }
1514
+ // SOL-4 — `receive` and `fallback` share ONE grammar rule
1515
+ // (fallback_receive_definition), split by the leading keyword token.
1516
+ case "fallback_receive_definition": {
1517
+ const first = node.childCount > 0 ? node.child(0) : null;
1518
+ const name = first !== null && first.type === "receive" ? "receive" : "fallback";
1519
+ const line = node.startPosition.row + 1;
1520
+ const fbSym = declareSymbol(
1521
+ name,
1522
+ ASTEngine.SymbolKind.Method,
1523
+ node,
1524
+ line,
1525
+ { ...modifiersOf(node), isFallbackOrReceive: true },
1526
+ collectParameters(node)
1527
+ );
1528
+ noteMember(top().owner, name);
1529
+ openFrame(node, name, name, line, false, false, fbSym);
1530
+ break;
1531
+ }
1532
+ case "event_definition": {
1533
+ const nameNode = node.childForFieldName("name");
1534
+ if (!nameNode) break;
1535
+ const name = nameNode.text;
1536
+ const line = nameNode.startPosition.row + 1;
1537
+ const paramNodes = findChildren(node, "event_parameter");
1538
+ const params = [];
1539
+ const indexed = [];
1540
+ let isAnonymous = false;
1541
+ for (let i = 0; i < node.childCount; i++) {
1542
+ const c = node.child(i);
1543
+ if (c && c.type === "anonymous") isAnonymous = true;
1544
+ }
1545
+ for (const p of paramNodes) {
1546
+ const n = p.childForFieldName("name");
1547
+ params.push({
1548
+ name: n ? n.text : "",
1549
+ type: typeTextOf(p.childForFieldName("type"))
1550
+ });
1551
+ for (let i = 0; i < p.childCount; i++) {
1552
+ const c = p.child(i);
1553
+ if (c && c.type === "indexed" && n) indexed.push(n.text);
1554
+ }
1555
+ }
1556
+ declareSymbol(name, ASTEngine.SymbolKind.Event, node, line, {
1557
+ declarationKeyword: "event",
1558
+ indexedParameters: indexed,
1559
+ isAnonymous
1560
+ }, params);
1561
+ noteMember(top().owner, name);
1562
+ if (atFileScope()) topLevelExports.push({ name, line });
1563
+ break;
1564
+ }
1565
+ // A custom error is a named, declared, ABI-visible entity with its own
1566
+ // parameter list. `Type` is the kind that says "a named type-ish
1567
+ // declaration" without claiming it is a class. Recorded as a decision in
1568
+ // the F9 spec, not an accident of convenience.
1569
+ case "error_declaration": {
1570
+ const nameNode = node.childForFieldName("name");
1571
+ if (!nameNode) break;
1572
+ const name = nameNode.text;
1573
+ const line = nameNode.startPosition.row + 1;
1574
+ const params = findChildren(node, "error_parameter").map((p) => ({
1575
+ name: p.childForFieldName("name")?.text ?? "",
1576
+ type: typeTextOf(p.childForFieldName("type"))
1577
+ }));
1578
+ declareSymbol(
1579
+ name,
1580
+ ASTEngine.SymbolKind.Type,
1581
+ node,
1582
+ line,
1583
+ { declarationKeyword: "error" },
1584
+ params
1585
+ );
1586
+ noteMember(top().owner, name);
1587
+ if (atFileScope()) topLevelExports.push({ name, line });
1588
+ break;
1589
+ }
1590
+ case "state_variable_declaration": {
1591
+ const nameNode = node.childForFieldName("name");
1592
+ if (!nameNode) break;
1593
+ const name = nameNode.text;
1594
+ const line = nameNode.startPosition.row + 1;
1595
+ const meta = modifiersOf(node);
1596
+ meta.declaredType = typeTextOf(node.childForFieldName("type"));
1597
+ noteVariableType(name, typeof meta.declaredType === "string" ? meta.declaredType : "");
1598
+ noteContainerElementType(name, node.childForFieldName("type"));
1599
+ if (node.childForFieldName("location")) {
1600
+ meta.stateLocation = node.childForFieldName("location").text;
1601
+ }
1602
+ if (STORE_REFUSED_SYMBOL_NAMES.has(name)) {
1603
+ storeRefusedDeclarations++;
1604
+ storeRefusedNames.add(name);
1605
+ break;
1606
+ }
1607
+ if (meta.visibility === "public") {
1608
+ meta.getterSynthesised = true;
1609
+ meta.getterVisibility = "public";
1610
+ meta.getterIsPayable = meta.stateMutability === "payable";
1611
+ const getterName = `${name}()`;
1612
+ const getter = pushSymbol(getterName, ASTEngine.SymbolKind.Method, node, {
1613
+ ...meta,
1614
+ isSynthesisedGetter: true,
1615
+ declarationKeyword: "public state variable getter",
1616
+ getterFor: name,
1617
+ ...meta.stateMutability === "view" || !meta.stateMutability ? { stateMutability: meta.stateMutability ?? "view" } : {}
1618
+ });
1619
+ if (getter) {
1620
+ getterSymbols++;
1621
+ const getterOwner = top().owner;
1622
+ if (getterOwner && getterOwner !== getterName) {
1623
+ addRel(getterOwner, getterName, ASTEngine.RelationshipType.Contains, 1, line, {
1624
+ isSynthesisedGetter: true,
1625
+ getterOfVariable: name
1626
+ });
1627
+ }
1628
+ addRel(
1629
+ name,
1630
+ getterName,
1631
+ ASTEngine.RelationshipType.References,
1632
+ 1,
1633
+ line,
1634
+ { isGetterFor: true, getterOfVariable: name }
1635
+ );
1636
+ }
1637
+ }
1638
+ declareSymbol(name, ASTEngine.SymbolKind.Property, node, line, meta);
1639
+ noteMember(top().owner, name);
1640
+ break;
1641
+ }
1642
+ // grammar.js:177 — a FILE-SCOPE-only declaration, one of the twelve
1643
+ // members of `_declaration`. Inside a contract the same source text is a
1644
+ // `state_variable_declaration`, which is why 352 of those and only 6 of
1645
+ // these exist in the corpus.
1646
+ case "constant_variable_declaration": {
1647
+ const nameNode = node.childForFieldName("name");
1648
+ if (!nameNode) break;
1649
+ const name = nameNode.text;
1650
+ const line = nameNode.startPosition.row + 1;
1651
+ declareSymbol(
1652
+ name,
1653
+ ASTEngine.SymbolKind.Constant,
1654
+ node,
1655
+ line,
1656
+ { ...modifiersOf(node), isConstant: true, declaredType: typeTextOf(node.childForFieldName("type")) }
1657
+ );
1658
+ topLevelExports.push({ name, line });
1659
+ break;
1660
+ }
1661
+ case "user_defined_type_definition": {
1662
+ const nameNode = node.childForFieldName("name");
1663
+ if (!nameNode) break;
1664
+ const name = nameNode.text;
1665
+ const line = nameNode.startPosition.row + 1;
1666
+ declareSymbol(name, ASTEngine.SymbolKind.TypeAlias, node, line, {
1667
+ declarationKeyword: "type",
1668
+ underlyingType: findFirstChild(node, "primitive_type")?.text ?? ""
1669
+ });
1670
+ typeNamesInFile.add(name);
1671
+ if (atFileScope()) topLevelExports.push({ name, line });
1672
+ break;
1673
+ }
1674
+ // ------------------------------------------------------------- locals
1675
+ case "variable_declaration": {
1676
+ const nameNode = node.childForFieldName("name");
1677
+ if (!nameNode) break;
1678
+ const line = nameNode.startPosition.row + 1;
1679
+ const isImmutable = findFirstChild(node, "immutable") !== null;
1680
+ const declaredType = typeTextOf(node.childForFieldName("type"));
1681
+ noteVariableType(nameNode.text, declaredType);
1682
+ noteContainerElementType(nameNode.text, node.childForFieldName("type"));
1683
+ declareSymbol(
1684
+ nameNode.text,
1685
+ isImmutable ? ASTEngine.SymbolKind.Constant : ASTEngine.SymbolKind.Variable,
1686
+ node,
1687
+ line,
1688
+ {
1689
+ declaredType,
1690
+ ...node.childForFieldName("value") ? { hasInitialiser: true } : {},
1691
+ isImmutable
1692
+ }
1693
+ );
1694
+ break;
1695
+ }
1696
+ // ----------------------------------------------------------------- Yul
1697
+ case "yul_variable_declaration": {
1698
+ const left = node.childForFieldName("left");
1699
+ if (!left) break;
1700
+ const names = left.type === "identifier" ? [left] : findDescendants(left, "identifier");
1701
+ for (const n of names) {
1702
+ declareSymbol(
1703
+ n.text,
1704
+ ASTEngine.SymbolKind.Variable,
1705
+ n,
1706
+ n.startPosition.row + 1,
1707
+ { isYul: true, declaredType: "yul" }
1708
+ );
1709
+ }
1710
+ break;
1711
+ }
1712
+ case "yul_function_definition": {
1713
+ const idents = findChildren(node, "yul_identifier");
1714
+ const nameNode = idents.length > 0 ? idents[0] : findDescendant(node, "yul_identifier");
1715
+ if (!nameNode) break;
1716
+ const name = nameNode.text;
1717
+ const line = nameNode.startPosition.row + 1;
1718
+ const params = idents.slice(1).map((p) => ({ name: p.text, type: "yul" }));
1719
+ declareSymbol(name, ASTEngine.SymbolKind.Function, node, line, { isYul: true }, params);
1720
+ noteMember(top().owner, name);
1721
+ openFrame(node, top().owner, name, line, true);
1722
+ for (const p of params) top().bindings.add(p.name);
1723
+ break;
1724
+ }
1725
+ // SOL-8 — a `yul_evm_builtin` is an OPCODE, not a function. The skip is
1726
+ // lossless: the lexer always spells a bare `add` as the builtin, so a
1727
+ // user-defined Yul function can never carry that name.
1728
+ case "yul_function_call": {
1729
+ const fn = node.childForFieldName("function");
1730
+ if (fn && fn.type === "yul_identifier") {
1731
+ addRel(
1732
+ top().scope,
1733
+ fn.text,
1734
+ ASTEngine.RelationshipType.Calls,
1735
+ 0.9,
1736
+ fn.startPosition.row + 1,
1737
+ { isYulCall: true }
1738
+ );
1739
+ }
1740
+ break;
1741
+ }
1742
+ case "yul_path": {
1743
+ const segs = findChildren(node, "yul_identifier");
1744
+ const segTexts = segs.length > 0 ? segs : findChildren(node, "identifier");
1745
+ segTexts.forEach((seg, i) => {
1746
+ if (EVM_BUILTIN_NAMES.has(seg.text)) return;
1747
+ const yulBindOwner = segTexts.length === 1 && i === 0 ? bindingOwnerOf(seg.text) : null;
1748
+ const yulLine = seg.startPosition.row + 1;
1749
+ const yulBareRefused = isReservedTarget(
1750
+ ASTEngine.RelationshipType.Uses,
1751
+ seg.text,
1752
+ { isYulPathSegment: true, isYulVariableRead: true }
1753
+ );
1754
+ if (yulBindOwner !== null && !yulBareRefused) {
1755
+ addRel(
1756
+ top().scope,
1757
+ `${yulBindOwner}${BINDING_SEP}${seg.text}`,
1758
+ ASTEngine.RelationshipType.Uses,
1759
+ 0.7,
1760
+ yulLine,
1761
+ { isYulPathSegment: true, isYulVariableRead: true, localBinding: true },
1762
+ { bindingTarget: true, dedupeAs: seg.text }
1763
+ );
1764
+ return;
1765
+ }
1766
+ addRel(top().scope, seg.text, ASTEngine.RelationshipType.Uses, 0.7, yulLine, {
1767
+ isYulPathSegment: true,
1768
+ ...i === 0 ? { isYulVariableRead: true } : { isYulMemberReference: true }
1769
+ });
1770
+ });
1771
+ break;
1772
+ }
1773
+ // ---------------------------------------------------------- inheritance
1774
+ // Handled at the contract level, where the frame is pushed and the SOL-5
1775
+ // kind decision is made. Descending in here would emit every base twice.
1776
+ case "inheritance_specifier": {
1777
+ descend = false;
1778
+ break;
1779
+ }
1780
+ // ------------------------------------------------------- type mentions
1781
+ case "user_defined_type": {
1782
+ const parent = node.parent;
1783
+ const ptype = parent ? parent.type : "";
1784
+ if (ptype === "inheritance_specifier" || ptype === "override_specifier" || ptype === "new_expression" || ptype === "struct_expression" || ptype === "catch_clause" || ptype === "meta_type_expression") break;
1785
+ const name = udtName(node);
1786
+ if (name !== "" && !GLOBAL_OBJECTS.has(name)) {
1787
+ addRel(
1788
+ top().scope,
1789
+ name,
1790
+ ASTEngine.RelationshipType.Uses,
1791
+ 0.85,
1792
+ node.startPosition.row + 1,
1793
+ { isTypeReference: true, typeReferenceSite: ptype }
1794
+ );
1795
+ }
1796
+ break;
1797
+ }
1798
+ // `using L for T` / `using {f as +} for T global` — the aliased entity.
1799
+ case "type_alias": {
1800
+ const id = findFirstChild(node, "identifier");
1801
+ if (id) {
1802
+ addRel(
1803
+ top().scope,
1804
+ id.text,
1805
+ ASTEngine.RelationshipType.References,
1806
+ 0.85,
1807
+ id.startPosition.row + 1,
1808
+ { isUsingDirectiveTarget: true }
1809
+ );
1810
+ }
1811
+ break;
1812
+ }
1813
+ // `using {eqCallType as ==} for CallType global;` — NEVER FIRED before
1814
+ // SOL-16. It looked for DIRECT `identifier` children, but the grammar nests
1815
+ // the name: `using_alias (user_defined_type (identifier))
1816
+ // (user_definable_operator)`, so `findChildren(node, "identifier")` was always
1817
+ // empty and the loop body never ran. Found by a per-case emit census over the
1818
+ // 389-file corpus — 4 `using_alias` nodes, all four in
1819
+ // contracts/account/utils/draft-ERC7579Utils.sol, 0 edges — which is the same
1820
+ // signature as the `revert_statement` dead branch, and the reason the census is
1821
+ // worth running rather than reading the code.
1822
+ //
1823
+ // The name is read the way every other type reference in this analyzer reads
1824
+ // one, through `udtName`, so it cannot disagree with the rest.
1825
+ case "using_alias": {
1826
+ const name = udtName(findDescendant(node, "user_defined_type"));
1827
+ if (name !== "") {
1828
+ addRel(
1829
+ top().scope,
1830
+ name,
1831
+ ASTEngine.RelationshipType.References,
1832
+ 0.85,
1833
+ (findDescendant(node, "user_defined_type") ?? node).startPosition.row + 1,
1834
+ { isUsingAliasFunction: true }
1835
+ );
1836
+ }
1837
+ break;
1838
+ }
1839
+ // `catch MyError(string memory reason)` names a declared error.
1840
+ case "catch_clause": {
1841
+ const id = findFirstChild(node, "identifier");
1842
+ if (id) {
1843
+ addRel(
1844
+ top().scope,
1845
+ id.text,
1846
+ ASTEngine.RelationshipType.References,
1847
+ 0.85,
1848
+ id.startPosition.row + 1,
1849
+ { isCaughtError: true }
1850
+ );
1851
+ }
1852
+ break;
1853
+ }
1854
+ // `type(uint).max` / `type(C).creationCode` — the TYPE is the reference;
1855
+ // `max`/`creationCode` are members of the meta type, not of the project.
1856
+ case "meta_type_expression": {
1857
+ const udt = findDescendant(node, "user_defined_type");
1858
+ const name = udtName(udt);
1859
+ if (name !== "" && typeNamesInFile.has(name)) {
1860
+ addRel(
1861
+ top().scope,
1862
+ name,
1863
+ ASTEngine.RelationshipType.References,
1864
+ 0.85,
1865
+ udt.startPosition.row + 1,
1866
+ { isMetaType: true }
1867
+ );
1868
+ }
1869
+ descend = false;
1870
+ break;
1871
+ }
1872
+ // ------------------------------------------------------------- calls
1873
+ case "call_expression": {
1874
+ const fn = node.childForFieldName("function");
1875
+ if (fn) claimedCallees.add(fn.id);
1876
+ const got = calleeNameOf(fn);
1877
+ if (got && got.name !== "") {
1878
+ const calleeNode = got.viaMember ? innerCalleeOf(fn) : null;
1879
+ const qObj = calleeNode ? memberObjectName(calleeNode) : "";
1880
+ if (qObj !== "" && qObj !== "abi" && qObj !== "type" && !ENGINE_FUNCTION_NAMES.has(got.name)) {
1881
+ top().qualifiedCalls.push(`${qObj}.${got.name}`);
1882
+ }
1883
+ const callBindOwnerRaw = got.viaMember ? null : bindingOwnerOf(got.name);
1884
+ const callBareRefused = callBindOwnerRaw !== null && (isReservedTarget(ASTEngine.RelationshipType.Calls, got.name, { isSolidityCall: true }) || ENGINE_FUNCTION_NAMES.has(got.name));
1885
+ const callBindOwner = callBareRefused ? null : callBindOwnerRaw;
1886
+ const callTarget = callBindOwner !== null ? `${callBindOwner}${BINDING_SEP}${got.name}` : got.name;
1887
+ const calleeObjectNode = calleeNode ? calleeNode.childForFieldName("object") : null;
1888
+ const callObjectBase = got.viaMember && qObj === "" ? indexedBaseOf(calleeObjectNode) : "";
1889
+ const callObjectConversion = got.viaMember && qObj === "" && callObjectBase === "" ? conversionTypeOf(calleeObjectNode) : "";
1890
+ addRel(top().scope, callTarget, ASTEngine.RelationshipType.Calls, 0.9, got.line, {
1891
+ isSolidityCall: true,
1892
+ ...callBindOwner !== null ? { localBinding: true } : {},
1893
+ // The member flag is REPORTED BY THE NAME RESOLVER, never by a second
1894
+ // traversal that re-decides the same question. The first version ran a
1895
+ // separate `isMemberLike(fn)` over the callee's DIRECT children, which
1896
+ // cannot see a `member_expression` nested under a `struct_expression`'s
1897
+ // `type:` field — so `t.call{value: 1}("")` resolved the name `call`
1898
+ // and then failed to say it was a member call, and the reserved-token
1899
+ // rule deleted the edge it had just resolved. `call`, `balance` and
1900
+ // `delegatecall` are EVM opcode TOKENS and also members of Solidity's
1901
+ // built-in types, so that deletion was silent and total. The name and
1902
+ // the fact about where it came from now cannot disagree.
1903
+ ...got.viaMember ? { isMemberCall: true } : {},
1904
+ // SOL-15 — the OBJECT a member call hangs off. `abi.encode(…)` and
1905
+ // `token.balanceOf(…)` are both `member_expression` callees, and only
1906
+ // the object separates a language builtin from a project member. Read
1907
+ // HERE, at the emit, because the post-pass has the callee NAME and
1908
+ // nothing else: `encode` alone cannot say which of the two it was.
1909
+ ...got.viaMember ? { calleeObject: qObj } : {},
1910
+ // W14 — the CONTAINER an index receiver reads from, kept apart from `calleeObject`
1911
+ // because the object of the member expression is `_m[k]`, not `_m`.
1912
+ ...callObjectBase !== "" ? { calleeObjectBase: callObjectBase } : {},
1913
+ // W14b — the TYPE a conversion receiver names (`IAccessManager(x).get()`).
1914
+ ...callObjectConversion !== "" ? { calleeObjectConversion: callObjectConversion } : {},
1915
+ // SOL-15 — the enclosing TYPE, captured here because the relationship's
1916
+ // SOURCE is the enclosing scope's LEAF name (`everything`, not
1917
+ // `Child.everything`), so the owner cannot be recovered from the row
1918
+ // afterwards. The first version tried, got no owner, and classified
1919
+ // every call as `inFileTypeMember` — a wrong answer produced by a
1920
+ // missing field rather than a wrong rule, which is the harder kind of
1921
+ // bug to see because the rule looks fine.
1922
+ ...enclosingTypeOf() !== "" ? { calleeOwner: enclosingTypeOf() } : {},
1923
+ ...fn && findFirstChild(fn, "struct_expression") !== null ? { hasCallOptions: true } : {}
1924
+ }, callBindOwner !== null ? { bindingTarget: true, dedupeAs: got.name } : {});
1925
+ }
1926
+ break;
1927
+ }
1928
+ // `emit Transfer(...)` is NOT a call_expression — the grammar models the
1929
+ // whole thing as `emit_statement` with a `name:` field, and 168 exist in
1930
+ // the corpus. A callee pass over `call_expression` alone would publish
1931
+ // ZERO event edges for the entire language.
1932
+ case "emit_statement": {
1933
+ const nameNode = node.childForFieldName("name");
1934
+ if (nameNode) claimedCallees.add(nameNode.id);
1935
+ const got = calleeNameOf(nameNode);
1936
+ if (got && got.name !== "") {
1937
+ addRel(
1938
+ top().scope,
1939
+ got.name,
1940
+ ASTEngine.RelationshipType.Calls,
1941
+ 0.9,
1942
+ got.line,
1943
+ { isEventEmission: true }
1944
+ );
1945
+ }
1946
+ break;
1947
+ }
1948
+ // `revert MyError(...)` — a custom error is a CALL. The name is the first
1949
+ // NAMED child when it is a bare identifier; `revert("msg")` has no such
1950
+ // child, which is the structural difference (measured on the tree, not
1951
+ // assumed: `revert E(m)` puts the identifier and the `revert_arguments` as
1952
+ // SIBLINGS).
1953
+ // A CUSTOM ERROR is a call, and this case NEVER FIRED before SOL-15 measured
1954
+ // it. It tested `node.namedChild(0).type === "identifier"`, but the grammar
1955
+ // puts an `expression` WRAPPER there for all three spellings —
1956
+ // `revert Boom(1);`, `revert Solo;` and `revert("x");` each report
1957
+ // `expression` — so the guard was false for every possible input and the
1958
+ // branch was decoration. The corpus has custom errors declared in
1959
+ // `contracts/interfaces/draft-IERC6093.sol` and elsewhere, and not one
1960
+ // reached the graph as a call.
1961
+ //
1962
+ // The name is the same leaf the callee resolver reads, so `revert Boom(1)`
1963
+ // resolves through the call's `function` field and `revert Solo` through the
1964
+ // wrapper, and `revert("x")` resolves to nothing at all — a string is not a
1965
+ // name, which is the correct outcome rather than a missed row.
1966
+ case "revert_statement": {
1967
+ const first = node.namedChildCount > 0 ? node.namedChild(0) : null;
1968
+ const calleeNode = first === null ? null : first.type === "call_expression" ? innerCalleeOf(first.childForFieldName("function")) : innerCalleeOf(first);
1969
+ const got = calleeNameOf(calleeNode);
1970
+ if (got && got.name !== "") {
1971
+ addRel(
1972
+ top().scope,
1973
+ got.name,
1974
+ ASTEngine.RelationshipType.Calls,
1975
+ 0.9,
1976
+ got.line,
1977
+ {
1978
+ isCustomErrorRevert: true,
1979
+ ...enclosingTypeOf() !== "" ? { calleeOwner: enclosingTypeOf() } : {}
1980
+ }
1981
+ );
1982
+ }
1983
+ break;
1984
+ }
1985
+ // `onlyOwner` in a function header, `Base(msg.sender)` in a constructor
1986
+ // header. grammar.js:765 — `modifier_invocation` is `$._identifier_path`
1987
+ // plus optional arguments, with NO field, so the name is the first
1988
+ // `identifier` child. `descend` stays TRUE so the arguments are read.
1989
+ //
1990
+ // TWO CONSTRUCTS WEAR ONE NODE TYPE, and the tree cannot tell them apart:
1991
+ // an APPLIED MODIFIER (`function f() onlyOwner`) and a BASE-CONSTRUCTOR CALL
1992
+ // (`constructor() Base(1)`). They are the same node with the same field-less
1993
+ // `identifier` child, so a test on the spelling cannot separate them — the
1994
+ // discriminator is RESOLUTION against the declaration name space (the prepass
1995
+ // above, plus the project index when one is present):
1996
+ //
1997
+ // target resolves to a declared `modifier_definition` → DECORATES.
1998
+ // The modifier is applied code that runs around the declaration's body;
1999
+ // the decorated declaration is the source and the modifier is the target,
2000
+ // the direction the vocabulary states ("a decorated declaration is
2001
+ // decorated BY the decorator"). NOT a `calls` edge: an application is not
2002
+ // a call site, and the fleet's relabel precedent is one site, one label.
2003
+ // target resolves to a declared contract/interface/library → stays `calls`.
2004
+ // It is the base constructor being invoked, not an annotation.
2005
+ // neither → stays `calls` with `isModifierInvocation`, unresolved: the file
2006
+ // and the index cannot say what the name is, so no decorates claim is made.
2007
+ //
2008
+ // MEASURED, whole admitted population (script/m14/solidity-extended-census.ts):
2009
+ // 144 decorates sites, 78 base-constructor calls, 0 ambiguous, 0 unresolved.
2010
+ // The 78 all sit in constructor definitions, and the 4 modifier applications
2011
+ // that also sit there are separated by the same resolution rule, not by the
2012
+ // enclosing declaration.
2013
+ case "modifier_invocation": {
2014
+ const id = findFirstChild(node, "identifier");
2015
+ if (id) {
2016
+ const name = id.text;
2017
+ const line = id.startPosition.row + 1;
2018
+ const inFileModifier = modifierNamesInFile.has(name);
2019
+ const inFileType = declaredTypeNamesForCalls.has(name);
2020
+ let projectType = false;
2021
+ let projectMember = false;
2022
+ let projectSymbol = false;
2023
+ if (!inFileModifier && !inFileType && projectIndex) {
2024
+ projectType = projectIndex.typeKinds.has(name);
2025
+ if (!projectType) {
2026
+ projectMember = projectTables().memberOwners.has(name);
2027
+ projectSymbol = !projectMember && projectIndex.symbolNames.has(name);
2028
+ }
2029
+ }
2030
+ if (inFileModifier || projectMember || projectSymbol) {
2031
+ addRel(
2032
+ top().scope,
2033
+ name,
2034
+ ASTEngine.RelationshipType.Decorates,
2035
+ inFileModifier || projectMember ? 0.9 : 0.8,
2036
+ line,
2037
+ {
2038
+ isModifierInvocation: true,
2039
+ isAppliedModifier: true,
2040
+ modifierResolution: inFileModifier ? "in-file" : projectMember ? "project-member" : "project-symbol",
2041
+ // W15b — the ENCLOSING TYPE, so a modifier applied on a contract whose base
2042
+ // declares it can be resolved through that contract's inheritance closure. The
2043
+ // edge had no owner at all, which is why the 17 cross-file modifier rows could
2044
+ // not use the mechanism SOL-25 applied to calls.
2045
+ ...enclosingTypeOf() !== "" ? { decoratesOwner: enclosingTypeOf() } : {}
2046
+ }
2047
+ );
2048
+ } else {
2049
+ addRel(top().scope, name, ASTEngine.RelationshipType.Calls, 0.85, line, {
2050
+ isModifierInvocation: true,
2051
+ ...inFileType || projectType ? { isBaseConstructorCall: true } : {}
2052
+ });
2053
+ }
2054
+ }
2055
+ break;
2056
+ }
2057
+ case "new_expression": {
2058
+ const udt = findDescendant(node, "user_defined_type");
2059
+ const name = udtName(udt);
2060
+ if (name !== "") {
2061
+ addRel(
2062
+ top().scope,
2063
+ name,
2064
+ ASTEngine.RelationshipType.Instantiates,
2065
+ 0.95,
2066
+ (udt ?? node).startPosition.row + 1,
2067
+ { isNewExpression: true }
2068
+ );
2069
+ }
2070
+ break;
2071
+ }
2072
+ // SOL-10 — the OTHER struct spelling: `S { a: 1 }` in a primary-expression
2073
+ // position is a struct construction, and its NAME is in the `type:` field.
2074
+ // When it is a call's callee the `call_expression` case owns it, so
2075
+ // `isCalleeOf` is what keeps `t.call{value: 1}(...)` from emitting twice.
2076
+ case "struct_expression": {
2077
+ if (isClaimedCallee(node)) break;
2078
+ const got = calleeNameOf(node.childForFieldName("type"));
2079
+ if (got && got.name !== "") {
2080
+ addRel(
2081
+ top().scope,
2082
+ got.name,
2083
+ ASTEngine.RelationshipType.Calls,
2084
+ 0.9,
2085
+ got.line,
2086
+ { isStructConstructor: true }
2087
+ );
2088
+ }
2089
+ descend = false;
2090
+ break;
2091
+ }
2092
+ // --------------------------------------------------------- references
2093
+ case "member_expression": {
2094
+ const prop = node.childForFieldName("property");
2095
+ if (!prop) break;
2096
+ if (isClaimedCallee(node)) break;
2097
+ const objectNode = node.childForFieldName("object");
2098
+ if (isEngineMemberObject(objectNode)) break;
2099
+ const object = memberObjectName(node);
2100
+ const objectBase = object === "" ? indexedBaseOf(objectNode) : "";
2101
+ addRel(
2102
+ top().scope,
2103
+ prop.text,
2104
+ ASTEngine.RelationshipType.Uses,
2105
+ 0.8,
2106
+ prop.startPosition.row + 1,
2107
+ {
2108
+ isMemberReference: true,
2109
+ ...object !== "" ? { viaObject: object } : {},
2110
+ ...objectBase !== "" ? { viaObjectBase: objectBase } : {}
2111
+ }
2112
+ );
2113
+ break;
2114
+ }
2115
+ case "identifier": {
2116
+ const parent = node.parent;
2117
+ if (!parent || parent.type !== "expression") break;
2118
+ if (isClaimedCalleeDirect(node)) break;
2119
+ if (top().yul) break;
2120
+ if (GLOBAL_OBJECTS.has(node.text)) break;
2121
+ {
2122
+ const bindOwner = bindingOwnerOf(node.text);
2123
+ const bareLine = node.startPosition.row + 1;
2124
+ const bareRefused = isReservedTarget(ASTEngine.RelationshipType.Uses, node.text, { isVariableReference: true });
2125
+ if (bindOwner !== null && !bareRefused) {
2126
+ addRel(
2127
+ top().scope,
2128
+ `${bindOwner}${BINDING_SEP}${node.text}`,
2129
+ ASTEngine.RelationshipType.Uses,
2130
+ 0.75,
2131
+ bareLine,
2132
+ { isVariableReference: true, localBinding: true },
2133
+ { bindingTarget: true, dedupeAs: node.text }
2134
+ );
2135
+ } else {
2136
+ addRel(
2137
+ top().scope,
2138
+ node.text,
2139
+ ASTEngine.RelationshipType.Uses,
2140
+ 0.75,
2141
+ bareLine,
2142
+ { isVariableReference: true }
2143
+ );
2144
+ }
2145
+ }
2146
+ break;
2147
+ }
2148
+ // ------------------------------------------------------------- silence
2149
+ // Named here so the F9 spec points at code rather than at an absence, and
2150
+ // so each reason lives beside the switch. Every one of these is either a
2151
+ // literal, a keyword, a wrapper, or a node whose whole content is
2152
+ // semantics the graph does not model.
2153
+ case "comment":
2154
+ case "string":
2155
+ case "string_literal":
2156
+ case "hex_string_literal":
2157
+ case "unicode_string_literal":
2158
+ case "number_literal":
2159
+ case "number_unit":
2160
+ case "boolean_literal":
2161
+ case "true":
2162
+ case "false":
2163
+ case "primitive_type":
2164
+ case "type_name":
2165
+ case "assembly_flags":
2166
+ case "assembly_statement":
2167
+ case "yul_evm_builtin":
2168
+ case "yul_boolean":
2169
+ case "yul_string_literal":
2170
+ case "yul_hex_string_literal":
2171
+ case "yul_hex_number":
2172
+ case "yul_decimal_number":
2173
+ case "yul_break":
2174
+ case "yul_continue":
2175
+ case "yul_leave":
2176
+ case "yul_label":
2177
+ case "layout_specifier":
2178
+ case "state_location":
2179
+ case "virtual":
2180
+ case "immutable":
2181
+ case "visibility":
2182
+ case "state_mutability":
2183
+ case "override_specifier":
2184
+ case "unchecked":
2185
+ case "enum_body":
2186
+ case "struct_body":
2187
+ case "contract_body":
2188
+ case "function_body":
2189
+ case "statement":
2190
+ case "expression":
2191
+ case "expression_statement":
2192
+ case "block_statement":
2193
+ case "yul_block":
2194
+ case "yul_assignment":
2195
+ case "if_statement":
2196
+ case "while_statement":
2197
+ case "for_statement":
2198
+ case "break_statement":
2199
+ case "continue_statement":
2200
+ case "do_while_statement":
2201
+ case "return_statement":
2202
+ case "return_parameter":
2203
+ case "revert_arguments":
2204
+ case "solidity_pragma_token":
2205
+ case "solidity_version":
2206
+ case "solidity_version_comparison_operator":
2207
+ case "pragma_value":
2208
+ case "any_pragma_token":
2209
+ case "any_source_type":
2210
+ case "inline_array_expression":
2211
+ break;
2212
+ default: {
2213
+ break;
2214
+ }
2215
+ }
2216
+ if (descend) {
2217
+ work.push({ node, exit: true });
2218
+ for (let i = node.childCount - 1; i >= 0; i--) {
2219
+ const c = node.child(i);
2220
+ if (c) work.push({ node: c, exit: false });
2221
+ }
2222
+ }
2223
+ }
2224
+ let overridesEmitted = 0;
2225
+ let overridesEmittedExternal = 0;
2226
+ let overridesUnresolved = 0;
2227
+ let overridesAmbiguousBaseCount = 0;
2228
+ for (const cand of overrideCandidates) {
2229
+ const bases = cand.explicit.length > 0 ? cand.explicit : cand.anyBase.length === 1 ? cand.anyBase : [];
2230
+ if (bases.length === 0) {
2231
+ overridesUnresolved++;
2232
+ overridesAmbiguousBaseCount++;
2233
+ continue;
2234
+ }
2235
+ let hit = false;
2236
+ for (const base of bases) {
2237
+ if (base === cand.owner) continue;
2238
+ if (!symbolMap.has(`${cand.owner}\0${cand.memberName}\0method`)) continue;
2239
+ const members = membersByType.get(base);
2240
+ const inFile = members !== void 0 && members.has(cand.memberName);
2241
+ const companion = context?.overrideCompanion?.get(base);
2242
+ const viaCompanion = !inFile && companion !== void 0 && companion.has(cand.memberName);
2243
+ const external = !inFile;
2244
+ addRel(cand.memberName, base, ASTEngine.RelationshipType.Overrides, external ? 0.75 : 0.9, cand.line, {
2245
+ overridesMember: cand.memberName,
2246
+ baseType: base,
2247
+ ...cand.explicit.length > 0 ? { baseNamedByOverrideSpecifier: true } : { baseFromContractHeader: true },
2248
+ ...external ? {
2249
+ externalBase: true,
2250
+ // A single header base is as strong as an explicit list: the compiler
2251
+ // requires SOME base to declare the member and there is only one
2252
+ // candidate. The reasonCode distinguishes them rather than merging
2253
+ // two different proofs under one label.
2254
+ reasonCode: cand.explicit.length > 0 ? "named-by-override-specifier" : viaCompanion ? "companion-index" : "single-header-base",
2255
+ ...viaCompanion ? { crossFileVerified: true } : {}
2256
+ } : {}
2257
+ });
2258
+ overridesEmitted++;
2259
+ if (external) overridesEmittedExternal++;
2260
+ hit = true;
2261
+ }
2262
+ if (!hit) overridesUnresolved++;
2263
+ }
2264
+ const memberClosure = (owner, seen = /* @__PURE__ */ new Set()) => {
2265
+ const out = /* @__PURE__ */ new Set();
2266
+ if (seen.has(owner)) return out;
2267
+ seen.add(owner);
2268
+ for (const m of membersByType.get(owner) ?? []) out.add(m);
2269
+ for (const b of basesByContract.get(owner) ?? []) {
2270
+ for (const m of memberClosure(b, seen)) out.add(m);
2271
+ }
2272
+ return out;
2273
+ };
2274
+ const closures = /* @__PURE__ */ new Map();
2275
+ for (const owner of membersByType.keys()) closures.set(owner, memberClosure(owner));
2276
+ const callKinds = {
2277
+ conversion: 0,
2278
+ namespaceBuiltin: 0,
2279
+ modifierInvocation: 0,
2280
+ baseConstructorCall: 0,
2281
+ localFunction: 0,
2282
+ inheritedMember: 0,
2283
+ inFileTypeMember: 0,
2284
+ crossFileInheritedMember: 0,
2285
+ projectMemberCandidate: 0,
2286
+ eventEmission: 0,
2287
+ errorRevert: 0,
2288
+ structConstructor: 0,
2289
+ external: 0,
2290
+ localBinding: 0,
2291
+ receiverTypedMember: 0,
2292
+ inheritedMemberQualified: 0,
2293
+ libraryStaticCall: 0
2294
+ };
2295
+ const projectAllMembers = /* @__PURE__ */ new Map();
2296
+ const projectMemberOwners = /* @__PURE__ */ new Map();
2297
+ const projectMemberDeclarer = /* @__PURE__ */ new Map();
2298
+ const projectMemberDeclarers = /* @__PURE__ */ new Map();
2299
+ if (projectIndex) {
2300
+ const { memberOwners, allMembers, memberDeclarer, memberDeclarers } = buildResolutionTables(projectIndex);
2301
+ for (const [t, ms] of allMembers) projectAllMembers.set(t, ms);
2302
+ for (const [m, ts] of memberOwners) projectMemberOwners.set(m, ts);
2303
+ for (const [t, byName] of memberDeclarer) projectMemberDeclarer.set(t, byName);
2304
+ for (const [m, n] of memberDeclarers) projectMemberDeclarers.set(m, n);
2305
+ }
2306
+ for (const rel of relationships) {
2307
+ if (rel.type !== ASTEngine.RelationshipType.Calls) continue;
2308
+ const md = rel.metadata;
2309
+ let target = rel.target ?? "";
2310
+ const owner = typeof md.calleeOwner === "string" ? md.calleeOwner : "";
2311
+ const ownDirect = new Set(membersByType.get(owner) ?? []);
2312
+ const ownInherited = /* @__PURE__ */ new Set();
2313
+ for (const b of basesByContract.get(owner) ?? []) {
2314
+ for (const m of memberClosure(b)) ownInherited.add(m);
2315
+ }
2316
+ let kind = (() => {
2317
+ if (md.localBinding === true) return "localBinding";
2318
+ if (md.isEventEmission === true) return "eventEmission";
2319
+ if (md.isCustomErrorRevert === true) return "errorRevert";
2320
+ if (md.isStructConstructor === true) return "structConstructor";
2321
+ if (md.isBaseConstructorCall === true) return "baseConstructorCall";
2322
+ if (isPrimitiveTypeToken(target)) return "conversion";
2323
+ const obj = typeof md.calleeObject === "string" ? md.calleeObject : "";
2324
+ if (obj === "abi" || obj === "type" || obj === "super" || obj === "this") return "namespaceBuiltin";
2325
+ if (modifierNamesInFile.has(target)) return "modifierInvocation";
2326
+ if (ownDirect.has(target)) return "localFunction";
2327
+ if (ownInherited.has(target)) return "inheritedMember";
2328
+ if (obj !== "") {
2329
+ for (const [otherOwner, set] of membersByType) {
2330
+ if (otherOwner === owner) continue;
2331
+ if (set.has(target)) return "inFileTypeMember";
2332
+ }
2333
+ }
2334
+ if (projectIndex) {
2335
+ const closed = projectAllMembers.get(owner);
2336
+ if (closed?.has(target)) return "crossFileInheritedMember";
2337
+ const owners = projectMemberOwners.get(target);
2338
+ if (owners) return "projectMemberCandidate";
2339
+ }
2340
+ return "external";
2341
+ })();
2342
+ md.calleeKind = kind;
2343
+ if (kind === "external" && target !== "" && rel.metadata.externalTarget !== true) {
2344
+ md.externalTarget = true;
2345
+ md.externalVia = "call";
2346
+ }
2347
+ const namespaceObject = typeof md.calleeObject === "string" ? md.calleeObject : "";
2348
+ if (kind === "namespaceBuiltin" && (namespaceObject === "abi" || namespaceObject === "type") && target !== "" && !target.includes(".")) {
2349
+ rel.target = `${namespaceObject}.${target}`;
2350
+ target = rel.target;
2351
+ md.languageNamespace = [namespaceObject];
2352
+ }
2353
+ if (projectIndex && typeof md.calleeObject === "string" && md.isMemberCall === true && target !== "" && !target.includes(".")) {
2354
+ const objectName = md.calleeObject;
2355
+ if (objectName !== "" && !declaredVariableTypes.has(objectName) && (projectIndex.symbolCounts.get(objectName) ?? 0) === 1) {
2356
+ const declarer = projectMemberDeclarer.get(objectName)?.get(target);
2357
+ if (declarer !== void 0 && declarer !== null) {
2358
+ rel.target = `${declarer}.${target}`;
2359
+ target = rel.target;
2360
+ md.ownerType = declarer;
2361
+ md.calleeKind = "libraryStaticCall";
2362
+ kind = "libraryStaticCall";
2363
+ }
2364
+ }
2365
+ }
2366
+ const calleeObject = typeof md.calleeObject === "string" ? md.calleeObject : "";
2367
+ const receiverName = calleeObject !== "" ? calleeObject : typeof md.calleeObjectBase === "string" ? md.calleeObjectBase : "";
2368
+ const conversionReceiver = typeof md.calleeObjectConversion === "string" ? md.calleeObjectConversion : "";
2369
+ if (projectIndex && (receiverName !== "" || conversionReceiver !== "") && target !== "" && !target.includes(".")) {
2370
+ const types = declaredVariableTypes.get(receiverName);
2371
+ const element = containerElementTypes.get(receiverName);
2372
+ const receiverType = types !== void 0 && types.size !== 1 ? void 0 : element ?? (types !== void 0 ? [...types][0] : void 0);
2373
+ const isContainer = conversionReceiver !== "" || element !== void 0;
2374
+ const effectiveReceiverType = conversionReceiver !== "" ? conversionReceiver : receiverType;
2375
+ const mayNameOneDeclaration = isContainer ? (projectIndex.symbolCounts.get(effectiveReceiverType ?? "") ?? 0) === 1 : (projectMemberDeclarers.get(target) ?? 0) > 1;
2376
+ if (effectiveReceiverType !== void 0 && mayNameOneDeclaration) {
2377
+ const declarer = projectMemberDeclarer.get(effectiveReceiverType)?.get(target);
2378
+ if (declarer !== void 0 && declarer !== null) {
2379
+ rel.target = `${declarer}.${target}`;
2380
+ target = rel.target;
2381
+ md.receiverType = effectiveReceiverType;
2382
+ md.ownerType = declarer;
2383
+ md.calleeKind = "receiverTypedMember";
2384
+ kind = "receiverTypedMember";
2385
+ }
2386
+ }
2387
+ }
2388
+ if (projectIndex && (kind === "crossFileInheritedMember" || kind === "inheritedMember") && owner !== "" && target !== "" && !target.includes(".")) {
2389
+ if ((projectMemberDeclarers.get(target) ?? 0) > 1) {
2390
+ const declarer = projectMemberDeclarer.get(owner)?.get(target);
2391
+ if (declarer !== void 0 && declarer !== null && declarer !== owner) {
2392
+ rel.target = `${declarer}.${target}`;
2393
+ target = rel.target;
2394
+ md.ownerType = declarer;
2395
+ md.calleeKind = "inheritedMemberQualified";
2396
+ kind = "inheritedMemberQualified";
2397
+ }
2398
+ }
2399
+ }
2400
+ callKinds[kind] = (callKinds[kind] ?? 0) + 1;
2401
+ }
2402
+ if (projectIndex) {
2403
+ for (const rel of relationships) {
2404
+ if (rel.type !== ASTEngine.RelationshipType.Uses) continue;
2405
+ const md = rel.metadata;
2406
+ if (md.isMemberReference !== true) continue;
2407
+ const viaObject = typeof md.viaObject === "string" ? md.viaObject : "";
2408
+ const receiverName = viaObject !== "" ? viaObject : typeof md.viaObjectBase === "string" ? md.viaObjectBase : "";
2409
+ if (receiverName === "") continue;
2410
+ const target = rel.target ?? "";
2411
+ if (target === "" || target.includes(".")) continue;
2412
+ const types = declaredVariableTypes.get(receiverName);
2413
+ const element = types === void 0 ? containerElementTypes.get(receiverName) : void 0;
2414
+ const receiverType = types !== void 0 && types.size === 1 ? [...types][0] : element;
2415
+ if (receiverType === void 0) continue;
2416
+ const declarer = projectMemberDeclarer.get(receiverType)?.get(target);
2417
+ if (declarer === void 0 || declarer === null) continue;
2418
+ rel.target = `${declarer}.${target}`;
2419
+ md.receiverType = receiverType;
2420
+ md.ownerType = declarer;
2421
+ }
2422
+ }
2423
+ if (projectIndex) {
2424
+ for (const rel of relationships) {
2425
+ if (rel.type !== ASTEngine.RelationshipType.Decorates) continue;
2426
+ const md = rel.metadata;
2427
+ if (typeof md.decoratesOwner !== "string" || md.decoratesOwner === "") continue;
2428
+ const target = rel.target ?? "";
2429
+ if (target === "" || target.includes(".")) continue;
2430
+ if ((projectMemberDeclarers.get(target) ?? 0) <= 1) continue;
2431
+ const owner = md.decoratesOwner;
2432
+ const declarer = projectMemberDeclarer.get(owner)?.get(target);
2433
+ if (declarer === void 0 || declarer === null || declarer === owner) continue;
2434
+ if ((projectIndex.symbolCounts.get(declarer) ?? 0) !== 1) continue;
2435
+ rel.target = `${declarer}.${target}`;
2436
+ md.ownerType = declarer;
2437
+ md.modifierResolution = "inherited-member";
2438
+ }
2439
+ }
2440
+ const exportedNames = /* @__PURE__ */ new Set();
2441
+ for (const { name, line } of topLevelExports) {
2442
+ if (exportedNames.has(name)) continue;
2443
+ exportedNames.add(name);
2444
+ addRel(
2445
+ fileName,
2446
+ name,
2447
+ ASTEngine.RelationshipType.Exports,
2448
+ 1,
2449
+ line,
2450
+ { isCompilationUnitSurface: true }
2451
+ );
2452
+ exports.push({
2453
+ name,
2454
+ range: this.lineRange(line),
2455
+ metadata: { soliditySpecific: true, isFileScopeDeclaration: true }
2456
+ });
2457
+ }
2458
+ {
2459
+ const seenImportPath = /* @__PURE__ */ new Set();
2460
+ for (const e of importEdges) {
2461
+ if (!e.path || seenImportPath.has(e.path)) continue;
2462
+ seenImportPath.add(e.path);
2463
+ const claim = typeof context?.projectRoot === "string" && context.projectRoot.length > 0 ? fileClaimForTarget({
2464
+ projectRoot: context.projectRoot,
2465
+ filePath,
2466
+ admittedExtensions: ["sol"],
2467
+ written: e.path
2468
+ }) : null;
2469
+ addRel(fileName, e.path, ASTEngine.RelationshipType.Imports, 1, e.line, {
2470
+ import: true,
2471
+ importPath: e.path,
2472
+ ...claim !== null ? { importProjectFile: claim } : {},
2473
+ // SOL-28 — AND WHEN THE PATH IS NOT A PROJECT FILE, THE SOURCE-PATH CLAIM IS THE TRUE ONE:
2474
+ // nothing the project ADMITS resolves to this written path (`forge-std/Test.sol`,
2475
+ // `@openzeppelin/contracts/utils/math/Math.sol`). Its polarity is the inverse of the file
2476
+ // claim's and the project's own admitted list is the whole evidence.
2477
+ ...claim === null ? { sourcePathClaim: [e.path] } : {},
2478
+ ...e.isNamespace ? { isNamespaceImport: true } : {}
2479
+ }, { pathTarget: true });
2480
+ }
2481
+ }
2482
+ for (const e of importEdges) {
2483
+ if (e.name === "*") {
2484
+ addRel(fileName, e.path, ASTEngine.RelationshipType.References, 1, e.line, {
2485
+ import: true,
2486
+ isNamespaceImport: true
2487
+ });
2488
+ continue;
2489
+ }
2490
+ addRel(fileName, e.name, ASTEngine.RelationshipType.References, 1, e.line, {
2491
+ import: true,
2492
+ importPath: e.path,
2493
+ // A namespace import IS a namespace import, and the ALIASED form
2494
+ // (`import * as Utils from …`) was not labelled as one — only the bare form
2495
+ // carried the marker. So nothing in the output said which edges came from a
2496
+ // namespace import, and the marker selected the wrong set: a filter keyed on
2497
+ // `isNamespaceImport` removed the bare import (which the F9 gate already
2498
+ // suppresses) and left the aliased one standing.
2499
+ ...e.isNamespace ? { isNamespaceImport: true } : {}
2500
+ });
2501
+ }
2502
+ const detectedPatterns = [...patterns];
2503
+ const detectedFrameworks = [...frameworks];
2504
+ if (projectIndex) {
2505
+ for (const rel of relationships) {
2506
+ const md = rel.metadata;
2507
+ if (md.projectIndexSupplied === void 0) md.projectIndexSupplied = true;
2508
+ }
2509
+ }
2510
+ return {
2511
+ language: "solidity",
2512
+ symbols,
2513
+ relationships,
2514
+ imports,
2515
+ exports,
2516
+ errors: [],
2517
+ metadata: {
2518
+ complexity: symbols.length + relationships.length,
2519
+ linesOfCode: lines.length,
2520
+ cyclomaticComplexity,
2521
+ framework: detectedFrameworks[0],
2522
+ patterns: detectedPatterns,
2523
+ detectedFrameworks,
2524
+ frameworkPatterns: detectedPatterns.map((p) => ({
2525
+ framework: "Solidity",
2526
+ patterns: [{ type: "usage", detected: true, description: p }]
2527
+ })),
2528
+ architecturalInsights: [],
2529
+ architecturalScore: Math.min(100, 60 + relationships.length),
2530
+ codeQuality: "Excellent",
2531
+ hasTypeInference: false,
2532
+ hasFrameworkIntelligence: true,
2533
+ analysisVersion: "6.0-sol-module-owners-lines-tiers",
2534
+ // Reported rather than hidden — see the F9 spec and the surface measure.
2535
+ commentsRead,
2536
+ commentsAttached,
2537
+ documentedSymbols,
2538
+ symbolsWithComplexity,
2539
+ symbolsWithQualifiedCalls,
2540
+ ...caseAudit ? { __caseHits: __hits, __caseEmits: __emits } : {},
2541
+ inheritanceKindsResolvedProjectWide,
2542
+ natSpecCheck: {
2543
+ symbolsWithUndocumentedParams,
2544
+ symbolsWithOverDocumentedParams,
2545
+ undocumentedParams: natSpecUndocumentedParams,
2546
+ overDocumentedParams: natSpecOverDocumentedParams,
2547
+ returnDocumentedWithoutReturn: natSpecReturnWithoutReturn,
2548
+ overDocumentedSamples: overDocumentedSamples.slice(0, 10),
2549
+ returnWithoutReturnSamples: returnWithoutReturnSamples.slice(0, 10),
2550
+ _note: "SOL-23. Documentation checked against the signature, both halves in the same tree. `undocumentedParameters` is usually benign \u2014 plenty of code documents only the interesting parameters. The other two are real errors in the source: a `@param` naming a parameter the function does not have, and a `@return` on a function that returns nothing. Reported as counts and samples, never as relationships: there is no second symbol and no second file for an edge to connect, and an edge to a parameter that may not exist would put a target in the graph no project declares."
2551
+ },
2552
+ callKinds,
2553
+ overridesEmitted,
2554
+ overridesEmittedExternal,
2555
+ overridesUnresolved,
2556
+ overridesAmbiguousBaseCount,
2557
+ engineCallsSuppressed,
2558
+ getterSymbols,
2559
+ storeRefusedDeclarations,
2560
+ storeRefusedNames: [...storeRefusedNames].sort(),
2561
+ symbolsSuppressedOnRecoveryLines,
2562
+ // The SET, not just the count, and for a testable reason: SOL-6's mutant
2563
+ // has to reinstate what the narrower rule would have emitted, and a revert
2564
+ // written from a count alone cannot be written generically — it would have
2565
+ // to hard-code the fixture's line, which measures the fixture rather than
2566
+ // the behaviour. The list is also the same fact at a resolution a reader
2567
+ // wants: which lines of this file the parser could not handle.
2568
+ recoveryLinesList: [...recoveryLines].sort((a, b) => a - b),
2569
+ implementsCrossFileUnresolved: extendsUnresolvedAsInterface
2570
+ // Not `Partial<FileMetadata>`: the declared return type is
2571
+ // `FileMetadata & Partial<FileMetadata>`, and casting to the Partial half made
2572
+ // `complexity` optional even though it is set two lines into this literal, which
2573
+ // is what the assignment rejected. `FileMetadata` also carries
2574
+ // `[key: string]: any`, so the Solidity-specific keys above need no exemption.
2575
+ }
2576
+ };
2577
+ }
2578
+ // ---------------------------------------------------------------- helpers
2579
+ /** SOL-1 — the file's own basename, extension included. */
2580
+ basenameOf(filePath) {
2581
+ const normalized = filePath.split("\\").join("/");
2582
+ const base = normalized.slice(normalized.lastIndexOf("/") + 1);
2583
+ return base.length > 0 ? base : "contract.sol";
2584
+ }
2585
+ lineRange(line) {
2586
+ return {
2587
+ startLine: line,
2588
+ startColumn: 1,
2589
+ endLine: line,
2590
+ endColumn: 1,
2591
+ startIndex: 0,
2592
+ endIndex: 0
2593
+ };
2594
+ }
2595
+ nodeToRange(node) {
2596
+ return {
2597
+ startLine: node.startPosition.row + 1,
2598
+ startColumn: node.startPosition.column + 1,
2599
+ endLine: node.endPosition.row + 1,
2600
+ endColumn: node.endPosition.column + 1,
2601
+ startIndex: node.startIndex,
2602
+ endIndex: node.endIndex
2603
+ };
2604
+ }
2605
+ };
2606
+ export {
2607
+ RealSolidityAnalyzer
2608
+ };