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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy