@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,15 +1,405 @@
1
+ import { CSHARP_SUPPORT, supportForFileWithoutHeaderSample } from "../languages.js";
1
2
  import { isJsTsLanguage } from "../languages/js-family.js";
2
3
  import { isPythonReceiverAttributeAssignmentName } from "../languages/definitions/python.js";
3
4
  import { sliceText } from "../util/ast.js";
5
+ import { foldPhpIdentifierCase } from "../util/identifiers.js";
4
6
  import { fileIdentityKey } from "../util/paths.js";
5
- import { getMemberAccessParts, getNavigationExpressionProperty, isMemberAccessNode, isReceiverNameNode, memberAccessTraversalTypes, } from "../util/member-access.js";
6
- import { declaresMembers, hasStaticMemberDistinction, isKeywordReceiver, nearestMemberContainer, receiverConstructorExpression, supportsReceiverMemberNavigation, unwrapNamedType, } from "../graphs/symbol-graph-detailed/receiver-calls.js";
7
+ import { collectMemberAccessChain, getMemberAccessParts, getNavigationExpressionProperty, isMemberAccessNode, isReceiverNameNode, memberAccessTraversalTypes, receiverKeywordText, } from "../util/member-access.js";
8
+ import { keywordReceiverKind, MEMBER_ACCESS_ROWS, supportsReceiverMemberNavigation, } from "../util/member-access-tables.js";
9
+ import { cppCallableShapeForNode } from "./cpp-callables.js";
10
+ import { typescriptCallableContainerKey, typescriptMergedNamespaceContainers, typescriptSelectOverloadCandidate, } from "./ts-callables.js";
11
+ import { effectiveExplicitBinding, isExpandedStarBinding, resolveStarImportedName, resolveStarImportedNamespace, starImportPrecedence, } from "./star-import-precedence.js";
12
+ import { classifyReceiver, cppOutOfLineOwnerPath, cppQualifiedNameSegments, declarationIsStaticEquivalent, declarationNodeIsStatic, declaresMembers, hasStaticMemberDistinction, memberContainerDef, nearestMemberContainer, keywordReceiverCrossesDynamicBoundary, isUnprovenHeritageExpression, keywordReceiverMemberScope, kotlinExtensionReceiverTypeNode, csharpDottedNameRoot, nodeInStaticMemberContext, importTypeQuerySpecifier, receiverConstructorExpression, rustImplSelfTypeNode, supportsStaticMemberScope, TRANSPARENT_MEMBER_CONTAINER_TYPES, unwrapNamedType, } from "../graphs/symbol-graph-detailed/receiver-calls.js";
13
+ import { CALLABLE_DECLARATION_NODE_TYPES, getCallableArity, getCallArgumentCount, memberLookupBinding, } from "../languages/callable-arity.js";
14
+ import { getCompilationUnitPeers } from "./compilation-units.js";
15
+ import { isExportedDeclaration, isPrivateDeclaration, isGoExportedMemberName, isSwiftCrossFileHiddenSharedOwnerMember, } from "./declaration-visibility.js";
7
16
  import { ensureParsedContext } from "./parse-context.js";
17
+ import { csharpLookupName, csharpQualifiedNameNode, findClosestScopeBinding, getOrBuildScopeIndex, resolveNamedDefinition, } from "./navigation-local.js";
18
+ import { resolveCppQualifiedMemberContainer, cppFunctionHasImplicitThis } from "./navigation-cpp.js";
8
19
  import { okGoToResult } from "./navigation-provenance.js";
9
- import { resolveExport, resolveImported } from "./navigation-resolve.js";
20
+ import { comparePhpReferenceNames, findPhpImportAlias } from "./navigation-php.js";
21
+ import { resolveIndexedPhpClassReference, resolvePhpNamespaceSymbol } from "./php-namespace-symbols.js";
22
+ import { cjsRequireValueBinding, memberContainerForDefinition, resolveCsharpQualifiedName, resolveExport, resolveImported, resolvePhpExportByImportType, resolveImportTypeMember, } from "./navigation-resolve.js";
23
+ import { CSHARP_PARTIAL_CONTAINER_TYPES, getSharedOwnerIdentity, isSwiftExtensionContainer, sharedOwnerCanUseMembers, } from "./shared-owner-identity.js";
10
24
  import { SymbolKind, } from "./types.js";
