mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 13:51:16 +00:00
[ty] Simplify unions of enum literals and subtypes thereof (#20324)
## Summary When adding an enum literal `E = Literal[Color.RED]` to a union which already contained a subtype of that enum literal(!), we were previously not simplifying the union correctly. My assumption is that our property tests didn't catch that earlier, because the only possible non-trivial subytpe of an enum literal that I can think of is `Any & E`. And in order for that to be detected by the property tests, it would have to randomly generate `Any & E | E` and then also compare that with `E` on the other side (in an equivalence test, or the subtyping-antisymmetry test). closes https://github.com/astral-sh/ty/issues/1155 ## Test Plan * Added a regression test. * I also ran the property tests for a while, but probably not for two months worth of daily CI runs.
This commit is contained in:
parent
7a75702237
commit
57d1f7132d
2 changed files with 83 additions and 72 deletions
|
@ -118,7 +118,8 @@ def _(
|
|||
|
||||
```py
|
||||
from enum import Enum
|
||||
from typing import Literal
|
||||
from typing import Literal, Any
|
||||
from ty_extensions import Intersection
|
||||
|
||||
class Color(Enum):
|
||||
RED = "red"
|
||||
|
@ -139,6 +140,13 @@ def _(
|
|||
reveal_type(u4) # revealed: Literal[Color.RED, Color.GREEN]
|
||||
reveal_type(u5) # revealed: Color
|
||||
reveal_type(u6) # revealed: Color
|
||||
|
||||
def _(
|
||||
u1: Intersection[Literal[Color.RED], Any] | Literal[Color.RED],
|
||||
u2: Literal[Color.RED] | Intersection[Literal[Color.RED], Any],
|
||||
):
|
||||
reveal_type(u1) # revealed: Literal[Color.RED]
|
||||
reveal_type(u2) # revealed: Literal[Color.RED]
|
||||
```
|
||||
|
||||
## Do not erase `Unknown`
|
||||
|
|
|
@ -444,8 +444,7 @@ impl<'db> UnionBuilder<'db> {
|
|||
.filter_map(UnionElement::to_type_element)
|
||||
.any(|ty| Type::EnumLiteral(enum_member_to_add).is_subtype_of(self.db, ty))
|
||||
{
|
||||
self.elements
|
||||
.push(UnionElement::Type(Type::EnumLiteral(enum_member_to_add)));
|
||||
self.push_type(Type::EnumLiteral(enum_member_to_add), seen_aliases);
|
||||
}
|
||||
}
|
||||
// Adding `object` to a union results in `object`.
|
||||
|
@ -453,6 +452,12 @@ impl<'db> UnionBuilder<'db> {
|
|||
self.collapse_to_object();
|
||||
}
|
||||
_ => {
|
||||
self.push_type(ty, seen_aliases);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn push_type(&mut self, ty: Type<'db>, seen_aliases: &mut Vec<Type<'db>>) {
|
||||
let bool_pair = if let Type::BooleanLiteral(b) = ty {
|
||||
Some(Type::BooleanLiteral(!b))
|
||||
} else {
|
||||
|
@ -530,8 +535,6 @@ impl<'db> UnionBuilder<'db> {
|
|||
self.elements.push(UnionElement::Type(ty));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn build(self) -> Type<'db> {
|
||||
self.try_build().unwrap_or(Type::Never)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue