mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-20 04:29:47 +00:00
[ty] Use the top materialization of classes for narrowing in class-patterns for match statements (#21150)
This commit is contained in:
parent
c0b04d4b7c
commit
13375d0e42
4 changed files with 113 additions and 8 deletions
|
|
@ -182,6 +182,11 @@ def match_non_exhaustive(x: Color):
|
|||
|
||||
## `isinstance` checks
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python-version = "3.12"
|
||||
```
|
||||
|
||||
```py
|
||||
from typing import assert_never
|
||||
|
||||
|
|
@ -189,6 +194,9 @@ class A: ...
|
|||
class B: ...
|
||||
class C: ...
|
||||
|
||||
class GenericClass[T]:
|
||||
x: T
|
||||
|
||||
def if_else_exhaustive(x: A | B | C):
|
||||
if isinstance(x, A):
|
||||
pass
|
||||
|
|
@ -253,6 +261,17 @@ def match_non_exhaustive(x: A | B | C):
|
|||
|
||||
# this diagnostic is correct: the inferred type of `x` is `B & ~A & ~C`
|
||||
assert_never(x) # error: [type-assertion-failure]
|
||||
|
||||
# Note: no invalid-return-type diagnostic; the `match` is exhaustive
|
||||
def match_exhaustive_generic[T](obj: GenericClass[T]) -> GenericClass[T]:
|
||||
match obj:
|
||||
case GenericClass(x=42):
|
||||
reveal_type(obj) # revealed: GenericClass[T@match_exhaustive_generic]
|
||||
return obj
|
||||
case GenericClass(x=x):
|
||||
reveal_type(x) # revealed: @Todo(`match` pattern definition types)
|
||||
reveal_type(obj) # revealed: GenericClass[T@match_exhaustive_generic]
|
||||
return obj
|
||||
```
|
||||
|
||||
## `isinstance` checks with generics
|
||||
|
|
|
|||
|
|
@ -69,6 +69,81 @@ match x:
|
|||
reveal_type(x) # revealed: object
|
||||
```
|
||||
|
||||
## Class patterns with generic classes
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python-version = "3.12"
|
||||
```
|
||||
|
||||
```py
|
||||
from typing import assert_never
|
||||
|
||||
class Covariant[T]:
|
||||
def get(self) -> T:
|
||||
raise NotImplementedError
|
||||
|
||||
def f(x: Covariant[int]):
|
||||
match x:
|
||||
case Covariant():
|
||||
reveal_type(x) # revealed: Covariant[int]
|
||||
case _:
|
||||
reveal_type(x) # revealed: Never
|
||||
assert_never(x)
|
||||
```
|
||||
|
||||
## Class patterns with generic `@final` classes
|
||||
|
||||
These work the same as non-`@final` classes.
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python-version = "3.12"
|
||||
```
|
||||
|
||||
```py
|
||||
from typing import assert_never, final
|
||||
|
||||
@final
|
||||
class Covariant[T]:
|
||||
def get(self) -> T:
|
||||
raise NotImplementedError
|
||||
|
||||
def f(x: Covariant[int]):
|
||||
match x:
|
||||
case Covariant():
|
||||
reveal_type(x) # revealed: Covariant[int]
|
||||
case _:
|
||||
reveal_type(x) # revealed: Never
|
||||
assert_never(x)
|
||||
```
|
||||
|
||||
## Class patterns where the class pattern does not resolve to a class
|
||||
|
||||
In general this does not allow for narrowing, but we make an exception for `Any`. This is to support
|
||||
[real ecosystem code](https://github.com/jax-ml/jax/blob/d2ce04b6c3d03ae18b145965b8b8b92e09e8009c/jax/_src/pallas/mosaic_gpu/lowering.py#L3372-L3387)
|
||||
found in `jax`.
|
||||
|
||||
```py
|
||||
from typing import Any
|
||||
|
||||
X = Any
|
||||
|
||||
def f(obj: object):
|
||||
match obj:
|
||||
case int():
|
||||
reveal_type(obj) # revealed: int
|
||||
case X():
|
||||
reveal_type(obj) # revealed: Any & ~int
|
||||
|
||||
def g(obj: object, Y: Any):
|
||||
match obj:
|
||||
case int():
|
||||
reveal_type(obj) # revealed: int
|
||||
case Y():
|
||||
reveal_type(obj) # revealed: Any & ~int
|
||||
```
|
||||
|
||||
## Value patterns
|
||||
|
||||
Value patterns are evaluated by equality, which is overridable. Therefore successfully matching on
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue