Skip to content

Commit 00f66c9

Browse files
committed
Distinguish redundant-expr warnings from unreachable warnings
1 parent b1f5121 commit 00f66c9

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
@@ -3669,7 +3678,7 @@ def check_for_comp(self, e: Union[GeneratorExpr, DictionaryComprehension]) -> No
36693678
for var, type in true_map.items():
36703679
self.chk.binder.put(var, type)
36713680

3672-
if self.chk.options.warn_unreachable:
3681+
if self.chk.options.warn_redundant_expr:
36733682
if true_map is None:
36743683
self.msg.redundant_condition_in_comprehension(False, condition)
36753684
elif false_map is None:
@@ -3682,7 +3691,7 @@ def visit_conditional_expr(self, e: ConditionalExpr, allow_none_return: bool = F
36823691
# Gain type information from isinstance if it is there
36833692
# but only for the current expression
36843693
if_map, else_map = self.chk.find_isinstance_check(e.cond)
3685-
if self.chk.options.warn_unreachable:
3694+
if self.chk.options.warn_redundant_expr:
36863695
if if_map is None:
36873696
self.msg.redundant_condition_in_if(False, e.cond)
36883697
elif else_map is None:

mypy/errorcodes.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ def __str__(self) -> str:
105105
'General') # type: Final
106106
UNREACHABLE = ErrorCode(
107107
'unreachable', "Warn about unreachable statements or expressions", 'General') # type: Final
108+
REDUNDANT_EXPR = ErrorCode(
109+
'redundant-expr', "Warn about redundant expressions", 'General') # type: Final
108110

109111
# Syntax errors are often blocking.
110112
SYNTAX = ErrorCode(

mypy/main.py

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

581584
# 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
@@ -48,6 +48,7 @@ class BuildType:
4848
"strict_optional_whitelist",
4949
"warn_no_return",
5050
"warn_return_any",
51+
"warn_redundant_expr",
5152
"warn_unreachable",
5253
"warn_unused_ignores",
5354
} # type: Final
@@ -171,6 +172,10 @@ def __init__(self) -> None:
171172
# type analysis.
172173
self.warn_unreachable = False
173174

175+
# Report an error for any expressions inferred to be redundant as a result of
176+
# type analysis.
177+
self.warn_redundant_expr = False
178+
174179
# Variable names considered True
175180
self.always_true = [] # type: List[str]
176181

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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy