@@ -172,6 +172,7 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
172172 }
173173}
174174
175+ #[ allow( clippy:: too_many_lines) ]
175176/// Implementation of the `ALMOST_SWAPPED` lint.
176177fn check_suspicious_swap ( cx : & LateContext < ' _ > , block : & Block < ' _ > ) {
177178 for w in block. stmts . windows ( 2 ) {
@@ -220,6 +221,79 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) {
220221 } ) ;
221222 }
222223 }
224+
225+ let lint_almost_swapped_note = |span, what : String , sugg, lhs, rhs| {
226+ span_lint_and_then (
227+ cx,
228+ ALMOST_SWAPPED ,
229+ span,
230+ & format ! ( "this looks like you are trying to swap{}" , what) ,
231+ |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+ }
237+ } ,
238+ ) ;
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 } ;
265+
266+ lint_almost_swapped_note ( span, what, sugg, lhs, rhs) ;
267+ }
268+
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 } ;
294+
295+ lint_almost_swapped_note ( span, what, sugg, lhs, rhs) ;
296+ }
223297 }
224298}
225299
0 commit comments