diff --git a/mypy/checker.py b/mypy/checker.py index 4f06df731d05..e55608ab896a 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -5363,6 +5363,12 @@ def flatten_types(t: Type) -> List[Type]: def get_isinstance_type(expr: Expression, type_map: Dict[Expression, Type]) -> Optional[List[TypeRange]]: + if isinstance(expr, OpExpr) and expr.op == '|': + left = get_isinstance_type(expr.left, type_map) + right = get_isinstance_type(expr.right, type_map) + if left is None or right is None: + return None + return left + right all_types = get_proper_types(flatten_types(type_map[expr])) types: List[TypeRange] = [] for typ in all_types: diff --git a/test-data/unit/check-union-or-syntax.test b/test-data/unit/check-union-or-syntax.test index 7137fcbfe310..3e5a19b8fe61 100644 --- a/test-data/unit/check-union-or-syntax.test +++ b/test-data/unit/check-union-or-syntax.test @@ -169,3 +169,30 @@ y: B class C(list[int | None]): pass [builtins fixtures/list.pyi] + +[case testUnionOrSyntaxInIsinstance] +# flags: --python-version 3.10 +class C: pass + +def f(x: int | str | C) -> None: + if isinstance(x, int | str): + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + else: + reveal_type(x) # N: Revealed type is "__main__.C" + +def g(x: int | str | tuple[int, str] | C) -> None: + if isinstance(x, int | str | tuple): + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, Tuple[builtins.int, builtins.str]]" + else: + reveal_type(x) # N: Revealed type is "__main__.C" +[builtins fixtures/isinstance_python3_10.pyi] + +[case testUnionOrSyntaxInIsinstanceNotSupported] +# flags: --python-version 3.9 +from typing import Union +def f(x: Union[int, str, None]) -> None: + if isinstance(x, int | str): # E: Unsupported left operand type for | ("Type[int]") + reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]" + else: + reveal_type(x) # N: Revealed type is "None" +[builtins fixtures/isinstance.pyi] diff --git a/test-data/unit/fixtures/isinstance_python3_10.pyi b/test-data/unit/fixtures/isinstance_python3_10.pyi new file mode 100644 index 000000000000..abb37ea81c00 --- /dev/null +++ b/test-data/unit/fixtures/isinstance_python3_10.pyi @@ -0,0 +1,29 @@ +# For Python 3.10+ only +from typing import Tuple, TypeVar, Generic, Union, cast, Any, Type +import types + +T = TypeVar('T') + +class object: + def __init__(self) -> None: pass + +class type(Generic[T]): + def __init__(self, x) -> None: pass + def __or__(self, x) -> types.Union: pass + +class tuple(Generic[T]): pass + +class function: pass + +def isinstance(x: object, t: Union[Type[object], Tuple[Type[object], ...], types.Union]) -> bool: pass +def issubclass(x: object, t: Union[Type[object], Tuple[Type[object], ...]]) -> bool: pass + +class int: + def __add__(self, other: 'int') -> 'int': pass +class float: pass +class bool(int): pass +class str: + def __add__(self, other: 'str') -> 'str': pass +class ellipsis: pass + +NotImplemented = cast(Any, None) diff --git a/test-data/unit/lib-stub/types.pyi b/test-data/unit/lib-stub/types.pyi index 02113aea3834..6fc596ecbf13 100644 --- a/test-data/unit/lib-stub/types.pyi +++ b/test-data/unit/lib-stub/types.pyi @@ -1,4 +1,5 @@ from typing import TypeVar +import sys _T = TypeVar('_T') @@ -8,3 +9,7 @@ class bool: ... class ModuleType: __file__ = ... # type: str + +if sys.version_info >= (3, 10): + class Union: + def __or__(self, x) -> Union: ...