@@ -8,8 +8,9 @@ use clippy_utils::{consts::constant_simple, diagnostics::span_lint};
8
8
use rustc_data_structures:: fx:: FxHashSet ;
9
9
use rustc_hir as hir;
10
10
use rustc_lint:: { LateContext , LateLintPass } ;
11
+ use rustc_middle:: ty:: Ty ;
11
12
use rustc_session:: impl_lint_pass;
12
- use rustc_span:: source_map:: Span ;
13
+ use rustc_span:: source_map:: { Span , Spanned } ;
13
14
14
15
const HARD_CODED_ALLOWED : & [ & str ] = & [ "std::num::Saturating" , "std::string::String" , "std::num::Wrapping" ] ;
15
16
@@ -46,40 +47,65 @@ impl Arithmetic {
46
47
)
47
48
}
48
49
50
+ /// Explicit integers like `1` or `i32::MAX`. Does not take into consideration references.
51
+ fn is_literal_integer ( expr : & hir:: Expr < ' _ > , expr_refs : Ty < ' _ > ) -> bool {
52
+ let is_integral = expr_refs. is_integral ( ) ;
53
+ let is_literal = matches ! ( expr. kind, hir:: ExprKind :: Lit ( _) ) ;
54
+ is_integral && is_literal
55
+ }
56
+
49
57
fn issue_lint ( & mut self , cx : & LateContext < ' _ > , expr : & hir:: Expr < ' _ > ) {
50
58
span_lint ( cx, ARITHMETIC , expr. span , "arithmetic detected" ) ;
51
59
self . expr_span = Some ( expr. span ) ;
52
60
}
61
+
62
+ /// Manages when the lint should be triggered. Hard coded types, custom allowed types and
63
+ /// integer literals already handled by the CTFE are ignored.
64
+ ///
65
+ /// It is sufficient to only check if one of the left or right hand side types are integers
66
+ /// because the compiler will warn operations of incompatible types. Such behavior fits for
67
+ /// both "raw" (-, +, *) and assign (-=, +=, *=) binary operators.
68
+ fn manage_bin_ops (
69
+ & mut self ,
70
+ cx : & LateContext < ' _ > ,
71
+ expr : & hir:: Expr < ' _ > ,
72
+ op : & Spanned < hir:: BinOpKind > ,
73
+ lhs : & hir:: Expr < ' _ > ,
74
+ rhs : & hir:: Expr < ' _ > ,
75
+ ) {
76
+ let (
77
+ hir:: BinOpKind :: Add
78
+ | hir:: BinOpKind :: Sub
79
+ | hir:: BinOpKind :: Mul
80
+ | hir:: BinOpKind :: Div
81
+ | hir:: BinOpKind :: Rem
82
+ | hir:: BinOpKind :: Shl
83
+ | hir:: BinOpKind :: Shr
84
+ ) = op. node else {
85
+ return ;
86
+ } ;
87
+ if self . is_allowed_ty ( cx, lhs) || self . is_allowed_ty ( cx, rhs) {
88
+ return ;
89
+ }
90
+ let lhs_refs = cx. typeck_results ( ) . expr_ty ( lhs) . peel_refs ( ) ;
91
+ let rhs_refs = cx. typeck_results ( ) . expr_ty ( rhs) . peel_refs ( ) ;
92
+ if Self :: is_literal_integer ( lhs, lhs_refs) || Self :: is_literal_integer ( rhs, rhs_refs) {
93
+ return ;
94
+ }
95
+ self . issue_lint ( cx, expr) ;
96
+ }
53
97
}
54
98
55
99
impl < ' tcx > LateLintPass < ' tcx > for Arithmetic {
56
100
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 ) {
101
+ if self . expr_span . is_some ( ) || self . const_span . map_or ( false , |sp| sp. contains ( expr. span ) ) {
61
102
return ;
62
103
}
63
104
match & expr. kind {
64
105
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) ;
106
+ self . manage_bin_ops ( cx, expr, op, lhs, rhs) ;
80
107
} ,
81
108
hir:: ExprKind :: Unary ( hir:: UnOp :: Neg , _) => {
82
- // CTFE already takes care of things like `-1` that do not overflow.
83
109
if constant_simple ( cx, cx. typeck_results ( ) , expr) . is_none ( ) {
84
110
self . issue_lint ( cx, expr) ;
85
111
}
@@ -89,16 +115,15 @@ impl<'tcx> LateLintPass<'tcx> for Arithmetic {
89
115
}
90
116
91
117
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 => { } ,
118
+ let body_owner = cx. tcx . hir ( ) . body_owner ( body. id ( ) ) ;
119
+ let body_owner_def_id = cx. tcx . hir ( ) . local_def_id ( body_owner) ;
120
+ let body_owner_kind = cx. tcx . hir ( ) . body_owner_kind ( body_owner_def_id) ;
121
+ if let hir:: BodyOwnerKind :: Const | hir:: BodyOwnerKind :: Static ( _) = body_owner_kind {
122
+ let body_span = cx. tcx . hir ( ) . span_with_body ( body_owner) ;
123
+ if let Some ( span) = self . const_span && span. contains ( body_span) {
124
+ return ;
125
+ }
126
+ self . const_span = Some ( body_span) ;
102
127
}
103
128
}
104
129
0 commit comments