From 0d7ed32494df58f62ed6b48a3491f258ca7fd086 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 29 Aug 2025 08:31:26 +0100 Subject: [PATCH] [ty] Enforce that an attribute on a class `X` must be callable in order to satisfy a member on a protocol `P` (#20142) ## Summary Small, incremental progress towards checking the types of method members. ## Test Plan Added an mdtest --- crates/ty_python_semantic/resources/mdtest/protocols.md | 4 ++++ crates/ty_python_semantic/src/types/protocol_class.rs | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/ty_python_semantic/resources/mdtest/protocols.md b/crates/ty_python_semantic/resources/mdtest/protocols.md index 427868e855..b94f2277a9 100644 --- a/crates/ty_python_semantic/resources/mdtest/protocols.md +++ b/crates/ty_python_semantic/resources/mdtest/protocols.md @@ -1704,7 +1704,11 @@ class NotSubtype: def m(self, x: int) -> int: return 42 +class DefinitelyNotSubtype: + m = None + static_assert(is_subtype_of(NominalSubtype, P)) +static_assert(not is_subtype_of(DefinitelyNotSubtype, P)) # TODO: should pass static_assert(not is_subtype_of(NotSubtype, P)) # error: [static-assert-error] diff --git a/crates/ty_python_semantic/src/types/protocol_class.rs b/crates/ty_python_semantic/src/types/protocol_class.rs index 3135c12011..20811ca497 100644 --- a/crates/ty_python_semantic/src/types/protocol_class.rs +++ b/crates/ty_python_semantic/src/types/protocol_class.rs @@ -560,7 +560,8 @@ impl<'a, 'db> ProtocolMember<'a, 'db> { db, matches!( other.to_meta_type(db).member(db, self.name).place, - Place::Type(_, Boundness::Bound) + Place::Type(ty, Boundness::Bound) + if ty.is_assignable_to(db, CallableType::single(db, Signature::dynamic(Type::any()))) ), ), // TODO: consider the types of the attribute on `other` for property members