Repository: python/mypy

Issue:
Better message for incompatible type error
Consider the following snippet:
```from typing import *
Foo = NewType('Foo', int)
Bar = NewType('Bar', str)
Baz = NewType('Baz', str)


def things(thing_1: Bar, thing_2: Bar) -> Baz:
    return Baz(thing_1 + thing_2)


stuff: Dict[Foo, Callable[[Bar, Bar], Bar]] = {
    Foo(1): things
}

it = stuff.get(Foo(1))
result = things(Bar('test'), Bar('strings'))
```
The result of running mypy is this:
`foo.py:11: error: Dict entry 0 has incompatible type "Foo":`
This message is a bit misleading (possibly truncated?); the issue here is with the mismatch in return type between lines 7 and 11 (Bar vs. Baz), not with the type of the key in the dictionary.


Discussion:

Resolving patch:
diff --git a/mypy/messages.py b/mypy/messages.py
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -540,13 +540,13 @@ def incompatible_argument(self, n: int, m: int, callee: CallableType, arg_type:
             name = callee.name[1:-1]
             n -= 1
             msg = '{} item {} has incompatible type {}'.format(
-                name.title(), n, self.format_simple(arg_type))
+                name.title(), n, self.format(arg_type))
         elif callee.name == '<dict>':
             name = callee.name[1:-1]
             n -= 1
             key_type, value_type = cast(TupleType, arg_type).items
             msg = '{} entry {} has incompatible type {}: {}'.format(
-                name.title(), n, self.format_simple(key_type), self.format_simple(value_type))
+                name.title(), n, self.format(key_type), self.format(value_type))
         elif callee.name == '<list-comprehension>':
             msg = 'List comprehension has incompatible type List[{}]'.format(
                 strip_quotes(self.format(arg_type)))
@@ -561,7 +561,7 @@ def incompatible_argument(self, n: int, m: int, callee: CallableType, arg_type:
                 self.format(callee.arg_types[n - 1]))
         elif callee.name == '<generator>':
             msg = 'Generator has incompatible item type {}'.format(
-                self.format_simple(arg_type))
+                self.format(arg_type))
         else:
             try:
                 expected_type = callee.arg_types[m - 1]


Test patch:
diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test
--- a/test-data/unit/check-expressions.test
+++ b/test-data/unit/check-expressions.test
@@ -1346,14 +1346,21 @@ main:4: error: Argument 1 to "f" has incompatible type "B"; expected "A"
 
 [case testSimpleGeneratorExpression]
 from typing import Iterator
-# The implementation is mostly identical to list comprehensions, so a single
-# test case is ok.
+# The implementation is mostly identical to list comprehensions, so only a few
+# test cases is ok.
 a = None # type: Iterator[int]
 a = (x for x in a)
 b = None # type: Iterator[str]
 b = (x for x in a) # E: Generator has incompatible item type "int"
 [builtins fixtures/for.pyi]
 
+[case testGeneratorIncompatibleErrorMessage]
+from typing import Callable, Iterator, List
+
+a = []  # type: List[Callable[[], str]]
+b = None  # type: Iterator[Callable[[], int]]
+b = (x for x in a)  # E: Generator has incompatible item type Callable[[], str]
+[builtins fixtures/list.pyi]
 
 -- Conditional expressions
 -- -----------------------
@@ -1697,6 +1704,16 @@ e = {1: 'a', **a}  # E: Argument 1 to "update" of "dict" has incompatible type D
 f = {**b}  # type: Dict[int, int]  # E: List item 0 has incompatible type Dict[str, int]
 [builtins fixtures/dict.pyi]
 
+[case testDictIncompatibleTypeErrorMessage]
+from typing import Dict, Callable
+
+def things() -> int:
+    return 42
+
+stuff: Dict[int, Callable[[], str]] = {  # E: Dict entry 0 has incompatible type "int": Callable[[], int]
+    1: things
+}
+[builtins fixtures/dict.pyi]
 
 -- Type checker default plugin
 -- ---------------------------
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
@@ -335,10 +335,10 @@ def f(x: C) -> C: pass
 from typing import Any, Callable, List
 def f(fields: List[Callable[[Any], Any]]): pass
 class C: pass
-f([C])  # E: List item 0 has incompatible type
+f([C])  # E: List item 0 has incompatible type Type[C]
 class D:
     def __init__(self, a, b): pass
-f([D])  # E: List item 0 has incompatible type
+f([D])  # E: List item 0 has incompatible type Type[D]
 [builtins fixtures/list.pyi]
 
 [case testSubtypingTypeTypeAsCallable]
diff --git a/test-data/unit/check-optional.test b/test-data/unit/check-optional.test
--- a/test-data/unit/check-optional.test
+++ b/test-data/unit/check-optional.test
@@ -458,6 +458,16 @@ reveal_type(l)  # E: Revealed type is 'builtins.list[typing.Generator*[builtins.
 x = [None] if "" else [1]  # E: List item 0 has incompatible type "int"
 [builtins fixtures/list.pyi]
 
+[case testListIncompatibleErrorMessage]
+from typing import List, Callable
+
+def foo(l: List[Callable[[], str]]) -> None: pass
+def f() -> int:
+    return 42
+
+foo([f])  # E: List item 0 has incompatible type Callable[[], int]
+[builtins fixtures/list.pyi]
+
 [case testInferEqualsNotOptional]
 from typing import Optional
 x = ''  # type: Optional[str]