@@ -56,20 +56,14 @@ impl Display for ShiftDirection {
5656 }
5757}
5858
59- fn parse_shift < ' tcx > (
60- cx : & LateContext < ' tcx > ,
61- expr : & ' tcx Expr < ' tcx > ,
62- ) -> Option < ( ShiftDirection , u128 , & ' tcx Expr < ' tcx > ) > {
59+ fn parse_shift < ' tcx > ( expr : & ' tcx Expr < ' tcx > ) -> Option < ( ShiftDirection , & ' tcx Expr < ' tcx > , & ' tcx Expr < ' tcx > ) > {
6360 if let ExprKind :: Binary ( op, l, r) = expr. kind {
6461 let dir = match op. node {
6562 BinOpKind :: Shl => ShiftDirection :: Left ,
6663 BinOpKind :: Shr => ShiftDirection :: Right ,
6764 _ => return None ,
6865 } ;
69- let const_expr = ConstEvalCtxt :: new ( cx) . eval_local ( r, expr. span . ctxt ( ) ) ?;
70- if let Constant :: Int ( shift) = const_expr {
71- return Some ( ( dir, shift, l) ) ;
72- }
66+ return Some ( ( dir, l, r) ) ;
7367 }
7468 None
7569}
@@ -78,40 +72,62 @@ impl LateLintPass<'_> for ManualRotate {
7872 fn check_expr < ' tcx > ( & mut self , cx : & LateContext < ' tcx > , expr : & Expr < ' tcx > ) {
7973 if let ExprKind :: Binary ( op, l, r) = expr. kind
8074 && let BinOpKind :: Add | BinOpKind :: BitOr = op. node
81- && let Some ( ( l_shift_dir, l_amount, l_expr) ) = parse_shift ( cx, l)
82- && let Some ( ( r_shift_dir, r_amount, r_expr) ) = parse_shift ( cx, r)
83- {
84- if l_shift_dir == r_shift_dir {
85- return ;
86- }
87- if !clippy_utils:: eq_expr_value ( cx, l_expr, r_expr) {
88- return ;
89- }
90- let Some ( bit_width) = ( match cx. typeck_results ( ) . expr_ty ( expr) . kind ( ) {
75+ && let Some ( ( l_shift_dir, l_expr, l_amount) ) = parse_shift ( l)
76+ && let Some ( ( r_shift_dir, r_expr, r_amount) ) = parse_shift ( r)
77+ && l_shift_dir != r_shift_dir
78+ && clippy_utils:: eq_expr_value ( cx, l_expr, r_expr)
79+ && let Some ( bit_width) = match cx. typeck_results ( ) . expr_ty ( expr) . kind ( ) {
9180 ty:: Int ( itype) => itype. bit_width ( ) ,
9281 ty:: Uint ( itype) => itype. bit_width ( ) ,
9382 _ => return ,
94- } ) else {
95- return ;
96- } ;
97- if l_amount + r_amount == u128:: from ( bit_width) {
98- let ( shift_function, amount) = if l_amount < r_amount {
83+ }
84+ {
85+ let const_eval = ConstEvalCtxt :: new ( cx) ;
86+
87+ let ctxt = expr. span . ctxt ( ) ;
88+ let ( shift_function, amount) = if let Some ( Constant :: Int ( l_amount_val) ) =
89+ const_eval. eval_local ( l_amount, ctxt)
90+ && let Some ( Constant :: Int ( r_amount_val) ) = const_eval. eval_local ( r_amount, ctxt)
91+ && l_amount_val + r_amount_val == u128:: from ( bit_width)
92+ {
93+ if l_amount_val < r_amount_val {
9994 ( l_shift_dir, l_amount)
10095 } else {
10196 ( 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 ,
102103 } ;
103- let mut applicability = Applicability :: MachineApplicable ;
104- let expr_sugg = sugg:: Sugg :: hir_with_applicability ( cx, l_expr, "_" , & mut applicability) . maybe_paren ( ) ;
105- span_lint_and_sugg (
106- cx,
107- MANUAL_ROTATE ,
108- expr. span ,
109- "there is no need to manually implement bit rotation" ,
110- "this expression can be rewritten as" ,
111- format ! ( "{expr_sugg}.{shift_function}({amount})" ) ,
112- Applicability :: MachineApplicable ,
113- ) ;
114- }
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+ ) ;
115131 }
116132 }
117133}
0 commit comments