ucn 5.5.1 → 5.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/.claude/skills/ucn/SKILL.md +35 -9
  2. package/.claude/skills/ucn/references/commands.md +40 -18
  3. package/.claude/skills/ucn/references/resolution-rules.md +126 -20
  4. package/.claude/skills/ucn/references/trust-contract.md +5 -3
  5. package/README.md +32 -21
  6. package/cli/index.js +2 -1
  7. package/core/accessors.js +341 -54
  8. package/core/account.js +74 -20
  9. package/core/analysis.js +257 -125
  10. package/core/ast-analysis.js +12 -3
  11. package/core/bridge.js +15 -65
  12. package/core/brief.js +8 -4
  13. package/core/build-worker.js +2 -1
  14. package/core/cache.js +137 -6
  15. package/core/call-name-cache.js +134 -0
  16. package/core/callable-scope.js +21 -0
  17. package/core/callers.js +4726 -940
  18. package/core/check.js +22 -5
  19. package/core/class-identity.js +399 -12
  20. package/core/client-receivers.js +189 -0
  21. package/core/constructor-calls.js +831 -0
  22. package/core/constructor-hierarchy.js +147 -0
  23. package/core/constructor-protocol.js +196 -0
  24. package/core/contract-membership.js +346 -140
  25. package/core/cpp-conversion-constraints.js +123 -0
  26. package/core/cpp-conversion-syntax.js +216 -0
  27. package/core/cpp-conversions.js +414 -0
  28. package/core/cpp-scope.js +245 -44
  29. package/core/deadcode.js +61 -16
  30. package/core/decorator-binding.js +137 -3
  31. package/core/enum-reference-syntax.js +134 -0
  32. package/core/execute.js +17 -1
  33. package/core/external-macros.js +42 -10
  34. package/core/go-packages.js +171 -0
  35. package/core/graph-build.js +2 -0
  36. package/core/graph.js +75 -59
  37. package/core/ignore-rules.js +2 -1
  38. package/core/imports.js +145 -19
  39. package/core/index-ir.js +41 -4
  40. package/core/inprocess-clients.js +41 -15
  41. package/core/ir.js +27 -6
  42. package/core/macro-expansion.js +38 -2
  43. package/core/out-of-class-members.js +182 -0
  44. package/core/output/analysis-ext.js +4 -2
  45. package/core/output/analysis.js +89 -20
  46. package/core/output/graph.js +12 -3
  47. package/core/output/lines.js +4 -3
  48. package/core/output/public.js +2 -0
  49. package/core/output/reporting.js +12 -1
  50. package/core/output/shared.js +2 -0
  51. package/core/output/tracing.js +28 -22
  52. package/core/output-budget.js +2 -2
  53. package/core/parallel-build.js +52 -1
  54. package/core/project.js +217 -112
  55. package/core/provenance-constructor-base.js +161 -0
  56. package/core/provenance-facts.js +257 -27
  57. package/core/provenance-overload.js +458 -54
  58. package/core/provenance-type.js +487 -0
  59. package/core/provenance.js +234 -15
  60. package/core/public-command.js +17 -2
  61. package/core/query-bodies.js +159 -0
  62. package/core/reflection.js +270 -2
  63. package/core/rename-token-syntax.js +111 -0
  64. package/core/reporting.js +13 -7
  65. package/core/request-helpers.js +63 -7
  66. package/core/rust-macro-expansion.js +151 -19
  67. package/core/rust-modules.js +1 -1
  68. package/core/rust-names.js +607 -0
  69. package/core/search.js +70 -17
  70. package/core/shared.js +98 -1
  71. package/core/source-memo.js +201 -0
  72. package/core/text-usage-syntax.js +38 -0
  73. package/core/tracing.js +7 -1
  74. package/core/type-denotation.js +124 -37
  75. package/core/type-references.js +615 -246
  76. package/core/value-references.js +2777 -0
  77. package/core/verify.js +1097 -281
  78. package/languages/adapter.js +28 -4
  79. package/languages/c-family.js +1118 -164
  80. package/languages/constructor-bases.js +105 -0
  81. package/languages/cpp-platform.js +26 -0
  82. package/languages/cpp-site-syntax.js +83 -0
  83. package/languages/csharp-scope.js +439 -0
  84. package/languages/csharp.js +428 -68
  85. package/languages/go.js +385 -207
  86. package/languages/html.js +4 -2
  87. package/languages/index.js +397 -2
  88. package/languages/java-platform.js +226 -0
  89. package/languages/java-unicode.js +269 -0
  90. package/languages/java.js +461 -117
  91. package/languages/javascript.js +418 -66
  92. package/languages/lexical-scope.js +190 -23
  93. package/languages/python.js +404 -54
  94. package/languages/rust.js +510 -82
  95. package/languages/utils.js +67 -8
  96. package/mcp/server.js +8 -2
  97. package/package.json +3 -3
