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;
6
10
use rustc_lint:: { LateContext , LateLintPass } ;
7
11
use rustc_middle:: ty:: Ty ;
8
12
use rustc_session:: impl_lint_pass;
9
- use rustc_span:: source_map:: { Span , Spanned } ;
13
+ use rustc_span:: {
14
+ source_map:: { Span , Spanned } ,
15
+ sym,
16
+ } ;
10
17
11
18
const HARD_CODED_ALLOWED : & [ & str ] = & [
12
19
"&str" ,
@@ -38,24 +45,6 @@ impl ArithmeticSideEffects {
38
45
}
39
46
}
40
47
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
48
/// Checks if the given `expr` has any of the inner `allowed` elements.
60
49
fn is_allowed_ty ( & self , ty : Ty < ' _ > ) -> bool {
61
50
self . allowed
@@ -74,15 +63,14 @@ impl ArithmeticSideEffects {
74
63
self . expr_span = Some ( expr. span ) ;
75
64
}
76
65
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 ) ) ;
66
+ /// If `expr` is not a literal integer like `1 `, returns `None`.
67
+ fn literal_integer ( expr : & hir:: Expr < ' _ > ) -> Option < u128 > {
68
+ if let hir :: ExprKind :: Lit ( ref lit ) = expr. kind && let ast :: LitKind :: Int ( n , _ ) = lit . node {
69
+ Some ( n )
81
70
}
82
- if let hir :: ExprKind :: AddrOf ( .. , inn ) = expr . kind && let hir :: ExprKind :: Lit ( _ ) = inn . kind {
83
- return Some ( LiteralIntegerTy :: Ref ( inn ) ) ;
71
+ else {
72
+ None
84
73
}
85
- None
86
74
}
87
75
88
76
/// Manages when the lint should be triggered. Operations in constant environments, hard coded
@@ -117,10 +105,20 @@ impl ArithmeticSideEffects {
117
105
return ;
118
106
}
119
107
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 ,
108
+ let ( actual_lhs, lhs_ref_counter) = peel_hir_expr_refs ( lhs) ;
109
+ let ( actual_rhs, rhs_ref_counter) = peel_hir_expr_refs ( rhs) ;
110
+ match ( Self :: literal_integer ( actual_lhs) , Self :: literal_integer ( actual_rhs) ) {
111
+ ( None , None ) => false ,
112
+ ( None , Some ( n) ) | ( Some ( n) , None ) => match ( & op. node , n) {
113
+ ( hir:: BinOpKind :: Div | hir:: BinOpKind :: Rem , 0 ) => false ,
114
+ ( hir:: BinOpKind :: Add | hir:: BinOpKind :: Sub , 0 )
115
+ | ( hir:: BinOpKind :: Div | hir:: BinOpKind :: Rem , _)
116
+ | ( hir:: BinOpKind :: Mul , 0 | 1 ) => true ,
117
+ _ => false ,
118
+ } ,
119
+ ( Some ( _) , Some ( _) ) => {
120
+ matches ! ( ( lhs_ref_counter, rhs_ref_counter) , ( 0 , 0 ) )
121
+ } ,
124
122
}
125
123
} else {
126
124
false
@@ -129,21 +127,45 @@ impl ArithmeticSideEffects {
129
127
self . issue_lint ( cx, expr) ;
130
128
}
131
129
}
130
+
131
+ fn manage_unary_ops < ' tcx > (
132
+ & mut self ,
133
+ cx : & LateContext < ' tcx > ,
134
+ expr : & hir:: Expr < ' tcx > ,
135
+ un_expr : & hir:: Expr < ' tcx > ,
136
+ un_op : hir:: UnOp ,
137
+ ) {
138
+ let hir:: UnOp :: Neg = un_op else { return ; } ;
139
+ if constant ( cx, cx. typeck_results ( ) , un_expr) . is_some ( ) {
140
+ return ;
141
+ }
142
+ let ty = cx. typeck_results ( ) . expr_ty ( expr) . peel_refs ( ) ;
143
+ if self . is_allowed_ty ( ty) {
144
+ return ;
145
+ }
146
+ let actual_un_expr = peel_hir_expr_refs ( un_expr) . 0 ;
147
+ if Self :: literal_integer ( actual_un_expr) . is_some ( ) {
148
+ return ;
149
+ }
150
+ self . issue_lint ( cx, expr) ;
151
+ }
152
+
153
+ fn should_skip_expr ( & mut self , expr : & hir:: Expr < ' _ > ) -> bool {
154
+ self . expr_span . is_some ( ) || self . const_span . map_or ( false , |sp| sp. contains ( expr. span ) )
155
+ }
132
156
}
133
157
134
158
impl < ' tcx > LateLintPass < ' tcx > for ArithmeticSideEffects {
135
159
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 ) ) {
160
+ if self . should_skip_expr ( expr) {
137
161
return ;
138
162
}
139
163
match & expr. kind {
140
- hir:: ExprKind :: Binary ( op, lhs, rhs) | hir:: ExprKind :: AssignOp ( op, lhs, rhs) => {
164
+ hir:: ExprKind :: AssignOp ( op, lhs, rhs) | hir:: ExprKind :: Binary ( op, lhs, rhs) => {
141
165
self . manage_bin_ops ( cx, expr, op, lhs, rhs) ;
142
166
} ,
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
- }
167
+ hir:: ExprKind :: Unary ( un_op, un_expr) => {
168
+ self . manage_unary_ops ( cx, expr, un_expr, * un_op) ;
147
169
} ,
148
170
_ => { } ,
149
171
}
@@ -177,22 +199,3 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
177
199
}
178
200
}
179
201
}
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