@lzehrung/codegraph 2.3.30 → 2.4.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 (416) hide show
  1. package/README.md +30 -137
  2. package/codegraph-skill/codegraph/SKILL.md +5 -3
  3. package/dist/agent/artifact.d.ts +2 -0
  4. package/dist/agent/artifact.d.ts.map +1 -1
  5. package/dist/agent/artifact.js.map +1 -1
  6. package/dist/agent/search.d.ts.map +1 -1
  7. package/dist/agent/search.js +3 -0
  8. package/dist/agent/search.js.map +1 -1
  9. package/dist/agent/session.d.ts +3 -0
  10. package/dist/agent/session.d.ts.map +1 -1
  11. package/dist/agent/session.js +10 -2
  12. package/dist/agent/session.js.map +1 -1
  13. package/dist/bin/cli.js +42229 -28876
  14. package/dist/bin/query-index-worker.js +73 -11
  15. package/dist/cli/affected.d.ts.map +1 -1
  16. package/dist/cli/affected.js +4 -1
  17. package/dist/cli/affected.js.map +1 -1
  18. package/dist/cli/command-catalog.js +1 -1
  19. package/dist/cli/command-catalog.js.map +1 -1
  20. package/dist/cli/graph-queries.d.ts.map +1 -1
  21. package/dist/cli/graph-queries.js +27 -1
  22. package/dist/cli/graph-queries.js.map +1 -1
  23. package/dist/cli/help.d.ts +2 -2
  24. package/dist/cli/help.d.ts.map +1 -1
  25. package/dist/cli/help.js +4 -2
  26. package/dist/cli/help.js.map +1 -1
  27. package/dist/cli/options.js +2 -2
  28. package/dist/cli/options.js.map +1 -1
  29. package/dist/drift/artifact.d.ts.map +1 -1
  30. package/dist/drift/artifact.js +15 -18
  31. package/dist/drift/artifact.js.map +1 -1
  32. package/dist/drift/compare.js +1 -1
  33. package/dist/drift/compare.js.map +1 -1
  34. package/dist/drift/snapshot.d.ts.map +1 -1
  35. package/dist/drift/snapshot.js +5 -6
  36. package/dist/drift/snapshot.js.map +1 -1
  37. package/dist/drift/types.d.ts +2 -0
  38. package/dist/drift/types.d.ts.map +1 -1
  39. package/dist/drift/types.js.map +1 -1
  40. package/dist/graph-builder.d.ts.map +1 -1
  41. package/dist/graph-builder.js +4 -6
  42. package/dist/graph-builder.js.map +1 -1
  43. package/dist/graph-edge-collector.d.ts +3 -0
  44. package/dist/graph-edge-collector.d.ts.map +1 -1
  45. package/dist/graph-edge-collector.js +8 -5
  46. package/dist/graph-edge-collector.js.map +1 -1
  47. package/dist/graphs/angularjs.d.ts.map +1 -1
  48. package/dist/graphs/angularjs.js +2 -1
  49. package/dist/graphs/angularjs.js.map +1 -1
  50. package/dist/graphs/edge-resolution.d.ts +4 -1
  51. package/dist/graphs/edge-resolution.d.ts.map +1 -1
  52. package/dist/graphs/edge-resolution.js +26 -2
  53. package/dist/graphs/edge-resolution.js.map +1 -1
  54. package/dist/graphs/specifiers.d.ts.map +1 -1
  55. package/dist/graphs/specifiers.js +6 -2
  56. package/dist/graphs/specifiers.js.map +1 -1
  57. package/dist/graphs/symbol-graph-detailed/ast.d.ts +20 -0
  58. package/dist/graphs/symbol-graph-detailed/ast.d.ts.map +1 -1
  59. package/dist/graphs/symbol-graph-detailed/ast.js +154 -15
  60. package/dist/graphs/symbol-graph-detailed/ast.js.map +1 -1
  61. package/dist/graphs/symbol-graph-detailed/edge-passes.d.ts +51 -8
  62. package/dist/graphs/symbol-graph-detailed/edge-passes.d.ts.map +1 -1
  63. package/dist/graphs/symbol-graph-detailed/edge-passes.js +823 -132
  64. package/dist/graphs/symbol-graph-detailed/edge-passes.js.map +1 -1
  65. package/dist/graphs/symbol-graph-detailed/import-aliases.d.ts +2 -1
  66. package/dist/graphs/symbol-graph-detailed/import-aliases.d.ts.map +1 -1
  67. package/dist/graphs/symbol-graph-detailed/import-aliases.js +139 -7
  68. package/dist/graphs/symbol-graph-detailed/import-aliases.js.map +1 -1
  69. package/dist/graphs/symbol-graph-detailed/member-chains.d.ts +1 -0
  70. package/dist/graphs/symbol-graph-detailed/member-chains.d.ts.map +1 -1
  71. package/dist/graphs/symbol-graph-detailed/member-chains.js +3 -1
  72. package/dist/graphs/symbol-graph-detailed/member-chains.js.map +1 -1
  73. package/dist/graphs/symbol-graph-detailed/receiver-calls.d.ts +128 -22
  74. package/dist/graphs/symbol-graph-detailed/receiver-calls.d.ts.map +1 -1
  75. package/dist/graphs/symbol-graph-detailed/receiver-calls.js +872 -125
  76. package/dist/graphs/symbol-graph-detailed/receiver-calls.js.map +1 -1
  77. package/dist/graphs/symbol-graph-detailed.d.ts.map +1 -1
  78. package/dist/graphs/symbol-graph-detailed.js +434 -40
  79. package/dist/graphs/symbol-graph-detailed.js.map +1 -1
  80. package/dist/graphs/symbol-graph.d.ts +2 -0
  81. package/dist/graphs/symbol-graph.d.ts.map +1 -1
  82. package/dist/graphs/symbol-graph.js +34 -16
  83. package/dist/graphs/symbol-graph.js.map +1 -1
  84. package/dist/impact/call-compatibility/providers/index.d.ts +12 -0
  85. package/dist/impact/call-compatibility/providers/index.d.ts.map +1 -1
  86. package/dist/impact/call-compatibility/providers/index.js +16 -22
  87. package/dist/impact/call-compatibility/providers/index.js.map +1 -1
  88. package/dist/impact/call-compatibility/providers/types.d.ts +2 -0
  89. package/dist/impact/call-compatibility/providers/types.d.ts.map +1 -1
  90. package/dist/impact/call-compatibility/types.d.ts +6 -0
  91. package/dist/impact/call-compatibility/types.d.ts.map +1 -1
  92. package/dist/impact/call-compatibility.d.ts.map +1 -1
  93. package/dist/impact/call-compatibility.js +316 -552
  94. package/dist/impact/call-compatibility.js.map +1 -1
  95. package/dist/impact/map.js +1 -0
  96. package/dist/impact/map.js.map +1 -1
  97. package/dist/impact/member-resolution-coverage.d.ts.map +1 -1
  98. package/dist/impact/member-resolution-coverage.js +1 -1
  99. package/dist/impact/member-resolution-coverage.js.map +1 -1
  100. package/dist/impact/reference-cache.d.ts.map +1 -1
  101. package/dist/impact/reference-cache.js +2 -2
  102. package/dist/impact/reference-cache.js.map +1 -1
  103. package/dist/index.d.ts +1 -1
  104. package/dist/index.d.ts.map +1 -1
  105. package/dist/index.js +1 -1
  106. package/dist/index.js.map +1 -1
  107. package/dist/indexer/ambiguous-resolution.d.ts +11 -0
  108. package/dist/indexer/ambiguous-resolution.d.ts.map +1 -0
  109. package/dist/indexer/ambiguous-resolution.js +19 -0
  110. package/dist/indexer/ambiguous-resolution.js.map +1 -0
  111. package/dist/indexer/build-cache/module-cache.d.ts +38 -0
  112. package/dist/indexer/build-cache/module-cache.d.ts.map +1 -1
  113. package/dist/indexer/build-cache/module-cache.js +151 -9
  114. package/dist/indexer/build-cache/module-cache.js.map +1 -1
  115. package/dist/indexer/build-cache/options.d.ts +16 -43
  116. package/dist/indexer/build-cache/options.d.ts.map +1 -1
  117. package/dist/indexer/build-cache/options.js +16 -43
  118. package/dist/indexer/build-cache/options.js.map +1 -1
  119. package/dist/indexer/build-cache/project-snapshot.d.ts +1 -0
  120. package/dist/indexer/build-cache/project-snapshot.d.ts.map +1 -1
  121. package/dist/indexer/build-cache/project-snapshot.js +117 -24
  122. package/dist/indexer/build-cache/project-snapshot.js.map +1 -1
  123. package/dist/indexer/build-cache/resolver-environment.d.ts.map +1 -1
  124. package/dist/indexer/build-cache/resolver-environment.js +32 -2
  125. package/dist/indexer/build-cache/resolver-environment.js.map +1 -1
  126. package/dist/indexer/build-cache.d.ts +1 -1
  127. package/dist/indexer/build-cache.d.ts.map +1 -1
  128. package/dist/indexer/build-cache.js +1 -1
  129. package/dist/indexer/build-cache.js.map +1 -1
  130. package/dist/indexer/build-index.d.ts.map +1 -1
  131. package/dist/indexer/build-index.js +593 -22
  132. package/dist/indexer/build-index.js.map +1 -1
  133. package/dist/indexer/call-hierarchy.js +1 -1
  134. package/dist/indexer/call-hierarchy.js.map +1 -1
  135. package/dist/indexer/compilation-units.d.ts +101 -0
  136. package/dist/indexer/compilation-units.d.ts.map +1 -0
  137. package/dist/indexer/compilation-units.js +605 -0
  138. package/dist/indexer/compilation-units.js.map +1 -0
  139. package/dist/indexer/cpp-callables.d.ts +20 -0
  140. package/dist/indexer/cpp-callables.d.ts.map +1 -0
  141. package/dist/indexer/cpp-callables.js +554 -0
  142. package/dist/indexer/cpp-callables.js.map +1 -0
  143. package/dist/indexer/declaration-visibility.d.ts +36 -2
  144. package/dist/indexer/declaration-visibility.d.ts.map +1 -1
  145. package/dist/indexer/declaration-visibility.js +136 -2
  146. package/dist/indexer/declaration-visibility.js.map +1 -1
  147. package/dist/indexer/expand-star-imports.d.ts.map +1 -1
  148. package/dist/indexer/expand-star-imports.js +28 -6
  149. package/dist/indexer/expand-star-imports.js.map +1 -1
  150. package/dist/indexer/import-types.d.ts +37 -0
  151. package/dist/indexer/import-types.d.ts.map +1 -1
  152. package/dist/indexer/import-types.js +24 -1
  153. package/dist/indexer/import-types.js.map +1 -1
  154. package/dist/indexer/imports/binding-ranges.d.ts +1 -1
  155. package/dist/indexer/imports/binding-ranges.d.ts.map +1 -1
  156. package/dist/indexer/imports/context.d.ts +11 -1
  157. package/dist/indexer/imports/context.d.ts.map +1 -1
  158. package/dist/indexer/imports/import-binding-tables.d.ts +75 -0
  159. package/dist/indexer/imports/import-binding-tables.d.ts.map +1 -0
  160. package/dist/indexer/imports/import-binding-tables.js +544 -0
  161. package/dist/indexer/imports/import-binding-tables.js.map +1 -0
  162. package/dist/indexer/imports/js-text-imports.js +4 -4
  163. package/dist/indexer/imports/js-text-imports.js.map +1 -1
  164. package/dist/indexer/imports/language-specific.d.ts +3 -22
  165. package/dist/indexer/imports/language-specific.d.ts.map +1 -1
  166. package/dist/indexer/imports/language-specific.js +29 -362
  167. package/dist/indexer/imports/language-specific.js.map +1 -1
  168. package/dist/indexer/imports/native-captures.d.ts.map +1 -1
  169. package/dist/indexer/imports/native-captures.js +18 -6
  170. package/dist/indexer/imports/native-captures.js.map +1 -1
  171. package/dist/indexer/imports/python.d.ts +10 -2
  172. package/dist/indexer/imports/python.d.ts.map +1 -1
  173. package/dist/indexer/imports/python.js +91 -40
  174. package/dist/indexer/imports/python.js.map +1 -1
  175. package/dist/indexer/imports/text-import-extractors.d.ts.map +1 -1
  176. package/dist/indexer/imports/text-import-extractors.js +51 -12
  177. package/dist/indexer/imports/text-import-extractors.js.map +1 -1
  178. package/dist/indexer/imports.d.ts.map +1 -1
  179. package/dist/indexer/imports.js +25 -5
  180. package/dist/indexer/imports.js.map +1 -1
  181. package/dist/indexer/incremental-plan.d.ts +33 -0
  182. package/dist/indexer/incremental-plan.d.ts.map +1 -1
  183. package/dist/indexer/incremental-plan.js +187 -1
  184. package/dist/indexer/incremental-plan.js.map +1 -1
  185. package/dist/indexer/locals-and-exports.d.ts +2 -0
  186. package/dist/indexer/locals-and-exports.d.ts.map +1 -1
  187. package/dist/indexer/locals-and-exports.js +565 -54
  188. package/dist/indexer/locals-and-exports.js.map +1 -1
  189. package/dist/indexer/name-lookup-policies/c-family.d.ts +7 -0
  190. package/dist/indexer/name-lookup-policies/c-family.d.ts.map +1 -0
  191. package/dist/indexer/name-lookup-policies/c-family.js +117 -0
  192. package/dist/indexer/name-lookup-policies/c-family.js.map +1 -0
  193. package/dist/indexer/name-lookup-policies/csharp-using-static.d.ts +11 -0
  194. package/dist/indexer/name-lookup-policies/csharp-using-static.d.ts.map +1 -0
  195. package/dist/indexer/name-lookup-policies/csharp-using-static.js +136 -0
  196. package/dist/indexer/name-lookup-policies/csharp-using-static.js.map +1 -0
  197. package/dist/indexer/name-lookup-policies/implicit-self.d.ts +10 -0
  198. package/dist/indexer/name-lookup-policies/implicit-self.d.ts.map +1 -0
  199. package/dist/indexer/name-lookup-policies/implicit-self.js +113 -0
  200. package/dist/indexer/name-lookup-policies/implicit-self.js.map +1 -0
  201. package/dist/indexer/name-lookup-policies/index.d.ts +3 -0
  202. package/dist/indexer/name-lookup-policies/index.d.ts.map +1 -0
  203. package/dist/indexer/name-lookup-policies/index.js +106 -0
  204. package/dist/indexer/name-lookup-policies/index.js.map +1 -0
  205. package/dist/indexer/name-lookup-policies/php.d.ts +3 -0
  206. package/dist/indexer/name-lookup-policies/php.d.ts.map +1 -0
  207. package/dist/indexer/name-lookup-policies/php.js +75 -0
  208. package/dist/indexer/name-lookup-policies/php.js.map +1 -0
  209. package/dist/indexer/name-resolution-types.d.ts +99 -0
  210. package/dist/indexer/name-resolution-types.d.ts.map +1 -0
  211. package/dist/indexer/name-resolution-types.js +2 -0
  212. package/dist/indexer/name-resolution-types.js.map +1 -0
  213. package/dist/indexer/name-resolution.d.ts +52 -0
  214. package/dist/indexer/name-resolution.d.ts.map +1 -0
  215. package/dist/indexer/name-resolution.js +189 -0
  216. package/dist/indexer/name-resolution.js.map +1 -0
  217. package/dist/indexer/navigation-cpp.d.ts +50 -0
  218. package/dist/indexer/navigation-cpp.d.ts.map +1 -0
  219. package/dist/indexer/navigation-cpp.js +654 -0
  220. package/dist/indexer/navigation-cpp.js.map +1 -0
  221. package/dist/indexer/navigation-goto.d.ts +107 -4
  222. package/dist/indexer/navigation-goto.d.ts.map +1 -1
  223. package/dist/indexer/navigation-goto.js +2075 -137
  224. package/dist/indexer/navigation-goto.js.map +1 -1
  225. package/dist/indexer/navigation-local.d.ts +18 -2
  226. package/dist/indexer/navigation-local.d.ts.map +1 -1
  227. package/dist/indexer/navigation-local.js +498 -61
  228. package/dist/indexer/navigation-local.js.map +1 -1
  229. package/dist/indexer/navigation-php.d.ts +66 -0
  230. package/dist/indexer/navigation-php.d.ts.map +1 -1
  231. package/dist/indexer/navigation-php.js +287 -4
  232. package/dist/indexer/navigation-php.js.map +1 -1
  233. package/dist/indexer/navigation-references.d.ts +72 -5
  234. package/dist/indexer/navigation-references.d.ts.map +1 -1
  235. package/dist/indexer/navigation-references.js +620 -54
  236. package/dist/indexer/navigation-references.js.map +1 -1
  237. package/dist/indexer/navigation-resolve.d.ts +33 -2
  238. package/dist/indexer/navigation-resolve.d.ts.map +1 -1
  239. package/dist/indexer/navigation-resolve.js +446 -193
  240. package/dist/indexer/navigation-resolve.js.map +1 -1
  241. package/dist/indexer/navigation.d.ts +28 -2
  242. package/dist/indexer/navigation.d.ts.map +1 -1
  243. package/dist/indexer/navigation.js +1082 -151
  244. package/dist/indexer/navigation.js.map +1 -1
  245. package/dist/indexer/php-namespace-symbols.d.ts +66 -0
  246. package/dist/indexer/php-namespace-symbols.d.ts.map +1 -0
  247. package/dist/indexer/php-namespace-symbols.js +342 -0
  248. package/dist/indexer/php-namespace-symbols.js.map +1 -0
  249. package/dist/indexer/reference-context.d.ts +2 -0
  250. package/dist/indexer/reference-context.d.ts.map +1 -1
  251. package/dist/indexer/reference-context.js +4 -0
  252. package/dist/indexer/reference-context.js.map +1 -1
  253. package/dist/indexer/scope-nodes.d.ts +92 -13
  254. package/dist/indexer/scope-nodes.d.ts.map +1 -1
  255. package/dist/indexer/scope-nodes.js +212 -20
  256. package/dist/indexer/scope-nodes.js.map +1 -1
  257. package/dist/indexer/scope-types.d.ts +25 -1
  258. package/dist/indexer/scope-types.d.ts.map +1 -1
  259. package/dist/indexer/scope.d.ts.map +1 -1
  260. package/dist/indexer/scope.js +894 -45
  261. package/dist/indexer/scope.js.map +1 -1
  262. package/dist/indexer/shared-owner-identity.d.ts +53 -0
  263. package/dist/indexer/shared-owner-identity.d.ts.map +1 -0
  264. package/dist/indexer/shared-owner-identity.js +460 -0
  265. package/dist/indexer/shared-owner-identity.js.map +1 -0
  266. package/dist/indexer/shared.d.ts +1 -1
  267. package/dist/indexer/shared.d.ts.map +1 -1
  268. package/dist/indexer/shared.js +1 -1
  269. package/dist/indexer/shared.js.map +1 -1
  270. package/dist/indexer/star-import-precedence.d.ts +114 -0
  271. package/dist/indexer/star-import-precedence.d.ts.map +1 -0
  272. package/dist/indexer/star-import-precedence.js +396 -0
  273. package/dist/indexer/star-import-precedence.js.map +1 -0
  274. package/dist/indexer/symbols.d.ts +3 -1
  275. package/dist/indexer/symbols.d.ts.map +1 -1
  276. package/dist/indexer/symbols.js +58 -18
  277. package/dist/indexer/symbols.js.map +1 -1
  278. package/dist/indexer/ts-callables.d.ts +51 -0
  279. package/dist/indexer/ts-callables.d.ts.map +1 -0
  280. package/dist/indexer/ts-callables.js +264 -0
  281. package/dist/indexer/ts-callables.js.map +1 -0
  282. package/dist/indexer/types.d.ts +23 -1
  283. package/dist/indexer/types.d.ts.map +1 -1
  284. package/dist/indexer/unused-exports.d.ts +13 -0
  285. package/dist/indexer/unused-exports.d.ts.map +1 -0
  286. package/dist/indexer/unused-exports.js +159 -0
  287. package/dist/indexer/unused-exports.js.map +1 -0
  288. package/dist/indexer/workspace-symbols.d.ts.map +1 -1
  289. package/dist/indexer/workspace-symbols.js +3 -2
  290. package/dist/indexer/workspace-symbols.js.map +1 -1
  291. package/dist/indexer.d.ts +2 -2
  292. package/dist/indexer.d.ts.map +1 -1
  293. package/dist/indexer.js +1 -1
  294. package/dist/indexer.js.map +1 -1
  295. package/dist/languages/callable-arity.d.ts +202 -0
  296. package/dist/languages/callable-arity.d.ts.map +1 -0
  297. package/dist/languages/callable-arity.js +1182 -0
  298. package/dist/languages/callable-arity.js.map +1 -0
  299. package/dist/languages/definitions/c-family.d.ts.map +1 -1
  300. package/dist/languages/definitions/c-family.js +30 -1
  301. package/dist/languages/definitions/c-family.js.map +1 -1
  302. package/dist/languages/definitions/c.d.ts +4 -0
  303. package/dist/languages/definitions/c.d.ts.map +1 -1
  304. package/dist/languages/definitions/c.js +19 -0
  305. package/dist/languages/definitions/c.js.map +1 -1
  306. package/dist/languages/definitions/cpp.d.ts.map +1 -1
  307. package/dist/languages/definitions/cpp.js +12 -0
  308. package/dist/languages/definitions/cpp.js.map +1 -1
  309. package/dist/languages/definitions/csharp.d.ts.map +1 -1
  310. package/dist/languages/definitions/csharp.js +2 -8
  311. package/dist/languages/definitions/csharp.js.map +1 -1
  312. package/dist/languages/definitions/go.d.ts.map +1 -1
  313. package/dist/languages/definitions/go.js +3 -0
  314. package/dist/languages/definitions/go.js.map +1 -1
  315. package/dist/languages/definitions/javascript.d.ts.map +1 -1
  316. package/dist/languages/definitions/javascript.js +10 -1
  317. package/dist/languages/definitions/javascript.js.map +1 -1
  318. package/dist/languages/definitions/js-family.d.ts +1 -1
  319. package/dist/languages/definitions/js-family.d.ts.map +1 -1
  320. package/dist/languages/definitions/js-family.js +7 -1
  321. package/dist/languages/definitions/js-family.js.map +1 -1
  322. package/dist/languages/definitions/php.d.ts.map +1 -1
  323. package/dist/languages/definitions/php.js +5 -1
  324. package/dist/languages/definitions/php.js.map +1 -1
  325. package/dist/languages/definitions/typescript.d.ts.map +1 -1
  326. package/dist/languages/definitions/typescript.js +8 -1
  327. package/dist/languages/definitions/typescript.js.map +1 -1
  328. package/dist/languages/definitions/zig.d.ts.map +1 -1
  329. package/dist/languages/definitions/zig.js +2 -0
  330. package/dist/languages/definitions/zig.js.map +1 -1
  331. package/dist/mcp/handlers.js +14 -7
  332. package/dist/mcp/handlers.js.map +1 -1
  333. package/dist/review/candidates.d.ts.map +1 -1
  334. package/dist/review/candidates.js +1 -1
  335. package/dist/review/candidates.js.map +1 -1
  336. package/dist/review/deleted.d.ts +3 -3
  337. package/dist/review/deleted.d.ts.map +1 -1
  338. package/dist/review/deleted.js +82 -32
  339. package/dist/review/deleted.js.map +1 -1
  340. package/dist/review/report.d.ts +1 -0
  341. package/dist/review/report.d.ts.map +1 -1
  342. package/dist/review/report.js +1 -1
  343. package/dist/review/report.js.map +1 -1
  344. package/dist/review.d.ts.map +1 -1
  345. package/dist/review.js +1 -0
  346. package/dist/review.js.map +1 -1
  347. package/dist/sql/navigation.d.ts.map +1 -1
  348. package/dist/sql/navigation.js +1 -0
  349. package/dist/sql/navigation.js.map +1 -1
  350. package/dist/sql/source-graph.d.ts.map +1 -1
  351. package/dist/sql/source-graph.js +2 -2
  352. package/dist/sql/source-graph.js.map +1 -1
  353. package/dist/sqlite/schema.d.ts +1 -1
  354. package/dist/sqlite/schema.d.ts.map +1 -1
  355. package/dist/sqlite/schema.js +8 -2
  356. package/dist/sqlite/schema.js.map +1 -1
  357. package/dist/sqlite/write.js +2 -2
  358. package/dist/sqlite/write.js.map +1 -1
  359. package/dist/types.d.ts +2 -0
  360. package/dist/types.d.ts.map +1 -1
  361. package/dist/util/bloom-filter.d.ts +7 -1
  362. package/dist/util/bloom-filter.d.ts.map +1 -1
  363. package/dist/util/bloom-filter.js +12 -2
  364. package/dist/util/bloom-filter.js.map +1 -1
  365. package/dist/util/dynamic-import-tables.d.ts +88 -0
  366. package/dist/util/dynamic-import-tables.d.ts.map +1 -0
  367. package/dist/util/dynamic-import-tables.js +100 -0
  368. package/dist/util/dynamic-import-tables.js.map +1 -0
  369. package/dist/util/graph-edges.d.ts +19 -1
  370. package/dist/util/graph-edges.d.ts.map +1 -1
  371. package/dist/util/graph-edges.js +30 -4
  372. package/dist/util/graph-edges.js.map +1 -1
  373. package/dist/util/identifiers.d.ts +15 -0
  374. package/dist/util/identifiers.d.ts.map +1 -1
  375. package/dist/util/identifiers.js +35 -0
  376. package/dist/util/identifiers.js.map +1 -1
  377. package/dist/util/member-access-tables.d.ts +129 -0
  378. package/dist/util/member-access-tables.d.ts.map +1 -0
  379. package/dist/util/member-access-tables.js +285 -0
  380. package/dist/util/member-access-tables.js.map +1 -0
  381. package/dist/util/member-access.d.ts +17 -0
  382. package/dist/util/member-access.d.ts.map +1 -1
  383. package/dist/util/member-access.js +138 -93
  384. package/dist/util/member-access.js.map +1 -1
  385. package/dist/util/resolution/cargo-targets.d.ts +11 -0
  386. package/dist/util/resolution/cargo-targets.d.ts.map +1 -1
  387. package/dist/util/resolution/cargo-targets.js +174 -13
  388. package/dist/util/resolution/cargo-targets.js.map +1 -1
  389. package/dist/util/resolution/csharp.d.ts +33 -1
  390. package/dist/util/resolution/csharp.d.ts.map +1 -1
  391. package/dist/util/resolution/csharp.js +145 -30
  392. package/dist/util/resolution/csharp.js.map +1 -1
  393. package/dist/util/resolution/php.d.ts.map +1 -1
  394. package/dist/util/resolution/php.js +35 -8
  395. package/dist/util/resolution/php.js.map +1 -1
  396. package/dist/util/resolution/python.d.ts +9 -0
  397. package/dist/util/resolution/python.d.ts.map +1 -1
  398. package/dist/util/resolution/python.js +46 -0
  399. package/dist/util/resolution/python.js.map +1 -1
  400. package/dist/util/resolution/rust.d.ts.map +1 -1
  401. package/dist/util/resolution/rust.js +95 -26
  402. package/dist/util/resolution/rust.js.map +1 -1
  403. package/dist/util/resolution-candidates.d.ts +5 -0
  404. package/dist/util/resolution-candidates.d.ts.map +1 -1
  405. package/dist/util/resolution-candidates.js +16 -3
  406. package/dist/util/resolution-candidates.js.map +1 -1
  407. package/dist/util/resolution.d.ts +2 -1
  408. package/dist/util/resolution.d.ts.map +1 -1
  409. package/dist/util/resolution.js +96 -5
  410. package/dist/util/resolution.js.map +1 -1
  411. package/dist/util/specifiers.d.ts +20 -0
  412. package/dist/util/specifiers.d.ts.map +1 -1
  413. package/dist/util/specifiers.js +39 -93
  414. package/dist/util/specifiers.js.map +1 -1
  415. package/docs/mcp.md +3 -2
  416. package/package.json +3 -3
