Skip to content

Commit c8bfb65

Browse files
miedzinskiilevkivskyi
authored andcommitted
Add expected type in incompatible arg errors (python#3515)
* Add expected type in incompatible arg errors * Update pythoneval tests * Use format instead of format_simple * Use is_same_type for dict incompatibility checks * Add tests for dict's invariance * Revert "Use is_same_type for dict incompatibility checks" This reverts commit 5a0903d. * Add test for dict's verbosity * Duplicate test for dict keys as well
1 parent b13699a commit c8bfb65

17 files changed

+113
-55
lines changed

mypy/messages.py

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -543,29 +543,59 @@ def incompatible_argument(self, n: int, m: int, callee: CallableType, arg_type:
543543
if callee.name == '<list>':
544544
name = callee.name[1:-1]
545545
n -= 1
546-
msg = '{} item {} has incompatible type {}'.format(
547-
name.title(), n, self.format(arg_type))
546+
actual_type_str, expected_type_str = self.format_distinctly(arg_type,
547+
callee.arg_types[0])
548+
msg = '{} item {} has incompatible type {}; expected {}'.format(
549+
name.title(), n, actual_type_str, expected_type_str)
548550
elif callee.name == '<dict>':
549551
name = callee.name[1:-1]
550552
n -= 1
551553
key_type, value_type = cast(TupleType, arg_type).items
552-
msg = '{} entry {} has incompatible type {}: {}'.format(
553-
name.title(), n, self.format(key_type), self.format(value_type))
554+
expected_key_type, expected_value_type = cast(TupleType, callee.arg_types[0]).items
555+
556+
# don't increase verbosity unless there is need to do so
557+
from mypy.subtypes import is_subtype
558+
if is_subtype(key_type, expected_key_type):
559+
key_type_str = self.format(key_type)
560+
expected_key_type_str = self.format(expected_key_type)
561+
else:
562+
key_type_str, expected_key_type_str = self.format_distinctly(
563+
key_type, expected_key_type)
564+
if is_subtype(value_type, expected_value_type):
565+
value_type_str = self.format(value_type)
566+
expected_value_type_str = self.format(expected_value_type)
567+
else:
568+
value_type_str, expected_value_type_str = self.format_distinctly(
569+
value_type, expected_value_type)
570+
571+
msg = '{} entry {} has incompatible type {}: {}; expected {}: {}'.format(
572+
name.title(), n, key_type_str, value_type_str,
573+
expected_key_type_str, expected_value_type_str)
554574
elif callee.name == '<list-comprehension>':
555-
msg = 'List comprehension has incompatible type List[{}]'.format(
556-
strip_quotes(self.format(arg_type)))
575+
actual_type_str, expected_type_str = map(strip_quotes,
576+
self.format_distinctly(arg_type,
577+
callee.arg_types[0]))
578+
msg = 'List comprehension has incompatible type List[{}]; expected List[{}]'.format(
579+
actual_type_str, expected_type_str)
557580
elif callee.name == '<set-comprehension>':
558-
msg = 'Set comprehension has incompatible type Set[{}]'.format(
559-
strip_quotes(self.format(arg_type)))
581+
actual_type_str, expected_type_str = map(strip_quotes,
582+
self.format_distinctly(arg_type,
583+
callee.arg_types[0]))
584+
msg = 'Set comprehension has incompatible type Set[{}]; expected Set[{}]'.format(
585+
actual_type_str, expected_type_str)
560586
elif callee.name == '<dictionary-comprehension>':
587+
actual_type_str, expected_type_str = self.format_distinctly(arg_type,
588+
callee.arg_types[n - 1])
561589
msg = ('{} expression in dictionary comprehension has incompatible type {}; '
562590
'expected type {}').format(
563591
'Key' if n == 1 else 'Value',
564-
self.format(arg_type),
565-
self.format(callee.arg_types[n - 1]))
592+
actual_type_str,
593+
expected_type_str)
566594
elif callee.name == '<generator>':
567-
msg = 'Generator has incompatible item type {}'.format(
568-
self.format(arg_type))
595+
actual_type_str, expected_type_str = self.format_distinctly(arg_type,
596+
callee.arg_types[0])
597+
msg = 'Generator has incompatible item type {}; expected {}'.format(
598+
actual_type_str, expected_type_str)
569599
else:
570600
try:
571601
expected_type = callee.arg_types[m - 1]

test-data/unit/check-class-namedtuple.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ class Parameterized(NamedTuple):
416416
z: List[int] = []
417417

418418
reveal_type(Parameterized(1)) # E: Revealed type is 'Tuple[builtins.int, builtins.list[builtins.int], builtins.list[builtins.int], fallback=__main__.Parameterized]'
419-
Parameterized(1, ['not an int']) # E: List item 0 has incompatible type "str"
419+
Parameterized(1, ['not an int']) # E: List item 0 has incompatible type "str"; expected "int"
420420

421421
class Default:
422422
pass

test-data/unit/check-classes.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2847,7 +2847,7 @@ class B(A[int]):
28472847
b = ['']
28482848
[builtins fixtures/list.pyi]
28492849
[out]
2850-
main:8: error: List item 0 has incompatible type "str"
2850+
main:8: error: List item 0 has incompatible type "str"; expected "int"
28512851

28522852
[case testVariableMethod]
28532853
class A:

test-data/unit/check-classvar.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ A().x.append(1)
151151
A().x.append('')
152152
[builtins fixtures/list.pyi]
153153
[out]
154-
main:4: error: List item 0 has incompatible type "str"
154+
main:4: error: List item 0 has incompatible type "str"; expected "int"
155155
main:6: error: Argument 1 to "append" of "list" has incompatible type "str"; expected "int"
156156

157157
[case testClassVarWithUnion]

test-data/unit/check-expressions.test

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,7 +1231,7 @@ fun(lambda: (yield from [1])) # E: Incompatible types in "yield from" (actual t
12311231
from typing import List
12321232
a = None # type: List[A]
12331233
a = [x for x in a]
1234-
b = [x for x in a] # type: List[B] # E: List comprehension has incompatible type List[A]
1234+
b = [x for x in a] # type: List[B] # E: List comprehension has incompatible type List[A]; expected List[B]
12351235
class A: pass
12361236
class B: pass
12371237
[builtins fixtures/for.pyi]
@@ -1240,7 +1240,7 @@ class B: pass
12401240
from typing import List, Tuple
12411241
l = None # type: List[Tuple[A, Tuple[A, B]]]
12421242
a = [a2 for a1, (a2, b1) in l] # type: List[A]
1243-
b = [a2 for a1, (a2, b1) in l] # type: List[B] # E: List comprehension has incompatible type List[A]
1243+
b = [a2 for a1, (a2, b1) in l] # type: List[B] # E: List comprehension has incompatible type List[A]; expected List[B]
12441244
class A: pass
12451245
class B: pass
12461246
[builtins fixtures/for.pyi]
@@ -1259,7 +1259,7 @@ from typing import List
12591259
a = None # type: List[A]
12601260
b = None # type: List[B]
12611261
b = [f(x) for x in a]
1262-
a = [f(x) for x in a] # E: List comprehension has incompatible type List[B]
1262+
a = [f(x) for x in a] # E: List comprehension has incompatible type List[B]; expected List[A]
12631263
([f(x) for x in b]) # E: Argument 1 to "f" has incompatible type "B"; expected "A"
12641264
class A: pass
12651265
class B: pass
@@ -1285,7 +1285,7 @@ from typing import List
12851285
class A:
12861286
a = None # type: List[A]
12871287
a = [x for x in a]
1288-
b = [x for x in a] # type: List[B] # E: List comprehension has incompatible type List[A]
1288+
b = [x for x in a] # type: List[B] # E: List comprehension has incompatible type List[A]; expected List[B]
12891289
class B: pass
12901290
[builtins fixtures/for.pyi]
12911291
[out]
@@ -1299,7 +1299,7 @@ class B: pass
12991299
from typing import Set
13001300
a = None # type: Set[A]
13011301
a = {x for x in a}
1302-
b = {x for x in a} # type: Set[B] # E: Set comprehension has incompatible type Set[A]
1302+
b = {x for x in a} # type: Set[B] # E: Set comprehension has incompatible type Set[A]; expected Set[B]
13031303
class A: pass
13041304
class B: pass
13051305
[builtins fixtures/set.pyi]
@@ -1351,15 +1351,15 @@ from typing import Iterator
13511351
a = None # type: Iterator[int]
13521352
a = (x for x in a)
13531353
b = None # type: Iterator[str]
1354-
b = (x for x in a) # E: Generator has incompatible item type "int"
1354+
b = (x for x in a) # E: Generator has incompatible item type "int"; expected "str"
13551355
[builtins fixtures/for.pyi]
13561356

13571357
[case testGeneratorIncompatibleErrorMessage]
13581358
from typing import Callable, Iterator, List
13591359

13601360
a = [] # type: List[Callable[[], str]]
13611361
b = None # type: Iterator[Callable[[], int]]
1362-
b = (x for x in a) # E: Generator has incompatible item type Callable[[], str]
1362+
b = (x for x in a) # E: Generator has incompatible item type Callable[[], str]; expected Callable[[], int]
13631363
[builtins fixtures/list.pyi]
13641364

13651365
-- Conditional expressions
@@ -1394,7 +1394,7 @@ y = '' # E: Incompatible types in assignment (expression has type "str", variabl
13941394
import typing
13951395
x = [1] if bool() else []
13961396
x = [1]
1397-
x = ['x'] # E: List item 0 has incompatible type "str"
1397+
x = ['x'] # E: List item 0 has incompatible type "str"; expected "int"
13981398
[builtins fixtures/list.pyi]
13991399

14001400

@@ -1550,8 +1550,8 @@ def g() -> Iterator[int]:
15501550
[case testDictWithKeywordArgsOnly]
15511551
from typing import Dict, Any
15521552
d1 = dict(a=1, b=2) # type: Dict[str, int]
1553-
d2 = dict(a=1, b='') # type: Dict[str, int] # E: Dict entry 1 has incompatible type "str": "str"
1554-
d3 = dict(a=1) # type: Dict[int, int] # E: Dict entry 0 has incompatible type "str": "int"
1553+
d2 = dict(a=1, b='') # type: Dict[str, int] # E: Dict entry 1 has incompatible type "str": "str"; expected "str": "int"
1554+
d3 = dict(a=1) # type: Dict[int, int] # E: Dict entry 0 has incompatible type "str": "int"; expected "int": "int"
15551555
d4 = dict(a=1, b=1)
15561556
d4.xyz # E: Dict[str, int] has no attribute "xyz"
15571557
d5 = dict(a=1, b='') # type: Dict[str, Any]
@@ -1568,7 +1568,7 @@ dict(undefined) # E: Name 'undefined' is not defined
15681568
from typing import Dict
15691569
d = dict([(1, 'x'), (2, 'y')])
15701570
d() # E: Dict[int, str] not callable
1571-
d2 = dict([(1, 'x')]) # type: Dict[str, str] # E: List item 0 has incompatible type "Tuple[int, str]"
1571+
d2 = dict([(1, 'x')]) # type: Dict[str, str] # E: List item 0 has incompatible type "Tuple[int, str]"; expected "Tuple[str, str]"
15721572
[builtins fixtures/dict.pyi]
15731573

15741574
[case testDictFromIterableAndKeywordArg]
@@ -1701,7 +1701,7 @@ b = {'z': 26, **a}
17011701
c = {**b}
17021702
d = {**a, **b, 'c': 3}
17031703
e = {1: 'a', **a} # E: Argument 1 to "update" of "dict" has incompatible type Dict[str, int]; expected Mapping[int, str]
1704-
f = {**b} # type: Dict[int, int] # E: List item 0 has incompatible type Dict[str, int]
1704+
f = {**b} # type: Dict[int, int] # E: List item 0 has incompatible type Dict[str, int]; expected Mapping[int, int]
17051705
[builtins fixtures/dict.pyi]
17061706

17071707
[case testDictIncompatibleTypeErrorMessage]
@@ -1710,11 +1710,39 @@ from typing import Dict, Callable
17101710
def things() -> int:
17111711
return 42
17121712

1713-
stuff: Dict[int, Callable[[], str]] = { # E: Dict entry 0 has incompatible type "int": Callable[[], int]
1713+
stuff: Dict[int, Callable[[], str]] = { # E: Dict entry 0 has incompatible type "int": Callable[[], int]; expected "int": Callable[[], str]
17141714
1: things
17151715
}
17161716
[builtins fixtures/dict.pyi]
17171717

1718+
[case testDictIncompatibleKeyVerbosity]
1719+
from typing import Dict
1720+
import mod
1721+
1722+
class A: ...
1723+
class B(A): ...
1724+
1725+
d: Dict[A, B] = {A(): mod.B()} # E: Dict entry 0 has incompatible type "A": "mod.B"; expected "A": "__main__.B"
1726+
1727+
[file mod.py]
1728+
class B: ...
1729+
1730+
[builtins fixtures/dict.pyi]
1731+
1732+
[case testDictIncompatibleValueVerbosity]
1733+
from typing import Dict
1734+
import mod
1735+
1736+
class A: ...
1737+
class B(A): ...
1738+
1739+
d: Dict[B, A] = {mod.B(): A()} # E: Dict entry 0 has incompatible type "mod.B": "A"; expected "__main__.B": "A"
1740+
1741+
[file mod.py]
1742+
class B: ...
1743+
1744+
[builtins fixtures/dict.pyi]
1745+
17181746
-- Type checker default plugin
17191747
-- ---------------------------
17201748

test-data/unit/check-functions.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -335,10 +335,10 @@ def f(x: C) -> C: pass
335335
from typing import Any, Callable, List
336336
def f(fields: List[Callable[[Any], Any]]): pass
337337
class C: pass
338-
f([C]) # E: List item 0 has incompatible type Type[C]
338+
f([C]) # E: List item 0 has incompatible type Type[C]; expected Callable[[Any], Any]
339339
class D:
340340
def __init__(self, a, b): pass
341-
f([D]) # E: List item 0 has incompatible type Type[D]
341+
f([D]) # E: List item 0 has incompatible type Type[D]; expected Callable[[Any], Any]
342342
[builtins fixtures/list.pyi]
343343

344344
[case testSubtypingTypeTypeAsCallable]

test-data/unit/check-generics.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,7 +1108,7 @@ from m import Alias
11081108

11091109
n = Alias[int]([1])
11101110
reveal_type(n) # E: Revealed type is 'm.Node[builtins.list*[builtins.int]]'
1111-
bad = Alias[str]([1]) # E: List item 0 has incompatible type "int"
1111+
bad = Alias[str]([1]) # E: List item 0 has incompatible type "int"; expected "str"
11121112

11131113
n2 = Alias([1]) # Same as Node[List[Any]]
11141114
reveal_type(n2) # E: Revealed type is 'm.Node[builtins.list*[Any]]'
@@ -1390,7 +1390,7 @@ def f(a: List[A]) -> A: pass
13901390
def f(a: B) -> B: pass
13911391

13921392
b = f([a]) # E: Incompatible types in assignment (expression has type "A", variable has type "B")
1393-
a = f([b]) # E: List item 0 has incompatible type "B"
1393+
a = f([b]) # E: List item 0 has incompatible type "B"; expected "A"
13941394
a = f(b) # E: Incompatible types in assignment (expression has type "B", variable has type "A")
13951395

13961396
a = f([a])

test-data/unit/check-inference-context.test

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -350,8 +350,8 @@ ao = None # type: List[object]
350350
a = None # type: A
351351
b = None # type: B
352352

353-
aa = [b] # E: List item 0 has incompatible type "B"
354-
ab = [a] # E: List item 0 has incompatible type "A"
353+
aa = [b] # E: List item 0 has incompatible type "B"; expected "A"
354+
ab = [a] # E: List item 0 has incompatible type "A"; expected "B"
355355

356356
aa = [a]
357357
ab = [b]
@@ -371,8 +371,8 @@ ao = None # type: List[object]
371371
a = None # type: A
372372
b = None # type: B
373373

374-
ab = [b, a] # E: List item 1 has incompatible type "A"
375-
ab = [a, b] # E: List item 0 has incompatible type "A"
374+
ab = [b, a] # E: List item 1 has incompatible type "A"; expected "B"
375+
ab = [a, b] # E: List item 0 has incompatible type "A"; expected "B"
376376

377377
aa = [a, b, a]
378378
ao = [a, b]
@@ -387,7 +387,7 @@ def f() -> None:
387387
a = [] # E: Need type annotation for variable
388388
b = [None]
389389
c = [B()]
390-
c = [object()] # E: List item 0 has incompatible type "object"
390+
c = [object()] # E: List item 0 has incompatible type "object"; expected "B"
391391
c = [B()]
392392
class B: pass
393393
[builtins fixtures/list.pyi]
@@ -401,8 +401,8 @@ ab = None # type: List[B]
401401
b = None # type: B
402402
o = None # type: object
403403

404-
aao = [[o], ab] # E: List item 1 has incompatible type List[B]
405-
aab = [[], [o]] # E: List item 0 has incompatible type "object"
404+
aao = [[o], ab] # E: List item 1 has incompatible type List[B]; expected List[object]
405+
aab = [[], [o]] # E: List item 0 has incompatible type "object"; expected "B"
406406

407407
aao = [[None], [b], [], [o]]
408408
aab = [[None], [b], []]
@@ -529,7 +529,7 @@ class set(Generic[t]):
529529
def __init__(self, iterable: Iterable[t]) -> None: pass
530530
b = bool()
531531
l = set([b])
532-
l = set([object()]) # E: List item 0 has incompatible type "object"
532+
l = set([object()]) # E: List item 0 has incompatible type "object"; expected "bool"
533533
[builtins fixtures/for.pyi]
534534

535535

@@ -564,7 +564,7 @@ class B:
564564
from typing import List, Callable
565565
f = None # type: Callable[[], List[A]]
566566
f = lambda: []
567-
f = lambda: [B()] # E: List item 0 has incompatible type "B"
567+
f = lambda: [B()] # E: List item 0 has incompatible type "B"; expected "A"
568568
class A: pass
569569
class B: pass
570570
[builtins fixtures/list.pyi]
@@ -743,14 +743,14 @@ class A:
743743
a = A()
744744
a.x = []
745745
a.x = [1]
746-
a.x = [''] # E: List item 0 has incompatible type "str"
746+
a.x = [''] # E: List item 0 has incompatible type "str"; expected "int"
747747
[builtins fixtures/list.pyi]
748748

749749
[case testListMultiplyInContext]
750750
from typing import List
751751
a = None # type: List[int]
752752
a = [None] * 3
753-
a = [''] * 3 # E: List item 0 has incompatible type "str"
753+
a = [''] * 3 # E: List item 0 has incompatible type "str"; expected "int"
754754
[builtins fixtures/list.pyi]
755755

756756
[case testUnionTypeContext]

test-data/unit/check-modules.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1360,7 +1360,7 @@ def do_something(dic: Row) -> None:
13601360
def do_another() -> Row:
13611361
return {}
13621362

1363-
do_something({'good': 'bad'}) # E: Dict entry 0 has incompatible type "str": "str"
1363+
do_something({'good': 'bad'}) # E: Dict entry 0 has incompatible type "str": "str"; expected "str": "int"
13641364
reveal_type(do_another()) # E: Revealed type is 'builtins.dict[builtins.str, builtins.int]'
13651365

13661366
[file ex2a.py]

test-data/unit/check-newtype.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ from typing import NewType, List
5555
UserId = NewType('UserId', int)
5656
IdList = NewType('IdList', List[UserId])
5757

58-
bad1 = IdList([1]) # E: List item 0 has incompatible type "int"
58+
bad1 = IdList([1]) # E: List item 0 has incompatible type "int"; expected "UserId"
5959

6060
foo = IdList([])
6161
foo.append(3) # E: Argument 1 to "append" of "list" has incompatible type "int"; expected "UserId"

0 commit comments

Comments
 (0)