@@ -484,6 +484,12 @@ def __init__(
484
484
# Used to pass information about current overload index to visit_func_def().
485
485
self .current_overload_item : int | None = None
486
486
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 .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
492
+
487
493
# mypyc doesn't properly handle implementing an abstractproperty
488
494
# with a regular attribute so we make them properties
489
495
@property
@@ -511,6 +517,25 @@ def allow_unbound_tvars_set(self) -> Iterator[None]:
511
517
finally :
512
518
self .allow_unbound_tvars = old
513
519
520
+ @contextmanager
521
+ def inside_except_star_block_set (
522
+ self , value : bool , entering_loop : bool = False
523
+ ) -> Iterator [None ]:
524
+ old = self .inside_except_star_block
525
+ self .inside_except_star_block = value
526
+
527
+ # Return statement would still be in except* scope if entering loops
528
+ if not entering_loop :
529
+ old_return_stmt_flag = self .return_stmt_inside_except_star_block
530
+ self .return_stmt_inside_except_star_block = value
531
+
532
+ try :
533
+ yield
534
+ finally :
535
+ self .inside_except_star_block = old
536
+ if not entering_loop :
537
+ self .return_stmt_inside_except_star_block = old_return_stmt_flag
538
+
514
539
#
515
540
# Preparing module (performed before semantic analysis)
516
541
#
@@ -877,7 +902,8 @@ def visit_func_def(self, defn: FuncDef) -> None:
877
902
return
878
903
879
904
with self .scope .function_scope (defn ):
880
- self .analyze_func_def (defn )
905
+ with self .inside_except_star_block_set (value = False ):
906
+ self .analyze_func_def (defn )
881
907
882
908
def function_fullname (self , fullname : str ) -> str :
883
909
if self .current_overload_item is None :
@@ -5264,6 +5290,8 @@ def visit_return_stmt(self, s: ReturnStmt) -> None:
5264
5290
self .statement = s
5265
5291
if not self .is_func_scope ():
5266
5292
self .fail ('"return" outside function' , s )
5293
+ if self .return_stmt_inside_except_star_block :
5294
+ self .fail ('"return" not allowed in except* block' , s , serious = True )
5267
5295
if s .expr :
5268
5296
s .expr .accept (self )
5269
5297
@@ -5297,7 +5325,8 @@ def visit_while_stmt(self, s: WhileStmt) -> None:
5297
5325
self .statement = s
5298
5326
s .expr .accept (self )
5299
5327
self .loop_depth [- 1 ] += 1
5300
- s .body .accept (self )
5328
+ with self .inside_except_star_block_set (value = False , entering_loop = True ):
5329
+ s .body .accept (self )
5301
5330
self .loop_depth [- 1 ] -= 1
5302
5331
self .visit_block_maybe (s .else_body )
5303
5332
@@ -5321,20 +5350,24 @@ def visit_for_stmt(self, s: ForStmt) -> None:
5321
5350
s .index_type = analyzed
5322
5351
5323
5352
self .loop_depth [- 1 ] += 1
5324
- self .visit_block (s .body )
5353
+ with self .inside_except_star_block_set (value = False , entering_loop = True ):
5354
+ self .visit_block (s .body )
5325
5355
self .loop_depth [- 1 ] -= 1
5326
-
5327
5356
self .visit_block_maybe (s .else_body )
5328
5357
5329
5358
def visit_break_stmt (self , s : BreakStmt ) -> None :
5330
5359
self .statement = s
5331
5360
if self .loop_depth [- 1 ] == 0 :
5332
5361
self .fail ('"break" outside loop' , s , serious = True , blocker = True )
5362
+ if self .inside_except_star_block :
5363
+ self .fail ('"break" not allowed in except* block' , s , serious = True )
5333
5364
5334
5365
def visit_continue_stmt (self , s : ContinueStmt ) -> None :
5335
5366
self .statement = s
5336
5367
if self .loop_depth [- 1 ] == 0 :
5337
5368
self .fail ('"continue" outside loop' , s , serious = True , blocker = True )
5369
+ if self .inside_except_star_block :
5370
+ self .fail ('"continue" not allowed in except* block' , s , serious = True )
5338
5371
5339
5372
def visit_if_stmt (self , s : IfStmt ) -> None :
5340
5373
self .statement = s
@@ -5355,7 +5388,8 @@ def analyze_try_stmt(self, s: TryStmt, visitor: NodeVisitor[None]) -> None:
5355
5388
type .accept (visitor )
5356
5389
if var :
5357
5390
self .analyze_lvalue (var )
5358
- handler .accept (visitor )
5391
+ with self .inside_except_star_block_set (self .inside_except_star_block or s .is_star ):
5392
+ handler .accept (visitor )
5359
5393
if s .else_body :
5360
5394
s .else_body .accept (visitor )
5361
5395
if s .finally_body :
0 commit comments