@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,17 +1,33 @@
1
+ import { SymbolKind } from "../../indexer/types.js";
2
+ import { cppCallableShapeForNode } from "../../indexer/cpp-callables.js";
3
+ import { getCompilationUnitPeers } from "../../indexer/compilation-units.js";
4
+ import { isExportedDeclaration, isGoExportedMemberName, isSwiftCrossFileHiddenSharedOwnerMember, isSwiftFileHiddenSharedOwnerMember, } from "../../indexer/declaration-visibility.js";
5
+ import { resolveCppQualifiedMemberContainer } from "../../indexer/navigation-cpp.js";
6
+ import { cppQualifiedOwnerHasImplicitThis, findTypeScriptNamespaceMemberCandidates, innermostNamespaceImport, isDirectKeywordMemberDeclaration, memberAcceptsCallAt, resolvePhpObjectCreationTarget, resolveRubyVisibleConstant, resolveSharedOwnerContainers, } from "../../indexer/navigation-goto.js";
7
+ import { findClosestScopeBinding } from "../../indexer/navigation-local.js";
8
+ import { findPhpImportAlias, inferPhpQualifiedReferenceImportType } from "../../indexer/navigation-php.js";
9
+ import { cjsRequireValueBinding, resolveImportTypeMember, resolvePhpExportByImportType, } from "../../indexer/navigation-resolve.js";
10
+ import { effectiveExplicitBinding } from "../../indexer/star-import-precedence.js";
11
+ import { typescriptSelectOverloadCandidate } from "../../indexer/ts-callables.js";
12
+ import { isSwiftConstrainedExtension, isSwiftExtensionContainer, selectCsharpPartialRepresentative, } from "../../indexer/shared-owner-identity.js";
13
+ import { isJsTsLanguage } from "../../languages/js-family.js";
14
+ import { getCallableArity, getCallArgumentCount, memberLookupBinding } from "../../languages/callable-arity.js";
1
15
  import { sliceText, toRange } from "../../util/ast.js";
2
- import { getMemberAccessParts } from "../../util/member-access.js";
16
+ import { getMemberAccessParts, isMemberAccessNode, rustTokenTreeHoldsExpressions, rustTokenTreeNameFollowsSeparator, } from "../../util/member-access.js";
17
+ import { foldPhpIdentifierCase } from "../../util/identifiers.js";
18
+ import { MEMBER_ACCESS_ROWS, keywordReceiverKind, } from "../../util/member-access-tables.js";
3
19
  import { fileIdentityKey } from "../../util/paths.js";
4
20
  import { defNodeId, nodeForDef } from "../symbol-graph.js";
5
- import { collectNodesByType, findFirstNodeByType, isIdentifierType } from "./ast.js";
6
- import { CALL_ARGUMENT_NODE_TYPES, callArgumentCount, classifyReceiver, declaresMembers, nearestMemberContainer, receiverCallAccess, } from "./receiver-calls.js";
21
+ import { collectNodesByType, declarationMemberArity, findFirstNodeByType, isIdentifierType } from "./ast.js";
22
+ import { CALL_ARGUMENT_NODE_TYPES, classifyReceiver, constructionTypeName, declarationIsStaticEquivalent, declarationNodeIsStatic, cppOutOfLineOwnerPath, cppOutOfLineMemberDeclarationNode, cppQualifiedNameSegments, csharpDottedNameRoot, declaresMembers, isUnprovenHeritageExpression, kotlinExtensionReceiverTypeNode, memberContainerDef, nearestMemberContainer, nodeInStaticMemberContext, phpObjectCreationKeyword, receiverCallAccess, rustImplSelfTypeNode, supportsImplicitSelfMemberCalls, supportsReceiverMemberOverloads, receiverConstructorExpression, importTypeQuerySpecifier, } from "./receiver-calls.js";
7
23
  function ensureNode(context, def) {
8
24
  const id = defNodeId(def);
9
25
  if (!context.nodes.has(id))
10
26
  context.nodes.set(id, nodeForDef(def));
11
27
  return id;
12
28
  }
