@cplieger/deadset-ts 3.11.0 → 3.12.1
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 +2 -2
- package/dist/src/analysis.js +51 -13
- package/dist/src/conformance-record.js +2 -12
- package/dist/src/consumers.js +49 -0
- package/dist/src/discover.js +59 -23
- package/dist/src/findings/completion.js +49 -17
- package/dist/src/findings/non-code-artifacts.js +9 -2
- package/dist/src/findings/reads-and-writes.js +3 -1
- package/dist/src/graph.js +15 -12
- package/dist/src/matrix.js +14 -3
- package/dist/src/references.js +21 -9
- package/dist/src/report.js +4 -1
- package/dist/src/roots.js +13 -8
- package/dist/src/session.js +15 -0
- package/dist/src/sweep.js +11 -0
- package/dist/src/verbs/analyze.js +6 -11
- package/dist/src/verbs/explain.js +49 -35
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/analysis.ts +109 -25
- package/src/conformance-record.ts +2 -14
- package/src/consumers.ts +86 -0
- package/src/discover.ts +67 -23
- package/src/findings/completion.ts +56 -20
- package/src/findings/non-code-artifacts.ts +9 -2
- package/src/findings/reads-and-writes.ts +3 -1
- package/src/graph.ts +21 -12
- package/src/matrix.ts +14 -5
- package/src/references.ts +48 -9
- package/src/report.ts +17 -2
- package/src/roots.ts +14 -9
- package/src/session.ts +19 -0
- package/src/sweep.ts +13 -1
- package/src/verbs/analyze.ts +6 -14
- package/src/verbs/explain.ts +57 -40
package/dist/src/references.js
CHANGED
|
@@ -3,6 +3,7 @@ import { aliasChains } from "./alias-chain.js";
|
|
|
3
3
|
import { destructuring, propertyReadsOf } from "./destructuring.js";
|
|
4
4
|
import { globExpression } from "./glob.js";
|
|
5
5
|
import { nodeKey } from "./inventory.js";
|
|
6
|
+
import { relativePath } from "./paths.js";
|
|
6
7
|
import { byPosition, renderPosition } from "./position.js";
|
|
7
8
|
/**
|
|
8
9
|
* The batch cap a run takes when the configuration names none. It bounds the size of
|
|
@@ -306,7 +307,9 @@ function compare(a, b) {
|
|
|
306
307
|
*
|
|
307
308
|
* `held` is the inventory of the same project, whose declaration map is what a resolved
|
|
308
309
|
* symbol is looked up in; `targetRoot` is the absolute path every position is rendered
|
|
309
|
-
* against, which is the root the inventory was read against.
|
|
310
|
+
* against, which is the root the inventory was read against. A pass over a consumer
|
|
311
|
+
* walks the consumer's files instead and renders against its root, as
|
|
312
|
+
* {@link ReferenceOptions.consumer} states.
|
|
310
313
|
*
|
|
311
314
|
* The order is by the referencing position, then by the declaration named, the
|
|
312
315
|
* declaration referencing it and the use. That is a total order because one name
|
|
@@ -326,7 +329,14 @@ export function references(project, held, targetRoot, options) {
|
|
|
326
329
|
let fileBatches = 0;
|
|
327
330
|
let residueFallbacks = 0;
|
|
328
331
|
let shorthandLookups = 0;
|
|
329
|
-
const
|
|
332
|
+
const consumer = options.consumer;
|
|
333
|
+
const root = consumer?.root ?? targetRoot;
|
|
334
|
+
const files = consumer === undefined
|
|
335
|
+
? project.ownSourceFiles()
|
|
336
|
+
: project
|
|
337
|
+
.ownSourceFiles()
|
|
338
|
+
.filter((file) => relativePath(consumer.root, file.fileName) !== undefined &&
|
|
339
|
+
relativePath(targetRoot, file.fileName) === undefined);
|
|
330
340
|
const fileIds = new Set(held.symbols.filter((symbol) => symbol.kind === "file").map((symbol) => symbol.id));
|
|
331
341
|
/** The identifier of the declaration one node of this program is, where it is one. */
|
|
332
342
|
const declaredAt = (node) => declarations.get(nodeKey(node.getSourceFile(), node));
|
|
@@ -352,7 +362,7 @@ export function references(project, held, targetRoot, options) {
|
|
|
352
362
|
return project.resolvedSymbolAt(project.handle(site.node));
|
|
353
363
|
};
|
|
354
364
|
for (const file of files) {
|
|
355
|
-
const path = renderPosition(file,
|
|
365
|
+
const path = renderPosition(file, root, 0).path;
|
|
356
366
|
const test = patterns.some((pattern) => pattern.test(path));
|
|
357
367
|
if (test) {
|
|
358
368
|
tests.push(path);
|
|
@@ -365,7 +375,7 @@ export function references(project, held, targetRoot, options) {
|
|
|
365
375
|
found.push({
|
|
366
376
|
from: declaration,
|
|
367
377
|
to: declaration,
|
|
368
|
-
position: renderPosition(file,
|
|
378
|
+
position: renderPosition(file, root, decorator.getStart()),
|
|
369
379
|
use: "decorator",
|
|
370
380
|
resolution: "syntax",
|
|
371
381
|
test,
|
|
@@ -401,7 +411,7 @@ export function references(project, held, targetRoot, options) {
|
|
|
401
411
|
if (symbol === undefined) {
|
|
402
412
|
continue;
|
|
403
413
|
}
|
|
404
|
-
const position = renderPosition(file,
|
|
414
|
+
const position = renderPosition(file, root, link.at.getStart());
|
|
405
415
|
for (const id of carriedBy(link, symbol)) {
|
|
406
416
|
found.push({
|
|
407
417
|
from: link.from,
|
|
@@ -418,7 +428,7 @@ export function references(project, held, targetRoot, options) {
|
|
|
418
428
|
if (module === undefined) {
|
|
419
429
|
continue;
|
|
420
430
|
}
|
|
421
|
-
const position = renderPosition(file,
|
|
431
|
+
const position = renderPosition(file, root, evaluation.node.getStart());
|
|
422
432
|
for (const id of chains.declarationsOf(module)) {
|
|
423
433
|
if (fileIds.has(id)) {
|
|
424
434
|
found.push({
|
|
@@ -439,7 +449,7 @@ export function references(project, held, targetRoot, options) {
|
|
|
439
449
|
if (symbol === undefined || read === undefined) {
|
|
440
450
|
return;
|
|
441
451
|
}
|
|
442
|
-
const position = renderPosition(file,
|
|
452
|
+
const position = renderPosition(file, root, read.key.getStart());
|
|
443
453
|
for (const target of chains.chainOf(symbol)) {
|
|
444
454
|
found.push({
|
|
445
455
|
from: read.from,
|
|
@@ -461,7 +471,7 @@ export function references(project, held, targetRoot, options) {
|
|
|
461
471
|
if (symbol === undefined) {
|
|
462
472
|
continue;
|
|
463
473
|
}
|
|
464
|
-
const position = renderPosition(file,
|
|
474
|
+
const position = renderPosition(file, root, site.node.getStart());
|
|
465
475
|
// A re-export something imports is used and so is the declaration behind it, so
|
|
466
476
|
// the use names every link of the chain its name stands at the head of.
|
|
467
477
|
for (const target of chains.chainOf(symbol)) {
|
|
@@ -482,7 +492,9 @@ export function references(project, held, targetRoot, options) {
|
|
|
482
492
|
compare(a.use, b.use));
|
|
483
493
|
return {
|
|
484
494
|
configFile: project.configFile,
|
|
485
|
-
references:
|
|
495
|
+
references: consumer === undefined
|
|
496
|
+
? found
|
|
497
|
+
: found.map((reference) => ({ ...reference, consumer: consumer.id })),
|
|
486
498
|
testFilePaths: tests.sort(compare),
|
|
487
499
|
cost: {
|
|
488
500
|
batched,
|
package/dist/src/report.js
CHANGED
|
@@ -188,6 +188,9 @@ export function buildReport(input) {
|
|
|
188
188
|
const unavailable = [...new Map(input.unavailable.map((one) => [one.id, one])).values()]
|
|
189
189
|
.sort((a, b) => compare(a.id, b.id))
|
|
190
190
|
.map((one) => ({ id: one.id, role: "consumer", reason: one.reason }));
|
|
191
|
+
const loaded = [...input.loaded]
|
|
192
|
+
.sort((a, b) => compare(a.id, b.id) || compare(a.path, b.path))
|
|
193
|
+
.map((one) => ({ id: one.id, role: "consumer", path: one.path }));
|
|
191
194
|
return {
|
|
192
195
|
schema_version: SCHEMA_VERSION,
|
|
193
196
|
contract_version: input.contractVersion,
|
|
@@ -207,7 +210,7 @@ export function buildReport(input) {
|
|
|
207
210
|
.sort((a, b) => compare(a.id, b.id))
|
|
208
211
|
.map((one) => ({ id: one.id, project: one.project })),
|
|
209
212
|
configurations_not_built: [],
|
|
210
|
-
consumers: { declared: unavailable.length, loaded
|
|
213
|
+
consumers: { declared: loaded.length + unavailable.length, loaded, unavailable },
|
|
211
214
|
findings,
|
|
212
215
|
edge_evaluations: input.result.edgeEvaluations.map(wireEvaluation).sort(byEdge),
|
|
213
216
|
stale_suppressions: input.result.staleSuppressions.map(wireStale).sort(canonicalStale),
|
package/dist/src/roots.js
CHANGED
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Every root seeds REACHABILITY, and no root counts as a reference. A root of a kind
|
|
6
6
|
* that names a caller is live under reference counting as well, which the sweep
|
|
7
|
-
* decides;
|
|
8
|
-
*
|
|
9
|
-
* candidate under reference counting.
|
|
7
|
+
* decides; the published API only supposes a consumer, so a published export is live
|
|
8
|
+
* under reachability and still a candidate under reference counting.
|
|
10
9
|
*/
|
|
11
10
|
import { aliasChains } from "./alias-chain.js";
|
|
12
11
|
import { entryPoints } from "./entry-points.js";
|
|
@@ -94,6 +93,7 @@ function childrenOf(held) {
|
|
|
94
93
|
* is a root with everything its module exports: each name of the module's export
|
|
95
94
|
* table, and where a name is a re-export, every link of the chain to the declaration
|
|
96
95
|
* it carries, so a re-exported declaration is a root of the file that publishes it.
|
|
96
|
+
* In a library, what a published file exports is rooted by the published API alone.
|
|
97
97
|
* A file a runtime executes without reading its exports, a test file or a worker, is
|
|
98
98
|
* a root by itself.
|
|
99
99
|
*/
|
|
@@ -116,14 +116,19 @@ export function roots(project, held, targetRoot, options) {
|
|
|
116
116
|
entered.push({ file, kind, source, exports });
|
|
117
117
|
};
|
|
118
118
|
const published = [];
|
|
119
|
-
const publishes = (entry) =>
|
|
119
|
+
const publishes = (entry) => options.publishedAPI &&
|
|
120
|
+
entry.published &&
|
|
120
121
|
(options.manifest.declaresExports ? entry.member.startsWith("exports") : true);
|
|
122
|
+
for (const entry of options.manifest.entries) {
|
|
123
|
+
if (publishes(entry)) {
|
|
124
|
+
published.push(...files.named(entry.path));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const publishedNames = new Set(published.map((file) => file.fileName));
|
|
121
128
|
for (const entry of options.manifest.entries) {
|
|
122
129
|
for (const file of files.named(entry.path)) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
published.push(file);
|
|
126
|
-
}
|
|
130
|
+
const kind = entry.role === "run" ? "manifest-binary" : "manifest-entry";
|
|
131
|
+
enter(file, kind, entry.member, !publishedNames.has(file.fileName));
|
|
127
132
|
}
|
|
128
133
|
}
|
|
129
134
|
for (const pattern of options.entryFiles) {
|
package/dist/src/session.js
CHANGED
|
@@ -71,6 +71,21 @@ function viewOf(project) {
|
|
|
71
71
|
},
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* The same project, its own source files narrowed to the ones `keep` admits. A consumer's
|
|
76
|
+
* program holds the target's files it imports beside its own, and a pass that enumerates
|
|
77
|
+
* the target's declarations in that program reads those files alone.
|
|
78
|
+
*/
|
|
79
|
+
export function narrowedTo(project, keep) {
|
|
80
|
+
let kept;
|
|
81
|
+
return {
|
|
82
|
+
...project,
|
|
83
|
+
ownSourceFiles: () => {
|
|
84
|
+
kept ??= project.ownSourceFiles().filter(keep);
|
|
85
|
+
return kept;
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
74
89
|
/** The three diagnostic sets one project carries, read before any analysis. */
|
|
75
90
|
export function diagnosticsOf(project) {
|
|
76
91
|
return {
|
package/dist/src/sweep.js
CHANGED
|
@@ -67,6 +67,13 @@ export function sweep(graph, input) {
|
|
|
67
67
|
});
|
|
68
68
|
return { liveUnder: live, candidates };
|
|
69
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* The references a loaded consumer makes that the mode counts. A production mode counts
|
|
72
|
+
* none a consumer's test file made, as it counts none of the target's.
|
|
73
|
+
*/
|
|
74
|
+
function callsOf(graph, mode) {
|
|
75
|
+
return graph.consumed.filter((edge) => !mode.production || !edge.test);
|
|
76
|
+
}
|
|
70
77
|
/** Per declaration of the graph, whether one of the identifiers names it. */
|
|
71
78
|
function flagged(graph, ids) {
|
|
72
79
|
const flags = graph.symbols.map(() => false);
|
|
@@ -126,6 +133,10 @@ function reachable(graph, held, mode) {
|
|
|
126
133
|
for (const root of graph.rooted) {
|
|
127
134
|
enter(root.at, false);
|
|
128
135
|
}
|
|
136
|
+
// A consumer's reference reaches what it names as one of the target's own does.
|
|
137
|
+
for (const edge of callsOf(graph, mode)) {
|
|
138
|
+
enter(edge.to, !edge.evaluation);
|
|
139
|
+
}
|
|
129
140
|
walk(false);
|
|
130
141
|
return reached;
|
|
131
142
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { runAnalysis } from "../analysis.js";
|
|
2
2
|
import { ConfigError } from "../config.js";
|
|
3
3
|
import { CONFORMANCE, DECLARED_GAPS } from "../conformance.js";
|
|
4
|
+
import { moduleIdentity } from "../consumers.js";
|
|
4
5
|
import { analyzerProvenance, recordedFindings, verdictOf } from "../findings-pass.js";
|
|
5
|
-
import { packageScope } from "../inventory.js";
|
|
6
6
|
import { joinPath, normalizePath, relativePath, resolvePath } from "../paths.js";
|
|
7
7
|
import { buildReport, capFindings, sortFindings, } from "../report.js";
|
|
8
8
|
import { json } from "../reporters/json.js";
|
|
@@ -20,8 +20,6 @@ export const ANALYZE_OPTIONS = {
|
|
|
20
20
|
/** The two values `--exit-code` takes; on, the default, puts the run's verdict in the exit code. */
|
|
21
21
|
const EXIT_CODE_ON = "on";
|
|
22
22
|
const EXIT_CODE_OFF = "off";
|
|
23
|
-
/** Why a declared consumer is not loaded, which every report names it with. */
|
|
24
|
-
const NOT_LOADED = "this analyzer loads no consumer beside the target";
|
|
25
23
|
/** A path of the run no report can name, which ends the run with the failure code. */
|
|
26
24
|
class ReportPathError extends Error {
|
|
27
25
|
constructor(message) {
|
|
@@ -73,10 +71,6 @@ function configurationsOf(projects, targetRoot) {
|
|
|
73
71
|
return { id: one.id, project };
|
|
74
72
|
});
|
|
75
73
|
}
|
|
76
|
-
/** The name one module publishes itself under: the scope's, else its manifest's. */
|
|
77
|
-
function identityOf(module, host) {
|
|
78
|
-
return module.id === "" ? packageScope(host, module.path)("index.ts").package : module.id;
|
|
79
|
-
}
|
|
80
74
|
/** A count with its noun, so a message reads for one record as well as for several. */
|
|
81
75
|
function counted(n, noun) {
|
|
82
76
|
return `${String(n)} ${noun}${n === 1 ? "" : "s"}`;
|
|
@@ -163,12 +157,13 @@ export const analyzeVerb = ({ err, host, inputs, scope, openClient, option, repe
|
|
|
163
157
|
version,
|
|
164
158
|
conformance: CONFORMANCE,
|
|
165
159
|
declaredGaps: DECLARED_GAPS,
|
|
166
|
-
target: { kind, root, identity:
|
|
160
|
+
target: { kind, root, identity: moduleIdentity(host, scoped.target) },
|
|
167
161
|
configurations: configurationsOf(run.projects, targetRoot),
|
|
168
|
-
|
|
169
|
-
id:
|
|
170
|
-
|
|
162
|
+
loaded: run.consumers.map((consumer) => ({
|
|
163
|
+
id: consumer.id,
|
|
164
|
+
path: runRelative(invoked, consumer.path),
|
|
171
165
|
})),
|
|
166
|
+
unavailable: [],
|
|
172
167
|
result,
|
|
173
168
|
testFileRules: run.testFileRules,
|
|
174
169
|
});
|
|
@@ -2,6 +2,7 @@ import { runAnalysis } from "../analysis.js";
|
|
|
2
2
|
import { listing } from "../components.js";
|
|
3
3
|
import { ConfigError } from "../config.js";
|
|
4
4
|
import { OUTSIDE, swept as judged } from "../graph.js";
|
|
5
|
+
import { referenceKey } from "../matrix.js";
|
|
5
6
|
import { positionKey } from "../position.js";
|
|
6
7
|
import { resolve } from "../resolve.js";
|
|
7
8
|
import { EXIT_CLEAN, EXIT_USAGE } from "./verb.js";
|
|
@@ -78,7 +79,7 @@ function referencesOf(configured) {
|
|
|
78
79
|
const seen = new Set();
|
|
79
80
|
const held = [];
|
|
80
81
|
for (const reference of configured.flatMap((one) => one.references)) {
|
|
81
|
-
const key =
|
|
82
|
+
const key = referenceKey(reference);
|
|
82
83
|
if (!seen.has(key)) {
|
|
83
84
|
seen.add(key);
|
|
84
85
|
held.push(reference);
|
|
@@ -101,8 +102,9 @@ function rootsOf(configured) {
|
|
|
101
102
|
});
|
|
102
103
|
}
|
|
103
104
|
/**
|
|
104
|
-
* The shortest path from a root to one declaration, walked breadth first
|
|
105
|
-
* once in the order the run names them,
|
|
105
|
+
* The shortest path from a root or a loaded consumer to one declaration, walked breadth first
|
|
106
|
+
* from every root at once in the order the run names them, a consumer's reference being the
|
|
107
|
+
* first hop of a path of its own, and following the references the sweep follows: a
|
|
106
108
|
* reference that reads a module reaches what the module exports, and an evaluation or a root
|
|
107
109
|
* reaches the module alone. Under `production` no reference a test file made is followed,
|
|
108
110
|
* which is the set the report's sweep counts.
|
|
@@ -110,10 +112,16 @@ function rootsOf(configured) {
|
|
|
110
112
|
function shortestPath(analysis, to, production) {
|
|
111
113
|
const graph = analysis.swept.matrix.union;
|
|
112
114
|
const out = new Map();
|
|
115
|
+
const consumed = [];
|
|
113
116
|
for (const reference of referencesOf(analysis.configured)) {
|
|
114
117
|
if (production && reference.test) {
|
|
115
118
|
continue;
|
|
116
119
|
}
|
|
120
|
+
const { consumer } = reference;
|
|
121
|
+
if (consumer !== undefined) {
|
|
122
|
+
consumed.push({ ...reference, consumer });
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
117
125
|
const held = out.get(reference.from);
|
|
118
126
|
if (held === undefined) {
|
|
119
127
|
out.set(reference.from, [reference]);
|
|
@@ -131,10 +139,32 @@ function shortestPath(analysis, to, production) {
|
|
|
131
139
|
steps.push(step);
|
|
132
140
|
}
|
|
133
141
|
};
|
|
142
|
+
const follow = (reference, previous, origin) => {
|
|
143
|
+
enter({ at: reference.to, hop: { k: "reference", reference }, previous, origin });
|
|
144
|
+
const target = graph.at(reference.to);
|
|
145
|
+
if (reference.use === "evaluation" || target === OUTSIDE || expanded.has(reference.to)) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
expanded.add(reference.to);
|
|
149
|
+
for (const exported of graph.exportsOf[target] ?? []) {
|
|
150
|
+
const id = graph.symbols[exported]?.id;
|
|
151
|
+
if (id !== undefined) {
|
|
152
|
+
enter({ at: id, hop: { k: "export", reference, to: id }, previous, origin });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
134
156
|
for (const root of rootsOf(analysis.configured)) {
|
|
135
|
-
enter({ at: root.id, hop: undefined, previous: -1, root });
|
|
157
|
+
enter({ at: root.id, hop: undefined, previous: -1, origin: { k: "root", root } });
|
|
136
158
|
}
|
|
137
|
-
|
|
159
|
+
// A consumer's reference is one hop long, so it joins the walk where the roots' first
|
|
160
|
+
// hops do.
|
|
161
|
+
const firstHops = steps.length;
|
|
162
|
+
for (let head = 0;; head += 1) {
|
|
163
|
+
if (head === firstHops) {
|
|
164
|
+
for (const reference of consumed) {
|
|
165
|
+
follow(reference, -1, { k: "consumer", consumer: reference.consumer });
|
|
166
|
+
}
|
|
167
|
+
}
|
|
138
168
|
const step = steps[head];
|
|
139
169
|
if (step === undefined) {
|
|
140
170
|
break;
|
|
@@ -144,35 +174,18 @@ function shortestPath(analysis, to, production) {
|
|
|
144
174
|
for (let at = step; at?.hop !== undefined; at = steps[at.previous]) {
|
|
145
175
|
path.unshift(at.hop);
|
|
146
176
|
}
|
|
147
|
-
return {
|
|
177
|
+
return { origin: step.origin, path };
|
|
148
178
|
}
|
|
149
179
|
for (const reference of out.get(step.at) ?? []) {
|
|
150
|
-
|
|
151
|
-
at: reference.to,
|
|
152
|
-
hop: { k: "reference", reference },
|
|
153
|
-
previous: head,
|
|
154
|
-
root: step.root,
|
|
155
|
-
});
|
|
156
|
-
const target = graph.at(reference.to);
|
|
157
|
-
if (reference.use === "evaluation" || target === OUTSIDE || expanded.has(reference.to)) {
|
|
158
|
-
continue;
|
|
159
|
-
}
|
|
160
|
-
expanded.add(reference.to);
|
|
161
|
-
for (const exported of graph.exportsOf[target] ?? []) {
|
|
162
|
-
const id = graph.symbols[exported]?.id;
|
|
163
|
-
if (id !== undefined) {
|
|
164
|
-
enter({
|
|
165
|
-
at: id,
|
|
166
|
-
hop: { k: "export", reference, to: id },
|
|
167
|
-
previous: head,
|
|
168
|
-
root: step.root,
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
}
|
|
180
|
+
follow(reference, head, step.origin);
|
|
172
181
|
}
|
|
173
182
|
}
|
|
174
183
|
return undefined;
|
|
175
184
|
}
|
|
185
|
+
/** What made a reference: a declaration of the target, or a loaded consumer. */
|
|
186
|
+
function madeBy(reference) {
|
|
187
|
+
return reference.consumer === undefined ? reference.from : `consumer ${reference.consumer}`;
|
|
188
|
+
}
|
|
176
189
|
function referenceLine(reference, tail) {
|
|
177
190
|
return ` reference: ${positionKey(reference.position)}\t${reference.use}\t${tail}${reference.test ? "\tfrom a test file" : ""}\n`;
|
|
178
191
|
}
|
|
@@ -190,9 +203,10 @@ function liveness(analysis, subject, cascade) {
|
|
|
190
203
|
}
|
|
191
204
|
const found = shortestPath(analysis, subject.id, true) ?? shortestPath(analysis, subject.id, false);
|
|
192
205
|
if (found !== undefined && found.path.length > 0) {
|
|
193
|
-
|
|
206
|
+
const { origin } = found;
|
|
207
|
+
text += ` reached from: ${origin.k === "root" ? `root ${origin.root.kind} ${origin.root.id}` : `consumer ${origin.consumer}`}\n`;
|
|
194
208
|
for (const hop of found.path) {
|
|
195
|
-
text += referenceLine(hop.reference, `${hop.reference
|
|
209
|
+
text += referenceLine(hop.reference, `${madeBy(hop.reference)} -> ${hop.reference.to}`);
|
|
196
210
|
if (hop.k === "export") {
|
|
197
211
|
text += ` export: ${hop.reference.to} -> ${hop.to}\n`;
|
|
198
212
|
}
|
|
@@ -200,10 +214,10 @@ function liveness(analysis, subject, cascade) {
|
|
|
200
214
|
}
|
|
201
215
|
else if (found === undefined) {
|
|
202
216
|
const references = referencesOf(analysis.configured).filter((one) => one.to === subject.id);
|
|
203
|
-
text += " unreachable: no path of references reaches it from a root\n";
|
|
217
|
+
text += " unreachable: no path of references reaches it from a root or a loaded consumer\n";
|
|
204
218
|
text += ` references: ${String(references.length)}\n`;
|
|
205
219
|
for (const reference of references) {
|
|
206
|
-
text += referenceLine(reference, `from ${reference
|
|
220
|
+
text += referenceLine(reference, `from ${madeBy(reference)}`);
|
|
207
221
|
}
|
|
208
222
|
}
|
|
209
223
|
if (candidate !== undefined) {
|
|
@@ -277,9 +291,9 @@ function explanation(analysis, subject, cascade) {
|
|
|
277
291
|
* Explains one symbol from the analysis the report is built from, so an explanation cannot
|
|
278
292
|
* disagree with a finding: why a reported symbol is reported, which exemption classes held a
|
|
279
293
|
* retained one back, and for any other declaration the relations that hold it live and the
|
|
280
|
-
* shortest path of references from a root, or the references it has where
|
|
281
|
-
* A request naming no one declaration exits with the usage code and names
|
|
282
|
-
* matches it.
|
|
294
|
+
* shortest path of references from a root or a loaded consumer, or the references it has where
|
|
295
|
+
* no path reaches it. A request naming no one declaration exits with the usage code and names
|
|
296
|
+
* what partially matches it.
|
|
283
297
|
*/
|
|
284
298
|
export const explainVerb = ({ out, err, host, inputs, scope, openClient, option }) => {
|
|
285
299
|
const named = namedSymbol(option);
|
package/jsr.json
CHANGED