gitnexus 1.6.3-rc.8 → 1.6.3

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 (285) hide show
  1. package/README.md +21 -5
  2. package/dist/_shared/graph/types.d.ts +16 -0
  3. package/dist/_shared/graph/types.d.ts.map +1 -1
  4. package/dist/_shared/index.d.ts +20 -2
  5. package/dist/_shared/index.d.ts.map +1 -1
  6. package/dist/_shared/index.js +11 -0
  7. package/dist/_shared/index.js.map +1 -1
  8. package/dist/_shared/scope-resolution/def-index.js +2 -2
  9. package/dist/_shared/scope-resolution/def-index.js.map +1 -1
  10. package/dist/_shared/scope-resolution/method-dispatch-index.d.ts +8 -0
  11. package/dist/_shared/scope-resolution/method-dispatch-index.d.ts.map +1 -1
  12. package/dist/_shared/scope-resolution/method-dispatch-index.js +2 -2
  13. package/dist/_shared/scope-resolution/method-dispatch-index.js.map +1 -1
  14. package/dist/_shared/scope-resolution/module-scope-index.d.ts +8 -0
  15. package/dist/_shared/scope-resolution/module-scope-index.d.ts.map +1 -1
  16. package/dist/_shared/scope-resolution/module-scope-index.js +10 -2
  17. package/dist/_shared/scope-resolution/module-scope-index.js.map +1 -1
  18. package/dist/_shared/scope-resolution/parsed-file.d.ts +76 -0
  19. package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -0
  20. package/dist/_shared/scope-resolution/parsed-file.js +54 -0
  21. package/dist/_shared/scope-resolution/parsed-file.js.map +1 -0
  22. package/dist/_shared/scope-resolution/position-index.d.ts +12 -0
  23. package/dist/_shared/scope-resolution/position-index.d.ts.map +1 -1
  24. package/dist/_shared/scope-resolution/position-index.js +2 -2
  25. package/dist/_shared/scope-resolution/position-index.js.map +1 -1
  26. package/dist/_shared/scope-resolution/qualified-name-index.js +2 -2
  27. package/dist/_shared/scope-resolution/qualified-name-index.js.map +1 -1
  28. package/dist/_shared/scope-resolution/reference-site.d.ts +75 -0
  29. package/dist/_shared/scope-resolution/reference-site.d.ts.map +1 -0
  30. package/dist/_shared/scope-resolution/reference-site.js +24 -0
  31. package/dist/_shared/scope-resolution/reference-site.js.map +1 -0
  32. package/dist/_shared/scope-resolution/registries/class-registry.d.ts +27 -0
  33. package/dist/_shared/scope-resolution/registries/class-registry.d.ts.map +1 -0
  34. package/dist/_shared/scope-resolution/registries/class-registry.js +30 -0
  35. package/dist/_shared/scope-resolution/registries/class-registry.js.map +1 -0
  36. package/dist/_shared/scope-resolution/registries/context.d.ts +69 -0
  37. package/dist/_shared/scope-resolution/registries/context.d.ts.map +1 -0
  38. package/dist/_shared/scope-resolution/registries/context.js +44 -0
  39. package/dist/_shared/scope-resolution/registries/context.js.map +1 -0
  40. package/dist/_shared/scope-resolution/registries/evidence.d.ts +56 -0
  41. package/dist/_shared/scope-resolution/registries/evidence.d.ts.map +1 -0
  42. package/dist/_shared/scope-resolution/registries/evidence.js +150 -0
  43. package/dist/_shared/scope-resolution/registries/evidence.js.map +1 -0
  44. package/dist/_shared/scope-resolution/registries/field-registry.d.ts +26 -0
  45. package/dist/_shared/scope-resolution/registries/field-registry.d.ts.map +1 -0
  46. package/dist/_shared/scope-resolution/registries/field-registry.js +31 -0
  47. package/dist/_shared/scope-resolution/registries/field-registry.js.map +1 -0
  48. package/dist/_shared/scope-resolution/registries/lookup-core.d.ts +81 -0
  49. package/dist/_shared/scope-resolution/registries/lookup-core.d.ts.map +1 -0
  50. package/dist/_shared/scope-resolution/registries/lookup-core.js +332 -0
  51. package/dist/_shared/scope-resolution/registries/lookup-core.js.map +1 -0
  52. package/dist/_shared/scope-resolution/registries/lookup-qualified.d.ts +33 -0
  53. package/dist/_shared/scope-resolution/registries/lookup-qualified.d.ts.map +1 -0
  54. package/dist/_shared/scope-resolution/registries/lookup-qualified.js +56 -0
  55. package/dist/_shared/scope-resolution/registries/lookup-qualified.js.map +1 -0
  56. package/dist/_shared/scope-resolution/registries/method-registry.d.ts +36 -0
  57. package/dist/_shared/scope-resolution/registries/method-registry.d.ts.map +1 -0
  58. package/dist/_shared/scope-resolution/registries/method-registry.js +32 -0
  59. package/dist/_shared/scope-resolution/registries/method-registry.js.map +1 -0
  60. package/dist/_shared/scope-resolution/registries/tie-breaks.d.ts +43 -0
  61. package/dist/_shared/scope-resolution/registries/tie-breaks.d.ts.map +1 -0
  62. package/dist/_shared/scope-resolution/registries/tie-breaks.js +60 -0
  63. package/dist/_shared/scope-resolution/registries/tie-breaks.js.map +1 -0
  64. package/dist/_shared/scope-resolution/resolve-type-ref.d.ts +1 -10
  65. package/dist/_shared/scope-resolution/resolve-type-ref.d.ts.map +1 -1
  66. package/dist/_shared/scope-resolution/resolve-type-ref.js +6 -0
  67. package/dist/_shared/scope-resolution/resolve-type-ref.js.map +1 -1
  68. package/dist/_shared/scope-resolution/scope-tree.d.ts +4 -4
  69. package/dist/_shared/scope-resolution/scope-tree.d.ts.map +1 -1
  70. package/dist/_shared/scope-resolution/scope-tree.js +3 -2
  71. package/dist/_shared/scope-resolution/scope-tree.js.map +1 -1
  72. package/dist/_shared/scope-resolution/shadow/aggregate.d.ts +6 -2
  73. package/dist/_shared/scope-resolution/shadow/aggregate.d.ts.map +1 -1
  74. package/dist/_shared/scope-resolution/shadow/aggregate.js +5 -0
  75. package/dist/_shared/scope-resolution/shadow/aggregate.js.map +1 -1
  76. package/dist/_shared/scope-resolution/types.d.ts +11 -0
  77. package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
  78. package/dist/cli/ai-context.js +35 -4
  79. package/dist/cli/analyze.d.ts +27 -0
  80. package/dist/cli/analyze.js +31 -1
  81. package/dist/cli/clean.js +19 -1
  82. package/dist/cli/group.js +73 -0
  83. package/dist/cli/index-repo.js +8 -1
  84. package/dist/cli/index.js +26 -1
  85. package/dist/cli/list.js +11 -1
  86. package/dist/cli/remove.d.ts +30 -0
  87. package/dist/cli/remove.js +99 -0
  88. package/dist/cli/setup.js +185 -57
  89. package/dist/cli/tool.d.ts +5 -0
  90. package/dist/cli/tool.js +42 -0
  91. package/dist/config/ignore-service.d.ts +9 -0
  92. package/dist/config/ignore-service.js +80 -13
  93. package/dist/core/embedding-mode.d.ts +30 -0
  94. package/dist/core/embedding-mode.js +30 -0
  95. package/dist/core/embeddings/ast-utils.js +22 -22
  96. package/dist/core/embeddings/chunker.js +30 -25
  97. package/dist/core/embeddings/embedding-pipeline.d.ts +6 -0
  98. package/dist/core/embeddings/embedding-pipeline.js +15 -6
  99. package/dist/core/embeddings/text-generator.d.ts +1 -1
  100. package/dist/core/embeddings/text-generator.js +33 -24
  101. package/dist/core/embeddings/types.d.ts +43 -1
  102. package/dist/core/embeddings/types.js +101 -29
  103. package/dist/core/git-staleness.d.ts +18 -0
  104. package/dist/core/git-staleness.js +108 -0
  105. package/dist/core/graph/graph.js +115 -20
  106. package/dist/core/graph/types.d.ts +12 -1
  107. package/dist/core/group/config-parser.d.ts +4 -0
  108. package/dist/core/group/config-parser.js +18 -1
  109. package/dist/core/group/cross-impact.d.ts +41 -0
  110. package/dist/core/group/cross-impact.js +441 -0
  111. package/dist/core/group/extractors/http-patterns/php.js +126 -18
  112. package/dist/core/group/group-path-utils.d.ts +17 -0
  113. package/dist/core/group/group-path-utils.js +40 -0
  114. package/dist/core/group/resolve-at-member.d.ts +10 -0
  115. package/dist/core/group/resolve-at-member.js +31 -0
  116. package/dist/core/group/service.d.ts +9 -0
  117. package/dist/core/group/service.js +259 -25
  118. package/dist/core/group/types.d.ts +30 -0
  119. package/dist/core/ingestion/ast-cache.d.ts +16 -1
  120. package/dist/core/ingestion/ast-cache.js +14 -2
  121. package/dist/core/ingestion/call-processor.js +9 -0
  122. package/dist/core/ingestion/emit-references.d.ts +88 -0
  123. package/dist/core/ingestion/emit-references.js +229 -0
  124. package/dist/core/ingestion/filesystem-walker.js +6 -4
  125. package/dist/core/ingestion/finalize-orchestrator.d.ts +63 -0
  126. package/dist/core/ingestion/finalize-orchestrator.js +139 -0
  127. package/dist/core/ingestion/framework-detection.js +6 -2
  128. package/dist/core/ingestion/import-processor.js +4 -0
  129. package/dist/core/ingestion/import-resolvers/python.js +9 -6
  130. package/dist/core/ingestion/import-target-adapter.d.ts +73 -0
  131. package/dist/core/ingestion/import-target-adapter.js +95 -0
  132. package/dist/core/ingestion/language-provider.d.ts +36 -33
  133. package/dist/core/ingestion/languages/csharp/accessor-unwrap.d.ts +21 -0
  134. package/dist/core/ingestion/languages/csharp/accessor-unwrap.js +56 -0
  135. package/dist/core/ingestion/languages/csharp/arity-metadata.d.ts +26 -0
  136. package/dist/core/ingestion/languages/csharp/arity-metadata.js +46 -0
  137. package/dist/core/ingestion/languages/csharp/arity.d.ts +23 -0
  138. package/dist/core/ingestion/languages/csharp/arity.js +37 -0
  139. package/dist/core/ingestion/languages/csharp/cache-stats.d.ts +15 -0
  140. package/dist/core/ingestion/languages/csharp/cache-stats.js +26 -0
  141. package/dist/core/ingestion/languages/csharp/captures.d.ts +19 -0
  142. package/dist/core/ingestion/languages/csharp/captures.js +249 -0
  143. package/dist/core/ingestion/languages/csharp/import-decomposer.d.ts +19 -0
  144. package/dist/core/ingestion/languages/csharp/import-decomposer.js +93 -0
  145. package/dist/core/ingestion/languages/csharp/import-target.d.ts +25 -0
  146. package/dist/core/ingestion/languages/csharp/import-target.js +123 -0
  147. package/dist/core/ingestion/languages/csharp/index.d.ts +82 -0
  148. package/dist/core/ingestion/languages/csharp/index.js +82 -0
  149. package/dist/core/ingestion/languages/csharp/interpret.d.ts +15 -0
  150. package/dist/core/ingestion/languages/csharp/interpret.js +132 -0
  151. package/dist/core/ingestion/languages/csharp/merge-bindings.d.ts +27 -0
  152. package/dist/core/ingestion/languages/csharp/merge-bindings.js +55 -0
  153. package/dist/core/ingestion/languages/csharp/namespace-siblings.d.ts +50 -0
  154. package/dist/core/ingestion/languages/csharp/namespace-siblings.js +374 -0
  155. package/dist/core/ingestion/languages/csharp/query.d.ts +35 -0
  156. package/dist/core/ingestion/languages/csharp/query.js +515 -0
  157. package/dist/core/ingestion/languages/csharp/receiver-binding.d.ts +31 -0
  158. package/dist/core/ingestion/languages/csharp/receiver-binding.js +135 -0
  159. package/dist/core/ingestion/languages/csharp/scope-resolver.d.ts +10 -0
  160. package/dist/core/ingestion/languages/csharp/scope-resolver.js +63 -0
  161. package/dist/core/ingestion/languages/csharp/simple-hooks.d.ts +53 -0
  162. package/dist/core/ingestion/languages/csharp/simple-hooks.js +76 -0
  163. package/dist/core/ingestion/languages/csharp.js +14 -0
  164. package/dist/core/ingestion/languages/python/arity-metadata.d.ts +24 -0
  165. package/dist/core/ingestion/languages/python/arity-metadata.js +45 -0
  166. package/dist/core/ingestion/languages/python/arity.d.ts +22 -0
  167. package/dist/core/ingestion/languages/python/arity.js +38 -0
  168. package/dist/core/ingestion/languages/python/cache-stats.d.ts +17 -0
  169. package/dist/core/ingestion/languages/python/cache-stats.js +28 -0
  170. package/dist/core/ingestion/languages/python/captures.d.ts +19 -0
  171. package/dist/core/ingestion/languages/python/captures.js +106 -0
  172. package/dist/core/ingestion/languages/python/import-decomposer.d.ts +15 -0
  173. package/dist/core/ingestion/languages/python/import-decomposer.js +112 -0
  174. package/dist/core/ingestion/languages/python/import-target.d.ts +21 -0
  175. package/dist/core/ingestion/languages/python/import-target.js +99 -0
  176. package/dist/core/ingestion/languages/python/index.d.ts +80 -0
  177. package/dist/core/ingestion/languages/python/index.js +80 -0
  178. package/dist/core/ingestion/languages/python/interpret.d.ts +15 -0
  179. package/dist/core/ingestion/languages/python/interpret.js +191 -0
  180. package/dist/core/ingestion/languages/python/merge-bindings.d.ts +16 -0
  181. package/dist/core/ingestion/languages/python/merge-bindings.js +44 -0
  182. package/dist/core/ingestion/languages/python/query.d.ts +9 -0
  183. package/dist/core/ingestion/languages/python/query.js +267 -0
  184. package/dist/core/ingestion/languages/python/receiver-binding.d.ts +21 -0
  185. package/dist/core/ingestion/languages/python/receiver-binding.js +116 -0
  186. package/dist/core/ingestion/languages/python/scope-resolver.d.ts +16 -0
  187. package/dist/core/ingestion/languages/python/scope-resolver.js +53 -0
  188. package/dist/core/ingestion/languages/python/simple-hooks.d.ts +23 -0
  189. package/dist/core/ingestion/languages/python/simple-hooks.js +35 -0
  190. package/dist/core/ingestion/languages/python.js +14 -0
  191. package/dist/core/ingestion/model/method-registry.d.ts +9 -0
  192. package/dist/core/ingestion/model/method-registry.js +4 -0
  193. package/dist/core/ingestion/model/scope-resolution-indexes.d.ts +59 -0
  194. package/dist/core/ingestion/model/scope-resolution-indexes.js +42 -0
  195. package/dist/core/ingestion/model/semantic-model.d.ts +64 -0
  196. package/dist/core/ingestion/model/semantic-model.js +55 -0
  197. package/dist/core/ingestion/mro-processor.js +38 -22
  198. package/dist/core/ingestion/parsing-processor.d.ts +18 -1
  199. package/dist/core/ingestion/parsing-processor.js +45 -11
  200. package/dist/core/ingestion/pipeline-phases/index.d.ts +1 -0
  201. package/dist/core/ingestion/pipeline-phases/index.js +1 -0
  202. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +10 -0
  203. package/dist/core/ingestion/pipeline-phases/parse-impl.js +17 -2
  204. package/dist/core/ingestion/pipeline-phases/parse.d.ts +18 -0
  205. package/dist/core/ingestion/pipeline.js +2 -1
  206. package/dist/core/ingestion/registry-primary-flag.d.ts +86 -0
  207. package/dist/core/ingestion/registry-primary-flag.js +111 -0
  208. package/dist/core/ingestion/resolve-references.d.ts +63 -0
  209. package/dist/core/ingestion/resolve-references.js +175 -0
  210. package/dist/core/ingestion/scope-extractor-bridge.d.ts +32 -0
  211. package/dist/core/ingestion/scope-extractor-bridge.js +44 -0
  212. package/dist/core/ingestion/scope-extractor.d.ts +86 -0
  213. package/dist/core/ingestion/scope-extractor.js +758 -0
  214. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +372 -0
  215. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.js +212 -0
  216. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +43 -0
  217. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +79 -0
  218. package/dist/core/ingestion/scope-resolution/graph-bridge/ids.d.ts +57 -0
  219. package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +112 -0
  220. package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.d.ts +17 -0
  221. package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.js +46 -0
  222. package/dist/core/ingestion/scope-resolution/graph-bridge/method-dispatch.d.ts +19 -0
  223. package/dist/core/ingestion/scope-resolution/graph-bridge/method-dispatch.js +30 -0
  224. package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.d.ts +37 -0
  225. package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +113 -0
  226. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +38 -0
  227. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +73 -0
  228. package/dist/core/ingestion/scope-resolution/passes/compound-receiver.d.ts +42 -0
  229. package/dist/core/ingestion/scope-resolution/passes/compound-receiver.js +198 -0
  230. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +27 -0
  231. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +131 -0
  232. package/dist/core/ingestion/scope-resolution/passes/imported-return-types.d.ts +48 -0
  233. package/dist/core/ingestion/scope-resolution/passes/imported-return-types.js +130 -0
  234. package/dist/core/ingestion/scope-resolution/passes/mro.d.ts +42 -0
  235. package/dist/core/ingestion/scope-resolution/passes/mro.js +99 -0
  236. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +26 -0
  237. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +61 -0
  238. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +46 -0
  239. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +327 -0
  240. package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +47 -0
  241. package/dist/core/ingestion/scope-resolution/pipeline/phase.js +130 -0
  242. package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.d.ts +68 -0
  243. package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +125 -0
  244. package/dist/core/ingestion/scope-resolution/pipeline/registry.d.ts +17 -0
  245. package/dist/core/ingestion/scope-resolution/pipeline/registry.js +21 -0
  246. package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +66 -0
  247. package/dist/core/ingestion/scope-resolution/pipeline/run.js +157 -0
  248. package/dist/core/ingestion/scope-resolution/scope/namespace-targets.d.ts +36 -0
  249. package/dist/core/ingestion/scope-resolution/scope/namespace-targets.js +52 -0
  250. package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +127 -0
  251. package/dist/core/ingestion/scope-resolution/scope/walkers.js +349 -0
  252. package/dist/core/ingestion/scope-resolution/workspace-index.d.ts +52 -0
  253. package/dist/core/ingestion/scope-resolution/workspace-index.js +61 -0
  254. package/dist/core/ingestion/shadow-harness.d.ts +113 -0
  255. package/dist/core/ingestion/shadow-harness.js +148 -0
  256. package/dist/core/ingestion/utils/ast-helpers.d.ts +19 -1
  257. package/dist/core/ingestion/utils/ast-helpers.js +70 -0
  258. package/dist/core/ingestion/utils/max-file-size.d.ts +20 -0
  259. package/dist/core/ingestion/utils/max-file-size.js +52 -0
  260. package/dist/core/ingestion/workers/parse-worker.d.ts +9 -0
  261. package/dist/core/ingestion/workers/parse-worker.js +57 -21
  262. package/dist/core/lbug/lbug-adapter.d.ts +22 -2
  263. package/dist/core/lbug/lbug-adapter.js +58 -14
  264. package/dist/core/lbug/pool-adapter.d.ts +17 -0
  265. package/dist/core/lbug/pool-adapter.js +24 -14
  266. package/dist/core/run-analyze.d.ts +32 -0
  267. package/dist/core/run-analyze.js +74 -19
  268. package/dist/core/search/bm25-index.d.ts +18 -0
  269. package/dist/core/search/bm25-index.js +125 -12
  270. package/dist/core/tree-sitter/parser-loader.js +6 -1
  271. package/dist/mcp/local/local-backend.d.ts +67 -3
  272. package/dist/mcp/local/local-backend.js +296 -34
  273. package/dist/mcp/resources.d.ts +31 -0
  274. package/dist/mcp/resources.js +100 -17
  275. package/dist/mcp/tools.d.ts +4 -1
  276. package/dist/mcp/tools.js +75 -54
  277. package/dist/server/api.js +6 -2
  278. package/dist/storage/git.d.ts +49 -0
  279. package/dist/storage/git.js +111 -0
  280. package/dist/storage/repo-manager.d.ts +246 -1
  281. package/dist/storage/repo-manager.js +391 -9
  282. package/package.json +7 -6
  283. package/scripts/bench-scope-resolution.ts +134 -0
  284. package/scripts/ci-list-migrated-languages.ts +24 -0
  285. package/skills/gitnexus-cli.md +1 -0
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Generic MRO (method-resolution-order) builder.
3
+ *
4
+ * Walks the graph's `EXTENDS` edges to recover an inheritance map,
5
+ * then asks the per-language `LinearizeStrategy` to order each class's
6
+ * ancestors. Returns `Map<classDefId, ancestorDefId[]>` ready to plug
7
+ * into `MethodDispatchIndex` via `buildPopulatedMethodDispatch`.
8
+ *
9
+ * **Why a strategy hook:** linearization differs across languages.
10
+ * - Python (depth-first first-seen, single inheritance): trivially
11
+ * correct; multi-inheritance falls back to BFS dedup. Real C3
12
+ * would handle diamond hierarchies — defer until we hit one.
13
+ * - Java (single-inheritance only): walk one parent.
14
+ * - C++ (multiple inheritance): C3-like or BFS depending on how
15
+ * strict the consumer needs to be.
16
+ * - Languages without inheritance (COBOL): return empty list.
17
+ *
18
+ * The strategy receives the FULL ancestry context (`directParents` +
19
+ * `parentsByDefId`) so C3 implementations have what they need.
20
+ */
21
+ import { resolveDefGraphId } from '../graph-bridge/ids.js';
22
+ /**
23
+ * Build an MRO map keyed by scope-resolution Class `DefId`.
24
+ *
25
+ * Steps:
26
+ * 1. Collect EXTENDS edges from the graph → `parentsByGraphId`.
27
+ * 2. Collect Class defs from `parsedFiles` and translate to graph
28
+ * ids via `nodeLookup` → `defIdByGraphId` (the bridge between
29
+ * scope-resolution DefId and the legacy graph node id).
30
+ * 3. For each Class def, ask `linearize` for its ancestor order.
31
+ */
32
+ export function buildMro(graph, parsedFiles, nodeLookup, linearize) {
33
+ // Step 1: parentsByGraphId — typed iterator skips the per-edge type
34
+ // check and the millions of CALLS/ACCESSES/IMPORTS/DEFINES edges
35
+ // that aren't relevant to MRO.
36
+ const parentsByGraphId = new Map();
37
+ for (const rel of graph.iterRelationshipsByType('EXTENDS')) {
38
+ let list = parentsByGraphId.get(rel.sourceId);
39
+ if (list === undefined) {
40
+ list = [];
41
+ parentsByGraphId.set(rel.sourceId, list);
42
+ }
43
+ list.push(rel.targetId);
44
+ }
45
+ // Step 2: defIdByGraphId — translate graph ids to scope-resolution DefIds.
46
+ const defIdByGraphId = new Map();
47
+ for (const parsed of parsedFiles) {
48
+ for (const def of parsed.localDefs) {
49
+ if (def.type !== 'Class')
50
+ continue;
51
+ const graphId = resolveDefGraphId(parsed.filePath, def, nodeLookup);
52
+ if (graphId !== undefined)
53
+ defIdByGraphId.set(graphId, def.nodeId);
54
+ }
55
+ }
56
+ // Step 2b: invert parentsByGraphId into parentsByDefId — the
57
+ // strategy works in DefId space.
58
+ const parentsByDefId = new Map();
59
+ for (const [childGraphId, parents] of parentsByGraphId) {
60
+ const childDefId = defIdByGraphId.get(childGraphId);
61
+ if (childDefId === undefined)
62
+ continue;
63
+ const parentDefIds = [];
64
+ for (const p of parents) {
65
+ const pd = defIdByGraphId.get(p);
66
+ if (pd !== undefined)
67
+ parentDefIds.push(pd);
68
+ }
69
+ parentsByDefId.set(childDefId, parentDefIds);
70
+ }
71
+ // Step 3: linearize per class.
72
+ const mroByDefId = new Map();
73
+ for (const defId of defIdByGraphId.values()) {
74
+ const directParents = parentsByDefId.get(defId) ?? [];
75
+ mroByDefId.set(defId, linearize(defId, directParents, parentsByDefId));
76
+ }
77
+ return mroByDefId;
78
+ }
79
+ /**
80
+ * Default linearization: depth-first BFS-with-visited, first-seen
81
+ * wins. Correct for single-inheritance languages and for Python's
82
+ * simplified MRO. Multi-inheritance diamond hierarchies need a real
83
+ * C3 implementation; per-language overrides land here.
84
+ */
85
+ export const defaultLinearize = (_classDefId, directParents, parentsByDefId) => {
86
+ const ancestors = [];
87
+ const visited = new Set();
88
+ const queue = [...directParents];
89
+ while (queue.length > 0) {
90
+ const cur = queue.shift();
91
+ if (visited.has(cur))
92
+ continue;
93
+ visited.add(cur);
94
+ ancestors.push(cur);
95
+ for (const p of parentsByDefId.get(cur) ?? [])
96
+ queue.push(p);
97
+ }
98
+ return ancestors;
99
+ };
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Overload narrowing — pick candidates from a list of same-named
3
+ * method / function overloads using the call-site's arity and
4
+ * argument-type signals.
5
+ *
6
+ * Used by both `receiver-bound-calls.ts::pickOverload` (explicit
7
+ * receiver member call) and `free-call-fallback.ts::pickImplicitThisOverload`
8
+ * (implicit `this` free-call inside a class-like body). Shared to keep
9
+ * narrowing semantics in lockstep across the two sites.
10
+ *
11
+ * Semantics (first-wins; callers take `result[0]`):
12
+ * 1. If `argCount` is undefined, arity is a pass-through.
13
+ * 2. Exact-required-match wins over variadic. Variadic is detected
14
+ * via a `parameterTypes` entry equal to `'params'` or starting
15
+ * with `'params '` (C# `params` / variadic marker).
16
+ * 3. If the arity filter empties the set, fall back to the full
17
+ * overload list rather than returning nothing — the caller still
18
+ * needs a best-effort candidate.
19
+ * 4. If `argTypes` is present, filter further by per-slot type
20
+ * equality. An empty string in `argTypes[i]` means "unknown" and
21
+ * counts as a match. Mismatches disqualify. A non-empty typed
22
+ * result wins; otherwise return the arity-filtered candidates.
23
+ * 5. Empty input returns empty output.
24
+ */
25
+ import type { SymbolDefinition } from '../../../../_shared/index.js';
26
+ export declare function narrowOverloadCandidates(overloads: readonly SymbolDefinition[], argCount: number | undefined, argTypes: readonly string[] | undefined): readonly SymbolDefinition[];
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Overload narrowing — pick candidates from a list of same-named
3
+ * method / function overloads using the call-site's arity and
4
+ * argument-type signals.
5
+ *
6
+ * Used by both `receiver-bound-calls.ts::pickOverload` (explicit
7
+ * receiver member call) and `free-call-fallback.ts::pickImplicitThisOverload`
8
+ * (implicit `this` free-call inside a class-like body). Shared to keep
9
+ * narrowing semantics in lockstep across the two sites.
10
+ *
11
+ * Semantics (first-wins; callers take `result[0]`):
12
+ * 1. If `argCount` is undefined, arity is a pass-through.
13
+ * 2. Exact-required-match wins over variadic. Variadic is detected
14
+ * via a `parameterTypes` entry equal to `'params'` or starting
15
+ * with `'params '` (C# `params` / variadic marker).
16
+ * 3. If the arity filter empties the set, fall back to the full
17
+ * overload list rather than returning nothing — the caller still
18
+ * needs a best-effort candidate.
19
+ * 4. If `argTypes` is present, filter further by per-slot type
20
+ * equality. An empty string in `argTypes[i]` means "unknown" and
21
+ * counts as a match. Mismatches disqualify. A non-empty typed
22
+ * result wins; otherwise return the arity-filtered candidates.
23
+ * 5. Empty input returns empty output.
24
+ */
25
+ export function narrowOverloadCandidates(overloads, argCount, argTypes) {
26
+ if (overloads.length === 0)
27
+ return [];
28
+ const arityMatches = argCount === undefined
29
+ ? overloads
30
+ : overloads.filter((d) => {
31
+ const max = d.parameterCount;
32
+ const min = d.requiredParameterCount;
33
+ if (max !== undefined && argCount > max) {
34
+ const variadic = d.parameterTypes !== undefined &&
35
+ d.parameterTypes.some((t) => t === 'params' || t.startsWith('params '));
36
+ if (!variadic)
37
+ return false;
38
+ }
39
+ if (min !== undefined && argCount < min)
40
+ return false;
41
+ return true;
42
+ });
43
+ const candidates = arityMatches.length > 0 ? arityMatches : overloads;
44
+ if (argTypes !== undefined && argTypes.length > 0) {
45
+ const typed = candidates.filter((d) => {
46
+ const params = d.parameterTypes;
47
+ if (params === undefined)
48
+ return false;
49
+ for (let i = 0; i < argTypes.length && i < params.length; i++) {
50
+ if (argTypes[i] === '')
51
+ continue;
52
+ if (argTypes[i] !== params[i])
53
+ return false;
54
+ }
55
+ return true;
56
+ });
57
+ if (typed.length > 0)
58
+ return typed;
59
+ }
60
+ return candidates;
61
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Receiver-bound CALLS / ACCESSES emit pass — generic 7-case
3
+ * dispatcher consuming `ScopeResolver` for the language-specific bits
4
+ * (super recognizer, field-fallback toggle).
5
+ *
6
+ * **Contract Invariant I4 — case order is load-bearing.** The cases
7
+ * are evaluated in this order; the FIRST that emits an edge wins:
8
+ *
9
+ * 1. **super branch** — `provider.isSuperReceiver(receiverName)` →
10
+ * MRO walk skipping self
11
+ * 2. **Case 0 (compound)** — receiver has `.` or `(` → compound resolver
12
+ * 3. **Case 1 (namespace)** — receiver in `namespaceTargets` → exported def
13
+ * 4. **Case 2 (class-name / static receiver)** — receiver resolves to a
14
+ * class-like binding (Class/Interface/Struct/Record/Enum/Trait) → MRO
15
+ * walk on that class. Also handles static-style invocations
16
+ * (`ILogger.Warn(...)`) with kind-aware reason/confidence for
17
+ * read/write ACCESSES.
18
+ * 5. **Case 3 (dotted typeBinding for namespace prefix)** —
19
+ * `typeRef.rawName` like `models.User`
20
+ * 6. **Case 3b (chain-typebinding)** — `typeRef.rawName` has a dot
21
+ * but not a namespace prefix → compound resolver
22
+ * 7. **Case 4 (simple typeBinding)** — `typeRef.rawName` has no dot →
23
+ * MRO walk + `findOwnedMember`
24
+ *
25
+ * Reordering or merging cases changes resolution semantics.
26
+ *
27
+ * **Contract Invariant I5 — pre-seeding `seen` is forbidden.** The
28
+ * orchestrator runs this pass FIRST (before `emitReferencesViaLookup`)
29
+ * and consumes the populated `handledSites` set. Pre-seeding `seen`
30
+ * from the shared resolver's emissions (an old optimization) actively
31
+ * suppresses correct emissions for sites the shared resolver also
32
+ * resolved to a wrong target.
33
+ */
34
+ import type { ParsedFile } from '../../../../_shared/index.js';
35
+ import type { KnowledgeGraph } from '../../../graph/types.js';
36
+ import type { ScopeResolutionIndexes } from '../../model/scope-resolution-indexes.js';
37
+ import type { SemanticModel } from '../../model/semantic-model.js';
38
+ import type { ScopeResolver } from '../contract/scope-resolver.js';
39
+ import type { GraphNodeLookup } from '../graph-bridge/node-lookup.js';
40
+ import type { WorkspaceResolutionIndex } from '../workspace-index.js';
41
+ /** Subset of `ScopeResolver` consumed by this pass. Accepting the
42
+ * subset rather than the full provider keeps tests and partial
43
+ * refactors lighter — callers only need to populate what we read. */
44
+ type ReceiverBoundProviderSubset = Pick<ScopeResolver, 'isSuperReceiver' | 'fieldFallbackOnMethodLookup' | 'collapseMemberCallsByCallerTarget' | 'unwrapCollectionAccessor' | 'hoistTypeBindingsToModule'>;
45
+ export declare function emitReceiverBoundCalls(graph: KnowledgeGraph, scopes: ScopeResolutionIndexes, parsedFiles: readonly ParsedFile[], nodeLookup: GraphNodeLookup, handledSites: Set<string>, provider: ReceiverBoundProviderSubset, index: WorkspaceResolutionIndex, model: SemanticModel): number;
46
+ export {};
@@ -0,0 +1,327 @@
1
+ /**
2
+ * Receiver-bound CALLS / ACCESSES emit pass — generic 7-case
3
+ * dispatcher consuming `ScopeResolver` for the language-specific bits
4
+ * (super recognizer, field-fallback toggle).
5
+ *
6
+ * **Contract Invariant I4 — case order is load-bearing.** The cases
7
+ * are evaluated in this order; the FIRST that emits an edge wins:
8
+ *
9
+ * 1. **super branch** — `provider.isSuperReceiver(receiverName)` →
10
+ * MRO walk skipping self
11
+ * 2. **Case 0 (compound)** — receiver has `.` or `(` → compound resolver
12
+ * 3. **Case 1 (namespace)** — receiver in `namespaceTargets` → exported def
13
+ * 4. **Case 2 (class-name / static receiver)** — receiver resolves to a
14
+ * class-like binding (Class/Interface/Struct/Record/Enum/Trait) → MRO
15
+ * walk on that class. Also handles static-style invocations
16
+ * (`ILogger.Warn(...)`) with kind-aware reason/confidence for
17
+ * read/write ACCESSES.
18
+ * 5. **Case 3 (dotted typeBinding for namespace prefix)** —
19
+ * `typeRef.rawName` like `models.User`
20
+ * 6. **Case 3b (chain-typebinding)** — `typeRef.rawName` has a dot
21
+ * but not a namespace prefix → compound resolver
22
+ * 7. **Case 4 (simple typeBinding)** — `typeRef.rawName` has no dot →
23
+ * MRO walk + `findOwnedMember`
24
+ *
25
+ * Reordering or merging cases changes resolution semantics.
26
+ *
27
+ * **Contract Invariant I5 — pre-seeding `seen` is forbidden.** The
28
+ * orchestrator runs this pass FIRST (before `emitReferencesViaLookup`)
29
+ * and consumes the populated `handledSites` set. Pre-seeding `seen`
30
+ * from the shared resolver's emissions (an old optimization) actively
31
+ * suppresses correct emissions for sites the shared resolver also
32
+ * resolved to a wrong target.
33
+ */
34
+ import { collectNamespaceTargets } from '../scope/namespace-targets.js';
35
+ import { findClassBindingInScope, findEnclosingClassDef, findExportedDef, findOwnedMember, findReceiverTypeBinding, } from '../scope/walkers.js';
36
+ import { tryEmitEdge } from '../graph-bridge/edges.js';
37
+ import { resolveCompoundReceiverClass } from '../passes/compound-receiver.js';
38
+ import { resolveDefGraphId } from '../graph-bridge/ids.js';
39
+ import { narrowOverloadCandidates } from './overload-narrowing.js';
40
+ export function emitReceiverBoundCalls(graph, scopes, parsedFiles, nodeLookup, handledSites, provider, index, model) {
41
+ let emitted = 0;
42
+ // Per-pass dedup so the multiple cases don't double-emit if two of
43
+ // them resolve the same site to the same target. NEVER pre-seed
44
+ // from the reference index — see Contract Invariant I5.
45
+ const seen = new Set();
46
+ const fieldFallback = provider.fieldFallbackOnMethodLookup ?? true;
47
+ const collapse = provider.collapseMemberCallsByCallerTarget === true;
48
+ const hoistTypeBindingsToModule = provider.hoistTypeBindingsToModule === true;
49
+ const compoundOpts = {
50
+ fieldFallback,
51
+ unwrapCollectionAccessor: provider.unwrapCollectionAccessor,
52
+ hoistTypeBindingsToModule,
53
+ };
54
+ // Build an interface → implementors map from IMPLEMENTS edges.
55
+ // Maps Interface graph-id → list of implementor class scope-def-ids.
56
+ // We translate graph-ids back to scope-resolution DefIds via
57
+ // `parsedFiles.localDefs` lookup so downstream `findOwnedMember`
58
+ // (which keys by DefId) can find the implementor's members.
59
+ const graphIdToClassDef = new Map();
60
+ for (const parsed of parsedFiles) {
61
+ for (const def of parsed.localDefs) {
62
+ if (def.type !== 'Class' && def.type !== 'Interface')
63
+ continue;
64
+ const graphId = resolveDefGraphId(parsed.filePath, def, nodeLookup);
65
+ if (graphId !== undefined)
66
+ graphIdToClassDef.set(graphId, def);
67
+ }
68
+ }
69
+ const implementorsByInterfaceDefId = new Map();
70
+ for (const rel of graph.iterRelationshipsByType('IMPLEMENTS')) {
71
+ const ifaceDef = graphIdToClassDef.get(rel.targetId);
72
+ const implDef = graphIdToClassDef.get(rel.sourceId);
73
+ if (ifaceDef === undefined || implDef === undefined)
74
+ continue;
75
+ let list = implementorsByInterfaceDefId.get(ifaceDef.nodeId);
76
+ if (list === undefined) {
77
+ list = [];
78
+ implementorsByInterfaceDefId.set(ifaceDef.nodeId, list);
79
+ }
80
+ list.push(implDef);
81
+ }
82
+ /** Emit secondary CALLS edges with reason='interface-dispatch'
83
+ * when the primary receiver-typed edge targeted an Interface's
84
+ * method. Each implementing class's same-named method gets a
85
+ * secondary edge (excluding the primary target itself). */
86
+ const emitInterfaceDispatchFor = (ownerDef, memberName, primaryMemberDef, site, confidence) => {
87
+ if (ownerDef.type !== 'Interface')
88
+ return 0;
89
+ const impls = implementorsByInterfaceDefId.get(ownerDef.nodeId);
90
+ if (impls === undefined)
91
+ return 0;
92
+ let n = 0;
93
+ for (const implDef of impls) {
94
+ const implMember = findOwnedMember(implDef.nodeId, memberName, model);
95
+ if (implMember === undefined)
96
+ continue;
97
+ if (implMember.nodeId === primaryMemberDef.nodeId)
98
+ continue;
99
+ const ok = tryEmitEdge(graph, scopes, nodeLookup, site, implMember, 'interface-dispatch', seen, confidence, collapse);
100
+ if (ok)
101
+ n++;
102
+ }
103
+ return n;
104
+ };
105
+ for (const parsed of parsedFiles) {
106
+ const namespaceTargets = collectNamespaceTargets(parsed, scopes);
107
+ for (const site of parsed.referenceSites) {
108
+ if (site.kind !== 'call' && site.kind !== 'read' && site.kind !== 'write')
109
+ continue;
110
+ if (site.explicitReceiver === undefined)
111
+ continue;
112
+ const receiverName = site.explicitReceiver.name;
113
+ const memberName = site.name;
114
+ const siteKey = `${parsed.filePath}:${site.atRange.startLine}:${site.atRange.startCol}`;
115
+ // ── super branch ─────────────────────────────────────────────
116
+ if (provider.isSuperReceiver(receiverName)) {
117
+ const enclosingClass = findEnclosingClassDef(site.inScope, scopes);
118
+ if (enclosingClass !== undefined) {
119
+ const ancestors = scopes.methodDispatch.mroFor(enclosingClass.nodeId);
120
+ let memberDef;
121
+ for (const ownerId of ancestors) {
122
+ memberDef = findOwnedMember(ownerId, memberName, model);
123
+ if (memberDef !== undefined)
124
+ break;
125
+ }
126
+ if (memberDef !== undefined) {
127
+ // Super/base calls resolve through the MRO chain, not
128
+ // through imports — the ancestor method is found by
129
+ // walking `methodDispatch.mroFor(enclosingClass)`, which
130
+ // is independent of whether a `using` / `import` directive
131
+ // brought the ancestor into scope. We emit the canonical
132
+ // `'global'` tier (ARCHITECTURE.md § Scope-Resolution
133
+ // Pipeline — edge vocabulary).
134
+ //
135
+ // Known legacy-path asymmetry: the C# legacy DAG also
136
+ // classifies `base.Save()` as `'global'` (same-graph); the
137
+ // Python legacy DAG classifies `super().save()` as
138
+ // `'import-resolved'` because Python's ancestor lookup
139
+ // flows through `typeEnv.lookup(...)` which resolves the
140
+ // superclass via its `import`/`from … import …` binding.
141
+ // Closing that gap requires realigning the legacy tier
142
+ // classifier and is tracked separately.
143
+ const ok = tryEmitEdge(graph, scopes, nodeLookup, site, memberDef, 'global', seen, 0.85, collapse);
144
+ if (ok)
145
+ emitted++;
146
+ // Always mark handled when the site was resolved, even
147
+ // if the edge was deduplicated (collapse mode), so
148
+ // `emitReferencesViaLookup` doesn't re-emit from the
149
+ // reference index.
150
+ handledSites.add(siteKey);
151
+ continue;
152
+ }
153
+ }
154
+ }
155
+ // ── Case 0: compound receiver ────────────────────────────────
156
+ if (receiverName.includes('.') || receiverName.includes('(')) {
157
+ const currentClass = resolveCompoundReceiverClass(receiverName, site.inScope, scopes, index, compoundOpts);
158
+ if (currentClass !== undefined) {
159
+ const chain = [currentClass.nodeId, ...scopes.methodDispatch.mroFor(currentClass.nodeId)];
160
+ let memberDef;
161
+ for (const ownerId of chain) {
162
+ memberDef = findOwnedMember(ownerId, memberName, model);
163
+ if (memberDef !== undefined)
164
+ break;
165
+ }
166
+ if (memberDef !== undefined) {
167
+ const ok = tryEmitEdge(graph, scopes, nodeLookup, site, memberDef, memberDef.filePath !== parsed.filePath ? 'import-resolved' : 'global', seen, 0.85, collapse);
168
+ if (ok)
169
+ emitted++;
170
+ // Always mark handled when the site was resolved, even
171
+ // if the edge was deduplicated (collapse mode), so
172
+ // `emitReferencesViaLookup` doesn't re-emit from the
173
+ // reference index.
174
+ handledSites.add(siteKey);
175
+ continue;
176
+ }
177
+ }
178
+ }
179
+ // ── Case 1: namespace receiver ───────────────────────────────
180
+ const targetFile = namespaceTargets.get(receiverName);
181
+ if (targetFile !== undefined) {
182
+ const memberDef = findExportedDef(targetFile, memberName, index);
183
+ if (memberDef !== undefined) {
184
+ const ok = tryEmitEdge(graph, scopes, nodeLookup, site, memberDef, memberDef.filePath !== parsed.filePath ? 'import-resolved' : 'global', seen, 0.85, collapse);
185
+ if (ok)
186
+ emitted++;
187
+ handledSites.add(siteKey);
188
+ continue;
189
+ }
190
+ }
191
+ // ── Case 2: class-name receiver ──────────────────────────────
192
+ const classDef = findClassBindingInScope(site.inScope, receiverName, scopes);
193
+ if (classDef !== undefined) {
194
+ const chain = [classDef.nodeId, ...scopes.methodDispatch.mroFor(classDef.nodeId)];
195
+ let memberDef;
196
+ for (const ownerId of chain) {
197
+ memberDef = findOwnedMember(ownerId, memberName, model);
198
+ if (memberDef !== undefined)
199
+ break;
200
+ }
201
+ if (memberDef !== undefined) {
202
+ const reason = site.kind === 'write' || site.kind === 'read'
203
+ ? site.kind
204
+ : memberDef.filePath !== parsed.filePath
205
+ ? 'import-resolved'
206
+ : 'global';
207
+ const confidence = site.kind === 'write' || site.kind === 'read' ? 1.0 : 0.85;
208
+ const ok = tryEmitEdge(graph, scopes, nodeLookup, site, memberDef, reason, seen, confidence, collapse);
209
+ if (ok)
210
+ emitted++;
211
+ handledSites.add(siteKey);
212
+ continue;
213
+ }
214
+ }
215
+ // ── Case 3: dotted typeBinding (`u: models.User`) ────────────
216
+ const typeRef = findReceiverTypeBinding(site.inScope, receiverName, scopes);
217
+ if (typeRef !== undefined && typeRef.rawName.includes('.')) {
218
+ const [nsName, ...classNameParts] = typeRef.rawName.split('.');
219
+ const className = classNameParts.join('.');
220
+ const targetFile3 = namespaceTargets.get(nsName);
221
+ if (targetFile3 !== undefined && className.length > 0) {
222
+ const classDef3 = findExportedDef(targetFile3, className, index);
223
+ if (classDef3 !== undefined) {
224
+ const memberDef = findOwnedMember(classDef3.nodeId, memberName, model);
225
+ if (memberDef !== undefined) {
226
+ const ok = tryEmitEdge(graph, scopes, nodeLookup, site, memberDef, memberDef.filePath !== parsed.filePath ? 'import-resolved' : 'global', seen);
227
+ if (ok) {
228
+ emitted++;
229
+ handledSites.add(siteKey);
230
+ }
231
+ continue;
232
+ }
233
+ }
234
+ }
235
+ }
236
+ // ── Case 3b: chain-typebinding (`city → user.get_city`) ──────
237
+ if (typeRef !== undefined &&
238
+ typeRef.rawName.includes('.') &&
239
+ !typeRef.rawName.includes('(') &&
240
+ !namespaceTargets.has(typeRef.rawName.split('.')[0])) {
241
+ // Try the plain dotted-field walk first — covers property /
242
+ // collection-accessor shapes (`.Values`, Kotlin `.size`) and
243
+ // field chains. Fall back to call-form (`x()`) which treats
244
+ // the last segment as a method invocation.
245
+ let ownerDef = resolveCompoundReceiverClass(typeRef.rawName, typeRef.declaredAtScope, scopes, index, compoundOpts);
246
+ if (ownerDef === undefined) {
247
+ ownerDef = resolveCompoundReceiverClass(typeRef.rawName + '()', typeRef.declaredAtScope, scopes, index, compoundOpts);
248
+ }
249
+ if (ownerDef !== undefined) {
250
+ const chain = [ownerDef.nodeId, ...scopes.methodDispatch.mroFor(ownerDef.nodeId)];
251
+ let memberDef;
252
+ for (const ownerId of chain) {
253
+ memberDef = findOwnedMember(ownerId, memberName, model);
254
+ if (memberDef !== undefined)
255
+ break;
256
+ }
257
+ if (memberDef !== undefined) {
258
+ const ok = tryEmitEdge(graph, scopes, nodeLookup, site, memberDef, memberDef.filePath !== parsed.filePath ? 'import-resolved' : 'global', seen, 0.85, collapse);
259
+ if (ok)
260
+ emitted++;
261
+ // Always mark handled when the site was resolved, even
262
+ // if the edge was deduplicated (collapse mode), so
263
+ // `emitReferencesViaLookup` doesn't re-emit from the
264
+ // reference index.
265
+ handledSites.add(siteKey);
266
+ continue;
267
+ }
268
+ }
269
+ }
270
+ // ── Case 4: simple typeBinding (`u: U`) ──────────────────────
271
+ if (typeRef !== undefined && !typeRef.rawName.includes('.')) {
272
+ const ownerDef = findClassBindingInScope(site.inScope, typeRef.rawName, scopes);
273
+ if (ownerDef !== undefined) {
274
+ const chain = [ownerDef.nodeId, ...scopes.methodDispatch.mroFor(ownerDef.nodeId)];
275
+ let memberDef;
276
+ for (const ownerId of chain) {
277
+ memberDef = pickOverload(ownerId, memberName, site, model);
278
+ if (memberDef !== undefined)
279
+ break;
280
+ }
281
+ if (memberDef !== undefined) {
282
+ // For read/write ACCESSES, mirror the legacy DAG's reason
283
+ // convention so consumers asserting `reason === 'write'`
284
+ // keep working.
285
+ const reason = site.kind === 'write' || site.kind === 'read'
286
+ ? site.kind
287
+ : memberDef.filePath !== parsed.filePath
288
+ ? 'import-resolved'
289
+ : 'global';
290
+ const confidence = site.kind === 'write' || site.kind === 'read' ? 1.0 : 0.85;
291
+ const ok = tryEmitEdge(graph, scopes, nodeLookup, site, memberDef, reason, seen, confidence, collapse);
292
+ if (ok)
293
+ emitted++;
294
+ // Interface dispatch: when the primary owner is an
295
+ // Interface, emit secondary CALLS edges to every
296
+ // implementing class's same-named method.
297
+ emitted += emitInterfaceDispatchFor(ownerDef, memberName, memberDef, site, confidence);
298
+ // Always mark handled when the site was resolved, even
299
+ // if the edge was deduplicated (collapse mode), so
300
+ // `emitReferencesViaLookup` doesn't re-emit from the
301
+ // reference index.
302
+ handledSites.add(siteKey);
303
+ continue;
304
+ }
305
+ }
306
+ }
307
+ }
308
+ }
309
+ return emitted;
310
+ }
311
+ /** Resolve a member by name on a class def, narrowing by argument
312
+ * types when multiple overloads share the name. Falls back to the
313
+ * first-seen def (legacy `findOwnedMember` semantics) when there's
314
+ * no narrowing signal or when `argumentTypes` is unavailable. */
315
+ function pickOverload(ownerId, memberName, site, model) {
316
+ const overloads = model.methods.lookupAllByOwner(ownerId, memberName);
317
+ if (overloads.length === 0) {
318
+ // Non-callable member (field / property / variable) — ACCESSES
319
+ // write/read sites target these too. Fall back to the field
320
+ // registry so owner-scoped attribute access resolves.
321
+ return model.fields.lookupFieldByOwner(ownerId, memberName);
322
+ }
323
+ if (overloads.length === 1)
324
+ return overloads[0];
325
+ const candidates = narrowOverloadCandidates(overloads, site.arity, site.argumentTypes);
326
+ return candidates[0] ?? overloads[0];
327
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Phase: scopeResolution
3
+ *
4
+ * Generic registry-primary resolution phase (RFC #909 Ring 3).
5
+ *
6
+ * For every language in `MIGRATED_LANGUAGES` (per-language flag set)
7
+ * whose provider is registered in `SCOPE_RESOLVERS`:
8
+ * 1. Filter scanned files by language extension.
9
+ * 2. Read file contents.
10
+ * 3. Drive the scope-based pipeline end-to-end via the generic
11
+ * `runScopeResolution(input, provider)` orchestrator.
12
+ * 4. Emit IMPORTS / CALLS / ACCESSES / INHERITS / USES edges.
13
+ *
14
+ * Pairs with the per-language gates in `import-processor.ts` and
15
+ * `call-processor.ts` that skip files when their language is registry-
16
+ * primary, so we don't double-emit edges from both code paths.
17
+ *
18
+ * Adding a language is two changes:
19
+ * - Implement `ScopeResolver` in `languages/<lang>/scope-resolver.ts`
20
+ * and register it in `scope-resolution/pipeline/registry.ts`.
21
+ * - Add the language to `MIGRATED_LANGUAGES` in
22
+ * `registry-primary-flag.ts`.
23
+ *
24
+ * @deps parse (needs Symbol nodes already in the graph so emit-references
25
+ * can attach edges to existing Function/Method/Class nodes)
26
+ * @reads scannedFiles
27
+ * @writes graph (IMPORTS, CALLS, ACCESSES, INHERITS, USES)
28
+ */
29
+ import type { PipelinePhase } from '../../pipeline-phases/types.js';
30
+ import { SupportedLanguages } from '../../../../_shared/index.js';
31
+ export interface ScopeResolutionOutput {
32
+ /** True when at least one language ran. */
33
+ readonly ran: boolean;
34
+ /** Files seen across all languages. `0` when `ran === false`. */
35
+ readonly filesProcessed: number;
36
+ /** IMPORTS edges emitted across all languages. */
37
+ readonly importsEmitted: number;
38
+ /** Reference (CALLS / ACCESSES / INHERITS / USES) edges emitted. */
39
+ readonly referenceEdgesEmitted: number;
40
+ /** Per-language breakdown for telemetry / shadow-parity. */
41
+ readonly perLanguage: ReadonlyMap<SupportedLanguages, {
42
+ readonly filesProcessed: number;
43
+ readonly importsEmitted: number;
44
+ readonly referenceEdgesEmitted: number;
45
+ }>;
46
+ }
47
+ export declare const scopeResolutionPhase: PipelinePhase<ScopeResolutionOutput>;