@zzzen/pyright-internal 1.2.0-dev.20220710 → 1.2.0-dev.20220717
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/analyzer/binder.js +14 -0
- package/dist/analyzer/binder.js.map +1 -1
- package/dist/analyzer/checker.js +47 -42
- package/dist/analyzer/checker.js.map +1 -1
- package/dist/analyzer/codeFlowEngine.d.ts +0 -1
- package/dist/analyzer/codeFlowEngine.js +190 -196
- package/dist/analyzer/codeFlowEngine.js.map +1 -1
- package/dist/analyzer/codeFlowTypes.d.ts +1 -1
- package/dist/analyzer/codeFlowTypes.js.map +1 -1
- package/dist/analyzer/constraintSolver.js +1 -1
- package/dist/analyzer/constraintSolver.js.map +1 -1
- package/dist/analyzer/importResolver.js +3 -2
- package/dist/analyzer/importResolver.js.map +1 -1
- package/dist/analyzer/parseTreeUtils.d.ts +3 -0
- package/dist/analyzer/parseTreeUtils.js +37 -3
- package/dist/analyzer/parseTreeUtils.js.map +1 -1
- package/dist/analyzer/service.js +1 -0
- package/dist/analyzer/service.js.map +1 -1
- package/dist/analyzer/sourceFile.js +40 -9
- package/dist/analyzer/sourceFile.js.map +1 -1
- package/dist/analyzer/typeEvaluator.d.ts +1 -1
- package/dist/analyzer/typeEvaluator.js +147 -82
- package/dist/analyzer/typeEvaluator.js.map +1 -1
- package/dist/analyzer/typeEvaluatorTypes.d.ts +4 -4
- package/dist/analyzer/typeEvaluatorWithTracker.js +8 -7
- package/dist/analyzer/typeEvaluatorWithTracker.js.map +1 -1
- package/dist/analyzer/typeGuards.js +1 -1
- package/dist/analyzer/typeGuards.js.map +1 -1
- package/dist/analyzer/typeUtils.d.ts +2 -1
- package/dist/analyzer/typeUtils.js +80 -4
- package/dist/analyzer/typeUtils.js.map +1 -1
- package/dist/analyzer/typedDicts.d.ts +1 -0
- package/dist/analyzer/typedDicts.js +22 -1
- package/dist/analyzer/typedDicts.js.map +1 -1
- package/dist/analyzer/types.d.ts +8 -0
- package/dist/analyzer/types.js +53 -0
- package/dist/analyzer/types.js.map +1 -1
- package/dist/common/diagnostic.d.ts +2 -1
- package/dist/common/diagnostic.js +2 -1
- package/dist/common/diagnostic.js.map +1 -1
- package/dist/common/diagnosticSink.d.ts +3 -0
- package/dist/common/diagnosticSink.js +15 -2
- package/dist/common/diagnosticSink.js.map +1 -1
- package/dist/languageServerBase.d.ts +2 -6
- package/dist/languageServerBase.js +27 -16
- package/dist/languageServerBase.js.map +1 -1
- package/dist/languageService/completionProvider.d.ts +15 -11
- package/dist/languageService/completionProvider.js +76 -5
- package/dist/languageService/completionProvider.js.map +1 -1
- package/dist/languageService/tooltipUtils.js +1 -3
- package/dist/languageService/tooltipUtils.js.map +1 -1
- package/dist/localization/localize.d.ts +5 -0
- package/dist/localization/localize.js +2 -0
- package/dist/localization/localize.js.map +1 -1
- package/dist/localization/package.nls.en-us.json +2 -0
- package/dist/parser/parser.js +9 -1
- package/dist/parser/parser.js.map +1 -1
- package/dist/pyright.js +3 -1
- package/dist/pyright.js.map +1 -1
- package/dist/pyrightFileSystem.d.ts +1 -1
- package/dist/pyrightFileSystem.js +11 -1
- package/dist/pyrightFileSystem.js.map +1 -1
- package/dist/tests/chainedSourceFiles.test.js +2 -0
- package/dist/tests/chainedSourceFiles.test.js.map +1 -1
- package/dist/tests/fourslash/completions.commitChars.fourslash.d.ts +1 -0
- package/dist/tests/fourslash/completions.commitChars.fourslash.js +47 -0
- package/dist/tests/fourslash/completions.commitChars.fourslash.js.map +1 -0
- package/dist/tests/fourslash/completions.triggers.fourslash.d.ts +1 -0
- package/dist/tests/fourslash/completions.triggers.fourslash.js +29 -0
- package/dist/tests/fourslash/completions.triggers.fourslash.js.map +1 -0
- package/dist/tests/fourslash/fourslash.d.ts +1 -0
- package/dist/tests/harness/fourslash/testState.js +11 -2
- package/dist/tests/harness/fourslash/testState.js.map +1 -1
- package/dist/tests/pyrightFileSystem.test.js +28 -0
- package/dist/tests/pyrightFileSystem.test.js.map +1 -1
- package/dist/tests/testUtils.d.ts +2 -1
- package/dist/tests/testUtils.js +8 -5
- package/dist/tests/testUtils.js.map +1 -1
- package/dist/tests/typeEvaluator1.test.js +2 -2
- package/dist/tests/typeEvaluator1.test.js.map +1 -1
- package/dist/tests/typeEvaluator2.test.js +12 -4
- package/dist/tests/typeEvaluator2.test.js.map +1 -1
- package/dist/tests/typeEvaluator3.test.js +1 -1
- package/dist/tests/typeEvaluator4.test.js +8 -0
- package/dist/tests/typeEvaluator4.test.js.map +1 -1
- package/package.json +1 -1
@@ -5,7 +5,6 @@ import { Type, TypeVarType } from './types';
|
|
5
5
|
export interface FlowNodeTypeResult {
|
6
6
|
type: Type | undefined;
|
7
7
|
isIncomplete: boolean;
|
8
|
-
isRecursionSentinel?: boolean;
|
9
8
|
generationCount?: number | undefined;
|
10
9
|
incompleteType?: Type | undefined;
|
11
10
|
incompleteSubtypes?: IncompleteSubtypeInfo[] | undefined;
|
@@ -20,6 +20,7 @@ const codeFlowTypes_1 = require("./codeFlowTypes");
|
|
20
20
|
const codeFlowUtils_1 = require("./codeFlowUtils");
|
21
21
|
const parseTreeUtils_1 = require("./parseTreeUtils");
|
22
22
|
const typeCache_1 = require("./typeCache");
|
23
|
+
const typedDicts_1 = require("./typedDicts");
|
23
24
|
const typeGuards_1 = require("./typeGuards");
|
24
25
|
const types_1 = require("./types");
|
25
26
|
const typeUtils_1 = require("./typeUtils");
|
@@ -42,7 +43,10 @@ function getCodeFlowEngine(evaluator, speculativeTypeTracker) {
|
|
42
43
|
function getFlowNodeTypeCacheForReference(referenceKey) {
|
43
44
|
let flowNodeTypeCache = flowNodeTypeCacheSet.get(referenceKey);
|
44
45
|
if (!flowNodeTypeCache) {
|
45
|
-
flowNodeTypeCache =
|
46
|
+
flowNodeTypeCache = {
|
47
|
+
cache: new Map(),
|
48
|
+
pendingNodes: new Set(),
|
49
|
+
};
|
46
50
|
flowNodeTypeCacheSet.set(referenceKey, flowNodeTypeCache);
|
47
51
|
}
|
48
52
|
return flowNodeTypeCache;
|
@@ -58,12 +62,12 @@ function getCodeFlowEngine(evaluator, speculativeTypeTracker) {
|
|
58
62
|
: '.';
|
59
63
|
const flowNodeTypeCache = getFlowNodeTypeCacheForReference(referenceKeyWithSymbolId);
|
60
64
|
// Caches the type of the flow node in our local cache, keyed by the flow node ID.
|
61
|
-
function setCacheEntry(flowNode, type, isIncomplete
|
65
|
+
function setCacheEntry(flowNode, type, isIncomplete) {
|
62
66
|
if (!isIncomplete) {
|
63
67
|
flowIncompleteGeneration++;
|
64
68
|
}
|
65
69
|
else if (type) {
|
66
|
-
const prevEntry = flowNodeTypeCache.get(flowNode.id);
|
70
|
+
const prevEntry = flowNodeTypeCache.cache.get(flowNode.id);
|
67
71
|
if (prevEntry === undefined) {
|
68
72
|
flowIncompleteGeneration++;
|
69
73
|
}
|
@@ -83,21 +87,19 @@ function getCodeFlowEngine(evaluator, speculativeTypeTracker) {
|
|
83
87
|
type,
|
84
88
|
incompleteSubtypes: [],
|
85
89
|
generationCount: flowIncompleteGeneration,
|
86
|
-
isRecursionSentinel,
|
87
90
|
}
|
88
91
|
: type;
|
89
|
-
flowNodeTypeCache.set(flowNode.id, entry);
|
90
|
-
speculativeTypeTracker.trackEntry(flowNodeTypeCache, flowNode.id);
|
92
|
+
flowNodeTypeCache.cache.set(flowNode.id, entry);
|
93
|
+
speculativeTypeTracker.trackEntry(flowNodeTypeCache.cache, flowNode.id);
|
91
94
|
return {
|
92
95
|
type,
|
93
96
|
isIncomplete,
|
94
|
-
isRecursionSentinel,
|
95
97
|
generationCount: flowIncompleteGeneration,
|
96
98
|
incompleteSubtypes: isIncomplete ? [] : undefined,
|
97
99
|
};
|
98
100
|
}
|
99
101
|
function setIncompleteSubtype(flowNode, index, type, isIncomplete, isPending, evaluationCount) {
|
100
|
-
const cachedEntry = flowNodeTypeCache.get(flowNode.id);
|
102
|
+
const cachedEntry = flowNodeTypeCache.cache.get(flowNode.id);
|
101
103
|
if (cachedEntry === undefined || !(0, typeCache_1.isIncompleteType)(cachedEntry)) {
|
102
104
|
(0, debug_1.fail)('setIncompleteSubtype can be called only on a valid incomplete cache entry');
|
103
105
|
}
|
@@ -123,10 +125,10 @@ function getCodeFlowEngine(evaluator, speculativeTypeTracker) {
|
|
123
125
|
return getCacheEntry(flowNode);
|
124
126
|
}
|
125
127
|
function getCacheEntry(flowNode) {
|
126
|
-
if (!flowNodeTypeCache.has(flowNode.id)) {
|
128
|
+
if (!flowNodeTypeCache.cache.has(flowNode.id)) {
|
127
129
|
return undefined;
|
128
130
|
}
|
129
|
-
const cachedEntry = flowNodeTypeCache.get(flowNode.id);
|
131
|
+
const cachedEntry = flowNodeTypeCache.cache.get(flowNode.id);
|
130
132
|
if (cachedEntry === undefined) {
|
131
133
|
return {
|
132
134
|
type: cachedEntry,
|
@@ -156,11 +158,10 @@ function getCodeFlowEngine(evaluator, speculativeTypeTracker) {
|
|
156
158
|
isIncomplete: true,
|
157
159
|
incompleteSubtypes: cachedEntry.incompleteSubtypes,
|
158
160
|
generationCount: cachedEntry.generationCount,
|
159
|
-
isRecursionSentinel: cachedEntry.isRecursionSentinel,
|
160
161
|
};
|
161
162
|
}
|
162
163
|
function deleteCacheEntry(flowNode) {
|
163
|
-
flowNodeTypeCache.delete(flowNode.id);
|
164
|
+
flowNodeTypeCache.cache.delete(flowNode.id);
|
164
165
|
}
|
165
166
|
function evaluateAssignmentFlowNode(flowNode) {
|
166
167
|
// For function and class nodes, the reference node is the name
|
@@ -177,10 +178,20 @@ function getCodeFlowEngine(evaluator, speculativeTypeTracker) {
|
|
177
178
|
evaluator.evaluateTypesForStatement(flowNode.node);
|
178
179
|
});
|
179
180
|
}
|
181
|
+
function preventRecursion(flowNode, callback) {
|
182
|
+
flowNodeTypeCache.pendingNodes.add(flowNode.id);
|
183
|
+
try {
|
184
|
+
return callback();
|
185
|
+
}
|
186
|
+
finally {
|
187
|
+
flowNodeTypeCache.pendingNodes.delete(flowNode.id);
|
188
|
+
}
|
189
|
+
}
|
180
190
|
// If this flow has no knowledge of the target expression, it returns undefined.
|
181
191
|
// If the start flow node for this scope is reachable, the typeAtStart value is
|
182
192
|
// returned.
|
183
193
|
function getTypeFromFlowNode(flowNode) {
|
194
|
+
var _a;
|
184
195
|
let curFlowNode = flowNode;
|
185
196
|
// This is a frequently-called routine, so it's a good place to call
|
186
197
|
// the cancellation check. If the operation is canceled, an exception
|
@@ -199,6 +210,10 @@ function getCodeFlowEngine(evaluator, speculativeTypeTracker) {
|
|
199
210
|
return { type: cachedEntry.type, isIncomplete: true };
|
200
211
|
}
|
201
212
|
}
|
213
|
+
// Check for recursion.
|
214
|
+
if (flowNodeTypeCache.pendingNodes.has(curFlowNode.id)) {
|
215
|
+
return { type: cachedEntry === null || cachedEntry === void 0 ? void 0 : cachedEntry.type, isIncomplete: true };
|
216
|
+
}
|
202
217
|
if (curFlowNode.flags & codeFlowTypes_1.FlowFlags.Unreachable) {
|
203
218
|
// We can get here if there are nodes in a compound logical expression
|
204
219
|
// (e.g. "False and x") that are never executed but are evaluated.
|
@@ -222,51 +237,72 @@ function getCodeFlowEngine(evaluator, speculativeTypeTracker) {
|
|
222
237
|
}
|
223
238
|
if (curFlowNode.flags & codeFlowTypes_1.FlowFlags.Assignment) {
|
224
239
|
const assignmentFlowNode = curFlowNode;
|
240
|
+
const targetNode = assignmentFlowNode.node;
|
225
241
|
// Are we targeting the same symbol? We need to do this extra check because the same
|
226
242
|
// symbol name might refer to different symbols in different scopes (e.g. a list
|
227
243
|
// comprehension introduces a new scope).
|
228
244
|
if (reference) {
|
229
245
|
if (targetSymbolId === assignmentFlowNode.targetSymbolId &&
|
230
|
-
(0, parseTreeUtils_1.isMatchingExpression)(reference,
|
246
|
+
(0, parseTreeUtils_1.isMatchingExpression)(reference, targetNode)) {
|
231
247
|
// Is this a special "unbind" assignment? If so,
|
232
248
|
// we can handle it immediately without any further evaluation.
|
233
249
|
if (curFlowNode.flags & codeFlowTypes_1.FlowFlags.Unbind) {
|
234
250
|
return setCacheEntry(curFlowNode, types_1.UnboundType.create(), /* isIncomplete */ false);
|
235
251
|
}
|
236
|
-
|
237
|
-
// case (something like "int: int = 4"). Avoid infinite recursion
|
238
|
-
// by returning an undefined type.
|
239
|
-
if (cachedEntry === null || cachedEntry === void 0 ? void 0 : cachedEntry.isRecursionSentinel) {
|
240
|
-
return { type: undefined, isIncomplete: true };
|
241
|
-
}
|
242
|
-
// Set the cache entry to undefined before evaluating the
|
243
|
-
// expression in case it depends on itself.
|
244
|
-
setCacheEntry(curFlowNode, reference ? undefined : initialType,
|
245
|
-
/* isIncomplete */ true,
|
246
|
-
/* isRecursionSentinel */ true);
|
247
|
-
let flowTypeResult = evaluateAssignmentFlowNode(assignmentFlowNode);
|
252
|
+
let flowTypeResult = preventRecursion(curFlowNode, () => evaluateAssignmentFlowNode(assignmentFlowNode));
|
248
253
|
if (flowTypeResult) {
|
249
254
|
if ((0, typeUtils_1.isTypeAliasPlaceholder)(flowTypeResult.type)) {
|
250
255
|
flowTypeResult = undefined;
|
251
256
|
}
|
252
257
|
else if (reference.nodeType === 35 /* MemberAccess */ &&
|
253
|
-
evaluator.isAsymmetricDescriptorAssignment(
|
258
|
+
evaluator.isAsymmetricDescriptorAssignment(targetNode)) {
|
254
259
|
flowTypeResult = undefined;
|
255
260
|
}
|
256
261
|
}
|
257
262
|
return setCacheEntry(curFlowNode, flowTypeResult === null || flowTypeResult === void 0 ? void 0 : flowTypeResult.type, !!(flowTypeResult === null || flowTypeResult === void 0 ? void 0 : flowTypeResult.isIncomplete));
|
258
263
|
}
|
259
|
-
else
|
260
|
-
//
|
261
|
-
//
|
262
|
-
//
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
264
|
+
else {
|
265
|
+
// Is this a simple assignment to an index expression? If so, it could
|
266
|
+
// be assigning to a TypedDict, which requires narrowing of the expression's
|
267
|
+
// base type.
|
268
|
+
if (targetNode.nodeType === 24 /* Index */ &&
|
269
|
+
(0, parseTreeUtils_1.isMatchingExpression)(reference, targetNode.baseExpression)) {
|
270
|
+
if (((_a = targetNode.parent) === null || _a === void 0 ? void 0 : _a.nodeType) === 3 /* Assignment */ &&
|
271
|
+
targetNode.items.length === 1 &&
|
272
|
+
!targetNode.trailingComma &&
|
273
|
+
!targetNode.items[0].name &&
|
274
|
+
targetNode.items[0].argumentCategory === 0 /* Simple */ &&
|
275
|
+
targetNode.items[0].valueExpression.nodeType === 48 /* StringList */ &&
|
276
|
+
targetNode.items[0].valueExpression.strings.length === 1 &&
|
277
|
+
targetNode.items[0].valueExpression.strings[0].nodeType === 49 /* String */) {
|
278
|
+
const keyValue = targetNode.items[0].valueExpression.strings[0].value;
|
279
|
+
const narrowedResult = preventRecursion(assignmentFlowNode, () => {
|
280
|
+
const flowTypeResult = getTypeFromFlowNode(assignmentFlowNode.antecedent);
|
281
|
+
if (flowTypeResult.type) {
|
282
|
+
flowTypeResult.type = (0, typeUtils_1.mapSubtypes)(flowTypeResult.type, (subtype) => {
|
283
|
+
if ((0, types_1.isClass)(subtype) && types_1.ClassType.isTypedDictClass(subtype)) {
|
284
|
+
return (0, typedDicts_1.narrowForKeyAssignment)(subtype, keyValue);
|
285
|
+
}
|
286
|
+
return subtype;
|
287
|
+
});
|
288
|
+
}
|
289
|
+
return flowTypeResult;
|
290
|
+
});
|
291
|
+
return setCacheEntry(curFlowNode, narrowedResult === null || narrowedResult === void 0 ? void 0 : narrowedResult.type, !!(narrowedResult === null || narrowedResult === void 0 ? void 0 : narrowedResult.isIncomplete));
|
292
|
+
}
|
293
|
+
}
|
294
|
+
if ((0, parseTreeUtils_1.isPartialMatchingExpression)(reference, targetNode)) {
|
295
|
+
// If the node partially matches the reference, we need to "kill" any narrowed
|
296
|
+
// types further above this point. For example, if we see the sequence
|
297
|
+
// a.b = 3
|
298
|
+
// a = Foo()
|
299
|
+
// x = a.b
|
300
|
+
// The type of "a.b" can no longer be assumed to be Literal[3].
|
301
|
+
return {
|
302
|
+
type: initialType,
|
303
|
+
isIncomplete: isInitialTypeIncomplete,
|
304
|
+
};
|
305
|
+
}
|
270
306
|
}
|
271
307
|
}
|
272
308
|
curFlowNode = assignmentFlowNode.antecedent;
|
@@ -312,22 +348,12 @@ function getCodeFlowEngine(evaluator, speculativeTypeTracker) {
|
|
312
348
|
continue;
|
313
349
|
}
|
314
350
|
}
|
315
|
-
return getTypeFromLoopFlowNode(loopNode);
|
351
|
+
return getTypeFromLoopFlowNode(loopNode, cachedEntry);
|
316
352
|
}
|
317
353
|
if (curFlowNode.flags & (codeFlowTypes_1.FlowFlags.TrueCondition | codeFlowTypes_1.FlowFlags.FalseCondition)) {
|
318
354
|
const conditionalFlowNode = curFlowNode;
|
319
355
|
if (reference) {
|
320
|
-
|
321
|
-
if (cachedEntry === null || cachedEntry === void 0 ? void 0 : cachedEntry.isRecursionSentinel) {
|
322
|
-
return cachedEntry;
|
323
|
-
}
|
324
|
-
// Before calling getTypeNarrowingCallback, set the type
|
325
|
-
// of this flow node in the cache to prevent recursion.
|
326
|
-
setCacheEntry(curFlowNode,
|
327
|
-
/* type */ undefined,
|
328
|
-
/* isIncomplete */ true,
|
329
|
-
/* isRecursionSentinel */ true);
|
330
|
-
try {
|
356
|
+
const narrowedResult = preventRecursion(curFlowNode, () => {
|
331
357
|
const typeNarrowingCallback = (0, typeGuards_1.getTypeNarrowingCallback)(evaluator, reference, conditionalFlowNode.expression, !!(conditionalFlowNode.flags &
|
332
358
|
(codeFlowTypes_1.FlowFlags.TrueCondition | codeFlowTypes_1.FlowFlags.TrueNeverCondition)));
|
333
359
|
if (typeNarrowingCallback) {
|
@@ -338,13 +364,10 @@ function getCodeFlowEngine(evaluator, speculativeTypeTracker) {
|
|
338
364
|
}
|
339
365
|
return setCacheEntry(curFlowNode, flowType, flowTypeResult.isIncomplete);
|
340
366
|
}
|
341
|
-
|
342
|
-
}
|
343
|
-
|
344
|
-
|
345
|
-
// doesn't handle it well during single stepping.
|
346
|
-
deleteCacheEntry(curFlowNode);
|
347
|
-
throw e;
|
367
|
+
return undefined;
|
368
|
+
});
|
369
|
+
if (narrowedResult) {
|
370
|
+
return narrowedResult;
|
348
371
|
}
|
349
372
|
}
|
350
373
|
curFlowNode = conditionalFlowNode.antecedent;
|
@@ -363,12 +386,7 @@ function getCodeFlowEngine(evaluator, speculativeTypeTracker) {
|
|
363
386
|
const symbolWithScope = evaluator.lookUpSymbolRecursive(conditionalFlowNode.reference, conditionalFlowNode.reference.value,
|
364
387
|
/* honorCodeFlow */ false);
|
365
388
|
if (symbolWithScope && symbolWithScope.symbol.getTypedDeclarations().length > 0) {
|
366
|
-
|
367
|
-
// of this flow node in the cache to prevent recursion.
|
368
|
-
setCacheEntry(curFlowNode, reference ? undefined : initialType,
|
369
|
-
/* isIncomplete */ true,
|
370
|
-
/* isRecursionSentinel */ true);
|
371
|
-
try {
|
389
|
+
const result = preventRecursion(curFlowNode, () => {
|
372
390
|
const typeNarrowingCallback = (0, typeGuards_1.getTypeNarrowingCallback)(evaluator, conditionalFlowNode.reference, conditionalFlowNode.expression, !!(conditionalFlowNode.flags &
|
373
391
|
(codeFlowTypes_1.FlowFlags.TrueCondition | codeFlowTypes_1.FlowFlags.TrueNeverCondition)));
|
374
392
|
if (typeNarrowingCallback) {
|
@@ -379,13 +397,10 @@ function getCodeFlowEngine(evaluator, speculativeTypeTracker) {
|
|
379
397
|
return setCacheEntry(curFlowNode, undefined, !!refTypeInfo.isIncomplete);
|
380
398
|
}
|
381
399
|
}
|
382
|
-
|
383
|
-
}
|
384
|
-
|
385
|
-
|
386
|
-
// doesn't handle it well during single stepping.
|
387
|
-
deleteCacheEntry(curFlowNode);
|
388
|
-
throw e;
|
400
|
+
return undefined;
|
401
|
+
});
|
402
|
+
if (result) {
|
403
|
+
return result;
|
389
404
|
}
|
390
405
|
}
|
391
406
|
}
|
@@ -445,19 +460,10 @@ function getCodeFlowEngine(evaluator, speculativeTypeTracker) {
|
|
445
460
|
if (reference && reference.nodeType === 38 /* Name */) {
|
446
461
|
const nameValue = reference.value;
|
447
462
|
if (wildcardImportFlowNode.names.some((name) => name === nameValue)) {
|
448
|
-
|
449
|
-
setCacheEntry(curFlowNode,
|
450
|
-
/* type */ undefined,
|
451
|
-
/* isIncomplete */ true,
|
452
|
-
/* isRecursionSentinel */ true);
|
453
|
-
try {
|
463
|
+
return preventRecursion(curFlowNode, () => {
|
454
464
|
const type = getTypeFromWildcardImport(wildcardImportFlowNode, nameValue);
|
455
465
|
return setCacheEntry(curFlowNode, type, /* isIncomplete */ false);
|
456
|
-
}
|
457
|
-
catch (e) {
|
458
|
-
deleteCacheEntry(curFlowNode);
|
459
|
-
throw e;
|
460
|
-
}
|
466
|
+
});
|
461
467
|
}
|
462
468
|
}
|
463
469
|
curFlowNode = wildcardImportFlowNode.antecedent;
|
@@ -472,130 +478,124 @@ function getCodeFlowEngine(evaluator, speculativeTypeTracker) {
|
|
472
478
|
const typesToCombine = [];
|
473
479
|
let sawIncomplete = false;
|
474
480
|
let isProvenReachable = false;
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
}
|
493
|
-
if (
|
494
|
-
|
481
|
+
return preventRecursion(branchNode, () => {
|
482
|
+
branchNode.antecedents.forEach((antecedent) => {
|
483
|
+
// If we're solving for "reachability", and we have now proven
|
484
|
+
// reachability, there's no reason to do more work.
|
485
|
+
if (reference === undefined && isProvenReachable) {
|
486
|
+
return;
|
487
|
+
}
|
488
|
+
const flowTypeResult = getTypeFromFlowNode(antecedent);
|
489
|
+
if (flowTypeResult.isIncomplete) {
|
490
|
+
sawIncomplete = true;
|
491
|
+
}
|
492
|
+
if (reference === undefined && flowTypeResult.type !== undefined) {
|
493
|
+
isProvenReachable = true;
|
494
|
+
}
|
495
|
+
if (flowTypeResult.type) {
|
496
|
+
typesToCombine.push(flowTypeResult.type);
|
497
|
+
}
|
498
|
+
});
|
499
|
+
if (isProvenReachable) {
|
500
|
+
return setCacheEntry(branchNode, initialType, /* isIncomplete */ false);
|
495
501
|
}
|
502
|
+
const effectiveType = typesToCombine.length > 0 ? (0, types_1.combineTypes)(typesToCombine) : undefined;
|
503
|
+
return setCacheEntry(branchNode, effectiveType, sawIncomplete);
|
496
504
|
});
|
497
|
-
if (isProvenReachable) {
|
498
|
-
return setCacheEntry(branchNode, initialType, /* isIncomplete */ false);
|
499
|
-
}
|
500
|
-
const effectiveType = typesToCombine.length > 0 ? (0, types_1.combineTypes)(typesToCombine) : undefined;
|
501
|
-
return setCacheEntry(branchNode, effectiveType, sawIncomplete);
|
502
505
|
}
|
503
|
-
function getTypeFromLoopFlowNode(loopNode) {
|
504
|
-
var _a, _b;
|
505
|
-
// See if we've been here before. If so, there will be an incomplete cache entry.
|
506
|
-
let cacheEntry = getCacheEntry(loopNode);
|
506
|
+
function getTypeFromLoopFlowNode(loopNode, cacheEntry) {
|
507
507
|
// The type result from one antecedent may depend on the type
|
508
508
|
// result from another, so loop up to one time for each
|
509
509
|
// antecedent in the loop.
|
510
510
|
const maxAttemptCount = loopNode.antecedents.length;
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
cacheEntry.incompleteSubtypes.
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
cacheEntry = getCacheEntry(loopNode);
|
537
|
-
// Have we already been here (i.e. does the entry exist and is
|
538
|
-
// not marked "pending")? If so, we can use the type that was already
|
539
|
-
// computed if it is complete.
|
540
|
-
const subtypeEntry = cacheEntry.incompleteSubtypes !== undefined && index < cacheEntry.incompleteSubtypes.length
|
541
|
-
? cacheEntry.incompleteSubtypes[index]
|
542
|
-
: undefined;
|
543
|
-
if (subtypeEntry === undefined || (!(subtypeEntry === null || subtypeEntry === void 0 ? void 0 : subtypeEntry.isPending) && (subtypeEntry === null || subtypeEntry === void 0 ? void 0 : subtypeEntry.isIncomplete))) {
|
544
|
-
const entryEvaluationCount = subtypeEntry === undefined ? 0 : subtypeEntry.evaluationCount;
|
545
|
-
// Set this entry to "pending" to prevent infinite recursion.
|
546
|
-
// We'll mark it "not pending" below.
|
547
|
-
cacheEntry = setIncompleteSubtype(loopNode, index, (_a = subtypeEntry === null || subtypeEntry === void 0 ? void 0 : subtypeEntry.type) !== null && _a !== void 0 ? _a : (reference ? undefined : initialType),
|
548
|
-
/* isIncomplete */ true,
|
549
|
-
/* isPending */ true, entryEvaluationCount);
|
550
|
-
try {
|
551
|
-
const flowTypeResult = getTypeFromFlowNode(antecedent);
|
552
|
-
if (flowTypeResult.isIncomplete) {
|
553
|
-
sawIncomplete = true;
|
554
|
-
}
|
555
|
-
cacheEntry = setIncompleteSubtype(loopNode, index, flowTypeResult.type, flowTypeResult.isIncomplete,
|
556
|
-
/* isPending */ false, entryEvaluationCount + 1);
|
511
|
+
return preventRecursion(loopNode, () => {
|
512
|
+
var _a, _b;
|
513
|
+
if (cacheEntry === undefined) {
|
514
|
+
// We haven't been here before, so create a new incomplete cache entry.
|
515
|
+
cacheEntry = setCacheEntry(loopNode, reference ? undefined : initialType,
|
516
|
+
/* isIncomplete */ true);
|
517
|
+
}
|
518
|
+
else if ((_a = cacheEntry.incompleteSubtypes) === null || _a === void 0 ? void 0 : _a.some((subtype) => subtype.isPending)) {
|
519
|
+
// If there are pending entries that have not been evaluated even once,
|
520
|
+
// treat it as incomplete.
|
521
|
+
const isIncomplete = cacheEntry.incompleteSubtypes.length < loopNode.antecedents.length ||
|
522
|
+
cacheEntry.incompleteSubtypes.some((subtype) => subtype.isPending && subtype.evaluationCount < maxAttemptCount);
|
523
|
+
return { type: cacheEntry.type, isIncomplete };
|
524
|
+
}
|
525
|
+
let attemptCount = 0;
|
526
|
+
while (true) {
|
527
|
+
let sawIncomplete = false;
|
528
|
+
let isProvenReachable = reference === undefined &&
|
529
|
+
((_b = cacheEntry.incompleteSubtypes) === null || _b === void 0 ? void 0 : _b.some((subtype) => subtype.type !== undefined));
|
530
|
+
loopNode.antecedents.forEach((antecedent, index) => {
|
531
|
+
var _a;
|
532
|
+
// If we've trying to determine reachability and we've already proven
|
533
|
+
// reachability, then we're done.
|
534
|
+
if (reference === undefined && isProvenReachable) {
|
535
|
+
return;
|
557
536
|
}
|
558
|
-
|
559
|
-
|
537
|
+
cacheEntry = getCacheEntry(loopNode);
|
538
|
+
// Have we already been here (i.e. does the entry exist and is
|
539
|
+
// not marked "pending")? If so, we can use the type that was already
|
540
|
+
// computed if it is complete.
|
541
|
+
const subtypeEntry = cacheEntry.incompleteSubtypes !== undefined &&
|
542
|
+
index < cacheEntry.incompleteSubtypes.length
|
543
|
+
? cacheEntry.incompleteSubtypes[index]
|
544
|
+
: undefined;
|
545
|
+
if (subtypeEntry === undefined ||
|
546
|
+
(!(subtypeEntry === null || subtypeEntry === void 0 ? void 0 : subtypeEntry.isPending) && (subtypeEntry === null || subtypeEntry === void 0 ? void 0 : subtypeEntry.isIncomplete))) {
|
547
|
+
const entryEvaluationCount = subtypeEntry === undefined ? 0 : subtypeEntry.evaluationCount;
|
548
|
+
// Set this entry to "pending" to prevent infinite recursion.
|
549
|
+
// We'll mark it "not pending" below.
|
550
|
+
cacheEntry = setIncompleteSubtype(loopNode, index, (_a = subtypeEntry === null || subtypeEntry === void 0 ? void 0 : subtypeEntry.type) !== null && _a !== void 0 ? _a : (reference ? undefined : initialType),
|
560
551
|
/* isIncomplete */ true,
|
561
|
-
/* isPending */
|
562
|
-
|
552
|
+
/* isPending */ true, entryEvaluationCount);
|
553
|
+
try {
|
554
|
+
const flowTypeResult = getTypeFromFlowNode(antecedent);
|
555
|
+
if (flowTypeResult.isIncomplete) {
|
556
|
+
sawIncomplete = true;
|
557
|
+
}
|
558
|
+
cacheEntry = setIncompleteSubtype(loopNode, index, flowTypeResult.type, flowTypeResult.isIncomplete,
|
559
|
+
/* isPending */ false, entryEvaluationCount + 1);
|
560
|
+
}
|
561
|
+
catch (e) {
|
562
|
+
setIncompleteSubtype(loopNode, index, undefined,
|
563
|
+
/* isIncomplete */ true,
|
564
|
+
/* isPending */ false, entryEvaluationCount + 1);
|
565
|
+
throw e;
|
566
|
+
}
|
563
567
|
}
|
568
|
+
if (reference === undefined && (cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.type) !== undefined) {
|
569
|
+
isProvenReachable = true;
|
570
|
+
}
|
571
|
+
});
|
572
|
+
if (isProvenReachable) {
|
573
|
+
return setCacheEntry(loopNode, initialType, /* isIncomplete */ false);
|
564
574
|
}
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
// it. Otherwise we might end up resolving the cycle with a type
|
576
|
-
// that includes an undesirable unknown.
|
577
|
-
if (effectiveType) {
|
578
|
-
const typeWithoutUnknown = (0, types_1.removeIncompleteUnknownFromUnion)(effectiveType);
|
579
|
-
if (!(0, types_1.isNever)(typeWithoutUnknown)) {
|
580
|
-
effectiveType = typeWithoutUnknown;
|
575
|
+
let effectiveType = cacheEntry.type;
|
576
|
+
if (sawIncomplete) {
|
577
|
+
// If there is an incomplete "Unknown" type within a union type, remove
|
578
|
+
// it. Otherwise we might end up resolving the cycle with a type
|
579
|
+
// that includes an undesirable unknown.
|
580
|
+
if (effectiveType) {
|
581
|
+
const typeWithoutUnknown = (0, types_1.removeIncompleteUnknownFromUnion)(effectiveType);
|
582
|
+
if (!(0, types_1.isNever)(typeWithoutUnknown)) {
|
583
|
+
effectiveType = typeWithoutUnknown;
|
584
|
+
}
|
581
585
|
}
|
582
586
|
}
|
587
|
+
if (!sawIncomplete || attemptCount >= maxAttemptCount) {
|
588
|
+
return setCacheEntry(loopNode, effectiveType, /* isIncomplete */ false);
|
589
|
+
}
|
590
|
+
attemptCount++;
|
583
591
|
}
|
584
|
-
|
585
|
-
return setCacheEntry(loopNode, effectiveType, /* isIncomplete */ false);
|
586
|
-
}
|
587
|
-
attemptCount++;
|
588
|
-
}
|
592
|
+
});
|
589
593
|
}
|
590
594
|
function getTypeFromPreFinallyGateFlowNode(preFinallyFlowNode) {
|
591
595
|
if (preFinallyFlowNode.isGateClosed) {
|
592
596
|
return { type: undefined, isIncomplete: false };
|
593
597
|
}
|
594
|
-
|
595
|
-
setCacheEntry(preFinallyFlowNode, reference ? undefined : initialType,
|
596
|
-
/* isIncomplete */ true,
|
597
|
-
/* isRecursionSentinel */ true);
|
598
|
-
try {
|
598
|
+
return preventRecursion(preFinallyFlowNode, () => {
|
599
599
|
const flowTypeResult = getTypeFromFlowNode(preFinallyFlowNode.antecedent);
|
600
600
|
// We want to cache the type only if we're evaluating the "gate closed" path.
|
601
601
|
deleteCacheEntry(preFinallyFlowNode);
|
@@ -603,11 +603,7 @@ function getCodeFlowEngine(evaluator, speculativeTypeTracker) {
|
|
603
603
|
type: flowTypeResult.type,
|
604
604
|
isIncomplete: flowTypeResult.isIncomplete,
|
605
605
|
};
|
606
|
-
}
|
607
|
-
catch (e) {
|
608
|
-
deleteCacheEntry(preFinallyFlowNode);
|
609
|
-
throw e;
|
610
|
-
}
|
606
|
+
});
|
611
607
|
}
|
612
608
|
function getTypeFromPostFinallyFlowNode(postFinallyFlowNode) {
|
613
609
|
const wasGateClosed = postFinallyFlowNode.preFinallyGate.isGateClosed;
|
@@ -966,12 +962,10 @@ function getCodeFlowEngine(evaluator, speculativeTypeTracker) {
|
|
966
962
|
else if ((0, types_1.isOverloadedFunction)(callSubtype)) {
|
967
963
|
let overloadCount = 0;
|
968
964
|
let noReturnOverloadCount = 0;
|
969
|
-
callSubtype.
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
noReturnOverloadCount++;
|
974
|
-
}
|
965
|
+
types_1.OverloadedFunctionType.getOverloads(callSubtype).forEach((overload) => {
|
966
|
+
overloadCount++;
|
967
|
+
if (isFunctionNoReturn(overload, isCallAwaited)) {
|
968
|
+
noReturnOverloadCount++;
|
975
969
|
}
|
976
970
|
});
|
977
971
|
// Was at least one of the overloaded return types NoReturn?
|