From 5350288d0773f986e90653c44a6304d9411b5782 Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Thu, 17 Apr 2025 00:21:59 +0530 Subject: [PATCH] [red-knot] Check assignability of bound methods to callables (#17430) ## Summary This is similar to https://github.com/astral-sh/ruff/pull/17095, it adds assignability check for bound methods to callables. ## Test Plan Add test cases to for assignability; specifically it uses gradual types because otherwise it would just delegate to `is_subtype_of`. --- .../mdtest/type_properties/is_assignable_to.md | 18 ++++++++++++++++++ crates/red_knot_python_semantic/src/types.rs | 4 ++++ 2 files changed, 22 insertions(+) diff --git a/crates/red_knot_python_semantic/resources/mdtest/type_properties/is_assignable_to.md b/crates/red_knot_python_semantic/resources/mdtest/type_properties/is_assignable_to.md index b128fe8013..5ca7c9c628 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/type_properties/is_assignable_to.md +++ b/crates/red_knot_python_semantic/resources/mdtest/type_properties/is_assignable_to.md @@ -504,4 +504,22 @@ c: Callable[[Any], str] = f c: Callable[[Any], str] = g ``` +### Method types + +```py +from typing import Any, Callable + +class A: + def f(self, x: Any) -> str: + return "" + + def g(self, x: Any) -> int: + return 1 + +c: Callable[[Any], str] = A().f + +# error: [invalid-assignment] "Object of type `bound method A.g(x: Any) -> int` is not assignable to `(Any, /) -> str`" +c: Callable[[Any], str] = A().g +``` + [typing documentation]: https://typing.python.org/en/latest/spec/concepts.html#the-assignable-to-or-consistent-subtyping-relation diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index 6e33f79847..9f3b17f3b6 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -1368,6 +1368,10 @@ impl<'db> Type<'db> { .is_assignable_to(db, target) } + (Type::BoundMethod(self_bound_method), Type::Callable(_)) => self_bound_method + .into_callable_type(db) + .is_assignable_to(db, target), + // TODO other types containing gradual forms (e.g. generics containing Any/Unknown) _ => self.is_subtype_of(db, target), }