@@ -45,6 +45,7 @@ const fs = require('fs');
45
45
  const path = require('path');
46
46
  const { langTraits } = require('../languages');
47
47
  const { findCargoRoot, findGoModule } = require('./imports');
48
+ const { projectFact, projectHas, fileSymbolFact } = require('./provenance-overload');
48
49
 
49
50
  const TYPE_KINDS = new Set(['class', 'struct', 'interface', 'trait', 'enum', 'record', 'union']);
50
51
 
@@ -232,15 +233,20 @@ function projectNamespaces(index, language) {
232
233
  });
233
234
  }
234
235
 
236
+ /** Does the project declare namespace/package `name`? A witness records
237
+ * the answer, never every file the inventory reads (fix #418). */
238
+ function projectNamespaceDeclared(index, language, name) {
239
+ return projectHas(index, `namespaces:${language}`, name, live => projectNamespaces(live, language));
240
+ }
241
+
235
242
  /** Is some leading dotted prefix of `qualified` (the full path excluded) a
236
243
  * namespace/package the project declares? `org.junit.Assert` is project
237
244
  * code when `org.junit` is a project package; nested types keep their
238
245
  * package prefix (`a.b.Outer.Inner`). */
239
246
  function hasProjectNamespacePrefix(index, language, qualified) {
240
- const names = projectNamespaces(index, language);
241
247
  const segments = String(qualified).split('.');
242
248
  for (let i = segments.length - 1; i > 0; i--) {
243
- if (names.has(segments.slice(0, i).join('.'))) return true;
249
+ if (projectNamespaceDeclared(index, language, segments.slice(0, i).join('.'))) return true;
244
250
  }
245
251
  return false;
246
252
  }
