@cplieger/deadset-ts 8.0.0-dev.5 → 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 (88) 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/exempt-classes.js +1 -59
  7. package/dist/src/exempt.js +8 -19
  8. package/dist/src/findings/completion.js +11 -20
  9. package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
  10. package/dist/src/findings/emitters.js +4 -4
  11. package/dist/src/findings/interfaces.js +4 -5
  12. package/dist/src/findings/intra-function.js +8 -7
  13. package/dist/src/findings/non-code-artifacts.js +5 -6
  14. package/dist/src/findings/reads-and-writes.js +9 -8
  15. package/dist/src/findings/self-check.js +7 -10
  16. package/dist/src/findings/unused-declarations.js +28 -27
  17. package/dist/src/findings/visibility-narrowing.js +9 -11
  18. package/dist/src/findings-pass.js +1 -3
  19. package/dist/src/index.js +1 -1
  20. package/dist/src/intra-function-parts.js +8 -13
  21. package/dist/src/kinds.js +4 -360
  22. package/dist/src/report-schema.js +273 -0
  23. package/dist/src/report.js +17 -11
  24. package/dist/src/resolve.js +1 -6
  25. package/dist/src/run.js +2 -1
  26. package/dist/src/setup-failure.js +2 -6
  27. package/dist/src/suppress-file.js +4 -3
  28. package/dist/src/suppress-inline.js +3 -2
  29. package/dist/src/suppress.js +29 -48
  30. package/dist/src/verbs/analyze.js +7 -2
  31. package/dist/src/verbs/describe.js +4 -4
  32. package/dist/src/verbs/explain.js +2 -1
  33. package/dist/src/verbs/print-config.js +2 -1
  34. package/dist/src/verbs/print-projects.js +2 -1
  35. package/dist/src/verbs/print-retained.js +2 -1
  36. package/dist/src/verbs/print-roots.js +2 -1
  37. package/dist/src/verbs/verb.js +1 -5
  38. package/dist/src/verbs/version.js +2 -2
  39. package/jsr.json +1 -1
  40. package/package.json +2 -1
  41. package/src/analysis.ts +3 -3
  42. package/src/config.ts +7 -13
  43. package/src/conformance-record.ts +1 -1
  44. package/src/contract/contract.generated.ts +1123 -0
  45. package/src/contract/report.generated.ts +1157 -0
  46. package/src/exempt-classes.ts +1 -70
  47. package/src/exempt.ts +12 -20
  48. package/src/finding.ts +9 -9
  49. package/src/findings/completion.ts +22 -23
  50. package/src/findings/dependencies-and-module-machinery.ts +4 -7
  51. package/src/findings/emitter.ts +6 -7
  52. package/src/findings/emitters.ts +6 -6
  53. package/src/findings/interfaces.ts +4 -6
  54. package/src/findings/intra-function.ts +8 -8
  55. package/src/findings/non-code-artifacts.ts +5 -7
  56. package/src/findings/reads-and-writes.ts +13 -11
  57. package/src/findings/self-check.ts +8 -15
  58. package/src/findings/unused-declarations.ts +28 -27
  59. package/src/findings/visibility-narrowing.ts +9 -12
  60. package/src/findings-pass.ts +1 -4
  61. package/src/index.ts +1 -1
  62. package/src/intra-function-parts.ts +8 -14
  63. package/src/kinds.ts +6 -406
  64. package/src/report-schema.ts +366 -0
  65. package/src/report.ts +42 -195
  66. package/src/reporters/annotations.ts +2 -2
  67. package/src/reporters/json.ts +1 -1
  68. package/src/reporters/reporter.ts +2 -2
  69. package/src/reporters/reporters.ts +1 -1
  70. package/src/reporters/sarif.ts +13 -8
  71. package/src/reporters/text.ts +3 -3
  72. package/src/resolve.ts +11 -11
  73. package/src/run.ts +2 -1
  74. package/src/setup-failure.ts +5 -7
  75. package/src/suppress-file.ts +3 -4
  76. package/src/suppress-inline.ts +2 -2
  77. package/src/suppress.ts +39 -55
  78. package/src/verbs/analyze.ts +16 -4
  79. package/src/verbs/describe.ts +10 -4
  80. package/src/verbs/explain.ts +2 -1
  81. package/src/verbs/print-config.ts +2 -1
  82. package/src/verbs/print-projects.ts +2 -1
  83. package/src/verbs/print-retained.ts +2 -1
  84. package/src/verbs/print-roots.ts +2 -1
  85. package/src/verbs/verb.ts +0 -6
  86. package/src/verbs/version.ts +2 -2
  87. package/dist/src/version.js +0 -7
  88. package/src/version.ts +0 -7
