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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/dist/src/analysis.js +3 -3
  2. package/dist/src/config.js +1 -1
  3. package/dist/src/conformance-record.js +1 -1
  4. package/dist/src/contract/contract.generated.js +1071 -0
  5. package/dist/src/contract/report.generated.js +796 -0
  6. package/dist/src/convention-rows.js +1 -1
  7. package/dist/src/exempt-classes.js +1 -59
  8. package/dist/src/exempt.js +8 -19
  9. package/dist/src/findings/completion.js +11 -20
  10. package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
  11. package/dist/src/findings/emitters.js +4 -4
  12. package/dist/src/findings/interfaces.js +4 -5
  13. package/dist/src/findings/intra-function.js +8 -7
  14. package/dist/src/findings/non-code-artifacts.js +5 -6
  15. package/dist/src/findings/reads-and-writes.js +9 -8
  16. package/dist/src/findings/self-check.js +7 -10
  17. package/dist/src/findings/unused-declarations.js +28 -27
  18. package/dist/src/findings/visibility-narrowing.js +9 -11
  19. package/dist/src/findings-pass.js +1 -3
  20. package/dist/src/index.js +1 -1
  21. package/dist/src/intra-function-parts.js +8 -13
  22. package/dist/src/kinds.js +4 -360
  23. package/dist/src/report-schema.js +273 -0
  24. package/dist/src/report.js +17 -11
  25. package/dist/src/resolve.js +1 -6
  26. package/dist/src/run.js +2 -1
  27. package/dist/src/setup-failure.js +2 -6
  28. package/dist/src/suppress-file.js +4 -3
  29. package/dist/src/suppress-inline.js +3 -2
  30. package/dist/src/suppress.js +29 -48
  31. package/dist/src/verbs/analyze.js +7 -2
  32. package/dist/src/verbs/describe.js +4 -4
  33. package/dist/src/verbs/explain.js +2 -1
  34. package/dist/src/verbs/print-config.js +2 -1
  35. package/dist/src/verbs/print-projects.js +2 -1
  36. package/dist/src/verbs/print-retained.js +2 -1
  37. package/dist/src/verbs/print-roots.js +2 -1
  38. package/dist/src/verbs/verb.js +1 -5
  39. package/dist/src/verbs/version.js +2 -2
  40. package/jsr.json +1 -1
  41. package/package.json +3 -2
  42. package/src/analysis.ts +3 -3
  43. package/src/config.ts +7 -13
  44. package/src/conformance-record.ts +1 -1
  45. package/src/contract/contract.generated.ts +1123 -0
  46. package/src/contract/report.generated.ts +1157 -0
  47. package/src/convention-rows.ts +1 -1
  48. package/src/exempt-classes.ts +1 -70
  49. package/src/exempt.ts +12 -20
  50. package/src/finding.ts +9 -9
  51. package/src/findings/completion.ts +22 -23
  52. package/src/findings/dependencies-and-module-machinery.ts +4 -7
  53. package/src/findings/emitter.ts +6 -7
  54. package/src/findings/emitters.ts +6 -6
  55. package/src/findings/interfaces.ts +4 -6
  56. package/src/findings/intra-function.ts +8 -8
  57. package/src/findings/non-code-artifacts.ts +5 -7
  58. package/src/findings/reads-and-writes.ts +13 -11
  59. package/src/findings/self-check.ts +8 -15
  60. package/src/findings/unused-declarations.ts +28 -27
  61. package/src/findings/visibility-narrowing.ts +9 -12
  62. package/src/findings-pass.ts +1 -4
  63. package/src/index.ts +1 -1
  64. package/src/intra-function-parts.ts +8 -14
  65. package/src/kinds.ts +6 -406
  66. package/src/report-schema.ts +366 -0
  67. package/src/report.ts +42 -195
  68. package/src/reporters/annotations.ts +2 -2
  69. package/src/reporters/json.ts +1 -1
  70. package/src/reporters/reporter.ts +2 -2
  71. package/src/reporters/reporters.ts +1 -1
  72. package/src/reporters/sarif.ts +13 -8
  73. package/src/reporters/text.ts +3 -3
  74. package/src/resolve.ts +11 -11
  75. package/src/run.ts +2 -1
  76. package/src/setup-failure.ts +5 -7
  77. package/src/suppress-file.ts +3 -4
  78. package/src/suppress-inline.ts +2 -2
  79. package/src/suppress.ts +39 -55
  80. package/src/verbs/analyze.ts +16 -4
  81. package/src/verbs/describe.ts +10 -4
  82. package/src/verbs/explain.ts +2 -1
  83. package/src/verbs/print-config.ts +2 -1
  84. package/src/verbs/print-projects.ts +2 -1
  85. package/src/verbs/print-retained.ts +2 -1
  86. package/src/verbs/print-roots.ts +2 -1
  87. package/src/verbs/verb.ts +0 -6
  88. package/src/verbs/version.ts +2 -2
  89. package/dist/src/version.js +0 -7
  90. package/src/version.ts +0 -7
