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
- const HARD_CODED_ALLOWED : & [ & str ] = & [ "std::num::Saturating" , "std::string::String" , "std::num::Wrapping" ] ;
16
+ const HARD_CODED_ALLOWED : & [ & str ] = & [
17
+ "f32" ,
18
+ "f64" ,
19
+ "std::num::Saturating" ,
20
+ "std::string::String" ,
21
+ "std::num::Wrapping" ,
22
+ ] ;
15
23
16
24
#[ derive( Debug ) ]
17
25
pub struct Arithmetic {
@@ -34,6 +42,27 @@ impl Arithmetic {
34
42
}
35
43
}
36
44
45
+ /// Checks assign operators (+=, -=, *=, /=) of integers in a non-constant environment that
46
+ /// won't overflow.
47
+ fn has_valid_assign_op ( op : & Spanned < hir:: BinOpKind > , rhs : & hir:: Expr < ' _ > , rhs_refs : Ty < ' _ > ) -> bool {
48
+ if !Self :: is_literal_integer ( rhs, rhs_refs) {
49
+ return false ;
50
+ }
51
+ if let hir:: BinOpKind :: Div | hir:: BinOpKind :: Mul = op. node
52
+ && let hir:: ExprKind :: Lit ( ref lit) = rhs. kind
53
+ && let ast:: LitKind :: Int ( 1 , _) = lit. node
54
+ {
55
+ return true ;
56
+ }
57
+ false
58
+ }
59
+
60
+ /// Checks "raw" binary operators (+, -, *, /) of integers in a non-constant environment
61
+ /// already handled by the CTFE.
62
+ fn has_valid_bin_op ( lhs : & hir:: Expr < ' _ > , lhs_refs : Ty < ' _ > , rhs : & hir:: Expr < ' _ > , rhs_refs : Ty < ' _ > ) -> bool {
63
+ Self :: is_literal_integer ( lhs, lhs_refs) && Self :: is_literal_integer ( rhs, rhs_refs)
64
+ }
65
+
37
66
/// Checks if the given `expr` has any of the inner `allowed` elements.
38
67
fn is_allowed_ty ( & self , cx : & LateContext < ' _ > , expr : & hir:: Expr < ' _ > ) -> bool {
39
68
self . allowed . contains (
@@ -46,40 +75,66 @@ impl Arithmetic {
46
75
)
47
76
}
48
77
78
+ /// Explicit integers like `1` or `i32::MAX`. Does not take into consideration references.
79
+ fn is_literal_integer ( expr : & hir:: Expr < ' _ > , expr_refs : Ty < ' _ > ) -> bool {
80
+ let is_integral = expr_refs. is_integral ( ) ;
81
+ let is_literal = matches ! ( expr. kind, hir:: ExprKind :: Lit ( _) ) ;
82
+ is_integral && is_literal
83
+ }
84
+
49
85
fn issue_lint ( & mut self , cx : & LateContext < ' _ > , expr : & hir:: Expr < ' _ > ) {
50
86
span_lint ( cx, ARITHMETIC , expr. span , "arithmetic detected" ) ;
51
87
self . expr_span = Some ( expr. span ) ;
52
88
}
89
+
90
+ /// Manages when the lint should be triggered. Operations in constant environments, hard coded
91
+ /// types, custom allowed types and non-constant operations that won't overflow are ignored.
92
+ fn manage_bin_ops (
93
+ & mut self ,
94
+ cx : & LateContext < ' _ > ,
95
+ expr : & hir:: Expr < ' _ > ,
96
+ op : & Spanned < hir:: BinOpKind > ,
97
+ lhs : & hir:: Expr < ' _ > ,
98
+ rhs : & hir:: Expr < ' _ > ,
99
+ ) {
100
+ if constant_simple ( cx, cx. typeck_results ( ) , expr) . is_some ( ) {
101
+ return ;
102
+ }
103
+ if !matches ! (
104
+ op. node,
105
+ hir:: BinOpKind :: Add
106
+ | hir:: BinOpKind :: Sub
107
+ | hir:: BinOpKind :: Mul
108
+ | hir:: BinOpKind :: Div
109
+ | hir:: BinOpKind :: Rem
110
+ | hir:: BinOpKind :: Shl
111
+ | hir:: BinOpKind :: Shr
112
+ ) {
113
+ return ;
114
+ } ;
115
+ if self . is_allowed_ty ( cx, lhs) || self . is_allowed_ty ( cx, rhs) {
116
+ return ;
117
+ }
118
+ let lhs_refs = cx. typeck_results ( ) . expr_ty ( lhs) . peel_refs ( ) ;
119
+ let rhs_refs = cx. typeck_results ( ) . expr_ty ( rhs) . peel_refs ( ) ;
120
+ let has_valid_assign_op = Self :: has_valid_assign_op ( op, rhs, rhs_refs) ;
121
+ if has_valid_assign_op || Self :: has_valid_bin_op ( lhs, lhs_refs, rhs, rhs_refs) {
122
+ return ;
123
+ }
124
+ self . issue_lint ( cx, expr) ;
125
+ }
53
126
}
54
127
55
128
impl < ' tcx > LateLintPass < ' tcx > for Arithmetic {
56
129
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 ) {
130
+ if self . expr_span . is_some ( ) || self . const_span . map_or ( false , |sp| sp. contains ( expr. span ) ) {
61
131
return ;
62
132
}
63
133
match & expr. kind {
64
134
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) ;
135
+ self . manage_bin_ops ( cx, expr, op, lhs, rhs) ;
80
136
} ,
81
137
hir:: ExprKind :: Unary ( hir:: UnOp :: Neg , _) => {
82
- // CTFE already takes care of things like `-1` that do not overflow.
83
138
if constant_simple ( cx, cx. typeck_results ( ) , expr) . is_none ( ) {
84
139
self . issue_lint ( cx, expr) ;
85
140
}
@@ -89,16 +144,15 @@ impl<'tcx> LateLintPass<'tcx> for Arithmetic {
89
144
}
90
145
91
146
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 => { } ,
147
+ let body_owner = cx. tcx . hir ( ) . body_owner ( body. id ( ) ) ;
148
+ let body_owner_def_id = cx. tcx . hir ( ) . local_def_id ( body_owner) ;
149
+ let body_owner_kind = cx. tcx . hir ( ) . body_owner_kind ( body_owner_def_id) ;
150
+ if let hir:: BodyOwnerKind :: Const | hir:: BodyOwnerKind :: Static ( _) = body_owner_kind {
151
+ let body_span = cx. tcx . hir ( ) . span_with_body ( body_owner) ;
152
+ if let Some ( span) = self . const_span && span. contains ( body_span) {
153
+ return ;
154
+ }
155
+ self . const_span = Some ( body_span) ;
102
156
}
103
157
}
104
158
0 commit comments