Skip to content

Commit 6c7ceb0

Browse files
committed
Process try statements in top-to-bottom order
Fixes #1289 (again).
1 parent 8c01031 commit 6c7ceb0

File tree

2 files changed

+44
-37
lines changed

2 files changed

+44
-37
lines changed

mypy/checker.py

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1543,44 +1543,51 @@ def visit_try_stmt(self, s: TryStmt) -> Type:
15431543
def visit_try_without_finally(self, s: TryStmt) -> None:
15441544
"""Type check a try statement, ignoring the finally block.
15451545
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.
15481550
"""
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])
15631573
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)
15841591

15851592
def visit_except_handler_test(self, n: Node) -> Type:
15861593
"""Type check an exception handler test clause."""

test-data/unit/check-statements.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ else:
516516
object(None) # E: Too many arguments for "object"
517517
[builtins fixtures/exception.py]
518518

519-
[case testRedefinedFunctionInTryWithElse-skip]
519+
[case testRedefinedFunctionInTryWithElse]
520520
def f() -> None: pass
521521
try:
522522
pass

0 commit comments

Comments
 (0)