@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.
- package/dist/analyzer/binder.js +9 -3
- package/dist/analyzer/binder.js.map +1 -1
- package/dist/analyzer/constructorTransform.js +12 -0
- package/dist/analyzer/constructorTransform.js.map +1 -1
- package/dist/analyzer/constructors.d.ts +1 -1
- package/dist/analyzer/constructors.js +1 -1
- package/dist/analyzer/constructors.js.map +1 -1
- package/dist/analyzer/decorators.d.ts +1 -1
- package/dist/analyzer/decorators.js.map +1 -1
- package/dist/analyzer/functionTransform.d.ts +5 -0
- package/dist/analyzer/functionTransform.js +6 -3
- package/dist/analyzer/functionTransform.js.map +1 -1
- package/dist/analyzer/missingBindingMetadata.d.ts +6 -0
- package/dist/analyzer/missingBindingMetadata.js +22 -0
- package/dist/analyzer/missingBindingMetadata.js.map +1 -0
- package/dist/analyzer/namedTuples.d.ts +4 -1
- package/dist/analyzer/namedTuples.js +1 -0
- package/dist/analyzer/namedTuples.js.map +1 -1
- package/dist/analyzer/patternMatching.d.ts +5 -1
- package/dist/analyzer/patternMatching.js +3 -0
- package/dist/analyzer/patternMatching.js.map +1 -1
- package/dist/analyzer/program.d.ts +3 -0
- package/dist/analyzer/program.js +71 -1
- package/dist/analyzer/program.js.map +1 -1
- package/dist/analyzer/tuples.d.ts +1 -1
- package/dist/analyzer/tuples.js.map +1 -1
- package/dist/analyzer/typeCacheUtils.d.ts +1 -0
- package/dist/analyzer/typeCacheUtils.js +10 -0
- package/dist/analyzer/typeCacheUtils.js.map +1 -1
- package/dist/analyzer/typeEvaluator/boundMethodSpecialization.d.ts +15 -0
- package/dist/analyzer/typeEvaluator/boundMethodSpecialization.js +98 -0
- package/dist/analyzer/typeEvaluator/boundMethodSpecialization.js.map +1 -0
- package/dist/analyzer/typeEvaluator/classConstruction.d.ts +25 -0
- package/dist/analyzer/typeEvaluator/classConstruction.js +251 -0
- package/dist/analyzer/typeEvaluator/classConstruction.js.map +1 -0
- package/dist/analyzer/typeEvaluator/collectionInference.d.ts +41 -0
- package/dist/analyzer/typeEvaluator/collectionInference.js +695 -0
- package/dist/analyzer/typeEvaluator/collectionInference.js.map +1 -0
- package/dist/analyzer/typeEvaluator/diagnostics.d.ts +33 -0
- package/dist/analyzer/typeEvaluator/diagnostics.js +198 -0
- package/dist/analyzer/typeEvaluator/diagnostics.js.map +1 -0
- package/dist/analyzer/typeEvaluator/evaluatorCore.d.ts +80 -0
- package/dist/analyzer/typeEvaluator/evaluatorCore.js +1126 -0
- package/dist/analyzer/typeEvaluator/evaluatorCore.js.map +1 -0
- package/dist/analyzer/typeEvaluator/evaluatorNarrowing.d.ts +17 -0
- package/dist/analyzer/typeEvaluator/evaluatorNarrowing.js +370 -0
- package/dist/analyzer/typeEvaluator/evaluatorNarrowing.js.map +1 -0
- package/dist/analyzer/typeEvaluator/expressionEvaluation.d.ts +81 -0
- package/dist/analyzer/typeEvaluator/expressionEvaluation.js +1802 -0
- package/dist/analyzer/typeEvaluator/expressionEvaluation.js.map +1 -0
- package/dist/analyzer/typeEvaluator/flowAnalysis.d.ts +33 -0
- package/dist/analyzer/typeEvaluator/flowAnalysis.js +217 -0
- package/dist/analyzer/typeEvaluator/flowAnalysis.js.map +1 -0
- package/dist/analyzer/typeEvaluator/internals.d.ts +137 -0
- package/dist/analyzer/typeEvaluator/internals.js +10 -0
- package/dist/analyzer/typeEvaluator/internals.js.map +1 -0
- package/dist/analyzer/typeEvaluator/literalTypes.d.ts +3 -0
- package/dist/analyzer/typeEvaluator/literalTypes.js +52 -0
- package/dist/analyzer/typeEvaluator/literalTypes.js.map +1 -0
- package/dist/analyzer/typeEvaluator/memberResolution.d.ts +79 -0
- package/dist/analyzer/typeEvaluator/memberResolution.js +1007 -0
- package/dist/analyzer/typeEvaluator/memberResolution.js.map +1 -0
- package/dist/analyzer/typeEvaluator/overrideValidation.d.ts +15 -0
- package/dist/analyzer/typeEvaluator/overrideValidation.js +422 -0
- package/dist/analyzer/typeEvaluator/overrideValidation.js.map +1 -0
- package/dist/analyzer/typeEvaluator/returnTypeInference.d.ts +33 -0
- package/dist/analyzer/typeEvaluator/returnTypeInference.js +226 -0
- package/dist/analyzer/typeEvaluator/returnTypeInference.js.map +1 -0
- package/dist/analyzer/typeEvaluator/specialFormCreation.d.ts +39 -0
- package/dist/analyzer/typeEvaluator/specialFormCreation.js +1728 -0
- package/dist/analyzer/typeEvaluator/specialFormCreation.js.map +1 -0
- package/dist/analyzer/typeEvaluator/tupleUnpacking.d.ts +2 -0
- package/dist/analyzer/typeEvaluator/tupleUnpacking.js +36 -0
- package/dist/analyzer/typeEvaluator/tupleUnpacking.js.map +1 -0
- package/dist/analyzer/typeEvaluator/typeCompatibility.d.ts +36 -0
- package/dist/analyzer/typeEvaluator/typeCompatibility.js +1320 -0
- package/dist/analyzer/typeEvaluator/typeCompatibility.js.map +1 -0
- package/dist/analyzer/typeEvaluator/typeGuardNarrowing.d.ts +186 -0
- package/dist/analyzer/typeEvaluator/typeGuardNarrowing.js +1683 -0
- package/dist/analyzer/typeEvaluator/typeGuardNarrowing.js.map +1 -0
- package/dist/analyzer/typeEvaluator/typeVarHandling.d.ts +48 -0
- package/dist/analyzer/typeEvaluator/typeVarHandling.js +756 -0
- package/dist/analyzer/typeEvaluator/typeVarHandling.js.map +1 -0
- package/dist/analyzer/typeEvaluator.d.ts +3 -31
- package/dist/analyzer/typeEvaluator.js +5033 -13761
- package/dist/analyzer/typeEvaluator.js.map +1 -1
- package/dist/analyzer/typeGuards.d.ts +2 -2
- package/dist/analyzer/typeGuards.js +212 -1567
- package/dist/analyzer/typeGuards.js.map +1 -1
- package/dist/analyzer/typeUtils.js +6 -2
- package/dist/analyzer/typeUtils.js.map +1 -1
- package/dist/analyzer/typedDicts.js +111 -0
- package/dist/analyzer/typedDicts.js.map +1 -1
- package/dist/browser/browserConnection.d.ts +12 -0
- package/dist/browser/browserConnection.js +23 -0
- package/dist/browser/browserConnection.js.map +1 -0
- package/dist/browser/browserFileSystem.d.ts +73 -0
- package/dist/browser/browserFileSystem.js +578 -0
- package/dist/browser/browserFileSystem.js.map +1 -0
- package/dist/browser/browserHost.d.ts +4 -0
- package/dist/browser/browserHost.js +10 -0
- package/dist/browser/browserHost.js.map +1 -0
- package/dist/browser/browserProcess.d.ts +1 -0
- package/dist/browser/browserProcess.js +17 -0
- package/dist/browser/browserProcess.js.map +1 -0
- package/dist/browser/browserServer.d.ts +8 -0
- package/dist/browser/browserServer.js +32 -0
- package/dist/browser/browserServer.js.map +1 -0
- package/dist/browser/cancellation/common.d.ts +10 -0
- package/dist/browser/cancellation/common.js +114 -0
- package/dist/browser/cancellation/common.js.map +1 -0
- package/dist/browser/cancellation/objectURLReceiverStrategy.d.ts +5 -0
- package/dist/browser/cancellation/objectURLReceiverStrategy.js +37 -0
- package/dist/browser/cancellation/objectURLReceiverStrategy.js.map +1 -0
- package/dist/browser/cancellation/objectURLSenderStrategy.d.ts +7 -0
- package/dist/browser/cancellation/objectURLSenderStrategy.js +32 -0
- package/dist/browser/cancellation/objectURLSenderStrategy.js.map +1 -0
- package/dist/browser/worker.d.ts +1 -0
- package/dist/browser/worker.js +33 -0
- package/dist/browser/worker.js.map +1 -0
- package/dist/pyrightServer.d.ts +34 -0
- package/dist/pyrightServer.js +239 -0
- package/dist/pyrightServer.js.map +1 -0
- package/dist/server.d.ts +3 -23
- package/dist/server.js +12 -232
- package/dist/server.js.map +1 -1
- package/dist/tests/benchmarks/typeInferenceComparison.d.ts +1 -0
- package/dist/tests/benchmarks/typeInferenceComparison.js +1235 -0
- package/dist/tests/benchmarks/typeInferenceComparison.js.map +1 -0
- package/dist/tests/benchmarks/typeInferencePyrightEntry.d.ts +1 -0
- package/dist/tests/benchmarks/typeInferencePyrightEntry.js +53 -0
- package/dist/tests/benchmarks/typeInferencePyrightEntry.js.map +1 -0
- package/dist/tests/browserCancellation.test.d.ts +1 -0
- package/dist/tests/browserCancellation.test.js +63 -0
- package/dist/tests/browserCancellation.test.js.map +1 -0
- package/dist/tests/browserServer.test.d.ts +1 -0
- package/dist/tests/browserServer.test.js +177 -0
- package/dist/tests/browserServer.test.js.map +1 -0
- package/dist/tests/constraintSolver.test.js +45 -0
- package/dist/tests/constraintSolver.test.js.map +1 -1
- package/dist/tests/mypyPrimerAnalysis.test.js +15 -1
- package/dist/tests/mypyPrimerAnalysis.test.js.map +1 -1
- package/dist/tests/typeCacheUtils.test.js +46 -0
- package/dist/tests/typeCacheUtils.test.js.map +1 -1
- package/dist/tests/typeEvaluator1.test.js +75 -0
- package/dist/tests/typeEvaluator1.test.js.map +1 -1
- package/dist/tests/typeEvaluator3.test.js +4 -0
- package/dist/tests/typeEvaluator3.test.js.map +1 -1
- package/dist/tests/typeEvaluator4.test.js +32 -0
- package/dist/tests/typeEvaluator4.test.js.map +1 -1
- package/dist/tests/typeEvaluator7.test.js +394 -0
- package/dist/tests/typeEvaluator7.test.js.map +1 -1
- package/dist/tests/typeUtils.test.js +28 -0
- package/dist/tests/typeUtils.test.js.map +1 -1
- package/dist/typeServer/stubGenerator.js +136 -3
- package/dist/typeServer/stubGenerator.js.map +1 -1
- package/dist/typeServer/typeServerConversionUtils.js +13 -0
- package/dist/typeServer/typeServerConversionUtils.js.map +1 -1
- 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
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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:
|
|
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:
|
|
224
|
-
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
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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:
|
|
207
|
+
isIncomplete: isLiteralOrClassInfo.isIncomplete,
|
|
240
208
|
};
|
|
241
209
|
};
|
|
242
210
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
-
|
|
299
|
-
|
|
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:
|
|
308
|
-
|
|
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
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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
|
-
|
|
356
|
-
if (
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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:
|
|
386
|
-
|
|
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:
|
|
401
|
-
isIncomplete:
|
|
266
|
+
type: narrowedType,
|
|
267
|
+
isIncomplete: equalsInfo.isIncomplete,
|
|
402
268
|
};
|
|
403
269
|
};
|
|
404
270
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
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:
|
|
453
|
-
isIncomplete:
|
|
283
|
+
type: narrowTypeForDiscriminatedLiteralFieldComparison(evaluator, type, memberAccessInfo.memberName, memberAccessInfo.rightType, memberAccessInfo.adjIsPositiveTest),
|
|
284
|
+
isIncomplete: memberAccessInfo.isIncomplete,
|
|
454
285
|
};
|
|
455
286
|
};
|
|
456
287
|
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
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
|
-
|
|
476
|
-
if (
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
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
|
-
|
|
707
|
-
|
|
708
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|