@@ -165,8 +165,9 @@ def visit_file(self, file_node: MypyFile, path: str) -> None:
165
165
# gross, but no other clear way to tell
166
166
self .is_typeshed_stub = self .is_stub and 'typeshed' in os .path .normpath (path ).split (os .sep )
167
167
168
- for d in file_node .defs :
169
- self .accept (d )
168
+ with self .binder .top_frame_context ():
169
+ for d in file_node .defs :
170
+ self .accept (d )
170
171
171
172
self .leave_partial_types ()
172
173
@@ -231,12 +232,10 @@ def accept_loop(self, body: Node, else_body: Node = None) -> Type:
231
232
Then check the else_body.
232
233
"""
233
234
# The outer frame accumulates the results of all iterations
234
- with self .binder .frame_context (1 ) as outer_frame :
235
+ with self .binder .frame_context (can_skip = False ) :
235
236
self .binder .push_loop_frame ()
236
237
while True :
237
- with self .binder .frame_context (1 ):
238
- # We may skip each iteration
239
- self .binder .options_on_return [- 1 ].append (outer_frame )
238
+ with self .binder .frame_context (can_skip = True ):
240
239
self .accept (body )
241
240
if not self .binder .last_pop_changed :
242
241
break
@@ -418,7 +417,7 @@ def check_func_def(self, defn: FuncItem, typ: CallableType, name: str) -> None:
418
417
for item , typ in self .expand_typevars (defn , typ ):
419
418
old_binder = self .binder
420
419
self .binder = ConditionalTypeBinder ()
421
- with self .binder .frame_context ():
420
+ with self .binder .top_frame_context ():
422
421
defn .expanded .append (item )
423
422
424
423
# We may be checking a function definition or an anonymous
@@ -505,7 +504,7 @@ def is_implicit_any(t: Type) -> bool:
505
504
self .accept (init )
506
505
507
506
# Type check body in a new scope.
508
- with self .binder .frame_context ():
507
+ with self .binder .top_frame_context ():
509
508
self .accept (item .body )
510
509
511
510
self .return_types .pop ()
@@ -815,7 +814,7 @@ def visit_class_def(self, defn: ClassDef) -> Type:
815
814
self .enter_partial_types ()
816
815
old_binder = self .binder
817
816
self .binder = ConditionalTypeBinder ()
818
- with self .binder .frame_context ():
817
+ with self .binder .top_frame_context ():
819
818
self .accept (defn .defs )
820
819
self .binder = old_binder
821
820
self .check_multiple_inheritance (typ )
@@ -911,9 +910,9 @@ def visit_block(self, b: Block) -> Type:
911
910
if b .is_unreachable :
912
911
return None
913
912
for s in b .body :
914
- self .accept (s )
915
- if self .binder .breaking_out :
913
+ if self .binder .is_unreachable ():
916
914
break
915
+ self .accept (s )
917
916
918
917
def visit_assignment_stmt (self , s : AssignmentStmt ) -> Type :
919
918
"""Type check an assignment statement.
@@ -1354,7 +1353,10 @@ def visit_expression_stmt(self, s: ExpressionStmt) -> Type:
1354
1353
1355
1354
def visit_return_stmt (self , s : ReturnStmt ) -> Type :
1356
1355
"""Type check a return statement."""
1357
- self .binder .breaking_out = True
1356
+ self .check_return_stmt (s )
1357
+ self .binder .unreachable ()
1358
+
1359
+ def check_return_stmt (self , s : ReturnStmt ) -> None :
1358
1360
if self .is_within_function ():
1359
1361
if self .function_stack [- 1 ].is_generator :
1360
1362
return_type = self .get_generator_return_type (self .return_types [- 1 ])
@@ -1422,9 +1424,9 @@ def count_nested_types(self, typ: Instance, check_type: str) -> int:
1422
1424
1423
1425
def visit_if_stmt (self , s : IfStmt ) -> Type :
1424
1426
"""Type check an if statement."""
1425
- breaking_out = True
1426
1427
# This frame records the knowledge from previous if/elif clauses not being taken.
1427
- with self .binder .frame_context ():
1428
+ # Fall-through to the original frame is handled explicitly in each block.
1429
+ with self .binder .frame_context (can_skip = False , fall_through = 0 ):
1428
1430
for e , b in zip (s .expr , s .body ):
1429
1431
t = self .accept (e )
1430
1432
self .check_not_void (t , e )
@@ -1438,13 +1440,12 @@ def visit_if_stmt(self, s: IfStmt) -> Type:
1438
1440
pass
1439
1441
else :
1440
1442
# Only type check body if the if condition can be true.
1441
- with self .binder .frame_context (2 ):
1443
+ with self .binder .frame_context (can_skip = True , fall_through = 2 ):
1442
1444
if if_map :
1443
1445
for var , type in if_map .items ():
1444
1446
self .binder .push (var , type )
1445
1447
1446
1448
self .accept (b )
1447
- breaking_out = breaking_out and self .binder .last_pop_breaking_out
1448
1449
1449
1450
if else_map :
1450
1451
for var , type in else_map .items ():
@@ -1457,12 +1458,9 @@ def visit_if_stmt(self, s: IfStmt) -> Type:
1457
1458
# print("Warning: isinstance always true")
1458
1459
break
1459
1460
else : # Didn't break => can't prove one of the conditions is always true
1460
- with self .binder .frame_context (2 ):
1461
+ with self .binder .frame_context (can_skip = False , fall_through = 2 ):
1461
1462
if s .else_body :
1462
1463
self .accept (s .else_body )
1463
- breaking_out = breaking_out and self .binder .last_pop_breaking_out
1464
- if breaking_out :
1465
- self .binder .breaking_out = True
1466
1464
return None
1467
1465
1468
1466
def visit_while_stmt (self , s : WhileStmt ) -> Type :
@@ -1498,11 +1496,11 @@ def visit_assert_stmt(self, s: AssertStmt) -> Type:
1498
1496
1499
1497
def visit_raise_stmt (self , s : RaiseStmt ) -> Type :
1500
1498
"""Type check a raise statement."""
1501
- self .binder .breaking_out = True
1502
1499
if s .expr :
1503
1500
self .type_check_raise (s .expr , s )
1504
1501
if s .from_expr :
1505
1502
self .type_check_raise (s .from_expr , s )
1503
+ self .binder .unreachable ()
1506
1504
1507
1505
def type_check_raise (self , e : Node , s : RaiseStmt ) -> None :
1508
1506
typ = self .accept (e )
@@ -1531,44 +1529,52 @@ def type_check_raise(self, e: Node, s: RaiseStmt) -> None:
1531
1529
def visit_try_stmt (self , s : TryStmt ) -> Type :
1532
1530
"""Type check a try statement."""
1533
1531
# Our enclosing frame will get the result if the try/except falls through.
1534
- # This one gets all possible intermediate states
1535
- with self .binder .frame_context ():
1532
+ # This one gets all possible states after the try block exited abnormally
1533
+ # (by exception, return, break, etc.)
1534
+ with self .binder .frame_context (can_skip = False , fall_through = 0 ):
1536
1535
if s .finally_body :
1537
1536
self .binder .try_frames .add (len (self .binder .frames ) - 1 )
1538
- breaking_out = self .visit_try_without_finally (s )
1537
+ self .visit_try_without_finally (s )
1539
1538
self .binder .try_frames .remove (len (self .binder .frames ) - 1 )
1540
1539
# First we check finally_body is type safe for all intermediate frames
1541
1540
self .accept (s .finally_body )
1542
- breaking_out = breaking_out or self .binder .breaking_out
1543
1541
else :
1544
- breaking_out = self .visit_try_without_finally (s )
1545
-
1546
- if not breaking_out and s .finally_body :
1547
- # Then we try again for the more restricted set of options that can fall through
1542
+ self .visit_try_without_finally (s )
1543
+
1544
+ if s .finally_body :
1545
+ # Then we try again for the more restricted set of options
1546
+ # that can fall through. (Why do we need to check the
1547
+ # finally clause twice? Depending on whether the finally
1548
+ # clause was reached by the try clause falling off the end
1549
+ # or exiting abnormally, after completing the finally clause
1550
+ # either flow will continue to after the entire try statement
1551
+ # or the exception/return/etc. will be processed and control
1552
+ # flow will escape. We need to check that the finally clause
1553
+ # type checks in both contexts, but only the resulting types
1554
+ # from the latter context affect the type state in the code
1555
+ # that follows the try statement.)
1548
1556
self .accept (s .finally_body )
1549
- self . binder . breaking_out = breaking_out
1557
+
1550
1558
return None
1551
1559
1552
- def visit_try_without_finally (self , s : TryStmt ) -> bool :
1560
+ def visit_try_without_finally (self , s : TryStmt ) -> None :
1553
1561
"""Type check a try statement, ignoring the finally block.
1554
1562
1555
- Return whether we are guaranteed to be breaking out.
1556
1563
Otherwise, it will place the results possible frames of
1557
1564
that don't break out into self.binder.frames[-2].
1558
1565
"""
1559
- breaking_out = True
1560
1566
# This frame records the possible states that exceptions can leave variables in
1561
1567
# during the try: block
1562
- with self .binder .frame_context ():
1563
- with self .binder .frame_context (3 ):
1568
+ with self .binder .frame_context (can_skip = False , fall_through = 0 ):
1569
+ with self .binder .frame_context (can_skip = False , fall_through = 3 ):
1564
1570
self .binder .try_frames .add (len (self .binder .frames ) - 2 )
1571
+ self .binder .allow_jump (- 1 )
1565
1572
self .accept (s .body )
1566
1573
self .binder .try_frames .remove (len (self .binder .frames ) - 2 )
1567
1574
if s .else_body :
1568
1575
self .accept (s .else_body )
1569
- breaking_out = breaking_out and self .binder .last_pop_breaking_out
1570
1576
for i in range (len (s .handlers )):
1571
- with self .binder .frame_context (3 ):
1577
+ with self .binder .frame_context (can_skip = True , fall_through = 3 ):
1572
1578
if s .types [i ]:
1573
1579
t = self .visit_except_handler_test (s .types [i ])
1574
1580
if s .vars [i ]:
@@ -1592,8 +1598,6 @@ def visit_try_without_finally(self, s: TryStmt) -> bool:
1592
1598
var = cast (Var , s .vars [i ].node )
1593
1599
var .type = DeletedType (source = source )
1594
1600
self .binder .cleanse (s .vars [i ])
1595
- breaking_out = breaking_out and self .binder .last_pop_breaking_out
1596
- return breaking_out
1597
1601
1598
1602
def visit_except_handler_test (self , n : Node ) -> Type :
1599
1603
"""Type check an exception handler test clause."""
@@ -1811,13 +1815,13 @@ def visit_member_expr(self, e: MemberExpr) -> Type:
1811
1815
return self .expr_checker .visit_member_expr (e )
1812
1816
1813
1817
def visit_break_stmt (self , s : BreakStmt ) -> Type :
1814
- self .binder .breaking_out = True
1815
1818
self .binder .allow_jump (self .binder .loop_frames [- 1 ] - 1 )
1819
+ self .binder .unreachable ()
1816
1820
return None
1817
1821
1818
1822
def visit_continue_stmt (self , s : ContinueStmt ) -> Type :
1819
- self .binder .breaking_out = True
1820
1823
self .binder .allow_jump (self .binder .loop_frames [- 1 ])
1824
+ self .binder .unreachable ()
1821
1825
return None
1822
1826
1823
1827
def visit_int_expr (self , e : IntExpr ) -> Type :
0 commit comments