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

Code before:
                            Place::Undefined.into(),
                            InstanceFallbackShadowsNonDataDescriptor::No,
                            MemberLookupPolicy::default(),
                        )
                        .place
                    else {
                        return ConstraintSet::from(false);
                    };
                    attribute_type
                };

                // TODO: Instances of `typing.Self` in the protocol member should specialize to the
                // type that we are checking. Without this, we will treat `Self` as an inferable
                // typevar, and allow it to match against _any_ type.
                //
                // It's not very principled, but we also use the literal fallback type, instead of
                // `other` directly. This lets us check whether things like `Literal[0]` satisfy a
                // protocol that includes methods that have `typing.Self` annotations, without
                // overly constraining `Self` to that specific literal.
                //
                // With the new solver, we should be to replace all of this with an additional
                // constraint that enforces what `Self` can specialize to.
                let fallback_other = other.literal_fallback_instance(db).unwrap_or(other);
                attribute_type
                    .try_upcast_to_callable(db)
                    .map(|callable| callable.apply_self(db, fallback_other))
                    .has_relation_to_impl(
                        db,
                        method.bind_self(db, Some(fallback_other)),
                        inferable,
                        relation,
                        relation_visitor,
                        disjointness_visitor,
                    )
            }
            // TODO: consider the types of the attribute on `other` for property members
            ProtocolMemberKind::Property(_) => ConstraintSet::from(matches!(
                other.member(db, self.name).place,
                Place::Defined(_, _, Definedness::AlwaysDefined)
            )),
            ProtocolMemberKind::Other(member_type) => {
                let Place::Defined(attribute_type, _, Definedness::AlwaysDefined) =
                    other.member(db, self.name).place
                else {
                    return ConstraintSet::from(false);
                };
                member_type
                    .has_relation_to_impl(
                        db,
                        attribute_type,
                        inferable,

Review comment (suggestion):
to make sure I understand correctly:
- we previously implicitly upcasted `attribute_type` to `Callable` as part of the `attribute_type.has_relation_to_impl(...)` check, because we have to upcast something like a `Type::FunctionLiteral` if we want to check whether it's assignable to a `Type::Callable`
- but we now `.apply_self` to the `Callable` supertype of `attribute_type` before we check whether `attribute_type <: other`, so we move the `Callable`-upcasting here

Code after:
                            Place::Undefined.into(),
                            InstanceFallbackShadowsNonDataDescriptor::No,
                            MemberLookupPolicy::default(),
                        )
                        .place
                    else {
                        return ConstraintSet::from(false);
                    };
                    attribute_type
                };

                // TODO: Instances of `typing.Self` in the protocol member should specialize to the
                // type that we are checking. Without this, we will treat `Self` as an inferable
                // typevar, and allow it to match against _any_ type.
                //
                // It's not very principled, but we also use the literal fallback type, instead of
                // `other` directly. This lets us check whether things like `Literal[0]` satisfy a
                // protocol that includes methods that have `typing.Self` annotations, without
                // overly constraining `Self` to that specific literal.
                //
                // With the new solver, we should be to replace all of this with an additional
                // constraint that enforces what `Self` can specialize to.
                let fallback_other = other.literal_fallback_instance(db).unwrap_or(other);
                attribute_type
                    .try_upcast_to_callable(db)
                    .when_some_and(|callables| {
                        callables
                            .map(|callable| callable.apply_self(db, fallback_other))
                            .has_relation_to_impl(
                                db,
                                method.bind_self(db, Some(fallback_other)),
                                inferable,
                                relation,
                                relation_visitor,
                                disjointness_visitor,
                            )
                    })
            }
            // TODO: consider the types of the attribute on `other` for property members
            ProtocolMemberKind::Property(_) => ConstraintSet::from(matches!(
                other.member(db, self.name).place,
                Place::Defined(_, _, Definedness::AlwaysDefined)
            )),
            ProtocolMemberKind::Other(member_type) => {
                let Place::Defined(attribute_type, _, Definedness::AlwaysDefined) =
                    other.member(db, self.name).place
                else {
                    return ConstraintSet::from(false);
                };
                member_type
                    .has_relation_to_impl(