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
@@ -50,36 +52,83 @@ impl Arithmetic {
50
52
span_lint ( cx, ARITHMETIC , expr. span , "arithmetic detected" ) ;
51
53
self . expr_span = Some ( expr. span ) ;
52
54
}
55
+
56
+ /// Checks assign operators (+=, -=, *=, /=) already handled by the CTFE.
57
+ fn has_valid_assign_op ( op : & Spanned < hir:: BinOpKind > , rhs : & hir:: Expr < ' _ > ) -> bool {
58
+ match op. node {
59
+ hir:: BinOpKind :: Div | hir:: BinOpKind :: Rem => match rhs. kind {
60
+ hir:: ExprKind :: Lit ( _) => true ,
61
+ hir:: ExprKind :: Unary ( hir:: UnOp :: Neg , expr) => {
62
+ let hir:: ExprKind :: Lit ( lit) = & expr. kind else {
63
+ return true ;
64
+ } ;
65
+ if let ast:: LitKind :: Int ( 1 , _) = lit. node {
66
+ return false ;
67
+ }
68
+ true
69
+ } ,
70
+ _ => false ,
71
+ } ,
72
+ _ => false ,
73
+ }
74
+ }
75
+
76
+ /// Checks binary operators (+, -, *, /) already handled by the CTFE.
77
+ fn has_valid_op ( lhs : & hir:: Expr < ' _ > , lhs_refs : Ty < ' _ > , rhs : & hir:: Expr < ' _ > , rhs_refs : Ty < ' _ > ) -> bool {
78
+ let lhs_is_integral = lhs_refs. is_integral ( ) ;
79
+ let lhs_is_literal = matches ! ( lhs. kind, hir:: ExprKind :: Lit ( _) ) ;
80
+ let rhs_is_integral = rhs_refs. is_integral ( ) ;
81
+ let rhs_is_literal = matches ! ( rhs. kind, hir:: ExprKind :: Lit ( _) ) ;
82
+ lhs_is_integral && lhs_is_literal && rhs_is_integral && rhs_is_literal
83
+ }
84
+
85
+ /// Manages when the lint should be triggered. Literal integers are ignored because they are
86
+ /// already handled by the CTFE.
87
+ fn manage_bin_ops (
88
+ & mut self ,
89
+ cx : & LateContext < ' _ > ,
90
+ expr : & hir:: Expr < ' _ > ,
91
+ op : & Spanned < hir:: BinOpKind > ,
92
+ lhs : & hir:: Expr < ' _ > ,
93
+ rhs : & hir:: Expr < ' _ > ,
94
+ ) {
95
+ let (
96
+ hir:: BinOpKind :: Add
97
+ | hir:: BinOpKind :: Sub
98
+ | hir:: BinOpKind :: Mul
99
+ | hir:: BinOpKind :: Div
100
+ | hir:: BinOpKind :: Rem
101
+ | hir:: BinOpKind :: Shl
102
+ | hir:: BinOpKind :: Shr
103
+ ) = op. node else {
104
+ return ;
105
+ } ;
106
+ if self . is_allowed_ty ( cx, lhs) || self . is_allowed_ty ( cx, rhs) {
107
+ return ;
108
+ }
109
+ let lhs_refs = cx. typeck_results ( ) . expr_ty ( lhs) . peel_refs ( ) ;
110
+ let rhs_refs = cx. typeck_results ( ) . expr_ty ( rhs) . peel_refs ( ) ;
111
+ if lhs_refs. is_floating_point ( ) && rhs_refs. is_floating_point ( ) {
112
+ self . issue_lint ( cx, expr) ;
113
+ return ;
114
+ }
115
+ if Self :: has_valid_assign_op ( op, rhs) || Self :: has_valid_op ( lhs, lhs_refs, rhs, rhs_refs) {
116
+ return ;
117
+ }
118
+ self . issue_lint ( cx, expr) ;
119
+ }
53
120
}
54
121
55
122
impl < ' tcx > LateLintPass < ' tcx > for Arithmetic {
56
123
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 ) {
124
+ if self . expr_span . is_some ( ) || self . const_span . map_or ( false , |sp| sp. contains ( expr. span ) ) {
61
125
return ;
62
126
}
63
127
match & expr. kind {
64
128
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) ;
129
+ self . manage_bin_ops ( cx, expr, op, lhs, rhs)
80
130
} ,
81
131
hir:: ExprKind :: Unary ( hir:: UnOp :: Neg , _) => {
82
- // CTFE already takes care of things like `-1` that do not overflow.
83
132
if constant_simple ( cx, cx. typeck_results ( ) , expr) . is_none ( ) {
84
133
self . issue_lint ( cx, expr) ;
85
134
}
@@ -89,16 +138,15 @@ impl<'tcx> LateLintPass<'tcx> for Arithmetic {
89
138
}
90
139
91
140
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 => { } ,
141
+ let body_owner = cx. tcx . hir ( ) . body_owner ( body. id ( ) ) ;
142
+ let body_owner_def_id = cx. tcx . hir ( ) . local_def_id ( body_owner) ;
143
+ let body_owner_kind = cx. tcx . hir ( ) . body_owner_kind ( body_owner_def_id) ;
144
+ if let hir:: BodyOwnerKind :: Const | hir:: BodyOwnerKind :: Static ( _) = body_owner_kind {
145
+ let body_span = cx. tcx . hir ( ) . span_with_body ( body_owner) ;
146
+ if let Some ( span) = self . const_span && span. contains ( body_span) {
147
+ return ;
148
+ }
149
+ self . const_span = Some ( body_span) ;
102
150
}
103
151
}
104
152
0 commit comments