Skip to content

Commit 742b5c6

Browse files
authored
Support __bool__ with Literal in --warn-unreachable (#15645)
This adds support for `Literal` as return type of `__bool__` in the reachability analysis. Fixes #7008
1 parent 0e4521a commit 742b5c6

File tree

2 files changed

+36
-8
lines changed

2 files changed

+36
-8
lines changed

mypy/typeops.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -602,10 +602,8 @@ def true_only(t: Type) -> ProperType:
602602
else:
603603
ret_type = _get_type_special_method_bool_ret_type(t)
604604

605-
if ret_type and ret_type.can_be_false and not ret_type.can_be_true:
606-
new_t = copy_type(t)
607-
new_t.can_be_true = False
608-
return new_t
605+
if ret_type and not ret_type.can_be_true:
606+
return UninhabitedType(line=t.line, column=t.column)
609607

610608
new_t = copy_type(t)
611609
new_t.can_be_false = False
@@ -637,10 +635,8 @@ def false_only(t: Type) -> ProperType:
637635
else:
638636
ret_type = _get_type_special_method_bool_ret_type(t)
639637

640-
if ret_type and ret_type.can_be_true and not ret_type.can_be_false:
641-
new_t = copy_type(t)
642-
new_t.can_be_false = False
643-
return new_t
638+
if ret_type and not ret_type.can_be_false:
639+
return UninhabitedType(line=t.line)
644640

645641
new_t = copy_type(t)
646642
new_t.can_be_true = False

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1379,6 +1379,38 @@ def f() -> None:
13791379
x = 1 # E: Statement is unreachable
13801380
[builtins fixtures/dict.pyi]
13811381

1382+
[case testUnreachableLiteralFrom__bool__]
1383+
# flags: --warn-unreachable
1384+
from typing_extensions import Literal
1385+
1386+
class Truth:
1387+
def __bool__(self) -> Literal[True]: ...
1388+
1389+
class Lie:
1390+
def __bool__(self) -> Literal[False]: ...
1391+
1392+
class Maybe:
1393+
def __bool__(self) -> Literal[True | False]: ...
1394+
1395+
t = Truth()
1396+
if t:
1397+
x = 1
1398+
else:
1399+
x = 2 # E: Statement is unreachable
1400+
1401+
if Lie():
1402+
x = 3 # E: Statement is unreachable
1403+
1404+
if Maybe():
1405+
x = 4
1406+
1407+
1408+
def foo() -> bool: ...
1409+
1410+
y = Truth() or foo() # E: Right operand of "or" is never evaluated
1411+
z = Lie() and foo() # E: Right operand of "and" is never evaluated
1412+
[builtins fixtures/dict.pyi]
1413+
13821414
[case testUnreachableModuleBody1]
13831415
# flags: --warn-unreachable
13841416
from typing import NoReturn

0 commit comments

Comments
 (0)