@lzehrung/codegraph 2.3.31 → 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 (407) 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 +42243 -29202
  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 -15
  74. package/dist/graphs/symbol-graph-detailed/receiver-calls.d.ts.map +1 -1
  75. package/dist/graphs/symbol-graph-detailed/receiver-calls.js +873 -93
  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 +4 -4
  85. package/dist/impact/call-compatibility/providers/index.d.ts.map +1 -1
  86. package/dist/impact/call-compatibility/providers/index.js +6 -6
  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 +310 -516
  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/reference-cache.d.ts.map +1 -1
  98. package/dist/impact/reference-cache.js +2 -2
  99. package/dist/impact/reference-cache.js.map +1 -1
  100. package/dist/index.d.ts +1 -1
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/index.js +1 -1
  103. package/dist/index.js.map +1 -1
  104. package/dist/indexer/ambiguous-resolution.d.ts +11 -0
  105. package/dist/indexer/ambiguous-resolution.d.ts.map +1 -0
  106. package/dist/indexer/ambiguous-resolution.js +19 -0
  107. package/dist/indexer/ambiguous-resolution.js.map +1 -0
  108. package/dist/indexer/build-cache/module-cache.d.ts +38 -0
  109. package/dist/indexer/build-cache/module-cache.d.ts.map +1 -1
  110. package/dist/indexer/build-cache/module-cache.js +151 -9
  111. package/dist/indexer/build-cache/module-cache.js.map +1 -1
  112. package/dist/indexer/build-cache/options.d.ts +16 -43
  113. package/dist/indexer/build-cache/options.d.ts.map +1 -1
  114. package/dist/indexer/build-cache/options.js +16 -43
  115. package/dist/indexer/build-cache/options.js.map +1 -1
  116. package/dist/indexer/build-cache/project-snapshot.d.ts +1 -0
  117. package/dist/indexer/build-cache/project-snapshot.d.ts.map +1 -1
  118. package/dist/indexer/build-cache/project-snapshot.js +117 -24
  119. package/dist/indexer/build-cache/project-snapshot.js.map +1 -1
  120. package/dist/indexer/build-cache/resolver-environment.d.ts.map +1 -1
  121. package/dist/indexer/build-cache/resolver-environment.js +32 -2
  122. package/dist/indexer/build-cache/resolver-environment.js.map +1 -1
  123. package/dist/indexer/build-cache.d.ts +1 -1
  124. package/dist/indexer/build-cache.d.ts.map +1 -1
  125. package/dist/indexer/build-cache.js +1 -1
  126. package/dist/indexer/build-cache.js.map +1 -1
  127. package/dist/indexer/build-index.d.ts.map +1 -1
  128. package/dist/indexer/build-index.js +593 -22
  129. package/dist/indexer/build-index.js.map +1 -1
  130. package/dist/indexer/call-hierarchy.js +1 -1
  131. package/dist/indexer/call-hierarchy.js.map +1 -1
  132. package/dist/indexer/compilation-units.d.ts +101 -0
  133. package/dist/indexer/compilation-units.d.ts.map +1 -0
  134. package/dist/indexer/compilation-units.js +605 -0
  135. package/dist/indexer/compilation-units.js.map +1 -0
  136. package/dist/indexer/cpp-callables.d.ts +20 -0
  137. package/dist/indexer/cpp-callables.d.ts.map +1 -0
  138. package/dist/indexer/cpp-callables.js +554 -0
  139. package/dist/indexer/cpp-callables.js.map +1 -0
  140. package/dist/indexer/declaration-visibility.d.ts +36 -2
  141. package/dist/indexer/declaration-visibility.d.ts.map +1 -1
  142. package/dist/indexer/declaration-visibility.js +136 -2
  143. package/dist/indexer/declaration-visibility.js.map +1 -1
  144. package/dist/indexer/expand-star-imports.d.ts.map +1 -1
  145. package/dist/indexer/expand-star-imports.js +28 -6
  146. package/dist/indexer/expand-star-imports.js.map +1 -1
  147. package/dist/indexer/import-types.d.ts +37 -0
  148. package/dist/indexer/import-types.d.ts.map +1 -1
  149. package/dist/indexer/import-types.js +24 -1
  150. package/dist/indexer/import-types.js.map +1 -1
  151. package/dist/indexer/imports/context.d.ts +11 -1
  152. package/dist/indexer/imports/context.d.ts.map +1 -1
  153. package/dist/indexer/imports/import-binding-tables.d.ts +12 -1
  154. package/dist/indexer/imports/import-binding-tables.d.ts.map +1 -1
  155. package/dist/indexer/imports/import-binding-tables.js +144 -19
  156. package/dist/indexer/imports/import-binding-tables.js.map +1 -1
  157. package/dist/indexer/imports/js-text-imports.js +4 -4
  158. package/dist/indexer/imports/js-text-imports.js.map +1 -1
  159. package/dist/indexer/imports/native-captures.d.ts.map +1 -1
  160. package/dist/indexer/imports/native-captures.js +18 -6
  161. package/dist/indexer/imports/native-captures.js.map +1 -1
  162. package/dist/indexer/imports/python.d.ts +10 -2
  163. package/dist/indexer/imports/python.d.ts.map +1 -1
  164. package/dist/indexer/imports/python.js +91 -40
  165. package/dist/indexer/imports/python.js.map +1 -1
  166. package/dist/indexer/imports/text-import-extractors.d.ts.map +1 -1
  167. package/dist/indexer/imports/text-import-extractors.js +51 -12
  168. package/dist/indexer/imports/text-import-extractors.js.map +1 -1
  169. package/dist/indexer/imports.d.ts.map +1 -1
  170. package/dist/indexer/imports.js +23 -4
  171. package/dist/indexer/imports.js.map +1 -1
  172. package/dist/indexer/incremental-plan.d.ts +33 -0
  173. package/dist/indexer/incremental-plan.d.ts.map +1 -1
  174. package/dist/indexer/incremental-plan.js +187 -1
  175. package/dist/indexer/incremental-plan.js.map +1 -1
  176. package/dist/indexer/locals-and-exports.d.ts +2 -0
  177. package/dist/indexer/locals-and-exports.d.ts.map +1 -1
  178. package/dist/indexer/locals-and-exports.js +565 -54
  179. package/dist/indexer/locals-and-exports.js.map +1 -1
  180. package/dist/indexer/name-lookup-policies/c-family.d.ts +7 -0
  181. package/dist/indexer/name-lookup-policies/c-family.d.ts.map +1 -0
  182. package/dist/indexer/name-lookup-policies/c-family.js +117 -0
  183. package/dist/indexer/name-lookup-policies/c-family.js.map +1 -0
  184. package/dist/indexer/name-lookup-policies/csharp-using-static.d.ts +11 -0
  185. package/dist/indexer/name-lookup-policies/csharp-using-static.d.ts.map +1 -0
  186. package/dist/indexer/name-lookup-policies/csharp-using-static.js +136 -0
  187. package/dist/indexer/name-lookup-policies/csharp-using-static.js.map +1 -0
  188. package/dist/indexer/name-lookup-policies/implicit-self.d.ts +10 -0
  189. package/dist/indexer/name-lookup-policies/implicit-self.d.ts.map +1 -0
  190. package/dist/indexer/name-lookup-policies/implicit-self.js +113 -0
  191. package/dist/indexer/name-lookup-policies/implicit-self.js.map +1 -0
  192. package/dist/indexer/name-lookup-policies/index.d.ts +3 -0
  193. package/dist/indexer/name-lookup-policies/index.d.ts.map +1 -0
  194. package/dist/indexer/name-lookup-policies/index.js +106 -0
  195. package/dist/indexer/name-lookup-policies/index.js.map +1 -0
  196. package/dist/indexer/name-lookup-policies/php.d.ts +3 -0
  197. package/dist/indexer/name-lookup-policies/php.d.ts.map +1 -0
  198. package/dist/indexer/name-lookup-policies/php.js +75 -0
  199. package/dist/indexer/name-lookup-policies/php.js.map +1 -0
  200. package/dist/indexer/name-resolution-types.d.ts +99 -0
  201. package/dist/indexer/name-resolution-types.d.ts.map +1 -0
  202. package/dist/indexer/name-resolution-types.js +2 -0
  203. package/dist/indexer/name-resolution-types.js.map +1 -0
  204. package/dist/indexer/name-resolution.d.ts +52 -0
  205. package/dist/indexer/name-resolution.d.ts.map +1 -0
  206. package/dist/indexer/name-resolution.js +189 -0
  207. package/dist/indexer/name-resolution.js.map +1 -0
  208. package/dist/indexer/navigation-cpp.d.ts +50 -0
  209. package/dist/indexer/navigation-cpp.d.ts.map +1 -0
  210. package/dist/indexer/navigation-cpp.js +654 -0
  211. package/dist/indexer/navigation-cpp.js.map +1 -0
  212. package/dist/indexer/navigation-goto.d.ts +106 -3
  213. package/dist/indexer/navigation-goto.d.ts.map +1 -1
  214. package/dist/indexer/navigation-goto.js +2074 -137
  215. package/dist/indexer/navigation-goto.js.map +1 -1
  216. package/dist/indexer/navigation-local.d.ts +18 -2
  217. package/dist/indexer/navigation-local.d.ts.map +1 -1
  218. package/dist/indexer/navigation-local.js +498 -61
  219. package/dist/indexer/navigation-local.js.map +1 -1
  220. package/dist/indexer/navigation-php.d.ts +66 -0
  221. package/dist/indexer/navigation-php.d.ts.map +1 -1
  222. package/dist/indexer/navigation-php.js +287 -4
  223. package/dist/indexer/navigation-php.js.map +1 -1
  224. package/dist/indexer/navigation-references.d.ts +72 -5
  225. package/dist/indexer/navigation-references.d.ts.map +1 -1
  226. package/dist/indexer/navigation-references.js +620 -54
  227. package/dist/indexer/navigation-references.js.map +1 -1
  228. package/dist/indexer/navigation-resolve.d.ts +33 -2
  229. package/dist/indexer/navigation-resolve.d.ts.map +1 -1
  230. package/dist/indexer/navigation-resolve.js +446 -193
  231. package/dist/indexer/navigation-resolve.js.map +1 -1
  232. package/dist/indexer/navigation.d.ts +28 -2
  233. package/dist/indexer/navigation.d.ts.map +1 -1
  234. package/dist/indexer/navigation.js +1082 -151
  235. package/dist/indexer/navigation.js.map +1 -1
  236. package/dist/indexer/php-namespace-symbols.d.ts +66 -0
  237. package/dist/indexer/php-namespace-symbols.d.ts.map +1 -0
  238. package/dist/indexer/php-namespace-symbols.js +342 -0
  239. package/dist/indexer/php-namespace-symbols.js.map +1 -0
  240. package/dist/indexer/reference-context.d.ts +2 -0
  241. package/dist/indexer/reference-context.d.ts.map +1 -1
  242. package/dist/indexer/reference-context.js +4 -0
  243. package/dist/indexer/reference-context.js.map +1 -1
  244. package/dist/indexer/scope-nodes.d.ts +92 -13
  245. package/dist/indexer/scope-nodes.d.ts.map +1 -1
  246. package/dist/indexer/scope-nodes.js +212 -20
  247. package/dist/indexer/scope-nodes.js.map +1 -1
  248. package/dist/indexer/scope-types.d.ts +25 -1
  249. package/dist/indexer/scope-types.d.ts.map +1 -1
  250. package/dist/indexer/scope.d.ts.map +1 -1
  251. package/dist/indexer/scope.js +894 -45
  252. package/dist/indexer/scope.js.map +1 -1
  253. package/dist/indexer/shared-owner-identity.d.ts +53 -0
  254. package/dist/indexer/shared-owner-identity.d.ts.map +1 -0
  255. package/dist/indexer/shared-owner-identity.js +460 -0
  256. package/dist/indexer/shared-owner-identity.js.map +1 -0
  257. package/dist/indexer/shared.d.ts +1 -1
  258. package/dist/indexer/shared.d.ts.map +1 -1
  259. package/dist/indexer/shared.js +1 -1
  260. package/dist/indexer/shared.js.map +1 -1
  261. package/dist/indexer/star-import-precedence.d.ts +114 -0
  262. package/dist/indexer/star-import-precedence.d.ts.map +1 -0
  263. package/dist/indexer/star-import-precedence.js +396 -0
  264. package/dist/indexer/star-import-precedence.js.map +1 -0
  265. package/dist/indexer/symbols.d.ts +3 -1
  266. package/dist/indexer/symbols.d.ts.map +1 -1
  267. package/dist/indexer/symbols.js +58 -18
  268. package/dist/indexer/symbols.js.map +1 -1
  269. package/dist/indexer/ts-callables.d.ts +51 -0
  270. package/dist/indexer/ts-callables.d.ts.map +1 -0
  271. package/dist/indexer/ts-callables.js +264 -0
  272. package/dist/indexer/ts-callables.js.map +1 -0
  273. package/dist/indexer/types.d.ts +23 -1
  274. package/dist/indexer/types.d.ts.map +1 -1
  275. package/dist/indexer/unused-exports.d.ts +13 -0
  276. package/dist/indexer/unused-exports.d.ts.map +1 -0
  277. package/dist/indexer/unused-exports.js +159 -0
  278. package/dist/indexer/unused-exports.js.map +1 -0
  279. package/dist/indexer/workspace-symbols.d.ts.map +1 -1
  280. package/dist/indexer/workspace-symbols.js +3 -2
  281. package/dist/indexer/workspace-symbols.js.map +1 -1
  282. package/dist/indexer.d.ts +2 -2
  283. package/dist/indexer.d.ts.map +1 -1
  284. package/dist/indexer.js +1 -1
  285. package/dist/indexer.js.map +1 -1
  286. package/dist/languages/callable-arity.d.ts +202 -0
  287. package/dist/languages/callable-arity.d.ts.map +1 -0
  288. package/dist/languages/callable-arity.js +1182 -0
  289. package/dist/languages/callable-arity.js.map +1 -0
  290. package/dist/languages/definitions/c-family.d.ts.map +1 -1
  291. package/dist/languages/definitions/c-family.js +30 -1
  292. package/dist/languages/definitions/c-family.js.map +1 -1
  293. package/dist/languages/definitions/c.d.ts +4 -0
  294. package/dist/languages/definitions/c.d.ts.map +1 -1
  295. package/dist/languages/definitions/c.js +19 -0
  296. package/dist/languages/definitions/c.js.map +1 -1
  297. package/dist/languages/definitions/cpp.d.ts.map +1 -1
  298. package/dist/languages/definitions/cpp.js +12 -0
  299. package/dist/languages/definitions/cpp.js.map +1 -1
  300. package/dist/languages/definitions/csharp.d.ts.map +1 -1
  301. package/dist/languages/definitions/csharp.js +2 -8
  302. package/dist/languages/definitions/csharp.js.map +1 -1
  303. package/dist/languages/definitions/go.d.ts.map +1 -1
  304. package/dist/languages/definitions/go.js +3 -0
  305. package/dist/languages/definitions/go.js.map +1 -1
  306. package/dist/languages/definitions/javascript.d.ts.map +1 -1
  307. package/dist/languages/definitions/javascript.js +10 -1
  308. package/dist/languages/definitions/javascript.js.map +1 -1
  309. package/dist/languages/definitions/js-family.d.ts +1 -1
  310. package/dist/languages/definitions/js-family.d.ts.map +1 -1
  311. package/dist/languages/definitions/js-family.js +7 -1
  312. package/dist/languages/definitions/js-family.js.map +1 -1
  313. package/dist/languages/definitions/php.d.ts.map +1 -1
  314. package/dist/languages/definitions/php.js +5 -1
  315. package/dist/languages/definitions/php.js.map +1 -1
  316. package/dist/languages/definitions/typescript.d.ts.map +1 -1
  317. package/dist/languages/definitions/typescript.js +8 -1
  318. package/dist/languages/definitions/typescript.js.map +1 -1
  319. package/dist/languages/definitions/zig.d.ts.map +1 -1
  320. package/dist/languages/definitions/zig.js +2 -0
  321. package/dist/languages/definitions/zig.js.map +1 -1
  322. package/dist/mcp/handlers.js +14 -7
  323. package/dist/mcp/handlers.js.map +1 -1
  324. package/dist/review/candidates.d.ts.map +1 -1
  325. package/dist/review/candidates.js +1 -1
  326. package/dist/review/candidates.js.map +1 -1
  327. package/dist/review/deleted.d.ts +3 -3
  328. package/dist/review/deleted.d.ts.map +1 -1
  329. package/dist/review/deleted.js +82 -32
  330. package/dist/review/deleted.js.map +1 -1
  331. package/dist/review/report.d.ts +1 -0
  332. package/dist/review/report.d.ts.map +1 -1
  333. package/dist/review/report.js +1 -1
  334. package/dist/review/report.js.map +1 -1
  335. package/dist/review.d.ts.map +1 -1
  336. package/dist/review.js +1 -0
  337. package/dist/review.js.map +1 -1
  338. package/dist/sql/navigation.d.ts.map +1 -1
  339. package/dist/sql/navigation.js +1 -0
  340. package/dist/sql/navigation.js.map +1 -1
  341. package/dist/sql/source-graph.d.ts.map +1 -1
  342. package/dist/sql/source-graph.js +2 -2
  343. package/dist/sql/source-graph.js.map +1 -1
  344. package/dist/sqlite/schema.d.ts +1 -1
  345. package/dist/sqlite/schema.d.ts.map +1 -1
  346. package/dist/sqlite/schema.js +8 -2
  347. package/dist/sqlite/schema.js.map +1 -1
  348. package/dist/sqlite/write.js +2 -2
  349. package/dist/sqlite/write.js.map +1 -1
  350. package/dist/types.d.ts +2 -0
  351. package/dist/types.d.ts.map +1 -1
  352. package/dist/util/bloom-filter.d.ts +7 -1
  353. package/dist/util/bloom-filter.d.ts.map +1 -1
  354. package/dist/util/bloom-filter.js +12 -2
  355. package/dist/util/bloom-filter.js.map +1 -1
  356. package/dist/util/graph-edges.d.ts +19 -1
  357. package/dist/util/graph-edges.d.ts.map +1 -1
  358. package/dist/util/graph-edges.js +30 -4
  359. package/dist/util/graph-edges.js.map +1 -1
  360. package/dist/util/identifiers.d.ts +15 -0
  361. package/dist/util/identifiers.d.ts.map +1 -1
  362. package/dist/util/identifiers.js +35 -0
  363. package/dist/util/identifiers.js.map +1 -1
  364. package/dist/util/member-access-tables.d.ts +61 -4
  365. package/dist/util/member-access-tables.d.ts.map +1 -1
  366. package/dist/util/member-access-tables.js +137 -19
  367. package/dist/util/member-access-tables.js.map +1 -1
  368. package/dist/util/member-access.d.ts +17 -0
  369. package/dist/util/member-access.d.ts.map +1 -1
  370. package/dist/util/member-access.js +95 -3
  371. package/dist/util/member-access.js.map +1 -1
  372. package/dist/util/resolution/cargo-targets.d.ts +11 -0
  373. package/dist/util/resolution/cargo-targets.d.ts.map +1 -1
  374. package/dist/util/resolution/cargo-targets.js +174 -13
  375. package/dist/util/resolution/cargo-targets.js.map +1 -1
  376. package/dist/util/resolution/csharp.d.ts +33 -1
  377. package/dist/util/resolution/csharp.d.ts.map +1 -1
  378. package/dist/util/resolution/csharp.js +145 -30
  379. package/dist/util/resolution/csharp.js.map +1 -1
  380. package/dist/util/resolution/php.d.ts.map +1 -1
  381. package/dist/util/resolution/php.js +35 -8
  382. package/dist/util/resolution/php.js.map +1 -1
  383. package/dist/util/resolution/python.d.ts +9 -0
  384. package/dist/util/resolution/python.d.ts.map +1 -1
  385. package/dist/util/resolution/python.js +46 -0
  386. package/dist/util/resolution/python.js.map +1 -1
  387. package/dist/util/resolution/rust.d.ts.map +1 -1
  388. package/dist/util/resolution/rust.js +95 -26
  389. package/dist/util/resolution/rust.js.map +1 -1
  390. package/dist/util/resolution-candidates.d.ts +5 -0
  391. package/dist/util/resolution-candidates.d.ts.map +1 -1
  392. package/dist/util/resolution-candidates.js +16 -3
  393. package/dist/util/resolution-candidates.js.map +1 -1
  394. package/dist/util/resolution.d.ts +2 -1
  395. package/dist/util/resolution.d.ts.map +1 -1
  396. package/dist/util/resolution.js +96 -5
  397. package/dist/util/resolution.js.map +1 -1
  398. package/dist/util/specifiers.d.ts +20 -0
  399. package/dist/util/specifiers.d.ts.map +1 -1
  400. package/dist/util/specifiers.js +29 -0
  401. package/dist/util/specifiers.js.map +1 -1
  402. package/docs/mcp.md +3 -2
  403. package/package.json +3 -3
  404. package/dist/impact/call-compatibility/providers/profiles.d.ts +0 -80
  405. package/dist/impact/call-compatibility/providers/profiles.d.ts.map +0 -1
  406. package/dist/impact/call-compatibility/providers/profiles.js +0 -164
  407. package/dist/impact/call-compatibility/providers/profiles.js.map +0 -1
