@@ -10,7 +10,7 @@ use rustc_lint::{LateContext, LateLintPass};
10
10
use rustc_middle:: ty;
11
11
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
12
12
use rustc_span:: source_map:: Spanned ;
13
- use rustc_span:: { sym, Span } ;
13
+ use rustc_span:: { sym, symbol :: Ident , Span } ;
14
14
15
15
declare_clippy_lint ! {
16
16
/// ### What it does
@@ -172,129 +172,76 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
172
172
}
173
173
}
174
174
175
- #[ allow( clippy:: too_many_lines) ]
176
175
/// Implementation of the `ALMOST_SWAPPED` lint.
177
176
fn check_suspicious_swap ( cx : & LateContext < ' _ > , block : & Block < ' _ > ) {
178
- for w in block. stmts . windows ( 2 ) {
179
- if_chain ! {
180
- if let StmtKind :: Semi ( first) = w[ 0 ] . kind;
181
- if let StmtKind :: Semi ( second) = w[ 1 ] . kind;
182
- if first. span. ctxt( ) == second. span. ctxt( ) ;
183
- if let ExprKind :: Assign ( lhs0, rhs0, _) = first. kind;
184
- if let ExprKind :: Assign ( lhs1, rhs1, _) = second. kind;
185
- if eq_expr_value( cx, lhs0, rhs1) ;
186
- if eq_expr_value( cx, lhs1, rhs0) ;
187
- then {
188
- let lhs0 = Sugg :: hir_opt( cx, lhs0) ;
189
- let rhs0 = Sugg :: hir_opt( cx, rhs0) ;
190
- let ( what, lhs, rhs) = if let ( Some ( first) , Some ( second) ) = ( lhs0, rhs0) {
191
- (
192
- format!( " `{first}` and `{second}`" ) ,
193
- first. mut_addr( ) . to_string( ) ,
194
- second. mut_addr( ) . to_string( ) ,
195
- )
196
- } else {
197
- ( String :: new( ) , String :: new( ) , String :: new( ) )
198
- } ;
199
-
200
- let span = first. span. to( second. span) ;
201
- let Some ( sugg) = std_or_core( cx) else { return } ;
202
-
203
- span_lint_and_then( cx,
204
- ALMOST_SWAPPED ,
205
- span,
206
- & format!( "this looks like you are trying to swap{what}" ) ,
207
- |diag| {
208
- if !what. is_empty( ) {
209
- diag. span_suggestion(
210
- span,
211
- "try" ,
212
- format!(
213
- "{sugg}::mem::swap({lhs}, {rhs})" ,
214
- ) ,
215
- Applicability :: MaybeIncorrect ,
216
- ) ;
217
- diag. note(
218
- format!( "or maybe you should use `{sugg}::mem::replace`?" )
219
- ) ;
220
- }
221
- } ) ;
177
+ for [ first, second] in block. stmts . array_windows ( ) {
178
+ if let Some ( ( lhs0, rhs0) ) = parse ( first)
179
+ && let Some ( ( lhs1, rhs1) ) = parse ( second)
180
+ && first. span . eq_ctxt ( second. span )
181
+ && is_same ( cx, lhs0, rhs1)
182
+ && is_same ( cx, lhs1, rhs0)
183
+ && let Some ( lhs_sugg) = match & lhs0 {
184
+ ExprOrIdent :: Expr ( expr) => Sugg :: hir_opt ( cx, expr) ,
185
+ ExprOrIdent :: Ident ( ident) => Some ( Sugg :: NonParen ( ident. as_str ( ) . into ( ) ) ) ,
222
186
}
223
- }
224
-
225
- let lint_almost_swapped_note = |span, what : String , sugg, lhs, rhs| {
187
+ && let Some ( rhs_sugg) = Sugg :: hir_opt ( cx, rhs0)
188
+ {
189
+ let span = first. span . to ( rhs1. span ) ;
190
+ let Some ( sugg) = std_or_core ( cx) else { return } ;
226
191
span_lint_and_then (
227
192
cx,
228
193
ALMOST_SWAPPED ,
229
194
span,
230
- & format ! ( "this looks like you are trying to swap{}" , what ) ,
195
+ & format ! ( "this looks like you are trying to swap `{lhs_sugg}` and `{rhs_sugg}`" ) ,
231
196
|diag| {
232
- if !what. is_empty ( ) {
233
- diag. note ( & format ! (
234
- "maybe you could use `{sugg}::mem::swap({lhs}, {rhs})` or `{sugg}::mem::replace`?"
235
- ) ) ;
236
- }
197
+ diag. span_suggestion (
198
+ span,
199
+ "try" ,
200
+ format ! ( "{sugg}::mem::swap({}, {})" , lhs_sugg. mut_addr( ) , rhs_sugg. mut_addr( ) ) ,
201
+ Applicability :: MaybeIncorrect ,
202
+ ) ;
203
+ diag. note ( format ! ( "or maybe you should use `{sugg}::mem::replace`?" ) ) ;
237
204
} ,
238
205
) ;
239
- } ;
240
-
241
- if let StmtKind :: Local ( first) = w[ 0 ] . kind
242
- && let StmtKind :: Local ( second) = w[ 1 ] . kind
243
- && first. span . ctxt ( ) == second. span . ctxt ( )
244
- && let Some ( rhs0) = first. init
245
- && let Some ( rhs1) = second. init
246
- && let ExprKind :: Path ( QPath :: Resolved ( None , path_l) ) = rhs0. kind
247
- && let ExprKind :: Path ( QPath :: Resolved ( None , path_r) ) = rhs1. kind
248
- && let PatKind :: Binding ( _, _, ident_l, _) = first. pat . kind
249
- && let PatKind :: Binding ( _, _, ident_r, _) = second. pat . kind
250
- && ident_l. name . as_str ( ) == path_r. segments . iter ( ) . map ( |el| el. ident . to_string ( ) ) . collect :: < Vec < _ > > ( ) . join ( "::" )
251
- && ident_r. name . as_str ( ) == path_l. segments . iter ( ) . map ( |el| el. ident . to_string ( ) ) . collect :: < Vec < _ > > ( ) . join ( "::" )
252
- {
253
- let rhs0 = Sugg :: hir_opt ( cx, rhs0) ;
254
- let ( what, lhs, rhs) = if let Some ( second) = rhs0 {
255
- (
256
- format ! ( " `{}` and `{}`" , ident_l, second) ,
257
- format ! ( "&mut {}" , ident_l) ,
258
- second. mut_addr ( ) . to_string ( ) ,
259
- )
260
- } else {
261
- ( String :: new ( ) , String :: new ( ) , String :: new ( ) )
262
- } ;
263
- let span = first. span . to ( second. span ) ;
264
- let Some ( sugg) = std_or_core ( cx) else { return } ;
206
+ }
207
+ }
208
+ }
265
209
266
- lint_almost_swapped_note ( span, what, sugg, lhs, rhs) ;
267
- }
210
+ fn is_same ( cx : & LateContext < ' _ > , lhs : ExprOrIdent < ' _ > , rhs : & Expr < ' _ > ) -> bool {
211
+ match lhs {
212
+ ExprOrIdent :: Expr ( expr) => eq_expr_value ( cx, expr, rhs) ,
213
+ ExprOrIdent :: Ident ( ident) => {
214
+ if let ExprKind :: Path ( QPath :: Resolved ( None , path) ) = rhs. kind
215
+ && let [ segment] = & path. segments
216
+ && segment. ident == ident
217
+ {
218
+ true
219
+ } else {
220
+ false
221
+ }
222
+ }
223
+ }
224
+ }
268
225
269
- if let StmtKind :: Local ( first) = w[ 0 ] . kind
270
- && let StmtKind :: Semi ( second) = w[ 1 ] . kind
271
- && first. span . ctxt ( ) == second. span . ctxt ( )
272
- && let Some ( rhs0) = first. init
273
- && let ExprKind :: Path ( QPath :: Resolved ( None , path_l) ) = rhs0. kind
274
- && let PatKind :: Binding ( _, _, ident_l, _) = first. pat . kind
275
- && let ExprKind :: Assign ( lhs1, rhs1, _) = second. kind
276
- && let ExprKind :: Path ( QPath :: Resolved ( None , lhs1_path) ) = lhs1. kind
277
- && let ExprKind :: Path ( QPath :: Resolved ( None , rhs1_path) ) = rhs1. kind
278
- && ident_l. name . as_str ( ) == rhs1_path. segments . iter ( ) . map ( |el| el. ident . to_string ( ) ) . collect :: < Vec < _ > > ( ) . join ( "::" )
279
- && path_l. segments . iter ( ) . map ( |el| el. ident . to_string ( ) ) . collect :: < Vec < _ > > ( ) . join ( "::" ) == lhs1_path. segments . iter ( ) . map ( |el| el. ident . to_string ( ) ) . collect :: < Vec < _ > > ( ) . join ( "::" )
280
- {
281
- let lhs1 = Sugg :: hir_opt ( cx, lhs1) ;
282
- let rhs1 = Sugg :: hir_opt ( cx, rhs1) ;
283
- let ( what, lhs, rhs) = if let ( Some ( first) , Some ( second) ) = ( lhs1, rhs1) {
284
- (
285
- format ! ( " `{}` and `{}`" , first, second) ,
286
- first. mut_addr ( ) . to_string ( ) ,
287
- second. mut_addr ( ) . to_string ( ) ,
288
- )
289
- } else {
290
- ( String :: new ( ) , String :: new ( ) , String :: new ( ) )
291
- } ;
292
- let span = first. span . to ( second. span ) ;
293
- let Some ( sugg) = std_or_core ( cx) else { return } ;
226
+ #[ derive( Debug , Clone , Copy ) ]
227
+ enum ExprOrIdent < ' a > {
228
+ Expr ( & ' a Expr < ' a > ) ,
229
+ Ident ( Ident ) ,
230
+ }
294
231
295
- lint_almost_swapped_note ( span, what, sugg, lhs, rhs) ;
296
- }
232
+ fn parse < ' a , ' hir > ( stmt : & ' a Stmt < ' hir > ) -> Option < ( ExprOrIdent < ' hir > , & ' a Expr < ' hir > ) > {
233
+ if let StmtKind :: Semi ( expr) = stmt. kind {
234
+ if let ExprKind :: Assign ( lhs, rhs, _) = expr. kind {
235
+ return Some ( ( ExprOrIdent :: Expr ( lhs) , rhs) ) ;
236
+ }
237
+ } else if let StmtKind :: Local ( expr) = stmt. kind {
238
+ if let Some ( rhs) = expr. init {
239
+ if let PatKind :: Binding ( _, _, ident_l, _) = expr. pat . kind {
240
+ return Some ( ( ExprOrIdent :: Ident ( ident_l) , rhs) ) ;
241
+ }
242
+ }
297
243
}
244
+ None
298
245
}
299
246
300
247
/// Implementation of the xor case for `MANUAL_SWAP` lint.
0 commit comments