Skip to content

Commit 70b10cd

Browse files
committed
Distinguish redundant-expr warnings from unreachable warnings
1 parent d94853d commit 70b10cd

File tree

8 files changed

+46
-16
lines changed

8 files changed

+46
-16
lines changed

mypy/checkexpr.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2740,6 +2740,17 @@ def check_boolean_op(self, e: OpExpr, context: Context) -> Type:
27402740
restricted_left_type = true_only(left_type)
27412741
result_is_left = not left_type.can_be_false
27422742

2743+
# If left_map is None then we know mypy considers the left expression
2744+
# to be reundant.
2745+
#
2746+
# Note that we perform these checks *before* we take into account
2747+
# the analysis from the semanal phase below. We assume that nodes
2748+
# marked as unreachable during semantic analysis were done so intentionally.
2749+
# So, we shouldn't report an error.
2750+
if self.chk.options.warn_redundant_expr:
2751+
if left_map is None:
2752+
self.msg.redundant_left_operand(e.op, e.left)
2753+
27432754
# If right_map is None then we know mypy considers the right branch
27442755
# to be unreachable and therefore any errors found in the right branch
27452756
# should be suppressed.
@@ -2749,10 +2760,8 @@ def check_boolean_op(self, e: OpExpr, context: Context) -> Type:
27492760
# marked as unreachable during semantic analysis were done so intentionally.
27502761
# So, we shouldn't report an error.
27512762
if self.chk.options.warn_unreachable:
2752-
if left_map is None:
2753-
self.msg.redundant_left_operand(e.op, e.left)
27542763
if right_map is None:
2755-
self.msg.redundant_right_operand(e.op, e.right)
2764+
self.msg.unreachable_right_operand(e.op, e.right)
27562765

27572766
if e.right_unreachable:
27582767
right_map = None
@@ -3673,7 +3682,7 @@ def check_for_comp(self, e: Union[GeneratorExpr, DictionaryComprehension]) -> No
36733682
for var, type in true_map.items():
36743683
self.chk.binder.put(var, type)
36753684

3676-
if self.chk.options.warn_unreachable:
3685+
if self.chk.options.warn_redundant_expr:
36773686
if true_map is None:
36783687
self.msg.redundant_condition_in_comprehension(False, condition)
36793688
elif false_map is None:
@@ -3686,7 +3695,7 @@ def visit_conditional_expr(self, e: ConditionalExpr, allow_none_return: bool = F
36863695
# Gain type information from isinstance if it is there
36873696
# but only for the current expression
36883697
if_map, else_map = self.chk.find_isinstance_check(e.cond)
3689-
if self.chk.options.warn_unreachable:
3698+
if self.chk.options.warn_redundant_expr:
36903699
if if_map is None:
36913700
self.msg.redundant_condition_in_if(False, e.cond)
36923701
elif else_map is None:

mypy/errorcodes.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ def __str__(self) -> str:
112112
'General') # type: Final
113113
UNREACHABLE = ErrorCode(
114114
'unreachable', "Warn about unreachable statements or expressions", 'General') # type: Final
115+
REDUNDANT_EXPR = ErrorCode(
116+
'redundant-expr', "Warn about redundant expressions", 'General') # type: Final
115117

116118
# Syntax errors are often blocking.
117119
SYNTAX = ErrorCode(

mypy/main.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,10 @@ def add_invertible_flag(flag: str,
576576
group=lint_group)
577577
add_invertible_flag('--warn-unreachable', default=False, strict_flag=False,
578578
help="Warn about statements or expressions inferred to be"
579-
" unreachable or redundant",
579+
" unreachable",
580+
group=lint_group)
581+
add_invertible_flag('--warn-redundant-expr', default=False, strict_flag=False,
582+
help="Warn about expressions inferred to be redundant",
580583
group=lint_group)
581584

582585
# Note: this group is intentionally added here even though we don't add

mypy/messages.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1280,7 +1280,7 @@ def redundant_left_operand(self, op_name: str, context: Context) -> None:
12801280
"""
12811281
self.redundant_expr("Left operand of '{}'".format(op_name), op_name == 'and', context)
12821282

1283-
def redundant_right_operand(self, op_name: str, context: Context) -> None:
1283+
def unreachable_right_operand(self, op_name: str, context: Context) -> None:
12841284
"""Indicates that the right operand of a boolean expression is redundant:
12851285
it does not change the truth value of the entire condition as a whole.
12861286
'op_name' should either be the string "and" or the string "or".
@@ -1299,7 +1299,7 @@ def redundant_condition_in_assert(self, truthiness: bool, context: Context) -> N
12991299

13001300
def redundant_expr(self, description: str, truthiness: bool, context: Context) -> None:
13011301
self.fail("{} is always {}".format(description, str(truthiness).lower()),
1302-
context, code=codes.UNREACHABLE)
1302+
context, code=codes.REDUNDANT_EXPR)
13031303

13041304
def impossible_intersection(self,
13051305
formatted_base_class_list: str,

mypy/options.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class BuildType:
5151
"strict_optional_whitelist",
5252
"warn_no_return",
5353
"warn_return_any",
54+
"warn_redundant_expr",
5455
"warn_unreachable",
5556
"warn_unused_ignores",
5657
} # type: Final
@@ -174,6 +175,10 @@ def __init__(self) -> None:
174175
# type analysis.
175176
self.warn_unreachable = False
176177

178+
# Report an error for any expressions inferred to be redundant as a result of
179+
# type analysis.
180+
self.warn_redundant_expr = False
181+
177182
# Variable names considered True
178183
self.always_true = [] # type: List[str]
179184

mypy/test/testcheck.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
'check-typevar-values.test',
4343
'check-unsupported.test',
4444
'check-unreachable-code.test',
45+
'check-redundant-expr.test',
4546
'check-unions.test',
4647
'check-isinstance.test',
4748
'check-lists.test',
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
-- Type checker test cases for conditional checks that result in some
2+
-- expressions classified as redundant.
3+
4+
[case testRedundantExpressions]
5+
# flags: --warn-redundant-expr
6+
def foo() -> bool: ...
7+
8+
lst = [1, 2, 3, 4]
9+
10+
b = False or foo() # E: Left operand of 'or' is always false
11+
c = True and foo() # E: Left operand of 'and' is always true
12+
g = 3 if True else 4 # E: If condition is always true
13+
h = 3 if False else 4 # E: If condition is always false
14+
i = [x for x in lst if True] # E: If condition in comprehension is always true
15+
j = [x for x in lst if False] # E: If condition in comprehension is always false
16+
k = [x for x in lst if isinstance(x, int) or foo()] # E: If condition in comprehension is always true
17+
[builtins fixtures/isinstancelist.pyi]

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

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -898,18 +898,11 @@ def foo() -> bool: ...
898898
lst = [1, 2, 3, 4]
899899

900900
a = True or foo() # E: Right operand of 'or' is never evaluated
901-
b = False or foo() # E: Left operand of 'or' is always false
902-
c = True and foo() # E: Left operand of 'and' is always true
903901
d = False and foo() # E: Right operand of 'and' is never evaluated
904902
e = True or (True or (True or foo())) # E: Right operand of 'or' is never evaluated
905903
f = (True or foo()) or (True or foo()) # E: Right operand of 'or' is never evaluated
906-
g = 3 if True else 4 # E: If condition is always true
907-
h = 3 if False else 4 # E: If condition is always false
908-
i = [x for x in lst if True] # E: If condition in comprehension is always true
909-
j = [x for x in lst if False] # E: If condition in comprehension is always false
910904

911-
k = [x for x in lst if isinstance(x, int) or foo()] # E: If condition in comprehension is always true \
912-
# E: Right operand of 'or' is never evaluated
905+
k = [x for x in lst if isinstance(x, int) or foo()] # E: Right operand of 'or' is never evaluated
913906
[builtins fixtures/isinstancelist.pyi]
914907

915908
[case testUnreachableFlagMiscTestCaseMissingMethod]

0 commit comments

Comments
 (0)