@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
package/src/suppress.ts CHANGED
@@ -7,6 +7,12 @@
7
7
  * withholds every such finding anyway, and stale otherwise.
8
8
  */
9
9
 
10
+ import {
11
+ type CODE,
12
+ KIND_ROWS,
13
+ SYMBOL_SUBJECTS,
14
+ type Subject,
15
+ } from "./contract/contract.generated.ts";
10
16
  import { ConfigError } from "./config.ts";
11
17
  import type { Finding } from "./finding.ts";
12
18
  import { positionKey, type Position } from "./position.ts";
@@ -15,15 +21,6 @@ import { isRef } from "./ref.ts";
15
21
  /** Which document a suppression is written in, spelled as a finding carries it. */
16
22
  export type Mechanism = "inline" | "ignore" | "baseline";
17
23
 
18
- /** The code a suppression that carries no reason is reported under. */
19
- export const SUPPRESSION_WITHOUT_REASON = "DS1701";
20
-
21
- /** The code an ignore entry or a baseline row that names no path is reported under. */
22
- export const UNSCOPED_ENTRY = "DS1702";
23
-
24
- /** The code a suppression that withholds nothing is reported under. */
25
- export const STALE_SUPPRESSION = "DS1703";
26
-
27
24
  /**
28
25
  * One suppression record: one code bound to one site. A directive naming two codes is two
29
26
  * records with one reason, and a directive above a line that declares two symbols is one
@@ -49,7 +46,7 @@ export interface SuppressionRecord {
49
46
  * nothing, and its finding reports the suppression as written.
50
47
  */
