gitnexus 1.6.3 → 1.6.4-rc.10
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/index.d.ts +1 -1
- package/dist/_shared/index.d.ts.map +1 -1
- package/dist/_shared/index.js +1 -1
- package/dist/_shared/index.js.map +1 -1
- package/dist/_shared/scope-resolution/finalize-algorithm.d.ts +22 -14
- package/dist/_shared/scope-resolution/finalize-algorithm.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/finalize-algorithm.js +298 -37
- package/dist/_shared/scope-resolution/finalize-algorithm.js.map +1 -1
- package/dist/_shared/scope-resolution/scope-tree.d.ts +23 -1
- package/dist/_shared/scope-resolution/scope-tree.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/scope-tree.js +36 -2
- package/dist/_shared/scope-resolution/scope-tree.js.map +1 -1
- package/dist/_shared/scope-resolution/types.d.ts +47 -3
- package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/types.js +10 -2
- package/dist/_shared/scope-resolution/types.js.map +1 -1
- package/dist/core/embeddings/embedder.js +2 -1
- package/dist/core/ingestion/call-processor.js +2 -2
- package/dist/core/ingestion/constants.d.ts +4 -3
- package/dist/core/ingestion/constants.js +8 -3
- package/dist/core/ingestion/finalize-orchestrator.js +6 -3
- package/dist/core/ingestion/heritage-processor.js +2 -2
- package/dist/core/ingestion/import-processor.js +1 -1
- package/dist/core/ingestion/languages/csharp/captures.js +4 -1
- package/dist/core/ingestion/languages/csharp/namespace-siblings.d.ts +14 -13
- package/dist/core/ingestion/languages/csharp/namespace-siblings.js +62 -50
- package/dist/core/ingestion/languages/python/captures.js +9 -1
- package/dist/core/ingestion/languages/python/index.d.ts +1 -1
- package/dist/core/ingestion/languages/python/index.js +1 -1
- package/dist/core/ingestion/languages/python/simple-hooks.d.ts +3 -1
- package/dist/core/ingestion/languages/python/simple-hooks.js +8 -0
- package/dist/core/ingestion/languages/python.js +2 -1
- package/dist/core/ingestion/languages/typescript/arity-metadata.d.ts +59 -0
- package/dist/core/ingestion/languages/typescript/arity-metadata.js +103 -0
- package/dist/core/ingestion/languages/typescript/arity.d.ts +37 -0
- package/dist/core/ingestion/languages/typescript/arity.js +54 -0
- package/dist/core/ingestion/languages/typescript/cache-stats.d.ts +17 -0
- package/dist/core/ingestion/languages/typescript/cache-stats.js +28 -0
- package/dist/core/ingestion/languages/typescript/captures.d.ts +28 -0
- package/dist/core/ingestion/languages/typescript/captures.js +451 -0
- package/dist/core/ingestion/languages/typescript/import-decomposer.d.ts +49 -0
- package/dist/core/ingestion/languages/typescript/import-decomposer.js +371 -0
- package/dist/core/ingestion/languages/typescript/import-target.d.ts +50 -0
- package/dist/core/ingestion/languages/typescript/import-target.js +61 -0
- package/dist/core/ingestion/languages/typescript/index.d.ts +94 -0
- package/dist/core/ingestion/languages/typescript/index.js +94 -0
- package/dist/core/ingestion/languages/typescript/interpret.d.ts +35 -0
- package/dist/core/ingestion/languages/typescript/interpret.js +317 -0
- package/dist/core/ingestion/languages/typescript/merge-bindings.d.ts +62 -0
- package/dist/core/ingestion/languages/typescript/merge-bindings.js +158 -0
- package/dist/core/ingestion/languages/typescript/query.d.ts +77 -0
- package/dist/core/ingestion/languages/typescript/query.js +778 -0
- package/dist/core/ingestion/languages/typescript/receiver-binding.d.ts +59 -0
- package/dist/core/ingestion/languages/typescript/receiver-binding.js +171 -0
- package/dist/core/ingestion/languages/typescript/scope-resolver.d.ts +16 -0
- package/dist/core/ingestion/languages/typescript/scope-resolver.js +113 -0
- package/dist/core/ingestion/languages/typescript/simple-hooks.d.ts +71 -0
- package/dist/core/ingestion/languages/typescript/simple-hooks.js +131 -0
- package/dist/core/ingestion/languages/typescript.js +19 -0
- package/dist/core/ingestion/model/scope-resolution-indexes.d.ts +14 -1
- package/dist/core/ingestion/parsing-processor.js +3 -3
- package/dist/core/ingestion/registry-primary-flag.d.ts +3 -1
- package/dist/core/ingestion/registry-primary-flag.js +4 -1
- package/dist/core/ingestion/scope-extractor-bridge.d.ts +5 -2
- package/dist/core/ingestion/scope-extractor-bridge.js +7 -2
- package/dist/core/ingestion/scope-extractor.js +19 -18
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +73 -11
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.js +48 -10
- package/dist/core/ingestion/scope-resolution/passes/compound-receiver.js +283 -14
- package/dist/core/ingestion/scope-resolution/passes/imported-return-types.d.ts +23 -2
- package/dist/core/ingestion/scope-resolution/passes/imported-return-types.js +109 -37
- package/dist/core/ingestion/scope-resolution/passes/mro.js +3 -1
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +13 -5
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +11 -2
- package/dist/core/ingestion/scope-resolution/pipeline/registry.js +2 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +8 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +21 -5
- package/dist/core/ingestion/scope-resolution/pipeline/validate-bindings-immutability.d.ts +39 -0
- package/dist/core/ingestion/scope-resolution/pipeline/validate-bindings-immutability.js +65 -0
- package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +54 -11
- package/dist/core/ingestion/scope-resolution/scope/walkers.js +105 -30
- package/dist/core/ingestion/utils/ast-helpers.d.ts +2 -0
- package/dist/core/ingestion/utils/ast-helpers.js +12 -0
- package/dist/core/ingestion/utils/env.d.ts +10 -0
- package/dist/core/ingestion/utils/env.js +14 -0
- package/dist/core/ingestion/workers/parse-worker.js +3 -3
- package/dist/core/lbug/lbug-adapter.d.ts +3 -4
- package/dist/core/lbug/lbug-adapter.js +6 -9
- package/dist/core/run-analyze.js +4 -6
- package/dist/core/search/bm25-index.d.ts +0 -17
- package/dist/core/search/bm25-index.js +10 -118
- package/dist/core/search/fts-indexes.d.ts +1 -0
- package/dist/core/search/fts-indexes.js +7 -0
- package/dist/core/search/fts-schema.d.ts +6 -0
- package/dist/core/search/fts-schema.js +7 -0
- package/dist/mcp/core/embedder.js +3 -1
- package/package.json +1 -1
- package/skills/gitnexus-cli.md +1 -1
|
@@ -11,17 +11,18 @@
|
|
|
11
11
|
* field-chain resolution fails at `findClassBindingInScope('User')`
|
|
12
12
|
* in the Service.cs scope chain.
|
|
13
13
|
*
|
|
14
|
-
* Implementation: after the finalize pass populates
|
|
15
|
-
* (from explicit `using` directives), walk each
|
|
16
|
-
* AST for `namespace_declaration` /
|
|
17
|
-
* and `using_directive` nodes.
|
|
18
|
-
* `treeCache` so files already parsed
|
|
19
|
-
* re-used instead of re-parsed —
|
|
20
|
-
* the single source of truth.
|
|
21
|
-
* cross-file sibling classes
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
14
|
+
* Implementation: after the finalize pass populates immutable
|
|
15
|
+
* `indexes.bindings` (from explicit `using` directives), walk each
|
|
16
|
+
* file's tree-sitter AST for `namespace_declaration` /
|
|
17
|
+
* `file_scoped_namespace_declaration` and `using_directive` nodes.
|
|
18
|
+
* The orchestrator hands us its `treeCache` so files already parsed
|
|
19
|
+
* by `extractParsedFile` are re-used instead of re-parsed —
|
|
20
|
+
* `ParsedFile`'s underlying tree is the single source of truth.
|
|
21
|
+
* Group classes by namespace, and append cross-file sibling classes
|
|
22
|
+
* into each Namespace scope's `bindingAugmentations` bucket with
|
|
23
|
+
* `origin: 'namespace'`. Finalized bindings remain first in
|
|
24
|
+
* `lookupBindingsAt`, and local lexical `Scope.bindings` remains the
|
|
25
|
+
* first-tier shadowing channel.
|
|
25
26
|
*
|
|
26
27
|
* The tree-sitter walk is authoritative: it sees `global using static`,
|
|
27
28
|
* aliased `using static X = Y.Z;`, attributed namespace declarations,
|
|
@@ -30,13 +31,17 @@
|
|
|
30
31
|
* coincidences).
|
|
31
32
|
*/
|
|
32
33
|
import { getCsharpParser } from './query.js';
|
|
34
|
+
import { getTreeSitterBufferSize } from '../../constants.js';
|
|
33
35
|
/** Build a structural view of a C# file by walking the tree-sitter
|
|
34
36
|
* AST. Prefers `cachedTree` (handed in via `treeCache`) so we don't
|
|
35
37
|
* re-parse files the orchestrator already parsed for `extractParsedFile`;
|
|
36
38
|
* falls back to a fresh parse on cache miss. Parser singleton is
|
|
37
39
|
* shared across calls. */
|
|
38
40
|
function extractFileStructure(content, cachedTree) {
|
|
39
|
-
const tree = cachedTree ??
|
|
41
|
+
const tree = cachedTree ??
|
|
42
|
+
getCsharpParser().parse(content, undefined, {
|
|
43
|
+
bufferSize: getTreeSitterBufferSize(content),
|
|
44
|
+
});
|
|
40
45
|
const namespaces = [];
|
|
41
46
|
const usingStaticPaths = [];
|
|
42
47
|
const visit = (node) => {
|
|
@@ -81,8 +86,8 @@ function extractFileStructure(content, cachedTree) {
|
|
|
81
86
|
return { namespaces, usingStaticPaths };
|
|
82
87
|
}
|
|
83
88
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
89
|
+
* Append cross-file sibling class defs to each Namespace scope's
|
|
90
|
+
* `bindingAugmentations` bucket. Class-like defs (Class / Interface /
|
|
86
91
|
* Struct / Record / Enum) are visible cross-file; method / field
|
|
87
92
|
* members are not.
|
|
88
93
|
*/
|
|
@@ -161,12 +166,15 @@ export function populateCsharpNamespaceSiblings(parsedFiles, indexes, inputs) {
|
|
|
161
166
|
}
|
|
162
167
|
}
|
|
163
168
|
}
|
|
164
|
-
// Inject cross-file siblings into each namespace scope's
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
|
|
169
|
+
// Inject cross-file siblings into each namespace scope's
|
|
170
|
+
// post-finalize augmentation channel (per I8). The
|
|
171
|
+
// `indexes.bindingAugmentations` map is the dedicated mutable
|
|
172
|
+
// append-only buffer for post-finalize hooks: inner `BindingRef[]`
|
|
173
|
+
// arrays here are NEVER frozen (unlike `indexes.bindings`, which
|
|
174
|
+
// `materializeBindings` freezes). Walkers consult both channels
|
|
175
|
+
// via `lookupBindingsAt`; we never need to consult or mutate
|
|
176
|
+
// `indexes.bindings`.
|
|
177
|
+
const augmentations = indexes.bindingAugmentations;
|
|
170
178
|
// Cross-namespace type-binding propagation: for each file, mirror
|
|
171
179
|
// method return-type bindings from same-namespace sibling files and
|
|
172
180
|
// from files in namespaces the importer `using`s, into the
|
|
@@ -268,18 +276,14 @@ export function populateCsharpNamespaceSiblings(parsedFiles, indexes, inputs) {
|
|
|
268
276
|
const simpleName = mq.includes('.') ? mq.slice(mq.lastIndexOf('.') + 1) : mq;
|
|
269
277
|
if (simpleName === '')
|
|
270
278
|
continue;
|
|
271
|
-
//
|
|
272
|
-
// `findCallableBindingInScope`
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
const existing = scopeBindings.get(simpleName) ?? [];
|
|
279
|
-
if (existing.some((b) => b.def.nodeId === memberDef.nodeId))
|
|
279
|
+
// Append to the augmentation bucket for the importer's module
|
|
280
|
+
// scope. `findCallableBindingInScope` reads via
|
|
281
|
+
// `lookupBindingsAt`, which fans out across `bindings` +
|
|
282
|
+
// `bindingAugmentations`.
|
|
283
|
+
const bucketArr = getAugmentationBucket(augmentations, moduleScope.id, simpleName);
|
|
284
|
+
if (bucketArr.some((b) => b.def.nodeId === memberDef.nodeId))
|
|
280
285
|
continue;
|
|
281
|
-
|
|
282
|
-
scopeBindings.set(simpleName, existing);
|
|
286
|
+
bucketArr.push({ def: memberDef, origin: 'import' });
|
|
283
287
|
}
|
|
284
288
|
}
|
|
285
289
|
}
|
|
@@ -310,16 +314,10 @@ export function populateCsharpNamespaceSiblings(parsedFiles, indexes, inputs) {
|
|
|
310
314
|
const simpleName = q.includes('.') ? q.slice(q.lastIndexOf('.') + 1) : q;
|
|
311
315
|
if (simpleName === '')
|
|
312
316
|
continue;
|
|
313
|
-
|
|
314
|
-
if (
|
|
315
|
-
scopeBindings = new Map();
|
|
316
|
-
finalized.set(moduleScope.id, scopeBindings);
|
|
317
|
-
}
|
|
318
|
-
const existing = scopeBindings.get(simpleName) ?? [];
|
|
319
|
-
if (existing.some((b) => b.def.nodeId === def.nodeId))
|
|
317
|
+
const bucketArr = getAugmentationBucket(augmentations, moduleScope.id, simpleName);
|
|
318
|
+
if (bucketArr.some((b) => b.def.nodeId === def.nodeId))
|
|
320
319
|
continue;
|
|
321
|
-
|
|
322
|
-
scopeBindings.set(simpleName, existing);
|
|
320
|
+
bucketArr.push({ def, origin: 'namespace' });
|
|
323
321
|
}
|
|
324
322
|
}
|
|
325
323
|
}
|
|
@@ -339,11 +337,6 @@ export function populateCsharpNamespaceSiblings(parsedFiles, indexes, inputs) {
|
|
|
339
337
|
defsByName.set(key, arr);
|
|
340
338
|
}
|
|
341
339
|
for (const { scopeId, filePath } of bucket.scopes) {
|
|
342
|
-
let scopeBindings = finalized.get(scopeId);
|
|
343
|
-
if (scopeBindings === undefined) {
|
|
344
|
-
scopeBindings = new Map();
|
|
345
|
-
finalized.set(scopeId, scopeBindings);
|
|
346
|
-
}
|
|
347
340
|
for (const [name, defs] of defsByName) {
|
|
348
341
|
// Skip names already present locally — `origin: 'local'` in
|
|
349
342
|
// scope.bindings would naturally shadow the cross-file
|
|
@@ -351,20 +344,39 @@ export function populateCsharpNamespaceSiblings(parsedFiles, indexes, inputs) {
|
|
|
351
344
|
const local = bucket.scopes.find((s) => s.filePath === filePath)?.scope.bindings.get(name);
|
|
352
345
|
if (local !== undefined && local.some((b) => b.origin === 'local'))
|
|
353
346
|
continue;
|
|
354
|
-
|
|
347
|
+
let bucketArr = null;
|
|
355
348
|
for (const def of defs) {
|
|
356
349
|
if (def.filePath === filePath)
|
|
357
350
|
continue; // don't self-reference
|
|
358
|
-
if (
|
|
351
|
+
if (bucketArr === null)
|
|
352
|
+
bucketArr = getAugmentationBucket(augmentations, scopeId, name);
|
|
353
|
+
if (bucketArr.some((b) => b.def.nodeId === def.nodeId))
|
|
359
354
|
continue;
|
|
360
|
-
|
|
355
|
+
bucketArr.push({ def, origin: 'namespace' });
|
|
361
356
|
}
|
|
362
|
-
if (existing.length > 0)
|
|
363
|
-
scopeBindings.set(name, existing);
|
|
364
357
|
}
|
|
365
358
|
}
|
|
366
359
|
}
|
|
367
360
|
}
|
|
361
|
+
/** Get-or-create a mutable inner bucket inside the `bindingAugmentations`
|
|
362
|
+
* channel. The inner arrays here are mutable by contract (see
|
|
363
|
+
* `ScopeResolutionIndexes.bindingAugmentations` doc + scope-resolver I8);
|
|
364
|
+
* callers may `push` directly. Allocating the outer/inner Maps lazily
|
|
365
|
+
* keeps the augmentation footprint zero for files with no cross-file
|
|
366
|
+
* fanout. */
|
|
367
|
+
function getAugmentationBucket(augmentations, scopeId, name) {
|
|
368
|
+
let scopeBindings = augmentations.get(scopeId);
|
|
369
|
+
if (scopeBindings === undefined) {
|
|
370
|
+
scopeBindings = new Map();
|
|
371
|
+
augmentations.set(scopeId, scopeBindings);
|
|
372
|
+
}
|
|
373
|
+
let bucketArr = scopeBindings.get(name);
|
|
374
|
+
if (bucketArr === undefined) {
|
|
375
|
+
bucketArr = [];
|
|
376
|
+
scopeBindings.set(name, bucketArr);
|
|
377
|
+
}
|
|
378
|
+
return bucketArr;
|
|
379
|
+
}
|
|
368
380
|
function isTypeDef(def) {
|
|
369
381
|
return (def.type === 'Class' ||
|
|
370
382
|
def.type === 'Interface' ||
|
|
@@ -21,6 +21,8 @@ import { getPythonParser, getPythonScopeQuery } from './query.js';
|
|
|
21
21
|
import { synthesizeReceiverTypeBinding } from './receiver-binding.js';
|
|
22
22
|
import { computePythonArityMetadata } from './arity-metadata.js';
|
|
23
23
|
import { recordCacheHit, recordCacheMiss } from './cache-stats.js';
|
|
24
|
+
import { getTreeSitterBufferSize } from '../../constants.js';
|
|
25
|
+
import { pythonFunctionDefinitionLabel } from './simple-hooks.js';
|
|
24
26
|
export function emitPythonScopeCaptures(sourceText, _filePath, cachedTree) {
|
|
25
27
|
// Skip the parse when the caller (parse phase's ASTCache) already
|
|
26
28
|
// produced a Tree for this source. Cache miss = re-parse, same as
|
|
@@ -29,7 +31,9 @@ export function emitPythonScopeCaptures(sourceText, _filePath, cachedTree) {
|
|
|
29
31
|
// here at the use site.
|
|
30
32
|
let tree = cachedTree;
|
|
31
33
|
if (tree === undefined) {
|
|
32
|
-
tree = getPythonParser().parse(sourceText
|
|
34
|
+
tree = getPythonParser().parse(sourceText, undefined, {
|
|
35
|
+
bufferSize: getTreeSitterBufferSize(sourceText),
|
|
36
|
+
});
|
|
33
37
|
recordCacheMiss();
|
|
34
38
|
}
|
|
35
39
|
else {
|
|
@@ -83,6 +87,10 @@ export function emitPythonScopeCaptures(sourceText, _filePath, cachedTree) {
|
|
|
83
87
|
const anchorCap = grouped['@declaration.function'];
|
|
84
88
|
const fnNode = findNodeAtRange(tree.rootNode, anchorCap.range, 'function_definition');
|
|
85
89
|
if (fnNode !== null) {
|
|
90
|
+
if (pythonFunctionDefinitionLabel(fnNode, 'Function') === 'Method') {
|
|
91
|
+
delete grouped['@declaration.function'];
|
|
92
|
+
grouped['@declaration.method'] = { ...anchorCap, name: '@declaration.method' };
|
|
93
|
+
}
|
|
86
94
|
const arity = computePythonArityMetadata(fnNode);
|
|
87
95
|
if (arity.parameterCount !== undefined) {
|
|
88
96
|
grouped['@declaration.parameter-count'] = syntheticCapture('@declaration.parameter-count', fnNode, String(arity.parameterCount));
|
|
@@ -77,4 +77,4 @@ export { interpretPythonImport, interpretPythonTypeBinding } from './interpret.j
|
|
|
77
77
|
export { pythonMergeBindings } from './merge-bindings.js';
|
|
78
78
|
export { pythonArityCompatibility } from './arity.js';
|
|
79
79
|
export { resolvePythonImportTarget, type PythonResolveContext } from './import-target.js';
|
|
80
|
-
export { pythonBindingScopeFor, pythonImportOwningScope, pythonReceiverBinding, } from './simple-hooks.js';
|
|
80
|
+
export { pythonBindingScopeFor, pythonFunctionDefinitionLabel, pythonImportOwningScope, pythonReceiverBinding, } from './simple-hooks.js';
|
|
@@ -77,4 +77,4 @@ export { interpretPythonImport, interpretPythonTypeBinding } from './interpret.j
|
|
|
77
77
|
export { pythonMergeBindings } from './merge-bindings.js';
|
|
78
78
|
export { pythonArityCompatibility } from './arity.js';
|
|
79
79
|
export { resolvePythonImportTarget } from './import-target.js';
|
|
80
|
-
export { pythonBindingScopeFor, pythonImportOwningScope, pythonReceiverBinding, } from './simple-hooks.js';
|
|
80
|
+
export { pythonBindingScopeFor, pythonFunctionDefinitionLabel, pythonImportOwningScope, pythonReceiverBinding, } from './simple-hooks.js';
|
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
* "absence == default") so reviewers don't have to re-derive the
|
|
6
6
|
* analysis.
|
|
7
7
|
*/
|
|
8
|
-
import type { CaptureMatch, ParsedImport, Scope, ScopeId, ScopeTree, TypeRef } from '../../../../_shared/index.js';
|
|
8
|
+
import type { CaptureMatch, NodeLabel, ParsedImport, Scope, ScopeId, ScopeTree, TypeRef } from '../../../../_shared/index.js';
|
|
9
|
+
import type { SyntaxNode } from 'tree-sitter';
|
|
10
|
+
export declare function pythonFunctionDefinitionLabel(functionNode: SyntaxNode, defaultLabel: NodeLabel): NodeLabel;
|
|
9
11
|
/** Python has no block scope, so the central extractor's "innermost
|
|
10
12
|
* enclosing scope" default is already correct: `for x in …` creates
|
|
11
13
|
* `x` in the enclosing function/module scope (because we never emit a
|
|
@@ -5,6 +5,14 @@
|
|
|
5
5
|
* "absence == default") so reviewers don't have to re-derive the
|
|
6
6
|
* analysis.
|
|
7
7
|
*/
|
|
8
|
+
import { findAncestorBeforeBoundary, FUNCTION_NODE_TYPES } from '../../utils/ast-helpers.js';
|
|
9
|
+
const PYTHON_METHOD_CONTAINER_TYPES = new Set(['class_definition']);
|
|
10
|
+
export function pythonFunctionDefinitionLabel(functionNode, defaultLabel) {
|
|
11
|
+
if (defaultLabel !== 'Function')
|
|
12
|
+
return defaultLabel;
|
|
13
|
+
const ancestor = findAncestorBeforeBoundary(functionNode, PYTHON_METHOD_CONTAINER_TYPES, FUNCTION_NODE_TYPES);
|
|
14
|
+
return ancestor === null ? 'Function' : 'Method';
|
|
15
|
+
}
|
|
8
16
|
// ─── bindingScopeFor ──────────────────────────────────────────────────────
|
|
9
17
|
/** Python has no block scope, so the central extractor's "innermost
|
|
10
18
|
* enclosing scope" default is already correct: `for x in …` creates
|
|
@@ -28,7 +28,7 @@ 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
30
|
import { createHeritageExtractor } from '../heritage-extractors/generic.js';
|
|
31
|
-
import { emitPythonScopeCaptures, interpretPythonImport, interpretPythonTypeBinding, pythonArityCompatibility, pythonBindingScopeFor, pythonImportOwningScope, pythonMergeBindings, pythonReceiverBinding, resolvePythonImportTarget, } from './python/index.js';
|
|
31
|
+
import { emitPythonScopeCaptures, pythonFunctionDefinitionLabel, interpretPythonImport, interpretPythonTypeBinding, pythonArityCompatibility, pythonBindingScopeFor, pythonImportOwningScope, pythonMergeBindings, pythonReceiverBinding, resolvePythonImportTarget, } from './python/index.js';
|
|
32
32
|
const BUILT_INS = new Set([
|
|
33
33
|
'print',
|
|
34
34
|
'len',
|
|
@@ -75,6 +75,7 @@ export const pythonProvider = defineLanguage({
|
|
|
75
75
|
classExtractor: createClassExtractor(pythonClassConfig),
|
|
76
76
|
heritageExtractor: createHeritageExtractor(SupportedLanguages.Python),
|
|
77
77
|
builtInNames: BUILT_INS,
|
|
78
|
+
labelOverride: pythonFunctionDefinitionLabel,
|
|
78
79
|
// ── RFC #909 Ring 3: scope-based resolution hooks (RFC §5) ──────────
|
|
79
80
|
// Python is the first migration. See ./python/index.ts for the
|
|
80
81
|
// full per-hook rationale and the canonical capture vocabulary in
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract TypeScript arity metadata from a method-like tree-sitter node —
|
|
3
|
+
* `method_definition`, `method_signature`, `abstract_method_signature`,
|
|
4
|
+
* `function_declaration`, `generator_function_declaration`, or
|
|
5
|
+
* `function_signature` (overload signature).
|
|
6
|
+
*
|
|
7
|
+
* Reuses `typescriptMethodConfig.extractParameters` so scope-extracted defs
|
|
8
|
+
* carry the same arity semantics as the legacy parse-worker path:
|
|
9
|
+
* - Rest parameters (`...args: T[]`) collapse `parameterCount` to
|
|
10
|
+
* `undefined`, which `typescriptArityCompatibility` treats as
|
|
11
|
+
* "max unknown" — the candidate stays eligible at
|
|
12
|
+
* `argCount >= required` (mirrors Python `*args` / C# `params`).
|
|
13
|
+
* - Optional (`p?: T`) and defaulted (`p: T = …`) parameters both
|
|
14
|
+
* contribute to `optionalCount`;
|
|
15
|
+
* `requiredParameterCount = total − optionalCount`.
|
|
16
|
+
* - `parameterTypes` collects declared type-annotation text for
|
|
17
|
+
* overload narrowing; TypeScript supports function overloading
|
|
18
|
+
* (`function f(x: string); function f(x: number); function f(x) {}`),
|
|
19
|
+
* so populated types let the registry disambiguate same-arity
|
|
20
|
+
* siblings by declared types.
|
|
21
|
+
* - A literal `'params'` marker is appended for variadic methods so
|
|
22
|
+
* `typescriptArityCompatibility` can detect rest params without
|
|
23
|
+
* re-reading the AST.
|
|
24
|
+
*
|
|
25
|
+
* ## Generics stripping
|
|
26
|
+
*
|
|
27
|
+
* TypeScript parameter types frequently contain generic instantiations
|
|
28
|
+
* (`User<string>`, `Array<User>`, `Promise<User[]>`). For overload
|
|
29
|
+
* narrowing by declared type, we want the "head" name — `User`,
|
|
30
|
+
* `Array`, `Promise` — so `arity-metadata` applies a light strip to
|
|
31
|
+
* each `parameterTypes[i]`:
|
|
32
|
+
*
|
|
33
|
+
* - `Foo<Bar>` → `Foo`
|
|
34
|
+
* - `Foo<Bar, Baz>` → `Foo`
|
|
35
|
+
* - `Foo[]` → `Foo`
|
|
36
|
+
* - `Foo<Bar>[]` → `Foo`
|
|
37
|
+
* - `Foo<Bar<Baz>>` → `Foo` (greedy — strip the outermost once)
|
|
38
|
+
* - plain `Foo` → `Foo`
|
|
39
|
+
*
|
|
40
|
+
* We do NOT strip unions / intersections at this layer — those stay
|
|
41
|
+
* intact because the registry's overload narrowing is a string
|
|
42
|
+
* equality check; union types shouldn't match anything and we prefer
|
|
43
|
+
* "unknown" to "accidental match". `undefined` / `null` in unions
|
|
44
|
+
* (TS strict mode) is handled by `interpret.ts`'s `stripNullableUnion`
|
|
45
|
+
* when the name would be consumed as a receiver type — that path is
|
|
46
|
+
* separate from this arity-metadata path.
|
|
47
|
+
*
|
|
48
|
+
* Generic type parameters on the function itself (`function f<T>(x: T)`)
|
|
49
|
+
* do NOT enter here — the method extractor reads the `parameters`
|
|
50
|
+
* field only, which contains value parameters, not type parameters.
|
|
51
|
+
*/
|
|
52
|
+
import type { SyntaxNode } from '../../utils/ast-helpers.js';
|
|
53
|
+
interface TsArityMetadata {
|
|
54
|
+
readonly parameterCount: number | undefined;
|
|
55
|
+
readonly requiredParameterCount: number | undefined;
|
|
56
|
+
readonly parameterTypes: readonly string[] | undefined;
|
|
57
|
+
}
|
|
58
|
+
export declare function computeTsArityMetadata(fnNode: SyntaxNode): TsArityMetadata;
|
|
59
|
+
export {};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract TypeScript arity metadata from a method-like tree-sitter node —
|
|
3
|
+
* `method_definition`, `method_signature`, `abstract_method_signature`,
|
|
4
|
+
* `function_declaration`, `generator_function_declaration`, or
|
|
5
|
+
* `function_signature` (overload signature).
|
|
6
|
+
*
|
|
7
|
+
* Reuses `typescriptMethodConfig.extractParameters` so scope-extracted defs
|
|
8
|
+
* carry the same arity semantics as the legacy parse-worker path:
|
|
9
|
+
* - Rest parameters (`...args: T[]`) collapse `parameterCount` to
|
|
10
|
+
* `undefined`, which `typescriptArityCompatibility` treats as
|
|
11
|
+
* "max unknown" — the candidate stays eligible at
|
|
12
|
+
* `argCount >= required` (mirrors Python `*args` / C# `params`).
|
|
13
|
+
* - Optional (`p?: T`) and defaulted (`p: T = …`) parameters both
|
|
14
|
+
* contribute to `optionalCount`;
|
|
15
|
+
* `requiredParameterCount = total − optionalCount`.
|
|
16
|
+
* - `parameterTypes` collects declared type-annotation text for
|
|
17
|
+
* overload narrowing; TypeScript supports function overloading
|
|
18
|
+
* (`function f(x: string); function f(x: number); function f(x) {}`),
|
|
19
|
+
* so populated types let the registry disambiguate same-arity
|
|
20
|
+
* siblings by declared types.
|
|
21
|
+
* - A literal `'params'` marker is appended for variadic methods so
|
|
22
|
+
* `typescriptArityCompatibility` can detect rest params without
|
|
23
|
+
* re-reading the AST.
|
|
24
|
+
*
|
|
25
|
+
* ## Generics stripping
|
|
26
|
+
*
|
|
27
|
+
* TypeScript parameter types frequently contain generic instantiations
|
|
28
|
+
* (`User<string>`, `Array<User>`, `Promise<User[]>`). For overload
|
|
29
|
+
* narrowing by declared type, we want the "head" name — `User`,
|
|
30
|
+
* `Array`, `Promise` — so `arity-metadata` applies a light strip to
|
|
31
|
+
* each `parameterTypes[i]`:
|
|
32
|
+
*
|
|
33
|
+
* - `Foo<Bar>` → `Foo`
|
|
34
|
+
* - `Foo<Bar, Baz>` → `Foo`
|
|
35
|
+
* - `Foo[]` → `Foo`
|
|
36
|
+
* - `Foo<Bar>[]` → `Foo`
|
|
37
|
+
* - `Foo<Bar<Baz>>` → `Foo` (greedy — strip the outermost once)
|
|
38
|
+
* - plain `Foo` → `Foo`
|
|
39
|
+
*
|
|
40
|
+
* We do NOT strip unions / intersections at this layer — those stay
|
|
41
|
+
* intact because the registry's overload narrowing is a string
|
|
42
|
+
* equality check; union types shouldn't match anything and we prefer
|
|
43
|
+
* "unknown" to "accidental match". `undefined` / `null` in unions
|
|
44
|
+
* (TS strict mode) is handled by `interpret.ts`'s `stripNullableUnion`
|
|
45
|
+
* when the name would be consumed as a receiver type — that path is
|
|
46
|
+
* separate from this arity-metadata path.
|
|
47
|
+
*
|
|
48
|
+
* Generic type parameters on the function itself (`function f<T>(x: T)`)
|
|
49
|
+
* do NOT enter here — the method extractor reads the `parameters`
|
|
50
|
+
* field only, which contains value parameters, not type parameters.
|
|
51
|
+
*/
|
|
52
|
+
import { typescriptMethodConfig } from '../../method-extractors/configs/typescript-javascript.js';
|
|
53
|
+
export function computeTsArityMetadata(fnNode) {
|
|
54
|
+
const params = typescriptMethodConfig.extractParameters?.(fnNode) ?? [];
|
|
55
|
+
let hasRest = false;
|
|
56
|
+
let optionalCount = 0;
|
|
57
|
+
const types = [];
|
|
58
|
+
for (const p of params) {
|
|
59
|
+
if (p.isVariadic)
|
|
60
|
+
hasRest = true;
|
|
61
|
+
else if (p.isOptional)
|
|
62
|
+
optionalCount++;
|
|
63
|
+
const t = p.type !== null && p.type !== undefined ? stripGenericsAndArraySuffix(p.type) : '';
|
|
64
|
+
types.push(t);
|
|
65
|
+
}
|
|
66
|
+
if (hasRest)
|
|
67
|
+
types.push('params');
|
|
68
|
+
const total = params.length;
|
|
69
|
+
const parameterCount = hasRest ? undefined : total;
|
|
70
|
+
const requiredParameterCount = hasRest ? undefined : total - optionalCount;
|
|
71
|
+
// Only emit parameterTypes when at least one param carries a non-
|
|
72
|
+
// empty type name. An array of all empty strings adds noise to the
|
|
73
|
+
// registry without aiding narrowing — callers treat absence as
|
|
74
|
+
// "types unknown".
|
|
75
|
+
const hasAnyType = types.some((t) => t !== '' && t !== 'params');
|
|
76
|
+
const parameterTypes = hasAnyType || hasRest ? (types.length > 0 ? types : undefined) : undefined;
|
|
77
|
+
return {
|
|
78
|
+
parameterCount,
|
|
79
|
+
requiredParameterCount,
|
|
80
|
+
parameterTypes,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Light generic + array-suffix strip used only for registry overload
|
|
85
|
+
* narrowing. See file-level JSDoc for the exact transformation table.
|
|
86
|
+
*
|
|
87
|
+
* Handles nesting greedily at the outermost level:
|
|
88
|
+
* `Foo<Bar<Baz>>[]` — strip `[]` → `Foo<Bar<Baz>>`, then strip
|
|
89
|
+
* outermost `<>` → `Foo`.
|
|
90
|
+
*/
|
|
91
|
+
function stripGenericsAndArraySuffix(raw) {
|
|
92
|
+
let t = raw.trim();
|
|
93
|
+
// Repeatedly peel trailing `[]` pairs, then peel the outermost `<…>`
|
|
94
|
+
// block once. We don't loop the `<>` peel since nesting is rare and
|
|
95
|
+
// the head name is already reached after one peel.
|
|
96
|
+
while (t.endsWith('[]'))
|
|
97
|
+
t = t.slice(0, -2).trim();
|
|
98
|
+
const lt = t.indexOf('<');
|
|
99
|
+
if (lt > 0 && t.endsWith('>')) {
|
|
100
|
+
t = t.slice(0, lt).trim();
|
|
101
|
+
}
|
|
102
|
+
return t;
|
|
103
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript arity check, accommodating rest parameters and optional
|
|
3
|
+
* (`p?: T`) / defaulted (`p: T = …`) parameters.
|
|
4
|
+
*
|
|
5
|
+
* TypeScript-specific semantics vs C#:
|
|
6
|
+
*
|
|
7
|
+
* - **Optional** — `p?: T` collapses to `isOptional` in the extractor
|
|
8
|
+
* and contributes to `optionalCount`, so `requiredParameterCount`
|
|
9
|
+
* excludes it. Same wire shape as a default-valued parameter.
|
|
10
|
+
* - **Rest** — `...args: T[]` makes `parameterCount` undefined (max
|
|
11
|
+
* unknown) and `parameterTypes` carries a literal `'params'` marker
|
|
12
|
+
* so this hook can detect variadic calls without re-reading the AST
|
|
13
|
+
* (mirrors the C# convention for cross-language consistency).
|
|
14
|
+
* - **Generics** — function-level generic type parameters (`<T, U>`)
|
|
15
|
+
* do NOT count toward arity; the method-extractor reads the
|
|
16
|
+
* `parameters` field and ignores `type_parameters`, so generic
|
|
17
|
+
* count never enters the metadata.
|
|
18
|
+
*
|
|
19
|
+
* The metadata shape (`parameterCount`, `requiredParameterCount`,
|
|
20
|
+
* `parameterTypes`) is synthesized by `arity-metadata.ts` and stored
|
|
21
|
+
* on `SymbolDefinition`. This file consumes that metadata.
|
|
22
|
+
*
|
|
23
|
+
* Verdicts:
|
|
24
|
+
* - `'compatible'` — `requiredParameterCount <= argCount <=
|
|
25
|
+
* parameterCount`, OR the def has rest params
|
|
26
|
+
* (any `argCount >= required`).
|
|
27
|
+
* - `'incompatible'` — argCount is below required, OR above max with
|
|
28
|
+
* no rest params.
|
|
29
|
+
* - `'unknown'` — metadata is absent / incomplete (treated as
|
|
30
|
+
* neutral by the registry).
|
|
31
|
+
*
|
|
32
|
+
* `'incompatible'` is a soft signal in `Registry.lookup` (penalized
|
|
33
|
+
* but still considered when no compatible candidate exists), per
|
|
34
|
+
* RFC §4.
|
|
35
|
+
*/
|
|
36
|
+
import type { Callsite, SymbolDefinition } from '../../../../_shared/index.js';
|
|
37
|
+
export declare function typescriptArityCompatibility(def: SymbolDefinition, callsite: Callsite): 'compatible' | 'unknown' | 'incompatible';
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript arity check, accommodating rest parameters and optional
|
|
3
|
+
* (`p?: T`) / defaulted (`p: T = …`) parameters.
|
|
4
|
+
*
|
|
5
|
+
* TypeScript-specific semantics vs C#:
|
|
6
|
+
*
|
|
7
|
+
* - **Optional** — `p?: T` collapses to `isOptional` in the extractor
|
|
8
|
+
* and contributes to `optionalCount`, so `requiredParameterCount`
|
|
9
|
+
* excludes it. Same wire shape as a default-valued parameter.
|
|
10
|
+
* - **Rest** — `...args: T[]` makes `parameterCount` undefined (max
|
|
11
|
+
* unknown) and `parameterTypes` carries a literal `'params'` marker
|
|
12
|
+
* so this hook can detect variadic calls without re-reading the AST
|
|
13
|
+
* (mirrors the C# convention for cross-language consistency).
|
|
14
|
+
* - **Generics** — function-level generic type parameters (`<T, U>`)
|
|
15
|
+
* do NOT count toward arity; the method-extractor reads the
|
|
16
|
+
* `parameters` field and ignores `type_parameters`, so generic
|
|
17
|
+
* count never enters the metadata.
|
|
18
|
+
*
|
|
19
|
+
* The metadata shape (`parameterCount`, `requiredParameterCount`,
|
|
20
|
+
* `parameterTypes`) is synthesized by `arity-metadata.ts` and stored
|
|
21
|
+
* on `SymbolDefinition`. This file consumes that metadata.
|
|
22
|
+
*
|
|
23
|
+
* Verdicts:
|
|
24
|
+
* - `'compatible'` — `requiredParameterCount <= argCount <=
|
|
25
|
+
* parameterCount`, OR the def has rest params
|
|
26
|
+
* (any `argCount >= required`).
|
|
27
|
+
* - `'incompatible'` — argCount is below required, OR above max with
|
|
28
|
+
* no rest params.
|
|
29
|
+
* - `'unknown'` — metadata is absent / incomplete (treated as
|
|
30
|
+
* neutral by the registry).
|
|
31
|
+
*
|
|
32
|
+
* `'incompatible'` is a soft signal in `Registry.lookup` (penalized
|
|
33
|
+
* but still considered when no compatible candidate exists), per
|
|
34
|
+
* RFC §4.
|
|
35
|
+
*/
|
|
36
|
+
export function typescriptArityCompatibility(def, callsite) {
|
|
37
|
+
const max = def.parameterCount;
|
|
38
|
+
const min = def.requiredParameterCount;
|
|
39
|
+
if (max === undefined && min === undefined)
|
|
40
|
+
return 'unknown';
|
|
41
|
+
const argCount = callsite.arity;
|
|
42
|
+
if (!Number.isFinite(argCount) || argCount < 0)
|
|
43
|
+
return 'unknown';
|
|
44
|
+
// Variadic detection: the `arity-metadata` synthesizer appends the
|
|
45
|
+
// literal `'params'` marker to `parameterTypes` when the def has a
|
|
46
|
+
// rest parameter, to avoid re-parsing the AST here.
|
|
47
|
+
const hasRest = def.parameterTypes !== undefined &&
|
|
48
|
+
def.parameterTypes.some((t) => t === 'params' || t.startsWith('params '));
|
|
49
|
+
if (min !== undefined && argCount < min)
|
|
50
|
+
return 'incompatible';
|
|
51
|
+
if (max !== undefined && argCount > max && !hasRest)
|
|
52
|
+
return 'incompatible';
|
|
53
|
+
return 'compatible';
|
|
54
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev-mode counters for the TypeScript cross-phase scope-captures parse cache.
|
|
3
|
+
*
|
|
4
|
+
* Gated by `PROF_SCOPE_RESOLUTION=1`. In production the module-level `PROF`
|
|
5
|
+
* constant is `false` and V8 folds every increment site into dead code, so the
|
|
6
|
+
* hot path in `captures.ts` stays branch-free.
|
|
7
|
+
*
|
|
8
|
+
* Extracted from `captures.ts` so the production hot-path module doesn't carry
|
|
9
|
+
* a module-global counter and its reset/export surface.
|
|
10
|
+
*/
|
|
11
|
+
export declare function recordCacheHit(): void;
|
|
12
|
+
export declare function recordCacheMiss(): void;
|
|
13
|
+
export declare function getTypescriptCaptureCacheStats(): {
|
|
14
|
+
hits: number;
|
|
15
|
+
misses: number;
|
|
16
|
+
};
|
|
17
|
+
export declare function resetTypescriptCaptureCacheStats(): void;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev-mode counters for the TypeScript cross-phase scope-captures parse cache.
|
|
3
|
+
*
|
|
4
|
+
* Gated by `PROF_SCOPE_RESOLUTION=1`. In production the module-level `PROF`
|
|
5
|
+
* constant is `false` and V8 folds every increment site into dead code, so the
|
|
6
|
+
* hot path in `captures.ts` stays branch-free.
|
|
7
|
+
*
|
|
8
|
+
* Extracted from `captures.ts` so the production hot-path module doesn't carry
|
|
9
|
+
* a module-global counter and its reset/export surface.
|
|
10
|
+
*/
|
|
11
|
+
const PROF = process.env.PROF_SCOPE_RESOLUTION === '1';
|
|
12
|
+
let CACHE_HITS = 0;
|
|
13
|
+
let CACHE_MISSES = 0;
|
|
14
|
+
export function recordCacheHit() {
|
|
15
|
+
if (PROF)
|
|
16
|
+
CACHE_HITS++;
|
|
17
|
+
}
|
|
18
|
+
export function recordCacheMiss() {
|
|
19
|
+
if (PROF)
|
|
20
|
+
CACHE_MISSES++;
|
|
21
|
+
}
|
|
22
|
+
export function getTypescriptCaptureCacheStats() {
|
|
23
|
+
return { hits: CACHE_HITS, misses: CACHE_MISSES };
|
|
24
|
+
}
|
|
25
|
+
export function resetTypescriptCaptureCacheStats() {
|
|
26
|
+
CACHE_HITS = 0;
|
|
27
|
+
CACHE_MISSES = 0;
|
|
28
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `emitScopeCaptures` for TypeScript.
|
|
3
|
+
*
|
|
4
|
+
* Drives the TypeScript scope query against tree-sitter-typescript and groups
|
|
5
|
+
* raw matches into `CaptureMatch[]` for the central extractor. Layers
|
|
6
|
+
* synthesized streams on top:
|
|
7
|
+
*
|
|
8
|
+
* 1. **Import decomposition** — each `import_statement` / re-export is
|
|
9
|
+
* re-emitted with `@import.kind/source/name/alias/typeOnly` markers so
|
|
10
|
+
* `interpretTsImport` can recover the `ParsedImport` shape without
|
|
11
|
+
* re-parsing raw text (see `import-decomposer.ts`). Unit 2 adds this;
|
|
12
|
+
* until then, raw `@import.statement` matches flow through as-is.
|
|
13
|
+
* 2. **Dynamic imports** — `import('./m')` is re-emitted as a
|
|
14
|
+
* decomposed `@import.statement` with `@import.kind=dynamic` so the
|
|
15
|
+
* central extractor treats it uniformly with static imports.
|
|
16
|
+
* 3. **Function-decl arity metadata** (Unit 5) — `@declaration.parameter-count`
|
|
17
|
+
* / `@declaration.required-parameter-count` / `@declaration.parameter-types`
|
|
18
|
+
* synthesized onto function-like declarations so the registry can narrow
|
|
19
|
+
* overloads.
|
|
20
|
+
* 4. **Callsite arity metadata** (Unit 5) — `@reference.arity` /
|
|
21
|
+
* `@reference.parameter-types` on every callsite.
|
|
22
|
+
* 5. **Receiver-binding synthesis** (Unit 3) — `this` type anchors on
|
|
23
|
+
* instance methods, with arrow-function lexical-this walk-up.
|
|
24
|
+
*
|
|
25
|
+
* Pure given the input source text. No I/O, no globals consulted.
|
|
26
|
+
*/
|
|
27
|
+
import type { CaptureMatch } from '../../../../_shared/index.js';
|
|
28
|
+
export declare function emitTsScopeCaptures(sourceText: string, filePath: string, cachedTree?: unknown): readonly CaptureMatch[];
|