@cplieger/deadset-ts 8.0.0-dev.4 → 8.0.0-dev.6

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.
Files changed (90) hide show
  1. package/dist/src/analysis.js +3 -3
  2. package/dist/src/config.js +1 -1
  3. package/dist/src/conformance-record.js +1 -1
  4. package/dist/src/contract/contract.generated.js +1071 -0
  5. package/dist/src/contract/report.generated.js +796 -0
  6. package/dist/src/convention-rows.js +1 -1
  7. package/dist/src/exempt-classes.js +1 -59
  8. package/dist/src/exempt.js +8 -19
  9. package/dist/src/findings/completion.js +11 -20
  10. package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
  11. package/dist/src/findings/emitters.js +4 -4
  12. package/dist/src/findings/interfaces.js +4 -5
  13. package/dist/src/findings/intra-function.js +8 -7
  14. package/dist/src/findings/non-code-artifacts.js +5 -6
  15. package/dist/src/findings/reads-and-writes.js +9 -8
  16. package/dist/src/findings/self-check.js +7 -10
  17. package/dist/src/findings/unused-declarations.js +28 -27
  18. package/dist/src/findings/visibility-narrowing.js +9 -11
  19. package/dist/src/findings-pass.js +1 -3
  20. package/dist/src/index.js +1 -1
  21. package/dist/src/intra-function-parts.js +8 -13
  22. package/dist/src/kinds.js +4 -360
  23. package/dist/src/report-schema.js +273 -0
  24. package/dist/src/report.js +17 -11
  25. package/dist/src/resolve.js +1 -6
  26. package/dist/src/run.js +2 -1
  27. package/dist/src/setup-failure.js +2 -6
  28. package/dist/src/suppress-file.js +4 -3
  29. package/dist/src/suppress-inline.js +3 -2
  30. package/dist/src/suppress.js +29 -48
  31. package/dist/src/verbs/analyze.js +7 -2
  32. package/dist/src/verbs/describe.js +4 -4
  33. package/dist/src/verbs/explain.js +2 -1
  34. package/dist/src/verbs/print-config.js +2 -1
  35. package/dist/src/verbs/print-projects.js +2 -1
  36. package/dist/src/verbs/print-retained.js +2 -1
  37. package/dist/src/verbs/print-roots.js +2 -1
  38. package/dist/src/verbs/verb.js +1 -5
  39. package/dist/src/verbs/version.js +2 -2
  40. package/jsr.json +1 -1
  41. package/package.json +3 -2
  42. package/src/analysis.ts +3 -3
  43. package/src/config.ts +7 -13
  44. package/src/conformance-record.ts +1 -1
  45. package/src/contract/contract.generated.ts +1123 -0
  46. package/src/contract/report.generated.ts +1157 -0
  47. package/src/convention-rows.ts +1 -1
  48. package/src/exempt-classes.ts +1 -70
  49. package/src/exempt.ts +12 -20
  50. package/src/finding.ts +9 -9
  51. package/src/findings/completion.ts +22 -23
  52. package/src/findings/dependencies-and-module-machinery.ts +4 -7
  53. package/src/findings/emitter.ts +6 -7
  54. package/src/findings/emitters.ts +6 -6
  55. package/src/findings/interfaces.ts +4 -6
  56. package/src/findings/intra-function.ts +8 -8
  57. package/src/findings/non-code-artifacts.ts +5 -7
  58. package/src/findings/reads-and-writes.ts +13 -11
  59. package/src/findings/self-check.ts +8 -15
  60. package/src/findings/unused-declarations.ts +28 -27
  61. package/src/findings/visibility-narrowing.ts +9 -12
  62. package/src/findings-pass.ts +1 -4
  63. package/src/index.ts +1 -1
  64. package/src/intra-function-parts.ts +8 -14
  65. package/src/kinds.ts +6 -406
  66. package/src/report-schema.ts +366 -0
  67. package/src/report.ts +42 -195
  68. package/src/reporters/annotations.ts +2 -2
  69. package/src/reporters/json.ts +1 -1
  70. package/src/reporters/reporter.ts +2 -2
  71. package/src/reporters/reporters.ts +1 -1
  72. package/src/reporters/sarif.ts +13 -8
  73. package/src/reporters/text.ts +3 -3
  74. package/src/resolve.ts +11 -11
  75. package/src/run.ts +2 -1
  76. package/src/setup-failure.ts +5 -7
  77. package/src/suppress-file.ts +3 -4
  78. package/src/suppress-inline.ts +2 -2
  79. package/src/suppress.ts +39 -55
  80. package/src/verbs/analyze.ts +16 -4
  81. package/src/verbs/describe.ts +10 -4
  82. package/src/verbs/explain.ts +2 -1
  83. package/src/verbs/print-config.ts +2 -1
  84. package/src/verbs/print-projects.ts +2 -1
  85. package/src/verbs/print-retained.ts +2 -1
  86. package/src/verbs/print-roots.ts +2 -1
  87. package/src/verbs/verb.ts +0 -6
  88. package/src/verbs/version.ts +2 -2
  89. package/dist/src/version.js +0 -7
  90. package/src/version.ts +0 -7