25
+ /**
26
+ * One bound for every receiver-hierarchy walk in this module: keyword `super`/`parent` lookup,
27
+ * Go struct embedding, and Python base classes. Each walk keeps its own visited set, so this is a
28
+ * resource bound rather than a cycle guard; keeping a single constant stops the three walks from
29
+ * drifting to different semantic cutoffs.
30
+ */
31
+ const RECEIVER_HIERARCHY_DEPTH = 16;
32
+ function collectSharedOwnerContainers(root, languageId, out) {
33
+ if (languageId === "csharp") {
34
+ if (CSHARP_PARTIAL_CONTAINER_TYPES.has(root.type))
35
+ out.push(root);
36
+ }
37
+ else if (languageId === "swift") {
38
+ if (root.type === "class_declaration")
39
+ out.push(root);
40
+ }
41
+ for (const child of root.namedChildren ?? [])
42
+ collectSharedOwnerContainers(child, languageId, out);
43
+ }
44
+ const sharedOwnerContainersByRoot = new WeakMap();
45
+ function sharedOwnerContainersInTree(root, languageId) {
46
+ let byLanguage = sharedOwnerContainersByRoot.get(root);
47
+ if (!byLanguage) {
48
+ byLanguage = new Map();
49
+ sharedOwnerContainersByRoot.set(root, byLanguage);
50
+ }
51
+ const cached = byLanguage.get(languageId);
52
+ if (cached)
53
+ return cached;
54
+ const collected = [];
55
+ collectSharedOwnerContainers(root, languageId, collected);
56
+ byLanguage.set(languageId, collected);
57
+ return collected;
58
+ }
59
+ export async function resolveSharedOwnerContainers(params) {
60
+ const { index, ownerFile, ownerContainer, ownerSource, languageId } = params;
61
+ if (languageId !== "csharp" && languageId !== "swift")
62
+ return [];
63
+ const ownerIdentity = getSharedOwnerIdentity(ownerContainer, ownerSource, languageId, ownerFile);
64
+ if (!ownerIdentity)
65
+ return [];
66
+ // A C# `file` owner has only same-file parts, so no other file is a candidate peer.
67
+ const peers = ownerIdentity.fileLocalTo
68
+ ? { files: new Set([ownerFile]), complete: true }
69
+ : getCompilationUnitPeers(index, ownerFile);
70
+ const out = [];
71
+ const seen = new Set();
72
+ const ownerIsExtension = languageId === "swift" ? isSwiftExtensionContainer(ownerContainer, ownerSource) : false;
73
+ const ownerKey = fileIdentityKey(ownerFile);
74
+ const units = [];
75
+ for (const file of peers.files) {
76
+ const peer = index.byFile.get(fileIdentityKey(file));
77
+ if (peer)
78
+ units.push(peer);
79
+ }
80
+ for (const peer of units) {
81
+ let peerContext;
82
+ try {
83
+ peerContext = await ensureParsedContext(peer.file, index.parsed?.get(fileIdentityKey(peer.file)), index.languageExtensions);
84
+ }
85
+ catch {
86
+ continue;
87
+ }
88
+ if (peerContext.sup.id !== languageId)
89
+ continue;
90
+ const containers = sharedOwnerContainersInTree(peerContext.tree.rootNode, languageId);
91
+ for (const container of containers) {
92
+ if (fileIdentityKey(peer.file) === ownerKey &&
93
+ container.startIndex === ownerContainer.startIndex &&
94
+ container.endIndex === ownerContainer.endIndex) {
95
+ continue;
96
+ }
97
+ const identity = getSharedOwnerIdentity(container, peerContext.source, languageId, peer.file);
98
+ if (!identity || !sharedOwnerCanUseMembers(ownerIdentity, identity))
99
+ continue;
100
+ if (languageId === "swift") {
101
+ const peerIsExtension = isSwiftExtensionContainer(container, peerContext.source);
102
+ if (!ownerIsExtension && !peerIsExtension)
103
+ continue;
104
+ }
105
+ const key = fileIdentityKey(peer.file) + ":" + container.startIndex + ":" + container.endIndex;
106
+ if (seen.has(key))
107
+ continue;
108
+ seen.add(key);
109
+ out.push({ file: peer.file, container, context: peerContext, module: peer });
110
+ }
111
+ }
112
+ return out;
113
+ }
114
+ /**
115
+ * Unit-boundary completeness for a C# partial or Swift type/extension member. Its other
116
+ * owner parts, and therefore its uses, are bounded by `getCompilationUnitPeers`, so an
117
+ * unproven unit boundary leaves the member's reference set incomplete. Returns null when
118
+ * the member's owner cannot be shared across files.
119
+ */
120
+ export async function sharedOwnerMemberUnitComplete(index, def) {
121
+ if (!def.isMember)
122
+ return null;
123
+ let context;
124
+ try {
125
+ context = await ensureParsedContext(def.file, index.parsed?.get(fileIdentityKey(def.file)), index.languageExtensions);
126
+ }
127
+ catch {
128
+ return null;
129
+ }
130
+ const languageId = context.sup.id;
131
+ if (languageId !== "csharp" && languageId !== "swift")
132
+ return null;
133
+ const nameNode = nameNodeForDef(context, def);
134
+ const container = nameNode ? nearestMemberContainer(nameNode) : null;
135
+ const identity = container ? getSharedOwnerIdentity(container, context.source, languageId, def.file) : null;
136
+ if (!identity)
137
+ return null;
138
+ // File-local owners have no parts in other files, so the reference set is complete.
139
+ if (identity.fileLocalTo)
140
+ return true;
141
+ // A C# member is reachable through a qualified owner (`P.Box`) from any namespace, so use the
142
+ // same qualified peer relation as reference candidate discovery.
143
+ return getCompilationUnitPeers(index, def.file, languageId === "csharp" ? { csharpQualifiedName: true } : undefined)
144
+ .complete;
145
+ }
146
+ /**
147
+ * Other C# `partial` type parts that share owner identity with `def`.
148
+ * Reference collection treats them as one type so uses that resolve to the
149
+ * coalesced representative still match a query started on any part.
150
+ */
151
+ export async function findCsharpPartialTypeEquivalents(index, def) {
152
+ if (!declaresMembers(def))
153
+ return [];
154
+ const module = index.byFile.get(fileIdentityKey(def.file));
155
+ if (!module)
156
+ return [];
157
+ let context;
158
+ try {
159
+ context = await ensureParsedContext(def.file, index.parsed?.get(fileIdentityKey(def.file)), index.languageExtensions);
160
+ }
161
+ catch {
162
+ return [];
163
+ }
164
+ if (context.sup.id !== "csharp")
165
+ return [];
166
+ const start = def.range.start;
167
+ const nameNode = context.tree.rootNode.descendantForPosition({ row: start.line - 1, column: start.column - 1 }, { row: start.line - 1, column: start.column - 1 });
168
+ const container = nearestMemberContainer(nameNode);
169
+ if (!container)
170
+ return [];
171
+ const peers = await resolveSharedOwnerContainers({
172
+ index,
173
+ ownerFile: def.file,
174
+ ownerContainer: container,
175
+ ownerSource: context.source,
176
+ languageId: "csharp",
177
+ });
178
+ const out = [];
179
+ for (const peer of peers) {
180
+ const peerName = peer.container.childForFieldName("name");
181
+ if (!peerName)
182
+ continue;
183
+ const peerDef = peer.module.locals.find((local) => declaresMembers(local) && local.range.start.index === peerName.startIndex);
184
+ if (peerDef)
185
+ out.push(peerDef);
186
+ }
187
+ return out;
188
+ }
189
+ /** Reusable shared-owner member lookup for keyword navigation and receiver graphs. */
190
+ export async function findSharedOwnerMemberDefinitions(params) {
191
+ const { index, ownerFile, ownerContainer, ownerSource, languageId, member, memberScope } = params;
192
+ const peers = await resolveSharedOwnerContainers({ index, ownerFile, ownerContainer, ownerSource, languageId });
193
+ const out = [];
194
+ for (const peer of peers) {
195
+ const predicate = !memberScope || memberScope === "any"
196
+ ? undefined
197
+ : (local) => matchesReceiverMemberScope(local, memberScope, peer.context, peer.container);
198
+ const hits = findDirectLocalsWithinNode(peer.module.locals, member, peer.container, peer.context, peer.context.sup.normalizeIdentifier, predicate);
199
+ for (const hit of hits) {
200
+ const hitNode = nameNodeForDef(peer.context, hit);
201
+ if (languageId === "swift" && fileIdentityKey(ownerFile) !== fileIdentityKey(peer.file)) {
202
+ if (!hitNode || isSwiftCrossFileHiddenSharedOwnerMember(languageId, ownerFile, peer.file, hitNode))
203
+ continue;
204
+ }
205
+ if (!out.includes(hit))
206
+ out.push(hit);
207
+ }
208
+ }
209
+ return out;
210
+ }
211
+ function nameNodeForDef(context, def) {
212
+ const start = def.range.start;
213
+ const index = start.index;
214
+ if (index !== undefined)
215
+ return context.tree.rootNode.descendantForIndex(index, index);
216
+ const position = { row: start.line - 1, column: start.column - 1 };
217
+ return context.tree.rootNode.descendantForPosition(position, position);
218
+ }
219
+ function enclosingImportScope(declarationName) {
220
+ let current = declarationName;
221
+ while (current && current.type !== "variable_declaration" && current.type !== "using_directive") {
222
+ current = current.parent;
223
+ }
224
+ return current?.parent ?? null;
225
+ }
226
+ /**
227
+ * Rewrites a valid C# `using X = Namespace; X::Target` lookup into the dotted form
228
+ * export resolution already understands. `global::` and ordinary dotted names keep
229
+ * their source spelling; an unknown alias stays `X::Target` instead of a bare name.
230
+ */
231
+ export function csharpAliasQualifiedLookupName(node, source, fallback, imports) {
232
+ const raw = csharpLookupName(node, source, fallback);
233
+ if (raw.startsWith("global::"))
234
+ return raw;
235
+ const chainNode = csharpQualifiedNameNode(node);
236
+ if (!chainNode || !raw.includes("::"))
237
+ return raw;
238
+ const names = [];
239
+ let current = chainNode;
240
+ let base = null;
241
+ while (current && (current.type === "qualified_name" || current.type === "alias_qualified_name")) {
242
+ const parts = getMemberAccessParts(CSHARP_SUPPORT, current);
243
+ base = parts.object ?? base;
244
+ let property = parts.property;
245
+ if (property?.type === "generic_name") {
246
+ property = property.childForFieldName("name") ?? property.namedChildren[0] ?? property;
247
+ }
248
+ if (property?.type === "identifier")
249
+ names.push(sliceText(property, source));
250
+ current = base;
251
+ }
252
+ if (!base || !isReceiverNameNode(CSHARP_SUPPORT, base.type) || !names.length)
253
+ return raw;
254
+ const alias = sliceText(base, source);
255
+ if (alias === "global")
256
+ return raw;
257
+ const imported = innermostNamespaceImport(imports, alias, base, CSHARP_SUPPORT.normalizeIdentifier);
258
+ if (!imported)
259
+ return raw;
260
+ return `${imported.from}.${[...names].reverse().join(".")}`;
261
+ }
262
+ /**
263
+ * Innermost in-scope namespace import bound to `alias`. `normalize` applies the language's
264
+ * identifier equality (C# `@X` and `X` are one alias); the default compares exact spelling.
265
+ */
266
+ export function innermostNamespaceImport(imports, alias, useNode, normalize = (name) => name) {
267
+ const normalizedAlias = normalize(alias);
268
+ const matches = imports.filter((candidate) => candidate.kind === "namespace" && normalize(candidate.localNS) === normalizedAlias);
269
+ if (!matches.length)
270
+ return undefined;
271
+ let root = useNode;
272
+ while (root.parent)
273
+ root = root.parent;
274
+ let best;
275
+ let bestScopeSpan = Number.POSITIVE_INFINITY;
276
+ let bestStart = -1;
277
+ for (const match of matches) {
278
+ const start = match.localRange?.start.index;
279
+ if (start === undefined)
280
+ continue;
281
+ let nameNode = root;
282
+ for (;;) {
283
+ const child = nameNode.namedChildren.find((candidate) => candidate.startIndex <= start && start < candidate.endIndex);
284
+ if (!child)
285
+ break;
286
+ nameNode = child;
287
+ }
288
+ const scope = enclosingImportScope(nameNode);
289
+ if (!scope)
290
+ continue;
291
+ if (start > useNode.startIndex && scope.parent)
292
+ continue;
293
+ if (scope.startIndex > useNode.startIndex || scope.endIndex < useNode.endIndex)
294
+ continue;
295
+ const span = scope.endIndex - scope.startIndex;
296
+ if (span < bestScopeSpan || (span === bestScopeSpan && start >= bestStart)) {
297
+ bestScopeSpan = span;
298
+ bestStart = start;
299
+ best = match;
300
+ }
301
+ }
302
+ if (best)
303
+ return best;
304
+ const unlocated = matches.filter((match) => match.localRange?.start.index === undefined);
305
+ if (unlocated.length === 1)
306
+ return unlocated[0];
307
+ const files = new Map();
308
+ for (const match of unlocated) {
309
+ if (typeof match.resolved !== "string")
310
+ continue;
311
+ const key = fileIdentityKey(match.resolved);
312
+ if (!files.has(key))
313
+ files.set(key, match);
314
+ }
315
+ if (files.size === 1)
316
+ return [...files.values()][0];
317
+ return undefined;
318
+ }
319
+ function rubyScopeResolutionPath(node, source) {
320
+ if (node.type !== "constant")
321
+ return null;
322
+ const owner = node.parent;
323
+ if (owner?.type !== "scope_resolution")
324
+ return null;
325
+ const ownerName = owner.childForFieldName("name");
326
+ if (!ownerName || ownerName.startIndex !== node.startIndex || ownerName.endIndex !== node.endIndex)
327
+ return null;
328
+ // `Outer::Inner::Tool` nests scope_resolution on `scope`, so flatten from the
329
+ // clicked name instead of stopping at the first nested scope.
330
+ const segments = [];
331
+ let current = owner;
332
+ while (current?.type === "scope_resolution") {
333
+ const name = current.childForFieldName("name");
334
+ const scope = current.childForFieldName("scope");
335
+ if (!name || name.type !== "constant")
336
+ return null;
337
+ const text = sliceText(name, source);
338
+ if (!text)
339
+ return null;
340
+ segments.unshift(text);
341
+ current = scope;
342
+ }
343
+ if (!current || current.type !== "constant" || segments.length === 0)
344
+ return null;
345
+ const root = sliceText(current, source);
346
+ if (!root)
347
+ return null;
348
+ return { path: `${root}::${segments.join("::")}`, root };
349
+ }
350
+ /**
351
+ * `Outer::Inner::Tool` is one constant path. Nested classes are exported under that
352
+ * path, not as bare `Tool`, so the chain has to ask for the qualified export.
353
+ */
354
+ function resolveRubyQualifiedConstantDefinition(index, mod, node, source) {
355
+ const qualified = rubyScopeResolutionPath(node, source);
356
+ if (!qualified)
357
+ return null;
358
+ const files = [];
359
+ const seen = new Set();
360
+ const add = (file) => {
361
+ if (!file)
362
+ return;
363
+ const key = fileIdentityKey(file);
364
+ if (seen.has(key))
365
+ return;
366
+ seen.add(key);
367
+ files.push(file);
368
+ };
369
+ for (const imp of mod.imports) {
370
+ if (imp.kind === "namespace" && imp.localNS === qualified.root && typeof imp.resolved === "string") {
371
+ add(imp.resolved);
372
+ }
373
+ }
374
+ add(mod.file);
375
+ const hits = [];
376
+ for (const file of files) {
377
+ const hit = resolveExport(index, file, qualified.path, { allowLocalFallback: false });
378
+ if (hit?.kind !== "resolved")
379
+ continue;
380
+ if (hits.some((candidate) => fileIdentityKey(candidate.file) === fileIdentityKey(hit.def.file) &&
381
+ candidate.localName === hit.def.localName &&
382
+ candidate.range.start.line === hit.def.range.start.line &&
383
+ candidate.range.start.column === hit.def.range.start.column)) {
384
+ continue;
385
+ }
386
+ hits.push(hit.def);
387
+ }
388
+ if (hits.length !== 1)
389
+ return null;
390
+ return okGoToResult(index, hits[0], {
391
+ via: { exportedName: qualified.path },
392
+ resolution: "member-access",
393
+ confidence: "medium",
394
+ });
395
+ }
11
396
  export async function resolveMemberAccessDefinition(params) {
12
- const { index, mod, node, source, sup, resolveLexicalBinding } = params;
397
+ const { index, mod, node, source, tree, sup, resolveLexicalBinding } = params;
398
+ if (sup.id === "ruby") {
399
+ const qualified = resolveRubyQualifiedConstantDefinition(index, mod, node, source);
400
+ if (qualified)
401
+ return qualified;
402
+ }
13
403
  const parent = node.parent;
14
404
  if (!parent || !sup.supportsCrossModuleSymbols) {
15
405
  return null;
@@ -25,46 +415,143 @@ export async function resolveMemberAccessDefinition(params) {
25
415
  return null;
26
416
  const { object: obj, property: prop } = getMemberAccessParts(sup, memberNode);
27
417
  const optionalMemberTypes = memberAccessTraversalTypes(sup);
418
+ // Only the outermost access is the callee; an inner hop (`A.B` in `A.B.C()`) is not called.
419
+ const callArgumentCountFor = (expr) => expr.id === memberNode.id
420
+ ? (getCallArgumentCount({ languageId: sup.id, source, call: memberNode.parent ?? memberNode }) ?? undefined)
421
+ : undefined;
28
422
  const resolveExpression = async (expr) => {
29
423
  const exprIsId = isReceiverNameNode(sup, expr.type) && !isMemberAccessNode(sup, expr);
30
424
  if (exprIsId) {
31
- const lexicalBinding = resolveLexicalBinding?.(expr);
32
- if (lexicalBinding)
33
- return { kind: "resolved", def: lexicalBinding };
425
+ const aliasQualifier = sup.id === "csharp" &&
426
+ expr.parent?.type === "alias_qualified_name" &&
427
+ getMemberAccessParts(sup, expr.parent).object?.id === expr.id;
428
+ if (!aliasQualifier) {
429
+ const lexicalBinding = resolveLexicalBinding?.(expr);
430
+ if (lexicalBinding)
431
+ return { kind: "resolved", def: lexicalBinding };
432
+ }
34
433
  const exprName = sliceText(expr, source);
35
- const imp = mod.imports.find((candidate) => (candidate.kind === "named" && candidate.local === exprName) ||
36
- (candidate.kind === "default" && candidate.local === exprName) ||
37
- (candidate.kind === "namespace" && candidate.localNS === exprName));
434
+ let imp;
435
+ if (sup.id === "php") {
436
+ imp = findPhpImportAlias(mod.imports, exprName, "class") ?? undefined;
437
+ }
438
+ else if (sup.id === "zig") {
439
+ imp = innermostNamespaceImport(mod.imports, exprName, expr);
440
+ if (!imp) {
441
+ imp = mod.imports.find((candidate) => (candidate.kind === "named" || candidate.kind === "default") && candidate.local === exprName);
442
+ }
443
+ }
444
+ else if (sup.id === "csharp") {
445
+ if (aliasQualifier) {
446
+ if (exprName !== "global")
447
+ imp = innermostNamespaceImport(mod.imports, exprName, expr, sup.normalizeIdentifier);
448
+ if (!imp)
449
+ return null;
450
+ }
451
+ else {
452
+ imp = innermostNamespaceImport(mod.imports, exprName, expr, sup.normalizeIdentifier);
453
+ if (!imp) {
454
+ imp = mod.imports.find((candidate) => (candidate.kind === "named" || candidate.kind === "default") &&
455
+ candidate.local === exprName &&
456
+ !isExpandedStarBinding(candidate, mod.imports));
457
+ }
458
+ }
459
+ }
460
+ else {
461
+ imp = effectiveExplicitBinding(mod.imports, sup.id, (candidate) => ((candidate.kind === "named" || candidate.kind === "default") && candidate.local === exprName) ||
462
+ (candidate.kind === "namespace" && candidate.localNS === exprName));
463
+ if (!imp && isJsTsLanguage(sup.id)) {
464
+ imp = innermostNamespaceImport(mod.imports, exprName, expr);
465
+ }
466
+ }
38
467
  if (imp) {
39
468
  if (imp.kind === "namespace") {
469
+ if (imp.mechanism === "cjs" && typeof imp.resolved === "string") {
470
+ const classValue = cjsRequireValueBinding(index, imp.resolved);
471
+ if (classValue)
472
+ return { kind: "resolved", def: classValue };
473
+ }
40
474
  return {
41
475
  kind: "namespace",
42
476
  file: typeof imp.resolved === "string" ? imp.resolved.replace(/\\/g, "/") : imp.resolved?.external || "",
43
477
  };
44
478
  }
479
+ if (sup.id === "php" && imp.kind === "named" && typeof imp.resolved === "string") {
480
+ const result = resolvePhpExportByImportType(index, imp.resolved, imp.imported, "class");
481
+ if (result)
482
+ return result;
483
+ }
45
484
  const result = resolveImported(index, imp, imp.kind === "named" ? imp.imported : "default");
46
485
  if (result) {
47
486
  if ("namespace" in result) {
48
487
  return { kind: "namespace", file: result.namespace };
49
488
  }
50
- return { kind: "resolved", def: result };
489
+ const container = asMemberContainer(index, result);
490
+ return { kind: "resolved", def: container ?? result };
51
491
  }
492
+ // Last-wins precedence (Python): the explicit import rebound the name, and an
493
+ // unresolved winner stays unresolved instead of falling back to an older star import.
494
+ if (starImportPrecedence(sup.id) === "last-wins")
495
+ return null;
52
496
  }
53
- const local = mod.locals.find((candidate) => candidate.localName === exprName);
54
- if (local)
55
- return { kind: "resolved", def: local };
56
- for (const starImport of mod.imports.filter((candidate) => candidate.kind === "star")) {
57
- const result = resolveImported(index, starImport, exprName);
58
- if (result) {
59
- if ("namespace" in result) {
60
- return { kind: "namespace", file: result.namespace };
61
- }
62
- return { kind: "resolved", def: result };
497
+ if (sup.id === "csharp") {
498
+ const local = resolveExport(index, mod.file, exprName, { referenceIndex: expr.startIndex });
499
+ if (local)
500
+ return local;
501
+ }
502
+ else {
503
+ const local = mod.locals.find((candidate) => {
504
+ if (candidate.localName === exprName)
505
+ return true;
506
+ return (sup.id === "php" &&
507
+ declaresMembers(candidate) &&
508
+ foldPhpIdentifierCase(candidate.localName) === foldPhpIdentifierCase(exprName));
509
+ });
510
+ if (local)
511
+ return { kind: "resolved", def: local };
512
+ if (sup.id === "java" || sup.id === "kotlin" || sup.id === "swift") {
513
+ const peer = resolveExport(index, mod.file, exprName);
514
+ if (peer)
515
+ return peer;
516
+ }
517
+ else if (sup.id === "php") {
518
+ const phpClass = resolveIndexedPhpClassReference(index, source, tree, expr, exprName, mod.imports);
519
+ if (phpClass)
520
+ return { kind: "resolved", def: phpClass };
63
521
  }
64
522
  }
523
+ const starDecision = resolveStarImportedName(index, mod, sup.id, exprName);
524
+ if (starDecision.status === "resolved")
525
+ return { kind: "resolved", def: starDecision.definition };
526
+ if (starDecision.status === "ambiguous")
527
+ return null;
528
+ const starNamespaceDecision = resolveStarImportedNamespace(index, mod, sup.id, exprName);
529
+ if (starNamespaceDecision.status === "resolved") {
530
+ return { kind: "namespace", file: starNamespaceDecision.namespace };
531
+ }
532
+ if (starNamespaceDecision.status === "ambiguous")
533
+ return null;
65
534
  return null;
66
535
  }
67
536
  if (optionalMemberTypes.has(expr.type)) {
537
+ // A Python unaliased dotted import (`import a.b`) binds only the first segment `a`,
538
+ // resolved to the leaf module `a.b` names; the source can only ever repeat that whole
539
+ // dotted phrase to reach it again (`a.b.symbol(...)`), never a bare intermediate segment
540
+ // on its own. Recognize that literal phrase up front so the chain walk below lands on the
541
+ // leaf module the import machinery already resolved, instead of treating each dot as an
542
+ // ordinary member-access hop and failing on the segment duplicating the import's spelling.
543
+ if (sup.id === "python") {
544
+ const dottedText = sliceText(expr, source);
545
+ if (dottedText.includes(".")) {
546
+ const dottedImport = mod.imports.find((imp) => imp.kind === "namespace" &&
547
+ imp.mechanism === "python" &&
548
+ imp.from === dottedText &&
549
+ typeof imp.resolved === "string");
550
+ if (dottedImport && typeof dottedImport.resolved === "string") {
551
+ return { kind: "namespace", file: dottedImport.resolved.replace(/\\/g, "/") };
552
+ }
553
+ }
554
+ }
68
555
  const parts = getMemberAccessParts(sup, expr);
69
556
  const subObj = parts.object;
70
557
  let subProp = parts.property;
@@ -74,15 +561,24 @@ export async function resolveMemberAccessDefinition(params) {
74
561
  if (subObj && subProp) {
75
562
  const base = await resolveExpression(subObj);
76
563
  const memberName = sliceText(subProp, source);
564
+ // `P.Mix` in `P.Mix.M()`: when the leftmost name binds nothing, a C# dotted name is a
565
+ // namespace-qualified type, which qualified export lookup resolves across every file that
566
+ // declares the namespace.
567
+ const dottedRoot = !base && sup.id === "csharp" ? csharpDottedNameRoot(expr) : null;
568
+ if (dottedRoot && !resolveLexicalBinding?.(dottedRoot)) {
569
+ return resolveCsharpQualifiedName(index, mod, sliceText(expr, source).replace(/\s+/gu, ""), expr.startIndex);
570
+ }
77
571
  if (base?.kind === "namespace") {
572
+ if (!isGoExportedMemberName(sup.id, memberName))
573
+ return null;
78
574
  return resolveExport(index, base.file, memberName, { allowLocalFallback: false });
79
575
  }
80
576
  if (base?.kind === "resolved") {
81
- if (sup.id === "java") {
82
- const memberDef = await resolveMemberDefinitionForBase(index, base.def, memberName);
577
+ if (sup.id === "java" || sup.id === "csharp") {
578
+ const memberDef = await resolveMemberDefinitionForBase(index, base.def, memberName, callArgumentCountFor(expr));
83
579
  return memberDef ? { kind: "resolved", def: memberDef } : null;
84
580
  }
85
- if (sup.id === "ruby") {
581
+ if (sup.id === "ruby" && declaresMembers(base.def)) {
86
582
  const memberDef = await resolveMemberDefinitionForBase(index, base.def, memberName);
87
583
  if (memberDef)
88
584
  return { kind: "resolved", def: memberDef };
@@ -104,7 +600,7 @@ export async function resolveMemberAccessDefinition(params) {
104
600
  return resolveExport(index, base.file, memberName, { allowLocalFallback: false });
105
601
  }
106
602
  if (base?.kind === "resolved") {
107
- const memberDef = await resolveMemberDefinitionForBase(index, base.def, memberName);
603
+ const memberDef = await resolveMemberDefinitionForBase(index, base.def, memberName, callArgumentCountFor(expr));
108
604
  return memberDef ? { kind: "resolved", def: memberDef } : null;
109
605
  }
110
606
  }
@@ -133,11 +629,11 @@ export async function resolveMemberAccessDefinition(params) {
133
629
  }
134
630
  }
135
631
  const receiverName = obj ? sliceText(obj, source) : "";
136
- const implicitClassReceiver = isKeywordReceiver(sup.id, receiverName);
137
- if (obj && prop && node.id === prop.id && (supportsReceiverMemberNavigation(sup.id) || implicitClassReceiver)) {
632
+ const receiverKind = keywordReceiverKind(sup.id, receiverName);
633
+ if (obj && prop && node.id === prop.id && supportsReceiverMemberNavigation(sup.id)) {
138
634
  const member = sliceText(prop, source);
139
635
  if (sup.id === "python") {
140
- const memberDef = await resolvePythonReceiverMember(index, mod, node, obj, member, source, sup, resolveExpression);
636
+ const memberDef = await resolvePythonReceiverMember(index, mod, node, obj, member, source, tree, sup, resolveExpression);
141
637
  if (!memberDef)
142
638
  return null;
143
639
  return okGoToResult(index, memberDef, {
@@ -146,11 +642,12 @@ export async function resolveMemberAccessDefinition(params) {
146
642
  confidence: "medium",
147
643
  });
148
644
  }
149
- if (!sup.membersAreImplicitlyInScope && implicitClassReceiver) {
150
- const classContainer = findEnclosingClassContainer(node);
151
- const memberDef = classContainer
152
- ? findLocalWithinNode(mod.locals, member, classContainer, sup.normalizeIdentifier)
153
- : undefined;
645
+ if (receiverKind && keywordReceiverCrossesDynamicBoundary(sup, node)) {
646
+ return null;
647
+ }
648
+ const keywordScope = receiverKind ? keywordReceiverMemberScope(sup, receiverName, node, source) : "any";
649
+ if (receiverKind === "own") {
650
+ const memberDef = await resolveKeywordReceiverMember(index, mod, node, member, keywordScope, false, getCallArgumentCount({ languageId: sup.id, source, call: memberNode.parent ?? memberNode }) ?? undefined);
154
651
  if (memberDef) {
155
652
  return okGoToResult(index, memberDef, {
156
653
  via: { exportedName: member },
@@ -158,8 +655,74 @@ export async function resolveMemberAccessDefinition(params) {
158
655
  confidence: "medium",
159
656
  });
160
657
  }
658
+ const outOfLineOwnerPath = cppOutOfLineOwnerPath(node, source, sup);
659
+ const outOfLineOwner = outOfLineOwnerPath
660
+ ? await resolveCppQualifiedMemberContainer(index, mod, outOfLineOwnerPath)
661
+ : null;
662
+ if (outOfLineOwner) {
663
+ const outOfLineMember = await resolveKeywordReceiverMember(index, mod, node, member, keywordScope, false, getCallArgumentCount({ languageId: sup.id, source, call: memberNode.parent ?? memberNode }) ?? undefined, outOfLineOwner);
664
+ if (outOfLineMember) {
665
+ return okGoToResult(index, outOfLineMember, {
666
+ resolution: "exact",
667
+ confidence: "high",
668
+ });
669
+ }
670
+ }
671
+ // A failed `self` lookup is not a license to bind an unrelated same-file
672
+ // extension member by its bare name (including an unproven where clause).
673
+ if (sup.id === "swift")
674
+ return { status: "not_found", reason: "No matching Swift member definition" };
161
675
  }
162
- const receiver = await resolveReceiverDefinition(obj, source, sup, resolveExpression, mod);
676
+ else if (receiverKind === "supertype") {
677
+ const memberDef = await resolveKeywordReceiverMember(index, mod, node, member, keywordScope, true, getCallArgumentCount({ languageId: sup.id, source, call: memberNode.parent ?? memberNode }) ?? undefined);
678
+ if (!memberDef)
679
+ return { status: "not_found", reason: "No matching supertype member definition" };
680
+ return okGoToResult(index, memberDef, {
681
+ via: { exportedName: member },
682
+ resolution: "member-access",
683
+ confidence: "medium",
684
+ });
685
+ }
686
+ if (sup.id === "cpp" && memberNode.type === "qualified_identifier") {
687
+ // `a::C::run` nests `C::run` inside `a::(...)`; only the outermost node carries the full owner.
688
+ let outermost = memberNode;
689
+ while (outermost.parent?.type === "qualified_identifier")
690
+ outermost = outermost.parent;
691
+ // The declarator of an out-of-line definition (`int Box<T>::f() {}`) names the declaration
692
+ // itself, not a member call; the declaration lookup below resolves it.
693
+ if (outermost.parent?.type === "function_declarator")
694
+ return null;
695
+ const qualified = cppQualifiedNameSegments(outermost, source);
696
+ const ownerPath = qualified.slice(0, -1);
697
+ const owner = await resolveCppQualifiedMemberContainer(index, mod, ownerPath);
698
+ if (owner) {
699
+ const knownArgumentCount = getCallArgumentCount({ languageId: sup.id, source, call: memberNode.parent ?? memberNode }) ?? undefined;
700
+ // Inside a member function, `Owner::member()` can name an instance member of the class or a
701
+ // base through the implicit `this`; elsewhere only static members qualify.
702
+ const implicitThis = await cppQualifiedOwnerHasImplicitThis(index, mod, memberNode, source, sup, owner);
703
+ const memberScope = implicitThis ? "any" : "static";
704
+ const memberDef = await resolveKeywordReceiverMember(index, mod, node, member, memberScope, false, knownArgumentCount, owner);
705
+ return memberDef
706
+ ? okGoToResult(index, memberDef, { resolution: "member-access", confidence: "high" })
707
+ : { status: "not_found", reason: "No matching C++ static member definition" };
708
+ }
709
+ // A qualifier that is not a class (a namespace) names a free function, which the qualified
710
+ // function lookup resolves; the short name must not be read as a same-named class receiver.
711
+ return null;
712
+ }
713
+ const importType = isJsTsLanguage(sup.id) ? receiverConstructorExpression(obj, source, sup) : null;
714
+ const importSpecifier = importType?.type === "type_query" ? importTypeQuerySpecifier(importType) : null;
715
+ if (importSpecifier) {
716
+ const memberDef = resolveImportTypeMember(index, mod.file, importSpecifier, member);
717
+ return memberDef
718
+ ? okGoToResult(index, memberDef, {
719
+ via: { exportedName: member },
720
+ resolution: "member-access",
721
+ confidence: "medium",
722
+ })
723
+ : null;
724
+ }
725
+ const receiver = await resolveReceiverDefinition(index, obj, source, sup, resolveExpression, mod);
163
726
  if (receiver) {
164
727
  const objDef = receiver.def;
165
728
  const targetContext = await ensureParsedContext(objDef.file, undefined, index.languageExtensions);
@@ -184,37 +747,1030 @@ export async function resolveMemberAccessDefinition(params) {
184
747
  const memberPredicate = receiver.memberScope === "any"
185
748
  ? undefined
186
749
  : (local) => matchesReceiverMemberScope(local, receiver.memberScope, targetContext, container);
750
+ const knownArgumentCount = getCallArgumentCount({ languageId: sup.id, source, call: memberNode.parent ?? memberNode }) ?? undefined;
187
751
  let memberDef;
188
- if (receiver.runtimeTypeOnly || targetContext.sup.id === "java") {
189
- memberDef = findDirectLocalWithinNode(targetModule.locals, member, container, targetContext, normalizeIdentifier, memberPredicate);
752
+ if ((targetContext.sup.id === "ts" || targetContext.sup.id === "tsx") &&
753
+ (container.type === "internal_module" || container.type === "module")) {
754
+ memberDef = await findReceiverMemberDefinition(index, targetModule.locals, member, objDef, container, targetContext, normalizeIdentifier, receiver.memberScope, knownArgumentCount);
755
+ }
756
+ else if (receiver.runtimeTypeOnly || targetContext.sup.id === "java") {
757
+ const candidates = findDirectLocalsWithinNode(targetModule.locals, member, container, targetContext, normalizeIdentifier, memberPredicate);
758
+ memberDef = await selectReceiverMemberCandidates(index, candidates, knownArgumentCount);
190
759
  }
191
760
  else {
192
- memberDef = findReceiverMemberDefinition(targetModule.locals, member, objDef, container, targetContext, normalizeIdentifier, receiver.memberScope);
761
+ memberDef = await findReceiverMemberDefinition(index, targetModule.locals, member, objDef, container, targetContext, normalizeIdentifier, receiver.memberScope, knownArgumentCount);
762
+ }
763
+ if (memberDef &&
764
+ (sup.id === "cpp" ||
765
+ isGoExportedMemberName(sup.id, member) ||
766
+ getCompilationUnitPeers(index, mod.file).files.has(memberDef.file)) &&
767
+ !crossFilePeerMemberHidden(mod.file, memberDef, targetContext)) {
768
+ return okGoToResult(index, memberDef, {
769
+ via: { exportedName: member },
770
+ resolution: "member-access",
771
+ confidence: "medium",
772
+ });
773
+ }
774
+ }
775
+ }
776
+ }
777
+ }
778
+ return null;
779
+ }
780
+ function findEnclosingClassContainer(node) {
781
+ return nearestMemberContainer(node);
782
+ }
783
+ function keywordClassKey(def) {
784
+ const start = def.range.start;
785
+ return `${fileIdentityKey(def.file)}:${start.index ?? `${start.line}:${start.column}`}`;
786
+ }
787
+ function keywordContainerKey(file, container) {
788
+ return `${fileIdentityKey(file)}:${container.startIndex}:${container.endIndex}`;
789
+ }
790
+ function peelTypeWrappers(node) {
791
+ let current = node;
792
+ while (current) {
793
+ if (current.type === "type_annotation" ||
794
+ current.type === "named_type" ||
795
+ current.type === "user_type" ||
796
+ current.type === "type" ||
797
+ current.type === "parenthesized_type" ||
798
+ current.type === "pointer_type" ||
799
+ current.type === "reference_type" ||
800
+ current.type === "optional_type" ||
801
+ current.type === "nullable_type" ||
802
+ current.type === "generic_type" ||
803
+ current.type === "generic_name") {
804
+ current = current.childForFieldName("type") ?? current.namedChildren[0] ?? null;
805
+ continue;
806
+ }
807
+ break;
808
+ }
809
+ return current ?? node;
810
+ }
811
+ function isSimpleTypeNameNode(node, sup) {
812
+ return isReceiverNameNode(sup, node.type) || node.type === "type_identifier" || node.type === "name";
813
+ }
814
+ function parseCppBaseType(node, source) {
815
+ let current = node;
816
+ while (current.type === "base_class_clause" ||
817
+ current.type === "access_specifier" ||
818
+ current.type === "virtual_specifier" ||
819
+ current.type === "type_descriptor") {
820
+ const nested = current.namedChildren.at(-1);
821
+ if (!nested || nested.id === current.id)
822
+ break;
823
+ current = nested;
824
+ }
825
+ const names = cppQualifiedNameSegments(current, source);
826
+ return names.length ? { base: names[0], path: names.slice(1) } : null;
827
+ }
828
+ function collectDeclaredBaseTypes(container, source, sup, superclassOnly = false) {
829
+ const ancestry = MEMBER_ACCESS_ROWS[sup.id]?.receiverAncestry;
830
+ if (!ancestry || (ancestry.clauses.length === 0 && !ancestry.mixinCalls?.length))
831
+ return [];
832
+ const bases = [];
833
+ const seen = new Set();
834
+ let usedFirstMatch = false;
835
+ const addSimple = (name, invoked) => {
836
+ if (!name || seen.has(name))
837
+ return;
838
+ seen.add(name);
839
+ bases.push(invoked ? { kind: "simple", name, invoked } : { kind: "simple", name });
840
+ };
841
+ const addQualified = (base, path, invoked) => {
842
+ if (!base || path.length === 0)
843
+ return;
844
+ const key = `${base}.${path.join(".")}`;
845
+ if (seen.has(key))
846
+ return;
847
+ seen.add(key);
848
+ bases.push(invoked ? { kind: "qualified", base, path, invoked } : { kind: "qualified", base, path });
849
+ };
850
+ const collectType = (node, invoked) => {
851
+ const core = peelTypeWrappers(node);
852
+ if (core.type === "constructor_invocation") {
853
+ // Kotlin writes the superclass as a constructor invocation (`Base()`); its callees are
854
+ // superclass entries, while bare types in the same base list are interfaces.
855
+ for (const child of core.namedChildren) {
856
+ if (child.type === "value_arguments")
857
+ continue;
858
+ collectType(child, true);
859
+ }
860
+ return;
861
+ }
862
+ // A computed heritage expression can name the runtime base factory rather than a class.
863
+ // Its descendants do not prove an inheritance edge.
864
+ if (isUnprovenHeritageExpression(core))
865
+ return;
866
+ if (sup.id === "cpp" && core.type === "base_class_clause") {
867
+ for (const child of core.namedChildren) {
868
+ if (child.type === "access_specifier" || child.type === "virtual_specifier")
869
+ continue;
870
+ collectType(child, invoked);
871
+ }
872
+ return;
873
+ }
874
+ if (sup.id === "cpp") {
875
+ const qualified = parseCppBaseType(core, source);
876
+ if (qualified) {
877
+ if (qualified.path.length)
878
+ addQualified(qualified.base, qualified.path, invoked);
879
+ else
880
+ addSimple(qualified.base, invoked);
881
+ return;
882
+ }
883
+ }
884
+ const unwrapped = unwrapNamedType(core, sup);
885
+ if (unwrapped) {
886
+ addSimple(sliceText(unwrapped, source), invoked);
887
+ return;
888
+ }
889
+ if (isMemberAccessNode(sup, core)) {
890
+ const chain = collectMemberAccessChain({ sup, source, chainNode: core });
891
+ if (chain && isSimpleTypeNameNode(chain.base, sup) && chain.names.length) {
892
+ addQualified(sliceText(chain.base, source), [...chain.names].reverse(), invoked);
893
+ }
894
+ return;
895
+ }
896
+ if (memberAccessTraversalTypes(sup).has(core.type))
897
+ return;
898
+ for (const child of core.namedChildren) {
899
+ if (child.type === "type_arguments")
900
+ continue;
901
+ collectType(child, invoked);
902
+ }
903
+ };
904
+ const consider = (node) => {
905
+ for (const rule of ancestry.clauses) {
906
+ if (node.type !== rule.nodeType)
907
+ continue;
908
+ if (superclassOnly && !rule.supertype)
909
+ continue;
910
+ if (superclassOnly && rule.supertype === "first-match") {
911
+ if (usedFirstMatch)
912
+ continue;
913
+ usedFirstMatch = true;
914
+ }
915
+ let target = node;
916
+ if (superclassOnly && rule.supertype === "first-child") {
917
+ target = target.namedChildren[0] ?? target;
918
+ }
919
+ collectType(target, false);
920
+ }
921
+ if (superclassOnly || node.type !== "call" || !ancestry.mixinCalls?.length)
922
+ return;
923
+ if (node.childForFieldName("receiver"))
924
+ return;
925
+ const methodNode = node.childForFieldName("method");
926
+ const methodName = methodNode ? sliceText(methodNode, source) : undefined;
927
+ if (!methodName || !ancestry.mixinCalls.includes(methodName))
928
+ return;
929
+ const args = node.childForFieldName("arguments");
930
+ if (args)
931
+ collectType(args, false);
932
+ };
933
+ for (const child of container.namedChildren) {
934
+ consider(child);
935
+ for (const grand of child.namedChildren)
936
+ consider(grand);
937
+ }
938
+ return bases;
939
+ }
940
+ function asMemberContainer(index, def) {
941
+ return memberContainerForDefinition(index, def);
942
+ }
943
+ function importedMemberContainer(index, result) {
944
+ if (!result || "namespace" in result)
945
+ return undefined;
946
+ return asMemberContainer(index, result);
947
+ }
948
+ function resolveNamedMemberContainer(index, mod, name, normalize) {
949
+ const typedLocals = memberDeclaringLocals(mod, name, normalize);
950
+ const topLevel = typedLocals.filter((local) => !local.isMember);
951
+ const candidates = topLevel.length ? topLevel : typedLocals;
952
+ if (candidates.length === 1)
953
+ return candidates[0];
954
+ if (candidates.length > 1)
955
+ return undefined;
956
+ const normalizedName = normalize(name);
957
+ const languageId = supportForFileWithoutHeaderSample(mod.file, index.languageExtensions)?.id;
958
+ const explicitBinding = effectiveExplicitBinding(mod.imports, languageId ?? "", (binding) => (binding.kind === "named" || binding.kind === "default") && normalize(binding.local) === normalizedName);
959
+ if (explicitBinding?.kind === "named") {
960
+ const container = importedMemberContainer(index, resolveImported(index, explicitBinding, explicitBinding.imported));
961
+ if (container)
962
+ return container;
963
+ }
964
+ else if (explicitBinding?.kind === "default") {
965
+ const container = importedMemberContainer(index, resolveImported(index, explicitBinding, "default"));
966
+ if (container)
967
+ return container;
968
+ }
969
+ if (languageId) {
970
+ const starDecision = resolveStarImportedName(index, mod, languageId, name);
971
+ if (starDecision.status === "ambiguous")
972
+ return undefined;
973
+ if (starDecision.status === "resolved") {
974
+ const container = asMemberContainer(index, starDecision.definition);
975
+ if (container)
976
+ return container;
977
+ }
978
+ }
979
+ const exported = resolveExport(index, mod.file, name, { allowLocalFallback: false });
980
+ if (exported?.kind === "resolved")
981
+ return asMemberContainer(index, exported.def);
982
+ return undefined;
983
+ }
984
+ async function resolveQualifiedMemberContainer(index, mod, baseName, path, normalize, sup) {
985
+ if (path.length === 0)
986
+ return undefined;
987
+ if (sup.id === "cpp") {
988
+ return (await resolveCppQualifiedMemberContainer(index, mod, [baseName, ...path])) ?? undefined;
989
+ }
990
+ const namespaceImports = mod.imports.filter((imp) => imp.kind === "namespace" && normalize(imp.localNS) === normalize(baseName));
991
+ if (namespaceImports.length !== 1)
992
+ return undefined;
993
+ const resolved = namespaceImports[0].resolved;
994
+ let file = typeof resolved === "string" ? resolved : undefined;
995
+ if (!file)
996
+ return undefined;
997
+ for (let partIndex = 0; partIndex < path.length; partIndex += 1) {
998
+ const part = path[partIndex];
999
+ const last = partIndex === path.length - 1;
1000
+ const hit = resolveExport(index, file, part, { allowLocalFallback: false });
1001
+ if (!hit)
1002
+ return undefined;
1003
+ if (hit.kind === "namespace") {
1004
+ if (last)
1005
+ return undefined;
1006
+ file = hit.file;
1007
+ continue;
1008
+ }
1009
+ if (!last)
1010
+ return undefined;
1011
+ return asMemberContainer(index, hit.def);
1012
+ }
1013
+ return undefined;
1014
+ }
1015
+ function nodeContainsUnprovenHeritage(node) {
1016
+ if (isUnprovenHeritageExpression(node))
1017
+ return true;
1018
+ return node.namedChildren.some((child) => nodeContainsUnprovenHeritage(child));
1019
+ }
1020
+ /** A heritage or mixin clause whose type expression is not a resolvable name. */
1021
+ function containerHeritageIsUnproven(container, source, sup) {
1022
+ const ancestry = MEMBER_ACCESS_ROWS[sup.id]?.receiverAncestry;
1023
+ if (!ancestry)
1024
+ return false;
1025
+ const clauseTypes = new Set(ancestry.clauses.map((rule) => rule.nodeType));
1026
+ const nodes = [];
1027
+ for (const child of container.namedChildren) {
1028
+ nodes.push(child);
1029
+ for (const grand of child.namedChildren)
1030
+ nodes.push(grand);
1031
+ }
1032
+ if (nodes.some((node) => clauseTypes.has(node.type) && nodeContainsUnprovenHeritage(node)))
1033
+ return true;
1034
+ const mixinCalls = ancestry.mixinCalls;
1035
+ if (!mixinCalls?.length)
1036
+ return false;
1037
+ return nodes.some((node) => {
1038
+ if (node.type !== "call" || node.childForFieldName("receiver"))
1039
+ return false;
1040
+ const methodNode = node.childForFieldName("method");
1041
+ const methodName = methodNode ? sliceText(methodNode, source) : undefined;
1042
+ if (!methodName || !mixinCalls.includes(methodName))
1043
+ return false;
1044
+ const args = node.childForFieldName("arguments");
1045
+ return !!args && nodeContainsUnprovenHeritage(args);
1046
+ });
1047
+ }
1048
+ function definitionForContainer(mod, container) {
1049
+ const nameNode = container.childForFieldName("name");
1050
+ if (!nameNode)
1051
+ return undefined;
1052
+ const matches = mod.locals.filter((local) => declaresMembers(local) && local.range.start.index === nameNode.startIndex);
1053
+ return matches.length === 1 ? matches[0] : undefined;
1054
+ }
1055
+ async function resolveReceiverTypeName(index, mod, sup, typeName) {
1056
+ const trimmed = typeName.trim().replace(/^\\+/, "");
1057
+ if (!trimmed)
1058
+ return undefined;
1059
+ const normalize = sup.normalizeIdentifier;
1060
+ for (const separator of ["::", "\\", "."]) {
1061
+ if (!trimmed.includes(separator))
1062
+ continue;
1063
+ const parts = trimmed.split(separator).filter(Boolean);
1064
+ if (parts.length < 2)
1065
+ return undefined;
1066
+ return resolveQualifiedMemberContainer(index, mod, parts[0], parts.slice(1), normalize, sup);
1067
+ }
1068
+ return resolveNamedMemberContainer(index, mod, trimmed, normalize);
1069
+ }
1070
+ async function resolveDeclaredBase(index, mod, sup, base) {
1071
+ const normalize = sup.normalizeIdentifier;
1072
+ if (base.kind === "simple")
1073
+ return resolveNamedMemberContainer(index, mod, base.name, normalize);
1074
+ return resolveQualifiedMemberContainer(index, mod, base.base, base.path, normalize, sup);
1075
+ }
1076
+ /**
1077
+ * True when `def` and every resolved supertype lack a direct `memberName`. An unresolved
1078
+ * supertype, an unreadable declaration, or a declared member returns false.
1079
+ */
1080
+ async function hierarchyOmitsMember(index, def, memberName, memberScope, seen) {
1081
+ const containerDef = asMemberContainer(index, def);
1082
+ if (!containerDef)
1083
+ return false;
1084
+ const start = containerDef.range.start;
1085
+ const key = `${fileIdentityKey(containerDef.file)}:${start.index ?? `${start.line}:${start.column}`}`;
1086
+ if (seen.has(key))
1087
+ return true;
1088
+ seen.add(key);
1089
+ const ref = await keywordClassRefFromDef(index, containerDef);
1090
+ if (!ref)
1091
+ return false;
1092
+ const normalize = ref.context.sup.normalizeIdentifier;
1093
+ const memberPredicate = memberScope === "any"
1094
+ ? undefined
1095
+ : (local) => matchesReceiverMemberScope(local, memberScope, ref.context, ref.container);
1096
+ if (findDirectLocalsWithinNode(ref.module.locals, memberName, ref.container, ref.context, normalize, memberPredicate)
1097
+ .length > 0) {
1098
+ return false;
1099
+ }
1100
+ if (ref.context.sup.id === "csharp" || ref.context.sup.id === "swift") {
1101
+ const shared = await findSharedOwnerMemberDefinitions({
1102
+ index,
1103
+ ownerFile: containerDef.file,
1104
+ ownerContainer: ref.container,
1105
+ ownerSource: ref.context.source,
1106
+ languageId: ref.context.sup.id,
1107
+ member: memberName,
1108
+ memberScope,
1109
+ });
1110
+ if (shared.length > 0)
1111
+ return false;
1112
+ }
1113
+ if (containerHeritageIsUnproven(ref.container, ref.context.source, ref.context.sup))
1114
+ return false;
1115
+ const bases = collectDeclaredBaseTypes(ref.container, ref.context.source, ref.context.sup, false);
1116
+ for (const base of bases) {
1117
+ const baseDef = await resolveDeclaredBase(index, ref.module, ref.context.sup, base);
1118
+ if (!baseDef)
1119
+ return false;
1120
+ if (!(await hierarchyOmitsMember(index, baseDef, memberName, memberScope, seen)))
1121
+ return false;
1122
+ }
1123
+ return true;
1124
+ }
1125
+ /**
1126
+ * A classified receiver (`new Widget()`, a named type, `this`, or `super`) excludes a failed
1127
+ * member lookup only when its type resolves to a member-declaring definition, no supertype is
1128
+ * unresolved, and that type does not declare `memberName`. Otherwise the site stays unproven.
1129
+ */
1130
+ export async function provenClassifiedReceiverOmitsMember(index, fileId, parsed, accessNode, objectNode, memberName) {
1131
+ const mod = index.byFile.get(fileIdentityKey(fileId));
1132
+ if (!mod)
1133
+ return false;
1134
+ const receiver = classifyReceiver(parsed.sup, objectNode, parsed.source, new Map(), accessNode.startIndex, accessNode, (callee) => {
1135
+ const scope = getOrBuildScopeIndex(index, fileId, parsed.source, parsed.sup, mod, parsed.tree);
1136
+ return !!findClosestScopeBinding(scope, sliceText(callee, parsed.source), callee, parsed.sup);
1137
+ });
1138
+ if (!receiver)
1139
+ return false;
1140
+ if (receiver.kind === "named-type") {
1141
+ const def = await resolveReceiverTypeName(index, mod, parsed.sup, receiver.typeName);
1142
+ if (!def)
1143
+ return false;
1144
+ return hierarchyOmitsMember(index, def, memberName, receiver.memberScope, new Set());
1145
+ }
1146
+ const container = nearestMemberContainer(accessNode);
1147
+ if (!container)
1148
+ return false;
1149
+ if (receiver.kind === "supertype") {
1150
+ if (containerHeritageIsUnproven(container, parsed.source, parsed.sup))
1151
+ return false;
1152
+ const bases = collectDeclaredBaseTypes(container, parsed.source, parsed.sup, true);
1153
+ if (!bases.length)
1154
+ return false;
1155
+ const seen = new Set();
1156
+ for (const base of bases) {
1157
+ const baseDef = await resolveDeclaredBase(index, mod, parsed.sup, base);
1158
+ if (!baseDef)
1159
+ return false;
1160
+ if (!(await hierarchyOmitsMember(index, baseDef, memberName, receiver.memberScope, seen)))
1161
+ return false;
1162
+ }
1163
+ return true;
1164
+ }
1165
+ const def = definitionForContainer(mod, container);
1166
+ if (!def)
1167
+ return false;
1168
+ return hierarchyOmitsMember(index, def, memberName, receiver.memberScope, new Set());
1169
+ }
1170
+ async function keywordClassRefFromDef(index, def) {
1171
+ const module = index.byFile.get(fileIdentityKey(def.file));
1172
+ if (!module)
1173
+ return null;
1174
+ const context = await ensureParsedContext(def.file, undefined, index.languageExtensions);
1175
+ const start = def.range.start;
1176
+ const position = {
1177
+ row: start.line - 1,
1178
+ column: start.column - 1,
1179
+ };
1180
+ const nameNode = context.tree.rootNode.descendantForPosition(position, position);
1181
+ const container = nearestMemberContainer(nameNode);
1182
+ if (!container)
1183
+ return null;
1184
+ return { file: def.file, container, context, module };
1185
+ }
1186
+ async function keywordClassRefFromNode(index, mod, node) {
1187
+ const context = await ensureParsedContext(mod.file, undefined, index.languageExtensions);
1188
+ const memberNode = context.tree.rootNode.descendantForPosition(node.startPosition, node.startPosition);
1189
+ const container = findEnclosingClassContainer(memberNode);
1190
+ if (!container)
1191
+ return null;
1192
+ return { file: mod.file, container, context, module: mod };
1193
+ }
1194
+ function crossFilePeerMemberHidden(useFile, memberDef, targetContext) {
1195
+ const languageId = targetContext.sup.id;
1196
+ if (languageId !== "java" && languageId !== "kotlin" && languageId !== "swift")
1197
+ return false;
1198
+ const start = memberDef.range.start;
1199
+ const position = { row: Math.max(0, start.line - 1), column: Math.max(0, start.column - 1) };
1200
+ const nameNode = targetContext.tree.rootNode.descendantForPosition(position, position);
1201
+ if (!nameNode)
1202
+ return false;
1203
+ if (languageId === "swift") {
1204
+ return isSwiftCrossFileHiddenSharedOwnerMember(languageId, useFile, memberDef.file, nameNode);
1205
+ }
1206
+ if (fileIdentityKey(useFile) === fileIdentityKey(memberDef.file))
1207
+ return false;
1208
+ return !isExportedDeclaration(languageId, nameNode);
1209
+ }
1210
+ async function baseRefsFromContainer(index, mod, container, source, sup, superclassOnly, tree) {
1211
+ const bases = collectDeclaredBaseTypes(container, source, sup, superclassOnly);
1212
+ const refs = [];
1213
+ const seen = new Set();
1214
+ const normalize = (name) => {
1215
+ const normalized = sup.normalizeIdentifier(name);
1216
+ return sup.id === "php" ? foldPhpIdentifierCase(normalized) : normalized;
1217
+ };
1218
+ for (const base of bases) {
1219
+ let def = base.kind === "simple"
1220
+ ? resolveNamedMemberContainer(index, mod, base.name, normalize)
1221
+ : await resolveQualifiedMemberContainer(index, mod, base.base, base.path, normalize, sup);
1222
+ if (!def && sup.id === "php" && base.kind === "simple") {
1223
+ def = resolvePhpNamespaceSymbol(index, source, tree, container, base.name, mod.imports, "class") ?? undefined;
1224
+ }
1225
+ if (!def)
1226
+ continue;
1227
+ const ref = await keywordClassRefFromDef(index, def);
1228
+ if (!ref)
1229
+ continue;
1230
+ // `super`, `base`, and `parent` follow class ancestors only. An own receiver also inherits
1231
+ // interface or protocol members, so apply the class-only filter only to supertype lookup.
1232
+ const interfaceLike = ref.container.type === "interface_declaration" ||
1233
+ ref.container.type === "protocol_declaration" ||
1234
+ ref.container.type === "trait_item" ||
1235
+ /^(?:interface|protocol|trait)\b/.test(sliceText(ref.container, ref.context.source).trimStart());
1236
+ if (superclassOnly && (def.kind !== SymbolKind.Class || interfaceLike))
1237
+ continue;
1238
+ // Kotlin classifies interfaces as SymbolKind.Class too, so the superclass is identified
1239
+ // syntactically: `Base()` is a constructor invocation, while bare delegation-specifier
1240
+ // entries (`Face`, `by` delegations) are interfaces. Interface-only super lookup stays
1241
+ // unresolved, while an own receiver can inherit their default members.
1242
+ if (superclassOnly && sup.id === "kotlin" && !base.invoked)
1243
+ continue;
1244
+ const key = keywordClassKey(def);
1245
+ if (seen.has(key))
1246
+ continue;
1247
+ seen.add(key);
1248
+ refs.push(ref);
1249
+ }
1250
+ return refs;
1251
+ }
1252
+ const HIERARCHY_OVERLOAD_LANGUAGES = new Set(["java", "kotlin", "csharp"]);
1253
+ async function resolveKeywordReceiverMember(index, mod, node, member, memberScope, startAtAncestor, knownArgumentCount, explicitClassDef, report) {
1254
+ const current = explicitClassDef
1255
+ ? await keywordClassRefFromDef(index, explicitClassDef)
1256
+ : await keywordClassRefFromNode(index, mod, node);
1257
+ if (!current)
1258
+ return undefined;
1259
+ let level = startAtAncestor
1260
+ ? await baseRefsFromContainer(index, current.module, current.container, current.context.source, current.context.sup, true, current.context.tree)
1261
+ : [current];
1262
+ if (level.length === 0)
1263
+ return undefined;
1264
+ // Java, Kotlin, and C# overload across the hierarchy: when no member at one level accepts the
1265
+ // call, a base level may. C++ and Swift hide a base name behind any same-named member.
1266
+ // An unknown count (a Kotlin spread) keeps every non-overridden overload as a candidate.
1267
+ const spansHierarchy = HIERARCHY_OVERLOAD_LANGUAGES.has(current.context.sup.id);
1268
+ // Java, Kotlin, and C# base members that are private are not accessible from a subclass.
1269
+ const filtersPrivateBases = HIERARCHY_OVERLOAD_LANGUAGES.has(current.context.sup.id);
1270
+ let lenient;
1271
+ // Accepted overloads across levels. A deeper one is overridden only by an accepted method with
1272
+ // the same parameter types in a class proven to derive from its owner on the walked path;
1273
+ // unrelated owners with one signature (two interfaces) stay separate and ambiguous.
1274
+ const accepted = [];
1275
+ const memberKey = (def) => `${fileIdentityKey(def.file)}:${def.range.start.index}`;
1276
+ const ownerOf = new Map();
1277
+ const subclassesOf = new Map();
1278
+ const recordOwner = (from, owner) => {
1279
+ for (let at = from; at < matches.length; at += 1)
1280
+ ownerOf.set(memberKey(matches[at]), owner);
1281
+ };
1282
+ let matches = [];
1283
+ const visited = new Set([
1284
+ keywordContainerKey(current.file, current.container),
1285
+ ...level.map((candidate) => keywordContainerKey(candidate.file, candidate.container)),
1286
+ ]);
1287
+ for (let depth = 0; depth < RECEIVER_HIERARCHY_DEPTH && level.length; depth += 1) {
1288
+ matches = [];
1289
+ for (const candidate of level) {
1290
+ const ownerKey = keywordContainerKey(candidate.file, candidate.container);
1291
+ const before = matches.length;
1292
+ const memberPredicate = memberScope === "any"
1293
+ ? undefined
1294
+ : (local) => matchesReceiverMemberScope(local, memberScope, candidate.context, candidate.container);
1295
+ appendDirectKeywordMembers(candidate.module.locals, member, candidate.container, candidate.context, candidate.context.sup.normalizeIdentifier, memberPredicate, matches);
1296
+ recordOwner(before, ownerKey);
1297
+ }
1298
+ for (const candidate of level) {
1299
+ if (candidate.context.sup.id !== "csharp" && candidate.context.sup.id !== "swift")
1300
+ continue;
1301
+ const ownerKey = keywordContainerKey(candidate.file, candidate.container);
1302
+ const before = matches.length;
1303
+ const sharedContainers = await resolveSharedOwnerContainers({
1304
+ index,
1305
+ ownerFile: candidate.file,
1306
+ ownerContainer: candidate.container,
1307
+ ownerSource: candidate.context.source,
1308
+ languageId: candidate.context.sup.id,
1309
+ });
1310
+ for (const shared of sharedContainers) {
1311
+ const crossFileSwift = candidate.context.sup.id === "swift" && fileIdentityKey(candidate.file) !== fileIdentityKey(shared.file);
1312
+ const sharedPredicate = (local) => {
1313
+ if (memberScope !== "any" &&
1314
+ !matchesReceiverMemberScope(local, memberScope, shared.context, shared.container)) {
1315
+ return false;
193
1316
  }
194
- if (memberDef) {
195
- return okGoToResult(index, memberDef, {
196
- via: { exportedName: member },
197
- resolution: "member-access",
198
- confidence: "medium",
199
- });
1317
+ if (!crossFileSwift)
1318
+ return true;
1319
+ const nameNode = nameNodeForDef(shared.context, local);
1320
+ return !!nameNode && !isSwiftCrossFileHiddenSharedOwnerMember("swift", candidate.file, shared.file, nameNode);
1321
+ };
1322
+ appendDirectKeywordMembers(shared.module.locals, member, shared.container, shared.context, shared.context.sup.normalizeIdentifier, sharedPredicate, matches);
1323
+ }
1324
+ // Partial parts of one type share its identity.
1325
+ recordOwner(before, ownerKey);
1326
+ }
1327
+ let uniqueMatches = uniqueReceiverMemberCandidates(matches);
1328
+ // A private member of a base class is not accessible: it neither answers nor stops the walk.
1329
+ // The first level of a `this` lookup is the class itself, including C# partial parts.
1330
+ if (filtersPrivateBases && (startAtAncestor || depth > 0) && uniqueMatches.length) {
1331
+ const inherited = [];
1332
+ for (const candidate of uniqueMatches) {
1333
+ if (!(await isUninheritedPrivateMember(index, mod, node, candidate)))
1334
+ inherited.push(candidate);
1335
+ }
1336
+ uniqueMatches = inherited;
1337
+ }
1338
+ if (uniqueMatches.length && report)
1339
+ report.named = true;
1340
+ if (uniqueMatches.length) {
1341
+ const allowUniqueArityMismatch = !startAtAncestor && depth === 0;
1342
+ if (!spansHierarchy) {
1343
+ return await selectReceiverMemberCandidates(index, uniqueMatches, knownArgumentCount, allowUniqueArityMismatch);
1344
+ }
1345
+ for (const candidate of uniqueMatches) {
1346
+ if (knownArgumentCount !== undefined &&
1347
+ (await receiverMemberAcceptsArgumentCount(index, candidate, knownArgumentCount)) === false) {
1348
+ continue;
1349
+ }
1350
+ const owner = ownerOf.get(memberKey(candidate)) ?? "";
1351
+ const subclasses = subclassesOf.get(owner);
1352
+ let overridden = false;
1353
+ for (const entry of accepted) {
1354
+ if (subclasses?.has(entry.owner) && (await sameParameterTypes(index, entry.def, candidate))) {
1355
+ overridden = true;
1356
+ break;
200
1357
  }
201
1358
  }
1359
+ if (!overridden)
1360
+ accepted.push({ def: candidate, owner });
1361
+ }
1362
+ // Navigation still names the only incompatible candidate when no ancestor accepts the call.
1363
+ if (allowUniqueArityMismatch && !lenient) {
1364
+ lenient = await selectReceiverMemberCandidates(index, uniqueMatches, knownArgumentCount, true);
1365
+ }
1366
+ }
1367
+ const next = [];
1368
+ for (const candidate of level) {
1369
+ const childKey = keywordContainerKey(candidate.file, candidate.container);
1370
+ const childSubclasses = subclassesOf.get(childKey);
1371
+ for (const parent of await baseRefsFromContainer(index, candidate.module, candidate.container, candidate.context.source, candidate.context.sup, startAtAncestor, candidate.context.tree)) {
1372
+ const key = keywordContainerKey(parent.file, parent.container);
1373
+ // Every class on a path to this ancestor derives from it, including through a diamond.
1374
+ const subclasses = subclassesOf.get(key) ?? new Set();
1375
+ subclasses.add(childKey);
1376
+ for (const subclass of childSubclasses ?? [])
1377
+ subclasses.add(subclass);
1378
+ subclassesOf.set(key, subclasses);
1379
+ if (visited.has(key))
1380
+ continue;
1381
+ visited.add(key);
1382
+ next.push(parent);
202
1383
  }
203
1384
  }
1385
+ level = next;
1386
+ }
1387
+ if (!spansHierarchy)
1388
+ return undefined;
1389
+ // Without type ranking, two surviving overloads are ambiguous, including a fixed-arity and a
1390
+ // variable-arity one: `hit(String)` and `hit(int...)` both take one argument, and only argument
1391
+ // types decide.
1392
+ if (accepted.length > 1) {
1393
+ if (report)
1394
+ report.ambiguous = true;
1395
+ return undefined;
1396
+ }
1397
+ return accepted[0]?.def ?? lenient;
1398
+ }
1399
+ /** The parameter list of a Java, Kotlin, or C# method declaration. */
1400
+ async function memberParameterList(index, def) {
1401
+ const context = await ensureParsedContext(def.file, index.parsed?.get(fileIdentityKey(def.file)), index.languageExtensions).catch(() => null);
1402
+ const start = def.range.start.index;
1403
+ if (!context || start === undefined)
1404
+ return null;
1405
+ const declaration = context.tree.rootNode.descendantForIndex(start, def.range.end.index ?? start).parent;
1406
+ return (declaration?.childForFieldName("parameters") ??
1407
+ declaration?.namedChildren.find((child) => child.type === "function_value_parameters") ??
1408
+ null);
1409
+ }
1410
+ /** Parameter type texts of a Java, Kotlin, or C# method; null when a type cannot be read. */
1411
+ async function memberParameterTypes(index, def) {
1412
+ const parameters = await memberParameterList(index, def);
1413
+ if (!parameters)
1414
+ return null;
1415
+ const types = [];
1416
+ for (const parameter of parameters.namedChildren) {
1417
+ const type = parameter.childForFieldName("type") ?? parameter.namedChildren.at(-1);
1418
+ if (!type)
1419
+ return null;
1420
+ types.push(type.text.replace(/\s+/g, ""));
1421
+ }
1422
+ return types;
1423
+ }
1424
+ /** The import that binds a type name in a module, as text; empty when no import binds it. */
1425
+ function importedTypeSource(module, name) {
1426
+ for (const imp of module.imports) {
1427
+ if (imp.kind !== "named" || imp.local !== name)
1428
+ continue;
1429
+ const target = typeof imp.resolved === "string" ? imp.resolved : (imp.resolved?.external ?? "");
1430
+ return `${target}:${imp.imported}`;
1431
+ }
1432
+ return "";
1433
+ }
1434
+ /** Type parameter names declared by a method or any type enclosing it. */
1435
+ async function typeParameterNamesAround(index, def) {
1436
+ const names = new Set();
1437
+ const context = await ensureParsedContext(def.file, index.parsed?.get(fileIdentityKey(def.file)), index.languageExtensions).catch(() => null);
1438
+ const start = def.range.start.index;
1439
+ if (!context || start === undefined)
1440
+ return names;
1441
+ let current = context.tree.rootNode.descendantForIndex(start, def.range.end.index ?? start);
1442
+ for (; current; current = current.parent) {
1443
+ const parameters = current.childForFieldName("type_parameters") ??
1444
+ current.namedChildren.find((child) => child.type === "type_parameters" || child.type === "type_parameter_list");
1445
+ if (!parameters)
1446
+ continue;
1447
+ for (const parameter of parameters.namedChildren) {
1448
+ const name = parameter.childForFieldName("name") ??
1449
+ parameter.namedChildren.find((child) => /identifier/u.test(child.type)) ??
1450
+ (/identifier/u.test(parameter.type) ? parameter : null);
1451
+ if (name)
1452
+ names.add(name.text);
1453
+ }
1454
+ }
1455
+ return names;
1456
+ }
1457
+ /** Built-in parameter types whose spelling names one type in every file. */
1458
+ const BUILTIN_PARAMETER_TYPES = {
1459
+ java: new Set([
1460
+ "int",
1461
+ "long",
1462
+ "short",
1463
+ "byte",
1464
+ "char",
1465
+ "boolean",
1466
+ "float",
1467
+ "double",
1468
+ "String",
1469
+ "Object",
1470
+ "Integer",
1471
+ "Long",
1472
+ "Boolean",
1473
+ "Double",
1474
+ "Float",
1475
+ "Short",
1476
+ "Byte",
1477
+ "Character",
1478
+ ]),
1479
+ kotlin: new Set(["Int", "Long", "Short", "Byte", "Char", "Boolean", "Float", "Double", "String", "Any", "Unit"]),
1480
+ csharp: new Set([
1481
+ "int",
1482
+ "long",
1483
+ "short",
1484
+ "byte",
1485
+ "char",
1486
+ "bool",
1487
+ "float",
1488
+ "double",
1489
+ "decimal",
1490
+ "string",
1491
+ "object",
1492
+ "uint",
1493
+ "ulong",
1494
+ "ushort",
1495
+ "sbyte",
1496
+ ]),
1497
+ };
1498
+ /**
1499
+ * Whether two methods take the same parameter types, so the one in a derived class overrides the
1500
+ * other. Equal spelling is proof only within one file or for built-in types; elsewhere each
1501
+ * spelling must resolve to the same declaration from its own file.
1502
+ */
1503
+ async function sameParameterTypes(index, left, right) {
1504
+ // A type parameter (`T`) names a different type in each declaration after substitution, so
1505
+ // equal spelling proves nothing.
1506
+ const [leftGenerics, rightGenerics] = await Promise.all([
1507
+ typeParameterNamesAround(index, left),
1508
+ typeParameterNamesAround(index, right),
1509
+ ]);
1510
+ const [leftTypes, rightTypes] = await Promise.all([
1511
+ memberParameterTypes(index, left),
1512
+ memberParameterTypes(index, right),
1513
+ ]);
1514
+ if (!leftTypes || !rightTypes || leftTypes.length !== rightTypes.length)
1515
+ return false;
1516
+ const sameFile = fileIdentityKey(left.file) === fileIdentityKey(right.file);
1517
+ const leftContext = await ensureParsedContext(left.file, index.parsed?.get(fileIdentityKey(left.file)), index.languageExtensions).catch(() => null);
1518
+ if (!leftContext)
1519
+ return false;
1520
+ for (let at = 0; at < leftTypes.length; at += 1) {
1521
+ const text = leftTypes[at];
1522
+ if (text !== rightTypes[at])
1523
+ return false;
1524
+ const typeNames = text.match(/[\p{L}_][\p{L}\p{N}_]*/gu) ?? [];
1525
+ if (typeNames.some((typeName) => leftGenerics.has(typeName) || rightGenerics.has(typeName)))
1526
+ return false;
1527
+ // One Java or Kotlin file declares one package; a C# file can open several namespaces, so the
1528
+ // same spelling there still needs resolution.
1529
+ if (sameFile && leftContext.sup.id !== "csharp")
1530
+ continue;
1531
+ const base = text
1532
+ .replace(/<.*$/su, "")
1533
+ .replace(/[?\[\].]+$/u, "")
1534
+ .split(".")
1535
+ .pop() ?? text;
1536
+ if (BUILTIN_PARAMETER_TYPES[leftContext.sup.id]?.has(base))
1537
+ continue;
1538
+ const rightContext = await ensureParsedContext(right.file, index.parsed?.get(fileIdentityKey(right.file)), index.languageExtensions).catch(() => null);
1539
+ const leftModule = index.byFile.get(fileIdentityKey(left.file));
1540
+ const rightModule = index.byFile.get(fileIdentityKey(right.file));
1541
+ if (!rightContext || !leftModule || !rightModule)
1542
+ return false;
1543
+ // Resolve at each declaration so a C# spelling reads its own namespace region.
1544
+ const leftType = resolveNamedDefinition(index, leftModule, left.file, leftContext.sup, base, undefined, left.range.start.index);
1545
+ const rightType = resolveNamedDefinition(index, rightModule, right.file, rightContext.sup, base, undefined, right.range.start.index);
1546
+ if (leftType?.status !== "ok" && rightType?.status !== "ok") {
1547
+ // Neither names a project type: both files must name it through the same import (or none).
1548
+ if (importedTypeSource(leftModule, base) !== importedTypeSource(rightModule, base))
1549
+ return false;
1550
+ continue;
1551
+ }
1552
+ if (leftType?.status !== "ok" || rightType?.status !== "ok")
1553
+ return false;
1554
+ if (fileIdentityKey(leftType.definition.file) !== fileIdentityKey(rightType.definition.file) ||
1555
+ leftType.definition.range.start.index !== rightType.definition.range.start.index) {
1556
+ return false;
1557
+ }
1558
+ }
1559
+ return true;
1560
+ }
1561
+ /**
1562
+ * Validate an unqualified member use against its actual lexical owner, including shared
1563
+ * owner parts in other files and inherited members. Swift checks every bare name; C#, Java, and
1564
+ * Kotlin check only bare call callees. A static context reaches only static members.
1565
+ */
1566
+ /** The callee of a receiverless call (`f()`), which may name a member through the implicit `this`. */
1567
+ export function implicitSelfCallee(languageId, call) {
1568
+ if (languageId === "csharp")
1569
+ return call.type === "invocation_expression" ? call.childForFieldName("function") : null;
1570
+ if (languageId === "java") {
1571
+ if (call.type !== "method_invocation" || call.childForFieldName("object"))
1572
+ return null;
1573
+ return call.childForFieldName("name");
204
1574
  }
1575
+ if (languageId === "kotlin")
1576
+ return call.type === "call_expression" ? (call.namedChildren[0] ?? null) : null;
205
1577
  return null;
206
1578
  }
207
- function findEnclosingClassContainer(node) {
208
- return nearestMemberContainer(node);
1579
+ export async function resolveImplicitSelfMember(index, mod, node, name, source, languageId) {
1580
+ const call = node.parent ?? node;
1581
+ if (languageId !== "swift" && implicitSelfCallee(languageId, call)?.id !== node.id)
1582
+ return undefined;
1583
+ const jvm = languageId === "java" || languageId === "kotlin";
1584
+ const staticScoped = languageId === "csharp" || jvm;
1585
+ const memberScope = staticScoped && nodeInStaticMemberContext(node, source) ? "static" : "any";
1586
+ const argumentCount = getCallArgumentCount({ languageId, source, call }) ?? undefined;
1587
+ const report = { named: false, ambiguous: false };
1588
+ const member = await resolveKeywordReceiverMember(index, mod, node, name, memberScope, false, argumentCount, undefined, report);
1589
+ if (member)
1590
+ return member;
1591
+ // Two inherited overloads of one arity are ambiguous without type ranking; in Java and C# any
1592
+ // inherited method of that name hides imports even when none accepts the call.
1593
+ if (report.ambiguous || (report.named && (languageId === "java" || languageId === "csharp")))
1594
+ return null;
1595
+ if (memberScope !== "static" || !jvm)
1596
+ return undefined;
1597
+ // In a static context an inherited instance method still shadows a static import or package
1598
+ // function; the call is invalid, so it has no target.
1599
+ const instanceReport = { named: false, ambiguous: false };
1600
+ const instance = await resolveKeywordReceiverMember(index, mod, node, name, "any", false, argumentCount, undefined, instanceReport);
1601
+ // Any inherited instance method of that name hides the import, even when none applies.
1602
+ return instance || instanceReport.named ? null : undefined;
1603
+ }
1604
+ /** Whether a Java or Kotlin member is `private` and declared outside the class enclosing the use. */
1605
+ async function isUninheritedPrivateMember(index, mod, use, member) {
1606
+ const container = findEnclosingClassContainer(use);
1607
+ const start = member.range.start.index ?? -1;
1608
+ const ownMember = !!container &&
1609
+ fileIdentityKey(member.file) === fileIdentityKey(mod.file) &&
1610
+ start >= container.startIndex &&
1611
+ start < container.endIndex;
1612
+ if (ownMember)
1613
+ return false;
1614
+ const parsed = await ensureParsedContext(member.file, index.parsed?.get(fileIdentityKey(member.file)), index.languageExtensions).catch(() => null);
1615
+ const nameNode = parsed?.tree.rootNode.descendantForIndex(start, member.range.end.index ?? start);
1616
+ return !!parsed && !!nameNode && isPrivateDeclaration(parsed.sup.id, nameNode);
1617
+ }
1618
+ /**
1619
+ * Whether `Owner::member()` at `use` can reach an instance member of `owner` through an implicit
1620
+ * `this`: the enclosing function is a non-static member function of `owner` or of a class that
1621
+ * derives from `owner` through proven bases. Any other context can only name a static member.
1622
+ */
1623
+ export async function cppQualifiedOwnerHasImplicitThis(index, mod, use, source, sup, owner) {
1624
+ const definition = cppEnclosingFunctionDefinition(use);
1625
+ if (!definition || !(await cppFunctionHasImplicitThis(index, mod, definition, source, sup)))
1626
+ return false;
1627
+ const outOfLinePath = cppOutOfLineOwnerPath(definition, source, sup);
1628
+ const enclosingDef = outOfLinePath ? await resolveCppQualifiedMemberContainer(index, mod, outOfLinePath) : null;
1629
+ const enclosing = enclosingDef
1630
+ ? await keywordClassRefFromDef(index, enclosingDef)
1631
+ : await keywordClassRefFromNode(index, mod, definition);
1632
+ const target = await keywordClassRefFromDef(index, owner);
1633
+ if (!enclosing || !target)
1634
+ return false;
1635
+ const targetKey = keywordContainerKey(target.file, target.container);
1636
+ const visited = new Set();
1637
+ let level = [enclosing];
1638
+ for (let depth = 0; depth < RECEIVER_HIERARCHY_DEPTH && level.length; depth += 1) {
1639
+ const next = [];
1640
+ for (const candidate of level) {
1641
+ const key = keywordContainerKey(candidate.file, candidate.container);
1642
+ if (key === targetKey)
1643
+ return true;
1644
+ if (visited.has(key))
1645
+ continue;
1646
+ visited.add(key);
1647
+ next.push(...(await baseRefsFromContainer(index, candidate.module, candidate.container, candidate.context.source, candidate.context.sup, false, candidate.context.tree)));
1648
+ }
1649
+ level = next;
1650
+ }
1651
+ return false;
1652
+ }
1653
+ /** The C++ function definition enclosing a use, if any. */
1654
+ export function cppEnclosingFunctionDefinition(node) {
1655
+ for (let current = node.parent; current; current = current.parent) {
1656
+ if (current.type === "function_definition")
1657
+ return current;
1658
+ }
1659
+ return null;
1660
+ }
1661
+ /** Proven unqualified member call from an out-of-line C++ method definition. */
1662
+ export async function resolveCppOutOfLineImplicitMember(index, mod, node, name, source, sup, requireAcceptedArity = false) {
1663
+ if (node.parent?.type !== "call_expression" || node.parent.childForFieldName("function")?.id !== node.id) {
1664
+ return undefined;
1665
+ }
1666
+ const ownerPath = cppOutOfLineOwnerPath(node, source, sup);
1667
+ const owner = ownerPath ? await resolveCppQualifiedMemberContainer(index, mod, ownerPath) : null;
1668
+ if (!owner)
1669
+ return undefined;
1670
+ const argumentCount = getCallArgumentCount({ languageId: "cpp", source, call: node.parent }) ?? undefined;
1671
+ const enclosingFunction = cppEnclosingFunctionDefinition(node);
1672
+ const memberScope = enclosingFunction && (await cppFunctionHasImplicitThis(index, mod, enclosingFunction, source, sup))
1673
+ ? "any"
1674
+ : "static";
1675
+ const matching = await resolveKeywordReceiverMember(index, mod, node, name, memberScope, false, argumentCount, owner);
1676
+ // A class member hides a same-named global even when this call cannot accept its arity. The
1677
+ // only member is still the call's target for navigation; an ambiguous overload set is not.
1678
+ const member = matching ??
1679
+ (argumentCount === undefined
1680
+ ? undefined
1681
+ : await resolveKeywordReceiverMember(index, mod, node, name, memberScope, false, undefined, owner));
1682
+ if (!member) {
1683
+ // In a static member function, an instance member still hides a same-named global: the call
1684
+ // is ill-formed, so neither consumer may fall back to the global.
1685
+ const hidden = memberScope === "static" &&
1686
+ (await resolveKeywordReceiverMember(index, mod, node, name, "any", false, undefined, owner));
1687
+ return hidden ? null : undefined;
1688
+ }
1689
+ // The call graph records no edge for a call its target cannot accept. `null` means a member hides
1690
+ // the name but is not the call's target, so no consumer may fall back to a same-named free function.
1691
+ const rejected = requireAcceptedArity &&
1692
+ argumentCount !== undefined &&
1693
+ !(await receiverMemberAcceptsArgumentCount(index, member, argumentCount));
1694
+ return rejected ? null : member;
209
1695
  }
210
- export { supportsReceiverCallEdges, supportsReceiverMemberNavigation, } from "../graphs/symbol-graph-detailed/receiver-calls.js";
1696
+ export { supportsReceiverCallEdges, supportsReceiverMemberNavigation } from "../util/member-access-tables.js";
211
1697
  function memberDeclaringLocals(mod, typeName, normalize) {
212
1698
  const normalized = normalize(typeName);
213
1699
  return mod.locals.filter((local) => normalize(local.localName) === normalized && declaresMembers(local));
214
1700
  }
215
- async function resolveReceiverDefinition(obj, source, sup, resolveExpression, mod) {
1701
+ /**
1702
+ * Ruby constant visible the same way go-to-definition resolves a bare `Widget`:
1703
+ * a same-file declaration, or the class a `require` brought in. Not project-wide.
1704
+ */
1705
+ export function resolveRubyVisibleConstant(index, mod, sup, typeName) {
1706
+ if (sup.id !== "ruby")
1707
+ return null;
1708
+ const resolved = resolveNamedDefinition(index, mod, mod.file, sup, typeName);
1709
+ if (resolved?.status !== "ok")
1710
+ return null;
1711
+ return declaresMembers(resolved.definition) ? resolved.definition : null;
1712
+ }
1713
+ /** Proven PHP `extends` base, using the same simple-name lookup as `parent::`. */
1714
+ function resolvePhpProvenBaseClass(index, mod, classNode, source, sup) {
1715
+ const bases = collectDeclaredBaseTypes(classNode, source, sup, true);
1716
+ if (bases.length !== 1)
1717
+ return null;
1718
+ const base = bases[0];
1719
+ if (!base || base.kind !== "simple")
1720
+ return null;
1721
+ const normalize = (name) => foldPhpIdentifierCase(sup.normalizeIdentifier(name));
1722
+ return resolveNamedMemberContainer(index, mod, base.name, normalize) ?? null;
1723
+ }
1724
+ /**
1725
+ * Class named by `new self()`, `new static()`, or `new parent()`.
1726
+ * `self` and `static` are the enclosing class. `parent` is the proven base.
1727
+ * Returns null when the keyword is not proven, so a class named `Parent` cannot win.
1728
+ */
1729
+ export function resolvePhpObjectCreationTarget(index, mod, keyword, source, sup) {
1730
+ if (!keyword.classNode)
1731
+ return null;
1732
+ if (keyword.keyword === "self" || keyword.keyword === "static") {
1733
+ return memberContainerDef(mod, keyword.classNode);
1734
+ }
1735
+ return resolvePhpProvenBaseClass(index, mod, keyword.classNode, source, sup);
1736
+ }
1737
+ function enclosingRubyMethod(node) {
1738
+ let current = node.parent;
1739
+ while (current) {
1740
+ if (current.type === "method" || current.type === "singleton_method")
1741
+ return current;
1742
+ current = current.parent;
1743
+ }
1744
+ return null;
1745
+ }
1746
+ /** Bare `super` / `super()` targets the same-named method on a proven superclass. */
1747
+ export async function resolveRubySuperDefinition(index, mod, node, source, sup) {
1748
+ if (sup.id !== "ruby" || node.type !== "super")
1749
+ return undefined;
1750
+ const method = enclosingRubyMethod(node);
1751
+ const nameNode = method?.childForFieldName("name") ?? null;
1752
+ if (!nameNode)
1753
+ return undefined;
1754
+ const member = sliceText(nameNode, source);
1755
+ if (!member)
1756
+ return undefined;
1757
+ return resolveKeywordReceiverMember(index, mod, node, member, "any", true);
1758
+ }
1759
+ async function resolveReceiverDefinition(index, obj, source, sup, resolveExpression, mod) {
216
1760
  const constructor = receiverConstructorExpression(obj, source, sup);
217
1761
  if (constructor) {
1762
+ if (sup.id === "cpp") {
1763
+ const qualifiedType = cppQualifiedNameSegments(constructor, source);
1764
+ if (qualifiedType.length > 1) {
1765
+ const def = await resolveCppQualifiedMemberContainer(index, mod, qualifiedType);
1766
+ if (def) {
1767
+ return {
1768
+ def,
1769
+ memberScope: hasStaticMemberDistinction(sup.id) ? "instance" : "any",
1770
+ };
1771
+ }
1772
+ }
1773
+ }
218
1774
  const typeName = sliceText(constructor, source);
219
1775
  const typedLocals = memberDeclaringLocals(mod, typeName, sup.normalizeIdentifier);
220
1776
  if (typedLocals.length === 1) {
@@ -223,13 +1779,36 @@ async function resolveReceiverDefinition(obj, source, sup, resolveExpression, mo
223
1779
  memberScope: hasStaticMemberDistinction(sup.id) ? "instance" : "any",
224
1780
  };
225
1781
  }
1782
+ if (sup.id === "php") {
1783
+ const normalizeTypeName = (name) => foldPhpIdentifierCase(sup.normalizeIdentifier(name));
1784
+ const namedContainer = resolveNamedMemberContainer(index, mod, typeName, normalizeTypeName);
1785
+ if (namedContainer) {
1786
+ return {
1787
+ def: namedContainer,
1788
+ memberScope: hasStaticMemberDistinction(sup.id) ? "instance" : "any",
1789
+ };
1790
+ }
1791
+ }
226
1792
  const result = await resolveExpression(constructor);
227
- if (result?.kind === "resolved" && declaresMembers(result.def)) {
1793
+ const constructed = result?.kind === "resolved" ? asMemberContainer(index, result.def) : undefined;
1794
+ if (constructed) {
228
1795
  return {
229
- def: result.def,
1796
+ def: constructed,
230
1797
  memberScope: hasStaticMemberDistinction(sup.id) ? "instance" : "any",
231
1798
  };
232
1799
  }
1800
+ // Ruby `require` exposes a class as a namespace import. A bare constant already
1801
+ // resolves through resolveNamedDefinition; `Klass.new` must use that same path
1802
+ // so goto, references, and call edges agree. Same-file classes returned above.
1803
+ if (sup.id === "ruby" && typedLocals.length === 0) {
1804
+ const rubyType = resolveRubyVisibleConstant(index, mod, sup, typeName);
1805
+ if (rubyType) {
1806
+ return {
1807
+ def: rubyType,
1808
+ memberScope: hasStaticMemberDistinction(sup.id) ? "instance" : "any",
1809
+ };
1810
+ }
1811
+ }
233
1812
  if (typedLocals[0]) {
234
1813
  return {
235
1814
  def: typedLocals[0],
@@ -238,12 +1817,13 @@ async function resolveReceiverDefinition(obj, source, sup, resolveExpression, mo
238
1817
  }
239
1818
  }
240
1819
  const direct = await resolveExpression(obj);
241
- if (direct?.kind === "resolved" && declaresMembers(direct.def)) {
242
- if (isJsTsLanguage(sup.id) && direct.def.kind === SymbolKind.TypeAlias) {
243
- return { def: direct.def, memberScope: "any", runtimeTypeOnly: true };
1820
+ const directContainer = direct?.kind === "resolved" ? asMemberContainer(index, direct.def) : undefined;
1821
+ if (directContainer) {
1822
+ if (isJsTsLanguage(sup.id) && directContainer.kind === SymbolKind.TypeAlias) {
1823
+ return { def: directContainer, memberScope: "any", runtimeTypeOnly: true };
244
1824
  }
245
- const memberScope = hasStaticMemberDistinction(sup.id) ? "static" : "any";
246
- return { def: direct.def, memberScope };
1825
+ const memberScope = supportsStaticMemberScope(sup.id) ? "static" : "any";
1826
+ return { def: directContainer, memberScope };
247
1827
  }
248
1828
  if (isJsTsLanguage(sup.id) && isReceiverNameNode(sup, obj.type)) {
249
1829
  return null;
@@ -253,7 +1833,25 @@ async function resolveReceiverDefinition(obj, source, sup, resolveExpression, mo
253
1833
  }
254
1834
  return null;
255
1835
  }
256
- async function resolveMemberDefinitionForBase(index, baseDef, member) {
1836
+ /** Whether a type-qualified access (`Util.M`) can name `def`: a nested type or a static member. */
1837
+ async function reachableThroughTypeName(index, def) {
1838
+ if (declaresMembers(def))
1839
+ return true;
1840
+ const context = await ensureParsedContext(def.file, index.parsed?.get(fileIdentityKey(def.file)), index.languageExtensions);
1841
+ const nameNode = nameNodeForDef(context, def);
1842
+ if (!nameNode)
1843
+ return false;
1844
+ return hasStaticModifier(def, context, nearestMemberContainer(nameNode) ?? context.tree.rootNode);
1845
+ }
1846
+ /**
1847
+ * A member declared directly in the type `baseDef` names (`Util.Two`), including the other parts
1848
+ * of a C# partial type, or inherited through it. Same-named overloads are narrowed to members a
1849
+ * type name reaches (static members and nested types) when the base is a type, then chosen by
1850
+ * the call's argument count; without a count, or when several accept it, the lookup is
1851
+ * ambiguous. A sole reachable candidate is kept even when the count does not fit, as for other
1852
+ * calls; the graph records no edge for it. Ruby reopens a method by redefining it, so it keeps the first match.
1853
+ */
1854
+ async function resolveMemberDefinitionForBase(index, baseDef, member, knownArgumentCount) {
257
1855
  const targetContext = await ensureParsedContext(baseDef.file, undefined, index.languageExtensions);
258
1856
  const start = baseDef.range.start;
259
1857
  const targetPosition = {
@@ -267,37 +1865,141 @@ async function resolveMemberDefinitionForBase(index, baseDef, member) {
267
1865
  const targetModule = index.byFile.get(fileIdentityKey(baseDef.file));
268
1866
  if (!targetModule)
269
1867
  return undefined;
1868
+ const languageId = targetContext.sup.id;
270
1869
  const normalizeIdentifier = targetContext.sup.normalizeIdentifier;
271
- const directHit = findDirectLocalWithinNode(targetModule.locals, member, container, targetContext, normalizeIdentifier);
272
- if (directHit)
273
- return directHit;
274
- if (targetContext.sup.id === "java")
1870
+ const candidates = findDirectLocalsWithinNode(targetModule.locals, member, container, targetContext, normalizeIdentifier);
1871
+ if (languageId === "csharp") {
1872
+ const partialParts = await findSharedOwnerMemberDefinitions({
1873
+ index,
1874
+ ownerFile: baseDef.file,
1875
+ ownerContainer: container,
1876
+ ownerSource: targetContext.source,
1877
+ languageId,
1878
+ member,
1879
+ });
1880
+ for (const hit of partialParts) {
1881
+ if (!candidates.includes(hit))
1882
+ candidates.push(hit);
1883
+ }
1884
+ }
1885
+ if (candidates.length > 1 && languageId !== "ruby") {
1886
+ const reachable = [];
1887
+ for (const candidate of candidates) {
1888
+ if (!declaresMembers(baseDef) || (await reachableThroughTypeName(index, candidate)))
1889
+ reachable.push(candidate);
1890
+ }
1891
+ return await selectReceiverMemberCandidates(index, reachable, knownArgumentCount);
1892
+ }
1893
+ if (candidates[0])
1894
+ return candidates[0];
1895
+ if (languageId === "java")
275
1896
  return undefined;
276
- return findReceiverMemberDefinition(targetModule.locals, member, baseDef, container, targetContext, normalizeIdentifier);
1897
+ return await findReceiverMemberDefinition(index, targetModule.locals, member, baseDef, container, targetContext, normalizeIdentifier);
1898
+ }
1899
+ function typescriptMergedInterfaceMemberCandidates(locals, member, receiverDef, container, targetContext, normalizeIdentifier, predicate) {
1900
+ const matches = findDirectLocalsWithinNode(locals, member, container, targetContext, normalizeIdentifier, predicate);
1901
+ if (!isJsTsLanguage(targetContext.sup.id) || container.type !== "interface_declaration")
1902
+ return matches;
1903
+ const receiverStart = receiverDef.range.start.index ?? 0;
1904
+ const receiverEnd = receiverDef.range.end.index ?? receiverStart;
1905
+ const receiverContainerKey = typescriptCallableContainerKey(targetContext.tree, receiverStart, receiverEnd);
1906
+ const seenBodies = new Set();
1907
+ const appendMembers = (interfaceDeclaration) => {
1908
+ const body = interfaceDeclaration.childForFieldName("body");
1909
+ if (!body)
1910
+ return;
1911
+ const bodyKey = typescriptCallableContainerKey(targetContext.tree, body.startIndex, body.endIndex);
1912
+ if (seenBodies.has(bodyKey))
1913
+ return;
1914
+ seenBodies.add(bodyKey);
1915
+ for (const candidate of findDirectLocalsWithinNode(locals, member, interfaceDeclaration, targetContext, normalizeIdentifier, predicate)) {
1916
+ if (!matches.includes(candidate))
1917
+ matches.push(candidate);
1918
+ }
1919
+ };
1920
+ const normalizedReceiverName = normalizeIdentifier(receiverDef.localName);
1921
+ for (const local of locals) {
1922
+ if (normalizeIdentifier(local.localName) !== normalizedReceiverName)
1923
+ continue;
1924
+ const start = local.range.start.index;
1925
+ if (start === undefined)
1926
+ continue;
1927
+ const end = local.range.end.index ?? start;
1928
+ if (typescriptCallableContainerKey(targetContext.tree, start, end) !== receiverContainerKey)
1929
+ continue;
1930
+ const nameNode = targetContext.tree.rootNode.descendantForIndex(start, end);
1931
+ const interfaceDeclaration = nameNode.parent;
1932
+ if (interfaceDeclaration?.type !== "interface_declaration")
1933
+ continue;
1934
+ appendMembers(interfaceDeclaration);
1935
+ }
1936
+ return matches;
1937
+ }
1938
+ /** Same-name namespace reopenings, excluding members of a decoy namespace or nested owner. */
1939
+ export function findTypeScriptNamespaceMemberCandidates(locals, member, receiverDef, context) {
1940
+ if (!isJsTsLanguage(context.sup.id))
1941
+ return [];
1942
+ const containers = typescriptMergedNamespaceContainers(context.tree, locals, receiverDef);
1943
+ const matches = [];
1944
+ for (const container of containers) {
1945
+ for (const candidate of findDirectLocalsWithinNode(locals, member, container, context, context.sup.normalizeIdentifier)) {
1946
+ if (!matches.includes(candidate))
1947
+ matches.push(candidate);
1948
+ }
1949
+ }
1950
+ return matches;
277
1951
  }
278
- function findReceiverMemberDefinition(locals, member, receiverDef, container, targetContext, normalizeIdentifier, memberScope = "any") {
1952
+ async function findReceiverMemberDefinition(index, locals, member, receiverDef, container, targetContext, normalizeIdentifier, memberScope = "any", knownArgumentCount) {
279
1953
  const memberPredicate = memberScope === "any"
280
1954
  ? undefined
281
1955
  : (local) => matchesReceiverMemberScope(local, memberScope, targetContext, container);
282
- const containerHit = memberScope === "any"
283
- ? findLocalWithinNode(locals, member, container, normalizeIdentifier)
284
- : findDirectLocalWithinNode(locals, member, container, targetContext, normalizeIdentifier, memberPredicate);
285
- if (containerHit)
286
- return containerHit;
1956
+ if ((targetContext.sup.id === "ts" || targetContext.sup.id === "tsx") &&
1957
+ (container.type === "internal_module" || container.type === "module")) {
1958
+ const candidates = findTypeScriptNamespaceMemberCandidates(locals, member, receiverDef, targetContext);
1959
+ return await selectReceiverMemberCandidates(index, candidates, knownArgumentCount);
1960
+ }
1961
+ const allReceiverMatches = typescriptMergedInterfaceMemberCandidates(locals, member, receiverDef, container, targetContext, normalizeIdentifier, memberPredicate);
1962
+ if (targetContext.sup.id === "csharp" || targetContext.sup.id === "swift") {
1963
+ const shared = await findSharedOwnerMemberDefinitions({
1964
+ index,
1965
+ ownerFile: receiverDef.file,
1966
+ ownerContainer: container,
1967
+ ownerSource: targetContext.source,
1968
+ languageId: targetContext.sup.id,
1969
+ member,
1970
+ memberScope,
1971
+ });
1972
+ for (const hit of shared) {
1973
+ if (!allReceiverMatches.includes(hit))
1974
+ allReceiverMatches.push(hit);
1975
+ }
1976
+ }
1977
+ if (allReceiverMatches.length) {
1978
+ return await selectReceiverMemberCandidates(index, allReceiverMatches, knownArgumentCount);
1979
+ }
287
1980
  if (targetContext.sup.id === "rust") {
288
- const implNode = findRustImplForType(targetContext.tree.rootNode, receiverDef.localName, targetContext.source);
289
- return implNode ? findLocalWithinNode(locals, member, implNode, normalizeIdentifier) : undefined;
1981
+ const matches = [];
1982
+ for (const implNode of findRustImplsForType(targetContext.tree.rootNode, receiverDef.localName, targetContext.source, targetContext.sup)) {
1983
+ appendDirectKeywordMembers(locals, member, implNode, targetContext, normalizeIdentifier, undefined, matches);
1984
+ }
1985
+ return await selectReceiverMemberCandidates(index, matches, knownArgumentCount);
290
1986
  }
291
1987
  if (targetContext.sup.id === "go") {
292
1988
  return findGoReceiverMember(locals, member, receiverDef.localName, targetContext, normalizeIdentifier);
293
1989
  }
1990
+ if (targetContext.sup.id === "kotlin") {
1991
+ const extensionMatches = kotlinExtensionFunctionsNamedOnType(locals, member, receiverDef.localName, targetContext, normalizeIdentifier);
1992
+ if (extensionMatches.length) {
1993
+ return await selectReceiverMemberCandidates(index, extensionMatches, knownArgumentCount);
1994
+ }
1995
+ }
294
1996
  return undefined;
295
1997
  }
296
- function findLocalWithinNode(locals, member, node, normalizeIdentifier = (name) => name, predicate) {
1998
+ function findLocalsWithinNode(locals, member, node, normalizeIdentifier = (name) => name, predicate) {
297
1999
  const containerStart = node.startIndex;
298
2000
  const containerEnd = node.endIndex;
299
2001
  const normalizedMember = normalizeIdentifier(member);
300
- return locals.find((local) => {
2002
+ return locals.filter((local) => {
301
2003
  const startIndex = local.range.start.index;
302
2004
  const endIndex = local.range.end.index;
303
2005
  return (normalizeIdentifier(local.localName) === normalizedMember &&
@@ -308,27 +2010,108 @@ function findLocalWithinNode(locals, member, node, normalizeIdentifier = (name)
308
2010
  (!predicate || predicate(local)));
309
2011
  });
310
2012
  }
2013
+ function findLocalWithinNode(locals, member, node, normalizeIdentifier = (name) => name, predicate) {
2014
+ return findLocalsWithinNode(locals, member, node, normalizeIdentifier, predicate)[0];
2015
+ }
311
2016
  function matchesReceiverMemberScope(local, memberScope, targetContext, container) {
312
2017
  if (memberScope === "any")
313
2018
  return true;
314
2019
  return hasStaticModifier(local, targetContext, container) === (memberScope === "static");
315
2020
  }
316
- function nodeDeclaresStatic(node, source) {
317
- if (node.type === "static" || node.type === "static_modifier")
2021
+ async function getCallableArityForDef(index, def) {
2022
+ const context = await ensureParsedContext(def.file, undefined, index.languageExtensions);
2023
+ const start = def.range.start;
2024
+ const position = { row: start.line - 1, column: start.column - 1 };
2025
+ const nameNode = context.tree.rootNode.descendantForPosition(position, position);
2026
+ const container = nearestMemberContainer(nameNode);
2027
+ let current = nameNode;
2028
+ while (current && current !== container) {
2029
+ if (CALLABLE_DECLARATION_NODE_TYPES[current.type]) {
2030
+ const range = getCallableArity({
2031
+ languageId: context.sup.id,
2032
+ source: context.source,
2033
+ declaration: current,
2034
+ binding: memberLookupBinding(context.sup.id),
2035
+ });
2036
+ if (range)
2037
+ return range;
2038
+ }
2039
+ current = current.parent;
2040
+ }
2041
+ return undefined;
2042
+ }
2043
+ /**
2044
+ * Whether a member found for a receiverless call can accept that call's argument count. Unknown
2045
+ * counts and unknown arities accept.
2046
+ */
2047
+ export async function memberAcceptsCallAt(index, member, callee, source, languageId) {
2048
+ const argumentCount = getCallArgumentCount({ languageId, source, call: callee.parent ?? callee });
2049
+ if (argumentCount === null)
318
2050
  return true;
319
- if (node.type === "storage_class_specifier" || node.type === "modifier" || node.type === "property_modifier") {
320
- return sliceText(node, source).trim() === "static";
2051
+ return (await receiverMemberAcceptsArgumentCount(index, member, argumentCount)) !== false;
2052
+ }
2053
+ async function receiverMemberAcceptsArgumentCount(index, def, argumentCount) {
2054
+ const context = await ensureParsedContext(def.file, undefined, index.languageExtensions);
2055
+ const start = def.range.start;
2056
+ const position = {
2057
+ row: start.line - 1,
2058
+ column: start.column - 1,
2059
+ };
2060
+ const nameNode = context.tree.rootNode.descendantForPosition(position, position);
2061
+ if (context.sup.id === "cpp") {
2062
+ const shape = cppCallableShapeForNode(nameNode);
2063
+ return shape
2064
+ ? argumentCount >= shape.minArity && (shape.maxArity === null || argumentCount <= shape.maxArity)
2065
+ : undefined;
321
2066
  }
322
- if (node.type === "modifiers") {
323
- for (let childIndex = 0;; childIndex += 1) {
324
- const child = node.child(childIndex);
325
- if (!child)
326
- break;
327
- if (nodeDeclaresStatic(child, source))
328
- return true;
2067
+ const range = await getCallableArityForDef(index, def);
2068
+ if (!range)
2069
+ return undefined;
2070
+ return argumentCount >= range.minArgs && (range.maxArgs === null || argumentCount <= range.maxArgs);
2071
+ }
2072
+ function uniqueReceiverMemberCandidates(candidates) {
2073
+ const seen = new Set();
2074
+ const unique = [];
2075
+ for (const candidate of candidates) {
2076
+ const key = keywordClassKey(candidate);
2077
+ if (seen.has(key))
2078
+ continue;
2079
+ seen.add(key);
2080
+ unique.push(candidate);
2081
+ }
2082
+ return unique;
2083
+ }
2084
+ async function selectReceiverMemberCandidates(index, candidates, knownArgumentCount, allowUniqueArityMismatch = true) {
2085
+ if (candidates.length > 1) {
2086
+ const file = candidates[0]?.file;
2087
+ if (file && candidates.every((candidate) => candidate.file === file)) {
2088
+ const context = await ensureParsedContext(file, undefined, index.languageExtensions);
2089
+ if (isJsTsLanguage(context.sup.id)) {
2090
+ // Signature-only overloads still need arity selection before generic member deduplication.
2091
+ return typescriptSelectOverloadCandidate({
2092
+ group: candidates,
2093
+ tree: context.tree,
2094
+ definitionOf: (candidate) => candidate,
2095
+ declarationOf: (candidate) => nameNodeForDef(context, candidate)?.parent,
2096
+ source: context.source,
2097
+ languageId: context.sup.id,
2098
+ argumentCount: knownArgumentCount ?? null,
2099
+ });
2100
+ }
329
2101
  }
330
2102
  }
331
- return false;
2103
+ const unique = uniqueReceiverMemberCandidates(candidates);
2104
+ if (unique.length === 1 && (allowUniqueArityMismatch || knownArgumentCount === undefined))
2105
+ return unique[0];
2106
+ if (knownArgumentCount === undefined)
2107
+ return undefined;
2108
+ const matches = [];
2109
+ for (const candidate of unique) {
2110
+ if ((await receiverMemberAcceptsArgumentCount(index, candidate, knownArgumentCount)) !== false) {
2111
+ matches.push(candidate);
2112
+ }
2113
+ }
2114
+ return matches.length === 1 ? matches[0] : undefined;
332
2115
  }
333
2116
  function hasStaticModifier(local, targetContext, container) {
334
2117
  const position = {
@@ -336,19 +2119,21 @@ function hasStaticModifier(local, targetContext, container) {
336
2119
  column: local.range.start.column - 1,
337
2120
  };
338
2121
  let current = targetContext.tree.rootNode.descendantForPosition(position, position);
2122
+ if (declarationIsStaticEquivalent(targetContext.sup.id, current))
2123
+ return true;
339
2124
  while (current && current !== container) {
340
- for (let childIndex = 0;; childIndex += 1) {
341
- const child = current.child(childIndex);
342
- if (!child)
343
- break;
344
- if (nodeDeclaresStatic(child, targetContext.source))
345
- return true;
346
- }
2125
+ if (targetContext.sup.id === "php" && (current.type === "const_declaration" || current.type === "enum_case"))
2126
+ return true;
2127
+ if (declarationNodeIsStatic(current, targetContext.source))
2128
+ return true;
347
2129
  current = current.parent;
348
2130
  }
349
2131
  return false;
350
2132
  }
351
2133
  const NESTED_MEMBER_LOCAL_CONTAINERS = new Set([
2134
+ "formal_parameters",
2135
+ "parameter_list",
2136
+ "parameters",
352
2137
  "block",
353
2138
  "class",
354
2139
  "class_declaration",
@@ -367,14 +2152,22 @@ const NESTED_MEMBER_LOCAL_CONTAINERS = new Set([
367
2152
  "module",
368
2153
  "statement_block",
369
2154
  ]);
370
- function findDirectLocalWithinNode(locals, member, container, targetContext, normalizeIdentifier, predicate) {
2155
+ function matchesReceiverMemberName(local, normalizedMember, targetContext, normalizeIdentifier) {
2156
+ const localName = normalizeIdentifier(local.localName);
2157
+ if (targetContext.sup.id === "php") {
2158
+ return comparePhpReferenceNames(normalizedMember, localName, { symbolKind: local.kind }) === "equivalent";
2159
+ }
2160
+ return localName === normalizedMember;
2161
+ }
2162
+ function findDirectLocalsWithinNode(locals, member, container, targetContext, normalizeIdentifier, predicate) {
2163
+ const matches = [];
371
2164
  const containerStart = container.startIndex;
372
2165
  const containerEnd = container.endIndex;
373
2166
  const normalizedMember = normalizeIdentifier(member);
374
2167
  for (const local of locals) {
375
2168
  const startIndex = local.range.start.index;
376
2169
  const endIndex = local.range.end.index;
377
- if (normalizeIdentifier(local.localName) !== normalizedMember ||
2170
+ if (!matchesReceiverMemberName(local, normalizedMember, targetContext, normalizeIdentifier) ||
378
2171
  startIndex === undefined ||
379
2172
  endIndex === undefined ||
380
2173
  startIndex < containerStart ||
@@ -390,8 +2183,18 @@ function findDirectLocalWithinNode(locals, member, container, targetContext, nor
390
2183
  let isDeclarationParent = true;
391
2184
  while (current && current !== container) {
392
2185
  const isDirectBody = (current.type === "statement_block" || current.type === "block") && current.parent === container;
2186
+ // A transparent container (Kotlin's `companion object`) donates its body's members to its
2187
+ // own enclosing class; the grammar never lets one nest under any other declaration, so its
2188
+ // body never counts as evidence of a foreign nested class here.
2189
+ const isTransparentBody = current.type === "class_body" &&
2190
+ !!current.parent &&
2191
+ TRANSPARENT_MEMBER_CONTAINER_TYPES[current.parent.type] === true;
2192
+ const definingDeclarator = current.type === "function_definition" ? current.childForFieldName("declarator") : null;
2193
+ const isOwnFunctionDeclaration = !!definingDeclarator && definingDeclarator.startIndex <= startIndex && definingDeclarator.endIndex >= endIndex;
393
2194
  if (!isDeclarationParent &&
394
2195
  !isDirectBody &&
2196
+ !isOwnFunctionDeclaration &&
2197
+ !isTransparentBody &&
395
2198
  ((current.type === "class_body" && current.parent !== container) ||
396
2199
  NESTED_MEMBER_LOCAL_CONTAINERS.has(current.type))) {
397
2200
  current = null;
@@ -401,30 +2204,138 @@ function findDirectLocalWithinNode(locals, member, container, targetContext, nor
401
2204
  current = current.parent;
402
2205
  }
403
2206
  if (current && (!predicate || predicate(local)))
404
- return local;
2207
+ matches.push(local);
405
2208
  }
406
- return undefined;
2209
+ return matches;
407
2210
  }
408
- function findRustImplForType(root, typeName, source) {
409
- let found = null;
410
- const visit = (node) => {
411
- if (node.type === "impl_item") {
412
- const text = sliceText(node, source);
413
- if (new RegExp(`^\\s*impl\\s+${escapeRegExp(typeName)}\\b`).test(text)) {
414
- found = node;
415
- return false;
416
- }
2211
+ function findDirectLocalWithinNode(locals, member, container, targetContext, normalizeIdentifier, predicate) {
2212
+ return findDirectLocalsWithinNode(locals, member, container, targetContext, normalizeIdentifier, predicate)[0];
2213
+ }
2214
+ function appendDirectKeywordMembers(locals, member, container, targetContext, normalizeIdentifier, predicate, matches) {
2215
+ const containerStart = container.startIndex;
2216
+ const containerEnd = container.endIndex;
2217
+ const normalizedMember = normalizeIdentifier(member);
2218
+ for (const local of locals) {
2219
+ const startIndex = local.range.start.index;
2220
+ const endIndex = local.range.end.index;
2221
+ if (!matchesReceiverMemberName(local, normalizedMember, targetContext, normalizeIdentifier) ||
2222
+ startIndex === undefined ||
2223
+ endIndex === undefined ||
2224
+ startIndex < containerStart ||
2225
+ endIndex > containerEnd) {
2226
+ continue;
417
2227
  }
418
- for (const child of node.namedChildren) {
419
- if (!visit(child))
420
- return false;
2228
+ const start = local.range.start;
2229
+ const position = {
2230
+ row: start.line - 1,
2231
+ column: start.column - 1,
2232
+ };
2233
+ const declarationNode = targetContext.tree.rootNode.descendantForPosition(position, position);
2234
+ if (!isDirectKeywordMemberDeclaration(declarationNode, container) || (predicate && !predicate(local))) {
2235
+ continue;
2236
+ }
2237
+ matches.push(local);
2238
+ }
2239
+ }
2240
+ const SWIFT_PARAMETER_OWNER_TYPES = {
2241
+ init_declaration: true,
2242
+ function_declaration: true,
2243
+ deinit_declaration: true,
2244
+ subscript_declaration: true,
2245
+ lambda_literal: true,
2246
+ protocol_function_declaration: true,
2247
+ };
2248
+ /**
2249
+ * Swift parameters and function-body locals share the type's byte span, but
2250
+ * `self.name` names the member. A parameter or local that shadows the bare name
2251
+ * is not a member candidate. Only `simple_identifier` names are Swift.
2252
+ */
2253
+ function isSwiftShadowingNonMember(declarationNode) {
2254
+ if (declarationNode.type !== "simple_identifier")
2255
+ return false;
2256
+ let current = declarationNode.parent;
2257
+ while (current) {
2258
+ if (current.type === "parameter" && current.parent && SWIFT_PARAMETER_OWNER_TYPES[current.parent.type]) {
2259
+ return true;
2260
+ }
2261
+ if (current.type === "function_body" ||
2262
+ current.type === "lambda_literal" ||
2263
+ current.type === "computed_property" ||
2264
+ current.type === "willset_didset_block") {
2265
+ return true;
421
2266
  }
2267
+ if (current.type === "class_declaration" ||
2268
+ current.type === "protocol_declaration" ||
2269
+ current.type === "enum_class_body") {
2270
+ return false;
2271
+ }
2272
+ current = current.parent;
2273
+ }
2274
+ return false;
2275
+ }
2276
+ /** Parameter lists whose entries are locals of the function, not members of its type. */
2277
+ const PARAMETER_LIST_TYPES = new Set([
2278
+ "formal_parameters",
2279
+ "parameter_list",
2280
+ "parameters",
2281
+ "function_value_parameters",
2282
+ ]);
2283
+ /**
2284
+ * A parameter that also declares a property of the enclosing type: a TypeScript parameter
2285
+ * property (`constructor(readonly path: string)`) or a PHP promoted constructor property.
2286
+ */
2287
+ function isPropertyParameter(declarationNode, list) {
2288
+ let parameter = declarationNode;
2289
+ while (parameter && parameter.parent !== list)
2290
+ parameter = parameter.parent;
2291
+ if (!parameter)
2292
+ return false;
2293
+ if (parameter.type === "property_promotion_parameter")
422
2294
  return true;
2295
+ return parameter.namedChildren.some((child) => child.type === "accessibility_modifier" || child.type === "override_modifier" || child.text === "readonly");
2296
+ }
2297
+ export function isDirectKeywordMemberDeclaration(declarationNode, container) {
2298
+ if (isSwiftShadowingNonMember(declarationNode))
2299
+ return false;
2300
+ if (nearestMemberContainer(declarationNode) !== container)
2301
+ return false;
2302
+ let functionDepth = 0;
2303
+ let current = declarationNode;
2304
+ while (current && current !== container) {
2305
+ // Swift local functions can nest inside a member; only the outer function is a member.
2306
+ if (current.type === "function_declaration" && ++functionDepth > 1)
2307
+ return false;
2308
+ const isMethodBody = (current.type === "block" || current.type === "compound_statement" || current.type === "statement_block") &&
2309
+ current.parent !== container;
2310
+ if (isMethodBody)
2311
+ return false;
2312
+ // A method parameter is a local of that method, unless it also declares a property.
2313
+ if (PARAMETER_LIST_TYPES.has(current.type))
2314
+ return isPropertyParameter(declarationNode, current);
2315
+ current = current.parent;
2316
+ }
2317
+ return current === container;
2318
+ }
2319
+ /**
2320
+ * Every Rust `impl` block whose self type is `typeName`, across inherent (`impl Circle`),
2321
+ * trait (`impl Shape for Circle`), and generic (`impl<T> Box<T>`) forms. The shared
2322
+ * self-type extraction keeps navigation and detailed-graph member ownership in agreement.
2323
+ */
2324
+ function findRustImplsForType(root, typeName, source, sup) {
2325
+ const normalized = sup.normalizeIdentifier(typeName);
2326
+ const found = [];
2327
+ const visit = (node) => {
2328
+ if (node.type === "impl_item") {
2329
+ const selfType = rustImplSelfTypeNode(node, sup);
2330
+ if (selfType && sup.normalizeIdentifier(sliceText(selfType, source)) === normalized)
2331
+ found.push(node);
2332
+ }
2333
+ for (const child of node.namedChildren)
2334
+ visit(child);
423
2335
  };
424
2336
  visit(root);
425
2337
  return found;
426
2338
  }
427
- const GO_EMBED_DEPTH = 16;
428
2339
  function goMethodReceiverTypeName(methodNode, source, sup) {
429
2340
  const receiver = methodNode.childForFieldName("receiver");
430
2341
  if (!receiver)
@@ -498,10 +2409,35 @@ function goMethodsNamedOnType(locals, member, typeName, targetContext, normalize
498
2409
  visit(targetContext.tree.rootNode);
499
2410
  return matches;
500
2411
  }
2412
+ /**
2413
+ * Kotlin extension functions declared `fun Type.member()` for a receiver type, searched across
2414
+ * the receiver type's own declaring file the same way `goMethodsNamedOnType` searches Go's file
2415
+ * for a receiver method. An ordinary class member never carries a receiver-type prefix, so this
2416
+ * never matches a real member and cannot shadow one.
2417
+ */
2418
+ function kotlinExtensionFunctionsNamedOnType(locals, member, typeName, targetContext, normalizeIdentifier) {
2419
+ const matches = [];
2420
+ const normalizedType = normalizeIdentifier(typeName);
2421
+ const visit = (node) => {
2422
+ if (node.type === "function_declaration") {
2423
+ const receiverType = kotlinExtensionReceiverTypeNode(node, targetContext.sup);
2424
+ if (receiverType && normalizeIdentifier(sliceText(receiverType, targetContext.source)) === normalizedType) {
2425
+ const local = findLocalWithinNode(locals, member, node, normalizeIdentifier);
2426
+ if (local && !matches.includes(local))
2427
+ matches.push(local);
2428
+ }
2429
+ return;
2430
+ }
2431
+ for (const child of node.namedChildren)
2432
+ visit(child);
2433
+ };
2434
+ visit(targetContext.tree.rootNode);
2435
+ return matches;
2436
+ }
501
2437
  function findGoReceiverMember(locals, member, typeName, targetContext, normalizeIdentifier) {
502
2438
  const visited = new Set();
503
2439
  let level = [typeName];
504
- for (let depth = 0; depth < GO_EMBED_DEPTH && level.length; depth += 1) {
2440
+ for (let depth = 0; depth < RECEIVER_HIERARCHY_DEPTH && level.length; depth += 1) {
505
2441
  const matches = [];
506
2442
  const next = [];
507
2443
  for (const currentType of level) {
@@ -525,20 +2461,23 @@ function findGoReceiverMember(locals, member, typeName, targetContext, normalize
525
2461
  }
526
2462
  return undefined;
527
2463
  }
528
- const PYTHON_SUPERTYPE_DEPTH = 16;
529
2464
  function pythonClassKey(def) {
530
2465
  const start = def.range.start;
531
2466
  return `${fileIdentityKey(def.file)}:${start.index ?? `${start.line}:${start.column}`}`;
532
2467
  }
533
- async function resolvePythonReceiverMember(index, mod, node, obj, member, source, sup, resolveExpression) {
534
- const classRef = await pythonReceiverClassRef(index, mod, node, obj, source, sup, resolveExpression);
2468
+ async function resolvePythonReceiverMember(index, mod, node, obj, member, source, tree, sup, resolveExpression) {
2469
+ const classRef = await pythonReceiverClassRef(index, mod, node, obj, source, tree, sup, resolveExpression);
535
2470
  if (!classRef)
536
2471
  return undefined;
537
- return lookupPythonClassMember(index, classRef, member);
2472
+ return lookupPythonClassMember(index, classRef.ref, member, classRef.startAtSupertype);
538
2473
  }
539
- async function pythonReceiverClassRef(index, mod, node, obj, source, sup, resolveExpression) {
540
- const receiverName = sliceText(obj, source);
541
- if (receiverName === "self" || receiverName === "cls") {
2474
+ async function pythonReceiverClassRef(index, mod, node, obj, source, tree, sup, resolveExpression) {
2475
+ const receiverName = receiverKeywordText(sup, obj, source, (callee) => {
2476
+ const scope = getOrBuildScopeIndex(index, mod.file, source, sup, mod, tree);
2477
+ return !!findClosestScopeBinding(scope, sliceText(callee, source), callee, sup);
2478
+ });
2479
+ const keywordKind = keywordReceiverKind(sup.id, receiverName);
2480
+ if (keywordKind) {
542
2481
  const container = findEnclosingClassContainer(node);
543
2482
  if (!container)
544
2483
  return null;
@@ -558,7 +2497,8 @@ async function pythonReceiverClassRef(index, mod, node, obj, source, sup, resolv
558
2497
  });
559
2498
  if (!def)
560
2499
  return null;
561
- return pythonClassRefFromDef(index, def);
2500
+ const ref = await pythonClassRefFromDef(index, def);
2501
+ return ref ? { ref, startAtSupertype: keywordKind === "supertype" } : null;
562
2502
  }
563
2503
  let classDef;
564
2504
  const constructor = receiverConstructorExpression(obj, source, sup);
@@ -576,7 +2516,8 @@ async function pythonReceiverClassRef(index, mod, node, obj, source, sup, resolv
576
2516
  }
577
2517
  if (!classDef)
578
2518
  return null;
579
- return pythonClassRefFromDef(index, classDef);
2519
+ const ref = await pythonClassRefFromDef(index, classDef);
2520
+ return ref ? { ref, startAtSupertype: false } : null;
580
2521
  }
581
2522
  async function pythonClassRefFromDef(index, def) {
582
2523
  const module = index.byFile.get(fileIdentityKey(def.file));
@@ -663,17 +2604,15 @@ function resolvePythonNamedClass(index, mod, name) {
663
2604
  return candidates[0];
664
2605
  if (candidates.length > 1)
665
2606
  return undefined;
666
- for (const imp of mod.imports) {
667
- if (imp.kind === "named" && imp.local === name) {
668
- const result = resolveImported(index, imp, imp.imported);
669
- if (result && !("namespace" in result) && result.kind === SymbolKind.Class)
670
- return result;
671
- }
672
- if (imp.kind === "star") {
673
- const result = resolveImported(index, imp, name);
674
- if (result && !("namespace" in result) && result.kind === SymbolKind.Class)
675
- return result;
676
- }
2607
+ const explicitBinding = effectiveExplicitBinding(mod.imports, "python", (binding) => binding.kind === "named" && binding.local === name);
2608
+ if (explicitBinding?.kind === "named") {
2609
+ const result = resolveImported(index, explicitBinding, explicitBinding.imported);
2610
+ if (result && !("namespace" in result) && result.kind === SymbolKind.Class)
2611
+ return result;
2612
+ }
2613
+ const starDecision = resolveStarImportedName(index, mod, "python", name);
2614
+ if (starDecision.status === "resolved") {
2615
+ return starDecision.definition.kind === SymbolKind.Class ? starDecision.definition : undefined;
677
2616
  }
678
2617
  const exported = resolveExport(index, mod.file, name, { preferredKind: SymbolKind.Class, allowLocalFallback: false });
679
2618
  if (exported?.kind === "resolved" && exported.def.kind === SymbolKind.Class)
@@ -698,15 +2637,17 @@ async function pythonBaseClassRefs(index, classRef) {
698
2637
  }
699
2638
  return refs;
700
2639
  }
701
- async function lookupPythonClassMember(index, start, member) {
702
- const own = pythonMembersOnClass(start, member);
703
- if (own.length === 1)
704
- return own[0];
705
- if (own.length > 1)
706
- return undefined;
2640
+ async function lookupPythonClassMember(index, start, member, startAtSupertype = false) {
2641
+ if (!startAtSupertype) {
2642
+ const own = pythonMembersOnClass(start, member);
2643
+ if (own.length === 1)
2644
+ return own[0];
2645
+ if (own.length > 1)
2646
+ return undefined;
2647
+ }
707
2648
  let level = await pythonBaseClassRefs(index, start);
708
2649
  const visited = new Set([pythonClassKey(start.def), ...level.map((base) => pythonClassKey(base.def))]);
709
- for (let depth = 0; depth < PYTHON_SUPERTYPE_DEPTH && level.length; depth += 1) {
2650
+ for (let depth = 0; depth < RECEIVER_HIERARCHY_DEPTH && level.length; depth += 1) {
710
2651
  const matches = [];
711
2652
  const seenMatch = new Set();
712
2653
  for (const base of level) {
@@ -736,7 +2677,4 @@ async function lookupPythonClassMember(index, start, member) {
736
2677
  }
737
2678
  return undefined;
738
2679
  }
739
- function escapeRegExp(value) {
740
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
741
- }
742
2680
  //# sourceMappingURL=navigation-goto.js.map