mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 05:44:56 +00:00
[ty] Normalize single-member enums to their instance type (#19502)
## Summary Fixes https://github.com/astral-sh/ty/issues/874 Labeling this as `internal`, since we haven't released the enum-expansion feature. ## Test Plan New Markdown tests
This commit is contained in:
parent
c281891b5c
commit
b605c3e232
3 changed files with 30 additions and 1 deletions
|
@ -13,7 +13,7 @@ materializations of `B`, and all materializations of `B` are also materializatio
|
||||||
### Fully static
|
### Fully static
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing_extensions import Literal, LiteralString, Never
|
from typing_extensions import Literal, LiteralString, Protocol, Never
|
||||||
from ty_extensions import Unknown, is_equivalent_to, static_assert, TypeOf, AlwaysTruthy, AlwaysFalsy
|
from ty_extensions import Unknown, is_equivalent_to, static_assert, TypeOf, AlwaysTruthy, AlwaysFalsy
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
|
@ -43,6 +43,16 @@ static_assert(is_equivalent_to(Literal[Single.VALUE], Single))
|
||||||
static_assert(is_equivalent_to(Single, Literal[Single.VALUE]))
|
static_assert(is_equivalent_to(Single, Literal[Single.VALUE]))
|
||||||
static_assert(is_equivalent_to(Literal[Single.VALUE], Literal[Single.VALUE]))
|
static_assert(is_equivalent_to(Literal[Single.VALUE], Literal[Single.VALUE]))
|
||||||
|
|
||||||
|
static_assert(is_equivalent_to(tuple[Single] | int | str, str | int | tuple[Literal[Single.VALUE]]))
|
||||||
|
|
||||||
|
class Protocol1(Protocol):
|
||||||
|
a: Single
|
||||||
|
|
||||||
|
class Protocol2(Protocol):
|
||||||
|
a: Literal[Single.VALUE]
|
||||||
|
|
||||||
|
static_assert(is_equivalent_to(Protocol1, Protocol2))
|
||||||
|
|
||||||
static_assert(is_equivalent_to(Never, Never))
|
static_assert(is_equivalent_to(Never, Never))
|
||||||
static_assert(is_equivalent_to(AlwaysTruthy, AlwaysTruthy))
|
static_assert(is_equivalent_to(AlwaysTruthy, AlwaysTruthy))
|
||||||
static_assert(is_equivalent_to(AlwaysFalsy, AlwaysFalsy))
|
static_assert(is_equivalent_to(AlwaysFalsy, AlwaysFalsy))
|
||||||
|
|
|
@ -1062,6 +1062,12 @@ impl<'db> Type<'db> {
|
||||||
type_is.with_type(db, type_is.return_type(db).normalized_impl(db, v))
|
type_is.with_type(db, type_is.return_type(db).normalized_impl(db, v))
|
||||||
}),
|
}),
|
||||||
Type::Dynamic(dynamic) => Type::Dynamic(dynamic.normalized()),
|
Type::Dynamic(dynamic) => Type::Dynamic(dynamic.normalized()),
|
||||||
|
Type::EnumLiteral(enum_literal)
|
||||||
|
if is_single_member_enum(db, enum_literal.enum_class(db)) =>
|
||||||
|
{
|
||||||
|
// Always normalize single-member enums to their class instance (`Literal[Single.VALUE]` => `Single`)
|
||||||
|
enum_literal.enum_class_instance(db)
|
||||||
|
}
|
||||||
Type::LiteralString
|
Type::LiteralString
|
||||||
| Type::AlwaysFalsy
|
| Type::AlwaysFalsy
|
||||||
| Type::AlwaysTruthy
|
| Type::AlwaysTruthy
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::db::tests::TestDb;
|
use crate::db::tests::TestDb;
|
||||||
use crate::place::{builtins_symbol, known_module_symbol};
|
use crate::place::{builtins_symbol, known_module_symbol};
|
||||||
|
use crate::types::enums::is_single_member_enum;
|
||||||
use crate::types::tuple::TupleType;
|
use crate::types::tuple::TupleType;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
BoundMethodType, CallableType, EnumLiteralType, IntersectionBuilder, KnownClass, Parameter,
|
BoundMethodType, CallableType, EnumLiteralType, IntersectionBuilder, KnownClass, Parameter,
|
||||||
|
@ -27,6 +28,8 @@ pub(crate) enum Ty {
|
||||||
BytesLiteral(&'static str),
|
BytesLiteral(&'static str),
|
||||||
// An enum literal variant, using `uuid.SafeUUID` as base
|
// An enum literal variant, using `uuid.SafeUUID` as base
|
||||||
EnumLiteral(&'static str),
|
EnumLiteral(&'static str),
|
||||||
|
// A single-member enum literal, using `dataclasses.MISSING`
|
||||||
|
SingleMemberEnumLiteral,
|
||||||
// BuiltinInstance("str") corresponds to an instance of the builtin `str` class
|
// BuiltinInstance("str") corresponds to an instance of the builtin `str` class
|
||||||
BuiltinInstance(&'static str),
|
BuiltinInstance(&'static str),
|
||||||
/// Members of the `abc` stdlib module
|
/// Members of the `abc` stdlib module
|
||||||
|
@ -145,6 +148,15 @@ impl Ty {
|
||||||
.expect_class_literal(),
|
.expect_class_literal(),
|
||||||
Name::new(name),
|
Name::new(name),
|
||||||
)),
|
)),
|
||||||
|
Ty::SingleMemberEnumLiteral => {
|
||||||
|
let ty = known_module_symbol(db, KnownModule::Dataclasses, "MISSING")
|
||||||
|
.place
|
||||||
|
.expect_type();
|
||||||
|
debug_assert!(
|
||||||
|
matches!(ty, Type::NominalInstance(instance) if is_single_member_enum(db, instance.class.class_literal(db).0))
|
||||||
|
);
|
||||||
|
ty
|
||||||
|
}
|
||||||
Ty::BuiltinInstance(s) => builtins_symbol(db, s)
|
Ty::BuiltinInstance(s) => builtins_symbol(db, s)
|
||||||
.place
|
.place
|
||||||
.expect_type()
|
.expect_type()
|
||||||
|
@ -265,6 +277,7 @@ fn arbitrary_core_type(g: &mut Gen, fully_static: bool) -> Ty {
|
||||||
Ty::EnumLiteral("safe"),
|
Ty::EnumLiteral("safe"),
|
||||||
Ty::EnumLiteral("unsafe"),
|
Ty::EnumLiteral("unsafe"),
|
||||||
Ty::EnumLiteral("unknown"),
|
Ty::EnumLiteral("unknown"),
|
||||||
|
Ty::SingleMemberEnumLiteral,
|
||||||
Ty::KnownClassInstance(KnownClass::Object),
|
Ty::KnownClassInstance(KnownClass::Object),
|
||||||
Ty::KnownClassInstance(KnownClass::Str),
|
Ty::KnownClassInstance(KnownClass::Str),
|
||||||
Ty::KnownClassInstance(KnownClass::Int),
|
Ty::KnownClassInstance(KnownClass::Int),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue