@cplieger/deadset-ts 8.0.0-dev.15 → 8.0.0-dev.17
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 +94 -54
- package/dist/src/augmentations.js +23 -24
- package/dist/src/bindings.js +382 -0
- package/dist/src/calls.js +25 -8
- package/dist/src/commands/read.js +6 -7
- 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 +9 -10
- package/dist/src/contextual-uses.js +1 -17
- package/dist/src/contract/config.generated.js +5 -2
- package/dist/src/contract/contract.generated.js +119 -2
- package/dist/src/contract/report.generated.js +6 -2
- package/dist/src/dependencies.js +98 -71
- package/dist/src/destructuring.js +1 -19
- package/dist/src/element-access.js +179 -164
- package/dist/src/entries/build-scripts.js +222 -0
- package/dist/src/{configuration-files.js → entries/configuration-files.js} +22 -64
- package/dist/src/{convention-rows.js → entries/convention-rows.js} +120 -27
- package/dist/src/entries/conventions.js +739 -0
- package/dist/src/{html-entries.js → entries/html-entries.js} +9 -12
- package/dist/src/{mdx-entries.js → entries/mdx-entries.js} +3 -3
- package/dist/src/entries/run-directories.js +77 -0
- package/dist/src/entries/sources.js +144 -0
- package/dist/src/entries/static-module.js +670 -0
- package/dist/src/entries/test-runners.js +684 -0
- package/dist/src/{workflow-steps.js → entries/workflow-steps.js} +18 -8
- package/dist/src/extensions.js +37 -0
- package/dist/src/facts.js +38 -0
- package/dist/src/findings/completion.js +40 -4
- package/dist/src/findings/emitters.js +5 -8
- 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/self-check.js +36 -20
- package/dist/src/findings-pass.js +5 -6
- package/dist/src/framework-lifecycle.js +42 -15
- package/dist/src/fs/manifest.js +15 -20
- package/dist/src/generated-file.js +172 -29
- package/dist/src/graph.js +14 -8
- package/dist/src/import-bindings.js +73 -28
- package/dist/src/index/sites.js +557 -0
- package/dist/src/index.js +1 -1
- package/dist/src/indexed-access.js +523 -478
- package/dist/src/injection-container.js +1 -1
- package/dist/src/interface-satisfaction.js +1 -1
- package/dist/src/inventory/commonjs.js +62 -17
- package/dist/src/inventory/enumerate.js +2 -1
- package/dist/src/javascript-file.js +26 -11
- package/dist/src/keyof-comparisons.js +124 -103
- package/dist/src/load/classify.js +28 -9
- package/dist/src/load/diagnostics.js +8 -1
- package/dist/src/load/discover.js +15 -7
- package/dist/src/load/expected-module.js +2 -2
- package/dist/src/load/plan.js +2 -2
- package/dist/src/load/specifiers.js +2 -4
- package/dist/src/load/synthesize.js +10 -5
- package/dist/src/load/workspace-manifests.js +1 -1
- package/dist/src/load/workspace-resolution.js +2 -2
- package/dist/src/matrix.js +12 -17
- package/dist/src/module-declarations.js +70 -85
- package/dist/src/overrides.js +134 -112
- package/dist/src/paths.js +56 -1
- package/dist/src/platform-calls.js +78 -0
- package/dist/src/platform-symbols.js +134 -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 +151 -0
- package/dist/src/references/sources.js +450 -0
- package/dist/src/references.js +111 -1116
- package/dist/src/resolve.js +1 -1
- package/dist/src/{configured-declarations.js → roots/configured-declarations.js} +7 -4
- package/dist/src/{entry-point-gaps.js → roots/entry-point-gaps.js} +5 -8
- package/dist/src/{manifest.js → roots/manifest.js} +4 -4
- package/dist/src/roots/published-reach.js +412 -0
- package/dist/src/roots/root-set.js +489 -0
- package/dist/src/roots/unseen-loads.js +236 -0
- package/dist/src/serialization-contract.js +9 -4
- package/dist/src/source-files.js +5 -9
- package/dist/src/stores.js +4 -2
- package/dist/src/suppress.js +30 -32
- package/dist/src/sweep.js +25 -13
- 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} +5 -12
- package/dist/src/type-parameter-reads.js +178 -139
- package/dist/src/unimplemented-callees.js +262 -242
- package/dist/src/verbs/analyze.js +15 -3
- package/dist/src/verbs/describe.js +5 -3
- package/dist/src/verbs/explain.js +50 -28
- package/dist/src/verbs/print-retained.js +2 -2
- package/jsr.json +1 -1
- package/package.json +3 -3
- package/src/alias-chain.ts +0 -7
- package/src/amd.ts +130 -0
- package/src/analysis.ts +156 -70
- package/src/augmentations.ts +41 -27
- package/src/automatic-types.ts +1 -1
- package/src/bindings.ts +460 -0
- package/src/calls.ts +25 -7
- package/src/commands/read.ts +6 -8
- 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 +12 -11
- package/src/contextual-uses.ts +2 -22
- package/src/contract/config.generated.ts +5 -2
- package/src/contract/contract.generated.ts +138 -2
- package/src/contract/report.generated.ts +6 -2
- package/src/dependencies.ts +117 -71
- package/src/destructuring.ts +1 -31
- package/src/element-access.ts +188 -182
- package/src/entries/build-scripts.ts +292 -0
- package/src/{configuration-files.ts → entries/configuration-files.ts} +33 -82
- package/src/{convention-rows.ts → entries/convention-rows.ts} +167 -28
- package/src/{conventions.ts → entries/conventions.ts} +349 -523
- package/src/{html-entries.ts → entries/html-entries.ts} +19 -18
- package/src/{mdx-entries.ts → entries/mdx-entries.ts} +5 -5
- package/src/entries/run-directories.ts +108 -0
- package/src/entries/sources.ts +266 -0
- package/src/entries/static-module.ts +858 -0
- package/src/entries/test-runners.ts +868 -0
- package/src/{workflow-steps.ts → entries/workflow-steps.ts} +29 -11
- package/src/exempt.ts +3 -2
- package/src/extensions.ts +45 -0
- package/src/facts.ts +46 -0
- package/src/file-facts.ts +1 -1
- package/src/findings/boundary.ts +2 -0
- package/src/findings/completion.ts +43 -4
- package/src/findings/emitter.ts +1 -1
- package/src/findings/emitters.ts +14 -10
- package/src/{implementations.ts → findings/implementations.ts} +4 -4
- package/src/{in-operands.ts → findings/in-operands.ts} +3 -3
- package/src/findings/self-check.ts +41 -22
- package/src/findings/visibility-narrowing.ts +1 -1
- package/src/findings-pass.ts +5 -12
- package/src/framework-lifecycle.ts +43 -15
- package/src/fs/manifest.ts +25 -21
- package/src/generated-file.ts +240 -36
- package/src/graph.ts +27 -11
- package/src/import-bindings.ts +98 -29
- package/src/index/sites.ts +781 -0
- package/src/index.ts +1 -1
- package/src/indexed-access.ts +570 -527
- package/src/injection-container.ts +1 -1
- package/src/interface-satisfaction.ts +1 -1
- package/src/inventory/commonjs.ts +102 -17
- package/src/inventory/enumerate.ts +2 -1
- package/src/javascript-file.ts +29 -12
- package/src/keyof-comparisons.ts +131 -116
- package/src/load/classify.ts +49 -13
- package/src/load/diagnostics.ts +11 -1
- package/src/load/discover.ts +27 -11
- package/src/load/expected-module.ts +8 -2
- package/src/load/plan.ts +11 -2
- package/src/load/specifiers.ts +8 -5
- package/src/load/synthesize.ts +12 -6
- package/src/load/test-globals.ts +1 -1
- package/src/load/workspace-manifests.ts +1 -1
- package/src/load/workspace-resolution.ts +9 -2
- package/src/load/workspace-run.ts +1 -1
- package/src/matrix.ts +13 -17
- package/src/module-declarations.ts +74 -98
- package/src/overrides.ts +162 -134
- package/src/paths.ts +65 -1
- package/src/platform-calls.ts +119 -0
- package/src/platform-symbols.ts +189 -0
- package/src/references/read-rule.ts +60 -0
- package/src/references/read-rules.ts +55 -0
- package/src/references/sink.ts +213 -0
- package/src/references/sources.ts +632 -0
- package/src/references.ts +174 -1492
- package/src/report.ts +2 -2
- package/src/resolve.ts +1 -1
- package/src/{configured-declarations.ts → roots/configured-declarations.ts} +10 -7
- package/src/{entry-point-gaps.ts → roots/entry-point-gaps.ts} +5 -8
- package/src/{manifest.ts → roots/manifest.ts} +5 -5
- package/src/roots/published-reach.ts +481 -0
- package/src/{roots.ts → roots/root-set.ts} +203 -630
- package/src/roots/unseen-loads.ts +345 -0
- package/src/serialization-contract.ts +9 -4
- package/src/source-files.ts +5 -10
- package/src/stores.ts +13 -2
- package/src/suppress.ts +48 -33
- package/src/sweep.ts +37 -14
- package/src/symbol-keys.ts +102 -102
- package/src/tests/file-class.ts +118 -0
- package/src/{outside-tests.ts → tests/outside.ts} +16 -10
- package/src/{test-support.ts → tests/support.ts} +15 -21
- package/src/type-parameter-reads.ts +187 -158
- package/src/unimplemented-callees.ts +297 -280
- package/src/verbs/analyze.ts +19 -5
- package/src/verbs/describe.ts +5 -2
- package/src/verbs/explain.ts +75 -32
- package/src/verbs/print-retained.ts +2 -2
- package/dist/src/build-scripts.js +0 -418
- package/dist/src/conventions.js +0 -907
- package/dist/src/entry-points.js +0 -444
- package/dist/src/own-body.js +0 -28
- package/dist/src/roots.js +0 -865
- package/dist/src/unseen-loads.js +0 -189
- package/src/build-scripts.ts +0 -536
- package/src/entry-points.ts +0 -584
- package/src/own-body.ts +0 -42
- package/src/unseen-loads.ts +0 -282
|
@@ -8,8 +8,6 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { isArrowFunction, isAsExpression, isCallExpression, isClassDeclaration, isClassExpression, isFunctionDeclaration, isFunctionExpression, isHeritageClause, isIdentifier, isMethodDeclaration, isNewExpression, isObjectLiteralExpression, isParameterDeclaration, isParenthesizedExpression, isPropertyAccessExpression, isPropertyAssignment, isPropertyDeclaration, isSatisfiesExpression, isTypeReferenceNode, isVariableDeclaration, } from "@typescript/native/ast";
|
|
10
10
|
import { SymbolFlags } from "@typescript/native/sync";
|
|
11
|
-
import { nodeKey } from "./inventory.js";
|
|
12
|
-
import { renderPosition } from "./position.js";
|
|
13
11
|
import { UNANSWERED } from "./query.js";
|
|
14
12
|
/** The value an `as` expression asserts a type of, through every `as` and parenthesis around it. */
|
|
15
13
|
function castOperand(node) {
|
|
@@ -61,252 +59,274 @@ function literalsOf(argument) {
|
|
|
61
59
|
];
|
|
62
60
|
}
|
|
63
61
|
/**
|
|
64
|
-
* The reads the object-literal arguments of
|
|
65
|
-
*
|
|
62
|
+
* The reads the object-literal arguments of a side's files make of the members of the types
|
|
63
|
+
* they are checked against, where the callee has no implementation in the target, and the
|
|
64
|
+
* reads of every member of each target type an outside value is handed over as. A type the
|
|
65
|
+
* checker leaves unanswered holds what its answer could have read.
|
|
66
66
|
*/
|
|
67
|
-
export
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
if (isClassDeclaration(node) || isClassExpression(node)) {
|
|
97
|
-
for (const clause of node.heritageClauses ?? []) {
|
|
98
|
-
if (isHeritageClause(clause)) {
|
|
99
|
-
implementing.push(...clause.types.map((one) => isTypeReferenceNode(one) ? one.typeName : one.expression));
|
|
67
|
+
export const unimplementedCallees = {
|
|
68
|
+
start() {
|
|
69
|
+
const passing = [];
|
|
70
|
+
const implementing = [];
|
|
71
|
+
const asserted = [];
|
|
72
|
+
const constructed = [];
|
|
73
|
+
const handing = [];
|
|
74
|
+
return {
|
|
75
|
+
visit(node, { file, from }) {
|
|
76
|
+
if ((isCallExpression(node) || isNewExpression(node)) &&
|
|
77
|
+
(node.arguments ?? []).some((argument) => isObjectLiteralExpression(argument))) {
|
|
78
|
+
passing.push({ file, from, call: node });
|
|
79
|
+
}
|
|
80
|
+
if (isNewExpression(node)) {
|
|
81
|
+
constructed.push({ file, from, call: node });
|
|
82
|
+
}
|
|
83
|
+
if ((isCallExpression(node) || isNewExpression(node)) &&
|
|
84
|
+
(node.arguments ?? []).some((argument) => isFunctionLike(argument) || isIdentifier(argument))) {
|
|
85
|
+
handing.push(node);
|
|
86
|
+
}
|
|
87
|
+
if (isAsExpression(node) && !isAsExpression(node.parent)) {
|
|
88
|
+
asserted.push({ file, from, node, operand: castOperand(node) });
|
|
89
|
+
}
|
|
90
|
+
if (isClassDeclaration(node) || isClassExpression(node)) {
|
|
91
|
+
for (const clause of node.heritageClauses ?? []) {
|
|
92
|
+
if (isHeritageClause(clause)) {
|
|
93
|
+
implementing.push(...clause.types.map((one) => isTypeReferenceNode(one) ? one.typeName : one.expression));
|
|
94
|
+
}
|
|
100
95
|
}
|
|
101
96
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
});
|
|
174
|
-
const named = passed.filter((argument) => isIdentifier(argument));
|
|
175
|
-
const reached = passed.some((argument) => isFunctionLike(argument)) ||
|
|
176
|
-
queries
|
|
177
|
-
.symbolsAt(named)
|
|
178
|
-
.some((symbol) => declarationsOf(resolved(symbol)).some((node) => ownFunction(node)));
|
|
179
|
-
reachedParameters.set(parameter, reached);
|
|
180
|
-
return reached;
|
|
181
|
-
};
|
|
182
|
-
/** Whether some function, class or object literal of the target stands for a signature. */
|
|
183
|
-
const standsFor = (passed) => {
|
|
184
|
-
const signature = queries.resolvedSignature(passed.call);
|
|
185
|
-
const handle = signature === UNANSWERED || signature === undefined ? undefined : signature.declaration;
|
|
186
|
-
const declaration = handle === undefined ? undefined : project.declarationAt(handle)?.node;
|
|
187
|
-
if (declaration === undefined) {
|
|
188
|
-
return true;
|
|
189
|
-
}
|
|
190
|
-
if (hasBody(declaration) || overloadImplemented(declaration)) {
|
|
191
|
-
return own.has(handle?.path ?? "");
|
|
192
|
-
}
|
|
193
|
-
const holder = declaration.parent;
|
|
194
|
-
if (holder !== undefined && implemented.has(holder)) {
|
|
195
|
-
return true;
|
|
196
|
-
}
|
|
197
|
-
const callee = passed.call.expression;
|
|
198
|
-
const name = isPropertyAccessExpression(callee) ? callee.name : callee;
|
|
199
|
-
const [named] = queries.symbolsAt([name]);
|
|
200
|
-
return declarationsOf(resolved(named)).some((node) => isParameterDeclaration(node)
|
|
201
|
-
? own.has(node.getSourceFile().fileName) && parameterReached(node)
|
|
202
|
-
: ownFunction(node) && !isFunctionLike(node));
|
|
203
|
-
};
|
|
204
|
-
const literals = [];
|
|
205
|
-
for (const passed of passing) {
|
|
206
|
-
if (standsFor(passed)) {
|
|
207
|
-
continue;
|
|
208
|
-
}
|
|
209
|
-
for (const argument of passed.call.arguments ?? []) {
|
|
210
|
-
literals.push(...literalsOf(argument).map((literal) => ({ passed, literal })));
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
const contexts = queries.contextualTypes(literals.map((one) => one.literal));
|
|
214
|
-
const found = [];
|
|
215
|
-
literals.forEach(({ passed, literal }, at) => {
|
|
216
|
-
const context = contexts[at];
|
|
217
|
-
if (context === UNANSWERED || context === undefined) {
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
const parts = queries.constituents(context);
|
|
221
|
-
const position = renderPosition(passed.file, root, literal.getStart());
|
|
222
|
-
for (const part of parts === UNANSWERED ? [context] : parts) {
|
|
223
|
-
const properties = queries.propertiesOf(part);
|
|
224
|
-
for (const property of properties === UNANSWERED ? [] : properties) {
|
|
225
|
-
for (const to of chains.declarationsOf(property)) {
|
|
226
|
-
found.push({
|
|
227
|
-
from: passed.from,
|
|
228
|
-
to,
|
|
229
|
-
position,
|
|
230
|
-
use: "read",
|
|
231
|
-
resolution: "contextual",
|
|
232
|
-
test: testFiles.has(position.path),
|
|
97
|
+
// An object literal typed as an interface implements that interface's methods.
|
|
98
|
+
if (isVariableDeclaration(node) &&
|
|
99
|
+
node.type !== undefined &&
|
|
100
|
+
isTypeReferenceNode(node.type) &&
|
|
101
|
+
node.initializer !== undefined &&
|
|
102
|
+
isObjectLiteralExpression(node.initializer)) {
|
|
103
|
+
implementing.push(node.type.typeName);
|
|
104
|
+
}
|
|
105
|
+
if ((isSatisfiesExpression(node) || isAsExpression(node)) &&
|
|
106
|
+
isObjectLiteralExpression(node.expression) &&
|
|
107
|
+
isTypeReferenceNode(node.type)) {
|
|
108
|
+
implementing.push(node.type.typeName);
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
reads({ project, chains }) {
|
|
112
|
+
const queries = project.queries;
|
|
113
|
+
const own = project.ownPaths();
|
|
114
|
+
const found = [];
|
|
115
|
+
if (passing.length === 0 && asserted.length === 0 && constructed.length === 0) {
|
|
116
|
+
return found;
|
|
117
|
+
}
|
|
118
|
+
const resolved = (symbol) => {
|
|
119
|
+
if (symbol === UNANSWERED || symbol === undefined) {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
if ((symbol.flags & SymbolFlags.Alias) === 0) {
|
|
123
|
+
return symbol;
|
|
124
|
+
}
|
|
125
|
+
const target = queries.aliased(symbol);
|
|
126
|
+
return target === UNANSWERED ? undefined : target;
|
|
127
|
+
};
|
|
128
|
+
const declarationsOf = (symbol) => (symbol?.declarations ?? []).flatMap((handle) => {
|
|
129
|
+
const node = project.declarationAt(handle)?.node;
|
|
130
|
+
return node === undefined ? [] : [node];
|
|
131
|
+
});
|
|
132
|
+
const implemented = new Set(queries.symbolsAt(implementing).flatMap((symbol) => declarationsOf(resolved(symbol))));
|
|
133
|
+
/** Whether a declaration is a function or a class of the target, or a variable or property it initializes. */
|
|
134
|
+
const ownFunction = (node) => own.has(node.getSourceFile().fileName) &&
|
|
135
|
+
(isFunctionLike(node) ||
|
|
136
|
+
((isVariableDeclaration(node) ||
|
|
137
|
+
isPropertyAssignment(node) ||
|
|
138
|
+
isPropertyDeclaration(node)) &&
|
|
139
|
+
isFunctionLike(node.initializer)));
|
|
140
|
+
/**
|
|
141
|
+
* Whether a function or a class of the target reaches one parameter of a function of the
|
|
142
|
+
* target, passed for it by a call that names the function. A call through another name
|
|
143
|
+
* is not followed, so its callee keeps no implementation and its literal reads more.
|
|
144
|
+
*/
|
|
145
|
+
const reachedParameters = new Map();
|
|
146
|
+
const parameterReached = (parameter) => {
|
|
147
|
+
const known = reachedParameters.get(parameter);
|
|
148
|
+
if (known !== undefined) {
|
|
149
|
+
return known;
|
|
150
|
+
}
|
|
151
|
+
const fn = parameter.parent;
|
|
152
|
+
const fnName = nameOf(fn) ?? (isVariableDeclaration(fn.parent) ? nameOf(fn.parent) : undefined);
|
|
153
|
+
const at = fn.parameters?.indexOf(parameter) ?? -1;
|
|
154
|
+
const passed = handing.flatMap((call) => {
|
|
155
|
+
const callee = call.expression;
|
|
156
|
+
const name = isPropertyAccessExpression(callee) ? callee.name : callee;
|
|
157
|
+
if (fnName === undefined || !isIdentifier(name) || name.text !== fnName) {
|
|
158
|
+
return [];
|
|
159
|
+
}
|
|
160
|
+
const signature = queries.resolvedSignature(call);
|
|
161
|
+
const handle = signature === UNANSWERED || signature === undefined
|
|
162
|
+
? undefined
|
|
163
|
+
: signature.declaration;
|
|
164
|
+
const argument = handle === undefined || project.declarationAt(handle)?.node !== fn
|
|
165
|
+
? undefined
|
|
166
|
+
: (call.arguments ?? [])[at];
|
|
167
|
+
return argument === undefined || at < 0 ? [] : [argument];
|
|
233
168
|
});
|
|
169
|
+
const named = passed.filter((argument) => isIdentifier(argument));
|
|
170
|
+
const reached = passed.some((argument) => isFunctionLike(argument)) ||
|
|
171
|
+
queries
|
|
172
|
+
.symbolsAt(named)
|
|
173
|
+
.some((symbol) => declarationsOf(resolved(symbol)).some((node) => ownFunction(node)));
|
|
174
|
+
reachedParameters.set(parameter, reached);
|
|
175
|
+
return reached;
|
|
176
|
+
};
|
|
177
|
+
/** Whether some function, class or object literal of the target stands for a signature. */
|
|
178
|
+
const standsFor = (passed) => {
|
|
179
|
+
const signature = queries.resolvedSignature(passed.call);
|
|
180
|
+
// A callee the checker cannot resolve may be code the target cannot see.
|
|
181
|
+
if (signature === UNANSWERED) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
const handle = signature?.declaration;
|
|
185
|
+
const declaration = handle === undefined ? undefined : project.declarationAt(handle)?.node;
|
|
186
|
+
if (declaration === undefined) {
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
if (hasBody(declaration) || overloadImplemented(declaration)) {
|
|
190
|
+
return own.has(handle?.path ?? "");
|
|
191
|
+
}
|
|
192
|
+
const holder = declaration.parent;
|
|
193
|
+
if (holder !== undefined && implemented.has(holder)) {
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
const callee = passed.call.expression;
|
|
197
|
+
const name = isPropertyAccessExpression(callee) ? callee.name : callee;
|
|
198
|
+
const [named] = queries.symbolsAt([name]);
|
|
199
|
+
return declarationsOf(resolved(named)).some((node) => isParameterDeclaration(node)
|
|
200
|
+
? own.has(node.getSourceFile().fileName) && parameterReached(node)
|
|
201
|
+
: ownFunction(node) && !isFunctionLike(node));
|
|
202
|
+
};
|
|
203
|
+
const literals = [];
|
|
204
|
+
for (const passed of passing) {
|
|
205
|
+
if (standsFor(passed)) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
for (const argument of passed.call.arguments ?? []) {
|
|
209
|
+
literals.push(...literalsOf(argument).map((literal) => ({ passed, literal })));
|
|
210
|
+
}
|
|
234
211
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
: queries.returnTypeOf(signature);
|
|
278
|
-
return result !== UNANSWERED && result !== undefined && !result.isTypeParameter();
|
|
279
|
-
})
|
|
280
|
-
.map((one) => ({ file: one.file, from: one.from, node: one.call }));
|
|
281
|
-
const handed = [...fromCasts, ...fromConstructors];
|
|
282
|
-
const types = queries.typesAt(handed.map((one) => one.node));
|
|
283
|
-
const reads = [];
|
|
284
|
-
handed.forEach((one, at) => {
|
|
285
|
-
const type = types[at];
|
|
286
|
-
if (type === UNANSWERED || type === undefined) {
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
const position = renderPosition(one.file, root, one.node.getStart());
|
|
290
|
-
const parts = queries.constituents(type);
|
|
291
|
-
for (const part of parts === UNANSWERED ? [type] : parts) {
|
|
292
|
-
if (!ownObject(part)) {
|
|
293
|
-
continue;
|
|
212
|
+
const contexts = queries.contextualTypes(literals.map((one) => one.literal));
|
|
213
|
+
literals.forEach(({ passed, literal }, at) => {
|
|
214
|
+
const context = contexts[at];
|
|
215
|
+
if (context === UNANSWERED) {
|
|
216
|
+
found.push({ kind: "held", from: passed.from, names: propertyNames(literal) });
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
if (context === undefined) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const parts = queries.constituents(context);
|
|
223
|
+
for (const part of parts === UNANSWERED ? [context] : parts) {
|
|
224
|
+
const properties = queries.propertiesOf(part);
|
|
225
|
+
if (properties === UNANSWERED) {
|
|
226
|
+
found.push({ kind: "held", from: passed.from, names: propertyNames(literal) });
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
for (const property of properties) {
|
|
230
|
+
for (const to of chains.declarationsOf(property)) {
|
|
231
|
+
found.push({
|
|
232
|
+
kind: "named",
|
|
233
|
+
from: passed.from,
|
|
234
|
+
to,
|
|
235
|
+
file: passed.file,
|
|
236
|
+
at: literal,
|
|
237
|
+
use: "read",
|
|
238
|
+
via: "contextual",
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
handedOver();
|
|
245
|
+
return found;
|
|
246
|
+
/** Whether a type is an object no file of the project declares, a platform object's among them. */
|
|
247
|
+
function outsideValue(type) {
|
|
248
|
+
if (!type.isObjectType()) {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
const symbol = queries.symbolOfType(type);
|
|
252
|
+
const declared = symbol === UNANSWERED ? undefined : symbol;
|
|
253
|
+
return declared?.declarations.every((handle) => !own.has(handle.path)) === true;
|
|
294
254
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
to,
|
|
301
|
-
position,
|
|
302
|
-
use: "read",
|
|
303
|
-
resolution: "contextual",
|
|
304
|
-
test: testFiles.has(position.path),
|
|
305
|
-
});
|
|
255
|
+
/** Whether a type is an object type a file of the project declares. */
|
|
256
|
+
function ownObject(type) {
|
|
257
|
+
const known = type === UNANSWERED ? undefined : type;
|
|
258
|
+
if (!known?.isObjectType()) {
|
|
259
|
+
return false;
|
|
306
260
|
}
|
|
261
|
+
const symbol = queries.symbolOfType(known);
|
|
262
|
+
const declared = symbol === UNANSWERED ? undefined : symbol;
|
|
263
|
+
return declared?.declarations.some((handle) => own.has(handle.path)) === true;
|
|
307
264
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
265
|
+
/** The reads of every member of each target type an outside value is handed over as. */
|
|
266
|
+
function handedOver() {
|
|
267
|
+
const operandTypes = queries.typesAt(asserted.map((one) => one.operand));
|
|
268
|
+
// An operand the checker cannot type may be an outside value.
|
|
269
|
+
const fromCasts = asserted.filter((_one, at) => {
|
|
270
|
+
const type = operandTypes[at];
|
|
271
|
+
return type === UNANSWERED || (type !== undefined && outsideValue(type));
|
|
272
|
+
});
|
|
273
|
+
const newTypes = queries.typesAt(constructed.map((one) => one.call));
|
|
274
|
+
// A construct signature whose declared result is one of its type parameters names no
|
|
275
|
+
// type the target declares, so only a result the signature itself names is handed over.
|
|
276
|
+
const fromConstructors = constructed
|
|
277
|
+
.filter((one, at) => {
|
|
278
|
+
if (!ownObject(newTypes[at]) || standsFor(one)) {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
const signature = queries.resolvedSignature(one.call);
|
|
282
|
+
const result = signature === UNANSWERED || signature === undefined
|
|
283
|
+
? undefined
|
|
284
|
+
: queries.returnTypeOf(signature);
|
|
285
|
+
return result !== UNANSWERED && result !== undefined && !result.isTypeParameter();
|
|
286
|
+
})
|
|
287
|
+
.map((one) => ({ file: one.file, from: one.from, node: one.call }));
|
|
288
|
+
const handed = [...fromCasts, ...fromConstructors];
|
|
289
|
+
const types = queries.typesAt(handed.map((one) => one.node));
|
|
290
|
+
handed.forEach((one, at) => {
|
|
291
|
+
const type = types[at];
|
|
292
|
+
if (type === UNANSWERED) {
|
|
293
|
+
found.push({ kind: "held", from: one.from, members: true });
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
if (type === undefined) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const parts = queries.constituents(type);
|
|
300
|
+
for (const part of parts === UNANSWERED ? [type] : parts) {
|
|
301
|
+
if (!ownObject(part)) {
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
const properties = queries.propertiesOf(part);
|
|
305
|
+
if (properties === UNANSWERED) {
|
|
306
|
+
found.push({ kind: "held", from: one.from, types: [part] });
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
for (const property of properties) {
|
|
310
|
+
for (const to of chains.declarationsOf(property)) {
|
|
311
|
+
found.push({
|
|
312
|
+
kind: "named",
|
|
313
|
+
from: one.from,
|
|
314
|
+
to,
|
|
315
|
+
file: one.file,
|
|
316
|
+
at: one.node,
|
|
317
|
+
use: "read",
|
|
318
|
+
via: "contextual",
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
};
|
|
327
|
+
},
|
|
328
|
+
};
|
|
329
|
+
/** The names an object literal's own properties spell. */
|
|
330
|
+
function propertyNames(literal) {
|
|
331
|
+
return literal.properties.flatMap((property) => nameOf(property) ?? []);
|
|
312
332
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { CONTRACT_VERSION, EXIT_CLEAN } from "../contract/contract.generated.js";
|
|
2
|
-
import { runAnalysis, runBaselineWrite, unansweredCounts, } from "../analysis.js";
|
|
2
|
+
import { productionMode, runAnalysis, runBaselineWrite, unansweredCounts, } from "../analysis.js";
|
|
3
3
|
import { kindOf } from "../config.js";
|
|
4
4
|
import { CONFORMANCE, DECLARED_GAPS } from "../conformance.js";
|
|
5
5
|
import { analyzerProvenance, verdictOf } from "../findings-pass.js";
|
|
6
|
+
import { factsDocument } from "../facts.js";
|
|
6
7
|
import { dirnamePath, joinPath, normalizePath, relativePath, resolvePath } from "../paths.js";
|
|
7
8
|
import { moduleIdentity } from "../load/discover.js";
|
|
8
9
|
import { partialNotes } from "../partial.js";
|
|
@@ -21,7 +22,7 @@ import { diagnose, message } from "../product.js";
|
|
|
21
22
|
import { ownVersion } from "./version.js";
|
|
22
23
|
/** The options `analyze` takes beside the ones every verb takes. */
|
|
23
24
|
export const ANALYZE_OPTIONS = {
|
|
24
|
-
plain: ["report", "baseline-write", "exit-code", "template"],
|
|
25
|
+
plain: ["report", "facts", "baseline-write", "exit-code", "template"],
|
|
25
26
|
repeatable: ["format"],
|
|
26
27
|
};
|
|
27
28
|
/** The two values `--exit-code` takes; on, the default, puts the run's verdict in the exit code. */
|
|
@@ -211,8 +212,13 @@ export const analyzeVerb = ({ err, host, inputs, scope, openClient, option, repe
|
|
|
211
212
|
const { config, provenance } = resolve(inputs());
|
|
212
213
|
const invoked = host.workingDirectory();
|
|
213
214
|
const baseline = option("baseline-write");
|
|
215
|
+
const facts = option("facts");
|
|
216
|
+
if (facts === "") {
|
|
217
|
+
throw new UsageError("--facts names the path the facts document is written to, and named none");
|
|
218
|
+
}
|
|
214
219
|
const documents = {
|
|
215
220
|
report: resolvePath(invoked, reportPath),
|
|
221
|
+
facts: facts === undefined ? undefined : resolvePath(invoked, facts),
|
|
216
222
|
formats: formatsOf(repeated("format"), config.reporters.formats),
|
|
217
223
|
baseline: baseline === undefined ? undefined : resolvePath(invoked, baseline),
|
|
218
224
|
};
|
|
@@ -222,7 +228,7 @@ export const analyzeVerb = ({ err, host, inputs, scope, openClient, option, repe
|
|
|
222
228
|
const version = ownVersion(host);
|
|
223
229
|
let recorded;
|
|
224
230
|
const root = runRelative(invoked, targetRoot);
|
|
225
|
-
const mode =
|
|
231
|
+
const mode = productionMode(config);
|
|
226
232
|
let analysis;
|
|
227
233
|
if (documents.baseline === undefined) {
|
|
228
234
|
analysis = runAnalysis(openClient(config), host, scoped, config, provenance, mode);
|
|
@@ -281,6 +287,12 @@ export const analyzeVerb = ({ err, host, inputs, scope, openClient, option, repe
|
|
|
281
287
|
throw productFault(`built a report that breaks the report schema at ${refused.pointer || "the document"}: ${refused.keyword}: ${refused.reason}`);
|
|
282
288
|
}
|
|
283
289
|
writeDocument(host, documents.report, "name a path the run can write", () => json(report));
|
|
290
|
+
const factsPath = documents.facts;
|
|
291
|
+
if (factsPath !== undefined) {
|
|
292
|
+
writeDocument(host, factsPath, "name a path the run can write", () => [
|
|
293
|
+
factsDocument(version, run.generated, run.bindings),
|
|
294
|
+
]);
|
|
295
|
+
}
|
|
284
296
|
const options = {
|
|
285
297
|
readSource: (path) => host.readFile(joinPath(targetRoot, path)),
|
|
286
298
|
template,
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { CONTRACT_VERSION, EXIT_CLEAN, EXIT_USAGE, SCHEMA_VERSIONS, } from "../contract/contract.generated.js";
|
|
1
|
+
import { CONTRACT_VERSION, EXIT_CLEAN, EXIT_USAGE, FACTS_VERSION, SCHEMA_VERSIONS, } from "../contract/contract.generated.js";
|
|
2
2
|
import { CONFORMANCE } from "../conformance.js";
|
|
3
3
|
import {} from "./verb.js";
|
|
4
4
|
import { diagnose, LANGUAGE, PRODUCT } from "../product.js";
|
|
5
5
|
import { ownVersion } from "./version.js";
|
|
6
6
|
/**
|
|
7
7
|
* The document `describe` writes: the analyzer, the Contract version it implements, the
|
|
8
|
-
* report schema versions it reads, the languages it claims,
|
|
9
|
-
* the conformance corpus, a member the document omits where
|
|
8
|
+
* report schema versions it reads, the languages it claims, the facts version it writes,
|
|
9
|
+
* and its recorded result over the conformance corpus, a member the document omits where
|
|
10
|
+
* no run is recorded.
|
|
10
11
|
*/
|
|
11
12
|
export function describeDocument(version, recorded) {
|
|
12
13
|
const document = {
|
|
@@ -15,6 +16,7 @@ export function describeDocument(version, recorded) {
|
|
|
15
16
|
contract_version: CONTRACT_VERSION,
|
|
16
17
|
schema_versions_accepted: SCHEMA_VERSIONS,
|
|
17
18
|
languages: [LANGUAGE],
|
|
19
|
+
facts_version_written: FACTS_VERSION,
|
|
18
20
|
...(recorded === undefined
|
|
19
21
|
? {}
|
|
20
22
|
: {
|