@cplieger/deadset-ts 3.6.0 → 3.7.0
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/finding.js +0 -11
- package/dist/src/findings/completion.js +62 -26
- package/dist/src/findings/dependencies-and-module-machinery.js +3 -11
- package/dist/src/findings/emitters.js +6 -4
- package/dist/src/findings/reads-and-writes.js +16 -69
- package/dist/src/findings/unused-declarations.js +8 -40
- package/dist/src/kinds.js +47 -45
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/finding.ts +76 -10
- package/src/findings/completion.ts +71 -59
- package/src/findings/dependencies-and-module-machinery.ts +4 -19
- package/src/findings/emitter.ts +3 -2
- package/src/findings/emitters.ts +8 -8
- package/src/findings/interfaces.ts +5 -17
- package/src/findings/reads-and-writes.ts +37 -151
- package/src/findings/unused-declarations.ts +11 -58
- package/src/findings/visibility-narrowing.ts +1 -6
- package/src/kinds.ts +79 -45
package/dist/src/finding.js
CHANGED
|
@@ -1,12 +1 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The members of a finding its emitter decides: the code, where the subject is,
|
|
3
|
-
* what the subject is, and the sentence the finding reports. They are the
|
|
4
|
-
* Contract's finding members of the same names, spelled in this language's case.
|
|
5
|
-
*
|
|
6
|
-
* Every other member the finding schema requires follows from these and from the
|
|
7
|
-
* run: the kind's name, its fixability and its ceiling come from the code's row of
|
|
8
|
-
* the issue-kind vocabulary, the severity and the confidence from the resolved
|
|
9
|
-
* configuration, the language from the analyzer. A report completes them, so an
|
|
10
|
-
* emitter cannot state one that disagrees with the vocabulary.
|
|
11
|
-
*/
|
|
12
1
|
export {};
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* What
|
|
3
|
-
* the
|
|
4
|
-
*
|
|
5
|
-
* severity the configuration gives its code. The dials withhold what they name and
|
|
6
|
-
* every finding that falls with a root they withhold.
|
|
2
|
+
* What every finding carries beyond what its emitter decides, read from the run and from
|
|
3
|
+
* the code's row of the issue-kind vocabulary, and the dials that withhold a finding
|
|
4
|
+
* together with every finding that falls with a root they withhold.
|
|
7
5
|
*/
|
|
8
6
|
import { componentId } from "../components.js";
|
|
7
|
+
import { OUTSIDE } from "../graph.js";
|
|
8
|
+
import { KINDS } from "../kinds.js";
|
|
9
9
|
import { FAMILY_KEY_LENGTH } from "../resolve.js";
|
|
10
10
|
/** The classes ranked by the claim each makes, the strongest highest. */
|
|
11
|
-
|
|
11
|
+
const RANK = { certain: 3, probable: 2, possible: 1 };
|
|
12
12
|
/** The weaker of two classes, which is a class capped by a ceiling. */
|
|
13
|
-
|
|
13
|
+
function capped(found, ceiling) {
|
|
14
14
|
return RANK[found] <= RANK[ceiling] ? found : ceiling;
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
17
|
* The severity the configuration gives one code: the key naming the code, else the key
|
|
18
18
|
* naming its family, else the code's default.
|
|
19
19
|
*/
|
|
20
|
-
|
|
20
|
+
function severityOf(config, code, fallback) {
|
|
21
21
|
return (config.severity.get(code) ?? config.severity.get(code.slice(0, FAMILY_KEY_LENGTH)) ?? fallback);
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
@@ -27,7 +27,7 @@ export function severityOf(config, code, fallback) {
|
|
|
27
27
|
* in the loaded program, a private member and a `#private` name among them, and is
|
|
28
28
|
* `certain`.
|
|
29
29
|
*/
|
|
30
|
-
|
|
30
|
+
function reachabilityClasses(swept) {
|
|
31
31
|
const union = swept.matrix.union;
|
|
32
32
|
const published = new Set(union.rooted
|
|
33
33
|
.filter((root) => root.kind === "published-api")
|
|
@@ -35,7 +35,7 @@ export function reachabilityClasses(swept) {
|
|
|
35
35
|
return (id) => (published.has(id) ? "possible" : "certain");
|
|
36
36
|
}
|
|
37
37
|
/** The component each dead declaration of the run falls in, and none for a live one. */
|
|
38
|
-
|
|
38
|
+
function findingComponents(swept) {
|
|
39
39
|
const held = new Map();
|
|
40
40
|
for (const component of swept.sweep.components) {
|
|
41
41
|
for (const member of component.members) {
|
|
@@ -55,31 +55,67 @@ export function findingComponents(swept) {
|
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
57
|
/**
|
|
58
|
-
* Every finding of the run
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* no two families mint one
|
|
58
|
+
* Every finding of the run completed, in the order given. A dead declaration carries its
|
|
59
|
+
* candidate's relation, counts, configurations and component; a live one carries no
|
|
60
|
+
* relation and holds where it is declared; a file or a manifest row is `certain` and
|
|
61
|
+
* holds in every configuration. A subject in no dead component gets one of its own,
|
|
62
|
+
* numbered past the computed ones in finding order, so no two families mint one.
|
|
63
63
|
*/
|
|
64
|
-
export function
|
|
64
|
+
export function completed(input, findings) {
|
|
65
|
+
const { swept } = input;
|
|
66
|
+
const union = swept.matrix.union;
|
|
67
|
+
const positions = new Map(union.symbols.map((symbol, at) => [symbol.ref, at]));
|
|
68
|
+
const candidates = new Map(swept.sweep.candidates.map((candidate) => [candidate.id, candidate]));
|
|
69
|
+
const classOf = reachabilityClasses(swept);
|
|
65
70
|
const componentOf = findingComponents(swept);
|
|
66
|
-
const ids = new Map(swept.matrix.union.symbols.map((symbol) => [symbol.ref, symbol.id]));
|
|
67
71
|
let minted = swept.sweep.components.length;
|
|
68
72
|
return findings.map((finding) => {
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return { ...finding, component };
|
|
73
|
+
const row = KINDS.get(finding.code);
|
|
74
|
+
if (row === undefined) {
|
|
75
|
+
throw new Error(`${finding.code} names no live row of the issue-kind vocabulary`);
|
|
73
76
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
const at = positions.get(finding.symbol.ref) ?? OUTSIDE;
|
|
78
|
+
const symbol = union.symbols[at];
|
|
79
|
+
const judged = union.subject[at] === true ? symbol : undefined;
|
|
80
|
+
const candidate = judged === undefined ? undefined : candidates.get(judged.id);
|
|
81
|
+
const found = symbol === undefined ? undefined : componentOf(symbol.id);
|
|
82
|
+
if (candidate !== undefined && found === undefined) {
|
|
83
|
+
throw new Error(`${candidate.id} is judged dead and falls in no dead component`);
|
|
84
|
+
}
|
|
85
|
+
let component = found;
|
|
86
|
+
if (component === undefined) {
|
|
87
|
+
minted += 1;
|
|
88
|
+
component = {
|
|
78
89
|
id: componentId(minted),
|
|
79
90
|
root: true,
|
|
80
91
|
symbolCount: 1,
|
|
81
92
|
deletableLines: finding.symbol.sizeLines,
|
|
82
|
-
}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const counts = candidate === undefined
|
|
96
|
+
? union.made[at]
|
|
97
|
+
: { production: candidate.productionRefs, test: candidate.testRefs };
|
|
98
|
+
const reachabilityClass = judged === undefined ? "certain" : classOf(judged.id);
|
|
99
|
+
return {
|
|
100
|
+
code: finding.code,
|
|
101
|
+
kind: row.name,
|
|
102
|
+
language: "ts",
|
|
103
|
+
position: finding.position,
|
|
104
|
+
symbol: finding.symbol,
|
|
105
|
+
reachabilityClass,
|
|
106
|
+
confidence: capped(reachabilityClass, row.maxClass),
|
|
107
|
+
...(candidate === undefined ? {} : { livenessRelation: candidate.relation }),
|
|
108
|
+
testOnly: judged !== undefined && counts?.production === 0 && counts.test > 0,
|
|
109
|
+
generated: false,
|
|
110
|
+
component,
|
|
111
|
+
retainedBy: [],
|
|
112
|
+
configurations: candidate?.configurations ??
|
|
113
|
+
(judged === undefined ? swept.matrix.configurations : (swept.matrix.heldIn[at] ?? [])),
|
|
114
|
+
consumersLoaded: input.boundary.consumers.loaded,
|
|
115
|
+
fixability: row.fixability,
|
|
116
|
+
severity: severityOf(input.config, finding.code, row.defaultSeverity),
|
|
117
|
+
message: finding.message,
|
|
118
|
+
details: finding.details ?? {},
|
|
83
119
|
};
|
|
84
120
|
});
|
|
85
121
|
}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { renderRef } from "../ref.js";
|
|
2
|
-
import { severityOf } from "./completion.js";
|
|
3
2
|
const UNUSED_DEPENDENCY = "DS1601";
|
|
4
|
-
/** The severity the kind ships at, which the configuration's keys override. */
|
|
5
|
-
const UNUSED_DEPENDENCY_SEVERITY = "deny";
|
|
6
3
|
const MESSAGES = {
|
|
7
4
|
dependency: "no import in the target needs the declared dependency",
|
|
8
5
|
"dev-dependency": "no import in the target needs the declared development dependency",
|
|
@@ -34,14 +31,9 @@ function neededBy(dependencies) {
|
|
|
34
31
|
}
|
|
35
32
|
/**
|
|
36
33
|
* A dependency, development dependency or peer dependency of the target's manifest
|
|
37
|
-
* that the target does not need, at the position of its key.
|
|
38
|
-
* `allow`. Every subject is a manifest entry, which is `certain` whatever the
|
|
39
|
-
* consumers, so no minimum confidence withholds one.
|
|
34
|
+
* that the target does not need, at the position of its key.
|
|
40
35
|
*/
|
|
41
|
-
function unusedDependencies(
|
|
42
|
-
if (severityOf(config, UNUSED_DEPENDENCY, UNUSED_DEPENDENCY_SEVERITY) === "allow") {
|
|
43
|
-
return [];
|
|
44
|
-
}
|
|
36
|
+
function unusedDependencies(dependencies) {
|
|
45
37
|
const needed = neededBy(dependencies);
|
|
46
38
|
return dependencies.declared
|
|
47
39
|
.filter((dependency) => !needed.has(dependency.name))
|
|
@@ -63,4 +55,4 @@ function unusedDependencies(config, dependencies) {
|
|
|
63
55
|
}));
|
|
64
56
|
}
|
|
65
57
|
/** The findings of the dependencies-and-module-machinery family, `DS1600` to `DS1699`. */
|
|
66
|
-
export const dependenciesAndModuleMachinery = ({
|
|
58
|
+
export const dependenciesAndModuleMachinery = ({ dependencies }) => unusedDependencies(dependencies);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { dependenciesAndModuleMachinery } from "./dependencies-and-module-machinery.js";
|
|
2
|
-
import {
|
|
2
|
+
import { completed, reportable } from "./completion.js";
|
|
3
3
|
import { interfaces } from "./interfaces.js";
|
|
4
4
|
import { intraFunction } from "./intra-function.js";
|
|
5
5
|
import { nonCodeArtifacts } from "./non-code-artifacts.js";
|
|
@@ -19,9 +19,11 @@ export const EMITTERS = new Map([
|
|
|
19
19
|
["intra-function", intraFunction],
|
|
20
20
|
]);
|
|
21
21
|
/**
|
|
22
|
-
* Every finding of the run, each family's in table order
|
|
23
|
-
* by
|
|
22
|
+
* Every finding of the run the dials do not withhold, each family's in table order: every
|
|
23
|
+
* family's findings completed by one step, then withheld by one step, so a root one
|
|
24
|
+
* family reports withholds what another family reports in its component.
|
|
24
25
|
*/
|
|
25
26
|
export function findingsOf(input) {
|
|
26
|
-
|
|
27
|
+
const found = [...EMITTERS.values()].flatMap((emit) => emit(input));
|
|
28
|
+
return reportable(completed(input, found), input.config.analysis.minConfidence);
|
|
27
29
|
}
|
|
@@ -1,28 +1,12 @@
|
|
|
1
1
|
import { positionKey } from "../position.js";
|
|
2
|
-
import { findingComponents, RANK, reachabilityClasses, severityOf, } from "./completion.js";
|
|
3
|
-
/**
|
|
4
|
-
* Each code's severity where the configuration sets none, as the issue-kind vocabulary
|
|
5
|
-
* states it.
|
|
6
|
-
*/
|
|
7
|
-
export const DEFAULT_SEVERITY = {
|
|
8
|
-
DS1301: "deny",
|
|
9
|
-
DS1302: "deny",
|
|
10
|
-
DS1303: "deny",
|
|
11
|
-
};
|
|
12
2
|
/** The containers whose type parameters `DS1303` reports: a function and a class method. */
|
|
13
3
|
const SIGNATURES = new Set(["function", "method"]);
|
|
14
|
-
/** The severity the configuration gives one code of the family. */
|
|
15
|
-
function severityIn(input, code) {
|
|
16
|
-
return severityOf(input.config, code, DEFAULT_SEVERITY[code]);
|
|
17
|
-
}
|
|
18
4
|
function indexed(input) {
|
|
19
5
|
const { matrix, sweep } = input.swept;
|
|
20
6
|
return {
|
|
21
7
|
input,
|
|
22
8
|
symbols: new Map(matrix.union.symbols.map((symbol) => [symbol.id, symbol])),
|
|
23
9
|
candidates: new Map(sweep.candidates.map((candidate) => [candidate.id, candidate])),
|
|
24
|
-
componentOf: findingComponents(input.swept),
|
|
25
|
-
classOf: reachabilityClasses(input.swept),
|
|
26
10
|
};
|
|
27
11
|
}
|
|
28
12
|
/** Where one declaration is, as a finding states it. */
|
|
@@ -34,11 +18,9 @@ function positionOf(symbol) {
|
|
|
34
18
|
endLine: Math.max(symbol.position.line, symbol.endLine),
|
|
35
19
|
};
|
|
36
20
|
}
|
|
37
|
-
/** The finding
|
|
38
|
-
function findingAbout(
|
|
21
|
+
/** The finding one code makes about one declaration. */
|
|
22
|
+
function findingAbout(symbol, code, message, details) {
|
|
39
23
|
const position = positionOf(symbol);
|
|
40
|
-
const reachabilityClass = run.classOf(symbol.id);
|
|
41
|
-
const component = run.componentOf(symbol.id);
|
|
42
24
|
return {
|
|
43
25
|
code,
|
|
44
26
|
position,
|
|
@@ -49,14 +31,7 @@ function findingAbout(run, symbol, code, message, facts) {
|
|
|
49
31
|
sizeLines: position.endLine - position.line + 1,
|
|
50
32
|
},
|
|
51
33
|
message,
|
|
52
|
-
|
|
53
|
-
confidence: reachabilityClass,
|
|
54
|
-
...(facts.livenessRelation === undefined ? {} : { livenessRelation: facts.livenessRelation }),
|
|
55
|
-
testOnly: facts.testOnly,
|
|
56
|
-
...(component === undefined ? {} : { component }),
|
|
57
|
-
configurations: facts.configurations,
|
|
58
|
-
severity: severityIn(run.input, code),
|
|
59
|
-
details: facts.details,
|
|
34
|
+
...(details === undefined ? {} : { details }),
|
|
60
35
|
};
|
|
61
36
|
}
|
|
62
37
|
/**
|
|
@@ -85,15 +60,9 @@ function writeOnlySymbols(run) {
|
|
|
85
60
|
for (const reference of stores.references) {
|
|
86
61
|
let held = uses.get(reference.to);
|
|
87
62
|
if (held === undefined) {
|
|
88
|
-
held = { writes: [], reads: 0
|
|
63
|
+
held = { writes: [], reads: 0 };
|
|
89
64
|
uses.set(reference.to, held);
|
|
90
65
|
}
|
|
91
|
-
if (reference.test) {
|
|
92
|
-
held.test += 1;
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
95
|
-
held.production += 1;
|
|
96
|
-
}
|
|
97
66
|
if (reference.use !== "write") {
|
|
98
67
|
held.reads += 1;
|
|
99
68
|
continue;
|
|
@@ -105,7 +74,7 @@ function writeOnlySymbols(run) {
|
|
|
105
74
|
}
|
|
106
75
|
}
|
|
107
76
|
const found = [];
|
|
108
|
-
matrix.union.symbols
|
|
77
|
+
for (const symbol of matrix.union.symbols) {
|
|
109
78
|
const held = uses.get(symbol.id);
|
|
110
79
|
if (held === undefined ||
|
|
111
80
|
held.writes.length === 0 ||
|
|
@@ -113,16 +82,14 @@ function writeOnlySymbols(run) {
|
|
|
113
82
|
!writable(run, symbol) ||
|
|
114
83
|
stores.exempt.has(symbol.id) ||
|
|
115
84
|
run.candidates.has(symbol.id)) {
|
|
116
|
-
|
|
85
|
+
continue;
|
|
117
86
|
}
|
|
118
87
|
const writes = held.writes.length === 1 ? "once" : `at ${String(held.writes.length)} positions`;
|
|
119
88
|
const word = symbol.kind === "variable" ? "variable" : "member";
|
|
120
|
-
found.push(findingAbout(
|
|
121
|
-
|
|
122
|
-
configurations: matrix.heldIn[at] ?? [],
|
|
123
|
-
details: { writePositions: held.writes },
|
|
89
|
+
found.push(findingAbout(symbol, "DS1301", `${word} ${symbol.name} is written ${writes} and never read`, {
|
|
90
|
+
writePositions: held.writes,
|
|
124
91
|
}));
|
|
125
|
-
}
|
|
92
|
+
}
|
|
126
93
|
return found;
|
|
127
94
|
}
|
|
128
95
|
/**
|
|
@@ -139,7 +106,7 @@ function unnamed(run, kind) {
|
|
|
139
106
|
run.candidates.has(symbol.parent)) {
|
|
140
107
|
return [];
|
|
141
108
|
}
|
|
142
|
-
return [
|
|
109
|
+
return [symbol];
|
|
143
110
|
});
|
|
144
111
|
}
|
|
145
112
|
/**
|
|
@@ -148,12 +115,7 @@ function unnamed(run, kind) {
|
|
|
148
115
|
* this kind.
|
|
149
116
|
*/
|
|
150
117
|
function unusedEnumMembers(run) {
|
|
151
|
-
return unnamed(run, "enum-member").map((
|
|
152
|
-
livenessRelation: candidate.relation,
|
|
153
|
-
testOnly: false,
|
|
154
|
-
configurations: candidate.configurations,
|
|
155
|
-
details: {},
|
|
156
|
-
}));
|
|
118
|
+
return unnamed(run, "enum-member").map((symbol) => findingAbout(symbol, "DS1302", `enum member ${symbol.name} is named nowhere`));
|
|
157
119
|
}
|
|
158
120
|
/**
|
|
159
121
|
* `DS1303`: a type parameter of a function or a class method that neither the
|
|
@@ -163,13 +125,8 @@ function unusedEnumMembers(run) {
|
|
|
163
125
|
*/
|
|
164
126
|
function unusedTypeParameters(run) {
|
|
165
127
|
return unnamed(run, "type-parameter")
|
|
166
|
-
.filter((
|
|
167
|
-
.map((
|
|
168
|
-
livenessRelation: candidate.relation,
|
|
169
|
-
testOnly: false,
|
|
170
|
-
configurations: candidate.configurations,
|
|
171
|
-
details: {},
|
|
172
|
-
}));
|
|
128
|
+
.filter((symbol) => SIGNATURES.has(run.symbols.get(symbol.parent)?.kind ?? ""))
|
|
129
|
+
.map((symbol) => findingAbout(symbol, "DS1303", `type parameter ${symbol.name} is named in neither its signature nor its body`));
|
|
173
130
|
}
|
|
174
131
|
/**
|
|
175
132
|
* The references of the declarations the write-only kind reports, whatever the
|
|
@@ -179,21 +136,11 @@ function unusedTypeParameters(run) {
|
|
|
179
136
|
export function writeOnlyDeclarations(input) {
|
|
180
137
|
return new Set(writeOnlySymbols(indexed(input)).map((finding) => finding.symbol.ref));
|
|
181
138
|
}
|
|
182
|
-
/** Each code's rule, in code order. */
|
|
183
|
-
const RULES = [
|
|
184
|
-
["DS1301", writeOnlySymbols],
|
|
185
|
-
["DS1302", unusedEnumMembers],
|
|
186
|
-
["DS1303", unusedTypeParameters],
|
|
187
|
-
];
|
|
188
139
|
/**
|
|
189
|
-
* The findings of the reads-and-writes family, `DS1300` to `DS1399`,
|
|
190
|
-
*
|
|
191
|
-
* confidence is below the configured minimum is withheld.
|
|
140
|
+
* The findings of the reads-and-writes family, `DS1300` to `DS1399`, in code order and
|
|
141
|
+
* each code's in site order.
|
|
192
142
|
*/
|
|
193
143
|
export const readsAndWrites = (input) => {
|
|
194
144
|
const run = indexed(input);
|
|
195
|
-
|
|
196
|
-
return RULES.flatMap(([code, rule]) => severityIn(input, code) === "allow"
|
|
197
|
-
? []
|
|
198
|
-
: rule(run).filter((finding) => RANK[finding.confidence] >= least));
|
|
145
|
+
return [...writeOnlySymbols(run), ...unusedEnumMembers(run), ...unusedTypeParameters(run)];
|
|
199
146
|
};
|
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
import { capped, findingComponents, reachabilityClasses, reportable, severityOf, } from "./completion.js";
|
|
2
1
|
import { unreachableExports } from "./visibility-narrowing.js";
|
|
3
|
-
/** The codes of the unused-declarations family, each by its row of the vocabulary. */
|
|
4
|
-
export const UNUSED_DECLARATION_KINDS = new Map([
|
|
5
|
-
["DS1001", { defaultSeverity: "deny", maxClass: "certain" }],
|
|
6
|
-
["DS1002", { defaultSeverity: "deny", maxClass: "certain" }],
|
|
7
|
-
["DS1003", { defaultSeverity: "deny", maxClass: "certain" }],
|
|
8
|
-
["DS1004", { defaultSeverity: "deny", maxClass: "certain" }],
|
|
9
|
-
["DS1005", { defaultSeverity: "deny", maxClass: "certain" }],
|
|
10
|
-
["DS1006", { defaultSeverity: "deny", maxClass: "certain" }],
|
|
11
|
-
]);
|
|
12
2
|
/** The kinds of declaration that are members of a class, an interface, a type or an enum. */
|
|
13
3
|
const MEMBERS = new Set([
|
|
14
4
|
"method",
|
|
@@ -120,19 +110,13 @@ function messageOf(code, judged) {
|
|
|
120
110
|
}
|
|
121
111
|
return `${subject} has no reference in the target`;
|
|
122
112
|
}
|
|
123
|
-
/**
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
export function unusedDeclarationFindings(input) {
|
|
128
|
-
const { config, swept } = input;
|
|
129
|
-
const union = swept.matrix.union;
|
|
130
|
-
const dead = new Set(swept.sweep.candidates.map((candidate) => candidate.id));
|
|
131
|
-
const classOf = reachabilityClasses(swept);
|
|
132
|
-
const componentOf = findingComponents(swept);
|
|
113
|
+
/** The findings of the unused-declarations family, `DS1000` to `DS1099`. */
|
|
114
|
+
export const unusedDeclarations = (input) => {
|
|
115
|
+
const union = input.swept.matrix.union;
|
|
116
|
+
const dead = new Set(input.swept.sweep.candidates.map((candidate) => candidate.id));
|
|
133
117
|
const unreachable = unreachableExports(input);
|
|
134
118
|
const found = [];
|
|
135
|
-
for (const candidate of swept.sweep.candidates) {
|
|
119
|
+
for (const candidate of input.swept.sweep.candidates) {
|
|
136
120
|
const at = union.at(candidate.id);
|
|
137
121
|
const symbol = union.symbols[at];
|
|
138
122
|
if (symbol === undefined) {
|
|
@@ -147,16 +131,10 @@ export function unusedDeclarationFindings(input) {
|
|
|
147
131
|
unreachable: unreachable.has(symbol.id),
|
|
148
132
|
};
|
|
149
133
|
const code = codeOf(judged);
|
|
150
|
-
|
|
151
|
-
if (code === undefined || row === undefined) {
|
|
134
|
+
if (code === undefined) {
|
|
152
135
|
continue;
|
|
153
136
|
}
|
|
154
|
-
const component = componentOf(symbol.id);
|
|
155
|
-
if (component === undefined) {
|
|
156
|
-
throw new Error(`${symbol.id} is reported dead and falls in no dead component`);
|
|
157
|
-
}
|
|
158
137
|
const endLine = Math.max(symbol.position.line, symbol.endLine);
|
|
159
|
-
const reachabilityClass = classOf(symbol.id);
|
|
160
138
|
found.push({
|
|
161
139
|
code,
|
|
162
140
|
position: { ...symbol.position, endLine },
|
|
@@ -167,17 +145,7 @@ export function unusedDeclarationFindings(input) {
|
|
|
167
145
|
sizeLines: endLine - symbol.position.line + 1,
|
|
168
146
|
},
|
|
169
147
|
message: messageOf(code, judged),
|
|
170
|
-
reachabilityClass,
|
|
171
|
-
confidence: capped(reachabilityClass, row.maxClass),
|
|
172
|
-
livenessRelation: candidate.relation,
|
|
173
|
-
testOnly: candidate.productionRefs === 0 && candidate.testRefs > 0,
|
|
174
|
-
component,
|
|
175
|
-
configurations: candidate.configurations,
|
|
176
|
-
severity: severityOf(config, code, row.defaultSeverity),
|
|
177
|
-
details: {},
|
|
178
148
|
});
|
|
179
149
|
}
|
|
180
|
-
return
|
|
181
|
-
}
|
|
182
|
-
/** The findings of the unused-declarations family, `DS1000` to `DS1099`. */
|
|
183
|
-
export const unusedDeclarations = unusedDeclarationFindings;
|
|
150
|
+
return found;
|
|
151
|
+
};
|
package/dist/src/kinds.js
CHANGED
|
@@ -1,52 +1,54 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* The codes are written out rather than read from the Contract at run time: the
|
|
7
|
-
* Contract is data a reader outside this program consults, and a product that
|
|
8
|
-
* read it while running would need it installed beside itself. A test compares
|
|
9
|
-
* this list against the Contract release the analyzer is written against, so a
|
|
10
|
-
* code the Contract fixes arrives here as a failing test.
|
|
2
|
+
* Every live row of the issue-kind vocabulary, in ascending code order, written out
|
|
3
|
+
* rather than read at run time, because a product that read the Contract while running
|
|
4
|
+
* would need it installed beside itself. A test compares this table against the
|
|
5
|
+
* Contract release the analyzer is written against.
|
|
11
6
|
*/
|
|
12
|
-
|
|
7
|
+
const ROWS = [
|
|
8
|
+
row("DS1001", "unused-exported", "deny", "deletable"),
|
|
9
|
+
row("DS1002", "unused-unexported", "deny", "deletable"),
|
|
10
|
+
row("DS1003", "unused-member", "deny", "deletable"),
|
|
11
|
+
row("DS1004", "test-only-use", "deny", "deletable"),
|
|
12
|
+
row("DS1005", "test-of-dead-code", "deny", "deletable"),
|
|
13
|
+
row("DS1006", "deprecated-unused", "deny", "deletable"),
|
|
14
|
+
row("DS1101", "unnecessary-export", "warn", "narrowable"),
|
|
15
|
+
row("DS1102", "unnecessary-exposure", "warn", "narrowable"),
|
|
16
|
+
row("DS1103", "unreachable-export", "deny", "deletable"),
|
|
17
|
+
row("DS1104", "redundant-export-keyword", "warn", "narrowable"),
|
|
18
|
+
row("DS1201", "unused-interface", "deny", "deletable"),
|
|
19
|
+
row("DS1203", "uncalled-interface-method", "warn", "manual"),
|
|
20
|
+
row("DS1204", "unused-satisfaction-assertion", "deny", "deletable"),
|
|
21
|
+
row("DS1301", "write-only-symbol", "deny", "deletable"),
|
|
22
|
+
row("DS1302", "unused-enum-member", "deny", "deletable"),
|
|
23
|
+
row("DS1303", "unused-type-parameter", "deny", "deletable"),
|
|
24
|
+
row("DS1501", "file-never-built", "deny", "deletable"),
|
|
25
|
+
row("DS1502", "file-never-imported", "deny", "deletable"),
|
|
26
|
+
row("DS1601", "unused-dependency", "deny", "manual"),
|
|
27
|
+
row("DS1605", "unused-module-directive", "warn", "deletable"),
|
|
28
|
+
row("DS1701", "suppression-without-reason", "deny", "none"),
|
|
29
|
+
row("DS1702", "unscoped-ignore-entry", "deny", "none"),
|
|
30
|
+
{ ...row("DS1703", "stale-suppression", "deny", "none"), fixed: true },
|
|
31
|
+
{ ...row("DS1704", "unmatched-root", "deny", "none"), fixed: true },
|
|
32
|
+
row("DS1705", "stale-cross-language-edge", "deny", "none"),
|
|
33
|
+
row("DS1801", "unused-parameter", "warn", "manual"),
|
|
34
|
+
row("DS1802", "unused-receiver", "warn", "deletable"),
|
|
35
|
+
row("DS1803", "unused-result", "warn", "manual"),
|
|
36
|
+
row("DS1805", "unreachable-statement", "deny", "deletable"),
|
|
37
|
+
row("DS1807", "dead-store", "deny", "deletable"),
|
|
38
|
+
row("DS1809", "unreachable-case", "deny", "deletable"),
|
|
39
|
+
];
|
|
40
|
+
/** A row at the `certain` ceiling whose severity a configuration may set. */
|
|
41
|
+
function row(code, name, defaultSeverity, fixability) {
|
|
42
|
+
return { code, name, defaultSeverity, maxClass: "certain", fixability, fixed: false };
|
|
43
|
+
}
|
|
44
|
+
/** Every live row of the issue-kind vocabulary, by its code, in ascending code order. */
|
|
45
|
+
export const KINDS = new Map(ROWS.map((one) => [one.code, one]));
|
|
13
46
|
/**
|
|
14
47
|
* The code of every live issue kind, in ascending order. A severity key names one of
|
|
15
48
|
* them or a family prefix whose range holds one; a key naming a retired code, an
|
|
16
49
|
* unassigned code or a range holding no live kind is an unimplemented key, so every
|
|
17
|
-
* severity a configuration sets is one some analyzer reports under.
|
|
18
|
-
* this list against the Contract release the analyzer is written against.
|
|
50
|
+
* severity a configuration sets is one some analyzer reports under.
|
|
19
51
|
*/
|
|
20
|
-
export const LIVE_CODES =
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"DS1003",
|
|
24
|
-
"DS1004",
|
|
25
|
-
"DS1005",
|
|
26
|
-
"DS1006",
|
|
27
|
-
"DS1101",
|
|
28
|
-
"DS1102",
|
|
29
|
-
"DS1103",
|
|
30
|
-
"DS1104",
|
|
31
|
-
"DS1201",
|
|
32
|
-
"DS1203",
|
|
33
|
-
"DS1204",
|
|
34
|
-
"DS1301",
|
|
35
|
-
"DS1302",
|
|
36
|
-
"DS1303",
|
|
37
|
-
"DS1501",
|
|
38
|
-
"DS1502",
|
|
39
|
-
"DS1601",
|
|
40
|
-
"DS1605",
|
|
41
|
-
"DS1701",
|
|
42
|
-
"DS1702",
|
|
43
|
-
"DS1703",
|
|
44
|
-
"DS1704",
|
|
45
|
-
"DS1705",
|
|
46
|
-
"DS1801",
|
|
47
|
-
"DS1802",
|
|
48
|
-
"DS1803",
|
|
49
|
-
"DS1805",
|
|
50
|
-
"DS1807",
|
|
51
|
-
"DS1809",
|
|
52
|
-
];
|
|
52
|
+
export const LIVE_CODES = ROWS.map((one) => one.code);
|
|
53
|
+
/** The issue-kind codes whose severity the Contract fixes, in ascending order. */
|
|
54
|
+
export const FIXED_SEVERITY_CODES = ROWS.filter((one) => one.fixed).map((one) => one.code);
|
package/jsr.json
CHANGED