Skip to content

Commit 4012dbf

Browse files
author
Guido van Rossum
committed
Change error messages about incompatible argument types to indicate whether * or ** was involved.
This fixes the issue reported in #1360 (comment) . Specifically, suppose you have these three calls (regardless of how f() is defined!) ``` f(a) f(*a) f(**a) ``` If the type checker determines that there's something wrong with the argument type it always gives the same error message. That error message is appropriate for the first example, `f(a)`, but for the second and third it should at least indicate that the mismatch is due to the star(s) in the call site. I am addressing this by adding one or two stars (as appropriate) in front of the actual type in the error message, so that e.g. instead of ``` Argument 1 to "f" has incompatible type Dict[str, int]; expected "str" ``` you would see ``` Argument 1 to "f" has incompatible type **Dict[str, int]; expected "str" ``` Hopefully this will inform the user that (1) the type of a in `f(**a)` is `Dict[str, int]` and (2) the actual type that doesn't match the expected type "str" is the value slot, in this case `int`. Similar for `*args`.
1 parent 7efb973 commit 4012dbf

File tree

4 files changed

+65
-38
lines changed

4 files changed

+65
-38
lines changed

mypy/checkexpr.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838

3939
# Type of callback user for checking individual function arguments. See
4040
# check_args() below for details.
41-
ArgChecker = Callable[[Type, Type, Type, int, int, CallableType, Context, MessageBuilder],
41+
ArgChecker = Callable[[Type, Type, int, Type, int, int, CallableType, Context, MessageBuilder],
4242
None]
4343

4444

@@ -604,7 +604,7 @@ def check_argument_types(self, arg_types: List[Type], arg_kinds: List[int],
604604
# and **args this is the item type, not the collection type).
605605
actual_type = get_actual_type(arg_type, arg_kinds[actual],
606606
tuple_counter)
607-
check_arg(actual_type, arg_type,
607+
check_arg(actual_type, arg_type, arg_kinds[actual],
608608
callee.arg_types[i],
609609
actual + 1, i + 1, callee, context, messages)
610610

@@ -618,11 +618,12 @@ def check_argument_types(self, arg_types: List[Type], arg_kinds: List[int],
618618
actual_type = get_actual_type(arg_type,
619619
arg_kinds[actual],
620620
tuple_counter)
621-
check_arg(actual_type, arg_type,
621+
check_arg(actual_type, arg_type, arg_kinds[actual],
622622
callee.arg_types[i],
623623
actual + 1, i + 1, callee, context, messages)
624624

625625
def check_arg(self, caller_type: Type, original_caller_type: Type,
626+
caller_kind: int,
626627
callee_type: Type, n: int, m: int, callee: CallableType,
627628
context: Context, messages: MessageBuilder) -> None:
628629
"""Check the type of a single argument in a call."""
@@ -632,7 +633,7 @@ def check_arg(self, caller_type: Type, original_caller_type: Type,
632633
messages.deleted_as_rvalue(caller_type, context)
633634
elif not is_subtype(caller_type, callee_type):
634635
messages.incompatible_argument(n, m, callee, original_caller_type,
635-
context)
636+
caller_kind, context)
636637

637638
def overload_call_target(self, arg_types: List[Type], arg_kinds: List[int],
638639
arg_names: List[str],
@@ -713,7 +714,7 @@ def erased_signature_similarity(self, arg_types: List[Type], arg_kinds: List[int
713714

714715
similarity = 2
715716

716-
def check_arg(caller_type: Type, original_caller_type: Type,
717+
def check_arg(caller_type: Type, original_caller_type: Type, caller_kind: int,
717718
callee_type: Type, n: int, m: int, callee: CallableType,
718719
context: Context, messages: MessageBuilder) -> None:
719720
nonlocal similarity
@@ -747,7 +748,7 @@ def match_signature_types(self, arg_types: List[Type], arg_kinds: List[int],
747748
lambda i: arg_types[i])
748749
ok = True
749750

750-
def check_arg(caller_type: Type, original_caller_type: Type,
751+
def check_arg(caller_type: Type, original_caller_type: Type, caller_kind: int,
751752
callee_type: Type, n: int, m: int, callee: CallableType,
752753
context: Context, messages: MessageBuilder) -> None:
753754
nonlocal ok

mypy/messages.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
Type, CallableType, Instance, TypeVarType, TupleType, UnionType, Void, NoneTyp, AnyType,
1414
Overloaded, FunctionLike, DeletedType
1515
)
16-
from mypy.nodes import TypeInfo, Context, MypyFile, op_methods, FuncDef, reverse_type_aliases
16+
from mypy.nodes import (
17+
TypeInfo, Context, MypyFile, op_methods, FuncDef, reverse_type_aliases,
18+
ARG_STAR, ARG_STAR2
19+
)
1720

1821

1922
# Constants that represent simple type checker error message, i.e. messages
@@ -400,7 +403,7 @@ def untyped_function_call(self, callee: CallableType, context: Context) -> Type:
400403
return AnyType()
401404

402405
def incompatible_argument(self, n: int, m: int, callee: CallableType, arg_type: Type,
403-
context: Context) -> None:
406+
arg_kind: int, context: Context) -> None:
404407
"""Report an error about an incompatible argument type.
405408
406409
The argument type is arg_type, argument number is n and the
@@ -465,6 +468,10 @@ def incompatible_argument(self, n: int, m: int, callee: CallableType, arg_type:
465468
except IndexError: # Varargs callees
466469
expected_type = callee.arg_types[-1]
467470
arg_type_str, expected_type_str = self.format_distinctly(arg_type, expected_type)
471+
if arg_kind == ARG_STAR:
472+
arg_type_str = '*' + arg_type_str
473+
elif arg_kind == ARG_STAR2:
474+
arg_type_str = '**' + arg_type_str
468475
msg = 'Argument {} {}has incompatible type {}; expected {}'.format(
469476
n, target, arg_type_str, expected_type_str)
470477
self.fail(msg, context)

mypy/test/data/check-kwargs.test

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,8 @@ d = None # type: Dict[str, A]
205205
f(**d)
206206
f(x=A(), **d)
207207
d2 = None # type: Dict[str, B]
208-
f(**d2) # E: Argument 1 to "f" has incompatible type Dict[str, B]; expected "A"
209-
f(x=A(), **d2) # E: Argument 2 to "f" has incompatible type Dict[str, B]; expected "A"
208+
f(**d2) # E: Argument 1 to "f" has incompatible type **Dict[str, B]; expected "A"
209+
f(x=A(), **d2) # E: Argument 2 to "f" has incompatible type **Dict[str, B]; expected "A"
210210
class A: pass
211211
class B: pass
212212
[builtins fixtures/dict.py]
@@ -226,7 +226,7 @@ def f(a: 'A', b: 'B') -> None: pass
226226
d = None # type: Dict[str, Any]
227227
f(**d)
228228
d2 = None # type: Dict[str, A]
229-
f(**d2) # E: Argument 1 to "f" has incompatible type Dict[str, A]; expected "B"
229+
f(**d2) # E: Argument 1 to "f" has incompatible type **Dict[str, A]; expected "B"
230230
class A: pass
231231
class B: pass
232232
[builtins fixtures/dict.py]
@@ -286,3 +286,22 @@ f(1, 2) # E: Argument 2 to "f" has incompatible type "int"; expected "str"
286286
f(1, y=1) # E: Argument 2 to "f" has incompatible type "int"; expected "A"
287287
f(1, z=1) # E: Argument 2 to "f" has incompatible type "int"; expected "B"
288288
[builtins fixtures/dict.py]
289+
290+
[case testCallsWithStars]
291+
def f(a: int) -> None:
292+
pass
293+
294+
s = ('',)
295+
f(*s) # E: Argument 1 to "f" has incompatible type *"Tuple[str]"; expected "int"
296+
297+
a = {'': 0}
298+
f(a) # E: Argument 1 to "f" has incompatible type Dict[str, int]; expected "int"
299+
f(**a) # okay
300+
301+
b = {'': ''}
302+
f(b) # E: Argument 1 to "f" has incompatible type Dict[str, str]; expected "int"
303+
f(**b) # E: Argument 1 to "f" has incompatible type **Dict[str, str]; expected "int"
304+
305+
c = {0: 0}
306+
f(**c) # E: Keywords must be strings
307+
[builtins fixtures/dict.py]

mypy/test/data/check-varargs.test

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ it1 = None # type: Iterable[int]
109109
it2 = None # type: Iterable[str]
110110
def f(*x: int) -> None: pass
111111
f(*it1)
112-
f(*it2) # E: Argument 1 to "f" has incompatible type Iterable[str]; expected "int"
112+
f(*it2) # E: Argument 1 to "f" has incompatible type *Iterable[str]; expected "int"
113113
[builtins fixtures/for.py]
114114

115115

@@ -190,7 +190,7 @@ class A: pass
190190
class B: pass
191191
[builtins fixtures/list.py]
192192
[out]
193-
main:7: error: Argument 1 to "f" has incompatible type List[A]; expected "B"
193+
main:7: error: Argument 1 to "f" has incompatible type *List[A]; expected "B"
194194

195195
[case testCallingWithTupleVarArgs]
196196

@@ -199,9 +199,9 @@ b = None # type: B
199199
c = None # type: C
200200
cc = None # type: CC
201201

202-
f(*(a, b, b)) # E: Argument 1 to "f" has incompatible type "Tuple[A, B, B]"; expected "C"
203-
f(*(b, b, c)) # E: Argument 1 to "f" has incompatible type "Tuple[B, B, C]"; expected "A"
204-
f(a, *(b, b)) # E: Argument 2 to "f" has incompatible type "Tuple[B, B]"; expected "C"
202+
f(*(a, b, b)) # E: Argument 1 to "f" has incompatible type *"Tuple[A, B, B]"; expected "C"
203+
f(*(b, b, c)) # E: Argument 1 to "f" has incompatible type *"Tuple[B, B, C]"; expected "A"
204+
f(a, *(b, b)) # E: Argument 2 to "f" has incompatible type *"Tuple[B, B]"; expected "C"
205205
f(b, *(b, c)) # E: Argument 1 to "f" has incompatible type "B"; expected "A"
206206
f(*(a, b)) # E: Too few arguments for "f"
207207
f(*(a, b, c, c)) # E: Too many arguments for "f"
@@ -259,26 +259,26 @@ class A: pass
259259
class B: pass
260260
[builtins fixtures/list.py]
261261
[out]
262-
main:3: error: Argument 1 to "f" has incompatible type List[A]; expected "B"
263-
main:4: error: Argument 2 to "f" has incompatible type List[A]; expected "B"
262+
main:3: error: Argument 1 to "f" has incompatible type *List[A]; expected "B"
263+
main:4: error: Argument 2 to "f" has incompatible type *List[A]; expected "B"
264264
main:5: error: Argument 1 to "f" has incompatible type "B"; expected "A"
265265
main:6: error: Argument 2 to "f" has incompatible type "A"; expected "B"
266-
main:7: error: Argument 3 to "f" has incompatible type List[A]; expected "B"
266+
main:7: error: Argument 3 to "f" has incompatible type *List[A]; expected "B"
267267
main:8: error: Argument 1 to "f" has incompatible type "B"; expected "A"
268-
main:9: error: Argument 1 to "g" has incompatible type List[B]; expected "A"
268+
main:9: error: Argument 1 to "g" has incompatible type *List[B]; expected "A"
269269

270270
[case testCallingVarArgsFunctionWithTupleVarArgs]
271271

272272
a, b, c, cc = None, None, None, None # type: (A, B, C, CC)
273273

274-
f(*(b, b, b)) # E: Argument 1 to "f" has incompatible type "Tuple[B, B, B]"; expected "A"
275-
f(*(a, a, b)) # E: Argument 1 to "f" has incompatible type "Tuple[A, A, B]"; expected "B"
276-
f(*(a, b, a)) # E: Argument 1 to "f" has incompatible type "Tuple[A, B, A]"; expected "B"
277-
f(a, *(a, b)) # E: Argument 2 to "f" has incompatible type "Tuple[A, B]"; expected "B"
274+
f(*(b, b, b)) # E: Argument 1 to "f" has incompatible type *"Tuple[B, B, B]"; expected "A"
275+
f(*(a, a, b)) # E: Argument 1 to "f" has incompatible type *"Tuple[A, A, B]"; expected "B"
276+
f(*(a, b, a)) # E: Argument 1 to "f" has incompatible type *"Tuple[A, B, A]"; expected "B"
277+
f(a, *(a, b)) # E: Argument 2 to "f" has incompatible type *"Tuple[A, B]"; expected "B"
278278
f(b, *(b, b)) # E: Argument 1 to "f" has incompatible type "B"; expected "A"
279279
f(b, b, *(b,)) # E: Argument 1 to "f" has incompatible type "B"; expected "A"
280280
f(a, a, *(b,)) # E: Argument 2 to "f" has incompatible type "A"; expected "B"
281-
f(a, b, *(a,)) # E: Argument 3 to "f" has incompatible type "Tuple[A]"; expected "B"
281+
f(a, b, *(a,)) # E: Argument 3 to "f" has incompatible type *"Tuple[A]"; expected "B"
282282
f(*()) # E: Too few arguments for "f"
283283
f(*(a, b, b))
284284
f(a, *(b, b))
@@ -322,7 +322,7 @@ from typing import List
322322
aa = None # type: List[A]
323323
ab = None # type: List[B]
324324

325-
g(*aa) # E: Argument 1 to "g" has incompatible type List[A]; expected "B"
325+
g(*aa) # E: Argument 1 to "g" has incompatible type *List[A]; expected "B"
326326
f(*aa)
327327
f(*ab)
328328
g(*ab)
@@ -359,25 +359,25 @@ class B: pass
359359
[builtins fixtures/list.py]
360360
[out]
361361
main:3: error: Too few arguments for "f"
362-
main:4: error: Argument 2 to "f" has incompatible type List[A]; expected "B"
363-
main:5: error: Argument 3 to "f" has incompatible type List[A]; expected "B"
364-
main:6: error: Argument 1 to "f" has incompatible type "Tuple[A, A, B]"; expected "B"
362+
main:4: error: Argument 2 to "f" has incompatible type *List[A]; expected "B"
363+
main:5: error: Argument 3 to "f" has incompatible type *List[A]; expected "B"
364+
main:6: error: Argument 1 to "f" has incompatible type *"Tuple[A, A, B]"; expected "B"
365365

366366
[case testVarArgsAfterKeywordArgInCall1]
367367
def f(x: int, y: str) -> None: pass
368368
f(x=1, *[2])
369369
[builtins fixtures/list.py]
370370
[out]
371371
main:2: error: "f" gets multiple values for keyword argument "x"
372-
main:2: error: Argument 2 to "f" has incompatible type List[int]; expected "str"
372+
main:2: error: Argument 2 to "f" has incompatible type *List[int]; expected "str"
373373

374374
[case testVarArgsAfterKeywordArgInCall2]
375375
def f(x: int, y: str) -> None: pass
376376
f(y='x', *[1])
377377
[builtins fixtures/list.py]
378378
[out]
379379
main:2: error: "f" gets multiple values for keyword argument "y"
380-
main:2: error: Argument 2 to "f" has incompatible type List[int]; expected "str"
380+
main:2: error: Argument 2 to "f" has incompatible type *List[int]; expected "str"
381381

382382
[case testVarArgsAfterKeywordArgInCall3]
383383
def f(x: int, y: str) -> None: pass
@@ -469,11 +469,11 @@ class A: pass
469469
class B: pass
470470
[builtins fixtures/list.py]
471471
[out]
472-
main:6: error: Argument 1 to "f" has incompatible type List[A]; expected "B"
473-
main:7: error: Argument 1 to "f" has incompatible type List[A]; expected "B"
472+
main:6: error: Argument 1 to "f" has incompatible type *List[A]; expected "B"
473+
main:7: error: Argument 1 to "f" has incompatible type *List[A]; expected "B"
474474
main:8: error: Argument 1 to "f" has incompatible type "B"; expected "A"
475-
main:9: error: Argument 2 to "f" has incompatible type List[A]; expected "B"
476-
main:10: error: Argument 3 to "f" has incompatible type List[A]; expected "B"
475+
main:9: error: Argument 2 to "f" has incompatible type *List[A]; expected "B"
476+
main:10: error: Argument 3 to "f" has incompatible type *List[A]; expected "B"
477477
main:11: error: List or tuple expected as variable arguments
478478
main:12: error: List or tuple expected as variable arguments
479479

@@ -483,9 +483,9 @@ S = TypeVar('S')
483483
T = TypeVar('T')
484484
a, b = None, None # type: (A, B)
485485

486-
a, a = f(*(a, b)) # E: Argument 1 to "f" has incompatible type "Tuple[A, B]"; expected "A"
486+
a, a = f(*(a, b)) # E: Argument 1 to "f" has incompatible type *"Tuple[A, B]"; expected "A"
487487
b, b = f(a, *(b,)) # E: Argument 1 to "f" has incompatible type "A"; expected "B"
488-
a, a = f(*(a, b)) # E: Argument 1 to "f" has incompatible type "Tuple[A, B]"; expected "A"
488+
a, a = f(*(a, b)) # E: Argument 1 to "f" has incompatible type *"Tuple[A, B]"; expected "A"
489489
b, b = f(a, *(b,)) # E: Argument 1 to "f" has incompatible type "A"; expected "B"
490490
a, b = f(*(a, b, b)) # E: Too many arguments for "f"
491491

@@ -525,7 +525,7 @@ class B: pass
525525
main:9: error: Incompatible types in assignment (expression has type List[A], variable has type "A")
526526
main:9: error: Incompatible types in assignment (expression has type List[None], variable has type List[A])
527527
main:10: error: Incompatible types in assignment (expression has type List[None], variable has type "A")
528-
main:11: error: Argument 1 to "f" of "G" has incompatible type List[A]; expected "B"
528+
main:11: error: Argument 1 to "f" of "G" has incompatible type *List[A]; expected "B"
529529
main:11: error: Incompatible types in assignment (expression has type List[None], variable has type List[A])
530530

531531

0 commit comments

Comments
 (0)