diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 4430d0773cfa..218568007b9e 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2185,6 +2185,10 @@ def get_arg_infer_passes( # run(test, 1, 2) # we will use `test` for inference, since it will allow to infer also # argument *names* for P <: [x: int, y: int]. + if isinstance(p_actual, Instance): + call_method = find_member("__call__", p_actual, p_actual, is_operator=True) + if call_method is not None: + p_actual = get_proper_type(call_method) if ( isinstance(p_actual, CallableType) and not p_actual.variables diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index ed1d59b376d2..a98c92ce14e7 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -1824,3 +1824,18 @@ class C(Generic[P]): ... c: C[int, [int, str], str] # E: Nested parameter specifications are not allowed reveal_type(c) # N: Revealed type is "__main__.C[Any]" [builtins fixtures/paramspec.pyi] + +[case testParamSpecInferenceWithCallbackProtocol] +from typing import Protocol, Callable, ParamSpec + +class CB(Protocol): + def __call__(self, x: str, y: int) -> None: ... + +P = ParamSpec('P') +def g(fn: Callable[P, None], *args: P.args, **kwargs: P.kwargs) -> None: ... + +cb: CB +g(cb, y=0, x='a') # OK +g(cb, y='a', x=0) # E: Argument "y" to "g" has incompatible type "str"; expected "int" \ + # E: Argument "x" to "g" has incompatible type "int"; expected "str" +[builtins fixtures/paramspec.pyi]