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
|
@@ -307,9 +307,12 @@ function protocolMemberCertainty(index, def) {
|
|
|
307
307
|
const lang = index.files.get(def.file)?.language;
|
|
308
308
|
const predicate = langTraits(lang)?.languageProtocolMember;
|
|
309
309
|
if (typeof predicate !== 'function') return null;
|
|
310
|
-
|
|
310
|
+
// A C/C++ member type alias names its class as `enclosingType` (fix #427).
|
|
311
|
+
const isTypeMember = def.type === 'type' && !!(def.className || def.enclosingType);
|
|
312
|
+
const isMember = !!def.className || isTypeMember;
|
|
311
313
|
const verdict = predicate(def.name, {
|
|
312
314
|
isMember,
|
|
315
|
+
isTypeMember,
|
|
313
316
|
isField: def.type === 'field' || def.type === 'property' || def.type === 'constant',
|
|
314
317
|
modifiers: def.modifiers || [],
|
|
315
318
|
paramTypes: Array.isArray(def.paramsStructured)
|
|
@@ -480,6 +483,18 @@ function externalContractOf(index, def) {
|
|
|
480
483
|
line: def.nameLine || def.startLine }],
|
|
481
484
|
};
|
|
482
485
|
}
|
|
486
|
+
// A member type alias the standard library reads by name (fix #427).
|
|
487
|
+
if (def && !def.className && def.type === 'type' && def.enclosingType) {
|
|
488
|
+
const certainty = protocolMemberCertainty(index, def);
|
|
489
|
+
if (!certainty) return null;
|
|
490
|
+
return {
|
|
491
|
+
certainty,
|
|
492
|
+
via: ['standard library traits (iterator_traits, pointer_traits, container)'],
|
|
493
|
+
reason: 'language-protocol-member',
|
|
494
|
+
sites: [{ file: def.file, relativePath: def.relativePath || _rel(index, def.file),
|
|
495
|
+
line: def.nameLine || def.startLine }],
|
|
496
|
+
};
|
|
497
|
+
}
|
|
483
498
|
if (!def || !def.className) return null;
|
|
484
499
|
const defSite = {
|
|
485
500
|
file: def.file,
|
|
@@ -683,8 +698,10 @@ function _typeParams(...defs) {
|
|
|
683
698
|
/**
|
|
684
699
|
* Could `member` override (occupy the same slot as) `pin` by parameter
|
|
685
700
|
* types? Types compare by their terminal name; a type parameter of either
|
|
686
|
-
* side's
|
|
687
|
-
*
|
|
701
|
+
* side's class matches anything (`m(T)` overridden as `m(String)`). Method
|
|
702
|
+
* type parameters correspond by position (fix #426, JLS 8.4.2 / C#
|
|
703
|
+
* signature identity): `Send<TRequest>(TRequest)` never takes the slot of
|
|
704
|
+
* `Send<TResponse>(IRequest<TResponse>)`.
|
|
688
705
|
*/
|
|
689
706
|
function _slotParamsCompatible(index, member, pin) {
|
|
690
707
|
const params = d => (d.paramsStructured || []).filter(p => !p?.extensionReceiver &&
|
|
@@ -692,13 +709,16 @@ function _slotParamsCompatible(index, member, pin) {
|
|
|
692
709
|
const a = params(member);
|
|
693
710
|
const b = params(pin);
|
|
694
711
|
if (a.length !== b.length) return false;
|
|
712
|
+
const correspondence = _methodParamCorrespondence(index, member, pin);
|
|
713
|
+
if (!correspondence) return false;
|
|
695
714
|
const owners = d => (d.className ? (index.symbols.get(d.className) || [])
|
|
696
715
|
.filter(c => c.file === d.file && CLASS_KINDS.has(c.type)) : []);
|
|
697
|
-
const variables = _typeParams(
|
|
716
|
+
const variables = _typeParams(...owners(member), ...owners(pin));
|
|
698
717
|
for (let i = 0; i < a.length; i++) {
|
|
699
|
-
const ta = _slotTypeName(_paramTypeText(a[i]));
|
|
700
|
-
const tb = _slotTypeName(_paramTypeText(b[i]));
|
|
718
|
+
const ta = _slotTypeName(_substituteType(_paramTypeText(a[i]), correspondence.member));
|
|
719
|
+
const tb = _slotTypeName(_substituteType(_paramTypeText(b[i]), correspondence.slot));
|
|
701
720
|
if (!ta || !tb || ta === tb) continue;
|
|
721
|
+
if (ta.startsWith('$M') || tb.startsWith('$M')) return false;
|
|
702
722
|
if (variables.has(ta) || variables.has(tb)) continue;
|
|
703
723
|
if (/^[A-Z]$/.test(ta) || /^[A-Z]$/.test(tb)) continue;
|
|
704
724
|
return false;
|
|
@@ -706,6 +726,47 @@ function _slotParamsCompatible(index, member, pin) {
|
|
|
706
726
|
return true;
|
|
707
727
|
}
|
|
708
728
|
|
|
729
|
+
/** A method's own type parameters in order, with each one's erasure (its
|
|
730
|
+
* first bound's head, `Object` without one). */
|
|
731
|
+
function _methodTypeParamList(def) {
|
|
732
|
+
const raw = String(def?.generics || '').trim().replace(/^<|>$/g, '');
|
|
733
|
+
if (!raw) return [];
|
|
734
|
+
return _splitTypeArgs(raw).map(part => {
|
|
735
|
+
const text = part.replace(/@[\w.]+(?:\([^)]*\))?/g, ' ').trim();
|
|
736
|
+
const tokens = text.split(/[\s:=]+/).filter(Boolean);
|
|
737
|
+
const name = (tokens.find(token => !TYPE_PARAM_PREFIXES.has(token)) || '_').replace(/\.\.\.$/, '');
|
|
738
|
+
const bound = /\bextends\b/.test(text)
|
|
739
|
+
? text.split(/\bextends\b/)[1].split('&')[0].replace(/<.*$/s, '').trim() : null;
|
|
740
|
+
return { name, erasure: bound || 'Object' };
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* How the method type parameters of a member and of the slot it may take
|
|
746
|
+
* correspond (fix #426): position by position into shared placeholders
|
|
747
|
+
* (`$M0`, ..), so `<U> m(U)` takes the slot `<V> m(V)` and `<Q> m(Q)` never
|
|
748
|
+
* takes `<R> m(Box<R>)`'s. Unequal counts: Java lets a non-generic method
|
|
749
|
+
* take a generic slot through the slot's erasure (JLS 8.4.2 subsignature);
|
|
750
|
+
* anything else is another slot (null).
|
|
751
|
+
*/
|
|
752
|
+
function _methodParamCorrespondence(index, member, slot) {
|
|
753
|
+
const own = _methodTypeParamList(member);
|
|
754
|
+
const other = _methodTypeParamList(slot);
|
|
755
|
+
const memberMap = new Map();
|
|
756
|
+
const slotMap = new Map();
|
|
757
|
+
if (own.length === other.length) {
|
|
758
|
+
own.forEach((param, i) => memberMap.set(param.name, `$M${i}`));
|
|
759
|
+
other.forEach((param, i) => slotMap.set(param.name, `$M${i}`));
|
|
760
|
+
return { member: memberMap, slot: slotMap };
|
|
761
|
+
}
|
|
762
|
+
const lang = index.files.get(member.file)?.language;
|
|
763
|
+
if (own.length === 0 && !langTraits(lang)?.genericArityIsIdentity) {
|
|
764
|
+
for (const param of other) slotMap.set(param.name, param.erasure);
|
|
765
|
+
return { member: memberMap, slot: slotMap };
|
|
766
|
+
}
|
|
767
|
+
return null;
|
|
768
|
+
}
|
|
769
|
+
|
|
709
770
|
// ---------------------------------------------------------------------------
|
|
710
771
|
// Generic base clauses as type-argument substitutions (fix #390)
|
|
711
772
|
// ---------------------------------------------------------------------------
|
|
@@ -837,21 +898,19 @@ function _slotParams(def) {
|
|
|
837
898
|
|
|
838
899
|
/**
|
|
839
900
|
* Does `member` take the slot whose parameter types, in the member's own
|
|
840
|
-
* class terms, are `slotTypes`? Method
|
|
841
|
-
*
|
|
901
|
+
* class terms, are `slotTypes`? Method type parameters correspond by
|
|
902
|
+
* position (fix #426) with those of `slotDef`, the method `slotTypes` were
|
|
903
|
+
* read from.
|
|
842
904
|
*/
|
|
843
|
-
function _slotTypesMatch(member, slotTypes,
|
|
905
|
+
function _slotTypesMatch(index, member, slotTypes, slotDef) {
|
|
844
906
|
const params = _slotParams(member);
|
|
845
907
|
if (params.length !== slotTypes.length) return false;
|
|
846
|
-
const
|
|
847
|
-
|
|
908
|
+
const correspondence = _methodParamCorrespondence(index, member, slotDef);
|
|
909
|
+
if (!correspondence) return false;
|
|
848
910
|
for (let i = 0; i < params.length; i++) {
|
|
849
|
-
const a = _slotTypeKey(params[i]?.type);
|
|
850
|
-
const b = _slotTypeKey(slotTypes[i]);
|
|
911
|
+
const a = _slotTypeKey(_substituteType(params[i]?.type, correspondence.member));
|
|
912
|
+
const b = _slotTypeKey(_substituteType(slotTypes[i], correspondence.slot));
|
|
851
913
|
if (!a || !b || a === b) continue;
|
|
852
|
-
const baseA = a.replace(/[^\w$].*$/s, '');
|
|
853
|
-
const baseB = b.replace(/[^\w$].*$/s, '');
|
|
854
|
-
if (wild.has(baseA) || wild.has(baseB)) continue;
|
|
855
914
|
return false;
|
|
856
915
|
}
|
|
857
916
|
return true;
|
|
@@ -880,11 +939,18 @@ function _explicitInterfaceName(member) {
|
|
|
880
939
|
? String(member.explicitInterface).replace(/<.*$/s, '').split('.').pop().trim() : null;
|
|
881
940
|
}
|
|
882
941
|
|
|
883
|
-
|
|
942
|
+
// A value slot (fix #420): fields and properties of one name, and the
|
|
943
|
+
// accessors that implement them.
|
|
944
|
+
const VALUE_SLOT_KINDS = new Set(['field', 'private field', 'constant', 'state', 'get', 'set', 'property']);
|
|
945
|
+
|
|
946
|
+
function _slotMembersOf(index, name, ref, pin, overloads, frame = null, slotInterfaces = null, valueSlot = false,
|
|
947
|
+
byClass = null) {
|
|
884
948
|
const { ownedBy } = require('./class-identity');
|
|
885
949
|
const refFile = ref.def?.file || null;
|
|
886
|
-
let defs = (index.symbols.get(name) || []).filter(member => {
|
|
887
|
-
if (
|
|
950
|
+
let defs = (byClass ? byClass.get(ref.name) || [] : index.symbols.get(name) || []).filter(member => {
|
|
951
|
+
if (member.className !== ref.name) return false;
|
|
952
|
+
if (valueSlot ? !VALUE_SLOT_KINDS.has(member.type) && !VALUE_SLOT_KINDS.has(member.memberType)
|
|
953
|
+
: NON_CALLABLE_TYPES.has(member.type)) return false;
|
|
888
954
|
const owned = ownedBy(index, member, ref);
|
|
889
955
|
return owned === 'yes' || (owned === 'maybe' && refFile && member.file === refFile);
|
|
890
956
|
});
|
|
@@ -912,18 +978,19 @@ function _slotMembersOf(index, name, ref, pin, overloads, frame = null, slotInte
|
|
|
912
978
|
same = same.filter(member => methodArity(member) === pinMethodArity);
|
|
913
979
|
}
|
|
914
980
|
if (frame && arity != null) {
|
|
915
|
-
const pinMethodParams = _orderedTypeParams({ generics: pin.generics }) || [];
|
|
916
981
|
if (frame.slotTypes) {
|
|
982
|
+
const slotDef = frame.slotDef || pin;
|
|
917
983
|
return same.filter(member => _arity(member) == null ||
|
|
918
|
-
_slotTypesMatch(member, frame.slotTypes,
|
|
984
|
+
_slotTypesMatch(index, member, frame.slotTypes, slotDef));
|
|
919
985
|
}
|
|
920
986
|
if (frame.memberSubst) {
|
|
921
|
-
const
|
|
987
|
+
const targetTypes = frame.targetTypes || _slotParams(pin).map(p => p?.type);
|
|
988
|
+
const targetDef = frame.targetDef || pin;
|
|
922
989
|
return same.filter(member => {
|
|
923
990
|
if (_arity(member) == null) return true;
|
|
924
991
|
const inPinTerms = { ...member, paramsStructured: _slotParams(member)
|
|
925
992
|
.map(p => ({ ...p, type: _substituteType(p?.type, frame.memberSubst) })) };
|
|
926
|
-
return _slotTypesMatch(inPinTerms,
|
|
993
|
+
return _slotTypesMatch(index, inPinTerms, targetTypes, targetDef);
|
|
927
994
|
});
|
|
928
995
|
}
|
|
929
996
|
}
|
|
@@ -952,12 +1019,21 @@ function _slotMembersOf(index, name, ref, pin, overloads, frame = null, slotInte
|
|
|
952
1019
|
* @returns {{pinRef, ancestors: {ref, members}[], descendants: {ref, members,
|
|
953
1020
|
* viaImplements}[], unresolved: string[], external: string[]}|null}
|
|
954
1021
|
*/
|
|
955
|
-
function hierarchySlotClosure(index, def, startRef = null) {
|
|
1022
|
+
function hierarchySlotClosure(index, def, startRef = null, options = {}) {
|
|
1023
|
+
const valueSlot = options.values === true;
|
|
1024
|
+
// The name's definitions by class, read once for every class visited.
|
|
1025
|
+
const byClass = new Map();
|
|
1026
|
+
for (const member of index.symbols.get(def.name) || []) {
|
|
1027
|
+
if (!member.className) continue;
|
|
1028
|
+
if (!byClass.has(member.className)) byClass.set(member.className, []);
|
|
1029
|
+
byClass.get(member.className).push(member);
|
|
1030
|
+
}
|
|
956
1031
|
const { ownerRefOf, supertypeRefsOf, classDefsNamed } = require('./class-identity');
|
|
957
1032
|
const pinRef = startRef || ownerRefOf(index, def);
|
|
958
1033
|
if (!pinRef || (!pinRef.key && !pinRef.def)) return null;
|
|
959
1034
|
const lang = index.files.get(def.file)?.language;
|
|
960
|
-
const
|
|
1035
|
+
const traits = langTraits(lang);
|
|
1036
|
+
const overloads = !!traits?.hasArityOverloads;
|
|
961
1037
|
const name = def.name;
|
|
962
1038
|
// Only a member declared inside the class body is constrained by the
|
|
963
1039
|
// class's implements clause (a Rust inherent method never fills the
|
|
@@ -974,15 +1050,19 @@ function hierarchySlotClosure(index, def, startRef = null) {
|
|
|
974
1050
|
.find(e => e.def.file === file);
|
|
975
1051
|
return entry ? { name: parent.name, key: entry.key, def: entry.def } : null;
|
|
976
1052
|
};
|
|
1053
|
+
// A member that hides the inherited one (C# `new`, fix #426) starts a
|
|
1054
|
+
// slot of its own: it never joins the inherited member's slot.
|
|
1055
|
+
const hides = member => !!traits?.memberHidingModifier &&
|
|
1056
|
+
(member?.modifiers || []).includes(traits.memberHidingModifier);
|
|
977
1057
|
|
|
978
1058
|
const ancestors = [];
|
|
979
1059
|
const unresolved = [];
|
|
980
1060
|
const external = [];
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
//
|
|
984
|
-
//
|
|
985
|
-
|
|
1061
|
+
// Classes that hold the slot (declare a member of it, or inherit one).
|
|
1062
|
+
const inSlot = new Set([refKey(pinRef)]);
|
|
1063
|
+
// Classes whose supertypes some climb already walked: a later climb
|
|
1064
|
+
// reaching one stops there, everything above it is known.
|
|
1065
|
+
const explored = new Set();
|
|
986
1066
|
// Interfaces whose member of this name a class on the upward path
|
|
987
1067
|
// implements explicitly (C#).
|
|
988
1068
|
const explicitViaOf = (ref, inherited) => {
|
|
@@ -991,45 +1071,86 @@ function hierarchySlotClosure(index, def, startRef = null) {
|
|
|
991
1071
|
if (own.length === 0) return inherited || null;
|
|
992
1072
|
return new Set([...(inherited || []), ...own]);
|
|
993
1073
|
};
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1074
|
+
// Each class carries the substitution of its type parameters into the
|
|
1075
|
+
// origin's terms, composed over the generic base clauses walked (fix
|
|
1076
|
+
// #390): `Mid<T> : Base<T, bool>` maps Base's S to the origin's T. null
|
|
1077
|
+
// when a base clause on the way cannot be read.
|
|
1078
|
+
const pendingRoots = [];
|
|
1079
|
+
// Where a class member is overridden only when it is virtual (C++
|
|
1080
|
+
// `virtual`, C# `virtual`/`abstract`/`override`; trait
|
|
1081
|
+
// virtualMemberModifiers), a holder's climb joins a class's member only
|
|
1082
|
+
// when it is (fix #426: `struct D : Pool, Plain` overrides Pool's
|
|
1083
|
+
// virtual, never Plain's plain member). Interface members always are.
|
|
1084
|
+
const virtualModifiers = traits?.virtualMemberModifiers || null;
|
|
1085
|
+
const overridable = (ref, member) => !virtualModifiers || ref.def?.type === 'interface' ||
|
|
1086
|
+
(member.modifiers || []).some(modifier => virtualModifiers.includes(modifier));
|
|
1087
|
+
const climb = (origin, originTypes, originDef, interfacesOnly, virtualOnly = false) => {
|
|
1088
|
+
const queue = [{ ref: origin, contextFile: origin.def?.file || def.file,
|
|
1089
|
+
subst: overloads ? new Map() : null, explicitVia: explicitViaOf(origin, null) }];
|
|
1090
|
+
explored.add(refKey(origin));
|
|
1091
|
+
let steps = 0;
|
|
1092
|
+
while (queue.length > 0 && steps++ < 256) {
|
|
1093
|
+
const current = queue.shift();
|
|
1094
|
+
for (const parent of supertypeRefsOf(index, current.ref,
|
|
1095
|
+
{ implements: followImplements })) {
|
|
1096
|
+
if (parent.external) {
|
|
1097
|
+
if (!external.includes(parent.name)) external.push(parent.name);
|
|
1098
|
+
continue;
|
|
1015
1099
|
}
|
|
1100
|
+
const resolved = resolveLoose(parent, current.contextFile);
|
|
1101
|
+
if (!resolved) continue;
|
|
1102
|
+
const key = refKey(resolved);
|
|
1103
|
+
if (explored.has(key)) continue;
|
|
1104
|
+
// A hiding member's slot reaches only the interfaces its own
|
|
1105
|
+
// class declares.
|
|
1106
|
+
if (interfacesOnly && current.ref === origin && resolved.def?.type !== 'interface') continue;
|
|
1107
|
+
let subst = null;
|
|
1108
|
+
if (current.subst && resolved.def) {
|
|
1109
|
+
const step = _baseSubstitution(index, current.ref, resolved);
|
|
1110
|
+
if (step) {
|
|
1111
|
+
subst = new Map();
|
|
1112
|
+
for (const [param, arg] of step) subst.set(param, _substituteType(arg, current.subst));
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
// A class on the way that implements this interface's member
|
|
1116
|
+
// explicitly keeps the slot out of the interface (fix #395,
|
|
1117
|
+
// C#): `string IValidator.M()` beside `protected virtual M()`
|
|
1118
|
+
// are two slots.
|
|
1119
|
+
if (!_explicitInterfaceName(originDef) && current.explicitVia?.has(resolved.name)) continue;
|
|
1120
|
+
// An interface member is implemented by public members only (C#,
|
|
1121
|
+
// Java, TS): a `protected`/`private`/`internal` member is never
|
|
1122
|
+
// its slot (fix #395).
|
|
1123
|
+
if (resolved.def?.type === 'interface' && !_explicitInterfaceName(originDef) &&
|
|
1124
|
+
_nonPublicMember(originDef)) continue;
|
|
1125
|
+
explored.add(key);
|
|
1126
|
+
// Types compare in the origin's terms; without them (a base
|
|
1127
|
+
// clause below that could not be read) they compare loosely
|
|
1128
|
+
// with the pin's.
|
|
1129
|
+
const frame = subst && originTypes
|
|
1130
|
+
? { memberSubst: subst, targetTypes: originTypes, targetDef: originDef } : null;
|
|
1131
|
+
const declared = inSlot.has(key) ? [] : _slotMembersOf(index, name, resolved, def, overloads,
|
|
1132
|
+
frame, null, valueSlot, byClass);
|
|
1133
|
+
// A member hiding the inherited one (C# `new virtual M`) is
|
|
1134
|
+
// the top of the slot below it: it joins, and what is above
|
|
1135
|
+
// it is another slot.
|
|
1136
|
+
const hiding = declared.length > 0 && declared.every(hides);
|
|
1137
|
+
const members = declared.filter(member => !virtualOnly || overridable(resolved, member));
|
|
1138
|
+
if (members.length > 0) {
|
|
1139
|
+
inSlot.add(key);
|
|
1140
|
+
const entry = { ref: resolved, members };
|
|
1141
|
+
ancestors.push(entry);
|
|
1142
|
+
pendingRoots.push(entry);
|
|
1143
|
+
}
|
|
1144
|
+
if (hiding) continue;
|
|
1145
|
+
queue.push({ ref: resolved, contextFile: resolved.def?.file || current.contextFile, subst,
|
|
1146
|
+
explicitVia: explicitViaOf(resolved, current.explicitVia) });
|
|
1016
1147
|
}
|
|
1017
|
-
// A class on the way that implements this interface's member
|
|
1018
|
-
// explicitly keeps the pin's slot out of the interface (fix
|
|
1019
|
-
// #395, C#): `string IValidator.M()` beside `protected virtual
|
|
1020
|
-
// M()` are two slots.
|
|
1021
|
-
if (!_explicitInterfaceName(def) && current.explicitVia?.has(resolved.name)) continue;
|
|
1022
|
-
// An interface member is implemented by public members only (C#,
|
|
1023
|
-
// Java, TS): a `protected`/`private`/`internal` member is never
|
|
1024
|
-
// its slot (fix #395).
|
|
1025
|
-
if (resolved.def?.type === 'interface' && !_explicitInterfaceName(def) && _nonPublicMember(def)) continue;
|
|
1026
|
-
const members = _slotMembersOf(index, name, resolved, def, overloads,
|
|
1027
|
-
subst ? { memberSubst: subst } : null);
|
|
1028
|
-
if (members.length > 0) ancestors.push({ ref: resolved, members });
|
|
1029
|
-
upQueue.push({ ref: resolved, contextFile: resolved.def?.file || current.contextFile, subst,
|
|
1030
|
-
explicitVia: explicitViaOf(resolved, current.explicitVia) });
|
|
1031
1148
|
}
|
|
1032
|
-
}
|
|
1149
|
+
};
|
|
1150
|
+
climb(pinRef, overloads ? _slotParams(def).map(p => p?.type) : null, def, hides(def));
|
|
1151
|
+
// The pin's own climb found the ancestors; descendants' climbs (below)
|
|
1152
|
+
// add the supertypes a slot holder reaches beside the pin's line.
|
|
1153
|
+
pendingRoots.length = 0;
|
|
1033
1154
|
|
|
1034
1155
|
// Children spelled their parent with a qualifier the inheritance graph
|
|
1035
1156
|
// kept (`extends Outer.Inner<T>`): look those buckets up too.
|
|
@@ -1049,8 +1170,7 @@ function hierarchySlotClosure(index, def, startRef = null) {
|
|
|
1049
1170
|
// Descendants of every slot owner: the pin's class and each declaring
|
|
1050
1171
|
// ancestor (siblings of the pin override the same slot).
|
|
1051
1172
|
const descendants = [];
|
|
1052
|
-
const
|
|
1053
|
-
const downSeen = new Set(roots.map(refKey));
|
|
1173
|
+
const downSeen = new Set([pinRef, ...ancestors.map(a => a.ref)].map(refKey));
|
|
1054
1174
|
// Each class carries the slot's parameter types in its own terms, read
|
|
1055
1175
|
// through the generic base clauses from the root that declares the slot
|
|
1056
1176
|
// (fix #390): below `Fmt : Visitor<TextWriter, bool>` the slot
|
|
@@ -1061,84 +1181,141 @@ function hierarchySlotClosure(index, def, startRef = null) {
|
|
|
1061
1181
|
// virtual (fix #395); a subclass re-implementing the interface is found
|
|
1062
1182
|
// through the interface.
|
|
1063
1183
|
const onlyExplicit = members => members.length > 0 && members.every(m => m.explicitInterface);
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1184
|
+
let interfaceSlot = false;
|
|
1185
|
+
const slotInterfaces = new Set(_explicitInterfaceName(def) ? [_explicitInterfaceName(def)] : []);
|
|
1186
|
+
const noteInterfaces = () => {
|
|
1187
|
+
interfaceSlot = pinRef.def?.type === 'interface' || !!def.explicitInterface ||
|
|
1188
|
+
ancestors.some(a => a.ref.def?.type === 'interface' && a.members.length > 0);
|
|
1189
|
+
if (pinRef.def?.type === 'interface') slotInterfaces.add(pinRef.name);
|
|
1190
|
+
for (const a of ancestors) {
|
|
1191
|
+
if (a.ref.def?.type === 'interface' && a.members.length > 0) slotInterfaces.add(a.ref.name);
|
|
1192
|
+
}
|
|
1193
|
+
};
|
|
1194
|
+
noteInterfaces();
|
|
1195
|
+
const rootEntry = (ref, members) => ({ ref,
|
|
1196
|
+
slotTypes: overloads && members.length === 1 ? slotTypesOf(members[0]) : null,
|
|
1197
|
+
slotDef: members.length === 1 ? members[0] : def,
|
|
1198
|
+
explicitOnly: onlyExplicit(members) });
|
|
1073
1199
|
const downQueue = [
|
|
1074
|
-
{ ref: pinRef, slotTypes: overloads ? slotTypesOf(def) : null,
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
explicitOnly: onlyExplicit(a.members) })),
|
|
1200
|
+
{ ref: pinRef, slotTypes: overloads ? slotTypesOf(def) : null, slotDef: def,
|
|
1201
|
+
explicitOnly: !!def.explicitInterface },
|
|
1202
|
+
...ancestors.map(a => rootEntry(a.ref, a.members)),
|
|
1078
1203
|
];
|
|
1079
|
-
// The slot's parameter types in each class's own terms (null: unknown)
|
|
1204
|
+
// The slot's parameter types in each class's own terms (null: unknown),
|
|
1205
|
+
// and the method whose type parameters they are spelled with.
|
|
1080
1206
|
const slotTypesByKey = new Map(downQueue.map(entry => [refKey(entry.ref), entry.slotTypes]));
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
const
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
const
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1207
|
+
const slotDefsByKey = new Map(downQueue.map(entry => [refKey(entry.ref), entry.slotDef]));
|
|
1208
|
+
// Supertypes of a slot holder beside the pin's line fill the slot too
|
|
1209
|
+
// (fix #426): a class's member implements the same-signature member of
|
|
1210
|
+
// every interface it implements (`GenericPool implements Pool,
|
|
1211
|
+
// PoolMXBean`), and where the language lets an inherited member
|
|
1212
|
+
// implement (Java, C#, TS), a class inheriting the slot member fills
|
|
1213
|
+
// its own interfaces with it (`Impl extends Base implements Bean`).
|
|
1214
|
+
const climbsFromHolders = !!traits?.memberFillsSupertypeSlots;
|
|
1215
|
+
const climbsFromInheritors = !!traits?.inheritedMemberFillsSlots;
|
|
1216
|
+
let rounds = 0;
|
|
1217
|
+
while (downQueue.length > 0 && rounds++ < 64) {
|
|
1218
|
+
while (downQueue.length > 0 && descendants.length < 5000) {
|
|
1219
|
+
const currentEntry = downQueue.shift();
|
|
1220
|
+
const current = currentEntry.ref;
|
|
1221
|
+
const children = [
|
|
1222
|
+
...(currentEntry.explicitOnly ? [] : qualifiedChildKeys(current.name).flatMap(key =>
|
|
1223
|
+
index.extendedByGraph.get(key) || []).map(child =>
|
|
1224
|
+
typeof child === 'string' ? { name: child, file: null }
|
|
1225
|
+
: { name: child.name, file: child.file || null })),
|
|
1226
|
+
...implementedBy(index, current.name),
|
|
1227
|
+
];
|
|
1228
|
+
for (const child of children) {
|
|
1229
|
+
if (!child.name) continue;
|
|
1230
|
+
const entries = classDefsNamed(index, child.name).entries
|
|
1231
|
+
.filter(e => !child.file || e.def.file === child.file);
|
|
1232
|
+
for (const entry of entries) {
|
|
1233
|
+
if (downSeen.has(entry.key)) continue;
|
|
1234
|
+
const childRef = { name: child.name, key: entry.key, def: entry.def };
|
|
1235
|
+
const link = supertypeRefsOf(index, childRef, { implements: true }).find(p => {
|
|
1236
|
+
if (p.external || _slotTypeName(p.name) !== current.name) return false;
|
|
1237
|
+
if (p.key) return p.key === current.key;
|
|
1238
|
+
return !!current.def?.file &&
|
|
1239
|
+
index._resolveClassFile(current.name, entry.def.file) === current.def.file;
|
|
1240
|
+
});
|
|
1241
|
+
if (!link) continue;
|
|
1242
|
+
downSeen.add(entry.key);
|
|
1243
|
+
let slotTypes = null;
|
|
1244
|
+
let instantiations = null;
|
|
1245
|
+
if (currentEntry.slotTypes && current.def) {
|
|
1246
|
+
const steps = _baseSubstitutions(index, childRef, current);
|
|
1247
|
+
if (steps?.length === 1) {
|
|
1248
|
+
slotTypes = currentEntry.slotTypes.map(type => _substituteType(type, steps[0]));
|
|
1249
|
+
} else if (steps?.length > 1) {
|
|
1250
|
+
instantiations = steps.map(step => currentEntry.slotTypes.map(type => _substituteType(type, step)));
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
const slotDef = currentEntry.slotDef || def;
|
|
1254
|
+
// A class implementing the generic base more than once fills
|
|
1255
|
+
// the slot once per instantiation (fix #400).
|
|
1256
|
+
let members = instantiations
|
|
1257
|
+
? [...new Set(instantiations.flatMap(types => _slotMembersOf(index, name, childRef, def,
|
|
1258
|
+
overloads, { slotTypes: types, slotDef }, slotInterfaces, valueSlot, byClass)))]
|
|
1259
|
+
: _slotMembersOf(index, name, childRef, def, overloads,
|
|
1260
|
+
slotTypes ? { slotTypes, slotDef } : null, slotInterfaces, valueSlot, byClass);
|
|
1261
|
+
if (link.viaImplements) members = members.filter(m => memberInClassBody(index, m));
|
|
1262
|
+
// In an interface member's slot a class takes part with a
|
|
1263
|
+
// public member or an explicit implementation only (fix
|
|
1264
|
+
// #395): a protected override below it overrides another
|
|
1265
|
+
// slot.
|
|
1266
|
+
if (interfaceSlot && childRef.def?.type !== 'interface') {
|
|
1267
|
+
members = members.filter(m => !!m.explicitInterface || !_nonPublicMember(m));
|
|
1268
|
+
}
|
|
1269
|
+
// A member hiding the slot member (C# `new`) is another
|
|
1270
|
+
// slot, and so is everything below it.
|
|
1271
|
+
if (members.length > 0 && members.every(hides)) continue;
|
|
1272
|
+
members = members.filter(m => !hides(m));
|
|
1273
|
+
inSlot.add(entry.key);
|
|
1274
|
+
descendants.push({ ref: childRef, members, viaImplements: !!link.viaImplements });
|
|
1275
|
+
downQueue.push({ ref: childRef, slotTypes, slotDef, explicitOnly: onlyExplicit(members) });
|
|
1276
|
+
slotTypesByKey.set(entry.key, slotTypes);
|
|
1277
|
+
slotDefsByKey.set(entry.key, slotDef);
|
|
1278
|
+
if (!onlyExplicit(members) &&
|
|
1279
|
+
(members.length > 0 ? climbsFromHolders : climbsFromInheritors)) {
|
|
1280
|
+
// A declared member overrides; an inherited one is the
|
|
1281
|
+
// implementation its provider declares, virtual or not.
|
|
1282
|
+
climb(childRef, slotTypes, members.length === 1 ? members[0] : def, false,
|
|
1283
|
+
members.length > 0);
|
|
1114
1284
|
}
|
|
1115
1285
|
}
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
// slot.
|
|
1128
|
-
if (interfaceSlot && childRef.def?.type !== 'interface') {
|
|
1129
|
-
members = members.filter(m => !!m.explicitInterface || !_nonPublicMember(m));
|
|
1130
|
-
}
|
|
1131
|
-
descendants.push({ ref: childRef, members, viaImplements: !!link.viaImplements });
|
|
1132
|
-
downQueue.push({ ref: childRef, slotTypes, explicitOnly: onlyExplicit(members) });
|
|
1133
|
-
slotTypesByKey.set(entry.key, slotTypes);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
// Ancestors a holder's climb joined: their descendants hold the slot.
|
|
1289
|
+
if (pendingRoots.length > 0) {
|
|
1290
|
+
noteInterfaces();
|
|
1291
|
+
for (const root of pendingRoots.splice(0)) {
|
|
1292
|
+
downSeen.add(refKey(root.ref));
|
|
1293
|
+
const entry = rootEntry(root.ref, root.members);
|
|
1294
|
+
downQueue.push(entry);
|
|
1295
|
+
slotTypesByKey.set(refKey(root.ref), entry.slotTypes);
|
|
1296
|
+
slotDefsByKey.set(refKey(root.ref), entry.slotDef);
|
|
1134
1297
|
}
|
|
1135
1298
|
}
|
|
1136
1299
|
}
|
|
1137
1300
|
if (startRef) {
|
|
1138
|
-
const own = _slotMembersOf(index, name, startRef, def, overloads);
|
|
1301
|
+
const own = _slotMembersOf(index, name, startRef, def, overloads, null, null, valueSlot, byClass);
|
|
1139
1302
|
if (own.length > 0) ancestors.unshift({ ref: startRef, members: own });
|
|
1140
1303
|
}
|
|
1141
|
-
return { pinRef, ancestors, descendants, unresolved, external, slotTypesByKey };
|
|
1304
|
+
return { pinRef, ancestors, descendants, unresolved, external, slotTypesByKey, slotDefsByKey };
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
function sourceSyntaxRecord(index, file, name, content, kind, positions, accept) {
|
|
1308
|
+
const cache = require('./cache');
|
|
1309
|
+
const { indexedContent, parseViewParts, syntaxMode } = require('./source-memo');
|
|
1310
|
+
const entry = index.files.get(file);
|
|
1311
|
+
const eligible = entry?.hash && index._usageResultCache && !index.isProvenanceFactIndex &&
|
|
1312
|
+
indexedContent(index, file, entry, content);
|
|
1313
|
+
const mode = eligible ? syntaxMode('contractSyntax', [...parseViewParts(entry), positions], kind) : null;
|
|
1314
|
+
const stored = eligible ? cache.getUsageResult(index, file, name, mode) : null;
|
|
1315
|
+
return {
|
|
1316
|
+
value: Array.isArray(stored) && accept(stored) ? stored : null,
|
|
1317
|
+
save(value) { if (eligible) cache.storeUsageResult(index, file, name, mode, value); },
|
|
1318
|
+
};
|
|
1142
1319
|
}
|
|
1143
1320
|
|
|
1144
1321
|
/**
|
|
@@ -1157,7 +1334,6 @@ function anonymousSlotMembers(index, name, perFile, closure, pin) {
|
|
|
1157
1334
|
...closure.descendants.map(d => d.ref)].filter(ref => ref?.key).map(ref => ref.key));
|
|
1158
1335
|
if (slotKeys.size === 0) return [];
|
|
1159
1336
|
const overloads = !!langTraits(index.files.get(pin.file)?.language)?.hasArityOverloads;
|
|
1160
|
-
const pinMethodParams = _orderedTypeParams({ generics: pin.generics }) || [];
|
|
1161
1337
|
const out = [];
|
|
1162
1338
|
for (const [file, lineNos] of perFile) {
|
|
1163
1339
|
const fileEntry = index.files.get(file);
|
|
@@ -1168,12 +1344,16 @@ function anonymousSlotMembers(index, name, perFile, closure, pin) {
|
|
|
1168
1344
|
if (!javaLike && !classExpressionLanguage) continue;
|
|
1169
1345
|
let content;
|
|
1170
1346
|
try { content = index._readFile(file); } catch { continue; }
|
|
1347
|
+
const lines = [...lineNos].sort((a, b) => a - b);
|
|
1348
|
+
const syntax = sourceSyntaxRecord(index, file, name, content, 'anonymous', lines,
|
|
1349
|
+
value => value.length === 1 && value[0] === 0);
|
|
1350
|
+
if (syntax.value) continue;
|
|
1171
1351
|
const tree = index._getParsedTree?.(file, content, language);
|
|
1172
1352
|
if (!tree) continue;
|
|
1173
1353
|
const namespace = (fileEntry.symbols || []).find(s => s.namespace)?.namespace || null;
|
|
1174
|
-
const lines = [...lineNos].sort((a, b) => a - b);
|
|
1175
1354
|
const fileLines = index._getFileLines(file);
|
|
1176
1355
|
const seen = new Set();
|
|
1356
|
+
let hasAnonymousMember = false;
|
|
1177
1357
|
for (const line of lines) {
|
|
1178
1358
|
const text = fileLines[line - 1] || '';
|
|
1179
1359
|
for (let at = text.indexOf(name); at >= 0; at = text.indexOf(name, at + 1)) {
|
|
@@ -1201,6 +1381,9 @@ function anonymousSlotMembers(index, name, perFile, closure, pin) {
|
|
|
1201
1381
|
typeText = value ? `${value.text}${extendsClause.namedChildren.find(child => child.type === 'type_arguments')?.text || ''}` : null;
|
|
1202
1382
|
} else continue;
|
|
1203
1383
|
if (!typeText) continue;
|
|
1384
|
+
// Only syntactic absence is cached. An unbound or external
|
|
1385
|
+
// owner may bind to a project slot after another file changes.
|
|
1386
|
+
hasAnonymousMember = true;
|
|
1204
1387
|
const key = `${file}:${nameNode.startIndex}`;
|
|
1205
1388
|
if (seen.has(key)) continue;
|
|
1206
1389
|
seen.add(key);
|
|
@@ -1237,9 +1420,9 @@ function anonymousSlotMembers(index, name, perFile, closure, pin) {
|
|
|
1237
1420
|
slotTypes = null; // diamond `new X<>() {..}`: arguments inferred
|
|
1238
1421
|
}
|
|
1239
1422
|
}
|
|
1240
|
-
const member = { paramsStructured: params, generics: null };
|
|
1423
|
+
const member = { paramsStructured: params, generics: null, file };
|
|
1241
1424
|
const fits = slotTypes
|
|
1242
|
-
? _slotTypesMatch(member, slotTypes,
|
|
1425
|
+
? _slotTypesMatch(index, member, slotTypes, closure.slotDefsByKey?.get(ref.key) || pin)
|
|
1243
1426
|
: params.length === _slotParams(pin).length;
|
|
1244
1427
|
if (!fits) continue;
|
|
1245
1428
|
if (!slotTypes) {
|
|
@@ -1250,6 +1433,7 @@ function anonymousSlotMembers(index, name, perFile, closure, pin) {
|
|
|
1250
1433
|
out.push({ ...site, verdict: 'edit' });
|
|
1251
1434
|
}
|
|
1252
1435
|
}
|
|
1436
|
+
if (!hasAnonymousMember) syntax.save([0]);
|
|
1253
1437
|
}
|
|
1254
1438
|
return out;
|
|
1255
1439
|
}
|
|
@@ -2111,6 +2295,14 @@ function rustMacroTemplateSites(index, name, files) {
|
|
|
2111
2295
|
// Only files that define a macro can hold template tokens; the
|
|
2112
2296
|
// AST below decides everything, this only skips needless parses.
|
|
2113
2297
|
if (!content.includes('macro_rules!')) continue;
|
|
2298
|
+
const syntax = sourceSyntaxRecord(index, file, name, content, 'rust-template', null,
|
|
2299
|
+
validRustTemplateSyntax);
|
|
2300
|
+
if (syntax.value) {
|
|
2301
|
+
for (const fact of syntax.value) sites.push({ file, relativePath: entry.relativePath || file,
|
|
2302
|
+
...fact, owners: fact.owners.map(owner => ({ ...owner })) });
|
|
2303
|
+
continue;
|
|
2304
|
+
}
|
|
2305
|
+
const siteStart = sites.length;
|
|
2114
2306
|
const tree = index._getParsedTree?.(file, content, 'rust') || safeParse(parser, content);
|
|
2115
2307
|
if (!tree) continue;
|
|
2116
2308
|
const macros = [];
|
|
@@ -2122,7 +2314,7 @@ function rustMacroTemplateSites(index, name, files) {
|
|
|
2122
2314
|
if (node.type === 'macro_invocation') invocations.push(node);
|
|
2123
2315
|
for (let i = node.namedChildCount - 1; i >= 0; i--) stack.push(node.namedChild(i));
|
|
2124
2316
|
}
|
|
2125
|
-
if (macros.length === 0) continue;
|
|
2317
|
+
if (macros.length === 0) { syntax.save([]); continue; }
|
|
2126
2318
|
const invocationContexts = (macroName) => {
|
|
2127
2319
|
const contexts = [];
|
|
2128
2320
|
for (const invocation of invocations) {
|
|
@@ -2200,12 +2392,26 @@ function rustMacroTemplateSites(index, name, files) {
|
|
|
2200
2392
|
};
|
|
2201
2393
|
walk(macro, false);
|
|
2202
2394
|
}
|
|
2395
|
+
syntax.save(sites.slice(siteStart).map(({ file: _file, relativePath: _relativePath, ...fact }) =>
|
|
2396
|
+
({ ...fact, owners: fact.owners.map(owner => ({ ...owner })) })));
|
|
2203
2397
|
}
|
|
2204
2398
|
sites.sort((a, b) => (a.relativePath < b.relativePath ? -1
|
|
2205
2399
|
: a.relativePath > b.relativePath ? 1 : a.line - b.line || a.column - b.column));
|
|
2206
2400
|
return sites;
|
|
2207
2401
|
}
|
|
2208
2402
|
|
|
2403
|
+
function validRustTemplateSyntax(value) {
|
|
2404
|
+
const optionalString = text => text == null || typeof text === 'string';
|
|
2405
|
+
return value.every(fact => fact && Number.isInteger(fact.line) && fact.line > 0 &&
|
|
2406
|
+
Number.isInteger(fact.column) && fact.column >= 0 &&
|
|
2407
|
+
['definition', 'method-call', 'path-call', 'call', 'other'].includes(fact.shape) &&
|
|
2408
|
+
optionalString(fact.macroName) && optionalString(fact.enclosingFn) &&
|
|
2409
|
+
optionalString(fact.pathQualifier) && Array.isArray(fact.owners) &&
|
|
2410
|
+
fact.owners.every(owner => owner && ['impl', 'fn-body', 'module'].includes(owner.kind) &&
|
|
2411
|
+
optionalString(owner.trait) && ['line', 'invocationLine'].every(key =>
|
|
2412
|
+
owner[key] == null || Number.isInteger(owner[key]) && owner[key] > 0)));
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2209
2415
|
/**
|
|
2210
2416
|
* Project Go types and interfaces whose selector `T.<memberName>` resolves to
|
|
2211
2417
|
* a project declaration (fix #383): methods (own and promoted through
|