@cplieger/deadset-ts 5.2.3 → 6.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 +1 -1
- package/dist/src/analysis.js +28 -5
- package/dist/src/augmentations.js +50 -0
- package/dist/src/components.js +15 -15
- package/dist/src/conformance-record.js +2 -2
- package/dist/src/convention-rows.js +17 -2
- package/dist/src/conventions.js +82 -1
- package/dist/src/dependencies.js +42 -26
- package/dist/src/entry-points.js +28 -14
- package/dist/src/exempt.js +1 -0
- package/dist/src/findings/completion.js +9 -4
- package/dist/src/findings/dependencies-and-module-machinery.js +2 -2
- package/dist/src/findings/emitters.js +64 -25
- package/dist/src/findings/interfaces.js +4 -3
- package/dist/src/findings/intra-function.js +15 -2
- package/dist/src/findings/reads-and-writes.js +3 -2
- package/dist/src/findings/unused-declarations.js +13 -1
- package/dist/src/findings/visibility-narrowing.js +2 -1
- package/dist/src/findings-pass.js +1 -0
- package/dist/src/graph.js +6 -6
- package/dist/src/implementations.js +1 -1
- package/dist/src/indexed-access.js +215 -0
- package/dist/src/interface-satisfaction.js +85 -23
- package/dist/src/intra-function-parts.js +139 -30
- package/dist/src/inventory.js +6 -1
- package/dist/src/kinds.js +13 -12
- package/dist/src/matrix.js +34 -9
- package/dist/src/module-declarations.js +180 -0
- package/dist/src/outside-tests.js +17 -15
- package/dist/src/overrides.js +70 -9
- package/dist/src/query.js +7 -0
- package/dist/src/ref.js +9 -0
- package/dist/src/references.js +6 -0
- package/dist/src/report.js +6 -1
- package/dist/src/reporters/sarif.js +6 -2
- package/dist/src/reporters/text.js +23 -3
- package/dist/src/roots.js +50 -10
- package/dist/src/suppress-file.js +3 -5
- package/dist/src/version.js +1 -1
- package/dist/src/workflow-steps.js +82 -0
- package/dist/src/workspace-programs.js +6 -31
- package/jsr.json +1 -1
- package/package.json +8 -2
- package/src/analysis.ts +40 -4
- package/src/augmentations.ts +72 -0
- package/src/components.ts +15 -13
- package/src/conformance-record.ts +2 -2
- package/src/convention-rows.ts +25 -3
- package/src/conventions.ts +106 -2
- package/src/dependencies.ts +51 -31
- package/src/entry-points.ts +32 -19
- package/src/exempt.ts +4 -1
- package/src/findings/completion.ts +11 -4
- package/src/findings/dependencies-and-module-machinery.ts +2 -2
- package/src/findings/emitters.ts +89 -27
- package/src/findings/interfaces.ts +7 -6
- package/src/findings/intra-function.ts +20 -2
- package/src/findings/reads-and-writes.ts +7 -2
- package/src/findings/unused-declarations.ts +16 -1
- package/src/findings/visibility-narrowing.ts +2 -1
- package/src/findings-pass.ts +4 -1
- package/src/graph.ts +6 -6
- package/src/implementations.ts +1 -1
- package/src/indexed-access.ts +295 -0
- package/src/interface-satisfaction.ts +118 -27
- package/src/intra-function-parts.ts +196 -35
- package/src/inventory.ts +8 -0
- package/src/kinds.ts +14 -12
- package/src/matrix.ts +51 -8
- package/src/module-declarations.ts +241 -0
- package/src/outside-tests.ts +18 -16
- package/src/overrides.ts +80 -14
- package/src/query.ts +28 -0
- package/src/ref.ts +10 -0
- package/src/references.ts +6 -0
- package/src/report.ts +11 -1
- package/src/reporters/sarif.ts +9 -2
- package/src/reporters/text.ts +25 -3
- package/src/roots.ts +56 -7
- package/src/suppress-file.ts +3 -6
- package/src/sweep.ts +12 -0
- package/src/version.ts +1 -1
- package/src/workflow-steps.ts +104 -0
- package/src/workspace-programs.ts +6 -35
package/src/findings/emitters.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { Confidence } from "../config.ts";
|
|
1
2
|
import { evaluateEdges, type EdgeEvaluation } from "../edges.ts";
|
|
2
3
|
import type { CompletedFinding } from "../finding.ts";
|
|
3
4
|
import { liveFilesAt } from "../matrix.ts";
|
|
5
|
+
import { declarationKey } from "../ref.ts";
|
|
4
6
|
import { ledgerOf, type Dials, type Ledger, type SuppressionRecord } from "../suppress.ts";
|
|
5
7
|
import { insideSkipped } from "../type-errors.ts";
|
|
6
8
|
import { completed, dialsOf, reportable } from "./completion.ts";
|
|
@@ -27,10 +29,10 @@ export const EMITTERS: Emitters = new Map<string, Emitter>([
|
|
|
27
29
|
]);
|
|
28
30
|
|
|
29
31
|
/**
|
|
30
|
-
* The
|
|
31
|
-
*
|
|
32
|
+
* The keys of every declaration an unanswered question could have kept live, which the run
|
|
33
|
+
* reports nothing about: a finding about one would be computed from the gap.
|
|
32
34
|
*/
|
|
33
|
-
function
|
|
35
|
+
function heldKeys(input: EmitterInput): ReadonlySet<string> {
|
|
34
36
|
const held = new Set(input.swept.heldByUnanswered);
|
|
35
37
|
if (held.size === 0) {
|
|
36
38
|
return held;
|
|
@@ -38,16 +40,16 @@ function heldRefs(input: EmitterInput): ReadonlySet<string> {
|
|
|
38
40
|
return new Set(
|
|
39
41
|
input.swept.matrix.union.symbols
|
|
40
42
|
.filter((symbol) => held.has(symbol.id))
|
|
41
|
-
.map((symbol) => symbol.ref),
|
|
43
|
+
.map((symbol) => declarationKey(symbol.ref, symbol.position.path)),
|
|
42
44
|
);
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
/**
|
|
46
|
-
* The
|
|
48
|
+
* The keys of every top-level declaration of a live component file. The file's
|
|
47
49
|
* markup may use each of them, which no analysis of the markup can rule out, so the run
|
|
48
50
|
* reports nothing about one while the file is live.
|
|
49
51
|
*/
|
|
50
|
-
function
|
|
52
|
+
function markupKeys(input: EmitterInput): ReadonlySet<string> {
|
|
51
53
|
const { swept } = input;
|
|
52
54
|
if (swept.componentFiles.length === 0) {
|
|
53
55
|
return new Set();
|
|
@@ -60,7 +62,7 @@ function markupRefs(input: EmitterInput): ReadonlySet<string> {
|
|
|
60
62
|
return new Set(
|
|
61
63
|
swept.matrix.union.symbols
|
|
62
64
|
.filter((symbol) => live.has(symbol.parent))
|
|
63
|
-
.map((symbol) => symbol.ref),
|
|
65
|
+
.map((symbol) => declarationKey(symbol.ref, symbol.position.path)),
|
|
64
66
|
);
|
|
65
67
|
}
|
|
66
68
|
|
|
@@ -82,8 +84,13 @@ export interface DecidedFindings {
|
|
|
82
84
|
readonly ledger: Ledger<CompletedFinding>;
|
|
83
85
|
/** Every finding a record could name: the run's with no mark, then the run's as swept. */
|
|
84
86
|
readonly would: readonly CompletedFinding[];
|
|
87
|
+
/** The findings the minimum confidence withheld, by confidence. */
|
|
88
|
+
readonly withheld: Withheld;
|
|
85
89
|
}
|
|
86
90
|
|
|
91
|
+
/** How many findings the minimum confidence withheld at each confidence. */
|
|
92
|
+
export type Withheld = Readonly<Record<Confidence, number>>;
|
|
93
|
+
|
|
87
94
|
/** Every family's findings over one sweep, and those of any further emitter, completed by one step. */
|
|
88
95
|
function completedOver(
|
|
89
96
|
input: EmitterInput,
|
|
@@ -116,12 +123,12 @@ function dialsOver(
|
|
|
116
123
|
}
|
|
117
124
|
|
|
118
125
|
/**
|
|
119
|
-
* Every finding of one run swept under its suppressions' marks, decided in one pass:
|
|
120
|
-
* family's findings completed by one step, so every component number is minted once,
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
126
|
+
* Every finding of one run swept under its suppressions' marks, decided in one pass: each
|
|
127
|
+
* family's findings completed by one step, so every component number is minted once, the
|
|
128
|
+
* finding each record claims withheld, the rest withheld by the dials, so a root one family
|
|
129
|
+
* reports withholds what another reports in its component, and every declared edge
|
|
130
|
+
* evaluated over what remains, so a pending finding is in its evaluation alone. What the
|
|
131
|
+
* minimum confidence withheld is what the same decision at the lowest minimum adds.
|
|
125
132
|
*/
|
|
126
133
|
export function decidedFindings(
|
|
127
134
|
input: EmitterInput,
|
|
@@ -131,26 +138,81 @@ export function decidedFindings(
|
|
|
131
138
|
const unmarked =
|
|
132
139
|
suppressed.records.length === 0 ? [] : completedOver(suppressed.unmarked, fallenMembers);
|
|
133
140
|
const would = [...unmarked, ...found];
|
|
141
|
+
const decided = dialledOver(input, suppressed, found, unmarked);
|
|
142
|
+
const shown =
|
|
143
|
+
input.config.analysis.minConfidence === "possible"
|
|
144
|
+
? decided
|
|
145
|
+
: dialledOver(
|
|
146
|
+
atPossible(input),
|
|
147
|
+
{
|
|
148
|
+
unmarked: atPossible(suppressed.unmarked),
|
|
149
|
+
records: suppressed.records,
|
|
150
|
+
},
|
|
151
|
+
found,
|
|
152
|
+
unmarked,
|
|
153
|
+
);
|
|
154
|
+
return { ...decided, would, withheld: withheldOf(decided.findings, shown.findings) };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** The run's input with the minimum confidence at its lowest, which withholds nothing. */
|
|
158
|
+
function atPossible(input: EmitterInput): EmitterInput {
|
|
159
|
+
const { config } = input;
|
|
160
|
+
return {
|
|
161
|
+
...input,
|
|
162
|
+
config: { ...config, analysis: { ...config.analysis, minConfidence: "possible" } },
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* The run's completed findings decided under its dials: the finding each record claims
|
|
168
|
+
* withheld, the rest withheld by the dials, and every declared edge evaluated over what
|
|
169
|
+
* remains.
|
|
170
|
+
*/
|
|
171
|
+
function dialledOver(
|
|
172
|
+
input: EmitterInput,
|
|
173
|
+
suppressed: Suppressed,
|
|
174
|
+
found: readonly CompletedFinding[],
|
|
175
|
+
unmarked: readonly CompletedFinding[],
|
|
176
|
+
): Omit<DecidedFindings, "would" | "withheld"> {
|
|
177
|
+
const would = [...unmarked, ...found];
|
|
134
178
|
const ledger = ledgerOf(suppressed.records, would, dialsOver(input, found, suppressed, unmarked));
|
|
135
|
-
const unanswered =
|
|
136
|
-
const markup =
|
|
179
|
+
const unanswered = heldKeys(input);
|
|
180
|
+
const markup = markupKeys(input);
|
|
137
181
|
const kept = reportable(
|
|
138
182
|
input,
|
|
139
|
-
found.filter(
|
|
140
|
-
(finding)
|
|
183
|
+
found.filter((finding) => {
|
|
184
|
+
const key = declarationKey(finding.symbol.ref, finding.position.path);
|
|
185
|
+
return (
|
|
141
186
|
!ledger.withheld(finding) &&
|
|
142
|
-
!unanswered.has(
|
|
143
|
-
!markup.has(
|
|
144
|
-
!insideSkipped(input.swept.skipped, finding.position)
|
|
145
|
-
|
|
187
|
+
!unanswered.has(key) &&
|
|
188
|
+
!markup.has(key) &&
|
|
189
|
+
!insideSkipped(input.swept.skipped, finding.position)
|
|
190
|
+
);
|
|
191
|
+
}),
|
|
146
192
|
);
|
|
147
193
|
const evaluated = evaluateEdges(kept, input.boundary.edges, input.swept.matrix.union.symbols);
|
|
148
|
-
return {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
194
|
+
return { findings: evaluated.findings, evaluations: evaluated.evaluations, ledger };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** The findings the lowest minimum would report that the run does not, counted by confidence. */
|
|
198
|
+
function withheldOf(
|
|
199
|
+
reported: readonly CompletedFinding[],
|
|
200
|
+
shown: readonly CompletedFinding[],
|
|
201
|
+
): Withheld {
|
|
202
|
+
const kept = new Set(reported.map(findingKey));
|
|
203
|
+
const counts = { certain: 0, probable: 0, possible: 0 };
|
|
204
|
+
for (const finding of shown) {
|
|
205
|
+
if (!kept.has(findingKey(finding))) {
|
|
206
|
+
counts[finding.confidence] += 1;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return counts;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** A finding's identity across two decisions of one run: its position, code and subject. */
|
|
213
|
+
function findingKey(finding: CompletedFinding): string {
|
|
214
|
+
const { path, line, column } = finding.position;
|
|
215
|
+
return JSON.stringify([path, line, column, finding.code, finding.symbol.ref]);
|
|
154
216
|
}
|
|
155
217
|
|
|
156
218
|
/** Every finding the run reports, each family's in table order. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Finding, FindingPosition, PositionedSymbol } from "../finding.ts";
|
|
2
2
|
import type { InventorySymbol } from "../inventory.ts";
|
|
3
3
|
import { byPosition } from "../position.ts";
|
|
4
|
-
import type {
|
|
4
|
+
import type { MatrixCandidate } from "../matrix.ts";
|
|
5
5
|
import type { Emitter, EmitterInput } from "./emitter.ts";
|
|
6
6
|
|
|
7
7
|
const UNUSED_INTERFACE = "DS1201";
|
|
@@ -31,7 +31,7 @@ function compare(a: string, b: string): number {
|
|
|
31
31
|
function indexed(input: EmitterInput): {
|
|
32
32
|
readonly symbolOf: (id: string) => InventorySymbol | undefined;
|
|
33
33
|
readonly inTest: (id: string) => boolean;
|
|
34
|
-
readonly candidateOf: (id: string) =>
|
|
34
|
+
readonly candidateOf: (id: string) => MatrixCandidate | undefined;
|
|
35
35
|
readonly implementationsOf: (id: string) => readonly PositionedSymbol[];
|
|
36
36
|
} {
|
|
37
37
|
const union = input.swept.matrix.union;
|
|
@@ -79,15 +79,16 @@ function findingOf(
|
|
|
79
79
|
* The findings of the interfaces family, `DS1200` to `DS1299`: `DS1201`, a dead
|
|
80
80
|
* interface, whose members fall with it unreported; and `DS1203`, a method of a live
|
|
81
81
|
* interface no reference names, unless it is a marker, a method whose every
|
|
82
|
-
* implementation has an empty body. A test file's declaration is neither kind's
|
|
83
|
-
*
|
|
82
|
+
* implementation has an empty body. A test file's declaration is neither kind's subject,
|
|
83
|
+
* because a production sweep counts none of the references it can have, save an interface
|
|
84
|
+
* the run that counts test references does not reach, which is `DS1201`'s.
|
|
84
85
|
*/
|
|
85
86
|
export const interfaces: Emitter = (input) => {
|
|
86
87
|
const run = indexed(input);
|
|
87
88
|
const found: Finding[] = [];
|
|
88
89
|
for (const candidate of input.swept.sweep.candidates) {
|
|
89
90
|
const symbol = run.symbolOf(candidate.id);
|
|
90
|
-
if (symbol === undefined || run.inTest(candidate.id)) {
|
|
91
|
+
if (symbol === undefined || (run.inTest(candidate.id) && !candidate.unreferencedTest)) {
|
|
91
92
|
continue;
|
|
92
93
|
}
|
|
93
94
|
if (symbol.kind === "interface") {
|
|
@@ -122,7 +123,7 @@ export const interfaces: Emitter = (input) => {
|
|
|
122
123
|
};
|
|
123
124
|
|
|
124
125
|
/** What one unused interface's finding says, by the references the sweep counted. */
|
|
125
|
-
function unusedMessage(candidate:
|
|
126
|
+
function unusedMessage(candidate: MatrixCandidate): string {
|
|
126
127
|
if (candidate.relation === "reachability") {
|
|
127
128
|
return UNUSED_DEAD_MESSAGE;
|
|
128
129
|
}
|
|
@@ -19,6 +19,24 @@ function overlapClause(overlap: readonly string[]): string {
|
|
|
19
19
|
: `also reported by ${spelled(overlap)}`;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Whether one parameter's name is reported: every unread one of a function whose
|
|
24
|
+
* signature is free, and of a function used as a value only one no read parameter follows,
|
|
25
|
+
* because a caller may pass such a function more arguments than it declares.
|
|
26
|
+
*/
|
|
27
|
+
function parameterReported(
|
|
28
|
+
part: PartFact,
|
|
29
|
+
free: ReadonlySet<string>,
|
|
30
|
+
freeButValued: ReadonlySet<string>,
|
|
31
|
+
): boolean {
|
|
32
|
+
const place = part.parameter;
|
|
33
|
+
if (place?.inValue !== true && free.has(part.declaration)) {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
const valued = place?.inValue === true || freeButValued.has(part.declaration);
|
|
37
|
+
return valued && place?.beforeARead !== true;
|
|
38
|
+
}
|
|
39
|
+
|
|
22
40
|
/**
|
|
23
41
|
* Whether one part is reported. A parameter needs its function's signature free to
|
|
24
42
|
* change; a result needs that, every call in the loaded program to discard it, and every
|
|
@@ -34,10 +52,10 @@ function reported(
|
|
|
34
52
|
if (part.used) {
|
|
35
53
|
return false;
|
|
36
54
|
}
|
|
37
|
-
const { free, discardedEverywhere } = input.intraFunction;
|
|
55
|
+
const { free, freeButValued, discardedEverywhere } = input.intraFunction;
|
|
38
56
|
switch (part.code) {
|
|
39
57
|
case UNUSED_PARAMETER:
|
|
40
|
-
return
|
|
58
|
+
return parameterReported(part, free, freeButValued);
|
|
41
59
|
case UNUSED_RESULT:
|
|
42
60
|
return (
|
|
43
61
|
free.has(part.declaration) &&
|
|
@@ -2,6 +2,7 @@ import type { Finding, FindingDetails, FindingPosition } from "../finding.ts";
|
|
|
2
2
|
import type { InventorySymbol } from "../inventory.ts";
|
|
3
3
|
import type { MatrixCandidate } from "../matrix.ts";
|
|
4
4
|
import { positionKey } from "../position.ts";
|
|
5
|
+
import { declarationKey } from "../ref.ts";
|
|
5
6
|
import type { Emitter, EmitterInput } from "./emitter.ts";
|
|
6
7
|
|
|
7
8
|
/** The codes of this family. */
|
|
@@ -242,12 +243,16 @@ function unusedTypeParameters(run: Run): readonly Finding[] {
|
|
|
242
243
|
}
|
|
243
244
|
|
|
244
245
|
/**
|
|
245
|
-
* The
|
|
246
|
+
* The keys of the declarations the write-only kind reports, whatever the
|
|
246
247
|
* severity: a narrowing kind yields each to it, since deleting the declaration
|
|
247
248
|
* supersedes narrowing it.
|
|
248
249
|
*/
|
|
249
250
|
export function writeOnlyDeclarations(input: EmitterInput): ReadonlySet<string> {
|
|
250
|
-
return new Set(
|
|
251
|
+
return new Set(
|
|
252
|
+
writeOnlySymbols(indexed(input)).map((finding) =>
|
|
253
|
+
declarationKey(finding.symbol.ref, finding.position.path),
|
|
254
|
+
),
|
|
255
|
+
);
|
|
251
256
|
}
|
|
252
257
|
|
|
253
258
|
/**
|
|
@@ -71,9 +71,16 @@ function codeOf(judged: Judged): string | undefined {
|
|
|
71
71
|
if (candidate.testOfDeadCode) {
|
|
72
72
|
return "DS1005";
|
|
73
73
|
}
|
|
74
|
-
if (
|
|
74
|
+
if (
|
|
75
|
+
(judged.test && !candidate.unreferencedTest) ||
|
|
76
|
+
judged.containerDead ||
|
|
77
|
+
NOT_THIS_FAMILY.has(symbol.kind)
|
|
78
|
+
) {
|
|
75
79
|
return undefined;
|
|
76
80
|
}
|
|
81
|
+
if (candidate.unreferencedTest) {
|
|
82
|
+
return unreferencedCode(symbol);
|
|
83
|
+
}
|
|
77
84
|
const referenced = candidate.productionRefs + candidate.testRefs > 0;
|
|
78
85
|
if (symbol.kind === "enum-member" && !referenced) {
|
|
79
86
|
return undefined;
|
|
@@ -96,6 +103,14 @@ function codeOf(judged: Judged): string | undefined {
|
|
|
96
103
|
return judged.unreachable ? undefined : "DS1001";
|
|
97
104
|
}
|
|
98
105
|
|
|
106
|
+
/** The code a declaration nothing references carries, by its shape and its visibility. */
|
|
107
|
+
function unreferencedCode(symbol: InventorySymbol): string {
|
|
108
|
+
if (MEMBERS.has(symbol.kind)) {
|
|
109
|
+
return "DS1003";
|
|
110
|
+
}
|
|
111
|
+
return symbol.exported ? "DS1001" : "DS1002";
|
|
112
|
+
}
|
|
113
|
+
|
|
99
114
|
/** The visibility a member's message names, where it narrows the member's reach. */
|
|
100
115
|
function visibilityWord(symbol: InventorySymbol): string {
|
|
101
116
|
switch (symbol.visibility) {
|
|
@@ -2,6 +2,7 @@ import type { Finding } from "../finding.ts";
|
|
|
2
2
|
import { OUTSIDE, type Graph } from "../graph.ts";
|
|
3
3
|
import type { InventorySymbol, SymbolKind } from "../inventory.ts";
|
|
4
4
|
import type { MatrixCandidate } from "../matrix.ts";
|
|
5
|
+
import { declarationKey } from "../ref.ts";
|
|
5
6
|
import type { RootKind } from "../roots.ts";
|
|
6
7
|
import type { Emitter, EmitterInput } from "./emitter.ts";
|
|
7
8
|
import { writeOnlyDeclarations } from "./reads-and-writes.ts";
|
|
@@ -159,7 +160,7 @@ function narrowing(
|
|
|
159
160
|
const file = facts.fileOf[at] ?? OUTSIDE;
|
|
160
161
|
if (
|
|
161
162
|
!topLevelExport(facts, at, symbol) ||
|
|
162
|
-
world.writeOnly.has(symbol.ref) ||
|
|
163
|
+
world.writeOnly.has(declarationKey(symbol.ref, symbol.position.path)) ||
|
|
163
164
|
symbol.kind === "export-alias" ||
|
|
164
165
|
entersByName(facts.rootedBy[at]) ||
|
|
165
166
|
entersByName(facts.rootedBy[file]) ||
|
package/src/findings-pass.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type { AnalysisInputs } from "./analysis.ts";
|
|
|
8
8
|
import { fails, type Config } from "./config.ts";
|
|
9
9
|
import type { EdgeEvaluation } from "./edges.ts";
|
|
10
10
|
import type { CompletedFinding, Finding } from "./finding.ts";
|
|
11
|
-
import { decidedFindings } from "./findings/emitters.ts";
|
|
11
|
+
import { decidedFindings, type Withheld } from "./findings/emitters.ts";
|
|
12
12
|
import {
|
|
13
13
|
fileRefsOf,
|
|
14
14
|
staleSuppressions,
|
|
@@ -26,6 +26,8 @@ export interface PassTotals {
|
|
|
26
26
|
readonly staleSuppressions: number;
|
|
27
27
|
/** The evaluations that hold a finding pending. */
|
|
28
28
|
readonly pending: number;
|
|
29
|
+
/** The findings the minimum confidence withheld, by confidence. */
|
|
30
|
+
readonly withheld: Withheld;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
/** What the pass answers about one run. */
|
|
@@ -98,6 +100,7 @@ export function findingsPass(inputs: AnalysisInputs): PassResult {
|
|
|
98
100
|
reasonsRecorded: counts.reasonsRecorded,
|
|
99
101
|
staleSuppressions: records.length,
|
|
100
102
|
pending: decided.evaluations.filter((one) => one.finding !== undefined).length,
|
|
103
|
+
withheld: decided.withheld,
|
|
101
104
|
},
|
|
102
105
|
ledger,
|
|
103
106
|
};
|
package/src/graph.ts
CHANGED
|
@@ -39,11 +39,11 @@ const SWEPT: Readonly<Record<SymbolKind, boolean>> = {
|
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* Whether a root of each kind names a caller, which holds it live under both relations: a
|
|
42
|
-
* tool reads
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
42
|
+
* tool reads what it loads and what its strings name, a runtime runs a test, setup or
|
|
43
|
+
* worker file, a page's module script, a command, a script or a workflow step, the manifest
|
|
44
|
+
* names what enters the package, an alias global stands for its export, and an entry file,
|
|
45
|
+
* a configured root and a pattern assert a caller. A library's published API only supposes
|
|
46
|
+
* an unseen consumer, so it is live under reachability alone.
|
|
47
47
|
*/
|
|
48
48
|
const NAMES_A_CALLER: Readonly<Record<RootKind, boolean>> = {
|
|
49
49
|
"entry-file": true,
|
|
@@ -59,10 +59,10 @@ const NAMES_A_CALLER: Readonly<Record<RootKind, boolean>> = {
|
|
|
59
59
|
"configuration-file": true,
|
|
60
60
|
"configuration-string": true,
|
|
61
61
|
"test-runner": true,
|
|
62
|
-
"mutation-testing": true,
|
|
63
62
|
"browser-tests": true,
|
|
64
63
|
worker: true,
|
|
65
64
|
"html-entry": true,
|
|
65
|
+
"workflow-step": true,
|
|
66
66
|
};
|
|
67
67
|
|
|
68
68
|
/** Whether the sweep judges a declaration of one kind. */
|
package/src/implementations.ts
CHANGED
|
@@ -118,7 +118,7 @@ function typesAt<Brand>(project: ProjectView<Brand>, nodes: readonly Node[]): Ma
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
/** Whether one class member is a method whose every written body holds no statement. */
|
|
121
|
-
function emptyImplementation<Brand>(project: ProjectView<Brand>, member: TSSymbol): boolean {
|
|
121
|
+
export function emptyImplementation<Brand>(project: ProjectView<Brand>, member: TSSymbol): boolean {
|
|
122
122
|
const own = project.ownPaths();
|
|
123
123
|
let bodies = 0;
|
|
124
124
|
for (const handle of member.declarations) {
|