Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 12 additions & 16 deletions mypy/checkpattern.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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):
Expand Down
27 changes: 27 additions & 0 deletions test-data/unit/check-python310.test
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down