mirror of
https://github.com/astral-sh/ruff
synced 2026-01-21 05:20:49 -05:00
[ty] Avoid panic for comparison on synthesized variants (#22509)
## Summary Like `ProtocolInstance`, we now use `left.cmp(right)` by deriving `PartialOrd` and `Ord`. IIUC, this uses Salsa ID for Salsa-interned types, but avoids `None.cmp(None)` for synthesized variants. Closes https://github.com/astral-sh/ty/issues/2451.
This commit is contained in:
@@ -556,3 +556,27 @@ def _(x: type[object], y: type[object], z: type[object]):
|
||||
if issubclass(z, Invariant):
|
||||
reveal_type(z) # revealed: type[Top[Invariant[Unknown]]]
|
||||
```
|
||||
|
||||
## Narrowing with TypedDict unions
|
||||
|
||||
Narrowing unions of `int` and multiple TypedDicts using `isinstance(x, dict)` should not panic
|
||||
during type ordering of normalized intersection types. Regression test for
|
||||
<https://github.com/astral-sh/ty/issues/2451>.
|
||||
|
||||
```py
|
||||
from typing import Any, TypedDict, cast
|
||||
|
||||
class A(TypedDict):
|
||||
x: str
|
||||
|
||||
class B(TypedDict):
|
||||
y: str
|
||||
|
||||
T = int | A | B
|
||||
|
||||
def test(a: Any, items: list[T]) -> None:
|
||||
combined = a or items
|
||||
v = combined[0]
|
||||
if isinstance(v, dict):
|
||||
cast(T, v) # no panic
|
||||
```
|
||||
|
||||
@@ -228,9 +228,7 @@ pub(super) fn union_or_intersection_elements_ordering<'db>(
|
||||
(Type::TypeAlias(_), _) => Ordering::Less,
|
||||
(_, Type::TypeAlias(_)) => Ordering::Greater,
|
||||
|
||||
(Type::TypedDict(left), Type::TypedDict(right)) => {
|
||||
left.defining_class().cmp(&right.defining_class())
|
||||
}
|
||||
(Type::TypedDict(left), Type::TypedDict(right)) => left.cmp(right),
|
||||
(Type::TypedDict(_), _) => Ordering::Less,
|
||||
(_, Type::TypedDict(_)) => Ordering::Greater,
|
||||
|
||||
|
||||
@@ -48,7 +48,14 @@ impl Default for TypedDictParams {
|
||||
|
||||
/// Type that represents the set of all inhabitants (`dict` instances) that conform to
|
||||
/// a given `TypedDict` schema.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, salsa::Update, Hash, get_size2::GetSize)]
|
||||
///
|
||||
/// # Ordering
|
||||
/// Ordering is derived from the variant order (`Class` < `Synthesized`) and the inner types.
|
||||
/// The Salsa IDs of inner types may change between runs or when the type was garbage collected
|
||||
/// and recreated.
|
||||
#[derive(
|
||||
Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, salsa::Update, Hash, get_size2::GetSize,
|
||||
)]
|
||||
pub enum TypedDictType<'db> {
|
||||
/// A reference to the class (inheriting from `typing.TypedDict`) that specifies the
|
||||
/// schema of this `TypedDict`.
|
||||
@@ -878,7 +885,11 @@ pub(super) fn validate_typed_dict_dict_literal<'db>(
|
||||
}
|
||||
}
|
||||
|
||||
/// # Ordering
|
||||
/// Ordering is based on the type's salsa-assigned id and not on its values.
|
||||
/// The id may change between runs, or when the type was garbage collected and recreated.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct SynthesizedTypedDictType<'db> {
|
||||
#[returns(ref)]
|
||||
pub(crate) items: TypedDictSchema<'db>,
|
||||
|
||||
Reference in New Issue
Block a user