@cplieger/deadset-ts 3.6.0 → 3.7.0

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.
@@ -1,72 +1,20 @@
1
- import type { Confidence, Severity } from "../config.ts";
2
- import type { Finding, FindingPosition } from "../finding.ts";
1
+ import type { Finding, FindingDetails, FindingPosition } from "../finding.ts";
3
2
  import type { InventorySymbol } from "../inventory.ts";
4
3
  import type { MatrixCandidate } from "../matrix.ts";
5
4
  import { positionKey } from "../position.ts";
6
- import type { Relation } from "../sweep.ts";
7
- import {
8
- findingComponents,
9
- RANK,
10
- reachabilityClasses,
11
- severityOf,
12
- type FindingComponent,
13
- } from "./completion.ts";
14
5
  import type { Emitter, EmitterInput } from "./emitter.ts";
15
6
 
16
7
  /** The codes of this family. */
17
- export type ReadsAndWritesCode = "DS1301" | "DS1302" | "DS1303";
18
-
19
- /**
20
- * Each code's severity where the configuration sets none, as the issue-kind vocabulary
21
- * states it.
22
- */
23
- export const DEFAULT_SEVERITY: Readonly<Record<ReadsAndWritesCode, Severity>> = {
24
- DS1301: "deny",
25
- DS1302: "deny",
26
- DS1303: "deny",
27
- };
28
-
29
- /** What only some codes carry. */
30
- export interface ReadsAndWritesDetails {
31
- /** Every position the subject is written at, on `DS1301` alone. */
32
- readonly writePositions?: readonly FindingPosition[];
33
- }
34
-
35
- /** One finding of this family, with every member the family decides. */
36
- export interface ReadsAndWritesFinding extends Finding {
37
- readonly code: ReadsAndWritesCode;
38
- readonly reachabilityClass: Confidence;
39
- readonly confidence: Confidence;
40
- /** The relation that decided the subject, absent on `DS1301`, whose subject is live. */
41
- readonly livenessRelation?: Relation;
42
- /** Whether every reference the run counts to the subject comes from a test file. */
43
- readonly testOnly: boolean;
44
- /**
45
- * The dead component the subject belongs to, absent where it belongs to none: a live
46
- * subject falls with nothing, and the report gives it a component of its own.
47
- */
48
- readonly component?: FindingComponent;
49
- /** The configurations the finding holds in, in the run's order. */
50
- readonly configurations: readonly string[];
51
- readonly severity: Severity;
52
- readonly details: ReadsAndWritesDetails;
53
- }
8
+ type ReadsAndWritesCode = "DS1301" | "DS1302" | "DS1303";
54
9
 
55
10
  /** The containers whose type parameters `DS1303` reports: a function and a class method. */
56
11
  const SIGNATURES: ReadonlySet<string> = new Set(["function", "method"]);
57
12
 
58
- /** The severity the configuration gives one code of the family. */
59
- function severityIn(input: EmitterInput, code: ReadsAndWritesCode): Severity {
60
- return severityOf(input.config, code, DEFAULT_SEVERITY[code]);
61
- }
62
-
63
13
  /** What the family reads of one run, indexed by declaration. */
64
14
  interface Run {
65
15
  readonly input: EmitterInput;
66
16
  readonly symbols: ReadonlyMap<string, InventorySymbol>;
67
17
  readonly candidates: ReadonlyMap<string, MatrixCandidate>;
68
- readonly componentOf: (id: string) => FindingComponent | undefined;
69
- readonly classOf: (id: string) => Confidence;
70
18
  }
71
19
 
72
20
  function indexed(input: EmitterInput): Run {
@@ -75,8 +23,6 @@ function indexed(input: EmitterInput): Run {
75
23
  input,
76
24
  symbols: new Map(matrix.union.symbols.map((symbol) => [symbol.id, symbol])),
77
25
  candidates: new Map(sweep.candidates.map((candidate) => [candidate.id, candidate])),
78
- componentOf: findingComponents(input.swept),
79
- classOf: reachabilityClasses(input.swept),
80
26
  };
81
27
  }
