@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
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { CODE, type Confidence } from "../contract/contract.generated.ts";
|
|
2
2
|
import type { Finding } from "../finding.ts";
|
|
3
3
|
import { KINDS } from "../kinds.ts";
|
|
4
4
|
import { parameterRef } from "../ref.ts";
|
|
5
|
-
import {
|
|
5
|
+
import { type PartFact } from "../intra-function-parts.ts";
|
|
6
6
|
import { reachedWithTheTests } from "../sweep.ts";
|
|
7
7
|
import type { Emitter, EmitterInput } from "./emitter.ts";
|
|
8
8
|
|
|
@@ -42,7 +42,8 @@ type ParameterVerdict =
|
|
|
42
42
|
* caller may pass a function used as a value more arguments than it declares, so there one
|
|
43
43
|
* a read parameter follows is renamed to an underscore name, which the compilers read as
|
|
44
44
|
* declared unread, and one no read parameter follows is deleted: kept where the call's
|
|
45
|
-
* inference may read its annotation, at `probable` where nothing reads the annotation
|
|
45
|
+
* inference may read its annotation, at `probable` where nothing reads the annotation or
|
|
46
|
+
* where it is a name a contextually typed object pattern binds.
|
|
46
47
|
*/
|
|
47
48
|
function parameterVerdict(
|
|
48
49
|
part: PartFact,
|
|
@@ -63,7 +64,8 @@ function parameterVerdict(
|
|
|
63
64
|
if (place?.annotated === "steers") {
|
|
64
65
|
return undefined;
|
|
65
66
|
}
|
|
66
|
-
|
|
67
|
+
const unseen = place?.annotated === "free" || place?.contextualPattern === true;
|
|
68
|
+
return { fix: "delete", confidence: unseen ? "probable" : "certain" };
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
/**
|
|
@@ -83,9 +85,9 @@ function reported(
|
|
|
83
85
|
}
|
|
84
86
|
const { free, freeButValued, discardedEverywhere } = input.intraFunction;
|
|
85
87
|
switch (part.code) {
|
|
86
|
-
case
|
|
88
|
+
case CODE.unusedParameter:
|
|
87
89
|
return parameterVerdict(part, free, freeButValued) !== undefined;
|
|
88
|
-
case
|
|
90
|
+
case CODE.unusedResult:
|
|
89
91
|
return (
|
|
90
92
|
free.has(part.declaration) &&
|
|
91
93
|
discardedEverywhere.has(part.declaration) &&
|
|
@@ -97,7 +99,7 @@ function reported(
|
|
|
97
99
|
}
|
|
98
100
|
|
|
99
101
|
/**
|
|
100
|
-
* The findings of the
|
|
102
|
+
* The findings of the inside-functions family, `DS1401` to `DS1406`, each about a part of
|
|
101
103
|
* the declaration its reference names, at the part's own position, whatever the sweep
|
|
102
104
|
* decided about a production declaration. A part of test code is reported only where the
|
|
103
105
|
* run that counts test references reaches its declaration and that is no test of dead
|
|
@@ -125,7 +127,7 @@ export const intraFunction: Emitter = (input) => {
|
|
|
125
127
|
}),
|
|
126
128
|
);
|
|
127
129
|
const { declared, loaded } = input.boundary.consumers;
|
|
128
|
-
const closed = input.config.
|
|
130
|
+
const closed = input.config.consumers.complete && declared.every((id) => loaded.includes(id));
|
|
129
131
|
const published = (id: string): boolean => {
|
|
130
132
|
const symbol = byId.get(id);
|
|
131
133
|
return (
|
|
@@ -144,12 +146,12 @@ export const intraFunction: Emitter = (input) => {
|
|
|
144
146
|
const found: Finding[] = [];
|
|
145
147
|
for (const part of input.intraFunction.parts) {
|
|
146
148
|
const declaration = byId.get(part.declaration);
|
|
147
|
-
const overlap = KINDS.get(part.code)?.overlap ?? [];
|
|
149
|
+
const overlap = KINDS.get(part.code)?.overlap?.ts ?? [];
|
|
148
150
|
if (declaration === undefined || !judged(declaration.id) || !reported(input, part, unknown)) {
|
|
149
151
|
continue;
|
|
150
152
|
}
|
|
151
153
|
const verdict =
|
|
152
|
-
part.code ===
|
|
154
|
+
part.code === CODE.unusedParameter
|
|
153
155
|
? parameterVerdict(part, input.intraFunction.free, input.intraFunction.freeButValued)
|
|
154
156
|
: undefined;
|
|
155
157
|
const message =
|
|
@@ -160,7 +162,7 @@ export const intraFunction: Emitter = (input) => {
|
|
|
160
162
|
code: part.code,
|
|
161
163
|
position: part.position,
|
|
162
164
|
symbol: {
|
|
163
|
-
...(part.code ===
|
|
165
|
+
...(part.code === CODE.unusedParameter
|
|
164
166
|
? { ref: parameterRef(declaration.ref, part.name), holder: declaration.ref }
|
|
165
167
|
: { ref: declaration.ref }),
|
|
166
168
|
kind: part.kind,
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { CODE } from "../contract/contract.generated.ts";
|
|
2
|
+
import type { ResolvedConfig } from "../config.ts";
|
|
2
3
|
import type { Finding } from "../finding.ts";
|
|
3
4
|
import type { Emitter, EmitterInput } from "./emitter.ts";
|
|
4
5
|
|
|
5
|
-
const FILE_NEVER_BUILT = "DS1501";
|
|
6
|
-
const FILE_NEVER_IMPORTED = "DS1502";
|
|
7
|
-
|
|
8
6
|
/** The subject kind of a finding about a source file, from the finding schema's vocabulary. */
|
|
9
7
|
const FILE_SUBJECT = "file";
|
|
10
8
|
|
|
@@ -17,10 +15,10 @@ const FILE_START = 1;
|
|
|
17
15
|
* completeness and listing no project declares it of a matrix the run derived, and a
|
|
18
16
|
* derived matrix is never complete.
|
|
19
17
|
*/
|
|
20
|
-
function completeMatrix(config:
|
|
18
|
+
function completeMatrix(config: ResolvedConfig): boolean {
|
|
21
19
|
return (
|
|
22
|
-
config.analysis.
|
|
23
|
-
config.analysis.configurations.some((entry) =>
|
|
20
|
+
config.analysis.matrix.complete &&
|
|
21
|
+
config.analysis.configurations.some((entry) => "project" in entry)
|
|
24
22
|
);
|
|
25
23
|
}
|
|
26
24
|
|
|
@@ -57,7 +55,7 @@ function fileNeverBuilt({ config, swept, files }: EmitterInput): readonly Findin
|
|
|
57
55
|
const message = `no project of the build matrix includes this file (${swept.matrix.configurations.join(", ")})`;
|
|
58
56
|
return files.tree
|
|
59
57
|
.filter((file) => !built.has(file.path))
|
|
60
|
-
.map((file) => fileFinding(
|
|
58
|
+
.map((file) => fileFinding(CODE.fileNeverBuilt, file, message));
|
|
61
59
|
}
|
|
62
60
|
|
|
63
61
|
/** The files of the run, each with whether a reference written in another file reaches it. */
|
|
@@ -152,21 +150,21 @@ function fileNeverImported(input: EmitterInput): readonly Finding[] {
|
|
|
152
150
|
return referenced.has(symbol.position.path)
|
|
153
151
|
? {
|
|
154
152
|
...fileFinding(
|
|
155
|
-
|
|
153
|
+
CODE.fileNeverImported,
|
|
156
154
|
file,
|
|
157
155
|
"only code that is itself dead imports this file, and no root names it or a declaration in it",
|
|
158
156
|
),
|
|
159
157
|
unreachableOnly: true as const,
|
|
160
158
|
}
|
|
161
159
|
: fileFinding(
|
|
162
|
-
|
|
160
|
+
CODE.fileNeverImported,
|
|
163
161
|
file,
|
|
164
162
|
"no import reaches this file and no root names it or a declaration in it",
|
|
165
163
|
);
|
|
166
164
|
});
|
|
167
165
|
}
|
|
168
166
|
|
|
169
|
-
/** The findings of the
|
|
167
|
+
/** The findings of the files family, `DS1600` to `DS1699`. */
|
|
170
168
|
export const nonCodeArtifacts: Emitter = (input) => [
|
|
171
169
|
...fileNeverBuilt(input),
|
|
172
170
|
...fileNeverImported(input),
|
|
@@ -1,17 +1,20 @@
|
|
|
1
|
+
import { CODE } from "../contract/contract.generated.ts";
|
|
1
2
|
import type { Finding, FindingDetails, FindingPosition } from "../finding.ts";
|
|
2
3
|
import type { InventorySymbol } from "../inventory.ts";
|
|
3
4
|
import type { MatrixCandidate } from "../matrix.ts";
|
|
4
5
|
import { positionKey } from "../position.ts";
|
|
5
6
|
import { declarationKey } from "../ref.ts";
|
|
6
7
|
import type { Emitter, EmitterInput } from "./emitter.ts";
|
|
8
|
+
import { bytewise } from "../text/compare.ts";
|
|
7
9
|
|
|
8
|
-
/** The codes
|
|
9
|
-
type ReadsAndWritesCode =
|
|
10
|
+
/** The codes this emitter reports. */
|
|
11
|
+
type ReadsAndWritesCode =
|
|
12
|
+
typeof CODE.writeOnlySymbol | typeof CODE.unusedEnumMember | typeof CODE.unusedTypeParameter;
|
|
10
13
|
|
|
11
|
-
/** The containers whose type parameters `
|
|
12
|
-
const SIGNATURES: ReadonlySet<string> = new Set(["function", "method"]);
|
|
14
|
+
/** The containers whose type parameters `DS1006` reports: a function, a class method and a type alias. */
|
|
15
|
+
const SIGNATURES: ReadonlySet<string> = new Set(["function", "method", "type"]);
|
|
13
16
|
|
|
14
|
-
/** What
|
|
17
|
+
/** What this emitter reads of one run, indexed by declaration. */
|
|
15
18
|
interface Run {
|
|
16
19
|
readonly input: EmitterInput;
|
|
17
20
|
readonly symbols: ReadonlyMap<string, InventorySymbol>;
|
|
@@ -121,7 +124,7 @@ function writeOnlyMessage(
|
|
|
121
124
|
: "never read";
|
|
122
125
|
const cascade = [
|
|
123
126
|
...parametersOnlyNamedBy(run, namedBy, symbol).map((name) => `type parameter ${name}`),
|
|
124
|
-
...[...held.alone].sort().map((name) => `import ${name}`),
|
|
127
|
+
...[...held.alone].sort(bytewise).map((name) => `import ${name}`),
|
|
125
128
|
];
|
|
126
129
|
const word = SUBJECT_WORDS[symbol.kind] ?? "member";
|
|
127
130
|
const message = `${word} ${symbol.name} is written ${writes} and ${read}`;
|
|
@@ -185,7 +188,7 @@ function writeOnlySymbols(run: Run): readonly Finding[] {
|
|
|
185
188
|
continue;
|
|
186
189
|
}
|
|
187
190
|
found.push(
|
|
188
|
-
findingAbout(symbol,
|
|
191
|
+
findingAbout(symbol, CODE.writeOnlySymbol, writeOnlyMessage(run, namedBy, symbol, held), {
|
|
189
192
|
writePositions: held.writes,
|
|
190
193
|
}),
|
|
191
194
|
);
|
|
@@ -214,21 +217,22 @@ function unnamed(run: Run, kind: InventorySymbol["kind"]): readonly InventorySym
|
|
|
214
217
|
}
|
|
215
218
|
|
|
216
219
|
/**
|
|
217
|
-
* `
|
|
220
|
+
* `DS1005`: a member of an enum declaration that nothing names. A member an enum-group
|
|
218
221
|
* record holds back is no candidate, so a member a conversion can produce never reaches
|
|
219
222
|
* this kind.
|
|
220
223
|
*/
|
|
221
224
|
function unusedEnumMembers(run: Run): readonly Finding[] {
|
|
222
225
|
return unnamed(run, "enum-member").map((symbol) =>
|
|
223
|
-
findingAbout(symbol,
|
|
226
|
+
findingAbout(symbol, CODE.unusedEnumMember, `enum member ${symbol.name} is named nowhere`),
|
|
224
227
|
);
|
|
225
228
|
}
|
|
226
229
|
|
|
227
230
|
/**
|
|
228
|
-
* `
|
|
229
|
-
* declaration's signature nor its body names.
|
|
230
|
-
* a
|
|
231
|
-
*
|
|
231
|
+
* `DS1006`: a type parameter of a function, a class method or a type alias that neither
|
|
232
|
+
* the declaration's signature nor its body names. An alias names no type of its own, so
|
|
233
|
+
* a parameter its type never names makes no two instantiations distinct. One of a class,
|
|
234
|
+
* an interface or a method signature is never reported: a class or an interface merges
|
|
235
|
+
* only with declarations of the same parameters.
|
|
232
236
|
*/
|
|
233
237
|
function unusedTypeParameters(run: Run): readonly Finding[] {
|
|
234
238
|
return unnamed(run, "type-parameter")
|
|
@@ -236,7 +240,7 @@ function unusedTypeParameters(run: Run): readonly Finding[] {
|
|
|
236
240
|
.map((symbol) =>
|
|
237
241
|
findingAbout(
|
|
238
242
|
symbol,
|
|
239
|
-
|
|
243
|
+
CODE.unusedTypeParameter,
|
|
240
244
|
`type parameter ${symbol.name} is named in neither its signature nor its body`,
|
|
241
245
|
),
|
|
242
246
|
);
|
|
@@ -256,8 +260,8 @@ export function writeOnlyDeclarations(input: EmitterInput): ReadonlySet<string>
|
|
|
256
260
|
}
|
|
257
261
|
|
|
258
262
|
/**
|
|
259
|
-
* The findings of the reads-and-writes
|
|
260
|
-
* each code's in site order.
|
|
263
|
+
* The findings of the reads-and-writes kinds, `DS1301`, `DS1005` and `DS1006`, in that
|
|
264
|
+
* order and each code's in site order.
|
|
261
265
|
*/
|
|
262
266
|
export const readsAndWrites: Emitter = (input) => {
|
|
263
267
|
const run = indexed(input);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CODE } from "../contract/contract.generated.ts";
|
|
1
2
|
import { REPOSITORY_DOCUMENT } from "../config.ts";
|
|
2
3
|
import type { Finding, FindingDetails, SuppressionEntry } from "../finding.ts";
|
|
3
4
|
import type { InventorySymbol } from "../inventory.ts";
|
|
@@ -6,19 +7,12 @@ import type { Position } from "../position.ts";
|
|
|
6
7
|
import {
|
|
7
8
|
isDependencyCode,
|
|
8
9
|
isUnsuppressibleCode,
|
|
9
|
-
STALE_SUPPRESSION,
|
|
10
|
-
SUPPRESSION_WITHOUT_REASON,
|
|
11
10
|
type Mechanism,
|
|
12
11
|
type Refusal,
|
|
13
12
|
type SuppressionRecord,
|
|
14
13
|
} from "../suppress.ts";
|
|
15
14
|
import type { Emitter } from "./emitter.ts";
|
|
16
|
-
|
|
17
|
-
/** The code a configured root that matches nothing is reported under. */
|
|
18
|
-
const UNMATCHED_ROOT = "DS1704";
|
|
19
|
-
|
|
20
|
-
/** The code a configured declaration that names nothing is reported under. */
|
|
21
|
-
const UNMATCHED_DECLARATION = "DS1706";
|
|
15
|
+
import { bytewise } from "../text/compare.ts";
|
|
22
16
|
|
|
23
17
|
/** The subject kind of a finding about a configured declaration. */
|
|
24
18
|
const DECLARATION_SUBJECT = "configured-declaration";
|
|
@@ -39,7 +33,7 @@ export interface StaleRecord {
|
|
|
39
33
|
readonly reports: readonly string[];
|
|
40
34
|
}
|
|
41
35
|
|
|
42
|
-
/** What the
|
|
36
|
+
/** What the configuration family reads: the run's refused suppressions and its configured roots. */
|
|
43
37
|
export interface SelfCheckFacts {
|
|
44
38
|
/** The suppressions the grammar refused with a finding, in reading order. */
|
|
45
39
|
readonly refusals: readonly Refusal[];
|
|
@@ -135,7 +129,7 @@ function refusedSuppressions(refusals: readonly Refusal[], files: FileRefs): rea
|
|
|
135
129
|
suppressionRef(refused.symbol, refused.path, files),
|
|
136
130
|
refused.site,
|
|
137
131
|
`${WRITTEN[refused.mechanism]} for ${refused.code} ${
|
|
138
|
-
refused.reported ===
|
|
132
|
+
refused.reported === CODE.suppressionWithoutReason
|
|
139
133
|
? "carries no reason"
|
|
140
134
|
: "names a symbol and no path"
|
|
141
135
|
}`,
|
|
@@ -146,7 +140,7 @@ function refusedSuppressions(refusals: readonly Refusal[], files: FileRefs): rea
|
|
|
146
140
|
|
|
147
141
|
/** One stale suppression as the report's stale-suppression record carries it. */
|
|
148
142
|
export interface StaleSuppression {
|
|
149
|
-
readonly code: typeof
|
|
143
|
+
readonly code: typeof CODE.staleSuppression;
|
|
150
144
|
readonly mechanism: Mechanism;
|
|
151
145
|
readonly entry: {
|
|
152
146
|
readonly code: string;
|
|
@@ -178,10 +172,10 @@ export function staleSuppressions(
|
|
|
178
172
|
bySite.set(key, held);
|
|
179
173
|
}
|
|
180
174
|
return [...bySite.values()].map(({ first, records }) => {
|
|
181
|
-
const codes = [...new Set(records.map((one) => one.record.code))].sort();
|
|
175
|
+
const codes = [...new Set(records.map((one) => one.record.code))].sort(bytewise);
|
|
182
176
|
const reports = [...new Set(records.flatMap((one) => one.reports))]
|
|
183
177
|
.filter((code) => !codes.includes(code))
|
|
184
|
-
.sort();
|
|
178
|
+
.sort(bytewise);
|
|
185
179
|
const rows = codes.filter(isUnsuppressibleCode);
|
|
186
180
|
const dependencies = first.mechanism === "inline" ? codes.filter(isDependencyCode) : [];
|
|
187
181
|
const settable = rows.filter((code) => !FIXED_SEVERITY_CODES.includes(code));
|
|
@@ -209,7 +203,7 @@ export function staleSuppressions(
|
|
|
209
203
|
? ""
|
|
210
204
|
: `, and ${declared}`;
|
|
211
205
|
return {
|
|
212
|
-
code:
|
|
206
|
+
code: CODE.staleSuppression,
|
|
213
207
|
mechanism: first.mechanism,
|
|
214
208
|
entry: {
|
|
215
209
|
code: first.code,
|
|
@@ -242,7 +236,7 @@ function unmatchedDeclarationFindings(
|
|
|
242
236
|
unmatched: readonly UnmatchedDeclaration[],
|
|
243
237
|
): readonly Finding[] {
|
|
244
238
|
return unmatched.map((one) => ({
|
|
245
|
-
code:
|
|
239
|
+
code: CODE.unmatchedConfiguredDeclaration,
|
|
246
240
|
position: documentPosition(one.document),
|
|
247
241
|
symbol: { ref: one.ref, kind: DECLARATION_SUBJECT, name: one.ref, sizeLines: 1 },
|
|
248
242
|
message: `${one.key} entry names no declaration in any project`,
|
|
@@ -262,7 +256,7 @@ export function unmatchedRootFindings(
|
|
|
262
256
|
): readonly Finding[] {
|
|
263
257
|
const position = documentPosition(document);
|
|
264
258
|
return sources.map((source) => ({
|
|
265
|
-
code:
|
|
259
|
+
code: CODE.unmatchedRoot,
|
|
266
260
|
position,
|
|
267
261
|
symbol: { ref: source, kind: ROOT_SUBJECT, name: source, sizeLines: 1 },
|
|
268
262
|
message:
|
|
@@ -273,7 +267,7 @@ export function unmatchedRootFindings(
|
|
|
273
267
|
}
|
|
274
268
|
|
|
275
269
|
/**
|
|
276
|
-
* The findings of the
|
|
270
|
+
* The findings of the configuration family, `DS9000` to `DS9099`: a suppression refused for
|
|
277
271
|
* carrying no reason or naming no path, and a configured root or declaration that named
|
|
278
272
|
* nothing. A
|
|
279
273
|
* suppression that matched nothing is a record of the report's stale suppressions, which
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { CODE } from "../contract/contract.generated.ts";
|
|
1
2
|
import type { Finding } from "../finding.ts";
|
|
2
3
|
import type { InventorySymbol, SymbolKind } from "../inventory.ts";
|
|
3
4
|
import type { MatrixCandidate } from "../matrix.ts";
|
|
4
5
|
import type { Emitter, EmitterInput } from "./emitter.ts";
|
|
6
|
+
import { testsUnseenAPI } from "./completion.ts";
|
|
5
7
|
import { unreachableExports } from "./visibility-narrowing.ts";
|
|
6
8
|
|
|
7
9
|
/** The kinds of declaration that are members of a class, an interface, a type or an enum. */
|
|
@@ -13,18 +15,18 @@ const MEMBERS: ReadonlySet<SymbolKind> = new Set([
|
|
|
13
15
|
]);
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
|
-
* The kinds of declaration no code of this
|
|
18
|
+
* The kinds of declaration no code of this emitter reports: an interface and a method an
|
|
17
19
|
* interface declares are the interface kinds' subjects, and a type parameter is the
|
|
18
20
|
* subject of the unused-type-parameter kind or of none.
|
|
19
21
|
*/
|
|
20
|
-
const
|
|
22
|
+
const NOT_REPORTED_HERE: ReadonlySet<SymbolKind> = new Set([
|
|
21
23
|
"file",
|
|
22
24
|
"interface",
|
|
23
25
|
"interface-method",
|
|
24
26
|
"type-parameter",
|
|
25
27
|
]);
|
|
26
28
|
|
|
27
|
-
/** The reader's word for each kind of declaration a finding of
|
|
29
|
+
/** The reader's word for each kind of declaration a finding of this emitter names. */
|
|
28
30
|
const WORDS: Readonly<Record<SymbolKind, string>> = {
|
|
29
31
|
file: "file",
|
|
30
32
|
function: "function",
|
|
@@ -55,21 +57,24 @@ interface Judged {
|
|
|
55
57
|
readonly deprecated: boolean;
|
|
56
58
|
/** Whether the unreachable-export kind reports it. */
|
|
57
59
|
readonly unreachable: boolean;
|
|
60
|
+
/** Whether it is a test with a target that is published API no declared consumer judges. */
|
|
61
|
+
readonly testsUnseenAPI: boolean;
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
/**
|
|
61
65
|
* The code one candidate is reported under, or none, each symbol once under the most
|
|
62
66
|
* specific code: a test of dead code first; no other test-file declaration or enum
|
|
63
67
|
* member nothing names; a test-support declaration test code references as test-only
|
|
64
|
-
* use; then deprecation, a test reference,
|
|
65
|
-
* unused export being the
|
|
68
|
+
* use; then deprecation, a test reference, or only dead production references beside a
|
|
69
|
+
* test that stays, and the unreferenced codes, an unimportable unused export being the
|
|
70
|
+
* unreachable kind's.
|
|
66
71
|
*/
|
|
67
72
|
function codeOf(judged: Judged): string | undefined {
|
|
68
73
|
const { candidate, symbol } = judged;
|
|
69
74
|
if (candidate.testOfDeadCode) {
|
|
70
|
-
return
|
|
75
|
+
return judged.testsUnseenAPI ? undefined : CODE.testOfDeadCode;
|
|
71
76
|
}
|
|
72
|
-
if ((judged.test && !candidate.unreferencedTest) ||
|
|
77
|
+
if ((judged.test && !candidate.unreferencedTest) || NOT_REPORTED_HERE.has(symbol.kind)) {
|
|
73
78
|
return undefined;
|
|
74
79
|
}
|
|
75
80
|
const referenced = candidate.productionRefs + candidate.testRefs > 0;
|
|
@@ -80,29 +85,33 @@ function codeOf(judged: Judged): string | undefined {
|
|
|
80
85
|
return unreferencedCode(symbol);
|
|
81
86
|
}
|
|
82
87
|
if (candidate.heldByTests === candidate.id) {
|
|
83
|
-
return
|
|
88
|
+
return CODE.testOnlyUse;
|
|
84
89
|
}
|
|
85
90
|
if (candidate.productionRefs === 0 && judged.deprecated) {
|
|
86
|
-
return
|
|
91
|
+
return CODE.deprecatedUnused;
|
|
87
92
|
}
|
|
88
|
-
if (
|
|
89
|
-
|
|
93
|
+
if (
|
|
94
|
+
candidate.testRefs > 0 &&
|
|
95
|
+
(candidate.productionRefs === 0 ||
|
|
96
|
+
(candidate.relation === "reachability" && candidate.keptByTests))
|
|
97
|
+
) {
|
|
98
|
+
return CODE.testOnlyUse;
|
|
90
99
|
}
|
|
91
100
|
if (MEMBERS.has(symbol.kind)) {
|
|
92
|
-
return
|
|
101
|
+
return CODE.unusedMember;
|
|
93
102
|
}
|
|
94
103
|
if (!symbol.exported) {
|
|
95
|
-
return
|
|
104
|
+
return CODE.unusedUnexported;
|
|
96
105
|
}
|
|
97
|
-
return judged.unreachable ? undefined :
|
|
106
|
+
return judged.unreachable ? undefined : CODE.unusedExported;
|
|
98
107
|
}
|
|
99
108
|
|
|
100
109
|
/** The code a declaration nothing references carries, by its shape and its visibility. */
|
|
101
110
|
function unreferencedCode(symbol: InventorySymbol): string {
|
|
102
111
|
if (MEMBERS.has(symbol.kind)) {
|
|
103
|
-
return
|
|
112
|
+
return CODE.unusedMember;
|
|
104
113
|
}
|
|
105
|
-
return symbol.exported ?
|
|
114
|
+
return symbol.exported ? CODE.unusedExported : CODE.unusedUnexported;
|
|
106
115
|
}
|
|
107
116
|
|
|
108
117
|
/** The visibility a member's message names, where it narrows the member's reach. */
|
|
@@ -127,15 +136,17 @@ function messageOf(code: string, judged: Judged): string {
|
|
|
127
136
|
const { candidate, symbol } = judged;
|
|
128
137
|
const word = WORDS[symbol.kind];
|
|
129
138
|
switch (code) {
|
|
130
|
-
case
|
|
139
|
+
case CODE.testOfDeadCode:
|
|
131
140
|
return TEST_OF_DEAD_CODE;
|
|
132
|
-
case
|
|
141
|
+
case CODE.deprecatedUnused:
|
|
133
142
|
return `deprecated ${word} has no production reference`;
|
|
134
|
-
case
|
|
135
|
-
return
|
|
143
|
+
case CODE.testOnlyUse:
|
|
144
|
+
return candidate.productionRefs === 0
|
|
145
|
+
? `${word} is referenced only from test files and never from production code`
|
|
146
|
+
: `${word} is referenced only from test files and from declarations that are themselves dead`;
|
|
136
147
|
}
|
|
137
148
|
const subject =
|
|
138
|
-
code ===
|
|
149
|
+
code === CODE.unusedMember
|
|
139
150
|
? `${visibilityWord(symbol)}${word}`
|
|
140
151
|
: symbol.kind === "export-alias"
|
|
141
152
|
? word
|
|
@@ -143,7 +154,7 @@ function messageOf(code: string, judged: Judged): string {
|
|
|
143
154
|
if (candidate.relation === "reachability") {
|
|
144
155
|
return `${subject} is referenced only from declarations that are themselves dead`;
|
|
145
156
|
}
|
|
146
|
-
if (code ===
|
|
157
|
+
if (code === CODE.unusedExported) {
|
|
147
158
|
return `${subject} has no reference in the target and none from any loaded consumer`;
|
|
148
159
|
}
|
|
149
160
|
return `${subject} has no reference in the target`;
|
|
@@ -195,25 +206,25 @@ function brandMembers(input: EmitterInput): ReadonlySet<string> {
|
|
|
195
206
|
/** The kinds of member a brand is: a data member of a type or of a class. */
|
|
196
207
|
const BRANDS: ReadonlySet<SymbolKind> = new Set(["type-member", "class-member"]);
|
|
197
208
|
|
|
198
|
-
/** The findings of the unused-
|
|
209
|
+
/** The findings of the unused-declaration kinds, `DS1001` to `DS1004` and `DS1102` to `DS1104`. */
|
|
199
210
|
export const unusedDeclarations: Emitter = (input) => [
|
|
200
|
-
...
|
|
211
|
+
...emitterFindings(input, false),
|
|
201
212
|
...unusedImports(input),
|
|
202
213
|
];
|
|
203
214
|
|
|
204
215
|
/**
|
|
205
|
-
* The import bindings their file never reads
|
|
206
|
-
*
|
|
216
|
+
* The import bindings their file never reads. A generated file's are the generator's to
|
|
217
|
+
* remove unless the run includes generated files.
|
|
207
218
|
*/
|
|
208
219
|
function unusedImports(input: EmitterInput): Finding[] {
|
|
209
220
|
const generated =
|
|
210
|
-
input.config.analysis.
|
|
221
|
+
input.config.analysis.generated_files === "include" ? undefined : input.files.generated;
|
|
211
222
|
return input.intraFunction.imports.flatMap((binding) =>
|
|
212
223
|
binding.used || generated?.has(binding.position.path) === true
|
|
213
224
|
? []
|
|
214
225
|
: [
|
|
215
226
|
{
|
|
216
|
-
code:
|
|
227
|
+
code: CODE.unusedImport,
|
|
217
228
|
position: binding.position,
|
|
218
229
|
symbol: { ref: binding.ref, kind: "import", name: binding.name, sizeLines: 1 },
|
|
219
230
|
message: `import binding ${binding.name} is never read in its file`,
|
|
@@ -227,13 +238,14 @@ function unusedImports(input: EmitterInput): Finding[] {
|
|
|
227
238
|
* a record on such a member is matched against, since the report carries the container's
|
|
228
239
|
* finding rather than one of the member's own.
|
|
229
240
|
*/
|
|
230
|
-
export const fallenMembers: Emitter = (input) =>
|
|
241
|
+
export const fallenMembers: Emitter = (input) => emitterFindings(input, true);
|
|
231
242
|
|
|
232
|
-
/**
|
|
233
|
-
function
|
|
243
|
+
/** This emitter's findings, or, where `fallen` is set, its dead containers' members' alone. */
|
|
244
|
+
function emitterFindings(input: EmitterInput, fallen: boolean): Finding[] {
|
|
234
245
|
const union = input.swept.matrix.union;
|
|
235
246
|
const dead = new Set(input.swept.sweep.candidates.map((candidate) => candidate.id));
|
|
236
247
|
const unreachable = unreachableExports(input);
|
|
248
|
+
const unseenAPI = testsUnseenAPI(input);
|
|
237
249
|
let brands: ReadonlySet<string> | undefined;
|
|
238
250
|
|
|
239
251
|
const found: Finding[] = [];
|
|
@@ -253,11 +265,12 @@ function familyFindings(input: EmitterInput, fallen: boolean): Finding[] {
|
|
|
253
265
|
test: union.test[at] === true,
|
|
254
266
|
deprecated: input.deprecated.has(symbol.id),
|
|
255
267
|
unreachable: unreachable.has(symbol.id),
|
|
268
|
+
testsUnseenAPI: candidate.testOfDeadCode && unseenAPI(candidate.id),
|
|
256
269
|
};
|
|
257
270
|
const code = codeOf(judged);
|
|
258
271
|
if (
|
|
259
272
|
code === undefined ||
|
|
260
|
-
(code ===
|
|
273
|
+
(code === CODE.unusedMember && (brands ??= brandMembers(input)).has(symbol.id))
|
|
261
274
|
) {
|
|
262
275
|
continue;
|
|
263
276
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CODE } from "../contract/contract.generated.ts";
|
|
1
2
|
import type { Finding } from "../finding.ts";
|
|
2
3
|
import { OUTSIDE, type Graph } from "../graph.ts";
|
|
3
4
|
import type { InventorySymbol, SymbolKind } from "../inventory.ts";
|
|
@@ -8,10 +9,6 @@ import { runnerCalls } from "../sweep.ts";
|
|
|
8
9
|
import type { Emitter, EmitterInput } from "./emitter.ts";
|
|
9
10
|
import { writeOnlyDeclarations } from "./reads-and-writes.ts";
|
|
10
11
|
|
|
11
|
-
const UNNECESSARY_EXPORT = "DS1101";
|
|
12
|
-
const UNREACHABLE_EXPORT = "DS1103";
|
|
13
|
-
const REDUNDANT_EXPORT_KEYWORD = "DS1104";
|
|
14
|
-
|
|
15
12
|
/** How far the references to one declaration reach, from the narrowest. */
|
|
16
13
|
type Reach = "file" | "package" | "outside";
|
|
17
14
|
|
|
@@ -22,7 +19,7 @@ type Reach = "file" | "package" | "outside";
|
|
|
22
19
|
*/
|
|
23
20
|
const PUBLISHING: ReadonlySet<RootKind> = new Set<RootKind>(["manifest-entry", "published-api"]);
|
|
24
21
|
|
|
25
|
-
/** The reader's word for each kind of declaration this
|
|
22
|
+
/** The reader's word for each kind of declaration this emitter reports. */
|
|
26
23
|
const WORDS: Partial<Readonly<Record<SymbolKind, string>>> = {
|
|
27
24
|
function: "function",
|
|
28
25
|
class: "class",
|
|
@@ -63,6 +60,12 @@ function entersByName(kinds: ReadonlySet<RootKind> | undefined): boolean {
|
|
|
63
60
|
return [...(kinds ?? [])].some((kind) => !PUBLISHING.has(kind) && !RUN_ONLY.has(kind));
|
|
64
61
|
}
|
|
65
62
|
|
|
63
|
+
/**
|
|
64
|
+
* The root kinds whose root of a file enters no export by name: a document's import roots
|
|
65
|
+
* each export it imports, so an export it does not import is judged as in any file.
|
|
66
|
+
*/
|
|
67
|
+
const PER_NAME: ReadonlySet<RootKind> = new Set<RootKind>(["document-import"]);
|
|
68
|
+
|
|
66
69
|
/**
|
|
67
70
|
* Whether the roots of a declaration's file enter it by name: a test runner's root
|
|
68
71
|
* enters only an export the runner calls, so another export of a test file is judged.
|
|
@@ -72,7 +75,7 @@ function fileEntersByName(
|
|
|
72
75
|
symbol: InventorySymbol,
|
|
73
76
|
): boolean {
|
|
74
77
|
const entering = [...(kinds ?? [])].filter(
|
|
75
|
-
(kind) => !PUBLISHING.has(kind) && !RUN_ONLY.has(kind),
|
|
78
|
+
(kind) => !PUBLISHING.has(kind) && !RUN_ONLY.has(kind) && !PER_NAME.has(kind),
|
|
76
79
|
);
|
|
77
80
|
return (
|
|
78
81
|
entering.some((kind) => kind !== "test-runner") || (entering.length > 0 && runnerCalls(symbol))
|
|
@@ -145,8 +148,8 @@ function wordOf(symbol: InventorySymbol): string {
|
|
|
145
148
|
|
|
146
149
|
/** The visibility each narrowing code names, which only the narrowing codes carry. */
|
|
147
150
|
const NARROWER: Readonly<Record<string, "file" | "package">> = {
|
|
148
|
-
[
|
|
149
|
-
[
|
|
151
|
+
[CODE.redundantExportKeyword]: "file",
|
|
152
|
+
[CODE.unnecessaryExport]: "package",
|
|
150
153
|
};
|
|
151
154
|
|
|
152
155
|
function findingOf(symbol: InventorySymbol, code: string, message: string): Finding {
|
|
@@ -200,7 +203,7 @@ function narrowing(
|
|
|
200
203
|
}
|
|
201
204
|
return findingOf(
|
|
202
205
|
symbol,
|
|
203
|
-
|
|
206
|
+
CODE.redundantExportKeyword,
|
|
204
207
|
`exported ${word} is referenced only inside the file that declares it`,
|
|
205
208
|
);
|
|
206
209
|
case "package":
|
|
@@ -209,7 +212,7 @@ function narrowing(
|
|
|
209
212
|
}
|
|
210
213
|
return findingOf(
|
|
211
214
|
symbol,
|
|
212
|
-
|
|
215
|
+
CODE.unnecessaryExport,
|
|
213
216
|
`exported ${word} is referenced only inside the package that declares it`,
|
|
214
217
|
);
|
|
215
218
|
default:
|
|
@@ -222,7 +225,7 @@ function narrowing(
|
|
|
222
225
|
* export no code outside the target can import, because the manifest declares
|
|
223
226
|
* `exports` and no root reaches it. Each arm yields to a more specific kind, so one
|
|
224
227
|
* declaration is reported once: a test whose every subject is dead, an interface, a
|
|
225
|
-
* declaration only a test file references, and a deprecated one.
|
|
228
|
+
* declaration only a test file references or a test that stays keeps, and a deprecated one.
|
|
226
229
|
*/
|
|
227
230
|
function unreachable(
|
|
228
231
|
facts: Facts,
|
|
@@ -237,7 +240,7 @@ function unreachable(
|
|
|
237
240
|
(facts.rootedBy[at]?.size ?? 0) > 0 ||
|
|
238
241
|
candidate.testOfDeadCode ||
|
|
239
242
|
symbol.kind === "interface" ||
|
|
240
|
-
(candidate.
|
|
243
|
+
(candidate.testRefs > 0 && (candidate.productionRefs === 0 || candidate.keptByTests)) ||
|
|
241
244
|
(candidate.productionRefs === 0 && input.deprecated.has(symbol.id))
|
|
242
245
|
) {
|
|
243
246
|
return undefined;
|
|
@@ -248,13 +251,13 @@ function unreachable(
|
|
|
248
251
|
: "has no reference in the target";
|
|
249
252
|
return findingOf(
|
|
250
253
|
symbol,
|
|
251
|
-
|
|
254
|
+
CODE.unreachableExport,
|
|
252
255
|
`exported ${wordOf(symbol)} ${found}, and nothing outside can import the file that declares it`,
|
|
253
256
|
);
|
|
254
257
|
}
|
|
255
258
|
|
|
256
259
|
/**
|
|
257
|
-
* The visibility-narrowing
|
|
260
|
+
* The visibility-narrowing kinds over one run: `DS1501`, `DS1101` and `DS1503`. A finding
|
|
258
261
|
* about a symbol a declared cross-language edge names is reported here like any other, and
|
|
259
262
|
* the edge's evaluation then holds it pending, because the edge stands for a reference from
|
|
260
263
|
* outside the file and the package whose use is decided in the other language. A
|
|
@@ -263,13 +266,13 @@ function unreachable(
|
|
|
263
266
|
export function narrowings(input: EmitterInput): readonly Finding[] {
|
|
264
267
|
const facts = factsOf(input.swept.matrix.union);
|
|
265
268
|
const { declared, loaded } = input.boundary.consumers;
|
|
266
|
-
const closed = input.config.
|
|
269
|
+
const closed = input.config.consumers.complete && declared.every((id) => loaded.includes(id));
|
|
267
270
|
const world = { closed, writeOnly: writeOnlyDeclarations(input) };
|
|
268
271
|
const candidates = new Map(
|
|
269
272
|
input.swept.sweep.candidates.map((candidate) => [candidate.id, candidate]),
|
|
270
273
|
);
|
|
271
274
|
const generated =
|
|
272
|
-
input.config.analysis.
|
|
275
|
+
input.config.analysis.generated_files === "include" ? undefined : input.files.generated;
|
|
273
276
|
|
|
274
277
|
const findings: Finding[] = [];
|
|
275
278
|
facts.graph.symbols.forEach((symbol, at) => {
|
|
@@ -310,5 +313,5 @@ export function unreachableExports(input: EmitterInput): ReadonlySet<string> {
|
|
|
310
313
|
return found;
|
|
311
314
|
}
|
|
312
315
|
|
|
313
|
-
/** The findings of the visibility-narrowing
|
|
316
|
+
/** The findings of the visibility-narrowing kinds, `DS1501`, `DS1101` and `DS1503`. */
|
|
314
317
|
export const visibilityNarrowing: Emitter = narrowings;
|