ruff/crates/ty_python_semantic/resources/mdtest/call
Alex Waygood bab688b76c
[ty] Retain the function-like-ness of `Callable` types when binding `self` (#21614)
## Summary

For something like this:

```py
from typing import Callable

def my_lossy_decorator(fn: Callable[..., int]) -> Callable[..., int]:
    return fn

class MyClass:
    @my_lossy_decorator
    def method(self) -> int:
        return 42
```

we will currently infer the type of `MyClass.method` as a function-like
`Callable`, but we will infer the type of `MyClass().method` as a
`Callable` that is _not_ function-like. That's because a `CallableType`
currently "forgets" whether it was function-like or not during the
`bound_self` transformation:


a57e291311/crates/ty_python_semantic/src/types.rs (L10985-L10987)

This seems incorrect, and it's quite different to what we do when
binding the `self` parameter of `FunctionLiteral` types: `BoundMethod`
types are all seen as subtypes of function-like `Callable` supertypes --
here's `BoundMethodType::into_callable_type`:


a57e291311/crates/ty_python_semantic/src/types.rs (L10844-L10860)

The bug here is also causing lots of false positives in the ecosystem
report on https://github.com/astral-sh/ruff/pull/21611: a decorated
method on a subclass is currently not seen as validly overriding an
undecorated method with the same signature on a superclass, because the
undecorated superclass method is seen as function-like after binding
`self` whereas the decorated subclass method is not.

Fixing the bug required adding a new API in `protocol_class.rs`, because
it turns out that for our purposes in protocol subtyping/assignability,
we really do want a callable type to forget its function-like-ness when
binding `self`.

I initially tried out this change without changing anything in
`protocol_class.rs`. However, it resulted in many ecosystem false
positives and new false positives on the typing conformance test suite.
This is because it would mean that no protocol with a `__call__` method
would ever be seen as a subtype of a `Callable` type, since the
`__call__` method on the protocol would be seen as being function-like
whereas the `Callable` type would not be seen as function-like.

## Test Plan

Added an mdtest that fails on `main`
2025-11-24 21:14:03 +00:00
..
annotation.md ty_python_semantic: add union type context to function call type errors 2025-05-09 13:40:51 -04:00
builtins.md [ty] Improve diagnostics when `NotImplemented` is called (#21523) 2025-11-19 19:27:12 +00:00
callable_instance.md [ty] Rename "possibly unbound" diagnostics to "possibly missing" (#20492) 2025-09-23 14:26:55 +00:00
callables_as_descriptors.md [ty] Retain the function-like-ness of `Callable` types when binding `self` (#21614) 2025-11-24 21:14:03 +00:00
constructor.md [ty] Rename "possibly unbound" diagnostics to "possibly missing" (#20492) 2025-09-23 14:26:55 +00:00
dunder.md [ty] Use "cannot" consistently over "can not" (#21255) 2025-11-03 10:38:20 -05:00
dunder_import.md [ty] Make `__getattr__` available for `ModuleType` instances (#21450) 2025-11-14 13:59:14 +01:00
function.md [ty] Improve `invalid-argument-type` diagnostics where a union type was provided (#21044) 2025-10-23 13:16:21 +00:00
getattr_static.md [ty] Use "cannot" consistently over "can not" (#21255) 2025-11-03 10:38:20 -05:00
invalid_syntax.md ty_python_semantic: add union type context to function call type errors 2025-05-09 13:40:51 -04:00
methods.md [ty] support PEP 613 type aliases (#21394) 2025-11-20 17:59:35 -08:00
never.md Rename Red Knot (#17820) 2025-05-03 19:49:15 +02:00
open.md [ty] support PEP 613 type aliases (#21394) 2025-11-20 17:59:35 -08:00
overloads.md [ty] `dict` is not assignable to `TypedDict` (#21238) 2025-11-03 16:57:49 -05:00
replace.md [ty] Sync vendored typeshed stubs (#20658) 2025-10-01 10:11:48 +02:00
str_startswith.md [ty] `"foo".startswith` is not an instance of `types.MethodWrapperType` (#20317) 2025-09-10 11:14:26 +00:00
subclass_of.md [ty] Include synthesized arguments in displayed counts for `too-many-positional-arguments` (#18098) 2025-05-14 22:51:23 -04:00
union.md [ty] `dict` is not assignable to `TypedDict` (#21238) 2025-11-03 16:57:49 -05:00