@cplieger/deadset-ts 8.0.0-dev.17 → 8.0.0-dev.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/analysis.js +26 -71
- package/dist/src/augmentations.js +1 -1
- package/dist/src/calls.js +267 -48
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/contract/contract.generated.js +2 -2
- package/dist/src/exempt.js +0 -26
- package/dist/src/exposure.js +400 -0
- package/dist/src/findings/completion.js +4 -17
- package/dist/src/findings/emitters.js +22 -33
- package/dist/src/findings/intra-function.js +3 -3
- package/dist/src/findings/reads-and-writes.js +71 -15
- package/dist/src/findings/unused-declarations.js +2 -22
- package/dist/src/findings/visibility-narrowing.js +14 -20
- package/dist/src/inventory.js +18 -0
- package/dist/src/{components.js → liveness/components.js} +1 -1
- package/dist/src/{graph.js → liveness/graph.js} +38 -21
- package/dist/src/liveness/matrix.js +99 -0
- package/dist/src/liveness/sweep.js +558 -0
- package/dist/src/query.js +46 -0
- package/dist/src/references/sink.js +87 -1
- package/dist/src/references.js +1 -1
- package/dist/src/roots/published-reach.js +4 -4
- package/dist/src/verbs/explain.js +73 -152
- package/dist/src/verbs/print-retained.js +1 -1
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/analysis.ts +41 -107
- package/src/augmentations.ts +1 -1
- package/src/calls.ts +326 -52
- package/src/conformance-record.ts +1 -1
- package/src/contract/contract.generated.ts +2 -2
- package/src/exempt.ts +31 -46
- package/src/exposure.ts +492 -0
- package/src/finding.ts +1 -1
- package/src/findings/boundary.ts +7 -0
- package/src/findings/completion.ts +5 -19
- package/src/findings/emitters.ts +23 -41
- package/src/findings/interfaces.ts +3 -3
- package/src/findings/intra-function.ts +3 -3
- package/src/findings/reads-and-writes.ts +99 -26
- package/src/findings/unused-declarations.ts +4 -28
- package/src/findings/visibility-narrowing.ts +25 -26
- package/src/interface-satisfaction.ts +1 -1
- package/src/inventory.ts +19 -0
- package/src/{components.ts → liveness/components.ts} +3 -3
- package/src/{graph.ts → liveness/graph.ts} +74 -26
- package/src/liveness/matrix.ts +162 -0
- package/src/liveness/sweep.ts +855 -0
- package/src/query.ts +81 -0
- package/src/references/sink.ts +101 -0
- package/src/references.ts +8 -1
- package/src/roots/published-reach.ts +9 -4
- package/src/stores.ts +2 -1
- package/src/tests/file-class.ts +19 -2
- package/src/verbs/explain.ts +81 -199
- package/src/verbs/print-retained.ts +1 -1
- package/dist/src/matrix.js +0 -275
- package/dist/src/sweep.js +0 -234
- package/src/matrix.ts +0 -466
- package/src/sweep.ts +0 -384
package/src/analysis.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
} from "./roots/configured-declarations.ts";
|
|
18
18
|
import { decorator } from "./decorator.ts";
|
|
19
19
|
import { configurationNeeds, dependenciesOf, projectNeeds } from "./dependencies.ts";
|
|
20
|
+
import { carriersOf, NO_CARRIERS, type Carriers } from "./exposure.ts";
|
|
20
21
|
import { deprecatedDeclarations, type Deprecation } from "./deprecation.ts";
|
|
21
22
|
import { moduleIdentity, type LoadedConsumer, type NotBuilt } from "./load/discover.ts";
|
|
22
23
|
import type { LibraryUnfound } from "./load/classify.ts";
|
|
@@ -35,9 +36,7 @@ import {
|
|
|
35
36
|
heldRecords,
|
|
36
37
|
disabledClasses,
|
|
37
38
|
exemptionsOf,
|
|
38
|
-
holdingWhile,
|
|
39
39
|
isUnansweredRecord,
|
|
40
|
-
retainedIn,
|
|
41
40
|
writtenRecords,
|
|
42
41
|
type Detector,
|
|
43
42
|
type Detectors,
|
|
@@ -57,15 +56,7 @@ import { intraFunctionFacts, projectParts, type ProjectParts } from "./intra-fun
|
|
|
57
56
|
import { type Inventory, type InventorySymbol } from "./inventory.ts";
|
|
58
57
|
import { inventory } from "./inventory/enumerate.ts";
|
|
59
58
|
import { readManifest, type ManifestEntry } from "./roots/manifest.ts";
|
|
60
|
-
import {
|
|
61
|
-
liveFilesAt,
|
|
62
|
-
matrixOf,
|
|
63
|
-
referenceKey,
|
|
64
|
-
sweepMatrix,
|
|
65
|
-
type Configured,
|
|
66
|
-
type Matrix,
|
|
67
|
-
type SweepResult,
|
|
68
|
-
} from "./matrix.ts";
|
|
59
|
+
import { matrixOf, referenceKey, type Configured, type Matrix } from "./liveness/matrix.ts";
|
|
69
60
|
import {
|
|
70
61
|
ancestors,
|
|
71
62
|
dirnamePath,
|
|
@@ -108,7 +99,9 @@ import { accessorsOf, storesOf } from "./stores.ts";
|
|
|
108
99
|
import { isPartKind, type SuppressionRecord, type Suppressions, type Verdict } from "./suppress.ts";
|
|
109
100
|
import { bindRows, readBaseline, readIgnoreFile, type Recorded } from "./suppress-file.ts";
|
|
110
101
|
import { inlineDirectives, inlineSuppressions, type InlineDirective } from "./suppress-inline.ts";
|
|
111
|
-
import
|
|
102
|
+
import { sweep, type Sweep, type SweepPolicy } from "./liveness/sweep.ts";
|
|
103
|
+
import type { Exemption } from "./exempt.ts";
|
|
104
|
+
import type { Mode } from "./tests/file-class.ts";
|
|
112
105
|
import { readTemplates, templateField } from "./template-field.ts";
|
|
113
106
|
import { bytewise } from "./text/compare.ts";
|
|
114
107
|
import { holdsManifest, manifestAt } from "./fs/manifest.ts";
|
|
@@ -434,27 +427,13 @@ export function productionMode(config: PartialConfig): Mode {
|
|
|
434
427
|
/** The run's sweep, beside the matrix it judged. */
|
|
435
428
|
export interface RunSweep {
|
|
436
429
|
readonly matrix: Matrix;
|
|
437
|
-
readonly sweep:
|
|
438
|
-
/** The exemption records that hold under the sweep. */
|
|
439
|
-
readonly exempt: readonly Exemption[];
|
|
440
|
-
/**
|
|
441
|
-
* The exemption records that held back a declaration some configuration of the run
|
|
442
|
-
* would otherwise judge dead, in the declarations' site order.
|
|
443
|
-
*/
|
|
444
|
-
readonly retained: readonly Exemption[];
|
|
430
|
+
readonly sweep: Sweep;
|
|
445
431
|
/** The derived configurations the run dropped. */
|
|
446
432
|
readonly notBuilt: readonly NotBuilt[];
|
|
447
433
|
/** Every question the checker could not answer, each once. */
|
|
448
434
|
readonly unanswered: readonly UnansweredQuestion[];
|
|
449
|
-
/**
|
|
450
|
-
* Every declaration an unanswered question could have kept live, and every one only
|
|
451
|
-
* such a declaration keeps live. The run reports none of them.
|
|
452
|
-
*/
|
|
453
|
-
readonly heldByUnanswered: readonly string[];
|
|
454
435
|
/** The lines of the units type errors skipped, on which the run reports nothing. */
|
|
455
436
|
readonly skipped: readonly SkippedUnit[];
|
|
456
|
-
/** The component files the run read, by path below the target root, each once. */
|
|
457
|
-
readonly componentFiles: readonly string[];
|
|
458
437
|
}
|
|
459
438
|
|
|
460
439
|
/** One `<script` start tag of a component file the run read no block for. */
|
|
@@ -527,6 +506,8 @@ interface ReadRun<Extra> {
|
|
|
527
506
|
readonly projects: readonly SweptProject<Extra>[];
|
|
528
507
|
/** Every declared consumer, each loaded, in the scope's order. */
|
|
529
508
|
readonly consumers: readonly LoadedConsumer[];
|
|
509
|
+
/** Whether the scope declares a consumer, loaded or not. */
|
|
510
|
+
readonly declaresConsumers: boolean;
|
|
530
511
|
/** The derived configurations the run dropped. */
|
|
531
512
|
readonly notBuilt: readonly NotBuilt[];
|
|
532
513
|
/** Every question the checker could not answer, each once. */
|
|
@@ -826,6 +807,7 @@ function readRun<Extra>(
|
|
|
826
807
|
],
|
|
827
808
|
projects,
|
|
828
809
|
consumers: read.plan.discovery.consumers,
|
|
810
|
+
declaresConsumers: scope.consumers.length > 0,
|
|
829
811
|
notBuilt: read.classified.notBuilt,
|
|
830
812
|
unanswered: read.unanswered,
|
|
831
813
|
componentsUnread: read.plan.workspace?.componentsUnread ?? [],
|
|
@@ -840,33 +822,6 @@ function readRun<Extra>(
|
|
|
840
822
|
};
|
|
841
823
|
}
|
|
842
824
|
|
|
843
|
-
/**
|
|
844
|
-
* The run swept under one set of marks with the records that hold. A record a component
|
|
845
|
-
* file's markup holds counts only while the file is live, and what it holds may be what
|
|
846
|
-
* keeps the file live, so each file's markup joins only once a sweep without it finds the
|
|
847
|
-
* file live, until a sweep finds no further file live.
|
|
848
|
-
*/
|
|
849
|
-
function holdingSweep<Extra>(
|
|
850
|
-
read: ReadRun<Extra>,
|
|
851
|
-
input: Pick<SweepInput, "marked" | "mode">,
|
|
852
|
-
): { readonly swept: SweepInput; readonly result: SweepResult } {
|
|
853
|
-
const paths = new Set(read.exempt.flatMap((record) => record.whileLive ?? []));
|
|
854
|
-
let live = new Set<string>();
|
|
855
|
-
for (;;) {
|
|
856
|
-
const swept: SweepInput = {
|
|
857
|
-
...input,
|
|
858
|
-
exempt: holdingWhile(read.exempt, live),
|
|
859
|
-
unanswered: read.unansweredHeld,
|
|
860
|
-
};
|
|
861
|
-
const result = sweepMatrix(read.matrix, swept);
|
|
862
|
-
const next = new Set(liveFilesAt(read.matrix, result, paths).map((one) => one.position.path));
|
|
863
|
-
if (next.size === live.size) {
|
|
864
|
-
return { swept, result };
|
|
865
|
-
}
|
|
866
|
-
live = next;
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
|
|
870
825
|
/**
|
|
871
826
|
* The test references exemption evidence written in a test file makes: each is a read of
|
|
872
827
|
* the declaration it would retain, made by the test file at the evidence's site.
|
|
@@ -939,62 +894,27 @@ function writtenOnly<Extra>(read: ReadRun<Extra>, mode: Mode): ReadonlySet<strin
|
|
|
939
894
|
return new Set([...written].filter((id) => !readSome.has(id)));
|
|
940
895
|
}
|
|
941
896
|
|
|
942
|
-
/**
|
|
943
|
-
* The sweep of a run read once, under one set of marks. Where a question went unanswered,
|
|
944
|
-
* the run is swept once more without what that question could have kept live and without
|
|
945
|
-
* the references that stood in for its answer, and every declaration that sweep judges
|
|
946
|
-
* dead and this one judges live, or dead by another rule, is held by the gap too, as is
|
|
947
|
-
* every declaration that makes such a reference.
|
|
948
|
-
*/
|
|
897
|
+
/** The sweep of a run read once, under one set of marks. */
|
|
949
898
|
function sweptOf<Extra>(
|
|
950
899
|
read: ReadRun<Extra>,
|
|
951
|
-
input: Pick<
|
|
900
|
+
input: Pick<SweepPolicy, "marked" | "mode">,
|
|
952
901
|
): RunSweep {
|
|
953
|
-
const
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
if (held.size > 0) {
|
|
957
|
-
// What a declaration reads where the checker did not answer is unknown, so a rule
|
|
958
|
-
// over what it reads, the test of dead code among them, cannot judge it.
|
|
959
|
-
for (const { configured } of read.projects) {
|
|
960
|
-
for (const reference of configured.references) {
|
|
961
|
-
if (reference.guessed === true) {
|
|
962
|
-
held.add(reference.from);
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
const dead = new Map(result.candidates.map((candidate) => [candidate.id, candidate]));
|
|
967
|
-
const answered = matrixOf(
|
|
968
|
-
read.projects.map(({ configured }) => ({
|
|
969
|
-
...configured,
|
|
970
|
-
references: configured.references.filter((one) => one.guessed !== true),
|
|
971
|
-
})),
|
|
972
|
-
);
|
|
973
|
-
const bare: SweepInput = {
|
|
974
|
-
...swept,
|
|
975
|
-
exempt: exempt.filter((record) => !isUnansweredRecord(record)),
|
|
976
|
-
unanswered: [],
|
|
977
|
-
};
|
|
978
|
-
for (const candidate of sweepMatrix(answered, bare).candidates) {
|
|
979
|
-
const kept = dead.get(candidate.id);
|
|
980
|
-
if (
|
|
981
|
-
kept?.testOfDeadCode !== candidate.testOfDeadCode ||
|
|
982
|
-
kept.relation !== candidate.relation
|
|
983
|
-
) {
|
|
984
|
-
held.add(candidate.id);
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
}
|
|
902
|
+
const componentFiles = [...new Set(read.projects.flatMap((one) => one.componentFiles))].sort(
|
|
903
|
+
bytewise,
|
|
904
|
+
);
|
|
988
905
|
return {
|
|
989
906
|
matrix: read.matrix,
|
|
990
|
-
sweep:
|
|
991
|
-
|
|
992
|
-
|
|
907
|
+
sweep: sweep(read.matrix, {
|
|
908
|
+
...input,
|
|
909
|
+
exempt: read.exempt,
|
|
910
|
+
unanswered: read.unansweredHeld,
|
|
911
|
+
componentFiles,
|
|
912
|
+
unseenCallers: !read.declaresConsumers,
|
|
913
|
+
writeOnly: writtenOnly(read, input.mode),
|
|
914
|
+
}),
|
|
993
915
|
notBuilt: read.notBuilt,
|
|
994
916
|
unanswered: read.unanswered,
|
|
995
|
-
heldByUnanswered: [...held],
|
|
996
917
|
skipped: read.projects.flatMap((one) => one.skipped),
|
|
997
|
-
componentFiles: [...new Set(read.projects.flatMap((one) => one.componentFiles))].sort(bytewise),
|
|
998
918
|
};
|
|
999
919
|
}
|
|
1000
920
|
|
|
@@ -1011,7 +931,7 @@ export function runSweep(
|
|
|
1011
931
|
host: Host,
|
|
1012
932
|
scope: Scope,
|
|
1013
933
|
config: ResolvedConfig,
|
|
1014
|
-
input: Pick<
|
|
934
|
+
input: Pick<SweepPolicy, "marked" | "mode">,
|
|
1015
935
|
detectors: Detectors = DETECTORS,
|
|
1016
936
|
): RunSweep {
|
|
1017
937
|
return sweptOf(
|
|
@@ -1035,6 +955,7 @@ interface EmitterExtra {
|
|
|
1035
955
|
readonly declarationsNamed: readonly boolean[];
|
|
1036
956
|
readonly parts: ProjectParts;
|
|
1037
957
|
readonly unseenLoads: readonly UnseenLoad[];
|
|
958
|
+
readonly carriers: Carriers;
|
|
1038
959
|
}
|
|
1039
960
|
|
|
1040
961
|
/** The project's own files below the target root that are no test file. */
|
|
@@ -1090,6 +1011,8 @@ export function readEmitterRun(
|
|
|
1090
1011
|
declarationsNamed: declarationsNamed(project, read.held, configuredDeclarations(config)),
|
|
1091
1012
|
parts: projectParts(project, read.held, references, targetRoot),
|
|
1092
1013
|
unseenLoads,
|
|
1014
|
+
// Only a closed world reports an unnecessary export, which is all the carriers decide.
|
|
1015
|
+
carriers: config.consumers.complete ? carriersOf(project, read.held) : NO_CARRIERS,
|
|
1093
1016
|
}),
|
|
1094
1017
|
);
|
|
1095
1018
|
// Whether a declaration carries the marker decides its code, so one whose answer went
|
|
@@ -1156,7 +1079,7 @@ export function emitterInputOver(
|
|
|
1156
1079
|
scope: Scope,
|
|
1157
1080
|
config: ResolvedConfig,
|
|
1158
1081
|
read: ReadRun<EmitterExtra>,
|
|
1159
|
-
input: Pick<
|
|
1082
|
+
input: Pick<SweepPolicy, "marked" | "mode">,
|
|
1160
1083
|
selfCheck: SelfCheckFacts,
|
|
1161
1084
|
): EmitterInput {
|
|
1162
1085
|
const targetRoot = scope.target.path;
|
|
@@ -1173,7 +1096,7 @@ export function emitterInputOver(
|
|
|
1173
1096
|
support: new Set(one.configured.supportFiles ?? []),
|
|
1174
1097
|
accessors: one.extra.accessors,
|
|
1175
1098
|
})),
|
|
1176
|
-
swept.exempt,
|
|
1099
|
+
swept.sweep.exempt,
|
|
1177
1100
|
input.mode,
|
|
1178
1101
|
),
|
|
1179
1102
|
dependencies: dependenciesOf(
|
|
@@ -1203,12 +1126,23 @@ export function emitterInputOver(
|
|
|
1203
1126
|
edges: readEdgeSides(host, targetRoot),
|
|
1204
1127
|
filledOutside: new Set(projects.flatMap((one) => one.filledOutside)),
|
|
1205
1128
|
possiblyLoaded: new Set(projects.flatMap((one) => one.possiblyLoaded)),
|
|
1129
|
+
carriers: {
|
|
1130
|
+
named: new Map(
|
|
1131
|
+
[
|
|
1132
|
+
...Map.groupBy(
|
|
1133
|
+
projects.flatMap((one) => [...one.extra.carriers.named]),
|
|
1134
|
+
([id]) => id,
|
|
1135
|
+
),
|
|
1136
|
+
].map(([id, entries]) => [id, [...new Set(entries.flatMap(([, named]) => named))]]),
|
|
1137
|
+
),
|
|
1138
|
+
unread: new Set(projects.flatMap((one) => [...one.extra.carriers.unread])),
|
|
1139
|
+
},
|
|
1206
1140
|
},
|
|
1207
1141
|
selfCheck,
|
|
1208
1142
|
intraFunction: intraFunctionFacts(
|
|
1209
1143
|
projects.map((one) => one.extra.parts),
|
|
1210
1144
|
projects.flatMap((one) => one.configured.references),
|
|
1211
|
-
new Set([...swept.exempt.map((one) => one.id), ...swept.
|
|
1145
|
+
new Set([...swept.sweep.exempt.map((one) => one.id), ...swept.sweep.held]),
|
|
1212
1146
|
),
|
|
1213
1147
|
};
|
|
1214
1148
|
}
|
|
@@ -1448,7 +1382,7 @@ export function runAnalysis(
|
|
|
1448
1382
|
* declarations of its own the run holds for them.
|
|
1449
1383
|
*/
|
|
1450
1384
|
export function unansweredCounts(analysis: RunAnalysis): readonly UnansweredCount[] {
|
|
1451
|
-
const held = new Set(analysis.swept.
|
|
1385
|
+
const held = new Set(analysis.swept.sweep.held);
|
|
1452
1386
|
return analysis.run.projects.flatMap((project) => {
|
|
1453
1387
|
const questions =
|
|
1454
1388
|
analysis.run.unanswered.filter((one) => one.configFile === project.configFile).length +
|
package/src/augmentations.ts
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
type Node,
|
|
15
15
|
} from "@typescript/native/ast";
|
|
16
16
|
import { nodeKey, type Inventory, type InventorySymbol } from "./inventory.ts";
|
|
17
|
-
import { referenceKey } from "./matrix.ts";
|
|
17
|
+
import { referenceKey } from "./liveness/matrix.ts";
|
|
18
18
|
import type { Reference } from "./references.ts";
|
|
19
19
|
import type { ProjectView } from "./session.ts";
|
|
20
20
|
import type { SourceFiles } from "./source-files.ts";
|