@cplieger/deadset-ts 7.0.0-dev.2 → 7.0.0-dev.4
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/README.md +1 -1
- package/dist/src/alias-chain.js +2 -2
- package/dist/src/analysis.js +89 -44
- package/dist/src/augmentations.js +14 -12
- package/dist/src/calls.js +2 -2
- package/dist/src/class-members.js +1 -1
- package/dist/src/components.js +62 -20
- package/dist/src/configuration-files.js +6 -6
- package/dist/src/configured-declarations.js +2 -2
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/contextual-uses.js +71 -14
- package/dist/src/convention-rows.js +6 -2
- package/dist/src/conventions.js +4 -2
- package/dist/src/dependencies.js +81 -27
- package/dist/src/deprecation.js +1 -1
- package/dist/src/destructuring.js +2 -2
- package/dist/src/discover.js +1 -1
- package/dist/src/element-access.js +341 -0
- package/dist/src/entry-points.js +2 -2
- package/dist/src/enum-group.js +2 -2
- package/dist/src/erased-stores.js +317 -0
- package/dist/src/findings/completion.js +46 -15
- package/dist/src/findings/dependencies-and-module-machinery.js +1 -1
- package/dist/src/findings/emitters.js +1 -1
- package/dist/src/findings/self-check.js +17 -4
- package/dist/src/findings-pass.js +6 -5
- package/dist/src/framework-lifecycle.js +1 -1
- package/dist/src/html-entries.js +1 -1
- package/dist/src/implementations.js +2 -2
- package/dist/src/in-operands.js +30 -0
- package/dist/src/indexed-access.js +264 -125
- package/dist/src/injection-container.js +1 -1
- package/dist/src/interface-satisfaction.js +2 -2
- package/dist/src/intra-function-parts.js +26 -12
- package/dist/src/inventory.js +26 -1
- package/dist/src/keyof-comparisons.js +170 -0
- package/dist/src/kinds.js +17 -7
- package/dist/src/manifest.js +6 -16
- package/dist/src/matrix.js +20 -2
- package/dist/src/member-names.js +1 -1
- package/dist/src/module-declarations.js +1 -1
- package/dist/src/namespace-objects.js +1 -1
- package/dist/src/outside-tests.js +1 -1
- package/dist/src/overrides.js +2 -2
- package/dist/src/query.js +4 -1
- package/dist/src/references.js +13 -2
- package/dist/src/reflective-lookup.js +2 -2
- package/dist/src/report.js +1 -1
- package/dist/src/reporters/sarif.js +4 -0
- package/dist/src/roots.js +1 -1
- package/dist/src/serialization-contract.js +45 -16
- package/dist/src/session.js +15 -8
- package/dist/src/signatures.js +1 -1
- package/dist/src/stores.js +1 -1
- package/dist/src/suppress-file.js +58 -6
- package/dist/src/suppress-inline.js +14 -5
- package/dist/src/suppress.js +54 -24
- package/dist/src/symbol-keys.js +1 -1
- package/dist/src/type-errors.js +1 -1
- package/dist/src/type-query-alias.js +1 -1
- package/dist/src/unimplemented-callees.js +312 -0
- package/dist/src/value-flow.js +1 -1
- package/dist/src/value-reach.js +8 -8
- package/dist/src/verbs/analyze.js +11 -9
- package/dist/src/workspace-programs.js +2 -2
- package/jsr.json +1 -1
- package/package.json +3 -3
- package/src/alias-chain.ts +2 -2
- package/src/analysis.ts +135 -52
- package/src/augmentations.ts +14 -11
- package/src/calls.ts +2 -2
- package/src/class-members.ts +2 -2
- package/src/component-layer.ts +1 -1
- package/src/components.ts +103 -21
- package/src/configuration-files.ts +6 -5
- package/src/configured-declarations.ts +2 -6
- package/src/conformance-record.ts +1 -1
- package/src/contextual-uses.ts +82 -18
- package/src/convention-rows.ts +6 -2
- package/src/conventions.ts +11 -2
- package/src/decorator.ts +1 -1
- package/src/dependencies.ts +108 -31
- package/src/deprecation.ts +1 -1
- package/src/destructuring.ts +2 -2
- package/src/discover.ts +2 -2
- package/src/element-access.ts +446 -0
- package/src/entry-points.ts +2 -2
- package/src/enum-group.ts +2 -2
- package/src/erased-stores.ts +413 -0
- package/src/file-facts.ts +3 -1
- package/src/findings/completion.ts +54 -20
- package/src/findings/dependencies-and-module-machinery.ts +1 -1
- package/src/findings/emitters.ts +1 -1
- package/src/findings/self-check.ts +18 -4
- package/src/findings-pass.ts +12 -5
- package/src/framework-lifecycle.ts +2 -2
- package/src/html-entries.ts +1 -1
- package/src/implementations.ts +2 -2
- package/src/in-operands.ts +45 -0
- package/src/indexed-access.ts +304 -146
- package/src/injection-container.ts +1 -1
- package/src/interface-satisfaction.ts +2 -2
- package/src/intra-function-parts.ts +42 -17
- package/src/inventory.ts +34 -2
- package/src/keyof-comparisons.ts +211 -0
- package/src/kinds.ts +20 -7
- package/src/manifest.ts +6 -21
- package/src/matrix.ts +34 -2
- package/src/member-names.ts +1 -1
- package/src/module-declarations.ts +1 -1
- package/src/namespace-objects.ts +1 -1
- package/src/outside-tests.ts +1 -1
- package/src/overrides.ts +2 -2
- package/src/position.ts +1 -1
- package/src/query.ts +12 -2
- package/src/references.ts +25 -3
- package/src/reflective-lookup.ts +2 -2
- package/src/report.ts +3 -1
- package/src/reporters/sarif.ts +4 -0
- package/src/roots.ts +2 -2
- package/src/serialization-contract.ts +55 -16
- package/src/session.ts +39 -32
- package/src/signatures.ts +1 -1
- package/src/source-files.ts +1 -1
- package/src/stores.ts +1 -1
- package/src/suppress-file.ts +68 -5
- package/src/suppress-inline.ts +15 -5
- package/src/suppress.ts +63 -28
- package/src/symbol-keys.ts +2 -2
- package/src/type-errors.ts +2 -2
- package/src/type-query-alias.ts +1 -1
- package/src/unimplemented-callees.ts +411 -0
- package/src/value-flow.ts +1 -1
- package/src/value-reach.ts +12 -8
- package/src/verbs/analyze.ts +15 -9
- package/src/workflow-steps.ts +1 -1
- package/src/workspace-programs.ts +4 -4
package/dist/src/inventory.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isClassDeclaration, isComputedPropertyName, isEnumDeclaration, isEnumMember, isExportAssignment, isExportDeclaration, isFunctionDeclaration, isGetAccessorDeclaration, isIdentifier, isInterfaceDeclaration, isIntersectionTypeNode, isMethodDeclaration, isMethodSignatureDeclaration, isModuleBlock, isModuleDeclaration, isNamespaceExport, isParenthesizedTypeNode, isPrivateIdentifier, isPropertyDeclaration, isPropertySignatureDeclaration, isSetAccessorDeclaration, isStringLiteral, isTypeAliasDeclaration, isTypeLiteralNode, isUnionTypeNode, isVariableStatement, ModifierFlags, } from "@typescript/native/
|
|
1
|
+
import { isClassDeclaration, isComputedPropertyName, isEnumDeclaration, isEnumMember, isExportAssignment, isExportDeclaration, isFunctionDeclaration, isGetAccessorDeclaration, isIdentifier, isInterfaceDeclaration, isIntersectionTypeNode, isMethodDeclaration, isMethodSignatureDeclaration, isModuleBlock, isModuleDeclaration, isNamespaceExport, isParenthesizedTypeNode, isPrivateIdentifier, isPropertyDeclaration, isPropertySignatureDeclaration, isSetAccessorDeclaration, isStringLiteral, isTypeAliasDeclaration, isTypeLiteralNode, isUnionTypeNode, isVariableStatement, ModifierFlags, } from "@typescript/native/ast";
|
|
2
2
|
import { dirnamePath, joinPath, relativePath } from "./paths.js";
|
|
3
3
|
import { byPosition, positionKey, renderPosition, writtenLength, } from "./position.js";
|
|
4
4
|
import { computedComponent, nameComponent, renderRef } from "./ref.js";
|
|
@@ -693,6 +693,30 @@ export function inventory(project, host, targetRoot) {
|
|
|
693
693
|
}
|
|
694
694
|
return undefined;
|
|
695
695
|
}
|
|
696
|
+
const dependents = new Map();
|
|
697
|
+
for (const container of containers.filter((one) => one.memberKind === "enum-member")) {
|
|
698
|
+
const ids = container.members.map((member) => {
|
|
699
|
+
const record = byNode.get(nodeKey(member.getSourceFile(), member));
|
|
700
|
+
return record === undefined ? undefined : positionKey(record.position);
|
|
701
|
+
});
|
|
702
|
+
container.members.forEach((_member, index) => {
|
|
703
|
+
const after = [];
|
|
704
|
+
for (let next = index + 1; next < container.members.length; next += 1) {
|
|
705
|
+
const member = container.members[next];
|
|
706
|
+
if (member === undefined || !isEnumMember(member) || member.initializer !== undefined) {
|
|
707
|
+
break;
|
|
708
|
+
}
|
|
709
|
+
const id = ids[next];
|
|
710
|
+
if (id !== undefined) {
|
|
711
|
+
after.push(id);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
const id = ids[index];
|
|
715
|
+
if (id !== undefined && after.length > 0) {
|
|
716
|
+
dependents.set(id, after);
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
}
|
|
696
720
|
const byId = new Map(building.map((record) => [positionKey(record.position), record]));
|
|
697
721
|
const symbols = building
|
|
698
722
|
.map(render)
|
|
@@ -706,6 +730,7 @@ export function inventory(project, host, targetRoot) {
|
|
|
706
730
|
.map((symbol) => symbol.id),
|
|
707
731
|
byName: namesOf(building),
|
|
708
732
|
modules: new Map(modules.map(([record, name]) => [positionKey(record.position), name])),
|
|
733
|
+
dependents,
|
|
709
734
|
};
|
|
710
735
|
}
|
|
711
736
|
/** Whether one declaration is, or is declared under, a scope or container left unanswered. */
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The members a comparison with a key reads: where one operand of `===`, `!==`, `==` or
|
|
3
|
+
* `!=`, or a `switch` expression a `case` label is compared with, is declared with a type
|
|
4
|
+
* that holds `keyof X`, the literal the other side has names the member of `X` under that
|
|
5
|
+
* key, because deleting the member makes the comparison a type error.
|
|
6
|
+
*/
|
|
7
|
+
import { isBinaryExpression, isCaseClause, isElementAccessExpression, isIdentifier, isParenthesizedExpression, isPropertyAccessExpression, isSwitchStatement, isTypeOperatorNode, SyntaxKind, } from "@typescript/native/ast";
|
|
8
|
+
import { SymbolFlags } from "@typescript/native/sync";
|
|
9
|
+
import { nodeKey } from "./inventory.js";
|
|
10
|
+
import { renderPosition } from "./position.js";
|
|
11
|
+
import { UNANSWERED } from "./query.js";
|
|
12
|
+
/** The operators that compare their operands for equality. */
|
|
13
|
+
const EQUALITIES = new Set([
|
|
14
|
+
SyntaxKind.EqualsEqualsEqualsToken,
|
|
15
|
+
SyntaxKind.ExclamationEqualsEqualsToken,
|
|
16
|
+
SyntaxKind.EqualsEqualsToken,
|
|
17
|
+
SyntaxKind.ExclamationEqualsToken,
|
|
18
|
+
]);
|
|
19
|
+
function unwrapped(node) {
|
|
20
|
+
return isParenthesizedExpression(node) ? unwrapped(node.expression) : node;
|
|
21
|
+
}
|
|
22
|
+
/** The name node an operand's symbol is read at: an identifier, or a property access's name. */
|
|
23
|
+
function nameOf(node) {
|
|
24
|
+
const at = unwrapped(node);
|
|
25
|
+
if (isIdentifier(at)) {
|
|
26
|
+
return at;
|
|
27
|
+
}
|
|
28
|
+
return isPropertyAccessExpression(at) ? at.name : undefined;
|
|
29
|
+
}
|
|
30
|
+
/** The operand of each `keyof` one type node holds, through every type node it is built of. */
|
|
31
|
+
function keyofOperands(node) {
|
|
32
|
+
const found = [];
|
|
33
|
+
const visit = (at) => {
|
|
34
|
+
if (isTypeOperatorNode(at) && at.operator === SyntaxKind.KeyOfKeyword) {
|
|
35
|
+
found.push(at.type);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
at.forEachChild(visit);
|
|
39
|
+
};
|
|
40
|
+
visit(node);
|
|
41
|
+
return found;
|
|
42
|
+
}
|
|
43
|
+
/** The names a literal type or a union of them spells, or none. */
|
|
44
|
+
function literalNames(parts) {
|
|
45
|
+
const names = [];
|
|
46
|
+
for (const part of parts) {
|
|
47
|
+
if (part.isStringLiteralType()) {
|
|
48
|
+
names.push(part.value);
|
|
49
|
+
}
|
|
50
|
+
else if (part.isNumberLiteralType()) {
|
|
51
|
+
names.push(String(part.value));
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return names;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The references the key comparisons of one project's own files make to the members their
|
|
61
|
+
* literal side names, each made by the declaration holding the comparison.
|
|
62
|
+
*/
|
|
63
|
+
export function keyofComparisonReferences(project, held, chains, root, testFiles) {
|
|
64
|
+
const queries = project.queries;
|
|
65
|
+
const comparisons = [];
|
|
66
|
+
for (const file of project.ownSourceFiles()) {
|
|
67
|
+
const fileId = held.declarations.get(nodeKey(file, file));
|
|
68
|
+
if (fileId === undefined) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const visit = (node, from) => {
|
|
72
|
+
const holder = held.declarations.get(nodeKey(file, node)) ?? from;
|
|
73
|
+
if (isBinaryExpression(node) && EQUALITIES.has(node.operatorToken.kind)) {
|
|
74
|
+
comparisons.push({ file, from: holder, keyed: node.left, other: node.right });
|
|
75
|
+
comparisons.push({ file, from: holder, keyed: node.right, other: node.left });
|
|
76
|
+
}
|
|
77
|
+
if (isSwitchStatement(node)) {
|
|
78
|
+
for (const clause of node.caseBlock.clauses) {
|
|
79
|
+
if (isCaseClause(clause)) {
|
|
80
|
+
comparisons.push({
|
|
81
|
+
file,
|
|
82
|
+
from: holder,
|
|
83
|
+
keyed: node.expression,
|
|
84
|
+
other: clause.expression,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
node.forEachChild((child) => {
|
|
90
|
+
visit(child, holder);
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
file.forEachChild((child) => {
|
|
94
|
+
visit(child, fileId);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
const named = comparisons.flatMap((one) => {
|
|
98
|
+
const name = nameOf(one.keyed);
|
|
99
|
+
return name === undefined || isElementAccessExpression(unwrapped(one.other))
|
|
100
|
+
? []
|
|
101
|
+
: [{ ...one, name }];
|
|
102
|
+
});
|
|
103
|
+
if (named.length === 0) {
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
// The operand's own declaration states the key type, which the checker resolves to
|
|
107
|
+
// the literals it admits and so no longer names the type the key indexes.
|
|
108
|
+
const symbols = queries.symbolsAt(named.map((one) => one.name));
|
|
109
|
+
const keyed = named.flatMap((one, at) => {
|
|
110
|
+
const asked = symbols[at];
|
|
111
|
+
const symbol = asked === UNANSWERED || asked === undefined || (asked.flags & SymbolFlags.Alias) === 0
|
|
112
|
+
? asked
|
|
113
|
+
: queries.aliased(asked);
|
|
114
|
+
if (symbol === UNANSWERED || symbol === undefined) {
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
const operands = symbol.declarations.flatMap((handle) => {
|
|
118
|
+
const declared = project.declarationAt(handle)?.node?.type;
|
|
119
|
+
return declared === undefined ? [] : keyofOperands(declared);
|
|
120
|
+
});
|
|
121
|
+
return operands.length === 0 ? [] : [{ ...one, operands }];
|
|
122
|
+
});
|
|
123
|
+
if (keyed.length === 0) {
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
126
|
+
const nodes = keyed.flatMap((one) => [one.other, ...one.operands]);
|
|
127
|
+
const types = queries.typesAt(nodes);
|
|
128
|
+
const typeOf = new Map();
|
|
129
|
+
nodes.forEach((node, at) => {
|
|
130
|
+
const type = types[at];
|
|
131
|
+
if (type !== UNANSWERED && type !== undefined) {
|
|
132
|
+
typeOf.set(node, type);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
const partsOf = (type) => {
|
|
136
|
+
const parts = queries.constituents(type);
|
|
137
|
+
return parts === UNANSWERED ? [type] : parts;
|
|
138
|
+
};
|
|
139
|
+
const found = [];
|
|
140
|
+
for (const one of keyed) {
|
|
141
|
+
const literal = typeOf.get(one.other);
|
|
142
|
+
const names = literal === undefined ? [] : literalNames(partsOf(literal));
|
|
143
|
+
if (names.length === 0) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const position = renderPosition(one.file, root, one.other.getStart());
|
|
147
|
+
for (const operand of one.operands) {
|
|
148
|
+
const object = typeOf.get(operand);
|
|
149
|
+
for (const part of object === undefined ? [] : partsOf(object)) {
|
|
150
|
+
const properties = queries.propertiesOf(part);
|
|
151
|
+
for (const property of properties === UNANSWERED ? [] : properties) {
|
|
152
|
+
if (!names.includes(property.name)) {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
for (const to of chains.declarationsOf(property)) {
|
|
156
|
+
found.push({
|
|
157
|
+
from: one.from,
|
|
158
|
+
to,
|
|
159
|
+
position,
|
|
160
|
+
use: "read",
|
|
161
|
+
resolution: "keyed",
|
|
162
|
+
test: testFiles.has(position.path),
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return found;
|
|
170
|
+
}
|
package/dist/src/kinds.js
CHANGED
|
@@ -13,6 +13,7 @@ const ROWS = [
|
|
|
13
13
|
defaultSeverity: "deny",
|
|
14
14
|
maxClass: "certain",
|
|
15
15
|
fixability: "deletable",
|
|
16
|
+
findingFixabilities: ["manual"],
|
|
16
17
|
fixed: false,
|
|
17
18
|
},
|
|
18
19
|
{
|
|
@@ -23,6 +24,7 @@ const ROWS = [
|
|
|
23
24
|
defaultSeverity: "deny",
|
|
24
25
|
maxClass: "certain",
|
|
25
26
|
fixability: "deletable",
|
|
27
|
+
findingFixabilities: ["manual"],
|
|
26
28
|
fixed: false,
|
|
27
29
|
},
|
|
28
30
|
{
|
|
@@ -34,6 +36,7 @@ const ROWS = [
|
|
|
34
36
|
defaultSeverity: "deny",
|
|
35
37
|
maxClass: "certain",
|
|
36
38
|
fixability: "deletable",
|
|
39
|
+
findingFixabilities: ["manual"],
|
|
37
40
|
fixed: false,
|
|
38
41
|
},
|
|
39
42
|
{
|
|
@@ -44,6 +47,7 @@ const ROWS = [
|
|
|
44
47
|
defaultSeverity: "deny",
|
|
45
48
|
maxClass: "certain",
|
|
46
49
|
fixability: "deletable",
|
|
50
|
+
findingFixabilities: ["manual"],
|
|
47
51
|
fixed: false,
|
|
48
52
|
},
|
|
49
53
|
{
|
|
@@ -54,6 +58,7 @@ const ROWS = [
|
|
|
54
58
|
defaultSeverity: "deny",
|
|
55
59
|
maxClass: "certain",
|
|
56
60
|
fixability: "deletable",
|
|
61
|
+
findingFixabilities: ["manual"],
|
|
57
62
|
fixed: false,
|
|
58
63
|
},
|
|
59
64
|
{
|
|
@@ -64,6 +69,7 @@ const ROWS = [
|
|
|
64
69
|
defaultSeverity: "deny",
|
|
65
70
|
maxClass: "certain",
|
|
66
71
|
fixability: "deletable",
|
|
72
|
+
findingFixabilities: ["manual"],
|
|
67
73
|
fixed: false,
|
|
68
74
|
},
|
|
69
75
|
{
|
|
@@ -71,7 +77,7 @@ const ROWS = [
|
|
|
71
77
|
name: "unnecessary-export",
|
|
72
78
|
languages: ["go", "ts"],
|
|
73
79
|
rule: "An exported symbol whose every reference is inside the symbol's own package or module, reported as a candidate for unexporting. The subject is a package-level declaration or a method. Three subjects are excluded. An interface method is excluded because its exportedness is the contract of the interface that declares it. A struct field is excluded because an encoder reads it by name. A method that satisfies an interface some symbol uses as a type is excluded because it cannot be unexported without its type ceasing to satisfy that interface, and so is a method whose name and signature are those of a method of an exported interface type declared by a package outside the target that the import closure holds, because that interface fixes them whether or not the program converts the method's type to it. The finding names the narrower visibility the references support. A declared cross-language edge counts as an out-of-package reference. Where the edge's other side is unknown to the analyzer, the finding is emitted pending. A reference from a Go external test package, a test file whose package clause names its directory's package with the suffix _test, is a reference from outside the package, because that test builds as another package and needs the export.",
|
|
74
|
-
precondition: "Closed world only. A Go main package, and an internal/ directory tree for importers outside its parent, are closed worlds whatever the configuration declares, because no other program can import them, so this finding is reported there always. A published package is a closed world only when the configuration declares the consumer set complete and every declared consumer loads. A library with no consumer loaded and no complete consumer set declared gets this finding on its internal/ tree and its main packages and never on its published API. A symbol the rule of DS1301 holds for is not reported under this code, whatever severity the configuration gives DS1301, because the write-only finding names the defect to fix first. A type that a parameter or a result of an exported function or method of its own package names is not reported when a reference from outside the package references that function or method, because the caller holds values of the type, and unexporting it would leave an exported signature naming a type its callers cannot name.",
|
|
80
|
+
precondition: "Closed world only. A Go main package, and an internal/ directory tree for importers outside its parent, are closed worlds whatever the configuration declares, because no other program can import them, so this finding is reported there always. A published package is a closed world only when the configuration declares the consumer set complete and every declared consumer loads. A library with no consumer loaded and no complete consumer set declared gets this finding on its internal/ tree and its main packages and never on its published API. A symbol the rule of DS1301 holds for is not reported under this code, whatever severity the configuration gives DS1301, because the write-only finding names the defect to fix first. A type that a parameter or a result of an exported function or method of its own package names is not reported when a reference from outside the package references that function or method, because the caller holds values of the type, and unexporting it would leave an exported signature naming a type its callers cannot name. A package-level declaration whose name a string literal spells carries the class grammar/analysis.md gives it.",
|
|
75
81
|
defaultSeverity: "warn",
|
|
76
82
|
maxClass: "certain",
|
|
77
83
|
fixability: "narrowable",
|
|
@@ -92,10 +98,11 @@ const ROWS = [
|
|
|
92
98
|
code: "DS1103",
|
|
93
99
|
name: "unreachable-export",
|
|
94
100
|
languages: ["go", "ts"],
|
|
95
|
-
rule: "An unused exported symbol in a package or file no external code can import: an unused-exported candidate whose enclosing package or file is unimportable from outside, reported once under this code at the certain class, because unimportability is a property of the package graph rather than of the consumer set.",
|
|
101
|
+
rule: "An unused exported symbol in a package or file no external code can import: an unused-exported candidate whose enclosing package or file is unimportable from outside, reported once under this code at the certain class, because unimportability is a property of the package graph rather than of the consumer set, except where the confidence rule of grammar/analysis.md gives a declaration a string literal spells the class possible.",
|
|
96
102
|
defaultSeverity: "deny",
|
|
97
103
|
maxClass: "certain",
|
|
98
104
|
fixability: "deletable",
|
|
105
|
+
findingFixabilities: ["manual"],
|
|
99
106
|
fixed: false,
|
|
100
107
|
},
|
|
101
108
|
{
|
|
@@ -117,6 +124,7 @@ const ROWS = [
|
|
|
117
124
|
defaultSeverity: "deny",
|
|
118
125
|
maxClass: "certain",
|
|
119
126
|
fixability: "deletable",
|
|
127
|
+
findingFixabilities: ["manual"],
|
|
120
128
|
fixed: false,
|
|
121
129
|
},
|
|
122
130
|
{
|
|
@@ -135,6 +143,7 @@ const ROWS = [
|
|
|
135
143
|
name: "unused-satisfaction-assertion",
|
|
136
144
|
languages: ["go"],
|
|
137
145
|
rule: "A compile-time satisfaction assertion whose interface no symbol uses as a type.",
|
|
146
|
+
precondition: "In a library, an assertion whose interface is published API, an exported interface of a package outside programs can import, has the reachability class possible, whatever consumers the run declares and loads. The assertion checks a promise the published API makes, that the type satisfies the published interface, and that promise is made to every program that imports the package, not only to the consumers the run declares. A loaded consumer that names the interface as a type uses it, and the assertion is not reported.",
|
|
138
147
|
defaultSeverity: "deny",
|
|
139
148
|
maxClass: "certain",
|
|
140
149
|
fixability: "deletable",
|
|
@@ -144,7 +153,7 @@ const ROWS = [
|
|
|
144
153
|
code: "DS1301",
|
|
145
154
|
name: "write-only-symbol",
|
|
146
155
|
languages: ["go", "ts"],
|
|
147
|
-
rule: "A package-level or module-level variable, struct field, class member, type member or collection that production code writes and never reads. In production mode a read from a test file counts as no read. A write from a loaded consumer's code counts as a read, because the deletion the finding names would break a consumer the target does not own. A write in a consumer's test file counts as a read in the run that counts test references. It counts in a production sweep only where the configuration counts consumer tests as production. A comparison of struct values reads every field of the type: in Go, a value of a struct type used as an operand of `==` or `!=`, as a map key, or as a `switch` tag or `case` expression is a read of every field of that type, transitively through the fields of every struct type it holds, recorded at the comparison, because equality reads every field and a field that decides equality carries information. In Go a type argument the program instantiates a type parameter with, where the parameter's constraint is `comparable` or embeds it, is compared as an operand of `==` of that type is, recorded at the instantiation, so the struct values `slices.Equal`, `slices.Index` or `maps.Equal` compares have every field read. `reflect.DeepEqual` is not a comparison under this rule. In TypeScript a comparison of two object references reads no member, because `===` compares identity and never a member. In TypeScript a property of an object literal writes the member of the same name that the literal's contextual type declares, whether or not the compiler checks the literal for excess properties. Where that contextual type is a union or an intersection, the property writes the member of that name on each constituent that declares one. So a literal that a declaration annotates, a function returns, a call passes or a `satisfies` expression checks writes the members of the type it is given. A store through the value a field or a member holds reads that field or member rather than writing it, because the store lands in storage every other holder of the value reads. In Go that store is an assignment, an increment, a compound assignment or a `delete` whose target is reached through a struct field holding a map, a slice or a pointer. Examples are `s.f[k] = v`, `s.f.x = v` and `delete(s.f, k)`. The same store through a field holding an array or a struct is a store into the field itself. TypeScript holds every object by reference, so an element or property store through a member's value reads the member. The finding names each write position, so the deletion set is visible. Deleting the subject and its writes also deletes every declaration whose only uses they hold. Such a declaration is a type parameter of the subject's declaring type that the subject's type alone names, an import binding the writes alone use, or a declaration whose every use is in the type annotation of the subject or of another subject this rule reports, or in the writes those findings name. The finding's message names each such declaration, and no finding is reported about the declaration itself.",
|
|
156
|
+
rule: "A package-level or module-level variable, struct field, class member, type member or collection that production code writes and never reads. In production mode a read from a test file counts as no read. A write from a loaded consumer's code counts as a read, because the deletion the finding names would break a consumer the target does not own. A write in a consumer's test file counts as a read in the run that counts test references. It counts in a production sweep only where the configuration counts consumer tests as production. A comparison of struct values reads every field of the type: in Go, a value of a struct type used as an operand of `==` or `!=`, as a map key, or as a `switch` tag or `case` expression is a read of every field of that type, transitively through the fields of every struct type it holds, recorded at the comparison, because equality reads every field and a field that decides equality carries information. In Go a type argument the program instantiates a type parameter with, where the parameter's constraint is `comparable` or embeds it, is compared as an operand of `==` of that type is, recorded at the instantiation, so the struct values `slices.Equal`, `slices.Index` or `maps.Equal` compares have every field read. `reflect.DeepEqual` is not a comparison under this rule. In Go, a conversion the compiler accepts between two struct types, or between two pointer types that are not named types and whose base types are struct types, reads every field of the two struct types where their struct type literals are distinct, recorded at the conversion, because the conversion is legal only while their fields are identical, ignoring tags, and so no field of either can be deleted alone. It reads in the same way every field of each pair of distinct struct type literals the identity of the two types compares in turn: through each field's type, and from there through the element, key, base, parameter and result types of the type literals it reaches, through the method signatures of an interface type literal, and through the type arguments of an instantiated generic type. A field reached there has no reference of its own, as grammar/symbol-ref.md states, so it is never a subject and those reads hold no finding back. It stops at a defined type, whose fields the conversion does not read, because a defined type is identical to itself whatever fields it declares. A pair whose two sides are one struct type literal, the two struct types themselves included, as where one type is declared from the other or the conversion is to the operand's own type, reads nothing, because a field deleted from that literal leaves both sides identical. Where the operand's type or the conversion's type is a type parameter, the struct type its constraint's core type is takes that side's place, and a type parameter operand reads in the same way the fields of each struct type argument an instantiation the program holds gives it, an instantiation made through another generic declaration's own type parameters included. A conversion to any other type, an interface type among them, reads no field under this rule. In TypeScript a comparison of two object references reads no member, because `===` compares identity and never a member. In TypeScript a property of an object literal writes the member of the same name that the literal's contextual type declares, whether or not the compiler checks the literal for excess properties. Where that contextual type is a union or an intersection, the property writes the member of that name on each constituent that declares one. So a literal that a declaration annotates, a function returns, a call passes or a `satisfies` expression checks writes the members of the type it is given. An object literal handed to a callee the target does not implement has its members read, as grammar/analysis.md states. A store through the value a field or a member holds reads that field or member rather than writing it, because the store lands in storage every other holder of the value reads. In Go that store is an assignment, an increment, a compound assignment or a `delete` whose target is reached through a struct field holding a map, a slice or a pointer. Examples are `s.f[k] = v`, `s.f.x = v` and `delete(s.f, k)`. The same store through a field holding an array or a struct is a store into the field itself. TypeScript holds every object by reference, so an element or property store through a member's value reads the member. The finding names each write position, so the deletion set is visible. Deleting the subject and its writes also deletes every declaration whose only uses they hold. Such a declaration is a type parameter of the subject's declaring type that the subject's type alone names, an import binding the writes alone use, or a declaration whose every use is in the type annotation of the subject or of another subject this rule reports, or in the writes those findings name. The finding's message names each such declaration, and no finding is reported about the declaration itself.",
|
|
148
157
|
defaultSeverity: "deny",
|
|
149
158
|
maxClass: "certain",
|
|
150
159
|
fixability: "deletable",
|
|
@@ -158,6 +167,7 @@ const ROWS = [
|
|
|
158
167
|
defaultSeverity: "deny",
|
|
159
168
|
maxClass: "certain",
|
|
160
169
|
fixability: "deletable",
|
|
170
|
+
findingFixabilities: ["manual"],
|
|
161
171
|
fixed: false,
|
|
162
172
|
},
|
|
163
173
|
{
|
|
@@ -175,7 +185,7 @@ const ROWS = [
|
|
|
175
185
|
code: "DS1501",
|
|
176
186
|
name: "file-never-built",
|
|
177
187
|
languages: ["go", "ts"],
|
|
178
|
-
rule: "A source file no configuration in the build matrix builds. On the Go side the finding names the build constraint that excluded the file. A file whose build constraint
|
|
188
|
+
rule: "A source file no configuration in the build matrix builds. On the Go side the finding names the build constraint that excluded the file. A file whose build constraint holds only when the ignore tag is set is never reported, because grammar/analysis.md keeps such a file outside the analysis wherever no configuration builds it: the tag is the toolchain's convention for a file kept out of ordinary package builds unless a configuration explicitly satisfies the tag, and built by hand, the toolchain applying no build constraint to a file named on its own command line. A file any other custom tag excludes is reported under a matrix the configuration declares complete, because completeness is the maintainer's assertion that the listed configurations are every one the target builds, and a configuration the maintainer builds by hand belongs in that list.",
|
|
179
189
|
precondition: 'Reported only when the configuration declares the build matrix complete, and never for a file the toolchain ignored solely because it imports "C" under a build with cgo disabled. Such a file is recorded as excluded by cgo rather than as never built.',
|
|
180
190
|
defaultSeverity: "deny",
|
|
181
191
|
maxClass: "certain",
|
|
@@ -197,7 +207,7 @@ const ROWS = [
|
|
|
197
207
|
name: "unused-dependency",
|
|
198
208
|
languages: ["go", "ts"],
|
|
199
209
|
rule: "A directly declared dependency that no import in the target needs. On the Go side, a direct require whose module provides no package any target package or test variant imports. On the TypeScript side, a manifest dependency, development dependency or peer dependency the project's import closure does not need. A deletion finding whose fix would remove the last use of a dependency names that dependency.",
|
|
200
|
-
precondition: "On the Go side the rule is the semantics of `go mod tidy -diff` exactly: a requirement marked indirect is never reported, because it exists to pin a transitive version and removing it changes the build list. On the TypeScript side the import closure is the files of the run's projects, and a call of `import.meta.resolve` or of `require.resolve` in them, whatever `require` names, whose first argument is a string literal or a template literal with no substitution needs each dependency an import of that specifier needs.
|
|
210
|
+
precondition: "On the Go side the rule is the semantics of `go mod tidy -diff` exactly: a requirement marked indirect is never reported, because it exists to pin a transitive version and removing it changes the build list. On the TypeScript side the import closure is the files of the run's projects, and a call of `import.meta.resolve` or of `require.resolve` in them, whatever `require` names, whose first argument is a string literal or a template literal with no substitution needs each dependency an import of that specifier needs. Four more dependencies are needed: one whose command a script entry or a workflow step runs, one that a string of a configuration file names, and one that a string names under a manifest key or a package key of an applied convention row, each as grammar/analysis.md states, and one that a needed dependency names as a peer, as stated below. A command no script entry and no workflow step runs needs nothing. A declared dependency that the installed manifest of a needed declared dependency names in its peerDependencies is needed, and so on until no further dependency joins. A peer dependency is a package the needed dependency expects beside it at a version its manifest states, whether or not it imports the package, and the project's declaration is what keeps that package present at a version the project chooses, so removing the declaration can break the needed dependency where nothing the analysis reads imports the peer. A peer that peerDependenciesMeta marks optional is needed in the same way, because the needed dependency's manifest still names it as a package it works with, and a package manager may leave an optional peer out where the project does not declare it, so the project's declaration is what keeps it present for the needed dependency. A declared dependency that only dependencies nothing needs name as a peer is needed by nothing. A dependency of a manifest whose directory holds source only in configurations the run lists in configurations_not_built is reported at the reachability class possible, because the source that may import it was not analyzed. An installed manifest is the package's manifest in the nearest node_modules directory at or above the directory of the manifest that declares the dependency, and a dependency with none is decided by the import closure alone. On the TypeScript side, the dependencies judged are those of every manifest grammar/analysis.md says the analysis reads, a nested package's among them, each read against its own directory: a dependency is needed by a use written in a file at or below the directory of the manifest that declares it, by a use no one file holds, and as a peer of a needed dependency of the same manifest or of a manifest below it. A dependency a nested package's manifest declares whose installed manifest the analysis cannot find is not judged, because no run requires a nested package to be installed and its commands and peers cannot be read, while a workspace member's and the target's own are judged as above.",
|
|
201
211
|
defaultSeverity: "deny",
|
|
202
212
|
maxClass: "certain",
|
|
203
213
|
fixability: "manual",
|
|
@@ -238,7 +248,7 @@ const ROWS = [
|
|
|
238
248
|
code: "DS1703",
|
|
239
249
|
name: "stale-suppression",
|
|
240
250
|
languages: ["go", "ts"],
|
|
241
|
-
rule: "A suppression that
|
|
251
|
+
rule: "A suppression record that is stale, as grammar/suppression.md defines it, at either mechanism and in the baseline alike. The run exits with the findings code when at least one is reported. The kind is fixed on at deny: no flag, no severity setting and no per-mechanism exception reduces it below a finding, and a configuration naming this code under a severity key is an unimplemented key.",
|
|
242
252
|
defaultSeverity: "deny",
|
|
243
253
|
maxClass: "certain",
|
|
244
254
|
fixability: "none",
|
|
@@ -303,7 +313,7 @@ const ROWS = [
|
|
|
303
313
|
name: "unused-result",
|
|
304
314
|
languages: ["go", "ts"],
|
|
305
315
|
rule: "A result value that no call site of the function uses, on a function whose signature is free to change.",
|
|
306
|
-
precondition: "The same free-signature rule as unused-parameter, plus a closed-world condition: every call site of the function must be in the loaded graph, because an unknown caller may consume the result, so the kind reports only for a function whose callers are all visible.",
|
|
316
|
+
precondition: "The same free-signature rule as unused-parameter, plus a closed-world condition: every call site of the function must be in the loaded graph, because an unknown caller may consume the result, so the kind reports only for a function whose callers are all visible. A call that is the operand of an await expression written as a statement of its own uses a result whose type has a member named then, a promise or another thenable, because the caller sequences on it, and discards any other result. A call written as a statement of its own, or as the operand of void, discards its result whatever its type.",
|
|
307
317
|
defaultSeverity: "warn",
|
|
308
318
|
maxClass: "certain",
|
|
309
319
|
fixability: "manual",
|
package/dist/src/manifest.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* shell running its command or one of its scripts. {@link readManifest} reads one
|
|
5
5
|
* directory's manifest, {@link packageEntries} those of the packages below the target root.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import { isAbsolutePath, joinPath, relativePath, resolvePath } from "./paths.js";
|
|
8
8
|
/** The manifest's own name. */
|
|
9
9
|
const MANIFEST = "package.json";
|
|
10
10
|
/** The three members that name an entry point beside `exports` and `bin`. */
|
|
@@ -148,22 +148,12 @@ export function readManifest(host, targetRoot) {
|
|
|
148
148
|
};
|
|
149
149
|
}
|
|
150
150
|
/**
|
|
151
|
-
* The entry points
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
* otherwise. Each entry is spelled under its manifest's path below the target root, and
|
|
155
|
-
* is published by its own manifest's rule, as the target's own entries are.
|
|
151
|
+
* The entry points the manifests of the nested packages in `dirs` name. Each entry is
|
|
152
|
+
* spelled under its manifest's path below the target root, and is published by its own
|
|
153
|
+
* manifest's rule, as the target's own entries are.
|
|
156
154
|
*/
|
|
157
|
-
export function packageEntries(host, targetRoot,
|
|
158
|
-
|
|
159
|
-
for (const configFile of configFiles) {
|
|
160
|
-
for (let dir = dirnamePath(resolvePath(targetRoot, configFile)); dir !== targetRoot && relativePath(targetRoot, dir) !== undefined; dir = dirnamePath(dir)) {
|
|
161
|
-
if (!known.has(dir) && host.kindOf(joinPath(dir, MANIFEST)) === "file") {
|
|
162
|
-
dirs.add(dir);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
return [...dirs].sort().flatMap((dir) => {
|
|
155
|
+
export function packageEntries(host, targetRoot, dirs) {
|
|
156
|
+
return dirs.flatMap((dir) => {
|
|
167
157
|
const manifest = readManifest(host, dir);
|
|
168
158
|
const where = `${relativePath(targetRoot, dir) ?? dir}/${MANIFEST}`;
|
|
169
159
|
return manifest.entries.map((entry) => ({
|
package/dist/src/matrix.js
CHANGED
|
@@ -43,7 +43,13 @@ export function matrixOf(per) {
|
|
|
43
43
|
const references = [];
|
|
44
44
|
const firstSeen = new Map();
|
|
45
45
|
const judged = new Set();
|
|
46
|
+
const dependents = new Map();
|
|
46
47
|
per.forEach((one, index) => {
|
|
48
|
+
for (const [id, after] of one.dependents ?? []) {
|
|
49
|
+
if (!dependents.has(id)) {
|
|
50
|
+
dependents.set(id, after);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
47
53
|
const referenceOnly = new Set(one.referenceOnly);
|
|
48
54
|
for (const symbol of one.symbols) {
|
|
49
55
|
const held = holders.get(symbol.id);
|
|
@@ -76,6 +82,7 @@ export function matrixOf(per) {
|
|
|
76
82
|
graphs: per.map((one) => graphOf(one.symbols, one.references, one.roots, one.testFiles, one.supportFiles)),
|
|
77
83
|
union: graphOf(symbols, references, per.flatMap((one) => one.roots), [...new Set(per.flatMap((one) => one.testFiles))], [...new Set(per.flatMap((one) => one.supportFiles ?? []))]),
|
|
78
84
|
heldIn: symbols.map((symbol) => holders.get(symbol.id) ?? []),
|
|
85
|
+
dependents,
|
|
79
86
|
};
|
|
80
87
|
}
|
|
81
88
|
/**
|
|
@@ -155,10 +162,11 @@ export function sweepMatrix(matrix, input) {
|
|
|
155
162
|
unreferencedTest: unreferenced,
|
|
156
163
|
});
|
|
157
164
|
});
|
|
165
|
+
const liveUnder = liveAnywhere(union.symbols, per);
|
|
158
166
|
return {
|
|
159
|
-
liveUnder
|
|
167
|
+
liveUnder,
|
|
160
168
|
candidates,
|
|
161
|
-
components: componentsOf(union, dead.map((one, at) => one && heldBy[at] === undefined), testOfDeadCode, unreferencedTest),
|
|
169
|
+
components: componentsOf(union, dead.map((one, at) => one && heldBy[at] === undefined), testOfDeadCode, unreferencedTest, positionsOf(union, matrix.dependents, liveUnder)),
|
|
162
170
|
};
|
|
163
171
|
}
|
|
164
172
|
/**
|
|
@@ -170,6 +178,16 @@ export function liveFilesAt(matrix, result, paths) {
|
|
|
170
178
|
paths.has(symbol.position.path) &&
|
|
171
179
|
(result.liveUnder.get(symbol.id)?.length ?? 0) === RELATIONS.length);
|
|
172
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* Each declaration's dependents by its place in the union, and per place whether the
|
|
183
|
+
* declaration is live under reachability in some configuration.
|
|
184
|
+
*/
|
|
185
|
+
function positionsOf(union, dependents, liveUnder) {
|
|
186
|
+
return {
|
|
187
|
+
dependents: union.symbols.map((symbol) => (dependents.get(symbol.id) ?? []).map((id) => union.at(id)).filter((at) => at !== OUTSIDE)),
|
|
188
|
+
live: union.symbols.map((symbol) => liveUnder.get(symbol.id)?.includes("reachability") === true),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
173
191
|
/** The relations in the order a declaration's entry lists them. */
|
|
174
192
|
const RELATIONS = ["reference-counting", "reachability"];
|
|
175
193
|
/**
|
package/dist/src/member-names.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* string key both hold a name and nothing else, so each holds back every member of
|
|
5
5
|
* that name its own mechanism can reach.
|
|
6
6
|
*/
|
|
7
|
-
import { isIdentifier, isNumericLiteral, isPrivateIdentifier, isStringLiteral, } from "@typescript/native/
|
|
7
|
+
import { isIdentifier, isNumericLiteral, isPrivateIdentifier, isStringLiteral, } from "@typescript/native/ast";
|
|
8
8
|
import { nodeKey } from "./inventory.js";
|
|
9
9
|
/** The kinds of declaration a member name can name. */
|
|
10
10
|
const MEMBER_KINDS = new Set([
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* to the same module. One that names it by name or pattern also reads the members it
|
|
6
6
|
* imports, whatever file the specifier resolves to, as a compiler reading no such file does.
|
|
7
7
|
*/
|
|
8
|
-
import { isCallExpression, isExportDeclaration, isExternalModuleReference, isImportDeclaration, isImportEqualsDeclaration, isImportTypeNode, isLiteralTypeNode, isModuleDeclaration, isNamespaceExport, isNamespaceImport, isNoSubstitutionTemplateLiteral, isStringLiteral, SyntaxKind, } from "@typescript/native/
|
|
8
|
+
import { isCallExpression, isExportDeclaration, isExternalModuleReference, isImportDeclaration, isImportEqualsDeclaration, isImportTypeNode, isLiteralTypeNode, isModuleDeclaration, isNamespaceExport, isNamespaceImport, isNoSubstitutionTemplateLiteral, isStringLiteral, SyntaxKind, } from "@typescript/native/ast";
|
|
9
9
|
import { nodeKey } from "./inventory.js";
|
|
10
10
|
import { renderPosition } from "./position.js";
|
|
11
11
|
import { UNANSWERED } from "./query.js";
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* names and no other: a property access by name or by a literal index, or a destructured
|
|
6
6
|
* property, names one export, and any other use may read every export.
|
|
7
7
|
*/
|
|
8
|
-
import { isArrowFunction, isAwaitExpression, isBindingElement, isCallExpression, isElementAccessExpression, isExpressionStatement, isFunctionExpression, isIdentifier, isImportDeclaration, isNamespaceImport, isNoSubstitutionTemplateLiteral, isObjectBindingPattern, isParenthesizedExpression, isPropertyAccessExpression, isQualifiedName, isStringLiteral, isVariableDeclaration, isVariableDeclarationList, isVoidExpression, NodeFlags, SyntaxKind, } from "@typescript/native/
|
|
8
|
+
import { isArrowFunction, isAwaitExpression, isBindingElement, isCallExpression, isElementAccessExpression, isExpressionStatement, isFunctionExpression, isIdentifier, isImportDeclaration, isNamespaceImport, isNoSubstitutionTemplateLiteral, isObjectBindingPattern, isParenthesizedExpression, isPropertyAccessExpression, isQualifiedName, isStringLiteral, isVariableDeclaration, isVariableDeclarationList, isVoidExpression, NodeFlags, SyntaxKind, } from "@typescript/native/ast";
|
|
9
9
|
function isLiteralText(node) {
|
|
10
10
|
return isStringLiteral(node) || isNoSubstitutionTemplateLiteral(node);
|
|
11
11
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Such a file is read without a program, so what its body references beyond the modules
|
|
7
7
|
* it names is not known.
|
|
8
8
|
*/
|
|
9
|
-
import { isCallExpression, isExportDeclaration, isImportDeclaration, isNamespaceExport, isNamespaceImport, isNoSubstitutionTemplateLiteral, isStringLiteral, SyntaxKind, } from "@typescript/native/
|
|
9
|
+
import { isCallExpression, isExportDeclaration, isImportDeclaration, isNamespaceExport, isNamespaceImport, isNoSubstitutionTemplateLiteral, isStringLiteral, SyntaxKind, } from "@typescript/native/ast";
|
|
10
10
|
import { RESOLVED_EXTENSIONS } from "./configuration-files.js";
|
|
11
11
|
import { emittedFrom } from "./emit-map.js";
|
|
12
12
|
import { namesImported } from "./module-declarations.js";
|
package/dist/src/overrides.js
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
* of its `extends` chain, is used by outside code holding the instance or the class
|
|
7
7
|
* through that class, so its own class references it.
|
|
8
8
|
*/
|
|
9
|
-
import { isClassDeclaration, isClassExpression, isExpressionWithTypeArguments, isPropertyAccessExpression, isQualifiedName, SyntaxKind, } from "@typescript/native/
|
|
9
|
+
import { isClassDeclaration, isClassExpression, isExpressionWithTypeArguments, isPropertyAccessExpression, isQualifiedName, SyntaxKind, } from "@typescript/native/ast";
|
|
10
10
|
import { memberComponent } from "./class-members.js";
|
|
11
11
|
import { nodeKey } from "./inventory.js";
|
|
12
|
-
import { SymbolFlags } from "@typescript/native/
|
|
12
|
+
import { SymbolFlags } from "@typescript/native/sync";
|
|
13
13
|
import { UNANSWERED } from "./query.js";
|
|
14
14
|
import { nameComponent } from "./ref.js";
|
|
15
15
|
/** The member kinds a subclass overrides. */
|
package/dist/src/query.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Questions of one accessor about many locations go out together, through the array
|
|
7
7
|
* form where it has one and otherwise as one batch, one round trip per capped run.
|
|
8
8
|
*/
|
|
9
|
-
import { isIdentifier } from "@typescript/native/
|
|
9
|
+
import { isIdentifier } from "@typescript/native/ast";
|
|
10
10
|
/**
|
|
11
11
|
* The greatest number of locations one batched request asks about. A batch is
|
|
12
12
|
* answered by an array of its own length, so an uncapped batch over a large file
|
|
@@ -214,6 +214,9 @@ export function queriesOf(checker, configFile, batch, log, ask = ASK_DIRECTLY) {
|
|
|
214
214
|
constraintOf: (type) => type.isTypeParameter()
|
|
215
215
|
? one("getConstraint", () => typeLocation(type), () => type.getConstraint())
|
|
216
216
|
: undefined,
|
|
217
|
+
indexedAccessOf: (type) => type.isIndexedAccessType()
|
|
218
|
+
? one("getObjectType", () => typeLocation(type), () => ({ object: type.getObjectType(), index: type.getIndexType() }))
|
|
219
|
+
: undefined,
|
|
217
220
|
symbolOfType: (type) => one("getSymbol", () => typeLocation(type), () => type.getSymbol()),
|
|
218
221
|
signaturesOf: (type, kind) => one("getSignaturesOfType", () => typeLocation(type), () => checker.getSignaturesOfType(type, kind)),
|
|
219
222
|
resolvedSignature: (node) => one("getResolvedSignature", () => nodeLocation(node), () => checker.getResolvedSignature(node)),
|
package/dist/src/references.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isArrayLiteralExpression, isAsExpression, isBinaryExpression, isCallExpression, isDecorator, isDeleteExpression, isElementAccessExpression, isExportDeclaration, isExpressionStatement, isExternalModuleReference, isForInStatement, isForOfStatement, isForStatement, isIdentifier, isImportDeclaration, isImportEqualsDeclaration, isMetaProperty, isNewExpression, isNamespaceExport, isNoSubstitutionTemplateLiteral, isObjectLiteralExpression, isParenthesizedExpression, isPostfixUnaryExpression, isPrefixUnaryExpression, isPropertyAccessExpression, isPropertyAssignment, isShorthandPropertyAssignment, isSpreadAssignment, isSpreadElement, isStringLiteral, isVoidExpression, SyntaxKind, } from "@typescript/native/
|
|
1
|
+
import { isArrayLiteralExpression, isAsExpression, isBinaryExpression, isCallExpression, isDecorator, isDeleteExpression, isElementAccessExpression, isExportDeclaration, isExpressionStatement, isExternalModuleReference, isForInStatement, isForOfStatement, isForStatement, isIdentifier, isImportDeclaration, isImportEqualsDeclaration, isMetaProperty, isNewExpression, isNamespaceExport, isNoSubstitutionTemplateLiteral, isObjectLiteralExpression, isParenthesizedExpression, isPostfixUnaryExpression, isPrefixUnaryExpression, isPropertyAccessExpression, isPropertyAssignment, isShorthandPropertyAssignment, isSpreadAssignment, isSpreadElement, isStringLiteral, isVoidExpression, SyntaxKind, } from "@typescript/native/ast";
|
|
2
2
|
import { aliasChains } from "./alias-chain.js";
|
|
3
3
|
import { contextualUses } from "./contextual-uses.js";
|
|
4
4
|
import { destructuring, propertyReadsOf } from "./destructuring.js";
|
|
@@ -7,7 +7,10 @@ import { namespaceUses } from "./namespace-objects.js";
|
|
|
7
7
|
import { globExpression } from "./glob.js";
|
|
8
8
|
import { declarationsByName, nodeKey } from "./inventory.js";
|
|
9
9
|
import { augmentationReferences } from "./augmentations.js";
|
|
10
|
+
import { elementAccessReferences } from "./element-access.js";
|
|
10
11
|
import { indexedAccessReferences } from "./indexed-access.js";
|
|
12
|
+
import { keyofComparisonReferences } from "./keyof-comparisons.js";
|
|
13
|
+
import { unimplementedCalleeReads } from "./unimplemented-callees.js";
|
|
11
14
|
import { moduleDeclarationReferences } from "./module-declarations.js";
|
|
12
15
|
import { overrideReferences } from "./overrides.js";
|
|
13
16
|
import { dirnamePath, queriedModulePath, relativePath, resolvePath } from "./paths.js";
|
|
@@ -1110,12 +1113,19 @@ export function references(project, held, targetRoot, options) {
|
|
|
1110
1113
|
}
|
|
1111
1114
|
}
|
|
1112
1115
|
}
|
|
1116
|
+
let typedLookups = 0;
|
|
1113
1117
|
if (consumer === undefined) {
|
|
1114
1118
|
found.push(...overrideReferences(project, held, chains, new Set(tests)));
|
|
1119
|
+
const before = options.requestCount?.() ?? 0;
|
|
1115
1120
|
found.push(...indexedAccessReferences(project, held, chains, root, new Set(tests)));
|
|
1121
|
+
found.push(...elementAccessReferences(project, held, chains, root, new Set(tests)));
|
|
1122
|
+
found.push(...keyofComparisonReferences(project, held, chains, root, new Set(tests)));
|
|
1123
|
+
found.push(...unimplementedCalleeReads(project, held, chains, root, new Set(tests)));
|
|
1124
|
+
typedLookups = (options.requestCount?.() ?? 0) - before;
|
|
1116
1125
|
found.push(...moduleDeclarationReferences(project, held, root, new Set(tests)));
|
|
1117
1126
|
}
|
|
1118
|
-
|
|
1127
|
+
const scripts = new Set(walked.filter((one) => one.file.externalModuleIndicator === undefined).map((one) => one.fileId));
|
|
1128
|
+
found.push(...augmentationReferences(held, found, scripts));
|
|
1119
1129
|
found.sort((a, b) => byPosition(a.position, b.position) ||
|
|
1120
1130
|
compare(a.to, b.to) ||
|
|
1121
1131
|
compare(a.from, b.from) ||
|
|
@@ -1135,6 +1145,7 @@ export function references(project, held, targetRoot, options) {
|
|
|
1135
1145
|
patternLookups: destructured.cost.patternLookups,
|
|
1136
1146
|
contextualBatches: contextual.batches,
|
|
1137
1147
|
contextualLookups: contextual.lookups,
|
|
1148
|
+
typedLookups,
|
|
1138
1149
|
},
|
|
1139
1150
|
};
|
|
1140
1151
|
}
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
* element access through a receiver whose type declares a string index signature, and is
|
|
13
13
|
* not `any`, reads that signature and holds nothing back.
|
|
14
14
|
*/
|
|
15
|
-
import { isCallExpression, isElementAccessExpression, isIdentifier, isNoSubstitutionTemplateLiteral, isPropertyAccessExpression, isStringLiteral, } from "@typescript/native/
|
|
16
|
-
import { TypeFlags } from "@typescript/native/
|
|
15
|
+
import { isCallExpression, isElementAccessExpression, isIdentifier, isNoSubstitutionTemplateLiteral, isPropertyAccessExpression, isStringLiteral, } from "@typescript/native/ast";
|
|
16
|
+
import { TypeFlags } from "@typescript/native/sync";
|
|
17
17
|
import { memberNames } from "./member-names.js";
|
|
18
18
|
import { renderPosition } from "./position.js";
|
|
19
19
|
import { isAnswered, UNANSWERED } from "./query.js";
|
package/dist/src/report.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* renders this document, so no two renderings disagree about which findings exist.
|
|
5
5
|
*/
|
|
6
6
|
/** The version of the report schema this analyzer writes a report to. */
|
|
7
|
-
const SCHEMA_VERSION = "8.
|
|
7
|
+
const SCHEMA_VERSION = "8.1.0";
|
|
8
8
|
/** Every report schema version this analyzer reads: the one the Contract it implements admits. */
|
|
9
9
|
export const SCHEMA_VERSIONS_ACCEPTED = [SCHEMA_VERSION];
|
|
10
10
|
/** The name this analyzer writes every document under and mints every identifier with. */
|
|
@@ -122,6 +122,10 @@ function relatedOf(found) {
|
|
|
122
122
|
for (const at of found.details.write_positions ?? []) {
|
|
123
123
|
related.push({ label: "write", at, endLine: at.end_line });
|
|
124
124
|
}
|
|
125
|
+
if (found.details.name_literal !== undefined) {
|
|
126
|
+
const at = found.details.name_literal;
|
|
127
|
+
related.push({ label: "name", at, endLine: at.end_line });
|
|
128
|
+
}
|
|
125
129
|
for (const one of found.component.members ?? []) {
|
|
126
130
|
if (declarationKey(one.ref, one.position.path) !==
|
|
127
131
|
declarationKey(found.symbol.ref, found.position.path)) {
|