@@ -352,7 +352,7 @@ export const CONVENTION_ROWS: readonly ConventionRow[] = [
352
352
  {
353
353
  name: "expo-router",
354
354
  package: "expo-router",
355
- range: ">=3.0.0 <58.0.0",
355
+ range: ">=3.0.0 <59.0.0",
356
356
  entries: [`{,src/}app/**/*.${SCRIPT}`],
357
357
  moves: [],
358
358
  },
@@ -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. */
package/src/exempt.ts CHANGED
@@ -7,10 +7,10 @@
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
12
  import { ConfigError } from "./config.ts";
12
13
  import {
13
- EXEMPTION_CLASSES,
14
14
  isExemptionClass,
15
15
  TS_EXEMPTION_CLASSES,
16
16
  typescriptVisibilityOf,
@@ -18,7 +18,7 @@ import {
18
18
  type TSExemptionClass,
19
19
  type TypeScriptVisibility,
20
20
  } from "./exempt-classes.ts";
21
- import type { Inventory, InventorySymbol, SymbolKind, Visibility } from "./inventory.ts";
21
+ import type { Inventory, InventorySymbol, Visibility } from "./inventory.ts";
22
22
  import type { Matrix } from "./matrix.ts";
23
23
  import { byPosition, positionKey, type Position } from "./position.ts";
24
24
  import { Unanswerable } from "./query.ts";
@@ -107,20 +107,12 @@ function retainable(allowed: TypeScriptVisibility, visibility: Visibility | unde
107
107
  return true;
108
108
  }
109
109
 
110
- /**
111
- * The kinds of declaration each class can hold back, which a detector that stops holds
112
- * back whole. A class with no row here holds back every declaration but a file.
113
- */
114
- const RETAINABLE: ReadonlyMap<TSExemptionClass, ReadonlySet<SymbolKind>> = new Map([
115
- ["interface-satisfaction", new Set<SymbolKind>(["method", "class-member"])],
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
- ]);
110
+ /** The kinds of declaration each class can hold back, which a detector that stops holds back whole. */
111
+ const RETAINABLE: ReadonlyMap<string, ReadonlySet<string>> = new Map(
112
+ EXEMPTION_CLASSES.flatMap((row) =>
113
+ "ts" in row.retainsKinds ? [[row.class, new Set<string>(row.retainsKinds.ts)] as const] : [],
114
+ ),
115
+ );
124
116
 
125
117
  /** The detail of a record a detector that could not finish holds. */
126
118
  const UNANSWERED_DETAIL = "kept live by a question the checker did not answer";
@@ -149,7 +141,7 @@ function detected<Brand>(
149
141
  }
150
142
  const kinds = RETAINABLE.get(exemptionClass);
151
143
  return input.held.symbols
152
- .filter((symbol) => (kinds === undefined ? symbol.kind !== "file" : kinds.has(symbol.kind)))
144
+ .filter((symbol) => kinds?.has(symbol.kind) === true)
153
145
  .map((symbol) => ({ id: symbol.id, detail: UNANSWERED_DETAIL, site: symbol.position }));
154
146
  }
155
147
  }
