@cplieger/deadset-ts 7.2.0-dev.3 → 7.2.0-dev.4
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/bin/mapper-connection.js +7 -4
- package/dist/src/analysis.js +10 -6
- package/dist/src/component-files.js +96 -31
- package/dist/src/configuration-files.js +4 -3
- package/dist/src/configured-declarations.js +24 -2
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/contextual-uses.js +26 -26
- package/dist/src/dependencies.js +2 -2
- package/dist/src/destructuring.js +77 -2
- package/dist/src/discover.js +68 -86
- package/dist/src/edges.js +1 -1
- package/dist/src/intra-function-parts.js +17 -6
- package/dist/src/inventory.js +11 -0
- package/dist/src/json-document.js +5 -5
- package/dist/src/namespace-objects.js +28 -18
- package/dist/src/overrides.js +7 -2
- package/dist/src/paths.js +9 -0
- package/dist/src/references.js +25 -12
- package/dist/src/reflective-lookup.js +8 -6
- package/dist/src/reporters/template.js +22 -13
- package/dist/src/serialization-contract.js +43 -12
- package/dist/src/session.js +19 -1
- package/dist/src/spelled-names.js +58 -0
- package/dist/src/suppress-file.js +2 -2
- package/dist/src/suppress-inline.js +3 -1
- package/dist/src/template-field.js +9 -6
- package/dist/src/type-errors.js +82 -28
- package/dist/src/value-reach.js +17 -5
- package/dist/src/verbs/analyze.js +3 -1
- package/dist/src/version-range.js +10 -5
- package/dist/src/workspace-resolution.js +19 -3
- package/dist/src/workspace.js +1 -1
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/analysis.ts +32 -6
- package/src/component-files.ts +116 -31
- package/src/configuration-files.ts +14 -6
- package/src/configured-declarations.ts +33 -4
- package/src/conformance-record.ts +1 -1
- package/src/contextual-uses.ts +27 -27
- package/src/dependencies.ts +2 -2
- package/src/destructuring.ts +93 -2
- package/src/discover.ts +92 -87
- package/src/edges.ts +1 -1
- package/src/intra-function-parts.ts +21 -5
- package/src/inventory.ts +12 -0
- package/src/json-document.ts +7 -7
- package/src/namespace-objects.ts +34 -21
- package/src/overrides.ts +10 -2
- package/src/paths.ts +10 -0
- package/src/references.ts +28 -16
- package/src/reflective-lookup.ts +9 -7
- package/src/reporters/template.ts +21 -13
- package/src/serialization-contract.ts +53 -13
- package/src/session.ts +22 -1
- package/src/spelled-names.ts +102 -0
- package/src/suppress-file.ts +2 -2
- package/src/suppress-inline.ts +3 -1
- package/src/template-field.ts +9 -6
- package/src/type-errors.ts +94 -35
- package/src/value-reach.ts +19 -6
- package/src/verbs/analyze.ts +6 -1
- package/src/version-range.ts +10 -5
- package/src/workspace-resolution.ts +24 -4
- package/src/workspace.ts +1 -1
package/dist/src/discover.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DiagnosticCategory } from "@typescript/native/sync";
|
|
2
|
-
import { dirnamePath,
|
|
2
|
+
import { dirnamePath, joinPath, relativePath, resolvePath } from "./paths.js";
|
|
3
3
|
import { SetupError } from "./setup-failure.js";
|
|
4
4
|
/** Directory names discovery does not descend into. */
|
|
5
5
|
export const IGNORED_DIRECTORIES = new Set(["node_modules", ".git"]);
|
|
@@ -60,12 +60,28 @@ function locatedIn(diagnostics, locate) {
|
|
|
60
60
|
export function diagnosticErrors(sets) {
|
|
61
61
|
return [...sets.configParsing, ...sets.syntactic, ...sets.semantic].filter(isError);
|
|
62
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* The codes of a type library and a lib the configuration names that the compiler
|
|
65
|
+
* cannot find, which it reports while it gathers the program's files:
|
|
66
|
+
* https://github.com/microsoft/typescript-go/blob/89d5d5b2849a0db0957065889ca58536fa6d2e4a/internal/compiler/processingDiagnostic.go
|
|
67
|
+
*/
|
|
68
|
+
const UNFOUND_LIBRARY = new Set([2688, 2726, 2727]);
|
|
63
69
|
/** The errors one open project carries, each located in the file it names. */
|
|
64
70
|
export function projectErrors(sets) {
|
|
65
|
-
|
|
71
|
+
// The compiler numbers its semantic messages from 2000 and its option messages from
|
|
72
|
+
// 5000. A file-less option error, a removed option or an output layout, leaves the
|
|
73
|
+
// program whole, and the compiler checks it all the same:
|
|
74
|
+
// https://github.com/microsoft/TypeScript/wiki/Coding-guidelines#diagnostic-message-codes
|
|
75
|
+
const semantic = sets.unplaced.filter((diagnostic) => isError(diagnostic) && diagnostic.code >= 2000 && diagnostic.code < 3000);
|
|
76
|
+
const unfound = semantic.filter(({ code }) => UNFOUND_LIBRARY.has(code));
|
|
77
|
+
const load = [
|
|
78
|
+
...sets.configParsing.filter(isError),
|
|
79
|
+
...semantic.filter(({ code }) => !UNFOUND_LIBRARY.has(code)),
|
|
80
|
+
];
|
|
66
81
|
return {
|
|
67
82
|
load: locatedIn(load, sets.locate),
|
|
68
83
|
check: load.length > 0 ? [] : locatedIn(diagnosticErrors(sets), sets.locate),
|
|
84
|
+
unfound: load.length > 0 ? [] : locatedIn(unfound, sets.locate),
|
|
69
85
|
};
|
|
70
86
|
}
|
|
71
87
|
/**
|
|
@@ -86,9 +102,6 @@ export function targetRelative(text, targetRoot) {
|
|
|
86
102
|
export function isGuess(discovery, configFile) {
|
|
87
103
|
return discovery.derived && !discovery.fromScope.includes(configFile);
|
|
88
104
|
}
|
|
89
|
-
function isRecord(value) {
|
|
90
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
91
|
-
}
|
|
92
105
|
/**
|
|
93
106
|
* The configuration files below `root`, in ascending path order, skipping an
|
|
94
107
|
* ignored directory. A directory that cannot be read is skipped: a tree the
|
|
@@ -124,46 +137,6 @@ function configFilesUnder(host, root) {
|
|
|
124
137
|
}
|
|
125
138
|
return found.sort();
|
|
126
139
|
}
|
|
127
|
-
/**
|
|
128
|
-
* The configuration files one configuration's `references` name, resolved against
|
|
129
|
-
* that configuration's own directory. A reference naming a directory names the
|
|
130
|
-
* `tsconfig.json` in it, which is the compiler's own rule.
|
|
131
|
-
*/
|
|
132
|
-
function referencesOf(host, configFile) {
|
|
133
|
-
let text;
|
|
134
|
-
try {
|
|
135
|
-
text = host.readFile(configFile);
|
|
136
|
-
}
|
|
137
|
-
catch {
|
|
138
|
-
return [];
|
|
139
|
-
}
|
|
140
|
-
let value;
|
|
141
|
-
try {
|
|
142
|
-
value = JSON.parse(text);
|
|
143
|
-
}
|
|
144
|
-
catch {
|
|
145
|
-
// What a configuration's contents say is refused by the compiler's own
|
|
146
|
-
// diagnostics, which name the position; nothing is inferred from it here.
|
|
147
|
-
return [];
|
|
148
|
-
}
|
|
149
|
-
if (!isRecord(value) || !Array.isArray(value["references"])) {
|
|
150
|
-
return [];
|
|
151
|
-
}
|
|
152
|
-
const dir = dirnamePath(configFile);
|
|
153
|
-
const found = [];
|
|
154
|
-
for (const entry of value["references"]) {
|
|
155
|
-
if (!isRecord(entry)) {
|
|
156
|
-
continue;
|
|
157
|
-
}
|
|
158
|
-
const path = entry["path"];
|
|
159
|
-
if (typeof path !== "string" || path === "") {
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
|
-
const resolved = isAbsolutePath(path) ? path : joinPath(dir, path);
|
|
163
|
-
found.push(resolved.endsWith(".json") ? resolved : joinPath(resolved, "tsconfig.json"));
|
|
164
|
-
}
|
|
165
|
-
return found;
|
|
166
|
-
}
|
|
167
140
|
/**
|
|
168
141
|
* The configuration file the scope names for the target directly, where its path
|
|
169
142
|
* names one rather than a directory. A target naming a directory is reached by the
|
|
@@ -177,12 +150,12 @@ function scopeConfigFiles(host, scope) {
|
|
|
177
150
|
}
|
|
178
151
|
/**
|
|
179
152
|
* Every configuration file reached from `pending`, in the order discovery reads them:
|
|
180
|
-
* each once, each followed by the configurations it references
|
|
181
|
-
* configuration the compiler cannot read ends the run naming it, unless
|
|
182
|
-
* is given, which is then told the configuration and why, and the walk
|
|
183
|
-
* without it.
|
|
153
|
+
* each once, each followed by the configurations it references that `follows` admits,
|
|
154
|
+
* transitively. A configuration the compiler cannot read ends the run naming it, unless
|
|
155
|
+
* `unreadable` is given, which is then told the configuration and why, and the walk
|
|
156
|
+
* goes on without it.
|
|
184
157
|
*/
|
|
185
|
-
function derivedFrom(engine, host, pending, unreadable) {
|
|
158
|
+
function derivedFrom(engine, host, pending, follows, unreadable) {
|
|
186
159
|
const seen = new Set();
|
|
187
160
|
const found = [];
|
|
188
161
|
while (pending.length > 0) {
|
|
@@ -195,16 +168,16 @@ function derivedFrom(engine, host, pending, unreadable) {
|
|
|
195
168
|
continue;
|
|
196
169
|
}
|
|
197
170
|
seen.add(path);
|
|
198
|
-
const
|
|
199
|
-
if (reason
|
|
171
|
+
const read = readConfiguration(engine, path);
|
|
172
|
+
if ("reason" in read) {
|
|
200
173
|
if (unreadable === undefined) {
|
|
201
|
-
throw new DiscoveryError(`${path} cannot be read as a compiler configuration: ${reason}`, []);
|
|
174
|
+
throw new DiscoveryError(`${path} cannot be read as a compiler configuration: ${read.reason}`, []);
|
|
202
175
|
}
|
|
203
|
-
unreadable(path, reason);
|
|
176
|
+
unreadable(path, read.reason);
|
|
204
177
|
continue;
|
|
205
178
|
}
|
|
206
179
|
found.push(path);
|
|
207
|
-
pending.push(...
|
|
180
|
+
pending.push(...read.references.filter(follows));
|
|
208
181
|
}
|
|
209
182
|
return found;
|
|
210
183
|
}
|
|
@@ -229,7 +202,7 @@ export function consumerProjects(engine, host, root) {
|
|
|
229
202
|
if (kind !== "directory") {
|
|
230
203
|
throw new DiscoveryError(`the consumer ${root} is not a directory`, []);
|
|
231
204
|
}
|
|
232
|
-
const found = derivedFrom(engine, host, configFilesUnder(host, root));
|
|
205
|
+
const found = derivedFrom(engine, host, configFilesUnder(host, root), () => true);
|
|
233
206
|
if (found.length === 0) {
|
|
234
207
|
throw new DiscoveryError(`the consumer ${root} holds no compiler configuration, so none of its references can be read`, []);
|
|
235
208
|
}
|
|
@@ -242,42 +215,48 @@ export function consumerProjects(engine, host, root) {
|
|
|
242
215
|
function derivedId(targetRoot, configFile) {
|
|
243
216
|
return relativePath(targetRoot, configFile) ?? configFile;
|
|
244
217
|
}
|
|
245
|
-
/**
|
|
246
|
-
|
|
218
|
+
/**
|
|
219
|
+
* The configuration files one configuration's `references` name, or why the compiler
|
|
220
|
+
* cannot read the configuration.
|
|
221
|
+
*/
|
|
222
|
+
function readConfiguration(engine, configFile) {
|
|
247
223
|
try {
|
|
248
|
-
engine.parseConfigFile(configFile);
|
|
249
|
-
|
|
224
|
+
const parsed = engine.parseConfigFile(configFile);
|
|
225
|
+
// The parse answers a directory reference as the directory; the compiler reads a
|
|
226
|
+
// path not ending `.json` as its `tsconfig.json`:
|
|
227
|
+
// https://github.com/microsoft/typescript-go/blob/89d5d5b2849a0db0957065889ca58536fa6d2e4a/internal/core/projectreference.go#L18
|
|
228
|
+
return {
|
|
229
|
+
references: (parsed.projectReferences ?? []).map(({ path }) => path.endsWith(".json") ? path : joinPath(path, "tsconfig.json")),
|
|
230
|
+
};
|
|
250
231
|
}
|
|
251
232
|
catch (error) {
|
|
252
|
-
return error instanceof Error ? error.message : String(error);
|
|
233
|
+
return { reason: error instanceof Error ? error.message : String(error) };
|
|
253
234
|
}
|
|
254
235
|
}
|
|
255
236
|
/**
|
|
256
|
-
*
|
|
257
|
-
* opened from a configuration the compiler cannot read would be analyzed with
|
|
258
|
-
* type information.
|
|
237
|
+
* The references of one compiler configuration file, or ends the run naming it: a
|
|
238
|
+
* project opened from a configuration the compiler cannot read would be analyzed with
|
|
239
|
+
* no type information.
|
|
259
240
|
*/
|
|
260
241
|
function parseOrRefuse(engine, configFile, named) {
|
|
261
|
-
const
|
|
262
|
-
if (reason
|
|
263
|
-
throw new DiscoveryError(`${named} cannot be read as a compiler configuration: ${reason}`, []);
|
|
242
|
+
const read = readConfiguration(engine, configFile);
|
|
243
|
+
if ("reason" in read) {
|
|
244
|
+
throw new DiscoveryError(`${named} cannot be read as a compiler configuration: ${read.reason}`, []);
|
|
264
245
|
}
|
|
246
|
+
return read.references;
|
|
265
247
|
}
|
|
266
248
|
/**
|
|
267
|
-
* The projects a build matrix declares, in
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
* naming both: the compiler builds a referenced project's sources into the program
|
|
274
|
-
* that references it without reporting their diagnostics, so the project the matrix
|
|
275
|
-
* left out would be analyzed with its errors unread. An entry naming a file an
|
|
276
|
-
* earlier entry named adds nothing, so the earlier identifier stands.
|
|
249
|
+
* The projects a build matrix declares, in its order, each entry a file below the target
|
|
250
|
+
* root: an entry naming an absent file, a directory or a file the compiler cannot read
|
|
251
|
+
* ends the run naming it, and one naming an earlier entry's file adds nothing. A
|
|
252
|
+
* reference below the root the matrix does not name ends the run naming both, because the
|
|
253
|
+
* compiler builds a referenced project's sources into the referencing program without
|
|
254
|
+
* reporting their diagnostics. A reference outside the root is an outside project.
|
|
277
255
|
*/
|
|
278
256
|
function declaredProjects(engine, host, targetRoot, entries) {
|
|
279
257
|
const seen = new Set();
|
|
280
258
|
const projects = [];
|
|
259
|
+
const references = new Map();
|
|
281
260
|
for (const entry of entries) {
|
|
282
261
|
const configFile = joinPath(targetRoot, entry.project);
|
|
283
262
|
const named = `the project ${JSON.stringify(entry.id)} (${configFile})`;
|
|
@@ -291,11 +270,13 @@ function declaredProjects(engine, host, targetRoot, entries) {
|
|
|
291
270
|
continue;
|
|
292
271
|
}
|
|
293
272
|
seen.add(configFile);
|
|
294
|
-
parseOrRefuse(engine, configFile, named);
|
|
273
|
+
references.set(configFile, parseOrRefuse(engine, configFile, named));
|
|
295
274
|
projects.push({ id: entry.id, configFile });
|
|
296
275
|
}
|
|
297
276
|
for (const project of projects) {
|
|
298
|
-
const unnamed =
|
|
277
|
+
const unnamed = references
|
|
278
|
+
.get(project.configFile)
|
|
279
|
+
?.find((path) => !seen.has(path) && relativePath(targetRoot, path) !== undefined);
|
|
299
280
|
if (unnamed !== undefined) {
|
|
300
281
|
throw new DiscoveryError(`the project ${JSON.stringify(project.id)} (${project.configFile}) references ` +
|
|
301
282
|
`${unnamed}, which the build matrix does not name. A referenced project is built ` +
|
|
@@ -311,18 +292,19 @@ function declaredProjects(engine, host, targetRoot, entries) {
|
|
|
311
292
|
};
|
|
312
293
|
}
|
|
313
294
|
/**
|
|
314
|
-
* The projects a run derives where the matrix declares none, in
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
* each
|
|
318
|
-
*
|
|
319
|
-
* it cannot be read; one the walk found is dropped and named.
|
|
295
|
+
* The projects a run derives where the matrix declares none, in discovery order: the
|
|
296
|
+
* paths the scope names, every compiler configuration under the target root outside an
|
|
297
|
+
* ignored directory, then each configuration below the root those reference,
|
|
298
|
+
* transitively, each once. A reference outside the root is an outside project, whose
|
|
299
|
+
* sources the compiler builds into the referencing program. A configuration the scope
|
|
300
|
+
* names ends the run where it cannot be read; one the walk found is dropped and named.
|
|
320
301
|
*/
|
|
321
302
|
function derivedProjects(engine, host, scope) {
|
|
322
303
|
const targetRoot = scope.target.path;
|
|
323
304
|
const fromScope = scopeConfigFiles(host, scope);
|
|
324
305
|
const notBuilt = [];
|
|
325
|
-
const
|
|
306
|
+
const rootDir = resolvePath(host.workingDirectory(), fromScope.length > 0 ? dirnamePath(targetRoot) : targetRoot);
|
|
307
|
+
const configFiles = derivedFrom(engine, host, [...fromScope, ...configFilesUnder(host, targetRoot)], (reference) => relativePath(rootDir, reference) !== undefined, (configFile, reason) => {
|
|
326
308
|
if (fromScope.includes(configFile)) {
|
|
327
309
|
throw new DiscoveryError(`${configFile} cannot be read as a compiler configuration: ${reason}`, []);
|
|
328
310
|
}
|
package/dist/src/edges.js
CHANGED
|
@@ -130,7 +130,7 @@ export function readEdgeSides(host, targetRoot) {
|
|
|
130
130
|
}
|
|
131
131
|
const sides = [];
|
|
132
132
|
edges.forEach((value, index) => {
|
|
133
|
-
const offset = walk.
|
|
133
|
+
const offset = walk.starts[index];
|
|
134
134
|
const at = offset === undefined ? EDGES_FILE : positionKey(positionAt(text, EDGES_FILE, offset));
|
|
135
135
|
const edge = readEdge(value, at);
|
|
136
136
|
const named = [
|
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
* call sites that use or discard it, each statement control flow cannot reach, each
|
|
5
5
|
* write to a local that no read reaches, and each case an earlier case covers.
|
|
6
6
|
*/
|
|
7
|
-
import { isArrayBindingPattern, isArrowFunction, isAwaitExpression, isBigIntLiteral, isBinaryExpression, isBlock, isBreakStatement, isBindingElement, isCallExpression, isCaseClause, isCatchClause, isClassDeclaration, isClassExpression, isConstructorDeclaration, isContinueStatement, isDecorator, isDefaultClause, isEmptyStatement, isExportAssignment, isExportSpecifier, isExpressionStatement, isFunctionDeclaration, isFunctionExpression, isHeritageClause, isIdentifier, isIfStatement, isImportClause, isImportSpecifier, isInterfaceDeclaration, isMethodDeclaration, isModuleDeclaration, isNamespaceImport, isNewExpression, isNoSubstitutionTemplateLiteral, isNumericLiteral, isObjectBindingPattern, isObjectLiteralExpression, isParenthesizedExpression, isPrefixUnaryExpression, isPropertyAccessExpression, isQualifiedName, isReturnStatement, isShorthandPropertyAssignment, isStringLiteral, isSwitchStatement, isThrowStatement, isTypeAliasDeclaration, isTypeReferenceNode, isVariableStatement, isVoidExpression, NodeFlags, SyntaxKind, } from "@typescript/native/ast";
|
|
7
|
+
import { isArrayBindingPattern, isArrowFunction, isAwaitExpression, isBigIntLiteral, isBinaryExpression, isBlock, isBreakStatement, isBindingElement, isCallExpression, isCaseClause, isCatchClause, isClassDeclaration, isClassExpression, isConstructorDeclaration, isContinueStatement, isDecorator, isDefaultClause, isEmptyStatement, isExportAssignment, isExportSpecifier, isExpressionStatement, isFunctionDeclaration, isFunctionExpression, isFunctionLikeDeclaration, isHeritageClause, isIdentifier, isIfStatement, isImportClause, isImportSpecifier, isInterfaceDeclaration, isMethodDeclaration, isModuleDeclaration, isNamespaceImport, isNewExpression, isNoSubstitutionTemplateLiteral, isNumericLiteral, isObjectBindingPattern, isObjectLiteralExpression, isParenthesizedExpression, isPrefixUnaryExpression, isPropertyAccessExpression, isQualifiedName, isReturnStatement, isShorthandPropertyAssignment, isSourceFile, isStringLiteral, isSwitchStatement, isThrowStatement, isTypeAliasDeclaration, isTypeReferenceNode, isVariableStatement, isVoidExpression, NodeFlags, SyntaxKind, } from "@typescript/native/ast";
|
|
8
8
|
import { isParameterProperty, nodeKey } from "./inventory.js";
|
|
9
9
|
import { positionKey, renderPosition } from "./position.js";
|
|
10
10
|
import { isAnswered, UNANSWERED } from "./query.js";
|
|
11
11
|
import { signatureOf } from "./signatures.js";
|
|
12
|
+
import { spellsNoBinding } from "./spelled-names.js";
|
|
12
13
|
/** The codes of the parts this module reads. */
|
|
13
14
|
export const UNUSED_PARAMETER = "DS1801";
|
|
14
15
|
export const UNUSED_RESULT = "DS1803";
|
|
@@ -117,13 +118,12 @@ function caseKey(expression) {
|
|
|
117
118
|
}
|
|
118
119
|
return undefined;
|
|
119
120
|
}
|
|
120
|
-
/** Every identifier below one node whose text one of the names spells
|
|
121
|
+
/** Every identifier below one node whose text one of the names spells as a reference. */
|
|
121
122
|
function identifiersNaming(node, names) {
|
|
122
123
|
const found = [];
|
|
123
124
|
const visit = (at) => {
|
|
124
125
|
if (isIdentifier(at)) {
|
|
125
|
-
|
|
126
|
-
if (names.has(at.text) && !(isPropertyAccessExpression(parent) && parent.name === at)) {
|
|
126
|
+
if (names.has(at.text) && !spellsNoBinding(at)) {
|
|
127
127
|
found.push(at);
|
|
128
128
|
}
|
|
129
129
|
return;
|
|
@@ -154,6 +154,15 @@ function inNestedFunction(node, outer) {
|
|
|
154
154
|
}
|
|
155
155
|
return false;
|
|
156
156
|
}
|
|
157
|
+
/** The nearest function around one node whose `arguments` object it names: an arrow has none. */
|
|
158
|
+
function ownArgumentsOf(node) {
|
|
159
|
+
for (let at = node.parent; !isSourceFile(at); at = at.parent) {
|
|
160
|
+
if (isFunctionLikeDeclaration(at) && !isArrowFunction(at)) {
|
|
161
|
+
return at;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
157
166
|
/** Whether a body is a stub: it holds no statement, or nothing but `throw` statements. */
|
|
158
167
|
function isStub(signature) {
|
|
159
168
|
const body = signature.body;
|
|
@@ -608,8 +617,10 @@ function withContextualRead(project, parts, contextual) {
|
|
|
608
617
|
* as read where its function reads any name it binds.
|
|
609
618
|
*/
|
|
610
619
|
function parameterParts(file, root, declaration, signature, reader, ids, inValue) {
|
|
611
|
-
// A function that reads `arguments` reads every parameter by position.
|
|
612
|
-
|
|
620
|
+
// A function that reads its `arguments` reads every parameter by position. A nested
|
|
621
|
+
// function other than an arrow has an `arguments` object of its own.
|
|
622
|
+
const readsArguments = !isArrowFunction(signature) &&
|
|
623
|
+
identifiersNaming(signature, new Set(["arguments"])).some((use) => ownArgumentsOf(use) === signature);
|
|
613
624
|
const read = reader.names.map(({ name }) => readsArguments ||
|
|
614
625
|
reader.uses.some((use) => use.text === name.text && readsBinding(use, name, ids)));
|
|
615
626
|
const lastRead = Math.max(-1, ...reader.names.filter((_, at) => read[at]).map((one) => one.at));
|
package/dist/src/inventory.js
CHANGED
|
@@ -204,6 +204,17 @@ function visibilityOf(node, name) {
|
|
|
204
204
|
function nameNodeOf(node) {
|
|
205
205
|
return node.name;
|
|
206
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* The fragment component one declaration's name spells in a reference, as the inventory
|
|
209
|
+
* spells it, or undefined for a name the grammar does not represent.
|
|
210
|
+
*/
|
|
211
|
+
export function declaredComponent(node) {
|
|
212
|
+
const component = componentOf(nameNodeOf(node));
|
|
213
|
+
if (component === undefined) {
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
return component.computed ? computedComponent(component.text) : nameComponent(component.text);
|
|
217
|
+
}
|
|
207
218
|
/**
|
|
208
219
|
* One node's identity inside one program, which is how two readings of that program
|
|
209
220
|
* name one declaration.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
/** The token walk over a document `JSON.parse` accepted, so every token is well formed. */
|
|
6
6
|
class Walk {
|
|
7
|
-
|
|
7
|
+
starts = [];
|
|
8
8
|
/** The member written twice, where one is, as the offset of its name. */
|
|
9
9
|
twice;
|
|
10
10
|
text;
|
|
@@ -33,11 +33,11 @@ class Walk {
|
|
|
33
33
|
}
|
|
34
34
|
value(role) {
|
|
35
35
|
this.skip();
|
|
36
|
+
if (role === "record") {
|
|
37
|
+
this.starts.push(this.at);
|
|
38
|
+
}
|
|
36
39
|
const char = this.text[this.at];
|
|
37
40
|
if (char === "{") {
|
|
38
|
-
if (role === "record") {
|
|
39
|
-
this.opened.push(this.at);
|
|
40
|
-
}
|
|
41
41
|
this.object(role);
|
|
42
42
|
}
|
|
43
43
|
else if (char === "[") {
|
|
@@ -100,7 +100,7 @@ class Walk {
|
|
|
100
100
|
export function walkDocument(text, array) {
|
|
101
101
|
const walk = new Walk(text, array);
|
|
102
102
|
walk.run();
|
|
103
|
-
return {
|
|
103
|
+
return { starts: walk.starts, twice: walk.twice };
|
|
104
104
|
}
|
|
105
105
|
/**
|
|
106
106
|
* One offset of a document as a finding carries it: the line counted from one, and the
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Module namespace objects: the binding of a namespace import, the value an `await` of a
|
|
3
3
|
* dynamic `import()` produces, the first parameter of the first callback passed to `then`
|
|
4
|
-
* on one, and a
|
|
4
|
+
* on one, and a variable binding initialized with one of these. A use reads the exports it
|
|
5
5
|
* names and no other: a property access by name or by a literal index, or a destructured
|
|
6
6
|
* property, names one export, and any other use may read every export.
|
|
7
7
|
*/
|
|
8
|
-
import { isArrowFunction, isAwaitExpression,
|
|
8
|
+
import { isArrowFunction, isAwaitExpression, isCallExpression, isClassStaticBlockDeclaration, isElementAccessExpression, isExpressionStatement, isFunctionExpression, isFunctionLikeDeclaration, isIdentifier, isImportDeclaration, isModuleBlock, isNamespaceImport, isNoSubstitutionTemplateLiteral, isObjectBindingPattern, isParenthesizedExpression, isPropertyAccessExpression, isQualifiedName, isSourceFile, isStringLiteral, isVariableDeclaration, isVariableDeclarationList, isVariableStatement, isVoidExpression, NodeFlags, SyntaxKind, } from "@typescript/native/ast";
|
|
9
|
+
import { spellsNoBinding } from "./spelled-names.js";
|
|
9
10
|
function isLiteralText(node) {
|
|
10
11
|
return isStringLiteral(node) || isNoSubstitutionTemplateLiteral(node);
|
|
11
12
|
}
|
|
@@ -25,21 +26,30 @@ function isLiteralImport(node) {
|
|
|
25
26
|
const [argument] = node.arguments;
|
|
26
27
|
return argument !== undefined && isLiteralText(argument);
|
|
27
28
|
}
|
|
28
|
-
/**
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
/**
|
|
30
|
+
* The node the binding one declaration list declares is visible below: for `let` and
|
|
31
|
+
* `const`, the loop whose head declares it, or else the block or file its statement is
|
|
32
|
+
* in; for `var`, the function, namespace body, static block or file that holds it.
|
|
33
|
+
*/
|
|
34
|
+
function scopeOf(list) {
|
|
35
|
+
if ((list.flags & (NodeFlags.Let | NodeFlags.Const | NodeFlags.Using)) !== 0) {
|
|
36
|
+
return isVariableStatement(list.parent) ? list.parent.parent : list.parent;
|
|
37
|
+
}
|
|
38
|
+
let at = list.parent;
|
|
39
|
+
while (!isSourceFile(at) &&
|
|
40
|
+
!isFunctionLikeDeclaration(at) &&
|
|
41
|
+
!isModuleBlock(at) &&
|
|
42
|
+
!isClassStaticBlockDeclaration(at)) {
|
|
43
|
+
at = at.parent;
|
|
44
|
+
}
|
|
45
|
+
return at;
|
|
32
46
|
}
|
|
33
|
-
/** Every identifier below `scope` that spells `name
|
|
47
|
+
/** Every identifier below `scope` that spells `name` as a reference, other than `declared`. */
|
|
34
48
|
function occurrences(scope, name, declared) {
|
|
35
49
|
const found = [];
|
|
36
50
|
const visit = (node) => {
|
|
37
51
|
if (isIdentifier(node)) {
|
|
38
|
-
|
|
39
|
-
const member = (isPropertyAccessExpression(parent) && parent.name === node) ||
|
|
40
|
-
(isQualifiedName(parent) && parent.right === node) ||
|
|
41
|
-
(isBindingElement(parent) && parent.propertyName === node);
|
|
42
|
-
if (node !== declared && node.text === name && !member) {
|
|
52
|
+
if (node !== declared && node.text === name && !spellsNoBinding(node)) {
|
|
43
53
|
found.push(node);
|
|
44
54
|
}
|
|
45
55
|
return;
|
|
@@ -52,8 +62,8 @@ function occurrences(scope, name, declared) {
|
|
|
52
62
|
/**
|
|
53
63
|
* Every use of the namespace objects one file holds, keyed by the node the use is written
|
|
54
64
|
* at, that reads more than a name the checker resolves: an indexed read by a literal, and
|
|
55
|
-
* any use that may read every export. A shadowing declaration of a binding's
|
|
56
|
-
* its scope is read as the binding, which can only keep an export live.
|
|
65
|
+
* any use that may read every export. A use of a shadowing declaration of a binding's
|
|
66
|
+
* name inside its scope is read as a use of the binding, which can only keep an export live.
|
|
57
67
|
*/
|
|
58
68
|
export function namespaceUses(file) {
|
|
59
69
|
const uses = new Map();
|
|
@@ -92,11 +102,11 @@ export function namespaceUses(file) {
|
|
|
92
102
|
}
|
|
93
103
|
return;
|
|
94
104
|
}
|
|
105
|
+
// A binding that is not `const` is followed too: each assignment to it is an
|
|
106
|
+
// occurrence `value` cannot narrow, so a reassigned binding still reads every export.
|
|
95
107
|
const list = parent.parent;
|
|
96
|
-
if (isIdentifier(pattern) &&
|
|
97
|
-
|
|
98
|
-
(list.flags & NodeFlags.Const) !== 0) {
|
|
99
|
-
binding(pattern, scopeOf(parent), specifier, seen);
|
|
108
|
+
if (isIdentifier(pattern) && isVariableDeclarationList(list)) {
|
|
109
|
+
binding(pattern, scopeOf(list), specifier, seen);
|
|
100
110
|
return;
|
|
101
111
|
}
|
|
102
112
|
}
|
package/dist/src/overrides.js
CHANGED
|
@@ -12,6 +12,10 @@ import { nodeKey } from "./inventory.js";
|
|
|
12
12
|
import { SymbolFlags } from "@typescript/native/sync";
|
|
13
13
|
import { UNANSWERED } from "./query.js";
|
|
14
14
|
import { nameComponent } from "./ref.js";
|
|
15
|
+
const CLASS_DECLARATIONS = new Set([
|
|
16
|
+
SyntaxKind.ClassDeclaration,
|
|
17
|
+
SyntaxKind.ClassExpression,
|
|
18
|
+
]);
|
|
15
19
|
/** The member kinds a subclass overrides. */
|
|
16
20
|
const OVERRIDABLE = new Set(["method", "class-member"]);
|
|
17
21
|
/** The node naming the class one `extends` entry names. */
|
|
@@ -82,8 +86,9 @@ export function overrideReferences(project, held, chains, testFiles) {
|
|
|
82
86
|
if (inTarget.length === 0 &&
|
|
83
87
|
declared !== UNANSWERED &&
|
|
84
88
|
(declared.flags & SymbolFlags.Class) !== 0 &&
|
|
85
|
-
|
|
86
|
-
|
|
89
|
+
// The target's augmentation of an outside class merges an interface into it,
|
|
90
|
+
// so only a class declaration says where the class is.
|
|
91
|
+
declared.declarations.some((handle) => CLASS_DECLARATIONS.has(handle.kind) && !own.has(handle.path))) {
|
|
87
92
|
outsideBases.set(id, declared);
|
|
88
93
|
}
|
|
89
94
|
}
|
package/dist/src/paths.js
CHANGED
|
@@ -61,6 +61,15 @@ export function dirnamePath(path) {
|
|
|
61
61
|
}
|
|
62
62
|
return cut === 0 ? SEPARATOR : normalized.slice(0, cut);
|
|
63
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* The extension of one path's last segment, from its last full stop, or the empty string
|
|
66
|
+
* where it has none. A segment that only starts with a full stop has none.
|
|
67
|
+
*/
|
|
68
|
+
export function extensionOf(path) {
|
|
69
|
+
const name = path.slice(path.lastIndexOf(SEPARATOR) + 1);
|
|
70
|
+
const dot = name.lastIndexOf(".");
|
|
71
|
+
return dot <= 0 ? "" : name.slice(dot);
|
|
72
|
+
}
|
|
64
73
|
/** One path resolved against a directory, absolute where that directory is. */
|
|
65
74
|
export function resolvePath(against, path) {
|
|
66
75
|
return isAbsolutePath(path) ? normalizePath(path) : joinPath(against, path);
|
package/dist/src/references.js
CHANGED
|
@@ -661,19 +661,29 @@ export function references(project, held, targetRoot, options) {
|
|
|
661
661
|
relativePath(targetRoot, file.fileName) === undefined);
|
|
662
662
|
const ownPaths = project.ownPaths();
|
|
663
663
|
const fileIds = new Set(held.symbols.filter((symbol) => symbol.kind === "file").map((symbol) => symbol.id));
|
|
664
|
-
let
|
|
665
|
-
/**
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
664
|
+
let moduleBodies;
|
|
665
|
+
/**
|
|
666
|
+
* The declarations one module holds directly: a file's top level, or the body of a
|
|
667
|
+
* `declare module` whose name is a string. Any other declaration holds none here.
|
|
668
|
+
*/
|
|
669
|
+
const declaredIn = (moduleId) => {
|
|
670
|
+
if (moduleBodies === undefined) {
|
|
671
|
+
const byModule = new Map();
|
|
669
672
|
for (const symbol of held.symbols) {
|
|
670
|
-
if (fileIds.has(symbol.parent)) {
|
|
671
|
-
|
|
673
|
+
if (!fileIds.has(symbol.parent) && !held.modules.has(symbol.parent)) {
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
676
|
+
const siblings = byModule.get(symbol.parent);
|
|
677
|
+
if (siblings === undefined) {
|
|
678
|
+
byModule.set(symbol.parent, [symbol]);
|
|
679
|
+
}
|
|
680
|
+
else {
|
|
681
|
+
siblings.push(symbol);
|
|
672
682
|
}
|
|
673
683
|
}
|
|
674
|
-
|
|
684
|
+
moduleBodies = byModule;
|
|
675
685
|
}
|
|
676
|
-
return
|
|
686
|
+
return moduleBodies.get(moduleId) ?? [];
|
|
677
687
|
};
|
|
678
688
|
const exportsCache = new Map();
|
|
679
689
|
/**
|
|
@@ -975,8 +985,11 @@ export function references(project, held, targetRoot, options) {
|
|
|
975
985
|
}
|
|
976
986
|
for (const url of urls) {
|
|
977
987
|
const constructor = url.constructor === undefined ? undefined : answered.get(url.constructor);
|
|
988
|
+
// An augmentation of the global `URL` merges into it, which keeps the
|
|
989
|
+
// library's declaration; a `URL` the target declares has no other.
|
|
978
990
|
if (typeof constructor === "object" &&
|
|
979
|
-
constructor.declarations.
|
|
991
|
+
constructor.declarations.length > 0 &&
|
|
992
|
+
constructor.declarations.every((handle) => ownPaths.has(handle.path))) {
|
|
980
993
|
continue;
|
|
981
994
|
}
|
|
982
995
|
const loaded = specifierCandidates(dirnamePath(file.fileName), url.specifier)
|
|
@@ -1133,14 +1146,14 @@ export function references(project, held, targetRoot, options) {
|
|
|
1133
1146
|
}
|
|
1134
1147
|
let typedLookups = 0;
|
|
1135
1148
|
if (consumer === undefined) {
|
|
1136
|
-
found.push(...overrideReferences(project, held, chains, new Set(tests)));
|
|
1137
1149
|
const before = options.requestCount?.() ?? 0;
|
|
1150
|
+
found.push(...overrideReferences(project, held, chains, new Set(tests)));
|
|
1138
1151
|
found.push(...indexedAccessReferences(project, held, chains, root, new Set(tests)));
|
|
1139
1152
|
found.push(...elementAccessReferences(project, held, chains, root, new Set(tests)));
|
|
1140
1153
|
found.push(...keyofComparisonReferences(project, held, chains, root, new Set(tests)));
|
|
1141
1154
|
found.push(...unimplementedCalleeReads(project, held, chains, root, new Set(tests)));
|
|
1142
|
-
typedLookups = (options.requestCount?.() ?? 0) - before;
|
|
1143
1155
|
found.push(...moduleDeclarationReferences(project, held, root, new Set(tests)));
|
|
1156
|
+
typedLookups = (options.requestCount?.() ?? 0) - before;
|
|
1144
1157
|
}
|
|
1145
1158
|
const scripts = new Set(walked.filter((one) => one.file.externalModuleIndicator === undefined).map((one) => one.fileId));
|
|
1146
1159
|
found.push(...augmentationReferences(held, found, scripts));
|
|
@@ -90,8 +90,9 @@ export function reflectiveLookup(input) {
|
|
|
90
90
|
if (lookups.length === 0) {
|
|
91
91
|
return [];
|
|
92
92
|
}
|
|
93
|
-
// Whether each written `Reflect` is the global one, asked in one batch: a
|
|
94
|
-
//
|
|
93
|
+
// Whether each written `Reflect` is the global one, asked in one batch: a name
|
|
94
|
+
// declared only in the project's own files shadows it, and an augmentation of the
|
|
95
|
+
// global merges into it, which keeps the library's declaration.
|
|
95
96
|
const ownFiles = project.ownPaths();
|
|
96
97
|
const reflects = lookups.flatMap((lookup) => lookup.reflect === undefined ? [] : [lookup.reflect]);
|
|
97
98
|
const global = new Set();
|
|
@@ -99,10 +100,11 @@ export function reflectiveLookup(input) {
|
|
|
99
100
|
project.symbolsAt(reflects.map((node) => project.handle(node))).forEach((symbol, index) => {
|
|
100
101
|
const node = reflects[index];
|
|
101
102
|
// A name the checker did not answer for is read as the global, which retains.
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
const isGlobal = symbol === UNANSWERED ||
|
|
104
|
+
(symbol !== undefined &&
|
|
105
|
+
(symbol.declarations.length === 0 ||
|
|
106
|
+
symbol.declarations.some((declaration) => !ownFiles.has(declaration.path))));
|
|
107
|
+
if (node !== undefined && isGlobal) {
|
|
106
108
|
global.add(node);
|
|
107
109
|
}
|
|
108
110
|
});
|