82
28
 
@@ -90,22 +36,14 @@ function positionOf(symbol: InventorySymbol): FindingPosition {
90
36
  };
91
37
  }
92
38
 
93
- /** The finding about one declaration, every member the family decides filled in. */
39
+ /** The finding one code makes about one declaration. */
94
40
  function findingAbout(
95
- run: Run,
96
41
  symbol: InventorySymbol,
97
42
  code: ReadsAndWritesCode,
98
43
  message: string,
99
- facts: {
100
- readonly livenessRelation?: Relation;
101
- readonly testOnly: boolean;
102
- readonly configurations: readonly string[];
103
- readonly details: ReadsAndWritesDetails;
104
- },
105
- ): ReadsAndWritesFinding {
44
+ details?: FindingDetails,
45
+ ): Finding {
106
46
  const position = positionOf(symbol);
107
- const reachabilityClass = run.classOf(symbol.id);
108
- const component = run.componentOf(symbol.id);
109
47
  return {
110
48
  code,
111
49
  position,
@@ -116,14 +54,7 @@ function findingAbout(
116
54
  sizeLines: position.endLine - position.line + 1,
117
55
  },
118
56
  message,
119
- reachabilityClass,
120
- confidence: reachabilityClass,
121
- ...(facts.livenessRelation === undefined ? {} : { livenessRelation: facts.livenessRelation }),
122
- testOnly: facts.testOnly,
123
- ...(component === undefined ? {} : { component }),
124
- configurations: facts.configurations,
125
- severity: severityIn(run.input, code),
126
- details: facts.details,
57
+ ...(details === undefined ? {} : { details }),
127
58
  };
128
59
  }
129
60
 
