5
5
6
6
use super :: ARITHMETIC ;
7
7
use clippy_utils:: { consts:: constant_simple, diagnostics:: span_lint} ;
8
+ use rustc_ast as ast;
8
9
use rustc_data_structures:: fx:: FxHashSet ;
9
10
use rustc_hir as hir;
10
11
use rustc_lint:: { LateContext , LateLintPass } ;
12
+ use rustc_middle:: ty:: Ty ;
11
13
use rustc_session:: impl_lint_pass;
12
- use rustc_span:: source_map:: Span ;
14
+ use rustc_span:: source_map:: { Span , Spanned } ;
13
15
14
16
const HARD_CODED_ALLOWED : & [ & str ] = & [ "std::num::Saturating" , "std::string::String" , "std::num::Wrapping" ] ;
15
17
@@ -34,6 +36,29 @@ impl Arithmetic {
34
36
}
35
37
}
36
38
39
+ /// Checks assign operators (+=, -=, *=, /=) of integers in a non-constant environment that
40
+ /// won't overflow.
41
+ fn has_valid_assign_op ( op : & Spanned < hir:: BinOpKind > , rhs : & hir:: Expr < ' _ > , rhs_refs : Ty < ' _ > ) -> bool {
42
+ if !Self :: is_literal_integer ( rhs, rhs_refs) {
43
+ return false ;
44
+ }
45
+ if let hir:: BinOpKind :: Div | hir:: BinOpKind :: Mul = op. node {
46
+ match rhs. kind {
47
+ hir:: ExprKind :: Lit ( ref lit) if let ast:: LitKind :: Int ( 1 , _) = lit. node => {
48
+ return true ;
49
+ }
50
+ _ => { }
51
+ }
52
+ }
53
+ false
54
+ }
55
+
56
+ /// Checks "raw" binary operators (+, -, *, /) of integers in a non-constant environment
57
+ /// already handled by the CTFE.
58
+ fn has_valid_bin_op ( lhs : & hir:: Expr < ' _ > , lhs_refs : Ty < ' _ > , rhs : & hir:: Expr < ' _ > , rhs_refs : Ty < ' _ > ) -> bool {
59
+ Self :: is_literal_integer ( lhs, lhs_refs) && Self :: is_literal_integer ( rhs, rhs_refs)
60
+ }
61
+
37
62
/// Checks if the given `expr` has any of the inner `allowed` elements.
38
63
fn is_allowed_ty ( & self , cx : & LateContext < ' _ > , expr : & hir:: Expr < ' _ > ) -> bool {
39
64
self . allowed . contains (
@@ -46,40 +71,65 @@ impl Arithmetic {
46
71
)
47
72
}
48
73
74
+ /// Explicit integers like `1` or `i32::MAX`. Does not take into consideration references.
75
+ fn is_literal_integer ( expr : & hir:: Expr < ' _ > , expr_refs : Ty < ' _ > ) -> bool {
76
+ let is_integral = expr_refs. is_integral ( ) ;
77
+ let is_literal = matches ! ( expr. kind, hir:: ExprKind :: Lit ( _) ) ;
78
+ is_integral && is_literal
79
+ }
80
+
49
81
fn issue_lint ( & mut self , cx : & LateContext < ' _ > , expr : & hir:: Expr < ' _ > ) {
50
82
span_lint ( cx, ARITHMETIC , expr. span , "arithmetic detected" ) ;
51
83
self . expr_span = Some ( expr. span ) ;
52
84
}
85
+
86
+ /// Manages when the lint should be triggered. Operations in constant environments, hard coded
87
+ /// types, custom allowed types and non-constant operations that won't overflow are ignored.
88
+ fn manage_bin_ops (
89
+ & mut self ,
90
+ cx : & LateContext < ' _ > ,
91
+ expr : & hir:: Expr < ' _ > ,
92
+ op : & Spanned < hir:: BinOpKind > ,
93
+ lhs : & hir:: Expr < ' _ > ,
94
+ rhs : & hir:: Expr < ' _ > ,
95
+ ) {
96
+ if constant_simple ( cx, cx. typeck_results ( ) , expr) . is_some ( ) {
97
+ return ;
98
+ }
99
+ let (
100
+ hir:: BinOpKind :: Add
101
+ | hir:: BinOpKind :: Sub
102
+ | hir:: BinOpKind :: Mul
103
+ | hir:: BinOpKind :: Div
104
+ | hir:: BinOpKind :: Rem
105
+ | hir:: BinOpKind :: Shl
106
+ | hir:: BinOpKind :: Shr
107
+ ) = op. node else {
108
+ return ;
109
+ } ;
110
+ if self . is_allowed_ty ( cx, lhs) || self . is_allowed_ty ( cx, rhs) {
111
+ return ;
112
+ }
113
+ let lhs_refs = cx. typeck_results ( ) . expr_ty ( lhs) . peel_refs ( ) ;
114
+ let rhs_refs = cx. typeck_results ( ) . expr_ty ( rhs) . peel_refs ( ) ;
115
+ let has_valid_assign_op = Self :: has_valid_assign_op ( op, rhs, rhs_refs) ;
116
+ if has_valid_assign_op || Self :: has_valid_bin_op ( lhs, lhs_refs, rhs, rhs_refs) {
117
+ return ;
118
+ }
119
+ self . issue_lint ( cx, expr) ;
120
+ }
53
121
}
54
122
55
123
impl < ' tcx > LateLintPass < ' tcx > for Arithmetic {
56
124
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx hir:: Expr < ' _ > ) {
57
- if self . expr_span . is_some ( ) {
58
- return ;
59
- }
60
- if let Some ( span) = self . const_span && span. contains ( expr. span ) {
125
+ if self . expr_span . is_some ( ) || self . const_span . map_or ( false , |sp| sp. contains ( expr. span ) ) {
61
126
return ;
62
127
}
63
128
match & expr. kind {
64
129
hir:: ExprKind :: Binary ( op, lhs, rhs) | hir:: ExprKind :: AssignOp ( op, lhs, rhs) => {
65
- let (
66
- hir:: BinOpKind :: Add
67
- | hir:: BinOpKind :: Sub
68
- | hir:: BinOpKind :: Mul
69
- | hir:: BinOpKind :: Div
70
- | hir:: BinOpKind :: Rem
71
- | hir:: BinOpKind :: Shl
72
- | hir:: BinOpKind :: Shr
73
- ) = op. node else {
74
- return ;
75
- } ;
76
- if self . is_allowed_ty ( cx, lhs) || self . is_allowed_ty ( cx, rhs) {
77
- return ;
78
- }
79
- self . issue_lint ( cx, expr) ;
130
+ self . manage_bin_ops ( cx, expr, op, lhs, rhs) ;
80
131
} ,
81
132
hir:: ExprKind :: Unary ( hir:: UnOp :: Neg , _) => {
82
- // CTFE already takes care of things like `-1` that do not overflow.
83
133
if constant_simple ( cx, cx. typeck_results ( ) , expr) . is_none ( ) {
84
134
self . issue_lint ( cx, expr) ;
85
135
}
@@ -89,16 +139,15 @@ impl<'tcx> LateLintPass<'tcx> for Arithmetic {
89
139
}
90
140
91
141
fn check_body ( & mut self , cx : & LateContext < ' _ > , body : & hir:: Body < ' _ > ) {
92
- let body_owner = cx. tcx . hir ( ) . body_owner_def_id ( body. id ( ) ) ;
93
- match cx. tcx . hir ( ) . body_owner_kind ( body_owner) {
94
- hir:: BodyOwnerKind :: Const | hir:: BodyOwnerKind :: Static ( _) => {
95
- let body_span = cx. tcx . def_span ( body_owner) ;
96
- if let Some ( span) = self . const_span && span. contains ( body_span) {
97
- return ;
98
- }
99
- self . const_span = Some ( body_span) ;
100
- } ,
101
- hir:: BodyOwnerKind :: Closure | hir:: BodyOwnerKind :: Fn => { } ,
142
+ let body_owner = cx. tcx . hir ( ) . body_owner ( body. id ( ) ) ;
143
+ let body_owner_def_id = cx. tcx . hir ( ) . local_def_id ( body_owner) ;
144
+ let body_owner_kind = cx. tcx . hir ( ) . body_owner_kind ( body_owner_def_id) ;
145
+ if let hir:: BodyOwnerKind :: Const | hir:: BodyOwnerKind :: Static ( _) = body_owner_kind {
146
+ let body_span = cx. tcx . hir ( ) . span_with_body ( body_owner) ;
147
+ if let Some ( span) = self . const_span && span. contains ( body_span) {
148
+ return ;
149
+ }
150
+ self . const_span = Some ( body_span) ;
102
151
}
103
152
}
104
153
0 commit comments