@@ -1,10 +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 { MEMBER_ACCESS_ROWS } from "../../util/member-access-tables.js";
6
- import { getMemberAccessParts, getNavigationExpressionProperty, isMemberAccessNode, isMemberReferencePropertyIdentifier, isReceiverNameNode, } from "../../util/member-access.js";
7
- const INSTANCE_RECEIVER_KEYWORDS = new Set(["this", "$this"]);
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";
8
12
  /** Languages whose grammar distinguishes static members from instance members. */
9
13
  const STATIC_MEMBER_LANGUAGES = {
10
14
  cpp: true,
@@ -16,11 +20,69 @@ const STATIC_MEMBER_LANGUAGES = {
16
20
  ts: true,
17
21
  tsx: true,
18
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
+ }
19
45
  /** Every language with a static-member distinction, guarded by the registry-consistency test. */
20
46
  export const staticMemberLanguageIds = Object.keys(STATIC_MEMBER_LANGUAGES);
21
47
  export function hasStaticMemberDistinction(languageId) {
22
48
  return STATIC_MEMBER_LANGUAGES[languageId] !== undefined;
23
49
  }
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)
67
+ return false;
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;
77
+ }
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);
85
+ }
24
86
  /**
25
87
  * Call nodes that carry the receiver and the member name on the call node itself
26
88
  * instead of exposing a member-access callee.
@@ -51,6 +113,25 @@ const MEMBER_CONTAINER_TYPES = {
51
113
  struct_specifier: true,
52
114
  trait_declaration: true,
53
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,
54
135
  };
55
136
  /** Nodes holding a call's argument list across the supported grammars. */
