about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs155
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs102
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs40
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs87
-rw-r--r--compiler/rustc_trait_selection/src/infer.rs72
-rw-r--r--tests/ui/borrowck/accidentally-cloning-ref-borrow-error.rs38
-rw-r--r--tests/ui/borrowck/accidentally-cloning-ref-borrow-error.stderr30
-rw-r--r--tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.fixed7
-rw-r--r--tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.rs1
-rw-r--r--tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr6
-rw-r--r--tests/ui/borrowck/clone-span-on-try-operator.fixed2
-rw-r--r--tests/ui/borrowck/clone-span-on-try-operator.stderr4
-rw-r--r--tests/ui/borrowck/issue-83760.fixed47
-rw-r--r--tests/ui/borrowck/issue-83760.rs15
-rw-r--r--tests/ui/borrowck/issue-83760.stderr42
-rw-r--r--tests/ui/borrowck/issue-85765-closure.rs1
-rw-r--r--tests/ui/borrowck/issue-85765-closure.stderr13
-rw-r--r--tests/ui/borrowck/issue-85765.rs1
-rw-r--r--tests/ui/borrowck/issue-85765.stderr13
-rw-r--r--tests/ui/borrowck/issue-91206.rs1
-rw-r--r--tests/ui/borrowck/issue-91206.stderr7
-rw-r--r--tests/ui/borrowck/suggest-as-ref-on-mut-closure.stderr4
-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/imports/issue-55884-2.stderr10
-rw-r--r--tests/ui/imports/private-std-reexport-suggest-public.fixed9
-rw-r--r--tests/ui/imports/private-std-reexport-suggest-public.rs9
-rw-r--r--tests/ui/imports/private-std-reexport-suggest-public.stderr23
-rw-r--r--tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.fixed30
-rw-r--r--tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.rs29
-rw-r--r--tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.stderr25
-rw-r--r--tests/ui/moves/move-fn-self-receiver.stderr8
-rw-r--r--tests/ui/moves/needs-clone-through-deref.fixed18
-rw-r--r--tests/ui/moves/needs-clone-through-deref.rs18
-rw-r--r--tests/ui/moves/needs-clone-through-deref.stderr18
-rw-r--r--tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.fixed28
-rw-r--r--tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.rs27
-rw-r--r--tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr23
-rw-r--r--tests/ui/moves/suggest-clone.fixed2
-rw-r--r--tests/ui/moves/suggest-clone.stderr4
-rw-r--r--tests/ui/privacy/privacy2.stderr2
-rw-r--r--tests/ui/proc-macro/disappearing-resolution.stderr6
-rw-r--r--tests/ui/suggestions/as-ref-2.stderr9
-rw-r--r--tests/ui/suggestions/option-content-move.fixed38
-rw-r--r--tests/ui/suggestions/option-content-move.rs1
-rw-r--r--tests/ui/suggestions/option-content-move.stderr12
46 files changed, 940 insertions, 103 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 4deed98d002..1844e766a82 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -11,6 +11,7 @@ use rustc_hir::def::{CtorKind, Namespace};
 use rustc_hir::CoroutineKind;
 use rustc_index::IndexSlice;
 use rustc_infer::infer::BoundRegionConversionTime;
+use rustc_infer::traits::{FulfillmentErrorCode, SelectionError};
 use rustc_middle::mir::tcx::PlaceTy;
 use rustc_middle::mir::{
     AggregateKind, CallSource, ConstOperand, FakeReadCause, Local, LocalInfo, LocalKind, Location,
@@ -24,10 +25,9 @@ use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult};
 use rustc_span::def_id::LocalDefId;
 use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP};
 use rustc_target::abi::{FieldIdx, VariantIdx};
-use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
-use rustc_trait_selection::traits::{
-    type_known_to_meet_bound_modulo_regions, Obligation, ObligationCause,
-};
+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 super::borrow_set::BorrowData;
 use super::MirBorrowckCtxt;
@@ -1043,7 +1043,38 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 }
                 CallKind::Normal { self_arg, desugaring, method_did, method_args } => {
                     let self_arg = self_arg.unwrap();
+                    let mut has_sugg = false;
                     let tcx = self.infcx.tcx;
+                    // Avoid pointing to the same function in multiple different
+                    // error messages.
+                    if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) {
+                        self.explain_iterator_advancement_in_for_loop_if_applicable(
+                            err,
+                            span,
+                            &move_spans,
+                        );
+
+                        let func = tcx.def_path_str(method_did);
+                        err.subdiagnostic(CaptureReasonNote::FuncTakeSelf {
+                            func,
+                            place_name: place_name.clone(),
+                            span: self_arg.span,
+                        });
+                    }
+                    let parent_did = tcx.parent(method_did);
+                    let parent_self_ty =
+                        matches!(tcx.def_kind(parent_did), rustc_hir::def::DefKind::Impl { .. })
+                            .then_some(parent_did)
+                            .and_then(|did| match tcx.type_of(did).instantiate_identity().kind() {
+                                ty::Adt(def, ..) => Some(def.did()),
+                                _ => None,
+                            });
+                    let is_option_or_result = parent_self_ty.is_some_and(|def_id| {
+                        matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result))
+                    });
+                    if is_option_or_result && maybe_reinitialized_locations_is_empty {
+                        err.subdiagnostic(CaptureReasonLabel::BorrowContent { var_span });
+                    }
                     if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring {
                         let ty = moved_place.ty(self.body, tcx).ty;
                         let suggest = match tcx.get_diagnostic_item(sym::IntoIterator) {
@@ -1108,7 +1139,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         // Erase and shadow everything that could be passed to the new infcx.
                         let ty = moved_place.ty(self.body, tcx).ty;
 
-                        if let ty::Adt(def, args) = ty.kind()
+                        if let ty::Adt(def, args) = ty.peel_refs().kind()
                             && Some(def.did()) == tcx.lang_items().pin_type()
                             && let ty::Ref(_, _, hir::Mutability::Mut) = args.type_at(0).kind()
                             && let self_ty = self.infcx.instantiate_binder_with_fresh_vars(
@@ -1124,56 +1155,76 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                                     span: move_span.shrink_to_hi(),
                                 },
                             );
+                            has_sugg = true;
                         }
-                        if let Some(clone_trait) = tcx.lang_items().clone_trait()
-                            && let trait_ref = ty::TraitRef::new(tcx, clone_trait, [ty])
-                            && let o = Obligation::new(
-                                tcx,
-                                ObligationCause::dummy(),
-                                self.param_env,
-                                ty::Binder::dummy(trait_ref),
-                            )
-                            && self.infcx.predicate_must_hold_modulo_regions(&o)
-                        {
-                            err.span_suggestion_verbose(
-                                move_span.shrink_to_hi(),
-                                "you can `clone` the value and consume it, but this might not be \
-                                 your desired behavior",
-                                ".clone()".to_string(),
-                                Applicability::MaybeIncorrect,
-                            );
+                        if let Some(clone_trait) = tcx.lang_items().clone_trait() {
+                            let sugg = if moved_place
+                                .iter_projections()
+                                .any(|(_, elem)| matches!(elem, ProjectionElem::Deref))
+                            {
+                                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()),
+                                ]
+                            } else {
+                                vec![(move_span.shrink_to_hi(), ".clone()".to_string())]
+                            };
+                            if let Some(errors) =
+                                self.infcx.could_impl_trait(clone_trait, ty, self.param_env)
+                                && !has_sugg
+                            {
+                                let msg = match &errors[..] {
+                                    [] => "you can `clone` the value and consume it, but this \
+                                            might not be your desired behavior"
+                                        .to_string(),
+                                    [error] => {
+                                        format!(
+                                            "you could `clone` the value and consume it, if \
+                                                the `{}` trait bound could be satisfied",
+                                            error.obligation.predicate,
+                                        )
+                                    }
+                                    [errors @ .., last] => {
+                                        format!(
+                                            "you could `clone` the value and consume it, if \
+                                                the following trait bounds could be satisfied: {} \
+                                                and `{}`",
+                                            errors
+                                                .iter()
+                                                .map(|e| format!("`{}`", e.obligation.predicate))
+                                                .collect::<Vec<_>>()
+                                                .join(", "),
+                                            last.obligation.predicate,
+                                        )
+                                    }
+                                };
+                                err.multipart_suggestion_verbose(
+                                    msg,
+                                    sugg.clone(),
+                                    Applicability::MaybeIncorrect,
+                                );
+                                for error in errors {
+                                    if let FulfillmentErrorCode::CodeSelectionError(
+                                        SelectionError::Unimplemented,
+                                    ) = error.code
+                                        && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(
+                                            pred,
+                                        )) = error.obligation.predicate.kind().skip_binder()
+                                    {
+                                        self.infcx.err_ctxt().suggest_derive(
+                                            &error.obligation,
+                                            err,
+                                            error.obligation.predicate.kind().rebind(pred),
+                                        );
+                                    }
+                                }
+                            }
                         }
                     }
