about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_borrowck/messages.ftl6
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs424
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs97
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/move_errors.rs22
-rw-r--r--tests/ui/associated-types/associated-types-outlives.rs4
-rw-r--r--tests/ui/associated-types/associated-types-outlives.stderr5
-rw-r--r--tests/ui/associated-types/issue-25700.stderr6
-rw-r--r--tests/ui/augmented-assignments.rs2
-rw-r--r--tests/ui/augmented-assignments.stderr9
-rw-r--r--tests/ui/borrowck/borrow-tuple-fields.stderr12
-rw-r--r--tests/ui/borrowck/borrowck-bad-nested-calls-move.stderr12
-rw-r--r--tests/ui/borrowck/borrowck-closures-slice-patterns.stderr11
-rw-r--r--tests/ui/borrowck/borrowck-field-sensitivity.stderr12
-rw-r--r--tests/ui/borrowck/borrowck-fn-in-const-a.rs1
-rw-r--r--tests/ui/borrowck/borrowck-fn-in-const-a.stderr6
-rw-r--r--tests/ui/borrowck/borrowck-in-static.rs2
-rw-r--r--tests/ui/borrowck/borrowck-in-static.stderr5
-rw-r--r--tests/ui/borrowck/borrowck-issue-2657-2.fixed2
-rw-r--r--tests/ui/borrowck/borrowck-issue-2657-2.stderr5
-rw-r--r--tests/ui/borrowck/borrowck-issue-48962.stderr5
-rw-r--r--tests/ui/borrowck/borrowck-loan-blocks-move-cc.stderr12
-rw-r--r--tests/ui/borrowck/borrowck-loan-blocks-move.stderr6
-rw-r--r--tests/ui/borrowck/borrowck-move-from-subpath-of-borrowed-path.stderr6
-rw-r--r--tests/ui/borrowck/borrowck-move-from-unsafe-ptr.stderr5
-rw-r--r--tests/ui/borrowck/borrowck-move-mut-base-ptr.stderr6
-rw-r--r--tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.fixed2
-rw-r--r--tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr4
-rw-r--r--tests/ui/borrowck/borrowck-move-out-of-overloaded-deref.stderr5
-rw-r--r--tests/ui/borrowck/borrowck-move-out-of-static-item.rs3
-rw-r--r--tests/ui/borrowck/borrowck-move-out-of-static-item.stderr8
-rw-r--r--tests/ui/borrowck/borrowck-move-subcomponent.stderr6
-rw-r--r--tests/ui/borrowck/borrowck-multiple-captures.stderr18
-rw-r--r--tests/ui/borrowck/borrowck-overloaded-call.stderr6
-rw-r--r--tests/ui/borrowck/borrowck-overloaded-index-move-from-vec.stderr4
-rw-r--r--tests/ui/borrowck/borrowck-struct-update-with-dtor.rs61
-rw-r--r--tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr198
-rw-r--r--tests/ui/borrowck/borrowck-unary-move.stderr6
-rw-r--r--tests/ui/borrowck/borrowck-unboxed-closures.stderr12
-rw-r--r--tests/ui/borrowck/borrowck-vec-pattern-nesting.rs3
-rw-r--r--tests/ui/borrowck/borrowck-vec-pattern-nesting.stderr20
-rw-r--r--tests/ui/borrowck/clone-on-ref.stderr5
-rw-r--r--tests/ui/borrowck/clone-span-on-try-operator.fixed2
-rw-r--r--tests/ui/borrowck/clone-span-on-try-operator.stderr5
-rw-r--r--tests/ui/borrowck/issue-101119.stderr14
-rw-r--r--tests/ui/borrowck/issue-103624.stderr6
-rw-r--r--tests/ui/borrowck/issue-119915-bad-clone-suggestion.stderr5
-rw-r--r--tests/ui/borrowck/issue-17718-static-move.stderr5
-rw-r--r--tests/ui/borrowck/issue-20801.stderr20
-rw-r--r--tests/ui/borrowck/issue-64453.stderr5
-rw-r--r--tests/ui/borrowck/issue-87456-point-to-closure.stderr4
-rw-r--r--tests/ui/borrowck/move-error-in-promoted-2.stderr6
-rw-r--r--tests/ui/borrowck/move-error-in-promoted.stderr5
-rw-r--r--tests/ui/borrowck/move-error-snippets.stderr5
-rw-r--r--tests/ui/borrowck/move-from-union-field-issue-66500.stderr24
-rw-r--r--tests/ui/borrowck/move-in-static-initializer-issue-38520.stderr12
-rw-r--r--tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.fixed2
-rw-r--r--tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr4
-rw-r--r--tests/ui/box/leak-alloc.stderr6
-rw-r--r--tests/ui/btreemap/btreemap_dropck.stderr6
-rw-r--r--tests/ui/check-static-values-constraints.stderr4
-rw-r--r--tests/ui/derives/deriving-with-repr-packed-move-errors.stderr96
-rw-r--r--tests/ui/derives/deriving-with-repr-packed.stderr5
-rw-r--r--tests/ui/dropck/drop-with-active-borrows-1.stderr5
-rw-r--r--tests/ui/error-codes/E0504.stderr6
-rw-r--r--tests/ui/error-codes/E0505.stderr6
-rw-r--r--tests/ui/error-codes/E0507.stderr5
-rw-r--r--tests/ui/error-codes/E0508-fail.stderr5
-rw-r--r--tests/ui/error-codes/E0508.stderr5
-rw-r--r--tests/ui/error-codes/E0509.stderr5
-rw-r--r--tests/ui/fn/implied-bounds-unnorm-associated-type-4.stderr6
-rw-r--r--tests/ui/fn/implied-bounds-unnorm-associated-type-5.stderr6
-rw-r--r--tests/ui/fn/implied-bounds-unnorm-associated-type.stderr6
-rw-r--r--tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr5
-rw-r--r--tests/ui/issues/issue-17385.stderr12
-rw-r--r--tests/ui/issues/issue-24357.rs2
-rw-r--r--tests/ui/issues/issue-24357.stderr6
-rw-r--r--tests/ui/issues/issue-2590.stderr5
-rw-r--r--tests/ui/issues/issue-40402-ref-hints/issue-40402-1.stderr4
-rw-r--r--tests/ui/issues/issue-52262.stderr6
-rw-r--r--tests/ui/mir/issue-102389.stderr6
-rw-r--r--tests/ui/moves/issue-72649-uninit-in-loop.rs4
-rw-r--r--tests/ui/moves/issue-72649-uninit-in-loop.stderr36
-rw-r--r--tests/ui/moves/issue-75904-move-closure-loop.stderr7
-rw-r--r--tests/ui/moves/move-fn-self-receiver.stderr11
-rw-r--r--tests/ui/moves/move-out-of-array-1.stderr6
-rw-r--r--tests/ui/moves/moves-based-on-type-capture-clause-bad.rs2
-rw-r--r--tests/ui/moves/moves-based-on-type-capture-clause-bad.stderr4
-rw-r--r--tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr5
-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
-rw-r--r--tests/ui/moves/suggest-clone.fixed2
-rw-r--r--tests/ui/moves/suggest-clone.stderr4
-rw-r--r--tests/ui/nll/cannot-move-block-spans.stderr42
-rw-r--r--tests/ui/nll/closure-access-spans.stderr16
-rw-r--r--tests/ui/nll/closure-move-spans.fixed23
-rw-r--r--tests/ui/nll/closure-move-spans.rs2
-rw-r--r--tests/ui/nll/closure-move-spans.stderr21
-rw-r--r--tests/ui/nll/closures-in-loops.stderr6
-rw-r--r--tests/ui/nll/issue-21232-partial-init-and-use.stderr12
-rw-r--r--tests/ui/nll/issue-27282-move-match-input-into-guard.stderr10
-rw-r--r--tests/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.stderr4
-rw-r--r--tests/ui/nll/issue-52086.stderr10
-rw-r--r--tests/ui/nll/issue-52663-span-decl-captured-variable.stderr5
-rw-r--r--tests/ui/nll/move-errors.stderr31
-rw-r--r--tests/ui/once-cant-call-twice-on-heap.stderr12
-rw-r--r--tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr10
-rw-r--r--tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr8
-rw-r--r--tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr8
-rw-r--r--tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr10
-rw-r--r--tests/ui/span/send-is-not-static-std-sync.stderr18
-rw-r--r--tests/ui/suggestions/borrow-for-loop-head.stderr6
-rw-r--r--tests/ui/suggestions/for-i-in-vec.fixed6
-rw-r--r--tests/ui/suggestions/for-i-in-vec.stderr13
-rw-r--r--tests/ui/suggestions/option-content-move.fixed4
-rw-r--r--tests/ui/suggestions/option-content-move.stderr8
-rw-r--r--tests/ui/trivial-bounds/trivial-bounds-leak-copy.stderr6
-rw-r--r--tests/ui/try-block/try-block-bad-lifetime.stderr5
-rw-r--r--tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr10
-rw-r--r--tests/ui/union/union-borrow-move-parent-sibling.stderr5
-rw-r--r--tests/ui/union/union-move.stderr15
-rw-r--r--tests/ui/variance/variance-issue-20533.rs19
-rw-r--r--tests/ui/variance/variance-issue-20533.stderr61
124 files changed, 1742 insertions, 143 deletions
diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl
index 587536e1f9a..c14a617eb91 100644
--- a/compiler/rustc_borrowck/messages.ftl
+++ b/compiler/rustc_borrowck/messages.ftl
@@ -87,6 +87,12 @@ borrowck_move_unsized =
 borrowck_moved_a_fn_once_in_call =
     this value implements `FnOnce`, which causes it to be moved when called
 
+borrowck_moved_a_fn_once_in_call_call =
+    `FnOnce` closures can only be called once
+
+borrowck_moved_a_fn_once_in_call_def =
+    `{$ty}` is made to be an `FnOnce` closure here
+
 borrowck_moved_due_to_await =
     {$place_name} {$is_partial ->
         [true] partially moved
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index c5458e3ff69..48cd9c268a1 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -203,13 +203,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
                 if !seen_spans.contains(&move_span) {
                     if !closure {
-                        self.suggest_ref_or_clone(
-                            mpi,
-                            move_span,
-                            &mut err,
-                            &mut in_pattern,
-                            move_spans,
-                        );
+                        self.suggest_ref_or_clone(mpi, &mut err, &mut in_pattern, move_spans);
                     }
 
                     let msg_opt = CapturedMessageOpt {
@@ -283,7 +277,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 Some(name) => format!("`{name}`"),
                 None => "value".to_owned(),
             };
-            if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, &note_msg) {
+            if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, &note_msg)
+                || if let UseSpans::FnSelfUse { kind, .. } = use_spans
+                    && let CallKind::FnCall { fn_trait_id, self_ty } = kind
+                    && let ty::Param(_) = self_ty.kind()
+                    && ty == self_ty
+                    && Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait()
+                {
+                    // this is a type parameter `T: FnOnce()`, don't suggest `T: FnOnce() + Clone`.
+                    true
+                } else {
+                    false
+                }
+            {
                 // Suppress the next suggestion since we don't want to put more bounds onto
                 // something that already has `Fn`-like bounds (or is a closure), so we can't
                 // restrict anyways.
@@ -339,18 +345,28 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     fn suggest_ref_or_clone(
         &self,
         mpi: MovePathIndex,
-        move_span: Span,
         err: &mut Diag<'tcx>,
         in_pattern: &mut bool,
         move_spans: UseSpans<'_>,
     ) {
+        let move_span = match move_spans {
+            UseSpans::ClosureUse { capture_kind_span, .. } => capture_kind_span,
+            _ => move_spans.args_or_use(),
+        };
         struct ExpressionFinder<'hir> {
             expr_span: Span,
             expr: Option<&'hir hir::Expr<'hir>>,
             pat: Option<&'hir hir::Pat<'hir>>,
             parent_pat: Option<&'hir hir::Pat<'hir>>,
+            hir: rustc_middle::hir::map::Map<'hir>,
         }
         impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> {
+            type NestedFilter = OnlyBodies;
+
+            fn nested_visit_map(&mut self) -> Self::Map {
+                self.hir
+            }
+
             fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
                 if e.span == self.expr_span {
                     self.expr = Some(e);
@@ -385,8 +401,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             let expr = hir.body(body_id).value;
             let place = &self.move_data.move_paths[mpi].place;
             let span = place.as_local().map(|local| self.body.local_decls[local].source_info.span);
-            let mut finder =
-                ExpressionFinder { expr_span: move_span, expr: None, pat: None, parent_pat: None };
+            let mut finder = ExpressionFinder {
+                expr_span: move_span,
+                expr: None,
+                pat: None,
+                parent_pat: None,
+                hir,
+            };
             finder.visit_expr(expr);
             if let Some(span) = span
                 && let Some(expr) = finder.expr
@@ -467,16 +488,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 } else if let UseSpans::ClosureUse {
                     closure_kind:
                         ClosureKind::Coroutine(CoroutineKind::Desugared(_, CoroutineSource::Block)),
-                    args_span: _,
-                    capture_kind_span: _,
-                    path_span,
+                    ..
                 } = move_spans
                 {
-                    self.suggest_cloning(err, ty, expr, path_span);
+                    self.suggest_cloning(err, ty, expr, None);
                 } else if self.suggest_hoisting_call_outside_loop(err, expr) {
                     // The place where the the type moves would be misleading to suggest clone.
                     // #121466
-                    self.suggest_cloning(err, ty, expr, move_span);
+                    self.suggest_cloning(err, ty, expr, None);
                 }
             }
             if let Some(pat) = finder.pat {
@@ -1031,8 +1050,272 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         can_suggest_clone
     }
 
-    fn suggest_cloning(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, expr: &hir::Expr<'_>, span: Span) {
+    /// We have `S { foo: val, ..base }`, and we suggest instead writing
+    /// `S { foo: val, bar: base.bar.clone(), .. }` when valid.
+    fn suggest_cloning_on_functional_record_update(
+        &self,
+        err: &mut Diag<'_>,
+        ty: Ty<'tcx>,
+        expr: &'cx hir::Expr<'cx>,
+    ) {
+        let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
+        let hir::ExprKind::Struct(struct_qpath, fields, Some(base)) = expr.kind else { return };
+        let hir::QPath::Resolved(_, path) = struct_qpath else { return };
+        let hir::def::Res::Def(_, def_id) = path.res else { return };
+        let Some(expr_ty) = typeck_results.node_type_opt(expr.hir_id) else { return };
+        let ty::Adt(def, args) = expr_ty.kind() else { return };
+        let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = base.kind else { return };
+        let (hir::def::Res::Local(_)
+        | hir::def::Res::Def(
+            DefKind::Const | DefKind::ConstParam | DefKind::Static { .. } | DefKind::AssocConst,
+            _,
+        )) = path.res
+        else {
+            return;
+        };
+        let Ok(base_str) = self.infcx.tcx.sess.source_map().span_to_snippet(base.span) else {
+            return;
+        };
+
+        // 1. look for the fields of type `ty`.
+        // 2. check if they are clone and add them to suggestion
+        // 3. check if there are any values left to `..` and remove it if not
+        // 4. emit suggestion to clone the field directly as `bar: base.bar.clone()`
+
+        let mut final_field_count = fields.len();
+        let Some(variant) = def.variants().iter().find(|variant| variant.def_id == def_id) else {
+            // When we have an enum, look for the variant that corresponds to the variant the user
+            // wrote.
+            return;
+        };
+        let mut sugg = vec![];
+        for field in &variant.fields {
+            // In practice unless there are more than one field with the same type, we'll be
+            // suggesting a single field at a type, because we don't aggregate multiple borrow
+            // checker errors involving the functional record update sytnax into a single one.
+            let field_ty = field.ty(self.infcx.tcx, args);
+            let ident = field.ident(self.infcx.tcx);
+            if field_ty == ty && fields.iter().all(|field| field.ident.name != ident.name) {
+                // Suggest adding field and cloning it.
+                sugg.push(format!("{ident}: {base_str}.{ident}.clone()"));
+                final_field_count += 1;
+            }
+        }
+        let (span, sugg) = match fields {
+            [.., last] => (
+                if final_field_count == variant.fields.len() {
+                    // We'll remove the `..base` as there aren't any fields left.
+                    last.span.shrink_to_hi().with_hi(base.span.hi())
+                } else {
+                    last.span.shrink_to_hi()
+                },
+                format!(", {}", sugg.join(", ")),
+            ),
+            // Account for no fields in suggestion span.
+            [] => (
+                expr.span.with_lo(struct_qpath.span().hi()),
+                if final_field_count == variant.fields.len() {
+                    // We'll remove the `..base` as there aren't any fields left.
+                    format!(" {{ {} }}", sugg.join(", "))
+                } else {
+                    format!(" {{ {}, ..{base_str} }}", sugg.join(", "))
+                },
+            ),
+        };
+        let prefix = if !self.implements_clone(ty) {
+            let msg = format!("`{ty}` doesn't implement `Copy` or `Clone`");
+            if let ty::Adt(def, _) = ty.kind() {
+                err.span_note(self.infcx.tcx.def_span(def.did()), msg);
+            } else {
+                err.note(msg);
+            }
+            format!("if `{ty}` implemented `Clone`, you could ")
+        } else {
+            String::new()
+        };
+        let msg = format!(
+            "{prefix}clone the value from the field instead of using the functional record update \
+             syntax",
+        );
+        err.span_suggestion_verbose(span, msg, sugg, Applicability::MachineApplicable);
+    }
+
+    pub(crate) fn suggest_cloning(
+        &self,
+        err: &mut Diag<'_>,
+        ty: Ty<'tcx>,
+        mut expr: &'cx hir::Expr<'cx>,
+        mut other_expr: Option<&'cx hir::Expr<'cx>>,
+    ) {
+        if let hir::ExprKind::Struct(_, _, Some(_)) = expr.kind {
+            // We have `S { foo: val, ..base }`. In `check_aggregate_rvalue` we have a single
+            // `Location` that covers both the `S { ... }` literal, all of its fields and the
+            // `base`. If the move happens because of `S { foo: val, bar: base.bar }` the `expr`
+            //  will already be correct. Instead, we see if we can suggest writing.
+            self.suggest_cloning_on_functional_record_update(err, ty, expr);
+            return;
+        }
+
+        if let Some(some_other_expr) = other_expr
+            && let Some(parent_binop) =
+                self.infcx.tcx.hir().parent_iter(expr.hir_id).find_map(|n| {
+                    if let (hir_id, hir::Node::Expr(e)) = n
+                        && let hir::ExprKind::AssignOp(_binop, target, _arg) = e.kind
+                        && target.hir_id == expr.hir_id
+                    {
+                        Some(hir_id)
+                    } else {
+                        None
+                    }
+                })
+            && let Some(other_parent_binop) =
+                self.infcx.tcx.hir().parent_iter(some_other_expr.hir_id).find_map(|n| {
+                    if let (hir_id, hir::Node::Expr(expr)) = n
+                        && let hir::ExprKind::AssignOp(..) = expr.kind
+                    {
+                        Some(hir_id)
+                    } else {
+                        None
+                    }
+                })
+            && parent_binop == other_parent_binop
+        {
+            // Explicitly look for `expr += other_expr;` and avoid suggesting
+            // `expr.clone() += other_expr;`, instead suggesting `expr += other_expr.clone();`.
+            other_expr = Some(expr);
+            expr = some_other_expr;
+        }
+        'outer: {
+            if let ty::Ref(..) = ty.kind() {
+                // We check for either `let binding = foo(expr, other_expr);` or
+                // `foo(expr, other_expr);` and if so we don't suggest an incorrect
+                // `foo(expr, other_expr).clone()`
+                if let Some(other_expr) = other_expr
+                    && let Some(parent_let) =
+                        self.infcx.tcx.hir().parent_iter(expr.hir_id).find_map(|n| {
+                            if let (hir_id, hir::Node::LetStmt(_) | hir::Node::Stmt(_)) = n {
+                                Some(hir_id)
+                            } else {
+                                None
+                            }
+                        })
+                    && let Some(other_parent_let) =
+                        self.infcx.tcx.hir().parent_iter(other_expr.hir_id).find_map(|n| {
+                            if let (hir_id, hir::Node::LetStmt(_) | hir::Node::Stmt(_)) = n {
+                                Some(hir_id)
+                            } else {
+                                None
+                            }
+                        })
+                    && parent_let == other_parent_let
+                {
+                    // Explicitly check that we don't have `foo(&*expr, other_expr)`, as cloning the
+                    // result of `foo(...)` won't help.
+                    break 'outer;
+                }
+
+                // We're suggesting `.clone()` on an borrowed value. See if the expression we have
+                // is an argument to a function or method call, and try to suggest cloning the
+                // *result* of the call, instead of the argument. This is closest to what people
+                // would actually be looking for in most cases, with maybe the exception of things
+                // like `fn(T) -> T`, but even then it is reasonable.
+                let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
+                let mut prev = expr;
+                while let hir::Node::Expr(parent) = self.infcx.tcx.parent_hir_node(prev.hir_id) {
+                    if let hir::ExprKind::Call(..) | hir::ExprKind::MethodCall(..) = parent.kind
+                        && let Some(call_ty) = typeck_results.node_type_opt(parent.hir_id)
+                        && let call_ty = call_ty.peel_refs()
+                        && (!call_ty
+                            .walk()
+                            .any(|t| matches!(t.unpack(), ty::GenericArgKind::Lifetime(_)))
+                            || if let ty::Alias(ty::Projection, _) = call_ty.kind() {
+                                // FIXME: this isn't quite right with lifetimes on assoc types,
+                                // but ignore for now. We will only suggest cloning if
+                                // `<Ty as Trait>::Assoc: Clone`, which should keep false positives
+                                // down to a managable ammount.
+                                true
+                            } else {
+                                false
+                            })
+                        && self.implements_clone(call_ty)
+                        && self.suggest_cloning_inner(err, call_ty, parent)
+                    {
+                        return;
+                    }
+                    prev = parent;
+                }
+            }
+        }
+        let ty = ty.peel_refs();
+        if self.implements_clone(ty) {
+            self.suggest_cloning_inner(err, ty, expr);
+        } else if let ty::Adt(def, args) = ty.kind()
+            && def.did().as_local().is_some()
+            && def.variants().iter().all(|variant| {
+                variant
+                    .fields
+                    .iter()
+                    .all(|field| self.implements_clone(field.ty(self.infcx.tcx, args)))
+            })
+        {
+            err.span_note(
+                self.infcx.tcx.def_span(def.did()),
+                format!("if `{ty}` implemented `Clone`, you could clone the value"),
+            );
+        }
+    }
+
+    fn implements_clone(&self, ty: Ty<'tcx>) -> bool {
+        let Some(clone_trait_def) = self.infcx.tcx.lang_items().clone_trait() else { return false };
+        self.infcx
+            .type_implements_trait(clone_trait_def, [ty], self.param_env)
+            .must_apply_modulo_regions()
+    }
+
+    /// Given an expression, check if it is a method call `foo.clone()`, where `foo` and
+    /// `foo.clone()` both have the same type, returning the span for `.clone()` if so.
+    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 in_move_closure(&self, expr: &hir::Expr<'_>) -> bool {
+        for (_, node) in self.infcx.tcx.hir().parent_iter(expr.hir_id) {
+            if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(closure), .. }) = node
+                && let hir::CaptureBy::Value { .. } = closure.capture_clause
+            {
+                // `move || x.clone()` will not work. FIXME: suggest `let y = x.clone(); move || y`
+                return true;
+            }
+        }
+        false
+    }
+
+    fn suggest_cloning_inner(
+        &self,
+        err: &mut Diag<'_>,
+        ty: Ty<'tcx>,
+        expr: &hir::Expr<'_>,
+    ) -> bool {
         let tcx = self.infcx.tcx;
+        if let Some(_) = self.clone_on_reference(expr) {
+            // Avoid redundant clone suggestion already suggested in `explain_captures`.
+            // See `tests/ui/moves/needs-clone-through-deref.rs`
+            return false;
+        }
+        if self.in_move_closure(expr) {
+            return false;
+        }
         // Try to find predicates on *generic params* that would allow copying `ty`
         let suggestion =
             if let Some(symbol) = tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
@@ -1040,27 +1323,39 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             } else {
                 ".clone()".to_owned()
             };
