Repository: python/mypy

Issue:
Mypy daemon dependency problem with __call__
This fine-grained incremental mode test case fails, probably because of missing dependencies to `__call__`:

```
[case testDunderCall]
from a import C

c = C()
c(1)

[file a.py]
class C:
    def __call__(self, x: int) -> None: ...

[file a.py.2]
class C:
    def __call__(self, x: str) -> None: ...

[out]
==                               
main:4: error: Argument 1 to "__call__" of "C" has incompatible type "int"; expected "str"
```

No error is currently reported when running the test case.


Discussion:

Resolving patch:
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -892,8 +892,13 @@ def check_call(self,
             # Apply method signature hook, if one exists
             call_function = self.transform_callee_type(
                 callable_name, call_function, args, arg_kinds, context, arg_names, callee)
-            return self.check_call(call_function, args, arg_kinds, context, arg_names,
-                                   callable_node, arg_messages, callable_name, callee)
+            result = self.check_call(call_function, args, arg_kinds, context, arg_names,
+                                     callable_node, arg_messages, callable_name, callee)
+            if callable_node:
+                # check_call() stored "call_function" as the type, which is incorrect.
+                # Override the type.
+                self.chk.store_type(callable_node, callee)
+            return result
         elif isinstance(callee, TypeVarType):
             return self.check_call(callee.upper_bound, args, arg_kinds, context, arg_names,
                                    callable_node, arg_messages)
diff --git a/mypy/server/deps.py b/mypy/server/deps.py
--- a/mypy/server/deps.py
+++ b/mypy/server/deps.py
@@ -661,6 +661,11 @@ def visit_call_expr(self, e: CallExpr) -> None:
             self.process_isinstance_call(e)
         else:
             super().visit_call_expr(e)
+            typ = self.type_map.get(e.callee)
+            if typ is not None:
+                typ = get_proper_type(typ)
+                if not isinstance(typ, FunctionLike):
+                    self.add_attribute_dependency(typ, '__call__')
 
     def process_isinstance_call(self, e: CallExpr) -> None:
         """Process "isinstance(...)" in a way to avoid some extra dependencies."""


Test patch:
diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test
--- a/test-data/unit/fine-grained.test
+++ b/test-data/unit/fine-grained.test
@@ -9594,3 +9594,56 @@ class N(p.util.Test):
 [builtins fixtures/list.pyi]
 [out]
 ==
+
+[case testDunderCall1]
+from a import C
+
+c = C()
+c(1)
+
+[file a.py]
+class C:
+    def __call__(self, x: int) -> None: ...
+
+[file a.py.2]
+class C:
+    def __call__(self, x: str) -> None: ...
+
+[out]
+==
+main:4: error: Argument 1 to "__call__" of "C" has incompatible type "int"; expected "str"
+
+[case testDunderCall2]
+from a import C
+
+C()(1)
+
+[file a.py]
+class C:
+    def __call__(self, x: int) -> None: ...
+
+[file a.py.2]
+class C:
+    def __call__(self, x: str) -> None: ...
+
+[out]
+==
+main:3: error: Argument 1 to "__call__" of "C" has incompatible type "int"; expected "str"
+
+[case testDunderCallAddition]
+from a import C
+
+c = C()
+x = c()  # type: ignore
+x + 42
+
+[file a.py]
+class C: ...
+
+[file a.py.2]
+class C:
+    def __call__(self) -> str: ...
+
+[out]
+==
+main:5: error: Unsupported left operand type for + ("str")