1
1
use super :: ARITHMETIC_SIDE_EFFECTS ;
2
- use clippy_utils:: { consts:: constant_simple, diagnostics:: span_lint} ;
2
+ use clippy_utils:: {
3
+ consts:: { constant, constant_simple} ,
4
+ diagnostics:: span_lint,
5
+ peel_hir_expr_refs,
6
+ } ;
3
7
use rustc_ast as ast;
4
8
use rustc_data_structures:: fx:: FxHashSet ;
5
9
use rustc_hir as hir;
@@ -38,24 +42,6 @@ impl ArithmeticSideEffects {
38
42
}
39
43
}
40
44
41
- /// Assuming that `expr` is a literal integer, checks operators (+=, -=, *, /) in a
42
- /// non-constant environment that won't overflow.
43
- fn has_valid_op ( op : & Spanned < hir:: BinOpKind > , expr : & hir:: Expr < ' _ > ) -> bool {
44
- if let hir:: ExprKind :: Lit ( ref lit) = expr. kind &&
45
- let ast:: LitKind :: Int ( value, _) = lit. node
46
- {
47
- match ( & op. node , value) {
48
- ( hir:: BinOpKind :: Div | hir:: BinOpKind :: Rem , 0 ) => false ,
49
- ( hir:: BinOpKind :: Add | hir:: BinOpKind :: Sub , 0 )
50
- | ( hir:: BinOpKind :: Div | hir:: BinOpKind :: Rem , _)
51
- | ( hir:: BinOpKind :: Mul , 0 | 1 ) => true ,
52
- _ => false ,
53
- }
54
- } else {
55
- false
56
- }
57
- }
58
-
59
45
/// Checks if the given `expr` has any of the inner `allowed` elements.
60
46
fn is_allowed_ty ( & self , ty : Ty < ' _ > ) -> bool {
61
47
self . allowed
@@ -74,15 +60,14 @@ impl ArithmeticSideEffects {
74
60
self . expr_span = Some ( expr. span ) ;
75
61
}
76
62
77
- /// If `expr` does not match any variant of `LiteralIntegerTy `, returns `None`.
78
- fn literal_integer < ' expr , ' tcx > ( expr : & ' expr hir:: Expr < ' tcx > ) -> Option < LiteralIntegerTy < ' expr , ' tcx > > {
79
- if matches ! ( expr. kind, hir :: ExprKind :: Lit ( _ ) ) {
80
- return Some ( LiteralIntegerTy :: Value ( expr ) ) ;
63
+ /// If `expr` is not a literal integer like `1 `, returns `None`.
64
+ fn literal_integer ( expr : & hir:: Expr < ' _ > ) -> Option < u128 > {
65
+ if let hir :: ExprKind :: Lit ( ref lit ) = expr. kind && let ast :: LitKind :: Int ( n , _ ) = lit . node {
66
+ Some ( n )
81
67
}
82
- if let hir :: ExprKind :: AddrOf ( .. , inn ) = expr . kind && let hir :: ExprKind :: Lit ( _ ) = inn . kind {
83
- return Some ( LiteralIntegerTy :: Ref ( inn ) ) ;
68
+ else {
69
+ None
84
70
}
85
- None
86
71
}
87
72
88
73
/// Manages when the lint should be triggered. Operations in constant environments, hard coded
@@ -117,10 +102,20 @@ impl ArithmeticSideEffects {
117
102
return ;
118
103
}
119
104
let has_valid_op = if Self :: is_integral ( lhs_ty) && Self :: is_integral ( rhs_ty) {
120
- match ( Self :: literal_integer ( lhs) , Self :: literal_integer ( rhs) ) {
121
- ( None , Some ( lit_int_ty) ) | ( Some ( lit_int_ty) , None ) => Self :: has_valid_op ( op, lit_int_ty. into ( ) ) ,
122
- ( Some ( LiteralIntegerTy :: Value ( _) ) , Some ( LiteralIntegerTy :: Value ( _) ) ) => true ,
123
- ( None , None ) | ( Some ( _) , Some ( _) ) => false ,
105
+ let ( actual_lhs, lhs_ref_counter) = peel_hir_expr_refs ( lhs) ;
106
+ let ( actual_rhs, rhs_ref_counter) = peel_hir_expr_refs ( rhs) ;
107
+ match ( Self :: literal_integer ( actual_lhs) , Self :: literal_integer ( actual_rhs) ) {
108
+ ( None , None ) => false ,
109
+ ( None , Some ( n) ) | ( Some ( n) , None ) => match ( & op. node , n) {
110
+ ( hir:: BinOpKind :: Div | hir:: BinOpKind :: Rem , 0 ) => false ,
111
+ ( hir:: BinOpKind :: Add | hir:: BinOpKind :: Sub , 0 )
112
+ | ( hir:: BinOpKind :: Div | hir:: BinOpKind :: Rem , _)
113
+ | ( hir:: BinOpKind :: Mul , 0 | 1 ) => true ,
114
+ _ => false ,
115
+ } ,
116
+ ( Some ( _) , Some ( _) ) => {
117
+ matches ! ( ( lhs_ref_counter, rhs_ref_counter) , ( 0 , 0 ) )
118
+ } ,
124
119
}
125
120
} else {
126
121
false
@@ -129,21 +124,45 @@ impl ArithmeticSideEffects {
129
124
self . issue_lint ( cx, expr) ;
130
125
}
131
126
}
127
+
128
+ fn manage_unary_ops < ' tcx > (
129
+ & mut self ,
130
+ cx : & LateContext < ' tcx > ,
131
+ expr : & hir:: Expr < ' tcx > ,
132
+ un_expr : & hir:: Expr < ' tcx > ,
133
+ un_op : hir:: UnOp ,
134
+ ) {
135
+ let hir:: UnOp :: Neg = un_op else { return ; } ;
136
+ if constant ( cx, cx. typeck_results ( ) , un_expr) . is_some ( ) {
137
+ return ;
138
+ }
139
+ let ty = cx. typeck_results ( ) . expr_ty ( expr) . peel_refs ( ) ;
140
+ if self . is_allowed_ty ( ty) {
141
+ return ;
142
+ }
143
+ let actual_un_expr = peel_hir_expr_refs ( un_expr) . 0 ;
144
+ if Self :: literal_integer ( actual_un_expr) . is_some ( ) {
145
+ return ;
146
+ }
147
+ self . issue_lint ( cx, expr) ;
148
+ }
149
+
150
+ fn should_skip_expr ( & mut self , expr : & hir:: Expr < ' _ > ) -> bool {
151
+ self . expr_span . is_some ( ) || self . const_span . map_or ( false , |sp| sp. contains ( expr. span ) )
152
+ }
132
153
}
133
154
134
155
impl < ' tcx > LateLintPass < ' tcx > for ArithmeticSideEffects {
135
156
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & hir:: Expr < ' tcx > ) {
136
- if self . expr_span . is_some ( ) || self . const_span . map_or ( false , |sp| sp . contains ( expr. span ) ) {
157
+ if self . should_skip_expr ( expr) {
137
158
return ;
138
159
}
139
160
match & expr. kind {
140
- hir:: ExprKind :: Binary ( op, lhs, rhs) | hir:: ExprKind :: AssignOp ( op, lhs, rhs) => {
161
+ hir:: ExprKind :: AssignOp ( op, lhs, rhs) | hir:: ExprKind :: Binary ( op, lhs, rhs) => {
141
162
self . manage_bin_ops ( cx, expr, op, lhs, rhs) ;
142
163
} ,
143
- hir:: ExprKind :: Unary ( hir:: UnOp :: Neg , _) => {
144
- if constant_simple ( cx, cx. typeck_results ( ) , expr) . is_none ( ) {
145
- self . issue_lint ( cx, expr) ;
146
- }
164
+ hir:: ExprKind :: Unary ( un_op, un_expr) => {
165
+ self . manage_unary_ops ( cx, expr, un_expr, * un_op) ;
147
166
} ,
148
167
_ => { } ,
149
168
}
@@ -177,22 +196,3 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
177
196
}
178
197
}
179
198
}
180
-
181
- /// Tells if an expression is a integer declared by value or by reference.
182
- ///
183
- /// If `LiteralIntegerTy::Ref`, then the contained value will be `hir::ExprKind::Lit` rather
184
- /// than `hirExprKind::Addr`.
185
- enum LiteralIntegerTy < ' expr , ' tcx > {
186
- /// For example, `&199`
187
- Ref ( & ' expr hir:: Expr < ' tcx > ) ,
188
- /// For example, `1` or `i32::MAX`
189
- Value ( & ' expr hir:: Expr < ' tcx > ) ,
190
- }
191
-
192
- impl < ' expr , ' tcx > From < LiteralIntegerTy < ' expr , ' tcx > > for & ' expr hir:: Expr < ' tcx > {
193
- fn from ( from : LiteralIntegerTy < ' expr , ' tcx > ) -> Self {
194
- match from {
195
- LiteralIntegerTy :: Ref ( elem) | LiteralIntegerTy :: Value ( elem) => elem,
196
- }
197
- }
198
- }
0 commit comments