Skip to content

Commit 4f319bb

Browse files
committed
Add an option to enable checking unreachable code
1 parent ac03c88 commit 4f319bb

File tree

7 files changed

+39
-3
lines changed

7 files changed

+39
-3
lines changed

docs/source/config_file.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,13 @@ section of the command line docs.
579579

580580
For example, it would report an error for :code:`def f(a: int, b)` but not :code:`def f(a, b)`.
581581

582+
.. confval:: check_unreachable
583+
584+
:type: boolean
585+
:default: False
586+
587+
Type-checks unreachable code.
588+
582589
.. confval:: check_untyped_defs
583590

584591
:type: boolean

mypy/checker.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,10 +470,16 @@ def check_first_pass(self) -> None:
470470
if not reported_unreachable and self.binder.is_unreachable():
471471
if not self.should_report_unreachable_issues():
472472
reported_unreachable = True
473+
if not self.options.check_unreachable:
474+
break
475+
473476
elif not self.is_noop_for_reachability(d):
474477
self.msg.unreachable_statement(d)
475478
self.binder.emitted_unreachable_warning()
476479
reported_unreachable = True
480+
if not self.options.check_unreachable:
481+
break
482+
477483
self.accept(d)
478484

479485
assert not self.current_node_deferred
@@ -3129,10 +3135,16 @@ def visit_block(self, b: Block) -> None:
31293135
break
31303136
elif not self.should_report_unreachable_issues():
31313137
reported_unreachable = True
3138+
if not self.options.check_unreachable:
3139+
break
3140+
31323141
elif not self.is_noop_for_reachability(s):
31333142
self.msg.unreachable_statement(s)
31343143
self.binder.emitted_unreachable_warning()
31353144
reported_unreachable = True
3145+
if not self.options.check_unreachable:
3146+
break
3147+
31363148
self.accept(s)
31373149

31383150
def should_report_unreachable_issues(self) -> bool:

mypy/checkexpr.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5925,11 +5925,11 @@ def analyze_cond_branch(
59255925
allow_none_return: bool = False,
59265926
suppress_unreachable_errors: bool | None = None,
59275927
) -> Type:
5928-
# TODO: default based on flag (default to `True` if flag is not passed)
59295928
unreachable_errors_suppressed = (
59305929
suppress_unreachable_errors
59315930
if suppress_unreachable_errors is not None
5932-
else self.chk.binder.is_unreachable_warning_suppressed()
5931+
else not self.chk.options.check_unreachable
5932+
or self.chk.binder.is_unreachable_warning_suppressed()
59335933
)
59345934
with self.chk.binder.frame_context(can_skip=True, fall_through=0):
59355935
self.chk.push_type_map(map)

mypy/main.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,14 @@ def add_invertible_flag(
910910
group=strictness_group,
911911
)
912912

913+
add_invertible_flag(
914+
"--check-unreachable",
915+
default=False,
916+
strict_flag=False,
917+
help="Type check unreachable code",
918+
group=strictness_group,
919+
)
920+
913921
strict_help = "Strict mode; enables the following flags: {}".format(
914922
", ".join(strict_flag_names)
915923
)

mypy/options.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class BuildType:
2626
"allow_untyped_globals",
2727
"always_false",
2828
"always_true",
29+
"check_unreachable",
2930
"check_untyped_defs",
3031
"debug_cache",
3132
"disable_error_code",
@@ -43,8 +44,8 @@ class BuildType:
4344
"enable_error_code",
4445
"enabled_error_codes",
4546
"extra_checks",
46-
"follow_imports_for_stubs",
4747
"follow_imports",
48+
"follow_imports_for_stubs",
4849
"follow_untyped_imports",
4950
"ignore_errors",
5051
"ignore_missing_imports",
@@ -159,6 +160,9 @@ def __init__(self) -> None:
159160
# Disallow defining incompletely typed functions
160161
self.disallow_incomplete_defs = False
161162

163+
# Type check unreachable code
164+
self.check_unreachable = False
165+
162166
# Type check unannotated functions
163167
self.check_untyped_defs = False
164168

mypy/test/testcheck.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ def run_case_once(
140140
options.force_uppercase_builtins = True
141141
if "union-error" not in testcase.file:
142142
options.force_union_syntax = True
143+
options.check_unreachable = not testcase.name.endswith("_skip_unreachable")
143144

144145
if incremental_step and options.incremental:
145146
# Don't overwrite # flags: --no-incremental in incremental test cases

test-data/unit/check-errorcodes.test

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1245,3 +1245,7 @@ def process(response1: int,response2: int) -> int: # E: Overloaded function sign
12451245

12461246
def process(response1,response2)-> Union[float,int]:
12471247
return response1 + response2
1248+
1249+
[case testUnreachableCodeSkipped_skip_unreachable]
1250+
if False:
1251+
reveal_type("x") # will not output anything

0 commit comments

Comments
 (0)