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

Code before:
                    Type::Callable(callable_ty.apply_self(db, self_instance))
                }
                Type::Union(union) => union.map(db, |element| {
                    apply_self_to_callable(db, *element, self_instance)
                }),
                Type::Intersection(intersection) => intersection.map_positive(db, |element| {
                    apply_self_to_callable(db, *element, self_instance)
                }),
                _ => ty,
            }
        }

        let mut member = self.class_member_inner(db, None, name, policy);

        // We generally treat dunder attributes with `Callable` types as function-like callables.
        // See `callables_as_descriptors.md` for more details.
        if name.starts_with("__") && name.ends_with("__") {
            member = member.map_type(|ty| into_function_like_callable(db, ty));
        }

        // For callable members that use `Self` type variables (e.g., synthesized methods like
        // `_replace` on NamedTuples), replace `Self` with the instance type of this class.
        // This ensures that when accessing `Person._replace`, we get `(self: Person, ...) -> Person`
        // instead of `(self: Self, ...) -> Self`.
        let self_instance = Type::instance(db, ClassType::NonGeneric(self));
        member = member.map_type(|ty| apply_self_to_callable(db, ty, self_instance));

        member
    }

    fn class_member_inner(
        self,
        db: &'db dyn Db,
        specialization: Option<Specialization<'db>>,
        name: &str,
        policy: MemberLookupPolicy,
    ) -> PlaceAndQualifiers<'db> {
        self.class_member_from_mro(db, name, policy, self.iter_mro(db, specialization))
    }

    pub(super) fn class_member_from_mro(
        self,
        db: &'db dyn Db,
        name: &str,
        policy: MemberLookupPolicy,
        mro_iter: impl Iterator<Item = ClassBase<'db>>,
    ) -> PlaceAndQualifiers<'db> {
        // If we encounter a dynamic type in this class's MRO, we'll save that dynamic type
        // in this variable. After we've traversed the MRO, we'll either:
        // (1) Use that dynamic type as the type for this attribute,
        //     if no other classes in the MRO define the attribute; or,

Review comment (suggestion):
What's not clear to me is why we are accessing `person._replace` on the class to begin with, when we are calling it on an instance. That's something we do for dunder methods (to mimic the runtime, which also does that), but it's not something we should do for a normal instance method call; `_replace` is not a dunder method. That seems to be at the root of the need for this.

Code after:

        member
    }

    fn class_member_inner(
        self,
        db: &'db dyn Db,
        specialization: Option<Specialization<'db>>,
        name: &str,
        policy: MemberLookupPolicy,
    ) -> PlaceAndQualifiers<'db> {
        self.class_member_from_mro(db, name, policy, self.iter_mro(db, specialization))
    }

    pub(super) fn class_member_from_mro(
        self,
        db: &'db dyn Db,
        name: &str,
        policy: MemberLookupPolicy,
        mro_iter: impl Iterator<Item = ClassBase<'db>>,
    ) -> PlaceAndQualifiers<'db> {
        // If we encounter a dynamic type in this class's MRO, we'll save that dynamic type
        // in this variable. After we've traversed the MRO, we'll either:
        // (1) Use that dynamic type as the type for this attribute,
        //     if no other classes in the MRO define the attribute; or,
        // (2) Intersect that dynamic type with the type of the attribute
        //     from the non-dynamic members of the class's MRO.
        let mut dynamic_type_to_intersect_with: Option<Type<'db>> = None;

        let mut lookup_result: LookupResult<'db> =
            Err(LookupError::Undefined(TypeQualifiers::empty()));

        for superclass in mro_iter {
            match superclass {
                ClassBase::Generic | ClassBase::Protocol => {
                    // Skip over these very special class bases that aren't really classes.
                }
                ClassBase::Dynamic(_) => {
                    // Note: calling `Type::from(superclass).member()` would be incorrect here.
                    // What we'd really want is a `Type::Any.own_class_member()` method,
                    // but adding such a method wouldn't make much sense -- it would always return `Any`!
                    dynamic_type_to_intersect_with.get_or_insert(Type::from(superclass));
                }
                ClassBase::Class(class) => {
                    let known = class.known(db);

                    if known == Some(KnownClass::Object)
                        // Only exclude `object` members if this is not an `object` class itself
                        && (policy.mro_no_object_fallback() && !self.is_known(db, KnownClass::Object))
                    {
                        continue;