8000 Add --warn-no-return, and major binder refactoring by rwbarton · Pull Request #1748 · python/mypy · GitHub
[go: up one dir, main page]

Skip to content

Add --warn-no-return, and major binder refactoring #1748

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Oct 18, 2016
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Process try statements in top-to-bottom order
Fixes #1289 (again).
  • Loading branch information
rwbarton committed Oct 13, 2016
commit 4388a8145eea27692a0884c7716b919aafe85f69
79 changes: 43 additions & 36 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -1643,44 +1643,51 @@ def visit_try_stmt(self, s: TryStmt) -> Type:
def visit_try_without_finally(self, s: TryStmt) -> None:
"""Type check a try statement, ignoring the finally block.

Otherwise, it will place the results possible frames of
that don't break out into self.binder.frames[-2].
On entry, the top frame should receive all flow that exits the
try block abnormally (i.e., such that the else block does not
execute), and its parent should receive all flow that exits
the try block normally.
"""
# This frame records the possible states that exceptions can leave variables in
# during the try: block
with self.binder.frame_context(can_skip=False, fall_through=0):
with self.binder.frame_context(can_skip=False, fall_through=3):
self.binder.try_frames.add(len(self.binder.frames) - 2)
self.binder.allow_jump(-1)
self.accept(s.body)
self.binder.try_frames.remove(len(self.binder.frames) - 2)
if s.else_body:
self.accept(s.else_body)
for i in range(len(s.handlers)):
with self.binder.frame_context(can_skip=True, fall_through=3):
if s.types[i]:
t = self.visit_except_handler_test(s.types[i])
# This frame will run the else block if the try fell through.
# In that case, control flow continues to the parent of what
# was the top frame on entry.
with self.binder.frame_context(can_skip=False, fall_through=2):
# This frame receives exit via exception, and runs exception handlers
with self.binder.frame_context(can_skip=False, fall_through=2):
# Finally, the body of the try statement
with self.binder.frame_context(can_skip=False, fall_through=2):
self.binder.try_frames.add(len(self.binder.frames) - 2)
self.binder.allow_jump(-1)
self.accept(s.body)
self.binder.try_frames.remove(len(self.binder.frames) - 2)
for i in range(len(s.handlers)):
with self.binder.frame_context(can_skip=True, fall_through=4):
if s.types[i]:
t = self.visit_except_handler_test(s.types[i])
if s.vars[i]:
# To support local variables, we make this a definition line,
# causing assignment to set the variable's type.
s.vars[i].is_def = True
self.check_assignment(s.vars[i], self.temp_node(t, s.vars[i]))
self.accept(s.handlers[i])
if s.vars[i]:
# To support local variables, we make this a definition line,
# causing assignment to set the variable's type.
s.vars[i].is_def = True
self.check_assignment(s.vars[i], self.temp_node(t, s.vars[i]))
self.accept(s.handlers[i])
if s.vars[i]:
# Exception variables are deleted in python 3 but not python 2.
8000 # But, since it's bad form in python 2 and the type checking
# wouldn't work very well, we delete it anyway.

# Unfortunately, this doesn't let us detect usage before the
# try/except block.
if self.options.python_version[0] >= 3:
source = s.vars[i].name
else:
source = ('(exception variable "{}", which we do not accept outside'
'except: blocks even in python 2)'.format(s.vars[i].name))
var = cast(Var, s.vars[i].node)
var.type = DeletedType(source=source)
self.binder.cleanse(s.vars[i])
# Exception variables are deleted in python 3 but not python 2.
# But, since it's bad form in python 2 and the type checking
# wouldn't work very well, we delete it anyway.

# Unfortunately, this doesn't let us detect usage before the
# try/except block.
if self.options.python_version[0] >= 3:
source = s.vars[i].name
else:
source = ('(exception variable "{}", which we do not '
'accept outside except: blocks even in '
'python 2)'.format(s.vars[i].name))
var = cast(Var, s.vars[i].node)
var.type = DeletedType(source=source)
self.binder.cleanse(s.vars[i])
if s.else_body:
self.accept(s.else_body)

def visit_except_handler_test(self, n: Expression) -> Type:
"""Type check an exception handler test clause."""
Expand Down
3 changes: 0 additions & 3 deletions test-data/unit/check-optional.test
Original file line number Diff line number Diff line change
Expand Up @@ -368,9 +368,6 @@ def lookup_field(name, obj):
attr = f()
else:
attr = None
[out]
main: note: In function "lookup_field":
main:10: error: Need type annotation for variable

[case testTernaryWithNone]
reveal_type(None if bool() else 0) # E: Revealed type is 'Union[builtins.int, builtins.None]'
Expand Down
2 changes: 1 addition & 1 deletion test-data/unit/check-statements.test
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ else:
object(None) # E: Too many arguments for "object"
[builtins fixtures/exception.pyi]

[case testRedefinedFunctionInTryWithElse-skip]
[case testRedefinedFunctionInTryWithElse]
def f() -> None: pass
try:
pass
Expand Down
0