@zzzen/pyright-internal 1.2.0-dev.20261018 → 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 +29 -12
- package/dist/analyzer/binder.js.map +1 -1
- package/dist/analyzer/checker.js +4 -0
- package/dist/analyzer/checker.js.map +1 -1
- package/dist/analyzer/codeFlowEngine.js +3 -3
- package/dist/analyzer/codeFlowEngine.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/dataClasses.js +14 -5
- package/dist/analyzer/dataClasses.js.map +1 -1
- package/dist/analyzer/decorators.d.ts +2 -2
- package/dist/analyzer/decorators.js +22 -2
- 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 +5 -1
- package/dist/analyzer/namedTuples.js +64 -35
- package/dist/analyzer/namedTuples.js.map +1 -1
- package/dist/analyzer/operations.js +45 -1
- package/dist/analyzer/operations.js.map +1 -1
- package/dist/analyzer/parseTreeUtils.d.ts +2 -0
- package/dist/analyzer/parseTreeUtils.js +43 -0
- package/dist/analyzer/parseTreeUtils.js.map +1 -1
- package/dist/analyzer/patternMatching.d.ts +5 -1
- package/dist/analyzer/patternMatching.js +6 -1
- 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/sentinel.js +7 -1
- package/dist/analyzer/sentinel.js.map +1 -1
- package/dist/analyzer/sourceFile.js +13 -6
- package/dist/analyzer/sourceFile.js.map +1 -1
- package/dist/analyzer/tupleComparison.d.ts +6 -0
- package/dist/analyzer/tupleComparison.js +731 -0
- package/dist/analyzer/tupleComparison.js.map +1 -0
- package/dist/analyzer/tuples.d.ts +1 -1
- package/dist/analyzer/tuples.js +35 -4
- 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 +5140 -13656
- package/dist/analyzer/typeEvaluator.js.map +1 -1
- package/dist/analyzer/typeEvaluatorTypes.d.ts +1 -1
- package/dist/analyzer/typeEvaluatorTypes.js +2 -3
- package/dist/analyzer/typeEvaluatorTypes.js.map +1 -1
- package/dist/analyzer/typeGuards.d.ts +3 -2
- package/dist/analyzer/typeGuards.js +236 -1375
- package/dist/analyzer/typeGuards.js.map +1 -1
- package/dist/analyzer/typeUtils.js +9 -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/analyzer/types.d.ts +14 -0
- package/dist/analyzer/types.js +215 -10
- package/dist/analyzer/types.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/common/processUtils.d.ts +6 -0
- package/dist/common/processUtils.js +56 -1
- package/dist/common/processUtils.js.map +1 -1
- package/dist/common/textEditTracker.js +1 -1
- package/dist/common/textEditTracker.js.map +1 -1
- package/dist/common/windowsProcessTree.d.ts +2 -0
- package/dist/common/windowsProcessTree.js +23 -0
- package/dist/common/windowsProcessTree.js.map +1 -1
- package/dist/common/windowsProcessTree.json +23 -3
- package/dist/languageServerBase.d.ts +3 -0
- package/dist/languageServerBase.js +44 -12
- package/dist/languageServerBase.js.map +1 -1
- package/dist/languageService/callHierarchyProvider.js +6 -6
- package/dist/languageService/callHierarchyProvider.js.map +1 -1
- package/dist/languageService/referencesProvider.js +3 -3
- package/dist/languageService/referencesProvider.js.map +1 -1
- package/dist/languageService/renameProvider.js +1 -1
- package/dist/languageService/renameProvider.js.map +1 -1
- package/dist/languageService/symbolIndexer.js +2 -2
- package/dist/languageService/symbolIndexer.js.map +1 -1
- package/dist/localization/localize.d.ts +2 -0
- package/dist/localization/localize.js +2 -0
- package/dist/localization/localize.js.map +1 -1
- package/dist/localization/package.nls.en-us.json +8 -0
- package/dist/parser/parseTreeTokenReuse.d.ts +34 -0
- package/dist/parser/parseTreeTokenReuse.js +897 -0
- package/dist/parser/parseTreeTokenReuse.js.map +1 -0
- package/dist/parser/tokenizer.d.ts +10 -1
- package/dist/parser/tokenizer.js +184 -130
- package/dist/parser/tokenizer.js.map +1 -1
- 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/checker.test.js +8 -0
- package/dist/tests/checker.test.js.map +1 -1
- package/dist/tests/constraintSolver.test.js +45 -0
- package/dist/tests/constraintSolver.test.js.map +1 -1
- package/dist/tests/fourSlashParser.test.js +35 -0
- package/dist/tests/fourSlashParser.test.js.map +1 -1
- package/dist/tests/harness/fourslash/fourSlashParser.js +14 -4
- package/dist/tests/harness/fourslash/fourSlashParser.js.map +1 -1
- package/dist/tests/languageServer.test.js +15 -0
- package/dist/tests/languageServer.test.js.map +1 -1
- package/dist/tests/lsp/languageServer.js +6 -6
- package/dist/tests/lsp/languageServer.js.map +1 -1
- package/dist/tests/mypyPrimerAnalysis.test.js +148 -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 +78 -1
- package/dist/tests/typeEvaluator1.test.js.map +1 -1
- package/dist/tests/typeEvaluator2.test.js +54 -1
- package/dist/tests/typeEvaluator2.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 +42 -3
- package/dist/tests/typeEvaluator4.test.js.map +1 -1
- package/dist/tests/typeEvaluator6.test.js +35 -0
- package/dist/tests/typeEvaluator6.test.js.map +1 -1
- package/dist/tests/typeEvaluator7.test.js +414 -0
- package/dist/tests/typeEvaluator7.test.js.map +1 -1
- package/dist/tests/typeEvaluator8.test.js +1291 -1
- package/dist/tests/typeEvaluator8.test.js.map +1 -1
- package/dist/tests/typeUtils.test.js +28 -0
- package/dist/tests/typeUtils.test.js.map +1 -1
- package/dist/tests/types.test.js +55 -0
- package/dist/tests/types.test.js.map +1 -1
- package/dist/typeServer/programWrapper.js +8 -2
- package/dist/typeServer/programWrapper.js.map +1 -1
- package/dist/typeServer/server.d.ts +12 -2
- package/dist/typeServer/server.js +97 -0
- package/dist/typeServer/server.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
|
@@ -43,6 +43,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
43
43
|
};
|
|
44
44
|
})();
|
|
45
45
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
exports.applyTypeNarrowingCallback = applyTypeNarrowingCallback;
|
|
46
47
|
exports.getTypeNarrowingCallback = getTypeNarrowingCallback;
|
|
47
48
|
exports.getIsInstanceClassTypes = getIsInstanceClassTypes;
|
|
48
49
|
exports.narrowTypeForInstanceOrSubclass = narrowTypeForInstanceOrSubclass;
|
|
@@ -57,15 +58,94 @@ const parseNodes_1 = require("../parser/parseNodes");
|
|
|
57
58
|
const analyzerNodeInfo_1 = require("./analyzerNodeInfo");
|
|
58
59
|
const constraintSolver_1 = require("./constraintSolver");
|
|
59
60
|
const constraintTracker_1 = require("./constraintTracker");
|
|
60
|
-
const enums_1 = require("./enums");
|
|
61
|
-
const operations_1 = require("./operations");
|
|
62
61
|
const parameterUtils_1 = require("./parameterUtils");
|
|
63
62
|
const ParseTreeUtils = __importStar(require("./parseTreeUtils"));
|
|
64
63
|
const scopeUtils_1 = require("./scopeUtils");
|
|
65
64
|
const symbol_1 = require("./symbol");
|
|
66
|
-
const
|
|
65
|
+
const TypeEvaluatorNarrowing = __importStar(require("./typeEvaluator/typeGuardNarrowing"));
|
|
67
66
|
const types_1 = require("./types");
|
|
68
67
|
const typeUtils_1 = require("./typeUtils");
|
|
68
|
+
function applyTypeNarrowingCallback(callback, type) {
|
|
69
|
+
const result = callback(type);
|
|
70
|
+
const baseType = (0, types_1.getInferredTupleLiteralBase)(type);
|
|
71
|
+
if (baseType === type) {
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
// Narrow both views at the flow boundary, not recursively within a guard.
|
|
75
|
+
// Independently established literals must survive mutable inference too.
|
|
76
|
+
const baseResult = callback(baseType);
|
|
77
|
+
if (!result && !baseResult) {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
type: (0, types_1.retainInferredTupleLiterals)(result?.type ?? type, (0, types_1.getInferredTupleLiteralBase)(baseResult?.type ?? baseType)),
|
|
82
|
+
isIncomplete: !!result?.isIncomplete || !!baseResult?.isIncomplete,
|
|
83
|
+
};
|
|
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
|
+
}
|
|
69
149
|
// Given a reference expression and a test expression, returns a callback that
|
|
70
150
|
// can be used to narrow the type described by the reference expression.
|
|
71
151
|
// If the specified flow node is not associated with the test expression,
|
|
@@ -81,394 +161,185 @@ function getTypeNarrowingCallback(evaluator, reference, testExpression, isPositi
|
|
|
81
161
|
if (testExpression.nodeType === 7 /* ParseNodeType.BinaryOperation */) {
|
|
82
162
|
const isOrIsNotOperator = testExpression.d.operator === 39 /* OperatorType.Is */ || testExpression.d.operator === 40 /* OperatorType.IsNot */;
|
|
83
163
|
const equalsOrNotEqualsOperator = testExpression.d.operator === 12 /* OperatorType.Equals */ || testExpression.d.operator === 28 /* OperatorType.NotEquals */;
|
|
84
|
-
const comparisonOperator = equalsOrNotEqualsOperator ||
|
|
85
|
-
testExpression.d.operator === 20 /* OperatorType.LessThan */ ||
|
|
86
|
-
testExpression.d.operator === 21 /* OperatorType.LessThanOrEqual */ ||
|
|
87
|
-
testExpression.d.operator === 15 /* OperatorType.GreaterThan */ ||
|
|
88
|
-
testExpression.d.operator === 16 /* OperatorType.GreaterThanOrEqual */;
|
|
89
164
|
if (isOrIsNotOperator || equalsOrNotEqualsOperator) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
: !isPositiveTest;
|
|
94
|
-
// Look for "X is None", "X is not None", "X == None", and "X != None".
|
|
95
|
-
// These are commonly-used patterns used in control flow.
|
|
96
|
-
if (testExpression.d.rightExpr.nodeType === 14 /* ParseNodeType.Constant */ &&
|
|
97
|
-
testExpression.d.rightExpr.d.constType === 27 /* KeywordType.None */) {
|
|
98
|
-
// Allow the LHS to be a simple expression or an assignment expression.
|
|
99
|
-
// For assignment expressions, narrow both the target and the RHS
|
|
100
|
-
// (consistent with truthiness narrowing).
|
|
101
|
-
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') {
|
|
102
168
|
return (type) => {
|
|
103
|
-
return {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const leftExpression = testExpression.d.leftExpr;
|
|
107
|
-
if (leftExpression.nodeType === 27 /* ParseNodeType.Index */ &&
|
|
108
|
-
ParseTreeUtils.isMatchingExpression(reference, leftExpression.d.leftExpr, (ref, expr) => isNameSameScope(evaluator, ref, expr)) &&
|
|
109
|
-
leftExpression.d.items.length === 1 &&
|
|
110
|
-
!leftExpression.d.trailingComma &&
|
|
111
|
-
leftExpression.d.items[0].d.argCategory === 0 /* ArgCategory.Simple */ &&
|
|
112
|
-
!leftExpression.d.items[0].d.name &&
|
|
113
|
-
leftExpression.d.items[0].d.valueExpr.nodeType === 40 /* ParseNodeType.Number */ &&
|
|
114
|
-
leftExpression.d.items[0].d.valueExpr.d.isInteger &&
|
|
115
|
-
!leftExpression.d.items[0].d.valueExpr.d.isImaginary) {
|
|
116
|
-
const indexValue = leftExpression.d.items[0].d.valueExpr.d.value;
|
|
117
|
-
if (typeof indexValue === 'number') {
|
|
118
|
-
return (type) => {
|
|
119
|
-
return {
|
|
120
|
-
type: narrowTupleTypeForIsNone(evaluator, type, adjIsPositiveTest, indexValue),
|
|
121
|
-
isIncomplete: false,
|
|
122
|
-
};
|
|
169
|
+
return {
|
|
170
|
+
type: narrowTypeForIsNone(evaluator, type, noneEllipsisInfo.adjIsPositiveTest),
|
|
171
|
+
isIncomplete: false,
|
|
123
172
|
};
|
|
124
|
-
}
|
|
173
|
+
};
|
|
125
174
|
}
|
|
126
|
-
|
|
127
|
-
// Look for "X is ...", "X is not ...", "X == ...", and "X != ...".
|
|
128
|
-
if (testExpression.d.rightExpr.nodeType === 21 /* ParseNodeType.Ellipsis */) {
|
|
129
|
-
if (isMatchingExpressionOrWalrusRhs(evaluator, reference, testExpression.d.leftExpr)) {
|
|
175
|
+
if (noneEllipsisInfo.kind === 'tuple-none') {
|
|
130
176
|
return (type) => {
|
|
131
177
|
return {
|
|
132
|
-
type:
|
|
178
|
+
type: narrowTupleTypeForIsNone(evaluator, type, noneEllipsisInfo.adjIsPositiveTest, noneEllipsisInfo.tupleIndex),
|
|
133
179
|
isIncomplete: false,
|
|
134
180
|
};
|
|
135
181
|
};
|
|
136
182
|
}
|
|
183
|
+
return (type) => {
|
|
184
|
+
return {
|
|
185
|
+
type: narrowTypeForIsEllipsis(evaluator, testExpression, type, noneEllipsisInfo.adjIsPositiveTest),
|
|
186
|
+
isIncomplete: false,
|
|
187
|
+
};
|
|
188
|
+
};
|
|
137
189
|
}
|
|
138
|
-
|
|
139
|
-
if (
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const rhsResult = evaluator.getTypeOfExpression(testExpression.d.rightExpr);
|
|
147
|
-
const classTypes = [];
|
|
148
|
-
let isClassType = true;
|
|
149
|
-
evaluator.mapSubtypesExpandTypeVars(rhsResult.type,
|
|
150
|
-
/* options */ undefined, (expandedSubtype) => {
|
|
151
|
-
if ((0, types_1.isInstantiableClass)(expandedSubtype)) {
|
|
152
|
-
classTypes.push(expandedSubtype);
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
155
|
-
isClassType = false;
|
|
156
|
-
}
|
|
157
|
-
return undefined;
|
|
158
|
-
});
|
|
159
|
-
if (isClassType && classTypes.length > 0) {
|
|
160
|
-
return (type) => {
|
|
161
|
-
return {
|
|
162
|
-
type: narrowTypeForTypeIs(evaluator, type, classTypes, adjIsPositiveTest),
|
|
163
|
-
isIncomplete: !!rhsResult.isIncomplete,
|
|
164
|
-
};
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
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
|
+
};
|
|
170
198
|
}
|
|
171
199
|
if (isOrIsNotOperator) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
// Look for "X is Y" or "X is not Y" where Y is a literal.
|
|
176
|
-
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') {
|
|
177
203
|
return (type) => {
|
|
178
204
|
return {
|
|
179
|
-
type: narrowTypeForLiteralComparison(evaluator, type, rightType, adjIsPositiveTest,
|
|
205
|
+
type: narrowTypeForLiteralComparison(evaluator, type, isLiteralOrClassInfo.rightType, isLiteralOrClassInfo.adjIsPositiveTest,
|
|
180
206
|
/* isIsOperator */ true),
|
|
181
|
-
isIncomplete:
|
|
207
|
+
isIncomplete: isLiteralOrClassInfo.isIncomplete,
|
|
182
208
|
};
|
|
183
209
|
};
|
|
184
210
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
type: narrowTypeForClassComparison(evaluator, type, rightType, adjIsPositiveTest),
|
|
190
|
-
isIncomplete: !!rightTypeResult.isIncomplete,
|
|
191
|
-
};
|
|
211
|
+
return (type) => {
|
|
212
|
+
return {
|
|
213
|
+
type: narrowTypeForClassComparison(evaluator, type, isLiteralOrClassInfo.rightType, isLiteralOrClassInfo.adjIsPositiveTest),
|
|
214
|
+
isIncomplete: isLiteralOrClassInfo.isIncomplete,
|
|
192
215
|
};
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
// Look for X[<literal>] is <literal> or X[<literal>] is not <literal>.
|
|
196
|
-
if (testExpression.d.leftExpr.nodeType === 27 /* ParseNodeType.Index */ &&
|
|
197
|
-
testExpression.d.leftExpr.d.items.length === 1 &&
|
|
198
|
-
!testExpression.d.leftExpr.d.trailingComma &&
|
|
199
|
-
testExpression.d.leftExpr.d.items[0].d.argCategory === 0 /* ArgCategory.Simple */ &&
|
|
200
|
-
ParseTreeUtils.isMatchingExpression(reference, testExpression.d.leftExpr.d.leftExpr, (ref, expr) => isNameSameScope(evaluator, ref, expr))) {
|
|
201
|
-
const indexTypeResult = evaluator.getTypeOfExpression(testExpression.d.leftExpr.d.items[0].d.valueExpr);
|
|
202
|
-
const indexType = indexTypeResult.type;
|
|
203
|
-
if ((0, types_1.isClassInstance)(indexType) && (0, typeUtils_1.isLiteralType)(indexType)) {
|
|
204
|
-
if (types_1.ClassType.isBuiltIn(indexType, 'str')) {
|
|
205
|
-
const rightType = evaluator.getTypeOfExpression(testExpression.d.rightExpr).type;
|
|
206
|
-
if ((0, types_1.isClassInstance)(rightType) && rightType.priv.literalValue !== undefined) {
|
|
207
|
-
return (type) => {
|
|
208
|
-
return {
|
|
209
|
-
type: narrowTypeForDiscriminatedDictEntryComparison(evaluator, type, indexType, rightType, adjIsPositiveTest),
|
|
210
|
-
isIncomplete: !!indexTypeResult.isIncomplete,
|
|
211
|
-
};
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
else if (types_1.ClassType.isBuiltIn(indexType, 'int')) {
|
|
216
|
-
const rightTypeResult = evaluator.getTypeOfExpression(testExpression.d.rightExpr);
|
|
217
|
-
const rightType = rightTypeResult.type;
|
|
218
|
-
if ((0, types_1.isClassInstance)(rightType) && rightType.priv.literalValue !== undefined) {
|
|
219
|
-
let canNarrow = false;
|
|
220
|
-
// Narrowing can be applied only for bool or enum literals.
|
|
221
|
-
if (types_1.ClassType.isBuiltIn(rightType, 'bool')) {
|
|
222
|
-
canNarrow = true;
|
|
223
|
-
}
|
|
224
|
-
else if (rightType.priv.literalValue instanceof types_1.EnumLiteral) {
|
|
225
|
-
canNarrow = true;
|
|
226
|
-
}
|
|
227
|
-
if (canNarrow) {
|
|
228
|
-
return (type) => {
|
|
229
|
-
return {
|
|
230
|
-
type: narrowTypeForDiscriminatedTupleComparison(evaluator, type, indexType, rightType, adjIsPositiveTest),
|
|
231
|
-
isIncomplete: !!rightTypeResult.isIncomplete,
|
|
232
|
-
};
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
216
|
+
};
|
|
238
217
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
const adjIsPositiveTest = testExpression.d.operator === 12 /* OperatorType.Equals */ ? isPositiveTest : !isPositiveTest;
|
|
243
|
-
if (isMatchingExpressionOrWalrusRhs(evaluator, reference, testExpression.d.leftExpr)) {
|
|
244
|
-
const rightTypeResult = evaluator.getTypeOfExpression(testExpression.d.rightExpr);
|
|
245
|
-
const rightType = rightTypeResult.type;
|
|
246
|
-
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') {
|
|
247
221
|
return (type) => {
|
|
248
222
|
return {
|
|
249
|
-
type:
|
|
250
|
-
|
|
251
|
-
isIncomplete: !!rightTypeResult.isIncomplete,
|
|
223
|
+
type: narrowTypeForDiscriminatedDictEntryComparison(evaluator, type, indexedLiteralInfo.indexType, indexedLiteralInfo.rightType, indexedLiteralInfo.adjIsPositiveTest),
|
|
224
|
+
isIncomplete: indexedLiteralInfo.isIncomplete,
|
|
252
225
|
};
|
|
253
226
|
};
|
|
254
227
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
type: narrowTypeForClassComparison(evaluator, type, rightType, adjIsPositiveTest,
|
|
260
|
-
/* isIsOperator */ false),
|
|
261
|
-
isIncomplete: !!rightTypeResult.isIncomplete,
|
|
262
|
-
};
|
|
228
|
+
return (type) => {
|
|
229
|
+
return {
|
|
230
|
+
type: narrowTypeForDiscriminatedTupleComparison(evaluator, type, indexedLiteralInfo.indexType, indexedLiteralInfo.rightType, indexedLiteralInfo.adjIsPositiveTest),
|
|
231
|
+
isIncomplete: indexedLiteralInfo.isIncomplete,
|
|
263
232
|
};
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
// Look for X[<literal>] == <literal> or X[<literal>] != <literal>
|
|
267
|
-
if (testExpression.d.leftExpr.nodeType === 27 /* ParseNodeType.Index */ &&
|
|
268
|
-
testExpression.d.leftExpr.d.items.length === 1 &&
|
|
269
|
-
!testExpression.d.leftExpr.d.trailingComma &&
|
|
270
|
-
testExpression.d.leftExpr.d.items[0].d.argCategory === 0 /* ArgCategory.Simple */ &&
|
|
271
|
-
ParseTreeUtils.isMatchingExpression(reference, testExpression.d.leftExpr.d.leftExpr, (ref, expr) => isNameSameScope(evaluator, ref, expr))) {
|
|
272
|
-
const indexTypeResult = evaluator.getTypeOfExpression(testExpression.d.leftExpr.d.items[0].d.valueExpr);
|
|
273
|
-
const indexType = indexTypeResult.type;
|
|
274
|
-
if ((0, types_1.isClassInstance)(indexType) && (0, typeUtils_1.isLiteralType)(indexType)) {
|
|
275
|
-
if (types_1.ClassType.isBuiltIn(indexType, ['str', 'int'])) {
|
|
276
|
-
const rightTypeResult = evaluator.getTypeOfExpression(testExpression.d.rightExpr);
|
|
277
|
-
const rightType = rightTypeResult.type;
|
|
278
|
-
if ((0, typeUtils_1.isLiteralTypeOrUnion)(rightType)) {
|
|
279
|
-
return (type) => {
|
|
280
|
-
let narrowedType;
|
|
281
|
-
if (types_1.ClassType.isBuiltIn(indexType, 'str')) {
|
|
282
|
-
narrowedType = narrowTypeForDiscriminatedDictEntryComparison(evaluator, type, indexType, rightType, adjIsPositiveTest);
|
|
283
|
-
}
|
|
284
|
-
else {
|
|
285
|
-
narrowedType = narrowTypeForDiscriminatedTupleComparison(evaluator, type, indexType, rightType, adjIsPositiveTest);
|
|
286
|
-
}
|
|
287
|
-
return {
|
|
288
|
-
type: narrowedType,
|
|
289
|
-
isIncomplete: !!indexTypeResult.isIncomplete || !!rightTypeResult.isIncomplete,
|
|
290
|
-
};
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
233
|
+
};
|
|
295
234
|
}
|
|
296
235
|
}
|
|
297
|
-
|
|
298
|
-
if (
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
if (rightType.priv.literalValue !== undefined || (0, typeUtils_1.isNoneInstance)(rightType)) {
|
|
306
|
-
return (type) => {
|
|
307
|
-
return {
|
|
308
|
-
type: narrowTypeForDiscriminatedLiteralFieldComparison(evaluator, type, memberName.d.value, rightType, adjIsPositiveTest),
|
|
309
|
-
isIncomplete: !!rightTypeResult.isIncomplete,
|
|
310
|
-
};
|
|
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,
|
|
311
244
|
};
|
|
312
|
-
}
|
|
245
|
+
};
|
|
313
246
|
}
|
|
314
|
-
|
|
315
|
-
// Look for X.Y is <literal> or X.Y is not <literal> where <literal> is
|
|
316
|
-
// an enum or bool literal
|
|
317
|
-
if (testExpression.d.leftExpr.nodeType === 35 /* ParseNodeType.MemberAccess */ &&
|
|
318
|
-
ParseTreeUtils.isMatchingExpression(reference, testExpression.d.leftExpr.d.leftExpr, (ref, expr) => isNameSameScope(evaluator, ref, expr))) {
|
|
319
|
-
const rightTypeResult = evaluator.getTypeOfExpression(testExpression.d.rightExpr);
|
|
320
|
-
const rightType = rightTypeResult.type;
|
|
321
|
-
const memberName = testExpression.d.leftExpr.d.member;
|
|
322
|
-
if ((0, types_1.isClassInstance)(rightType) &&
|
|
323
|
-
(types_1.ClassType.isEnumClass(rightType) || types_1.ClassType.isBuiltIn(rightType, 'bool')) &&
|
|
324
|
-
rightType.priv.literalValue !== undefined) {
|
|
247
|
+
if (equalsInfo.kind === 'class') {
|
|
325
248
|
return (type) => {
|
|
326
249
|
return {
|
|
327
|
-
type:
|
|
328
|
-
|
|
250
|
+
type: narrowTypeForClassComparison(evaluator, type, equalsInfo.rightType, equalsInfo.adjIsPositiveTest,
|
|
251
|
+
/* isIsOperator */ false),
|
|
252
|
+
isIncomplete: equalsInfo.isIncomplete,
|
|
329
253
|
};
|
|
330
254
|
};
|
|
331
255
|
}
|
|
332
|
-
}
|
|
333
|
-
// Look for X.Y is None or X.Y is not None
|
|
334
|
-
// These are commonly-used patterns used in control flow.
|
|
335
|
-
if (testExpression.d.leftExpr.nodeType === 35 /* ParseNodeType.MemberAccess */ &&
|
|
336
|
-
ParseTreeUtils.isMatchingExpression(reference, testExpression.d.leftExpr.d.leftExpr, (ref, expr) => isNameSameScope(evaluator, ref, expr)) &&
|
|
337
|
-
testExpression.d.rightExpr.nodeType === 14 /* ParseNodeType.Constant */ &&
|
|
338
|
-
testExpression.d.rightExpr.d.constType === 27 /* KeywordType.None */) {
|
|
339
|
-
const memberName = testExpression.d.leftExpr.d.member;
|
|
340
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
|
+
}
|
|
341
265
|
return {
|
|
342
|
-
type:
|
|
343
|
-
isIncomplete:
|
|
266
|
+
type: narrowedType,
|
|
267
|
+
isIncomplete: equalsInfo.isIncomplete,
|
|
344
268
|
};
|
|
345
269
|
};
|
|
346
270
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
const callTypeResult = evaluator.getTypeOfExpression(testExpression.d.leftExpr.d.leftExpr, 2 /* EvalFlags.CallBaseDefaults */);
|
|
355
|
-
const callType = callTypeResult.type;
|
|
356
|
-
if ((0, types_1.isFunction)(callType) && callType.shared.fullName === 'builtins.len') {
|
|
357
|
-
const rightTypeResult = evaluator.getTypeOfExpression(testExpression.d.rightExpr);
|
|
358
|
-
const rightType = rightTypeResult.type;
|
|
359
|
-
if ((0, types_1.isClassInstance)(rightType) &&
|
|
360
|
-
typeof rightType.priv.literalValue === 'number' &&
|
|
361
|
-
rightType.priv.literalValue >= 0) {
|
|
362
|
-
let tupleLength = rightType.priv.literalValue;
|
|
363
|
-
// We'll treat <, <= and == as positive tests with >=, > and != as
|
|
364
|
-
// their negative counterparts.
|
|
365
|
-
const isLessOrEqual = testExpression.d.operator === 12 /* OperatorType.Equals */ ||
|
|
366
|
-
testExpression.d.operator === 20 /* OperatorType.LessThan */ ||
|
|
367
|
-
testExpression.d.operator === 21 /* OperatorType.LessThanOrEqual */;
|
|
368
|
-
const adjIsPositiveTest = isLessOrEqual ? isPositiveTest : !isPositiveTest;
|
|
369
|
-
// For <= (or its negative counterpart >), adjust the tuple length by 1.
|
|
370
|
-
if (testExpression.d.operator === 21 /* OperatorType.LessThanOrEqual */ ||
|
|
371
|
-
testExpression.d.operator === 15 /* OperatorType.GreaterThan */) {
|
|
372
|
-
tupleLength++;
|
|
373
|
-
}
|
|
374
|
-
const isEqualityCheck = testExpression.d.operator === 12 /* OperatorType.Equals */ ||
|
|
375
|
-
testExpression.d.operator === 28 /* OperatorType.NotEquals */;
|
|
376
|
-
return (type) => {
|
|
377
|
-
return {
|
|
378
|
-
type: narrowTypeForTupleLength(evaluator, type, tupleLength, adjIsPositiveTest, !isEqualityCheck),
|
|
379
|
-
isIncomplete: !!callTypeResult.isIncomplete || !!rightTypeResult.isIncomplete,
|
|
380
|
-
};
|
|
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,
|
|
381
278
|
};
|
|
382
|
-
}
|
|
279
|
+
};
|
|
383
280
|
}
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
if (testExpression.d.operator === 41 /* OperatorType.In */ || testExpression.d.operator === 42 /* OperatorType.NotIn */) {
|
|
387
|
-
// Look for "x in y" or "x not in y" where y is one of several built-in types.
|
|
388
|
-
if (isMatchingExpressionOrWalrusRhs(evaluator, reference, testExpression.d.leftExpr)) {
|
|
389
|
-
const rightTypeResult = evaluator.getTypeOfExpression(testExpression.d.rightExpr);
|
|
390
|
-
const rightType = rightTypeResult.type;
|
|
391
|
-
const adjIsPositiveTest = testExpression.d.operator === 41 /* OperatorType.In */ ? isPositiveTest : !isPositiveTest;
|
|
392
281
|
return (type) => {
|
|
393
282
|
return {
|
|
394
|
-
type:
|
|
395
|
-
isIncomplete:
|
|
283
|
+
type: narrowTypeForDiscriminatedLiteralFieldComparison(evaluator, type, memberAccessInfo.memberName, memberAccessInfo.rightType, memberAccessInfo.adjIsPositiveTest),
|
|
284
|
+
isIncomplete: memberAccessInfo.isIncomplete,
|
|
396
285
|
};
|
|
397
286
|
};
|
|
398
287
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
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,
|
|
411
305
|
};
|
|
412
|
-
}
|
|
306
|
+
};
|
|
413
307
|
}
|
|
308
|
+
return (type) => {
|
|
309
|
+
return {
|
|
310
|
+
type: narrowTypeForTypedDictKey(evaluator, type, inOperatorInfo.typedDictKeyType, inOperatorInfo.adjIsPositiveTest),
|
|
311
|
+
isIncomplete: inOperatorInfo.isIncomplete,
|
|
312
|
+
};
|
|
313
|
+
};
|
|
414
314
|
}
|
|
415
315
|
}
|
|
416
316
|
if (testExpression.nodeType === 9 /* ParseNodeType.Call */) {
|
|
417
|
-
|
|
418
|
-
if (
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
const callTypeResult = evaluator.getTypeOfExpression(testExpression.d.leftExpr, 2 /* EvalFlags.CallBaseDefaults */);
|
|
426
|
-
const callType = callTypeResult.type;
|
|
427
|
-
if ((0, types_1.isFunction)(callType) && types_1.FunctionType.isBuiltIn(callType, ['isinstance', 'issubclass'])) {
|
|
428
|
-
const isInstanceCheck = types_1.FunctionType.isBuiltIn(callType, 'isinstance');
|
|
429
|
-
const arg1TypeResult = evaluator.getTypeOfExpression(arg1Expr, 536871546 /* EvalFlags.IsInstanceArgDefaults */);
|
|
430
|
-
const arg1Type = arg1TypeResult.type;
|
|
431
|
-
const classTypeInfo = getIsInstanceClassTypes(evaluator, arg1Type, arg1Expr);
|
|
432
|
-
const isIncomplete = !!callTypeResult.isIncomplete || !!arg1TypeResult.isIncomplete;
|
|
433
|
-
if (classTypeInfo) {
|
|
434
|
-
const classTypeList = isPositiveTest
|
|
435
|
-
? classTypeInfo.classTypes
|
|
436
|
-
: classTypeInfo.negativeClassTypes;
|
|
437
|
-
return (type) => {
|
|
438
|
-
return {
|
|
439
|
-
type: narrowTypeForInstanceOrSubclass(evaluator, type, classTypeList, isInstanceCheck,
|
|
440
|
-
/* isTypeIsCheck */ false, isPositiveTest, testExpression, nodeInfo),
|
|
441
|
-
isIncomplete,
|
|
442
|
-
};
|
|
443
|
-
};
|
|
444
|
-
}
|
|
445
|
-
else if (isIncomplete) {
|
|
446
|
-
// If the type is incomplete, it may include unknowns, which will result
|
|
447
|
-
// in classTypeList being undefined.
|
|
448
|
-
return (type) => {
|
|
449
|
-
return {
|
|
450
|
-
type,
|
|
451
|
-
isIncomplete: true,
|
|
452
|
-
};
|
|
453
|
-
};
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
// Look for "bool(X)"
|
|
459
|
-
if (testExpression.d.args.length === 1 && !testExpression.d.args[0].d.name) {
|
|
460
|
-
if (isMatchingExpressionOrWalrusRhs(evaluator, reference, testExpression.d.args[0].d.valueExpr)) {
|
|
461
|
-
const callTypeResult = evaluator.getTypeOfExpression(testExpression.d.leftExpr, 2 /* EvalFlags.CallBaseDefaults */);
|
|
462
|
-
const callType = callTypeResult.type;
|
|
463
|
-
if ((0, types_1.isInstantiableClass)(callType) && types_1.ClassType.isBuiltIn(callType, 'bool')) {
|
|
464
|
-
return (type) => {
|
|
465
|
-
return {
|
|
466
|
-
type: narrowTypeForTruthiness(evaluator, type, isPositiveTest),
|
|
467
|
-
isIncomplete: !!callTypeResult.isIncomplete,
|
|
468
|
-
};
|
|
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,
|
|
469
325
|
};
|
|
470
|
-
}
|
|
326
|
+
};
|
|
471
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
|
+
};
|
|
472
343
|
}
|
|
473
344
|
// Look for a TypeGuard function.
|
|
474
345
|
const isFunctionReturnTypeGuard = (type) => {
|
|
@@ -645,11 +516,9 @@ function getTypeNarrowingCallback(evaluator, reference, testExpression, isPositi
|
|
|
645
516
|
// We normally won't find a "not" operator here because they are stripped out
|
|
646
517
|
// by the binder when it creates condition flow nodes, but we can find this
|
|
647
518
|
// in the case of local variables type narrowing.
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
return getTypeNarrowingCallback(evaluator, reference, testExpression.d.expr, !isPositiveTest, nodeInfo, recursionCount);
|
|
652
|
-
}
|
|
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);
|
|
653
522
|
}
|
|
654
523
|
return undefined;
|
|
655
524
|
}
|
|
@@ -744,377 +613,34 @@ function getTypeNarrowingCallbackForAssignmentExpression(evaluator, reference, t
|
|
|
744
613
|
getTypeNarrowingCallback(evaluator, reference, testExpression.d.name, isPositiveTest, nodeInfo, recursionCount));
|
|
745
614
|
}
|
|
746
615
|
function narrowTypeForUserDefinedTypeGuard(evaluator, type, typeGuardType, isPositiveTest, isStrictTypeGuard, errorNode, nodeInfo) {
|
|
747
|
-
|
|
748
|
-
// in the positive case and don't narrow in the negative case.
|
|
749
|
-
if (!isStrictTypeGuard) {
|
|
750
|
-
let result = type;
|
|
751
|
-
if (isPositiveTest) {
|
|
752
|
-
result = typeGuardType;
|
|
753
|
-
// If the type guard is a non-constrained TypeVar, add a
|
|
754
|
-
// condition to the resulting type.
|
|
755
|
-
if ((0, types_1.isTypeVar)(type) && !(0, types_1.isParamSpec)(type) && !types_1.TypeVarType.hasConstraints(type)) {
|
|
756
|
-
result = (0, typeUtils_1.addConditionToType)(result, [{ typeVar: type, constraintIndex: 0 }]);
|
|
757
|
-
}
|
|
758
|
-
return result;
|
|
759
|
-
}
|
|
760
|
-
return result;
|
|
761
|
-
}
|
|
762
|
-
const filterTypes = [];
|
|
763
|
-
(0, typeUtils_1.doForEachSubtype)(typeGuardType, (typeGuardSubtype) => {
|
|
764
|
-
filterTypes.push((0, typeUtils_1.convertToInstantiable)(typeGuardSubtype));
|
|
765
|
-
});
|
|
766
|
-
return narrowTypeForInstanceOrSubclass(evaluator, type, filterTypes,
|
|
767
|
-
/* isInstanceCheck */ true,
|
|
768
|
-
/* isTypeIsCheck */ true, isPositiveTest, errorNode, nodeInfo);
|
|
616
|
+
return TypeEvaluatorNarrowing.narrowTypeForUserDefinedTypeGuard(evaluator, type, typeGuardType, isPositiveTest, isStrictTypeGuard, errorNode, nodeInfo, narrowTypeForInstanceOrSubclass);
|
|
769
617
|
}
|
|
770
618
|
// Narrow the type based on whether the subtype can be true or false.
|
|
771
619
|
function narrowTypeForTruthiness(evaluator, type, isPositiveTest) {
|
|
772
|
-
return (
|
|
773
|
-
if (isPositiveTest) {
|
|
774
|
-
if (evaluator.canBeTruthy(subtype)) {
|
|
775
|
-
return evaluator.removeFalsinessFromType(subtype);
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
else {
|
|
779
|
-
if (evaluator.canBeFalsy(subtype)) {
|
|
780
|
-
return evaluator.removeTruthinessFromType(subtype);
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
return undefined;
|
|
784
|
-
});
|
|
620
|
+
return TypeEvaluatorNarrowing.narrowTypeForTruthiness(evaluator, type, isPositiveTest);
|
|
785
621
|
}
|
|
786
622
|
// Handle type narrowing for expressions of the form "a[I] is None" and "a[I] is not None" where
|
|
787
623
|
// I is an integer and a is a union of Tuples (or subtypes thereof) with known lengths and entry types.
|
|
788
624
|
function narrowTupleTypeForIsNone(evaluator, type, isPositiveTest, indexValue) {
|
|
789
|
-
return
|
|
790
|
-
const tupleType = (0, typeUtils_1.getSpecializedTupleType)(subtype);
|
|
791
|
-
if (!tupleType || (0, typeUtils_1.isUnboundedTupleClass)(tupleType) || !tupleType.priv.tupleTypeArgs) {
|
|
792
|
-
return subtype;
|
|
793
|
-
}
|
|
794
|
-
const tupleLength = tupleType.priv.tupleTypeArgs.length;
|
|
795
|
-
if (indexValue < 0 || indexValue >= tupleLength) {
|
|
796
|
-
return subtype;
|
|
797
|
-
}
|
|
798
|
-
const typeOfEntry = evaluator.makeTopLevelTypeVarsConcrete(tupleType.priv.tupleTypeArgs[indexValue].type);
|
|
799
|
-
if (isPositiveTest) {
|
|
800
|
-
if (!evaluator.assignType(typeOfEntry, evaluator.getNoneType())) {
|
|
801
|
-
return undefined;
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
else {
|
|
805
|
-
if ((0, typeUtils_1.isNoneInstance)(typeOfEntry)) {
|
|
806
|
-
return undefined;
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
return subtype;
|
|
810
|
-
});
|
|
811
|
-
}
|
|
812
|
-
// Walks a chain of NewType instances down to the innermost non-NewType base
|
|
813
|
-
// instance (e.g. NewType("Y", NewType("X", bool)) -> an instance of bool).
|
|
814
|
-
// Returns undefined if `subtype` is not a NewType instance or its base cannot
|
|
815
|
-
// be resolved to a class.
|
|
816
|
-
function getInnermostNewTypeBaseInstance(subtype) {
|
|
817
|
-
if (!(0, types_1.isClassInstance)(subtype) || !types_1.ClassType.isNewTypeClass(subtype)) {
|
|
818
|
-
return undefined;
|
|
819
|
-
}
|
|
820
|
-
let currentType = subtype;
|
|
821
|
-
while ((0, types_1.isClassInstance)(currentType) && types_1.ClassType.isNewTypeClass(currentType)) {
|
|
822
|
-
if (currentType.shared.baseClasses.length === 0) {
|
|
823
|
-
return undefined;
|
|
824
|
-
}
|
|
825
|
-
const baseClass = currentType.shared.baseClasses[0];
|
|
826
|
-
if (!(0, types_1.isClass)(baseClass)) {
|
|
827
|
-
return undefined;
|
|
828
|
-
}
|
|
829
|
-
currentType = types_1.ClassType.cloneAsInstance(baseClass);
|
|
830
|
-
}
|
|
831
|
-
return currentType;
|
|
625
|
+
return TypeEvaluatorNarrowing.narrowTupleTypeForIsNone(evaluator, type, isPositiveTest, indexValue);
|
|
832
626
|
}
|
|
833
627
|
// Handle type narrowing for expressions of the form "x is None" and "x is not None".
|
|
834
628
|
function narrowTypeForIsNone(evaluator, type, isPositiveTest) {
|
|
835
|
-
|
|
836
|
-
return (0, typeUtils_1.transformPossibleRecursiveTypeAlias)(subtype);
|
|
837
|
-
});
|
|
838
|
-
let resultIncludesNoneSubtype = false;
|
|
839
|
-
const result = evaluator.mapSubtypesExpandTypeVars(expandedType,
|
|
840
|
-
/* options */ undefined, (subtype, unexpandedSubtype) => {
|
|
841
|
-
if ((0, types_1.isAnyOrUnknown)(subtype)) {
|
|
842
|
-
// Narrow to None in positive tests, matching the behavior of other
|
|
843
|
-
// narrowing functions (narrowTypeForInstanceOrSubclass, narrowTypeForLiteralComparison).
|
|
844
|
-
if (isPositiveTest) {
|
|
845
|
-
resultIncludesNoneSubtype = true;
|
|
846
|
-
return (0, typeUtils_1.addConditionToType)(evaluator.getNoneType(), subtype.props?.condition);
|
|
847
|
-
}
|
|
848
|
-
// For negative tests, keep the original type.
|
|
849
|
-
return subtype;
|
|
850
|
-
}
|
|
851
|
-
let useExpandedSubtype = false;
|
|
852
|
-
if ((0, types_1.isTypeVar)(unexpandedSubtype) && !types_1.TypeVarType.isSelf(unexpandedSubtype)) {
|
|
853
|
-
// If the TypeVar has value constraints and one or more of them
|
|
854
|
-
// are possibly compatible with None, use the expanded subtypes.
|
|
855
|
-
if (unexpandedSubtype.shared.constraints.some((constraint) => {
|
|
856
|
-
return evaluator.assignType(constraint, evaluator.getNoneType());
|
|
857
|
-
})) {
|
|
858
|
-
useExpandedSubtype = true;
|
|
859
|
-
}
|
|
860
|
-
// If the TypeVar han an explicit bound that is possibly compatible
|
|
861
|
-
// with None (e.g. "T: int | None"), use the expanded subtypes.
|
|
862
|
-
if (unexpandedSubtype.shared.boundType &&
|
|
863
|
-
evaluator.assignType(unexpandedSubtype.shared.boundType, evaluator.getNoneType())) {
|
|
864
|
-
useExpandedSubtype = true;
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
const adjustedSubtype = useExpandedSubtype ? subtype : unexpandedSubtype;
|
|
868
|
-
// A NewType whose innermost base is exactly None (e.g. NewType("Apple", NoneType))
|
|
869
|
-
// can be identity-compared with None: keep the NewType identity on the positive
|
|
870
|
-
// branch and eliminate it on the negative branch. A NewType whose base is merely
|
|
871
|
-
// None-compatible (e.g. NewType("Obj", object)) is intentionally not handled here;
|
|
872
|
-
// it falls through to the generic checks below so that "is not None" does not
|
|
873
|
-
// incorrectly collapse to Never.
|
|
874
|
-
const newTypeBaseInstance = getInnermostNewTypeBaseInstance(adjustedSubtype);
|
|
875
|
-
if (newTypeBaseInstance && (0, typeUtils_1.isNoneInstance)(newTypeBaseInstance)) {
|
|
876
|
-
resultIncludesNoneSubtype = true;
|
|
877
|
-
return isPositiveTest ? adjustedSubtype : undefined;
|
|
878
|
-
}
|
|
879
|
-
// Is it an exact match for None?
|
|
880
|
-
if ((0, typeUtils_1.isNoneInstance)(subtype)) {
|
|
881
|
-
resultIncludesNoneSubtype = true;
|
|
882
|
-
return isPositiveTest ? adjustedSubtype : undefined;
|
|
883
|
-
}
|
|
884
|
-
// Is it potentially None?
|
|
885
|
-
if (evaluator.assignType(subtype, evaluator.getNoneType())) {
|
|
886
|
-
resultIncludesNoneSubtype = true;
|
|
887
|
-
return isPositiveTest
|
|
888
|
-
? (0, typeUtils_1.addConditionToType)(evaluator.getNoneType(), subtype.props?.condition)
|
|
889
|
-
: adjustedSubtype;
|
|
890
|
-
}
|
|
891
|
-
return isPositiveTest ? undefined : adjustedSubtype;
|
|
892
|
-
});
|
|
893
|
-
// If this is a positive test and the result is a union that includes None,
|
|
894
|
-
// we can eliminate all the non-None subtypes include Any or Unknown. If some
|
|
895
|
-
// of the subtypes are None types with conditions, retain those.
|
|
896
|
-
if (isPositiveTest && resultIncludesNoneSubtype) {
|
|
897
|
-
return (0, typeUtils_1.mapSubtypes)(result, (subtype) => {
|
|
898
|
-
const baseInstance = getInnermostNewTypeBaseInstance(subtype);
|
|
899
|
-
return (0, typeUtils_1.isNoneInstance)(subtype) || (baseInstance && (0, typeUtils_1.isNoneInstance)(baseInstance)) ? subtype : undefined;
|
|
900
|
-
});
|
|
901
|
-
}
|
|
902
|
-
return result;
|
|
629
|
+
return TypeEvaluatorNarrowing.narrowTypeForIsNone(evaluator, type, isPositiveTest);
|
|
903
630
|
}
|
|
904
631
|
// Handle type narrowing for expressions of the form "x is ..." and "x is not ...".
|
|
905
632
|
function narrowTypeForIsEllipsis(evaluator, node, type, isPositiveTest) {
|
|
906
|
-
|
|
907
|
-
return (0, typeUtils_1.transformPossibleRecursiveTypeAlias)(subtype);
|
|
908
|
-
});
|
|
909
|
-
let resultIncludesEllipsisSubtype = false;
|
|
910
|
-
const ellipsisType = evaluator.getBuiltInObject(node, 'EllipsisType') ??
|
|
911
|
-
evaluator.getBuiltInObject(node, 'ellipsis') ??
|
|
912
|
-
types_1.AnyType.create();
|
|
913
|
-
const isEllipsisInstance = (subtype) => {
|
|
914
|
-
return (0, types_1.isClassInstance)(subtype) && types_1.ClassType.isBuiltIn(subtype, ['EllipsisType', 'ellipsis']);
|
|
915
|
-
};
|
|
916
|
-
const result = evaluator.mapSubtypesExpandTypeVars(expandedType,
|
|
917
|
-
/* options */ undefined, (subtype, unexpandedSubtype) => {
|
|
918
|
-
if ((0, types_1.isAnyOrUnknown)(subtype)) {
|
|
919
|
-
// We need to assume that "Any" is always both ellipsis and not ellipsis,
|
|
920
|
-
// so it matches regardless of whether the test is positive or negative.
|
|
921
|
-
return subtype;
|
|
922
|
-
}
|
|
923
|
-
// If this is a TypeVar that isn't constrained, use the unexpanded
|
|
924
|
-
// TypeVar. For all other cases (including constrained TypeVars),
|
|
925
|
-
// use the expanded subtype.
|
|
926
|
-
const adjustedSubtype = (0, types_1.isTypeVar)(unexpandedSubtype) && !types_1.TypeVarType.hasConstraints(unexpandedSubtype)
|
|
927
|
-
? unexpandedSubtype
|
|
928
|
-
: subtype;
|
|
929
|
-
// Only a NewType whose innermost base is exactly the ellipsis type is treated
|
|
930
|
-
// as the singleton (see narrowTypeForIsNone for the rationale); a merely
|
|
931
|
-
// ellipsis-compatible base falls through so "is not ..." does not collapse to Never.
|
|
932
|
-
const newTypeBaseInstance = getInnermostNewTypeBaseInstance(adjustedSubtype);
|
|
933
|
-
if (newTypeBaseInstance && isEllipsisInstance(newTypeBaseInstance)) {
|
|
934
|
-
resultIncludesEllipsisSubtype = true;
|
|
935
|
-
return isPositiveTest ? adjustedSubtype : undefined;
|
|
936
|
-
}
|
|
937
|
-
// Is it an exact match for ellipsis?
|
|
938
|
-
if (isEllipsisInstance(subtype)) {
|
|
939
|
-
resultIncludesEllipsisSubtype = true;
|
|
940
|
-
return isPositiveTest ? adjustedSubtype : undefined;
|
|
941
|
-
}
|
|
942
|
-
// Is it potentially ellipsis?
|
|
943
|
-
if (evaluator.assignType(subtype, ellipsisType)) {
|
|
944
|
-
resultIncludesEllipsisSubtype = true;
|
|
945
|
-
return isPositiveTest ? (0, typeUtils_1.addConditionToType)(ellipsisType, subtype.props?.condition) : adjustedSubtype;
|
|
946
|
-
}
|
|
947
|
-
return isPositiveTest ? undefined : adjustedSubtype;
|
|
948
|
-
});
|
|
949
|
-
// If this is a positive test and the result is a union that includes ellipsis,
|
|
950
|
-
// we can eliminate all the non-ellipsis subtypes include Any or Unknown. If some
|
|
951
|
-
// of the subtypes are ellipsis types with conditions, retain those.
|
|
952
|
-
if (isPositiveTest && resultIncludesEllipsisSubtype) {
|
|
953
|
-
return (0, typeUtils_1.mapSubtypes)(result, (subtype) => {
|
|
954
|
-
const baseInstance = getInnermostNewTypeBaseInstance(subtype);
|
|
955
|
-
return isEllipsisInstance(subtype) || (baseInstance && isEllipsisInstance(baseInstance))
|
|
956
|
-
? subtype
|
|
957
|
-
: undefined;
|
|
958
|
-
});
|
|
959
|
-
}
|
|
960
|
-
return result;
|
|
633
|
+
return TypeEvaluatorNarrowing.narrowTypeForIsEllipsis(evaluator, node, type, isPositiveTest);
|
|
961
634
|
}
|
|
962
635
|
// The "isinstance" and "issubclass" calls accept classes, runtime unions,
|
|
963
636
|
// and arbitrarily-nested tuples of these. This method extracts possible
|
|
964
637
|
// classes and guaranteed negative filters,
|
|
965
638
|
// or undefined if the filter contains a non-class type.
|
|
966
639
|
function getIsInstanceClassTypes(evaluator, argType, argExpr) {
|
|
967
|
-
|
|
968
|
-
const classTypes = [];
|
|
969
|
-
const negativeClassTypes = [];
|
|
970
|
-
// Create a helper function that returns a list of class types or
|
|
971
|
-
// undefined if any of the types are not valid.
|
|
972
|
-
const addClassTypesToList = (types, isAmbiguous, isUnboundedEntry) => {
|
|
973
|
-
types.forEach((subtype) => {
|
|
974
|
-
if ((0, types_1.isClass)(subtype)) {
|
|
975
|
-
subtype = (0, typeUtils_1.specializeWithUnknownTypeArgs)(subtype, evaluator.getTupleClassType());
|
|
976
|
-
if ((0, types_1.isInstantiableClass)(subtype) && types_1.ClassType.isBuiltIn(subtype, 'Callable')) {
|
|
977
|
-
subtype = (0, typeUtils_1.convertToInstantiable)((0, typeUtils_1.getUnknownTypeForCallable)());
|
|
978
|
-
}
|
|
979
|
-
else if (types_1.TypeBase.isInstance(subtype) && types_1.ClassType.isBuiltIn(subtype, 'type')) {
|
|
980
|
-
if (subtype.priv.typeArgs && subtype.priv.typeArgs.length > 0) {
|
|
981
|
-
const typeArg = subtype.priv.typeArgs[0];
|
|
982
|
-
if ((0, types_1.isAnyOrUnknown)(typeArg)) {
|
|
983
|
-
foundNonClassType = true;
|
|
984
|
-
return;
|
|
985
|
-
}
|
|
986
|
-
if ((0, types_1.isInstantiableClass)(typeArg)) {
|
|
987
|
-
subtype = types_1.ClassType.cloneIncludeSubclasses(typeArg);
|
|
988
|
-
}
|
|
989
|
-
else if ((0, types_1.isClassInstance)(typeArg)) {
|
|
990
|
-
subtype = types_1.ClassType.cloneAsInstantiable(typeArg);
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
let classType;
|
|
996
|
-
if ((0, types_1.isInstantiableClass)(subtype)) {
|
|
997
|
-
// If this is a reference to a class that has type promotions (e.g.
|
|
998
|
-
// float or complex), remove the promotions for purposes of the
|
|
999
|
-
// isinstance check).
|
|
1000
|
-
if (!subtype.priv.includeSubclasses && subtype.priv.includePromotions) {
|
|
1001
|
-
subtype = types_1.ClassType.cloneRemoveTypePromotions(subtype);
|
|
1002
|
-
}
|
|
1003
|
-
classType = subtype;
|
|
1004
|
-
}
|
|
1005
|
-
else if ((0, types_1.isTypeVar)(subtype) && types_1.TypeBase.isInstantiable(subtype)) {
|
|
1006
|
-
classType = subtype;
|
|
1007
|
-
}
|
|
1008
|
-
else if ((0, typeUtils_1.isNoneTypeClass)(subtype)) {
|
|
1009
|
-
(0, debug_1.assert)((0, types_1.isInstantiableClass)(subtype));
|
|
1010
|
-
classType = subtype;
|
|
1011
|
-
}
|
|
1012
|
-
else if ((0, types_1.isFunction)(subtype) &&
|
|
1013
|
-
subtype.shared.parameters.length === 2 &&
|
|
1014
|
-
subtype.shared.parameters[0].category === 1 /* ParamCategory.ArgsList */ &&
|
|
1015
|
-
subtype.shared.parameters[1].category === 2 /* ParamCategory.KwargsDict */) {
|
|
1016
|
-
classType = subtype;
|
|
1017
|
-
}
|
|
1018
|
-
else {
|
|
1019
|
-
foundNonClassType = true;
|
|
1020
|
-
return;
|
|
1021
|
-
}
|
|
1022
|
-
classTypes.push(classType);
|
|
1023
|
-
// Unbounded entries may be absent, and alternative class objects
|
|
1024
|
-
// are not all guaranteed to participate in the runtime check.
|
|
1025
|
-
if (!isUnboundedEntry && !isAmbiguous) {
|
|
1026
|
-
negativeClassTypes.push(classType);
|
|
1027
|
-
}
|
|
1028
|
-
});
|
|
1029
|
-
};
|
|
1030
|
-
const addClassTypesRecursive = (type, isAmbiguous, isUnboundedEntry, recursionCount, expression) => {
|
|
1031
|
-
if (recursionCount > types_1.maxTypeRecursionCount) {
|
|
1032
|
-
return;
|
|
1033
|
-
}
|
|
1034
|
-
// A runtime union checks both operands, but either operand can itself
|
|
1035
|
-
// be an alternative class object. Keep their certainty separate.
|
|
1036
|
-
if ((0, types_1.isUnion)(type) &&
|
|
1037
|
-
expression?.nodeType === 7 /* ParseNodeType.BinaryOperation */ &&
|
|
1038
|
-
expression.d.operator === 6 /* OperatorType.BitwiseOr */) {
|
|
1039
|
-
const leftType = evaluator.getCachedType(expression.d.leftExpr);
|
|
1040
|
-
const rightType = evaluator.getCachedType(expression.d.rightExpr);
|
|
1041
|
-
if (leftType &&
|
|
1042
|
-
rightType &&
|
|
1043
|
-
!(0, operations_1.customMetaclassSupportsMethod)(leftType, '__or__') &&
|
|
1044
|
-
!(0, operations_1.customMetaclassSupportsMethod)(rightType, '__ror__')) {
|
|
1045
|
-
// Match the union evaluator's normalization of None operands to NoneType.
|
|
1046
|
-
const adjustedLeftType = (0, typeUtils_1.isNoneInstance)(leftType)
|
|
1047
|
-
? (0, typeUtils_1.convertToInstantiable)(evaluator.getNoneType())
|
|
1048
|
-
: leftType;
|
|
1049
|
-
const adjustedRightType = (0, typeUtils_1.isNoneInstance)(rightType)
|
|
1050
|
-
? (0, typeUtils_1.convertToInstantiable)(evaluator.getNoneType())
|
|
1051
|
-
: rightType;
|
|
1052
|
-
addClassTypesRecursive(adjustedLeftType, isAmbiguous, isUnboundedEntry, recursionCount, expression.d.leftExpr);
|
|
1053
|
-
addClassTypesRecursive(adjustedRightType, isAmbiguous, isUnboundedEntry, recursionCount, expression.d.rightExpr);
|
|
1054
|
-
return;
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
const isRuntimeUnion = type.props?.specialForm && types_1.ClassType.isBuiltIn(type.props.specialForm, 'UnionType');
|
|
1058
|
-
const isAmbiguousType = isAmbiguous || ((0, types_1.isUnion)(type) && !isRuntimeUnion);
|
|
1059
|
-
(0, typeUtils_1.doForEachSubtype)(type, (subtype) => {
|
|
1060
|
-
if ((0, types_1.isClassInstance)(subtype) && (0, typeUtils_1.isTupleClass)(subtype)) {
|
|
1061
|
-
if (subtype.priv.tupleTypeArgs) {
|
|
1062
|
-
let entryExpressions;
|
|
1063
|
-
if (expression?.nodeType === 52 /* ParseNodeType.Tuple */) {
|
|
1064
|
-
const expressions = [];
|
|
1065
|
-
expression.d.items.forEach((item) => {
|
|
1066
|
-
if (item.nodeType !== 56 /* ParseNodeType.Unpack */) {
|
|
1067
|
-
expressions.push(item);
|
|
1068
|
-
return;
|
|
1069
|
-
}
|
|
1070
|
-
const unpackedType = evaluator.getCachedType(item.d.expr);
|
|
1071
|
-
const entryCount = unpackedType && (0, types_1.isClassInstance)(unpackedType) && (0, typeUtils_1.isTupleClass)(unpackedType)
|
|
1072
|
-
? unpackedType.priv.tupleTypeArgs?.length ?? 1
|
|
1073
|
-
: 1;
|
|
1074
|
-
for (let index = 0; index < entryCount; index++) {
|
|
1075
|
-
expressions.push(undefined);
|
|
1076
|
-
}
|
|
1077
|
-
});
|
|
1078
|
-
if (expressions.length === subtype.priv.tupleTypeArgs.length) {
|
|
1079
|
-
entryExpressions = expressions;
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
subtype.priv.tupleTypeArgs.forEach((tupleEntry, index) => {
|
|
1083
|
-
addClassTypesRecursive(tupleEntry.type, isAmbiguousType, isUnboundedEntry || tupleEntry.isUnbounded, recursionCount + 1, entryExpressions?.[index]);
|
|
1084
|
-
});
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
else {
|
|
1088
|
-
addClassTypesToList([subtype], isAmbiguousType, isUnboundedEntry);
|
|
1089
|
-
}
|
|
1090
|
-
});
|
|
1091
|
-
};
|
|
1092
|
-
addClassTypesRecursive(argType, /* isAmbiguous */ false, /* isUnboundedEntry */ false, 0, argExpr);
|
|
1093
|
-
return foundNonClassType ? undefined : { classTypes, negativeClassTypes };
|
|
640
|
+
return TypeEvaluatorNarrowing.getIsInstanceClassTypes(evaluator, argType, argExpr);
|
|
1094
641
|
}
|
|
1095
642
|
function narrowTypeForInstanceOrSubclass(evaluator, type, filterTypes, isInstanceCheck, isTypeIsCheck, isPositiveTest, errorNode, nodeInfo) {
|
|
1096
|
-
|
|
1097
|
-
// A custom subclass check can reject even the exact same final class.
|
|
1098
|
-
// Preserve the existing narrowing policy for non-final classes, including ABCs.
|
|
1099
|
-
filterTypes = filterTypes.filter((filterType) => {
|
|
1100
|
-
const concreteFilterType = evaluator.makeTopLevelTypeVarsConcrete(filterType);
|
|
1101
|
-
return (!(0, types_1.isInstantiableClass)(concreteFilterType) ||
|
|
1102
|
-
!types_1.ClassType.isFinal(concreteFilterType) ||
|
|
1103
|
-
!(0, operations_1.customMetaclassSupportsMethod)(concreteFilterType, '__subclasscheck__'));
|
|
1104
|
-
});
|
|
1105
|
-
}
|
|
1106
|
-
if (!isPositiveTest && filterTypes.length === 0) {
|
|
1107
|
-
return type;
|
|
1108
|
-
}
|
|
1109
|
-
// First try with intersection types disallowed.
|
|
1110
|
-
const narrowedType = narrowTypeForInstanceOrSubclassInternal(evaluator, type, filterTypes, isInstanceCheck, isTypeIsCheck, isPositiveTest,
|
|
1111
|
-
/* allowIntersections */ false, errorNode, nodeInfo);
|
|
1112
|
-
if (!(0, types_1.isNever)(narrowedType)) {
|
|
1113
|
-
return narrowedType;
|
|
1114
|
-
}
|
|
1115
|
-
// Try again with intersection types allowed.
|
|
1116
|
-
return narrowTypeForInstanceOrSubclassInternal(evaluator, type, filterTypes, isInstanceCheck, isTypeIsCheck, isPositiveTest,
|
|
1117
|
-
/* allowIntersections */ true, errorNode, nodeInfo);
|
|
643
|
+
return TypeEvaluatorNarrowing.narrowTypeForInstanceOrSubclass(evaluator, type, filterTypes, isInstanceCheck, isTypeIsCheck, isPositiveTest, errorNode, nodeInfo, narrowTypeForInstanceOrSubclassInternal);
|
|
1118
644
|
}
|
|
1119
645
|
function narrowTypeForInstanceOrSubclassInternal(evaluator, type, filterTypes, isInstanceCheck, isTypeIsCheck, isPositiveTest, allowIntersections, errorNode, nodeInfo) {
|
|
1120
646
|
const result = (0, typeUtils_1.mapSubtypes)(type, (subtype) => {
|
|
@@ -1594,756 +1120,91 @@ function intersectTupleTypes(type1, type2) {
|
|
|
1594
1120
|
}
|
|
1595
1121
|
// Attempts to narrow a union of tuples based on their known length.
|
|
1596
1122
|
function narrowTypeForTupleLength(evaluator, referenceType, lengthValue, isPositiveTest, isLessThanCheck) {
|
|
1597
|
-
return (
|
|
1598
|
-
const concreteSubtype = evaluator.makeTopLevelTypeVarsConcrete(subtype);
|
|
1599
|
-
// If it's not a tuple, we can't narrow it.
|
|
1600
|
-
if (!(0, types_1.isClassInstance)(concreteSubtype) ||
|
|
1601
|
-
!(0, typeUtils_1.isTupleClass)(concreteSubtype) ||
|
|
1602
|
-
!concreteSubtype.priv.tupleTypeArgs) {
|
|
1603
|
-
return subtype;
|
|
1604
|
-
}
|
|
1605
|
-
// If the tuple contains a TypeVarTuple, we can't narrow it.
|
|
1606
|
-
if (concreteSubtype.priv.tupleTypeArgs.some((typeArg) => (0, types_1.isUnpackedTypeVarTuple)(typeArg.type))) {
|
|
1607
|
-
return subtype;
|
|
1608
|
-
}
|
|
1609
|
-
// If the tuple contains no unbounded elements, then we know its length exactly.
|
|
1610
|
-
if (!concreteSubtype.priv.tupleTypeArgs.some((typeArg) => typeArg.isUnbounded)) {
|
|
1611
|
-
const tupleLengthMatches = isLessThanCheck
|
|
1612
|
-
? concreteSubtype.priv.tupleTypeArgs.length < lengthValue
|
|
1613
|
-
: concreteSubtype.priv.tupleTypeArgs.length === lengthValue;
|
|
1614
|
-
return tupleLengthMatches === isPositiveTest ? subtype : undefined;
|
|
1615
|
-
}
|
|
1616
|
-
// The tuple contains a "...". We'll expand this into as many elements as
|
|
1617
|
-
// necessary to match the lengthValue.
|
|
1618
|
-
const elementsToAdd = lengthValue - concreteSubtype.priv.tupleTypeArgs.length + 1;
|
|
1619
|
-
if (!isLessThanCheck) {
|
|
1620
|
-
// If the specified length is smaller than the minimum length of this tuple,
|
|
1621
|
-
// we can rule it out for a positive test and rule it in for a negative test.
|
|
1622
|
-
if (elementsToAdd < 0) {
|
|
1623
|
-
return isPositiveTest ? undefined : subtype;
|
|
1624
|
-
}
|
|
1625
|
-
if (!isPositiveTest) {
|
|
1626
|
-
// If this is an equality check for the minimum length (e.g.
|
|
1627
|
-
// "len(x) == 0"), we can expand the minimum length by one).
|
|
1628
|
-
const minLen = concreteSubtype.priv.tupleTypeArgs.length - 1;
|
|
1629
|
-
if (lengthValue === minLen) {
|
|
1630
|
-
return expandUnboundedTupleElement(concreteSubtype, 1, /* keepUnbounded */ true);
|
|
1631
|
-
}
|
|
1632
|
-
return subtype;
|
|
1633
|
-
}
|
|
1634
|
-
return expandUnboundedTupleElement(concreteSubtype, elementsToAdd, /* keepUnbounded */ false);
|
|
1635
|
-
}
|
|
1636
|
-
// If this is a tuple related to an "*args: P.args" parameter, don't expand it.
|
|
1637
|
-
if ((0, types_1.isParamSpec)(subtype) && subtype.priv.paramSpecAccess) {
|
|
1638
|
-
return subtype;
|
|
1639
|
-
}
|
|
1640
|
-
// Place an upper limit on the number of union subtypes we
|
|
1641
|
-
// will expand the tuple to.
|
|
1642
|
-
const maxTupleUnionExpansion = 32;
|
|
1643
|
-
if (elementsToAdd > maxTupleUnionExpansion) {
|
|
1644
|
-
return subtype;
|
|
1645
|
-
}
|
|
1646
|
-
if (isPositiveTest) {
|
|
1647
|
-
if (elementsToAdd < 1) {
|
|
1648
|
-
return undefined;
|
|
1649
|
-
}
|
|
1650
|
-
const typesToCombine = [];
|
|
1651
|
-
for (let i = 0; i < elementsToAdd; i++) {
|
|
1652
|
-
typesToCombine.push(expandUnboundedTupleElement(concreteSubtype, i, /* keepUnbounded */ false));
|
|
1653
|
-
}
|
|
1654
|
-
return (0, types_1.combineTypes)(typesToCombine);
|
|
1655
|
-
}
|
|
1656
|
-
return expandUnboundedTupleElement(concreteSubtype, elementsToAdd, /* keepUnbounded */ true);
|
|
1657
|
-
});
|
|
1123
|
+
return TypeEvaluatorNarrowing.narrowTypeForTupleLength(evaluator, referenceType, lengthValue, isPositiveTest, isLessThanCheck);
|
|
1658
1124
|
}
|
|
1659
1125
|
// Expands a tuple type that contains an unbounded element to include
|
|
1660
1126
|
// multiple bounded elements of that same type in place of (or in addition
|
|
1661
1127
|
// to) the unbounded element.
|
|
1662
|
-
function expandUnboundedTupleElement(tupleType, elementsToAdd, keepUnbounded) {
|
|
1663
|
-
const tupleTypeArgs = [];
|
|
1664
|
-
tupleType.priv.tupleTypeArgs.forEach((typeArg) => {
|
|
1665
|
-
if (!typeArg.isUnbounded) {
|
|
1666
|
-
tupleTypeArgs.push(typeArg);
|
|
1667
|
-
}
|
|
1668
|
-
else {
|
|
1669
|
-
for (let i = 0; i < elementsToAdd; i++) {
|
|
1670
|
-
tupleTypeArgs.push({ isUnbounded: false, type: typeArg.type });
|
|
1671
|
-
}
|
|
1672
|
-
if (keepUnbounded) {
|
|
1673
|
-
tupleTypeArgs.push(typeArg);
|
|
1674
|
-
}
|
|
1675
|
-
}
|
|
1676
|
-
});
|
|
1677
|
-
return (0, typeUtils_1.specializeTupleClass)(tupleType, tupleTypeArgs);
|
|
1678
|
-
}
|
|
1679
1128
|
// Attempts to narrow a type (make it more constrained) based on an "in" binary operator.
|
|
1680
1129
|
function narrowTypeForContainerType(evaluator, referenceType, containerType, isPositiveTest) {
|
|
1681
|
-
|
|
1682
|
-
const elementType = getElementTypeForContainerNarrowing(containerType);
|
|
1683
|
-
if (!elementType) {
|
|
1684
|
-
return referenceType;
|
|
1685
|
-
}
|
|
1686
|
-
return narrowTypeForContainerElementType(evaluator, referenceType, evaluator.makeTopLevelTypeVarsConcrete(elementType));
|
|
1687
|
-
}
|
|
1688
|
-
// Narrowing in the negative case is possible only with tuples
|
|
1689
|
-
// with a known length.
|
|
1690
|
-
if (!(0, types_1.isClassInstance)(containerType) ||
|
|
1691
|
-
!types_1.ClassType.isBuiltIn(containerType, 'tuple') ||
|
|
1692
|
-
!containerType.priv.tupleTypeArgs) {
|
|
1693
|
-
return referenceType;
|
|
1694
|
-
}
|
|
1695
|
-
// Determine which tuple types can be eliminated. Only "None" and
|
|
1696
|
-
// literal types can be handled here.
|
|
1697
|
-
const typesToEliminate = [];
|
|
1698
|
-
containerType.priv.tupleTypeArgs.forEach((tupleEntry) => {
|
|
1699
|
-
if (!tupleEntry.isUnbounded) {
|
|
1700
|
-
if ((0, typeUtils_1.isNoneInstance)(tupleEntry.type)) {
|
|
1701
|
-
typesToEliminate.push(tupleEntry.type);
|
|
1702
|
-
}
|
|
1703
|
-
else if ((0, types_1.isClassInstance)(tupleEntry.type) && (0, typeUtils_1.isLiteralType)(tupleEntry.type)) {
|
|
1704
|
-
typesToEliminate.push(tupleEntry.type);
|
|
1705
|
-
}
|
|
1706
|
-
}
|
|
1707
|
-
});
|
|
1708
|
-
if (typesToEliminate.length === 0) {
|
|
1709
|
-
return referenceType;
|
|
1710
|
-
}
|
|
1711
|
-
return (0, typeUtils_1.mapSubtypes)(referenceType, (referenceSubtype) => {
|
|
1712
|
-
referenceSubtype = evaluator.makeTopLevelTypeVarsConcrete(referenceSubtype);
|
|
1713
|
-
if ((0, types_1.isClassInstance)(referenceSubtype) && referenceSubtype.priv.literalValue === undefined) {
|
|
1714
|
-
// If we're able to enumerate all possible literal values
|
|
1715
|
-
// (for bool or enum), we can eliminate all others in a negative test.
|
|
1716
|
-
const allLiteralTypes = enumerateLiteralsForType(evaluator, referenceSubtype);
|
|
1717
|
-
if (allLiteralTypes && allLiteralTypes.length > 0) {
|
|
1718
|
-
return (0, types_1.combineTypes)(allLiteralTypes.filter((type) => !typesToEliminate.some((t) => (0, types_1.isTypeSame)(t, type))));
|
|
1719
|
-
}
|
|
1720
|
-
}
|
|
1721
|
-
if (typesToEliminate.some((t) => (0, types_1.isTypeSame)(t, referenceSubtype))) {
|
|
1722
|
-
return undefined;
|
|
1723
|
-
}
|
|
1724
|
-
return referenceSubtype;
|
|
1725
|
-
});
|
|
1130
|
+
return TypeEvaluatorNarrowing.narrowTypeForContainerType(evaluator, referenceType, containerType, isPositiveTest);
|
|
1726
1131
|
}
|
|
1727
1132
|
function getElementTypeForContainerNarrowing(containerType) {
|
|
1728
|
-
|
|
1729
|
-
const supportedContainers = ['list', 'set', 'frozenset', 'deque', 'tuple', 'dict', 'defaultdict', 'OrderedDict'];
|
|
1730
|
-
if (!(0, types_1.isClassInstance)(containerType) || !types_1.ClassType.isBuiltIn(containerType, supportedContainers)) {
|
|
1731
|
-
return undefined;
|
|
1732
|
-
}
|
|
1733
|
-
if (!containerType.priv.typeArgs || containerType.priv.typeArgs.length < 1) {
|
|
1734
|
-
return undefined;
|
|
1735
|
-
}
|
|
1736
|
-
let elementType = containerType.priv.typeArgs[0];
|
|
1737
|
-
if ((0, typeUtils_1.isTupleClass)(containerType) && containerType.priv.tupleTypeArgs) {
|
|
1738
|
-
elementType = (0, types_1.combineTypes)(containerType.priv.tupleTypeArgs.map((t) => t.type));
|
|
1739
|
-
}
|
|
1740
|
-
return elementType;
|
|
1133
|
+
return TypeEvaluatorNarrowing.getElementTypeForContainerNarrowing(containerType);
|
|
1741
1134
|
}
|
|
1742
1135
|
function narrowTypeForContainerElementType(evaluator, referenceType, elementType) {
|
|
1743
|
-
return
|
|
1744
|
-
return (0, typeUtils_1.mapSubtypes)(elementType, (elementSubtype) => {
|
|
1745
|
-
if ((0, types_1.isAnyOrUnknown)(elementSubtype)) {
|
|
1746
|
-
return referenceSubtype;
|
|
1747
|
-
}
|
|
1748
|
-
// If the two types are disjoint (i.e. are not comparable), eliminate this subtype.
|
|
1749
|
-
if (!evaluator.isTypeComparable(elementSubtype, referenceSubtype)) {
|
|
1750
|
-
return undefined;
|
|
1751
|
-
}
|
|
1752
|
-
// If one of the two types is a literal, we can narrow to that type.
|
|
1753
|
-
if ((0, types_1.isClassInstance)(elementSubtype) &&
|
|
1754
|
-
((0, typeUtils_1.isLiteralLikeType)(elementSubtype) || (0, typeUtils_1.isNoneInstance)(elementSubtype)) &&
|
|
1755
|
-
evaluator.assignType(referenceSubtype, elementSubtype)) {
|
|
1756
|
-
return (0, typeUtils_1.stripTypeForm)((0, typeUtils_1.addConditionToType)(elementSubtype, referenceSubtype.props?.condition));
|
|
1757
|
-
}
|
|
1758
|
-
if ((0, types_1.isClassInstance)(referenceSubtype) &&
|
|
1759
|
-
((0, typeUtils_1.isLiteralLikeType)(referenceSubtype) || (0, typeUtils_1.isNoneInstance)(referenceSubtype)) &&
|
|
1760
|
-
evaluator.assignType(elementSubtype, referenceSubtype)) {
|
|
1761
|
-
return (0, typeUtils_1.stripTypeForm)((0, typeUtils_1.addConditionToType)(referenceSubtype, elementSubtype.props?.condition));
|
|
1762
|
-
}
|
|
1763
|
-
// If the element type is a known class object that is assignable to
|
|
1764
|
-
// the reference type, we can narrow to that class object.
|
|
1765
|
-
if ((0, types_1.isInstantiableClass)(elementSubtype) &&
|
|
1766
|
-
!elementSubtype.priv.includeSubclasses &&
|
|
1767
|
-
evaluator.assignType(referenceSubtype, elementSubtype)) {
|
|
1768
|
-
return (0, typeUtils_1.stripTypeForm)((0, typeUtils_1.addConditionToType)(elementSubtype, referenceSubtype.props?.condition));
|
|
1769
|
-
}
|
|
1770
|
-
// It's not safe to narrow.
|
|
1771
|
-
return referenceSubtype;
|
|
1772
|
-
});
|
|
1773
|
-
});
|
|
1136
|
+
return TypeEvaluatorNarrowing.narrowTypeForContainerElementType(evaluator, referenceType, elementType);
|
|
1774
1137
|
}
|
|
1775
1138
|
// Attempts to narrow a type based on whether it is a TypedDict with
|
|
1776
1139
|
// a literal key value.
|
|
1777
1140
|
function narrowTypeForTypedDictKey(evaluator, referenceType, literalKey, isPositiveTest) {
|
|
1778
|
-
|
|
1779
|
-
/* options */ undefined, (subtype, unexpandedSubtype) => {
|
|
1780
|
-
if ((0, types_1.isParamSpec)(unexpandedSubtype)) {
|
|
1781
|
-
return unexpandedSubtype;
|
|
1782
|
-
}
|
|
1783
|
-
if ((0, types_1.isClassInstance)(subtype) && types_1.ClassType.isTypedDictClass(subtype)) {
|
|
1784
|
-
const entries = (0, typedDicts_1.getTypedDictMembersForClass)(evaluator, subtype, /* allowNarrowed */ true);
|
|
1785
|
-
const knownEntry = entries.knownItems.get(literalKey.priv.literalValue);
|
|
1786
|
-
const tdEntry = knownEntry ?? entries.extraItems;
|
|
1787
|
-
if (isPositiveTest) {
|
|
1788
|
-
// The code that is commented out below implements the behavior that is technically
|
|
1789
|
-
// correct, but until we PEP 728 is ratified and we have a way to express "extra items"
|
|
1790
|
-
// and closed TypedDicts, we'll preserve the older (less correct) behavior to enable
|
|
1791
|
-
// narrowing of TypedDicts based on checks for specific keys.
|
|
1792
|
-
// TODO - remove this behavior once PEP 728 is accepted and the feature is no
|
|
1793
|
-
// longer experimental.
|
|
1794
|
-
if (!tdEntry) {
|
|
1795
|
-
return undefined;
|
|
1796
|
-
}
|
|
1797
|
-
// if (!tdEntry) {
|
|
1798
|
-
// // If there is no TD entry for this key and no "extra items" defined,
|
|
1799
|
-
// // we have to assume that the TypedDict may contain extra items, so
|
|
1800
|
-
// // narrowing it isn't possible in this case.
|
|
1801
|
-
// return subtype;
|
|
1802
|
-
// }
|
|
1803
|
-
if ((0, types_1.isNever)(tdEntry.valueType)) {
|
|
1804
|
-
// If the entry is typed as Never or the "extra items" is typed as Never,
|
|
1805
|
-
// then this key cannot be present in the TypedDict, and we can eliminate it.
|
|
1806
|
-
// A closed TypedDict has an "extra items" of Never, so this is what allows
|
|
1807
|
-
// a key check to discriminate between closed TypedDicts.
|
|
1808
|
-
return undefined;
|
|
1809
|
-
}
|
|
1810
|
-
// If the entry is currently not required and not marked provided, we can mark
|
|
1811
|
-
// it as provided after this guard expression confirms it is.
|
|
1812
|
-
if (tdEntry.isRequired || tdEntry.isProvided) {
|
|
1813
|
-
return subtype;
|
|
1814
|
-
}
|
|
1815
|
-
const newNarrowedEntriesMap = new Map(subtype.priv.typedDictNarrowedEntries ?? []);
|
|
1816
|
-
// Add the new entry.
|
|
1817
|
-
newNarrowedEntriesMap.set(literalKey.priv.literalValue, {
|
|
1818
|
-
valueType: tdEntry.valueType,
|
|
1819
|
-
isReadOnly: tdEntry.isReadOnly,
|
|
1820
|
-
isRequired: false,
|
|
1821
|
-
isProvided: true,
|
|
1822
|
-
});
|
|
1823
|
-
// Clone the TypedDict object with the new entries.
|
|
1824
|
-
return types_1.ClassType.cloneAsInstance(types_1.ClassType.cloneForNarrowedTypedDictEntries(types_1.ClassType.cloneAsInstantiable(subtype), newNarrowedEntriesMap));
|
|
1825
|
-
}
|
|
1826
|
-
else {
|
|
1827
|
-
if (!knownEntry) {
|
|
1828
|
-
// The key is not one of the known items, so it can be supplied
|
|
1829
|
-
// only through an "extra items" entry, which says nothing about
|
|
1830
|
-
// whether this particular key is present. Its absence is therefore
|
|
1831
|
-
// consistent with the type, and the subtype cannot be eliminated.
|
|
1832
|
-
return subtype;
|
|
1833
|
-
}
|
|
1834
|
-
return knownEntry.isRequired || knownEntry.isProvided ? undefined : subtype;
|
|
1835
|
-
}
|
|
1836
|
-
}
|
|
1837
|
-
return subtype;
|
|
1838
|
-
});
|
|
1839
|
-
return narrowedType;
|
|
1141
|
+
return TypeEvaluatorNarrowing.narrowTypeForTypedDictKey(evaluator, referenceType, literalKey, isPositiveTest);
|
|
1840
1142
|
}
|
|
1841
1143
|
// Attempts to narrow a TypedDict type based on a comparison (equal or not
|
|
1842
1144
|
// equal) between a discriminating entry type that has a declared literal
|
|
1843
1145
|
// type to a literal value.
|
|
1844
1146
|
function narrowTypeForDiscriminatedDictEntryComparison(evaluator, referenceType, indexLiteralType, literalType, isPositiveTest) {
|
|
1845
|
-
|
|
1846
|
-
const narrowedType = (0, typeUtils_1.mapSubtypes)(referenceType, (subtype) => {
|
|
1847
|
-
if ((0, types_1.isClassInstance)(subtype) && types_1.ClassType.isTypedDictClass(subtype)) {
|
|
1848
|
-
const symbolMap = (0, typedDicts_1.getTypedDictMembersForClass)(evaluator, subtype);
|
|
1849
|
-
const tdEntry = symbolMap.knownItems.get(indexLiteralType.priv.literalValue);
|
|
1850
|
-
if (tdEntry && (0, typeUtils_1.isLiteralTypeOrUnion)(tdEntry.valueType)) {
|
|
1851
|
-
if (isPositiveTest) {
|
|
1852
|
-
let foundMatch = false;
|
|
1853
|
-
(0, typeUtils_1.doForEachSubtype)(literalType, (literalSubtype) => {
|
|
1854
|
-
if (evaluator.assignType(tdEntry.valueType, literalSubtype)) {
|
|
1855
|
-
foundMatch = true;
|
|
1856
|
-
}
|
|
1857
|
-
});
|
|
1858
|
-
return foundMatch ? subtype : undefined;
|
|
1859
|
-
}
|
|
1860
|
-
else {
|
|
1861
|
-
let foundNonMatch = false;
|
|
1862
|
-
(0, typeUtils_1.doForEachSubtype)(literalType, (literalSubtype) => {
|
|
1863
|
-
if (!evaluator.assignType(literalSubtype, tdEntry.valueType)) {
|
|
1864
|
-
foundNonMatch = true;
|
|
1865
|
-
}
|
|
1866
|
-
});
|
|
1867
|
-
return foundNonMatch ? subtype : undefined;
|
|
1868
|
-
}
|
|
1869
|
-
}
|
|
1870
|
-
}
|
|
1871
|
-
canNarrow = false;
|
|
1872
|
-
return subtype;
|
|
1873
|
-
});
|
|
1874
|
-
return canNarrow ? narrowedType : referenceType;
|
|
1147
|
+
return TypeEvaluatorNarrowing.narrowTypeForDiscriminatedDictEntryComparison(evaluator, referenceType, indexLiteralType, literalType, isPositiveTest);
|
|
1875
1148
|
}
|
|
1876
1149
|
function narrowTypeForDiscriminatedTupleComparison(evaluator, referenceType, indexLiteralType, literalType, isPositiveTest) {
|
|
1877
|
-
|
|
1878
|
-
const narrowedType = (0, typeUtils_1.mapSubtypes)(referenceType, (subtype) => {
|
|
1879
|
-
if ((0, types_1.isClassInstance)(subtype) &&
|
|
1880
|
-
types_1.ClassType.isTupleClass(subtype) &&
|
|
1881
|
-
!(0, typeUtils_1.isUnboundedTupleClass)(subtype) &&
|
|
1882
|
-
typeof indexLiteralType.priv.literalValue === 'number' &&
|
|
1883
|
-
(0, types_1.isClassInstance)(literalType)) {
|
|
1884
|
-
const indexValue = indexLiteralType.priv.literalValue;
|
|
1885
|
-
if (subtype.priv.tupleTypeArgs && indexValue >= 0 && indexValue < subtype.priv.tupleTypeArgs.length) {
|
|
1886
|
-
const tupleEntryType = subtype.priv.tupleTypeArgs[indexValue]?.type;
|
|
1887
|
-
if (tupleEntryType && (0, typeUtils_1.isLiteralTypeOrUnion)(tupleEntryType)) {
|
|
1888
|
-
if (isPositiveTest) {
|
|
1889
|
-
return evaluator.assignType(tupleEntryType, literalType) ? subtype : undefined;
|
|
1890
|
-
}
|
|
1891
|
-
else {
|
|
1892
|
-
return evaluator.assignType(literalType, tupleEntryType) ? undefined : subtype;
|
|
1893
|
-
}
|
|
1894
|
-
}
|
|
1895
|
-
}
|
|
1896
|
-
}
|
|
1897
|
-
canNarrow = false;
|
|
1898
|
-
return subtype;
|
|
1899
|
-
});
|
|
1900
|
-
return canNarrow ? narrowedType : referenceType;
|
|
1150
|
+
return TypeEvaluatorNarrowing.narrowTypeForDiscriminatedTupleComparison(evaluator, referenceType, indexLiteralType, literalType, isPositiveTest);
|
|
1901
1151
|
}
|
|
1902
1152
|
// Attempts to narrow a type based on a comparison (equal or not equal)
|
|
1903
1153
|
// between a discriminating field that has a declared literal type to a
|
|
1904
1154
|
// literal value.
|
|
1905
1155
|
function narrowTypeForDiscriminatedLiteralFieldComparison(evaluator, referenceType, memberName, literalType, isPositiveTest) {
|
|
1906
|
-
|
|
1907
|
-
let memberInfo;
|
|
1908
|
-
if ((0, types_1.isClassInstance)(subtype)) {
|
|
1909
|
-
memberInfo = (0, typeUtils_1.lookUpObjectMember)(subtype, memberName);
|
|
1910
|
-
}
|
|
1911
|
-
else if ((0, types_1.isInstantiableClass)(subtype)) {
|
|
1912
|
-
memberInfo = (0, typeUtils_1.lookUpClassMember)(subtype, memberName);
|
|
1913
|
-
}
|
|
1914
|
-
if (memberInfo && memberInfo.isTypeDeclared) {
|
|
1915
|
-
let memberType = evaluator.getTypeOfMember(memberInfo);
|
|
1916
|
-
// Handle the case where the field is a property
|
|
1917
|
-
// that has a declared literal return type for its getter.
|
|
1918
|
-
if ((0, types_1.isClassInstance)(subtype) && (0, types_1.isClassInstance)(memberType) && (0, typeUtils_1.isProperty)(memberType)) {
|
|
1919
|
-
const getterType = memberType.priv.fgetInfo?.methodType;
|
|
1920
|
-
if (getterType && (0, types_1.isFunction)(getterType) && getterType.shared.declaredReturnType) {
|
|
1921
|
-
const getterReturnType = types_1.FunctionType.getEffectiveReturnType(getterType);
|
|
1922
|
-
if (getterReturnType) {
|
|
1923
|
-
memberType = getterReturnType;
|
|
1924
|
-
}
|
|
1925
|
-
}
|
|
1926
|
-
}
|
|
1927
|
-
if ((0, typeUtils_1.isLiteralTypeOrUnion)(memberType, /* allowNone */ true)) {
|
|
1928
|
-
if (isPositiveTest) {
|
|
1929
|
-
return evaluator.assignType(memberType, literalType) ? subtype : undefined;
|
|
1930
|
-
}
|
|
1931
|
-
else {
|
|
1932
|
-
return evaluator.assignType(literalType, memberType) ? undefined : subtype;
|
|
1933
|
-
}
|
|
1934
|
-
}
|
|
1935
|
-
}
|
|
1936
|
-
return subtype;
|
|
1937
|
-
});
|
|
1938
|
-
return narrowedType;
|
|
1156
|
+
return TypeEvaluatorNarrowing.narrowTypeForDiscriminatedLiteralFieldComparison(evaluator, referenceType, memberName, literalType, isPositiveTest);
|
|
1939
1157
|
}
|
|
1940
1158
|
// Attempts to narrow a type based on a comparison (equal or not equal)
|
|
1941
1159
|
// between a discriminating field that has a declared None type to a
|
|
1942
1160
|
// None.
|
|
1943
1161
|
function narrowTypeForDiscriminatedFieldNoneComparison(evaluator, referenceType, memberName, isPositiveTest) {
|
|
1944
|
-
return (
|
|
1945
|
-
let memberInfo;
|
|
1946
|
-
if ((0, types_1.isClassInstance)(subtype)) {
|
|
1947
|
-
memberInfo = (0, typeUtils_1.lookUpObjectMember)(subtype, memberName);
|
|
1948
|
-
}
|
|
1949
|
-
else if ((0, types_1.isInstantiableClass)(subtype)) {
|
|
1950
|
-
memberInfo = (0, typeUtils_1.lookUpClassMember)(subtype, memberName);
|
|
1951
|
-
}
|
|
1952
|
-
if (memberInfo && memberInfo.isTypeDeclared) {
|
|
1953
|
-
// Check the declared type before narrowing, since the member type
|
|
1954
|
-
// below will be concretized and lose descriptor identity.
|
|
1955
|
-
const declaredType = evaluator.getDeclaredTypeOfSymbol(memberInfo.symbol)?.type;
|
|
1956
|
-
if (!declaredType) {
|
|
1957
|
-
// isTypeDeclared is true but the type couldn't be resolved (e.g. an
|
|
1958
|
-
// unresolvable stub). Conservatively skip narrowing rather than risk
|
|
1959
|
-
// incorrectly eliminating a descriptor-typed member.
|
|
1960
|
-
return subtype;
|
|
1961
|
-
}
|
|
1962
|
-
// Check if any subtype of the declared type is a descriptor or property.
|
|
1963
|
-
// isMaybeDescriptorInstance handles declared types in instance form (ClassInstance),
|
|
1964
|
-
// while isMaybeDescriptorClass handles declared types in instantiable form
|
|
1965
|
-
// (InstantiableClass), which occurs when the annotation refers to the class object itself.
|
|
1966
|
-
// This check applies to both positive and negative test paths: descriptor __get__
|
|
1967
|
-
// return values don't reflect stored values regardless of test polarity.
|
|
1968
|
-
const isDescriptorOrProperty = (0, typeUtils_1.someSubtypes)(declaredType, (declaredSubtype) => (0, typeUtils_1.isProperty)(declaredSubtype) ||
|
|
1969
|
-
(0, typeUtils_1.isMaybeDescriptorInstance)(declaredSubtype) ||
|
|
1970
|
-
(0, typeUtils_1.isMaybeDescriptorClass)(declaredSubtype));
|
|
1971
|
-
if (isDescriptorOrProperty) {
|
|
1972
|
-
return subtype;
|
|
1973
|
-
}
|
|
1974
|
-
const memberType = evaluator.makeTopLevelTypeVarsConcrete(evaluator.getTypeOfMember(memberInfo));
|
|
1975
|
-
let canNarrow = true;
|
|
1976
|
-
if (isPositiveTest) {
|
|
1977
|
-
(0, typeUtils_1.doForEachSubtype)(memberType, (memberSubtype) => {
|
|
1978
|
-
memberSubtype = evaluator.makeTopLevelTypeVarsConcrete(memberSubtype);
|
|
1979
|
-
if ((0, types_1.isAnyOrUnknown)(memberSubtype) || (0, typeUtils_1.isNoneInstance)(memberSubtype) || (0, types_1.isNever)(memberSubtype)) {
|
|
1980
|
-
canNarrow = false;
|
|
1981
|
-
}
|
|
1982
|
-
});
|
|
1983
|
-
}
|
|
1984
|
-
else {
|
|
1985
|
-
canNarrow = (0, typeUtils_1.isNoneInstance)(memberType);
|
|
1986
|
-
}
|
|
1987
|
-
if (canNarrow) {
|
|
1988
|
-
return undefined;
|
|
1989
|
-
}
|
|
1990
|
-
}
|
|
1991
|
-
return subtype;
|
|
1992
|
-
});
|
|
1162
|
+
return TypeEvaluatorNarrowing.narrowTypeForDiscriminatedFieldNoneComparison(evaluator, referenceType, memberName, isPositiveTest);
|
|
1993
1163
|
}
|
|
1164
|
+
// Determines whether two classes have incompatible disjoint bases (PEP 800),
|
|
1165
|
+
// in which case no class can derive from both of them.
|
|
1166
|
+
// Fill gradual holes without replacing facts inferred from the guarded value.
|
|
1994
1167
|
// Attempts to narrow a type based on a "type(x) is y" or "type(x) is not y" check.
|
|
1995
|
-
function narrowTypeForTypeIs(evaluator, type, classTypes, isPositiveTest) {
|
|
1996
|
-
|
|
1997
|
-
// when there are more than one class types.
|
|
1998
|
-
if (!isPositiveTest && classTypes.length > 1) {
|
|
1999
|
-
return type;
|
|
2000
|
-
}
|
|
2001
|
-
const typesToCombine = classTypes.map((classType) => {
|
|
2002
|
-
return evaluator.mapSubtypesExpandTypeVars(type,
|
|
2003
|
-
/* options */ undefined, (subtype, unexpandedSubtype) => {
|
|
2004
|
-
if ((0, types_1.isClassInstance)(subtype)) {
|
|
2005
|
-
const matches = types_1.ClassType.isDerivedFrom(classType, types_1.ClassType.cloneAsInstantiable(subtype));
|
|
2006
|
-
if (isPositiveTest) {
|
|
2007
|
-
if (matches) {
|
|
2008
|
-
if (types_1.ClassType.isSameGenericClass(types_1.ClassType.cloneAsInstantiable(subtype), classType)) {
|
|
2009
|
-
return (0, typeUtils_1.addConditionToType)(subtype, (0, typeUtils_1.getTypeCondition)(classType));
|
|
2010
|
-
}
|
|
2011
|
-
return (0, typeUtils_1.addConditionToType)(types_1.ClassType.cloneAsInstance(classType), subtype.props?.condition);
|
|
2012
|
-
}
|
|
2013
|
-
if (!classType.priv.includeSubclasses) {
|
|
2014
|
-
return undefined;
|
|
2015
|
-
}
|
|
2016
|
-
if (!(0, types_1.isTypeVar)(unexpandedSubtype) || !types_1.TypeVarType.isSelf(unexpandedSubtype)) {
|
|
2017
|
-
return (0, typeUtils_1.addConditionToType)(subtype, classType.props?.condition);
|
|
2018
|
-
}
|
|
2019
|
-
}
|
|
2020
|
-
if (!classType.priv.includeSubclasses) {
|
|
2021
|
-
// If the class if marked final and it matches, then
|
|
2022
|
-
// we can eliminate it in the negative case.
|
|
2023
|
-
if (matches && types_1.ClassType.isFinal(subtype)) {
|
|
2024
|
-
return undefined;
|
|
2025
|
-
}
|
|
2026
|
-
// We can't eliminate the subtype in the negative
|
|
2027
|
-
// case because it could be a subclass of the type,
|
|
2028
|
-
// in which case `type(x) is y` would fail.
|
|
2029
|
-
return subtype;
|
|
2030
|
-
}
|
|
2031
|
-
}
|
|
2032
|
-
if ((0, types_1.isAnyOrUnknown)(subtype)) {
|
|
2033
|
-
return isPositiveTest
|
|
2034
|
-
? types_1.ClassType.cloneAsInstance((0, typeUtils_1.addConditionToType)(classType, (0, typeUtils_1.getTypeCondition)(subtype)))
|
|
2035
|
-
: subtype;
|
|
2036
|
-
}
|
|
2037
|
-
return unexpandedSubtype;
|
|
2038
|
-
});
|
|
2039
|
-
});
|
|
2040
|
-
return (0, types_1.combineTypes)(typesToCombine);
|
|
2041
|
-
}
|
|
2042
|
-
function hasUnresolvedMetaclassInMro(classType) {
|
|
2043
|
-
return classType.shared.mro.some((baseClass) => !(0, types_1.isInstantiableClass)(baseClass) || !!(baseClass.shared.flags & 134217728 /* ClassTypeFlags.HasUnresolvedMetaclass */));
|
|
1168
|
+
function narrowTypeForTypeIs(evaluator, type, classTypes, isPositiveTest, isIsOperator, classTypeVar, allowLegacyEquality) {
|
|
1169
|
+
return TypeEvaluatorNarrowing.narrowTypeForTypeIs(evaluator, type, classTypes, isPositiveTest, isIsOperator, classTypeVar, allowLegacyEquality);
|
|
2044
1170
|
}
|
|
2045
1171
|
// Determines whether an equality comparison ("==" or "!=") involving the
|
|
2046
1172
|
// specified class object is guaranteed to have identity semantics. This
|
|
2047
1173
|
// requires that the class is statically closed (it cannot be a subclass
|
|
2048
1174
|
// with a different metaclass), is not generic (specialized aliases compare
|
|
2049
1175
|
// by value), and its metaclass does not override "__eq__" or "__ne__".
|
|
2050
|
-
|
|
2051
|
-
if (classType.priv.includeSubclasses && !types_1.ClassType.isFinal(classType)) {
|
|
2052
|
-
return false;
|
|
2053
|
-
}
|
|
2054
|
-
if (classType.shared.typeParams.length > 0) {
|
|
2055
|
-
return false;
|
|
2056
|
-
}
|
|
2057
|
-
const metaclass = classType.shared.effectiveMetaclass;
|
|
2058
|
-
if (!metaclass ||
|
|
2059
|
-
!(0, types_1.isInstantiableClass)(metaclass) ||
|
|
2060
|
-
hasUnresolvedMetaclassInMro(classType) ||
|
|
2061
|
-
hasUnresolvedMetaclassInMro(metaclass)) {
|
|
2062
|
-
return false;
|
|
2063
|
-
}
|
|
2064
|
-
if (types_1.ClassType.isBuiltIn(metaclass, 'type')) {
|
|
2065
|
-
return true;
|
|
2066
|
-
}
|
|
2067
|
-
// Cloned metaclasses share method definitions, but the class-specific checks above cannot be shared.
|
|
2068
|
-
const cachedResult = metaclassEqualityResults.get(metaclass.shared);
|
|
2069
|
-
if (cachedResult !== undefined) {
|
|
2070
|
-
return cachedResult;
|
|
2071
|
-
}
|
|
2072
|
-
const result = !(0, operations_1.customMetaclassSupportsMethod)(classType, '__eq__') && !(0, operations_1.customMetaclassSupportsMethod)(classType, '__ne__');
|
|
2073
|
-
metaclassEqualityResults.set(metaclass.shared, result);
|
|
2074
|
-
return result;
|
|
2075
|
-
}
|
|
1176
|
+
// Legacy forward type(x) equality guards check declared metaclasses without closing subclass hierarchies.
|
|
2076
1177
|
// Attempts to narrow a type based on a comparison with a class using "is" or
|
|
2077
1178
|
// "is not", or "==" or "!=".
|
|
2078
1179
|
function narrowTypeForClassComparison(evaluator, referenceType, classType, isPositiveTest, isIsOperator = true) {
|
|
2079
|
-
|
|
2080
|
-
const metaclassEqualityResults = isIsOperator ? undefined : new Map();
|
|
2081
|
-
if (metaclassEqualityResults && !hasIdentityEqualitySemantics(classType, metaclassEqualityResults)) {
|
|
2082
|
-
return referenceType;
|
|
2083
|
-
}
|
|
2084
|
-
return (0, typeUtils_1.mapSubtypes)(referenceType, (subtype) => {
|
|
2085
|
-
// Multiple class constraints expand to a union with distinct conditions, which equality cannot narrow.
|
|
2086
|
-
if (metaclassEqualityResults &&
|
|
2087
|
-
(0, types_1.isTypeVar)(subtype) &&
|
|
2088
|
-
!subtype.shared.recursiveAlias &&
|
|
2089
|
-
subtype.shared.constraints.length > 1 &&
|
|
2090
|
-
subtype.shared.constraints.every(types_1.isClassInstance)) {
|
|
2091
|
-
return subtype;
|
|
2092
|
-
}
|
|
2093
|
-
let concreteSubtype = evaluator.makeTopLevelTypeVarsConcrete(subtype);
|
|
2094
|
-
if (metaclassEqualityResults &&
|
|
2095
|
-
(!(0, types_1.isInstantiableClass)(concreteSubtype) ||
|
|
2096
|
-
!hasIdentityEqualitySemantics(concreteSubtype, metaclassEqualityResults))) {
|
|
2097
|
-
return subtype;
|
|
2098
|
-
}
|
|
2099
|
-
if (isPositiveTest) {
|
|
2100
|
-
if ((0, types_1.isClassInstance)(concreteSubtype) &&
|
|
2101
|
-
types_1.TypeBase.isInstance(subtype) &&
|
|
2102
|
-
types_1.ClassType.isBuiltIn(concreteSubtype, 'type')) {
|
|
2103
|
-
concreteSubtype =
|
|
2104
|
-
concreteSubtype.priv.typeArgs && concreteSubtype.priv.typeArgs.length > 0
|
|
2105
|
-
? (0, typeUtils_1.convertToInstantiable)(concreteSubtype.priv.typeArgs[0])
|
|
2106
|
-
: types_1.UnknownType.create();
|
|
2107
|
-
}
|
|
2108
|
-
if ((0, types_1.isAnyOrUnknown)(concreteSubtype)) {
|
|
2109
|
-
return (0, typeUtils_1.addConditionToType)(classType, (0, typeUtils_1.getTypeCondition)(concreteSubtype));
|
|
2110
|
-
}
|
|
2111
|
-
if ((0, types_1.isClass)(concreteSubtype)) {
|
|
2112
|
-
if (types_1.TypeBase.isInstance(concreteSubtype)) {
|
|
2113
|
-
if (!isIsOperator) {
|
|
2114
|
-
return subtype;
|
|
2115
|
-
}
|
|
2116
|
-
return types_1.ClassType.isBuiltIn(concreteSubtype, 'object') ? classType : undefined;
|
|
2117
|
-
}
|
|
2118
|
-
const isSuperType = isFilterSuperclass(subtype, concreteSubtype, classType, classType);
|
|
2119
|
-
if (!classType.priv.includeSubclasses) {
|
|
2120
|
-
// Handle the case where the LHS and RHS operands are specific
|
|
2121
|
-
// classes, as opposed to types that represent classes and their
|
|
2122
|
-
// subclasses.
|
|
2123
|
-
if (!concreteSubtype.priv.includeSubclasses) {
|
|
2124
|
-
return types_1.ClassType.isSameGenericClass(concreteSubtype, classType) ? classType : undefined;
|
|
2125
|
-
}
|
|
2126
|
-
if (isSuperType) {
|
|
2127
|
-
return (0, typeUtils_1.addConditionToType)(classType, (0, typeUtils_1.getTypeCondition)(concreteSubtype));
|
|
2128
|
-
}
|
|
2129
|
-
const isSubType = types_1.ClassType.isDerivedFrom(classType, concreteSubtype);
|
|
2130
|
-
if (isSubType) {
|
|
2131
|
-
return (0, typeUtils_1.addConditionToType)(classType, (0, typeUtils_1.getTypeCondition)(concreteSubtype));
|
|
2132
|
-
}
|
|
2133
|
-
return undefined;
|
|
2134
|
-
}
|
|
2135
|
-
if (types_1.ClassType.isFinal(concreteSubtype) && !isSuperType) {
|
|
2136
|
-
return undefined;
|
|
2137
|
-
}
|
|
2138
|
-
}
|
|
2139
|
-
}
|
|
2140
|
-
else {
|
|
2141
|
-
if ((0, types_1.isInstantiableClass)(concreteSubtype) &&
|
|
2142
|
-
types_1.ClassType.isSameGenericClass(classType, concreteSubtype) &&
|
|
2143
|
-
types_1.ClassType.isFinal(classType)) {
|
|
2144
|
-
return undefined;
|
|
2145
|
-
}
|
|
2146
|
-
}
|
|
2147
|
-
return subtype;
|
|
2148
|
-
});
|
|
2149
|
-
}
|
|
2150
|
-
function isFilterSuperclass(varType, concreteVarType, filterType, concreteFilterType) {
|
|
2151
|
-
if ((0, types_1.isTypeVar)(filterType) || concreteFilterType.priv.literalValue !== undefined) {
|
|
2152
|
-
return (0, types_1.isTypeSame)((0, typeUtils_1.convertToInstance)(filterType), varType);
|
|
2153
|
-
}
|
|
2154
|
-
// If the filter type represents all possible subclasses
|
|
2155
|
-
// of a type, we can't make any statements about its superclass
|
|
2156
|
-
// relationship with concreteVarType.
|
|
2157
|
-
if (concreteFilterType.priv.includeSubclasses) {
|
|
2158
|
-
return false;
|
|
2159
|
-
}
|
|
2160
|
-
if (types_1.ClassType.isDerivedFrom(concreteVarType, concreteFilterType)) {
|
|
2161
|
-
return true;
|
|
2162
|
-
}
|
|
2163
|
-
// Handle the special case where the variable type is a TypedDict and
|
|
2164
|
-
// we're filtering against 'dict'. TypedDict isn't derived from dict,
|
|
2165
|
-
// but at runtime, isinstance returns True.
|
|
2166
|
-
if (types_1.ClassType.isBuiltIn(concreteFilterType, 'dict') && types_1.ClassType.isTypedDictClass(concreteVarType)) {
|
|
2167
|
-
return true;
|
|
2168
|
-
}
|
|
2169
|
-
return false;
|
|
1180
|
+
return TypeEvaluatorNarrowing.narrowTypeForClassComparison(evaluator, referenceType, classType, isPositiveTest, isIsOperator);
|
|
2170
1181
|
}
|
|
2171
1182
|
// Determines whether the specified class is an enum class whose members derive
|
|
2172
1183
|
// from a primitive type (e.g. IntEnum or StrEnum). Members of such classes
|
|
2173
1184
|
// compare equal to their underlying primitive value at runtime.
|
|
2174
|
-
function isPrimitiveBackedEnumClass(classType) {
|
|
2175
|
-
if (!types_1.ClassType.isEnumClass(classType)) {
|
|
2176
|
-
return false;
|
|
2177
|
-
}
|
|
2178
|
-
return (0, typeUtils_1.derivesFromStdlibClass)(classType, 'int') || (0, typeUtils_1.derivesFromStdlibClass)(classType, 'str');
|
|
2179
|
-
}
|
|
2180
1185
|
// Determines whether enumType is a primitive-backed enum class and primitiveType
|
|
2181
1186
|
// is the primitive type (int or str) from which it derives.
|
|
2182
|
-
function isPrimitiveBackedEnumAndPrimitive(enumType, primitiveType) {
|
|
2183
|
-
if (types_1.ClassType.isEnumClass(primitiveType) || !isPrimitiveBackedEnumClass(enumType)) {
|
|
2184
|
-
return false;
|
|
2185
|
-
}
|
|
2186
|
-
// Exclude bool, which derives from int but whose literal values are distinct
|
|
2187
|
-
// from the corresponding int literals.
|
|
2188
|
-
if (types_1.ClassType.isBuiltIn(primitiveType, 'int')) {
|
|
2189
|
-
return (0, typeUtils_1.derivesFromStdlibClass)(enumType, 'int');
|
|
2190
|
-
}
|
|
2191
|
-
if (types_1.ClassType.isBuiltIn(primitiveType, 'str')) {
|
|
2192
|
-
return (0, typeUtils_1.derivesFromStdlibClass)(enumType, 'str');
|
|
2193
|
-
}
|
|
2194
|
-
return false;
|
|
2195
|
-
}
|
|
2196
1187
|
// If the specified type is a literal member of a primitive-backed enum class,
|
|
2197
1188
|
// returns the corresponding primitive literal type. Otherwise returns the type
|
|
2198
1189
|
// unmodified.
|
|
2199
|
-
function unwrapPrimitiveBackedEnumLiteral(classType) {
|
|
2200
|
-
const literalValue = classType.priv.literalValue;
|
|
2201
|
-
if (!(literalValue instanceof types_1.EnumLiteral) || !isPrimitiveBackedEnumClass(classType)) {
|
|
2202
|
-
return classType;
|
|
2203
|
-
}
|
|
2204
|
-
const itemType = literalValue.itemType;
|
|
2205
|
-
if ((0, types_1.isClassInstance)(itemType) && itemType.priv.literalValue !== undefined) {
|
|
2206
|
-
return itemType;
|
|
2207
|
-
}
|
|
2208
|
-
return classType;
|
|
2209
|
-
}
|
|
2210
1190
|
// Determines whether two literal types compare equal using the `==` operator at
|
|
2211
1191
|
// runtime. This differs from ClassType.isLiteralValueSame only for members of
|
|
2212
1192
|
// primitive-backed enum classes, which compare equal to their underlying
|
|
2213
1193
|
// primitive values.
|
|
2214
|
-
function isLiteralValueEqualAtRuntime(type1, type2) {
|
|
2215
|
-
return types_1.ClassType.isLiteralValueSame(unwrapPrimitiveBackedEnumLiteral(type1), unwrapPrimitiveBackedEnumLiteral(type2));
|
|
2216
|
-
}
|
|
2217
1194
|
// Attempts to narrow a type (make it more constrained) based on a comparison
|
|
2218
1195
|
// (equal or not equal) to a literal value. It also handles "is" or "is not"
|
|
2219
1196
|
// operators if isIsOperator is true.
|
|
2220
1197
|
function narrowTypeForLiteralComparison(evaluator, referenceType, literalType, isPositiveTest, isIsOperator) {
|
|
2221
|
-
return
|
|
2222
|
-
subtype = evaluator.makeTopLevelTypeVarsConcrete(subtype);
|
|
2223
|
-
if ((0, types_1.isAnyOrUnknown)(subtype)) {
|
|
2224
|
-
if (isPositiveTest) {
|
|
2225
|
-
return literalType;
|
|
2226
|
-
}
|
|
2227
|
-
return subtype;
|
|
2228
|
-
}
|
|
2229
|
-
// Determine whether this is a comparison between a primitive-backed enum
|
|
2230
|
-
// (e.g. IntEnum or StrEnum) and its underlying primitive type. Such a
|
|
2231
|
-
// comparison can be used for narrowing because the enum member compares
|
|
2232
|
-
// equal to its underlying primitive value at runtime.
|
|
2233
|
-
const isPrimitiveBackedEnumComparison = !isIsOperator &&
|
|
2234
|
-
(0, types_1.isClassInstance)(subtype) &&
|
|
2235
|
-
(isPrimitiveBackedEnumAndPrimitive(subtype, literalType) ||
|
|
2236
|
-
isPrimitiveBackedEnumAndPrimitive(literalType, subtype));
|
|
2237
|
-
if ((0, types_1.isClassInstance)(subtype) &&
|
|
2238
|
-
(types_1.ClassType.isSameGenericClass(literalType, subtype) || isPrimitiveBackedEnumComparison)) {
|
|
2239
|
-
if (subtype.priv.literalValue !== undefined) {
|
|
2240
|
-
const literalValueMatches = isPrimitiveBackedEnumComparison
|
|
2241
|
-
? isLiteralValueEqualAtRuntime(subtype, literalType)
|
|
2242
|
-
: types_1.ClassType.isLiteralValueSame(subtype, literalType);
|
|
2243
|
-
if (isPositiveTest) {
|
|
2244
|
-
return literalValueMatches ? subtype : undefined;
|
|
2245
|
-
}
|
|
2246
|
-
const isSingleton = types_1.ClassType.isEnumClass(literalType) ||
|
|
2247
|
-
(0, typeUtils_1.isSentinelLiteral)(subtype) ||
|
|
2248
|
-
types_1.ClassType.isBuiltIn(literalType, 'bool');
|
|
2249
|
-
// For negative tests, we can eliminate the literal value if it doesn't match,
|
|
2250
|
-
// but only for equality tests or for 'is' tests that involve enums, bools, or sentinels.
|
|
2251
|
-
return literalValueMatches && (isSingleton || !isIsOperator) ? undefined : subtype;
|
|
2252
|
-
}
|
|
2253
|
-
if (isPositiveTest) {
|
|
2254
|
-
if (isPrimitiveBackedEnumComparison) {
|
|
2255
|
-
// If the reference type is the enum, attempt to find the member
|
|
2256
|
-
// whose value matches the primitive literal. If no member can be
|
|
2257
|
-
// identified (e.g. for an IntFlag class, whose members cannot be
|
|
2258
|
-
// enumerated), retain the reference type rather than narrowing it
|
|
2259
|
-
// to the primitive literal type.
|
|
2260
|
-
if (types_1.ClassType.isEnumClass(subtype)) {
|
|
2261
|
-
const allLiteralTypes = enumerateLiteralsForType(evaluator, subtype);
|
|
2262
|
-
const match = allLiteralTypes?.find((type) => isLiteralValueEqualAtRuntime(type, literalType));
|
|
2263
|
-
return match ?? subtype;
|
|
2264
|
-
}
|
|
2265
|
-
// The reference type is the primitive type, so narrowing it to the
|
|
2266
|
-
// enum literal would lose its (potentially derived) class. Retain
|
|
2267
|
-
// the reference type instead.
|
|
2268
|
-
return subtype;
|
|
2269
|
-
}
|
|
2270
|
-
return literalType;
|
|
2271
|
-
}
|
|
2272
|
-
// If we're able to enumerate all possible literal values
|
|
2273
|
-
// (for bool or enum), we can eliminate all others in a negative test.
|
|
2274
|
-
const allLiteralTypes = enumerateLiteralsForType(evaluator, subtype);
|
|
2275
|
-
if (allLiteralTypes && allLiteralTypes.length > 0) {
|
|
2276
|
-
return (0, types_1.combineTypes)(allLiteralTypes.filter((type) => isPrimitiveBackedEnumComparison
|
|
2277
|
-
? !isLiteralValueEqualAtRuntime(type, literalType)
|
|
2278
|
-
: !types_1.ClassType.isLiteralValueSame(type, literalType)));
|
|
2279
|
-
}
|
|
2280
|
-
return subtype;
|
|
2281
|
-
}
|
|
2282
|
-
if (isPositiveTest) {
|
|
2283
|
-
if ((0, types_1.isClassInstance)(subtype) && types_1.ClassType.isBuiltIn(subtype, 'LiteralString')) {
|
|
2284
|
-
return literalType;
|
|
2285
|
-
}
|
|
2286
|
-
if (isIsOperator || (0, typeUtils_1.isNoneInstance)(subtype)) {
|
|
2287
|
-
const compareType = getInnermostNewTypeBaseInstance(subtype) ?? subtype;
|
|
2288
|
-
const isSubtype = evaluator.assignType(compareType, literalType);
|
|
2289
|
-
if (isSubtype) {
|
|
2290
|
-
return (0, types_1.isClassInstance)(subtype) && types_1.ClassType.isNewTypeClass(subtype) ? subtype : literalType;
|
|
2291
|
-
}
|
|
2292
|
-
return undefined;
|
|
2293
|
-
}
|
|
2294
|
-
}
|
|
2295
|
-
return subtype;
|
|
2296
|
-
});
|
|
1198
|
+
return TypeEvaluatorNarrowing.narrowTypeForLiteralComparison(evaluator, referenceType, literalType, isPositiveTest, isIsOperator);
|
|
2297
1199
|
}
|
|
2298
1200
|
function enumerateLiteralsForType(evaluator, type) {
|
|
2299
|
-
|
|
2300
|
-
// Booleans have only two types: True and False.
|
|
2301
|
-
return [
|
|
2302
|
-
types_1.ClassType.cloneWithLiteral(type, /* value */ true),
|
|
2303
|
-
types_1.ClassType.cloneWithLiteral(type, /* value */ false),
|
|
2304
|
-
];
|
|
2305
|
-
}
|
|
2306
|
-
if (types_1.ClassType.isEnumClass(type)) {
|
|
2307
|
-
// Enum expansion doesn't apply if the member set cannot be statically
|
|
2308
|
-
// enumerated or if the class derives from enum.Flag.
|
|
2309
|
-
if (types_1.ClassType.isEnumMemberSetMayBeDynamicallyModified(type) ||
|
|
2310
|
-
type.shared.mro.some((mroClass) => (0, types_1.isClass)(mroClass) && types_1.ClassType.isBuiltIn(mroClass, 'Flag'))) {
|
|
2311
|
-
return undefined;
|
|
2312
|
-
}
|
|
2313
|
-
// Enumerate all of the values in this enumeration.
|
|
2314
|
-
const enumList = [];
|
|
2315
|
-
const fields = types_1.ClassType.getSymbolTable(type);
|
|
2316
|
-
fields.forEach((symbol, name) => {
|
|
2317
|
-
if (!symbol.isIgnoredForProtocolMatch()) {
|
|
2318
|
-
let symbolType = evaluator.getEffectiveTypeOfSymbol(symbol);
|
|
2319
|
-
symbolType = (0, enums_1.transformTypeForEnumMember)(evaluator, type, name, (0, analyzerNodeInfo_1.getInfoReader)(evaluator)) ?? symbolType;
|
|
2320
|
-
if ((0, types_1.isClassInstance)(symbolType) &&
|
|
2321
|
-
types_1.ClassType.isSameGenericClass(type, symbolType) &&
|
|
2322
|
-
symbolType.priv.literalValue !== undefined) {
|
|
2323
|
-
enumList.push(symbolType);
|
|
2324
|
-
}
|
|
2325
|
-
}
|
|
2326
|
-
});
|
|
2327
|
-
return enumList;
|
|
2328
|
-
}
|
|
2329
|
-
return undefined;
|
|
1201
|
+
return TypeEvaluatorNarrowing.enumerateLiteralsForType(evaluator, type);
|
|
2330
1202
|
}
|
|
2331
1203
|
// Determines whether the expression name node is in the same scope or
|
|
2332
1204
|
// an outer scope from the reference name node. This allows isMatchingExpression
|
|
2333
1205
|
// to determine whether two name nodes are referring to the same symbol.
|
|
2334
1206
|
function isNameSameScope(evaluator, reference, expression) {
|
|
2335
|
-
|
|
2336
|
-
const exprSymbol = evaluator.lookUpSymbolRecursive(expression, expression.d.value, /* honorCodeFlow */ false);
|
|
2337
|
-
if (!refSymbol || !exprSymbol) {
|
|
2338
|
-
// This shouldn't happen, but just to be safe...
|
|
2339
|
-
return true;
|
|
2340
|
-
}
|
|
2341
|
-
const refScope = refSymbol.scope;
|
|
2342
|
-
const exprScope = exprSymbol.scope;
|
|
2343
|
-
if (refScope === exprScope) {
|
|
2344
|
-
return true;
|
|
2345
|
-
}
|
|
2346
|
-
return (0, scopeUtils_1.isScopeContainedWithin)(refScope, exprScope);
|
|
1207
|
+
return TypeEvaluatorNarrowing.isNameSameScope(evaluator, reference, expression);
|
|
2347
1208
|
}
|
|
2348
1209
|
// Matches a reference against an expression, including the RHS of an assignment
|
|
2349
1210
|
// expression. This keeps walrus narrowing consistent with truthiness handling in
|