11use clippy_utils:: diagnostics:: span_lint_and_sugg;
22use clippy_utils:: source:: snippet_with_applicability;
3+ use clippy_utils:: SpanlessEq ;
34use rustc_ast:: LitKind ;
45use rustc_data_structures:: packed:: Pu128 ;
56use rustc_errors:: Applicability ;
@@ -10,19 +11,19 @@ use rustc_session::declare_lint_pass;
1011
1112declare_clippy_lint ! {
1213 /// ### What it does
13- /// Checks for expressions like `x.count_ones() == 1` or `x & (x - 1) == 0`, which are manual
14+ /// Checks for expressions like `x.count_ones() == 1` or `x & (x - 1) == 0`, with x and unsigned integer, which are manual
1415 /// reimplementations of `x.is_power_of_two()`.
1516 /// ### Why is this bad?
1617 /// Manual reimplementations of `is_power_of_two` increase code complexity for little benefit.
1718 /// ### Example
1819 /// ```no_run
19- /// let x : u32 = 1 ;
20- /// let result = x .count_ones() == 1;
20+ /// let a : u32 = 4 ;
21+ /// let result = a .count_ones() == 1;
2122 /// ```
2223 /// Use instead:
2324 /// ```no_run
24- /// let x : u32 = 1 ;
25- /// let result = x .is_power_of_two();
25+ /// let a : u32 = 4 ;
26+ /// let result = a .is_power_of_two();
2627 /// ```
2728 #[ clippy:: version = "1.82.0" ]
2829 pub MANUAL_IS_POWER_OF_TWO ,
@@ -36,53 +37,106 @@ impl LateLintPass<'_> for ManualIsPowerOfTwo {
3637 fn check_expr ( & mut self , cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
3738 let mut applicability = Applicability :: MachineApplicable ;
3839
39- // x.count_ones() == 1
40- if let ExprKind :: Binary ( op, left, right) = expr. kind
41- && BinOpKind :: Eq == op. node
42- && let ExprKind :: MethodCall ( method_name, reciever, _, _) = left. kind
43- && method_name. ident . as_str ( ) == "count_ones"
44- && let ExprKind :: Lit ( lit) = right. kind
45- && let LitKind :: Int ( Pu128 ( 1 ) , _) = lit. node
46- && let & Uint ( _) = cx. typeck_results ( ) . expr_ty ( reciever) . kind ( )
40+ if let ExprKind :: Binary ( bin_op, left, right) = expr. kind
41+ && bin_op. node == BinOpKind :: Eq
4742 {
48- let snippet = snippet_with_applicability ( cx, reciever. span , ".." , & mut applicability) ;
49- let sugg = format ! ( "{snippet}.is_power_of_two()" ) ;
50- span_lint_and_sugg (
51- cx,
52- MANUAL_IS_POWER_OF_TWO ,
53- expr. span ,
54- "manually reimplementing `is_power_of_two`" ,
55- "consider using `.is_power_of_two()`" ,
56- sugg,
57- applicability,
58- ) ;
59- }
43+ // a.count_ones() == 1
44+ if let ExprKind :: MethodCall ( method_name, reciever, _, _) = left. kind
45+ && method_name. ident . as_str ( ) == "count_ones"
46+ && let & Uint ( _) = cx. typeck_results ( ) . expr_ty ( reciever) . kind ( )
47+ && check_lit ( right, 1 )
48+ {
49+ build_sugg ( cx, expr, reciever, & mut applicability) ;
50+ }
6051
61- // x & (x - 1) == 0
62- if let ExprKind :: Binary ( op, left, right) = expr. kind
63- && BinOpKind :: Eq == op. node
64- && let ExprKind :: Binary ( op1, left1, right1) = left. kind
65- && BinOpKind :: BitAnd == op1. node
66- && let ExprKind :: Binary ( op2, left2, right2) = right1. kind
67- && BinOpKind :: Sub == op2. node
68- && left1. span . eq_ctxt ( left2. span )
69- && let & Uint ( _) = cx. typeck_results ( ) . expr_ty ( left1) . kind ( )
70- && let ExprKind :: Lit ( lit) = right2. kind
71- && let LitKind :: Int ( Pu128 ( 1 ) , _) = lit. node
72- && let ExprKind :: Lit ( lit1) = right. kind
73- && let LitKind :: Int ( Pu128 ( 0 ) , _) = lit1. node
74- {
75- let snippet = snippet_with_applicability ( cx, left1. span , ".." , & mut applicability) ;
76- let sugg = format ! ( "{snippet}.is_power_of_two()" ) ;
77- span_lint_and_sugg (
78- cx,
79- MANUAL_IS_POWER_OF_TWO ,
80- expr. span ,
81- "manually reimplementing `is_power_of_two`" ,
82- "consider using `.is_power_of_two()`" ,
83- sugg,
84- applicability,
85- ) ;
52+ // 1 == a.count_ones()
53+ if let ExprKind :: MethodCall ( method_name, reciever, _, _) = right. kind
54+ && method_name. ident . as_str ( ) == "count_ones"
55+ && let & Uint ( _) = cx. typeck_results ( ) . expr_ty ( reciever) . kind ( )
56+ && check_lit ( left, 1 )
57+ {
58+ build_sugg ( cx, expr, reciever, & mut applicability) ;
59+ }
60+
61+ // a & (a - 1) == 0
62+ if let ExprKind :: Binary ( op1, left1, right1) = left. kind
63+ && op1. node == BinOpKind :: BitAnd
64+ && let ExprKind :: Binary ( op2, left2, right2) = right1. kind
65+ && op2. node == BinOpKind :: Sub
66+ && check_eq_expr ( cx, left1, left2)
67+ && let & Uint ( _) = cx. typeck_results ( ) . expr_ty ( left1) . kind ( )
68+ && check_lit ( right2, 1 )
69+ && check_lit ( right, 0 )
70+ {
71+ build_sugg ( cx, expr, left1, & mut applicability) ;
72+ }
73+
74+ // (a - 1) & a == 0;
75+ if let ExprKind :: Binary ( op1, left1, right1) = left. kind
76+ && op1. node == BinOpKind :: BitAnd
77+ && let ExprKind :: Binary ( op2, left2, right2) = left1. kind
78+ && op2. node == BinOpKind :: Sub
79+ && check_eq_expr ( cx, right1, left2)
80+ && let & Uint ( _) = cx. typeck_results ( ) . expr_ty ( right1) . kind ( )
81+ && check_lit ( right2, 1 )
82+ && check_lit ( right, 0 )
83+ {
84+ build_sugg ( cx, expr, right1, & mut applicability) ;
85+ }
86+
87+ // 0 == a & (a - 1);
88+ if let ExprKind :: Binary ( op1, left1, right1) = right. kind
89+ && op1. node == BinOpKind :: BitAnd
90+ && let ExprKind :: Binary ( op2, left2, right2) = right1. kind
91+ && op2. node == BinOpKind :: Sub
92+ && check_eq_expr ( cx, left1, left2)
93+ && let & Uint ( _) = cx. typeck_results ( ) . expr_ty ( left1) . kind ( )
94+ && check_lit ( right2, 1 )
95+ && check_lit ( left, 0 )
96+ {
97+ build_sugg ( cx, expr, left1, & mut applicability) ;
98+ }
99+
100+ // 0 == (a - 1) & a
101+ if let ExprKind :: Binary ( op1, left1, right1) = right. kind
102+ && op1. node == BinOpKind :: BitAnd
103+ && let ExprKind :: Binary ( op2, left2, right2) = left1. kind
104+ && op2. node == BinOpKind :: Sub
105+ && check_eq_expr ( cx, right1, left2)
106+ && let & Uint ( _) = cx. typeck_results ( ) . expr_ty ( right1) . kind ( )
107+ && check_lit ( right2, 1 )
108+ && check_lit ( left, 0 )
109+ {
110+ build_sugg ( cx, expr, right1, & mut applicability) ;
111+ }
86112 }
87113 }
88114}
115+
116+ fn build_sugg ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , reciever : & Expr < ' _ > , applicability : & mut Applicability ) {
117+ let snippet = snippet_with_applicability ( cx, reciever. span , ".." , applicability) ;
118+
119+ span_lint_and_sugg (
120+ cx,
121+ MANUAL_IS_POWER_OF_TWO ,
122+ expr. span ,
123+ "manually reimplementing `is_power_of_two`" ,
124+ "consider using `.is_power_of_two()`" ,
125+ format ! ( "{snippet}.is_power_of_two()" ) ,
126+ * applicability,
127+ ) ;
128+ }
129+
130+ fn check_lit ( expr : & Expr < ' _ > , expected_num : u128 ) -> bool {
131+ if let ExprKind :: Lit ( lit) = expr. kind
132+ && let LitKind :: Int ( Pu128 ( num) , _) = lit. node
133+ && num == expected_num
134+ {
135+ return true ;
136+ }
137+ false
138+ }
139+
140+ fn check_eq_expr ( cx : & LateContext < ' _ > , lhs : & Expr < ' _ > , rhs : & Expr < ' _ > ) -> bool {
141+ SpanlessEq :: new ( cx) . eq_expr ( lhs, rhs)
142+ }
0 commit comments