Repository: python/mypy

Issue:
Mypy doesn't recognize accept a callable result type when type hint allows any result type 
**Bug Report**

When a decorator function hinted to return a callable with any result type effectively returns a callable with more specific return type there's a mypy error.

**To Reproduce**

1. Write this code:

```
_PRouteParams = ParamSpec("_PRouteParams")
def decorate_me(func: Callable[Concatenate[int, _PRouteParams], Any]) -> Callable[Concatenate[int, _PRouteParams], Any]:
        def inner(x: int, *args: _PRouteParams.args, **kwargs: _PRouteParams.kwargs) -> dict[str, Any]:
            return {"result": func(x, *args, **kwargs)}

        return inner
```

2. Run mypy on above code with "strict=True"

**Expected Behavior**

I expect mypy to not have errors for above code because a a callable that returns any type `T`, in above example `T=dict[str, Any]`, satisfies being a callable that returns `Any`.

More generally speaking, if a decorator should return a callable with return type T1 and T2 inherits from T1 then a callable returning T2 should be allowed to be returned by that decorator.

**Actual Behavior**

Mypy emits error:

error: Incompatible return value type (got "Callable[[int, **_PRouteParams], Dict[str, Any]]", expected "Callable[[int, **_PRouteParams], Any]")
src\zenity\infra\common\decorators\zenity_api.py:55: note: This may be because "inner" has arguments named: "x"

**Your Environment**

- Mypy version used: 0.971
- Mypy command-line flags: N/A
- Mypy configuration options from `mypy.ini` (and other config files):

follow_imports = "normal"
ignore_errors = false
implicit_reexport = false
warn_redundant_casts = true
warn_unused_ignores = true
disallow_any_generics = true
disallow_untyped_defs = true
check_untyped_defs = true
allow_redefinition = false
local_partial_types = true
strict_optional = true
strict_equality = true
warn_unused_configs = true
warn_unreachable = true
warn_no_return = true
no_implicit_optional = true
strict = true

- Python version used: 3.10.4
- Operating system and version: Windows 11

`Arg(T, 'name')` not equivalent to `T` in `Callable`
Given the code

```python
T = TypeVar("T", bound="Foo")
P = ParamSpec("P")

# this class serves as a decorator
class print_y_first:
    def __call__(
        self, method: Callable[Concatenate[T, P], str]
    ) -> Callable[Concatenate[T, P], str]:
        def wrapper(_self: T, *args: P.args, **kwargs: P.kwargs) -> str:
            print(_self.y)
            return method(*args, **kwargs)

        return wrapper


class Foo:

    y: int

    @print_y_first()
    def stringify(self, x: int) -> str:
        return str(x)
```

Mypy reports

```
error: Incompatible return value type (got "Callable[[Arg(T, '_self'), **P], str]", expected "Callable[[T, **P], str]")  [return-value]
note: This may be because "wrapper" has arguments named: "_self"
```

It does not recognize that 

```python
Callable[[Arg(T, '_self'), **P], str] == Callable[[T, **P], str]
```

Mypy version: `mypy 0.971 (compiled: yes)`
Mypy config options: `--strict=True`
Python version: 3.10.4




Discussion:
Mypy is correct (albeit a bit pedantic here):
```py
@decorate_me
def f(y: int, *, x: int) -> dict[str, Any]:
    return {}

f(42, x=42)  # ouch.
```

<details>
  <summary>Full code</summary>

```py
from __future__ import annotations

from typing import Callable, Any
from typing_extensions import ParamSpec, Concatenate

# ---- issue code 
_PRouteParams = ParamSpec("_PRouteParams")
def decorate_me(func: Callable[Concatenate[int, _PRouteParams], Any]) -> Callable[Concatenate[int, _PRouteParams], Any]:
        def inner(x: int, *args: _PRouteParams.args, **kwargs: _PRouteParams.kwargs) -> dict[str, Any]:
            return {"result": func(x, *args, **kwargs)}

        return inner
# ---- end issue code

# example usage:
@decorate_me
def f(y: int, *, x: int) -> dict[str, Any]:
    return {}

f(42, x=42)  # ouch.
```
</details>

Solutions:
 - `--no-strict-concatenate` (if I remember correctly)
 - `def inner(x: int, /, *args: _PRouteParams.args, **kwargs: _PRouteParams.kwargs) -> dict[str, Any]`
Heya! This was a bit confusing while I was implementing Concatenate which is why I added that note -- it doesn't seem to be helping so I may have to reconsider wording...

But AFAICT, this is because:
```
@print_y_first()
def blah(self: str, _self: int) -> str:
    return str(_self)
```

Now, `blah("m", _self=42)` would be accepted if `blah` were a `Callable[[str, _self: int], str]`... But you can probably see the unsafety here :P

The solution is either to stick a `/` after `_self` in the decorator, or `--no-strict-concatenate` (I think that was the flag?)

Resolving patch:
diff --git a/mypy/messages.py b/mypy/messages.py
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -791,8 +791,8 @@ def maybe_note_concatenate_pos_args(
             if names:
                 missing_arguments = '"' + '", "'.join(names) + '"'
                 self.note(
-                    f'This may be because "{original_caller_type.name}" has arguments '
-                    f"named: {missing_arguments}",
+                    f'This is likely because "{original_caller_type.name}" has named arguments: '
+                    f"{missing_arguments}. Consider marking them positional-only",
                     context,
                     code=code,
                 )


Test patch:
diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test
--- a/test-data/unit/check-parameter-specification.test
+++ b/test-data/unit/check-parameter-specification.test
@@ -530,7 +530,7 @@ reveal_type(transform(bar)) # N: Revealed type is "def (builtins.str, *args: bui
 def expects_int_first(x: Callable[Concatenate[int, P], int]) -> None: ...
 
 @expects_int_first  # E: Argument 1 to "expects_int_first" has incompatible type "Callable[[str], int]"; expected "Callable[[int], int]" \
-                    # N: This may be because "one" has arguments named: "x"
+                    # N: This is likely because "one" has named arguments: "x". Consider marking them positional-only
 def one(x: str) -> int: ...
 
 @expects_int_first  # E: Argument 1 to "expects_int_first" has incompatible type "Callable[[NamedArg(int, 'x')], int]"; expected "Callable[[int, NamedArg(int, 'x')], int]"
@@ -598,7 +598,7 @@ f2(lambda x: 42)(42, x=42)
 main:10: error: invalid syntax; you likely need to run mypy using Python 3.8 or newer
 [out version>=3.8]
 main:17: error: Incompatible return value type (got "Callable[[Arg(int, 'x'), **P], R]", expected "Callable[[int, **P], R]")
-main:17: note: This may be because "result" has arguments named: "x"
+main:17: note: This is likely because "result" has named arguments: "x". Consider marking them positional-only
 
 [case testNonStrictParamSpecConcatenateNamedArgs]
 # flags: --python-version 3.8