56
137
  export const CALL_ARGUMENT_NODE_TYPES = {
@@ -66,6 +147,16 @@ const HIERARCHY_LABELS = {
66
147
  mixin: true,
67
148
  trait: true,
68
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
+ }
69
160
  /**
70
161
  * Nodes that bind a value name across the supported grammars: locals, parameters,
71
162
  * and assignment targets. A receiver naming one of these is a value, not a type.
@@ -75,6 +166,9 @@ const VALUE_BINDING_TYPES = {
75
166
  assignment_expression: true,
76
167
  assignment_statement: true,
77
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,
78
172
  formal_parameter: true,
79
173
  init_declarator: true,
80
174
  let_declaration: true,
@@ -90,21 +184,6 @@ const VALUE_BINDING_TYPES = {
90
184
  variable_declaration: true,
91
185
  variable_declarator: true,
92
186
  };
93
- /**
94
- * Parameter lists whose identifier children are value bindings (Ruby has no wrapping param node).
95
- * Union of the receiver-call and call-compatibility lists so Kotlin `function_value_parameters`
96
- * and Ruby `block_parameters` are both recognized.
97
- */
98
- export const PARAMETER_LIST_NODE_TYPES = {
99
- block_parameters: true,
100
- formal_parameters: true,
101
- function_parameter_clause: true,
102
- function_value_parameters: true,
103
- lambda_parameters: true,
104
- method_parameters: true,
105
- parameter_list: true,
106
- parameters: true,
107
- };
108
187
  /**
109
188
  * Access nodes whose syntax is type-scoped (`::`), not dotted member access.
110
189
  * A dotted identifier is never itself proof of a type.
@@ -180,6 +259,8 @@ const BINDING_DECLARATION_TYPES = new Set([
180
259
  "property_declaration",
181
260
  "local_variable_declaration",
182
261
  "local_declaration_statement",
262
+ // C/C++ local variable statement, e.g. `Box b;`, `Box* p = raw;`, `Box w{5};`.
263
+ "declaration",
183
264
  ]);
184
265
  const RUBY_CONSTANT_SOURCE = String.raw `(?=\p{Lu})${XID_IDENTIFIER_SOURCE}`;
185
266
  /** Proven construction forms that name a receiver's type, keyed by language. */
@@ -193,7 +274,7 @@ const LANGUAGE_CONSTRUCTION_FORMS = {
193
274
  php: { newExpression: true },
194
275
  python: { capitalizedCall: true },
195
276
  ruby: { rubyNew: true },
196
- rust: { capitalizedCall: true, rustUnitStruct: true },
277
+ rust: { capitalizedCall: true, rustUnitStruct: true, compositeLiteral: true },
197
278
  swift: { capitalizedCall: true },
198
279
  ts: { newExpression: true },
199
280
  tsx: { newExpression: true },
@@ -237,12 +318,13 @@ export function receiverCallAccess(sup, callNode, callee) {
237
318
  return { accessNode, receiver, property };
238
319
  }
239
320
  /**
240
- * Identifier a binding node declares: a `name` field, a nested C/C++ declarator,
241
- * an assignment left-hand side, or the last identifier child (C++ parameters hide
242
- * 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.
243
325
  */
244
326
  function bindingIdentifier(node, sup) {
245
- const named = node.childForFieldName("name");
327
+ const named = node.childForFieldName("name") ?? node.childForFieldName("pattern");
246
328
  if (named && (isReceiverNameNode(sup, named.type) || named.type === "field_identifier")) {
247
329
  return named;
248
330
  }
@@ -323,6 +405,17 @@ function containsIndex(node, index) {
323
405
  return node.startIndex <= index && node.endIndex >= index;
324
406
  }
325
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
+ }
326
419
  const constructor = node.childForFieldName("constructor") ?? node.child(0);
327
420
  if (constructor && isReceiverNameNode(sup, constructor.type)) {
328
421
  return constructor;
@@ -334,14 +427,85 @@ function constructorNameNode(node, sup) {
334
427
  }
335
428
  return null;
336
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
+ }
337
470
  function rubyNewReceiverNameNode(node, source, sup) {
338
471
  if (!new RegExp(String.raw `^(?:${RUBY_CONSTANT_SOURCE})\.new$`, "u").test(sliceText(node, source)))
339
472
  return null;
340
473
  return node.namedChildren.find((child) => isReceiverNameNode(sup, child.type) || child.type === "constant") ?? null;
341
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
+ }
342
500
  export function unwrapNamedType(node, sup) {
343
501
  let current = node;
344
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
+ }
345
509
  if (current.type === "type_annotation" ||
346
510
  current.type === "named_type" ||
347
511
  current.type === "user_type" ||
@@ -358,6 +522,16 @@ export function unwrapNamedType(node, sup) {
358
522
  current = current.childForFieldName("type") ?? current.namedChildren[0] ?? null;
359
523
  continue;
360
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
+ }
361
535
  break;
362
536
  }
363
537
  if (!current)
@@ -367,6 +541,34 @@ export function unwrapNamedType(node, sup) {
367
541
  }
368
542
  return null;
369
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
+ }
370
572
  function capitalizedCallTypeName(expr, source, sup) {
371
573
  if (expr.type !== "call" && expr.type !== "call_expression")
372
574
  return null;
@@ -395,11 +597,21 @@ function compositeLiteralTypeName(expr, source, sup) {
395
597
  return null;
396
598
  current = operand;
397
599
  }
398
- 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") {
399
605
  return null;
606
+ }
400
607
  const typeNode = current.childForFieldName("type") ??
401
- 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) ??
402
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.
403
615
  return typeNode ? (unwrapNamedType(typeNode, sup) ?? typeNode) : null;