@@ -1,29 +1,14 @@
1
1
  import { readFileSync } from "node:fs";
2
+ import { findCommentEnd } from "../../impact/call-compatibility/text-scanner.js";
3
+ import { isGoExportedMemberName } from "../../indexer/declaration-visibility.js";
2
4
  import { SymbolKind } from "../../indexer/types.js";
5
+ import { isJsTsLanguage } from "../../languages/js-family.js";
3
6
  import { sliceText } from "../../util/ast.js";
4
- import { XID_IDENTIFIER_SOURCE } from "../../util/identifiers.js";
5
- import { getMemberAccessParts, getNavigationExpressionProperty, isMemberAccessNode, isMemberReferencePropertyIdentifier, isReceiverNameNode, } from "../../util/member-access.js";
6
- const THIS_SUPER_RECEIVERS = { own: ["this"], supertype: ["super"] };
7
- const SELF_RECEIVERS = { own: ["self"], supertype: ["super"] };
8
- const RECEIVER_KEYWORDS = {
9
- cpp: { own: ["this"], supertype: [] },
10
- csharp: { own: ["this"], supertype: ["base"] },
11
- go: { own: [], supertype: [] },
12
- java: THIS_SUPER_RECEIVERS,
13
- js: THIS_SUPER_RECEIVERS,
14
- kotlin: THIS_SUPER_RECEIVERS,
15
- php: { own: ["$this", "self", "static"], supertype: ["parent"] },
16
- python: { own: ["self", "cls"], supertype: [] },
17
- ruby: { own: ["self"], supertype: [] },
18
- rust: { own: ["self", "Self"], supertype: [] },
19
- swift: SELF_RECEIVERS,
20
- ts: THIS_SUPER_RECEIVERS,
21
- tsx: THIS_SUPER_RECEIVERS,
22
- zig: { own: ["self"], supertype: [] },
23
- };
24
- const INSTANCE_RECEIVER_KEYWORDS = new Set(["this", "$this"]);
25
- /** Every language that declares receiver keywords, guarded by the registry-consistency test. */
26
- export const receiverKeywordLanguageIds = Object.keys(RECEIVER_KEYWORDS);
7
+ import { foldPhpIdentifierCase, XID_IDENTIFIER_SOURCE } from "../../util/identifiers.js";
8
+ import { fileIdentityKey } from "../../util/paths.js";
9
+ import { keywordReceiverKind, ownReceiverMemberScope } from "../../util/member-access-tables.js";
10
+ import { getMemberAccessParts, getNavigationExpressionProperty, isMemberAccessNode, isMemberReferencePropertyIdentifier, isReceiverNameNode, receiverKeywordText, } from "../../util/member-access.js";
11
+ import { declarationMemberArity, findFirstNodeByType, isIdentifierType, PARAMETER_LIST_NODE_TYPES } from "./ast.js";
27
12
  /** Languages whose grammar distinguishes static members from instance members. */