@@ -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");
@@ -6,8 +6,9 @@
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 { EXEMPTION_CLASSES } from "./contract/contract.generated.js";
9
10
  import { ConfigError } from "./config.js";
10
- import { EXEMPTION_CLASSES, isExemptionClass, TS_EXEMPTION_CLASSES, typescriptVisibilityOf, } from "./exempt-classes.js";
11
+ import { isExemptionClass, TS_EXEMPTION_CLASSES, typescriptVisibilityOf, } from "./exempt-classes.js";
11
12
  import { byPosition, positionKey } from "./position.js";
12
13
  import { Unanswerable } from "./query.js";
13
14
  import { sweep } from "./sweep.js";
@@ -40,20 +41,8 @@ function retainable(allowed, visibility) {
40
41
  }
41
42
  return true;
42
43
  }
43
- /**
44
- * The kinds of declaration each class can hold back, which a detector that stops holds
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
- ]);
44
+ /** The kinds of declaration each class can hold back, which a detector that stops holds back whole. */
45
+ const RETAINABLE = new Map(EXEMPTION_CLASSES.flatMap((row) => "ts" in row.retainsKinds ? [[row.class, new Set(row.retainsKinds.ts)]] : []));
57
46
  /** The detail of a record a detector that could not finish holds. */
58
47
  const UNANSWERED_DETAIL = "kept live by a question the checker did not answer";
59
48
  /** Whether one record is held by a detector that could not finish. */
@@ -76,7 +65,7 @@ function detected(exemptionClass, detect, input) {
76
65
  }
77
66
  const kinds = RETAINABLE.get(exemptionClass);
78
67
  return input.held.symbols
79
- .filter((symbol) => (kinds === undefined ? symbol.kind !== "file" : kinds.has(symbol.kind)))
68
+ .filter((symbol) => kinds?.has(symbol.kind) === true)
80
69
  .map((symbol) => ({ id: symbol.id, detail: UNANSWERED_DETAIL, site: symbol.position }));
81
70
  }
82
71
  }
@@ -197,8 +186,8 @@ export function retainedIn(matrix, input, writeOnly = new Set()) {
197
186
  return matrix.union.symbols.flatMap((symbol) => held.get(symbol.id) ?? []);
198
187
  }
199
188
  /**
200
- * One line per held-back declaration, in site order: its reference, then each record's
201
- * class, site and detail, all separated by tabs.
189
+ * One line per held-back declaration, in site order: its reference and position, then
190
+ * each record's class, site and detail, all separated by tabs.
202
191
  */
203
192
  export function retainedLines(symbols, retained) {
204
193
  const held = byDeclaration(retained);
@@ -212,6 +201,6 @@ export function retainedLines(symbols, retained) {
212
201
  positionKey(record.site),
213
202
  record.detail,
214
203
  ]);
215
- return [[symbol.ref, ...fields].join("\t")];
204
+ return [[symbol.ref, positionKey(symbol.position), ...fields].join("\t")];
216
205
  });
217
206
  }
@@ -3,29 +3,20 @@
3
3
  * the code's row of the issue-kind vocabulary, and the dials that withhold a finding
4
4
  * together with every finding that falls with a root they withhold.
5
5
  */
6
+ import { CODE, FAMILY_KEY_LENGTH, KIND_ROWS, } from "../contract/contract.generated.js";
6
7
  import { componentId } from "../components.js";
