@@ -10,7 +10,7 @@ use rustc_lint::{LateContext, LateLintPass};
1010use rustc_middle:: ty;
1111use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
1212use rustc_span:: source_map:: Spanned ;
13- use rustc_span:: { sym, Span } ;
13+ use rustc_span:: { sym, symbol :: Ident , Span } ;
1414
1515declare_clippy_lint ! {
1616 /// ### What it does
@@ -172,129 +172,76 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
172172 }
173173}
174174
175- #[ allow( clippy:: too_many_lines) ]
176175/// Implementation of the `ALMOST_SWAPPED` lint.
177176fn 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 ( ) ) ) ,
222186 }
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 } ;
226191 span_lint_and_then (
227192 cx,
228193 ALMOST_SWAPPED ,
229194 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}`" ) ,
231196 |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`?" ) ) ;
237204 } ,
238205 ) ;
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+ }
265209
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+ }
268225
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+ }
294231
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+ }
297243 }
244+ None
298245}
299246
300247/// Implementation of the xor case for `MANUAL_SWAP` lint.
0 commit comments