@quolu/lattice 0.69.5 → 0.71.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (605) hide show
  1. package/README.ja.md +5 -1
  2. package/package.json +1 -1
  3. package/sensor/dist/bin/lattice-sensor.js +746 -323
  4. package/sensor/dist/bin/viewer-gate.d.ts +17 -0
  5. package/sensor/dist/bin/viewer-gate.d.ts.map +1 -0
  6. package/sensor/dist/bin/viewer-gate.js +31 -0
  7. package/sensor/dist/bin/viewer-gate.js.map +1 -0
  8. package/sensor/dist/context/formatter.d.ts +7 -1
  9. package/sensor/dist/context/formatter.d.ts.map +1 -1
  10. package/sensor/dist/context/formatter.js +12 -6
  11. package/sensor/dist/context/formatter.js.map +1 -1
  12. package/sensor/dist/context/index.d.ts +12 -0
  13. package/sensor/dist/context/index.d.ts.map +1 -1
  14. package/sensor/dist/context/index.js +103 -16
  15. package/sensor/dist/context/index.js.map +1 -1
  16. package/sensor/dist/db/index.d.ts +23 -2
  17. package/sensor/dist/db/index.d.ts.map +1 -1
  18. package/sensor/dist/db/index.js +109 -31
  19. package/sensor/dist/db/index.js.map +1 -1
  20. package/sensor/dist/db/migrations.d.ts +1 -1
  21. package/sensor/dist/db/migrations.d.ts.map +1 -1
  22. package/sensor/dist/db/migrations.js +68 -1
  23. package/sensor/dist/db/migrations.js.map +1 -1
  24. package/sensor/dist/db/queries.d.ts +583 -6
  25. package/sensor/dist/db/queries.d.ts.map +1 -1
  26. package/sensor/dist/db/queries.js +1425 -57
  27. package/sensor/dist/db/queries.js.map +1 -1
  28. package/sensor/dist/db/schema.sql +27 -4
  29. package/sensor/dist/db/sqlite-adapter.d.ts +2 -0
  30. package/sensor/dist/db/sqlite-adapter.d.ts.map +1 -1
  31. package/sensor/dist/db/sqlite-adapter.js +56 -7
  32. package/sensor/dist/db/sqlite-adapter.js.map +1 -1
  33. package/sensor/dist/db/synthesis-stage.d.ts +12 -0
  34. package/sensor/dist/db/synthesis-stage.d.ts.map +1 -0
  35. package/sensor/dist/db/synthesis-stage.js +93 -0
  36. package/sensor/dist/db/synthesis-stage.js.map +1 -0
  37. package/sensor/dist/db/wal-valve.d.ts +32 -12
  38. package/sensor/dist/db/wal-valve.d.ts.map +1 -1
  39. package/sensor/dist/db/wal-valve.js +65 -22
  40. package/sensor/dist/db/wal-valve.js.map +1 -1
  41. package/sensor/dist/db/wsl-shared-index.d.ts +53 -0
  42. package/sensor/dist/db/wsl-shared-index.d.ts.map +1 -0
  43. package/sensor/dist/db/wsl-shared-index.js +121 -0
  44. package/sensor/dist/db/wsl-shared-index.js.map +1 -0
  45. package/sensor/dist/directory.d.ts +126 -2
  46. package/sensor/dist/directory.d.ts.map +1 -1
  47. package/sensor/dist/directory.js +285 -16
  48. package/sensor/dist/directory.js.map +1 -1
  49. package/sensor/dist/errors.d.ts +14 -0
  50. package/sensor/dist/errors.d.ts.map +1 -1
  51. package/sensor/dist/errors.js +16 -1
  52. package/sensor/dist/errors.js.map +1 -1
  53. package/sensor/dist/extraction/cfml-extractor.d.ts +1 -0
  54. package/sensor/dist/extraction/cfml-extractor.d.ts.map +1 -1
  55. package/sensor/dist/extraction/cfml-extractor.js +15 -3
  56. package/sensor/dist/extraction/cfml-extractor.js.map +1 -1
  57. package/sensor/dist/extraction/dfm-extractor.d.ts.map +1 -1
  58. package/sensor/dist/extraction/dfm-extractor.js +14 -10
  59. package/sensor/dist/extraction/dfm-extractor.js.map +1 -1
  60. package/sensor/dist/extraction/extraction-version.d.ts +1 -1
  61. package/sensor/dist/extraction/extraction-version.d.ts.map +1 -1
  62. package/sensor/dist/extraction/extraction-version.js +3 -1
  63. package/sensor/dist/extraction/extraction-version.js.map +1 -1
  64. package/sensor/dist/extraction/function-ref.d.ts +6 -4
  65. package/sensor/dist/extraction/function-ref.d.ts.map +1 -1
  66. package/sensor/dist/extraction/function-ref.js +35 -14
  67. package/sensor/dist/extraction/function-ref.js.map +1 -1
  68. package/sensor/dist/extraction/generated-detection.d.ts +48 -12
  69. package/sensor/dist/extraction/generated-detection.d.ts.map +1 -1
  70. package/sensor/dist/extraction/generated-detection.js +176 -11
  71. package/sensor/dist/extraction/generated-detection.js.map +1 -1
  72. package/sensor/dist/extraction/grammars.d.ts +38 -0
  73. package/sensor/dist/extraction/grammars.d.ts.map +1 -1
  74. package/sensor/dist/extraction/grammars.js +115 -3
  75. package/sensor/dist/extraction/grammars.js.map +1 -1
  76. package/sensor/dist/extraction/index.d.ts +218 -14
  77. package/sensor/dist/extraction/index.d.ts.map +1 -1
  78. package/sensor/dist/extraction/index.js +887 -175
  79. package/sensor/dist/extraction/index.js.map +1 -1
  80. package/sensor/dist/extraction/languages/c-cpp.d.ts +58 -0
  81. package/sensor/dist/extraction/languages/c-cpp.d.ts.map +1 -1
  82. package/sensor/dist/extraction/languages/c-cpp.js +310 -19
  83. package/sensor/dist/extraction/languages/c-cpp.js.map +1 -1
  84. package/sensor/dist/extraction/languages/dart.d.ts.map +1 -1
  85. package/sensor/dist/extraction/languages/dart.js +8 -3
  86. package/sensor/dist/extraction/languages/dart.js.map +1 -1
  87. package/sensor/dist/extraction/languages/erlang.d.ts.map +1 -1
  88. package/sensor/dist/extraction/languages/erlang.js +58 -22
  89. package/sensor/dist/extraction/languages/erlang.js.map +1 -1
  90. package/sensor/dist/extraction/languages/javascript.js +1 -1
  91. package/sensor/dist/extraction/languages/javascript.js.map +1 -1
  92. package/sensor/dist/extraction/languages/kotlin.d.ts.map +1 -1
  93. package/sensor/dist/extraction/languages/kotlin.js +169 -36
  94. package/sensor/dist/extraction/languages/kotlin.js.map +1 -1
  95. package/sensor/dist/extraction/languages/lua.d.ts.map +1 -1
  96. package/sensor/dist/extraction/languages/lua.js +4 -1
  97. package/sensor/dist/extraction/languages/lua.js.map +1 -1
  98. package/sensor/dist/extraction/languages/objc.d.ts.map +1 -1
  99. package/sensor/dist/extraction/languages/objc.js +4 -0
  100. package/sensor/dist/extraction/languages/objc.js.map +1 -1
  101. package/sensor/dist/extraction/languages/python.d.ts.map +1 -1
  102. package/sensor/dist/extraction/languages/python.js +71 -0
  103. package/sensor/dist/extraction/languages/python.js.map +1 -1
  104. package/sensor/dist/extraction/languages/rust.d.ts +20 -0
  105. package/sensor/dist/extraction/languages/rust.d.ts.map +1 -1
  106. package/sensor/dist/extraction/languages/rust.js +50 -20
  107. package/sensor/dist/extraction/languages/rust.js.map +1 -1
  108. package/sensor/dist/extraction/languages/scala.d.ts.map +1 -1
  109. package/sensor/dist/extraction/languages/scala.js +18 -3
  110. package/sensor/dist/extraction/languages/scala.js.map +1 -1
  111. package/sensor/dist/extraction/languages/swift.d.ts +15 -0
  112. package/sensor/dist/extraction/languages/swift.d.ts.map +1 -1
  113. package/sensor/dist/extraction/languages/swift.js +50 -0
  114. package/sensor/dist/extraction/languages/swift.js.map +1 -1
  115. package/sensor/dist/extraction/languages/typescript.d.ts.map +1 -1
  116. package/sensor/dist/extraction/languages/typescript.js +9 -2
  117. package/sensor/dist/extraction/languages/typescript.js.map +1 -1
  118. package/sensor/dist/extraction/liquid-extractor.d.ts +18 -0
  119. package/sensor/dist/extraction/liquid-extractor.d.ts.map +1 -1
  120. package/sensor/dist/extraction/liquid-extractor.js +98 -22
  121. package/sensor/dist/extraction/liquid-extractor.js.map +1 -1
  122. package/sensor/dist/extraction/mybatis-extractor.d.ts.map +1 -1
  123. package/sensor/dist/extraction/mybatis-extractor.js +6 -4
  124. package/sensor/dist/extraction/mybatis-extractor.js.map +1 -1
  125. package/sensor/dist/extraction/parse-pool.d.ts +7 -0
  126. package/sensor/dist/extraction/parse-pool.d.ts.map +1 -1
  127. package/sensor/dist/extraction/parse-pool.js +20 -1
  128. package/sensor/dist/extraction/parse-pool.js.map +1 -1
  129. package/sensor/dist/extraction/syntax-tokens.d.ts +103 -0
  130. package/sensor/dist/extraction/syntax-tokens.d.ts.map +1 -0
  131. package/sensor/dist/extraction/syntax-tokens.js +381 -0
  132. package/sensor/dist/extraction/syntax-tokens.js.map +1 -0
  133. package/sensor/dist/extraction/tree-sitter-helpers.d.ts +5 -0
  134. package/sensor/dist/extraction/tree-sitter-helpers.d.ts.map +1 -1
  135. package/sensor/dist/extraction/tree-sitter-helpers.js +17 -0
  136. package/sensor/dist/extraction/tree-sitter-helpers.js.map +1 -1
  137. package/sensor/dist/extraction/tree-sitter-types.d.ts +31 -3
  138. package/sensor/dist/extraction/tree-sitter-types.d.ts.map +1 -1
  139. package/sensor/dist/extraction/tree-sitter.d.ts +101 -18
  140. package/sensor/dist/extraction/tree-sitter.d.ts.map +1 -1
  141. package/sensor/dist/extraction/tree-sitter.js +928 -134
  142. package/sensor/dist/extraction/tree-sitter.js.map +1 -1
  143. package/sensor/dist/extraction/wasm/tree-sitter-scala.wasm +0 -0
  144. package/sensor/dist/file-limits.d.ts +40 -0
  145. package/sensor/dist/file-limits.d.ts.map +1 -0
  146. package/sensor/dist/file-limits.js +147 -0
  147. package/sensor/dist/file-limits.js.map +1 -0
  148. package/sensor/dist/graph/branch-guards.d.ts +265 -0
  149. package/sensor/dist/graph/branch-guards.d.ts.map +1 -0
  150. package/sensor/dist/graph/branch-guards.js +2325 -0
  151. package/sensor/dist/graph/branch-guards.js.map +1 -0
  152. package/sensor/dist/graph/dead-code.d.ts +251 -0
  153. package/sensor/dist/graph/dead-code.d.ts.map +1 -0
  154. package/sensor/dist/graph/dead-code.js +776 -0
  155. package/sensor/dist/graph/dead-code.js.map +1 -0
  156. package/sensor/dist/graph/dynamic-boundary-report.d.ts +121 -0
  157. package/sensor/dist/graph/dynamic-boundary-report.d.ts.map +1 -0
  158. package/sensor/dist/graph/dynamic-boundary-report.js +283 -0
  159. package/sensor/dist/graph/dynamic-boundary-report.js.map +1 -0
  160. package/sensor/dist/graph/index.d.ts +4 -0
  161. package/sensor/dist/graph/index.d.ts.map +1 -1
  162. package/sensor/dist/graph/index.js +16 -1
  163. package/sensor/dist/graph/index.js.map +1 -1
  164. package/sensor/dist/graph/named-symbol-flow.d.ts +125 -0
  165. package/sensor/dist/graph/named-symbol-flow.d.ts.map +1 -0
  166. package/sensor/dist/graph/named-symbol-flow.js +552 -0
  167. package/sensor/dist/graph/named-symbol-flow.js.map +1 -0
  168. package/sensor/dist/graph/queries.d.ts.map +1 -1
  169. package/sensor/dist/graph/queries.js +2 -0
  170. package/sensor/dist/graph/queries.js.map +1 -1
  171. package/sensor/dist/graph/symbol-lookup.d.ts +83 -0
  172. package/sensor/dist/graph/symbol-lookup.d.ts.map +1 -0
  173. package/sensor/dist/graph/symbol-lookup.js +181 -0
  174. package/sensor/dist/graph/symbol-lookup.js.map +1 -0
  175. package/sensor/dist/graph/traversal.d.ts +9 -0
  176. package/sensor/dist/graph/traversal.d.ts.map +1 -1
  177. package/sensor/dist/graph/traversal.js +62 -36
  178. package/sensor/dist/graph/traversal.js.map +1 -1
  179. package/sensor/dist/graph/type-hierarchy.d.ts +155 -0
  180. package/sensor/dist/graph/type-hierarchy.d.ts.map +1 -0
  181. package/sensor/dist/graph/type-hierarchy.js +382 -0
  182. package/sensor/dist/graph/type-hierarchy.js.map +1 -0
  183. package/sensor/dist/index.d.ts +272 -10
  184. package/sensor/dist/index.d.ts.map +1 -1
  185. package/sensor/dist/index.js +510 -34
  186. package/sensor/dist/index.js.map +1 -1
  187. package/sensor/dist/mcp/answer-freshness.d.ts +10 -0
  188. package/sensor/dist/mcp/answer-freshness.d.ts.map +1 -0
  189. package/sensor/dist/mcp/answer-freshness.js +68 -0
  190. package/sensor/dist/mcp/answer-freshness.js.map +1 -0
  191. package/sensor/dist/mcp/daemon-manager.d.ts +1 -1
  192. package/sensor/dist/mcp/daemon-manager.d.ts.map +1 -1
  193. package/sensor/dist/mcp/daemon-manager.js +17 -1
  194. package/sensor/dist/mcp/daemon-manager.js.map +1 -1
  195. package/sensor/dist/mcp/daemon-paths.d.ts +8 -0
  196. package/sensor/dist/mcp/daemon-paths.d.ts.map +1 -1
  197. package/sensor/dist/mcp/daemon-paths.js +65 -2
  198. package/sensor/dist/mcp/daemon-paths.js.map +1 -1
  199. package/sensor/dist/mcp/daemon-registry.d.ts +17 -4
  200. package/sensor/dist/mcp/daemon-registry.d.ts.map +1 -1
  201. package/sensor/dist/mcp/daemon-registry.js +133 -23
  202. package/sensor/dist/mcp/daemon-registry.js.map +1 -1
  203. package/sensor/dist/mcp/daemon.d.ts +26 -13
  204. package/sensor/dist/mcp/daemon.d.ts.map +1 -1
  205. package/sensor/dist/mcp/daemon.js +126 -32
  206. package/sensor/dist/mcp/daemon.js.map +1 -1
  207. package/sensor/dist/mcp/dynamic-boundaries.d.ts +1 -10
  208. package/sensor/dist/mcp/dynamic-boundaries.d.ts.map +1 -1
  209. package/sensor/dist/mcp/dynamic-boundaries.js +46 -42
  210. package/sensor/dist/mcp/dynamic-boundaries.js.map +1 -1
  211. package/sensor/dist/mcp/engine.d.ts +31 -9
  212. package/sensor/dist/mcp/engine.d.ts.map +1 -1
  213. package/sensor/dist/mcp/engine.js +190 -79
  214. package/sensor/dist/mcp/engine.js.map +1 -1
  215. package/sensor/dist/mcp/explore-dedup.d.ts +140 -0
  216. package/sensor/dist/mcp/explore-dedup.d.ts.map +1 -0
  217. package/sensor/dist/mcp/explore-dedup.js +239 -0
  218. package/sensor/dist/mcp/explore-dedup.js.map +1 -0
  219. package/sensor/dist/mcp/explore-diagnostics.d.ts +289 -0
  220. package/sensor/dist/mcp/explore-diagnostics.d.ts.map +1 -0
  221. package/sensor/dist/mcp/explore-diagnostics.js +558 -0
  222. package/sensor/dist/mcp/explore-diagnostics.js.map +1 -0
  223. package/sensor/dist/mcp/explore-session-state.d.ts +217 -0
  224. package/sensor/dist/mcp/explore-session-state.d.ts.map +1 -0
  225. package/sensor/dist/mcp/explore-session-state.js +322 -0
  226. package/sensor/dist/mcp/explore-session-state.js.map +1 -0
  227. package/sensor/dist/mcp/index-freshness-worker.d.ts +2 -0
  228. package/sensor/dist/mcp/index-freshness-worker.d.ts.map +1 -0
  229. package/sensor/dist/mcp/index-freshness-worker.js +30 -0
  230. package/sensor/dist/mcp/index-freshness-worker.js.map +1 -0
  231. package/sensor/dist/mcp/index-freshness.d.ts +7 -0
  232. package/sensor/dist/mcp/index-freshness.d.ts.map +1 -0
  233. package/sensor/dist/mcp/index-freshness.js +94 -0
  234. package/sensor/dist/mcp/index-freshness.js.map +1 -0
  235. package/sensor/dist/mcp/index.d.ts +3 -1
  236. package/sensor/dist/mcp/index.d.ts.map +1 -1
  237. package/sensor/dist/mcp/index.js +109 -12
  238. package/sensor/dist/mcp/index.js.map +1 -1
  239. package/sensor/dist/mcp/liveness-watchdog.d.ts +4 -1
  240. package/sensor/dist/mcp/liveness-watchdog.d.ts.map +1 -1
  241. package/sensor/dist/mcp/liveness-watchdog.js +11 -2
  242. package/sensor/dist/mcp/liveness-watchdog.js.map +1 -1
  243. package/sensor/dist/mcp/project-lifecycle.d.ts +9 -0
  244. package/sensor/dist/mcp/project-lifecycle.d.ts.map +1 -0
  245. package/sensor/dist/mcp/project-lifecycle.js +182 -0
  246. package/sensor/dist/mcp/project-lifecycle.js.map +1 -0
  247. package/sensor/dist/mcp/proxy.d.ts.map +1 -1
  248. package/sensor/dist/mcp/proxy.js +15 -1
  249. package/sensor/dist/mcp/proxy.js.map +1 -1
  250. package/sensor/dist/mcp/query-pool.d.ts +2 -2
  251. package/sensor/dist/mcp/query-pool.d.ts.map +1 -1
  252. package/sensor/dist/mcp/query-pool.js +9 -7
  253. package/sensor/dist/mcp/query-pool.js.map +1 -1
  254. package/sensor/dist/mcp/query-worker.js +2 -2
  255. package/sensor/dist/mcp/query-worker.js.map +1 -1
  256. package/sensor/dist/mcp/server-instructions.d.ts +2 -2
  257. package/sensor/dist/mcp/server-instructions.d.ts.map +1 -1
  258. package/sensor/dist/mcp/server-instructions.js +13 -6
  259. package/sensor/dist/mcp/server-instructions.js.map +1 -1
  260. package/sensor/dist/mcp/session.d.ts +16 -0
  261. package/sensor/dist/mcp/session.d.ts.map +1 -1
  262. package/sensor/dist/mcp/session.js +36 -13
  263. package/sensor/dist/mcp/session.js.map +1 -1
  264. package/sensor/dist/mcp/startup-handshake.d.ts +3 -1
  265. package/sensor/dist/mcp/startup-handshake.d.ts.map +1 -1
  266. package/sensor/dist/mcp/startup-handshake.js +10 -2
  267. package/sensor/dist/mcp/startup-handshake.js.map +1 -1
  268. package/sensor/dist/mcp/tools.d.ts +496 -38
  269. package/sensor/dist/mcp/tools.d.ts.map +1 -1
  270. package/sensor/dist/mcp/tools.js +4021 -772
  271. package/sensor/dist/mcp/tools.js.map +1 -1
  272. package/sensor/dist/mcp/writer-lock.d.ts +61 -0
  273. package/sensor/dist/mcp/writer-lock.d.ts.map +1 -0
  274. package/sensor/dist/mcp/writer-lock.js +260 -0
  275. package/sensor/dist/mcp/writer-lock.js.map +1 -0
  276. package/sensor/dist/project-config.d.ts +23 -0
  277. package/sensor/dist/project-config.d.ts.map +1 -1
  278. package/sensor/dist/project-config.js +39 -2
  279. package/sensor/dist/project-config.js.map +1 -1
  280. package/sensor/dist/resolution/alias-binding.d.ts +56 -0
  281. package/sensor/dist/resolution/alias-binding.d.ts.map +1 -0
  282. package/sensor/dist/resolution/alias-binding.js +127 -0
  283. package/sensor/dist/resolution/alias-binding.js.map +1 -0
  284. package/sensor/dist/resolution/c-fnptr-synthesizer.d.ts.map +1 -1
  285. package/sensor/dist/resolution/c-fnptr-synthesizer.js +21 -19
  286. package/sensor/dist/resolution/c-fnptr-synthesizer.js.map +1 -1
  287. package/sensor/dist/resolution/callback-synthesizer.d.ts +23 -1
  288. package/sensor/dist/resolution/callback-synthesizer.d.ts.map +1 -1
  289. package/sensor/dist/resolution/callback-synthesizer.js +356 -155
  290. package/sensor/dist/resolution/callback-synthesizer.js.map +1 -1
  291. package/sensor/dist/resolution/cpp-constructor.d.ts +4 -0
  292. package/sensor/dist/resolution/cpp-constructor.d.ts.map +1 -0
  293. package/sensor/dist/resolution/cpp-constructor.js +123 -0
  294. package/sensor/dist/resolution/cpp-constructor.js.map +1 -0
  295. package/sensor/dist/resolution/cpp-macro-visibility.d.ts +12 -0
  296. package/sensor/dist/resolution/cpp-macro-visibility.d.ts.map +1 -0
  297. package/sensor/dist/resolution/cpp-macro-visibility.js +342 -0
  298. package/sensor/dist/resolution/cpp-macro-visibility.js.map +1 -0
  299. package/sensor/dist/resolution/expo-router-synthesizer.d.ts +32 -0
  300. package/sensor/dist/resolution/expo-router-synthesizer.d.ts.map +1 -0
  301. package/sensor/dist/resolution/expo-router-synthesizer.js +189 -0
  302. package/sensor/dist/resolution/expo-router-synthesizer.js.map +1 -0
  303. package/sensor/dist/resolution/frameworks/csharp.d.ts.map +1 -1
  304. package/sensor/dist/resolution/frameworks/csharp.js +96 -2
  305. package/sensor/dist/resolution/frameworks/csharp.js.map +1 -1
  306. package/sensor/dist/resolution/frameworks/expo-router.d.ts +189 -0
  307. package/sensor/dist/resolution/frameworks/expo-router.d.ts.map +1 -0
  308. package/sensor/dist/resolution/frameworks/expo-router.js +785 -0
  309. package/sensor/dist/resolution/frameworks/expo-router.js.map +1 -0
  310. package/sensor/dist/resolution/frameworks/express.d.ts.map +1 -1
  311. package/sensor/dist/resolution/frameworks/express.js +251 -16
  312. package/sensor/dist/resolution/frameworks/express.js.map +1 -1
  313. package/sensor/dist/resolution/frameworks/index.d.ts +5 -0
  314. package/sensor/dist/resolution/frameworks/index.d.ts.map +1 -1
  315. package/sensor/dist/resolution/frameworks/index.js +29 -1
  316. package/sensor/dist/resolution/frameworks/index.js.map +1 -1
  317. package/sensor/dist/resolution/frameworks/java.d.ts.map +1 -1
  318. package/sensor/dist/resolution/frameworks/java.js +79 -47
  319. package/sensor/dist/resolution/frameworks/java.js.map +1 -1
  320. package/sensor/dist/resolution/frameworks/laravel.d.ts.map +1 -1
  321. package/sensor/dist/resolution/frameworks/laravel.js +294 -4
  322. package/sensor/dist/resolution/frameworks/laravel.js.map +1 -1
  323. package/sensor/dist/resolution/frameworks/nestjs.d.ts.map +1 -1
  324. package/sensor/dist/resolution/frameworks/nestjs.js +6 -14
  325. package/sensor/dist/resolution/frameworks/nestjs.js.map +1 -1
  326. package/sensor/dist/resolution/frameworks/nextjs.d.ts +76 -0
  327. package/sensor/dist/resolution/frameworks/nextjs.d.ts.map +1 -0
  328. package/sensor/dist/resolution/frameworks/nextjs.js +328 -0
  329. package/sensor/dist/resolution/frameworks/nextjs.js.map +1 -0
  330. package/sensor/dist/resolution/frameworks/object-literal.d.ts +32 -0
  331. package/sensor/dist/resolution/frameworks/object-literal.d.ts.map +1 -0
  332. package/sensor/dist/resolution/frameworks/object-literal.js +139 -0
  333. package/sensor/dist/resolution/frameworks/object-literal.js.map +1 -0
  334. package/sensor/dist/resolution/frameworks/package-deps.d.ts +14 -0
  335. package/sensor/dist/resolution/frameworks/package-deps.d.ts.map +1 -0
  336. package/sensor/dist/resolution/frameworks/package-deps.js +78 -0
  337. package/sensor/dist/resolution/frameworks/package-deps.js.map +1 -0
  338. package/sensor/dist/resolution/frameworks/python.d.ts.map +1 -1
  339. package/sensor/dist/resolution/frameworks/python.js +166 -0
  340. package/sensor/dist/resolution/frameworks/python.js.map +1 -1
  341. package/sensor/dist/resolution/frameworks/react-native.d.ts +26 -0
  342. package/sensor/dist/resolution/frameworks/react-native.d.ts.map +1 -1
  343. package/sensor/dist/resolution/frameworks/react-native.js +153 -14
  344. package/sensor/dist/resolution/frameworks/react-native.js.map +1 -1
  345. package/sensor/dist/resolution/frameworks/react-router.d.ts +46 -0
  346. package/sensor/dist/resolution/frameworks/react-router.d.ts.map +1 -0
  347. package/sensor/dist/resolution/frameworks/react-router.js +187 -0
  348. package/sensor/dist/resolution/frameworks/react-router.js.map +1 -0
  349. package/sensor/dist/resolution/frameworks/react.d.ts +2 -1
  350. package/sensor/dist/resolution/frameworks/react.d.ts.map +1 -1
  351. package/sensor/dist/resolution/frameworks/react.js +188 -139
  352. package/sensor/dist/resolution/frameworks/react.js.map +1 -1
  353. package/sensor/dist/resolution/frameworks/svelte.d.ts.map +1 -1
  354. package/sensor/dist/resolution/frameworks/svelte.js +6 -1
  355. package/sensor/dist/resolution/frameworks/svelte.js.map +1 -1
  356. package/sensor/dist/resolution/frameworks/sveltekit-router.d.ts +36 -0
  357. package/sensor/dist/resolution/frameworks/sveltekit-router.d.ts.map +1 -0
  358. package/sensor/dist/resolution/frameworks/sveltekit-router.js +156 -0
  359. package/sensor/dist/resolution/frameworks/sveltekit-router.js.map +1 -0
  360. package/sensor/dist/resolution/frameworks/swift.d.ts.map +1 -1
  361. package/sensor/dist/resolution/frameworks/swift.js +140 -99
  362. package/sensor/dist/resolution/frameworks/swift.js.map +1 -1
  363. package/sensor/dist/resolution/frameworks/tanstack-router.d.ts +84 -0
  364. package/sensor/dist/resolution/frameworks/tanstack-router.d.ts.map +1 -0
  365. package/sensor/dist/resolution/frameworks/tanstack-router.js +437 -0
  366. package/sensor/dist/resolution/frameworks/tanstack-router.js.map +1 -0
  367. package/sensor/dist/resolution/frameworks/vue-router.d.ts +82 -0
  368. package/sensor/dist/resolution/frameworks/vue-router.d.ts.map +1 -0
  369. package/sensor/dist/resolution/frameworks/vue-router.js +377 -0
  370. package/sensor/dist/resolution/frameworks/vue-router.js.map +1 -0
  371. package/sensor/dist/resolution/import-resolver.d.ts +38 -0
  372. package/sensor/dist/resolution/import-resolver.d.ts.map +1 -1
  373. package/sensor/dist/resolution/import-resolver.js +394 -28
  374. package/sensor/dist/resolution/import-resolver.js.map +1 -1
  375. package/sensor/dist/resolution/index.d.ts +89 -25
  376. package/sensor/dist/resolution/index.d.ts.map +1 -1
  377. package/sensor/dist/resolution/index.js +573 -148
  378. package/sensor/dist/resolution/index.js.map +1 -1
  379. package/sensor/dist/resolution/js-builtins.d.ts +13 -0
  380. package/sensor/dist/resolution/js-builtins.d.ts.map +1 -0
  381. package/sensor/dist/resolution/js-builtins.js +46 -0
  382. package/sensor/dist/resolution/js-builtins.js.map +1 -0
  383. package/sensor/dist/resolution/name-matcher.d.ts +99 -22
  384. package/sensor/dist/resolution/name-matcher.d.ts.map +1 -1
  385. package/sensor/dist/resolution/name-matcher.js +2220 -86
  386. package/sensor/dist/resolution/name-matcher.js.map +1 -1
  387. package/sensor/dist/resolution/next-router-synthesizer.d.ts +27 -0
  388. package/sensor/dist/resolution/next-router-synthesizer.d.ts.map +1 -0
  389. package/sensor/dist/resolution/next-router-synthesizer.js +113 -0
  390. package/sensor/dist/resolution/next-router-synthesizer.js.map +1 -0
  391. package/sensor/dist/resolution/path-aliases.d.ts +6 -4
  392. package/sensor/dist/resolution/path-aliases.d.ts.map +1 -1
  393. package/sensor/dist/resolution/path-aliases.js +125 -17
  394. package/sensor/dist/resolution/path-aliases.js.map +1 -1
  395. package/sensor/dist/resolution/react-router-synthesizer.d.ts +33 -0
  396. package/sensor/dist/resolution/react-router-synthesizer.d.ts.map +1 -0
  397. package/sensor/dist/resolution/react-router-synthesizer.js +126 -0
  398. package/sensor/dist/resolution/react-router-synthesizer.js.map +1 -0
  399. package/sensor/dist/resolution/resolver-pool.d.ts +2 -0
  400. package/sensor/dist/resolution/resolver-pool.d.ts.map +1 -1
  401. package/sensor/dist/resolution/resolver-pool.js +4 -0
  402. package/sensor/dist/resolution/resolver-pool.js.map +1 -1
  403. package/sensor/dist/resolution/strip-comments.d.ts +6 -0
  404. package/sensor/dist/resolution/strip-comments.d.ts.map +1 -1
  405. package/sensor/dist/resolution/strip-comments.js +58 -0
  406. package/sensor/dist/resolution/strip-comments.js.map +1 -1
  407. package/sensor/dist/resolution/sveltekit-synthesizer.d.ts +58 -0
  408. package/sensor/dist/resolution/sveltekit-synthesizer.d.ts.map +1 -0
  409. package/sensor/dist/resolution/sveltekit-synthesizer.js +173 -0
  410. package/sensor/dist/resolution/sveltekit-synthesizer.js.map +1 -0
  411. package/sensor/dist/resolution/swift-type-visibility.d.ts +52 -0
  412. package/sensor/dist/resolution/swift-type-visibility.d.ts.map +1 -0
  413. package/sensor/dist/resolution/swift-type-visibility.js +328 -0
  414. package/sensor/dist/resolution/swift-type-visibility.js.map +1 -0
  415. package/sensor/dist/resolution/synth-utils.d.ts +26 -0
  416. package/sensor/dist/resolution/synth-utils.d.ts.map +1 -0
  417. package/sensor/dist/resolution/synth-utils.js +75 -0
  418. package/sensor/dist/resolution/synth-utils.js.map +1 -0
  419. package/sensor/dist/resolution/tanstack-router-synthesizer.d.ts +31 -0
  420. package/sensor/dist/resolution/tanstack-router-synthesizer.d.ts.map +1 -0
  421. package/sensor/dist/resolution/tanstack-router-synthesizer.js +114 -0
  422. package/sensor/dist/resolution/tanstack-router-synthesizer.js.map +1 -0
  423. package/sensor/dist/resolution/tier-synthesizer.d.ts +54 -0
  424. package/sensor/dist/resolution/tier-synthesizer.d.ts.map +1 -0
  425. package/sensor/dist/resolution/tier-synthesizer.js +1294 -0
  426. package/sensor/dist/resolution/tier-synthesizer.js.map +1 -0
  427. package/sensor/dist/resolution/types.d.ts +78 -2
  428. package/sensor/dist/resolution/types.d.ts.map +1 -1
  429. package/sensor/dist/resolution/types.js +64 -0
  430. package/sensor/dist/resolution/types.js.map +1 -1
  431. package/sensor/dist/resolution/vue-router-synthesizer.d.ts +30 -0
  432. package/sensor/dist/resolution/vue-router-synthesizer.d.ts.map +1 -0
  433. package/sensor/dist/resolution/vue-router-synthesizer.js +115 -0
  434. package/sensor/dist/resolution/vue-router-synthesizer.js.map +1 -0
  435. package/sensor/dist/search/identifier-segments.d.ts +10 -0
  436. package/sensor/dist/search/identifier-segments.d.ts.map +1 -1
  437. package/sensor/dist/search/identifier-segments.js +29 -0
  438. package/sensor/dist/search/identifier-segments.js.map +1 -1
  439. package/sensor/dist/search/query-paths.d.ts +106 -0
  440. package/sensor/dist/search/query-paths.d.ts.map +1 -0
  441. package/sensor/dist/search/query-paths.js +513 -0
  442. package/sensor/dist/search/query-paths.js.map +1 -0
  443. package/sensor/dist/search/query-utils.d.ts +22 -2
  444. package/sensor/dist/search/query-utils.d.ts.map +1 -1
  445. package/sensor/dist/search/query-utils.js +56 -16
  446. package/sensor/dist/search/query-utils.js.map +1 -1
  447. package/sensor/dist/sync/watch-policy.d.ts +14 -0
  448. package/sensor/dist/sync/watch-policy.d.ts.map +1 -1
  449. package/sensor/dist/sync/watch-policy.js +19 -0
  450. package/sensor/dist/sync/watch-policy.js.map +1 -1
  451. package/sensor/dist/sync/watcher.d.ts +74 -35
  452. package/sensor/dist/sync/watcher.d.ts.map +1 -1
  453. package/sensor/dist/sync/watcher.js +281 -99
  454. package/sensor/dist/sync/watcher.js.map +1 -1
  455. package/sensor/dist/telemetry/index.d.ts +4 -2
  456. package/sensor/dist/telemetry/index.d.ts.map +1 -1
  457. package/sensor/dist/telemetry/index.js +92 -41
  458. package/sensor/dist/telemetry/index.js.map +1 -1
  459. package/sensor/dist/types.d.ts +18 -2
  460. package/sensor/dist/types.d.ts.map +1 -1
  461. package/sensor/dist/types.js +2 -0
  462. package/sensor/dist/types.js.map +1 -1
  463. package/sensor/dist/ui-server/api/deadcode.d.ts +84 -0
  464. package/sensor/dist/ui-server/api/deadcode.d.ts.map +1 -0
  465. package/sensor/dist/ui-server/api/deadcode.js +136 -0
  466. package/sensor/dist/ui-server/api/deadcode.js.map +1 -0
  467. package/sensor/dist/ui-server/api/effects.d.ts +94 -0
  468. package/sensor/dist/ui-server/api/effects.d.ts.map +1 -0
  469. package/sensor/dist/ui-server/api/effects.js +527 -0
  470. package/sensor/dist/ui-server/api/effects.js.map +1 -0
  471. package/sensor/dist/ui-server/api/entrypoints.d.ts +92 -0
  472. package/sensor/dist/ui-server/api/entrypoints.d.ts.map +1 -0
  473. package/sensor/dist/ui-server/api/entrypoints.js +361 -0
  474. package/sensor/dist/ui-server/api/entrypoints.js.map +1 -0
  475. package/sensor/dist/ui-server/api/events.d.ts +183 -0
  476. package/sensor/dist/ui-server/api/events.d.ts.map +1 -0
  477. package/sensor/dist/ui-server/api/events.js +430 -0
  478. package/sensor/dist/ui-server/api/events.js.map +1 -0
  479. package/sensor/dist/ui-server/api/file.d.ts +66 -0
  480. package/sensor/dist/ui-server/api/file.d.ts.map +1 -0
  481. package/sensor/dist/ui-server/api/file.js +235 -0
  482. package/sensor/dist/ui-server/api/file.js.map +1 -0
  483. package/sensor/dist/ui-server/api/filecode.d.ts +126 -0
  484. package/sensor/dist/ui-server/api/filecode.d.ts.map +1 -0
  485. package/sensor/dist/ui-server/api/filecode.js +205 -0
  486. package/sensor/dist/ui-server/api/filecode.js.map +1 -0
  487. package/sensor/dist/ui-server/api/flow.d.ts +226 -0
  488. package/sensor/dist/ui-server/api/flow.d.ts.map +1 -0
  489. package/sensor/dist/ui-server/api/flow.js +612 -0
  490. package/sensor/dist/ui-server/api/flow.js.map +1 -0
  491. package/sensor/dist/ui-server/api/hierarchy.d.ts +81 -0
  492. package/sensor/dist/ui-server/api/hierarchy.d.ts.map +1 -0
  493. package/sensor/dist/ui-server/api/hierarchy.js +92 -0
  494. package/sensor/dist/ui-server/api/hierarchy.js.map +1 -0
  495. package/sensor/dist/ui-server/api/index.d.ts +90 -0
  496. package/sensor/dist/ui-server/api/index.d.ts.map +1 -0
  497. package/sensor/dist/ui-server/api/index.js +332 -0
  498. package/sensor/dist/ui-server/api/index.js.map +1 -0
  499. package/sensor/dist/ui-server/api/map.d.ts +256 -0
  500. package/sensor/dist/ui-server/api/map.d.ts.map +1 -0
  501. package/sensor/dist/ui-server/api/map.js +760 -0
  502. package/sensor/dist/ui-server/api/map.js.map +1 -0
  503. package/sensor/dist/ui-server/api/node.d.ts +81 -0
  504. package/sensor/dist/ui-server/api/node.d.ts.map +1 -0
  505. package/sensor/dist/ui-server/api/node.js +388 -0
  506. package/sensor/dist/ui-server/api/node.js.map +1 -0
  507. package/sensor/dist/ui-server/api/nodes.d.ts +25 -0
  508. package/sensor/dist/ui-server/api/nodes.d.ts.map +1 -0
  509. package/sensor/dist/ui-server/api/nodes.js +45 -0
  510. package/sensor/dist/ui-server/api/nodes.js.map +1 -0
  511. package/sensor/dist/ui-server/api/program.d.ts +139 -0
  512. package/sensor/dist/ui-server/api/program.d.ts.map +1 -0
  513. package/sensor/dist/ui-server/api/program.js +315 -0
  514. package/sensor/dist/ui-server/api/program.js.map +1 -0
  515. package/sensor/dist/ui-server/api/respond.d.ts +76 -0
  516. package/sensor/dist/ui-server/api/respond.d.ts.map +1 -0
  517. package/sensor/dist/ui-server/api/respond.js +186 -0
  518. package/sensor/dist/ui-server/api/respond.js.map +1 -0
  519. package/sensor/dist/ui-server/api/route-roots.d.ts +39 -0
  520. package/sensor/dist/ui-server/api/route-roots.d.ts.map +1 -0
  521. package/sensor/dist/ui-server/api/route-roots.js +96 -0
  522. package/sensor/dist/ui-server/api/route-roots.js.map +1 -0
  523. package/sensor/dist/ui-server/api/routes.d.ts +59 -0
  524. package/sensor/dist/ui-server/api/routes.d.ts.map +1 -0
  525. package/sensor/dist/ui-server/api/routes.js +108 -0
  526. package/sensor/dist/ui-server/api/routes.js.map +1 -0
  527. package/sensor/dist/ui-server/api/screens.d.ts +117 -0
  528. package/sensor/dist/ui-server/api/screens.d.ts.map +1 -0
  529. package/sensor/dist/ui-server/api/screens.js +611 -0
  530. package/sensor/dist/ui-server/api/screens.js.map +1 -0
  531. package/sensor/dist/ui-server/api/search.d.ts +29 -0
  532. package/sensor/dist/ui-server/api/search.d.ts.map +1 -0
  533. package/sensor/dist/ui-server/api/search.js +232 -0
  534. package/sensor/dist/ui-server/api/search.js.map +1 -0
  535. package/sensor/dist/ui-server/api/session.d.ts +52 -0
  536. package/sensor/dist/ui-server/api/session.d.ts.map +1 -0
  537. package/sensor/dist/ui-server/api/session.js +196 -0
  538. package/sensor/dist/ui-server/api/session.js.map +1 -0
  539. package/sensor/dist/ui-server/api/source.d.ts +200 -0
  540. package/sensor/dist/ui-server/api/source.d.ts.map +1 -0
  541. package/sensor/dist/ui-server/api/source.js +422 -0
  542. package/sensor/dist/ui-server/api/source.js.map +1 -0
  543. package/sensor/dist/ui-server/api/stats.d.ts +27 -0
  544. package/sensor/dist/ui-server/api/stats.d.ts.map +1 -0
  545. package/sensor/dist/ui-server/api/stats.js +164 -0
  546. package/sensor/dist/ui-server/api/stats.js.map +1 -0
  547. package/sensor/dist/ui-server/api/steps.d.ts +274 -0
  548. package/sensor/dist/ui-server/api/steps.d.ts.map +1 -0
  549. package/sensor/dist/ui-server/api/steps.js +1544 -0
  550. package/sensor/dist/ui-server/api/steps.js.map +1 -0
  551. package/sensor/dist/ui-server/api/trail-store.d.ts +142 -0
  552. package/sensor/dist/ui-server/api/trail-store.d.ts.map +1 -0
  553. package/sensor/dist/ui-server/api/trail-store.js +325 -0
  554. package/sensor/dist/ui-server/api/trail-store.js.map +1 -0
  555. package/sensor/dist/ui-server/api/trails.d.ts +163 -0
  556. package/sensor/dist/ui-server/api/trails.d.ts.map +1 -0
  557. package/sensor/dist/ui-server/api/trails.js +369 -0
  558. package/sensor/dist/ui-server/api/trails.js.map +1 -0
  559. package/sensor/dist/ui-server/api/when.d.ts +133 -0
  560. package/sensor/dist/ui-server/api/when.d.ts.map +1 -0
  561. package/sensor/dist/ui-server/api/when.js +275 -0
  562. package/sensor/dist/ui-server/api/when.js.map +1 -0
  563. package/sensor/dist/ui-server/api/wire.d.ts +179 -0
  564. package/sensor/dist/ui-server/api/wire.d.ts.map +1 -0
  565. package/sensor/dist/ui-server/api/wire.js +231 -0
  566. package/sensor/dist/ui-server/api/wire.js.map +1 -0
  567. package/sensor/dist/ui-server/assets.d.ts +40 -0
  568. package/sensor/dist/ui-server/assets.d.ts.map +1 -0
  569. package/sensor/dist/ui-server/assets.js +110 -0
  570. package/sensor/dist/ui-server/assets.js.map +1 -0
  571. package/sensor/dist/ui-server/constants.d.ts +32 -0
  572. package/sensor/dist/ui-server/constants.d.ts.map +1 -0
  573. package/sensor/dist/ui-server/constants.js +35 -0
  574. package/sensor/dist/ui-server/constants.js.map +1 -0
  575. package/sensor/dist/ui-server/highlight/index.d.ts +113 -0
  576. package/sensor/dist/ui-server/highlight/index.d.ts.map +1 -0
  577. package/sensor/dist/ui-server/highlight/index.js +307 -0
  578. package/sensor/dist/ui-server/highlight/index.js.map +1 -0
  579. package/sensor/dist/ui-server/highlight/languages.d.ts +36 -0
  580. package/sensor/dist/ui-server/highlight/languages.d.ts.map +1 -0
  581. package/sensor/dist/ui-server/highlight/languages.js +50 -0
  582. package/sensor/dist/ui-server/highlight/languages.js.map +1 -0
  583. package/sensor/dist/ui-server/index.d.ts +92 -0
  584. package/sensor/dist/ui-server/index.d.ts.map +1 -0
  585. package/sensor/dist/ui-server/index.js +399 -0
  586. package/sensor/dist/ui-server/index.js.map +1 -0
  587. package/sensor/dist/ui-server/open-browser.d.ts +31 -0
  588. package/sensor/dist/ui-server/open-browser.d.ts.map +1 -0
  589. package/sensor/dist/ui-server/open-browser.js +79 -0
  590. package/sensor/dist/ui-server/open-browser.js.map +1 -0
  591. package/sensor/dist/ui-server/security.d.ts +127 -0
  592. package/sensor/dist/ui-server/security.d.ts.map +1 -0
  593. package/sensor/dist/ui-server/security.js +332 -0
  594. package/sensor/dist/ui-server/security.js.map +1 -0
  595. package/sensor/dist/ui-server/static.d.ts +45 -0
  596. package/sensor/dist/ui-server/static.d.ts.map +1 -0
  597. package/sensor/dist/ui-server/static.js +165 -0
  598. package/sensor/dist/ui-server/static.js.map +1 -0
  599. package/sensor/dist/utils.d.ts +14 -2
  600. package/sensor/dist/utils.d.ts.map +1 -1
  601. package/sensor/dist/utils.js +25 -10
  602. package/sensor/dist/utils.js.map +1 -1
  603. package/src/cli-help.mjs +4 -2
  604. package/src/sensor-adapter.mjs +5 -1
  605. package/src/todo-cli.mjs +88 -11
