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;
@@ -38,24 +42,6 @@ impl ArithmeticSideEffects {
3842 }
3943 }
4044
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-
5945 /// Checks if the given `expr` has any of the inner `allowed` elements.
6046 fn is_allowed_ty ( & self , ty : Ty < ' _ > ) -> bool {
6147 self . allowed
@@ -74,15 +60,14 @@ impl ArithmeticSideEffects {
7460 self . expr_span = Some ( expr. span ) ;
7561 }
7662
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 )
8167 }
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
8470 }
85- None
8671 }
8772
8873 /// Manages when the lint should be triggered. Operations in constant environments, hard coded
@@ -117,10 +102,20 @@ impl ArithmeticSideEffects {
117102 return ;
118103 }
119104 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+ } ,
124119 }
125120 } else {
126121 false
@@ -129,21 +124,45 @@ impl ArithmeticSideEffects {
129124 self . issue_lint ( cx, expr) ;
130125 }
131126 }
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+ }
132153}
133154
134155impl < ' tcx > LateLintPass < ' tcx > for ArithmeticSideEffects {
135156 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) {
137158 return ;
138159 }
139160 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) => {
141162 self . manage_bin_ops ( cx, expr, op, lhs, rhs) ;
142163 } ,
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) ;
147166 } ,
148167 _ => { } ,
149168 }
@@ -177,22 +196,3 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
177196 }
178197 }
179198}
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