Skip to content

Commit

Permalink
Don’t leak unreachability from lambda body to surrounding scope (pyth…
Browse files Browse the repository at this point in the history
…on#17287)

Fixes python#17254

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
  • Loading branch information
andersk committed May 26, 2024
1 parent 9315d62 commit 5059ffd
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 4 deletions.
10 changes: 6 additions & 4 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -5223,15 +5223,16 @@ def visit_lambda_expr(self, e: LambdaExpr) -> Type:
self.chk.return_types.append(AnyType(TypeOfAny.special_form))
# Type check everything in the body except for the final return
# statement (it can contain tuple unpacking before return).
with self.chk.scope.push_function(e):
with self.chk.binder.frame_context(
can_skip=True, fall_through=0
), self.chk.scope.push_function(e):
# Lambdas can have more than one element in body,
# when we add "fictional" AssigmentStatement nodes, like in:
# `lambda (a, b): a`
for stmt in e.body.body[:-1]:
stmt.accept(self.chk)
# Only type check the return expression, not the return statement.
# This is important as otherwise the following statements would be
# considered unreachable. There's no useful type context.
# There's no useful type context.
ret_type = self.accept(e.expr(), allow_none_return=True)
fallback = self.named_type("builtins.function")
self.chk.return_types.pop()
Expand All @@ -5243,7 +5244,8 @@ def visit_lambda_expr(self, e: LambdaExpr) -> Type:
self.chk.check_func_item(e, type_override=type_override)
if not self.chk.has_type(e.expr()):
# TODO: return expression must be accepted before exiting function scope.
self.accept(e.expr(), allow_none_return=True)
with self.chk.binder.frame_context(can_skip=True, fall_through=0):
self.accept(e.expr(), allow_none_return=True)
ret_type = self.chk.lookup_type(e.expr())
self.chk.return_types.pop()
return replace_callable_return_type(inferred_type, ret_type)
Expand Down
20 changes: 20 additions & 0 deletions test-data/unit/check-unreachable-code.test
Original file line number Diff line number Diff line change
Expand Up @@ -1494,3 +1494,23 @@ from typing import Generator
def f() -> Generator[None, None, None]:
return None
yield None

[case testLambdaNoReturn]
# flags: --warn-unreachable
from typing import Callable, NoReturn

def foo() -> NoReturn:
raise

f = lambda: foo()
x = 0 # not unreachable

[case testLambdaNoReturnAnnotated]
# flags: --warn-unreachable
from typing import Callable, NoReturn

def foo() -> NoReturn:
raise

f: Callable[[], NoReturn] = lambda: foo() # E: Return statement in function which does not return # (false positive: https://github.com/python/mypy/issues/17254)
x = 0 # not unreachable

0 comments on commit 5059ffd

Please sign in to comment.