diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 9e0aa57b2553f..14929ae41b938 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -130,6 +130,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let partial_str = if is_partial_move { "partial " } else { "" }; let partially_str = if is_partial_move { "partially " } else { "" }; + let mpi = self.move_data.moves[move_out_indices[0]].path; + let place = &self.move_data.move_paths[mpi].place; + let ty = place.ty(self.body, self.infcx.tcx).ty; + let mut err = self.cannot_act_on_moved_value( span, desired_action.as_noun(), @@ -186,6 +190,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } else { "" }; + let suggest_clone = self.suggest_using_clone(ty) && !move_spans.for_closure(); if location == move_out.source { is_loop_move = true; @@ -202,6 +207,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { move_msg, is_loop_move, maybe_reinitialized_locations.is_empty(), + suggest_clone, ); if let (UseSpans::PatUse(span), []) = @@ -237,8 +243,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } - let ty = used_place.ty(self.body, self.infcx.tcx).ty; - let needs_note = match ty.kind() { + let used_ty = used_place.ty(self.body, self.infcx.tcx).ty; + let needs_note = match used_ty.kind() { ty::Closure(id, _) => { let tables = self.infcx.tcx.typeck(id.expect_local()); let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(id.expect_local()); @@ -248,10 +254,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { _ => true, }; - let mpi = self.move_data.moves[move_out_indices[0]].path; - let place = &self.move_data.move_paths[mpi].place; - let ty = place.ty(self.body, self.infcx.tcx).ty; - // If we're in pattern, we do nothing in favor of the previous suggestion (#80913). // Same for if we're in a loop, see #101119. if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) { diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 7f26af67c71b2..3ca3936b9c2d5 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -301,6 +301,16 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } + /// We only suggest clone for `std::sync::Arc` and `std::rc::Rc` types. + fn suggest_using_clone(&self, ty: Ty<'tcx>) -> bool { + if let ty::Adt(adt, _) = ty.kind() && + (self.infcx.tcx.is_diagnostic_item(sym::Arc, adt.did()) || + self.infcx.tcx.is_diagnostic_item(sym::Rc, adt.did())) { + return true; + } + false + } + /// End-user visible description of the `field`nth field of `base` fn describe_field( &self, @@ -1029,6 +1039,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { move_msg: &str, is_loop_move: bool, maybe_reinitialized_locations_is_empty: bool, + suggest_clone: bool, ) { if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans { let place_name = self @@ -1166,6 +1177,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { move_span, format!("value {}moved{} here{}", partially_str, move_msg, loop_message), ); + if suggest_clone { + err.span_suggestion_verbose( + move_span.shrink_to_hi(), + "consider cloning here", + ".clone()", + Applicability::MaybeIncorrect, + ); + } } // If the move error occurs due to a loop, don't show // another message for the same span diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 5a47f45677ecb..c0b329dc70104 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -401,7 +401,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }; if let Some(use_spans) = use_spans { self.explain_captures( - &mut err, span, span, use_spans, move_place, "", "", "", false, true, + &mut err, span, span, use_spans, move_place, "", "", "", false, true, false, ); } err diff --git a/src/test/ui/borrowck/issue-104232-suggest-clone.rs b/src/test/ui/borrowck/issue-104232-suggest-clone.rs new file mode 100644 index 0000000000000..faf8aa864a9a6 --- /dev/null +++ b/src/test/ui/borrowck/issue-104232-suggest-clone.rs @@ -0,0 +1,36 @@ +use std::sync::Arc; +use std::rc::Rc; + +fn foo1(_: Arc) {} +fn bar1(_: Arc) {} +fn test_arc() { + let x = Arc::new(1); + foo1(x); + foo1(x); //~ ERROR use of moved value + bar1(x); //~ ERROR use of moved value +} + +fn foo2(_: Rc) {} +fn bar2(_: Rc) {} +fn test_rc() { + let x = Rc::new(1); + foo2(x); + foo2(x); //~ ERROR use of moved value + bar2(x); //~ ERROR use of moved value +} + +fn test_closure() { + let x = Arc::new(1); + for _ in 0..4 { + // Ideally we should suggest `let x = x.clone();` here. + std::thread::spawn(move || { //~ ERROR use of moved value + println!("{}", x); + }); + } +} + +fn main() { + test_rc(); + test_arc(); + test_closure(); +} diff --git a/src/test/ui/borrowck/issue-104232-suggest-clone.stderr b/src/test/ui/borrowck/issue-104232-suggest-clone.stderr new file mode 100644 index 0000000000000..7e31cc0f169ba --- /dev/null +++ b/src/test/ui/borrowck/issue-104232-suggest-clone.stderr @@ -0,0 +1,76 @@ +error[E0382]: use of moved value: `x` + --> $DIR/issue-104232-suggest-clone.rs:9:10 + | +LL | let x = Arc::new(1); + | - move occurs because `x` has type `Arc`, which does not implement the `Copy` trait +LL | foo1(x); + | - value moved here +LL | foo1(x); + | ^ value used here after move + | +help: consider cloning here + | +LL | foo1(x.clone()); + | ++++++++ + +error[E0382]: use of moved value: `x` + --> $DIR/issue-104232-suggest-clone.rs:10:10 + | +LL | let x = Arc::new(1); + | - move occurs because `x` has type `Arc`, which does not implement the `Copy` trait +LL | foo1(x); +LL | foo1(x); + | - value moved here +LL | bar1(x); + | ^ value used here after move + | +help: consider cloning here + | +LL | foo1(x.clone()); + | ++++++++ + +error[E0382]: use of moved value: `x` + --> $DIR/issue-104232-suggest-clone.rs:18:10 + | +LL | let x = Rc::new(1); + | - move occurs because `x` has type `Rc`, which does not implement the `Copy` trait +LL | foo2(x); + | - value moved here +LL | foo2(x); + | ^ value used here after move + | +help: consider cloning here + | +LL | foo2(x.clone()); + | ++++++++ + +error[E0382]: use of moved value: `x` + --> $DIR/issue-104232-suggest-clone.rs:19:10 + | +LL | let x = Rc::new(1); + | - move occurs because `x` has type `Rc`, which does not implement the `Copy` trait +LL | foo2(x); +LL | foo2(x); + | - value moved here +LL | bar2(x); + | ^ value used here after move + | +help: consider cloning here + | +LL | foo2(x.clone()); + | ++++++++ + +error[E0382]: use of moved value: `x` + --> $DIR/issue-104232-suggest-clone.rs:26:28 + | +LL | let x = Arc::new(1); + | - move occurs because `x` has type `Arc`, which does not implement the `Copy` trait +... +LL | std::thread::spawn(move || { + | ^^^^^^^ value moved into closure here, in previous iteration of loop +LL | println!("{}", x); + | - use occurs due to use in closure + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/moves/use_of_moved_value_clone_suggestions.stderr b/src/test/ui/moves/use_of_moved_value_clone_suggestions.stderr index c25981e6f8063..bc4a5dbc91c90 100644 --- a/src/test/ui/moves/use_of_moved_value_clone_suggestions.stderr +++ b/src/test/ui/moves/use_of_moved_value_clone_suggestions.stderr @@ -7,6 +7,11 @@ LL | (t, t) | - ^ value used here after move | | | value moved here + | +help: consider cloning here + | +LL | (t.clone(), t) + | ++++++++ error: aborting due to previous error