Skip to content

Commit 0ceacbe

Browse files
committed
Auto merge of rust-lang#8730 - tamaroning:fix8724, r=Alexendoo
[FP] identity_op in front of if fix rust-lang#8724 changelog: FP: [`identity_op`]: is now allowed in front of if statements, blocks and other expressions where the suggestion would be invalid. Resolved simular problems with blocks, mathces, and loops. identity_op always does NOT suggest reducing `0 + if b { 1 } else { 2 } + 3` into `if b { 1 } else { 2 } + 3` even in the case that the expression is in `f(expr)` or `let x = expr;` for now.
2 parents 95f8b26 + 6ad810f commit 0ceacbe

File tree

3 files changed

+180
-24
lines changed

3 files changed

+180
-24
lines changed

clippy_lints/src/identity_op.rs

Lines changed: 59 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use clippy_utils::get_parent_expr;
12
use clippy_utils::source::snippet;
23
use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind};
34
use rustc_lint::{LateContext, LateLintPass};
@@ -22,6 +23,11 @@ declare_clippy_lint! {
2223
/// # let x = 1;
2324
/// x / 1 + 0 * 1 - 0 | 0;
2425
/// ```
26+
///
27+
/// ### Known problems
28+
/// False negatives: `f(0 + if b { 1 } else { 2 } + 3);` is reducible to
29+
/// `f(if b { 1 } else { 2 } + 3);`. But the lint doesn't trigger for the code.
30+
/// See [#8724](https://github.com/rust-lang/rust-clippy/issues/8724)
2531
#[clippy::version = "pre 1.29.0"]
2632
pub IDENTITY_OP,
2733
complexity,
@@ -31,36 +37,66 @@ declare_clippy_lint! {
3137
declare_lint_pass!(IdentityOp => [IDENTITY_OP]);
3238

3339
impl<'tcx> LateLintPass<'tcx> for IdentityOp {
34-
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
35-
if e.span.from_expansion() {
40+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
41+
if expr.span.from_expansion() {
3642
return;
3743
}
38-
if let ExprKind::Binary(cmp, left, right) = e.kind {
39-
if is_allowed(cx, cmp, left, right) {
40-
return;
41-
}
42-
match cmp.node {
43-
BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
44-
check(cx, left, 0, e.span, right.span);
45-
check(cx, right, 0, e.span, left.span);
46-
},
47-
BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => check(cx, right, 0, e.span, left.span),
48-
BinOpKind::Mul => {
49-
check(cx, left, 1, e.span, right.span);
50-
check(cx, right, 1, e.span, left.span);
51-
},
52-
BinOpKind::Div => check(cx, right, 1, e.span, left.span),
53-
BinOpKind::BitAnd => {
54-
check(cx, left, -1, e.span, right.span);
55-
check(cx, right, -1, e.span, left.span);
56-
},
57-
BinOpKind::Rem => check_remainder(cx, left, right, e.span, left.span),
58-
_ => (),
44+
if let ExprKind::Binary(cmp, left, right) = &expr.kind {
45+
if !is_allowed(cx, *cmp, left, right) {
46+
match cmp.node {
47+
BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
48+
if reducible_to_right(cx, expr, right) {
49+
check(cx, left, 0, expr.span, right.span);
50+
}
51+
check(cx, right, 0, expr.span, left.span);
52+
},
53+
BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
54+
check(cx, right, 0, expr.span, left.span);
55+
},
56+
BinOpKind::Mul => {
57+
if reducible_to_right(cx, expr, right) {
58+
check(cx, left, 1, expr.span, right.span);
59+
}
60+
check(cx, right, 1, expr.span, left.span);
61+
},
62+
BinOpKind::Div => check(cx, right, 1, expr.span, left.span),
63+
BinOpKind::BitAnd => {
64+
if reducible_to_right(cx, expr, right) {
65+
check(cx, left, -1, expr.span, right.span);
66+
}
67+
check(cx, right, -1, expr.span, left.span);
68+
},
69+
BinOpKind::Rem => {
70+
// Don't call reducible_to_right because N % N is always reducible to 1
71+
check_remainder(cx, left, right, expr.span, left.span);
72+
},
73+
_ => (),
74+
}
5975
}
6076
}
6177
}
6278
}
6379

80+
/// Checks if `left op ..right` can be actually reduced to `right`
81+
/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }`
82+
/// cannot be reduced to `if b { 1 } else { 2 } + if b { 3 } else { 4 }`
83+
/// See #8724
84+
fn reducible_to_right(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> bool {
85+
if let ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) = right.kind {
86+
is_toplevel_binary(cx, binary)
87+
} else {
88+
true
89+
}
90+
}
91+
92+
fn is_toplevel_binary(cx: &LateContext<'_>, must_be_binary: &Expr<'_>) -> bool {
93+
if let Some(parent) = get_parent_expr(cx, must_be_binary) && let ExprKind::Binary(..) = &parent.kind {
94+
false
95+
} else {
96+
true
97+
}
98+
}
99+
64100
fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool {
65101
// This lint applies to integers
66102
!cx.typeck_results().expr_ty(left).peel_refs().is_integral()

tests/ui/identity_op.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,34 @@ fn main() {
7777
(x + 1) % 3; // no error
7878
4 % 3; // no error
7979
4 % -3; // no error
80+
81+
// See #8724
82+
let a = 0;
83+
let b = true;
84+
0 + if b { 1 } else { 2 };
85+
0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }; // no error
86+
0 + match a { 0 => 10, _ => 20 };
87+
0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 }; // no error
88+
0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 }; // no error
89+
0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 }; // no error
90+
91+
0 + if b { 0 + 1 } else { 2 };
92+
0 + match a { 0 => 0 + 10, _ => 20 };
93+
0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 };
94+
95+
let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 };
96+
let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 };
97+
98+
0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0;
99+
100+
0 + { a } + 3; // no error
101+
0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 }; // no error
102+
103+
fn f(_: i32) {
104+
todo!();
105+
}
106+
f(1 * a + { 8 * 5 });
107+
f(0 + if b { 1 } else { 2 } + 3); // no error
108+
const _: i32 = { 2 * 4 } + 0 + 3;
109+
const _: i32 = 0 + { 1 + 2 * 3 } + 3; // no error
80110
}

tests/ui/identity_op.stderr

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,5 +108,95 @@ error: the operation is ineffective. Consider reducing it to `1`
108108
LL | x + 1 % 3;
109109
| ^^^^^
110110

111-
error: aborting due to 18 previous errors
111+
error: the operation is ineffective. Consider reducing it to `if b { 1 } else { 2 }`
112+
--> $DIR/identity_op.rs:84:5
113+
|
114+
LL | 0 + if b { 1 } else { 2 };
115+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
116+
117+
error: the operation is ineffective. Consider reducing it to `match a { 0 => 10, _ => 20 }`
118+
--> $DIR/identity_op.rs:86:5
119+
|
120+
LL | 0 + match a { 0 => 10, _ => 20 };
121+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
122+
123+
error: the operation is ineffective. Consider reducing it to `if b { 0 + 1 } else { 2 }`
124+
--> $DIR/identity_op.rs:91:5
125+
|
126+
LL | 0 + if b { 0 + 1 } else { 2 };
127+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
128+
129+
error: the operation is ineffective. Consider reducing it to `1`
130+
--> $DIR/identity_op.rs:91:16
131+
|
132+
LL | 0 + if b { 0 + 1 } else { 2 };
133+
| ^^^^^
134+
135+
error: the operation is ineffective. Consider reducing it to `match a { 0 => 0 + 10, _ => 20 }`
136+
--> $DIR/identity_op.rs:92:5
137+
|
138+
LL | 0 + match a { 0 => 0 + 10, _ => 20 };
139+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
140+
141+
error: the operation is ineffective. Consider reducing it to `10`
142+
--> $DIR/identity_op.rs:92:25
143+
|
144+
LL | 0 + match a { 0 => 0 + 10, _ => 20 };
145+
| ^^^^^^
146+
147+
error: the operation is ineffective. Consider reducing it to `1`
148+
--> $DIR/identity_op.rs:93:16
149+
|
150+
LL | 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 };
151+
| ^^^^^
152+
153+
error: the operation is ineffective. Consider reducing it to `30`
154+
--> $DIR/identity_op.rs:93:52
155+
|
156+
LL | 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 };
157+
| ^^^^^^
158+
159+
error: the operation is ineffective. Consider reducing it to `1`
160+
--> $DIR/identity_op.rs:95:20
161+
|
162+
LL | let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 };
163+
| ^^^^^
164+
165+
error: the operation is ineffective. Consider reducing it to `1`
166+
--> $DIR/identity_op.rs:95:52
167+
|
168+
LL | let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 };
169+
| ^^^^^
170+
171+
error: the operation is ineffective. Consider reducing it to `1`
172+
--> $DIR/identity_op.rs:96:23
173+
|
174+
LL | let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 };
175+
| ^^^^^
176+
177+
error: the operation is ineffective. Consider reducing it to `1`
178+
--> $DIR/identity_op.rs:96:58
179+
|
180+
LL | let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 };
181+
| ^^^^^
182+
183+
error: the operation is ineffective. Consider reducing it to `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }`
184+
--> $DIR/identity_op.rs:98:5
185+
|
186+
LL | 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0;
187+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
188+
189+
error: the operation is ineffective. Consider reducing it to `a`
190+
--> $DIR/identity_op.rs:106:7
191+
|
192+
LL | f(1 * a + { 8 * 5 });
193+
| ^^^^^
194+
195+
error: the operation is ineffective. Consider reducing it to `{ 2 * 4 }`
196+
--> $DIR/identity_op.rs:108:20
197+
|
198+
LL | const _: i32 = { 2 * 4 } + 0 + 3;
199+
| ^^^^^^^^^^^^^
200+
201+
error: aborting due to 33 previous errors
112202

0 commit comments

Comments
 (0)