@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,16 +1,59 @@
1
+ import path from "node:path";
1
2
  import { supportForFileWithoutHeaderSample } from "../languages.js";
2
- import { fileIdentityKey } from "../util/paths.js";
3
+ import { fileIdentityKey, normalizePath } from "../util/paths.js";
3
4
  import { sliceText, toRange } from "../util/ast.js";
5
+ import { getMemberAccessParts, isMemberAccessNode, isReceiverNameNode } from "../util/member-access.js";
6
+ import { classifyReceiver, declaresMembers, receiverConstructorExpression, } from "../graphs/symbol-graph-detailed/receiver-calls.js";
7
+ import { provenClassifiedReceiverOmitsMember } from "./navigation-goto.js";
4
8
  import { ensureParsedContext } from "./parse-context.js";
5
- import { sameDef } from "./reference-context.js";
6
- import { readPhpNamespaceFromRange } from "./navigation-php.js";
9
+ import { definitionIdentityKey, sameDef } from "./reference-context.js";
10
+ import { canonicalPhpReferenceNames, comparePhpReferenceNames, getPhpQualifiedReference, inferPhpQualifiedReferenceImportType, isInsidePhpUseDeclaration, isPhpQualifiedReferenceNode, phpLastIdentifierSegment, readPhpNamespaceFromRange, selectFirstExistingPhpCanonicalName, } from "./navigation-php.js";
11
+ import { isKeywordReceiver, memberSyntaxNamesFreeFunction } from "../util/member-access-tables.js";
12
+ import { getCompilationUnitPeers } from "./compilation-units.js";
13
+ import { findClosestScopeBinding } from "./navigation-local.js";
14
+ import { scopeNodesFor } from "./scope-nodes.js";
7
15
  import { candidateFilesImportingTarget } from "./reference-candidates.js";
8
16
  import { buildScopeIndexFromSource } from "./scope.js";
17
+ import { bindingKindToSymbolKind } from "./declarations.js";
9
18
  import { resolveExport, resolveImported } from "./navigation-resolve.js";
10
- import { ECMASCRIPT_IDENTIFIER_SOURCE } from "../util/identifiers.js";
19
+ import { isAmbiguousResolutionReason } from "./ambiguous-resolution.js";
20
+ import { ensurePhpNamespaceSymbolIndex, phpNamespaceSymbolIndexFor, phpReferenceRoleMatchesKind, } from "./php-namespace-symbols.js";
21
+ import { SymbolKind, } from "./types.js";
22
+ import { ECMASCRIPT_IDENTIFIER_SOURCE, foldPhpIdentifierCase } from "../util/identifiers.js";
11
23
  const EXPORT_FROM_PATTERN = new RegExp(String.raw `\bexport\s+(?:type\s+)?\{([^}]*)\}\s*from\s*(["'])([^"']+)\2`, "gu");
12
24
  const NAMESPACE_EXPORT_PATTERN = new RegExp(String.raw `\bexport\s*\*\s*as\s*(${ECMASCRIPT_IDENTIFIER_SOURCE})\s*from\s*(["'])([^"']+)\2`, "gu");
13
25
  const EXPORT_FROM_SPECIFIER_PATTERN = new RegExp(String.raw `^(${ECMASCRIPT_IDENTIFIER_SOURCE})`, "u");
26
+ const importClosureCache = new WeakMap();
27
+ /** File keys reachable from `file` through resolved imports and includes, including `file`. */
28
+ function importClosure(index, file) {
29
+ let byFile = importClosureCache.get(index);
30
+ if (!byFile) {
31
+ byFile = new Map();
32
+ importClosureCache.set(index, byFile);
33
+ }
34
+ const startKey = fileIdentityKey(file);
35
+ const cached = byFile.get(startKey);
36
+ if (cached)
37
+ return cached;
38
+ const reached = new Set([startKey]);
39
+ const startModule = index.byFile.get(startKey);
40
+ const pending = startModule ? [startModule] : [];
41
+ while (pending.length) {
42
+ for (const imp of pending.pop().imports) {
43
+ if (typeof imp.resolved !== "string")
44
+ continue;
45
+ const key = fileIdentityKey(imp.resolved);
46
+ if (reached.has(key))
47
+ continue;
48
+ reached.add(key);
49
+ const moduleEntry = index.byFile.get(key);
50
+ if (moduleEntry)
51
+ pending.push(moduleEntry);
52
+ }
53
+ }
54
+ byFile.set(startKey, reached);
55
+ return reached;
56
+ }
14
57
  export function exportFromIdentifier(index, fileId, range, parsed) {
15
58
  if (!parsed.sup.supportsExportFromReferences)
16
59
  return null;
@@ -67,11 +110,25 @@ export function exportFromIdentifier(index, fileId, range, parsed) {
67
110
  return null;
68
111
  }
69
112
  export function getCachedScope(index, fileId, moduleIndex, parsedCtx) {
113
+ const keepOccurrence = (binding, occurrence) => {
114
+ if (exportFromIdentifier(index, fileId, occurrence, parsedCtx)?.isExportFrom)
115
+ return false;
116
+ if (parsedCtx.sup.id !== "php" || !binding.def || occurrence.start.index === binding.def.start.index)
117
+ return true;
118
+ const kind = bindingKindToSymbolKind(binding.kind);
119
+ if (kind === SymbolKind.Variable)
120
+ return true;
121
+ const start = occurrence.start.index;
122
+ if (start === undefined)
123
+ return true;
124
+ const node = parsedCtx.tree.rootNode.descendantForIndex(start, start);
125
+ return phpReferenceRoleMatchesKind(node, kind);
126
+ };
70
127
  const fileKey = fileIdentityKey(fileId);
71
128
  const cachedScope = index.scopeCache.get(fileKey);
72
129
  if (cachedScope) {
73
130
  for (const binding of cachedScope.all) {
74
- binding.occurrences = binding.occurrences.filter((occurrence) => !exportFromIdentifier(index, fileId, occurrence, parsedCtx)?.isExportFrom);
131
+ binding.occurrences = binding.occurrences.filter((occurrence) => keepOccurrence(binding, occurrence));
75
132
  }
76
133
  return cachedScope;
77
134
  }
@@ -79,28 +136,182 @@ export function getCachedScope(index, fileId, moduleIndex, parsedCtx) {
79
136
  tree: parsedCtx.tree,
80
137
  });
81
138
  for (const binding of scopeIndex.all) {
82
- binding.occurrences = binding.occurrences.filter((occurrence) => !exportFromIdentifier(index, fileId, occurrence, parsedCtx)?.isExportFrom);
139
+ binding.occurrences = binding.occurrences.filter((occurrence) => keepOccurrence(binding, occurrence));
83
140
  }
84
141
  index.scopeCache.set(fileKey, scopeIndex);
85
142
  return scopeIndex;
86
143
  }
