@cplieger/deadset-ts 8.0.0-dev.1 → 8.0.0-dev.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/bin/mapper-connection.js +2 -1
- package/dist/bin/node-host.js +24 -5
- package/dist/src/analysis.js +181 -341
- package/dist/src/augmentations.js +106 -13
- package/dist/src/automatic-types.js +10 -29
- package/dist/src/build-scripts.js +425 -0
- package/dist/src/component-layer.js +4 -2
- package/dist/src/components.js +2 -3
- package/dist/src/config.js +6 -82
- package/dist/src/configuration-files.js +28 -176
- package/dist/src/configured-declarations.js +15 -20
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/consumers.js +3 -30
- package/dist/src/contextual-uses.js +56 -9
- package/dist/src/contract/config.generated.js +558 -0
- package/dist/src/contract/contract.generated.js +1244 -0
- package/dist/src/contract/report.generated.js +862 -0
- package/dist/src/convention-rows.js +73 -3
- package/dist/src/conventions.js +36 -66
- package/dist/src/dependencies.js +103 -40
- package/dist/src/edges.js +38 -122
- package/dist/src/element-access.js +4 -6
- package/dist/src/entry-point-gaps.js +2 -8
- package/dist/src/entry-points.js +11 -27
- package/dist/src/enum-group.js +3 -10
- package/dist/src/erased-stores.js +4 -6
- package/dist/src/exempt-classes.js +1 -63
- package/dist/src/exempt.js +21 -40
- package/dist/src/failure.js +36 -0
- package/dist/src/file-facts.js +2 -25
- package/dist/src/findings/completion.js +139 -72
- package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
- package/dist/src/findings/emitters.js +5 -5
- package/dist/src/findings/interfaces.js +4 -5
- package/dist/src/findings/intra-function.js +13 -10
- package/dist/src/findings/non-code-artifacts.js +7 -8
- package/dist/src/findings/reads-and-writes.js +16 -13
- package/dist/src/findings/self-check.js +10 -12
- package/dist/src/findings/unused-declarations.js +41 -32
- package/dist/src/findings/visibility-narrowing.js +19 -16
- package/dist/src/findings-pass.js +8 -15
- package/dist/src/framework-lifecycle.js +7 -7
- package/dist/src/fs/document.js +52 -0
- package/dist/src/fs/manifest.js +72 -0
- package/dist/src/fs/tree.js +88 -0
- package/dist/src/generated-file.js +7 -4
- package/dist/src/git-ignore.js +39 -26
- package/dist/src/graph.js +5 -3
- package/dist/src/html-entries.js +6 -33
- package/dist/src/in-operands.js +2 -1
- package/dist/src/index.js +1 -1
- package/dist/src/injection-container.js +1 -1
- package/dist/src/intra-function-parts.js +63 -43
- package/dist/src/inventory.js +2 -21
- package/dist/src/javascript-file.js +33 -0
- package/dist/src/json-document.js +198 -67
- package/dist/src/keyof-comparisons.js +4 -6
- package/dist/src/kinds.js +3 -366
- package/dist/src/load/classify.js +270 -0
- package/dist/src/load/diagnostics.js +80 -0
- package/dist/src/load/discover.js +277 -0
- package/dist/src/{emit-map.js → load/emit-map.js} +1 -1
- package/dist/src/load/expected-module.js +136 -0
- package/dist/src/load/load.js +27 -0
- package/dist/src/load/module-lookup.js +37 -0
- package/dist/src/load/plan.js +313 -0
- package/dist/src/load/specifiers.js +115 -0
- package/dist/src/load/synthesize.js +140 -0
- package/dist/src/load/test-globals.js +207 -0
- package/dist/src/load/test-joins.js +82 -0
- package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +4 -4
- package/dist/src/{workspace-programs.js → load/workspace-programs.js} +89 -66
- package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +23 -65
- package/dist/src/{workspace-run.js → load/workspace-run.js} +7 -7
- package/dist/src/{workspace.js → load/workspace.js} +62 -124
- package/dist/src/manifest.js +49 -54
- package/dist/src/matrix.js +76 -4
- package/dist/src/mdx-entries.js +3 -26
- package/dist/src/outside-tests.js +1 -1
- package/dist/src/partial.js +2 -1
- package/dist/src/paths.js +26 -15
- package/dist/src/position.js +2 -1
- package/dist/src/print.js +11 -79
- package/dist/src/product.js +13 -0
- package/dist/src/query.js +1 -0
- package/dist/src/ref.js +1 -0
- package/dist/src/references.js +48 -30
- package/dist/src/report-schema.js +51 -0
- package/dist/src/report.js +57 -41
- package/dist/src/reporters/annotations.js +15 -8
- package/dist/src/reporters/sarif.js +16 -10
- package/dist/src/reporters/template.js +4 -19
- package/dist/src/resolve.js +245 -729
- package/dist/src/roots.js +273 -87
- package/dist/src/run.js +66 -77
- package/dist/src/schema.js +320 -343
- package/dist/src/scope.js +38 -115
- package/dist/src/serialization-contract.js +595 -21
- package/dist/src/session.js +34 -29
- package/dist/src/source-files.js +1 -1
- package/dist/src/suppress-file.js +25 -28
- package/dist/src/suppress-inline.js +3 -2
- package/dist/src/suppress.js +33 -51
- package/dist/src/sweep.js +7 -9
- package/dist/src/symbol-keys.js +3 -5
- package/dist/src/template-field.js +9 -19
- package/dist/src/test-support.js +8 -11
- package/dist/src/text/compare.js +26 -0
- package/dist/src/text/glob.js +194 -0
- package/dist/src/text/named-files.js +54 -0
- package/dist/src/text/shell.js +159 -0
- package/dist/src/text/yaml.js +393 -0
- package/dist/src/type-errors.js +11 -232
- package/dist/src/unseen-loads.js +100 -0
- package/dist/src/unwrap.js +23 -0
- package/dist/src/usage.js +10 -0
- package/dist/src/verbs/analyze.js +166 -126
- package/dist/src/verbs/describe.js +9 -16
- package/dist/src/verbs/explain.js +10 -7
- package/dist/src/verbs/print-config.js +2 -1
- package/dist/src/verbs/print-projects.js +6 -4
- package/dist/src/verbs/print-retained.js +4 -2
- package/dist/src/verbs/print-roots.js +4 -2
- package/dist/src/verbs/verb.js +1 -5
- package/dist/src/verbs/version.js +14 -3
- package/dist/src/workflow-steps.js +62 -64
- package/jsr.json +1 -1
- package/package.json +5 -4
- package/src/analysis.ts +245 -502
- package/src/augmentations.ts +135 -17
- package/src/automatic-types.ts +11 -29
- package/src/build-scripts.ts +543 -0
- package/src/component-layer.ts +4 -2
- package/src/components.ts +2 -4
- package/src/config.ts +31 -302
- package/src/configuration-files.ts +34 -179
- package/src/configured-declarations.ts +18 -23
- package/src/conformance-record.ts +1 -1
- package/src/consumers.ts +6 -57
- package/src/contextual-uses.ts +64 -12
- package/src/contract/config.generated.ts +818 -0
- package/src/contract/contract.generated.ts +1308 -0
- package/src/contract/report.generated.ts +1237 -0
- package/src/convention-rows.ts +85 -3
- package/src/conventions.ts +46 -84
- package/src/dependencies.ts +118 -46
- package/src/edges.ts +41 -140
- package/src/element-access.ts +4 -7
- package/src/entry-point-gaps.ts +3 -9
- package/src/entry-points.ts +12 -38
- package/src/enum-group.ts +2 -11
- package/src/erased-stores.ts +3 -7
- package/src/exempt-classes.ts +1 -75
- package/src/exempt.ts +36 -50
- package/src/failure.ts +63 -0
- package/src/file-facts.ts +9 -24
- package/src/finding.ts +14 -9
- package/src/findings/boundary.ts +2 -0
- package/src/findings/completion.ts +181 -84
- package/src/findings/dependencies-and-module-machinery.ts +4 -7
- package/src/findings/emitter.ts +8 -9
- package/src/findings/emitters.ts +7 -7
- package/src/findings/interfaces.ts +4 -6
- package/src/findings/intra-function.ts +13 -11
- package/src/findings/non-code-artifacts.ts +9 -11
- package/src/findings/reads-and-writes.ts +20 -16
- package/src/findings/self-check.ts +11 -17
- package/src/findings/unused-declarations.ts +45 -32
- package/src/findings/visibility-narrowing.ts +20 -17
- package/src/findings-pass.ts +10 -19
- package/src/framework-lifecycle.ts +7 -7
- package/src/fs/document.ts +70 -0
- package/src/fs/manifest.ts +92 -0
- package/src/fs/tree.ts +110 -0
- package/src/generated-file.ts +7 -4
- package/src/git-ignore.ts +50 -26
- package/src/graph.ts +5 -3
- package/src/host.ts +12 -5
- package/src/html-entries.ts +6 -33
- package/src/in-operands.ts +2 -1
- package/src/index.ts +1 -1
- package/src/injection-container.ts +1 -1
- package/src/intra-function-parts.ts +73 -44
- package/src/inventory.ts +2 -20
- package/src/javascript-file.ts +37 -0
- package/src/json-document.ts +241 -76
- package/src/keyof-comparisons.ts +3 -7
- package/src/kinds.ts +5 -413
- package/src/load/classify.ts +397 -0
- package/src/load/diagnostics.ts +144 -0
- package/src/load/discover.ts +455 -0
- package/src/{emit-map.ts → load/emit-map.ts} +1 -1
- package/src/load/expected-module.ts +188 -0
- package/src/load/load.ts +73 -0
- package/src/load/module-lookup.ts +50 -0
- package/src/load/plan.ts +504 -0
- package/src/load/specifiers.ts +168 -0
- package/src/load/synthesize.ts +211 -0
- package/src/load/test-globals.ts +251 -0
- package/src/load/test-joins.ts +98 -0
- package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +5 -5
- package/src/{workspace-programs.ts → load/workspace-programs.ts} +128 -85
- package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +33 -76
- package/src/{workspace-run.ts → load/workspace-run.ts} +18 -16
- package/src/{workspace.ts → load/workspace.ts} +90 -134
- package/src/manifest.ts +81 -75
- package/src/matrix.ts +122 -4
- package/src/mdx-entries.ts +3 -26
- package/src/outside-tests.ts +1 -1
- package/src/partial.ts +3 -2
- package/src/paths.ts +27 -15
- package/src/position.ts +2 -1
- package/src/print.ts +12 -92
- package/src/product.ts +18 -0
- package/src/query.ts +11 -0
- package/src/ref.ts +1 -0
- package/src/references.ts +52 -31
- package/src/report-schema.ts +68 -0
- package/src/report.ts +101 -224
- package/src/reporters/annotations.ts +22 -11
- package/src/reporters/json.ts +1 -1
- package/src/reporters/reporter.ts +2 -4
- package/src/reporters/reporters.ts +1 -1
- package/src/reporters/sarif.ts +29 -16
- package/src/reporters/template.ts +4 -20
- package/src/reporters/text.ts +3 -3
- package/src/resolve.ts +283 -1114
- package/src/roots.ts +339 -104
- package/src/run.ts +75 -91
- package/src/schema.ts +391 -388
- package/src/scope.ts +46 -138
- package/src/serialization-contract.ts +763 -22
- package/src/session.ts +70 -54
- package/src/source-files.ts +1 -1
- package/src/suppress-file.ts +24 -33
- package/src/suppress-inline.ts +2 -2
- package/src/suppress.ts +43 -58
- package/src/sweep.ts +23 -9
- package/src/symbol-keys.ts +2 -5
- package/src/template-field.ts +15 -25
- package/src/test-support.ts +17 -13
- package/src/text/compare.ts +27 -0
- package/src/text/glob.ts +210 -0
- package/src/text/named-files.inputs.json +1522 -0
- package/src/text/named-files.ts +68 -0
- package/src/text/shell.ts +161 -0
- package/src/text/yaml.ts +423 -0
- package/src/type-errors.ts +18 -288
- package/src/unseen-loads.ts +134 -0
- package/src/unwrap.ts +34 -0
- package/src/usage.ts +10 -0
- package/src/verbs/analyze.ts +224 -162
- package/src/verbs/describe.ts +14 -15
- package/src/verbs/explain.ts +10 -11
- package/src/verbs/print-config.ts +2 -1
- package/src/verbs/print-projects.ts +6 -4
- package/src/verbs/print-retained.ts +4 -2
- package/src/verbs/print-roots.ts +4 -2
- package/src/verbs/verb.ts +2 -8
- package/src/verbs/version.ts +15 -3
- package/src/workflow-steps.ts +74 -64
- package/dist/src/base-url.js +0 -18
- package/dist/src/discover.js +0 -468
- package/dist/src/glob.js +0 -65
- package/dist/src/pnpm-workspace.js +0 -120
- package/dist/src/setup-failure.js +0 -23
- package/dist/src/test-joins.js +0 -101
- package/dist/src/version.js +0 -7
- package/src/base-url.ts +0 -24
- package/src/discover.ts +0 -653
- package/src/glob.ts +0 -67
- package/src/pnpm-workspace.ts +0 -135
- package/src/setup-failure.ts +0 -39
- package/src/test-joins.ts +0 -120
- package/src/version.ts +0 -7
package/dist/src/edges.js
CHANGED
|
@@ -4,153 +4,69 @@
|
|
|
4
4
|
* never resolves the paired symbol: the other language's analyzer decides whether the
|
|
5
5
|
* pairing is a use, and the merge reads the two evaluations together.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
7
|
+
import { EDGES_SCHEMA } from "./contract/config.generated.js";
|
|
8
|
+
import { readTargetDocument } from "./fs/document.js";
|
|
9
|
+
import { dotted, placeIn, positionAt, readStrict, StrictJSONError } from "./json-document.js";
|
|
10
10
|
import { positionKey } from "./position.js";
|
|
11
|
+
import { firstRefusal } from "./schema.js";
|
|
11
12
|
import { isPartSubject } from "./suppress.js";
|
|
13
|
+
import { bytewise } from "./text/compare.js";
|
|
14
|
+
import { UsageError } from "./usage.js";
|
|
12
15
|
/** The name and the location the Contract fixes for the edges document. */
|
|
13
16
|
export const EDGES_FILE = "deadset-edges.json";
|
|
14
17
|
/** The language prefix of every reference this analyzer evaluates. */
|
|
15
18
|
const OWN_PREFIX = "ts://";
|
|
16
|
-
/** The closed key list of the document and of one edge. */
|
|
17
|
-
const DOCUMENT_KEYS = new Set(["description", "edges"]);
|
|
18
|
-
const EDGE_KEYS = new Set(["id", "because", "provides", "used_by"]);
|
|
19
|
-
/** An edge identifier: solidus-separated segments of ASCII letters, digits, `_`, `.` and `-`. */
|
|
20
|
-
const ID_FORM = /^[A-Za-z0-9_.-]+(?:\/[A-Za-z0-9_.-]+)*$/u;
|
|
21
|
-
/** A reference naming one symbol of some language: `<language>://<scope>#<fragment>`. */
|
|
22
|
-
const REFERENCE_FORM = /^[a-z][a-z0-9]*:\/\/[^ \t\r\n#]+#[^ \t\r\n]*$/u;
|
|
23
|
-
const DOCUMENT_WANT = "an object carrying the edges array, and an optional description";
|
|
24
|
-
const EDGE_WANT = "an edge naming id, provides and used_by, each a string, and an optional because";
|
|
25
|
-
const ID_WANT = "one or more solidus-separated segments of ASCII letters, digits, underscore, full stop and hyphen";
|
|
26
|
-
const REFERENCE_WANT = "a stable symbol reference naming one symbol exactly, <language>://<scope>#<fragment>";
|
|
27
19
|
/** An edges document the Contract refuses: a malformed input, answered with the usage code. */
|
|
28
|
-
class EdgesError extends
|
|
29
|
-
constructor(at,
|
|
30
|
-
super(
|
|
20
|
+
class EdgesError extends UsageError {
|
|
21
|
+
constructor(at, reason) {
|
|
22
|
+
super(`${at}: ${reason}`);
|
|
31
23
|
this.name = "EdgesError";
|
|
32
24
|
}
|
|
33
25
|
}
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
* would name a set of symbols rather than one.
|
|
37
|
-
*/
|
|
38
|
-
function carriesWildcard(ref) {
|
|
39
|
-
let quoted = false;
|
|
40
|
-
let computed = false;
|
|
41
|
-
let escaped = false;
|
|
42
|
-
for (const char of ref) {
|
|
43
|
-
if (escaped) {
|
|
44
|
-
escaped = false;
|
|
45
|
-
}
|
|
46
|
-
else if (quoted && char === "\\") {
|
|
47
|
-
escaped = true;
|
|
48
|
-
}
|
|
49
|
-
else if (quoted) {
|
|
50
|
-
quoted = char !== "'";
|
|
51
|
-
}
|
|
52
|
-
else if (computed) {
|
|
53
|
-
computed = char !== "]";
|
|
54
|
-
}
|
|
55
|
-
else if (char === "'") {
|
|
56
|
-
quoted = true;
|
|
57
|
-
}
|
|
58
|
-
else if (char === "[") {
|
|
59
|
-
computed = true;
|
|
60
|
-
}
|
|
61
|
-
else if (char === "*") {
|
|
62
|
-
return true;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
function isObject(value) {
|
|
68
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
69
|
-
}
|
|
70
|
-
/** One edge, checked: the closed key list, the value types and the value forms. */
|
|
71
|
-
function readEdge(value, at) {
|
|
72
|
-
const refuse = (text, want) => {
|
|
73
|
-
throw new EdgesError(at, text, want);
|
|
74
|
-
};
|
|
75
|
-
if (!isObject(value)) {
|
|
76
|
-
return refuse(JSON.stringify(value), EDGE_WANT);
|
|
77
|
-
}
|
|
78
|
-
for (const [key, held] of Object.entries(value)) {
|
|
79
|
-
if (!EDGE_KEYS.has(key) || typeof held !== "string") {
|
|
80
|
-
refuse(`the member ${JSON.stringify(key)} holding ${JSON.stringify(held)}`, EDGE_WANT);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
const edge = value;
|
|
84
|
-
const id = edge["id"] ?? refuse("an edge naming no id", EDGE_WANT);
|
|
85
|
-
const provides = edge["provides"] ?? refuse("an edge naming no provides side", EDGE_WANT);
|
|
86
|
-
const usedBy = edge["used_by"] ?? refuse("an edge naming no used_by side", EDGE_WANT);
|
|
87
|
-
if (!ID_FORM.test(id)) {
|
|
88
|
-
refuse(JSON.stringify(id), ID_WANT);
|
|
89
|
-
}
|
|
90
|
-
for (const ref of [provides, usedBy]) {
|
|
91
|
-
if (!REFERENCE_FORM.test(ref) || carriesWildcard(ref)) {
|
|
92
|
-
refuse(JSON.stringify(ref), REFERENCE_WANT);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
return { id, provides, usedBy };
|
|
96
|
-
}
|
|
26
|
+
/** The sides of one edge, in the order a reader yields them. */
|
|
27
|
+
const SIDE_NAMES = ["provides", "used_by"];
|
|
97
28
|
/**
|
|
98
29
|
* Every side of a declared edge that names a symbol of this language, in document order,
|
|
99
30
|
* `provides` before `used_by` within an edge. An absent document declares no edge. The
|
|
100
|
-
* document is strict JSON
|
|
101
|
-
* {@link EdgesError}.
|
|
31
|
+
* document is strict JSON held to the Contract's edges schema, and a defect in it is an
|
|
32
|
+
* {@link EdgesError} at the line and column of the edge it is in, where it is in one.
|
|
102
33
|
*/
|
|
103
34
|
export function readEdgeSides(host, targetRoot) {
|
|
104
|
-
const
|
|
105
|
-
if (
|
|
35
|
+
const text = readTargetDocument(host, targetRoot, EDGES_FILE);
|
|
36
|
+
if (text === undefined) {
|
|
106
37
|
return [];
|
|
107
38
|
}
|
|
108
|
-
|
|
109
|
-
let parsed;
|
|
39
|
+
let read;
|
|
110
40
|
try {
|
|
111
|
-
|
|
41
|
+
read = readStrict(text, "edges");
|
|
112
42
|
}
|
|
113
43
|
catch (error) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
throw new EdgesError(EDGES_FILE,
|
|
118
|
-
}
|
|
119
|
-
const walk = walkDocument(text, "edges");
|
|
120
|
-
if (walk.twice !== undefined) {
|
|
121
|
-
throw new EdgesError(positionKey(positionAt(text, EDGES_FILE, walk.twice.offset)), `the member ${JSON.stringify(walk.twice.name)} is written twice, so neither value is chosen`, DOCUMENT_WANT);
|
|
44
|
+
if (!(error instanceof StrictJSONError)) {
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
throw new EdgesError(positionKey(positionAt(text, EDGES_FILE, error.offset)), error.message);
|
|
122
48
|
}
|
|
123
|
-
const
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
49
|
+
const value = read.value;
|
|
50
|
+
const refused = firstRefusal(EDGES_SCHEMA, value);
|
|
51
|
+
if (refused !== undefined) {
|
|
52
|
+
const place = placeIn(value, refused.pointer, refused.member);
|
|
53
|
+
const offset = place.length > 1 ? read.starts.get(place[1] ?? "") : undefined;
|
|
54
|
+
const at = offset === undefined ? EDGES_FILE : positionKey(positionAt(text, EDGES_FILE, offset));
|
|
55
|
+
const where = dotted(place);
|
|
56
|
+
throw new EdgesError(at, where === "" ? refused.reason : `${where}: ${refused.reason}`);
|
|
130
57
|
}
|
|
58
|
+
const edges = value.edges;
|
|
131
59
|
const sides = [];
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
["used_by", edge.usedBy],
|
|
139
|
-
];
|
|
140
|
-
for (const [side, symbol] of named) {
|
|
141
|
-
if (symbol.startsWith(OWN_PREFIX)) {
|
|
142
|
-
sides.push({ edge: edge.id, side, symbol });
|
|
60
|
+
for (const id of read.starts.keys()) {
|
|
61
|
+
const edge = String(id);
|
|
62
|
+
for (const side of SIDE_NAMES) {
|
|
63
|
+
const symbol = edges[edge]?.[side];
|
|
64
|
+
if (symbol?.startsWith(OWN_PREFIX) === true) {
|
|
65
|
+
sides.push({ edge, side, symbol });
|
|
143
66
|
}
|
|
144
67
|
}
|
|
145
|
-
});
|
|
146
|
-
return sides;
|
|
147
|
-
}
|
|
148
|
-
/** Two strings ordered bytewise. */
|
|
149
|
-
function compare(a, b) {
|
|
150
|
-
if (a === b) {
|
|
151
|
-
return 0;
|
|
152
68
|
}
|
|
153
|
-
return
|
|
69
|
+
return sides;
|
|
154
70
|
}
|
|
155
71
|
/**
|
|
156
72
|
* Evaluates every declared side of this language over the run's findings. A side naming a
|
|
@@ -191,6 +107,6 @@ export function evaluateEdges(findings, sides, symbols) {
|
|
|
191
107
|
return { ...side, state: "dead", finding };
|
|
192
108
|
});
|
|
193
109
|
});
|
|
194
|
-
evaluations.sort((a, b) =>
|
|
110
|
+
evaluations.sort((a, b) => bytewise(a.edge, b.edge) || bytewise(a.side, b.side));
|
|
195
111
|
return { findings: findings.filter((_finding, at) => !pending.has(at)), evaluations };
|
|
196
112
|
}
|
|
@@ -8,6 +8,7 @@ import { isArrayLiteralExpression, isArrowFunction, isBinaryExpression, isCallEx
|
|
|
8
8
|
import { nodeKey } from "./inventory.js";
|
|
9
9
|
import { renderPosition } from "./position.js";
|
|
10
10
|
import { UNANSWERED } from "./query.js";
|
|
11
|
+
import { parenthesesRemoved } from "./unwrap.js";
|
|
11
12
|
/** The assignment operators that read their target before they write it. */
|
|
12
13
|
const COMPOUND = new Set([
|
|
13
14
|
SyntaxKind.PlusEqualsToken,
|
|
@@ -47,12 +48,9 @@ function useOf(node) {
|
|
|
47
48
|
}
|
|
48
49
|
return "read";
|
|
49
50
|
}
|
|
50
|
-
function unwrapped(node) {
|
|
51
|
-
return isParenthesizedExpression(node) ? unwrapped(node.expression) : node;
|
|
52
|
-
}
|
|
53
51
|
/** Whether an index is a literal the checker resolves to a member by its own text. */
|
|
54
52
|
function isLiteralIndex(node) {
|
|
55
|
-
const at =
|
|
53
|
+
const at = parenthesesRemoved(node);
|
|
56
54
|
return isStringLiteral(at) || isNumericLiteral(at) || isNoSubstitutionTemplateLiteral(at);
|
|
57
55
|
}
|
|
58
56
|
/**
|
|
@@ -195,7 +193,7 @@ export function elementAccessReferences(project, held, chains, root, testFiles)
|
|
|
195
193
|
const parameterOf = new Map();
|
|
196
194
|
const byCallable = new Map();
|
|
197
195
|
for (const candidate of candidates) {
|
|
198
|
-
const index =
|
|
196
|
+
const index = parenthesesRemoved(candidate.access.argumentExpression);
|
|
199
197
|
const fn = isIdentifier(index) ? enclosingFunction(candidate.access) : undefined;
|
|
200
198
|
const callable = fn === undefined ? undefined : callableOf(fn);
|
|
201
199
|
if (callable === undefined || !isIdentifier(index)) {
|
|
@@ -252,7 +250,7 @@ export function elementAccessReferences(project, held, chains, root, testFiles)
|
|
|
252
250
|
continue;
|
|
253
251
|
}
|
|
254
252
|
// An object that is a parameter too is read as the argument each call passes there.
|
|
255
|
-
const object =
|
|
253
|
+
const object = parenthesesRemoved(candidate.access.expression);
|
|
256
254
|
const objectAt = isIdentifier(object)
|
|
257
255
|
? deferred.callable.parameters.findIndex((parameter) => isIdentifier(parameter.name) && parameter.name.text === object.text)
|
|
258
256
|
: -1;
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* Every row carries a reason, because the list is read where the rows are shown
|
|
8
8
|
* one at a time: beside a finding, or in a report's declared gaps.
|
|
9
9
|
*/
|
|
10
|
+
import { bytewise } from "./text/compare.js";
|
|
10
11
|
/** What is declined for a tool whose own conventions are entered only through its configuration files. */
|
|
11
12
|
const EVERY_CONVENTION = "every file it loads by its own convention beyond its configuration files";
|
|
12
13
|
/** Why a tool whose own conventions are not read declines them. */
|
|
@@ -291,15 +292,8 @@ const PARTLY_ENTERED = [
|
|
|
291
292
|
reason: NOT_LITERAL,
|
|
292
293
|
},
|
|
293
294
|
];
|
|
294
|
-
/** Two strings ordered bytewise. */
|
|
295
|
-
function compare(a, b) {
|
|
296
|
-
if (a === b) {
|
|
297
|
-
return 0;
|
|
298
|
-
}
|
|
299
|
-
return a < b ? -1 : 1;
|
|
300
|
-
}
|
|
301
295
|
/** Every declined entry-point convention, ordered by tool and then by convention. */
|
|
302
296
|
export const DECLINED_CONVENTIONS = [
|
|
303
297
|
...UNREAD_TOOLS.map((tool) => ({ tool, convention: EVERY_CONVENTION, reason: UNREAD })),
|
|
304
298
|
...PARTLY_ENTERED,
|
|
305
|
-
].sort((a, b) =>
|
|
299
|
+
].sort((a, b) => bytewise(a.tool, b.tool) || bytewise(a.convention, b.convention));
|
package/dist/src/entry-points.js
CHANGED
|
@@ -10,15 +10,18 @@
|
|
|
10
10
|
* value that is not a literal is not evaluated and not guessed, so a file it would
|
|
11
11
|
* have named is entered by nothing here.
|
|
12
12
|
*/
|
|
13
|
-
import { isArrayLiteralExpression,
|
|
13
|
+
import { isArrayLiteralExpression, isCallExpression, isExportAssignment, isIdentifier, isMetaProperty, isNewExpression, isNoSubstitutionTemplateLiteral, isObjectLiteralExpression, isPropertyAccessExpression, isPropertyAssignment, isStringLiteral, SyntaxKind, } from "@typescript/native/ast";
|
|
14
14
|
import { SymbolFlags } from "@typescript/native/sync";
|
|
15
15
|
import { configurationFiles, filesNamedBy, moduleStrings, } from "./configuration-files.js";
|
|
16
|
-
import {
|
|
16
|
+
import { buildScriptEntries } from "./build-scripts.js";
|
|
17
17
|
import { htmlEntries } from "./html-entries.js";
|
|
18
18
|
import { documentEntries } from "./mdx-entries.js";
|
|
19
19
|
import { dirnamePath, relativePath, resolvePath } from "./paths.js";
|
|
20
20
|
import { UNANSWERED } from "./query.js";
|
|
21
|
+
import { computedLoadDirectory, leadingLiteral } from "./unseen-loads.js";
|
|
21
22
|
import { workflowEntries } from "./workflow-steps.js";
|
|
23
|
+
import { unwrapped } from "./unwrap.js";
|
|
24
|
+
import { globExpression } from "./text/glob.js";
|
|
22
25
|
/** The extensions the test runner and the browser-test runner load a configuration from. */
|
|
23
26
|
const MODULE_EXTENSIONS = ["js", "mjs", "cjs", "ts", "mts", "cts"];
|
|
24
27
|
/** The tool file names the configuration form does not cover. */
|
|
@@ -67,14 +70,6 @@ function literalOf(node) {
|
|
|
67
70
|
}
|
|
68
71
|
return undefined;
|
|
69
72
|
}
|
|
70
|
-
/** The expression a wrapper that changes no value holds: parentheses and type assertions. */
|
|
71
|
-
function unwrapped(node) {
|
|
72
|
-
let at = node;
|
|
73
|
-
while (isParenthesizedExpression(at) || isAsExpression(at) || isSatisfiesExpression(at)) {
|
|
74
|
-
at = at.expression;
|
|
75
|
-
}
|
|
76
|
-
return at;
|
|
77
|
-
}
|
|
78
73
|
/**
|
|
79
74
|
* The object literals one configuration file's default export is written as: the
|
|
80
75
|
* literal itself, or each literal argument of the call it is, at any depth of calls.
|
|
@@ -210,7 +205,7 @@ function configurationEntries(host, targetRoot, files, parse, selected) {
|
|
|
210
205
|
strings(dirnamePath(document.path), unselectedIn(document.path, document.strings));
|
|
211
206
|
}
|
|
212
207
|
for (const { file } of outside) {
|
|
213
|
-
strings(dirnamePath(file.fileName),
|
|
208
|
+
strings(dirnamePath(file.fileName), unselected(file));
|
|
214
209
|
}
|
|
215
210
|
return found;
|
|
216
211
|
}
|
|
@@ -398,17 +393,6 @@ function workersOf(project, file, files) {
|
|
|
398
393
|
}
|
|
399
394
|
return found;
|
|
400
395
|
}
|
|
401
|
-
/** The literal text a computed path starts with: a template's head, or a concatenation's first literal. */
|
|
402
|
-
function leadingLiteral(node) {
|
|
403
|
-
const at = unwrapped(node);
|
|
404
|
-
if (isTemplateExpression(at)) {
|
|
405
|
-
return at.head.text;
|
|
406
|
-
}
|
|
407
|
-
if (isBinaryExpression(at) && at.operatorToken.kind === SyntaxKind.PlusToken) {
|
|
408
|
-
return literalOf(unwrapped(at.left)) ?? leadingLiteral(at.left);
|
|
409
|
-
}
|
|
410
|
-
return undefined;
|
|
411
|
-
}
|
|
412
396
|
/** Whether one call is `import.meta.glob(...)`. */
|
|
413
397
|
function isMetaGlob(node) {
|
|
414
398
|
if (!isCallExpression(node) || !isPropertyAccessExpression(node.expression)) {
|
|
@@ -432,11 +416,8 @@ function computedImportsOf(file, files) {
|
|
|
432
416
|
const dir = dirnamePath(file.fileName);
|
|
433
417
|
const found = [];
|
|
434
418
|
const below = (prefix) => {
|
|
435
|
-
const target =
|
|
436
|
-
if (
|
|
437
|
-
!prefix.endsWith("/") ||
|
|
438
|
-
target === dir ||
|
|
439
|
-
relativePath(target, dir) !== undefined) {
|
|
419
|
+
const target = computedLoadDirectory(dir, prefix);
|
|
420
|
+
if (target === undefined) {
|
|
440
421
|
return;
|
|
441
422
|
}
|
|
442
423
|
for (const candidate of files.byName.values()) {
|
|
@@ -522,6 +503,9 @@ export function entryPoints(project, files, testFiles, host, targetRoot, parse,
|
|
|
522
503
|
...(names === undefined ? {} : { names }),
|
|
523
504
|
});
|
|
524
505
|
}
|
|
506
|
+
for (const { file, source } of buildScriptEntries(host, targetRoot, files, parse)) {
|
|
507
|
+
found.push({ file, rule: "bundler-entry", source, exports: true });
|
|
508
|
+
}
|
|
525
509
|
for (const { file, source } of workflowEntries(host, targetRoot, files)) {
|
|
526
510
|
found.push({ file, rule: "workflow-step", source, exports: true });
|
|
527
511
|
}
|
package/dist/src/enum-group.js
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* or `unknown` expression asserted to it, or bound to a variable or returned from a
|
|
7
7
|
* function it annotates, where a number counts too. An enum's own value converts nothing.
|
|
8
8
|
*/
|
|
9
|
-
import { isArrowFunction, isAsExpression, isElementAccessExpression, isEnumDeclaration, isFunctionDeclaration, isFunctionExpression, isGetAccessorDeclaration, isIdentifier, isMethodDeclaration, isNoSubstitutionTemplateLiteral,
|
|
9
|
+
import { isArrowFunction, isAsExpression, isElementAccessExpression, isEnumDeclaration, isFunctionDeclaration, isFunctionExpression, isGetAccessorDeclaration, isIdentifier, isMethodDeclaration, isNoSubstitutionTemplateLiteral, isParenthesizedTypeNode, isPropertyAccessExpression, isReturnStatement, isStringLiteral, isVariableDeclaration, SyntaxKind, } from "@typescript/native/ast";
|
|
10
|
+
import { parenthesesRemoved } from "./unwrap.js";
|
|
10
11
|
import { TypeFlags } from "@typescript/native/sync";
|
|
11
12
|
import { aliasChains } from "./alias-chain.js";
|
|
12
13
|
import { nodeKey } from "./inventory.js";
|
|
@@ -16,14 +17,6 @@ import { isAnswered, UNANSWERED } from "./query.js";
|
|
|
16
17
|
const NUMBER = TypeFlags.NumberLike;
|
|
17
18
|
const STRING = TypeFlags.StringLike;
|
|
18
19
|
const DECODED = TypeFlags.Any | TypeFlags.Unknown;
|
|
19
|
-
/** The expression one node is, through however many parentheses are written around it. */
|
|
20
|
-
function unparenthesized(node) {
|
|
21
|
-
let held = node;
|
|
22
|
-
while (isParenthesizedExpression(held)) {
|
|
23
|
-
held = held.expression;
|
|
24
|
-
}
|
|
25
|
-
return held;
|
|
26
|
-
}
|
|
27
20
|
/**
|
|
28
21
|
* The name node a type node names its declaration by: the identifier of a type
|
|
29
22
|
* reference, the right side of a qualified one, through parentheses. A type that is no
|
|
@@ -45,7 +38,7 @@ function typeName(type) {
|
|
|
45
38
|
}
|
|
46
39
|
/** The name node an expression names an object by: an identifier, or a property access's name. */
|
|
47
40
|
function objectName(expression) {
|
|
48
|
-
const held =
|
|
41
|
+
const held = parenthesesRemoved(expression);
|
|
49
42
|
if (isIdentifier(held)) {
|
|
50
43
|
return held;
|
|
51
44
|
}
|
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
* either stores. A store into an erased property of a type the analyzed program does not
|
|
7
7
|
* declare hands the value to the code that declares it.
|
|
8
8
|
*/
|
|
9
|
-
import { isArrayLiteralExpression, isBinaryExpression, isCallExpression, isElementAccessExpression, isIdentifier, isNoSubstitutionTemplateLiteral, isNumericLiteral, isObjectLiteralExpression,
|
|
9
|
+
import { isArrayLiteralExpression, isBinaryExpression, isCallExpression, isElementAccessExpression, isIdentifier, isNoSubstitutionTemplateLiteral, isNumericLiteral, isObjectLiteralExpression, isPropertyAccessExpression, isPropertyAssignment, isShorthandPropertyAssignment, isSpreadElement, isStringLiteral, isVariableDeclaration, SyntaxKind, } from "@typescript/native/ast";
|
|
10
10
|
import { SymbolFlags, TypeFlags } from "@typescript/native/sync";
|
|
11
11
|
import { must } from "./query.js";
|
|
12
12
|
import { checkedTypes } from "./value-reach.js";
|
|
13
|
+
import { parenthesesRemoved } from "./unwrap.js";
|
|
13
14
|
/** The types that keep nothing of the value they are given. */
|
|
14
15
|
const ERASED = TypeFlags.Any | TypeFlags.Unknown;
|
|
15
16
|
/** The array methods whose arguments become elements of the array they are called on. */
|
|
@@ -20,9 +21,6 @@ function literalKey(node) {
|
|
|
20
21
|
? node.text
|
|
21
22
|
: undefined;
|
|
22
23
|
}
|
|
23
|
-
function unwrapped(node) {
|
|
24
|
-
return isParenthesizedExpression(node) ? unwrapped(node.expression) : node;
|
|
25
|
-
}
|
|
26
24
|
/**
|
|
27
25
|
* Whether a variable declaration's type can differ from what is stored in it: it carries an
|
|
28
26
|
* annotation, or it has neither an annotation nor an initializer, so its type evolves.
|
|
@@ -76,8 +74,8 @@ function propertyAt(source, name) {
|
|
|
76
74
|
* here, and a default value is stored in its target too.
|
|
77
75
|
*/
|
|
78
76
|
function patternTargets(pattern, from, out) {
|
|
79
|
-
const target =
|
|
80
|
-
const source = from.path.length === 0 ?
|
|
77
|
+
const target = parenthesesRemoved(pattern);
|
|
78
|
+
const source = from.path.length === 0 ? parenthesesRemoved(from.value) : from.value;
|
|
81
79
|
const into = (element, step, literal) => {
|
|
82
80
|
patternTargets(element, literal === undefined
|
|
83
81
|
? { value: from.value, path: [...from.path, step] }
|
|
@@ -1,62 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* The exemption classes of the Contract's vocabulary, in its order: each class's name, its
|
|
3
|
-
* languages and, for a TypeScript class, which private members it may retain. The rows are
|
|
4
|
-
* written out, as the fixed severity codes are, and a test holds them equal to the Contract.
|
|
5
|
-
* A class name is a type, so a table or a record naming a class outside the vocabulary, or
|
|
6
|
-
* one of another language only, does not compile.
|
|
7
|
-
*/
|
|
8
|
-
/** Every class of the vocabulary, in the order the Contract lists them. */
|
|
9
|
-
export const EXEMPTION_CLASSES = [
|
|
10
|
-
{
|
|
11
|
-
class: "interface-satisfaction",
|
|
12
|
-
languages: ["go", "ts"],
|
|
13
|
-
typescriptVisibility: { private: false, privateName: false },
|
|
14
|
-
},
|
|
15
|
-
{ class: "encoding-reflection", languages: ["go"] },
|
|
16
|
-
{ class: "format-verb-contract", languages: ["go"] },
|
|
17
|
-
{ class: "errors-duck-typing", languages: ["go"] },
|
|
18
|
-
{
|
|
19
|
-
class: "enum-group",
|
|
20
|
-
languages: ["go", "ts"],
|
|
21
|
-
typescriptVisibility: { private: false, privateName: false },
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
class: "generated-file",
|
|
25
|
-
languages: ["go", "ts"],
|
|
26
|
-
typescriptVisibility: { private: true, privateName: false },
|
|
27
|
-
},
|
|
28
|
-
{ class: "linkname-cgo-asm-plugin", languages: ["go"] },
|
|
29
|
-
{
|
|
30
|
-
class: "template-field",
|
|
31
|
-
languages: ["go", "ts"],
|
|
32
|
-
typescriptVisibility: { private: true, privateName: false },
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
class: "reflective-lookup",
|
|
36
|
-
languages: ["go", "ts"],
|
|
37
|
-
typescriptVisibility: { private: true, privateName: false },
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
class: "decorator",
|
|
41
|
-
languages: ["ts"],
|
|
42
|
-
typescriptVisibility: { private: true, privateName: false },
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
class: "injection-container",
|
|
46
|
-
languages: ["ts"],
|
|
47
|
-
typescriptVisibility: { private: true, privateName: false },
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
class: "framework-lifecycle",
|
|
51
|
-
languages: ["ts"],
|
|
52
|
-
typescriptVisibility: { private: true, privateName: false },
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
class: "serialization-contract",
|
|
56
|
-
languages: ["ts"],
|
|
57
|
-
typescriptVisibility: { private: true, privateName: false },
|
|
58
|
-
},
|
|
59
|
-
];
|
|
1
|
+
import { EXEMPTION_CLASSES } from "./contract/contract.generated.js";
|
|
60
2
|
function runsOnTS(row) {
|
|
61
3
|
const languages = row.languages;
|
|
62
4
|
return languages.includes("ts");
|
|
@@ -69,7 +11,3 @@ const VISIBILITY = new Map(TS_ROWS.map((row) => [row.class, row.typescriptVisibi
|
|
|
69
11
|
export function typescriptVisibilityOf(exemptionClass) {
|
|
70
12
|
return VISIBILITY.get(exemptionClass) ?? { private: false, privateName: false };
|
|
71
13
|
}
|
|
72
|
-
/** Whether one name is a class of the vocabulary, whichever language it runs on. */
|
|
73
|
-
export function isExemptionClass(name) {
|
|
74
|
-
return EXEMPTION_CLASSES.some((row) => row.class === name);
|
|
75
|
-
}
|
package/dist/src/exempt.js
CHANGED
|
@@ -6,26 +6,18 @@
|
|
|
6
6
|
* which records held something back. It names no class's detector: the composition
|
|
7
7
|
* hands it the table of the classes it has.
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { EXEMPTION_CLASSES } from "./contract/contract.generated.js";
|
|
10
|
+
import { TS_EXEMPTION_CLASSES, typescriptVisibilityOf, } from "./exempt-classes.js";
|
|
11
11
|
import { byPosition, positionKey } from "./position.js";
|
|
12
12
|
import { Unanswerable } from "./query.js";
|
|
13
13
|
import { sweep } from "./sweep.js";
|
|
14
|
+
import { bytewise } from "./text/compare.js";
|
|
14
15
|
/**
|
|
15
|
-
* The classes `exemptions.disabled` switches off. A
|
|
16
|
-
*
|
|
17
|
-
* switches nothing off here, so one configuration serves every analyzer.
|
|
16
|
+
* The classes `exemptions.disabled` switches off. A class that runs on another language
|
|
17
|
+
* only switches nothing off here, so one configuration serves every analyzer.
|
|
18
18
|
*/
|
|
19
19
|
export function disabledClasses(config) {
|
|
20
|
-
|
|
21
|
-
for (const name of config.exemptionsDisabled) {
|
|
22
|
-
if (!isExemptionClass(name)) {
|
|
23
|
-
const known = EXEMPTION_CLASSES.map((row) => row.class).join(", ");
|
|
24
|
-
throw new ConfigError("malformed", "exemptions.disabled", `exemptions.disabled: ${JSON.stringify(name)} is not an exemption class: the classes are ${known}`);
|
|
25
|
-
}
|
|
26
|
-
disabled.add(name);
|
|
27
|
-
}
|
|
28
|
-
return disabled;
|
|
20
|
+
return new Set(config.exemptions.disabled);
|
|
29
21
|
}
|
|
30
22
|
/**
|
|
31
23
|
* Whether a class whose row states `allowed` may retain a member of one visibility. A
|
|
@@ -40,20 +32,8 @@ function retainable(allowed, visibility) {
|
|
|
40
32
|
}
|
|
41
33
|
return true;
|
|
42
34
|
}
|
|
43
|
-
/**
|
|
44
|
-
|
|
45
|
-
* back whole. A class with no row here holds back every declaration but a file.
|
|
46
|
-
*/
|
|
47
|
-
const RETAINABLE = new Map([
|
|
48
|
-
["interface-satisfaction", new Set(["method", "class-member"])],
|
|
49
|
-
["enum-group", new Set(["enum-member"])],
|
|
50
|
-
["injection-container", new Set(["method", "class-member"])],
|
|
51
|
-
["framework-lifecycle", new Set(["method", "class-member"])],
|
|
52
|
-
[
|
|
53
|
-
"serialization-contract",
|
|
54
|
-
new Set(["method", "class-member", "interface-method", "type-member"]),
|
|
55
|
-
],
|
|
56
|
-
]);
|
|
35
|
+
/** The kinds of declaration each class can hold back, which a detector that stops holds back whole. */
|
|
36
|
+
const RETAINABLE = new Map(EXEMPTION_CLASSES.flatMap((row) => "ts" in row.retainsKinds ? [[row.class, new Set(row.retainsKinds.ts)]] : []));
|
|
57
37
|
/** The detail of a record a detector that could not finish holds. */
|
|
58
38
|
const UNANSWERED_DETAIL = "kept live by a question the checker did not answer";
|
|
59
39
|
/** Whether one record is held by a detector that could not finish. */
|
|
@@ -76,7 +56,7 @@ function detected(exemptionClass, detect, input) {
|
|
|
76
56
|
}
|
|
77
57
|
const kinds = RETAINABLE.get(exemptionClass);
|
|
78
58
|
return input.held.symbols
|
|
79
|
-
.filter((symbol) =>
|
|
59
|
+
.filter((symbol) => kinds?.has(symbol.kind) === true)
|
|
80
60
|
.map((symbol) => ({ id: symbol.id, detail: UNANSWERED_DETAIL, site: symbol.position }));
|
|
81
61
|
}
|
|
82
62
|
}
|
|
@@ -106,11 +86,19 @@ export function evidenceRecords(input, detectors, disabled) {
|
|
|
106
86
|
site: evidence.site,
|
|
107
87
|
...(evidence.whileLive === undefined ? {} : { whileLive: evidence.whileLive }),
|
|
108
88
|
...(evidence.implementing === undefined ? {} : { implementing: evidence.implementing }),
|
|
89
|
+
...(evidence.whileWritten === undefined ? {} : { whileWritten: evidence.whileWritten }),
|
|
109
90
|
});
|
|
110
91
|
}
|
|
111
92
|
}
|
|
112
93
|
return found;
|
|
113
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* The records that hold given the declarations a write the mode counts stores into: one
|
|
97
|
+
* that holds only while written holds where its declaration is among them.
|
|
98
|
+
*/
|
|
99
|
+
export function writtenRecords(records, written) {
|
|
100
|
+
return records.flatMap(({ whileWritten, ...record }) => whileWritten === undefined || written.has(record.id) ? [record] : []);
|
|
101
|
+
}
|
|
114
102
|
/** The records that hold under one mode, beside the evidence a production mode found in a test file. */
|
|
115
103
|
export function heldRecords(records, holding) {
|
|
116
104
|
const found = [];
|
|
@@ -125,20 +113,13 @@ export function heldRecords(records, holding) {
|
|
|
125
113
|
}
|
|
126
114
|
return { records: exemptionsOf(found), inTestFiles };
|
|
127
115
|
}
|
|
128
|
-
/** Two strings ordered bytewise. */
|
|
129
|
-
function compare(a, b) {
|
|
130
|
-
if (a === b) {
|
|
131
|
-
return 0;
|
|
132
|
-
}
|
|
133
|
-
return a < b ? -1 : 1;
|
|
134
|
-
}
|
|
135
116
|
/**
|
|
136
117
|
* Records as a run holds them: ordered by site, then class, then declaration, and one
|
|
137
118
|
* per declaration, class, detail and file it holds while live, the one at the first
|
|
138
119
|
* site. A fact found at many sites, or by several projects, is one record.
|
|
139
120
|
*/
|
|
140
121
|
export function exemptionsOf(found) {
|
|
141
|
-
const ordered = [...found].sort((a, b) => byPosition(a.site, b.site) ||
|
|
122
|
+
const ordered = [...found].sort((a, b) => byPosition(a.site, b.site) || bytewise(a.class, b.class) || bytewise(a.id, b.id));
|
|
142
123
|
const seen = new Set();
|
|
143
124
|
return ordered.filter((record) => {
|
|
144
125
|
const fact = [record.id, record.class, record.detail, record.whileLive ?? ""].join("\u0000");
|
|
@@ -189,8 +170,8 @@ export function retainedIn(matrix, input, writeOnly = new Set()) {
|
|
|
189
170
|
return matrix.union.symbols.flatMap((symbol) => held.get(symbol.id) ?? []);
|
|
190
171
|
}
|
|
191
172
|
/**
|
|
192
|
-
* One line per held-back declaration, in site order: its reference, then
|
|
193
|
-
* class, site and detail, all separated by tabs.
|
|
173
|
+
* One line per held-back declaration, in site order: its reference and position, then
|
|
174
|
+
* each record's class, site and detail, all separated by tabs.
|
|
194
175
|
*/
|
|
195
176
|
export function retainedLines(symbols, retained) {
|
|
196
177
|
const held = byDeclaration(retained);
|
|
@@ -204,6 +185,6 @@ export function retainedLines(symbols, retained) {
|
|
|
204
185
|
positionKey(record.site),
|
|
205
186
|
record.detail,
|
|
206
187
|
]);
|
|
207
|
-
return [[symbol.ref, ...fields].join("\t")];
|
|
188
|
+
return [[symbol.ref, positionKey(symbol.position), ...fields].join("\t")];
|
|
208
189
|
});
|
|
209
190
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { FAILURE_CLASSES, FAILURE_PREFIXES } from "./contract/contract.generated.js";
|
|
2
|
+
import { PRODUCT } from "./product.js";
|
|
3
|
+
/** The failure as a class line of the error stream, opened by its group's prefix. */
|
|
4
|
+
export function failureLine(failure) {
|
|
5
|
+
return `${FAILURE_PREFIXES[FAILURE_CLASSES[failure.failureClass]]}${failure.failureClass}: ${failure.detail}`;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* The lines of the error stream that name the failures: the first one's class line, the run's
|
|
9
|
+
* only one, then each further failure as its class and detail with no group prefix.
|
|
10
|
+
*/
|
|
11
|
+
export function failureLines([first, ...further]) {
|
|
12
|
+
return [failureLine(first), ...further.map((one) => `${one.failureClass}: ${one.detail}`)];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A run the analysis cannot answer. It ends the run with the failure code and no finding
|
|
16
|
+
* list, printing the compiler's located errors and then the failures' lines.
|
|
17
|
+
*/
|
|
18
|
+
export class FailureExit extends Error {
|
|
19
|
+
failures;
|
|
20
|
+
/** The compiler's errors behind the failures, in the order it reported them. */
|
|
21
|
+
diagnostics;
|
|
22
|
+
constructor(failures, diagnostics = []) {
|
|
23
|
+
super(failureLines(failures).join("\n"));
|
|
24
|
+
this.name = "FailureExit";
|
|
25
|
+
this.failures = failures;
|
|
26
|
+
this.diagnostics = diagnostics;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** A failure exit of one failure. */
|
|
30
|
+
export function failureExit(failureClass, detail, diagnostics = []) {
|
|
31
|
+
return new FailureExit([{ failureClass, detail }], diagnostics);
|
|
32
|
+
}
|
|
33
|
+
/** A fault of this analyzer itself, `fault` saying what it cannot do. */
|
|
34
|
+
export function productFault(fault) {
|
|
35
|
+
return failureExit("product-fault", `${PRODUCT} ${fault}: reinstall ${PRODUCT}, and report the fault to its maintainers if a reinstalled ${PRODUCT} fails the same way`);
|
|
36
|
+
}
|