28
13
  const STATIC_MEMBER_LANGUAGES = {
29
14
  cpp: true,
@@ -35,24 +20,68 @@ const STATIC_MEMBER_LANGUAGES = {
35
20
  ts: true,
36
21
  tsx: true,
37
22
  };
23
+ const MEMBER_OVERLOAD_LANGUAGE_IDS = {
24
+ cpp: true,
25
+ csharp: true,
26
+ java: true,
27
+ kotlin: true,
28
+ swift: true,
29
+ ts: true,
30
+ tsx: true,
31
+ };
32
+ /** Bare calls inside members can target a proven member even without an explicit receiver. */
33
+ const IMPLICIT_SELF_MEMBER_CALL_LANGUAGES = {
34
+ csharp: true,
35
+ swift: true,
36
+ };
37
+ /** Whether a bare, receiver-less call inside a member function may target `this`/an inherited member. */
38
+ export function supportsImplicitSelfMemberCalls(languageId) {
39
+ return !!IMPLICIT_SELF_MEMBER_CALL_LANGUAGES[languageId];
40
+ }
41
+ /** Whether member lookup uses call arity to select or reject same-name declarations. */
42
+ export function supportsReceiverMemberOverloads(languageId) {
43
+ return !!MEMBER_OVERLOAD_LANGUAGE_IDS[languageId];
44
+ }
38
45
  /** Every language with a static-member distinction, guarded by the registry-consistency test. */
39
46
  export const staticMemberLanguageIds = Object.keys(STATIC_MEMBER_LANGUAGES);
40
- /** Languages that emit proven receiver `calls` edges from the shared keyword table. */
41
- export function supportsReceiverCallEdges(languageId) {
42
- return RECEIVER_KEYWORDS[languageId] !== undefined;
43
- }
44
- /** Languages where goto and references require proven receivers and do not fall back to a bare name. */
45
- export function supportsReceiverMemberNavigation(languageId) {
46
- return RECEIVER_KEYWORDS[languageId] !== undefined;
47
+ export function hasStaticMemberDistinction(languageId) {
48
+ return STATIC_MEMBER_LANGUAGES[languageId] !== undefined;
47
49
  }
48
- export function isKeywordReceiver(languageId, receiverName) {
49
- const keywords = RECEIVER_KEYWORDS[languageId];
50
- if (!keywords)
50
+ /**
51
+ * Languages whose grammar gives members a static-equivalent scope without a `static` keyword:
52
+ * a Kotlin member of a `companion object` or a named `object` declaration is reachable as
53
+ * `Outer.member()`. Member lookup classifies those members "static" and every other member
54
+ * "instance", matching the keyword-`static` languages.
55
+ */
56
+ const STATIC_EQUIVALENT_MEMBER_CONTAINERS = {
57
+ kotlin: { companion_object: true, object_declaration: true },
58
+ };
59
+ /**
60
+ * Whether `node` is lexically declared inside its language's static-equivalent member
61
+ * container. The nearest enclosing member container decides: a nested type's members belong
62
+ * to that type, not to an outer `object`.
63
+ */
64
+ export function declarationIsStaticEquivalent(languageId, node) {
65
+ const containers = STATIC_EQUIVALENT_MEMBER_CONTAINERS[languageId];
66
+ if (!containers)
51
67
  return false;
52
- return keywords.own.includes(receiverName) || keywords.supertype.includes(receiverName);
68
+ let current = node.parent;
69
+ while (current) {
70
+ if (containers[current.type] === true)
71
+ return true;
72
+ if (MEMBER_CONTAINER_TYPES[current.type] === true)
73
+ return false;
74
+ current = current.parent;
75
+ }
76
+ return false;
53
77
  }
54
- export function hasStaticMemberDistinction(languageId) {
55
- return STATIC_MEMBER_LANGUAGES[languageId] !== undefined;
78
+ /**
79
+ * Whether member lookup for `languageId` distinguishes static-equivalent members from
80
+ * instance members. A bare type-name receiver restricts lookup to the static-equivalent
81
+ * scope exactly when this holds; otherwise every member must stay reachable.
82
+ */
83
+ export function supportsStaticMemberScope(languageId) {
84
+ return (STATIC_MEMBER_LANGUAGES[languageId] !== undefined || STATIC_EQUIVALENT_MEMBER_CONTAINERS[languageId] !== undefined);
56
85
  }
57
86
  /**
58
87
  * Call nodes that carry the receiver and the member name on the call node itself
@@ -84,6 +113,25 @@ const MEMBER_CONTAINER_TYPES = {
84
113
  struct_specifier: true,
85
114
  trait_declaration: true,
86
115
  trait_item: true,
116
+ type_alias_declaration: true,
117
+ // C/C++ unions declare members exactly like structs (tree-sitter-cpp captures
118
+ // union names and classifies them as classes).
119
+ union_specifier: true,
120
+ };
121
+ const CPP_MEMBER_CONTAINER_TYPES = {
122
+ class_specifier: true,
123
+ struct_specifier: true,
124
+ union_specifier: true,
125
+ };
126
+ /**
127
+ * Container nodes whose declared members belong to the *enclosing* member container for
128
+ * direct-member lookup, rather than forming a separate nested type. Kotlin's unnamed
129
+ * `companion object { ... }` block is the language's static-member mechanism: `create()`
130
+ * declared inside one is a member of the enclosing class, reachable as `Outer.create()`,
131
+ * not a member of a distinct "Companion" type the indexer would need to name separately.
132
+ */
133
+ export const TRANSPARENT_MEMBER_CONTAINER_TYPES = {
134
+ companion_object: true,
87
135
  };
88
136
  /** Nodes holding a call's argument list across the supported grammars. */
89
137
  export const CALL_ARGUMENT_NODE_TYPES = {
@@ -99,6 +147,16 @@ const HIERARCHY_LABELS = {
99
147
  mixin: true,
100
148
  trait: true,
101
149
  };
150
+ const UNPROVEN_HERITAGE_EXPRESSION_TYPES = new Set([
151
+ "binary_expression",
152
+ "call_expression",
153
+ "new_expression",
154
+ "subscript_expression",
155
+ "ternary_expression",
156
+ ]);
157
+ export function isUnprovenHeritageExpression(node) {
158
+ return UNPROVEN_HERITAGE_EXPRESSION_TYPES.has(node.type);
159
+ }
102
160
  /**
103
161
  * Nodes that bind a value name across the supported grammars: locals, parameters,
104
162
  * and assignment targets. A receiver naming one of these is a value, not a type.
@@ -108,6 +166,9 @@ const VALUE_BINDING_TYPES = {
108
166
  assignment_expression: true,
109
167
  assignment_statement: true,
110
168
  class_parameter: true,
169
+ // C/C++ local variable statement (`Box b;`, `Box* p = raw;`); the declared type sits
170
+ // beside the declarator, unlike other grammars' dedicated `variable_declaration` node.
171
+ declaration: true,
111
172
  formal_parameter: true,
112
173
  init_declarator: true,
113
174
  let_declaration: true,
@@ -123,21 +184,6 @@ const VALUE_BINDING_TYPES = {
123
184
  variable_declaration: true,
124
185
  variable_declarator: true,
125
186
  };
126
- /**
127
- * Parameter lists whose identifier children are value bindings (Ruby has no wrapping param node).
128
- * Union of the receiver-call and call-compatibility lists so Kotlin `function_value_parameters`
129
- * and Ruby `block_parameters` are both recognized.
130
- */
131
- export const PARAMETER_LIST_NODE_TYPES = {
132
- block_parameters: true,
133
- formal_parameters: true,
134
- function_parameter_clause: true,
135
- function_value_parameters: true,
136
- lambda_parameters: true,
137
- method_parameters: true,
138
- parameter_list: true,
139
- parameters: true,
140
- };
141
187
  /**
142
188
  * Access nodes whose syntax is type-scoped (`::`), not dotted member access.
143
189
  * A dotted identifier is never itself proof of a type.
@@ -213,6 +259,8 @@ const BINDING_DECLARATION_TYPES = new Set([
213
259
  "property_declaration",
214
260
  "local_variable_declaration",
215
261
  "local_declaration_statement",
262
+ // C/C++ local variable statement, e.g. `Box b;`, `Box* p = raw;`, `Box w{5};`.
263
+ "declaration",
216
264
  ]);
217
265
  const RUBY_CONSTANT_SOURCE = String.raw `(?=\p{Lu})${XID_IDENTIFIER_SOURCE}`;
218
266
  /** Proven construction forms that name a receiver's type, keyed by language. */
@@ -226,7 +274,7 @@ const LANGUAGE_CONSTRUCTION_FORMS = {
226
274
  php: { newExpression: true },
227
275
  python: { capitalizedCall: true },
228
276
  ruby: { rubyNew: true },
229
- rust: { capitalizedCall: true, rustUnitStruct: true },
277
+ rust: { capitalizedCall: true, rustUnitStruct: true, compositeLiteral: true },
230
278
  swift: { capitalizedCall: true },
231
279
  ts: { newExpression: true },
232
280
  tsx: { newExpression: true },
@@ -270,12 +318,13 @@ export function receiverCallAccess(sup, callNode, callee) {
270
318
  return { accessNode, receiver, property };
271
319
  }
272
320
  /**
273
- * Identifier a binding node declares: a `name` field, a nested C/C++ declarator,
274
- * an assignment left-hand side, or the last identifier child (C++ parameters hide
275
- * the name after the type).
321
+ * Identifier a binding node declares: a `name` or `pattern` field, a nested C/C++
322
+ * declarator, an assignment left-hand side, or the last identifier child (C++
323
+ * parameters hide the name after the type). Rust spells parameter and `let` names in
324
+ * the `pattern` field, where the last-identifier fallback would pick the declared type.
276
325
  */
277
326
  function bindingIdentifier(node, sup) {
278
- const named = node.childForFieldName("name");
327
+ const named = node.childForFieldName("name") ?? node.childForFieldName("pattern");
279
328
  if (named && (isReceiverNameNode(sup, named.type) || named.type === "field_identifier")) {
280
329
  return named;
281
330
  }
@@ -356,6 +405,17 @@ function containsIndex(node, index) {
356
405
  return node.startIndex <= index && node.endIndex >= index;
357
406
  }
358
407
  function constructorNameNode(node, sup) {
408
+ // A dedicated `type` field (C#'s `object_creation_expression`, C/C++'s `new_expression`) can
409
+ // wrap a qualified (`Outer.Inner`) or generic (`Box<int>`) shape the fallback scan below never
410
+ // matches; a language without that field (TS/PHP's `constructor` field, or none) falls through.
411
+ const typeField = node.childForFieldName("type");
412
+ if (typeField) {
413
+ if (isMemberAccessNode(sup, typeField))
414
+ return typeField;
415
+ const unwrapped = unwrapNamedType(typeField, sup);
416
+ if (unwrapped)
417
+ return unwrapped;
418
+ }
359
419
  const constructor = node.childForFieldName("constructor") ?? node.child(0);
360
420
  if (constructor && isReceiverNameNode(sup, constructor.type)) {
361
421
  return constructor;
@@ -367,14 +427,85 @@ function constructorNameNode(node, sup) {
367
427
  }
368
428
  return null;
369
429
  }
430
+ const PHP_OBJECT_CREATION_KEYWORDS = {
431
+ self: "self",
432
+ static: "static",
433
+ parent: "parent",
434
+ };
435
+ export function phpObjectCreationKeyword(node, source, sup) {
436
+ if (sup.id !== "php")
437
+ return null;
438
+ let creation = node;
439
+ while (creation && creation.type !== "object_creation_expression") {
440
+ if (creation.type === "method_declaration" ||
441
+ creation.type === "function_definition" ||
442
+ creation.type === "class_declaration") {
443
+ return null;
444
+ }
445
+ creation = creation.parent;
446
+ }
447
+ if (!creation)
448
+ return null;
449
+ const nameNode = constructorNameNode(creation, sup);
450
+ if (!nameNode)
451
+ return null;
452
+ const onName = node === creation || (node.startIndex >= nameNode.startIndex && node.endIndex <= nameNode.endIndex);
453
+ if (!onName)
454
+ return null;
455
+ const keyword = PHP_OBJECT_CREATION_KEYWORDS[foldPhpIdentifierCase(sliceText(nameNode, source))];
456
+ if (!keyword)
457
+ return null;
458
+ const container = nearestMemberContainer(creation);
459
+ const classNode = container?.type === "class_declaration" ? container : null;
460
+ return { keyword, nameNode, classNode };
461
+ }
462
+ /** Members-declaring symbol whose name node is this container's name, when that match is unique. */
463
+ export function memberContainerDef(mod, container) {
464
+ const nameNode = container.childForFieldName("name");
465
+ if (!nameNode)
466
+ return null;
467
+ const matches = mod.locals.filter((local) => declaresMembers(local) && local.range.start.index === nameNode.startIndex);
468
+ return matches.length === 1 ? (matches[0] ?? null) : null;
469
+ }
370
470
  function rubyNewReceiverNameNode(node, source, sup) {
371
471
  if (!new RegExp(String.raw `^(?:${RUBY_CONSTANT_SOURCE})\.new$`, "u").test(sliceText(node, source)))
372
472
  return null;
373
473
  return node.namedChildren.find((child) => isReceiverNameNode(sup, child.type) || child.type === "constant") ?? null;
374
474
  }
475
+ const NULLISH_TYPE_TEXT = new Set(["undefined", "null", "void"]);
476
+ /**
477
+ * The `typeof import("spec")` type query a TypeScript annotation names, ignoring `| undefined` and
478
+ * `| null`: such a binding holds the module namespace of `spec`.
479
+ */
480
+ export function typescriptImportTypeQuery(annotation) {
481
+ let current = annotation;
482
+ while (current?.type === "type_annotation")
483
+ current = current.namedChildren[0] ?? null;
484
+ if (current?.type === "union_type") {
485
+ const members = current.namedChildren.filter((member) => !NULLISH_TYPE_TEXT.has(member.text.trim()));
486
+ current = members.length === 1 ? members[0] : null;
487
+ }
488
+ if (current?.type !== "type_query")
489
+ return null;
490
+ const call = current.namedChildren[0];
491
+ return call?.type === "call_expression" && call.childForFieldName("function")?.type === "import" ? current : null;
492
+ }
493
+ /** The module specifier of a `typeof import("spec")` type query. */
494
+ export function importTypeQuerySpecifier(typeQuery) {
495
+ const argument = typeQuery.namedChildren[0]?.childForFieldName("arguments")?.namedChildren[0];
496
+ if (argument?.type !== "string")
497
+ return null;
498
+ return argument.namedChildren.find((child) => child.type === "string_fragment")?.text ?? null;
499
+ }
375
500
  export function unwrapNamedType(node, sup) {
376
501
  let current = node;
377
502
  while (current) {
503
+ if (current.type === "union_type") {
504
+ // `Store | undefined` and `Store | null` still prove `Store` for a member call.
505
+ const members = current.namedChildren.filter((member) => !NULLISH_TYPE_TEXT.has(member.text.trim()));
506
+ current = members.length === 1 ? members[0] : null;
507
+ continue;
508
+ }
378
509
  if (current.type === "type_annotation" ||
379
510
  current.type === "named_type" ||
380
511
  current.type === "user_type" ||
@@ -391,6 +522,16 @@ export function unwrapNamedType(node, sup) {
391
522
  current = current.childForFieldName("type") ?? current.namedChildren[0] ?? null;
392
523
  continue;
393
524
  }
525
+ if (current.type === "qualified_name") {
526
+ // C#'s `Outer.Inner` names the nested type by its last segment. PHP also parses a
527
+ // namespaced name as `qualified_name`, but as one flat token with no `name` field, so an
528
+ // absent field leaves `current` as that whole node instead of nulling the result out.
529
+ const segment = current.childForFieldName("name");
530
+ if (!segment)
531
+ break;
532
+ current = segment;
533
+ continue;
534
+ }
394
535
  break;
395
536
  }
396
537
  if (!current)
@@ -400,6 +541,34 @@ export function unwrapNamedType(node, sup) {
400
541
  }
401
542
  return null;
402
543
  }
544
+ /**
545
+ * Kotlin extension-function receiver type, e.g. `Widget` in `fun Widget.describe(): String`.
546
+ * The receiver type is an unfielded `user_type` positioned before the function's own `name`;
547
+ * an ordinary function or class member declares no such node.
548
+ */
549
+ export function kotlinExtensionReceiverTypeNode(node, sup) {
550
+ if (node.type !== "function_declaration")
551
+ return null;
552
+ const nameNode = node.childForFieldName("name");
553
+ if (!nameNode)
554
+ return null;
555
+ const receiverType = node.namedChildren.find((child) => child.startIndex < nameNode.startIndex && (child.type === "user_type" || child.type === "nullable_type"));
556
+ return receiverType ? unwrapNamedType(receiverType, sup) : null;
557
+ }
558
+ /**
559
+ * Self-type name node of a Rust `impl` block: `Circle` in `impl Circle`, `impl Shape for
560
+ * Circle`, `impl<T> Box<T>`, or `impl Shape for &Circle`. The `type` field always names the
561
+ * self type (the `trait` field names the implemented trait), and `unwrapNamedType` strips
562
+ * generic and reference wrappers down to the base type identifier. Shared by member
563
+ * ownership in the detailed graph and receiver-member navigation so both attribute an
564
+ * impl method to the same owner.
565
+ */
566
+ export function rustImplSelfTypeNode(implItem, sup) {
567
+ if (implItem.type !== "impl_item")
568
+ return null;
569
+ const typeNode = implItem.childForFieldName("type");
570
+ return typeNode ? unwrapNamedType(typeNode, sup) : null;
571
+ }
403
572
  function capitalizedCallTypeName(expr, source, sup) {
404
573
  if (expr.type !== "call" && expr.type !== "call_expression")
405
574
  return null;
@@ -428,11 +597,21 @@ function compositeLiteralTypeName(expr, source, sup) {
428
597
  return null;
429
598
  current = operand;
430
599
  }
431
- if (current.type !== "composite_literal" && current.type !== "struct_initializer")
600
+ // tree-sitter-go composite_literal, tree-sitter-zig struct_initializer, tree-sitter-rust
601
+ // struct_expression: `Type { field: value }` / `Type{ .field = value }`.
602
+ if (current.type !== "composite_literal" &&
603
+ current.type !== "struct_initializer" &&
604
+ current.type !== "struct_expression") {
432
605
  return null;
606
+ }
433
607
  const typeNode = current.childForFieldName("type") ??
434
- current.namedChildren.find((child) => child.type === "type_identifier" || child.type === "identifier") ??
608
+ current.namedChildren.find((child) => child.type === "type_identifier" ||
609
+ child.type === "identifier" ||
610
+ child.type === sup.nodeTypes.memberExpression) ??
435
611
  null;
612
+ // A qualified literal type (Zig `ns.Struct{...}`) has no dedicated `type` field and is not a
613
+ // simple named type either; `unwrapNamedType` rejects it, so the member-access node itself is
614
+ // kept so the caller can resolve it the same way as any other qualified expression.
436
615
  return typeNode ? (unwrapNamedType(typeNode, sup) ?? typeNode) : null;
437
616
  }
438
617
  /**
@@ -491,13 +670,13 @@ function bindingValueExpression(node) {
491
670
  function declaredTypeNameNode(node, sup) {
492
671
  const typeField = node.childForFieldName("type");
493
672
  if (typeField)
494
- return unwrapNamedType(typeField, sup);
673
+ return unwrapNamedType(typeField, sup) ?? typeField;
495
674
  const typedChild = node.namedChildren.find((child) => child.type === "named_type" ||
496
675
  child.type === "user_type" ||
497
676
  child.type === "type_annotation" ||
498
677
  child.type === "type");
499
678
  if (typedChild)
500
- return unwrapNamedType(typedChild, sup);
679
+ return unwrapNamedType(typedChild, sup) ?? typedChild;
501
680
  const ids = node.namedChildren.filter((child) => isReceiverNameNode(sup, child.type) || child.type === "type_identifier" || child.type === "name");
502
681
  if (ids.length >= 2)
503
682
  return unwrapNamedType(ids[0], sup);
@@ -575,6 +754,10 @@ function bindingProof(node, receiverName, source, sup) {
575
754
  return { status: "none" };
576
755
  if (!bindingDeclaresReceiverName(node, receiverName, source, sup))
577
756
  return { status: "none" };
757
+ const annotation = isJsTsLanguage(sup.id) ? node.childForFieldName("type") : null;
758
+ const annotated = annotation ? (unwrapNamedType(annotation, sup) ?? typescriptImportTypeQuery(annotation)) : null;
759
+ if (annotated)
760
+ return { status: "declared", node: annotated };
578
761
  const typeNode = constructionTypeFromBinding(node, receiverName, source, sup);
579
762
  if (typeNode)
580
763
  return { status: "type", node: typeNode };
@@ -586,12 +769,21 @@ function isSkippableBindingContainer(node, receiver) {
586
769
  function findPriorConstructorInContainer(node, receiver, receiverName, source, sup) {
587
770
  let constructor = null;
588
771
  let sawUnproven = false;
772
+ let declared = false;
589
773
  const visit = (current) => {
590
774
  if (current.startIndex >= receiver.startIndex)
591
775
  return true;
592
776
  if (current !== node && isSkippableBindingContainer(current, receiver))
593
777
  return true;
594
778
  const proof = bindingProof(current, receiverName, source, sup);
779
+ if (proof.status === "declared") {
780
+ constructor = proof.node;
781
+ declared = true;
782
+ return true;
783
+ }
784
+ // A later assignment to an annotated binding cannot change its declared type.
785
+ if (declared && proof.status !== "none")
786
+ return true;
595
787
  if (proof.status === "unproven") {
596
788
  if (constructor) {
597
789
  constructor = null;
@@ -627,7 +819,10 @@ function bindingContainerDeclaresNameBefore(node, receiver, receiverName, source
627
819
  return false;
628
820
  if (current !== node && isSkippableBindingContainer(current, receiver))
629
821
  return false;
630
- if (BINDING_DECLARATION_TYPES.has(current.type) &&
822
+ // A JavaScript/TypeScript assignment updates an existing binding; it declares nothing.
823
+ const declaresNewBinding = !(isJsTsLanguage(sup.id) && current.type === "assignment_expression");
824
+ if (declaresNewBinding &&
825
+ BINDING_DECLARATION_TYPES.has(current.type) &&
631
826
  bindingDeclaresReceiverName(current, receiverName, source, sup)) {
632
827
  return true;
633
828
  }
@@ -662,33 +857,290 @@ function findVisiblePriorConstructor(receiver, receiverName, source, sup) {
662
857
  * Resolves the node naming the type a receiver expression was constructed from, or
663
858
  * null when no constructor is proven for it. Shared with detailed symbol-graph call
664
859
  * extraction so `goto` and resolved `calls` edges accept the same receiver forms.
860
+ * A bare name is unit-struct construction only while no local binding of the same
861
+ * name shadows it; a shadowing binding is a value whose type must come from the
862
+ * binding, never from the type the name also spells.
665
863
  */
666
864
  export function receiverConstructorExpression(obj, source, sup) {
667
865
  const direct = constructionTypeName(obj, source, sup);
668
- if (direct)
669
- return direct;
670
866
  if (!isReceiverNameNode(sup, obj.type))
671
- return null;
867
+ return direct;
672
868
  const receiverName = sliceText(obj, source);
869
+ if (direct && !bindsLocalValue(obj, receiverName, source, sup))
870
+ return direct;
673
871
  return findVisiblePriorConstructor(obj, receiverName, source, sup);
674
872
  }
675
- function ownTypeMemberScope(receiverName) {
676
- return INSTANCE_RECEIVER_KEYWORDS.has(receiverName) ? "instance" : "any";
873
+ /** Identifier segments in a C++ qualified name, excluding template arguments. */
874
+ export function cppQualifiedNameSegments(node, source) {
875
+ return cppQualifiedTextSegments(sliceText(node, source));
876
+ }
877
+ /** `::`-separated segments of a C++ qualified name, ignoring `::` inside template arguments and dropping them. */
878
+ export function cppQualifiedTextSegments(text) {
879
+ const segments = [];
880
+ let segmentStart = 0;
881
+ let templateDepth = 0;
882
+ for (let index = 0; index < text.length; index += 1) {
883
+ const char = text[index];
884
+ if (char === "<") {
885
+ templateDepth += 1;
886
+ continue;
887
+ }
888
+ if (char === ">") {
889
+ templateDepth = Math.max(0, templateDepth - 1);
890
+ continue;
891
+ }
892
+ if (char !== ":" || text[index + 1] !== ":" || templateDepth !== 0)
893
+ continue;
894
+ const segment = text.slice(segmentStart, index).trim();
895
+ if (segment)
896
+ segments.push(segment.replace(/<.*$/u, "").trim());
897
+ segmentStart = index + 2;
898
+ index += 1;
899
+ }
900
+ const finalSegment = text.slice(segmentStart).trim();
901
+ if (finalSegment)
902
+ segments.push(finalSegment.replace(/<.*$/u, "").trim());
903
+ return segments.filter(Boolean);
904
+ }
905
+ /** Exact namespace/type path that qualifies a C++ out-of-line function definition. */
906
+ export function cppOutOfLineOwnerPath(node, source, sup) {
907
+ if (sup.id !== "cpp")
908
+ return null;
909
+ let current = node;
910
+ while (current) {
911
+ if (current.type === "function_definition") {
912
+ let declarator = current.childForFieldName("declarator");
913
+ while (declarator) {
914
+ if (declarator.type === "qualified_identifier") {
915
+ const path = cppQualifiedNameSegments(declarator, source);
916
+ path.pop();
917
+ if (!sliceText(declarator, source).trimStart().startsWith("::")) {
918
+ const namespaces = [];
919
+ let parent = current.parent;
920
+ while (parent) {
921
+ if (parent.type === "namespace_definition") {
922
+ const name = parent.childForFieldName("name");
923
+ if (name)
924
+ namespaces.push(cppQualifiedNameSegments(name, source));
925
+ }
926
+ parent = parent.parent;
927
+ }
928
+ for (const namespace of namespaces.reverse())
929
+ path.unshift(...namespace);
930
+ }
931
+ return path.length ? path : null;
932
+ }
933
+ const nested = declarator.childForFieldName("declarator") ?? declarator.namedChildren.at(-1);
934
+ if (!nested || nested.id === declarator.id)
935
+ break;
936
+ declarator = nested;
937
+ }
938
+ return null;
939
+ }
940
+ current = current.parent;
941
+ }
942
+ return null;
943
+ }
944
+ /** Unqualified member name from a C++ out-of-line function definition. */
945
+ export function cppOutOfLineMemberName(node, source, sup) {
946
+ if (sup.id !== "cpp")
947
+ return null;
948
+ let current = node;
949
+ while (current) {
950
+ if (current.type === "function_definition") {
951
+ const nameNode = cppFunctionDeclaratorName(current, sup);
952
+ return nameNode ? sliceText(nameNode, source) : null;
953
+ }
954
+ current = current.parent;
955
+ }
956
+ return null;
957
+ }
958
+ function sameSyntaxNode(left, right) {
959
+ if (!left)
960
+ return false;
961
+ if (left.id !== undefined && right.id !== undefined)
962
+ return left.id === right.id;
963
+ return left.type === right.type && left.startIndex === right.startIndex && left.endIndex === right.endIndex;
964
+ }
965
+ function cppFunctionDeclaratorName(node, sup) {
966
+ const functionDeclarator = node.type === "function_declarator" ? node : findFirstNodeByType(node, "function_declarator");
967
+ let current = functionDeclarator?.childForFieldName("declarator") ?? null;
968
+ while (current) {
969
+ if (isIdentifierType(sup, current.type) || current.type === "operator_name" || current.type === "destructor_name") {
970
+ return current;
971
+ }
972
+ let name = current.childForFieldName("name");
973
+ while (name?.childForFieldName("name"))
974
+ name = name.childForFieldName("name");
975
+ if (name &&
976
+ (isIdentifierType(sup, name.type) || name.type === "operator_name" || name.type === "destructor_name")) {
977
+ return name;
978
+ }
979
+ const nested = current.childForFieldName("declarator");
980
+ if (!nested || nested.id === current.id)
981
+ return null;
982
+ current = nested;
983
+ }
984
+ return null;
985
+ }
986
+ function cppMemberArityFromAncestor(node) {
987
+ let current = node;
988
+ while (current) {
989
+ const arity = declarationMemberArity(current, "cpp");
990
+ if (arity !== undefined)
991
+ return arity;
992
+ current = current.parent;
993
+ }
994
+ return undefined;
995
+ }
996
+ function collectCppMemberDeclarations(node, ownerNode, localName, ownerSource, sup, expectedArity, out) {
997
+ if ((node.type === "field_declaration" || node.type === "declaration") &&
998
+ sameSyntaxNode(nearestMemberContainer(node), ownerNode)) {
999
+ const nameNode = cppFunctionDeclaratorName(node, sup);
1000
+ if (nameNode &&
1001
+ sup.normalizeIdentifier(sliceText(nameNode, ownerSource)) === sup.normalizeIdentifier(localName) &&
1002
+ declarationMemberArity(node, sup.id) === expectedArity) {
1003
+ out.push({ node, nameNode });
1004
+ }
1005
+ return;
1006
+ }
1007
+ for (const child of node.namedChildren) {
1008
+ collectCppMemberDeclarations(child, ownerNode, localName, ownerSource, sup, expectedArity, out);
1009
+ }
1010
+ }
1011
+ /**
1012
+ * Finds the unique in-class declaration corresponding to a C++ out-of-line member
1013
+ * definition. Name and arity must both match so overloads do not collapse.
1014
+ */
1015
+ export function cppOutOfLineMemberDeclarationNode(definitionNameNode, localName, ownerNameNode, ownerSource, sup) {
1016
+ if (sup.id !== "cpp")
1017
+ return null;
1018
+ const ownerNode = nearestMemberContainer(ownerNameNode);
1019
+ if (!ownerNode)
1020
+ return null;
1021
+ const expectedArity = cppMemberArityFromAncestor(definitionNameNode);
1022
+ const declarations = [];
1023
+ collectCppMemberDeclarations(ownerNode, ownerNode, localName, ownerSource, sup, expectedArity, declarations);
1024
+ return declarations.length === 1 ? declarations[0] : null;
1025
+ }
1026
+ export function nodeDeclaresStatic(node, source) {
1027
+ if (node.type === "static" || node.type === "static_modifier")
1028
+ return true;
1029
+ if (node.type === "storage_class_specifier" || node.type === "modifier" || node.type === "property_modifier") {
1030
+ return sliceText(node, source).trim() === "static";
1031
+ }
1032
+ if (node.type === "class") {
1033
+ const parentType = node.parent?.type;
1034
+ return (parentType === "function_declaration" ||
1035
+ parentType === "property_declaration" ||
1036
+ parentType === "subscript_declaration");
1037
+ }
1038
+ if (node.type === "modifiers") {
1039
+ for (let childIndex = 0;; childIndex += 1) {
1040
+ const child = node.child(childIndex);
1041
+ if (!child)
1042
+ break;
1043
+ if (nodeDeclaresStatic(child, source))
1044
+ return true;
1045
+ }
1046
+ }
1047
+ return false;
1048
+ }
1049
+ export function declarationNodeIsStatic(node, source) {
1050
+ if (nodeDeclaresStatic(node, source))
1051
+ return true;
1052
+ for (let childIndex = 0;; childIndex += 1) {
1053
+ const child = node.child(childIndex);
1054
+ if (!child)
1055
+ return false;
1056
+ if (nodeDeclaresStatic(child, source))
1057
+ return true;
1058
+ }
1059
+ }
1060
+ export function nodeInStaticMemberContext(node, source) {
1061
+ const container = nearestMemberContainer(node);
1062
+ if (!container)
1063
+ return false;
1064
+ let current = node;
1065
+ while (current && current !== container) {
1066
+ if (declarationNodeIsStatic(current, source))
1067
+ return true;
1068
+ current = current.parent;
1069
+ }
1070
+ return false;
1071
+ }
1072
+ const JS_TS_DYNAMIC_THIS_FUNCTION_TYPES = {
1073
+ function: true,
1074
+ function_declaration: true,
1075
+ function_expression: true,
1076
+ generator_function: true,
1077
+ generator_function_declaration: true,
1078
+ method_definition: true,
1079
+ };
1080
+ /**
1081
+ * Ordinary JS/TS functions and object-literal methods own `this`/`super`; arrows preserve the
1082
+ * enclosing class member's receiver. Crossing one of those dynamic boundaries makes the class
1083
+ * receiver unproven.
1084
+ */
1085
+ export function keywordReceiverCrossesDynamicBoundary(sup, node) {
1086
+ if (!isJsTsLanguage(sup.id))
1087
+ return false;
1088
+ const container = nearestMemberContainer(node);
1089
+ if (!container)
1090
+ return false;
1091
+ let current = node.parent;
1092
+ while (current && current !== container) {
1093
+ if (JS_TS_DYNAMIC_THIS_FUNCTION_TYPES[current.type]) {
1094
+ const directlyOwnedClassMethod = current.type === "method_definition" &&
1095
+ current.parent?.type === "class_body" &&
1096
+ current.parent.parent?.startIndex === container.startIndex;
1097
+ return !directlyOwnedClassMethod;
1098
+ }
1099
+ current = current.parent;
1100
+ }
1101
+ return false;
1102
+ }
1103
+ export function keywordReceiverMemberScope(sup, receiverName, node, source) {
1104
+ if (!hasStaticMemberDistinction(sup.id))
1105
+ return "any";
1106
+ if (nodeInStaticMemberContext(node, source))
1107
+ return "static";
1108
+ const kind = keywordReceiverKind(sup.id, receiverName);
1109
+ if (kind === "supertype")
1110
+ return "instance";
1111
+ return ownReceiverMemberScope(sup.id, receiverName) ?? "any";
1112
+ }
1113
+ /**
1114
+ * The leftmost identifier of a C# chain of plain identifiers joined by `.` (`P` in `P.Inner.Mix`),
1115
+ * or `null` when `node` has a call, index, keyword, or type arguments. When that identifier binds
1116
+ * nothing, the chain is a namespace-qualified type name.
1117
+ */
1118
+ export function csharpDottedNameRoot(node) {
1119
+ if (node.type === "identifier")
1120
+ return node;
1121
+ if (node.type !== "member_access_expression")
1122
+ return null;
1123
+ const object = node.childForFieldName("expression");
1124
+ if (!object || node.childForFieldName("name")?.type !== "identifier")
1125
+ return null;
1126
+ return csharpDottedNameRoot(object);
677
1127
  }
678
1128
  /**
679
1129
  * Classifies a receiver as the declaring type, a supertype, or a named/constructed type.
680
1130
  * Returns null when the receiver cannot be proven.
681
1131
  * Named-local constructor lookup is memoized per enclosing function and receiver text.
682
1132
  */
683
- export function classifyReceiver(sup, receiver, source, proofCache, cacheScope, accessNode) {
684
- const keywords = RECEIVER_KEYWORDS[sup.id];
685
- const text = sliceText(receiver, source).trim();
1133
+ export function classifyReceiver(sup, receiver, source, proofCache, cacheScope, accessNode, hasLexicalBinding) {
1134
+ const text = receiverKeywordText(sup, receiver, source, hasLexicalBinding);
686
1135
  if (!text)
687
1136
  return null;
688
- if (keywords?.own.includes(text))
689
- return { kind: "own-type", memberScope: ownTypeMemberScope(text) };
690
- if (keywords?.supertype.includes(text))
691
- return { kind: "supertype", memberScope: "any" };
1137
+ const keywordKind = keywordReceiverKind(sup.id, text);
1138
+ if (keywordKind) {
1139
+ if (keywordReceiverCrossesDynamicBoundary(sup, accessNode))
1140
+ return null;
1141
+ const memberScope = keywordReceiverMemberScope(sup, text, accessNode, source);
1142
+ return keywordKind === "own" ? { kind: "own-type", memberScope } : { kind: "supertype", memberScope };
1143
+ }
692
1144
  const receiverIsName = isReceiverNameNode(sup, receiver.type);
693
1145
  const cacheKey = `${cacheScope}\u0000${text}`;
694
1146
  let proof = proofCache.get(cacheKey);
@@ -704,7 +1156,9 @@ export function classifyReceiver(sup, receiver, source, proofCache, cacheScope,
704
1156
  return {
705
1157
  kind: "named-type",
706
1158
  typeName: sliceText(proof.constructed, source),
1159
+ typeNode: proof.constructed,
707
1160
  memberScope: hasStaticMemberDistinction(sup.id) ? "instance" : "any",
1161
+ constructed: true,
708
1162
  };
709
1163
  }
710
1164
  if (!receiverIsName)
@@ -713,24 +1167,83 @@ export function classifyReceiver(sup, receiver, source, proofCache, cacheScope,
713
1167
  // `Example::shared()` would still be attributed to a colliding parameter named Example.
714
1168
  if (proof.locallyBound)
715
1169
  return null;
716
- // Dotted `Cfg.load()` is not proof: the identifier may be a value. Type-scoped `::`
717
- // is the remaining named-type proof. Ruby capitalized names are `constant` tokens
718
- // even when they name a parameter, so `constant` is not itself type proof.
1170
+ // Dotted `Cfg.load()` is not proof in languages where the identifier may be a
1171
+ // value. Type-scoped `::` is one named-type proof; C# `Box.Left()` is another
1172
+ // because a capitalized unbound name is the static type receiver. Ruby
1173
+ // capitalized names are `constant` tokens even when they name a parameter, so
1174
+ // `constant` is not itself type proof.
719
1175
  const property = getMemberAccessParts(sup, accessNode).property;
720
1176
  const between = property ? source.slice(receiver.endIndex, property.startIndex) : "";
721
1177
  const typeScoped = TYPE_SCOPED_ACCESS_TYPES[accessNode.type] === true || between.includes("::");
722
- if (receiver.type !== "type_identifier" && !typeScoped)
723
- return null;
1178
+ if (receiver.type !== "type_identifier" && !typeScoped) {
1179
+ // A capitalized bare name is type proof where construction already types
1180
+ // `Box()` as a Box, and for static type-name receivers (`Box.Left()`).
1181
+ // The named type must still resolve to a members-declaring definition
1182
+ // before any call edge is recorded, so a name alone never invents a target.
1183
+ if (!capitalizedTypeReceiverName(sup, receiver, text))
1184
+ return null;
1185
+ return {
1186
+ kind: "named-type",
1187
+ typeName: text,
1188
+ typeNode: receiver,
1189
+ memberScope: UNBOUND_INSTANCE_CALL_LANGUAGE_IDS[sup.id] ? "any" : "static",
1190
+ };
1191
+ }
724
1192
  return {
725
1193
  kind: "named-type",
726
1194
  typeName: text,
1195
+ typeNode: receiver,
727
1196
  memberScope: hasStaticMemberDistinction(sup.id) && typeScoped ? "static" : "any",
728
1197
  };
729
1198
  }
1199
+ /**
1200
+ * Languages whose runtime allows an instance member to be invoked through a type
1201
+ * name (`Box.instanceMethod(args)` is a real unbound call), so a type-named
1202
+ * receiver restricts nothing about static versus instance members.
1203
+ */
1204
+ const UNBOUND_INSTANCE_CALL_LANGUAGE_IDS = {
1205
+ python: true,
1206
+ };
1207
+ /**
1208
+ * Languages whose dotted type-name receivers (`Box.Left()`) name the type itself.
1209
+ * Distinct from `capitalizedCall`, which also treats `Box()` as construction.
1210
+ */
1211
+ const STATIC_TYPE_NAME_RECEIVER_LANGUAGE_IDS = {
1212
+ csharp: true,
1213
+ java: true,
1214
+ js: true,
1215
+ kotlin: true,
1216
+ ts: true,
1217
+ tsx: true,
1218
+ };
1219
+ /** Whether a receiver name is capitalized like a type in a capitalized-name language. */
1220
+ function capitalizedTypeReceiverName(sup, receiver, text) {
1221
+ if (LANGUAGE_CONSTRUCTION_FORMS[sup.id]?.capitalizedCall !== true &&
1222
+ !STATIC_TYPE_NAME_RECEIVER_LANGUAGE_IDS[sup.id]) {
1223
+ return false;
1224
+ }
1225
+ if (!isReceiverNameNode(sup, receiver.type))
1226
+ return false;
1227
+ const first = text[0];
1228
+ return !!first && first === first.toUpperCase() && first !== first.toLowerCase();
1229
+ }
730
1230
  /** Whether a resolved definition can declare callable members. */
731
1231
  export function declaresMembers(def) {
732
1232
  return def.kind === SymbolKind.Class || def.kind === SymbolKind.Interface || def.kind === SymbolKind.TypeAlias;
733
1233
  }
1234
+ /** Whether a C++ definition denotes a class, struct, or union that can own methods. */
1235
+ export function isCppMemberContainerDefinition(tree, def) {
1236
+ const position = {
1237
+ row: Math.max(0, def.range.start.line - 1),
1238
+ column: Math.max(0, def.range.start.column - 1),
1239
+ };
1240
+ const nameNode = tree.rootNode.descendantForPosition(position, position);
1241
+ const container = nearestMemberContainer(nameNode);
1242
+ if (!container || CPP_MEMBER_CONTAINER_TYPES[container.type] === undefined)
1243
+ return false;
1244
+ const containerName = container.childForFieldName("name");
1245
+ return containerName?.startPosition.row === position.row && containerName.startPosition.column === position.column;
1246
+ }
734
1247
  /** Nearest enclosing declaration that lexically owns `node` as a member. */
735
1248
  export function nearestMemberContainer(node) {
736
1249
  let current = node.parent;
@@ -741,29 +1254,168 @@ export function nearestMemberContainer(node) {
741
1254
  }
742
1255
  return null;
743
1256
  }
744
- /** Positional argument count of a call, ignoring comments. */
745
- export function callArgumentCount(callNode) {
746
- const explicit = callNode.childForFieldName("arguments") ?? callNode.childForFieldName("argument_list");
747
- let argumentNode = explicit ?? (callNode.namedChildren ?? []).find((child) => CALL_ARGUMENT_NODE_TYPES[child.type]) ?? null;
1257
+ /** Positional argument count of a call, including Kotlin/Swift trailing lambdas. */
1258
+ export function callArgumentCount(callNode, source) {
1259
+ // Kotlin wraps `pick(1) { }` in an outer call node whose only other child is the
1260
+ // callee call; the trailing lambda belongs to the inner call's argument list.
1261
+ let scope = callNode;
1262
+ for (let wrapper = trailingLambdaWrapper(scope); wrapper; wrapper = trailingLambdaWrapper(scope)) {
1263
+ scope = wrapper;
1264
+ }
1265
+ let argumentNode = callNode.childForFieldName("arguments") ??
1266
+ callNode.childForFieldName("argument_list") ??
1267
+ (callNode.namedChildren ?? []).find((child) => CALL_ARGUMENT_NODE_TYPES[child.type]) ??
1268
+ null;
748
1269
  if (argumentNode?.type === "call_suffix") {
749
1270
  argumentNode = (argumentNode.namedChildren ?? []).find((child) => child.type === "value_arguments") ?? null;
750
1271
  }
751
- if (!argumentNode)
752
- return 0;
753
- return (argumentNode.namedChildren ?? []).filter((argument) => argument.type !== "comment").length;
1272
+ if (!argumentNode) {
1273
+ // Swift allows a call suffix with only a trailing lambda (`pick { }`), and Kotlin
1274
+ // `pick { }` keeps the lambda directly on the call node: no argument list exists.
1275
+ const suffix = (callNode.namedChildren ?? []).find((child) => child.type === "call_suffix");
1276
+ return ((suffix ?? scope).namedChildren ?? []).filter((child) => TRAILING_LAMBDA_NODE_TYPES[child.type] && child.startIndex >= callNode.startIndex).length;
1277
+ }
1278
+ let count = (argumentNode.namedChildren ?? []).filter((argument) => argument.type !== "comment").length;
1279
+ const trailingEnd = argumentNode.parent?.type === "call_suffix" ? argumentNode.parent.endIndex : scope.endIndex;
1280
+ if (trailingEnd > argumentNode.endIndex && containsTrailingLambdaNode(scope, argumentNode.endIndex, trailingEnd)) {
1281
+ const trailing = countTrailingClosureArguments(source.slice(argumentNode.endIndex, trailingEnd));
1282
+ // A null scan means the trailing-closure count is unknown, so the whole call
1283
+ // arity is unknown rather than the parenthesized count alone.
1284
+ if (trailing === null)
1285
+ return null;
1286
+ count += trailing;
1287
+ }
1288
+ return count;
754
1289
  }
755
- function parseDefNodeId(id) {
756
- const indexSep = id.lastIndexOf("::");
757
- if (indexSep <= 0)
1290
+ /** Trailing lambda nodes across the supported grammars. */
1291
+ const TRAILING_LAMBDA_NODE_TYPES = {
1292
+ annotated_lambda: true, // Kotlin wraps the lambda on the outer call node.
1293
+ lambda_literal: true, // Swift keeps trailing lambdas inside `call_suffix`.
1294
+ };
1295
+ /** Call nodes that can wrap a callee call plus a Kotlin trailing lambda. */
1296
+ const TRAILING_LAMBDA_WRAPPER_TYPES = {
1297
+ call: true,
1298
+ call_expression: true,
1299
+ };
1300
+ /**
1301
+ * The outer call node attaching a trailing lambda to `node`, or null. The wrapper
1302
+ * never carries its own argument list, which distinguishes it from a chained call.
1303
+ */
1304
+ function trailingLambdaWrapper(node) {
1305
+ const parent = node.parent;
1306
+ if (!parent || !TRAILING_LAMBDA_WRAPPER_TYPES[parent.type])
758
1307
  return null;
759
- const index = Number(id.slice(indexSep + 2));
760
- if (!Number.isFinite(index))
1308
+ const children = parent.namedChildren ?? [];
1309
+ if (children.some((child) => CALL_ARGUMENT_NODE_TYPES[child.type]))
761
1310
  return null;
762
- const rest = id.slice(0, indexSep);
763
- const nameSep = rest.lastIndexOf("::");
764
- if (nameSep <= 0)
1311
+ return children.some((child) => TRAILING_LAMBDA_NODE_TYPES[child.type]) ? parent : null;
1312
+ }
1313
+ /** Whether a trailing lambda node overlaps `[start, end)` anywhere under `node`. */
1314
+ function containsTrailingLambdaNode(node, start, end) {
1315
+ for (const child of node.namedChildren ?? []) {
1316
+ if (child.endIndex <= start || child.startIndex >= end)
1317
+ continue;
1318
+ if (TRAILING_LAMBDA_NODE_TYPES[child.type])
1319
+ return true;
1320
+ if (containsTrailingLambdaNode(child, start, end))
1321
+ return true;
1322
+ }
1323
+ return false;
1324
+ }
1325
+ /** End index after a Swift trailing-closure label and its colon, or null when absent. */
1326
+ function trailingClosureLabelEnd(text, startIndex) {
1327
+ if (text[startIndex] === "`") {
1328
+ const escapedEnd = text.indexOf("`", startIndex + 1);
1329
+ if (escapedEnd < 0 || text[escapedEnd + 1] !== ":")
1330
+ return null;
1331
+ return escapedEnd + 2;
1332
+ }
1333
+ const identifier = /^[_\p{ID_Start}][_\p{ID_Continue}]*/u.exec(text.slice(startIndex));
1334
+ if (!identifier)
765
1335
  return null;
766
- return { file: rest.slice(0, nameSep), name: rest.slice(nameSep + 2), index };
1336
+ const colonIndex = startIndex + identifier[0].length;
1337
+ return text[colonIndex] === ":" ? colonIndex + 1 : null;
1338
+ }
1339
+ /**
1340
+ * Counts the top-level `{ ... }` blocks in trailing call text, one per trailing
1341
+ * closure, or null when the text is not a well-formed trailing-closure run.
1342
+ * Shared with call-compatibility extraction so both paths count identically.
1343
+ */
1344
+ export function countTrailingClosureArguments(text) {
1345
+ let startIndex = 0;
1346
+ let count = 0;
1347
+ while (startIndex < text.length) {
1348
+ while (/\s/.test(text[startIndex] ?? "")) {
1349
+ startIndex += 1;
1350
+ }
1351
+ if (startIndex === text.length) {
1352
+ return count;
1353
+ }
1354
+ if (text[startIndex] !== "{") {
1355
+ const labelEnd = trailingClosureLabelEnd(text, startIndex);
1356
+ if (labelEnd === null)
1357
+ return null;
1358
+ startIndex = labelEnd;
1359
+ while (/\s/.test(text[startIndex] ?? "")) {
1360
+ startIndex += 1;
1361
+ }
1362
+ if (text[startIndex] !== "{")
1363
+ return null;
1364
+ }
1365
+ let braceDepth = 0;
1366
+ let quote = null;
1367
+ let escaped = false;
1368
+ let closed = false;
1369
+ for (let index = startIndex; index < text.length; index += 1) {
1370
+ const char = text[index];
1371
+ if (quote) {
1372
+ if (escaped) {
1373
+ escaped = false;
1374
+ continue;
1375
+ }
1376
+ if (char === "\\") {
1377
+ escaped = true;
1378
+ continue;
1379
+ }
1380
+ if (char === quote) {
1381
+ quote = null;
1382
+ }
1383
+ continue;
1384
+ }
1385
+ const commentEnd = findCommentEnd(text, index);
1386
+ if (commentEnd !== null) {
1387
+ if (commentEnd < 0) {
1388
+ return null;
1389
+ }
1390
+ index = commentEnd - 1;
1391
+ continue;
1392
+ }
1393
+ if (char === '"' || char === "'" || char === "`") {
1394
+ quote = char;
1395
+ continue;
1396
+ }
1397
+ if (char === "{") {
1398
+ braceDepth += 1;
1399
+ continue;
1400
+ }
1401
+ if (char === "}") {
1402
+ braceDepth -= 1;
1403
+ if (braceDepth < 0) {
1404
+ return null;
1405
+ }
1406
+ if (!braceDepth) {
1407
+ startIndex = index + 1;
1408
+ count += 1;
1409
+ closed = true;
1410
+ break;
1411
+ }
1412
+ }
1413
+ }
1414
+ if (!closed) {
1415
+ return null;
1416
+ }
1417
+ }
1418
+ return count;
767
1419
  }
768
1420
  function loadSource(file, cache) {
769
1421
  const cached = cache.get(file);
@@ -779,20 +1431,6 @@ function loadSource(file, cache) {
779
1431
  return "";
780
1432
  }
781
1433
  }
782
- function memberIdLooksStatic(memberId, sourceCache) {
783
- const parsed = parseDefNodeId(memberId);
784
- if (!parsed)
785
- return false;
786
- const source = loadSource(parsed.file, sourceCache);
787
- if (!source)
788
- return false;
789
- const beforeBrace = source.lastIndexOf("{", parsed.index);
790
- const beforeSemi = source.lastIndexOf(";", parsed.index);
791
- const beforeClose = source.lastIndexOf("}", parsed.index);
792
- const declStart = Math.max(beforeBrace, beforeSemi, beforeClose);
793
- const prefix = source.slice(declStart + 1, parsed.index);
794
- return /(?:^|[^\w$])static(?:$|[^\w$])/.test(prefix) || /(?:^|[^\w$])def\s+self\s*\./.test(prefix);
795
- }
796
1434
  function inferCallMemberScope(site, sourceCache) {
797
1435
  const source = loadSource(site.file, sourceCache);
798
1436
  if (!source)
@@ -807,22 +1445,25 @@ function inferCallMemberScope(site, sourceCache) {
807
1445
  return "instance";
808
1446
  if (/\)\s*\.\s*$/.test(before))
809
1447
  return "instance";
810
- // HeritageEdges does not yet copy classifyReceiver.memberScope onto candidates.
811
- // A lowercase dotted receiver is an instance value (`c.StaticMethod()`), not a type.
1448
+ // Older serialized candidates lack memberScope. Infer the common dotted-receiver cases.
812
1449
  if (/(?:^|[^A-Za-z0-9_$])[a-z_][A-Za-z0-9_]*\s*\.\s*$/.test(before))
813
1450
  return "instance";
814
1451
  if (/(?:^|[^A-Za-z0-9_$])[A-Z][A-Za-z0-9_]*\s*\.\s*$/.test(before))
815
1452
  return "static";
816
1453
  return "any";
817
1454
  }
1455
+ function callSiteKey(callerId, site) {
1456
+ const { start, end } = site.range;
1457
+ return `${callerId}\u0000${site.file}\u0000${start.line}:${start.column}:${start.index ?? ""}-${end.line}:${end.column}:${end.index ?? ""}`;
1458
+ }
818
1459
  /**
819
1460
  * Records proven `calls` edges for deferred receiver invocations.
820
1461
  * Ambiguous names at a level stop the walk.
821
1462
  * `super`/`base`/`parent` follow class `extends` ancestors only.
822
1463
  */
823
- export function emitReceiverCallEdges(graph, candidates, recordEdge) {
1464
+ export function emitReceiverCallEdges(graph, candidates, recordEdge, memberScopes = new Map(), nodeAliases = new Map(), memberArities = new Map(), ownerAnchors = new Map(), accessibleMembers = new Map(), fileHiddenMemberIds = new Set(), acceptsCallTarget) {
824
1465
  if (!candidates.length)
825
- return;
1466
+ return [];
826
1467
  const membersByOwner = new Map();
827
1468
  const ownerByMember = new Map();
828
1469
  const supertypesByOwner = new Map();
@@ -847,27 +1488,75 @@ export function emitReceiverCallEdges(graph, candidates, recordEdge) {
847
1488
  if (label === "extends")
848
1489
  pushUnique(classAncestorsByOwner, edge.from, edge.to);
849
1490
  }
1491
+ // Receiver-local access does not change the nominal type's membership edges.
1492
+ for (const [ownerId, memberIds] of accessibleMembers) {
1493
+ for (const memberId of memberIds)
1494
+ pushUnique(membersByOwner, ownerId, memberId);
1495
+ }
850
1496
  const nextOwners = (ownerId, viaSupertypes) => {
851
1497
  if (!viaSupertypes)
852
1498
  return supertypesByOwner.get(ownerId) ?? [];
853
1499
  return (classAncestorsByOwner.get(ownerId) ?? []).filter((id) => graph.nodes.get(id)?.kind === "class");
854
1500
  };
1501
+ const callTargetsBySite = new Map();
1502
+ for (const edge of graph.edges) {
1503
+ if (edge.label !== "calls" || !edge.site)
1504
+ continue;
1505
+ const key = callSiteKey(edge.from, edge.site);
1506
+ const targets = callTargetsBySite.get(key);
1507
+ if (targets)
1508
+ targets.add(edge.to);
1509
+ else
1510
+ callTargetsBySite.set(key, new Set([edge.to]));
1511
+ }
1512
+ const rejectedCallSites = new Set();
855
1513
  const sourceCache = new Map();
856
1514
  for (const candidate of candidates) {
857
- const owner = candidate.ownerId ?? ownerByMember.get(candidate.callerId);
858
- if (!owner)
1515
+ const siteKey = callSiteKey(candidate.callerId, candidate.site);
1516
+ const existingTargets = callTargetsBySite.get(siteKey) ?? new Set();
1517
+ if (existingTargets.size > 1) {
1518
+ rejectedCallSites.add(siteKey);
1519
+ continue;
1520
+ }
1521
+ const rawOwner = candidate.ownerId ?? ownerByMember.get(candidate.callerId);
1522
+ if (!rawOwner)
859
1523
  continue;
1524
+ // Shared-owner anchors redirect Swift extension and C# partial owners to the
1525
+ // coalesced type identity so lookup starts on the whole member set.
1526
+ const owner = ownerAnchors.get(rawOwner) ?? rawOwner;
860
1527
  const memberScope = candidate.memberScope ?? inferCallMemberScope(candidate.site, sourceCache);
861
1528
  let level = candidate.viaSupertypes ? nextOwners(owner, true) : [owner];
862
1529
  const visited = new Set(level);
1530
+ let receiverDisposition = "none";
863
1531
  for (let depth = 0; depth < MAX_SUPERTYPE_DEPTH && level.length; depth += 1) {
864
- const lookup = provenMemberTarget(graph, membersByOwner, level, candidate, memberScope, sourceCache);
1532
+ const lookup = provenMemberTarget(graph, membersByOwner, level, candidate, memberScope, memberScopes, nodeAliases, memberArities, fileHiddenMemberIds, candidate.site.file);
865
1533
  if (lookup.status === "unique") {
866
- recordEdge(candidate.callerId, lookup.memberId, "calls", candidate.site);
1534
+ if (acceptsCallTarget && !acceptsCallTarget(lookup.memberId, candidate)) {
1535
+ receiverDisposition = "ambiguous";
1536
+ if (existingTargets.size)
1537
+ rejectedCallSites.add(siteKey);
1538
+ break;
1539
+ }
1540
+ receiverDisposition = "resolved";
1541
+ const combinedTargets = new Set(existingTargets);
1542
+ combinedTargets.add(lookup.memberId);
1543
+ if (combinedTargets.size === 1) {
1544
+ if (existingTargets.size === 0 && recordEdge(candidate.callerId, lookup.memberId, "calls", candidate.site)) {
1545
+ existingTargets.add(lookup.memberId);
1546
+ callTargetsBySite.set(siteKey, existingTargets);
1547
+ }
1548
+ }
1549
+ else {
1550
+ rejectedCallSites.add(siteKey);
1551
+ }
867
1552
  break;
868
1553
  }
869
- if (lookup.status === "ambiguous")
1554
+ if (lookup.status === "ambiguous") {
1555
+ receiverDisposition = "ambiguous";
1556
+ if (existingTargets.size)
1557
+ rejectedCallSites.add(siteKey);
870
1558
  break;
1559
+ }
871
1560
  const next = [];
872
1561
  for (const ownerId of level) {
873
1562
  for (const supertype of nextOwners(ownerId, candidate.viaSupertypes)) {
@@ -879,7 +1568,46 @@ export function emitReceiverCallEdges(graph, candidates, recordEdge) {
879
1568
  }
880
1569
  level = next;
881
1570
  }
1571
+ if (receiverDisposition === "none") {
1572
+ if (candidate.fallbackTargetId && !existingTargets.size) {
1573
+ recordEdge(candidate.callerId, candidate.fallbackTargetId, "calls", candidate.site);
1574
+ }
1575
+ else if (existingTargets.size) {
1576
+ rejectedCallSites.add(siteKey);
1577
+ }
1578
+ }
1579
+ }
1580
+ const removed = [];
1581
+ if (rejectedCallSites.size) {
1582
+ let writeIndex = 0;
1583
+ for (const edge of graph.edges) {
1584
+ if (edge.label === "calls" && edge.site && rejectedCallSites.has(callSiteKey(edge.from, edge.site))) {
1585
+ removed.push(edge);
1586
+ continue;
1587
+ }
1588
+ graph.edges[writeIndex] = edge;
1589
+ writeIndex += 1;
1590
+ }
1591
+ graph.edges.length = writeIndex;
1592
+ }
1593
+ return removed;
1594
+ }
1595
+ function canonicalMemberId(id, aliases) {
1596
+ let current = id;
1597
+ const seen = new Set();
1598
+ while (aliases.has(current) && !seen.has(current)) {
1599
+ seen.add(current);
1600
+ current = aliases.get(current);
882
1601
  }
1602
+ return current;
1603
+ }
1604
+ function memberArityMatches(memberId, argumentCount, memberArities) {
1605
+ if (argumentCount === null)
1606
+ return true;
1607
+ const range = memberArities.get(memberId);
1608
+ // Declaration parameter counts do not prove required/default/variadic call bounds.
1609
+ // An unknown range cannot reject a unique target or eliminate an overload.
1610
+ return !range || (argumentCount >= range.min && (range.max === null || argumentCount <= range.max));
883
1611
  }
884
1612
  /**
885
1613
  * The single callable member named by `candidate` across `owners`.
@@ -887,27 +1615,46 @@ export function emitReceiverCallEdges(graph, candidates, recordEdge) {
887
1615
  * `ambiguous` means this depth matched the name but could not prove one member,
888
1616
  * including arity ambiguity, and the walk must stop.
889
1617
  */
890
- function provenMemberTarget(graph, membersByOwner, owners, candidate, memberScope, sourceCache) {
1618
+ function provenMemberTarget(graph, membersByOwner, owners, candidate, memberScope, memberScopes, nodeAliases, memberArities, fileHiddenMemberIds, useFile) {
891
1619
  const matches = new Set();
892
1620
  for (const ownerId of owners) {
893
1621
  for (const memberId of membersByOwner.get(ownerId) ?? []) {
894
- const node = graph.nodes.get(memberId);
895
- if (!node || node.kind !== "function" || node.name !== candidate.memberName)
1622
+ const canonicalId = canonicalMemberId(memberId, nodeAliases);
1623
+ const node = graph.nodes.get(memberId) ?? graph.nodes.get(canonicalId);
1624
+ if (!node || (node.kind !== "function" && !node.callable))
896
1625
  continue;
897
- if (memberScope === "static" && !memberIdLooksStatic(memberId, sourceCache))
1626
+ if ((fileHiddenMemberIds.has(memberId) || fileHiddenMemberIds.has(canonicalId)) &&
1627
+ fileIdentityKey(node.file) !== fileIdentityKey(useFile)) {
898
1628
  continue;
899
- if (memberScope === "instance" && memberIdLooksStatic(memberId, sourceCache))
1629
+ }
1630
+ const nameMatches = candidate.caseInsensitiveMemberName
1631
+ ? foldPhpIdentifierCase(node.name) === foldPhpIdentifierCase(candidate.memberName)
1632
+ : node.name === candidate.memberName;
1633
+ if (!nameMatches)
900
1634
  continue;
901
- matches.add(memberId);
1635
+ if (candidate.goPackagePeerFiles &&
1636
+ !isGoExportedMemberName("go", node.name) &&
1637
+ !candidate.goPackagePeerFiles.has(node.file)) {
1638
+ continue;
1639
+ }
1640
+ const scope = memberScopes.get(memberId) ?? memberScopes.get(canonicalId);
1641
+ if (memberScope !== "any" && scope !== memberScope)
1642
+ continue;
1643
+ matches.add(canonicalId);
902
1644
  }
903
1645
  }
904
1646
  if (!matches.size)
905
1647
  return { status: "none" };
906
1648
  if (matches.size === 1) {
907
1649
  const [memberId] = matches;
908
- return { status: "unique", memberId: memberId };
1650
+ if (memberArityMatches(memberId, candidate.argumentCount, memberArities)) {
1651
+ return { status: "unique", memberId: memberId };
1652
+ }
1653
+ return { status: "ambiguous" };
909
1654
  }
910
- const byArity = [...matches].filter((memberId) => graph.nodes.get(memberId)?.memberArity === candidate.argumentCount);
1655
+ const byArity = candidate.argumentCount === null
1656
+ ? []
1657
+ : [...matches].filter((memberId) => memberArityMatches(memberId, candidate.argumentCount, memberArities));
911
1658
  if (byArity.length === 1)
912
1659
  return { status: "unique", memberId: byArity[0] };
913
1660
  return { status: "ambiguous" };