1- use clippy_utils:: diagnostics:: span_lint_and_then;
1+ 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 ;
8- use rustc_hir:: { Block , Expr , ExprKind , PatKind , QPath , StmtKind } ;
8+ use 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} ;
12- use rustc_span:: sym;
12+ use rustc_span:: source_map:: Spanned ;
13+ use rustc_span:: { sym, Span } ;
1314
1415declare_clippy_lint ! {
1516 /// ### What it does
@@ -70,9 +71,67 @@ impl<'tcx> LateLintPass<'tcx> for Swap {
7071 fn check_block ( & mut self , cx : & LateContext < ' tcx > , block : & ' tcx Block < ' _ > ) {
7172 check_manual_swap ( cx, block) ;
7273 check_suspicious_swap ( cx, block) ;
74+ check_xor_swap ( cx, block) ;
7375 }
7476}
7577
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+
76135/// Implementation of the `MANUAL_SWAP` lint.
77136fn check_manual_swap ( cx : & LateContext < ' _ > , block : & Block < ' _ > ) {
78137 for w in block. stmts . windows ( 3 ) {
@@ -96,121 +155,11 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
96155 if eq_expr_value( cx, tmp_init, lhs1) ;
97156 if eq_expr_value( cx, rhs1, lhs2) ;
98157 then {
99- if let ExprKind :: Field ( lhs1, _) = lhs1. kind {
100- if let ExprKind :: Field ( lhs2, _) = lhs2. kind {
101- if lhs1. hir_id. owner == lhs2. hir_id. owner {
102- return ;
103- }
104- }
105- }
106-
107- let mut applicability = Applicability :: MachineApplicable ;
108-
109- let slice = check_for_slice( cx, lhs1, lhs2) ;
110- let ( replace, what, sugg) = if let Slice :: NotSwappable = slice {
111- return ;
112- } else if let Slice :: Swappable ( slice, idx1, idx2) = slice {
113- if let Some ( slice) = Sugg :: hir_opt( cx, slice) {
114- (
115- false ,
116- format!( " elements of `{}`" , slice) ,
117- format!(
118- "{}.swap({}, {})" ,
119- slice. maybe_par( ) ,
120- snippet_with_applicability( cx, idx1. span, ".." , & mut applicability) ,
121- snippet_with_applicability( cx, idx2. span, ".." , & mut applicability) ,
122- ) ,
123- )
124- } else {
125- ( false , String :: new( ) , String :: new( ) )
126- }
127- } else if let ( Some ( first) , Some ( second) ) = ( Sugg :: hir_opt( cx, lhs1) , Sugg :: hir_opt( cx, rhs1) ) {
128- (
129- true ,
130- format!( " `{}` and `{}`" , first, second) ,
131- format!( "std::mem::swap({}, {})" , first. mut_addr( ) , second. mut_addr( ) ) ,
132- )
133- } else {
134- ( true , String :: new( ) , String :: new( ) )
135- } ;
136-
137158 let span = w[ 0 ] . span. to( second. span) ;
138-
139- span_lint_and_then(
140- cx,
141- MANUAL_SWAP ,
142- span,
143- & format!( "this looks like you are swapping{} manually" , what) ,
144- |diag| {
145- if !sugg. is_empty( ) {
146- diag. span_suggestion(
147- span,
148- "try" ,
149- sugg,
150- applicability,
151- ) ;
152-
153- if replace {
154- diag. note( "or maybe you should use `std::mem::replace`?" ) ;
155- }
156- }
157- }
158- ) ;
159- }
160- }
161- }
162- }
163-
164- enum Slice < ' a > {
165- /// `slice.swap(idx1, idx2)` can be used
166- ///
167- /// ## Example
168- ///
169- /// ```rust
170- /// # let mut a = vec![0, 1];
171- /// let t = a[1];
172- /// a[1] = a[0];
173- /// a[0] = t;
174- /// // can be written as
175- /// a.swap(0, 1);
176- /// ```
177- Swappable ( & ' a Expr < ' a > , & ' a Expr < ' a > , & ' a Expr < ' a > ) ,
178- /// The `swap` function cannot be used.
179- ///
180- /// ## Example
181- ///
182- /// ```rust
183- /// # let mut a = [vec![1, 2], vec![3, 4]];
184- /// let t = a[0][1];
185- /// a[0][1] = a[1][0];
186- /// a[1][0] = t;
187- /// ```
188- NotSwappable ,
189- /// Not a slice
190- None ,
191- }
192-
193- /// Checks if both expressions are index operations into "slice-like" types.
194- fn check_for_slice < ' a > ( cx : & LateContext < ' _ > , lhs1 : & ' a Expr < ' _ > , lhs2 : & ' a Expr < ' _ > ) -> Slice < ' a > {
195- if let ExprKind :: Index ( lhs1, idx1) = lhs1. kind {
196- if let ExprKind :: Index ( lhs2, idx2) = lhs2. kind {
197- if eq_expr_value ( cx, lhs1, lhs2) {
198- let ty = cx. typeck_results ( ) . expr_ty ( lhs1) . peel_refs ( ) ;
199-
200- if matches ! ( ty. kind( ) , ty:: Slice ( _) )
201- || matches ! ( ty. kind( ) , ty:: Array ( _, _) )
202- || is_type_diagnostic_item ( cx, ty, sym:: vec_type)
203- || is_type_diagnostic_item ( cx, ty, sym:: vecdeque_type)
204- {
205- return Slice :: Swappable ( lhs1, idx1, idx2) ;
206- }
207- } else {
208- return Slice :: NotSwappable ;
159+ generate_swap_warning( cx, lhs1, lhs2, span, false ) ;
209160 }
210161 }
211162 }
212-
213- Slice :: None
214163}
215164
216165/// Implementation of the `ALMOST_SWAPPED` lint.
@@ -262,3 +211,40 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) {
262211 }
263212 }
264213}
214+
215+ /// Implementation of the xor case for `MANUAL_SWAP` lint.
216+ fn check_xor_swap ( cx : & LateContext < ' _ > , block : & Block < ' _ > ) {
217+ for window in block. stmts . windows ( 3 ) {
218+ if_chain ! {
219+ if let Some ( ( lhs0, rhs0) ) = extract_sides_of_xor_assign( & window[ 0 ] ) ;
220+ if let Some ( ( lhs1, rhs1) ) = extract_sides_of_xor_assign( & window[ 1 ] ) ;
221+ if let Some ( ( lhs2, rhs2) ) = extract_sides_of_xor_assign( & window[ 2 ] ) ;
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) ;
226+ then {
227+ let span = window[ 0 ] . span. to( window[ 2 ] . span) ;
228+ generate_swap_warning( cx, lhs0, rhs0, span, true ) ;
229+ }
230+ } ;
231+ }
232+ }
233+
234+ /// Returns the lhs and rhs of an xor assignment statement.
235+ fn extract_sides_of_xor_assign < ' a , ' hir > ( stmt : & ' a Stmt < ' hir > ) -> Option < ( & ' a Expr < ' hir > , & ' a Expr < ' hir > ) > {
236+ if let StmtKind :: Semi ( expr) = stmt. kind {
237+ if let ExprKind :: AssignOp (
238+ Spanned {
239+ node : BinOpKind :: BitXor ,
240+ ..
241+ } ,
242+ lhs,
243+ rhs,
244+ ) = expr. kind
245+ {
246+ return Some ( ( lhs, rhs) ) ;
247+ }
248+ }
249+ None
250+ }
0 commit comments