diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index 7d0ae974d35df..7585d91deb0e1 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -14,7 +14,7 @@ from mypy.maptype import map_instance_to_supertype from mypy.meet import narrow_declared_type from mypy.messages import MessageBuilder -from mypy.nodes import ARG_POS, Context, Expression, NameExpr, TempNode, TypeAlias, Var +from mypy.nodes import ARG_POS, Expression, NameExpr, TempNode, TypeAlias, Var from mypy.options import Options from mypy.patterns import ( AsPattern, @@ -394,11 +394,14 @@ def visit_sequence_pattern(self, o: SequencePattern) -> PatternType: new_inner_type = UninhabitedType() for typ in new_inner_types: new_inner_type = join_types(new_inner_type, typ) - if isinstance(current_type, TypeVarType): - new_bound = self.narrow_sequence_child(current_type.upper_bound, new_inner_type, o) - new_type = current_type.copy_modified(upper_bound=new_bound) - else: - new_type = self.narrow_sequence_child(current_type, new_inner_type, o) + new_type = self.construct_sequence_child(current_type, new_inner_type) + new_type, possible_rest_type = self.chk.conditional_types_with_intersection( + current_type, [get_type_range(new_type)], o, default=current_type + ) + if star_position is not None and len(o.patterns) == 1: + # Match cannot be refuted, so narrow the remaining type + rest_type = possible_rest_type + return PatternType(new_type, rest_type, captures) def contract_starred_pattern_types( @@ -478,16 +481,6 @@ def expand_starred_pattern_types( return new_types - def narrow_sequence_child(self, outer_type: Type, inner_type: Type, ctx: Context) -> Type: - new_type = self.construct_sequence_child(outer_type, inner_type) - if is_subtype(new_type, outer_type): - new_type, _ = self.chk.conditional_types_with_intersection( - outer_type, [get_type_range(new_type)], ctx, default=outer_type - ) - else: - new_type = outer_type - return new_type - def visit_starred_pattern(self, o: StarredPattern) -> PatternType: captures: dict[Expression, Type] = {} if o.capture is not None: @@ -796,6 +789,9 @@ def construct_sequence_child(self, outer_type: Type, inner_type: Type) -> Type: 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, TypeVarType): + new_bound = self.construct_sequence_child(proper_type.upper_bound, inner_type) + return proper_type.copy_modified(upper_bound=new_bound) if isinstance(proper_type, AnyType): return outer_type if isinstance(proper_type, UnionType): diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index aa792c41470b6..723125eb8def8 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -1746,6 +1746,33 @@ match m6: [builtins fixtures/tuple.pyi] +[case testMatchSequenceWildcardRefutability] +# flags: --strict-equality --warn-unreachable +def f1(x: int | list[int]): + match x: + case [*foo]: + reveal_type(x) # N: Revealed type is "builtins.list[builtins.int]" + reveal_type(foo) # N: Revealed type is "builtins.list[builtins.int]" + case _: + reveal_type(x) # N: Revealed type is "builtins.int" + +def f2(x: object): + match x: + case [*foo]: + reveal_type(x) # N: Revealed type is "typing.Sequence[builtins.object]" + reveal_type(foo) # N: Revealed type is "builtins.list[builtins.object]" + case _: + reveal_type(x) # N: Revealed type is "builtins.object" + +def f3(x: int | list[int]): + match x: + case [1, *foo]: + reveal_type(x) # N: Revealed type is "builtins.list[builtins.int]" + reveal_type(foo) # N: Revealed type is "builtins.list[builtins.int]" + case _: + reveal_type(x) # N: Revealed type is "builtins.int | builtins.list[builtins.int]" +[builtins fixtures/tuple.pyi] + [case testMatchTupleUnions] from typing_extensions import Unpack