51
48
  export interface Refusal {
52
- readonly reported: typeof SUPPRESSION_WITHOUT_REASON | typeof UNSCOPED_ENTRY;
49
+ readonly reported: typeof CODE.suppressionWithoutReason | typeof CODE.unscopedIgnoreEntry;
53
50
  readonly code: string;
54
51
  /** The reference the entry names, empty for a directive. */
55
52
  readonly symbol: string;
@@ -158,48 +155,48 @@ export interface Ledger {
158
155
  readonly withheld: (finding: Finding) => boolean;
159
156
  }
160
157
 
161
- /**
162
- * The subject kinds that are a dependency row, each with the code that reports one. An
163
- * ignore entry or a baseline row naming the row's reference and file withholds its finding
164
- * and marks nothing, and an inline directive never binds one.
165
- */
166
- const DEPENDENCY_SUBJECTS: ReadonlyMap<string, readonly string[]> = new Map([
167
- ["dependency", ["DS1601"]],
168
- ["module-directive", ["DS1605"]],
169
- ]);
158
+ /** The subject of the kind each live code names. */
159
+ const SUBJECT_OF_CODE: ReadonlyMap<string, Subject> = new Map(
160
+ KIND_ROWS.map((row): [string, Subject] => [row.code, row.subject]),
161
+ );
162
+
163
+ /** The subject a finding about one symbol kind is about. */
164
+ function subjectOf(kind: string): Subject | undefined {
165
+ return Object.hasOwn(SYMBOL_SUBJECTS, kind)
166
+ ? SYMBOL_SUBJECTS[kind as keyof typeof SYMBOL_SUBJECTS]
167
+ : undefined;
168
+ }
169
+
170
+ /** Whether a subject is a row of a document rather than a declaration of the program. */
171
+ function rowOf(subject: Subject | undefined): boolean {
172
+ return subject === "file" || subject === "dependency" || subject === "record";
173
+ }
170
174
 
171
175
  /**
172
- * The subject kinds that are a row of a document rather than a declaration of the program,
173
- * each with the codes that report one. Apart from a dependency row, nothing withholds a
174
- * finding about one.
176
+ * Whether a finding's subject is a row of a document rather than a declaration. Apart
177
+ * from a dependency row, nothing withholds a finding about one.
175
178
  */
176
- const ROW_SUBJECTS: ReadonlyMap<string, readonly string[]> = new Map([
177
- ["file", ["DS1501", "DS1502"]],
178
- ...DEPENDENCY_SUBJECTS,
179
- ["root", ["DS1704"]],
180
- ["configured-declaration", ["DS1706"]],
181
- ["suppression", ["DS1701", "DS1702", "DS1703"]],
182
- ["edge", ["DS1705"]],
183
- ]);
184
-
185
- /** Whether a finding's subject is a row of a document rather than a declaration. */
186
179
  export function isRowSubject(kind: string): boolean {
187
- return ROW_SUBJECTS.has(kind);
180
+ return rowOf(subjectOf(kind));
188
181
  }
189
182
 
190
183
  /** Whether a code reports a row of a document. */
191
184
  export function isRowCode(code: string): boolean {
192
- return [...ROW_SUBJECTS.values()].some((codes) => codes.includes(code));
185
+ return rowOf(SUBJECT_OF_CODE.get(code));
193
186
  }
194
187
 
195
- /** Whether a finding's subject is a dependency row. */
188
+ /**
189
+ * Whether a finding's subject is a dependency row. An ignore entry or a baseline row
190
+ * naming the row's reference and file withholds its finding and marks nothing, and an
191
+ * inline directive never binds one.
192
+ */
196
193
  export function isDependencySubject(kind: string): boolean {
197
- return DEPENDENCY_SUBJECTS.has(kind);
194
+ return subjectOf(kind) === "dependency";
198
195
  }
199
196
 
200
197
  /** Whether a code reports a dependency row, which only an ignore entry or a baseline row names. */
201
198
  export function isDependencyCode(code: string): boolean {
202
- return [...DEPENDENCY_SUBJECTS.values()].some((codes) => codes.includes(code));
199
+ return SUBJECT_OF_CODE.get(code) === "dependency";
203
200
  }
204
201
 
205
202
  /** Whether a code reports a row of a document that no record of any mechanism can name. */
@@ -208,33 +205,20 @@ export function isUnsuppressibleCode(code: string): boolean {
208
205
  }
209
206
 
210
207
  /**
211
- * The subject kinds that are a part of a declaration rather than a declaration: a part
208
+ * Whether a finding's subject is a part of the declaration its reference names: a part
212
209
  * has no reference of its own, so a finding about one names the declaration that holds
213
210
  * it, and the part is decided inside that declaration whatever uses it.
214
211
  */
215
- const PART_SUBJECTS: ReadonlySet<string> = new Set([
216
- "parameter",
217
- "receiver",
218
- "result",
219
- "statement",
220
- "case",
221
- "store",
222
- ]);
223
-
224
- /** Whether a finding's subject is a part of the declaration its reference names. */
225
212
  export function isPartSubject(kind: string): boolean {
226
- return PART_SUBJECTS.has(kind);
213
+ return subjectOf(kind) === "part";
227
214
  }
228
215
 
229
- /** The prefix of the codes of the part kinds, the intra-function range. */
230
- const PART_KIND_PREFIX = "DS18";
231
-
232
216
  /**
233
217
  * Whether a code is a part kind, whose record withholds its finding and marks nothing:
234
218
  * the record says the part is wanted, not that the declaration holding it is.
235
219
  */
236
220
  export function isPartKind(code: string): boolean {
237
- return code.startsWith(PART_KIND_PREFIX);
221
+ return SUBJECT_OF_CODE.get(code) === "part";
238
222
  }
239
223
 
240
224
  /** One finding's identity: its code and the position of the thing it names. */
@@ -254,14 +238,14 @@ export function pairs(record: SuppressionRecord, finding: Finding): boolean {
254
238
  if (record.code !== finding.code) {
255
239
  return false;
256
240
  }
257
- if (DEPENDENCY_SUBJECTS.has(finding.symbol.kind)) {
241
+ if (isDependencySubject(finding.symbol.kind)) {
258
242
  return (
259
243
  record.mechanism !== "inline" &&
260
244
  record.symbol === finding.symbol.ref &&
261
245
  record.path === finding.position.path
262
246
  );
263
247
  }
264
- if (ROW_SUBJECTS.has(finding.symbol.kind)) {
248
+ if (isRowSubject(finding.symbol.kind)) {
265
249
  return false;
266
250
  }
267
251
  if (record.bound !== "" && record.bound === positionKey(finding.position)) {
@@ -1,3 +1,9 @@
1
+ import {
2
+ CONTRACT_VERSION,
3
+ EXIT_CLEAN,
4
+ EXIT_FAILURE,
5
+ type Format,
6
+ } from "../contract/contract.generated.ts";
1
7
  import {
2
8
  runAnalysis,
3
9
  runBaselineWrite,
@@ -6,7 +12,7 @@ import {
6
12
  type RunAnalysis,
7
13
  type RunProject,
8
14
  } from "../analysis.ts";
9
- import { ConfigError, kindOf, type Format } from "../config.ts";
15
+ import { ConfigError, kindOf } from "../config.ts";
10
16
  import { CONFORMANCE, DECLARED_GAPS } from "../conformance.ts";
11
17
  import { moduleIdentity } from "../consumers.ts";
12
18
  import { analyzerProvenance, verdictOf } from "../findings-pass.ts";
@@ -20,19 +26,19 @@ import {
20
26
  LANGUAGE,
21
27
  capFindings,
22
28
  sortFindings,
23
- type Report,
24
29
  type ReportConfiguration,
25
30
  type ReportNotBuilt,
26
31
  type ReportNote,
27
32
  } from "../report.ts";
33
+ import type { Report } from "../contract/report.generated.ts";
34
+ import { reportRefusals } from "../report-schema.ts";
28
35
  import { json } from "../reporters/json.ts";
29
36
  import { RENDERINGS } from "../reporters/reporters.ts";
30
37
  import { parseTemplate, TemplateError, type Template } from "../reporters/template.ts";
31
38
  import { resolve } from "../resolve.ts";
32
39
  import { otherSchemeRows, rowReason, writeBaseline, type Recorded } from "../suppress-file.ts";
33
40
  import type { UnseenLoad } from "../unseen-loads.ts";
34
- import { CONTRACT_VERSION } from "../version.ts";
35
- import { EXIT_CLEAN, EXIT_FAILURE, type Verb, type VerbOptions } from "./verb.ts";
41
+ import { type Verb, type VerbOptions } from "./verb.ts";
36
42
 
37
43
  /** The options `analyze` takes beside the ones every verb takes. */
38
44
  export const ANALYZE_OPTIONS: VerbOptions = {
@@ -348,6 +354,12 @@ export const analyzeVerb: Verb = ({ err, host, inputs, scope, openClient, option
348
354
  }
349
355
 
350
356
  try {
357
+ const [refused] = reportRefusals(report);
358
+ if (refused !== undefined) {
359
+ throw new Error(
360
+ `the report breaks the report schema at ${refused.pointer || "the document"}: ${refused.keyword}: ${refused.reason}`,
361
+ );
362
+ }
351
363
  host.writeDocument(documents.report, json(report));
352
364
  const options = {
353
365
  readSource: (path: string) => host.readFile(joinPath(targetRoot, path)),
@@ -1,7 +1,13 @@
1
+ import {
2
+ CONTRACT_VERSION,
3
+ EXIT_CLEAN,
4
+ EXIT_FAILURE,
5
+ EXIT_USAGE,
6
+ SCHEMA_VERSIONS,
7
+ } from "../contract/contract.generated.ts";
1
8
  import { CONFORMANCE, type Conformance } from "../conformance.ts";
2
- import { ANALYZER_NAME, LANGUAGE, SCHEMA_VERSIONS_ACCEPTED } from "../report.ts";
3
- import { CONTRACT_VERSION } from "../version.ts";
4
- import { EXIT_CLEAN, EXIT_FAILURE, EXIT_USAGE, type Verb } from "./verb.ts";
9
+ import { ANALYZER_NAME, LANGUAGE } from "../report.ts";
10
+ import { type Verb } from "./verb.ts";
5
11
 
6
12
  /**
7
13
  * The document `describe` writes: the analyzer, the Contract version it implements, the
@@ -13,7 +19,7 @@ export function describeDocument(version: string, recorded: Conformance | undefi
13
19
  name: ANALYZER_NAME,
14
20
  version,
15
21
  contract_version: CONTRACT_VERSION,
16
- schema_versions_accepted: SCHEMA_VERSIONS_ACCEPTED,
22
+ schema_versions_accepted: SCHEMA_VERSIONS,
17
23
  languages: [LANGUAGE],
18
24
  ...(recorded === undefined
19
25
  ? {}
@@ -1,3 +1,4 @@
1
+ import { EXIT_CLEAN, EXIT_USAGE } from "../contract/contract.generated.ts";
1
2
  import { runAnalysis, type RunAnalysis } from "../analysis.ts";
2
3
  import { listing } from "../components.ts";
3
4
  import type { Cascade } from "../config.ts";
@@ -12,7 +13,7 @@ import { positionKey } from "../position.ts";
12
13
  import type { Reference } from "../references.ts";
13
14
  import { resolve } from "../resolve.ts";
14
15
  import type { Root } from "../roots.ts";
15
- import { EXIT_CLEAN, EXIT_USAGE, type Verb, type VerbOptions } from "./verb.ts";
16
+ import { type Verb, type VerbOptions } from "./verb.ts";
16
17
 
17
18
  /**
18
19
  * The three spellings of an explanation request, each naming the symbol to explain. They are
@@ -1,6 +1,7 @@
1
+ import { EXIT_CLEAN } from "../contract/contract.generated.ts";
1
2
  import { printConfig } from "../print.ts";
2
3
  import { resolve } from "../resolve.ts";
3
- import { EXIT_CLEAN, type Verb } from "./verb.ts";
4
+ import { type Verb } from "./verb.ts";
4
5
 
5
6
  /** Writes the resolved configuration with the source of every setting. */
6
7
  export const printConfigVerb: Verb = ({ out, inputs }) => {
@@ -1,8 +1,9 @@
1
+ import { EXIT_CLEAN } from "../contract/contract.generated.ts";
1
2
  import { runProjects } from "../analysis.ts";
2
3
  import { partialNotes } from "../partial.ts";
3
4
  import { resolvePath } from "../paths.ts";
4
5
  import { resolveUnkinded } from "../resolve.ts";
5
- import { EXIT_CLEAN, type Verb } from "./verb.ts";
6
+ import { type Verb } from "./verb.ts";
6
7
 
7
8
  /**
8
9
  * Writes the name of every project one run analyzes, one per line: the identifier the
@@ -1,9 +1,10 @@
1
+ import { EXIT_CLEAN } from "../contract/contract.generated.ts";
1
2
  import { runSweep } from "../analysis.ts";
2
3
  import { retainedLines } from "../exempt.ts";
3
4
  import { partialNotes } from "../partial.ts";
4
5
  import { resolvePath } from "../paths.ts";
5
6
  import { resolve } from "../resolve.ts";
6
- import { EXIT_CLEAN, type Verb } from "./verb.ts";
7
+ import { type Verb } from "./verb.ts";
7
8
 
8
9
  /**
9
10
  * Writes every declaration an exemption held back in the production sweep a report is
@@ -1,8 +1,9 @@
1
+ import { EXIT_CLEAN, EXIT_FINDINGS } from "../contract/contract.generated.ts";
1
2
  import { runRoots, type RunRoot } from "../analysis.ts";
2
3
  import { partialNotes } from "../partial.ts";
3
4
  import { resolvePath } from "../paths.ts";
4
5
  import { resolve } from "../resolve.ts";
5
- import { EXIT_CLEAN, EXIT_FINDINGS, type Verb } from "./verb.ts";
6
+ import { type Verb } from "./verb.ts";
6
7
 
7
8
  /**
8
9
  * One root as a line: the declaration's reference, why it is a root, and the string
package/src/verbs/verb.ts CHANGED
@@ -4,12 +4,6 @@ import type { Writer } from "../run.ts";
4
4
  import type { Scope } from "../scope.ts";
5
5
  import type { Engine } from "../session.ts";
6
6
 
7
- /** The exit codes the Contract's exit-code table names. */
8
- export const EXIT_CLEAN = 0;
9
- export const EXIT_FINDINGS = 1;
10
- export const EXIT_USAGE = 2;
11
- export const EXIT_FAILURE = 3;
12
-
13
7
  /**
14
8
  * What one verb is handed: the streams it writes, the platform it reads, and the
15
9
  * options that follow its name, read only when the verb asks for what they name, so
@@ -1,5 +1,5 @@
1
- import { CONTRACT_VERSION } from "../version.ts";
2
- import { EXIT_CLEAN, type Verb } from "./verb.ts";
1
+ import { CONTRACT_VERSION, EXIT_CLEAN } from "../contract/contract.generated.ts";
2
+ import { type Verb } from "./verb.ts";
3
3
 
4
4
  /** Writes the version the host reports and the Contract version this analyzer implements. */
5
5
  export const versionVerb: Verb = ({ out, host }) => {
@@ -1,7 +0,0 @@
1
- /**
2
- * The Contract version this analyzer implements. It is the resolved value of
3
- * `contract_version` when no configuration source supplies one, and it moves
4
- * independently of the analyzer's own version, which is the one the host answers
5
- * from the package the analyzer was installed from.
6
- */
7
- export const CONTRACT_VERSION = "8.0.0";
package/src/version.ts DELETED
@@ -1,7 +0,0 @@
1
- /**
2
- * The Contract version this analyzer implements. It is the resolved value of
3
- * `contract_version` when no configuration source supplies one, and it moves
4
- * independently of the analyzer's own version, which is the one the host answers
5
- * from the package the analyzer was installed from.
6
- */
7
- export const CONTRACT_VERSION = "8.0.0";