11use 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+ } ;
37use rustc_ast as ast;
48use rustc_data_structures:: fx:: FxHashSet ;
59use rustc_hir as hir;
610use rustc_lint:: { LateContext , LateLintPass } ;
711use rustc_middle:: ty:: Ty ;
812use 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+ } ;
1017
1118const HARD_CODED_ALLOWED : & [ & str ] = & [
1219 "&str" ,
@@ -38,24 +45,6 @@ impl ArithmeticSideEffects {
3845 }
3946 }
4047
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-
5948 /// Checks if the given `expr` has any of the inner `allowed` elements.
6049 fn is_allowed_ty ( & self , ty : Ty < ' _ > ) -> bool {
6150 self . allowed
@@ -74,15 +63,14 @@ impl ArithmeticSideEffects {
7463 self . expr_span = Some ( expr. span ) ;
7564 }
7665
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 )
8170 }
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
8473 }
85- None
8674 }
8775
8876 /// Manages when the lint should be triggered. Operations in constant environments, hard coded
@@ -117,10 +105,20 @@ impl ArithmeticSideEffects {
117105 return ;
118106 }
119107 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+ } ,
124122 }
125123 } else {
126124 false
@@ -129,21 +127,45 @@ impl ArithmeticSideEffects {
129127 self . issue_lint ( cx, expr) ;
130128 }
131129 }
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+ }
132156}
133157
134158impl < ' tcx > LateLintPass < ' tcx > for ArithmeticSideEffects {
135159 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) {
137161 return ;
138162 }
139163 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) => {
141165 self . manage_bin_ops ( cx, expr, op, lhs, rhs) ;
142166 } ,
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) ;
147169 } ,
148170 _ => { } ,
149171 }
@@ -177,22 +199,3 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
177199 }
178200 }
179201}
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