diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 66a23eb4125d5..a0563599d3259 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -66,6 +66,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { location, desired_action, moved_place, used_place, span, mpi ); + let tcx = self.infcx.tcx; let use_spans = self.move_spans(moved_place, location).or_else(|| self.borrow_spans(span, location)); let span = use_spans.args_or_use(); @@ -174,12 +175,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let mut is_loop_move = false; let mut in_pattern = false; + let mut to_clone_spans = Vec::new(); + for move_site in &move_site_vec { let move_out = self.move_data.moves[(*move_site).moi]; let moved_place = &self.move_data.move_paths[move_out.path].place; let move_spans = self.move_spans(moved_place.as_ref(), move_out.source); let move_span = move_spans.args_or_use(); + to_clone_spans.push(move_spans.var_or_use_path_span()); let move_msg = if move_spans.for_closure() { " into closure" } else { "" }; @@ -243,11 +247,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } - let ty = used_place.ty(self.body, self.infcx.tcx).ty; + let ty = used_place.ty(self.body, tcx).ty; let needs_note = match 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()); + let tables = tcx.typeck(id.expect_local()); + let hir_id = tcx.hir().local_def_id_to_hir_id(id.expect_local()); tables.closure_kind_origins().get(hir_id).is_none() } @@ -256,7 +260,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { 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 ty = place.ty(self.body, tcx).ty; // If we're in pattern, we do nothing in favor of the previous suggestion (#80913). if is_loop_move & !in_pattern { @@ -285,61 +289,69 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { }; // Try to find predicates on *generic params* that would allow copying `ty` - let tcx = self.infcx.tcx; let generics = tcx.generics_of(self.mir_def_id()); if let Some(hir_generics) = tcx .typeck_root_def_id(self.mir_def_id().to_def_id()) .as_local() .and_then(|def_id| tcx.hir().get_generics(def_id)) { - let predicates: Result, _> = tcx.infer_ctxt().enter(|infcx| { - let mut fulfill_cx = - >::new(infcx.tcx); - - let copy_did = infcx.tcx.lang_items().copy_trait().unwrap(); - let cause = ObligationCause::new( - span, - self.mir_hir_id(), - rustc_infer::traits::ObligationCauseCode::MiscObligation, - ); - fulfill_cx.register_bound( - &infcx, - self.param_env, - // Erase any region vids from the type, which may not be resolved - infcx.tcx.erase_regions(ty), - copy_did, - cause, - ); - // Select all, including ambiguous predicates - let errors = fulfill_cx.select_all_or_error(&infcx); - - // Only emit suggestion if all required predicates are on generic - errors - .into_iter() - .map(|err| match err.obligation.predicate.kind().skip_binder() { - PredicateKind::Trait(predicate) => { - match predicate.self_ty().kind() { - ty::Param(param_ty) => Ok(( - generics.type_param(param_ty, tcx), - predicate.trait_ref.print_only_trait_path().to_string(), - )), - _ => Err(()), - } - } - _ => Err(()), - }) - .collect() - }); + let copy_did = tcx.lang_items().copy_trait().unwrap(); + let copy_predicates = + self.try_find_missing_generic_bounds(ty, copy_did, generics, span); + + let clone_did = tcx.lang_items().clone_trait().unwrap(); + let clone_predicates = + self.try_find_missing_generic_bounds(ty, clone_did, generics, span); + + match (copy_predicates, clone_predicates) { + // The type is already `Clone`, suggest cloning all values + (_, Ok(clone_predicates)) if clone_predicates.is_empty() => { + err.multipart_suggestion_verbose( + &format!("consider cloning {}", note_msg), + to_clone_spans + .into_iter() + .map(|move_span| { + (move_span.shrink_to_hi(), ".clone()".to_owned()) + }) + .collect(), + Applicability::MaybeIncorrect, + ); + } + // The type can *not* be `Copy`, but can be `Clone`, suggest adding bounds to make the type `Clone` and then cloning all values + (Err(_), Ok(clone_predicates)) => { + suggest_constraining_type_params( + tcx, + hir_generics, + &mut err, + clone_predicates.iter().map(|(param, constraint)| { + (param.name.as_str(), &**constraint, None) + }), + ); - if let Ok(predicates) = predicates { - suggest_constraining_type_params( - tcx, - hir_generics, - &mut err, - predicates.iter().map(|(param, constraint)| { - (param.name.as_str(), &**constraint, None) - }), - ); + err.multipart_suggestion_verbose( + &format!("...and cloning {}", note_msg), + to_clone_spans + .into_iter() + .map(|move_span| { + (move_span.shrink_to_hi(), ".clone()".to_owned()) + }) + .collect(), + Applicability::MaybeIncorrect, + ); + } + // The type can be `Copy`, suggest adding bound to make it `Copy` + (Ok(copy_predicates), _) => { + suggest_constraining_type_params( + tcx, + hir_generics, + &mut err, + copy_predicates.iter().map(|(param, constraint)| { + (param.name.as_str(), &**constraint, None) + }), + ); + } + // The type can never be `Clone` or `Copy`, there is nothing to suggest :( + (Err(_), Err(_)) => {} } } @@ -364,7 +376,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { )); // Check first whether the source is accessible (issue #87060) - if self.infcx.tcx.sess.source_map().span_to_snippet(deref_target).is_ok() { + if tcx.sess.source_map().span_to_snippet(deref_target).is_ok() { err.span_note(deref_target, "deref defined here"); } } @@ -1047,6 +1059,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { location, name, borrow, drop_span, borrow_spans ); + let tcx = self.infcx.tcx; let borrow_span = borrow_spans.var_or_use_path_span(); if let BorrowExplanation::MustBeValidFor { category, @@ -1083,19 +1096,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { format!( "...but `{}` will be dropped here, when the {} returns", name, - self.infcx - .tcx - .hir() + tcx.hir() .opt_name(fn_hir_id) .map(|name| format!("function `{}`", name)) .unwrap_or_else(|| { - match &self - .infcx - .tcx - .typeck(self.mir_def_id()) - .node_type(fn_hir_id) - .kind() - { + match &tcx.typeck(self.mir_def_id()).node_type(fn_hir_id).kind() { ty::Closure(..) => "enclosing closure", ty::Generator(..) => "enclosing generator", kind => bug!("expected closure or generator, found {:?}", kind), @@ -1117,7 +1122,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if let BorrowExplanation::MustBeValidFor { .. } = explanation { } else { explanation.add_explanation_to_diagnostic( - self.infcx.tcx, + tcx, &self.body, &self.local_names, &mut err, @@ -1135,7 +1140,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { borrow_spans.args_span_label(&mut err, format!("value captured here{}", within)); explanation.add_explanation_to_diagnostic( - self.infcx.tcx, + tcx, &self.body, &self.local_names, &mut err, @@ -2256,6 +2261,53 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } } + + /// Tries to find bounds on `generics` that satisfy `ty: required_trait_did` bound. + fn try_find_missing_generic_bounds( + &self, + ty: Ty<'tcx>, + required_trait_did: DefId, + generics: &'tcx ty::Generics, + obligation_cause_span: Span, + ) -> Result, ()> { + let tcx = self.infcx.tcx; + let predicates: Result, _> = tcx.infer_ctxt().enter(|infcx| { + let mut fulfill_cx = >::new(infcx.tcx); + + let cause = ObligationCause::new( + obligation_cause_span, + self.mir_hir_id(), + rustc_infer::traits::ObligationCauseCode::MiscObligation, + ); + fulfill_cx.register_bound( + &infcx, + self.param_env, + // Erase any region vids from the type, which may not be resolved + infcx.tcx.erase_regions(ty), + required_trait_did, + cause, + ); + // Select all, including ambiguous predicates + let errors = fulfill_cx.select_all_or_error(&infcx); + + // Only emit suggestion if all required predicates are on generic + errors + .into_iter() + .map(|err| match err.obligation.predicate.kind().skip_binder() { + PredicateKind::Trait(predicate) => match predicate.self_ty().kind() { + ty::Param(param_ty) => Ok(( + generics.type_param(param_ty, tcx), + predicate.trait_ref.print_only_trait_path().to_string(), + )), + _ => Err(()), + }, + _ => Err(()), + }) + .collect() + }); + + predicates + } } #[derive(Debug)] diff --git a/src/test/ui/moves/issue-46099-move-in-macro.stderr b/src/test/ui/moves/issue-46099-move-in-macro.stderr index baa87e3e9fdb2..7c7d4be31247d 100644 --- a/src/test/ui/moves/issue-46099-move-in-macro.stderr +++ b/src/test/ui/moves/issue-46099-move-in-macro.stderr @@ -5,6 +5,11 @@ LL | let b = Box::new(true); | - move occurs because `b` has type `Box`, which does not implement the `Copy` trait LL | test!({b}); | ^ value used here after move + | +help: consider cloning `b` + | +LL | test!({b.clone()}); + | ++++++++ error: aborting due to previous error diff --git a/src/test/ui/moves/move-fn-self-receiver.stderr b/src/test/ui/moves/move-fn-self-receiver.stderr index 3a686121a9283..74310778637cf 100644 --- a/src/test/ui/moves/move-fn-self-receiver.stderr +++ b/src/test/ui/moves/move-fn-self-receiver.stderr @@ -12,6 +12,10 @@ note: this function takes ownership of the receiver `self`, which moves `val.0` LL | fn into_iter(self) -> Self::IntoIter; | ^^^^ = note: move occurs because `val.0` has type `Vec`, which does not implement the `Copy` trait +help: consider cloning `val.0` + | +LL | val.0.clone().into_iter().next(); + | ++++++++ error[E0382]: use of moved value: `foo` --> $DIR/move-fn-self-receiver.rs:34:5 @@ -96,6 +100,10 @@ note: this function takes ownership of the receiver `self`, which moves `rc_foo` | LL | fn use_rc_self(self: Rc) {} | ^^^^ +help: consider cloning `rc_foo` + | +LL | rc_foo.clone().use_rc_self(); + | ++++++++ error[E0382]: use of moved value: `foo_add` --> $DIR/move-fn-self-receiver.rs:59:5 @@ -127,6 +135,10 @@ help: consider iterating over a slice of the `Vec`'s content to avoid movi | LL | for _val in &implicit_into_iter {} | + +help: consider cloning `implicit_into_iter` + | +LL | for _val in implicit_into_iter.clone() {} + | ++++++++ error[E0382]: use of moved value: `explicit_into_iter` --> $DIR/move-fn-self-receiver.rs:67:5 @@ -137,6 +149,11 @@ LL | for _val in explicit_into_iter.into_iter() {} | ----------- `explicit_into_iter` moved due to this method call LL | explicit_into_iter; | ^^^^^^^^^^^^^^^^^^ value used here after move + | +help: consider cloning `explicit_into_iter` + | +LL | for _val in explicit_into_iter.clone().into_iter() {} + | ++++++++ error[E0382]: use of moved value: `container` --> $DIR/move-fn-self-receiver.rs:71:5 diff --git a/src/test/ui/moves/move-guard-same-consts.stderr b/src/test/ui/moves/move-guard-same-consts.stderr index 2048fefefa31b..018434ef6effb 100644 --- a/src/test/ui/moves/move-guard-same-consts.stderr +++ b/src/test/ui/moves/move-guard-same-consts.stderr @@ -8,6 +8,11 @@ LL | (1, 2) if take(x) => (), | - value moved here LL | (1, 2) if take(x) => (), | ^ value used here after move + | +help: consider cloning `x` + | +LL | (1, 2) if take(x.clone()) => (), + | ++++++++ error: aborting due to previous error diff --git a/src/test/ui/moves/move-in-guard-1.stderr b/src/test/ui/moves/move-in-guard-1.stderr index 5e9aa66b90dae..07b5afda6893b 100644 --- a/src/test/ui/moves/move-in-guard-1.stderr +++ b/src/test/ui/moves/move-in-guard-1.stderr @@ -8,6 +8,11 @@ LL | (1, _) if take(x) => (), | - value moved here LL | (_, 2) if take(x) => (), | ^ value used here after move + | +help: consider cloning `x` + | +LL | (1, _) if take(x.clone()) => (), + | ++++++++ error: aborting due to previous error diff --git a/src/test/ui/moves/move-in-guard-2.stderr b/src/test/ui/moves/move-in-guard-2.stderr index 8d636c11b78c7..2aa1b09183d7c 100644 --- a/src/test/ui/moves/move-in-guard-2.stderr +++ b/src/test/ui/moves/move-in-guard-2.stderr @@ -6,6 +6,11 @@ LL | let x: Box<_> = Box::new(1); ... LL | (_, 2) if take(x) => (), | ^ value used here after move + | +help: consider cloning `x` + | +LL | (_, 2) if take(x.clone()) => (), + | ++++++++ error: aborting due to previous error diff --git a/src/test/ui/moves/move-out-of-tuple-field.stderr b/src/test/ui/moves/move-out-of-tuple-field.stderr index bb4eb76772abb..d67e7bc2b6eba 100644 --- a/src/test/ui/moves/move-out-of-tuple-field.stderr +++ b/src/test/ui/moves/move-out-of-tuple-field.stderr @@ -7,6 +7,10 @@ LL | let z = x.0; | ^^^ value used here after move | = note: move occurs because `x.0` has type `Box`, which does not implement the `Copy` trait +help: consider cloning `x.0` + | +LL | let y = x.0.clone(); + | ++++++++ error[E0382]: use of moved value: `x.0` --> $DIR/move-out-of-tuple-field.rs:12:13 @@ -17,6 +21,10 @@ LL | let z = x.0; | ^^^ value used here after move | = note: move occurs because `x.0` has type `Box`, which does not implement the `Copy` trait +help: consider cloning `x.0` + | +LL | let y = x.0.clone(); + | ++++++++ error: aborting due to 2 previous errors diff --git a/src/test/ui/moves/moves-based-on-type-access-to-field.stderr b/src/test/ui/moves/moves-based-on-type-access-to-field.stderr index 3cc8ca29144ca..c89dec29ad6af 100644 --- a/src/test/ui/moves/moves-based-on-type-access-to-field.stderr +++ b/src/test/ui/moves/moves-based-on-type-access-to-field.stderr @@ -13,6 +13,10 @@ note: this function takes ownership of the receiver `self`, which moves `x` | LL | fn into_iter(self) -> Self::IntoIter; | ^^^^ +help: consider cloning `x` + | +LL | consume(x.clone().into_iter().next().unwrap()); + | ++++++++ error: aborting due to previous error diff --git a/src/test/ui/moves/moves-based-on-type-capture-clause-bad.stderr b/src/test/ui/moves/moves-based-on-type-capture-clause-bad.stderr index ac921c18e07d5..65286d9fb4764 100644 --- a/src/test/ui/moves/moves-based-on-type-capture-clause-bad.stderr +++ b/src/test/ui/moves/moves-based-on-type-capture-clause-bad.stderr @@ -12,6 +12,10 @@ LL | println!("{}", x); | ^ value borrowed here after move | = note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider cloning `x` + | +LL | println!("{}", x.clone()); + | ++++++++ error: aborting due to previous error diff --git a/src/test/ui/moves/moves-based-on-type-distribute-copy-over-paren.stderr b/src/test/ui/moves/moves-based-on-type-distribute-copy-over-paren.stderr index ee7971691a4a1..8757c81f14619 100644 --- a/src/test/ui/moves/moves-based-on-type-distribute-copy-over-paren.stderr +++ b/src/test/ui/moves/moves-based-on-type-distribute-copy-over-paren.stderr @@ -9,6 +9,11 @@ LL | let _y = Foo { f:x }; LL | LL | touch(&x); | ^^ value borrowed here after move + | +help: consider cloning `x` + | +LL | let _y = Foo { f:x.clone() }; + | ++++++++ error[E0382]: borrow of moved value: `x` --> $DIR/moves-based-on-type-distribute-copy-over-paren.rs:21:11 @@ -21,6 +26,11 @@ LL | let _y = Foo { f:(((x))) }; LL | LL | touch(&x); | ^^ value borrowed here after move + | +help: consider cloning `x` + | +LL | let _y = Foo { f:(((x))).clone() }; + | ++++++++ error: aborting due to 2 previous errors diff --git a/src/test/ui/moves/moves-based-on-type-exprs.stderr b/src/test/ui/moves/moves-based-on-type-exprs.stderr index 9bcec36740d62..77d9196331ec9 100644 --- a/src/test/ui/moves/moves-based-on-type-exprs.stderr +++ b/src/test/ui/moves/moves-based-on-type-exprs.stderr @@ -7,6 +7,11 @@ LL | let _y = Foo { f:x }; | - value moved here LL | touch(&x); | ^^ value borrowed here after move + | +help: consider cloning `x` + | +LL | let _y = Foo { f:x.clone() }; + | ++++++++ error[E0382]: borrow of moved value: `x` --> $DIR/moves-based-on-type-exprs.rs:18:11 @@ -17,6 +22,11 @@ LL | let _y = (x, 3); | - value moved here LL | touch(&x); | ^^ value borrowed here after move + | +help: consider cloning `x` + | +LL | let _y = (x.clone(), 3); + | ++++++++ error[E0382]: borrow of moved value: `x` --> $DIR/moves-based-on-type-exprs.rs:35:11 @@ -29,6 +39,11 @@ LL | x ... LL | touch(&x); | ^^ value borrowed here after move + | +help: consider cloning `x` + | +LL | x.clone() + | ++++++++ error[E0382]: borrow of moved value: `y` --> $DIR/moves-based-on-type-exprs.rs:36:11 @@ -41,6 +56,11 @@ LL | y ... LL | touch(&y); | ^^ value borrowed here after move + | +help: consider cloning `y` + | +LL | y.clone() + | ++++++++ error[E0382]: borrow of moved value: `x` --> $DIR/moves-based-on-type-exprs.rs:46:11 @@ -53,6 +73,11 @@ LL | true => x, ... LL | touch(&x); | ^^ value borrowed here after move + | +help: consider cloning `x` + | +LL | true => x.clone(), + | ++++++++ error[E0382]: borrow of moved value: `y` --> $DIR/moves-based-on-type-exprs.rs:47:11 @@ -65,6 +90,11 @@ LL | false => y ... LL | touch(&y); | ^^ value borrowed here after move + | +help: consider cloning `y` + | +LL | false => y.clone() + | ++++++++ error[E0382]: borrow of moved value: `x` --> $DIR/moves-based-on-type-exprs.rs:58:11 @@ -77,6 +107,11 @@ LL | _ if guard(x) => 10, ... LL | touch(&x); | ^^ value borrowed here after move + | +help: consider cloning `x` + | +LL | _ if guard(x.clone()) => 10, + | ++++++++ error[E0382]: borrow of moved value: `x` --> $DIR/moves-based-on-type-exprs.rs:65:11 @@ -87,6 +122,11 @@ LL | let _y = [x]; | - value moved here LL | touch(&x); | ^^ value borrowed here after move + | +help: consider cloning `x` + | +LL | let _y = [x.clone()]; + | ++++++++ error[E0382]: borrow of moved value: `x` --> $DIR/moves-based-on-type-exprs.rs:71:11 @@ -97,6 +137,11 @@ LL | let _y = vec![x]; | - value moved here LL | touch(&x); | ^^ value borrowed here after move + | +help: consider cloning `x` + | +LL | let _y = vec![x.clone()]; + | ++++++++ error[E0382]: borrow of moved value: `x` --> $DIR/moves-based-on-type-exprs.rs:77:11 @@ -113,6 +158,10 @@ note: this function takes ownership of the receiver `self`, which moves `x` | LL | fn into_iter(self) -> Self::IntoIter; | ^^^^ +help: consider cloning `x` + | +LL | let _y = x.clone().into_iter().next().unwrap(); + | ++++++++ error[E0382]: borrow of moved value: `x` --> $DIR/moves-based-on-type-exprs.rs:83:11 @@ -129,6 +178,10 @@ note: this function takes ownership of the receiver `self`, which moves `x` | LL | fn into_iter(self) -> Self::IntoIter; | ^^^^ +help: consider cloning `x` + | +LL | let _y = [x.clone().into_iter().next().unwrap(); 1]; + | ++++++++ error: aborting due to 11 previous errors diff --git a/src/test/ui/moves/moves-based-on-type-match-bindings.stderr b/src/test/ui/moves/moves-based-on-type-match-bindings.stderr index ad1a2db8b52ba..2507e03c05684 100644 --- a/src/test/ui/moves/moves-based-on-type-match-bindings.stderr +++ b/src/test/ui/moves/moves-based-on-type-match-bindings.stderr @@ -8,6 +8,10 @@ LL | touch(&x); | ^^ value borrowed here after partial move | = note: partial move occurs because `x.f` has type `String`, which does not implement the `Copy` trait +help: consider cloning `x.f` + | +LL | Foo {f.clone()} => {} + | ++++++++ error: aborting due to previous error diff --git a/src/test/ui/moves/moves-based-on-type-tuple.stderr b/src/test/ui/moves/moves-based-on-type-tuple.stderr index eef8ce61fa9d8..c6666f542a6f0 100644 --- a/src/test/ui/moves/moves-based-on-type-tuple.stderr +++ b/src/test/ui/moves/moves-based-on-type-tuple.stderr @@ -8,6 +8,11 @@ LL | Box::new((x, x)) | - ^ value used here after move | | | value moved here + | +help: consider cloning `x` + | +LL | Box::new((x.clone(), x)) + | ++++++++ error: aborting due to previous error diff --git a/src/test/ui/moves/moves-sru-moved-field.stderr b/src/test/ui/moves/moves-sru-moved-field.stderr index cf7213637ce19..58b47e11ecfab 100644 --- a/src/test/ui/moves/moves-sru-moved-field.stderr +++ b/src/test/ui/moves/moves-sru-moved-field.stderr @@ -7,6 +7,10 @@ LL | let _c = Foo {noncopyable: h, ..f}; | ^^^^^^^^^^^^^^^^^^^^^^^^^ value used here after move | = note: move occurs because `f.moved` has type `Box`, which does not implement the `Copy` trait +help: consider cloning `f.moved` + | +LL | let _b = Foo {noncopyable: g, ..f}.clone(); + | ++++++++ error: aborting due to previous error diff --git a/src/test/ui/moves/use_of_moved_value_clone_suggestions.fixed b/src/test/ui/moves/use_of_moved_value_clone_suggestions.fixed new file mode 100644 index 0000000000000..dcced236beb44 --- /dev/null +++ b/src/test/ui/moves/use_of_moved_value_clone_suggestions.fixed @@ -0,0 +1,33 @@ +// run-rustfix +#![allow(dead_code)] + +// `Rc` is not ever `Copy`, we should not suggest adding `T: Copy` constraint. +// But should suggest adding `.clone()`. +fn move_rc(t: std::rc::Rc) { + [t.clone(), t]; //~ use of moved value: `t` +} + +// Even though `T` could be `Copy` it's already `Clone` +// so don't suggest adding `T: Copy` constraint, +// instead suggest adding `.clone()`. +fn move_clone_already(t: T) { + [t.clone(), t]; //~ use of moved value: `t` +} + +// Same as `Rc` +fn move_clone_only(t: (T, String)) { + [t.clone(), t]; //~ use of moved value: `t` +} + +// loop +fn move_in_a_loop(t: T) { + loop { + if true { + drop(t.clone()); //~ use of moved value: `t` + } else { + drop(t.clone()); + } + } +} + +fn main() {} diff --git a/src/test/ui/moves/use_of_moved_value_clone_suggestions.rs b/src/test/ui/moves/use_of_moved_value_clone_suggestions.rs index d5c8d4e6bdf2b..ff4f4303ce504 100644 --- a/src/test/ui/moves/use_of_moved_value_clone_suggestions.rs +++ b/src/test/ui/moves/use_of_moved_value_clone_suggestions.rs @@ -1,6 +1,33 @@ -// `Rc` is not ever `Copy`, we should not suggest adding `T: Copy` constraint -fn duplicate_rc(t: std::rc::Rc) -> (std::rc::Rc, std::rc::Rc) { - (t, t) //~ use of moved value: `t` +// run-rustfix +#![allow(dead_code)] + +// `Rc` is not ever `Copy`, we should not suggest adding `T: Copy` constraint. +// But should suggest adding `.clone()`. +fn move_rc(t: std::rc::Rc) { + [t, t]; //~ use of moved value: `t` +} + +// Even though `T` could be `Copy` it's already `Clone` +// so don't suggest adding `T: Copy` constraint, +// instead suggest adding `.clone()`. +fn move_clone_already(t: T) { + [t, t]; //~ use of moved value: `t` +} + +// Same as `Rc` +fn move_clone_only(t: (T, String)) { + [t, t]; //~ use of moved value: `t` +} + +// loop +fn move_in_a_loop(t: T) { + loop { + if true { + drop(t); //~ use of moved value: `t` + } else { + drop(t); + } + } } fn main() {} 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..49ef8e722c3d1 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 @@ -1,13 +1,71 @@ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_clone_suggestions.rs:3:9 + --> $DIR/use_of_moved_value_clone_suggestions.rs:7:9 | -LL | fn duplicate_rc(t: std::rc::Rc) -> (std::rc::Rc, std::rc::Rc) { - | - move occurs because `t` has type `Rc`, which does not implement the `Copy` trait -LL | (t, t) +LL | fn move_rc(t: std::rc::Rc) { + | - move occurs because `t` has type `Rc`, which does not implement the `Copy` trait +LL | [t, t]; | - ^ value used here after move | | | value moved here + | +help: consider cloning `t` + | +LL | [t.clone(), t]; + | ++++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_clone_suggestions.rs:14:9 + | +LL | fn move_clone_already(t: T) { + | - move occurs because `t` has type `T`, which does not implement the `Copy` trait +LL | [t, t]; + | - ^ value used here after move + | | + | value moved here + | +help: consider cloning `t` + | +LL | [t.clone(), t]; + | ++++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_clone_suggestions.rs:19:9 + | +LL | fn move_clone_only(t: (T, String)) { + | - move occurs because `t` has type `(T, String)`, which does not implement the `Copy` trait +LL | [t, t]; + | - ^ value used here after move + | | + | value moved here + | +help: consider restricting type parameter `T` + | +LL | fn move_clone_only(t: (T, String)) { + | +++++++ +help: ...and cloning `t` + | +LL | [t.clone(), t]; + | ++++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_clone_suggestions.rs:26:18 + | +LL | fn move_in_a_loop(t: T) { + | - move occurs because `t` has type `T`, which does not implement the `Copy` trait +... +LL | drop(t); + | ^ value moved here, in previous iteration of loop +LL | } else { +LL | drop(t); + | - value moved here, in previous iteration of loop + | +help: consider cloning `t` + | +LL ~ drop(t.clone()); +LL | } else { +LL ~ drop(t.clone()); + | -error: aborting due to previous error +error: aborting due to 4 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_bad.rs b/src/test/ui/moves/use_of_moved_value_clone_suggestions_bad.rs new file mode 100644 index 0000000000000..852e7da393452 --- /dev/null +++ b/src/test/ui/moves/use_of_moved_value_clone_suggestions_bad.rs @@ -0,0 +1,27 @@ +// Bad `.clone()` suggestions + +struct S { + t: A, +} + +fn struct_field_shortcut_move(t: T) { + S { t }; + t; //~ use of moved value: `t` +} + +fn closure_clone_will_not_help(t: T) { + (move || { + t; + })(); + t; //~ use of moved value: `t` +} + +#[derive(Clone)] +struct CloneOnly; + +fn update_syntax(s: S) { + S { ..s }; + S { ..s }; //~ use of moved value: `s.t` +} + +fn main() {} diff --git a/src/test/ui/moves/use_of_moved_value_clone_suggestions_bad.stderr b/src/test/ui/moves/use_of_moved_value_clone_suggestions_bad.stderr new file mode 100644 index 0000000000000..8d5e15faf4b6b --- /dev/null +++ b/src/test/ui/moves/use_of_moved_value_clone_suggestions_bad.stderr @@ -0,0 +1,50 @@ +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_clone_suggestions_bad.rs:9:5 + | +LL | fn struct_field_shortcut_move(t: T) { + | - move occurs because `t` has type `T`, which does not implement the `Copy` trait +LL | S { t }; + | - value moved here +LL | t; + | ^ value used here after move + | +help: consider cloning `t` + | +LL | S { t.clone() }; + | ++++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_clone_suggestions_bad.rs:16:5 + | +LL | fn closure_clone_will_not_help(t: T) { + | - move occurs because `t` has type `T`, which does not implement the `Copy` trait +LL | (move || { + | ------- value moved into closure here +LL | t; + | - variable moved due to use in closure +LL | })(); +LL | t; + | ^ value used here after move + | +help: consider cloning `t` + | +LL | t.clone(); + | ++++++++ + +error[E0382]: use of moved value: `s.t` + --> $DIR/use_of_moved_value_clone_suggestions_bad.rs:24:5 + | +LL | S { ..s }; + | --------- value moved here +LL | S { ..s }; + | ^^^^^^^^^ value used here after move + | + = note: move occurs because `s.t` has type `T`, which does not implement the `Copy` trait +help: consider cloning `s.t` + | +LL | S { ..s }.clone(); + | ++++++++ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/moves/use_of_moved_value_no_suggestions.rs b/src/test/ui/moves/use_of_moved_value_no_suggestions.rs new file mode 100644 index 0000000000000..ab10c6a0e0012 --- /dev/null +++ b/src/test/ui/moves/use_of_moved_value_no_suggestions.rs @@ -0,0 +1,9 @@ +// No suggestions? :( + +// In the future, we may want to suggest deriving `Clone, Copy` for `No` (and then adding `T: Copy`) +struct No; +fn move_non_clone_non_copy(t: (T, No)) { + [t, t]; //~ use of moved value: `t` +} + +fn main() {} diff --git a/src/test/ui/moves/use_of_moved_value_no_suggestions.stderr b/src/test/ui/moves/use_of_moved_value_no_suggestions.stderr new file mode 100644 index 0000000000000..ad8d4c57b3ec7 --- /dev/null +++ b/src/test/ui/moves/use_of_moved_value_no_suggestions.stderr @@ -0,0 +1,13 @@ +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_no_suggestions.rs:6:9 + | +LL | fn move_non_clone_non_copy(t: (T, No)) { + | - move occurs because `t` has type `(T, No)`, which does not implement the `Copy` trait +LL | [t, t]; + | - ^ value used here after move + | | + | value moved here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/nll/move-subpaths-moves-root.stderr b/src/test/ui/nll/move-subpaths-moves-root.stderr index ae9287f92266f..2d3cd908481e7 100644 --- a/src/test/ui/nll/move-subpaths-moves-root.stderr +++ b/src/test/ui/nll/move-subpaths-moves-root.stderr @@ -7,6 +7,10 @@ LL | drop(x); | ^ value used here after partial move | = note: partial move occurs because `x.0` has type `Vec`, which does not implement the `Copy` trait +help: consider cloning `x.0` + | +LL | drop(x.0.clone()); + | ++++++++ error: aborting due to previous error