@@ -47,6 +47,7 @@ const function_ref_1 = require("./function-ref");
47
47
  const generated_detection_1 = require("./generated-detection");
48
48
  const languages_1 = require("./languages");
49
49
  const c_cpp_1 = require("./languages/c-cpp");
50
+ const rust_1 = require("./languages/rust");
50
51
  const liquid_extractor_1 = require("./liquid-extractor");
51
52
  const razor_extractor_1 = require("./razor-extractor");
52
53
  const svelte_extractor_1 = require("./svelte-extractor");
@@ -73,6 +74,19 @@ const RTK_HOOK_NAME_RE = /^use[A-Z][A-Za-z0-9]*(?:Query|Mutation)$/;
73
74
  /** React HOC callees whose result is itself a component — a PascalCase const
74
75
  * initialized with one of these is a component, not a constant (#841). */
75
76
  const REACT_COMPONENT_HOCS = new Set(['forwardRef', 'memo', 'React.forwardRef', 'React.memo']);
77
+ /**
78
+ * Method node types that spell a SIGNATURE — a declaration with no body (#1638).
79
+ *
80
+ * They are a method of whatever type declares them and nothing on their own, so
81
+ * they must not take `extractMethod`'s "no class-like parent, so treat it as a
82
+ * free function" fallback. The other `methodTypes` can: a `method_definition`
83
+ * outside a class really is a function. This one appears outside a class only
84
+ * inside a type literal (`type Handle = { stop(): void }`), whose members
85
+ * `extractTypeAlias` already extracts and attaches to the alias (#359) — take
86
+ * the fallback and the file gains a phantom top-level `function stop` beside
87
+ * the real `Handle::stop`.
88
+ */
89
+ const SIGNATURE_METHOD_NODE_TYPES = new Set(['method_signature']);
76
90
  /** Vue store collections whose object-literal members are the symbols an agent
77
91
  * looks for. Extracted as function nodes so `actions`/`mutations`/`getters` are
78
92
  * findable + readable (the foundation under any later dispatch-bridge synth). */
