@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,22 +1,40 @@
1
1
  import { supportForFileWithoutHeaderSample } from "../languages.js";
2
2
  import { ensureParsedContext } from "./parse-context.js";
3
- import { resolveMemberAccessDefinition, supportsReceiverMemberNavigation } from "./navigation-goto.js";
4
- import { findClosestBinding, findDeclarationNameNode, getOrBuildScopeIndex, resolveNamedDefinition, } from "./navigation-local.js";
3
+ import { getCompilationUnitPeers, IMPLICIT_UNIT_LANGUAGES } from "./compilation-units.js";
4
+ import { getReverseNeighbors, graphAdjacencyFor } from "../graphs/adjacency.js";
5
+ import { isGoExportedMemberName } from "./declaration-visibility.js";
6
+ import { memberSyntaxNamesFreeFunction } from "../util/member-access-tables.js";
7
+ import { phpObjectCreationKeyword } from "../graphs/symbol-graph-detailed/receiver-calls.js";
8
+ import { findCsharpPartialTypeEquivalents, innermostNamespaceImport, resolveMemberAccessDefinition, resolvePhpObjectCreationTarget, resolveRubySuperDefinition, sharedOwnerMemberUnitComplete, supportsReceiverMemberNavigation, } from "./navigation-goto.js";
9
+ import { findClosestBinding, findClosestScopeBinding, findDeclarationNameNode, getOrBuildScopeIndex, toModuleRef, } from "./navigation-local.js";
10
+ import { typescriptCallableCandidatesInContainer } from "./ts-callables.js";
11
+ import { isExplicitMethodCall, scopeNodesFor } from "./scope-nodes.js";
12
+ import { AMBIGUOUS_CPP_OVERLOAD_REASON } from "./ambiguous-resolution.js";
13
+ import { effectiveExplicitBinding, findRubyReopenedConstantParts, isExpandedStarBinding, resolveStarImportedDefinition, resolveStarImportedNamespace, } from "./star-import-precedence.js";
5
14
  import { createNavigationProvenance, okGoToResult } from "./navigation-provenance.js";
6
- import { getPhpQualifiedReference, inferPhpQualifiedReferenceImportType, normalizePhpQualifiedReference, } from "./navigation-php.js";
7
- import { buildIndexedCandidateCoverage, buildPhpQualifiedNames, collectVerifiedNamedNodeReferences, getCachedScope, exportFromIdentifier, getCachedReferenceCandidateFiles, getCandidateReferenceNames, hasExpandedNamedImport, importBindingDeclarationRangeKeys, importBindingIdentityVerificationSites, importBindingReferenceSites, rangeIdentityKey, referenceSiteKey, } from "./navigation-references.js";
8
- import { resolveExport, resolveImported } from "./navigation-resolve.js";
15
+ import { getPhpQualifiedReference, inferPhpQualifiedReferenceImportType, isPhpCaseInsensitiveSymbolKind, normalizePhpQualifiedReference, phpLastIdentifierSegment, } from "./navigation-php.js";
16
+ import { ensurePhpNamespaceSymbolIndex, phpReferenceRoleMatchesKind } from "./php-namespace-symbols.js";
17
+ import { buildIndexedCandidateCoverage, buildPhpQualifiedNames, cppCanonicalStructuralExport, describeReferenceStrategies, collectVerifiedNamedNodeReferences, isMemberAccessPropertyRange, getCachedScope, exportFromIdentifier, getCachedReferenceCandidateFiles, getCandidateReferenceNames, hasExpandedNamedImport, pythonParentPackageNamespacePaths, importBindingDeclarationRangeKeys, importBindingIdentityVerificationSites, importBindingReferenceSites, rangeIdentityKey, referenceSiteKey, } from "./navigation-references.js";
18
+ import { directModuleValueEntry, resolveExport, resolveImported, resolveModuleExports, resolvePhpExportByImportType, } from "./navigation-resolve.js";
9
19
  import { extractEnclosingBlock, extractLineContext, rangeContains, sameDef } from "./reference-context.js";
10
20
  import { DEFAULT_REF_CONTEXT_LINES } from "./shared.js";
11
- import {} from "./scope.js";
12
21
  import {} from "../types.js";
13
22
  import { loadNearestTsconfigFor, resolveImportSpecifier } from "../util/resolution.js";
14
23
  import { fileIdentityKey } from "../util/paths.js";
15
24
  import { sliceText, toRange } from "../util/ast.js";
25
+ import { foldPhpIdentifierCase } from "../util/identifiers.js";
16
26
  import { getMemberAccessParts, isMemberAccessNode, isMemberObjectIdentifier, isMemberReferencePropertyIdentifier, isReceiverNameNode, } from "../util/member-access.js";
27
+ import { cppOutOfLineOwnerPath, cppOutOfLineMemberName, cppOutOfLineMemberDeclarationNode, cppQualifiedNameSegments, } from "../graphs/symbol-graph-detailed/receiver-calls.js";
28
+ import { cppCallableShapeForNode, cppEquivalentCallableBindings } from "./cpp-callables.js";
29
+ import { resolveCppExportedCallables, resolveCppQualifiedMemberContainer } from "./navigation-cpp.js";
17
30
  import { SymbolKind, } from "./types.js";
18
31
  import { findSqlReferences, goToSqlDefinition } from "../sql/navigation.js";
32
+ import { createLoadingParsedFileProvider, phpImportTypeAtPosition, resolveBareName, settleNameResolution, withParsedFiles, } from "./name-resolution.js";
33
+ import { resolveCppQualifiedName } from "./name-lookup-policies/c-family.js";
19
34
  export { resolveExport, resolveImported } from "./navigation-resolve.js";