404
616
  }
405
617
  /**
@@ -458,13 +670,13 @@ function bindingValueExpression(node) {
458
670
  function declaredTypeNameNode(node, sup) {
459
671
  const typeField = node.childForFieldName("type");
460
672
  if (typeField)
461
- return unwrapNamedType(typeField, sup);
673
+ return unwrapNamedType(typeField, sup) ?? typeField;
462
674
  const typedChild = node.namedChildren.find((child) => child.type === "named_type" ||
463
675
  child.type === "user_type" ||
464
676
  child.type === "type_annotation" ||
465
677
  child.type === "type");
466
678
  if (typedChild)
467
- return unwrapNamedType(typedChild, sup);
679
+ return unwrapNamedType(typedChild, sup) ?? typedChild;
468
680
  const ids = node.namedChildren.filter((child) => isReceiverNameNode(sup, child.type) || child.type === "type_identifier" || child.type === "name");
469
681
  if (ids.length >= 2)
470
682
  return unwrapNamedType(ids[0], sup);
@@ -542,6 +754,10 @@ function bindingProof(node, receiverName, source, sup) {
542
754
  return { status: "none" };
543
755
  if (!bindingDeclaresReceiverName(node, receiverName, source, sup))
544
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 };
545
761
  const typeNode = constructionTypeFromBinding(node, receiverName, source, sup);
546
762
  if (typeNode)
547
763
  return { status: "type", node: typeNode };
@@ -553,12 +769,21 @@ function isSkippableBindingContainer(node, receiver) {
553
769
  function findPriorConstructorInContainer(node, receiver, receiverName, source, sup) {
554
770
  let constructor = null;
555
771
  let sawUnproven = false;
772
+ let declared = false;
556
773
  const visit = (current) => {
557
774
  if (current.startIndex >= receiver.startIndex)
558
775
  return true;
559
776
  if (current !== node && isSkippableBindingContainer(current, receiver))
560
777
  return true;
561
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;
562
787
  if (proof.status === "unproven") {
563
788
  if (constructor) {
564
789
  constructor = null;
@@ -594,7 +819,10 @@ function bindingContainerDeclaresNameBefore(node, receiver, receiverName, source
594
819
  return false;
595
820
  if (current !== node && isSkippableBindingContainer(current, receiver))
596
821
  return false;
597
- 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) &&
598
826
  bindingDeclaresReceiverName(current, receiverName, source, sup)) {
599
827
  return true;
600
828
  }
@@ -629,33 +857,290 @@ function findVisiblePriorConstructor(receiver, receiverName, source, sup) {
629
857
  * Resolves the node naming the type a receiver expression was constructed from, or
630
858
  * null when no constructor is proven for it. Shared with detailed symbol-graph call
631
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.
632
863
  */
