ucn 5.5.0 → 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 +41 -27
- package/cli/index.js +6 -1
- package/core/accessors.js +341 -54
- package/core/account.js +106 -31
- package/core/analysis.js +266 -127
- package/core/ast-analysis.js +12 -3
- package/core/brief.js +8 -4
- package/core/build-worker.js +2 -1
- package/core/cache.js +144 -6
- package/core/call-name-cache.js +134 -0
- package/core/callable-scope.js +21 -0
- package/core/callers.js +5111 -1024
- 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 +228 -114
- 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 +1202 -157
- package/core/rust-modules.js +21 -3
- 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 +157 -46
- 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 +548 -89
- package/languages/utils.js +67 -8
- package/mcp/server.js +8 -2
- package/package.json +2 -2
package/core/type-denotation.js
CHANGED
|
@@ -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 (
|
|
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
|
-
|
|
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
|
-
!
|
|
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 =>
|
|
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 (
|
|
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
|
|
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,
|
|
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
|
-
*
|
|
606
|
-
*
|
|
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
|
-
|
|
611
|
-
|
|
612
|
-
|
|
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
|
|
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
|
-
|
|
625
|
-
|
|
626
|
-
|
|
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
|
|
716
|
+
if (!scopes) return;
|
|
647
717
|
const encloses = (outer, inner) => outer !== inner && (outer === '' || inner.startsWith(`${outer}.`));
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
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
|
-
|
|
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 (
|
|
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 =
|
|
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 =
|
|
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;
|
|
@@ -774,11 +860,14 @@ function rustScopedLine(index, filePath, line, name) {
|
|
|
774
860
|
if (line == null) return null;
|
|
775
861
|
const fileEntry = index.files.get(filePath);
|
|
776
862
|
if (fileEntry?.language !== 'rust') return null;
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
863
|
+
// Per file and name (fix #405: every `T::new()` site asked again).
|
|
864
|
+
const scoped = memoized(index, `scoped\x00${filePath}\x00${name}`, () => {
|
|
865
|
+
const declares = (index.symbols.get(name) || []).some(definition => definition.file === filePath &&
|
|
866
|
+
(TYPE_KINDS.has(definition.type) || definition.type === 'type'));
|
|
867
|
+
return declares || (fileEntry.importBindings || []).some(binding =>
|
|
868
|
+
binding.name !== '*' && (binding.alias || binding.name) === name && binding.module);
|
|
869
|
+
});
|
|
870
|
+
return scoped ? line : null;
|
|
782
871
|
}
|
|
783
872
|
|
|
784
873
|
/** Bindings visible at `line`: those of the innermost enclosing scope that
|
|
@@ -946,6 +1035,30 @@ function projectTypeNamed(index, name) {
|
|
|
946
1035
|
return (index.symbols.get(name) || []).some(definition => TYPE_KINDS.has(definition.type));
|
|
947
1036
|
}
|
|
948
1037
|
|
|
1038
|
+
/**
|
|
1039
|
+
* The type a call record writes and what it denotes in its file. A record
|
|
1040
|
+
* never changes once its file's calls are read, so the answer is kept per
|
|
1041
|
+
* record object while the index lives (fix #405b: orientation refines every
|
|
1042
|
+
* definition of a name over the same records, and each refinement asked
|
|
1043
|
+
* again per record).
|
|
1044
|
+
*/
|
|
1045
|
+
function siteDenotation(index, filePath, fileEntry, call) {
|
|
1046
|
+
const memos = memoOf(index);
|
|
1047
|
+
let memo = memos.get('\x01site');
|
|
1048
|
+
if (!memo) memos.set('\x01site', memo = new WeakMap());
|
|
1049
|
+
let site = memo.get(call);
|
|
1050
|
+
if (site === undefined) {
|
|
1051
|
+
const written = siteWrittenType(index, fileEntry, call);
|
|
1052
|
+
site = {
|
|
1053
|
+
written,
|
|
1054
|
+
denotation: written ? externalTypeDenotation(index, filePath, written.name, written.qualifier,
|
|
1055
|
+
call.line, { unscoped: written.unscoped === true }) : null,
|
|
1056
|
+
};
|
|
1057
|
+
memo.set(call, site);
|
|
1058
|
+
}
|
|
1059
|
+
return site;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
949
1062
|
/**
|
|
950
1063
|
* Verdict for a call site against candidate target definitions (the pinned
|
|
951
1064
|
* targets on the caller side, every same-name callable on the callee side).
|
|
@@ -961,10 +1074,7 @@ function externalTypeGate(index, filePath, fileEntry, call, targetsOrThunk) {
|
|
|
961
1074
|
// every project type with the method set satisfies; the existing
|
|
962
1075
|
// package-qualified physics routes it (possible dispatch).
|
|
963
1076
|
if (fileEntry.language === 'go') return null;
|
|
964
|
-
const written =
|
|
965
|
-
if (!written) return null;
|
|
966
|
-
const denotation = externalTypeDenotation(index, filePath, written.name, written.qualifier, call.line,
|
|
967
|
-
{ unscoped: written.unscoped === true });
|
|
1077
|
+
const { written, denotation } = siteDenotation(index, filePath, fileEntry, call);
|
|
968
1078
|
if (!denotation) return null;
|
|
969
1079
|
const { original, via } = denotation;
|
|
970
1080
|
// Only a real collision needs the gate: without a project type of that
|
|
@@ -1143,6 +1253,7 @@ function stripExternalReceiverType(call, via) {
|
|
|
1143
1253
|
}
|
|
1144
1254
|
|
|
1145
1255
|
module.exports = {
|
|
1256
|
+
memoizedDerivation: memoized,
|
|
1146
1257
|
externalTypeDenotation,
|
|
1147
1258
|
externalReceiverVerdict,
|
|
1148
1259
|
externalProducerVia,
|