diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index b8720d9402f8..78662b574032 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -33,6 +33,7 @@ coerce_to_literal, make_simplified_union, try_getting_str_literals_from_type, + tuple_fallback, ) from mypy.types import ( AnyType, @@ -325,7 +326,9 @@ def get_sequence_type(self, t: Type) -> Type | None: else: return None - if self.chk.type_is_iterable(t) and isinstance(t, Instance): + if self.chk.type_is_iterable(t) and isinstance(t, (Instance, TupleType)): + if isinstance(t, TupleType): + t = tuple_fallback(t) return self.chk.iterable_item_type(t) else: return None @@ -645,6 +648,9 @@ def construct_sequence_child(self, outer_type: Type, inner_type: Type) -> Type: For example: construct_sequence_child(List[int], str) = List[str] + + TODO: this doesn't make sense. For example if one has class S(Sequence[int], Generic[T]) + or class T(Sequence[Tuple[T, T]]), there is no way any of those can map to Sequence[str]. """ proper_type = get_proper_type(outer_type) if isinstance(proper_type, UnionType): @@ -657,6 +663,8 @@ def construct_sequence_child(self, outer_type: Type, inner_type: Type) -> Type: sequence = self.chk.named_generic_type("typing.Sequence", [inner_type]) if is_subtype(outer_type, self.chk.named_type("typing.Sequence")): proper_type = get_proper_type(outer_type) + if isinstance(proper_type, TupleType): + proper_type = tuple_fallback(proper_type) assert isinstance(proper_type, Instance) empty_type = fill_typevars(proper_type.type) partial_type = expand_type_by_instance(empty_type, sequence) diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 0003ad2601e0..298ec9e45998 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -1600,3 +1600,31 @@ def foo(x: NoneType): # E: NoneType should not be used as a type, please use Non reveal_type(x) # N: Revealed type is "None" [builtins fixtures/tuple.pyi] + +[case testMatchTupleInstanceUnionNoCrash] +from typing import Union + +def func(e: Union[str, tuple[str]]) -> None: + match e: + case (a,) if isinstance(a, str): + reveal_type(a) # N: Revealed type is "builtins.str" +[builtins fixtures/tuple.pyi] + +[case testMatchTupleOptionalNoCrash] +# flags: --strict-optional +foo: tuple[int] | None +match foo: + case x,: + reveal_type(x) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] + +[case testMatchUnionTwoTuplesNoCrash] +var: tuple[int, int] | tuple[str, str] + +# TODO: we can infer better here. +match var: + case (42, a): + reveal_type(a) # N: Revealed type is "Union[builtins.int, builtins.str]" + case ("yes", b): + reveal_type(b) # N: Revealed type is "Union[builtins.int, builtins.str]" +[builtins fixtures/tuple.pyi]