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

Code before:
    /// Returns `true` if this class defines any ordering method (`__lt__`, `__le__`, `__gt__`,
    /// `__ge__`) in its own body (not inherited). Used by `@total_ordering` to determine if
    /// synthesis is valid.
    #[salsa::tracked]
    pub(crate) fn has_own_ordering_method(self, db: &'db dyn Db) -> bool {
        let body_scope = self.body_scope(db);
        ["__lt__", "__le__", "__gt__", "__ge__"]
            .iter()
            .any(|method| !class_member(db, body_scope, method).is_undefined())
    }

    /// Returns `true` if any class in this class's MRO (excluding `object`) defines an ordering
    /// method (`__lt__`, `__le__`, `__gt__`, `__ge__`). Used by `@total_ordering` validation.
    pub(super) fn has_ordering_method_in_mro(
        self,
        db: &'db dyn Db,
        specialization: Option<Specialization<'db>>,
    ) -> bool {
        self.total_ordering_root_method(db, specialization)
            .is_some()
    }

    /// Returns the type of the ordering method used by `@total_ordering`, if any.
    ///
    /// We prefer methods defined on the class itself, following `functools.total_ordering`
    /// precedence, and fall back to inherited methods if none are defined locally.
    pub(super) fn total_ordering_root_method(
        self,
        db: &'db dyn Db,
        specialization: Option<Specialization<'db>>,
    ) -> Option<Type<'db>> {
        const ORDERING_METHODS: [&str; 4] = ["__lt__", "__le__", "__gt__", "__ge__"];

        let body_scope = self.body_scope(db);

        for name in ORDERING_METHODS {
            let member = class_member(db, body_scope, name);
            if let Some(ty) = member.ignore_possibly_undefined() {
                return Some(ty.apply_optional_specialization(db, specialization));
            }
        }

        for name in ORDERING_METHODS {
            for base in self.iter_mro(db, specialization).skip(1) {
                let Some(base_class) = base.into_class() else {
                    continue;
                };
                let (base_literal, base_specialization) = base_class.class_literal(db);
                if base_literal.is_known(db, KnownClass::Object) {
                    continue;
                }

Review comment (suggestion):
_Why_ do we "prefer methods defined on the class itself"? That doesn't seem to be how `total_ordering` behaves at runtime -- at runtime, it _always_ prefers a `__lt__` method over a `__gt__` method, even if the `__lt__` method was defined on a superclass and the `__gt__` method was defined on the class itself:

```pycon
>>> class Base:
...     def __lt__(self, other):
...         return True
...         
>>> class Child(Base):
...     def __gt__(self, other):
...         1/0
...         
>>> from functools import total_ordering
>>> Transformed = total_ordering(Child)
>>> Transformed() <= Transformed()
True
>>> Transformed() >= Transformed()
False
```

Code after:
    /// Returns `true` if this class defines any ordering method (`__lt__`, `__le__`, `__gt__`,
    /// `__ge__`) in its own body (not inherited). Used by `@total_ordering` to determine if
    /// synthesis is valid.
    #[salsa::tracked]
    pub(crate) fn has_own_ordering_method(self, db: &'db dyn Db) -> bool {
        let body_scope = self.body_scope(db);
        ["__lt__", "__le__", "__gt__", "__ge__"]
            .iter()
            .any(|method| !class_member(db, body_scope, method).is_undefined())
    }

    /// Returns `true` if any class in this class's MRO (excluding `object`) defines an ordering
    /// method (`__lt__`, `__le__`, `__gt__`, `__ge__`). Used by `@total_ordering` validation.
    pub(super) fn has_ordering_method_in_mro(
        self,
        db: &'db dyn Db,
        specialization: Option<Specialization<'db>>,
    ) -> bool {
        self.total_ordering_root_method(db, specialization)
            .is_some()
    }

    /// Returns the type of the ordering method used by `@total_ordering`, if any.
    ///
    /// Following `functools.total_ordering` precedence, we prefer `__lt__` > `__le__` > `__gt__` >
    /// `__ge__`, regardless of whether the method is defined locally or inherited.
    pub(super) fn total_ordering_root_method(
        self,
        db: &'db dyn Db,
        specialization: Option<Specialization<'db>>,
    ) -> Option<Type<'db>> {
        const ORDERING_METHODS: [&str; 4] = ["__lt__", "__le__", "__gt__", "__ge__"];

        for name in ORDERING_METHODS {
            for base in self.iter_mro(db, specialization) {
                let Some(base_class) = base.into_class() else {
                    continue;
                };
                let (base_literal, base_specialization) = base_class.class_literal(db);
                if base_literal.is_known(db, KnownClass::Object) {
                    continue;
                }
                let member = class_member(db, base_literal.body_scope(db), name);
                if let Some(ty) = member.ignore_possibly_undefined() {
                    return Some(ty.apply_optional_specialization(db, base_specialization));
                }
            }
        }

        None
    }