@cplieger/deadset-ts 8.0.0-dev.17 → 8.0.0-dev.19
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 +30 -72
- package/dist/src/augmentations.js +1 -1
- package/dist/src/calls.js +267 -48
- package/dist/src/commands/held.js +499 -0
- package/dist/src/commands/read.js +16 -10
- package/dist/src/commands/shell.js +109 -15
- package/dist/src/commands/tools.js +1540 -149
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/contract/contract.generated.js +9 -2
- package/dist/src/entries/conventions.js +7 -6
- package/dist/src/entries/test-runners.js +6 -40
- package/dist/src/entries/workflow-steps.js +202 -28
- package/dist/src/exempt.js +0 -26
- package/dist/src/exposure.js +400 -0
- package/dist/src/findings/completion.js +4 -17
- package/dist/src/findings/emitters.js +22 -33
- package/dist/src/findings/intra-function.js +3 -3
- package/dist/src/findings/non-code-artifacts.js +11 -5
- package/dist/src/findings/reads-and-writes.js +71 -15
- package/dist/src/findings/unused-declarations.js +2 -22
- package/dist/src/findings/visibility-narrowing.js +14 -20
- package/dist/src/inventory.js +18 -0
- package/dist/src/{components.js → liveness/components.js} +1 -1
- package/dist/src/{graph.js → liveness/graph.js} +38 -21
- package/dist/src/liveness/matrix.js +99 -0
- package/dist/src/liveness/sweep.js +558 -0
- package/dist/src/query.js +46 -0
- package/dist/src/references/sink.js +87 -1
- package/dist/src/references.js +1 -1
- package/dist/src/roots/manifest.js +47 -4
- package/dist/src/roots/published-reach.js +4 -4
- package/dist/src/roots/root-set.js +14 -16
- package/dist/src/text/named-files.js +257 -34
- package/dist/src/verbs/analyze.js +16 -1
- package/dist/src/verbs/explain.js +73 -152
- package/dist/src/verbs/print-retained.js +1 -1
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/analysis.ts +54 -108
- package/src/augmentations.ts +1 -1
- package/src/calls.ts +326 -52
- package/src/commands/held.ts +632 -0
- package/src/commands/read.ts +26 -12
- package/src/commands/shell.ts +117 -15
- package/src/commands/tools.ts +1972 -202
- package/src/conformance-record.ts +1 -1
- package/src/contract/contract.generated.ts +9 -2
- package/src/dependencies.ts +1 -1
- package/src/entries/conventions.ts +8 -12
- package/src/entries/test-runners.ts +12 -45
- package/src/entries/workflow-steps.ts +227 -38
- package/src/exempt.ts +31 -46
- package/src/exposure.ts +492 -0
- package/src/file-facts.ts +5 -0
- package/src/finding.ts +1 -1
- package/src/findings/boundary.ts +7 -0
- package/src/findings/completion.ts +5 -19
- package/src/findings/emitters.ts +23 -41
- package/src/findings/interfaces.ts +3 -3
- package/src/findings/intra-function.ts +3 -3
- package/src/findings/non-code-artifacts.ts +16 -5
- package/src/findings/reads-and-writes.ts +99 -26
- package/src/findings/unused-declarations.ts +4 -28
- package/src/findings/visibility-narrowing.ts +25 -26
- package/src/interface-satisfaction.ts +1 -1
- package/src/inventory.ts +19 -0
- package/src/{components.ts → liveness/components.ts} +3 -3
- package/src/{graph.ts → liveness/graph.ts} +74 -26
- package/src/liveness/matrix.ts +162 -0
- package/src/liveness/sweep.ts +855 -0
- package/src/query.ts +81 -0
- package/src/references/sink.ts +101 -0
- package/src/references.ts +8 -1
- package/src/roots/manifest.ts +53 -8
- package/src/roots/published-reach.ts +9 -4
- package/src/roots/root-set.ts +18 -16
- package/src/stores.ts +2 -1
- package/src/tests/file-class.ts +19 -2
- package/src/text/named-files.ts +333 -36
- package/src/verbs/analyze.ts +16 -1
- package/src/verbs/explain.ts +81 -199
- package/src/verbs/print-retained.ts +1 -1
- package/dist/src/matrix.js +0 -275
- package/dist/src/sweep.js +0 -234
- package/src/matrix.ts +0 -466
- package/src/sweep.ts +0 -384
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* reachability, indexed for the sweep. The graph reads no file and asks the
|
|
4
|
-
* nothing, so a sweep over it is a function of the values it was built from, and
|
|
5
|
-
* inventory's order is the order every set a sweep answers reads in.
|
|
2
|
+
* The run's declarations, the references every configuration makes between them and the
|
|
3
|
+
* roots that seed reachability, indexed for the sweep. The graph reads no file and asks the
|
|
4
|
+
* compiler nothing, so a sweep over it is a function of the values it was built from, and
|
|
5
|
+
* the inventory's order is the order every set a sweep answers reads in.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type { InventorySymbol, SymbolKind } from "
|
|
9
|
-
import type { Reference } from "
|
|
10
|
-
import { madeByTestCode } from "
|
|
11
|
-
import type { Root, RootKind } from "
|
|
8
|
+
import type { InventorySymbol, SymbolKind } from "../inventory.ts";
|
|
9
|
+
import type { Reference } from "../references.ts";
|
|
10
|
+
import { madeByTestCode } from "../tests/file-class.ts";
|
|
11
|
+
import type { Root, RootKind } from "../roots/root-set.ts";
|
|
12
12
|
|
|
13
13
|
/** The position of a declaration the inventory does not hold. */
|
|
14
14
|
export const OUTSIDE = -1;
|
|
@@ -80,7 +80,11 @@ export function namesACaller(kind: RootKind): boolean {
|
|
|
80
80
|
/** One reference as the sweep reads it: the declaration named, and whether a test file made it. */
|
|
81
81
|
export interface Edge {
|
|
82
82
|
readonly to: number;
|
|
83
|
+
/** The reference's place in {@link Graph.references}. */
|
|
84
|
+
readonly reference: number;
|
|
83
85
|
readonly test: boolean;
|
|
86
|
+
/** Whether only the name answered, which counts at the sweep's guessed tier alone. */
|
|
87
|
+
readonly guessed: boolean;
|
|
84
88
|
/**
|
|
85
89
|
* Whether the reference evaluates the module whose file it names, which reaches the
|
|
86
90
|
* module's top level and none of what it exports.
|
|
@@ -105,9 +109,11 @@ export interface Counts {
|
|
|
105
109
|
}
|
|
106
110
|
|
|
107
111
|
/** One root the inventory holds, at the position of the declaration it names. */
|
|
108
|
-
interface Rooted {
|
|
112
|
+
export interface Rooted {
|
|
109
113
|
readonly at: number;
|
|
110
114
|
readonly kind: RootKind;
|
|
115
|
+
/** What names the root, as the root set spells it. */
|
|
116
|
+
readonly source: string;
|
|
111
117
|
}
|
|
112
118
|
|
|
113
119
|
/** The index the sweep and the component pass read. */
|
|
@@ -118,6 +124,12 @@ export interface Graph {
|
|
|
118
124
|
readonly out: readonly (readonly Edge[])[];
|
|
119
125
|
/** Per declaration, the references made to it, whoever made them. */
|
|
120
126
|
readonly made: readonly Counts[];
|
|
127
|
+
/** Per declaration, those of {@link Graph.made} only a name answered. */
|
|
128
|
+
readonly guessedMade: readonly Counts[];
|
|
129
|
+
/** The references the graph was built from, in the order given. */
|
|
130
|
+
readonly references: readonly Reference[];
|
|
131
|
+
/** Per reference, the configurations that make it, in the run's order. */
|
|
132
|
+
readonly configurations: readonly (readonly string[])[];
|
|
121
133
|
/** Per declaration, the position of its container, or {@link OUTSIDE} for a file. */
|
|
122
134
|
readonly parent: readonly number[];
|
|
123
135
|
/** Per declaration, whether a test file holds it. */
|
|
@@ -137,17 +149,44 @@ export interface Graph {
|
|
|
137
149
|
/** Every root naming a declaration the inventory holds, in the order the root set gives them. */
|
|
138
150
|
readonly rooted: readonly Rooted[];
|
|
139
151
|
/**
|
|
140
|
-
* Every reference a loaded consumer
|
|
141
|
-
* inventory holds, in the order given. Such
|
|
142
|
-
* sweep holds what it names live under both relations
|
|
152
|
+
* Every reference a loaded consumer, an unheld file or a declaration no configuration
|
|
153
|
+
* judges makes to a declaration the inventory holds, in the order given. Such code is a
|
|
154
|
+
* caller the analysis read, so the sweep holds what it names live under both relations
|
|
155
|
+
* and reaches on from there.
|
|
143
156
|
*/
|
|
144
157
|
readonly consumed: readonly Edge[];
|
|
145
158
|
/** The position of the declaration one identifier names, or {@link OUTSIDE}. */
|
|
146
159
|
at(id: string): number;
|
|
147
160
|
}
|
|
148
161
|
|
|
162
|
+
/** What the run says of a graph's references beyond the references themselves. */
|
|
163
|
+
interface Sources {
|
|
164
|
+
/**
|
|
165
|
+
* The declarations a configuration holds only for their references, judged by none: each
|
|
166
|
+
* reference one makes enters as a consumer's does, since deleting what it names breaks
|
|
167
|
+
* that configuration's compile (matrix.5).
|
|
168
|
+
*/
|
|
169
|
+
readonly unjudged?: ReadonlySet<string>;
|
|
170
|
+
/** Per reference, in the order given, the configurations that make it. */
|
|
171
|
+
readonly configurations?: readonly (readonly string[])[];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Adds one reference's count to the counts of the declaration it names. */
|
|
175
|
+
function count(
|
|
176
|
+
counts: { production: number; test: number; consumerTest: number },
|
|
177
|
+
test: boolean,
|
|
178
|
+
consumer: boolean,
|
|
179
|
+
): void {
|
|
180
|
+
if (test) {
|
|
181
|
+
counts.test += 1;
|
|
182
|
+
counts.consumerTest += consumer ? 1 : 0;
|
|
183
|
+
} else {
|
|
184
|
+
counts.production += 1;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
149
188
|
/**
|
|
150
|
-
* Indexes
|
|
189
|
+
* Indexes the run's inventory, references and roots. `testFiles` are the paths the
|
|
151
190
|
* reference pass classified as test files, so a declaration one holds is a test
|
|
152
191
|
* declaration. A reference to a declaration the inventory does not hold is dropped; one
|
|
153
192
|
* from such a declaration still counts toward its target, because reference counting
|
|
@@ -160,42 +199,48 @@ export function graphOf(
|
|
|
160
199
|
testFiles: readonly string[],
|
|
161
200
|
supportFiles: readonly string[] = [],
|
|
162
201
|
ambient: ReadonlySet<string> = new Set(),
|
|
202
|
+
sources: Sources = {},
|
|
163
203
|
): Graph {
|
|
164
204
|
const supports = new Set(supportFiles);
|
|
165
205
|
const index = new Map(symbols.map((symbol, at) => [symbol.id, at]));
|
|
166
206
|
const at = (id: string): number => index.get(id) ?? OUTSIDE;
|
|
167
207
|
const tests = new Set(testFiles);
|
|
208
|
+
const unjudged = sources.unjudged ?? new Set<string>();
|
|
168
209
|
|
|
169
210
|
const out: Edge[][] = symbols.map(() => []);
|
|
170
211
|
const made = symbols.map(() => ({ production: 0, test: 0, consumerTest: 0 }));
|
|
212
|
+
const guessedMade = symbols.map(() => ({ production: 0, test: 0, consumerTest: 0 }));
|
|
171
213
|
const consumed: Edge[] = [];
|
|
172
|
-
|
|
214
|
+
references.forEach((reference, place) => {
|
|
173
215
|
const to = at(reference.to);
|
|
174
216
|
if (to === OUTSIDE) {
|
|
175
|
-
|
|
217
|
+
return;
|
|
176
218
|
}
|
|
177
219
|
const test = madeByTestCode(reference, supports);
|
|
220
|
+
const consumer = reference.consumer !== undefined;
|
|
221
|
+
const guessed = reference.guessed === true;
|
|
178
222
|
const counts = made[to];
|
|
179
223
|
if (counts !== undefined) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
224
|
+
count(counts, test, consumer);
|
|
225
|
+
}
|
|
226
|
+
const guesses = guessedMade[to];
|
|
227
|
+
if (guessed && guesses !== undefined) {
|
|
228
|
+
count(guesses, test, consumer);
|
|
186
229
|
}
|
|
187
230
|
const edge = {
|
|
188
231
|
to,
|
|
232
|
+
reference: place,
|
|
189
233
|
test,
|
|
234
|
+
guessed,
|
|
190
235
|
evaluation: reference.use === "evaluation",
|
|
191
|
-
consumer
|
|
236
|
+
consumer,
|
|
192
237
|
};
|
|
193
|
-
if (
|
|
238
|
+
if (consumer || reference.unheld === true || unjudged.has(reference.from)) {
|
|
194
239
|
consumed.push(edge);
|
|
195
|
-
|
|
240
|
+
return;
|
|
196
241
|
}
|
|
197
242
|
out[at(reference.from)]?.push(edge);
|
|
198
|
-
}
|
|
243
|
+
});
|
|
199
244
|
|
|
200
245
|
const parent = symbols.map((symbol) => at(symbol.parent));
|
|
201
246
|
const exportsOf: number[][] = symbols.map(() => []);
|
|
@@ -210,7 +255,7 @@ export function graphOf(
|
|
|
210
255
|
for (const root of roots) {
|
|
211
256
|
const position = at(root.id);
|
|
212
257
|
if (position !== OUTSIDE) {
|
|
213
|
-
rooted.push({ at: position, kind: root.kind });
|
|
258
|
+
rooted.push({ at: position, kind: root.kind, source: root.source });
|
|
214
259
|
}
|
|
215
260
|
}
|
|
216
261
|
|
|
@@ -218,6 +263,9 @@ export function graphOf(
|
|
|
218
263
|
symbols,
|
|
219
264
|
out,
|
|
220
265
|
made,
|
|
266
|
+
guessedMade,
|
|
267
|
+
references,
|
|
268
|
+
configurations: sources.configurations ?? references.map(() => []),
|
|
221
269
|
parent,
|
|
222
270
|
test: symbols.map((symbol) => tests.has(symbol.position.path)),
|
|
223
271
|
support: symbols.map((symbol) => supports.has(symbol.position.path)),
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One run's projects as one matrix: every declaration any project judges, keyed by its
|
|
3
|
+
* position and carrying the configurations that hold it, and one graph over every
|
|
4
|
+
* configuration's references and roots, which the sweep judges once (matrix.4, matrix.5).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { graphOf, swept, type Graph } from "./graph.ts";
|
|
8
|
+
import type { InventorySymbol } from "../inventory.ts";
|
|
9
|
+
import { byPosition } from "../position.ts";
|
|
10
|
+
import type { Reference } from "../references.ts";
|
|
11
|
+
import type { Root } from "../roots/root-set.ts";
|
|
12
|
+
|
|
13
|
+
/** What the passes answered for one configuration of the run. */
|
|
14
|
+
export interface Configured {
|
|
15
|
+
/** The name discovery gives the configuration's project. */
|
|
16
|
+
readonly configuration: string;
|
|
17
|
+
readonly symbols: readonly InventorySymbol[];
|
|
18
|
+
readonly references: readonly Reference[];
|
|
19
|
+
readonly roots: readonly Root[];
|
|
20
|
+
/** The paths the reference pass classified as test files. */
|
|
21
|
+
readonly testFiles: readonly string[];
|
|
22
|
+
/** The paths of the test-support files, which only test code imports. */
|
|
23
|
+
readonly supportFiles?: readonly string[] | undefined;
|
|
24
|
+
/** The paths of the files only tests reach that are no test-support file. */
|
|
25
|
+
readonly testUsedFiles?: readonly string[] | undefined;
|
|
26
|
+
/** The ids of the ambient declarations, which describe code the target does not implement. */
|
|
27
|
+
readonly ambient?: ReadonlySet<string> | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* The paths of the workspace-package files the configuration holds but does not
|
|
30
|
+
* compile itself. Their declarations count in its graph and its verdict on them is no
|
|
31
|
+
* finding.
|
|
32
|
+
*/
|
|
33
|
+
readonly referenceOnly?: readonly string[] | undefined;
|
|
34
|
+
/** Each declaration's dependents, by id, as the configuration's inventory states them. */
|
|
35
|
+
readonly dependents?: ReadonlyMap<string, readonly string[]> | undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** The run's configurations, indexed for the sweep. */
|
|
39
|
+
export interface Matrix {
|
|
40
|
+
/** The configurations, in the run's order. */
|
|
41
|
+
readonly configurations: readonly string[];
|
|
42
|
+
/**
|
|
43
|
+
* The graph over every configuration at once: every declaration a configuration
|
|
44
|
+
* holds in a file it does not hold only for its references, once per position and in
|
|
45
|
+
* site order, and every reference any configuration makes, a reference several
|
|
46
|
+
* configurations make at one position counted once and tagged with each of them.
|
|
47
|
+
*/
|
|
48
|
+
readonly union: Graph;
|
|
49
|
+
/**
|
|
50
|
+
* Per declaration of {@link Matrix.union}, at its position there, the configurations
|
|
51
|
+
* whose project holds it, in the run's order.
|
|
52
|
+
*/
|
|
53
|
+
readonly heldIn: readonly (readonly string[])[];
|
|
54
|
+
/**
|
|
55
|
+
* Per declaration of {@link Matrix.union}, whether a file only tests reach that is no
|
|
56
|
+
* test-support file holds it in some configuration: production code only tests use.
|
|
57
|
+
*/
|
|
58
|
+
readonly testUsed: readonly boolean[];
|
|
59
|
+
/** Each declaration's dependents, by id, as the first configuration holding it states them. */
|
|
60
|
+
readonly dependents: ReadonlyMap<string, readonly string[]>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* What makes one reference one reference, whichever configuration made it. A consumer's
|
|
65
|
+
* position is rendered against its own root, so the consumer is part of what it names.
|
|
66
|
+
*/
|
|
67
|
+
export function referenceKey(reference: Reference): string {
|
|
68
|
+
const { path, line, column } = reference.position;
|
|
69
|
+
return [
|
|
70
|
+
reference.consumer ?? "",
|
|
71
|
+
reference.from,
|
|
72
|
+
reference.to,
|
|
73
|
+
path,
|
|
74
|
+
String(line),
|
|
75
|
+
String(column),
|
|
76
|
+
reference.use,
|
|
77
|
+
].join("\u0000");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Indexes what the passes answered for each configuration of one run, in the run's order.
|
|
82
|
+
* A declaration is keyed by its rendered position, with the fields of the first
|
|
83
|
+
* configuration that judges it; one in a file every holder holds only for its references
|
|
84
|
+
* is judged by none and is not in the union, and a reference it makes enters the union as a
|
|
85
|
+
* consumer's does. One reference at one position of a file several configurations compile
|
|
86
|
+
* is one reference; two one configuration makes are two.
|
|
87
|
+
*/
|
|
88
|
+
export function matrixOf(per: readonly Configured[]): Matrix {
|
|
89
|
+
const symbols: InventorySymbol[] = [];
|
|
90
|
+
const holders = new Map<string, string[]>();
|
|
91
|
+
const references: Reference[] = [];
|
|
92
|
+
const makers: string[][] = [];
|
|
93
|
+
const firstSeen = new Map<string, { readonly index: number; readonly places: number[] }>();
|
|
94
|
+
const judged = new Set<string>();
|
|
95
|
+
const unjudged = new Set<string>();
|
|
96
|
+
const dependents = new Map<string, readonly string[]>();
|
|
97
|
+
per.forEach((one, index) => {
|
|
98
|
+
for (const [id, after] of one.dependents ?? []) {
|
|
99
|
+
if (!dependents.has(id)) {
|
|
100
|
+
dependents.set(id, after);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const referenceOnly = new Set(one.referenceOnly);
|
|
104
|
+
for (const symbol of one.symbols) {
|
|
105
|
+
const held = holders.get(symbol.id);
|
|
106
|
+
if (held === undefined) {
|
|
107
|
+
holders.set(symbol.id, [one.configuration]);
|
|
108
|
+
} else if (!held.includes(one.configuration)) {
|
|
109
|
+
held.push(one.configuration);
|
|
110
|
+
}
|
|
111
|
+
if (referenceOnly.has(symbol.position.path)) {
|
|
112
|
+
if (swept(symbol.kind)) {
|
|
113
|
+
unjudged.add(symbol.id);
|
|
114
|
+
}
|
|
115
|
+
} else if (!judged.has(symbol.id)) {
|
|
116
|
+
judged.add(symbol.id);
|
|
117
|
+
symbols.push(symbol);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
for (const reference of one.references) {
|
|
121
|
+
const key = referenceKey(reference);
|
|
122
|
+
const seen = firstSeen.get(key);
|
|
123
|
+
if (seen !== undefined && seen.index !== index) {
|
|
124
|
+
for (const place of seen.places) {
|
|
125
|
+
const making = makers[place];
|
|
126
|
+
if (making !== undefined && !making.includes(one.configuration)) {
|
|
127
|
+
making.push(one.configuration);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const places = seen?.places ?? [];
|
|
133
|
+
if (seen === undefined) {
|
|
134
|
+
firstSeen.set(key, { index, places });
|
|
135
|
+
}
|
|
136
|
+
places.push(references.length);
|
|
137
|
+
references.push(reference);
|
|
138
|
+
makers.push([one.configuration]);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
for (const id of judged) {
|
|
142
|
+
unjudged.delete(id);
|
|
143
|
+
}
|
|
144
|
+
symbols.sort((a, b) => byPosition(a.position, b.position));
|
|
145
|
+
const testUsed = new Set(per.flatMap((one) => one.testUsedFiles ?? []));
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
configurations: per.map((one) => one.configuration),
|
|
149
|
+
union: graphOf(
|
|
150
|
+
symbols,
|
|
151
|
+
references,
|
|
152
|
+
per.flatMap((one) => one.roots),
|
|
153
|
+
[...new Set(per.flatMap((one) => one.testFiles))],
|
|
154
|
+
[...new Set(per.flatMap((one) => one.supportFiles ?? []))],
|
|
155
|
+
new Set(per.flatMap((one) => [...(one.ambient ?? [])])),
|
|
156
|
+
{ unjudged, configurations: makers },
|
|
157
|
+
),
|
|
158
|
+
heldIn: symbols.map((symbol) => holders.get(symbol.id) ?? []),
|
|
159
|
+
testUsed: symbols.map((symbol) => testUsed.has(symbol.position.path)),
|
|
160
|
+
dependents,
|
|
161
|
+
};
|
|
162
|
+
}
|