@cplieger/deadset-ts 8.0.0-dev.17 → 8.0.0-dev.18

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 (60) hide show
  1. package/dist/src/analysis.js +26 -71
  2. package/dist/src/augmentations.js +1 -1
  3. package/dist/src/calls.js +267 -48
  4. package/dist/src/conformance-record.js +1 -1
  5. package/dist/src/contract/contract.generated.js +2 -2
  6. package/dist/src/exempt.js +0 -26
  7. package/dist/src/exposure.js +400 -0
  8. package/dist/src/findings/completion.js +4 -17
  9. package/dist/src/findings/emitters.js +22 -33
  10. package/dist/src/findings/intra-function.js +3 -3
  11. package/dist/src/findings/reads-and-writes.js +71 -15
  12. package/dist/src/findings/unused-declarations.js +2 -22
  13. package/dist/src/findings/visibility-narrowing.js +14 -20
  14. package/dist/src/inventory.js +18 -0
  15. package/dist/src/{components.js → liveness/components.js} +1 -1
  16. package/dist/src/{graph.js → liveness/graph.js} +38 -21
  17. package/dist/src/liveness/matrix.js +99 -0
  18. package/dist/src/liveness/sweep.js +558 -0
  19. package/dist/src/query.js +46 -0
  20. package/dist/src/references/sink.js +87 -1
  21. package/dist/src/references.js +1 -1
  22. package/dist/src/roots/published-reach.js +4 -4
  23. package/dist/src/verbs/explain.js +73 -152
  24. package/dist/src/verbs/print-retained.js +1 -1
  25. package/jsr.json +1 -1
  26. package/package.json +1 -1
  27. package/src/analysis.ts +41 -107
  28. package/src/augmentations.ts +1 -1
  29. package/src/calls.ts +326 -52
  30. package/src/conformance-record.ts +1 -1
  31. package/src/contract/contract.generated.ts +2 -2
  32. package/src/exempt.ts +31 -46
  33. package/src/exposure.ts +492 -0
  34. package/src/finding.ts +1 -1
  35. package/src/findings/boundary.ts +7 -0
  36. package/src/findings/completion.ts +5 -19
  37. package/src/findings/emitters.ts +23 -41
  38. package/src/findings/interfaces.ts +3 -3
  39. package/src/findings/intra-function.ts +3 -3
  40. package/src/findings/reads-and-writes.ts +99 -26
  41. package/src/findings/unused-declarations.ts +4 -28
  42. package/src/findings/visibility-narrowing.ts +25 -26
  43. package/src/interface-satisfaction.ts +1 -1
  44. package/src/inventory.ts +19 -0
  45. package/src/{components.ts → liveness/components.ts} +3 -3
  46. package/src/{graph.ts → liveness/graph.ts} +74 -26
  47. package/src/liveness/matrix.ts +162 -0
  48. package/src/liveness/sweep.ts +855 -0
  49. package/src/query.ts +81 -0
  50. package/src/references/sink.ts +101 -0
  51. package/src/references.ts +8 -1
  52. package/src/roots/published-reach.ts +9 -4
  53. package/src/stores.ts +2 -1
  54. package/src/tests/file-class.ts +19 -2
  55. package/src/verbs/explain.ts +81 -199
  56. package/src/verbs/print-retained.ts +1 -1
  57. package/dist/src/matrix.js +0 -275
  58. package/dist/src/sweep.js +0 -234
  59. package/src/matrix.ts +0 -466
  60. package/src/sweep.ts +0 -384