@@ -131,8 +62,6 @@ function findingAbout(
131
62
  interface Uses {
132
63
  readonly writes: FindingPosition[];
133
64
  reads: number;
134
- production: number;
135
- test: number;
136
65
  }
137
66
 
138
67
  /**
@@ -155,21 +84,16 @@ function writable(run: Run, symbol: InventorySymbol): boolean {
155
84
  * written one live and its candidates are never this kind's. Every reference that is
156
85
  * not a store reads, and so does an exemption record naming the declaration.
157
86
  */
158
- function writeOnlySymbols(run: Run): readonly ReadsAndWritesFinding[] {
87
+ function writeOnlySymbols(run: Run): readonly Finding[] {
159
88
  const { stores } = run.input;
160
89
  const { matrix } = run.input.swept;
161
90
  const uses = new Map<string, Uses>();
162
91
  for (const reference of stores.references) {
163
92
  let held = uses.get(reference.to);
164
93
  if (held === undefined) {
165
- held = { writes: [], reads: 0, production: 0, test: 0 };
94
+ held = { writes: [], reads: 0 };
166
95
  uses.set(reference.to, held);
167
96
  }
168
- if (reference.test) {
169
- held.test += 1;
170
- } else {
171
- held.production += 1;
172
- }
173
97
  if (reference.use !== "write") {
174
98
  held.reads += 1;
175
99
  continue;
@@ -181,8 +105,8 @@ function writeOnlySymbols(run: Run): readonly ReadsAndWritesFinding[] {
181
105
  }
182
106
  }
183
107
 
184
- const found: ReadsAndWritesFinding[] = [];
185
- matrix.union.symbols.forEach((symbol, at) => {
108
+ const found: Finding[] = [];
109
+ for (const symbol of matrix.union.symbols) {
186
110
  const held = uses.get(symbol.id);
187
111
  if (
188
112
  held === undefined ||
@@ -192,24 +116,16 @@ function writeOnlySymbols(run: Run): readonly ReadsAndWritesFinding[] {
192
116
  stores.exempt.has(symbol.id) ||
193
117
  run.candidates.has(symbol.id)
194
118
  ) {
195
- return;
119
+ continue;
196
120
  }
197
121
  const writes = held.writes.length === 1 ? "once" : `at ${String(held.writes.length)} positions`;
198
122
  const word = symbol.kind === "variable" ? "variable" : "member";
199
123
  found.push(
200
- findingAbout(
201
- run,
202
- symbol,
203
- "DS1301",
204
- `${word} ${symbol.name} is written ${writes} and never read`,
205
- {
206
- testOnly: held.production === 0 && held.test > 0,
207
- configurations: matrix.heldIn[at] ?? [],
208
- details: { writePositions: held.writes },
209
- },
210
- ),
124
+ findingAbout(symbol, "DS1301", `${word} ${symbol.name} is written ${writes} and never read`, {
125
+ writePositions: held.writes,
126
+ }),
211
127
  );
212
- });
128
+ }
213
129
  return found;
214
130
  }
215
131
 
@@ -218,24 +134,19 @@ function writeOnlySymbols(run: Run): readonly ReadsAndWritesFinding[] {
218
134
  * one kind. A reference from a test file alone leaves a candidate to the test-only kind,
219
135
  * and a member of a dead container falls with it and is reported inside its component.
220
136
  */
221
- function unnamed(
222
- run: Run,
223
- kind: InventorySymbol["kind"],
224
- ): readonly [MatrixCandidate, InventorySymbol][] {
225
- return run.input.swept.sweep.candidates.flatMap(
226
- (candidate): [MatrixCandidate, InventorySymbol][] => {
227
- const symbol = run.symbols.get(candidate.id);
228
- if (
229
- symbol?.kind !== kind ||
230
- candidate.productionRefs > 0 ||
231
- candidate.testRefs > 0 ||
232
- run.candidates.has(symbol.parent)
233
- ) {
234
- return [];
235
- }
236
- return [[candidate, symbol]];
237
- },
238
- );
137
+ function unnamed(run: Run, kind: InventorySymbol["kind"]): readonly InventorySymbol[] {
138
+ return run.input.swept.sweep.candidates.flatMap((candidate): InventorySymbol[] => {
139
+ const symbol = run.symbols.get(candidate.id);
140
+ if (
141
+ symbol?.kind !== kind ||
142
+ candidate.productionRefs > 0 ||
143
+ candidate.testRefs > 0 ||
144
+ run.candidates.has(symbol.parent)
145
+ ) {
146
+ return [];
147
+ }
148
+ return [symbol];
149
+ });
239
150
  }
240
151
 
241
152
  /**
@@ -243,14 +154,9 @@ function unnamed(
243
154
  * record holds back is no candidate, so a member a conversion can produce never reaches
244
155
  * this kind.
245
156
  */
246
- function unusedEnumMembers(run: Run): readonly ReadsAndWritesFinding[] {
247
- return unnamed(run, "enum-member").map(([candidate, symbol]) =>
248
- findingAbout(run, symbol, "DS1302", `enum member ${symbol.name} is named nowhere`, {
249
- livenessRelation: candidate.relation,
250
- testOnly: false,
251
- configurations: candidate.configurations,
252
- details: {},
253
- }),
157
+ function unusedEnumMembers(run: Run): readonly Finding[] {
158
+ return unnamed(run, "enum-member").map((symbol) =>
159
+ findingAbout(symbol, "DS1302", `enum member ${symbol.name} is named nowhere`),
254
160
  );
255
161
  }
256
162
 
@@ -260,21 +166,14 @@ function unusedEnumMembers(run: Run): readonly ReadsAndWritesFinding[] {
260
166
  * a type alias or a method signature belongs to a type declaration and is never
261
167
  * reported, because a phantom parameter makes two instantiations distinct types.
262
168
  */
263
- function unusedTypeParameters(run: Run): readonly ReadsAndWritesFinding[] {
169
+ function unusedTypeParameters(run: Run): readonly Finding[] {
264
170
  return unnamed(run, "type-parameter")
265
- .filter(([, symbol]) => SIGNATURES.has(run.symbols.get(symbol.parent)?.kind ?? ""))
266
- .map(([candidate, symbol]) =>
171
+ .filter((symbol) => SIGNATURES.has(run.symbols.get(symbol.parent)?.kind ?? ""))
172
+ .map((symbol) =>
267
173
  findingAbout(
268
- run,
269
174
  symbol,
270
175
  "DS1303",
271
176
  `type parameter ${symbol.name} is named in neither its signature nor its body`,
272
- {
273
- livenessRelation: candidate.relation,
274
- testOnly: false,
275
- configurations: candidate.configurations,
276
- details: {},
277
- },
278
177
  ),
279
178
  );
280
179
  }
@@ -288,24 +187,11 @@ export function writeOnlyDeclarations(input: EmitterInput): ReadonlySet<string>
288
187
  return new Set(writeOnlySymbols(indexed(input)).map((finding) => finding.symbol.ref));
289
188
  }
290
189
 
291
- /** Each code's rule, in code order. */
292
- const RULES: readonly [ReadsAndWritesCode, (run: Run) => readonly ReadsAndWritesFinding[]][] = [
293
- ["DS1301", writeOnlySymbols],
294
- ["DS1302", unusedEnumMembers],
295
- ["DS1303", unusedTypeParameters],
296
- ];
297
-
298
190
  /**
299
- * The findings of the reads-and-writes family, `DS1300` to `DS1399`, each code's in site
300
- * order. A code the configuration sets to `allow` reports nothing, and a finding whose
301
- * confidence is below the configured minimum is withheld.
191
+ * The findings of the reads-and-writes family, `DS1300` to `DS1399`, in code order and
192
+ * each code's in site order.
302
193
  */
303
194
  export const readsAndWrites: Emitter = (input) => {
304
195
  const run = indexed(input);
305
- const least = RANK[input.config.analysis.minConfidence];
306
- return RULES.flatMap(([code, rule]) =>
307
- severityIn(input, code) === "allow"
308
- ? []
309
- : rule(run).filter((finding) => RANK[finding.confidence] >= least),
310
- );
196
+ return [...writeOnlySymbols(run), ...unusedEnumMembers(run), ...unusedTypeParameters(run)];
311
197
  };
@@ -1,33 +1,9 @@
1
- import type { Confidence, Severity } from "../config.ts";
1
+ import type { Finding } from "../finding.ts";
2
2
  import type { InventorySymbol, SymbolKind } from "../inventory.ts";
3
3
  import type { MatrixCandidate } from "../matrix.ts";
4
- import {
5
- capped,
6
- findingComponents,
7
- reachabilityClasses,
8
- reportable,
9
- severityOf,
10
- type CompletedFinding,
11
- } from "./completion.ts";
12
- import type { Emitter, EmitterInput } from "./emitter.ts";
4
+ import type { Emitter } from "./emitter.ts";
13
5
  import { unreachableExports } from "./visibility-narrowing.ts";
14
6
 
15
- /** What the issue-kind vocabulary states about one code of the family. */
16
- interface KindRow {
17
- readonly defaultSeverity: Severity;
18
- readonly maxClass: Confidence;
19
- }
20
-
21
- /** The codes of the unused-declarations family, each by its row of the vocabulary. */
22
- export const UNUSED_DECLARATION_KINDS: ReadonlyMap<string, KindRow> = new Map([
23
- ["DS1001", { defaultSeverity: "deny", maxClass: "certain" }],
24
- ["DS1002", { defaultSeverity: "deny", maxClass: "certain" }],
25
- ["DS1003", { defaultSeverity: "deny", maxClass: "certain" }],
26
- ["DS1004", { defaultSeverity: "deny", maxClass: "certain" }],
27
- ["DS1005", { defaultSeverity: "deny", maxClass: "certain" }],
28
- ["DS1006", { defaultSeverity: "deny", maxClass: "certain" }],
29
- ]);
30
-
31
7
  /** The kinds of declaration that are members of a class, an interface, a type or an enum. */
32
8
  const MEMBERS: ReadonlySet<SymbolKind> = new Set([
33
9
  "method",
@@ -160,20 +136,14 @@ function messageOf(code: string, judged: Judged): string {
160
136
  return `${subject} has no reference in the target`;
161
137
  }
162
138
 
163
- /**
164
- * The findings of the unused-declarations family, `DS1000` to `DS1099`, completed and
165
- * with what the dials withhold left out.
166
- */
167
- export function unusedDeclarationFindings(input: EmitterInput): readonly CompletedFinding[] {
168
- const { config, swept } = input;
169
- const union = swept.matrix.union;
170
- const dead = new Set(swept.sweep.candidates.map((candidate) => candidate.id));
171
- const classOf = reachabilityClasses(swept);
172
- const componentOf = findingComponents(swept);
139
+ /** The findings of the unused-declarations family, `DS1000` to `DS1099`. */
140
+ export const unusedDeclarations: Emitter = (input) => {
141
+ const union = input.swept.matrix.union;
142
+ const dead = new Set(input.swept.sweep.candidates.map((candidate) => candidate.id));
173
143
  const unreachable = unreachableExports(input);
174
144
 
175
- const found: CompletedFinding[] = [];
176
- for (const candidate of swept.sweep.candidates) {
145
+ const found: Finding[] = [];
146
+ for (const candidate of input.swept.sweep.candidates) {
177
147
  const at = union.at(candidate.id);
178
148
  const symbol = union.symbols[at];
179
149
  if (symbol === undefined) {
@@ -188,16 +158,10 @@ export function unusedDeclarationFindings(input: EmitterInput): readonly Complet
188
158
  unreachable: unreachable.has(symbol.id),
189
159
  };
190
160
  const code = codeOf(judged);
191
- const row = code === undefined ? undefined : UNUSED_DECLARATION_KINDS.get(code);
192
- if (code === undefined || row === undefined) {
161
+ if (code === undefined) {
193
162
  continue;
194
163
  }
195
- const component = componentOf(symbol.id);
196
- if (component === undefined) {
197
- throw new Error(`${symbol.id} is reported dead and falls in no dead component`);
198
- }
199
164
  const endLine = Math.max(symbol.position.line, symbol.endLine);
200
- const reachabilityClass = classOf(symbol.id);
201
165
  found.push({
202
166
  code,
203
167
  position: { ...symbol.position, endLine },
@@ -208,18 +172,7 @@ export function unusedDeclarationFindings(input: EmitterInput): readonly Complet
208
172
  sizeLines: endLine - symbol.position.line + 1,
209
173
  },
210
174
  message: messageOf(code, judged),
211
- reachabilityClass,
212
- confidence: capped(reachabilityClass, row.maxClass),
213
- livenessRelation: candidate.relation,
214
- testOnly: candidate.productionRefs === 0 && candidate.testRefs > 0,
215
- component,
216
- configurations: candidate.configurations,
217
- severity: severityOf(config, code, row.defaultSeverity),
218
- details: {},
219
175
  });
220
176
  }
221
- return reportable(found, config.analysis.minConfidence);
222
- }
223
-
224
- /** The findings of the unused-declarations family, `DS1000` to `DS1099`. */
225
- export const unusedDeclarations: Emitter = unusedDeclarationFindings;
177
+ return found;
178
+ };
@@ -126,18 +126,13 @@ function wordOf(symbol: InventorySymbol): string {
126
126
  return WORDS[symbol.kind] ?? symbol.kind;
127
127
  }
128
128
 
129
- /** A finding of this family, with the visibility a narrowing would give its subject. */
130
- interface NarrowingFinding extends Finding {
131
- readonly details: { readonly narrowerVisibility?: "file" | "package" };
132
- }
133
-
134
129
  /** The visibility each narrowing code names, which only the narrowing codes carry. */
135
130
  const NARROWER: Readonly<Record<string, "file" | "package">> = {
136
131
  [REDUNDANT_EXPORT_KEYWORD]: "file",
137
132
  [UNNECESSARY_EXPORT]: "package",
138
133
  };
139
134
 
140
- function findingOf(symbol: InventorySymbol, code: string, message: string): NarrowingFinding {
135
+ function findingOf(symbol: InventorySymbol, code: string, message: string): Finding {
141
136
  const { path, line, column } = symbol.position;
142
137
  const narrower = Object.hasOwn(NARROWER, code) ? NARROWER[code] : undefined;
143
138
  const endLine = Math.max(line, symbol.endLine);
package/src/kinds.ts CHANGED
@@ -1,53 +1,87 @@
1
+ import type { Confidence, Severity } from "./config.ts";
2
+
3
+ /** What a mechanical edit may do with a finding of one kind. */
4
+ export type Fixability = "deletable" | "narrowable" | "manual" | "none";
5
+
6
+ /** One live row of the issue-kind vocabulary, as far as an analyzer reads it. */
7
+ export interface KindRow {
8
+ readonly code: string;
9
+ /** The kind's name, which a finding carries as its `kind`. */
10
+ readonly name: string;
11
+ /** The severity a finding carries where no severity key names its code or its family. */
12
+ readonly defaultSeverity: Severity;
13
+ /** The highest confidence a finding of the kind carries. */
14
+ readonly maxClass: Confidence;
15
+ readonly fixability: Fixability;
16
+ /**
17
+ * Whether the Contract fixes the kind on at its default severity, so a severity key
18
+ * naming the code, or a family prefix whose range holds it, is an unimplemented key.
19
+ */
20
+ readonly fixed: boolean;
21
+ }
22
+
1
23
  /**
2
- * The issue-kind codes whose severity the Contract fixes. A severity key naming
3
- * one, or a family prefix whose range holds one, is an unimplemented key rather
4
- * than a setting.
5
- *
6
- * The codes are written out rather than read from the Contract at run time: the
7
- * Contract is data a reader outside this program consults, and a product that
8
- * read it while running would need it installed beside itself. A test compares
9
- * this list against the Contract release the analyzer is written against, so a
10
- * code the Contract fixes arrives here as a failing test.
24
+ * Every live row of the issue-kind vocabulary, in ascending code order, written out
25
+ * rather than read at run time, because a product that read the Contract while running
26
+ * would need it installed beside itself. A test compares this table against the
27
+ * Contract release the analyzer is written against.
11
28
  */
12
- export const FIXED_SEVERITY_CODES: readonly string[] = ["DS1703", "DS1704"];
29
+ const ROWS: readonly KindRow[] = [
30
+ row("DS1001", "unused-exported", "deny", "deletable"),
31
+ row("DS1002", "unused-unexported", "deny", "deletable"),
32
+ row("DS1003", "unused-member", "deny", "deletable"),
33
+ row("DS1004", "test-only-use", "deny", "deletable"),
34
+ row("DS1005", "test-of-dead-code", "deny", "deletable"),
35
+ row("DS1006", "deprecated-unused", "deny", "deletable"),
36
+ row("DS1101", "unnecessary-export", "warn", "narrowable"),
37
+ row("DS1102", "unnecessary-exposure", "warn", "narrowable"),
38
+ row("DS1103", "unreachable-export", "deny", "deletable"),
39
+ row("DS1104", "redundant-export-keyword", "warn", "narrowable"),
40
+ row("DS1201", "unused-interface", "deny", "deletable"),
41
+ row("DS1203", "uncalled-interface-method", "warn", "manual"),
42
+ row("DS1204", "unused-satisfaction-assertion", "deny", "deletable"),
43
+ row("DS1301", "write-only-symbol", "deny", "deletable"),
44
+ row("DS1302", "unused-enum-member", "deny", "deletable"),
45
+ row("DS1303", "unused-type-parameter", "deny", "deletable"),
46
+ row("DS1501", "file-never-built", "deny", "deletable"),
47
+ row("DS1502", "file-never-imported", "deny", "deletable"),
48
+ row("DS1601", "unused-dependency", "deny", "manual"),
49
+ row("DS1605", "unused-module-directive", "warn", "deletable"),
50
+ row("DS1701", "suppression-without-reason", "deny", "none"),
51
+ row("DS1702", "unscoped-ignore-entry", "deny", "none"),
52
+ { ...row("DS1703", "stale-suppression", "deny", "none"), fixed: true },
53
+ { ...row("DS1704", "unmatched-root", "deny", "none"), fixed: true },
54
+ row("DS1705", "stale-cross-language-edge", "deny", "none"),
55
+ row("DS1801", "unused-parameter", "warn", "manual"),
56
+ row("DS1802", "unused-receiver", "warn", "deletable"),
57
+ row("DS1803", "unused-result", "warn", "manual"),
58
+ row("DS1805", "unreachable-statement", "deny", "deletable"),
59
+ row("DS1807", "dead-store", "deny", "deletable"),
60
+ row("DS1809", "unreachable-case", "deny", "deletable"),
61
+ ];
62
+
63
+ /** A row at the `certain` ceiling whose severity a configuration may set. */
64
+ function row(
65
+ code: string,
66
+ name: string,
67
+ defaultSeverity: Severity,
68
+ fixability: Fixability,
69
+ ): KindRow {
70
+ return { code, name, defaultSeverity, maxClass: "certain", fixability, fixed: false };
71
+ }
72
+
73
+ /** Every live row of the issue-kind vocabulary, by its code, in ascending code order. */
74
+ export const KINDS: ReadonlyMap<string, KindRow> = new Map(ROWS.map((one) => [one.code, one]));
13
75
 
14
76
  /**
15
77
  * The code of every live issue kind, in ascending order. A severity key names one of
16
78
  * them or a family prefix whose range holds one; a key naming a retired code, an
17
79
  * unassigned code or a range holding no live kind is an unimplemented key, so every
18
- * severity a configuration sets is one some analyzer reports under. A test compares
19
- * this list against the Contract release the analyzer is written against.
80
+ * severity a configuration sets is one some analyzer reports under.
20
81
  */
21
- export const LIVE_CODES: readonly string[] = [
22
- "DS1001",
23
- "DS1002",
24
- "DS1003",
25
- "DS1004",
26
- "DS1005",
27
- "DS1006",
28
- "DS1101",
29
- "DS1102",
30
- "DS1103",
31
- "DS1104",
32
- "DS1201",
33
- "DS1203",
34
- "DS1204",
35
- "DS1301",
36
- "DS1302",
37
- "DS1303",
38
- "DS1501",
39
- "DS1502",
40
- "DS1601",
41
- "DS1605",
42
- "DS1701",
43
- "DS1702",
44
- "DS1703",
45
- "DS1704",
46
- "DS1705",
47
- "DS1801",
48
- "DS1802",
49
- "DS1803",
50
- "DS1805",
51
- "DS1807",
52
- "DS1809",
53
- ];
82
+ export const LIVE_CODES: readonly string[] = ROWS.map((one) => one.code);
83
+
84
+ /** The issue-kind codes whose severity the Contract fixes, in ascending order. */
85
+ export const FIXED_SEVERITY_CODES: readonly string[] = ROWS.filter((one) => one.fixed).map(
86
+ (one) => one.code,
87
+ );