87
- export async function buildPhpQualifiedNames(index, definitionFile, def) {
144
+ /**
145
+ * The authoritative PHP spellings of a definition: `Namespace\Name`, or the bare `Name` for a
146
+ * global-namespace declaration. These are the names a reference must resolve to, so they keep
147
+ * the declaration's own case. A global-namespace declaration is still addressable by its bare
148
+ * name and by the fully-qualified `\Name` spelling, so the global-name scan must run for it;
149
+ * returning `[]` here left a global PHP symbol on the importer-narrowed path, which never scans
150
+ * a consumer that has no `use` statement.
151
+ */
152
+ async function readPhpDefinitionNames(index, definitionFile, def) {
88
153
  try {
89
154
  const definitionParsed = await ensureParsedContext(definitionFile, index.parsed?.get(fileIdentityKey(definitionFile)), index.languageExtensions);
90
155
  if (definitionParsed.sup.id !== "php") {
91
156
  return [];
92
157
  }
93
158
  const phpNamespace = readPhpNamespaceFromRange(definitionParsed.tree, definitionParsed.source, def.range);
94
- if (!phpNamespace)
95
- return [];
96
- const qualifiedName = `${phpNamespace}\\${def.localName}`;
97
- return [qualifiedName, `\\${qualifiedName}`];
159
+ return [phpNamespace ? `${phpNamespace}\\${def.localName}` : def.localName];
98
160
  }
99
161
  catch {
100
162
  return [];
101
163
  }
102
164
  }
103
- async function collectNamedNodeReferences(index, fileId, symbolName) {
165
+ export async function buildPhpQualifiedNames(index, definitionFile, def) {
166
+ // Keep one canonical spelling. Case-variant consumers are admitted during the single AST
167
+ // walk by comparePhpReferenceNames; extra folded probes would multiply whole-file scans.
168
+ return readPhpDefinitionNames(index, definitionFile, def);
169
+ }
170
+ const phpCanonicalNamesCache = new WeakMap();
171
+ const phpNameEquivalenceGaps = new WeakMap();
172
+ async function phpCanonicalDefinitionNames(index, def) {
173
+ let perIndex = phpCanonicalNamesCache.get(index);
174
+ if (!perIndex) {
175
+ perIndex = new Map();
176
+ phpCanonicalNamesCache.set(index, perIndex);
177
+ }
178
+ const key = definitionIdentityKey(def);
179
+ const cached = perIndex.get(key);
180
+ if (cached)
181
+ return cached;
182
+ const indexed = phpNamespaceSymbolIndexFor(index)?.canonicalByDefinition.get(key);
183
+ if (indexed) {
184
+ perIndex.set(key, [indexed]);
185
+ return [indexed];
186
+ }
187
+ const canonicalNames = (await readPhpDefinitionNames(index, def.file, def)).map((name) => name.replace(/^\\+/, ""));
188
+ perIndex.set(key, canonicalNames);
189
+ return canonicalNames;
190
+ }
191
+ function markPhpNameEquivalenceGap(index, def) {
192
+ let gaps = phpNameEquivalenceGaps.get(index);
193
+ if (!gaps) {
194
+ gaps = new Set();
195
+ phpNameEquivalenceGaps.set(index, gaps);
196
+ }
197
+ gaps.add(definitionIdentityKey(def));
198
+ }
199
+ const PHP_OWN_RECEIVER_KEYWORDS = new Set(["$this", "self", "static"]);
200
+ const PHP_TYPE_CONTAINER_TYPES = new Set([
201
+ "class_declaration",
202
+ "interface_declaration",
203
+ "trait_declaration",
204
+ "enum_declaration",
205
+ ]);
206
+ async function phpOwnerInfo(index, def) {
207
+ const module = index.byFile.get(fileIdentityKey(def.file));
208
+ if (!module)
209
+ return null;
210
+ const parsed = await ensureParsedContext(def.file, index.parsed?.get(fileIdentityKey(def.file)), index.languageExtensions);
211
+ const start = def.range.start;
212
+ const position = { row: Math.max(0, start.line - 1), column: Math.max(0, start.column - 1) };
213
+ let current = parsed.tree.rootNode.descendantForPosition(position, position);
214
+ while (current) {
215
+ if (PHP_TYPE_CONTAINER_TYPES.has(current.type)) {
216
+ const nameNode = current.childForFieldName("name");
217
+ const className = nameNode ? sliceText(nameNode, parsed.source) : null;
218
+ if (!className)
219
+ return null;
220
+ const owner = module.locals.find((local) => local.localName === className &&
221
+ local.range.start.index === nameNode?.startIndex &&
222
+ (local.kind === SymbolKind.Class ||
223
+ local.kind === SymbolKind.Interface ||
224
+ local.kind === SymbolKind.TypeAlias)) ?? null;
225
+ if (!owner)
226
+ return null;
227
+ return { owner, containerStart: current.startIndex, containerEnd: current.endIndex };
228
+ }
229
+ current = current.parent;
230
+ }
231
+ return null;
232
+ }
233
+ function phpMemberAccessProperty(node, parsed) {
234
+ const parent = node.parent;
235
+ if (!parent || !isMemberAccessNode(parsed.sup, parent))
236
+ return null;
237
+ const parts = getMemberAccessParts(parsed.sup, parent);
238
+ if (!parts.property || !parts.object)
239
+ return null;
240
+ const isProperty = parts.property === node ||
241
+ (parts.property.id !== undefined && parts.property.id === node.id) ||
242
+ (parts.property.startIndex === node.startIndex && parts.property.endIndex === node.endIndex);
243
+ if (!isProperty)
244
+ return null;
245
+ if (parts.object.startIndex === parts.property.startIndex)
246
+ return null;
247
+ return { object: parts.object, property: parts.property, parent };
248
+ }
249
+ async function phpCaseInsensitiveReceiverMemberMatch(index, fileId, node, parsed, expectedDef) {
250
+ const access = phpMemberAccessProperty(node, parsed);
251
+ if (!access)
252
+ return "skip";
253
+ const propertyText = sliceText(access.property, parsed.source);
254
+ const comparison = comparePhpReferenceNames(propertyText, expectedDef.localName, {
255
+ symbolKind: expectedDef.kind,
256
+ });
257
+ if (comparison !== "equivalent")
258
+ return "skip";
259
+ const exactSpelling = propertyText === expectedDef.localName;
260
+ const ownerInfo = await phpOwnerInfo(index, expectedDef);
261
+ if (!ownerInfo)
262
+ return exactSpelling ? "skip" : "unverified";
263
+ const owner = ownerInfo.owner;
264
+ const receiverName = sliceText(access.object, parsed.source);
265
+ if (PHP_OWN_RECEIVER_KEYWORDS.has(receiverName)) {
266
+ if (fileIdentityKey(fileId) === fileIdentityKey(expectedDef.file) &&
267
+ node.startIndex >= ownerInfo.containerStart &&
268
+ node.startIndex <= ownerInfo.containerEnd) {
269
+ return "matched";
270
+ }
271
+ return exactSpelling ? "skip" : "unverified";
272
+ }
273
+ if (isKeywordReceiver(parsed.sup.id, receiverName)) {
274
+ return exactSpelling ? "skip" : "unverified";
275
+ }
276
+ const constructor = receiverConstructorExpression(access.object, parsed.source, parsed.sup);
277
+ const typeNode = constructor ?? (access.parent.type === "scoped_call_expression" ? access.object : null);
278
+ if (!typeNode)
279
+ return exactSpelling ? "skip" : "unverified";
280
+ const ownerNames = await phpCanonicalDefinitionNames(index, owner);
281
+ const imports = index.byFile.get(fileIdentityKey(fileId))?.imports;
282
+ const typeNames = canonicalPhpReferenceNames(sliceText(typeNode, parsed.source), parsed.source, parsed.tree, typeNode, {
283
+ ...(imports ? { imports } : {}),
284
+ role: "class",
285
+ });
286
+ const matchesOwner = typeNames.some((typeName) => ownerNames.some((ownerName) => comparePhpReferenceNames(typeName, ownerName, { symbolKind: SymbolKind.Class }) === "equivalent"));
287
+ if (matchesOwner)
288
+ return "matched";
289
+ return "skip";
290
+ }
291
+ function matchesPhpFallbackDefinition(node, parsed, expectedDef) {
292
+ if (expectedDef.isMember)
293
+ return false;
294
+ const parent = node.parent;
295
+ if (parent && isMemberAccessNode(parsed.sup, parent)) {
296
+ const property = getMemberAccessParts(parsed.sup, parent).property;
297
+ if (property &&
298
+ (property === node ||
299
+ (property.id !== undefined && property.id === node.id) ||
300
+ (property.startIndex === node.startIndex && property.endIndex === node.endIndex))) {
301
+ return false;
302
+ }
303
+ }
304
+ const referenceKind = inferPhpQualifiedReferenceImportType(node);
305
+ if (expectedDef.kind === SymbolKind.Function)
306
+ return referenceKind === "function";
307
+ if (expectedDef.kind === SymbolKind.Class ||
308
+ expectedDef.kind === SymbolKind.Interface ||
309
+ expectedDef.kind === SymbolKind.TypeAlias) {
310
+ return referenceKind === "class";
311
+ }
312
+ return false;
313
+ }
314
+ async function collectNamedNodeReferences(index, fileId, symbolName, symbolKind) {
104
315
  try {
105
316
  const parsedEntry = index.parsed?.get(fileIdentityKey(fileId));
106
317
  const parsed = await ensureParsedContext(fileId, parsedEntry, index.languageExtensions);
@@ -112,15 +323,39 @@ async function collectNamedNodeReferences(index, fileId, symbolName) {
112
323
  "field_identifier",
113
324
  ]);
114
325
  const canonicalSymbolName = parsed.sup.normalizeIdentifier(symbolName);
115
- const ranges = [];
326
+ const isPhp = parsed.sup.id === "php";
327
+ const matched = [];
328
+ let nameEquivalenceUnavailable = false;
116
329
  const moduleIndex = index.byFile.get(fileIdentityKey(fileId));
117
330
  const importDeclarationKeys = importBindingDeclarationRangeKeys(moduleIndex);
331
+ const row = scopeNodesFor(parsed.sup.id);
118
332
  const walk = (node) => {
119
- if (identifierTypes.has(node.type) &&
120
- parsed.sup.normalizeIdentifier(sliceText(node, parsed.source)) === canonicalSymbolName) {
121
- const range = toRange(node);
122
- if (!importDeclarationKeys.has(rangeIdentityKey(range))) {
123
- ranges.push(range);
333
+ if (identifierTypes.has(node.type)) {
334
+ const text = parsed.sup.normalizeIdentifier(sliceText(node, parsed.source));
335
+ let isMatch;
336
+ if (!isPhp) {
337
+ isMatch = text === canonicalSymbolName;
338
+ }
339
+ else if ((node.type === "name" || node.type === "namespace_name") &&
340
+ (isPhpQualifiedReferenceNode(node.parent) ||
341
+ (node.parent && identifierTypes.has(node.parent.type) && row.childSkipNameTypes?.has(node.type)))) {
342
+ isMatch = false;
343
+ }
344
+ else {
345
+ const comparisonText = node.type === "qualified_name" || node.type === "relative_name" ? phpLastIdentifierSegment(text) : text;
346
+ const comparison = comparePhpReferenceNames(comparisonText, phpLastIdentifierSegment(canonicalSymbolName), {
347
+ caseSensitiveForm: node.type === "variable_name" || node.type === "constant",
348
+ ...(symbolKind ? { symbolKind } : {}),
349
+ });
350
+ if (comparison === "unverified")
351
+ nameEquivalenceUnavailable = true;
352
+ isMatch = comparison === "equivalent";
353
+ }
354
+ if (isMatch) {
355
+ const range = toRange(node);
356
+ if (!importDeclarationKeys.has(rangeIdentityKey(range)) && !(isPhp && isInsidePhpUseDeclaration(node))) {
357
+ matched.push({ range, node });
358
+ }
124
359
  }
125
360
  }
126
361
  for (const child of node.namedChildren) {
@@ -128,31 +363,149 @@ async function collectNamedNodeReferences(index, fileId, symbolName) {
128
363
  }
129
364
  };
130
365
  walk(parsed.tree.rootNode);
131
- return { ranges, parsed };
366
+ return { matched, parsed, nameEquivalenceUnavailable };
132
367
  }
133
368
  catch {
134
369
  return null;
135
370
  }
136
371
  }
137
- export async function collectVerifiedNamedNodeReferences(index, fileId, symbolName, expectedDef, resolveDefinition, maxVerified, includeReference) {
138
- const collected = await collectNamedNodeReferences(index, fileId, symbolName);
372
+ /** True when `range` is the property of a member-access expression, not a bare call. */
373
+ export function isMemberAccessPropertyRange(parsed, range) {
374
+ const startIndex = range.start.index;
375
+ const node = startIndex !== undefined
376
+ ? parsed.tree.rootNode.descendantForIndex(startIndex, range.end.index ?? startIndex)
377
+ : parsed.tree.rootNode.descendantForPosition({ row: range.start.line - 1, column: range.start.column - 1 }, { row: range.start.line - 1, column: range.start.column - 1 });
378
+ const parent = node.parent;
379
+ if (!parent || !isMemberAccessNode(parsed.sup, parent))
380
+ return false;
381
+ const property = getMemberAccessParts(parsed.sup, parent).property;
382
+ return !!property && node.startIndex >= property.startIndex && node.endIndex <= property.endIndex;
383
+ }
384
+ /**
385
+ * A member call on a local, parameter, field, or `this`/`self` cannot name a free function
386
+ * in a language whose member syntax never calls one. A missing receiver, or a module or
387
+ * namespace binding (`util.helper()`, `mod.thing()`), stays unproven.
388
+ */
389
+ function freeFunctionMemberSiteIsProvenNonReference(index, fileId, parsed, objectNode, expectedDef) {
390
+ if (expectedDef.isMember || expectedDef.kind !== SymbolKind.Function)
391
+ return false;
392
+ if (memberSyntaxNamesFreeFunction(parsed.sup.id))
393
+ return false;
394
+ const receiverText = sliceText(objectNode, parsed.source).trim();
395
+ if (isKeywordReceiver(parsed.sup.id, receiverText))
396
+ return true;
397
+ if (!isReceiverNameNode(parsed.sup, objectNode.type))
398
+ return false;
399
+ const mod = index.byFile.get(fileIdentityKey(fileId));
400
+ if (!mod)
401
+ return false;
402
+ const binding = findClosestScopeBinding(getCachedScope(index, fileId, mod, parsed), receiverText, objectNode, parsed.sup);
403
+ return binding?.kind === "local" || binding?.kind === "param";
404
+ }
405
+ async function receiverProofUnavailable(index, fileId, parsed, range, resolveDefinition, expectedDef) {
406
+ const position = {
407
+ row: range.start.line - 1,
408
+ column: range.start.column - 1,
409
+ };
410
+ const nameNode = parsed.tree.rootNode.descendantForPosition(position, position);
411
+ let current = nameNode.parent;
412
+ while (current) {
413
+ if (isMemberAccessNode(parsed.sup, current)) {
414
+ const { object, property } = getMemberAccessParts(parsed.sup, current);
415
+ if (!object || !property || property.startIndex !== range.start.index)
416
+ return false;
417
+ const receiver = classifyReceiver(parsed.sup, object, parsed.source, new Map(), current.startIndex, current, (callee) => {
418
+ const mod = index.byFile.get(fileIdentityKey(fileId));
419
+ if (!mod)
420
+ return true;
421
+ const scope = getCachedScope(index, fileId, mod, parsed);
422
+ return !!findClosestScopeBinding(scope, sliceText(callee, parsed.source), callee, parsed.sup);
423
+ });
424
+ // A recognized shape is not proof. Exclude it only when the type is a resolved
425
+ // member-declaring definition, every supertype resolves, and none declare this member.
426
+ let unavailable;
427
+ if (receiver) {
428
+ const omits = await provenClassifiedReceiverOmitsMember(index, fileId, parsed, current, object, sliceText(property, parsed.source));
429
+ unavailable = !omits;
430
+ }
431
+ else {
432
+ const receiverRange = toRange(object);
433
+ const resolvedReceiver = await resolveDefinition({
434
+ file: fileId,
435
+ line: receiverRange.start.line,
436
+ column: receiverRange.start.column,
437
+ }, parsed);
438
+ unavailable = !(resolvedReceiver.status === "ok" &&
439
+ resolvedReceiver.definition &&
440
+ declaresMembers(resolvedReceiver.definition));
441
+ }
442
+ if (!unavailable)
443
+ return false;
444
+ return !freeFunctionMemberSiteIsProvenNonReference(index, fileId, parsed, object, expectedDef);
445
+ }
446
+ current = current.parent;
447
+ }
448
+ return false;
449
+ }
450
+ export async function collectVerifiedNamedNodeReferences(index, fileId, symbolName, expectedDef, resolveDefinition, maxVerified, includeReference, onReceiverProofUnavailable, equivalentDefinitions = []) {
451
+ const collected = await collectNamedNodeReferences(index, fileId, symbolName, expectedDef.kind);
139
452
  if (!collected)
140
453
  return [];
141
- const { ranges, parsed } = collected;
454
+ const { matched, parsed, nameEquivalenceUnavailable } = collected;
455
+ if (nameEquivalenceUnavailable)
456
+ markPhpNameEquivalenceGap(index, expectedDef);
457
+ const phpCanonicalNames = parsed.sup.id === "php" ? await phpCanonicalDefinitionNames(index, expectedDef) : undefined;
458
+ const phpExistingFunctionNames = phpCanonicalNames && expectedDef.kind === SymbolKind.Function
459
+ ? (await ensurePhpNamespaceSymbolIndex(index)).functionNames
460
+ : undefined;
142
461
  const verified = [];
462
+ const matchesExpectedDefinition = (definition) => sameDef(definition, expectedDef, index.languageExtensions) ||
463
+ equivalentDefinitions.some((equivalent) => sameDef(definition, equivalent, index.languageExtensions));
464
+ const candidateFileKey = fileIdentityKey(fileId);
465
+ const equivalentDefinitionsInFile = equivalentDefinitions.filter((equivalent) => fileIdentityKey(equivalent.file) === candidateFileKey);
466
+ const isEquivalentDeclarationRange = (range) => equivalentDefinitionsInFile.some((equivalent) => {
467
+ const startMatches = range.start.index !== undefined && equivalent.range.start.index !== undefined
468
+ ? range.start.index === equivalent.range.start.index
469
+ : range.start.line === equivalent.range.start.line && range.start.column === equivalent.range.start.column;
470
+ const endMatches = range.end.index !== undefined && equivalent.range.end.index !== undefined
471
+ ? range.end.index === equivalent.range.end.index
472
+ : range.end.line === equivalent.range.end.line && range.end.column === equivalent.range.end.column;
473
+ return startMatches && endMatches;
474
+ });
143
475
  const pushVerified = (reference) => {
144
476
  if (!includeReference || includeReference(reference))
145
477
  verified.push(reference);
146
478
  };
147
- for (const range of ranges) {
479
+ let definitionVisible;
480
+ const definitionVisibleHere = () => {
481
+ if (definitionVisible === undefined) {
482
+ const closure = importClosure(index, fileId);
483
+ definitionVisible = [expectedDef, ...equivalentDefinitions].some((definition) => closure.has(fileIdentityKey(definition.file)));
484
+ }
485
+ return definitionVisible;
486
+ };
487
+ for (const { range, node } of matched) {
148
488
  if (maxVerified !== undefined && maxVerified > 0 && verified.length >= maxVerified) {
149
489
  break;
150
490
  }
491
+ if (isEquivalentDeclarationRange(range)) {
492
+ pushVerified({ range });
493
+ continue;
494
+ }
495
+ if (parsed.sup.id === "c" && expectedDef.kind === SymbolKind.Function) {
496
+ let enclosing = node.parent;
497
+ while (enclosing && enclosing.type !== "field_declaration" && enclosing.type !== "translation_unit") {
498
+ enclosing = enclosing.parent;
499
+ }
500
+ // A C struct function-pointer field is not a declaration or use of a free function.
501
+ if (enclosing?.type === "field_declaration")
502
+ continue;
503
+ }
151
504
  const exportFrom = exportFromIdentifier(index, fileId, range, parsed);
152
505
  if (exportFrom?.entry) {
153
506
  const reexported = resolveExport(index, exportFrom.entry.fromModule, exportFrom.entry.sourceSpecifier);
154
507
  if (reexported?.kind === "resolved") {
155
- if (sameDef(reexported.def, expectedDef, index.languageExtensions)) {
508
+ if (matchesExpectedDefinition(reexported.def)) {
156
509
  pushVerified({ range, via: { reexport: true } });
157
510
  }
158
511
  continue;
@@ -163,14 +516,71 @@ export async function collectVerifiedNamedNodeReferences(index, fileId, symbolNa
163
516
  line: range.start.line,
164
517
  column: range.start.column,
165
518
  }, parsed);
166
- if (resolved.status !== "ok" || !resolved.definition)
519
+ if (resolved.status === "ok" && resolved.definition) {
520
+ if (matchesExpectedDefinition(resolved.definition)) {
521
+ pushVerified({
522
+ range,
523
+ ...(exportFrom?.isExportFrom ? { via: { reexport: true } } : {}),
524
+ ...(resolved.provenance ? { provenance: resolved.provenance } : {}),
525
+ });
526
+ }
167
527
  continue;
168
- if (sameDef(resolved.definition, expectedDef, index.languageExtensions)) {
169
- pushVerified({
170
- range,
171
- ...(exportFrom?.isExportFrom ? { via: { reexport: true } } : {}),
172
- ...(resolved.provenance ? { provenance: resolved.provenance } : {}),
528
+ }
529
+ let recoveredByLanguageFallback = false;
530
+ if (parsed.sup.id === "php" && expectedDef.isMember) {
531
+ const memberMatch = await phpCaseInsensitiveReceiverMemberMatch(index, fileId, node, parsed, expectedDef);
532
+ if (memberMatch === "matched") {
533
+ pushVerified({ range, ...(exportFrom?.isExportFrom ? { via: { reexport: true } } : {}) });
534
+ recoveredByLanguageFallback = true;
535
+ }
536
+ else if (memberMatch === "unverified") {
537
+ markPhpNameEquivalenceGap(index, expectedDef);
538
+ }
539
+ }
540
+ else if (phpCanonicalNames && matchesPhpFallbackDefinition(node, parsed, expectedDef)) {
541
+ // PHP names are case-insensitive, but a namespace spelling alone cannot prove a member or
542
+ // distinguish a class reference from a function call. Restrict the fallback to syntax whose
543
+ // role matches the namespace-level definition.
544
+ const rawText = getPhpQualifiedReference(node, parsed.source) ?? sliceText(node, parsed.source);
545
+ const imports = index.byFile.get(fileIdentityKey(fileId))?.imports;
546
+ const role = inferPhpQualifiedReferenceImportType(node);
547
+ const candidates = canonicalPhpReferenceNames(rawText, parsed.source, parsed.tree, node, {
548
+ ...(imports ? { imports } : {}),
549
+ ...(role ? { role } : {}),
173
550
  });
551
+ const existingNames = phpExistingFunctionNames ?? phpCanonicalNames;
552
+ const selectedCandidate = selectFirstExistingPhpCanonicalName(candidates, existingNames, expectedDef.kind);
553
+ let matchedCanonical;
554
+ if (selectedCandidate) {
555
+ matchedCanonical = phpCanonicalNames.find((canonicalName) => comparePhpReferenceNames(selectedCandidate, canonicalName, { symbolKind: expectedDef.kind }) ===
556
+ "equivalent");
557
+ }
558
+ if (matchedCanonical) {
559
+ // A qualified path proves the namespace, but a bare case-variant `name` could also be a
560
+ // same-named constant, so the equivalence is unproven for that form and coverage says so
561
+ // instead of silently returning a short list.
562
+ if (!isPhpQualifiedReferenceNode(node)) {
563
+ const lastSegment = matchedCanonical.split("\\").pop() ?? matchedCanonical;
564
+ const bareText = sliceText(node, parsed.source).trim();
565
+ if (bareText !== lastSegment && foldPhpIdentifierCase(bareText) === foldPhpIdentifierCase(lastSegment)) {
566
+ markPhpNameEquivalenceGap(index, expectedDef);
567
+ }
568
+ }
569
+ pushVerified({ range, ...(exportFrom?.isExportFrom ? { via: { reexport: true } } : {}) });
570
+ recoveredByLanguageFallback = true;
571
+ }
572
+ }
573
+ // A same-name node that direct resolution and every language-specific fallback both failed
574
+ // to place is not provably unrelated: report the file so coverage cannot silently claim
575
+ // `complete` while this occurrence's status stays unknown. An ambiguous result (star
576
+ // imports, C++ overloads, or using targets) is the same kind of gap when this definition
577
+ // (or an equivalent declaration) is visible from this file: one of the candidates may be
578
+ // it. A name with no binding at all is proven unrelated by scope and stays out.
579
+ if (!recoveredByLanguageFallback && onReceiverProofUnavailable) {
580
+ if ((isAmbiguousResolutionReason(resolved.reason) && definitionVisibleHere()) ||
581
+ (await receiverProofUnavailable(index, fileId, parsed, range, resolveDefinition, expectedDef))) {
582
+ onReceiverProofUnavailable(fileId);
583
+ }
174
584
  }
175
585
  }
176
586
  return verified;
@@ -210,31 +620,77 @@ export function hasExpandedNamedImport(moduleIndex, targetFile, symbolName) {
210
620
  fileIdentityKey(candidate.resolved) === targetKey);
211
621
  }
212
622
  const referenceCandidateCache = new WeakMap();
623
+ /** A package import may expose a child only along its exact dotted module path. */
624
+ export function pythonParentPackageNamespacePaths(moduleIndex, definitionFile) {
625
+ const file = normalizePath(definitionFile);
626
+ const basename = path.posix.basename(file);
627
+ if (!file.endsWith(".py") && !file.endsWith(".pyi"))
628
+ return [];
629
+ const moduleStem = basename === "__init__.py" || basename === "__init__.pyi"
630
+ ? path.posix.dirname(file)
631
+ : file.slice(0, -path.posix.extname(file).length);
632
+ const paths = [];
633
+ for (const imp of moduleIndex.imports) {
634
+ if (imp.kind !== "namespace" || imp.mechanism !== "python" || typeof imp.resolved !== "string")
635
+ continue;
636
+ const target = normalizePath(imp.resolved);
637
+ const targetName = path.posix.basename(target);
638
+ const initializer = targetName === "__init__.py" || targetName === "__init__.pyi";
639
+ if (!initializer && (target.endsWith(".py") || target.endsWith(".pyi")))
640
+ continue;
641
+ const packageDirectory = initializer ? path.posix.dirname(target) : target;
642
+ const child = path.posix.relative(packageDirectory, moduleStem);
643
+ if (!child || child.startsWith("..") || path.posix.isAbsolute(child))
644
+ continue;
645
+ const boundName = imp.from.includes(".") && !imp.explicitAlias ? imp.from : imp.localNS;
646
+ paths.push({ namespace: boundName + "." + child.replaceAll("/", "."), importBinding: imp });
647
+ }
648
+ return paths;
649
+ }
213
650
  function referenceCandidateCacheKey(index, def, exportedNames) {
214
651
  const normalizeIdentifier = supportForFileWithoutHeaderSample(def.file, index.languageExtensions)?.normalizeIdentifier ?? ((name) => name);
215
652
  const sortedNames = exportedNames.map(normalizeIdentifier).sort();
216
653
  return `${fileIdentityKey(def.file)}::${def.range.start.index ?? 0}::canonical::${sortedNames.join("\0")}`;
217
654
  }
218
- function importCanReferenceDefinition(index, imp, def, exportedNames) {
655
+ function importCanReferenceDefinition(index, imp, def, exportedNames, languageId) {
219
656
  const targetFile = typeof imp.resolved === "string" ? imp.resolved : undefined;
220
657
  if (!targetFile)
221
658
  return false;
659
+ let cNamespace;
660
+ if (languageId === "c")
661
+ cNamespace = def.cTag ? "tag" : "ordinary";
662
+ const exportOptions = cNamespace ? { cNamespace } : undefined;
663
+ if (cNamespace && imp.kind === "named" && (imp.cNamespace ?? "ordinary") !== cNamespace)
664
+ return false;
222
665
  const resolvesToDefinition = (exportedName) => {
223
- const hit = resolveExport(index, targetFile, exportedName);
666
+ const hit = resolveExport(index, targetFile, exportedName, exportOptions);
224
667
  if (hit?.kind === "resolved") {
225
668
  return sameDef(hit.def, def, index.languageExtensions);
226
669
  }
670
+ if (cppCanonicalStructuralExport(index, targetFile, exportedName, def, languageId)) {
671
+ return true;
672
+ }
227
673
  return imp.kind === "namespace" && fileIdentityKey(targetFile) === fileIdentityKey(def.file);
228
674
  };
229
675
  if (imp.kind === "named") {
230
- return resolvesToDefinition(imp.imported);
676
+ if (resolvesToDefinition(imp.imported))
677
+ return true;
678
+ // A python `from pkg import name` binds `name` from the package's own namespace; when the
679
+ // package has no such export, Python's own import system falls back to treating `name` as
680
+ // an implicit submodule attribute instead (the same fallback `resolveImported` applies at
681
+ // consumption time). Reusing that fallback here keeps candidate-file discovery in agreement
682
+ // with the goto/reference-collection consumers that already resolve through it.
683
+ if (languageId !== "python")
684
+ return false;
685
+ const result = resolveImported(index, imp, imp.imported, exportOptions);
686
+ return !!result && "namespace" in result && fileIdentityKey(result.namespace) === fileIdentityKey(def.file);
231
687
  }
232
688
  if (imp.kind === "default") {
233
689
  return resolvesToDefinition("default");
234
690
  }
235
691
  if (imp.kind === "star") {
236
692
  return exportedNames.some((exportedName) => {
237
- const result = resolveImported(index, imp, exportedName);
693
+ const result = resolveImported(index, imp, exportedName, exportOptions);
238
694
  return !!result && !("namespace" in result) && sameDef(result, def, index.languageExtensions);
239
695
  });
240
696
  }
@@ -259,10 +715,16 @@ function moduleExportProbeNames(index, moduleIndex, exportedNames, visited = new
259
715
  }
260
716
  return [...names];
261
717
  }
262
- function filesExportingDefinition(index, def, exportedNames) {
718
+ function filesExportingDefinition(index, def, exportedNames, languageId) {
263
719
  const files = new Map([[fileIdentityKey(def.file), def.file]]);
720
+ const namespaceLinks = [];
264
721
  for (const moduleIndex of index.byFile.values()) {
265
722
  const fileId = moduleIndex.file;
723
+ for (const entry of moduleIndex.exports) {
724
+ if (entry.type === "namespaceReexport") {
725
+ namespaceLinks.push({ sourceFile: fileId, exportedAs: entry.exportedAs, targetFile: entry.fromModule });
726
+ }
727
+ }
266
728
  if (fileIdentityKey(fileId) === fileIdentityKey(def.file) || !moduleIndex.exports.length)
267
729
  continue;
268
730
  for (const exportedName of moduleExportProbeNames(index, moduleIndex, exportedNames)) {
@@ -271,6 +733,10 @@ function filesExportingDefinition(index, def, exportedNames) {
271
733
  files.set(fileIdentityKey(fileId), fileId);
272
734
  break;
273
735
  }
736
+ if (cppCanonicalStructuralExport(index, fileId, exportedName, def, languageId)) {
737
+ files.set(fileIdentityKey(fileId), fileId);
738
+ break;
739
+ }
274
740
  }
275
741
  if (moduleIndex.exports.some((entry) => entry.type === "reexport" &&
276
742
  exportedNames.includes(entry.sourceSpecifier) &&
@@ -278,15 +744,31 @@ function filesExportingDefinition(index, def, exportedNames) {
278
744
  files.set(fileIdentityKey(fileId), fileId);
279
745
  }
280
746
  }
747
+ // A namespace re-export can expose an inner symbol through a member chain even
748
+ // though it does not export that symbol under its own name. Include consumers
749
+ // only when the visible namespace alias really targets the exporting module.
750
+ let grew = true;
751
+ while (grew) {
752
+ grew = false;
753
+ for (const link of namespaceLinks) {
754
+ const sourceKey = fileIdentityKey(link.sourceFile);
755
+ if (files.has(sourceKey) || !files.has(fileIdentityKey(link.targetFile)))
756
+ continue;
757
+ const visible = resolveExport(index, link.sourceFile, link.exportedAs, { allowLocalFallback: false });
758
+ if (visible?.kind !== "namespace" || fileIdentityKey(visible.file) !== fileIdentityKey(link.targetFile)) {
759
+ continue;
760
+ }
761
+ files.set(sourceKey, link.sourceFile);
762
+ grew = true;
763
+ }
764
+ }
281
765
  return [...files.values()];
282
766
  }
283
- function getIndexedReferenceCandidateFiles(index, def, exportedNames) {
284
- if (def.file.toLowerCase().endsWith(".php"))
285
- return undefined;
767
+ function getIndexedReferenceCandidateFiles(index, def, exportedNames, languageId) {
286
768
  if (!index.referenceCandidates)
287
769
  return undefined;
288
770
  const files = new Map();
289
- for (const exportingFile of filesExportingDefinition(index, def, exportedNames)) {
771
+ for (const exportingFile of filesExportingDefinition(index, def, exportedNames, languageId)) {
290
772
  for (const importingFile of candidateFilesImportingTarget(index.referenceCandidates, exportingFile) ?? []) {
291
773
  files.set(fileIdentityKey(importingFile), importingFile);
292
774
  }
@@ -295,30 +777,49 @@ function getIndexedReferenceCandidateFiles(index, def, exportedNames) {
295
777
  const fileId = moduleIndex.file;
296
778
  if (fileIdentityKey(fileId) === fileIdentityKey(def.file) || files.has(fileIdentityKey(fileId)))
297
779
  continue;
298
- if (moduleIndex.imports.some((imp) => (imp.kind === "star" || imp.kind === "namespace") &&
299
- importCanReferenceDefinition(index, imp, def, exportedNames))) {
780
+ if (moduleIndex.imports.some((imp) => (imp.kind === "star" || imp.kind === "namespace" || (imp.kind === "named" && imp.mechanism === "python")) &&
781
+ importCanReferenceDefinition(index, imp, def, exportedNames, languageId))) {
300
782
  files.set(fileIdentityKey(fileId), fileId);
301
783
  }
302
784
  }
303
785
  return [...files.values()].sort((left, right) => left.localeCompare(right));
304
786
  }
305
- export function getCachedReferenceCandidateFiles(index, def, exportedNames, hasGlobalNameReferences) {
787
+ export function getCachedReferenceCandidateFiles(index, def, exportedNames, hasGlobalNameReferences, languageId) {
306
788
  if (hasGlobalNameReferences) {
307
- return Array.from(index.byFile.values(), (module) => module.file).sort((left, right) => left.localeCompare(right));
789
+ // Callers only set this for a PHP definition (`buildPhpQualifiedNames` returns names for no
790
+ // other language), and PHP's global-namespace fallback can be referenced from any PHP file
791
+ // without an import, so the import-graph narrowing below cannot apply. Every other language
792
+ // is still a sound, source-free rejection: a PHP symbol can never be referenced from a file
793
+ // whose own language a different parser owns.
794
+ return Array.from(index.byFile.values(), (module) => module.file)
795
+ .filter((file) => supportForFileWithoutHeaderSample(file, index.languageExtensions)?.id === "php")
796
+ .sort((left, right) => left.localeCompare(right));
308
797
  }
309
798
  let cache = referenceCandidateCache.get(index);
310
799
  if (!cache) {
311
800
  cache = new Map();
312
801
  referenceCandidateCache.set(index, cache);
313
802
  }
314
- const key = referenceCandidateCacheKey(index, def, exportedNames);
803
+ const key = `${languageId}:${referenceCandidateCacheKey(index, def, exportedNames)}`;
315
804
  const cached = cache.get(key);
316
805
  if (cached)
317
806
  return cached;
318
807
  const candidates = new Map();
319
- const candidateFileEntries = getIndexedReferenceCandidateFiles(index, def, exportedNames) ??
808
+ if (def.isMember)
809
+ candidates.set(fileIdentityKey(def.file), def.file);
810
+ // Files sharing the definition's compilation unit can name it without any import edge (Go and
811
+ // JVM packages, C# namespaces, Swift modules), so they bypass the import filter below. C#
812
+ // also includes same-directory files that can reach the type only through a qualified name,
813
+ // so dotted and `global::` uses are candidates even when the consumer declares unrelated
814
+ // namespaces. Bare-name resolution still uses the related-namespace peer set.
815
+ for (const unitPeer of getCompilationUnitPeers(index, def.file, languageId === "csharp" ? { csharpQualifiedName: true } : undefined).files) {
816
+ if (fileIdentityKey(unitPeer) !== fileIdentityKey(def.file)) {
817
+ candidates.set(fileIdentityKey(unitPeer), unitPeer);
818
+ }
819
+ }
820
+ const candidateFileEntries = getIndexedReferenceCandidateFiles(index, def, exportedNames, languageId) ??
320
821
  Array.from(index.byFile.values(), (module) => module.file);
321
- const exportingFileIds = filesExportingDefinition(index, def, exportedNames);
822
+ const exportingFileIds = filesExportingDefinition(index, def, exportedNames, languageId);
322
823
  const exportingFiles = new Set(exportingFileIds.map((file) => fileIdentityKey(file)));
323
824
  for (const fileId of exportingFileIds) {
324
825
  if (fileIdentityKey(fileId) !== fileIdentityKey(def.file)) {
@@ -335,16 +836,61 @@ export function getCachedReferenceCandidateFiles(index, def, exportedNames, hasG
335
836
  if (typeof imp.resolved !== "string")
336
837
  return false;
337
838
  return (exportingFiles.has(fileIdentityKey(imp.resolved)) ||
338
- importCanReferenceDefinition(index, imp, def, exportedNames));
839
+ importCanReferenceDefinition(index, imp, def, exportedNames, languageId));
339
840
  })) {
340
841
  candidates.set(fileIdentityKey(fileId), fileId);
341
842
  }
342
843
  }
844
+ // A plain package import does not prove its child's attribute, but a same-name use
845
+ // through that package must be checked before coverage can claim completeness.
846
+ if (languageId === "python" && !def.isMember) {
847
+ for (const moduleIndex of index.byFile.values()) {
848
+ if (pythonParentPackageNamespacePaths(moduleIndex, def.file).length) {
849
+ candidates.set(fileIdentityKey(moduleIndex.file), moduleIndex.file);
850
+ }
851
+ }
852
+ }
343
853
  const sorted = [...candidates.values()].sort((left, right) => left.localeCompare(right));
344
854
  cache.set(key, sorted);
345
855
  return sorted;
346
856
  }
347
- const COVERAGE_REASON_ORDER = ["parser_degraded", "unresolved_import", "truncated"];
857
+ /**
858
+ * The single precedence table for reference-coverage reasons, shared by the direct
859
+ * indexed-candidate builder and the bounded reference lookup cache. Existing reasons keep
860
+ * their established relative order so current expectations stay stable; the two strategy
861
+ * reasons sit after the file-level reasons they refine and before `truncated`, because a
862
+ * skipped collection strategy is a correctness gap while `truncated` only reflects the
863
+ * caller's own requested bound.
864
+ */
865
+ export const REFERENCE_COVERAGE_REASON_ORDER = [
866
+ "parser_degraded",
867
+ "unresolved_import",
868
+ "strategy_unavailable",
869
+ "name_equivalence_unavailable",
870
+ "truncated",
871
+ ];
872
+ export function describeReferenceStrategies(args) {
873
+ const applicable = [];
874
+ const executed = [];
875
+ if (args.sameFileOccurrence?.applicable) {
876
+ applicable.push("same_file_occurrence");
877
+ if (args.sameFileOccurrence.executed)
878
+ executed.push("same_file_occurrence");
879
+ }
880
+ if (args.implicitUnitPeers?.applicable) {
881
+ applicable.push("implicit_unit_peers");
882
+ if (args.implicitUnitPeers.executed)
883
+ executed.push("implicit_unit_peers");
884
+ }
885
+ if (args.languageId === "php") {
886
+ // Every PHP definition is addressable by its global or namespace-qualified spelling, so
887
+ // the qualified-name scan is required; the receiver scan is not the PHP strategy.
888
+ applicable.push("php_qualified_name");
889
+ if (args.phpQualifiedNames.length)
890
+ executed.push("php_qualified_name");
891
+ }
892
+ return { applicable, executed };
893
+ }
348
894
  export function rangeIdentityKey(range) {
349
895
  return `${range.start.line}:${range.start.column}:${range.start.index ?? ""}:${range.end.line}:${range.end.column}:${range.end.index ?? ""}`;
350
896
  }
@@ -379,7 +925,7 @@ export function importBindingReferenceSites(imp) {
379
925
  sites.push({ range: localRange, importBinding: "local" });
380
926
  }
381
927
  }
382
- else if (imp.kind === "default") {
928
+ else if (imp.kind === "default" || imp.kind === "namespace") {
383
929
  const { localRange } = bindingTokenRanges(imp);
384
930
  if (localRange) {
385
931
  sites.push({ range: localRange, importBinding: "local" });
@@ -443,7 +989,13 @@ function structurallyExportsDefinition(index, moduleFile, exportedName, def, def
443
989
  }
444
990
  return false;
445
991
  }
446
- function isUnresolvedIndexedImport(index, imp, def, exportedNames) {
992
+ /** Recover C++ overload candidates through real exports, never the unresolved-import name shortcut. */
993
+ export function cppCanonicalStructuralExport(index, targetFile, exportedName, def, languageId) {
994
+ if (languageId !== "cpp")
995
+ return false;
996
+ return structurallyExportsDefinition(index, targetFile, exportedName, def, []);
997
+ }
998
+ function isUnresolvedIndexedImport(index, imp, def, exportedNames, languageId) {
447
999
  if (typeof imp.resolved !== "string")
448
1000
  return false;
449
1001
  let importedName;
@@ -455,7 +1007,7 @@ function isUnresolvedIndexedImport(index, imp, def, exportedNames) {
455
1007
  return false;
456
1008
  if (!structurallyExportsDefinition(index, imp.resolved, importedName, def, exportedNames))
457
1009
  return false;
458
- return !importCanReferenceDefinition(index, imp, def, exportedNames);
1010
+ return !importCanReferenceDefinition(index, imp, def, exportedNames, languageId);
459
1011
  }
460
1012
  function parserDegradedCandidateFiles(index, scannedFiles) {
461
1013
  const report = index.buildReport?.backend?.parser;
@@ -484,7 +1036,7 @@ function parserDegradedCandidateFiles(index, scannedFiles) {
484
1036
  return { files: affected, hasUnlistedCandidate };
485
1037
  }
486
1038
  export function buildIndexedCandidateCoverage(args) {
487
- const { index, def, exportedNames, candidateFiles, scannedFiles, truncated } = args;
1039
+ const { index, def, languageId, exportedNames, candidateFiles, scannedFiles, truncated, strategies, strategyUnavailableFiles = [], } = args;
488
1040
  const reasons = [];
489
1041
  const affectedFiles = [];
490
1042
  const affectedSeen = new Set();
@@ -506,7 +1058,7 @@ export function buildIndexedCandidateCoverage(args) {
506
1058
  const moduleIndex = index.byFile.get(fileIdentityKey(fileId));
507
1059
  if (!moduleIndex)
508
1060
  continue;
509
- if (moduleIndex.imports.some((imp) => isUnresolvedIndexedImport(index, imp, def, exportedNames))) {
1061
+ if (moduleIndex.imports.some((imp) => isUnresolvedIndexedImport(index, imp, def, exportedNames, languageId))) {
510
1062
  unresolvedFiles.push(fileId);
511
1063
  }
512
1064
  }
@@ -515,6 +1067,20 @@ export function buildIndexedCandidateCoverage(args) {
515
1067
  for (const file of unresolvedFiles)
516
1068
  addAffected(file);
517
1069
  }
1070
+ if (strategyUnavailableFiles.length) {
1071
+ reasons.push("strategy_unavailable");
1072
+ for (const file of strategyUnavailableFiles)
1073
+ addAffected(file);
1074
+ }
1075
+ if (strategies) {
1076
+ const executed = new Set(strategies.executed);
1077
+ if (strategies.applicable.some((strategy) => !executed.has(strategy))) {
1078
+ reasons.push("strategy_unavailable");
1079
+ }
1080
+ }
1081
+ if (phpNameEquivalenceGaps.get(index)?.has(definitionIdentityKey(def))) {
1082
+ reasons.push("name_equivalence_unavailable");
1083
+ }
518
1084
  if (truncated)
519
1085
  reasons.push("truncated");
520
1086
  if (!reasons.length) {
@@ -523,7 +1089,7 @@ export function buildIndexedCandidateCoverage(args) {
523
1089
  return {
524
1090
  scope: "indexed_candidates",
525
1091
  state: "partial",
526
- reasons: COVERAGE_REASON_ORDER.filter((reason) => reasons.includes(reason)),
1092
+ reasons: REFERENCE_COVERAGE_REASON_ORDER.filter((reason) => reasons.includes(reason)),
527
1093
  ...(affectedFiles.length ? { affectedFiles } : {}),
528
1094
  };
529
1095
  }