about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs23
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs12
-rw-r--r--tests/ui/moves/needs-clone-through-deref.fixed2
-rw-r--r--tests/ui/moves/needs-clone-through-deref.stderr4
-rw-r--r--tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.fixed2
-rw-r--r--tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr4
6 files changed, 39 insertions, 8 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 62e16d445c6..cf550a2e5d8 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -987,6 +987,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         can_suggest_clone
     }
 
+    pub(crate) fn clone_on_reference(&self, expr: &hir::Expr<'_>) -> Option<Span> {
+        let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
+        if let hir::ExprKind::MethodCall(segment, rcvr, args, span) = expr.kind
+            && let Some(expr_ty) = typeck_results.node_type_opt(expr.hir_id)
+            && let Some(rcvr_ty) = typeck_results.node_type_opt(rcvr.hir_id)
+            && rcvr_ty == expr_ty
+            && segment.ident.name == sym::clone
+            && args.is_empty()
+        {
+            Some(span)
+        } else {
+            None
+        }
+    }
+
     fn suggest_cloning(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, expr: &hir::Expr<'_>, span: Span) {
         let tcx = self.infcx.tcx;
         // Try to find predicates on *generic params* that would allow copying `ty`
@@ -1654,6 +1669,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         );
     }
 
+    pub(crate) fn find_expr(&self, span: Span) -> Option<&hir::Expr<'_>> {
+        let tcx = self.infcx.tcx;
+        let body_id = tcx.hir_node(self.mir_hir_id()).body_id()?;
+        let mut expr_finder = FindExprBySpan::new(span);
+        expr_finder.visit_expr(tcx.hir().body(body_id).value);
+        expr_finder.result
+    }
+
     fn suggest_slice_method_if_applicable(
         &self,
         err: &mut Diag<'_>,
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 0106e285604..5425161ed48 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -1212,13 +1212,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                                 .iter_projections()
                                 .any(|(_, elem)| matches!(elem, ProjectionElem::Deref))
                             {
+                                let (start, end) = if let Some(expr) = self.find_expr(move_span)
+                                    && let Some(_) = self.clone_on_reference(expr)
+                                    && let hir::ExprKind::MethodCall(_, rcvr, _, _) = expr.kind
+                                {
+                                    (move_span.shrink_to_lo(), move_span.with_lo(rcvr.span.hi()))
+                                } else {
+                                    (move_span.shrink_to_lo(), move_span.shrink_to_hi())
+                                };
                                 vec![
                                     // We use the fully-qualified path because `.clone()` can
                                     // sometimes choose `<&T as Clone>` instead of `<T as Clone>`
                                     // when going through auto-deref, so this ensures that doesn't
                                     // happen, causing suggestions for `.clone().clone()`.
-                                    (move_span.shrink_to_lo(), format!("<{ty} as Clone>::clone(&")),
-                                    (move_span.shrink_to_hi(), ")".to_string()),
+                                    (start, format!("<{ty} as Clone>::clone(&")),
+                                    (end, ")".to_string()),
                                 ]
                             } else {
                                 vec![(move_span.shrink_to_hi(), ".clone()".to_string())]
diff --git a/tests/ui/moves/needs-clone-through-deref.fixed b/tests/ui/moves/needs-clone-through-deref.fixed
index 43ea15d1b63..8b201c4720d 100644
--- a/tests/ui/moves/needs-clone-through-deref.fixed
+++ b/tests/ui/moves/needs-clone-through-deref.fixed
@@ -12,7 +12,7 @@ impl Deref for S {
 impl S {
     fn foo(&self) {
         // `self.clone()` returns `&S`, not `Vec`
-        for _ in <Vec<usize> as Clone>::clone(&self.clone()).into_iter() {} //~ ERROR cannot move out of dereference of `S`
+        for _ in <Vec<usize> as Clone>::clone(&self).into_iter() {} //~ ERROR cannot move out of dereference of `S`
     }
 }
 fn main() {}
diff --git a/tests/ui/moves/needs-clone-through-deref.stderr b/tests/ui/moves/needs-clone-through-deref.stderr
index ff92f32e8d2..1f9aefeb4dd 100644
--- a/tests/ui/moves/needs-clone-through-deref.stderr
+++ b/tests/ui/moves/needs-clone-through-deref.stderr
@@ -10,8 +10,8 @@ note: `into_iter` takes ownership of the receiver `self`, which moves value
   --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
 help: you can `clone` the value and consume it, but this might not be your desired behavior
    |
-LL |         for _ in <Vec<usize> as Clone>::clone(&self.clone()).into_iter() {}
-   |                  ++++++++++++++++++++++++++++++            +
+LL |         for _ in <Vec<usize> as Clone>::clone(&self).into_iter() {}
+   |                  ++++++++++++++++++++++++++++++    ~
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.fixed b/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.fixed
index b3eae0b22b6..cdc86f61d24 100644
--- a/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.fixed
+++ b/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.fixed
@@ -18,7 +18,7 @@ pub fn hashmap_copy<T, U>(
     map: &HashMap<T, U, Hash128_1>,
 ) where T: Hash + Clone, U: Clone
 {
-    let mut copy: Vec<U> = <HashMap<T, U, Hash128_1> as Clone>::clone(&map.clone()).into_values().collect(); //~ ERROR
+    let mut copy: Vec<U> = <HashMap<T, U, Hash128_1> as Clone>::clone(&map).into_values().collect(); //~ ERROR
 }
 
 pub fn make_map() -> HashMap<String, i64, Hash128_1>
diff --git a/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr b/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr
index 403daf8ff7c..755bbc5c21b 100644
--- a/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr
+++ b/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr
@@ -10,8 +10,8 @@ note: `HashMap::<K, V, S>::into_values` takes ownership of the receiver `self`,
   --> $SRC_DIR/std/src/collections/hash/map.rs:LL:COL
 help: you could `clone` the value and consume it, if the `Hash128_1: Clone` trait bound could be satisfied
    |
-LL |     let mut copy: Vec<U> = <HashMap<T, U, Hash128_1> as Clone>::clone(&map.clone()).into_values().collect();
-   |                            ++++++++++++++++++++++++++++++++++++++++++++           +
+LL |     let mut copy: Vec<U> = <HashMap<T, U, Hash128_1> as Clone>::clone(&map).into_values().collect();
+   |                            ++++++++++++++++++++++++++++++++++++++++++++   ~
 help: consider annotating `Hash128_1` with `#[derive(Clone)]`
    |
 LL + #[derive(Clone)]