1- use clippy_utils :: SpanlessEq ;
1+ use clippy_config :: Conf ;
22use 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} ;
67use rustc_errors:: Applicability ;
7- use rustc_hir:: { BinOpKind , Expr , ExprKind } ;
8+ use rustc_hir:: { BinOpKind , Expr , ExprKind , QPath } ;
89use 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 ;
1112
1213declare_clippy_lint ! {
1314 /// ### What it does
@@ -33,112 +34,111 @@ declare_clippy_lint! {
3334 "manually reimplementing `is_power_of_two`"
3435}
3536
36- declare_lint_pass ! ( ManualIsPowerOfTwo => [ MANUAL_IS_POWER_OF_TWO ] ) ;
37+ pub struct ManualIsPowerOfTwo {
38+ msrv : Msrv ,
39+ }
3740
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 ] ) ;
4142
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+ }
5347
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+ }
6252
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) ;
7555
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+ }
8867
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 )
9875 {
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)
11187 {
112- build_sugg ( cx, expr, right1 , & mut applicability ) ;
88+ self . build_sugg ( cx, expr, a ) ;
11389 }
11490 }
11591 }
11692}
11793
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)
130107}
131108
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 ( _) )
136119 {
137- return true ;
120+ Some ( greater)
121+ } else {
122+ None
138123 }
139- false
140124}
141125
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+ }
144144}
0 commit comments