-        if let Some(clone_trait_def) = tcx.lang_items().clone_trait()
-            && self
-                .infcx
-                .type_implements_trait(clone_trait_def, [ty], self.param_env)
-                .must_apply_modulo_regions()
+        let mut sugg = Vec::with_capacity(2);
+        let mut inner_expr = expr;
+        // Remove uses of `&` and `*` when suggesting `.clone()`.
+        while let hir::ExprKind::AddrOf(.., inner) | hir::ExprKind::Unary(hir::UnOp::Deref, inner) =
+            &inner_expr.kind
         {
-            let msg = if let ty::Adt(def, _) = ty.kind()
-                && [tcx.get_diagnostic_item(sym::Arc), tcx.get_diagnostic_item(sym::Rc)]
-                    .contains(&Some(def.did()))
-            {
-                "clone the value to increment its reference count"
-            } else {
-                "consider cloning the value if the performance cost is acceptable"
-            };
-            err.span_suggestion_verbose(
-                span.shrink_to_hi(),
-                msg,
-                suggestion,
-                Applicability::MachineApplicable,
-            );
+            if let hir::ExprKind::AddrOf(_, hir::Mutability::Mut, _) = inner_expr.kind {
+                // We assume that `&mut` refs are desired for their side-effects, so cloning the
+                // value wouldn't do what the user wanted.
+                return false;
+            }
+            inner_expr = inner;
+        }
+        if inner_expr.span.lo() != expr.span.lo() {
+            sugg.push((expr.span.with_hi(inner_expr.span.lo()), String::new()));
         }
+        let span = if inner_expr.span.hi() != expr.span.hi() {
+            // Account for `(*x)` to suggest `x.clone()`.
+            expr.span.with_lo(inner_expr.span.hi())
+        } else {
+            expr.span.shrink_to_hi()
+        };
+        sugg.push((span, suggestion));
+        let msg = if let ty::Adt(def, _) = ty.kind()
+            && [tcx.get_diagnostic_item(sym::Arc), tcx.get_diagnostic_item(sym::Rc)]
+                .contains(&Some(def.did()))
+        {
+            "clone the value to increment its reference count"
+        } else {
+            "consider cloning the value if the performance cost is acceptable"
+        };
+        err.multipart_suggestion_verbose(msg, sugg, Applicability::MachineApplicable);
+        true
     }
 
     fn suggest_adding_bounds(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, def_id: DefId, span: Span) {
@@ -1165,6 +1460,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 None,
             );
         self.suggest_copy_for_type_in_cloned_ref(&mut err, place);
+        let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
+        if let Some(expr) = self.find_expr(borrow_span)
+            && let Some(ty) = typeck_results.node_type_opt(expr.hir_id)
+        {
+            self.suggest_cloning(&mut err, ty, expr, self.find_expr(span));
+        }
         self.buffer_error(err);
     }
 
@@ -1586,22 +1887,33 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             }
         }
         for ty in types_to_constrain {
-            self.suggest_adding_bounds(err, ty, clone, body.span);
-            if let ty::Adt(..) = ty.kind() {
-                // The type doesn't implement Clone.
-                let trait_ref = ty::Binder::dummy(ty::TraitRef::new(self.infcx.tcx, clone, [ty]));
-                let obligation = Obligation::new(
-                    self.infcx.tcx,
-                    ObligationCause::dummy(),
-                    self.param_env,
-                    trait_ref,
-                );
-                self.infcx.err_ctxt().suggest_derive(
-                    &obligation,
-                    err,
-                    trait_ref.to_predicate(self.infcx.tcx),
-                );
-            }
+            self.suggest_adding_bounds_or_derive(err, ty, clone, body.span);
+        }
+    }
+
+    pub(crate) fn suggest_adding_bounds_or_derive(
+        &self,
+        err: &mut Diag<'_>,
+        ty: Ty<'tcx>,
+        trait_def_id: DefId,
+        span: Span,
+    ) {
+        self.suggest_adding_bounds(err, ty, trait_def_id, span);
+        if let ty::Adt(..) = ty.kind() {
+            // The type doesn't implement the trait.
+            let trait_ref =
+                ty::Binder::dummy(ty::TraitRef::new(self.infcx.tcx, trait_def_id, [ty]));
+            let obligation = Obligation::new(
+                self.infcx.tcx,
+                ObligationCause::dummy(),
+                self.param_env,
+                trait_ref,
+            );
+            self.infcx.err_ctxt().suggest_derive(
+                &obligation,
+                err,
+                trait_ref.to_predicate(self.infcx.tcx),
+            );
         }
     }
 
@@ -1702,6 +2014,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..dbea317e7bb 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -5,6 +5,7 @@ use crate::session_diagnostics::{
     CaptureVarKind, CaptureVarPathUseCause, OnClosureNote,
 };
 use rustc_errors::{Applicability, Diag};
+use rustc_errors::{DiagCtxt, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, Namespace};
 use rustc_hir::CoroutineKind;
@@ -29,6 +30,8 @@ use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _;
 use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions;
 
+use crate::fluent_generated as fluent;
+
 use super::borrow_set::BorrowData;
 use super::MirBorrowckCtxt;
 
