about summary refs log tree commit diff
diff options
context:
space:
mode:
authorsjwang05 <63834813+sjwang05@users.noreply.github.com>2024-01-11 21:02:43 -0800
committersjwang05 <63834813+sjwang05@users.noreply.github.com>2024-01-19 01:30:46 -0800
commitf9faf161817eef2b885bd47908ceaeef675ea72f (patch)
tree1a56a5d2a70c27b4e575d12f2ecca26b8a6f8091
parente9271846294c4ee5bd7706df68180320c0b5ff20 (diff)
downloadrust-f9faf161817eef2b885bd47908ceaeef675ea72f.tar.gz
rust-f9faf161817eef2b885bd47908ceaeef675ea72f.zip
Suggest .swap() instead of mem::swap() in more cases
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs99
-rw-r--r--tests/ui/suggestions/suggest-slice-swap.fixed9
-rw-r--r--tests/ui/suggestions/suggest-slice-swap.rs9
-rw-r--r--tests/ui/suggestions/suggest-slice-swap.stderr17
4 files changed, 127 insertions, 7 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 5baa108ed3c..a230ba0e439 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -1,3 +1,5 @@
+// ignore-tidy-filelength
+
 use either::Either;
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::FxIndexSet;
@@ -24,6 +26,7 @@ use rustc_span::hygiene::DesugaringKind;
 use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::{BytePos, Span, Symbol};
 use rustc_trait_selection::infer::InferCtxtExt;
+use rustc_trait_selection::traits::error_reporting::FindExprBySpan;
 use rustc_trait_selection::traits::ObligationCtxt;
 use std::iter;
 
@@ -1295,14 +1298,96 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         place: Place<'tcx>,
         borrowed_place: Place<'tcx>,
     ) {
-        if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) =
-            (&place.projection[..], &borrowed_place.projection[..])
+        let tcx = self.infcx.tcx;
+        let hir = tcx.hir();
+
+        if let ([ProjectionElem::Index(index1)], [ProjectionElem::Index(index2)])
+        | (
+            [ProjectionElem::Deref, ProjectionElem::Index(index1)],
+            [ProjectionElem::Deref, ProjectionElem::Index(index2)],
+        ) = (&place.projection[..], &borrowed_place.projection[..])
         {
-            err.help(
-                "consider using `.split_at_mut(position)` or similar method to obtain \
-                     two mutable non-overlapping sub-slices",
-            )
-            .help("consider using `.swap(index_1, index_2)` to swap elements at the specified indices");
+            let mut note_default_suggestion = || {
+                err.help(
+                    "consider using `.split_at_mut(position)` or similar method to obtain \
+                         two mutable non-overlapping sub-slices",
+                )
+                .help("consider using `.swap(index_1, index_2)` to swap elements at the specified indices");
+            };
+
+            let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else {
+                note_default_suggestion();
+                return;
+            };
+
+            let mut expr_finder =
+                FindExprBySpan::new(self.body.local_decls[*index1].source_info.span);
+            expr_finder.visit_expr(hir.body(body_id).value);
+            let Some(index1) = expr_finder.result else {
+                note_default_suggestion();
+                return;
+            };
+
+            expr_finder = FindExprBySpan::new(self.body.local_decls[*index2].source_info.span);
+            expr_finder.visit_expr(hir.body(body_id).value);
+            let Some(index2) = expr_finder.result else {
+                note_default_suggestion();
+                return;
+            };
+
+            let sm = tcx.sess.source_map();
+
+            let Ok(index1_str) = sm.span_to_snippet(index1.span) else {
+                note_default_suggestion();
+                return;
+            };
+
+            let Ok(index2_str) = sm.span_to_snippet(index2.span) else {
+                note_default_suggestion();
+                return;
+            };
+
+            let Some(object) = hir.parent_id_iter(index1.hir_id).find_map(|id| {
+                if let hir::Node::Expr(expr) = tcx.hir_node(id)
+                    && let hir::ExprKind::Index(obj, ..) = expr.kind
+                {
+                    Some(obj)
+                } else {
+                    None
+                }
+            }) else {
+                note_default_suggestion();
+                return;
+            };
+
+            let Ok(obj_str) = sm.span_to_snippet(object.span) else {
+                note_default_suggestion();
+                return;
+            };
+
+            let Some(swap_call) = hir.parent_id_iter(object.hir_id).find_map(|id| {
+                if let hir::Node::Expr(call) = tcx.hir_node(id)
+                    && let hir::ExprKind::Call(callee, ..) = call.kind
+                    && let hir::ExprKind::Path(qpath) = callee.kind
+                    && let hir::QPath::Resolved(None, res) = qpath
+                    && let hir::def::Res::Def(_, did) = res.res
+                    && tcx.is_diagnostic_item(sym::mem_swap, did)
+                {
+                    Some(call)
+                } else {
+                    None
+                }
+            }) else {
+                note_default_suggestion();
+                return;
+            };
+
+            err.span_suggestion(
+                swap_call.span,
+                "use `.swap()` to swap elements at the specified indices instead",
+                format!("{obj_str}.swap({index1_str}, {index2_str})"),
+                Applicability::MachineApplicable,
+            );
         }
     }
 
diff --git a/tests/ui/suggestions/suggest-slice-swap.fixed b/tests/ui/suggestions/suggest-slice-swap.fixed
new file mode 100644
index 00000000000..05b7ec26379
--- /dev/null
+++ b/tests/ui/suggestions/suggest-slice-swap.fixed
@@ -0,0 +1,9 @@
+// run-rustfix
+#![allow(dead_code)]
+
+fn swap(arr: &mut [u32; 2]) {
+    arr.swap(1, 0);
+    //~^ ERROR cannot borrow `arr[_]` as mutable more than once at a time
+}
+
+fn main() {}
diff --git a/tests/ui/suggestions/suggest-slice-swap.rs b/tests/ui/suggestions/suggest-slice-swap.rs
new file mode 100644
index 00000000000..9f3659aac16
--- /dev/null
+++ b/tests/ui/suggestions/suggest-slice-swap.rs
@@ -0,0 +1,9 @@
+// run-rustfix
+#![allow(dead_code)]
+
+fn swap(arr: &mut [u32; 2]) {
+    std::mem::swap(&mut arr[0], &mut arr[1]);
+    //~^ ERROR cannot borrow `arr[_]` as mutable more than once at a time
+}
+
+fn main() {}
diff --git a/tests/ui/suggestions/suggest-slice-swap.stderr b/tests/ui/suggestions/suggest-slice-swap.stderr
new file mode 100644
index 00000000000..2840fc0a761
--- /dev/null
+++ b/tests/ui/suggestions/suggest-slice-swap.stderr
@@ -0,0 +1,17 @@
+error[E0499]: cannot borrow `arr[_]` as mutable more than once at a time
+  --> $DIR/suggest-slice-swap.rs:5:33
+   |
+LL |     std::mem::swap(&mut arr[0], &mut arr[1]);
+   |     -------------- -----------  ^^^^^^^^^^^ second mutable borrow occurs here
+   |     |              |
+   |     |              first mutable borrow occurs here
+   |     first borrow later used by call
+   |
+help: use `.swap()` to swap elements at the specified indices instead
+   |
+LL |     arr.swap(1, 0);
+   |     ~~~~~~~~~~~~~~
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0499`.