@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
|
@@ -37,12 +37,8 @@ import {
|
|
|
37
37
|
type SourceFile,
|
|
38
38
|
} from "@typescript/native/ast";
|
|
39
39
|
import { SymbolFlags, type Symbol as TSSymbol, type Type } from "@typescript/native/sync";
|
|
40
|
-
import type { AliasChains } from "./alias-chain.ts";
|
|
41
|
-
import { nodeKey, type Inventory } from "./inventory.ts";
|
|
42
|
-
import { renderPosition } from "./position.ts";
|
|
43
40
|
import { UNANSWERED } from "./query.ts";
|
|
44
|
-
import type {
|
|
45
|
-
import type { ProjectView } from "./session.ts";
|
|
41
|
+
import type { Read, ReadRule } from "./references/read-rule.ts";
|
|
46
42
|
|
|
47
43
|
/** One call or construction passing an object literal, and the declaration that writes it. */
|
|
48
44
|
interface Passing {
|
|
@@ -118,294 +114,315 @@ function literalsOf(argument: Expression): readonly ObjectLiteralExpression[] {
|
|
|
118
114
|
}
|
|
119
115
|
|
|
120
116
|
/**
|
|
121
|
-
* The reads the object-literal arguments of
|
|
122
|
-
*
|
|
117
|
+
* The reads the object-literal arguments of a side's files make of the members of the types
|
|
118
|
+
* they are checked against, where the callee has no implementation in the target, and the
|
|
119
|
+
* reads of every member of each target type an outside value is handed over as. A type the
|
|
120
|
+
* checker leaves unanswered holds what its answer could have read.
|
|
123
121
|
*/
|
|
124
|
-
export
|
|
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
|
-
if (isAsExpression(node) && !isAsExpression(node.parent)) {
|
|
163
|
-
asserted.push({ file, from: holder, node, operand: castOperand(node) });
|
|
164
|
-
}
|
|
165
|
-
if (isClassDeclaration(node) || isClassExpression(node)) {
|
|
166
|
-
for (const clause of node.heritageClauses ?? []) {
|
|
167
|
-
if (isHeritageClause(clause)) {
|
|
168
|
-
implementing.push(
|
|
169
|
-
...clause.types.map((one) =>
|
|
170
|
-
isTypeReferenceNode(one) ? one.typeName : one.expression,
|
|
171
|
-
),
|
|
172
|
-
);
|
|
122
|
+
export const unimplementedCallees: ReadRule = {
|
|
123
|
+
start() {
|
|
124
|
+
const passing: Passing[] = [];
|
|
125
|
+
const implementing: Node[] = [];
|
|
126
|
+
const asserted: (HandedOver & { readonly operand: Expression })[] = [];
|
|
127
|
+
const constructed: Passing[] = [];
|
|
128
|
+
const handing: (CallExpression | NewExpression)[] = [];
|
|
129
|
+
return {
|
|
130
|
+
visit(node, { file, from }) {
|
|
131
|
+
if (
|
|
132
|
+
(isCallExpression(node) || isNewExpression(node)) &&
|
|
133
|
+
(node.arguments ?? []).some((argument) => isObjectLiteralExpression(argument))
|
|
134
|
+
) {
|
|
135
|
+
passing.push({ file, from, call: node });
|
|
136
|
+
}
|
|
137
|
+
if (isNewExpression(node)) {
|
|
138
|
+
constructed.push({ file, from, call: node });
|
|
139
|
+
}
|
|
140
|
+
if (
|
|
141
|
+
(isCallExpression(node) || isNewExpression(node)) &&
|
|
142
|
+
(node.arguments ?? []).some(
|
|
143
|
+
(argument) => isFunctionLike(argument) || isIdentifier(argument),
|
|
144
|
+
)
|
|
145
|
+
) {
|
|
146
|
+
handing.push(node);
|
|
147
|
+
}
|
|
148
|
+
if (isAsExpression(node) && !isAsExpression(node.parent)) {
|
|
149
|
+
asserted.push({ file, from, node, operand: castOperand(node) });
|
|
150
|
+
}
|
|
151
|
+
if (isClassDeclaration(node) || isClassExpression(node)) {
|
|
152
|
+
for (const clause of node.heritageClauses ?? []) {
|
|
153
|
+
if (isHeritageClause(clause)) {
|
|
154
|
+
implementing.push(
|
|
155
|
+
...clause.types.map((one) =>
|
|
156
|
+
isTypeReferenceNode(one) ? one.typeName : one.expression,
|
|
157
|
+
),
|
|
158
|
+
);
|
|
159
|
+
}
|
|
173
160
|
}
|
|
174
161
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
if (passing.length === 0 && asserted.length === 0 && constructed.length === 0) {
|
|
202
|
-
return [];
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const resolved = (symbol: TSSymbol | undefined | typeof UNANSWERED): TSSymbol | undefined => {
|
|
206
|
-
if (symbol === UNANSWERED || symbol === undefined) {
|
|
207
|
-
return undefined;
|
|
208
|
-
}
|
|
209
|
-
if ((symbol.flags & SymbolFlags.Alias) === 0) {
|
|
210
|
-
return symbol;
|
|
211
|
-
}
|
|
212
|
-
const target = queries.aliased(symbol);
|
|
213
|
-
return target === UNANSWERED ? undefined : target;
|
|
214
|
-
};
|
|
215
|
-
const declarationsOf = (symbol: TSSymbol | undefined): readonly Node[] =>
|
|
216
|
-
(symbol?.declarations ?? []).flatMap((handle) => {
|
|
217
|
-
const node = project.declarationAt(handle)?.node;
|
|
218
|
-
return node === undefined ? [] : [node];
|
|
219
|
-
});
|
|
162
|
+
// An object literal typed as an interface implements that interface's methods.
|
|
163
|
+
if (
|
|
164
|
+
isVariableDeclaration(node) &&
|
|
165
|
+
node.type !== undefined &&
|
|
166
|
+
isTypeReferenceNode(node.type) &&
|
|
167
|
+
node.initializer !== undefined &&
|
|
168
|
+
isObjectLiteralExpression(node.initializer)
|
|
169
|
+
) {
|
|
170
|
+
implementing.push(node.type.typeName);
|
|
171
|
+
}
|
|
172
|
+
if (
|
|
173
|
+
(isSatisfiesExpression(node) || isAsExpression(node)) &&
|
|
174
|
+
isObjectLiteralExpression(node.expression) &&
|
|
175
|
+
isTypeReferenceNode(node.type)
|
|
176
|
+
) {
|
|
177
|
+
implementing.push(node.type.typeName);
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
reads({ project, chains }) {
|
|
181
|
+
const queries = project.queries;
|
|
182
|
+
const own = project.ownPaths();
|
|
183
|
+
const found: Read[] = [];
|
|
184
|
+
if (passing.length === 0 && asserted.length === 0 && constructed.length === 0) {
|
|
185
|
+
return found;
|
|
186
|
+
}
|
|
220
187
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
188
|
+
const resolved = (
|
|
189
|
+
symbol: TSSymbol | undefined | typeof UNANSWERED,
|
|
190
|
+
): TSSymbol | undefined => {
|
|
191
|
+
if (symbol === UNANSWERED || symbol === undefined) {
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
if ((symbol.flags & SymbolFlags.Alias) === 0) {
|
|
195
|
+
return symbol;
|
|
196
|
+
}
|
|
197
|
+
const target = queries.aliased(symbol);
|
|
198
|
+
return target === UNANSWERED ? undefined : target;
|
|
199
|
+
};
|
|
200
|
+
const declarationsOf = (symbol: TSSymbol | undefined): readonly Node[] =>
|
|
201
|
+
(symbol?.declarations ?? []).flatMap((handle) => {
|
|
202
|
+
const node = project.declarationAt(handle)?.node;
|
|
203
|
+
return node === undefined ? [] : [node];
|
|
204
|
+
});
|
|
224
205
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
(isFunctionLike(node) ||
|
|
229
|
-
((isVariableDeclaration(node) || isPropertyAssignment(node) || isPropertyDeclaration(node)) &&
|
|
230
|
-
isFunctionLike(node.initializer)));
|
|
206
|
+
const implemented = new Set<Node>(
|
|
207
|
+
queries.symbolsAt(implementing).flatMap((symbol) => declarationsOf(resolved(symbol))),
|
|
208
|
+
);
|
|
231
209
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
if (known !== undefined) {
|
|
241
|
-
return known;
|
|
242
|
-
}
|
|
243
|
-
const fn = parameter.parent;
|
|
244
|
-
const fnName = nameOf(fn) ?? (isVariableDeclaration(fn.parent) ? nameOf(fn.parent) : undefined);
|
|
245
|
-
const at =
|
|
246
|
-
(fn as { readonly parameters?: readonly Node[] }).parameters?.indexOf(parameter) ?? -1;
|
|
247
|
-
const passed = handing.flatMap((call) => {
|
|
248
|
-
const callee = call.expression;
|
|
249
|
-
const name = isPropertyAccessExpression(callee) ? callee.name : callee;
|
|
250
|
-
if (fnName === undefined || !isIdentifier(name) || name.text !== fnName) {
|
|
251
|
-
return [];
|
|
252
|
-
}
|
|
253
|
-
const signature = queries.resolvedSignature(call);
|
|
254
|
-
const handle =
|
|
255
|
-
signature === UNANSWERED || signature === undefined ? undefined : signature.declaration;
|
|
256
|
-
const argument =
|
|
257
|
-
handle === undefined || project.declarationAt(handle)?.node !== fn
|
|
258
|
-
? undefined
|
|
259
|
-
: (call.arguments ?? [])[at];
|
|
260
|
-
return argument === undefined || at < 0 ? [] : [argument];
|
|
261
|
-
});
|
|
262
|
-
const named = passed.filter((argument) => isIdentifier(argument));
|
|
263
|
-
const reached =
|
|
264
|
-
passed.some((argument) => isFunctionLike(argument)) ||
|
|
265
|
-
queries
|
|
266
|
-
.symbolsAt(named)
|
|
267
|
-
.some((symbol) => declarationsOf(resolved(symbol)).some((node) => ownFunction(node)));
|
|
268
|
-
reachedParameters.set(parameter, reached);
|
|
269
|
-
return reached;
|
|
270
|
-
};
|
|
210
|
+
/** Whether a declaration is a function or a class of the target, or a variable or property it initializes. */
|
|
211
|
+
const ownFunction = (node: Node): boolean =>
|
|
212
|
+
own.has(node.getSourceFile().fileName) &&
|
|
213
|
+
(isFunctionLike(node) ||
|
|
214
|
+
((isVariableDeclaration(node) ||
|
|
215
|
+
isPropertyAssignment(node) ||
|
|
216
|
+
isPropertyDeclaration(node)) &&
|
|
217
|
+
isFunctionLike(node.initializer)));
|
|
271
218
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
219
|
+
/**
|
|
220
|
+
* Whether a function or a class of the target reaches one parameter of a function of the
|
|
221
|
+
* target, passed for it by a call that names the function. A call through another name
|
|
222
|
+
* is not followed, so its callee keeps no implementation and its literal reads more.
|
|
223
|
+
*/
|
|
224
|
+
const reachedParameters = new Map<ParameterDeclaration, boolean>();
|
|
225
|
+
const parameterReached = (parameter: ParameterDeclaration): boolean => {
|
|
226
|
+
const known = reachedParameters.get(parameter);
|
|
227
|
+
if (known !== undefined) {
|
|
228
|
+
return known;
|
|
229
|
+
}
|
|
230
|
+
const fn = parameter.parent;
|
|
231
|
+
const fnName =
|
|
232
|
+
nameOf(fn) ?? (isVariableDeclaration(fn.parent) ? nameOf(fn.parent) : undefined);
|
|
233
|
+
const at =
|
|
234
|
+
(fn as { readonly parameters?: readonly Node[] }).parameters?.indexOf(parameter) ?? -1;
|
|
235
|
+
const passed = handing.flatMap((call) => {
|
|
236
|
+
const callee = call.expression;
|
|
237
|
+
const name = isPropertyAccessExpression(callee) ? callee.name : callee;
|
|
238
|
+
if (fnName === undefined || !isIdentifier(name) || name.text !== fnName) {
|
|
239
|
+
return [];
|
|
240
|
+
}
|
|
241
|
+
const signature = queries.resolvedSignature(call);
|
|
242
|
+
const handle =
|
|
243
|
+
signature === UNANSWERED || signature === undefined
|
|
244
|
+
? undefined
|
|
245
|
+
: signature.declaration;
|
|
246
|
+
const argument =
|
|
247
|
+
handle === undefined || project.declarationAt(handle)?.node !== fn
|
|
248
|
+
? undefined
|
|
249
|
+
: (call.arguments ?? [])[at];
|
|
250
|
+
return argument === undefined || at < 0 ? [] : [argument];
|
|
251
|
+
});
|
|
252
|
+
const named = passed.filter((argument) => isIdentifier(argument));
|
|
253
|
+
const reached =
|
|
254
|
+
passed.some((argument) => isFunctionLike(argument)) ||
|
|
255
|
+
queries
|
|
256
|
+
.symbolsAt(named)
|
|
257
|
+
.some((symbol) => declarationsOf(resolved(symbol)).some((node) => ownFunction(node)));
|
|
258
|
+
reachedParameters.set(parameter, reached);
|
|
259
|
+
return reached;
|
|
260
|
+
};
|
|
297
261
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
262
|
+
/** Whether some function, class or object literal of the target stands for a signature. */
|
|
263
|
+
const standsFor = (passed: Passing): boolean => {
|
|
264
|
+
const signature = queries.resolvedSignature(passed.call);
|
|
265
|
+
// A callee the checker cannot resolve may be code the target cannot see.
|
|
266
|
+
if (signature === UNANSWERED) {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
const handle = signature?.declaration;
|
|
270
|
+
const declaration =
|
|
271
|
+
handle === undefined ? undefined : project.declarationAt(handle)?.node;
|
|
272
|
+
if (declaration === undefined) {
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
if (hasBody(declaration) || overloadImplemented(declaration)) {
|
|
276
|
+
return own.has(handle?.path ?? "");
|
|
277
|
+
}
|
|
278
|
+
const holder = declaration.parent as Node | undefined;
|
|
279
|
+
if (holder !== undefined && implemented.has(holder)) {
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
282
|
+
const callee = passed.call.expression;
|
|
283
|
+
const name = isPropertyAccessExpression(callee) ? callee.name : callee;
|
|
284
|
+
const [named] = queries.symbolsAt([name]);
|
|
285
|
+
return declarationsOf(resolved(named)).some((node) =>
|
|
286
|
+
isParameterDeclaration(node)
|
|
287
|
+
? own.has(node.getSourceFile().fileName) && parameterReached(node)
|
|
288
|
+
: ownFunction(node) && !isFunctionLike(node),
|
|
289
|
+
);
|
|
290
|
+
};
|
|
308
291
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
const properties = queries.propertiesOf(part);
|
|
319
|
-
for (const property of properties === UNANSWERED ? [] : properties) {
|
|
320
|
-
for (const to of chains.declarationsOf(property)) {
|
|
321
|
-
found.push({
|
|
322
|
-
from: passed.from,
|
|
323
|
-
to,
|
|
324
|
-
position,
|
|
325
|
-
use: "read",
|
|
326
|
-
resolution: "contextual",
|
|
327
|
-
test: testFiles.has(position.path),
|
|
328
|
-
});
|
|
292
|
+
const literals: { readonly passed: Passing; readonly literal: ObjectLiteralExpression }[] =
|
|
293
|
+
[];
|
|
294
|
+
for (const passed of passing) {
|
|
295
|
+
if (standsFor(passed)) {
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
for (const argument of passed.call.arguments ?? []) {
|
|
299
|
+
literals.push(...literalsOf(argument).map((literal) => ({ passed, literal })));
|
|
300
|
+
}
|
|
329
301
|
}
|
|
330
|
-
|
|
331
|
-
}
|
|
332
|
-
});
|
|
333
|
-
found.push(...handedOver());
|
|
334
|
-
return found;
|
|
302
|
+
const contexts = queries.contextualTypes(literals.map((one) => one.literal));
|
|
335
303
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
304
|
+
literals.forEach(({ passed, literal }, at) => {
|
|
305
|
+
const context = contexts[at];
|
|
306
|
+
if (context === UNANSWERED) {
|
|
307
|
+
found.push({ kind: "held", from: passed.from, names: propertyNames(literal) });
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
if (context === undefined) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
const parts = queries.constituents(context);
|
|
314
|
+
for (const part of parts === UNANSWERED ? [context] : parts) {
|
|
315
|
+
const properties = queries.propertiesOf(part);
|
|
316
|
+
if (properties === UNANSWERED) {
|
|
317
|
+
found.push({ kind: "held", from: passed.from, names: propertyNames(literal) });
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
for (const property of properties) {
|
|
321
|
+
for (const to of chains.declarationsOf(property)) {
|
|
322
|
+
found.push({
|
|
323
|
+
kind: "named",
|
|
324
|
+
from: passed.from,
|
|
325
|
+
to,
|
|
326
|
+
file: passed.file,
|
|
327
|
+
at: literal,
|
|
328
|
+
use: "read",
|
|
329
|
+
via: "contextual",
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
handedOver();
|
|
336
|
+
return found;
|
|
356
337
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
// A construct signature whose declared result is one of its type parameters names no
|
|
366
|
-
// type the target declares, so only a result the signature itself names is handed over.
|
|
367
|
-
const fromConstructors: HandedOver[] = constructed
|
|
368
|
-
.filter((one, at) => {
|
|
369
|
-
if (!ownObject(newTypes[at]) || standsFor(one)) {
|
|
370
|
-
return false;
|
|
371
|
-
}
|
|
372
|
-
const signature = queries.resolvedSignature(one.call);
|
|
373
|
-
const result =
|
|
374
|
-
signature === UNANSWERED || signature === undefined
|
|
375
|
-
? undefined
|
|
376
|
-
: queries.returnTypeOf(signature);
|
|
377
|
-
return result !== UNANSWERED && result !== undefined && !result.isTypeParameter();
|
|
378
|
-
})
|
|
379
|
-
.map((one) => ({ file: one.file, from: one.from, node: one.call }));
|
|
380
|
-
const handed = [...fromCasts, ...fromConstructors];
|
|
381
|
-
const types = queries.typesAt(handed.map((one) => one.node));
|
|
382
|
-
const reads: Reference[] = [];
|
|
383
|
-
handed.forEach((one, at) => {
|
|
384
|
-
const type = types[at];
|
|
385
|
-
if (type === UNANSWERED || type === undefined) {
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
const position = renderPosition(one.file, root, one.node.getStart());
|
|
389
|
-
const parts = queries.constituents(type);
|
|
390
|
-
for (const part of parts === UNANSWERED ? [type] : parts) {
|
|
391
|
-
if (!ownObject(part)) {
|
|
392
|
-
continue;
|
|
338
|
+
/** Whether a type is an object no file of the project declares, a platform object's among them. */
|
|
339
|
+
function outsideValue(type: Type): boolean {
|
|
340
|
+
if (!type.isObjectType()) {
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
const symbol = queries.symbolOfType(type);
|
|
344
|
+
const declared = symbol === UNANSWERED ? undefined : symbol;
|
|
345
|
+
return declared?.declarations.every((handle) => !own.has(handle.path)) === true;
|
|
393
346
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
position,
|
|
401
|
-
use: "read",
|
|
402
|
-
resolution: "contextual",
|
|
403
|
-
test: testFiles.has(position.path),
|
|
404
|
-
});
|
|
347
|
+
|
|
348
|
+
/** Whether a type is an object type a file of the project declares. */
|
|
349
|
+
function ownObject(type: Type | undefined | typeof UNANSWERED): type is Type {
|
|
350
|
+
const known = type === UNANSWERED ? undefined : type;
|
|
351
|
+
if (!known?.isObjectType()) {
|
|
352
|
+
return false;
|
|
405
353
|
}
|
|
354
|
+
const symbol = queries.symbolOfType(known);
|
|
355
|
+
const declared = symbol === UNANSWERED ? undefined : symbol;
|
|
356
|
+
return declared?.declarations.some((handle) => own.has(handle.path)) === true;
|
|
406
357
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
358
|
+
|
|
359
|
+
/** The reads of every member of each target type an outside value is handed over as. */
|
|
360
|
+
function handedOver(): void {
|
|
361
|
+
const operandTypes = queries.typesAt(asserted.map((one) => one.operand));
|
|
362
|
+
// An operand the checker cannot type may be an outside value.
|
|
363
|
+
const fromCasts: HandedOver[] = asserted.filter((_one, at) => {
|
|
364
|
+
const type = operandTypes[at];
|
|
365
|
+
return type === UNANSWERED || (type !== undefined && outsideValue(type));
|
|
366
|
+
});
|
|
367
|
+
const newTypes = queries.typesAt(constructed.map((one) => one.call));
|
|
368
|
+
// A construct signature whose declared result is one of its type parameters names no
|
|
369
|
+
// type the target declares, so only a result the signature itself names is handed over.
|
|
370
|
+
const fromConstructors: HandedOver[] = constructed
|
|
371
|
+
.filter((one, at) => {
|
|
372
|
+
if (!ownObject(newTypes[at]) || standsFor(one)) {
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
const signature = queries.resolvedSignature(one.call);
|
|
376
|
+
const result =
|
|
377
|
+
signature === UNANSWERED || signature === undefined
|
|
378
|
+
? undefined
|
|
379
|
+
: queries.returnTypeOf(signature);
|
|
380
|
+
return result !== UNANSWERED && result !== undefined && !result.isTypeParameter();
|
|
381
|
+
})
|
|
382
|
+
.map((one) => ({ file: one.file, from: one.from, node: one.call }));
|
|
383
|
+
const handed = [...fromCasts, ...fromConstructors];
|
|
384
|
+
const types = queries.typesAt(handed.map((one) => one.node));
|
|
385
|
+
handed.forEach((one, at) => {
|
|
386
|
+
const type = types[at];
|
|
387
|
+
if (type === UNANSWERED) {
|
|
388
|
+
found.push({ kind: "held", from: one.from, members: true });
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
if (type === undefined) {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
const parts = queries.constituents(type);
|
|
395
|
+
for (const part of parts === UNANSWERED ? [type] : parts) {
|
|
396
|
+
if (!ownObject(part)) {
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
const properties = queries.propertiesOf(part);
|
|
400
|
+
if (properties === UNANSWERED) {
|
|
401
|
+
found.push({ kind: "held", from: one.from, types: [part] });
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
for (const property of properties) {
|
|
405
|
+
for (const to of chains.declarationsOf(property)) {
|
|
406
|
+
found.push({
|
|
407
|
+
kind: "named",
|
|
408
|
+
from: one.from,
|
|
409
|
+
to,
|
|
410
|
+
file: one.file,
|
|
411
|
+
at: one.node,
|
|
412
|
+
use: "read",
|
|
413
|
+
via: "contextual",
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
};
|
|
422
|
+
},
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
/** The names an object literal's own properties spell. */
|
|
426
|
+
function propertyNames(literal: ObjectLiteralExpression): readonly string[] {
|
|
427
|
+
return literal.properties.flatMap((property) => nameOf(property) ?? []);
|
|
411
428
|
}
|