gitnexus 1.6.2-rc.21 → 1.6.2-rc.22
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.
- package/dist/_shared/mro-strategy.d.ts +38 -16
- package/dist/_shared/mro-strategy.d.ts.map +1 -1
- package/dist/core/ingestion/call-processor.d.ts +1 -1
- package/dist/core/ingestion/call-processor.js +172 -42
- package/dist/core/ingestion/call-routing.d.ts +8 -12
- package/dist/core/ingestion/call-routing.js +13 -34
- package/dist/core/ingestion/call-types.d.ts +75 -0
- package/dist/core/ingestion/heritage-extractors/configs/go.d.ts +13 -0
- package/dist/core/ingestion/heritage-extractors/configs/go.js +20 -0
- package/dist/core/ingestion/heritage-extractors/configs/ruby.d.ts +18 -0
- package/dist/core/ingestion/heritage-extractors/configs/ruby.js +65 -0
- package/dist/core/ingestion/heritage-extractors/generic.d.ts +23 -0
- package/dist/core/ingestion/heritage-extractors/generic.js +47 -0
- package/dist/core/ingestion/heritage-processor.d.ts +9 -0
- package/dist/core/ingestion/heritage-processor.js +120 -85
- package/dist/core/ingestion/heritage-types.d.ts +73 -0
- package/dist/core/ingestion/heritage-types.js +2 -0
- package/dist/core/ingestion/language-provider.d.ts +69 -1
- package/dist/core/ingestion/languages/c-cpp.js +3 -0
- package/dist/core/ingestion/languages/csharp.js +2 -0
- package/dist/core/ingestion/languages/dart.js +2 -0
- package/dist/core/ingestion/languages/go.js +3 -0
- package/dist/core/ingestion/languages/java.js +2 -0
- package/dist/core/ingestion/languages/kotlin.js +2 -0
- package/dist/core/ingestion/languages/php.js +2 -0
- package/dist/core/ingestion/languages/python.js +2 -0
- package/dist/core/ingestion/languages/ruby.js +92 -15
- package/dist/core/ingestion/languages/rust.js +2 -0
- package/dist/core/ingestion/languages/swift.js +2 -0
- package/dist/core/ingestion/languages/typescript.js +3 -0
- package/dist/core/ingestion/languages/vue.js +2 -0
- package/dist/core/ingestion/model/heritage-map.d.ts +35 -0
- package/dist/core/ingestion/model/heritage-map.js +110 -9
- package/dist/core/ingestion/model/resolve.d.ts +30 -28
- package/dist/core/ingestion/model/resolve.js +105 -25
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +1 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +9 -3
- package/dist/core/ingestion/pipeline-phases/parse.d.ts +7 -0
- package/dist/core/ingestion/pipeline.d.ts +11 -0
- package/dist/core/ingestion/pipeline.js +9 -2
- package/dist/core/ingestion/utils/ast-helpers.js +19 -2
- package/dist/core/ingestion/utils/ruby-self-call.d.ts +52 -0
- package/dist/core/ingestion/utils/ruby-self-call.js +59 -0
- package/dist/core/ingestion/workers/parse-worker.js +57 -60
- package/dist/types/pipeline.d.ts +6 -0
- package/package.json +1 -1
|
@@ -27,6 +27,7 @@ import { createVariableExtractor } from '../variable-extractors/generic.js';
|
|
|
27
27
|
import { dartVariableConfig } from '../variable-extractors/configs/dart.js';
|
|
28
28
|
import { createCallExtractor } from '../call-extractors/generic.js';
|
|
29
29
|
import { dartCallConfig } from '../call-extractors/configs/dart.js';
|
|
30
|
+
import { createHeritageExtractor } from '../heritage-extractors/generic.js';
|
|
30
31
|
/**
|
|
31
32
|
* Resolve the enclosing function from a `function_body` node by looking at its
|
|
32
33
|
* previous sibling. In Dart's tree-sitter grammar, function_signature and
|
|
@@ -95,6 +96,7 @@ export const dartProvider = defineLanguage({
|
|
|
95
96
|
methodExtractor: createMethodExtractor(dartMethodConfig),
|
|
96
97
|
variableExtractor: createVariableExtractor(dartVariableConfig),
|
|
97
98
|
classExtractor: createClassExtractor(dartClassConfig),
|
|
99
|
+
heritageExtractor: createHeritageExtractor(SupportedLanguages.Dart),
|
|
98
100
|
enclosingFunctionFinder: dartEnclosingFunctionFinder,
|
|
99
101
|
builtInNames: BUILT_INS,
|
|
100
102
|
});
|
|
@@ -25,6 +25,8 @@ import { createVariableExtractor } from '../variable-extractors/generic.js';
|
|
|
25
25
|
import { goVariableConfig } from '../variable-extractors/configs/go.js';
|
|
26
26
|
import { createCallExtractor } from '../call-extractors/generic.js';
|
|
27
27
|
import { goCallConfig } from '../call-extractors/configs/go.js';
|
|
28
|
+
import { createHeritageExtractor } from '../heritage-extractors/generic.js';
|
|
29
|
+
import { goHeritageConfig } from '../heritage-extractors/configs/go.js';
|
|
28
30
|
export const goProvider = defineLanguage({
|
|
29
31
|
id: SupportedLanguages.Go,
|
|
30
32
|
extensions: ['.go'],
|
|
@@ -38,4 +40,5 @@ export const goProvider = defineLanguage({
|
|
|
38
40
|
methodExtractor: createMethodExtractor(goMethodConfig),
|
|
39
41
|
variableExtractor: createVariableExtractor(goVariableConfig),
|
|
40
42
|
classExtractor: createClassExtractor(goClassConfig),
|
|
43
|
+
heritageExtractor: createHeritageExtractor(goHeritageConfig),
|
|
41
44
|
});
|
|
@@ -24,6 +24,7 @@ import { createMethodExtractor } from '../method-extractors/generic.js';
|
|
|
24
24
|
import { javaMethodConfig } from '../method-extractors/configs/jvm.js';
|
|
25
25
|
import { createVariableExtractor } from '../variable-extractors/generic.js';
|
|
26
26
|
import { javaVariableConfig } from '../variable-extractors/configs/jvm.js';
|
|
27
|
+
import { createHeritageExtractor } from '../heritage-extractors/generic.js';
|
|
27
28
|
export const javaProvider = defineLanguage({
|
|
28
29
|
id: SupportedLanguages.Java,
|
|
29
30
|
extensions: ['.java'],
|
|
@@ -39,4 +40,5 @@ export const javaProvider = defineLanguage({
|
|
|
39
40
|
methodExtractor: createMethodExtractor(javaMethodConfig),
|
|
40
41
|
variableExtractor: createVariableExtractor(javaVariableConfig),
|
|
41
42
|
classExtractor: createClassExtractor(javaClassConfig),
|
|
43
|
+
heritageExtractor: createHeritageExtractor(SupportedLanguages.Java),
|
|
42
44
|
});
|
|
@@ -25,6 +25,7 @@ import { createMethodExtractor } from '../method-extractors/generic.js';
|
|
|
25
25
|
import { kotlinMethodConfig } from '../method-extractors/configs/jvm.js';
|
|
26
26
|
import { createVariableExtractor } from '../variable-extractors/generic.js';
|
|
27
27
|
import { kotlinVariableConfig } from '../variable-extractors/configs/jvm.js';
|
|
28
|
+
import { createHeritageExtractor } from '../heritage-extractors/generic.js';
|
|
28
29
|
/** Check if a Kotlin function_declaration capture is inside a class_body (i.e., a method).
|
|
29
30
|
* Kotlin grammar uses function_declaration for both top-level functions and class methods.
|
|
30
31
|
* Returns true when the captured definition node has a class_body ancestor. */
|
|
@@ -110,6 +111,7 @@ export const kotlinProvider = defineLanguage({
|
|
|
110
111
|
methodExtractor: createMethodExtractor(kotlinMethodConfig),
|
|
111
112
|
variableExtractor: createVariableExtractor(kotlinVariableConfig),
|
|
112
113
|
classExtractor: createClassExtractor(kotlinClassConfig),
|
|
114
|
+
heritageExtractor: createHeritageExtractor(SupportedLanguages.Kotlin),
|
|
113
115
|
builtInNames: BUILT_INS,
|
|
114
116
|
labelOverride: (functionNode, defaultLabel) => {
|
|
115
117
|
if (defaultLabel !== 'Function')
|
|
@@ -24,6 +24,7 @@ import { createVariableExtractor } from '../variable-extractors/generic.js';
|
|
|
24
24
|
import { phpVariableConfig } from '../variable-extractors/configs/php.js';
|
|
25
25
|
import { createCallExtractor } from '../call-extractors/generic.js';
|
|
26
26
|
import { phpCallConfig } from '../call-extractors/configs/php.js';
|
|
27
|
+
import { createHeritageExtractor } from '../heritage-extractors/generic.js';
|
|
27
28
|
const BUILT_INS = new Set([
|
|
28
29
|
'echo',
|
|
29
30
|
'isset',
|
|
@@ -232,6 +233,7 @@ export const phpProvider = defineLanguage({
|
|
|
232
233
|
methodExtractor: createMethodExtractor(phpMethodConfig),
|
|
233
234
|
variableExtractor: createVariableExtractor(phpVariableConfig),
|
|
234
235
|
classExtractor: createClassExtractor(phpClassConfig),
|
|
236
|
+
heritageExtractor: createHeritageExtractor(SupportedLanguages.PHP),
|
|
235
237
|
descriptionExtractor: phpDescriptionExtractor,
|
|
236
238
|
isRouteFile: isPhpRouteFile,
|
|
237
239
|
builtInNames: BUILT_INS,
|
|
@@ -27,6 +27,7 @@ import { createVariableExtractor } from '../variable-extractors/generic.js';
|
|
|
27
27
|
import { pythonVariableConfig } from '../variable-extractors/configs/python.js';
|
|
28
28
|
import { createCallExtractor } from '../call-extractors/generic.js';
|
|
29
29
|
import { pythonCallConfig } from '../call-extractors/configs/python.js';
|
|
30
|
+
import { createHeritageExtractor } from '../heritage-extractors/generic.js';
|
|
30
31
|
const BUILT_INS = new Set([
|
|
31
32
|
'print',
|
|
32
33
|
'len',
|
|
@@ -71,5 +72,6 @@ export const pythonProvider = defineLanguage({
|
|
|
71
72
|
methodExtractor: createMethodExtractor(pythonMethodConfig),
|
|
72
73
|
variableExtractor: createVariableExtractor(pythonVariableConfig),
|
|
73
74
|
classExtractor: createClassExtractor(pythonClassConfig),
|
|
75
|
+
heritageExtractor: createHeritageExtractor(SupportedLanguages.Python),
|
|
74
76
|
builtInNames: BUILT_INS,
|
|
75
77
|
});
|
|
@@ -24,6 +24,27 @@ import { createVariableExtractor } from '../variable-extractors/generic.js';
|
|
|
24
24
|
import { rubyVariableConfig } from '../variable-extractors/configs/ruby.js';
|
|
25
25
|
import { createCallExtractor } from '../call-extractors/generic.js';
|
|
26
26
|
import { rubyCallConfig } from '../call-extractors/configs/ruby.js';
|
|
27
|
+
import { createHeritageExtractor } from '../heritage-extractors/generic.js';
|
|
28
|
+
import { rubyHeritageConfig } from '../heritage-extractors/configs/ruby.js';
|
|
29
|
+
import { maybeRewriteRubyBareCallToSelf } from '../utils/ruby-self-call.js';
|
|
30
|
+
import { findEnclosingClassInfo } from '../utils/ast-helpers.js';
|
|
31
|
+
/**
|
|
32
|
+
* Ruby label override. Applied to:
|
|
33
|
+
* - `definition.module` captures in the structure phase — remaps to `Trait`
|
|
34
|
+
* so Ruby modules are registered in the class-like type registry and are
|
|
35
|
+
* therefore resolvable by `lookupClassByName` during mixin heritage
|
|
36
|
+
* resolution (`include`/`extend`/`prepend`).
|
|
37
|
+
* - `definition.function` captures — Ruby has no bare "function" construct
|
|
38
|
+
* (top-level `def` is a method on `main`); return the default so generic
|
|
39
|
+
* logic continues to apply.
|
|
40
|
+
*
|
|
41
|
+
* Returning `null` means "skip this definition"; we never do that here.
|
|
42
|
+
*/
|
|
43
|
+
const rubyLabelOverride = (_node, defaultLabel) => {
|
|
44
|
+
if (defaultLabel === 'Module')
|
|
45
|
+
return 'Trait';
|
|
46
|
+
return defaultLabel;
|
|
47
|
+
};
|
|
27
48
|
/** Ruby method/singleton_method: extract name from 'name' field, label as Method. */
|
|
28
49
|
const rubyExtractFunctionName = (node) => {
|
|
29
50
|
if (node.type !== 'method' && node.type !== 'singleton_method')
|
|
@@ -97,6 +118,26 @@ const BUILT_INS = new Set([
|
|
|
97
118
|
'flatten',
|
|
98
119
|
'uniq',
|
|
99
120
|
]);
|
|
121
|
+
/**
|
|
122
|
+
* Remaps `class << self` (singleton_class) to its enclosing class/module for
|
|
123
|
+
* receiver inference. A `singleton_class` node is not itself a type — walking
|
|
124
|
+
* up to the real owner lets `inferImplicitReceiver` set `hint='singleton'`.
|
|
125
|
+
* Returns null for orphaned singleton_class (no enclosing class/module found).
|
|
126
|
+
* All other container types are returned as-is.
|
|
127
|
+
*/
|
|
128
|
+
const rubyResolveEnclosingOwner = (node) => {
|
|
129
|
+
if (node.type === 'singleton_class') {
|
|
130
|
+
let ancestor = node.parent;
|
|
131
|
+
while (ancestor) {
|
|
132
|
+
if (ancestor.type === 'class' || ancestor.type === 'module') {
|
|
133
|
+
return ancestor;
|
|
134
|
+
}
|
|
135
|
+
ancestor = ancestor.parent;
|
|
136
|
+
}
|
|
137
|
+
return null; // no enclosing class/module — skip
|
|
138
|
+
}
|
|
139
|
+
return node; // use as-is for all other container types
|
|
140
|
+
};
|
|
100
141
|
export const rubyProvider = defineLanguage({
|
|
101
142
|
id: SupportedLanguages.Ruby,
|
|
102
143
|
extensions: ['.rb', '.rake', '.gemspec'],
|
|
@@ -107,21 +148,7 @@ export const rubyProvider = defineLanguage({
|
|
|
107
148
|
callRouter: routeRubyCall,
|
|
108
149
|
importSemantics: 'wildcard-leaf',
|
|
109
150
|
callExtractor: createCallExtractor(rubyCallConfig),
|
|
110
|
-
resolveEnclosingOwner
|
|
111
|
-
// Ruby singleton_class (class << self) should resolve to the enclosing
|
|
112
|
-
// class or module for owner/container resolution (HAS_METHOD edges, class IDs).
|
|
113
|
-
if (node.type === 'singleton_class') {
|
|
114
|
-
let ancestor = node.parent;
|
|
115
|
-
while (ancestor) {
|
|
116
|
-
if (ancestor.type === 'class' || ancestor.type === 'module') {
|
|
117
|
-
return ancestor;
|
|
118
|
-
}
|
|
119
|
-
ancestor = ancestor.parent;
|
|
120
|
-
}
|
|
121
|
-
return null; // no enclosing class/module — skip
|
|
122
|
-
}
|
|
123
|
-
return node; // use as-is for all other container types
|
|
124
|
-
},
|
|
151
|
+
resolveEnclosingOwner: rubyResolveEnclosingOwner,
|
|
125
152
|
fieldExtractor: createFieldExtractor(rubyFieldConfig),
|
|
126
153
|
methodExtractor: createMethodExtractor({
|
|
127
154
|
...rubyMethodConfig,
|
|
@@ -129,5 +156,55 @@ export const rubyProvider = defineLanguage({
|
|
|
129
156
|
}),
|
|
130
157
|
variableExtractor: createVariableExtractor(rubyVariableConfig),
|
|
131
158
|
classExtractor: createClassExtractor(rubyClassConfig),
|
|
159
|
+
heritageExtractor: createHeritageExtractor(rubyHeritageConfig),
|
|
160
|
+
labelOverride: rubyLabelOverride,
|
|
161
|
+
// Ruby MRO is kind-aware: prepend providers beat the class's own method,
|
|
162
|
+
// which in turn beats include providers. See `lookupMethodByOwnerWithMRO`
|
|
163
|
+
// in `model/resolve.ts` for the walk order.
|
|
164
|
+
mroStrategy: 'ruby-mixin',
|
|
165
|
+
// ── DAG hooks ────────────────────────────────────────────────────
|
|
166
|
+
//
|
|
167
|
+
// DAG stage 3: rewrite bare calls (e.g. `serialize` in Account#call_serialize)
|
|
168
|
+
// as `self.serialize` so they route through owner-scoped MRO instead of
|
|
169
|
+
// global free-call lookup. `dispatchKind` goes into `hint` for stage 4.
|
|
170
|
+
inferImplicitReceiver: ({ calledName, callForm, receiverName, receiverTypeName, callNode, filePath, }) => {
|
|
171
|
+
// Only fire when no receiver has been resolved already.
|
|
172
|
+
if (receiverName || receiverTypeName)
|
|
173
|
+
return null;
|
|
174
|
+
const enclosing = findEnclosingClassInfo(callNode, filePath, rubyResolveEnclosingOwner);
|
|
175
|
+
const rewrite = maybeRewriteRubyBareCallToSelf(calledName, callForm, callNode, enclosing?.className ?? null, { isBuiltInName: (n) => BUILT_INS.has(n), mroStrategy: 'ruby-mixin' });
|
|
176
|
+
if (!rewrite)
|
|
177
|
+
return null;
|
|
178
|
+
return {
|
|
179
|
+
callForm: rewrite.callForm,
|
|
180
|
+
receiverName: rewrite.receiverName,
|
|
181
|
+
receiverTypeName: rewrite.receiverTypeName,
|
|
182
|
+
receiverSource: 'implicit-self',
|
|
183
|
+
hint: rewrite.dispatchKind, // 'instance' | 'singleton'
|
|
184
|
+
};
|
|
185
|
+
},
|
|
186
|
+
// DAG stage 4: two Ruby dispatch overrides —
|
|
187
|
+
// implicit-self: MRO walk first, fallback to free-arity-narrowed on miss.
|
|
188
|
+
// class-as-receiver: singleton ancestry (extend providers only); miss null-routes.
|
|
189
|
+
selectDispatch: ({ receiverSource, hint }) => {
|
|
190
|
+
if (receiverSource === 'implicit-self') {
|
|
191
|
+
// hint='instance' → instance ancestry (prepend→direct→include, see mro-strategy.ts § 'ruby-mixin')
|
|
192
|
+
// hint='singleton' → singleton ancestry (extend providers only; miss null-routes)
|
|
193
|
+
const ancestryView = hint === 'singleton' ? 'singleton' : 'instance';
|
|
194
|
+
return {
|
|
195
|
+
primary: 'owner-scoped',
|
|
196
|
+
fallback: 'free-arity-narrowed',
|
|
197
|
+
ancestryView,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
if (receiverSource === 'class-as-receiver') {
|
|
201
|
+
// Class constant receiver (e.g. Account.log): singleton ancestry only; miss null-routes.
|
|
202
|
+
return {
|
|
203
|
+
primary: 'owner-scoped',
|
|
204
|
+
ancestryView: 'singleton',
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
return null;
|
|
208
|
+
},
|
|
132
209
|
builtInNames: BUILT_INS,
|
|
133
210
|
});
|
|
@@ -27,6 +27,7 @@ import { createVariableExtractor } from '../variable-extractors/generic.js';
|
|
|
27
27
|
import { rustVariableConfig } from '../variable-extractors/configs/rust.js';
|
|
28
28
|
import { createCallExtractor } from '../call-extractors/generic.js';
|
|
29
29
|
import { rustCallConfig } from '../call-extractors/configs/rust.js';
|
|
30
|
+
import { createHeritageExtractor } from '../heritage-extractors/generic.js';
|
|
30
31
|
/** Rust impl_item: find the function_item child and extract its name as a Method. */
|
|
31
32
|
const rustExtractFunctionName = (node) => {
|
|
32
33
|
if (node.type !== 'impl_item')
|
|
@@ -126,5 +127,6 @@ export const rustProvider = defineLanguage({
|
|
|
126
127
|
}),
|
|
127
128
|
variableExtractor: createVariableExtractor(rustVariableConfig),
|
|
128
129
|
classExtractor: createClassExtractor(rustClassConfig),
|
|
130
|
+
heritageExtractor: createHeritageExtractor(SupportedLanguages.Rust),
|
|
129
131
|
builtInNames: BUILT_INS,
|
|
130
132
|
});
|
|
@@ -26,6 +26,7 @@ import { createVariableExtractor } from '../variable-extractors/generic.js';
|
|
|
26
26
|
import { swiftVariableConfig } from '../variable-extractors/configs/swift.js';
|
|
27
27
|
import { createCallExtractor } from '../call-extractors/generic.js';
|
|
28
28
|
import { swiftCallConfig } from '../call-extractors/configs/swift.js';
|
|
29
|
+
import { createHeritageExtractor } from '../heritage-extractors/generic.js';
|
|
29
30
|
/**
|
|
30
31
|
* Group Swift files by SPM target for implicit module visibility.
|
|
31
32
|
* If SwiftPackageConfig is available, use target -> directory mappings.
|
|
@@ -237,6 +238,7 @@ export const swiftProvider = defineLanguage({
|
|
|
237
238
|
}),
|
|
238
239
|
variableExtractor: createVariableExtractor(swiftVariableConfig),
|
|
239
240
|
classExtractor: createClassExtractor(swiftClassConfig),
|
|
241
|
+
heritageExtractor: createHeritageExtractor(SupportedLanguages.Swift),
|
|
240
242
|
implicitImportWirer: wireSwiftImplicitImports,
|
|
241
243
|
builtInNames: BUILT_INS,
|
|
242
244
|
});
|
|
@@ -25,6 +25,7 @@ import { createVariableExtractor } from '../variable-extractors/generic.js';
|
|
|
25
25
|
import { typescriptVariableConfig, javascriptVariableConfig, } from '../variable-extractors/configs/typescript-javascript.js';
|
|
26
26
|
import { createCallExtractor } from '../call-extractors/generic.js';
|
|
27
27
|
import { typescriptCallConfig, javascriptCallConfig, } from '../call-extractors/configs/typescript-javascript.js';
|
|
28
|
+
import { createHeritageExtractor } from '../heritage-extractors/generic.js';
|
|
28
29
|
/**
|
|
29
30
|
* TypeScript/JavaScript: arrow_function and function_expression get their name
|
|
30
31
|
* from the parent variable_declarator (e.g. `const foo = () => {}`).
|
|
@@ -159,6 +160,7 @@ export const typescriptProvider = defineLanguage({
|
|
|
159
160
|
}),
|
|
160
161
|
variableExtractor: createVariableExtractor(typescriptVariableConfig),
|
|
161
162
|
classExtractor: createClassExtractor(typescriptClassConfig),
|
|
163
|
+
heritageExtractor: createHeritageExtractor(SupportedLanguages.TypeScript),
|
|
162
164
|
builtInNames: BUILT_INS,
|
|
163
165
|
});
|
|
164
166
|
export const javascriptProvider = defineLanguage({
|
|
@@ -177,5 +179,6 @@ export const javascriptProvider = defineLanguage({
|
|
|
177
179
|
}),
|
|
178
180
|
variableExtractor: createVariableExtractor(javascriptVariableConfig),
|
|
179
181
|
classExtractor: createClassExtractor(javascriptClassConfig),
|
|
182
|
+
heritageExtractor: createHeritageExtractor(SupportedLanguages.JavaScript),
|
|
180
183
|
builtInNames: BUILT_INS,
|
|
181
184
|
});
|
|
@@ -26,6 +26,7 @@ import { createVariableExtractor } from '../variable-extractors/generic.js';
|
|
|
26
26
|
import { typescriptVariableConfig } from '../variable-extractors/configs/typescript-javascript.js';
|
|
27
27
|
import { createCallExtractor } from '../call-extractors/generic.js';
|
|
28
28
|
import { typescriptCallConfig } from '../call-extractors/configs/typescript-javascript.js';
|
|
29
|
+
import { createHeritageExtractor } from '../heritage-extractors/generic.js';
|
|
29
30
|
const VUE_SPECIFIC_BUILT_INS = [
|
|
30
31
|
'ref',
|
|
31
32
|
'reactive',
|
|
@@ -71,5 +72,6 @@ export const vueProvider = defineLanguage({
|
|
|
71
72
|
fieldExtractor: typescriptFieldExtractor,
|
|
72
73
|
variableExtractor: createVariableExtractor(typescriptVariableConfig),
|
|
73
74
|
classExtractor: vueClassExtractor,
|
|
75
|
+
heritageExtractor: createHeritageExtractor(SupportedLanguages.TypeScript),
|
|
74
76
|
builtInNames: VUE_BUILT_INS,
|
|
75
77
|
});
|
|
@@ -43,11 +43,46 @@ export declare const resolveExtendsType: (parentName: string, currentFilePath: s
|
|
|
43
43
|
type: "EXTENDS" | "IMPLEMENTS";
|
|
44
44
|
idPrefix: string;
|
|
45
45
|
};
|
|
46
|
+
/**
|
|
47
|
+
* Direct parent entry with the heritage kind that produced it. Preserved
|
|
48
|
+
* so kind-aware consumers (Ruby MRO, see `lookupMethodByOwnerWithMRO`) can
|
|
49
|
+
* walk prepend/include providers in the correct order. Flat-string consumers
|
|
50
|
+
* use `getParents` / `getAncestors` and see only the parent nodeIds.
|
|
51
|
+
*/
|
|
52
|
+
export interface ParentEntry {
|
|
53
|
+
readonly parentId: string;
|
|
54
|
+
/** 'extends' | 'implements' | 'trait-impl' | 'include' | 'extend' | 'prepend' */
|
|
55
|
+
readonly kind: string;
|
|
56
|
+
}
|
|
46
57
|
export interface HeritageMap {
|
|
47
58
|
/** Direct parents of `childNodeId` (extends + implements + trait-impl). */
|
|
48
59
|
getParents(childNodeId: string): string[];
|
|
49
60
|
/** Full ancestor chain (BFS, bounded depth, cycle-safe). */
|
|
50
61
|
getAncestors(childNodeId: string): string[];
|
|
62
|
+
/**
|
|
63
|
+
* Direct parents with heritage kind preserved, insertion-ordered. Used by
|
|
64
|
+
* kind-aware consumers (Ruby MRO) that need to distinguish prepend /
|
|
65
|
+
* include / extend / extends for walk-order decisions.
|
|
66
|
+
*
|
|
67
|
+
* Insertion order mirrors the order `ExtractedHeritage` records were fed
|
|
68
|
+
* into `buildHeritageMap`, which in turn mirrors tree-sitter match order.
|
|
69
|
+
* For Ruby, this matches source declaration order for `prepend` / `include`
|
|
70
|
+
* statements — the MRO walk reverses this (last-declared-first) at the
|
|
71
|
+
* consumer side.
|
|
72
|
+
*/
|
|
73
|
+
getParentEntries(childNodeId: string): readonly ParentEntry[];
|
|
74
|
+
/**
|
|
75
|
+
* Ordered ancestry for instance method dispatch (Ruby-aware): includes
|
|
76
|
+
* `extends`, `implements`, `trait-impl`, `include`, `prepend` kinds.
|
|
77
|
+
* Excludes `extend` (singleton-only). Order is caller-determined in Unit 3.
|
|
78
|
+
* For non-Ruby callers (first-wins, c3, etc.), this matches `getAncestors`.
|
|
79
|
+
*/
|
|
80
|
+
getInstanceAncestry(childNodeId: string): readonly ParentEntry[];
|
|
81
|
+
/**
|
|
82
|
+
* Ordered ancestry for singleton / class-method dispatch (Ruby-aware):
|
|
83
|
+
* only `extend` kind parents. For non-Ruby languages this is always empty.
|
|
84
|
+
*/
|
|
85
|
+
getSingletonAncestry(childNodeId: string): readonly ParentEntry[];
|
|
51
86
|
/**
|
|
52
87
|
* File paths of classes that directly implement or extend-as-interface the
|
|
53
88
|
* given interface/abstract-class **name**. Replaces the standalone
|
|
@@ -64,8 +64,12 @@ const DEFAULT_HERITAGE_STRATEGY = { defaultEdge: 'EXTENDS' };
|
|
|
64
64
|
* paths) used by interface-dispatch in call resolution.
|
|
65
65
|
*/
|
|
66
66
|
export const buildHeritageMap = (heritage, ctx, getHeritageStrategy) => {
|
|
67
|
-
// childNodeId →
|
|
67
|
+
// childNodeId → insertion-ordered array of { parentId, kind }.
|
|
68
|
+
// Ordered array (not Set) because Ruby MRO walk depends on declaration
|
|
69
|
+
// order. A parallel `seen` map dedupes `(parentId, kind)` pairs without
|
|
70
|
+
// losing order.
|
|
68
71
|
const directParents = new Map();
|
|
72
|
+
const seenParents = new Map();
|
|
69
73
|
// interfaceName → Set<filePath> (implementor lookup for interface dispatch)
|
|
70
74
|
const implementorFiles = new Map();
|
|
71
75
|
for (const h of heritage) {
|
|
@@ -80,10 +84,23 @@ export const buildHeritageMap = (heritage, ctx, getHeritageStrategy) => {
|
|
|
80
84
|
continue;
|
|
81
85
|
let parents = directParents.get(child.nodeId);
|
|
82
86
|
if (!parents) {
|
|
83
|
-
parents =
|
|
87
|
+
parents = [];
|
|
84
88
|
directParents.set(child.nodeId, parents);
|
|
85
89
|
}
|
|
86
|
-
|
|
90
|
+
let seen = seenParents.get(child.nodeId);
|
|
91
|
+
if (!seen) {
|
|
92
|
+
seen = new Set();
|
|
93
|
+
seenParents.set(child.nodeId, seen);
|
|
94
|
+
}
|
|
95
|
+
// Dedup by `parentId + kind` so the same parent under two different
|
|
96
|
+
// kinds (e.g. a module that is both included and prepended — legal
|
|
97
|
+
// Ruby though unusual) is recorded twice; the consumer needs both
|
|
98
|
+
// kinds in the walk. A single (parent, kind) pair is deduped.
|
|
99
|
+
const key = `${parent.nodeId}|${h.kind}`;
|
|
100
|
+
if (!seen.has(key)) {
|
|
101
|
+
seen.add(key);
|
|
102
|
+
parents.push({ parentId: parent.nodeId, kind: h.kind });
|
|
103
|
+
}
|
|
87
104
|
}
|
|
88
105
|
}
|
|
89
106
|
}
|
|
@@ -120,9 +137,28 @@ export const buildHeritageMap = (heritage, ctx, getHeritageStrategy) => {
|
|
|
120
137
|
}
|
|
121
138
|
}
|
|
122
139
|
// --- Public API ---------------------------------------------------
|
|
140
|
+
/** Internal helper: return the entries array (may be undefined). */
|
|
141
|
+
const entriesFor = (nodeId) => directParents.get(nodeId);
|
|
142
|
+
const getParentEntries = (childNodeId) => {
|
|
143
|
+
const entries = entriesFor(childNodeId);
|
|
144
|
+
return entries ?? [];
|
|
145
|
+
};
|
|
123
146
|
const getParents = (childNodeId) => {
|
|
124
|
-
const
|
|
125
|
-
|
|
147
|
+
const entries = entriesFor(childNodeId);
|
|
148
|
+
if (!entries)
|
|
149
|
+
return [];
|
|
150
|
+
// Deduplicate parent ids across kinds so the flat-string contract
|
|
151
|
+
// (used by non-Ruby MRO strategies and by the C3 linearizer) stays
|
|
152
|
+
// identical to its pre-kind-awareness behavior.
|
|
153
|
+
const out = [];
|
|
154
|
+
const seen = new Set();
|
|
155
|
+
for (const e of entries) {
|
|
156
|
+
if (!seen.has(e.parentId)) {
|
|
157
|
+
seen.add(e.parentId);
|
|
158
|
+
out.push(e.parentId);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return out;
|
|
126
162
|
};
|
|
127
163
|
const getAncestors = (childNodeId) => {
|
|
128
164
|
const result = [];
|
|
@@ -139,11 +175,15 @@ export const buildHeritageMap = (heritage, ctx, getHeritageStrategy) => {
|
|
|
139
175
|
visited.add(parentId);
|
|
140
176
|
result.push(parentId);
|
|
141
177
|
// Expand parent's own parents for next level
|
|
142
|
-
const grandparents =
|
|
178
|
+
const grandparents = entriesFor(parentId);
|
|
143
179
|
if (grandparents) {
|
|
180
|
+
const gpSeen = new Set();
|
|
144
181
|
for (const gp of grandparents) {
|
|
145
|
-
if (
|
|
146
|
-
|
|
182
|
+
if (gpSeen.has(gp.parentId))
|
|
183
|
+
continue;
|
|
184
|
+
gpSeen.add(gp.parentId);
|
|
185
|
+
if (!visited.has(gp.parentId))
|
|
186
|
+
nextFrontier.push(gp.parentId);
|
|
147
187
|
}
|
|
148
188
|
}
|
|
149
189
|
}
|
|
@@ -152,8 +192,69 @@ export const buildHeritageMap = (heritage, ctx, getHeritageStrategy) => {
|
|
|
152
192
|
}
|
|
153
193
|
return result;
|
|
154
194
|
};
|
|
195
|
+
/**
|
|
196
|
+
* Lazy-computed per-owner split of direct parents into instance-dispatch
|
|
197
|
+
* (non-`extend`) and singleton-dispatch (`extend`-only) views. Memoized on
|
|
198
|
+
* first request so the `.filter()` pass happens at most once per owner per
|
|
199
|
+
* HeritageMap lifetime, not per call-site dispatch.
|
|
200
|
+
*
|
|
201
|
+
* Shared empty-array sentinels for owners with no entries in a given view
|
|
202
|
+
* avoid per-call allocation when the split is asymmetric (common Ruby case:
|
|
203
|
+
* a class has `include` but no `extend`, so its singleton view is empty).
|
|
204
|
+
*/
|
|
205
|
+
const EMPTY_PARENT_ENTRIES = [];
|
|
206
|
+
const splitCache = new Map();
|
|
207
|
+
const splitForOwner = (childNodeId) => {
|
|
208
|
+
let cached = splitCache.get(childNodeId);
|
|
209
|
+
if (cached)
|
|
210
|
+
return cached;
|
|
211
|
+
const entries = entriesFor(childNodeId);
|
|
212
|
+
if (!entries || entries.length === 0) {
|
|
213
|
+
cached = { instance: EMPTY_PARENT_ENTRIES, singleton: EMPTY_PARENT_ENTRIES };
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
const instance = [];
|
|
217
|
+
const singleton = [];
|
|
218
|
+
for (const e of entries) {
|
|
219
|
+
if (e.kind === 'extend')
|
|
220
|
+
singleton.push(e);
|
|
221
|
+
else
|
|
222
|
+
instance.push(e);
|
|
223
|
+
}
|
|
224
|
+
cached = {
|
|
225
|
+
instance: instance.length === 0 ? EMPTY_PARENT_ENTRIES : instance,
|
|
226
|
+
singleton: singleton.length === 0 ? EMPTY_PARENT_ENTRIES : singleton,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
splitCache.set(childNodeId, cached);
|
|
230
|
+
return cached;
|
|
231
|
+
};
|
|
232
|
+
/**
|
|
233
|
+
* Instance-dispatch ancestry walk. Excludes `extend` (singleton-only).
|
|
234
|
+
* For kind-aware consumers (Ruby MRO): walks parents in source-insertion
|
|
235
|
+
* order. The consumer is responsible for interleaving self / reversing
|
|
236
|
+
* prepend order / etc. This method preserves raw declaration order.
|
|
237
|
+
*
|
|
238
|
+
* Result is cached per owner; repeat calls return the same array.
|
|
239
|
+
*/
|
|
240
|
+
const getInstanceAncestry = (childNodeId) => splitForOwner(childNodeId).instance;
|
|
241
|
+
/**
|
|
242
|
+
* Singleton-dispatch ancestry walk. Only `extend` parents. For non-Ruby
|
|
243
|
+
* languages this is always empty (no language currently produces `extend`
|
|
244
|
+
* heritage records outside Ruby).
|
|
245
|
+
*
|
|
246
|
+
* Result is cached per owner; repeat calls return the same array.
|
|
247
|
+
*/
|
|
248
|
+
const getSingletonAncestry = (childNodeId) => splitForOwner(childNodeId).singleton;
|
|
155
249
|
const getImplementorFiles = (interfaceName) => {
|
|
156
250
|
return implementorFiles.get(interfaceName) ?? EMPTY_SET;
|
|
157
251
|
};
|
|
158
|
-
return {
|
|
252
|
+
return {
|
|
253
|
+
getParents,
|
|
254
|
+
getAncestors,
|
|
255
|
+
getParentEntries,
|
|
256
|
+
getInstanceAncestry,
|
|
257
|
+
getSingletonAncestry,
|
|
258
|
+
getImplementorFiles,
|
|
259
|
+
};
|
|
159
260
|
};
|
|
@@ -28,32 +28,34 @@ declare function gatherAncestors(classId: string, parentMap: Map<string, string[
|
|
|
28
28
|
export declare function c3Linearize(classId: string, parentMap: Map<string, string[]>, cache: Map<string, string[] | null>, inProgress?: Set<string>): string[] | null;
|
|
29
29
|
export { gatherAncestors };
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* - `
|
|
40
|
-
* - `
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
31
|
+
* DAG stage 5 helper: look up a method on an owner class via MRO walk.
|
|
32
|
+
*
|
|
33
|
+
* Low-level resolver; no dependency on SymbolTable, language registry, or
|
|
34
|
+
* resolution-context (keeps model/ layer free of cross-layer imports).
|
|
35
|
+
* All strategies respect `argCount` for overload narrowing.
|
|
36
|
+
* `ancestryOverride` replaces the default walk; caller must compute it correctly.
|
|
37
|
+
*
|
|
38
|
+
* Strategy summary (full docs in gitnexus-shared/mro-strategy.ts):
|
|
39
|
+
* - `first-wins` / `leftmost-base` / `implements-split`: BFS, first match wins.
|
|
40
|
+
* - `c3`: C3-linearized order; falls back to BFS on cycle/inconsistency.
|
|
41
|
+
* - `qualified-syntax`: returns undefined immediately (Rust requires explicit syntax).
|
|
42
|
+
* - `ruby-mixin`: kind-aware walk — see inline comments below.
|
|
43
|
+
*
|
|
44
|
+
* Internal API: exported for call-processor resolvers and tests.
|
|
45
|
+
* External callers should use resolveMemberCall instead.
|
|
46
|
+
*
|
|
47
|
+
* @see gitnexus-shared/mro-strategy.ts § 'ruby-mixin'
|
|
48
|
+
* @see call-processor.ts § resolveMemberCall
|
|
49
|
+
*/
|
|
50
|
+
export declare const lookupMethodByOwnerWithMRO: (ownerNodeId: string, methodName: string, heritageMap: HeritageMap, model: SemanticModel, strategy: MroStrategy, argCount?: number,
|
|
51
|
+
/**
|
|
52
|
+
* Optional pre-computed ancestry list. When provided, overrides the default
|
|
53
|
+
* per-strategy ancestry source. Primarily used by Ruby singleton dispatch:
|
|
54
|
+
* the caller supplies `heritageMap.getSingletonAncestry(ownerNodeId)` as
|
|
55
|
+
* node-id array so this walker resolves against `extend` providers only.
|
|
56
|
+
*
|
|
57
|
+
* For `ruby-mixin` strategy, passing an override switches the walker into
|
|
58
|
+
* a no-prepend-no-direct linear scan (the caller has already decided the
|
|
59
|
+
* order), which is the correct semantics for singleton dispatch.
|
|
58
60
|
*/
|
|
59
|
-
|
|
61
|
+
ancestryOverride?: readonly string[]) => SymbolDefinition | undefined;
|