@@ -316,8 +308,8 @@ export function retainedIn(
316
308
  }
317
309
 
318
310
  /**
319
- * One line per held-back declaration, in site order: its reference, then each record's
320
- * class, site and detail, all separated by tabs.
311
+ * One line per held-back declaration, in site order: its reference and position, then
312
+ * each record's class, site and detail, all separated by tabs.
321
313
  */
322
314
  export function retainedLines(
323
315
  symbols: readonly InventorySymbol[],
@@ -334,6 +326,6 @@ export function retainedLines(
334
326
  positionKey(record.site),
335
327
  record.detail,
336
328
  ]);
337
- return [[symbol.ref, ...fields].join("\t")];
329
+ return [[symbol.ref, positionKey(symbol.position), ...fields].join("\t")];
338
330
  });
339
331
  }
package/src/finding.ts CHANGED
@@ -1,8 +1,8 @@
1
- import type { Confidence, Severity } from "./config.ts";
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: string;
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 `DS1101` and `DS1104`: the widest scope that holds every reference to the
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 `DS1203`: the classes implementing the interface, in site order. */
62
+ /** On `DS1201` and `DS1202`: the classes implementing the interface, in site order. */
63
63
  readonly implementations?: readonly PositionedSymbol[];
64
- /** On `DS1301` and `DS1807`: every position the subject is written at, in site order. */
64
+ /** On `DS1301` and `DS1404`: every position the subject is written at, in site order. */
65
65
  readonly writePositions?: readonly FindingPosition[];
66
- /** On `DS1601`: the manifest section that declares the dependency. */
66
+ /** On `DS1701`: the manifest section that declares the dependency. */
67
67
  readonly dependencyClass?: DependencySection;
68
- /** On `DS1701` and `DS1702`: the document that holds the refused suppression. */
68
+ /** On `DS9001` and `DS9002`: the document that holds the refused suppression. */
69
69
  readonly mechanism?: Mechanism;