-                    // Avoid pointing to the same function in multiple different
-                    // error messages.
-                    if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) {
-                        self.explain_iterator_advancement_in_for_loop_if_applicable(
-                            err,
-                            span,
-                            &move_spans,
-                        );
-
-                        let func = tcx.def_path_str(method_did);
-                        err.subdiagnostic(CaptureReasonNote::FuncTakeSelf {
-                            func,
-                            place_name,
-                            span: self_arg.span,
-                        });
-                    }
-                    let parent_did = tcx.parent(method_did);
-                    let parent_self_ty =
-                        matches!(tcx.def_kind(parent_did), rustc_hir::def::DefKind::Impl { .. })
-                            .then_some(parent_did)
-                            .and_then(|did| match tcx.type_of(did).instantiate_identity().kind() {
-                                ty::Adt(def, ..) => Some(def.did()),
-                                _ => None,
-                            });
-                    let is_option_or_result = parent_self_ty.is_some_and(|def_id| {
-                        matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result))
-                    });
-                    if is_option_or_result && maybe_reinitialized_locations_is_empty {
-                        err.subdiagnostic(CaptureReasonLabel::BorrowContent { var_span });
-                    }
                 }
                 // Other desugarings takes &self, which cannot cause a move
                 _ => {}
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index d9ec2860962..8fe552708ed 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -3,8 +3,9 @@ use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed
 use rustc_hir as hir;
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::Node;
+use rustc_infer::traits;
 use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem};
-use rustc_middle::ty::{self, InstanceDef, Ty, TyCtxt};
+use rustc_middle::ty::{self, InstanceDef, ToPredicate, Ty, TyCtxt};
 use rustc_middle::{
     hir::place::PlaceBase,
     mir::{self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location},
@@ -12,6 +13,8 @@ use rustc_middle::{
 use rustc_span::symbol::{kw, Symbol};
 use rustc_span::{sym, BytePos, DesugaringKind, Span};
 use rustc_target::abi::FieldIdx;
+use rustc_trait_selection::infer::InferCtxtExt;
+use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt;
 
 use crate::diagnostics::BorrowedContentSource;
 use crate::util::FindAssignments;
@@ -1212,6 +1215,103 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 if let Some(hir_id) = hir_id
                     && let Some(hir::Node::Local(local)) = hir_map.find(hir_id)
                 {
+                    let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap());
+                    if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait()
+                        && let Some(expr) = local.init
+                        && let ty = tables.node_type_opt(expr.hir_id)
+                        && let Some(ty) = ty
+                        && let ty::Ref(..) = ty.kind()
+                    {
+                        match self
+                            .infcx
+                            .could_impl_trait(clone_trait, ty.peel_refs(), self.param_env)
+                            .as_deref()
+                        {
+                            Some([]) => {
+                                // The type implements Clone.
+                                err.span_help(
+                                    expr.span,
+                                    format!(
+                                        "you can `clone` the `{}` value and consume it, but this \
+                                         might not be your desired behavior",
+                                        ty.peel_refs(),
+                                    ),
+                                );
+                            }
+                            None => {
+                                if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) =
+                                    expr.kind
+                                    && segment.ident.name == sym::clone
+                                {
+                                    err.span_help(
+                                        span,
+                                        format!(
+                                            "`{}` doesn't implement `Clone`, so this call clones \
+                                             the reference `{ty}`",
+                                            ty.peel_refs(),
+                                        ),
+                                    );
+                                }
+                                // The type doesn't implement Clone.
+                                let trait_ref = ty::Binder::dummy(ty::TraitRef::new(
+                                    self.infcx.tcx,
+                                    clone_trait,
+                                    [ty.peel_refs()],
+                                ));
+                                let obligation = traits::Obligation::new(
+                                    self.infcx.tcx,
+                                    traits::ObligationCause::dummy(),
+                                    self.param_env,
+                                    trait_ref,
+                                );
+                                self.infcx.err_ctxt().suggest_derive(
+                                    &obligation,
+                                    err,
+                                    trait_ref.to_predicate(self.infcx.tcx),
+                                );
+                            }
+                            Some(errors) => {
+                                if let hir::ExprKind::MethodCall(segment, _rcvr, [], span) =
+                                    expr.kind
+                                    && segment.ident.name == sym::clone
+                                {
+                                    err.span_help(
+                                        span,
+                                        format!(
+                                            "`{}` doesn't implement `Clone` because its \
+                                             implementations trait bounds could not be met, so \
+                                             this call clones the reference `{ty}`",
+                                            ty.peel_refs(),
+                                        ),
+                                    );
+                                    err.note(format!(
+                                        "the following trait bounds weren't met: {}",
+                                        errors
+                                            .iter()
+                                            .map(|e| e.obligation.predicate.to_string())
+                                            .collect::<Vec<_>>()
+                                            .join("\n"),
+                                    ));
+                                }
+                                // The type doesn't implement Clone because of unmet obligations.
+                                for error in errors {
+                                    if let traits::FulfillmentErrorCode::CodeSelectionError(
+                                        traits::SelectionError::Unimplemented,
+                                    ) = error.code
+                                        && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(
+                                            pred,
+                                        )) = error.obligation.predicate.kind().skip_binder()
+                                    {
+                                        self.infcx.err_ctxt().suggest_derive(
+                                            &error.obligation,
+                                            err,
+                                            error.obligation.predicate.kind().rebind(pred),
+                                        );
+                                    }
+                                }
+                            }
+                        }
+                    }
                     let (changing, span, sugg) = match local.ty {
                         Some(ty) => ("changing", ty.span, message),
                         None => {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index a904b419a94..2c9942caab2 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -1619,6 +1619,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     None,
                 );
             } else {
+                if let Some(errors) =
+                    self.could_impl_trait(clone_trait_did, expected_ty, self.param_env)
+                {
+                    match &errors[..] {
+                        [] => {}
+                        [error] => {
+                            diag.help(format!(
+                                "`Clone` is not implemented because the trait bound `{}` is \
+                                 not satisfied",
+                                error.obligation.predicate,
+                            ));
+                        }
+                        [errors @ .., last] => {
+                            diag.help(format!(
+                                "`Clone` is not implemented because the following trait bounds \
+                                 could not be satisfied: {} and `{}`",
+                                errors
+                                    .iter()
+                                    .map(|e| format!("`{}`", e.obligation.predicate))
+                                    .collect::<Vec<_>>()
+                                    .join(", "),
+                                last.obligation.predicate,
+                            ));
+                        }
+                    }
+                    for error in errors {
+                        if let traits::FulfillmentErrorCode::CodeSelectionError(
+                            traits::SelectionError::Unimplemented,
+                        ) = error.code
+                            && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
+                                error.obligation.predicate.kind().skip_binder()
+                        {
+                            self.infcx.err_ctxt().suggest_derive(
+                                &error.obligation,
+                                diag,
+                                error.obligation.predicate.kind().rebind(pred),
+                            );
+                        }
+                    }
+                }
                 self.suggest_derive(diag, &[(trait_ref.to_predicate(self.tcx), None, None)]);
             }
         }
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 6c387a385e6..444110c7e7e 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -1697,6 +1697,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             struct_span_err!(self.tcx.sess, ident.span, E0603, "{} `{}` is private", descr, ident);
         err.span_label(ident.span, format!("private {descr}"));
 
+        let mut not_publicly_reexported = false;
         if let Some((this_res, outer_ident)) = outermost_res {
             let import_suggestions = self.lookup_import_candidates(
                 outer_ident,
@@ -1717,6 +1718,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             );
             // If we suggest importing a public re-export, don't point at the definition.
             if point_to_def && ident.span != outer_ident.span {
+                not_publicly_reexported = true;
                 err.span_label(
                     outer_ident.span,
                     format!("{} `{outer_ident}` is not publicly re-exported", this_res.descr()),
@@ -1749,10 +1751,51 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             }
         }
 
+        let mut sugg_paths = vec![];
+        if let Some(mut def_id) = res.opt_def_id() {
+            // We can't use `def_path_str` in resolve.
+            let mut path = vec![def_id];
+            while let Some(parent) = self.tcx.opt_parent(def_id) {
+                def_id = parent;
+                if !def_id.is_top_level_module() {
+                    path.push(def_id);
+                } else {
+                    break;
+                }
+            }
+            // We will only suggest importing directly if it is accessible through that path.
+            let path_names: Option<Vec<String>> = path
+                .iter()
+                .rev()
+                .map(|def_id| {
+                    self.tcx.opt_item_name(*def_id).map(|n| {
+                        if def_id.is_top_level_module() {
+                            "crate".to_string()
+                        } else {
+                            n.to_string()
+                        }
+                    })
+                })
+                .collect();
+            if let Some(def_id) = path.get(0)
+                && let Some(path) = path_names
+            {
+                if let Some(def_id) = def_id.as_local() {
+                    if self.effective_visibilities.is_directly_public(def_id) {
+                        sugg_paths.push((path, false));
+                    }
+                } else if self.is_accessible_from(self.tcx.visibility(def_id), parent_scope.module)
+                {
+                    sugg_paths.push((path, false));
+                }
+            }
+        }
+
         // Print the whole import chain to make it easier to see what happens.
         let first_binding = binding;
         let mut next_binding = Some(binding);
         let mut next_ident = ident;
+        let mut path = vec![];
         while let Some(binding) = next_binding {
             let name = next_ident;
             next_binding = match binding.kind {
@@ -1771,6 +1814,21 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 _ => None,
             };
 
+            match binding.kind {
+                NameBindingKind::Import { import, .. } => {
+                    for segment in import.module_path.iter().skip(1) {
+                        path.push(segment.ident.to_string());
+                    }
+                    sugg_paths.push((
+                        path.iter()
+                            .cloned()
+                            .chain(vec![ident.to_string()].into_iter())
+                            .collect::<Vec<_>>(),
+                        true, // re-export
+                    ));
+                }
+                NameBindingKind::Res(_) | NameBindingKind::Module(_) => {}
+            }
             let first = binding == first_binding;
             let msg = format!(
                 "{and_refers_to}the {item} `{name}`{which} is defined here{dots}",
@@ -1782,7 +1840,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             let def_span = self.tcx.sess.source_map().guess_head_span(binding.span);
             let mut note_span = MultiSpan::from_span(def_span);
             if !first && binding.vis.is_public() {
-                note_span.push_span_label(def_span, "consider importing it directly");
+                let desc = match binding.kind {
+                    NameBindingKind::Import { .. } => "re-export",
+                    _ => "directly",
+                };
+                note_span.push_span_label(def_span, format!("you could import this {desc}"));
             }
             // Final step in the import chain, point out if the ADT is `non_exhaustive`
             // which is probably why this privacy violation occurred.
@@ -1796,6 +1858,29 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             }
             err.span_note(note_span, msg);
         }
+        // We prioritize shorter paths, non-core imports and direct imports over the alternatives.
+        sugg_paths.sort_by_key(|(p, reexport)| (p.len(), p[0] == "core", *reexport));
+        for (sugg, reexport) in sugg_paths {
+            if not_publicly_reexported {
+                break;
+            }
+            if sugg.len() <= 1 {
+                // A single path segment suggestion is wrong. This happens on circular imports.
+                // `tests/ui/imports/issue-55884-2.rs`
+                continue;
+            }
+            let path = sugg.join("::");
+            err.span_suggestion_verbose(
+                dedup_span,
+                format!(
+                    "import `{ident}` {}",
+                    if reexport { "through the re-export" } else { "directly" }
+                ),
+                path,
+                Applicability::MachineApplicable,
+            );
+            break;
+        }
 
         err.emit();
     }
diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs
index 38153cccfdd..992bfd97e0e 100644
--- a/compiler/rustc_trait_selection/src/infer.rs
+++ b/compiler/rustc_trait_selection/src/infer.rs
@@ -1,8 +1,10 @@
+use crate::solve::FulfillmentCtxt;
 use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
 use crate::traits::{self, DefiningAnchor, ObligationCtxt};
 
 use rustc_hir::def_id::DefId;
 use rustc_hir::lang_items::LangItem;
+use rustc_infer::traits::{TraitEngine, TraitEngineExt};
 use rustc_middle::arena::ArenaAllocatable;
 use rustc_middle::infer::canonical::{Canonical, CanonicalQueryResponse, QueryResponse};
 use rustc_middle::traits::query::NoSolution;
@@ -35,6 +37,13 @@ pub trait InferCtxtExt<'tcx> {
         params: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
         param_env: ty::ParamEnv<'tcx>,
     ) -> traits::EvaluationResult;
+
+    fn could_impl_trait(
+        &self,
+        trait_def_id: DefId,
+        ty: Ty<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> Option<Vec<traits::FulfillmentError<'tcx>>>;
 }
 
 impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
@@ -76,6 +85,69 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
         };
         self.evaluate_obligation(&obligation).unwrap_or(traits::EvaluationResult::EvaluatedToErr)
     }
+
+    fn could_impl_trait(
+        &self,
+        trait_def_id: DefId,
+        ty: Ty<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> Option<Vec<traits::FulfillmentError<'tcx>>> {
+        self.probe(|_snapshot| {
+            if let ty::Adt(def, args) = ty.kind()
+                && let Some((impl_def_id, _)) = self
+                    .tcx
+                    .all_impls(trait_def_id)
+                    .filter_map(|impl_def_id| {
+                        self.tcx.impl_trait_ref(impl_def_id).map(|r| (impl_def_id, r))
+                    })
+                    .map(|(impl_def_id, imp)| (impl_def_id, imp.skip_binder()))
+                    .filter(|(_, imp)| match imp.self_ty().peel_refs().kind() {
+                        ty::Adt(i_def, _) if i_def.did() == def.did() => true,
+                        _ => false,
+                    })
+                    .next()
+            {
+                let mut fulfill_cx = FulfillmentCtxt::new(self);
+                // We get all obligations from the impl to talk about specific
+                // trait bounds.
+                let obligations = self
+                    .tcx
+                    .predicates_of(impl_def_id)
+                    .instantiate(self.tcx, args)
+                    .into_iter()
+                    .map(|(clause, span)| {
+                        traits::Obligation::new(
+                            self.tcx,
+                            traits::ObligationCause::dummy_with_span(span),
+                            param_env,
+                            clause,
+                        )
+                    })
+                    .collect::<Vec<_>>();
+                fulfill_cx.register_predicate_obligations(self, obligations);
+                let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty]);
+                let obligation = traits::Obligation::new(
+                    self.tcx,
+                    traits::ObligationCause::dummy(),
+                    param_env,
+                    trait_ref,
+                );
+                fulfill_cx.register_predicate_obligation(self, obligation);
+                let mut errors = fulfill_cx.select_all_or_error(self);
+                // We remove the last predicate failure, which corresponds to
+                // the top-level obligation, because most of the type we only
+                // care about the other ones, *except* when it is the only one.
+                // This seems to only be relevant for arbitrary self-types.
+                // Look at `tests/ui/moves/move-fn-self-receiver.rs`.
+                if errors.len() > 1 {
+                    errors.truncate(errors.len() - 1);
+                }
+                Some(errors)
+            } else {
+                None
+            }
+        })
+    }
 }
 
 pub trait InferCtxtBuilderExt<'tcx> {
