Repository: python/mypy

Issue:
Return type incompatible with supertype error should specify the types involved
I'm a relatively new user to mypy, and I have a quick bit of feedback / suggestion:

(Tangentially related to #2783 and/or #2782)

I see this error message:

```
error: Return type of "__iter__" incompatible with supertype "Iterable"
```

It would be really helpful if the message specified what the return type is, and more helpfully, what mypy thinks it should be.

This is particularly helpful in the context of generic/templated types like `Iterable`, where reading the definition of the type doesn't immediately give you the answer.  In other words: "Oh no!  I have to think!" 😃 


Discussion:
I would like to give this a go if that's ok.
OK, great!

Resolving patch:
diff --git a/mypy/checker.py b/mypy/checker.py
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -1556,7 +1556,7 @@ def erase_override(t: Type) -> Type:
                 if not is_subtype(erase_override(override.ret_type),
                                   original.ret_type):
                     self.msg.return_type_incompatible_with_supertype(
-                        name, name_in_super, supertype, node)
+                        name, name_in_super, supertype, original.ret_type, override.ret_type, node)
                     emitted_msg = True
             elif isinstance(override, Overloaded) and isinstance(original, Overloaded):
                 # Give a more detailed message in the case where the user is trying to
diff --git a/mypy/messages.py b/mypy/messages.py
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -826,10 +826,11 @@ def __eq__(self, other: object) -> bool:
 
     def return_type_incompatible_with_supertype(
             self, name: str, name_in_supertype: str, supertype: str,
+            original: Type, override: Type,
             context: Context) -> None:
         target = self.override_target(name, name_in_supertype, supertype)
-        self.fail('Return type of "{}" incompatible with {}'
-                  .format(name, target), context)
+        self.fail('Return type {} of "{}" incompatible with return type {} in {}'
+                  .format(self.format(override), name, self.format(original), target), context)
 
     def override_target(self, name: str, name_in_super: str,
                         supertype: str) -> str:


Test patch:
diff --git a/test-data/unit/check-abstract.test b/test-data/unit/check-abstract.test
--- a/test-data/unit/check-abstract.test
+++ b/test-data/unit/check-abstract.test
@@ -326,7 +326,7 @@ class A(I, J):
     def f(self, x: str) -> int: pass \
         # E: Argument 1 of "f" is incompatible with supertype "I"; supertype defines the argument type as "int"
     def g(self, x: str) -> int: pass \
-        # E: Return type of "g" incompatible with supertype "J"
+        # E: Return type "int" of "g" incompatible with return type "str" in supertype "J"
     def h(self) -> int: pass # Not related to any base class
 [out]
 
@@ -372,7 +372,7 @@ class A(I):
         pass
 [out]
 main:11: error: Argument 1 of "h" is incompatible with supertype "I"; supertype defines the argument type as "I"
-main:11: error: Return type of "h" incompatible with supertype "I"
+main:11: error: Return type "I" of "h" incompatible with return type "A" in supertype "I"
 
 
 -- Accessing abstract members
@@ -770,7 +770,7 @@ class A(metaclass=ABCMeta):
     def x(self) -> int: pass
 class B(A):
     @property
-    def x(self) -> str: pass # E: Return type of "x" incompatible with supertype "A"
+    def x(self) -> str: pass # E: Return type "str" of "x" incompatible with return type "int" in supertype "A"
 b = B()
 b.x() # E: "str" not callable
 [builtins fixtures/property.pyi]
diff --git a/test-data/unit/check-class-namedtuple.test b/test-data/unit/check-class-namedtuple.test
--- a/test-data/unit/check-class-namedtuple.test
+++ b/test-data/unit/check-class-namedtuple.test
@@ -572,7 +572,7 @@ class Child(Base):
         return self.x
     def good_override(self) -> int:
         return 0
-    def bad_override(self) -> str:  # E: Return type of "bad_override" incompatible with supertype "Base"
+    def bad_override(self) -> str:  # E: Return type "str" of "bad_override" incompatible with return type "int" in supertype "Base"
         return 'incompatible'
 
 def takes_base(base: Base) -> int:
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -281,7 +281,7 @@ class B(A):
   def h(self, x: A, y: 'B') -> object: pass  # Fail
 [out]
 main:7: error: Argument 1 of "f" is incompatible with supertype "A"; supertype defines the argument type as "A"
-main:9: error: Return type of "h" incompatible with supertype "A"
+main:9: error: Return type "object" of "h" incompatible with return type "A" in supertype "A"
 
 [case testEqMethodsOverridingWithNonObjects]
 class A:
