Skip to content

Commit 5059ffd

Browse files
authored
Don’t leak unreachability from lambda body to surrounding scope (#17287)
Fixes #17254 Signed-off-by: Anders Kaseorg <[email protected]>
1 parent 9315d62 commit 5059ffd

File tree

2 files changed

+26
-4
lines changed

2 files changed

+26
-4
lines changed

mypy/checkexpr.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5223,15 +5223,16 @@ def visit_lambda_expr(self, e: LambdaExpr) -> Type:
52235223
self.chk.return_types.append(AnyType(TypeOfAny.special_form))
52245224
# Type check everything in the body except for the final return
52255225
# statement (it can contain tuple unpacking before return).
5226-
with self.chk.scope.push_function(e):
5226+
with self.chk.binder.frame_context(
5227+
can_skip=True, fall_through=0
5228+
), self.chk.scope.push_function(e):
52275229
# Lambdas can have more than one element in body,
52285230
# when we add "fictional" AssigmentStatement nodes, like in:
52295231
# `lambda (a, b): a`
52305232
for stmt in e.body.body[:-1]:
52315233
stmt.accept(self.chk)
52325234
# Only type check the return expression, not the return statement.
5233-
# This is important as otherwise the following statements would be
5234-
# considered unreachable. There's no useful type context.
5235+
# There's no useful type context.
52355236
ret_type = self.accept(e.expr(), allow_none_return=True)
52365237
fallback = self.named_type("builtins.function")
52375238
self.chk.return_types.pop()
@@ -5243,7 +5244,8 @@ def visit_lambda_expr(self, e: LambdaExpr) -> Type:
52435244
self.chk.check_func_item(e, type_override=type_override)
52445245
if not self.chk.has_type(e.expr()):
52455246
# TODO: return expression must be accepted before exiting function scope.
5246-
self.accept(e.expr(), allow_none_return=True)
5247+
with self.chk.binder.frame_context(can_skip=True, fall_through=0):
5248+
self.accept(e.expr(), allow_none_return=True)
52475249
ret_type = self.chk.lookup_type(e.expr())
52485250
self.chk.return_types.pop()
52495251
return replace_callable_return_type(inferred_type, ret_type)

test-data/unit/check-unreachable-code.test

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1494,3 +1494,23 @@ from typing import Generator
14941494
def f() -> Generator[None, None, None]:
14951495
return None
14961496
yield None
1497+
1498+
[case testLambdaNoReturn]
1499+
# flags: --warn-unreachable
1500+
from typing import Callable, NoReturn
1501+
1502+
def foo() -> NoReturn:
1503+
raise
1504+
1505+
f = lambda: foo()
1506+
x = 0 # not unreachable
1507+
1508+
[case testLambdaNoReturnAnnotated]
1509+
# flags: --warn-unreachable
1510+
from typing import Callable, NoReturn
1511+
1512+
def foo() -> NoReturn:
1513+
raise
1514+
1515+
f: Callable[[], NoReturn] = lambda: foo() # E: Return statement in function which does not return # (false positive: https://github.com/python/mypy/issues/17254)
1516+
x = 0 # not unreachable

0 commit comments

Comments
 (0)