@cplieger/deadset-ts 7.2.0-dev.4 → 8.0.0-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/analysis.js +106 -10
- package/dist/src/augmentations.js +21 -8
- package/dist/src/automatic-types.js +189 -0
- package/dist/src/base-url.js +18 -0
- package/dist/src/components.js +7 -6
- package/dist/src/config.js +1 -0
- package/dist/src/configuration-files.js +50 -12
- package/dist/src/conformance-record.js +2 -2
- package/dist/src/convention-rows.js +75 -0
- package/dist/src/conventions.js +101 -5
- package/dist/src/decorator.js +21 -18
- package/dist/src/dependencies.js +183 -28
- package/dist/src/discover.js +132 -11
- package/dist/src/entry-point-gaps.js +7 -10
- package/dist/src/entry-points.js +112 -11
- package/dist/src/findings/completion.js +116 -21
- package/dist/src/findings/dependencies-and-module-machinery.js +71 -25
- package/dist/src/findings/emitters.js +1 -1
- package/dist/src/findings/intra-function.js +44 -13
- package/dist/src/findings/non-code-artifacts.js +66 -33
- package/dist/src/findings/unused-declarations.js +21 -1
- package/dist/src/findings/visibility-narrowing.js +22 -3
- package/dist/src/generated-file.js +11 -0
- package/dist/src/git-ignore.js +86 -0
- package/dist/src/graph.js +4 -1
- package/dist/src/import-bindings.js +97 -0
- package/dist/src/indexed-access.js +130 -28
- package/dist/src/interface-satisfaction.js +30 -12
- package/dist/src/intra-function-parts.js +297 -45
- package/dist/src/inventory.js +52 -8
- package/dist/src/kinds.js +13 -13
- package/dist/src/matrix.js +18 -9
- package/dist/src/mdx-entries.js +197 -0
- package/dist/src/module-declarations.js +9 -13
- package/dist/src/overrides.js +41 -10
- package/dist/src/print.js +1 -0
- package/dist/src/ref.js +10 -0
- package/dist/src/references.js +4 -1
- package/dist/src/report.js +4 -2
- package/dist/src/resolve.js +2 -0
- package/dist/src/roots.js +110 -38
- package/dist/src/schema.js +1 -0
- package/dist/src/serialization-contract.js +54 -8
- package/dist/src/session.js +7 -0
- package/dist/src/source-files.js +39 -14
- package/dist/src/suppress-file.js +10 -2
- package/dist/src/suppress-inline.js +18 -2
- package/dist/src/suppress.js +7 -5
- package/dist/src/sweep.js +27 -10
- package/dist/src/symbol-keys.js +103 -2
- package/dist/src/test-support.js +121 -17
- package/dist/src/type-errors.js +34 -7
- package/dist/src/type-parameter-reads.js +172 -0
- package/dist/src/verbs/analyze.js +16 -0
- package/dist/src/verbs/explain.js +4 -2
- package/dist/src/version.js +1 -1
- package/dist/src/workflow-steps.js +119 -22
- package/dist/src/workspace-programs.js +7 -3
- package/dist/src/workspace-resolution.js +53 -10
- package/jsr.json +1 -1
- package/package.json +2 -2
- package/src/analysis.ts +143 -10
- package/src/augmentations.ts +21 -10
- package/src/automatic-types.ts +246 -0
- package/src/base-url.ts +24 -0
- package/src/components.ts +7 -6
- package/src/config.ts +3 -0
- package/src/configuration-files.ts +66 -12
- package/src/conformance-record.ts +2 -2
- package/src/convention-rows.ts +103 -0
- package/src/conventions.ts +121 -7
- package/src/decorator.ts +28 -18
- package/src/dependencies.ts +247 -41
- package/src/discover.ts +156 -13
- package/src/entry-point-gaps.ts +8 -11
- package/src/entry-points.ts +133 -11
- package/src/finding.ts +10 -0
- package/src/findings/completion.ts +147 -24
- package/src/findings/dependencies-and-module-machinery.ts +90 -28
- package/src/findings/emitters.ts +1 -1
- package/src/findings/intra-function.ts +56 -14
- package/src/findings/non-code-artifacts.ts +81 -41
- package/src/findings/unused-declarations.ts +25 -1
- package/src/findings/visibility-narrowing.ts +32 -3
- package/src/generated-file.ts +16 -0
- package/src/git-ignore.ts +98 -0
- package/src/graph.ts +6 -0
- package/src/host.ts +5 -9
- package/src/import-bindings.ts +128 -0
- package/src/indexed-access.ts +152 -30
- package/src/interface-satisfaction.ts +33 -13
- package/src/intra-function-parts.ts +394 -59
- package/src/inventory.ts +71 -7
- package/src/kinds.ts +13 -13
- package/src/matrix.ts +32 -14
- package/src/mdx-entries.ts +241 -0
- package/src/module-declarations.ts +11 -13
- package/src/overrides.ts +51 -12
- package/src/print.ts +1 -0
- package/src/ref.ts +12 -0
- package/src/references.ts +10 -1
- package/src/report.ts +16 -3
- package/src/resolve.ts +3 -0
- package/src/roots.ts +163 -45
- package/src/schema.ts +1 -0
- package/src/serialization-contract.ts +55 -4
- package/src/session.ts +7 -0
- package/src/source-files.ts +66 -16
- package/src/suppress-file.ts +12 -1
- package/src/suppress-inline.ts +18 -1
- package/src/suppress.ts +9 -5
- package/src/sweep.ts +32 -11
- package/src/symbol-keys.ts +129 -2
- package/src/test-support.ts +149 -16
- package/src/type-errors.ts +38 -6
- package/src/type-parameter-reads.ts +227 -0
- package/src/verbs/analyze.ts +22 -0
- package/src/verbs/explain.ts +5 -2
- package/src/version.ts +1 -1
- package/src/workflow-steps.ts +127 -22
- package/src/workspace-programs.ts +7 -3
- package/src/workspace-resolution.ts +62 -10
package/src/indexed-access.ts
CHANGED
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
isCallExpression,
|
|
12
12
|
isClassDeclaration,
|
|
13
13
|
isClassExpression,
|
|
14
|
+
isConstructorDeclaration,
|
|
15
|
+
isConstructorTypeNode,
|
|
14
16
|
isFunctionDeclaration,
|
|
15
17
|
isFunctionExpression,
|
|
16
18
|
isFunctionTypeNode,
|
|
@@ -27,6 +29,7 @@ import {
|
|
|
27
29
|
isObjectBindingPattern,
|
|
28
30
|
isParenthesizedTypeNode,
|
|
29
31
|
isPropertyAccessExpression,
|
|
32
|
+
isPropertyAssignment,
|
|
30
33
|
isPropertySignatureDeclaration,
|
|
31
34
|
isStringLiteral,
|
|
32
35
|
isTypeAliasDeclaration,
|
|
@@ -71,6 +74,7 @@ function isGeneric(node: Node): node is Generic {
|
|
|
71
74
|
isArrowFunction(node) ||
|
|
72
75
|
isFunctionExpression(node) ||
|
|
73
76
|
isFunctionTypeNode(node) ||
|
|
77
|
+
isConstructorTypeNode(node) ||
|
|
74
78
|
isTypeAliasDeclaration(node) ||
|
|
75
79
|
isInterfaceDeclaration(node) ||
|
|
76
80
|
isClassDeclaration(node) ||
|
|
@@ -79,6 +83,25 @@ function isGeneric(node: Node): node is Generic {
|
|
|
79
83
|
);
|
|
80
84
|
}
|
|
81
85
|
|
|
86
|
+
/** Whether a function declares no type parameter and a parameter whose type holds `keyof`. */
|
|
87
|
+
function takesKeyof(node: Node): node is Generic {
|
|
88
|
+
if (
|
|
89
|
+
!(
|
|
90
|
+
isFunctionDeclaration(node) ||
|
|
91
|
+
isMethodDeclaration(node) ||
|
|
92
|
+
isMethodSignatureDeclaration(node) ||
|
|
93
|
+
isArrowFunction(node) ||
|
|
94
|
+
isFunctionExpression(node)
|
|
95
|
+
) ||
|
|
96
|
+
node.typeParameters !== undefined
|
|
97
|
+
) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
return node.parameters.some(
|
|
101
|
+
(parameter) => parameter.type !== undefined && keyofOperands(parameter.type).length > 0,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
82
105
|
/** Whether one generic is a receiver's: a class or an interface whose methods it parameterizes. */
|
|
83
106
|
function isReceiver(node: Node): boolean {
|
|
84
107
|
return isInterfaceDeclaration(node) || isClassDeclaration(node) || isClassExpression(node);
|
|
@@ -121,8 +144,8 @@ function unwrappedType(node: TypeNode): TypeNode {
|
|
|
121
144
|
function literalNames(index: TypeNode): readonly string[] | undefined {
|
|
122
145
|
const node = unwrappedType(index);
|
|
123
146
|
if (isUnionTypeNode(node)) {
|
|
124
|
-
const names = node.types.
|
|
125
|
-
return names.
|
|
147
|
+
const names = node.types.flatMap((one) => literalNames(one) ?? []);
|
|
148
|
+
return names.length > 0 ? names : undefined;
|
|
126
149
|
}
|
|
127
150
|
if (!isLiteralTypeNode(node)) {
|
|
128
151
|
return undefined;
|
|
@@ -134,19 +157,14 @@ function literalNames(index: TypeNode): readonly string[] | undefined {
|
|
|
134
157
|
return isNumericLiteral(literal) ? [String(Number(literal.text))] : undefined;
|
|
135
158
|
}
|
|
136
159
|
|
|
137
|
-
/** The
|
|
138
|
-
function literalTypeNames(
|
|
139
|
-
type: Type,
|
|
140
|
-
parts: (type: Type) => readonly Type[],
|
|
141
|
-
): string[] | undefined {
|
|
160
|
+
/** The names the literal constituents of a type carry: a key the type names is one the access requires. */
|
|
161
|
+
function literalTypeNames(type: Type, parts: (type: Type) => readonly Type[]): string[] {
|
|
142
162
|
const names: string[] = [];
|
|
143
163
|
for (const part of parts(type)) {
|
|
144
164
|
if (part.isStringLiteralType()) {
|
|
145
165
|
names.push(part.value);
|
|
146
166
|
} else if (part.isNumberLiteralType()) {
|
|
147
167
|
names.push(String(part.value));
|
|
148
|
-
} else {
|
|
149
|
-
return undefined;
|
|
150
168
|
}
|
|
151
169
|
}
|
|
152
170
|
return names;
|
|
@@ -266,7 +284,9 @@ function calledName(generic: Generic): string | undefined {
|
|
|
266
284
|
}
|
|
267
285
|
const parent = generic.parent as Node | undefined;
|
|
268
286
|
return parent !== undefined &&
|
|
269
|
-
(isVariableDeclaration(parent) ||
|
|
287
|
+
(isVariableDeclaration(parent) ||
|
|
288
|
+
isPropertySignatureDeclaration(parent) ||
|
|
289
|
+
isPropertyAssignment(parent)) &&
|
|
270
290
|
isIdentifier(parent.name)
|
|
271
291
|
? parent.name.text
|
|
272
292
|
: undefined;
|
|
@@ -274,9 +294,12 @@ function calledName(generic: Generic): string | undefined {
|
|
|
274
294
|
|
|
275
295
|
/** The generic a declaration a symbol names is: the function a variable is initialized with included. */
|
|
276
296
|
function genericOf(node: Node): Generic | undefined {
|
|
277
|
-
if (isGeneric(node) || isReceiverMember(node)) {
|
|
297
|
+
if (isGeneric(node) || isReceiverMember(node) || takesKeyof(node)) {
|
|
278
298
|
return node;
|
|
279
299
|
}
|
|
300
|
+
if (isConstructorDeclaration(node) && isGeneric(node.parent)) {
|
|
301
|
+
return node.parent;
|
|
302
|
+
}
|
|
280
303
|
if (isPropertySignatureDeclaration(node)) {
|
|
281
304
|
// The typings mark the annotation present; a signature written without one has none.
|
|
282
305
|
const declared = (node as { readonly type?: TypeNode }).type;
|
|
@@ -378,6 +401,8 @@ export function indexedAccessReferences<Brand>(
|
|
|
378
401
|
const carried = new Map<Generic, Carried[]>();
|
|
379
402
|
const names = new Set<string>();
|
|
380
403
|
const sites: Site[] = [];
|
|
404
|
+
/** Accesses indexed by a type parameter whose constraint is no `keyof`: it requires each key it names. */
|
|
405
|
+
const constrained: (Omit<Access, "index"> & { readonly constraint: TypeNode })[] = [];
|
|
381
406
|
/** Calls passing an argument, which a generic declared outside the own files may fix. */
|
|
382
407
|
const argumentCalls: Site[] = [];
|
|
383
408
|
|
|
@@ -407,13 +432,28 @@ export function indexedAccessReferences<Brand>(
|
|
|
407
432
|
const own = held.declarations.get(nodeKey(file, node));
|
|
408
433
|
const holder = own ?? from;
|
|
409
434
|
const inside = isGeneric(node) ? [...enclosing, node] : enclosing;
|
|
410
|
-
if (isGeneric(node)) {
|
|
435
|
+
if (isGeneric(node) || takesKeyof(node)) {
|
|
411
436
|
carriedIn(node).forEach((one) => carry(node, one));
|
|
412
437
|
}
|
|
413
438
|
if (isIndexedAccessTypeNode(node) && !aliasTypes.has(node)) {
|
|
414
439
|
const literal = literalNames(node.indexType);
|
|
440
|
+
const parameter =
|
|
441
|
+
literal === undefined ? namedParameter(node.indexType, inside) : undefined;
|
|
442
|
+
const constraint =
|
|
443
|
+
parameter === undefined
|
|
444
|
+
? undefined
|
|
445
|
+
: (
|
|
446
|
+
parameter.generic.typeParameters?.[parameter.index] as
|
|
447
|
+
{ readonly constraint?: TypeNode } | undefined
|
|
448
|
+
)?.constraint;
|
|
415
449
|
if (literal !== undefined) {
|
|
416
450
|
accesses.push({ file, from: holder, at: node, object: node.objectType, index: literal });
|
|
451
|
+
} else if (
|
|
452
|
+
constraint !== undefined &&
|
|
453
|
+
keyofOperands(constraint).length === 0 &&
|
|
454
|
+
namedParameter(node.objectType, inside) === undefined
|
|
455
|
+
) {
|
|
456
|
+
constrained.push({ file, from: holder, at: node, object: node.objectType, constraint });
|
|
417
457
|
}
|
|
418
458
|
}
|
|
419
459
|
if (
|
|
@@ -433,7 +473,7 @@ export function indexedAccessReferences<Brand>(
|
|
|
433
473
|
if (name !== undefined) {
|
|
434
474
|
const site = { file, from: holder, node, name, enclosing: inside };
|
|
435
475
|
sites.push(site);
|
|
436
|
-
if (
|
|
476
|
+
if (argumentsOf(node).length > 0) {
|
|
437
477
|
argumentCalls.push(site);
|
|
438
478
|
}
|
|
439
479
|
}
|
|
@@ -447,6 +487,36 @@ export function indexedAccessReferences<Brand>(
|
|
|
447
487
|
});
|
|
448
488
|
}
|
|
449
489
|
|
|
490
|
+
/** The literal keys one constraint names, through the type aliases it references. */
|
|
491
|
+
const requiredNames = (node: TypeNode, seen: ReadonlySet<Node>): readonly string[] => {
|
|
492
|
+
const type = unwrappedType(node);
|
|
493
|
+
if (isUnionTypeNode(type)) {
|
|
494
|
+
return type.types.flatMap((one) => requiredNames(one, seen));
|
|
495
|
+
}
|
|
496
|
+
if (!isTypeReferenceNode(type) || type.typeArguments !== undefined) {
|
|
497
|
+
return literalNames(type) ?? [];
|
|
498
|
+
}
|
|
499
|
+
const [symbol] = queries.symbolsAt([type.typeName]);
|
|
500
|
+
if (symbol === UNANSWERED || symbol === undefined) {
|
|
501
|
+
return [];
|
|
502
|
+
}
|
|
503
|
+
return symbol.declarations.flatMap((handle) => {
|
|
504
|
+
const declared = project.declarationAt(handle)?.node;
|
|
505
|
+
return declared !== undefined &&
|
|
506
|
+
isTypeAliasDeclaration(declared) &&
|
|
507
|
+
declared.typeParameters === undefined &&
|
|
508
|
+
!seen.has(declared)
|
|
509
|
+
? requiredNames(declared.type, new Set([...seen, declared]))
|
|
510
|
+
: [];
|
|
511
|
+
});
|
|
512
|
+
};
|
|
513
|
+
for (const { constraint, ...access } of constrained) {
|
|
514
|
+
const index = requiredNames(constraint, new Set());
|
|
515
|
+
if (index.length > 0) {
|
|
516
|
+
accesses.push({ ...access, index });
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
450
520
|
function partsOf(type: Type): readonly Type[] {
|
|
451
521
|
const parts = queries.constituents(type);
|
|
452
522
|
return parts === UNANSWERED ? [type] : parts;
|
|
@@ -471,11 +541,13 @@ export function indexedAccessReferences<Brand>(
|
|
|
471
541
|
* variable holding the callee is initialized with.
|
|
472
542
|
*/
|
|
473
543
|
const receiverTypeOf = (callee: Expression): Type | undefined => {
|
|
544
|
+
let operand: Type | undefined;
|
|
474
545
|
if (isPropertyAccessExpression(callee)) {
|
|
475
546
|
const type = queries.typeAt(callee.expression);
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
547
|
+
operand = type === UNANSWERED ? undefined : type;
|
|
548
|
+
const args = operand === undefined ? [] : queries.typeArguments(operand);
|
|
549
|
+
if (operand !== undefined && args !== UNANSWERED && args.length > 0) {
|
|
550
|
+
return operand;
|
|
479
551
|
}
|
|
480
552
|
}
|
|
481
553
|
const name = isPropertyAccessExpression(callee) ? callee.name : callee;
|
|
@@ -485,7 +557,7 @@ export function indexedAccessReferences<Brand>(
|
|
|
485
557
|
? named
|
|
486
558
|
: queries.aliased(named);
|
|
487
559
|
if (symbol === UNANSWERED || symbol === undefined) {
|
|
488
|
-
return
|
|
560
|
+
return operand;
|
|
489
561
|
}
|
|
490
562
|
for (const handle of symbol.declarations) {
|
|
491
563
|
const node = project.declarationAt(handle)?.node;
|
|
@@ -506,7 +578,42 @@ export function indexedAccessReferences<Brand>(
|
|
|
506
578
|
return type;
|
|
507
579
|
}
|
|
508
580
|
}
|
|
509
|
-
return
|
|
581
|
+
return operand;
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* The instantiation of `owner` a receiver's type is, or the first one among the base
|
|
586
|
+
* types it extends, breadth first: a subclass fixes its base's type parameters, and
|
|
587
|
+
* `this` stands for its class.
|
|
588
|
+
*/
|
|
589
|
+
const instantiationOf = (type: Type, owner: Node): Type | undefined => {
|
|
590
|
+
const seen = new Set<number>();
|
|
591
|
+
for (let queue = [type]; queue.length > 0;) {
|
|
592
|
+
const next: Type[] = [];
|
|
593
|
+
for (const at of queue) {
|
|
594
|
+
if (seen.has(at.id)) {
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
seen.add(at.id);
|
|
598
|
+
const target = at.isTypeReference() ? queries.targetOf(at) : at;
|
|
599
|
+
const symbol = target === UNANSWERED ? undefined : queries.symbolOfType(target);
|
|
600
|
+
const declares =
|
|
601
|
+
symbol !== undefined &&
|
|
602
|
+
symbol !== UNANSWERED &&
|
|
603
|
+
symbol.declarations.some((handle) => project.declarationAt(handle)?.node === owner);
|
|
604
|
+
if (declares) {
|
|
605
|
+
return at;
|
|
606
|
+
}
|
|
607
|
+
const constraint = at.isTypeParameter() ? queries.constraintOf(at) : undefined;
|
|
608
|
+
const bases = at.isTypeParameter() ? [] : queries.baseTypes(at);
|
|
609
|
+
next.push(...(bases === UNANSWERED ? [] : bases));
|
|
610
|
+
if (constraint !== undefined && constraint !== UNANSWERED) {
|
|
611
|
+
next.push(constraint);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
queue = next;
|
|
615
|
+
}
|
|
616
|
+
return type;
|
|
510
617
|
};
|
|
511
618
|
|
|
512
619
|
/** What one site fixes for one side of a carried access. */
|
|
@@ -517,7 +624,10 @@ export function indexedAccessReferences<Brand>(
|
|
|
517
624
|
const { node } = site;
|
|
518
625
|
if (side.kind === "receiver") {
|
|
519
626
|
const callee = isCallExpression(node) ? node.expression : undefined;
|
|
520
|
-
const
|
|
627
|
+
const owner = receiverOf(generic);
|
|
628
|
+
const typed = callee === undefined ? undefined : receiverTypeOf(callee);
|
|
629
|
+
const receiver =
|
|
630
|
+
typed === undefined || owner === undefined ? typed : instantiationOf(typed, owner);
|
|
521
631
|
if (receiver === undefined) {
|
|
522
632
|
return undefined;
|
|
523
633
|
}
|
|
@@ -541,7 +651,7 @@ export function indexedAccessReferences<Brand>(
|
|
|
541
651
|
if (isTypeReferenceNode(node)) {
|
|
542
652
|
return undefined;
|
|
543
653
|
}
|
|
544
|
-
const at = (generic
|
|
654
|
+
const at = parametersOf(generic).findIndex((parameter) => {
|
|
545
655
|
const type = parameter.type === undefined ? undefined : unwrappedType(parameter.type);
|
|
546
656
|
return (
|
|
547
657
|
type !== undefined &&
|
|
@@ -645,9 +755,7 @@ export function indexedAccessReferences<Brand>(
|
|
|
645
755
|
// by its type parameters and its receiver's. Only a call passing an argument the compiler
|
|
646
756
|
// types as a literal can fix an index, so only such a call's signature is resolved.
|
|
647
757
|
const outside = argumentCalls.filter((site) => !resolvedGenerics.has(site));
|
|
648
|
-
const argumentNodes = argumentCalls.flatMap((site) =>
|
|
649
|
-
...(site.node as CallExpression).arguments,
|
|
650
|
-
]);
|
|
758
|
+
const argumentNodes = argumentCalls.flatMap((site) => argumentsOf(site.node));
|
|
651
759
|
const argumentTypes = queries.typesAt(argumentNodes);
|
|
652
760
|
const literalArgument = new Set<Node>();
|
|
653
761
|
argumentTypes.forEach((type, at) => {
|
|
@@ -656,14 +764,14 @@ export function indexedAccessReferences<Brand>(
|
|
|
656
764
|
node !== undefined &&
|
|
657
765
|
type !== UNANSWERED &&
|
|
658
766
|
type !== undefined &&
|
|
659
|
-
|
|
767
|
+
literalTypeNames(type, partsOf).length > 0
|
|
660
768
|
) {
|
|
661
769
|
literalArgument.add(node);
|
|
662
770
|
}
|
|
663
771
|
});
|
|
664
772
|
const own = project.ownPaths();
|
|
665
773
|
for (const site of outside) {
|
|
666
|
-
if (!(site.node
|
|
774
|
+
if (!argumentsOf(site.node).some((one) => literalArgument.has(one))) {
|
|
667
775
|
continue;
|
|
668
776
|
}
|
|
669
777
|
const resolved = queries.resolvedSignature(site.node);
|
|
@@ -682,7 +790,7 @@ export function indexedAccessReferences<Brand>(
|
|
|
682
790
|
}
|
|
683
791
|
}
|
|
684
792
|
for (const site of argumentCalls) {
|
|
685
|
-
if ((site.node
|
|
793
|
+
if (argumentsOf(site.node).some((one) => literalArgument.has(one))) {
|
|
686
794
|
accesses.push(...instantiatedAccesses(site));
|
|
687
795
|
}
|
|
688
796
|
}
|
|
@@ -706,7 +814,7 @@ export function indexedAccessReferences<Brand>(
|
|
|
706
814
|
) {
|
|
707
815
|
return [];
|
|
708
816
|
}
|
|
709
|
-
const count = (site.node
|
|
817
|
+
const count = argumentsOf(site.node).length;
|
|
710
818
|
const parameters: Type[] = [];
|
|
711
819
|
for (let at = 0; at < count; at += 1) {
|
|
712
820
|
const type = queries.parameterType(generic, at);
|
|
@@ -745,13 +853,13 @@ export function indexedAccessReferences<Brand>(
|
|
|
745
853
|
}
|
|
746
854
|
const names = access.index.isTypeParameter()
|
|
747
855
|
? parameters.flatMap((parameter, at) => {
|
|
748
|
-
if (parameter.id
|
|
856
|
+
if (!partsOf(parameter).some((part) => part.id === access.index.id)) {
|
|
749
857
|
return [];
|
|
750
858
|
}
|
|
751
859
|
const passed = queries.typeAtPosition(resolved, at);
|
|
752
|
-
return passed === UNANSWERED ? [] :
|
|
860
|
+
return passed === UNANSWERED ? [] : literalTypeNames(passed, partsOf);
|
|
753
861
|
})
|
|
754
|
-
:
|
|
862
|
+
: literalTypeNames(access.index, partsOf);
|
|
755
863
|
if (names.length > 0) {
|
|
756
864
|
found.push({
|
|
757
865
|
file: site.file,
|
|
@@ -819,6 +927,20 @@ export function indexedAccessReferences<Brand>(
|
|
|
819
927
|
return found;
|
|
820
928
|
}
|
|
821
929
|
|
|
930
|
+
/** The arguments a call or a `new` expression passes, none for a type reference. */
|
|
931
|
+
function argumentsOf(node: Site["node"]): readonly Expression[] {
|
|
932
|
+
return isTypeReferenceNode(node) ? [] : (node.arguments ?? []);
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/** The parameters a site's arguments bind: a class's are its constructor's. */
|
|
936
|
+
function parametersOf(generic: Generic): NonNullable<Generic["parameters"]> {
|
|
937
|
+
if (isClassDeclaration(generic) || isClassExpression(generic)) {
|
|
938
|
+
const constructor = generic.members.find(isConstructorDeclaration);
|
|
939
|
+
return constructor?.parameters ?? [];
|
|
940
|
+
}
|
|
941
|
+
return generic.parameters ?? [];
|
|
942
|
+
}
|
|
943
|
+
|
|
822
944
|
/** What makes two carried accesses one: the kind and position of each side, or its written node. */
|
|
823
945
|
function sideKey(one: Carried): readonly unknown[] {
|
|
824
946
|
const key = (side: Side): unknown =>
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The interface-satisfaction class: a member is held back when a value of its class
|
|
3
|
-
* reaches a position
|
|
4
|
-
* `implements` entry, a typed initializer, an assignment, an
|
|
5
|
-
* element, an object property or an `as` or `satisfies`
|
|
6
|
-
*
|
|
7
|
-
* lookup has no batch form, so it is asked only of a value
|
|
3
|
+
* reaches a position typed as an interface, or an object type a type alias names, that
|
|
4
|
+
* requires the member: an `implements` entry, a typed initializer, an assignment, an
|
|
5
|
+
* argument, a return, an array element, an object property or an `as` or `satisfies`
|
|
6
|
+
* operand. `this` is a value of its class; a type parameter reaches its constraint. The
|
|
7
|
+
* contextual lookup has no batch form, so it is asked only of a value of a target class.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import {
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
isNewExpression,
|
|
21
21
|
isObjectLiteralExpression,
|
|
22
22
|
isSetAccessorDeclaration,
|
|
23
|
+
isTypeLiteralNode,
|
|
23
24
|
ModifierFlags,
|
|
24
25
|
SyntaxKind,
|
|
25
26
|
type Expression,
|
|
@@ -74,6 +75,11 @@ interface Pair {
|
|
|
74
75
|
readonly sites: Node[];
|
|
75
76
|
}
|
|
76
77
|
|
|
78
|
+
/** Whether a type alias names one type: the client's type object carries the alias it was written through. */
|
|
79
|
+
function aliased(type: Type): boolean {
|
|
80
|
+
return (type as { readonly aliasSymbol?: unknown }).aliasSymbol !== undefined;
|
|
81
|
+
}
|
|
82
|
+
|
|
77
83
|
/** Whether one node declares a static member, whose `this` is the class itself. */
|
|
78
84
|
function isStatic(node: Node): boolean {
|
|
79
85
|
const flags = (node as { readonly modifierFlags?: number }).modifierFlags ?? 0;
|
|
@@ -225,24 +231,38 @@ export function interfaceSatisfaction<Brand>(input: DetectorInput<Brand>): reado
|
|
|
225
231
|
return generic === undefined ? [] : [{ owner: generic, type: part }];
|
|
226
232
|
});
|
|
227
233
|
|
|
228
|
-
const interfaceNames = new Map<
|
|
229
|
-
/**
|
|
234
|
+
const interfaceNames = new Map<string, string | undefined>();
|
|
235
|
+
/**
|
|
236
|
+
* The interface types one position's type is or holds, each with the interface's name:
|
|
237
|
+
* an interface, or an object type literal a type alias names, directly or as a
|
|
238
|
+
* constituent of an intersection the alias names.
|
|
239
|
+
*/
|
|
230
240
|
const interfacesOf = (type: Type): readonly { type: Type; name: string }[] =>
|
|
231
241
|
partsOf(type).flatMap((part): readonly { type: Type; name: string }[] => {
|
|
232
242
|
if (part.isTypeParameter()) {
|
|
233
243
|
const constraint = must(queries.constraintOf(part));
|
|
234
244
|
return constraint === undefined ? [] : interfacesOf(constraint);
|
|
235
245
|
}
|
|
236
|
-
|
|
246
|
+
const named = aliased(part) ? part : aliased(type) ? type : undefined;
|
|
247
|
+
const key = `${String(part.id)}:${String(named?.id ?? "")}`;
|
|
248
|
+
if (!interfaceNames.has(key)) {
|
|
237
249
|
const symbol = part.isObjectType() ? must(queries.symbolOfType(part)) : undefined;
|
|
250
|
+
const flags = symbol?.flags ?? 0;
|
|
238
251
|
interfaceNames.set(
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
? symbol
|
|
242
|
-
:
|
|
252
|
+
key,
|
|
253
|
+
(flags & SymbolFlags.Interface) !== 0
|
|
254
|
+
? symbol?.name
|
|
255
|
+
: (flags & SymbolFlags.TypeLiteral) !== 0 &&
|
|
256
|
+
named !== undefined &&
|
|
257
|
+
symbol?.declarations.some((handle) => {
|
|
258
|
+
const node = project.declarationAt(handle)?.node;
|
|
259
|
+
return node !== undefined && isTypeLiteralNode(node);
|
|
260
|
+
}) === true
|
|
261
|
+
? must(queries.typeToString(named))
|
|
262
|
+
: undefined,
|
|
243
263
|
);
|
|
244
264
|
}
|
|
245
|
-
const name = interfaceNames.get(
|
|
265
|
+
const name = interfaceNames.get(key);
|
|
246
266
|
return name === undefined ? [] : [{ type: part, name }];
|
|
247
267
|
});
|
|
248
268
|
|