Skip to content

Commit 101573f

Browse files
committed
feat: also detect non-consts
1 parent 023cb52 commit 101573f

File tree

4 files changed

+88
-16
lines changed

4 files changed

+88
-16
lines changed

clippy_lints/src/manual_rotate.rs

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -84,28 +84,48 @@ impl LateLintPass<'_> for ManualRotate {
8484
{
8585
let const_eval = ConstEvalCtxt::new(cx);
8686

87-
if let Some(Constant::Int(l_amount_val)) = const_eval.eval(l_amount)
87+
let (shift_function, amount) = if let Some(Constant::Int(l_amount_val)) = const_eval.eval(l_amount)
8888
&& let Some(Constant::Int(r_amount_val)) = const_eval.eval(r_amount)
8989
&& l_amount_val + r_amount_val == u128::from(bit_width)
9090
{
91-
let (shift_function, amount) = if l_amount_val < r_amount_val {
91+
if l_amount_val < r_amount_val {
9292
(l_shift_dir, l_amount)
9393
} else {
9494
(r_shift_dir, r_amount)
95+
}
96+
} else {
97+
let (amount1, binop, minuend, amount2, shift_direction) = match (l_amount.kind, r_amount.kind) {
98+
(_, ExprKind::Binary(binop, minuend, other)) => (l_amount, binop, minuend, other, l_shift_dir),
99+
(ExprKind::Binary(binop, minuend, other), _) => (r_amount, binop, minuend, other, r_shift_dir),
100+
_ => return,
95101
};
96-
let mut applicability = Applicability::MachineApplicable;
97-
let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren();
98-
let amount = sugg::Sugg::hir_with_applicability(cx, amount, "_", &mut applicability);
99-
span_lint_and_sugg(
100-
cx,
101-
MANUAL_ROTATE,
102-
expr.span,
103-
"there is no need to manually implement bit rotation",
104-
"this expression can be rewritten as",
105-
format!("{expr_sugg}.{shift_function}({amount})"),
106-
Applicability::MachineApplicable,
107-
);
108-
}
102+
103+
if let Some(Constant::Int(minuend)) = const_eval.eval_simple(minuend)
104+
&& clippy_utils::eq_expr_value(cx, amount1, amount2)
105+
// (x << s) | (x >> bit_width - s)
106+
&& ((binop.node == BinOpKind::Sub && u128::from(bit_width) == minuend)
107+
// (x << s) | (x >> (bit_width - 1) ^ s)
108+
|| (binop.node == BinOpKind::BitXor && u128::from(bit_width).checked_sub(minuend) == Some(1)))
109+
{
110+
// NOTE: we take these from the side that _doesn't_ have the binop, since it's probably simpler
111+
(shift_direction, amount1)
112+
} else {
113+
return;
114+
}
115+
};
116+
117+
let mut applicability = Applicability::MachineApplicable;
118+
let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren();
119+
let amount = sugg::Sugg::hir_with_applicability(cx, amount, "_", &mut applicability);
120+
span_lint_and_sugg(
121+
cx,
122+
MANUAL_ROTATE,
123+
expr.span,
124+
"there is no need to manually implement bit rotation",
125+
"this expression can be rewritten as",
126+
format!("{expr_sugg}.{shift_function}({amount})"),
127+
Applicability::MachineApplicable,
128+
);
109129
}
110130
}
111131
}

tests/ui/manual_rotate.fixed

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,20 @@ fn main() {
4444
let mut l = vec![12_u8, 34];
4545
let y = (l.pop().unwrap() << 3) + (l.pop().unwrap() >> 5);
4646
}
47+
48+
fn issue13028() {
49+
let s = 5;
50+
let u = 5;
51+
let x: u32 = 123456;
52+
53+
let _ = x.rotate_left(s);
54+
//~^ manual_rotate
55+
let _ = x.rotate_left(s);
56+
//~^ manual_rotate
57+
// still works with consts
58+
let _ = x.rotate_right(9);
59+
//~^ manual_rotate
60+
61+
// don't lint, because `s` and `u` are different variables, albeit with the same value
62+
let _ = (x << s) | (x >> (32 - u));
63+
}

tests/ui/manual_rotate.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,20 @@ fn main() {
4444
let mut l = vec![12_u8, 34];
4545
let y = (l.pop().unwrap() << 3) + (l.pop().unwrap() >> 5);
4646
}
47+
48+
fn issue13028() {
49+
let s = 5;
50+
let u = 5;
51+
let x: u32 = 123456;
52+
53+
let _ = (x << s) | (x >> (32 - s));
54+
//~^ manual_rotate
55+
let _ = (x << s) | (x >> (31 ^ s));
56+
//~^ manual_rotate
57+
// still works with consts
58+
let _ = (x >> 9) | (x << (32 - 9));
59+
//~^ manual_rotate
60+
61+
// don't lint, because `s` and `u` are different variables, albeit with the same value
62+
let _ = (x << s) | (x >> (32 - u));
63+
}

tests/ui/manual_rotate.stderr

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,23 @@ error: there is no need to manually implement bit rotation
7373
LL | let _ = (x_i64 >> N) | (x_i64 << (64 - N));
7474
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i64.rotate_right(N)`
7575

76-
error: aborting due to 12 previous errors
76+
error: there is no need to manually implement bit rotation
77+
--> tests/ui/manual_rotate.rs:53:13
78+
|
79+
LL | let _ = (x << s) | (x >> (32 - s));
80+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x.rotate_left(s)`
81+
82+
error: there is no need to manually implement bit rotation
83+
--> tests/ui/manual_rotate.rs:55:13
84+
|
85+
LL | let _ = (x << s) | (x >> (31 ^ s));
86+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x.rotate_left(s)`
87+
88+
error: there is no need to manually implement bit rotation
89+
--> tests/ui/manual_rotate.rs:58:13
90+
|
91+
LL | let _ = (x >> 9) | (x << (32 - 9));
92+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x.rotate_right(9)`
93+
94+
error: aborting due to 15 previous errors
7795

0 commit comments

Comments
 (0)