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

Code before:
                db,
                ProtocolInterface::with_property_members(db, members),
                &NormalizedVisitor::default(),
            ),
        ))
    }

    /// Return `true` if `self` conforms to the interface described by `protocol`.
    pub(super) fn satisfies_protocol(
        self,
        db: &'db dyn Db,
        protocol: ProtocolInstanceType<'db>,
        inferable: InferableTypeVars<'_, 'db>,
        relation: TypeRelation<'db>,
        relation_visitor: &HasRelationToVisitor<'db>,
        disjointness_visitor: &IsDisjointVisitor<'db>,
    ) -> ConstraintSet<'db> {
        // Iff we're checking a NamedTuple against NamedTupleLike, skip the `_replace` method
        // signature. NamedTuples synthesize `_replace` methods with specific keyword-only
        // parameters (to detect invalid arguments), which are not strictly subtypes of the
        // protocol's `(**kwargs)` signature, but are intended to be considered as satisfying it.
        let is_namedtuple_protocol_check = matches!(&protocol.inner, Protocol::FromClass(class) if class.is_known(db, KnownClass::NamedTupleLike))
            && self.as_nominal_instance().is_some_and(|instance| {
                let (class_literal, specialization) = instance.class(db).class_literal(db);
                CodeGeneratorKind::NamedTuple.matches(db, class_literal, specialization)
            });

        let structurally_satisfied = if let Type::ProtocolInstance(self_protocol) = self {
            self_protocol.interface(db).has_relation_to_impl(
                db,
                protocol.interface(db),
                inferable,
                relation,
                relation_visitor,
                disjointness_visitor,
            )
        } else {
            protocol
                .inner
                .interface(db)
                .members(db)
                .filter(|member| {
                    // Skip `_replace` check for NamedTuple vs NamedTupleLike. NamedTuples
                    // synthesize `_replace` with specific keyword-only parameters to detect
                    // invalid arguments, but this signature is not a strict subtype of the
                    // protocol's `(**kwargs)` signature.
                    if is_namedtuple_protocol_check && member.name() == "_replace" {
                        return false;
                    }
                    true
                })

Review comment (suggestion):
I guess for the mypy equivalent we'd just totally short-circuit here and return true, since we've already done the work to check that we're comparing a named-tuple with `NamedTupleLike`. That seems... probably fine in practice? I'd want to see if @AlexWaygood knows of any reason we shouldn't just do that.

Code after:
                ProtocolInterface::with_property_members(db, members),
                &NormalizedVisitor::default(),
            ),
        ))
    }

    /// Return `true` if `self` conforms to the interface described by `protocol`.
    pub(super) fn satisfies_protocol(
        self,
        db: &'db dyn Db,
        protocol: ProtocolInstanceType<'db>,
        inferable: InferableTypeVars<'_, 'db>,
        relation: TypeRelation<'db>,
        relation_visitor: &HasRelationToVisitor<'db>,
        disjointness_visitor: &IsDisjointVisitor<'db>,
    ) -> ConstraintSet<'db> {
        let structurally_satisfied = if let Type::ProtocolInstance(self_protocol) = self {
            self_protocol.interface(db).has_relation_to_impl(
                db,
                protocol.interface(db),
                inferable,
                relation,
                relation_visitor,
                disjointness_visitor,
            )
        } else {
            protocol
                .inner
                .interface(db)
                .members(db)
                .when_all(db, |member| {
                    member.is_satisfied_by(
                        db,
                        self,
                        inferable,
                        relation,
                        relation_visitor,
                        disjointness_visitor,
                    )
                })
        };

        // Even if `self` does not satisfy the protocol from a structural perspective,
        // we may still need to consider it as satisfying the protocol if `protocol` is
        // a class-based protocol and `self` has the protocol class in its MRO.
        //
        // This matches the behaviour of other type checkers, and is required for us to
        // recognise `str` as a subtype of `Container[str]`.
        structurally_satisfied.or(db, || {
            let Some(nominal_instance) = protocol.as_nominal_type() else {
                return ConstraintSet::from(false);