mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-02 22:54:42 +00:00
[ty] Fix narrowing and reachability of class patterns with arguments (#19512)
## Summary I noticed that our type narrowing and reachability analysis was incorrect for class patterns that are not irrefutable. The test cases below compare the old and the new behavior: ```py from dataclasses import dataclass @dataclass class Point: x: int y: int class Other: ... def _(target: Point): y = 1 match target: case Point(0, 0): y = 2 case Point(x=0, y=1): y = 3 case Point(x=1, y=0): y = 4 reveal_type(y) # revealed: Literal[1, 2, 3, 4] (previously: Literal[2]) def _(target: Point | Other): match target: case Point(0, 0): reveal_type(target) # revealed: Point case Point(x=0, y=1): reveal_type(target) # revealed: Point (previously: Never) case Point(x=1, y=0): reveal_type(target) # revealed: Point (previously: Never) case Other(): reveal_type(target) # revealed: Other (previously: Other & ~Point) ``` ## Test Plan New Markdown test
This commit is contained in:
parent
fa1df4cedc
commit
3d17897c02
5 changed files with 111 additions and 11 deletions
|
@ -80,6 +80,8 @@ def _(subject: C):
|
|||
A `case` branch with a class pattern is taken if the subject is an instance of the given class, and
|
||||
all subpatterns in the class pattern match.
|
||||
|
||||
### Without arguments
|
||||
|
||||
```py
|
||||
from typing import final
|
||||
|
||||
|
@ -136,6 +138,51 @@ def _(target: FooSub | str):
|
|||
reveal_type(y) # revealed: Literal[1, 3, 4]
|
||||
```
|
||||
|
||||
### With arguments
|
||||
|
||||
```py
|
||||
from typing_extensions import assert_never
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class Point:
|
||||
x: int
|
||||
y: int
|
||||
|
||||
class Other: ...
|
||||
|
||||
def _(target: Point):
|
||||
y = 1
|
||||
|
||||
match target:
|
||||
case Point(0, 0):
|
||||
y = 2
|
||||
case Point(x=0, y=1):
|
||||
y = 3
|
||||
case Point(x=1, y=0):
|
||||
y = 4
|
||||
|
||||
reveal_type(y) # revealed: Literal[1, 2, 3, 4]
|
||||
|
||||
def _(target: Point):
|
||||
match target:
|
||||
case Point(x, y): # irrefutable sub-patterns
|
||||
pass
|
||||
case _:
|
||||
assert_never(target)
|
||||
|
||||
def _(target: Point | Other):
|
||||
match target:
|
||||
case Point(0, 0):
|
||||
reveal_type(target) # revealed: Point
|
||||
case Point(x=0, y=1):
|
||||
reveal_type(target) # revealed: Point
|
||||
case Point(x=1, y=0):
|
||||
reveal_type(target) # revealed: Point
|
||||
case Other():
|
||||
reveal_type(target) # revealed: Other
|
||||
```
|
||||
|
||||
## Singleton match
|
||||
|
||||
Singleton patterns are matched based on identity, not equality comparisons or `isinstance()` checks.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue