@cplieger/deadset-ts 8.0.0-dev.5 → 8.0.0-dev.7
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/bin/mapper-connection.js +2 -1
- package/dist/bin/node-host.js +3 -2
- package/dist/src/analysis.js +28 -46
- package/dist/src/automatic-types.js +10 -29
- package/dist/src/build-scripts.js +17 -29
- package/dist/src/component-layer.js +4 -2
- package/dist/src/components.js +2 -3
- package/dist/src/config.js +3 -2
- package/dist/src/configuration-files.js +27 -168
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/contract/contract.generated.js +1084 -0
- package/dist/src/contract/report.generated.js +796 -0
- package/dist/src/conventions.js +20 -60
- package/dist/src/dependencies.js +25 -43
- package/dist/src/discover.js +13 -39
- package/dist/src/edges.js +7 -15
- package/dist/src/element-access.js +4 -6
- package/dist/src/entry-point-gaps.js +2 -8
- package/dist/src/entry-points.js +3 -10
- package/dist/src/enum-group.js +3 -10
- package/dist/src/erased-stores.js +4 -6
- package/dist/src/exempt-classes.js +3 -63
- package/dist/src/exempt.js +13 -40
- package/dist/src/file-facts.js +2 -25
- package/dist/src/findings/completion.js +11 -20
- package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
- package/dist/src/findings/emitters.js +4 -4
- package/dist/src/findings/interfaces.js +4 -5
- package/dist/src/findings/intra-function.js +8 -7
- package/dist/src/findings/non-code-artifacts.js +5 -6
- package/dist/src/findings/reads-and-writes.js +11 -9
- package/dist/src/findings/self-check.js +10 -12
- package/dist/src/findings/unused-declarations.js +28 -27
- package/dist/src/findings/visibility-narrowing.js +9 -11
- package/dist/src/findings-pass.js +7 -14
- package/dist/src/fs/manifest.js +43 -0
- package/dist/src/fs/tree.js +88 -0
- package/dist/src/git-ignore.js +34 -25
- 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/intra-function-parts.js +18 -26
- package/dist/src/inventory.js +2 -21
- package/dist/src/json-document.js +4 -0
- package/dist/src/keyof-comparisons.js +4 -6
- package/dist/src/kinds.js +4 -360
- package/dist/src/manifest.js +5 -43
- package/dist/src/mdx-entries.js +3 -26
- 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 +3 -2
- package/dist/src/product.js +13 -0
- package/dist/src/references.js +14 -27
- package/dist/src/report-schema.js +272 -0
- package/dist/src/report.js +39 -42
- package/dist/src/reporters/sarif.js +4 -2
- package/dist/src/reporters/template.js +4 -19
- package/dist/src/resolve.js +12 -26
- package/dist/src/roots.js +7 -17
- package/dist/src/run.js +10 -8
- package/dist/src/schema.js +4 -3
- package/dist/src/scope.js +4 -5
- package/dist/src/serialization-contract.js +4 -8
- package/dist/src/session.js +2 -1
- package/dist/src/setup-failure.js +2 -6
- package/dist/src/suppress-file.js +12 -13
- package/dist/src/suppress-inline.js +3 -2
- package/dist/src/suppress.js +29 -48
- package/dist/src/symbol-keys.js +3 -5
- package/dist/src/template-field.js +5 -15
- package/dist/src/test-globals.js +6 -28
- package/dist/src/test-joins.js +6 -6
- package/dist/src/text/compare.js +26 -0
- package/dist/src/text/glob.js +194 -0
- package/dist/src/text/shell.js +159 -0
- package/dist/src/{yaml-document.js → text/yaml.js} +38 -0
- package/dist/src/type-errors.js +14 -38
- package/dist/src/unseen-loads.js +2 -9
- package/dist/src/unwrap.js +23 -0
- package/dist/src/verbs/analyze.js +22 -15
- package/dist/src/verbs/describe.js +7 -7
- package/dist/src/verbs/explain.js +7 -4
- package/dist/src/verbs/print-config.js +2 -1
- package/dist/src/verbs/print-projects.js +4 -2
- 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 +4 -3
- package/dist/src/workflow-steps.js +9 -46
- package/dist/src/workspace-manifests.js +2 -2
- package/dist/src/workspace-programs.js +5 -3
- package/dist/src/workspace-resolution.js +13 -17
- package/dist/src/workspace.js +28 -116
- package/jsr.json +1 -1
- package/package.json +2 -1
- package/src/analysis.ts +29 -54
- package/src/automatic-types.ts +11 -29
- package/src/build-scripts.ts +17 -38
- package/src/component-layer.ts +4 -2
- package/src/components.ts +2 -4
- package/src/config.ts +11 -15
- package/src/configuration-files.ts +27 -170
- package/src/conformance-record.ts +1 -1
- package/src/contract/contract.generated.ts +1148 -0
- package/src/contract/report.generated.ts +1157 -0
- package/src/conventions.ts +26 -74
- package/src/dependencies.ts +32 -50
- package/src/discover.ts +14 -39
- package/src/edges.ts +7 -21
- package/src/element-access.ts +4 -7
- package/src/entry-point-gaps.ts +3 -9
- package/src/entry-points.ts +2 -13
- package/src/enum-group.ts +2 -11
- package/src/erased-stores.ts +3 -7
- package/src/exempt-classes.ts +5 -74
- package/src/exempt.ts +17 -47
- package/src/file-facts.ts +6 -24
- package/src/finding.ts +9 -9
- package/src/findings/completion.ts +22 -23
- package/src/findings/dependencies-and-module-machinery.ts +4 -7
- package/src/findings/emitter.ts +6 -7
- package/src/findings/emitters.ts +6 -6
- package/src/findings/interfaces.ts +4 -6
- package/src/findings/intra-function.ts +8 -8
- package/src/findings/non-code-artifacts.ts +5 -7
- package/src/findings/reads-and-writes.ts +15 -12
- package/src/findings/self-check.ts +11 -17
- package/src/findings/unused-declarations.ts +28 -27
- package/src/findings/visibility-narrowing.ts +9 -12
- package/src/findings-pass.ts +7 -16
- package/src/fs/manifest.ts +52 -0
- package/src/fs/tree.ts +110 -0
- package/src/git-ignore.ts +38 -25
- package/src/html-entries.ts +6 -33
- package/src/in-operands.ts +2 -1
- package/src/index.ts +1 -1
- package/src/intra-function-parts.ts +18 -28
- package/src/inventory.ts +2 -20
- package/src/json-document.ts +5 -0
- package/src/keyof-comparisons.ts +3 -7
- package/src/kinds.ts +6 -406
- package/src/manifest.ts +5 -46
- package/src/mdx-entries.ts +3 -26
- package/src/partial.ts +2 -1
- package/src/paths.ts +27 -15
- package/src/position.ts +2 -1
- package/src/print.ts +3 -2
- package/src/product.ts +18 -0
- package/src/references.ts +14 -29
- package/src/report-schema.ts +364 -0
- package/src/report.ts +66 -229
- package/src/reporters/annotations.ts +2 -2
- package/src/reporters/json.ts +1 -1
- package/src/reporters/reporter.ts +2 -2
- package/src/reporters/reporters.ts +1 -1
- package/src/reporters/sarif.ts +17 -12
- package/src/reporters/template.ts +4 -20
- package/src/reporters/text.ts +3 -3
- package/src/resolve.ts +31 -38
- package/src/roots.ts +7 -20
- package/src/run.ts +10 -14
- package/src/schema.ts +4 -3
- package/src/scope.ts +4 -10
- package/src/serialization-contract.ts +4 -9
- package/src/session.ts +2 -1
- package/src/setup-failure.ts +5 -7
- package/src/suppress-file.ts +11 -15
- package/src/suppress-inline.ts +2 -2
- package/src/suppress.ts +39 -55
- package/src/symbol-keys.ts +2 -5
- package/src/template-field.ts +5 -18
- package/src/test-globals.ts +9 -27
- package/src/test-joins.ts +6 -8
- package/src/text/compare.ts +27 -0
- package/src/text/glob.ts +210 -0
- package/src/text/shell.ts +161 -0
- package/src/{yaml-document.ts → text/yaml.ts} +46 -0
- package/src/type-errors.ts +25 -36
- package/src/unseen-loads.ts +1 -12
- package/src/unwrap.ts +34 -0
- package/src/verbs/analyze.ts +38 -25
- package/src/verbs/describe.ts +13 -7
- package/src/verbs/explain.ts +7 -4
- package/src/verbs/print-config.ts +2 -1
- package/src/verbs/print-projects.ts +4 -2
- package/src/verbs/print-retained.ts +4 -2
- package/src/verbs/print-roots.ts +4 -2
- package/src/verbs/verb.ts +0 -6
- package/src/verbs/version.ts +4 -3
- package/src/workflow-steps.ts +9 -44
- package/src/workspace-manifests.ts +2 -2
- package/src/workspace-programs.ts +5 -3
- package/src/workspace-resolution.ts +21 -17
- package/src/workspace.ts +35 -121
- package/dist/src/glob.js +0 -65
- package/dist/src/pnpm-workspace.js +0 -120
- package/dist/src/version.js +0 -7
- package/src/glob.ts +0 -67
- package/src/pnpm-workspace.ts +0 -135
- package/src/version.ts +0 -7
package/src/enum-group.ts
CHANGED
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
isIdentifier,
|
|
19
19
|
isMethodDeclaration,
|
|
20
20
|
isNoSubstitutionTemplateLiteral,
|
|
21
|
-
isParenthesizedExpression,
|
|
22
21
|
isParenthesizedTypeNode,
|
|
23
22
|
isPropertyAccessExpression,
|
|
24
23
|
isReturnStatement,
|
|
@@ -28,6 +27,7 @@ import {
|
|
|
28
27
|
type Node,
|
|
29
28
|
type SourceFile,
|
|
30
29
|
} from "@typescript/native/ast";
|
|
30
|
+
import { parenthesesRemoved } from "./unwrap.ts";
|
|
31
31
|
import { TypeFlags, type Type } from "@typescript/native/sync";
|
|
32
32
|
import { aliasChains } from "./alias-chain.ts";
|
|
33
33
|
import type { DetectorInput, Evidence } from "./exempt.ts";
|
|
@@ -55,15 +55,6 @@ interface Site {
|
|
|
55
55
|
readonly at: Node;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
/** The expression one node is, through however many parentheses are written around it. */
|
|
59
|
-
function unparenthesized(node: Node): Node {
|
|
60
|
-
let held = node;
|
|
61
|
-
while (isParenthesizedExpression(held)) {
|
|
62
|
-
held = held.expression;
|
|
63
|
-
}
|
|
64
|
-
return held;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
58
|
/**
|
|
68
59
|
* The name node a type node names its declaration by: the identifier of a type
|
|
69
60
|
* reference, the right side of a qualified one, through parentheses. A type that is no
|
|
@@ -86,7 +77,7 @@ function typeName(type: Node | undefined): Node | undefined {
|
|
|
86
77
|
|
|
87
78
|
/** The name node an expression names an object by: an identifier, or a property access's name. */
|
|
88
79
|
function objectName(expression: Node): Node | undefined {
|
|
89
|
-
const held =
|
|
80
|
+
const held = parenthesesRemoved(expression);
|
|
90
81
|
if (isIdentifier(held)) {
|
|
91
82
|
return held;
|
|
92
83
|
}
|
package/src/erased-stores.ts
CHANGED
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
isNoSubstitutionTemplateLiteral,
|
|
17
17
|
isNumericLiteral,
|
|
18
18
|
isObjectLiteralExpression,
|
|
19
|
-
isParenthesizedExpression,
|
|
20
19
|
isPropertyAccessExpression,
|
|
21
20
|
isPropertyAssignment,
|
|
22
21
|
isShorthandPropertyAssignment,
|
|
@@ -31,6 +30,7 @@ import { SymbolFlags, TypeFlags, type Type } from "@typescript/native/sync";
|
|
|
31
30
|
import { must } from "./query.ts";
|
|
32
31
|
import type { ProjectView } from "./session.ts";
|
|
33
32
|
import { checkedTypes } from "./value-reach.ts";
|
|
33
|
+
import { parenthesesRemoved } from "./unwrap.ts";
|
|
34
34
|
|
|
35
35
|
/** The types that keep nothing of the value they are given. */
|
|
36
36
|
const ERASED = TypeFlags.Any | TypeFlags.Unknown;
|
|
@@ -81,10 +81,6 @@ function literalKey(node: Expression): string | undefined {
|
|
|
81
81
|
: undefined;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
function unwrapped(node: Expression): Expression {
|
|
85
|
-
return isParenthesizedExpression(node) ? unwrapped(node.expression) : node;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
84
|
/**
|
|
89
85
|
* Whether a variable declaration's type can differ from what is stored in it: it carries an
|
|
90
86
|
* annotation, or it has neither an annotation nor an initializer, so its type evolves.
|
|
@@ -150,8 +146,8 @@ function propertyAt(source: Expression, name: string): Expression | undefined {
|
|
|
150
146
|
* here, and a default value is stored in its target too.
|
|
151
147
|
*/
|
|
152
148
|
function patternTargets(pattern: Expression, from: Stored, out: Target[]): void {
|
|
153
|
-
const target =
|
|
154
|
-
const source = from.path.length === 0 ?
|
|
149
|
+
const target = parenthesesRemoved(pattern);
|
|
150
|
+
const source = from.path.length === 0 ? parenthesesRemoved(from.value) : from.value;
|
|
155
151
|
const into = (element: Expression, step: Step, literal: Expression | undefined): void => {
|
|
156
152
|
patternTargets(
|
|
157
153
|
element,
|
package/src/exempt-classes.ts
CHANGED
|
@@ -1,12 +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
|
-
|
|
9
|
-
import type { Language } from "./config.ts";
|
|
1
|
+
import { EXEMPTION_CLASSES, type Language } from "./contract/contract.generated.ts";
|
|
10
2
|
|
|
11
3
|
/**
|
|
12
4
|
* Whether a class may retain a member declared with the `private` modifier, and one
|
|
@@ -19,67 +11,6 @@ export interface TypeScriptVisibility {
|
|
|
19
11
|
readonly privateName: boolean;
|
|
20
12
|
}
|
|
21
13
|
|
|
22
|
-
/** One class of the vocabulary. */
|
|
23
|
-
interface ExemptionClassRow {
|
|
24
|
-
readonly class: string;
|
|
25
|
-
readonly languages: readonly Language[];
|
|
26
|
-
/** Present exactly on a class that runs on TypeScript. */
|
|
27
|
-
readonly typescriptVisibility?: TypeScriptVisibility;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/** Every class of the vocabulary, in the order the Contract lists them. */
|
|
31
|
-
export const EXEMPTION_CLASSES = [
|
|
32
|
-
{
|
|
33
|
-
class: "interface-satisfaction",
|
|
34
|
-
languages: ["go", "ts"],
|
|
35
|
-
typescriptVisibility: { private: false, privateName: false },
|
|
36
|
-
},
|
|
37
|
-
{ class: "encoding-reflection", languages: ["go"] },
|
|
38
|
-
{ class: "format-verb-contract", languages: ["go"] },
|
|
39
|
-
{ class: "errors-duck-typing", languages: ["go"] },
|
|
40
|
-
{
|
|
41
|
-
class: "enum-group",
|
|
42
|
-
languages: ["go", "ts"],
|
|
43
|
-
typescriptVisibility: { private: false, privateName: false },
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
class: "generated-file",
|
|
47
|
-
languages: ["go", "ts"],
|
|
48
|
-
typescriptVisibility: { private: true, privateName: false },
|
|
49
|
-
},
|
|
50
|
-
{ class: "linkname-cgo-asm-plugin", languages: ["go"] },
|
|
51
|
-
{
|
|
52
|
-
class: "template-field",
|
|
53
|
-
languages: ["go", "ts"],
|
|
54
|
-
typescriptVisibility: { private: true, privateName: false },
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
class: "reflective-lookup",
|
|
58
|
-
languages: ["go", "ts"],
|
|
59
|
-
typescriptVisibility: { private: true, privateName: false },
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
class: "decorator",
|
|
63
|
-
languages: ["ts"],
|
|
64
|
-
typescriptVisibility: { private: true, privateName: false },
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
class: "injection-container",
|
|
68
|
-
languages: ["ts"],
|
|
69
|
-
typescriptVisibility: { private: true, privateName: false },
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
class: "framework-lifecycle",
|
|
73
|
-
languages: ["ts"],
|
|
74
|
-
typescriptVisibility: { private: true, privateName: false },
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
class: "serialization-contract",
|
|
78
|
-
languages: ["ts"],
|
|
79
|
-
typescriptVisibility: { private: true, privateName: false },
|
|
80
|
-
},
|
|
81
|
-
] as const satisfies readonly ExemptionClassRow[];
|
|
82
|
-
|
|
83
14
|
type Row = (typeof EXEMPTION_CLASSES)[number];
|
|
84
15
|
|
|
85
16
|
/** The name of a class of the vocabulary. */
|
|
@@ -117,7 +48,7 @@ export function typescriptVisibilityOf(exemptionClass: TSExemptionClass): TypeSc
|
|
|
117
48
|
return VISIBILITY.get(exemptionClass) ?? { private: false, privateName: false };
|
|
118
49
|
}
|
|
119
50
|
|
|
120
|
-
/**
|
|
121
|
-
export
|
|
122
|
-
|
|
123
|
-
|
|
51
|
+
/** Every class of the vocabulary, whichever language it runs on, in the vocabulary's order. */
|
|
52
|
+
export const EXEMPTION_CLASS_NAMES: readonly ExemptionClass[] = EXEMPTION_CLASSES.map(
|
|
53
|
+
(row) => row.class,
|
|
54
|
+
);
|
package/src/exempt.ts
CHANGED
|
@@ -7,24 +7,23 @@
|
|
|
7
7
|
* hands it the table of the classes it has.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { EXEMPTION_CLASSES } from "./contract/contract.generated.ts";
|
|
10
11
|
import type { Config, TSSection } from "./config.ts";
|
|
11
|
-
import { ConfigError } from "./config.ts";
|
|
12
12
|
import {
|
|
13
|
-
EXEMPTION_CLASSES,
|
|
14
|
-
isExemptionClass,
|
|
15
13
|
TS_EXEMPTION_CLASSES,
|
|
16
14
|
typescriptVisibilityOf,
|
|
17
15
|
type ExemptionClass,
|
|
18
16
|
type TSExemptionClass,
|
|
19
17
|
type TypeScriptVisibility,
|
|
20
18
|
} from "./exempt-classes.ts";
|
|
21
|
-
import type { Inventory, InventorySymbol,
|
|
19
|
+
import type { Inventory, InventorySymbol, Visibility } from "./inventory.ts";
|
|
22
20
|
import type { Matrix } from "./matrix.ts";
|
|
23
21
|
import { byPosition, positionKey, type Position } from "./position.ts";
|
|
24
22
|
import { Unanswerable } from "./query.ts";
|
|
25
23
|
import type { ProjectView } from "./session.ts";
|
|
26
24
|
import type { Templates } from "./template-field.ts";
|
|
27
25
|
import { sweep, type Exemption, type Implementing, type Mode, type SweepInput } from "./sweep.ts";
|
|
26
|
+
import { bytewise } from "./text/compare.ts";
|
|
28
27
|
|
|
29
28
|
/** One declaration a detector holds back, and where and why. */
|
|
30
29
|
export interface Evidence {
|
|
@@ -73,24 +72,11 @@ interface Holding {
|
|
|
73
72
|
}
|
|
74
73
|
|
|
75
74
|
/**
|
|
76
|
-
* The classes `exemptions.disabled` switches off. A
|
|
77
|
-
*
|
|
78
|
-
* switches nothing off here, so one configuration serves every analyzer.
|
|
75
|
+
* The classes `exemptions.disabled` switches off. A class that runs on another language
|
|
76
|
+
* only switches nothing off here, so one configuration serves every analyzer.
|
|
79
77
|
*/
|
|
80
78
|
export function disabledClasses(config: Config): ReadonlySet<ExemptionClass> {
|
|
81
|
-
|
|
82
|
-
for (const name of config.exemptionsDisabled) {
|
|
83
|
-
if (!isExemptionClass(name)) {
|
|
84
|
-
const known = EXEMPTION_CLASSES.map((row) => row.class).join(", ");
|
|
85
|
-
throw new ConfigError(
|
|
86
|
-
"malformed",
|
|
87
|
-
"exemptions.disabled",
|
|
88
|
-
`exemptions.disabled: ${JSON.stringify(name)} is not an exemption class: the classes are ${known}`,
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
disabled.add(name);
|
|
92
|
-
}
|
|
93
|
-
return disabled;
|
|
79
|
+
return new Set(config.exemptionsDisabled);
|
|
94
80
|
}
|
|
95
81
|
|
|
96
82
|
/**
|
|
@@ -107,20 +93,12 @@ function retainable(allowed: TypeScriptVisibility, visibility: Visibility | unde
|
|
|
107
93
|
return true;
|
|
108
94
|
}
|
|
109
95
|
|
|
110
|
-
/**
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
["enum-group", new Set<SymbolKind>(["enum-member"])],
|
|
117
|
-
["injection-container", new Set<SymbolKind>(["method", "class-member"])],
|
|
118
|
-
["framework-lifecycle", new Set<SymbolKind>(["method", "class-member"])],
|
|
119
|
-
[
|
|
120
|
-
"serialization-contract",
|
|
121
|
-
new Set<SymbolKind>(["method", "class-member", "interface-method", "type-member"]),
|
|
122
|
-
],
|
|
123
|
-
]);
|
|
96
|
+
/** The kinds of declaration each class can hold back, which a detector that stops holds back whole. */
|
|
97
|
+
const RETAINABLE: ReadonlyMap<string, ReadonlySet<string>> = new Map(
|
|
98
|
+
EXEMPTION_CLASSES.flatMap((row) =>
|
|
99
|
+
"ts" in row.retainsKinds ? [[row.class, new Set<string>(row.retainsKinds.ts)] as const] : [],
|
|
100
|
+
),
|
|
101
|
+
);
|
|
124
102
|
|
|
125
103
|
/** The detail of a record a detector that could not finish holds. */
|
|
126
104
|
const UNANSWERED_DETAIL = "kept live by a question the checker did not answer";
|
|
@@ -149,7 +127,7 @@ function detected<Brand>(
|
|
|
149
127
|
}
|
|
150
128
|
const kinds = RETAINABLE.get(exemptionClass);
|
|
151
129
|
return input.held.symbols
|
|
152
|
-
.filter((symbol) =>
|
|
130
|
+
.filter((symbol) => kinds?.has(symbol.kind) === true)
|
|
153
131
|
.map((symbol) => ({ id: symbol.id, detail: UNANSWERED_DETAIL, site: symbol.position }));
|
|
154
132
|
}
|
|
155
133
|
}
|
|
@@ -229,14 +207,6 @@ export function heldRecords(records: readonly Exemption[], holding: Holding): De
|
|
|
229
207
|
return { records: exemptionsOf(found), inTestFiles };
|
|
230
208
|
}
|
|
231
209
|
|
|
232
|
-
/** Two strings ordered bytewise. */
|
|
233
|
-
function compare(a: string, b: string): number {
|
|
234
|
-
if (a === b) {
|
|
235
|
-
return 0;
|
|
236
|
-
}
|
|
237
|
-
return a < b ? -1 : 1;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
210
|
/**
|
|
241
211
|
* Records as a run holds them: ordered by site, then class, then declaration, and one
|
|
242
212
|
* per declaration, class, detail and file it holds while live, the one at the first
|
|
@@ -244,7 +214,7 @@ function compare(a: string, b: string): number {
|
|
|
244
214
|
*/
|
|
245
215
|
export function exemptionsOf(found: readonly Exemption[]): readonly Exemption[] {
|
|
246
216
|
const ordered = [...found].sort(
|
|
247
|
-
(a, b) => byPosition(a.site, b.site) ||
|
|
217
|
+
(a, b) => byPosition(a.site, b.site) || bytewise(a.class, b.class) || bytewise(a.id, b.id),
|
|
248
218
|
);
|
|
249
219
|
const seen = new Set<string>();
|
|
250
220
|
return ordered.filter((record) => {
|
|
@@ -316,8 +286,8 @@ export function retainedIn(
|
|
|
316
286
|
}
|
|
317
287
|
|
|
318
288
|
/**
|
|
319
|
-
* One line per held-back declaration, in site order: its reference, then
|
|
320
|
-
* class, site and detail, all separated by tabs.
|
|
289
|
+
* One line per held-back declaration, in site order: its reference and position, then
|
|
290
|
+
* each record's class, site and detail, all separated by tabs.
|
|
321
291
|
*/
|
|
322
292
|
export function retainedLines(
|
|
323
293
|
symbols: readonly InventorySymbol[],
|
|
@@ -334,6 +304,6 @@ export function retainedLines(
|
|
|
334
304
|
positionKey(record.site),
|
|
335
305
|
record.detail,
|
|
336
306
|
]);
|
|
337
|
-
return [[symbol.ref, ...fields].join("\t")];
|
|
307
|
+
return [[symbol.ref, positionKey(symbol.position), ...fields].join("\t")];
|
|
338
308
|
});
|
|
339
309
|
}
|
package/src/file-facts.ts
CHANGED
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { SourceFile } from "@typescript/native/ast";
|
|
8
|
-
import { IGNORED_DIRECTORIES } from "./discover.ts";
|
|
9
8
|
import type { Host } from "./host.ts";
|
|
10
9
|
import { packageScope } from "./inventory.ts";
|
|
11
10
|
import { dirnamePath, joinPath, relativePath, resolvePath } from "./paths.ts";
|
|
12
11
|
import { renderRef } from "./ref.ts";
|
|
13
12
|
import type { ProjectView } from "./session.ts";
|
|
14
13
|
import type { UnseenLoad } from "./unseen-loads.ts";
|
|
14
|
+
import { filesBelow } from "./fs/tree.ts";
|
|
15
15
|
|
|
16
16
|
/** A file name the compiler reads as a source file. */
|
|
17
17
|
const SOURCE_FILE = /\.(?:ts|tsx|mts|cts)$/u;
|
|
@@ -74,29 +74,11 @@ function lastLine(text: string): number {
|
|
|
74
74
|
* that cannot be read is skipped, as discovery skips it.
|
|
75
75
|
*/
|
|
76
76
|
export function sourcePaths(host: Host, targetRoot: string): readonly string[] {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
entries = host.readDirectory(joinPath(targetRoot, dir));
|
|
83
|
-
} catch {
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
for (const entry of entries) {
|
|
87
|
-
const path = dir === "" ? entry.name : `${dir}/${entry.name}`;
|
|
88
|
-
if (entry.directory) {
|
|
89
|
-
if (!IGNORED_DIRECTORIES.has(entry.name)) {
|
|
90
|
-
pending.push(path);
|
|
91
|
-
}
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
if (SOURCE_FILE.test(entry.name) && !DECLARATION_FILE.test(entry.name)) {
|
|
95
|
-
found.push(path);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return found.sort((a, b) => (a < b ? -1 : 1));
|
|
77
|
+
return filesBelow(
|
|
78
|
+
host,
|
|
79
|
+
targetRoot,
|
|
80
|
+
(name) => SOURCE_FILE.test(name) && !DECLARATION_FILE.test(name),
|
|
81
|
+
);
|
|
100
82
|
}
|
|
101
83
|
|
|
102
84
|
/**
|
package/src/finding.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { Confidence, Severity } from "./
|
|
2
|
-
import type { Fixability } from "./kinds.ts";
|
|
1
|
+
import type { Fixability, Confidence, Severity } from "./contract/contract.generated.ts";
|
|
3
2
|
import type { DependencySection } from "./ref.ts";
|
|
4
3
|
import type { Mechanism } from "./suppress.ts";
|
|
5
4
|
import type { Relation } from "./sweep.ts";
|
|
5
|
+
import type { FindingSymbol } from "./contract/report.generated.ts";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* The finding: what an emitter decides about one subject, and every member the
|
|
@@ -29,7 +29,7 @@ interface FindingSubject {
|
|
|
29
29
|
/** The stable symbol reference, or for a configured root the root as written. */
|
|
30
30
|
readonly ref: string;
|
|
31
31
|
/** The subject kind, from the closed vocabulary of the finding schema. */
|
|
32
|
-
readonly kind:
|
|
32
|
+
readonly kind: FindingSymbol["kind"];
|
|
33
33
|
/** The display name a text line renders. */
|
|
34
34
|
readonly name: string;
|
|
35
35
|
/** The number of source lines the subject spans. */
|
|
@@ -55,19 +55,19 @@ export interface PositionedSymbol {
|
|
|
55
55
|
*/
|
|
56
56
|
export interface FindingDetails {
|
|
57
57
|
/**
|
|
58
|
-
* On `
|
|
58
|
+
* On `DS1501` and `DS1503`: the widest scope that holds every reference to the
|
|
59
59
|
* subject, which is the visibility its references support.
|
|
60
60
|
*/
|
|
61
61
|
readonly narrowerVisibility?: "file" | "package";
|
|
62
|
-
/** On `DS1201` and `
|
|
62
|
+
/** On `DS1201` and `DS1202`: the classes implementing the interface, in site order. */
|
|
63
63
|
readonly implementations?: readonly PositionedSymbol[];
|
|
64
|
-
/** On `DS1301` and `
|
|
64
|
+
/** On `DS1301` and `DS1404`: every position the subject is written at, in site order. */
|
|
65
65
|
readonly writePositions?: readonly FindingPosition[];
|
|
66
|
-
/** On `
|
|
66
|
+
/** On `DS1701`: the manifest section that declares the dependency. */
|
|
67
67
|
readonly dependencyClass?: DependencySection;
|
|
68
|
-
/** On `
|
|
68
|
+
/** On `DS9001` and `DS9002`: the document that holds the refused suppression. */
|
|
69
69
|
readonly mechanism?: Mechanism;
|
|
70
|
-
/** On `
|
|
70
|
+
/** On `DS9001` and `DS9002`: the refused suppression as written, a member absent where it lacks one. */
|
|
71
71
|
readonly entry?: SuppressionEntry;
|
|
72
72
|
/** On the intra-function kinds: the external rules that report the same kind, as the vocabulary lists them. */
|
|
73
73
|
readonly overlap?: readonly string[];
|
|
@@ -4,39 +4,38 @@
|
|
|
4
4
|
* together with every finding that falls with a root they withhold.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import {
|
|
8
|
+
CODE,
|
|
9
|
+
FAMILY_KEY_LENGTH,
|
|
10
|
+
type Confidence,
|
|
11
|
+
type Severity,
|
|
12
|
+
KIND_ROWS,
|
|
13
|
+
type KindRow,
|
|
14
|
+
} from "../contract/contract.generated.ts";
|
|
7
15
|
import type { RunSweep } from "../analysis.ts";
|
|
8
16
|
import { componentId, type Component } from "../components.ts";
|
|
9
17
|
import { deadComponents } from "../matrix.ts";
|
|
10
|
-
import type { Cascade
|
|
18
|
+
import type { Cascade } from "../config.ts";
|
|
11
19
|
import type { CompletedFinding, Finding, FindingComponent, PositionedSymbol } from "../finding.ts";
|
|
12
20
|
import { OUTSIDE, type Graph } from "../graph.ts";
|
|
13
21
|
import type { SymbolKind } from "../inventory.ts";
|
|
14
|
-
import { KINDS
|
|
22
|
+
import { KINDS } from "../kinds.ts";
|
|
15
23
|
import { byPosition, positionKey } from "../position.ts";
|
|
16
24
|
import { declarationKey } from "../ref.ts";
|
|
17
|
-
import { FAMILY_KEY_LENGTH } from "../resolve.ts";
|
|
18
25
|
import { isPartSubject, isRowSubject, type Dials } from "../suppress.ts";
|
|
19
26
|
import type { Candidate } from "../sweep.ts";
|
|
20
27
|
import type { Boundary } from "./boundary.ts";
|
|
21
28
|
import type { EmitterInput } from "./emitter.ts";
|
|
22
29
|
|
|
23
|
-
/** The unused-file code, whose deletion removes every use its file writes. */
|
|
24
|
-
const FILE_NEVER_IMPORTED = "DS1502";
|
|
25
|
-
|
|
26
30
|
/**
|
|
27
|
-
* The codes whose subject the run that counts test references holds live,
|
|
28
|
-
*
|
|
29
|
-
* none of a dead declaration's sweep facts.
|
|
31
|
+
* The codes whose subject the run that counts test references holds live, so a test
|
|
32
|
+
* file's candidate reported under one carries none of a dead declaration's sweep facts.
|
|
30
33
|
*/
|
|
31
|
-
const LIVE_SUBJECT: ReadonlySet<string> = new Set(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"DS1104",
|
|
35
|
-
"DS1204",
|
|
36
|
-
"DS1301",
|
|
37
|
-
]);
|
|
34
|
+
const LIVE_SUBJECT: ReadonlySet<string> = new Set(
|
|
35
|
+
KIND_ROWS.filter((row) => row.holdsLive).map((row) => row.code),
|
|
36
|
+
);
|
|
38
37
|
|
|
39
|
-
/** The fixability grammar/analysis.md
|
|
38
|
+
/** The fixability grammar/analysis/fixability.md gives a finding about a held declaration. */
|
|
40
39
|
const MANUAL = "manual";
|
|
41
40
|
|
|
42
41
|
/** The member kinds an object property can declare, which an `in` expression tests by name. */
|
|
@@ -186,7 +185,7 @@ function testCeilings(
|
|
|
186
185
|
|
|
187
186
|
/**
|
|
188
187
|
* The subjects dead only because production code only tests use is: each declaration the
|
|
189
|
-
* sweep marks test-used, each `
|
|
188
|
+
* sweep marks test-used, each `DS1602` file of such code, and each test of dead code every
|
|
190
189
|
* target of which is one. Nothing they hold references a dead declaration outside them, so
|
|
191
190
|
* the rest of a component they share is reported without them.
|
|
192
191
|
*/
|
|
@@ -286,7 +285,7 @@ function membersListed(
|
|
|
286
285
|
}
|
|
287
286
|
|
|
288
287
|
/**
|
|
289
|
-
* The run's dead components with each file a `
|
|
288
|
+
* The run's dead components with each file a `DS1602` finding reports dead among them, so
|
|
290
289
|
* the file's declarations fall with it, and with the components each subject that is no
|
|
291
290
|
* declaration falls with joined into one.
|
|
292
291
|
*/
|
|
@@ -300,7 +299,7 @@ function runComponents(
|
|
|
300
299
|
} {
|
|
301
300
|
const union = swept.matrix.union;
|
|
302
301
|
const reported = new Set(
|
|
303
|
-
findings.filter((one) => one.code ===
|
|
302
|
+
findings.filter((one) => one.code === CODE.fileNeverImported).map((one) => one.position.path),
|
|
304
303
|
);
|
|
305
304
|
const files = new Map(
|
|
306
305
|
union.symbols.flatMap((symbol, at) =>
|
|
@@ -419,7 +418,7 @@ export function completed(
|
|
|
419
418
|
const fallsWith = finding.fallsWith?.[0];
|
|
420
419
|
const joined = fallsWith === undefined ? undefined : componentOf(fallsWith);
|
|
421
420
|
const file =
|
|
422
|
-
finding.code ===
|
|
421
|
+
finding.code === CODE.fileNeverImported ? files.get(finding.position.path) : undefined;
|
|
423
422
|
const found =
|
|
424
423
|
joined !== undefined
|
|
425
424
|
? { ...joined, root: false }
|
|
@@ -471,7 +470,7 @@ export function completed(
|
|
|
471
470
|
const used =
|
|
472
471
|
judged !== undefined
|
|
473
472
|
? candidate !== undefined && testUsed.declarations.has(judged.id)
|
|
474
|
-
: finding.code ===
|
|
473
|
+
: finding.code === CODE.fileNeverImported && testUsed.files.has(finding.position.path);
|
|
475
474
|
const reachabilityClass = used && candidate?.testOfDeadCode !== true ? "possible" : ownClass;
|
|
476
475
|
const configurations =
|
|
477
476
|
candidate?.configurations ??
|
|
@@ -499,7 +498,7 @@ export function completed(
|
|
|
499
498
|
? undefined
|
|
500
499
|
: candidate !== undefined
|
|
501
500
|
? input.dependencies.lastUses.get(candidate.id)
|
|
502
|
-
: finding.code ===
|
|
501
|
+
: finding.code === CODE.fileNeverImported
|
|
503
502
|
? input.dependencies.fileLastUses.get(finding.position.path)
|
|
504
503
|
: undefined;
|
|
505
504
|
return {
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
+
import { CODE } from "../contract/contract.generated.ts";
|
|
1
2
|
import { declarationKey, type Dependencies } from "../dependencies.ts";
|
|
2
3
|
import type { Finding } from "../finding.ts";
|
|
3
4
|
import { renderRef, type DependencySection } from "../ref.ts";
|
|
4
5
|
import type { Emitter, EmitterInput } from "./emitter.ts";
|
|
5
6
|
import { nonCodeArtifacts } from "./non-code-artifacts.ts";
|
|
6
7
|
|
|
7
|
-
const UNUSED_DEPENDENCY = "DS1601";
|
|
8
|
-
|
|
9
|
-
const FILE_NEVER_IMPORTED = "DS1502";
|
|
10
|
-
|
|
11
8
|
const MESSAGES: Readonly<Record<DependencySection, string>> = {
|
|
12
9
|
dependency: "no import in the target needs the declared dependency",
|
|
13
10
|
"dev-dependency": "no import in the target needs the declared development dependency",
|
|
@@ -46,7 +43,7 @@ function deadCode(input: EmitterInput): DeadCode {
|
|
|
46
43
|
);
|
|
47
44
|
for (const finding of nonCodeArtifacts(input)) {
|
|
48
45
|
const id = files.get(finding.position.path);
|
|
49
|
-
if (finding.code ===
|
|
46
|
+
if (finding.code === CODE.fileNeverImported && id !== undefined) {
|
|
50
47
|
dead.add(id);
|
|
51
48
|
if (finding.unreachableOnly === true) {
|
|
52
49
|
reachedOnly.add(id);
|
|
@@ -81,7 +78,7 @@ function unusedDependencies(input: EmitterInput, dependencies: Dependencies): Fi
|
|
|
81
78
|
const atPossible = unbuilt || users?.some((id) => published.has(id)) === true;
|
|
82
79
|
return [
|
|
83
80
|
{
|
|
84
|
-
code:
|
|
81
|
+
code: CODE.unusedDependency,
|
|
85
82
|
position: dependency.position,
|
|
86
83
|
symbol: {
|
|
87
84
|
ref: renderRef(dependency.manifest, {
|
|
@@ -105,6 +102,6 @@ function unusedDependencies(input: EmitterInput, dependencies: Dependencies): Fi
|
|
|
105
102
|
});
|
|
106
103
|
}
|
|
107
104
|
|
|
108
|
-
/** The findings of the dependencies
|
|
105
|
+
/** The findings of the dependencies family, `DS1700` to `DS1799`. */
|
|
109
106
|
export const dependenciesAndModuleMachinery: Emitter = (input) =>
|
|
110
107
|
unusedDependencies(input, input.dependencies);
|
package/src/findings/emitter.ts
CHANGED
|
@@ -11,7 +11,7 @@ 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
17
|
readonly config: Config;
|
|
@@ -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
|
*/
|