diff --git a/tests/ui/borrowck/accidentally-cloning-ref-borrow-error.rs b/tests/ui/borrowck/accidentally-cloning-ref-borrow-error.rs
new file mode 100644
index 00000000000..2b25a5b2348
--- /dev/null
+++ b/tests/ui/borrowck/accidentally-cloning-ref-borrow-error.rs
@@ -0,0 +1,38 @@
+#[derive(Debug)]
+struct X<T>(T);
+
+impl<T: Clone> Clone for X<T> {
+    fn clone(&self) -> X<T> {
+        X(self.0.clone())
+    }
+}
+
+#[derive(Debug)]
+struct Y;
+
+#[derive(Debug)]
+struct Str {
+   x: Option<i32>,
+}
+
+fn foo(s: &mut Option<i32>) {
+    if s.is_none() {
+        *s = Some(0);
+    }
+    println!("{:?}", s);
+}
+
+fn bar<T: std::fmt::Debug>(s: &mut X<T>) {
+    println!("{:?}", s);
+}
+fn main() {
+    let s = Str { x: None };
+    let sr = &s;
+    let mut sm = sr.clone();
+    foo(&mut sm.x); //~ ERROR cannot borrow `sm.x` as mutable, as it is behind a `&` reference
+
+    let x = X(Y);
+    let xr = &x;
+    let mut xm = xr.clone();
+    bar(&mut xm); //~ ERROR cannot borrow data in a `&` reference as mutable
+}
diff --git a/tests/ui/borrowck/accidentally-cloning-ref-borrow-error.stderr b/tests/ui/borrowck/accidentally-cloning-ref-borrow-error.stderr
new file mode 100644
index 00000000000..7e51a4819ee
--- /dev/null
+++ b/tests/ui/borrowck/accidentally-cloning-ref-borrow-error.stderr
@@ -0,0 +1,30 @@
+error[E0596]: cannot borrow `sm.x` as mutable, as it is behind a `&` reference
+  --> $DIR/accidentally-cloning-ref-borrow-error.rs:32:9
+   |
+LL |     foo(&mut sm.x);
+   |         ^^^^^^^^^ `sm` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+   |
+help: `Str` doesn't implement `Clone`, so this call clones the reference `&Str`
+  --> $DIR/accidentally-cloning-ref-borrow-error.rs:31:21
+   |
+LL |     let mut sm = sr.clone();
+   |                     ^^^^^^^
+help: consider annotating `Str` with `#[derive(Clone)]`
+   |
+LL + #[derive(Clone)]
+LL | struct Str {
+   |
+help: consider specifying this binding's type
+   |
+LL |     let mut sm: &mut Str = sr.clone();
+   |               ++++++++++
+
+error[E0596]: cannot borrow data in a `&` reference as mutable
+  --> $DIR/accidentally-cloning-ref-borrow-error.rs:37:9
+   |
+LL |     bar(&mut xm);
+   |         ^^^^^^^ cannot borrow as mutable
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0596`.
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
new file mode 100644
index 00000000000..0b7551b97af
--- /dev/null
+++ b/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.fixed
@@ -0,0 +1,7 @@
+// run-rustfix
+use std::rc::Rc;
+
+pub fn main() {
+    let _x = <Vec<i32> as Clone>::clone(&Rc::new(vec![1, 2])).into_iter();
+    //~^ ERROR [E0507]
+}
diff --git a/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.rs b/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.rs
index 0b9e7102cd5..5cb8ceaca08 100644
--- a/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.rs
+++ b/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.rs
@@ -1,3 +1,4 @@
+// run-rustfix
 use std::rc::Rc;
 
 pub fn main() {
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 02794a12fad..076f0ce3440 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
@@ -1,5 +1,5 @@
 error[E0507]: cannot move out of an `Rc`
-  --> $DIR/borrowck-move-out-of-overloaded-auto-deref.rs:4:14
+  --> $DIR/borrowck-move-out-of-overloaded-auto-deref.rs:5:14
    |
 LL |     let _x = Rc::new(vec![1, 2]).into_iter();
    |              ^^^^^^^^^^^^^^^^^^^ ----------- value moved due to this method call
@@ -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 |     let _x = Rc::new(vec![1, 2]).clone().into_iter();
-   |                                 ++++++++
+LL |     let _x = <Vec<i32> as Clone>::clone(&Rc::new(vec![1, 2])).into_iter();
+   |              ++++++++++++++++++++++++++++                   +
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/clone-span-on-try-operator.fixed b/tests/ui/borrowck/clone-span-on-try-operator.fixed
index 52f66e43a93..4fad75b9a3d 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).clone().foo(); //~ ERROR cannot move out
+    <Foo as Clone>::clone(&(*foo)).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 5a55088d67a..adf84e49a9f 100644
--- a/tests/ui/borrowck/clone-span-on-try-operator.stderr
+++ b/tests/ui/borrowck/clone-span-on-try-operator.stderr
@@ -13,8 +13,8 @@ LL |     fn foo(self) {}
    |            ^^^^
 help: you can `clone` the value and consume it, but this might not be your desired behavior
    |
-LL |     (*foo).clone().foo();
-   |           ++++++++
+LL |     <Foo as Clone>::clone(&(*foo)).foo();
+   |     +++++++++++++++++++++++      +
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/issue-83760.fixed b/tests/ui/borrowck/issue-83760.fixed
new file mode 100644
index 00000000000..4544eeb6e19
--- /dev/null
+++ b/tests/ui/borrowck/issue-83760.fixed
@@ -0,0 +1,47 @@
+// run-rustfix
+#![allow(unused_variables, dead_code)]
+#[derive(Clone)]
+struct Struct;
+#[derive(Clone)]
+struct Struct2;
+// We use a second one here because otherwise when applying suggestions we'd end up with two
+// `#[derive(Clone)]` annotations.
+
+fn test1() {
+    let mut val = Some(Struct);
+    while let Some(ref foo) = val { //~ ERROR use of moved value
+        if true {
+            val = None;
+        } else {
+
+        }
+    }
+}
+
+fn test2() {
+    let mut foo = Some(Struct);
+    let _x = foo.clone().unwrap();
+    if true {
+        foo = Some(Struct);
+    } else {
+    }
+    let _y = foo; //~ ERROR use of moved value: `foo`
+}
+
+fn test3() {
+    let mut foo = Some(Struct2);
+    let _x = foo.clone().unwrap();
+    if true {
+        foo = Some(Struct2);
+    } else if true {
+        foo = Some(Struct2);
+    } else if true {
+        foo = Some(Struct2);
+    } else if true {
+        foo = Some(Struct2);
+    } else {
+    }
+    let _y = foo; //~ ERROR use of moved value: `foo`
+}
+
+fn main() {}
diff --git a/tests/ui/borrowck/issue-83760.rs b/tests/ui/borrowck/issue-83760.rs
index e25b4f72785..81bfdf0fcc7 100644
--- a/tests/ui/borrowck/issue-83760.rs
+++ b/tests/ui/borrowck/issue-83760.rs
@@ -1,4 +1,9 @@
+// run-rustfix
+#![allow(unused_variables, dead_code)]
 struct Struct;
+struct Struct2;
+// We use a second one here because otherwise when applying suggestions we'd end up with two
+// `#[derive(Clone)]` annotations.
 
 fn test1() {
     let mut val = Some(Struct);
@@ -22,16 +27,16 @@ fn test2() {
 }
 
 fn test3() {
-    let mut foo = Some(Struct);
+    let mut foo = Some(Struct2);
     let _x = foo.unwrap();
     if true {
-        foo = Some(Struct);
+        foo = Some(Struct2);
     } else if true {
-        foo = Some(Struct);
+        foo = Some(Struct2);
     } else if true {
-        foo = Some(Struct);
+        foo = Some(Struct2);
     } else if true {
-        foo = Some(Struct);
+        foo = Some(Struct2);
     } else {
     }
     let _y = foo; //~ ERROR use of moved value: `foo`
diff --git a/tests/ui/borrowck/issue-83760.stderr b/tests/ui/borrowck/issue-83760.stderr
index a585bff0c65..d120adbc03b 100644
--- a/tests/ui/borrowck/issue-83760.stderr
+++ b/tests/ui/borrowck/issue-83760.stderr
@@ -1,5 +1,5 @@
 error[E0382]: use of moved value
-  --> $DIR/issue-83760.rs:5:20
+  --> $DIR/issue-83760.rs:10:20
    |
 LL |     while let Some(foo) = val {
    |                    ^^^ value moved here, in previous iteration of loop
@@ -14,7 +14,7 @@ LL |     while let Some(ref foo) = val {
    |                    +++
 
 error[E0382]: use of moved value: `foo`
-  --> $DIR/issue-83760.rs:21:14
+  --> $DIR/issue-83760.rs:26:14
    |
 LL |     let mut foo = Some(Struct);
    |         ------- move occurs because `foo` has type `Option<Struct>`, which does not implement the `Copy` trait
@@ -29,12 +29,21 @@ LL |     let _y = foo;
    |
 note: `Option::<T>::unwrap` takes ownership of the receiver `self`, which moves `foo`
   --> $SRC_DIR/core/src/option.rs:LL:COL
+help: you could `clone` the value and consume it, if the `Struct: Clone` trait bound could be satisfied
+   |
+LL |     let _x = foo.clone().unwrap();
+   |                 ++++++++
+help: consider annotating `Struct` with `#[derive(Clone)]`
+   |
+LL + #[derive(Clone)]
+LL | struct Struct;
+   |
 
 error[E0382]: use of moved value: `foo`
-  --> $DIR/issue-83760.rs:37:14
+  --> $DIR/issue-83760.rs:42:14
    |
-LL |     let mut foo = Some(Struct);
-   |         ------- move occurs because `foo` has type `Option<Struct>`, which does not implement the `Copy` trait
+LL |     let mut foo = Some(Struct2);
+   |         ------- move occurs because `foo` has type `Option<Struct2>`, which does not implement the `Copy` trait
 LL |     let _x = foo.unwrap();
    |                  -------- `foo` moved due to this method call
 ...
@@ -42,18 +51,27 @@ LL |     let _y = foo;
    |              ^^^ value used here after move
    |
 note: these 3 reinitializations and 1 other might get skipped
-  --> $DIR/issue-83760.rs:30:9
+  --> $DIR/issue-83760.rs:35:9
    |
-LL |         foo = Some(Struct);
-   |         ^^^^^^^^^^^^^^^^^^
+LL |         foo = Some(Struct2);
+   |         ^^^^^^^^^^^^^^^^^^^
 LL |     } else if true {
-LL |         foo = Some(Struct);
-   |         ^^^^^^^^^^^^^^^^^^
+LL |         foo = Some(Struct2);
+   |         ^^^^^^^^^^^^^^^^^^^
 LL |     } else if true {
-LL |         foo = Some(Struct);
-   |         ^^^^^^^^^^^^^^^^^^
+LL |         foo = Some(Struct2);
+   |         ^^^^^^^^^^^^^^^^^^^
 note: `Option::<T>::unwrap` takes ownership of the receiver `self`, which moves `foo`
   --> $SRC_DIR/core/src/option.rs:LL:COL
+help: you could `clone` the value and consume it, if the `Struct2: Clone` trait bound could be satisfied
+   |
+LL |     let _x = foo.clone().unwrap();
+   |                 ++++++++
+help: consider annotating `Struct2` with `#[derive(Clone)]`
+   |
+LL + #[derive(Clone)]
+LL | struct Struct2;
+   |
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/borrowck/issue-85765-closure.rs b/tests/ui/borrowck/issue-85765-closure.rs
index f2d1dd0fbc3..edc9eeaffb5 100644
--- a/tests/ui/borrowck/issue-85765-closure.rs
+++ b/tests/ui/borrowck/issue-85765-closure.rs
@@ -3,6 +3,7 @@ fn main() {
         let mut test = Vec::new();
         let rofl: &Vec<Vec<i32>> = &mut test;
         //~^ HELP consider changing this binding's type
+        //~| HELP you can `clone` the `Vec<Vec<i32>>` value and consume it, but this might not be your desired behavior
         rofl.push(Vec::new());
         //~^ ERROR cannot borrow `*rofl` as mutable, as it is behind a `&` reference
         //~| NOTE `rofl` is a `&` reference, so the data it refers to cannot be borrowed as mutable
diff --git a/tests/ui/borrowck/issue-85765-closure.stderr b/tests/ui/borrowck/issue-85765-closure.stderr
index 936ddd67bcd..4a6a0e94bec 100644
--- a/tests/ui/borrowck/issue-85765-closure.stderr
+++ b/tests/ui/borrowck/issue-85765-closure.stderr
@@ -1,16 +1,21 @@
 error[E0596]: cannot borrow `*rofl` as mutable, as it is behind a `&` reference
-  --> $DIR/issue-85765-closure.rs:6:9
+  --> $DIR/issue-85765-closure.rs:7:9
    |
 LL |         rofl.push(Vec::new());
    |         ^^^^ `rofl` is a `&` reference, so the data it refers to cannot be borrowed as mutable
    |
+help: you can `clone` the `Vec<Vec<i32>>` value and consume it, but this might not be your desired behavior
+  --> $DIR/issue-85765-closure.rs:4:36
+   |
+LL |         let rofl: &Vec<Vec<i32>> = &mut test;
+   |                                    ^^^^^^^^^
 help: consider changing this binding's type
    |
 LL |         let rofl: &mut Vec<Vec<i32>> = &mut test;
    |                   ~~~~~~~~~~~~~~~~~~
 
 error[E0594]: cannot assign to `*r`, which is behind a `&` reference
-  --> $DIR/issue-85765-closure.rs:13:9
+  --> $DIR/issue-85765-closure.rs:14:9
    |
 LL |         *r = 0;
    |         ^^^^^^ `r` is a `&` reference, so the data it refers to cannot be written
@@ -21,7 +26,7 @@ LL |         let r = &mut mutvar;
    |                  +++
 
 error[E0594]: cannot assign to `*x`, which is behind a `&` reference
-  --> $DIR/issue-85765-closure.rs:20:9
+  --> $DIR/issue-85765-closure.rs:21:9
    |
 LL |         *x = 1;
    |         ^^^^^^ `x` is a `&` reference, so the data it refers to cannot be written
@@ -32,7 +37,7 @@ LL |         let x: &mut usize = &mut{0};
    |                ~~~~~~~~~~
 
 error[E0594]: cannot assign to `*y`, which is behind a `&` reference
-  --> $DIR/issue-85765-closure.rs:27:9
+  --> $DIR/issue-85765-closure.rs:28:9
    |
 LL |         *y = 1;
    |         ^^^^^^ `y` is a `&` reference, so the data it refers to cannot be written
diff --git a/tests/ui/borrowck/issue-85765.rs b/tests/ui/borrowck/issue-85765.rs
index 76e0b517354..ce5740bc0e7 100644
--- a/tests/ui/borrowck/issue-85765.rs
+++ b/tests/ui/borrowck/issue-85765.rs
@@ -2,6 +2,7 @@ fn main() {
     let mut test = Vec::new();
     let rofl: &Vec<Vec<i32>> = &mut test;
     //~^ HELP consider changing this binding's type
+    //~| HELP you can `clone` the `Vec<Vec<i32>>` value and consume it, but this might not be your desired behavior
     rofl.push(Vec::new());
     //~^ ERROR cannot borrow `*rofl` as mutable, as it is behind a `&` reference
     //~| NOTE `rofl` is a `&` reference, so the data it refers to cannot be borrowed as mutable
diff --git a/tests/ui/borrowck/issue-85765.stderr b/tests/ui/borrowck/issue-85765.stderr
index 57900bfb612..4889f774afa 100644
--- a/tests/ui/borrowck/issue-85765.stderr
+++ b/tests/ui/borrowck/issue-85765.stderr
@@ -1,16 +1,21 @@
 error[E0596]: cannot borrow `*rofl` as mutable, as it is behind a `&` reference
-  --> $DIR/issue-85765.rs:5:5
+  --> $DIR/issue-85765.rs:6:5
    |
 LL |     rofl.push(Vec::new());
    |     ^^^^ `rofl` is a `&` reference, so the data it refers to cannot be borrowed as mutable
    |
+help: you can `clone` the `Vec<Vec<i32>>` value and consume it, but this might not be your desired behavior
+  --> $DIR/issue-85765.rs:3:32
+   |
+LL |     let rofl: &Vec<Vec<i32>> = &mut test;
+   |                                ^^^^^^^^^
 help: consider changing this binding's type
    |
 LL |     let rofl: &mut Vec<Vec<i32>> = &mut test;
    |               ~~~~~~~~~~~~~~~~~~
 
 error[E0594]: cannot assign to `*r`, which is behind a `&` reference
-  --> $DIR/issue-85765.rs:12:5
+  --> $DIR/issue-85765.rs:13:5
    |
 LL |     *r = 0;
    |     ^^^^^^ `r` is a `&` reference, so the data it refers to cannot be written
@@ -21,7 +26,7 @@ LL |     let r = &mut mutvar;
    |              +++
 
 error[E0594]: cannot assign to `*x`, which is behind a `&` reference
-  --> $DIR/issue-85765.rs:19:5
+  --> $DIR/issue-85765.rs:20:5
    |
 LL |     *x = 1;
    |     ^^^^^^ `x` is a `&` reference, so the data it refers to cannot be written
@@ -32,7 +37,7 @@ LL |     let x: &mut usize = &mut{0};
    |            ~~~~~~~~~~
 
 error[E0594]: cannot assign to `*y`, which is behind a `&` reference
-  --> $DIR/issue-85765.rs:26:5
+  --> $DIR/issue-85765.rs:27:5
    |
 LL |     *y = 1;
    |     ^^^^^^ `y` is a `&` reference, so the data it refers to cannot be written
diff --git a/tests/ui/borrowck/issue-91206.rs b/tests/ui/borrowck/issue-91206.rs
index e062a253767..c60ac62fa34 100644
--- a/tests/ui/borrowck/issue-91206.rs
+++ b/tests/ui/borrowck/issue-91206.rs
@@ -10,6 +10,7 @@ fn main() {
     let client = TestClient;
     let inner = client.get_inner_ref();
     //~^ HELP consider specifying this binding's type
+    //~| HELP you can `clone` the `Vec<usize>` value and consume it, but this might not be your desired behavior
     inner.clear();
     //~^ ERROR cannot borrow `*inner` as mutable, as it is behind a `&` reference [E0596]
     //~| NOTE `inner` is a `&` reference, so the data it refers to cannot be borrowed as mutable
diff --git a/tests/ui/borrowck/issue-91206.stderr b/tests/ui/borrowck/issue-91206.stderr
index f96b0c7d9e1..e3dd65b6419 100644
--- a/tests/ui/borrowck/issue-91206.stderr
+++ b/tests/ui/borrowck/issue-91206.stderr
@@ -1,9 +1,14 @@
 error[E0596]: cannot borrow `*inner` as mutable, as it is behind a `&` reference
-  --> $DIR/issue-91206.rs:13:5
+  --> $DIR/issue-91206.rs:14:5
    |
 LL |     inner.clear();
    |     ^^^^^ `inner` is a `&` reference, so the data it refers to cannot be borrowed as mutable
    |
+help: you can `clone` the `Vec<usize>` value and consume it, but this might not be your desired behavior
+  --> $DIR/issue-91206.rs:11:17
+   |
+LL |     let inner = client.get_inner_ref();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^
 help: consider specifying this binding's type
    |
 LL |     let inner: &mut Vec<usize> = client.get_inner_ref();
diff --git a/tests/ui/borrowck/suggest-as-ref-on-mut-closure.stderr b/tests/ui/borrowck/suggest-as-ref-on-mut-closure.stderr
index bada08368fc..1e98006a9a7 100644
--- a/tests/ui/borrowck/suggest-as-ref-on-mut-closure.stderr
+++ b/tests/ui/borrowck/suggest-as-ref-on-mut-closure.stderr
@@ -9,6 +9,10 @@ LL |     cb.map(|cb| cb());
    |
 note: `Option::<T>::map` takes ownership of the receiver `self`, which moves `*cb`
   --> $SRC_DIR/core/src/option.rs:LL:COL
+help: you could `clone` the value and consume it, if the `&mut dyn FnMut(): Clone` trait bound could be satisfied
+   |
+LL |     <Option<&mut dyn FnMut()> as Clone>::clone(&cb).map(|cb| cb());
+   |     ++++++++++++++++++++++++++++++++++++++++++++  +
 
 error[E0596]: cannot borrow `*cb` as mutable, as it is behind a `&` reference
   --> $DIR/suggest-as-ref-on-mut-closure.rs:12:26
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 b0c5376105b..85acafd88f6 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(|| {
-        y.clone().into_iter();
+        <Vec<String> as Clone>::clone(&y).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 fd168b43ac5..a2ff70255f5 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
@@ -14,8 +14,8 @@ note: `into_iter` takes ownership of the receiver `self`, which moves `y`
   --> $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 |         y.clone().into_iter();
-   |          ++++++++
+LL |         <Vec<String> as Clone>::clone(&y).into_iter();
+   |         +++++++++++++++++++++++++++++++ +
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/imports/issue-55884-2.stderr b/tests/ui/imports/issue-55884-2.stderr
index a409265525b..8a9d5f2a6d8 100644
--- a/tests/ui/imports/issue-55884-2.stderr
+++ b/tests/ui/imports/issue-55884-2.stderr
@@ -13,17 +13,21 @@ note: ...and refers to the struct import `ParseOptions` which is defined here...
   --> $DIR/issue-55884-2.rs:13:9
    |
 LL | pub use parser::ParseOptions;
-   |         ^^^^^^^^^^^^^^^^^^^^ consider importing it directly
+   |         ^^^^^^^^^^^^^^^^^^^^ you could import this re-export
 note: ...and refers to the struct import `ParseOptions` which is defined here...
   --> $DIR/issue-55884-2.rs:6:13
    |
 LL |     pub use options::*;
-   |             ^^^^^^^^^^ consider importing it directly
+   |             ^^^^^^^^^^ you could import this re-export
 note: ...and refers to the struct `ParseOptions` which is defined here
   --> $DIR/issue-55884-2.rs:2:5
    |
 LL |     pub struct ParseOptions {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^ consider importing it directly
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ you could import this directly
+help: import `ParseOptions` through the re-export
+   |
+LL | pub use parser::ParseOptions;
+   |         ~~~~~~~~~~~~~~~~~~~~
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/imports/private-std-reexport-suggest-public.fixed b/tests/ui/imports/private-std-reexport-suggest-public.fixed
new file mode 100644
index 00000000000..b6fd22f5d3f
--- /dev/null
+++ b/tests/ui/imports/private-std-reexport-suggest-public.fixed
@@ -0,0 +1,9 @@
+// run-rustfix
+#![allow(unused_imports)]
+fn main() {
+    use std::mem; //~ ERROR module import `mem` is private
+}
+
+pub mod foo {
+    use std::mem;
+}
diff --git a/tests/ui/imports/private-std-reexport-suggest-public.rs b/tests/ui/imports/private-std-reexport-suggest-public.rs
new file mode 100644
index 00000000000..1247055af60
--- /dev/null
+++ b/tests/ui/imports/private-std-reexport-suggest-public.rs
@@ -0,0 +1,9 @@
+// run-rustfix
+#![allow(unused_imports)]
+fn main() {
+    use foo::mem; //~ ERROR module import `mem` is private
+}
+
+pub mod foo {
+    use std::mem;
+}
diff --git a/tests/ui/imports/private-std-reexport-suggest-public.stderr b/tests/ui/imports/private-std-reexport-suggest-public.stderr
new file mode 100644
index 00000000000..222553235aa
--- /dev/null
+++ b/tests/ui/imports/private-std-reexport-suggest-public.stderr
@@ -0,0 +1,23 @@
+error[E0603]: module import `mem` is private
+  --> $DIR/private-std-reexport-suggest-public.rs:4:14
+   |
+LL |     use foo::mem;
+   |              ^^^ private module import
+   |
+note: the module import `mem` is defined here...
+  --> $DIR/private-std-reexport-suggest-public.rs:8:9
+   |
+LL |     use std::mem;
+   |         ^^^^^^^^
+note: ...and refers to the module `mem` which is defined here
+  --> $SRC_DIR/std/src/lib.rs:LL:COL
+   |
+   = note: you could import this directly
+help: import `mem` through the re-export
+   |
+LL |     use std::mem;
+   |         ~~~~~~~~
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0603`.
diff --git a/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.fixed b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.fixed
new file mode 100644
index 00000000000..e88ca6079ec
--- /dev/null
+++ b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.fixed
@@ -0,0 +1,30 @@
+// run-rustfix
+#![allow(unused_variables, dead_code)]
+use std::collections::BTreeMap;
+use std::collections::HashSet;
+
+#[derive(Debug,Eq,PartialEq,Hash)]
+#[derive(Clone)]
+enum Day {
+    Mon,
+}
+
+struct Class {
+    days: BTreeMap<u32, HashSet<Day>>
+}
+
+impl Class {
+    fn do_stuff(&self) {
+        for (_, v) in &self.days {
+            let mut x: HashSet<Day> = v.clone(); //~ ERROR
+            let y: Vec<Day> = x.drain().collect();
+            println!("{:?}", x);
+        }
+    }
+}
+
+fn fail() {
+    let c = Class { days: BTreeMap::new() };
+    c.do_stuff();
+}
+fn main() {}
diff --git a/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.rs b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.rs
new file mode 100644
index 00000000000..ba277c4a9c4
--- /dev/null
+++ b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.rs
@@ -0,0 +1,29 @@
+// run-rustfix
+#![allow(unused_variables, dead_code)]
+use std::collections::BTreeMap;
+use std::collections::HashSet;
+
+#[derive(Debug,Eq,PartialEq,Hash)]
+enum Day {
+    Mon,
+}
+
+struct Class {
+    days: BTreeMap<u32, HashSet<Day>>
+}
+
+impl Class {
+    fn do_stuff(&self) {
+        for (_, v) in &self.days {
+            let mut x: HashSet<Day> = v.clone(); //~ ERROR
+            let y: Vec<Day> = x.drain().collect();
+            println!("{:?}", x);
+        }
+    }
+}
+
+fn fail() {
+    let c = Class { days: BTreeMap::new() };
+    c.do_stuff();
+}
+fn main() {}
diff --git a/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.stderr b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.stderr
new file mode 100644
index 00000000000..821661f1a56
--- /dev/null
+++ b/tests/ui/moves/assignment-of-clone-call-on-ref-due-to-missing-bound.stderr
@@ -0,0 +1,25 @@
+error[E0308]: mismatched types
+  --> $DIR/assignment-of-clone-call-on-ref-due-to-missing-bound.rs:18:39
+   |
+LL |             let mut x: HashSet<Day> = v.clone();
+   |                        ------------   ^^^^^^^^^ expected `HashSet<Day>`, found `&HashSet<Day>`
+   |                        |
+   |                        expected due to this
+   |
+   = note: expected struct `HashSet<Day>`
+           found reference `&HashSet<Day>`
+note: `HashSet<Day>` does not implement `Clone`, so `&HashSet<Day>` was cloned instead
+  --> $DIR/assignment-of-clone-call-on-ref-due-to-missing-bound.rs:18:39
+   |
+LL |             let mut x: HashSet<Day> = v.clone();
+   |                                       ^
+   = help: `Clone` is not implemented because the trait bound `Day: Clone` is not satisfied
+help: consider annotating `Day` with `#[derive(Clone)]`
+   |
+LL + #[derive(Clone)]
+LL | enum Day {
+   |
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/moves/move-fn-self-receiver.stderr b/tests/ui/moves/move-fn-self-receiver.stderr
index c91a8b5efac..462deacbe8d 100644
--- a/tests/ui/moves/move-fn-self-receiver.stderr
+++ b/tests/ui/moves/move-fn-self-receiver.stderr
@@ -55,6 +55,10 @@ note: `Foo::use_box_self` takes ownership of the receiver `self`, which moves `b
    |
 LL |     fn use_box_self(self: Box<Self>) {}
    |                     ^^^^
+help: you could `clone` the value and consume it, if the `Box<Foo>: Clone` trait bound could be satisfied
+   |
+LL |     boxed_foo.clone().use_box_self();
+   |              ++++++++
 
 error[E0382]: use of moved value: `pin_box_foo`
   --> $DIR/move-fn-self-receiver.rs:46:5
@@ -71,6 +75,10 @@ note: `Foo::use_pin_box_self` takes ownership of the receiver `self`, which move
    |
 LL |     fn use_pin_box_self(self: Pin<Box<Self>>) {}
    |                         ^^^^
+help: you could `clone` the value and consume it, if the `Box<Foo>: Clone` trait bound could be satisfied
+   |
+LL |     pin_box_foo.clone().use_pin_box_self();
+   |                ++++++++
 
 error[E0505]: cannot move out of `mut_foo` because it is borrowed
   --> $DIR/move-fn-self-receiver.rs:50:5
diff --git a/tests/ui/moves/needs-clone-through-deref.fixed b/tests/ui/moves/needs-clone-through-deref.fixed
new file mode 100644
index 00000000000..419718175e9
--- /dev/null
+++ b/tests/ui/moves/needs-clone-through-deref.fixed
@@ -0,0 +1,18 @@
+// run-rustfix
+#![allow(dead_code, noop_method_call)]
+use std::ops::Deref;
+struct S(Vec<usize>);
+impl Deref for S {
+    type Target = Vec<usize>;
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+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`
+    }
+}
+fn main() {}
diff --git a/tests/ui/moves/needs-clone-through-deref.rs b/tests/ui/moves/needs-clone-through-deref.rs
new file mode 100644
index 00000000000..8116008ffe3
--- /dev/null
+++ b/tests/ui/moves/needs-clone-through-deref.rs
@@ -0,0 +1,18 @@
+// run-rustfix
+#![allow(dead_code, noop_method_call)]
+use std::ops::Deref;
+struct S(Vec<usize>);
+impl Deref for S {
+    type Target = Vec<usize>;
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl S {
+    fn foo(&self) {
+        // `self.clone()` returns `&S`, not `Vec`
+        for _ in self.clone().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
new file mode 100644
index 00000000000..ff92f32e8d2
--- /dev/null
+++ b/tests/ui/moves/needs-clone-through-deref.stderr
@@ -0,0 +1,18 @@
+error[E0507]: cannot move out of dereference of `S`
+  --> $DIR/needs-clone-through-deref.rs:15:18
+   |
+LL |         for _ in self.clone().into_iter() {}
+   |                  ^^^^^^^^^^^^ ----------- value moved due to this method call
+   |                  |
+   |                  move occurs because value has type `Vec<usize>`, which does not implement the `Copy` trait
+   |
+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() {}
+   |                  ++++++++++++++++++++++++++++++            +
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0507`.
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
new file mode 100644
index 00000000000..a4e219e1c9b
--- /dev/null
+++ b/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.fixed
@@ -0,0 +1,28 @@
+// run-rustfix
+// Issue #109429
+use std::collections::hash_map::DefaultHasher;
+use std::collections::HashMap;
+use std::hash::BuildHasher;
+use std::hash::Hash;
+
+#[derive(Clone)]
+pub struct Hash128_1;
+
+impl BuildHasher for Hash128_1 {
+    type Hasher = DefaultHasher;
+    fn build_hasher(&self) -> DefaultHasher { DefaultHasher::default() }
+}
+
+#[allow(unused)]
+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
+}
+
+pub fn make_map() -> HashMap<String, i64, Hash128_1>
+{
+    HashMap::with_hasher(Hash128_1)
+}
+fn main() {}
diff --git a/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.rs b/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.rs
new file mode 100644
index 00000000000..efe035ebae0
--- /dev/null
+++ b/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.rs
@@ -0,0 +1,27 @@
+// run-rustfix
+// Issue #109429
+use std::collections::hash_map::DefaultHasher;
+use std::collections::HashMap;
+use std::hash::BuildHasher;
+use std::hash::Hash;
+
+pub struct Hash128_1;
+
+impl BuildHasher for Hash128_1 {
+    type Hasher = DefaultHasher;
+    fn build_hasher(&self) -> DefaultHasher { DefaultHasher::default() }
+}
+
+#[allow(unused)]
+pub fn hashmap_copy<T, U>(
+    map: &HashMap<T, U, Hash128_1>,
+) where T: Hash + Clone, U: Clone
+{
+    let mut copy: Vec<U> = map.clone().into_values().collect(); //~ ERROR
+}
+
+pub fn make_map() -> HashMap<String, i64, Hash128_1>
+{
+    HashMap::with_hasher(Hash128_1)
+}
+fn main() {}
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
new file mode 100644
index 00000000000..403daf8ff7c
--- /dev/null
+++ b/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr
@@ -0,0 +1,23 @@
+error[E0507]: cannot move out of a shared reference
+  --> $DIR/suggest-clone-when-some-obligation-is-unmet.rs:20:28
+   |
+LL |     let mut copy: Vec<U> = map.clone().into_values().collect();
+   |                            ^^^^^^^^^^^ ------------- value moved due to this method call
+   |                            |
+   |                            move occurs because value has type `HashMap<T, U, Hash128_1>`, which does not implement the `Copy` trait
+   |
+note: `HashMap::<K, V, S>::into_values` takes ownership of the receiver `self`, which moves value
+  --> $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();
+   |                            ++++++++++++++++++++++++++++++++++++++++++++           +
+help: consider annotating `Hash128_1` with `#[derive(Clone)]`
+   |
+LL + #[derive(Clone)]
+LL | pub struct Hash128_1;
+   |
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0507`.
diff --git a/tests/ui/moves/suggest-clone.fixed b/tests/ui/moves/suggest-clone.fixed
index 204bfdb10b0..0c4a94d77e4 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.clone().foo(); //~ ERROR cannot move out
+    <Foo as Clone>::clone(&foo).foo(); //~ ERROR cannot move out
 }
diff --git a/tests/ui/moves/suggest-clone.stderr b/tests/ui/moves/suggest-clone.stderr
index e0b68f249ee..25e89a58955 100644
--- a/tests/ui/moves/suggest-clone.stderr
+++ b/tests/ui/moves/suggest-clone.stderr
@@ -13,8 +13,8 @@ LL |     fn foo(self) {}
    |            ^^^^
 help: you can `clone` the value and consume it, but this might not be your desired behavior
    |
-LL |     foo.clone().foo();
-   |        ++++++++
+LL |     <Foo as Clone>::clone(&foo).foo();
+   |     +++++++++++++++++++++++   +
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/privacy/privacy2.stderr b/tests/ui/privacy/privacy2.stderr
index e7135d3fd8a..46bb9823dbf 100644
--- a/tests/ui/privacy/privacy2.stderr
+++ b/tests/ui/privacy/privacy2.stderr
@@ -19,7 +19,7 @@ note: ...and refers to the function `foo` which is defined here
   --> $DIR/privacy2.rs:16:1
    |
 LL | pub fn foo() {}
-   | ^^^^^^^^^^^^ consider importing it directly
+   | ^^^^^^^^^^^^ you could import this directly
 
 error: requires `sized` lang_item
 
diff --git a/tests/ui/proc-macro/disappearing-resolution.stderr b/tests/ui/proc-macro/disappearing-resolution.stderr
index 5b969549a11..e6d0868687e 100644
--- a/tests/ui/proc-macro/disappearing-resolution.stderr
+++ b/tests/ui/proc-macro/disappearing-resolution.stderr
@@ -19,7 +19,11 @@ note: ...and refers to the derive macro `Empty` which is defined here
   --> $DIR/auxiliary/test-macros.rs:25:1
    |
 LL | pub fn empty_derive(_: TokenStream) -> TokenStream {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ consider importing it directly
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ you could import this directly
+help: import `Empty` directly
+   |
+LL | use test_macros::Empty;
+   |     ~~~~~~~~~~~~~~~~~~
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/suggestions/as-ref-2.stderr b/tests/ui/suggestions/as-ref-2.stderr
index 5432fcb1a58..30928577537 100644
--- a/tests/ui/suggestions/as-ref-2.stderr
+++ b/tests/ui/suggestions/as-ref-2.stderr
@@ -12,6 +12,15 @@ LL |     let _y = foo;
    |
 note: `Option::<T>::map` takes ownership of the receiver `self`, which moves `foo`
   --> $SRC_DIR/core/src/option.rs:LL:COL
+help: you could `clone` the value and consume it, if the `Struct: Clone` trait bound could be satisfied
+   |
+LL |     let _x: Option<Struct> = foo.clone().map(|s| bar(&s));
+   |                                 ++++++++
+help: consider annotating `Struct` with `#[derive(Clone)]`
+   |
+LL + #[derive(Clone)]
+LL | struct Struct;
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/suggestions/option-content-move.fixed b/tests/ui/suggestions/option-content-move.fixed
new file mode 100644
index 00000000000..5e79cf71d82
--- /dev/null
+++ b/tests/ui/suggestions/option-content-move.fixed
@@ -0,0 +1,38 @@
+// run-rustfix
+pub struct LipogramCorpora {
+    selections: Vec<(char, Option<String>)>,
+}
+
+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) {
+                //~^ ERROR cannot move out of `selection.1`
+                    return Err(selection.0);
+                }
+            }
+        }
+        Ok(())
+    }
+}
+
+pub struct LipogramCorpora2 {
+    selections: Vec<(char, Result<String, String>)>,
+}
+
+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) {
+                //~^ ERROR cannot move out of `selection.1`
+                    return Err(selection.0);
+                }
+            }
+        }
+        Ok(())
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/suggestions/option-content-move.rs b/tests/ui/suggestions/option-content-move.rs
index 46c895b95f5..58efbe71f27 100644
--- a/tests/ui/suggestions/option-content-move.rs
+++ b/tests/ui/suggestions/option-content-move.rs
@@ -1,3 +1,4 @@
+// run-rustfix
 pub struct LipogramCorpora {
     selections: Vec<(char, Option<String>)>,
 }
diff --git a/tests/ui/suggestions/option-content-move.stderr b/tests/ui/suggestions/option-content-move.stderr
index 5060606d842..e5de150275d 100644
--- a/tests/ui/suggestions/option-content-move.stderr
+++ b/tests/ui/suggestions/option-content-move.stderr
@@ -1,5 +1,5 @@
 error[E0507]: cannot move out of `selection.1` which is behind a shared reference
-  --> $DIR/option-content-move.rs:9:20
+  --> $DIR/option-content-move.rs:10:20
    |
 LL |                 if selection.1.unwrap().contains(selection.0) {
    |                    ^^^^^^^^^^^ -------- `selection.1` moved due to this method call
@@ -11,11 +11,11 @@ note: `Option::<T>::unwrap` takes ownership of the receiver `self`, which moves
   --> $SRC_DIR/core/src/option.rs:LL:COL
 help: you can `clone` the value and consume it, but this might not be your desired behavior
    |
-LL |                 if selection.1.clone().unwrap().contains(selection.0) {
-   |                               ++++++++
+LL |                 if <Option<String> as Clone>::clone(&selection.1).unwrap().contains(selection.0) {
+   |                    ++++++++++++++++++++++++++++++++++           +
 
 error[E0507]: cannot move out of `selection.1` which is behind a shared reference
-  --> $DIR/option-content-move.rs:27:20
+  --> $DIR/option-content-move.rs:28:20
    |
 LL |                 if selection.1.unwrap().contains(selection.0) {
    |                    ^^^^^^^^^^^ -------- `selection.1` moved due to this method call
@@ -27,8 +27,8 @@ note: `Result::<T, E>::unwrap` takes ownership of the receiver `self`, which mov
   --> $SRC_DIR/core/src/result.rs:LL:COL
 help: you can `clone` the value and consume it, but this might not be your desired behavior
    |
-LL |                 if selection.1.clone().unwrap().contains(selection.0) {
-   |                               ++++++++
+LL |                 if <Result<String, String> as Clone>::clone(&selection.1).unwrap().contains(selection.0) {
+   |                    ++++++++++++++++++++++++++++++++++++++++++           +
 
 error: aborting due to 2 previous errors