@cplieger/deadset-ts 3.13.0 → 4.0.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/README.md +67 -49
- package/dist/bin/node-host.js +10 -7
- package/dist/src/alias-chain.js +59 -7
- package/dist/src/analysis.js +120 -24
- package/dist/src/calls.js +23 -22
- package/dist/src/class-members.js +10 -11
- package/dist/src/configured-declarations.js +22 -9
- package/dist/src/dependencies.js +34 -17
- package/dist/src/deprecation.js +35 -24
- package/dist/src/destructuring.js +37 -29
- package/dist/src/discover.js +106 -29
- package/dist/src/entry-point-gaps.js +2 -2
- package/dist/src/entry-points.js +7 -0
- package/dist/src/enum-group.js +46 -40
- package/dist/src/exempt.js +42 -1
- package/dist/src/findings/emitters.js +15 -1
- package/dist/src/findings/interfaces.js +7 -1
- package/dist/src/framework-lifecycle.js +8 -6
- package/dist/src/implementations.js +45 -23
- package/dist/src/interface-satisfaction.js +30 -27
- package/dist/src/intra-function-parts.js +37 -20
- package/dist/src/inventory.js +85 -4
- package/dist/src/json-chunks.js +76 -0
- package/dist/src/partial.js +23 -0
- package/dist/src/query.js +207 -0
- package/dist/src/references.js +92 -20
- package/dist/src/reflective-lookup.js +18 -9
- package/dist/src/report.js +3 -1
- package/dist/src/reporters/annotations.js +9 -5
- package/dist/src/reporters/json.js +3 -2
- package/dist/src/reporters/sarif.js +2 -1
- package/dist/src/reporters/template.js +51 -10
- package/dist/src/reporters/text.js +8 -6
- package/dist/src/roots.js +27 -11
- package/dist/src/schema.js +2 -2
- package/dist/src/serialization-contract.js +12 -10
- package/dist/src/session.js +79 -39
- package/dist/src/suppress-file.js +2 -1
- package/dist/src/sweep.js +4 -1
- package/dist/src/value-reach.js +14 -19
- package/dist/src/verbs/analyze.js +20 -3
- package/dist/src/verbs/explain.js +22 -9
- package/dist/src/verbs/print-projects.js +41 -8
- package/dist/src/verbs/print-retained.js +6 -2
- package/dist/src/verbs/print-roots.js +5 -1
- package/jsr.json +1 -1
- package/package.json +3 -3
- package/src/alias-chain.ts +75 -11
- package/src/analysis.ts +224 -62
- package/src/calls.ts +10 -9
- package/src/class-members.ts +10 -11
- package/src/configured-declarations.ts +29 -13
- package/src/dependencies.ts +42 -20
- package/src/deprecation.ts +53 -27
- package/src/destructuring.ts +50 -39
- package/src/discover.ts +178 -34
- package/src/entry-point-gaps.ts +2 -2
- package/src/entry-points.ts +7 -0
- package/src/enum-group.ts +56 -42
- package/src/exempt.ts +50 -2
- package/src/findings/emitters.ts +18 -1
- package/src/findings/interfaces.ts +7 -1
- package/src/framework-lifecycle.ts +10 -6
- package/src/host.ts +5 -3
- package/src/implementations.ts +59 -23
- package/src/interface-satisfaction.ts +30 -27
- package/src/intra-function-parts.ts +43 -25
- package/src/inventory.ts +97 -4
- package/src/json-chunks.ts +87 -0
- package/src/partial.ts +40 -0
- package/src/query.ts +522 -0
- package/src/references.ts +119 -38
- package/src/reflective-lookup.ts +21 -11
- package/src/report.ts +14 -2
- package/src/reporters/annotations.ts +23 -21
- package/src/reporters/json.ts +3 -2
- package/src/reporters/reporter.ts +2 -2
- package/src/reporters/sarif.ts +3 -2
- package/src/reporters/template.ts +53 -11
- package/src/reporters/text.ts +12 -12
- package/src/roots.ts +34 -12
- package/src/schema.ts +2 -2
- package/src/serialization-contract.ts +12 -10
- package/src/session.ts +146 -58
- package/src/suppress-file.ts +6 -2
- package/src/sweep.ts +9 -4
- package/src/value-reach.ts +20 -23
- package/src/verbs/analyze.ts +25 -3
- package/src/verbs/explain.ts +20 -9
- package/src/verbs/print-projects.ts +54 -8
- package/src/verbs/print-retained.ts +6 -2
- package/src/verbs/print-roots.ts +5 -1
package/dist/src/calls.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { isArrayLiteralExpression, isCallExpression, isClassDeclaration, isDecorator, isIdentifier, isObjectLiteralExpression, isParenthesizedExpression, isPropertyAccessExpression, isPropertyAssignment, isShorthandPropertyAssignment, isSpreadElement, } from "@typescript/native/unstable/ast";
|
|
9
9
|
import { SymbolFlags } from "@typescript/native/unstable/sync";
|
|
10
10
|
import { nodeKey } from "./inventory.js";
|
|
11
|
-
import {
|
|
11
|
+
import { must } from "./query.js";
|
|
12
12
|
/** The name node a callee or a decorator expression resolves through, where it has one. */
|
|
13
13
|
export function calleeName(expression) {
|
|
14
14
|
if (isParenthesizedExpression(expression)) {
|
|
@@ -37,31 +37,32 @@ export function decoratorsOf(node) {
|
|
|
37
37
|
/**
|
|
38
38
|
* What each of a batch of nodes resolves to, every alias followed to the declaration
|
|
39
39
|
* it stands for. A node the checker resolves to nothing, and an alias that resolves
|
|
40
|
-
* to nothing, are absent from the answer. Each alias is followed once.
|
|
40
|
+
* to nothing, are absent from the answer. Each alias is followed once. A question the
|
|
41
|
+
* checker does not answer stops the pass, as {@link must} stops.
|
|
41
42
|
*/
|
|
42
43
|
export function resolvedTargets(project, nodes) {
|
|
43
44
|
const followed = new Map();
|
|
44
45
|
const targets = new Map();
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
target = followed.get(symbol.id);
|
|
59
|
-
}
|
|
60
|
-
if (target !== undefined) {
|
|
61
|
-
targets.set(node, target);
|
|
46
|
+
project
|
|
47
|
+
.symbolsAt(nodes.map((node) => project.handle(node)))
|
|
48
|
+
.map(must)
|
|
49
|
+
.forEach((symbol, index) => {
|
|
50
|
+
const node = nodes[index];
|
|
51
|
+
if (symbol === undefined || node === undefined) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
let target = symbol;
|
|
55
|
+
if ((symbol.flags & SymbolFlags.Alias) !== 0) {
|
|
56
|
+
if (!followed.has(symbol.id)) {
|
|
57
|
+
const aliased = must(project.queries.aliased(symbol));
|
|
58
|
+
followed.set(symbol.id, aliased.declarations.length === 0 ? undefined : aliased);
|
|
62
59
|
}
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
target = followed.get(symbol.id);
|
|
61
|
+
}
|
|
62
|
+
if (target !== undefined) {
|
|
63
|
+
targets.set(node, target);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
65
66
|
return targets;
|
|
66
67
|
}
|
|
67
68
|
/** The values one expression passes: itself, or what an array or object literal holds. */
|
|
@@ -109,7 +110,7 @@ export function classesPassed(project, held, chains, calls) {
|
|
|
109
110
|
const classes = new Set(held.symbols.filter((symbol) => symbol.kind === "class").map((symbol) => symbol.id));
|
|
110
111
|
const classesOf = (value) => {
|
|
111
112
|
const symbol = value.shorthand
|
|
112
|
-
? project.shorthandValueAt(project.handle(value.name))
|
|
113
|
+
? must(project.shorthandValueAt(project.handle(value.name)))
|
|
113
114
|
: targets.get(value.name);
|
|
114
115
|
return symbol === undefined
|
|
115
116
|
? []
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { isClassDeclaration, isClassExpression } from "@typescript/native/unstable/ast";
|
|
7
7
|
import { nodeKey } from "./inventory.js";
|
|
8
|
-
import {
|
|
8
|
+
import { must } from "./query.js";
|
|
9
9
|
/** The name node of each class declaration of the target, by the class's identifier. */
|
|
10
10
|
export function classNames(project, held) {
|
|
11
11
|
const names = new Map();
|
|
@@ -23,13 +23,12 @@ export function classNames(project, held) {
|
|
|
23
23
|
}
|
|
24
24
|
return names;
|
|
25
25
|
}
|
|
26
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* The types of a batch of nodes, for a pass that cannot go on past a type the checker
|
|
28
|
+
* did not answer: it stops there, as {@link must} stops.
|
|
29
|
+
*/
|
|
27
30
|
export function typesAt(project, nodes) {
|
|
28
|
-
|
|
29
|
-
for (let from = 0; from < nodes.length; from += DEFAULT_BATCH_CAP) {
|
|
30
|
-
types.push(...project.checker.getTypeAtLocation(nodes.slice(from, from + DEFAULT_BATCH_CAP)));
|
|
31
|
-
}
|
|
32
|
-
return types;
|
|
31
|
+
return project.queries.typesAt(nodes).map(must);
|
|
33
32
|
}
|
|
34
33
|
/**
|
|
35
34
|
* The type one side of each class is: the instance type at the class's name, or the
|
|
@@ -39,9 +38,9 @@ function sideTypes(project, nodes, side) {
|
|
|
39
38
|
if (side === "instance") {
|
|
40
39
|
return typesAt(project, nodes);
|
|
41
40
|
}
|
|
42
|
-
const symbols = project.symbolsAt(nodes.map((node) => project.handle(node)));
|
|
41
|
+
const symbols = project.symbolsAt(nodes.map((node) => project.handle(node))).map(must);
|
|
43
42
|
const present = symbols.filter((symbol) => symbol !== undefined);
|
|
44
|
-
const types =
|
|
43
|
+
const types = project.queries.typesOfSymbols(present).map(must);
|
|
45
44
|
let next = 0;
|
|
46
45
|
return symbols.map((symbol) => (symbol === undefined ? undefined : types[next++]));
|
|
47
46
|
}
|
|
@@ -52,7 +51,7 @@ function sideTypes(project, nodes, side) {
|
|
|
52
51
|
*/
|
|
53
52
|
export function classMembers(project, held, classIds, side) {
|
|
54
53
|
const named = [...classNames(project, held)].filter(([id]) => classIds.has(id));
|
|
55
|
-
const own =
|
|
54
|
+
const own = project.ownPaths();
|
|
56
55
|
const types = sideTypes(project, named.map(([, node]) => node), side);
|
|
57
56
|
const members = new Map();
|
|
58
57
|
named.forEach(([id], index) => {
|
|
@@ -61,7 +60,7 @@ export function classMembers(project, held, classIds, side) {
|
|
|
61
60
|
return;
|
|
62
61
|
}
|
|
63
62
|
const found = [];
|
|
64
|
-
for (const property of project.
|
|
63
|
+
for (const property of must(project.queries.propertiesOf(type))) {
|
|
65
64
|
for (const handle of property.declarations) {
|
|
66
65
|
if (!own.has(handle.path)) {
|
|
67
66
|
continue;
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { isCallExpression, isExportDeclaration, isExternalModuleReference, isImportDeclaration, isImportEqualsDeclaration, isNoSubstitutionTemplateLiteral, isStringLiteral, SyntaxKind, } from "@typescript/native/unstable/ast";
|
|
8
8
|
import { SymbolFlags, } from "@typescript/native/unstable/sync";
|
|
9
9
|
import { resolvedTargets } from "./calls.js";
|
|
10
|
+
import { must, Unanswerable } from "./query.js";
|
|
10
11
|
import { nameComponent } from "./ref.js";
|
|
11
12
|
/** The meanings a name of the global scope is looked up under: any of them. */
|
|
12
13
|
const ANY_MEANING = SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace;
|
|
@@ -55,7 +56,7 @@ function unaliased(project, symbol) {
|
|
|
55
56
|
if ((symbol.flags & SymbolFlags.Alias) === 0) {
|
|
56
57
|
return symbol;
|
|
57
58
|
}
|
|
58
|
-
const target = project.
|
|
59
|
+
const target = must(project.queries.aliased(symbol));
|
|
59
60
|
return target.declarations.length === 0 ? undefined : target;
|
|
60
61
|
}
|
|
61
62
|
/** Whether one symbol is a member a class declares: a property, a method or an accessor. */
|
|
@@ -68,20 +69,20 @@ function isClassMember(symbol) {
|
|
|
68
69
|
* with the static suffix names one of the first and a component without it names an
|
|
69
70
|
* instance member or one of the second.
|
|
70
71
|
*/
|
|
71
|
-
function candidatesOf(container, isStatic) {
|
|
72
|
-
const exported = [...
|
|
72
|
+
function candidatesOf(project, container, isStatic) {
|
|
73
|
+
const exported = [...must(project.queries.exportsOf(container)).values()];
|
|
73
74
|
if (isStatic) {
|
|
74
75
|
return exported.filter(isClassMember);
|
|
75
76
|
}
|
|
76
77
|
const isClass = (container.flags & SymbolFlags.Class) !== 0;
|
|
77
78
|
return [
|
|
78
|
-
...
|
|
79
|
+
...must(project.queries.membersOf(container)).values(),
|
|
79
80
|
...exported.filter((member) => !isClass || !isClassMember(member)),
|
|
80
81
|
];
|
|
81
82
|
}
|
|
82
83
|
/** The member of one container a path component spells, followed through any alias. */
|
|
83
84
|
function memberOf(project, container, component, isStatic) {
|
|
84
|
-
const member = candidatesOf(container, isStatic).find((candidate) => nameComponent(candidate.name) === component);
|
|
85
|
+
const member = candidatesOf(project, container, isStatic).find((candidate) => nameComponent(candidate.name) === component);
|
|
85
86
|
return member === undefined ? undefined : unaliased(project, member);
|
|
86
87
|
}
|
|
87
88
|
/** The declaration a path names from one starting symbol, component by component. */
|
|
@@ -131,7 +132,7 @@ function modulesNamed(project, specifiers) {
|
|
|
131
132
|
file.forEachChild(visit);
|
|
132
133
|
}
|
|
133
134
|
const specified = [...written];
|
|
134
|
-
const symbols = project.symbolsAt(specified.map(([, node]) => project.handle(node)));
|
|
135
|
+
const symbols = project.symbolsAt(specified.map(([, node]) => project.handle(node))).map(must);
|
|
135
136
|
const modules = new Map();
|
|
136
137
|
specified.forEach(([name], index) => {
|
|
137
138
|
const symbol = symbols[index];
|
|
@@ -171,7 +172,7 @@ export function resolveEntries(project, held, entries) {
|
|
|
171
172
|
start = module === undefined ? undefined : memberOf(project, module, first, false);
|
|
172
173
|
}
|
|
173
174
|
else if (nameComponent(first) === first) {
|
|
174
|
-
const global = project.
|
|
175
|
+
const global = must(project.queries.resolveName(first, ANY_MEANING, undefined, false));
|
|
175
176
|
start = global === undefined ? undefined : unaliased(project, global);
|
|
176
177
|
}
|
|
177
178
|
const declared = walkPath(project, start, rest, isStatic);
|
|
@@ -247,9 +248,21 @@ export function configuredDeclarations(config) {
|
|
|
247
248
|
...config.ts.serializers.map((entry) => ({ key: "ts.serializers", entry })),
|
|
248
249
|
];
|
|
249
250
|
}
|
|
250
|
-
/**
|
|
251
|
+
/**
|
|
252
|
+
* Whether each configured entry names a declaration in one project, in the entries'
|
|
253
|
+
* order. Where the checker leaves a question of the resolution unanswered, every entry
|
|
254
|
+
* counts as naming one, so no entry is reported as naming nothing on a gap.
|
|
255
|
+
*/
|
|
251
256
|
export function declarationsNamed(project, held, configured) {
|
|
252
|
-
|
|
257
|
+
try {
|
|
258
|
+
return resolveEntries(project, held, configured.map((one) => one.entry)).map(namesADeclaration);
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
if (!(error instanceof Unanswerable)) {
|
|
262
|
+
throw error;
|
|
263
|
+
}
|
|
264
|
+
return configured.map(() => true);
|
|
265
|
+
}
|
|
253
266
|
}
|
|
254
267
|
/**
|
|
255
268
|
* The reference a finding about a configured entry names it by, the entry as the
|
package/dist/src/dependencies.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { isExportDeclaration, isExternalModuleReference, isIdentifier, isImportDeclaration, isImportEqualsDeclaration, isNamespaceExport, isShorthandPropertyAssignment, isStringLiteral, isNoSubstitutionTemplateLiteral, SyntaxKind, } from "@typescript/native/unstable/ast";
|
|
9
9
|
import { nodeKey, packageScope } from "./inventory.js";
|
|
10
10
|
import { dirnamePath, isAbsolutePath, joinPath, resolvePath } from "./paths.js";
|
|
11
|
-
import {
|
|
11
|
+
import { UNANSWERED } from "./query.js";
|
|
12
12
|
const MANIFEST = "package.json";
|
|
13
13
|
const MODULES_DIR = "node_modules";
|
|
14
14
|
const MODULES_SEGMENT = `/${MODULES_DIR}/`;
|
|
@@ -207,10 +207,11 @@ export function projectNeeds(project, held, host) {
|
|
|
207
207
|
}
|
|
208
208
|
};
|
|
209
209
|
use([PROJECT_OWNER], packages);
|
|
210
|
+
let unanswered = false;
|
|
210
211
|
for (const file of project.ownSourceFiles()) {
|
|
211
|
-
fileNeeds(project, held, file, programPackages, use);
|
|
212
|
+
unanswered = fileNeeds(project, held, file, programPackages, use) || unanswered;
|
|
212
213
|
}
|
|
213
|
-
return { packages, uses };
|
|
214
|
+
return { packages, uses, unanswered };
|
|
214
215
|
}
|
|
215
216
|
/** The declarations of the inventory a use written at one node belongs to. */
|
|
216
217
|
function ownersOf(file, held, node) {
|
|
@@ -235,6 +236,7 @@ function ownersOf(file, held, node) {
|
|
|
235
236
|
}
|
|
236
237
|
return [PROJECT_OWNER];
|
|
237
238
|
}
|
|
239
|
+
/** Records the needs of one file, and answers whether a specifier of it went unanswered. */
|
|
238
240
|
function fileNeeds(project, held, file, programPackages, use) {
|
|
239
241
|
const specifiers = [...file.imports, ...file.moduleAugmentations].filter(isLiteralText);
|
|
240
242
|
const bindings = [];
|
|
@@ -262,13 +264,8 @@ function fileNeeds(project, held, file, programPackages, use) {
|
|
|
262
264
|
visit(file);
|
|
263
265
|
}
|
|
264
266
|
const asked = [...specifiers, ...bindings.map(({ node }) => node), ...candidates];
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
const handles = asked
|
|
268
|
-
.slice(at, at + DEFAULT_BATCH_CAP)
|
|
269
|
-
.map((node) => project.handle(node));
|
|
270
|
-
resolved.push(...project.symbolsAt(handles));
|
|
271
|
-
}
|
|
267
|
+
const handles = asked.map((node) => project.handle(node));
|
|
268
|
+
const resolved = project.symbolsAt(handles);
|
|
272
269
|
const packagesOf = new Map();
|
|
273
270
|
specifiers.forEach((specifier, index) => {
|
|
274
271
|
const named = new Set();
|
|
@@ -276,7 +273,8 @@ function fileNeeds(project, held, file, programPackages, use) {
|
|
|
276
273
|
if (byName !== undefined) {
|
|
277
274
|
named.add(byName);
|
|
278
275
|
}
|
|
279
|
-
|
|
276
|
+
const symbol = resolved[index];
|
|
277
|
+
for (const declaration of typeof symbol === "object" ? symbol.declarations : []) {
|
|
280
278
|
const byPath = packageOfPath(declaration.path);
|
|
281
279
|
if (byPath !== undefined) {
|
|
282
280
|
named.add(byPath);
|
|
@@ -284,11 +282,20 @@ function fileNeeds(project, held, file, programPackages, use) {
|
|
|
284
282
|
}
|
|
285
283
|
packagesOf.set(specifier, [...named]);
|
|
286
284
|
});
|
|
285
|
+
// A name the checker could not resolve counts as a use of every package a binding of
|
|
286
|
+
// that name brings in, so a gap never makes a dependency read as unused.
|
|
287
287
|
const bySymbol = new Map();
|
|
288
|
-
|
|
288
|
+
const byName = new Map();
|
|
289
|
+
const unresolvedNames = new Set();
|
|
290
|
+
bindings.forEach(({ node, specifier }, index) => {
|
|
291
|
+
const packages = packagesOf.get(specifier) ?? [];
|
|
292
|
+
byName.set(node.text, [...(byName.get(node.text) ?? []), ...packages]);
|
|
289
293
|
const symbol = resolved[specifiers.length + index];
|
|
290
|
-
if (symbol
|
|
291
|
-
|
|
294
|
+
if (symbol === UNANSWERED) {
|
|
295
|
+
unresolvedNames.add(node.text);
|
|
296
|
+
}
|
|
297
|
+
else if (symbol !== undefined) {
|
|
298
|
+
bySymbol.set(symbol.id, packages);
|
|
292
299
|
}
|
|
293
300
|
});
|
|
294
301
|
for (const specifier of specifiers) {
|
|
@@ -304,7 +311,12 @@ function fileNeeds(project, held, file, programPackages, use) {
|
|
|
304
311
|
if (parent !== undefined && isShorthandPropertyAssignment(parent) && parent.name === node) {
|
|
305
312
|
symbol = project.shorthandValueAt(project.handle(node));
|
|
306
313
|
}
|
|
307
|
-
const named = symbol ===
|
|
314
|
+
const named = symbol === UNANSWERED || (unresolvedNames.has(node.text) && symbol === undefined)
|
|
315
|
+
? byName.get(node.text)
|
|
316
|
+
: symbol === undefined
|
|
317
|
+
? undefined
|
|
318
|
+
: (bySymbol.get(symbol.id) ??
|
|
319
|
+
(unresolvedNames.has(node.text) ? byName.get(node.text) : undefined));
|
|
308
320
|
if (named !== undefined) {
|
|
309
321
|
use(ownersOf(file, held, node), named);
|
|
310
322
|
}
|
|
@@ -312,6 +324,7 @@ function fileNeeds(project, held, file, programPackages, use) {
|
|
|
312
324
|
for (const reference of file.typeReferenceDirectives) {
|
|
313
325
|
use(ownersOf(file, held, file), typePackages(reference.fileName, programPackages));
|
|
314
326
|
}
|
|
327
|
+
return resolved.slice(0, specifiers.length).includes(UNANSWERED);
|
|
315
328
|
}
|
|
316
329
|
/** The offset just past the JSON value that starts at `at` in text the parser has accepted. */
|
|
317
330
|
function valueEnd(text, at) {
|
|
@@ -470,12 +483,16 @@ export function dependenciesOf(host, targetRoot, perProject, candidates) {
|
|
|
470
483
|
const needed = new Set();
|
|
471
484
|
const uses = new Map();
|
|
472
485
|
for (const project of perProject) {
|
|
473
|
-
|
|
474
|
-
|
|
486
|
+
// A specifier the checker left unanswered may resolve to any declared dependency,
|
|
487
|
+
// and what the configuration needs is never deleted, so it is needed there.
|
|
488
|
+
const unknown = project.unanswered ? declared.map((dependency) => dependency.name) : [];
|
|
489
|
+
const held = unknown.length === 0 ? [] : [[PROJECT_OWNER, new Set(unknown)]];
|
|
490
|
+
for (const [id, used] of [...project.uses, ...held]) {
|
|
475
491
|
const merged = uses.get(id) ?? new Set();
|
|
476
492
|
used.forEach((one) => merged.add(one));
|
|
477
493
|
uses.set(id, merged);
|
|
478
494
|
}
|
|
495
|
+
[...project.packages, ...unknown].forEach((one) => needed.add(one));
|
|
479
496
|
}
|
|
480
497
|
const users = new Map();
|
|
481
498
|
for (const used of uses.values()) {
|
package/dist/src/deprecation.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { isVariableStatement, SyntaxKind } from "@typescript/native/unstable/ast";
|
|
10
10
|
import { nodeKey } from "./inventory.js";
|
|
11
|
-
import {
|
|
11
|
+
import { isAnswered, UNANSWERED } from "./query.js";
|
|
12
12
|
/** The tag the TypeScript tools recognize as marking a declaration deprecated. */
|
|
13
13
|
const DEPRECATED_TAG = "deprecated";
|
|
14
14
|
/** Whether one node's own documentation carries the deprecation tag. */
|
|
@@ -21,15 +21,15 @@ function nameNode(node) {
|
|
|
21
21
|
return node.name ?? node;
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
|
-
* The
|
|
25
|
-
* marker, in the inventory's order.
|
|
24
|
+
* The declarations of one project that carry the deprecation marker.
|
|
26
25
|
*
|
|
27
26
|
* The checker is asked only for a symbol with a declaration in a file whose tree holds
|
|
28
27
|
* the tag, so a project whose files hold none costs no round trip, and one that does
|
|
29
|
-
* costs one batch per file and one
|
|
28
|
+
* costs one batch per file and one for the tags of the symbols that could carry it.
|
|
30
29
|
*/
|
|
31
30
|
export function deprecatedDeclarations(project, held) {
|
|
32
31
|
const marked = new Set();
|
|
32
|
+
const unknown = new Set();
|
|
33
33
|
const markedPaths = new Set();
|
|
34
34
|
const declared = [];
|
|
35
35
|
for (const file of project.ownSourceFiles()) {
|
|
@@ -56,28 +56,39 @@ export function deprecatedDeclarations(project, held) {
|
|
|
56
56
|
declared.push(named);
|
|
57
57
|
}
|
|
58
58
|
if (markedPaths.size > 0) {
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
const pairs = declared.flatMap((named) => {
|
|
60
|
+
const symbols = project.symbolsAt(named.map((one) => one.handle));
|
|
61
|
+
return named.flatMap((one, at) => {
|
|
62
|
+
const symbol = symbols[at];
|
|
63
|
+
if (symbol === UNANSWERED) {
|
|
64
|
+
unknown.add(one.id);
|
|
65
|
+
}
|
|
66
|
+
return typeof symbol === "object" &&
|
|
67
|
+
symbol.declarations.some((declaration) => markedPaths.has(declaration.path))
|
|
68
|
+
? [{ id: one.id, symbol }]
|
|
69
|
+
: [];
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
const distinct = [...new Map(pairs.map((one) => [one.symbol.id, one.symbol])).values()];
|
|
73
|
+
const tags = project.queries.jsDocTags(distinct);
|
|
74
|
+
const unread = new Set(distinct.filter((_, at) => tags[at] === UNANSWERED).map((one) => one.id));
|
|
75
|
+
const carries = new Set(distinct.flatMap((symbol, at) => {
|
|
76
|
+
const read = tags[at];
|
|
77
|
+
return read !== undefined &&
|
|
78
|
+
isAnswered(read) &&
|
|
79
|
+
read.some((tag) => tag.name === DEPRECATED_TAG)
|
|
80
|
+
? [symbol.id]
|
|
81
|
+
: [];
|
|
82
|
+
}));
|
|
83
|
+
for (const one of pairs) {
|
|
84
|
+
if (carries.has(one.symbol.id)) {
|
|
85
|
+
marked.add(one.id);
|
|
64
86
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
asked.set(symbol.id, carries);
|
|
68
|
-
return carries;
|
|
69
|
-
};
|
|
70
|
-
for (const named of declared) {
|
|
71
|
-
for (let start = 0; start < named.length; start += DEFAULT_BATCH_CAP) {
|
|
72
|
-
const batch = named.slice(start, start + DEFAULT_BATCH_CAP);
|
|
73
|
-
project.symbolsAt(batch.map((one) => one.handle)).forEach((symbol, at) => {
|
|
74
|
-
const one = batch[at];
|
|
75
|
-
if (symbol !== undefined && one !== undefined && carriesTag(symbol)) {
|
|
76
|
-
marked.add(one.id);
|
|
77
|
-
}
|
|
78
|
-
});
|
|
87
|
+
if (unread.has(one.symbol.id)) {
|
|
88
|
+
unknown.add(one.id);
|
|
79
89
|
}
|
|
80
90
|
}
|
|
81
91
|
}
|
|
82
|
-
|
|
92
|
+
const inOrder = (ids) => held.symbols.filter((symbol) => ids.has(symbol.id)).map((symbol) => symbol.id);
|
|
93
|
+
return { deprecated: inOrder(marked), unanswered: inOrder(unknown) };
|
|
83
94
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { isArrayLiteralExpression, isBinaryExpression, isForOfStatement, isIdentifier, isNoSubstitutionTemplateLiteral, isNumericLiteral, isObjectBindingPattern, isObjectLiteralExpression, isParameterDeclaration, isParenthesizedExpression, isPropertyAssignment, isShorthandPropertyAssignment, isStringLiteral, SyntaxKind, } from "@typescript/native/unstable/ast";
|
|
9
9
|
import { TypeFlags } from "@typescript/native/unstable/sync";
|
|
10
|
+
import { isAnswered, UNANSWERED } from "./query.js";
|
|
10
11
|
/** The text a property name spells, or `undefined` for a computed name. */
|
|
11
12
|
function nameText(node) {
|
|
12
13
|
if (node === undefined) {
|
|
@@ -151,29 +152,22 @@ function sourceOf(target) {
|
|
|
151
152
|
* places and a table answers every name of it.
|
|
152
153
|
*/
|
|
153
154
|
export function destructuring(project, cap) {
|
|
154
|
-
const
|
|
155
|
+
const queries = project.queries;
|
|
155
156
|
const tables = new Map();
|
|
156
157
|
let patternBatches = 0;
|
|
157
158
|
let patternLookups = 0;
|
|
158
|
-
const typesAt = (nodes) => {
|
|
159
|
-
const types = [];
|
|
160
|
-
for (let from = 0; from < nodes.length; from += cap) {
|
|
161
|
-
types.push(...checker.getTypeAtLocation(nodes.slice(from, from + cap)));
|
|
162
|
-
patternBatches += 1;
|
|
163
|
-
}
|
|
164
|
-
return types;
|
|
165
|
-
};
|
|
166
159
|
const tableOf = (type) => {
|
|
167
160
|
const known = tables.get(type.id);
|
|
168
161
|
if (known !== undefined) {
|
|
169
162
|
return known;
|
|
170
163
|
}
|
|
171
|
-
|
|
164
|
+
let table = new Map();
|
|
172
165
|
if ((type.flags & TypeFlags.AnyOrUnknown) === 0) {
|
|
173
166
|
patternLookups += 1;
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
167
|
+
const properties = queries.propertiesOf(type);
|
|
168
|
+
table = isAnswered(properties)
|
|
169
|
+
? new Map(properties.map((property) => [property.name, property]))
|
|
170
|
+
: UNANSWERED;
|
|
177
171
|
}
|
|
178
172
|
tables.set(type.id, table);
|
|
179
173
|
return table;
|
|
@@ -183,7 +177,7 @@ export function destructuring(project, cap) {
|
|
|
183
177
|
return type;
|
|
184
178
|
}
|
|
185
179
|
patternLookups += 1;
|
|
186
|
-
return
|
|
180
|
+
return queries.nonNullable(type);
|
|
187
181
|
};
|
|
188
182
|
/**
|
|
189
183
|
* The type one element of an array or tuple value has: a tuple's element at `index`,
|
|
@@ -193,33 +187,42 @@ export function destructuring(project, cap) {
|
|
|
193
187
|
if (!type.isTypeReference()) {
|
|
194
188
|
return undefined;
|
|
195
189
|
}
|
|
196
|
-
|
|
197
|
-
if (checker.isTupleType(type)) {
|
|
190
|
+
if (type.isTupleType()) {
|
|
198
191
|
if (index === undefined) {
|
|
199
192
|
return undefined;
|
|
200
193
|
}
|
|
201
194
|
patternLookups += 1;
|
|
202
|
-
|
|
195
|
+
const elements = queries.typeArguments(type);
|
|
196
|
+
return isAnswered(elements) ? elements[index] : UNANSWERED;
|
|
203
197
|
}
|
|
204
198
|
patternLookups += 1;
|
|
205
|
-
|
|
199
|
+
const array = queries.isArray(type);
|
|
200
|
+
if (!isAnswered(array)) {
|
|
201
|
+
return UNANSWERED;
|
|
202
|
+
}
|
|
203
|
+
if (!array) {
|
|
206
204
|
return undefined;
|
|
207
205
|
}
|
|
208
206
|
patternLookups += 1;
|
|
209
|
-
|
|
207
|
+
const elements = queries.typeArguments(type);
|
|
208
|
+
return isAnswered(elements) ? elements[0] : UNANSWERED;
|
|
210
209
|
};
|
|
211
210
|
const step = (type, next) => {
|
|
212
211
|
const defined = definedOf(type);
|
|
213
|
-
if (defined === undefined) {
|
|
214
|
-
return
|
|
212
|
+
if (defined === undefined || defined === UNANSWERED) {
|
|
213
|
+
return defined;
|
|
215
214
|
}
|
|
216
215
|
if (next.kind === "property") {
|
|
217
|
-
const
|
|
216
|
+
const table = tableOf(defined);
|
|
217
|
+
if (!isAnswered(table)) {
|
|
218
|
+
return UNANSWERED;
|
|
219
|
+
}
|
|
220
|
+
const property = table.get(next.name);
|
|
218
221
|
if (property === undefined) {
|
|
219
222
|
return undefined;
|
|
220
223
|
}
|
|
221
224
|
patternLookups += 1;
|
|
222
|
-
return
|
|
225
|
+
return queries.typeOfSymbol(property);
|
|
223
226
|
}
|
|
224
227
|
return elementOf(defined, next.kind === "element" ? next.index : undefined);
|
|
225
228
|
};
|
|
@@ -238,11 +241,12 @@ export function destructuring(project, cap) {
|
|
|
238
241
|
asking.add(source.expression);
|
|
239
242
|
}
|
|
240
243
|
}
|
|
241
|
-
const answered = new Map();
|
|
242
244
|
const asked = [...asking];
|
|
243
|
-
|
|
245
|
+
patternBatches += Math.ceil(asked.length / cap);
|
|
246
|
+
const answered = new Map();
|
|
247
|
+
queries.typesAt(asked, cap).forEach((type, index) => {
|
|
244
248
|
const node = asked[index];
|
|
245
|
-
if (
|
|
249
|
+
if (node !== undefined) {
|
|
246
250
|
answered.set(node, type);
|
|
247
251
|
}
|
|
248
252
|
});
|
|
@@ -255,13 +259,17 @@ export function destructuring(project, cap) {
|
|
|
255
259
|
}
|
|
256
260
|
let type = answered.get(source.expression);
|
|
257
261
|
for (const next of source.steps) {
|
|
258
|
-
type = type === undefined ?
|
|
262
|
+
type = type === undefined || type === UNANSWERED ? type : step(type, next);
|
|
259
263
|
}
|
|
260
|
-
patternTypes.set(pattern, type === undefined ?
|
|
264
|
+
patternTypes.set(pattern, type === undefined || type === UNANSWERED ? type : definedOf(type));
|
|
261
265
|
}
|
|
262
266
|
return reads.map((read) => {
|
|
263
267
|
const type = patternTypes.get(read.pattern);
|
|
264
|
-
|
|
268
|
+
if (type === undefined || type === UNANSWERED) {
|
|
269
|
+
return type;
|
|
270
|
+
}
|
|
271
|
+
const table = tableOf(type);
|
|
272
|
+
return isAnswered(table) ? table.get(read.name) : UNANSWERED;
|
|
265
273
|
});
|
|
266
274
|
};
|
|
267
275
|
return {
|