Skip to content

Commit 0c9f506

Browse files
committed
Decouple warning and skipping unreachable code
1 parent 2ab8849 commit 0c9f506

9 files changed

+117
-62
lines changed

mypy/checker.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -462,14 +462,13 @@ def check_first_pass(self) -> None:
462462
with self.tscope.module_scope(self.tree.fullname):
463463
with self.enter_partial_types(), self.binder.top_frame_context():
464464
for d in self.tree.defs:
465-
if (
466-
self.binder.is_unreachable()
467-
and self.should_report_unreachable_issues()
468-
and not self.is_raising_or_empty(d)
469-
):
470-
self.msg.unreachable_statement(d)
471-
break
472-
self.accept(d)
465+
if self.binder.is_unreachable():
466+
if not self.is_noop_for_reachability(d):
467+
if self.should_report_unreachable_issues():
468+
self.msg.unreachable_statement(d)
469+
break
470+
else:
471+
self.accept(d)
473472

474473
assert not self.current_node_deferred
475474

@@ -2678,10 +2677,12 @@ def visit_block(self, b: Block) -> None:
26782677
return
26792678
for s in b.body:
26802679
if self.binder.is_unreachable():
2681-
if self.should_report_unreachable_issues() and not self.is_raising_or_empty(s):
2682-
self.msg.unreachable_statement(s)
2683-
break
2684-
self.accept(s)
2680+
if not self.is_noop_for_reachability(s):
2681+
if self.should_report_unreachable_issues():
2682+
self.msg.unreachable_statement(s)
2683+
break
2684+
else:
2685+
self.accept(s)
26852686

26862687
def should_report_unreachable_issues(self) -> bool:
26872688
return (
@@ -2691,11 +2692,11 @@ def should_report_unreachable_issues(self) -> bool:
26912692
and not self.binder.is_unreachable_warning_suppressed()
26922693
)
26932694