633
864
  export function receiverConstructorExpression(obj, source, sup) {
634
865
  const direct = constructionTypeName(obj, source, sup);
635
- if (direct)
636
- return direct;
637
866
  if (!isReceiverNameNode(sup, obj.type))
638
- return null;
867
+ return direct;
639
868
  const receiverName = sliceText(obj, source);
869
+ if (direct && !bindsLocalValue(obj, receiverName, source, sup))
870
+ return direct;
640
871
  return findVisiblePriorConstructor(obj, receiverName, source, sup);
641
872
  }
642
- function ownTypeMemberScope(receiverName) {
643
- 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);
644
1127
  }
645
1128
  /**
646
1129
  * Classifies a receiver as the declaring type, a supertype, or a named/constructed type.
647
1130
  * Returns null when the receiver cannot be proven.
648
1131
  * Named-local constructor lookup is memoized per enclosing function and receiver text.
649
1132
  */
650
- export function classifyReceiver(sup, receiver, source, proofCache, cacheScope, accessNode) {
651
- const keywords = MEMBER_ACCESS_ROWS[sup.id]?.receiverKeywords;
652
- 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);
653
1135
  if (!text)
654
1136
  return null;
655
- if (keywords?.own.includes(text))
656
- return { kind: "own-type", memberScope: ownTypeMemberScope(text) };
657
- if (keywords?.supertype.includes(text))
658
- 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
+ }
659
1144
  const receiverIsName = isReceiverNameNode(sup, receiver.type);
