diff --git a/mypy/checker.py b/mypy/checker.py index 760a137500f9..00d419da42ce 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -740,9 +740,7 @@ 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) + self.expr_checker.check_default_arg(arg) # Type check body in a new scope. with self.binder.top_frame_context(): diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index ebe1da07e942..b2750af78e59 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3,7 +3,7 @@ from collections import OrderedDict from typing import cast, Dict, Set, List, Tuple, Callable, Union, Optional -from mypy.errors import report_internal_error +from mypy.errors import Errors, report_internal_error from mypy.typeanal import has_any_from_unimported_type, check_for_explicit_any, set_any_tvars from mypy.types import ( Type, AnyType, CallableType, Overloaded, NoneTyp, TypeVarDef, @@ -1011,6 +1011,20 @@ def check_arg(self, caller_type: Type, original_caller_type: Type, messages.incompatible_argument(n, m, callee, original_caller_type, caller_kind, context) + def check_default_arg(self, arg: mypy.nodes.Argument) -> None: + rvalue = arg.initializer + lval_type = arg.variable.type + if rvalue and lval_type and not (self.chk.is_stub and isinstance(rvalue, EllipsisExpr)): + single = CallableType([lval_type], [ARG_POS], [None], + AnyType(), self.named_type('builtins.function')) + msg = MessageBuilder(Errors(), self.chk.modules) + self.check_call(single, [rvalue], [ARG_POS], arg, arg_messages=msg) + if msg.is_errors(): + rval_type = self.accept(rvalue, lval_type) + varname = arg.variable.name() + self.msg.incompatible_default_argument( + varname, lval_type, rval_type, arg) + def overload_call_target(self, arg_types: List[Type], arg_kinds: List[int], arg_names: List[str], overload: Overloaded, context: Context, diff --git a/mypy/messages.py b/mypy/messages.py index 44bc57548ada..6e171ff55929 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -492,6 +492,16 @@ def untyped_function_call(self, callee: CallableType, context: Context) -> Type: self.fail('Call to untyped function {} in typed context'.format(name), context) return AnyType() + def incompatible_default_argument(self, name: str, lvalue: Type, rvalue: Type, + context: Context) -> None: + if name.startswith('__tuple_arg_'): + fmt = "Incompatible default for tuple argument no' {}".format(name[12:]) + else: + fmt = 'Incompatible default for argument "{}"'.format(name) + fmt += ' (argument has type {}, default has type {})' + lvalt, rvalt = self.format_distinctly(lvalue, rvalue) + self.fail(fmt.format(lvalt, rvalt), context) + def incompatible_argument(self, n: int, m: int, callee: CallableType, arg_type: Type, arg_kind: int, context: Context) -> None: """Report an error about an incompatible argument type. diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index bc5a56b56014..5f22e27d3e3c 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -388,17 +388,42 @@ 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" (argument has type "A", default has type "B") 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" (argument has type "T", default has type "B") + 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" (argument has type "A", default has type "B") + # type: (A) -> None + b = x # type: B # E: Incompatible types in assignment (expression has type "A", variable has type "B") + a = x # type: A + +def g((x, y) = (A(), B())): # E: Incompatible default for tuple argument no' 1 (argument has type "Tuple[B, B]", default has type "Tuple[A, 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") + +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" (argument has type "B", default has type "A") pass def g(x: 'A' = B()) -> None: pass @@ -409,7 +434,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" (argument has type "A", default has type "B") pass def h(x: 'A' = A(), y: 'B' = B()) -> None: pass @@ -420,7 +445,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" (argument has type "B", default has type "A") pass class A: pass diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 42cd312c0531..ffe856496f01 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -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" (argument has type Callable[..., None], default has type Callable[[VarArg(Any), KwArg(Any)], int]) pass [builtins fixtures/dict.pyi] diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index 6e363bdb24ea..46d32dfdbf28 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -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" (argument has type "int", default has type "str") +main:2: error: Incompatible default for argument "x" (argument has type "int", default has type "ellipsis") [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" (argument has type "int", default has type "ellipsis") [out] [case testStarImportOverlapping] diff --git a/test-data/unit/check-optional.test b/test-data/unit/check-optional.test index 0103386fca4e..f9c971011b4a 100644 --- a/test-data/unit/check-optional.test +++ b/test-data/unit/check-optional.test @@ -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" (argument has type "int", default has type None) pass [out] @@ -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" (argument has type "int", default has type None) # type: (int) -> None pass [out]