@@ -13,12 +13,15 @@ export class Sink {
13
13
  #testFile;
14
14
  #consumer;
15
15
  #found = [];
16
+ #declaredAt;
16
17
  #valued = new Map();
18
+ #named = new Map();
17
19
  #held = new Set();
18
- constructor(root, testFile, consumer) {
20
+ constructor(root, testFile, consumer, declaredAt) {
19
21
  this.#root = root;
20
22
  this.#testFile = testFile;
21
23
  this.#consumer = consumer;
24
+ this.#declaredAt = declaredAt;
22
25
  }
23
26
  /** Whether a test file wrote one walked file's uses. */
24
27
  isTest(file) {
@@ -47,6 +50,11 @@ export class Sink {
47
50
  test,
48
51
  ...(target.guessed === true ? { guessed: true } : {}),
49
52
  });
53
+ if (this.#consumer === undefined && !("column" in at)) {
54
+ const named = this.#named.get(file) ?? [];
55
+ named.push({ index: this.#found.length - 1, at, annotated: this.#annotates(at, from) });
56
+ this.#named.set(file, named);
57
+ }
50
58
  if (use === "write" && value !== undefined) {
51
59
  const valued = this.#valued.get(file) ?? [];
52
60
  valued.push({ index: this.#found.length - 1, value });
@@ -54,6 +62,26 @@ export class Sink {
54
62
  }
55
63
  }
56
64
  }
65
+ /**
66
+ * Whether a name sits in the annotation of `from`'s own declaration. An annotation holds
67
+ * no block, so the walk stops at the first declaration or block above the name.
68
+ */
69
+ #annotates(name, from) {
70
+ if (name.kind === SyntaxKind.SourceFile) {
71
+ return false;
72
+ }
73
+ let child = name;
74
+ let parent = name.parent;
75
+ while (parent.kind !== SyntaxKind.Block && parent.kind !== SyntaxKind.SourceFile) {
76
+ const id = this.#declaredAt(parent);
77
+ if (id !== undefined) {
78
+ return id === from && parent.type === child;
79
+ }
80
+ child = parent;
81
+ parent = parent.parent;
82
+ }
83
+ return false;
84
+ }
57
85
  /** Holds declarations an unanswered question could have made live, and the one whose read asked it. */
58
86
  hold(ids) {
59
87
  for (const id of ids) {
@@ -83,6 +111,14 @@ export class Sink {
83
111
  }
84
112
  }
85
113
  }
114
+ for (const [file, named] of this.#named) {
115
+ for (const { index, carriers } of carriersIn(named, this.#valued.get(file) ?? [], found)) {
116
+ const reference = found[index];
117
+ if (reference !== undefined) {
118
+ found[index] = { ...reference, carriers };
119
+ }
120
+ }
121
+ }
86
122
  found.sort((a, b) => byPosition(a.position, b.position) ||
87
123
  bytewise(a.to, b.to) ||
88
124
  bytewise(a.from, b.from) ||
@@ -149,3 +185,53 @@ function aloneIn(file, uses, writes, found) {
149
185
  }
150
186
  return result;
151
187
  }
188
+ /**
189
+ * The carriers of each named record of one file: `from` where its own annotation holds the
190
+ * name, then the target of each write whose stored value holds it and that names another
191
+ * declaration, the innermost value first. Values nest or are disjoint.
192
+ */
193
+ function carriersIn(named, writes, found) {
194
+ const byStart = [...named].sort((a, b) => a.at.pos - b.at.pos);
195
+ const holding = new Map();
196
+ for (const write of writes) {
197
+ let low = 0;
198
+ let high = byStart.length;
199
+ while (low < high) {
200
+ const middle = (low + high) >>> 1;
201
+ if ((byStart[middle]?.at.pos ?? 0) < write.value.pos) {
202
+ low = middle + 1;
203
+ }
204
+ else {
205
+ high = middle;
206
+ }
207
+ }
208
+ for (let at = low; at < byStart.length; at += 1) {
209
+ const one = byStart[at];
210
+ if (one === undefined || one.at.pos >= write.value.end) {
211
+ break;
212
+ }
213
+ if (one.at.end <= write.value.end) {
214
+ holding.set(one.index, [...(holding.get(one.index) ?? []), write]);
215
+ }
216
+ }
217
+ }
218
+ const result = [];
219
+ for (const one of named) {
220
+ const reference = found[one.index];
221
+ if (reference === undefined) {
222
+ continue;
223
+ }
224
+ const carriers = one.annotated ? [reference.from] : [];
225
+ const values = [...(holding.get(one.index) ?? [])].sort((a, b) => a.value.end - a.value.pos - (b.value.end - b.value.pos));
226
+ for (const write of values) {
227
+ const to = found[write.index]?.to;
228
+ if (to !== undefined && to !== reference.to && !carriers.includes(to)) {
229
+ carriers.push(to);
230
+ }
231
+ }
232
+ if (carriers.length > 0) {
233
+ result.push({ index: one.index, carriers });
234
+ }
235
+ }
236
+ return result;
237
+ }
@@ -49,10 +49,10 @@ export function references(project, held, targetRoot, options) {
49
49
  .ownSourceFiles()
50
50
  .filter((file) => relativePath(consumer.root, file.fileName) !== undefined &&
51
51
  relativePath(targetRoot, file.fileName) === undefined);
52
- const sink = new Sink(consumer?.root ?? targetRoot, options.testFile, consumer);
53
52
  const ownPaths = project.ownPaths();
54
53
  const fileIds = new Set(held.symbols.filter((symbol) => symbol.kind === "file").map((symbol) => symbol.id));
55
54
  const declaredAt = (node) => declarations.get(nodeKey(node.getSourceFile(), node));
55
+ const sink = new Sink(consumer?.root ?? targetRoot, options.testFile, consumer, declaredAt);
56
56
  let moduleBodies;
57
57
  /**
58
58
  * The declarations one module holds directly: a file's top level, or the body of a
@@ -182,7 +182,7 @@ function declarationNodes(held, byPath) {
182
182
  return {
183
183
  typeNamesOf: (symbol) => {
184
184
  const { byId, nodes } = nodesIn(symbol.position.path);
185
- return (byId.get(symbol.id) ?? []).flatMap((declaration) => typeNamesOf(declaration, nodes));
185
+ return (byId.get(symbol.id) ?? []).flatMap((declaration) => typeNamesOf(declaration, (nested) => nodes.has(nested)));
186
186
  },
187
187
  nodesOf: (symbol) => nodesIn(symbol.position.path).byId.get(symbol.id) ?? [],
188
188
  };
@@ -369,12 +369,12 @@ function inferredTypeSymbols(project, declarations) {
369
369
  /**
370
370
  * The names of types one declaration's own syntax writes: in a type reference, a
371
371
  * heritage clause or a type query. A function body is not read, and neither is a
372
- * declaration nested in this one, which `nested` holds and which is read on its own.
372
+ * declaration nested in this one that `readAlone` says is read on its own.
373
373
  */
374
- function typeNamesOf(declaration, nested) {
374
+ export function typeNamesOf(declaration, readAlone) {
375
375
  const found = [];
376
376
  const visit = (node) => {
377
- if (node !== declaration && nested.has(node)) {
377
+ if (node !== declaration && readAlone(node)) {
378
378
  return;
379
379
  }
380
380
  if (isTypeReferenceNode(node)) {
@@ -1,12 +1,11 @@
1
1
  import { EXIT_CLEAN, EXIT_USAGE } from "../contract/contract.generated.js";
2
2
  import { productionMode, runAnalysis } from "../analysis.js";
3
- import { listing } from "../components.js";
4
- import { OUTSIDE, swept as judged } from "../graph.js";
5
- import { referenceKey } from "../matrix.js";
3
+ import { listing } from "../liveness/components.js";
4
+ import { swept as judged } from "../liveness/graph.js";
6
5
  import { partialNotes } from "../partial.js";
7
6
  import { resolvePath } from "../paths.js";
8
7
  import { positionKey } from "../position.js";
9
- import { madeByTestCode, withheldBy } from "../tests/file-class.js";
8
+ import { madeByTestCode } from "../tests/file-class.js";
10
9
  import { resolve } from "../resolve.js";
11
10
  import {} from "./verb.js";
12
11
  import { UsageError } from "../usage.js";
@@ -80,172 +79,94 @@ function subjectOf(symbols, named) {
80
79
  .sort(bytewise);
81
80
  return { candidates: partial.slice(0, MAX_PARTIAL_MATCHES) };
82
81
  }
83
- /** Every reference of the run once, a reference several configurations make counted once. */
84
- function referencesOf(configured) {
85
- const seen = new Set();
86
- const held = [];
87
- for (const reference of configured.flatMap((one) => one.references)) {
88
- const key = referenceKey(reference);
89
- if (!seen.has(key)) {
90
- seen.add(key);
91
- held.push(reference);
92
- }
93
- }
94
- return held;
95
- }
96
- /** Every root of the run once per reason, in the order the projects name them. */
97
- function rootsOf(configured) {
98
- const seen = new Set();
99
- return configured
100
- .flatMap((one) => one.roots)
101
- .filter((root) => {
102
- const key = [root.id, root.kind, root.source].join("\u0000");
103
- if (seen.has(key)) {
104
- return false;
105
- }
106
- seen.add(key);
107
- return true;
108
- });
109
- }
110
- /**
111
- * The shortest path from a root, a loaded consumer or a test file no project holds to one
112
- * declaration, walked breadth first from every root at once in the order the run names them,
113
- * a reference of a consumer or of such a file being the first hop of a path of its own, as
114
- * the sweep seeds them, and following the references the sweep follows: a
115
- * reference that reads a module reaches what the module exports, and an evaluation or a root
116
- * reaches the module alone. Under `production` no reference a test file made is followed,
117
- * which is the set the report's sweep counts.
118
- */
119
- function shortestPath(analysis, to, mode, support) {
120
- const graph = analysis.swept.matrix.union;
121
- const out = new Map();
122
- const consumed = [];
123
- for (const reference of referencesOf(analysis.configured)) {
124
- if (withheldBy(mode, reference, support)) {
125
- continue;
126
- }
127
- const { consumer } = reference;
128
- if (consumer !== undefined) {
129
- consumed.push({ reference, origin: { k: "consumer", consumer } });
130
- continue;
131
- }
132
- if (reference.unheld === true) {
133
- consumed.push({
134
- reference,
135
- origin: { k: "unheld", file: reference.from.replace(/#$/u, "") },
136
- });
137
- continue;
138
- }
139
- const held = out.get(reference.from);
140
- if (held === undefined) {
141
- out.set(reference.from, [reference]);
142
- }
143
- else {
144
- held.push(reference);
145
- }
146
- }
147
- const steps = [];
148
- const seen = new Set();
149
- const expanded = new Set();
150
- const enter = (step) => {
151
- if (!seen.has(step.at)) {
152
- seen.add(step.at);
153
- steps.push(step);
154
- }
155
- };
156
- const follow = (reference, previous, origin) => {
157
- enter({ at: reference.to, hop: { k: "reference", reference }, previous, origin });
158
- const target = graph.at(reference.to);
159
- if (reference.use === "evaluation" || target === OUTSIDE || expanded.has(reference.to)) {
160
- return;
161
- }
162
- expanded.add(reference.to);
163
- for (const exported of graph.exportsOf[target] ?? []) {
164
- const id = graph.symbols[exported]?.id;
165
- if (id !== undefined) {
166
- enter({ at: id, hop: { k: "export", reference, to: id }, previous, origin });
167
- }
168
- }
169
- };
170
- for (const root of rootsOf(analysis.configured)) {
171
- enter({ at: root.id, hop: undefined, previous: -1, origin: { k: "root", root } });
172
- }
173
- // A consumed reference is one hop long, so it joins the walk where the roots' first hops do.
174
- const firstHops = steps.length;
175
- for (let head = 0;; head += 1) {
176
- if (head === firstHops) {
177
- for (const { reference, origin } of consumed) {
178
- follow(reference, -1, origin);
179
- }
180
- }
181
- const step = steps[head];
182
- if (step === undefined) {
183
- break;
184
- }
185
- if (step.at === to) {
186
- const path = [];
187
- for (let at = step; at?.hop !== undefined; at = steps[at.previous]) {
188
- path.unshift(at.hop);
189
- }
190
- return { origin: step.origin, path };
191
- }
192
- for (const reference of out.get(step.at) ?? []) {
193
- follow(reference, head, step.origin);
194
- }
195
- }
196
- return undefined;
197
- }
198
82
  /** Where a path starts, as a line names it. */
199
- function originText(origin) {
83
+ function originText(origin, graph) {
84
+ const id = (at) => graph.symbols[at]?.id ?? "";
200
85
  switch (origin.k) {
201
86
  case "root":
202
- return `root ${origin.root.kind} ${origin.root.id}`;
203
- case "consumer":
204
- return `consumer ${origin.consumer}`;
205
- case "unheld":
206
- return `test file no project holds ${origin.file}`;
87
+ return `root ${origin.rooted.kind} ${id(origin.rooted.at)}`;
88
+ case "consumed": {
89
+ const reference = graph.references[origin.edge.reference];
90
+ if (reference?.consumer !== undefined) {
91
+ return `consumer ${reference.consumer}`;
92
+ }
93
+ return reference?.unheld === true
94
+ ? `test file no project holds ${reference.from.replace(/#$/u, "")}`
95
+ : `declaration no configuration judges ${reference?.from ?? ""}`;
96
+ }
97
+ case "exemption":
98
+ return `exemption ${origin.record.class} ${positionKey(origin.record.site)}`;
99
+ case "mark":
100
+ return `suppression of ${id(origin.at)}`;
101
+ case "unanswered":
102
+ return `unanswered question about ${id(origin.at)}`;
207
103
  }
208
104
  }
209
105
  /** What made a reference: a declaration of the target, or a loaded consumer. */
210
106
  function madeBy(reference) {
211
107
  return reference.consumer === undefined ? reference.from : `consumer ${reference.consumer}`;
212
108
  }
213
- /** One reference of an answer, marked where test code made it, a test-support file's included. */
214
- function referenceLine(reference, tail, support) {
215
- return ` reference: ${positionKey(reference.position)}\t${reference.use}\t${tail}${madeByTestCode(reference, support) ? "\tfrom a test file" : ""}\n`;
109
+ /**
110
+ * One reference of an answer, marked where test code made it, a test-support file's included,
111
+ * and naming the configurations that make it where they are not every one of the run.
112
+ */
113
+ function referenceLine(reference, tail, support, configurations) {
114
+ const partial = configurations.making.length < configurations.run.length;
115
+ return ` reference: ${positionKey(reference.position)}\t${reference.use}\t${tail}${madeByTestCode(reference, support) ? "\tfrom a test file" : ""}${partial ? `\tmade in ${configurations.making.join(" ")}` : ""}\n`;
216
116
  }
217
117
  /** Why a declaration no finding names and no exemption held back is live, or how it fell. */
218
- function liveness(analysis, subject, cascade, mode) {
219
- const { sweep } = analysis.swept;
118
+ function liveness(analysis, subject, cascade) {
119
+ const { matrix, sweep } = analysis.swept;
120
+ const graph = matrix.union;
220
121
  const candidate = sweep.candidates.find((one) => one.id === subject.id);
221
122
  if (candidate === undefined && !judged(subject.kind)) {
222
123
  return `answer: unjudged\n unjudged: neither liveness relation judges a ${subject.kind}\n`;
223
124
  }
224
125
  let text = `answer: ${candidate === undefined ? "live" : "dead"}\n`;
225
126
  text += ` live under: ${listed(sweep.liveUnder.get(subject.id) ?? [])}\n`;
226
- for (const root of rootsOf(analysis.configured).filter((one) => one.id === subject.id)) {
227
- text += ` root: ${root.kind}${root.source === "" ? "" : `\tnamed by ${root.source}`}\n`;
127
+ const at = graph.at(subject.id);
128
+ const seen = new Set();
129
+ for (const root of graph.rooted.filter((one) => one.at === at)) {
130
+ const key = `${root.kind}\u0000${root.source}`;
131
+ if (!seen.has(key)) {
132
+ seen.add(key);
133
+ text += ` root: ${root.kind}${root.source === "" ? "" : `\tnamed by ${root.source}`}\n`;
134
+ }
228
135
  }
229
136
  const support = new Set(analysis.configured.flatMap((one) => one.supportFiles ?? []));
230
- const found = shortestPath(analysis, subject.id, mode, support) ??
231
- shortestPath(analysis, subject.id, { production: false }, support);
232
- if (found !== undefined && found.path.length > 0) {
233
- const { origin } = found;
234
- text += ` reached from: ${originText(origin)}\n`;
235
- for (const hop of found.path) {
236
- text += referenceLine(hop.reference, `${madeBy(hop.reference)} -> ${hop.reference.to}`, support);
137
+ const line = (edge, tail) => {
138
+ const reference = graph.references[edge.reference];
139
+ return reference === undefined
140
+ ? ""
141
+ : referenceLine(reference, tail(reference), support, {
142
+ making: graph.configurations[edge.reference] ?? [],
143
+ run: matrix.configurations,
144
+ });
145
+ };
146
+ const found = sweep.path(subject.id);
147
+ if (found !== undefined && found.hops.length > 0) {
148
+ text += ` reached from: ${originText(found.origin, graph)}\n`;
149
+ for (const hop of found.hops) {
150
+ text += line(hop.edge, (reference) => `${madeBy(reference)} -> ${reference.to}`);
237
151
  if (hop.k === "export") {
238
- text += ` export: ${hop.reference.to} -> ${hop.to}\n`;
152
+ const reference = graph.references[hop.edge.reference];
153
+ text += ` export: ${reference?.to ?? ""} -> ${graph.symbols[hop.to]?.id ?? ""}\n`;
239
154
  }
240
155
  }
241
156
  }
242
157
  else if (found === undefined) {
243
- const references = referencesOf(analysis.configured).filter((one) => one.to === subject.id);
158
+ const references = graph.references.flatMap((one, place) => one.to === subject.id ? [place] : []);
244
159
  text +=
245
160
  " unreachable: no path of references reaches it from a root, a loaded consumer or a test file no project holds\n";
246
161
  text += ` references: ${String(references.length)}\n`;
247
- for (const reference of references) {
248
- text += referenceLine(reference, `from ${madeBy(reference)}`, support);
162
+ for (const place of references) {
163
+ const reference = graph.references[place];
164
+ if (reference !== undefined) {
165
+ text += referenceLine(reference, `from ${madeBy(reference)}`, support, {
166
+ making: graph.configurations[place] ?? [],
167
+ run: matrix.configurations,
168
+ });
169
+ }
249
170
  }
250
171
  }
251
172
  if (candidate !== undefined) {
@@ -294,7 +215,7 @@ function namedElsewhere(findings, subject) {
294
215
  .join("");
295
216
  }
296
217
  /** The one explanation that applies to a declaration, built whole before it is written. */
297
- function explanation(analysis, subject, cascade, mode) {
218
+ function explanation(analysis, subject, cascade) {
298
219
  const graph = analysis.swept.matrix.union;
299
220
  const at = graph.at(subject.id);
300
221
  let text = `symbol: ${subject.ref}\n`;
@@ -302,7 +223,7 @@ function explanation(analysis, subject, cascade, mode) {
302
223
  text += `position: ${subject.id}\n`;
303
224
  text += `configurations: ${listed(analysis.swept.matrix.heldIn[at] ?? [])}\n`;
304
225
  const found = analysis.result.findings.find((one) => positionKey(one.position) === subject.id);
305
- const retained = analysis.swept.retained.filter((one) => one.id === subject.id);
226
+ const retained = analysis.swept.sweep.retained.filter((one) => one.id === subject.id);
306
227
  if (found !== undefined) {
307
228
  text += reported(found);
308
229
  }
@@ -312,22 +233,22 @@ function explanation(analysis, subject, cascade, mode) {
312
233
  text += ` class: ${one.class}\t${positionKey(one.site)}\t${one.detail}\n`;
313
234
  }
314
235
  }
315
- else if (analysis.swept.heldByUnanswered.includes(subject.id)) {
236
+ else if (analysis.swept.sweep.held.includes(subject.id)) {
316
237
  text +=
317
238
  "answer: held\n held by: a question the checker did not answer, which could have kept it live, so no finding names it\n";
318
239
  }
319
240
  else {
320
- text += liveness(analysis, subject, cascade, mode);
241
+ text += liveness(analysis, subject, cascade);
321
242
  }
322
243
  return text + namedElsewhere(analysis.result.findings, subject);
323
244
  }
324
245
  /**
325
246
  * Explains one symbol from the analysis the report is built from: why a reported symbol is
326
247
  * reported, which exemption classes held a retained one back, that an unanswered question held
327
- * a held one, and for any other declaration the relations that hold it live and the shortest
328
- * path from a root, a loaded consumer or a test file no project holds, or the references it
329
- * has where no path reaches it. A request naming no one declaration exits with the usage
330
- * code and names what partially matches it. Where the analysis was partial, the error stream says so.
248
+ * a held one, and for any other declaration the relations that hold it live and the path the
249
+ * sweep's strongest tier reaching it follows, or the references it has where no path reaches
250
+ * it. A request naming no one declaration exits with the usage code and names what partially
251
+ * matches it. Where the analysis was partial, the error stream says so.
331
252
  */
332
253
  export const explainVerb = ({ out, err, host, inputs, scope, openClient, option }) => {
333
254
  const named = namedSymbol(option);
@@ -351,7 +272,7 @@ export const explainVerb = ({ out, err, host, inputs, scope, openClient, option
351
272
  }
352
273
  }
353
274
  else {
354
- out.write(explanation(analysis, subject, config.reporters.cascade, mode));
275
+ out.write(explanation(analysis, subject, config.reporters.cascade));
355
276
  }
356
277
  for (const note of notes) {
357
278
  diagnose(err, note);
@@ -20,7 +20,7 @@ export const printRetainedVerb = ({ out, err, host, inputs, scope, openClient })
20
20
  marked: [],
21
21
  mode: productionMode(config),
22
22
  });
23
- for (const line of retainedLines(swept.matrix.union.symbols, swept.retained)) {
23
+ for (const line of retainedLines(swept.matrix.union.symbols, swept.sweep.retained)) {
24
24
  out.write(`${line}\n`);
25
25
  }
26
26
  for (const note of partialNotes({
package/jsr.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cplieger/deadset-ts",
3
- "version": "8.0.0-dev.17",
3
+ "version": "8.0.0-dev.18",
4
4
  "license": "GPL-3.0-or-later",
5
5
  "exports": {
6
6
  ".": "./src/index.ts"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cplieger/deadset-ts",
3
- "version": "8.0.0-dev.17",
3
+ "version": "8.0.0-dev.18",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "Deterministic dead-code analysis for TypeScript, class and type members included",