660
1145
  const cacheKey = `${cacheScope}\u0000${text}`;
661
1146
  let proof = proofCache.get(cacheKey);
@@ -671,7 +1156,9 @@ export function classifyReceiver(sup, receiver, source, proofCache, cacheScope,
671
1156
  return {
672
1157
  kind: "named-type",
673
1158
  typeName: sliceText(proof.constructed, source),
1159
+ typeNode: proof.constructed,
674
1160
  memberScope: hasStaticMemberDistinction(sup.id) ? "instance" : "any",
1161
+ constructed: true,
675
1162
  };
676
1163
  }
677
1164
  if (!receiverIsName)
@@ -680,24 +1167,83 @@ export function classifyReceiver(sup, receiver, source, proofCache, cacheScope,
680
1167
  // `Example::shared()` would still be attributed to a colliding parameter named Example.
681
1168
  if (proof.locallyBound)
682
1169
  return null;
683
- // Dotted `Cfg.load()` is not proof: the identifier may be a value. Type-scoped `::`
684
- // is the remaining named-type proof. Ruby capitalized names are `constant` tokens
685
- // 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.
686
1175
  const property = getMemberAccessParts(sup, accessNode).property;
687
1176
  const between = property ? source.slice(receiver.endIndex, property.startIndex) : "";
688
1177
  const typeScoped = TYPE_SCOPED_ACCESS_TYPES[accessNode.type] === true || between.includes("::");
689
- if (receiver.type !== "type_identifier" && !typeScoped)
690
- 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
+ }
691
1192
  return {
692
1193
  kind: "named-type",
693
1194
  typeName: text,
1195
+ typeNode: receiver,
694
1196
  memberScope: hasStaticMemberDistinction(sup.id) && typeScoped ? "static" : "any",
695
1197
  };