@@ -587,7 +590,7 @@ impl UseSpans<'_> {
     #[allow(rustc::diagnostic_outside_of_impl)]
     pub(super) fn args_subdiag(
         self,
-        dcx: &rustc_errors::DiagCtxt,
+        dcx: &DiagCtxt,
         err: &mut Diag<'_>,
         f: impl FnOnce(Span) -> CaptureArgLabel,
     ) {
@@ -601,7 +604,7 @@ impl UseSpans<'_> {
     #[allow(rustc::diagnostic_outside_of_impl)]
     pub(super) fn var_path_only_subdiag(
         self,
-        dcx: &rustc_errors::DiagCtxt,
+        dcx: &DiagCtxt,
         err: &mut Diag<'_>,
         action: crate::InitializationRequiringAction,
     ) {
@@ -639,7 +642,7 @@ impl UseSpans<'_> {
     #[allow(rustc::diagnostic_outside_of_impl)]
     pub(super) fn var_subdiag(
         self,
-        dcx: &rustc_errors::DiagCtxt,
+        dcx: &DiagCtxt,
         err: &mut Diag<'_>,
         kind: Option<rustc_middle::mir::BorrowKind>,
         f: impl FnOnce(hir::ClosureKind, Span) -> CaptureVarCause,
@@ -1034,7 +1037,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 .map(|n| format!("`{n}`"))
                 .unwrap_or_else(|| "value".to_owned());
             match kind {
-                CallKind::FnCall { fn_trait_id, .. }
+                CallKind::FnCall { fn_trait_id, self_ty }
                     if Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait() =>
                 {
                     err.subdiagnostic(
@@ -1046,7 +1049,79 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                             is_loop_message,
                         },
                     );
-                    err.subdiagnostic(self.dcx(), CaptureReasonNote::FnOnceMoveInCall { var_span });
+                    // Check if the move occurs on a value because of a call on a closure that comes
+                    // from a type parameter `F: FnOnce()`. If so, we provide a targeted `note`:
+                    // ```
+                    // error[E0382]: use of moved value: `blk`
+                    //   --> $DIR/once-cant-call-twice-on-heap.rs:8:5
+                    //    |
+                    // LL | fn foo<F:FnOnce()>(blk: F) {
+                    //    |                    --- move occurs because `blk` has type `F`, which does not implement the `Copy` trait
+                    // LL | blk();
+                    //    | ----- `blk` moved due to this call
+                    // LL | blk();
+                    //    | ^^^ value used here after move
+                    //    |
+                    // note: `FnOnce` closures can only be called once
+                    //   --> $DIR/once-cant-call-twice-on-heap.rs:6:10
+                    //    |
+                    // LL | fn foo<F:FnOnce()>(blk: F) {
+                    //    |        ^^^^^^^^ `F` is made to be an `FnOnce` closure here
+                    // LL | blk();
+                    //    | ----- this value implements `FnOnce`, which causes it to be moved when called
+                    // ```
+                    if let ty::Param(param_ty) = self_ty.kind()
+                        && let generics = self.infcx.tcx.generics_of(self.mir_def_id())
+                        && let param = generics.type_param(param_ty, self.infcx.tcx)
+                        && let Some(hir_generics) = self
+                            .infcx
+                            .tcx
+                            .typeck_root_def_id(self.mir_def_id().to_def_id())
+                            .as_local()
+                            .and_then(|def_id| self.infcx.tcx.hir().get_generics(def_id))
+                        && let spans = hir_generics
+                            .predicates
+                            .iter()
+                            .filter_map(|pred| match pred {
+                                hir::WherePredicate::BoundPredicate(pred) => Some(pred),
+                                _ => None,
+                            })
+                            .filter(|pred| {
+                                if let Some((id, _)) = pred.bounded_ty.as_generic_param() {
+                                    id == param.def_id
+                                } else {
+                                    false
+                                }
+                            })
+                            .flat_map(|pred| pred.bounds)
+                            .filter_map(|bound| {
+                                if let Some(trait_ref) = bound.trait_ref()
+                                    && let Some(trait_def_id) = trait_ref.trait_def_id()
+                                    && trait_def_id == fn_trait_id
+                                {
+                                    Some(bound.span())
+                                } else {
+                                    None
+                                }
+                            })
+                            .collect::<Vec<Span>>()
+                        && !spans.is_empty()
+                    {
+                        let mut span: MultiSpan = spans.clone().into();
+                        for sp in spans {
+                            span.push_span_label(sp, fluent::borrowck_moved_a_fn_once_in_call_def);
+                        }
+                        span.push_span_label(
+                            fn_call_span,
+                            fluent::borrowck_moved_a_fn_once_in_call,
+                        );
+                        err.span_note(span, fluent::borrowck_moved_a_fn_once_in_call_call);
+                    } else {
+                        err.subdiagnostic(
+                            self.dcx(),
+                            CaptureReasonNote::FnOnceMoveInCall { var_span },
+                        );
+                    }
                 }
                 CallKind::Operator { self_arg, trait_id, .. } => {
                     let self_arg = self_arg.unwrap();
@@ -1212,13 +1287,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/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
index 0d1b875cbed..bc02c5be93d 100644
--- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
@@ -435,7 +435,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
 
     fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diag<'_>, span: Span) {
         match error {
-            GroupedMoveError::MovesFromPlace { mut binds_to, move_from, .. } => {
+            GroupedMoveError::MovesFromPlace {
+                mut binds_to, move_from, span: other_span, ..
+            } => {
                 self.add_borrow_suggestions(err, span);
                 if binds_to.is_empty() {
                     let place_ty = move_from.ty(self.body, self.infcx.tcx).ty;
@@ -444,6 +446,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                         None => "value".to_string(),
                     };
 
+                    if let Some(expr) = self.find_expr(span) {
+                        self.suggest_cloning(err, place_ty, expr, self.find_expr(other_span));
+                    }
+
                     err.subdiagnostic(
                         self.dcx(),
                         crate::session_diagnostics::TypeNoCopy::Label {
@@ -468,19 +474,24 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             }
             // No binding. Nothing to suggest.
             GroupedMoveError::OtherIllegalMove { ref original_path, use_spans, .. } => {
-                let span = use_spans.var_or_use();
+                let use_span = use_spans.var_or_use();
                 let place_ty = original_path.ty(self.body, self.infcx.tcx).ty;
                 let place_desc = match self.describe_place(original_path.as_ref()) {
                     Some(desc) => format!("`{desc}`"),
                     None => "value".to_string(),
                 };
+
+                if let Some(expr) = self.find_expr(use_span) {
+                    self.suggest_cloning(err, place_ty, expr, self.find_expr(span));
+                }
+
                 err.subdiagnostic(
                     self.dcx(),
                     crate::session_diagnostics::TypeNoCopy::Label {
                         is_partial_move: false,
                         ty: place_ty,
                         place: &place_desc,
-                        span,
+                        span: use_span,
                     },
                 );
 
@@ -582,6 +593,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
 
             if binds_to.len() == 1 {
                 let place_desc = &format!("`{}`", self.local_names[*local].unwrap());
+
+                if let Some(expr) = self.find_expr(binding_span) {
+                    self.suggest_cloning(err, bind_to.ty, expr, None);
+                }
+
                 err.subdiagnostic(
                     self.dcx(),
                     crate::session_diagnostics::TypeNoCopy::Label {
diff --git a/tests/ui/associated-types/associated-types-outlives.rs b/tests/ui/associated-types/associated-types-outlives.rs
index 55c276280b9..245218067b4 100644
--- a/tests/ui/associated-types/associated-types-outlives.rs
+++ b/tests/ui/associated-types/associated-types-outlives.rs
@@ -3,10 +3,10 @@
 // fn body, causing this (invalid) code to be accepted.
 
 pub trait Foo<'a> {
-    type Bar;
+    type Bar: Clone;
 }
 
-impl<'a, T:'a> Foo<'a> for T {
+impl<'a, T: 'a> Foo<'a> for T {
     type Bar = &'a T;
 }
 
diff --git a/tests/ui/associated-types/associated-types-outlives.stderr b/tests/ui/associated-types/associated-types-outlives.stderr
index deeedd22266..c97af672c33 100644
--- a/tests/ui/associated-types/associated-types-outlives.stderr
+++ b/tests/ui/associated-types/associated-types-outlives.stderr
@@ -10,6 +10,11 @@ LL |         drop(x);
    |              ^ move out of `x` occurs here
 LL |         return f(y);
    |                  - borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         's: loop { y = denormalise(&x).clone(); break }
+   |                                       ++++++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/associated-types/issue-25700.stderr b/tests/ui/associated-types/issue-25700.stderr
index 4e432c0e702..fb0e63c207a 100644
--- a/tests/ui/associated-types/issue-25700.stderr
+++ b/tests/ui/associated-types/issue-25700.stderr
@@ -7,6 +7,12 @@ LL |     drop(t);
    |          - value moved here
 LL |     drop(t);
    |          ^ value used here after move
+   |
+note: if `S<()>` implemented `Clone`, you could clone the value
+  --> $DIR/issue-25700.rs:1:1
+   |
+LL | struct S<T: 'static>(#[allow(dead_code)] Option<&'static T>);
+   | ^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/augmented-assignments.rs b/tests/ui/augmented-assignments.rs
index bd2435a78bf..8b263e03593 100644
--- a/tests/ui/augmented-assignments.rs
+++ b/tests/ui/augmented-assignments.rs
@@ -1,5 +1,6 @@
 use std::ops::AddAssign;
 
+#[derive(Clone)]
 struct Int(i32);
 
 impl AddAssign for Int {
@@ -16,6 +17,7 @@ fn main() {
     x;
     //~^ ERROR cannot move out of `x` because it is borrowed
     //~| move out of `x` occurs here
+    //~| HELP consider cloning
 
     let y = Int(2);
     //~^ HELP consider changing this to be mutable
diff --git a/tests/ui/augmented-assignments.stderr b/tests/ui/augmented-assignments.stderr
index d1096aea279..6b2900dd5d1 100644
--- a/tests/ui/augmented-assignments.stderr
+++ b/tests/ui/augmented-assignments.stderr
@@ -1,5 +1,5 @@
 error[E0505]: cannot move out of `x` because it is borrowed
-  --> $DIR/augmented-assignments.rs:16:5
+  --> $DIR/augmented-assignments.rs:17:5
    |
 LL |     let mut x = Int(1);
    |         ----- binding `x` declared here
@@ -8,9 +8,14 @@ LL |     x
 ...
 LL |     x;
    |     ^ move out of `x` occurs here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     x.clone();
+   |      ++++++++
 
 error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable
-  --> $DIR/augmented-assignments.rs:23:5
+  --> $DIR/augmented-assignments.rs:25:5
    |
 LL |     y
    |     ^ cannot borrow as mutable
diff --git a/tests/ui/borrowck/borrow-tuple-fields.stderr b/tests/ui/borrowck/borrow-tuple-fields.stderr
index e324ebfb50f..8ea7a9a4989 100644
--- a/tests/ui/borrowck/borrow-tuple-fields.stderr
+++ b/tests/ui/borrowck/borrow-tuple-fields.stderr
@@ -10,6 +10,12 @@ LL |     let y = x;
 LL |
 LL |     r.use_ref();
    |     - borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let r = &x.0;
+LL +     let r = x.0.clone();
+   |
 
 error[E0502]: cannot borrow `x.0` as mutable because it is also borrowed as immutable
   --> $DIR/borrow-tuple-fields.rs:18:13
@@ -42,6 +48,12 @@ LL |     let y = x;
    |             ^ move out of `x` occurs here
 LL |     r.use_ref();
    |     - borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let r = &x.0;
+LL +     let r = x.0.clone();
+   |
 
 error[E0502]: cannot borrow `x.0` as mutable because it is also borrowed as immutable
   --> $DIR/borrow-tuple-fields.rs:33:13
diff --git a/tests/ui/borrowck/borrowck-bad-nested-calls-move.stderr b/tests/ui/borrowck/borrowck-bad-nested-calls-move.stderr
index e582ec605de..b96949fbb0e 100644
--- a/tests/ui/borrowck/borrowck-bad-nested-calls-move.stderr
+++ b/tests/ui/borrowck/borrowck-bad-nested-calls-move.stderr
@@ -10,6 +10,12 @@ LL |         &*a,
    |         --- borrow of `*a` occurs here
 LL |         a);
    |         ^ move out of `a` occurs here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -         &*a,
+LL +         a.clone(),
+   |
 
 error[E0505]: cannot move out of `a` because it is borrowed
   --> $DIR/borrowck-bad-nested-calls-move.rs:32:9
@@ -22,6 +28,12 @@ LL |         &*a,
    |         --- borrow of `*a` occurs here
 LL |         a);
    |         ^ move out of `a` occurs here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -         &*a,
+LL +         a.clone(),
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/borrowck/borrowck-closures-slice-patterns.stderr b/tests/ui/borrowck/borrowck-closures-slice-patterns.stderr
index 411d85b8e05..9066891d298 100644
--- a/tests/ui/borrowck/borrowck-closures-slice-patterns.stderr
+++ b/tests/ui/borrowck/borrowck-closures-slice-patterns.stderr
@@ -38,6 +38,11 @@ LL |         let [y, z @ ..] = x;
 LL |     };
 LL |     &x;
    |     ^^ value borrowed here after move
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         let [y, z @ ..] = x.clone();
+   |                            ++++++++
 
 error[E0502]: cannot borrow `*x` as mutable because it is also borrowed as immutable
   --> $DIR/borrowck-closures-slice-patterns.rs:33:13
@@ -79,6 +84,12 @@ LL |         let [y, z @ ..] = *x;
 LL |     };
 LL |     &x;
    |     ^^ value borrowed here after move
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -         let [y, z @ ..] = *x;
+LL +         let [y, z @ ..] = x.clone();
+   |
 
 error[E0502]: cannot borrow `*x` as mutable because it is also borrowed as immutable
   --> $DIR/borrowck-closures-slice-patterns.rs:59:13
diff --git a/tests/ui/borrowck/borrowck-field-sensitivity.stderr b/tests/ui/borrowck/borrowck-field-sensitivity.stderr
index 11812847dd1..ea552ff7820 100644
--- a/tests/ui/borrowck/borrowck-field-sensitivity.stderr
+++ b/tests/ui/borrowck/borrowck-field-sensitivity.stderr
@@ -49,6 +49,12 @@ LL |     drop(x.b);
    |          ^^^ move out of `x.b` occurs here
 LL |     drop(**p);
    |          --- borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let p = &x.b;
+LL +     let p = x.b.clone();
+   |
 
 error[E0505]: cannot move out of `x.b` because it is borrowed
   --> $DIR/borrowck-field-sensitivity.rs:41:14
@@ -61,6 +67,12 @@ LL |     let _y = A { a: 3, .. x };
    |              ^^^^^^^^^^^^^^^^ move out of `x.b` occurs here
 LL |     drop(**p);
    |          --- borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let p = &x.b;
+LL +     let p = x.b.clone();
+   |
 
 error[E0499]: cannot borrow `x.a` as mutable more than once at a time
   --> $DIR/borrowck-field-sensitivity.rs:48:13
diff --git a/tests/ui/borrowck/borrowck-fn-in-const-a.rs b/tests/ui/borrowck/borrowck-fn-in-const-a.rs
index d4ceae2963b..d52ec342b1a 100644
--- a/tests/ui/borrowck/borrowck-fn-in-const-a.rs
+++ b/tests/ui/borrowck/borrowck-fn-in-const-a.rs
@@ -9,4 +9,5 @@ const MOVE: fn(&String) -> String = {
 };
 
 fn main() {
+    println!("{}", MOVE(&String::new()));
 }
diff --git a/tests/ui/borrowck/borrowck-fn-in-const-a.stderr b/tests/ui/borrowck/borrowck-fn-in-const-a.stderr
index e05696864fd..7bf0f859fdd 100644
--- a/tests/ui/borrowck/borrowck-fn-in-const-a.stderr
+++ b/tests/ui/borrowck/borrowck-fn-in-const-a.stderr
@@ -3,6 +3,12 @@ error[E0507]: cannot move out of `*x` which is behind a shared reference
    |
 LL |         return *x
    |                ^^ move occurs because `*x` has type `String`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -         return *x
+LL +         return x.clone()
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/borrowck-in-static.rs b/tests/ui/borrowck/borrowck-in-static.rs
index a45f7b18e07..864dff40f46 100644
--- a/tests/ui/borrowck/borrowck-in-static.rs
+++ b/tests/ui/borrowck/borrowck-in-static.rs
@@ -1,6 +1,6 @@
 // check that borrowck looks inside consts/statics
 
-static FN : &'static (dyn Fn() -> (Box<dyn Fn()->Box<i32>>) + Sync) = &|| {
+static FN : &'static (dyn Fn() -> Box<dyn Fn()->Box<i32>> + Sync) = &|| {
     let x = Box::new(0);
     Box::new(|| x) //~ ERROR cannot move out of `x`, a captured variable in an `Fn` closure
 };
diff --git a/tests/ui/borrowck/borrowck-in-static.stderr b/tests/ui/borrowck/borrowck-in-static.stderr
index 8171e6950ac..745b02ae21b 100644
--- a/tests/ui/borrowck/borrowck-in-static.stderr
+++ b/tests/ui/borrowck/borrowck-in-static.stderr
@@ -7,6 +7,11 @@ LL |     Box::new(|| x)
    |              -- ^ move occurs because `x` has type `Box<i32>`, which does not implement the `Copy` trait
    |              |
    |              captured by this `Fn` closure
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     Box::new(|| x.clone())
+   |                  ++++++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/borrowck-issue-2657-2.fixed b/tests/ui/borrowck/borrowck-issue-2657-2.fixed
index e5aaf7d2de7..e532aa3e68c 100644
--- a/tests/ui/borrowck/borrowck-issue-2657-2.fixed
+++ b/tests/ui/borrowck/borrowck-issue-2657-2.fixed
@@ -5,7 +5,7 @@ fn main() {
 
     match x {
       Some(ref y) => {
-        let _b = y; //~ ERROR cannot move out
+        let _b = y.clone(); //~ ERROR cannot move out
       }
       _ => {}
     }
diff --git a/tests/ui/borrowck/borrowck-issue-2657-2.stderr b/tests/ui/borrowck/borrowck-issue-2657-2.stderr
index 6fab19000fc..16186792b93 100644
--- a/tests/ui/borrowck/borrowck-issue-2657-2.stderr
+++ b/tests/ui/borrowck/borrowck-issue-2657-2.stderr
@@ -9,6 +9,11 @@ help: consider removing the dereference here
 LL -         let _b = *y;
 LL +         let _b = y;
    |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -         let _b = *y;
+LL +         let _b = y.clone();
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/borrowck-issue-48962.stderr b/tests/ui/borrowck/borrowck-issue-48962.stderr
index ee174f6736e..6e821a4c6b0 100644
--- a/tests/ui/borrowck/borrowck-issue-48962.stderr
+++ b/tests/ui/borrowck/borrowck-issue-48962.stderr
@@ -17,6 +17,11 @@ LL |     {src};
    |      --- value moved here
 LL |     src.0 = 66;
    |     ^^^^^^^^^^ value used here after move
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     {src.clone()};
+   |         ++++++++
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/borrowck/borrowck-loan-blocks-move-cc.stderr b/tests/ui/borrowck/borrowck-loan-blocks-move-cc.stderr
index 86479043a06..370ae058f44 100644
--- a/tests/ui/borrowck/borrowck-loan-blocks-move-cc.stderr
+++ b/tests/ui/borrowck/borrowck-loan-blocks-move-cc.stderr
@@ -13,6 +13,12 @@ LL |         println!("v={}", *v);
 LL |     });
 LL |     w.use_ref();
    |     - borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let w = &v;
+LL +     let w = v.clone();
+   |
 
 error[E0505]: cannot move out of `v` because it is borrowed
   --> $DIR/borrowck-loan-blocks-move-cc.rs:24:19
@@ -29,6 +35,12 @@ LL |         println!("v={}", *v);
 LL |     });
 LL |     w.use_ref();
    |     - borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let w = &v;
+LL +     let w = v.clone();
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/borrowck/borrowck-loan-blocks-move.stderr b/tests/ui/borrowck/borrowck-loan-blocks-move.stderr
index d1fbc5b47db..8a8005dbb83 100644
--- a/tests/ui/borrowck/borrowck-loan-blocks-move.stderr
+++ b/tests/ui/borrowck/borrowck-loan-blocks-move.stderr
@@ -9,6 +9,12 @@ LL |     take(v);
    |          ^ move out of `v` occurs here
 LL |     w.use_ref();
    |     - borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let w = &v;
+LL +     let w = v.clone();
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/borrowck-move-from-subpath-of-borrowed-path.stderr b/tests/ui/borrowck/borrowck-move-from-subpath-of-borrowed-path.stderr
index a41c4af98e7..a23a203d999 100644
--- a/tests/ui/borrowck/borrowck-move-from-subpath-of-borrowed-path.stderr
+++ b/tests/ui/borrowck/borrowck-move-from-subpath-of-borrowed-path.stderr
@@ -10,6 +10,12 @@ LL |     let z = *a;
    |             ^^ move out of `*a` occurs here
 LL |     b.use_ref();
    |     - borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let b = &a;
+LL +     let b = a.clone();
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/borrowck-move-from-unsafe-ptr.stderr b/tests/ui/borrowck/borrowck-move-from-unsafe-ptr.stderr
index 7213f85ad98..ebc3b6ebcac 100644
--- a/tests/ui/borrowck/borrowck-move-from-unsafe-ptr.stderr
+++ b/tests/ui/borrowck/borrowck-move-from-unsafe-ptr.stderr
@@ -9,6 +9,11 @@ help: consider removing the dereference here
 LL -     let y = *x;
 LL +     let y = x;
    |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let y = *x;
+LL +     let y = x.clone();
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/borrowck-move-mut-base-ptr.stderr b/tests/ui/borrowck/borrowck-move-mut-base-ptr.stderr
index 88eb6c8ceee..acf426906c3 100644
--- a/tests/ui/borrowck/borrowck-move-mut-base-ptr.stderr
+++ b/tests/ui/borrowck/borrowck-move-mut-base-ptr.stderr
@@ -10,6 +10,12 @@ LL |     let t1 = t0;
 LL |     *t1 = 22;
 LL |     p.use_ref();
    |     - borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let p: &isize = &*t0; // Freezes `*t0`
+LL +     let p: &isize = t0.clone(); // Freezes `*t0`
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.fixed b/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.fixed
index 8d5ebbc7744..a19db7e5cd3 100644
--- a/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.fixed
+++ b/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.fixed
@@ -2,6 +2,6 @@
 use std::rc::Rc;
 
 pub fn main() {
-    let _x = <Vec<i32> as Clone>::clone(&Rc::new(vec![1, 2])).into_iter();
+    let _x = <Vec<i32> as Clone>::clone(&Rc::new(vec![1, 2]).clone()).into_iter();
     //~^ ERROR [E0507]
 }
diff --git a/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr b/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr
index 076f0ce3440..577c2de38be 100644
--- a/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr
+++ b/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr
@@ -12,6 +12,10 @@ help: you can `clone` the value and consume it, but this might not be your desir
    |
 LL |     let _x = <Vec<i32> as Clone>::clone(&Rc::new(vec![1, 2])).into_iter();
    |              ++++++++++++++++++++++++++++                   +
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     let _x = Rc::new(vec![1, 2]).clone().into_iter();
+   |                                 ++++++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/borrowck-move-out-of-overloaded-deref.stderr b/tests/ui/borrowck/borrowck-move-out-of-overloaded-deref.stderr
index dce1f4d0775..774aac3ec9c 100644
--- a/tests/ui/borrowck/borrowck-move-out-of-overloaded-deref.stderr
+++ b/tests/ui/borrowck/borrowck-move-out-of-overloaded-deref.stderr
@@ -9,6 +9,11 @@ help: consider removing the dereference here
 LL -     let _x = *Rc::new("hi".to_string());
 LL +     let _x = Rc::new("hi".to_string());
    |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let _x = *Rc::new("hi".to_string());
+LL +     let _x = Rc::new("hi".to_string()).clone();
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/borrowck-move-out-of-static-item.rs b/tests/ui/borrowck/borrowck-move-out-of-static-item.rs
index d01fb261894..b24d9b932cd 100644
--- a/tests/ui/borrowck/borrowck-move-out-of-static-item.rs
+++ b/tests/ui/borrowck/borrowck-move-out-of-static-item.rs
@@ -8,7 +8,8 @@ static BAR: Foo = Foo { foo: 5 };
 
 
 fn test(f: Foo) {
-    let _f = Foo{foo: 4, ..f};
+    let f = Foo { foo: 4, ..f };
+    println!("{}", f.foo);
 }
 
 fn main() {
diff --git a/tests/ui/borrowck/borrowck-move-out-of-static-item.stderr b/tests/ui/borrowck/borrowck-move-out-of-static-item.stderr
index 07dcaf875e7..86bddacbdc7 100644
--- a/tests/ui/borrowck/borrowck-move-out-of-static-item.stderr
+++ b/tests/ui/borrowck/borrowck-move-out-of-static-item.stderr
@@ -1,8 +1,14 @@
 error[E0507]: cannot move out of static item `BAR`
-  --> $DIR/borrowck-move-out-of-static-item.rs:15:10
+  --> $DIR/borrowck-move-out-of-static-item.rs:16:10
    |
 LL |     test(BAR);
    |          ^^^ move occurs because `BAR` has type `Foo`, which does not implement the `Copy` trait
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrowck-move-out-of-static-item.rs:3:1
+   |
+LL | struct Foo {
+   | ^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/borrowck-move-subcomponent.stderr b/tests/ui/borrowck/borrowck-move-subcomponent.stderr
index 8408d99156a..4d9477f8581 100644
--- a/tests/ui/borrowck/borrowck-move-subcomponent.stderr
+++ b/tests/ui/borrowck/borrowck-move-subcomponent.stderr
@@ -9,6 +9,12 @@ LL |   let S { x: ax } = a;
    |              ^^ move out of `a.x` occurs here
 LL |   f(pb);
    |     -- borrow later used here
+   |
+note: if `S` implemented `Clone`, you could clone the value
+  --> $DIR/borrowck-move-subcomponent.rs:6:1
+   |
+LL | struct S {
+   | ^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/borrowck-multiple-captures.stderr b/tests/ui/borrowck/borrowck-multiple-captures.stderr
index 70abe7b346e..fdac4c27cee 100644
--- a/tests/ui/borrowck/borrowck-multiple-captures.stderr
+++ b/tests/ui/borrowck/borrowck-multiple-captures.stderr
@@ -14,6 +14,12 @@ LL |         drop(x1);
 ...
 LL |     borrow(&*p1);
    |            ---- borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let p1 = &x1;
+LL +     let p1 = x1.clone();
+   |
 
 error[E0505]: cannot move out of `x2` because it is borrowed
   --> $DIR/borrowck-multiple-captures.rs:12:19
@@ -30,6 +36,12 @@ LL |         drop(x2);
 ...
 LL |     borrow(&*p2);
    |            ---- borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let p2 = &x2;
+LL +     let p2 = x2.clone();
+   |
 
 error[E0382]: use of moved value: `x1`
   --> $DIR/borrowck-multiple-captures.rs:27:19
@@ -93,6 +105,12 @@ LL |         drop(x);
 ...
 LL |     borrow(&*p);
    |            --- borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let p = &x;
+LL +     let p = x.clone();
+   |
 
 error[E0382]: use of moved value: `x`
   --> $DIR/borrowck-multiple-captures.rs:52:14
diff --git a/tests/ui/borrowck/borrowck-overloaded-call.stderr b/tests/ui/borrowck/borrowck-overloaded-call.stderr
index 723b19f4124..1602058c183 100644
--- a/tests/ui/borrowck/borrowck-overloaded-call.stderr
+++ b/tests/ui/borrowck/borrowck-overloaded-call.stderr
@@ -29,6 +29,12 @@ LL |     s(" world".to_string());
    |     - value moved here
 LL |     s(" world".to_string());
    |     ^ value used here after move
+   |
+note: if `SFnOnce` implemented `Clone`, you could clone the value
+  --> $DIR/borrowck-overloaded-call.rs:41:1
+   |
+LL | struct SFnOnce {
+   | ^^^^^^^^^^^^^^
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/borrowck/borrowck-overloaded-index-move-from-vec.stderr b/tests/ui/borrowck/borrowck-overloaded-index-move-from-vec.stderr
index b4106702cd1..3e874ed1a2f 100644
--- a/tests/ui/borrowck/borrowck-overloaded-index-move-from-vec.stderr
+++ b/tests/ui/borrowck/borrowck-overloaded-index-move-from-vec.stderr
@@ -8,6 +8,10 @@ help: consider borrowing here
    |
 LL |     let bad = &v[0];
    |               +
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     let bad = v[0].clone();
+   |                   ++++++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/borrowck-struct-update-with-dtor.rs b/tests/ui/borrowck/borrowck-struct-update-with-dtor.rs
index 1f6ed6d46aa..f0d067477c6 100644
--- a/tests/ui/borrowck/borrowck-struct-update-with-dtor.rs
+++ b/tests/ui/borrowck/borrowck-struct-update-with-dtor.rs
@@ -2,20 +2,63 @@
 // move, when the struct implements Drop.
 
 struct B;
-struct S { a: isize, b: B }
-impl Drop for S { fn drop(&mut self) { } }
+struct S<K> { a: isize, b: B, c: K }
+impl<K> Drop for S<K> { fn drop(&mut self) { } }
 
-struct T { a: isize, mv: Box<isize> }
+struct T { a: isize, b: Box<isize> }
 impl Drop for T { fn drop(&mut self) { } }
 
-fn f(s0:S) {
-    let _s2 = S{a: 2, ..s0};
-    //~^ ERROR [E0509]
+struct V<K> { a: isize, b: Box<isize>, c: K }
+impl<K> Drop for V<K> { fn drop(&mut self) { } }
+
+#[derive(Clone)]
+struct Clonable;
+
+mod not_all_clone {
+    use super::*;
+    fn a(s0: S<()>) {
+        let _s2 = S { a: 2, ..s0 };
+        //~^ ERROR [E0509]
+    }
+    fn b(s0: S<B>) {
+        let _s2 = S { a: 2, ..s0 };
+        //~^ ERROR [E0509]
+        //~| ERROR [E0509]
+    }
+    fn c<K: Clone>(s0: S<K>) {
+        let _s2 = S { a: 2, ..s0 };
+        //~^ ERROR [E0509]
+        //~| ERROR [E0509]
+    }
 }
+mod all_clone {
+    use super::*;
+    fn a(s0: T) {
+        let _s2 = T { a: 2, ..s0 };
+        //~^ ERROR [E0509]
+    }
+
+    fn b(s0: T) {
+        let _s2 = T { ..s0 };
+        //~^ ERROR [E0509]
+    }
+
+    fn c(s0: T) {
+        let _s2 = T { a: 2, b: s0.b };
+        //~^ ERROR [E0509]
+    }
+
+    fn d<K: Clone>(s0: V<K>) {
+        let _s2 = V { a: 2, ..s0 };
+        //~^ ERROR [E0509]
+        //~| ERROR [E0509]
+    }
 
-fn g(s0:T) {
-    let _s2 = T{a: 2, ..s0};
-    //~^ ERROR [E0509]
+    fn e(s0: V<Clonable>) {
+        let _s2 = V { a: 2, ..s0 };
+        //~^ ERROR [E0509]
+        //~| ERROR [E0509]
+    }
 }
 
 fn main() { }
diff --git a/tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr b/tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr
index af32f279100..bc11204acf2 100644
--- a/tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr
+++ b/tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr
@@ -1,21 +1,191 @@
-error[E0509]: cannot move out of type `S`, which implements the `Drop` trait
-  --> $DIR/borrowck-struct-update-with-dtor.rs:12:15
+error[E0509]: cannot move out of type `S<()>`, which implements the `Drop` trait
+  --> $DIR/borrowck-struct-update-with-dtor.rs:20:19
    |
-LL |     let _s2 = S{a: 2, ..s0};
-   |               ^^^^^^^^^^^^^
-   |               |
-   |               cannot move out of here
-   |               move occurs because `s0.b` has type `B`, which does not implement the `Copy` trait
+LL |         let _s2 = S { a: 2, ..s0 };
+   |                   ^^^^^^^^^^^^^^^^
+   |                   |
+   |                   cannot move out of here
+   |                   move occurs because `s0.b` has type `B`, which does not implement the `Copy` trait
+   |
+note: `B` doesn't implement `Copy` or `Clone`
+  --> $DIR/borrowck-struct-update-with-dtor.rs:4:1
+   |
+LL | struct B;
+   | ^^^^^^^^
+help: if `B` implemented `Clone`, you could clone the value from the field instead of using the functional record update syntax
+   |
+LL |         let _s2 = S { a: 2, b: s0.b.clone(), ..s0 };
+   |                           +++++++++++++++++
+
+error[E0509]: cannot move out of type `S<B>`, which implements the `Drop` trait
+  --> $DIR/borrowck-struct-update-with-dtor.rs:24:19
+   |
+LL |         let _s2 = S { a: 2, ..s0 };
+   |                   ^^^^^^^^^^^^^^^^
+   |                   |
+   |                   cannot move out of here
+   |                   move occurs because `s0.b` has type `B`, which does not implement the `Copy` trait
+   |
+note: `B` doesn't implement `Copy` or `Clone`
+  --> $DIR/borrowck-struct-update-with-dtor.rs:4:1
+   |
+LL | struct B;
+   | ^^^^^^^^
+help: if `B` implemented `Clone`, you could clone the value from the field instead of using the functional record update syntax
+   |
+LL |         let _s2 = S { a: 2, b: s0.b.clone(), c: s0.c.clone() };
+   |                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error[E0509]: cannot move out of type `S<B>`, which implements the `Drop` trait
+  --> $DIR/borrowck-struct-update-with-dtor.rs:24:19
+   |
+LL |         let _s2 = S { a: 2, ..s0 };
+   |                   ^^^^^^^^^^^^^^^^
+   |                   |
+   |                   cannot move out of here
+   |                   move occurs because `s0.c` has type `B`, which does not implement the `Copy` trait
+   |
+note: `B` doesn't implement `Copy` or `Clone`
+  --> $DIR/borrowck-struct-update-with-dtor.rs:4:1
+   |
+LL | struct B;
+   | ^^^^^^^^
+help: if `B` implemented `Clone`, you could clone the value from the field instead of using the functional record update syntax
+   |
+LL |         let _s2 = S { a: 2, b: s0.b.clone(), c: s0.c.clone() };
+   |                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error[E0509]: cannot move out of type `S<K>`, which implements the `Drop` trait
+  --> $DIR/borrowck-struct-update-with-dtor.rs:29:19
+   |
+LL |         let _s2 = S { a: 2, ..s0 };
+   |                   ^^^^^^^^^^^^^^^^
+   |                   |
+   |                   cannot move out of here
+   |                   move occurs because `s0.b` has type `B`, which does not implement the `Copy` trait
+   |
+note: `B` doesn't implement `Copy` or `Clone`
+  --> $DIR/borrowck-struct-update-with-dtor.rs:4:1
+   |
+LL | struct B;
+   | ^^^^^^^^
+help: if `B` implemented `Clone`, you could clone the value from the field instead of using the functional record update syntax
+   |
+LL |         let _s2 = S { a: 2, b: s0.b.clone(), ..s0 };
+   |                           +++++++++++++++++
+
+error[E0509]: cannot move out of type `S<K>`, which implements the `Drop` trait
+  --> $DIR/borrowck-struct-update-with-dtor.rs:29:19
+   |
+LL |         let _s2 = S { a: 2, ..s0 };
+   |                   ^^^^^^^^^^^^^^^^
+   |                   |
+   |                   cannot move out of here
+   |                   move occurs because `s0.c` has type `K`, which does not implement the `Copy` trait
+   |
+help: clone the value from the field instead of using the functional record update syntax
+   |
+LL |         let _s2 = S { a: 2, c: s0.c.clone(), ..s0 };
+   |                           +++++++++++++++++
+
+error[E0509]: cannot move out of type `T`, which implements the `Drop` trait
+  --> $DIR/borrowck-struct-update-with-dtor.rs:37:19
+   |
+LL |         let _s2 = T { a: 2, ..s0 };
+   |                   ^^^^^^^^^^^^^^^^
+   |                   |
+   |                   cannot move out of here
+   |                   move occurs because `s0.b` has type `Box<isize>`, which does not implement the `Copy` trait
+   |
+help: clone the value from the field instead of using the functional record update syntax
+   |
+LL |         let _s2 = T { a: 2, b: s0.b.clone() };
+   |                           ~~~~~~~~~~~~~~~~~
+
+error[E0509]: cannot move out of type `T`, which implements the `Drop` trait
+  --> $DIR/borrowck-struct-update-with-dtor.rs:42:19
+   |
+LL |         let _s2 = T { ..s0 };
+   |                   ^^^^^^^^^^
+   |                   |
+   |                   cannot move out of here
+   |                   move occurs because `s0.b` has type `Box<isize>`, which does not implement the `Copy` trait
+   |
+help: clone the value from the field instead of using the functional record update syntax
+   |
+LL |         let _s2 = T { b: s0.b.clone(), ..s0 };
+   |                     ~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error[E0509]: cannot move out of type `T`, which implements the `Drop` trait
-  --> $DIR/borrowck-struct-update-with-dtor.rs:17:15
+  --> $DIR/borrowck-struct-update-with-dtor.rs:47:32
+   |
+LL |         let _s2 = T { a: 2, b: s0.b };
+   |                                ^^^^
+   |                                |
+   |                                cannot move out of here
+   |                                move occurs because `s0.b` has type `Box<isize>`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         let _s2 = T { a: 2, b: s0.b.clone() };
+   |                                    ++++++++
+
+error[E0509]: cannot move out of type `V<K>`, which implements the `Drop` trait
+  --> $DIR/borrowck-struct-update-with-dtor.rs:52:19
+   |
+LL |         let _s2 = V { a: 2, ..s0 };
+   |                   ^^^^^^^^^^^^^^^^
+   |                   |
+   |                   cannot move out of here
+   |                   move occurs because `s0.b` has type `Box<isize>`, which does not implement the `Copy` trait
+   |
+help: clone the value from the field instead of using the functional record update syntax
+   |
+LL |         let _s2 = V { a: 2, b: s0.b.clone(), ..s0 };
+   |                           +++++++++++++++++
+
+error[E0509]: cannot move out of type `V<K>`, which implements the `Drop` trait
+  --> $DIR/borrowck-struct-update-with-dtor.rs:52:19
+   |
+LL |         let _s2 = V { a: 2, ..s0 };
+   |                   ^^^^^^^^^^^^^^^^
+   |                   |
+   |                   cannot move out of here
+   |                   move occurs because `s0.c` has type `K`, which does not implement the `Copy` trait
+   |
+help: clone the value from the field instead of using the functional record update syntax
+   |
+LL |         let _s2 = V { a: 2, c: s0.c.clone(), ..s0 };
+   |                           +++++++++++++++++
+
+error[E0509]: cannot move out of type `V<Clonable>`, which implements the `Drop` trait
+  --> $DIR/borrowck-struct-update-with-dtor.rs:58:19
+   |
+LL |         let _s2 = V { a: 2, ..s0 };
+   |                   ^^^^^^^^^^^^^^^^
+   |                   |
+   |                   cannot move out of here
+   |                   move occurs because `s0.b` has type `Box<isize>`, which does not implement the `Copy` trait
+   |
+help: clone the value from the field instead of using the functional record update syntax
+   |
+LL |         let _s2 = V { a: 2, b: s0.b.clone(), ..s0 };
+   |                           +++++++++++++++++
+
+error[E0509]: cannot move out of type `V<Clonable>`, which implements the `Drop` trait
+  --> $DIR/borrowck-struct-update-with-dtor.rs:58:19
+   |
+LL |         let _s2 = V { a: 2, ..s0 };
+   |                   ^^^^^^^^^^^^^^^^
+   |                   |
+   |                   cannot move out of here
+   |                   move occurs because `s0.c` has type `Clonable`, which does not implement the `Copy` trait
+   |
+help: clone the value from the field instead of using the functional record update syntax
    |
-LL |     let _s2 = T{a: 2, ..s0};
-   |               ^^^^^^^^^^^^^
-   |               |
-   |               cannot move out of here
-   |               move occurs because `s0.mv` has type `Box<isize>`, which does not implement the `Copy` trait
+LL |         let _s2 = V { a: 2, c: s0.c.clone(), ..s0 };
+   |                           +++++++++++++++++
 
-error: aborting due to 2 previous errors
+error: aborting due to 12 previous errors
 
 For more information about this error, try `rustc --explain E0509`.
diff --git a/tests/ui/borrowck/borrowck-unary-move.stderr b/tests/ui/borrowck/borrowck-unary-move.stderr
index e6c3869f67a..598ecb53778 100644
--- a/tests/ui/borrowck/borrowck-unary-move.stderr
+++ b/tests/ui/borrowck/borrowck-unary-move.stderr
@@ -9,6 +9,12 @@ LL |     free(x);
    |          ^ move out of `x` occurs here
 LL |     *y
    |     -- borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let y = &*x;
+LL +     let y = x.clone();
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/borrowck-unboxed-closures.stderr b/tests/ui/borrowck/borrowck-unboxed-closures.stderr
index 3634676463c..a4513bd614e 100644
--- a/tests/ui/borrowck/borrowck-unboxed-closures.stderr
+++ b/tests/ui/borrowck/borrowck-unboxed-closures.stderr
@@ -29,15 +29,13 @@ LL |     f(1, 2);
 LL |     f(1, 2);
    |     ^ value used here after move
    |
-note: this value implements `FnOnce`, which causes it to be moved when called
-  --> $DIR/borrowck-unboxed-closures.rs:11:5
+note: `FnOnce` closures can only be called once
+  --> $DIR/borrowck-unboxed-closures.rs:10:8
    |
+LL | fn c<F:FnOnce(isize, isize) -> isize>(f: F) {
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `F` is made to be an `FnOnce` closure here
 LL |     f(1, 2);
-   |     ^
-help: consider further restricting this bound
-   |
-LL | fn c<F:FnOnce(isize, isize) -> isize + Copy>(f: F) {
-   |                                      ++++++
+   |     ------- this value implements `FnOnce`, which causes it to be moved when called
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/borrowck/borrowck-vec-pattern-nesting.rs b/tests/ui/borrowck/borrowck-vec-pattern-nesting.rs
index 1bda7a49713..ec074d2cf1c 100644
--- a/tests/ui/borrowck/borrowck-vec-pattern-nesting.rs
+++ b/tests/ui/borrowck/borrowck-vec-pattern-nesting.rs
@@ -47,6 +47,7 @@ fn c() {
     //~| NOTE cannot move out of here
     //~| NOTE move occurs because
     //~| HELP consider borrowing here
+    //~| HELP consider cloning
 }
 
 fn d() {
@@ -66,6 +67,7 @@ fn d() {
     //~| NOTE cannot move out of here
     //~| NOTE move occurs because
     //~| HELP consider borrowing here
+    //~| HELP consider cloning
 }
 
 fn e() {
@@ -86,6 +88,7 @@ fn e() {
     //~| NOTE cannot move out of here
     //~| NOTE move occurs because
     //~| HELP consider borrowing here
+    //~| HELP consider cloning
 }
 
 fn main() {}
diff --git a/tests/ui/borrowck/borrowck-vec-pattern-nesting.stderr b/tests/ui/borrowck/borrowck-vec-pattern-nesting.stderr
index 024cb006c26..fff997fd555 100644
--- a/tests/ui/borrowck/borrowck-vec-pattern-nesting.stderr
+++ b/tests/ui/borrowck/borrowck-vec-pattern-nesting.stderr
@@ -53,9 +53,13 @@ help: consider borrowing here
    |
 LL |     let a = &vec[0];
    |             +
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     let a = vec[0].clone();
+   |                   ++++++++
 
 error[E0508]: cannot move out of type `[Box<isize>]`, a non-copy slice
-  --> $DIR/borrowck-vec-pattern-nesting.rs:55:11
+  --> $DIR/borrowck-vec-pattern-nesting.rs:56:11
    |
 LL |     match vec {
    |           ^^^ cannot move out of here
@@ -73,7 +77,7 @@ LL +         [
    |
 
 error[E0508]: cannot move out of type `[Box<isize>]`, a non-copy slice
-  --> $DIR/borrowck-vec-pattern-nesting.rs:65:13
+  --> $DIR/borrowck-vec-pattern-nesting.rs:66:13
    |
 LL |     let a = vec[0];
    |             ^^^^^^
@@ -85,9 +89,13 @@ help: consider borrowing here
    |
 LL |     let a = &vec[0];
    |             +
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     let a = vec[0].clone();
+   |                   ++++++++
 
 error[E0508]: cannot move out of type `[Box<isize>]`, a non-copy slice
-  --> $DIR/borrowck-vec-pattern-nesting.rs:74:11
+  --> $DIR/borrowck-vec-pattern-nesting.rs:76:11
    |
 LL |     match vec {
    |           ^^^ cannot move out of here
@@ -106,7 +114,7 @@ LL +         [_a, _b, _c] => {}
    |
 
 error[E0508]: cannot move out of type `[Box<isize>]`, a non-copy slice
-  --> $DIR/borrowck-vec-pattern-nesting.rs:85:13
+  --> $DIR/borrowck-vec-pattern-nesting.rs:87:13
    |
 LL |     let a = vec[0];
    |             ^^^^^^
@@ -118,6 +126,10 @@ help: consider borrowing here
    |
 LL |     let a = &vec[0];
    |             +
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     let a = vec[0].clone();
+   |                   ++++++++
 
 error: aborting due to 8 previous errors
 
diff --git a/tests/ui/borrowck/clone-on-ref.stderr b/tests/ui/borrowck/clone-on-ref.stderr
index ee4fcadf55a..f0eaf4bac7d 100644
--- a/tests/ui/borrowck/clone-on-ref.stderr
+++ b/tests/ui/borrowck/clone-on-ref.stderr
@@ -52,6 +52,11 @@ LL |
 LL |     println!("{b:?}");
    |               ----- borrow later used here
    |
+note: if `A` implemented `Clone`, you could clone the value
+  --> $DIR/clone-on-ref.rs:19:1
+   |
+LL | struct A;
+   | ^^^^^^^^
 help: consider annotating `A` with `#[derive(Clone)]`
    |
 LL + #[derive(Clone)]
diff --git a/tests/ui/borrowck/clone-span-on-try-operator.fixed b/tests/ui/borrowck/clone-span-on-try-operator.fixed
index 59253c98079..59a162e72c1 100644
--- a/tests/ui/borrowck/clone-span-on-try-operator.fixed
+++ b/tests/ui/borrowck/clone-span-on-try-operator.fixed
@@ -7,5 +7,5 @@ impl Foo {
 }
 fn main() {
     let foo = &Foo;
-    <Foo as Clone>::clone(&(*foo)).foo(); //~ ERROR cannot move out
+    <Foo as Clone>::clone(&foo.clone()).foo(); //~ ERROR cannot move out
 }
diff --git a/tests/ui/borrowck/clone-span-on-try-operator.stderr b/tests/ui/borrowck/clone-span-on-try-operator.stderr
index adf84e49a9f..c2c63f94943 100644
--- a/tests/ui/borrowck/clone-span-on-try-operator.stderr
+++ b/tests/ui/borrowck/clone-span-on-try-operator.stderr
@@ -15,6 +15,11 @@ help: you can `clone` the value and consume it, but this might not be your desir
    |
 LL |     <Foo as Clone>::clone(&(*foo)).foo();
    |     +++++++++++++++++++++++      +
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     (*foo).foo();
+LL +     foo.clone().foo();
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/issue-101119.stderr b/tests/ui/borrowck/issue-101119.stderr
index 1f32ece3d3d..b4775496f4f 100644
--- a/tests/ui/borrowck/issue-101119.stderr
+++ b/tests/ui/borrowck/issue-101119.stderr
@@ -4,11 +4,25 @@ error[E0382]: use of moved value: `state`
 LL | fn fill_memory_blocks_mt(state: &mut State) {
    |                          ----- move occurs because `state` has type `&mut State`, which does not implement the `Copy` trait
 LL |     loop {
+   |     ---- inside of this loop
 LL |         once(move || {
    |              ^^^^^^^ value moved into closure here, in previous iteration of loop
 LL |
 LL |             fill_segment(state);
    |                          ----- use occurs due to use in closure
+   |
+note: consider changing this parameter type in function `fill_segment` to borrow instead if owning the value isn't necessary
+  --> $DIR/issue-101119.rs:14:20
+   |
+LL | fn fill_segment(_: &mut State) {}
+   |    ------------    ^^^^^^^^^^ this parameter takes ownership of the value
+   |    |
+   |    in this function
+note: if `State` implemented `Clone`, you could clone the value
+  --> $DIR/issue-101119.rs:1:1
+   |
+LL | struct State;
+   | ^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/issue-103624.stderr b/tests/ui/borrowck/issue-103624.stderr
index 7a281e8aa30..94421c35c65 100644
--- a/tests/ui/borrowck/issue-103624.stderr
+++ b/tests/ui/borrowck/issue-103624.stderr
@@ -9,6 +9,12 @@ LL |         spawn_blocking(move || {
 LL |
 LL |             self.b;
    |             ^^^^^^ move occurs because `self.b` has type `StructB`, which does not implement the `Copy` trait
+   |
+note: if `StructB` implemented `Clone`, you could clone the value
+  --> $DIR/issue-103624.rs:23:1
+   |
+LL | struct StructB {}
+   | ^^^^^^^^^^^^^^
 
 error[E0521]: borrowed data escapes outside of method
   --> $DIR/issue-103624.rs:14:9
diff --git a/tests/ui/borrowck/issue-119915-bad-clone-suggestion.stderr b/tests/ui/borrowck/issue-119915-bad-clone-suggestion.stderr
index ab42205d510..701f00d079d 100644
--- a/tests/ui/borrowck/issue-119915-bad-clone-suggestion.stderr
+++ b/tests/ui/borrowck/issue-119915-bad-clone-suggestion.stderr
@@ -11,6 +11,11 @@ note: `Example::<E, FakeParam>::change` takes ownership of the receiver `self`,
    |
 LL |     unsafe fn change<NewFakeParam>(self) -> Example<E, NewFakeParam> {
    |                                    ^^^^
+note: if `Example<E, NoLifetime>` implemented `Clone`, you could clone the value
+  --> $DIR/issue-119915-bad-clone-suggestion.rs:3:1
+   |
+LL | struct Example<E, FakeParam>(PhantomData<(fn(E), fn(FakeParam))>);
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/issue-17718-static-move.stderr b/tests/ui/borrowck/issue-17718-static-move.stderr
index 5ca0a7fb885..e2c3a9d5a26 100644
--- a/tests/ui/borrowck/issue-17718-static-move.stderr
+++ b/tests/ui/borrowck/issue-17718-static-move.stderr
@@ -4,6 +4,11 @@ error[E0507]: cannot move out of static item `FOO`
 LL |     let _a = FOO;
    |              ^^^ move occurs because `FOO` has type `Foo`, which does not implement the `Copy` trait
    |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/issue-17718-static-move.rs:1:1
+   |
+LL | struct Foo;
+   | ^^^^^^^^^^
 help: consider borrowing here
    |
 LL |     let _a = &FOO;
diff --git a/tests/ui/borrowck/issue-20801.stderr b/tests/ui/borrowck/issue-20801.stderr
index 97294afd3df..1da6f0bef02 100644
--- a/tests/ui/borrowck/issue-20801.stderr
+++ b/tests/ui/borrowck/issue-20801.stderr
@@ -19,6 +19,11 @@ error[E0507]: cannot move out of a mutable reference
 LL |     let a = unsafe { *mut_ref() };
    |                      ^^^^^^^^^^ move occurs because value has type `T`, which does not implement the `Copy` trait
    |
+note: if `T` implemented `Clone`, you could clone the value
+  --> $DIR/issue-20801.rs:3:1
+   |
+LL | struct T(u8);
+   | ^^^^^^^^
 help: consider removing the dereference here
    |
 LL -     let a = unsafe { *mut_ref() };
@@ -31,6 +36,11 @@ error[E0507]: cannot move out of a shared reference
 LL |     let b = unsafe { *imm_ref() };
    |                      ^^^^^^^^^^ move occurs because value has type `T`, which does not implement the `Copy` trait
    |
+note: if `T` implemented `Clone`, you could clone the value
+  --> $DIR/issue-20801.rs:3:1
+   |
+LL | struct T(u8);
+   | ^^^^^^^^
 help: consider removing the dereference here
    |
 LL -     let b = unsafe { *imm_ref() };
@@ -43,6 +53,11 @@ error[E0507]: cannot move out of a raw pointer
 LL |     let c = unsafe { *mut_ptr() };
    |                      ^^^^^^^^^^ move occurs because value has type `T`, which does not implement the `Copy` trait
    |
+note: if `T` implemented `Clone`, you could clone the value
+  --> $DIR/issue-20801.rs:3:1
+   |
+LL | struct T(u8);
+   | ^^^^^^^^
 help: consider removing the dereference here
    |
 LL -     let c = unsafe { *mut_ptr() };
@@ -55,6 +70,11 @@ error[E0507]: cannot move out of a raw pointer
 LL |     let d = unsafe { *const_ptr() };
    |                      ^^^^^^^^^^^^ move occurs because value has type `T`, which does not implement the `Copy` trait
    |
+note: if `T` implemented `Clone`, you could clone the value
+  --> $DIR/issue-20801.rs:3:1
+   |
+LL | struct T(u8);
+   | ^^^^^^^^
 help: consider removing the dereference here
    |
 LL -     let d = unsafe { *const_ptr() };
diff --git a/tests/ui/borrowck/issue-64453.stderr b/tests/ui/borrowck/issue-64453.stderr
index f032ea779dd..0e4a8d42f6e 100644
--- a/tests/ui/borrowck/issue-64453.stderr
+++ b/tests/ui/borrowck/issue-64453.stderr
@@ -22,6 +22,11 @@ error[E0507]: cannot move out of static item `settings_dir`
    |
 LL |     let settings_data = from_string(settings_dir);
    |                                     ^^^^^^^^^^^^ move occurs because `settings_dir` has type `String`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     let settings_data = from_string(settings_dir.clone());
+   |                                                 ++++++++
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/borrowck/issue-87456-point-to-closure.stderr b/tests/ui/borrowck/issue-87456-point-to-closure.stderr
index a15909df07b..a0c7cac2add 100644
--- a/tests/ui/borrowck/issue-87456-point-to-closure.stderr
+++ b/tests/ui/borrowck/issue-87456-point-to-closure.stderr
@@ -14,6 +14,10 @@ help: consider borrowing here
    |
 LL |         let _foo: String = &val;
    |                            +
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         let _foo: String = val.clone();
+   |                               ++++++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/move-error-in-promoted-2.stderr b/tests/ui/borrowck/move-error-in-promoted-2.stderr
index 0d5edadcb46..43f4e820857 100644
--- a/tests/ui/borrowck/move-error-in-promoted-2.stderr
+++ b/tests/ui/borrowck/move-error-in-promoted-2.stderr
@@ -6,6 +6,12 @@ LL |     &([S][0],);
    |       |
    |       cannot move out of here
    |       move occurs because value has type `S`, which does not implement the `Copy` trait
+   |
+note: if `S` implemented `Clone`, you could clone the value
+  --> $DIR/move-error-in-promoted-2.rs:3:1
+   |
+LL | struct S;
+   | ^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/move-error-in-promoted.stderr b/tests/ui/borrowck/move-error-in-promoted.stderr
index 03c0297c5a9..8d42df24e27 100644
--- a/tests/ui/borrowck/move-error-in-promoted.stderr
+++ b/tests/ui/borrowck/move-error-in-promoted.stderr
@@ -6,6 +6,11 @@ LL |     let _ = S1(C[0]).clone();
    |                |
    |                cannot move out of here
    |                move occurs because value has type `S2`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     let _ = S1(C[0].clone()).clone();
+   |                    ++++++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/move-error-snippets.stderr b/tests/ui/borrowck/move-error-snippets.stderr
index 83f9e19aa0d..40b64398aef 100644
--- a/tests/ui/borrowck/move-error-snippets.stderr
+++ b/tests/ui/borrowck/move-error-snippets.stderr
@@ -9,6 +9,11 @@ LL |         let a = $c;
 LL | sss!();
    | ------ in this macro invocation
    |
+note: if `A` implemented `Clone`, you could clone the value
+  --> $DIR/move-error-snippets.rs:9:1
+   |
+LL | struct A;
+   | ^^^^^^^^
    = note: this error originates in the macro `aaa` which comes from the expansion of the macro `sss` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider borrowing here
    |
diff --git a/tests/ui/borrowck/move-from-union-field-issue-66500.stderr b/tests/ui/borrowck/move-from-union-field-issue-66500.stderr
index 70078582713..c951ce8e3cd 100644
--- a/tests/ui/borrowck/move-from-union-field-issue-66500.stderr
+++ b/tests/ui/borrowck/move-from-union-field-issue-66500.stderr
@@ -3,24 +3,48 @@ error[E0507]: cannot move out of `*u.a` which is behind a shared reference
    |
 LL |     *u.a
    |     ^^^^ move occurs because `*u.a` has type `String`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     *u.a
+LL +     u.a.clone()
+   |
 
 error[E0507]: cannot move out of `*u.b` which is behind a mutable reference
   --> $DIR/move-from-union-field-issue-66500.rs:16:5
    |
 LL |     *u.b
    |     ^^^^ move occurs because `*u.b` has type `String`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     *u.b
+LL +     u.b.clone()
+   |
 
 error[E0507]: cannot move out of `*u.c` which is behind a raw pointer
   --> $DIR/move-from-union-field-issue-66500.rs:20:5
    |
 LL |     *u.c
    |     ^^^^ move occurs because `*u.c` has type `String`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     *u.c
+LL +     u.c.clone()
+   |
 
 error[E0507]: cannot move out of `*u.d` which is behind a raw pointer
   --> $DIR/move-from-union-field-issue-66500.rs:24:5
    |
 LL |     *u.d
    |     ^^^^ move occurs because `*u.d` has type `String`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     *u.d
+LL +     u.d.clone()
+   |
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/borrowck/move-in-static-initializer-issue-38520.stderr b/tests/ui/borrowck/move-in-static-initializer-issue-38520.stderr
index 6619fb42c28..a4e70b50646 100644
--- a/tests/ui/borrowck/move-in-static-initializer-issue-38520.stderr
+++ b/tests/ui/borrowck/move-in-static-initializer-issue-38520.stderr
@@ -3,12 +3,24 @@ error[E0507]: cannot move out of a shared reference
    |
 LL | static Y: usize = get(*&X);
    |                       ^^^ move occurs because value has type `Foo`, which does not implement the `Copy` trait
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/move-in-static-initializer-issue-38520.rs:5:1
+   |
+LL | struct Foo(usize);
+   | ^^^^^^^^^^
 
 error[E0507]: cannot move out of a shared reference
   --> $DIR/move-in-static-initializer-issue-38520.rs:13:22
    |
 LL | const Z: usize = get(*&X);
    |                      ^^^ move occurs because value has type `Foo`, which does not implement the `Copy` trait
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/move-in-static-initializer-issue-38520.rs:5:1
+   |
+LL | struct Foo(usize);
+   | ^^^^^^^^^^
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.fixed b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.fixed
index 8add3a5f2b6..3b4f7c8465c 100644
--- a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.fixed
+++ b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.fixed
@@ -9,7 +9,7 @@ fn call<F>(f: F) where F : Fn() {
 fn main() {
     let y = vec![format!("World")];
     call(|| {
-        <Vec<String> as Clone>::clone(&y).into_iter();
+        <Vec<String> as Clone>::clone(&y.clone()).into_iter();
         //~^ ERROR cannot move out of `y`, a captured variable in an `Fn` closure
     });
 }
diff --git a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr
index a2ff70255f5..177e9c8d248 100644
--- a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr
+++ b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr
@@ -16,6 +16,10 @@ help: you can `clone` the value and consume it, but this might not be your desir
    |
 LL |         <Vec<String> as Clone>::clone(&y).into_iter();
    |         +++++++++++++++++++++++++++++++ +
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         y.clone().into_iter();
+   |          ++++++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/box/leak-alloc.stderr b/tests/ui/box/leak-alloc.stderr
index 8b8cea3fe84..53ff5f0107d 100644
--- a/tests/ui/box/leak-alloc.stderr
+++ b/tests/ui/box/leak-alloc.stderr
@@ -11,6 +11,12 @@ LL |     drop(alloc);
 LL |
 LL |     use_value(*theref)
    |               ------- borrow later used here
+   |
+note: if `Alloc` implemented `Clone`, you could clone the value
+  --> $DIR/leak-alloc.rs:8:1
+   |
+LL | struct Alloc {}
+   | ^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/btreemap/btreemap_dropck.stderr b/tests/ui/btreemap/btreemap_dropck.stderr
index 805c2112bdc..873f8cf9a01 100644
--- a/tests/ui/btreemap/btreemap_dropck.stderr
+++ b/tests/ui/btreemap/btreemap_dropck.stderr
@@ -9,6 +9,12 @@ LL |     drop(s);
    |          ^ move out of `s` occurs here
 LL | }
    | - borrow might be used here, when `_map` is dropped and runs the `Drop` code for type `BTreeMap`
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let _map = BTreeMap::from_iter([((), PrintOnDrop(&s))]);
+LL +     let _map = BTreeMap::from_iter([((), PrintOnDrop(s.clone()))]);
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/check-static-values-constraints.stderr b/tests/ui/check-static-values-constraints.stderr
index dee1f2b1210..fe5f2a34272 100644
--- a/tests/ui/check-static-values-constraints.stderr
+++ b/tests/ui/check-static-values-constraints.stderr
@@ -160,6 +160,10 @@ help: consider borrowing here
    |
 LL |         &x
    |         +
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         x.clone()
+   |          ++++++++
 
 error: aborting due to 17 previous errors
 
diff --git a/tests/ui/derives/deriving-with-repr-packed-move-errors.stderr b/tests/ui/derives/deriving-with-repr-packed-move-errors.stderr
index c538061b365..2de4ee4eabd 100644
--- a/tests/ui/derives/deriving-with-repr-packed-move-errors.stderr
+++ b/tests/ui/derives/deriving-with-repr-packed-move-errors.stderr
@@ -8,6 +8,10 @@ LL | struct StructA(String);
    |
    = note: `#[derive(Debug)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour
    = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL | struct StructA(String.clone());
+   |                      ++++++++
 
 error[E0507]: cannot move out of `self` which is behind a shared reference
   --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16
@@ -19,6 +23,10 @@ LL | struct StructA(String);
    |
    = note: `#[derive(PartialEq)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour
    = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL | struct StructA(String.clone());
+   |                      ++++++++
 
 error[E0507]: cannot move out of `other` which is behind a shared reference
   --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16
@@ -30,6 +38,10 @@ LL | struct StructA(String);
    |
    = note: `#[derive(PartialEq)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour
    = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL | struct StructA(String.clone());
+   |                      ++++++++
 
 error[E0507]: cannot move out of `self` which is behind a shared reference
   --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16
@@ -41,6 +53,10 @@ LL | struct StructA(String);
    |
    = note: `#[derive(PartialOrd)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour
    = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL | struct StructA(String.clone());
+   |                      ++++++++
 
 error[E0507]: cannot move out of `other` which is behind a shared reference
   --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16
@@ -52,6 +68,10 @@ LL | struct StructA(String);
    |
    = note: `#[derive(PartialOrd)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour
    = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL | struct StructA(String.clone());
+   |                      ++++++++
 
 error[E0507]: cannot move out of `self` which is behind a shared reference
   --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16
@@ -63,6 +83,10 @@ LL | struct StructA(String);
    |
    = note: `#[derive(Ord)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour
    = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL | struct StructA(String.clone());
+   |                      ++++++++
 
 error[E0507]: cannot move out of `other` which is behind a shared reference
   --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16
@@ -74,6 +98,10 @@ LL | struct StructA(String);
    |
    = note: `#[derive(Ord)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour
    = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL | struct StructA(String.clone());
+   |                      ++++++++
 
 error[E0507]: cannot move out of `self` which is behind a shared reference
   --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16
@@ -85,6 +113,10 @@ LL | struct StructA(String);
    |
    = note: `#[derive(Hash)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour
    = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL | struct StructA(String.clone());
+   |                      ++++++++
 
 error[E0507]: cannot move out of `self` which is behind a shared reference
   --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16
@@ -96,78 +128,142 @@ LL | struct StructA(String);
    |
    = note: `#[derive(Clone)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour
    = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL | struct StructA(String.clone());
+   |                      ++++++++
 
 error[E0507]: cannot move out of `self` which is behind a shared reference
   --> $DIR/deriving-with-repr-packed-move-errors.rs:28:9
    |
 LL |         self.0
    |         ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         self.0.clone()
+   |               ++++++++
 
 error[E0507]: cannot move out of `self` which is behind a shared reference
   --> $DIR/deriving-with-repr-packed-move-errors.rs:38:20
    |
 LL |         let x = &{ self.0 };
    |                    ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         let x = &{ self.0.clone() };
+   |                          ++++++++
 
 error[E0507]: cannot move out of `self` which is behind a shared reference
   --> $DIR/deriving-with-repr-packed-move-errors.rs:45:12
    |
 LL |         ({ self.0 }) == ({ other.0 })
    |            ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         ({ self.0.clone() }) == ({ other.0 })
+   |                  ++++++++
 
 error[E0507]: cannot move out of `other` which is behind a shared reference
   --> $DIR/deriving-with-repr-packed-move-errors.rs:45:28
    |
 LL |         ({ self.0 }) == ({ other.0 })
    |                            ^^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         ({ self.0 }) == ({ other.0.clone() })
+   |                                   ++++++++
 
 error[E0507]: cannot move out of `self` which is behind a shared reference
   --> $DIR/deriving-with-repr-packed-move-errors.rs:53:36
    |
 LL |         PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 })
    |                                    ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         PartialOrd::partial_cmp(&{ self.0.clone() }, &{ other.0 })
+   |                                          ++++++++
 
 error[E0507]: cannot move out of `other` which is behind a shared reference
   --> $DIR/deriving-with-repr-packed-move-errors.rs:53:49
    |
 LL |         PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 })
    |                                                 ^^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         PartialOrd::partial_cmp(&{ self.0 }, &{ other.0.clone() })
+   |                                                        ++++++++
 
 error[E0507]: cannot move out of `self` which is behind a shared reference
   --> $DIR/deriving-with-repr-packed-move-errors.rs:68:20
    |
 LL |         let x = &{ self.0 };
    |                    ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         let x = &{ self.0.clone() };
+   |                          ++++++++
 
 error[E0507]: cannot move out of `self` which is behind a shared reference
   --> $DIR/deriving-with-repr-packed-move-errors.rs:75:12
    |
 LL |         ({ self.0 }) == ({ other.0 })
    |            ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         ({ self.0.clone() }) == ({ other.0 })
+   |                  ++++++++
 
 error[E0507]: cannot move out of `other` which is behind a shared reference
   --> $DIR/deriving-with-repr-packed-move-errors.rs:75:28
    |
 LL |         ({ self.0 }) == ({ other.0 })
    |                            ^^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         ({ self.0 }) == ({ other.0.clone() })
+   |                                   ++++++++
 
 error[E0507]: cannot move out of `self` which is behind a shared reference
   --> $DIR/deriving-with-repr-packed-move-errors.rs:83:36
    |
 LL |         PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 })
    |                                    ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         PartialOrd::partial_cmp(&{ self.0.clone() }, &{ other.0 })
+   |                                          ++++++++
 
 error[E0507]: cannot move out of `other` which is behind a shared reference
   --> $DIR/deriving-with-repr-packed-move-errors.rs:83:49
    |
 LL |         PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 })
    |                                                 ^^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         PartialOrd::partial_cmp(&{ self.0 }, &{ other.0.clone() })
+   |                                                        ++++++++
 
 error[E0507]: cannot move out of `arg` which is behind a shared reference
   --> $DIR/deriving-with-repr-packed-move-errors.rs:92:5
    |
 LL |     arg.0
    |     ^^^^^ move occurs because `arg.0` has type `String`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     arg.0.clone()
+   |          ++++++++
 
 error: aborting due to 21 previous errors
 
diff --git a/tests/ui/derives/deriving-with-repr-packed.stderr b/tests/ui/derives/deriving-with-repr-packed.stderr
index 151be6901b0..26ac532263f 100644
--- a/tests/ui/derives/deriving-with-repr-packed.stderr
+++ b/tests/ui/derives/deriving-with-repr-packed.stderr
@@ -36,6 +36,11 @@ LL | #[repr(packed)]
 LL | struct X(Y);
    |          ^ move occurs because `self.0` has type `Y`, which does not implement the `Copy` trait
    |
+note: if `Y` implemented `Clone`, you could clone the value
+  --> $DIR/deriving-with-repr-packed.rs:16:1
+   |
+LL | struct Y(usize);
+   | ^^^^^^^^
    = note: `#[derive(Debug)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour
    = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info)
 
diff --git a/tests/ui/dropck/drop-with-active-borrows-1.stderr b/tests/ui/dropck/drop-with-active-borrows-1.stderr
index 229514c6fee..7d1633267f0 100644
--- a/tests/ui/dropck/drop-with-active-borrows-1.stderr
+++ b/tests/ui/dropck/drop-with-active-borrows-1.stderr
@@ -9,6 +9,11 @@ LL |     drop(a);
    |          ^ move out of `a` occurs here
 LL |     for s in &b {
    |              -- borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     let b: Vec<&str> = a.clone().lines().collect();
+   |                         ++++++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/error-codes/E0504.stderr b/tests/ui/error-codes/E0504.stderr
index c8a48961cb3..900cb706bd9 100644
--- a/tests/ui/error-codes/E0504.stderr
+++ b/tests/ui/error-codes/E0504.stderr
@@ -13,6 +13,12 @@ LL |         println!("child function: {}", fancy_num.num);
 ...
 LL |     println!("main function: {}", fancy_ref.num);
    |                                   ------------- borrow later used here
+   |
+note: if `FancyNum` implemented `Clone`, you could clone the value
+  --> $DIR/E0504.rs:1:1
+   |
+LL | struct FancyNum {
+   | ^^^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/error-codes/E0505.stderr b/tests/ui/error-codes/E0505.stderr
index 250680d2c1c..ce01298a70d 100644
--- a/tests/ui/error-codes/E0505.stderr
+++ b/tests/ui/error-codes/E0505.stderr
@@ -10,6 +10,12 @@ LL |         eat(x);
    |             ^ move out of `x` occurs here
 LL |         _ref_to_val.use_ref();
    |         ----------- borrow later used here
+   |
+note: if `Value` implemented `Clone`, you could clone the value
+  --> $DIR/E0505.rs:1:1
+   |
+LL | struct Value {}
+   | ^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/error-codes/E0507.stderr b/tests/ui/error-codes/E0507.stderr
index 767fedfccbf..60a4daa9d38 100644
--- a/tests/ui/error-codes/E0507.stderr
+++ b/tests/ui/error-codes/E0507.stderr
@@ -11,6 +11,11 @@ note: `TheDarkKnight::nothing_is_true` takes ownership of the receiver `self`, w
    |
 LL |     fn nothing_is_true(self) {}
    |                        ^^^^
+note: if `TheDarkKnight` implemented `Clone`, you could clone the value
+  --> $DIR/E0507.rs:3:1
+   |
+LL | struct TheDarkKnight;
+   | ^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/error-codes/E0508-fail.stderr b/tests/ui/error-codes/E0508-fail.stderr
index 1153b1d09c7..96d3bcb67a5 100644
--- a/tests/ui/error-codes/E0508-fail.stderr
+++ b/tests/ui/error-codes/E0508-fail.stderr
@@ -7,6 +7,11 @@ LL |     let _value = array[0];
    |                  cannot move out of here
    |                  move occurs because `array[_]` has type `NonCopy`, which does not implement the `Copy` trait
    |
+note: if `NonCopy` implemented `Clone`, you could clone the value
+  --> $DIR/E0508-fail.rs:1:1
+   |
+LL | struct NonCopy;
+   | ^^^^^^^^^^^^^^
 help: consider borrowing here
    |
 LL |     let _value = &array[0];
diff --git a/tests/ui/error-codes/E0508.stderr b/tests/ui/error-codes/E0508.stderr
index 4c864e24144..c1b622e2432 100644
--- a/tests/ui/error-codes/E0508.stderr
+++ b/tests/ui/error-codes/E0508.stderr
@@ -7,6 +7,11 @@ LL |     let _value = array[0];
    |                  cannot move out of here
    |                  move occurs because `array[_]` has type `NonCopy`, which does not implement the `Copy` trait
    |
+note: if `NonCopy` implemented `Clone`, you could clone the value
+  --> $DIR/E0508.rs:1:1
+   |
+LL | struct NonCopy;
+   | ^^^^^^^^^^^^^^
 help: consider borrowing here
    |
 LL |     let _value = &array[0];
diff --git a/tests/ui/error-codes/E0509.stderr b/tests/ui/error-codes/E0509.stderr
index 59843a5491a..75c372d0440 100644
--- a/tests/ui/error-codes/E0509.stderr
+++ b/tests/ui/error-codes/E0509.stderr
@@ -7,6 +7,11 @@ LL |     let fancy_field = drop_struct.fancy;
    |                       cannot move out of here
    |                       move occurs because `drop_struct.fancy` has type `FancyNum`, which does not implement the `Copy` trait
    |
+note: if `FancyNum` implemented `Clone`, you could clone the value
+  --> $DIR/E0509.rs:1:1
+   |
+LL | struct FancyNum {
+   | ^^^^^^^^^^^^^^^
 help: consider borrowing here
    |
 LL |     let fancy_field = &drop_struct.fancy;
diff --git a/tests/ui/fn/implied-bounds-unnorm-associated-type-4.stderr b/tests/ui/fn/implied-bounds-unnorm-associated-type-4.stderr
index 3be630e2b23..b8ec2e3b7e7 100644
--- a/tests/ui/fn/implied-bounds-unnorm-associated-type-4.stderr
+++ b/tests/ui/fn/implied-bounds-unnorm-associated-type-4.stderr
@@ -10,6 +10,12 @@ LL |     drop(x);
 LL |
 LL |     println!("{}", y);
    |                    - borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let y = f(&x, ());
+LL +     let y = f(x.clone(), ());
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/fn/implied-bounds-unnorm-associated-type-5.stderr b/tests/ui/fn/implied-bounds-unnorm-associated-type-5.stderr
index bf6d77b6269..382ab8636a2 100644
--- a/tests/ui/fn/implied-bounds-unnorm-associated-type-5.stderr
+++ b/tests/ui/fn/implied-bounds-unnorm-associated-type-5.stderr
@@ -27,6 +27,12 @@ LL |     drop(x);
    |          ^ move out of `x` occurs here
 LL |     println!("{}", y);
    |                    - borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let y = f(&x, ());
+LL +     let y = f(x.clone(), ());
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/fn/implied-bounds-unnorm-associated-type.stderr b/tests/ui/fn/implied-bounds-unnorm-associated-type.stderr
index c2a8fa741ca..ce97d8527e8 100644
--- a/tests/ui/fn/implied-bounds-unnorm-associated-type.stderr
+++ b/tests/ui/fn/implied-bounds-unnorm-associated-type.stderr
@@ -10,6 +10,12 @@ LL |     drop(x);
 LL |
 LL |     println!("{}", y);
    |                    - borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let y = f(&x, ());
+LL +     let y = f(x.clone(), ());
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr b/tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr
index 16808f29dac..d167a60dad3 100644
--- a/tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr
+++ b/tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr
@@ -6,6 +6,11 @@ LL |     let _b = A { y: Arc::new(3), ..a };
    |              |
    |              cannot move out of here
    |              move occurs because `a.x` has type `Arc<isize>`, which does not implement the `Copy` trait
+   |
+help: clone the value from the field instead of using the functional record update syntax
+   |
+LL |     let _b = A { y: Arc::new(3), x: a.x.clone() };
+   |                                ~~~~~~~~~~~~~~~~
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/issues/issue-17385.stderr b/tests/ui/issues/issue-17385.stderr
index 77aa201b335..988db0fb1fc 100644
--- a/tests/ui/issues/issue-17385.stderr
+++ b/tests/ui/issues/issue-17385.stderr
@@ -7,6 +7,12 @@ LL |     drop(foo);
    |          --- value moved here
 LL |     match foo {
    |     ^^^^^^^^^ value used here after move
+   |
+note: if `X` implemented `Clone`, you could clone the value
+  --> $DIR/issue-17385.rs:1:1
+   |
+LL | struct X(isize);
+   | ^^^^^^^^
 
 error[E0382]: use of moved value: `e`
   --> $DIR/issue-17385.rs:25:11
@@ -17,6 +23,12 @@ LL |     drop(e);
    |          - value moved here
 LL |     match e {
    |           ^ value used here after move
+   |
+note: if `Enum` implemented `Clone`, you could clone the value
+  --> $DIR/issue-17385.rs:3:1
+   |
+LL | enum Enum {
+   | ^^^^^^^^^
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/issues/issue-24357.rs b/tests/ui/issues/issue-24357.rs
index 152e69ebc87..d1a9e37251e 100644
--- a/tests/ui/issues/issue-24357.rs
+++ b/tests/ui/issues/issue-24357.rs
@@ -1,4 +1,4 @@
-struct NoCopy;
+struct NoCopy; //~ NOTE if `NoCopy` implemented `Clone`, you could clone the value
 fn main() {
    let x = NoCopy;
    //~^ NOTE move occurs because `x` has type `NoCopy`
diff --git a/tests/ui/issues/issue-24357.stderr b/tests/ui/issues/issue-24357.stderr
index 08a5a8ac56e..6d50eea7e21 100644
--- a/tests/ui/issues/issue-24357.stderr
+++ b/tests/ui/issues/issue-24357.stderr
@@ -11,6 +11,12 @@ LL |    let f = move || { let y = x; };
 ...
 LL |    let z = x;
    |            ^ value used here after move
+   |
+note: if `NoCopy` implemented `Clone`, you could clone the value
+  --> $DIR/issue-24357.rs:1:1
+   |
+LL | struct NoCopy;
+   | ^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/issues/issue-2590.stderr b/tests/ui/issues/issue-2590.stderr
index 517b4814eae..822856652e9 100644
--- a/tests/ui/issues/issue-2590.stderr
+++ b/tests/ui/issues/issue-2590.stderr
@@ -3,6 +3,11 @@ error[E0507]: cannot move out of `self.tokens` which is behind a shared referenc
    |
 LL |         self.tokens
    |         ^^^^^^^^^^^ move occurs because `self.tokens` has type `Vec<isize>`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         self.tokens.clone()
+   |                    ++++++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/issues/issue-40402-ref-hints/issue-40402-1.stderr b/tests/ui/issues/issue-40402-ref-hints/issue-40402-1.stderr
index 7976d090542..d27b6e6324f 100644
--- a/tests/ui/issues/issue-40402-ref-hints/issue-40402-1.stderr
+++ b/tests/ui/issues/issue-40402-ref-hints/issue-40402-1.stderr
@@ -8,6 +8,10 @@ help: consider borrowing here
    |
 LL |     let e = &f.v[0];
    |             +
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     let e = f.v[0].clone();
+   |                   ++++++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/issues/issue-52262.stderr b/tests/ui/issues/issue-52262.stderr
index ce8e6fe2bf8..51959f22b97 100644
--- a/tests/ui/issues/issue-52262.stderr
+++ b/tests/ui/issues/issue-52262.stderr
@@ -3,6 +3,12 @@ error[E0507]: cannot move out of `*key` which is behind a shared reference
    |
 LL |                 String::from_utf8(*key).unwrap()
    |                                   ^^^^ move occurs because `*key` has type `Vec<u8>`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -                 String::from_utf8(*key).unwrap()
+LL +                 String::from_utf8(key.clone()).unwrap()
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/mir/issue-102389.stderr b/tests/ui/mir/issue-102389.stderr
index 1f04d119b56..838eaffb5a0 100644
--- a/tests/ui/mir/issue-102389.stderr
+++ b/tests/ui/mir/issue-102389.stderr
@@ -3,6 +3,12 @@ error[E0507]: cannot move out of `*inbounds` which is behind a shared reference
    |
 LL |     array[*inbounds as usize]
    |           ^^^^^^^^^ move occurs because `*inbounds` has type `Enum`, which does not implement the `Copy` trait
+   |
+note: if `Enum` implemented `Clone`, you could clone the value
+  --> $DIR/issue-102389.rs:1:1
+   |
+LL | enum Enum { A, B, C }
+   | ^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/moves/issue-72649-uninit-in-loop.rs b/tests/ui/moves/issue-72649-uninit-in-loop.rs
index 56c225bab8c..86f389cb3af 100644
--- a/tests/ui/moves/issue-72649-uninit-in-loop.rs
+++ b/tests/ui/moves/issue-72649-uninit-in-loop.rs
@@ -3,6 +3,10 @@
 // 'value moved in previous iteration of loop' message
 
 struct NonCopy;
+//~^ NOTE if `NonCopy` implemented `Clone`
+//~| NOTE if `NonCopy` implemented `Clone`
+//~| NOTE if `NonCopy` implemented `Clone`
+//~| NOTE if `NonCopy` implemented `Clone`
 
 fn good() {
     loop {
diff --git a/tests/ui/moves/issue-72649-uninit-in-loop.stderr b/tests/ui/moves/issue-72649-uninit-in-loop.stderr
index 49a5a188cde..35216f8a66f 100644
--- a/tests/ui/moves/issue-72649-uninit-in-loop.stderr
+++ b/tests/ui/moves/issue-72649-uninit-in-loop.stderr
@@ -1,5 +1,5 @@
 error[E0382]: use of moved value: `value`
-  --> $DIR/issue-72649-uninit-in-loop.rs:20:22
+  --> $DIR/issue-72649-uninit-in-loop.rs:24:22
    |
 LL |         let value = NonCopy{};
    |             ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
@@ -9,9 +9,15 @@ LL |         let _used = value;
 LL |
 LL |         let _used2 = value;
    |                      ^^^^^ value used here after move
+   |
+note: if `NonCopy` implemented `Clone`, you could clone the value
+  --> $DIR/issue-72649-uninit-in-loop.rs:5:1
+   |
+LL | struct NonCopy;
+   | ^^^^^^^^^^^^^^
 
 error[E0382]: use of moved value: `value`
-  --> $DIR/issue-72649-uninit-in-loop.rs:32:26
+  --> $DIR/issue-72649-uninit-in-loop.rs:36:26
    |
 LL |     let value = NonCopy{};
    |         ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
@@ -23,9 +29,15 @@ LL |         let _used = value;
 ...
 LL |             let _used2 = value;
    |                          ^^^^^ value used here after move
+   |
+note: if `NonCopy` implemented `Clone`, you could clone the value
+  --> $DIR/issue-72649-uninit-in-loop.rs:5:1
+   |
+LL | struct NonCopy;
+   | ^^^^^^^^^^^^^^
 
 error[E0382]: use of moved value: `value`
-  --> $DIR/issue-72649-uninit-in-loop.rs:42:21
+  --> $DIR/issue-72649-uninit-in-loop.rs:46:21
    |
 LL |     let value = NonCopy{};
    |         ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
@@ -34,9 +46,15 @@ LL |     loop {
    |     ---- inside of this loop
 LL |         let _used = value;
    |                     ^^^^^ value moved here, in previous iteration of loop
+   |
+note: if `NonCopy` implemented `Clone`, you could clone the value
+  --> $DIR/issue-72649-uninit-in-loop.rs:5:1
+   |
+LL | struct NonCopy;
+   | ^^^^^^^^^^^^^^
 
 error[E0382]: use of moved value: `value`
-  --> $DIR/issue-72649-uninit-in-loop.rs:53:22
+  --> $DIR/issue-72649-uninit-in-loop.rs:57:22
    |
 LL |     let mut value = NonCopy{};
    |         --------- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
@@ -45,9 +63,15 @@ LL |     loop {
    |     ---- inside of this loop
 LL |         let _used2 = value;
    |                      ^^^^^ value moved here, in previous iteration of loop
+   |
+note: if `NonCopy` implemented `Clone`, you could clone the value
+  --> $DIR/issue-72649-uninit-in-loop.rs:5:1
+   |
+LL | struct NonCopy;
+   | ^^^^^^^^^^^^^^
 
 error[E0381]: used binding `value` isn't initialized
-  --> $DIR/issue-72649-uninit-in-loop.rs:61:21
+  --> $DIR/issue-72649-uninit-in-loop.rs:65:21
    |
 LL |         let value: NonCopy;
    |             ----- binding declared here but left uninitialized
@@ -60,7 +84,7 @@ LL |         let value: NonCopy = value;
    |                            +++++++
 
 error[E0381]: used binding `value` isn't initialized
-  --> $DIR/issue-72649-uninit-in-loop.rs:69:21
+  --> $DIR/issue-72649-uninit-in-loop.rs:73:21
    |
 LL |     let mut value: NonCopy;
    |         --------- binding declared here but left uninitialized
diff --git a/tests/ui/moves/issue-75904-move-closure-loop.stderr b/tests/ui/moves/issue-75904-move-closure-loop.stderr
index 6f04105a35e..b6ad906bbdb 100644
--- a/tests/ui/moves/issue-75904-move-closure-loop.stderr
+++ b/tests/ui/moves/issue-75904-move-closure-loop.stderr
@@ -4,11 +4,18 @@ error[E0382]: use of moved value: `a`
 LL |     let mut a = NotCopy;
    |         ----- move occurs because `a` has type `NotCopy`, which does not implement the `Copy` trait
 LL |     loop {
+   |     ---- inside of this loop
 LL |         || {
    |         ^^ value moved into closure here, in previous iteration of loop
 LL |             &mut a;
 LL |             a;
    |             - use occurs due to use in closure
+   |
+note: if `NotCopy` implemented `Clone`, you could clone the value
+  --> $DIR/issue-75904-move-closure-loop.rs:5:1
+   |
+LL | struct NotCopy;
+   | ^^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/moves/move-fn-self-receiver.stderr b/tests/ui/moves/move-fn-self-receiver.stderr
index 17f48f5f7bf..e6bf52276ac 100644
--- a/tests/ui/moves/move-fn-self-receiver.stderr
+++ b/tests/ui/moves/move-fn-self-receiver.stderr
@@ -101,6 +101,12 @@ LL |     mut_foo;
    |     ^^^^^^^ move out of `mut_foo` occurs here
 LL |     ret;
    |     --- borrow later used here
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/move-fn-self-receiver.rs:5:1
+   |
+LL | struct Foo;
+   | ^^^^^^^^^^
 
 error[E0382]: use of moved value: `rc_foo`
   --> $DIR/move-fn-self-receiver.rs:55:5
@@ -132,6 +138,11 @@ LL |     foo_add + Foo;
 LL |     foo_add;
    |     ^^^^^^^ value used here after move
    |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/move-fn-self-receiver.rs:5:1
+   |
+LL | struct Foo;
+   | ^^^^^^^^^^
 note: calling this operator moves the left-hand side
   --> $SRC_DIR/core/src/ops/arith.rs:LL:COL
 
diff --git a/tests/ui/moves/move-out-of-array-1.stderr b/tests/ui/moves/move-out-of-array-1.stderr
index aa0251dbd85..9e4a08e0cef 100644
--- a/tests/ui/moves/move-out-of-array-1.stderr
+++ b/tests/ui/moves/move-out-of-array-1.stderr
@@ -6,6 +6,12 @@ LL |     a[i]
    |     |
    |     cannot move out of here
    |     move occurs because `a[_]` has type `D`, which does not implement the `Copy` trait
+   |
+note: if `D` implemented `Clone`, you could clone the value
+  --> $DIR/move-out-of-array-1.rs:5:1
+   |
+LL | struct D { _x: u8 }
+   | ^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/moves/moves-based-on-type-capture-clause-bad.rs b/tests/ui/moves/moves-based-on-type-capture-clause-bad.rs
index b2f68352f89..9d7277c1c24 100644
--- a/tests/ui/moves/moves-based-on-type-capture-clause-bad.rs
+++ b/tests/ui/moves/moves-based-on-type-capture-clause-bad.rs
@@ -2,7 +2,7 @@ use std::thread;
 
 fn main() {
     let x = "Hello world!".to_string();
-    thread::spawn(move|| {
+    thread::spawn(move || {
         println!("{}", x);
     });
     println!("{}", x); //~ ERROR borrow of moved value
diff --git a/tests/ui/moves/moves-based-on-type-capture-clause-bad.stderr b/tests/ui/moves/moves-based-on-type-capture-clause-bad.stderr
index 5e527bf445e..c2b9aeab237 100644
--- a/tests/ui/moves/moves-based-on-type-capture-clause-bad.stderr
+++ b/tests/ui/moves/moves-based-on-type-capture-clause-bad.stderr
@@ -3,8 +3,8 @@ error[E0382]: borrow of moved value: `x`
    |
 LL |     let x = "Hello world!".to_string();
    |         - move occurs because `x` has type `String`, which does not implement the `Copy` trait
-LL |     thread::spawn(move|| {
-   |                   ------ value moved into closure here
+LL |     thread::spawn(move || {
+   |                   ------- value moved into closure here
 LL |         println!("{}", x);
    |                        - variable moved due to use in closure
 LL |     });
diff --git a/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr b/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr
index 513631b2060..523134a9425 100644
--- a/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr
+++ b/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr
@@ -7,6 +7,11 @@ LL |     let _f = to_fn(|| test(i));
    |                    --      ^ move occurs because `i` has type `Box<usize>`, which does not implement the `Copy` trait
    |                    |
    |                    captured by this `Fn` closure
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     let _f = to_fn(|| test(i.clone()));
+   |                             ++++++++
 
 error: aborting due to 1 previous error
 
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)]
diff --git a/tests/ui/moves/suggest-clone.fixed b/tests/ui/moves/suggest-clone.fixed
index 59276a7b96d..59a162e72c1 100644
--- a/tests/ui/moves/suggest-clone.fixed
+++ b/tests/ui/moves/suggest-clone.fixed
@@ -7,5 +7,5 @@ impl Foo {
 }
 fn main() {
     let foo = &Foo;
-    <Foo as Clone>::clone(&foo).foo(); //~ ERROR cannot move out
+    <Foo as Clone>::clone(&foo.clone()).foo(); //~ ERROR cannot move out
 }
diff --git a/tests/ui/moves/suggest-clone.stderr b/tests/ui/moves/suggest-clone.stderr
index 25e89a58955..f8e0ccdfcef 100644
--- a/tests/ui/moves/suggest-clone.stderr
+++ b/tests/ui/moves/suggest-clone.stderr
@@ -15,6 +15,10 @@ help: you can `clone` the value and consume it, but this might not be your desir
    |
 LL |     <Foo as Clone>::clone(&foo).foo();
    |     +++++++++++++++++++++++   +
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     foo.clone().foo();
+   |        ++++++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/nll/cannot-move-block-spans.stderr b/tests/ui/nll/cannot-move-block-spans.stderr
index 0dc5c08ea5f..d96773e1edf 100644
--- a/tests/ui/nll/cannot-move-block-spans.stderr
+++ b/tests/ui/nll/cannot-move-block-spans.stderr
@@ -9,6 +9,11 @@ help: consider removing the dereference here
 LL -     let x = { *r };
 LL +     let x = { r };
    |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let x = { *r };
+LL +     let x = { r.clone() };
+   |
 
 error[E0507]: cannot move out of `*r` which is behind a shared reference
   --> $DIR/cannot-move-block-spans.rs:6:22
@@ -21,6 +26,11 @@ help: consider removing the dereference here
 LL -     let y = unsafe { *r };
 LL +     let y = unsafe { r };
    |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let y = unsafe { *r };
+LL +     let y = unsafe { r.clone() };
+   |
 
 error[E0507]: cannot move out of `*r` which is behind a shared reference
   --> $DIR/cannot-move-block-spans.rs:7:26
@@ -33,6 +43,11 @@ help: consider removing the dereference here
 LL -     let z = loop { break *r; };
 LL +     let z = loop { break r; };
    |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let z = loop { break *r; };
+LL +     let z = loop { break r.clone(); };
+   |
 
 error[E0508]: cannot move out of type `[String; 2]`, a non-copy array
   --> $DIR/cannot-move-block-spans.rs:11:15
@@ -47,6 +62,10 @@ help: consider borrowing here
    |
 LL |     let x = { &arr[0] };
    |               +
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     let x = { arr[0].clone() };
+   |                     ++++++++
 
 error[E0508]: cannot move out of type `[String; 2]`, a non-copy array
   --> $DIR/cannot-move-block-spans.rs:12:22
@@ -61,6 +80,10 @@ help: consider borrowing here
    |
 LL |     let y = unsafe { &arr[0] };
    |                      +
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     let y = unsafe { arr[0].clone() };
+   |                            ++++++++
 
 error[E0508]: cannot move out of type `[String; 2]`, a non-copy array
   --> $DIR/cannot-move-block-spans.rs:13:26
@@ -75,6 +98,10 @@ help: consider borrowing here
    |
 LL |     let z = loop { break &arr[0]; };
    |                          +
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     let z = loop { break arr[0].clone(); };
+   |                                ++++++++
 
 error[E0507]: cannot move out of `*r` which is behind a shared reference
   --> $DIR/cannot-move-block-spans.rs:17:38
@@ -87,6 +114,11 @@ help: consider removing the dereference here
 LL -     let x = { let mut u = 0; u += 1; *r };
 LL +     let x = { let mut u = 0; u += 1; r };
    |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let x = { let mut u = 0; u += 1; *r };
+LL +     let x = { let mut u = 0; u += 1; r.clone() };
+   |
 
 error[E0507]: cannot move out of `*r` which is behind a shared reference
   --> $DIR/cannot-move-block-spans.rs:18:45
@@ -99,6 +131,11 @@ help: consider removing the dereference here
 LL -     let y = unsafe { let mut u = 0; u += 1; *r };
 LL +     let y = unsafe { let mut u = 0; u += 1; r };
    |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let y = unsafe { let mut u = 0; u += 1; *r };
+LL +     let y = unsafe { let mut u = 0; u += 1; r.clone() };
+   |
 
 error[E0507]: cannot move out of `*r` which is behind a shared reference
   --> $DIR/cannot-move-block-spans.rs:19:49
@@ -111,6 +148,11 @@ help: consider removing the dereference here
 LL -     let z = loop { let mut u = 0; u += 1; break *r; u += 2; };
 LL +     let z = loop { let mut u = 0; u += 1; break r; u += 2; };
    |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let z = loop { let mut u = 0; u += 1; break *r; u += 2; };
+LL +     let z = loop { let mut u = 0; u += 1; break r.clone(); u += 2; };
+   |
 
 error: aborting due to 9 previous errors
 
diff --git a/tests/ui/nll/closure-access-spans.stderr b/tests/ui/nll/closure-access-spans.stderr
index 3e98fbd5e1d..f789e5e9f95 100644
--- a/tests/ui/nll/closure-access-spans.stderr
+++ b/tests/ui/nll/closure-access-spans.stderr
@@ -57,6 +57,12 @@ LL |     || x;
    |     move out of `x` occurs here
 LL |     r.use_ref();
    |     - borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     let r = &x;
+LL +     let r = x.clone();
+   |
 
 error[E0382]: borrow of moved value: `x`
   --> $DIR/closure-access-spans.rs:35:5
@@ -103,6 +109,11 @@ LL |     || *x = String::new();
    |     ^^ -- borrow occurs due to use in closure
    |     |
    |     value borrowed here after move
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     let r = x.clone();
+   |              ++++++++
 
 error[E0382]: use of moved value: `x`
   --> $DIR/closure-access-spans.rs:50:5
@@ -115,6 +126,11 @@ LL |     || x;
    |     ^^ - use occurs due to use in closure
    |     |
    |     value used here after move
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     let r = x.clone();
+   |              ++++++++
 
 error: aborting due to 9 previous errors
 
diff --git a/tests/ui/nll/closure-move-spans.fixed b/tests/ui/nll/closure-move-spans.fixed
new file mode 100644
index 00000000000..edd74e434e0
--- /dev/null
+++ b/tests/ui/nll/closure-move-spans.fixed
@@ -0,0 +1,23 @@
+// check that moves due to a closure capture give a special note
+//@ run-rustfix
+#![allow(unused_variables, unused_must_use, dead_code)]
+
+fn move_after_move(x: String) {
+    || x.clone();
+    let y = x; //~ ERROR
+}
+
+fn borrow_after_move(x: String) {
+    || x.clone();
+    let y = &x; //~ ERROR
+}
+
+fn borrow_mut_after_move(mut x: String) {
+    || x.clone();
+    let y = &mut x; //~ ERROR
+}
+
+fn fn_ref<F: Fn()>(f: F) -> F { f }
+fn fn_mut<F: FnMut()>(f: F) -> F { f }
+
+fn main() {}
diff --git a/tests/ui/nll/closure-move-spans.rs b/tests/ui/nll/closure-move-spans.rs
index bf2431870a9..bba5c3776e6 100644
--- a/tests/ui/nll/closure-move-spans.rs
+++ b/tests/ui/nll/closure-move-spans.rs
@@ -1,4 +1,6 @@
 // check that moves due to a closure capture give a special note
+//@ run-rustfix
+#![allow(unused_variables, unused_must_use, dead_code)]
 
 fn move_after_move(x: String) {
     || x;
diff --git a/tests/ui/nll/closure-move-spans.stderr b/tests/ui/nll/closure-move-spans.stderr
index 0446ef7b066..0b1da57605c 100644
--- a/tests/ui/nll/closure-move-spans.stderr
+++ b/tests/ui/nll/closure-move-spans.stderr
@@ -1,5 +1,5 @@
 error[E0382]: use of moved value: `x`
-  --> $DIR/closure-move-spans.rs:5:13
+  --> $DIR/closure-move-spans.rs:7:13
    |
 LL | fn move_after_move(x: String) {
    |                    - move occurs because `x` has type `String`, which does not implement the `Copy` trait
@@ -9,9 +9,14 @@ LL |     || x;
    |     value moved into closure here
 LL |     let y = x;
    |             ^ value used here after move
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     || x.clone();
+   |         ++++++++
 
 error[E0382]: borrow of moved value: `x`
-  --> $DIR/closure-move-spans.rs:10:13
+  --> $DIR/closure-move-spans.rs:12:13
    |
 LL | fn borrow_after_move(x: String) {
    |                      - move occurs because `x` has type `String`, which does not implement the `Copy` trait
@@ -21,9 +26,14 @@ LL |     || x;
    |     value moved into closure here
 LL |     let y = &x;
    |             ^^ value borrowed here after move
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     || x.clone();
+   |         ++++++++
 
 error[E0382]: borrow of moved value: `x`
-  --> $DIR/closure-move-spans.rs:15:13
+  --> $DIR/closure-move-spans.rs:17:13
    |
 LL | fn borrow_mut_after_move(mut x: String) {
    |                          ----- move occurs because `x` has type `String`, which does not implement the `Copy` trait
@@ -33,6 +43,11 @@ LL |     || x;
    |     value moved into closure here
 LL |     let y = &mut x;
    |             ^^^^^^ value borrowed here after move
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     || x.clone();
+   |         ++++++++
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/nll/closures-in-loops.stderr b/tests/ui/nll/closures-in-loops.stderr
index 2c1008c516c..050b220e626 100644
--- a/tests/ui/nll/closures-in-loops.stderr
+++ b/tests/ui/nll/closures-in-loops.stderr
@@ -4,10 +4,16 @@ error[E0382]: use of moved value: `x`
 LL | fn repreated_move(x: String) {
    |                   - move occurs because `x` has type `String`, which does not implement the `Copy` trait
 LL |     for i in 0..10 {
+   |     -------------- inside of this loop
 LL |         || x;
    |         ^^ - use occurs due to use in closure
    |         |
    |         value moved into closure here, in previous iteration of loop
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         || x.clone();
+   |             ++++++++
 
 error[E0499]: cannot borrow `x` as mutable more than once at a time
   --> $DIR/closures-in-loops.rs:13:16
diff --git a/tests/ui/nll/issue-21232-partial-init-and-use.stderr b/tests/ui/nll/issue-21232-partial-init-and-use.stderr
index 97ed414b1ec..2aff375f0a7 100644
--- a/tests/ui/nll/issue-21232-partial-init-and-use.stderr
+++ b/tests/ui/nll/issue-21232-partial-init-and-use.stderr
@@ -27,6 +27,12 @@ LL |     let mut s: S<B> = S::new(); drop(s);
    |         move occurs because `s` has type `S<Box<u32>>`, which does not implement the `Copy` trait
 LL |     s.x = 10; s.y = Box::new(20);
    |     ^^^^^^^^ value partially assigned here after move
+   |
+note: if `S<Box<u32>>` implemented `Clone`, you could clone the value
+  --> $DIR/issue-21232-partial-init-and-use.rs:15:1
+   |
+LL | struct S<Y> {
+   | ^^^^^^^^^^^
 
 error[E0382]: assign to part of moved value: `t`
   --> $DIR/issue-21232-partial-init-and-use.rs:116:5
@@ -72,6 +78,12 @@ LL |     let mut s: S<B> = S::new(); drop(s);
    |         move occurs because `s` has type `S<Box<u32>>`, which does not implement the `Copy` trait
 LL |     s.x = 10;
    |     ^^^^^^^^ value partially assigned here after move
+   |
+note: if `S<Box<u32>>` implemented `Clone`, you could clone the value
+  --> $DIR/issue-21232-partial-init-and-use.rs:15:1
+   |
+LL | struct S<Y> {
+   | ^^^^^^^^^^^
 
 error[E0382]: assign to part of moved value: `t`
   --> $DIR/issue-21232-partial-init-and-use.rs:142:5
diff --git a/tests/ui/nll/issue-27282-move-match-input-into-guard.stderr b/tests/ui/nll/issue-27282-move-match-input-into-guard.stderr
index ae797800457..39ec45b20ea 100644
--- a/tests/ui/nll/issue-27282-move-match-input-into-guard.stderr
+++ b/tests/ui/nll/issue-27282-move-match-input-into-guard.stderr
@@ -10,6 +10,11 @@ LL |         _ if { (|| { let bar = b; *bar = false; })();
    |                 --             - variable moved due to use in closure
    |                 |
    |                 value moved into closure here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         _ if { (|| { let bar = b.clone(); *bar = false; })();
+   |                                 ++++++++
 
 error[E0382]: use of moved value: `b`
   --> $DIR/issue-27282-move-match-input-into-guard.rs:24:5
@@ -23,6 +28,11 @@ LL |             (|| { let bar = b; *bar = false; })();
    |              --             - variable moved due to use in closure
    |              |
    |              value moved into closure here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |             (|| { let bar = b.clone(); *bar = false; })();
+   |                              ++++++++
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.stderr b/tests/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.stderr
index 7f9cbc3c30a..f4e7869bf00 100644
--- a/tests/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.stderr
+++ b/tests/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.stderr
@@ -41,6 +41,10 @@ help: consider borrowing here
    |
 LL |     let p = &s.url; p
    |             +
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     let p = s.url.clone(); p
+   |                  ++++++++
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/nll/issue-52086.stderr b/tests/ui/nll/issue-52086.stderr
index 3b2dae9b72b..e4f0c49e557 100644
--- a/tests/ui/nll/issue-52086.stderr
+++ b/tests/ui/nll/issue-52086.stderr
@@ -3,12 +3,22 @@ error[E0507]: cannot move out of an `Rc`
    |
 LL |     drop(x.field);
    |          ^^^^^^^ move occurs because value has type `Vec<i32>`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     drop(x.field.clone());
+   |                 ++++++++
 
 error[E0507]: cannot move out of an `Arc`
   --> $DIR/issue-52086.rs:12:10
    |
 LL |     drop(y.field);
    |          ^^^^^^^ move occurs because value has type `Vec<i32>`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     drop(y.field.clone());
+   |                 ++++++++
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr b/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr
index 587f3071027..fbaec8a6008 100644
--- a/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr
+++ b/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr
@@ -7,6 +7,11 @@ LL |        expect_fn(|| drop(x.0));
    |                  --      ^^^ move occurs because `x.0` has type `Vec<i32>`, which does not implement the `Copy` trait
    |                  |
    |                  captured by this `Fn` closure
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |        expect_fn(|| drop(x.0.clone()));
+   |                             ++++++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/nll/move-errors.stderr b/tests/ui/nll/move-errors.stderr
index 0d994ef29ba..842ecaf524b 100644
--- a/tests/ui/nll/move-errors.stderr
+++ b/tests/ui/nll/move-errors.stderr
@@ -4,6 +4,11 @@ error[E0507]: cannot move out of `*a` which is behind a shared reference
 LL |     let b = *a;
    |             ^^ move occurs because `*a` has type `A`, which does not implement the `Copy` trait
    |
+note: if `A` implemented `Clone`, you could clone the value
+  --> $DIR/move-errors.rs:1:1
+   |
+LL | struct A(String);
+   | ^^^^^^^^
 help: consider removing the dereference here
    |
 LL -     let b = *a;
@@ -19,6 +24,11 @@ LL |     let b = a[0];
    |             cannot move out of here
    |             move occurs because `a[_]` has type `A`, which does not implement the `Copy` trait
    |
+note: if `A` implemented `Clone`, you could clone the value
+  --> $DIR/move-errors.rs:1:1
+   |
+LL | struct A(String);
+   | ^^^^^^^^
 help: consider borrowing here
    |
 LL |     let b = &a[0];
@@ -30,6 +40,11 @@ error[E0507]: cannot move out of `**r` which is behind a shared reference
 LL |     let s = **r;
    |             ^^^ move occurs because `**r` has type `A`, which does not implement the `Copy` trait
    |
+note: if `A` implemented `Clone`, you could clone the value
+  --> $DIR/move-errors.rs:1:1
+   |
+LL | struct A(String);
+   | ^^^^^^^^
 help: consider removing the dereference here
    |
 LL -     let s = **r;
@@ -42,6 +57,11 @@ error[E0507]: cannot move out of an `Rc`
 LL |     let s = *r;
    |             ^^ move occurs because value has type `A`, which does not implement the `Copy` trait
    |
+note: if `A` implemented `Clone`, you could clone the value
+  --> $DIR/move-errors.rs:1:1
+   |
+LL | struct A(String);
+   | ^^^^^^^^
 help: consider removing the dereference here
    |
 LL -     let s = *r;
@@ -57,6 +77,11 @@ LL |     let a = [A("".to_string())][0];
    |             cannot move out of here
    |             move occurs because value has type `A`, which does not implement the `Copy` trait
    |
+note: if `A` implemented `Clone`, you could clone the value
+  --> $DIR/move-errors.rs:1:1
+   |
+LL | struct A(String);
+   | ^^^^^^^^
 help: consider borrowing here
    |
 LL |     let a = &[A("".to_string())][0];
@@ -96,6 +121,12 @@ error[E0507]: cannot move out of `*a` which is behind a shared reference
    |
 LL |     b = *a;
    |         ^^ move occurs because `*a` has type `A`, which does not implement the `Copy` trait
+   |
+note: if `A` implemented `Clone`, you could clone the value
+  --> $DIR/move-errors.rs:1:1
+   |
+LL | struct A(String);
+   | ^^^^^^^^
 
 error[E0508]: cannot move out of type `[B; 1]`, a non-copy array
   --> $DIR/move-errors.rs:74:11
diff --git a/tests/ui/once-cant-call-twice-on-heap.stderr b/tests/ui/once-cant-call-twice-on-heap.stderr
index 33dd840dbc2..42697374115 100644
--- a/tests/ui/once-cant-call-twice-on-heap.stderr
+++ b/tests/ui/once-cant-call-twice-on-heap.stderr
@@ -8,15 +8,13 @@ LL |     blk();
 LL |     blk();
    |     ^^^ value used here after move
    |
-note: this value implements `FnOnce`, which causes it to be moved when called
-  --> $DIR/once-cant-call-twice-on-heap.rs:7:5
+note: `FnOnce` closures can only be called once
+  --> $DIR/once-cant-call-twice-on-heap.rs:6:10
    |
+LL | fn foo<F:FnOnce()>(blk: F) {
+   |          ^^^^^^^^ `F` is made to be an `FnOnce` closure here
 LL |     blk();
-   |     ^^^
-help: consider further restricting this bound
-   |
-LL | fn foo<F:FnOnce() + Copy>(blk: F) {
-   |                   ++++++
+   |     ----- this value implements `FnOnce`, which causes it to be moved when called
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr b/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr
index 9359244c6eb..00964cb8336 100644
--- a/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr
+++ b/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr
@@ -333,6 +333,11 @@ LL |         ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { drop(b); false
    |                                                                  ^ move occurs because `b` has type `&mut U`, which does not implement the `Copy` trait
    |
    = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
+note: if `U` implemented `Clone`, you could clone the value
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:17:5
+   |
+LL |     struct U;
+   |     ^^^^^^^^
 
 error[E0507]: cannot move out of `b` in pattern guard
   --> $DIR/borrowck-pat-ref-mut-and-ref.rs:103:66
@@ -341,6 +346,11 @@ LL |         ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { drop(b); false
    |                                                                  ^ move occurs because `b` has type `&mut U`, which does not implement the `Copy` trait
    |
    = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
+note: if `U` implemented `Clone`, you could clone the value
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:17:5
+   |
+LL |     struct U;
+   |     ^^^^^^^^
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0507]: cannot move out of `a` in pattern guard
diff --git a/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr
index a749361bf30..0ab70c5ae8a 100644
--- a/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr
+++ b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr
@@ -5,6 +5,10 @@ LL |         VecWrapper::A(v) if { drop(v); false } => 1,
    |                                    ^ move occurs because `v` has type `Vec<i32>`, which does not implement the `Copy` trait
    |
    = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         VecWrapper::A(v) if { drop(v.clone()); false } => 1,
+   |                                     ++++++++
 
 error[E0507]: cannot move out of `v` in pattern guard
   --> $DIR/rfc-reject-double-move-across-arms.rs:15:51
@@ -13,6 +17,10 @@ LL |         VecWrapper::A(v) if let Some(()) = { drop(v); None } => 1,
    |                                                   ^ move occurs because `v` has type `Vec<i32>`, which does not implement the `Copy` trait
    |
    = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         VecWrapper::A(v) if let Some(()) = { drop(v.clone()); None } => 1,
+   |                                                    ++++++++
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr
index 9285492b224..c261f994283 100644
--- a/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr
+++ b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr
@@ -5,6 +5,10 @@ LL |         A { a: v } if { drop(v); true } => v,
    |                              ^ move occurs because `v` has type `Box<i32>`, which does not implement the `Copy` trait
    |
    = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         A { a: v } if { drop(v.clone()); true } => v,
+   |                               ++++++++
 
 error[E0507]: cannot move out of `v` in pattern guard
   --> $DIR/rfc-reject-double-move-in-first-arm.rs:17:45
@@ -13,6 +17,10 @@ LL |         A { a: v } if let Some(()) = { drop(v); Some(()) } => v,
    |                                             ^ move occurs because `v` has type `Box<i32>`, which does not implement the `Copy` trait
    |
    = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         A { a: v } if let Some(()) = { drop(v.clone()); Some(()) } => v,
+   |                                              ++++++++
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr b/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr
index 29a606c4f01..f37dc320fa3 100644
--- a/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr
+++ b/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr
@@ -43,6 +43,11 @@ LL |     f(Box::new(|a| {
 LL |
 LL |         foo(f);
    |             ^ move occurs because `f` has type `{closure@$DIR/borrowck-call-is-borrow-issue-12224.rs:52:17: 52:58}`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         foo(f.clone());
+   |              ++++++++
 
 error[E0505]: cannot move out of `f` because it is borrowed
   --> $DIR/borrowck-call-is-borrow-issue-12224.rs:55:16
@@ -57,6 +62,11 @@ LL |     f(Box::new(|a| {
 LL |
 LL |         foo(f);
    |             - move occurs due to use in closure
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |     f.clone()(Box::new(|a| {
+   |      ++++++++
 
 error: aborting due to 5 previous errors
 
diff --git a/tests/ui/span/send-is-not-static-std-sync.stderr b/tests/ui/span/send-is-not-static-std-sync.stderr
index 46534b39168..50b8ffe0114 100644
--- a/tests/ui/span/send-is-not-static-std-sync.stderr
+++ b/tests/ui/span/send-is-not-static-std-sync.stderr
@@ -11,6 +11,12 @@ LL |     drop(y);
 ...
 LL |         *lock.lock().unwrap() = &z;
    |          ---- borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     *lock.lock().unwrap() = &*y;
+LL +     *lock.lock().unwrap() = y.clone();
+   |
 
 error[E0597]: `z` does not live long enough
   --> $DIR/send-is-not-static-std-sync.rs:16:33
@@ -38,6 +44,12 @@ LL |     drop(y);
 ...
 LL |         *lock.write().unwrap() = &z;
    |          ---- borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     *lock.write().unwrap() = &*y;
+LL +     *lock.write().unwrap() = y.clone();
+   |
 
 error[E0597]: `z` does not live long enough
   --> $DIR/send-is-not-static-std-sync.rs:30:34
@@ -65,6 +77,12 @@ LL |     drop(y);
 ...
 LL |         tx.send(&z).unwrap();
    |         -- borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     tx.send(&*y);
+LL +     tx.send(y.clone());
+   |
 
 error[E0597]: `z` does not live long enough
   --> $DIR/send-is-not-static-std-sync.rs:46:17
diff --git a/tests/ui/suggestions/borrow-for-loop-head.stderr b/tests/ui/suggestions/borrow-for-loop-head.stderr
index 0f179438a12..a8de9986c31 100644
--- a/tests/ui/suggestions/borrow-for-loop-head.stderr
+++ b/tests/ui/suggestions/borrow-for-loop-head.stderr
@@ -7,6 +7,12 @@ LL |     for i in &a {
    |              -- borrow of `a` occurs here
 LL |         for j in a {
    |                  ^ move out of `a` occurs here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     for i in &a {
+LL +     for i in a.clone() {
+   |
 
 error[E0382]: use of moved value: `a`
   --> $DIR/borrow-for-loop-head.rs:4:18
diff --git a/tests/ui/suggestions/for-i-in-vec.fixed b/tests/ui/suggestions/for-i-in-vec.fixed
index f266e80bcfa..17ae6fb5ab7 100644
--- a/tests/ui/suggestions/for-i-in-vec.fixed
+++ b/tests/ui/suggestions/for-i-in-vec.fixed
@@ -8,9 +8,9 @@ struct Foo {
 
 impl Foo {
     fn bar(&self) {
-        for _ in &self.v { //~ ERROR cannot move out of `self.v` which is behind a shared reference
+        for _ in &self.v.clone() { //~ ERROR cannot move out of `self.v` which is behind a shared reference
         }
-        for _ in &self.h { //~ ERROR cannot move out of `self.h` which is behind a shared reference
+        for _ in &self.h.clone() { //~ ERROR cannot move out of `self.h` which is behind a shared reference
         }
     }
 }
@@ -18,7 +18,7 @@ impl Foo {
 const LOADERS: &Vec<&'static u8> = &Vec::new();
 
 pub fn break_code() -> Option<&'static u8> {
-    for loader in &*LOADERS { //~ ERROR cannot move out of a shared reference
+    for loader in &LOADERS.clone() { //~ ERROR cannot move out of a shared reference
         return Some(loader);
     }
     None
diff --git a/tests/ui/suggestions/for-i-in-vec.stderr b/tests/ui/suggestions/for-i-in-vec.stderr
index c5b81e6b871..64eb4f8bd23 100644
--- a/tests/ui/suggestions/for-i-in-vec.stderr
+++ b/tests/ui/suggestions/for-i-in-vec.stderr
@@ -13,6 +13,10 @@ help: consider iterating over a slice of the `Vec<u32>`'s content to avoid movin
    |
 LL |         for _ in &self.v {
    |                  +
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         for _ in self.v.clone() {
+   |                        ++++++++
 
 error[E0507]: cannot move out of `self.h` which is behind a shared reference
   --> $DIR/for-i-in-vec.rs:13:18
@@ -27,6 +31,10 @@ help: consider iterating over a slice of the `HashMap<i32, i32>`'s content to av
    |
 LL |         for _ in &self.h {
    |                  +
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         for _ in self.h.clone() {
+   |                        ++++++++
 
 error[E0507]: cannot move out of a shared reference
   --> $DIR/for-i-in-vec.rs:21:19
@@ -43,6 +51,11 @@ help: consider iterating over a slice of the `Vec<&u8>`'s content to avoid movin
    |
 LL |     for loader in &*LOADERS {
    |                   +
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     for loader in *LOADERS {
+LL +     for loader in LOADERS.clone() {
+   |
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/suggestions/option-content-move.fixed b/tests/ui/suggestions/option-content-move.fixed
index fbed486cef7..4a5a9483c20 100644
--- a/tests/ui/suggestions/option-content-move.fixed
+++ b/tests/ui/suggestions/option-content-move.fixed
@@ -7,7 +7,7 @@ impl LipogramCorpora {
     pub fn validate_all(&mut self) -> Result<(), char> {
         for selection in &self.selections {
             if selection.1.is_some() {
-                if <Option<String> as Clone>::clone(&selection.1).unwrap().contains(selection.0) {
+                if <Option<String> as Clone>::clone(&selection.1.clone()).unwrap().contains(selection.0) {
                 //~^ ERROR cannot move out of `selection.1`
                     return Err(selection.0);
                 }
@@ -25,7 +25,7 @@ impl LipogramCorpora2 {
     pub fn validate_all(&mut self) -> Result<(), char> {
         for selection in &self.selections {
             if selection.1.is_ok() {
-                if <Result<String, String> as Clone>::clone(&selection.1).unwrap().contains(selection.0) {
+                if <Result<String, String> as Clone>::clone(&selection.1.clone()).unwrap().contains(selection.0) {
                 //~^ ERROR cannot move out of `selection.1`
                     return Err(selection.0);
                 }
diff --git a/tests/ui/suggestions/option-content-move.stderr b/tests/ui/suggestions/option-content-move.stderr
index e5de150275d..a382a04344a 100644
--- a/tests/ui/suggestions/option-content-move.stderr
+++ b/tests/ui/suggestions/option-content-move.stderr
@@ -13,6 +13,10 @@ help: you can `clone` the value and consume it, but this might not be your desir
    |
 LL |                 if <Option<String> as Clone>::clone(&selection.1).unwrap().contains(selection.0) {
    |                    ++++++++++++++++++++++++++++++++++           +
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |                 if selection.1.clone().unwrap().contains(selection.0) {
+   |                               ++++++++
 
 error[E0507]: cannot move out of `selection.1` which is behind a shared reference
   --> $DIR/option-content-move.rs:28:20
@@ -29,6 +33,10 @@ help: you can `clone` the value and consume it, but this might not be your desir
    |
 LL |                 if <Result<String, String> as Clone>::clone(&selection.1).unwrap().contains(selection.0) {
    |                    ++++++++++++++++++++++++++++++++++++++++++           +
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |                 if selection.1.clone().unwrap().contains(selection.0) {
+   |                               ++++++++
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/trivial-bounds/trivial-bounds-leak-copy.stderr b/tests/ui/trivial-bounds/trivial-bounds-leak-copy.stderr
index e48d48a7271..4efb883ac74 100644
--- a/tests/ui/trivial-bounds/trivial-bounds-leak-copy.stderr
+++ b/tests/ui/trivial-bounds/trivial-bounds-leak-copy.stderr
@@ -3,6 +3,12 @@ error[E0507]: cannot move out of `*t` which is behind a shared reference
    |
 LL |     *t
    |     ^^ move occurs because `*t` has type `String`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -     *t
+LL +     t.clone()
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/try-block/try-block-bad-lifetime.stderr b/tests/ui/try-block/try-block-bad-lifetime.stderr
index 28941cb0a9e..6f693295357 100644
--- a/tests/ui/try-block/try-block-bad-lifetime.stderr
+++ b/tests/ui/try-block/try-block-bad-lifetime.stderr
@@ -34,6 +34,11 @@ LL |             Err(k) ?;
 ...
 LL |         ::std::mem::drop(k);
    |                          ^ value used here after move
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |             Err(k.clone()) ?;
+   |                  ++++++++
 
 error[E0506]: cannot assign to `i` because it is borrowed
   --> $DIR/try-block-bad-lifetime.rs:32:9
diff --git a/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr b/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr
index bfa3061de08..cf4391311d0 100644
--- a/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr
+++ b/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr
@@ -7,6 +7,11 @@ LL |         let f = to_fn(|| drop(x));
    |                       --      ^ move occurs because `x` has type `Box<i32>`, which does not implement the `Copy` trait
    |                       |
    |                       captured by this `Fn` closure
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         let f = to_fn(|| drop(x.clone()));
+   |                                ++++++++
 
 error[E0507]: cannot move out of `x`, a captured variable in an `FnMut` closure
   --> $DIR/unboxed-closure-illegal-move.rs:19:35
@@ -17,6 +22,11 @@ LL |         let f = to_fn_mut(|| drop(x));
    |                           --      ^ move occurs because `x` has type `Box<i32>`, which does not implement the `Copy` trait
    |                           |
    |                           captured by this `FnMut` closure
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         let f = to_fn_mut(|| drop(x.clone()));
+   |                                    ++++++++
 
 error[E0507]: cannot move out of `x`, a captured variable in an `Fn` closure
   --> $DIR/unboxed-closure-illegal-move.rs:28:36
diff --git a/tests/ui/union/union-borrow-move-parent-sibling.stderr b/tests/ui/union/union-borrow-move-parent-sibling.stderr
index c9a440a66cc..782fa63280e 100644
--- a/tests/ui/union/union-borrow-move-parent-sibling.stderr
+++ b/tests/ui/union/union-borrow-move-parent-sibling.stderr
@@ -50,6 +50,11 @@ error[E0507]: cannot move out of dereference of `ManuallyDrop<((MockVec<u8>, Moc
 LL |     let a = (u.x.0).0;
    |             ^^^^^^^^^ move occurs because value has type `MockVec<u8>`, which does not implement the `Copy` trait
    |
+note: if `MockVec<u8>` implemented `Clone`, you could clone the value
+  --> $DIR/union-borrow-move-parent-sibling.rs:25:1
+   |
+LL | struct MockVec<T> {
+   | ^^^^^^^^^^^^^^^^^
 help: consider borrowing here
    |
 LL |     let a = &(u.x.0).0;
diff --git a/tests/ui/union/union-move.stderr b/tests/ui/union/union-move.stderr
index 47fb801a50e..5ebb2716e5a 100644
--- a/tests/ui/union/union-move.stderr
+++ b/tests/ui/union/union-move.stderr
@@ -16,6 +16,11 @@ LL | fn move_out<T>(x: T) {}
    |    --------       ^ this parameter takes ownership of the value
    |    |
    |    in this function
+note: if `U1` implemented `Clone`, you could clone the value
+  --> $DIR/union-move.rs:9:1
+   |
+LL | union U1 {
+   | ^^^^^^^^
 
 error[E0382]: use of moved value: `x`
   --> $DIR/union-move.rs:42:18
@@ -35,6 +40,11 @@ LL | fn move_out<T>(x: T) {}
    |    --------       ^ this parameter takes ownership of the value
    |    |
    |    in this function
+note: if `U1` implemented `Clone`, you could clone the value
+  --> $DIR/union-move.rs:9:1
+   |
+LL | union U1 {
+   | ^^^^^^^^
 
 error[E0509]: cannot move out of type `U2`, which implements the `Drop` trait
   --> $DIR/union-move.rs:49:18
@@ -44,6 +54,11 @@ LL |         move_out(x.f1_nocopy);
    |                  |
    |                  cannot move out of here
    |                  move occurs because `x.f1_nocopy` has type `ManuallyDrop<RefCell<i32>>`, which does not implement the `Copy` trait
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         move_out(x.f1_nocopy.clone());
+   |                             ++++++++
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/variance/variance-issue-20533.rs b/tests/ui/variance/variance-issue-20533.rs
index a2459f8730c..4c109608906 100644
--- a/tests/ui/variance/variance-issue-20533.rs
+++ b/tests/ui/variance/variance-issue-20533.rs
@@ -19,8 +19,15 @@ fn baz<'a, T>(_x: &'a T) -> Baked<'a> {
     Baked(PhantomData)
 }
 
+fn bat(x: &AffineU32) -> &u32 {
+    &x.0
+}
+
 struct AffineU32(u32);
 
+#[derive(Clone)]
+struct ClonableAffineU32(u32);
+
 fn main() {
     {
         let a = AffineU32(1);
@@ -40,4 +47,16 @@ fn main() {
         drop(a); //~ ERROR cannot move out of `a`
         drop(x);
     }
+    {
+        let a = AffineU32(1);
+        let x = bat(&a);
+        drop(a); //~ ERROR cannot move out of `a`
+        drop(x);
+    }
+    {
+        let a = ClonableAffineU32(1);
+        let x = foo(&a);
+        drop(a); //~ ERROR cannot move out of `a`
+        drop(x);
+    }
 }
diff --git a/tests/ui/variance/variance-issue-20533.stderr b/tests/ui/variance/variance-issue-20533.stderr
index 258f67db5ce..4515d313ec0 100644
--- a/tests/ui/variance/variance-issue-20533.stderr
+++ b/tests/ui/variance/variance-issue-20533.stderr
@@ -1,5 +1,5 @@
 error[E0505]: cannot move out of `a` because it is borrowed
-  --> $DIR/variance-issue-20533.rs:28:14
+  --> $DIR/variance-issue-20533.rs:35:14
    |
 LL |         let a = AffineU32(1);
    |             - binding `a` declared here
@@ -9,9 +9,15 @@ LL |         drop(a);
    |              ^ move out of `a` occurs here
 LL |         drop(x);
    |              - borrow later used here
+   |
+note: if `AffineU32` implemented `Clone`, you could clone the value
+  --> $DIR/variance-issue-20533.rs:26:1
+   |
+LL | struct AffineU32(u32);
+   | ^^^^^^^^^^^^^^^^
 
 error[E0505]: cannot move out of `a` because it is borrowed
-  --> $DIR/variance-issue-20533.rs:34:14
+  --> $DIR/variance-issue-20533.rs:41:14
    |
 LL |         let a = AffineU32(1);
    |             - binding `a` declared here
@@ -21,9 +27,15 @@ LL |         drop(a);
    |              ^ move out of `a` occurs here
 LL |         drop(x);
    |              - borrow later used here
+   |
+note: if `AffineU32` implemented `Clone`, you could clone the value
+  --> $DIR/variance-issue-20533.rs:26:1
+   |
+LL | struct AffineU32(u32);
+   | ^^^^^^^^^^^^^^^^
 
 error[E0505]: cannot move out of `a` because it is borrowed
-  --> $DIR/variance-issue-20533.rs:40:14
+  --> $DIR/variance-issue-20533.rs:47:14
    |
 LL |         let a = AffineU32(1);
    |             - binding `a` declared here
@@ -33,7 +45,48 @@ LL |         drop(a);
    |              ^ move out of `a` occurs here
 LL |         drop(x);
    |              - borrow later used here
+   |
+note: if `AffineU32` implemented `Clone`, you could clone the value
+  --> $DIR/variance-issue-20533.rs:26:1
+   |
+LL | struct AffineU32(u32);
+   | ^^^^^^^^^^^^^^^^
+
+error[E0505]: cannot move out of `a` because it is borrowed
+  --> $DIR/variance-issue-20533.rs:53:14
+   |
+LL |         let a = AffineU32(1);
+   |             - binding `a` declared here
+LL |         let x = bat(&a);
+   |                     -- borrow of `a` occurs here
+LL |         drop(a);
+   |              ^ move out of `a` occurs here
+LL |         drop(x);
+   |              - borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         let x = bat(&a).clone();
+   |                        ++++++++
+
+error[E0505]: cannot move out of `a` because it is borrowed
+  --> $DIR/variance-issue-20533.rs:59:14
+   |
+LL |         let a = ClonableAffineU32(1);
+   |             - binding `a` declared here
+LL |         let x = foo(&a);
+   |                     -- borrow of `a` occurs here
+LL |         drop(a);
+   |              ^ move out of `a` occurs here
+LL |         drop(x);
+   |              - borrow later used here
+   |
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL -         let x = foo(&a);
+LL +         let x = foo(a.clone());
+   |
 
-error: aborting due to 3 previous errors
+error: aborting due to 5 previous errors
 
 For more information about this error, try `rustc --explain E0505`.