8000 Distinguish redundant-expr warnings from unreachable warnings (#9125) · python/mypy@f2dfd91 · GitHub
[go: up one dir, main page]

Skip to content

Commit f2dfd91

Browse files
authored
Distinguish redundant-expr warnings from unreachable warnings (#9125)
1 parent 835b427 commit f2dfd91

File tree

7 files changed

+59
-16
lines changed

7 files changed

+59
-16
lines changed

docs/source/error_code_list2.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,24 @@ incorrect control flow or conditional checks that are accidentally always true o
193193
return
194194
# Error: Statement is unreachable [unreachable]
195195
print('unreachable')
196+
197+
Check that expression is redundant [redundant-expr]
198+
---------------------------------------------------
199+
200+
If you use :option:`--enable-error-code redundant-expr <mypy --enable-error-code>`,
201+
mypy generates an error if it thinks that an expression is redundant.
202+
203+
.. code-block:: python
204+
205+
# mypy: enable-error-code redundant-expr
206+
207+
def example(x: int) -> None:
208+
# Error: Left operand of 'and' is always true [redundant-expr]
209+
if isinstance(x, int) and x > 0:
210+
pass
211+
212+
# Error: If condition is always true [redundant-expr]
213+
1 if isinstance(x, int) else 0
214+
215+
# Error: If condition in comprehension is always true [redundant-expr]
216+
[i for i in range(x) if isinstance(i, int)]

mypy/checkexpr.py

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

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

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

3677-
if self.chk.options.warn_unreachable:
3686+
if codes.REDUNDANT_EXPR in self.chk.options.enabled_error_codes:
36783687
if true_map is None:
36793688
self.msg.redundant_condition_in_comprehension(False, condition)
36803689
elif false_map is None:
@@ -3687,7 +3696,7 @@ def visit_conditional_expr(self, e: ConditionalExpr, allow_none_return: bool = F
36873696
# Gain type information from isinstance if it is there
36883697
# but only for the current expression
36893698
if_map, else_map = self.chk.find_isinstance_check(e.cond)
3690-
if self.chk.options.warn_unreachable:
3699+
if codes.REDUNDANT_EXPR in self.chk.options.enabled_error_codes:
36913700
if if_map is None:
36923701
self.msg.redundant_condition_in_if(False, e.cond)
36933702
elif else_map is None:

mypy/errorcodes.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ 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',
117+
"Warn about redundant expressions",
118+
'General',
119+
default_enabled=False) # type: Final
115120

116121
# Syntax errors are often blocking.
117122
SYNTAX = ErrorCode(

mypy/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,7 @@ def add_invertible_flag(flag: str,
574574
group=lint_group)
575575
add_invertible_flag(' A93C --warn-unreachable', default=False, strict_flag=False,
576576
help="Warn about statements or expressions inferred to be"
577-
" unreachable or redundant",
577+
" unreachable",
578578
group=lint_group)
579579

580580
# 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,

test-data/unit/check-errorcodes.test

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,3 +771,18 @@ def f(**x: int) -> None:
771771
f(**1) # type: ignore[arg-type]
772772
[builtins fixtures/dict.pyi]
773773
[typing fixtures/typing-typeddict.pyi]
774+
775+
[case testRedundantExpressions]
776+
# flags: --enable-error-code redundant-expr
777+
def foo() -> bool: ...
778+
779+
lst = [1, 2, 3, 4]
780+
781+
b = False or foo() # E: Left operand of 'or' is always false [redundant-expr]
782+
c = True and foo() # E: Left operand of 'and' is always true [redundant-expr]
783+
g = 3 if True else 4 # E: If condition is always true [redundant-expr]
784+
h = 3 if False else 4 # E: If condition is always false [redundant-expr]
785+
i = [x for x in lst if True] # E: If condition in comprehension is always true [redundant-expr]
786+
j = [x for x in lst if False] # E: If condition in comprehension is always false [redundant-expr]
787+
k = [x for x in lst if isinstance(x, int) or foo()] # E: If condition in comprehension is always true [redundant-expr]
788+
[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)
0