@@ -187,7 +201,7 @@ function extractNameRaw(node, source, extractor) {
187
201
  // not from identifiers in their body. Without this, single-expression arrow
188
202
  // functions like `const fn = () => someIdentifier` get named "someIdentifier"
189
203
  // instead of "fn", because the fallback below finds the body identifier.
190
- if (node.type === 'arrow_function' || node.type === 'function_expression') {
204
+ if (node.type === 'arrow_function' || node.type === 'function_expression' || node.type === 'generator_function') {
191
205
  return '<anonymous>';
192
206
  }
193
207
  // Fall back to first identifier child
@@ -385,12 +399,107 @@ const LITERAL_RECEIVER_TYPES = new Set([
385
399
  'list', 'list_literal', 'array', 'array_literal', 'array_creation_expression',
386
400
  'dictionary', 'dict_literal', 'object', 'tuple', 'set',
387
401
  ]);
402
+ /**
403
+ * Languages whose member calls go through the TS/JS grammars.
404
+ */
405
+ const TS_JS_CHAIN_LANGUAGES = new Set(['typescript', 'tsx', 'javascript', 'jsx']);
406
+ /** Receiver node types (TS/JS grammars) that continue a member chain downward. */
407
+ const TS_JS_CHAIN_RECEIVER_TYPES = new Set(['member_expression', 'subscript_expression']);
408
+ /** The field of a `this.<field>.<method>()` receiver: public or ES private (#1496, #1987). */
409
+ const THIS_FIELD_PROPERTY_TYPES = new Set(['property_identifier', 'private_property_identifier']);
410
+ /**
411
+ * Identifier-rooted member chains have no inferred property type (#1566),
412
+ * including host API chains (#1707). Keep the existing `window.MyNamespace`
413
+ * escape for project globals; call-result and `this` receivers have their own
414
+ * paths and are outside this guard.
415
+ */
416
+ function isUnresolvedTsJsChain(node, source) {
417
+ let cur = node;
418
+ while (cur && TS_JS_CHAIN_RECEIVER_TYPES.has(cur.type)) {
419
+ cur = (0, tree_sitter_helpers_1.getChildByField)(cur, 'object');
420
+ }
421
+ return !!cur && cur.type === 'identifier' && (0, tree_sitter_helpers_1.getNodeText)(cur, source) !== 'window';
422
+ }
423
+ /**
424
+ * TS/JS wrappers that leave a member call's receiver the same object:
425
+ * `(x).m()`, `x!.m()`, `(x as T).m()`, `(x satisfies T).m()`, `(<T>x).m()`
426
+ * and `(await x).m()` all call `m` on what `x` holds.
427
+ */
428
+ const TS_JS_TRANSPARENT_RECEIVER_TYPES = new Set([
429
+ 'parenthesized_expression', 'non_null_expression', 'as_expression',
430
+ 'satisfies_expression', 'type_assertion', 'await_expression',
431
+ ]);
432
+ /**
433
+ * Strip {@link TS_JS_TRANSPARENT_RECEIVER_TYPES} wrappers off a receiver.
434
+ * tree-sitter-typescript parses `a && b!.c()` as `(a && b)!.c()`; the `!`
435
+ * belongs to the right operand, so a non-null over a binary expression peels
436
+ * to that operand.
437
+ */
438
+ function peelTsJsReceiver(node) {
439
+ let cur = node;
440
+ while (TS_JS_TRANSPARENT_RECEIVER_TYPES.has(cur.type)) {
441
+ let inner = cur.type === 'type_assertion'
442
+ ? cur.namedChild(cur.namedChildCount - 1)
443
+ : cur.namedChild(0);
444
+ if (cur.type === 'non_null_expression') {
445
+ while (inner?.type === 'binary_expression')
446
+ inner = (0, tree_sitter_helpers_1.getChildByField)(inner, 'right');
447
+ }
448
+ if (!inner)
449
+ break;
450
+ cur = inner;
451
+ }
452
+ return cur;
453
+ }
454
+ /**
455
+ * Whether a TS/JS receiver still collapses to the bare method name: `this` /
456
+ * `super` (the resolver reads the owner off the enclosing class), a member
457
+ * chain rooted at either or at `window` (the project-global escape of
458
+ * {@link isUnresolvedTsJsChain}), and `new C()` (its class is written at the
459
+ * call).
460
+ */
461
+ function keepsBareTsJsReceiver(node, source) {
462
+ let cur = node;
463
+ while (cur && TS_JS_CHAIN_RECEIVER_TYPES.has(cur.type)) {
464
+ const object = (0, tree_sitter_helpers_1.getChildByField)(cur, 'object');
465
+ cur = object ? peelTsJsReceiver(object) : null;
466
+ }
467
+ if (!cur)
468
+ return false;
469
+ if (cur.type === 'this' || cur.type === 'super')
470
+ return true;
471
+ if (cur.type === 'identifier')
472
+ return (0, tree_sitter_helpers_1.getNodeText)(cur, source) === 'window';
473
+ return cur === node && cur.type === 'new_expression';
474
+ }
475
+ /**
476
+ * React hooks that bind a NAME to a handler function (`const onPress =
477
+ * useCallback(() => {…}, [])`). The arrow inside is extracted as a function
478
+ * node named by the declarator — see `reactHookBoundName`.
479
+ */
480
+ const REACT_HANDLER_HOOKS = /^(?:React\.)?use(?:Callback|EffectEvent|Event)$/;
388
481
  class TreeSitterExtractor {
482
+ /**
483
+ * The node's prose, from either place it can live: a preceding comment
484
+ * sibling (every language) or a docstring inside the body (Python's bare
485
+ * first-statement string, and the same shape in other languages that opt in
486
+ * via `getBodyDocstring`). When a node carries both, they are joined rather
487
+ * than one winning — they are two separate things the author wrote about the
488
+ * same symbol, and the column holds free text (#1905).
489
+ */
490
+ docstringFor(node) {
491
+ const preceding = (0, tree_sitter_helpers_1.getPrecedingDocstring)(node, this.source);
492
+ const body = this.extractor?.getBodyDocstring?.(node, this.source);
493
+ if (preceding && body)
494
+ return `${preceding}\n\n${body}`;
495
+ return body || preceding;
496
+ }
389
497
  filePath;
390
498
  language;
391
499
  source;
392
500
  tree = null;
393
501
  nodes = [];
502
+ nodeIds = new tree_sitter_helpers_1.NodeIdAllocator();
394
503
  edges = [];
395
504
  unresolvedReferences = [];
396
505
  // Value-reference edges (default ON; set LATTICE_SENSOR_VALUE_REFS=0 to disable; see flushValueRefs).
@@ -506,6 +615,9 @@ class TreeSitterExtractor {
506
615
  isExported: false,
507
616
  updatedAt: Date.now(),
508
617
  };
618
+ const fileDocstring = this.extractor?.getBodyDocstring?.(this.tree.rootNode, this.source);
619
+ if (fileDocstring)
620
+ fileNode.docstring = fileDocstring;
509
621
  this.nodes.push(fileNode);
510
622
  // Push file node onto stack so top-level declarations get contains edges
511
623
  this.nodeStack.push(fileNode.id);
@@ -524,6 +636,16 @@ class TreeSitterExtractor {
524
636
  if (packageNodeId)
525
637
  this.nodeStack.pop();
526
638
  this.nodeStack.pop();
639
+ // hasError is routine for several grammars; warn only when no symbols survived.
640
+ const symbolCount = this.nodes.filter((n) => n.kind !== 'file').length;
641
+ if (this.tree?.rootNode.hasError && symbolCount === 0) {
642
+ this.errors.push({
643
+ message: `${this.filePath}: parse produced no symbols (tree has errors) — ` +
644
+ `the file is indexed but contributes nothing to the graph`,
645
+ severity: 'warning',
646
+ code: 'parse_error',
647
+ });
648
+ }
527
649
  }
528
650
  catch (error) {
529
651
  const msg = error instanceof Error ? error.message : String(error);
@@ -589,6 +711,7 @@ class TreeSitterExtractor {
589
711
  return;
590
712
  const nodeType = node.type;
591
713
  if (depth > 0 && (this.extractor?.functionTypes.includes(nodeType) ||
714
+ ((this.language === 'lua' || this.language === 'luau') && nodeType === 'function_definition') ||
592
715
  nodeType === 'arrow_function' ||
593
716
  nodeType === 'function_expression' ||
594
717
  nodeType === 'lambda_literal' ||
@@ -983,7 +1106,14 @@ class TreeSitterExtractor {
983
1106
  if (n.type === 'identifier' || n.type === 'constant' ||
984
1107
  n.type === 'name' || n.type === 'simple_identifier') {
985
1108
  const refName = (0, tree_sitter_helpers_1.getNodeText)(n, this.source);
986
- const targetId = targets.get(refName);
1109
+ // C/C++: the callee of a call is a call, not a value read — even when a
1110
+ // same-named function-like macro (`#define helper(x) …`) is a file-scope
1111
+ // constant. The call edge resolves it; a value ref here would be a second,
1112
+ // wrong reading of the same token (upstream #2070 made such macros nodes).
1113
+ const calleePosition = (this.language === 'c' || this.language === 'cpp')
1114
+ && n.parent?.type === 'call_expression'
1115
+ && (0, tree_sitter_helpers_1.getChildByField)(n.parent, 'function')?.startIndex === n.startIndex;
1116
+ const targetId = calleePosition ? undefined : targets.get(refName);
987
1117
  // Skip self and same-name targets: a symbol referencing a file-scope
988
1118
  // sibling of its own name (the two halves of a conditional `try: X=…;
989
1119
  // except: X=…`) is never a meaningful value read.
@@ -1039,6 +1169,25 @@ class TreeSitterExtractor {
1039
1169
  if (skipChildren)
1040
1170
  return;
1041
1171
  }
1172
+ if (this.language === 'cpp' && (0, c_cpp_1.isCppConstructorDeclaration)(node)) {
1173
+ this.extractMethod(node);
1174
+ return;
1175
+ }
1176
+ // C/C++ function-like macros (`#define TRACE(x) ...`) become `constant`
1177
+ // nodes carrying the directive as their signature. A macro is a value,
1178
+ // never an executable callee: the resolver reads these to recognize a
1179
+ // call whose name is a macro visible in the translation unit and refuses
1180
+ // to bind it to a same-named function elsewhere (#1838). Mirrored in the
1181
+ // kernel (ccpp/mod.rs visit_node).
1182
+ if ((this.language === 'c' || this.language === 'cpp') && nodeType === 'preproc_function_def') {
1183
+ const name = (0, tree_sitter_helpers_1.getChildByField)(node, 'name');
1184
+ if (name) {
1185
+ this.createNode('constant', (0, tree_sitter_helpers_1.getNodeText)(name, this.source), node, {
1186
+ signature: (0, tree_sitter_helpers_1.getNodeText)(node, this.source).trim(),
1187
+ });
1188
+ }
1189
+ return;
1190
+ }
1042
1191
  // C++ namespace blocks: carry the namespace name as a qualifiedName prefix
1043
1192
  // while walking the body, so `namespace flash { void compute_attn(); }`
1044
1193
  // indexes compute_attn with qualifiedName `flash::compute_attn` and a
@@ -1097,6 +1246,9 @@ class TreeSitterExtractor {
1097
1246
  else if (classification === 'trait') {
1098
1247
  this.extractClass(node, 'trait');
1099
1248
  }
1249
+ else if (classification === 'module') {
1250
+ this.extractClass(node, 'module');
1251
+ }
1100
1252
  else {
1101
1253
  this.extractClass(node);
1102
1254
  }
@@ -1107,12 +1259,23 @@ class TreeSitterExtractor {
1107
1259
  this.extractClass(node);
1108
1260
  skipChildren = true;
1109
1261
  }
1110
- // Check for method declarations (only if not already handled by functionTypes)
1111
- else if (this.extractor.methodTypes.includes(nodeType)) {
1262
+ // Check for method declarations (only if not already handled by functionTypes).
1263
+ // A bodiless SIGNATURE only counts as one where a type declares it — see
1264
+ // SIGNATURE_METHOD_NODE_TYPES for what falling through would otherwise mint.
1265
+ else if (this.extractor.methodTypes.includes(nodeType)
1266
+ && (!(TS_JS_CHAIN_LANGUAGES.has(this.language) || this.language === 'arkts')
1267
+ || !SIGNATURE_METHOD_NODE_TYPES.has(nodeType) || this.isInsideClassLikeNode())) {
1112
1268
  // TS/JS class fields parse as a methodTypes node; only function-valued
1113
1269
  // fields are methods — a plain field (`public fonts: Fonts;`) is a
1114
- // property (#808). classifyMethodNode is absent for other languages.
1115
- if (this.extractor.classifyMethodNode?.(node) === 'property') {
1270
+ // property (#808). C++ lists `field_declaration` so pure-virtual methods
1271
+ // mint nodes (#1727); non-callable ones return 'skip' and fall through to
1272
+ // the children walk. classifyMethodNode is absent for other languages.
1273
+ const methodClass = this.extractor.classifyMethodNode?.(node) ?? 'method';
1274
+ if (methodClass === 'skip') {
1275
+ // Not a method — leave skipChildren false so data-member initializers
1276
+ // still contribute call/instantiation edges under the enclosing class.
1277
+ }
1278
+ else if (methodClass === 'property') {
1116
1279
  const propNode = this.extractProperty(node);
1117
1280
  // Walk the initializer so its calls/instantiations attribute to the
1118
1281
  // property (`history = createHistory()` → history calls
@@ -1145,6 +1308,11 @@ class TreeSitterExtractor {
1145
1308
  this.extractStruct(node);
1146
1309
  skipChildren = true; // extractStruct visits body children
1147
1310
  }
1311
+ // Check for union declarations
1312
+ else if (this.extractor.unionTypes?.includes(nodeType)) {
1313
+ this.extractUnion(node);
1314
+ skipChildren = true; // extractUnion visits body children
1315
+ }
1148
1316
  // Check for enum declarations
1149
1317
  else if (this.extractor.enumTypes.includes(nodeType)) {
1150
1318
  this.extractEnum(node);
@@ -1350,20 +1518,16 @@ class TreeSitterExtractor {
1350
1518
  else if (nodeType === 'impl_item') {
1351
1519
  this.extractRustImplItem(node);
1352
1520
  }
1353
- // TypeScript interface members: property_signature (`foo: T`, `foo?: T`)
1354
- // and method_signature (`foo(arg: A): R`) both carry type annotations the
1355
- // interface walker would otherwise drop. Extract them as `references`
1356
- // edges from the interface so resolvers can wire callers/impact for
1357
- // types that only appear in interface members.
1358
- else if ((nodeType === 'property_signature' || nodeType === 'method_signature') &&
1359
- this.isInsideClassLikeNode() &&
1360
- this.TYPE_ANNOTATION_LANGUAGES.has(this.language)) {
1361
- const parentId = this.nodeStack[this.nodeStack.length - 1];
1362
- if (parentId) {
1363
- this.extractTypeAnnotations(node, parentId);
1364
- }
1365
- // don't skipChildren — nested signatures still need traversal
1366
- }
1521
+ // NOTE: `property_signature` / `method_signature` used to be handled here,
1522
+ // hanging their type annotations off the ENCLOSING INTERFACE — the only
1523
+ // anchor available while the members themselves went unextracted. Since
1524
+ // #1638 they are in the TS extractor's `methodTypes` / `propertyTypes`, so
1525
+ // the branches above claim them first (under the same `isInsideClassLikeNode`
1526
+ // guard this branch had, so nothing it used to reach is now missed) and this
1527
+ // one was dead. The `references` edges survive — `extractMethod` and
1528
+ // `extractProperty` each call `extractTypeAnnotations` — but now hang off
1529
+ // the member, which is the more precise anchor: `Api::fetch → PageId` says
1530
+ // which member wants the type, where `Api → PageId` only said the file did.
1367
1531
  // Visit children (unless the extract method already visited them)
1368
1532
  if (!skipChildren) {
1369
1533
  for (let i = 0; i < node.namedChildCount; i++) {
@@ -1383,7 +1547,7 @@ class TreeSitterExtractor {
1383
1547
  if (!name) {
1384
1548
  return null;
1385
1549
  }
1386
- const id = (0, tree_sitter_helpers_1.generateNodeId)(this.filePath, kind, name, node.startPosition.row + 1);
1550
+ const id = this.nodeIds.generate(this.filePath, kind, name, node.startPosition.row + 1, node.startPosition.column);
1387
1551
  // Some grammars (e.g. Dart) model a function/method body as a *sibling* of
1388
1552
  // the signature node, so the declaration node's own range is just the
1389
1553
  // signature line. Extend endLine to the resolved body when it sits beyond
@@ -1537,7 +1701,7 @@ class TreeSitterExtractor {
1537
1701
  }
1538
1702
  /**
1539
1703
  * Check if the current node stack indicates we are inside a class-like node
1540
- * (class, struct, interface, trait). File nodes do not count as class-like.
1704
+ * (class, struct, union, interface, trait). File nodes do not count as class-like.
1541
1705
  */
1542
1706
  isInsideClassLikeNode() {
1543
1707
  if (this.nodeStack.length === 0)
@@ -1550,6 +1714,7 @@ class TreeSitterExtractor {
1550
1714
  return false;
1551
1715
  return (parentNode.kind === 'class' ||
1552
1716
  parentNode.kind === 'struct' ||
1717
+ parentNode.kind === 'union' ||
1553
1718
  parentNode.kind === 'interface' ||
1554
1719
  parentNode.kind === 'trait' ||
1555
1720
  parentNode.kind === 'enum' ||
@@ -1584,13 +1749,15 @@ class TreeSitterExtractor {
1584
1749
  // — SvelteKit actions). Inline-object arrows reached by the general walker
1585
1750
  // get no override, so they still fall through to the <anonymous> skip below.
1586
1751
  let name = nameOverride ?? extractName(node, this.source, this.extractor);
1752
+ // A CommonJS export assignment names the function it holds — see below.
1753
+ let commonJsExport = false;
1587
1754
  // For arrow functions and function expressions assigned to variables,
1588
1755
  // resolve the name from the parent variable_declarator.
1589
1756
  // e.g. `export const useAuth = () => { ... }` — the arrow_function node
1590
1757
  // has no `name` field; the name lives on the variable_declarator.
1591
1758
  if (!nameOverride &&
1592
1759
  name === '<anonymous>' &&
1593
- (node.type === 'arrow_function' || node.type === 'function_expression')) {
1760
+ (node.type === 'arrow_function' || node.type === 'function_expression' || node.type === 'generator_function')) {
1594
1761
  const parent = node.parent;
1595
1762
  if (parent?.type === 'variable_declarator') {
1596
1763
  const varName = (0, tree_sitter_helpers_1.getChildByField)(parent, 'name');
@@ -1598,6 +1765,19 @@ class TreeSitterExtractor {
1598
1765
  name = (0, tree_sitter_helpers_1.getNodeText)(varName, this.source);
1599
1766
  }
1600
1767
  }
1768
+ else if (parent?.type === 'assignment_expression') {
1769
+ // `exports.getItems = async (req, res) => {…}` / `module.exports.x =
1770
+ // function () {…}` — the CommonJS controller style. The function is
1771
+ // anonymous only syntactically: the export property is the name every
1772
+ // `router.get('/items', getItems)` resolves. Without a node the handler
1773
+ // is invisible to callers/impact and its calls attribute to the file
1774
+ // (#1675). Same treatment `const X = () => {}` already gets.
1775
+ const exportName = this.commonJsExportName(parent, node);
1776
+ if (exportName) {
1777
+ name = exportName;
1778
+ commonJsExport = true;
1779
+ }
1780
+ }
1601
1781
  }
1602
1782
  if (name === '<anonymous>') {
1603
1783
  // Don't emit a node for the anonymous wrapper itself, but still visit its
@@ -1622,10 +1802,10 @@ class TreeSitterExtractor {
1622
1802
  }
1623
1803
  return;
1624
1804
  }
1625
- const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(node, this.source);
1805
+ const docstring = this.docstringFor(node);
1626
1806
  const signature = this.extractor.getSignature?.(node, this.source);
1627
1807
  const visibility = this.extractor.getVisibility?.(node);
1628
- const isExported = this.extractor.isExported?.(node, this.source);
1808
+ const isExported = commonJsExport || this.extractor.isExported?.(node, this.source);
1629
1809
  const isAsync = this.extractor.isAsync?.(node);
1630
1810
  const isStatic = this.extractor.isStatic?.(node);
1631
1811
  const returnType = this.extractor.getReturnType?.(node, this.source);
@@ -1735,7 +1915,7 @@ class TreeSitterExtractor {
1735
1915
  if (this.extractor.skipBodilessClass && !resolvedBody)
1736
1916
  return;
1737
1917
  const name = extractName(node, this.source, this.extractor);
1738
- const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(node, this.source);
1918
+ const docstring = this.docstringFor(node);
1739
1919
  const visibility = this.extractor.getVisibility?.(node);
1740
1920
  const isExported = this.extractor.isExported?.(node, this.source);
1741
1921
  const classNode = this.createNode(kind, name, node, {
@@ -1807,11 +1987,14 @@ class TreeSitterExtractor {
1807
1987
  }
1808
1988
  return;
1809
1989
  }
1810
- const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(node, this.source);
1990
+ const docstring = this.docstringFor(node);
1811
1991
  const signature = this.extractor.getSignature?.(node, this.source);
1812
1992
  const visibility = this.extractor.getVisibility?.(node);
1813
1993
  const isAsync = this.extractor.isAsync?.(node);
1814
1994
  const isStatic = this.extractor.isStatic?.(node);
1995
+ // Only persist abstract when true — a false return must not mint `isAbstract: false`
1996
+ // on every ordinary method (breaks kernel↔wasm parity JSON equality).
1997
+ const isAbstract = this.extractor.isAbstract?.(node) ? true : undefined;
1815
1998
  const returnType = this.extractor.getReturnType?.(node, this.source);
1816
1999
  const extraProps = {
1817
2000
  docstring,
@@ -1819,6 +2002,7 @@ class TreeSitterExtractor {
1819
2002
  visibility,
1820
2003
  isAsync,
1821
2004
  isStatic,
2005
+ isAbstract,
1822
2006
  returnType,
1823
2007
  };
1824
2008
  if (receiverType) {
@@ -1832,7 +2016,7 @@ class TreeSitterExtractor {
1832
2016
  if (receiverType && !this.isInsideClassLikeNode()) {
1833
2017
  const ownerNode = this.nodes.find((n) => n.name === receiverType &&
1834
2018
  n.filePath === this.filePath &&
1835
- (n.kind === 'struct' || n.kind === 'class' || n.kind === 'enum' || n.kind === 'trait'));
2019
+ (n.kind === 'struct' || n.kind === 'union' || n.kind === 'class' || n.kind === 'enum' || n.kind === 'trait'));
1836
2020
  if (ownerNode) {
1837
2021
  this.edges.push({
1838
2022
  source: ownerNode.id,
@@ -1861,7 +2045,7 @@ class TreeSitterExtractor {
1861
2045
  if (!this.extractor)
1862
2046
  return;
1863
2047
  const name = extractName(node, this.source, this.extractor);
1864
- const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(node, this.source);
2048
+ const docstring = this.docstringFor(node);
1865
2049
  const isExported = this.extractor.isExported?.(node, this.source);
1866
2050
  const kind = this.extractor.interfaceKind ?? 'interface';
1867
2051
  const interfaceNode = this.createNode(kind, name, node, {
@@ -1890,38 +2074,53 @@ class TreeSitterExtractor {
1890
2074
  * Extract a struct
1891
2075
  */
1892
2076
  extractStruct(node) {
2077
+ this.extractAggregate(node, 'struct');
2078
+ }
2079
+ /** Extract a union while sharing the member-walk behavior of aggregate types. */
2080
+ extractUnion(node) {
2081
+ this.extractAggregate(node, 'union');
2082
+ }
2083
+ /** Extract a struct-like declaration without conflating its semantic kind. */
2084
+ extractAggregate(node, kind) {
1893
2085
  if (!this.extractor)
1894
2086
  return;
1895
2087
  // Skip forward declarations and type references (no body = not a definition)
1896
2088
  // — EXCEPT C# positional records (`record struct M(decimal Amount);`),
1897
2089
  // complete definitions with no body block. (#831)
2090
+ //
2091
+ // `allowBodilessStruct` is the per-language escape hatch for the same
2092
+ // situation: a bodiless struct that IS a complete definition (Rust's unit
2093
+ // struct `struct Unit;`). Opposite polarity from `skipBodilessClass`
2094
+ // (#1093) because the two defaults differ — a bodiless CLASS is kept
2095
+ // unless a language opts into skipping, a bodiless STRUCT is skipped
2096
+ // unless a language opts into keeping.
1898
2097
  const body = (0, tree_sitter_helpers_1.getChildByField)(node, this.extractor.bodyField);
1899
- if (!body && node.type !== 'record_declaration')
2098
+ if (!body && node.type !== 'record_declaration' && !this.extractor.allowBodilessStruct)
1900
2099
  return;
1901
2100
  const name = extractName(node, this.source, this.extractor);
1902
- const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(node, this.source);
2101
+ const docstring = this.docstringFor(node);
1903
2102
  const visibility = this.extractor.getVisibility?.(node);
1904
2103
  const isExported = this.extractor.isExported?.(node, this.source);
1905
- const structNode = this.createNode('struct', name, node, {
2104
+ const aggregateNode = this.createNode(kind, name, node, {
1906
2105
  docstring,
1907
2106
  visibility,
1908
2107
  isExported,
1909
2108
  });
1910
- if (!structNode)
2109
+ if (!aggregateNode)
1911
2110
  return;
1912
2111
  // Rust `#[derive]`/attributes precede the struct as siblings — without this
1913
2112
  // the widened extent (v11) never covers them and a cut struct silently
1914
2113
  // loses its derives.
1915
- this.extractDecoratorsFor(node, structNode.id);
2114
+ this.extractDecoratorsFor(node, aggregateNode.id);
1916
2115
  // Extract inheritance (e.g. Swift: struct HTTPMethod: RawRepresentable)
1917
- this.extractInheritance(node, structNode.id);
2116
+ this.extractInheritance(node, aggregateNode.id);
1918
2117
  // C# primary-constructor parameter dependencies (`struct P(int x)`, and
1919
2118
  // `record struct M(decimal Amount)` which the grammar nests here).
1920
- this.extractCsharpPrimaryCtorParamRefs(node, structNode.id);
2119
+ this.extractCsharpPrimaryCtorParamRefs(node, aggregateNode.id);
1921
2120
  // Push to stack for field extraction (bodiless positional records have
1922
2121
  // no members to visit)
1923
2122
  if (body) {
1924
- this.nodeStack.push(structNode.id);
2123
+ this.nodeStack.push(aggregateNode.id);
1925
2124
  for (let i = 0; i < body.namedChildCount; i++) {
1926
2125
  const child = body.namedChild(i);
1927
2126
  if (child) {
@@ -1943,7 +2142,7 @@ class TreeSitterExtractor {
1943
2142
  if (!body)
1944
2143
  return;
1945
2144
  const name = extractName(node, this.source, this.extractor);
1946
- const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(node, this.source);
2145
+ const docstring = this.docstringFor(node);
1947
2146
  const visibility = this.extractor.getVisibility?.(node);
1948
2147
  const isExported = this.extractor.isExported?.(node, this.source);
1949
2148
  const enumNode = this.createNode('enum', name, node, {
@@ -2003,7 +2202,7 @@ class TreeSitterExtractor {
2003
2202
  extractProperty(node) {
2004
2203
  if (!this.extractor)
2005
2204
  return null;
2006
- const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(node, this.source);
2205
+ const docstring = this.docstringFor(node);
2007
2206
  const visibility = this.extractor.getVisibility?.(node);
2008
2207
  const isStatic = this.extractor.isStatic?.(node) ?? false;
2009
2208
  const hookName = this.extractor.extractPropertyName?.(node, this.source);
@@ -2022,7 +2221,17 @@ class TreeSitterExtractor {
2022
2221
  // and the initializer VALUE, which the generic finder below would
2023
2222
  // wrongly pick — so fields use the type field only (#808). Other
2024
2223
  // languages (C# property_declaration) keep the generic scan.
2025
- const isTsJsField = node.type === 'public_field_definition' || node.type === 'field_definition';
2224
+ //
2225
+ // A `property_signature` (an interface member, #1638) carries a `type`
2226
+ // field and no value, so it reads the type field too. It cannot take the
2227
+ // generic scan: that scan's exclusion list covers `identifier` but not the
2228
+ // `property_identifier` an interface member is named with, so it stops on
2229
+ // the name and `interface Stats { counts: Record<string, number> }` yields
2230
+ // `signature: "counts counts"` instead of the type. Named explicitly
2231
+ // rather than folded into the field test so no other language's
2232
+ // `property_declaration` moves off the generic scan.
2233
+ const isTsJsField = node.type === 'public_field_definition' || node.type === 'field_definition'
2234
+ || node.type === 'property_signature';
2026
2235
  const typeNode = isTsJsField
2027
2236
  ? (0, tree_sitter_helpers_1.getChildByField)(node, 'type')
2028
2237
  : node.namedChildren.find(c => c.type !== 'modifier' && c.type !== 'modifiers'
@@ -2056,7 +2265,7 @@ class TreeSitterExtractor {
2056
2265
  extractField(node) {
2057
2266
  if (!this.extractor)
2058
2267
  return;
2059
- const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(node, this.source);
2268
+ const docstring = this.docstringFor(node);
2060
2269
  const visibility = this.extractor.getVisibility?.(node);
2061
2270
  const isStatic = this.extractor.isStatic?.(node) ?? false;
2062
2271
  // A class field that is actually a CONSTANT (Java `static final`, C# `const`
@@ -2136,6 +2345,21 @@ class TreeSitterExtractor {
2136
2345
  // and the language-aware path in `extractTypeAnnotations` descends
2137
2346
  // into that wrapper (#381).
2138
2347
  this.extractTypeAnnotations(node, fieldNode.id);
2348
+ // Walk the initializer ATTRIBUTED to the declared field (#693, the
2349
+ // Go fix; same shape as the TS/JS class-field walk above). The
2350
+ // dispatcher only scanned this subtree for function-as-value
2351
+ // candidates, so a lambda / method reference / anonymous class in
2352
+ // `private final Runnable r = () -> target();` contributed NO call
2353
+ // edge at all and `target` looked callerless. Keyed on the `value`
2354
+ // FIELD, which only Java's `variable_declarator` carries — C#,
2355
+ // VB.NET and PHP spell their initializer differently and are
2356
+ // deliberately untouched here.
2357
+ const valueNode = (0, tree_sitter_helpers_1.getChildByField)(decl, 'value');
2358
+ if (valueNode) {
2359
+ this.nodeStack.push(fieldNode.id);
2360
+ this.visitFunctionBody(valueNode, fieldNode.id);
2361
+ this.nodeStack.pop();
2362
+ }
2139
2363
  }
2140
2364
  }
2141
2365
  }
@@ -2171,6 +2395,13 @@ class TreeSitterExtractor {
2171
2395
  if (key && value && (value.type === 'arrow_function' || value.type === 'function_expression')) {
2172
2396
  this.extractFunction(value, this.objectKeyName(key));
2173
2397
  }
2398
+ else if (value?.type === 'call_expression') {
2399
+ // `key: Effect.fn("…")(function* () {…})` — see curriedWrapperBoundName.
2400
+ const fn = (0, tree_sitter_helpers_1.getChildByField)(value, 'arguments')?.namedChild(0);
2401
+ const bound = fn ? this.curriedWrapperBoundName(fn) : null;
2402
+ if (fn && bound)
2403
+ this.extractFunction(fn, bound);
2404
+ }
2174
2405
  }
2175
2406
  else if (member.type === 'method_definition') {
2176
2407
  // Method shorthand: `{ fetchUser() {...} }`. extractMethod deliberately
@@ -2183,6 +2414,21 @@ class TreeSitterExtractor {
2183
2414
  }
2184
2415
  }
2185
2416
  }
2417
+ /**
2418
+ * A top-level binding exported by a LATER statement rather than at its
2419
+ * declaration: `export default NAME`, `export { NAME }`, `export { NAME as
2420
+ * default }`. The declaration's own `isExported` (an `export_statement`
2421
+ * ancestor) cannot see these, so a store written as `const useStore =
2422
+ * create(…)` + `export default useStore` read as unexported and its actions
2423
+ * were never extracted. One anchored regex over the file source; JS-family
2424
+ * callers only.
2425
+ */
2426
+ isExportedLater(name) {
2427
+ if (!/^[A-Za-z_$][\w$]*$/.test(name))
2428
+ return false;
2429
+ const re = new RegExp(`^[ \\t]*export\\s+(?:default\\s+${name}\\s*;?[ \\t]*$|\\{[^}]*\\b${name}\\b[^}]*\\})`, 'm');
2430
+ return re.test(this.source);
2431
+ }
2186
2432
  /** Property-key text with surrounding quotes stripped (`'foo'` → `foo`). */
2187
2433
  objectKeyName(key) {
2188
2434
  return (0, tree_sitter_helpers_1.getNodeText)(key, this.source).replace(/^['"`]|['"`]$/g, '');
@@ -2587,7 +2833,7 @@ class TreeSitterExtractor {
2587
2833
  // Go: var_declaration, short_var_declaration, const_declaration
2588
2834
  const isConst = this.extractor.isConst?.(node) ?? false;
2589
2835
  const kind = isConst ? 'constant' : 'variable';
2590
- const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(node, this.source);
2836
+ const docstring = this.docstringFor(node);
2591
2837
  const isExported = this.extractor.isExported?.(node, this.source) ?? false;
2592
2838
  // Extract variable declarators based on language
2593
2839
  if (this.language === 'typescript' || this.language === 'javascript' ||
@@ -2615,7 +2861,7 @@ class TreeSitterExtractor {
2615
2861
  }
2616
2862
  const name = (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source);
2617
2863
  // Arrow functions / function expressions: extract as function instead of variable
2618
- if (valueNode && (valueNode.type === 'arrow_function' || valueNode.type === 'function_expression')) {
2864
+ if (valueNode && (valueNode.type === 'arrow_function' || valueNode.type === 'function_expression' || valueNode.type === 'generator_function')) {
2619
2865
  this.extractFunction(valueNode);
2620
2866
  continue;
2621
2867
  }
@@ -2665,7 +2911,10 @@ class TreeSitterExtractor {
2665
2911
  // never nodes — so `node`/`callers` on `fetchUser` return "not
2666
2912
  // found" and the agent Reads the store to reconstruct the flow.
2667
2913
  // Scoped to EXPORTED consts to exclude inline-object noise
2668
- // (`ctx.set({...})`) the object-method skip deliberately avoids.
2914
+ // (`ctx.set({...})`) the object-method skip deliberately avoids —
2915
+ // where "exported" includes the two-statement form `const useStore
2916
+ // = create(…)` … `export default useStore` (see isExportedLater),
2917
+ // the shape most React Native stores are written in.
2669
2918
  const objectOfFns = valueNode && (valueNode.type === 'object' || valueNode.type === 'object_expression')
2670
2919
  ? valueNode
2671
2920
  : valueNode?.type === 'call_expression'
@@ -2677,7 +2926,7 @@ class TreeSitterExtractor {
2677
2926
  // whose functions are body-local consts — it must fall through to a
2678
2927
  // normal body walk (extracting those consts), not be skipped here.
2679
2928
  const hasInlineFns = !!objectOfFns && this.objectHasInlineFunctions(objectOfFns);
2680
- const extractObjectMethods = isExported && !!objectOfFns && hasInlineFns;
2929
+ const extractObjectMethods = (isExported || this.isExportedLater(name)) && !!objectOfFns && hasInlineFns;
2681
2930
  // RTK Query: `createApi`/`injectEndpoints` define endpoints as
2682
2931
  // object-literal properties whose values are `build.query/mutation(...)`
2683
2932
  // calls — nested under an `endpoints` arrow, so neither the
@@ -2704,19 +2953,25 @@ class TreeSitterExtractor {
2704
2953
  VUE_STORE_COLLECTION_NAMES.has(name) && this.looksLikeVueStoreFile()) {
2705
2954
  storeCollections.push(objectOfFns);
2706
2955
  }
2707
- // Visit the initializer body for calls — EXCEPT object literals (their
2708
- // function-valued properties are extracted below) and the store-factory
2709
- // / createApi / store-collection call whose nested objects we extract
2710
- // method-by-method below (walking the whole call would re-visit those
2711
- // method arrows and mis-attribute their inner calls to the file scope).
2712
- if (valueNode &&
2713
- valueNode.type !== 'object' &&
2714
- valueNode.type !== 'object_expression' &&
2715
- !(extractObjectMethods && valueNode.type === 'call_expression') &&
2716
- !rtkEndpoints &&
2717
- !piniaSetup &&
2718
- storeCollections.length === 0) {
2956
+ // Visit the initializer body for calls, ATTRIBUTED to the declared
2957
+ // symbol (#693) — EXCEPT the shapes whose members are extracted
2958
+ // one-by-one below (the store-factory / createApi / store-collection
2959
+ // objects), where walking the whole initializer would re-visit each
2960
+ // member arrow and double-count its calls.
2961
+ //
2962
+ // Two things were wrong here before. The walk ran with only the FILE
2963
+ // on the stack, so `const cfg = load()` recorded the FILE as load's
2964
+ // caller — the exact leak Go's #693 fixed. And an object literal was
2965
+ // skipped outright, so `const obj = { handler: () => target() }`
2966
+ // contributed nothing at all unless the const was exported (only then
2967
+ // does extractObjectLiteralFunctions mint the members).
2968
+ const membersExtractedSeparately = extractObjectMethods || !!rtkEndpoints || !!piniaSetup || storeCollections.length > 0;
2969
+ if (valueNode && !membersExtractedSeparately) {
2970
+ if (varNode)
2971
+ this.nodeStack.push(varNode.id);
2719
2972
  this.visitFunctionBody(valueNode, '');
2973
+ if (varNode)
2974
+ this.nodeStack.pop();
2720
2975
  }
2721
2976
  if (extractObjectMethods && objectOfFns) {
2722
2977
  this.extractObjectLiteralFunctions(objectOfFns);
@@ -2740,17 +2995,32 @@ class TreeSitterExtractor {
2740
2995
  const right = (0, tree_sitter_helpers_1.getChildByField)(node, 'right') || node.namedChild(1);
2741
2996
  // Ruby constant assignments (`MAX = 3`) have a `constant`-typed LHS, not
2742
2997
  // `identifier`; without this they were never extracted as symbols at all.
2998
+ let assigned = null;
2743
2999
  if (left && (left.type === 'identifier' || left.type === 'constant')) {
2744
3000
  const name = (0, tree_sitter_helpers_1.getNodeText)(left, this.source);
2745
3001
  // Skip if name starts with lowercase and looks like a function call result
2746
3002
  // Python constants are usually UPPER_CASE
2747
3003
  const initValue = right ? (0, tree_sitter_helpers_1.getNodeText)(right, this.source).slice(0, 100) : undefined;
2748
3004
  const initSignature = initValue ? `= ${initValue}${initValue.length >= 100 ? '...' : ''}` : undefined;
2749
- this.createNode(kind, name, node, {
3005
+ assigned = this.createNode(kind, name, node, {
2750
3006
  docstring,
2751
3007
  signature: initSignature,
2752
3008
  });
2753
3009
  }
3010
+ // Walk the initializer ATTRIBUTED to the assigned name (#693). A
3011
+ // module-level `app = FastAPI()` / `ENGINE = create_engine(url)` /
3012
+ // `handler = lambda: run()` dropped every call on the right-hand side, so
3013
+ // whatever the module builds at import time linked to nothing. A tuple
3014
+ // target (`a, b = f(), g()`) mints no symbol, so its RHS is walked at the
3015
+ // enclosing scope rather than lost. Python only: Ruby shares this branch
3016
+ // and gets its own turn.
3017
+ if (this.language === 'python' && right) {
3018
+ if (assigned)
3019
+ this.nodeStack.push(assigned.id);
3020
+ this.visitFunctionBody(right, '');
3021
+ if (assigned)
3022
+ this.nodeStack.pop();
3023
+ }
2754
3024
  }
2755
3025
  else if (this.language === 'go') {
2756
3026
  // Go: var_declaration, short_var_declaration, const_declaration
@@ -2816,15 +3086,26 @@ class TreeSitterExtractor {
2816
3086
  const varList = assign.namedChildren.find((c) => c.type === 'variable_list');
2817
3087
  const exprList = assign.namedChildren.find((c) => c.type === 'expression_list');
2818
3088
  const values = exprList ? exprList.namedChildren : [];
2819
- const names = varList ? varList.namedChildren.filter((c) => c.type === 'identifier') : [];
2820
- names.forEach((nameNode, i) => {
2821
- const name = (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source);
2822
- if (!name)
2823
- return;
3089
+ const targets = varList ? varList.namedChildren : [];
3090
+ targets.forEach((nameNode, i) => {
2824
3091
  const valueNode = values[i];
3092
+ const target = this.luaAssignmentTarget(nameNode);
3093
+ if (!target)
3094
+ return;
3095
+ if (valueNode?.type === 'function_definition') {
3096
+ this.extractLuaFunctionValue(valueNode, target.name, target.receiver, docstring);
3097
+ return;
3098
+ }
3099
+ if (valueNode?.type === 'table_constructor') {
3100
+ this.extractLuaTableFunctions(valueNode, target.fullName);
3101
+ }
3102
+ // A dotted assignment updates a table member; it is not a standalone
3103
+ // variable node. Function-valued members were handled above.
3104
+ if (target.receiver || node.type === 'assignment_statement')
3105
+ return;
2825
3106
  const initValue = valueNode ? (0, tree_sitter_helpers_1.getNodeText)(valueNode, this.source).slice(0, 100) : undefined;
2826
3107
  const initSignature = initValue ? `= ${initValue}${initValue.length >= 100 ? '...' : ''}` : undefined;
2827
- this.createNode(kind, name, nameNode, { docstring, signature: initSignature, isExported });
3108
+ this.createNode(kind, target.name, nameNode, { docstring, signature: initSignature, isExported });
2828
3109
  });
2829
3110
  }
2830
3111
  else if (this.language === 'c') {
@@ -2889,6 +3170,8 @@ class TreeSitterExtractor {
2889
3170
  else {
2890
3171
  // Generic fallback for other languages
2891
3172
  // Try to find identifier children
3173
+ const nameField = (0, tree_sitter_helpers_1.getChildByField)(node, 'name');
3174
+ let declared = null;
2892
3175
  for (let i = 0; i < node.namedChildCount; i++) {
2893
3176
  const child = node.namedChild(i);
2894
3177
  if (child?.type === 'identifier' || child?.type === 'variable_declarator') {
@@ -2896,13 +3179,103 @@ class TreeSitterExtractor {
2896
3179
  ? (0, tree_sitter_helpers_1.getNodeText)(child, this.source)
2897
3180
  : extractName(child, this.source, this.extractor);
2898
3181
  if (name && name !== '<anonymous>') {
2899
- this.createNode(kind, name, child, {
3182
+ const created = this.createNode(kind, name, child, {
2900
3183
  docstring,
2901
3184
  isExported,
2902
3185
  });
3186
+ if (created && nameField && child.startIndex === nameField.startIndex) {
3187
+ declared = created;
3188
+ }
2903
3189
  }
2904
3190
  }
2905
3191
  }
3192
+ // Walk the initializer ATTRIBUTED to the declared symbol (#693). Rust
3193
+ // only for now: `const N: usize = compute()` and
3194
+ // `static REGISTRY: Lazy<T> = Lazy::new(|| build())` dropped every call
3195
+ // inside the initializer, so a handler table or a lazily-built singleton
3196
+ // linked to nothing. The other languages sharing this fallback spell
3197
+ // their initializer differently and get their own turn.
3198
+ if (this.language === 'rust') {
3199
+ const valueNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'value');
3200
+ if (valueNode) {
3201
+ if (declared)
3202
+ this.nodeStack.push(declared.id);
3203
+ this.visitFunctionBody(valueNode, '');
3204
+ if (declared)
3205
+ this.nodeStack.pop();
3206
+ }
3207
+ }
3208
+ }
3209
+ }
3210
+ /** Resolve a Lua assignment target into its callable name and optional table receiver. */
3211
+ luaAssignmentTarget(node) {
3212
+ if (node.type === 'identifier') {
3213
+ const name = (0, tree_sitter_helpers_1.getNodeText)(node, this.source).trim();
3214
+ return name ? { name, fullName: name } : null;
3215
+ }
3216
+ if (node.type !== 'dot_index_expression' &&
3217
+ node.type !== 'method_index_expression' &&
3218
+ node.type !== 'bracket_index_expression')
3219
+ return null;
3220
+ const table = (0, tree_sitter_helpers_1.getChildByField)(node, 'table');
3221
+ const field = (0, tree_sitter_helpers_1.getChildByField)(node, 'field') ?? (0, tree_sitter_helpers_1.getChildByField)(node, 'method');
3222
+ if (!table || !field)
3223
+ return null;
3224
+ const receiver = (0, tree_sitter_helpers_1.getNodeText)(table, this.source).trim();
3225
+ const name = this.luaStaticFieldName(field, node.type === 'bracket_index_expression');
3226
+ if (!receiver || !name)
3227
+ return null;
3228
+ return { name, receiver, fullName: `${receiver}.${name}` };
3229
+ }
3230
+ /** A statically-known Lua field name; dynamic bracket keys are not callable identities. */
3231
+ luaStaticFieldName(node, bracketed) {
3232
+ if (node.type === 'identifier') {
3233
+ return bracketed ? '' : (0, tree_sitter_helpers_1.getNodeText)(node, this.source).trim();
3234
+ }
3235
+ if (node.type === 'string') {
3236
+ const content = node.namedChildren.find((child) => child.type === 'string_content');
3237
+ return content ? (0, tree_sitter_helpers_1.getNodeText)(content, this.source).trim() : '';
3238
+ }
3239
+ return '';
3240
+ }
3241
+ /** Extract an anonymous Lua function using the name supplied by its assignment target. */
3242
+ extractLuaFunctionValue(node, name, receiver, docstring) {
3243
+ if (!this.extractor)
3244
+ return;
3245
+ const signature = this.extractor.getSignature?.(node, this.source);
3246
+ const extra = { docstring, signature };
3247
+ if (receiver)
3248
+ extra.qualifiedName = this.composeReceiverQualifiedName(receiver, name);
3249
+ else
3250
+ extra.isExported = this.extractor.isExported?.(node, this.source);
3251
+ const functionNode = this.createNode(receiver ? 'method' : 'function', name, node, extra);
3252
+ if (!functionNode)
3253
+ return;
3254
+ this.nodeStack.push(functionNode.id);
3255
+ const body = (0, tree_sitter_helpers_1.getChildByField)(node, this.extractor.bodyField);
3256
+ if (body)
3257
+ this.visitFunctionBody(body, functionNode.id);
3258
+ this.nodeStack.pop();
3259
+ }
3260
+ /** Extract function-valued keyed fields from a Lua table, including nested tables. */
3261
+ extractLuaTableFunctions(table, receiver) {
3262
+ for (const field of table.namedChildren) {
3263
+ if (field.type !== 'field')
3264
+ continue;
3265
+ const nameNode = (0, tree_sitter_helpers_1.getChildByField)(field, 'name');
3266
+ const valueNode = (0, tree_sitter_helpers_1.getChildByField)(field, 'value');
3267
+ if (!nameNode || !valueNode)
3268
+ continue;
3269
+ const bracketed = (0, tree_sitter_helpers_1.getNodeText)(field, this.source).trimStart().startsWith('[');
3270
+ const name = this.luaStaticFieldName(nameNode, bracketed);
3271
+ if (!name)
3272
+ continue;
3273
+ if (valueNode.type === 'function_definition') {
3274
+ this.extractLuaFunctionValue(valueNode, name, receiver);
3275
+ }
3276
+ else if (valueNode.type === 'table_constructor') {
3277
+ this.extractLuaTableFunctions(valueNode, `${receiver}.${name}`);
3278
+ }
2906
3279
  }
2907
3280
  }
2908
3281
  /**
@@ -2917,23 +3290,23 @@ class TreeSitterExtractor {
2917
3290
  const name = extractName(node, this.source, this.extractor);
2918
3291
  if (name === '<anonymous>')
2919
3292
  return false;
2920
- const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(node, this.source);
3293
+ const docstring = this.docstringFor(node);
2921
3294
  const isExported = this.extractor.isExported?.(node, this.source);
2922
3295
  // Check if this type alias is actually a struct or interface definition
2923
3296
  // (e.g. Go: `type Foo struct { ... }` is a type_spec wrapping struct_type)
2924
3297
  const resolvedKind = this.extractor.resolveTypeAliasKind?.(node, this.source);
2925
- if (resolvedKind === 'struct') {
2926
- const structNode = this.createNode('struct', name, node, { docstring, isExported });
2927
- if (!structNode)
3298
+ if (resolvedKind === 'struct' || resolvedKind === 'union') {
3299
+ const aggregateNode = this.createNode(resolvedKind, name, node, { docstring, isExported });
3300
+ if (!aggregateNode)
2928
3301
  return true;
2929
3302
  // Visit body children for field extraction
2930
- this.nodeStack.push(structNode.id);
2931
- // Try Go-style 'type' field first, then find inner struct child (C typedef struct)
3303
+ this.nodeStack.push(aggregateNode.id);
3304
+ // Try Go-style 'type' field first, then find the matching inner aggregate child.
2932
3305
  const typeChild = (0, tree_sitter_helpers_1.getChildByField)(node, 'type')
2933
- || this.findChildByTypes(node, this.extractor.structTypes);
3306
+ || this.findChildByTypes(node, resolvedKind === 'union' ? (this.extractor.unionTypes ?? []) : this.extractor.structTypes);
2934
3307
  if (typeChild) {
2935
3308
  // Extract struct embedding (e.g. Go: `type DB struct { *Head; Queryable }`)
2936
- this.extractInheritance(typeChild, structNode.id);
3309
+ this.extractInheritance(typeChild, aggregateNode.id);
2937
3310
  const body = (0, tree_sitter_helpers_1.getChildByField)(typeChild, this.extractor.bodyField) || typeChild;
2938
3311
  for (let i = 0; i < body.namedChildCount; i++) {
2939
3312
  const child = body.namedChild(i);
@@ -3081,7 +3454,7 @@ class TreeSitterExtractor {
3081
3454
  const memberKind = child.type === 'method_signature'
3082
3455
  ? 'method'
3083
3456
  : this.isTsFunctionTypedProperty(child) ? 'method' : 'property';
3084
- const docstring = (0, tree_sitter_helpers_1.getPrecedingDocstring)(child, this.source);
3457
+ const docstring = this.docstringFor(child);
3085
3458
  const signature = (0, tree_sitter_helpers_1.getNodeText)(child, this.source);
3086
3459
  this.createNode(memberKind, memberName, child, {
3087
3460
  docstring,
@@ -3935,15 +4308,18 @@ class TreeSitterExtractor {
3935
4308
  }
3936
4309
  // Erlang: a local call is `call(expr: atom, args)`; a remote call nests it
3937
4310
  // under `remote(module: remote_module, fun: call)` — the module qualifier
3938
- // lives on the PARENT. Remote calls are emitted as `mod::fn`, which is
3939
- // byte-identical to the qualifiedName the module namespace gives every
3940
- // function (see packageTypes in languages/erlang.ts), so they resolve via
3941
- // matchByQualifiedName. A var/macro callee or module (`F(X)`, `?M(X)`,
3942
- // `Mod:handle(X)`) has no static target — except `?MODULE:fn(X)`, which the
3943
- // bare name + same-file preference resolves correctly. `fun name/1` /
3944
- // `fun mod:name/1` values are function REFERENCES (callback registration),
3945
- // and record construction/update/index/field-access are `references` to the
3946
- // record's struct node.
4311
+ // lives on the PARENT. Arity is part of a function's identity (#1610), so
4312
+ // refs carry the call-site arity: remote calls are emitted as `mod::fn/2`,
4313
+ // byte-identical to the qualifiedName the module namespace + arity suffix
4314
+ // gives every function (see languages/erlang.ts), so they resolve via
4315
+ // matchByQualifiedName; local calls are emitted `fn/2` and resolved by the
4316
+ // erlang arity step in matchReference (same-file first). A var/macro callee
4317
+ // or module (`F(X)`, `?M(X)`, `Mod:handle(X)`) has no static target —
4318
+ // except `?MODULE:fn(X)`, which the bare-name-with-arity + same-file
4319
+ // preference resolves correctly. `fun name/1` / `fun mod:name/1` values
4320
+ // are function REFERENCES (callback registration) carrying their own
4321
+ // written arity, and record construction/update/index/field-access are
4322
+ // `references` to the record's struct node.
3947
4323
  if (this.language === 'erlang') {
3948
4324
  const line = node.startPosition.row + 1;
3949
4325
  const column = node.startPosition.column;
@@ -3977,9 +4353,13 @@ class TreeSitterExtractor {
3977
4353
  if (!macroName || (0, tree_sitter_helpers_1.getNodeText)(macroName, this.source) !== 'MODULE')
3978
4354
  return;
3979
4355
  }
4356
+ // Arity from the call site's own argument list — part of the callee's
4357
+ // identity, and what disambiguates `f/1` from `f/2` (#1610).
4358
+ const callArgsNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'args');
4359
+ const callArity = callArgsNode ? callArgsNode.namedChildCount : 0;
3980
4360
  this.unresolvedReferences.push({
3981
4361
  fromNodeId: callerId,
3982
- referenceName: calleeName,
4362
+ referenceName: `${calleeName}/${callArity}`,
3983
4363
  referenceKind: 'calls',
3984
4364
  line,
3985
4365
  column,
@@ -4000,9 +4380,10 @@ class TreeSitterExtractor {
4000
4380
  const target = argsNode?.namedChild(0) ?? null;
4001
4381
  const targetModule = target ? this.resolveErlangGenServerTarget(target) : null;
4002
4382
  if (targetModule) {
4383
+ // OTP fixes the handler arities: handle_call/3, handle_cast/2.
4003
4384
  this.unresolvedReferences.push({
4004
4385
  fromNodeId: callerId,
4005
- referenceName: `${targetModule}::${fnBare === 'cast' ? 'handle_cast' : 'handle_call'}`,
4386
+ referenceName: `${targetModule}::${fnBare === 'cast' ? 'handle_cast/2' : 'handle_call/3'}`,
4006
4387
  referenceKind: 'calls',
4007
4388
  line,
4008
4389
  column,
@@ -4034,9 +4415,17 @@ class TreeSitterExtractor {
4034
4415
  (0, tree_sitter_helpers_1.getNodeText)((0, tree_sitter_helpers_1.getChildByField)(m, 'name'), this.source) === 'MODULE';
4035
4416
  if (m.type !== 'atom' && !isLocalModule)
4036
4417
  continue;
4418
+ // Arity of the spawned/applied function = the length of the
4419
+ // static args-list literal directly after the (M, F) pair, when
4420
+ // present (`spawn_link(?MODULE, request_process, [Req, Env])` →
4421
+ // /2). A var/absent list leaves the ref arity-less; the
4422
+ // qualified matcher then resolves it only when the module
4423
+ // defines exactly one arity of that name.
4424
+ const mfaList = argExprs[i + 2];
4425
+ const arityTail = mfaList?.type === 'list' ? `/${mfaList.namedChildCount}` : '';
4037
4426
  this.unresolvedReferences.push({
4038
4427
  fromNodeId: callerId,
4039
- referenceName: isLocalModule ? erlAtom(f) : `${erlAtom(m)}::${erlAtom(f)}`,
4428
+ referenceName: (isLocalModule ? erlAtom(f) : `${erlAtom(m)}::${erlAtom(f)}`) + arityTail,
4040
4429
  referenceKind: 'calls',
4041
4430
  line: f.startPosition.row + 1,
4042
4431
  column: f.startPosition.column,
@@ -4059,6 +4448,12 @@ class TreeSitterExtractor {
4059
4448
  return;
4060
4449
  refName = `${erlAtom(moduleAtom)}::${refName}`;
4061
4450
  }
4451
+ // `fun f/1` writes its arity — carry it so the ref lands on the
4452
+ // matching arity's node (#1610).
4453
+ const funArityNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'arity');
4454
+ const funArityValue = funArityNode ? (0, tree_sitter_helpers_1.getChildByField)(funArityNode, 'value') : null;
4455
+ if (funArityValue)
4456
+ refName = `${refName}/${(0, tree_sitter_helpers_1.getNodeText)(funArityValue, this.source)}`;
4062
4457
  this.unresolvedReferences.push({
4063
4458
  fromNodeId: callerId,
4064
4459
  referenceName: refName,
@@ -4603,10 +4998,17 @@ class TreeSitterExtractor {
4603
4998
  // This helps the resolver distinguish method calls from bare function calls
4604
4999
  // (e.g., Python's console.print() vs builtin print())
4605
5000
  // Skip self/this/cls as they don't aid resolution
4606
- const receiver = (0, tree_sitter_helpers_1.getChildByField)(func, 'object') ||
5001
+ const rawReceiver = (0, tree_sitter_helpers_1.getChildByField)(func, 'object') ||
4607
5002
  (0, tree_sitter_helpers_1.getChildByField)(func, 'operand') ||
4608
5003
  (0, tree_sitter_helpers_1.getChildByField)(func, 'argument') ||
4609
5004
  func.namedChild(0);
5005
+ // TS/JS: look through wrappers that keep the receiver the same
5006
+ // object (`(x).m()`, `x!.m()`, `(x as T).m()`, `(await f()).m()`),
5007
+ // so the branches below see the identifier / call / chain the
5008
+ // call is really made on.
5009
+ const receiver = rawReceiver && TS_JS_CHAIN_LANGUAGES.has(this.language)
5010
+ ? peelTsJsReceiver(rawReceiver)
5011
+ : rawReceiver;
4610
5012
  // A LITERAL receiver — `", ".join(...)`, `"x".toUpperCase()`,
4611
5013
  // `5.times`, `[].concat(...)` — calls a builtin of the literal's
4612
5014
  // type, never a project symbol. The bare-name fallback below let
@@ -4627,6 +5029,37 @@ class TreeSitterExtractor {
4627
5029
  calleeName = methodName;
4628
5030
  }
4629
5031
  }
5032
+ else if (this.language === 'rust' && receiver && receiver.type === 'self') {
5033
+ // Rust `self.method()`. Keep the `self.` prefix, exactly as the
5034
+ // field shape below does (#1585): the resolver reads the owner
5035
+ // off the CALLING method's qualified name and resolves the
5036
+ // method on that type. Collapsing to the bare method name handed
5037
+ // the resolver a name with no owner, which it then matched among
5038
+ // all same-named methods by file proximity — so `self.reset()`
5039
+ // inside `impl Target` landed on a `Decoy::reset` that happened
5040
+ // to sit nearer, with nothing in the edge to show it was a guess
5041
+ // (#1861). Mirrored in the kernel's extract_call (rustlang.rs).
5042
+ calleeName = `self.${methodName}`;
5043
+ }
5044
+ else if (this.language === 'rust' &&
5045
+ receiver &&
5046
+ receiver.type === 'field_expression' &&
5047
+ (0, tree_sitter_helpers_1.getChildByField)(receiver, 'value')?.type === 'self' &&
5048
+ (0, tree_sitter_helpers_1.getChildByField)(receiver, 'field')?.type === 'field_identifier') {
5049
+ // Rust `self.<field>.<method>()` — a call through a field of the
5050
+ // enclosing type (#1585). Keep the `self.` prefix: the resolver
5051
+ // recognizes the shape, reads the field's declared type off the
5052
+ // owner struct's declaration, and resolves the method on THAT
5053
+ // type — or leaves the ref unresolved when the type is external
5054
+ // or unknown. Previously this collapsed to the bare method name,
5055
+ // which exact-matched whichever same-named method was nearest —
5056
+ // often the calling method itself, a self-edge not in the source.
5057
+ // Deeper chains (`self.a.b.m()`), `self.f().m()` and parenthesized
5058
+ // receivers keep the bare name. Mirrored in the kernel's
5059
+ // extract_call (rustlang.rs).
5060
+ const fieldName = (0, tree_sitter_helpers_1.getNodeText)((0, tree_sitter_helpers_1.getChildByField)(receiver, 'field'), this.source);
5061
+ calleeName = `self.${fieldName}.${methodName}`;
5062
+ }
4630
5063
  else if ((this.language === 'cpp' ||
4631
5064
  this.language === 'c' ||
4632
5065
  this.language === 'kotlin' ||
@@ -4702,6 +5135,56 @@ class TreeSitterExtractor {
4702
5135
  // name, which either failed to resolve or resolved ambiguously.
4703
5136
  calleeName = `${(0, tree_sitter_helpers_1.getNodeText)(receiver, this.source)}.${methodName}`;
4704
5137
  }
5138
+ else if ((this.language === 'typescript' ||
5139
+ this.language === 'javascript' ||
5140
+ this.language === 'tsx' ||
5141
+ this.language === 'jsx') &&
5142
+ receiver &&
5143
+ receiver.type === 'member_expression' &&
5144
+ (0, tree_sitter_helpers_1.getChildByField)(receiver, 'object')?.type === 'this' &&
5145
+ THIS_FIELD_PROPERTY_TYPES.has((0, tree_sitter_helpers_1.getChildByField)(receiver, 'property')?.type ?? '')) {
5146
+ // TS/JS call through a field of the enclosing class —
5147
+ // `this.mailer.send()` (#1496). Keep the `this.<field>` prefix:
5148
+ // the resolver reads the field's declared type off the class's
5149
+ // own declaration (`private mailer: Mailer`, `mailer = new
5150
+ // Mailer()`) and resolves the method on THAT type — or leaves the
5151
+ // ref unresolved when the type is external or unknown. Previously
5152
+ // this collapsed to the bare method name, which exact-matched
5153
+ // whichever same-named method was nearest — the calling method
5154
+ // itself when the two share a name, a self-edge not in the
5155
+ // source. Same discipline as Rust's `self.<field>` (#1585).
5156
+ // Mirrored in the kernel's extract_call (tsjs/extractors.rs).
5157
+ // An ES private field (`this.#items.add()`) is the same shape;
5158
+ // its name keeps the `#` (#1987).
5159
+ const fieldName = (0, tree_sitter_helpers_1.getNodeText)((0, tree_sitter_helpers_1.getChildByField)(receiver, 'property'), this.source);
5160
+ calleeName = `this.${fieldName}.${methodName}`;
5161
+ }
5162
+ else if ((this.language === 'typescript' ||
5163
+ this.language === 'javascript' ||
5164
+ this.language === 'tsx' ||
5165
+ this.language === 'jsx' ||
5166
+ this.language === 'python') &&
5167
+ receiver &&
5168
+ (receiver.type === 'call_expression' || receiver.type === 'call')) {
5169
+ // Receiver that is itself a call — `d.setdefault(k, []).append(v)`,
5170
+ // `make().run()`, `res.json().data` (#1683). The bare method name
5171
+ // this used to emit exact-matched any top-level project symbol of
5172
+ // that name and fabricated a call edge from an unrelated function
5173
+ // (`append`, `get`, `run`…). Keep the inner callee, encoded as
5174
+ // `<inner>().<method>` like the Java/Kotlin/C++ chains: the
5175
+ // marker never appears in an ordinary ref, so nothing name-matches
5176
+ // it, and a chain resolver can later infer the receiver's type
5177
+ // from what the inner call returns. An inner callee that is not a
5178
+ // plain name or member chain (`(await x)()`, `arr[0]()`) has no
5179
+ // static receiver at all — emit nothing: a silent miss, never a
5180
+ // wrong edge. The inner call is visited on its own either way.
5181
+ // Mirrored in the kernel (tsjs/extractors.rs, python.rs).
5182
+ const innerFn = (0, tree_sitter_helpers_1.getChildByField)(receiver, 'function');
5183
+ const innerCallee = innerFn ? (0, tree_sitter_helpers_1.getNodeText)(innerFn, this.source).replace(/\s+/g, '') : '';
5184
+ if (!/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/.test(innerCallee))
5185
+ return;
5186
+ calleeName = `${innerCallee}().${methodName}`;
5187
+ }
4705
5188
  else if (this.language === 'go' &&
4706
5189
  receiver &&
4707
5190
  receiver.type === 'selector_expression' &&
@@ -4716,6 +5199,30 @@ class TreeSitterExtractor {
4716
5199
  // inference (see matchGoFieldChainCall) or stay unresolved.
4717
5200
  calleeName = `${(0, tree_sitter_helpers_1.getNodeText)(receiver, this.source).replace(/\s+/g, '')}.${methodName}`;
4718
5201
  }
5202
+ else if (TS_JS_CHAIN_LANGUAGES.has(this.language) &&
5203
+ receiver &&
5204
+ TS_JS_CHAIN_RECEIVER_TYPES.has(receiver.type) &&
5205
+ isUnresolvedTsJsChain(receiver, this.source)) {
5206
+ // Keep the source call for effect reporting, but never collapse
5207
+ // it to a guessed method. The resolver only lets frameworks
5208
+ // with receiver evidence handle these qualified chains.
5209
+ const chain = (0, tree_sitter_helpers_1.getNodeText)(func, this.source).replace(/\s+/g, '').replace(/\?\./g, '.');
5210
+ if (!/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*){2,}$/.test(chain))
5211
+ return;
5212
+ calleeName = chain;
5213
+ }
5214
+ else if (TS_JS_CHAIN_LANGUAGES.has(this.language) &&
5215
+ receiver &&
5216
+ !keepsBareTsJsReceiver(receiver, this.source)) {
5217
+ // Any other TS/JS receiver is an expression with no static
5218
+ // type here — `(a ?? b).map()`, `f().list.map()`,
5219
+ // `arr[0].run()`, `(() => {}).call()`. The bare method name this
5220
+ // used to emit exact-matched whichever project method shared the
5221
+ // name (`(await list()).map()` onto an adapter class's `map`).
5222
+ // Emit nothing: a silent miss, never a wrong edge. Mirrored in
5223
+ // the kernel's extract_call (tsjs/extractors.rs).
5224
+ return;
5225
+ }
4719
5226
  else {
4720
5227
  calleeName = methodName;
4721
5228
  }
@@ -4919,43 +5426,103 @@ class TreeSitterExtractor {
4919
5426
  }
4920
5427
  }
4921
5428
  /**
4922
- * Is this C++ `declaration` a stack/direct-initialization object construction
4923
- * that invokes a constructor — `Calculator calc(0)` (direct-init) or
4924
- * `Widget w{1, 2}` (brace-init) — as opposed to a plain variable or a
4925
- * function declaration? Used to emit an `instantiates` edge for the
5429
+ * C++ stack construction — `Calculator calc(0)` / `Widget w{1, 2}` — is
4926
5430
  * call-less construction syntax (#1035); heap `new T(...)` is handled
4927
5431
  * separately by INSTANTIATION_KINDS.
4928
5432
  *
4929
- * Two signals, both required:
4930
- * - the `type` field is a class-like NAMED type (`type_identifier`,
4931
- * `template_type`, or `qualified_identifier`). Primitives (`int x(0)`),
4932
- * `auto` (`placeholder_type_specifier` — that form always carries a real
4933
- * `call_expression`, already handled), and sized specifiers are excluded —
4934
- * they construct no class; and
4935
- * - a declarator carries constructor arguments: an `init_declarator` whose
4936
- * `value` is an `argument_list` (`(args)`) or `initializer_list` (`{args}`).
4937
- * This skips default construction `Calculator c;` (no value) and the
4938
- * most-vexing-parse `Calculator c();` (a bodyless `function_declarator`,
4939
- * a function decl — not a construction).
5433
+ * The `type` field must be a class-like NAMED type (`type_identifier`,
5434
+ * `template_type`, or `qualified_identifier`). Primitives (`int x(0)`),
5435
+ * `auto` (`placeholder_type_specifier` — that form always carries a real
5436
+ * `call_expression`, already handled), and sized specifiers are excluded —
5437
+ * they construct no class. `extern T x;` declares, it constructs nothing.
5438
+ *
5439
+ * Per declarator:
5440
+ * - a bare `identifier` is default construction (`T item;`) — arity 0;
5441
+ * - an `init_declarator` whose `value` is an `argument_list` (`(args)`) or
5442
+ * `initializer_list` (`{args}`) carries constructor arguments — arity is
5443
+ * the argument count. An array declarator's braces hold ELEMENTS, not
5444
+ * constructor arguments, so each braced element has its own arity;
5445
+ * - pointer / reference / function declarators construct nothing:
5446
+ * `T* p{}` is a null pointer, `T& r{x}` binds a reference, and the
5447
+ * most-vexing-parse `T c();` is a function declaration.
5448
+ *
5449
+ * `instantiates` (the type) is retained for initialized declarations,
5450
+ * preserving the #1035 dependency edges. `calls` (`T::T/arity`, resolved to
5451
+ * the constructor by src/resolution/cpp-constructor.ts, #1839) is emitted
5452
+ * for every declarator with an arity, default construction included.
5453
+ * Mirrored in the kernel (ccpp/mod.rs cpp_stack_constructions).
4940
5454
  */
4941
- isCppStackConstruction(node) {
5455
+ cppStackConstructions(node) {
5456
+ const none = { instantiates: false, arities: [] };
4942
5457
  const typeNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'type');
4943
5458
  if (!typeNode ||
4944
5459
  (typeNode.type !== 'type_identifier' &&
4945
5460
  typeNode.type !== 'template_type' &&
4946
5461
  typeNode.type !== 'qualified_identifier')) {
4947
- return false;
5462
+ return none;
4948
5463
  }
5464
+ let instantiates = false;
5465
+ const arities = [];
4949
5466
  for (let i = 0; i < node.namedChildCount; i++) {
4950
5467
  const child = node.namedChild(i);
4951
- if (child?.type !== 'init_declarator')
5468
+ if (!child)
5469
+ continue;
5470
+ if (child.type === 'storage_class_specifier' && (0, tree_sitter_helpers_1.getNodeText)(child, this.source) === 'extern')
5471
+ return none;
5472
+ if (child.type === 'identifier' || child.type === 'array_declarator') {
5473
+ if (child.type === 'array_declarator' && !this.cppObjectArray(child))
5474
+ continue;
5475
+ arities.push(0);
5476
+ continue;
5477
+ }
5478
+ if (child.type !== 'init_declarator')
5479
+ continue;
5480
+ const declarator = (0, tree_sitter_helpers_1.getChildByField)(child, 'declarator');
5481
+ if (declarator?.type !== 'identifier' && declarator?.type !== 'array_declarator')
4952
5482
  continue;
4953
5483
  const value = (0, tree_sitter_helpers_1.getChildByField)(child, 'value');
4954
- if (value && (value.type === 'argument_list' || value.type === 'initializer_list')) {
4955
- return true;
5484
+ if (!value || (value.type !== 'argument_list' && value.type !== 'initializer_list'))
5485
+ continue;
5486
+ instantiates = true;
5487
+ if (declarator.type === 'identifier') {
5488
+ arities.push(value.namedChildren.filter((c) => c.type !== 'comment').length);
5489
+ }
5490
+ else if (this.cppObjectArray(declarator)) {
5491
+ const dimensions = [];
5492
+ let array = declarator;
5493
+ while (array?.type === 'array_declarator') {
5494
+ const size = (0, tree_sitter_helpers_1.getChildByField)(array, 'size')?.text ?? '';
5495
+ dimensions.unshift(/^\d+$/.test(size) && Number.isSafeInteger(Number(size)) ? Number(size) : NaN);
5496
+ array = (0, tree_sitter_helpers_1.getChildByField)(array, 'declarator');
5497
+ }
5498
+ const elements = (list, depth) => {
5499
+ const entries = list.namedChildren.filter((c) => c.type !== 'comment');
5500
+ let elided = false;
5501
+ for (const entry of entries) {
5502
+ if (depth + 1 < dimensions.length) {
5503
+ if (entry.type === 'initializer_list')
5504
+ elements(entry, depth + 1);
5505
+ else
5506
+ elided = true; // Unbraced multidimensional layout needs type information.
5507
+ }
5508
+ else {
5509
+ arities.push(entry.type === 'initializer_list'
5510
+ ? entry.namedChildren.filter((c) => c.type !== 'comment').length : 1);
5511
+ }
5512
+ }
5513
+ if (!elided && (entries.length === 0 || dimensions[depth] > entries.length))
5514
+ arities.push(0);
5515
+ };
5516
+ elements(value, 0);
4956
5517
  }
4957
5518
  }
4958
- return false;
5519
+ return { instantiates, arities };
5520
+ }
5521
+ cppObjectArray(node) {
5522
+ let element = (0, tree_sitter_helpers_1.getChildByField)(node, 'declarator');
5523
+ while (element?.type === 'array_declarator')
5524
+ element = (0, tree_sitter_helpers_1.getChildByField)(element, 'declarator');
5525
+ return element?.type === 'identifier';
4959
5526
  }
4960
5527
  /**
4961
5528
  * Static-member / value-read pass. A type/enum/class used only via a member
@@ -5123,6 +5690,24 @@ class TreeSitterExtractor {
5123
5690
  if (extentRow === null || row < extentRow)
5124
5691
  extentRow = row;
5125
5692
  };
5693
+ // Rust outer attributes are siblings, not children of the function.
5694
+ // Preserve Tauri's runtime registration for the dead-code decorator rule.
5695
+ if (this.language === 'rust' && declNode.type === 'function_item') {
5696
+ for (let sibling = declNode.previousNamedSibling; sibling; sibling = sibling.previousNamedSibling) {
5697
+ if (sibling.type === 'line_comment' || sibling.type === 'block_comment')
5698
+ continue;
5699
+ if (sibling.type !== 'attribute_item')
5700
+ break;
5701
+ const attribute = sibling.namedChild(0);
5702
+ const name = attribute?.namedChild(0)?.text.replace(/\s/g, '');
5703
+ if (name === 'tauri::command') {
5704
+ const decorated = this.nodes.find(n => n.id === decoratedId);
5705
+ if (decorated)
5706
+ decorated.decorators = ['tauri::command'];
5707
+ break;
5708
+ }
5709
+ }
5710
+ }
5126
5711
  const consider = (n) => {
5127
5712
  if (!n)
5128
5713
  return;
@@ -5375,11 +5960,166 @@ class TreeSitterExtractor {
5375
5960
  }
5376
5961
  targets.add(target);
5377
5962
  }
5963
+ /**
5964
+ * Whether an anonymous function is the whole value of a `variable_declarator`
5965
+ * with a plain identifier name — `const NAME = () => {…}` / `= function () {…}`.
5966
+ * JS-family only.
5967
+ */
5968
+ declaratorBoundFunction(node) {
5969
+ if (this.language !== 'typescript' &&
5970
+ this.language !== 'javascript' &&
5971
+ this.language !== 'tsx' &&
5972
+ this.language !== 'jsx') {
5973
+ return false;
5974
+ }
5975
+ if (node.type !== 'arrow_function' && node.type !== 'function_expression')
5976
+ return false;
5977
+ const declarator = node.parent;
5978
+ if (!declarator || declarator.type !== 'variable_declarator')
5979
+ return false;
5980
+ const value = (0, tree_sitter_helpers_1.getChildByField)(declarator, 'value');
5981
+ if (!value || value.startIndex !== node.startIndex || value.endIndex !== node.endIndex)
5982
+ return false;
5983
+ return (0, tree_sitter_helpers_1.getChildByField)(declarator, 'name')?.type === 'identifier';
5984
+ }
5985
+ /**
5986
+ * The property a CommonJS export assignment binds a function to —
5987
+ * `exports.NAME = <node>` or `module.exports.NAME = <node>` — or null for
5988
+ * any other assignment. JS-family only; the node must be the assignment's
5989
+ * whole right-hand side.
5990
+ */
5991
+ commonJsExportName(assignment, value) {
5992
+ if (this.language !== 'typescript' &&
5993
+ this.language !== 'javascript' &&
5994
+ this.language !== 'tsx' &&
5995
+ this.language !== 'jsx') {
5996
+ return null;
5997
+ }
5998
+ const right = (0, tree_sitter_helpers_1.getChildByField)(assignment, 'right');
5999
+ if (!right || right.startIndex !== value.startIndex || right.endIndex !== value.endIndex)
6000
+ return null;
6001
+ const left = (0, tree_sitter_helpers_1.getChildByField)(assignment, 'left');
6002
+ if (!left || left.type !== 'member_expression')
6003
+ return null;
6004
+ const object = (0, tree_sitter_helpers_1.getChildByField)(left, 'object');
6005
+ const property = (0, tree_sitter_helpers_1.getChildByField)(left, 'property');
6006
+ if (!object || !property || property.type !== 'property_identifier')
6007
+ return null;
6008
+ const objectText = (0, tree_sitter_helpers_1.getNodeText)(object, this.source);
6009
+ if (objectText !== 'exports' && objectText !== 'module.exports')
6010
+ return null;
6011
+ return (0, tree_sitter_helpers_1.getNodeText)(property, this.source);
6012
+ }
6013
+ /**
6014
+ * The declarator name a React handler hook binds an anonymous function to —
6015
+ * `const NAME = useCallback(<node>, [...])` — or null for any other shape.
6016
+ * JS-family only; the node must be the hook call's FIRST argument, and the
6017
+ * call's value must be bound directly by a `variable_declarator`.
6018
+ */
6019
+ reactHookBoundName(node) {
6020
+ if (this.language !== 'typescript' &&
6021
+ this.language !== 'javascript' &&
6022
+ this.language !== 'tsx' &&
6023
+ this.language !== 'jsx') {
6024
+ return null;
6025
+ }
6026
+ if (node.type !== 'arrow_function' && node.type !== 'function_expression')
6027
+ return null;
6028
+ const args = node.parent;
6029
+ if (!args || args.type !== 'arguments')
6030
+ return null;
6031
+ const first = args.namedChild(0);
6032
+ if (!first || first.startIndex !== node.startIndex || first.endIndex !== node.endIndex)
6033
+ return null;
6034
+ const call = args.parent;
6035
+ if (!call || call.type !== 'call_expression')
6036
+ return null;
6037
+ const callee = (0, tree_sitter_helpers_1.getChildByField)(call, 'function');
6038
+ if (!callee || !REACT_HANDLER_HOOKS.test((0, tree_sitter_helpers_1.getNodeText)(callee, this.source)))
6039
+ return null;
6040
+ const declarator = call.parent;
6041
+ if (!declarator || declarator.type !== 'variable_declarator')
6042
+ return null;
6043
+ const nameNode = (0, tree_sitter_helpers_1.getChildByField)(declarator, 'name');
6044
+ return nameNode?.type === 'identifier' ? (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source) : null;
6045
+ }
6046
+ /**
6047
+ * The declarator name for an anonymous function passed to a CURRIED wrapper
6048
+ * call — `const NAME = factory(...)(function () {…})` — or the property key
6049
+ * when the call is an object member, `{ NAME: factory(...)(fn) }`; else null.
6050
+ *
6051
+ * `reactHookBoundName` above already names a function through the declarator
6052
+ * that binds it; the shape is general, but that method is deliberately
6053
+ * bounded to the three React handler hooks. This is the same shape with a
6054
+ * different, equally decidable bound: the callee is itself a call, i.e. a
6055
+ * factory that returns the wrapper (#1747).
6056
+ *
6057
+ * Requiring the callee to be a call is what keeps this narrow. It admits
6058
+ * `Effect.fn("Session.run")(function* () {…})`, `connect(mapState)(fn)` and
6059
+ * a project's own `wrap("name")(fn)`, and it does not admit the one-call
6060
+ * forms where the argument is a computation rather than a body worth a node
6061
+ * of its own — `useMemo(() => 1 + 1, [])`, `arr.map(() => …)` — which stay
6062
+ * anonymous exactly as before.
6063
+ *
6064
+ * Generators are included here and not in `reactHookBoundName`: a React
6065
+ * handler is never a generator, while `function*` is the common form in the
6066
+ * ecosystem this shape comes from.
6067
+ */
6068
+ curriedWrapperBoundName(node) {
6069
+ if (this.language !== 'typescript' &&
6070
+ this.language !== 'javascript' &&
6071
+ this.language !== 'tsx' &&
6072
+ this.language !== 'jsx') {
6073
+ return null;
6074
+ }
6075
+ if (node.type !== 'arrow_function' &&
6076
+ node.type !== 'function_expression' &&
6077
+ node.type !== 'generator_function') {
6078
+ return null;
6079
+ }
6080
+ const args = node.parent;
6081
+ if (!args || args.type !== 'arguments')
6082
+ return null;
6083
+ const first = args.namedChild(0);
6084
+ if (!first || first.startIndex !== node.startIndex || first.endIndex !== node.endIndex)
6085
+ return null;
6086
+ const call = args.parent;
6087
+ if (!call || call.type !== 'call_expression')
6088
+ return null;
6089
+ // The bound that replaces the hook allowlist: the thing being called is
6090
+ // itself a call, so this is the second application of a curried wrapper.
6091
+ const callee = (0, tree_sitter_helpers_1.getChildByField)(call, 'function');
6092
+ if (!callee || callee.type !== 'call_expression')
6093
+ return null;
6094
+ const binder = call.parent;
6095
+ if (!binder)
6096
+ return null;
6097
+ // `{ getMode: Effect.fn("…")(function* () {…}) }` — a service is often an
6098
+ // object a factory returns, so the wrapper's result lands in a `pair`. The
6099
+ // property key names it, as extractObjectLiteralFunctions names
6100
+ // `key: () => {}`.
6101
+ if (binder.type === 'pair') {
6102
+ const key = (0, tree_sitter_helpers_1.getChildByField)(binder, 'key');
6103
+ const value = (0, tree_sitter_helpers_1.getChildByField)(binder, 'value');
6104
+ if (!key || !value || value.startIndex !== call.startIndex || value.endIndex !== call.endIndex)
6105
+ return null;
6106
+ return this.objectKeyName(key);
6107
+ }
6108
+ if (binder.type !== 'variable_declarator')
6109
+ return null;
6110
+ const nameNode = (0, tree_sitter_helpers_1.getChildByField)(binder, 'name');
6111
+ return nameNode?.type === 'identifier' ? (0, tree_sitter_helpers_1.getNodeText)(nameNode, this.source) : null;
6112
+ }
5378
6113
  visitFunctionBody(body, _functionId) {
5379
6114
  if (!this.extractor)
5380
6115
  return;
5381
6116
  const visitForCallsAndStructure = (node) => {
5382
6117
  const nodeType = node.type;
6118
+ // A function-like macro defined inside a body is still a macro (#1838).
6119
+ if ((this.language === 'c' || this.language === 'cpp') && nodeType === 'preproc_function_def') {
6120
+ this.visitNode(node);
6121
+ return;
6122
+ }
5383
6123
  // Function-as-value capture (#756) — function bodies are walked here,
5384
6124
  // not in visitNode, so the capture hook must fire in both walkers.
5385
6125
  this.maybeCaptureFnRefs(node, nodeType);
@@ -5429,8 +6169,30 @@ class TreeSitterExtractor {
5429
6169
  // (which strips template args / namespace and emits the `instantiates`
5430
6170
  // ref). Children still recurse below, so a nested ctor-arg call
5431
6171
  // (`Calculator calc(make())`) keeps its own `calls` ref.
5432
- if (nodeType === 'declaration' && this.language === 'cpp' && this.isCppStackConstruction(node)) {
5433
- this.extractInstantiation(node);
6172
+ if (nodeType === 'declaration' && this.language === 'cpp') {
6173
+ const { instantiates, arities } = this.cppStackConstructions(node);
6174
+ if (instantiates)
6175
+ this.extractInstantiation(node);
6176
+ // One `calls` ref per constructed object, naming the constructor and
6177
+ // its argument count (`ns::T::T/1`) so the resolver can pick the
6178
+ // overload — a type is not a callee (#1839).
6179
+ const callerId = this.nodeStack[this.nodeStack.length - 1];
6180
+ const typeNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'type');
6181
+ if (callerId && typeNode && arities.length) {
6182
+ const className = (0, c_cpp_1.stripCppTemplateArgs)((0, tree_sitter_helpers_1.getNodeText)(typeNode, this.source));
6183
+ const name = className.split('::').filter(Boolean).pop();
6184
+ if (name) {
6185
+ for (const arity of arities) {
6186
+ this.unresolvedReferences.push({
6187
+ fromNodeId: callerId,
6188
+ referenceName: `${className}::${name}/${arity}`,
6189
+ referenceKind: 'calls',
6190
+ line: node.startPosition.row + 1,
6191
+ column: node.startPosition.column,
6192
+ });
6193
+ }
6194
+ }
6195
+ }
5434
6196
  }
5435
6197
  // C++ local function-pointer bindings (see cppLocalFnPtrs): record
5436
6198
  // `auto kernel = &fn<…>;` declarations and `kernel = &other_fn<…>;`
@@ -5488,6 +6250,43 @@ class TreeSitterExtractor {
5488
6250
  this.extractFunction(node);
5489
6251
  return;
5490
6252
  }
6253
+ // `const handleSubmit = useCallback(() => {…}, [deps])` — React's
6254
+ // memoised handler. The function is anonymous only syntactically: the
6255
+ // arrow is the first argument of a call whose result the declarator
6256
+ // binds, and that binding is the name every `onPress={handleSubmit}`
6257
+ // and `addListener('x', handleSubmit)` uses. Without a node of its own
6258
+ // the handler's calls attribute to the component and the JSX prop or
6259
+ // event registration has nothing to resolve to — the trigger of a flow
6260
+ // is invisible. Bounded to the hooks React documents for handlers
6261
+ // (`useCallback`, `useEffectEvent`, the experimental `useEvent`):
6262
+ // `useMemo` / `useEffect` callbacks are computations, not handlers.
6263
+ const hookBound = this.reactHookBoundName(node);
6264
+ if (hookBound) {
6265
+ this.extractFunction(node, hookBound);
6266
+ return;
6267
+ }
6268
+ // `const run = Effect.fn("Session.run")(function* () {…})` (#1747) —
6269
+ // the same declarator binding through a curried wrapper. Without a node
6270
+ // the body's calls attribute to the enclosing container, so the file or
6271
+ // the outer function picks up an outgoing edge that belongs to this
6272
+ // function and the callee's caller list names the wrong thing.
6273
+ const wrapperBound = this.curriedWrapperBoundName(node);
6274
+ if (wrapperBound) {
6275
+ this.extractFunction(node, wrapperBound);
6276
+ return;
6277
+ }
6278
+ // `const handleClear = () => {…}` inside a body (#1669) — the same
6279
+ // binding that names a function at module scope names one here, and in
6280
+ // a React component it is how every handler that skips `useCallback`
6281
+ // is written. Without a node the handler is absent from callers /
6282
+ // impact ("Symbol not found" reads like "no callers") and its calls
6283
+ // attribute to the component. extractFunction resolves the name from
6284
+ // the declarator; a destructuring or otherwise unnamed binding stays
6285
+ // anonymous and falls through.
6286
+ if (this.declaratorBoundFunction(node)) {
6287
+ this.extractFunction(node);
6288
+ return;
6289
+ }
5491
6290
  }
5492
6291
  // Extract structural nodes found inside function bodies.
5493
6292
  // Each extract method visits its own children, so we return after extracting.
@@ -5501,6 +6300,8 @@ class TreeSitterExtractor {
5501
6300
  this.extractInterface(node);
5502
6301
  else if (classification === 'trait')
5503
6302
  this.extractClass(node, 'trait');
6303
+ else if (classification === 'module')
6304
+ this.extractClass(node, 'module');
5504
6305
  else
5505
6306
  this.extractClass(node);
5506
6307
  return;
@@ -5509,6 +6310,10 @@ class TreeSitterExtractor {
5509
6310
  this.extractStruct(node);
5510
6311
  return;
5511
6312
  }
6313
+ if (this.extractor.unionTypes?.includes(nodeType)) {
6314
+ this.extractUnion(node);
6315
+ return;
6316
+ }
5512
6317
  if (this.extractor.enumTypes.includes(nodeType)) {
5513
6318
  this.extractEnum(node);
5514
6319
  return;
@@ -5917,31 +6722,20 @@ class TreeSitterExtractor {
5917
6722
  * For plain `impl Type { ... }` (no trait), no inheritance edge is needed.
5918
6723
  */
5919
6724
  extractRustImplItem(node) {
5920
- // Check if this is `impl Trait for Type` by looking for a `for` keyword
5921
- const hasFor = node.children.some((c) => c.type === 'for' && !c.isNamed);
5922
- if (!hasFor)
6725
+ // `impl Trait for Type` carries the trait in the grammar's `trait` field;
6726
+ // an inherent `impl Type { … }` has none and needs no inheritance edge.
6727
+ const traitNode = (0, tree_sitter_helpers_1.getChildByField)(node, 'trait');
6728
+ if (!traitNode)
5923
6729
  return;
5924
- // In `impl Trait for Type`, the type_identifiers are:
5925
- // first = Trait name, last = implementing Type name
5926
- // Also handle generic types like `impl<T> Trait for MyStruct<T>`
5927
- const typeIdents = node.namedChildren.filter((c) => c.type === 'type_identifier' || c.type === 'generic_type' || c.type === 'scoped_type_identifier');
5928
- if (typeIdents.length < 2)
6730
+ // Full text, so a scoped path (`std::fmt::Display`) and a generic trait
6731
+ // (`From<u32>`) keep their spelling.
6732
+ const traitName = (0, tree_sitter_helpers_1.getNodeText)(traitNode, this.source);
6733
+ // The implementing type from the `type` field (#1588). The old positional
6734
+ // scan took the LAST type-shaped child, which for a parameterized
6735
+ // implementing type (`BufSource<T>`, `Parents<'a>`, `&Foo`) was the trait.
6736
+ const typeName = (0, rust_1.rustImplTypeName)((0, tree_sitter_helpers_1.getChildByField)(node, 'type'), this.source);
6737
+ if (!typeName)
5929
6738
  return;
5930
- const traitNode = typeIdents[0];
5931
- const typeNode = typeIdents[typeIdents.length - 1];
5932
- // Get the trait name (handle scoped paths like std::fmt::Display)
5933
- const traitName = traitNode.type === 'scoped_type_identifier'
5934
- ? this.source.substring(traitNode.startIndex, traitNode.endIndex)
5935
- : (0, tree_sitter_helpers_1.getNodeText)(traitNode, this.source);
5936
- // Get the implementing type name (extract inner type_identifier for generics)
5937
- let typeName;
5938
- if (typeNode.type === 'generic_type') {
5939
- const inner = typeNode.namedChildren.find((c) => c.type === 'type_identifier');
5940
- typeName = inner ? (0, tree_sitter_helpers_1.getNodeText)(inner, this.source) : (0, tree_sitter_helpers_1.getNodeText)(typeNode, this.source);
5941
- }
5942
- else {
5943
- typeName = (0, tree_sitter_helpers_1.getNodeText)(typeNode, this.source);
5944
- }
5945
6739
  // Find the struct/type node for the implementing type
5946
6740
  const typeNodeId = this.findNodeByName(typeName);
5947
6741
  if (typeNodeId) {
@@ -5959,7 +6753,7 @@ class TreeSitterExtractor {
5959
6753
  */
5960
6754
  findNodeByName(name) {
5961
6755
  for (const node of this.nodes) {
5962
- if (node.name === name && (node.kind === 'struct' || node.kind === 'enum' || node.kind === 'class')) {
6756
+ if (node.name === name && (node.kind === 'struct' || node.kind === 'union' || node.kind === 'enum' || node.kind === 'class')) {
5963
6757
  return node.id;
5964
6758
  }
5965
6759
  }