Skip to content

Commit d24b83b

Browse files
committed
[ty] handle more enum.auto() cases
1 parent 76efc80 commit d24b83b

File tree

4 files changed

+90
-7
lines changed

4 files changed

+90
-7
lines changed

crates/ty_python_semantic/resources/mdtest/enums.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,11 @@ reveal_type(enum_members(Answer))
320320

321321
reveal_type(Answer.YES.value) # revealed: Literal[1]
322322
reveal_type(Answer.NO.value) # revealed: Literal[2]
323+
324+
class SingleMember(Enum):
325+
SINGLE = auto()
326+
327+
reveal_type(SingleMember.SINGLE.value) # revealed: Literal[1]
323328
```
324329

325330
Usages of `auto()` can be combined with manual value assignments:
@@ -348,6 +353,11 @@ class Answer(StrEnum):
348353

349354
reveal_type(Answer.YES.value) # revealed: Literal["yes"]
350355
reveal_type(Answer.NO.value) # revealed: Literal["no"]
356+
357+
class SingleMember(StrEnum):
358+
SINGLE = auto()
359+
360+
reveal_type(SingleMember.SINGLE.value) # revealed: Literal["single"]
351361
```
352362

353363
Using `auto()` with `IntEnum` also works as expected:
@@ -363,6 +373,36 @@ reveal_type(Answer.YES.value) # revealed: Literal[1]
363373
reveal_type(Answer.NO.value) # revealed: Literal[2]
364374
```
365375

376+
Using `auto()` with non-integer mixins:
377+
378+
```python
379+
from enum import Enum, auto
380+
381+
class A(str, Enum):
382+
X = auto()
383+
Y = auto()
384+
385+
reveal_type(A.X.value) # revealed: Any
386+
387+
class B(bytes, Enum):
388+
X = auto()
389+
Y = auto()
390+
391+
reveal_type(B.X.value) # revealed: Any
392+
393+
class C(tuple, Enum):
394+
X = auto()
395+
Y = auto()
396+
397+
reveal_type(C.X.value) # revealed: Any
398+
399+
class D(float, Enum):
400+
X = auto()
401+
Y = auto()
402+
403+
reveal_type(D.X.value) # revealed: Any
404+
```
405+
366406
Combining aliases with `auto()`:
367407

368408
```py

crates/ty_python_semantic/src/types.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4365,6 +4365,16 @@ impl<'db> Type<'db> {
43654365
Place::bound(todo_type!("ParamSpecArgs / ParamSpecKwargs")).into()
43664366
}
43674367

4368+
Type::NominalInstance(instance)
4369+
if matches!(name_str, "value" | "_value_")
4370+
&& is_single_member_enum(db, instance.class(db).class_literal(db).0) =>
4371+
{
4372+
enum_metadata(db, instance.class(db).class_literal(db).0)
4373+
.and_then(|metadata| metadata.members.get_index(0).map(|(_, v)| *v))
4374+
.map_or(Place::Undefined, Place::bound)
4375+
.into()
4376+
}
4377+
43684378
Type::NominalInstance(..)
43694379
| Type::ProtocolInstance(..)
43704380
| Type::BooleanLiteral(..)

crates/ty_python_semantic/src/types/class.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3778,6 +3778,7 @@ pub enum KnownClass {
37783778
Member,
37793779
Nonmember,
37803780
StrEnum,
3781+
IntEnum,
37813782
// abc
37823783
ABCMeta,
37833784
// Types
@@ -3925,6 +3926,7 @@ impl KnownClass {
39253926
| Self::Member
39263927
| Self::Nonmember
39273928
| Self::StrEnum
3929+
| Self::IntEnum
39283930
| Self::ABCMeta
39293931
| Self::Iterable
39303932
| Self::Iterator
@@ -3986,6 +3988,7 @@ impl KnownClass {
39863988
| KnownClass::Member
39873989
| KnownClass::Nonmember
39883990
| KnownClass::StrEnum
3991+
| KnownClass::IntEnum
39893992
| KnownClass::ABCMeta
39903993
| KnownClass::GenericAlias
39913994
| KnownClass::ModuleType
@@ -4069,6 +4072,7 @@ impl KnownClass {
40694072
| KnownClass::Member
40704073
| KnownClass::Nonmember
40714074
| KnownClass::StrEnum
4075+
| KnownClass::IntEnum
40724076
| KnownClass::ABCMeta
40734077
| KnownClass::GenericAlias
40744078
| KnownClass::ModuleType
@@ -4152,6 +4156,7 @@ impl KnownClass {
41524156
| KnownClass::Member
41534157
| KnownClass::Nonmember
41544158
| KnownClass::StrEnum
4159+
| KnownClass::IntEnum
41554160
| KnownClass::ABCMeta
41564161
| KnownClass::GenericAlias
41574162
| KnownClass::ModuleType
@@ -4274,6 +4279,7 @@ impl KnownClass {
42744279
| Self::Member
42754280
| Self::Nonmember
42764281
| Self::StrEnum
4282+
| Self::IntEnum
42774283
| Self::ABCMeta
42784284
| Self::Super
42794285
| Self::StdlibAlias
@@ -4330,6 +4336,7 @@ impl KnownClass {
43304336
| KnownClass::Member
43314337
| KnownClass::Nonmember
43324338
| KnownClass::StrEnum
4339+
| KnownClass::IntEnum
43334340
| KnownClass::ABCMeta
43344341
| KnownClass::GenericAlias
43354342
| KnownClass::ModuleType
@@ -4447,6 +4454,7 @@ impl KnownClass {
44474454
Self::Member => "member",
44484455
Self::Nonmember => "nonmember",
44494456
Self::StrEnum => "StrEnum",
4457+
Self::IntEnum => "IntEnum",
44504458
Self::ABCMeta => "ABCMeta",
44514459
Self::Super => "super",
44524460
Self::Iterable => "Iterable",
@@ -4719,7 +4727,8 @@ impl KnownClass {
47194727
| Self::Auto
47204728
| Self::Member
47214729
| Self::Nonmember
4722-
| Self::StrEnum => KnownModule::Enum,
4730+
| Self::StrEnum
4731+
| Self::IntEnum => KnownModule::Enum,
47234732
Self::GenericAlias
47244733
| Self::ModuleType
47254734
| Self::FunctionType
@@ -4850,6 +4859,7 @@ impl KnownClass {
48504859
| Self::Member
48514860
| Self::Nonmember
48524861
| Self::StrEnum
4862+
| Self::IntEnum
48534863
| Self::ABCMeta
48544864
| Self::Super
48554865
| Self::NewType
@@ -4937,6 +4947,7 @@ impl KnownClass {
49374947
| Self::Member
49384948
| Self::Nonmember
49394949
| Self::StrEnum
4950+
| Self::IntEnum
49404951
| Self::ABCMeta
49414952
| Self::Super
49424953
| Self::UnionType
@@ -5106,6 +5117,7 @@ impl KnownClass {
51065117
| Self::Member
51075118
| Self::Nonmember
51085119
| Self::StrEnum
5120+
| Self::IntEnum
51095121
| Self::ABCMeta
51105122
| Self::Super
51115123
| Self::NotImplementedType

crates/ty_python_semantic/src/types/enums.rs

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,6 @@ pub(crate) fn enum_metadata<'db>(
6868
return None;
6969
}
7070

71-
let is_str_enum =
72-
Type::ClassLiteral(class).is_subtype_of(db, KnownClass::StrEnum.to_subclass_of(db));
73-
7471
let scope_id = class.body_scope(db);
7572
let use_def_map = use_def_map(db, scope_id);
7673
let table = place_table(db, scope_id);
@@ -141,14 +138,38 @@ pub(crate) fn enum_metadata<'db>(
141138
// enum.auto
142139
Some(KnownClass::Auto) => {
143140
auto_counter += 1;
144-
Some(if is_str_enum {
141+
let auto_value_ty = if Type::ClassLiteral(class)
142+
.is_subtype_of(db, KnownClass::StrEnum.to_subclass_of(db))
143+
{
145144
Type::StringLiteral(StringLiteralType::new(
146145
db,
147146
name.to_lowercase().as_str(),
148147
))
149-
} else {
148+
} else if Type::ClassLiteral(class)
149+
.is_subtype_of(db, KnownClass::IntEnum.to_subclass_of(db))
150+
{
150151
Type::IntLiteral(auto_counter)
151-
})
152+
} else {
153+
let has_custom_mixin =
154+
class.iter_mro(db, None).skip(1).any(|base| {
155+
base.into_class().is_some_and(|class| {
156+
!(class.is_object(db)
157+
|| KnownClass::Enum
158+
.to_subclass_of(db)
159+
.to_class_type(db)
160+
.is_some_and(|enum_class| {
161+
class.is_subclass_of(db, enum_class)
162+
})
163+
|| class.is_known(db, KnownClass::Enum))
164+
})
165+
});
166+
if has_custom_mixin {
167+
Type::any()
168+
} else {
169+
Type::IntLiteral(auto_counter)
170+
}
171+
};
172+
Some(auto_value_ty)
152173
}
153174

154175
_ => None,

0 commit comments

Comments
 (0)