@cplieger/deadset-ts 8.0.0-dev.1 → 8.0.0-dev.11
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/bin/mapper-connection.js +2 -1
- package/dist/bin/node-host.js +24 -5
- package/dist/src/analysis.js +181 -341
- package/dist/src/augmentations.js +106 -13
- package/dist/src/automatic-types.js +10 -29
- package/dist/src/build-scripts.js +425 -0
- package/dist/src/component-layer.js +4 -2
- package/dist/src/components.js +2 -3
- package/dist/src/config.js +6 -82
- package/dist/src/configuration-files.js +28 -176
- package/dist/src/configured-declarations.js +15 -20
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/consumers.js +3 -30
- package/dist/src/contextual-uses.js +56 -9
- package/dist/src/contract/config.generated.js +558 -0
- package/dist/src/contract/contract.generated.js +1244 -0
- package/dist/src/contract/report.generated.js +862 -0
- package/dist/src/convention-rows.js +73 -3
- package/dist/src/conventions.js +36 -66
- package/dist/src/dependencies.js +103 -40
- package/dist/src/edges.js +38 -122
- package/dist/src/element-access.js +4 -6
- package/dist/src/entry-point-gaps.js +2 -8
- package/dist/src/entry-points.js +11 -27
- package/dist/src/enum-group.js +3 -10
- package/dist/src/erased-stores.js +4 -6
- package/dist/src/exempt-classes.js +1 -63
- package/dist/src/exempt.js +21 -40
- package/dist/src/failure.js +36 -0
- package/dist/src/file-facts.js +2 -25
- package/dist/src/findings/completion.js +139 -72
- package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
- package/dist/src/findings/emitters.js +5 -5
- package/dist/src/findings/interfaces.js +4 -5
- package/dist/src/findings/intra-function.js +13 -10
- package/dist/src/findings/non-code-artifacts.js +7 -8
- package/dist/src/findings/reads-and-writes.js +16 -13
- package/dist/src/findings/self-check.js +10 -12
- package/dist/src/findings/unused-declarations.js +41 -32
- package/dist/src/findings/visibility-narrowing.js +19 -16
- package/dist/src/findings-pass.js +8 -15
- package/dist/src/framework-lifecycle.js +7 -7
- package/dist/src/fs/document.js +52 -0
- package/dist/src/fs/manifest.js +72 -0
- package/dist/src/fs/tree.js +88 -0
- package/dist/src/generated-file.js +7 -4
- package/dist/src/git-ignore.js +39 -26
- package/dist/src/graph.js +5 -3
- package/dist/src/html-entries.js +6 -33
- package/dist/src/in-operands.js +2 -1
- package/dist/src/index.js +1 -1
- package/dist/src/injection-container.js +1 -1
- package/dist/src/intra-function-parts.js +63 -43
- package/dist/src/inventory.js +2 -21
- package/dist/src/javascript-file.js +33 -0
- package/dist/src/json-document.js +198 -67
- package/dist/src/keyof-comparisons.js +4 -6
- package/dist/src/kinds.js +3 -366
- package/dist/src/load/classify.js +270 -0
- package/dist/src/load/diagnostics.js +80 -0
- package/dist/src/load/discover.js +277 -0
- package/dist/src/{emit-map.js → load/emit-map.js} +1 -1
- package/dist/src/load/expected-module.js +136 -0
- package/dist/src/load/load.js +27 -0
- package/dist/src/load/module-lookup.js +37 -0
- package/dist/src/load/plan.js +313 -0
- package/dist/src/load/specifiers.js +115 -0
- package/dist/src/load/synthesize.js +140 -0
- package/dist/src/load/test-globals.js +207 -0
- package/dist/src/load/test-joins.js +82 -0
- package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +4 -4
- package/dist/src/{workspace-programs.js → load/workspace-programs.js} +89 -66
- package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +23 -65
- package/dist/src/{workspace-run.js → load/workspace-run.js} +7 -7
- package/dist/src/{workspace.js → load/workspace.js} +62 -124
- package/dist/src/manifest.js +49 -54
- package/dist/src/matrix.js +76 -4
- package/dist/src/mdx-entries.js +3 -26
- package/dist/src/outside-tests.js +1 -1
- package/dist/src/partial.js +2 -1
- package/dist/src/paths.js +26 -15
- package/dist/src/position.js +2 -1
- package/dist/src/print.js +11 -79
- package/dist/src/product.js +13 -0
- package/dist/src/query.js +1 -0
- package/dist/src/ref.js +1 -0
- package/dist/src/references.js +48 -30
- package/dist/src/report-schema.js +51 -0
- package/dist/src/report.js +57 -41
- package/dist/src/reporters/annotations.js +15 -8
- package/dist/src/reporters/sarif.js +16 -10
- package/dist/src/reporters/template.js +4 -19
- package/dist/src/resolve.js +245 -729
- package/dist/src/roots.js +273 -87
- package/dist/src/run.js +66 -77
- package/dist/src/schema.js +320 -343
- package/dist/src/scope.js +38 -115
- package/dist/src/serialization-contract.js +595 -21
- package/dist/src/session.js +34 -29
- package/dist/src/source-files.js +1 -1
- package/dist/src/suppress-file.js +25 -28
- package/dist/src/suppress-inline.js +3 -2
- package/dist/src/suppress.js +33 -51
- package/dist/src/sweep.js +7 -9
- package/dist/src/symbol-keys.js +3 -5
- package/dist/src/template-field.js +9 -19
- package/dist/src/test-support.js +8 -11
- package/dist/src/text/compare.js +26 -0
- package/dist/src/text/glob.js +194 -0
- package/dist/src/text/named-files.js +54 -0
- package/dist/src/text/shell.js +159 -0
- package/dist/src/text/yaml.js +393 -0
- package/dist/src/type-errors.js +11 -232
- package/dist/src/unseen-loads.js +100 -0
- package/dist/src/unwrap.js +23 -0
- package/dist/src/usage.js +10 -0
- package/dist/src/verbs/analyze.js +166 -126
- package/dist/src/verbs/describe.js +9 -16
- package/dist/src/verbs/explain.js +10 -7
- package/dist/src/verbs/print-config.js +2 -1
- package/dist/src/verbs/print-projects.js +6 -4
- package/dist/src/verbs/print-retained.js +4 -2
- package/dist/src/verbs/print-roots.js +4 -2
- package/dist/src/verbs/verb.js +1 -5
- package/dist/src/verbs/version.js +14 -3
- package/dist/src/workflow-steps.js +62 -64
- package/jsr.json +1 -1
- package/package.json +5 -4
- package/src/analysis.ts +245 -502
- package/src/augmentations.ts +135 -17
- package/src/automatic-types.ts +11 -29
- package/src/build-scripts.ts +543 -0
- package/src/component-layer.ts +4 -2
- package/src/components.ts +2 -4
- package/src/config.ts +31 -302
- package/src/configuration-files.ts +34 -179
- package/src/configured-declarations.ts +18 -23
- package/src/conformance-record.ts +1 -1
- package/src/consumers.ts +6 -57
- package/src/contextual-uses.ts +64 -12
- package/src/contract/config.generated.ts +818 -0
- package/src/contract/contract.generated.ts +1308 -0
- package/src/contract/report.generated.ts +1237 -0
- package/src/convention-rows.ts +85 -3
- package/src/conventions.ts +46 -84
- package/src/dependencies.ts +118 -46
- package/src/edges.ts +41 -140
- package/src/element-access.ts +4 -7
- package/src/entry-point-gaps.ts +3 -9
- package/src/entry-points.ts +12 -38
- package/src/enum-group.ts +2 -11
- package/src/erased-stores.ts +3 -7
- package/src/exempt-classes.ts +1 -75
- package/src/exempt.ts +36 -50
- package/src/failure.ts +63 -0
- package/src/file-facts.ts +9 -24
- package/src/finding.ts +14 -9
- package/src/findings/boundary.ts +2 -0
- package/src/findings/completion.ts +181 -84
- package/src/findings/dependencies-and-module-machinery.ts +4 -7
- package/src/findings/emitter.ts +8 -9
- package/src/findings/emitters.ts +7 -7
- package/src/findings/interfaces.ts +4 -6
- package/src/findings/intra-function.ts +13 -11
- package/src/findings/non-code-artifacts.ts +9 -11
- package/src/findings/reads-and-writes.ts +20 -16
- package/src/findings/self-check.ts +11 -17
- package/src/findings/unused-declarations.ts +45 -32
- package/src/findings/visibility-narrowing.ts +20 -17
- package/src/findings-pass.ts +10 -19
- package/src/framework-lifecycle.ts +7 -7
- package/src/fs/document.ts +70 -0
- package/src/fs/manifest.ts +92 -0
- package/src/fs/tree.ts +110 -0
- package/src/generated-file.ts +7 -4
- package/src/git-ignore.ts +50 -26
- package/src/graph.ts +5 -3
- package/src/host.ts +12 -5
- package/src/html-entries.ts +6 -33
- package/src/in-operands.ts +2 -1
- package/src/index.ts +1 -1
- package/src/injection-container.ts +1 -1
- package/src/intra-function-parts.ts +73 -44
- package/src/inventory.ts +2 -20
- package/src/javascript-file.ts +37 -0
- package/src/json-document.ts +241 -76
- package/src/keyof-comparisons.ts +3 -7
- package/src/kinds.ts +5 -413
- package/src/load/classify.ts +397 -0
- package/src/load/diagnostics.ts +144 -0
- package/src/load/discover.ts +455 -0
- package/src/{emit-map.ts → load/emit-map.ts} +1 -1
- package/src/load/expected-module.ts +188 -0
- package/src/load/load.ts +73 -0
- package/src/load/module-lookup.ts +50 -0
- package/src/load/plan.ts +504 -0
- package/src/load/specifiers.ts +168 -0
- package/src/load/synthesize.ts +211 -0
- package/src/load/test-globals.ts +251 -0
- package/src/load/test-joins.ts +98 -0
- package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +5 -5
- package/src/{workspace-programs.ts → load/workspace-programs.ts} +128 -85
- package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +33 -76
- package/src/{workspace-run.ts → load/workspace-run.ts} +18 -16
- package/src/{workspace.ts → load/workspace.ts} +90 -134
- package/src/manifest.ts +81 -75
- package/src/matrix.ts +122 -4
- package/src/mdx-entries.ts +3 -26
- package/src/outside-tests.ts +1 -1
- package/src/partial.ts +3 -2
- package/src/paths.ts +27 -15
- package/src/position.ts +2 -1
- package/src/print.ts +12 -92
- package/src/product.ts +18 -0
- package/src/query.ts +11 -0
- package/src/ref.ts +1 -0
- package/src/references.ts +52 -31
- package/src/report-schema.ts +68 -0
- package/src/report.ts +101 -224
- package/src/reporters/annotations.ts +22 -11
- package/src/reporters/json.ts +1 -1
- package/src/reporters/reporter.ts +2 -4
- package/src/reporters/reporters.ts +1 -1
- package/src/reporters/sarif.ts +29 -16
- package/src/reporters/template.ts +4 -20
- package/src/reporters/text.ts +3 -3
- package/src/resolve.ts +283 -1114
- package/src/roots.ts +339 -104
- package/src/run.ts +75 -91
- package/src/schema.ts +391 -388
- package/src/scope.ts +46 -138
- package/src/serialization-contract.ts +763 -22
- package/src/session.ts +70 -54
- package/src/source-files.ts +1 -1
- package/src/suppress-file.ts +24 -33
- package/src/suppress-inline.ts +2 -2
- package/src/suppress.ts +43 -58
- package/src/sweep.ts +23 -9
- package/src/symbol-keys.ts +2 -5
- package/src/template-field.ts +15 -25
- package/src/test-support.ts +17 -13
- package/src/text/compare.ts +27 -0
- package/src/text/glob.ts +210 -0
- package/src/text/named-files.inputs.json +1522 -0
- package/src/text/named-files.ts +68 -0
- package/src/text/shell.ts +161 -0
- package/src/text/yaml.ts +423 -0
- package/src/type-errors.ts +18 -288
- package/src/unseen-loads.ts +134 -0
- package/src/unwrap.ts +34 -0
- package/src/usage.ts +10 -0
- package/src/verbs/analyze.ts +224 -162
- package/src/verbs/describe.ts +14 -15
- package/src/verbs/explain.ts +10 -11
- package/src/verbs/print-config.ts +2 -1
- package/src/verbs/print-projects.ts +6 -4
- package/src/verbs/print-retained.ts +4 -2
- package/src/verbs/print-roots.ts +4 -2
- package/src/verbs/verb.ts +2 -8
- package/src/verbs/version.ts +15 -3
- package/src/workflow-steps.ts +74 -64
- package/dist/src/base-url.js +0 -18
- package/dist/src/discover.js +0 -468
- package/dist/src/glob.js +0 -65
- package/dist/src/pnpm-workspace.js +0 -120
- package/dist/src/setup-failure.js +0 -23
- package/dist/src/test-joins.js +0 -101
- package/dist/src/version.js +0 -7
- package/src/base-url.ts +0 -24
- package/src/discover.ts +0 -653
- package/src/glob.ts +0 -67
- package/src/pnpm-workspace.ts +0 -135
- package/src/setup-failure.ts +0 -39
- package/src/test-joins.ts +0 -120
- package/src/version.ts +0 -7
|
@@ -4,39 +4,38 @@
|
|
|
4
4
|
* together with every finding that falls with a root they withhold.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import {
|
|
8
|
+
CODE,
|
|
9
|
+
FAMILY_KEY_LENGTH,
|
|
10
|
+
type Confidence,
|
|
11
|
+
type Severity,
|
|
12
|
+
KIND_ROWS,
|
|
13
|
+
type KindRow,
|
|
14
|
+
} from "../contract/contract.generated.ts";
|
|
7
15
|
import type { RunSweep } from "../analysis.ts";
|
|
8
16
|
import { componentId, type Component } from "../components.ts";
|
|
9
17
|
import { deadComponents } from "../matrix.ts";
|
|
10
|
-
import type { Cascade
|
|
18
|
+
import type { Cascade } from "../config.ts";
|
|
11
19
|
import type { CompletedFinding, Finding, FindingComponent, PositionedSymbol } from "../finding.ts";
|
|
12
20
|
import { OUTSIDE, type Graph } from "../graph.ts";
|
|
13
21
|
import type { SymbolKind } from "../inventory.ts";
|
|
14
|
-
import { KINDS
|
|
22
|
+
import { KINDS } from "../kinds.ts";
|
|
15
23
|
import { byPosition, positionKey } from "../position.ts";
|
|
16
24
|
import { declarationKey } from "../ref.ts";
|
|
17
|
-
import { FAMILY_KEY_LENGTH } from "../resolve.ts";
|
|
18
25
|
import { isPartSubject, isRowSubject, type Dials } from "../suppress.ts";
|
|
19
26
|
import type { Candidate } from "../sweep.ts";
|
|
20
27
|
import type { Boundary } from "./boundary.ts";
|
|
21
28
|
import type { EmitterInput } from "./emitter.ts";
|
|
22
29
|
|
|
23
|
-
/** The unused-file code, whose deletion removes every use its file writes. */
|
|
24
|
-
const FILE_NEVER_IMPORTED = "DS1502";
|
|
25
|
-
|
|
26
30
|
/**
|
|
27
|
-
* The codes whose subject the run that counts test references holds live,
|
|
28
|
-
*
|
|
29
|
-
* none of a dead declaration's sweep facts.
|
|
31
|
+
* The codes whose subject the run that counts test references holds live, so a test
|
|
32
|
+
* file's candidate reported under one carries none of a dead declaration's sweep facts.
|
|
30
33
|
*/
|
|
31
|
-
const LIVE_SUBJECT: ReadonlySet<string> = new Set(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"DS1104",
|
|
35
|
-
"DS1204",
|
|
36
|
-
"DS1301",
|
|
37
|
-
]);
|
|
34
|
+
const LIVE_SUBJECT: ReadonlySet<string> = new Set(
|
|
35
|
+
KIND_ROWS.filter((row) => row.holdsLive).map((row) => row.code),
|
|
36
|
+
);
|
|
38
37
|
|
|
39
|
-
/** The fixability grammar/analysis.md
|
|
38
|
+
/** The fixability grammar/analysis/fixability.md gives a finding about a held declaration. */
|
|
40
39
|
const MANUAL = "manual";
|
|
41
40
|
|
|
42
41
|
/** The member kinds an object property can declare, which an `in` expression tests by name. */
|
|
@@ -66,10 +65,9 @@ function everyDeclaredLoaded(boundary: Boundary): boolean {
|
|
|
66
65
|
* naming its family, else the code's default.
|
|
67
66
|
*/
|
|
68
67
|
function severityOf(input: EmitterInput, code: string, fallback: Severity): Severity {
|
|
69
|
-
const {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
);
|
|
68
|
+
const { severity } = input.config;
|
|
69
|
+
const key = [code, code.slice(0, FAMILY_KEY_LENGTH)].find((one) => Object.hasOwn(severity, one));
|
|
70
|
+
return key === undefined ? fallback : (severity[key] ?? fallback);
|
|
73
71
|
}
|
|
74
72
|
|
|
75
73
|
/**
|
|
@@ -112,10 +110,12 @@ function testedByIn(input: EmitterInput): ReadonlySet<string> {
|
|
|
112
110
|
* published API, a public member of a published type included, is `certain` where the
|
|
113
111
|
* run loaded every consumer the scope declared and declared one, `probable` where one did
|
|
114
112
|
* not load, and `possible` where it declared none, whatever the configuration says of the
|
|
115
|
-
* consumer set. A member an `in` expression tests by name
|
|
113
|
+
* consumer set. A member an `in` expression tests by name, and one a function outside the
|
|
114
|
+
* analysis fills, is `possible`.
|
|
116
115
|
*/
|
|
117
116
|
function reachabilityClasses(input: EmitterInput): (id: string) => Confidence {
|
|
118
117
|
const tested = testedByIn(input);
|
|
118
|
+
const filled = input.boundary.filledOutside ?? new Set<string>();
|
|
119
119
|
const published = publishedIds(input.swept.matrix.union);
|
|
120
120
|
const { declared } = input.boundary.consumers;
|
|
121
121
|
const outside: Confidence =
|
|
@@ -125,13 +125,41 @@ function reachabilityClasses(input: EmitterInput): (id: string) => Confidence {
|
|
|
125
125
|
? "certain"
|
|
126
126
|
: "probable";
|
|
127
127
|
return (id) => {
|
|
128
|
-
if (tested.has(id)) {
|
|
128
|
+
if (tested.has(id) || filled.has(id)) {
|
|
129
129
|
return "possible";
|
|
130
130
|
}
|
|
131
131
|
return published.has(id) ? outside : "certain";
|
|
132
132
|
};
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
/**
|
|
136
|
+
* Whether one test of dead code references a target symbol that is published API of a run
|
|
137
|
+
* that declares no consumer, which outside programs the run cannot see may use, so the
|
|
138
|
+
* target counts as live for the test and the test is no test of dead code.
|
|
139
|
+
*/
|
|
140
|
+
export function testsUnseenAPI(input: EmitterInput): (id: string) => boolean {
|
|
141
|
+
if (input.boundary.consumers.declared.length > 0) {
|
|
142
|
+
return () => false;
|
|
143
|
+
}
|
|
144
|
+
const union = input.swept.matrix.union;
|
|
145
|
+
const published = publishedIds(union);
|
|
146
|
+
return (id) => testTargets(union, id).some((target) => published.has(target));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** The target symbols one test of dead code references, by id: production declarations the sweep judges. */
|
|
150
|
+
function testTargets(union: Graph, id: string): readonly string[] {
|
|
151
|
+
return (union.out[union.at(id)] ?? []).flatMap((edge) => {
|
|
152
|
+
const target = union.symbols[edge.to];
|
|
153
|
+
const testCode = union.test[edge.to] === true || union.support[edge.to] === true;
|
|
154
|
+
return target !== undefined &&
|
|
155
|
+
!testCode &&
|
|
156
|
+
union.ambient[edge.to] !== true &&
|
|
157
|
+
union.subject[edge.to] === true
|
|
158
|
+
? [target.id]
|
|
159
|
+
: [];
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
135
163
|
/**
|
|
136
164
|
* The confidence ceiling of a test of dead code: the lowest reachability class among the
|
|
137
165
|
* target symbols it references, because its deadness is theirs, whether or not they share
|
|
@@ -145,40 +173,59 @@ function testCeilings(
|
|
|
145
173
|
const tests = new Set(
|
|
146
174
|
input.swept.sweep.candidates.filter((one) => one.testOfDeadCode).map((one) => one.id),
|
|
147
175
|
);
|
|
148
|
-
return (id) =>
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
176
|
+
return (id) =>
|
|
177
|
+
tests.has(id)
|
|
178
|
+
? testTargets(union, id).reduce<Confidence>(
|
|
179
|
+
(lowest, target) => capped(lowest, classOf(target)),
|
|
180
|
+
"certain",
|
|
181
|
+
)
|
|
182
|
+
: "certain";
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* The subjects dead only because production code only tests use is: each declaration the
|
|
187
|
+
* sweep marks test-used, each `DS1602` file of such code, and each test of dead code every
|
|
188
|
+
* target of which is one. Nothing they hold references a dead declaration outside them, so
|
|
189
|
+
* the rest of a component they share is reported without them.
|
|
190
|
+
*/
|
|
191
|
+
function testUsedSubjects(input: EmitterInput): {
|
|
192
|
+
readonly declarations: ReadonlySet<string>;
|
|
193
|
+
readonly files: ReadonlySet<string>;
|
|
194
|
+
} {
|
|
195
|
+
const { matrix, sweep } = input.swept;
|
|
196
|
+
const union = matrix.union;
|
|
197
|
+
const used = new Set(sweep.candidates.filter((one) => one.testUsed).map((one) => one.id));
|
|
198
|
+
for (const test of sweep.candidates.filter((one) => one.testOfDeadCode)) {
|
|
199
|
+
const targets = testTargets(union, test.id);
|
|
200
|
+
if (targets.length > 0 && targets.every((target) => used.has(target))) {
|
|
201
|
+
used.add(test.id);
|
|
164
202
|
}
|
|
165
|
-
|
|
166
|
-
|
|
203
|
+
}
|
|
204
|
+
const files = new Set(
|
|
205
|
+
union.symbols.flatMap((symbol, at) =>
|
|
206
|
+
matrix.testUsed[at] === true ? [symbol.position.path] : [],
|
|
207
|
+
),
|
|
208
|
+
);
|
|
209
|
+
return { declarations: used, files };
|
|
167
210
|
}
|
|
168
211
|
|
|
169
212
|
/**
|
|
170
213
|
* The ceiling the roots the run found put on one dead subject: a subject other declarations
|
|
171
|
-
* reference, found dead only because no root reaches them, is `probable` unless
|
|
172
|
-
*
|
|
173
|
-
*
|
|
214
|
+
* reference, found dead only because no root reaches them, is `probable` unless the
|
|
215
|
+
* configuration declares the roots complete and no configuration holding the subject loads
|
|
216
|
+
* a module the analysis cannot resolve, because a root the analysis cannot see would make
|
|
217
|
+
* it live; every other declaration has none below `certain`.
|
|
174
218
|
*/
|
|
175
219
|
function rootsCeiling(
|
|
176
220
|
input: EmitterInput,
|
|
177
221
|
finding: Finding,
|
|
178
222
|
candidate: Candidate | undefined,
|
|
223
|
+
configurations: readonly string[],
|
|
179
224
|
): Confidence {
|
|
180
225
|
const unreachable = candidate?.relation === "reachability" || finding.unreachableOnly === true;
|
|
181
|
-
|
|
226
|
+
const unseen = new Set((input.files.unseenLoads ?? []).map((load) => load.configuration));
|
|
227
|
+
const complete = input.config.ts.roots_complete && !configurations.some((one) => unseen.has(one));
|
|
228
|
+
return unreachable && !complete ? "probable" : "certain";
|
|
182
229
|
}
|
|
183
230
|
|
|
184
231
|
/**
|
|
@@ -196,9 +243,28 @@ function findingComponents(
|
|
|
196
243
|
held.set(member, component);
|
|
197
244
|
}
|
|
198
245
|
}
|
|
246
|
+
const listed = membersListed(swept);
|
|
247
|
+
return (id) => {
|
|
248
|
+
const component = held.get(id);
|
|
249
|
+
return component === undefined
|
|
250
|
+
? undefined
|
|
251
|
+
: {
|
|
252
|
+
id: component.id,
|
|
253
|
+
root: component.roots.includes(id),
|
|
254
|
+
symbolCount: component.members.length,
|
|
255
|
+
deletableLines: component.deletableLines,
|
|
256
|
+
...(cascade === "full" ? { members: listed(component.members) } : {}),
|
|
257
|
+
};
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** The members of a component as the run lists them, positioned and in the canonical order. */
|
|
262
|
+
function membersListed(
|
|
263
|
+
swept: RunSweep,
|
|
264
|
+
): (members: readonly string[]) => readonly PositionedSymbol[] {
|
|
199
265
|
const symbols = new Map(swept.matrix.union.symbols.map((symbol) => [symbol.id, symbol]));
|
|
200
|
-
|
|
201
|
-
|
|
266
|
+
return (members) =>
|
|
267
|
+
members
|
|
202
268
|
.flatMap((member) => {
|
|
203
269
|
const symbol = symbols.get(member);
|
|
204
270
|
return symbol === undefined
|
|
@@ -215,22 +281,10 @@ function findingComponents(
|
|
|
215
281
|
];
|
|
216
282
|
})
|
|
217
283
|
.sort((a, b) => byPosition(a.position, b.position));
|
|
218
|
-
return (id) => {
|
|
219
|
-
const component = held.get(id);
|
|
220
|
-
return component === undefined
|
|
221
|
-
? undefined
|
|
222
|
-
: {
|
|
223
|
-
id: component.id,
|
|
224
|
-
root: component.roots.includes(id),
|
|
225
|
-
symbolCount: component.members.length,
|
|
226
|
-
deletableLines: component.deletableLines,
|
|
227
|
-
...(cascade === "full" ? { members: listed(component) } : {}),
|
|
228
|
-
};
|
|
229
|
-
};
|
|
230
284
|
}
|
|
231
285
|
|
|
232
286
|
/**
|
|
233
|
-
* The run's dead components with each file a `
|
|
287
|
+
* The run's dead components with each file a `DS1602` finding reports dead among them, so
|
|
234
288
|
* the file's declarations fall with it, and with the components each subject that is no
|
|
235
289
|
* declaration falls with joined into one.
|
|
236
290
|
*/
|
|
@@ -244,7 +298,7 @@ function runComponents(
|
|
|
244
298
|
} {
|
|
245
299
|
const union = swept.matrix.union;
|
|
246
300
|
const reported = new Set(
|
|
247
|
-
findings.filter((one) => one.code ===
|
|
301
|
+
findings.filter((one) => one.code === CODE.fileNeverImported).map((one) => one.position.path),
|
|
248
302
|
);
|
|
249
303
|
const files = new Map(
|
|
250
304
|
union.symbols.flatMap((symbol, at) =>
|
|
@@ -321,18 +375,20 @@ export function completed(
|
|
|
321
375
|
const candidates = new Map(swept.sweep.candidates.map((candidate) => [candidate.id, candidate]));
|
|
322
376
|
const classOf = reachabilityClasses(input);
|
|
323
377
|
const ceilingOf = testCeilings(input, classOf);
|
|
378
|
+
const testUsed = testUsedSubjects(input);
|
|
324
379
|
const { components, computed, files } = runComponents(swept, findings);
|
|
325
380
|
const componentOf = findingComponents(swept, components, input.config.reporters.cascade);
|
|
326
381
|
const held = new Set(components.filter((one) => one.held).flatMap((one) => one.members));
|
|
327
382
|
// A join keeps its first component's identifier, so a minted one counts past every computed one.
|
|
328
383
|
let minted = computed;
|
|
329
384
|
const dead = new Set(components.map((component) => component.id));
|
|
330
|
-
const fallingWith = new Map<string,
|
|
331
|
-
for (const { heldByTests } of swept.sweep.candidates) {
|
|
385
|
+
const fallingWith = new Map<string, string[]>();
|
|
386
|
+
for (const { id, heldByTests } of swept.sweep.candidates) {
|
|
332
387
|
if (heldByTests !== undefined) {
|
|
333
|
-
fallingWith.set(heldByTests, (fallingWith.get(heldByTests) ??
|
|
388
|
+
fallingWith.set(heldByTests, [...(fallingWith.get(heldByTests) ?? []), id]);
|
|
334
389
|
}
|
|
335
390
|
}
|
|
391
|
+
const listed = membersListed(swept);
|
|
336
392
|
|
|
337
393
|
const done = findings.map((finding): CompletedFinding => {
|
|
338
394
|
const row = kinds.get(finding.code);
|
|
@@ -361,7 +417,7 @@ export function completed(
|
|
|
361
417
|
const fallsWith = finding.fallsWith?.[0];
|
|
362
418
|
const joined = fallsWith === undefined ? undefined : componentOf(fallsWith);
|
|
363
419
|
const file =
|
|
364
|
-
finding.code ===
|
|
420
|
+
finding.code === CODE.fileNeverImported ? files.get(finding.position.path) : undefined;
|
|
365
421
|
const found =
|
|
366
422
|
joined !== undefined
|
|
367
423
|
? { ...joined, root: false }
|
|
@@ -383,19 +439,50 @@ export function completed(
|
|
|
383
439
|
let component = found;
|
|
384
440
|
if (component === undefined) {
|
|
385
441
|
minted += 1;
|
|
442
|
+
const falling = symbol?.kind === "interface" ? fallingWith.get(symbol.id) : undefined;
|
|
386
443
|
component = {
|
|
387
444
|
id: componentId(minted),
|
|
388
445
|
root: true,
|
|
389
|
-
symbolCount:
|
|
446
|
+
symbolCount: falling?.length ?? 1,
|
|
390
447
|
deletableLines: 0,
|
|
448
|
+
...(input.config.reporters.cascade !== "full"
|
|
449
|
+
? {}
|
|
450
|
+
: falling === undefined
|
|
451
|
+
? {
|
|
452
|
+
members: [
|
|
453
|
+
{
|
|
454
|
+
ref: finding.symbol.ref,
|
|
455
|
+
name: finding.symbol.name,
|
|
456
|
+
position: finding.position,
|
|
457
|
+
},
|
|
458
|
+
],
|
|
459
|
+
}
|
|
460
|
+
: { members: listed(falling) }),
|
|
391
461
|
};
|
|
392
462
|
}
|
|
393
463
|
const counts =
|
|
394
464
|
candidate === undefined
|
|
395
465
|
? union.made[at]
|
|
396
466
|
: { production: candidate.productionRefs, test: candidate.testRefs };
|
|
397
|
-
const
|
|
467
|
+
const ownClass =
|
|
398
468
|
judged === undefined ? (finding.reachabilityClass ?? "certain") : classOf(judged.id);
|
|
469
|
+
const used =
|
|
470
|
+
judged !== undefined
|
|
471
|
+
? candidate !== undefined && testUsed.declarations.has(judged.id)
|
|
472
|
+
: finding.code === CODE.fileNeverImported && testUsed.files.has(finding.position.path);
|
|
473
|
+
const reachabilityClass = used && candidate?.testOfDeadCode !== true ? "possible" : ownClass;
|
|
474
|
+
const configurations =
|
|
475
|
+
candidate?.configurations ??
|
|
476
|
+
(judged === undefined && !part
|
|
477
|
+
? swept.matrix.configurations
|
|
478
|
+
: (swept.matrix.heldIn[at] ?? swept.matrix.configurations));
|
|
479
|
+
const untested = capped(
|
|
480
|
+
capped(
|
|
481
|
+
capped(ownClass, row.maxClass),
|
|
482
|
+
rootsCeiling(input, finding, candidate, configurations),
|
|
483
|
+
),
|
|
484
|
+
judged === undefined ? "certain" : ceilingOf(judged.id),
|
|
485
|
+
);
|
|
399
486
|
const generated = input.files.generated?.has(finding.position.path) === true;
|
|
400
487
|
const fixability = generated
|
|
401
488
|
? "none"
|
|
@@ -410,7 +497,7 @@ export function completed(
|
|
|
410
497
|
? undefined
|
|
411
498
|
: candidate !== undefined
|
|
412
499
|
? input.dependencies.lastUses.get(candidate.id)
|
|
413
|
-
: finding.code ===
|
|
500
|
+
: finding.code === CODE.fileNeverImported
|
|
414
501
|
? input.dependencies.fileLastUses.get(finding.position.path)
|
|
415
502
|
: undefined;
|
|
416
503
|
return {
|
|
@@ -420,20 +507,14 @@ export function completed(
|
|
|
420
507
|
position: finding.position,
|
|
421
508
|
symbol: finding.symbol,
|
|
422
509
|
reachabilityClass,
|
|
423
|
-
confidence:
|
|
424
|
-
|
|
425
|
-
judged === undefined ? "certain" : ceilingOf(judged.id),
|
|
426
|
-
),
|
|
510
|
+
confidence: used ? "possible" : untested,
|
|
511
|
+
...(used ? { componentConfidence: untested } : {}),
|
|
427
512
|
...(candidate === undefined ? {} : { livenessRelation: candidate.relation }),
|
|
428
513
|
testOnly: judged !== undefined && counts?.production === 0 && counts.test > 0,
|
|
429
514
|
generated,
|
|
430
515
|
component,
|
|
431
516
|
retainedBy: [],
|
|
432
|
-
configurations
|
|
433
|
-
candidate?.configurations ??
|
|
434
|
-
(judged === undefined && !part
|
|
435
|
-
? swept.matrix.configurations
|
|
436
|
-
: (swept.matrix.heldIn[at] ?? swept.matrix.configurations)),
|
|
517
|
+
configurations,
|
|
437
518
|
consumersLoaded: input.boundary.consumers.loaded,
|
|
438
519
|
fixability,
|
|
439
520
|
severity: severityOf(input, finding.code, row.defaultSeverity),
|
|
@@ -450,7 +531,7 @@ export function completed(
|
|
|
450
531
|
/**
|
|
451
532
|
* The findings with every finding of a dead component capped by the lowest confidence
|
|
452
533
|
* among the component's root members, so the minimum confidence reports or withholds a
|
|
453
|
-
* component whole.
|
|
534
|
+
* component whole but for the findings test-used code lowers on their own.
|
|
454
535
|
*/
|
|
455
536
|
function componentCapped(
|
|
456
537
|
findings: readonly CompletedFinding[],
|
|
@@ -461,17 +542,29 @@ function componentCapped(
|
|
|
461
542
|
const { id, root } = finding.component;
|
|
462
543
|
if (root && dead.has(id)) {
|
|
463
544
|
const held = lowest.get(id);
|
|
464
|
-
|
|
545
|
+
const own = componentConfidenceOf(finding);
|
|
546
|
+
lowest.set(id, held === undefined ? own : capped(held, own));
|
|
465
547
|
}
|
|
466
548
|
}
|
|
467
549
|
return findings.map((finding) => {
|
|
468
550
|
const cap = lowest.get(finding.component.id);
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
551
|
+
if (cap === undefined) {
|
|
552
|
+
return finding;
|
|
553
|
+
}
|
|
554
|
+
const own = finding.componentConfidence;
|
|
555
|
+
return {
|
|
556
|
+
...finding,
|
|
557
|
+
confidence: capped(finding.confidence, cap),
|
|
558
|
+
...(own === undefined ? {} : { componentConfidence: capped(own, cap) }),
|
|
559
|
+
};
|
|
472
560
|
});
|
|
473
561
|
}
|
|
474
562
|
|
|
563
|
+
/** The confidence a finding gives its component's cap and the dials. */
|
|
564
|
+
function componentConfidenceOf(finding: CompletedFinding): Confidence {
|
|
565
|
+
return finding.componentConfidence ?? finding.confidence;
|
|
566
|
+
}
|
|
567
|
+
|
|
475
568
|
/** Whether the dials withhold a finding by its own severity or confidence. */
|
|
476
569
|
function dialed(
|
|
477
570
|
finding: Pick<CompletedFinding, "severity" | "confidence">,
|
|
@@ -491,10 +584,14 @@ export function dialsOf(
|
|
|
491
584
|
input: EmitterInput,
|
|
492
585
|
findings: readonly CompletedFinding[],
|
|
493
586
|
): Dials<CompletedFinding> {
|
|
494
|
-
const minConfidence = input.config.analysis.
|
|
587
|
+
const minConfidence = input.config.analysis.min_confidence;
|
|
495
588
|
const components = new Set(
|
|
496
589
|
findings
|
|
497
|
-
.filter(
|
|
590
|
+
.filter(
|
|
591
|
+
(finding) =>
|
|
592
|
+
finding.component.root &&
|
|
593
|
+
dialed({ ...finding, confidence: componentConfidenceOf(finding) }, minConfidence),
|
|
594
|
+
)
|
|
498
595
|
.map((finding) => finding.component.id),
|
|
499
596
|
);
|
|
500
597
|
return {
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
+
import { CODE } from "../contract/contract.generated.ts";
|
|
1
2
|
import { declarationKey, type Dependencies } from "../dependencies.ts";
|
|
2
3
|
import type { Finding } from "../finding.ts";
|
|
3
4
|
import { renderRef, type DependencySection } from "../ref.ts";
|
|
4
5
|
import type { Emitter, EmitterInput } from "./emitter.ts";
|
|
5
6
|
import { nonCodeArtifacts } from "./non-code-artifacts.ts";
|
|
6
7
|
|
|
7
|
-
const UNUSED_DEPENDENCY = "DS1601";
|
|
8
|
-
|
|
9
|
-
const FILE_NEVER_IMPORTED = "DS1502";
|
|
10
|
-
|
|
11
8
|
const MESSAGES: Readonly<Record<DependencySection, string>> = {
|
|
12
9
|
dependency: "no import in the target needs the declared dependency",
|
|
13
10
|
"dev-dependency": "no import in the target needs the declared development dependency",
|
|
@@ -46,7 +43,7 @@ function deadCode(input: EmitterInput): DeadCode {
|
|
|
46
43
|
);
|
|
47
44
|
for (const finding of nonCodeArtifacts(input)) {
|
|
48
45
|
const id = files.get(finding.position.path);
|
|
49
|
-
if (finding.code ===
|
|
46
|
+
if (finding.code === CODE.fileNeverImported && id !== undefined) {
|
|
50
47
|
dead.add(id);
|
|
51
48
|
if (finding.unreachableOnly === true) {
|
|
52
49
|
reachedOnly.add(id);
|
|
@@ -81,7 +78,7 @@ function unusedDependencies(input: EmitterInput, dependencies: Dependencies): Fi
|
|
|
81
78
|
const atPossible = unbuilt || users?.some((id) => published.has(id)) === true;
|
|
82
79
|
return [
|
|
83
80
|
{
|
|
84
|
-
code:
|
|
81
|
+
code: CODE.unusedDependency,
|
|
85
82
|
position: dependency.position,
|
|
86
83
|
symbol: {
|
|
87
84
|
ref: renderRef(dependency.manifest, {
|
|
@@ -105,6 +102,6 @@ function unusedDependencies(input: EmitterInput, dependencies: Dependencies): Fi
|
|
|
105
102
|
});
|
|
106
103
|
}
|
|
107
104
|
|
|
108
|
-
/** The findings of the dependencies
|
|
105
|
+
/** The findings of the dependencies family, `DS1700` to `DS1799`. */
|
|
109
106
|
export const dependenciesAndModuleMachinery: Emitter = (input) =>
|
|
110
107
|
unusedDependencies(input, input.dependencies);
|
package/src/findings/emitter.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RunSweep } from "../analysis.ts";
|
|
2
|
-
import type {
|
|
2
|
+
import type { ResolvedConfig } from "../config.ts";
|
|
3
3
|
import type { Dependencies } from "../dependencies.ts";
|
|
4
4
|
import type { FileFacts } from "../file-facts.ts";
|
|
5
5
|
import type { Finding } from "../finding.ts";
|
|
@@ -11,10 +11,10 @@ import type { SelfCheckFacts } from "./self-check.ts";
|
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* What every emitter reads: the configuration the run resolved, the run's sweep, and
|
|
14
|
-
* the facts about the run each
|
|
14
|
+
* the facts about the run each emitter reads beside the sweep.
|
|
15
15
|
*/
|
|
16
16
|
export interface EmitterInput {
|
|
17
|
-
readonly config:
|
|
17
|
+
readonly config: ResolvedConfig;
|
|
18
18
|
/** The production sweep a report is built from, beside the matrix it judged. */
|
|
19
19
|
readonly swept: RunSweep;
|
|
20
20
|
/** The declarations carrying the deprecation marker in any configuration of the run. */
|
|
@@ -29,18 +29,17 @@ export interface EmitterInput {
|
|
|
29
29
|
readonly files: FileFacts;
|
|
30
30
|
/** What can reach the target from outside it: consumers, declared edges, and `exports`. */
|
|
31
31
|
readonly boundary: Boundary;
|
|
32
|
-
/** The run's suppression outcomes and configured roots, which the
|
|
32
|
+
/** The run's suppression outcomes and configured roots, which the configuration family reports. */
|
|
33
33
|
readonly selfCheck: SelfCheckFacts;
|
|
34
|
-
/** The parts of the run's declarations, which the
|
|
34
|
+
/** The parts of the run's declarations, which the inside-functions family reports. */
|
|
35
35
|
readonly intraFunction: IntraFunctionFacts;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
|
-
* One
|
|
40
|
-
*
|
|
41
|
-
* configuration sets, in no particular order.
|
|
39
|
+
* One emitter's rule over one run: every finding of the codes it reports, whatever
|
|
40
|
+
* severity and minimum confidence the configuration sets, in no particular order.
|
|
42
41
|
*/
|
|
43
42
|
export type Emitter = (input: EmitterInput) => readonly Finding[];
|
|
44
43
|
|
|
45
|
-
/**
|
|
44
|
+
/** Every emitter this analyzer runs, by the name its tests select it by. */
|
|
46
45
|
export type Emitters = ReadonlyMap<string, Emitter>;
|
package/src/findings/emitters.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Confidence } from "../
|
|
1
|
+
import type { Confidence } from "../contract/contract.generated.ts";
|
|
2
2
|
import { evaluateEdges, type EdgeEvaluation } from "../edges.ts";
|
|
3
3
|
import type { CompletedFinding } from "../finding.ts";
|
|
4
4
|
import { liveFilesAt } from "../matrix.ts";
|
|
@@ -16,7 +16,7 @@ import { selfCheck } from "./self-check.ts";
|
|
|
16
16
|
import { fallenMembers, unusedDeclarations } from "./unused-declarations.ts";
|
|
17
17
|
import { visibilityNarrowing } from "./visibility-narrowing.ts";
|
|
18
18
|
|
|
19
|
-
/** Every
|
|
19
|
+
/** Every emitter this analyzer runs, by the name its tests select it by. */
|
|
20
20
|
export const EMITTERS: Emitters = new Map<string, Emitter>([
|
|
21
21
|
["unused-declarations", unusedDeclarations],
|
|
22
22
|
["visibility-narrowing", visibilityNarrowing],
|
|
@@ -76,7 +76,7 @@ interface Suppressed {
|
|
|
76
76
|
|
|
77
77
|
/** What the emitter table answers about one run. */
|
|
78
78
|
interface DecidedFindings {
|
|
79
|
-
/** Every finding the run reports, each
|
|
79
|
+
/** Every finding the run reports, each emitter's in table order. */
|
|
80
80
|
readonly findings: readonly CompletedFinding[];
|
|
81
81
|
/** One evaluation per declared edge side of this language, ordered by edge, then by side. */
|
|
82
82
|
readonly evaluations: readonly EdgeEvaluation<CompletedFinding>[];
|
|
@@ -91,7 +91,7 @@ interface DecidedFindings {
|
|
|
91
91
|
/** How many findings the minimum confidence withheld at each confidence. */
|
|
92
92
|
export type Withheld = Readonly<Record<Confidence, number>>;
|
|
93
93
|
|
|
94
|
-
/** Every
|
|
94
|
+
/** Every emitter's findings over one sweep, and those of any further emitter, completed by one step. */
|
|
95
95
|
function completedOver(
|
|
96
96
|
input: EmitterInput,
|
|
97
97
|
...more: readonly Emitter[]
|
|
@@ -124,8 +124,8 @@ function dialsOver(
|
|
|
124
124
|
|
|
125
125
|
/**
|
|
126
126
|
* Every finding of one run swept under its suppressions' marks, decided in one pass: each
|
|
127
|
-
*
|
|
128
|
-
* finding each record claims withheld, the rest withheld by the dials, so a root one
|
|
127
|
+
* emitter'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 emitter
|
|
129
129
|
* reports withholds what another reports in its component, and every declared edge
|
|
130
130
|
* evaluated over what remains, so a pending finding is in its evaluation alone.
|
|
131
131
|
*/
|
|
@@ -145,7 +145,7 @@ function atPossible(input: EmitterInput): EmitterInput {
|
|
|
145
145
|
const { config } = input;
|
|
146
146
|
return {
|
|
147
147
|
...input,
|
|
148
|
-
config: { ...config, analysis: { ...config.analysis,
|
|
148
|
+
config: { ...config, analysis: { ...config.analysis, min_confidence: "possible" } },
|
|
149
149
|
};
|
|
150
150
|
}
|
|
151
151
|
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
+
import { CODE } from "../contract/contract.generated.ts";
|
|
1
2
|
import type { Finding, FindingPosition, PositionedSymbol } from "../finding.ts";
|
|
2
3
|
import type { InventorySymbol } from "../inventory.ts";
|
|
3
4
|
import { byPosition } from "../position.ts";
|
|
4
5
|
import type { MatrixCandidate } from "../matrix.ts";
|
|
5
6
|
import type { Emitter, EmitterInput } from "./emitter.ts";
|
|
6
7
|
|
|
7
|
-
const UNUSED_INTERFACE = "DS1201";
|
|
8
|
-
const UNCALLED_INTERFACE_METHOD = "DS1203";
|
|
9
|
-
|
|
10
8
|
/** What each finding says, by what the sweep found about the subject's references. */
|
|
11
9
|
const UNUSED_MESSAGE = "nothing in the target names the interface as a type";
|
|
12
10
|
const UNUSED_DEAD_MESSAGE =
|
|
@@ -69,7 +67,7 @@ function findingOf(
|
|
|
69
67
|
|
|
70
68
|
/**
|
|
71
69
|
* The findings of the interfaces family, `DS1200` to `DS1299`: `DS1201`, a dead
|
|
72
|
-
* interface, whose members fall with it unreported; and `
|
|
70
|
+
* interface, whose members fall with it unreported; and `DS1202`, a method of a live
|
|
73
71
|
* interface no reference names, unless it is a marker, a method whose every
|
|
74
72
|
* implementation has an empty body. A test file's declaration is neither kind's subject,
|
|
75
73
|
* because a production sweep counts none of the references it can have, save an interface
|
|
@@ -86,7 +84,7 @@ export const interfaces: Emitter = (input) => {
|
|
|
86
84
|
if (symbol.kind === "interface") {
|
|
87
85
|
found.push(
|
|
88
86
|
findingOf(
|
|
89
|
-
|
|
87
|
+
CODE.unusedInterface,
|
|
90
88
|
symbol,
|
|
91
89
|
unusedMessage(candidate),
|
|
92
90
|
run.implementationsOf(candidate.id),
|
|
@@ -104,7 +102,7 @@ export const interfaces: Emitter = (input) => {
|
|
|
104
102
|
}
|
|
105
103
|
found.push(
|
|
106
104
|
findingOf(
|
|
107
|
-
|
|
105
|
+
CODE.uncalledInterfaceMethod,
|
|
108
106
|
symbol,
|
|
109
107
|
UNCALLED_MESSAGE,
|
|
110
108
|
run.implementationsOf(symbol.parent),
|