@cplieger/deadset-ts 7.2.0-dev.4 → 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 +245 -309
- package/dist/src/augmentations.js +124 -18
- package/dist/src/automatic-types.js +170 -0
- package/dist/src/build-scripts.js +425 -0
- package/dist/src/component-layer.js +4 -2
- package/dist/src/components.js +9 -9
- package/dist/src/config.js +6 -81
- package/dist/src/configuration-files.js +78 -188
- package/dist/src/configured-declarations.js +15 -20
- package/dist/src/conformance-record.js +2 -2
- 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 +146 -1
- package/dist/src/conventions.js +136 -70
- package/dist/src/decorator.js +21 -18
- package/dist/src/dependencies.js +280 -62
- package/dist/src/edges.js +38 -122
- package/dist/src/element-access.js +4 -6
- package/dist/src/entry-point-gaps.js +9 -18
- package/dist/src/entry-points.js +106 -21
- 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 +225 -63
- package/dist/src/findings/dependencies-and-module-machinery.js +72 -27
- package/dist/src/findings/emitters.js +6 -6
- package/dist/src/findings/interfaces.js +4 -5
- package/dist/src/findings/intra-function.js +53 -19
- package/dist/src/findings/non-code-artifacts.js +71 -39
- 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 +57 -28
- package/dist/src/findings/visibility-narrowing.js +40 -18
- 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 +18 -4
- package/dist/src/git-ignore.js +99 -0
- package/dist/src/graph.js +9 -4
- package/dist/src/html-entries.js +6 -33
- package/dist/src/import-bindings.js +97 -0
- package/dist/src/in-operands.js +2 -1
- package/dist/src/index.js +1 -1
- package/dist/src/indexed-access.js +130 -28
- package/dist/src/injection-container.js +1 -1
- package/dist/src/interface-satisfaction.js +30 -12
- package/dist/src/intra-function-parts.js +341 -69
- package/dist/src/inventory.js +54 -29
- 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} +88 -61
- package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +60 -59
- 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 +94 -13
- package/dist/src/mdx-entries.js +174 -0
- package/dist/src/module-declarations.js +9 -13
- package/dist/src/outside-tests.js +1 -1
- package/dist/src/overrides.js +41 -10
- 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 -78
- package/dist/src/product.js +13 -0
- package/dist/src/query.js +1 -0
- package/dist/src/ref.js +11 -0
- package/dist/src/references.js +52 -31
- package/dist/src/report-schema.js +51 -0
- package/dist/src/report.js +59 -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 -727
- package/dist/src/roots.js +374 -116
- package/dist/src/run.js +66 -77
- package/dist/src/schema.js +320 -342
- package/dist/src/scope.js +38 -115
- package/dist/src/serialization-contract.js +649 -29
- package/dist/src/session.js +41 -29
- package/dist/src/source-files.js +40 -15
- package/dist/src/suppress-file.js +34 -29
- package/dist/src/suppress-inline.js +21 -4
- package/dist/src/suppress.js +40 -56
- package/dist/src/sweep.js +32 -17
- package/dist/src/symbol-keys.js +105 -6
- package/dist/src/template-field.js +9 -19
- package/dist/src/test-support.js +119 -18
- 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 +27 -221
- package/dist/src/type-parameter-reads.js +172 -0
- 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 +181 -125
- package/dist/src/verbs/describe.js +9 -16
- package/dist/src/verbs/explain.js +14 -9
- 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 +128 -33
- package/jsr.json +1 -1
- package/package.json +5 -4
- package/src/analysis.ts +333 -457
- package/src/augmentations.ts +152 -23
- package/src/automatic-types.ts +228 -0
- package/src/build-scripts.ts +543 -0
- package/src/component-layer.ts +4 -2
- package/src/components.ts +9 -10
- package/src/config.ts +31 -299
- package/src/configuration-files.ts +100 -191
- package/src/configured-declarations.ts +18 -23
- package/src/conformance-record.ts +2 -2
- 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 +186 -1
- package/src/conventions.ts +166 -90
- package/src/decorator.ts +28 -18
- package/src/dependencies.ts +356 -78
- package/src/edges.ts +41 -140
- package/src/element-access.ts +4 -7
- package/src/entry-point-gaps.ts +11 -20
- package/src/entry-points.ts +124 -28
- 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 +24 -9
- package/src/findings/boundary.ts +2 -0
- package/src/findings/completion.ts +297 -77
- package/src/findings/dependencies-and-module-machinery.ts +90 -31
- package/src/findings/emitter.ts +8 -9
- package/src/findings/emitters.ts +8 -8
- package/src/findings/interfaces.ts +4 -6
- package/src/findings/intra-function.ts +64 -20
- package/src/findings/non-code-artifacts.ts +88 -50
- package/src/findings/reads-and-writes.ts +20 -16
- package/src/findings/self-check.ts +11 -17
- package/src/findings/unused-declarations.ts +65 -28
- package/src/findings/visibility-narrowing.ts +51 -19
- 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 +23 -4
- package/src/git-ignore.ts +122 -0
- package/src/graph.ts +11 -3
- package/src/host.ts +17 -14
- package/src/html-entries.ts +6 -33
- package/src/import-bindings.ts +128 -0
- package/src/in-operands.ts +2 -1
- package/src/index.ts +1 -1
- package/src/indexed-access.ts +152 -30
- package/src/injection-container.ts +1 -1
- package/src/interface-satisfaction.ts +33 -13
- package/src/intra-function-parts.ts +449 -85
- package/src/inventory.ts +73 -27
- 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} +127 -80
- package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +73 -64
- 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 +154 -18
- package/src/mdx-entries.ts +218 -0
- package/src/module-declarations.ts +11 -13
- package/src/outside-tests.ts +1 -1
- package/src/overrides.ts +51 -12
- package/src/partial.ts +3 -2
- package/src/paths.ts +27 -15
- package/src/position.ts +2 -1
- package/src/print.ts +12 -91
- package/src/product.ts +18 -0
- package/src/query.ts +11 -0
- package/src/ref.ts +13 -0
- package/src/references.ts +62 -32
- package/src/report-schema.ts +68 -0
- package/src/report.ts +105 -215
- 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 -1111
- package/src/roots.ts +490 -137
- package/src/run.ts +75 -91
- package/src/schema.ts +391 -387
- package/src/scope.ts +46 -138
- package/src/serialization-contract.ts +818 -26
- package/src/session.ts +77 -54
- package/src/source-files.ts +67 -17
- package/src/suppress-file.ts +36 -34
- package/src/suppress-inline.ts +20 -3
- package/src/suppress.ts +52 -63
- package/src/sweep.ts +53 -18
- package/src/symbol-keys.ts +131 -7
- package/src/template-field.ts +15 -25
- package/src/test-support.ts +155 -18
- 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 +35 -273
- package/src/type-parameter-reads.ts +227 -0
- package/src/unseen-loads.ts +134 -0
- package/src/unwrap.ts +34 -0
- package/src/usage.ts +10 -0
- package/src/verbs/analyze.ts +245 -161
- package/src/verbs/describe.ts +14 -15
- package/src/verbs/explain.ts +15 -13
- 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 +149 -34
- package/dist/src/discover.js +0 -347
- 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/discover.ts +0 -510
- 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/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
|
|
|
@@ -165,7 +165,7 @@ function dialledOver(
|
|
|
165
165
|
const unanswered = heldKeys(input);
|
|
166
166
|
const markup = markupKeys(input);
|
|
167
167
|
const candidates = found.filter((finding) => {
|
|
168
|
-
const key = declarationKey(finding.symbol.ref, finding.position.path);
|
|
168
|
+
const key = declarationKey(finding.symbol.holder ?? finding.symbol.ref, finding.position.path);
|
|
169
169
|
return (
|
|
170
170
|
!ledger.withheld(finding) &&
|
|
171
171
|
!unanswered.has(key) &&
|
|
@@ -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),
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { CODE, type Confidence } from "../contract/contract.generated.ts";
|
|
1
2
|
import type { Finding } from "../finding.ts";
|
|
2
3
|
import { KINDS } from "../kinds.ts";
|
|
3
|
-
import {
|
|
4
|
+
import { parameterRef } from "../ref.ts";
|
|
5
|
+
import { type PartFact } from "../intra-function-parts.ts";
|
|
4
6
|
import { reachedWithTheTests } from "../sweep.ts";
|
|
5
7
|
import type { Emitter, EmitterInput } from "./emitter.ts";
|
|
6
8
|
|
|
@@ -12,29 +14,58 @@ function spelled(rules: readonly string[]): string {
|
|
|
12
14
|
return `${rules.slice(0, -1).join(", ")} and ${rules.at(-1) ?? ""}`;
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
/** The
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
/** The compiler's rule of the parameter kind's overlap list, which reports only in a file it checks. */
|
|
18
|
+
const COMPILER_PARAMETER_RULE = "tsc --noUnusedParameters";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The clause a message ends with: the other rules that report the part, or that none is
|
|
22
|
+
* known. A parameter of a file the compiler does not check is no report of the compiler's.
|
|
23
|
+
*/
|
|
24
|
+
function overlapClause(overlap: readonly string[], part: PartFact): string {
|
|
25
|
+
const rules =
|
|
26
|
+
part.parameter?.checked === false
|
|
27
|
+
? overlap.filter((rule) => rule !== COMPILER_PARAMETER_RULE)
|
|
28
|
+
: overlap;
|
|
29
|
+
return rules.length === 1 && rules[0] === "none known"
|
|
18
30
|
? "no other rule is known to report it"
|
|
19
|
-
: `also reported by ${spelled(
|
|
31
|
+
: `also reported by ${spelled(rules)}`;
|
|
20
32
|
}
|
|
21
33
|
|
|
34
|
+
/** What a parameter's finding asks: its deletion, or, where a read parameter follows it, its rename. */
|
|
35
|
+
type ParameterVerdict =
|
|
36
|
+
| { readonly fix: "delete"; readonly confidence: Confidence }
|
|
37
|
+
| { readonly fix: "rename" }
|
|
38
|
+
| undefined;
|
|
39
|
+
|
|
22
40
|
/**
|
|
23
|
-
* Whether one parameter
|
|
24
|
-
*
|
|
25
|
-
*
|
|
41
|
+
* Whether one unread parameter is reported, and how. Of a free signature it is deleted. A
|
|
42
|
+
* caller may pass a function used as a value more arguments than it declares, so there one
|
|
43
|
+
* a read parameter follows is renamed to an underscore name, which the compilers read as
|
|
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 or
|
|
46
|
+
* where it is a name a contextually typed object pattern binds.
|
|
26
47
|
*/
|
|
27
|
-
function
|
|
48
|
+
function parameterVerdict(
|
|
28
49
|
part: PartFact,
|
|
29
50
|
free: ReadonlySet<string>,
|
|
30
51
|
freeButValued: ReadonlySet<string>,
|
|
31
|
-
):
|
|
52
|
+
): ParameterVerdict {
|
|
32
53
|
const place = part.parameter;
|
|
33
54
|
if (place?.inValue !== true && free.has(part.declaration)) {
|
|
34
|
-
return
|
|
55
|
+
return place?.annotated === undefined ? { fix: "delete", confidence: "certain" } : undefined;
|
|
35
56
|
}
|
|
36
57
|
const valued = place?.inValue === true || freeButValued.has(part.declaration);
|
|
37
|
-
|
|
58
|
+
if (!valued) {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
if (place?.beforeARead === true) {
|
|
62
|
+
return { fix: "rename" };
|
|
63
|
+
}
|
|
64
|
+
if (place?.annotated === "steers") {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
const unseen = place?.annotated === "free" || place?.contextualPattern === true;
|
|
68
|
+
return { fix: "delete", confidence: unseen ? "probable" : "certain" };
|
|
38
69
|
}
|
|
39
70
|
|
|
40
71
|
/**
|
|
@@ -54,9 +85,9 @@ function reported(
|
|
|
54
85
|
}
|
|
55
86
|
const { free, freeButValued, discardedEverywhere } = input.intraFunction;
|
|
56
87
|
switch (part.code) {
|
|
57
|
-
case
|
|
58
|
-
return
|
|
59
|
-
case
|
|
88
|
+
case CODE.unusedParameter:
|
|
89
|
+
return parameterVerdict(part, free, freeButValued) !== undefined;
|
|
90
|
+
case CODE.unusedResult:
|
|
60
91
|
return (
|
|
61
92
|
free.has(part.declaration) &&
|
|
62
93
|
discardedEverywhere.has(part.declaration) &&
|
|
@@ -68,7 +99,7 @@ function reported(
|
|
|
68
99
|
}
|
|
69
100
|
|
|
70
101
|
/**
|
|
71
|
-
* The findings of the
|
|
102
|
+
* The findings of the inside-functions family, `DS1401` to `DS1406`, each about a part of
|
|
72
103
|
* the declaration its reference names, at the part's own position, whatever the sweep
|
|
73
104
|
* decided about a production declaration. A part of test code is reported only where the
|
|
74
105
|
* run that counts test references reaches its declaration and that is no test of dead
|
|
@@ -96,7 +127,7 @@ export const intraFunction: Emitter = (input) => {
|
|
|
96
127
|
}),
|
|
97
128
|
);
|
|
98
129
|
const { declared, loaded } = input.boundary.consumers;
|
|
99
|
-
const closed = input.config.
|
|
130
|
+
const closed = input.config.consumers.complete && declared.every((id) => loaded.includes(id));
|
|
100
131
|
const published = (id: string): boolean => {
|
|
101
132
|
const symbol = byId.get(id);
|
|
102
133
|
return (
|
|
@@ -115,24 +146,37 @@ export const intraFunction: Emitter = (input) => {
|
|
|
115
146
|
const found: Finding[] = [];
|
|
116
147
|
for (const part of input.intraFunction.parts) {
|
|
117
148
|
const declaration = byId.get(part.declaration);
|
|
118
|
-
const overlap = KINDS.get(part.code)?.overlap ?? [];
|
|
149
|
+
const overlap = KINDS.get(part.code)?.overlap?.ts ?? [];
|
|
119
150
|
if (declaration === undefined || !judged(declaration.id) || !reported(input, part, unknown)) {
|
|
120
151
|
continue;
|
|
121
152
|
}
|
|
153
|
+
const verdict =
|
|
154
|
+
part.code === CODE.unusedParameter
|
|
155
|
+
? parameterVerdict(part, input.intraFunction.free, input.intraFunction.freeButValued)
|
|
156
|
+
: undefined;
|
|
157
|
+
const message =
|
|
158
|
+
verdict?.fix === "rename"
|
|
159
|
+
? `${part.message}, and a read parameter follows it, so rename it to _${part.name}`
|
|
160
|
+
: part.message;
|
|
122
161
|
found.push({
|
|
123
162
|
code: part.code,
|
|
124
163
|
position: part.position,
|
|
125
164
|
symbol: {
|
|
126
|
-
|
|
165
|
+
...(part.code === CODE.unusedParameter
|
|
166
|
+
? { ref: parameterRef(declaration.ref, part.name), holder: declaration.ref }
|
|
167
|
+
: { ref: declaration.ref }),
|
|
127
168
|
kind: part.kind,
|
|
128
169
|
name: part.name,
|
|
129
170
|
sizeLines: part.position.endLine - part.position.line + 1,
|
|
130
171
|
},
|
|
131
|
-
message: `${
|
|
172
|
+
message: `${message}; ${overlapClause(overlap, part)}`,
|
|
132
173
|
details: {
|
|
133
174
|
...(part.kind === "store" ? { writePositions: [part.position] } : {}),
|
|
134
175
|
overlap,
|
|
135
176
|
},
|
|
177
|
+
...(verdict?.fix === "delete" && verdict.confidence !== "certain"
|
|
178
|
+
? { reachabilityClass: verdict.confidence }
|
|
179
|
+
: {}),
|
|
136
180
|
});
|
|
137
181
|
}
|
|
138
182
|
return found;
|
|
@@ -1,11 +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
|
-
import type { Graph } from "../graph.ts";
|
|
4
4
|
import type { Emitter, EmitterInput } from "./emitter.ts";
|
|
5
5
|
|
|
6
|
-
const FILE_NEVER_BUILT = "DS1501";
|
|
7
|
-
const FILE_NEVER_IMPORTED = "DS1502";
|
|
8
|
-
|
|
9
6
|
/** The subject kind of a finding about a source file, from the finding schema's vocabulary. */
|
|
10
7
|
const FILE_SUBJECT = "file";
|
|
11
8
|
|
|
@@ -18,10 +15,10 @@ const FILE_START = 1;
|
|
|
18
15
|
* completeness and listing no project declares it of a matrix the run derived, and a
|
|
19
16
|
* derived matrix is never complete.
|
|
20
17
|
*/
|
|
21
|
-
function completeMatrix(config:
|
|
18
|
+
function completeMatrix(config: ResolvedConfig): boolean {
|
|
22
19
|
return (
|
|
23
|
-
config.analysis.
|
|
24
|
-
config.analysis.configurations.some((entry) =>
|
|
20
|
+
config.analysis.matrix.complete &&
|
|
21
|
+
config.analysis.configurations.some((entry) => "project" in entry)
|
|
25
22
|
);
|
|
26
23
|
}
|
|
27
24
|
|
|
@@ -58,46 +55,37 @@ function fileNeverBuilt({ config, swept, files }: EmitterInput): readonly Findin
|
|
|
58
55
|
const message = `no project of the build matrix includes this file (${swept.matrix.configurations.join(", ")})`;
|
|
59
56
|
return files.tree
|
|
60
57
|
.filter((file) => !built.has(file.path))
|
|
61
|
-
.map((file) => fileFinding(
|
|
58
|
+
.map((file) => fileFinding(CODE.fileNeverBuilt, file, message));
|
|
62
59
|
}
|
|
63
60
|
|
|
64
|
-
/**
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
function reachedFromOutside(union: Graph): ReadonlySet<string> {
|
|
71
|
-
const reached = new Set<string>();
|
|
72
|
-
for (const edge of union.consumed) {
|
|
73
|
-
const to = union.symbols[edge.to];
|
|
74
|
-
if (to !== undefined) {
|
|
75
|
-
reached.add(to.position.path);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
union.out.forEach((edges, from) => {
|
|
79
|
-
const fromPath = union.symbols[from]?.position.path;
|
|
80
|
-
for (const edge of edges) {
|
|
81
|
-
const to = union.symbols[edge.to];
|
|
82
|
-
if (to !== undefined && to.position.path !== fromPath) {
|
|
83
|
-
reached.add(to.position.path);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
return reached;
|
|
61
|
+
/** The files of the run, each with whether a reference written in another file reaches it. */
|
|
62
|
+
interface KeptFiles {
|
|
63
|
+
/** The files kept: reached from a live file, or named by a root, an edge or a program. */
|
|
64
|
+
readonly kept: ReadonlySet<string>;
|
|
65
|
+
/** The files a reference written in another file reaches, whatever holds that file. */
|
|
66
|
+
readonly referenced: ReadonlySet<string>;
|
|
88
67
|
}
|
|
89
68
|
|
|
90
69
|
/**
|
|
91
|
-
* The paths of the files
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
70
|
+
* The paths of the files a reference from a live declaration of another file reaches, a
|
|
71
|
+
* loaded consumer's file included, a root names, or a declared edge names a declaration in,
|
|
72
|
+
* and of every file a reference written anywhere reaches. A file holding a declaration the
|
|
73
|
+
* sweep judged live is kept whatever held it live, and so is a file a program holds by
|
|
74
|
+
* inclusion, which acts on every file of the program without an import naming it. A
|
|
75
|
+
* reference from a file's top level counts once the file is kept.
|
|
95
76
|
*/
|
|
96
|
-
function keptFiles({ swept, files, boundary }: EmitterInput):
|
|
77
|
+
function keptFiles({ swept, files, boundary }: EmitterInput): KeptFiles {
|
|
97
78
|
const union = swept.matrix.union;
|
|
98
79
|
const edgeNamed = new Set(boundary.edges.map((side) => side.symbol));
|
|
99
80
|
const dead = new Set(swept.sweep.candidates.map((candidate) => candidate.id));
|
|
100
|
-
const kept = new Set(
|
|
81
|
+
const kept = new Set(files.heldByInclusion);
|
|
82
|
+
const referenced = new Set<string>();
|
|
83
|
+
for (const edge of union.consumed) {
|
|
84
|
+
const to = union.symbols[edge.to];
|
|
85
|
+
if (to !== undefined) {
|
|
86
|
+
kept.add(to.position.path);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
101
89
|
for (const root of union.rooted) {
|
|
102
90
|
const named = union.symbols[root.at];
|
|
103
91
|
if (named !== undefined) {
|
|
@@ -109,24 +97,74 @@ function keptFiles({ swept, files, boundary }: EmitterInput): ReadonlySet<string
|
|
|
109
97
|
kept.add(symbol.position.path);
|
|
110
98
|
}
|
|
111
99
|
}
|
|
112
|
-
|
|
100
|
+
const fromTopLevel = new Map<string, string[]>();
|
|
101
|
+
const pending: string[] = [];
|
|
102
|
+
const keep = (path: string): void => {
|
|
103
|
+
if (!kept.has(path)) {
|
|
104
|
+
kept.add(path);
|
|
105
|
+
pending.push(path);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
union.out.forEach((edges, from) => {
|
|
109
|
+
const source = union.symbols[from];
|
|
110
|
+
if (source === undefined) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
for (const edge of edges) {
|
|
114
|
+
const to = union.symbols[edge.to]?.position.path;
|
|
115
|
+
if (to === undefined || to === source.position.path) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
referenced.add(to);
|
|
119
|
+
if (source.kind === FILE_SUBJECT) {
|
|
120
|
+
fromTopLevel.set(source.position.path, [
|
|
121
|
+
...(fromTopLevel.get(source.position.path) ?? []),
|
|
122
|
+
to,
|
|
123
|
+
]);
|
|
124
|
+
} else if (!dead.has(source.id)) {
|
|
125
|
+
pending.push(to);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
pending.push(...kept);
|
|
130
|
+
for (let path = pending.pop(); path !== undefined; path = pending.pop()) {
|
|
131
|
+
kept.add(path);
|
|
132
|
+
for (const to of fromTopLevel.get(path) ?? []) {
|
|
133
|
+
keep(to);
|
|
134
|
+
}
|
|
135
|
+
fromTopLevel.delete(path);
|
|
136
|
+
}
|
|
137
|
+
return { kept, referenced };
|
|
113
138
|
}
|
|
114
139
|
|
|
115
|
-
/**
|
|
140
|
+
/**
|
|
141
|
+
* Every file of the run that no reference from live code reaches and no root or edge
|
|
142
|
+
* names. A file only dead code imports is found dead under reachability alone.
|
|
143
|
+
*/
|
|
116
144
|
function fileNeverImported(input: EmitterInput): readonly Finding[] {
|
|
117
|
-
const kept = keptFiles(input);
|
|
145
|
+
const { kept, referenced } = keptFiles(input);
|
|
118
146
|
return input.swept.matrix.union.symbols
|
|
119
147
|
.filter((symbol) => symbol.kind === FILE_SUBJECT && !kept.has(symbol.position.path))
|
|
120
|
-
.map((symbol) =>
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
{
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
148
|
+
.map((symbol) => {
|
|
149
|
+
const file = { path: symbol.position.path, ref: symbol.ref, endLine: symbol.endLine };
|
|
150
|
+
return referenced.has(symbol.position.path)
|
|
151
|
+
? {
|
|
152
|
+
...fileFinding(
|
|
153
|
+
CODE.fileNeverImported,
|
|
154
|
+
file,
|
|
155
|
+
"only code that is itself dead imports this file, and no root names it or a declaration in it",
|
|
156
|
+
),
|
|
157
|
+
unreachableOnly: true as const,
|
|
158
|
+
}
|
|
159
|
+
: fileFinding(
|
|
160
|
+
CODE.fileNeverImported,
|
|
161
|
+
file,
|
|
162
|
+
"no import reaches this file and no root names it or a declaration in it",
|
|
163
|
+
);
|
|
164
|
+
});
|
|
127
165
|
}
|
|
128
166
|
|
|
129
|
-
/** The findings of the
|
|
167
|
+
/** The findings of the files family, `DS1600` to `DS1699`. */
|
|
130
168
|
export const nonCodeArtifacts: Emitter = (input) => [
|
|
131
169
|
...fileNeverBuilt(input),
|
|
132
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);
|