7
8
  import { deadComponents } from "../matrix.js";
8
9
  import { OUTSIDE } from "../graph.js";
9
10
  import { KINDS } from "../kinds.js";
10
11
  import { byPosition, positionKey } from "../position.js";
11
12
  import { declarationKey } from "../ref.js";
12
- import { FAMILY_KEY_LENGTH } from "../resolve.js";
13
13
  import { isPartSubject, isRowSubject } from "../suppress.js";
14
- /** The unused-file code, whose deletion removes every use its file writes. */
15
- const FILE_NEVER_IMPORTED = "DS1502";
16
14
  /**
17
- * The codes whose subject the run that counts test references holds live, as
18
- * finding.schema.json lists them, so a test file's candidate reported under one carries
19
- * none of a dead declaration's sweep facts.
15
+ * The codes whose subject the run that counts test references holds live, so a test
16
+ * file's candidate reported under one carries none of a dead declaration's sweep facts.
20
17
  */
21
- const LIVE_SUBJECT = new Set([
22
- "DS1101",
23
- "DS1102",
24
- "DS1104",
25
- "DS1204",
26
- "DS1301",
27
- ]);
28
- /** The fixability grammar/analysis.md "Fixability" gives a finding about a held declaration. */
18
+ const LIVE_SUBJECT = new Set(KIND_ROWS.filter((row) => row.holdsLive).map((row) => row.code));
19
+ /** The fixability grammar/analysis/fixability.md gives a finding about a held declaration. */
29
20
  const MANUAL = "manual";
30
21
  /** The member kinds an object property can declare, which an `in` expression tests by name. */
