@zzzen/pyright-internal 1.2.0-dev.2022-07-02 → 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.
Files changed (166) hide show
  1. package/README.md +82 -1
  2. package/dist/analyzer/aliasDeclarationUtils.js +2 -2
  3. package/dist/analyzer/analyzerFileInfo.d.ts +2 -1
  4. package/dist/analyzer/analyzerFileInfo.js.map +1 -1
  5. package/dist/analyzer/analyzerNodeInfo.d.ts +4 -1
  6. package/dist/analyzer/analyzerNodeInfo.js +12 -1
  7. package/dist/analyzer/analyzerNodeInfo.js.map +1 -1
  8. package/dist/analyzer/binder.d.ts +6 -1
  9. package/dist/analyzer/binder.js +148 -31
  10. package/dist/analyzer/binder.js.map +1 -1
  11. package/dist/analyzer/checker.d.ts +4 -1
  12. package/dist/analyzer/checker.js +176 -90
  13. package/dist/analyzer/checker.js.map +1 -1
  14. package/dist/analyzer/codeFlowEngine.d.ts +0 -1
  15. package/dist/analyzer/codeFlowEngine.js +196 -197
  16. package/dist/analyzer/codeFlowEngine.js.map +1 -1
  17. package/dist/analyzer/codeFlowTypes.d.ts +1 -1
  18. package/dist/analyzer/codeFlowTypes.js.map +1 -1
  19. package/dist/analyzer/constraintSolver.js +9 -6
  20. package/dist/analyzer/constraintSolver.js.map +1 -1
  21. package/dist/analyzer/declaration.d.ts +18 -6
  22. package/dist/analyzer/declaration.js +19 -9
  23. package/dist/analyzer/declaration.js.map +1 -1
  24. package/dist/analyzer/declarationUtils.d.ts +1 -1
  25. package/dist/analyzer/declarationUtils.js +19 -16
  26. package/dist/analyzer/declarationUtils.js.map +1 -1
  27. package/dist/analyzer/functionTransform.js +2 -1
  28. package/dist/analyzer/functionTransform.js.map +1 -1
  29. package/dist/analyzer/importResolver.js +3 -2
  30. package/dist/analyzer/importResolver.js.map +1 -1
  31. package/dist/analyzer/packageTypeVerifier.js +6 -6
  32. package/dist/analyzer/parseTreeUtils.d.ts +6 -3
  33. package/dist/analyzer/parseTreeUtils.js +65 -21
  34. package/dist/analyzer/parseTreeUtils.js.map +1 -1
  35. package/dist/analyzer/parseTreeWalker.d.ts +4 -1
  36. package/dist/analyzer/parseTreeWalker.js +19 -1
  37. package/dist/analyzer/parseTreeWalker.js.map +1 -1
  38. package/dist/analyzer/patternMatching.js +1 -1
  39. package/dist/analyzer/patternMatching.js.map +1 -1
  40. package/dist/analyzer/program.d.ts +2 -2
  41. package/dist/analyzer/program.js +1 -1
  42. package/dist/analyzer/program.js.map +1 -1
  43. package/dist/analyzer/properties.js +2 -0
  44. package/dist/analyzer/properties.js.map +1 -1
  45. package/dist/analyzer/protocols.d.ts +0 -1
  46. package/dist/analyzer/protocols.js +1 -63
  47. package/dist/analyzer/protocols.js.map +1 -1
  48. package/dist/analyzer/service.d.ts +3 -2
  49. package/dist/analyzer/service.js +4 -2
  50. package/dist/analyzer/service.js.map +1 -1
  51. package/dist/analyzer/sourceFile.d.ts +6 -1
  52. package/dist/analyzer/sourceFile.js +57 -14
  53. package/dist/analyzer/sourceFile.js.map +1 -1
  54. package/dist/analyzer/tracePrinter.js +8 -4
  55. package/dist/analyzer/tracePrinter.js.map +1 -1
  56. package/dist/analyzer/typeDocStringUtils.js +1 -1
  57. package/dist/analyzer/typeEvaluator.d.ts +1 -1
  58. package/dist/analyzer/typeEvaluator.js +839 -375
  59. package/dist/analyzer/typeEvaluator.js.map +1 -1
  60. package/dist/analyzer/typeEvaluatorTypes.d.ts +9 -7
  61. package/dist/analyzer/typeEvaluatorWithTracker.js +10 -7
  62. package/dist/analyzer/typeEvaluatorWithTracker.js.map +1 -1
  63. package/dist/analyzer/typeGuards.js +6 -1
  64. package/dist/analyzer/typeGuards.js.map +1 -1
  65. package/dist/analyzer/typePrinter.js +4 -1
  66. package/dist/analyzer/typePrinter.js.map +1 -1
  67. package/dist/analyzer/typeStubWriter.d.ts +4 -1
  68. package/dist/analyzer/typeStubWriter.js +36 -0
  69. package/dist/analyzer/typeStubWriter.js.map +1 -1
  70. package/dist/analyzer/typeUtils.d.ts +3 -2
  71. package/dist/analyzer/typeUtils.js +94 -13
  72. package/dist/analyzer/typeUtils.js.map +1 -1
  73. package/dist/analyzer/typedDicts.d.ts +1 -0
  74. package/dist/analyzer/typedDicts.js +25 -2
  75. package/dist/analyzer/typedDicts.js.map +1 -1
  76. package/dist/analyzer/types.d.ts +21 -5
  77. package/dist/analyzer/types.js +87 -11
  78. package/dist/analyzer/types.js.map +1 -1
  79. package/dist/common/diagnostic.d.ts +2 -1
  80. package/dist/common/diagnostic.js +2 -1
  81. package/dist/common/diagnostic.js.map +1 -1
  82. package/dist/common/diagnosticSink.d.ts +3 -0
  83. package/dist/common/diagnosticSink.js +15 -2
  84. package/dist/common/diagnosticSink.js.map +1 -1
  85. package/dist/languageServerBase.d.ts +5 -8
  86. package/dist/languageServerBase.js +30 -18
  87. package/dist/languageServerBase.js.map +1 -1
  88. package/dist/languageService/autoImporter.js +1 -1
  89. package/dist/languageService/callHierarchyProvider.js +9 -9
  90. package/dist/languageService/completionProvider.d.ts +15 -11
  91. package/dist/languageService/completionProvider.js +95 -18
  92. package/dist/languageService/completionProvider.js.map +1 -1
  93. package/dist/languageService/definitionProvider.js +3 -3
  94. package/dist/languageService/documentSymbolCollector.js +1 -1
  95. package/dist/languageService/documentSymbolProvider.js +10 -7
  96. package/dist/languageService/documentSymbolProvider.js.map +1 -1
  97. package/dist/languageService/hoverProvider.js +19 -5
  98. package/dist/languageService/hoverProvider.js.map +1 -1
  99. package/dist/languageService/indentationUtils.js +3 -2
  100. package/dist/languageService/indentationUtils.js.map +1 -1
  101. package/dist/languageService/insertionPointUtils.d.ts +9 -0
  102. package/dist/languageService/insertionPointUtils.js +110 -0
  103. package/dist/languageService/insertionPointUtils.js.map +1 -0
  104. package/dist/languageService/referencesProvider.js +8 -5
  105. package/dist/languageService/referencesProvider.js.map +1 -1
  106. package/dist/languageService/signatureHelpProvider.js +4 -2
  107. package/dist/languageService/signatureHelpProvider.js.map +1 -1
  108. package/dist/languageService/tooltipUtils.js +2 -4
  109. package/dist/languageService/tooltipUtils.js.map +1 -1
  110. package/dist/localization/localize.d.ts +32 -0
  111. package/dist/localization/localize.js +18 -0
  112. package/dist/localization/localize.js.map +1 -1
  113. package/dist/localization/package.nls.en-us.json +20 -2
  114. package/dist/parser/parseNodes.d.ts +41 -5
  115. package/dist/parser/parseNodes.js +83 -4
  116. package/dist/parser/parseNodes.js.map +1 -1
  117. package/dist/parser/parser.d.ts +5 -1
  118. package/dist/parser/parser.js +140 -14
  119. package/dist/parser/parser.js.map +1 -1
  120. package/dist/parser/tokenizer.d.ts +2 -1
  121. package/dist/parser/tokenizer.js +7 -5
  122. package/dist/parser/tokenizer.js.map +1 -1
  123. package/dist/parser/tokenizerTypes.d.ts +5 -3
  124. package/dist/parser/tokenizerTypes.js +6 -4
  125. package/dist/parser/tokenizerTypes.js.map +1 -1
  126. package/dist/pyright.js +3 -1
  127. package/dist/pyright.js.map +1 -1
  128. package/dist/pyrightFileSystem.d.ts +1 -1
  129. package/dist/pyrightFileSystem.js +11 -1
  130. package/dist/pyrightFileSystem.js.map +1 -1
  131. package/dist/tests/chainedSourceFiles.test.js +4 -1
  132. package/dist/tests/chainedSourceFiles.test.js.map +1 -1
  133. package/dist/tests/fourslash/completions.commitChars.fourslash.d.ts +1 -0
  134. package/dist/tests/fourslash/completions.commitChars.fourslash.js +47 -0
  135. package/dist/tests/fourslash/completions.commitChars.fourslash.js.map +1 -0
  136. package/dist/tests/fourslash/completions.triggers.fourslash.d.ts +1 -0
  137. package/dist/tests/fourslash/completions.triggers.fourslash.js +29 -0
  138. package/dist/tests/fourslash/completions.triggers.fourslash.js.map +1 -0
  139. package/dist/tests/fourslash/fourslash.d.ts +1 -0
  140. package/dist/tests/fourslash/import.multipart.fourslash.d.ts +1 -0
  141. package/dist/tests/fourslash/import.multipart.fourslash.js +18 -0
  142. package/dist/tests/fourslash/import.multipart.fourslash.js.map +1 -0
  143. package/dist/tests/fourslash/signature.simple.fourslash.js +16 -0
  144. package/dist/tests/fourslash/signature.simple.fourslash.js.map +1 -1
  145. package/dist/tests/harness/fourslash/testState.js +11 -2
  146. package/dist/tests/harness/fourslash/testState.js.map +1 -1
  147. package/dist/tests/insertionPointUtils.test.d.ts +1 -0
  148. package/dist/tests/insertionPointUtils.test.js +74 -0
  149. package/dist/tests/insertionPointUtils.test.js.map +1 -0
  150. package/dist/tests/pyrightFileSystem.test.js +28 -0
  151. package/dist/tests/pyrightFileSystem.test.js.map +1 -1
  152. package/dist/tests/testUtils.d.ts +2 -1
  153. package/dist/tests/testUtils.js +10 -6
  154. package/dist/tests/testUtils.js.map +1 -1
  155. package/dist/tests/typeEvaluator1.test.js +2 -2
  156. package/dist/tests/typeEvaluator1.test.js.map +1 -1
  157. package/dist/tests/typeEvaluator2.test.js +12 -4
  158. package/dist/tests/typeEvaluator2.test.js.map +1 -1
  159. package/dist/tests/typeEvaluator3.test.js +9 -1
  160. package/dist/tests/typeEvaluator3.test.js.map +1 -1
  161. package/dist/tests/typeEvaluator4.test.js +18 -0
  162. package/dist/tests/typeEvaluator4.test.js.map +1 -1
  163. package/dist/tests/typeEvaluator5.test.d.ts +1 -0
  164. package/dist/tests/typeEvaluator5.test.js +118 -0
  165. package/dist/tests/typeEvaluator5.test.js.map +1 -0
  166. package/package.json +3 -2
@@ -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 = new Map();
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, isRecursionSentinel) {
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, assignmentFlowNode.node)) {
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
- // If there was a cache entry already, that means we hit a recursive
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(assignmentFlowNode.node)) {
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 if ((0, parseTreeUtils_1.isPartialMatchingExpression)(reference, assignmentFlowNode.node)) {
260
- // If the node partially matches the reference, we need to "kill" any narrowed
261
- // types further above this point. For example, if we see the sequence
262
- // a.b = 3
263
- // a = Foo()
264
- // x = a.b
265
- // The type of "a.b" can no longer be assumed to be Literal[3].
266
- return {
267
- type: initialType,
268
- isIncomplete: isInitialTypeIncomplete,
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
- // Was an incomplete entry added to prevent recursion?
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
- deleteCacheEntry(curFlowNode);
342
- }
343
- catch (e) {
344
- // We don't use finally here because the debugger
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
- // Before calling getTypeNarrowingCallback, set the type
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
- deleteCacheEntry(curFlowNode);
383
- }
384
- catch (e) {
385
- // We don't use finally here because the debugger
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
- // Before calling getTypeFromWildcardImport, set the cache entry to prevent infinite recursion.
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
- // Set the cache entry to undefined before evaluating the
476
- // expression in case it depends on itself.
477
- setCacheEntry(branchNode, reference ? undefined : initialType,
478
- /* isIncomplete */ true,
479
- /* isRecursionSentinel */ true);
480
- branchNode.antecedents.forEach((antecedent) => {
481
- // If we're solving for "reachability", and we have now proven
482
- // reachability, there's no reason to do more work.
483
- if (reference === undefined && isProvenReachable) {
484
- return;
485
- }
486
- const flowTypeResult = getTypeFromFlowNode(antecedent);
487
- if (flowTypeResult.isIncomplete) {
488
- sawIncomplete = true;
489
- }
490
- if (reference === undefined && flowTypeResult.type !== undefined) {
491
- isProvenReachable = true;
492
- }
493
- if (flowTypeResult.type) {
494
- typesToCombine.push(flowTypeResult.type);
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
- if (cacheEntry === undefined) {
512
- // We haven't been here before, so create a new incomplete cache entry.
513
- cacheEntry = setCacheEntry(loopNode, reference ? undefined : initialType,
514
- /* isIncomplete */ true,
515
- /* isRecursionSentinel */ true);
516
- }
517
- else if ((_a = cacheEntry.incompleteSubtypes) === null || _a === void 0 ? void 0 : _a.some((subtype) => subtype.isPending)) {
518
- // If there are pending entries that have not been evaluated even once,
519
- // treat it as incomplete.
520
- const isIncomplete = cacheEntry.incompleteSubtypes.length < loopNode.antecedents.length ||
521
- cacheEntry.incompleteSubtypes.some((subtype) => subtype.isPending && subtype.evaluationCount < maxAttemptCount);
522
- return { type: cacheEntry.type, isIncomplete };
523
- }
524
- let attemptCount = 0;
525
- while (true) {
526
- let sawIncomplete = false;
527
- let isProvenReachable = reference === undefined &&
528
- ((_b = cacheEntry.incompleteSubtypes) === null || _b === void 0 ? void 0 : _b.some((subtype) => subtype.type !== undefined));
529
- loopNode.antecedents.forEach((antecedent, index) => {
530
- var _a;
531
- // If we've trying to determine reachability and we've already proven
532
- // reachability, then we're done.
533
- if (reference === undefined && isProvenReachable) {
534
- return;
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
- catch (e) {
559
- setIncompleteSubtype(loopNode, index, undefined,
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 */ false, entryEvaluationCount + 1);
562
- throw e;
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
- if (reference === undefined && (cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.type) !== undefined) {
566
- isProvenReachable = true;
567
- }
568
- });
569
- if (isProvenReachable) {
570
- return setCacheEntry(loopNode, initialType, /* isIncomplete */ false);
571
- }
572
- let effectiveType = cacheEntry.type;
573
- if (sawIncomplete) {
574
- // If there is an incomplete "Unknown" type within a union type, remove
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
- if (!sawIncomplete || attemptCount >= maxAttemptCount) {
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
- // Before recursively calling, set the cache entry to prevent infinite recursion.
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.overloads.forEach((overload) => {
970
- if (types_1.FunctionType.isOverloaded(overload)) {
971
- overloadCount++;
972
- if (isFunctionNoReturn(overload, isCallAwaited)) {
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?
@@ -1088,7 +1082,12 @@ function getCodeFlowEngine(evaluator, speculativeTypeTracker) {
1088
1082
  const exitType = (_a = evaluator.getTypeOfObjectMember(node, cmType, exitMethodName)) === null || _a === void 0 ? void 0 : _a.type;
1089
1083
  if (exitType && (0, types_1.isFunction)(exitType) && exitType.details.declaredReturnType) {
1090
1084
  const returnType = exitType.details.declaredReturnType;
1091
- cmSwallowsExceptions = (0, types_1.isClassInstance)(returnType) && types_1.ClassType.isBuiltIn(returnType, 'bool');
1085
+ cmSwallowsExceptions = false;
1086
+ if ((0, types_1.isClassInstance)(returnType) && types_1.ClassType.isBuiltIn(returnType, 'bool')) {
1087
+ if (returnType.literalValue === undefined || returnType.literalValue === true) {
1088
+ cmSwallowsExceptions = true;
1089
+ }
1090
+ }
1092
1091
  }
1093
1092
  }
1094
1093
  }