@lzehrung/codegraph 2.3.29 → 2.3.30

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 (303) hide show
  1. package/dist/bin/cli.js +5982 -4145
  2. package/dist/bin/query-index-worker.js +1302 -753
  3. package/dist/document-links/asciidoc.d.ts.map +1 -1
  4. package/dist/document-links/asciidoc.js +3 -4
  5. package/dist/document-links/asciidoc.js.map +1 -1
  6. package/dist/document-links/html-forms.d.ts +39 -0
  7. package/dist/document-links/html-forms.d.ts.map +1 -0
  8. package/dist/document-links/html-forms.js +94 -0
  9. package/dist/document-links/html-forms.js.map +1 -0
  10. package/dist/document-links/html.d.ts +8 -0
  11. package/dist/document-links/html.d.ts.map +1 -1
  12. package/dist/document-links/html.js +30 -15
  13. package/dist/document-links/html.js.map +1 -1
  14. package/dist/document-links/markdown.d.ts.map +1 -1
  15. package/dist/document-links/markdown.js +6 -7
  16. package/dist/document-links/markdown.js.map +1 -1
  17. package/dist/document-links/rst.d.ts.map +1 -1
  18. package/dist/document-links/rst.js +6 -0
  19. package/dist/document-links/rst.js.map +1 -1
  20. package/dist/document-links/sfc.d.ts.map +1 -1
  21. package/dist/document-links/sfc.js +4 -4
  22. package/dist/document-links/sfc.js.map +1 -1
  23. package/dist/graphs/angularjs.d.ts.map +1 -1
  24. package/dist/graphs/angularjs.js +9 -1
  25. package/dist/graphs/angularjs.js.map +1 -1
  26. package/dist/graphs/edge-resolution.d.ts.map +1 -1
  27. package/dist/graphs/edge-resolution.js +8 -3
  28. package/dist/graphs/edge-resolution.js.map +1 -1
  29. package/dist/graphs/specifiers.d.ts.map +1 -1
  30. package/dist/graphs/specifiers.js +62 -104
  31. package/dist/graphs/specifiers.js.map +1 -1
  32. package/dist/graphs/symbol-graph-detailed/edge-passes.d.ts.map +1 -1
  33. package/dist/graphs/symbol-graph-detailed/edge-passes.js +118 -86
  34. package/dist/graphs/symbol-graph-detailed/edge-passes.js.map +1 -1
  35. package/dist/graphs/symbol-graph-detailed/receiver-calls.d.ts +34 -0
  36. package/dist/graphs/symbol-graph-detailed/receiver-calls.d.ts.map +1 -1
  37. package/dist/graphs/symbol-graph-detailed/receiver-calls.js +525 -25
  38. package/dist/graphs/symbol-graph-detailed/receiver-calls.js.map +1 -1
  39. package/dist/impact/call-compatibility/providers/index.d.ts +3 -1
  40. package/dist/impact/call-compatibility/providers/index.d.ts.map +1 -1
  41. package/dist/impact/call-compatibility/providers/index.js +11 -4
  42. package/dist/impact/call-compatibility/providers/index.js.map +1 -1
  43. package/dist/impact/call-compatibility.d.ts.map +1 -1
  44. package/dist/impact/call-compatibility.js +2 -7
  45. package/dist/impact/call-compatibility.js.map +1 -1
  46. package/dist/impact/member-resolution-coverage.js +2 -2
  47. package/dist/impact/member-resolution-coverage.js.map +1 -1
  48. package/dist/indexer/build-cache/manifest.d.ts +7 -0
  49. package/dist/indexer/build-cache/manifest.d.ts.map +1 -1
  50. package/dist/indexer/build-cache/manifest.js +32 -1
  51. package/dist/indexer/build-cache/manifest.js.map +1 -1
  52. package/dist/indexer/build-cache/options.d.ts +16 -2
  53. package/dist/indexer/build-cache/options.d.ts.map +1 -1
  54. package/dist/indexer/build-cache/options.js +17 -3
  55. package/dist/indexer/build-cache/options.js.map +1 -1
  56. package/dist/indexer/build-cache/project-snapshot.d.ts.map +1 -1
  57. package/dist/indexer/build-cache/project-snapshot.js +1 -0
  58. package/dist/indexer/build-cache/project-snapshot.js.map +1 -1
  59. package/dist/indexer/build-index.d.ts.map +1 -1
  60. package/dist/indexer/build-index.js +80 -2
  61. package/dist/indexer/build-index.js.map +1 -1
  62. package/dist/indexer/build-manifest.d.ts +1 -0
  63. package/dist/indexer/build-manifest.d.ts.map +1 -1
  64. package/dist/indexer/build-manifest.js +18 -0
  65. package/dist/indexer/build-manifest.js.map +1 -1
  66. package/dist/indexer/build-workers.js +1 -1
  67. package/dist/indexer/build-workers.js.map +1 -1
  68. package/dist/indexer/declaration-visibility.d.ts +43 -0
  69. package/dist/indexer/declaration-visibility.d.ts.map +1 -0
  70. package/dist/indexer/declaration-visibility.js +202 -0
  71. package/dist/indexer/declaration-visibility.js.map +1 -0
  72. package/dist/indexer/imports/binding-ranges.d.ts +1 -1
  73. package/dist/indexer/imports/binding-ranges.d.ts.map +1 -1
  74. package/dist/indexer/imports/binding-ranges.js +2 -123
  75. package/dist/indexer/imports/binding-ranges.js.map +1 -1
  76. package/dist/indexer/imports/js-text-imports.d.ts.map +1 -1
  77. package/dist/indexer/imports/js-text-imports.js +5 -2
  78. package/dist/indexer/imports/js-text-imports.js.map +1 -1
  79. package/dist/indexer/imports/language-specific.d.ts.map +1 -1
  80. package/dist/indexer/imports/language-specific.js +30 -216
  81. package/dist/indexer/imports/language-specific.js.map +1 -1
  82. package/dist/indexer/imports/native-captures.d.ts.map +1 -1
  83. package/dist/indexer/imports/native-captures.js +23 -32
  84. package/dist/indexer/imports/native-captures.js.map +1 -1
  85. package/dist/indexer/imports/text-import-extractors.d.ts +37 -0
  86. package/dist/indexer/imports/text-import-extractors.d.ts.map +1 -0
  87. package/dist/indexer/imports/text-import-extractors.js +358 -0
  88. package/dist/indexer/imports/text-import-extractors.js.map +1 -0
  89. package/dist/indexer/locals-and-exports.d.ts.map +1 -1
  90. package/dist/indexer/locals-and-exports.js +23 -27
  91. package/dist/indexer/locals-and-exports.js.map +1 -1
  92. package/dist/indexer/navigation-goto.d.ts +1 -7
  93. package/dist/indexer/navigation-goto.d.ts.map +1 -1
  94. package/dist/indexer/navigation-goto.js +180 -381
  95. package/dist/indexer/navigation-goto.js.map +1 -1
  96. package/dist/indexer/navigation-resolve.d.ts.map +1 -1
  97. package/dist/indexer/navigation-resolve.js +20 -1
  98. package/dist/indexer/navigation-resolve.js.map +1 -1
  99. package/dist/indexer/navigation.d.ts.map +1 -1
  100. package/dist/indexer/navigation.js +39 -7
  101. package/dist/indexer/navigation.js.map +1 -1
  102. package/dist/indexer/parse-context.js +2 -2
  103. package/dist/indexer/parse-context.js.map +1 -1
  104. package/dist/indexer/scope-nodes.d.ts +108 -0
  105. package/dist/indexer/scope-nodes.d.ts.map +1 -0
  106. package/dist/indexer/scope-nodes.js +238 -0
  107. package/dist/indexer/scope-nodes.js.map +1 -0
  108. package/dist/indexer/scope.d.ts.map +1 -1
  109. package/dist/indexer/scope.js +70 -130
  110. package/dist/indexer/scope.js.map +1 -1
  111. package/dist/indexer/types.d.ts +7 -0
  112. package/dist/indexer/types.d.ts.map +1 -1
  113. package/dist/languages/definitions/c-family.d.ts +18 -4
  114. package/dist/languages/definitions/c-family.d.ts.map +1 -1
  115. package/dist/languages/definitions/c-family.js +51 -13
  116. package/dist/languages/definitions/c-family.js.map +1 -1
  117. package/dist/languages/definitions/c.d.ts.map +1 -1
  118. package/dist/languages/definitions/c.js +7 -43
  119. package/dist/languages/definitions/c.js.map +1 -1
  120. package/dist/languages/definitions/cpp.d.ts.map +1 -1
  121. package/dist/languages/definitions/cpp.js +15 -61
  122. package/dist/languages/definitions/cpp.js.map +1 -1
  123. package/dist/languages/definitions/csharp.d.ts.map +1 -1
  124. package/dist/languages/definitions/csharp.js +46 -52
  125. package/dist/languages/definitions/csharp.js.map +1 -1
  126. package/dist/languages/definitions/css-like.d.ts.map +1 -1
  127. package/dist/languages/definitions/css-like.js +13 -10
  128. package/dist/languages/definitions/css-like.js.map +1 -1
  129. package/dist/languages/definitions/go.d.ts.map +1 -1
  130. package/dist/languages/definitions/go.js +29 -42
  131. package/dist/languages/definitions/go.js.map +1 -1
  132. package/dist/languages/definitions/html.d.ts.map +1 -1
  133. package/dist/languages/definitions/html.js +15 -14
  134. package/dist/languages/definitions/html.js.map +1 -1
  135. package/dist/languages/definitions/java.d.ts.map +1 -1
  136. package/dist/languages/definitions/java.js +42 -48
  137. package/dist/languages/definitions/java.js.map +1 -1
  138. package/dist/languages/definitions/javascript.d.ts.map +1 -1
  139. package/dist/languages/definitions/javascript.js +26 -54
  140. package/dist/languages/definitions/javascript.js.map +1 -1
  141. package/dist/languages/definitions/js-family.d.ts +15 -1
  142. package/dist/languages/definitions/js-family.d.ts.map +1 -1
  143. package/dist/languages/definitions/js-family.js +51 -0
  144. package/dist/languages/definitions/js-family.js.map +1 -1
  145. package/dist/languages/definitions/kotlin.d.ts.map +1 -1
  146. package/dist/languages/definitions/kotlin.js +26 -43
  147. package/dist/languages/definitions/kotlin.js.map +1 -1
  148. package/dist/languages/definitions/php.d.ts.map +1 -1
  149. package/dist/languages/definitions/php.js +50 -60
  150. package/dist/languages/definitions/php.js.map +1 -1
  151. package/dist/languages/definitions/python.d.ts.map +1 -1
  152. package/dist/languages/definitions/python.js +23 -14
  153. package/dist/languages/definitions/python.js.map +1 -1
  154. package/dist/languages/definitions/ruby.d.ts.map +1 -1
  155. package/dist/languages/definitions/ruby.js +14 -17
  156. package/dist/languages/definitions/ruby.js.map +1 -1
  157. package/dist/languages/definitions/rust.d.ts.map +1 -1
  158. package/dist/languages/definitions/rust.js +30 -52
  159. package/dist/languages/definitions/rust.js.map +1 -1
  160. package/dist/languages/definitions/scss.d.ts.map +1 -1
  161. package/dist/languages/definitions/scss.js +33 -49
  162. package/dist/languages/definitions/scss.js.map +1 -1
  163. package/dist/languages/definitions/sfc-graph.d.ts +4 -0
  164. package/dist/languages/definitions/sfc-graph.d.ts.map +1 -0
  165. package/dist/languages/definitions/sfc-graph.js +16 -0
  166. package/dist/languages/definitions/sfc-graph.js.map +1 -0
  167. package/dist/languages/definitions/shared.d.ts +33 -0
  168. package/dist/languages/definitions/shared.d.ts.map +1 -0
  169. package/dist/languages/definitions/shared.js +63 -0
  170. package/dist/languages/definitions/shared.js.map +1 -0
  171. package/dist/languages/definitions/sql.d.ts.map +1 -1
  172. package/dist/languages/definitions/sql.js +1 -0
  173. package/dist/languages/definitions/sql.js.map +1 -1
  174. package/dist/languages/definitions/svelte.d.ts.map +1 -1
  175. package/dist/languages/definitions/svelte.js +2 -10
  176. package/dist/languages/definitions/svelte.js.map +1 -1
  177. package/dist/languages/definitions/swift.d.ts.map +1 -1
  178. package/dist/languages/definitions/swift.js +37 -31
  179. package/dist/languages/definitions/swift.js.map +1 -1
  180. package/dist/languages/definitions/typescript.d.ts.map +1 -1
  181. package/dist/languages/definitions/typescript.js +56 -90
  182. package/dist/languages/definitions/typescript.js.map +1 -1
  183. package/dist/languages/definitions/vue.d.ts.map +1 -1
  184. package/dist/languages/definitions/vue.js +2 -10
  185. package/dist/languages/definitions/vue.js.map +1 -1
  186. package/dist/languages/definitions/zig.d.ts.map +1 -1
  187. package/dist/languages/definitions/zig.js +9 -15
  188. package/dist/languages/definitions/zig.js.map +1 -1
  189. package/dist/languages/graph-captures.d.ts +40 -0
  190. package/dist/languages/graph-captures.d.ts.map +1 -0
  191. package/dist/languages/graph-captures.js +46 -0
  192. package/dist/languages/graph-captures.js.map +1 -0
  193. package/dist/languages/import-statement-parsers.d.ts +17 -0
  194. package/dist/languages/import-statement-parsers.d.ts.map +1 -1
  195. package/dist/languages/import-statement-parsers.js +108 -0
  196. package/dist/languages/import-statement-parsers.js.map +1 -1
  197. package/dist/languages/js-family.d.ts +2 -0
  198. package/dist/languages/js-family.d.ts.map +1 -1
  199. package/dist/languages/js-family.js +15 -6
  200. package/dist/languages/js-family.js.map +1 -1
  201. package/dist/languages/types.d.ts +4 -0
  202. package/dist/languages/types.d.ts.map +1 -1
  203. package/dist/languages/types.js +1 -1
  204. package/dist/languages/types.js.map +1 -1
  205. package/dist/languages.js +1 -1
  206. package/dist/languages.js.map +1 -1
  207. package/dist/native/contracts.d.ts +1 -1
  208. package/dist/native/contracts.d.ts.map +1 -1
  209. package/dist/native/js-bridge.d.ts +10 -0
  210. package/dist/native/js-bridge.d.ts.map +1 -1
  211. package/dist/native/js-bridge.js +10 -0
  212. package/dist/native/js-bridge.js.map +1 -1
  213. package/dist/native/native-backend-report.d.ts.map +1 -1
  214. package/dist/native/native-backend-report.js +2 -0
  215. package/dist/native/native-backend-report.js.map +1 -1
  216. package/dist/util/comments.d.ts +7 -7
  217. package/dist/util/comments.d.ts.map +1 -1
  218. package/dist/util/comments.js +15 -214
  219. package/dist/util/comments.js.map +1 -1
  220. package/dist/util/lines.d.ts.map +1 -1
  221. package/dist/util/lines.js +4 -1
  222. package/dist/util/lines.js.map +1 -1
  223. package/dist/util/member-access.d.ts +2 -0
  224. package/dist/util/member-access.d.ts.map +1 -1
  225. package/dist/util/member-access.js +18 -5
  226. package/dist/util/member-access.js.map +1 -1
  227. package/dist/util/paths.d.ts +4 -0
  228. package/dist/util/paths.d.ts.map +1 -1
  229. package/dist/util/paths.js +26 -0
  230. package/dist/util/paths.js.map +1 -1
  231. package/dist/util/project-files.js +1 -1
  232. package/dist/util/project-files.js.map +1 -1
  233. package/dist/util/resolution/cargo-targets.d.ts.map +1 -1
  234. package/dist/util/resolution/cargo-targets.js +17 -14
  235. package/dist/util/resolution/cargo-targets.js.map +1 -1
  236. package/dist/util/resolution/cpp.d.ts +6 -0
  237. package/dist/util/resolution/cpp.d.ts.map +1 -0
  238. package/dist/util/resolution/cpp.js +69 -0
  239. package/dist/util/resolution/cpp.js.map +1 -0
  240. package/dist/util/resolution/csharp.d.ts +4 -3
  241. package/dist/util/resolution/csharp.d.ts.map +1 -1
  242. package/dist/util/resolution/csharp.js +27 -113
  243. package/dist/util/resolution/csharp.js.map +1 -1
  244. package/dist/util/resolution/files.d.ts +6 -0
  245. package/dist/util/resolution/files.d.ts.map +1 -1
  246. package/dist/util/resolution/files.js +108 -4
  247. package/dist/util/resolution/files.js.map +1 -1
  248. package/dist/util/resolution/go.d.ts +1 -0
  249. package/dist/util/resolution/go.d.ts.map +1 -1
  250. package/dist/util/resolution/go.js +4 -3
  251. package/dist/util/resolution/go.js.map +1 -1
  252. package/dist/util/resolution/jvm.d.ts +3 -3
  253. package/dist/util/resolution/jvm.d.ts.map +1 -1
  254. package/dist/util/resolution/jvm.js +90 -54
  255. package/dist/util/resolution/jvm.js.map +1 -1
  256. package/dist/util/resolution/node.js +2 -2
  257. package/dist/util/resolution/node.js.map +1 -1
  258. package/dist/util/resolution/php-composer.d.ts.map +1 -1
  259. package/dist/util/resolution/php-composer.js +8 -7
  260. package/dist/util/resolution/php-composer.js.map +1 -1
  261. package/dist/util/resolution/php.d.ts.map +1 -1
  262. package/dist/util/resolution/php.js +30 -43
  263. package/dist/util/resolution/php.js.map +1 -1
  264. package/dist/util/resolution/project-symbols.d.ts +5 -7
  265. package/dist/util/resolution/project-symbols.d.ts.map +1 -1
  266. package/dist/util/resolution/project-symbols.js +23 -9
  267. package/dist/util/resolution/project-symbols.js.map +1 -1
  268. package/dist/util/resolution/python.d.ts.map +1 -1
  269. package/dist/util/resolution/python.js +107 -62
  270. package/dist/util/resolution/python.js.map +1 -1
  271. package/dist/util/resolution/rust.d.ts.map +1 -1
  272. package/dist/util/resolution/rust.js +45 -34
  273. package/dist/util/resolution/rust.js.map +1 -1
  274. package/dist/util/resolution/tsconfig.d.ts.map +1 -1
  275. package/dist/util/resolution/tsconfig.js +3 -9
  276. package/dist/util/resolution/tsconfig.js.map +1 -1
  277. package/dist/util/resolution-candidates.d.ts +5 -1
  278. package/dist/util/resolution-candidates.d.ts.map +1 -1
  279. package/dist/util/resolution-candidates.js +18 -0
  280. package/dist/util/resolution-candidates.js.map +1 -1
  281. package/dist/util/resolution.d.ts.map +1 -1
  282. package/dist/util/resolution.js +93 -49
  283. package/dist/util/resolution.js.map +1 -1
  284. package/dist/util/specifiers.d.ts +4 -10
  285. package/dist/util/specifiers.d.ts.map +1 -1
  286. package/dist/util/specifiers.js +213 -164
  287. package/dist/util/specifiers.js.map +1 -1
  288. package/dist/util/trivia-tables.d.ts +69 -0
  289. package/dist/util/trivia-tables.d.ts.map +1 -0
  290. package/dist/util/trivia-tables.js +186 -0
  291. package/dist/util/trivia-tables.js.map +1 -0
  292. package/dist/util/trivia.d.ts +24 -0
  293. package/dist/util/trivia.d.ts.map +1 -0
  294. package/dist/util/trivia.js +621 -0
  295. package/dist/util/trivia.js.map +1 -0
  296. package/dist/util.d.ts +1 -1
  297. package/dist/util.d.ts.map +1 -1
  298. package/dist/util.js +1 -1
  299. package/dist/util.js.map +1 -1
  300. package/dist/worker/native-extract-worker.d.ts.map +1 -1
  301. package/dist/worker/native-extract-worker.js +4 -4
  302. package/dist/worker/native-extract-worker.js.map +1 -1
  303. package/package.json +3 -3
@@ -1,13 +1,14 @@
1
- import { receiverConstructorExpression } from "../../indexer/navigation-goto.js";
1
+ import { readFileSync } from "node:fs";
2
2
  import { SymbolKind } from "../../indexer/types.js";
3
3
  import { sliceText } from "../../util/ast.js";
4
- import { getMemberAccessParts, getNavigationExpressionProperty, isMemberAccessNode, isMemberReferencePropertyIdentifier, } from "../../util/member-access.js";
5
- import { isIdentifierType } from "./ast.js";
4
+ import { XID_IDENTIFIER_SOURCE } from "../../util/identifiers.js";
5
+ import { getMemberAccessParts, getNavigationExpressionProperty, isMemberAccessNode, isMemberReferencePropertyIdentifier, isReceiverNameNode, } from "../../util/member-access.js";
6
6
  const THIS_SUPER_RECEIVERS = { own: ["this"], supertype: ["super"] };
7
7
  const SELF_RECEIVERS = { own: ["self"], supertype: ["super"] };
8
8
  const RECEIVER_KEYWORDS = {
9
9
  cpp: { own: ["this"], supertype: [] },
10
10
  csharp: { own: ["this"], supertype: ["base"] },
11
+ go: { own: [], supertype: [] },
11
12
  java: THIS_SUPER_RECEIVERS,
12
13
  js: THIS_SUPER_RECEIVERS,
13
14
  kotlin: THIS_SUPER_RECEIVERS,
@@ -15,12 +16,44 @@ const RECEIVER_KEYWORDS = {
15
16
  python: { own: ["self", "cls"], supertype: [] },
16
17
  ruby: { own: ["self"], supertype: [] },
17
18
  rust: { own: ["self", "Self"], supertype: [] },
18
- svelte: THIS_SUPER_RECEIVERS,
19
19
  swift: SELF_RECEIVERS,
20
20
  ts: THIS_SUPER_RECEIVERS,
21
21
  tsx: THIS_SUPER_RECEIVERS,
22
22
  zig: { own: ["self"], supertype: [] },
23
23
  };
24
+ const INSTANCE_RECEIVER_KEYWORDS = new Set(["this", "$this"]);
25
+ /** Every language that declares receiver keywords, guarded by the registry-consistency test. */
26
+ export const receiverKeywordLanguageIds = Object.keys(RECEIVER_KEYWORDS);
27
+ /** Languages whose grammar distinguishes static members from instance members. */
28
+ const STATIC_MEMBER_LANGUAGES = {
29
+ cpp: true,
30
+ csharp: true,
31
+ java: true,
32
+ js: true,
33
+ php: true,
34
+ swift: true,
35
+ ts: true,
36
+ tsx: true,
37
+ };
38
+ /** Every language with a static-member distinction, guarded by the registry-consistency test. */
39
+ export const staticMemberLanguageIds = Object.keys(STATIC_MEMBER_LANGUAGES);
40
+ /** Languages that emit proven receiver `calls` edges from the shared keyword table. */
41
+ export function supportsReceiverCallEdges(languageId) {
42
+ return RECEIVER_KEYWORDS[languageId] !== undefined;
43
+ }
44
+ /** Languages where goto and references require proven receivers and do not fall back to a bare name. */
45
+ export function supportsReceiverMemberNavigation(languageId) {
46
+ return RECEIVER_KEYWORDS[languageId] !== undefined;
47
+ }
48
+ export function isKeywordReceiver(languageId, receiverName) {
49
+ const keywords = RECEIVER_KEYWORDS[languageId];
50
+ if (!keywords)
51
+ return false;
52
+ return keywords.own.includes(receiverName) || keywords.supertype.includes(receiverName);
53
+ }
54
+ export function hasStaticMemberDistinction(languageId) {
55
+ return STATIC_MEMBER_LANGUAGES[languageId] !== undefined;
56
+ }
24
57
  /**
25
58
  * Call nodes that carry the receiver and the member name on the call node itself
26
59
  * instead of exposing a member-access callee.
@@ -39,7 +72,6 @@ const MEMBER_CONTAINER_TYPES = {
39
72
  class_definition: true,
40
73
  class_specifier: true,
41
74
  enum_declaration: true,
42
- extension_declaration: true,
43
75
  impl_item: true,
44
76
  interface_declaration: true,
45
77
  module: true,
@@ -91,11 +123,16 @@ const VALUE_BINDING_TYPES = {
91
123
  variable_declaration: true,
92
124
  variable_declarator: true,
93
125
  };
94
- /** Parameter lists whose identifier children are value bindings (Ruby has no wrapping param node). */
95
- const PARAMETER_LIST_TYPES = {
126
+ /**
127
+ * Parameter lists whose identifier children are value bindings (Ruby has no wrapping param node).
128
+ * Union of the receiver-call and call-compatibility lists so Kotlin `function_value_parameters`
129
+ * and Ruby `block_parameters` are both recognized.
130
+ */
131
+ export const PARAMETER_LIST_NODE_TYPES = {
96
132
  block_parameters: true,
97
133
  formal_parameters: true,
98
134
  function_parameter_clause: true,
135
+ function_value_parameters: true,
99
136
  lambda_parameters: true,
100
137
  method_parameters: true,
101
138
  parameter_list: true,
@@ -136,6 +173,65 @@ const BINDING_SCOPE_TYPES = {
136
173
  impl_item: true,
137
174
  translation_unit: true,
138
175
  };
176
+ const BINDING_CONTAINER_TYPES = new Set([
177
+ "program",
178
+ "compilation_unit",
179
+ "source_file",
180
+ "translation_unit",
181
+ "statement_block",
182
+ "block",
183
+ "compound_statement",
184
+ "function_body",
185
+ "body_statement",
186
+ "function_declaration",
187
+ "function_item",
188
+ "function_definition",
189
+ "function",
190
+ "function_expression",
191
+ "arrow_function",
192
+ "method_definition",
193
+ "method_declaration",
194
+ "method",
195
+ "impl_item",
196
+ ]);
197
+ const BINDING_DECLARATION_TYPES = new Set([
198
+ "variable_declarator",
199
+ "variable_declaration",
200
+ "let_declaration",
201
+ "assignment",
202
+ "assignment_expression",
203
+ "assignment_statement",
204
+ "formal_parameter",
205
+ "required_parameter",
206
+ "optional_parameter",
207
+ "simple_parameter",
208
+ "parameter",
209
+ "parameter_declaration",
210
+ "typed_parameter",
211
+ "short_var_declaration",
212
+ "var_spec",
213
+ "property_declaration",
214
+ "local_variable_declaration",
215
+ "local_declaration_statement",
216
+ ]);
217
+ const RUBY_CONSTANT_SOURCE = String.raw `(?=\p{Lu})${XID_IDENTIFIER_SOURCE}`;
218
+ /** Proven construction forms that name a receiver's type, keyed by language. */
219
+ const LANGUAGE_CONSTRUCTION_FORMS = {
220
+ cpp: { newExpression: true },
221
+ csharp: { newExpression: true },
222
+ go: { compositeLiteral: true },
223
+ java: { newExpression: true },
224
+ js: { newExpression: true },
225
+ kotlin: { capitalizedCall: true },
226
+ php: { newExpression: true },
227
+ python: { capitalizedCall: true },
228
+ ruby: { rubyNew: true },
229
+ rust: { capitalizedCall: true, rustUnitStruct: true },
230
+ swift: { capitalizedCall: true },
231
+ ts: { newExpression: true },
232
+ tsx: { newExpression: true },
233
+ zig: { compositeLiteral: true },
234
+ };
139
235
  /** Guards against a cyclic or pathological declared hierarchy. */
140
236
  const MAX_SUPERTYPE_DEPTH = 16;
141
237
  /**
@@ -180,8 +276,7 @@ export function receiverCallAccess(sup, callNode, callee) {
180
276
  */
181
277
  function bindingIdentifier(node, sup) {
182
278
  const named = node.childForFieldName("name");
183
- if (named &&
184
- (isIdentifierType(sup, named.type) || named.type === "identifier" || named.type === "field_identifier")) {
279
+ if (named && (isReceiverNameNode(sup, named.type) || named.type === "field_identifier")) {
185
280
  return named;
186
281
  }
187
282
  if (node.type === "assignment" ||
@@ -189,16 +284,16 @@ function bindingIdentifier(node, sup) {
189
284
  node.type === "assignment_statement" ||
190
285
  node.type === "short_var_declaration") {
191
286
  const left = node.childForFieldName("left") ?? node.child(0);
192
- if (left && (isIdentifierType(sup, left.type) || left.type === "identifier"))
287
+ if (left && isReceiverNameNode(sup, left.type))
193
288
  return left;
194
- if (left?.type === "expression_list") {
195
- return (left.namedChildren.find((child) => child.type === "identifier" || isIdentifierType(sup, child.type)) ?? null);
289
+ if (left?.type === "expression_list" || left?.type === "pattern") {
290
+ return left.namedChildren.find((child) => isReceiverNameNode(sup, child.type)) ?? null;
196
291
  }
197
292
  return null;
198
293
  }
199
294
  let current = node.childForFieldName("declarator");
200
295
  while (current) {
201
- if (current.type === "identifier" || current.type === "field_identifier" || isIdentifierType(sup, current.type)) {
296
+ if (isReceiverNameNode(sup, current.type) || current.type === "field_identifier") {
202
297
  return current;
203
298
  }
204
299
  const nested = current.childForFieldName("declarator");
@@ -208,8 +303,15 @@ function bindingIdentifier(node, sup) {
208
303
  }
209
304
  return (current.namedChildren.find((child) => child.type === "identifier" || child.type === "field_identifier") ?? null);
210
305
  }
211
- const identifiers = node.namedChildren.filter((child) => child.type === "identifier" || child.type === "field_identifier");
212
- return identifiers.length > 0 ? identifiers[identifiers.length - 1] : null;
306
+ const identifiers = node.namedChildren.filter((child) => isReceiverNameNode(sup, child.type) || child.type === "field_identifier");
307
+ if (identifiers.length === 0)
308
+ return null;
309
+ // `let name = Type;` / `var name = Type{}` put the binding first and the type second.
310
+ // C++ parameters keep the name last, after the type.
311
+ if (node.type === "let_declaration" || node.type === "variable_declaration" || node.type === "variable_declarator") {
312
+ return identifiers[0];
313
+ }
314
+ return identifiers[identifiers.length - 1];
213
315
  }
214
316
  /**
215
317
  * Whether a scope encloses `receiver` and binds `receiverName` as a value before it.
@@ -222,7 +324,7 @@ function bindsLocalValue(receiver, receiverName, source, sup) {
222
324
  return false;
223
325
  if (node !== receiver && BINDING_SCOPE_TYPES[node.type] && !containsIndex(node, receiver.startIndex))
224
326
  return false;
225
- if (PARAMETER_LIST_TYPES[node.type]) {
327
+ if (PARAMETER_LIST_NODE_TYPES[node.type]) {
226
328
  for (const child of node.namedChildren) {
227
329
  if (child.startIndex >= receiver.startIndex)
228
330
  continue;
@@ -230,7 +332,7 @@ function bindsLocalValue(receiver, receiverName, source, sup) {
230
332
  // name in ERROR. Treat recovered parameter names as value bindings.
231
333
  const names = child.type === "ERROR" ? child.namedChildren : [child];
232
334
  for (const name of names) {
233
- if ((isIdentifierType(sup, name.type) || name.type === "identifier" || name.type === "constant") &&
335
+ if ((isReceiverNameNode(sup, name.type) || name.type === "identifier" || name.type === "constant") &&
234
336
  sliceText(name, source) === receiverName) {
235
337
  return true;
236
338
  }
@@ -253,6 +355,326 @@ function bindsLocalValue(receiver, receiverName, source, sup) {
253
355
  function containsIndex(node, index) {
254
356
  return node.startIndex <= index && node.endIndex >= index;
255
357
  }
358
+ function constructorNameNode(node, sup) {
359
+ const constructor = node.childForFieldName("constructor") ?? node.child(0);
360
+ if (constructor && isReceiverNameNode(sup, constructor.type)) {
361
+ return constructor;
362
+ }
363
+ for (const child of node.namedChildren) {
364
+ if (isReceiverNameNode(sup, child.type) || child.type === "type_identifier" || child.type === "constant") {
365
+ return child;
366
+ }
367
+ }
368
+ return null;
369
+ }
370
+ function rubyNewReceiverNameNode(node, source, sup) {
371
+ if (!new RegExp(String.raw `^(?:${RUBY_CONSTANT_SOURCE})\.new$`, "u").test(sliceText(node, source)))
372
+ return null;
373
+ return node.namedChildren.find((child) => isReceiverNameNode(sup, child.type) || child.type === "constant") ?? null;
374
+ }
375
+ export function unwrapNamedType(node, sup) {
376
+ let current = node;
377
+ while (current) {
378
+ if (current.type === "type_annotation" ||
379
+ current.type === "named_type" ||
380
+ current.type === "user_type" ||
381
+ current.type === "type" ||
382
+ current.type === "parenthesized_type" ||
383
+ current.type === "pointer_type" ||
384
+ current.type === "reference_type" ||
385
+ current.type === "optional_type" ||
386
+ current.type === "nullable_type") {
387
+ current = current.childForFieldName("type") ?? current.namedChildren[0] ?? null;
388
+ continue;
389
+ }
390
+ if (current.type === "generic_type" || current.type === "generic_name") {
391
+ current = current.childForFieldName("type") ?? current.namedChildren[0] ?? null;
392
+ continue;
393
+ }
394
+ break;
395
+ }
396
+ if (!current)
397
+ return null;
398
+ if (isReceiverNameNode(sup, current.type) || current.type === "type_identifier" || current.type === "name") {
399
+ return current;
400
+ }
401
+ return null;
402
+ }
403
+ function capitalizedCallTypeName(expr, source, sup) {
404
+ if (expr.type !== "call" && expr.type !== "call_expression")
405
+ return null;
406
+ const callee = expr.childForFieldName("function") ?? expr.namedChildren[0] ?? null;
407
+ if (!callee || !isReceiverNameNode(sup, callee.type))
408
+ return null;
409
+ const name = sliceText(callee, source);
410
+ if (!name)
411
+ return null;
412
+ const first = name[0];
413
+ if (first !== first.toUpperCase())
414
+ return null;
415
+ return callee;
416
+ }
417
+ function compositeLiteralTypeName(expr, source, sup) {
418
+ let current = expr;
419
+ if (current.type === "unary_expression") {
420
+ const operand = current.childForFieldName("operand");
421
+ if (!operand)
422
+ return null;
423
+ const operator = current.childForFieldName("operator");
424
+ let isAddr = sliceText(current, source).startsWith("&");
425
+ if (operator)
426
+ isAddr = sliceText(operator, source) === "&";
427
+ if (!isAddr)
428
+ return null;
429
+ current = operand;
430
+ }
431
+ if (current.type !== "composite_literal" && current.type !== "struct_initializer")
432
+ return null;
433
+ const typeNode = current.childForFieldName("type") ??
434
+ current.namedChildren.find((child) => child.type === "type_identifier" || child.type === "identifier") ??
435
+ null;
436
+ return typeNode ? (unwrapNamedType(typeNode, sup) ?? typeNode) : null;
437
+ }
438
+ /**
439
+ * Resolves the node naming the type a construction expression denotes, or null
440
+ * when the expression is not a proven constructor form.
441
+ */
442
+ export function constructionTypeName(expr, source, sup) {
443
+ const forms = LANGUAGE_CONSTRUCTION_FORMS[sup.id];
444
+ if (!forms)
445
+ return null;
446
+ if (forms.newExpression && (expr.type === "new_expression" || expr.type === "object_creation_expression")) {
447
+ return constructorNameNode(expr, sup);
448
+ }
449
+ if (forms.rubyNew && expr.type === "call") {
450
+ const rubyConstructor = rubyNewReceiverNameNode(expr, source, sup);
451
+ if (rubyConstructor)
452
+ return rubyConstructor;
453
+ }
454
+ if (forms.compositeLiteral) {
455
+ const composite = compositeLiteralTypeName(expr, source, sup);
456
+ if (composite)
457
+ return composite;
458
+ }
459
+ if (forms.capitalizedCall) {
460
+ const fromCall = capitalizedCallTypeName(expr, source, sup);
461
+ if (fromCall)
462
+ return fromCall;
463
+ }
464
+ // Rust unit structs are constructed by the type name itself: `let service = Service;`.
465
+ if (forms.rustUnitStruct && isReceiverNameNode(sup, expr.type)) {
466
+ const name = sliceText(expr, source);
467
+ const first = name[0];
468
+ if (first && first === first.toUpperCase() && first !== first.toLowerCase())
469
+ return expr;
470
+ }
471
+ return null;
472
+ }
473
+ function bindingValueExpression(node) {
474
+ const named = node.childForFieldName("value") ?? node.childForFieldName("right") ?? node.childForFieldName("default_value");
475
+ if (named) {
476
+ if (named.type === "expression_list")
477
+ return named.namedChildren[0] ?? null;
478
+ return named;
479
+ }
480
+ const expressionTypes = new Set([
481
+ "new_expression",
482
+ "object_creation_expression",
483
+ "call",
484
+ "call_expression",
485
+ "composite_literal",
486
+ "struct_initializer",
487
+ "unary_expression",
488
+ ]);
489
+ return node.namedChildren.find((child) => expressionTypes.has(child.type)) ?? null;
490
+ }
491
+ function declaredTypeNameNode(node, sup) {
492
+ const typeField = node.childForFieldName("type");
493
+ if (typeField)
494
+ return unwrapNamedType(typeField, sup);
495
+ const typedChild = node.namedChildren.find((child) => child.type === "named_type" ||
496
+ child.type === "user_type" ||
497
+ child.type === "type_annotation" ||
498
+ child.type === "type");
499
+ if (typedChild)
500
+ return unwrapNamedType(typedChild, sup);
501
+ const ids = node.namedChildren.filter((child) => isReceiverNameNode(sup, child.type) || child.type === "type_identifier" || child.type === "name");
502
+ if (ids.length >= 2)
503
+ return unwrapNamedType(ids[0], sup);
504
+ return null;
505
+ }
506
+ function goBindingNameNodes(node) {
507
+ if (node.type === "short_var_declaration") {
508
+ const left = node.childForFieldName("left");
509
+ return (left?.namedChildren ?? []).filter((child) => child.type === "identifier");
510
+ }
511
+ if (node.type === "var_spec") {
512
+ return (node.namedChildren ?? []).filter((child) => child.type === "identifier");
513
+ }
514
+ return [];
515
+ }
516
+ function bindingDeclaresReceiverName(node, receiverName, source, sup) {
517
+ if (sup.id === "go") {
518
+ const goNames = goBindingNameNodes(node);
519
+ if (goNames.some((name) => sliceText(name, source) === receiverName))
520
+ return true;
521
+ }
522
+ const name = bindingIdentifier(node, sup);
523
+ if (name && sliceText(name, source) === receiverName)
524
+ return true;
525
+ if (node.type === "property_declaration") {
526
+ return node.namedChildren.some((child) => {
527
+ if (child.type === "variable_declaration" || child.type === "pattern") {
528
+ return child.namedChildren.some((inner) => isReceiverNameNode(sup, inner.type) && sliceText(inner, source) === receiverName);
529
+ }
530
+ return isReceiverNameNode(sup, child.type) && sliceText(child, source) === receiverName;
531
+ });
532
+ }
533
+ return false;
534
+ }
535
+ function constructionTypeFromBinding(node, receiverName, source, sup) {
536
+ if (sup.id === "go" && (node.type === "short_var_declaration" || node.type === "var_spec")) {
537
+ return constructorFromGoBinding(node, receiverName, source, sup) ?? declaredTypeNameNode(node, sup);
538
+ }
539
+ const value = bindingValueExpression(node);
540
+ if (value) {
541
+ const fromValue = constructionTypeName(value, source, sup);
542
+ if (fromValue)
543
+ return fromValue;
544
+ }
545
+ return declaredTypeNameNode(node, sup);
546
+ }
547
+ function constructorFromGoBinding(node, receiverName, source, sup) {
548
+ if (node.type === "short_var_declaration") {
549
+ const left = node.childForFieldName("left");
550
+ const right = node.childForFieldName("right");
551
+ if (!left || !right)
552
+ return null;
553
+ const names = (left.namedChildren ?? []).filter((child) => child.type === "identifier");
554
+ const values = right.namedChildren ?? [];
555
+ const index = names.findIndex((name) => sliceText(name, source) === receiverName);
556
+ if (index < 0)
557
+ return null;
558
+ const value = values[index] ?? null;
559
+ return value ? constructionTypeName(value, source, sup) : null;
560
+ }
561
+ if (node.type !== "var_spec")
562
+ return null;
563
+ const value = node.childForFieldName("value");
564
+ if (value) {
565
+ const first = value.type === "expression_list" ? (value.namedChildren[0] ?? null) : value;
566
+ const fromValue = first ? constructionTypeName(first, source, sup) : null;
567
+ if (fromValue)
568
+ return fromValue;
569
+ }
570
+ const typeNode = node.childForFieldName("type");
571
+ return typeNode ? unwrapNamedType(typeNode, sup) : null;
572
+ }
573
+ function bindingProof(node, receiverName, source, sup) {
574
+ if (!BINDING_DECLARATION_TYPES.has(node.type))
575
+ return { status: "none" };
576
+ if (!bindingDeclaresReceiverName(node, receiverName, source, sup))
577
+ return { status: "none" };
578
+ const typeNode = constructionTypeFromBinding(node, receiverName, source, sup);
579
+ if (typeNode)
580
+ return { status: "type", node: typeNode };
581
+ return { status: "unproven" };
582
+ }
583
+ function isSkippableBindingContainer(node, receiver) {
584
+ return BINDING_CONTAINER_TYPES.has(node.type) && !containsIndex(node, receiver.startIndex);
585
+ }
586
+ function findPriorConstructorInContainer(node, receiver, receiverName, source, sup) {
587
+ let constructor = null;
588
+ let sawUnproven = false;
589
+ const visit = (current) => {
590
+ if (current.startIndex >= receiver.startIndex)
591
+ return true;
592
+ if (current !== node && isSkippableBindingContainer(current, receiver))
593
+ return true;
594
+ const proof = bindingProof(current, receiverName, source, sup);
595
+ if (proof.status === "unproven") {
596
+ if (constructor) {
597
+ constructor = null;
598
+ return false;
599
+ }
600
+ sawUnproven = true;
601
+ return true;
602
+ }
603
+ if (proof.status === "type") {
604
+ if (sawUnproven) {
605
+ constructor = null;
606
+ return false;
607
+ }
608
+ if (constructor && sliceText(constructor, source) !== sliceText(proof.node, source)) {
609
+ constructor = null;
610
+ return false;
611
+ }
612
+ constructor = proof.node;
613
+ return true;
614
+ }
615
+ for (const child of current.namedChildren) {
616
+ if (!visit(child))
617
+ return false;
618
+ }
619
+ return true;
620
+ };
621
+ visit(node);
622
+ return constructor;
623
+ }
624
+ function bindingContainerDeclaresNameBefore(node, receiver, receiverName, source, sup) {
625
+ const visit = (current) => {
626
+ if (current.startIndex >= receiver.startIndex)
627
+ return false;
628
+ if (current !== node && isSkippableBindingContainer(current, receiver))
629
+ return false;
630
+ if (BINDING_DECLARATION_TYPES.has(current.type) &&
631
+ bindingDeclaresReceiverName(current, receiverName, source, sup)) {
632
+ return true;
633
+ }
634
+ for (const child of current.namedChildren) {
635
+ if (visit(child))
636
+ return true;
637
+ }
638
+ return false;
639
+ };
640
+ return visit(node);
641
+ }
642
+ function rootOf(node) {
643
+ let current = node;
644
+ while (current.parent)
645
+ current = current.parent;
646
+ return current;
647
+ }
648
+ function findVisiblePriorConstructor(receiver, receiverName, source, sup) {
649
+ let current = receiver;
650
+ while (current) {
651
+ if (BINDING_CONTAINER_TYPES.has(current.type)) {
652
+ const constructor = findPriorConstructorInContainer(current, receiver, receiverName, source, sup);
653
+ if (constructor || bindingContainerDeclaresNameBefore(current, receiver, receiverName, source, sup)) {
654
+ return constructor;
655
+ }
656
+ }
657
+ current = current.parent;
658
+ }
659
+ return findPriorConstructorInContainer(rootOf(receiver), receiver, receiverName, source, sup);
660
+ }
661
+ /**
662
+ * Resolves the node naming the type a receiver expression was constructed from, or
663
+ * null when no constructor is proven for it. Shared with detailed symbol-graph call
664
+ * extraction so `goto` and resolved `calls` edges accept the same receiver forms.
665
+ */
666
+ export function receiverConstructorExpression(obj, source, sup) {
667
+ const direct = constructionTypeName(obj, source, sup);
668
+ if (direct)
669
+ return direct;
670
+ if (!isReceiverNameNode(sup, obj.type))
671
+ return null;
672
+ const receiverName = sliceText(obj, source);
673
+ return findVisiblePriorConstructor(obj, receiverName, source, sup);
674
+ }
675
+ function ownTypeMemberScope(receiverName) {
676
+ return INSTANCE_RECEIVER_KEYWORDS.has(receiverName) ? "instance" : "any";
677
+ }
256
678
  /**
257
679
  * Classifies a receiver as the declaring type, a supertype, or a named/constructed type.
258
680
  * Returns null when the receiver cannot be proven.
@@ -264,10 +686,10 @@ export function classifyReceiver(sup, receiver, source, proofCache, cacheScope,
264
686
  if (!text)
265
687
  return null;
266
688
  if (keywords?.own.includes(text))
267
- return { kind: "own-type" };
689
+ return { kind: "own-type", memberScope: ownTypeMemberScope(text) };
268
690
  if (keywords?.supertype.includes(text))
269
- return { kind: "supertype" };
270
- const receiverIsName = isIdentifierType(sup, receiver.type) || receiver.type === "type_identifier" || receiver.type === "constant";
691
+ return { kind: "supertype", memberScope: "any" };
692
+ const receiverIsName = isReceiverNameNode(sup, receiver.type);
271
693
  const cacheKey = `${cacheScope}\u0000${text}`;
272
694
  let proof = proofCache.get(cacheKey);
273
695
  if (!proof) {
@@ -278,8 +700,13 @@ export function classifyReceiver(sup, receiver, source, proofCache, cacheScope,
278
700
  };
279
701
  proofCache.set(cacheKey, proof);
280
702
  }
281
- if (proof.constructed)
282
- return { kind: "named-type", typeName: sliceText(proof.constructed, source) };
703
+ if (proof.constructed) {
704
+ return {
705
+ kind: "named-type",
706
+ typeName: sliceText(proof.constructed, source),
707
+ memberScope: hasStaticMemberDistinction(sup.id) ? "instance" : "any",
708
+ };
709
+ }
283
710
  if (!receiverIsName)
284
711
  return null;
285
712
  // A name bound by a local or parameter is a value, not a type. Without this guard
@@ -294,7 +721,11 @@ export function classifyReceiver(sup, receiver, source, proofCache, cacheScope,
294
721
  const typeScoped = TYPE_SCOPED_ACCESS_TYPES[accessNode.type] === true || between.includes("::");
295
722
  if (receiver.type !== "type_identifier" && !typeScoped)
296
723
  return null;
297
- return { kind: "named-type", typeName: text };
724
+ return {
725
+ kind: "named-type",
726
+ typeName: text,
727
+ memberScope: hasStaticMemberDistinction(sup.id) && typeScoped ? "static" : "any",
728
+ };
298
729
  }
299
730
  /** Whether a resolved definition can declare callable members. */
300
731
  export function declaresMembers(def) {
@@ -321,6 +752,69 @@ export function callArgumentCount(callNode) {
321
752
  return 0;
322
753
  return (argumentNode.namedChildren ?? []).filter((argument) => argument.type !== "comment").length;
323
754
  }
755
+ function parseDefNodeId(id) {
756
+ const indexSep = id.lastIndexOf("::");
757
+ if (indexSep <= 0)
758
+ return null;
759
+ const index = Number(id.slice(indexSep + 2));
760
+ if (!Number.isFinite(index))
761
+ return null;
762
+ const rest = id.slice(0, indexSep);
763
+ const nameSep = rest.lastIndexOf("::");
764
+ if (nameSep <= 0)
765
+ return null;
766
+ return { file: rest.slice(0, nameSep), name: rest.slice(nameSep + 2), index };
767
+ }
768
+ function loadSource(file, cache) {
769
+ const cached = cache.get(file);
770
+ if (cached !== undefined)
771
+ return cached;
772
+ try {
773
+ const source = readFileSync(file, "utf8");
774
+ cache.set(file, source);
775
+ return source;
776
+ }
777
+ catch {
778
+ cache.set(file, "");
779
+ return "";
780
+ }
781
+ }
782
+ function memberIdLooksStatic(memberId, sourceCache) {
783
+ const parsed = parseDefNodeId(memberId);
784
+ if (!parsed)
785
+ return false;
786
+ const source = loadSource(parsed.file, sourceCache);
787
+ if (!source)
788
+ return false;
789
+ const beforeBrace = source.lastIndexOf("{", parsed.index);
790
+ const beforeSemi = source.lastIndexOf(";", parsed.index);
791
+ const beforeClose = source.lastIndexOf("}", parsed.index);
792
+ const declStart = Math.max(beforeBrace, beforeSemi, beforeClose);
793
+ const prefix = source.slice(declStart + 1, parsed.index);
794
+ return /(?:^|[^\w$])static(?:$|[^\w$])/.test(prefix) || /(?:^|[^\w$])def\s+self\s*\./.test(prefix);
795
+ }
796
+ function inferCallMemberScope(site, sourceCache) {
797
+ const source = loadSource(site.file, sourceCache);
798
+ if (!source)
799
+ return "any";
800
+ const start = site.range.start.index ?? 0;
801
+ const before = source.slice(Math.max(0, start - 120), start);
802
+ if (/\b(?:self|static|parent|super|base)\s*::\s*$/i.test(before))
803
+ return "any";
804
+ if (/::\s*$/.test(before))
805
+ return "static";
806
+ if (/\?->\s*$/.test(before) || /->\s*$/.test(before))
807
+ return "instance";
808
+ if (/\)\s*\.\s*$/.test(before))
809
+ return "instance";
810
+ // HeritageEdges does not yet copy classifyReceiver.memberScope onto candidates.
811
+ // A lowercase dotted receiver is an instance value (`c.StaticMethod()`), not a type.
812
+ if (/(?:^|[^A-Za-z0-9_$])[a-z_][A-Za-z0-9_]*\s*\.\s*$/.test(before))
813
+ return "instance";
814
+ if (/(?:^|[^A-Za-z0-9_$])[A-Z][A-Za-z0-9_]*\s*\.\s*$/.test(before))
815
+ return "static";
816
+ return "any";
817
+ }
324
818
  /**
325
819
  * Records proven `calls` edges for deferred receiver invocations.
326
820
  * Ambiguous names at a level stop the walk.
@@ -358,14 +852,16 @@ export function emitReceiverCallEdges(graph, candidates, recordEdge) {
358
852
  return supertypesByOwner.get(ownerId) ?? [];
359
853
  return (classAncestorsByOwner.get(ownerId) ?? []).filter((id) => graph.nodes.get(id)?.kind === "class");
360
854
  };
855
+ const sourceCache = new Map();
361
856
  for (const candidate of candidates) {
362
857
  const owner = candidate.ownerId ?? ownerByMember.get(candidate.callerId);
363
858
  if (!owner)
364
859
  continue;
860
+ const memberScope = candidate.memberScope ?? inferCallMemberScope(candidate.site, sourceCache);
365
861
  let level = candidate.viaSupertypes ? nextOwners(owner, true) : [owner];
366
862
  const visited = new Set(level);
367
863
  for (let depth = 0; depth < MAX_SUPERTYPE_DEPTH && level.length; depth += 1) {
368
- const lookup = provenMemberTarget(graph, membersByOwner, level, candidate);
864
+ const lookup = provenMemberTarget(graph, membersByOwner, level, candidate, memberScope, sourceCache);
369
865
  if (lookup.status === "unique") {
370
866
  recordEdge(candidate.callerId, lookup.memberId, "calls", candidate.site);
371
867
  break;
@@ -391,13 +887,17 @@ export function emitReceiverCallEdges(graph, candidates, recordEdge) {
391
887
  * `ambiguous` means this depth matched the name but could not prove one member,
392
888
  * including arity ambiguity, and the walk must stop.
393
889
  */
394
- function provenMemberTarget(graph, membersByOwner, owners, candidate) {
890
+ function provenMemberTarget(graph, membersByOwner, owners, candidate, memberScope, sourceCache) {
395
891
  const matches = new Set();
396
892
  for (const ownerId of owners) {
397
893
  for (const memberId of membersByOwner.get(ownerId) ?? []) {
398
894
  const node = graph.nodes.get(memberId);
399
895
  if (!node || node.kind !== "function" || node.name !== candidate.memberName)
400
896
  continue;
897
+ if (memberScope === "static" && !memberIdLooksStatic(memberId, sourceCache))
898
+ continue;
899
+ if (memberScope === "instance" && memberIdLooksStatic(memberId, sourceCache))
900
+ continue;
401
901
  matches.add(memberId);
402
902
  }
403
903
  }