1
1
use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
2
use clippy_utils:: source:: snippet_with_applicability;
3
+ use clippy_utils:: SpanlessEq ;
3
4
use rustc_ast:: LitKind ;
4
5
use rustc_data_structures:: packed:: Pu128 ;
5
6
use rustc_errors:: Applicability ;
@@ -10,19 +11,19 @@ use rustc_session::declare_lint_pass;
10
11
11
12
declare_clippy_lint ! {
12
13
/// ### 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
14
15
/// reimplementations of `x.is_power_of_two()`.
15
16
/// ### Why is this bad?
16
17
/// Manual reimplementations of `is_power_of_two` increase code complexity for little benefit.
17
18
/// ### Example
18
19
/// ```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;
21
22
/// ```
22
23
/// Use instead:
23
24
/// ```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();
26
27
/// ```
27
28
#[ clippy:: version = "1.82.0" ]
28
29
pub MANUAL_IS_POWER_OF_TWO ,
@@ -36,53 +37,106 @@ impl LateLintPass<'_> for ManualIsPowerOfTwo {
36
37
fn check_expr ( & mut self , cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
37
38
let mut applicability = Applicability :: MachineApplicable ;
38
39
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
47
42
{
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
+ }
60
51
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
+ }
86
112
}
87
113
}
88
114
}
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