Repository: python/mypy

Issue:
Class with function fields incorrectly thinks first argument is self
This is a bug report.

# Minimal repro

```python
from dataclasses import dataclass
from typing import Callable

@dataclass
class Repro:
    fn: Callable[[int], str]

r = Repro(fn=repr)
assert "1" == r.fn(1)
```

Works correctly when dataclass is not used, i.e. when `Repro` is

```python
class Repro:
    def __init__(self, fn: Callable[[int], str]) -> None:
        self.fn = fn
```

## Actual behavior

```python
repro.py:9: error: Invalid self argument "Repro" to attribute function "fn" with type "Callable[[int], str]"
repro.py:9: error: Too many arguments
```

When running with Python, works correctly. Doesn't throw any exceptions.

## Expected behavior

Successful type check

# Environment

## Versions

Tested with two versions, 0.620 and current version from GitHub.

* Python 3.7.0
* mypy 0.620
* mypy 0.630+dev-a2ffde90635baf05e264f8d899f5705c52b27c2a

## Mypy config

```ini
[mypy]
warn_redundant_casts = True
warn_unused_ignores = True
incremental = True

follow_imports = normal
ignore_missing_imports = True
disallow_untyped_calls = True
disallow_untyped_defs = True
check_untyped_defs = True
warn_return_any = True
warn_no_return = True
no_implicit_optional = True
strict_optional = True
```



Discussion:
This is not something specific to dataclasses. There is a simpler repro that gives same errors:
```python
class Repro:
    fn: Callable[[int], str]

r = Repro()
r.fn(1)
```
The problem is the same as in https://github.com/python/mypy/issues/708. I would however _not_ close this as a duplicate, because this issue highlights another aspect of the problem.
I just came across the same issue. I guess a lot of people are going to face this issue very soon, as it's very easy to reproduce it using dataclasses.
I guess this should be not hard to fix, mypy cares whether a callable was assigned in class (to match what Python does), so we should just tweak the `Var`s in plugin to pretend they were assigned on `self`. But we still need a volunteer who will fix this.
If someone needs a workaround, this works:

```python
from dataclasses import dataclass
from typing import *

T = TypeVar("T")


@dataclass
class Box(Generic[T]):
    inner: T

    @property
    def unboxed(self) -> T:
        return self.inner


@dataclass
class Repro:
    fn: Box[Callable[[int], str]]


Repro(fn=Box(repr)).fn.unboxed(1)
```
I've found that another workaround to this is to use a [callback protocol](https://mypy.readthedocs.io/en/latest/protocols.html#callback-protocols) instead of `Callable`
When using NamedTuple instead of dataclass this problem does not arise.
I volunteer to fix this and any guidances will be greatly helpful @ilevkivskyi 
@TH3CHARLie Fixing this in general may be tricky, but fixing just the dataclass case, should be easy. In any case, please go ahead!
> Fixing this in general may be tricky,

Taking into account the fact that parent issue is from 2015 I'd say that is an understatement of the year :P
Spent hours on this until someone linked me the issue.
I'm still running into this issue with dataclasses. @TH3CHARLie @ilevkivskyi  Any resolution on this? 😃 

### versions
mypy 0.782
Python 3.8.7
@jordan-bucholtz i don't know if it makes any difference but fyi there is 0.800 (and 0.790 in between).

https://pypi.org/project/mypy/0.800/
@altendky I'm using 0.800, and this is still broken for me.
Wondering if the solution is to somehow flag the Callable instance variable as a static method.  Because that's essentially the intended use.  Now, I'm not sure if the fix should be in mypy or in the typing library.

