Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
8 changes: 8 additions & 0 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,7 @@ def check_first_pass(self, recurse_into_functions: bool = True) -> None:
and not self.is_noop_for_reachability(d)
):
self.msg.unreachable_statement(d)
self.binder.suppress_unreachable_warnings()
finish = True
reported_unreachable = True

Expand Down Expand Up @@ -3331,6 +3332,7 @@ def visit_block(self, b: Block) -> None:
and not self.is_noop_for_reachability(s)
):
self.msg.unreachable_statement(s)
self.binder.suppress_unreachable_warnings()
finish = True
reported_unreachable = True

Expand Down Expand Up @@ -5517,6 +5519,7 @@ def visit_try_stmt(self, s: TryStmt) -> None:
self.accept(s.finally_body)

if s.finally_body:
previously_suppressed = self.binder.is_unreachable_warning_suppressed()
# Then we try again for the more restricted set of options
# that can fall through. (Why do we need to check the
# finally clause twice? Depending on whether the finally
Expand All @@ -5534,6 +5537,11 @@ def visit_try_stmt(self, s: TryStmt) -> None:
self.accept(s.finally_body)
self.msg.iteration_dependent_errors(iter_errors)

if not previously_suppressed:
# The finally body might have warned about unreachability,
# but we still want anything afterwards to warn too.
self.binder.frames[-1].suppress_unreachable_warnings = False

def visit_try_without_finally(self, s: TryStmt, try_frame: bool) -> None:
"""Type check a try statement, ignoring the finally block.

Expand Down
27 changes: 27 additions & 0 deletions test-data/unit/check-unreachable-code.test
Original file line number Diff line number Diff line change
Expand Up @@ -1751,3 +1751,30 @@ assert sys.platform == "win32"
if False:
reveal_type(5) # E: Statement is unreachable \
# N: Revealed type is "Literal[5]?"

[case testCheckUnreachableOnlyFlagsOnce]
# flags: --check-unreachable --warn-unreachable
if False:
if False: # E: Statement is unreachable
"not flagged!"

[case testCheckUnreachableHandlesFinallyRight]
# flags: --check-unreachable --warn-unreachable
def f1() -> None:
# it doesn't reset suppression
assert False
try: # E: Statement is unreachable
pass
finally:
reveal_type(5) # N: Revealed type is "Literal[5]?"
"something"

def f2() -> None:
# but it does still mark things after as unreachable
try:
pass
finally:
assert False
reveal_type(5) # E: Statement is unreachable \
# N: Revealed type is "Literal[5]?"
"something" # E: Statement is unreachable
Loading