696
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
+ }
697
1230
  /** Whether a resolved definition can declare callable members. */
698
1231
  export function declaresMembers(def) {
699
1232
  return def.kind === SymbolKind.Class || def.kind === SymbolKind.Interface || def.kind === SymbolKind.TypeAlias;
700
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
+ }
701
1247
  /** Nearest enclosing declaration that lexically owns `node` as a member. */
702
1248
  export function nearestMemberContainer(node) {
703
1249
  let current = node.parent;
@@ -708,29 +1254,168 @@ export function nearestMemberContainer(node) {
708
1254
  }
709
1255
  return null;
710
1256
  }
711
- /** Positional argument count of a call, ignoring comments. */
712
- export function callArgumentCount(callNode) {
713
- const explicit = callNode.childForFieldName("arguments") ?? callNode.childForFieldName("argument_list");
714
- 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;
715
1269
  if (argumentNode?.type === "call_suffix") {
716
1270
  argumentNode = (argumentNode.namedChildren ?? []).find((child) => child.type === "value_arguments") ?? null;
717
1271
  }
718
- if (!argumentNode)
719
- return 0;
720
- 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;
721
1289
  }
722
- function parseDefNodeId(id) {
723
- const indexSep = id.lastIndexOf("::");
724
- 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])
725
1307
  return null;
726
- const index = Number(id.slice(indexSep + 2));
727
- if (!Number.isFinite(index))
1308
+ const children = parent.namedChildren ?? [];
1309
+ if (children.some((child) => CALL_ARGUMENT_NODE_TYPES[child.type]))
728
1310
  return null;
729
- const rest = id.slice(0, indexSep);
730
- const nameSep = rest.lastIndexOf("::");
731
- 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)
732
1335
  return null;
733
- 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;
734
1419
  }
735
1420
  function loadSource(file, cache) {
736
1421
  const cached = cache.get(file);
@@ -746,20 +1431,6 @@ function loadSource(file, cache) {
746
1431
  return "";
747
1432
  }
748
1433
  }
