@@ -1543,44 +1543,51 @@ def visit_try_stmt(self, s: TryStmt) -> Type:
1543
1543
def visit_try_without_finally (self , s : TryStmt ) -> None :
1544
1544
"""Type check a try statement, ignoring the finally block.
1545
1545
1546
- Otherwise, it will place the results possible frames of
1547
- that don't break out into self.binder.frames[-2].
1546
+ On entry, the top frame should receive all flow that exits the
1547
+ try block abnormally (i.e., such that the else block does not
1548
+ execute), and its parent should receive all flow that exits
1549
+ the try block normally.
1548
1550
"""
1549
- # This frame records the possible states that exceptions can leave variables in
1550
- # during the try: block
1551
- with self .binder .frame_context (can_skip = False , fall_through = 0 ):
1552
- with self .binder .frame_context (can_skip = False , fall_through = 3 ):
1553
- self .binder .try_frames .add (len (self .binder .frames ) - 2 )
1554
- self .binder .allow_jump (- 1 )
1555
- self .accept (s .body )
1556
- self .binder .try_frames .remove (len (self .binder .frames ) - 2 )
1557
- if s .else_body :
1558
- self .accept (s .else_body )
1559
- for i in range (len (s .handlers )):
1560
- with self .binder .frame_context (can_skip = True , fall_through = 3 ):
1561
- if s .types [i ]:
1562
- t = self .visit_except_handler_test (s .types [i ])
1551
+ # This frame will run the else block if the try fell through.
1552
+ # In that case, control flow continues to the parent of what
1553
+ # was the top frame on entry.
1554
+ with self .binder .frame_context (can_skip = False , fall_through = 2 ):
1555
+ # This frame receives exit via exception, and runs exception handlers
1556
+ with self .binder .frame_context (can_skip = False , fall_through = 2 ):
1557
+ # Finally, the body of the try statement
1558
+ with self .binder .frame_context (can_skip = False , fall_through = 2 ):
1559
+ self .binder .try_frames .add (len (self .binder .frames ) - 2 )
1560
+ self .binder .allow_jump (- 1 )
1561
+ self .accept (s .body )
1562
+ self .binder .try_frames .remove (len (self .binder .frames ) - 2 )
1563
+ for i in range (len (s .handlers )):
1564
+ with self .binder .frame_context (can_skip = True , fall_through = 4 ):
1565
+ if s .types [i ]:
1566
+ t = self .visit_except_handler_test (s .types [i ])
1567
+ if s .vars [i ]:
1568
+ # To support local variables, we make this a definition line,
1569
+ # causing assignment to set the variable's type.
1570
+ s .vars [i ].is_def = True
1571
+ self .check_assignment (s .vars [i ], self .temp_node (t , s .vars [i ]))
1572
+ self .accept (s .handlers [i ])
1563
1573
if s .vars [i ]:
1564
- # To support local variables, we make this a definition line,
1565
- # causing assignment to set the variable's type.
1566
- s .vars [i ].is_def = True
1567
- self .check_assignment (s .vars [i ], self .temp_node (t , s .vars [i ]))
1568
- self .accept (s .handlers [i ])
1569
- if s .vars [i ]:
1570
- # Exception variables are deleted in python 3 but not python 2.
1571
- # But, since it's bad form in python 2 and the type checking
1572
- # wouldn't work very well, we delete it anyway.
1573
-
1574
- # Unfortunately, this doesn't let us detect usage before the
1575
- # try/except block.
1576
- if self .pyversion [0 ] >= 3 :
1577
- source = s .vars [i ].name
1578
- else :
1579
- source = ('(exception variable "{}", which we do not accept outside'
1580
- 'except: blocks even in python 2)' .format (s .vars [i ].name ))
1581
- var = cast (Var , s .vars [i ].node )
1582
- var .type = DeletedType (source = source )
1583
- self .binder .cleanse (s .vars [i ])
1574
+ # Exception variables are deleted in python 3 but not python 2.
1575
+ # But, since it's bad form in python 2 and the type checking
1576
+ # wouldn't work very well, we delete it anyway.
1577
+
1578
+ # Unfortunately, this doesn't let us detect usage before the
1579
+ # try/except block.
1580
+ if self .pyversion [0 ] >= 3 :
1581
+ source = s .vars [i ].name
1582
+ else :
1583
+ source = ('(exception variable "{}", which we do not '
1584
+ 'accept outside except: blocks even in '
1585
+ 'python 2)' .format (s .vars [i ].name ))
1586
+ var = cast (Var , s .vars [i ].node )
1587
+ var .type = DeletedType (source = source )
1588
+ self .binder .cleanse (s .vars [i ])
1589
+ if s .else_body :
1590
+ self .accept (s .else_body )
1584
1591
1585
1592
def visit_except_handler_test (self , n : Node ) -> Type :
1586
1593
"""Type check an exception handler test clause."""
0 commit comments