Skip to content

Commit d9a613e

Browse files
committed
Added checks for invalid usage of continue/break/return in except* block
1 parent 3b00002 commit d9a613e

File tree

2 files changed

+65
-1
lines changed

2 files changed

+65
-1
lines changed

mypy/semanal.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,10 @@ def __init__(
484484
# Used to pass information about current overload index to visit_func_def().
485485
self.current_overload_item: int | None = None
486486

487+
# Used to track whether currently inside an except* block. This helps
488+
# to invoke errors when continue/break/return is used inside except * block.
489+
self.is_in_except_block: bool = False
490+
487491
# mypyc doesn't properly handle implementing an abstractproperty
488492
# with a regular attribute so we make them properties
489493
@property
@@ -854,6 +858,9 @@ def file_context(
854858
def visit_func_def(self, defn: FuncDef) -> None:
855859
self.statement = defn
856860

861+
was_in_except_block: bool = self.is_in_except_block
862+
self.is_in_except_block = False
863+
857864
# Visit default values because they may contain assignment expressions.
858865
for arg in defn.arguments:
859866
if arg.initializer:
@@ -874,11 +881,14 @@ def visit_func_def(self, defn: FuncDef) -> None:
874881
self.add_function_to_symbol_table(defn)
875882

876883
if not self.recurse_into_functions:
884+
self.is_in_except_block = was_in_except_block
877885
return
878886

879887
with self.scope.function_scope(defn):
880888
self.analyze_func_def(defn)
881889

890+
self.is_in_except_block = was_in_except_block
891+
882892
def function_fullname(self, fullname: str) -> str:
883893
if self.current_overload_item is None:
884894
return fullname
@@ -5263,6 +5273,8 @@ def visit_return_stmt(self, s: ReturnStmt) -> None:
52635273
self.statement = s
52645274
if not self.is_func_scope():
52655275
self.fail('"return" outside function', s)
5276+
if self.is_in_except_block:
5277+
self.fail('"return" not allowed in except* block', s, serious=True, blocker=True)
52665278
if s.expr:
52675279
s.expr.accept(self)
52685280

@@ -5295,9 +5307,12 @@ def visit_operator_assignment_stmt(self, s: OperatorAssignmentStmt) -> None:
52955307
def visit_while_stmt(self, s: WhileStmt) -> None:
52965308
self.statement = s
52975309
s.expr.accept(self)
5310+
was_in_except_block: bool = self.is_in_except_block
5311+
self.is_in_except_block = False
52985312
self.loop_depth[-1] += 1
52995313
s.body.accept(self)
53005314
self.loop_depth[-1] -= 1
5315+
self.is_in_except_block = was_in_except_block
53015316
self.visit_block_maybe(s.else_body)
53025317

53035318
def visit_for_stmt(self, s: ForStmt) -> None:
@@ -5319,21 +5334,28 @@ def visit_for_stmt(self, s: ForStmt) -> None:
53195334
self.store_declared_types(s.index, analyzed)
53205335
s.index_type = analyzed
53215336

5337+
was_in_except_block: bool = self.is_in_except_block
5338+
self.is_in_except_block = False
53225339
self.loop_depth[-1] += 1
53235340
self.visit_block(s.body)
53245341
self.loop_depth[-1] -= 1
5342+
self.is_in_except_block = was_in_except_block
53255343

53265344
self.visit_block_maybe(s.else_body)
53275345

53285346
def visit_break_stmt(self, s: BreakStmt) -> None:
53295347
self.statement = s
53305348
if self.loop_depth[-1] == 0:
53315349
self.fail('"break" outside loop', s, serious=True, blocker=True)
5350+
if self.is_in_except_block:
5351+
self.fail('"break" not allowed in except* block', s, serious=True, blocker=True)
53325352

53335353
def visit_continue_stmt(self, s: ContinueStmt) -> None:
53345354
self.statement = s
53355355
if self.loop_depth[-1] == 0:
53365356
self.fail('"continue" outside loop', s, serious=True, blocker=True)
5357+
if self.is_in_except_block:
5358+
self.fail('"continue" not allowed in except* block', s, serious=True, blocker=True)
53375359

53385360
def visit_if_stmt(self, s: IfStmt) -> None:
53395361
self.statement = s
@@ -5354,7 +5376,13 @@ def analyze_try_stmt(self, s: TryStmt, visitor: NodeVisitor[None]) -> None:
53545376
type.accept(visitor)
53555377
if var:
53565378
self.analyze_lvalue(var)
5357-
handler.accept(visitor)
5379+
if s.is_star:
5380+
was_in_except_block: bool = self.is_in_except_block
5381+
self.is_in_except_block = True
5382+
handler.accept(visitor)
5383+
self.is_in_except_block = was_in_except_block
5384+
else:
5385+
handler.accept(visitor)
53585386
if s.else_body:
53595387
s.else_body.accept(visitor)
53605388
if s.finally_body:
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
[case testExceptStarForbiddenBasic]
2+
# flags: --python-version 3.11
3+
[builtins fixtures/exception.pyi]
4+
def foo() -> None:
5+
for _ in range(5):
6+
try:
7+
...
8+
except* Exception:
9+
continue # E: "continue" not allowed in except* block
10+
except* ValueError:
11+
for _ in range(2):
12+
continue
13+
break # E: "break" not allowed in except* block
14+
except* TypeError:
15+
return # E: "return" not allowed in except* block
16+
17+
[case testExceptStarForbiddenEdgeCases]
18+
# flags: --python-version 3.11
19+
[builtins fixtures/exception.pyi]
20+
def foo2():
21+
while True:
22+
try:
23+
...
24+
except* ValueError:
25+
def inner():
26+
while True:
27+
if 1 < 1:
28+
continue
29+
else:
30+
break
31+
return
32+
if 1 < 2:
33+
break # E: "break" not allowed in except* block
34+
if 1 < 2:
35+
continue # E: "continue" not allowed in except* block
36+
return # E: "return" not allowed in except* block

0 commit comments

Comments
 (0)