@cplieger/deadset-ts 3.10.0 → 3.11.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.
package/README.md CHANGED
@@ -8,7 +8,7 @@ deadset-ts is a command-line analyzer for TypeScript and JavaScript projects. It
8
8
 
9
9
  deadset-ts is report-only. It never edits source, and it refuses any `--fix` flag.
10
10
 
11
- **Status: pre-release.** This version implements contract 3.2.0 and ships the analyzer's foundation: one compiler session per run over the projects `analysis.configurations` declares, in the order it lists them, each a compiler configuration file below the target, or, where it declares none, every `tsconfig` project discovered under the target (the explicit scope, every `tsconfig*.json`, each project's `references`), a fail-closed read of every project's diagnostics, configuration decoding against the contract's closed key list with `print-config` and its provenance, `print-projects`, the report-only guard, the symbol inventory of every declaration a project's own files hold down to its class and type members, the one-pass reference resolution over that inventory, which resolves each file's identifiers in batches ([batch-cap calibration](docs/batch-cap-calibration.md) is the measurement behind the batch size), and the root set `print-roots` prints. A root is a declaration the analysis keeps live without a reference: what a file exports when the manifest names it through `main`, `module`, `types`, `bin` or `exports`, or when `ts.entry_files` matches it; a library target's published API; the configuration, setup and test files of Vitest, Stryker and Playwright, the flat configuration of ESLint, and a worker or service worker a call addresses by a string literal; and every declaration a `roots.patterns` entry names. An entry that names nothing is reported as `DS1704` and fails the run. The entry-point conventions of other tools are not read, and `DECLINED_CONVENTIONS` lists each one with the reason. Seven exemption classes hold a declaration back from the sweep, and `print-retained` lists what each one held back: `interface-satisfaction`, a member an interface requires of a class whose value reaches that interface; `decorator`, a decorated member and every member of a decorated class; `injection-container`, a decorated property of a class a dependency-injection container constructs, being a class passed to a call `ts.injection_registrations` names or one a parameter of whose constructor carries a decorator; `framework-lifecycle`, a member a `ts.lifecycle_contracts` entry lists, on a class that is the entry's component by a decorator or a call the entry names or by extending a class it names; `serialization-contract`, the properties of a class whose values reach `JSON.stringify` or a declaration `ts.serializers` names, and those and its `toJSON` and `toString` where a value is passed to an `unknown` or `any` parameter of a function outside the analysis; and, at the lowest confidence, `template-field`, a member an action of a template under `analysis.template_dirs` names, and `reflective-lookup`, a member a literal key of an element access or of `Reflect.get`, `Reflect.set` or `Reflect.has` names. None of them holds back a `#private` member. `analyze` writes the report the Contract's report schema declares, with a text or JSON rendering beside it on request, and `describe` prints the analyzer's description, which states no conformance record, so an orchestrator that requires a conformance pass refuses this analyzer. The analysis is reached through the command, and through `run()` for a caller that runs the command in-process; `explain` exits with a usage message. The one runtime dependency is the `typescript` package at exactly 7.0.2, installed under the `@typescript/native` name until TypeScript 7.1 ships a compiler API the tooling can share.
11
+ **Status: pre-release.** This version implements contract 3.2.0 and ships the analyzer's foundation: one compiler session per run over the projects `analysis.configurations` declares, in the order it lists them, each a compiler configuration file below the target, or, where it declares none, every `tsconfig` project discovered under the target (the explicit scope, every `tsconfig*.json`, each project's `references`), a fail-closed read of every project's diagnostics, configuration decoding against the contract's closed key list with `print-config` and its provenance, `print-projects`, the report-only guard, the symbol inventory of every declaration a project's own files hold down to its class and type members, the one-pass reference resolution over that inventory, which resolves each file's identifiers in batches ([batch-cap calibration](docs/batch-cap-calibration.md) is the measurement behind the batch size), and the root set `print-roots` prints. A root is a declaration the analysis keeps live without a reference: what a file exports when the manifest names it through `main`, `module`, `types`, `bin` or `exports`, or when `ts.entry_files` matches it; a library target's published API; the configuration, setup and test files of Vitest, Stryker and Playwright, the flat configuration of ESLint, and a worker or service worker a call addresses by a string literal; and every declaration a `roots.patterns` entry names. An entry that names nothing is reported as `DS1704` and fails the run. The entry-point conventions of other tools are not read, and `DECLINED_CONVENTIONS` lists each one with the reason. Seven exemption classes hold a declaration back from the sweep, and `print-retained` lists what each one held back: `interface-satisfaction`, a member an interface requires of a class whose value reaches that interface; `decorator`, a decorated member and every member of a decorated class; `injection-container`, a decorated property of a class a dependency-injection container constructs, being a class passed to a call `ts.injection_registrations` names or one a parameter of whose constructor carries a decorator; `framework-lifecycle`, a member a `ts.lifecycle_contracts` entry lists, on a class that is the entry's component by a decorator or a call the entry names or by extending a class it names; `serialization-contract`, the properties of a class whose values reach `JSON.stringify` or a declaration `ts.serializers` names, and those and its `toJSON` and `toString` where a value is passed to an `unknown` or `any` parameter of a function outside the analysis; and, at the lowest confidence, `template-field`, a member an action of a template under `analysis.template_dirs` names, and `reflective-lookup`, a member a literal key of an element access or of `Reflect.get`, `Reflect.set` or `Reflect.has` names. None of them holds back a `#private` member. `analyze` writes the report the Contract's report schema declares, with text, JSON, GitHub annotation, SARIF 2.1.0 and template renderings beside it on request. `explain` answers why one declaration is reported, retained, live or dead, from the same analysis the report is built from. `describe` prints the analyzer's description with its recorded conformance result, which is `fail` for this version because consumer projects are not loaded yet, so an orchestrator that requires a conformance pass refuses this analyzer. The analysis is reached through the command, and through `run()` for a caller that runs the command in-process. The one runtime dependency is the `typescript` package at exactly 7.0.2, installed under the `@typescript/native` name until TypeScript 7.1 ships a compiler API the tooling can share.
12
12
 
13
13
  ## Install
14
14
 
@@ -28,18 +28,18 @@ The npm package carries the command and the library as JavaScript that the compi
28
28
  npx deadset-ts version
29
29
  ```
30
30
 
31
- `version` prints the analyzer version and the Contract version it implements, and exits 0. `print-config`, `print-projects`, `print-roots` and `print-retained` print what their row of the table says and exit 0, or 1 when `print-roots` reports a `DS1704`, and `print-retained` exits 2 when an `exemptions.disabled` entry is not an exemption class or an `analysis.template_dirs` entry is not a directory below the target root. `analyze --report=PATH` writes the JSON report to `PATH` through a temporary file renamed into place, each `--format=text` or `--format=json` rendering at `PATH` with `.txt` or `.json` appended, and with `--baseline-write=FILE` a baseline of every finding; it writes nothing to standard output and exits with the Contract's exit-code table: 0 clean, 1 for a finding at or above `reporters.fail_on` or a stale suppression, 2 for a usage error, 3 for a failed load, and 4 for a pending finding, which outranks 1. `--exit-code=off` exits 0 whatever the verdict and names the verdict on standard error. The report names the target relative to the directory the run is invoked from, so a target outside that directory fails with 3. `describe` writes its JSON document to standard output and exits 0. `explain`, any other invocation and no arguments print the usage line and exit 2.
32
-
33
- | Verb | Job |
34
- | ---------------- | -------------------------------------------------------------------------------- |
35
- | `analyze` | Analyze a project and write the report, its renderings and a baseline |
36
- | `explain` | Explain one finding: the relation, the roots and the references that decided it |
37
- | `print-config` | Print the resolved configuration and where each setting came from |
38
- | `print-projects` | Print the identifier of every project a run analyzes |
39
- | `print-roots` | Print every declaration the analysis keeps live without a reference, and why |
40
- | `print-retained` | Print every declaration an exemption held back: each class, its site and detail |
41
- | `describe` | Describe the analyzer: versions, accepted report schemas, languages, conformance |
42
- | `version` | Print the analyzer and Contract versions |
31
+ `version` prints the analyzer version and the Contract version it implements, and exits 0. `print-config`, `print-projects`, `print-roots` and `print-retained` print what their row of the table says and exit 0, or 1 when `print-roots` reports a `DS1704`, and `print-retained` exits 2 when an `exemptions.disabled` entry is not an exemption class or an `analysis.template_dirs` entry is not a directory below the target root. `analyze --report=PATH` writes the JSON report to `PATH` through a temporary file renamed into place, each `--format` rendering at `PATH` with its suffix appended (`text` as `.txt`, `json` as `.json`, `github` as `.annotations` in GitHub workflow-command form, `sarif` as `.sarif`, and `template` as `.tmpl`, rendering the file `--template=FILE` names, written in a subset of Go's `text/template` action grammar over the report's JSON member names), and with `--baseline-write=FILE` a baseline of every finding; it writes nothing to standard output and exits with the Contract's exit-code table: 0 clean, 1 for a finding at or above `reporters.fail_on` or a stale suppression, 2 for a usage error, including a template that cannot be read or does not parse, 3 for a failed load or a rendering that cannot be written, and 4 for a pending finding, which outranks 1. `--exit-code=off` exits 0 whatever the verdict and names the verdict on standard error. The report names the target relative to the directory the run is invoked from, so a target outside that directory fails with 3. `describe` writes its JSON document to standard output and exits 0. `explain --why=SYM`, or the same request spelled `--why-live=SYM` or `--why-not=SYM`, prints why that one declaration is reported, retained, live or dead, with the shortest reference path from a root for a live one, and exits 0; a request that names no one declaration exits 2 and lists up to 20 declarations it partially matches. Any other invocation and no arguments print the usage line and exit 2.
32
+
33
+ | Verb | Job |
34
+ | ---------------- | ---------------------------------------------------------------------------------- |
35
+ | `analyze` | Analyze a project and write the report, its renderings and a baseline |
36
+ | `explain` | Explain one declaration: the relation, the roots and the references that decide it |
37
+ | `print-config` | Print the resolved configuration and where each setting came from |
38
+ | `print-projects` | Print the identifier of every project a run analyzes |
39
+ | `print-roots` | Print every declaration the analysis keeps live without a reference, and why |
40
+ | `print-retained` | Print every declaration an exemption held back: each class, its site and detail |
41
+ | `describe` | Describe the analyzer: versions, accepted report schemas, languages, conformance |
42
+ | `version` | Print the analyzer and Contract versions |
43
43
 
44
44
  ## Requirements
45
45
 
@@ -304,14 +304,16 @@ function runAnalysisInputs(engine, host, scope, config, provenance, mode, detect
304
304
  })),
305
305
  testFileRules: testFileRulesOf(config.ts.testFiles, read.projects.flatMap((one) => one.configured.testFiles)),
306
306
  },
307
+ configured: read.projects.map((one) => one.configured),
307
308
  };
308
309
  }
309
310
  /**
310
311
  * The analysis of the run the scope and the configuration describe: the run read with its
311
312
  * suppressions bound, every family's findings with the suppressions applied and the
312
- * declared edges evaluated, and the projects and test-file rules the report names.
313
+ * declared edges evaluated, the projects and test-file rules the report names, and the sweep
314
+ * an explanation reads.
313
315
  */
314
316
  export function runAnalysis(engine, host, scope, config, provenance, mode, detectors = DETECTORS) {
315
- const { inputs, run } = runAnalysisInputs(engine, host, scope, config, provenance, mode, detectors);
316
- return { result: findingsPass(inputs), run };
317
+ const { inputs, run, configured } = runAnalysisInputs(engine, host, scope, config, provenance, mode, detectors);
318
+ return { result: findingsPass(inputs), run, swept: inputs.marked.swept, configured };
317
319
  }
@@ -5,6 +5,12 @@ import { CONTRACT_VERSION } from "./version.js";
5
5
  * target supplied the setting it is about.
6
6
  */
7
7
  export const REPOSITORY_DOCUMENT = "deadset.json";
8
+ /** The severities ranked, the strongest highest. */
9
+ const SEVERITY_RANK = { allow: 0, warn: 1, deny: 2 };
10
+ /** Whether a finding at one severity fails a run under the failing severity. */
11
+ export function fails(severity, failOn) {
12
+ return SEVERITY_RANK[severity] >= SEVERITY_RANK[failOn];
13
+ }
8
14
  /**
9
15
  * The configuration every documented default states. `targetKind` is empty: the
10
16
  * field has no default and is never inferred.
@@ -3,14 +3,13 @@
3
3
  * applied and the declared edges evaluated, and the stale suppressions, the totals and the
4
4
  * verdict a report carries beside them.
5
5
  */
6
+ import { fails } from "./config.js";
6
7
  import { decidedFindings } from "./findings/emitters.js";
7
8
  import { fileRefsOf, staleSuppressions, } from "./findings/self-check.js";
8
9
  import { isRowSubject, pairs, suppressionCounts } from "./suppress.js";
9
10
  import { EXIT_CLEAN, EXIT_FINDINGS } from "./verbs/verb.js";
10
11
  /** The exit code of a report holding at least one pending finding. */
11
12
  export const EXIT_PENDING = 4;
12
- /** The severities ranked, the strongest highest. */
13
- const SEVERITY_RANK = { allow: 0, warn: 1, deny: 2 };
14
13
  /** Two strings ordered bytewise. */
15
14
  function compare(a, b) {
16
15
  if (a === b) {
@@ -73,9 +72,8 @@ export function verdictOf(result, config) {
73
72
  if (result.totals.staleSuppressions > 0) {
74
73
  return EXIT_FINDINGS;
75
74
  }
76
- const failing = SEVERITY_RANK[config.reporters.failOn];
77
- const fails = result.findings.some((finding) => SEVERITY_RANK[finding.severity] >= failing);
78
- return fails ? EXIT_FINDINGS : EXIT_CLEAN;
75
+ const failing = result.findings.some((finding) => fails(finding.severity, config.reporters.failOn));
76
+ return failing ? EXIT_FINDINGS : EXIT_CLEAN;
79
77
  }
80
78
  /**
81
79
  * The findings of a report as a baseline records them, in report order. A finding about a
@@ -0,0 +1,36 @@
1
+ import { fails } from "../config.js";
2
+ import { KINDS } from "../kinds.js";
3
+ /** The escapes a workflow command's message needs, so a line break does not end the command. */
4
+ const DATA_ESCAPES = { "%": "%25", "\r": "%0D", "\n": "%0A" };
5
+ /** The escapes a property value needs: a message's, and the property list's two separators. */
6
+ const PROPERTY_ESCAPES = {
7
+ ...DATA_ESCAPES,
8
+ ":": "%3A",
9
+ ",": "%2C",
10
+ };
11
+ function escapeData(value) {
12
+ return value.replace(/[%\r\n]/gu, (char) => DATA_ESCAPES[char] ?? char);
13
+ }
14
+ function escapeProperty(value) {
15
+ return value.replace(/[%\r\n:,]/gu, (char) => PROPERTY_ESCAPES[char] ?? char);
16
+ }
17
+ /** The name of the kind a code names, and the code itself where no live kind carries it. */
18
+ function kindName(code) {
19
+ return KINDS.get(code)?.name ?? code;
20
+ }
21
+ function command(level, at, endLine, title, message) {
22
+ return `::${level} file=${escapeProperty(at.path)},line=${String(at.line)},col=${String(at.column)},endLine=${String(endLine)},title=${escapeProperty(title)}::${escapeData(message)}`;
23
+ }
24
+ /**
25
+ * One workflow annotation per finding, then one per stale suppression, each naming its
26
+ * file, line, column and last line, titled by its code and kind. A finding at or above the
27
+ * failing severity is an error and one below it a warning; a stale suppression is an error,
28
+ * because its kind is fixed on at the failing severity.
29
+ */
30
+ export function annotations(report, options) {
31
+ const lines = report.findings.map((found) => command(fails(found.severity, options.failOn) ? "error" : "warning", found.position, found.position.end_line, `${found.code} ${found.kind}`, found.message));
32
+ for (const stale of report.stale_suppressions) {
33
+ lines.push(command("error", stale.position, stale.position.line, `${stale.code} ${kindName(stale.code)}`, stale.message));
34
+ }
35
+ return lines.map((line) => `${line}\n`).join("");
36
+ }
@@ -0,0 +1,61 @@
1
+ import { sha256Hex } from "./sha256.js";
2
+ /** The unit appended after the last unit of a file. */
3
+ const SENTINEL = 0xffffn;
4
+ /** The number of significant units one line's hash covers. */
5
+ const WINDOW = 100;
6
+ const FACTOR = 37n;
7
+ /** Arithmetic is unsigned 64-bit with wraparound. */
8
+ const MASK = (1n << 64n) - 1n;
9
+ const SPACE = 0x20;
10
+ const TAB = 0x09;
11
+ const CR = 0x0d;
12
+ const LF = 0x0a;
13
+ /**
14
+ * The units of one file the line fingerprint counts: every UTF-16 code unit but a space or
15
+ * a tab, a carriage return read as a line feed and the line feed following it dropped, then
16
+ * the sentinel.
17
+ */
18
+ function significantUnits(text) {
19
+ const kept = [];
20
+ let afterCR = false;
21
+ for (let i = 0; i < text.length; i += 1) {
22
+ const unit = text.charCodeAt(i);
23
+ if (unit === SPACE || unit === TAB || (afterCR && unit === LF)) {
24
+ afterCR = false;
25
+ continue;
26
+ }
27
+ afterCR = unit === CR;
28
+ kept.push(BigInt(afterCR ? LF : unit));
29
+ }
30
+ kept.push(SENTINEL);
31
+ return kept;
32
+ }
33
+ /**
34
+ * The line fingerprint of every line of one file, indexed from zero. The procedure is the one
35
+ * a code-scanning service computes for a result that carries none, followed exactly, because
36
+ * a value that differs from the service's opens a second alert for one finding.
37
+ */
38
+ export function lineHashes(text) {
39
+ const units = significantUnits(text);
40
+ const starts = [0];
41
+ for (let i = 0; i < units.length - 1; i += 1) {
42
+ if (units[i] === BigInt(LF)) {
43
+ starts.push(i + 1);
44
+ }
45
+ }
46
+ const seen = new Map();
47
+ return starts.map((start) => {
48
+ let hash = 0n;
49
+ for (let i = start; i < start + WINDOW; i += 1) {
50
+ hash = (hash * FACTOR + (units[i] ?? 0n)) & MASK;
51
+ }
52
+ const rendered = hash.toString(16);
53
+ const count = (seen.get(rendered) ?? 0) + 1;
54
+ seen.set(rendered, count);
55
+ return `${rendered}:${String(count)}`;
56
+ });
57
+ }
58
+ /** The key a baseline joins on, which a line move leaves unchanged. */
59
+ export function symbolFingerprint(code, ref) {
60
+ return sha256Hex(`${code}\n${ref}`);
61
+ }
@@ -1 +1,7 @@
1
- export {};
1
+ /** A rendering that cannot be written, which ends the run with the failure code. */
2
+ export class RenderError extends Error {
3
+ constructor(message) {
4
+ super(message);
5
+ this.name = "RenderError";
6
+ }
7
+ }
@@ -1,4 +1,8 @@
1
+ import { annotations } from "./annotations.js";
1
2
  import { json } from "./json.js";
3
+ import { RenderError } from "./reporter.js";
4
+ import { sarif } from "./sarif.js";
5
+ import { renderTemplate } from "./template.js";
2
6
  import { text } from "./text.js";
3
7
  /**
4
8
  * Every format this analyzer renders, each by its reporter and its file suffix. A format
@@ -7,4 +11,18 @@ import { text } from "./text.js";
7
11
  export const RENDERINGS = new Map([
8
12
  ["text", { render: text, suffix: ".txt" }],
9
13
  ["json", { render: json, suffix: ".json" }],
14
+ ["github", { render: annotations, suffix: ".annotations" }],
15
+ ["sarif", { render: sarif, suffix: ".sarif" }],
16
+ [
17
+ "template",
18
+ {
19
+ render: (report, options) => {
20
+ if (options.template === undefined) {
21
+ throw new RenderError("the template rendering renders a template, and none was named");
22
+ }
23
+ return renderTemplate(options.template, report);
24
+ },
25
+ suffix: ".tmpl",
26
+ },
27
+ ],
10
28
  ]);
@@ -0,0 +1,178 @@
1
+ /**
2
+ * The text of every live issue kind this analyzer's language carries, which a SARIF rule
3
+ * describes the kind with, written out rather than read at run time. A test compares this
4
+ * table against the Contract release the analyzer is written against.
5
+ */
6
+ /** Every live issue kind whose languages include this analyzer's, by code, in ascending order. */
7
+ export const RULE_TEXTS = new Map([
8
+ [
9
+ "DS1001",
10
+ {
11
+ rule: "An exported symbol with no reference in the target and no reference from any loaded consumer. A symbol referenced only from its own declaration site counts as unreferenced.",
12
+ },
13
+ ],
14
+ [
15
+ "DS1002",
16
+ {
17
+ rule: "An unexported symbol with no reference in the target. A symbol referenced only from its own declaration site counts as unreferenced.",
18
+ },
19
+ ],
20
+ [
21
+ "DS1003",
22
+ {
23
+ rule: "A struct field, class member or type member with no reference, a private member included. A member referenced only from its own declaration site counts as unreferenced.",
24
+ },
25
+ ],
26
+ [
27
+ "DS1004",
28
+ {
29
+ rule: "A symbol with zero production references and at least one test reference: an unused-exported or unused-unexported candidate whose test reference count is not zero, reported once under this code. A reference from a consumer's test files is a test reference unless the configuration counts consumer tests as production.",
30
+ },
31
+ ],
32
+ [
33
+ "DS1005",
34
+ {
35
+ rule: "A test symbol whose set of referenced target symbols is non-empty and every member of that set is reported dead. A test that references at least one live target symbol is never reported, no notion of a test's subject and no name matching enters the rule, and the message states the rule. The test joins the dead component of the symbols it references.",
36
+ },
37
+ ],
38
+ [
39
+ "DS1006",
40
+ {
41
+ rule: "A symbol carrying a deprecation marker and no production reference: an unused-exported, unused-unexported or unused-member candidate whose symbol is deprecated, reported once under this code. Go identifies the marker by the convention the standard tools recognize, TypeScript by the documentation tag its tools recognize.",
42
+ },
43
+ ],
44
+ [
45
+ "DS1101",
46
+ {
47
+ rule: "An exported symbol whose every reference is inside the symbol's own package or module, reported as a candidate for unexporting. The subject is a package-level declaration or a method, and three subjects are excluded: an interface method, whose exportedness is the contract of the interface that declares it; a struct field, which an encoder reads by name; and a method that satisfies an interface some symbol uses as a type, which cannot be unexported without its type ceasing to satisfy that interface. The finding names the narrower visibility the references support. A declared cross-language edge counts as an out-of-package reference; where the edge's other side is unknown to the analyzer the finding is emitted pending.",
48
+ precondition: "Closed world only. Reported always for a main package and an internal/ directory tree, and for a published package only when the configuration declares the consumer set complete and every declared consumer loads. A library with no consumer loaded and no complete consumer set declared gets this finding on its internal/ tree and its main packages and never on its published API.",
49
+ },
50
+ ],
51
+ [
52
+ "DS1103",
53
+ {
54
+ rule: "An unused exported symbol in a package or file no external code can import: an unused-exported candidate whose enclosing package or file is unimportable from outside, reported once under this code at the certain class, because unimportability is a property of the package graph rather than of the consumer set.",
55
+ },
56
+ ],
57
+ [
58
+ "DS1104",
59
+ {
60
+ rule: "An exported declaration used only inside its own file, reported as a candidate for removing the export keyword. The finding names the narrower visibility the references support.",
61
+ precondition: "Closed world only. Reported in a file that is not an entry file and that either belongs to a project whose consumer set the configuration declares complete or is reached by no manifest export. A declared cross-language edge counts as a reference from outside the file; where the edge's other side is unknown to the analyzer the finding is emitted pending.",
62
+ },
63
+ ],
64
+ [
65
+ "DS1201",
66
+ {
67
+ rule: "An interface no symbol uses as a type, counting uses from every loaded module. The finding names the concrete implementations and their positions, and the interface's members join its dead component rather than being reported as independent unused declarations.",
68
+ },
69
+ ],
70
+ [
71
+ "DS1203",
72
+ {
73
+ rule: "An interface method that no call site invokes or selects through the interface, whatever the number of implementations. The finding names the concrete implementations and their positions.",
74
+ precondition: "Exempt: every method of an interface that declares an unexported method, the sum-type shape whose method set exists to restrict the implementors; and every marker method, an interface method whose every implementation carries an empty body.",
75
+ },
76
+ ],
77
+ [
78
+ "DS1301",
79
+ {
80
+ rule: "A package-level or module-level variable, struct field, class member or collection that production code writes and never reads. In production mode a read from a test file counts as no read. A comparison of struct values reads every field of the type: in Go, a value of a struct type used as an operand of `==` or `!=`, as a map key, or as a `switch` tag or `case` expression is a read of every field of that type, transitively through the fields of every struct type it holds, recorded at the comparison, because equality reads every field and a field that decides equality carries information. In TypeScript a comparison of two object references reads no member, because `===` compares identity and never a member. The finding names each write position, so the deletion set is visible.",
81
+ },
82
+ ],
83
+ [
84
+ "DS1302",
85
+ {
86
+ rule: "An enumerated member that no symbol names, at the reachability class derived for the member and with no confidence ceiling below it. Every member of a type that carries a string, text or binary conversion method, or that a conversion from an integer or a wire value produces, is retained by the enum-group exemption first, so a member reached only by value is never a candidate.",
87
+ },
88
+ ],
89
+ [
90
+ "DS1303",
91
+ {
92
+ rule: "A type parameter of a function or method that no part of the declaration's signature and no part of the declaration's body names, at the certain class. The deletion is local to the declaration and the explicit instantiations the reference set already lists.",
93
+ precondition: "Function and method type parameters only. A type parameter of a type declaration is never reported, because a phantom type parameter such as `type ID[T any] int` makes two instantiations distinct types while naming the parameter nowhere, so deleting it changes the program.",
94
+ },
95
+ ],
96
+ [
97
+ "DS1501",
98
+ {
99
+ rule: "A source file no configuration in the build matrix builds. On the Go side the finding names the build constraint that excluded the file. A file whose build constraint is the ignore tag is never reported: the toolchain applies no build constraint to a file named on its own command line, so that tag is the toolchain's convention for a file built by hand. A file any other custom tag excludes is reported under a matrix the configuration declares complete, because completeness is the maintainer's assertion that the listed configurations are every one the target builds, and a configuration the maintainer builds by hand belongs in that list.",
100
+ precondition: 'Reported only when the configuration declares the build matrix complete, and never for a file the toolchain ignored solely because it imports "C" under a build with cgo disabled; such a file is recorded as excluded by cgo rather than as never built.',
101
+ },
102
+ ],
103
+ [
104
+ "DS1502",
105
+ {
106
+ rule: "A source file that no import reaches, that no root names, and in which no declared cross-language edge names a declaration. An edge's side names a declaration as a root names one, so a file holding a declaration an edge names is evaluated with the edge rather than reported: whether that declaration is dead is the edge evaluation's to say, and the merge resolves it against the paired side.",
107
+ },
108
+ ],
109
+ [
110
+ "DS1601",
111
+ {
112
+ rule: "A directly declared dependency that no import in the target needs. On the Go side, a direct require whose module provides no package any target package or test variant imports. On the TypeScript side, a manifest dependency, development dependency or peer dependency the project's import closure does not need. A deletion finding whose fix would remove the last use of a dependency names that dependency.",
113
+ precondition: "On the Go side the rule is the semantics of `go mod tidy -diff` exactly: a requirement marked indirect is never reported, because it exists to pin a transitive version and removing it changes the build list.",
114
+ },
115
+ ],
116
+ [
117
+ "DS1701",
118
+ {
119
+ rule: "A suppression that carries no reason, in any of the three documents: an inline directive, an ignore-file entry or a baseline row. For the ignore file and the baseline, an entry or a row whose reason field is absent, empty or whitespace only is refused and reported rather than matched.",
120
+ },
121
+ ],
122
+ [
123
+ "DS1702",
124
+ {
125
+ rule: "An ignore-file entry or a baseline row that names a symbol and no file path. The entry is reported rather than matched, so a bare name cannot mask a match anywhere else in the project; the inline directive is scoped by its position and cannot be unscoped.",
126
+ },
127
+ ],
128
+ [
129
+ "DS1703",
130
+ {
131
+ rule: "A suppression that matches no current finding, at either mechanism and in the baseline alike. The run exits with the findings code when at least one is reported. The kind is fixed on at deny: no flag, no severity setting and no per-mechanism exception reduces it below a finding, and a configuration naming this code under a severity key is an unimplemented key.",
132
+ },
133
+ ],
134
+ [
135
+ "DS1704",
136
+ {
137
+ rule: "A configured root that matches no symbol, or a configured root pattern that matches no symbol. A root set nobody checks silently changes every result, so a stale one is a finding. The kind is fixed on at deny: no flag, no severity setting and no exception reduces it below a finding, and a configuration naming this code under a severity key is an unimplemented key.",
138
+ },
139
+ ],
140
+ [
141
+ "DS1705",
142
+ {
143
+ rule: "A declared cross-language edge with a side that at least one analyzer evaluated and every evaluation of that side reports absent, so no analyzer that ran enumerates that side's symbol. Emitted by the merge, once per edge, never by an analyzer, so a stale edge is visible rather than silently inert. A dead side paired with an absent side drops its pending finding, and the edge is the reported defect: a misspelled reference must not turn a live pairing into a deletion.",
144
+ },
145
+ ],
146
+ [
147
+ "DS1801",
148
+ {
149
+ rule: "A parameter with no reference inside its function body, on a function whose signature is free to change.",
150
+ precondition: "The signature must be free, defined as follows: the function is not a method retained by interface satisfaction, is not used as a value, is not a go:linkname or cgo target, and is not a stub whose body is empty or only panics. A parameter its body never names is dead whatever the callers, so a published declaration of a library is reported too, with the fixability the vocabulary gives the kind: the signature change is a breaking change.",
151
+ },
152
+ ],
153
+ [
154
+ "DS1803",
155
+ {
156
+ rule: "A result value that no call site of the function uses, on a function whose signature is free to change.",
157
+ precondition: "The same free-signature rule as unused-parameter, plus a closed-world condition: every call site of the function must be in the loaded graph, because an unknown caller may consume the result, so the kind reports only for a function whose callers are all visible.",
158
+ },
159
+ ],
160
+ [
161
+ "DS1805",
162
+ {
163
+ rule: "A statement that control flow cannot reach, at the precision the language's compiler applies to the same construct.",
164
+ },
165
+ ],
166
+ [
167
+ "DS1807",
168
+ {
169
+ rule: "A write to a local variable with no read before the next write to it or the end of its scope, computed on the same read-and-write classification the write-only-symbol kind uses. The finding names the write position.",
170
+ },
171
+ ],
172
+ [
173
+ "DS1809",
174
+ {
175
+ rule: "A switch case whose type or value an earlier case in the same switch already covers.",
176
+ },
177
+ ],
178
+ ]);