13
- function markImplementationTarget(context, id, declarationNode, def) {
14
- const declaration = sliceText(declarationNode, context.source);
29
+ function markImplementationTarget(context, id, declarationNode, declarationSource, def) {
30
+ const declaration = sliceText(declarationNode, declarationSource);
15
31
  const nameIndex = declaration.indexOf(def.localName);
16
32
  const prefix = nameIndex >= 0 ? declaration.slice(0, nameIndex) : declaration;
17
33
  if (!/\b(?:abstract|virtual|override)\b/.test(prefix))
@@ -21,27 +37,58 @@ function markImplementationTarget(context, id, declarationNode, def) {
21
37
  node.implementationTarget = true;
22
38
  }
23
39
  function markMemberArity(context, id, declarationNode) {
24
- let parameters = declarationNode.childForFieldName("parameters");
25
- if (!parameters) {
26
- for (const type of [
27
- "formal_parameters",
28
- "parameter_list",
29
- "parameters",
30
- "method_parameters",
31
- "function_parameter_clause",
32
- ]) {
33
- parameters = findFirstNodeByType(declarationNode, type);
34
- if (parameters)
35
- break;
36
- }
37
- }
38
- if (!parameters)
40
+ const arity = declarationMemberArity(declarationNode, context.sup.id);
41
+ if (arity === undefined)
39
42
  return;
40
- const arity = (parameters.namedChildren ?? []).filter((child) => child.type !== "comment").length;
41
43
  const node = context.nodes.get(id);
42
44
  if (node)
43
45
  node.memberArity = arity;
44
46
  }
47
+ function cppShapeNode(node) {
48
+ return node.type === "function_declarator" ? node : (findFirstNodeByType(node, "function_declarator") ?? node);
49
+ }
50
+ function mergeCppCallableShapes(...shapes) {
51
+ const present = shapes.filter((shape) => !!shape);
52
+ if (!present.length)
53
+ return undefined;
54
+ let min = present[0].minArity;
55
+ let max = present[0].maxArity;
56
+ for (const shape of present.slice(1)) {
57
+ min = Math.min(min, shape.minArity);
58
+ if (max === null || shape.maxArity === null)
59
+ max = null;
60
+ else
61
+ max = Math.max(max, shape.maxArity);
62
+ }
63
+ return { min, max };
64
+ }
65
+ /**
66
+ * Accepted explicit-argument range of a member declaration from the shared callable
67
+ * facts (default parameters, varargs, explicit receivers), or undefined when the
68
+ * declaration shape is unknown or the language does not select members by call arity.
69
+ * The binding follows the call form member lookup resolves (see `memberLookupBinding`).
70
+ */
71
+ function acceptedMemberArityRange(context, declarationNode, source) {
72
+ if (!supportsReceiverMemberOverloads(context.sup.id))
73
+ return undefined;
74
+ const arity = getCallableArity({
75
+ languageId: context.sup.id,
76
+ source,
77
+ declaration: declarationNode,
78
+ binding: memberLookupBinding(context.sup.id),
79
+ });
80
+ return arity ? { min: arity.minArgs, max: arity.maxArgs } : undefined;
81
+ }
82
+ function recordMemberLookupIdentity(context, definitionId, memberId, memberScope, arityRange) {
83
+ context.receiverMemberScopes.set(definitionId, memberScope);
84
+ if (memberId !== definitionId)
85
+ context.receiverMemberScopes.set(memberId, memberScope);
86
+ if (!arityRange)
87
+ return;
88
+ context.receiverMemberArities.set(definitionId, arityRange);
89
+ if (memberId !== definitionId)
90
+ context.receiverMemberArities.set(memberId, arityRange);
91
+ }
45
92
  function recordDefEdge(context, fromId, target, label, siteNode) {
46
93
  const toId = ensureNode(context, target);
47
94
  return context.recordEdge(fromId, toId, label, siteNode ? { file: context.moduleEntry.file, range: toRange(siteNode) } : undefined);
@@ -56,10 +103,26 @@ function tryResolveChain(context, node, fromId, label = "uses") {
56
103
  }
57
104
  /** Records an edge for a resolvable target node. Returns whether a target was resolved. */
58
105
  function tryResolveNode(context, node, fromId, label) {
59
- if (isIdentifierType(context.sup, node.type) || node.type === "type_identifier") {
106
+ if (context.sup.id === "cpp" && node.type === "qualified_identifier") {
107
+ const name = cppQualifiedNameSegments(node, context.source).join("::");
108
+ const target = context.resolveIdentifier(name, node);
109
+ if (target) {
110
+ recordDefEdge(context, fromId, target, label, node);
111
+ return true;
112
+ }
113
+ return false;
114
+ }
115
+ if (isIdentifierType(context.sup, node.type) ||
116
+ node.type === "type_identifier" ||
117
+ (context.sup.id === "csharp" && (node.type === "qualified_name" || node.type === "alias_qualified_name")) ||
118
+ (context.sup.id === "cpp" && (node.type === "operator_name" || node.type === "destructor_name"))) {
60
119
  const name = sliceText(node, context.source);
61
120
  const target = context.resolveIdentifier(name, node);
62
121
  if (target) {
122
+ // Zig struct declarations require an explicit type receiver (`Self.helper()`).
123
+ // An unqualified identifier is not a call to a member of its enclosing struct.
124
+ if (label === "calls" && context.sup.id === "zig" && target.isMember)
125
+ return false;
63
126
  recordDefEdge(context, fromId, target, label, node);
64
127
  return true;
65
128
  }
@@ -69,6 +132,32 @@ function tryResolveNode(context, node, fromId, label) {
69
132
  }
70
133
  return false;
71
134
  }
135
+ /**
136
+ * Rust macro arguments stay unparsed token trees, so `println!("{}", greet())` never forms a
137
+ * call node even though goto resolves the identifier. An `identifier` directly followed by a
138
+ * `(` group is a call in the argument list, resolved like a bare call. The macro's own name
139
+ * sits outside the token tree and is never a call; a `!` gap marks a nested macro invocation,
140
+ * and a `.` or `::` gap marks a member or path receiver that raw tokens cannot prove, matching
141
+ * navigation's refusals inside token trees.
142
+ */
143
+ function recordRustMacroArgumentCalls(context, tokenTree, fromId) {
144
+ if (context.sup.id !== "rust" || !rustTokenTreeHoldsExpressions(tokenTree))
145
+ return;
146
+ const children = tokenTree.namedChildren;
147
+ for (let index = 0; index + 1 < children.length; index += 1) {
148
+ const name = children[index];
149
+ const args = children[index + 1];
150
+ if (name.type !== "identifier" || args.type !== "token_tree")
151
+ continue;
152
+ if (!args.text.startsWith("("))
153
+ continue;
154
+ if (context.source.slice(name.endIndex, args.startIndex).trim())
155
+ continue;
156
+ if (rustTokenTreeNameFollowsSeparator(name))
157
+ continue;
158
+ tryResolveNode(context, name, fromId, "calls");
159
+ }
160
+ }
72
161
  function getCallTarget(node) {
73
162
  const explicitTarget = node.childForFieldName("function") ??
74
163
  node.childForFieldName("callee") ??
@@ -76,6 +165,11 @@ function getCallTarget(node) {
76
165
  node.childForFieldName("method") ??
77
166
  node.childForFieldName("member") ??
78
167
  node.childForFieldName("expression");
168
+ // tree-sitter-typescript parses `await f<T>(x)` as a call whose callee is `await f`; the awaited
169
+ // value is the call's result, so the callee is `f`.
170
+ if (explicitTarget?.type === "await_expression" && node.childForFieldName("type_arguments")) {
171
+ return explicitTarget.namedChildren[0] ?? null;
172
+ }
79
173
  if (explicitTarget)
80
174
  return explicitTarget;
81
175
  // Kotlin and Swift calls name no callee field, so the sole non-argument child is it.
@@ -83,11 +177,16 @@ function getCallTarget(node) {
83
177
  return nonArgumentChildren.length === 1 ? (nonArgumentChildren[0] ?? null) : null;
84
178
  }
85
179
  function getNewTarget(node) {
86
- return (node.childForFieldName("constructor") ??
87
- node.childForFieldName("type") ??
88
- node.childForFieldName("name") ??
89
- node.namedChildren.find((child) => child.type === "type_identifier") ??
90
- node.child(0));
180
+ const field = node.childForFieldName("constructor") ?? node.childForFieldName("type") ?? node.childForFieldName("name");
181
+ if (field)
182
+ return field;
183
+ // PHP `new Base()` has no type field; the type is a `name` / `qualified_name` child and
184
+ // `child(0)` is the `new` keyword, which is not a type.
185
+ const namedType = node.namedChildren.find((child) => child.type === "type_identifier" ||
186
+ child.type === "name" ||
187
+ child.type === "qualified_name" ||
188
+ child.type === "relative_name");
189
+ return namedType ?? node.child(0);
91
190
  }
92
191
  export function emitPythonDecoratorEdges(context, rootNode) {
93
192
  if (context.sup.id !== "python")
@@ -151,34 +250,219 @@ export function emitPythonDecoratorEdges(context, rootNode) {
151
250
  };
152
251
  addDecoratorUses(rootNode);
153
252
  }
154
- export function emitMemberOwnershipEdges(context, functionNodes, classNodes) {
253
+ /** Whether a function declaration can participate in class member lookup and ownership. */
254
+ function isClassMemberFunction(fn) {
255
+ return fn.node.type !== "local_function_statement";
256
+ }
257
+ export async function emitMemberOwnershipEdges(context, functionNodes, classNodes) {
155
258
  for (const fn of functionNodes) {
156
- const ownerDef = memberOwnerDef(context, fn, classNodes);
157
- if (!ownerDef)
259
+ const owner = await memberOwner(context, fn, classNodes);
260
+ if (!owner)
261
+ continue;
262
+ const outOfLineDeclaration = owner.cppOutOfLine
263
+ ? await cppOutOfLineMemberDeclaration(context, fn, owner.def)
264
+ : null;
265
+ const definitionId = ensureNode(context, fn.def);
266
+ let memberDef = fn.def;
267
+ if (outOfLineDeclaration) {
268
+ const declarationModule = context.index.byFile.get(fileIdentityKey(owner.def.file));
269
+ const declarationDef = declarationModule?.locals.find((candidate) => candidate.localName === fn.def.localName &&
270
+ candidate.kind === fn.def.kind &&
271
+ candidate.range.start.index === outOfLineDeclaration.nameNode.startIndex &&
272
+ candidate.range.end.index === outOfLineDeclaration.nameNode.endIndex);
273
+ if (declarationDef)
274
+ memberDef = declarationDef;
275
+ }
276
+ const memberId = ensureNode(context, memberDef);
277
+ if (definitionId !== memberId)
278
+ context.nodeAliases.set(definitionId, memberId);
279
+ if (context.sup.id === "java" || context.sup.id === "kotlin" || context.sup.id === "swift") {
280
+ const declarationNode = outOfLineDeclaration?.node ?? fn.node;
281
+ const hidden = context.sup.id === "swift"
282
+ ? isSwiftFileHiddenSharedOwnerMember(context.sup.id, declarationNode)
283
+ : !isExportedDeclaration(context.sup.id, declarationNode);
284
+ if (hidden) {
285
+ context.fileHiddenMemberIds.add(memberId);
286
+ if (definitionId !== memberId)
287
+ context.fileHiddenMemberIds.add(definitionId);
288
+ }
289
+ }
290
+ markImplementationTarget(context, memberId, outOfLineDeclaration?.node ?? fn.node, outOfLineDeclaration?.source ?? context.source, fn.def);
291
+ const arityNode = outOfLineDeclaration?.node ?? fn.node;
292
+ markMemberArity(context, definitionId, arityNode);
293
+ if (memberId !== definitionId)
294
+ markMemberArity(context, memberId, arityNode);
295
+ const memberScope = memberScopeForDefinition(context, fn, owner.cppOutOfLine, outOfLineDeclaration);
296
+ const arityRange = context.sup.id === "cpp"
297
+ ? mergeCppCallableShapes(cppCallableShapeForNode(cppShapeNode(fn.node)), outOfLineDeclaration ? cppCallableShapeForNode(cppShapeNode(outOfLineDeclaration.node)) : undefined)
298
+ : acceptedMemberArityRange(context, arityNode, outOfLineDeclaration?.source ?? context.source);
299
+ recordMemberLookupIdentity(context, definitionId, memberId, memberScope, arityRange);
300
+ recordDefEdge(context, definitionId, owner.def, "member_of");
301
+ await emitSharedOwnerMembershipEdges(context, owner, definitionId, fn.node);
302
+ }
303
+ }
304
+ /** Owner def of a shared-owner container, matched by its declaration name range. */
305
+ function sharedOwnerPeerDef(peer, callerFile) {
306
+ const nameNode = peer.container.childForFieldName("name");
307
+ if (!nameNode)
308
+ return null;
309
+ const def = peer.module.locals.find((local) => declaresMembers(local) && local.range.start.index === nameNode.startIndex);
310
+ if (!def)
311
+ return null;
312
+ if (!callerFile)
313
+ return { def, isExtension: isSwiftExtensionContainer(peer.container, peer.context.source) };
314
+ const visibleMemberIds = [];
315
+ for (const local of peer.module.locals) {
316
+ const start = local.range.start.index;
317
+ if (start === undefined || start < peer.container.startIndex || start >= peer.container.endIndex)
318
+ continue;
319
+ const name = peer.context.tree.rootNode.descendantForIndex(start, start);
320
+ if (!isDirectKeywordMemberDeclaration(name, peer.container))
321
+ continue;
322
+ if (isSwiftCrossFileHiddenSharedOwnerMember("swift", callerFile, peer.file, name))
323
+ continue;
324
+ visibleMemberIds.push(defNodeId(local));
325
+ }
326
+ return { def, isExtension: isSwiftExtensionContainer(peer.container, peer.context.source), visibleMemberIds };
327
+ }
328
+ /**
329
+ * C# partial declarations and unconstrained Swift extensions share type membership
330
+ * across proven peers. Constrained Swift extensions instead retain their own members
331
+ * and record only the peer members their lexical owner can use. This keeps unproven
332
+ * receivers from inheriting constrained members while preserving calls inside the
333
+ * extension. Same-named types in other namespaces or paths remain separate.
334
+ */
335
+ async function emitSharedOwnerMembershipEdges(context, owner, definitionId, memberNode) {
336
+ const container = owner.container;
337
+ if (!container)
338
+ return;
339
+ if (context.sup.id !== "csharp" && context.sup.id !== "swift")
340
+ return;
341
+ const isExtension = context.sup.id === "swift" && isSwiftExtensionContainer(container, context.source);
342
+ // Swift base types already own their full member set; only extensions and C#
343
+ // partials reach across owner declarations. Non-partial C# containers resolve
344
+ // to no peers inside the shared-owner relation.
345
+ // Constrained extensions can use nominal members but cannot donate their own
346
+ // members to every instance of the nominal type without proving the where clause.
347
+ const constrained = context.sup.id === "swift" && isSwiftConstrainedExtension(container, context.source);
348
+ if (constrained && context.sharedOwnerAccessibleMembers.has(defNodeId(owner.def)))
349
+ return;
350
+ const cacheKey = `${fileIdentityKey(context.moduleEntry.file)}\u0000${container.startIndex}\u0000${container.endIndex}`;
351
+ let peers = context.sharedOwnerPeers.get(cacheKey);
352
+ if (!peers) {
353
+ peers = resolveSharedOwnerContainers({
354
+ index: context.index,
355
+ ownerFile: context.moduleEntry.file,
356
+ ownerContainer: container,
357
+ ownerSource: context.source,
358
+ languageId: context.sup.id,
359
+ }).then((resolved) => resolved
360
+ .map((peer) => sharedOwnerPeerDef(peer, constrained ? context.moduleEntry.file : undefined))
361
+ .filter((peer) => !!peer));
362
+ context.sharedOwnerPeers.set(cacheKey, peers);
363
+ }
364
+ const peerDefs = await peers;
365
+ if (constrained) {
366
+ const ownerId = defNodeId(owner.def);
367
+ const accessible = context.sharedOwnerAccessibleMembers.get(ownerId) ?? new Set();
368
+ for (const peer of peerDefs)
369
+ for (const memberId of peer.visibleMemberIds ?? [])
370
+ accessible.add(memberId);
371
+ context.sharedOwnerAccessibleMembers.set(ownerId, accessible);
372
+ return;
373
+ }
374
+ for (const peer of peerDefs) {
375
+ if (isSwiftCrossFileHiddenSharedOwnerMember(context.sup.id, peer.def.file, context.moduleEntry.file, memberNode)) {
158
376
  continue;
159
- const memberId = ensureNode(context, fn.def);
160
- markImplementationTarget(context, memberId, fn.node, fn.def);
161
- markMemberArity(context, memberId, fn.node);
162
- recordDefEdge(context, memberId, ownerDef, "member_of");
377
+ }
378
+ recordDefEdge(context, definitionId, peer.def, "member_of");
379
+ }
380
+ if (isExtension) {
381
+ const anchor = peerDefs.find((peer) => !peer.isExtension);
382
+ context.sharedOwnerAnchors.set(defNodeId(owner.def), defNodeId(anchor ? anchor.def : owner.def));
383
+ return;
384
+ }
385
+ if (context.sup.id === "csharp" && peerDefs.length) {
386
+ const representative = selectCsharpPartialRepresentative([owner.def, ...peerDefs.map((peer) => peer.def)]);
387
+ context.sharedOwnerAnchors.set(defNodeId(owner.def), defNodeId(representative));
163
388
  }
164
389
  }
165
- /** Lexical class body, or the named Go receiver type for an out-of-line method. */
166
- function memberOwnerDef(context, fn, classNodes) {
390
+ function memberScopeForDefinition(context, fn, cppOutOfLine, outOfLineDeclaration) {
391
+ if (outOfLineDeclaration) {
392
+ return declarationNodeIsStatic(outOfLineDeclaration.node, outOfLineDeclaration.source) ? "static" : "instance";
393
+ }
394
+ if (cppOutOfLine)
395
+ return "any";
396
+ // Kotlin has no `static` keyword; a member of a `companion object` or a named `object`
397
+ // declaration is the language's static-equivalent mechanism (reachable as `Outer.member()`).
398
+ if (declarationIsStaticEquivalent(context.sup.id, fn.node))
399
+ return "static";
400
+ const declarationNode = fn.node.parent?.type === "public_field_definition" || fn.node.parent?.type === "field_definition"
401
+ ? fn.node.parent
402
+ : fn.node;
403
+ return declarationNodeIsStatic(declarationNode, context.source) ? "static" : "instance";
404
+ }
405
+ /** Lexical type body, named Go receiver type, or named C++ out-of-line owner. */
406
+ async function memberOwner(context, fn, classNodes) {
407
+ if (!isClassMemberFunction(fn))
408
+ return null;
167
409
  if (context.sup.id === "go" && fn.node.type === "method_declaration") {
168
- return goMethodReceiverTypeDef(context, fn.node);
410
+ const def = goMethodReceiverTypeDef(context, fn.node);
411
+ return def ? { def, container: null, cppOutOfLine: false } : null;
169
412
  }
170
413
  const owners = classNodes
171
414
  .filter((candidate) => candidate.node.startIndex <= fn.node.startIndex && candidate.node.endIndex >= fn.node.endIndex)
172
415
  .sort((left, right) => left.node.endIndex - left.node.startIndex - (right.node.endIndex - right.node.startIndex));
173
- if (owners[0]?.def)
174
- return owners[0].def;
416
+ if (owners[0]?.def) {
417
+ if (!isDirectKeywordMemberDeclaration(fn.node, owners[0].node))
418
+ return null;
419
+ return { def: owners[0].def, container: owners[0].node, cppOutOfLine: false };
420
+ }
421
+ if (context.sup.id === "cpp") {
422
+ const ownerPath = cppOutOfLineOwnerPath(fn.node, context.source, context.sup);
423
+ const def = ownerPath
424
+ ? await resolveCppQualifiedMemberContainer(context.index, context.moduleEntry, ownerPath, context.loadParsedFile)
425
+ : null;
426
+ return def ? { def, container: null, cppOutOfLine: true } : null;
427
+ }
428
+ if (context.sup.id === "kotlin") {
429
+ // `fun Widget.describe()` is a top-level declaration outside Widget's own body, so it is
430
+ // never one of `owners` above; its receiver-type prefix is the only proof of ownership.
431
+ const receiverTypeNode = kotlinExtensionReceiverTypeNode(fn.node, context.sup);
432
+ if (!receiverTypeNode)
433
+ return null;
434
+ const def = resolveNamedType(context, sliceText(receiverTypeNode, context.source), receiverTypeNode);
435
+ return def ? { def, container: null, cppOutOfLine: false } : null;
436
+ }
437
+ if (context.sup.id === "rust") {
438
+ // A Rust impl method sits beside its self type, not inside it (like Go's receiver type);
439
+ // the `impl` block's self type is the owner, and only direct impl members count.
440
+ const container = nearestMemberContainer(fn.node);
441
+ if (!container || container.type !== "impl_item" || !isDirectKeywordMemberDeclaration(fn.node, container)) {
442
+ return null;
443
+ }
444
+ const nameNode = rustImplSelfTypeNode(container, context.sup);
445
+ const def = nameNode ? resolveNamedType(context, sliceText(nameNode, context.source), nameNode) : null;
446
+ return def ? { def, container, cppOutOfLine: false } : null;
447
+ }
175
448
  if (context.sup.id !== "zig")
176
449
  return null;
177
450
  const container = nearestMemberContainer(fn.node);
178
451
  if (container?.type !== "struct_declaration")
179
452
  return null;
180
453
  const name = container.parent?.namedChildren.find((child) => child.type === "identifier");
181
- return name ? resolveNamedType(context, sliceText(name, context.source), name) : null;
454
+ const def = name ? resolveNamedType(context, sliceText(name, context.source), name) : null;
455
+ return def ? { def, container, cppOutOfLine: false } : null;
456
+ }
457
+ async function cppOutOfLineMemberDeclaration(context, fn, ownerDef) {
458
+ const parsed = await context.loadParsedFile(ownerDef.file);
459
+ const startIndex = ownerDef.range.start.index;
460
+ const endIndex = ownerDef.range.end.index;
461
+ if (!parsed || startIndex === undefined || endIndex === undefined)
462
+ return null;
463
+ const ownerNameNode = parsed.tree.rootNode.descendantForIndex(startIndex, endIndex);
464
+ const declaration = cppOutOfLineMemberDeclarationNode(fn.node, fn.def.localName, ownerNameNode, parsed.source, context.sup);
465
+ return declaration ? { node: declaration.node, nameNode: declaration.nameNode, source: parsed.source } : null;
182
466
  }
183
467
  /** Receiver type of `func (b *T) M()` / `func (b T) M()`, unwrapped through pointers. */
184
468
  function goMethodReceiverTypeDef(context, methodNode) {
@@ -208,16 +492,157 @@ function unwrapGoNamedType(node) {
208
492
  return current?.type === "type_identifier" ? current : null;
209
493
  }
210
494
  /** Type-like defs only, so a PHP `use function` alias cannot steal `Example::m()`. */
211
- function resolveNamedType(context, name, node) {
495
+ function resolveNamedType(context, name, node, rubyConstructed = false) {
496
+ if (context.sup.id === "go" && context.optionalMemberTypes.has(node.type)) {
497
+ const qualified = context.resolveMemberChainTarget(node);
498
+ return qualified && declaresMembers(qualified) ? qualified : null;
499
+ }
212
500
  const target = context.resolveIdentifier(name, node);
213
501
  if (target && declaresMembers(target))
214
502
  return target;
215
503
  // A parameter/annotation type name is a closer scope binding than the class it names.
216
504
  const normalized = context.sup.normalizeIdentifier(name);
217
505
  const typed = context.moduleEntry.locals.filter((local) => context.sup.normalizeIdentifier(local.localName) === normalized && declaresMembers(local));
218
- return typed.length === 1 ? typed[0] : null;
506
+ if (typed.length === 1)
507
+ return typed[0];
508
+ const imported = context.aliasToTargetDef.get(name);
509
+ if (imported && declaresMembers(imported))
510
+ return imported;
511
+ // Only a constructed `Klass.new` type uses Ruby's bare-constant visibility, and
512
+ // only when this file does not already have several classes of that name.
513
+ // Inheritance names stay on resolveIdentifier so an unproven superclass is not invented.
514
+ if (!rubyConstructed || typed.length > 1)
515
+ return null;
516
+ return resolveRubyVisibleConstant(context.index, context.moduleEntry, context.sup, name);
517
+ }
518
+ /**
519
+ * Collection can attach an arrow to any same-name local when the binding site is
520
+ * not that local (`obj.helper = () => 1` beside `const helper = 1`). Prove that
521
+ * this function is the value of `fn.def` itself: the declarator name is that
522
+ * definition, or an identifier assignment resolves to it. Member and pattern
523
+ * left-hand sides do not prove a local binding.
524
+ */
525
+ function provesCallableBinding(context, fn) {
526
+ const parent = fn.node.parent;
527
+ if (!parent)
528
+ return false;
529
+ const start = fn.def.range.start.index;
530
+ const end = fn.def.range.end.index;
531
+ if (start === undefined || end === undefined)
532
+ return false;
533
+ const sameNodeRange = (candidate) => !!candidate && candidate.startIndex === fn.node.startIndex && candidate.endIndex === fn.node.endIndex;
534
+ if ((parent.type === "variable_declarator" ||
535
+ parent.type === "public_field_definition" ||
536
+ parent.type === "field_definition") &&
537
+ sameNodeRange(parent.childForFieldName("value"))) {
538
+ const bindingName = parent.childForFieldName("name") ?? parent.childForFieldName("property");
539
+ return (!!bindingName &&
540
+ (isIdentifierType(context.sup, bindingName.type) || context.propertyIdentifierTypes.includes(bindingName.type)) &&
541
+ bindingName.startIndex === start &&
542
+ bindingName.endIndex === end);
543
+ }
544
+ if (parent.type === "assignment_expression" && sameNodeRange(parent.childForFieldName("right"))) {
545
+ const left = parent.childForFieldName("left");
546
+ if (!left || !isIdentifierType(context.sup, left.type))
547
+ return false;
548
+ const resolved = context.resolveIdentifier(sliceText(left, context.source), left);
549
+ return !!resolved && defNodeId(resolved) === defNodeId(fn.def);
550
+ }
551
+ return false;
552
+ }
553
+ /**
554
+ * Whether a resolved member can be a `calls` target: a function, a class, or a binding the
555
+ * declaration pass proved holds a function. A plain value (`export const value = 1`) is not.
556
+ */
557
+ /** A JS/TS class is not callable without `new`; construction is an `instantiates` edge instead. */
558
+ function isCallTarget(context, def) {
559
+ return def.kind === SymbolKind.Function || !!context.nodes.get(defNodeId(def))?.callable;
219
560
  }
220
- export function emitFunctionBodyEdges(context, functionNodes) {
561
+ /**
562
+ * A member of a proven same-file TS namespace receiver, selected with navigation's container and
563
+ * overload rules. `undefined` means the receiver is not such a namespace.
564
+ */
565
+ function resolveTypeScriptNamespaceMember(context, call, receiverNode, property) {
566
+ if ((context.sup.id !== "ts" && context.sup.id !== "tsx") || !isIdentifierType(context.sup, receiverNode.type))
567
+ return undefined;
568
+ const receiver = context.resolveIdentifier(sliceText(receiverNode, context.source), receiverNode);
569
+ if (!receiver || fileIdentityKey(receiver.file) !== fileIdentityKey(context.moduleEntry.file))
570
+ return undefined;
571
+ const member = sliceText(property, context.source);
572
+ const candidates = findTypeScriptNamespaceMemberCandidates(context.moduleEntry.locals, member, receiver, context);
573
+ if (!candidates.length)
574
+ return undefined;
575
+ return typescriptSelectOverloadCandidate({
576
+ group: candidates,
577
+ tree: context.tree,
578
+ definitionOf: (candidate) => candidate,
579
+ declarationOf: (candidate) => {
580
+ const start = candidate.range.start.index ?? 0;
581
+ return context.tree.rootNode.descendantForIndex(start, candidate.range.end.index ?? start).parent;
582
+ },
583
+ source: context.source,
584
+ languageId: context.sup.id,
585
+ argumentCount: getCallArgumentCount({ languageId: context.sup.id, source: context.source, call }),
586
+ });
587
+ }
588
+ function recordTypeScriptNamespaceCall(context, call, access, fromId) {
589
+ const selected = resolveTypeScriptNamespaceMember(context, call, access.receiver, access.property);
590
+ if (selected === undefined)
591
+ return false;
592
+ if (selected && isCallTarget(context, selected))
593
+ recordDefEdge(context, fromId, selected, "calls", access.property);
594
+ return true;
595
+ }
596
+ /** `new N.C()` on a same-file TS namespace constructs the namespace's class. */
597
+ function recordTypeScriptNamespaceConstruction(context, node, target, fromId) {
598
+ if (!isMemberAccessNode(context.sup, target))
599
+ return false;
600
+ const { object, property } = getMemberAccessParts(context.sup, target);
601
+ if (!object || !property)
602
+ return false;
603
+ const selected = resolveTypeScriptNamespaceMember(context, node, object, property);
604
+ if (selected === undefined)
605
+ return false;
606
+ if (selected?.kind === SymbolKind.Class)
607
+ recordDefEdge(context, fromId, selected, "instantiates", property);
608
+ return true;
609
+ }
610
+ /** A member call on a `typeof import("spec")` binding names that module's export. */
611
+ function recordImportTypeCall(context, access, fromId) {
612
+ if (!isJsTsLanguage(context.sup.id))
613
+ return false;
614
+ const importType = receiverConstructorExpression(access.receiver, context.source, context.sup);
615
+ const specifier = importType?.type === "type_query" ? importTypeQuerySpecifier(importType) : null;
616
+ if (!specifier)
617
+ return false;
618
+ const member = sliceText(access.property, context.source);
619
+ const target = resolveImportTypeMember(context.index, context.moduleEntry.file, specifier, member);
620
+ if (target && isCallTarget(context, target))
621
+ recordDefEdge(context, fromId, target, "calls", access.property);
622
+ return true;
623
+ }
624
+ /** Swift `Foo()` / `Worker(name:)` is construction. Record `instantiates` and skip the call path. */
625
+ function recordSwiftCapitalizedConstruction(context, node, fromId) {
626
+ if (context.sup.id !== "swift")
627
+ return false;
628
+ const constructed = constructionTypeName(node, context.source, context.sup);
629
+ if (!constructed)
630
+ return false;
631
+ const name = sliceText(constructed, context.source);
632
+ // An inherited method can share a type's name; a deferred member lookup decides after the walk.
633
+ if (context.resolveName(name, constructed)?.status === "deferred")
634
+ return false;
635
+ const target = context.resolveIdentifier(name, constructed);
636
+ if (!target || !declaresMembers(target))
637
+ return false;
638
+ recordDefEdge(context, fromId, target, "instantiates", constructed);
639
+ return true;
640
+ }
641
+ export async function emitFunctionBodyEdges(context, functionNodes) {
642
+ const qualifiedConstructionTargets = [];
643
+ // C# calls through a dotted receiver (`P.Mix.M()`, `Imported.Outer.Inner.M()`): navigation's
644
+ // member-access resolution decides the target after the walk, so both consumers agree.
645
+ const dottedReceiverCalls = [];
221
646
  const callNodeTypes = new Set([
222
647
  "call_expression",
223
648
  "call",
@@ -235,15 +660,21 @@ export function emitFunctionBodyEdges(context, functionNodes) {
235
660
  "struct_expression",
236
661
  "composite_literal",
237
662
  ]);
238
- // Receiver typing and lexical member lookup are only needed once a receiver call
239
- // fails the cheaper identifier and import-chain resolution, so both are lazy.
663
+ // Receiver typing and lexical member lookup are initialized only for receiver calls.
664
+ const receiverProofs = new Map();
665
+ const hasLexicalBinding = (callee) => {
666
+ const scope = context.index.scopeCache.get(fileIdentityKey(context.moduleEntry.file));
667
+ return !scope || !!findClosestScopeBinding(scope, sliceText(callee, context.source), callee, context.sup);
668
+ };
240
669
  let membersByContainer;
241
670
  const lexicalMembers = (container) => {
242
671
  if (!membersByContainer) {
243
672
  membersByContainer = new Map();
244
673
  for (const candidate of functionNodes) {
674
+ if (!isClassMemberFunction(candidate))
675
+ continue;
245
676
  const owner = nearestMemberContainer(candidate.node);
246
- if (!owner)
677
+ if (!owner || !isDirectKeywordMemberDeclaration(candidate.node, owner))
247
678
  continue;
248
679
  const members = membersByContainer.get(owner.startIndex);
249
680
  if (members)
@@ -254,10 +685,21 @@ export function emitFunctionBodyEdges(context, functionNodes) {
254
685
  }
255
686
  return membersByContainer.get(container.startIndex) ?? [];
256
687
  };
257
- const receiverProofs = new Map();
258
688
  for (const fn of functionNodes) {
689
+ const phpCaseInsensitive = context.sup.id === "php";
259
690
  const fromId = ensureNode(context, fn.def);
691
+ const provenNode = context.nodes.get(fromId);
692
+ // Function-valued bindings (`const helper = () => 1`) keep their `variable` kind;
693
+ // the callable metadata records that this binding was proven to hold a function.
694
+ if (provenNode && provenNode.kind !== "function" && provesCallableBinding(context, fn)) {
695
+ provenNode.callable = true;
696
+ context.noteCallableName(fn.name, context.sup.id === "php");
697
+ }
260
698
  const seenAliases = new Set();
699
+ // Calls whose target needs an async member lookup (C++ out-of-line owner members, included
700
+ // C/C++ declarations of one callable); settled after the walk.
701
+ const deferredCalls = [];
702
+ const qualifiedCppCalls = [];
261
703
  const nestedFunctions = new Set(functionNodes
262
704
  .filter((candidate) => candidate.node !== fn.node &&
263
705
  candidate.node.startIndex >= fn.node.startIndex &&
@@ -267,11 +709,32 @@ export function emitFunctionBodyEdges(context, functionNodes) {
267
709
  if (context.membersOnly || !isIdentifierType(context.sup, node.type))
268
710
  return;
269
711
  const name = sliceText(node, context.source);
712
+ if (context.sup.id === "php") {
713
+ // Resolve the occurrence's namespace; a plain-name map cannot distinguish PHP roles.
714
+ const importType = inferPhpQualifiedReferenceImportType(node) ?? "const";
715
+ const aliasName = importType === "const" ? name : foldPhpIdentifierCase(name);
716
+ const seenKey = `${importType}:${aliasName}`;
717
+ if (seenAliases.has(seenKey))
718
+ return;
719
+ seenAliases.add(seenKey);
720
+ const phpImport = findPhpImportAlias(context.moduleEntry.imports, name, importType);
721
+ if (!phpImport || typeof phpImport.resolved !== "string")
722
+ return;
723
+ const resolved = resolvePhpExportByImportType(context.index, phpImport.resolved, phpImport.imported, importType);
724
+ if (resolved?.kind === "resolved")
725
+ recordDefEdge(context, fromId, resolved.def, "uses");
726
+ return;
727
+ }
270
728
  if (seenAliases.has(name))
271
729
  return;
272
730
  let target = context.aliasToTargetDef.get(name) ?? null;
273
731
  if (!target) {
274
- const modFile = context.aliasToTargetModule.get(name);
732
+ // A local variable can shadow a Go package alias (`u := LocalU{}` alongside
733
+ // `import u "pkg"`); aliasToTargetModule is a blind per-file text map, so refuse it
734
+ // here too whenever a closer, non-namespace scope binding owns the name.
735
+ const modFile = context.sup.id === "go" && context.hasNonModuleBinding(name, node)
736
+ ? undefined
737
+ : context.aliasToTargetModule.get(name);
275
738
  if (modFile) {
276
739
  let exportedName = null;
277
740
  const parent = node.parent;
@@ -284,7 +747,7 @@ export function emitFunctionBodyEdges(context, functionNodes) {
284
747
  }
285
748
  if (exportedName) {
286
749
  target = context.resolveExportFrom(modFile, exportedName);
287
- if (!target) {
750
+ if (!target && (context.sup.id !== "go" || isGoExportedMemberName(context.sup.id, exportedName))) {
288
751
  const targetModule = context.index.byFile.get(fileIdentityKey(modFile));
289
752
  target = (targetModule?.locals ?? []).find((local) => local.localName === exportedName) ?? null;
290
753
  }
@@ -309,17 +772,32 @@ export function emitFunctionBodyEdges(context, functionNodes) {
309
772
  * alongside the caller resolve here; anything needing another module's members
310
773
  * becomes a deferred candidate.
311
774
  */
312
- const recordReceiverCall = (node, access) => {
775
+ const recordReceiverCall = (node, access, forcedMemberScope) => {
313
776
  const memberName = sliceText(access.property, context.source);
314
- if (!memberName || !context.hasCallableNamed(memberName))
777
+ if (!memberName)
315
778
  return;
316
- const binding = classifyReceiver(context.sup, access.receiver, context.source, receiverProofs, fn.node.startIndex, access.accessNode);
779
+ let binding = classifyReceiver(context.sup, access.receiver, context.source, receiverProofs, fn.node.startIndex, access.accessNode, hasLexicalBinding);
780
+ if (isJsTsLanguage(context.sup.id) && isIdentifierType(context.sup, access.receiver.type)) {
781
+ const receiverName = sliceText(access.receiver, context.source);
782
+ const imported = effectiveExplicitBinding(context.moduleEntry.imports, context.sup.id, (candidate) => candidate.kind === "namespace" && candidate.localNS === receiverName, access.receiver.startIndex) ?? innermostNamespaceImport(context.moduleEntry.imports, receiverName, access.receiver);
783
+ if (imported?.mechanism === "cjs" && typeof imported.resolved === "string") {
784
+ const value = cjsRequireValueBinding(context.index, imported.resolved);
785
+ const lexical = value ? context.resolveIdentifier(receiverName, access.receiver) : null;
786
+ if (!value || !declaresMembers(value) || !lexical || defNodeId(value) !== defNodeId(lexical))
787
+ return;
788
+ binding = { kind: "named-type", typeName: receiverName, typeNode: access.receiver, memberScope: "static" };
789
+ }
790
+ }
317
791
  if (!binding)
318
792
  return;
319
793
  const site = { file: context.moduleEntry.file, range: toRange(access.property) };
320
- const argumentCount = callArgumentCount(node);
794
+ // Shared call-count facts treat unproven spread expansions as unknown (null),
795
+ // so overload selection never fabricates an argument count.
796
+ const argumentCount = supportsReceiverMemberOverloads(context.sup.id)
797
+ ? getCallArgumentCount({ languageId: context.sup.id, source: context.source, call: node })
798
+ : null;
321
799
  if (binding.kind === "named-type") {
322
- const typeDef = resolveNamedType(context, binding.typeName, access.receiver);
800
+ const typeDef = resolveNamedType(context, binding.typeName, binding.typeNode, binding.constructed);
323
801
  if (!typeDef)
324
802
  return;
325
803
  context.receiverCalls.push({
@@ -329,26 +807,89 @@ export function emitFunctionBodyEdges(context, functionNodes) {
329
807
  memberName,
330
808
  argumentCount,
331
809
  site,
810
+ memberScope: forcedMemberScope ?? binding.memberScope,
811
+ ...(context.sup.id === "go" && !isGoExportedMemberName(context.sup.id, memberName)
812
+ ? { goPackagePeerFiles: getCompilationUnitPeers(context.index, context.moduleEntry.file).files }
813
+ : {}),
814
+ caseInsensitiveMemberName: phpCaseInsensitive,
332
815
  });
333
816
  return;
334
817
  }
335
818
  if (binding.kind === "own-type") {
336
819
  const container = nearestMemberContainer(fn.node);
337
820
  const declared = container
338
- ? lexicalMembers(container).filter((candidate) => candidate.def.localName === memberName)
821
+ ? lexicalMembers(container).filter((candidate) => {
822
+ const candidateName = candidate.def.localName;
823
+ const nameMatches = phpCaseInsensitive
824
+ ? foldPhpIdentifierCase(candidateName) === foldPhpIdentifierCase(memberName)
825
+ : candidateName === memberName;
826
+ if (!nameMatches)
827
+ return false;
828
+ if (binding.memberScope === "any")
829
+ return true;
830
+ return declarationNodeIsStatic(candidate.node, context.source) === (binding.memberScope === "static");
831
+ })
339
832
  : [];
340
833
  if (declared.length === 1) {
341
- recordDefEdge(context, fromId, declared[0].def, "calls", access.property);
342
- return;
834
+ const unique = declared[0];
835
+ recordDefEdge(context, fromId, unique.def, "calls", access.property);
836
+ // File-hidden Swift members stay off cross-file shared owners. Skipping the
837
+ // coalesced deferred lookup keeps a same-container private call from being stripped.
838
+ if (isSwiftFileHiddenSharedOwnerMember(context.sup.id, unique.node))
839
+ return;
343
840
  }
344
841
  }
842
+ const receiverContainer = nearestMemberContainer(access.accessNode);
843
+ const receiverContainerName = receiverContainer?.childForFieldName("name");
844
+ const receiverOwnerDef = receiverContainerName
845
+ ? context.moduleEntry.locals.find((local) => local.range.start.index === receiverContainerName.startIndex && declaresMembers(local))
846
+ : undefined;
345
847
  context.receiverCalls.push({
346
848
  callerId: fromId,
347
- ownerId: null,
849
+ ownerId: receiverOwnerDef ? ensureNode(context, receiverOwnerDef) : null,
348
850
  viaSupertypes: binding.kind === "supertype",
349
851
  memberName,
350
852
  argumentCount,
351
853
  site,
854
+ memberScope: binding.memberScope,
855
+ caseInsensitiveMemberName: phpCaseInsensitive,
856
+ });
857
+ };
858
+ /**
859
+ * A bare call inside a member function carries no receiver syntax, but in
860
+ * languages where that implicitly targets `this` it can still name an
861
+ * inherited member. Deferred like `recordReceiverCall`'s own-type/supertype
862
+ * candidates so cross-file base-type members resolve once every module's
863
+ * `member_of` and hierarchy edges are known.
864
+ */
865
+ const recordImplicitSelfMemberCall = (node, callee, fallbackDef = null) => {
866
+ if (!supportsImplicitSelfMemberCalls(context.sup.id) || !isIdentifierType(context.sup, callee.type))
867
+ return;
868
+ const container = nearestMemberContainer(fn.node);
869
+ const containerName = container?.childForFieldName("name");
870
+ const ownerDef = containerName
871
+ ? context.moduleEntry.locals.find((local) => local.range.start.index === containerName.startIndex && declaresMembers(local))
872
+ : undefined;
873
+ if (!ownerDef)
874
+ return;
875
+ const memberName = sliceText(callee, context.source);
876
+ if (!memberName)
877
+ return;
878
+ const argumentCount = supportsReceiverMemberOverloads(context.sup.id)
879
+ ? getCallArgumentCount({ languageId: context.sup.id, source: context.source, call: node })
880
+ : null;
881
+ context.receiverCalls.push({
882
+ callerId: fromId,
883
+ ownerId: ensureNode(context, ownerDef),
884
+ viaSupertypes: false,
885
+ memberName,
886
+ argumentCount,
887
+ site: { file: context.moduleEntry.file, range: toRange(callee) },
888
+ // A static caller can only reach a static bare member; an instance caller
889
+ // may bare-call either, matching C#'s unqualified invocation rules.
890
+ memberScope: nodeInStaticMemberContext(fn.node, context.source) ? "static" : "any",
891
+ caseInsensitiveMemberName: phpCaseInsensitive,
892
+ fallbackTargetId: fallbackDef ? ensureNode(context, fallbackDef) : undefined,
352
893
  });
353
894
  };
354
895
  /**
@@ -360,14 +901,94 @@ export function emitFunctionBodyEdges(context, functionNodes) {
360
901
  const resolveCallTarget = (node, callee) => {
361
902
  const access = receiverCallAccess(context.sup, node, callee);
362
903
  if (access) {
363
- if (!tryResolveChain(context, access.accessNode, fromId, "calls"))
904
+ if (recordTypeScriptNamespaceCall(context, node, access, fromId))
905
+ return;
906
+ if (recordImportTypeCall(context, access, fromId))
907
+ return;
908
+ const receiverName = sliceText(access.receiver, context.source);
909
+ const typeScopedCppCall = context.sup.id === "cpp" &&
910
+ context.source.slice(access.receiver.endIndex, access.property.startIndex).includes("::");
911
+ if (typeScopedCppCall) {
912
+ const qualifiedName = cppQualifiedNameSegments(access.accessNode, context.source).join("::");
913
+ const qualifiedTarget = context.resolveIdentifier(qualifiedName, access.property);
914
+ // Whether the owner is a class, and whether the caller has an implicit `this` of that class,
915
+ // needs the exact qualified path, so the decision waits until the function body is walked.
916
+ qualifiedCppCalls.push({
917
+ node,
918
+ access,
919
+ target: qualifiedTarget,
920
+ ownerPath: qualifiedName.split("::").slice(0, -1),
921
+ });
922
+ return;
923
+ }
924
+ if (keywordReceiverKind(context.sup.id, receiverName)) {
364
925
  recordReceiverCall(node, access);
926
+ return;
927
+ }
928
+ if (tryResolveChain(context, access.accessNode, fromId, "calls"))
929
+ return;
930
+ if (context.sup.id === "csharp" &&
931
+ access.receiver.type === "member_access_expression" &&
932
+ csharpDottedNameRoot(access.receiver)) {
933
+ dottedReceiverCalls.push({ fromId, access });
934
+ return;
935
+ }
936
+ recordReceiverCall(node, access);
937
+ return;
938
+ }
939
+ if (!callee)
365
940
  return;
941
+ // A name whose lookup waits on an async member step (implicit `this`/`self`, C++ out-of-line
942
+ // owners, included C/C++ declarations) settles after the walk exactly as navigation does.
943
+ if (isIdentifierType(context.sup, callee.type)) {
944
+ const name = sliceText(callee, context.source);
945
+ const resolution = context.resolveName(name, callee);
946
+ if (resolution?.status === "deferred") {
947
+ deferredCalls.push({ callee, name, resolution });
948
+ return;
949
+ }
950
+ }
951
+ const implicitOwnerLanguage = context.sup.id === "swift" || context.sup.id === "csharp";
952
+ if (implicitOwnerLanguage && isIdentifierType(context.sup, callee.type) && nearestMemberContainer(fn.node)) {
953
+ const name = sliceText(callee, context.source);
954
+ const lexical = context.resolveIdentifier(name, callee);
955
+ const importedCsharpMember = context.sup.id === "csharp" &&
956
+ !!lexical?.isMember &&
957
+ fileIdentityKey(lexical.file) !== fileIdentityKey(context.moduleEntry.file);
958
+ if (!importedCsharpMember && (!lexical || lexical.isMember || !context.hasNonModuleBinding(name, callee))) {
959
+ // A method's local binding wins. Type members require receiver ownership
960
+ // and static-scope proof; only a free function can be a fallback.
961
+ recordImplicitSelfMemberCall(node, callee, lexical?.isMember ? null : lexical);
962
+ return;
963
+ }
366
964
  }
367
- if (callee)
368
- tryResolveNode(context, callee, fromId, "calls");
965
+ if (!tryResolveNode(context, callee, fromId, "calls"))
966
+ recordImplicitSelfMemberCall(node, callee);
967
+ };
968
+ const recordRubySuper = (superNode) => {
969
+ const container = nearestMemberContainer(fn.node);
970
+ if (!container || container.type !== "class")
971
+ return;
972
+ const owner = memberContainerDef(context.moduleEntry, container);
973
+ if (!owner)
974
+ return;
975
+ context.receiverCalls.push({
976
+ callerId: fromId,
977
+ ownerId: ensureNode(context, owner),
978
+ viaSupertypes: true,
979
+ memberName: fn.def.localName,
980
+ argumentCount: null,
981
+ site: { file: context.moduleEntry.file, range: toRange(superNode) },
982
+ memberScope: "any",
983
+ });
369
984
  };
370
985
  const recordCallOrInstantiation = (node) => {
986
+ if (context.sup.id === "ruby" && node.type === "super") {
987
+ recordRubySuper(node);
988
+ return true;
989
+ }
990
+ if (node.type === "token_tree")
991
+ recordRustMacroArgumentCalls(context, node, fromId);
371
992
  if (callNodeTypes.has(node.type)) {
372
993
  if (context.sup.id === "go") {
373
994
  const callTarget = getCallTarget(node);
@@ -386,20 +1007,53 @@ export function emitFunctionBodyEdges(context, functionNodes) {
386
1007
  const receiverNode = node.childForFieldName("receiver");
387
1008
  const methodName = methodNode ? sliceText(methodNode, context.source) : null;
388
1009
  if (methodName === "new" && receiverNode) {
389
- tryResolveNode(context, receiverNode, fromId, "instantiates");
1010
+ const recorded = tryResolveNode(context, receiverNode, fromId, "instantiates");
1011
+ if (!recorded) {
1012
+ const rubyType = resolveRubyVisibleConstant(context.index, context.moduleEntry, context.sup, sliceText(receiverNode, context.source));
1013
+ if (rubyType)
1014
+ recordDefEdge(context, fromId, rubyType, "instantiates", receiverNode);
1015
+ }
390
1016
  return false;
391
1017
  }
1018
+ if (methodNode?.type === "super")
1019
+ return true;
392
1020
  if (methodNode) {
393
1021
  resolveCallTarget(node, methodNode);
394
1022
  return false;
395
1023
  }
1024
+ const callee = getCallTarget(node);
1025
+ if (callee?.type === "super")
1026
+ return true;
1027
+ resolveCallTarget(node, callee);
1028
+ return false;
396
1029
  }
1030
+ if (recordSwiftCapitalizedConstruction(context, node, fromId))
1031
+ return true;
397
1032
  resolveCallTarget(node, getCallTarget(node));
398
1033
  }
399
1034
  if (newNodeTypes.has(node.type)) {
400
- const target = getNewTarget(node);
401
- if (target)
402
- tryResolveNode(context, target, fromId, "instantiates");
1035
+ const keyword = phpObjectCreationKeyword(node, context.source, context.sup);
1036
+ if (keyword) {
1037
+ const created = resolvePhpObjectCreationTarget(context.index, context.moduleEntry, keyword, context.source, context.sup);
1038
+ if (created)
1039
+ recordDefEdge(context, fromId, created, "instantiates", keyword.nameNode);
1040
+ }
1041
+ else {
1042
+ const target = constructionTypeName(node, context.source, context.sup) ?? getNewTarget(node);
1043
+ if (target) {
1044
+ if (recordTypeScriptNamespaceConstruction(context, node, target, fromId))
1045
+ return true;
1046
+ const property = isMemberAccessNode(context.sup, target)
1047
+ ? getMemberAccessParts(context.sup, target).property
1048
+ : null;
1049
+ if ((context.sup.id === "java" || context.sup.id === "csharp") && property) {
1050
+ qualifiedConstructionTargets.push({ fromId, member: property, target });
1051
+ }
1052
+ else {
1053
+ tryResolveNode(context, target, fromId, "instantiates");
1054
+ }
1055
+ }
1056
+ }
403
1057
  }
404
1058
  return true;
405
1059
  };
@@ -413,11 +1067,55 @@ export function emitFunctionBodyEdges(context, functionNodes) {
413
1067
  walkFunctionBody(child, allowChildCallProcessing);
414
1068
  };
415
1069
  walkFunctionBody(fn.node, true);
1070
+ for (const call of deferredCalls) {
1071
+ const target = await context.settleName(call.name, call.callee, call.resolution);
1072
+ // Swift `Foo()` whose deferred lookup settles on a type is construction.
1073
+ const label = context.sup.id === "swift" && target && declaresMembers(target) ? "instantiates" : "calls";
1074
+ if (target)
1075
+ recordDefEdge(context, fromId, target, label, call.callee);
1076
+ }
1077
+ for (const call of qualifiedCppCalls) {
1078
+ const owner = call.ownerPath.length
1079
+ ? await resolveCppQualifiedMemberContainer(context.index, context.moduleEntry, call.ownerPath, context.loadParsedFile)
1080
+ : null;
1081
+ if (!owner) {
1082
+ // A namespace (or no) qualifier names the free function directly.
1083
+ if (call.target)
1084
+ recordDefEdge(context, fromId, call.target, "calls", call.access.property);
1085
+ continue;
1086
+ }
1087
+ const implicitThis = await cppQualifiedOwnerHasImplicitThis(context.index, context.moduleEntry, call.access.accessNode, context.source, context.sup, owner);
1088
+ if (implicitThis && call.target) {
1089
+ recordDefEdge(context, fromId, call.target, "calls", call.access.property);
1090
+ }
1091
+ else {
1092
+ // Without an implicit `this` of the owner, `Owner::member()` can only name a static member.
1093
+ recordReceiverCall(call.node, call.access, implicitThis ? undefined : "static");
1094
+ }
1095
+ }
1096
+ }
1097
+ for (const { fromId, access } of dottedReceiverCalls) {
1098
+ const member = await context.resolveMemberAccessTarget(access.property);
1099
+ // The graph records no edge for a call its target cannot accept, as for other member calls.
1100
+ if (member &&
1101
+ (await memberAcceptsCallAt(context.index, member, access.accessNode, context.source, context.sup.id))) {
1102
+ recordDefEdge(context, fromId, member, "calls", access.property);
1103
+ }
1104
+ }
1105
+ for (const target of qualifiedConstructionTargets) {
1106
+ const resolved = await context.resolveMemberAccessTarget(target.member);
1107
+ if (resolved) {
1108
+ recordDefEdge(context, target.fromId, resolved, "instantiates", target.member);
1109
+ }
1110
+ else {
1111
+ tryResolveNode(context, target.target, target.fromId, "instantiates");
1112
+ }
416
1113
  }
417
1114
  }
418
1115
  /** Qualifiers name a container, not another base type. */
419
1116
  const QUALIFIER_NAME_FIELD = {
420
1117
  qualified_name: "name", // C#: Namespace.Base, Outer.Inner
1118
+ alias_qualified_name: "name", // C#: X::Base, global::Namespace.Base
421
1119
  qualified_identifier: "name", // C++: ns::Base
422
1120
  scope_resolution: "name", // Ruby: Module::Base
423
1121
  qualified_type: "name", // Go: pkg.Base
@@ -504,9 +1202,19 @@ function narrowBaseSpecifierNode(node) {
504
1202
  }
505
1203
  /** Collect one type identifier per direct base or interface specifier. */
506
1204
  function collectBaseSpecifierIdentifiers(node, sup, out) {
1205
+ if (isUnprovenHeritageExpression(node))
1206
+ return;
507
1207
  if (BASE_TYPE_IGNORED_TYPES[node.type])
508
1208
  return;
1209
+ if (sup.id === "cpp" && node.type === "qualified_identifier") {
1210
+ out.push(node);
1211
+ return;
1212
+ }
509
1213
  const narrowed = narrowBaseSpecifierNode(node);
1214
+ if ((sup.id === "ts" || sup.id === "tsx") && narrowed.type === "member_expression") {
1215
+ out.push(narrowed);
1216
+ return;
1217
+ }
510
1218
  if (isIdentifierType(sup, narrowed.type) || narrowed.type === "type_identifier") {
511
1219
  out.push(narrowed);
512
1220
  return;
@@ -514,19 +1222,39 @@ function collectBaseSpecifierIdentifiers(node, sup, out) {
514
1222
  for (const child of narrowed.namedChildren ?? [])
515
1223
  collectBaseSpecifierIdentifiers(child, sup, out);
516
1224
  }
517
- function recordIdentifierRelations(context, fromId, container, relationForTarget) {
1225
+ async function recordIdentifierRelations(context, fromId, container, relationForTarget) {
518
1226
  const identifiers = [];
519
1227
  collectBaseSpecifierIdentifiers(container, context.sup, identifiers);
520
1228
  const seen = new Set();
521
1229
  for (const [index, identifier] of identifiers.entries()) {
522
- const target = context.resolveIdentifier(sliceText(identifier, context.source), identifier);
1230
+ let target;
1231
+ if (context.sup.id === "cpp" && identifier.type === "qualified_identifier") {
1232
+ const qualifiedPath = cppQualifiedNameSegments(identifier, context.source);
1233
+ target = await resolveCppQualifiedMemberContainer(context.index, context.moduleEntry, qualifiedPath, context.loadParsedFile);
1234
+ if (!target && qualifiedPath.length > 1) {
1235
+ target = context.resolveIdentifier(qualifiedPath.join("::"), identifier);
1236
+ }
1237
+ }
1238
+ else if ((context.sup.id === "ts" || context.sup.id === "tsx") && identifier.type === "member_expression") {
1239
+ const qualified = context.resolveMemberChainTarget(identifier);
1240
+ target = qualified && declaresMembers(qualified) ? qualified : null;
1241
+ }
1242
+ else {
1243
+ const name = sliceText(identifier, context.source);
1244
+ target = context.resolveIdentifier(name, identifier);
1245
+ // Ruby `require` publishes a top-level constant, not a nested one. Reuse the
1246
+ // same visibility as goto so `class Worker < Base` follows the required class.
1247
+ if (!target && context.sup.id === "ruby") {
1248
+ target = resolveRubyVisibleConstant(context.index, context.moduleEntry, context.sup, name);
1249
+ }
1250
+ }
523
1251
  if (!target)
524
1252
  continue;
525
1253
  const targetId = defNodeId(target);
526
1254
  if (seen.has(targetId))
527
1255
  continue;
528
1256
  seen.add(targetId);
529
- recordDefEdge(context, fromId, target, relationForTarget(target, index), identifier);
1257
+ recordDefEdge(context, fromId, target, relationForTarget(target, index, identifier), identifier);
530
1258
  }
531
1259
  }
532
1260
  const RUBY_NESTED_SCOPE_TYPES = new Set(["class", "module", "method", "singleton_method", "block", "do_block"]);
@@ -540,68 +1268,24 @@ function collectDirectCallsExcludingNestedScopes(node, out) {
540
1268
  collectDirectCallsExcludingNestedScopes(child, out);
541
1269
  }
542
1270
  }
543
- const TYPESCRIPT_INHERITANCE_RULES = {
544
- clauses: [
545
- // The value field is the superclass expression; the sibling type_arguments
546
- // field holds super-call type arguments, which are not base types.
547
- { type: "extends_clause", label: "extends", field: "value" },
548
- { type: "implements_clause", label: "implements" },
549
- ],
550
- };
551
- /**
552
- * Per-language class hierarchy forms. The grammar and the language runtime are
553
- * the only sources of these node names: JavaScript emits `class_heritage` for
554
- * `extends`, while TypeScript emits `extends_clause` and `implements_clause`.
555
- */
556
- const INHERITANCE_RULES = {
557
- js: { clauses: [{ type: "class_heritage", label: "extends" }] },
558
- ts: TYPESCRIPT_INHERITANCE_RULES,
559
- tsx: TYPESCRIPT_INHERITANCE_RULES,
560
- java: {
561
- clauses: [
562
- { type: "superclass", label: "extends" },
563
- { type: "super_interfaces", label: "implements" },
564
- ],
565
- },
566
- csharp: { clauses: [{ type: "base_list", label: "superclass-first" }] },
567
- kotlin: { clauses: [{ type: "delegation_specifiers", label: "superclass-first" }] },
568
- swift: { clauses: [{ type: "inheritance_specifier", label: "superclass-first", each: true }] },
569
- python: { clauses: [{ type: "argument_list", label: "extends" }] },
570
- php: {
571
- clauses: [
572
- { type: "base_clause", label: "extends" },
573
- { type: "class_interface_clause", label: "implements" },
574
- { type: "use_declaration", label: "trait", each: true },
575
- ],
576
- },
577
- ruby: {
578
- clauses: [{ type: "superclass", label: "extends" }],
579
- mixinCalls: ["include", "extend", "prepend"],
580
- },
581
- cpp: { clauses: [{ type: "base_class_clause", label: "extends" }] },
582
- go: {
583
- clauses: [],
584
- embeds: [
585
- { body: "interface_type", member: "type_elem" },
586
- {
587
- body: "struct_type",
588
- memberList: "field_declaration_list",
589
- member: "field_declaration",
590
- typeField: "type",
591
- nameless: true,
592
- },
593
- ],
594
- },
595
- };
596
- function baseClauseRelation(label, target, index, interfaceIds) {
1271
+ function baseClauseRelation(label, target, index, interfaceIds, identifier, kotlin) {
597
1272
  if (label !== "superclass-first")
598
1273
  return label;
1274
+ if (kotlin) {
1275
+ let current = identifier.parent;
1276
+ while (current && current.type !== "delegation_specifiers") {
1277
+ if (current.type === "constructor_invocation")
1278
+ return "extends";
1279
+ current = current.parent;
1280
+ }
1281
+ return "implements";
1282
+ }
599
1283
  if (interfaceIds.has(defNodeId(target)) || index > 0)
600
1284
  return "implements";
601
1285
  return "extends";
602
1286
  }
603
1287
  /** Records `implements` for every embedded type of a Go interface or struct declaration. */
604
- function recordEmbedRelations(context, fromId, declaration, embeds) {
1288
+ async function recordEmbedRelations(context, fromId, declaration, embeds) {
605
1289
  const declaredType = declaration.childForFieldName("type");
606
1290
  if (!declaredType)
607
1291
  return;
@@ -619,12 +1303,12 @@ function recordEmbedRelations(context, fromId, declaration, embeds) {
619
1303
  const specifier = rule.typeField ? member.childForFieldName(rule.typeField) : member;
620
1304
  if (!specifier)
621
1305
  continue;
622
- recordIdentifierRelations(context, fromId, specifier, () => "implements");
1306
+ await recordIdentifierRelations(context, fromId, specifier, () => "implements");
623
1307
  }
624
1308
  }
625
1309
  }
626
- export function emitClassInheritanceEdges(context, classNodes) {
627
- const rules = INHERITANCE_RULES[context.sup.id];
1310
+ export async function emitClassInheritanceEdges(context, classNodes) {
1311
+ const rules = MEMBER_ACCESS_ROWS[context.sup.id]?.receiverAncestry;
628
1312
  if (!rules)
629
1313
  return;
630
1314
  const interfaceIds = new Set(classNodes
@@ -635,20 +1319,27 @@ export function emitClassInheritanceEdges(context, classNodes) {
635
1319
  .map((candidate) => defNodeId(candidate.def)));
636
1320
  for (const cls of classNodes) {
637
1321
  const fromId = ensureNode(context, cls.def);
638
- markImplementationTarget(context, fromId, cls.node, cls.def);
1322
+ markImplementationTarget(context, fromId, cls.node, context.source, cls.def);
1323
+ if ((context.sup.id === "ts" || context.sup.id === "tsx") && cls.node.type === "class_declaration") {
1324
+ const heritage = cls.node.namedChildren.find((child) => child.type === "class_heritage");
1325
+ const superclass = heritage?.namedChildren.find((child) => child.type === "extends_clause");
1326
+ // TypeScript classes have one superclass. Invalid multi-base syntax proves neither target.
1327
+ if (superclass && superclass.namedChildren.length > 1)
1328
+ continue;
1329
+ }
639
1330
  for (const rule of rules.clauses) {
640
1331
  const clauses = [];
641
1332
  if (rule.each) {
642
- collectNodesByType(cls.node, rule.type, clauses);
1333
+ collectNodesByType(cls.node, rule.nodeType, clauses);
643
1334
  }
644
1335
  else {
645
- const found = findFirstNodeByType(cls.node, rule.type);
1336
+ const found = findFirstNodeByType(cls.node, rule.nodeType);
646
1337
  if (found)
647
1338
  clauses.push(found);
648
1339
  }
649
1340
  for (const clause of clauses) {
650
1341
  const specifiers = rule.field ? (clause.childForFieldName(rule.field) ?? clause) : clause;
651
- recordIdentifierRelations(context, fromId, specifiers, (target, index) => baseClauseRelation(rule.label, target, index, interfaceIds));
1342
+ await recordIdentifierRelations(context, fromId, specifiers, (target, index, identifier) => baseClauseRelation(rule.relation, target, index, interfaceIds, identifier, context.sup.id === "kotlin"));
652
1343
  }
653
1344
  }
654
1345
  if (rules.mixinCalls) {
@@ -663,11 +1354,11 @@ export function emitClassInheritanceEdges(context, classNodes) {
663
1354
  continue;
664
1355
  const args = call.childForFieldName("arguments");
665
1356
  if (args)
666
- recordIdentifierRelations(context, fromId, args, () => "mixin");
1357
+ await recordIdentifierRelations(context, fromId, args, () => "mixin");
667
1358
  }
668
1359
  }
669
1360
  if (rules.embeds)
670
- recordEmbedRelations(context, fromId, cls.node, rules.embeds);
1361
+ await recordEmbedRelations(context, fromId, cls.node, rules.embeds);
671
1362
  }
672
1363
  }
673
1364
  export function emitRustImplEdges(context, rootNode) {