@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
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { isCallExpression, isClassDeclaration, isClassExpression, isClassStaticBlockDeclaration, isFunctionDeclaration, isFunctionExpression, isGetAccessorDeclaration, isHeritageClause, isMethodDeclaration, isNewExpression, isObjectLiteralExpression, isSetAccessorDeclaration, isTypeLiteralNode, ModifierFlags, SyntaxKind, } from "@typescript/native/ast";
|
|
10
10
|
import { SymbolFlags } from "@typescript/native/sync";
|
|
11
11
|
import { aliasChains } from "./alias-chain.js";
|
|
12
|
-
import { emptyImplementation } from "./implementations.js";
|
|
12
|
+
import { emptyImplementation } from "./findings/implementations.js";
|
|
13
13
|
import { nodeKey } from "./inventory.js";
|
|
14
14
|
import { renderPosition } from "./position.js";
|
|
15
15
|
import { must } from "./query.js";
|
package/dist/src/inventory.js
CHANGED
|
@@ -2,6 +2,24 @@ import { isClassDeclaration, isComputedPropertyName, isConstructorDeclaration, i
|
|
|
2
2
|
import { byPosition, positionKey, renderPosition, startLine } from "./position.js";
|
|
3
3
|
import { renderRef, spell } from "./ref.js";
|
|
4
4
|
import { signatureOf } from "./signatures.js";
|
|
5
|
+
/** The reader's word for each kind of declaration, as a finding names it. */
|
|
6
|
+
export const WORDS = {
|
|
7
|
+
file: "file",
|
|
8
|
+
function: "function",
|
|
9
|
+
class: "class",
|
|
10
|
+
interface: "interface",
|
|
11
|
+
type: "type alias",
|
|
12
|
+
enum: "enum",
|
|
13
|
+
namespace: "namespace",
|
|
14
|
+
variable: "variable",
|
|
15
|
+
"export-alias": "re-export",
|
|
16
|
+
method: "method",
|
|
17
|
+
"class-member": "class member",
|
|
18
|
+
"interface-method": "interface method",
|
|
19
|
+
"type-member": "type member",
|
|
20
|
+
"enum-member": "enum member",
|
|
21
|
+
"type-parameter": "type parameter",
|
|
22
|
+
};
|
|
5
23
|
/**
|
|
6
24
|
* One node's identity inside one program, which is how two readings of that program name
|
|
7
25
|
* one declaration: the binder's table and the tree meet here, and so does the reference
|
|
@@ -4,10 +4,8 @@
|
|
|
4
4
|
* that holds `keyof X`, the literal the other side has names the member of `X` under that
|
|
5
5
|
* key, because deleting the member makes the comparison a type error.
|
|
6
6
|
*/
|
|
7
|
-
import { isBinaryExpression, isCaseClause, isElementAccessExpression, isIdentifier, isPropertyAccessExpression, isSwitchStatement, isTypeOperatorNode, SyntaxKind, } from "@typescript/native/ast";
|
|
7
|
+
import { isBinaryExpression, isCaseClause, isElementAccessExpression, isIdentifier, isNoSubstitutionTemplateLiteral, isNumericLiteral, isPropertyAccessExpression, isStringLiteral, isSwitchStatement, isTypeOperatorNode, SyntaxKind, } from "@typescript/native/ast";
|
|
8
8
|
import { SymbolFlags } from "@typescript/native/sync";
|
|
9
|
-
import { nodeKey } from "./inventory.js";
|
|
10
|
-
import { renderPosition } from "./position.js";
|
|
11
9
|
import { UNANSWERED } from "./query.js";
|
|
12
10
|
import { parenthesesRemoved } from "./unwrap.js";
|
|
13
11
|
/** The operators that compare their operands for equality. */
|
|
@@ -55,114 +53,137 @@ function literalNames(parts) {
|
|
|
55
53
|
return names;
|
|
56
54
|
}
|
|
57
55
|
/**
|
|
58
|
-
* The
|
|
59
|
-
*
|
|
56
|
+
* The reads the key comparisons of a side's files make of the members their literal side
|
|
57
|
+
* names, each made by the declaration holding the comparison. A question the checker leaves
|
|
58
|
+
* unanswered holds what its answer could have read.
|
|
60
59
|
*/
|
|
61
|
-
export
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
60
|
+
export const keyofComparisons = {
|
|
61
|
+
start() {
|
|
62
|
+
const comparisons = [];
|
|
63
|
+
return {
|
|
64
|
+
visit(node, { file, from }) {
|
|
65
|
+
if (isBinaryExpression(node) && EQUALITIES.has(node.operatorToken.kind)) {
|
|
66
|
+
comparisons.push({ file, from, keyed: node.left, other: node.right });
|
|
67
|
+
comparisons.push({ file, from, keyed: node.right, other: node.left });
|
|
68
|
+
}
|
|
69
|
+
if (isSwitchStatement(node)) {
|
|
70
|
+
for (const clause of node.caseBlock.clauses) {
|
|
71
|
+
if (isCaseClause(clause)) {
|
|
72
|
+
comparisons.push({ file, from, keyed: node.expression, other: clause.expression });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
reads({ project, chains }) {
|
|
78
|
+
const queries = project.queries;
|
|
79
|
+
const found = [];
|
|
80
|
+
const named = comparisons.flatMap((one) => {
|
|
81
|
+
const name = nameOf(one.keyed);
|
|
82
|
+
return name === undefined || isElementAccessExpression(parenthesesRemoved(one.other))
|
|
83
|
+
? []
|
|
84
|
+
: [{ ...one, name }];
|
|
85
|
+
});
|
|
86
|
+
if (named.length === 0) {
|
|
87
|
+
return found;
|
|
88
|
+
}
|
|
89
|
+
// The operand's own declaration states the key type, which the checker resolves to
|
|
90
|
+
// the literals it admits and so no longer names the type the key indexes.
|
|
91
|
+
const symbols = queries.symbolsAt(named.map((one) => one.name));
|
|
92
|
+
const keyed = named.flatMap((one, at) => {
|
|
93
|
+
const asked = symbols[at];
|
|
94
|
+
const symbol = asked === UNANSWERED || asked === undefined || (asked.flags & SymbolFlags.Alias) === 0
|
|
95
|
+
? asked
|
|
96
|
+
: queries.aliased(asked);
|
|
97
|
+
if (symbol === UNANSWERED) {
|
|
98
|
+
const text = literalText(one.other);
|
|
99
|
+
found.push({
|
|
100
|
+
kind: "held",
|
|
101
|
+
from: one.from,
|
|
102
|
+
...(text === undefined ? {} : { names: [text] }),
|
|
83
103
|
});
|
|
104
|
+
return [];
|
|
84
105
|
}
|
|
106
|
+
if (symbol === undefined) {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
const operands = symbol.declarations.flatMap((handle) => {
|
|
110
|
+
const declared = project.declarationAt(handle)?.node?.type;
|
|
111
|
+
return declared === undefined ? [] : keyofOperands(declared);
|
|
112
|
+
});
|
|
113
|
+
return operands.length === 0 ? [] : [{ ...one, operands }];
|
|
114
|
+
});
|
|
115
|
+
if (keyed.length === 0) {
|
|
116
|
+
return found;
|
|
85
117
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
const named = comparisons.flatMap((one) => {
|
|
96
|
-
const name = nameOf(one.keyed);
|
|
97
|
-
return name === undefined || isElementAccessExpression(parenthesesRemoved(one.other))
|
|
98
|
-
? []
|
|
99
|
-
: [{ ...one, name }];
|
|
100
|
-
});
|
|
101
|
-
if (named.length === 0) {
|
|
102
|
-
return [];
|
|
103
|
-
}
|
|
104
|
-
// The operand's own declaration states the key type, which the checker resolves to
|
|
105
|
-
// the literals it admits and so no longer names the type the key indexes.
|
|
106
|
-
const symbols = queries.symbolsAt(named.map((one) => one.name));
|
|
107
|
-
const keyed = named.flatMap((one, at) => {
|
|
108
|
-
const asked = symbols[at];
|
|
109
|
-
const symbol = asked === UNANSWERED || asked === undefined || (asked.flags & SymbolFlags.Alias) === 0
|
|
110
|
-
? asked
|
|
111
|
-
: queries.aliased(asked);
|
|
112
|
-
if (symbol === UNANSWERED || symbol === undefined) {
|
|
113
|
-
return [];
|
|
114
|
-
}
|
|
115
|
-
const operands = symbol.declarations.flatMap((handle) => {
|
|
116
|
-
const declared = project.declarationAt(handle)?.node?.type;
|
|
117
|
-
return declared === undefined ? [] : keyofOperands(declared);
|
|
118
|
-
});
|
|
119
|
-
return operands.length === 0 ? [] : [{ ...one, operands }];
|
|
120
|
-
});
|
|
121
|
-
if (keyed.length === 0) {
|
|
122
|
-
return [];
|
|
123
|
-
}
|
|
124
|
-
const nodes = keyed.flatMap((one) => [one.other, ...one.operands]);
|
|
125
|
-
const types = queries.typesAt(nodes);
|
|
126
|
-
const typeOf = new Map();
|
|
127
|
-
nodes.forEach((node, at) => {
|
|
128
|
-
const type = types[at];
|
|
129
|
-
if (type !== UNANSWERED && type !== undefined) {
|
|
130
|
-
typeOf.set(node, type);
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
const partsOf = (type) => {
|
|
134
|
-
const parts = queries.constituents(type);
|
|
135
|
-
return parts === UNANSWERED ? [type] : parts;
|
|
136
|
-
};
|
|
137
|
-
const found = [];
|
|
138
|
-
for (const one of keyed) {
|
|
139
|
-
const literal = typeOf.get(one.other);
|
|
140
|
-
const names = literal === undefined ? [] : literalNames(partsOf(literal));
|
|
141
|
-
if (names.length === 0) {
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
const position = renderPosition(one.file, root, one.other.getStart());
|
|
145
|
-
for (const operand of one.operands) {
|
|
146
|
-
const object = typeOf.get(operand);
|
|
147
|
-
for (const part of object === undefined ? [] : partsOf(object)) {
|
|
148
|
-
const properties = queries.propertiesOf(part);
|
|
149
|
-
for (const property of properties === UNANSWERED ? [] : properties) {
|
|
150
|
-
if (!names.includes(property.name)) {
|
|
151
|
-
continue;
|
|
118
|
+
const nodes = keyed.flatMap((one) => [one.other, ...one.operands]);
|
|
119
|
+
const types = queries.typesAt(nodes);
|
|
120
|
+
const typeOf = new Map();
|
|
121
|
+
nodes.forEach((node, at) => {
|
|
122
|
+
const type = types[at];
|
|
123
|
+
if (type !== undefined) {
|
|
124
|
+
typeOf.set(node, type);
|
|
152
125
|
}
|
|
153
|
-
|
|
126
|
+
});
|
|
127
|
+
const partsOf = (type) => {
|
|
128
|
+
const parts = queries.constituents(type);
|
|
129
|
+
return parts === UNANSWERED ? [type] : parts;
|
|
130
|
+
};
|
|
131
|
+
for (const one of keyed) {
|
|
132
|
+
const literal = typeOf.get(one.other);
|
|
133
|
+
const objects = one.operands.map((operand) => typeOf.get(operand));
|
|
134
|
+
if (literal === UNANSWERED) {
|
|
135
|
+
const known = objects.filter((type) => type !== undefined && type !== UNANSWERED);
|
|
154
136
|
found.push({
|
|
137
|
+
kind: "held",
|
|
155
138
|
from: one.from,
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
use: "read",
|
|
159
|
-
resolution: "keyed",
|
|
160
|
-
test: testFiles.has(position.path),
|
|
139
|
+
types: known.flatMap(partsOf),
|
|
140
|
+
...(known.length < objects.length ? { members: true } : {}),
|
|
161
141
|
});
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
const names = literal === undefined ? [] : literalNames(partsOf(literal));
|
|
145
|
+
if (names.length === 0) {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
for (const object of objects) {
|
|
149
|
+
if (object === UNANSWERED) {
|
|
150
|
+
found.push({ kind: "held", from: one.from, names });
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
for (const part of object === undefined ? [] : partsOf(object)) {
|
|
154
|
+
const properties = queries.propertiesOf(part);
|
|
155
|
+
if (properties === UNANSWERED) {
|
|
156
|
+
found.push({ kind: "held", from: one.from, names });
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
for (const property of properties) {
|
|
160
|
+
if (!names.includes(property.name)) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
for (const to of chains.declarationsOf(property)) {
|
|
164
|
+
found.push({
|
|
165
|
+
kind: "named",
|
|
166
|
+
from: one.from,
|
|
167
|
+
to,
|
|
168
|
+
file: one.file,
|
|
169
|
+
at: one.other,
|
|
170
|
+
use: "read",
|
|
171
|
+
via: "keyed",
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
162
176
|
}
|
|
163
177
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
178
|
+
return found;
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
/** The text a string or number literal operand spells, where it is one. */
|
|
184
|
+
function literalText(node) {
|
|
185
|
+
const at = parenthesesRemoved(node);
|
|
186
|
+
return isStringLiteral(at) || isNumericLiteral(at) || isNoSubstitutionTemplateLiteral(at)
|
|
187
|
+
? at.text
|
|
188
|
+
: undefined;
|
|
168
189
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* and every dead declaration that falls with them, being dead only through them.
|
|
5
5
|
*/
|
|
6
6
|
import { OUTSIDE } from "./graph.js";
|
|
7
|
-
import { PRODUCT } from "
|
|
7
|
+
import { PRODUCT } from "../product.js";
|
|
8
8
|
/** The fewest digits a component identifier's number is written with. */
|
|
9
9
|
const ID_DIGITS = 4;
|
|
10
10
|
/** The identifier of the component at one place of the run's order, counted from one. */
|
|
@@ -1,9 +1,10 @@
|
|
|
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
|
+
import { madeByTestCode } from "../tests/file-class.js";
|
|
7
8
|
/** The position of a declaration the inventory does not hold. */
|
|
8
9
|
export const OUTSIDE = -1;
|
|
9
10
|
/**
|
|
@@ -66,42 +67,63 @@ export function swept(kind) {
|
|
|
66
67
|
export function namesACaller(kind) {
|
|
67
68
|
return NAMES_A_CALLER[kind];
|
|
68
69
|
}
|
|
70
|
+
/** Adds one reference's count to the counts of the declaration it names. */
|
|
71
|
+
function count(counts, test, consumer) {
|
|
72
|
+
if (test) {
|
|
73
|
+
counts.test += 1;
|
|
74
|
+
counts.consumerTest += consumer ? 1 : 0;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
counts.production += 1;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
69
80
|
/**
|
|
70
|
-
* Indexes
|
|
81
|
+
* Indexes the run's inventory, references and roots. `testFiles` are the paths the
|
|
71
82
|
* reference pass classified as test files, so a declaration one holds is a test
|
|
72
83
|
* declaration. A reference to a declaration the inventory does not hold is dropped; one
|
|
73
84
|
* from such a declaration still counts toward its target, because reference counting
|
|
74
85
|
* asks whether any declaration names the target. A root naming nothing is dropped.
|
|
75
86
|
*/
|
|
76
|
-
export function graphOf(symbols, references, roots, testFiles, supportFiles = [], ambient = new Set()) {
|
|
87
|
+
export function graphOf(symbols, references, roots, testFiles, supportFiles = [], ambient = new Set(), sources = {}) {
|
|
77
88
|
const supports = new Set(supportFiles);
|
|
78
89
|
const index = new Map(symbols.map((symbol, at) => [symbol.id, at]));
|
|
79
90
|
const at = (id) => index.get(id) ?? OUTSIDE;
|
|
80
91
|
const tests = new Set(testFiles);
|
|
92
|
+
const unjudged = sources.unjudged ?? new Set();
|
|
81
93
|
const out = symbols.map(() => []);
|
|
82
|
-
const made = symbols.map(() => ({ production: 0, test: 0 }));
|
|
94
|
+
const made = symbols.map(() => ({ production: 0, test: 0, consumerTest: 0 }));
|
|
95
|
+
const guessedMade = symbols.map(() => ({ production: 0, test: 0, consumerTest: 0 }));
|
|
83
96
|
const consumed = [];
|
|
84
|
-
|
|
97
|
+
references.forEach((reference, place) => {
|
|
85
98
|
const to = at(reference.to);
|
|
86
99
|
if (to === OUTSIDE) {
|
|
87
|
-
|
|
100
|
+
return;
|
|
88
101
|
}
|
|
102
|
+
const test = madeByTestCode(reference, supports);
|
|
103
|
+
const consumer = reference.consumer !== undefined;
|
|
104
|
+
const guessed = reference.guessed === true;
|
|
89
105
|
const counts = made[to];
|
|
90
106
|
if (counts !== undefined) {
|
|
91
|
-
|
|
92
|
-
counts.test += 1;
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
95
|
-
counts.production += 1;
|
|
96
|
-
}
|
|
107
|
+
count(counts, test, consumer);
|
|
97
108
|
}
|
|
98
|
-
const
|
|
99
|
-
if (
|
|
109
|
+
const guesses = guessedMade[to];
|
|
110
|
+
if (guessed && guesses !== undefined) {
|
|
111
|
+
count(guesses, test, consumer);
|
|
112
|
+
}
|
|
113
|
+
const edge = {
|
|
114
|
+
to,
|
|
115
|
+
reference: place,
|
|
116
|
+
test,
|
|
117
|
+
guessed,
|
|
118
|
+
evaluation: reference.use === "evaluation",
|
|
119
|
+
consumer,
|
|
120
|
+
};
|
|
121
|
+
if (consumer || reference.unheld === true || unjudged.has(reference.from)) {
|
|
100
122
|
consumed.push(edge);
|
|
101
|
-
|
|
123
|
+
return;
|
|
102
124
|
}
|
|
103
125
|
out[at(reference.from)]?.push(edge);
|
|
104
|
-
}
|
|
126
|
+
});
|
|
105
127
|
const parent = symbols.map((symbol) => at(symbol.parent));
|
|
106
128
|
const exportsOf = symbols.map(() => []);
|
|
107
129
|
symbols.forEach((symbol, position) => {
|
|
@@ -114,13 +136,16 @@ export function graphOf(symbols, references, roots, testFiles, supportFiles = []
|
|
|
114
136
|
for (const root of roots) {
|
|
115
137
|
const position = at(root.id);
|
|
116
138
|
if (position !== OUTSIDE) {
|
|
117
|
-
rooted.push({ at: position, kind: root.kind });
|
|
139
|
+
rooted.push({ at: position, kind: root.kind, source: root.source });
|
|
118
140
|
}
|
|
119
141
|
}
|
|
120
142
|
return {
|
|
121
143
|
symbols,
|
|
122
144
|
out,
|
|
123
145
|
made,
|
|
146
|
+
guessedMade,
|
|
147
|
+
references,
|
|
148
|
+
configurations: sources.configurations ?? references.map(() => []),
|
|
124
149
|
parent,
|
|
125
150
|
test: symbols.map((symbol) => tests.has(symbol.position.path)),
|
|
126
151
|
support: symbols.map((symbol) => supports.has(symbol.position.path)),
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One run's projects as one matrix: every declaration any project judges, keyed by its
|
|
3
|
+
* position and carrying the configurations that hold it, and one graph over every
|
|
4
|
+
* configuration's references and roots, which the sweep judges once (matrix.4, matrix.5).
|
|
5
|
+
*/
|
|
6
|
+
import { graphOf, swept } from "./graph.js";
|
|
7
|
+
import { byPosition } from "../position.js";
|
|
8
|
+
/**
|
|
9
|
+
* What makes one reference one reference, whichever configuration made it. A consumer's
|
|
10
|
+
* position is rendered against its own root, so the consumer is part of what it names.
|
|
11
|
+
*/
|
|
12
|
+
export function referenceKey(reference) {
|
|
13
|
+
const { path, line, column } = reference.position;
|
|
14
|
+
return [
|
|
15
|
+
reference.consumer ?? "",
|
|
16
|
+
reference.from,
|
|
17
|
+
reference.to,
|
|
18
|
+
path,
|
|
19
|
+
String(line),
|
|
20
|
+
String(column),
|
|
21
|
+
reference.use,
|
|
22
|
+
].join("\u0000");
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Indexes what the passes answered for each configuration of one run, in the run's order.
|
|
26
|
+
* A declaration is keyed by its rendered position, with the fields of the first
|
|
27
|
+
* configuration that judges it; one in a file every holder holds only for its references
|
|
28
|
+
* is judged by none and is not in the union, and a reference it makes enters the union as a
|
|
29
|
+
* consumer's does. One reference at one position of a file several configurations compile
|
|
30
|
+
* is one reference; two one configuration makes are two.
|
|
31
|
+
*/
|
|
32
|
+
export function matrixOf(per) {
|
|
33
|
+
const symbols = [];
|
|
34
|
+
const holders = new Map();
|
|
35
|
+
const references = [];
|
|
36
|
+
const makers = [];
|
|
37
|
+
const firstSeen = new Map();
|
|
38
|
+
const judged = new Set();
|
|
39
|
+
const unjudged = new Set();
|
|
40
|
+
const dependents = new Map();
|
|
41
|
+
per.forEach((one, index) => {
|
|
42
|
+
for (const [id, after] of one.dependents ?? []) {
|
|
43
|
+
if (!dependents.has(id)) {
|
|
44
|
+
dependents.set(id, after);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const referenceOnly = new Set(one.referenceOnly);
|
|
48
|
+
for (const symbol of one.symbols) {
|
|
49
|
+
const held = holders.get(symbol.id);
|
|
50
|
+
if (held === undefined) {
|
|
51
|
+
holders.set(symbol.id, [one.configuration]);
|
|
52
|
+
}
|
|
53
|
+
else if (!held.includes(one.configuration)) {
|
|
54
|
+
held.push(one.configuration);
|
|
55
|
+
}
|
|
56
|
+
if (referenceOnly.has(symbol.position.path)) {
|
|
57
|
+
if (swept(symbol.kind)) {
|
|
58
|
+
unjudged.add(symbol.id);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else if (!judged.has(symbol.id)) {
|
|
62
|
+
judged.add(symbol.id);
|
|
63
|
+
symbols.push(symbol);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
for (const reference of one.references) {
|
|
67
|
+
const key = referenceKey(reference);
|
|
68
|
+
const seen = firstSeen.get(key);
|
|
69
|
+
if (seen !== undefined && seen.index !== index) {
|
|
70
|
+
for (const place of seen.places) {
|
|
71
|
+
const making = makers[place];
|
|
72
|
+
if (making !== undefined && !making.includes(one.configuration)) {
|
|
73
|
+
making.push(one.configuration);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const places = seen?.places ?? [];
|
|
79
|
+
if (seen === undefined) {
|
|
80
|
+
firstSeen.set(key, { index, places });
|
|
81
|
+
}
|
|
82
|
+
places.push(references.length);
|
|
83
|
+
references.push(reference);
|
|
84
|
+
makers.push([one.configuration]);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
for (const id of judged) {
|
|
88
|
+
unjudged.delete(id);
|
|
89
|
+
}
|
|
90
|
+
symbols.sort((a, b) => byPosition(a.position, b.position));
|
|
91
|
+
const testUsed = new Set(per.flatMap((one) => one.testUsedFiles ?? []));
|
|
92
|
+
return {
|
|
93
|
+
configurations: per.map((one) => one.configuration),
|
|
94
|
+
union: graphOf(symbols, references, per.flatMap((one) => one.roots), [...new Set(per.flatMap((one) => one.testFiles))], [...new Set(per.flatMap((one) => one.supportFiles ?? []))], new Set(per.flatMap((one) => [...(one.ambient ?? [])])), { unjudged, configurations: makers }),
|
|
95
|
+
heldIn: symbols.map((symbol) => holders.get(symbol.id) ?? []),
|
|
96
|
+
testUsed: symbols.map((symbol) => testUsed.has(symbol.position.path)),
|
|
97
|
+
dependents,
|
|
98
|
+
};
|
|
99
|
+
}
|