Skip to content

Commit bde4749

Browse files
committed
added check for edge case when return stmt inside loop can affect control flow in except* block
1 parent d65b1b8 commit bde4749

File tree

2 files changed

+29
-6
lines changed

2 files changed

+29
-6
lines changed

mypy/semanal.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,8 @@ def __init__(
487487
# Used to track whether currently inside an except* block. This helps
488488
# to invoke errors when continue/break/return is used inside except* block.
489489
self.inside_except_star_block: bool = False
490+
# Used to track edge case when return is still inside except* if it enters a loop
491+
self.return_stmt_inside_except_star_block: bool = False
490492

491493
# mypyc doesn't properly handle implementing an abstractproperty
492494
# with a regular attribute so we make them properties
@@ -516,13 +518,23 @@ def allow_unbound_tvars_set(self) -> Iterator[None]:
516518
self.allow_unbound_tvars = old
517519

518520
@contextmanager
519-
def inside_except_star_block_set(self, value: bool) -> Iterator[None]:
521+
def inside_except_star_block_set(
522+
self, value: bool, enteringLoop: bool = False
523+
) -> Iterator[None]:
520524
old = self.inside_except_star_block
521525
self.inside_except_star_block = value
526+
527+
# Return statement would still be in except* scope if entering loops
528+
if not enteringLoop:
529+
old_return_stmt_flag = self.return_stmt_inside_except_star_block
530+
self.return_stmt_inside_except_star_block = value
531+
522532
try:
523533
yield
524534
finally:
525535
self.inside_except_star_block = old
536+
if not enteringLoop:
537+
self.return_stmt_inside_except_star_block = old_return_stmt_flag
526538

527539
#
528540
# Preparing module (performed before semantic analysis)
@@ -890,7 +902,7 @@ def visit_func_def(self, defn: FuncDef) -> None:
890902
return
891903

892904
with self.scope.function_scope(defn):
893-
with self.inside_except_star_block_set(False):
905+
with self.inside_except_star_block_set(value=False):
894906
self.analyze_func_def(defn)
895907

896908
def function_fullname(self, fullname: str) -> str:
@@ -5277,7 +5289,7 @@ def visit_return_stmt(self, s: ReturnStmt) -> None:
52775289
self.statement = s
52785290
if not self.is_func_scope():
52795291
self.fail('"return" outside function', s)
5280-
if self.inside_except_star_block:
5292+
if self.return_stmt_inside_except_star_block:
52815293
self.fail('"return" not allowed in except* block', s, serious=True, blocker=True)
52825294
if s.expr:
52835295
s.expr.accept(self)
@@ -5312,7 +5324,7 @@ def visit_while_stmt(self, s: WhileStmt) -> None:
53125324
self.statement = s
53135325
s.expr.accept(self)
53145326
self.loop_depth[-1] += 1
5315-
with self.inside_except_star_block_set(False):
5327+
with self.inside_except_star_block_set(value=False, enteringLoop=True):
53165328
s.body.accept(self)
53175329
self.loop_depth[-1] -= 1
53185330
self.visit_block_maybe(s.else_body)
@@ -5337,7 +5349,7 @@ def visit_for_stmt(self, s: ForStmt) -> None:
53375349
s.index_type = analyzed
53385350

53395351
self.loop_depth[-1] += 1
5340-
with self.inside_except_star_block_set(False):
5352+
with self.inside_except_star_block_set(value=False, enteringLoop=True):
53415353
self.visit_block(s.body)
53425354
self.loop_depth[-1] -= 1
53435355
self.visit_block_maybe(s.else_body)

test-data/unit/check-python311.test

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ x4: Alias4[int] # E: Bad number of arguments for type alias, expected 0, given
174174
reveal_type(x4) # N: Revealed type is "def (*Any) -> builtins.int"
175175
[builtins fixtures/tuple.pyi]
176176

177-
[case testReturnInExceptStarBlock]
177+
[case testReturnInExceptStarBlock1]
178178
# flags: --python-version 3.11
179179
def foo() -> None:
180180
try:
@@ -185,6 +185,17 @@ def foo() -> None:
185185
return
186186
[builtins fixtures/exception.pyi]
187187

188+
[case testReturnInExceptStarBlock2]
189+
# flags: --python-version 3.11
190+
def foo():
191+
while True:
192+
try:
193+
pass
194+
except* Exception:
195+
while True:
196+
return # E: "return" not allowed in except* block
197+
[builtins fixtures/exception.pyi]
198+
188199
[case testBreakContinueReturnInExceptStarBlock1]
189200
# flags: --python-version 3.11
190201
class range: pass

0 commit comments

Comments
 (0)