@cplieger/deadset-ts 8.0.0-dev.16 → 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.
- package/dist/src/alias-chain.js +0 -5
- package/dist/src/amd.js +81 -0
- package/dist/src/analysis.js +82 -91
- package/dist/src/augmentations.js +24 -25
- package/dist/src/bindings.js +382 -0
- package/dist/src/calls.js +279 -43
- package/dist/src/commands/tools.js +5 -9
- package/dist/src/component-files.js +3 -2
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/consumers.js +6 -9
- package/dist/src/contextual-uses.js +1 -17
- package/dist/src/contract/config.generated.js +4 -1
- package/dist/src/contract/contract.generated.js +2 -2
- package/dist/src/contract/report.generated.js +6 -2
- package/dist/src/dependencies.js +25 -29
- package/dist/src/destructuring.js +1 -19
- package/dist/src/element-access.js +179 -164
- package/dist/src/entries/build-scripts.js +2 -2
- package/dist/src/entries/configuration-files.js +3 -7
- package/dist/src/entries/conventions.js +2 -2
- package/dist/src/entries/html-entries.js +7 -10
- package/dist/src/entries/mdx-entries.js +2 -2
- package/dist/src/entries/static-module.js +2 -1
- package/dist/src/exempt.js +0 -26
- package/dist/src/exposure.js +400 -0
- package/dist/src/facts.js +4 -4
- package/dist/src/findings/completion.js +44 -21
- package/dist/src/findings/emitters.js +22 -33
- package/dist/src/{implementations.js → findings/implementations.js} +3 -3
- package/dist/src/{in-operands.js → findings/in-operands.js} +2 -2
- package/dist/src/findings/intra-function.js +3 -3
- package/dist/src/findings/reads-and-writes.js +71 -15
- package/dist/src/findings/unused-declarations.js +2 -22
- package/dist/src/findings/visibility-narrowing.js +14 -20
- package/dist/src/framework-lifecycle.js +41 -14
- package/dist/src/import-bindings.js +73 -28
- package/dist/src/index/sites.js +557 -0
- package/dist/src/indexed-access.js +523 -478
- package/dist/src/interface-satisfaction.js +1 -1
- package/dist/src/inventory.js +18 -0
- package/dist/src/keyof-comparisons.js +124 -103
- package/dist/src/{components.js → liveness/components.js} +1 -1
- package/dist/src/{graph.js → liveness/graph.js} +45 -20
- package/dist/src/liveness/matrix.js +99 -0
- package/dist/src/liveness/sweep.js +558 -0
- package/dist/src/load/expected-module.js +2 -2
- package/dist/src/load/specifiers.js +2 -4
- package/dist/src/load/synthesize.js +5 -0
- package/dist/src/load/workspace-resolution.js +2 -2
- package/dist/src/module-declarations.js +70 -85
- package/dist/src/overrides.js +134 -112
- package/dist/src/paths.js +46 -7
- package/dist/src/platform-calls.js +2 -2
- package/dist/src/platform-symbols.js +13 -19
- package/dist/src/query.js +46 -0
- package/dist/src/references/read-rule.js +1 -0
- package/dist/src/references/read-rules.js +48 -0
- package/dist/src/references/sink.js +237 -0
- package/dist/src/references/sources.js +450 -0
- package/dist/src/references.js +110 -1285
- package/dist/src/roots/configured-declarations.js +3 -0
- package/dist/src/roots/published-reach.js +4 -4
- package/dist/src/roots/root-set.js +1 -1
- package/dist/src/roots/unseen-loads.js +38 -10
- package/dist/src/serialization-contract.js +8 -3
- package/dist/src/stores.js +4 -2
- package/dist/src/symbol-keys.js +89 -83
- package/dist/src/tests/file-class.js +76 -0
- package/dist/src/{outside-tests.js → tests/outside.js} +7 -7
- package/dist/src/{test-support.js → tests/support.js} +2 -11
- package/dist/src/type-parameter-reads.js +178 -139
- package/dist/src/unimplemented-callees.js +262 -242
- package/dist/src/verbs/analyze.js +3 -3
- package/dist/src/verbs/explain.js +75 -132
- package/dist/src/verbs/print-retained.js +3 -3
- package/jsr.json +1 -1
- package/package.json +2 -2
- package/src/alias-chain.ts +0 -7
- package/src/amd.ts +130 -0
- package/src/analysis.ts +125 -128
- package/src/augmentations.ts +42 -28
- package/src/bindings.ts +460 -0
- package/src/calls.ts +338 -46
- package/src/commands/tools.ts +5 -11
- package/src/component-files.ts +4 -2
- package/src/conformance-record.ts +1 -1
- package/src/consumers.ts +8 -11
- package/src/contextual-uses.ts +2 -22
- package/src/contract/config.generated.ts +4 -1
- package/src/contract/contract.generated.ts +2 -2
- package/src/contract/report.generated.ts +6 -2
- package/src/dependencies.ts +27 -28
- package/src/destructuring.ts +1 -31
- package/src/element-access.ts +188 -182
- package/src/entries/build-scripts.ts +2 -2
- package/src/entries/configuration-files.ts +11 -8
- package/src/entries/conventions.ts +2 -1
- package/src/entries/html-entries.ts +15 -14
- package/src/entries/mdx-entries.ts +2 -2
- package/src/entries/static-module.ts +2 -1
- package/src/exempt.ts +31 -46
- package/src/exposure.ts +492 -0
- package/src/facts.ts +5 -3
- package/src/finding.ts +1 -1
- package/src/findings/boundary.ts +9 -0
- package/src/findings/completion.ts +48 -23
- package/src/findings/emitter.ts +1 -1
- package/src/findings/emitters.ts +23 -41
- package/src/{implementations.ts → findings/implementations.ts} +4 -4
- package/src/{in-operands.ts → findings/in-operands.ts} +3 -3
- package/src/findings/interfaces.ts +3 -3
- package/src/findings/intra-function.ts +3 -3
- package/src/findings/reads-and-writes.ts +99 -26
- package/src/findings/unused-declarations.ts +4 -28
- package/src/findings/visibility-narrowing.ts +25 -26
- package/src/framework-lifecycle.ts +42 -14
- package/src/import-bindings.ts +98 -29
- package/src/index/sites.ts +781 -0
- package/src/indexed-access.ts +570 -527
- package/src/interface-satisfaction.ts +2 -2
- package/src/inventory.ts +19 -0
- package/src/keyof-comparisons.ts +131 -116
- package/src/{components.ts → liveness/components.ts} +3 -3
- package/src/{graph.ts → liveness/graph.ts} +93 -27
- package/src/liveness/matrix.ts +162 -0
- package/src/liveness/sweep.ts +855 -0
- package/src/load/expected-module.ts +8 -2
- package/src/load/specifiers.ts +8 -5
- package/src/load/synthesize.ts +6 -0
- package/src/load/workspace-resolution.ts +9 -2
- package/src/module-declarations.ts +74 -98
- package/src/overrides.ts +162 -134
- package/src/paths.ts +54 -8
- package/src/platform-calls.ts +3 -3
- package/src/platform-symbols.ts +11 -27
- package/src/query.ts +81 -0
- package/src/references/read-rule.ts +60 -0
- package/src/references/read-rules.ts +55 -0
- package/src/references/sink.ts +314 -0
- package/src/references/sources.ts +632 -0
- package/src/references.ts +175 -1733
- package/src/report.ts +1 -1
- package/src/roots/configured-declarations.ts +3 -0
- package/src/roots/published-reach.ts +9 -4
- package/src/roots/root-set.ts +1 -1
- package/src/roots/unseen-loads.ts +49 -11
- package/src/serialization-contract.ts +8 -3
- package/src/stores.ts +15 -3
- package/src/symbol-keys.ts +102 -102
- package/src/tests/file-class.ts +135 -0
- package/src/{outside-tests.ts → tests/outside.ts} +16 -10
- package/src/{test-support.ts → tests/support.ts} +9 -20
- package/src/type-parameter-reads.ts +187 -158
- package/src/unimplemented-callees.ts +297 -280
- package/src/verbs/analyze.ts +3 -2
- package/src/verbs/explain.ts +83 -158
- package/src/verbs/print-retained.ts +3 -3
- package/dist/src/matrix.js +0 -273
- package/dist/src/sweep.js +0 -222
- package/src/matrix.ts +0 -463
- package/src/sweep.ts +0 -361
|
@@ -29,10 +29,10 @@ import {
|
|
|
29
29
|
} from "@typescript/native/ast";
|
|
30
30
|
import { SymbolFlags, type Type } from "@typescript/native/sync";
|
|
31
31
|
import { aliasChains } from "./alias-chain.ts";
|
|
32
|
-
import { emptyImplementation } from "./implementations.ts";
|
|
32
|
+
import { emptyImplementation } from "./findings/implementations.ts";
|
|
33
33
|
import type { DetectorInput, Evidence } from "./exempt.ts";
|
|
34
34
|
import type { Reference } from "./references.ts";
|
|
35
|
-
import type { Exemption } from "./
|
|
35
|
+
import type { Exemption } from "./exempt.ts";
|
|
36
36
|
import { nodeKey, type Inventory } from "./inventory.ts";
|
|
37
37
|
import { renderPosition } from "./position.ts";
|
|
38
38
|
import { must } from "./query.ts";
|
package/src/inventory.ts
CHANGED
|
@@ -50,6 +50,25 @@ export type SymbolKind =
|
|
|
50
50
|
| "enum-member"
|
|
51
51
|
| "type-parameter";
|
|
52
52
|
|
|
53
|
+
/** The reader's word for each kind of declaration, as a finding names it. */
|
|
54
|
+
export const WORDS: Readonly<Record<SymbolKind, string>> = {
|
|
55
|
+
file: "file",
|
|
56
|
+
function: "function",
|
|
57
|
+
class: "class",
|
|
58
|
+
interface: "interface",
|
|
59
|
+
type: "type alias",
|
|
60
|
+
enum: "enum",
|
|
61
|
+
namespace: "namespace",
|
|
62
|
+
variable: "variable",
|
|
63
|
+
"export-alias": "re-export",
|
|
64
|
+
method: "method",
|
|
65
|
+
"class-member": "class member",
|
|
66
|
+
"interface-method": "interface method",
|
|
67
|
+
"type-member": "type member",
|
|
68
|
+
"enum-member": "enum member",
|
|
69
|
+
"type-parameter": "type parameter",
|
|
70
|
+
};
|
|
71
|
+
|
|
53
72
|
/**
|
|
54
73
|
* How a member may be named from outside its container. A `private` member is still
|
|
55
74
|
* reachable at run time by its name; a private name does not exist outside the class body.
|
package/src/keyof-comparisons.ts
CHANGED
|
@@ -10,7 +10,10 @@ import {
|
|
|
10
10
|
isCaseClause,
|
|
11
11
|
isElementAccessExpression,
|
|
12
12
|
isIdentifier,
|
|
13
|
+
isNoSubstitutionTemplateLiteral,
|
|
14
|
+
isNumericLiteral,
|
|
13
15
|
isPropertyAccessExpression,
|
|
16
|
+
isStringLiteral,
|
|
14
17
|
isSwitchStatement,
|
|
15
18
|
isTypeOperatorNode,
|
|
16
19
|
SyntaxKind,
|
|
@@ -20,12 +23,8 @@ import {
|
|
|
20
23
|
type TypeNode,
|
|
21
24
|
} from "@typescript/native/ast";
|
|
22
25
|
import { SymbolFlags, type Type } from "@typescript/native/sync";
|
|
23
|
-
import type { AliasChains } from "./alias-chain.ts";
|
|
24
|
-
import { nodeKey, type Inventory } from "./inventory.ts";
|
|
25
|
-
import { renderPosition } from "./position.ts";
|
|
26
26
|
import { UNANSWERED } from "./query.ts";
|
|
27
|
-
import type {
|
|
28
|
-
import type { ProjectView } from "./session.ts";
|
|
27
|
+
import type { Read, ReadRule } from "./references/read-rule.ts";
|
|
29
28
|
import { parenthesesRemoved } from "./unwrap.ts";
|
|
30
29
|
|
|
31
30
|
/** The operators that compare their operands for equality. */
|
|
@@ -83,125 +82,141 @@ function literalNames(parts: readonly Type[]): readonly string[] {
|
|
|
83
82
|
}
|
|
84
83
|
|
|
85
84
|
/**
|
|
86
|
-
* The
|
|
87
|
-
*
|
|
85
|
+
* The reads the key comparisons of a side's files make of the members their literal side
|
|
86
|
+
* names, each made by the declaration holding the comparison. A question the checker leaves
|
|
87
|
+
* unanswered holds what its answer could have read.
|
|
88
88
|
*/
|
|
89
|
-
export
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
89
|
+
export const keyofComparisons: ReadRule = {
|
|
90
|
+
start() {
|
|
91
|
+
const comparisons: Comparison[] = [];
|
|
92
|
+
return {
|
|
93
|
+
visit(node, { file, from }) {
|
|
94
|
+
if (isBinaryExpression(node) && EQUALITIES.has(node.operatorToken.kind)) {
|
|
95
|
+
comparisons.push({ file, from, keyed: node.left, other: node.right });
|
|
96
|
+
comparisons.push({ file, from, keyed: node.right, other: node.left });
|
|
97
|
+
}
|
|
98
|
+
if (isSwitchStatement(node)) {
|
|
99
|
+
for (const clause of node.caseBlock.clauses) {
|
|
100
|
+
if (isCaseClause(clause)) {
|
|
101
|
+
comparisons.push({ file, from, keyed: node.expression, other: clause.expression });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
reads({ project, chains }) {
|
|
107
|
+
const queries = project.queries;
|
|
108
|
+
const found: Read[] = [];
|
|
109
|
+
const named = comparisons.flatMap((one) => {
|
|
110
|
+
const name = nameOf(one.keyed);
|
|
111
|
+
return name === undefined || isElementAccessExpression(parenthesesRemoved(one.other))
|
|
112
|
+
? []
|
|
113
|
+
: [{ ...one, name }];
|
|
114
|
+
});
|
|
115
|
+
if (named.length === 0) {
|
|
116
|
+
return found;
|
|
117
|
+
}
|
|
118
|
+
// The operand's own declaration states the key type, which the checker resolves to
|
|
119
|
+
// the literals it admits and so no longer names the type the key indexes.
|
|
120
|
+
const symbols = queries.symbolsAt(named.map((one) => one.name));
|
|
121
|
+
const keyed = named.flatMap((one, at) => {
|
|
122
|
+
const asked = symbols[at];
|
|
123
|
+
const symbol =
|
|
124
|
+
asked === UNANSWERED || asked === undefined || (asked.flags & SymbolFlags.Alias) === 0
|
|
125
|
+
? asked
|
|
126
|
+
: queries.aliased(asked);
|
|
127
|
+
if (symbol === UNANSWERED) {
|
|
128
|
+
const text = literalText(one.other);
|
|
129
|
+
found.push({
|
|
130
|
+
kind: "held",
|
|
131
|
+
from: one.from,
|
|
132
|
+
...(text === undefined ? {} : { names: [text] }),
|
|
117
133
|
});
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
if (symbol === undefined) {
|
|
137
|
+
return [];
|
|
118
138
|
}
|
|
139
|
+
const operands = symbol.declarations.flatMap((handle) => {
|
|
140
|
+
const declared = (
|
|
141
|
+
project.declarationAt(handle)?.node as { readonly type?: TypeNode } | undefined
|
|
142
|
+
)?.type;
|
|
143
|
+
return declared === undefined ? [] : keyofOperands(declared);
|
|
144
|
+
});
|
|
145
|
+
return operands.length === 0 ? [] : [{ ...one, operands }];
|
|
146
|
+
});
|
|
147
|
+
if (keyed.length === 0) {
|
|
148
|
+
return found;
|
|
119
149
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
const named = comparisons.flatMap((one) => {
|
|
130
|
-
const name = nameOf(one.keyed);
|
|
131
|
-
return name === undefined || isElementAccessExpression(parenthesesRemoved(one.other))
|
|
132
|
-
? []
|
|
133
|
-
: [{ ...one, name }];
|
|
134
|
-
});
|
|
135
|
-
if (named.length === 0) {
|
|
136
|
-
return [];
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// The operand's own declaration states the key type, which the checker resolves to
|
|
140
|
-
// the literals it admits and so no longer names the type the key indexes.
|
|
141
|
-
const symbols = queries.symbolsAt(named.map((one) => one.name));
|
|
142
|
-
const keyed = named.flatMap((one, at) => {
|
|
143
|
-
const asked = symbols[at];
|
|
144
|
-
const symbol =
|
|
145
|
-
asked === UNANSWERED || asked === undefined || (asked.flags & SymbolFlags.Alias) === 0
|
|
146
|
-
? asked
|
|
147
|
-
: queries.aliased(asked);
|
|
148
|
-
if (symbol === UNANSWERED || symbol === undefined) {
|
|
149
|
-
return [];
|
|
150
|
-
}
|
|
151
|
-
const operands = symbol.declarations.flatMap((handle) => {
|
|
152
|
-
const declared = (
|
|
153
|
-
project.declarationAt(handle)?.node as { readonly type?: TypeNode } | undefined
|
|
154
|
-
)?.type;
|
|
155
|
-
return declared === undefined ? [] : keyofOperands(declared);
|
|
156
|
-
});
|
|
157
|
-
return operands.length === 0 ? [] : [{ ...one, operands }];
|
|
158
|
-
});
|
|
159
|
-
if (keyed.length === 0) {
|
|
160
|
-
return [];
|
|
161
|
-
}
|
|
162
|
-
const nodes = keyed.flatMap((one) => [one.other, ...one.operands]);
|
|
163
|
-
const types = queries.typesAt(nodes);
|
|
164
|
-
const typeOf = new Map<Node, Type>();
|
|
165
|
-
nodes.forEach((node, at) => {
|
|
166
|
-
const type = types[at];
|
|
167
|
-
if (type !== UNANSWERED && type !== undefined) {
|
|
168
|
-
typeOf.set(node, type);
|
|
169
|
-
}
|
|
170
|
-
});
|
|
171
|
-
const partsOf = (type: Type): readonly Type[] => {
|
|
172
|
-
const parts = queries.constituents(type);
|
|
173
|
-
return parts === UNANSWERED ? [type] : parts;
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
const found: Reference[] = [];
|
|
177
|
-
for (const one of keyed) {
|
|
178
|
-
const literal = typeOf.get(one.other);
|
|
179
|
-
const names = literal === undefined ? [] : literalNames(partsOf(literal));
|
|
180
|
-
if (names.length === 0) {
|
|
181
|
-
continue;
|
|
182
|
-
}
|
|
183
|
-
const position = renderPosition(one.file, root, one.other.getStart());
|
|
184
|
-
for (const operand of one.operands) {
|
|
185
|
-
const object = typeOf.get(operand);
|
|
186
|
-
for (const part of object === undefined ? [] : partsOf(object)) {
|
|
187
|
-
const properties = queries.propertiesOf(part);
|
|
188
|
-
for (const property of properties === UNANSWERED ? [] : properties) {
|
|
189
|
-
if (!names.includes(property.name)) {
|
|
190
|
-
continue;
|
|
150
|
+
const nodes = keyed.flatMap((one) => [one.other, ...one.operands]);
|
|
151
|
+
const types = queries.typesAt(nodes);
|
|
152
|
+
const typeOf = new Map<Node, Type | typeof UNANSWERED>();
|
|
153
|
+
nodes.forEach((node, at) => {
|
|
154
|
+
const type = types[at];
|
|
155
|
+
if (type !== undefined) {
|
|
156
|
+
typeOf.set(node, type);
|
|
191
157
|
}
|
|
192
|
-
|
|
158
|
+
});
|
|
159
|
+
const partsOf = (type: Type): readonly Type[] => {
|
|
160
|
+
const parts = queries.constituents(type);
|
|
161
|
+
return parts === UNANSWERED ? [type] : parts;
|
|
162
|
+
};
|
|
163
|
+
for (const one of keyed) {
|
|
164
|
+
const literal = typeOf.get(one.other);
|
|
165
|
+
const objects = one.operands.map((operand) => typeOf.get(operand));
|
|
166
|
+
if (literal === UNANSWERED) {
|
|
167
|
+
const known = objects.filter((type) => type !== undefined && type !== UNANSWERED);
|
|
193
168
|
found.push({
|
|
169
|
+
kind: "held",
|
|
194
170
|
from: one.from,
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
use: "read",
|
|
198
|
-
resolution: "keyed",
|
|
199
|
-
test: testFiles.has(position.path),
|
|
171
|
+
types: known.flatMap(partsOf),
|
|
172
|
+
...(known.length < objects.length ? { members: true as const } : {}),
|
|
200
173
|
});
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
const names = literal === undefined ? [] : literalNames(partsOf(literal));
|
|
177
|
+
if (names.length === 0) {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
for (const object of objects) {
|
|
181
|
+
if (object === UNANSWERED) {
|
|
182
|
+
found.push({ kind: "held", from: one.from, names });
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
for (const part of object === undefined ? [] : partsOf(object)) {
|
|
186
|
+
const properties = queries.propertiesOf(part);
|
|
187
|
+
if (properties === UNANSWERED) {
|
|
188
|
+
found.push({ kind: "held", from: one.from, names });
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
for (const property of properties) {
|
|
192
|
+
if (!names.includes(property.name)) {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
for (const to of chains.declarationsOf(property)) {
|
|
196
|
+
found.push({
|
|
197
|
+
kind: "named",
|
|
198
|
+
from: one.from,
|
|
199
|
+
to,
|
|
200
|
+
file: one.file,
|
|
201
|
+
at: one.other,
|
|
202
|
+
use: "read",
|
|
203
|
+
via: "keyed",
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
201
208
|
}
|
|
202
209
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
210
|
+
return found;
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
/** The text a string or number literal operand spells, where it is one. */
|
|
217
|
+
function literalText(node: Expression): string | undefined {
|
|
218
|
+
const at = parenthesesRemoved(node);
|
|
219
|
+
return isStringLiteral(at) || isNumericLiteral(at) || isNoSubstitutionTemplateLiteral(at)
|
|
220
|
+
? at.text
|
|
221
|
+
: undefined;
|
|
207
222
|
}
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* and every dead declaration that falls with them, being dead only through them.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type { Cascade } from "
|
|
7
|
+
import type { Cascade } from "../config.ts";
|
|
8
8
|
import { OUTSIDE, type Graph } from "./graph.ts";
|
|
9
|
-
import type { InventorySymbol } from "
|
|
10
|
-
import { PRODUCT } from "
|
|
9
|
+
import type { InventorySymbol } from "../inventory.ts";
|
|
10
|
+
import { PRODUCT } from "../product.ts";
|
|
11
11
|
|
|
12
12
|
/** The fewest digits a component identifier's number is written with. */
|
|
13
13
|
const ID_DIGITS = 4;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* reachability, indexed for the sweep. The graph reads no file and asks the
|
|
4
|
-
* nothing, so a sweep over it is a function of the values it was built from, and
|
|
5
|
-
* inventory's order is the order every set a sweep answers reads in.
|
|
2
|
+
* The run's declarations, the references every configuration makes between them and the
|
|
3
|
+
* roots that seed reachability, indexed for the sweep. The graph reads no file and asks the
|
|
4
|
+
* compiler nothing, so a sweep over it is a function of the values it was built from, and
|
|
5
|
+
* the inventory's order is the order every set a sweep answers reads in.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type { InventorySymbol, SymbolKind } from "
|
|
9
|
-
import type { Reference } from "
|
|
10
|
-
import
|
|
8
|
+
import type { InventorySymbol, SymbolKind } from "../inventory.ts";
|
|
9
|
+
import type { Reference } from "../references.ts";
|
|
10
|
+
import { madeByTestCode } from "../tests/file-class.ts";
|
|
11
|
+
import type { Root, RootKind } from "../roots/root-set.ts";
|
|
11
12
|
|
|
12
13
|
/** The position of a declaration the inventory does not hold. */
|
|
13
14
|
export const OUTSIDE = -1;
|
|
@@ -79,24 +80,40 @@ export function namesACaller(kind: RootKind): boolean {
|
|
|
79
80
|
/** One reference as the sweep reads it: the declaration named, and whether a test file made it. */
|
|
80
81
|
export interface Edge {
|
|
81
82
|
readonly to: number;
|
|
83
|
+
/** The reference's place in {@link Graph.references}. */
|
|
84
|
+
readonly reference: number;
|
|
82
85
|
readonly test: boolean;
|
|
86
|
+
/** Whether only the name answered, which counts at the sweep's guessed tier alone. */
|
|
87
|
+
readonly guessed: boolean;
|
|
83
88
|
/**
|
|
84
89
|
* Whether the reference evaluates the module whose file it names, which reaches the
|
|
85
90
|
* module's top level and none of what it exports.
|
|
86
91
|
*/
|
|
87
92
|
readonly evaluation: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Whether a loaded consumer made it, whose test references a mode may class as
|
|
95
|
+
* production. Only a consumed edge can be one.
|
|
96
|
+
*/
|
|
97
|
+
readonly consumer: boolean;
|
|
88
98
|
}
|
|
89
99
|
|
|
90
|
-
/**
|
|
91
|
-
|
|
100
|
+
/**
|
|
101
|
+
* How many references one declaration carries, split by the classification of the file that
|
|
102
|
+
* made each; `consumerTest` counts the test references a loaded consumer's test files made,
|
|
103
|
+
* which a mode may class as production (`sweep.ts` `countedRefs`).
|
|
104
|
+
*/
|
|
105
|
+
export interface Counts {
|
|
92
106
|
readonly production: number;
|
|
93
107
|
readonly test: number;
|
|
108
|
+
readonly consumerTest: number;
|
|
94
109
|
}
|
|
95
110
|
|
|
96
111
|
/** One root the inventory holds, at the position of the declaration it names. */
|
|
97
|
-
interface Rooted {
|
|
112
|
+
export interface Rooted {
|
|
98
113
|
readonly at: number;
|
|
99
114
|
readonly kind: RootKind;
|
|
115
|
+
/** What names the root, as the root set spells it. */
|
|
116
|
+
readonly source: string;
|
|
100
117
|
}
|
|
101
118
|
|
|
102
119
|
/** The index the sweep and the component pass read. */
|
|
@@ -107,6 +124,12 @@ export interface Graph {
|
|
|
107
124
|
readonly out: readonly (readonly Edge[])[];
|
|
108
125
|
/** Per declaration, the references made to it, whoever made them. */
|
|
109
126
|
readonly made: readonly Counts[];
|
|
127
|
+
/** Per declaration, those of {@link Graph.made} only a name answered. */
|
|
128
|
+
readonly guessedMade: readonly Counts[];
|
|
129
|
+
/** The references the graph was built from, in the order given. */
|
|
130
|
+
readonly references: readonly Reference[];
|
|
131
|
+
/** Per reference, the configurations that make it, in the run's order. */
|
|
132
|
+
readonly configurations: readonly (readonly string[])[];
|
|
110
133
|
/** Per declaration, the position of its container, or {@link OUTSIDE} for a file. */
|
|
111
134
|
readonly parent: readonly number[];
|
|
112
135
|
/** Per declaration, whether a test file holds it. */
|
|
@@ -126,17 +149,44 @@ export interface Graph {
|
|
|
126
149
|
/** Every root naming a declaration the inventory holds, in the order the root set gives them. */
|
|
127
150
|
readonly rooted: readonly Rooted[];
|
|
128
151
|
/**
|
|
129
|
-
* Every reference a loaded consumer
|
|
130
|
-
* inventory holds, in the order given. Such
|
|
131
|
-
* sweep holds what it names live under both relations
|
|
152
|
+
* Every reference a loaded consumer, an unheld file or a declaration no configuration
|
|
153
|
+
* judges makes to a declaration the inventory holds, in the order given. Such code is a
|
|
154
|
+
* caller the analysis read, so the sweep holds what it names live under both relations
|
|
155
|
+
* and reaches on from there.
|
|
132
156
|
*/
|
|
133
157
|
readonly consumed: readonly Edge[];
|
|
134
158
|
/** The position of the declaration one identifier names, or {@link OUTSIDE}. */
|
|
135
159
|
at(id: string): number;
|
|
136
160
|
}
|
|
137
161
|
|
|
162
|
+
/** What the run says of a graph's references beyond the references themselves. */
|
|
163
|
+
interface Sources {
|
|
164
|
+
/**
|
|
165
|
+
* The declarations a configuration holds only for their references, judged by none: each
|
|
166
|
+
* reference one makes enters as a consumer's does, since deleting what it names breaks
|
|
167
|
+
* that configuration's compile (matrix.5).
|
|
168
|
+
*/
|
|
169
|
+
readonly unjudged?: ReadonlySet<string>;
|
|
170
|
+
/** Per reference, in the order given, the configurations that make it. */
|
|
171
|
+
readonly configurations?: readonly (readonly string[])[];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Adds one reference's count to the counts of the declaration it names. */
|
|
175
|
+
function count(
|
|
176
|
+
counts: { production: number; test: number; consumerTest: number },
|
|
177
|
+
test: boolean,
|
|
178
|
+
consumer: boolean,
|
|
179
|
+
): void {
|
|
180
|
+
if (test) {
|
|
181
|
+
counts.test += 1;
|
|
182
|
+
counts.consumerTest += consumer ? 1 : 0;
|
|
183
|
+
} else {
|
|
184
|
+
counts.production += 1;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
138
188
|
/**
|
|
139
|
-
* Indexes
|
|
189
|
+
* Indexes the run's inventory, references and roots. `testFiles` are the paths the
|
|
140
190
|
* reference pass classified as test files, so a declaration one holds is a test
|
|
141
191
|
* declaration. A reference to a declaration the inventory does not hold is dropped; one
|
|
142
192
|
* from such a declaration still counts toward its target, because reference counting
|
|
@@ -149,35 +199,48 @@ export function graphOf(
|
|
|
149
199
|
testFiles: readonly string[],
|
|
150
200
|
supportFiles: readonly string[] = [],
|
|
151
201
|
ambient: ReadonlySet<string> = new Set(),
|
|
202
|
+
sources: Sources = {},
|
|
152
203
|
): Graph {
|
|
153
204
|
const supports = new Set(supportFiles);
|
|
154
205
|
const index = new Map(symbols.map((symbol, at) => [symbol.id, at]));
|
|
155
206
|
const at = (id: string): number => index.get(id) ?? OUTSIDE;
|
|
156
207
|
const tests = new Set(testFiles);
|
|
208
|
+
const unjudged = sources.unjudged ?? new Set<string>();
|
|
157
209
|
|
|
158
210
|
const out: Edge[][] = symbols.map(() => []);
|
|
159
|
-
const made = symbols.map(() => ({ production: 0, test: 0 }));
|
|
211
|
+
const made = symbols.map(() => ({ production: 0, test: 0, consumerTest: 0 }));
|
|
212
|
+
const guessedMade = symbols.map(() => ({ production: 0, test: 0, consumerTest: 0 }));
|
|
160
213
|
const consumed: Edge[] = [];
|
|
161
|
-
|
|
214
|
+
references.forEach((reference, place) => {
|
|
162
215
|
const to = at(reference.to);
|
|
163
216
|
if (to === OUTSIDE) {
|
|
164
|
-
|
|
217
|
+
return;
|
|
165
218
|
}
|
|
219
|
+
const test = madeByTestCode(reference, supports);
|
|
220
|
+
const consumer = reference.consumer !== undefined;
|
|
221
|
+
const guessed = reference.guessed === true;
|
|
166
222
|
const counts = made[to];
|
|
167
223
|
if (counts !== undefined) {
|
|
168
|
-
|
|
169
|
-
counts.test += 1;
|
|
170
|
-
} else {
|
|
171
|
-
counts.production += 1;
|
|
172
|
-
}
|
|
224
|
+
count(counts, test, consumer);
|
|
173
225
|
}
|
|
174
|
-
const
|
|
175
|
-
if (
|
|
226
|
+
const guesses = guessedMade[to];
|
|
227
|
+
if (guessed && guesses !== undefined) {
|
|
228
|
+
count(guesses, test, consumer);
|
|
229
|
+
}
|
|
230
|
+
const edge = {
|
|
231
|
+
to,
|
|
232
|
+
reference: place,
|
|
233
|
+
test,
|
|
234
|
+
guessed,
|
|
235
|
+
evaluation: reference.use === "evaluation",
|
|
236
|
+
consumer,
|
|
237
|
+
};
|
|
238
|
+
if (consumer || reference.unheld === true || unjudged.has(reference.from)) {
|
|
176
239
|
consumed.push(edge);
|
|
177
|
-
|
|
240
|
+
return;
|
|
178
241
|
}
|
|
179
242
|
out[at(reference.from)]?.push(edge);
|
|
180
|
-
}
|
|
243
|
+
});
|
|
181
244
|
|
|
182
245
|
const parent = symbols.map((symbol) => at(symbol.parent));
|
|
183
246
|
const exportsOf: number[][] = symbols.map(() => []);
|
|
@@ -192,7 +255,7 @@ export function graphOf(
|
|
|
192
255
|
for (const root of roots) {
|
|
193
256
|
const position = at(root.id);
|
|
194
257
|
if (position !== OUTSIDE) {
|
|
195
|
-
rooted.push({ at: position, kind: root.kind });
|
|
258
|
+
rooted.push({ at: position, kind: root.kind, source: root.source });
|
|
196
259
|
}
|
|
197
260
|
}
|
|
198
261
|
|
|
@@ -200,6 +263,9 @@ export function graphOf(
|
|
|
200
263
|
symbols,
|
|
201
264
|
out,
|
|
202
265
|
made,
|
|
266
|
+
guessedMade,
|
|
267
|
+
references,
|
|
268
|
+
configurations: sources.configurations ?? references.map(() => []),
|
|
203
269
|
parent,
|
|
204
270
|
test: symbols.map((symbol) => tests.has(symbol.position.path)),
|
|
205
271
|
support: symbols.map((symbol) => supports.has(symbol.position.path)),
|