@@ -1,3 +1,4 @@
1
+ import { CODE } from "../contract/contract.generated.js";
1
2
  import { testsUnseenAPI } from "./completion.js";
2
3
  import { unreachableExports } from "./visibility-narrowing.js";
3
4
  /** The kinds of declaration that are members of a class, an interface, a type or an enum. */
@@ -8,17 +9,17 @@ const MEMBERS = new Set([
8
9
  "enum-member",
9
10
  ]);
10
11
  /**
11
- * The kinds of declaration no code of this family reports: an interface and a method an
12
+ * The kinds of declaration no code of this emitter reports: an interface and a method an
12
13
  * interface declares are the interface kinds' subjects, and a type parameter is the
13
14
  * subject of the unused-type-parameter kind or of none.
14
15
  */
15
- const NOT_THIS_FAMILY = new Set([
16
+ const NOT_REPORTED_HERE = new Set([
16
17
  "file",
17
18
  "interface",
18
19
  "interface-method",
19
20
  "type-parameter",
20
21
  ]);
21
- /** The reader's word for each kind of declaration a finding of the family names. */
22
+ /** The reader's word for each kind of declaration a finding of this emitter names. */
22
23
  const WORDS = {
23
24
  file: "file",
24
25
  function: "function",
@@ -49,9 +50,9 @@ const TEST_OF_DEAD_CODE = "every target symbol this test references is reported
49
50
  function codeOf(judged) {
50
51
  const { candidate, symbol } = judged;
51
52
  if (candidate.testOfDeadCode) {
52
- return judged.testsUnseenAPI ? undefined : "DS1005";
53
+ return judged.testsUnseenAPI ? undefined : CODE.testOfDeadCode;
53
54
  }
54
- if ((judged.test && !candidate.unreferencedTest) || NOT_THIS_FAMILY.has(symbol.kind)) {
55
+ if ((judged.test && !candidate.unreferencedTest) || NOT_REPORTED_HERE.has(symbol.kind)) {
55
56
  return undefined;
56
57
  }
57
58
  const referenced = candidate.productionRefs + candidate.testRefs > 0;
@@ -62,30 +63,30 @@ function codeOf(judged) {
62
63
  return unreferencedCode(symbol);
63
64
  }
64
65
  if (candidate.heldByTests === candidate.id) {
65
- return "DS1004";
66
+ return CODE.testOnlyUse;
66
67
  }
67
68
  if (candidate.productionRefs === 0 && judged.deprecated) {
68
- return "DS1006";
69
+ return CODE.deprecatedUnused;
69
70
  }
70
71
  if (candidate.testRefs > 0 &&
71
72
  (candidate.productionRefs === 0 ||
72
73
  (candidate.relation === "reachability" && candidate.keptByTests))) {
73
- return "DS1004";
74
+ return CODE.testOnlyUse;
74
75
  }
75
76
  if (MEMBERS.has(symbol.kind)) {
76
- return "DS1003";
77
+ return CODE.unusedMember;
77
78
  }
78
79
  if (!symbol.exported) {
79
- return "DS1002";
80
+ return CODE.unusedUnexported;
80
81
  }
81
- return judged.unreachable ? undefined : "DS1001";
82
+ return judged.unreachable ? undefined : CODE.unusedExported;
82
83
  }
83
84
  /** The code a declaration nothing references carries, by its shape and its visibility. */
84
85
  function unreferencedCode(symbol) {
85
86
  if (MEMBERS.has(symbol.kind)) {
86
- return "DS1003";
87
+ return CODE.unusedMember;
87
88
  }
88
- return symbol.exported ? "DS1001" : "DS1002";
89
+ return symbol.exported ? CODE.unusedExported : CODE.unusedUnexported;
89
90
  }
90
91
  /** The visibility a member's message names, where it narrows the member's reach. */
91
92
  function visibilityWord(symbol) {
@@ -108,16 +109,16 @@ function messageOf(code, judged) {
108
109
  const { candidate, symbol } = judged;
109
110
  const word = WORDS[symbol.kind];
110
111
  switch (code) {
111
- case "DS1005":
112
+ case CODE.testOfDeadCode:
112
113
  return TEST_OF_DEAD_CODE;
113
- case "DS1006":
114
+ case CODE.deprecatedUnused:
114
115
  return `deprecated ${word} has no production reference`;
115
- case "DS1004":
116
+ case CODE.testOnlyUse:
116
117
  return candidate.productionRefs === 0
117
118
  ? `${word} is referenced only from test files and never from production code`
118
119
  : `${word} is referenced only from test files and from declarations that are themselves dead`;
119
120
  }
120
- const subject = code === "DS1003"
121
+ const subject = code === CODE.unusedMember
121
122
  ? `${visibilityWord(symbol)}${word}`
122
123
  : symbol.kind === "export-alias"
123
124
  ? word
@@ -125,7 +126,7 @@ function messageOf(code, judged) {
125
126
  if (candidate.relation === "reachability") {
126
127
  return `${subject} is referenced only from declarations that are themselves dead`;
127
128
  }
128
- if (code === "DS1001") {
129
+ if (code === CODE.unusedExported) {
129
130
  return `${subject} has no reference in the target and none from any loaded consumer`;
130
131
  }
131
132
  return `${subject} has no reference in the target`;
@@ -167,14 +168,14 @@ function brandMembers(input) {
167
168
  }
168
169
  /** The kinds of member a brand is: a data member of a type or of a class. */
169
170
  const BRANDS = new Set(["type-member", "class-member"]);
170
- /** The findings of the unused-declarations family, `DS1000` to `DS1099`. */
171
+ /** The findings of the unused-declaration kinds, `DS1001` to `DS1004` and `DS1102` to `DS1104`. */
171
172
  export const unusedDeclarations = (input) => [
172
- ...familyFindings(input, false),
173
+ ...emitterFindings(input, false),
173
174
  ...unusedImports(input),
174
175
  ];
175
176
  /**
176
- * The import bindings their file never reads, under the unexported code. A generated
177
- * file's are the generator's to remove unless the run includes generated files.
177
+ * The import bindings their file never reads. A generated file's are the generator's to
178
+ * remove unless the run includes generated files.
178
179
  */
179
180
  function unusedImports(input) {
180
181
  const generated = input.config.analysis.generatedFiles === "include" ? undefined : input.files.generated;
@@ -182,7 +183,7 @@ function unusedImports(input) {
182
183
  ? []
183
184
  : [
184
185
  {
185
- code: "DS1002",
186
+ code: CODE.unusedImport,
186
187
  position: binding.position,
187
188
  symbol: { ref: binding.ref, kind: "import", name: binding.name, sizeLines: 1 },
188
189
  message: `import binding ${binding.name} is never read in its file`,
@@ -194,9 +195,9 @@ function unusedImports(input) {
194
195
  * a record on such a member is matched against, since the report carries the container's
195
196
  * finding rather than one of the member's own.
196
197
  */
197
- export const fallenMembers = (input) => familyFindings(input, true);
198
- /** The family's findings, or, where `fallen` is set, its dead containers' members' alone. */
199
- function familyFindings(input, fallen) {
198
+ export const fallenMembers = (input) => emitterFindings(input, true);
199
+ /** This emitter's findings, or, where `fallen` is set, its dead containers' members' alone. */
200
+ function emitterFindings(input, fallen) {
200
201
  const union = input.swept.matrix.union;
201
202
  const dead = new Set(input.swept.sweep.candidates.map((candidate) => candidate.id));
202
203
  const unreachable = unreachableExports(input);
@@ -223,7 +224,7 @@ function familyFindings(input, fallen) {
223
224
  };
224
225
  const code = codeOf(judged);
225
226
  if (code === undefined ||
226
- (code === "DS1003" && (brands ??= brandMembers(input)).has(symbol.id))) {
227
+ (code === CODE.unusedMember && (brands ??= brandMembers(input)).has(symbol.id))) {
227
228
  continue;
228
229
  }
229
230
  const endLine = Math.max(symbol.position.line, symbol.endLine);
@@ -1,17 +1,15 @@
1
+ import { CODE } from "../contract/contract.generated.js";
1
2
  import { OUTSIDE } from "../graph.js";
2
3
  import { declarationKey } from "../ref.js";
3
4
  import { runnerCalls } from "../sweep.js";
4
5
  import { writeOnlyDeclarations } from "./reads-and-writes.js";
5
- const UNNECESSARY_EXPORT = "DS1101";
6
- const UNREACHABLE_EXPORT = "DS1103";
7
- const REDUNDANT_EXPORT_KEYWORD = "DS1104";
8
6
  /**
9
7
  * The root kinds by which a consumer importing the package reaches a declaration. A
10
8
  * declaration or a file rooted by any other kind is entered by something that names it
11
9
  * from outside the reference graph: a runtime, a tool, a framework or the configuration.
12
10
  */
13
11
  const PUBLISHING = new Set(["manifest-entry", "published-api"]);
14
- /** The reader's word for each kind of declaration this family reports. */
12
+ /** The reader's word for each kind of declaration this emitter reports. */
15
13
  const WORDS = {
16
14
  function: "function",
17
15
  class: "class",
@@ -109,8 +107,8 @@ function wordOf(symbol) {
109
107
  }
110
108
  /** The visibility each narrowing code names, which only the narrowing codes carry. */
111
109
  const NARROWER = {
112
- [REDUNDANT_EXPORT_KEYWORD]: "file",
113
- [UNNECESSARY_EXPORT]: "package",
110
+ [CODE.redundantExportKeyword]: "file",
111
+ [CODE.unnecessaryExport]: "package",
114
112
  };
115
113
  function findingOf(symbol, code, message) {
116
114
  const { path, line, column } = symbol.position;
@@ -153,12 +151,12 @@ function narrowing(facts, at, symbol, world) {
153
151
  if (facts.published[file] === true && !closed) {
154
152
  return undefined;
155
153
  }
156
- return findingOf(symbol, REDUNDANT_EXPORT_KEYWORD, `exported ${word} is referenced only inside the file that declares it`);
154
+ return findingOf(symbol, CODE.redundantExportKeyword, `exported ${word} is referenced only inside the file that declares it`);
157
155
  case "package":
158
156
  if (!closed || !publishes(facts.rootedBy[at])) {
159
157
  return undefined;
160
158
  }
161
- return findingOf(symbol, UNNECESSARY_EXPORT, `exported ${word} is referenced only inside the package that declares it`);
159
+ return findingOf(symbol, CODE.unnecessaryExport, `exported ${word} is referenced only inside the package that declares it`);
162
160
  default:
163
161
  return undefined;
164
162
  }
@@ -183,10 +181,10 @@ function unreachable(facts, at, symbol, candidate, input) {
183
181
  const found = candidate.relation === "reachability"
184
182
  ? "is referenced only from declarations that are themselves dead"
185
183
  : "has no reference in the target";
186
- return findingOf(symbol, UNREACHABLE_EXPORT, `exported ${wordOf(symbol)} ${found}, and nothing outside can import the file that declares it`);
184
+ return findingOf(symbol, CODE.unreachableExport, `exported ${wordOf(symbol)} ${found}, and nothing outside can import the file that declares it`);
187
185
  }
188
186
  /**
189
- * The visibility-narrowing family over one run: `DS1101`, `DS1103` and `DS1104`. A finding
187
+ * The visibility-narrowing kinds over one run: `DS1501`, `DS1101` and `DS1503`. A finding
190
188
  * about a symbol a declared cross-language edge names is reported here like any other, and
191
189
  * the edge's evaluation then holds it pending, because the edge stands for a reference from
192
190
  * outside the file and the package whose use is decided in the other language. A
@@ -234,5 +232,5 @@ export function unreachableExports(input) {
234
232
  }
235
233
  return found;
236
234
  }
237
- /** The findings of the visibility-narrowing family, `DS1100` to `DS1199`. */
235
+ /** The findings of the visibility-narrowing kinds, `DS1501`, `DS1101` and `DS1503`. */
238
236
  export const visibilityNarrowing = narrowings;
@@ -3,13 +3,11 @@
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 { EXIT_CLEAN, EXIT_FINDINGS, EXIT_PENDING } from "./contract/contract.generated.js";
6
7
  import { fails } from "./config.js";
7
8
  import { decidedFindings } from "./findings/emitters.js";
8
9
  import { fileRefsOf, staleSuppressions, } from "./findings/self-check.js";
9
10
  import { isDependencySubject, isRowSubject, pairs, suppressionCounts, } from "./suppress.js";
10
- import { EXIT_CLEAN, EXIT_FINDINGS } from "./verbs/verb.js";
11
- /** The exit code of a report holding at least one pending finding. */
12
- export const EXIT_PENDING = 4;
13
11
  /** Two strings ordered bytewise. */
14
12
  function compare(a, b) {
15
13
  if (a === b) {
package/dist/src/index.js CHANGED
@@ -2,4 +2,4 @@ export { run, SETTING_OPTIONS } from "./run.js";
2
2
  export { DECLINED_CONVENTIONS } from "./entry-point-gaps.js";
3
3
  export { byPosition, positionKey, PositionError, renderPosition, } from "./position.js";
4
4
  export { computedComponent, isRef, nameComponent, REF_EXPRESSIONS, renderRef, } from "./ref.js";
5
- export { CONTRACT_VERSION } from "./version.js";
5
+ export { CONTRACT_VERSION } from "./contract/contract.generated.js";
@@ -4,6 +4,7 @@
4
4
  * call sites that use or discard it, each statement control flow cannot reach, each
5
5
  * write to a local that no read reaches, and each case an earlier case covers.
6
6
  */
7
+ import { CODE } from "./contract/contract.generated.js";
7
8
  import { isArrowFunction, isAwaitExpression, isBigIntLiteral, isBinaryExpression, isBlock, isBreakStatement, isBindingElement, isCallExpression, isCaseClause, isCatchClause, isClassDeclaration, isClassExpression, isConstructorDeclaration, isContinueStatement, isDecorator, isDefaultClause, isEmptyStatement, isExportAssignment, isExportSpecifier, isExpressionStatement, isFunctionDeclaration, isFunctionExpression, isForInStatement, isForOfStatement, isFunctionLikeDeclaration, isHeritageClause, isIdentifier, isIfStatement, isImportClause, isImportSpecifier, isInterfaceDeclaration, isMethodDeclaration, isModuleDeclaration, isNamespaceImport, isNewExpression, isNoSubstitutionTemplateLiteral, isNumericLiteral, isObjectBindingPattern, isObjectLiteralExpression, isParenthesizedExpression, isPrefixUnaryExpression, isPropertyAssignment, isPropertyAccessExpression, isQualifiedName, isReturnStatement, isShorthandPropertyAssignment, isSourceFile, isStringLiteral, isSwitchStatement, isThrowStatement, isTypeAliasDeclaration, isTypeReferenceNode, isVariableDeclarationList, isVariableStatement, isVoidExpression, NodeFlags, SyntaxKind, } from "@typescript/native/ast";
8
9
  import { SignatureKind } from "@typescript/native/sync";
9
10
  import { exportsLocally, implicitReads, importBindings, } from "./import-bindings.js";
@@ -13,12 +14,6 @@ import { importRef } from "./ref.js";
13
14
  import { isAnswered, UNANSWERED } from "./query.js";
14
15
  import { signatureOf } from "./signatures.js";
15
16
  import { spellsNoBinding } from "./spelled-names.js";
16
- /** The codes of the parts this module reads. */
17
- export const UNUSED_PARAMETER = "DS1801";
18
- export const UNUSED_RESULT = "DS1803";
19
- const UNREACHABLE_STATEMENT = "DS1805";
20
- const DEAD_STORE = "DS1807";
21
- const UNREACHABLE_CASE = "DS1809";
22
17
  /** The result types that carry no value, as the checker spells them. */
23
18
  const NO_VALUE = new Set([
24
19
  "void",
@@ -572,7 +567,7 @@ export function projectParts(project, held, references, targetRoot) {
572
567
  }
573
568
  walk.catches.forEach(({ name, holder }, at) => {
574
569
  parts.push({
575
- code: DEAD_STORE,
570
+ code: CODE.deadStore,
576
571
  declaration: holder,
577
572
  kind: "store",
578
573
  name: name.text,
@@ -584,7 +579,7 @@ export function projectParts(project, held, references, targetRoot) {
584
579
  for (const { names, uses, holder } of loopVariables) {
585
580
  for (const name of names) {
586
581
  parts.push({
587
- code: DEAD_STORE,
582
+ code: CODE.deadStore,
588
583
  declaration: holder,
589
584
  kind: "store",
590
585
  name: name.text,
@@ -602,7 +597,7 @@ export function projectParts(project, held, references, targetRoot) {
602
597
  });
603
598
  for (const { statement, holder } of walk.unreachable) {
604
599
  parts.push({
605
- code: UNREACHABLE_STATEMENT,
600
+ code: CODE.unreachableStatement,
606
601
  declaration: holder,
607
602
  kind: "statement",
608
603
  name: "statement",
@@ -613,7 +608,7 @@ export function projectParts(project, held, references, targetRoot) {
613
608
  }
614
609
  for (const { clause, text, holder } of walk.cases) {
615
610
  parts.push({
616
- code: UNREACHABLE_CASE,
611
+ code: CODE.unreachableCase,
617
612
  declaration: holder,
618
613
  kind: "case",
619
614
  name: text,
@@ -826,7 +821,7 @@ function parameterParts(file, root, declaration, signature, reader, ids, place)
826
821
  return;
827
822
  }
828
823
  const part = {
829
- code: UNUSED_PARAMETER,
824
+ code: CODE.unusedParameter,
830
825
  declaration,
831
826
  kind: "parameter",
832
827
  name: name.text,
@@ -964,7 +959,7 @@ function isPlainWrite(use) {
964
959
  function deadStores(file, root, block, holder, locals, ids) {
965
960
  const found = [];
966
961
  const deadAt = (local, target) => ({
967
- code: DEAD_STORE,
962
+ code: CODE.deadStore,
968
963
  declaration: holder,
969
964
  kind: "store",
970
965
  name: local.name.text,
@@ -1080,7 +1075,7 @@ function resultParts(project, root, sites, references, calls) {
1080
1075
  const valued = project.queries.nonNullable(result);
1081
1076
  const members = isAnswered(valued) ? project.queries.propertiesOf(valued) : UNANSWERED;
1082
1077
  found.push({
1083
- code: UNUSED_RESULT,
1078
+ code: CODE.unusedResult,
1084
1079
  declaration: id,
1085
1080
  kind: "result",
1086
1081
  name: "result",