Resolving patch:
diff --git a/mypy/checkmember.py b/mypy/checkmember.py
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -531,6 +531,17 @@ def instance_alias_type(alias: TypeAlias,
     return expand_type_by_instance(tp, target)
 
 
+def is_instance_var(var: Var, info: TypeInfo) -> bool:
+    """Return if var is an instance variable according to PEP 526."""
+    return (
+        # check the type_info node is the var (not a decorated function, etc.)
+        var.name in info.names and info.names[var.name].node is var
+        and not var.is_classvar
+        # variables without annotations are treated as classvar
+        and not var.is_inferred
+    )
+
+
 def analyze_var(name: str,
                 var: Var,
                 itype: Instance,
@@ -559,7 +570,12 @@ def analyze_var(name: str,
         t = get_proper_type(expand_type_by_instance(typ, itype))
         result = t  # type: Type
         typ = get_proper_type(typ)
-        if var.is_initialized_in_class and isinstance(typ, FunctionLike) and not typ.is_type_obj():
+        if (
+            var.is_initialized_in_class
+            and not is_instance_var(var, info)
+            and isinstance(typ, FunctionLike)
+            and not typ.is_type_obj()
+        ):
             if mx.is_lvalue:
                 if var.is_property:
                     if not var.is_settable_property:


Test patch:
diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test
--- a/test-data/unit/check-dataclasses.test
+++ b/test-data/unit/check-dataclasses.test
@@ -1109,71 +1109,37 @@ reveal_type(B)  # N: Revealed type is "def (foo: builtins.int) -> __main__.B"
 
 [builtins fixtures/property.pyi]
 
-[case testDataclassCallableProperty]
+[case testDataclassCallableFieldAccess]
 # flags: --python-version 3.7
 from dataclasses import dataclass
 from typing import Callable
 
 @dataclass
 class A:
-    foo: Callable[[int], int]
+    x: Callable[[int], int]
+    y: Callable[[int], int] = lambda i: i
 
-def my_foo(x: int) -> int:
-    return x
+a = A(lambda i:i)
+x: int = a.x(0)
+y: str = a.y(0) # E: Incompatible types in assignment (expression has type "int", variable has type "str")
+reveal_type(a.x)  # N: Revealed type is "def (builtins.int) -> builtins.int"
+reveal_type(a.y)  # N: Revealed type is "def (builtins.int) -> builtins.int"
+reveal_type(A.y)  # N: Revealed type is "def (builtins.int) -> builtins.int"
 
-a = A(foo=my_foo)
-a.foo(1)
-reveal_type(a.foo)  # N: Revealed type is "def (builtins.int) -> builtins.int"
-reveal_type(A.foo)  # N: Revealed type is "def (builtins.int) -> builtins.int"
-[typing fixtures/typing-medium.pyi]
-[case testDataclassCallableAssignment]
-# flags: --python-version 3.7
-from dataclasses import dataclass
-from typing import Callable
-
-@dataclass
-class A:
-    foo: Callable[[int], int]
-
-def my_foo(x: int) -> int:
-    return x
-
-a = A(foo=my_foo)
-
-def another_foo(x: int) -> int:
-    return x + 1
-
-a.foo = another_foo
-[case testDataclassCallablePropertyWrongType]
+[case testDataclassCallableFieldAssignment]
 # flags: --python-version 3.7
 from dataclasses import dataclass
 from typing import Callable
 
 @dataclass
 class A:
-    foo: Callable[[int], int]
+    x: Callable[[int], int]
 
-def my_foo(x: int) -> str:
-    return "foo"
+def x(i: int) -> int:
+    return i
+def x2(s: str) -> str:
+    return s
 
-a = A(foo=my_foo)  # E: Argument "foo" to "A" has incompatible type "Callable[[int], str]"; expected "Callable[[int], int]"
-[typing fixtures/typing-medium.pyi]
-[case testDataclassCallablePropertyWrongTypeAssignment]
-# flags: --python-version 3.7
-from dataclasses import dataclass
-from typing import Callable
-
-@dataclass
-class A:
-    foo: Callable[[int], int]
-
-def my_foo(x: int) -> int:
-    return x
-
-a = A(foo=my_foo)
-
-def another_foo(x: int) -> str:
-    return "foo"
-
-a.foo = another_foo  # E: Incompatible types in assignment (expression has type "Callable[[int], str]", variable has type "Callable[[int], int]")
-[typing fixtures/typing-medium.pyi]
+a = A(lambda i:i)
+a.x = x
+a.x = x2 # E: Incompatible types in assignment (expression has type "Callable[[str], str]", variable has type "Callable[[int], int]")
diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test
--- a/test-data/unit/check-functions.test
+++ b/test-data/unit/check-functions.test
@@ -588,12 +588,12 @@ A().f('') # E: Argument 1 to "f" of "A" has incompatible type "str"; expected "i
 
 
 [case testMethodAsDataAttribute]
-from typing import Any, Callable
+from typing import Any, Callable, ClassVar
 class B: pass
 x = None # type: Any
 class A:
-    f = x # type: Callable[[A], None]
-    g = x # type: Callable[[A, B], None]
+    f = x # type: ClassVar[Callable[[A], None]]
+    g = x # type: ClassVar[Callable[[A, B], None]]
 a = None # type: A
 a.f()
 a.g(B())
@@ -601,26 +601,38 @@ a.f(a) # E: Too many arguments
 a.g()  # E: Too few arguments
 
 [case testMethodWithInvalidMethodAsDataAttribute]
-from typing import Any, Callable
+from typing import Any, Callable, ClassVar
 class B: pass
 x = None # type: Any
 class A:
-    f = x # type: Callable[[], None]
-    g = x # type: Callable[[B], None]
+    f = x # type: ClassVar[Callable[[], None]]
+    g = x # type: ClassVar[Callable[[B], None]]
 a = None # type: A
 a.f() # E: Attribute function "f" with type "Callable[[], None]" does not accept self argument
 a.g() # E: Invalid self argument "A" to attribute function "g" with type "Callable[[B], None]"
 
 [case testMethodWithDynamicallyTypedMethodAsDataAttribute]
-from typing import Any, Callable
+from typing import Any, Callable, ClassVar
 class B: pass
 x = None # type: Any
 class A:
-    f = x # type: Callable[[Any], Any]
+    f = x # type: ClassVar[Callable[[Any], Any]]
 a = None # type: A
 a.f()
 a.f(a) # E: Too many arguments
 
+[case testMethodWithInferredMethodAsDataAttribute]
+from typing import Any
+def m(self: "A") -> int: ...
+
+class A:
+    n = m
+
+a = A()
+reveal_type(a.n())  # N: Revealed type is "builtins.int"
+reveal_type(A.n(a))  # N: Revealed type is "builtins.int"
+A.n()  # E: Too few arguments
+
 [case testOverloadedMethodAsDataAttribute]
 from foo import *
 [file foo.pyi]
@@ -662,35 +674,35 @@ a.g(B())
 a.g(a)   # E: Argument 1 has incompatible type "A[B]"; expected "B"
 
 [case testInvalidMethodAsDataAttributeInGenericClass]
-from typing import Any, TypeVar, Generic, Callable
+from typing import Any, TypeVar, Generic, Callable, ClassVar
 t = TypeVar('t')
 class B: pass
 class C: pass
 x = None # type: Any
 class A(Generic[t]):
-    f = x # type: Callable[[A[B]], None]
+    f = x # type: ClassVar[Callable[[A[B]], None]]
 ab = None # type: A[B]
 ac = None # type: A[C]
 ab.f()
 ac.f()   # E: Invalid self argument "A[C]" to attribute function "f" with type "Callable[[A[B]], None]"
 
 [case testPartiallyTypedSelfInMethodDataAttribute]
-from typing import Any, TypeVar, Generic, Callable
+from typing import Any, TypeVar, Generic, Callable, ClassVar
 t = TypeVar('t')
 class B: pass
 class C: pass
 x = None # type: Any
 class A(Generic[t]):
-    f = x # type: Callable[[A], None]
+    f = x # type: ClassVar[Callable[[A], None]]
 ab = None # type: A[B]
 ac = None # type: A[C]
 ab.f()
 ac.f()
 
 [case testCallableDataAttribute]
-from typing import Callable
+from typing import Callable, ClassVar
 class A:
-    g = None # type: Callable[[A], None]
+    g = None # type: ClassVar[Callable[[A], None]]
     def __init__(self, f: Callable[[], None]) -> None:
         self.f = f
 a = A(None)
diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test
--- a/test-data/unit/check-functools.test
+++ b/test-data/unit/check-functools.test
@@ -25,12 +25,12 @@ Ord() >= 1  # E: Unsupported operand types for >= ("Ord" and "int")
 
 [case testTotalOrderingLambda]
 from functools import total_ordering
-from typing import Any, Callable
+from typing import Any, Callable, ClassVar
 
 @total_ordering
 class Ord:
-    __eq__: Callable[[Any, object], bool] = lambda self, other: False
-    __lt__: Callable[[Any, "Ord"], bool] = lambda self, other: False
+    __eq__: ClassVar[Callable[[Any, object], bool]] = lambda self, other: False
+    __lt__: ClassVar[Callable[[Any, "Ord"], bool]] = lambda self, other: False
 
 reveal_type(Ord() < Ord())  # N: Revealed type is "builtins.bool"
 reveal_type(Ord() <= Ord())  # N: Revealed type is "builtins.bool"
diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test
--- a/test-data/unit/check-selftype.test
+++ b/test-data/unit/check-selftype.test
@@ -366,7 +366,7 @@ reveal_type(x.f)  # N: Revealed type is "builtins.int"
 [builtins fixtures/property.pyi]
 
 [case testSelfTypeProperSupertypeAttribute]
-from typing import Callable, TypeVar
+from typing import Callable, TypeVar, ClassVar
 class K: pass
 T = TypeVar('T', bound=K)
 class A(K):
@@ -374,8 +374,8 @@ class A(K):
     def g(self: K) -> int: return 0
     @property
     def gt(self: T) -> T: return self
-    f: Callable[[object], int]
-    ft: Callable[[T], T]
+    f: ClassVar[Callable[[object], int]]
+    ft: ClassVar[Callable[[T], T]]
 
 class B(A):
     pass
@@ -392,15 +392,15 @@ reveal_type(B().ft())  # N: Revealed type is "__main__.B*"
 [builtins fixtures/property.pyi]
 
 [case testSelfTypeProperSupertypeAttributeTuple]
-from typing import Callable, TypeVar, Tuple
+from typing import Callable, TypeVar, Tuple, ClassVar
 T = TypeVar('T')
 class A(Tuple[int, int]):
     @property
     def g(self: object) -> int: return 0
     @property
     def gt(self: T) -> T: return self
-    f: Callable[[object], int]
-    ft: Callable[[T], T]
+    f: ClassVar[Callable[[object], int]]
+    ft: ClassVar[Callable[[T], T]]
 
 class B(A):
     pass
@@ -450,7 +450,7 @@ reveal_type(X1.ft())  # N: Revealed type is "Type[__main__.X]"
 [builtins fixtures/property.pyi]
 
 [case testSelfTypeProperSupertypeAttributeGeneric]
-from typing import Callable, TypeVar, Generic
+from typing import Callable, TypeVar, Generic, ClassVar
 Q = TypeVar('Q', covariant=True)
 class K(Generic[Q]):
     q: Q
@@ -460,8 +460,8 @@ class A(K[Q]):
     def g(self: K[object]) -> int: return 0
     @property
     def gt(self: K[T]) -> T: return self.q
-    f: Callable[[object], int]
-    ft: Callable[[T], T]
+    f: ClassVar[Callable[[object], int]]
+    ft: ClassVar[Callable[[T], T]]
 
 class B(A[Q]):
     pass