Skip to content

Commit

Permalink
Better error message for incompatible default argument (#3783)
Browse files Browse the repository at this point in the history
  • Loading branch information
elazarg authored and gvanrossum committed Jul 31, 2017
1 parent d7761e4 commit 6366a02
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 14 deletions.
12 changes: 9 additions & 3 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -740,9 +740,15 @@ def is_implicit_any(t: Type) -> bool:

# Type check initialization expressions.
for arg in item.arguments:
init = arg.initialization_statement
if init:
self.accept(init)
if arg.initializer is not None:
name = arg.variable.name()
msg = 'Incompatible default for '
if name.startswith('__tuple_arg_'):
msg += "tuple argument {}".format(name[12:])
else:
msg += 'argument "{}"'.format(name)
self.check_simple_assignment(arg.variable.type, arg.initializer,
context=arg, msg=msg, lvalue_name='argument', rvalue_name='default')

# Type check body in a new scope.
with self.binder.top_frame_context():
Expand Down
47 changes: 42 additions & 5 deletions test-data/unit/check-functions.test
Original file line number Diff line number Diff line change
Expand Up @@ -388,17 +388,54 @@ class A: pass

[case testDefaultArgumentExpressions2]
import typing
def f(x: 'A' = B()) -> None: # E: Incompatible types in assignment (expression has type "B", variable has type "A")
def f(x: 'A' = B()) -> None: # E: Incompatible default for argument "x" (default has type "B", argument has type "A")
b = x # type: B # E: Incompatible types in assignment (expression has type "A", variable has type "B")
a = x # type: A

class B: pass
class A: pass
[out]

[case testDefaultArgumentExpressionsGeneric]
from typing import TypeVar
T = TypeVar('T', bound='A')
def f(x: T = B()) -> None: # E: Incompatible default for argument "x" (default has type "B", argument has type "T")
b = x # type: B # E: Incompatible types in assignment (expression has type "T", variable has type "B")
a = x # type: A

class B: pass
class A: pass

[case testDefaultArgumentExpressionsPython2]
# flags: --python-version 2.7
from typing import Tuple
def f(x = B()): # E: Incompatible default for argument "x" (default has type "B", argument has type "A")
# type: (A) -> None
b = x # type: B # E: Incompatible types in assignment (expression has type "A", variable has type "B")
a = x # type: A

class B: pass
class A: pass

[case testDefaultTupleArgumentExpressionsPython2]
# flags: --python-version 2.7
from typing import Tuple
def f((x, y) = (A(), B())): # E: Incompatible default for tuple argument 1 (default has type "Tuple[A, B]", argument has type "Tuple[B, B]")
# type: (Tuple[B, B]) -> None
b = x # type: B
a = x # type: A # E: Incompatible types in assignment (expression has type "B", variable has type "A")
def g(a, (x, y) = (A(),)): # E: Incompatible default for tuple argument 2 (default has type "Tuple[A]", argument has type "Tuple[B, B]")
# type: (int, Tuple[B, B]) -> None
pass
def h((x, y) = (A(), B(), A())): # E: Incompatible default for tuple argument 1 (default has type "Tuple[A, B, A]", argument has type "Tuple[B, B]")
# type: (Tuple[B, B]) -> None
pass

class B: pass
class A: pass

[case testDefaultArgumentsWithSubtypes]
import typing
def f(x: 'B' = A()) -> None: # E: Incompatible types in assignment (expression has type "A", variable has type "B")
def f(x: 'B' = A()) -> None: # E: Incompatible default for argument "x" (default has type "A", argument has type "B")
pass
def g(x: 'A' = B()) -> None:
pass
Expand All @@ -409,7 +446,7 @@ class B(A): pass

[case testMultipleDefaultArgumentExpressions]
import typing
def f(x: 'A' = B(), y: 'B' = B()) -> None: # E: Incompatible types in assignment (expression has type "B", variable has type "A")
def f(x: 'A' = B(), y: 'B' = B()) -> None: # E: Incompatible default for argument "x" (default has type "B", argument has type "A")
pass
def h(x: 'A' = A(), y: 'B' = B()) -> None:
pass
Expand All @@ -420,7 +457,7 @@ class B: pass

[case testMultipleDefaultArgumentExpressions2]
import typing
def g(x: 'A' = A(), y: 'B' = A()) -> None: # E: Incompatible types in assignment (expression has type "A", variable has type "B")
def g(x: 'A' = A(), y: 'B' = A()) -> None: # E: Incompatible default for argument "y" (default has type "A", argument has type "B")
pass

class A: pass
Expand Down
2 changes: 1 addition & 1 deletion test-data/unit/check-inference.test
Original file line number Diff line number Diff line change
Expand Up @@ -1119,7 +1119,7 @@ from typing import Callable
def f(a: Callable[..., None] = lambda *a, **k: None):
pass

def g(a: Callable[..., None] = lambda *a, **k: 1): # E: Incompatible types in assignment (expression has type Callable[[VarArg(Any), KwArg(Any)], int], variable has type Callable[..., None])
def g(a: Callable[..., None] = lambda *a, **k: 1): # E: Incompatible default for argument "a" (default has type Callable[[VarArg(Any), KwArg(Any)], int], argument has type Callable[..., None])
pass
[builtins fixtures/dict.pyi]

Expand Down
6 changes: 3 additions & 3 deletions test-data/unit/check-modules.test
Original file line number Diff line number Diff line change
Expand Up @@ -475,11 +475,11 @@ def f(x: int = ...) -> None: pass
[file m.pyi]
def g(x: int = '') -> None: pass
[out]
tmp/m.pyi:1: error: Incompatible types in assignment (expression has type "str", variable has type "int")
main:2: error: Incompatible types in assignment (expression has type "ellipsis", variable has type "int")
tmp/m.pyi:1: error: Incompatible default for argument "x" (default has type "str", argument has type "int")
main:2: error: Incompatible default for argument "x" (default has type "ellipsis", argument has type "int")

[case testEllipsisDefaultArgValueInNonStub]
def f(x: int = ...) -> None: pass # E: Incompatible types in assignment (expression has type "ellipsis", variable has type "int")
def f(x: int = ...) -> None: pass # E: Incompatible default for argument "x" (default has type "ellipsis", argument has type "int")
[out]

[case testStarImportOverlapping]
Expand Down
4 changes: 2 additions & 2 deletions test-data/unit/check-optional.test
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ f(None)

[case testNoInferOptionalFromDefaultNone]
# flags: --no-implicit-optional
def f(x: int = None) -> None: # E: Incompatible types in assignment (expression has type None, variable has type "int")
def f(x: int = None) -> None: # E: Incompatible default for argument "x" (default has type None, argument has type "int")
pass
[out]

Expand All @@ -140,7 +140,7 @@ f(None)

[case testNoInferOptionalFromDefaultNoneComment]
# flags: --no-implicit-optional
def f(x=None): # E: Incompatible types in assignment (expression has type None, variable has type "int")
def f(x=None): # E: Incompatible default for argument "x" (default has type None, argument has type "int")
# type: (int) -> None
pass
[out]
Expand Down

0 comments on commit 6366a02

Please sign in to comment.