Skip to content

Commit 05ad42a

Browse files
committed
Generalize special case for enums that have int mixins
1 parent b69c358 commit 05ad42a

File tree

3 files changed

+44
-36
lines changed

3 files changed

+44
-36
lines changed

crates/ty_python_semantic/resources/mdtest/enums.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,19 @@ reveal_type(Answer.YES.value) # revealed: Literal[1]
373373
reveal_type(Answer.NO.value) # revealed: Literal[2]
374374
```
375375

376+
As does using `auto()` for other enums that use `int` as a mixin:
377+
378+
```py
379+
from enum import Enum, auto
380+
381+
class Answer(int, Enum):
382+
YES = auto()
383+
NO = auto()
384+
385+
reveal_type(Answer.YES.value) # revealed: Literal[1]
386+
reveal_type(Answer.NO.value) # revealed: Literal[2]
387+
```
388+
376389
Using `auto()` with non-integer mixins:
377390

378391
```python

crates/ty_python_semantic/src/types/class.rs

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3778,7 +3778,6 @@ pub enum KnownClass {
37783778
Member,
37793779
Nonmember,
37803780
StrEnum,
3781-
IntEnum,
37823781
// abc
37833782
ABCMeta,
37843783
// Types
@@ -3926,7 +3925,6 @@ impl KnownClass {
39263925
| Self::Member
39273926
| Self::Nonmember
39283927
| Self::StrEnum
3929-
| Self::IntEnum
39303928
| Self::ABCMeta
39313929
| Self::Iterable
39323930
| Self::Iterator
@@ -3988,7 +3986,6 @@ impl KnownClass {
39883986
| KnownClass::Member
39893987
| KnownClass::Nonmember
39903988
| KnownClass::StrEnum
3991-
| KnownClass::IntEnum
39923989
| KnownClass::ABCMeta
39933990
| KnownClass::GenericAlias
39943991
| KnownClass::ModuleType
@@ -4072,7 +4069,6 @@ impl KnownClass {
40724069
| KnownClass::Member
40734070
| KnownClass::Nonmember
40744071
| KnownClass::StrEnum
4075-
| KnownClass::IntEnum
40764072
| KnownClass::ABCMeta
40774073
| KnownClass::GenericAlias
40784074
| KnownClass::ModuleType
@@ -4156,7 +4152,6 @@ impl KnownClass {
41564152
| KnownClass::Member
41574153
| KnownClass::Nonmember
41584154
| KnownClass::StrEnum
4159-
| KnownClass::IntEnum
41604155
| KnownClass::ABCMeta
41614156
| KnownClass::GenericAlias
41624157
| KnownClass::ModuleType
@@ -4279,7 +4274,6 @@ impl KnownClass {
42794274
| Self::Member
42804275
| Self::Nonmember
42814276
| Self::StrEnum
4282-
| Self::IntEnum
42834277
| Self::ABCMeta
42844278
| Self::Super
42854279
| Self::StdlibAlias
@@ -4336,7 +4330,6 @@ impl KnownClass {
43364330
| KnownClass::Member
43374331
| KnownClass::Nonmember
43384332
| KnownClass::StrEnum
4339-
| KnownClass::IntEnum
43404333
| KnownClass::ABCMeta
43414334
| KnownClass::GenericAlias
43424335
| KnownClass::ModuleType
@@ -4454,7 +4447,6 @@ impl KnownClass {
44544447
Self::Member => "member",
44554448
Self::Nonmember => "nonmember",
44564449
Self::StrEnum => "StrEnum",
4457-
Self::IntEnum => "IntEnum",
44584450
Self::ABCMeta => "ABCMeta",
44594451
Self::Super => "super",
44604452
Self::Iterable => "Iterable",
@@ -4727,8 +4719,7 @@ impl KnownClass {
47274719
| Self::Auto
47284720
| Self::Member
47294721
| Self::Nonmember
4730-
| Self::StrEnum
4731-
| Self::IntEnum => KnownModule::Enum,
4722+
| Self::StrEnum => KnownModule::Enum,
47324723
Self::GenericAlias
47334724
| Self::ModuleType
47344725
| Self::FunctionType
@@ -4859,7 +4850,6 @@ impl KnownClass {
48594850
| Self::Member
48604851
| Self::Nonmember
48614852
| Self::StrEnum
4862-
| Self::IntEnum
48634853
| Self::ABCMeta
48644854
| Self::Super
48654855
| Self::NewType
@@ -4947,7 +4937,6 @@ impl KnownClass {
49474937
| Self::Member
49484938
| Self::Nonmember
49494939
| Self::StrEnum
4950-
| Self::IntEnum
49514940
| Self::ABCMeta
49524941
| Self::Super
49534942
| Self::UnionType
@@ -5039,9 +5028,6 @@ impl KnownClass {
50395028
"StrEnum" if Program::get(db).python_version(db) >= PythonVersion::PY311 => {
50405029
&[Self::StrEnum]
50415030
}
5042-
"IntEnum" if Program::get(db).python_version(db) >= PythonVersion::PY311 => {
5043-
&[Self::IntEnum]
5044-
}
50455031
"auto" => &[Self::Auto],
50465032
"member" => &[Self::Member],
50475033
"nonmember" => &[Self::Nonmember],
@@ -5120,7 +5106,6 @@ impl KnownClass {
51205106
| Self::Member
51215107
| Self::Nonmember
51225108
| Self::StrEnum
5123-
| Self::IntEnum
51245109
| Self::ABCMeta
51255110
| Self::Super
51265111
| Self::NotImplementedType

crates/ty_python_semantic/src/types/enums.rs

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{
66
place::{Place, PlaceAndQualifiers, place_from_bindings, place_from_declarations},
77
semantic_index::{place_table, use_def_map},
88
types::{
9-
ClassLiteral, DynamicType, EnumLiteralType, KnownClass, MemberLookupPolicy,
9+
ClassBase, ClassLiteral, DynamicType, EnumLiteralType, KnownClass, MemberLookupPolicy,
1010
StringLiteralType, Type, TypeQualifiers,
1111
},
1212
};
@@ -138,35 +138,45 @@ pub(crate) fn enum_metadata<'db>(
138138
// enum.auto
139139
Some(KnownClass::Auto) => {
140140
auto_counter += 1;
141+
142+
// `StrEnum`s have different `auto()` behaviour to enums inheriting from `(str, Enum)`
141143
let auto_value_ty = if Type::ClassLiteral(class)
142144
.is_subtype_of(db, KnownClass::StrEnum.to_subclass_of(db))
143145
{
144146
Type::StringLiteral(StringLiteralType::new(
145147
db,
146148
name.to_lowercase().as_str(),
147149
))
148-
} else if Type::ClassLiteral(class)
149-
.is_subtype_of(db, KnownClass::IntEnum.to_subclass_of(db))
150-
{
151-
Type::IntLiteral(auto_counter)
152150
} 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))
151+
let custom_mixins: smallvec::SmallVec<[Option<KnownClass>; 1]> =
152+
class
153+
.iter_mro(db, None)
154+
.skip(1)
155+
.filter_map(ClassBase::into_class)
156+
.filter(|class| {
157+
!Type::from(*class).is_subtype_of(
158+
db,
159+
KnownClass::Enum.to_subclass_of(db),
160+
)
164161
})
165-
});
166-
if has_custom_mixin {
167-
Type::any()
168-
} else {
162+
.map(|class| class.known(db))
163+
.filter(|class| {
164+
!matches!(class, Some(KnownClass::Object))
165+
})
166+
.collect();
167+
168+
// `IntEnum`s have the same `auto()` behaviour to enums inheriting from `(int, Enum)`,
169+
// and `IntEnum`s also have `int` in their MROs, so both cases are handled here.
170+
//
171+
// In general, the `auto()` behaviour for enums with non-`int` mixins is hard to predict,
172+
// so we fall back to `Any` in those cases.
173+
if matches!(
174+
custom_mixins.as_slice(),
175+
[] | [Some(KnownClass::Int)]
176+
) {
169177
Type::IntLiteral(auto_counter)
178+
} else {
179+
Type::any()
170180
}
171181
};
172182
Some(auto_value_ty)

0 commit comments

Comments
 (0)