Skip to content

Commit b249f13

Browse files
committed
feat: also detect non-consts
1 parent 4423e05 commit b249f13

File tree

4 files changed

+89
-16
lines changed

4 files changed

+89
-16
lines changed

clippy_lints/src/manual_rotate.rs

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

8787
let ctxt = expr.span.ctxt();
88-
if let Some(Constant::Int(l_amount_val)) = const_eval.eval_local(l_amount, ctxt)
88+
let (shift_function, amount) = if let Some(Constant::Int(l_amount_val)) =
89+
const_eval.eval_local(l_amount, ctxt)
8990
&& let Some(Constant::Int(r_amount_val)) = const_eval.eval_local(r_amount, ctxt)
9091
&& l_amount_val + r_amount_val == u128::from(bit_width)
9192
{
92-
let (shift_function, amount) = if l_amount_val < r_amount_val {
93+
if l_amount_val < r_amount_val {
9394
(l_shift_dir, l_amount)
9495
} else {
9596
(r_shift_dir, r_amount)
97+
}
98+
} else {
99+
let (amount1, binop, minuend, amount2, shift_direction) = match (l_amount.kind, r_amount.kind) {
100+
(_, ExprKind::Binary(binop, minuend, other)) => (l_amount, binop, minuend, other, l_shift_dir),
101+
(ExprKind::Binary(binop, minuend, other), _) => (r_amount, binop, minuend, other, r_shift_dir),
102+
_ => return,
96103
};
97-
let mut applicability = Applicability::MachineApplicable;
98-
let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren();
99-
let amount = sugg::Sugg::hir_with_applicability(cx, amount, "_", &mut applicability);
100-
span_lint_and_sugg(
101-
cx,
102-
MANUAL_ROTATE,
103-
expr.span,
104-
"there is no need to manually implement bit rotation",
105-
"this expression can be rewritten as",
106-
format!("{expr_sugg}.{shift_function}({amount})"),
107-
Applicability::MachineApplicable,
108-
);
109-
}
104+
105+
if let Some(Constant::Int(minuend)) = const_eval.eval_local(minuend, ctxt)
106+
&& clippy_utils::eq_expr_value(cx, amount1, amount2)
107+
// (x << s) | (x >> bit_width - s)
108+
&& ((binop.node == BinOpKind::Sub && u128::from(bit_width) == minuend)
109+
// (x << s) | (x >> (bit_width - 1) ^ s)
110+
|| (binop.node == BinOpKind::BitXor && u128::from(bit_width).checked_sub(minuend) == Some(1)))
111+
{
112+
// NOTE: we take these from the side that _doesn't_ have the binop, since it's probably simpler
113+
(shift_direction, amount1)
114+
} else {
115+
return;
116+
}
117+
};
118+
119+
let mut applicability = Applicability::MachineApplicable;
120+
let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_paren();
121+
let amount = sugg::Sugg::hir_with_applicability(cx, amount, "_", &mut applicability);
122+
span_lint_and_sugg(
123+
cx,
124+
MANUAL_ROTATE,
125+
expr.span,
126+
"there is no need to manually implement bit rotation",
127+
"this expression can be rewritten as",
128+
format!("{expr_sugg}.{shift_function}({amount})"),
129+
Applicability::MachineApplicable,
130+
);
110131
}
111132
}
112133
}

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)