Repository: astral-sh/ruff
File: crates/ty_python_semantic/src/types/enums.rs (Rust)

Code before:
                                    .ignore_possibly_unbound()
                                    .unwrap_or(Type::unknown()),
                            ),

                            // enum.auto
                            Some(KnownClass::Auto) => {
                                auto_counter += 1;
                                let auto_value_ty = if Type::ClassLiteral(class)
                                    .is_subtype_of(db, KnownClass::StrEnum.to_subclass_of(db))
                                {
                                    Type::StringLiteral(StringLiteralType::new(
                                        db,
                                        name.to_lowercase().as_str(),
                                    ))
                                } else if Type::ClassLiteral(class)
                                    .is_subtype_of(db, KnownClass::Str.to_subclass_of(db))
                                {
                                    KnownClass::Str.to_instance(db)
                                } else if Type::ClassLiteral(class)
                                    .is_subtype_of(db, KnownClass::Bytes.to_subclass_of(db))
                                {
                                    KnownClass::Bytes.to_instance(db)
                                } else if Type::ClassLiteral(class)
                                    .is_subtype_of(db, KnownClass::Float.to_subclass_of(db))
                                {
                                    KnownClass::Float.to_instance(db)
                                } else {
                                    Type::IntLiteral(auto_counter)
                                };
                                Some(auto_value_ty)
                            }

                            _ => None,
                        },

                        _ => None,
                    };

                    if let Some(special_case) = special_case {
                        special_case
                    } else {
                        let dunder_get = ty
                            .member_lookup_with_policy(
                                db,
                                "__get__".into(),
                                MemberLookupPolicy::NO_INSTANCE_FALLBACK,
                            )
                            .place;

                        match dunder_get {
                            Place::Unbound | Place::Type(Type::Dynamic(_), _) => ty,

Review comment (suggestion):
hmm, I'm not sure we should be adding more special cases here. An enum class that uses `bytes` as a mixin isn't conceptually any different from an enum that uses `tuple` or `list` or `dict` as a mixin, or a custom dataclass or custom `NamedTuple` as a mixin. Why should we treat `bytes` and `float` specially here?

Rather than adding more special cases, I think we should have a generalised fallback logic that iterates through the MRO of the enum class to check whether it has a custom mixin that isn't `str` or `int`. (Those two make sense to special-case, I think, because of the presence of `StrEnum` and `IntEnum` in the stdlib. You can determine whether an element in the MRO is a "custom mixin" class by checking if it isn't `object` and isn't a subclass of `enum.Enum` -- if both those conditions hold, I think we can treat it as a mixin class.) If the enum class has a custom mixin and `auto()` is used to define the value of an enum member, we should probably infer the `.value` of that enum member as just being `Any`, which would still be more accurate than what we have on `main` (even if it isn't very precise!). It seems pretty hard to predict whether the `.value` property is going to give you an instance of the mixin or an integer:

```pycon
>>> class Foo: ...
>>> import enum
>>> class FooEnum(Foo, enum.Enum):
...     X = enum.auto()
...     
>>> FooEnum.X.value
1
>>> class BEnum(bytes, enum.Enum):
...     X = enum.auto()
...     
>>> BEnum.X.value
b'\x00'
>>> class TEnum(tuple, enum.Enum):
...     X = enum.auto()
...     
>>> TEnum.X.value
(1,)
```

Code after:
                                let auto_value_ty = if Type::ClassLiteral(class)
                                    .is_subtype_of(db, KnownClass::StrEnum.to_subclass_of(db))
                                {
                                    Type::StringLiteral(StringLiteralType::new(
                                        db,
                                        name.to_lowercase().as_str(),
                                    ))
                                } else {
                                    let custom_mixins: smallvec::SmallVec<[Option<KnownClass>; 1]> =
                                        class
                                            .iter_mro(db, None)
                                            .skip(1)
                                            .filter_map(ClassBase::into_class)
                                            .filter(|class| {
                                                !Type::from(*class).is_subtype_of(
                                                    db,
                                                    KnownClass::Enum.to_subclass_of(db),
                                                )
                                            })
                                            .map(|class| class.known(db))
                                            .filter(|class| {
                                                !matches!(class, Some(KnownClass::Object))
                                            })
                                            .collect();

                                    // `IntEnum`s have the same `auto()` behaviour to enums inheriting from `(int, Enum)`,
                                    // and `IntEnum`s also have `int` in their MROs, so both cases are handled here.
                                    //
                                    // In general, the `auto()` behaviour for enums with non-`int` mixins is hard to predict,
                                    // so we fall back to `Any` in those cases.
                                    if matches!(
                                        custom_mixins.as_slice(),
                                        [] | [Some(KnownClass::Int)]
                                    ) {
                                        Type::IntLiteral(auto_counter)
                                    } else {
                                        Type::any()
                                    }
                                };
                                Some(auto_value_ty)
                            }

                            _ => None,
                        },

                        _ => None,
                    };

                    if let Some(special_case) = special_case {
                        special_case
                    } else {