Skip to content

Commit 7208cf6

Browse files
committed
[ty] handle more enum.auto() cases
1 parent 09f570a commit 7208cf6

File tree

3 files changed

+51
-5
lines changed

3 files changed

+51
-5
lines changed

crates/ty_python_semantic/resources/mdtest/enums.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,11 @@ reveal_type(enum_members(Answer))
285285

286286
reveal_type(Answer.YES.value) # revealed: Literal[1]
287287
reveal_type(Answer.NO.value) # revealed: Literal[2]
288+
289+
class SingleMember(Enum):
290+
SINGLE = auto()
291+
292+
reveal_type(SingleMember.SINGLE.value) # revealed: Literal[1]
288293
```
289294

290295
Usages of `auto()` can be combined with manual value assignments:
@@ -313,6 +318,11 @@ class Answer(StrEnum):
313318

314319
reveal_type(Answer.YES.value) # revealed: Literal["yes"]
315320
reveal_type(Answer.NO.value) # revealed: Literal["no"]
321+
322+
class SingleMember(StrEnum):
323+
SINGLE = auto()
324+
325+
reveal_type(SingleMember.SINGLE.value) # revealed: Literal["single"]
316326
```
317327

318328
Using `auto()` with `IntEnum` also works as expected:
@@ -328,6 +338,24 @@ reveal_type(Answer.YES.value) # revealed: Literal[1]
328338
reveal_type(Answer.NO.value) # revealed: Literal[2]
329339
```
330340

341+
Using `auto()` with non-integer mixins:
342+
343+
```python
344+
from enum import Enum, auto
345+
346+
class A(str, Enum):
347+
X = auto()
348+
Y = auto()
349+
350+
reveal_type(A.X.value) # revealed: str
351+
352+
class B(bytes, Enum):
353+
X = auto()
354+
Y = auto()
355+
356+
reveal_type(B.X.value) # revealed: bytes
357+
```
358+
331359
Combining aliases with `auto()`:
332360

333361
```py

crates/ty_python_semantic/src/types.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3592,6 +3592,16 @@ impl<'db> Type<'db> {
35923592
.into()
35933593
}
35943594

3595+
Type::NominalInstance(instance)
3596+
if matches!(name_str, "value" | "_value_")
3597+
&& is_single_member_enum(db, instance.class(db).class_literal(db).0) =>
3598+
{
3599+
enum_metadata(db, instance.class(db).class_literal(db).0)
3600+
.and_then(|metadata| metadata.members.get_index(0).map(|(_, v)| *v))
3601+
.map_or(Place::Unbound, Place::bound)
3602+
.into()
3603+
}
3604+
35953605
Type::NominalInstance(..)
35963606
| Type::ProtocolInstance(..)
35973607
| Type::BooleanLiteral(..)

crates/ty_python_semantic/src/types/enums.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,6 @@ pub(crate) fn enum_metadata<'db>(
7777
return None;
7878
}
7979

80-
let is_str_enum =
81-
Type::ClassLiteral(class).is_subtype_of(db, KnownClass::StrEnum.to_subclass_of(db));
82-
8380
let scope_id = class.body_scope(db);
8481
let use_def_map = use_def_map(db, scope_id);
8582
let table = place_table(db, scope_id);
@@ -150,14 +147,25 @@ pub(crate) fn enum_metadata<'db>(
150147
// enum.auto
151148
Some(KnownClass::Auto) => {
152149
auto_counter += 1;
153-
Some(if is_str_enum {
150+
let auto_value_ty = if (Type::ClassLiteral(class)
151+
.is_subtype_of(db, KnownClass::StrEnum.to_subclass_of(db)))
152+
{
154153
Type::StringLiteral(StringLiteralType::new(
155154
db,
156155
name.to_lowercase().as_str(),
157156
))
157+
} else if (Type::ClassLiteral(class)
158+
.is_subtype_of(db, KnownClass::Str.to_subclass_of(db)))
159+
{
160+
KnownClass::Str.to_instance(db)
161+
} else if (Type::ClassLiteral(class)
162+
.is_subtype_of(db, KnownClass::Bytes.to_subclass_of(db)))
163+
{
164+
KnownClass::Bytes.to_instance(db)
158165
} else {
159166
Type::IntLiteral(auto_counter)
160-
})
167+
};
168+
Some(auto_value_ty)
161169
}
162170

163171
_ => None,

0 commit comments

Comments
 (0)