Fixed a bug in the isinstance and issubclass type narrowing code paths. They were not handling nested tuples for the second argument.

This commit is contained in:
Eric Traut 2022-05-30 00:44:56 -07:00
parent 255497446a
commit 6df560a638
2 changed files with 23 additions and 5 deletions

View File

@ -859,8 +859,8 @@ function narrowTypeForIsNone(evaluator: TypeEvaluator, type: Type, isPositiveTes
// The "isinstance" and "issubclass" calls support two forms - a simple form
// that accepts a single class, and a more complex form that accepts a tuple
// of classes. This method determines which form and returns a list of classes
// or undefined.
// of classes (including arbitrarily-nested tuples). This method determines
// which form and returns a list of classes or undefined.
function getIsInstanceClassTypes(argType: Type): (ClassType | TypeVarType | NoneType | FunctionType)[] | undefined {
let foundNonClassType = false;
const classTypeList: (ClassType | TypeVarType | NoneType | FunctionType)[] = [];
@ -886,16 +886,24 @@ function getIsInstanceClassTypes(argType: Type): (ClassType | TypeVarType | None
});
};
doForEachSubtype(argType, (subtype) => {
const addClassTypesRecursive = (subtype: Type, recursionCount = 0) => {
if (recursionCount > maxTypeRecursionCount) {
return;
}
if (isClass(subtype) && TypeBase.isInstance(subtype) && isTupleClass(subtype)) {
if (subtype.tupleTypeArguments) {
addClassTypesToList(subtype.tupleTypeArguments.map((t) => t.type));
subtype.tupleTypeArguments.forEach((tupleEntry) => {
addClassTypesRecursive(tupleEntry.type, recursionCount + 1);
});
}
} else {
addClassTypesToList([subtype]);
}
};
return undefined;
doForEachSubtype(argType, (subtype) => {
addClassTypesRecursive(subtype);
});
return foundNonClassType ? undefined : classTypeList;

View File

@ -150,3 +150,13 @@ def handler(node: Base1) -> Any:
reveal_type(node.value, expected_text="Base1")
if isinstance(node.value, Sub1):
reveal_type(node.value, expected_text="Sub1")
def func8(a: int | list[int] | dict[str, int] | None):
if isinstance(
a,
(str, (int, list, type(None))),
):
reveal_type(a, expected_text="int | list[int] | None")
else:
reveal_type(a, expected_text="dict[str, int]")