@cplieger/deadset-ts 8.0.0-dev.1 → 8.0.0-dev.10
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 +4 -3
- 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
package/dist/src/file-facts.js
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
* file below the target root, whether or not a project includes it, and the files a
|
|
4
4
|
* program holds by inclusion rather than by an import.
|
|
5
5
|
*/
|
|
6
|
-
import { IGNORED_DIRECTORIES } from "./discover.js";
|
|
7
6
|
import { packageScope } from "./inventory.js";
|
|
8
7
|
import { dirnamePath, joinPath, relativePath, resolvePath } from "./paths.js";
|
|
9
8
|
import { renderRef } from "./ref.js";
|
|
9
|
+
import { filesBelow } from "./fs/tree.js";
|
|
10
10
|
/** A file name the compiler reads as a source file. */
|
|
11
11
|
const SOURCE_FILE = /\.(?:ts|tsx|mts|cts)$/u;
|
|
12
12
|
/** A file name the compiler reads as a declaration file, which no build emits code from. */
|
|
@@ -30,30 +30,7 @@ function lastLine(text) {
|
|
|
30
30
|
* that cannot be read is skipped, as discovery skips it.
|
|
31
31
|
*/
|
|
32
32
|
export function sourcePaths(host, targetRoot) {
|
|
33
|
-
|
|
34
|
-
const pending = [""];
|
|
35
|
-
for (let dir = pending.pop(); dir !== undefined; dir = pending.pop()) {
|
|
36
|
-
let entries;
|
|
37
|
-
try {
|
|
38
|
-
entries = host.readDirectory(joinPath(targetRoot, dir));
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
for (const entry of entries) {
|
|
44
|
-
const path = dir === "" ? entry.name : `${dir}/${entry.name}`;
|
|
45
|
-
if (entry.directory) {
|
|
46
|
-
if (!IGNORED_DIRECTORIES.has(entry.name)) {
|
|
47
|
-
pending.push(path);
|
|
48
|
-
}
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
if (SOURCE_FILE.test(entry.name) && !DECLARATION_FILE.test(entry.name)) {
|
|
52
|
-
found.push(path);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return found.sort((a, b) => (a < b ? -1 : 1));
|
|
33
|
+
return filesBelow(host, targetRoot, (name) => SOURCE_FILE.test(name) && !DECLARATION_FILE.test(name));
|
|
57
34
|
}
|
|
58
35
|
/**
|
|
59
36
|
* Every source file below the target root, in path order. A file that cannot be read is
|
|
@@ -3,29 +3,20 @@
|
|
|
3
3
|
* the code's row of the issue-kind vocabulary, and the dials that withhold a finding
|
|
4
4
|
* together with every finding that falls with a root they withhold.
|
|
5
5
|
*/
|
|
6
|
+
import { CODE, FAMILY_KEY_LENGTH, KIND_ROWS, } from "../contract/contract.generated.js";
|
|
6
7
|
import { componentId } from "../components.js";
|
|
7
8
|
import { deadComponents } from "../matrix.js";
|
|
8
9
|
import { OUTSIDE } from "../graph.js";
|
|
9
10
|
import { KINDS } from "../kinds.js";
|
|
10
11
|
import { byPosition, positionKey } from "../position.js";
|
|
11
12
|
import { declarationKey } from "../ref.js";
|
|
12
|
-
import { FAMILY_KEY_LENGTH } from "../resolve.js";
|
|
13
13
|
import { isPartSubject, isRowSubject } from "../suppress.js";
|
|
14
|
-
/** The unused-file code, whose deletion removes every use its file writes. */
|
|
15
|
-
const FILE_NEVER_IMPORTED = "DS1502";
|
|
16
14
|
/**
|
|
17
|
-
* The codes whose subject the run that counts test references holds live,
|
|
18
|
-
*
|
|
19
|
-
* none of a dead declaration's sweep facts.
|
|
15
|
+
* The codes whose subject the run that counts test references holds live, so a test
|
|
16
|
+
* file's candidate reported under one carries none of a dead declaration's sweep facts.
|
|
20
17
|
*/
|
|
21
|
-
const LIVE_SUBJECT = new Set(
|
|
22
|
-
|
|
23
|
-
"DS1102",
|
|
24
|
-
"DS1104",
|
|
25
|
-
"DS1204",
|
|
26
|
-
"DS1301",
|
|
27
|
-
]);
|
|
28
|
-
/** The fixability grammar/analysis.md "Fixability" gives a finding about a held declaration. */
|
|
18
|
+
const LIVE_SUBJECT = new Set(KIND_ROWS.filter((row) => row.holdsLive).map((row) => row.code));
|
|
19
|
+
/** The fixability grammar/analysis/fixability.md gives a finding about a held declaration. */
|
|
29
20
|
const MANUAL = "manual";
|
|
30
21
|
/** The member kinds an object property can declare, which an `in` expression tests by name. */
|
|
31
22
|
const IN_TESTED = new Set([
|
|
@@ -50,8 +41,9 @@ function everyDeclaredLoaded(boundary) {
|
|
|
50
41
|
* naming its family, else the code's default.
|
|
51
42
|
*/
|
|
52
43
|
function severityOf(input, code, fallback) {
|
|
53
|
-
const {
|
|
54
|
-
|
|
44
|
+
const { severity } = input.config;
|
|
45
|
+
const key = [code, code.slice(0, FAMILY_KEY_LENGTH)].find((one) => Object.hasOwn(severity, one));
|
|
46
|
+
return key === undefined ? fallback : (severity[key] ?? fallback);
|
|
55
47
|
}
|
|
56
48
|
/**
|
|
57
49
|
* The declarations the roots name as a library's published API, each public member of a
|
|
@@ -87,10 +79,12 @@ function testedByIn(input) {
|
|
|
87
79
|
* published API, a public member of a published type included, is `certain` where the
|
|
88
80
|
* run loaded every consumer the scope declared and declared one, `probable` where one did
|
|
89
81
|
* not load, and `possible` where it declared none, whatever the configuration says of the
|
|
90
|
-
* consumer set. A member an `in` expression tests by name
|
|
82
|
+
* consumer set. A member an `in` expression tests by name, and one a function outside the
|
|
83
|
+
* analysis fills, is `possible`.
|
|
91
84
|
*/
|
|
92
85
|
function reachabilityClasses(input) {
|
|
93
86
|
const tested = testedByIn(input);
|
|
87
|
+
const filled = input.boundary.filledOutside ?? new Set();
|
|
94
88
|
const published = publishedIds(input.swept.matrix.union);
|
|
95
89
|
const { declared } = input.boundary.consumers;
|
|
96
90
|
const outside = declared.length === 0
|
|
@@ -99,12 +93,38 @@ function reachabilityClasses(input) {
|
|
|
99
93
|
? "certain"
|
|
100
94
|
: "probable";
|
|
101
95
|
return (id) => {
|
|
102
|
-
if (tested.has(id)) {
|
|
96
|
+
if (tested.has(id) || filled.has(id)) {
|
|
103
97
|
return "possible";
|
|
104
98
|
}
|
|
105
99
|
return published.has(id) ? outside : "certain";
|
|
106
100
|
};
|
|
107
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Whether one test of dead code references a target symbol that is published API of a run
|
|
104
|
+
* that declares no consumer, which outside programs the run cannot see may use, so the
|
|
105
|
+
* target counts as live for the test and the test is no test of dead code.
|
|
106
|
+
*/
|
|
107
|
+
export function testsUnseenAPI(input) {
|
|
108
|
+
if (input.boundary.consumers.declared.length > 0) {
|
|
109
|
+
return () => false;
|
|
110
|
+
}
|
|
111
|
+
const union = input.swept.matrix.union;
|
|
112
|
+
const published = publishedIds(union);
|
|
113
|
+
return (id) => testTargets(union, id).some((target) => published.has(target));
|
|
114
|
+
}
|
|
115
|
+
/** The target symbols one test of dead code references, by id: production declarations the sweep judges. */
|
|
116
|
+
function testTargets(union, id) {
|
|
117
|
+
return (union.out[union.at(id)] ?? []).flatMap((edge) => {
|
|
118
|
+
const target = union.symbols[edge.to];
|
|
119
|
+
const testCode = union.test[edge.to] === true || union.support[edge.to] === true;
|
|
120
|
+
return target !== undefined &&
|
|
121
|
+
!testCode &&
|
|
122
|
+
union.ambient[edge.to] !== true &&
|
|
123
|
+
union.subject[edge.to] === true
|
|
124
|
+
? [target.id]
|
|
125
|
+
: [];
|
|
126
|
+
});
|
|
127
|
+
}
|
|
108
128
|
/**
|
|
109
129
|
* The confidence ceiling of a test of dead code: the lowest reachability class among the
|
|
110
130
|
* target symbols it references, because its deadness is theirs, whether or not they share
|
|
@@ -113,33 +133,41 @@ function reachabilityClasses(input) {
|
|
|
113
133
|
function testCeilings(input, classOf) {
|
|
114
134
|
const union = input.swept.matrix.union;
|
|
115
135
|
const tests = new Set(input.swept.sweep.candidates.filter((one) => one.testOfDeadCode).map((one) => one.id));
|
|
116
|
-
return (id) =>
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
136
|
+
return (id) => tests.has(id)
|
|
137
|
+
? testTargets(union, id).reduce((lowest, target) => capped(lowest, classOf(target)), "certain")
|
|
138
|
+
: "certain";
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The subjects dead only because production code only tests use is: each declaration the
|
|
142
|
+
* sweep marks test-used, each `DS1602` file of such code, and each test of dead code every
|
|
143
|
+
* target of which is one. Nothing they hold references a dead declaration outside them, so
|
|
144
|
+
* the rest of a component they share is reported without them.
|
|
145
|
+
*/
|
|
146
|
+
function testUsedSubjects(input) {
|
|
147
|
+
const { matrix, sweep } = input.swept;
|
|
148
|
+
const union = matrix.union;
|
|
149
|
+
const used = new Set(sweep.candidates.filter((one) => one.testUsed).map((one) => one.id));
|
|
150
|
+
for (const test of sweep.candidates.filter((one) => one.testOfDeadCode)) {
|
|
151
|
+
const targets = testTargets(union, test.id);
|
|
152
|
+
if (targets.length > 0 && targets.every((target) => used.has(target))) {
|
|
153
|
+
used.add(test.id);
|
|
130
154
|
}
|
|
131
|
-
|
|
132
|
-
|
|
155
|
+
}
|
|
156
|
+
const files = new Set(union.symbols.flatMap((symbol, at) => matrix.testUsed[at] === true ? [symbol.position.path] : []));
|
|
157
|
+
return { declarations: used, files };
|
|
133
158
|
}
|
|
134
159
|
/**
|
|
135
160
|
* The ceiling the roots the run found put on one dead subject: a subject other declarations
|
|
136
|
-
* reference, found dead only because no root reaches them, is `probable` unless
|
|
137
|
-
*
|
|
138
|
-
*
|
|
161
|
+
* reference, found dead only because no root reaches them, is `probable` unless the
|
|
162
|
+
* configuration declares the roots complete and no configuration holding the subject loads
|
|
163
|
+
* a module the analysis cannot resolve, because a root the analysis cannot see would make
|
|
164
|
+
* it live; every other declaration has none below `certain`.
|
|
139
165
|
*/
|
|
140
|
-
function rootsCeiling(input, finding, candidate) {
|
|
166
|
+
function rootsCeiling(input, finding, candidate, configurations) {
|
|
141
167
|
const unreachable = candidate?.relation === "reachability" || finding.unreachableOnly === true;
|
|
142
|
-
|
|
168
|
+
const unseen = new Set((input.files.unseenLoads ?? []).map((load) => load.configuration));
|
|
169
|
+
const complete = input.config.ts.roots_complete && !configurations.some((one) => unseen.has(one));
|
|
170
|
+
return unreachable && !complete ? "probable" : "certain";
|
|
143
171
|
}
|
|
144
172
|
/**
|
|
145
173
|
* The component each dead declaration of the run falls in, and none for a live one, with
|
|
@@ -152,8 +180,24 @@ function findingComponents(swept, components, cascade) {
|
|
|
152
180
|
held.set(member, component);
|
|
153
181
|
}
|
|
154
182
|
}
|
|
183
|
+
const listed = membersListed(swept);
|
|
184
|
+
return (id) => {
|
|
185
|
+
const component = held.get(id);
|
|
186
|
+
return component === undefined
|
|
187
|
+
? undefined
|
|
188
|
+
: {
|
|
189
|
+
id: component.id,
|
|
190
|
+
root: component.roots.includes(id),
|
|
191
|
+
symbolCount: component.members.length,
|
|
192
|
+
deletableLines: component.deletableLines,
|
|
193
|
+
...(cascade === "full" ? { members: listed(component.members) } : {}),
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/** The members of a component as the run lists them, positioned and in the canonical order. */
|
|
198
|
+
function membersListed(swept) {
|
|
155
199
|
const symbols = new Map(swept.matrix.union.symbols.map((symbol) => [symbol.id, symbol]));
|
|
156
|
-
|
|
200
|
+
return (members) => members
|
|
157
201
|
.flatMap((member) => {
|
|
158
202
|
const symbol = symbols.get(member);
|
|
159
203
|
return symbol === undefined
|
|
@@ -170,27 +214,15 @@ function findingComponents(swept, components, cascade) {
|
|
|
170
214
|
];
|
|
171
215
|
})
|
|
172
216
|
.sort((a, b) => byPosition(a.position, b.position));
|
|
173
|
-
return (id) => {
|
|
174
|
-
const component = held.get(id);
|
|
175
|
-
return component === undefined
|
|
176
|
-
? undefined
|
|
177
|
-
: {
|
|
178
|
-
id: component.id,
|
|
179
|
-
root: component.roots.includes(id),
|
|
180
|
-
symbolCount: component.members.length,
|
|
181
|
-
deletableLines: component.deletableLines,
|
|
182
|
-
...(cascade === "full" ? { members: listed(component) } : {}),
|
|
183
|
-
};
|
|
184
|
-
};
|
|
185
217
|
}
|
|
186
218
|
/**
|
|
187
|
-
* The run's dead components with each file a `
|
|
219
|
+
* The run's dead components with each file a `DS1602` finding reports dead among them, so
|
|
188
220
|
* the file's declarations fall with it, and with the components each subject that is no
|
|
189
221
|
* declaration falls with joined into one.
|
|
190
222
|
*/
|
|
191
223
|
function runComponents(swept, findings) {
|
|
192
224
|
const union = swept.matrix.union;
|
|
193
|
-
const reported = new Set(findings.filter((one) => one.code ===
|
|
225
|
+
const reported = new Set(findings.filter((one) => one.code === CODE.fileNeverImported).map((one) => one.position.path));
|
|
194
226
|
const files = new Map(union.symbols.flatMap((symbol, at) => symbol.kind === "file" && reported.has(symbol.position.path) ? [[at, symbol]] : []));
|
|
195
227
|
const computed = files.size === 0
|
|
196
228
|
? swept.sweep.components
|
|
@@ -251,6 +283,7 @@ export function completed(input, findings, kinds = KINDS) {
|
|
|
251
283
|
const candidates = new Map(swept.sweep.candidates.map((candidate) => [candidate.id, candidate]));
|
|
252
284
|
const classOf = reachabilityClasses(input);
|
|
253
285
|
const ceilingOf = testCeilings(input, classOf);
|
|
286
|
+
const testUsed = testUsedSubjects(input);
|
|
254
287
|
const { components, computed, files } = runComponents(swept, findings);
|
|
255
288
|
const componentOf = findingComponents(swept, components, input.config.reporters.cascade);
|
|
256
289
|
const held = new Set(components.filter((one) => one.held).flatMap((one) => one.members));
|
|
@@ -258,11 +291,12 @@ export function completed(input, findings, kinds = KINDS) {
|
|
|
258
291
|
let minted = computed;
|
|
259
292
|
const dead = new Set(components.map((component) => component.id));
|
|
260
293
|
const fallingWith = new Map();
|
|
261
|
-
for (const { heldByTests } of swept.sweep.candidates) {
|
|
294
|
+
for (const { id, heldByTests } of swept.sweep.candidates) {
|
|
262
295
|
if (heldByTests !== undefined) {
|
|
263
|
-
fallingWith.set(heldByTests, (fallingWith.get(heldByTests) ??
|
|
296
|
+
fallingWith.set(heldByTests, [...(fallingWith.get(heldByTests) ?? []), id]);
|
|
264
297
|
}
|
|
265
298
|
}
|
|
299
|
+
const listed = membersListed(swept);
|
|
266
300
|
const done = findings.map((finding) => {
|
|
267
301
|
const row = kinds.get(finding.code);
|
|
268
302
|
if (row === undefined) {
|
|
@@ -288,7 +322,7 @@ export function completed(input, findings, kinds = KINDS) {
|
|
|
288
322
|
: candidates.get(judged.id);
|
|
289
323
|
const fallsWith = finding.fallsWith?.[0];
|
|
290
324
|
const joined = fallsWith === undefined ? undefined : componentOf(fallsWith);
|
|
291
|
-
const file = finding.code ===
|
|
325
|
+
const file = finding.code === CODE.fileNeverImported ? files.get(finding.position.path) : undefined;
|
|
292
326
|
const found = joined !== undefined
|
|
293
327
|
? { ...joined, root: false }
|
|
294
328
|
: file !== undefined
|
|
@@ -307,17 +341,40 @@ export function completed(input, findings, kinds = KINDS) {
|
|
|
307
341
|
let component = found;
|
|
308
342
|
if (component === undefined) {
|
|
309
343
|
minted += 1;
|
|
344
|
+
const falling = symbol?.kind === "interface" ? fallingWith.get(symbol.id) : undefined;
|
|
310
345
|
component = {
|
|
311
346
|
id: componentId(minted),
|
|
312
347
|
root: true,
|
|
313
|
-
symbolCount:
|
|
348
|
+
symbolCount: falling?.length ?? 1,
|
|
314
349
|
deletableLines: 0,
|
|
350
|
+
...(input.config.reporters.cascade !== "full"
|
|
351
|
+
? {}
|
|
352
|
+
: falling === undefined
|
|
353
|
+
? {
|
|
354
|
+
members: [
|
|
355
|
+
{
|
|
356
|
+
ref: finding.symbol.ref,
|
|
357
|
+
name: finding.symbol.name,
|
|
358
|
+
position: finding.position,
|
|
359
|
+
},
|
|
360
|
+
],
|
|
361
|
+
}
|
|
362
|
+
: { members: listed(falling) }),
|
|
315
363
|
};
|
|
316
364
|
}
|
|
317
365
|
const counts = candidate === undefined
|
|
318
366
|
? union.made[at]
|
|
319
367
|
: { production: candidate.productionRefs, test: candidate.testRefs };
|
|
320
|
-
const
|
|
368
|
+
const ownClass = judged === undefined ? (finding.reachabilityClass ?? "certain") : classOf(judged.id);
|
|
369
|
+
const used = judged !== undefined
|
|
370
|
+
? candidate !== undefined && testUsed.declarations.has(judged.id)
|
|
371
|
+
: finding.code === CODE.fileNeverImported && testUsed.files.has(finding.position.path);
|
|
372
|
+
const reachabilityClass = used && candidate?.testOfDeadCode !== true ? "possible" : ownClass;
|
|
373
|
+
const configurations = candidate?.configurations ??
|
|
374
|
+
(judged === undefined && !part
|
|
375
|
+
? swept.matrix.configurations
|
|
376
|
+
: (swept.matrix.heldIn[at] ?? swept.matrix.configurations));
|
|
377
|
+
const untested = capped(capped(capped(ownClass, row.maxClass), rootsCeiling(input, finding, candidate, configurations)), judged === undefined ? "certain" : ceilingOf(judged.id));
|
|
321
378
|
const generated = input.files.generated?.has(finding.position.path) === true;
|
|
322
379
|
const fixability = generated
|
|
323
380
|
? "none"
|
|
@@ -331,7 +388,7 @@ export function completed(input, findings, kinds = KINDS) {
|
|
|
331
388
|
? undefined
|
|
332
389
|
: candidate !== undefined
|
|
333
390
|
? input.dependencies.lastUses.get(candidate.id)
|
|
334
|
-
: finding.code ===
|
|
391
|
+
: finding.code === CODE.fileNeverImported
|
|
335
392
|
? input.dependencies.fileLastUses.get(finding.position.path)
|
|
336
393
|
: undefined;
|
|
337
394
|
return {
|
|
@@ -341,16 +398,14 @@ export function completed(input, findings, kinds = KINDS) {
|
|
|
341
398
|
position: finding.position,
|
|
342
399
|
symbol: finding.symbol,
|
|
343
400
|
reachabilityClass,
|
|
344
|
-
confidence:
|
|
401
|
+
confidence: used ? "possible" : untested,
|
|
402
|
+
...(used ? { componentConfidence: untested } : {}),
|
|
345
403
|
...(candidate === undefined ? {} : { livenessRelation: candidate.relation }),
|
|
346
404
|
testOnly: judged !== undefined && counts?.production === 0 && counts.test > 0,
|
|
347
405
|
generated,
|
|
348
406
|
component,
|
|
349
407
|
retainedBy: [],
|
|
350
|
-
configurations
|
|
351
|
-
(judged === undefined && !part
|
|
352
|
-
? swept.matrix.configurations
|
|
353
|
-
: (swept.matrix.heldIn[at] ?? swept.matrix.configurations)),
|
|
408
|
+
configurations,
|
|
354
409
|
consumersLoaded: input.boundary.consumers.loaded,
|
|
355
410
|
fixability,
|
|
356
411
|
severity: severityOf(input, finding.code, row.defaultSeverity),
|
|
@@ -365,7 +420,7 @@ export function completed(input, findings, kinds = KINDS) {
|
|
|
365
420
|
/**
|
|
366
421
|
* The findings with every finding of a dead component capped by the lowest confidence
|
|
367
422
|
* among the component's root members, so the minimum confidence reports or withholds a
|
|
368
|
-
* component whole.
|
|
423
|
+
* component whole but for the findings test-used code lowers on their own.
|
|
369
424
|
*/
|
|
370
425
|
function componentCapped(findings, dead) {
|
|
371
426
|
const lowest = new Map();
|
|
@@ -373,16 +428,27 @@ function componentCapped(findings, dead) {
|
|
|
373
428
|
const { id, root } = finding.component;
|
|
374
429
|
if (root && dead.has(id)) {
|
|
375
430
|
const held = lowest.get(id);
|
|
376
|
-
|
|
431
|
+
const own = componentConfidenceOf(finding);
|
|
432
|
+
lowest.set(id, held === undefined ? own : capped(held, own));
|
|
377
433
|
}
|
|
378
434
|
}
|
|
379
435
|
return findings.map((finding) => {
|
|
380
436
|
const cap = lowest.get(finding.component.id);
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
437
|
+
if (cap === undefined) {
|
|
438
|
+
return finding;
|
|
439
|
+
}
|
|
440
|
+
const own = finding.componentConfidence;
|
|
441
|
+
return {
|
|
442
|
+
...finding,
|
|
443
|
+
confidence: capped(finding.confidence, cap),
|
|
444
|
+
...(own === undefined ? {} : { componentConfidence: capped(own, cap) }),
|
|
445
|
+
};
|
|
384
446
|
});
|
|
385
447
|
}
|
|
448
|
+
/** The confidence a finding gives its component's cap and the dials. */
|
|
449
|
+
function componentConfidenceOf(finding) {
|
|
450
|
+
return finding.componentConfidence ?? finding.confidence;
|
|
451
|
+
}
|
|
386
452
|
/** Whether the dials withhold a finding by its own severity or confidence. */
|
|
387
453
|
function dialed(finding, minConfidence) {
|
|
388
454
|
return finding.severity === "allow" || RANK[finding.confidence] < RANK[minConfidence];
|
|
@@ -395,9 +461,10 @@ function dialed(finding, minConfidence) {
|
|
|
395
461
|
* root withholds itself alone.
|
|
396
462
|
*/
|
|
397
463
|
export function dialsOf(input, findings) {
|
|
398
|
-
const minConfidence = input.config.analysis.
|
|
464
|
+
const minConfidence = input.config.analysis.min_confidence;
|
|
399
465
|
const components = new Set(findings
|
|
400
|
-
.filter((finding) => finding.component.root &&
|
|
466
|
+
.filter((finding) => finding.component.root &&
|
|
467
|
+
dialed({ ...finding, confidence: componentConfidenceOf(finding) }, minConfidence))
|
|
401
468
|
.map((finding) => finding.component.id));
|
|
402
469
|
return {
|
|
403
470
|
allows: (code) => {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
+
import { CODE } from "../contract/contract.generated.js";
|
|
1
2
|
import { declarationKey } from "../dependencies.js";
|
|
2
3
|
import { renderRef } from "../ref.js";
|
|
3
4
|
import { nonCodeArtifacts } from "./non-code-artifacts.js";
|
|
4
|
-
const UNUSED_DEPENDENCY = "DS1601";
|
|
5
|
-
const FILE_NEVER_IMPORTED = "DS1502";
|
|
6
5
|
const MESSAGES = {
|
|
7
6
|
dependency: "no import in the target needs the declared dependency",
|
|
8
7
|
"dev-dependency": "no import in the target needs the declared development dependency",
|
|
@@ -25,7 +24,7 @@ function deadCode(input) {
|
|
|
25
24
|
const reachedOnly = new Set(swept.sweep.candidates.filter((one) => one.relation === "reachability").map((one) => one.id));
|
|
26
25
|
for (const finding of nonCodeArtifacts(input)) {
|
|
27
26
|
const id = files.get(finding.position.path);
|
|
28
|
-
if (finding.code ===
|
|
27
|
+
if (finding.code === CODE.fileNeverImported && id !== undefined) {
|
|
29
28
|
dead.add(id);
|
|
30
29
|
if (finding.unreachableOnly === true) {
|
|
31
30
|
reachedOnly.add(id);
|
|
@@ -57,7 +56,7 @@ function unusedDependencies(input, dependencies) {
|
|
|
57
56
|
const atPossible = unbuilt || users?.some((id) => published.has(id)) === true;
|
|
58
57
|
return [
|
|
59
58
|
{
|
|
60
|
-
code:
|
|
59
|
+
code: CODE.unusedDependency,
|
|
61
60
|
position: dependency.position,
|
|
62
61
|
symbol: {
|
|
63
62
|
ref: renderRef(dependency.manifest, {
|
|
@@ -80,5 +79,5 @@ function unusedDependencies(input, dependencies) {
|
|
|
80
79
|
];
|
|
81
80
|
});
|
|
82
81
|
}
|
|
83
|
-
/** The findings of the dependencies
|
|
82
|
+
/** The findings of the dependencies family, `DS1700` to `DS1799`. */
|
|
84
83
|
export const dependenciesAndModuleMachinery = (input) => unusedDependencies(input, input.dependencies);
|
|
@@ -12,7 +12,7 @@ import { readsAndWrites } from "./reads-and-writes.js";
|
|
|
12
12
|
import { selfCheck } from "./self-check.js";
|
|
13
13
|
import { fallenMembers, unusedDeclarations } from "./unused-declarations.js";
|
|
14
14
|
import { visibilityNarrowing } from "./visibility-narrowing.js";
|
|
15
|
-
/** Every
|
|
15
|
+
/** Every emitter this analyzer runs, by the name its tests select it by. */
|
|
16
16
|
export const EMITTERS = new Map([
|
|
17
17
|
["unused-declarations", unusedDeclarations],
|
|
18
18
|
["visibility-narrowing", visibilityNarrowing],
|
|
@@ -51,7 +51,7 @@ function markupKeys(input) {
|
|
|
51
51
|
.filter((symbol) => live.has(symbol.parent))
|
|
52
52
|
.map((symbol) => declarationKey(symbol.ref, symbol.position.path)));
|
|
53
53
|
}
|
|
54
|
-
/** Every
|
|
54
|
+
/** Every emitter's findings over one sweep, and those of any further emitter, completed by one step. */
|
|
55
55
|
function completedOver(input, ...more) {
|
|
56
56
|
return completed(input, [...EMITTERS.values(), ...more].flatMap((emit) => emit(input)));
|
|
57
57
|
}
|
|
@@ -70,8 +70,8 @@ function dialsOver(input, found, suppressed, unmarked) {
|
|
|
70
70
|
}
|
|
71
71
|
/**
|
|
72
72
|
* Every finding of one run swept under its suppressions' marks, decided in one pass: each
|
|
73
|
-
*
|
|
74
|
-
* finding each record claims withheld, the rest withheld by the dials, so a root one
|
|
73
|
+
* emitter's findings completed by one step, so every component number is minted once, the
|
|
74
|
+
* finding each record claims withheld, the rest withheld by the dials, so a root one emitter
|
|
75
75
|
* reports withholds what another reports in its component, and every declared edge
|
|
76
76
|
* evaluated over what remains, so a pending finding is in its evaluation alone.
|
|
77
77
|
*/
|
|
@@ -86,7 +86,7 @@ function atPossible(input) {
|
|
|
86
86
|
const { config } = input;
|
|
87
87
|
return {
|
|
88
88
|
...input,
|
|
89
|
-
config: { ...config, analysis: { ...config.analysis,
|
|
89
|
+
config: { ...config, analysis: { ...config.analysis, min_confidence: "possible" } },
|
|
90
90
|
};
|
|
91
91
|
}
|
|
92
92
|
/**
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
+
import { CODE } from "../contract/contract.generated.js";
|
|
1
2
|
import { byPosition } from "../position.js";
|
|
2
|
-
const UNUSED_INTERFACE = "DS1201";
|
|
3
|
-
const UNCALLED_INTERFACE_METHOD = "DS1203";
|
|
4
3
|
/** What each finding says, by what the sweep found about the subject's references. */
|
|
5
4
|
const UNUSED_MESSAGE = "nothing in the target names the interface as a type";
|
|
6
5
|
const UNUSED_DEAD_MESSAGE = "the interface is named as a type only from declarations that are themselves dead";
|
|
@@ -45,7 +44,7 @@ function findingOf(code, symbol, message, implementations) {
|
|
|
45
44
|
}
|
|
46
45
|
/**
|
|
47
46
|
* The findings of the interfaces family, `DS1200` to `DS1299`: `DS1201`, a dead
|
|
48
|
-
* interface, whose members fall with it unreported; and `
|
|
47
|
+
* interface, whose members fall with it unreported; and `DS1202`, a method of a live
|
|
49
48
|
* interface no reference names, unless it is a marker, a method whose every
|
|
50
49
|
* implementation has an empty body. A test file's declaration is neither kind's subject,
|
|
51
50
|
* because a production sweep counts none of the references it can have, save an interface
|
|
@@ -60,7 +59,7 @@ export const interfaces = (input) => {
|
|
|
60
59
|
continue;
|
|
61
60
|
}
|
|
62
61
|
if (symbol.kind === "interface") {
|
|
63
|
-
found.push(findingOf(
|
|
62
|
+
found.push(findingOf(CODE.unusedInterface, symbol, unusedMessage(candidate), run.implementationsOf(candidate.id)));
|
|
64
63
|
continue;
|
|
65
64
|
}
|
|
66
65
|
if (symbol.kind !== "interface-method" ||
|
|
@@ -69,7 +68,7 @@ export const interfaces = (input) => {
|
|
|
69
68
|
marker(input, candidate.id)) {
|
|
70
69
|
continue;
|
|
71
70
|
}
|
|
72
|
-
found.push(findingOf(
|
|
71
|
+
found.push(findingOf(CODE.uncalledInterfaceMethod, symbol, UNCALLED_MESSAGE, run.implementationsOf(symbol.parent)));
|
|
73
72
|
}
|
|
74
73
|
return found;
|
|
75
74
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { CODE } from "../contract/contract.generated.js";
|
|
1
2
|
import { KINDS } from "../kinds.js";
|
|
2
3
|
import { parameterRef } from "../ref.js";
|
|
3
|
-
import {
|
|
4
|
+
import {} from "../intra-function-parts.js";
|
|
4
5
|
import { reachedWithTheTests } from "../sweep.js";
|
|
5
6
|
/** A list of rules as a sentence names them. */
|
|
6
7
|
function spelled(rules) {
|
|
@@ -28,7 +29,8 @@ function overlapClause(overlap, part) {
|
|
|
28
29
|
* caller may pass a function used as a value more arguments than it declares, so there one
|
|
29
30
|
* a read parameter follows is renamed to an underscore name, which the compilers read as
|
|
30
31
|
* declared unread, and one no read parameter follows is deleted: kept where the call's
|
|
31
|
-
* inference may read its annotation, at `probable` where nothing reads the annotation
|
|
32
|
+
* inference may read its annotation, at `probable` where nothing reads the annotation or
|
|
33
|
+
* where it is a name a contextually typed object pattern binds.
|
|
32
34
|
*/
|
|
33
35
|
function parameterVerdict(part, free, freeButValued) {
|
|
34
36
|
const place = part.parameter;
|
|
@@ -45,7 +47,8 @@ function parameterVerdict(part, free, freeButValued) {
|
|
|
45
47
|
if (place?.annotated === "steers") {
|
|
46
48
|
return undefined;
|
|
47
49
|
}
|
|
48
|
-
|
|
50
|
+
const unseen = place?.annotated === "free" || place?.contextualPattern === true;
|
|
51
|
+
return { fix: "delete", confidence: unseen ? "probable" : "certain" };
|
|
49
52
|
}
|
|
50
53
|
/**
|
|
51
54
|
* Whether one part is reported. A parameter needs its function's signature free to
|
|
@@ -60,9 +63,9 @@ function reported(input, part, callersUnknown) {
|
|
|
60
63
|
}
|
|
61
64
|
const { free, freeButValued, discardedEverywhere } = input.intraFunction;
|
|
62
65
|
switch (part.code) {
|
|
63
|
-
case
|
|
66
|
+
case CODE.unusedParameter:
|
|
64
67
|
return parameterVerdict(part, free, freeButValued) !== undefined;
|
|
65
|
-
case
|
|
68
|
+
case CODE.unusedResult:
|
|
66
69
|
return (free.has(part.declaration) &&
|
|
67
70
|
discardedEverywhere.has(part.declaration) &&
|
|
68
71
|
!callersUnknown.has(part.declaration));
|
|
@@ -71,7 +74,7 @@ function reported(input, part, callersUnknown) {
|
|
|
71
74
|
}
|
|
72
75
|
}
|
|
73
76
|
/**
|
|
74
|
-
* The findings of the
|
|
77
|
+
* The findings of the inside-functions family, `DS1401` to `DS1406`, each about a part of
|
|
75
78
|
* the declaration its reference names, at the part's own position, whatever the sweep
|
|
76
79
|
* decided about a production declaration. A part of test code is reported only where the
|
|
77
80
|
* run that counts test references reaches its declaration and that is no test of dead
|
|
@@ -95,7 +98,7 @@ export const intraFunction = (input) => {
|
|
|
95
98
|
return symbol === undefined ? [] : [symbol.id];
|
|
96
99
|
}));
|
|
97
100
|
const { declared, loaded } = input.boundary.consumers;
|
|
98
|
-
const closed = input.config.
|
|
101
|
+
const closed = input.config.consumers.complete && declared.every((id) => loaded.includes(id));
|
|
99
102
|
const published = (id) => {
|
|
100
103
|
const symbol = byId.get(id);
|
|
101
104
|
return (symbol !== undefined &&
|
|
@@ -112,11 +115,11 @@ export const intraFunction = (input) => {
|
|
|
112
115
|
const found = [];
|
|
113
116
|
for (const part of input.intraFunction.parts) {
|
|
114
117
|
const declaration = byId.get(part.declaration);
|
|
115
|
-
const overlap = KINDS.get(part.code)?.overlap ?? [];
|
|
118
|
+
const overlap = KINDS.get(part.code)?.overlap?.ts ?? [];
|
|
116
119
|
if (declaration === undefined || !judged(declaration.id) || !reported(input, part, unknown)) {
|
|
117
120
|
continue;
|
|
118
121
|
}
|
|
119
|
-
const verdict = part.code ===
|
|
122
|
+
const verdict = part.code === CODE.unusedParameter
|
|
120
123
|
? parameterVerdict(part, input.intraFunction.free, input.intraFunction.freeButValued)
|
|
121
124
|
: undefined;
|
|
122
125
|
const message = verdict?.fix === "rename"
|
|
@@ -126,7 +129,7 @@ export const intraFunction = (input) => {
|
|
|
126
129
|
code: part.code,
|
|
127
130
|
position: part.position,
|
|
128
131
|
symbol: {
|
|
129
|
-
...(part.code ===
|
|
132
|
+
...(part.code === CODE.unusedParameter
|
|
130
133
|
? { ref: parameterRef(declaration.ref, part.name), holder: declaration.ref }
|
|
131
134
|
: { ref: declaration.ref }),
|
|
132
135
|
kind: part.kind,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
const FILE_NEVER_IMPORTED = "DS1502";
|
|
1
|
+
import { CODE } from "../contract/contract.generated.js";
|
|
3
2
|
/** The subject kind of a finding about a source file, from the finding schema's vocabulary. */
|
|
4
3
|
const FILE_SUBJECT = "file";
|
|
5
4
|
/** A file's subject starts at its first character. */
|
|
@@ -11,8 +10,8 @@ const FILE_START = 1;
|
|
|
11
10
|
* derived matrix is never complete.
|
|
12
11
|
*/
|
|
13
12
|
function completeMatrix(config) {
|
|
14
|
-
return (config.analysis.
|
|
15
|
-
config.analysis.configurations.some((entry) =>
|
|
13
|
+
return (config.analysis.matrix.complete &&
|
|
14
|
+
config.analysis.configurations.some((entry) => "project" in entry));
|
|
16
15
|
}
|
|
17
16
|
/** One finding about a whole file. */
|
|
18
17
|
function fileFinding(code, file, message) {
|
|
@@ -40,7 +39,7 @@ function fileNeverBuilt({ config, swept, files }) {
|
|
|
40
39
|
const message = `no project of the build matrix includes this file (${swept.matrix.configurations.join(", ")})`;
|
|
41
40
|
return files.tree
|
|
42
41
|
.filter((file) => !built.has(file.path))
|
|
43
|
-
.map((file) => fileFinding(
|
|
42
|
+
.map((file) => fileFinding(CODE.fileNeverBuilt, file, message));
|
|
44
43
|
}
|
|
45
44
|
/**
|
|
46
45
|
* The paths of the files a reference from a live declaration of another file reaches, a
|
|
@@ -125,13 +124,13 @@ function fileNeverImported(input) {
|
|
|
125
124
|
const file = { path: symbol.position.path, ref: symbol.ref, endLine: symbol.endLine };
|
|
126
125
|
return referenced.has(symbol.position.path)
|
|
127
126
|
? {
|
|
128
|
-
...fileFinding(
|
|
127
|
+
...fileFinding(CODE.fileNeverImported, file, "only code that is itself dead imports this file, and no root names it or a declaration in it"),
|
|
129
128
|
unreachableOnly: true,
|
|
130
129
|
}
|
|
131
|
-
: fileFinding(
|
|
130
|
+
: fileFinding(CODE.fileNeverImported, file, "no import reaches this file and no root names it or a declaration in it");
|
|
132
131
|
});
|
|
133
132
|
}
|
|
134
|
-
/** The findings of the
|
|
133
|
+
/** The findings of the files family, `DS1600` to `DS1699`. */
|
|
135
134
|
export const nonCodeArtifacts = (input) => [
|
|
136
135
|
...fileNeverBuilt(input),
|
|
137
136
|
...fileNeverImported(input),
|