749
- function memberIdLooksStatic(memberId, sourceCache) {
750
- const parsed = parseDefNodeId(memberId);
751
- if (!parsed)
752
- return false;
753
- const source = loadSource(parsed.file, sourceCache);
754
- if (!source)
755
- return false;
756
- const beforeBrace = source.lastIndexOf("{", parsed.index);
757
- const beforeSemi = source.lastIndexOf(";", parsed.index);
758
- const beforeClose = source.lastIndexOf("}", parsed.index);
759
- const declStart = Math.max(beforeBrace, beforeSemi, beforeClose);
760
- const prefix = source.slice(declStart + 1, parsed.index);
761
- return /(?:^|[^\w$])static(?:$|[^\w$])/.test(prefix) || /(?:^|[^\w$])def\s+self\s*\./.test(prefix);
762
- }
763
1434
  function inferCallMemberScope(site, sourceCache) {
764
1435
  const source = loadSource(site.file, sourceCache);
765
1436
  if (!source)
@@ -774,22 +1445,25 @@ function inferCallMemberScope(site, sourceCache) {
774
1445
  return "instance";
775
1446
  if (/\)\s*\.\s*$/.test(before))
776
1447
  return "instance";
777
- // HeritageEdges does not yet copy classifyReceiver.memberScope onto candidates.
778
- // 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.
779
1449
  if (/(?:^|[^A-Za-z0-9_$])[a-z_][A-Za-z0-9_]*\s*\.\s*$/.test(before))
780
1450
  return "instance";
781
1451
  if (/(?:^|[^A-Za-z0-9_$])[A-Z][A-Za-z0-9_]*\s*\.\s*$/.test(before))
782
1452
  return "static";
783
1453
  return "any";
784
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
+ }
785
1459
  /**
786
1460
  * Records proven `calls` edges for deferred receiver invocations.
787
1461
  * Ambiguous names at a level stop the walk.
788
1462
  * `super`/`base`/`parent` follow class `extends` ancestors only.
789
1463
  */
790
- 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) {
791
1465
  if (!candidates.length)
792
- return;
1466
+ return [];
793
1467
  const membersByOwner = new Map();
794
1468
  const ownerByMember = new Map();
795
1469
  const supertypesByOwner = new Map();
@@ -814,27 +1488,75 @@ export function emitReceiverCallEdges(graph, candidates, recordEdge) {
814
1488
  if (label === "extends")
815
1489
  pushUnique(classAncestorsByOwner, edge.from, edge.to);
816
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
+ }
817
1496
  const nextOwners = (ownerId, viaSupertypes) => {
818
1497
  if (!viaSupertypes)
819
1498
  return supertypesByOwner.get(ownerId) ?? [];
820
1499
  return (classAncestorsByOwner.get(ownerId) ?? []).filter((id) => graph.nodes.get(id)?.kind === "class");
821
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();
822
1513
  const sourceCache = new Map();
823
1514
  for (const candidate of candidates) {
824
- const owner = candidate.ownerId ?? ownerByMember.get(candidate.callerId);
825
- 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)
826
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;
827
1527
  const memberScope = candidate.memberScope ?? inferCallMemberScope(candidate.site, sourceCache);
828
1528
  let level = candidate.viaSupertypes ? nextOwners(owner, true) : [owner];
829
1529
  const visited = new Set(level);
1530
+ let receiverDisposition = "none";
830
1531
  for (let depth = 0; depth < MAX_SUPERTYPE_DEPTH && level.length; depth += 1) {
831
- 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);
832
1533
  if (lookup.status === "unique") {
833
- 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
+ }
834
1552
  break;
835
1553
  }
836
- if (lookup.status === "ambiguous")
1554
+ if (lookup.status === "ambiguous") {
1555
+ receiverDisposition = "ambiguous";
1556
+ if (existingTargets.size)
1557
+ rejectedCallSites.add(siteKey);
837
1558
  break;
1559
+ }
838
1560
  const next = [];
839
1561
  for (const ownerId of level) {
840
1562
  for (const supertype of nextOwners(ownerId, candidate.viaSupertypes)) {
@@ -846,7 +1568,46 @@ export function emitReceiverCallEdges(graph, candidates, recordEdge) {
846
1568
  }
847
1569
  level = next;
848
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);
849
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));
850
1611
  }
851
1612
  /**
852
1613
  * The single callable member named by `candidate` across `owners`.
@@ -854,27 +1615,46 @@ export function emitReceiverCallEdges(graph, candidates, recordEdge) {
854
1615
  * `ambiguous` means this depth matched the name but could not prove one member,
855
1616
  * including arity ambiguity, and the walk must stop.
856
1617
  */
857
- function provenMemberTarget(graph, membersByOwner, owners, candidate, memberScope, sourceCache) {
1618
+ function provenMemberTarget(graph, membersByOwner, owners, candidate, memberScope, memberScopes, nodeAliases, memberArities, fileHiddenMemberIds, useFile) {
858
1619
  const matches = new Set();
859
1620
  for (const ownerId of owners) {
860
1621
  for (const memberId of membersByOwner.get(ownerId) ?? []) {
861
- const node = graph.nodes.get(memberId);
862
- 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))
863
1625
  continue;
864
- if (memberScope === "static" && !memberIdLooksStatic(memberId, sourceCache))
1626
+ if ((fileHiddenMemberIds.has(memberId) || fileHiddenMemberIds.has(canonicalId)) &&
1627
+ fileIdentityKey(node.file) !== fileIdentityKey(useFile)) {
865
1628
  continue;
866
- 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)
867
1634
  continue;
868
- 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);
869
1644
  }
870
1645
  }
871
1646
  if (!matches.size)
872
1647
  return { status: "none" };
873
1648
  if (matches.size === 1) {
874
1649
  const [memberId] = matches;
875
- return { status: "unique", memberId: memberId };
1650
+ if (memberArityMatches(memberId, candidate.argumentCount, memberArities)) {
1651
+ return { status: "unique", memberId: memberId };
1652
+ }
1653
+ return { status: "ambiguous" };
876
1654
  }
877
- 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));
878
1658
  if (byArity.length === 1)
879
1659
  return { status: "unique", memberId: byArity[0] };
880
1660
  return { status: "ambiguous" };