70
- /** On `DS1701` and `DS1702`: the refused suppression as written, a member absent where it lacks one. */
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, Confidence, Severity } from "../config.ts";
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, type KindRow } from "../kinds.ts";
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, as
28
- * finding.schema.json lists them, so a test file's candidate reported under one carries
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
- "DS1101",
33
- "DS1102",
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 "Fixability" gives a finding about a held declaration. */
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 `DS1502` file of such code, and each test of dead code every
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 `DS1502` finding reports dead among them, so
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 === FILE_NEVER_IMPORTED).map((one) => one.position.path),
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 === FILE_NEVER_IMPORTED ? files.get(finding.position.path) : undefined;
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 === FILE_NEVER_IMPORTED && testUsed.files.has(finding.position.path);
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 === FILE_NEVER_IMPORTED
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 === FILE_NEVER_IMPORTED && id !== undefined) {
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: UNUSED_DEPENDENCY,
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-and-module-machinery family, `DS1600` to `DS1699`. */
105
+ /** The findings of the dependencies family, `DS1700` to `DS1799`. */
109
106
  export const dependenciesAndModuleMachinery: Emitter = (input) =>
110
107
  unusedDependencies(input, input.dependencies);
@@ -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 kind family reads beside the sweep.
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 self-check family reports. */
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 intra-function family reports. */
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 kind family's rule over one run: every finding of the codes in its family's range
40
- * of the Contract's code space, whatever severity and minimum confidence the
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
- /** The emitter of each kind family, by the family's name in the Contract, in code order. */
44
+ /** Every emitter this analyzer runs, by the name its tests select it by. */
46
45
  export type Emitters = ReadonlyMap<string, Emitter>;
@@ -1,4 +1,4 @@
1
- import type { Confidence } from "../config.ts";
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 kind family this analyzer reports, each by its emitter, in code order. */
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 family's in table order. */
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 family's findings over one sweep, and those of any further emitter, completed by one step. */
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
- * family'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 family
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
  */
@@ -1,12 +1,10 @@
1
+ import { CODE } from "../contract/contract.generated.ts";
1
2
  import type { Finding, FindingPosition, PositionedSymbol } from "../finding.ts";
2
3
  import type { InventorySymbol } from "../inventory.ts";
3
4
  import { byPosition } from "../position.ts";
4
5
  import type { MatrixCandidate } from "../matrix.ts";
5
6
  import type { Emitter, EmitterInput } from "./emitter.ts";
6
7
 
7
- const UNUSED_INTERFACE = "DS1201";
8
- const UNCALLED_INTERFACE_METHOD = "DS1203";
9
-
10
8
  /** What each finding says, by what the sweep found about the subject's references. */
11
9
  const UNUSED_MESSAGE = "nothing in the target names the interface as a type";
12
10
  const UNUSED_DEAD_MESSAGE =
@@ -69,7 +67,7 @@ function findingOf(
69
67
 
70
68
  /**
71
69
  * The findings of the interfaces family, `DS1200` to `DS1299`: `DS1201`, a dead
72
- * interface, whose members fall with it unreported; and `DS1203`, a method of a live
70
+ * interface, whose members fall with it unreported; and `DS1202`, a method of a live
73
71
  * interface no reference names, unless it is a marker, a method whose every
74
72
  * implementation has an empty body. A test file's declaration is neither kind's subject,
75
73
  * because a production sweep counts none of the references it can have, save an interface
@@ -86,7 +84,7 @@ export const interfaces: Emitter = (input) => {
86
84
  if (symbol.kind === "interface") {
87
85
  found.push(
88
86
  findingOf(
89
- UNUSED_INTERFACE,
87
+ CODE.unusedInterface,
90
88
  symbol,
91
89
  unusedMessage(candidate),
92
90
  run.implementationsOf(candidate.id),
@@ -104,7 +102,7 @@ export const interfaces: Emitter = (input) => {
104
102
  }
105
103
  found.push(
106
104
  findingOf(
107
- UNCALLED_INTERFACE_METHOD,
105
+ CODE.uncalledInterfaceMethod,
108
106
  symbol,
109
107
  UNCALLED_MESSAGE,
110
108
  run.implementationsOf(symbol.parent),
@@ -1,8 +1,8 @@
1
- import type { Confidence } from "../config.ts";
1
+ import { CODE, type Confidence } from "../contract/contract.generated.ts";
2
2
  import type { Finding } from "../finding.ts";
3
3
  import { KINDS } from "../kinds.ts";
4
4
  import { parameterRef } from "../ref.ts";
5
- import { UNUSED_PARAMETER, UNUSED_RESULT, type PartFact } from "../intra-function-parts.ts";
5
+ import { type PartFact } from "../intra-function-parts.ts";
6
6
  import { reachedWithTheTests } from "../sweep.ts";
7
7
  import type { Emitter, EmitterInput } from "./emitter.ts";
8
8
 
@@ -85,9 +85,9 @@ function reported(
85
85
  }
86
86
  const { free, freeButValued, discardedEverywhere } = input.intraFunction;
87
87
  switch (part.code) {
88
- case UNUSED_PARAMETER:
88
+ case CODE.unusedParameter:
89
89
  return parameterVerdict(part, free, freeButValued) !== undefined;
90
- case UNUSED_RESULT:
90
+ case CODE.unusedResult:
91
91
  return (
92
92
  free.has(part.declaration) &&
93
93
  discardedEverywhere.has(part.declaration) &&
@@ -99,7 +99,7 @@ function reported(
99
99
  }
100
100
 
101
101
  /**
102
- * The findings of the intra-function family, `DS1800` to `DS1899`, each about a part of
102
+ * The findings of the inside-functions family, `DS1401` to `DS1406`, each about a part of
103
103
  * the declaration its reference names, at the part's own position, whatever the sweep
104
104
  * decided about a production declaration. A part of test code is reported only where the
105
105
  * run that counts test references reaches its declaration and that is no test of dead
@@ -146,12 +146,12 @@ export const intraFunction: Emitter = (input) => {
146
146
  const found: Finding[] = [];
147
147
  for (const part of input.intraFunction.parts) {
148
148
  const declaration = byId.get(part.declaration);
149
- const overlap = KINDS.get(part.code)?.overlap ?? [];
149
+ const overlap = KINDS.get(part.code)?.overlap?.ts ?? [];
150
150
  if (declaration === undefined || !judged(declaration.id) || !reported(input, part, unknown)) {
151
151
  continue;
152
152
  }
153
153
  const verdict =
154
- part.code === UNUSED_PARAMETER
154
+ part.code === CODE.unusedParameter
155
155
  ? parameterVerdict(part, input.intraFunction.free, input.intraFunction.freeButValued)
156
156
  : undefined;
157
157
  const message =
@@ -162,7 +162,7 @@ export const intraFunction: Emitter = (input) => {
162
162
  code: part.code,
163
163
  position: part.position,
164
164
  symbol: {
165
- ...(part.code === UNUSED_PARAMETER
165
+ ...(part.code === CODE.unusedParameter
166
166
  ? { ref: parameterRef(declaration.ref, part.name), holder: declaration.ref }
167
167
  : { ref: declaration.ref }),
168
168
  kind: part.kind,
@@ -1,10 +1,8 @@
1
+ import { CODE } from "../contract/contract.generated.ts";
1
2
  import type { Config } from "../config.ts";
2
3
  import type { Finding } from "../finding.ts";
3
4
  import type { Emitter, EmitterInput } from "./emitter.ts";
4
5
 
5
- const FILE_NEVER_BUILT = "DS1501";
6
- const FILE_NEVER_IMPORTED = "DS1502";
7
-
8
6
  /** The subject kind of a finding about a source file, from the finding schema's vocabulary. */
9
7
  const FILE_SUBJECT = "file";
10
8
 
@@ -57,7 +55,7 @@ function fileNeverBuilt({ config, swept, files }: EmitterInput): readonly Findin
57
55
  const message = `no project of the build matrix includes this file (${swept.matrix.configurations.join(", ")})`;
58
56
  return files.tree
59
57
  .filter((file) => !built.has(file.path))
60
- .map((file) => fileFinding(FILE_NEVER_BUILT, file, message));
58
+ .map((file) => fileFinding(CODE.fileNeverBuilt, file, message));
61
59
  }
62
60
 
63
61
  /** The files of the run, each with whether a reference written in another file reaches it. */
@@ -152,21 +150,21 @@ function fileNeverImported(input: EmitterInput): readonly Finding[] {
152
150
  return referenced.has(symbol.position.path)
153
151
  ? {
154
152
  ...fileFinding(
155
- FILE_NEVER_IMPORTED,
153
+ CODE.fileNeverImported,
156
154
  file,
157
155
  "only code that is itself dead imports this file, and no root names it or a declaration in it",
158
156
  ),
159
157
  unreachableOnly: true as const,
160
158
  }
161
159
  : fileFinding(
162
- FILE_NEVER_IMPORTED,
160
+ CODE.fileNeverImported,
163
161
  file,
164
162
  "no import reaches this file and no root names it or a declaration in it",
165
163
  );
166
164
  });
