ucn 5.5.1 → 5.5.2
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/.claude/skills/ucn/SKILL.md +35 -9
- package/.claude/skills/ucn/references/commands.md +38 -16
- package/.claude/skills/ucn/references/resolution-rules.md +126 -20
- package/.claude/skills/ucn/references/trust-contract.md +5 -3
- package/README.md +30 -21
- package/cli/index.js +1 -1
- package/core/accessors.js +341 -54
- package/core/account.js +74 -20
- package/core/analysis.js +257 -125
- package/core/ast-analysis.js +12 -3
- package/core/brief.js +8 -4
- package/core/build-worker.js +2 -1
- package/core/cache.js +137 -6
- package/core/call-name-cache.js +134 -0
- package/core/callable-scope.js +21 -0
- package/core/callers.js +4726 -940
- package/core/check.js +22 -5
- package/core/class-identity.js +399 -12
- package/core/constructor-calls.js +831 -0
- package/core/constructor-hierarchy.js +147 -0
- package/core/constructor-protocol.js +196 -0
- package/core/contract-membership.js +346 -140
- package/core/cpp-conversion-constraints.js +123 -0
- package/core/cpp-conversion-syntax.js +216 -0
- package/core/cpp-conversions.js +414 -0
- package/core/cpp-scope.js +245 -44
- package/core/deadcode.js +35 -15
- package/core/decorator-binding.js +137 -3
- package/core/enum-reference-syntax.js +134 -0
- package/core/execute.js +17 -1
- package/core/external-macros.js +42 -10
- package/core/go-packages.js +171 -0
- package/core/graph-build.js +2 -0
- package/core/graph.js +75 -59
- package/core/ignore-rules.js +2 -1
- package/core/imports.js +145 -19
- package/core/index-ir.js +41 -4
- package/core/ir.js +27 -6
- package/core/macro-expansion.js +38 -2
- package/core/out-of-class-members.js +182 -0
- package/core/output/analysis-ext.js +4 -2
- package/core/output/analysis.js +89 -20
- package/core/output/graph.js +12 -3
- package/core/output/lines.js +4 -3
- package/core/output/public.js +1 -0
- package/core/output/reporting.js +6 -1
- package/core/output/shared.js +2 -0
- package/core/output/tracing.js +28 -22
- package/core/output-budget.js +2 -2
- package/core/parallel-build.js +52 -1
- package/core/project.js +217 -112
- package/core/provenance-constructor-base.js +161 -0
- package/core/provenance-facts.js +257 -27
- package/core/provenance-overload.js +458 -54
- package/core/provenance-type.js +487 -0
- package/core/provenance.js +234 -15
- package/core/public-command.js +17 -2
- package/core/query-bodies.js +159 -0
- package/core/reflection.js +255 -2
- package/core/rename-token-syntax.js +111 -0
- package/core/reporting.js +13 -7
- package/core/rust-macro-expansion.js +151 -19
- package/core/rust-modules.js +1 -1
- package/core/rust-names.js +607 -0
- package/core/search.js +70 -17
- package/core/shared.js +98 -1
- package/core/source-memo.js +201 -0
- package/core/text-usage-syntax.js +38 -0
- package/core/tracing.js +7 -1
- package/core/type-denotation.js +124 -37
- package/core/type-references.js +615 -246
- package/core/value-references.js +2777 -0
- package/core/verify.js +1097 -281
- package/languages/adapter.js +28 -4
- package/languages/c-family.js +1118 -164
- package/languages/constructor-bases.js +105 -0
- package/languages/cpp-platform.js +26 -0
- package/languages/cpp-site-syntax.js +83 -0
- package/languages/csharp-scope.js +439 -0
- package/languages/csharp.js +428 -68
- package/languages/go.js +385 -207
- package/languages/html.js +4 -2
- package/languages/index.js +397 -2
- package/languages/java-platform.js +226 -0
- package/languages/java-unicode.js +269 -0
- package/languages/java.js +461 -117
- package/languages/javascript.js +418 -66
- package/languages/lexical-scope.js +190 -23
- package/languages/python.js +404 -54
- package/languages/rust.js +510 -82
- package/languages/utils.js +67 -8
- package/mcp/server.js +8 -2
- package/package.json +2 -2
package/core/graph.js
CHANGED
|
@@ -319,16 +319,27 @@ function fileExports(index, filePath, _visited) {
|
|
|
319
319
|
(isPythonImplicit && symbol.name && !symbol.name.startsWith('_') && !symbol.className && !symbol.isMethod);
|
|
320
320
|
|
|
321
321
|
if (isExported) {
|
|
322
|
+
const variable = symbol.type === 'state' && (fileEntry.exportDetails || [])
|
|
323
|
+
.find(detail => detail.isVariable && detail.name === symbol.name);
|
|
324
|
+
const details = (fileEntry.exportDetails || []).filter(detail =>
|
|
325
|
+
(detail.localName || detail.name) === symbol.name);
|
|
326
|
+
const defaultLike = detail => detail.type === 'default' ||
|
|
327
|
+
detail.type === 'module.exports' && detail.defaultLike;
|
|
328
|
+
const defaultOnly = details.some(defaultLike) &&
|
|
329
|
+
!details.some(detail => !defaultLike(detail));
|
|
322
330
|
results.push({
|
|
323
331
|
name: symbol.name,
|
|
324
|
-
type: symbol.type,
|
|
332
|
+
type: variable ? 'variable' : symbol.type,
|
|
325
333
|
file: fileEntry.relativePath,
|
|
326
334
|
startLine: symbol.startLine,
|
|
327
335
|
endLine: symbol.endLine,
|
|
328
336
|
...(symbol.className && { className: symbol.className }),
|
|
329
337
|
params: symbol.params,
|
|
330
|
-
returnType: symbol.returnType,
|
|
331
|
-
signature:
|
|
338
|
+
returnType: variable ? variable.typeAnnotation || null : symbol.returnType,
|
|
339
|
+
signature: variable
|
|
340
|
+
? `${variable.declKind} ${variable.name}${variable.typeAnnotation ? ': ' + variable.typeAnnotation : ''}`
|
|
341
|
+
: formatExportSignature(index, symbol),
|
|
342
|
+
...(defaultOnly && { isDefault: true }),
|
|
332
343
|
});
|
|
333
344
|
}
|
|
334
345
|
}
|
|
@@ -522,63 +533,59 @@ function fileExports(index, filePath, _visited) {
|
|
|
522
533
|
const matchedNames = new Set(results.map(r => r.name));
|
|
523
534
|
const unmatched = fileEntry.exports.filter(name => !matchedNames.has(name));
|
|
524
535
|
if (unmatched.length > 0) {
|
|
525
|
-
//
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
if (name !== '*') nameToModule.set(name, imp.module);
|
|
535
|
-
}
|
|
536
|
+
// Read imports for exported names that have no local declaration.
|
|
537
|
+
const content = index._readFile(absPath);
|
|
538
|
+
const { imports: rawImports } = extractImports(content, 'python');
|
|
539
|
+
// Build name→module map from raw imports
|
|
540
|
+
const nameToModule = new Map();
|
|
541
|
+
for (const imp of rawImports) {
|
|
542
|
+
if (imp.names) {
|
|
543
|
+
for (const name of imp.names) {
|
|
544
|
+
if (name !== '*') nameToModule.set(name, imp.module);
|
|
536
545
|
}
|
|
537
546
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
547
|
+
}
|
|
548
|
+
for (const name of unmatched) {
|
|
549
|
+
const sourceModule = nameToModule.get(name);
|
|
550
|
+
if (!sourceModule) continue;
|
|
551
|
+
const resolvedSrc = resolveImport(sourceModule, absPath, {
|
|
552
|
+
language: 'python',
|
|
553
|
+
root: index.root,
|
|
554
|
+
extensions: index.extensions
|
|
555
|
+
});
|
|
556
|
+
if (!resolvedSrc) continue;
|
|
557
|
+
const sourceEntry = index.files.get(resolvedSrc);
|
|
558
|
+
const srcSymbol = sourceEntry && sourceEntry.symbols.find(s => s.name === name);
|
|
559
|
+
if (srcSymbol) {
|
|
560
|
+
matchedNames.add(name);
|
|
561
|
+
results.push({
|
|
562
|
+
name,
|
|
563
|
+
type: srcSymbol.type,
|
|
564
|
+
file: fileEntry.relativePath,
|
|
565
|
+
startLine: srcSymbol.startLine,
|
|
566
|
+
endLine: srcSymbol.endLine,
|
|
567
|
+
params: srcSymbol.params,
|
|
568
|
+
returnType: srcSymbol.returnType,
|
|
569
|
+
signature: index.formatSignature(srcSymbol),
|
|
570
|
+
reExportedFrom: sourceEntry.relativePath
|
|
571
|
+
});
|
|
572
|
+
} else {
|
|
573
|
+
// Source not indexed or symbol not found — still list it
|
|
574
|
+
matchedNames.add(name);
|
|
575
|
+
results.push({
|
|
576
|
+
name,
|
|
577
|
+
type: 're-export',
|
|
578
|
+
file: fileEntry.relativePath,
|
|
579
|
+
startLine: undefined,
|
|
580
|
+
endLine: undefined,
|
|
581
|
+
params: undefined,
|
|
582
|
+
returnType: null,
|
|
583
|
+
signature: `re-export ${name} from '${sourceModule}'`,
|
|
584
|
+
reExportedFrom: resolvedSrc
|
|
585
|
+
? (sourceEntry ? sourceEntry.relativePath : resolvedSrc)
|
|
586
|
+
: sourceModule
|
|
545
587
|
});
|
|
546
|
-
if (!resolvedSrc) continue;
|
|
547
|
-
const sourceEntry = index.files.get(resolvedSrc);
|
|
548
|
-
const srcSymbol = sourceEntry && sourceEntry.symbols.find(s => s.name === name);
|
|
549
|
-
if (srcSymbol) {
|
|
550
|
-
matchedNames.add(name);
|
|
551
|
-
results.push({
|
|
552
|
-
name,
|
|
553
|
-
type: srcSymbol.type,
|
|
554
|
-
file: fileEntry.relativePath,
|
|
555
|
-
startLine: srcSymbol.startLine,
|
|
556
|
-
endLine: srcSymbol.endLine,
|
|
557
|
-
params: srcSymbol.params,
|
|
558
|
-
returnType: srcSymbol.returnType,
|
|
559
|
-
signature: index.formatSignature(srcSymbol),
|
|
560
|
-
reExportedFrom: sourceEntry.relativePath
|
|
561
|
-
});
|
|
562
|
-
} else {
|
|
563
|
-
// Source not indexed or symbol not found — still list it
|
|
564
|
-
matchedNames.add(name);
|
|
565
|
-
results.push({
|
|
566
|
-
name,
|
|
567
|
-
type: 're-export',
|
|
568
|
-
file: fileEntry.relativePath,
|
|
569
|
-
startLine: undefined,
|
|
570
|
-
endLine: undefined,
|
|
571
|
-
params: undefined,
|
|
572
|
-
returnType: null,
|
|
573
|
-
signature: `re-export ${name} from '${sourceModule}'`,
|
|
574
|
-
reExportedFrom: resolvedSrc
|
|
575
|
-
? (sourceEntry ? sourceEntry.relativePath : resolvedSrc)
|
|
576
|
-
: sourceModule
|
|
577
|
-
});
|
|
578
|
-
}
|
|
579
588
|
}
|
|
580
|
-
} catch (_) {
|
|
581
|
-
// File read failure — skip Python re-export resolution
|
|
582
589
|
}
|
|
583
590
|
}
|
|
584
591
|
}
|
|
@@ -991,6 +998,11 @@ function circularDeps(index, options = {}) {
|
|
|
991
998
|
}),
|
|
992
999
|
});
|
|
993
1000
|
}
|
|
1001
|
+
// Go/Rust/Java/C#/C/C++ files run no module code when they
|
|
1002
|
+
// are imported or included: their cycles are static
|
|
1003
|
+
// dependency cycles, neither import-time nor deferred.
|
|
1004
|
+
const runsModules = rotatedAbs.some(file =>
|
|
1005
|
+
langTraits(index.files.get(file)?.language)?.importsRunModules !== false);
|
|
994
1006
|
const deferredEdges = edges.filter(edge => edge.deferred)
|
|
995
1007
|
.map(edge => ({
|
|
996
1008
|
from: edge.from,
|
|
@@ -1002,7 +1014,7 @@ function circularDeps(index, options = {}) {
|
|
|
1002
1014
|
uniqueCycles.push({
|
|
1003
1015
|
files: rotated,
|
|
1004
1016
|
length: rotated.length,
|
|
1005
|
-
classification: deferredEdges.length > 0 ? 'deferred' : 'eager',
|
|
1017
|
+
classification: !runsModules ? 'static' : deferredEdges.length > 0 ? 'deferred' : 'eager',
|
|
1006
1018
|
edges,
|
|
1007
1019
|
deferredEdges,
|
|
1008
1020
|
});
|
|
@@ -1023,8 +1035,10 @@ function circularDeps(index, options = {}) {
|
|
|
1023
1035
|
if (targets && targets.size > 0) filesWithImports++;
|
|
1024
1036
|
}
|
|
1025
1037
|
|
|
1026
|
-
const eagerCycles = result.filter(cycle => cycle.classification !== 'deferred'
|
|
1027
|
-
|
|
1038
|
+
const eagerCycles = result.filter(cycle => cycle.classification !== 'deferred' &&
|
|
1039
|
+
cycle.classification !== 'static').length;
|
|
1040
|
+
const deferredCycles = result.filter(cycle => cycle.classification === 'deferred').length;
|
|
1041
|
+
const staticCycles = result.length - eagerCycles - deferredCycles;
|
|
1028
1042
|
const groupOf = new Map();
|
|
1029
1043
|
componentSummaries.forEach((group, i) => { for (const f of group.files) groupOf.set(f, i); });
|
|
1030
1044
|
for (const group of componentSummaries) { group.eagerCycles = 0; group.deferredCycles = 0; }
|
|
@@ -1032,6 +1046,7 @@ function circularDeps(index, options = {}) {
|
|
|
1032
1046
|
const group = componentSummaries[groupOf.get(cycle.files[0])];
|
|
1033
1047
|
if (!group) continue;
|
|
1034
1048
|
if (cycle.classification === 'deferred') group.deferredCycles++;
|
|
1049
|
+
else if (cycle.classification === 'static') group.staticCycles = (group.staticCycles || 0) + 1;
|
|
1035
1050
|
else group.eagerCycles++;
|
|
1036
1051
|
}
|
|
1037
1052
|
const cycleGroups = fileFilter
|
|
@@ -1050,6 +1065,7 @@ function circularDeps(index, options = {}) {
|
|
|
1050
1065
|
filesInCycles: new Set(cycleGroups.flatMap(c => c.files)).size,
|
|
1051
1066
|
eagerCycles,
|
|
1052
1067
|
deferredCycles,
|
|
1068
|
+
...(staticCycles > 0 && { staticCycles }),
|
|
1053
1069
|
componentCount: cycleGroups.length,
|
|
1054
1070
|
...(truncated && { truncated: true, cycleLimit,
|
|
1055
1071
|
maxComponentSize: MAX_ENUMERATED_COMPONENT,
|
package/core/ignore-rules.js
CHANGED
|
@@ -46,7 +46,8 @@ const POSIX_CLASSES = {
|
|
|
46
46
|
lower: 'a-z',
|
|
47
47
|
print: '\\x20-\\x7e',
|
|
48
48
|
punct: '!-\\/:-@\\[-`{-~',
|
|
49
|
-
|
|
49
|
+
// git's sane ctype isspace: space, \t, \n and \r, never \v or \f (fix #433).
|
|
50
|
+
space: ' \\t\\n\\r',
|
|
50
51
|
upper: 'A-Z',
|
|
51
52
|
xdigit: '0-9A-Fa-f',
|
|
52
53
|
};
|
package/core/imports.js
CHANGED
|
@@ -22,16 +22,12 @@ function extractImports(content, language) {
|
|
|
22
22
|
|
|
23
23
|
const langModule = getLanguageAdapter(moduleLang);
|
|
24
24
|
if (langModule && typeof langModule.findImportsInCode === 'function') {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return { imports, dynamicCount, importAliases };
|
|
32
|
-
}
|
|
33
|
-
} catch (e) {
|
|
34
|
-
// AST parsing failed
|
|
25
|
+
const parser = getParser(language);
|
|
26
|
+
if (parser) {
|
|
27
|
+
const imports = langModule.findImportsInCode(content, parser);
|
|
28
|
+
const dynamicCount = imports.filter(i => i.dynamic).length;
|
|
29
|
+
const importAliases = imports.aliases || null;
|
|
30
|
+
return { imports, dynamicCount, importAliases };
|
|
35
31
|
}
|
|
36
32
|
}
|
|
37
33
|
|
|
@@ -47,14 +43,10 @@ function extractExports(content, language) {
|
|
|
47
43
|
|
|
48
44
|
const langModule = getLanguageAdapter(moduleLang);
|
|
49
45
|
if (langModule && typeof langModule.findExportsInCode === 'function') {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return { exports: foundExports };
|
|
55
|
-
}
|
|
56
|
-
} catch (e) {
|
|
57
|
-
// AST parsing failed
|
|
46
|
+
const parser = getParser(language);
|
|
47
|
+
if (parser) {
|
|
48
|
+
const foundExports = langModule.findExportsInCode(content, parser);
|
|
49
|
+
return { exports: foundExports };
|
|
58
50
|
}
|
|
59
51
|
}
|
|
60
52
|
|
|
@@ -202,6 +194,17 @@ function resolveImport(importPath, fromFile, config = {}) {
|
|
|
202
194
|
// Relative imports
|
|
203
195
|
const extensions = config.extensions || getExtensions(config.language);
|
|
204
196
|
const resolved = path.resolve(fromDir, normalizedPath);
|
|
197
|
+
// A JS/TS directory specifier (`require('..')`, `require('../')`) loads
|
|
198
|
+
// the directory's package.json `main` before its index file (Node's
|
|
199
|
+
// directory resolution, fix #423), unless a file of that name exists.
|
|
200
|
+
if (_isJsFamily(config.language) && _isDir(resolved) && !_isFile(resolved) &&
|
|
201
|
+
!extensions.some(ext => _isFile(resolved + ext))) {
|
|
202
|
+
const manifest = _findPackageJson(resolved, resolved);
|
|
203
|
+
if (manifest && manifest.dir === resolved && typeof manifest.main === 'string' && manifest.main) {
|
|
204
|
+
const main = resolveFilePath(path.resolve(resolved, manifest.main), extensions);
|
|
205
|
+
if (main) return main;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
205
208
|
const direct = resolveFilePath(resolved, extensions);
|
|
206
209
|
if (direct) return direct;
|
|
207
210
|
|
|
@@ -459,6 +462,9 @@ function snapshotWorkspaceManifests(projectRoot) {
|
|
|
459
462
|
packageName: info.packageName,
|
|
460
463
|
srcDir: rel(info.srcDir),
|
|
461
464
|
targetDirs: info.targetDirs.map(rel),
|
|
465
|
+
...(info.dependencies && { dependencies: Object.fromEntries(Object.entries(info.dependencies)
|
|
466
|
+
.map(([name, dep]) => [name, { ...dep, ...(dep.path && { path: rel(dep.path) }) }])) }),
|
|
467
|
+
...(info.edition && { edition: info.edition }),
|
|
462
468
|
});
|
|
463
469
|
}
|
|
464
470
|
workspaceCrateCache.set(projectRoot, _registryFromManifestDirs(dirs));
|
|
@@ -480,6 +486,9 @@ function seedWorkspaceManifests(projectRoot, manifests) {
|
|
|
480
486
|
srcDir: path.join(projectRoot, m.srcDir),
|
|
481
487
|
packageName: m.packageName,
|
|
482
488
|
targetDirs: m.targetDirs.map(t => path.join(projectRoot, t)),
|
|
489
|
+
...(m.dependencies && { dependencies: Object.fromEntries(Object.entries(m.dependencies)
|
|
490
|
+
.map(([name, dep]) => [name, { ...dep, ...(dep.path && { path: path.join(projectRoot, dep.path) }) }])) }),
|
|
491
|
+
...(m.edition && { edition: m.edition }),
|
|
483
492
|
});
|
|
484
493
|
}
|
|
485
494
|
workspaceCrateCache.set(projectRoot, _registryFromManifestDirs(dirs));
|
|
@@ -516,6 +525,50 @@ function resetCargoCaches(projectRoot) {
|
|
|
516
525
|
}
|
|
517
526
|
}
|
|
518
527
|
|
|
528
|
+
/**
|
|
529
|
+
* The crate names a manifest's dependency tables declare (fix #422), as
|
|
530
|
+
* code spells them (`-` read as `_`): `{ name: { path?, package?,
|
|
531
|
+
* workspace? } }` with `path` the dependency's directory for a path
|
|
532
|
+
* dependency, `package` the package a rename names, `workspace` for an entry
|
|
533
|
+
* inherited from the workspace. Null when the manifest declares none.
|
|
534
|
+
*/
|
|
535
|
+
function cargoDependencies(toml, dir) {
|
|
536
|
+
const deps = {};
|
|
537
|
+
let section = null;
|
|
538
|
+
let tableDep = null;
|
|
539
|
+
const add = (name, body) => {
|
|
540
|
+
const key = name.replace(/-/g, '_');
|
|
541
|
+
const entry = deps[key] || (deps[key] = {});
|
|
542
|
+
const pathMatch = /\bpath\s*=\s*"([^"]+)"/.exec(body);
|
|
543
|
+
if (pathMatch) entry.path = path.resolve(dir, pathMatch[1]);
|
|
544
|
+
const pkg = /\bpackage\s*=\s*"([^"]+)"/.exec(body);
|
|
545
|
+
if (pkg) entry.package = pkg[1].replace(/-/g, '_');
|
|
546
|
+
if (/\bworkspace\s*=\s*true\b/.test(body)) entry.workspace = true;
|
|
547
|
+
};
|
|
548
|
+
for (const raw of toml.split(/\r?\n/)) {
|
|
549
|
+
const line = raw.replace(/#.*$/, '').trim();
|
|
550
|
+
if (!line) continue;
|
|
551
|
+
const header = /^\[\s*([^\]]+?)\s*\]$/.exec(line);
|
|
552
|
+
if (header) {
|
|
553
|
+
const name = header[1].replace(/\s+/g, '');
|
|
554
|
+
const parts = name.split('.');
|
|
555
|
+
const kind = parts.findIndex(part => /^(dev-|build-)?dependencies$/.test(part));
|
|
556
|
+
section = kind >= 0 ? name : null;
|
|
557
|
+
tableDep = kind >= 0 && kind === parts.length - 2 ? parts[parts.length - 1].replace(/^["']|["']$/g, '') : null;
|
|
558
|
+
if (tableDep) add(tableDep, '');
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
if (!section) continue;
|
|
562
|
+
if (tableDep) {
|
|
563
|
+
add(tableDep, line);
|
|
564
|
+
continue;
|
|
565
|
+
}
|
|
566
|
+
const entry = /^["']?([A-Za-z0-9_-]+)["']?(\.workspace)?\s*=\s*(.*)$/.exec(line);
|
|
567
|
+
if (entry) add(entry[1], entry[2] ? 'workspace = true' : entry[3]);
|
|
568
|
+
}
|
|
569
|
+
return Object.keys(deps).length > 0 ? deps : null;
|
|
570
|
+
}
|
|
571
|
+
|
|
519
572
|
/**
|
|
520
573
|
* Find the nearest Cargo.toml and return the crate's source root
|
|
521
574
|
* @param {string} startDir - Directory to start searching from
|
|
@@ -539,11 +592,20 @@ function findCargoRoot(startDir) {
|
|
|
539
592
|
// tests/*.rs — fix #246); `-` normalizes to `_` in code.
|
|
540
593
|
let packageName = null;
|
|
541
594
|
const targetDirs = [];
|
|
595
|
+
let dependencies = null;
|
|
596
|
+
let edition = null;
|
|
542
597
|
try {
|
|
543
598
|
const toml = fs.readFileSync(cargoPath, 'utf-8');
|
|
544
599
|
const pkgSection = toml.split(/^\s*\[/m).find(s => s.startsWith('package]'));
|
|
545
600
|
const m = pkgSection && pkgSection.match(/^\s*name\s*=\s*"([^"]+)"/m);
|
|
546
601
|
if (m) packageName = m[1].replace(/-/g, '_');
|
|
602
|
+
// The edition decides how `use` paths start (fix #422): a
|
|
603
|
+
// package without one is 2015 (paths from the crate root);
|
|
604
|
+
// an inherited edition is not known here.
|
|
605
|
+
if (pkgSection) {
|
|
606
|
+
const ed = pkgSection.match(/^\s*edition\s*=\s*"(\d+)"/m);
|
|
607
|
+
edition = ed ? ed[1] : /^\s*edition(\.workspace|\s*=\s*\{)/m.test(pkgSection) ? null : '2015';
|
|
608
|
+
}
|
|
547
609
|
// Explicit target roots (fix #260b, ripgrep-measured): a
|
|
548
610
|
// manifest may root its targets OUTSIDE src/ — ripgrep's
|
|
549
611
|
// `[[bin]] path = "crates/core/main.rs"` puts the whole bin
|
|
@@ -554,8 +616,10 @@ function findCargoRoot(startDir) {
|
|
|
554
616
|
const tDir = path.dirname(path.resolve(dir, tm[1]));
|
|
555
617
|
if (!targetDirs.includes(tDir)) targetDirs.push(tDir);
|
|
556
618
|
}
|
|
619
|
+
dependencies = cargoDependencies(toml, dir);
|
|
557
620
|
} catch { /* unreadable Cargo.toml — no package identity */ }
|
|
558
|
-
const result = { root: dir, srcDir: fs.existsSync(srcDir) ? srcDir : dir, packageName, targetDirs
|
|
621
|
+
const result = { root: dir, srcDir: fs.existsSync(srcDir) ? srcDir : dir, packageName, targetDirs,
|
|
622
|
+
...(dependencies && { dependencies }), ...(edition && { edition }) };
|
|
559
623
|
cargoCache.set(startDir, result);
|
|
560
624
|
return result;
|
|
561
625
|
}
|
|
@@ -1006,7 +1070,10 @@ function _resolvePackageSpecifier(pkg, importPath, config) {
|
|
|
1006
1070
|
return subpath === '.' ? tryTargets(exp, null) : null;
|
|
1007
1071
|
}
|
|
1008
1072
|
if (exp && typeof exp === 'object') {
|
|
1073
|
+
// The subpath's entry, exact or through a '*' pattern.
|
|
1074
|
+
let matched = null;
|
|
1009
1075
|
if (exp[subpath] !== undefined) {
|
|
1076
|
+
matched = { entry: exp[subpath], wildcard: null };
|
|
1010
1077
|
const hit = tryTargets(exp[subpath], null);
|
|
1011
1078
|
if (hit) return hit;
|
|
1012
1079
|
}
|
|
@@ -1018,6 +1085,7 @@ function _resolvePackageSpecifier(pkg, importPath, config) {
|
|
|
1018
1085
|
if (subpath.length > pre.length + post.length &&
|
|
1019
1086
|
subpath.startsWith(pre) && subpath.endsWith(post)) {
|
|
1020
1087
|
const wild = subpath.slice(pre.length, subpath.length - post.length);
|
|
1088
|
+
if (!matched) matched = { entry: val, wildcard: wild };
|
|
1021
1089
|
const hit = tryTargets(val, wild);
|
|
1022
1090
|
if (hit) return hit;
|
|
1023
1091
|
}
|
|
@@ -1031,6 +1099,11 @@ function _resolvePackageSpecifier(pkg, importPath, config) {
|
|
|
1031
1099
|
if (source) return source;
|
|
1032
1100
|
}
|
|
1033
1101
|
if (subpath === '.' && pkg.workspace) return _workspaceSourceEntry(pkg, extensions);
|
|
1102
|
+
// A subpath of a package of the repository whose targets are build
|
|
1103
|
+
// output absent from the checkout (fix #423, swr `swr/infinite` ->
|
|
1104
|
+
// `./dist/infinite/index.mjs`) is its source under `src/`, the
|
|
1105
|
+
// layout the package's own entry falls back to.
|
|
1106
|
+
if (matched && pkg.workspace) return _workspaceSubpathSource(pkg, matched, extensions);
|
|
1034
1107
|
return null;
|
|
1035
1108
|
}
|
|
1036
1109
|
// No exports map: bare name -> main/index; subpath -> direct file
|
|
@@ -1042,6 +1115,55 @@ function _resolvePackageSpecifier(pkg, importPath, config) {
|
|
|
1042
1115
|
return resolveFilePath(path.resolve(pkg.dir, subpath), extensions);
|
|
1043
1116
|
}
|
|
1044
1117
|
|
|
1118
|
+
// Export conditions in the order a source build resolves them: the module
|
|
1119
|
+
// and default entries first, platform and custom conditions after.
|
|
1120
|
+
const _SOURCE_CONDITION_RANK = ['import', 'module', 'default', 'require', 'node', 'browser'];
|
|
1121
|
+
|
|
1122
|
+
/** The export targets of an entry with their condition paths (fix #423). */
|
|
1123
|
+
function _conditionTargets(entry, conditions = [], out = []) {
|
|
1124
|
+
if (typeof entry === 'string') out.push({ target: entry, conditions });
|
|
1125
|
+
else if (Array.isArray(entry)) for (const e of entry) _conditionTargets(e, conditions, out);
|
|
1126
|
+
else if (entry && typeof entry === 'object') {
|
|
1127
|
+
for (const [cond, v] of Object.entries(entry)) _conditionTargets(v, [...conditions, cond], out);
|
|
1128
|
+
}
|
|
1129
|
+
return out;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
/**
|
|
1133
|
+
* The source of a workspace package subpath whose export targets name build
|
|
1134
|
+
* output absent from the checkout (fix #423): each target's path below its
|
|
1135
|
+
* output directory, under the package's `src/` (`./dist/infinite/index.mjs`
|
|
1136
|
+
* -> `src/infinite/index.ts`). Standard conditions are tried before custom
|
|
1137
|
+
* ones (`react-server`), declaration targets last; a target leaving the
|
|
1138
|
+
* package or a declaration file never answers.
|
|
1139
|
+
*/
|
|
1140
|
+
function _workspaceSubpathSource(pkg, matched, extensions) {
|
|
1141
|
+
const rank = conditions => {
|
|
1142
|
+
let worst = 0;
|
|
1143
|
+
for (const cond of conditions) {
|
|
1144
|
+
const at = cond === 'types' ? _SOURCE_CONDITION_RANK.length + 1
|
|
1145
|
+
: (_SOURCE_CONDITION_RANK.indexOf(cond) === -1 ? _SOURCE_CONDITION_RANK.length
|
|
1146
|
+
: _SOURCE_CONDITION_RANK.indexOf(cond));
|
|
1147
|
+
if (at > worst) worst = at;
|
|
1148
|
+
}
|
|
1149
|
+
return worst;
|
|
1150
|
+
};
|
|
1151
|
+
const targets = _conditionTargets(matched.entry)
|
|
1152
|
+
.map((t, order) => ({ ...t, order, rank: rank(t.conditions) }))
|
|
1153
|
+
.sort((a, b) => a.rank - b.rank || a.order - b.order);
|
|
1154
|
+
for (const { target } of targets) {
|
|
1155
|
+
const concrete = matched.wildcard != null ? target.replace(/\*/g, matched.wildcard) : target;
|
|
1156
|
+
const rel = path.relative(pkg.dir, path.resolve(pkg.dir, concrete));
|
|
1157
|
+
if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) continue;
|
|
1158
|
+
const segments = rel.split(path.sep);
|
|
1159
|
+
if (segments.length < 2 || segments[0] === 'src') continue;
|
|
1160
|
+
const rest = segments.slice(1).join(path.sep).replace(/(\.d)?\.[cm]?[jt]sx?$/, '');
|
|
1161
|
+
const hit = resolveFilePath(path.join(pkg.dir, 'src', rest), extensions);
|
|
1162
|
+
if (hit && !/\.d\.[cm]?ts$/.test(hit)) return hit;
|
|
1163
|
+
}
|
|
1164
|
+
return null;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1045
1167
|
// A workspace package whose `main` names a build artifact absent from the
|
|
1046
1168
|
// source checkout: its declared source/module/types entries, else src/index.
|
|
1047
1169
|
function _workspaceSourceEntry(pkg, extensions) {
|
|
@@ -1105,6 +1227,10 @@ function _isDir(p) {
|
|
|
1105
1227
|
try { return fs.statSync(p).isDirectory(); } catch { return false; }
|
|
1106
1228
|
}
|
|
1107
1229
|
|
|
1230
|
+
function _isJsFamily(language) {
|
|
1231
|
+
return language === 'javascript' || language === 'typescript' || language === 'tsx';
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1108
1234
|
/**
|
|
1109
1235
|
* Resolve an absolute Python module against the nearest non-package ancestor
|
|
1110
1236
|
* of the importing file (fix #366). Walks from the importing directory up to
|
package/core/index-ir.js
CHANGED
|
@@ -45,6 +45,11 @@ function createImportBindings(imports) {
|
|
|
45
45
|
...(item.deferred && { deferred: true }),
|
|
46
46
|
...(item.dynamic && { dynamic: true }),
|
|
47
47
|
...(item.namespace && { namespace: item.namespace }),
|
|
48
|
+
// Rust block `use` (fix #422): visible in its block only.
|
|
49
|
+
...(item.lexicalScopeStartLine != null && {
|
|
50
|
+
lexicalScopeStartLine: item.lexicalScopeStartLine,
|
|
51
|
+
lexicalScopeEndLine: item.lexicalScopeEndLine,
|
|
52
|
+
}),
|
|
48
53
|
};
|
|
49
54
|
});
|
|
50
55
|
});
|
|
@@ -106,6 +111,13 @@ function createFileEntryFromIR({
|
|
|
106
111
|
...(item.global && { global: true }),
|
|
107
112
|
// Go (fix #400): the import writes no local name.
|
|
108
113
|
...(item.implicitName && { implicitName: true }),
|
|
114
|
+
// Rust block `use` (fix #422): visible in its block only; a
|
|
115
|
+
// `use` item's visibility.
|
|
116
|
+
...(item.lexicalScopeStartLine != null && {
|
|
117
|
+
lexicalScopeStartLine: item.lexicalScopeStartLine,
|
|
118
|
+
lexicalScopeEndLine: item.lexicalScopeEndLine,
|
|
119
|
+
}),
|
|
120
|
+
...(item.visibility && { visibility: item.visibility }),
|
|
109
121
|
})),
|
|
110
122
|
globalImports: imports.filter(item => item.global).map(item => item.module),
|
|
111
123
|
importNames: imports.flatMap(item => item.names || []),
|
|
@@ -126,6 +138,7 @@ function createFileEntryFromIR({
|
|
|
126
138
|
...(ir.asyncClosureNames?.length > 0 && {
|
|
127
139
|
asyncClosureNames: ir.asyncClosureNames,
|
|
128
140
|
}),
|
|
141
|
+
...(ir.itemMacros?.length > 0 && { itemMacros: ir.itemMacros }),
|
|
129
142
|
...(Array.isArray(ir.openCalls) && { openCalls: ir.openCalls }),
|
|
130
143
|
...(ir.typeConversions?.length > 0 && {
|
|
131
144
|
typeConversions: ir.typeConversions,
|
|
@@ -133,12 +146,16 @@ function createFileEntryFromIR({
|
|
|
133
146
|
...(ir.reflectionSites?.length > 0 && {
|
|
134
147
|
reflectionSites: ir.reflectionSites,
|
|
135
148
|
}),
|
|
149
|
+
...(Array.isArray(ir.computedDispatchSites) && {
|
|
150
|
+
computedDispatchSites: ir.computedDispatchSites,
|
|
151
|
+
}),
|
|
136
152
|
...(ir.macroScopeMarkers?.length > 0 && {
|
|
137
153
|
macroScopeMarkers: ir.macroScopeMarkers,
|
|
138
154
|
}),
|
|
139
155
|
...(ir.cppUsings?.length > 0 && {
|
|
140
156
|
cppUsings: ir.cppUsings,
|
|
141
157
|
}),
|
|
158
|
+
...(Array.isArray(ir.typeParameterNames) && { typeParameterNames: ir.typeParameterNames }),
|
|
142
159
|
...(ir.languageFeatures?.length > 0 && {
|
|
143
160
|
languageFeatures: ir.languageFeatures,
|
|
144
161
|
}),
|
|
@@ -152,6 +169,7 @@ function createFileEntryFromIR({
|
|
|
152
169
|
...(ir.moduleValueAliases?.length > 0 && { moduleValueAliases: ir.moduleValueAliases }),
|
|
153
170
|
...(ir.moduleConstructedValues?.length > 0 && { moduleConstructedValues: ir.moduleConstructedValues }),
|
|
154
171
|
...(ir.configurationItems?.length > 0 && { configurationItems: ir.configurationItems }),
|
|
172
|
+
...(Array.isArray(ir.cppBaseSyntax) && { cppBaseSyntax: ir.cppBaseSyntax }),
|
|
155
173
|
...(Array.isArray(ir.recoveryBlanks) && { recoveryBlanks: ir.recoveryBlanks }),
|
|
156
174
|
...(Array.isArray(ir.recoveryCandidates) && { recoveryCandidates: ir.recoveryCandidates }),
|
|
157
175
|
...(Array.isArray(ir.externalMacroNames) && { externalMacroNames: ir.externalMacroNames }),
|
|
@@ -161,13 +179,13 @@ function createFileEntryFromIR({
|
|
|
161
179
|
}
|
|
162
180
|
|
|
163
181
|
const OPTIONAL_SYMBOL_FIELDS = Object.freeze([
|
|
164
|
-
'returnedFunctionResult', 'isFunctionVariable', 'paramTypes', 'isAsync',
|
|
165
|
-
'isGenerator', 'generics', 'ownerGenerics', 'genericBounds', 'extends', 'implements', 'indent', 'isNested',
|
|
182
|
+
'sourceSpan', 'returnedFunctionResult', 'isFunctionVariable', 'paramTypes', 'isAsync',
|
|
183
|
+
'isGenerator', 'generics', 'ownerGenerics', 'genericBounds', 'typeParameterConstraints', 'extends', 'implements', 'indent', 'isNested',
|
|
166
184
|
'enclosingType', 'isMethod', 'receiver', 'memberType', 'fieldType', 'embedded',
|
|
167
185
|
'aliasOf', 'aliasQualifier', 'aliasMembers', 'aliasTypeText', 'aliasTypeParameters', 'aliasTypeDefaults', 'derefTarget', 'decorators', 'decoratorsWithArgs',
|
|
168
186
|
'annotationsWithArgs', 'attributesWithArgs', 'nameLine', 'traitImpl',
|
|
169
187
|
'traitName', 'isSignature', 'memberAssigned', 'assignedReceiver', 'assignedObject', 'selfNamed', 'bodyScopedName',
|
|
170
|
-
'registryMember', 'registryContainer', 'registryContainerType', 'objectLiteralLine', 'namespace',
|
|
188
|
+
'registryMember', 'registryContainer', 'registryContainerType', 'objectLiteralLine', 'namespace', 'isConstructor',
|
|
171
189
|
'isExtensionMethod', 'extensionReceiver', 'explicitInterface', 'primaryParams',
|
|
172
190
|
'lexicalScopeStartLine', 'lexicalScopeEndLine',
|
|
173
191
|
'returnTypeQualifier', 'returnTypeResolved', 'supertraits', 'ownerGenericBounds', 'ownerSelfArgs', 'implSelfRef', 'implSelfQualifier', 'blanketSelfBounds', 'selfParamKind', 'macroNeverReturns', 'callbackParamTypes', 'iteratorItemType', 'futureReturn',
|
|
@@ -181,7 +199,9 @@ const OPTIONAL_SYMBOL_FIELDS = Object.freeze([
|
|
|
181
199
|
'generatedByMacro',
|
|
182
200
|
'typeArity', 'ownerTypeArity',
|
|
183
201
|
'annotationType', 'valueShape', 'friendOf', 'templateParams', 'delegateParams',
|
|
184
|
-
'typedefName',
|
|
202
|
+
'typedefName', 'forwardDeclaration', 'anonymousNamespace', 'specialization',
|
|
203
|
+
'memberConst', 'refQualifier', 'fieldPointers', 'memberPaths', 'instanceAttribute',
|
|
204
|
+
'ambientAugmentation',
|
|
185
205
|
]);
|
|
186
206
|
|
|
187
207
|
function materializeSymbol(fileEntry, item) {
|
|
@@ -214,8 +234,25 @@ function materializeSymbol(fileEntry, item) {
|
|
|
214
234
|
return symbol;
|
|
215
235
|
}
|
|
216
236
|
|
|
237
|
+
const fileBindingIds = new WeakMap();
|
|
238
|
+
|
|
217
239
|
function addIRSymbol(fileEntry, item, symbolTable = null) {
|
|
218
240
|
const symbol = materializeSymbol(fileEntry, item);
|
|
241
|
+
// Several declarations can occupy one source line. Keep the ordinary
|
|
242
|
+
// line-based id for its first declaration and give each later declaration
|
|
243
|
+
// an id of its own, in the parser's stable source order.
|
|
244
|
+
let ids = fileBindingIds.get(fileEntry);
|
|
245
|
+
if (!ids) {
|
|
246
|
+
ids = new Set(fileEntry.symbols.map(existing => existing.bindingId));
|
|
247
|
+
fileBindingIds.set(fileEntry, ids);
|
|
248
|
+
}
|
|
249
|
+
if (!item.id && ids.has(symbol.bindingId)) {
|
|
250
|
+
const base = symbol.bindingId;
|
|
251
|
+
let ordinal = 2;
|
|
252
|
+
while (ids.has(`${base}#${ordinal}`)) ordinal++;
|
|
253
|
+
symbol.bindingId = `${base}#${ordinal}`;
|
|
254
|
+
}
|
|
255
|
+
ids.add(symbol.bindingId);
|
|
219
256
|
fileEntry.symbols.push(symbol);
|
|
220
257
|
// A Rust `impl X`/`impl Trait for X` block introduces NO name into any
|
|
221
258
|
// scope (fix #286b, cursive-measured: the impl symbol stole the bare-name
|
package/core/ir.js
CHANGED
|
@@ -62,8 +62,8 @@ function normalizeSymbol(symbol, family, language, kind, owner = null) {
|
|
|
62
62
|
modifiers: [...(symbol.modifiers || [])],
|
|
63
63
|
};
|
|
64
64
|
const passthrough = [
|
|
65
|
-
'docstring', 'returnedFunctionResult', 'isFunctionVariable', 'paramTypes',
|
|
66
|
-
'isAsync', 'isGenerator', 'generics', 'ownerGenerics', 'genericBounds', 'extends', 'implements', 'indent',
|
|
65
|
+
'docstring', 'sourceSpan', 'returnedFunctionResult', 'isFunctionVariable', 'paramTypes',
|
|
66
|
+
'isAsync', 'isGenerator', 'generics', 'ownerGenerics', 'genericBounds', 'typeParameterConstraints', 'extends', 'implements', 'indent',
|
|
67
67
|
'isNested', 'enclosingType', 'isMethod', 'memberType', 'fieldType', 'embedded',
|
|
68
68
|
'aliasOf', 'aliasQualifier', 'aliasMembers', 'aliasTypeText', 'aliasTypeParameters', 'aliasTypeDefaults', 'derefTarget', 'decorators', 'decoratorsWithArgs',
|
|
69
69
|
'annotationsWithArgs', 'attributesWithArgs', 'nameLine', 'traitImpl',
|
|
@@ -82,7 +82,9 @@ function normalizeSymbol(symbol, family, language, kind, owner = null) {
|
|
|
82
82
|
'generatedByMacro',
|
|
83
83
|
'typeArity', 'ownerTypeArity',
|
|
84
84
|
'annotationType', 'valueShape', 'friendOf', 'templateParams', 'delegateParams',
|
|
85
|
-
'typedefName',
|
|
85
|
+
'typedefName', 'forwardDeclaration', 'anonymousNamespace', 'specialization',
|
|
86
|
+
'memberConst', 'refQualifier', 'fieldPointers', 'memberPaths', 'instanceAttribute',
|
|
87
|
+
'ambientAugmentation',
|
|
86
88
|
];
|
|
87
89
|
for (const field of passthrough) {
|
|
88
90
|
if (symbol[field] !== undefined && symbol[field] !== null) {
|
|
@@ -110,9 +112,15 @@ function createFileIR({
|
|
|
110
112
|
const append = (symbol, family, kind, owner = null) => {
|
|
111
113
|
// Members one macro invocation declares share its lines (fix #385):
|
|
112
114
|
// their own binding ids keep overloads apart.
|
|
113
|
-
const key =
|
|
114
|
-
|
|
115
|
-
|
|
115
|
+
const key = JSON.stringify([
|
|
116
|
+
owner || symbol.className || '', symbol.name, kind,
|
|
117
|
+
symbol.startLine, symbol.endLine, symbol.namespace || '',
|
|
118
|
+
symbol.enclosingType || '', symbol.lexicalScopeStartLine ?? null,
|
|
119
|
+
symbol.lexicalScopeEndLine ?? null, symbol.anonymousNamespace || false,
|
|
120
|
+
symbol.typeArity ?? null, symbol.ownerTypeArity ?? null,
|
|
121
|
+
symbol.params ?? null,
|
|
122
|
+
symbol.generatedByMacro ? symbol.bindingId || '' : '',
|
|
123
|
+
]);
|
|
116
124
|
if (seen.has(key)) return;
|
|
117
125
|
seen.add(key);
|
|
118
126
|
normalizedSymbols.push(normalizeSymbol(symbol, family, language, kind, owner));
|
|
@@ -236,6 +244,10 @@ function createFileIR({
|
|
|
236
244
|
...(parsed.asyncClosureNames?.length > 0 && {
|
|
237
245
|
asyncClosureNames: [...parsed.asyncClosureNames],
|
|
238
246
|
}),
|
|
247
|
+
// Rust item-position macro invocations of each module (fix #422).
|
|
248
|
+
...(parsed.itemMacros?.length > 0 && {
|
|
249
|
+
itemMacros: parsed.itemMacros.map(item => ({ ...item })),
|
|
250
|
+
}),
|
|
239
251
|
// Shapes of calls whose value is not consumed where it is produced
|
|
240
252
|
// (fix #371/#372): audit-async reads a file's call records only when
|
|
241
253
|
// one of these can reach a future producer.
|
|
@@ -249,12 +261,18 @@ function createFileIR({
|
|
|
249
261
|
...(parsed.reflectionSites?.length > 0 && {
|
|
250
262
|
reflectionSites: parsed.reflectionSites.map(item => ({ ...item })),
|
|
251
263
|
}),
|
|
264
|
+
...(Array.isArray(parsed.computedDispatchSites) && {
|
|
265
|
+
computedDispatchSites: parsed.computedDispatchSites.map(item => ({ ...item })),
|
|
266
|
+
}),
|
|
252
267
|
...(parsed.macroScopeMarkers?.length > 0 && {
|
|
253
268
|
macroScopeMarkers: parsed.macroScopeMarkers.map(item => ({ ...item })),
|
|
254
269
|
}),
|
|
255
270
|
...(parsed.cppUsings?.length > 0 && {
|
|
256
271
|
cppUsings: parsed.cppUsings.map(item => ({ ...item })),
|
|
257
272
|
}),
|
|
273
|
+
// Type-parameter names a file declares (fix #421), when the parser
|
|
274
|
+
// read them from the raw source.
|
|
275
|
+
...(Array.isArray(parsed.typeParameterNames) && { typeParameterNames: [...parsed.typeParameterNames] }),
|
|
258
276
|
...(parsed.languageFeatures?.length > 0 && {
|
|
259
277
|
languageFeatures: [...parsed.languageFeatures],
|
|
260
278
|
}),
|
|
@@ -289,6 +307,9 @@ function createFileIR({
|
|
|
289
307
|
}),
|
|
290
308
|
// The byte ranges the C/C++ recovery blanked (fix #387): a query
|
|
291
309
|
// rebuilds the recovered tree with one parse.
|
|
310
|
+
...(Array.isArray(parsed.cppBaseSyntax) && { cppBaseSyntax: parsed.cppBaseSyntax.map(row => ({
|
|
311
|
+
...row, syntax: row.syntax && { ...row.syntax, namespaces: [...row.syntax.namespaces], classes: [...row.syntax.classes] },
|
|
312
|
+
})) }),
|
|
292
313
|
...(Array.isArray(parsed.recoveryBlanks) && {
|
|
293
314
|
recoveryBlanks: parsed.recoveryBlanks.map(range => [...range]),
|
|
294
315
|
}),
|