@@ -2,15 +2,15 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
22use clippy_utils:: source:: snippet_with_applicability;
33use clippy_utils:: sugg:: Sugg ;
44use clippy_utils:: ty:: is_type_diagnostic_item;
5- use clippy_utils:: { differing_macro_contexts, eq_expr_value} ;
5+ use clippy_utils:: { can_mut_borrow_both , differing_macro_contexts, eq_expr_value} ;
66use if_chain:: if_chain;
77use rustc_errors:: Applicability ;
88use rustc_hir:: { BinOpKind , Block , Expr , ExprKind , PatKind , QPath , Stmt , StmtKind } ;
99use 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;
13+ use rustc_span:: { sym, Span } ;
1414
1515declare_clippy_lint ! {
1616 /// ### What it does
@@ -65,37 +65,7 @@ declare_clippy_lint! {
6565 "`foo = bar; bar = foo` sequence"
6666}
6767
68- declare_clippy_lint ! {
69- /// **What it does:** Checks for uses of xor-based swaps.
70- ///
71- /// **Why is this bad?** The `std::mem::swap` function exposes the intent better
72- /// without deinitializing or copying either variable.
73- ///
74- /// **Known problems:** None.
75- ///
76- /// **Example:**
77- ///
78- /// ```rust
79- /// let mut a = 1;
80- /// let mut b = 2;
81- ///
82- /// a ^= b;
83- /// b ^= a;
84- /// a ^= b;
85- /// ```
86- ///
87- /// Use std::mem::swap() instead:
88- /// ```rust
89- /// let mut a = 1;
90- /// let mut b = 2;
91- /// std::mem::swap(&mut a, &mut b);
92- /// ```
93- pub XOR_SWAP ,
94- complexity,
95- "xor-based swap of two variables"
96- }
97-
98- declare_lint_pass ! ( Swap => [ MANUAL_SWAP , ALMOST_SWAPPED , XOR_SWAP ] ) ;
68+ declare_lint_pass ! ( Swap => [ MANUAL_SWAP , ALMOST_SWAPPED ] ) ;
9969
10070impl < ' tcx > LateLintPass < ' tcx > for Swap {
10171 fn check_block ( & mut self , cx : & LateContext < ' tcx > , block : & ' tcx Block < ' _ > ) {
@@ -105,6 +75,63 @@ impl<'tcx> LateLintPass<'tcx> for Swap {
10575 }
10676}
10777
78+ fn generate_swap_warning ( cx : & LateContext < ' _ > , e1 : & Expr < ' _ > , e2 : & Expr < ' _ > , span : Span , is_xor_based : bool ) {
79+ let mut applicability = Applicability :: MachineApplicable ;
80+
81+ if !can_mut_borrow_both ( cx, e1, e2) {
82+ if let ExprKind :: Index ( lhs1, idx1) = e1. kind {
83+ if let ExprKind :: Index ( lhs2, idx2) = e2. kind {
84+ if eq_expr_value ( cx, lhs1, lhs2) {
85+ let ty = cx. typeck_results ( ) . expr_ty ( lhs1) . peel_refs ( ) ;
86+
87+ if matches ! ( ty. kind( ) , ty:: Slice ( _) )
88+ || matches ! ( ty. kind( ) , ty:: Array ( _, _) )
89+ || is_type_diagnostic_item ( cx, ty, sym:: vec_type)
90+ || is_type_diagnostic_item ( cx, ty, sym:: vecdeque_type)
91+ {
92+ let slice = Sugg :: hir_with_applicability ( cx, lhs1, "<slice>" , & mut applicability) ;
93+ span_lint_and_sugg (
94+ cx,
95+ MANUAL_SWAP ,
96+ span,
97+ & format ! ( "this looks like you are swapping elements of `{}` manually" , slice) ,
98+ "try" ,
99+ format ! (
100+ "{}.swap({}, {})" ,
101+ slice. maybe_par( ) ,
102+ snippet_with_applicability( cx, idx1. span, ".." , & mut applicability) ,
103+ snippet_with_applicability( cx, idx2. span, ".." , & mut applicability) ,
104+ ) ,
105+ applicability,
106+ ) ;
107+ }
108+ }
109+ }
110+ }
111+ return ;
112+ }
113+
114+ let first = Sugg :: hir_with_applicability ( cx, e1, ".." , & mut applicability) ;
115+ let second = Sugg :: hir_with_applicability ( cx, e2, ".." , & mut applicability) ;
116+ span_lint_and_then (
117+ cx,
118+ MANUAL_SWAP ,
119+ span,
120+ & format ! ( "this looks like you are swapping `{}` and `{}` manually" , first, second) ,
121+ |diag| {
122+ diag. span_suggestion (
123+ span,
124+ "try" ,
125+ format ! ( "std::mem::swap({}, {})" , first. mut_addr( ) , second. mut_addr( ) ) ,
126+ applicability,
127+ ) ;
128+ if !is_xor_based {
129+ diag. note ( "or maybe you should use `std::mem::replace`?" ) ;
130+ }
131+ } ,
132+ ) ;
133+ }
134+
108135/// Implementation of the `MANUAL_SWAP` lint.
109136fn check_manual_swap ( cx : & LateContext < ' _ > , block : & Block < ' _ > ) {
110137 for w in block. stmts . windows ( 3 ) {
@@ -128,123 +155,13 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
128155 if eq_expr_value( cx, tmp_init, lhs1) ;
129156 if eq_expr_value( cx, rhs1, lhs2) ;
130157 then {
131- if let ExprKind :: Field ( lhs1, _) = lhs1. kind {
132- if let ExprKind :: Field ( lhs2, _) = lhs2. kind {
133- if lhs1. hir_id. owner == lhs2. hir_id. owner {
134- return ;
135- }
136- }
137- }
138-
139- let mut applicability = Applicability :: MachineApplicable ;
140-
141- let slice = check_for_slice( cx, lhs1, lhs2) ;
142- let ( replace, what, sugg) = if let Slice :: NotSwappable = slice {
143- return ;
144- } else if let Slice :: Swappable ( slice, idx1, idx2) = slice {
145- if let Some ( slice) = Sugg :: hir_opt( cx, slice) {
146- (
147- false ,
148- format!( " elements of `{}`" , slice) ,
149- format!(
150- "{}.swap({}, {})" ,
151- slice. maybe_par( ) ,
152- snippet_with_applicability( cx, idx1. span, ".." , & mut applicability) ,
153- snippet_with_applicability( cx, idx2. span, ".." , & mut applicability) ,
154- ) ,
155- )
156- } else {
157- ( false , String :: new( ) , String :: new( ) )
158- }
159- } else if let ( Some ( first) , Some ( second) ) = ( Sugg :: hir_opt( cx, lhs1) , Sugg :: hir_opt( cx, rhs1) ) {
160- (
161- true ,
162- format!( " `{}` and `{}`" , first, second) ,
163- format!( "std::mem::swap({}, {})" , first. mut_addr( ) , second. mut_addr( ) ) ,
164- )
165- } else {
166- ( true , String :: new( ) , String :: new( ) )
167- } ;
168-
169158 let span = w[ 0 ] . span. to( second. span) ;
170-
171- span_lint_and_then(
172- cx,
173- MANUAL_SWAP ,
174- span,
175- & format!( "this looks like you are swapping{} manually" , what) ,
176- |diag| {
177- if !sugg. is_empty( ) {
178- diag. span_suggestion(
179- span,
180- "try" ,
181- sugg,
182- applicability,
183- ) ;
184-
185- if replace {
186- diag. note( "or maybe you should use `std::mem::replace`?" ) ;
187- }
188- }
189- }
190- ) ;
159+ generate_swap_warning( cx, lhs1, lhs2, span, false ) ;
191160 }
192161 }
193162 }
194163}
195164
196- enum Slice < ' a > {
197- /// `slice.swap(idx1, idx2)` can be used
198- ///
199- /// ## Example
200- ///
201- /// ```rust
202- /// # let mut a = vec![0, 1];
203- /// let t = a[1];
204- /// a[1] = a[0];
205- /// a[0] = t;
206- /// // can be written as
207- /// a.swap(0, 1);
208- /// ```
209- Swappable ( & ' a Expr < ' a > , & ' a Expr < ' a > , & ' a Expr < ' a > ) ,
210- /// The `swap` function cannot be used.
211- ///
212- /// ## Example
213- ///
214- /// ```rust
215- /// # let mut a = [vec![1, 2], vec![3, 4]];
216- /// let t = a[0][1];
217- /// a[0][1] = a[1][0];
218- /// a[1][0] = t;
219- /// ```
220- NotSwappable ,
221- /// Not a slice
222- None ,
223- }
224-
225- /// Checks if both expressions are index operations into "slice-like" types.
226- fn check_for_slice < ' a > ( cx : & LateContext < ' _ > , lhs1 : & ' a Expr < ' _ > , lhs2 : & ' a Expr < ' _ > ) -> Slice < ' a > {
227- if let ExprKind :: Index ( lhs1, idx1) = lhs1. kind {
228- if let ExprKind :: Index ( lhs2, idx2) = lhs2. kind {
229- if eq_expr_value ( cx, lhs1, lhs2) {
230- let ty = cx. typeck_results ( ) . expr_ty ( lhs1) . peel_refs ( ) ;
231-
232- if matches ! ( ty. kind( ) , ty:: Slice ( _) )
233- || matches ! ( ty. kind( ) , ty:: Array ( _, _) )
234- || is_type_diagnostic_item ( cx, ty, sym:: vec_type)
235- || is_type_diagnostic_item ( cx, ty, sym:: vecdeque_type)
236- {
237- return Slice :: Swappable ( lhs1, idx1, idx2) ;
238- }
239- } else {
240- return Slice :: NotSwappable ;
241- }
242- }
243- }
244-
245- Slice :: None
246- }
247-
248165/// Implementation of the `ALMOST_SWAPPED` lint.
249166fn check_suspicious_swap ( cx : & LateContext < ' _ > , block : & Block < ' _ > ) {
250167 for w in block. stmts . windows ( 2 ) {
@@ -295,62 +212,20 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) {
295212 }
296213}
297214
298- /// Implementation of the `XOR_SWAP ` lint.
215+ /// Implementation of the xor case for `MANUAL_SWAP ` lint.
299216fn check_xor_swap ( cx : & LateContext < ' _ > , block : & Block < ' _ > ) {
300217 for window in block. stmts . windows ( 3 ) {
301218 if_chain ! {
302219 if let Some ( ( lhs0, rhs0) ) = extract_sides_of_xor_assign( & window[ 0 ] ) ;
303220 if let Some ( ( lhs1, rhs1) ) = extract_sides_of_xor_assign( & window[ 1 ] ) ;
304221 if let Some ( ( lhs2, rhs2) ) = extract_sides_of_xor_assign( & window[ 2 ] ) ;
305- if eq_expr_value( cx, lhs0, rhs1)
306- && eq_expr_value( cx, rhs1 , lhs2 )
307- && eq_expr_value( cx, rhs0 , lhs1 )
308- && eq_expr_value( cx, lhs1, rhs2) ;
222+ if eq_expr_value( cx, lhs0, rhs1) ;
223+ if eq_expr_value( cx, lhs2 , rhs1 ) ;
224+ if eq_expr_value( cx, lhs1 , rhs0 ) ;
225+ if eq_expr_value( cx, lhs1, rhs2) ;
309226 then {
310227 let span = window[ 0 ] . span. to( window[ 2 ] . span) ;
311- let mut applicability = Applicability :: MachineApplicable ;
312- match check_for_slice( cx, lhs0, rhs0) {
313- Slice :: Swappable ( slice, idx0, idx1) => {
314- if let Some ( slice) = Sugg :: hir_opt( cx, slice) {
315- span_lint_and_sugg(
316- cx,
317- XOR_SWAP ,
318- span,
319- & format!(
320- "this xor-based swap of the elements of `{}` can be \
321- more clearly expressed using `.swap`",
322- slice
323- ) ,
324- "try" ,
325- format!(
326- "{}.swap({}, {})" ,
327- slice. maybe_par( ) ,
328- snippet_with_applicability( cx, idx0. span, ".." , & mut applicability) ,
329- snippet_with_applicability( cx, idx1. span, ".." , & mut applicability)
330- ) ,
331- applicability
332- )
333- }
334- }
335- Slice :: None => {
336- if let ( Some ( first) , Some ( second) ) = ( Sugg :: hir_opt( cx, lhs0) , Sugg :: hir_opt( cx, rhs0) ) {
337- span_lint_and_sugg(
338- cx,
339- XOR_SWAP ,
340- span,
341- & format!(
342- "this xor-based swap of `{}` and `{}` can be \
343- more clearly expressed using `std::mem::swap`",
344- first, second
345- ) ,
346- "try" ,
347- format!( "std::mem::swap({}, {})" , first. mut_addr( ) , second. mut_addr( ) ) ,
348- applicability,
349- ) ;
350- }
351- }
352- Slice :: NotSwappable => { }
353- }
228+ generate_swap_warning( cx, lhs0, rhs0, span, true ) ;
354229 }
355230 } ;
356231 }
0 commit comments