@@ -328,7 +328,7 @@ class C(B): # with multiple implementations
     def f(self) -> B:  # Fail
         pass
 [out]
-main:7: error: Return type of "f" incompatible with supertype "B"
+main:7: error: Return type "B" of "f" incompatible with return type "C" in supertype "B"
 
 [case testMethodOverridingWithVoidReturnValue]
 import typing
@@ -339,7 +339,7 @@ class B(A):
     def f(self) -> A: pass  # Fail
     def g(self) -> None: pass
 [out]
-main:6: error: Return type of "f" incompatible with supertype "A"
+main:6: error: Return type "A" of "f" incompatible with return type "None" in supertype "A"
 
 [case testOverride__new__WithDifferentSignature]
 class A:
@@ -2451,7 +2451,7 @@ class C(A):
 class D(A):
     def __iadd__(self, x: 'A') -> 'B': pass
 [out]
-main:6: error: Return type of "__iadd__" incompatible with "__add__" of supertype "A"
+main:6: error: Return type "A" of "__iadd__" incompatible with return type "B" in "__add__" of supertype "A"
 main:8: error: Argument 1 of "__iadd__" is incompatible with "__add__" of supertype "A"; supertype defines the argument type as "A"
 main:8: error: Signatures of "__iadd__" and "__add__" are incompatible
 
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
@@ -185,7 +185,7 @@ class C(A):
         pass
 
 class D(A):
-   def copy(self: A) -> A:  # E: Return type of "copy" incompatible with supertype "A"
+   def copy(self: A) -> A:  # E: Return type "A" of "copy" incompatible with return type "D" in supertype "A"
        pass
 
 TB = TypeVar('TB', bound='B', covariant=True)
diff --git a/test-data/unit/check-typevar-values.test b/test-data/unit/check-typevar-values.test
--- a/test-data/unit/check-typevar-values.test
+++ b/test-data/unit/check-typevar-values.test
@@ -487,7 +487,7 @@ class B(A[str]):
     def f(self) -> 'B': pass
 class C(A[str]):
     @abstractmethod
-    def f(self) -> int: # E: Return type of "f" incompatible with supertype "A"
+    def f(self) -> int: # E: Return type "int" of "f" incompatible with return type "A[str]" in supertype "A"
         pass
 [out]
 
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
@@ -1063,9 +1063,9 @@ class A:
 [file n.py.3]
 [out]
 ==
-main:3: error: Return type of "f" incompatible with supertype "A"
+main:3: error: Return type "str" of "f" incompatible with return type "int" in supertype "A"
 ==
-main:3: error: Return type of "f" incompatible with supertype "A"
+main:3: error: Return type "str" of "f" incompatible with return type "int" in supertype "A"
 
 [case testModifyBaseClassMethodCausingInvalidOverride]
 import m
@@ -1079,7 +1079,7 @@ class A:
     def f(self) -> int: pass
 [out]
 ==
-main:3: error: Return type of "f" incompatible with supertype "A"
+main:3: error: Return type "str" of "f" incompatible with return type "int" in supertype "A"
 
 [case testAddBaseClassAttributeCausingErrorInSubclass]
 import m
@@ -1985,11 +1985,11 @@ class B:
 class B:
     def foo(self) -> int: return 12
 [out]
-a.py:9: error: Return type of "foo" incompatible with supertype "B"
+a.py:9: error: Return type "int" of "foo" incompatible with return type "str" in supertype "B"
 ==
-a.py:9: error: Return type of "foo" incompatible with supertype "B"
+a.py:9: error: Return type "int" of "foo" incompatible with return type "str" in supertype "B"
 ==
-a.py:9: error: Return type of "foo" incompatible with supertype "B"
+a.py:9: error: Return type "int" of "foo" incompatible with return type "str" in supertype "B"
 ==
 
 [case testPreviousErrorInMethodSemanal1]
@@ -7449,7 +7449,7 @@ def deco(f: F) -> F:
 [out]
 main:7: error: Unsupported operand types for + ("str" and "int")
 ==
-main:5: error: Return type of "m" incompatible with supertype "B"
+main:5: error: Return type "str" of "m" incompatible with return type "int" in supertype "B"
 
 [case testLiskovFineVariableClean-only_when_nocache]
 import b
@@ -7553,7 +7553,7 @@ def deco(f: F) -> F:
     pass
 [out]
 ==
-main:5: error: Return type of "m" incompatible with supertype "B"
+main:5: error: Return type "str" of "m" incompatible with return type "int" in supertype "B"
 
 [case testAddAbstractMethod]
 from b import D