167
165
  }
168
166
 
169
- /** The findings of the non-code-artifacts family, `DS1500` to `DS1599`. */
167
+ /** The findings of the files family, `DS1600` to `DS1699`. */
170
168
  export const nonCodeArtifacts: Emitter = (input) => [
171
169
  ...fileNeverBuilt(input),
172
170
  ...fileNeverImported(input),
@@ -1,3 +1,4 @@
1
+ import { CODE } from "../contract/contract.generated.ts";
1
2
  import type { Finding, FindingDetails, FindingPosition } from "../finding.ts";
2
3
  import type { InventorySymbol } from "../inventory.ts";
3
4
  import type { MatrixCandidate } from "../matrix.ts";
@@ -5,13 +6,14 @@ import { positionKey } from "../position.ts";
5
6
  import { declarationKey } from "../ref.ts";
6
7
  import type { Emitter, EmitterInput } from "./emitter.ts";
7
8
 
8
- /** The codes of this family. */
9
- type ReadsAndWritesCode = "DS1301" | "DS1302" | "DS1303";
9
+ /** The codes this emitter reports. */
10
+ type ReadsAndWritesCode =
11
+ typeof CODE.writeOnlySymbol | typeof CODE.unusedEnumMember | typeof CODE.unusedTypeParameter;
10
12
 
11
- /** The containers whose type parameters `DS1303` reports: a function, a class method and a type alias. */
13
+ /** The containers whose type parameters `DS1006` reports: a function, a class method and a type alias. */
12
14
  const SIGNATURES: ReadonlySet<string> = new Set(["function", "method", "type"]);
13
15
 
14
- /** What the family reads of one run, indexed by declaration. */
16
+ /** What this emitter reads of one run, indexed by declaration. */
15
17
  interface Run {
16
18
  readonly input: EmitterInput;
17
19
  readonly symbols: ReadonlyMap<string, InventorySymbol>;
@@ -185,7 +187,7 @@ function writeOnlySymbols(run: Run): readonly Finding[] {
185
187
  continue;
186
188
  }
187
189
  found.push(
188
- findingAbout(symbol, "DS1301", writeOnlyMessage(run, namedBy, symbol, held), {
190
+ findingAbout(symbol, CODE.writeOnlySymbol, writeOnlyMessage(run, namedBy, symbol, held), {
189
191
  writePositions: held.writes,
190
192
  }),
191
193
  );
@@ -214,18 +216,18 @@ function unnamed(run: Run, kind: InventorySymbol["kind"]): readonly InventorySym
214
216
  }
215
217
 
216
218
  /**
217
- * `DS1302`: a member of an enum declaration that nothing names. A member an enum-group
219
+ * `DS1005`: a member of an enum declaration that nothing names. A member an enum-group
218
220
  * record holds back is no candidate, so a member a conversion can produce never reaches
219
221
  * this kind.
220
222
  */
221
223
  function unusedEnumMembers(run: Run): readonly Finding[] {
222
224
  return unnamed(run, "enum-member").map((symbol) =>
223
- findingAbout(symbol, "DS1302", `enum member ${symbol.name} is named nowhere`),
225
+ findingAbout(symbol, CODE.unusedEnumMember, `enum member ${symbol.name} is named nowhere`),
224
226
  );
225
227
  }
226
228
 
227
229
  /**
228
- * `DS1303`: a type parameter of a function, a class method or a type alias that neither
230
+ * `DS1006`: a type parameter of a function, a class method or a type alias that neither
229
231
  * the declaration's signature nor its body names. An alias names no type of its own, so
230
232
  * a parameter its type never names makes no two instantiations distinct. One of a class,
231
233
  * an interface or a method signature is never reported: a class or an interface merges
@@ -237,7 +239,7 @@ function unusedTypeParameters(run: Run): readonly Finding[] {
237
239
  .map((symbol) =>
238
240
  findingAbout(
239
241
  symbol,
240
- "DS1303",
242
+ CODE.unusedTypeParameter,
241
243
  `type parameter ${symbol.name} is named in neither its signature nor its body`,
242
244
  ),
243
245
  );
@@ -257,8 +259,8 @@ export function writeOnlyDeclarations(input: EmitterInput): ReadonlySet<string>
257
259
  }
258
260
 
259
261
  /**
260
- * The findings of the reads-and-writes family, `DS1300` to `DS1399`, in code order and
261
- * each code's in site order.
262
+ * The findings of the reads-and-writes kinds, `DS1301`, `DS1005` and `DS1006`, in that
263
+ * order and each code's in site order.
262
264
  */
263
265
  export const readsAndWrites: Emitter = (input) => {
264
266
  const run = indexed(input);