31
22
  const IN_TESTED = new Set([
@@ -147,7 +138,7 @@ function testCeilings(input, classOf) {
147
138
  }
148
139
  /**
149
140
  * The subjects dead only because production code only tests use is: each declaration the
150
- * sweep marks test-used, each `DS1502` file of such code, and each test of dead code every
141
+ * sweep marks test-used, each `DS1602` file of such code, and each test of dead code every
151
142
  * target of which is one. Nothing they hold references a dead declaration outside them, so
152
143
  * the rest of a component they share is reported without them.
153
144
  */
@@ -224,13 +215,13 @@ function membersListed(swept) {
224
215
  .sort((a, b) => byPosition(a.position, b.position));
225
216
  }
226
217
  /**
227
- * The run's dead components with each file a `DS1502` finding reports dead among them, so
218
+ * The run's dead components with each file a `DS1602` finding reports dead among them, so
228
219
  * the file's declarations fall with it, and with the components each subject that is no
229
220
  * declaration falls with joined into one.
230
221
  */
231
222
  function runComponents(swept, findings) {
232
223
  const union = swept.matrix.union;
233
- const reported = new Set(findings.filter((one) => one.code === FILE_NEVER_IMPORTED).map((one) => one.position.path));
224
+ const reported = new Set(findings.filter((one) => one.code === CODE.fileNeverImported).map((one) => one.position.path));
234
225
  const files = new Map(union.symbols.flatMap((symbol, at) => symbol.kind === "file" && reported.has(symbol.position.path) ? [[at, symbol]] : []));
235
226
  const computed = files.size === 0
236
227
  ? swept.sweep.components
@@ -330,7 +321,7 @@ export function completed(input, findings, kinds = KINDS) {
330
321
  : candidates.get(judged.id);
331
322
  const fallsWith = finding.fallsWith?.[0];
332
323
  const joined = fallsWith === undefined ? undefined : componentOf(fallsWith);
333
- const file = finding.code === FILE_NEVER_IMPORTED ? files.get(finding.position.path) : undefined;
324
+ const file = finding.code === CODE.fileNeverImported ? files.get(finding.position.path) : undefined;
334
325
  const found = joined !== undefined
335
326
  ? { ...joined, root: false }
336
327
  : file !== undefined
@@ -376,7 +367,7 @@ export function completed(input, findings, kinds = KINDS) {
376
367
  const ownClass = judged === undefined ? (finding.reachabilityClass ?? "certain") : classOf(judged.id);
377
368
  const used = judged !== undefined
378
369
  ? candidate !== undefined && testUsed.declarations.has(judged.id)
379
- : finding.code === FILE_NEVER_IMPORTED && testUsed.files.has(finding.position.path);
370
+ : finding.code === CODE.fileNeverImported && testUsed.files.has(finding.position.path);
380
371
  const reachabilityClass = used && candidate?.testOfDeadCode !== true ? "possible" : ownClass;
381
372
  const configurations = candidate?.configurations ??
382
373
  (judged === undefined && !part
@@ -396,7 +387,7 @@ export function completed(input, findings, kinds = KINDS) {
396
387
  ? undefined
397
388
  : candidate !== undefined
398
389
  ? input.dependencies.lastUses.get(candidate.id)
399
- : finding.code === FILE_NEVER_IMPORTED
390
+ : finding.code === CODE.fileNeverImported
400
391
  ? input.dependencies.fileLastUses.get(finding.position.path)
401
392
  : undefined;
402
393
  return {
@@ -1,8 +1,7 @@
1
+ import { CODE } from "../contract/contract.generated.js";
1
2
  import { declarationKey } from "../dependencies.js";
2
3
  import { renderRef } from "../ref.js";
3
4
  import { nonCodeArtifacts } from "./non-code-artifacts.js";
4
- const UNUSED_DEPENDENCY = "DS1601";
5
- const FILE_NEVER_IMPORTED = "DS1502";
6
5
  const MESSAGES = {
7
6
  dependency: "no import in the target needs the declared dependency",
8
7
  "dev-dependency": "no import in the target needs the declared development dependency",
@@ -25,7 +24,7 @@ function deadCode(input) {
25
24
  const reachedOnly = new Set(swept.sweep.candidates.filter((one) => one.relation === "reachability").map((one) => one.id));
26
25
  for (const finding of nonCodeArtifacts(input)) {
27
26
  const id = files.get(finding.position.path);
28
- if (finding.code === FILE_NEVER_IMPORTED && id !== undefined) {
27
+ if (finding.code === CODE.fileNeverImported && id !== undefined) {
29
28
  dead.add(id);
30
29
  if (finding.unreachableOnly === true) {
31
30
  reachedOnly.add(id);
@@ -57,7 +56,7 @@ function unusedDependencies(input, dependencies) {
57
56
  const atPossible = unbuilt || users?.some((id) => published.has(id)) === true;
58
57
  return [
59
58
  {
60
- code: UNUSED_DEPENDENCY,
59
+ code: CODE.unusedDependency,
61
60
  position: dependency.position,
62
61
  symbol: {
63
62
  ref: renderRef(dependency.manifest, {
@@ -80,5 +79,5 @@ function unusedDependencies(input, dependencies) {
80
79
  ];
81
80
  });
82
81
  }
83
- /** The findings of the dependencies-and-module-machinery family, `DS1600` to `DS1699`. */
82
+ /** The findings of the dependencies family, `DS1700` to `DS1799`. */
84
83
  export const dependenciesAndModuleMachinery = (input) => unusedDependencies(input, input.dependencies);
@@ -12,7 +12,7 @@ import { readsAndWrites } from "./reads-and-writes.js";
12
12
  import { selfCheck } from "./self-check.js";
13
13
  import { fallenMembers, unusedDeclarations } from "./unused-declarations.js";
14
14
  import { visibilityNarrowing } from "./visibility-narrowing.js";
15
- /** Every kind family this analyzer reports, each by its emitter, in code order. */
15
+ /** Every emitter this analyzer runs, by the name its tests select it by. */
16
16
  export const EMITTERS = new Map([
17
17
  ["unused-declarations", unusedDeclarations],
18
18
  ["visibility-narrowing", visibilityNarrowing],
@@ -51,7 +51,7 @@ function markupKeys(input) {
51
51
  .filter((symbol) => live.has(symbol.parent))
52
52
  .map((symbol) => declarationKey(symbol.ref, symbol.position.path)));
53
53
  }
54
- /** Every family's findings over one sweep, and those of any further emitter, completed by one step. */
54
+ /** Every emitter's findings over one sweep, and those of any further emitter, completed by one step. */
55
55
  function completedOver(input, ...more) {
56
56
  return completed(input, [...EMITTERS.values(), ...more].flatMap((emit) => emit(input)));
57
57
  }
@@ -70,8 +70,8 @@ function dialsOver(input, found, suppressed, unmarked) {
70
70
  }
71
71
  /**
72
72
  * Every finding of one run swept under its suppressions' marks, decided in one pass: each
73
- * family's findings completed by one step, so every component number is minted once, the
74
- * finding each record claims withheld, the rest withheld by the dials, so a root one family
73
+ * emitter's findings completed by one step, so every component number is minted once, the
74
+ * finding each record claims withheld, the rest withheld by the dials, so a root one emitter
75
75
  * reports withholds what another reports in its component, and every declared edge
76
76
  * evaluated over what remains, so a pending finding is in its evaluation alone.
77
77
  */
@@ -1,6 +1,5 @@
1
+ import { CODE } from "../contract/contract.generated.js";
1
2
  import { byPosition } from "../position.js";
2
- const UNUSED_INTERFACE = "DS1201";
3
- const UNCALLED_INTERFACE_METHOD = "DS1203";
4
3
  /** What each finding says, by what the sweep found about the subject's references. */
5
4
  const UNUSED_MESSAGE = "nothing in the target names the interface as a type";
6
5
  const UNUSED_DEAD_MESSAGE = "the interface is named as a type only from declarations that are themselves dead";
@@ -45,7 +44,7 @@ function findingOf(code, symbol, message, implementations) {
45
44
  }
46
45
  /**
47
46
  * The findings of the interfaces family, `DS1200` to `DS1299`: `DS1201`, a dead
48
- * interface, whose members fall with it unreported; and `DS1203`, a method of a live
47
+ * interface, whose members fall with it unreported; and `DS1202`, a method of a live
49
48
  * interface no reference names, unless it is a marker, a method whose every
50
49
  * implementation has an empty body. A test file's declaration is neither kind's subject,
51
50
  * because a production sweep counts none of the references it can have, save an interface
@@ -60,7 +59,7 @@ export const interfaces = (input) => {
60
59
  continue;
61
60
  }
62
61
  if (symbol.kind === "interface") {
63
- found.push(findingOf(UNUSED_INTERFACE, symbol, unusedMessage(candidate), run.implementationsOf(candidate.id)));
62
+ found.push(findingOf(CODE.unusedInterface, symbol, unusedMessage(candidate), run.implementationsOf(candidate.id)));
64
63
  continue;
65
64
  }
66
65
  if (symbol.kind !== "interface-method" ||
@@ -69,7 +68,7 @@ export const interfaces = (input) => {
69
68
  marker(input, candidate.id)) {
70
69
  continue;
71
70
  }
72
- found.push(findingOf(UNCALLED_INTERFACE_METHOD, symbol, UNCALLED_MESSAGE, run.implementationsOf(symbol.parent)));
71
+ found.push(findingOf(CODE.uncalledInterfaceMethod, symbol, UNCALLED_MESSAGE, run.implementationsOf(symbol.parent)));
73
72
  }
74
73
  return found;
75
74
  };
@@ -1,6 +1,7 @@
1
+ import { CODE } from "../contract/contract.generated.js";
1
2
  import { KINDS } from "../kinds.js";
2
3
  import { parameterRef } from "../ref.js";
3
- import { UNUSED_PARAMETER, UNUSED_RESULT } from "../intra-function-parts.js";
4
+ import {} from "../intra-function-parts.js";
4
5
  import { reachedWithTheTests } from "../sweep.js";
5
6
  /** A list of rules as a sentence names them. */
6
7
  function spelled(rules) {
@@ -62,9 +63,9 @@ function reported(input, part, callersUnknown) {
62
63
  }
63
64
  const { free, freeButValued, discardedEverywhere } = input.intraFunction;
64
65
  switch (part.code) {
65
- case UNUSED_PARAMETER:
66
+ case CODE.unusedParameter:
66
67
  return parameterVerdict(part, free, freeButValued) !== undefined;
67
- case UNUSED_RESULT:
68
+ case CODE.unusedResult:
68
69
  return (free.has(part.declaration) &&
69
70
  discardedEverywhere.has(part.declaration) &&
70
71
  !callersUnknown.has(part.declaration));
@@ -73,7 +74,7 @@ function reported(input, part, callersUnknown) {
73
74
  }
74
75
  }
75
76
  /**
76
- * The findings of the intra-function family, `DS1800` to `DS1899`, each about a part of
77
+ * The findings of the inside-functions family, `DS1401` to `DS1406`, each about a part of
77
78
  * the declaration its reference names, at the part's own position, whatever the sweep
78
79
  * decided about a production declaration. A part of test code is reported only where the
79
80
  * run that counts test references reaches its declaration and that is no test of dead
@@ -114,11 +115,11 @@ export const intraFunction = (input) => {
114
115
  const found = [];
115
116
  for (const part of input.intraFunction.parts) {
116
117
  const declaration = byId.get(part.declaration);
117
- const overlap = KINDS.get(part.code)?.overlap ?? [];
118
+ const overlap = KINDS.get(part.code)?.overlap?.ts ?? [];
118
119
  if (declaration === undefined || !judged(declaration.id) || !reported(input, part, unknown)) {
119
120
  continue;
120
121
  }
121
- const verdict = part.code === UNUSED_PARAMETER
122
+ const verdict = part.code === CODE.unusedParameter
122
123
  ? parameterVerdict(part, input.intraFunction.free, input.intraFunction.freeButValued)
123
124
  : undefined;
124
125
  const message = verdict?.fix === "rename"
@@ -128,7 +129,7 @@ export const intraFunction = (input) => {
128
129
  code: part.code,
129
130
  position: part.position,
130
131
  symbol: {
131
- ...(part.code === UNUSED_PARAMETER
132
+ ...(part.code === CODE.unusedParameter
132
133
  ? { ref: parameterRef(declaration.ref, part.name), holder: declaration.ref }
133
134
  : { ref: declaration.ref }),
134
135
  kind: part.kind,
@@ -1,5 +1,4 @@
1
- const FILE_NEVER_BUILT = "DS1501";
2
- const FILE_NEVER_IMPORTED = "DS1502";
1
+ import { CODE } from "../contract/contract.generated.js";
3
2
  /** The subject kind of a finding about a source file, from the finding schema's vocabulary. */
4
3
  const FILE_SUBJECT = "file";
5
4
  /** A file's subject starts at its first character. */
@@ -40,7 +39,7 @@ function fileNeverBuilt({ config, swept, files }) {
40
39
  const message = `no project of the build matrix includes this file (${swept.matrix.configurations.join(", ")})`;
41
40
  return files.tree
42
41
  .filter((file) => !built.has(file.path))
43
- .map((file) => fileFinding(FILE_NEVER_BUILT, file, message));
42
+ .map((file) => fileFinding(CODE.fileNeverBuilt, file, message));
44
43
  }
45
44
  /**
46
45
  * The paths of the files a reference from a live declaration of another file reaches, a
@@ -125,13 +124,13 @@ function fileNeverImported(input) {
125
124
  const file = { path: symbol.position.path, ref: symbol.ref, endLine: symbol.endLine };
126
125
  return referenced.has(symbol.position.path)
127
126
  ? {
128
- ...fileFinding(FILE_NEVER_IMPORTED, file, "only code that is itself dead imports this file, and no root names it or a declaration in it"),
127
+ ...fileFinding(CODE.fileNeverImported, file, "only code that is itself dead imports this file, and no root names it or a declaration in it"),
129
128
  unreachableOnly: true,
130
129
  }
131
- : fileFinding(FILE_NEVER_IMPORTED, file, "no import reaches this file and no root names it or a declaration in it");
130
+ : fileFinding(CODE.fileNeverImported, file, "no import reaches this file and no root names it or a declaration in it");
132
131
  });
133
132
  }
134
- /** The findings of the non-code-artifacts family, `DS1500` to `DS1599`. */
133
+ /** The findings of the files family, `DS1600` to `DS1699`. */
135
134
  export const nonCodeArtifacts = (input) => [
136
135
  ...fileNeverBuilt(input),
137
136
  ...fileNeverImported(input),
@@ -1,6 +1,7 @@
1
+ import { CODE } from "../contract/contract.generated.js";
1
2
  import { positionKey } from "../position.js";
2
3
  import { declarationKey } from "../ref.js";
3
- /** The containers whose type parameters `DS1303` reports: a function, a class method and a type alias. */
4
+ /** The containers whose type parameters `DS1006` reports: a function, a class method and a type alias. */
4
5
  const SIGNATURES = new Set(["function", "method", "type"]);
5
6
  function indexed(input) {
6
7
  const { matrix, sweep } = input.swept;
@@ -133,7 +134,7 @@ function writeOnlySymbols(run) {
133
134
  run.candidates.has(symbol.id)) {
134
135
  continue;
135
136
  }
136
- found.push(findingAbout(symbol, "DS1301", writeOnlyMessage(run, namedBy, symbol, held), {
137
+ found.push(findingAbout(symbol, CODE.writeOnlySymbol, writeOnlyMessage(run, namedBy, symbol, held), {
137
138
  writePositions: held.writes,
138
139
  }));
139
140
  }
@@ -157,15 +158,15 @@ function unnamed(run, kind) {
157
158
  });
158
159
  }
159
160
  /**
160
- * `DS1302`: a member of an enum declaration that nothing names. A member an enum-group
161
+ * `DS1005`: a member of an enum declaration that nothing names. A member an enum-group
161
162
  * record holds back is no candidate, so a member a conversion can produce never reaches
162
163
  * this kind.
163
164
  */
164
165
  function unusedEnumMembers(run) {
165
- return unnamed(run, "enum-member").map((symbol) => findingAbout(symbol, "DS1302", `enum member ${symbol.name} is named nowhere`));
166
+ return unnamed(run, "enum-member").map((symbol) => findingAbout(symbol, CODE.unusedEnumMember, `enum member ${symbol.name} is named nowhere`));
166
167
  }
167
168
  /**
168
- * `DS1303`: a type parameter of a function, a class method or a type alias that neither
169
+ * `DS1006`: a type parameter of a function, a class method or a type alias that neither
169
170
  * the declaration's signature nor its body names. An alias names no type of its own, so
170
171
  * a parameter its type never names makes no two instantiations distinct. One of a class,
171
172
  * an interface or a method signature is never reported: a class or an interface merges
@@ -174,7 +175,7 @@ function unusedEnumMembers(run) {
174
175
  function unusedTypeParameters(run) {
175
176
  return unnamed(run, "type-parameter")
176
177
  .filter((symbol) => SIGNATURES.has(run.symbols.get(symbol.parent)?.kind ?? ""))
177
- .map((symbol) => findingAbout(symbol, "DS1303", `type parameter ${symbol.name} is named in neither its signature nor its body`));
178
+ .map((symbol) => findingAbout(symbol, CODE.unusedTypeParameter, `type parameter ${symbol.name} is named in neither its signature nor its body`));
178
179
  }
179
180
  /**
180
181
  * The keys of the declarations the write-only kind reports, whatever the
@@ -185,8 +186,8 @@ export function writeOnlyDeclarations(input) {
185
186
  return new Set(writeOnlySymbols(indexed(input)).map((finding) => declarationKey(finding.symbol.ref, finding.position.path)));
186
187
  }
187
188
  /**
188
- * The findings of the reads-and-writes family, `DS1300` to `DS1399`, in code order and
189
- * each code's in site order.
189
+ * The findings of the reads-and-writes kinds, `DS1301`, `DS1005` and `DS1006`, in that
190
+ * order and each code's in site order.
190
191
  */
191
192
  export const readsAndWrites = (input) => {
192
193
  const run = indexed(input);
@@ -1,10 +1,7 @@
1
+ import { CODE } from "../contract/contract.generated.js";
1
2
  import { REPOSITORY_DOCUMENT } from "../config.js";
2
3
  import { FIXED_SEVERITY_CODES } from "../kinds.js";
3
- import { isDependencyCode, isUnsuppressibleCode, STALE_SUPPRESSION, SUPPRESSION_WITHOUT_REASON, } from "../suppress.js";
4
- /** The code a configured root that matches nothing is reported under. */
5
- const UNMATCHED_ROOT = "DS1704";
6
- /** The code a configured declaration that names nothing is reported under. */
7
- const UNMATCHED_DECLARATION = "DS1706";
4
+ import { isDependencyCode, isUnsuppressibleCode, } from "../suppress.js";
8
5
  /** The subject kind of a finding about a configured declaration. */
9
6
  const DECLARATION_SUBJECT = "configured-declaration";
10
7
  /** The subject kind of a finding about a suppression record. */
@@ -63,7 +60,7 @@ function suppressionRef(symbol, path, files) {
63
60
  }
64
61
  /** One finding per refused suppression: the grammar's reason refusal and its scope refusal. */
65
62
  function refusedSuppressions(refusals, files) {
66
- return refusals.map((refused) => aboutSuppression(refused.reported, suppressionRef(refused.symbol, refused.path, files), refused.site, `${WRITTEN[refused.mechanism]} for ${refused.code} ${refused.reported === SUPPRESSION_WITHOUT_REASON
63
+ return refusals.map((refused) => aboutSuppression(refused.reported, suppressionRef(refused.symbol, refused.path, files), refused.site, `${WRITTEN[refused.mechanism]} for ${refused.code} ${refused.reported === CODE.suppressionWithoutReason
67
64
  ? "carries no reason"
68
65
  : "names a symbol and no path"}`, { mechanism: refused.mechanism, entry: writtenEntry(refused) }));
69
66
  }
@@ -112,7 +109,7 @@ export function staleSuppressions(stale, files) {
112
109
  ? ""
113
110
  : `, and ${declared}`;
114
111
  return {
115
- code: STALE_SUPPRESSION,
112
+ code: CODE.staleSuppression,
116
113
  mechanism: first.mechanism,
117
114
  entry: {
118
115
  code: first.code,
@@ -141,7 +138,7 @@ function documentPosition(document) {
141
138
  */
142
139
  function unmatchedDeclarationFindings(unmatched) {
143
140
  return unmatched.map((one) => ({
144
- code: UNMATCHED_DECLARATION,
141
+ code: CODE.unmatchedConfiguredDeclaration,
145
142
  position: documentPosition(one.document),
146
143
  symbol: { ref: one.ref, kind: DECLARATION_SUBJECT, name: one.ref, sizeLines: 1 },
147
144
  message: `${one.key} entry names no declaration in any project`,
@@ -157,7 +154,7 @@ function unmatchedDeclarationFindings(unmatched) {
157
154
  export function unmatchedRootFindings(sources, document) {
158
155
  const position = documentPosition(document);
159
156
  return sources.map((source) => ({
160
- code: UNMATCHED_ROOT,
157
+ code: CODE.unmatchedRoot,
161
158
  position,
162
159
  symbol: { ref: source, kind: ROOT_SUBJECT, name: source, sizeLines: 1 },
163
160
  message: source.includes("*") || source.includes("?")
@@ -166,7 +163,7 @@ export function unmatchedRootFindings(sources, document) {
166
163
  }));
167
164
  }
168
165
  /**
169
- * The findings of the self-check family, `DS1700` to `DS1799`: a suppression refused for
166
+ * The findings of the configuration family, `DS9000` to `DS9099`: a suppression refused for
170
167
  * carrying no reason or naming no path, and a configured root or declaration that named
171
168
  * nothing. A
172
169
  * suppression that matched nothing is a record of the report's stale suppressions, which