Skip to content

Commit 5bb6796

Browse files
authored
Analyze and check default arguments to lambdas (#7306)
This will allow us to report errors in them and also is needed to support them in mypyc.
1 parent 4ff341f commit 5bb6796

File tree

4 files changed

+33
-17
lines changed

4 files changed

+33
-17
lines changed

mypy/checker.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -926,19 +926,7 @@ def check_func_def(self, defn: FuncItem, typ: CallableType, name: Optional[str])
926926

927927
# Type check initialization expressions.
928928
body_is_trivial = self.is_trivial_body(defn.body)
929-
for arg in item.arguments:
930-
if arg.initializer is None:
931-
continue
932-
if body_is_trivial and isinstance(arg.initializer, EllipsisExpr):
933-
continue
934-
name = arg.variable.name()
935-
msg = 'Incompatible default for '
936-
if name.startswith('__tuple_arg_'):
937-
msg += "tuple argument {}".format(name[12:])
938-
else:
939-
msg += 'argument "{}"'.format(name)
940-
self.check_simple_assignment(arg.variable.type, arg.initializer,
941-
context=arg, msg=msg, lvalue_name='argument', rvalue_name='default')
929+
self.check_default_args(item, body_is_trivial)
942930

943931
# Type check body in a new scope.
944932
with self.binder.top_frame_context():
@@ -978,6 +966,21 @@ def check_func_def(self, defn: FuncItem, typ: CallableType, name: Optional[str])
978966

979967
self.binder = old_binder
980968

969+
def check_default_args(self, item: FuncItem, body_is_trivial: bool) -> None:
970+
for arg in item.arguments:
971+
if arg.initializer is None:
972+
continue
973+
if body_is_trivial and isinstance(arg.initializer, EllipsisExpr):
974+
continue
975+
name = arg.variable.name()
976+
msg = 'Incompatible default for '
977+
if name.startswith('__tuple_arg_'):
978+
msg += "tuple argument {}".format(name[12:])
979+
else:
980+
msg += 'argument "{}"'.format(name)
981+
self.check_simple_assignment(arg.variable.type, arg.initializer,
982+
context=arg, msg=msg, lvalue_name='argument', rvalue_name='default')
983+
981984
def is_forward_op_method(self, method_name: str) -> bool:
982985
if self.options.python_version[0] == 2 and method_name == '__div__':
983986
return True

mypy/checkexpr.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3065,6 +3065,7 @@ def find_typeddict_context(self, context: Optional[Type]) -> Optional[TypedDictT
30653065

30663066
def visit_lambda_expr(self, e: LambdaExpr) -> Type:
30673067
"""Type check lambda expression."""
3068+
self.chk.check_default_args(e, body_is_trivial=False)
30683069
inferred_type, type_override = self.infer_lambda_type_using_context(e)
30693070
if not inferred_type:
30703071
self.chk.return_types.append(AnyType(TypeOfAny.special_form))

mypy/semanal.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,7 @@ def analyze_func_def(self, defn: FuncDef) -> None:
557557
defn.type = defn.type.copy_modified(ret_type=NoneType())
558558
self.prepare_method_signature(defn, self.type)
559559

560-
# Analyze function signature and initializers first.
560+
# Analyze function signature
561561
with self.tvar_scope_frame(self.tvar_scope.method_frame()):
562562
if defn.type:
563563
self.check_classvar_in_signature(defn.type)
@@ -577,10 +577,8 @@ def analyze_func_def(self, defn: FuncDef) -> None:
577577
if isinstance(defn, FuncDef):
578578
assert isinstance(defn.type, CallableType)
579579
defn.type = set_callable_name(defn.type, defn)
580-
for arg in defn.arguments:
581-
if arg.initializer:
582-
arg.initializer.accept(self)
583580

581+
self.analyze_arg_initializers(defn)
584582
self.analyze_function_body(defn)
585583
if defn.is_coroutine and isinstance(defn.type, CallableType) and not self.deferred:
586584
if defn.is_async_generator:
@@ -868,6 +866,13 @@ def add_function_to_symbol_table(self, func: Union[FuncDef, OverloadedFuncDef])
868866
func._fullname = self.qualified_name(func.name())
869867
self.add_symbol(func.name(), func, func)
870868

869+
def analyze_arg_initializers(self, defn: FuncItem) -> None:
870+
with self.tvar_scope_frame(self.tvar_scope.method_frame()):
871+
# Analyze default arguments
872+
for arg in defn.arguments:
873+
if arg.initializer:
874+
arg.initializer.accept(self)
875+
871876
def analyze_function_body(self, defn: FuncItem) -> None:
872877
is_method = self.is_class_scope()
873878
with self.tvar_scope_frame(self.tvar_scope.method_frame()):
@@ -3722,6 +3727,7 @@ def analyze_comp_for_2(self, expr: Union[GeneratorExpr,
37223727
expr.sequences[0].accept(self)
37233728

37243729
def visit_lambda_expr(self, expr: LambdaExpr) -> None:
3730+
self.analyze_arg_initializers(expr)
37253731
self.analyze_function_body(expr)
37263732

37273733
def visit_conditional_expr(self, expr: ConditionalExpr) -> None:

test-data/unit/check-functions.test

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2549,3 +2549,9 @@ def f() -> int: ...
25492549
[file p/d.py]
25502550
import p
25512551
def f() -> int: ...
2552+
2553+
[case testLambdaDefaultTypeErrors]
2554+
lambda a=nonsense: a # E: Name 'nonsense' is not defined
2555+
lambda a=(1 + 'asdf'): a # E: Unsupported operand types for + ("int" and "str")
2556+
def f(x: int = i): # E: Name 'i' is not defined
2557+
i = 42

0 commit comments

Comments
 (0)