mirror of
https://github.com/astral-sh/ruff.git
synced 2026-10-28 10:45:37 +08:00
[ty] Fix disjointness of type guards and boolean literals (#28363)
This commit is contained in:
1 parent
12e65d6d7e
commit
321eebc2d6
3 files changed
+52
No files matched your search
@@ -159,6 +159,27 @@ def f(x: N, y: int, z: O):
|
||||
reveal_type(x is not z) # revealed: Literal[True]
|
||||
```
|
||||
|
||||
## Identity comparisons with type guard results
|
||||
|
||||
`TypeIs` and `TypeGuard` functions return booleans, so comparing their results with `True` or
|
||||
`False` can succeed or fail.
|
||||
|
||||
```py
|
||||
from typing_extensions import TypeGuard, TypeIs
|
||||
|
||||
def is_int(x: object) -> TypeIs[int]:
|
||||
return isinstance(x, int)
|
||||
|
||||
def is_int_guard(x: object) -> TypeGuard[int]:
|
||||
return isinstance(x, int)
|
||||
|
||||
def f(x: object):
|
||||
reveal_type(is_int(x) is True) # revealed: bool
|
||||
reveal_type(is_int(x) is False) # revealed: bool
|
||||
reveal_type(is_int_guard(x) is True) # revealed: bool
|
||||
reveal_type(is_int_guard(x) is False) # revealed: bool
|
||||
```
|
||||
|
||||
## Identity comparisons see through type aliases
|
||||
|
||||
```toml
|
||||
|
||||
@@ -754,6 +754,32 @@ static_assert(is_disjoint_from(str, TypeGuard[str]))
|
||||
static_assert(is_disjoint_from(str, TypeIs[str]))
|
||||
```
|
||||
|
||||
Either kind of type guard can return `True` or `False`, so both overlap with each boolean literal.
|
||||
|
||||
```py
|
||||
from typing import Literal
|
||||
|
||||
static_assert(not is_disjoint_from(TypeGuard[str], Literal[True]))
|
||||
static_assert(not is_disjoint_from(TypeGuard[str], Literal[False]))
|
||||
static_assert(not is_disjoint_from(TypeIs[str], Literal[True]))
|
||||
static_assert(not is_disjoint_from(TypeIs[str], Literal[False]))
|
||||
|
||||
static_assert(not is_disjoint_from(Literal[True], TypeGuard[str]))
|
||||
static_assert(not is_disjoint_from(Literal[False], TypeGuard[str]))
|
||||
static_assert(not is_disjoint_from(Literal[True], TypeIs[str]))
|
||||
static_assert(not is_disjoint_from(Literal[False], TypeIs[str]))
|
||||
```
|
||||
|
||||
The integer literals `0` and `1` are distinct from boolean literals, so they remain disjoint from
|
||||
type guard return types.
|
||||
|
||||
```py
|
||||
static_assert(is_disjoint_from(TypeGuard[str], Literal[0]))
|
||||
static_assert(is_disjoint_from(TypeIs[str], Literal[1]))
|
||||
static_assert(is_disjoint_from(Literal[1], TypeGuard[str]))
|
||||
static_assert(is_disjoint_from(Literal[0], TypeIs[str]))
|
||||
```
|
||||
|
||||
### `Protocol`
|
||||
|
||||
A protocol is disjoint from another type if any of the protocol's members are available as an
|
||||
|
||||
@@ -3767,6 +3767,11 @@ impl<'a, 'c, 'db> DisjointnessChecker<'a, 'c, 'db> {
|
||||
})
|
||||
}
|
||||
|
||||
(Type::TypeIs(_) | Type::TypeGuard(_), Type::LiteralValue(literal))
|
||||
| (Type::LiteralValue(literal), Type::TypeIs(_) | Type::TypeGuard(_)) => {
|
||||
ConstraintSet::from_bool(self.constraints, !literal.is_bool())
|
||||
}
|
||||
|
||||
(Type::TypeIs(_) | Type::TypeGuard(_), Type::NominalInstance(instance))
|
||||
| (Type::NominalInstance(instance), Type::TypeIs(_) | Type::TypeGuard(_)) => {
|
||||
// A boolean literal must be an instance of exactly `bool`
|
||||
|
||||
Reference in new issue
Block a user