@zzzen/pyright-internal 1.2.0-dev.20261101 → 1.2.0-dev.20261108

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 (159) hide show
  1. package/dist/analyzer/binder.js +9 -3
  2. package/dist/analyzer/binder.js.map +1 -1
  3. package/dist/analyzer/constructorTransform.js +12 -0
  4. package/dist/analyzer/constructorTransform.js.map +1 -1
  5. package/dist/analyzer/constructors.d.ts +1 -1
  6. package/dist/analyzer/constructors.js +1 -1
  7. package/dist/analyzer/constructors.js.map +1 -1
  8. package/dist/analyzer/decorators.d.ts +1 -1
  9. package/dist/analyzer/decorators.js.map +1 -1
  10. package/dist/analyzer/functionTransform.d.ts +5 -0
  11. package/dist/analyzer/functionTransform.js +6 -3
  12. package/dist/analyzer/functionTransform.js.map +1 -1
  13. package/dist/analyzer/missingBindingMetadata.d.ts +6 -0
  14. package/dist/analyzer/missingBindingMetadata.js +22 -0
  15. package/dist/analyzer/missingBindingMetadata.js.map +1 -0
  16. package/dist/analyzer/namedTuples.d.ts +4 -1
  17. package/dist/analyzer/namedTuples.js +1 -0
  18. package/dist/analyzer/namedTuples.js.map +1 -1
  19. package/dist/analyzer/patternMatching.d.ts +5 -1
  20. package/dist/analyzer/patternMatching.js +3 -0
  21. package/dist/analyzer/patternMatching.js.map +1 -1
  22. package/dist/analyzer/program.d.ts +3 -0
  23. package/dist/analyzer/program.js +71 -1
  24. package/dist/analyzer/program.js.map +1 -1
  25. package/dist/analyzer/tuples.d.ts +1 -1
  26. package/dist/analyzer/tuples.js.map +1 -1
  27. package/dist/analyzer/typeCacheUtils.d.ts +1 -0
  28. package/dist/analyzer/typeCacheUtils.js +10 -0
  29. package/dist/analyzer/typeCacheUtils.js.map +1 -1
  30. package/dist/analyzer/typeEvaluator/boundMethodSpecialization.d.ts +15 -0
  31. package/dist/analyzer/typeEvaluator/boundMethodSpecialization.js +98 -0
  32. package/dist/analyzer/typeEvaluator/boundMethodSpecialization.js.map +1 -0
  33. package/dist/analyzer/typeEvaluator/classConstruction.d.ts +25 -0
  34. package/dist/analyzer/typeEvaluator/classConstruction.js +251 -0
  35. package/dist/analyzer/typeEvaluator/classConstruction.js.map +1 -0
  36. package/dist/analyzer/typeEvaluator/collectionInference.d.ts +41 -0
  37. package/dist/analyzer/typeEvaluator/collectionInference.js +695 -0
  38. package/dist/analyzer/typeEvaluator/collectionInference.js.map +1 -0
  39. package/dist/analyzer/typeEvaluator/diagnostics.d.ts +33 -0
  40. package/dist/analyzer/typeEvaluator/diagnostics.js +198 -0
  41. package/dist/analyzer/typeEvaluator/diagnostics.js.map +1 -0
  42. package/dist/analyzer/typeEvaluator/evaluatorCore.d.ts +80 -0
  43. package/dist/analyzer/typeEvaluator/evaluatorCore.js +1126 -0
  44. package/dist/analyzer/typeEvaluator/evaluatorCore.js.map +1 -0
  45. package/dist/analyzer/typeEvaluator/evaluatorNarrowing.d.ts +17 -0
  46. package/dist/analyzer/typeEvaluator/evaluatorNarrowing.js +370 -0
  47. package/dist/analyzer/typeEvaluator/evaluatorNarrowing.js.map +1 -0
  48. package/dist/analyzer/typeEvaluator/expressionEvaluation.d.ts +81 -0
  49. package/dist/analyzer/typeEvaluator/expressionEvaluation.js +1802 -0
  50. package/dist/analyzer/typeEvaluator/expressionEvaluation.js.map +1 -0
  51. package/dist/analyzer/typeEvaluator/flowAnalysis.d.ts +33 -0
  52. package/dist/analyzer/typeEvaluator/flowAnalysis.js +217 -0
  53. package/dist/analyzer/typeEvaluator/flowAnalysis.js.map +1 -0
  54. package/dist/analyzer/typeEvaluator/internals.d.ts +137 -0
  55. package/dist/analyzer/typeEvaluator/internals.js +10 -0
  56. package/dist/analyzer/typeEvaluator/internals.js.map +1 -0
  57. package/dist/analyzer/typeEvaluator/literalTypes.d.ts +3 -0
  58. package/dist/analyzer/typeEvaluator/literalTypes.js +52 -0
  59. package/dist/analyzer/typeEvaluator/literalTypes.js.map +1 -0
  60. package/dist/analyzer/typeEvaluator/memberResolution.d.ts +79 -0
  61. package/dist/analyzer/typeEvaluator/memberResolution.js +1007 -0
  62. package/dist/analyzer/typeEvaluator/memberResolution.js.map +1 -0
  63. package/dist/analyzer/typeEvaluator/overrideValidation.d.ts +15 -0
  64. package/dist/analyzer/typeEvaluator/overrideValidation.js +422 -0
  65. package/dist/analyzer/typeEvaluator/overrideValidation.js.map +1 -0
  66. package/dist/analyzer/typeEvaluator/returnTypeInference.d.ts +33 -0
  67. package/dist/analyzer/typeEvaluator/returnTypeInference.js +226 -0
  68. package/dist/analyzer/typeEvaluator/returnTypeInference.js.map +1 -0
  69. package/dist/analyzer/typeEvaluator/specialFormCreation.d.ts +39 -0
  70. package/dist/analyzer/typeEvaluator/specialFormCreation.js +1728 -0
  71. package/dist/analyzer/typeEvaluator/specialFormCreation.js.map +1 -0
  72. package/dist/analyzer/typeEvaluator/tupleUnpacking.d.ts +2 -0
  73. package/dist/analyzer/typeEvaluator/tupleUnpacking.js +36 -0
  74. package/dist/analyzer/typeEvaluator/tupleUnpacking.js.map +1 -0
  75. package/dist/analyzer/typeEvaluator/typeCompatibility.d.ts +36 -0
  76. package/dist/analyzer/typeEvaluator/typeCompatibility.js +1320 -0
  77. package/dist/analyzer/typeEvaluator/typeCompatibility.js.map +1 -0
  78. package/dist/analyzer/typeEvaluator/typeGuardNarrowing.d.ts +186 -0
  79. package/dist/analyzer/typeEvaluator/typeGuardNarrowing.js +1683 -0
  80. package/dist/analyzer/typeEvaluator/typeGuardNarrowing.js.map +1 -0
  81. package/dist/analyzer/typeEvaluator/typeVarHandling.d.ts +48 -0
  82. package/dist/analyzer/typeEvaluator/typeVarHandling.js +756 -0
  83. package/dist/analyzer/typeEvaluator/typeVarHandling.js.map +1 -0
  84. package/dist/analyzer/typeEvaluator.d.ts +3 -31
  85. package/dist/analyzer/typeEvaluator.js +5033 -13761
  86. package/dist/analyzer/typeEvaluator.js.map +1 -1
  87. package/dist/analyzer/typeGuards.d.ts +2 -2
  88. package/dist/analyzer/typeGuards.js +212 -1567
  89. package/dist/analyzer/typeGuards.js.map +1 -1
  90. package/dist/analyzer/typeUtils.js +6 -2
  91. package/dist/analyzer/typeUtils.js.map +1 -1
  92. package/dist/analyzer/typedDicts.js +111 -0
  93. package/dist/analyzer/typedDicts.js.map +1 -1
  94. package/dist/browser/browserConnection.d.ts +12 -0
  95. package/dist/browser/browserConnection.js +23 -0
  96. package/dist/browser/browserConnection.js.map +1 -0
  97. package/dist/browser/browserFileSystem.d.ts +73 -0
  98. package/dist/browser/browserFileSystem.js +578 -0
  99. package/dist/browser/browserFileSystem.js.map +1 -0
  100. package/dist/browser/browserHost.d.ts +4 -0
  101. package/dist/browser/browserHost.js +10 -0
  102. package/dist/browser/browserHost.js.map +1 -0
  103. package/dist/browser/browserProcess.d.ts +1 -0
  104. package/dist/browser/browserProcess.js +17 -0
  105. package/dist/browser/browserProcess.js.map +1 -0
  106. package/dist/browser/browserServer.d.ts +8 -0
  107. package/dist/browser/browserServer.js +32 -0
  108. package/dist/browser/browserServer.js.map +1 -0
  109. package/dist/browser/cancellation/common.d.ts +10 -0
  110. package/dist/browser/cancellation/common.js +114 -0
  111. package/dist/browser/cancellation/common.js.map +1 -0
  112. package/dist/browser/cancellation/objectURLReceiverStrategy.d.ts +5 -0
  113. package/dist/browser/cancellation/objectURLReceiverStrategy.js +37 -0
  114. package/dist/browser/cancellation/objectURLReceiverStrategy.js.map +1 -0
  115. package/dist/browser/cancellation/objectURLSenderStrategy.d.ts +7 -0
  116. package/dist/browser/cancellation/objectURLSenderStrategy.js +32 -0
  117. package/dist/browser/cancellation/objectURLSenderStrategy.js.map +1 -0
  118. package/dist/browser/worker.d.ts +1 -0
  119. package/dist/browser/worker.js +33 -0
  120. package/dist/browser/worker.js.map +1 -0
  121. package/dist/pyrightServer.d.ts +34 -0
  122. package/dist/pyrightServer.js +239 -0
  123. package/dist/pyrightServer.js.map +1 -0
  124. package/dist/server.d.ts +3 -23
  125. package/dist/server.js +12 -232
  126. package/dist/server.js.map +1 -1
  127. package/dist/tests/benchmarks/typeInferenceComparison.d.ts +1 -0
  128. package/dist/tests/benchmarks/typeInferenceComparison.js +1235 -0
  129. package/dist/tests/benchmarks/typeInferenceComparison.js.map +1 -0
  130. package/dist/tests/benchmarks/typeInferencePyrightEntry.d.ts +1 -0
  131. package/dist/tests/benchmarks/typeInferencePyrightEntry.js +53 -0
  132. package/dist/tests/benchmarks/typeInferencePyrightEntry.js.map +1 -0
  133. package/dist/tests/browserCancellation.test.d.ts +1 -0
  134. package/dist/tests/browserCancellation.test.js +63 -0
  135. package/dist/tests/browserCancellation.test.js.map +1 -0
  136. package/dist/tests/browserServer.test.d.ts +1 -0
  137. package/dist/tests/browserServer.test.js +177 -0
  138. package/dist/tests/browserServer.test.js.map +1 -0
  139. package/dist/tests/constraintSolver.test.js +45 -0
  140. package/dist/tests/constraintSolver.test.js.map +1 -1
  141. package/dist/tests/mypyPrimerAnalysis.test.js +15 -1
  142. package/dist/tests/mypyPrimerAnalysis.test.js.map +1 -1
  143. package/dist/tests/typeCacheUtils.test.js +46 -0
  144. package/dist/tests/typeCacheUtils.test.js.map +1 -1
  145. package/dist/tests/typeEvaluator1.test.js +75 -0
  146. package/dist/tests/typeEvaluator1.test.js.map +1 -1
  147. package/dist/tests/typeEvaluator3.test.js +4 -0
  148. package/dist/tests/typeEvaluator3.test.js.map +1 -1
  149. package/dist/tests/typeEvaluator4.test.js +32 -0
  150. package/dist/tests/typeEvaluator4.test.js.map +1 -1
  151. package/dist/tests/typeEvaluator7.test.js +394 -0
  152. package/dist/tests/typeEvaluator7.test.js.map +1 -1
  153. package/dist/tests/typeUtils.test.js +28 -0
  154. package/dist/tests/typeUtils.test.js.map +1 -1
  155. package/dist/typeServer/stubGenerator.js +136 -3
  156. package/dist/typeServer/stubGenerator.js.map +1 -1
  157. package/dist/typeServer/typeServerConversionUtils.js +13 -0
  158. package/dist/typeServer/typeServerConversionUtils.js.map +1 -1
  159. package/package.json +3 -1
@@ -58,13 +58,11 @@ const parseNodes_1 = require("../parser/parseNodes");
58
58
  const analyzerNodeInfo_1 = require("./analyzerNodeInfo");
59
59
  const constraintSolver_1 = require("./constraintSolver");
60
60
  const constraintTracker_1 = require("./constraintTracker");
61
- const enums_1 = require("./enums");
62
- const operations_1 = require("./operations");
63
61
  const parameterUtils_1 = require("./parameterUtils");
64
62
  const ParseTreeUtils = __importStar(require("./parseTreeUtils"));
65
63
  const scopeUtils_1 = require("./scopeUtils");
66
64
  const symbol_1 = require("./symbol");
67
- const typedDicts_1 = require("./typedDicts");
65
+ const TypeEvaluatorNarrowing = __importStar(require("./typeEvaluator/typeGuardNarrowing"));
68
66
  const types_1 = require("./types");
69
67
  const typeUtils_1 = require("./typeUtils");
70
68
  function applyTypeNarrowingCallback(callback, type) {
@@ -84,6 +82,70 @@ function applyTypeNarrowingCallback(callback, type) {
84
82
  isIncomplete: !!result?.isIncomplete || !!baseResult?.isIncomplete,
85
83
  };
86
84
  }
85
+ function getEqualsNarrowingContext(evaluator) {
86
+ return {
87
+ getTypeOfExpression: (expression) => evaluator.getTypeOfExpression(expression),
88
+ isMatchingExpression: (reference, expression) => ParseTreeUtils.isMatchingExpression(reference, expression, (ref, expr) => isNameSameScope(evaluator, ref, expr)),
89
+ isMatchingExpressionOrWalrusRhs: (reference, expression) => isMatchingExpressionOrWalrusRhs(evaluator, reference, expression),
90
+ };
91
+ }
92
+ function getMemberAccessNarrowingContext(evaluator) {
93
+ return {
94
+ getTypeOfExpression: (expression) => evaluator.getTypeOfExpression(expression),
95
+ isMatchingExpression: (reference, expression) => ParseTreeUtils.isMatchingExpression(reference, expression, (ref, expr) => isNameSameScope(evaluator, ref, expr)),
96
+ };
97
+ }
98
+ function getLenComparisonNarrowingContext(evaluator) {
99
+ return {
100
+ getTypeOfExpression: (expression, flags) => evaluator.getTypeOfExpression(expression, flags),
101
+ isMatchingExpression: (reference, expression) => ParseTreeUtils.isMatchingExpression(reference, expression, (ref, expr) => isNameSameScope(evaluator, ref, expr)),
102
+ };
103
+ }
104
+ function getInOperatorNarrowingContext(evaluator) {
105
+ return {
106
+ getTypeOfExpression: (expression, flags) => evaluator.getTypeOfExpression(expression, flags),
107
+ isMatchingExpression: (reference, expression) => ParseTreeUtils.isMatchingExpression(reference, expression, (ref, expr) => isNameSameScope(evaluator, ref, expr)),
108
+ isMatchingExpressionOrWalrusRhs: (reference, expression) => isMatchingExpressionOrWalrusRhs(evaluator, reference, expression),
109
+ };
110
+ }
111
+ function getNoneEllipsisNarrowingContext(evaluator) {
112
+ return {
113
+ isMatchingExpression: (reference, expression) => ParseTreeUtils.isMatchingExpression(reference, expression, (ref, expr) => isNameSameScope(evaluator, ref, expr)),
114
+ isMatchingExpressionOrWalrusRhs: (reference, expression) => isMatchingExpressionOrWalrusRhs(evaluator, reference, expression),
115
+ };
116
+ }
117
+ function getTypeIsCallNarrowingContext(evaluator) {
118
+ return {
119
+ getTypeOfExpression: (expression, flags) => evaluator.getTypeOfExpression(expression, flags),
120
+ isMatchingExpressionOrWalrusRhs: (reference, expression) => isMatchingExpressionOrWalrusRhs(evaluator, reference, expression),
121
+ mapSubtypesExpandTypeVars: (type, callback) => evaluator.mapSubtypesExpandTypeVars(type, /* options */ undefined, callback),
122
+ };
123
+ }
124
+ function getIsLiteralOrClassNarrowingContext(evaluator) {
125
+ return {
126
+ getTypeOfExpression: (expression) => evaluator.getTypeOfExpression(expression),
127
+ isMatchingExpressionOrWalrusRhs: (reference, expression) => isMatchingExpressionOrWalrusRhs(evaluator, reference, expression),
128
+ };
129
+ }
130
+ function getIndexedLiteralNarrowingContext(evaluator) {
131
+ return {
132
+ getTypeOfExpression: (expression) => evaluator.getTypeOfExpression(expression),
133
+ isMatchingExpression: (reference, expression) => ParseTreeUtils.isMatchingExpression(reference, expression, (ref, expr) => isNameSameScope(evaluator, ref, expr)),
134
+ };
135
+ }
136
+ function getIsInstanceCallNarrowingContext(evaluator) {
137
+ return {
138
+ getTypeOfExpression: (expression, flags) => evaluator.getTypeOfExpression(expression, flags),
139
+ getIsInstanceClassTypes: (argType, argExpr) => getIsInstanceClassTypes(evaluator, argType, argExpr),
140
+ isMatchingExpressionOrWalrusRhs: (reference, expression) => isMatchingExpressionOrWalrusRhs(evaluator, reference, expression),
141
+ };
142
+ }
143
+ function getBoolCallNarrowingContext(evaluator) {
144
+ return {
145
+ getTypeOfExpression: (expression, flags) => evaluator.getTypeOfExpression(expression, flags),
146
+ isMatchingExpressionOrWalrusRhs: (reference, expression) => isMatchingExpressionOrWalrusRhs(evaluator, reference, expression),
147
+ };
148
+ }
87
149
  // Given a reference expression and a test expression, returns a callback that
88
150
  // can be used to narrow the type described by the reference expression.
89
151
  // If the specified flow node is not associated with the test expression,
@@ -99,434 +161,185 @@ function getTypeNarrowingCallback(evaluator, reference, testExpression, isPositi
99
161
  if (testExpression.nodeType === 7 /* ParseNodeType.BinaryOperation */) {
100
162
  const isOrIsNotOperator = testExpression.d.operator === 39 /* OperatorType.Is */ || testExpression.d.operator === 40 /* OperatorType.IsNot */;
101
163
  const equalsOrNotEqualsOperator = testExpression.d.operator === 12 /* OperatorType.Equals */ || testExpression.d.operator === 28 /* OperatorType.NotEquals */;
102
- const comparisonOperator = equalsOrNotEqualsOperator ||
103
- testExpression.d.operator === 20 /* OperatorType.LessThan */ ||
104
- testExpression.d.operator === 21 /* OperatorType.LessThanOrEqual */ ||
105
- testExpression.d.operator === 15 /* OperatorType.GreaterThan */ ||
106
- testExpression.d.operator === 16 /* OperatorType.GreaterThanOrEqual */;
107
164
  if (isOrIsNotOperator || equalsOrNotEqualsOperator) {
108
- // Invert the "isPositiveTest" value if this is an "is not" operation.
109
- const adjIsPositiveTest = testExpression.d.operator === 39 /* OperatorType.Is */ || testExpression.d.operator === 12 /* OperatorType.Equals */
110
- ? isPositiveTest
111
- : !isPositiveTest;
112
- // Look for "X is None", "X is not None", "X == None", and "X != None".
113
- // These are commonly-used patterns used in control flow.
114
- if (testExpression.d.rightExpr.nodeType === 14 /* ParseNodeType.Constant */ &&
115
- testExpression.d.rightExpr.d.constType === 27 /* KeywordType.None */) {
116
- // Allow the LHS to be a simple expression or an assignment expression.
117
- // For assignment expressions, narrow both the target and the RHS
118
- // (consistent with truthiness narrowing).
119
- if (isMatchingExpressionOrWalrusRhs(evaluator, reference, testExpression.d.leftExpr)) {
120
- return (type) => {
121
- return { type: narrowTypeForIsNone(evaluator, type, adjIsPositiveTest), isIncomplete: false };
122
- };
123
- }
124
- const leftExpression = testExpression.d.leftExpr;
125
- if (leftExpression.nodeType === 27 /* ParseNodeType.Index */ &&
126
- ParseTreeUtils.isMatchingExpression(reference, leftExpression.d.leftExpr, (ref, expr) => isNameSameScope(evaluator, ref, expr)) &&
127
- leftExpression.d.items.length === 1 &&
128
- !leftExpression.d.trailingComma &&
129
- leftExpression.d.items[0].d.argCategory === 0 /* ArgCategory.Simple */ &&
130
- !leftExpression.d.items[0].d.name &&
131
- leftExpression.d.items[0].d.valueExpr.nodeType === 40 /* ParseNodeType.Number */ &&
132
- leftExpression.d.items[0].d.valueExpr.d.isInteger &&
133
- !leftExpression.d.items[0].d.valueExpr.d.isImaginary) {
134
- const indexValue = leftExpression.d.items[0].d.valueExpr.d.value;
135
- if (typeof indexValue === 'number') {
136
- return (type) => {
137
- return {
138
- type: narrowTupleTypeForIsNone(evaluator, type, adjIsPositiveTest, indexValue),
139
- isIncomplete: false,
140
- };
141
- };
142
- }
143
- }
144
- }
145
- // Look for "X is ...", "X is not ...", "X == ...", and "X != ...".
146
- if (testExpression.d.rightExpr.nodeType === 21 /* ParseNodeType.Ellipsis */) {
147
- if (isMatchingExpressionOrWalrusRhs(evaluator, reference, testExpression.d.leftExpr)) {
165
+ const noneEllipsisInfo = TypeEvaluatorNarrowing.getNoneEllipsisNarrowingInfo(getNoneEllipsisNarrowingContext(evaluator), reference, testExpression, isPositiveTest);
166
+ if (noneEllipsisInfo) {
167
+ if (noneEllipsisInfo.kind === 'none') {
148
168
  return (type) => {
149
169
  return {
150
- type: narrowTypeForIsEllipsis(evaluator, testExpression, type, adjIsPositiveTest),
170
+ type: narrowTypeForIsNone(evaluator, type, noneEllipsisInfo.adjIsPositiveTest),
151
171
  isIncomplete: false,
152
172
  };
153
173
  };
154
174
  }
155
- }
156
- // Look for "type(X) is Y", "type(X) is not Y", "type(X) == Y" or "type(X) != Y",
157
- // as well as reverse forms "Y is type(X)", "Y is not type(X)", "Y == type(X)", "Y != type(X)".
158
- let typeCallNode;
159
- let otherExpr;
160
- const isTypeCall = (expr, referenceExpr) => expr.nodeType === 9 /* ParseNodeType.Call */ &&
161
- expr.d.args.length === 1 &&
162
- expr.d.args[0].d.argCategory === 0 /* ArgCategory.Simple */ &&
163
- (!referenceExpr ||
164
- isMatchingExpressionOrWalrusRhs(evaluator, referenceExpr, expr.d.args[0].d.valueExpr)) &&
165
- isTypeCallBase(evaluator.getTypeOfExpression(expr.d.leftExpr, 2 /* EvalFlags.CallBaseDefaults */).type);
166
- // Either operand may be the call; pick the one whose argument matches the reference.
167
- if (isTypeCall(testExpression.d.leftExpr, reference)) {
168
- typeCallNode = testExpression.d.leftExpr;
169
- otherExpr = testExpression.d.rightExpr;
170
- }
171
- else if (isTypeCall(testExpression.d.rightExpr, reference)) {
172
- typeCallNode = testExpression.d.rightExpr;
173
- otherExpr = testExpression.d.leftExpr;
174
- }
175
- if (typeCallNode && otherExpr) {
176
- const otherResult = evaluator.getTypeOfExpression(otherExpr);
177
- const classTypes = [];
178
- let isClassType = true;
179
- let classTypeVar;
180
- if ((0, types_1.isTypeVar)(otherResult.type) && types_1.TypeBase.isInstantiable(otherResult.type)) {
181
- classTypeVar = (0, typeUtils_1.convertToInstance)(otherResult.type);
182
- }
183
- else if ((0, types_1.isClassInstance)(otherResult.type) &&
184
- types_1.ClassType.isBuiltIn(otherResult.type, 'type') &&
185
- otherResult.type.priv.typeArgs?.length === 1 &&
186
- (0, types_1.isTypeVar)(otherResult.type.priv.typeArgs[0])) {
187
- classTypeVar = otherResult.type.priv.typeArgs[0];
188
- }
189
- evaluator.mapSubtypesExpandTypeVars(otherResult.type, /* options */ undefined, (expandedSubtype) => {
190
- let instantiable;
191
- if ((0, types_1.isClass)(expandedSubtype)) {
192
- if ((0, types_1.isClassInstance)(expandedSubtype) &&
193
- types_1.ClassType.isBuiltIn(expandedSubtype, 'type') &&
194
- expandedSubtype.priv.typeArgs &&
195
- expandedSubtype.priv.typeArgs.length > 0) {
196
- const extracted = (0, typeUtils_1.convertToInstantiable)(expandedSubtype.priv.typeArgs[0],
197
- /* includeSubclasses */ true);
198
- if ((0, types_1.isInstantiableClass)(extracted)) {
199
- instantiable = extracted;
200
- }
201
- }
202
- else if ((0, types_1.isInstantiableClass)(expandedSubtype)) {
203
- instantiable = expandedSubtype;
204
- }
205
- }
206
- if (instantiable) {
207
- classTypes.push(instantiable);
208
- }
209
- else {
210
- isClassType = false;
211
- }
212
- return undefined;
213
- });
214
- if (isClassType && classTypes.length > 0) {
215
- const allowLegacyEquality = !isOrIsNotOperator &&
216
- typeCallNode === testExpression.d.leftExpr &&
217
- (((0, types_1.isInstantiableClass)(otherResult.type) &&
218
- !otherResult.type.priv.includeSubclasses &&
219
- !otherResult.type.priv.isTypeArgExplicit) ||
220
- isTypeCall(otherExpr));
175
+ if (noneEllipsisInfo.kind === 'tuple-none') {
221
176
  return (type) => {
222
177
  return {
223
- type: narrowTypeForTypeIs(evaluator, type, classTypes, adjIsPositiveTest, isOrIsNotOperator, classTypeVar, allowLegacyEquality),
224
- isIncomplete: !!otherResult.isIncomplete,
178
+ type: narrowTupleTypeForIsNone(evaluator, type, noneEllipsisInfo.adjIsPositiveTest, noneEllipsisInfo.tupleIndex),
179
+ isIncomplete: false,
225
180
  };
226
181
  };
227
182
  }
183
+ return (type) => {
184
+ return {
185
+ type: narrowTypeForIsEllipsis(evaluator, testExpression, type, noneEllipsisInfo.adjIsPositiveTest),
186
+ isIncomplete: false,
187
+ };
188
+ };
189
+ }
190
+ const typeIsCallInfo = TypeEvaluatorNarrowing.getTypeIsCallNarrowingInfo(getTypeIsCallNarrowingContext(evaluator), reference, testExpression, isPositiveTest);
191
+ if (typeIsCallInfo) {
192
+ return (type) => {
193
+ return {
194
+ type: narrowTypeForTypeIs(evaluator, type, typeIsCallInfo.classTypes, typeIsCallInfo.adjIsPositiveTest, typeIsCallInfo.isOrIsNotOperator, typeIsCallInfo.classTypeVar, typeIsCallInfo.allowLegacyEquality),
195
+ isIncomplete: typeIsCallInfo.isIncomplete,
196
+ };
197
+ };
228
198
  }
229
199
  if (isOrIsNotOperator) {
230
- if (isMatchingExpressionOrWalrusRhs(evaluator, reference, testExpression.d.leftExpr)) {
231
- const rightTypeResult = evaluator.getTypeOfExpression(testExpression.d.rightExpr);
232
- const rightType = rightTypeResult.type;
233
- // Look for "X is Y" or "X is not Y" where Y is a literal.
234
- if ((0, types_1.isClassInstance)(rightType) && rightType.priv.literalValue !== undefined) {
200
+ const isLiteralOrClassInfo = TypeEvaluatorNarrowing.getIsLiteralOrClassNarrowingInfo(getIsLiteralOrClassNarrowingContext(evaluator), reference, testExpression, isPositiveTest);
201
+ if (isLiteralOrClassInfo) {
202
+ if (isLiteralOrClassInfo.kind === 'literal') {
235
203
  return (type) => {
236
204
  return {
237
- type: narrowTypeForLiteralComparison(evaluator, type, rightType, adjIsPositiveTest,
205
+ type: narrowTypeForLiteralComparison(evaluator, type, isLiteralOrClassInfo.rightType, isLiteralOrClassInfo.adjIsPositiveTest,
238
206
  /* isIsOperator */ true),
239
- isIncomplete: !!rightTypeResult.isIncomplete,
207
+ isIncomplete: isLiteralOrClassInfo.isIncomplete,
240
208
  };
241
209
  };
242
210
  }
243
- // Look for X is <class> or X is not <class>.
244
- if ((0, types_1.isInstantiableClass)(rightType)) {
245
- return (type) => {
246
- return {
247
- type: narrowTypeForClassComparison(evaluator, type, rightType, adjIsPositiveTest),
248
- isIncomplete: !!rightTypeResult.isIncomplete,
249
- };
211
+ return (type) => {
212
+ return {
213
+ type: narrowTypeForClassComparison(evaluator, type, isLiteralOrClassInfo.rightType, isLiteralOrClassInfo.adjIsPositiveTest),
214
+ isIncomplete: isLiteralOrClassInfo.isIncomplete,
250
215
  };
251
- }
252
- }
253
- // Look for X[<literal>] is <literal> or X[<literal>] is not <literal>.
254
- if (testExpression.d.leftExpr.nodeType === 27 /* ParseNodeType.Index */ &&
255
- testExpression.d.leftExpr.d.items.length === 1 &&
256
- !testExpression.d.leftExpr.d.trailingComma &&
257
- testExpression.d.leftExpr.d.items[0].d.argCategory === 0 /* ArgCategory.Simple */ &&
258
- ParseTreeUtils.isMatchingExpression(reference, testExpression.d.leftExpr.d.leftExpr, (ref, expr) => isNameSameScope(evaluator, ref, expr))) {
259
- const indexTypeResult = evaluator.getTypeOfExpression(testExpression.d.leftExpr.d.items[0].d.valueExpr);
260
- const indexType = indexTypeResult.type;
261
- if ((0, types_1.isClassInstance)(indexType) && (0, typeUtils_1.isLiteralType)(indexType)) {
262
- if (types_1.ClassType.isBuiltIn(indexType, 'str')) {
263
- const rightType = evaluator.getTypeOfExpression(testExpression.d.rightExpr).type;
264
- if ((0, types_1.isClassInstance)(rightType) && rightType.priv.literalValue !== undefined) {
265
- return (type) => {
266
- return {
267
- type: narrowTypeForDiscriminatedDictEntryComparison(evaluator, type, indexType, rightType, adjIsPositiveTest),
268
- isIncomplete: !!indexTypeResult.isIncomplete,
269
- };
270
- };
271
- }
272
- }
273
- else if (types_1.ClassType.isBuiltIn(indexType, 'int')) {
274
- const rightTypeResult = evaluator.getTypeOfExpression(testExpression.d.rightExpr);
275
- const rightType = rightTypeResult.type;
276
- if ((0, types_1.isClassInstance)(rightType) && rightType.priv.literalValue !== undefined) {
277
- let canNarrow = false;
278
- // Narrowing can be applied only for bool or enum literals.
279
- if (types_1.ClassType.isBuiltIn(rightType, 'bool')) {
280
- canNarrow = true;
281
- }
282
- else if (rightType.priv.literalValue instanceof types_1.EnumLiteral) {
283
- canNarrow = true;
284
- }
285
- if (canNarrow) {
286
- return (type) => {
287
- return {
288
- type: narrowTypeForDiscriminatedTupleComparison(evaluator, type, indexType, rightType, adjIsPositiveTest),
289
- isIncomplete: !!rightTypeResult.isIncomplete,
290
- };
291
- };
292
- }
293
- }
294
- }
295
- }
216
+ };
296
217
  }
297
- }
298
- if (equalsOrNotEqualsOperator) {
299
- // Look for X == <literal> or X != <literal>
300
- const adjIsPositiveTest = testExpression.d.operator === 12 /* OperatorType.Equals */ ? isPositiveTest : !isPositiveTest;
301
- if (isMatchingExpressionOrWalrusRhs(evaluator, reference, testExpression.d.leftExpr)) {
302
- const rightTypeResult = evaluator.getTypeOfExpression(testExpression.d.rightExpr);
303
- const rightType = rightTypeResult.type;
304
- if ((0, types_1.isClassInstance)(rightType) && rightType.priv.literalValue !== undefined) {
218
+ const indexedLiteralInfo = TypeEvaluatorNarrowing.getIndexedLiteralNarrowingInfo(getIndexedLiteralNarrowingContext(evaluator), reference, testExpression, isPositiveTest);
219
+ if (indexedLiteralInfo) {
220
+ if (indexedLiteralInfo.kind === 'dict') {
305
221
  return (type) => {
306
222
  return {
307
- type: narrowTypeForLiteralComparison(evaluator, type, rightType, adjIsPositiveTest,
308
- /* isIsOperator */ false),
309
- isIncomplete: !!rightTypeResult.isIncomplete,
223
+ type: narrowTypeForDiscriminatedDictEntryComparison(evaluator, type, indexedLiteralInfo.indexType, indexedLiteralInfo.rightType, indexedLiteralInfo.adjIsPositiveTest),
224
+ isIncomplete: indexedLiteralInfo.isIncomplete,
310
225
  };
311
226
  };
312
227
  }
313
- // Look for X == <class> or X != <class>.
314
- if ((0, types_1.isInstantiableClass)(rightType)) {
315
- return (type) => {
316
- return {
317
- type: narrowTypeForClassComparison(evaluator, type, rightType, adjIsPositiveTest,
318
- /* isIsOperator */ false),
319
- isIncomplete: !!rightTypeResult.isIncomplete,
320
- };
228
+ return (type) => {
229
+ return {
230
+ type: narrowTypeForDiscriminatedTupleComparison(evaluator, type, indexedLiteralInfo.indexType, indexedLiteralInfo.rightType, indexedLiteralInfo.adjIsPositiveTest),
231
+ isIncomplete: indexedLiteralInfo.isIncomplete,
321
232
  };
322
- }
323
- }
324
- // Look for X[<literal>] == <literal> or X[<literal>] != <literal>
325
- if (testExpression.d.leftExpr.nodeType === 27 /* ParseNodeType.Index */ &&
326
- testExpression.d.leftExpr.d.items.length === 1 &&
327
- !testExpression.d.leftExpr.d.trailingComma &&
328
- testExpression.d.leftExpr.d.items[0].d.argCategory === 0 /* ArgCategory.Simple */ &&
329
- ParseTreeUtils.isMatchingExpression(reference, testExpression.d.leftExpr.d.leftExpr, (ref, expr) => isNameSameScope(evaluator, ref, expr))) {
330
- const indexTypeResult = evaluator.getTypeOfExpression(testExpression.d.leftExpr.d.items[0].d.valueExpr);
331
- const indexType = indexTypeResult.type;
332
- if ((0, types_1.isClassInstance)(indexType) && (0, typeUtils_1.isLiteralType)(indexType)) {
333
- if (types_1.ClassType.isBuiltIn(indexType, ['str', 'int'])) {
334
- const rightTypeResult = evaluator.getTypeOfExpression(testExpression.d.rightExpr);
335
- const rightType = rightTypeResult.type;
336
- if ((0, typeUtils_1.isLiteralTypeOrUnion)(rightType)) {
337
- return (type) => {
338
- let narrowedType;
339
- if (types_1.ClassType.isBuiltIn(indexType, 'str')) {
340
- narrowedType = narrowTypeForDiscriminatedDictEntryComparison(evaluator, type, indexType, rightType, adjIsPositiveTest);
341
- }
342
- else {
343
- narrowedType = narrowTypeForDiscriminatedTupleComparison(evaluator, type, indexType, rightType, adjIsPositiveTest);
344
- }
345
- return {
346
- type: narrowedType,
347
- isIncomplete: !!indexTypeResult.isIncomplete || !!rightTypeResult.isIncomplete,
348
- };
349
- };
350
- }
351
- }
352
- }
233
+ };
353
234
  }
354
235
  }
355
- // Look for X.Y == <literal> or X.Y != <literal>
356
- if (equalsOrNotEqualsOperator &&
357
- testExpression.d.leftExpr.nodeType === 35 /* ParseNodeType.MemberAccess */ &&
358
- ParseTreeUtils.isMatchingExpression(reference, testExpression.d.leftExpr.d.leftExpr, (ref, expr) => isNameSameScope(evaluator, ref, expr))) {
359
- const rightTypeResult = evaluator.getTypeOfExpression(testExpression.d.rightExpr);
360
- const rightType = rightTypeResult.type;
361
- const memberName = testExpression.d.leftExpr.d.member;
362
- if ((0, types_1.isClassInstance)(rightType)) {
363
- if (rightType.priv.literalValue !== undefined || (0, typeUtils_1.isNoneInstance)(rightType)) {
364
- return (type) => {
365
- return {
366
- type: narrowTypeForDiscriminatedLiteralFieldComparison(evaluator, type, memberName.d.value, rightType, adjIsPositiveTest),
367
- isIncomplete: !!rightTypeResult.isIncomplete,
368
- };
236
+ const equalsInfo = TypeEvaluatorNarrowing.getEqualsNarrowingInfo(getEqualsNarrowingContext(evaluator), reference, testExpression, isPositiveTest);
237
+ if (equalsInfo) {
238
+ if (equalsInfo.kind === 'literal') {
239
+ return (type) => {
240
+ return {
241
+ type: narrowTypeForLiteralComparison(evaluator, type, equalsInfo.rightType, equalsInfo.adjIsPositiveTest,
242
+ /* isIsOperator */ false),
243
+ isIncomplete: equalsInfo.isIncomplete,
369
244
  };
370
- }
245
+ };
371
246
  }
372
- }
373
- // Look for X.Y is <literal> or X.Y is not <literal> where <literal> is
374
- // an enum or bool literal
375
- if (testExpression.d.leftExpr.nodeType === 35 /* ParseNodeType.MemberAccess */ &&
376
- ParseTreeUtils.isMatchingExpression(reference, testExpression.d.leftExpr.d.leftExpr, (ref, expr) => isNameSameScope(evaluator, ref, expr))) {
377
- const rightTypeResult = evaluator.getTypeOfExpression(testExpression.d.rightExpr);
378
- const rightType = rightTypeResult.type;
379
- const memberName = testExpression.d.leftExpr.d.member;
380
- if ((0, types_1.isClassInstance)(rightType) &&
381
- (types_1.ClassType.isEnumClass(rightType) || types_1.ClassType.isBuiltIn(rightType, 'bool')) &&
382
- rightType.priv.literalValue !== undefined) {
247
+ if (equalsInfo.kind === 'class') {
383
248
  return (type) => {
384
249
  return {
385
- type: narrowTypeForDiscriminatedLiteralFieldComparison(evaluator, type, memberName.d.value, rightType, adjIsPositiveTest),
386
- isIncomplete: !!rightTypeResult.isIncomplete,
250
+ type: narrowTypeForClassComparison(evaluator, type, equalsInfo.rightType, equalsInfo.adjIsPositiveTest,
251
+ /* isIsOperator */ false),
252
+ isIncomplete: equalsInfo.isIncomplete,
387
253
  };
388
254
  };
389
255
  }
390
- }
391
- // Look for X.Y is None or X.Y is not None
392
- // These are commonly-used patterns used in control flow.
393
- if (testExpression.d.leftExpr.nodeType === 35 /* ParseNodeType.MemberAccess */ &&
394
- ParseTreeUtils.isMatchingExpression(reference, testExpression.d.leftExpr.d.leftExpr, (ref, expr) => isNameSameScope(evaluator, ref, expr)) &&
395
- testExpression.d.rightExpr.nodeType === 14 /* ParseNodeType.Constant */ &&
396
- testExpression.d.rightExpr.d.constType === 27 /* KeywordType.None */) {
397
- const memberName = testExpression.d.leftExpr.d.member;
398
256
  return (type) => {
257
+ let narrowedType;
258
+ const indexType = equalsInfo.indexType;
259
+ if (types_1.ClassType.isBuiltIn(indexType, 'str')) {
260
+ narrowedType = narrowTypeForDiscriminatedDictEntryComparison(evaluator, type, indexType, equalsInfo.rightType, equalsInfo.adjIsPositiveTest);
261
+ }
262
+ else {
263
+ narrowedType = narrowTypeForDiscriminatedTupleComparison(evaluator, type, indexType, equalsInfo.rightType, equalsInfo.adjIsPositiveTest);
264
+ }
399
265
  return {
400
- type: narrowTypeForDiscriminatedFieldNoneComparison(evaluator, type, memberName.d.value, adjIsPositiveTest),
401
- isIncomplete: false,
266
+ type: narrowedType,
267
+ isIncomplete: equalsInfo.isIncomplete,
402
268
  };
403
269
  };
404
270
  }
405
- }
406
- // Look for len(x) == <literal>, len(x) != <literal>, len(x) < <literal>, etc.
407
- if (comparisonOperator &&
408
- testExpression.d.leftExpr.nodeType === 9 /* ParseNodeType.Call */ &&
409
- testExpression.d.leftExpr.d.args.length === 1) {
410
- const arg0Expr = testExpression.d.leftExpr.d.args[0].d.valueExpr;
411
- if (ParseTreeUtils.isMatchingExpression(reference, arg0Expr, (ref, expr) => isNameSameScope(evaluator, ref, expr))) {
412
- const callTypeResult = evaluator.getTypeOfExpression(testExpression.d.leftExpr.d.leftExpr, 2 /* EvalFlags.CallBaseDefaults */);
413
- const callType = callTypeResult.type;
414
- if ((0, types_1.isFunction)(callType) && callType.shared.fullName === 'builtins.len') {
415
- const rightTypeResult = evaluator.getTypeOfExpression(testExpression.d.rightExpr);
416
- const rightType = rightTypeResult.type;
417
- if ((0, types_1.isClassInstance)(rightType) &&
418
- typeof rightType.priv.literalValue === 'number' &&
419
- rightType.priv.literalValue >= 0) {
420
- let tupleLength = rightType.priv.literalValue;
421
- // We'll treat <, <= and == as positive tests with >=, > and != as
422
- // their negative counterparts.
423
- const isLessOrEqual = testExpression.d.operator === 12 /* OperatorType.Equals */ ||
424
- testExpression.d.operator === 20 /* OperatorType.LessThan */ ||
425
- testExpression.d.operator === 21 /* OperatorType.LessThanOrEqual */;
426
- const adjIsPositiveTest = isLessOrEqual ? isPositiveTest : !isPositiveTest;
427
- // For <= (or its negative counterpart >), adjust the tuple length by 1.
428
- if (testExpression.d.operator === 21 /* OperatorType.LessThanOrEqual */ ||
429
- testExpression.d.operator === 15 /* OperatorType.GreaterThan */) {
430
- tupleLength++;
431
- }
432
- const isEqualityCheck = testExpression.d.operator === 12 /* OperatorType.Equals */ ||
433
- testExpression.d.operator === 28 /* OperatorType.NotEquals */;
434
- return (type) => {
435
- return {
436
- type: narrowTypeForTupleLength(evaluator, type, tupleLength, adjIsPositiveTest, !isEqualityCheck),
437
- isIncomplete: !!callTypeResult.isIncomplete || !!rightTypeResult.isIncomplete,
438
- };
271
+ const memberAccessInfo = TypeEvaluatorNarrowing.getMemberAccessNarrowingInfo(getMemberAccessNarrowingContext(evaluator), reference, testExpression, isPositiveTest);
272
+ if (memberAccessInfo) {
273
+ if (memberAccessInfo.kind === 'none') {
274
+ return (type) => {
275
+ return {
276
+ type: narrowTypeForDiscriminatedFieldNoneComparison(evaluator, type, memberAccessInfo.memberName, memberAccessInfo.adjIsPositiveTest),
277
+ isIncomplete: false,
439
278
  };
440
- }
279
+ };
441
280
  }
442
- }
443
- }
444
- if (testExpression.d.operator === 41 /* OperatorType.In */ || testExpression.d.operator === 42 /* OperatorType.NotIn */) {
445
- // Look for "x in y" or "x not in y" where y is one of several built-in types.
446
- if (isMatchingExpressionOrWalrusRhs(evaluator, reference, testExpression.d.leftExpr)) {
447
- const rightTypeResult = evaluator.getTypeOfExpression(testExpression.d.rightExpr);
448
- const rightType = rightTypeResult.type;
449
- const adjIsPositiveTest = testExpression.d.operator === 41 /* OperatorType.In */ ? isPositiveTest : !isPositiveTest;
450
281
  return (type) => {
451
282
  return {
452
- type: narrowTypeForContainerType(evaluator, type, rightType, adjIsPositiveTest),
453
- isIncomplete: !!rightTypeResult.isIncomplete,
283
+ type: narrowTypeForDiscriminatedLiteralFieldComparison(evaluator, type, memberAccessInfo.memberName, memberAccessInfo.rightType, memberAccessInfo.adjIsPositiveTest),
284
+ isIncomplete: memberAccessInfo.isIncomplete,
454
285
  };
455
286
  };
456
287
  }
457
- if (ParseTreeUtils.isMatchingExpression(reference, testExpression.d.rightExpr, (ref, expr) => isNameSameScope(evaluator, ref, expr))) {
458
- // Look for <string literal> in y where y is a union that contains
459
- // one or more TypedDicts.
460
- const leftTypeResult = evaluator.getTypeOfExpression(testExpression.d.leftExpr);
461
- const leftType = leftTypeResult.type;
462
- if ((0, types_1.isClassInstance)(leftType) && types_1.ClassType.isBuiltIn(leftType, 'str') && (0, typeUtils_1.isLiteralType)(leftType)) {
463
- const adjIsPositiveTest = testExpression.d.operator === 41 /* OperatorType.In */ ? isPositiveTest : !isPositiveTest;
464
- return (type) => {
465
- return {
466
- type: narrowTypeForTypedDictKey(evaluator, type, types_1.ClassType.cloneAsInstantiable(leftType), adjIsPositiveTest),
467
- isIncomplete: !!leftTypeResult.isIncomplete,
468
- };
288
+ }
289
+ const lenComparisonInfo = TypeEvaluatorNarrowing.getLenComparisonNarrowingInfo(getLenComparisonNarrowingContext(evaluator), reference, testExpression, isPositiveTest);
290
+ if (lenComparisonInfo) {
291
+ return (type) => {
292
+ return {
293
+ type: narrowTypeForTupleLength(evaluator, type, lenComparisonInfo.tupleLength, lenComparisonInfo.adjIsPositiveTest, lenComparisonInfo.isLessThanCheck),
294
+ isIncomplete: lenComparisonInfo.isIncomplete,
295
+ };
296
+ };
297
+ }
298
+ const inOperatorInfo = TypeEvaluatorNarrowing.getInOperatorNarrowingInfo(getInOperatorNarrowingContext(evaluator), reference, testExpression, isPositiveTest);
299
+ if (inOperatorInfo) {
300
+ if (inOperatorInfo.kind === 'container') {
301
+ return (type) => {
302
+ return {
303
+ type: narrowTypeForContainerType(evaluator, type, inOperatorInfo.containerType, inOperatorInfo.adjIsPositiveTest),
304
+ isIncomplete: inOperatorInfo.isIncomplete,
469
305
  };
470
- }
306
+ };
471
307
  }
308
+ return (type) => {
309
+ return {
310
+ type: narrowTypeForTypedDictKey(evaluator, type, inOperatorInfo.typedDictKeyType, inOperatorInfo.adjIsPositiveTest),
311
+ isIncomplete: inOperatorInfo.isIncomplete,
312
+ };
313
+ };
472
314
  }
473
315
  }
474
316
  if (testExpression.nodeType === 9 /* ParseNodeType.Call */) {
475
- // Look for "isinstance(X, Y)" or "issubclass(X, Y)".
476
- if (testExpression.d.args.length === 2) {
477
- // Make sure the first parameter is a supported expression type
478
- // and the second parameter is a valid class type or a tuple
479
- // of valid class types.
480
- const arg0Expr = testExpression.d.args[0].d.valueExpr;
481
- const arg1Expr = testExpression.d.args[1].d.valueExpr;
482
- if (isMatchingExpressionOrWalrusRhs(evaluator, reference, arg0Expr)) {
483
- const callTypeResult = evaluator.getTypeOfExpression(testExpression.d.leftExpr, 2 /* EvalFlags.CallBaseDefaults */);
484
- const callType = callTypeResult.type;
485
- if ((0, types_1.isFunction)(callType) && types_1.FunctionType.isBuiltIn(callType, ['isinstance', 'issubclass'])) {
486
- const isInstanceCheck = types_1.FunctionType.isBuiltIn(callType, 'isinstance');
487
- const arg1TypeResult = evaluator.getTypeOfExpression(arg1Expr, 536871546 /* EvalFlags.IsInstanceArgDefaults */);
488
- const arg1Type = arg1TypeResult.type;
489
- const classTypeInfo = getIsInstanceClassTypes(evaluator, arg1Type, arg1Expr);
490
- const isIncomplete = !!callTypeResult.isIncomplete || !!arg1TypeResult.isIncomplete;
491
- if (classTypeInfo) {
492
- const classTypeList = isPositiveTest
493
- ? classTypeInfo.classTypes
494
- : classTypeInfo.negativeClassTypes;
495
- return (type) => {
496
- return {
497
- type: narrowTypeForInstanceOrSubclass(evaluator, type, classTypeList, isInstanceCheck,
498
- /* isTypeIsCheck */ false, isPositiveTest, testExpression, nodeInfo),
499
- isIncomplete,
500
- };
501
- };
502
- }
503
- else if (isIncomplete) {
504
- // If the type is incomplete, it may include unknowns, which will result
505
- // in classTypeList being undefined.
506
- return (type) => {
507
- return {
508
- type,
509
- isIncomplete: true,
510
- };
511
- };
512
- }
513
- }
514
- }
515
- }
516
- // Look for "bool(X)"
517
- if (testExpression.d.args.length === 1 && !testExpression.d.args[0].d.name) {
518
- if (isMatchingExpressionOrWalrusRhs(evaluator, reference, testExpression.d.args[0].d.valueExpr)) {
519
- const callTypeResult = evaluator.getTypeOfExpression(testExpression.d.leftExpr, 2 /* EvalFlags.CallBaseDefaults */);
520
- const callType = callTypeResult.type;
521
- if ((0, types_1.isInstantiableClass)(callType) && types_1.ClassType.isBuiltIn(callType, 'bool')) {
522
- return (type) => {
523
- return {
524
- type: narrowTypeForTruthiness(evaluator, type, isPositiveTest),
525
- isIncomplete: !!callTypeResult.isIncomplete,
526
- };
317
+ const isInstanceInfo = TypeEvaluatorNarrowing.getIsInstanceCallNarrowingInfo(getIsInstanceCallNarrowingContext(evaluator), reference, testExpression, isPositiveTest);
318
+ if (isInstanceInfo) {
319
+ if (isInstanceInfo.kind === 'narrow') {
320
+ return (type) => {
321
+ return {
322
+ type: narrowTypeForInstanceOrSubclass(evaluator, type, isInstanceInfo.classTypeList, isInstanceInfo.isInstanceCheck,
323
+ /* isTypeIsCheck */ false, isPositiveTest, testExpression, nodeInfo),
324
+ isIncomplete: isInstanceInfo.isIncomplete,
527
325
  };
528
- }
326
+ };
529
327
  }
328
+ return (type) => {
329
+ return {
330
+ type,
331
+ isIncomplete: true,
332
+ };
333
+ };
334
+ }
335
+ const boolCallInfo = TypeEvaluatorNarrowing.getBoolCallNarrowingInfo(getBoolCallNarrowingContext(evaluator), reference, testExpression);
336
+ if (boolCallInfo) {
337
+ return (type) => {
338
+ return {
339
+ type: narrowTypeForTruthiness(evaluator, type, isPositiveTest),
340
+ isIncomplete: boolCallInfo.isIncomplete,
341
+ };
342
+ };
530
343
  }
531
344
  // Look for a TypeGuard function.
532
345
  const isFunctionReturnTypeGuard = (type) => {
@@ -703,11 +516,9 @@ function getTypeNarrowingCallback(evaluator, reference, testExpression, isPositi
703
516
  // We normally won't find a "not" operator here because they are stripped out
704
517
  // by the binder when it creates condition flow nodes, but we can find this
705
518
  // in the case of local variables type narrowing.
706
- if (reference.nodeType === 38 /* ParseNodeType.Name */) {
707
- if (testExpression.nodeType === 55 /* ParseNodeType.UnaryOperation */ &&
708
- testExpression.d.operator === 38 /* OperatorType.Not */) {
709
- return getTypeNarrowingCallback(evaluator, reference, testExpression.d.expr, !isPositiveTest, nodeInfo, recursionCount);
710
- }
519
+ const notExpressionInfo = TypeEvaluatorNarrowing.getNotExpressionNarrowingInfo(reference, testExpression, isPositiveTest);
520
+ if (reference.nodeType === 38 /* ParseNodeType.Name */ && notExpressionInfo) {
521
+ return getTypeNarrowingCallback(evaluator, reference, notExpressionInfo.innerExpression, notExpressionInfo.adjIsPositiveTest, nodeInfo, recursionCount);
711
522
  }
712
523
  return undefined;
713
524
  }
@@ -802,377 +613,34 @@ function getTypeNarrowingCallbackForAssignmentExpression(evaluator, reference, t
802
613
  getTypeNarrowingCallback(evaluator, reference, testExpression.d.name, isPositiveTest, nodeInfo, recursionCount));
803
614
  }
804
615
  function narrowTypeForUserDefinedTypeGuard(evaluator, type, typeGuardType, isPositiveTest, isStrictTypeGuard, errorNode, nodeInfo) {
805
- // For non-strict type guards, always narrow to the typeGuardType
806
- // in the positive case and don't narrow in the negative case.
807
- if (!isStrictTypeGuard) {
808
- let result = type;
809
- if (isPositiveTest) {
810
- result = typeGuardType;
811
- // If the type guard is a non-constrained TypeVar, add a
812
- // condition to the resulting type.
813
- if ((0, types_1.isTypeVar)(type) && !(0, types_1.isParamSpec)(type) && !types_1.TypeVarType.hasConstraints(type)) {
814
- result = (0, typeUtils_1.addConditionToType)(result, [{ typeVar: type, constraintIndex: 0 }]);
815
- }
816
- return result;
817
- }
818
- return result;
819
- }
820
- const filterTypes = [];
821
- (0, typeUtils_1.doForEachSubtype)(typeGuardType, (typeGuardSubtype) => {
822
- filterTypes.push((0, typeUtils_1.convertToInstantiable)(typeGuardSubtype));
823
- });
824
- return narrowTypeForInstanceOrSubclass(evaluator, type, filterTypes,
825
- /* isInstanceCheck */ true,
826
- /* isTypeIsCheck */ true, isPositiveTest, errorNode, nodeInfo);
616
+ return TypeEvaluatorNarrowing.narrowTypeForUserDefinedTypeGuard(evaluator, type, typeGuardType, isPositiveTest, isStrictTypeGuard, errorNode, nodeInfo, narrowTypeForInstanceOrSubclass);
827
617
  }
828
618
  // Narrow the type based on whether the subtype can be true or false.
829
619
  function narrowTypeForTruthiness(evaluator, type, isPositiveTest) {
830
- return (0, typeUtils_1.mapSubtypes)(type, (subtype) => {
831
- if (isPositiveTest) {
832
- if (evaluator.canBeTruthy(subtype)) {
833
- return evaluator.removeFalsinessFromType(subtype);
834
- }
835
- }
836
- else {
837
- if (evaluator.canBeFalsy(subtype)) {
838
- return evaluator.removeTruthinessFromType(subtype);
839
- }
840
- }
841
- return undefined;
842
- });
620
+ return TypeEvaluatorNarrowing.narrowTypeForTruthiness(evaluator, type, isPositiveTest);
843
621
  }
844
622
  // Handle type narrowing for expressions of the form "a[I] is None" and "a[I] is not None" where
845
623
  // I is an integer and a is a union of Tuples (or subtypes thereof) with known lengths and entry types.
846
624
  function narrowTupleTypeForIsNone(evaluator, type, isPositiveTest, indexValue) {
847
- return evaluator.mapSubtypesExpandTypeVars(type, /* options */ undefined, (subtype) => {
848
- const tupleType = (0, typeUtils_1.getSpecializedTupleType)(subtype);
849
- if (!tupleType || (0, typeUtils_1.isUnboundedTupleClass)(tupleType) || !tupleType.priv.tupleTypeArgs) {
850
- return subtype;
851
- }
852
- const tupleLength = tupleType.priv.tupleTypeArgs.length;
853
- if (indexValue < 0 || indexValue >= tupleLength) {
854
- return subtype;
855
- }
856
- const typeOfEntry = evaluator.makeTopLevelTypeVarsConcrete(tupleType.priv.tupleTypeArgs[indexValue].type);
857
- if (isPositiveTest) {
858
- if (!evaluator.assignType(typeOfEntry, evaluator.getNoneType())) {
859
- return undefined;
860
- }
861
- }
862
- else {
863
- if ((0, typeUtils_1.isNoneInstance)(typeOfEntry)) {
864
- return undefined;
865
- }
866
- }
867
- return subtype;
868
- });
869
- }
870
- // Walks a chain of NewType instances down to the innermost non-NewType base
871
- // instance (e.g. NewType("Y", NewType("X", bool)) -> an instance of bool).
872
- // Returns undefined if `subtype` is not a NewType instance or its base cannot
873
- // be resolved to a class.
874
- function getInnermostNewTypeBaseInstance(subtype) {
875
- if (!(0, types_1.isClassInstance)(subtype) || !types_1.ClassType.isNewTypeClass(subtype)) {
876
- return undefined;
877
- }
878
- let currentType = subtype;
879
- while ((0, types_1.isClassInstance)(currentType) && types_1.ClassType.isNewTypeClass(currentType)) {
880
- if (currentType.shared.baseClasses.length === 0) {
881
- return undefined;
882
- }
883
- const baseClass = currentType.shared.baseClasses[0];
884
- if (!(0, types_1.isClass)(baseClass)) {
885
- return undefined;
886
- }
887
- currentType = types_1.ClassType.cloneAsInstance(baseClass);
888
- }
889
- return currentType;
625
+ return TypeEvaluatorNarrowing.narrowTupleTypeForIsNone(evaluator, type, isPositiveTest, indexValue);
890
626
  }
891
627
  // Handle type narrowing for expressions of the form "x is None" and "x is not None".
892
628
  function narrowTypeForIsNone(evaluator, type, isPositiveTest) {
893
- const expandedType = (0, typeUtils_1.mapSubtypes)(type, (subtype) => {
894
- return (0, typeUtils_1.transformPossibleRecursiveTypeAlias)(subtype);
895
- });
896
- let resultIncludesNoneSubtype = false;
897
- const result = evaluator.mapSubtypesExpandTypeVars(expandedType,
898
- /* options */ undefined, (subtype, unexpandedSubtype) => {
899
- if ((0, types_1.isAnyOrUnknown)(subtype)) {
900
- // Narrow to None in positive tests, matching the behavior of other
901
- // narrowing functions (narrowTypeForInstanceOrSubclass, narrowTypeForLiteralComparison).
902
- if (isPositiveTest) {
903
- resultIncludesNoneSubtype = true;
904
- return (0, typeUtils_1.addConditionToType)(evaluator.getNoneType(), subtype.props?.condition);
905
- }
906
- // For negative tests, keep the original type.
907
- return subtype;
908
- }
909
- let useExpandedSubtype = false;
910
- if ((0, types_1.isTypeVar)(unexpandedSubtype) && !types_1.TypeVarType.isSelf(unexpandedSubtype)) {
911
- // If the TypeVar has value constraints and one or more of them
912
- // are possibly compatible with None, use the expanded subtypes.
913
- if (unexpandedSubtype.shared.constraints.some((constraint) => {
914
- return evaluator.assignType(constraint, evaluator.getNoneType());
915
- })) {
916
- useExpandedSubtype = true;
917
- }
918
- // If the TypeVar han an explicit bound that is possibly compatible
919
- // with None (e.g. "T: int | None"), use the expanded subtypes.
920
- if (unexpandedSubtype.shared.boundType &&
921
- evaluator.assignType(unexpandedSubtype.shared.boundType, evaluator.getNoneType())) {
922
- useExpandedSubtype = true;
923
- }
924
- }
925
- const adjustedSubtype = useExpandedSubtype ? subtype : unexpandedSubtype;
926
- // A NewType whose innermost base is exactly None (e.g. NewType("Apple", NoneType))
927
- // can be identity-compared with None: keep the NewType identity on the positive
928
- // branch and eliminate it on the negative branch. A NewType whose base is merely
929
- // None-compatible (e.g. NewType("Obj", object)) is intentionally not handled here;
930
- // it falls through to the generic checks below so that "is not None" does not
931
- // incorrectly collapse to Never.
932
- const newTypeBaseInstance = getInnermostNewTypeBaseInstance(adjustedSubtype);
933
- if (newTypeBaseInstance && (0, typeUtils_1.isNoneInstance)(newTypeBaseInstance)) {
934
- resultIncludesNoneSubtype = true;
935
- return isPositiveTest ? adjustedSubtype : undefined;
936
- }
937
- // Is it an exact match for None?
938
- if ((0, typeUtils_1.isNoneInstance)(subtype)) {
939
- resultIncludesNoneSubtype = true;
940
- return isPositiveTest ? adjustedSubtype : undefined;
941
- }
942
- // Is it potentially None?
943
- if (evaluator.assignType(subtype, evaluator.getNoneType())) {
944
- resultIncludesNoneSubtype = true;
945
- return isPositiveTest
946
- ? (0, typeUtils_1.addConditionToType)(evaluator.getNoneType(), subtype.props?.condition)
947
- : adjustedSubtype;
948
- }
949
- return isPositiveTest ? undefined : adjustedSubtype;
950
- });
951
- // If this is a positive test and the result is a union that includes None,
952
- // we can eliminate all the non-None subtypes include Any or Unknown. If some
953
- // of the subtypes are None types with conditions, retain those.
954
- if (isPositiveTest && resultIncludesNoneSubtype) {
955
- return (0, typeUtils_1.mapSubtypes)(result, (subtype) => {
956
- const baseInstance = getInnermostNewTypeBaseInstance(subtype);
957
- return (0, typeUtils_1.isNoneInstance)(subtype) || (baseInstance && (0, typeUtils_1.isNoneInstance)(baseInstance)) ? subtype : undefined;
958
- });
959
- }
960
- return result;
629
+ return TypeEvaluatorNarrowing.narrowTypeForIsNone(evaluator, type, isPositiveTest);
961
630
  }
962
631
  // Handle type narrowing for expressions of the form "x is ..." and "x is not ...".
963
632
  function narrowTypeForIsEllipsis(evaluator, node, type, isPositiveTest) {
964
- const expandedType = (0, typeUtils_1.mapSubtypes)(type, (subtype) => {
965
- return (0, typeUtils_1.transformPossibleRecursiveTypeAlias)(subtype);
966
- });
967
- let resultIncludesEllipsisSubtype = false;
968
- const ellipsisType = evaluator.getBuiltInObject(node, 'EllipsisType') ??
969
- evaluator.getBuiltInObject(node, 'ellipsis') ??
970
- types_1.AnyType.create();
971
- const isEllipsisInstance = (subtype) => {
972
- return (0, types_1.isClassInstance)(subtype) && types_1.ClassType.isBuiltIn(subtype, ['EllipsisType', 'ellipsis']);
973
- };
974
- const result = evaluator.mapSubtypesExpandTypeVars(expandedType,
975
- /* options */ undefined, (subtype, unexpandedSubtype) => {
976
- if ((0, types_1.isAnyOrUnknown)(subtype)) {
977
- // We need to assume that "Any" is always both ellipsis and not ellipsis,
978
- // so it matches regardless of whether the test is positive or negative.
979
- return subtype;
980
- }
981
- // If this is a TypeVar that isn't constrained, use the unexpanded
982
- // TypeVar. For all other cases (including constrained TypeVars),
983
- // use the expanded subtype.
984
- const adjustedSubtype = (0, types_1.isTypeVar)(unexpandedSubtype) && !types_1.TypeVarType.hasConstraints(unexpandedSubtype)
985
- ? unexpandedSubtype
986
- : subtype;
987
- // Only a NewType whose innermost base is exactly the ellipsis type is treated
988
- // as the singleton (see narrowTypeForIsNone for the rationale); a merely
989
- // ellipsis-compatible base falls through so "is not ..." does not collapse to Never.
990
- const newTypeBaseInstance = getInnermostNewTypeBaseInstance(adjustedSubtype);
991
- if (newTypeBaseInstance && isEllipsisInstance(newTypeBaseInstance)) {
992
- resultIncludesEllipsisSubtype = true;
993
- return isPositiveTest ? adjustedSubtype : undefined;
994
- }
995
- // Is it an exact match for ellipsis?
996
- if (isEllipsisInstance(subtype)) {
997
- resultIncludesEllipsisSubtype = true;
998
- return isPositiveTest ? adjustedSubtype : undefined;
999
- }
1000
- // Is it potentially ellipsis?
1001
- if (evaluator.assignType(subtype, ellipsisType)) {
1002
- resultIncludesEllipsisSubtype = true;
1003
- return isPositiveTest ? (0, typeUtils_1.addConditionToType)(ellipsisType, subtype.props?.condition) : adjustedSubtype;
1004
- }
1005
- return isPositiveTest ? undefined : adjustedSubtype;
1006
- });
1007
- // If this is a positive test and the result is a union that includes ellipsis,
1008
- // we can eliminate all the non-ellipsis subtypes include Any or Unknown. If some
1009
- // of the subtypes are ellipsis types with conditions, retain those.
1010
- if (isPositiveTest && resultIncludesEllipsisSubtype) {
1011
- return (0, typeUtils_1.mapSubtypes)(result, (subtype) => {
1012
- const baseInstance = getInnermostNewTypeBaseInstance(subtype);
1013
- return isEllipsisInstance(subtype) || (baseInstance && isEllipsisInstance(baseInstance))
1014
- ? subtype
1015
- : undefined;
1016
- });
1017
- }
1018
- return result;
633
+ return TypeEvaluatorNarrowing.narrowTypeForIsEllipsis(evaluator, node, type, isPositiveTest);
1019
634
  }
1020
635
  // The "isinstance" and "issubclass" calls accept classes, runtime unions,
1021
636
  // and arbitrarily-nested tuples of these. This method extracts possible
1022
637
  // classes and guaranteed negative filters,
1023
638
  // or undefined if the filter contains a non-class type.
1024
639
  function getIsInstanceClassTypes(evaluator, argType, argExpr) {
1025
- let foundNonClassType = false;
1026
- const classTypes = [];
1027
- const negativeClassTypes = [];
1028
- // Create a helper function that returns a list of class types or
1029
- // undefined if any of the types are not valid.
1030
- const addClassTypesToList = (types, isAmbiguous, isUnboundedEntry) => {
1031
- types.forEach((subtype) => {
1032
- if ((0, types_1.isClass)(subtype)) {
1033
- subtype = (0, typeUtils_1.specializeWithUnknownTypeArgs)(subtype, evaluator.getTupleClassType());
1034
- if ((0, types_1.isInstantiableClass)(subtype) && types_1.ClassType.isBuiltIn(subtype, 'Callable')) {
1035
- subtype = (0, typeUtils_1.convertToInstantiable)((0, typeUtils_1.getUnknownTypeForCallable)());
1036
- }
1037
- else if (types_1.TypeBase.isInstance(subtype) && types_1.ClassType.isBuiltIn(subtype, 'type')) {
1038
- if (subtype.priv.typeArgs && subtype.priv.typeArgs.length > 0) {
1039
- const typeArg = subtype.priv.typeArgs[0];
1040
- if ((0, types_1.isAnyOrUnknown)(typeArg)) {
1041
- foundNonClassType = true;
1042
- return;
1043
- }
1044
- if ((0, types_1.isInstantiableClass)(typeArg)) {
1045
- subtype = types_1.ClassType.cloneIncludeSubclasses(typeArg);
1046
- }
1047
- else if ((0, types_1.isClassInstance)(typeArg)) {
1048
- subtype = types_1.ClassType.cloneAsInstantiable(typeArg);
1049
- }
1050
- }
1051
- }
1052
- }
1053
- let classType;
1054
- if ((0, types_1.isInstantiableClass)(subtype)) {
1055
- // If this is a reference to a class that has type promotions (e.g.
1056
- // float or complex), remove the promotions for purposes of the
1057
- // isinstance check).
1058
- if (!subtype.priv.includeSubclasses && subtype.priv.includePromotions) {
1059
- subtype = types_1.ClassType.cloneRemoveTypePromotions(subtype);
1060
- }
1061
- classType = subtype;
1062
- }
1063
- else if ((0, types_1.isTypeVar)(subtype) && types_1.TypeBase.isInstantiable(subtype)) {
1064
- classType = subtype;
1065
- }
1066
- else if ((0, typeUtils_1.isNoneTypeClass)(subtype)) {
1067
- (0, debug_1.assert)((0, types_1.isInstantiableClass)(subtype));
1068
- classType = subtype;
1069
- }
1070
- else if ((0, types_1.isFunction)(subtype) &&
1071
- subtype.shared.parameters.length === 2 &&
1072
- subtype.shared.parameters[0].category === 1 /* ParamCategory.ArgsList */ &&
1073
- subtype.shared.parameters[1].category === 2 /* ParamCategory.KwargsDict */) {
1074
- classType = subtype;
1075
- }
1076
- else {
1077
- foundNonClassType = true;
1078
- return;
1079
- }
1080
- classTypes.push(classType);
1081
- // Unbounded entries may be absent, and alternative class objects
1082
- // are not all guaranteed to participate in the runtime check.
1083
- if (!isUnboundedEntry && !isAmbiguous) {
1084
- negativeClassTypes.push(classType);
1085
- }
1086
- });
1087
- };
1088
- const addClassTypesRecursive = (type, isAmbiguous, isUnboundedEntry, recursionCount, expression) => {
1089
- if (recursionCount > types_1.maxTypeRecursionCount) {
1090
- return;
1091
- }
1092
- // A runtime union checks both operands, but either operand can itself
1093
- // be an alternative class object. Keep their certainty separate.
1094
- if ((0, types_1.isUnion)(type) &&
1095
- expression?.nodeType === 7 /* ParseNodeType.BinaryOperation */ &&
1096
- expression.d.operator === 6 /* OperatorType.BitwiseOr */) {
1097
- const leftType = evaluator.getCachedType(expression.d.leftExpr);
1098
- const rightType = evaluator.getCachedType(expression.d.rightExpr);
1099
- if (leftType &&
1100
- rightType &&
1101
- !(0, operations_1.customMetaclassSupportsMethod)(leftType, '__or__') &&
1102
- !(0, operations_1.customMetaclassSupportsMethod)(rightType, '__ror__')) {
1103
- // Match the union evaluator's normalization of None operands to NoneType.
1104
- const adjustedLeftType = (0, typeUtils_1.isNoneInstance)(leftType)
1105
- ? (0, typeUtils_1.convertToInstantiable)(evaluator.getNoneType())
1106
- : leftType;
1107
- const adjustedRightType = (0, typeUtils_1.isNoneInstance)(rightType)
1108
- ? (0, typeUtils_1.convertToInstantiable)(evaluator.getNoneType())
1109
- : rightType;
1110
- addClassTypesRecursive(adjustedLeftType, isAmbiguous, isUnboundedEntry, recursionCount, expression.d.leftExpr);
1111
- addClassTypesRecursive(adjustedRightType, isAmbiguous, isUnboundedEntry, recursionCount, expression.d.rightExpr);
1112
- return;
1113
- }
1114
- }
1115
- const isRuntimeUnion = type.props?.specialForm && types_1.ClassType.isBuiltIn(type.props.specialForm, 'UnionType');
1116
- const isAmbiguousType = isAmbiguous || ((0, types_1.isUnion)(type) && !isRuntimeUnion);
1117
- (0, typeUtils_1.doForEachSubtype)(type, (subtype) => {
1118
- if ((0, types_1.isClassInstance)(subtype) && (0, typeUtils_1.isTupleClass)(subtype)) {
1119
- if (subtype.priv.tupleTypeArgs) {
1120
- let entryExpressions;
1121
- if (expression?.nodeType === 52 /* ParseNodeType.Tuple */) {
1122
- const expressions = [];
1123
- expression.d.items.forEach((item) => {
1124
- if (item.nodeType !== 56 /* ParseNodeType.Unpack */) {
1125
- expressions.push(item);
1126
- return;
1127
- }
1128
- const unpackedType = evaluator.getCachedType(item.d.expr);
1129
- const entryCount = unpackedType && (0, types_1.isClassInstance)(unpackedType) && (0, typeUtils_1.isTupleClass)(unpackedType)
1130
- ? unpackedType.priv.tupleTypeArgs?.length ?? 1
1131
- : 1;
1132
- for (let index = 0; index < entryCount; index++) {
1133
- expressions.push(undefined);
1134
- }
1135
- });
1136
- if (expressions.length === subtype.priv.tupleTypeArgs.length) {
1137
- entryExpressions = expressions;
1138
- }
1139
- }
1140
- subtype.priv.tupleTypeArgs.forEach((tupleEntry, index) => {
1141
- addClassTypesRecursive(tupleEntry.type, isAmbiguousType, isUnboundedEntry || tupleEntry.isUnbounded, recursionCount + 1, entryExpressions?.[index]);
1142
- });
1143
- }
1144
- }
1145
- else {
1146
- addClassTypesToList([subtype], isAmbiguousType, isUnboundedEntry);
1147
- }
1148
- });
1149
- };
1150
- addClassTypesRecursive(argType, /* isAmbiguous */ false, /* isUnboundedEntry */ false, 0, argExpr);
1151
- return foundNonClassType ? undefined : { classTypes, negativeClassTypes };
640
+ return TypeEvaluatorNarrowing.getIsInstanceClassTypes(evaluator, argType, argExpr);
1152
641
  }
1153
642
  function narrowTypeForInstanceOrSubclass(evaluator, type, filterTypes, isInstanceCheck, isTypeIsCheck, isPositiveTest, errorNode, nodeInfo) {
1154
- if (!isInstanceCheck && !isTypeIsCheck && !isPositiveTest) {
1155
- // A custom subclass check can reject even the exact same final class.
1156
- // Preserve the existing narrowing policy for non-final classes, including ABCs.
1157
- filterTypes = filterTypes.filter((filterType) => {
1158
- const concreteFilterType = evaluator.makeTopLevelTypeVarsConcrete(filterType);
1159
- return (!(0, types_1.isInstantiableClass)(concreteFilterType) ||
1160
- !types_1.ClassType.isFinal(concreteFilterType) ||
1161
- !(0, operations_1.customMetaclassSupportsMethod)(concreteFilterType, '__subclasscheck__'));
1162
- });
1163
- }
1164
- if (!isPositiveTest && filterTypes.length === 0) {
1165
- return type;
1166
- }
1167
- // First try with intersection types disallowed.
1168
- const narrowedType = narrowTypeForInstanceOrSubclassInternal(evaluator, type, filterTypes, isInstanceCheck, isTypeIsCheck, isPositiveTest,
1169
- /* allowIntersections */ false, errorNode, nodeInfo);
1170
- if (!(0, types_1.isNever)(narrowedType)) {
1171
- return narrowedType;
1172
- }
1173
- // Try again with intersection types allowed.
1174
- return narrowTypeForInstanceOrSubclassInternal(evaluator, type, filterTypes, isInstanceCheck, isTypeIsCheck, isPositiveTest,
1175
- /* allowIntersections */ true, errorNode, nodeInfo);
643
+ return TypeEvaluatorNarrowing.narrowTypeForInstanceOrSubclass(evaluator, type, filterTypes, isInstanceCheck, isTypeIsCheck, isPositiveTest, errorNode, nodeInfo, narrowTypeForInstanceOrSubclassInternal);
1176
644
  }
1177
645
  function narrowTypeForInstanceOrSubclassInternal(evaluator, type, filterTypes, isInstanceCheck, isTypeIsCheck, isPositiveTest, allowIntersections, errorNode, nodeInfo) {
1178
646
  const result = (0, typeUtils_1.mapSubtypes)(type, (subtype) => {
@@ -1652,914 +1120,91 @@ function intersectTupleTypes(type1, type2) {
1652
1120
  }
1653
1121
  // Attempts to narrow a union of tuples based on their known length.
1654
1122
  function narrowTypeForTupleLength(evaluator, referenceType, lengthValue, isPositiveTest, isLessThanCheck) {
1655
- return (0, typeUtils_1.mapSubtypes)(referenceType, (subtype) => {
1656
- const concreteSubtype = evaluator.makeTopLevelTypeVarsConcrete(subtype);
1657
- // If it's not a tuple, we can't narrow it.
1658
- if (!(0, types_1.isClassInstance)(concreteSubtype) ||
1659
- !(0, typeUtils_1.isTupleClass)(concreteSubtype) ||
1660
- !concreteSubtype.priv.tupleTypeArgs) {
1661
- return subtype;
1662
- }
1663
- // If the tuple contains a TypeVarTuple, we can't narrow it.
1664
- if (concreteSubtype.priv.tupleTypeArgs.some((typeArg) => (0, types_1.isUnpackedTypeVarTuple)(typeArg.type))) {
1665
- return subtype;
1666
- }
1667
- // If the tuple contains no unbounded elements, then we know its length exactly.
1668
- if (!concreteSubtype.priv.tupleTypeArgs.some((typeArg) => typeArg.isUnbounded)) {
1669
- const tupleLengthMatches = isLessThanCheck
1670
- ? concreteSubtype.priv.tupleTypeArgs.length < lengthValue
1671
- : concreteSubtype.priv.tupleTypeArgs.length === lengthValue;
1672
- return tupleLengthMatches === isPositiveTest ? subtype : undefined;
1673
- }
1674
- // The tuple contains a "...". We'll expand this into as many elements as
1675
- // necessary to match the lengthValue.
1676
- const elementsToAdd = lengthValue - concreteSubtype.priv.tupleTypeArgs.length + 1;
1677
- if (!isLessThanCheck) {
1678
- // If the specified length is smaller than the minimum length of this tuple,
1679
- // we can rule it out for a positive test and rule it in for a negative test.
1680
- if (elementsToAdd < 0) {
1681
- return isPositiveTest ? undefined : subtype;
1682
- }
1683
- if (!isPositiveTest) {
1684
- // If this is an equality check for the minimum length (e.g.
1685
- // "len(x) == 0"), we can expand the minimum length by one).
1686
- const minLen = concreteSubtype.priv.tupleTypeArgs.length - 1;
1687
- if (lengthValue === minLen) {
1688
- return expandUnboundedTupleElement(concreteSubtype, 1, /* keepUnbounded */ true);
1689
- }
1690
- return subtype;
1691
- }
1692
- return expandUnboundedTupleElement(concreteSubtype, elementsToAdd, /* keepUnbounded */ false);
1693
- }
1694
- // If this is a tuple related to an "*args: P.args" parameter, don't expand it.
1695
- if ((0, types_1.isParamSpec)(subtype) && subtype.priv.paramSpecAccess) {
1696
- return subtype;
1697
- }
1698
- // Place an upper limit on the number of union subtypes we
1699
- // will expand the tuple to.
1700
- const maxTupleUnionExpansion = 32;
1701
- if (elementsToAdd > maxTupleUnionExpansion) {
1702
- return subtype;
1703
- }
1704
- if (isPositiveTest) {
1705
- if (elementsToAdd < 1) {
1706
- return undefined;
1707
- }
1708
- const typesToCombine = [];
1709
- for (let i = 0; i < elementsToAdd; i++) {
1710
- typesToCombine.push(expandUnboundedTupleElement(concreteSubtype, i, /* keepUnbounded */ false));
1711
- }
1712
- return (0, types_1.combineTypes)(typesToCombine);
1713
- }
1714
- return expandUnboundedTupleElement(concreteSubtype, elementsToAdd, /* keepUnbounded */ true);
1715
- });
1123
+ return TypeEvaluatorNarrowing.narrowTypeForTupleLength(evaluator, referenceType, lengthValue, isPositiveTest, isLessThanCheck);
1716
1124
  }
1717
1125
  // Expands a tuple type that contains an unbounded element to include
1718
1126
  // multiple bounded elements of that same type in place of (or in addition
1719
1127
  // to) the unbounded element.
1720
- function expandUnboundedTupleElement(tupleType, elementsToAdd, keepUnbounded) {
1721
- const tupleTypeArgs = [];
1722
- tupleType.priv.tupleTypeArgs.forEach((typeArg) => {
1723
- if (!typeArg.isUnbounded) {
1724
- tupleTypeArgs.push(typeArg);
1725
- }
1726
- else {
1727
- for (let i = 0; i < elementsToAdd; i++) {
1728
- tupleTypeArgs.push({ isUnbounded: false, type: typeArg.type });
1729
- }
1730
- if (keepUnbounded) {
1731
- tupleTypeArgs.push(typeArg);
1732
- }
1733
- }
1734
- });
1735
- return (0, typeUtils_1.specializeTupleClass)(tupleType, tupleTypeArgs);
1736
- }
1737
1128
  // Attempts to narrow a type (make it more constrained) based on an "in" binary operator.
1738
1129
  function narrowTypeForContainerType(evaluator, referenceType, containerType, isPositiveTest) {
1739
- if (isPositiveTest) {
1740
- const elementType = getElementTypeForContainerNarrowing(containerType);
1741
- if (!elementType) {
1742
- return referenceType;
1743
- }
1744
- return narrowTypeForContainerElementType(evaluator, referenceType, evaluator.makeTopLevelTypeVarsConcrete(elementType));
1745
- }
1746
- // Narrowing in the negative case is possible only with tuples
1747
- // with a known length.
1748
- if (!(0, types_1.isClassInstance)(containerType) ||
1749
- !types_1.ClassType.isBuiltIn(containerType, 'tuple') ||
1750
- !containerType.priv.tupleTypeArgs) {
1751
- return referenceType;
1752
- }
1753
- // Determine which tuple types can be eliminated. Only "None",
1754
- // literal types, and final instantiable classes can be handled here.
1755
- // Non-final instantiable classes (type[A]) cannot be eliminated in negative
1756
- // tests because a subclass SubA(A) reaches the negative branch at runtime
1757
- // (since SubA != A). Generic classes are also excluded because a
1758
- // specialized class object like Box[int] is not the same runtime object
1759
- // as Box. Classes that define or inherit __class_getitem__ are excluded
1760
- // for the same reason.
1761
- const typesToEliminate = [];
1762
- containerType.priv.tupleTypeArgs.forEach((tupleEntry) => {
1763
- if (!tupleEntry.isUnbounded) {
1764
- if ((0, typeUtils_1.isNoneInstance)(tupleEntry.type)) {
1765
- typesToEliminate.push(tupleEntry.type);
1766
- }
1767
- else if ((0, types_1.isClassInstance)(tupleEntry.type) && (0, typeUtils_1.isLiteralType)(tupleEntry.type)) {
1768
- typesToEliminate.push(tupleEntry.type);
1769
- }
1770
- else if ((0, types_1.isInstantiableClass)(tupleEntry.type) &&
1771
- types_1.ClassType.isFinal(tupleEntry.type) &&
1772
- tupleEntry.type.shared.typeParams.length === 0 &&
1773
- !(0, typeUtils_1.lookUpClassMember)(tupleEntry.type, '__class_getitem__')) {
1774
- typesToEliminate.push(tupleEntry.type);
1775
- }
1776
- }
1777
- });
1778
- if (typesToEliminate.length === 0) {
1779
- return referenceType;
1780
- }
1781
- const canNarrowLiterals = typesToEliminate.some(types_1.isClassInstance);
1782
- return (0, typeUtils_1.mapSubtypes)(referenceType, (unexpandedSubtype) => {
1783
- const referenceSubtype = evaluator.makeTopLevelTypeVarsConcrete(unexpandedSubtype);
1784
- if (canNarrowLiterals &&
1785
- (0, types_1.isClassInstance)(referenceSubtype) &&
1786
- referenceSubtype.priv.literalValue === undefined) {
1787
- // If we're able to enumerate all possible literal values
1788
- // (for bool or enum), we can eliminate all others in a negative test.
1789
- const allLiteralTypes = enumerateLiteralsForType(evaluator, referenceSubtype);
1790
- if (allLiteralTypes && allLiteralTypes.length > 0) {
1791
- return (0, types_1.combineTypes)(allLiteralTypes.filter((type) => !typesToEliminate.some((t) => (0, types_1.isTypeSame)(t, type))));
1792
- }
1793
- }
1794
- if (typesToEliminate.some((t) => (0, types_1.isTypeSame)(t, referenceSubtype))) {
1795
- return undefined;
1796
- }
1797
- return unexpandedSubtype;
1798
- });
1130
+ return TypeEvaluatorNarrowing.narrowTypeForContainerType(evaluator, referenceType, containerType, isPositiveTest);
1799
1131
  }
1800
1132
  function getElementTypeForContainerNarrowing(containerType) {
1801
- // We support contains narrowing only for certain built-in types that have been specialized.
1802
- const supportedContainers = ['list', 'set', 'frozenset', 'deque', 'tuple', 'dict', 'defaultdict', 'OrderedDict'];
1803
- if (!(0, types_1.isClassInstance)(containerType) || !types_1.ClassType.isBuiltIn(containerType, supportedContainers)) {
1804
- return undefined;
1805
- }
1806
- if (!containerType.priv.typeArgs || containerType.priv.typeArgs.length < 1) {
1807
- return undefined;
1808
- }
1809
- let elementType = containerType.priv.typeArgs[0];
1810
- if ((0, typeUtils_1.isTupleClass)(containerType) && containerType.priv.tupleTypeArgs) {
1811
- elementType = (0, types_1.combineTypes)(containerType.priv.tupleTypeArgs.map((t) => t.type));
1812
- }
1813
- return elementType;
1133
+ return TypeEvaluatorNarrowing.getElementTypeForContainerNarrowing(containerType);
1814
1134
  }
1815
1135
  function narrowTypeForContainerElementType(evaluator, referenceType, elementType) {
1816
- return evaluator.mapSubtypesExpandTypeVars(referenceType, /* options */ undefined, (referenceSubtype) => {
1817
- return (0, typeUtils_1.mapSubtypes)(elementType, (elementSubtype) => {
1818
- if ((0, types_1.isAnyOrUnknown)(elementSubtype)) {
1819
- return referenceSubtype;
1820
- }
1821
- // If the two types are disjoint (i.e. are not comparable), eliminate this subtype.
1822
- if (!evaluator.isTypeComparable(elementSubtype, referenceSubtype)) {
1823
- return undefined;
1824
- }
1825
- // If one of the two types is a literal, we can narrow to that type.
1826
- if ((0, types_1.isClassInstance)(elementSubtype) &&
1827
- ((0, typeUtils_1.isLiteralLikeType)(elementSubtype) || (0, typeUtils_1.isNoneInstance)(elementSubtype)) &&
1828
- evaluator.assignType(referenceSubtype, elementSubtype)) {
1829
- return (0, typeUtils_1.stripTypeForm)((0, typeUtils_1.addConditionToType)(elementSubtype, referenceSubtype.props?.condition));
1830
- }
1831
- if ((0, types_1.isClassInstance)(referenceSubtype) &&
1832
- ((0, typeUtils_1.isLiteralLikeType)(referenceSubtype) || (0, typeUtils_1.isNoneInstance)(referenceSubtype)) &&
1833
- evaluator.assignType(elementSubtype, referenceSubtype)) {
1834
- return (0, typeUtils_1.stripTypeForm)((0, typeUtils_1.addConditionToType)(referenceSubtype, elementSubtype.props?.condition));
1835
- }
1836
- // If the element type is a known class object that is assignable to
1837
- // the reference type, we can narrow to that class object.
1838
- if ((0, types_1.isInstantiableClass)(elementSubtype) &&
1839
- !elementSubtype.priv.includeSubclasses &&
1840
- evaluator.assignType(referenceSubtype, elementSubtype)) {
1841
- return (0, typeUtils_1.stripTypeForm)((0, typeUtils_1.addConditionToType)(elementSubtype, referenceSubtype.props?.condition));
1842
- }
1843
- // It's not safe to narrow.
1844
- return referenceSubtype;
1845
- });
1846
- });
1136
+ return TypeEvaluatorNarrowing.narrowTypeForContainerElementType(evaluator, referenceType, elementType);
1847
1137
  }
1848
1138
  // Attempts to narrow a type based on whether it is a TypedDict with
1849
1139
  // a literal key value.
1850
1140
  function narrowTypeForTypedDictKey(evaluator, referenceType, literalKey, isPositiveTest) {
1851
- const narrowedType = evaluator.mapSubtypesExpandTypeVars(referenceType,
1852
- /* options */ undefined, (subtype, unexpandedSubtype) => {
1853
- if ((0, types_1.isParamSpec)(unexpandedSubtype)) {
1854
- return unexpandedSubtype;
1855
- }
1856
- if ((0, types_1.isClassInstance)(subtype) && types_1.ClassType.isTypedDictClass(subtype)) {
1857
- const entries = (0, typedDicts_1.getTypedDictMembersForClass)(evaluator, subtype, /* allowNarrowed */ true);
1858
- const knownEntry = entries.knownItems.get(literalKey.priv.literalValue);
1859
- const tdEntry = knownEntry ?? entries.extraItems;
1860
- if (isPositiveTest) {
1861
- // The code that is commented out below implements the behavior that is technically
1862
- // correct, but until we PEP 728 is ratified and we have a way to express "extra items"
1863
- // and closed TypedDicts, we'll preserve the older (less correct) behavior to enable
1864
- // narrowing of TypedDicts based on checks for specific keys.
1865
- // TODO - remove this behavior once PEP 728 is accepted and the feature is no
1866
- // longer experimental.
1867
- if (!tdEntry) {
1868
- return undefined;
1869
- }
1870
- // if (!tdEntry) {
1871
- // // If there is no TD entry for this key and no "extra items" defined,
1872
- // // we have to assume that the TypedDict may contain extra items, so
1873
- // // narrowing it isn't possible in this case.
1874
- // return subtype;
1875
- // }
1876
- if ((0, types_1.isNever)(tdEntry.valueType)) {
1877
- // If the entry is typed as Never or the "extra items" is typed as Never,
1878
- // then this key cannot be present in the TypedDict, and we can eliminate it.
1879
- // A closed TypedDict has an "extra items" of Never, so this is what allows
1880
- // a key check to discriminate between closed TypedDicts.
1881
- return undefined;
1882
- }
1883
- // If the entry is currently not required and not marked provided, we can mark
1884
- // it as provided after this guard expression confirms it is.
1885
- if (tdEntry.isRequired || tdEntry.isProvided) {
1886
- return subtype;
1887
- }
1888
- const newNarrowedEntriesMap = new Map(subtype.priv.typedDictNarrowedEntries ?? []);
1889
- // Add the new entry.
1890
- newNarrowedEntriesMap.set(literalKey.priv.literalValue, {
1891
- valueType: tdEntry.valueType,
1892
- isReadOnly: tdEntry.isReadOnly,
1893
- isRequired: false,
1894
- isProvided: true,
1895
- });
1896
- // Clone the TypedDict object with the new entries.
1897
- return types_1.ClassType.cloneAsInstance(types_1.ClassType.cloneForNarrowedTypedDictEntries(types_1.ClassType.cloneAsInstantiable(subtype), newNarrowedEntriesMap));
1898
- }
1899
- else {
1900
- if (!knownEntry) {
1901
- // The key is not one of the known items, so it can be supplied
1902
- // only through an "extra items" entry, which says nothing about
1903
- // whether this particular key is present. Its absence is therefore
1904
- // consistent with the type, and the subtype cannot be eliminated.
1905
- return subtype;
1906
- }
1907
- return knownEntry.isRequired || knownEntry.isProvided ? undefined : subtype;
1908
- }
1909
- }
1910
- return subtype;
1911
- });
1912
- return narrowedType;
1141
+ return TypeEvaluatorNarrowing.narrowTypeForTypedDictKey(evaluator, referenceType, literalKey, isPositiveTest);
1913
1142
  }
1914
1143
  // Attempts to narrow a TypedDict type based on a comparison (equal or not
1915
1144
  // equal) between a discriminating entry type that has a declared literal
1916
1145
  // type to a literal value.
1917
1146
  function narrowTypeForDiscriminatedDictEntryComparison(evaluator, referenceType, indexLiteralType, literalType, isPositiveTest) {
1918
- let canNarrow = true;
1919
- const narrowedType = (0, typeUtils_1.mapSubtypes)(referenceType, (subtype) => {
1920
- if ((0, types_1.isClassInstance)(subtype) && types_1.ClassType.isTypedDictClass(subtype)) {
1921
- const symbolMap = (0, typedDicts_1.getTypedDictMembersForClass)(evaluator, subtype);
1922
- const tdEntry = symbolMap.knownItems.get(indexLiteralType.priv.literalValue);
1923
- if (tdEntry && (0, typeUtils_1.isLiteralTypeOrUnion)(tdEntry.valueType)) {
1924
- if (isPositiveTest) {
1925
- let foundMatch = false;
1926
- (0, typeUtils_1.doForEachSubtype)(literalType, (literalSubtype) => {
1927
- if (evaluator.assignType(tdEntry.valueType, literalSubtype)) {
1928
- foundMatch = true;
1929
- }
1930
- });
1931
- return foundMatch ? subtype : undefined;
1932
- }
1933
- else {
1934
- let foundNonMatch = false;
1935
- (0, typeUtils_1.doForEachSubtype)(literalType, (literalSubtype) => {
1936
- if (!evaluator.assignType(literalSubtype, tdEntry.valueType)) {
1937
- foundNonMatch = true;
1938
- }
1939
- });
1940
- return foundNonMatch ? subtype : undefined;
1941
- }
1942
- }
1943
- }
1944
- canNarrow = false;
1945
- return subtype;
1946
- });
1947
- return canNarrow ? narrowedType : referenceType;
1147
+ return TypeEvaluatorNarrowing.narrowTypeForDiscriminatedDictEntryComparison(evaluator, referenceType, indexLiteralType, literalType, isPositiveTest);
1948
1148
  }
1949
1149
  function narrowTypeForDiscriminatedTupleComparison(evaluator, referenceType, indexLiteralType, literalType, isPositiveTest) {
1950
- let canNarrow = true;
1951
- const narrowedType = (0, typeUtils_1.mapSubtypes)(referenceType, (subtype) => {
1952
- if ((0, types_1.isClassInstance)(subtype) &&
1953
- types_1.ClassType.isTupleClass(subtype) &&
1954
- !(0, typeUtils_1.isUnboundedTupleClass)(subtype) &&
1955
- typeof indexLiteralType.priv.literalValue === 'number' &&
1956
- (0, types_1.isClassInstance)(literalType)) {
1957
- const indexValue = indexLiteralType.priv.literalValue;
1958
- if (subtype.priv.tupleTypeArgs && indexValue >= 0 && indexValue < subtype.priv.tupleTypeArgs.length) {
1959
- const tupleEntryType = subtype.priv.tupleTypeArgs[indexValue]?.type;
1960
- if (tupleEntryType && (0, typeUtils_1.isLiteralTypeOrUnion)(tupleEntryType)) {
1961
- if (isPositiveTest) {
1962
- return evaluator.assignType(tupleEntryType, literalType) ? subtype : undefined;
1963
- }
1964
- else {
1965
- return evaluator.assignType(literalType, tupleEntryType) ? undefined : subtype;
1966
- }
1967
- }
1968
- }
1969
- }
1970
- canNarrow = false;
1971
- return subtype;
1972
- });
1973
- return canNarrow ? narrowedType : referenceType;
1150
+ return TypeEvaluatorNarrowing.narrowTypeForDiscriminatedTupleComparison(evaluator, referenceType, indexLiteralType, literalType, isPositiveTest);
1974
1151
  }
1975
1152
  // Attempts to narrow a type based on a comparison (equal or not equal)
1976
1153
  // between a discriminating field that has a declared literal type to a
1977
1154
  // literal value.
1978
1155
  function narrowTypeForDiscriminatedLiteralFieldComparison(evaluator, referenceType, memberName, literalType, isPositiveTest) {
1979
- const narrowedType = (0, typeUtils_1.mapSubtypes)(referenceType, (subtype) => {
1980
- let memberInfo;
1981
- if ((0, types_1.isClassInstance)(subtype)) {
1982
- memberInfo = (0, typeUtils_1.lookUpObjectMember)(subtype, memberName);
1983
- }
1984
- else if ((0, types_1.isInstantiableClass)(subtype)) {
1985
- memberInfo = (0, typeUtils_1.lookUpClassMember)(subtype, memberName);
1986
- }
1987
- if (memberInfo && memberInfo.isTypeDeclared) {
1988
- let memberType = evaluator.getTypeOfMember(memberInfo);
1989
- // Handle the case where the field is a property
1990
- // that has a declared literal return type for its getter.
1991
- if ((0, types_1.isClassInstance)(subtype) && (0, types_1.isClassInstance)(memberType) && (0, typeUtils_1.isProperty)(memberType)) {
1992
- const getterType = memberType.priv.fgetInfo?.methodType;
1993
- if (getterType && (0, types_1.isFunction)(getterType) && getterType.shared.declaredReturnType) {
1994
- const getterReturnType = types_1.FunctionType.getEffectiveReturnType(getterType);
1995
- if (getterReturnType) {
1996
- memberType = getterReturnType;
1997
- }
1998
- }
1999
- }
2000
- if ((0, typeUtils_1.isLiteralTypeOrUnion)(memberType, /* allowNone */ true)) {
2001
- if (isPositiveTest) {
2002
- return evaluator.assignType(memberType, literalType) ? subtype : undefined;
2003
- }
2004
- else {
2005
- return evaluator.assignType(literalType, memberType) ? undefined : subtype;
2006
- }
2007
- }
2008
- }
2009
- return subtype;
2010
- });
2011
- return narrowedType;
1156
+ return TypeEvaluatorNarrowing.narrowTypeForDiscriminatedLiteralFieldComparison(evaluator, referenceType, memberName, literalType, isPositiveTest);
2012
1157
  }
2013
1158
  // Attempts to narrow a type based on a comparison (equal or not equal)
2014
1159
  // between a discriminating field that has a declared None type to a
2015
1160
  // None.
2016
1161
  function narrowTypeForDiscriminatedFieldNoneComparison(evaluator, referenceType, memberName, isPositiveTest) {
2017
- return (0, typeUtils_1.mapSubtypes)(referenceType, (subtype) => {
2018
- let memberInfo;
2019
- if ((0, types_1.isClassInstance)(subtype)) {
2020
- memberInfo = (0, typeUtils_1.lookUpObjectMember)(subtype, memberName);
2021
- }
2022
- else if ((0, types_1.isInstantiableClass)(subtype)) {
2023
- memberInfo = (0, typeUtils_1.lookUpClassMember)(subtype, memberName);
2024
- }
2025
- if (memberInfo && memberInfo.isTypeDeclared) {
2026
- // Check the declared type before narrowing, since the member type
2027
- // below will be concretized and lose descriptor identity.
2028
- const declaredType = evaluator.getDeclaredTypeOfSymbol(memberInfo.symbol)?.type;
2029
- if (!declaredType) {
2030
- // isTypeDeclared is true but the type couldn't be resolved (e.g. an
2031
- // unresolvable stub). Conservatively skip narrowing rather than risk
2032
- // incorrectly eliminating a descriptor-typed member.
2033
- return subtype;
2034
- }
2035
- // Check if any subtype of the declared type is a descriptor or property.
2036
- // isMaybeDescriptorInstance handles declared types in instance form (ClassInstance),
2037
- // while isMaybeDescriptorClass handles declared types in instantiable form
2038
- // (InstantiableClass), which occurs when the annotation refers to the class object itself.
2039
- // This check applies to both positive and negative test paths: descriptor __get__
2040
- // return values don't reflect stored values regardless of test polarity.
2041
- const isDescriptorOrProperty = (0, typeUtils_1.someSubtypes)(declaredType, (declaredSubtype) => (0, typeUtils_1.isProperty)(declaredSubtype) ||
2042
- (0, typeUtils_1.isMaybeDescriptorInstance)(declaredSubtype) ||
2043
- (0, typeUtils_1.isMaybeDescriptorClass)(declaredSubtype));
2044
- if (isDescriptorOrProperty) {
2045
- return subtype;
2046
- }
2047
- const memberType = evaluator.makeTopLevelTypeVarsConcrete(evaluator.getTypeOfMember(memberInfo));
2048
- let canNarrow = true;
2049
- if (isPositiveTest) {
2050
- (0, typeUtils_1.doForEachSubtype)(memberType, (memberSubtype) => {
2051
- memberSubtype = evaluator.makeTopLevelTypeVarsConcrete(memberSubtype);
2052
- if ((0, types_1.isAnyOrUnknown)(memberSubtype) || (0, typeUtils_1.isNoneInstance)(memberSubtype) || (0, types_1.isNever)(memberSubtype)) {
2053
- canNarrow = false;
2054
- }
2055
- });
2056
- }
2057
- else {
2058
- canNarrow = (0, typeUtils_1.isNoneInstance)(memberType);
2059
- }
2060
- if (canNarrow) {
2061
- return undefined;
2062
- }
2063
- }
2064
- return subtype;
2065
- });
2066
- }
2067
- function isTypeCallBase(callType) {
2068
- return (0, types_1.isInstantiableClass)(callType) && types_1.ClassType.isBuiltIn(callType, 'type');
1162
+ return TypeEvaluatorNarrowing.narrowTypeForDiscriminatedFieldNoneComparison(evaluator, referenceType, memberName, isPositiveTest);
2069
1163
  }
2070
1164
  // Determines whether two classes have incompatible disjoint bases (PEP 800),
2071
1165
  // in which case no class can derive from both of them.
2072
- function areDisjointBasesIncompatible(classType1, classType2) {
2073
- const base1 = types_1.ClassType.getDisjointBase(classType1);
2074
- const base2 = types_1.ClassType.getDisjointBase(classType2);
2075
- return !!base1 && !!base2 && !types_1.ClassType.getMostDerivedDisjointBase([base1, base2]);
2076
- }
2077
1166
  // Fill gradual holes without replacing facts inferred from the guarded value.
2078
- function fillTypeGuardGradualArgs(input, hint, results, recursionCount = 0) {
2079
- if (!hint || input === hint || recursionCount > types_1.maxTypeRecursionCount) {
2080
- return input;
2081
- }
2082
- if ((0, types_1.isAnyOrUnknown)(input)) {
2083
- return (0, types_1.isAnyOrUnknown)(hint) ? ((0, types_1.isAny)(hint) ? hint : input) : hint;
2084
- }
2085
- if (!(0, types_1.isClass)(input) || !(0, types_1.isClass)(hint) || !types_1.ClassType.isSameGenericClass(input, hint)) {
2086
- return input;
2087
- }
2088
- let inputResults = results.get(input);
2089
- const cachedResult = inputResults?.get(hint);
2090
- if (cachedResult !== undefined) {
2091
- return cachedResult;
2092
- }
2093
- if (!inputResults) {
2094
- inputResults = new Map();
2095
- results.set(input, inputResults);
2096
- }
2097
- // Bound recursive aliases and reuse shared argument graphs within this narrowing operation.
2098
- inputResults.set(hint, input);
2099
- const inputTuple = input.priv.tupleTypeArgs;
2100
- const hintTuple = hint.priv.tupleTypeArgs;
2101
- if (inputTuple || hintTuple) {
2102
- if (!inputTuple ||
2103
- !hintTuple ||
2104
- inputTuple.length !== hintTuple.length ||
2105
- inputTuple.some((arg, index) => arg.isUnbounded !== hintTuple[index].isUnbounded ||
2106
- !!arg.isOptional !== !!hintTuple[index].isOptional)) {
2107
- return input;
2108
- }
2109
- const tupleArgs = inputTuple.map((arg, index) => ({
2110
- ...arg,
2111
- type: fillTypeGuardGradualArgs(arg.type, hintTuple[index].type, results, recursionCount + 1),
2112
- }));
2113
- const result = tupleArgs.every((arg, index) => arg.type === inputTuple[index].type)
2114
- ? input
2115
- : (0, typeUtils_1.specializeTupleClass)(input, tupleArgs, input.priv.isTypeArgExplicit, input.priv.isUnpacked);
2116
- inputResults.set(hint, result);
2117
- return result;
2118
- }
2119
- const inputArgs = input.priv.typeArgs;
2120
- const hintArgs = hint.priv.typeArgs;
2121
- if (!inputArgs || !hintArgs || inputArgs.length !== hintArgs.length) {
2122
- return input;
2123
- }
2124
- const typeArgs = inputArgs.map((arg, index) => fillTypeGuardGradualArgs(arg, hintArgs[index], results, recursionCount + 1));
2125
- const result = typeArgs.every((arg, index) => arg === inputArgs[index])
2126
- ? input
2127
- : types_1.ClassType.specialize(input, typeArgs, input.priv.isTypeArgExplicit);
2128
- inputResults.set(hint, result);
2129
- return result;
2130
- }
2131
1167
  // Attempts to narrow a type based on a "type(x) is y" or "type(x) is not y" check.
2132
1168
  function narrowTypeForTypeIs(evaluator, type, classTypes, isPositiveTest, isIsOperator, classTypeVar, allowLegacyEquality) {
2133
- const metaclassEqualityResults = isIsOperator ? undefined : new Map();
2134
- const hasEqualitySemantics = allowLegacyEquality
2135
- ? hasDeclaredIdentityEqualitySemantics
2136
- : hasIdentityEqualitySemantics;
2137
- if (metaclassEqualityResults &&
2138
- classTypes.some((classType) => !hasEqualitySemantics(classType, metaclassEqualityResults))) {
2139
- return type;
2140
- }
2141
- // We currently don't support narrowing in the negative direction
2142
- // when there are more than one class types.
2143
- if (!isPositiveTest && classTypes.length > 1) {
2144
- return type;
2145
- }
2146
- // Retain the TypeVar rather than its expanded constraints so assignments and loop joins preserve correlation.
2147
- if (isIsOperator && isPositiveTest && classTypeVar && (0, types_1.isAnyOrUnknown)(type)) {
2148
- return classTypeVar;
2149
- }
2150
- let gradualArgResults;
2151
- const typesToCombine = classTypes.map((classType) => {
2152
- return evaluator.mapSubtypesExpandTypeVars(type,
2153
- /* options */ undefined, (subtype, unexpandedSubtype) => {
2154
- if ((0, types_1.isClassInstance)(subtype)) {
2155
- const isLiteral = subtype.priv.literalValue !== undefined;
2156
- const instantiableSubtype = isLiteral
2157
- ? types_1.ClassType.cloneIncludeSubclasses(types_1.ClassType.cloneAsInstantiable(types_1.ClassType.cloneWithLiteral(subtype, undefined)),
2158
- /* includeSubclasses */ false)
2159
- : types_1.ClassType.cloneAsInstantiable(subtype);
2160
- if (metaclassEqualityResults &&
2161
- !hasEqualitySemantics(instantiableSubtype, metaclassEqualityResults)) {
2162
- return unexpandedSubtype;
2163
- }
2164
- const matches = types_1.ClassType.isDerivedFrom(classType, instantiableSubtype);
2165
- // A literal has an exact runtime class, not an arbitrary subclass.
2166
- if (matches && isLiteral && !types_1.ClassType.isSameGenericClass(instantiableSubtype, classType)) {
2167
- return isPositiveTest ? undefined : subtype;
2168
- }
2169
- if (isPositiveTest) {
2170
- if (matches) {
2171
- if (types_1.ClassType.isSameGenericClass(instantiableSubtype, classType)) {
2172
- return (0, typeUtils_1.addConditionToType)(subtype, (0, typeUtils_1.getTypeCondition)(classType));
2173
- }
2174
- let narrowedClass = classType;
2175
- if (subtype.priv.typeArgs || subtype.priv.tupleTypeArgs) {
2176
- if (classType.shared.typeParams.length > 0) {
2177
- if ((0, typeUtils_1.containsAnyRecursive)(classType) ||
2178
- !evaluator.assignType(subtype, types_1.ClassType.cloneAsInstance(classType))) {
2179
- const genericClass = (0, typeUtils_1.selfSpecializeClass)(types_1.ClassType.specialize(classType, undefined));
2180
- const constraints = new constraintTracker_1.ConstraintTracker();
2181
- if (!evaluator.assignType(subtype, types_1.ClassType.cloneAsInstance(genericClass), undefined, constraints, 2 /* AssignTypeFlags.Contravariant */ |
2182
- 2048 /* AssignTypeFlags.PopulateExpectedType */ |
2183
- 4096 /* AssignTypeFlags.SkipPopulateUnknownExpectedType */)) {
2184
- return (0, typeUtils_1.addConditionToType)(subtype, (0, typeUtils_1.getTypeCondition)(classType));
2185
- }
2186
- const specializedClass = evaluator.solveAndApplyConstraints(genericClass, constraints, {
2187
- replaceUnsolved: {
2188
- scopeIds: (0, typeUtils_1.getTypeVarScopeIds)(classType),
2189
- useUnknown: true,
2190
- tupleClassType: evaluator.getTupleClassType(),
2191
- },
2192
- });
2193
- if (!(0, types_1.isInstantiableClass)(specializedClass)) {
2194
- return (0, typeUtils_1.addConditionToType)(subtype, (0, typeUtils_1.getTypeCondition)(classType));
2195
- }
2196
- const results = (gradualArgResults ?? (gradualArgResults = new Map()));
2197
- narrowedClass = types_1.ClassType.specialize(specializedClass, specializedClass.priv.typeArgs?.map((arg, index) => fillTypeGuardGradualArgs(arg, classType.priv.typeArgs?.[index], results)));
2198
- }
2199
- }
2200
- else if (!evaluator.assignType(subtype, types_1.ClassType.cloneAsInstance(classType))) {
2201
- return classType.priv.includeSubclasses && !types_1.ClassType.isFinal(classType)
2202
- ? (0, typeUtils_1.addConditionToType)(subtype, (0, typeUtils_1.getTypeCondition)(classType))
2203
- : undefined;
2204
- }
2205
- }
2206
- return (0, typeUtils_1.addConditionToType)(types_1.ClassType.cloneAsInstance(narrowedClass), subtype.props?.condition);
2207
- }
2208
- if (!classType.priv.includeSubclasses) {
2209
- if (evaluator.assignType(subtype, types_1.ClassType.cloneAsInstance(classType))) {
2210
- return (0, typeUtils_1.addConditionToType)(types_1.ClassType.cloneAsInstance(classType), (0, typeUtils_1.getTypeCondition)(subtype));
2211
- }
2212
- return undefined;
2213
- }
2214
- // A subclass of both the subtype and the class could satisfy the
2215
- // comparison, so eliminate the subtype only if no such class can exist.
2216
- if ((types_1.ClassType.isFinal(classType) ||
2217
- isLiteral ||
2218
- types_1.ClassType.isFinal(instantiableSubtype) ||
2219
- areDisjointBasesIncompatible(instantiableSubtype, classType)) &&
2220
- !evaluator.assignType(types_1.ClassType.cloneAsInstance(classType), subtype) &&
2221
- !evaluator.assignType(subtype, types_1.ClassType.cloneAsInstance(classType))) {
2222
- return undefined;
2223
- }
2224
- if (!(0, types_1.isTypeVar)(unexpandedSubtype) || !types_1.TypeVarType.isSelf(unexpandedSubtype)) {
2225
- return (0, typeUtils_1.addConditionToType)(subtype, classType.props?.condition);
2226
- }
2227
- }
2228
- // A final class has no subclasses, so it is treated as an exact class.
2229
- if (!classType.priv.includeSubclasses || types_1.ClassType.isFinal(classType)) {
2230
- // If the runtime class is exact and it matches, then
2231
- // we can eliminate it in the negative case.
2232
- if (matches && (isLiteral || types_1.ClassType.isFinal(instantiableSubtype))) {
2233
- return undefined;
2234
- }
2235
- // We can't eliminate the subtype in the negative
2236
- // case because it could be a subclass of the type,
2237
- // in which case `type(x) is y` would fail.
2238
- return subtype;
2239
- }
2240
- }
2241
- if ((0, types_1.isAnyOrUnknown)(subtype)) {
2242
- if (metaclassEqualityResults && !allowLegacyEquality) {
2243
- return subtype;
2244
- }
2245
- return isPositiveTest
2246
- ? types_1.ClassType.cloneAsInstance((0, typeUtils_1.addConditionToType)(classType, (0, typeUtils_1.getTypeCondition)(subtype)))
2247
- : subtype;
2248
- }
2249
- return unexpandedSubtype;
2250
- });
2251
- });
2252
- return (0, types_1.combineTypes)(typesToCombine);
2253
- }
2254
- function hasUnresolvedMetaclassInMro(classType) {
2255
- return classType.shared.mro.some((baseClass) => !(0, types_1.isInstantiableClass)(baseClass) || !!(baseClass.shared.flags & 134217728 /* ClassTypeFlags.HasUnresolvedMetaclass */));
1169
+ return TypeEvaluatorNarrowing.narrowTypeForTypeIs(evaluator, type, classTypes, isPositiveTest, isIsOperator, classTypeVar, allowLegacyEquality);
2256
1170
  }
2257
1171
  // Determines whether an equality comparison ("==" or "!=") involving the
2258
1172
  // specified class object is guaranteed to have identity semantics. This
2259
1173
  // requires that the class is statically closed (it cannot be a subclass
2260
1174
  // with a different metaclass), is not generic (specialized aliases compare
2261
1175
  // by value), and its metaclass does not override "__eq__" or "__ne__".
2262
- function hasIdentityEqualitySemantics(classType, metaclassEqualityResults) {
2263
- if (classType.priv.includeSubclasses && !types_1.ClassType.isFinal(classType)) {
2264
- return false;
2265
- }
2266
- if (classType.shared.typeParams.length > 0) {
2267
- return false;
2268
- }
2269
- return hasDeclaredIdentityEqualitySemantics(classType, metaclassEqualityResults);
2270
- }
2271
1176
  // Legacy forward type(x) equality guards check declared metaclasses without closing subclass hierarchies.
2272
- function hasDeclaredIdentityEqualitySemantics(classType, metaclassEqualityResults) {
2273
- const metaclass = classType.shared.effectiveMetaclass;
2274
- if (!metaclass ||
2275
- !(0, types_1.isInstantiableClass)(metaclass) ||
2276
- hasUnresolvedMetaclassInMro(classType) ||
2277
- hasUnresolvedMetaclassInMro(metaclass)) {
2278
- return false;
2279
- }
2280
- if (types_1.ClassType.isBuiltIn(metaclass, 'type')) {
2281
- return true;
2282
- }
2283
- // Cloned metaclasses share method definitions, but the class-specific checks above cannot be shared.
2284
- const cachedResult = metaclassEqualityResults.get(metaclass.shared);
2285
- if (cachedResult !== undefined) {
2286
- return cachedResult;
2287
- }
2288
- const result = !(0, operations_1.customMetaclassSupportsMethod)(classType, '__eq__') && !(0, operations_1.customMetaclassSupportsMethod)(classType, '__ne__');
2289
- metaclassEqualityResults.set(metaclass.shared, result);
2290
- return result;
2291
- }
2292
1177
  // Attempts to narrow a type based on a comparison with a class using "is" or
2293
1178
  // "is not", or "==" or "!=".
2294
1179
  function narrowTypeForClassComparison(evaluator, referenceType, classType, isPositiveTest, isIsOperator = true) {
2295
- // Limit reuse to this invocation so results cannot outlive the evaluation that established them.
2296
- const metaclassEqualityResults = isIsOperator ? undefined : new Map();
2297
- if (metaclassEqualityResults && !hasIdentityEqualitySemantics(classType, metaclassEqualityResults)) {
2298
- return referenceType;
2299
- }
2300
- return (0, typeUtils_1.mapSubtypes)(referenceType, (subtype) => {
2301
- // Multiple class constraints expand to a union with distinct conditions, which equality cannot narrow.
2302
- if (metaclassEqualityResults &&
2303
- (0, types_1.isTypeVar)(subtype) &&
2304
- !subtype.shared.recursiveAlias &&
2305
- subtype.shared.constraints.length > 1 &&
2306
- subtype.shared.constraints.every(types_1.isClassInstance)) {
2307
- return subtype;
2308
- }
2309
- let concreteSubtype = evaluator.makeTopLevelTypeVarsConcrete(subtype);
2310
- if (metaclassEqualityResults &&
2311
- (!(0, types_1.isInstantiableClass)(concreteSubtype) ||
2312
- !hasIdentityEqualitySemantics(concreteSubtype, metaclassEqualityResults))) {
2313
- return subtype;
2314
- }
2315
- if (isPositiveTest) {
2316
- if ((0, types_1.isClassInstance)(concreteSubtype) &&
2317
- types_1.TypeBase.isInstance(subtype) &&
2318
- types_1.ClassType.isBuiltIn(concreteSubtype, 'type')) {
2319
- concreteSubtype =
2320
- concreteSubtype.priv.typeArgs && concreteSubtype.priv.typeArgs.length > 0
2321
- ? (0, typeUtils_1.convertToInstantiable)(concreteSubtype.priv.typeArgs[0])
2322
- : types_1.UnknownType.create();
2323
- }
2324
- if ((0, types_1.isAnyOrUnknown)(concreteSubtype)) {
2325
- return (0, typeUtils_1.addConditionToType)(classType, (0, typeUtils_1.getTypeCondition)(concreteSubtype));
2326
- }
2327
- if ((0, types_1.isClass)(concreteSubtype)) {
2328
- if (types_1.TypeBase.isInstance(concreteSubtype)) {
2329
- if (!isIsOperator) {
2330
- return subtype;
2331
- }
2332
- return types_1.ClassType.isBuiltIn(concreteSubtype, 'object') ? classType : undefined;
2333
- }
2334
- const isSuperType = isFilterSuperclass(subtype, concreteSubtype, classType, classType);
2335
- if (!classType.priv.includeSubclasses) {
2336
- // Handle the case where the LHS and RHS operands are specific
2337
- // classes, as opposed to types that represent classes and their
2338
- // subclasses.
2339
- if (!concreteSubtype.priv.includeSubclasses) {
2340
- return types_1.ClassType.isSameGenericClass(concreteSubtype, classType) ? classType : undefined;
2341
- }
2342
- if (isSuperType) {
2343
- return (0, typeUtils_1.addConditionToType)(classType, (0, typeUtils_1.getTypeCondition)(concreteSubtype));
2344
- }
2345
- const isSubType = types_1.ClassType.isDerivedFrom(classType, concreteSubtype);
2346
- if (isSubType) {
2347
- return (0, typeUtils_1.addConditionToType)(classType, (0, typeUtils_1.getTypeCondition)(concreteSubtype));
2348
- }
2349
- return undefined;
2350
- }
2351
- if (types_1.ClassType.isFinal(concreteSubtype) && !isSuperType) {
2352
- return undefined;
2353
- }
2354
- }
2355
- }
2356
- else {
2357
- if ((0, types_1.isInstantiableClass)(concreteSubtype) &&
2358
- types_1.ClassType.isSameGenericClass(classType, concreteSubtype) &&
2359
- types_1.ClassType.isFinal(classType)) {
2360
- return undefined;
2361
- }
2362
- }
2363
- return subtype;
2364
- });
2365
- }
2366
- function isFilterSuperclass(varType, concreteVarType, filterType, concreteFilterType) {
2367
- if ((0, types_1.isTypeVar)(filterType) || concreteFilterType.priv.literalValue !== undefined) {
2368
- return (0, types_1.isTypeSame)((0, typeUtils_1.convertToInstance)(filterType), varType);
2369
- }
2370
- // If the filter type represents all possible subclasses
2371
- // of a type, we can't make any statements about its superclass
2372
- // relationship with concreteVarType.
2373
- if (concreteFilterType.priv.includeSubclasses) {
2374
- return false;
2375
- }
2376
- if (types_1.ClassType.isDerivedFrom(concreteVarType, concreteFilterType)) {
2377
- return true;
2378
- }
2379
- // Handle the special case where the variable type is a TypedDict and
2380
- // we're filtering against 'dict'. TypedDict isn't derived from dict,
2381
- // but at runtime, isinstance returns True.
2382
- if (types_1.ClassType.isBuiltIn(concreteFilterType, 'dict') && types_1.ClassType.isTypedDictClass(concreteVarType)) {
2383
- return true;
2384
- }
2385
- return false;
1180
+ return TypeEvaluatorNarrowing.narrowTypeForClassComparison(evaluator, referenceType, classType, isPositiveTest, isIsOperator);
2386
1181
  }
2387
1182
  // Determines whether the specified class is an enum class whose members derive
2388
1183
  // from a primitive type (e.g. IntEnum or StrEnum). Members of such classes
2389
1184
  // compare equal to their underlying primitive value at runtime.
2390
- function isPrimitiveBackedEnumClass(classType) {
2391
- if (!types_1.ClassType.isEnumClass(classType)) {
2392
- return false;
2393
- }
2394
- return (0, typeUtils_1.derivesFromStdlibClass)(classType, 'int') || (0, typeUtils_1.derivesFromStdlibClass)(classType, 'str');
2395
- }
2396
1185
  // Determines whether enumType is a primitive-backed enum class and primitiveType
2397
1186
  // is the primitive type (int or str) from which it derives.
2398
- function isPrimitiveBackedEnumAndPrimitive(enumType, primitiveType) {
2399
- if (types_1.ClassType.isEnumClass(primitiveType) || !isPrimitiveBackedEnumClass(enumType)) {
2400
- return false;
2401
- }
2402
- // Exclude bool, which derives from int but whose literal values are distinct
2403
- // from the corresponding int literals.
2404
- if (types_1.ClassType.isBuiltIn(primitiveType, 'int')) {
2405
- return (0, typeUtils_1.derivesFromStdlibClass)(enumType, 'int');
2406
- }
2407
- if (types_1.ClassType.isBuiltIn(primitiveType, 'str')) {
2408
- return (0, typeUtils_1.derivesFromStdlibClass)(enumType, 'str');
2409
- }
2410
- return false;
2411
- }
2412
1187
  // If the specified type is a literal member of a primitive-backed enum class,
2413
1188
  // returns the corresponding primitive literal type. Otherwise returns the type
2414
1189
  // unmodified.
2415
- function unwrapPrimitiveBackedEnumLiteral(classType) {
2416
- const literalValue = classType.priv.literalValue;
2417
- if (!(literalValue instanceof types_1.EnumLiteral) || !isPrimitiveBackedEnumClass(classType)) {
2418
- return classType;
2419
- }
2420
- const itemType = literalValue.itemType;
2421
- if ((0, types_1.isClassInstance)(itemType) && itemType.priv.literalValue !== undefined) {
2422
- return itemType;
2423
- }
2424
- return classType;
2425
- }
2426
1190
  // Determines whether two literal types compare equal using the `==` operator at
2427
1191
  // runtime. This differs from ClassType.isLiteralValueSame only for members of
2428
1192
  // primitive-backed enum classes, which compare equal to their underlying
2429
1193
  // primitive values.
2430
- function isLiteralValueEqualAtRuntime(type1, type2) {
2431
- return types_1.ClassType.isLiteralValueSame(unwrapPrimitiveBackedEnumLiteral(type1), unwrapPrimitiveBackedEnumLiteral(type2));
2432
- }
2433
1194
  // Attempts to narrow a type (make it more constrained) based on a comparison
2434
1195
  // (equal or not equal) to a literal value. It also handles "is" or "is not"
2435
1196
  // operators if isIsOperator is true.
2436
1197
  function narrowTypeForLiteralComparison(evaluator, referenceType, literalType, isPositiveTest, isIsOperator) {
2437
- return evaluator.mapSubtypesExpandTypeVars(referenceType, /* options */ undefined, (subtype) => {
2438
- subtype = evaluator.makeTopLevelTypeVarsConcrete(subtype);
2439
- if ((0, types_1.isAnyOrUnknown)(subtype)) {
2440
- if (isPositiveTest) {
2441
- return literalType;
2442
- }
2443
- return subtype;
2444
- }
2445
- // Determine whether this is a comparison between a primitive-backed enum
2446
- // (e.g. IntEnum or StrEnum) and its underlying primitive type. Such a
2447
- // comparison can be used for narrowing because the enum member compares
2448
- // equal to its underlying primitive value at runtime.
2449
- const isPrimitiveBackedEnumComparison = !isIsOperator &&
2450
- (0, types_1.isClassInstance)(subtype) &&
2451
- (isPrimitiveBackedEnumAndPrimitive(subtype, literalType) ||
2452
- isPrimitiveBackedEnumAndPrimitive(literalType, subtype));
2453
- if ((0, types_1.isClassInstance)(subtype) &&
2454
- (types_1.ClassType.isSameGenericClass(literalType, subtype) || isPrimitiveBackedEnumComparison)) {
2455
- if (subtype.priv.literalValue !== undefined) {
2456
- const literalValueMatches = isPrimitiveBackedEnumComparison
2457
- ? isLiteralValueEqualAtRuntime(subtype, literalType)
2458
- : types_1.ClassType.isLiteralValueSame(subtype, literalType);
2459
- if (isPositiveTest) {
2460
- return literalValueMatches ? subtype : undefined;
2461
- }
2462
- const isSingleton = types_1.ClassType.isEnumClass(literalType) ||
2463
- (0, typeUtils_1.isSentinelLiteral)(subtype) ||
2464
- types_1.ClassType.isBuiltIn(literalType, 'bool');
2465
- // For negative tests, we can eliminate the literal value if it doesn't match,
2466
- // but only for equality tests or for 'is' tests that involve enums, bools, or sentinels.
2467
- return literalValueMatches && (isSingleton || !isIsOperator) ? undefined : subtype;
2468
- }
2469
- if (isPositiveTest) {
2470
- if (isPrimitiveBackedEnumComparison) {
2471
- // If the reference type is the enum, attempt to find the member
2472
- // whose value matches the primitive literal. If no member can be
2473
- // identified (e.g. for an IntFlag class, whose members cannot be
2474
- // enumerated), retain the reference type rather than narrowing it
2475
- // to the primitive literal type.
2476
- if (types_1.ClassType.isEnumClass(subtype)) {
2477
- const allLiteralTypes = enumerateLiteralsForType(evaluator, subtype);
2478
- const match = allLiteralTypes?.find((type) => isLiteralValueEqualAtRuntime(type, literalType));
2479
- return match ?? subtype;
2480
- }
2481
- // The reference type is the primitive type, so narrowing it to the
2482
- // enum literal would lose its (potentially derived) class. Retain
2483
- // the reference type instead.
2484
- return subtype;
2485
- }
2486
- return literalType;
2487
- }
2488
- // If we're able to enumerate all possible literal values
2489
- // (for bool or enum), we can eliminate all others in a negative test.
2490
- const allLiteralTypes = enumerateLiteralsForType(evaluator, subtype);
2491
- if (allLiteralTypes && allLiteralTypes.length > 0) {
2492
- return (0, types_1.combineTypes)(allLiteralTypes.filter((type) => isPrimitiveBackedEnumComparison
2493
- ? !isLiteralValueEqualAtRuntime(type, literalType)
2494
- : !types_1.ClassType.isLiteralValueSame(type, literalType)));
2495
- }
2496
- return subtype;
2497
- }
2498
- if (isPositiveTest) {
2499
- if ((0, types_1.isClassInstance)(subtype) && types_1.ClassType.isBuiltIn(subtype, 'LiteralString')) {
2500
- return literalType;
2501
- }
2502
- if (isIsOperator || (0, typeUtils_1.isNoneInstance)(subtype)) {
2503
- const compareType = getInnermostNewTypeBaseInstance(subtype) ?? subtype;
2504
- const isSubtype = evaluator.assignType(compareType, literalType);
2505
- if (isSubtype) {
2506
- return (0, types_1.isClassInstance)(subtype) && types_1.ClassType.isNewTypeClass(subtype) ? subtype : literalType;
2507
- }
2508
- return undefined;
2509
- }
2510
- }
2511
- return subtype;
2512
- });
1198
+ return TypeEvaluatorNarrowing.narrowTypeForLiteralComparison(evaluator, referenceType, literalType, isPositiveTest, isIsOperator);
2513
1199
  }
2514
1200
  function enumerateLiteralsForType(evaluator, type) {
2515
- if (types_1.ClassType.isBuiltIn(type, 'bool')) {
2516
- // Booleans have only two types: True and False.
2517
- return [
2518
- types_1.ClassType.cloneWithLiteral(type, /* value */ true),
2519
- types_1.ClassType.cloneWithLiteral(type, /* value */ false),
2520
- ];
2521
- }
2522
- if (types_1.ClassType.isEnumClass(type)) {
2523
- // Enum expansion doesn't apply if the member set cannot be statically
2524
- // enumerated or if the class derives from enum.Flag.
2525
- if (types_1.ClassType.isEnumMemberSetMayBeDynamicallyModified(type) ||
2526
- type.shared.mro.some((mroClass) => (0, types_1.isClass)(mroClass) && types_1.ClassType.isBuiltIn(mroClass, 'Flag'))) {
2527
- return undefined;
2528
- }
2529
- // Enumerate all of the values in this enumeration.
2530
- const enumList = [];
2531
- const fields = types_1.ClassType.getSymbolTable(type);
2532
- fields.forEach((symbol, name) => {
2533
- if (!symbol.isIgnoredForProtocolMatch()) {
2534
- let symbolType = evaluator.getEffectiveTypeOfSymbol(symbol);
2535
- symbolType = (0, enums_1.transformTypeForEnumMember)(evaluator, type, name, (0, analyzerNodeInfo_1.getInfoReader)(evaluator)) ?? symbolType;
2536
- if ((0, types_1.isClassInstance)(symbolType) &&
2537
- types_1.ClassType.isSameGenericClass(type, symbolType) &&
2538
- symbolType.priv.literalValue !== undefined) {
2539
- enumList.push(symbolType);
2540
- }
2541
- }
2542
- });
2543
- return enumList;
2544
- }
2545
- return undefined;
1201
+ return TypeEvaluatorNarrowing.enumerateLiteralsForType(evaluator, type);
2546
1202
  }
2547
1203
  // Determines whether the expression name node is in the same scope or
2548
1204
  // an outer scope from the reference name node. This allows isMatchingExpression
2549
1205
  // to determine whether two name nodes are referring to the same symbol.
2550
1206
  function isNameSameScope(evaluator, reference, expression) {
2551
- const refSymbol = evaluator.lookUpSymbolRecursive(reference, reference.d.value, /* honorCodeFlow */ false);
2552
- const exprSymbol = evaluator.lookUpSymbolRecursive(expression, expression.d.value, /* honorCodeFlow */ false);
2553
- if (!refSymbol || !exprSymbol) {
2554
- // This shouldn't happen, but just to be safe...
2555
- return true;
2556
- }
2557
- const refScope = refSymbol.scope;
2558
- const exprScope = exprSymbol.scope;
2559
- if (refScope === exprScope) {
2560
- return true;
2561
- }
2562
- return (0, scopeUtils_1.isScopeContainedWithin)(refScope, exprScope);
1207
+ return TypeEvaluatorNarrowing.isNameSameScope(evaluator, reference, expression);
2563
1208
  }
2564
1209
  // Matches a reference against an expression, including the RHS of an assignment
2565
1210
  // expression. This keeps walrus narrowing consistent with truthiness handling in