1
- use clippy_utils :: SpanlessEq ;
1
+ use clippy_config :: Conf ;
2
2
use clippy_utils:: diagnostics:: span_lint_and_sugg;
3
- use clippy_utils:: source:: snippet_with_applicability;
4
- use rustc_ast:: LitKind ;
5
- use rustc_data_structures:: packed:: Pu128 ;
3
+ use clippy_utils:: msrvs:: { self , Msrv } ;
4
+ use clippy_utils:: sugg:: Sugg ;
5
+ use clippy_utils:: ty:: ty_from_hir_ty;
6
+ use clippy_utils:: { SpanlessEq , is_in_const_context, is_integer_literal} ;
6
7
use rustc_errors:: Applicability ;
7
- use rustc_hir:: { BinOpKind , Expr , ExprKind } ;
8
+ use rustc_hir:: { BinOpKind , Expr , ExprKind , QPath } ;
8
9
use rustc_lint:: { LateContext , LateLintPass } ;
9
- use rustc_middle:: ty:: Uint ;
10
- use rustc_session:: declare_lint_pass ;
10
+ use rustc_middle:: ty;
11
+ use rustc_session:: impl_lint_pass ;
11
12
12
13
declare_clippy_lint ! {
13
14
/// ### What it does
@@ -33,112 +34,111 @@ declare_clippy_lint! {
33
34
"manually reimplementing `is_power_of_two`"
34
35
}
35
36
36
- declare_lint_pass ! ( ManualIsPowerOfTwo => [ MANUAL_IS_POWER_OF_TWO ] ) ;
37
+ pub struct ManualIsPowerOfTwo {
38
+ msrv : Msrv ,
39
+ }
37
40
38
- impl LateLintPass < ' _ > for ManualIsPowerOfTwo {
39
- fn check_expr ( & mut self , cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
40
- let mut applicability = Applicability :: MachineApplicable ;
41
+ impl_lint_pass ! ( ManualIsPowerOfTwo => [ MANUAL_IS_POWER_OF_TWO ] ) ;
41
42
42
- if let ExprKind :: Binary ( bin_op, left, right) = expr. kind
43
- && bin_op. node == BinOpKind :: Eq
44
- {
45
- // a.count_ones() == 1
46
- if let ExprKind :: MethodCall ( method_name, receiver, [ ] , _) = left. kind
47
- && method_name. ident . as_str ( ) == "count_ones"
48
- && let & Uint ( _) = cx. typeck_results ( ) . expr_ty ( receiver) . kind ( )
49
- && check_lit ( right, 1 )
50
- {
51
- build_sugg ( cx, expr, receiver, & mut applicability) ;
52
- }
43
+ impl ManualIsPowerOfTwo {
44
+ pub fn new ( conf : & ' static Conf ) -> Self {
45
+ Self { msrv : conf. msrv }
46
+ }
53
47
54
- // 1 == a.count_ones()
55
- if let ExprKind :: MethodCall ( method_name, receiver, [ ] , _) = right. kind
56
- && method_name. ident . as_str ( ) == "count_ones"
57
- && let & Uint ( _) = cx. typeck_results ( ) . expr_ty ( receiver) . kind ( )
58
- && check_lit ( left, 1 )
59
- {
60
- build_sugg ( cx, expr, receiver, & mut applicability) ;
61
- }
48
+ fn build_sugg ( & self , cx : & LateContext < ' _ > , expr : & Expr < ' _ > , receiver : & Expr < ' _ > ) {
49
+ if is_in_const_context ( cx) && !self . msrv . meets ( cx, msrvs:: CONST_IS_POWER_OF_TWO ) {
50
+ return ;
51
+ }
62
52
63
- // a & (a - 1) == 0
64
- if let ExprKind :: Binary ( op1, left1, right1) = left. kind
65
- && op1. node == BinOpKind :: BitAnd
66
- && let ExprKind :: Binary ( op2, left2, right2) = right1. kind
67
- && op2. node == BinOpKind :: Sub
68
- && check_eq_expr ( cx, left1, left2)
69
- && let & Uint ( _) = cx. typeck_results ( ) . expr_ty ( left1) . kind ( )
70
- && check_lit ( right2, 1 )
71
- && check_lit ( right, 0 )
72
- {
73
- build_sugg ( cx, expr, left1, & mut applicability) ;
74
- }
53
+ let mut applicability = Applicability :: MachineApplicable ;
54
+ let snippet = Sugg :: hir_with_applicability ( cx, receiver, "_" , & mut applicability) ;
75
55
76
- // (a - 1) & a == 0;
77
- if let ExprKind :: Binary ( op1, left1, right1) = left. kind
78
- && op1. node == BinOpKind :: BitAnd
79
- && let ExprKind :: Binary ( op2, left2, right2) = left1. kind
80
- && op2. node == BinOpKind :: Sub
81
- && check_eq_expr ( cx, right1, left2)
82
- && let & Uint ( _) = cx. typeck_results ( ) . expr_ty ( right1) . kind ( )
83
- && check_lit ( right2, 1 )
84
- && check_lit ( right, 0 )
85
- {
86
- build_sugg ( cx, expr, right1, & mut applicability) ;
87
- }
56
+ span_lint_and_sugg (
57
+ cx,
58
+ MANUAL_IS_POWER_OF_TWO ,
59
+ expr. span ,
60
+ "manually reimplementing `is_power_of_two`" ,
61
+ "consider using `.is_power_of_two()`" ,
62
+ format ! ( "{}.is_power_of_two()" , snippet. maybe_paren( ) ) ,
63
+ applicability,
64
+ ) ;
65
+ }
66
+ }
88
67
89
- // 0 == a & (a - 1);
90
- if let ExprKind :: Binary ( op1, left1, right1) = right. kind
91
- && op1. node == BinOpKind :: BitAnd
92
- && let ExprKind :: Binary ( op2, left2, right2) = right1. kind
93
- && op2. node == BinOpKind :: Sub
94
- && check_eq_expr ( cx, left1, left2)
95
- && let & Uint ( _) = cx. typeck_results ( ) . expr_ty ( left1) . kind ( )
96
- && check_lit ( right2, 1 )
97
- && check_lit ( left, 0 )
68
+ impl < ' tcx > LateLintPass < ' tcx > for ManualIsPowerOfTwo {
69
+ fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & Expr < ' tcx > ) {
70
+ if !expr. span . from_expansion ( )
71
+ && let Some ( ( lhs, rhs) ) = unexpanded_binop_operands ( expr, BinOpKind :: Eq )
72
+ {
73
+ if let Some ( a) = count_ones_receiver ( cx, lhs)
74
+ && is_integer_literal ( rhs, 1 )
98
75
{
99
- build_sugg ( cx, expr, left1, & mut applicability) ;
100
- }
101
-
102
- // 0 == (a - 1) & a
103
- if let ExprKind :: Binary ( op1, left1, right1) = right. kind
104
- && op1. node == BinOpKind :: BitAnd
105
- && let ExprKind :: Binary ( op2, left2, right2) = left1. kind
106
- && op2. node == BinOpKind :: Sub
107
- && check_eq_expr ( cx, right1, left2)
108
- && let & Uint ( _) = cx. typeck_results ( ) . expr_ty ( right1) . kind ( )
109
- && check_lit ( right2, 1 )
110
- && check_lit ( left, 0 )
76
+ self . build_sugg ( cx, expr, a) ;
77
+ } else if let Some ( a) = count_ones_receiver ( cx, rhs)
78
+ && is_integer_literal ( lhs, 1 )
79
+ {
80
+ self . build_sugg ( cx, expr, a) ;
81
+ } else if is_integer_literal ( rhs, 0 )
82
+ && let Some ( a) = is_and_minus_one ( cx, lhs)
83
+ {
84
+ self . build_sugg ( cx, expr, a) ;
85
+ } else if is_integer_literal ( lhs, 0 )
86
+ && let Some ( a) = is_and_minus_one ( cx, rhs)
111
87
{
112
- build_sugg ( cx, expr, right1 , & mut applicability ) ;
88
+ self . build_sugg ( cx, expr, a ) ;
113
89
}
114
90
}
115
91
}
116
92
}
117
93
118
- fn build_sugg ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , receiver : & Expr < ' _ > , applicability : & mut Applicability ) {
119
- let snippet = snippet_with_applicability ( cx, receiver. span , ".." , applicability) ;
120
-
121
- span_lint_and_sugg (
122
- cx,
123
- MANUAL_IS_POWER_OF_TWO ,
124
- expr. span ,
125
- "manually reimplementing `is_power_of_two`" ,
126
- "consider using `.is_power_of_two()`" ,
127
- format ! ( "{snippet}.is_power_of_two()" ) ,
128
- * applicability,
129
- ) ;
94
+ /// Return the unsigned integer receiver of `.count_ones()` or the argument of
95
+ /// `<int-type>::count_ones(…)`.
96
+ fn count_ones_receiver < ' tcx > ( cx : & LateContext < ' tcx > , expr : & Expr < ' tcx > ) -> Option < & ' tcx Expr < ' tcx > > {
97
+ let ( method, ty, receiver) = if let ExprKind :: MethodCall ( method_name, receiver, [ ] , _) = expr. kind {
98
+ ( method_name, cx. typeck_results ( ) . expr_ty_adjusted ( receiver) , receiver)
99
+ } else if let ExprKind :: Call ( func, [ arg] ) = expr. kind
100
+ && let ExprKind :: Path ( QPath :: TypeRelative ( ty, func_name) ) = func. kind
101
+ {
102
+ ( func_name, ty_from_hir_ty ( cx, ty) , arg)
103
+ } else {
104
+ return None ;
105
+ } ;
106
+ ( method. ident . as_str ( ) == "count_ones" && matches ! ( ty. kind( ) , ty:: Uint ( _) ) ) . then_some ( receiver)
130
107
}
131
108
132
- fn check_lit ( expr : & Expr < ' _ > , expected_num : u128 ) -> bool {
133
- if let ExprKind :: Lit ( lit) = expr. kind
134
- && let LitKind :: Int ( Pu128 ( num) , _) = lit. node
135
- && num == expected_num
109
+ /// Return `greater` if `smaller == greater - 1`
110
+ fn is_one_less < ' tcx > (
111
+ cx : & LateContext < ' tcx > ,
112
+ greater : & ' tcx Expr < ' tcx > ,
113
+ smaller : & Expr < ' tcx > ,
114
+ ) -> Option < & ' tcx Expr < ' tcx > > {
115
+ if let Some ( ( lhs, rhs) ) = unexpanded_binop_operands ( smaller, BinOpKind :: Sub )
116
+ && SpanlessEq :: new ( cx) . eq_expr ( greater, lhs)
117
+ && is_integer_literal ( rhs, 1 )
118
+ && matches ! ( cx. typeck_results( ) . expr_ty_adjusted( greater) . kind( ) , ty:: Uint ( _) )
136
119
{
137
- return true ;
120
+ Some ( greater)
121
+ } else {
122
+ None
138
123
}
139
- false
140
124
}
141
125
142
- fn check_eq_expr ( cx : & LateContext < ' _ > , lhs : & Expr < ' _ > , rhs : & Expr < ' _ > ) -> bool {
143
- SpanlessEq :: new ( cx) . eq_expr ( lhs, rhs)
126
+ /// Return `v` if `expr` is `v & (v - 1)` or `(v - 1) & v`
127
+ fn is_and_minus_one < ' tcx > ( cx : & LateContext < ' tcx > , expr : & Expr < ' tcx > ) -> Option < & ' tcx Expr < ' tcx > > {
128
+ let ( lhs, rhs) = unexpanded_binop_operands ( expr, BinOpKind :: BitAnd ) ?;
129
+ is_one_less ( cx, lhs, rhs) . or_else ( || is_one_less ( cx, rhs, lhs) )
130
+ }
131
+
132
+ /// Return the operands of the `expr` binary operation if the operator is `op` and none of the
133
+ /// operands come from expansion.
134
+ fn unexpanded_binop_operands < ' hir > ( expr : & Expr < ' hir > , op : BinOpKind ) -> Option < ( & ' hir Expr < ' hir > , & ' hir Expr < ' hir > ) > {
135
+ if let ExprKind :: Binary ( binop, lhs, rhs) = expr. kind
136
+ && binop. node == op
137
+ && !lhs. span . from_expansion ( )
138
+ && !rhs. span . from_expansion ( )
139
+ {
140
+ Some ( ( lhs, rhs) )
141
+ } else {
142
+ None
143
+ }
144
144
}
0 commit comments