@@ -363,7 +369,10 @@ function externalTypeDenotation(index, filePath, name, qualifier, line, options
363
369
  // `unscoped`: the site's spelling of the name is unknown (the parser kept
364
370
  // only the last segment), so package/namespace scoping cannot apply.
365
371
  const unscoped = options.unscoped === true;
366
- const key = `${filePath}\x00${name}\x00${qualifier || ''}\x00${scoped ?? ''}\x00${unscoped ? 1 : 0}`;
372
+ // Java/C# scoping reads the site's package or namespace (fix #433): a
373
+ // C# file with several namespace blocks decides a name per block.
374
+ const siteScope = !qualifier && !unscoped ? scopingSiteKey(index, filePath, line) : '';
375
+ const key = `${filePath}\x00${name}\x00${qualifier || ''}\x00${scoped ?? ''}\x00${unscoped ? 1 : 0}\x00${siteScope}`;
367
376
  return memoized(index, key, () => {
368
377
  const fileEntry = index.files.get(filePath);
369
378
  if (!fileEntry) return null;
@@ -465,7 +474,7 @@ function scopedTypeDenotation(index, fileEntry, filePath, name, line, scoping) {
465
474
  }
466
475
  if (implicitHas) return { original: name, via: `${implicit.module}.${name}` };
467
476
  const external = onDemand.filter(module => !hasProjectNamespacePrefix(index, language, `${module}.${name}`) &&
468
- !projectNamespaces(index, language).has(module));
477
+ !projectNamespaceDeclared(index, language, module));
469
478
  if (external.length === 0) return null;
470
479
  return { original: name, via: `${external[0]}.*`,
471
480
  ...(!external.every(module => standardRooted(language, module)) && { certain: false }) };
@@ -485,10 +494,26 @@ function scopedTypeDenotation(index, fileEntry, filePath, name, line, scoping) {
485
494
  // `using static T` also brings T's nested types (already abstained above).
486
495
  const usings = new Set([...scope.namespaces, ...scope.statics]);
487
496
  if (projectTypes.some(definition => usings.has(definition.namespace))) return null;
488
- const external = [...usings].filter(module => !csharpDeclaredNames(index).namespaces.has(module));
497
+ const external = [...usings].filter(module =>
498
+ !projectHas(index, 'cs-declared-namespaces', module, live => csharpDeclaredNames(live).namespaces));
489
499
  return { original: name, via: external.length > 0 ? `${external.sort()[0]}.${name}` : name };
490
500
  }
491
501
 
502
+ /** What `scopedTypeDenotation` reads of the site: its package/namespace
503
+ * (`siteNamespaceOf`), constant for a file whose types share one namespace;
504
+ * '' for a language without type-name scoping. */
505
+ function scopingSiteKey(index, filePath, line) {
506
+ const fileEntry = index.files.get(filePath);
507
+ if (!fileEntry || !langTraits(fileEntry.language)?.typeNameScoping) return '';
508
+ const namespaces = memoized(index, `\x01site-ns\x00${filePath}`, () => new Set(
509
+ fileSymbolFact(index, filePath, `file-type-namespaces\0${filePath}`, (symbols, live) => symbols
510
+ ? memoized(live, `\x01site-ns-facts\x00${filePath}`, () => symbols
511
+ .filter(symbol => TYPE_KINDS.has(symbol.type)).map(symbol => symbol.namespace || '')) : [])));
512
+ if (namespaces.size <= 1) return '\x01';
513
+ const namespace = siteNamespaceOf(fileEntry, line);
514
+ return namespace === undefined ? '\x02' : `\x03${namespace}`;
515
+ }
516
+
492
517
  /** Could a project type named `name` be the type `fqn` names? Only a type
493
518
  * in a named package/namespace that prefixes it (nested types included). */
494
519
  function fqnMayName(index, language, name, fqn) {
@@ -551,10 +576,12 @@ function csharpResolveUsingName(index, written, enclosing) {
551
576
  const text = String(written || '').replace(/\s+/g, '').replace(/^global::/, '');
552
577
  if (!enclosing || !text) return text;
553
578
  const first = text.split('.')[0].replace(/<.*$/s, '');
554
- const { namespaces, types } = csharpDeclaredNames(index);
555
579
  for (let parts = String(enclosing).split('.'); parts.length > 0; parts = parts.slice(0, -1)) {
556
580
  const candidate = `${parts.join('.')}.${first}`;
557
- if (namespaces.has(candidate) || types.has(candidate)) return `${parts.join('.')}.${text}`;
581
+ if (projectHas(index, 'cs-declared-namespaces', candidate, live => csharpDeclaredNames(live).namespaces) ||
582
+ projectHas(index, 'cs-declared-types', candidate, live => csharpDeclaredNames(live).types)) {
583
+ return `${parts.join('.')}.${text}`;
584
+ }
558
585
  }
559
586
  return text;
560
587
  }
@@ -584,17 +611,36 @@ function csharpUsingTarget(text) {
584
611
  return String(text || '').replace(/<.*$/s, '');
585
612
  }
586
613
 
587
- function csharpAddDirective(scope, index, { module, names, static: isStatic, namespace }) {
588
- const target = csharpUsingTarget(csharpResolveUsingName(index, module, namespace));
614
+ function csharpAddDirective(scope, index, { module, names, static: isStatic, namespace, lexicalNamespace = namespace, rooted = false }) {
615
+ const syntax = csharpResolveUsingName(index, module,
616
+ rooted || String(module || '').startsWith('global::') ? '' : namespace);
617
+ const target = csharpUsingTarget(syntax);
589
618
  if (!target) return;
590
619
  const alias = (names || []).find(name => name && name !== '*');
591
620
  if (alias) {
621
+ const at = lexicalNamespace || '';
622
+ if (!scope.aliasDefinitions.has(alias)) scope.aliasDefinitions.set(alias, []);
623
+ scope.aliasDefinitions.get(alias).push({ namespace: at, syntax,
624
+ absolute: rooted || String(module || '').startsWith('global::') });
625
+ const previous = scope.aliasScopes?.get(alias);
626
+ // Project-file imports are added after source directives; that order
627
+ // cannot override a nearer namespace's alias.
628
+ if (scope.scoped && previous != null && previous.length > at.length) return;
629
+ if (scope.aliases.has(alias) && previous === at && scope.aliases.get(alias) !== target) {
630
+ scope.ambiguousAliases.add(alias);
631
+ } else if (previous !== at) scope.ambiguousAliases.delete(alias);
592
632
  scope.aliases.set(alias, target);
633
+ scope.aliasSyntax.set(alias, syntax);
593
634
  if (!scope.aliasScopes) scope.aliasScopes = new Map();
594
- scope.aliasScopes.set(alias, namespace || '');
635
+ scope.aliasScopes.set(alias, at);
636
+ } else {
637
+ const values = isStatic ? scope.statics : scope.namespaces;
638
+ const origins = isStatic ? scope.staticScopes : scope.namespaceScopes;
639
+ values.add(target);
640
+ if (isStatic) scope.staticSyntax.set(target, syntax);
641
+ if (!origins.has(target)) origins.set(target, new Set());
642
+ origins.get(target).add(lexicalNamespace || '');
595
643
  }
596
- else if (isStatic) scope.statics.add(target);
597
- else scope.namespaces.add(target);
598
644
  }
599
645
 
600
646
  /**
@@ -602,29 +648,53 @@ function csharpAddDirective(scope, index, { module, names, static: isStatic, nam
602
648
  * directives (resolved from the namespace each is written in), `global
603
649
  * using` directives of every source file, and the <Using> items of the
604
650
  * project files above it (`csharpProjectFileUsings`).
605
- * Returns { namespaces: Set, statics: Set (type names), aliases: Map
606
- * (alias -> target), unreadable } - unreadable when a project file could not
651
+ * An optional namespace restricts local directives to that lexical chain;
652
+ * origins and original alias syntax support positive identity proofs.
653
+ * Returns namespaces/statics Sets, aliases and origin Maps, and unreadable.
654
+ * unreadable is true when a project file could not
607
655
  * be read (its items are unknown).
608
656
  */
609
- function csharpUsings(index, filePath) {
610
- return memoized(index, `\x01cs-usings\x00${filePath}`, () => {
611
- const scope = { namespaces: new Set(), statics: new Set(), aliases: new Map(), unreadable: false };
612
- const globals = memoized(index, '\x01cs-global-usings', () => {
657
+ function csharpUsings(index, filePath, namespace = null, siteNode = null, syntax = null) {
658
+ const scopes = syntax?.scopes || (siteNode ? require('../languages/csharp-scope').usingScopesOf(siteNode) : null);
659
+ const scopeKey = scopes?.map(scope => `${scope.startIndex}:${scope.endIndex}`).join(',') ?? '*';
660
+ return memoized(index, `\x01cs-usings\x00${filePath}\x00${namespace ?? '*'}\x00${scopeKey}`, () => {
661
+ const scope = { namespaces: new Set(), statics: new Set(), aliases: new Map(),
662
+ ambiguousAliases: new Set(), aliasSyntax: new Map(), aliasDefinitions: new Map(), scoped: namespace != null,
663
+ namespaceScopes: new Map(), staticScopes: new Map(), staticSyntax: new Map(), unreadable: false };
664
+ const globals = projectFact(index, 'cs-global-usings', live => memoized(live, '\x01cs-global-usings', () => {
613
665
  const out = [];
614
- for (const [, entry] of index.files) {
666
+ for (const [, entry] of live.files) {
615
667
  if (entry.language !== 'csharp') continue;
616
668
  for (const detail of entry.importDetails || []) {
617
669
  if (detail.global && detail.module) out.push(detail);
618
670
  }
619
671
  }
620
672
  return out;
621
- });
673
+ }));
622
674
  for (const detail of globals) csharpAddDirective(scope, index, detail);
623
675
  const fileEntry = index.files.get(filePath);
624
- for (const detail of fileEntry?.importDetails || []) {
625
- if (detail.type === 'using' && detail.module && !detail.global) {
626
- csharpAddDirective(scope, index, detail);
627
- }
676
+ let local = fileEntry?.importDetails || [];
677
+ if (syntax) local = syntax.directives;
678
+ else if (scopes) {
679
+ const content = index._readFile(filePath);
680
+ const language = require('../languages');
681
+ const parser = language.getParser(fileEntry.language);
682
+ const adapter = language.getLanguageAdapter(fileEntry.language);
683
+ const tree = (index.isProvenanceFactIndex && siteNode.tree) ||
684
+ (require('./query-bodies').isLocalNameTree(siteNode.tree) && siteNode.tree) ||
685
+ index._getParsedTree(filePath, content, fileEntry.language) ||
686
+ adapter.queryTree(content, parser, fileEntry);
687
+ local = adapter.queryUsingDirectives(tree);
688
+ }
689
+ for (const detail of local) {
690
+ if (detail.type !== 'using' || !detail.module || detail.global) continue;
691
+ if (scopes) {
692
+ const owner = detail.usingScopes.at(-1);
693
+ if (owner && !scopes.some(current => current.startIndex === owner.startIndex &&
694
+ current.endIndex === owner.endIndex)) continue;
695
+ csharpAddDirective(scope, index, { ...detail, lexicalNamespace: owner?.namespace || '' });
696
+ } else if (namespace == null || !detail.namespace || namespace === detail.namespace ||
697
+ namespace.startsWith(detail.namespace + '.')) csharpAddDirective(scope, index, detail);
628
698
  }
629
699
  for (const item of csharpProjectFileUsings(index, filePath)) {
630
700
  if (item === null) { scope.unreadable = true; continue; }
@@ -643,26 +713,41 @@ function csharpUsings(index, filePath) {
643
713
  */
644
714
  function csharpExpandAliasTargets(scope) {
645
715
  const scopes = scope.aliasScopes;
646
- if (!scopes || scope.aliases.size < 2) return;
716
+ if (!scopes) return;
647
717
  const encloses = (outer, inner) => outer !== inner && (outer === '' || inner.startsWith(`${outer}.`));
648
- for (let pass = 0; pass < 4; pass++) {
649
- let changed = false;
650
- for (const [alias, target] of scope.aliases) {
651
- const [head, ...rest] = String(target).split('.');
652
- if (head === alias || !scope.aliases.has(head)) continue;
653
- if (!encloses(scopes.get(head) ?? '', scopes.get(alias) ?? '')) continue;
654
- scope.aliases.set(alias, [scope.aliases.get(head), ...rest].join('.'));
655
- changed = true;
718
+ const expand = (definition, seen = new Set()) => {
719
+ if (definition.absolute) return definition.syntax;
720
+ if (seen.has(definition) || seen.size >= 16) return null;
721
+ const [head, ...rest] = definition.syntax.split('.');
722
+ const outer = (scope.aliasDefinitions.get(head) || []).filter(other =>
723
+ encloses(other.namespace, definition.namespace));
724
+ if (!outer.length) return definition.syntax;
725
+ const nearest = Math.max(...outer.map(other => other.namespace.length));
726
+ const next = new Set(seen); next.add(definition);
727
+ const targets = new Set(outer.filter(other => other.namespace.length === nearest)
728
+ .map(other => expand(other, next)));
729
+ if (targets.has(null) || targets.size !== 1) return null;
730
+ return [[...targets][0], ...rest].join('.');
731
+ };
732
+ for (const [alias, at] of scopes) {
733
+ const definitions = (scope.aliasDefinitions.get(alias) || []).filter(definition => definition.namespace === at);
734
+ const targets = new Set(definitions.map(definition => expand(definition)));
735
+ if (targets.has(null) || targets.size !== 1) {
736
+ scope.ambiguousAliases.add(alias);
737
+ continue;
656
738
  }
657
- if (!changed) break;
739
+ const syntax = [...targets][0];
740
+ scope.aliasSyntax.set(alias, syntax);
741
+ scope.aliases.set(alias, csharpUsingTarget(syntax));
658
742
  }
659
743
  }
660
744
 
661
745
  /** The directory whose `.git` marks the repository holding `dir`, or null. */
662
746
  function gitTopOf(index, dir) {
663
747
  return memoized(index, `\x01git-top\x00${dir}`, () => {
748
+ const sourceFs = index.isProvenanceFactIndex ? index.provenanceFs : fs;
664
749
  for (let current = dir; ; current = path.dirname(current)) {
665
- try { if (fs.existsSync(path.join(current, '.git'))) return current; } catch { /* keep walking */ }
750
+ try { if (sourceFs.existsSync(path.join(current, '.git'))) return current; } catch { /* keep walking */ }
666
751
  if (current === path.dirname(current)) return null;
667
752
  }
668
753
  });
@@ -679,6 +764,7 @@ function gitTopOf(index, dir) {
679
764
  * null marks a file that could not be read.
680
765
  */
681
766
  function csharpProjectFileUsings(index, filePath) {
767
+ const sourceFs = index.isProvenanceFactIndex ? index.provenanceFs : fs;
682
768
  const dirs = [];
683
769
  const root = path.resolve(index.root);
684
770
  let dir = path.dirname(filePath);
@@ -697,12 +783,12 @@ function csharpProjectFileUsings(index, filePath) {
697
783
  }
698
784
  return dirs.flatMap(current => memoized(index, `\x01cs-proj-usings\x00${current}`, () => {
699
785
  let names;
700
- try { names = fs.readdirSync(current); } catch { return []; }
786
+ try { names = sourceFs.readdirSync(current); } catch { return []; }
701
787
  const out = [];
702
788
  for (const file of names.sort()) {
703
789
  if (!/\.csproj$|^Directory\.Build\.(props|targets)$/.test(file)) continue;
704
790
  let text;
705
- try { text = fs.readFileSync(path.join(current, file), 'utf-8'); } catch { out.push(null); continue; }
791
+ try { text = sourceFs.readFileSync(path.join(current, file), 'utf-8'); } catch { out.push(null); continue; }
706
792
  for (const match of text.matchAll(/<Using\b([^>]*)>/g)) {
707
793
  const include = /\bInclude\s*=\s*"([^"]+)"/.exec(match[1]);
708
794
  if (!include) continue;
@@ -1167,6 +1253,7 @@ function stripExternalReceiverType(call, via) {
1167
1253
  }
1168
1254
 
1169
1255
  module.exports = {
1256
+ memoizedDerivation: memoized,
1170
1257
  externalTypeDenotation,
1171
1258
  externalReceiverVerdict,
1172
1259
  externalProducerVia,