35
+ const CPP_MEMBER_CONTAINER_TYPES = new Set(["class_specifier", "struct_specifier", "union_specifier"]);
36
+ const MAX_REFERENCE_NAMESPACE_DEPTH = 8;
37
+ const MAX_REFERENCE_NAMESPACE_PATHS = 64;
20
38
  export async function goToDefinition(index, req, parsedContext) {
21
39
  const { file, line, column } = req;
22
40
  const mod = index.byFile.get(fileIdentityKey(file));
@@ -30,11 +48,20 @@ export async function goToDefinition(index, req, parsedContext) {
30
48
  const sup = context.sup;
31
49
  const source = context.source;
32
50
  const tree = context.tree;
51
+ if (sup.id === "php")
52
+ await ensurePhpNamespaceSymbolIndex(index);
33
53
  const pos = {
34
54
  row: Math.max(0, line - 1),
35
55
  column: Math.max(0, column - 1),
36
56
  };
37
57
  let node = tree.rootNode.descendantForPosition(pos, pos);
58
+ // A position at the start of `b` in `a?.b` also touches the end of the `?.` token, which the
59
+ // parser reports first. The use is the name that starts at the position.
60
+ if (node?.type === "optional_chain" && node.parent) {
61
+ const startsHere = node.parent.namedChildren.find((child) => child.startPosition.row === pos.row && child.startPosition.column === pos.column);
62
+ if (startsHere)
63
+ node = startsHere;
64
+ }
38
65
  if (node && node.type === "variable_declarator") {
39
66
  const value = node.childForFieldName("value");
40
67
  if (value && value.type === "call_expression") {
@@ -52,6 +79,21 @@ export async function goToDefinition(index, req, parsedContext) {
52
79
  node = node.parent;
53
80
  if (!node)
54
81
  return { status: "not_found", reason: "No node at position" };
82
+ if (sup.id === "ruby" && node.type === "super") {
83
+ const target = await resolveRubySuperDefinition(index, mod, node, source, sup);
84
+ if (!target)
85
+ return { status: "not_found", reason: "No matching Ruby superclass method" };
86
+ return okGoToResult(index, target, { resolution: "member-access", confidence: "medium" });
87
+ }
88
+ if (sup.id === "php") {
89
+ const keyword = phpObjectCreationKeyword(node, source, sup);
90
+ if (keyword) {
91
+ const created = resolvePhpObjectCreationTarget(index, mod, keyword, source, sup);
92
+ if (!created)
93
+ return { status: "not_found", reason: "No matching PHP class" };
94
+ return okGoToResult(index, created, { resolution: "member-access", confidence: "medium" });
95
+ }
96
+ }
55
97
  const shorthandId = sup.nodeTypes.shorthandPropertyIdentifier ?? [];
56
98
  const isId = sup.nodeTypes.identifier.includes(node.type) || shorthandId.includes(node.type);
57
99
  let name = isId ? sliceText(node, source) : null;
@@ -62,6 +104,9 @@ export async function goToDefinition(index, req, parsedContext) {
62
104
  name = sliceText(declNameNode, source);
63
105
  }
64
106
  }
107
+ const phpImportType = sup.id === "php"
108
+ ? (phpImportTypeAtPosition(mod.imports, pos.row, pos.column) ?? inferPhpQualifiedReferenceImportType(node))
109
+ : undefined;
65
110
  if (node && sup.supportsExportFromReferences && index.projectRoot) {
66
111
  const exportFrom = exportFromIdentifier(index, file, toRange(node), context);
67
112
  if (exportFrom?.entry) {
@@ -93,28 +138,60 @@ export async function goToDefinition(index, req, parsedContext) {
93
138
  }
94
139
  }
95
140
  if (sup.supportsCrossModuleSymbols) {
96
- const scopeIndex = node.parent && isMemberAccessNode(sup, node.parent)
97
- ? getOrBuildScopeIndex(index, file, source, sup, mod, tree)
98
- : null;
99
- const memberAccessResult = await resolveMemberAccessDefinition({
100
- index,
101
- mod,
102
- node,
103
- source,
104
- sup,
105
- ...(scopeIndex
106
- ? {
107
- resolveLexicalBinding: (receiver) => {
108
- if (!isReceiverNameNode(sup, receiver.type))
109
- return null;
110
- return findClosestBinding(scopeIndex, file, sliceText(receiver, source), receiver, sup);
111
- },
112
- }
113
- : {}),
114
- });
141
+ let memberAccessNode = null;
142
+ if (node.parent && isMemberAccessNode(sup, node.parent)) {
143
+ memberAccessNode = node.parent;
144
+ }
145
+ else if (node.parent?.type === "template_function" &&
146
+ node.parent.parent &&
147
+ isMemberAccessNode(sup, node.parent.parent)) {
148
+ memberAccessNode = node.parent.parent;
149
+ }
150
+ if (sup.id === "cpp") {
151
+ while (memberAccessNode?.parent && isMemberAccessNode(sup, memberAccessNode.parent)) {
152
+ memberAccessNode = memberAccessNode.parent;
153
+ }
154
+ }
155
+ // A receiverless call (Java `hit()`) reuses the member-call node with the name in both slots;
156
+ // it is a bare name, so the shared lookup (implicit members, imports, package) decides it.
157
+ const receiverless = !!memberAccessNode && isReceiverlessMemberCall(sup, memberAccessNode);
158
+ const scopeIndex = memberAccessNode ? getOrBuildScopeIndex(index, file, source, sup, mod, tree) : null;
159
+ const memberAccessResult = !receiverless &&
160
+ !isExplicitMethodCall(scopeNodesFor(sup.id), node) &&
161
+ (await resolveMemberAccessDefinition({
162
+ index,
163
+ mod,
164
+ node,
165
+ source,
166
+ tree,
167
+ sup,
168
+ ...(scopeIndex
169
+ ? {
170
+ resolveLexicalBinding: (receiver) => {
171
+ if (!isReceiverNameNode(sup, receiver.type))
172
+ return null;
173
+ const receiverName = sliceText(receiver, source);
174
+ const binding = findClosestScopeBinding(scopeIndex, receiverName, receiver, sup);
175
+ if (binding?.kind === "importDefault" ||
176
+ binding?.kind === "importNamed" ||
177
+ binding?.kind === "namespace") {
178
+ return null;
179
+ }
180
+ return findClosestBinding(scopeIndex, file, receiverName, receiver, sup, source, tree);
181
+ },
182
+ }
183
+ : {}),
184
+ }));
115
185
  if (memberAccessResult) {
116
186
  return memberAccessResult;
117
187
  }
188
+ if (sup.id === "cpp" && scopeIndex && memberAccessNode) {
189
+ const files = createLoadingParsedFileProvider(index, { file, parsed: context });
190
+ const qualifiedName = cppQualifiedNameSegments(memberAccessNode, source).join("::");
191
+ const qualified = await withParsedFiles(files, () => resolveCppQualifiedName({ index, mod, file, parsed: context, scopeIndex, files, node, name: qualifiedName }));
192
+ if (qualified)
193
+ return qualified;
194
+ }
118
195
  if (isUnresolvedReceiverMemberProperty(sup, node)) {
119
196
  return { status: "not_found", reason: "No matching receiver member definition" };
120
197
  }
@@ -122,23 +199,14 @@ export async function goToDefinition(index, req, parsedContext) {
122
199
  if (sup.id === "php" && phpQualifiedReference && index.projectRoot) {
123
200
  const normalizedQualifiedReference = normalizePhpQualifiedReference(phpQualifiedReference, source, tree, node);
124
201
  if (normalizedQualifiedReference?.includes("\\")) {
125
- const phpImportType = inferPhpQualifiedReferenceImportType(node);
202
+ const qualifiedImportType = phpImportType;
126
203
  const resolvedTarget = await resolveImportSpecifier(index.projectRoot, file, normalizedQualifiedReference, "php", {
127
- ...(phpImportType ? { phpImportType } : {}),
204
+ ...(qualifiedImportType ? { phpImportType: qualifiedImportType } : {}),
128
205
  });
129
206
  if (typeof resolvedTarget === "string") {
130
207
  const exportedName = normalizedQualifiedReference.split("\\").filter(Boolean).pop() ?? null;
131
208
  if (exportedName) {
132
- let preferredKind;
133
- if (phpImportType === "function") {
134
- preferredKind = SymbolKind.Function;
135
- }
136
- else if (phpImportType === "class") {
137
- preferredKind = SymbolKind.Class;
138
- }
139
- const hit = resolveExport(index, resolvedTarget, exportedName, {
140
- ...(preferredKind ? { preferredKind } : {}),
141
- });
209
+ const hit = resolvePhpExportByImportType(index, resolvedTarget, exportedName, qualifiedImportType);
142
210
  if (hit?.kind === "resolved") {
143
211
  return okGoToResult(index, hit.def, {
144
212
  via: { importedFrom: resolvedTarget, exportedName },
@@ -152,24 +220,26 @@ export async function goToDefinition(index, req, parsedContext) {
152
220
  }
153
221
  if (name) {
154
222
  const scopeIndex = getOrBuildScopeIndex(index, file, source, sup, mod, tree);
155
- const local = findClosestBinding(scopeIndex, file, name, node, sup);
156
- if (local) {
157
- return okGoToResult(index, local, {
158
- resolution: "exact",
159
- confidence: "high",
160
- });
161
- }
162
- if (sup.supportsCrossModuleSymbols) {
163
- const resolvedName = resolveNamedDefinition(index, mod, file, sup, name);
164
- if (resolvedName) {
165
- return resolvedName;
166
- }
167
- }
223
+ const files = createLoadingParsedFileProvider(index, { file, parsed: context });
224
+ const use = {
225
+ index,
226
+ mod,
227
+ file,
228
+ parsed: context,
229
+ scopeIndex,
230
+ files,
231
+ node,
232
+ name,
233
+ ...(phpImportType ? { phpImportType } : {}),
234
+ };
235
+ const resolution = await withParsedFiles(files, () => resolveBareName(use));
236
+ const settled = await settleNameResolution(use, resolution, {
237
+ recoverIncludedStar: (lookupName, cNamespace) => recoverIncludedCallableStar(index, mod, sup.id, lookupName, cNamespace, node, source),
238
+ });
239
+ if (settled)
240
+ return settled;
168
241
  }
169
- const localAtPosition = mod.locals.find((local) => rangeContains(local.range, {
170
- row: line,
171
- column: column,
172
- }));
242
+ const localAtPosition = mod.locals.find((local) => local.range.start.index === node.startIndex && local.range.end.index === node.endIndex);
173
243
  if (localAtPosition) {
174
244
  return okGoToResult(index, localAtPosition, {
175
245
  resolution: "exact",
@@ -181,9 +251,23 @@ export async function goToDefinition(index, req, parsedContext) {
181
251
  reason: "No matching local or imported definition",
182
252
  };
183
253
  }
254
+ /** Whether a member-access node has no receiver distinct from its member name. */
255
+ function isReceiverlessMemberCall(sup, access) {
256
+ // Java `<T>hit()` has no `object` field; the generic parts fall back to the type arguments.
257
+ if (access.type === "method_invocation")
258
+ return !access.childForFieldName("object");
259
+ const { object, property } = getMemberAccessParts(sup, access);
260
+ return !!property && (!object || object.startIndex === property.startIndex);
261
+ }
262
+ function swiftNavigationMemberAccess(sup, node) {
263
+ if (sup.id !== "swift" || node.parent?.type !== "navigation_suffix")
264
+ return null;
265
+ const access = node.parent.parent;
266
+ return access && isMemberAccessNode(sup, access) ? access : null;
267
+ }
184
268
  function isUnresolvedReceiverMemberProperty(sup, node) {
185
- const parent = node.parent;
186
- if (!parent || !supportsReceiverMemberNavigation(sup.id) || !isMemberAccessNode(sup, parent)) {
269
+ const parent = swiftNavigationMemberAccess(sup, node) ?? node.parent;
270
+ if (!parent || (!supportsReceiverMemberNavigation(sup.id) && sup.id !== "c") || !isMemberAccessNode(sup, parent)) {
187
271
  return false;
188
272
  }
189
273
  const { object, property } = getMemberAccessParts(sup, parent);
@@ -195,6 +279,7 @@ function isUnresolvedReceiverMemberProperty(sup, node) {
195
279
  // Namespace and nested-type qualification still resolve through the language
196
280
  // paths below; only value-receiver members must not fall back to a bare name.
197
281
  if (parent.type === "qualified_name" ||
282
+ parent.type === "alias_qualified_name" ||
198
283
  parent.type === "qualified_identifier" ||
199
284
  parent.type === "qualified_type" ||
200
285
  parent.type === "scoped_identifier" ||
@@ -232,6 +317,110 @@ export async function findUsageReferences(index, req, opts) {
232
317
  export async function findRenameReferences(index, def, opts) {
233
318
  return findReferencesInternal(index, { def }, opts, "rename");
234
319
  }
320
+ /**
321
+ * Proven equivalent callable definition family for one definition site.
322
+ *
323
+ * Returns the incoming definition first — enriched to its indexed SymbolDef when the definition
324
+ * file's module.locals holds an exact identity match (same file, kind, localName, and full range
325
+ * span), restoring metadata such as `isMember` — followed by every equivalent
326
+ * declaration/definition that reference collection itself proves: same-scope prototype/definition
327
+ * pairs (C and C++), C prototypes linked to definitions through an include, namespace-qualified
328
+ * equivalents, the in-class declaration for out-of-line
329
+ * member definitions, and out-of-line definitions for in-class member declarations linked through
330
+ * the export index. Matching stays conservative: candidates must already exist in the definition
331
+ * file's scope bindings or the export index with a proven signature; no name-only or arity-only
332
+ * equivalence is introduced, and overload sets are never collapsed.
333
+ *
334
+ * `parsedContext` is optional; when omitted the definition file is parsed on demand. Definitions
335
+ * outside C/C++ return just the enriched definition.
336
+ */
337
+ /**
338
+ * Two included declarations of one C/C++ function are one callable, using
339
+ * {@link getCppEquivalentCallableDefinitions}. Different signatures stay an
340
+ * overload set and are chosen by call arity. Non-callables (typedefs, structs)
341
+ * are left to the star-import ambiguity result.
342
+ */
343
+ export async function recoverIncludedCallableStar(index, mod, languageId, name, cNamespace, node, source) {
344
+ const candidates = [];
345
+ for (const imp of mod.imports) {
346
+ if (isExpandedStarBinding(imp, mod.imports) || imp.kind !== "star")
347
+ continue;
348
+ const def = resolveStarImportedDefinition(index, imp, name, languageId, cNamespace);
349
+ if (def)
350
+ candidates.push({ imp, def });
351
+ }
352
+ const functions = candidates.filter((candidate) => candidate.def.kind === SymbolKind.Function && !candidate.def.cTag);
353
+ if (functions.length < 2)
354
+ return null;
355
+ const familyOf = new Map();
356
+ const family = async (def) => {
357
+ const key = referenceSiteKey(def.file, def.range);
358
+ const cached = familyOf.get(key);
359
+ if (cached)
360
+ return cached;
361
+ const defs = await getCppEquivalentCallableDefinitions(index, def);
362
+ familyOf.set(key, defs);
363
+ return defs;
364
+ };
365
+ const parent = functions.map((_, candidateIndex) => candidateIndex);
366
+ const find = (start) => {
367
+ let current = start;
368
+ while (parent[current] !== current) {
369
+ const next = parent[current];
370
+ parent[current] = parent[next] ?? next;
371
+ current = next;
372
+ }
373
+ return current;
374
+ };
375
+ const unite = (left, right) => {
376
+ const leftRoot = find(left);
377
+ const rightRoot = find(right);
378
+ if (leftRoot !== rightRoot)
379
+ parent[rightRoot] = leftRoot;
380
+ };
381
+ for (let left = 0; left < functions.length; left += 1) {
382
+ const defs = await family(functions[left].def);
383
+ for (let right = left + 1; right < functions.length; right += 1) {
384
+ if (defs.some((item) => sameDef(item, functions[right].def, index.languageExtensions)))
385
+ unite(left, right);
386
+ }
387
+ }
388
+ const roots = new Set(functions.map((_, candidateIndex) => find(candidateIndex)));
389
+ if (roots.size === 1) {
390
+ const chosen = functions[0];
391
+ const importedFrom = toModuleRef(chosen.imp.resolved);
392
+ return okGoToResult(index, chosen.def, {
393
+ via: { ...(importedFrom ? { importedFrom } : {}), exportedName: name },
394
+ resolution: "import-star",
395
+ confidence: "medium",
396
+ });
397
+ }
398
+ const parsedByFile = new Map();
399
+ for (const candidate of functions) {
400
+ const key = fileIdentityKey(candidate.def.file);
401
+ if (parsedByFile.has(key))
402
+ continue;
403
+ try {
404
+ parsedByFile.set(key, await ensureParsedContext(candidate.def.file, index.parsed?.get(key), index.languageExtensions));
405
+ }
406
+ catch {
407
+ return languageId === "cpp"
408
+ ? { status: "not_found", reason: AMBIGUOUS_CPP_OVERLOAD_REASON }
409
+ : { status: "not_found", reason: "No matching local or imported definition" };
410
+ }
411
+ }
412
+ const selected = resolveCppExportedCallables(index, functions.map((candidate) => candidate.def), node, source, (targetFile) => parsedByFile.get(fileIdentityKey(targetFile)) ?? null);
413
+ if (selected) {
414
+ return okGoToResult(index, selected, { resolution: "exact", confidence: "high" });
415
+ }
416
+ return languageId === "cpp"
417
+ ? { status: "not_found", reason: AMBIGUOUS_CPP_OVERLOAD_REASON }
418
+ : { status: "not_found", reason: "No matching local or imported definition" };
419
+ }
420
+ export async function getCppEquivalentCallableDefinitions(index, def, parsedContext) {
421
+ const family = await cppEquivalentCallableFamily(index, def, parsedContext);
422
+ return [family.definition, ...family.equivalents];
423
+ }
235
424
  async function findReferencesInternal(index, req, opts, collectionMode) {
236
425
  let def = null;
237
426
  let provenance;
@@ -260,6 +449,17 @@ async function findReferencesInternal(index, req, opts, collectionMode) {
260
449
  if (!def) {
261
450
  return { status: "not_found", reason: "Could not resolve definition" };
262
451
  }
452
+ if (def.cTag) {
453
+ const canonical = await goToDefinition(index, {
454
+ file: def.file,
455
+ line: def.range.start.line,
456
+ column: def.range.start.column,
457
+ });
458
+ if (canonical.status === "ok") {
459
+ def = canonical.definition;
460
+ provenance = canonical.provenance;
461
+ }
462
+ }
263
463
  const maxReferences = typeof opts?.maxReferences === "number" && opts.maxReferences > 0 ? opts.maxReferences : undefined;
264
464
  const sqlReferences = await findSqlReferences(index, def, {
265
465
  includeDefinition: collectionMode === "all",
@@ -293,16 +493,23 @@ async function findReferencesInternal(index, req, opts, collectionMode) {
293
493
  };
294
494
  const parsedDef = index.parsed?.get(fileIdentityKey(definitionFile));
295
495
  const parsedContext = await ensureParsedContext(definitionFile, parsedDef, index.languageExtensions);
496
+ let cNamespace;
497
+ if (parsedContext.sup.id === "c")
498
+ cNamespace = def.cTag ? "tag" : "ordinary";
499
+ const exportOptions = cNamespace ? { cNamespace } : undefined;
296
500
  const mod = index.byFile.get(fileIdentityKey(definitionFile));
297
501
  if (!mod)
298
502
  return { status: "not_found", reason: "Module not found" };
299
- const scope = getCachedScope(index, definitionFile, mod, parsedContext);
300
503
  const refs = [];
301
504
  const seenRefs = new Map();
302
505
  const collectionLimit = maxReferences !== undefined ? maxReferences + 1 : undefined;
303
506
  const hasReachedCollectionLimit = () => collectionLimit !== undefined && refs.length >= collectionLimit;
304
507
  const remainingCollectionSlots = () => collectionLimit !== undefined ? Math.max(0, collectionLimit - refs.length) : undefined;
305
- const importBindingRank = (ref) => (ref.via?.importBinding === "imported" ? 1 : 0);
508
+ const referenceMetadataRank = (ref) => {
509
+ if (ref.via?.importBinding === "imported")
510
+ return 2;
511
+ return ref.via?.namespaceMember ? 1 : 0;
512
+ };
306
513
  const pushRef = (ref) => {
307
514
  if (!includeReference(ref))
308
515
  return;
@@ -310,7 +517,7 @@ async function findReferencesInternal(index, req, opts, collectionMode) {
310
517
  const existingIndex = seenRefs.get(key);
311
518
  if (existingIndex !== undefined) {
312
519
  const existing = refs[existingIndex];
313
- if (importBindingRank(ref) > importBindingRank(existing)) {
520
+ if (referenceMetadataRank(ref) > referenceMetadataRank(existing)) {
314
521
  refs[existingIndex] = ref;
315
522
  }
316
523
  return;
@@ -320,29 +527,128 @@ async function findReferencesInternal(index, req, opts, collectionMode) {
320
527
  seenRefs.set(key, refs.length);
321
528
  refs.push(ref);
322
529
  };
323
- const normalizedLocalName = parsedContext.sup.normalizeIdentifier(def.localName);
324
- const localBindings = scope.bindings.get(normalizedLocalName) ?? [];
325
- const localBinding = localBindings.find((binding) => binding.def && binding.def.start.index === def.range.start.index);
326
- pushRef({ file: definitionFile, range: def.range });
327
- if (localBinding) {
530
+ const family = await cppEquivalentCallableFamily(index, def, parsedContext);
531
+ const definition = family.definition;
532
+ const referenceDef = family.referenceDef;
533
+ const localBinding = family.localBinding;
534
+ pushRef({ file: definitionFile, range: definition.range });
535
+ const receiverMemberDefinition = isReceiverMemberDefinition(definition, parsedContext, !!family.receiverOwner);
536
+ const csharpPartialEquivalents = parsedContext.sup.id === "csharp" &&
537
+ !definition.isMember &&
538
+ (definition.kind === SymbolKind.Class ||
539
+ definition.kind === SymbolKind.Interface ||
540
+ definition.kind === SymbolKind.TypeAlias)
541
+ ? await findCsharpPartialTypeEquivalents(index, definition)
542
+ : [];
543
+ const rubyReopen = parsedContext.sup.id === "ruby" ? findRubyReopenedConstantParts(index, definition) : undefined;
544
+ const equivalentDefinitions = [...family.equivalents, ...csharpPartialEquivalents, ...(rubyReopen?.parts ?? [])];
545
+ for (const equivalent of equivalentDefinitions) {
546
+ pushRef({ file: equivalent.file, range: equivalent.range });
547
+ }
548
+ const matchesReferenceDefinition = (candidate) => sameDef(candidate, definition, index.languageExtensions) ||
549
+ equivalentDefinitions.some((equivalent) => sameDef(candidate, equivalent, index.languageExtensions));
550
+ const exportedNames = [];
551
+ for (const candidate of [definition, ...equivalentDefinitions]) {
552
+ const candidateModule = index.byFile.get(fileIdentityKey(candidate.file));
553
+ for (const entry of candidateModule?.exports ?? []) {
554
+ if (entry.type === "local" &&
555
+ sameDef(entry.target, candidate, index.languageExtensions) &&
556
+ !exportedNames.includes(entry.exportedAs)) {
557
+ exportedNames.push(entry.exportedAs);
558
+ }
559
+ }
560
+ }
561
+ if (!exportedNames.length && !receiverMemberDefinition) {
562
+ exportedNames.push(definition.localName);
563
+ }
564
+ const exportedNameSet = new Set(exportedNames);
565
+ const phpQualifiedNames = await buildPhpQualifiedNames(index, definitionFile, definition);
566
+ const scansReceiverReferences = shouldScanVerifiedReferences(definition, parsedContext, receiverMemberDefinition);
567
+ const scansNamespaceReferences = parsedContext.sup.id === "csharp" &&
568
+ !definition.isMember &&
569
+ (definition.kind === SymbolKind.Class ||
570
+ definition.kind === SymbolKind.Interface ||
571
+ definition.kind === SymbolKind.TypeAlias);
572
+ let requiresTypeScriptOverloadVerifiedScan = false;
573
+ if ((parsedContext.sup.id === "ts" || parsedContext.sup.id === "tsx") &&
574
+ definition.kind === SymbolKind.Function &&
575
+ !receiverMemberDefinition &&
576
+ localBinding?.sameScopeFunctionBindings) {
577
+ const start = definition.range.start.index ?? 0;
578
+ const end = definition.range.end.index ?? start;
579
+ const overloadBindings = typescriptCallableCandidatesInContainer(localBinding.sameScopeFunctionBindings, parsedContext.tree, (binding) => binding.def, start, end);
580
+ requiresTypeScriptOverloadVerifiedScan = overloadBindings.length > 1;
581
+ }
582
+ const requiresSameFileVerifiedScan = ((parsedContext.sup.id === "c" || parsedContext.sup.id === "cpp") &&
583
+ definition.kind === SymbolKind.Function &&
584
+ !receiverMemberDefinition) ||
585
+ scansNamespaceReferences ||
586
+ requiresTypeScriptOverloadVerifiedScan;
587
+ let sameFileVerifiedScanExecuted = false;
588
+ const receiverProofUnavailableFiles = new Map();
589
+ let memberCallOccurrencesNeedVerification = false;
590
+ if (localBinding &&
591
+ localBinding.occurrencesComplete !== false &&
592
+ !requiresSameFileVerifiedScan &&
593
+ !scansReceiverReferences &&
594
+ !requiresTypeScriptOverloadVerifiedScan) {
595
+ const verifyMemberCalls = memberSyntaxNamesFreeFunction(parsedContext.sup.id);
328
596
  for (const occurrence of localBinding.occurrences) {
329
597
  if (hasReachedCollectionLimit())
330
598
  break;
599
+ // A scope occurrence is not proof of a member call. Languages that invoke a free
600
+ // function that way verify the site; an unresolvable receiver stays unavailable.
601
+ if (verifyMemberCalls && isMemberAccessPropertyRange(parsedContext, occurrence)) {
602
+ memberCallOccurrencesNeedVerification = true;
603
+ continue;
604
+ }
331
605
  pushRef({ file: definitionFile, range: occurrence });
332
606
  }
333
607
  }
334
- const exportedNames = [];
335
- for (const entry of mod.exports) {
336
- if (entry.type === "local" && sameDef(entry.target, def, index.languageExtensions)) {
337
- exportedNames.push(entry.exportedAs);
608
+ if (memberCallOccurrencesNeedVerification && !hasReachedCollectionLimit()) {
609
+ const ranges = await collectVerifiedNamedNodeReferences(index, definitionFile, referenceDef.localName, referenceDef, (params, parsed) => goToDefinition(index, params, parsed), remainingCollectionSlots(), verifiedReferenceFilter(definitionFile), (unavailableFile) => receiverProofUnavailableFiles.set(fileIdentityKey(unavailableFile), unavailableFile), equivalentDefinitions);
610
+ for (const { range, provenance, via } of ranges) {
611
+ if (hasReachedCollectionLimit())
612
+ break;
613
+ pushRef({ file: definitionFile, range, ...(via ? { via } : {}), ...(provenance ? { provenance } : {}) });
338
614
  }
339
615
  }
340
- if (!exportedNames.length && shouldUseLocalNameAsExportFallback(def, parsedContext)) {
341
- exportedNames.push(def.localName);
616
+ if (requiresSameFileVerifiedScan && !hasReachedCollectionLimit()) {
617
+ const ranges = await collectVerifiedNamedNodeReferences(index, definitionFile, referenceDef.localName, referenceDef, (params, parsed) => goToDefinition(index, params, parsed), remainingCollectionSlots(), verifiedReferenceFilter(definitionFile), (unavailableFile) => receiverProofUnavailableFiles.set(fileIdentityKey(unavailableFile), unavailableFile), equivalentDefinitions);
618
+ sameFileVerifiedScanExecuted = true;
619
+ for (const { range, provenance, via } of ranges) {
620
+ if (hasReachedCollectionLimit())
621
+ break;
622
+ pushRef({
623
+ file: definitionFile,
624
+ range,
625
+ ...(via ? { via } : {}),
626
+ ...(provenance ? { provenance } : {}),
627
+ });
628
+ }
342
629
  }
343
- const exportedNameSet = new Set(exportedNames);
344
- const phpQualifiedNames = await buildPhpQualifiedNames(index, definitionFile, def);
345
- let candidateFiles = getCachedReferenceCandidateFiles(index, def, exportedNames, !!phpQualifiedNames.length);
630
+ let candidateFiles = [
631
+ ...new Map([referenceDef, ...equivalentDefinitions]
632
+ .flatMap((candidate) => getCachedReferenceCandidateFiles(index, candidate, exportedNames, !!phpQualifiedNames.length, parsedContext.sup.id))
633
+ .map((candidateFile) => [fileIdentityKey(candidateFile), candidateFile])).values(),
634
+ ].sort((left, right) => left.localeCompare(right));
635
+ // Candidates that share a compilation unit with the definition (or an equivalent
636
+ // declaration) can name it without an import edge; the per-candidate scan below treats
637
+ // them as bare-name reference sources in addition to the import-derived branches.
638
+ const unitPeerKeys = new Set();
639
+ for (const candidate of [referenceDef, ...equivalentDefinitions]) {
640
+ for (const unitPeer of getCompilationUnitPeers(index, candidate.file, parsedContext.sup.id === "csharp" ? { csharpQualifiedName: true } : undefined).files) {
641
+ unitPeerKeys.add(fileIdentityKey(unitPeer));
642
+ }
643
+ }
644
+ // A bloom filter holds each candidate file's identifiers in that file's own spelling, and a
645
+ // probe can only test one spelling. PHP resolves class, interface, trait, enum, and function
646
+ // names case-insensitively, so `new \App\sErViCe()` must still match a `Service` definition.
647
+ // `buildBloomFilterFromSource` stores every PHP file's identifiers both in their own spelling
648
+ // and ASCII-case-folded, so folding the probe's last identifier segment here narrows those
649
+ // kinds exactly instead of skipping narrowing and walking every indexed PHP file. Variables,
650
+ // properties, and constants stay case-sensitive and probe with their own spelling.
651
+ const phpCaseInsensitiveDefinition = phpQualifiedNames.length && isPhpCaseInsensitiveSymbolKind(definition.kind);
346
652
  if (index.bloomFilters && phpQualifiedNames.length) {
347
653
  candidateFiles = candidateFiles.filter((candidateFile) => {
348
654
  const module = index.byFile.get(fileIdentityKey(candidateFile));
@@ -355,12 +661,62 @@ async function findReferencesInternal(index, req, opts, collectionMode) {
355
661
  const normalizeIdentifier = supportForFileWithoutHeaderSample(candidateFile, index.languageExtensions)?.normalizeIdentifier ??
356
662
  ((name) => name);
357
663
  const aliases = getCandidateReferenceNames(module, definitionFile, exportedNameSet);
358
- if (!aliases.length) {
359
- return [...exportedNames, ...phpQualifiedNames].some((candidateName) => filter.mightContain(normalizeIdentifier(candidateName)));
664
+ const probeNames = aliases.length ? aliases : [...exportedNames, ...phpQualifiedNames];
665
+ if (phpCaseInsensitiveDefinition) {
666
+ return probeNames.some((candidateName) => filter.mightContain(foldPhpIdentifierCase(normalizeIdentifier(phpLastIdentifierSegment(candidateName)))));
360
667
  }
361
- return aliases.some((alias) => filter.mightContain(normalizeIdentifier(alias)));
668
+ return probeNames.some((candidateName) => filter.mightContain(normalizeIdentifier(phpLastIdentifierSegment(candidateName))));
362
669
  });
363
670
  }
671
+ // A file reached only through a dynamic import, or another construct that never creates a
672
+ // modeled ImportBinding, still leaves a real file-dependency edge behind. Without this, such
673
+ // a file is never a scan candidate at all, so a genuine same-name use inside it is silently
674
+ // invisible instead of making coverage `partial` — exactly the gap F3 forbids. Member
675
+ // definitions already get every file scanned below, so this only adds files for the narrower,
676
+ // import-graph-driven search.
677
+ if (!definition.isMember && !scansReceiverReferences) {
678
+ const candidateFileKeys = new Set(candidateFiles.map((file) => fileIdentityKey(file)));
679
+ const adjacency = index.graphAdjacency ?? graphAdjacencyFor(index.graph);
680
+ const graphLinkedOrphans = new Map();
681
+ for (const candidate of [definition, ...equivalentDefinitions]) {
682
+ for (const importer of getReverseNeighbors(adjacency, candidate.file)) {
683
+ const key = fileIdentityKey(importer);
684
+ if (key === fileIdentityKey(definitionFile) || candidateFileKeys.has(key))
685
+ continue;
686
+ graphLinkedOrphans.set(key, importer);
687
+ }
688
+ }
689
+ // A star import can expose a namespace reexport rather than the member definition itself.
690
+ // Include direct star importers of that package so their namespace member use is verified.
691
+ const namespaceTargetKeys = new Set([definition, ...equivalentDefinitions].map((candidate) => fileIdentityKey(candidate.file)));
692
+ for (const namespaceOwner of index.byFile.values()) {
693
+ const reexportsDefinitionAsNamespace = namespaceOwner.exports.some((entry) => entry.type === "namespaceReexport" && namespaceTargetKeys.has(fileIdentityKey(entry.fromModule)));
694
+ if (!reexportsDefinitionAsNamespace)
695
+ continue;
696
+ const namespaceOwnerKey = fileIdentityKey(namespaceOwner.file);
697
+ if (namespaceOwnerKey !== fileIdentityKey(definitionFile) && !candidateFileKeys.has(namespaceOwnerKey)) {
698
+ graphLinkedOrphans.set(namespaceOwnerKey, namespaceOwner.file);
699
+ }
700
+ for (const importer of getReverseNeighbors(adjacency, namespaceOwner.file)) {
701
+ const key = fileIdentityKey(importer);
702
+ if (key === fileIdentityKey(definitionFile) || candidateFileKeys.has(key))
703
+ continue;
704
+ const importerModule = index.byFile.get(key);
705
+ const importsNamespaceOwner = importerModule?.imports.some((candidate) => candidate.kind === "star" &&
706
+ typeof candidate.resolved === "string" &&
707
+ fileIdentityKey(candidate.resolved) === namespaceOwnerKey);
708
+ if (importsNamespaceOwner)
709
+ graphLinkedOrphans.set(key, importer);
710
+ }
711
+ }
712
+ for (const [key, file] of graphLinkedOrphans) {
713
+ candidateFiles.push(file);
714
+ candidateFileKeys.add(key);
715
+ unitPeerKeys.add(key);
716
+ }
717
+ if (graphLinkedOrphans.size)
718
+ candidateFiles.sort((left, right) => left.localeCompare(right));
719
+ }
364
720
  for (const fileId of candidateFiles) {
365
721
  if (hasReachedCollectionLimit())
366
722
  break;
@@ -392,10 +748,10 @@ async function findReferencesInternal(index, req, opts, collectionMode) {
392
748
  if (!exportedNameSet.has(entry.sourceSpecifier))
393
749
  continue;
394
750
  const resolved = resolveExport(index, entry.fromModule, entry.sourceSpecifier);
395
- if (resolved?.kind === "resolved" && !sameDef(resolved.def, def, index.languageExtensions))
751
+ if (resolved?.kind === "resolved" && !matchesReferenceDefinition(resolved.def))
396
752
  continue;
397
753
  const remainingReferences = remainingCollectionSlots();
398
- const ranges = await collectVerifiedNamedNodeReferences(index, fileId, entry.sourceSpecifier, def, (params, parsed) => goToDefinition(index, params, parsed), remainingReferences, verifiedReferenceFilter(fileId));
754
+ const ranges = await collectVerifiedNamedNodeReferences(index, fileId, entry.sourceSpecifier, definition, (params, parsed) => goToDefinition(index, params, parsed), remainingReferences, verifiedReferenceFilter(fileId), (unavailableFile) => receiverProofUnavailableFiles.set(fileIdentityKey(unavailableFile), unavailableFile));
399
755
  for (const { range, provenance, via } of ranges) {
400
756
  if (hasReachedCollectionLimit())
401
757
  break;
@@ -408,6 +764,8 @@ async function findReferencesInternal(index, req, opts, collectionMode) {
408
764
  for (const imp of module.imports) {
409
765
  if (hasReachedCollectionLimit())
410
766
  break;
767
+ if (cNamespace && imp.kind === "named" && (imp.cNamespace ?? "ordinary") !== cNamespace)
768
+ continue;
411
769
  const targetFile = typeof imp.resolved === "string" ? imp.resolved : undefined;
412
770
  const bindingSites = importBindingReferenceSites(imp);
413
771
  let verifiedBindingMatches;
@@ -415,6 +773,11 @@ async function findReferencesInternal(index, req, opts, collectionMode) {
415
773
  if (verifiedBindingMatches !== undefined)
416
774
  return verifiedBindingMatches;
417
775
  verifiedBindingMatches = false;
776
+ if (imp.kind === "named" &&
777
+ imp.mechanism === "php" &&
778
+ !phpReferenceRoleMatchesKind(imp.phpImportType ?? "class", definition.kind)) {
779
+ return verifiedBindingMatches;
780
+ }
418
781
  const parsed = await ensureCandidateParsed();
419
782
  for (const verificationSite of importBindingIdentityVerificationSites(imp)) {
420
783
  const resolved = await goToDefinition(index, {
@@ -422,7 +785,7 @@ async function findReferencesInternal(index, req, opts, collectionMode) {
422
785
  line: verificationSite.range.start.line,
423
786
  column: verificationSite.range.start.column,
424
787
  }, parsed);
425
- if (resolved.status === "ok" && sameDef(resolved.definition, def, index.languageExtensions)) {
788
+ if (resolved.status === "ok" && matchesReferenceDefinition(resolved.definition)) {
426
789
  verifiedBindingMatches = true;
427
790
  break;
428
791
  }
@@ -443,20 +806,95 @@ async function findReferencesInternal(index, req, opts, collectionMode) {
443
806
  via: { import: imp, importBinding: site.importBinding },
444
807
  });
445
808
  }
809
+ if (!hasReachedCollectionLimit() && imp.kind === "named") {
810
+ const ranges = await collectVerifiedNamedNodeReferences(index, fileId, imp.local, definition, (params, parsed) => goToDefinition(index, params, parsed), remainingCollectionSlots(), verifiedReferenceFilter(fileId), (unavailableFile) => receiverProofUnavailableFiles.set(fileIdentityKey(unavailableFile), unavailableFile));
811
+ for (const { range, provenance, via } of ranges) {
812
+ if (hasReachedCollectionLimit())
813
+ break;
814
+ pushRef({
815
+ file: fileId,
816
+ range,
817
+ via: { import: imp, ...(via ?? {}) },
818
+ ...(provenance ? { provenance } : {}),
819
+ });
820
+ }
821
+ }
446
822
  continue;
447
823
  }
824
+ const cjsTargetModule = imp.kind === "namespace" && imp.mechanism === "cjs" ? index.byFile.get(fileIdentityKey(targetFile)) : undefined;
825
+ const cjsExportedName = cjsTargetModule && directModuleValueEntry(cjsTargetModule)?.exportedAs;
448
826
  for (const exportedName of exportedNames) {
449
827
  if (hasReachedCollectionLimit())
450
828
  break;
451
- if (imp.kind === "namespace") {
452
- const hit = resolveExport(index, targetFile, exportedName);
829
+ const cjsModuleValue = cjsExportedName === exportedName;
830
+ if (imp.kind === "namespace" && !cjsModuleValue) {
831
+ if (!isGoExportedMemberName(supportForFileWithoutHeaderSample(fileId, index.languageExtensions)?.id, exportedName)) {
832
+ continue;
833
+ }
834
+ const hit = resolveExport(index, targetFile, exportedName, exportOptions);
453
835
  const matchesDef = hit?.kind === "resolved"
454
- ? sameDef(hit.def, def, index.languageExtensions)
455
- : imp.kind === "namespace" && fileIdentityKey(targetFile) === fileIdentityKey(definitionFile);
456
- if (!matchesDef)
836
+ ? matchesReferenceDefinition(hit.def)
837
+ : imp.kind === "namespace" &&
838
+ [definition, ...equivalentDefinitions].some((candidate) => fileIdentityKey(targetFile) === fileIdentityKey(candidate.file));
839
+ // Python binds the first segment of an unaliased dotted import, while an alias
840
+ // binds the whole module. Other namespaces use their local name directly.
841
+ const namespaceSearchName = imp.mechanism === "python" && imp.from.includes(".") && !imp.explicitAlias ? imp.from : imp.localNS;
842
+ if (!matchesDef) {
843
+ const targetModule = index.byFile.get(fileIdentityKey(targetFile));
844
+ if (!targetModule?.exports.some((entry) => entry.type === "namespaceReexport" || entry.type === "exportStar")) {
845
+ continue;
846
+ }
847
+ // A namespace re-export exposes a member at paths such as W.helpers.helper,
848
+ // not as a bare export of the intermediate module. Follow only export-proven
849
+ // namespace edges, then verify each use through the same goto resolver.
850
+ const namespacePaths = [
851
+ { file: targetFile, name: namespaceSearchName, depth: 0 },
852
+ ];
853
+ for (let pathIndex = 0; pathIndex < namespacePaths.length; pathIndex++) {
854
+ if (pathIndex >= MAX_REFERENCE_NAMESPACE_PATHS) {
855
+ receiverProofUnavailableFiles.set(fileIdentityKey(fileId), fileId);
856
+ break;
857
+ }
858
+ const namespacePath = namespacePaths[pathIndex];
859
+ if (namespacePath.depth) {
860
+ const nestedHit = resolveExport(index, namespacePath.file, exportedName, {
861
+ allowLocalFallback: false,
862
+ ...(exportOptions ?? {}),
863
+ });
864
+ if (nestedHit?.kind === "resolved" && matchesReferenceDefinition(nestedHit.def)) {
865
+ const parsed = await ensureCandidateParsed();
866
+ const ranges = await collectNamespaceMemberRefs(fileId, namespacePath.name, exportedName, parsed, index.languageExtensions, imp, module.imports);
867
+ for (const range of ranges) {
868
+ if (hasReachedCollectionLimit())
869
+ break;
870
+ const proof = await goToDefinition(index, { file: fileId, line: range.start.line, column: range.start.column }, parsed);
871
+ if (proof.status !== "ok" || !matchesReferenceDefinition(proof.definition))
872
+ continue;
873
+ pushRef({ file: fileId, range, via: { import: imp, namespaceMember: exportedName } });
874
+ }
875
+ }
876
+ }
877
+ const exports = resolveModuleExports(index, namespacePath.file, { allowLocalFallback: false });
878
+ if (namespacePath.depth >= MAX_REFERENCE_NAMESPACE_DEPTH) {
879
+ if ([...exports.values()].some((entry) => entry.kind === "namespace")) {
880
+ receiverProofUnavailableFiles.set(fileIdentityKey(fileId), fileId);
881
+ }
882
+ continue;
883
+ }
884
+ for (const [alias, entry] of exports) {
885
+ if (entry.kind !== "namespace")
886
+ continue;
887
+ namespacePaths.push({
888
+ file: entry.file,
889
+ name: `${namespacePath.name}.${alias}`,
890
+ depth: namespacePath.depth + 1,
891
+ });
892
+ }
893
+ }
457
894
  continue;
895
+ }
458
896
  const parsed = await ensureCandidateParsed();
459
- const ranges = await collectNamespaceMemberRefs(fileId, imp.localNS, exportedName, parsed, index.languageExtensions);
897
+ const ranges = await collectNamespaceMemberRefs(fileId, namespaceSearchName, exportedName, parsed, index.languageExtensions, imp, module.imports);
460
898
  for (const range of ranges) {
461
899
  if (hasReachedCollectionLimit())
462
900
  break;
@@ -468,51 +906,130 @@ async function findReferencesInternal(index, req, opts, collectionMode) {
468
906
  }
469
907
  }
470
908
  else if (imp.kind === "star") {
471
- const result = resolveImported(index, imp, exportedName);
472
- const matchesDef = !!result && !("namespace" in result) && sameDef(result, def, index.languageExtensions);
473
- if (!matchesDef)
474
- continue;
475
- if (hasExpandedNamedImport(module, targetFile, exportedName)) {
909
+ const result = resolveImported(index, imp, exportedName, exportOptions);
910
+ const matchesDef = !!result && !("namespace" in result) && matchesReferenceDefinition(result);
911
+ const matchesStructural = cppCanonicalStructuralExport(index, targetFile, exportedName, definition, parsedContext.sup.id);
912
+ if (matchesDef || matchesStructural) {
913
+ if (hasExpandedNamedImport(module, targetFile, exportedName)) {
914
+ continue;
915
+ }
916
+ const remainingReferences = remainingCollectionSlots();
917
+ const ranges = await collectVerifiedNamedNodeReferences(index, fileId, parsedContext.sup.id === "cpp" || parsedContext.sup.id === "ruby"
918
+ ? (exportedName.split("::").pop() ?? exportedName)
919
+ : exportedName, definition, (params, parsed) => goToDefinition(index, params, parsed), remainingReferences, verifiedReferenceFilter(fileId), (unavailableFile) => receiverProofUnavailableFiles.set(fileIdentityKey(unavailableFile), unavailableFile), equivalentDefinitions);
920
+ for (const { range, provenance, via } of ranges) {
921
+ if (hasReachedCollectionLimit())
922
+ break;
923
+ pushRef({
924
+ file: fileId,
925
+ range,
926
+ via: { import: imp, ...(via ?? {}) },
927
+ ...(provenance ? { provenance } : {}),
928
+ });
929
+ }
476
930
  continue;
477
931
  }
478
- const remainingReferences = remainingCollectionSlots();
479
- const ranges = await collectVerifiedNamedNodeReferences(index, fileId, exportedName, def, (params, parsed) => goToDefinition(index, params, parsed), remainingReferences, verifiedReferenceFilter(fileId));
480
- for (const { range, provenance, via } of ranges) {
481
- if (hasReachedCollectionLimit())
482
- break;
483
- pushRef({
484
- file: fileId,
485
- range,
486
- via: { import: imp, ...(via ?? {}) },
487
- ...(provenance ? { provenance } : {}),
488
- });
932
+ // A star import can publish a namespace such as a package submodule. Resolve that
933
+ // namespace under the same precedence as navigation before attributing its member use.
934
+ const namespaceExports = resolveModuleExports(index, targetFile);
935
+ const parsed = await ensureCandidateParsed();
936
+ for (const [namespaceName, namespaceResult] of namespaceExports) {
937
+ if (namespaceResult.kind !== "namespace")
938
+ continue;
939
+ if (![definition, ...equivalentDefinitions].some((candidate) => fileIdentityKey(namespaceResult.file) === fileIdentityKey(candidate.file))) {
940
+ continue;
941
+ }
942
+ const namespaceDecision = resolveStarImportedNamespace(index, module, parsed.sup.id, namespaceName);
943
+ if (namespaceDecision.status !== "resolved" || namespaceDecision.imp !== imp)
944
+ continue;
945
+ const ranges = await collectNamespaceMemberRefs(fileId, namespaceName, exportedName, parsed, index.languageExtensions, undefined, module.imports);
946
+ for (const range of ranges) {
947
+ if (hasReachedCollectionLimit())
948
+ break;
949
+ pushRef({ file: fileId, range, via: { import: imp, namespaceMember: exportedName } });
950
+ }
489
951
  }
490
952
  }
491
953
  else {
492
954
  let exported = exportedName;
955
+ const importedLocalName = imp.kind === "namespace" ? imp.localNS : imp.local;
493
956
  if (imp.kind === "named") {
494
957
  exported = imp.imported;
495
958
  }
496
959
  else if (imp.kind === "default") {
497
960
  exported = "default";
498
961
  }
499
- const hit = resolveExport(index, targetFile, exported);
500
- let matchesDef = hit?.kind === "resolved" && sameDef(hit.def, def, index.languageExtensions);
962
+ const hit = resolveExport(index, targetFile, exported, exportOptions);
963
+ let matchesDef = hit?.kind === "resolved" && matchesReferenceDefinition(hit.def);
964
+ let attributedByProof = matchesDef;
501
965
  if (!matchesDef && bindingSites.length) {
502
966
  matchesDef = await bindingMatchesDefinition();
967
+ attributedByProof = matchesDef;
968
+ }
969
+ if (!matchesDef &&
970
+ cppCanonicalStructuralExport(index, targetFile, exported, definition, parsedContext.sup.id)) {
971
+ // Recover candidates, but prove each overload call through goToDefinition.
972
+ // Structural visibility alone cannot attribute the import token.
973
+ matchesDef = true;
974
+ }
975
+ // A python `from pkg import name` binds `name` from the package's own namespace.
976
+ // Usually that is a real re-exported symbol (handled above); when the package has no
977
+ // such export, Python's own import system falls back to treating `name` as an
978
+ // implicit submodule attribute instead. Reuse the namespace-style dotted scan the
979
+ // `imp.kind === "namespace"` branch above runs, rather than the bare-identifier scan
980
+ // this branch runs next, so a same-name use through the bound local still counts.
981
+ if (!matchesDef && imp.kind === "named" && imp.mechanism === "python") {
982
+ const importedResult = resolveImported(index, imp, exported, exportOptions);
983
+ const submoduleFile = importedResult && "namespace" in importedResult ? importedResult.namespace : undefined;
984
+ if (submoduleFile &&
985
+ [definition, ...equivalentDefinitions].some((candidate) => fileIdentityKey(submoduleFile) === fileIdentityKey(candidate.file))) {
986
+ const parsed = await ensureCandidateParsed();
987
+ const ranges = await collectNamespaceMemberRefs(fileId, imp.local, exportedName, parsed, index.languageExtensions, undefined, module.imports);
988
+ for (const range of ranges) {
989
+ if (hasReachedCollectionLimit())
990
+ break;
991
+ pushRef({ file: fileId, range, via: { import: imp, namespaceMember: exportedName } });
992
+ }
993
+ continue;
994
+ }
503
995
  }
504
- if (!matchesDef)
996
+ if (!matchesDef) {
997
+ // The requested export slot itself does not exist at all (as opposed to existing
998
+ // and structurally naming a different, already-proven definition): the binding may
999
+ // still be a whole-module handle onto this exact file under a shape resolution does
1000
+ // not model (a misclassified default import, for example). Verify every same-name
1001
+ // occurrence directly instead of silently treating the file as clean.
1002
+ if (!hasReachedCollectionLimit() &&
1003
+ hit?.kind !== "resolved" &&
1004
+ [definition, ...equivalentDefinitions].some((candidate) => fileIdentityKey(targetFile) === fileIdentityKey(candidate.file))) {
1005
+ const remainingReferences = remainingCollectionSlots();
1006
+ const ranges = await collectVerifiedNamedNodeReferences(index, fileId, exportedName, definition, (params, parsed) => goToDefinition(index, params, parsed), remainingReferences, verifiedReferenceFilter(fileId), (unavailableFile) => receiverProofUnavailableFiles.set(fileIdentityKey(unavailableFile), unavailableFile), equivalentDefinitions);
1007
+ for (const { range, provenance, via } of ranges) {
1008
+ if (hasReachedCollectionLimit())
1009
+ break;
1010
+ pushRef({
1011
+ file: fileId,
1012
+ range,
1013
+ via: { import: imp, ...(via ?? {}) },
1014
+ ...(provenance ? { provenance } : {}),
1015
+ });
1016
+ }
1017
+ }
505
1018
  continue;
506
- for (const site of bindingSites) {
507
- pushRef({
508
- file: fileId,
509
- range: site.range,
510
- via: { import: imp, importBinding: site.importBinding },
511
- });
512
1019
  }
513
- if (fileIdentityKey(targetFile) !== fileIdentityKey(definitionFile)) {
1020
+ if (attributedByProof) {
1021
+ for (const site of bindingSites) {
1022
+ pushRef({
1023
+ file: fileId,
1024
+ range: site.range,
1025
+ via: { import: imp, importBinding: site.importBinding },
1026
+ });
1027
+ }
1028
+ }
1029
+ const scansQualifiedCppImport = parsedContext.sup.id === "cpp" && imp.kind === "named" && imp.local.includes("::");
1030
+ if (imp.kind === "named" || fileIdentityKey(targetFile) !== fileIdentityKey(definitionFile)) {
514
1031
  const remainingReferences = remainingCollectionSlots();
515
- const ranges = await collectVerifiedNamedNodeReferences(index, fileId, imp.local, def, (params, parsed) => goToDefinition(index, params, parsed), remainingReferences, verifiedReferenceFilter(fileId));
1032
+ const ranges = await collectVerifiedNamedNodeReferences(index, fileId, scansQualifiedCppImport ? (importedLocalName.split("::").pop() ?? importedLocalName) : importedLocalName, definition, (params, parsed) => goToDefinition(index, params, parsed), remainingReferences, verifiedReferenceFilter(fileId), (unavailableFile) => receiverProofUnavailableFiles.set(fileIdentityKey(unavailableFile), unavailableFile), equivalentDefinitions);
516
1033
  for (const { range, provenance, via } of ranges) {
517
1034
  if (hasReachedCollectionLimit())
518
1035
  break;
@@ -527,7 +1044,7 @@ async function findReferencesInternal(index, req, opts, collectionMode) {
527
1044
  }
528
1045
  const parsed = await ensureCandidateParsed();
529
1046
  const resolvedScope = await ensureScope();
530
- const localName = parsed.sup.normalizeIdentifier(imp.local);
1047
+ const localName = parsed.sup.normalizeIdentifier(importedLocalName);
531
1048
  const declarationKeys = importBindingDeclarationRangeKeys(module);
532
1049
  const bindings = resolvedScope.bindings.get(localName) ?? [];
533
1050
  for (const binding of bindings) {
@@ -544,34 +1061,67 @@ async function findReferencesInternal(index, req, opts, collectionMode) {
544
1061
  }
545
1062
  }
546
1063
  }
547
- if (phpQualifiedNames.length) {
548
- const remainingReferences = remainingCollectionSlots();
549
- for (const candidateName of [...exportedNames, ...phpQualifiedNames]) {
1064
+ // A unit peer names the definition directly (Go and JVM package siblings, C# namespace
1065
+ // peers, Swift module siblings), with no import binding to attribute. The single bare-name
1066
+ // scan keeps reference sites in agreement with what bare-name resolution can prove.
1067
+ if (!definition.isMember &&
1068
+ fileIdentityKey(fileId) !== fileIdentityKey(definitionFile) &&
1069
+ unitPeerKeys.has(fileIdentityKey(fileId)) &&
1070
+ !hasReachedCollectionLimit()) {
1071
+ const ranges = await collectVerifiedNamedNodeReferences(index, fileId, referenceDef.localName, definition, (params, parsed) => goToDefinition(index, params, parsed), remainingCollectionSlots(), verifiedReferenceFilter(fileId), (unavailableFile) => receiverProofUnavailableFiles.set(fileIdentityKey(unavailableFile), unavailableFile), equivalentDefinitions);
1072
+ for (const { range, provenance, via } of ranges) {
550
1073
  if (hasReachedCollectionLimit())
551
1074
  break;
552
- const ranges = await collectVerifiedNamedNodeReferences(index, fileId, candidateName, def, (params, parsed) => goToDefinition(index, params, parsed), remainingReferences, verifiedReferenceFilter(fileId));
553
- for (const { range, provenance, via } of ranges) {
1075
+ pushRef({ file: fileId, range, ...(via ? { via } : {}), ...(provenance ? { provenance } : {}) });
1076
+ }
1077
+ }
1078
+ // Only the exact dotted submodule path can refer to this definition through a
1079
+ // parent-package import. Other same-name uses are proven unrelated.
1080
+ if (parsedContext.sup.id === "python" && !definition.isMember && !hasReachedCollectionLimit()) {
1081
+ for (const { namespace, importBinding } of pythonParentPackageNamespacePaths(module, definitionFile)) {
1082
+ const parsed = await ensureCandidateParsed();
1083
+ const ranges = await collectNamespaceMemberRefs(fileId, namespace, referenceDef.localName, parsed, index.languageExtensions, importBinding, module.imports);
1084
+ for (const range of ranges) {
554
1085
  if (hasReachedCollectionLimit())
555
1086
  break;
556
- pushRef({ file: fileId, range, ...(via ? { via } : {}), ...(provenance ? { provenance } : {}) });
1087
+ const hit = await goToDefinition(index, { file: fileId, line: range.start.line, column: range.start.column }, parsed);
1088
+ if (hit.status === "ok") {
1089
+ if (matchesReferenceDefinition(hit.definition))
1090
+ pushRef({ file: fileId, range });
1091
+ }
1092
+ else {
1093
+ receiverProofUnavailableFiles.set(fileIdentityKey(fileId), fileId);
1094
+ }
557
1095
  }
558
1096
  }
559
1097
  }
1098
+ if (phpQualifiedNames.length) {
1099
+ const remainingReferences = remainingCollectionSlots();
1100
+ const ranges = await collectVerifiedNamedNodeReferences(index, fileId, referenceDef.localName, referenceDef, (params, parsed) => goToDefinition(index, params, parsed), remainingReferences, verifiedReferenceFilter(fileId), (unavailableFile) => receiverProofUnavailableFiles.set(fileIdentityKey(unavailableFile), unavailableFile));
1101
+ for (const { range, provenance, via } of ranges) {
1102
+ if (hasReachedCollectionLimit())
1103
+ break;
1104
+ pushRef({ file: fileId, range, ...(via ? { via } : {}), ...(provenance ? { provenance } : {}) });
1105
+ }
1106
+ }
560
1107
  }
561
1108
  const receiverScannedFiles = [];
562
- if (shouldScanVerifiedReferences(def, phpQualifiedNames, parsedContext)) {
1109
+ if (scansReceiverReferences) {
563
1110
  for (const fileId of Array.from(index.byFile.values(), (module) => module.file).sort((left, right) => left.localeCompare(right))) {
564
1111
  if (hasReachedCollectionLimit())
565
1112
  break;
566
1113
  receiverScannedFiles.push(fileId);
567
1114
  const filter = index.bloomFilters?.get(fileIdentityKey(fileId));
568
- // Bloom filters contain names normalized by the candidate file's language, so probes must use that rule.
569
- const canonicalName = supportForFileWithoutHeaderSample(fileId, index.languageExtensions)?.normalizeIdentifier(def.localName) ??
570
- def.localName;
1115
+ const candidateSupport = supportForFileWithoutHeaderSample(fileId, index.languageExtensions);
1116
+ // Bloom filters store folded PHP identifiers in addition to their source spelling.
1117
+ const normalizedName = candidateSupport?.normalizeIdentifier(referenceDef.localName) ?? referenceDef.localName;
1118
+ const canonicalName = candidateSupport?.id === "php" && isPhpCaseInsensitiveSymbolKind(definition.kind)
1119
+ ? foldPhpIdentifierCase(normalizedName)
1120
+ : normalizedName;
571
1121
  if (filter && !filter.mightContain(canonicalName))
572
1122
  continue;
573
1123
  const remainingReferences = remainingCollectionSlots();
574
- const ranges = await collectVerifiedNamedNodeReferences(index, fileId, def.localName, def, (params, parsed) => goToDefinition(index, params, parsed), remainingReferences, verifiedReferenceFilter(fileId));
1124
+ const ranges = await collectVerifiedNamedNodeReferences(index, fileId, referenceDef.localName, referenceDef, (params, parsed) => goToDefinition(index, params, parsed), remainingReferences, verifiedReferenceFilter(fileId), (unavailableFile) => receiverProofUnavailableFiles.set(fileIdentityKey(unavailableFile), unavailableFile), equivalentDefinitions);
575
1125
  for (const { range, provenance, via } of ranges) {
576
1126
  if (hasReachedCollectionLimit())
577
1127
  break;
@@ -612,44 +1162,385 @@ async function findReferencesInternal(index, req, opts, collectionMode) {
612
1162
  }
613
1163
  }
614
1164
  const scannedFiles = [definitionFile, ...candidateFiles, ...receiverScannedFiles];
1165
+ // Top-level implicit-unit names and C# partial / Swift shared-owner members are both bounded
1166
+ // by the source-unit relation; an unproven boundary leaves their reference set partial.
1167
+ const implicitUnitComplete = !definition.isMember && IMPLICIT_UNIT_LANGUAGES[parsedContext.sup.id]
1168
+ ? getCompilationUnitPeers(index, definitionFile, parsedContext.sup.id === "csharp" ? { csharpQualifiedName: true } : undefined).complete
1169
+ : await sharedOwnerMemberUnitComplete(index, definition);
615
1170
  const referenceCoverage = buildIndexedCandidateCoverage({
616
1171
  index,
617
- def,
1172
+ def: definition,
1173
+ languageId: parsedContext.sup.id,
618
1174
  exportedNames,
619
1175
  candidateFiles,
620
1176
  scannedFiles,
621
1177
  truncated,
1178
+ strategies: describeReferenceStrategies({
1179
+ languageId: parsedContext.sup.id,
1180
+ phpQualifiedNames,
1181
+ sameFileOccurrence: {
1182
+ // C/C++ callable siblings and reopened C# namespace regions can refer to a
1183
+ // declaration without sharing its lexical scope. Verify those uses through
1184
+ // navigation; parameters and local variables remain lexical-only.
1185
+ applicable: requiresSameFileVerifiedScan,
1186
+ executed: sameFileVerifiedScanExecuted,
1187
+ },
1188
+ // An unproven compilation-unit boundary means the peer universe may extend beyond the
1189
+ // enumerated files, so coverage must not imply that every possible consumer was scanned.
1190
+ ...(implicitUnitComplete !== null
1191
+ ? { implicitUnitPeers: { applicable: true, executed: implicitUnitComplete } }
1192
+ : {}),
1193
+ }),
1194
+ strategyUnavailableFiles: [
1195
+ ...receiverProofUnavailableFiles.values(),
1196
+ ...(rubyReopen?.incomplete ? [definition.file] : []),
1197
+ ],
622
1198
  });
623
1199
  return {
624
1200
  status: "ok",
625
- definition: def,
1201
+ definition,
626
1202
  references: refs,
627
1203
  referenceCoverage,
628
1204
  ...(provenance ? { provenance } : {}),
629
1205
  };
630
1206
  }
631
- function shouldScanVerifiedReferences(def, phpQualifiedNames, parsedContext) {
632
- if (phpQualifiedNames.length)
633
- return false;
634
- if (!supportsReceiverMemberNavigation(parsedContext.sup.id))
1207
+ function syntaxNodeForDefinition(parsedContext, def) {
1208
+ const position = {
1209
+ row: def.range.start.line - 1,
1210
+ column: def.range.start.column - 1,
1211
+ };
1212
+ return parsedContext.tree.rootNode.descendantForPosition(position, position);
1213
+ }
1214
+ /**
1215
+ * Restores indexed metadata for definitions that arrive without it — impact fabricates reference
1216
+ * defs from changed-symbol projections, dropping fields such as `isMember`. Matching is exact
1217
+ * identity against the definition file's module.locals: same kind, localName, and full range span.
1218
+ * Definitions without byte indices or without a matching local are returned unchanged.
1219
+ */
1220
+ function indexedDefinitionByIdentity(index, def) {
1221
+ const mod = index.byFile.get(fileIdentityKey(def.file));
1222
+ if (!mod)
1223
+ return def;
1224
+ const startIndex = def.range.start.index;
1225
+ const endIndex = def.range.end.index;
1226
+ if (startIndex === undefined || endIndex === undefined)
1227
+ return def;
1228
+ return (mod.locals.find((candidate) => candidate.kind === def.kind &&
1229
+ candidate.localName === def.localName &&
1230
+ candidate.range.start.index === startIndex &&
1231
+ candidate.range.end.index === endIndex) ?? def);
1232
+ }
1233
+ /**
1234
+ * Reverse include edges for one index. Rebuilt only when the index object is
1235
+ * new, matching the WeakMap caches in navigation-references.ts.
1236
+ */
1237
+ const cIncludedByCache = new WeakMap();
1238
+ function cIncludedBy(index) {
1239
+ const cached = cIncludedByCache.get(index);
1240
+ if (cached)
1241
+ return cached;
1242
+ const includedBy = new Map();
1243
+ for (const moduleEntry of index.byFile.values()) {
1244
+ const includerKey = fileIdentityKey(moduleEntry.file);
1245
+ for (const imp of moduleEntry.imports) {
1246
+ if (typeof imp.resolved !== "string")
1247
+ continue;
1248
+ const includedKey = fileIdentityKey(imp.resolved);
1249
+ const includers = includedBy.get(includedKey);
1250
+ if (includers)
1251
+ includers.push(includerKey);
1252
+ else
1253
+ includedBy.set(includedKey, [includerKey]);
1254
+ }
1255
+ }
1256
+ const stored = new Map();
1257
+ for (const [key, includers] of includedBy)
1258
+ stored.set(key, includers);
1259
+ cIncludedByCache.set(index, stored);
1260
+ return stored;
1261
+ }
1262
+ /** Indexed files linked to `startFile` by `#include` in either direction, excluding the file itself. */
1263
+ function cIncludeLinkedModules(index, startFile) {
1264
+ const startKey = fileIdentityKey(startFile);
1265
+ const includedBy = cIncludedBy(index);
1266
+ const linked = new Set();
1267
+ const walk = (origin, neighbors) => {
1268
+ const pending = [origin];
1269
+ const seen = new Set([origin]);
1270
+ while (pending.length) {
1271
+ const current = pending.pop();
1272
+ for (const neighbor of neighbors(current)) {
1273
+ if (seen.has(neighbor))
1274
+ continue;
1275
+ seen.add(neighbor);
1276
+ linked.add(neighbor);
1277
+ pending.push(neighbor);
1278
+ }
1279
+ }
1280
+ };
1281
+ walk(startKey, (key) => {
1282
+ const moduleEntry = index.byFile.get(key);
1283
+ if (!moduleEntry)
1284
+ return [];
1285
+ const included = [];
1286
+ for (const imp of moduleEntry.imports) {
1287
+ if (typeof imp.resolved === "string")
1288
+ included.push(fileIdentityKey(imp.resolved));
1289
+ }
1290
+ return included;
1291
+ });
1292
+ walk(startKey, (key) => includedBy.get(key) ?? []);
1293
+ linked.delete(startKey);
1294
+ const modules = [];
1295
+ for (const key of linked) {
1296
+ const moduleEntry = index.byFile.get(key);
1297
+ if (moduleEntry)
1298
+ modules.push(moduleEntry);
1299
+ }
1300
+ return modules;
1301
+ }
1302
+ /**
1303
+ * C prototype and definition of one signature, joined only when one file includes
1304
+ * the other. Two translation units that merely share an unrelated header stay apart,
1305
+ * and `static` functions are not exports so they never join the family.
1306
+ */
1307
+ async function cIncludeLinkedCallableEquivalents(index, def, definitionNameNode) {
1308
+ if (def.kind !== SymbolKind.Function || def.cTag)
1309
+ return [];
1310
+ const expected = cppCallableShapeForNode(definitionNameNode);
1311
+ if (!expected)
1312
+ return [];
1313
+ const origin = index.byFile.get(fileIdentityKey(def.file));
1314
+ const exported = origin?.exports.some((entry) => entry.type === "local" && sameDef(entry.target, def, index.languageExtensions));
1315
+ if (!exported)
1316
+ return [];
1317
+ const equivalents = new Map();
1318
+ for (const moduleEntry of cIncludeLinkedModules(index, def.file)) {
1319
+ const candidates = [];
1320
+ for (const entry of moduleEntry.exports) {
1321
+ if (entry.type !== "local")
1322
+ continue;
1323
+ if (entry.exportedAs !== def.localName || entry.target.localName !== def.localName)
1324
+ continue;
1325
+ if (entry.target.kind !== SymbolKind.Function || entry.target.cTag)
1326
+ continue;
1327
+ candidates.push(entry.target);
1328
+ }
1329
+ if (!candidates.length)
1330
+ continue;
1331
+ let candidateParsed;
1332
+ try {
1333
+ candidateParsed = await ensureParsedContext(moduleEntry.file, index.parsed?.get(fileIdentityKey(moduleEntry.file)), index.languageExtensions);
1334
+ }
1335
+ catch {
1336
+ continue;
1337
+ }
1338
+ if (candidateParsed.sup.id !== "c")
1339
+ continue;
1340
+ for (const candidate of candidates) {
1341
+ if (sameDef(candidate, def, index.languageExtensions))
1342
+ continue;
1343
+ const candidateNode = syntaxNodeForDefinition(candidateParsed, candidate);
1344
+ if (cppCallableShapeForNode(candidateNode)?.signature !== expected.signature)
1345
+ continue;
1346
+ equivalents.set(referenceSiteKey(candidate.file, candidate.range), candidate);
1347
+ }
1348
+ }
1349
+ return [...equivalents.values()];
1350
+ }
1351
+ /**
1352
+ * Single calculation behind reference collection and getCppEquivalentCallableDefinitions: resolves
1353
+ * the receiver owner, the normalized reference name, the definition-site scope binding, and the
1354
+ * proven equivalent definition family for one callable definition site.
1355
+ */
1356
+ async function cppEquivalentCallableFamily(index, def, parsedContext) {
1357
+ const definition = indexedDefinitionByIdentity(index, def);
1358
+ const mod = index.byFile.get(fileIdentityKey(definition.file));
1359
+ if (!mod) {
1360
+ return { definition, referenceDef: definition, receiverOwner: null, localBinding: undefined, equivalents: [] };
1361
+ }
1362
+ const context = parsedContext ??
1363
+ (await ensureParsedContext(definition.file, index.parsed?.get(fileIdentityKey(definition.file)), index.languageExtensions));
1364
+ const definitionNameNode = syntaxNodeForDefinition(context, definition);
1365
+ const receiverOwner = await cppOutOfLineReceiverOwner(index, mod, definition, context, definitionNameNode);
1366
+ const memberName = receiverOwner ? cppOutOfLineMemberName(definitionNameNode, context.source, context.sup) : null;
1367
+ const referenceDef = memberName && memberName !== definition.localName ? { ...definition, localName: memberName } : definition;
1368
+ const normalizedLocalName = context.sup.normalizeIdentifier(referenceDef.localName);
1369
+ const scope = getCachedScope(index, definition.file, mod, context);
1370
+ const localBindings = scope.bindings.get(normalizedLocalName) ?? [];
1371
+ const localBinding = localBindings.find((binding) => binding.def && binding.def.start.index === definition.range.start.index);
1372
+ let sameFileFunctionBindings = localBindings;
1373
+ if (context.sup.id === "cpp") {
1374
+ sameFileFunctionBindings = localBinding ? cppEquivalentCallableBindings(localBinding) : [];
1375
+ }
1376
+ const sameFileFunctionEquivalentDefinitions = (context.sup.id === "c" || context.sup.id === "cpp") && definition.kind === SymbolKind.Function
1377
+ ? sameFileFunctionBindings.flatMap((binding) => {
1378
+ const bindingRange = binding.def;
1379
+ if (!bindingRange || bindingRange.start.index === definition.range.start.index)
1380
+ return [];
1381
+ const local = mod.locals.find((candidate) => candidate.kind === SymbolKind.Function &&
1382
+ candidate.localName === definition.localName &&
1383
+ candidate.range.start.index === bindingRange.start.index &&
1384
+ candidate.range.end.index === bindingRange.end.index);
1385
+ return local ? [local] : [];
1386
+ })
1387
+ : [];
1388
+ let equivalents;
1389
+ if (receiverOwner) {
1390
+ equivalents = await cppOutOfLineEquivalentDefinitions(index, referenceDef, context, definitionNameNode, receiverOwner);
1391
+ }
1392
+ else if (definition.isMember && context.sup.id === "cpp") {
1393
+ equivalents = await cppInClassMemberEquivalentDefinitions(index, definition, context, definitionNameNode);
1394
+ }
1395
+ else if (context.sup.id === "cpp") {
1396
+ equivalents = [
1397
+ ...sameFileFunctionEquivalentDefinitions,
1398
+ ...(await cppNamespaceFunctionEquivalentDefinitions(index, definition, context, definitionNameNode)),
1399
+ ];
1400
+ }
1401
+ else if (context.sup.id === "c" && definition.kind === SymbolKind.Function && !definition.cTag) {
1402
+ const linked = await cIncludeLinkedCallableEquivalents(index, definition, definitionNameNode);
1403
+ const seen = new Set(sameFileFunctionEquivalentDefinitions.map((item) => referenceSiteKey(item.file, item.range)));
1404
+ equivalents = [
1405
+ ...sameFileFunctionEquivalentDefinitions,
1406
+ ...linked.filter((item) => !seen.has(referenceSiteKey(item.file, item.range))),
1407
+ ];
1408
+ }
1409
+ else {
1410
+ equivalents = sameFileFunctionEquivalentDefinitions;
1411
+ }
1412
+ return { definition, referenceDef, receiverOwner, localBinding, equivalents };
1413
+ }
1414
+ async function cppOutOfLineReceiverOwner(index, mod, def, parsedContext, definitionNameNode) {
1415
+ if (parsedContext.sup.id !== "cpp" || def.kind !== SymbolKind.Function)
1416
+ return null;
1417
+ const ownerPath = cppOutOfLineOwnerPath(definitionNameNode, parsedContext.source, parsedContext.sup);
1418
+ return ownerPath ? await resolveCppQualifiedMemberContainer(index, mod, ownerPath) : null;
1419
+ }
1420
+ async function cppOutOfLineEquivalentDefinitions(index, def, parsedContext, definitionNameNode, owner) {
1421
+ const ownerModule = index.byFile.get(fileIdentityKey(owner.file));
1422
+ if (!ownerModule)
1423
+ return [];
1424
+ const ownerParsed = await ensureParsedContext(owner.file, index.parsed?.get(fileIdentityKey(owner.file)), index.languageExtensions);
1425
+ const ownerNameNode = syntaxNodeForDefinition(ownerParsed, owner);
1426
+ const declaration = cppOutOfLineMemberDeclarationNode(definitionNameNode, def.localName, ownerNameNode, ownerParsed.source, parsedContext.sup);
1427
+ if (!declaration)
1428
+ return [];
1429
+ const normalizedName = parsedContext.sup.normalizeIdentifier(def.localName);
1430
+ return ownerModule.locals.filter((candidate) => parsedContext.sup.normalizeIdentifier(candidate.localName) === normalizedName &&
1431
+ candidate.range.start.index === declaration.nameNode.startIndex);
1432
+ }
1433
+ async function cppInClassMemberEquivalentDefinitions(index, def, parsedContext, definitionNameNode) {
1434
+ if (def.kind !== SymbolKind.Function)
1435
+ return [];
1436
+ const ownerSegments = [];
1437
+ let current = definitionNameNode.parent;
1438
+ while (current) {
1439
+ if (CPP_MEMBER_CONTAINER_TYPES.has(current.type) || current.type === "namespace_definition") {
1440
+ const name = current.childForFieldName("name");
1441
+ if (!name)
1442
+ return [];
1443
+ ownerSegments.unshift(cppQualifiedNameSegments(name, parsedContext.source));
1444
+ }
1445
+ current = current.parent;
1446
+ }
1447
+ const owners = ownerSegments.flat();
1448
+ if (!owners.length)
1449
+ return [];
1450
+ const qualifiedName = [...owners, def.localName].join("::");
1451
+ const expectedShape = cppCallableShapeForNode(definitionNameNode);
1452
+ if (!expectedShape)
1453
+ return [];
1454
+ const equivalents = new Map();
1455
+ for (const module of index.byFile.values()) {
1456
+ for (const entry of module.exports) {
1457
+ if (entry.type !== "local" ||
1458
+ entry.exportedAs !== qualifiedName ||
1459
+ entry.target.kind !== SymbolKind.Function ||
1460
+ sameDef(entry.target, def, index.languageExtensions)) {
1461
+ continue;
1462
+ }
1463
+ const candidateParsed = await ensureParsedContext(entry.target.file, index.parsed?.get(fileIdentityKey(entry.target.file)), index.languageExtensions);
1464
+ const candidateNode = syntaxNodeForDefinition(candidateParsed, entry.target);
1465
+ const candidateShape = cppCallableShapeForNode(candidateNode);
1466
+ if (!candidateShape)
1467
+ continue;
1468
+ let explicitSpecialization = false;
1469
+ let current = candidateNode;
1470
+ while (current) {
1471
+ if (current.type === "template_declaration" &&
1472
+ /^\s*template\s*<\s*>/u.test(sliceText(current, candidateParsed.source))) {
1473
+ explicitSpecialization = true;
1474
+ break;
1475
+ }
1476
+ current = current.parent;
1477
+ }
1478
+ const arityMatches = candidateShape.minArity === expectedShape.minArity && candidateShape.maxArity === expectedShape.maxArity;
1479
+ if (candidateShape.signature !== expectedShape.signature && !(explicitSpecialization && arityMatches)) {
1480
+ continue;
1481
+ }
1482
+ equivalents.set(referenceSiteKey(entry.target.file, entry.target.range), entry.target);
1483
+ }
1484
+ }
1485
+ return [...equivalents.values()];
1486
+ }
1487
+ function shouldScanVerifiedReferences(def, parsedContext, receiverMemberDefinition) {
1488
+ if (parsedContext.sup.id === "php" && !isPhpCaseInsensitiveSymbolKind(def.kind))
635
1489
  return false;
636
- return isReceiverMemberDefinition(def, parsedContext);
1490
+ return supportsReceiverMemberNavigation(parsedContext.sup.id) && receiverMemberDefinition;
637
1491
  }
638
- function shouldUseLocalNameAsExportFallback(def, parsedContext) {
639
- return !isReceiverMemberDefinition(def, parsedContext);
1492
+ async function cppNamespaceFunctionEquivalentDefinitions(index, def, parsedContext, definitionNameNode) {
1493
+ if (parsedContext.sup.id !== "cpp" || def.kind !== SymbolKind.Function)
1494
+ return [];
1495
+ const ownerPath = cppOutOfLineOwnerPath(definitionNameNode, parsedContext.source, parsedContext.sup);
1496
+ const namespacePath = [];
1497
+ if (!ownerPath) {
1498
+ let current = definitionNameNode.parent;
1499
+ while (current) {
1500
+ if (CPP_MEMBER_CONTAINER_TYPES.has(current.type))
1501
+ return [];
1502
+ if (current.type === "namespace_definition") {
1503
+ const name = current.childForFieldName("name");
1504
+ if (!name)
1505
+ return [];
1506
+ namespacePath.unshift(cppQualifiedNameSegments(name, parsedContext.source));
1507
+ }
1508
+ current = current.parent;
1509
+ }
1510
+ }
1511
+ const owners = ownerPath ?? namespacePath.flat();
1512
+ if (!owners.length)
1513
+ return [];
1514
+ const memberName = cppOutOfLineMemberName(definitionNameNode, parsedContext.source, parsedContext.sup) ?? def.localName;
1515
+ const qualifiedName = [...owners, memberName].join("::");
1516
+ const expectedShape = cppCallableShapeForNode(definitionNameNode);
1517
+ if (!expectedShape)
1518
+ return [];
1519
+ const equivalents = new Map();
1520
+ for (const module of index.byFile.values()) {
1521
+ for (const entry of module.exports) {
1522
+ if (entry.type !== "local" ||
1523
+ entry.exportedAs !== qualifiedName ||
1524
+ entry.target.kind !== SymbolKind.Function ||
1525
+ sameDef(entry.target, def, index.languageExtensions)) {
1526
+ continue;
1527
+ }
1528
+ const candidateParsed = await ensureParsedContext(entry.target.file, index.parsed?.get(fileIdentityKey(entry.target.file)), index.languageExtensions);
1529
+ const candidateNode = syntaxNodeForDefinition(candidateParsed, entry.target);
1530
+ if (cppCallableShapeForNode(candidateNode)?.signature !== expectedShape.signature)
1531
+ continue;
1532
+ equivalents.set(referenceSiteKey(entry.target.file, entry.target.range), entry.target);
1533
+ }
1534
+ }
1535
+ return [...equivalents.values()];
640
1536
  }
641
- function isReceiverMemberDefinition(def, parsedContext) {
642
- if (def.isMember)
1537
+ function isReceiverMemberDefinition(def, parsedContext, hasCppOutOfLineOwner) {
1538
+ if (def.isMember || hasCppOutOfLineOwner)
643
1539
  return true;
644
- if (def.kind !== SymbolKind.Function) {
1540
+ if (def.kind !== SymbolKind.Function)
645
1541
  return false;
646
- }
647
- const start = def.range.start;
648
- const position = {
649
- row: start.line - 1,
650
- column: start.column - 1,
651
- };
652
- let current = parsedContext.tree.rootNode.descendantForPosition(position, position);
1542
+ let current = syntaxNodeForDefinition(parsedContext, def);
1543
+ let sawCppFunction = false;
653
1544
  let sawRustImplFunction = false;
654
1545
  while (current) {
655
1546
  if (current.type === "method_definition" ||
@@ -659,33 +1550,73 @@ function isReceiverMemberDefinition(def, parsedContext) {
659
1550
  current.type === "method") {
660
1551
  return true;
661
1552
  }
1553
+ if (parsedContext.sup.id === "cpp") {
1554
+ if (current.type === "function_definition")
1555
+ sawCppFunction = true;
1556
+ if (sawCppFunction && CPP_MEMBER_CONTAINER_TYPES.has(current.type))
1557
+ return true;
1558
+ }
662
1559
  if (parsedContext.sup.id === "rust" && current.type === "function_item") {
663
1560
  sawRustImplFunction = true;
664
1561
  }
665
- if (sawRustImplFunction && current.type === "impl_item") {
1562
+ if (sawRustImplFunction && current.type === "impl_item")
666
1563
  return true;
667
- }
668
- if (current.type === "function_declaration" || current.type === "program") {
1564
+ if (current.type === "function_declaration" || current.type === "program")
669
1565
  return false;
670
- }
671
1566
  current = current.parent;
672
1567
  }
673
1568
  return false;
674
1569
  }
675
- export async function collectNamespaceMemberRefs(file, ns, member, parsedContext, languageExtensions) {
1570
+ export async function collectNamespaceMemberRefs(file, ns, member, parsedContext, languageExtensions, importBinding, imports) {
676
1571
  const parsed = parsedContext ?? (await ensureParsedContext(file, undefined, languageExtensions));
677
1572
  const sup = parsed.sup;
678
1573
  const source = parsed.source;
679
1574
  const tree = parsed.tree;
680
1575
  const ranges = [];
1576
+ // Identifier equality follows the language rule, so C# `@X::@Target` matches alias `X` member `Target`.
1577
+ const normalize = sup.normalizeIdentifier;
1578
+ const normalizedNs = normalize(ns);
1579
+ const normalizedMember = normalize(member);
1580
+ let effectivePythonBinding;
1581
+ if (sup.id === "python" && importBinding && imports) {
1582
+ let bindingName;
1583
+ if (importBinding.kind === "namespace")
1584
+ bindingName = importBinding.localNS;
1585
+ else if (importBinding.kind === "named" || importBinding.kind === "default")
1586
+ bindingName = importBinding.local;
1587
+ if (bindingName) {
1588
+ const normalizedBindingName = normalize(bindingName);
1589
+ effectivePythonBinding = effectiveExplicitBinding(imports, sup.id, (binding) => {
1590
+ if (binding.kind === "namespace")
1591
+ return normalize(binding.localNS) === normalizedBindingName;
1592
+ return ((binding.kind === "named" || binding.kind === "default") && normalize(binding.local) === normalizedBindingName);
1593
+ });
1594
+ }
1595
+ }
681
1596
  const walk = (node) => {
682
1597
  if (isMemberAccessNode(sup, node)) {
683
1598
  const { object: obj, property: prop } = getMemberAccessParts(sup, node);
684
- if (obj && prop && isMemberObjectIdentifier(obj.type) && isMemberReferencePropertyIdentifier(sup, prop.type)) {
1599
+ if (obj && prop && isMemberReferencePropertyIdentifier(sup, prop.type)) {
685
1600
  const objectName = sliceText(obj, source);
686
1601
  const propertyName = sliceText(prop, source);
687
- if (objectName === ns && propertyName === member) {
688
- ranges.push(toRange(prop));
1602
+ // A python multi-segment dotted import (`import a.b`) binds only the first segment;
1603
+ // the source only ever repeats the whole dotted phrase to reach the resolved leaf
1604
+ // module again, so `ns` can be that literal phrase and `obj` a compound chain rather
1605
+ // than the single bare identifier every other caller passes.
1606
+ const objectMatches = normalizedNs.includes(".")
1607
+ ? normalize(objectName) === normalizedNs
1608
+ : isMemberObjectIdentifier(obj.type) && normalize(objectName) === normalizedNs;
1609
+ if (objectMatches && normalize(propertyName) === normalizedMember) {
1610
+ const inPythonBindingScope = sup.id !== "python" || !effectivePythonBinding || effectivePythonBinding === importBinding;
1611
+ const inAliasScope = inPythonBindingScope &&
1612
+ (sup.id !== "csharp" ||
1613
+ !importBinding ||
1614
+ !imports ||
1615
+ importBinding.kind !== "namespace" ||
1616
+ !importBinding.localRange ||
1617
+ innermostNamespaceImport(imports, objectName, obj, normalize) === importBinding);
1618
+ if (inAliasScope)
1619
+ ranges.push(toRange(prop));
689
1620
  }
690
1621
  }
691
1622
  }