Skip to content

Commit b031f1c

Browse files
authored
Fix type inference in pattern matching by positional argument (#13618)
Oh, this was very interesting. During the debug sessions I learned a lot about how pattern matching and its analysis do work. But, the problem was that `expand_type` did not preserve `.last_known_value` for some reason. I used `.copy_modified` to preserve everything we know about `Instance`. However, I expect that some tests might fail now. This code even has something similar in `TODO` some lines above: https://github.com/python/mypy/blob/88aed94ae3de2542491f6cd65d1236c4f0cdedb1/mypy/expandtype.py#L144-L148 Let's see what will happen. Closes #13612
1 parent 80dfb36 commit b031f1c

File tree

2 files changed

+68
-1
lines changed

2 files changed

+68
-1
lines changed

mypy/expandtype.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ def visit_erased_type(self, t: ErasedType) -> Type:
136136
def visit_instance(self, t: Instance) -> Type:
137137
args = self.expand_types_with_unpack(list(t.args))
138138
if isinstance(args, list):
139-
return Instance(t.type, args, t.line, t.column)
139+
return t.copy_modified(args=args)
140140
else:
141141
return args
142142

test-data/unit/check-python310.test

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1628,3 +1628,70 @@ match var:
16281628
case ("yes", b):
16291629
reveal_type(b) # N: Revealed type is "Union[builtins.int, builtins.str]"
16301630
[builtins fixtures/tuple.pyi]
1631+
1632+
[case testMatchNamedAndKeywordsAreTheSame]
1633+
from typing import Generic, TypeVar, Union
1634+
from typing_extensions import Final
1635+
from dataclasses import dataclass
1636+
1637+
T = TypeVar("T")
1638+
1639+
class Regular:
1640+
x: str
1641+
y: int
1642+
__match_args__ = ("x",)
1643+
class ReveresedOrder:
1644+
x: int
1645+
y: str
1646+
__match_args__ = ("y",)
1647+
class GenericRegular(Generic[T]):
1648+
x: T
1649+
__match_args__ = ("x",)
1650+
class GenericWithFinal(Generic[T]):
1651+
x: T
1652+
__match_args__: Final = ("x",)
1653+
class RegularSubtype(GenericRegular[str]): ...
1654+
1655+
@dataclass
1656+
class GenericDataclass(Generic[T]):
1657+
x: T
1658+
1659+
input_arg: Union[
1660+
Regular,
1661+
ReveresedOrder,
1662+
GenericRegular[str],
1663+
GenericWithFinal[str],
1664+
RegularSubtype,
1665+
GenericDataclass[str],
1666+
]
1667+
1668+
# Positional:
1669+
match input_arg:
1670+
case Regular(a):
1671+
reveal_type(a) # N: Revealed type is "builtins.str"
1672+
case ReveresedOrder(a):
1673+
reveal_type(a) # N: Revealed type is "builtins.str"
1674+
case GenericWithFinal(a):
1675+
reveal_type(a) # N: Revealed type is "builtins.str"
1676+
case RegularSubtype(a):
1677+
reveal_type(a) # N: Revealed type is "builtins.str"
1678+
case GenericRegular(a):
1679+
reveal_type(a) # N: Revealed type is "builtins.str"
1680+
case GenericDataclass(a):
1681+
reveal_type(a) # N: Revealed type is "builtins.str"
1682+
1683+
# Keywords:
1684+
match input_arg:
1685+
case Regular(x=a):
1686+
reveal_type(a) # N: Revealed type is "builtins.str"
1687+
case ReveresedOrder(x=b): # Order is different
1688+
reveal_type(b) # N: Revealed type is "builtins.int"
1689+
case GenericWithFinal(x=a):
1690+
reveal_type(a) # N: Revealed type is "builtins.str"
1691+
case RegularSubtype(x=a):
1692+
reveal_type(a) # N: Revealed type is "builtins.str"
1693+
case GenericRegular(x=a):
1694+
reveal_type(a) # N: Revealed type is "builtins.str"
1695+
case GenericDataclass(x=a):
1696+
reveal_type(a) # N: Revealed type is "builtins.str"
1697+
[builtins fixtures/dataclasses.pyi]

0 commit comments

Comments
 (0)