2694-
def is_raising_or_empty(self, s: Statement) -> bool:
2695+
def is_noop_for_reachability(self, s: Statement) -> bool:
26952696
"""Returns 'true' if the given statement either throws an error of some kind
26962697
or is a no-op.
26972698
2698-
We use this function mostly while handling the '--warn-unreachable' flag. When
2699+
We use this function while handling the '--warn-unreachable' flag. When
26992700
that flag is present, we normally report an error on any unreachable statement.
27002701
But if that statement is just something like a 'pass' or a just-in-case 'assert False',
27012702
reporting an error would be annoying.

test-data/unit/check-classes.test

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7684,10 +7684,14 @@ class D:
76847684
def __new__(cls) -> NoReturn: ...
76857685
def __init__(self) -> NoReturn: ...
76867686

7687-
reveal_type(A()) # N: Revealed type is "<nothing>"
7688-
reveal_type(B()) # N: Revealed type is "<nothing>"
7689-
reveal_type(C()) # N: Revealed type is "<nothing>"
7690-
reveal_type(D()) # N: Revealed type is "<nothing>"
7687+
if object():
7688+
reveal_type(A()) # N: Revealed type is "<nothing>"
7689+
if object():
7690+
reveal_type(B()) # N: Revealed type is "<nothing>"
7691+
if object():
7692+
reveal_type(C()) # N: Revealed type is "<nothing>"
7693+
if object():
7694+
reveal_type(D()) # N: Revealed type is "<nothing>"
76917695

76927696
[case testOverloadedNewAndInitNoReturn]
76937697
from typing import NoReturn, overload
@@ -7726,13 +7730,20 @@ class D:
77267730
def __init__(self, a: int) -> None: ...
77277731
def __init__(self, a: int = ...) -> None: ...
77287732

7729-
reveal_type(A()) # N: Revealed type is "<nothing>"
7733+
if object():
7734+
reveal_type(A()) # N: Revealed type is "<nothing>"
77307735
reveal_type(A(1)) # N: Revealed type is "__main__.A"
7731-
reveal_type(B()) # N: Revealed type is "<nothing>"
7736+
7737+
if object():
7738+
reveal_type(B()) # N: Revealed type is "<nothing>"
77327739
reveal_type(B(1)) # N: Revealed type is "__main__.B"
7733-
reveal_type(C()) # N: Revealed type is "<nothing>"
7740+
7741+
if object():
7742+
reveal_type(C()) # N: Revealed type is "<nothing>"
77347743
reveal_type(C(1)) # N: Revealed type is "__main__.C"
7735-
reveal_type(D()) # N: Revealed type is "<nothing>"
7744+
7745+
if object():
7746+
reveal_type(D()) # N: Revealed type is "<nothing>"
77367747
reveal_type(D(1)) # N: Revealed type is "__main__.D"
77377748

77387749
[case testClassScopeImportWithWrapperAndError]

test-data/unit/check-fastparse.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,8 @@ def g(): # E: Type signature has too many arguments
228228
assert 1, 2
229229
assert (1, 2) # E: Assertion is always true, perhaps remove parentheses?
230230
assert (1, 2), 3 # E: Assertion is always true, perhaps remove parentheses?
231-
assert ()
232231
assert (1,) # E: Assertion is always true, perhaps remove parentheses?
232+
assert ()
233233
[builtins fixtures/tuple.pyi]
234234

235235
[case testFastParseAssertMessage]

test-data/unit/check-incremental.test

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5383,6 +5383,7 @@ tmp/c.py:2: note: Revealed type is "b.<subclass of "A" and "B">"
53835383
tmp/c.py:2: note: Revealed type is "b.<subclass of "A" and "C">"
53845384

53855385
[case testIsInstanceAdHocIntersectionIncrementalIntersectionToUnreachable]
5386+
# flags: --warn-unreachable
53865387
import c
53875388
[file a.py]
53885389
class A:
@@ -5399,7 +5400,9 @@ class A:
53995400
class B:
54005401
x: str
54015402
x: A
5402-
assert isinstance(x, B)
5403+
if object():
5404+
assert isinstance(x, B)
5405+
foo = 42
54035406
y = x
54045407

54055408
[file b.py]
@@ -5413,17 +5416,22 @@ reveal_type(z)
54135416
[out]
54145417
tmp/c.py:2: note: Revealed type is "a.<subclass of "A" and "B">"
54155418
[out2]
5419+
tmp/a.py:7: error: Subclass of "A" and "B" cannot exist: would have incompatible method signatures
5420+
tmp/a.py:8: error: Statement is unreachable
54165421
tmp/c.py:2: note: Revealed type is "a.A"
54175422

54185423
[case testIsInstanceAdHocIntersectionIncrementalUnreachaableToIntersection]
5424+
# flags: --warn-unreachable
54195425
import c
54205426
[file a.py]
54215427
class A:
54225428
x: int
54235429
class B:
54245430
x: str
54255431
x: A
5426-
assert isinstance(x, B)
5432+
if object():
5433+
assert isinstance(x, B)
5434+
foo = 42
54275435
y = x
54285436

54295437
[file a.py.2]
@@ -5444,6 +5452,8 @@ from b import z
54445452
reveal_type(z)
54455453
[builtins fixtures/isinstance.pyi]
54465454
[out]
5455+
tmp/a.py:7: error: Subclass of "A" and "B" cannot exist: would have incompatible method signatures
5456+
tmp/a.py:8: error: Statement is unreachable
54475457
tmp/c.py:2: note: Revealed type is "a.A"
54485458
[out2]
54495459
tmp/c.py:2: note: Revealed type is "a.<subclass of "A" and "B">"

test-data/unit/check-inference-context.test

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -622,8 +622,10 @@ reveal_type((lambda x, y: x + y)(1, 2)) # N: Revealed type is "builtins.int"
622622
reveal_type((lambda s, i: s)(i=0, s='x')) # N: Revealed type is "Literal['x']?"
623623
reveal_type((lambda s, i: i)(i=0, s='x')) # N: Revealed type is "Literal[0]?"
624624
reveal_type((lambda x, s, i: x)(1.0, i=0, s='x')) # N: Revealed type is "builtins.float"
625-
(lambda x, s, i: x)() # E: Too few arguments
626-
(lambda: 0)(1) # E: Too many arguments
625+
if object():
626+
(lambda x, s, i: x)() # E: Too few arguments
627+
if object():
628+
(lambda: 0)(1) # E: Too many arguments
627629
-- varargs are not handled, but it should not crash
628630
reveal_type((lambda *k, s, i: i)(type, i=0, s='x')) # N: Revealed type is "Any"
629631
reveal_type((lambda s, *k, i: i)(i=0, s='x')) # N: Revealed type is "Any"

test-data/unit/check-native-int.test

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,10 @@ reveal_type(meet(f32, f)) # N: Revealed type is "mypy_extensions.i32"
8787
reveal_type(meet(f, f32)) # N: Revealed type is "mypy_extensions.i32"
8888
reveal_type(meet(f64, f)) # N: Revealed type is "mypy_extensions.i64"
8989
reveal_type(meet(f, f64)) # N: Revealed type is "mypy_extensions.i64"
90-
reveal_type(meet(f32, f64)) # N: Revealed type is "<nothing>"
91-
reveal_type(meet(f64, f32)) # N: Revealed type is "<nothing>"
90+
if object():
91+
reveal_type(meet(f32, f64)) # N: Revealed type is "<nothing>"
92+
if object():
93+
reveal_type(meet(f64, f32)) # N: Revealed type is "<nothing>"
9294

9395
reveal_type(meet(f, fa)) # N: Revealed type is "builtins.int"
9496
reveal_type(meet(f32, fa)) # N: Revealed type is "mypy_extensions.i32"
@@ -148,8 +150,10 @@ def meet(c1: Callable[[T], None], c2: Callable[[T], None]) -> T:
148150
def ff(x: float) -> None: pass
149151
def fi32(x: i32) -> None: pass
150152

151-
reveal_type(meet(ff, fi32)) # N: Revealed type is "<nothing>"
152-
reveal_type(meet(fi32, ff)) # N: Revealed type is "<nothing>"
153+
if object():
154+
reveal_type(meet(ff, fi32)) # N: Revealed type is "<nothing>"
155+
if object():
156+
reveal_type(meet(fi32, ff)) # N: Revealed type is "<nothing>"
153157
[builtins fixtures/dict.pyi]
154158

155159
[case testNativeIntForLoopRange]

test-data/unit/check-statements.test

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -405,11 +405,16 @@ main:5: error: Exception must be derived from BaseException
405405
class A: pass
406406
class MyError(BaseException): pass
407407
def f(): pass
408-
raise BaseException
409-
raise MyError
410-
raise A # E: Exception must be derived from BaseException
411-
raise object # E: Exception must be derived from BaseException
412-
raise f # E: Exception must be derived from BaseException
408+
if object():
409+
raise BaseException
410+
if object():
411+
raise MyError
412+
if object():
413+
raise A # E: Exception must be derived from BaseException
414+
if object():
415+
raise object # E: Exception must be derived from BaseException
416+
if object():
417+
raise f # E: Exception must be derived from BaseException
413418
[builtins fixtures/exception.pyi]
414419

415420
[case testRaiseClassObjectCustomInit]
@@ -425,18 +430,30 @@ class MyKwError(Exception):
425430
class MyErrorWithDefault(Exception):
426431
def __init__(self, optional=1) -> None:
427432
...
428-
raise BaseException
429-
raise Exception
430-
raise BaseException(1)
431-
raise Exception(2)
432-
raise MyBaseError(4)
433-
raise MyError(5, 6)
434-
raise MyKwError(kwonly=7)
435-
raise MyErrorWithDefault(8)
436-
raise MyErrorWithDefault
437-
raise MyBaseError # E: Too few arguments for "MyBaseError"
438-
raise MyError # E: Too few arguments for "MyError"
439-
raise MyKwError # E: Missing named argument "kwonly" for "MyKwError"
433+
if object():
434+
raise BaseException
435+
if object():
436+
raise Exception
437+
if object():
438+
raise BaseException(1)
439+
if object():
440+
raise Exception(2)
441+
if object():
442+
raise MyBaseError(4)
443+
if object():
444+
raise MyError(5, 6)
445+
if object():
446+
raise MyKwError(kwonly=7)
447+
if object():
448+
raise MyErrorWithDefault(8)
449+
if object():
450+
raise MyErrorWithDefault
451+
if object():
452+
raise MyBaseError # E: Too few arguments for "MyBaseError"
453+
if object():
454+
raise MyError # E: Too few arguments for "MyError"
455+
if object():
456+
raise MyKwError # E: Missing named argument "kwonly" for "MyKwError"
440457
[builtins fixtures/exception.pyi]
441458

442459
[case testRaiseExceptionType]
@@ -469,10 +486,14 @@ f = None # type: MyError
469486
a = None # type: A
470487
x = None # type: BaseException
471488
del x
472-
raise e from a # E: Exception must be derived from BaseException
473-
raise e from e
474-
raise e from f
475-
raise e from x # E: Trying to read deleted variable "x"
489+
if object():
490+
raise e from a # E: Exception must be derived from BaseException
491+
if object():
492+
raise e from e
493+
if object():
494+
raise e from f
495+
if object():
496+
raise e from x # E: Trying to read deleted variable "x"
476497
class A: pass
477498
class MyError(BaseException): pass
478499
[builtins fixtures/exception.pyi]
@@ -482,11 +503,16 @@ import typing
482503
class A: pass
483504
class MyError(BaseException): pass
484505
def f(): pass
485-
raise BaseException from BaseException
486-
raise BaseException from MyError
487-
raise BaseException from A # E: Exception must be derived from BaseException
488-
raise BaseException from object # E: Exception must be derived from BaseException
489-
raise BaseException from f # E: Exception must be derived from BaseException
506+
if object():
507+
raise BaseException from BaseException
508+
if object():
509+
raise BaseException from MyError
510+
if object():
511+
raise BaseException from A # E: Exception must be derived from BaseException
512+
if object():
513+
raise BaseException from object # E: Exception must be derived from BaseException
514+
if object():
515+
raise BaseException from f # E: Exception must be derived from BaseException
490516
[builtins fixtures/exception.pyi]
491517

492518
[case testTryFinallyStatement]

test-data/unit/check-typevar-tuple.test

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ reveal_type(f(args)) # N: Revealed type is "Tuple[builtins.int, builtins.str]"
1717

1818
reveal_type(f(varargs)) # N: Revealed type is "builtins.tuple[builtins.int, ...]"
1919

20-
f(0) # E: Argument 1 to "f" has incompatible type "int"; expected <nothing>
20+
if object():
21+
f(0) # E: Argument 1 to "f" has incompatible type "int"; expected <nothing>
2122

2223
def g(a: Tuple[Unpack[Ts]], b: Tuple[Unpack[Ts]]) -> Tuple[Unpack[Ts]]:
2324
return a

test-data/unit/check-unreachable-code.test

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -873,15 +873,15 @@ def expect_str(x: str) -> str: pass
873873
x: int
874874
if False:
875875
assert False
876-
reveal_type(x)
876+
reveal_type(x) # E: Statement is unreachable
877877

878878
if False:
879879
raise Exception()
880-
reveal_type(x)
880+
reveal_type(x) # E: Statement is unreachable
881881

882882
if False:
883883
assert_never(x)
884-
reveal_type(x)
884+
reveal_type(x) # E: Statement is unreachable
885885

886886
if False:
887887
nonthrowing_assert_never(x) # E: Statement is unreachable
@@ -890,7 +890,7 @@ if False:
890890
if False:
891891
# Ignore obvious type errors
892892
assert_never(expect_str(x))
893-
reveal_type(x)
893+
reveal_type(x) # E: Statement is unreachable
894894
[builtins fixtures/exception.pyi]
895895

896896
[case testNeverVariants]

0 commit comments

Comments
 (0)