1
1
use clippy_config:: Conf ;
2
- use clippy_utils:: diagnostics:: span_lint_and_sugg ;
2
+ use clippy_utils:: diagnostics:: span_lint ;
3
3
use clippy_utils:: msrvs:: { self , Msrv } ;
4
- use clippy_utils:: source:: snippet_with_applicability;
5
- use clippy_utils:: sugg:: Sugg ;
4
+
6
5
use clippy_utils:: { is_mutable, path_to_local} ;
7
6
use rustc_ast:: { BinOpKind , LitIntType , LitKind } ;
8
7
use rustc_data_structures:: packed:: Pu128 ;
9
- use rustc_errors:: Applicability ;
10
- use rustc_hir:: intravisit:: { Visitor , walk_expr, walk_stmt} ;
11
- use rustc_hir:: { Expr , ExprKind , PatKind , QPath } ;
8
+ use rustc_hir:: intravisit:: { Visitor , walk_expr} ;
9
+ use rustc_hir:: { Expr , ExprKind } ;
12
10
use rustc_lint:: { LateContext , LateLintPass } ;
13
11
use rustc_middle:: ty:: { self } ;
14
12
use rustc_session:: impl_lint_pass;
15
- use std:: fmt:: Write ;
16
13
17
14
declare_clippy_lint ! {
18
15
/// ### What it does
@@ -79,12 +76,16 @@ impl<'tcx> LateLintPass<'tcx> for ManualCheckedSub {
79
76
return ;
80
77
}
81
78
79
+ // Skip if either lhs or rhs is a macro call
80
+ if lhs. span . from_expansion ( ) || rhs. span . from_expansion ( ) {
81
+ return ;
82
+ }
83
+
82
84
if let BinOpKind :: Ge | BinOpKind :: Gt | BinOpKind :: Le | BinOpKind :: Lt = op. node {
83
85
SubExprVisitor {
84
86
cx,
85
87
if_expr : expr,
86
- if_body_block : if_expr. then ,
87
- else_block : if_expr. r#else ,
88
+
88
89
condition_lhs : lhs,
89
90
condition_rhs : rhs,
90
91
condition_op : op. node ,
@@ -98,8 +99,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualCheckedSub {
98
99
struct SubExprVisitor < ' cx , ' tcx > {
99
100
cx : & ' cx LateContext < ' tcx > ,
100
101
if_expr : & ' tcx Expr < ' tcx > ,
101
- if_body_block : & ' tcx Expr < ' tcx > ,
102
- else_block : Option < & ' tcx Expr < ' tcx > > ,
102
+
103
103
condition_lhs : & ' tcx Expr < ' tcx > ,
104
104
condition_rhs : & ' tcx Expr < ' tcx > ,
105
105
condition_op : BinOpKind ,
@@ -110,20 +110,25 @@ impl<'tcx> Visitor<'tcx> for SubExprVisitor<'_, 'tcx> {
110
110
if let ExprKind :: Binary ( op, sub_lhs, sub_rhs) = expr. kind
111
111
&& let BinOpKind :: Sub = op. node
112
112
{
113
+ // // Skip if either sub_lhs or sub_rhs is a macro call
114
+ if sub_lhs. span . from_expansion ( ) || sub_rhs. span . from_expansion ( ) {
115
+ return ;
116
+ }
117
+
113
118
if let ExprKind :: Lit ( lit) = self . condition_lhs . kind
114
119
&& self . condition_op == BinOpKind :: Lt
115
120
&& let LitKind :: Int ( Pu128 ( 0 ) , _) = lit. node
116
121
&& ( is_referencing_same_variable ( sub_lhs, self . condition_rhs ) )
117
122
{
118
- self . emit_lint ( expr , Some ( sub_rhs ) ) ;
123
+ self . emit_lint ( ) ;
119
124
}
120
125
121
126
if let ExprKind :: Lit ( lit) = self . condition_rhs . kind
122
127
&& self . condition_op == BinOpKind :: Gt
123
128
&& let LitKind :: Int ( Pu128 ( 0 ) , _) = lit. node
124
129
&& ( is_referencing_same_variable ( sub_lhs, self . condition_lhs ) )
125
130
{
126
- self . emit_lint ( expr , Some ( sub_rhs ) ) ;
131
+ self . emit_lint ( ) ;
127
132
}
128
133
129
134
if self . condition_op == BinOpKind :: Ge
@@ -132,7 +137,7 @@ impl<'tcx> Visitor<'tcx> for SubExprVisitor<'_, 'tcx> {
132
137
&& ( is_referencing_same_variable ( sub_rhs, self . condition_rhs )
133
138
|| are_literals_equal ( sub_rhs, self . condition_rhs ) )
134
139
{
135
- self . emit_lint ( expr , None ) ;
140
+ self . emit_lint ( ) ;
136
141
}
137
142
138
143
if self . condition_op == BinOpKind :: Le
@@ -141,7 +146,7 @@ impl<'tcx> Visitor<'tcx> for SubExprVisitor<'_, 'tcx> {
141
146
&& ( is_referencing_same_variable ( sub_rhs, self . condition_lhs )
142
147
|| are_literals_equal ( sub_rhs, self . condition_lhs ) )
143
148
{
144
- self . emit_lint ( expr , None ) ;
149
+ self . emit_lint ( ) ;
145
150
}
146
151
}
147
152
@@ -150,62 +155,12 @@ impl<'tcx> Visitor<'tcx> for SubExprVisitor<'_, 'tcx> {
150
155
}
151
156
152
157
impl < ' tcx > SubExprVisitor < ' _ , ' tcx > {
153
- fn emit_lint ( & mut self , expr : & Expr < ' tcx > , sub_rhs_expr : Option < & ' tcx Expr < ' tcx > > ) {
154
- let mut applicability = Applicability :: MachineApplicable ;
155
-
156
- let body_snippet = snippet_with_applicability ( self . cx , self . if_body_block . span , ".." , & mut applicability) ;
157
-
158
- let checked_sub_result_var_name = match self . condition_op {
159
- BinOpKind :: Lt | BinOpKind :: Gt => generate_unique_var_name ( "decremented" , self . if_body_block ) ,
160
- _ => generate_unique_var_name ( "difference" , self . if_body_block ) ,
161
- } ;
162
-
163
- let binary_expr_snippet = snippet_with_applicability ( self . cx , expr. span , ".." , & mut applicability) ;
164
- let updated_usage_context_snippet = body_snippet
165
- . as_ref ( )
166
- . replace ( binary_expr_snippet. as_ref ( ) , & checked_sub_result_var_name) ;
167
-
168
- let lhs_snippet = Sugg :: hir ( self . cx , self . condition_lhs , ".." ) . maybe_paren ( ) ;
169
-
170
- let rhs_snippet = match sub_rhs_expr {
171
- Some ( sub_rhs) => Sugg :: hir ( self . cx , sub_rhs, ".." ) . maybe_paren ( ) ,
172
- None => Sugg :: hir ( self . cx , self . condition_rhs , ".." ) . maybe_paren ( ) ,
173
- } ;
174
-
175
- // TODO: Check result variable is not already in scope
176
- let mut suggestion = match self . condition_op {
177
- BinOpKind :: Le => {
178
- format ! (
179
- "if let Some({checked_sub_result_var_name}) = {rhs_snippet}.checked_sub({lhs_snippet}) {updated_usage_context_snippet}"
180
- )
181
- } ,
182
-
183
- BinOpKind :: Lt => {
184
- format ! (
185
- "if let Some({checked_sub_result_var_name}) = {}.checked_sub({rhs_snippet}) {updated_usage_context_snippet}" ,
186
- Sugg :: hir( self . cx, self . condition_rhs, ".." ) . maybe_paren( )
187
- )
188
- } ,
189
- _ => {
190
- format ! (
191
- "if let Some({checked_sub_result_var_name}) = {lhs_snippet}.checked_sub({rhs_snippet}) {updated_usage_context_snippet}"
192
- )
193
- } ,
194
- } ;
195
-
196
- if let Some ( else_expr) = self . else_block {
197
- let else_snippet = snippet_with_applicability ( self . cx , else_expr. span , ".." , & mut applicability) ;
198
- write ! ( suggestion, " else {else_snippet}" ) . unwrap ( ) ;
199
- }
200
-
201
- span_lint_and_sugg (
158
+ fn emit_lint ( & mut self ) {
159
+ span_lint (
202
160
self . cx ,
203
161
MANUAL_CHECKED_SUB ,
204
162
self . if_expr . span ,
205
- "manual re-implementation of checked subtraction" ,
206
- "consider using `.checked_sub()`" ,
207
- suggestion,
208
- applicability,
163
+ "manual re-implementation of checked subtraction - consider using `.checked_sub()`" ,
209
164
) ;
210
165
}
211
166
}
@@ -236,93 +191,3 @@ fn is_referencing_same_variable<'tcx>(expr1: &'tcx Expr<'_>, expr2: &'tcx Expr<'
236
191
237
192
false
238
193
}
239
-
240
- /// Generates a unique variable name based on the provided base name.
241
- /// If the base name already exists in the scope, appends a number to make it unique.
242
- fn generate_unique_var_name < ' tcx > ( base_name : & str , scope_expr : & ' tcx Expr < ' tcx > ) -> String {
243
- struct VarNameVisitor < ' cx > {
244
- base_name : & ' cx str ,
245
- is_name_in_scope : bool ,
246
- }
247
-
248
- impl < ' tcx > Visitor < ' tcx > for VarNameVisitor < ' _ > {
249
- fn visit_expr ( & mut self , expr : & ' tcx Expr < ' _ > ) {
250
- if let ExprKind :: Path ( QPath :: Resolved ( _, path) ) = & expr. kind
251
- && let Some ( segment) = path. segments . last ( )
252
- {
253
- let name = segment. ident . name . to_string ( ) ;
254
- if name == self . base_name {
255
- self . is_name_in_scope = true ;
256
- }
257
- }
258
-
259
- walk_expr ( self , expr) ;
260
- }
261
-
262
- fn visit_stmt ( & mut self , stmt : & ' tcx rustc_hir:: Stmt < ' _ > ) {
263
- if let rustc_hir:: StmtKind :: Let ( let_stmt) = & stmt. kind
264
- && let PatKind :: Binding ( _, _, ident, _) = & let_stmt. pat . kind
265
- {
266
- let name = ident. name . to_string ( ) ;
267
- if name == self . base_name {
268
- self . is_name_in_scope = true ;
269
- }
270
- }
271
-
272
- walk_stmt ( self , stmt) ;
273
- }
274
- }
275
-
276
- let mut visitor = VarNameVisitor {
277
- base_name,
278
- is_name_in_scope : false ,
279
- } ;
280
-
281
- if let ExprKind :: Block ( block, _) = & scope_expr. kind {
282
- for stmt in block. stmts {
283
- visitor. visit_stmt ( stmt) ;
284
- }
285
-
286
- // Visit the optional expression at the end of the block
287
- if let Some ( expr) = block. expr {
288
- visitor. visit_expr ( expr) ;
289
- }
290
- } else {
291
- visitor. visit_expr ( scope_expr) ;
292
- }
293
-
294
- if !visitor. is_name_in_scope {
295
- return base_name. to_string ( ) ;
296
- }
297
-
298
- // If the base name is in scope, append a number
299
- // Keep incrementing until we find a name that's not in scope
300
- let mut counter = 1 ;
301
- loop {
302
- let candidate = format ! ( "{base_name}_{counter}" ) ;
303
-
304
- let mut candidate_visitor = VarNameVisitor {
305
- base_name : & candidate,
306
- is_name_in_scope : false ,
307
- } ;
308
-
309
- // Check if the candidate name is in scope
310
- if let ExprKind :: Block ( block, _) = & scope_expr. kind {
311
- for stmt in block. stmts {
312
- candidate_visitor. visit_stmt ( stmt) ;
313
- }
314
-
315
- if let Some ( expr) = block. expr {
316
- candidate_visitor. visit_expr ( expr) ;
317
- }
318
- } else {
319
- candidate_visitor. visit_expr ( scope_expr) ;
320
- }
321
-
322
- if !candidate_visitor. is_name_in_scope {
323
- return candidate;
324
- }
325
-
326
- counter += 1 ;
327
- }
328
- }
0 commit comments