From 5eb573a343df5d5c955d0bcf671505d42310bf01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 12 Mar 2024 20:01:51 +0000 Subject: [PATCH 01/19] Account for `.clone()` when suggesting `::clone` --- .../src/diagnostics/conflict_errors.rs | 23 +++++++++++++++++++ .../rustc_borrowck/src/diagnostics/mod.rs | 12 ++++++++-- .../ui/moves/needs-clone-through-deref.fixed | 2 +- .../ui/moves/needs-clone-through-deref.stderr | 4 ++-- ...-clone-when-some-obligation-is-unmet.fixed | 2 +- ...clone-when-some-obligation-is-unmet.stderr | 4 ++-- 6 files changed, 39 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 62e16d445c63f..cf550a2e5d862 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -987,6 +987,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { can_suggest_clone } + pub(crate) fn clone_on_reference(&self, expr: &hir::Expr<'_>) -> Option { + let typeck_results = self.infcx.tcx.typeck(self.mir_def_id()); + if let hir::ExprKind::MethodCall(segment, rcvr, args, span) = expr.kind + && let Some(expr_ty) = typeck_results.node_type_opt(expr.hir_id) + && let Some(rcvr_ty) = typeck_results.node_type_opt(rcvr.hir_id) + && rcvr_ty == expr_ty + && segment.ident.name == sym::clone + && args.is_empty() + { + Some(span) + } else { + None + } + } + fn suggest_cloning(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, expr: &hir::Expr<'_>, span: Span) { let tcx = self.infcx.tcx; // Try to find predicates on *generic params* that would allow copying `ty` @@ -1654,6 +1669,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } + pub(crate) fn find_expr(&self, span: Span) -> Option<&hir::Expr<'_>> { + let tcx = self.infcx.tcx; + let body_id = tcx.hir_node(self.mir_hir_id()).body_id()?; + let mut expr_finder = FindExprBySpan::new(span); + expr_finder.visit_expr(tcx.hir().body(body_id).value); + expr_finder.result + } + fn suggest_slice_method_if_applicable( &self, err: &mut Diag<'_>, diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 0106e285604d6..5425161ed4887 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -1212,13 +1212,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { .iter_projections() .any(|(_, elem)| matches!(elem, ProjectionElem::Deref)) { + let (start, end) = if let Some(expr) = self.find_expr(move_span) + && let Some(_) = self.clone_on_reference(expr) + && let hir::ExprKind::MethodCall(_, rcvr, _, _) = expr.kind + { + (move_span.shrink_to_lo(), move_span.with_lo(rcvr.span.hi())) + } else { + (move_span.shrink_to_lo(), move_span.shrink_to_hi()) + }; vec![ // We use the fully-qualified path because `.clone()` can // sometimes choose `<&T as Clone>` instead of `` // when going through auto-deref, so this ensures that doesn't // happen, causing suggestions for `.clone().clone()`. - (move_span.shrink_to_lo(), format!("<{ty} as Clone>::clone(&")), - (move_span.shrink_to_hi(), ")".to_string()), + (start, format!("<{ty} as Clone>::clone(&")), + (end, ")".to_string()), ] } else { vec![(move_span.shrink_to_hi(), ".clone()".to_string())] diff --git a/tests/ui/moves/needs-clone-through-deref.fixed b/tests/ui/moves/needs-clone-through-deref.fixed index 43ea15d1b63fc..8b201c4720dd6 100644 --- a/tests/ui/moves/needs-clone-through-deref.fixed +++ b/tests/ui/moves/needs-clone-through-deref.fixed @@ -12,7 +12,7 @@ impl Deref for S { impl S { fn foo(&self) { // `self.clone()` returns `&S`, not `Vec` - for _ in as Clone>::clone(&self.clone()).into_iter() {} //~ ERROR cannot move out of dereference of `S` + for _ in as Clone>::clone(&self).into_iter() {} //~ ERROR cannot move out of dereference of `S` } } fn main() {} diff --git a/tests/ui/moves/needs-clone-through-deref.stderr b/tests/ui/moves/needs-clone-through-deref.stderr index ff92f32e8d2b3..1f9aefeb4dd7a 100644 --- a/tests/ui/moves/needs-clone-through-deref.stderr +++ b/tests/ui/moves/needs-clone-through-deref.stderr @@ -10,8 +10,8 @@ note: `into_iter` takes ownership of the receiver `self`, which moves value --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: you can `clone` the value and consume it, but this might not be your desired behavior | -LL | for _ in as Clone>::clone(&self.clone()).into_iter() {} - | ++++++++++++++++++++++++++++++ + +LL | for _ in as Clone>::clone(&self).into_iter() {} + | ++++++++++++++++++++++++++++++ ~ error: aborting due to 1 previous error diff --git a/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.fixed b/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.fixed index b3eae0b22b64b..cdc86f61d24ba 100644 --- a/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.fixed +++ b/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.fixed @@ -18,7 +18,7 @@ pub fn hashmap_copy( map: &HashMap, ) where T: Hash + Clone, U: Clone { - let mut copy: Vec = as Clone>::clone(&map.clone()).into_values().collect(); //~ ERROR + let mut copy: Vec = as Clone>::clone(&map).into_values().collect(); //~ ERROR } pub fn make_map() -> HashMap diff --git a/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr b/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr index 403daf8ff7c7f..755bbc5c21bdb 100644 --- a/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr +++ b/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr @@ -10,8 +10,8 @@ note: `HashMap::::into_values` takes ownership of the receiver `self`, --> $SRC_DIR/std/src/collections/hash/map.rs:LL:COL help: you could `clone` the value and consume it, if the `Hash128_1: Clone` trait bound could be satisfied | -LL | let mut copy: Vec = as Clone>::clone(&map.clone()).into_values().collect(); - | ++++++++++++++++++++++++++++++++++++++++++++ + +LL | let mut copy: Vec = as Clone>::clone(&map).into_values().collect(); + | ++++++++++++++++++++++++++++++++++++++++++++ ~ help: consider annotating `Hash128_1` with `#[derive(Clone)]` | LL + #[derive(Clone)] From ccae456863cf2c10e66d1e179b74ff8e950ebfa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 13 Mar 2024 00:02:45 +0000 Subject: [PATCH 02/19] Minor test fmt --- tests/ui/borrowck/borrowck-fn-in-const-a.rs | 1 + tests/ui/borrowck/borrowck-in-static.rs | 2 +- tests/ui/borrowck/borrowck-move-out-of-static-item.rs | 3 ++- tests/ui/borrowck/borrowck-move-out-of-static-item.stderr | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/ui/borrowck/borrowck-fn-in-const-a.rs b/tests/ui/borrowck/borrowck-fn-in-const-a.rs index d4ceae2963b34..d52ec342b1a90 100644 --- a/tests/ui/borrowck/borrowck-fn-in-const-a.rs +++ b/tests/ui/borrowck/borrowck-fn-in-const-a.rs @@ -9,4 +9,5 @@ const MOVE: fn(&String) -> String = { }; fn main() { + println!("{}", MOVE(&String::new())); } diff --git a/tests/ui/borrowck/borrowck-in-static.rs b/tests/ui/borrowck/borrowck-in-static.rs index a45f7b18e07d6..864dff40f46bb 100644 --- a/tests/ui/borrowck/borrowck-in-static.rs +++ b/tests/ui/borrowck/borrowck-in-static.rs @@ -1,6 +1,6 @@ // check that borrowck looks inside consts/statics -static FN : &'static (dyn Fn() -> (BoxBox>) + Sync) = &|| { +static FN : &'static (dyn Fn() -> BoxBox> + Sync) = &|| { let x = Box::new(0); Box::new(|| x) //~ ERROR cannot move out of `x`, a captured variable in an `Fn` closure }; diff --git a/tests/ui/borrowck/borrowck-move-out-of-static-item.rs b/tests/ui/borrowck/borrowck-move-out-of-static-item.rs index d01fb261894c8..b24d9b932cdb0 100644 --- a/tests/ui/borrowck/borrowck-move-out-of-static-item.rs +++ b/tests/ui/borrowck/borrowck-move-out-of-static-item.rs @@ -8,7 +8,8 @@ static BAR: Foo = Foo { foo: 5 }; fn test(f: Foo) { - let _f = Foo{foo: 4, ..f}; + let f = Foo { foo: 4, ..f }; + println!("{}", f.foo); } fn main() { diff --git a/tests/ui/borrowck/borrowck-move-out-of-static-item.stderr b/tests/ui/borrowck/borrowck-move-out-of-static-item.stderr index 07dcaf875e701..c4530820be5db 100644 --- a/tests/ui/borrowck/borrowck-move-out-of-static-item.stderr +++ b/tests/ui/borrowck/borrowck-move-out-of-static-item.stderr @@ -1,5 +1,5 @@ error[E0507]: cannot move out of static item `BAR` - --> $DIR/borrowck-move-out-of-static-item.rs:15:10 + --> $DIR/borrowck-move-out-of-static-item.rs:16:10 | LL | test(BAR); | ^^^ move occurs because `BAR` has type `Foo`, which does not implement the `Copy` trait From fa2fc3ab9638aec571574e551cd9708aa4b64280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 13 Mar 2024 01:51:08 +0000 Subject: [PATCH 03/19] Suggest `.clone()` when moved while borrowed --- .../src/diagnostics/conflict_errors.rs | 73 ++++++++++++++----- .../associated-types-outlives.stderr | 5 ++ tests/ui/binop/binop-move-semantics.stderr | 5 ++ tests/ui/borrowck/borrow-tuple-fields.stderr | 10 +++ .../borrowck-bad-nested-calls-move.stderr | 10 +++ .../borrowck-field-sensitivity.stderr | 10 +++ .../borrowck-loan-blocks-move-cc.stderr | 10 +++ .../borrowck/borrowck-loan-blocks-move.stderr | 5 ++ ...-move-from-subpath-of-borrowed-path.stderr | 5 ++ .../borrowck-move-mut-base-ptr.stderr | 5 ++ .../borrowck-move-subcomponent.stderr | 5 ++ .../borrowck-multiple-captures.stderr | 15 ++++ tests/ui/borrowck/borrowck-unary-move.stderr | 5 ++ tests/ui/borrowck/clone-on-ref.fixed | 4 +- tests/ui/borrowck/clone-on-ref.stderr | 8 ++ tests/ui/btreemap/btreemap_dropck.stderr | 5 ++ .../dropck/drop-with-active-borrows-1.stderr | 5 ++ tests/ui/error-codes/E0504.stderr | 5 ++ tests/ui/error-codes/E0505.stderr | 5 ++ ...ied-bounds-unnorm-associated-type-4.stderr | 5 ++ ...ied-bounds-unnorm-associated-type-5.stderr | 5 ++ ...plied-bounds-unnorm-associated-type.stderr | 5 ++ tests/ui/nll/closure-access-spans.stderr | 5 ++ .../borrowck-move-ref-pattern.stderr | 5 ++ ...borrowck-call-is-borrow-issue-12224.stderr | 5 ++ .../span/send-is-not-static-std-sync.stderr | 15 ++++ .../suggestions/borrow-for-loop-head.stderr | 5 ++ tests/ui/unop-move-semantics.stderr | 5 ++ tests/ui/variance/variance-issue-20533.stderr | 15 ++++ 29 files changed, 246 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index cf550a2e5d862..8249d5142eda6 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -987,6 +987,23 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { can_suggest_clone } + pub(crate) fn suggest_cloning( + &self, + err: &mut Diag<'_>, + ty: Ty<'tcx>, + expr: &hir::Expr<'_>, + span: Span, + ) { + if let Some(clone_trait_def) = self.infcx.tcx.lang_items().clone_trait() + && self + .infcx + .type_implements_trait(clone_trait_def, [ty], self.param_env) + .must_apply_modulo_regions() + { + self.suggest_cloning_inner(err, ty, expr, span); + } + } + pub(crate) fn clone_on_reference(&self, expr: &hir::Expr<'_>) -> Option { let typeck_results = self.infcx.tcx.typeck(self.mir_def_id()); if let hir::ExprKind::MethodCall(segment, rcvr, args, span) = expr.kind @@ -1002,7 +1019,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } - fn suggest_cloning(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, expr: &hir::Expr<'_>, span: Span) { + fn suggest_cloning_inner( + &self, + err: &mut Diag<'_>, + ty: Ty<'tcx>, + expr: &hir::Expr<'_>, + span: Span, + ) { let tcx = self.infcx.tcx; // Try to find predicates on *generic params* that would allow copying `ty` let suggestion = @@ -1136,6 +1159,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { None, ); self.suggest_copy_for_type_in_cloned_ref(&mut err, place); + let typeck_results = self.infcx.tcx.typeck(self.mir_def_id()); + if let Some(expr) = self.find_expr(borrow_span) + && let Some(ty) = typeck_results.node_type_opt(expr.hir_id) + { + self.suggest_cloning(&mut err, ty, expr, borrow_span); + } self.buffer_error(err); } @@ -1553,22 +1582,32 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } for ty in types_to_constrain { - self.suggest_adding_bounds(err, ty, clone, body.span); - if let ty::Adt(..) = ty.kind() { - // The type doesn't implement Clone. - let trait_ref = ty::Binder::dummy(ty::TraitRef::new(self.infcx.tcx, clone, [ty])); - let obligation = Obligation::new( - self.infcx.tcx, - ObligationCause::dummy(), - self.param_env, - trait_ref, - ); - self.infcx.err_ctxt().suggest_derive( - &obligation, - err, - trait_ref.to_predicate(self.infcx.tcx), - ); - } + self.suggest_adding_bounds_or_derive(err, ty, clone, body.span); + } + } + + pub(crate) fn suggest_adding_bounds_or_derive( + &self, + err: &mut Diag<'_>, + ty: Ty<'tcx>, + def_id: DefId, + span: Span, + ) { + self.suggest_adding_bounds(err, ty, def_id, span); + if let ty::Adt(..) = ty.kind() { + // The type doesn't implement DefId. + let trait_ref = ty::Binder::dummy(ty::TraitRef::new(self.infcx.tcx, def_id, [ty])); + let obligation = Obligation::new( + self.infcx.tcx, + ObligationCause::dummy(), + self.param_env, + trait_ref, + ); + self.infcx.err_ctxt().suggest_derive( + &obligation, + err, + trait_ref.to_predicate(self.infcx.tcx), + ); } } diff --git a/tests/ui/associated-types/associated-types-outlives.stderr b/tests/ui/associated-types/associated-types-outlives.stderr index deeedd2226698..dcd05ba9c2e76 100644 --- a/tests/ui/associated-types/associated-types-outlives.stderr +++ b/tests/ui/associated-types/associated-types-outlives.stderr @@ -10,6 +10,11 @@ LL | drop(x); | ^ move out of `x` occurs here LL | return f(y); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | 's: loop { y = denormalise(&x.clone()); break } + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/binop/binop-move-semantics.stderr b/tests/ui/binop/binop-move-semantics.stderr index 1dd8c9a87d472..06dd84c3b9a59 100644 --- a/tests/ui/binop/binop-move-semantics.stderr +++ b/tests/ui/binop/binop-move-semantics.stderr @@ -51,6 +51,11 @@ LL | x ... LL | use_mut(n); use_imm(m); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let m = &x.clone(); + | ++++++++ error[E0505]: cannot move out of `y` because it is borrowed --> $DIR/binop-move-semantics.rs:23:5 diff --git a/tests/ui/borrowck/borrow-tuple-fields.stderr b/tests/ui/borrowck/borrow-tuple-fields.stderr index e324ebfb50f96..277c335cfccaa 100644 --- a/tests/ui/borrowck/borrow-tuple-fields.stderr +++ b/tests/ui/borrowck/borrow-tuple-fields.stderr @@ -10,6 +10,11 @@ LL | let y = x; LL | LL | r.use_ref(); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let r = &x.0.clone(); + | ++++++++ error[E0502]: cannot borrow `x.0` as mutable because it is also borrowed as immutable --> $DIR/borrow-tuple-fields.rs:18:13 @@ -42,6 +47,11 @@ LL | let y = x; | ^ move out of `x` occurs here LL | r.use_ref(); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let r = &x.0.clone(); + | ++++++++ error[E0502]: cannot borrow `x.0` as mutable because it is also borrowed as immutable --> $DIR/borrow-tuple-fields.rs:33:13 diff --git a/tests/ui/borrowck/borrowck-bad-nested-calls-move.stderr b/tests/ui/borrowck/borrowck-bad-nested-calls-move.stderr index e582ec605defe..eb5e2fa89f8e4 100644 --- a/tests/ui/borrowck/borrowck-bad-nested-calls-move.stderr +++ b/tests/ui/borrowck/borrowck-bad-nested-calls-move.stderr @@ -10,6 +10,11 @@ LL | &*a, | --- borrow of `*a` occurs here LL | a); | ^ move out of `a` occurs here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | &*a.clone(), + | ++++++++ error[E0505]: cannot move out of `a` because it is borrowed --> $DIR/borrowck-bad-nested-calls-move.rs:32:9 @@ -22,6 +27,11 @@ LL | &*a, | --- borrow of `*a` occurs here LL | a); | ^ move out of `a` occurs here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | &*a.clone(), + | ++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/borrowck/borrowck-field-sensitivity.stderr b/tests/ui/borrowck/borrowck-field-sensitivity.stderr index 11812847dd181..b30dd144a4d80 100644 --- a/tests/ui/borrowck/borrowck-field-sensitivity.stderr +++ b/tests/ui/borrowck/borrowck-field-sensitivity.stderr @@ -49,6 +49,11 @@ LL | drop(x.b); | ^^^ move out of `x.b` occurs here LL | drop(**p); | --- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let p = &x.b.clone(); + | ++++++++ error[E0505]: cannot move out of `x.b` because it is borrowed --> $DIR/borrowck-field-sensitivity.rs:41:14 @@ -61,6 +66,11 @@ LL | let _y = A { a: 3, .. x }; | ^^^^^^^^^^^^^^^^ move out of `x.b` occurs here LL | drop(**p); | --- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let p = &x.b.clone(); + | ++++++++ error[E0499]: cannot borrow `x.a` as mutable more than once at a time --> $DIR/borrowck-field-sensitivity.rs:48:13 diff --git a/tests/ui/borrowck/borrowck-loan-blocks-move-cc.stderr b/tests/ui/borrowck/borrowck-loan-blocks-move-cc.stderr index 86479043a0646..d9af5cf7d12e8 100644 --- a/tests/ui/borrowck/borrowck-loan-blocks-move-cc.stderr +++ b/tests/ui/borrowck/borrowck-loan-blocks-move-cc.stderr @@ -13,6 +13,11 @@ LL | println!("v={}", *v); LL | }); LL | w.use_ref(); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let w = &v.clone(); + | ++++++++ error[E0505]: cannot move out of `v` because it is borrowed --> $DIR/borrowck-loan-blocks-move-cc.rs:24:19 @@ -29,6 +34,11 @@ LL | println!("v={}", *v); LL | }); LL | w.use_ref(); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let w = &v.clone(); + | ++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/borrowck/borrowck-loan-blocks-move.stderr b/tests/ui/borrowck/borrowck-loan-blocks-move.stderr index d1fbc5b47db14..1698035f20ffb 100644 --- a/tests/ui/borrowck/borrowck-loan-blocks-move.stderr +++ b/tests/ui/borrowck/borrowck-loan-blocks-move.stderr @@ -9,6 +9,11 @@ LL | take(v); | ^ move out of `v` occurs here LL | w.use_ref(); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let w = &v.clone(); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-move-from-subpath-of-borrowed-path.stderr b/tests/ui/borrowck/borrowck-move-from-subpath-of-borrowed-path.stderr index a41c4af98e7cd..b78c374c45620 100644 --- a/tests/ui/borrowck/borrowck-move-from-subpath-of-borrowed-path.stderr +++ b/tests/ui/borrowck/borrowck-move-from-subpath-of-borrowed-path.stderr @@ -10,6 +10,11 @@ LL | let z = *a; | ^^ move out of `*a` occurs here LL | b.use_ref(); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let b = &a.clone(); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-move-mut-base-ptr.stderr b/tests/ui/borrowck/borrowck-move-mut-base-ptr.stderr index 88eb6c8ceeee6..3e540a5dc98a3 100644 --- a/tests/ui/borrowck/borrowck-move-mut-base-ptr.stderr +++ b/tests/ui/borrowck/borrowck-move-mut-base-ptr.stderr @@ -10,6 +10,11 @@ LL | let t1 = t0; LL | *t1 = 22; LL | p.use_ref(); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let p: &isize = &*t0.clone(); // Freezes `*t0` + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-move-subcomponent.stderr b/tests/ui/borrowck/borrowck-move-subcomponent.stderr index 8408d99156a30..5eb02ee2be996 100644 --- a/tests/ui/borrowck/borrowck-move-subcomponent.stderr +++ b/tests/ui/borrowck/borrowck-move-subcomponent.stderr @@ -9,6 +9,11 @@ LL | let S { x: ax } = a; | ^^ move out of `a.x` occurs here LL | f(pb); | -- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let pb = &a.clone(); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-multiple-captures.stderr b/tests/ui/borrowck/borrowck-multiple-captures.stderr index 70abe7b346e1a..01b648ea64717 100644 --- a/tests/ui/borrowck/borrowck-multiple-captures.stderr +++ b/tests/ui/borrowck/borrowck-multiple-captures.stderr @@ -14,6 +14,11 @@ LL | drop(x1); ... LL | borrow(&*p1); | ---- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let p1 = &x1.clone(); + | ++++++++ error[E0505]: cannot move out of `x2` because it is borrowed --> $DIR/borrowck-multiple-captures.rs:12:19 @@ -30,6 +35,11 @@ LL | drop(x2); ... LL | borrow(&*p2); | ---- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let p2 = &x2.clone(); + | ++++++++ error[E0382]: use of moved value: `x1` --> $DIR/borrowck-multiple-captures.rs:27:19 @@ -93,6 +103,11 @@ LL | drop(x); ... LL | borrow(&*p); | --- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let p = &x.clone(); + | ++++++++ error[E0382]: use of moved value: `x` --> $DIR/borrowck-multiple-captures.rs:52:14 diff --git a/tests/ui/borrowck/borrowck-unary-move.stderr b/tests/ui/borrowck/borrowck-unary-move.stderr index e6c3869f67ae4..a4118b3d1a3ad 100644 --- a/tests/ui/borrowck/borrowck-unary-move.stderr +++ b/tests/ui/borrowck/borrowck-unary-move.stderr @@ -9,6 +9,11 @@ LL | free(x); | ^ move out of `x` occurs here LL | *y | -- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let y = &*x.clone(); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/clone-on-ref.fixed b/tests/ui/borrowck/clone-on-ref.fixed index b6927ba590e34..58339919696d4 100644 --- a/tests/ui/borrowck/clone-on-ref.fixed +++ b/tests/ui/borrowck/clone-on-ref.fixed @@ -9,7 +9,7 @@ fn foo(list: &mut Vec) { drop(cloned_items); } fn bar(x: T) { - let a = &x; + let a = &x.clone(); let b = a.clone(); drop(x); //~^ ERROR cannot move out of `x` because it is borrowed @@ -19,7 +19,7 @@ fn bar(x: T) { #[derive(Clone)] struct A; fn qux(x: A) { - let a = &x; + let a = &x.clone(); let b = a.clone(); drop(x); //~^ ERROR cannot move out of `x` because it is borrowed diff --git a/tests/ui/borrowck/clone-on-ref.stderr b/tests/ui/borrowck/clone-on-ref.stderr index ee4fcadf55ae1..732a9370fa709 100644 --- a/tests/ui/borrowck/clone-on-ref.stderr +++ b/tests/ui/borrowck/clone-on-ref.stderr @@ -36,6 +36,10 @@ help: consider further restricting this bound | LL | fn bar(x: T) { | +++++++ +help: consider cloning the value if the performance cost is acceptable + | +LL | let a = &x.clone(); + | ++++++++ error[E0505]: cannot move out of `x` because it is borrowed --> $DIR/clone-on-ref.rs:23:10 @@ -57,6 +61,10 @@ help: consider annotating `A` with `#[derive(Clone)]` LL + #[derive(Clone)] LL | struct A; | +help: consider cloning the value if the performance cost is acceptable + | +LL | let a = &x.clone(); + | ++++++++ error: aborting due to 3 previous errors diff --git a/tests/ui/btreemap/btreemap_dropck.stderr b/tests/ui/btreemap/btreemap_dropck.stderr index 805c2112bdc10..e8f14552af26d 100644 --- a/tests/ui/btreemap/btreemap_dropck.stderr +++ b/tests/ui/btreemap/btreemap_dropck.stderr @@ -9,6 +9,11 @@ LL | drop(s); | ^ move out of `s` occurs here LL | } | - borrow might be used here, when `_map` is dropped and runs the `Drop` code for type `BTreeMap` + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let _map = BTreeMap::from_iter([((), PrintOnDrop(&s.clone()))]); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/dropck/drop-with-active-borrows-1.stderr b/tests/ui/dropck/drop-with-active-borrows-1.stderr index 229514c6feef0..7d1633267f0e2 100644 --- a/tests/ui/dropck/drop-with-active-borrows-1.stderr +++ b/tests/ui/dropck/drop-with-active-borrows-1.stderr @@ -9,6 +9,11 @@ LL | drop(a); | ^ move out of `a` occurs here LL | for s in &b { | -- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let b: Vec<&str> = a.clone().lines().collect(); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0504.stderr b/tests/ui/error-codes/E0504.stderr index c8a48961cb3b1..9759e81a2c7ba 100644 --- a/tests/ui/error-codes/E0504.stderr +++ b/tests/ui/error-codes/E0504.stderr @@ -13,6 +13,11 @@ LL | println!("child function: {}", fancy_num.num); ... LL | println!("main function: {}", fancy_ref.num); | ------------- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let fancy_ref = &fancy_num.clone(); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0505.stderr b/tests/ui/error-codes/E0505.stderr index 250680d2c1c5d..6808943796cdc 100644 --- a/tests/ui/error-codes/E0505.stderr +++ b/tests/ui/error-codes/E0505.stderr @@ -10,6 +10,11 @@ LL | eat(x); | ^ move out of `x` occurs here LL | _ref_to_val.use_ref(); | ----------- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let _ref_to_val: &Value = &x.clone(); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/fn/implied-bounds-unnorm-associated-type-4.stderr b/tests/ui/fn/implied-bounds-unnorm-associated-type-4.stderr index 3be630e2b2352..4e64ed6f482e0 100644 --- a/tests/ui/fn/implied-bounds-unnorm-associated-type-4.stderr +++ b/tests/ui/fn/implied-bounds-unnorm-associated-type-4.stderr @@ -10,6 +10,11 @@ LL | drop(x); LL | LL | println!("{}", y); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let y = f(&x.clone(), ()); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/fn/implied-bounds-unnorm-associated-type-5.stderr b/tests/ui/fn/implied-bounds-unnorm-associated-type-5.stderr index bf6d77b626993..b898df0835c2a 100644 --- a/tests/ui/fn/implied-bounds-unnorm-associated-type-5.stderr +++ b/tests/ui/fn/implied-bounds-unnorm-associated-type-5.stderr @@ -27,6 +27,11 @@ LL | drop(x); | ^ move out of `x` occurs here LL | println!("{}", y); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let y = f(&x.clone(), ()); + | ++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/fn/implied-bounds-unnorm-associated-type.stderr b/tests/ui/fn/implied-bounds-unnorm-associated-type.stderr index c2a8fa741ca5a..2a7431305fed4 100644 --- a/tests/ui/fn/implied-bounds-unnorm-associated-type.stderr +++ b/tests/ui/fn/implied-bounds-unnorm-associated-type.stderr @@ -10,6 +10,11 @@ LL | drop(x); LL | LL | println!("{}", y); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let y = f(&x.clone(), ()); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/nll/closure-access-spans.stderr b/tests/ui/nll/closure-access-spans.stderr index 3e98fbd5e1dea..a8024a8c20b9e 100644 --- a/tests/ui/nll/closure-access-spans.stderr +++ b/tests/ui/nll/closure-access-spans.stderr @@ -57,6 +57,11 @@ LL | || x; | move out of `x` occurs here LL | r.use_ref(); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let r = &x.clone(); + | ++++++++ error[E0382]: borrow of moved value: `x` --> $DIR/closure-access-spans.rs:35:5 diff --git a/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr b/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr index a033cc0655ef9..62896e1fd03ae 100644 --- a/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr +++ b/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr @@ -10,6 +10,11 @@ LL | let [ref _x0_hold, _x1, ref xs_hold @ ..] = arr; LL | _x1 = U; LL | drop(hold_all); | -------- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let hold_all = &arr.clone(); + | ++++++++ error[E0384]: cannot assign twice to immutable variable `_x1` --> $DIR/borrowck-move-ref-pattern.rs:9:5 diff --git a/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr b/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr index 29a606c4f0188..bac7801f7f430 100644 --- a/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr +++ b/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr @@ -57,6 +57,11 @@ LL | f(Box::new(|a| { LL | LL | foo(f); | - move occurs due to use in closure + | +help: consider cloning the value if the performance cost is acceptable + | +LL | f.clone()(Box::new(|a| { + | ++++++++ error: aborting due to 5 previous errors diff --git a/tests/ui/span/send-is-not-static-std-sync.stderr b/tests/ui/span/send-is-not-static-std-sync.stderr index 46534b39168cb..1f4462f4fe72d 100644 --- a/tests/ui/span/send-is-not-static-std-sync.stderr +++ b/tests/ui/span/send-is-not-static-std-sync.stderr @@ -11,6 +11,11 @@ LL | drop(y); ... LL | *lock.lock().unwrap() = &z; | ---- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | *lock.lock().unwrap() = &*y.clone(); + | ++++++++ error[E0597]: `z` does not live long enough --> $DIR/send-is-not-static-std-sync.rs:16:33 @@ -38,6 +43,11 @@ LL | drop(y); ... LL | *lock.write().unwrap() = &z; | ---- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | *lock.write().unwrap() = &*y.clone(); + | ++++++++ error[E0597]: `z` does not live long enough --> $DIR/send-is-not-static-std-sync.rs:30:34 @@ -65,6 +75,11 @@ LL | drop(y); ... LL | tx.send(&z).unwrap(); | -- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | tx.send(&*y.clone()); + | ++++++++ error[E0597]: `z` does not live long enough --> $DIR/send-is-not-static-std-sync.rs:46:17 diff --git a/tests/ui/suggestions/borrow-for-loop-head.stderr b/tests/ui/suggestions/borrow-for-loop-head.stderr index 0f179438a1263..55fcb44168c49 100644 --- a/tests/ui/suggestions/borrow-for-loop-head.stderr +++ b/tests/ui/suggestions/borrow-for-loop-head.stderr @@ -7,6 +7,11 @@ LL | for i in &a { | -- borrow of `a` occurs here LL | for j in a { | ^ move out of `a` occurs here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | for i in &a.clone() { + | ++++++++ error[E0382]: use of moved value: `a` --> $DIR/borrow-for-loop-head.rs:4:18 diff --git a/tests/ui/unop-move-semantics.stderr b/tests/ui/unop-move-semantics.stderr index 187dd66b2fe59..a6549d84a8a09 100644 --- a/tests/ui/unop-move-semantics.stderr +++ b/tests/ui/unop-move-semantics.stderr @@ -33,6 +33,11 @@ LL | !x; ... LL | use_mut(n); use_imm(m); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let m = &x.clone(); + | ++++++++ error[E0505]: cannot move out of `y` because it is borrowed --> $DIR/unop-move-semantics.rs:17:6 diff --git a/tests/ui/variance/variance-issue-20533.stderr b/tests/ui/variance/variance-issue-20533.stderr index 258f67db5ce4b..7fed947972f74 100644 --- a/tests/ui/variance/variance-issue-20533.stderr +++ b/tests/ui/variance/variance-issue-20533.stderr @@ -9,6 +9,11 @@ LL | drop(a); | ^ move out of `a` occurs here LL | drop(x); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let x = foo(&a.clone()); + | ++++++++ error[E0505]: cannot move out of `a` because it is borrowed --> $DIR/variance-issue-20533.rs:34:14 @@ -21,6 +26,11 @@ LL | drop(a); | ^ move out of `a` occurs here LL | drop(x); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let x = bar(&a.clone()); + | ++++++++ error[E0505]: cannot move out of `a` because it is borrowed --> $DIR/variance-issue-20533.rs:40:14 @@ -33,6 +43,11 @@ LL | drop(a); | ^ move out of `a` occurs here LL | drop(x); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let x = baz(&a.clone()); + | ++++++++ error: aborting due to 3 previous errors From bce78102c31c2e71313af1e8119eb882dea35fad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 13 Mar 2024 02:13:51 +0000 Subject: [PATCH 04/19] Account for unops when suggesting cloning --- .../src/diagnostics/conflict_errors.rs | 43 +++++++++++-------- .../associated-types-outlives.stderr | 5 ++- tests/ui/binop/binop-move-semantics.stderr | 5 ++- tests/ui/borrowck/borrow-tuple-fields.stderr | 10 +++-- .../borrowck-bad-nested-calls-move.stderr | 10 +++-- .../borrowck-field-sensitivity.stderr | 10 +++-- .../borrowck-loan-blocks-move-cc.stderr | 10 +++-- .../borrowck/borrowck-loan-blocks-move.stderr | 5 ++- ...-move-from-subpath-of-borrowed-path.stderr | 5 ++- .../borrowck-move-mut-base-ptr.stderr | 5 ++- .../borrowck-move-subcomponent.stderr | 5 ++- .../borrowck-multiple-captures.stderr | 15 ++++--- tests/ui/borrowck/borrowck-unary-move.stderr | 5 ++- tests/ui/borrowck/clone-on-ref.fixed | 4 +- tests/ui/borrowck/clone-on-ref.stderr | 10 +++-- tests/ui/btreemap/btreemap_dropck.stderr | 5 ++- tests/ui/error-codes/E0504.stderr | 5 ++- tests/ui/error-codes/E0505.stderr | 5 ++- ...ied-bounds-unnorm-associated-type-4.stderr | 5 ++- ...ied-bounds-unnorm-associated-type-5.stderr | 5 ++- ...plied-bounds-unnorm-associated-type.stderr | 5 ++- tests/ui/nll/closure-access-spans.stderr | 5 ++- .../borrowck-move-ref-pattern.stderr | 5 ++- .../span/send-is-not-static-std-sync.stderr | 15 ++++--- .../suggestions/borrow-for-loop-head.stderr | 5 ++- tests/ui/unop-move-semantics.stderr | 5 ++- tests/ui/variance/variance-issue-20533.stderr | 15 ++++--- 27 files changed, 134 insertions(+), 93 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 8249d5142eda6..4bbeadbae705d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1034,27 +1034,32 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } else { ".clone()".to_owned() }; - if let Some(clone_trait_def) = tcx.lang_items().clone_trait() - && self - .infcx - .type_implements_trait(clone_trait_def, [ty], self.param_env) - .must_apply_modulo_regions() + let mut sugg = Vec::with_capacity(2); + let mut inner_expr = expr; + while let hir::ExprKind::AddrOf(.., inner) | hir::ExprKind::Unary(hir::UnOp::Deref, inner) = + &inner_expr.kind { - let msg = if let ty::Adt(def, _) = ty.kind() - && [tcx.get_diagnostic_item(sym::Arc), tcx.get_diagnostic_item(sym::Rc)] - .contains(&Some(def.did())) - { - "clone the value to increment its reference count" - } else { - "consider cloning the value if the performance cost is acceptable" - }; - err.span_suggestion_verbose( - span.shrink_to_hi(), - msg, - suggestion, - Applicability::MachineApplicable, - ); + inner_expr = inner; } + if inner_expr.span.lo() != expr.span.lo() { + sugg.push((expr.span.with_hi(inner_expr.span.lo()), String::new())); + } + let span = if inner_expr.span.hi() != expr.span.hi() { + // Account for `(*x)` to suggest `x.clone()`. + expr.span.with_lo(inner_expr.span.hi()) + } else { + span.shrink_to_hi() + }; + sugg.push((span, suggestion)); + let msg = if let ty::Adt(def, _) = ty.kind() + && [tcx.get_diagnostic_item(sym::Arc), tcx.get_diagnostic_item(sym::Rc)] + .contains(&Some(def.did())) + { + "clone the value to increment its reference count" + } else { + "consider cloning the value if the performance cost is acceptable" + }; + err.multipart_suggestion_verbose(msg, sugg, Applicability::MachineApplicable); } fn suggest_adding_bounds(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, def_id: DefId, span: Span) { diff --git a/tests/ui/associated-types/associated-types-outlives.stderr b/tests/ui/associated-types/associated-types-outlives.stderr index dcd05ba9c2e76..4a496b5fa87f1 100644 --- a/tests/ui/associated-types/associated-types-outlives.stderr +++ b/tests/ui/associated-types/associated-types-outlives.stderr @@ -13,8 +13,9 @@ LL | return f(y); | help: consider cloning the value if the performance cost is acceptable | -LL | 's: loop { y = denormalise(&x.clone()); break } - | ++++++++ +LL - 's: loop { y = denormalise(&x); break } +LL + 's: loop { y = denormalise(x.clone()); break } + | error: aborting due to 1 previous error diff --git a/tests/ui/binop/binop-move-semantics.stderr b/tests/ui/binop/binop-move-semantics.stderr index 06dd84c3b9a59..219bfeaae0675 100644 --- a/tests/ui/binop/binop-move-semantics.stderr +++ b/tests/ui/binop/binop-move-semantics.stderr @@ -54,8 +54,9 @@ LL | use_mut(n); use_imm(m); | help: consider cloning the value if the performance cost is acceptable | -LL | let m = &x.clone(); - | ++++++++ +LL - let m = &x; +LL + let m = x.clone(); + | error[E0505]: cannot move out of `y` because it is borrowed --> $DIR/binop-move-semantics.rs:23:5 diff --git a/tests/ui/borrowck/borrow-tuple-fields.stderr b/tests/ui/borrowck/borrow-tuple-fields.stderr index 277c335cfccaa..8ea7a9a498947 100644 --- a/tests/ui/borrowck/borrow-tuple-fields.stderr +++ b/tests/ui/borrowck/borrow-tuple-fields.stderr @@ -13,8 +13,9 @@ LL | r.use_ref(); | help: consider cloning the value if the performance cost is acceptable | -LL | let r = &x.0.clone(); - | ++++++++ +LL - let r = &x.0; +LL + let r = x.0.clone(); + | error[E0502]: cannot borrow `x.0` as mutable because it is also borrowed as immutable --> $DIR/borrow-tuple-fields.rs:18:13 @@ -50,8 +51,9 @@ LL | r.use_ref(); | help: consider cloning the value if the performance cost is acceptable | -LL | let r = &x.0.clone(); - | ++++++++ +LL - let r = &x.0; +LL + let r = x.0.clone(); + | error[E0502]: cannot borrow `x.0` as mutable because it is also borrowed as immutable --> $DIR/borrow-tuple-fields.rs:33:13 diff --git a/tests/ui/borrowck/borrowck-bad-nested-calls-move.stderr b/tests/ui/borrowck/borrowck-bad-nested-calls-move.stderr index eb5e2fa89f8e4..b96949fbb0e78 100644 --- a/tests/ui/borrowck/borrowck-bad-nested-calls-move.stderr +++ b/tests/ui/borrowck/borrowck-bad-nested-calls-move.stderr @@ -13,8 +13,9 @@ LL | a); | help: consider cloning the value if the performance cost is acceptable | -LL | &*a.clone(), - | ++++++++ +LL - &*a, +LL + a.clone(), + | error[E0505]: cannot move out of `a` because it is borrowed --> $DIR/borrowck-bad-nested-calls-move.rs:32:9 @@ -30,8 +31,9 @@ LL | a); | help: consider cloning the value if the performance cost is acceptable | -LL | &*a.clone(), - | ++++++++ +LL - &*a, +LL + a.clone(), + | error: aborting due to 2 previous errors diff --git a/tests/ui/borrowck/borrowck-field-sensitivity.stderr b/tests/ui/borrowck/borrowck-field-sensitivity.stderr index b30dd144a4d80..ea552ff7820e2 100644 --- a/tests/ui/borrowck/borrowck-field-sensitivity.stderr +++ b/tests/ui/borrowck/borrowck-field-sensitivity.stderr @@ -52,8 +52,9 @@ LL | drop(**p); | help: consider cloning the value if the performance cost is acceptable | -LL | let p = &x.b.clone(); - | ++++++++ +LL - let p = &x.b; +LL + let p = x.b.clone(); + | error[E0505]: cannot move out of `x.b` because it is borrowed --> $DIR/borrowck-field-sensitivity.rs:41:14 @@ -69,8 +70,9 @@ LL | drop(**p); | help: consider cloning the value if the performance cost is acceptable | -LL | let p = &x.b.clone(); - | ++++++++ +LL - let p = &x.b; +LL + let p = x.b.clone(); + | error[E0499]: cannot borrow `x.a` as mutable more than once at a time --> $DIR/borrowck-field-sensitivity.rs:48:13 diff --git a/tests/ui/borrowck/borrowck-loan-blocks-move-cc.stderr b/tests/ui/borrowck/borrowck-loan-blocks-move-cc.stderr index d9af5cf7d12e8..370ae058f444d 100644 --- a/tests/ui/borrowck/borrowck-loan-blocks-move-cc.stderr +++ b/tests/ui/borrowck/borrowck-loan-blocks-move-cc.stderr @@ -16,8 +16,9 @@ LL | w.use_ref(); | help: consider cloning the value if the performance cost is acceptable | -LL | let w = &v.clone(); - | ++++++++ +LL - let w = &v; +LL + let w = v.clone(); + | error[E0505]: cannot move out of `v` because it is borrowed --> $DIR/borrowck-loan-blocks-move-cc.rs:24:19 @@ -37,8 +38,9 @@ LL | w.use_ref(); | help: consider cloning the value if the performance cost is acceptable | -LL | let w = &v.clone(); - | ++++++++ +LL - let w = &v; +LL + let w = v.clone(); + | error: aborting due to 2 previous errors diff --git a/tests/ui/borrowck/borrowck-loan-blocks-move.stderr b/tests/ui/borrowck/borrowck-loan-blocks-move.stderr index 1698035f20ffb..8a8005dbb8337 100644 --- a/tests/ui/borrowck/borrowck-loan-blocks-move.stderr +++ b/tests/ui/borrowck/borrowck-loan-blocks-move.stderr @@ -12,8 +12,9 @@ LL | w.use_ref(); | help: consider cloning the value if the performance cost is acceptable | -LL | let w = &v.clone(); - | ++++++++ +LL - let w = &v; +LL + let w = v.clone(); + | error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-move-from-subpath-of-borrowed-path.stderr b/tests/ui/borrowck/borrowck-move-from-subpath-of-borrowed-path.stderr index b78c374c45620..a23a203d99978 100644 --- a/tests/ui/borrowck/borrowck-move-from-subpath-of-borrowed-path.stderr +++ b/tests/ui/borrowck/borrowck-move-from-subpath-of-borrowed-path.stderr @@ -13,8 +13,9 @@ LL | b.use_ref(); | help: consider cloning the value if the performance cost is acceptable | -LL | let b = &a.clone(); - | ++++++++ +LL - let b = &a; +LL + let b = a.clone(); + | error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-move-mut-base-ptr.stderr b/tests/ui/borrowck/borrowck-move-mut-base-ptr.stderr index 3e540a5dc98a3..acf426906c3b2 100644 --- a/tests/ui/borrowck/borrowck-move-mut-base-ptr.stderr +++ b/tests/ui/borrowck/borrowck-move-mut-base-ptr.stderr @@ -13,8 +13,9 @@ LL | p.use_ref(); | help: consider cloning the value if the performance cost is acceptable | -LL | let p: &isize = &*t0.clone(); // Freezes `*t0` - | ++++++++ +LL - let p: &isize = &*t0; // Freezes `*t0` +LL + let p: &isize = t0.clone(); // Freezes `*t0` + | error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-move-subcomponent.stderr b/tests/ui/borrowck/borrowck-move-subcomponent.stderr index 5eb02ee2be996..1e4d94920f488 100644 --- a/tests/ui/borrowck/borrowck-move-subcomponent.stderr +++ b/tests/ui/borrowck/borrowck-move-subcomponent.stderr @@ -12,8 +12,9 @@ LL | f(pb); | help: consider cloning the value if the performance cost is acceptable | -LL | let pb = &a.clone(); - | ++++++++ +LL - let pb = &a; +LL + let pb = a.clone(); + | error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-multiple-captures.stderr b/tests/ui/borrowck/borrowck-multiple-captures.stderr index 01b648ea64717..fdac4c27cee3b 100644 --- a/tests/ui/borrowck/borrowck-multiple-captures.stderr +++ b/tests/ui/borrowck/borrowck-multiple-captures.stderr @@ -17,8 +17,9 @@ LL | borrow(&*p1); | help: consider cloning the value if the performance cost is acceptable | -LL | let p1 = &x1.clone(); - | ++++++++ +LL - let p1 = &x1; +LL + let p1 = x1.clone(); + | error[E0505]: cannot move out of `x2` because it is borrowed --> $DIR/borrowck-multiple-captures.rs:12:19 @@ -38,8 +39,9 @@ LL | borrow(&*p2); | help: consider cloning the value if the performance cost is acceptable | -LL | let p2 = &x2.clone(); - | ++++++++ +LL - let p2 = &x2; +LL + let p2 = x2.clone(); + | error[E0382]: use of moved value: `x1` --> $DIR/borrowck-multiple-captures.rs:27:19 @@ -106,8 +108,9 @@ LL | borrow(&*p); | help: consider cloning the value if the performance cost is acceptable | -LL | let p = &x.clone(); - | ++++++++ +LL - let p = &x; +LL + let p = x.clone(); + | error[E0382]: use of moved value: `x` --> $DIR/borrowck-multiple-captures.rs:52:14 diff --git a/tests/ui/borrowck/borrowck-unary-move.stderr b/tests/ui/borrowck/borrowck-unary-move.stderr index a4118b3d1a3ad..598ecb537780f 100644 --- a/tests/ui/borrowck/borrowck-unary-move.stderr +++ b/tests/ui/borrowck/borrowck-unary-move.stderr @@ -12,8 +12,9 @@ LL | *y | help: consider cloning the value if the performance cost is acceptable | -LL | let y = &*x.clone(); - | ++++++++ +LL - let y = &*x; +LL + let y = x.clone(); + | error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/clone-on-ref.fixed b/tests/ui/borrowck/clone-on-ref.fixed index 58339919696d4..539ecd0b32c7b 100644 --- a/tests/ui/borrowck/clone-on-ref.fixed +++ b/tests/ui/borrowck/clone-on-ref.fixed @@ -9,7 +9,7 @@ fn foo(list: &mut Vec) { drop(cloned_items); } fn bar(x: T) { - let a = &x.clone(); + let a = x.clone(); let b = a.clone(); drop(x); //~^ ERROR cannot move out of `x` because it is borrowed @@ -19,7 +19,7 @@ fn bar(x: T) { #[derive(Clone)] struct A; fn qux(x: A) { - let a = &x.clone(); + let a = x.clone(); let b = a.clone(); drop(x); //~^ ERROR cannot move out of `x` because it is borrowed diff --git a/tests/ui/borrowck/clone-on-ref.stderr b/tests/ui/borrowck/clone-on-ref.stderr index 732a9370fa709..5ee56edd7b585 100644 --- a/tests/ui/borrowck/clone-on-ref.stderr +++ b/tests/ui/borrowck/clone-on-ref.stderr @@ -38,8 +38,9 @@ LL | fn bar(x: T) { | +++++++ help: consider cloning the value if the performance cost is acceptable | -LL | let a = &x.clone(); - | ++++++++ +LL - let a = &x; +LL + let a = x.clone(); + | error[E0505]: cannot move out of `x` because it is borrowed --> $DIR/clone-on-ref.rs:23:10 @@ -63,8 +64,9 @@ LL | struct A; | help: consider cloning the value if the performance cost is acceptable | -LL | let a = &x.clone(); - | ++++++++ +LL - let a = &x; +LL + let a = x.clone(); + | error: aborting due to 3 previous errors diff --git a/tests/ui/btreemap/btreemap_dropck.stderr b/tests/ui/btreemap/btreemap_dropck.stderr index e8f14552af26d..873f8cf9a0147 100644 --- a/tests/ui/btreemap/btreemap_dropck.stderr +++ b/tests/ui/btreemap/btreemap_dropck.stderr @@ -12,8 +12,9 @@ LL | } | help: consider cloning the value if the performance cost is acceptable | -LL | let _map = BTreeMap::from_iter([((), PrintOnDrop(&s.clone()))]); - | ++++++++ +LL - let _map = BTreeMap::from_iter([((), PrintOnDrop(&s))]); +LL + let _map = BTreeMap::from_iter([((), PrintOnDrop(s.clone()))]); + | error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0504.stderr b/tests/ui/error-codes/E0504.stderr index 9759e81a2c7ba..47f1218d2fa17 100644 --- a/tests/ui/error-codes/E0504.stderr +++ b/tests/ui/error-codes/E0504.stderr @@ -16,8 +16,9 @@ LL | println!("main function: {}", fancy_ref.num); | help: consider cloning the value if the performance cost is acceptable | -LL | let fancy_ref = &fancy_num.clone(); - | ++++++++ +LL - let fancy_ref = &fancy_num; +LL + let fancy_ref = fancy_num.clone(); + | error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0505.stderr b/tests/ui/error-codes/E0505.stderr index 6808943796cdc..7e99d5252bdc0 100644 --- a/tests/ui/error-codes/E0505.stderr +++ b/tests/ui/error-codes/E0505.stderr @@ -13,8 +13,9 @@ LL | _ref_to_val.use_ref(); | help: consider cloning the value if the performance cost is acceptable | -LL | let _ref_to_val: &Value = &x.clone(); - | ++++++++ +LL - let _ref_to_val: &Value = &x; +LL + let _ref_to_val: &Value = x.clone(); + | error: aborting due to 1 previous error diff --git a/tests/ui/fn/implied-bounds-unnorm-associated-type-4.stderr b/tests/ui/fn/implied-bounds-unnorm-associated-type-4.stderr index 4e64ed6f482e0..b8ec2e3b7e7b9 100644 --- a/tests/ui/fn/implied-bounds-unnorm-associated-type-4.stderr +++ b/tests/ui/fn/implied-bounds-unnorm-associated-type-4.stderr @@ -13,8 +13,9 @@ LL | println!("{}", y); | help: consider cloning the value if the performance cost is acceptable | -LL | let y = f(&x.clone(), ()); - | ++++++++ +LL - let y = f(&x, ()); +LL + let y = f(x.clone(), ()); + | error: aborting due to 1 previous error diff --git a/tests/ui/fn/implied-bounds-unnorm-associated-type-5.stderr b/tests/ui/fn/implied-bounds-unnorm-associated-type-5.stderr index b898df0835c2a..382ab8636a294 100644 --- a/tests/ui/fn/implied-bounds-unnorm-associated-type-5.stderr +++ b/tests/ui/fn/implied-bounds-unnorm-associated-type-5.stderr @@ -30,8 +30,9 @@ LL | println!("{}", y); | help: consider cloning the value if the performance cost is acceptable | -LL | let y = f(&x.clone(), ()); - | ++++++++ +LL - let y = f(&x, ()); +LL + let y = f(x.clone(), ()); + | error: aborting due to 2 previous errors diff --git a/tests/ui/fn/implied-bounds-unnorm-associated-type.stderr b/tests/ui/fn/implied-bounds-unnorm-associated-type.stderr index 2a7431305fed4..ce97d8527e857 100644 --- a/tests/ui/fn/implied-bounds-unnorm-associated-type.stderr +++ b/tests/ui/fn/implied-bounds-unnorm-associated-type.stderr @@ -13,8 +13,9 @@ LL | println!("{}", y); | help: consider cloning the value if the performance cost is acceptable | -LL | let y = f(&x.clone(), ()); - | ++++++++ +LL - let y = f(&x, ()); +LL + let y = f(x.clone(), ()); + | error: aborting due to 1 previous error diff --git a/tests/ui/nll/closure-access-spans.stderr b/tests/ui/nll/closure-access-spans.stderr index a8024a8c20b9e..7dc8acf84999b 100644 --- a/tests/ui/nll/closure-access-spans.stderr +++ b/tests/ui/nll/closure-access-spans.stderr @@ -60,8 +60,9 @@ LL | r.use_ref(); | help: consider cloning the value if the performance cost is acceptable | -LL | let r = &x.clone(); - | ++++++++ +LL - let r = &x; +LL + let r = x.clone(); + | error[E0382]: borrow of moved value: `x` --> $DIR/closure-access-spans.rs:35:5 diff --git a/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr b/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr index 62896e1fd03ae..124731d01f361 100644 --- a/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr +++ b/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr @@ -13,8 +13,9 @@ LL | drop(hold_all); | help: consider cloning the value if the performance cost is acceptable | -LL | let hold_all = &arr.clone(); - | ++++++++ +LL - let hold_all = &arr; +LL + let hold_all = arr.clone(); + | error[E0384]: cannot assign twice to immutable variable `_x1` --> $DIR/borrowck-move-ref-pattern.rs:9:5 diff --git a/tests/ui/span/send-is-not-static-std-sync.stderr b/tests/ui/span/send-is-not-static-std-sync.stderr index 1f4462f4fe72d..50b8ffe0114b0 100644 --- a/tests/ui/span/send-is-not-static-std-sync.stderr +++ b/tests/ui/span/send-is-not-static-std-sync.stderr @@ -14,8 +14,9 @@ LL | *lock.lock().unwrap() = &z; | help: consider cloning the value if the performance cost is acceptable | -LL | *lock.lock().unwrap() = &*y.clone(); - | ++++++++ +LL - *lock.lock().unwrap() = &*y; +LL + *lock.lock().unwrap() = y.clone(); + | error[E0597]: `z` does not live long enough --> $DIR/send-is-not-static-std-sync.rs:16:33 @@ -46,8 +47,9 @@ LL | *lock.write().unwrap() = &z; | help: consider cloning the value if the performance cost is acceptable | -LL | *lock.write().unwrap() = &*y.clone(); - | ++++++++ +LL - *lock.write().unwrap() = &*y; +LL + *lock.write().unwrap() = y.clone(); + | error[E0597]: `z` does not live long enough --> $DIR/send-is-not-static-std-sync.rs:30:34 @@ -78,8 +80,9 @@ LL | tx.send(&z).unwrap(); | help: consider cloning the value if the performance cost is acceptable | -LL | tx.send(&*y.clone()); - | ++++++++ +LL - tx.send(&*y); +LL + tx.send(y.clone()); + | error[E0597]: `z` does not live long enough --> $DIR/send-is-not-static-std-sync.rs:46:17 diff --git a/tests/ui/suggestions/borrow-for-loop-head.stderr b/tests/ui/suggestions/borrow-for-loop-head.stderr index 55fcb44168c49..a8de9986c312d 100644 --- a/tests/ui/suggestions/borrow-for-loop-head.stderr +++ b/tests/ui/suggestions/borrow-for-loop-head.stderr @@ -10,8 +10,9 @@ LL | for j in a { | help: consider cloning the value if the performance cost is acceptable | -LL | for i in &a.clone() { - | ++++++++ +LL - for i in &a { +LL + for i in a.clone() { + | error[E0382]: use of moved value: `a` --> $DIR/borrow-for-loop-head.rs:4:18 diff --git a/tests/ui/unop-move-semantics.stderr b/tests/ui/unop-move-semantics.stderr index a6549d84a8a09..3c887f25ae8fb 100644 --- a/tests/ui/unop-move-semantics.stderr +++ b/tests/ui/unop-move-semantics.stderr @@ -36,8 +36,9 @@ LL | use_mut(n); use_imm(m); | help: consider cloning the value if the performance cost is acceptable | -LL | let m = &x.clone(); - | ++++++++ +LL - let m = &x; +LL + let m = x.clone(); + | error[E0505]: cannot move out of `y` because it is borrowed --> $DIR/unop-move-semantics.rs:17:6 diff --git a/tests/ui/variance/variance-issue-20533.stderr b/tests/ui/variance/variance-issue-20533.stderr index 7fed947972f74..bee9b802135a5 100644 --- a/tests/ui/variance/variance-issue-20533.stderr +++ b/tests/ui/variance/variance-issue-20533.stderr @@ -12,8 +12,9 @@ LL | drop(x); | help: consider cloning the value if the performance cost is acceptable | -LL | let x = foo(&a.clone()); - | ++++++++ +LL - let x = foo(&a); +LL + let x = foo(a.clone()); + | error[E0505]: cannot move out of `a` because it is borrowed --> $DIR/variance-issue-20533.rs:34:14 @@ -29,8 +30,9 @@ LL | drop(x); | help: consider cloning the value if the performance cost is acceptable | -LL | let x = bar(&a.clone()); - | ++++++++ +LL - let x = bar(&a); +LL + let x = bar(a.clone()); + | error[E0505]: cannot move out of `a` because it is borrowed --> $DIR/variance-issue-20533.rs:40:14 @@ -46,8 +48,9 @@ LL | drop(x); | help: consider cloning the value if the performance cost is acceptable | -LL | let x = baz(&a.clone()); - | ++++++++ +LL - let x = baz(&a); +LL + let x = baz(a.clone()); + | error: aborting due to 3 previous errors From 10c2fbec2437a873463382726d4815520430ff1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 13 Mar 2024 03:41:41 +0000 Subject: [PATCH 05/19] Suggest `.clone()` in some move errors ``` error[E0507]: cannot move out of `*x` which is behind a shared reference --> $DIR/borrowck-fn-in-const-a.rs:6:16 | LL | return *x | ^^ move occurs because `*x` has type `String`, which does not implement the `Copy` trait | help: consider cloning the value if the performance cost is acceptable | LL - return *x LL + return x.clone() | ``` --- .../src/diagnostics/move_errors.rs | 10 ++ .../ui/borrowck/borrowck-fn-in-const-a.stderr | 6 ++ tests/ui/borrowck/borrowck-in-static.stderr | 5 + ...ck-move-out-of-overloaded-auto-deref.fixed | 2 +- ...k-move-out-of-overloaded-auto-deref.stderr | 4 + .../borrowck-struct-update-with-dtor.stderr | 5 + .../borrowck/clone-span-on-try-operator.fixed | 2 +- .../clone-span-on-try-operator.stderr | 5 + tests/ui/borrowck/issue-64453.stderr | 5 + .../ui/borrowck/move-error-in-promoted.stderr | 5 + .../move-from-union-field-issue-66500.stderr | 24 +++++ ...move-upvar-from-non-once-ref-closure.fixed | 2 +- ...ove-upvar-from-non-once-ref-closure.stderr | 4 + ...riving-with-repr-packed-move-errors.stderr | 96 +++++++++++++++++++ ...unctional-struct-update-noncopyable.stderr | 5 + tests/ui/issues/issue-2590.stderr | 5 + tests/ui/issues/issue-52262.stderr | 6 ++ ...-move-out-of-closure-env-issue-1965.stderr | 5 + .../ui/moves/needs-clone-through-deref.fixed | 18 ---- tests/ui/moves/needs-clone-through-deref.rs | 1 - .../ui/moves/needs-clone-through-deref.stderr | 6 +- tests/ui/moves/suggest-clone.fixed | 2 +- tests/ui/moves/suggest-clone.stderr | 4 + tests/ui/nll/issue-52086.stderr | 10 ++ ...e-52663-span-decl-captured-variable.stderr | 5 + .../rfc-reject-double-move-across-arms.stderr | 8 ++ ...rfc-reject-double-move-in-first-arm.stderr | 8 ++ ...borrowck-call-is-borrow-issue-12224.stderr | 5 + tests/ui/suggestions/for-i-in-vec.fixed | 6 +- tests/ui/suggestions/for-i-in-vec.stderr | 13 +++ .../ui/suggestions/option-content-move.fixed | 4 +- .../ui/suggestions/option-content-move.stderr | 8 ++ .../trivial-bounds-leak-copy.stderr | 6 ++ .../unboxed-closure-illegal-move.stderr | 20 ++++ tests/ui/union/union-move.stderr | 5 + 35 files changed, 296 insertions(+), 29 deletions(-) delete mode 100644 tests/ui/moves/needs-clone-through-deref.fixed diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 0d1b875cbed5b..3a1fbab452eb1 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -474,6 +474,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { Some(desc) => format!("`{desc}`"), None => "value".to_string(), }; + + if let Some(expr) = self.find_expr(span) { + self.suggest_cloning(err, place_ty, expr, span); + } + err.subdiagnostic( self.dcx(), crate::session_diagnostics::TypeNoCopy::Label { @@ -582,6 +587,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if binds_to.len() == 1 { let place_desc = &format!("`{}`", self.local_names[*local].unwrap()); + + if let Some(expr) = self.find_expr(binding_span) { + self.suggest_cloning(err, bind_to.ty, expr, binding_span); + } + err.subdiagnostic( self.dcx(), crate::session_diagnostics::TypeNoCopy::Label { diff --git a/tests/ui/borrowck/borrowck-fn-in-const-a.stderr b/tests/ui/borrowck/borrowck-fn-in-const-a.stderr index e05696864fdf8..7bf0f859fddf4 100644 --- a/tests/ui/borrowck/borrowck-fn-in-const-a.stderr +++ b/tests/ui/borrowck/borrowck-fn-in-const-a.stderr @@ -3,6 +3,12 @@ error[E0507]: cannot move out of `*x` which is behind a shared reference | LL | return *x | ^^ move occurs because `*x` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL - return *x +LL + return x.clone() + | error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-in-static.stderr b/tests/ui/borrowck/borrowck-in-static.stderr index 8171e6950ace5..745b02ae21b81 100644 --- a/tests/ui/borrowck/borrowck-in-static.stderr +++ b/tests/ui/borrowck/borrowck-in-static.stderr @@ -7,6 +7,11 @@ LL | Box::new(|| x) | -- ^ move occurs because `x` has type `Box`, which does not implement the `Copy` trait | | | captured by this `Fn` closure + | +help: consider cloning the value if the performance cost is acceptable + | +LL | Box::new(|| x.clone()) + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.fixed b/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.fixed index 8d5ebbc774408..a19db7e5cd32a 100644 --- a/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.fixed +++ b/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.fixed @@ -2,6 +2,6 @@ use std::rc::Rc; pub fn main() { - let _x = as Clone>::clone(&Rc::new(vec![1, 2])).into_iter(); + let _x = as Clone>::clone(&Rc::new(vec![1, 2]).clone()).into_iter(); //~^ ERROR [E0507] } diff --git a/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr b/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr index 076f0ce3440a0..577c2de38be01 100644 --- a/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr +++ b/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr @@ -12,6 +12,10 @@ help: you can `clone` the value and consume it, but this might not be your desir | LL | let _x = as Clone>::clone(&Rc::new(vec![1, 2])).into_iter(); | ++++++++++++++++++++++++++++ + +help: consider cloning the value if the performance cost is acceptable + | +LL | let _x = Rc::new(vec![1, 2]).clone().into_iter(); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr b/tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr index af32f27910076..01004fa56c684 100644 --- a/tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr +++ b/tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr @@ -15,6 +15,11 @@ LL | let _s2 = T{a: 2, ..s0}; | | | cannot move out of here | move occurs because `s0.mv` has type `Box`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let _s2 = T{a: 2, ..s0}.clone(); + | ++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/borrowck/clone-span-on-try-operator.fixed b/tests/ui/borrowck/clone-span-on-try-operator.fixed index 59253c98079b2..59a162e72c176 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; - ::clone(&(*foo)).foo(); //~ ERROR cannot move out + ::clone(&foo.clone()).foo(); //~ ERROR cannot move out } diff --git a/tests/ui/borrowck/clone-span-on-try-operator.stderr b/tests/ui/borrowck/clone-span-on-try-operator.stderr index adf84e49a9f8d..c2c63f9494362 100644 --- a/tests/ui/borrowck/clone-span-on-try-operator.stderr +++ b/tests/ui/borrowck/clone-span-on-try-operator.stderr @@ -15,6 +15,11 @@ help: you can `clone` the value and consume it, but this might not be your desir | LL | ::clone(&(*foo)).foo(); | +++++++++++++++++++++++ + +help: consider cloning the value if the performance cost is acceptable + | +LL - (*foo).foo(); +LL + foo.clone().foo(); + | error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/issue-64453.stderr b/tests/ui/borrowck/issue-64453.stderr index f032ea779dd48..0e4a8d42f6e84 100644 --- a/tests/ui/borrowck/issue-64453.stderr +++ b/tests/ui/borrowck/issue-64453.stderr @@ -22,6 +22,11 @@ error[E0507]: cannot move out of static item `settings_dir` | LL | let settings_data = from_string(settings_dir); | ^^^^^^^^^^^^ move occurs because `settings_dir` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let settings_data = from_string(settings_dir.clone()); + | ++++++++ error: aborting due to 3 previous errors diff --git a/tests/ui/borrowck/move-error-in-promoted.stderr b/tests/ui/borrowck/move-error-in-promoted.stderr index 03c0297c5a980..8d42df24e2772 100644 --- a/tests/ui/borrowck/move-error-in-promoted.stderr +++ b/tests/ui/borrowck/move-error-in-promoted.stderr @@ -6,6 +6,11 @@ LL | let _ = S1(C[0]).clone(); | | | cannot move out of here | move occurs because value has type `S2`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let _ = S1(C[0].clone()).clone(); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/move-from-union-field-issue-66500.stderr b/tests/ui/borrowck/move-from-union-field-issue-66500.stderr index 70078582713c6..c951ce8e3cd8f 100644 --- a/tests/ui/borrowck/move-from-union-field-issue-66500.stderr +++ b/tests/ui/borrowck/move-from-union-field-issue-66500.stderr @@ -3,24 +3,48 @@ error[E0507]: cannot move out of `*u.a` which is behind a shared reference | LL | *u.a | ^^^^ move occurs because `*u.a` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL - *u.a +LL + u.a.clone() + | error[E0507]: cannot move out of `*u.b` which is behind a mutable reference --> $DIR/move-from-union-field-issue-66500.rs:16:5 | LL | *u.b | ^^^^ move occurs because `*u.b` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL - *u.b +LL + u.b.clone() + | error[E0507]: cannot move out of `*u.c` which is behind a raw pointer --> $DIR/move-from-union-field-issue-66500.rs:20:5 | LL | *u.c | ^^^^ move occurs because `*u.c` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL - *u.c +LL + u.c.clone() + | error[E0507]: cannot move out of `*u.d` which is behind a raw pointer --> $DIR/move-from-union-field-issue-66500.rs:24:5 | LL | *u.d | ^^^^ move occurs because `*u.d` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL - *u.d +LL + u.d.clone() + | error: aborting due to 4 previous errors diff --git a/tests/ui/borrowck/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 8add3a5f2b6f5..3b4f7c8465cfa 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) where F : Fn() { fn main() { let y = vec![format!("World")]; call(|| { - as Clone>::clone(&y).into_iter(); + as Clone>::clone(&y.clone()).into_iter(); //~^ ERROR cannot move out of `y`, a captured variable in an `Fn` closure }); } diff --git a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr index a2ff70255f575..177e9c8d2487e 100644 --- a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr +++ b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr @@ -16,6 +16,10 @@ help: you can `clone` the value and consume it, but this might not be your desir | LL | as Clone>::clone(&y).into_iter(); | +++++++++++++++++++++++++++++++ + +help: consider cloning the value if the performance cost is acceptable + | +LL | y.clone().into_iter(); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/derives/deriving-with-repr-packed-move-errors.stderr b/tests/ui/derives/deriving-with-repr-packed-move-errors.stderr index c538061b365e2..2de4ee4eabd58 100644 --- a/tests/ui/derives/deriving-with-repr-packed-move-errors.stderr +++ b/tests/ui/derives/deriving-with-repr-packed-move-errors.stderr @@ -8,6 +8,10 @@ LL | struct StructA(String); | = note: `#[derive(Debug)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider cloning the value if the performance cost is acceptable + | +LL | struct StructA(String.clone()); + | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 @@ -19,6 +23,10 @@ LL | struct StructA(String); | = note: `#[derive(PartialEq)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider cloning the value if the performance cost is acceptable + | +LL | struct StructA(String.clone()); + | ++++++++ error[E0507]: cannot move out of `other` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 @@ -30,6 +38,10 @@ LL | struct StructA(String); | = note: `#[derive(PartialEq)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider cloning the value if the performance cost is acceptable + | +LL | struct StructA(String.clone()); + | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 @@ -41,6 +53,10 @@ LL | struct StructA(String); | = note: `#[derive(PartialOrd)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider cloning the value if the performance cost is acceptable + | +LL | struct StructA(String.clone()); + | ++++++++ error[E0507]: cannot move out of `other` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 @@ -52,6 +68,10 @@ LL | struct StructA(String); | = note: `#[derive(PartialOrd)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour = note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider cloning the value if the performance cost is acceptable + | +LL | struct StructA(String.clone()); + | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 @@ -63,6 +83,10 @@ LL | struct StructA(String); | = note: `#[derive(Ord)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider cloning the value if the performance cost is acceptable + | +LL | struct StructA(String.clone()); + | ++++++++ error[E0507]: cannot move out of `other` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 @@ -74,6 +98,10 @@ LL | struct StructA(String); | = note: `#[derive(Ord)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour = note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider cloning the value if the performance cost is acceptable + | +LL | struct StructA(String.clone()); + | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 @@ -85,6 +113,10 @@ LL | struct StructA(String); | = note: `#[derive(Hash)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider cloning the value if the performance cost is acceptable + | +LL | struct StructA(String.clone()); + | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 @@ -96,78 +128,142 @@ LL | struct StructA(String); | = note: `#[derive(Clone)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider cloning the value if the performance cost is acceptable + | +LL | struct StructA(String.clone()); + | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:28:9 | LL | self.0 | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | self.0.clone() + | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:38:20 | LL | let x = &{ self.0 }; | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let x = &{ self.0.clone() }; + | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:45:12 | LL | ({ self.0 }) == ({ other.0 }) | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | ({ self.0.clone() }) == ({ other.0 }) + | ++++++++ error[E0507]: cannot move out of `other` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:45:28 | LL | ({ self.0 }) == ({ other.0 }) | ^^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | ({ self.0 }) == ({ other.0.clone() }) + | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:53:36 | LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 }) | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | PartialOrd::partial_cmp(&{ self.0.clone() }, &{ other.0 }) + | ++++++++ error[E0507]: cannot move out of `other` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:53:49 | LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 }) | ^^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0.clone() }) + | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:68:20 | LL | let x = &{ self.0 }; | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let x = &{ self.0.clone() }; + | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:75:12 | LL | ({ self.0 }) == ({ other.0 }) | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | ({ self.0.clone() }) == ({ other.0 }) + | ++++++++ error[E0507]: cannot move out of `other` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:75:28 | LL | ({ self.0 }) == ({ other.0 }) | ^^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | ({ self.0 }) == ({ other.0.clone() }) + | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:83:36 | LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 }) | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | PartialOrd::partial_cmp(&{ self.0.clone() }, &{ other.0 }) + | ++++++++ error[E0507]: cannot move out of `other` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:83:49 | LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 }) | ^^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0.clone() }) + | ++++++++ error[E0507]: cannot move out of `arg` which is behind a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:92:5 | LL | arg.0 | ^^^^^ move occurs because `arg.0` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | arg.0.clone() + | ++++++++ error: aborting due to 21 previous errors diff --git a/tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr b/tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr index 16808f29daca1..4d314f4f7c3c2 100644 --- a/tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr +++ b/tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr @@ -6,6 +6,11 @@ LL | let _b = A { y: Arc::new(3), ..a }; | | | cannot move out of here | move occurs because `a.x` has type `Arc`, which does not implement the `Copy` trait + | +help: clone the value to increment its reference count + | +LL | let _b = A { y: Arc::new(3), ..a }.clone(); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-2590.stderr b/tests/ui/issues/issue-2590.stderr index 517b4814eaea5..822856652e9df 100644 --- a/tests/ui/issues/issue-2590.stderr +++ b/tests/ui/issues/issue-2590.stderr @@ -3,6 +3,11 @@ error[E0507]: cannot move out of `self.tokens` which is behind a shared referenc | LL | self.tokens | ^^^^^^^^^^^ move occurs because `self.tokens` has type `Vec`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | self.tokens.clone() + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-52262.stderr b/tests/ui/issues/issue-52262.stderr index ce8e6fe2bf8dd..51959f22b97a4 100644 --- a/tests/ui/issues/issue-52262.stderr +++ b/tests/ui/issues/issue-52262.stderr @@ -3,6 +3,12 @@ error[E0507]: cannot move out of `*key` which is behind a shared reference | LL | String::from_utf8(*key).unwrap() | ^^^^ move occurs because `*key` has type `Vec`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL - String::from_utf8(*key).unwrap() +LL + String::from_utf8(key.clone()).unwrap() + | error: aborting due to 1 previous error diff --git a/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr b/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr index 513631b20604c..523134a9425f4 100644 --- a/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr +++ b/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr @@ -7,6 +7,11 @@ LL | let _f = to_fn(|| test(i)); | -- ^ move occurs because `i` has type `Box`, which does not implement the `Copy` trait | | | captured by this `Fn` closure + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let _f = to_fn(|| test(i.clone())); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/moves/needs-clone-through-deref.fixed b/tests/ui/moves/needs-clone-through-deref.fixed deleted file mode 100644 index 8b201c4720dd6..0000000000000 --- a/tests/ui/moves/needs-clone-through-deref.fixed +++ /dev/null @@ -1,18 +0,0 @@ -//@ run-rustfix -#![allow(dead_code, noop_method_call)] -use std::ops::Deref; -struct S(Vec); -impl Deref for S { - type Target = Vec; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl S { - fn foo(&self) { - // `self.clone()` returns `&S`, not `Vec` - for _ in as Clone>::clone(&self).into_iter() {} //~ ERROR cannot move out of dereference of `S` - } -} -fn main() {} diff --git a/tests/ui/moves/needs-clone-through-deref.rs b/tests/ui/moves/needs-clone-through-deref.rs index ca57478ba98e3..245b7c167fa46 100644 --- a/tests/ui/moves/needs-clone-through-deref.rs +++ b/tests/ui/moves/needs-clone-through-deref.rs @@ -1,4 +1,3 @@ -//@ run-rustfix #![allow(dead_code, noop_method_call)] use std::ops::Deref; struct S(Vec); diff --git a/tests/ui/moves/needs-clone-through-deref.stderr b/tests/ui/moves/needs-clone-through-deref.stderr index 1f9aefeb4dd7a..8357ed9ff7931 100644 --- a/tests/ui/moves/needs-clone-through-deref.stderr +++ b/tests/ui/moves/needs-clone-through-deref.stderr @@ -1,5 +1,5 @@ error[E0507]: cannot move out of dereference of `S` - --> $DIR/needs-clone-through-deref.rs:15:18 + --> $DIR/needs-clone-through-deref.rs:14:18 | LL | for _ in self.clone().into_iter() {} | ^^^^^^^^^^^^ ----------- value moved due to this method call @@ -12,6 +12,10 @@ help: you can `clone` the value and consume it, but this might not be your desir | LL | for _ in as Clone>::clone(&self).into_iter() {} | ++++++++++++++++++++++++++++++ ~ +help: consider cloning the value if the performance cost is acceptable + | +LL | for _ in self.clone().clone().into_iter() {} + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/moves/suggest-clone.fixed b/tests/ui/moves/suggest-clone.fixed index 59276a7b96d4c..59a162e72c176 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; - ::clone(&foo).foo(); //~ ERROR cannot move out + ::clone(&foo.clone()).foo(); //~ ERROR cannot move out } diff --git a/tests/ui/moves/suggest-clone.stderr b/tests/ui/moves/suggest-clone.stderr index 25e89a5895531..f8e0ccdfceff0 100644 --- a/tests/ui/moves/suggest-clone.stderr +++ b/tests/ui/moves/suggest-clone.stderr @@ -15,6 +15,10 @@ help: you can `clone` the value and consume it, but this might not be your desir | LL | ::clone(&foo).foo(); | +++++++++++++++++++++++ + +help: consider cloning the value if the performance cost is acceptable + | +LL | foo.clone().foo(); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/nll/issue-52086.stderr b/tests/ui/nll/issue-52086.stderr index 3b2dae9b72bb0..e4f0c49e557da 100644 --- a/tests/ui/nll/issue-52086.stderr +++ b/tests/ui/nll/issue-52086.stderr @@ -3,12 +3,22 @@ error[E0507]: cannot move out of an `Rc` | LL | drop(x.field); | ^^^^^^^ move occurs because value has type `Vec`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | drop(x.field.clone()); + | ++++++++ error[E0507]: cannot move out of an `Arc` --> $DIR/issue-52086.rs:12:10 | LL | drop(y.field); | ^^^^^^^ move occurs because value has type `Vec`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | drop(y.field.clone()); + | ++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr b/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr index 587f307102766..fbaec8a6008a1 100644 --- a/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr +++ b/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr @@ -7,6 +7,11 @@ LL | expect_fn(|| drop(x.0)); | -- ^^^ move occurs because `x.0` has type `Vec`, which does not implement the `Copy` trait | | | captured by this `Fn` closure + | +help: consider cloning the value if the performance cost is acceptable + | +LL | expect_fn(|| drop(x.0.clone())); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr index a749361bf30ee..0ab70c5ae8ab2 100644 --- a/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr +++ b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr @@ -5,6 +5,10 @@ LL | VecWrapper::A(v) if { drop(v); false } => 1, | ^ move occurs because `v` has type `Vec`, which does not implement the `Copy` trait | = note: variables bound in patterns cannot be moved from until after the end of the pattern guard +help: consider cloning the value if the performance cost is acceptable + | +LL | VecWrapper::A(v) if { drop(v.clone()); false } => 1, + | ++++++++ error[E0507]: cannot move out of `v` in pattern guard --> $DIR/rfc-reject-double-move-across-arms.rs:15:51 @@ -13,6 +17,10 @@ LL | VecWrapper::A(v) if let Some(()) = { drop(v); None } => 1, | ^ move occurs because `v` has type `Vec`, which does not implement the `Copy` trait | = note: variables bound in patterns cannot be moved from until after the end of the pattern guard +help: consider cloning the value if the performance cost is acceptable + | +LL | VecWrapper::A(v) if let Some(()) = { drop(v.clone()); None } => 1, + | ++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr index 9285492b22450..c261f9942838a 100644 --- a/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr +++ b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr @@ -5,6 +5,10 @@ LL | A { a: v } if { drop(v); true } => v, | ^ move occurs because `v` has type `Box`, which does not implement the `Copy` trait | = note: variables bound in patterns cannot be moved from until after the end of the pattern guard +help: consider cloning the value if the performance cost is acceptable + | +LL | A { a: v } if { drop(v.clone()); true } => v, + | ++++++++ error[E0507]: cannot move out of `v` in pattern guard --> $DIR/rfc-reject-double-move-in-first-arm.rs:17:45 @@ -13,6 +17,10 @@ LL | A { a: v } if let Some(()) = { drop(v); Some(()) } => v, | ^ move occurs because `v` has type `Box`, which does not implement the `Copy` trait | = note: variables bound in patterns cannot be moved from until after the end of the pattern guard +help: consider cloning the value if the performance cost is acceptable + | +LL | A { a: v } if let Some(()) = { drop(v.clone()); Some(()) } => v, + | ++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr b/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr index bac7801f7f430..f37dc320fa315 100644 --- a/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr +++ b/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr @@ -43,6 +43,11 @@ LL | f(Box::new(|a| { LL | LL | foo(f); | ^ move occurs because `f` has type `{closure@$DIR/borrowck-call-is-borrow-issue-12224.rs:52:17: 52:58}`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | foo(f.clone()); + | ++++++++ error[E0505]: cannot move out of `f` because it is borrowed --> $DIR/borrowck-call-is-borrow-issue-12224.rs:55:16 diff --git a/tests/ui/suggestions/for-i-in-vec.fixed b/tests/ui/suggestions/for-i-in-vec.fixed index f266e80bcfa64..17ae6fb5ab706 100644 --- a/tests/ui/suggestions/for-i-in-vec.fixed +++ b/tests/ui/suggestions/for-i-in-vec.fixed @@ -8,9 +8,9 @@ struct Foo { impl Foo { fn bar(&self) { - for _ in &self.v { //~ ERROR cannot move out of `self.v` which is behind a shared reference + for _ in &self.v.clone() { //~ ERROR cannot move out of `self.v` which is behind a shared reference } - for _ in &self.h { //~ ERROR cannot move out of `self.h` which is behind a shared reference + for _ in &self.h.clone() { //~ ERROR cannot move out of `self.h` which is behind a shared reference } } } @@ -18,7 +18,7 @@ impl Foo { const LOADERS: &Vec<&'static u8> = &Vec::new(); pub fn break_code() -> Option<&'static u8> { - for loader in &*LOADERS { //~ ERROR cannot move out of a shared reference + for loader in &LOADERS.clone() { //~ ERROR cannot move out of a shared reference return Some(loader); } None diff --git a/tests/ui/suggestions/for-i-in-vec.stderr b/tests/ui/suggestions/for-i-in-vec.stderr index c5b81e6b87174..64eb4f8bd23ba 100644 --- a/tests/ui/suggestions/for-i-in-vec.stderr +++ b/tests/ui/suggestions/for-i-in-vec.stderr @@ -13,6 +13,10 @@ help: consider iterating over a slice of the `Vec`'s content to avoid movin | LL | for _ in &self.v { | + +help: consider cloning the value if the performance cost is acceptable + | +LL | for _ in self.v.clone() { + | ++++++++ error[E0507]: cannot move out of `self.h` which is behind a shared reference --> $DIR/for-i-in-vec.rs:13:18 @@ -27,6 +31,10 @@ help: consider iterating over a slice of the `HashMap`'s content to av | LL | for _ in &self.h { | + +help: consider cloning the value if the performance cost is acceptable + | +LL | for _ in self.h.clone() { + | ++++++++ error[E0507]: cannot move out of a shared reference --> $DIR/for-i-in-vec.rs:21:19 @@ -43,6 +51,11 @@ help: consider iterating over a slice of the `Vec<&u8>`'s content to avoid movin | LL | for loader in &*LOADERS { | + +help: consider cloning the value if the performance cost is acceptable + | +LL - for loader in *LOADERS { +LL + for loader in LOADERS.clone() { + | error: aborting due to 3 previous errors diff --git a/tests/ui/suggestions/option-content-move.fixed b/tests/ui/suggestions/option-content-move.fixed index fbed486cef7ca..4a5a9483c20c8 100644 --- a/tests/ui/suggestions/option-content-move.fixed +++ b/tests/ui/suggestions/option-content-move.fixed @@ -7,7 +7,7 @@ impl LipogramCorpora { pub fn validate_all(&mut self) -> Result<(), char> { for selection in &self.selections { if selection.1.is_some() { - if as Clone>::clone(&selection.1).unwrap().contains(selection.0) { + if as Clone>::clone(&selection.1.clone()).unwrap().contains(selection.0) { //~^ ERROR cannot move out of `selection.1` return Err(selection.0); } @@ -25,7 +25,7 @@ impl LipogramCorpora2 { pub fn validate_all(&mut self) -> Result<(), char> { for selection in &self.selections { if selection.1.is_ok() { - if as Clone>::clone(&selection.1).unwrap().contains(selection.0) { + if as Clone>::clone(&selection.1.clone()).unwrap().contains(selection.0) { //~^ ERROR cannot move out of `selection.1` return Err(selection.0); } diff --git a/tests/ui/suggestions/option-content-move.stderr b/tests/ui/suggestions/option-content-move.stderr index e5de150275dbb..a382a04344aeb 100644 --- a/tests/ui/suggestions/option-content-move.stderr +++ b/tests/ui/suggestions/option-content-move.stderr @@ -13,6 +13,10 @@ help: you can `clone` the value and consume it, but this might not be your desir | LL | if as Clone>::clone(&selection.1).unwrap().contains(selection.0) { | ++++++++++++++++++++++++++++++++++ + +help: consider cloning the value if the performance cost is acceptable + | +LL | if selection.1.clone().unwrap().contains(selection.0) { + | ++++++++ error[E0507]: cannot move out of `selection.1` which is behind a shared reference --> $DIR/option-content-move.rs:28:20 @@ -29,6 +33,10 @@ help: you can `clone` the value and consume it, but this might not be your desir | LL | if as Clone>::clone(&selection.1).unwrap().contains(selection.0) { | ++++++++++++++++++++++++++++++++++++++++++ + +help: consider cloning the value if the performance cost is acceptable + | +LL | if selection.1.clone().unwrap().contains(selection.0) { + | ++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/trivial-bounds/trivial-bounds-leak-copy.stderr b/tests/ui/trivial-bounds/trivial-bounds-leak-copy.stderr index e48d48a7271d1..4efb883ac740e 100644 --- a/tests/ui/trivial-bounds/trivial-bounds-leak-copy.stderr +++ b/tests/ui/trivial-bounds/trivial-bounds-leak-copy.stderr @@ -3,6 +3,12 @@ error[E0507]: cannot move out of `*t` which is behind a shared reference | LL | *t | ^^ move occurs because `*t` has type `String`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL - *t +LL + t.clone() + | error: aborting due to 1 previous error diff --git a/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr b/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr index bfa3061de0815..5b995ff158536 100644 --- a/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr +++ b/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr @@ -7,6 +7,11 @@ LL | let f = to_fn(|| drop(x)); | -- ^ move occurs because `x` has type `Box`, which does not implement the `Copy` trait | | | captured by this `Fn` closure + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let f = to_fn(|| drop(x.clone())); + | ++++++++ error[E0507]: cannot move out of `x`, a captured variable in an `FnMut` closure --> $DIR/unboxed-closure-illegal-move.rs:19:35 @@ -17,6 +22,11 @@ LL | let f = to_fn_mut(|| drop(x)); | -- ^ move occurs because `x` has type `Box`, which does not implement the `Copy` trait | | | captured by this `FnMut` closure + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let f = to_fn_mut(|| drop(x.clone())); + | ++++++++ error[E0507]: cannot move out of `x`, a captured variable in an `Fn` closure --> $DIR/unboxed-closure-illegal-move.rs:28:36 @@ -27,6 +37,11 @@ LL | let f = to_fn(move || drop(x)); | ------- ^ move occurs because `x` has type `Box`, which does not implement the `Copy` trait | | | captured by this `Fn` closure + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let f = to_fn(move || drop(x.clone())); + | ++++++++ error[E0507]: cannot move out of `x`, a captured variable in an `FnMut` closure --> $DIR/unboxed-closure-illegal-move.rs:32:40 @@ -37,6 +52,11 @@ LL | let f = to_fn_mut(move || drop(x)); | ------- ^ move occurs because `x` has type `Box`, which does not implement the `Copy` trait | | | captured by this `FnMut` closure + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let f = to_fn_mut(move || drop(x.clone())); + | ++++++++ error: aborting due to 4 previous errors diff --git a/tests/ui/union/union-move.stderr b/tests/ui/union/union-move.stderr index 47fb801a50ecd..7d13094fbed98 100644 --- a/tests/ui/union/union-move.stderr +++ b/tests/ui/union/union-move.stderr @@ -44,6 +44,11 @@ LL | move_out(x.f1_nocopy); | | | cannot move out of here | move occurs because `x.f1_nocopy` has type `ManuallyDrop>`, which does not implement the `Copy` trait + | +help: consider cloning the value if the performance cost is acceptable + | +LL | move_out(x.f1_nocopy.clone()); + | ++++++++ error: aborting due to 3 previous errors From 065454dd1da7a759cd414468992b2afdfcf477b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 13 Mar 2024 05:26:03 +0000 Subject: [PATCH 06/19] More move error suggestions to clone ``` error[E0507]: cannot move out of `val`, a captured variable in an `FnMut` closure --> $DIR/issue-87456-point-to-closure.rs:10:28 | LL | let val = String::new(); | --- captured outer variable LL | LL | take_mut(|| { | -- captured by this `FnMut` closure LL | LL | let _foo: String = val; | ^^^ move occurs because `val` has type `String`, which does not implement the `Copy` trait | help: consider borrowing here | LL | let _foo: String = &val; | + help: consider cloning the value if the performance cost is acceptable | LL | let _foo: String = val.clone(); | ++++++++ ``` --- .../src/diagnostics/move_errors.rs | 4 ++ tests/ui/borrowck/borrowck-issue-2657-2.fixed | 2 +- .../ui/borrowck/borrowck-issue-2657-2.stderr | 5 +++ .../borrowck-move-from-unsafe-ptr.stderr | 5 +++ ...rrowck-move-out-of-overloaded-deref.stderr | 5 +++ ...owck-overloaded-index-move-from-vec.stderr | 4 ++ .../borrowck/borrowck-vec-pattern-nesting.rs | 3 ++ .../borrowck-vec-pattern-nesting.stderr | 20 +++++++-- .../issue-87456-point-to-closure.stderr | 4 ++ .../ui/check-static-values-constraints.stderr | 4 ++ .../issue-40402-1.stderr | 4 ++ tests/ui/nll/cannot-move-block-spans.stderr | 42 +++++++++++++++++++ 12 files changed, 97 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 3a1fbab452eb1..17e43a5b39ce3 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -444,6 +444,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { None => "value".to_string(), }; + if let Some(expr) = self.find_expr(span) { + self.suggest_cloning(err, place_ty, expr, span); + } + err.subdiagnostic( self.dcx(), crate::session_diagnostics::TypeNoCopy::Label { diff --git a/tests/ui/borrowck/borrowck-issue-2657-2.fixed b/tests/ui/borrowck/borrowck-issue-2657-2.fixed index e5aaf7d2de740..e532aa3e68cde 100644 --- a/tests/ui/borrowck/borrowck-issue-2657-2.fixed +++ b/tests/ui/borrowck/borrowck-issue-2657-2.fixed @@ -5,7 +5,7 @@ fn main() { match x { Some(ref y) => { - let _b = y; //~ ERROR cannot move out + let _b = y.clone(); //~ ERROR cannot move out } _ => {} } diff --git a/tests/ui/borrowck/borrowck-issue-2657-2.stderr b/tests/ui/borrowck/borrowck-issue-2657-2.stderr index 6fab19000fcb7..16186792b9357 100644 --- a/tests/ui/borrowck/borrowck-issue-2657-2.stderr +++ b/tests/ui/borrowck/borrowck-issue-2657-2.stderr @@ -9,6 +9,11 @@ help: consider removing the dereference here LL - let _b = *y; LL + let _b = y; | +help: consider cloning the value if the performance cost is acceptable + | +LL - let _b = *y; +LL + let _b = y.clone(); + | error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-move-from-unsafe-ptr.stderr b/tests/ui/borrowck/borrowck-move-from-unsafe-ptr.stderr index 7213f85ad98fc..ebc3b6ebcacda 100644 --- a/tests/ui/borrowck/borrowck-move-from-unsafe-ptr.stderr +++ b/tests/ui/borrowck/borrowck-move-from-unsafe-ptr.stderr @@ -9,6 +9,11 @@ help: consider removing the dereference here LL - let y = *x; LL + let y = x; | +help: consider cloning the value if the performance cost is acceptable + | +LL - let y = *x; +LL + let y = x.clone(); + | error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-move-out-of-overloaded-deref.stderr b/tests/ui/borrowck/borrowck-move-out-of-overloaded-deref.stderr index dce1f4d0775e6..774aac3ec9c38 100644 --- a/tests/ui/borrowck/borrowck-move-out-of-overloaded-deref.stderr +++ b/tests/ui/borrowck/borrowck-move-out-of-overloaded-deref.stderr @@ -9,6 +9,11 @@ help: consider removing the dereference here LL - let _x = *Rc::new("hi".to_string()); LL + let _x = Rc::new("hi".to_string()); | +help: consider cloning the value if the performance cost is acceptable + | +LL - let _x = *Rc::new("hi".to_string()); +LL + let _x = Rc::new("hi".to_string()).clone(); + | error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-overloaded-index-move-from-vec.stderr b/tests/ui/borrowck/borrowck-overloaded-index-move-from-vec.stderr index b4106702cd1a8..3e874ed1a2f6e 100644 --- a/tests/ui/borrowck/borrowck-overloaded-index-move-from-vec.stderr +++ b/tests/ui/borrowck/borrowck-overloaded-index-move-from-vec.stderr @@ -8,6 +8,10 @@ help: consider borrowing here | LL | let bad = &v[0]; | + +help: consider cloning the value if the performance cost is acceptable + | +LL | let bad = v[0].clone(); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-vec-pattern-nesting.rs b/tests/ui/borrowck/borrowck-vec-pattern-nesting.rs index 1bda7a4971375..ec074d2cf1c9a 100644 --- a/tests/ui/borrowck/borrowck-vec-pattern-nesting.rs +++ b/tests/ui/borrowck/borrowck-vec-pattern-nesting.rs @@ -47,6 +47,7 @@ fn c() { //~| NOTE cannot move out of here //~| NOTE move occurs because //~| HELP consider borrowing here + //~| HELP consider cloning } fn d() { @@ -66,6 +67,7 @@ fn d() { //~| NOTE cannot move out of here //~| NOTE move occurs because //~| HELP consider borrowing here + //~| HELP consider cloning } fn e() { @@ -86,6 +88,7 @@ fn e() { //~| NOTE cannot move out of here //~| NOTE move occurs because //~| HELP consider borrowing here + //~| HELP consider cloning } fn main() {} diff --git a/tests/ui/borrowck/borrowck-vec-pattern-nesting.stderr b/tests/ui/borrowck/borrowck-vec-pattern-nesting.stderr index 024cb006c2613..fff997fd5559f 100644 --- a/tests/ui/borrowck/borrowck-vec-pattern-nesting.stderr +++ b/tests/ui/borrowck/borrowck-vec-pattern-nesting.stderr @@ -53,9 +53,13 @@ help: consider borrowing here | LL | let a = &vec[0]; | + +help: consider cloning the value if the performance cost is acceptable + | +LL | let a = vec[0].clone(); + | ++++++++ error[E0508]: cannot move out of type `[Box]`, a non-copy slice - --> $DIR/borrowck-vec-pattern-nesting.rs:55:11 + --> $DIR/borrowck-vec-pattern-nesting.rs:56:11 | LL | match vec { | ^^^ cannot move out of here @@ -73,7 +77,7 @@ LL + [ | error[E0508]: cannot move out of type `[Box]`, a non-copy slice - --> $DIR/borrowck-vec-pattern-nesting.rs:65:13 + --> $DIR/borrowck-vec-pattern-nesting.rs:66:13 | LL | let a = vec[0]; | ^^^^^^ @@ -85,9 +89,13 @@ help: consider borrowing here | LL | let a = &vec[0]; | + +help: consider cloning the value if the performance cost is acceptable + | +LL | let a = vec[0].clone(); + | ++++++++ error[E0508]: cannot move out of type `[Box]`, a non-copy slice - --> $DIR/borrowck-vec-pattern-nesting.rs:74:11 + --> $DIR/borrowck-vec-pattern-nesting.rs:76:11 | LL | match vec { | ^^^ cannot move out of here @@ -106,7 +114,7 @@ LL + [_a, _b, _c] => {} | error[E0508]: cannot move out of type `[Box]`, a non-copy slice - --> $DIR/borrowck-vec-pattern-nesting.rs:85:13 + --> $DIR/borrowck-vec-pattern-nesting.rs:87:13 | LL | let a = vec[0]; | ^^^^^^ @@ -118,6 +126,10 @@ help: consider borrowing here | LL | let a = &vec[0]; | + +help: consider cloning the value if the performance cost is acceptable + | +LL | let a = vec[0].clone(); + | ++++++++ error: aborting due to 8 previous errors diff --git a/tests/ui/borrowck/issue-87456-point-to-closure.stderr b/tests/ui/borrowck/issue-87456-point-to-closure.stderr index a15909df07bf7..a0c7cac2addd0 100644 --- a/tests/ui/borrowck/issue-87456-point-to-closure.stderr +++ b/tests/ui/borrowck/issue-87456-point-to-closure.stderr @@ -14,6 +14,10 @@ help: consider borrowing here | LL | let _foo: String = &val; | + +help: consider cloning the value if the performance cost is acceptable + | +LL | let _foo: String = val.clone(); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/check-static-values-constraints.stderr b/tests/ui/check-static-values-constraints.stderr index dee1f2b1210a5..fe5f2a34272dd 100644 --- a/tests/ui/check-static-values-constraints.stderr +++ b/tests/ui/check-static-values-constraints.stderr @@ -160,6 +160,10 @@ help: consider borrowing here | LL | &x | + +help: consider cloning the value if the performance cost is acceptable + | +LL | x.clone() + | ++++++++ error: aborting due to 17 previous errors diff --git a/tests/ui/issues/issue-40402-ref-hints/issue-40402-1.stderr b/tests/ui/issues/issue-40402-ref-hints/issue-40402-1.stderr index 7976d09054201..d27b6e6324fe7 100644 --- a/tests/ui/issues/issue-40402-ref-hints/issue-40402-1.stderr +++ b/tests/ui/issues/issue-40402-ref-hints/issue-40402-1.stderr @@ -8,6 +8,10 @@ help: consider borrowing here | LL | let e = &f.v[0]; | + +help: consider cloning the value if the performance cost is acceptable + | +LL | let e = f.v[0].clone(); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/nll/cannot-move-block-spans.stderr b/tests/ui/nll/cannot-move-block-spans.stderr index 0dc5c08ea5f08..d96773e1edfde 100644 --- a/tests/ui/nll/cannot-move-block-spans.stderr +++ b/tests/ui/nll/cannot-move-block-spans.stderr @@ -9,6 +9,11 @@ help: consider removing the dereference here LL - let x = { *r }; LL + let x = { r }; | +help: consider cloning the value if the performance cost is acceptable + | +LL - let x = { *r }; +LL + let x = { r.clone() }; + | error[E0507]: cannot move out of `*r` which is behind a shared reference --> $DIR/cannot-move-block-spans.rs:6:22 @@ -21,6 +26,11 @@ help: consider removing the dereference here LL - let y = unsafe { *r }; LL + let y = unsafe { r }; | +help: consider cloning the value if the performance cost is acceptable + | +LL - let y = unsafe { *r }; +LL + let y = unsafe { r.clone() }; + | error[E0507]: cannot move out of `*r` which is behind a shared reference --> $DIR/cannot-move-block-spans.rs:7:26 @@ -33,6 +43,11 @@ help: consider removing the dereference here LL - let z = loop { break *r; }; LL + let z = loop { break r; }; | +help: consider cloning the value if the performance cost is acceptable + | +LL - let z = loop { break *r; }; +LL + let z = loop { break r.clone(); }; + | error[E0508]: cannot move out of type `[String; 2]`, a non-copy array --> $DIR/cannot-move-block-spans.rs:11:15 @@ -47,6 +62,10 @@ help: consider borrowing here | LL | let x = { &arr[0] }; | + +help: consider cloning the value if the performance cost is acceptable + | +LL | let x = { arr[0].clone() }; + | ++++++++ error[E0508]: cannot move out of type `[String; 2]`, a non-copy array --> $DIR/cannot-move-block-spans.rs:12:22 @@ -61,6 +80,10 @@ help: consider borrowing here | LL | let y = unsafe { &arr[0] }; | + +help: consider cloning the value if the performance cost is acceptable + | +LL | let y = unsafe { arr[0].clone() }; + | ++++++++ error[E0508]: cannot move out of type `[String; 2]`, a non-copy array --> $DIR/cannot-move-block-spans.rs:13:26 @@ -75,6 +98,10 @@ help: consider borrowing here | LL | let z = loop { break &arr[0]; }; | + +help: consider cloning the value if the performance cost is acceptable + | +LL | let z = loop { break arr[0].clone(); }; + | ++++++++ error[E0507]: cannot move out of `*r` which is behind a shared reference --> $DIR/cannot-move-block-spans.rs:17:38 @@ -87,6 +114,11 @@ help: consider removing the dereference here LL - let x = { let mut u = 0; u += 1; *r }; LL + let x = { let mut u = 0; u += 1; r }; | +help: consider cloning the value if the performance cost is acceptable + | +LL - let x = { let mut u = 0; u += 1; *r }; +LL + let x = { let mut u = 0; u += 1; r.clone() }; + | error[E0507]: cannot move out of `*r` which is behind a shared reference --> $DIR/cannot-move-block-spans.rs:18:45 @@ -99,6 +131,11 @@ help: consider removing the dereference here LL - let y = unsafe { let mut u = 0; u += 1; *r }; LL + let y = unsafe { let mut u = 0; u += 1; r }; | +help: consider cloning the value if the performance cost is acceptable + | +LL - let y = unsafe { let mut u = 0; u += 1; *r }; +LL + let y = unsafe { let mut u = 0; u += 1; r.clone() }; + | error[E0507]: cannot move out of `*r` which is behind a shared reference --> $DIR/cannot-move-block-spans.rs:19:49 @@ -111,6 +148,11 @@ help: consider removing the dereference here LL - let z = loop { let mut u = 0; u += 1; break *r; u += 2; }; LL + let z = loop { let mut u = 0; u += 1; break r; u += 2; }; | +help: consider cloning the value if the performance cost is acceptable + | +LL - let z = loop { let mut u = 0; u += 1; break *r; u += 2; }; +LL + let z = loop { let mut u = 0; u += 1; break r.clone(); u += 2; }; + | error: aborting due to 9 previous errors From 01b810e0523806445273e6aaeb26d1c77c66556c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 13 Mar 2024 05:46:53 +0000 Subject: [PATCH 07/19] Silence redundant clone suggestion --- .../src/diagnostics/conflict_errors.rs | 5 +++++ tests/ui/moves/needs-clone-through-deref.fixed | 18 ++++++++++++++++++ tests/ui/moves/needs-clone-through-deref.rs | 1 + .../ui/moves/needs-clone-through-deref.stderr | 6 +----- 4 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 tests/ui/moves/needs-clone-through-deref.fixed diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 4bbeadbae705d..3bc9f39d0f858 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1027,6 +1027,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { span: Span, ) { let tcx = self.infcx.tcx; + if let Some(_) = self.clone_on_reference(expr) { + // Avoid redundant clone suggestion already suggested in `explain_captures`. + // See `tests/ui/moves/needs-clone-through-deref.rs` + return; + } // Try to find predicates on *generic params* that would allow copying `ty` let suggestion = if let Some(symbol) = tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) { 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 0000000000000..8b201c4720dd6 --- /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); +impl Deref for S { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl S { + fn foo(&self) { + // `self.clone()` returns `&S`, not `Vec` + for _ in as Clone>::clone(&self).into_iter() {} //~ ERROR cannot move out of dereference of `S` + } +} +fn main() {} diff --git a/tests/ui/moves/needs-clone-through-deref.rs b/tests/ui/moves/needs-clone-through-deref.rs index 245b7c167fa46..ca57478ba98e3 100644 --- a/tests/ui/moves/needs-clone-through-deref.rs +++ b/tests/ui/moves/needs-clone-through-deref.rs @@ -1,3 +1,4 @@ +//@ run-rustfix #![allow(dead_code, noop_method_call)] use std::ops::Deref; struct S(Vec); diff --git a/tests/ui/moves/needs-clone-through-deref.stderr b/tests/ui/moves/needs-clone-through-deref.stderr index 8357ed9ff7931..1f9aefeb4dd7a 100644 --- a/tests/ui/moves/needs-clone-through-deref.stderr +++ b/tests/ui/moves/needs-clone-through-deref.stderr @@ -1,5 +1,5 @@ error[E0507]: cannot move out of dereference of `S` - --> $DIR/needs-clone-through-deref.rs:14:18 + --> $DIR/needs-clone-through-deref.rs:15:18 | LL | for _ in self.clone().into_iter() {} | ^^^^^^^^^^^^ ----------- value moved due to this method call @@ -12,10 +12,6 @@ help: you can `clone` the value and consume it, but this might not be your desir | LL | for _ in as Clone>::clone(&self).into_iter() {} | ++++++++++++++++++++++++++++++ ~ -help: consider cloning the value if the performance cost is acceptable - | -LL | for _ in self.clone().clone().into_iter() {} - | ++++++++ error: aborting due to 1 previous error From 259348cf7e6078e5e955ad53e14aeb9cc24c48ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 13 Mar 2024 06:10:12 +0000 Subject: [PATCH 08/19] Remove unnecessary argument from `suggest_cloning` --- .../src/diagnostics/conflict_errors.rs | 24 +++++-------------- .../src/diagnostics/move_errors.rs | 6 ++--- 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 3bc9f39d0f858..f978a8df66b9d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -476,7 +476,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } else if self.suggest_hoisting_call_outside_loop(err, expr) { // The place where the the type moves would be misleading to suggest clone. // #121466 - self.suggest_cloning(err, ty, expr, move_span); + self.suggest_cloning(err, ty, expr); } } if let Some(pat) = finder.pat { @@ -987,20 +987,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { can_suggest_clone } - pub(crate) fn suggest_cloning( - &self, - err: &mut Diag<'_>, - ty: Ty<'tcx>, - expr: &hir::Expr<'_>, - span: Span, - ) { + pub(crate) fn suggest_cloning(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, expr: &hir::Expr<'_>) { if let Some(clone_trait_def) = self.infcx.tcx.lang_items().clone_trait() && self .infcx .type_implements_trait(clone_trait_def, [ty], self.param_env) .must_apply_modulo_regions() { - self.suggest_cloning_inner(err, ty, expr, span); + self.suggest_cloning_inner(err, ty, expr); } } @@ -1019,13 +1013,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } - fn suggest_cloning_inner( - &self, - err: &mut Diag<'_>, - ty: Ty<'tcx>, - expr: &hir::Expr<'_>, - span: Span, - ) { + fn suggest_cloning_inner(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, expr: &hir::Expr<'_>) { let tcx = self.infcx.tcx; if let Some(_) = self.clone_on_reference(expr) { // Avoid redundant clone suggestion already suggested in `explain_captures`. @@ -1053,7 +1041,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // Account for `(*x)` to suggest `x.clone()`. expr.span.with_lo(inner_expr.span.hi()) } else { - span.shrink_to_hi() + expr.span.shrink_to_hi() }; sugg.push((span, suggestion)); let msg = if let ty::Adt(def, _) = ty.kind() @@ -1173,7 +1161,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if let Some(expr) = self.find_expr(borrow_span) && let Some(ty) = typeck_results.node_type_opt(expr.hir_id) { - self.suggest_cloning(&mut err, ty, expr, borrow_span); + self.suggest_cloning(&mut err, ty, expr); } self.buffer_error(err); } diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 17e43a5b39ce3..31ef3519fff6b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -445,7 +445,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }; if let Some(expr) = self.find_expr(span) { - self.suggest_cloning(err, place_ty, expr, span); + self.suggest_cloning(err, place_ty, expr); } err.subdiagnostic( @@ -480,7 +480,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }; if let Some(expr) = self.find_expr(span) { - self.suggest_cloning(err, place_ty, expr, span); + self.suggest_cloning(err, place_ty, expr); } err.subdiagnostic( @@ -593,7 +593,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let place_desc = &format!("`{}`", self.local_names[*local].unwrap()); if let Some(expr) = self.find_expr(binding_span) { - self.suggest_cloning(err, bind_to.ty, expr, binding_span); + self.suggest_cloning(err, bind_to.ty, expr); } err.subdiagnostic( From 5a7caa3174f8174db817228d8c2a02aa4913095c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 13 Mar 2024 15:37:27 +0000 Subject: [PATCH 09/19] Fix accuracy of `T: Clone` check in suggestion --- .../src/diagnostics/conflict_errors.rs | 1 + .../associated-types-outlives.stderr | 6 ------ tests/ui/binop/binop-move-semantics.stderr | 6 ------ tests/ui/borrowck/borrowck-issue-48962.stderr | 5 +++++ .../borrowck/borrowck-move-subcomponent.stderr | 6 ------ ...borrowck-overloaded-index-move-index.stderr | 12 ++++++++++++ tests/ui/borrowck/clone-on-ref.fixed | 4 ++-- tests/ui/borrowck/clone-on-ref.stderr | 10 ---------- tests/ui/error-codes/E0504.stderr | 6 ------ tests/ui/error-codes/E0505.stderr | 6 ------ tests/ui/nll/closure-access-spans.stderr | 10 ++++++++++ ...report-when-borrow-and-drop-conflict.stderr | 4 ++++ .../ui/nll/polonius/polonius-smoke-test.stderr | 12 ++++++++++++ .../borrowck-move-ref-pattern.stderr | 6 ------ .../ui/try-block/try-block-bad-lifetime.stderr | 5 +++++ tests/ui/unop-move-semantics.stderr | 6 ------ tests/ui/variance/variance-issue-20533.stderr | 18 ------------------ 17 files changed, 51 insertions(+), 72 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index f978a8df66b9d..c385fb583bb7b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -988,6 +988,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } pub(crate) fn suggest_cloning(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, expr: &hir::Expr<'_>) { + let ty = ty.peel_refs(); if let Some(clone_trait_def) = self.infcx.tcx.lang_items().clone_trait() && self .infcx diff --git a/tests/ui/associated-types/associated-types-outlives.stderr b/tests/ui/associated-types/associated-types-outlives.stderr index 4a496b5fa87f1..deeedd2226698 100644 --- a/tests/ui/associated-types/associated-types-outlives.stderr +++ b/tests/ui/associated-types/associated-types-outlives.stderr @@ -10,12 +10,6 @@ LL | drop(x); | ^ move out of `x` occurs here LL | return f(y); | - borrow later used here - | -help: consider cloning the value if the performance cost is acceptable - | -LL - 's: loop { y = denormalise(&x); break } -LL + 's: loop { y = denormalise(x.clone()); break } - | error: aborting due to 1 previous error diff --git a/tests/ui/binop/binop-move-semantics.stderr b/tests/ui/binop/binop-move-semantics.stderr index 219bfeaae0675..1dd8c9a87d472 100644 --- a/tests/ui/binop/binop-move-semantics.stderr +++ b/tests/ui/binop/binop-move-semantics.stderr @@ -51,12 +51,6 @@ LL | x ... LL | use_mut(n); use_imm(m); | - borrow later used here - | -help: consider cloning the value if the performance cost is acceptable - | -LL - let m = &x; -LL + let m = x.clone(); - | error[E0505]: cannot move out of `y` because it is borrowed --> $DIR/binop-move-semantics.rs:23:5 diff --git a/tests/ui/borrowck/borrowck-issue-48962.stderr b/tests/ui/borrowck/borrowck-issue-48962.stderr index ee174f6736e1e..6e821a4c6b0b1 100644 --- a/tests/ui/borrowck/borrowck-issue-48962.stderr +++ b/tests/ui/borrowck/borrowck-issue-48962.stderr @@ -17,6 +17,11 @@ LL | {src}; | --- value moved here LL | src.0 = 66; | ^^^^^^^^^^ value used here after move + | +help: consider cloning the value if the performance cost is acceptable + | +LL | {src.clone()}; + | ++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/borrowck/borrowck-move-subcomponent.stderr b/tests/ui/borrowck/borrowck-move-subcomponent.stderr index 1e4d94920f488..8408d99156a30 100644 --- a/tests/ui/borrowck/borrowck-move-subcomponent.stderr +++ b/tests/ui/borrowck/borrowck-move-subcomponent.stderr @@ -9,12 +9,6 @@ LL | let S { x: ax } = a; | ^^ move out of `a.x` occurs here LL | f(pb); | -- borrow later used here - | -help: consider cloning the value if the performance cost is acceptable - | -LL - let pb = &a; -LL + let pb = a.clone(); - | error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-overloaded-index-move-index.stderr b/tests/ui/borrowck/borrowck-overloaded-index-move-index.stderr index 7f8cc74a7157a..3386d2074a0b7 100644 --- a/tests/ui/borrowck/borrowck-overloaded-index-move-index.stderr +++ b/tests/ui/borrowck/borrowck-overloaded-index-move-index.stderr @@ -11,6 +11,12 @@ LL | println!("{}", f[s]); ... LL | use_mut(rs); | -- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let rs = &mut s; +LL + let rs = s.clone(); + | error[E0505]: cannot move out of `s` because it is borrowed --> $DIR/borrowck-overloaded-index-move-index.rs:53:7 @@ -25,6 +31,12 @@ LL | f[s] = 10; ... LL | use_mut(rs); | -- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let rs = &mut s; +LL + let rs = s.clone(); + | error[E0382]: use of moved value: `s` --> $DIR/borrowck-overloaded-index-move-index.rs:53:7 diff --git a/tests/ui/borrowck/clone-on-ref.fixed b/tests/ui/borrowck/clone-on-ref.fixed index 539ecd0b32c7b..b6927ba590e34 100644 --- a/tests/ui/borrowck/clone-on-ref.fixed +++ b/tests/ui/borrowck/clone-on-ref.fixed @@ -9,7 +9,7 @@ fn foo(list: &mut Vec) { drop(cloned_items); } fn bar(x: T) { - let a = x.clone(); + let a = &x; let b = a.clone(); drop(x); //~^ ERROR cannot move out of `x` because it is borrowed @@ -19,7 +19,7 @@ fn bar(x: T) { #[derive(Clone)] struct A; fn qux(x: A) { - let a = x.clone(); + let a = &x; let b = a.clone(); drop(x); //~^ ERROR cannot move out of `x` because it is borrowed diff --git a/tests/ui/borrowck/clone-on-ref.stderr b/tests/ui/borrowck/clone-on-ref.stderr index 5ee56edd7b585..ee4fcadf55ae1 100644 --- a/tests/ui/borrowck/clone-on-ref.stderr +++ b/tests/ui/borrowck/clone-on-ref.stderr @@ -36,11 +36,6 @@ help: consider further restricting this bound | LL | fn bar(x: T) { | +++++++ -help: consider cloning the value if the performance cost is acceptable - | -LL - let a = &x; -LL + let a = x.clone(); - | error[E0505]: cannot move out of `x` because it is borrowed --> $DIR/clone-on-ref.rs:23:10 @@ -62,11 +57,6 @@ help: consider annotating `A` with `#[derive(Clone)]` LL + #[derive(Clone)] LL | struct A; | -help: consider cloning the value if the performance cost is acceptable - | -LL - let a = &x; -LL + let a = x.clone(); - | error: aborting due to 3 previous errors diff --git a/tests/ui/error-codes/E0504.stderr b/tests/ui/error-codes/E0504.stderr index 47f1218d2fa17..c8a48961cb3b1 100644 --- a/tests/ui/error-codes/E0504.stderr +++ b/tests/ui/error-codes/E0504.stderr @@ -13,12 +13,6 @@ LL | println!("child function: {}", fancy_num.num); ... LL | println!("main function: {}", fancy_ref.num); | ------------- borrow later used here - | -help: consider cloning the value if the performance cost is acceptable - | -LL - let fancy_ref = &fancy_num; -LL + let fancy_ref = fancy_num.clone(); - | error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0505.stderr b/tests/ui/error-codes/E0505.stderr index 7e99d5252bdc0..250680d2c1c5d 100644 --- a/tests/ui/error-codes/E0505.stderr +++ b/tests/ui/error-codes/E0505.stderr @@ -10,12 +10,6 @@ LL | eat(x); | ^ move out of `x` occurs here LL | _ref_to_val.use_ref(); | ----------- borrow later used here - | -help: consider cloning the value if the performance cost is acceptable - | -LL - let _ref_to_val: &Value = &x; -LL + let _ref_to_val: &Value = x.clone(); - | error: aborting due to 1 previous error diff --git a/tests/ui/nll/closure-access-spans.stderr b/tests/ui/nll/closure-access-spans.stderr index 7dc8acf84999b..f789e5e9f9571 100644 --- a/tests/ui/nll/closure-access-spans.stderr +++ b/tests/ui/nll/closure-access-spans.stderr @@ -109,6 +109,11 @@ LL | || *x = String::new(); | ^^ -- borrow occurs due to use in closure | | | value borrowed here after move + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let r = x.clone(); + | ++++++++ error[E0382]: use of moved value: `x` --> $DIR/closure-access-spans.rs:50:5 @@ -121,6 +126,11 @@ LL | || x; | ^^ - use occurs due to use in closure | | | value used here after move + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let r = x.clone(); + | ++++++++ error: aborting due to 9 previous errors diff --git a/tests/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.stderr b/tests/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.stderr index 7f9cbc3c30a92..f4e7869bf0056 100644 --- a/tests/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.stderr +++ b/tests/ui/nll/issue-52059-report-when-borrow-and-drop-conflict.stderr @@ -41,6 +41,10 @@ help: consider borrowing here | LL | let p = &s.url; p | + +help: consider cloning the value if the performance cost is acceptable + | +LL | let p = s.url.clone(); p + | ++++++++ error: aborting due to 4 previous errors diff --git a/tests/ui/nll/polonius/polonius-smoke-test.stderr b/tests/ui/nll/polonius/polonius-smoke-test.stderr index 534813b2d9f5a..77722beab98d2 100644 --- a/tests/ui/nll/polonius/polonius-smoke-test.stderr +++ b/tests/ui/nll/polonius/polonius-smoke-test.stderr @@ -27,6 +27,12 @@ LL | let z = x; | ^ move out of `x` occurs here LL | y | - returning this value requires that `*x` is borrowed for `'1` + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let y = &mut *x; +LL + let y = x.clone(); + | error[E0505]: cannot move out of `s` because it is borrowed --> $DIR/polonius-smoke-test.rs:42:5 @@ -40,6 +46,12 @@ LL | s; | ^ move out of `s` occurs here LL | tmp; | --- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let r = &mut *s; +LL + let r = s.clone(); + | error: aborting due to 4 previous errors diff --git a/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr b/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr index 124731d01f361..a033cc0655ef9 100644 --- a/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr +++ b/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr @@ -10,12 +10,6 @@ LL | let [ref _x0_hold, _x1, ref xs_hold @ ..] = arr; LL | _x1 = U; LL | drop(hold_all); | -------- borrow later used here - | -help: consider cloning the value if the performance cost is acceptable - | -LL - let hold_all = &arr; -LL + let hold_all = arr.clone(); - | error[E0384]: cannot assign twice to immutable variable `_x1` --> $DIR/borrowck-move-ref-pattern.rs:9:5 diff --git a/tests/ui/try-block/try-block-bad-lifetime.stderr b/tests/ui/try-block/try-block-bad-lifetime.stderr index 28941cb0a9e40..6f69329535711 100644 --- a/tests/ui/try-block/try-block-bad-lifetime.stderr +++ b/tests/ui/try-block/try-block-bad-lifetime.stderr @@ -34,6 +34,11 @@ LL | Err(k) ?; ... LL | ::std::mem::drop(k); | ^ value used here after move + | +help: consider cloning the value if the performance cost is acceptable + | +LL | Err(k.clone()) ?; + | ++++++++ error[E0506]: cannot assign to `i` because it is borrowed --> $DIR/try-block-bad-lifetime.rs:32:9 diff --git a/tests/ui/unop-move-semantics.stderr b/tests/ui/unop-move-semantics.stderr index 3c887f25ae8fb..187dd66b2fe59 100644 --- a/tests/ui/unop-move-semantics.stderr +++ b/tests/ui/unop-move-semantics.stderr @@ -33,12 +33,6 @@ LL | !x; ... LL | use_mut(n); use_imm(m); | - borrow later used here - | -help: consider cloning the value if the performance cost is acceptable - | -LL - let m = &x; -LL + let m = x.clone(); - | error[E0505]: cannot move out of `y` because it is borrowed --> $DIR/unop-move-semantics.rs:17:6 diff --git a/tests/ui/variance/variance-issue-20533.stderr b/tests/ui/variance/variance-issue-20533.stderr index bee9b802135a5..258f67db5ce4b 100644 --- a/tests/ui/variance/variance-issue-20533.stderr +++ b/tests/ui/variance/variance-issue-20533.stderr @@ -9,12 +9,6 @@ LL | drop(a); | ^ move out of `a` occurs here LL | drop(x); | - borrow later used here - | -help: consider cloning the value if the performance cost is acceptable - | -LL - let x = foo(&a); -LL + let x = foo(a.clone()); - | error[E0505]: cannot move out of `a` because it is borrowed --> $DIR/variance-issue-20533.rs:34:14 @@ -27,12 +21,6 @@ LL | drop(a); | ^ move out of `a` occurs here LL | drop(x); | - borrow later used here - | -help: consider cloning the value if the performance cost is acceptable - | -LL - let x = bar(&a); -LL + let x = bar(a.clone()); - | error[E0505]: cannot move out of `a` because it is borrowed --> $DIR/variance-issue-20533.rs:40:14 @@ -45,12 +33,6 @@ LL | drop(a); | ^ move out of `a` occurs here LL | drop(x); | - borrow later used here - | -help: consider cloning the value if the performance cost is acceptable - | -LL - let x = baz(&a); -LL + let x = baz(a.clone()); - | error: aborting due to 3 previous errors From 7f7f6792f161d8b7f383ca18a1d820d2ca811bc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 13 Mar 2024 16:35:58 +0000 Subject: [PATCH 10/19] Do not recomment cloning explicit `&mut` expressions --- .../src/diagnostics/conflict_errors.rs | 5 +++++ .../borrowck-overloaded-index-move-index.stderr | 12 ------------ tests/ui/nll/polonius/polonius-smoke-test.stderr | 12 ------------ 3 files changed, 5 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index c385fb583bb7b..7c4623e6ea7e1 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1033,6 +1033,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { while let hir::ExprKind::AddrOf(.., inner) | hir::ExprKind::Unary(hir::UnOp::Deref, inner) = &inner_expr.kind { + if let hir::ExprKind::AddrOf(_, hir::Mutability::Mut, _) = inner_expr.kind { + // We assume that `&mut` refs are desired for their side-effects, so cloning the + // value wouldn't do what the user wanted. + return; + } inner_expr = inner; } if inner_expr.span.lo() != expr.span.lo() { diff --git a/tests/ui/borrowck/borrowck-overloaded-index-move-index.stderr b/tests/ui/borrowck/borrowck-overloaded-index-move-index.stderr index 3386d2074a0b7..7f8cc74a7157a 100644 --- a/tests/ui/borrowck/borrowck-overloaded-index-move-index.stderr +++ b/tests/ui/borrowck/borrowck-overloaded-index-move-index.stderr @@ -11,12 +11,6 @@ LL | println!("{}", f[s]); ... LL | use_mut(rs); | -- borrow later used here - | -help: consider cloning the value if the performance cost is acceptable - | -LL - let rs = &mut s; -LL + let rs = s.clone(); - | error[E0505]: cannot move out of `s` because it is borrowed --> $DIR/borrowck-overloaded-index-move-index.rs:53:7 @@ -31,12 +25,6 @@ LL | f[s] = 10; ... LL | use_mut(rs); | -- borrow later used here - | -help: consider cloning the value if the performance cost is acceptable - | -LL - let rs = &mut s; -LL + let rs = s.clone(); - | error[E0382]: use of moved value: `s` --> $DIR/borrowck-overloaded-index-move-index.rs:53:7 diff --git a/tests/ui/nll/polonius/polonius-smoke-test.stderr b/tests/ui/nll/polonius/polonius-smoke-test.stderr index 77722beab98d2..534813b2d9f5a 100644 --- a/tests/ui/nll/polonius/polonius-smoke-test.stderr +++ b/tests/ui/nll/polonius/polonius-smoke-test.stderr @@ -27,12 +27,6 @@ LL | let z = x; | ^ move out of `x` occurs here LL | y | - returning this value requires that `*x` is borrowed for `'1` - | -help: consider cloning the value if the performance cost is acceptable - | -LL - let y = &mut *x; -LL + let y = x.clone(); - | error[E0505]: cannot move out of `s` because it is borrowed --> $DIR/polonius-smoke-test.rs:42:5 @@ -46,12 +40,6 @@ LL | s; | ^ move out of `s` occurs here LL | tmp; | --- borrow later used here - | -help: consider cloning the value if the performance cost is acceptable - | -LL - let r = &mut *s; -LL + let r = s.clone(); - | error: aborting due to 4 previous errors From a1a3abb08fca88ba1c76888c0201870a5f590b5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 13 Mar 2024 20:58:54 +0000 Subject: [PATCH 11/19] When possible, suggest cloning the result of a call instead of an argument ``` error[E0505]: cannot move out of `a` because it is borrowed --> $DIR/variance-issue-20533.rs:28:14 | LL | let a = AffineU32(1); | - binding `a` declared here LL | let x = foo(&a); | -- borrow of `a` occurs here LL | drop(a); | ^ move out of `a` occurs here LL | drop(x); | - borrow later used here | help: consider cloning the value if the performance cost is acceptable | LL | let x = foo(&a).clone(); | ++++++++ ``` --- .../src/diagnostics/conflict_errors.rs | 89 +++++++++++++++++-- .../src/diagnostics/move_errors.rs | 16 ++-- .../associated-types-outlives.rs | 4 +- .../associated-types-outlives.stderr | 5 ++ tests/ui/variance/variance-issue-20533.rs | 19 ++++ tests/ui/variance/variance-issue-20533.stderr | 43 ++++++++- 6 files changed, 157 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 7c4623e6ea7e1..27cb0bbe537ca 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -476,7 +476,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } else if self.suggest_hoisting_call_outside_loop(err, expr) { // The place where the the type moves would be misleading to suggest clone. // #121466 - self.suggest_cloning(err, ty, expr); + self.suggest_cloning(err, ty, expr, None); } } if let Some(pat) = finder.pat { @@ -987,7 +987,78 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { can_suggest_clone } - pub(crate) fn suggest_cloning(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, expr: &hir::Expr<'_>) { + pub(crate) fn suggest_cloning( + &self, + err: &mut Diag<'_>, + ty: Ty<'tcx>, + expr: &hir::Expr<'_>, + other_expr: Option<&hir::Expr<'_>>, + ) { + 'outer: { + if let ty::Ref(..) = ty.kind() { + // We check for either `let binding = foo(expr, other_expr);` or + // `foo(expr, other_expr);` and if so we don't suggest an incorrect + // `foo(expr, other_expr).clone()` + if let Some(other_expr) = other_expr + && let Some(parent_let) = + self.infcx.tcx.hir().parent_iter(expr.hir_id).find_map(|n| { + if let (hir_id, hir::Node::Local(_) | hir::Node::Stmt(_)) = n { + Some(hir_id) + } else { + None + } + }) + && let Some(other_parent_let) = + self.infcx.tcx.hir().parent_iter(other_expr.hir_id).find_map(|n| { + if let (hir_id, hir::Node::Local(_) | hir::Node::Stmt(_)) = n { + Some(hir_id) + } else { + None + } + }) + && parent_let == other_parent_let + { + // Explicitly check that we don't have `foo(&*expr, other_expr)`, as cloning the + // result of `foo(...)` won't help. + break 'outer; + } + + // We're suggesting `.clone()` on an borrowed value. See if the expression we have + // is an argument to a function or method call, and try to suggest cloning the + // *result* of the call, instead of the argument. This is closest to what people + // would actually be looking for in most cases, with maybe the exception of things + // like `fn(T) -> T`, but even then it is reasonable. + let typeck_results = self.infcx.tcx.typeck(self.mir_def_id()); + let mut prev = expr; + while let hir::Node::Expr(parent) = self.infcx.tcx.parent_hir_node(prev.hir_id) { + if let hir::ExprKind::Call(..) | hir::ExprKind::MethodCall(..) = parent.kind + && let Some(call_ty) = typeck_results.node_type_opt(parent.hir_id) + && let call_ty = call_ty.peel_refs() + && (!call_ty + .walk() + .any(|t| matches!(t.unpack(), ty::GenericArgKind::Lifetime(_))) + || if let ty::Alias(ty::Projection, _) = call_ty.kind() { + // FIXME: this isn't quite right with lifetimes on assoc types, + // but ignore for now. We will only suggest cloning if + // `::Assoc: Clone`, which should keep false positives + // down to a managable ammount. + true + } else { + false + }) + && let Some(clone_trait_def) = self.infcx.tcx.lang_items().clone_trait() + && self + .infcx + .type_implements_trait(clone_trait_def, [call_ty], self.param_env) + .must_apply_modulo_regions() + && self.suggest_cloning_inner(err, call_ty, parent) + { + return; + } + prev = parent; + } + } + } let ty = ty.peel_refs(); if let Some(clone_trait_def) = self.infcx.tcx.lang_items().clone_trait() && self @@ -1014,12 +1085,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } - fn suggest_cloning_inner(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, expr: &hir::Expr<'_>) { + fn suggest_cloning_inner( + &self, + err: &mut Diag<'_>, + ty: Ty<'tcx>, + expr: &hir::Expr<'_>, + ) -> bool { let tcx = self.infcx.tcx; if let Some(_) = self.clone_on_reference(expr) { // Avoid redundant clone suggestion already suggested in `explain_captures`. // See `tests/ui/moves/needs-clone-through-deref.rs` - return; + return false; } // Try to find predicates on *generic params* that would allow copying `ty` let suggestion = @@ -1036,7 +1112,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if let hir::ExprKind::AddrOf(_, hir::Mutability::Mut, _) = inner_expr.kind { // We assume that `&mut` refs are desired for their side-effects, so cloning the // value wouldn't do what the user wanted. - return; + return false; } inner_expr = inner; } @@ -1059,6 +1135,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { "consider cloning the value if the performance cost is acceptable" }; err.multipart_suggestion_verbose(msg, sugg, Applicability::MachineApplicable); + true } fn suggest_adding_bounds(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, def_id: DefId, span: Span) { @@ -1167,7 +1244,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if let Some(expr) = self.find_expr(borrow_span) && let Some(ty) = typeck_results.node_type_opt(expr.hir_id) { - self.suggest_cloning(&mut err, ty, expr); + self.suggest_cloning(&mut err, ty, expr, self.find_expr(span)); } self.buffer_error(err); } diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 31ef3519fff6b..bc02c5be93d61 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -435,7 +435,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diag<'_>, span: Span) { match error { - GroupedMoveError::MovesFromPlace { mut binds_to, move_from, .. } => { + GroupedMoveError::MovesFromPlace { + mut binds_to, move_from, span: other_span, .. + } => { self.add_borrow_suggestions(err, span); if binds_to.is_empty() { let place_ty = move_from.ty(self.body, self.infcx.tcx).ty; @@ -445,7 +447,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }; if let Some(expr) = self.find_expr(span) { - self.suggest_cloning(err, place_ty, expr); + self.suggest_cloning(err, place_ty, expr, self.find_expr(other_span)); } err.subdiagnostic( @@ -472,15 +474,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } // No binding. Nothing to suggest. GroupedMoveError::OtherIllegalMove { ref original_path, use_spans, .. } => { - let span = use_spans.var_or_use(); + let use_span = use_spans.var_or_use(); let place_ty = original_path.ty(self.body, self.infcx.tcx).ty; let place_desc = match self.describe_place(original_path.as_ref()) { Some(desc) => format!("`{desc}`"), None => "value".to_string(), }; - if let Some(expr) = self.find_expr(span) { - self.suggest_cloning(err, place_ty, expr); + if let Some(expr) = self.find_expr(use_span) { + self.suggest_cloning(err, place_ty, expr, self.find_expr(span)); } err.subdiagnostic( @@ -489,7 +491,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { is_partial_move: false, ty: place_ty, place: &place_desc, - span, + span: use_span, }, ); @@ -593,7 +595,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let place_desc = &format!("`{}`", self.local_names[*local].unwrap()); if let Some(expr) = self.find_expr(binding_span) { - self.suggest_cloning(err, bind_to.ty, expr); + self.suggest_cloning(err, bind_to.ty, expr, None); } err.subdiagnostic( diff --git a/tests/ui/associated-types/associated-types-outlives.rs b/tests/ui/associated-types/associated-types-outlives.rs index 55c276280b97a..245218067b406 100644 --- a/tests/ui/associated-types/associated-types-outlives.rs +++ b/tests/ui/associated-types/associated-types-outlives.rs @@ -3,10 +3,10 @@ // fn body, causing this (invalid) code to be accepted. pub trait Foo<'a> { - type Bar; + type Bar: Clone; } -impl<'a, T:'a> Foo<'a> for T { +impl<'a, T: 'a> Foo<'a> for T { type Bar = &'a T; } diff --git a/tests/ui/associated-types/associated-types-outlives.stderr b/tests/ui/associated-types/associated-types-outlives.stderr index deeedd2226698..c97af672c334f 100644 --- a/tests/ui/associated-types/associated-types-outlives.stderr +++ b/tests/ui/associated-types/associated-types-outlives.stderr @@ -10,6 +10,11 @@ LL | drop(x); | ^ move out of `x` occurs here LL | return f(y); | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | 's: loop { y = denormalise(&x).clone(); break } + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/variance/variance-issue-20533.rs b/tests/ui/variance/variance-issue-20533.rs index a2459f8730c52..4c1096089064b 100644 --- a/tests/ui/variance/variance-issue-20533.rs +++ b/tests/ui/variance/variance-issue-20533.rs @@ -19,8 +19,15 @@ fn baz<'a, T>(_x: &'a T) -> Baked<'a> { Baked(PhantomData) } +fn bat(x: &AffineU32) -> &u32 { + &x.0 +} + struct AffineU32(u32); +#[derive(Clone)] +struct ClonableAffineU32(u32); + fn main() { { let a = AffineU32(1); @@ -40,4 +47,16 @@ fn main() { drop(a); //~ ERROR cannot move out of `a` drop(x); } + { + let a = AffineU32(1); + let x = bat(&a); + drop(a); //~ ERROR cannot move out of `a` + drop(x); + } + { + let a = ClonableAffineU32(1); + let x = foo(&a); + drop(a); //~ ERROR cannot move out of `a` + drop(x); + } } diff --git a/tests/ui/variance/variance-issue-20533.stderr b/tests/ui/variance/variance-issue-20533.stderr index 258f67db5ce4b..7060144c7760d 100644 --- a/tests/ui/variance/variance-issue-20533.stderr +++ b/tests/ui/variance/variance-issue-20533.stderr @@ -1,5 +1,5 @@ error[E0505]: cannot move out of `a` because it is borrowed - --> $DIR/variance-issue-20533.rs:28:14 + --> $DIR/variance-issue-20533.rs:35:14 | LL | let a = AffineU32(1); | - binding `a` declared here @@ -11,7 +11,7 @@ LL | drop(x); | - borrow later used here error[E0505]: cannot move out of `a` because it is borrowed - --> $DIR/variance-issue-20533.rs:34:14 + --> $DIR/variance-issue-20533.rs:41:14 | LL | let a = AffineU32(1); | - binding `a` declared here @@ -23,7 +23,7 @@ LL | drop(x); | - borrow later used here error[E0505]: cannot move out of `a` because it is borrowed - --> $DIR/variance-issue-20533.rs:40:14 + --> $DIR/variance-issue-20533.rs:47:14 | LL | let a = AffineU32(1); | - binding `a` declared here @@ -34,6 +34,41 @@ LL | drop(a); LL | drop(x); | - borrow later used here -error: aborting due to 3 previous errors +error[E0505]: cannot move out of `a` because it is borrowed + --> $DIR/variance-issue-20533.rs:53:14 + | +LL | let a = AffineU32(1); + | - binding `a` declared here +LL | let x = bat(&a); + | -- borrow of `a` occurs here +LL | drop(a); + | ^ move out of `a` occurs here +LL | drop(x); + | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let x = bat(&a).clone(); + | ++++++++ + +error[E0505]: cannot move out of `a` because it is borrowed + --> $DIR/variance-issue-20533.rs:59:14 + | +LL | let a = ClonableAffineU32(1); + | - binding `a` declared here +LL | let x = foo(&a); + | -- borrow of `a` occurs here +LL | drop(a); + | ^ move out of `a` occurs here +LL | drop(x); + | - borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let x = foo(&a); +LL + let x = foo(a.clone()); + | + +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0505`. From b83ebea5de815b69b9715f810f4f317a547e499a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 13 Mar 2024 21:14:54 +0000 Subject: [PATCH 12/19] Update test to show incorrecct `+=` clone suggestion --- tests/ui/augmented-assignments.rs | 2 ++ tests/ui/augmented-assignments.stderr | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/ui/augmented-assignments.rs b/tests/ui/augmented-assignments.rs index bd2435a78bf22..ca12395e432ab 100644 --- a/tests/ui/augmented-assignments.rs +++ b/tests/ui/augmented-assignments.rs @@ -1,5 +1,6 @@ use std::ops::AddAssign; +#[derive(Clone)] struct Int(i32); impl AddAssign for Int { @@ -12,6 +13,7 @@ fn main() { let mut x = Int(1); //~ NOTE binding `x` declared here x //~^ NOTE borrow of `x` occurs here + //~| HELP consider cloning += x; //~^ ERROR cannot move out of `x` because it is borrowed diff --git a/tests/ui/augmented-assignments.stderr b/tests/ui/augmented-assignments.stderr index d1096aea2794e..465b88fcd5b16 100644 --- a/tests/ui/augmented-assignments.stderr +++ b/tests/ui/augmented-assignments.stderr @@ -1,5 +1,5 @@ error[E0505]: cannot move out of `x` because it is borrowed - --> $DIR/augmented-assignments.rs:16:5 + --> $DIR/augmented-assignments.rs:17:5 | LL | let mut x = Int(1); | ----- binding `x` declared here @@ -8,9 +8,14 @@ LL | x ... LL | x; | ^ move out of `x` occurs here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | x.clone() + | ++++++++ error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable - --> $DIR/augmented-assignments.rs:23:5 + --> $DIR/augmented-assignments.rs:25:5 | LL | y | ^ cannot borrow as mutable From dfe28debb914dc944bf8de270016071f6742098d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 13 Mar 2024 22:45:25 +0000 Subject: [PATCH 13/19] Account for assign binops in clone suggestions Explicitly look for `expr += other_expr;` and avoid suggesting `expr.clone() += other_expr;`, instead suggesting `expr += other_expr.clone();`. --- .../src/diagnostics/conflict_errors.rs | 34 +++++++++++++++++-- tests/ui/augmented-assignments.rs | 2 +- tests/ui/augmented-assignments.stderr | 2 +- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 27cb0bbe537ca..9abc29f52b250 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -991,9 +991,39 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &self, err: &mut Diag<'_>, ty: Ty<'tcx>, - expr: &hir::Expr<'_>, - other_expr: Option<&hir::Expr<'_>>, + mut expr: &'cx hir::Expr<'cx>, + mut other_expr: Option<&'cx hir::Expr<'cx>>, ) { + if let Some(some_other_expr) = other_expr + && let Some(parent_binop) = + self.infcx.tcx.hir().parent_iter(expr.hir_id).find_map(|n| { + if let (hir_id, hir::Node::Expr(e)) = n + && let hir::ExprKind::AssignOp(_binop, target, _arg) = e.kind + && target.hir_id == expr.hir_id + { + Some(hir_id) + } else { + None + } + }) + && let Some(other_parent_binop) = + self.infcx.tcx.hir().parent_iter(some_other_expr.hir_id).find_map(|n| { + if let (hir_id, hir::Node::Expr(expr)) = n + && let hir::ExprKind::AssignOp(..) = expr.kind + { + Some(hir_id) + } else { + None + } + }) + && { true } + && parent_binop == other_parent_binop + { + // Explicitly look for `expr += other_expr;` and avoid suggesting + // `expr.clone() += other_expr;`, instead suggesting `expr += other_expr.clone();`. + other_expr = Some(expr); + expr = some_other_expr; + } 'outer: { if let ty::Ref(..) = ty.kind() { // We check for either `let binding = foo(expr, other_expr);` or diff --git a/tests/ui/augmented-assignments.rs b/tests/ui/augmented-assignments.rs index ca12395e432ab..8b263e03593b8 100644 --- a/tests/ui/augmented-assignments.rs +++ b/tests/ui/augmented-assignments.rs @@ -13,11 +13,11 @@ fn main() { let mut x = Int(1); //~ NOTE binding `x` declared here x //~^ NOTE borrow of `x` occurs here - //~| HELP consider cloning += x; //~^ ERROR cannot move out of `x` because it is borrowed //~| move out of `x` occurs here + //~| HELP consider cloning let y = Int(2); //~^ HELP consider changing this to be mutable diff --git a/tests/ui/augmented-assignments.stderr b/tests/ui/augmented-assignments.stderr index 465b88fcd5b16..6b2900dd5d1bc 100644 --- a/tests/ui/augmented-assignments.stderr +++ b/tests/ui/augmented-assignments.stderr @@ -11,7 +11,7 @@ LL | x; | help: consider cloning the value if the performance cost is acceptable | -LL | x.clone() +LL | x.clone(); | ++++++++ error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable From 4ca876b7a43c52a51cfcaf865a29a3ff27059b26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 15 Mar 2024 18:08:12 +0000 Subject: [PATCH 14/19] Better account for `FnOnce` in move errors ``` error[E0382]: use of moved value: `blk` --> $DIR/once-cant-call-twice-on-heap.rs:8:5 | LL | fn foo(blk: F) { | --- move occurs because `blk` has type `F`, which does not implement the `Copy` trait LL | blk(); | ----- `blk` moved due to this call LL | blk(); | ^^^ value used here after move | note: `FnOnce` closures can only be called once --> $DIR/once-cant-call-twice-on-heap.rs:6:10 | LL | fn foo(blk: F) { | ^^^^^^^^ `F` is made to be an `FnOnce` closure here LL | blk(); | ----- this value implements `FnOnce`, which causes it to be moved when called ``` --- compiler/rustc_borrowck/messages.ftl | 6 ++ .../src/diagnostics/conflict_errors.rs | 15 ++++- .../rustc_borrowck/src/diagnostics/mod.rs | 64 +++++++++++++++++-- .../borrowck/borrowck-unboxed-closures.stderr | 12 ++-- tests/ui/once-cant-call-twice-on-heap.stderr | 12 ++-- 5 files changed, 88 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl index 587536e1f9a3b..c14a617eb91fe 100644 --- a/compiler/rustc_borrowck/messages.ftl +++ b/compiler/rustc_borrowck/messages.ftl @@ -87,6 +87,12 @@ borrowck_move_unsized = borrowck_moved_a_fn_once_in_call = this value implements `FnOnce`, which causes it to be moved when called +borrowck_moved_a_fn_once_in_call_call = + `FnOnce` closures can only be called once + +borrowck_moved_a_fn_once_in_call_def = + `{$ty}` is made to be an `FnOnce` closure here + borrowck_moved_due_to_await = {$place_name} {$is_partial -> [true] partially moved diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 9abc29f52b250..d2d4345fed9e7 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -283,7 +283,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { Some(name) => format!("`{name}`"), None => "value".to_owned(), }; - if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, ¬e_msg) { + if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, ¬e_msg) + || if let UseSpans::FnSelfUse { kind, .. } = use_spans + && let CallKind::FnCall { fn_trait_id, self_ty } = kind + && let ty::Param(_) = self_ty.kind() + && ty == self_ty + && Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait() + { + // this is a type parameter `T: FnOnce()`, don't suggest `T: FnOnce() + Clone`. + true + } else { + false + } + { // Suppress the next suggestion since we don't want to put more bounds onto // something that already has `Fn`-like bounds (or is a closure), so we can't // restrict anyways. @@ -1016,7 +1028,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { None } }) - && { true } && parent_binop == other_parent_binop { // Explicitly look for `expr += other_expr;` and avoid suggesting diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 5425161ed4887..53697b4b8b04a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -5,6 +5,7 @@ use crate::session_diagnostics::{ CaptureVarKind, CaptureVarPathUseCause, OnClosureNote, }; use rustc_errors::{Applicability, Diag}; +use rustc_errors::{DiagCtxt, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::CoroutineKind; @@ -29,6 +30,8 @@ use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _; use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions; +use crate::fluent_generated as fluent; + use super::borrow_set::BorrowData; use super::MirBorrowckCtxt; @@ -587,7 +590,7 @@ impl UseSpans<'_> { #[allow(rustc::diagnostic_outside_of_impl)] pub(super) fn args_subdiag( self, - dcx: &rustc_errors::DiagCtxt, + dcx: &DiagCtxt, err: &mut Diag<'_>, f: impl FnOnce(Span) -> CaptureArgLabel, ) { @@ -601,7 +604,7 @@ impl UseSpans<'_> { #[allow(rustc::diagnostic_outside_of_impl)] pub(super) fn var_path_only_subdiag( self, - dcx: &rustc_errors::DiagCtxt, + dcx: &DiagCtxt, err: &mut Diag<'_>, action: crate::InitializationRequiringAction, ) { @@ -639,7 +642,7 @@ impl UseSpans<'_> { #[allow(rustc::diagnostic_outside_of_impl)] pub(super) fn var_subdiag( self, - dcx: &rustc_errors::DiagCtxt, + dcx: &DiagCtxt, err: &mut Diag<'_>, kind: Option, f: impl FnOnce(hir::ClosureKind, Span) -> CaptureVarCause, @@ -1034,7 +1037,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { .map(|n| format!("`{n}`")) .unwrap_or_else(|| "value".to_owned()); match kind { - CallKind::FnCall { fn_trait_id, .. } + CallKind::FnCall { fn_trait_id, self_ty } if Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait() => { err.subdiagnostic( @@ -1046,7 +1049,58 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { is_loop_message, }, ); - err.subdiagnostic(self.dcx(), CaptureReasonNote::FnOnceMoveInCall { var_span }); + if let ty::Param(param_ty) = self_ty.kind() + && let generics = self.infcx.tcx.generics_of(self.mir_def_id()) + && let param = generics.type_param(param_ty, self.infcx.tcx) + && let Some(hir_generics) = self + .infcx + .tcx + .typeck_root_def_id(self.mir_def_id().to_def_id()) + .as_local() + .and_then(|def_id| self.infcx.tcx.hir().get_generics(def_id)) + && let spans = hir_generics + .predicates + .iter() + .filter_map(|pred| match pred { + hir::WherePredicate::BoundPredicate(pred) => Some(pred), + _ => None, + }) + .filter(|pred| { + if let Some((id, _)) = pred.bounded_ty.as_generic_param() { + id == param.def_id + } else { + false + } + }) + .flat_map(|pred| pred.bounds) + .filter_map(|bound| { + if let Some(trait_ref) = bound.trait_ref() + && let Some(trait_def_id) = trait_ref.trait_def_id() + && trait_def_id == fn_trait_id + { + Some(bound.span()) + } else { + None + } + }) + .collect::>() + && !spans.is_empty() + { + let mut span: MultiSpan = spans.clone().into(); + for sp in spans { + span.push_span_label(sp, fluent::borrowck_moved_a_fn_once_in_call_def); + } + span.push_span_label( + fn_call_span, + fluent::borrowck_moved_a_fn_once_in_call, + ); + err.span_note(span, fluent::borrowck_moved_a_fn_once_in_call_call); + } else { + err.subdiagnostic( + self.dcx(), + CaptureReasonNote::FnOnceMoveInCall { var_span }, + ); + } } CallKind::Operator { self_arg, trait_id, .. } => { let self_arg = self_arg.unwrap(); diff --git a/tests/ui/borrowck/borrowck-unboxed-closures.stderr b/tests/ui/borrowck/borrowck-unboxed-closures.stderr index 3634676463c62..a4513bd614ead 100644 --- a/tests/ui/borrowck/borrowck-unboxed-closures.stderr +++ b/tests/ui/borrowck/borrowck-unboxed-closures.stderr @@ -29,15 +29,13 @@ LL | f(1, 2); LL | f(1, 2); | ^ value used here after move | -note: this value implements `FnOnce`, which causes it to be moved when called - --> $DIR/borrowck-unboxed-closures.rs:11:5 +note: `FnOnce` closures can only be called once + --> $DIR/borrowck-unboxed-closures.rs:10:8 | +LL | fn c isize>(f: F) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `F` is made to be an `FnOnce` closure here LL | f(1, 2); - | ^ -help: consider further restricting this bound - | -LL | fn c isize + Copy>(f: F) { - | ++++++ + | ------- this value implements `FnOnce`, which causes it to be moved when called error: aborting due to 3 previous errors diff --git a/tests/ui/once-cant-call-twice-on-heap.stderr b/tests/ui/once-cant-call-twice-on-heap.stderr index 33dd840dbc2b9..42697374115ca 100644 --- a/tests/ui/once-cant-call-twice-on-heap.stderr +++ b/tests/ui/once-cant-call-twice-on-heap.stderr @@ -8,15 +8,13 @@ LL | blk(); LL | blk(); | ^^^ value used here after move | -note: this value implements `FnOnce`, which causes it to be moved when called - --> $DIR/once-cant-call-twice-on-heap.rs:7:5 +note: `FnOnce` closures can only be called once + --> $DIR/once-cant-call-twice-on-heap.rs:6:10 | +LL | fn foo(blk: F) { + | ^^^^^^^^ `F` is made to be an `FnOnce` closure here LL | blk(); - | ^^^ -help: consider further restricting this bound - | -LL | fn foo(blk: F) { - | ++++++ + | ----- this value implements `FnOnce`, which causes it to be moved when called error: aborting due to 1 previous error From d578ac9e476a376246320db1d934972601c5b0f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 16 Mar 2024 18:19:17 +0000 Subject: [PATCH 15/19] Account for move error in the spread operator on struct literals We attempt to suggest an appropriate clone for move errors on expressions like `S { ..s }` where a field isn't `Copy`. If we can't suggest, we still don't emit the incorrect suggestion of `S { ..s }.clone()`. ``` error[E0509]: cannot move out of type `S`, which implements the `Drop` trait --> $DIR/borrowck-struct-update-with-dtor.rs:28:19 | LL | let _s2 = S { a: 2, ..s0 }; | ^^^^^^^^^^^^^^^^ | | | cannot move out of here | move occurs because `s0.c` has type `K`, which does not implement the `Copy` trait | help: clone the value from the field instead of using the spread operator syntax | LL | let _s2 = S { a: 2, c: s0.c.clone(), ..s0 }; | +++++++++++++++++ ``` ``` error[E0509]: cannot move out of type `S<()>`, which implements the `Drop` trait --> $DIR/borrowck-struct-update-with-dtor.rs:20:19 | LL | let _s2 = S { a: 2, ..s0 }; | ^^^^^^^^^^^^^^^^ | | | cannot move out of here | move occurs because `s0.b` has type `B`, which does not implement the `Copy` trait | note: `B` doesn't implement `Copy` or `Clone` --> $DIR/borrowck-struct-update-with-dtor.rs:4:1 | LL | struct B; | ^^^^^^^^ help: if `B` implemented `Clone`, you could clone the value from the field instead of using the spread operator syntax | LL | let _s2 = S { a: 2, b: s0.b.clone(), ..s0 }; | +++++++++++++++++ ``` --- .../src/diagnostics/conflict_errors.rs | 118 ++++++++++- .../borrowck-struct-update-with-dtor.rs | 61 +++++- .../borrowck-struct-update-with-dtor.stderr | 197 ++++++++++++++++-- ...unctional-struct-update-noncopyable.stderr | 6 +- 4 files changed, 343 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index d2d4345fed9e7..ee50e82d80ddc 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -999,6 +999,93 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { can_suggest_clone } + fn suggest_cloning_on_spread_operator( + &self, + err: &mut Diag<'_>, + ty: Ty<'tcx>, + expr: &'cx hir::Expr<'cx>, + ) { + let typeck_results = self.infcx.tcx.typeck(self.mir_def_id()); + let hir::ExprKind::Struct(struct_qpath, fields, Some(base)) = expr.kind else { return }; + let hir::QPath::Resolved(_, path) = struct_qpath else { return }; + let hir::def::Res::Def(_, def_id) = path.res else { return }; + let Some(expr_ty) = typeck_results.node_type_opt(expr.hir_id) else { return }; + let ty::Adt(def, args) = expr_ty.kind() else { return }; + let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = base.kind else { return }; + let (hir::def::Res::Local(_) + | hir::def::Res::Def( + DefKind::Const | DefKind::ConstParam | DefKind::Static { .. } | DefKind::AssocConst, + _, + )) = path.res + else { + return; + }; + let Ok(base_str) = self.infcx.tcx.sess.source_map().span_to_snippet(base.span) else { + return; + }; + + // 1. look for the fields of type `ty`. + // 2. check if they are clone and add them to suggestion + // 3. check if there are any values left to `..` and remove it if not + // 4. emit suggestion to clone the field directly as `bar: base.bar.clone()` + + let mut final_field_count = fields.len(); + let Some(variant) = def.variants().iter().find(|variant| variant.def_id == def_id) else { + // When we have an enum, look for the variant that corresponds to the variant the user + // wrote. + return; + }; + let mut sugg = vec![]; + for field in &variant.fields { + // In practice unless there are more than one field with the same type, we'll be + // suggesting a single field at a type, because we don't aggregate multiple borrow + // checker errors involving the spread operator into a single one. + let field_ty = field.ty(self.infcx.tcx, args); + let ident = field.ident(self.infcx.tcx); + if field_ty == ty && fields.iter().all(|field| field.ident.name != ident.name) { + // Suggest adding field and cloning it. + sugg.push(format!("{ident}: {base_str}.{ident}.clone()")); + final_field_count += 1; + } + } + let (span, sugg) = match fields { + [.., last] => ( + if final_field_count == variant.fields.len() { + // We'll remove the `..base` as there aren't any fields left. + last.span.shrink_to_hi().with_hi(base.span.hi()) + } else { + last.span.shrink_to_hi() + }, + format!(", {}", sugg.join(", ")), + ), + // Account for no fields in suggestion span. + [] => ( + expr.span.with_lo(struct_qpath.span().hi()), + if final_field_count == variant.fields.len() { + // We'll remove the `..base` as there aren't any fields left. + format!(" {{ {} }}", sugg.join(", ")) + } else { + format!(" {{ {}, ..{base_str} }}", sugg.join(", ")) + }, + ), + }; + let prefix = if !self.implements_clone(ty) { + let msg = format!("`{ty}` doesn't implement `Copy` or `Clone`"); + if let ty::Adt(def, _) = ty.kind() { + err.span_note(self.infcx.tcx.def_span(def.did()), msg); + } else { + err.note(msg); + } + format!("if `{ty}` implemented `Clone`, you could ") + } else { + String::new() + }; + let msg = format!( + "{prefix}clone the value from the field instead of using the spread operator syntax", + ); + err.span_suggestion_verbose(span, msg, sugg, Applicability::MachineApplicable); + } + pub(crate) fn suggest_cloning( &self, err: &mut Diag<'_>, @@ -1006,6 +1093,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { mut expr: &'cx hir::Expr<'cx>, mut other_expr: Option<&'cx hir::Expr<'cx>>, ) { + if let hir::ExprKind::Struct(_, _, Some(_)) = expr.kind { + // We have `S { foo: val, ..base }`. In `check_aggregate_rvalue` we have a single + // `Location` that covers both the `S { ... }` literal, all of its fields and the + // `base`. If the move happens because of `S { foo: val, bar: base.bar }` the `expr` + // will already be correct. Instead, we see if we can suggest writing. + self.suggest_cloning_on_spread_operator(err, ty, expr); + return; + } + if let Some(some_other_expr) = other_expr && let Some(parent_binop) = self.infcx.tcx.hir().parent_iter(expr.hir_id).find_map(|n| { @@ -1087,11 +1183,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } else { false }) - && let Some(clone_trait_def) = self.infcx.tcx.lang_items().clone_trait() - && self - .infcx - .type_implements_trait(clone_trait_def, [call_ty], self.param_env) - .must_apply_modulo_regions() + && self.implements_clone(call_ty) && self.suggest_cloning_inner(err, call_ty, parent) { return; @@ -1101,16 +1193,20 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } let ty = ty.peel_refs(); - if let Some(clone_trait_def) = self.infcx.tcx.lang_items().clone_trait() - && self - .infcx - .type_implements_trait(clone_trait_def, [ty], self.param_env) - .must_apply_modulo_regions() - { + if self.implements_clone(ty) { self.suggest_cloning_inner(err, ty, expr); + // } else { + // err.note(format!("if `{ty}` implemented `Clone`, you could clone the value")); } } + fn implements_clone(&self, ty: Ty<'tcx>) -> bool { + let Some(clone_trait_def) = self.infcx.tcx.lang_items().clone_trait() else { return false }; + self.infcx + .type_implements_trait(clone_trait_def, [ty], self.param_env) + .must_apply_modulo_regions() + } + pub(crate) fn clone_on_reference(&self, expr: &hir::Expr<'_>) -> Option { let typeck_results = self.infcx.tcx.typeck(self.mir_def_id()); if let hir::ExprKind::MethodCall(segment, rcvr, args, span) = expr.kind diff --git a/tests/ui/borrowck/borrowck-struct-update-with-dtor.rs b/tests/ui/borrowck/borrowck-struct-update-with-dtor.rs index 1f6ed6d46aa69..f0d067477c65e 100644 --- a/tests/ui/borrowck/borrowck-struct-update-with-dtor.rs +++ b/tests/ui/borrowck/borrowck-struct-update-with-dtor.rs @@ -2,20 +2,63 @@ // move, when the struct implements Drop. struct B; -struct S { a: isize, b: B } -impl Drop for S { fn drop(&mut self) { } } +struct S { a: isize, b: B, c: K } +impl Drop for S { fn drop(&mut self) { } } -struct T { a: isize, mv: Box } +struct T { a: isize, b: Box } impl Drop for T { fn drop(&mut self) { } } -fn f(s0:S) { - let _s2 = S{a: 2, ..s0}; - //~^ ERROR [E0509] +struct V { a: isize, b: Box, c: K } +impl Drop for V { fn drop(&mut self) { } } + +#[derive(Clone)] +struct Clonable; + +mod not_all_clone { + use super::*; + fn a(s0: S<()>) { + let _s2 = S { a: 2, ..s0 }; + //~^ ERROR [E0509] + } + fn b(s0: S) { + let _s2 = S { a: 2, ..s0 }; + //~^ ERROR [E0509] + //~| ERROR [E0509] + } + fn c(s0: S) { + let _s2 = S { a: 2, ..s0 }; + //~^ ERROR [E0509] + //~| ERROR [E0509] + } } +mod all_clone { + use super::*; + fn a(s0: T) { + let _s2 = T { a: 2, ..s0 }; + //~^ ERROR [E0509] + } + + fn b(s0: T) { + let _s2 = T { ..s0 }; + //~^ ERROR [E0509] + } + + fn c(s0: T) { + let _s2 = T { a: 2, b: s0.b }; + //~^ ERROR [E0509] + } + + fn d(s0: V) { + let _s2 = V { a: 2, ..s0 }; + //~^ ERROR [E0509] + //~| ERROR [E0509] + } -fn g(s0:T) { - let _s2 = T{a: 2, ..s0}; - //~^ ERROR [E0509] + fn e(s0: V) { + let _s2 = V { a: 2, ..s0 }; + //~^ ERROR [E0509] + //~| ERROR [E0509] + } } fn main() { } diff --git a/tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr b/tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr index 01004fa56c684..d34b07fbcc6d9 100644 --- a/tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr +++ b/tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr @@ -1,26 +1,191 @@ -error[E0509]: cannot move out of type `S`, which implements the `Drop` trait - --> $DIR/borrowck-struct-update-with-dtor.rs:12:15 +error[E0509]: cannot move out of type `S<()>`, which implements the `Drop` trait + --> $DIR/borrowck-struct-update-with-dtor.rs:20:19 | -LL | let _s2 = S{a: 2, ..s0}; - | ^^^^^^^^^^^^^ - | | - | cannot move out of here - | move occurs because `s0.b` has type `B`, which does not implement the `Copy` trait +LL | let _s2 = S { a: 2, ..s0 }; + | ^^^^^^^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because `s0.b` has type `B`, which does not implement the `Copy` trait + | +note: `B` doesn't implement `Copy` or `Clone` + --> $DIR/borrowck-struct-update-with-dtor.rs:4:1 + | +LL | struct B; + | ^^^^^^^^ +help: if `B` implemented `Clone`, you could clone the value from the field instead of using the spread operator syntax + | +LL | let _s2 = S { a: 2, b: s0.b.clone(), ..s0 }; + | +++++++++++++++++ + +error[E0509]: cannot move out of type `S`, which implements the `Drop` trait + --> $DIR/borrowck-struct-update-with-dtor.rs:24:19 + | +LL | let _s2 = S { a: 2, ..s0 }; + | ^^^^^^^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because `s0.b` has type `B`, which does not implement the `Copy` trait + | +note: `B` doesn't implement `Copy` or `Clone` + --> $DIR/borrowck-struct-update-with-dtor.rs:4:1 + | +LL | struct B; + | ^^^^^^^^ +help: if `B` implemented `Clone`, you could clone the value from the field instead of using the spread operator syntax + | +LL | let _s2 = S { a: 2, b: s0.b.clone(), c: s0.c.clone() }; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error[E0509]: cannot move out of type `S`, which implements the `Drop` trait + --> $DIR/borrowck-struct-update-with-dtor.rs:24:19 + | +LL | let _s2 = S { a: 2, ..s0 }; + | ^^^^^^^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because `s0.c` has type `B`, which does not implement the `Copy` trait + | +note: `B` doesn't implement `Copy` or `Clone` + --> $DIR/borrowck-struct-update-with-dtor.rs:4:1 + | +LL | struct B; + | ^^^^^^^^ +help: if `B` implemented `Clone`, you could clone the value from the field instead of using the spread operator syntax + | +LL | let _s2 = S { a: 2, b: s0.b.clone(), c: s0.c.clone() }; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error[E0509]: cannot move out of type `S`, which implements the `Drop` trait + --> $DIR/borrowck-struct-update-with-dtor.rs:29:19 + | +LL | let _s2 = S { a: 2, ..s0 }; + | ^^^^^^^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because `s0.b` has type `B`, which does not implement the `Copy` trait + | +note: `B` doesn't implement `Copy` or `Clone` + --> $DIR/borrowck-struct-update-with-dtor.rs:4:1 + | +LL | struct B; + | ^^^^^^^^ +help: if `B` implemented `Clone`, you could clone the value from the field instead of using the spread operator syntax + | +LL | let _s2 = S { a: 2, b: s0.b.clone(), ..s0 }; + | +++++++++++++++++ + +error[E0509]: cannot move out of type `S`, which implements the `Drop` trait + --> $DIR/borrowck-struct-update-with-dtor.rs:29:19 + | +LL | let _s2 = S { a: 2, ..s0 }; + | ^^^^^^^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because `s0.c` has type `K`, which does not implement the `Copy` trait + | +help: clone the value from the field instead of using the spread operator syntax + | +LL | let _s2 = S { a: 2, c: s0.c.clone(), ..s0 }; + | +++++++++++++++++ + +error[E0509]: cannot move out of type `T`, which implements the `Drop` trait + --> $DIR/borrowck-struct-update-with-dtor.rs:37:19 + | +LL | let _s2 = T { a: 2, ..s0 }; + | ^^^^^^^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because `s0.b` has type `Box`, which does not implement the `Copy` trait + | +help: clone the value from the field instead of using the spread operator syntax + | +LL | let _s2 = T { a: 2, b: s0.b.clone() }; + | ~~~~~~~~~~~~~~~~~ + +error[E0509]: cannot move out of type `T`, which implements the `Drop` trait + --> $DIR/borrowck-struct-update-with-dtor.rs:42:19 + | +LL | let _s2 = T { ..s0 }; + | ^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because `s0.b` has type `Box`, which does not implement the `Copy` trait + | +help: clone the value from the field instead of using the spread operator syntax + | +LL | let _s2 = T { b: s0.b.clone(), ..s0 }; + | ~~~~~~~~~~~~~~~~~~~~~~~~~ error[E0509]: cannot move out of type `T`, which implements the `Drop` trait - --> $DIR/borrowck-struct-update-with-dtor.rs:17:15 + --> $DIR/borrowck-struct-update-with-dtor.rs:47:32 | -LL | let _s2 = T{a: 2, ..s0}; - | ^^^^^^^^^^^^^ - | | - | cannot move out of here - | move occurs because `s0.mv` has type `Box`, which does not implement the `Copy` trait +LL | let _s2 = T { a: 2, b: s0.b }; + | ^^^^ + | | + | cannot move out of here + | move occurs because `s0.b` has type `Box`, which does not implement the `Copy` trait | help: consider cloning the value if the performance cost is acceptable | -LL | let _s2 = T{a: 2, ..s0}.clone(); - | ++++++++ +LL | let _s2 = T { a: 2, b: s0.b.clone() }; + | ++++++++ + +error[E0509]: cannot move out of type `V`, which implements the `Drop` trait + --> $DIR/borrowck-struct-update-with-dtor.rs:52:19 + | +LL | let _s2 = V { a: 2, ..s0 }; + | ^^^^^^^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because `s0.b` has type `Box`, which does not implement the `Copy` trait + | +help: clone the value from the field instead of using the spread operator syntax + | +LL | let _s2 = V { a: 2, b: s0.b.clone(), ..s0 }; + | +++++++++++++++++ + +error[E0509]: cannot move out of type `V`, which implements the `Drop` trait + --> $DIR/borrowck-struct-update-with-dtor.rs:52:19 + | +LL | let _s2 = V { a: 2, ..s0 }; + | ^^^^^^^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because `s0.c` has type `K`, which does not implement the `Copy` trait + | +help: clone the value from the field instead of using the spread operator syntax + | +LL | let _s2 = V { a: 2, c: s0.c.clone(), ..s0 }; + | +++++++++++++++++ + +error[E0509]: cannot move out of type `V`, which implements the `Drop` trait + --> $DIR/borrowck-struct-update-with-dtor.rs:58:19 + | +LL | let _s2 = V { a: 2, ..s0 }; + | ^^^^^^^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because `s0.b` has type `Box`, which does not implement the `Copy` trait + | +help: clone the value from the field instead of using the spread operator syntax + | +LL | let _s2 = V { a: 2, b: s0.b.clone(), ..s0 }; + | +++++++++++++++++ + +error[E0509]: cannot move out of type `V`, which implements the `Drop` trait + --> $DIR/borrowck-struct-update-with-dtor.rs:58:19 + | +LL | let _s2 = V { a: 2, ..s0 }; + | ^^^^^^^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because `s0.c` has type `Clonable`, which does not implement the `Copy` trait + | +help: clone the value from the field instead of using the spread operator syntax + | +LL | let _s2 = V { a: 2, c: s0.c.clone(), ..s0 }; + | +++++++++++++++++ -error: aborting due to 2 previous errors +error: aborting due to 12 previous errors For more information about this error, try `rustc --explain E0509`. diff --git a/tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr b/tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr index 4d314f4f7c3c2..3f07142eeb729 100644 --- a/tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr +++ b/tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr @@ -7,10 +7,10 @@ LL | let _b = A { y: Arc::new(3), ..a }; | cannot move out of here | move occurs because `a.x` has type `Arc`, which does not implement the `Copy` trait | -help: clone the value to increment its reference count +help: clone the value from the field instead of using the spread operator syntax | -LL | let _b = A { y: Arc::new(3), ..a }.clone(); - | ++++++++ +LL | let _b = A { y: Arc::new(3), x: a.x.clone() }; + | ~~~~~~~~~~~~~~~~ error: aborting due to 1 previous error From d97d2fe7440ea3032a93fa3ebf8d62fea0337d04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 17 Mar 2024 21:25:38 +0000 Subject: [PATCH 16/19] Mention when the type of the moved value doesn't implement `Clone` --- .../src/diagnostics/conflict_errors.rs | 15 ++++++-- tests/ui/associated-types/issue-25700.stderr | 6 ++++ .../borrowck-move-out-of-static-item.stderr | 6 ++++ .../borrowck-move-subcomponent.stderr | 6 ++++ .../borrowck/borrowck-overloaded-call.stderr | 6 ++++ tests/ui/borrowck/clone-on-ref.stderr | 5 +++ tests/ui/borrowck/issue-103624.stderr | 6 ++++ .../issue-119915-bad-clone-suggestion.stderr | 5 +++ .../borrowck/issue-17718-static-move.stderr | 5 +++ tests/ui/borrowck/issue-20801.stderr | 20 +++++++++++ .../borrowck/move-error-in-promoted-2.stderr | 6 ++++ tests/ui/borrowck/move-error-snippets.stderr | 5 +++ ...e-in-static-initializer-issue-38520.stderr | 12 +++++++ tests/ui/box/leak-alloc.stderr | 6 ++++ .../derives/deriving-with-repr-packed.stderr | 5 +++ tests/ui/error-codes/E0504.stderr | 6 ++++ tests/ui/error-codes/E0505.stderr | 6 ++++ tests/ui/error-codes/E0507.stderr | 5 +++ tests/ui/error-codes/E0508-fail.stderr | 5 +++ tests/ui/error-codes/E0508.stderr | 5 +++ tests/ui/error-codes/E0509.stderr | 5 +++ tests/ui/issues/issue-17385.stderr | 12 +++++++ tests/ui/mir/issue-102389.stderr | 6 ++++ tests/ui/moves/issue-72649-uninit-in-loop.rs | 4 +++ .../moves/issue-72649-uninit-in-loop.stderr | 36 +++++++++++++++---- tests/ui/moves/move-fn-self-receiver.stderr | 11 ++++++ tests/ui/moves/move-out-of-array-1.stderr | 6 ++++ .../issue-21232-partial-init-and-use.stderr | 12 +++++++ tests/ui/nll/move-errors.stderr | 31 ++++++++++++++++ .../borrowck-pat-ref-mut-and-ref.stderr | 10 ++++++ .../union-borrow-move-parent-sibling.stderr | 5 +++ tests/ui/union/union-move.stderr | 10 ++++++ tests/ui/variance/variance-issue-20533.stderr | 18 ++++++++++ 33 files changed, 299 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index ee50e82d80ddc..860f8882ad3ca 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1195,8 +1195,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let ty = ty.peel_refs(); if self.implements_clone(ty) { self.suggest_cloning_inner(err, ty, expr); - // } else { - // err.note(format!("if `{ty}` implemented `Clone`, you could clone the value")); + } else if let ty::Adt(def, args) = ty.kind() + && def.did().as_local().is_some() + && def.variants().iter().all(|variant| { + variant + .fields + .iter() + .all(|field| self.implements_clone(field.ty(self.infcx.tcx, args))) + }) + { + err.span_note( + self.infcx.tcx.def_span(def.did()), + format!("if `{ty}` implemented `Clone`, you could clone the value"), + ); } } diff --git a/tests/ui/associated-types/issue-25700.stderr b/tests/ui/associated-types/issue-25700.stderr index 4e432c0e702a9..fb0e63c207a54 100644 --- a/tests/ui/associated-types/issue-25700.stderr +++ b/tests/ui/associated-types/issue-25700.stderr @@ -7,6 +7,12 @@ LL | drop(t); | - value moved here LL | drop(t); | ^ value used here after move + | +note: if `S<()>` implemented `Clone`, you could clone the value + --> $DIR/issue-25700.rs:1:1 + | +LL | struct S(#[allow(dead_code)] Option<&'static T>); + | ^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-move-out-of-static-item.stderr b/tests/ui/borrowck/borrowck-move-out-of-static-item.stderr index c4530820be5db..86bddacbdc727 100644 --- a/tests/ui/borrowck/borrowck-move-out-of-static-item.stderr +++ b/tests/ui/borrowck/borrowck-move-out-of-static-item.stderr @@ -3,6 +3,12 @@ error[E0507]: cannot move out of static item `BAR` | LL | test(BAR); | ^^^ move occurs because `BAR` has type `Foo`, which does not implement the `Copy` trait + | +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/borrowck-move-out-of-static-item.rs:3:1 + | +LL | struct Foo { + | ^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-move-subcomponent.stderr b/tests/ui/borrowck/borrowck-move-subcomponent.stderr index 8408d99156a30..4d9477f85811d 100644 --- a/tests/ui/borrowck/borrowck-move-subcomponent.stderr +++ b/tests/ui/borrowck/borrowck-move-subcomponent.stderr @@ -9,6 +9,12 @@ LL | let S { x: ax } = a; | ^^ move out of `a.x` occurs here LL | f(pb); | -- borrow later used here + | +note: if `S` implemented `Clone`, you could clone the value + --> $DIR/borrowck-move-subcomponent.rs:6:1 + | +LL | struct S { + | ^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-overloaded-call.stderr b/tests/ui/borrowck/borrowck-overloaded-call.stderr index 723b19f4124ac..1602058c183fc 100644 --- a/tests/ui/borrowck/borrowck-overloaded-call.stderr +++ b/tests/ui/borrowck/borrowck-overloaded-call.stderr @@ -29,6 +29,12 @@ LL | s(" world".to_string()); | - value moved here LL | s(" world".to_string()); | ^ value used here after move + | +note: if `SFnOnce` implemented `Clone`, you could clone the value + --> $DIR/borrowck-overloaded-call.rs:41:1 + | +LL | struct SFnOnce { + | ^^^^^^^^^^^^^^ error: aborting due to 3 previous errors diff --git a/tests/ui/borrowck/clone-on-ref.stderr b/tests/ui/borrowck/clone-on-ref.stderr index ee4fcadf55ae1..f0eaf4bac7dfa 100644 --- a/tests/ui/borrowck/clone-on-ref.stderr +++ b/tests/ui/borrowck/clone-on-ref.stderr @@ -52,6 +52,11 @@ LL | LL | println!("{b:?}"); | ----- borrow later used here | +note: if `A` implemented `Clone`, you could clone the value + --> $DIR/clone-on-ref.rs:19:1 + | +LL | struct A; + | ^^^^^^^^ help: consider annotating `A` with `#[derive(Clone)]` | LL + #[derive(Clone)] diff --git a/tests/ui/borrowck/issue-103624.stderr b/tests/ui/borrowck/issue-103624.stderr index 7a281e8aa30ca..94421c35c65d2 100644 --- a/tests/ui/borrowck/issue-103624.stderr +++ b/tests/ui/borrowck/issue-103624.stderr @@ -9,6 +9,12 @@ LL | spawn_blocking(move || { LL | LL | self.b; | ^^^^^^ move occurs because `self.b` has type `StructB`, which does not implement the `Copy` trait + | +note: if `StructB` implemented `Clone`, you could clone the value + --> $DIR/issue-103624.rs:23:1 + | +LL | struct StructB {} + | ^^^^^^^^^^^^^^ error[E0521]: borrowed data escapes outside of method --> $DIR/issue-103624.rs:14:9 diff --git a/tests/ui/borrowck/issue-119915-bad-clone-suggestion.stderr b/tests/ui/borrowck/issue-119915-bad-clone-suggestion.stderr index ab42205d510f0..701f00d079d38 100644 --- a/tests/ui/borrowck/issue-119915-bad-clone-suggestion.stderr +++ b/tests/ui/borrowck/issue-119915-bad-clone-suggestion.stderr @@ -11,6 +11,11 @@ note: `Example::::change` takes ownership of the receiver `self`, | LL | unsafe fn change(self) -> Example { | ^^^^ +note: if `Example` implemented `Clone`, you could clone the value + --> $DIR/issue-119915-bad-clone-suggestion.rs:3:1 + | +LL | struct Example(PhantomData<(fn(E), fn(FakeParam))>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/issue-17718-static-move.stderr b/tests/ui/borrowck/issue-17718-static-move.stderr index 5ca0a7fb8853e..e2c3a9d5a2663 100644 --- a/tests/ui/borrowck/issue-17718-static-move.stderr +++ b/tests/ui/borrowck/issue-17718-static-move.stderr @@ -4,6 +4,11 @@ error[E0507]: cannot move out of static item `FOO` LL | let _a = FOO; | ^^^ move occurs because `FOO` has type `Foo`, which does not implement the `Copy` trait | +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/issue-17718-static-move.rs:1:1 + | +LL | struct Foo; + | ^^^^^^^^^^ help: consider borrowing here | LL | let _a = &FOO; diff --git a/tests/ui/borrowck/issue-20801.stderr b/tests/ui/borrowck/issue-20801.stderr index 97294afd3df9e..1da6f0bef0234 100644 --- a/tests/ui/borrowck/issue-20801.stderr +++ b/tests/ui/borrowck/issue-20801.stderr @@ -19,6 +19,11 @@ error[E0507]: cannot move out of a mutable reference LL | let a = unsafe { *mut_ref() }; | ^^^^^^^^^^ move occurs because value has type `T`, which does not implement the `Copy` trait | +note: if `T` implemented `Clone`, you could clone the value + --> $DIR/issue-20801.rs:3:1 + | +LL | struct T(u8); + | ^^^^^^^^ help: consider removing the dereference here | LL - let a = unsafe { *mut_ref() }; @@ -31,6 +36,11 @@ error[E0507]: cannot move out of a shared reference LL | let b = unsafe { *imm_ref() }; | ^^^^^^^^^^ move occurs because value has type `T`, which does not implement the `Copy` trait | +note: if `T` implemented `Clone`, you could clone the value + --> $DIR/issue-20801.rs:3:1 + | +LL | struct T(u8); + | ^^^^^^^^ help: consider removing the dereference here | LL - let b = unsafe { *imm_ref() }; @@ -43,6 +53,11 @@ error[E0507]: cannot move out of a raw pointer LL | let c = unsafe { *mut_ptr() }; | ^^^^^^^^^^ move occurs because value has type `T`, which does not implement the `Copy` trait | +note: if `T` implemented `Clone`, you could clone the value + --> $DIR/issue-20801.rs:3:1 + | +LL | struct T(u8); + | ^^^^^^^^ help: consider removing the dereference here | LL - let c = unsafe { *mut_ptr() }; @@ -55,6 +70,11 @@ error[E0507]: cannot move out of a raw pointer LL | let d = unsafe { *const_ptr() }; | ^^^^^^^^^^^^ move occurs because value has type `T`, which does not implement the `Copy` trait | +note: if `T` implemented `Clone`, you could clone the value + --> $DIR/issue-20801.rs:3:1 + | +LL | struct T(u8); + | ^^^^^^^^ help: consider removing the dereference here | LL - let d = unsafe { *const_ptr() }; diff --git a/tests/ui/borrowck/move-error-in-promoted-2.stderr b/tests/ui/borrowck/move-error-in-promoted-2.stderr index 0d5edadcb4641..43f4e820857a0 100644 --- a/tests/ui/borrowck/move-error-in-promoted-2.stderr +++ b/tests/ui/borrowck/move-error-in-promoted-2.stderr @@ -6,6 +6,12 @@ LL | &([S][0],); | | | cannot move out of here | move occurs because value has type `S`, which does not implement the `Copy` trait + | +note: if `S` implemented `Clone`, you could clone the value + --> $DIR/move-error-in-promoted-2.rs:3:1 + | +LL | struct S; + | ^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/move-error-snippets.stderr b/tests/ui/borrowck/move-error-snippets.stderr index 83f9e19aa0d5c..40b64398aef6a 100644 --- a/tests/ui/borrowck/move-error-snippets.stderr +++ b/tests/ui/borrowck/move-error-snippets.stderr @@ -9,6 +9,11 @@ LL | let a = $c; LL | sss!(); | ------ in this macro invocation | +note: if `A` implemented `Clone`, you could clone the value + --> $DIR/move-error-snippets.rs:9:1 + | +LL | struct A; + | ^^^^^^^^ = note: this error originates in the macro `aaa` which comes from the expansion of the macro `sss` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider borrowing here | diff --git a/tests/ui/borrowck/move-in-static-initializer-issue-38520.stderr b/tests/ui/borrowck/move-in-static-initializer-issue-38520.stderr index 6619fb42c281a..a4e70b506462d 100644 --- a/tests/ui/borrowck/move-in-static-initializer-issue-38520.stderr +++ b/tests/ui/borrowck/move-in-static-initializer-issue-38520.stderr @@ -3,12 +3,24 @@ error[E0507]: cannot move out of a shared reference | LL | static Y: usize = get(*&X); | ^^^ move occurs because value has type `Foo`, which does not implement the `Copy` trait + | +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/move-in-static-initializer-issue-38520.rs:5:1 + | +LL | struct Foo(usize); + | ^^^^^^^^^^ error[E0507]: cannot move out of a shared reference --> $DIR/move-in-static-initializer-issue-38520.rs:13:22 | LL | const Z: usize = get(*&X); | ^^^ move occurs because value has type `Foo`, which does not implement the `Copy` trait + | +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/move-in-static-initializer-issue-38520.rs:5:1 + | +LL | struct Foo(usize); + | ^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/box/leak-alloc.stderr b/tests/ui/box/leak-alloc.stderr index 8b8cea3fe8491..53ff5f0107df3 100644 --- a/tests/ui/box/leak-alloc.stderr +++ b/tests/ui/box/leak-alloc.stderr @@ -11,6 +11,12 @@ LL | drop(alloc); LL | LL | use_value(*theref) | ------- borrow later used here + | +note: if `Alloc` implemented `Clone`, you could clone the value + --> $DIR/leak-alloc.rs:8:1 + | +LL | struct Alloc {} + | ^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/derives/deriving-with-repr-packed.stderr b/tests/ui/derives/deriving-with-repr-packed.stderr index 151be6901b01c..26ac532263fd0 100644 --- a/tests/ui/derives/deriving-with-repr-packed.stderr +++ b/tests/ui/derives/deriving-with-repr-packed.stderr @@ -36,6 +36,11 @@ LL | #[repr(packed)] LL | struct X(Y); | ^ move occurs because `self.0` has type `Y`, which does not implement the `Copy` trait | +note: if `Y` implemented `Clone`, you could clone the value + --> $DIR/deriving-with-repr-packed.rs:16:1 + | +LL | struct Y(usize); + | ^^^^^^^^ = note: `#[derive(Debug)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/error-codes/E0504.stderr b/tests/ui/error-codes/E0504.stderr index c8a48961cb3b1..900cb706bd97c 100644 --- a/tests/ui/error-codes/E0504.stderr +++ b/tests/ui/error-codes/E0504.stderr @@ -13,6 +13,12 @@ LL | println!("child function: {}", fancy_num.num); ... LL | println!("main function: {}", fancy_ref.num); | ------------- borrow later used here + | +note: if `FancyNum` implemented `Clone`, you could clone the value + --> $DIR/E0504.rs:1:1 + | +LL | struct FancyNum { + | ^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0505.stderr b/tests/ui/error-codes/E0505.stderr index 250680d2c1c5d..ce01298a70d90 100644 --- a/tests/ui/error-codes/E0505.stderr +++ b/tests/ui/error-codes/E0505.stderr @@ -10,6 +10,12 @@ LL | eat(x); | ^ move out of `x` occurs here LL | _ref_to_val.use_ref(); | ----------- borrow later used here + | +note: if `Value` implemented `Clone`, you could clone the value + --> $DIR/E0505.rs:1:1 + | +LL | struct Value {} + | ^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0507.stderr b/tests/ui/error-codes/E0507.stderr index 767fedfccbfb8..60a4daa9d382e 100644 --- a/tests/ui/error-codes/E0507.stderr +++ b/tests/ui/error-codes/E0507.stderr @@ -11,6 +11,11 @@ note: `TheDarkKnight::nothing_is_true` takes ownership of the receiver `self`, w | LL | fn nothing_is_true(self) {} | ^^^^ +note: if `TheDarkKnight` implemented `Clone`, you could clone the value + --> $DIR/E0507.rs:3:1 + | +LL | struct TheDarkKnight; + | ^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0508-fail.stderr b/tests/ui/error-codes/E0508-fail.stderr index 1153b1d09c733..96d3bcb67a57f 100644 --- a/tests/ui/error-codes/E0508-fail.stderr +++ b/tests/ui/error-codes/E0508-fail.stderr @@ -7,6 +7,11 @@ LL | let _value = array[0]; | cannot move out of here | move occurs because `array[_]` has type `NonCopy`, which does not implement the `Copy` trait | +note: if `NonCopy` implemented `Clone`, you could clone the value + --> $DIR/E0508-fail.rs:1:1 + | +LL | struct NonCopy; + | ^^^^^^^^^^^^^^ help: consider borrowing here | LL | let _value = &array[0]; diff --git a/tests/ui/error-codes/E0508.stderr b/tests/ui/error-codes/E0508.stderr index 4c864e2414463..c1b622e243213 100644 --- a/tests/ui/error-codes/E0508.stderr +++ b/tests/ui/error-codes/E0508.stderr @@ -7,6 +7,11 @@ LL | let _value = array[0]; | cannot move out of here | move occurs because `array[_]` has type `NonCopy`, which does not implement the `Copy` trait | +note: if `NonCopy` implemented `Clone`, you could clone the value + --> $DIR/E0508.rs:1:1 + | +LL | struct NonCopy; + | ^^^^^^^^^^^^^^ help: consider borrowing here | LL | let _value = &array[0]; diff --git a/tests/ui/error-codes/E0509.stderr b/tests/ui/error-codes/E0509.stderr index 59843a5491ab2..75c372d044019 100644 --- a/tests/ui/error-codes/E0509.stderr +++ b/tests/ui/error-codes/E0509.stderr @@ -7,6 +7,11 @@ LL | let fancy_field = drop_struct.fancy; | cannot move out of here | move occurs because `drop_struct.fancy` has type `FancyNum`, which does not implement the `Copy` trait | +note: if `FancyNum` implemented `Clone`, you could clone the value + --> $DIR/E0509.rs:1:1 + | +LL | struct FancyNum { + | ^^^^^^^^^^^^^^^ help: consider borrowing here | LL | let fancy_field = &drop_struct.fancy; diff --git a/tests/ui/issues/issue-17385.stderr b/tests/ui/issues/issue-17385.stderr index 77aa201b33564..988db0fb1fc4b 100644 --- a/tests/ui/issues/issue-17385.stderr +++ b/tests/ui/issues/issue-17385.stderr @@ -7,6 +7,12 @@ LL | drop(foo); | --- value moved here LL | match foo { | ^^^^^^^^^ value used here after move + | +note: if `X` implemented `Clone`, you could clone the value + --> $DIR/issue-17385.rs:1:1 + | +LL | struct X(isize); + | ^^^^^^^^ error[E0382]: use of moved value: `e` --> $DIR/issue-17385.rs:25:11 @@ -17,6 +23,12 @@ LL | drop(e); | - value moved here LL | match e { | ^ value used here after move + | +note: if `Enum` implemented `Clone`, you could clone the value + --> $DIR/issue-17385.rs:3:1 + | +LL | enum Enum { + | ^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/mir/issue-102389.stderr b/tests/ui/mir/issue-102389.stderr index 1f04d119b5668..838eaffb5a0dc 100644 --- a/tests/ui/mir/issue-102389.stderr +++ b/tests/ui/mir/issue-102389.stderr @@ -3,6 +3,12 @@ error[E0507]: cannot move out of `*inbounds` which is behind a shared reference | LL | array[*inbounds as usize] | ^^^^^^^^^ move occurs because `*inbounds` has type `Enum`, which does not implement the `Copy` trait + | +note: if `Enum` implemented `Clone`, you could clone the value + --> $DIR/issue-102389.rs:1:1 + | +LL | enum Enum { A, B, C } + | ^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/moves/issue-72649-uninit-in-loop.rs b/tests/ui/moves/issue-72649-uninit-in-loop.rs index 56c225bab8cbc..86f389cb3af1e 100644 --- a/tests/ui/moves/issue-72649-uninit-in-loop.rs +++ b/tests/ui/moves/issue-72649-uninit-in-loop.rs @@ -3,6 +3,10 @@ // 'value moved in previous iteration of loop' message struct NonCopy; +//~^ NOTE if `NonCopy` implemented `Clone` +//~| NOTE if `NonCopy` implemented `Clone` +//~| NOTE if `NonCopy` implemented `Clone` +//~| NOTE if `NonCopy` implemented `Clone` fn good() { loop { diff --git a/tests/ui/moves/issue-72649-uninit-in-loop.stderr b/tests/ui/moves/issue-72649-uninit-in-loop.stderr index 7e119fe8cda64..fe9fa6c0a07f9 100644 --- a/tests/ui/moves/issue-72649-uninit-in-loop.stderr +++ b/tests/ui/moves/issue-72649-uninit-in-loop.stderr @@ -1,5 +1,5 @@ error[E0382]: use of moved value: `value` - --> $DIR/issue-72649-uninit-in-loop.rs:20:22 + --> $DIR/issue-72649-uninit-in-loop.rs:24:22 | LL | let value = NonCopy{}; | ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait @@ -9,9 +9,15 @@ LL | let _used = value; LL | LL | let _used2 = value; | ^^^^^ value used here after move + | +note: if `NonCopy` implemented `Clone`, you could clone the value + --> $DIR/issue-72649-uninit-in-loop.rs:5:1 + | +LL | struct NonCopy; + | ^^^^^^^^^^^^^^ error[E0382]: use of moved value: `value` - --> $DIR/issue-72649-uninit-in-loop.rs:32:26 + --> $DIR/issue-72649-uninit-in-loop.rs:36:26 | LL | let value = NonCopy{}; | ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait @@ -23,9 +29,15 @@ LL | let _used = value; ... LL | let _used2 = value; | ^^^^^ value used here after move + | +note: if `NonCopy` implemented `Clone`, you could clone the value + --> $DIR/issue-72649-uninit-in-loop.rs:5:1 + | +LL | struct NonCopy; + | ^^^^^^^^^^^^^^ error[E0382]: use of moved value: `value` - --> $DIR/issue-72649-uninit-in-loop.rs:42:21 + --> $DIR/issue-72649-uninit-in-loop.rs:46:21 | LL | let value = NonCopy{}; | ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait @@ -34,9 +46,15 @@ LL | loop { | ---- inside of this loop LL | let _used = value; | ^^^^^ value moved here, in previous iteration of loop + | +note: if `NonCopy` implemented `Clone`, you could clone the value + --> $DIR/issue-72649-uninit-in-loop.rs:5:1 + | +LL | struct NonCopy; + | ^^^^^^^^^^^^^^ error[E0382]: use of moved value: `value` - --> $DIR/issue-72649-uninit-in-loop.rs:53:22 + --> $DIR/issue-72649-uninit-in-loop.rs:57:22 | LL | let mut value = NonCopy{}; | --------- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait @@ -45,9 +63,15 @@ LL | loop { | ---- inside of this loop LL | let _used2 = value; | ^^^^^ value moved here, in previous iteration of loop + | +note: if `NonCopy` implemented `Clone`, you could clone the value + --> $DIR/issue-72649-uninit-in-loop.rs:5:1 + | +LL | struct NonCopy; + | ^^^^^^^^^^^^^^ error[E0381]: used binding `value` isn't initialized - --> $DIR/issue-72649-uninit-in-loop.rs:61:21 + --> $DIR/issue-72649-uninit-in-loop.rs:65:21 | LL | let value: NonCopy; | ----- binding declared here but left uninitialized @@ -60,7 +84,7 @@ LL | let value: NonCopy = todo!(); | +++++++++ error[E0381]: used binding `value` isn't initialized - --> $DIR/issue-72649-uninit-in-loop.rs:69:21 + --> $DIR/issue-72649-uninit-in-loop.rs:73:21 | LL | let mut value: NonCopy; | --------- binding declared here but left uninitialized diff --git a/tests/ui/moves/move-fn-self-receiver.stderr b/tests/ui/moves/move-fn-self-receiver.stderr index 17f48f5f7bf71..e6bf52276ac2f 100644 --- a/tests/ui/moves/move-fn-self-receiver.stderr +++ b/tests/ui/moves/move-fn-self-receiver.stderr @@ -101,6 +101,12 @@ LL | mut_foo; | ^^^^^^^ move out of `mut_foo` occurs here LL | ret; | --- borrow later used here + | +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/move-fn-self-receiver.rs:5:1 + | +LL | struct Foo; + | ^^^^^^^^^^ error[E0382]: use of moved value: `rc_foo` --> $DIR/move-fn-self-receiver.rs:55:5 @@ -132,6 +138,11 @@ LL | foo_add + Foo; LL | foo_add; | ^^^^^^^ value used here after move | +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/move-fn-self-receiver.rs:5:1 + | +LL | struct Foo; + | ^^^^^^^^^^ note: calling this operator moves the left-hand side --> $SRC_DIR/core/src/ops/arith.rs:LL:COL diff --git a/tests/ui/moves/move-out-of-array-1.stderr b/tests/ui/moves/move-out-of-array-1.stderr index aa0251dbd8531..9e4a08e0cef54 100644 --- a/tests/ui/moves/move-out-of-array-1.stderr +++ b/tests/ui/moves/move-out-of-array-1.stderr @@ -6,6 +6,12 @@ LL | a[i] | | | cannot move out of here | move occurs because `a[_]` has type `D`, which does not implement the `Copy` trait + | +note: if `D` implemented `Clone`, you could clone the value + --> $DIR/move-out-of-array-1.rs:5:1 + | +LL | struct D { _x: u8 } + | ^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/nll/issue-21232-partial-init-and-use.stderr b/tests/ui/nll/issue-21232-partial-init-and-use.stderr index 97ed414b1eceb..2aff375f0a77c 100644 --- a/tests/ui/nll/issue-21232-partial-init-and-use.stderr +++ b/tests/ui/nll/issue-21232-partial-init-and-use.stderr @@ -27,6 +27,12 @@ LL | let mut s: S = S::new(); drop(s); | move occurs because `s` has type `S>`, which does not implement the `Copy` trait LL | s.x = 10; s.y = Box::new(20); | ^^^^^^^^ value partially assigned here after move + | +note: if `S>` implemented `Clone`, you could clone the value + --> $DIR/issue-21232-partial-init-and-use.rs:15:1 + | +LL | struct S { + | ^^^^^^^^^^^ error[E0382]: assign to part of moved value: `t` --> $DIR/issue-21232-partial-init-and-use.rs:116:5 @@ -72,6 +78,12 @@ LL | let mut s: S = S::new(); drop(s); | move occurs because `s` has type `S>`, which does not implement the `Copy` trait LL | s.x = 10; | ^^^^^^^^ value partially assigned here after move + | +note: if `S>` implemented `Clone`, you could clone the value + --> $DIR/issue-21232-partial-init-and-use.rs:15:1 + | +LL | struct S { + | ^^^^^^^^^^^ error[E0382]: assign to part of moved value: `t` --> $DIR/issue-21232-partial-init-and-use.rs:142:5 diff --git a/tests/ui/nll/move-errors.stderr b/tests/ui/nll/move-errors.stderr index 0d994ef29ba37..842ecaf524b47 100644 --- a/tests/ui/nll/move-errors.stderr +++ b/tests/ui/nll/move-errors.stderr @@ -4,6 +4,11 @@ error[E0507]: cannot move out of `*a` which is behind a shared reference LL | let b = *a; | ^^ move occurs because `*a` has type `A`, which does not implement the `Copy` trait | +note: if `A` implemented `Clone`, you could clone the value + --> $DIR/move-errors.rs:1:1 + | +LL | struct A(String); + | ^^^^^^^^ help: consider removing the dereference here | LL - let b = *a; @@ -19,6 +24,11 @@ LL | let b = a[0]; | cannot move out of here | move occurs because `a[_]` has type `A`, which does not implement the `Copy` trait | +note: if `A` implemented `Clone`, you could clone the value + --> $DIR/move-errors.rs:1:1 + | +LL | struct A(String); + | ^^^^^^^^ help: consider borrowing here | LL | let b = &a[0]; @@ -30,6 +40,11 @@ error[E0507]: cannot move out of `**r` which is behind a shared reference LL | let s = **r; | ^^^ move occurs because `**r` has type `A`, which does not implement the `Copy` trait | +note: if `A` implemented `Clone`, you could clone the value + --> $DIR/move-errors.rs:1:1 + | +LL | struct A(String); + | ^^^^^^^^ help: consider removing the dereference here | LL - let s = **r; @@ -42,6 +57,11 @@ error[E0507]: cannot move out of an `Rc` LL | let s = *r; | ^^ move occurs because value has type `A`, which does not implement the `Copy` trait | +note: if `A` implemented `Clone`, you could clone the value + --> $DIR/move-errors.rs:1:1 + | +LL | struct A(String); + | ^^^^^^^^ help: consider removing the dereference here | LL - let s = *r; @@ -57,6 +77,11 @@ LL | let a = [A("".to_string())][0]; | cannot move out of here | move occurs because value has type `A`, which does not implement the `Copy` trait | +note: if `A` implemented `Clone`, you could clone the value + --> $DIR/move-errors.rs:1:1 + | +LL | struct A(String); + | ^^^^^^^^ help: consider borrowing here | LL | let a = &[A("".to_string())][0]; @@ -96,6 +121,12 @@ error[E0507]: cannot move out of `*a` which is behind a shared reference | LL | b = *a; | ^^ move occurs because `*a` has type `A`, which does not implement the `Copy` trait + | +note: if `A` implemented `Clone`, you could clone the value + --> $DIR/move-errors.rs:1:1 + | +LL | struct A(String); + | ^^^^^^^^ error[E0508]: cannot move out of type `[B; 1]`, a non-copy array --> $DIR/move-errors.rs:74:11 diff --git a/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr b/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr index 9359244c6ebc1..00964cb8336ed 100644 --- a/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr +++ b/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr @@ -333,6 +333,11 @@ LL | ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { drop(b); false | ^ move occurs because `b` has type `&mut U`, which does not implement the `Copy` trait | = note: variables bound in patterns cannot be moved from until after the end of the pattern guard +note: if `U` implemented `Clone`, you could clone the value + --> $DIR/borrowck-pat-ref-mut-and-ref.rs:17:5 + | +LL | struct U; + | ^^^^^^^^ error[E0507]: cannot move out of `b` in pattern guard --> $DIR/borrowck-pat-ref-mut-and-ref.rs:103:66 @@ -341,6 +346,11 @@ LL | ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { drop(b); false | ^ move occurs because `b` has type `&mut U`, which does not implement the `Copy` trait | = note: variables bound in patterns cannot be moved from until after the end of the pattern guard +note: if `U` implemented `Clone`, you could clone the value + --> $DIR/borrowck-pat-ref-mut-and-ref.rs:17:5 + | +LL | struct U; + | ^^^^^^^^ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0507]: cannot move out of `a` in pattern guard diff --git a/tests/ui/union/union-borrow-move-parent-sibling.stderr b/tests/ui/union/union-borrow-move-parent-sibling.stderr index c9a440a66cc82..782fa63280ed6 100644 --- a/tests/ui/union/union-borrow-move-parent-sibling.stderr +++ b/tests/ui/union/union-borrow-move-parent-sibling.stderr @@ -50,6 +50,11 @@ error[E0507]: cannot move out of dereference of `ManuallyDrop<((MockVec, Moc LL | let a = (u.x.0).0; | ^^^^^^^^^ move occurs because value has type `MockVec`, which does not implement the `Copy` trait | +note: if `MockVec` implemented `Clone`, you could clone the value + --> $DIR/union-borrow-move-parent-sibling.rs:25:1 + | +LL | struct MockVec { + | ^^^^^^^^^^^^^^^^^ help: consider borrowing here | LL | let a = &(u.x.0).0; diff --git a/tests/ui/union/union-move.stderr b/tests/ui/union/union-move.stderr index 7d13094fbed98..5ebb2716e5a06 100644 --- a/tests/ui/union/union-move.stderr +++ b/tests/ui/union/union-move.stderr @@ -16,6 +16,11 @@ LL | fn move_out(x: T) {} | -------- ^ this parameter takes ownership of the value | | | in this function +note: if `U1` implemented `Clone`, you could clone the value + --> $DIR/union-move.rs:9:1 + | +LL | union U1 { + | ^^^^^^^^ error[E0382]: use of moved value: `x` --> $DIR/union-move.rs:42:18 @@ -35,6 +40,11 @@ LL | fn move_out(x: T) {} | -------- ^ this parameter takes ownership of the value | | | in this function +note: if `U1` implemented `Clone`, you could clone the value + --> $DIR/union-move.rs:9:1 + | +LL | union U1 { + | ^^^^^^^^ error[E0509]: cannot move out of type `U2`, which implements the `Drop` trait --> $DIR/union-move.rs:49:18 diff --git a/tests/ui/variance/variance-issue-20533.stderr b/tests/ui/variance/variance-issue-20533.stderr index 7060144c7760d..4515d313ec0a4 100644 --- a/tests/ui/variance/variance-issue-20533.stderr +++ b/tests/ui/variance/variance-issue-20533.stderr @@ -9,6 +9,12 @@ LL | drop(a); | ^ move out of `a` occurs here LL | drop(x); | - borrow later used here + | +note: if `AffineU32` implemented `Clone`, you could clone the value + --> $DIR/variance-issue-20533.rs:26:1 + | +LL | struct AffineU32(u32); + | ^^^^^^^^^^^^^^^^ error[E0505]: cannot move out of `a` because it is borrowed --> $DIR/variance-issue-20533.rs:41:14 @@ -21,6 +27,12 @@ LL | drop(a); | ^ move out of `a` occurs here LL | drop(x); | - borrow later used here + | +note: if `AffineU32` implemented `Clone`, you could clone the value + --> $DIR/variance-issue-20533.rs:26:1 + | +LL | struct AffineU32(u32); + | ^^^^^^^^^^^^^^^^ error[E0505]: cannot move out of `a` because it is borrowed --> $DIR/variance-issue-20533.rs:47:14 @@ -33,6 +45,12 @@ LL | drop(a); | ^ move out of `a` occurs here LL | drop(x); | - borrow later used here + | +note: if `AffineU32` implemented `Clone`, you could clone the value + --> $DIR/variance-issue-20533.rs:26:1 + | +LL | struct AffineU32(u32); + | ^^^^^^^^^^^^^^^^ error[E0505]: cannot move out of `a` because it is borrowed --> $DIR/variance-issue-20533.rs:53:14 From 3cdc6897c5ad21006a15a1bd567bfa5c2f3c9e49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 12 Apr 2024 04:45:50 +0000 Subject: [PATCH 17/19] Fix rebase --- compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 860f8882ad3ca..662bdce197f76 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1139,7 +1139,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if let Some(other_expr) = other_expr && let Some(parent_let) = self.infcx.tcx.hir().parent_iter(expr.hir_id).find_map(|n| { - if let (hir_id, hir::Node::Local(_) | hir::Node::Stmt(_)) = n { + if let (hir_id, hir::Node::LetStmt(_) | hir::Node::Stmt(_)) = n { Some(hir_id) } else { None @@ -1147,7 +1147,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { }) && let Some(other_parent_let) = self.infcx.tcx.hir().parent_iter(other_expr.hir_id).find_map(|n| { - if let (hir_id, hir::Node::Local(_) | hir::Node::Stmt(_)) = n { + if let (hir_id, hir::Node::LetStmt(_) | hir::Node::Stmt(_)) = n { Some(hir_id) } else { None From dea9b5031ccc8e199e920f8950e26e1b7dcdb7c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 12 Apr 2024 04:46:31 +0000 Subject: [PATCH 18/19] Better account for more cases involving closures --- .../src/diagnostics/conflict_errors.rs | 50 +++++++++++++------ .../borrowck-closures-slice-patterns.stderr | 11 ++++ tests/ui/borrowck/issue-101119.stderr | 14 ++++++ tests/ui/issues/issue-24357.rs | 2 +- tests/ui/issues/issue-24357.stderr | 6 +++ .../issue-75904-move-closure-loop.stderr | 7 +++ .../moves-based-on-type-capture-clause-bad.rs | 2 +- ...es-based-on-type-capture-clause-bad.stderr | 4 +- tests/ui/nll/closure-move-spans.fixed | 23 +++++++++ tests/ui/nll/closure-move-spans.rs | 2 + tests/ui/nll/closure-move-spans.stderr | 21 ++++++-- tests/ui/nll/closures-in-loops.stderr | 6 +++ ...e-27282-move-match-input-into-guard.stderr | 10 ++++ .../unboxed-closure-illegal-move.stderr | 10 ---- 14 files changed, 137 insertions(+), 31 deletions(-) create mode 100644 tests/ui/nll/closure-move-spans.fixed diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 662bdce197f76..ae17caf75b67e 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -203,13 +203,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if !seen_spans.contains(&move_span) { if !closure { - self.suggest_ref_or_clone( - mpi, - move_span, - &mut err, - &mut in_pattern, - move_spans, - ); + self.suggest_ref_or_clone(mpi, &mut err, &mut in_pattern, move_spans); } let msg_opt = CapturedMessageOpt { @@ -351,18 +345,28 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn suggest_ref_or_clone( &self, mpi: MovePathIndex, - move_span: Span, err: &mut Diag<'tcx>, in_pattern: &mut bool, move_spans: UseSpans<'_>, ) { + let move_span = match move_spans { + UseSpans::ClosureUse { capture_kind_span, .. } => capture_kind_span, + _ => move_spans.args_or_use(), + }; struct ExpressionFinder<'hir> { expr_span: Span, expr: Option<&'hir hir::Expr<'hir>>, pat: Option<&'hir hir::Pat<'hir>>, parent_pat: Option<&'hir hir::Pat<'hir>>, + hir: rustc_middle::hir::map::Map<'hir>, } impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> { + type NestedFilter = OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.hir + } + fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) { if e.span == self.expr_span { self.expr = Some(e); @@ -397,8 +401,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let expr = hir.body(body_id).value; let place = &self.move_data.move_paths[mpi].place; let span = place.as_local().map(|local| self.body.local_decls[local].source_info.span); - let mut finder = - ExpressionFinder { expr_span: move_span, expr: None, pat: None, parent_pat: None }; + let mut finder = ExpressionFinder { + expr_span: move_span, + expr: None, + pat: None, + parent_pat: None, + hir, + }; finder.visit_expr(expr); if let Some(span) = span && let Some(expr) = finder.expr @@ -479,12 +488,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } else if let UseSpans::ClosureUse { closure_kind: ClosureKind::Coroutine(CoroutineKind::Desugared(_, CoroutineSource::Block)), - args_span: _, - capture_kind_span: _, - path_span, + .. } = move_spans { - self.suggest_cloning(err, ty, expr, path_span); + self.suggest_cloning(err, ty, expr, None); } else if self.suggest_hoisting_call_outside_loop(err, expr) { // The place where the the type moves would be misleading to suggest clone. // #121466 @@ -1233,6 +1240,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } + fn in_move_closure(&self, expr: &hir::Expr<'_>) -> bool { + for (_, node) in self.infcx.tcx.hir().parent_iter(expr.hir_id) { + if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(closure), .. }) = node + && let hir::CaptureBy::Value { .. } = closure.capture_clause + { + // `move || x.clone()` will not work. FIXME: suggest `let y = x.clone(); move || y` + return true; + } + } + false + } + fn suggest_cloning_inner( &self, err: &mut Diag<'_>, @@ -1245,6 +1264,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // See `tests/ui/moves/needs-clone-through-deref.rs` return false; } + if self.in_move_closure(expr) { + return false; + } // Try to find predicates on *generic params* that would allow copying `ty` let suggestion = if let Some(symbol) = tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) { diff --git a/tests/ui/borrowck/borrowck-closures-slice-patterns.stderr b/tests/ui/borrowck/borrowck-closures-slice-patterns.stderr index 411d85b8e0562..9066891d2985f 100644 --- a/tests/ui/borrowck/borrowck-closures-slice-patterns.stderr +++ b/tests/ui/borrowck/borrowck-closures-slice-patterns.stderr @@ -38,6 +38,11 @@ LL | let [y, z @ ..] = x; LL | }; LL | &x; | ^^ value borrowed here after move + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let [y, z @ ..] = x.clone(); + | ++++++++ error[E0502]: cannot borrow `*x` as mutable because it is also borrowed as immutable --> $DIR/borrowck-closures-slice-patterns.rs:33:13 @@ -79,6 +84,12 @@ LL | let [y, z @ ..] = *x; LL | }; LL | &x; | ^^ value borrowed here after move + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let [y, z @ ..] = *x; +LL + let [y, z @ ..] = x.clone(); + | error[E0502]: cannot borrow `*x` as mutable because it is also borrowed as immutable --> $DIR/borrowck-closures-slice-patterns.rs:59:13 diff --git a/tests/ui/borrowck/issue-101119.stderr b/tests/ui/borrowck/issue-101119.stderr index 1f32ece3d3d29..b4775496f4f48 100644 --- a/tests/ui/borrowck/issue-101119.stderr +++ b/tests/ui/borrowck/issue-101119.stderr @@ -4,11 +4,25 @@ error[E0382]: use of moved value: `state` LL | fn fill_memory_blocks_mt(state: &mut State) { | ----- move occurs because `state` has type `&mut State`, which does not implement the `Copy` trait LL | loop { + | ---- inside of this loop LL | once(move || { | ^^^^^^^ value moved into closure here, in previous iteration of loop LL | LL | fill_segment(state); | ----- use occurs due to use in closure + | +note: consider changing this parameter type in function `fill_segment` to borrow instead if owning the value isn't necessary + --> $DIR/issue-101119.rs:14:20 + | +LL | fn fill_segment(_: &mut State) {} + | ------------ ^^^^^^^^^^ this parameter takes ownership of the value + | | + | in this function +note: if `State` implemented `Clone`, you could clone the value + --> $DIR/issue-101119.rs:1:1 + | +LL | struct State; + | ^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-24357.rs b/tests/ui/issues/issue-24357.rs index 152e69ebc873f..d1a9e37251ee7 100644 --- a/tests/ui/issues/issue-24357.rs +++ b/tests/ui/issues/issue-24357.rs @@ -1,4 +1,4 @@ -struct NoCopy; +struct NoCopy; //~ NOTE if `NoCopy` implemented `Clone`, you could clone the value fn main() { let x = NoCopy; //~^ NOTE move occurs because `x` has type `NoCopy` diff --git a/tests/ui/issues/issue-24357.stderr b/tests/ui/issues/issue-24357.stderr index 08a5a8ac56e1b..6d50eea7e21a0 100644 --- a/tests/ui/issues/issue-24357.stderr +++ b/tests/ui/issues/issue-24357.stderr @@ -11,6 +11,12 @@ LL | let f = move || { let y = x; }; ... LL | let z = x; | ^ value used here after move + | +note: if `NoCopy` implemented `Clone`, you could clone the value + --> $DIR/issue-24357.rs:1:1 + | +LL | struct NoCopy; + | ^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/moves/issue-75904-move-closure-loop.stderr b/tests/ui/moves/issue-75904-move-closure-loop.stderr index 6f04105a35edf..b6ad906bbdb09 100644 --- a/tests/ui/moves/issue-75904-move-closure-loop.stderr +++ b/tests/ui/moves/issue-75904-move-closure-loop.stderr @@ -4,11 +4,18 @@ error[E0382]: use of moved value: `a` LL | let mut a = NotCopy; | ----- move occurs because `a` has type `NotCopy`, which does not implement the `Copy` trait LL | loop { + | ---- inside of this loop LL | || { | ^^ value moved into closure here, in previous iteration of loop LL | &mut a; LL | a; | - use occurs due to use in closure + | +note: if `NotCopy` implemented `Clone`, you could clone the value + --> $DIR/issue-75904-move-closure-loop.rs:5:1 + | +LL | struct NotCopy; + | ^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/moves/moves-based-on-type-capture-clause-bad.rs b/tests/ui/moves/moves-based-on-type-capture-clause-bad.rs index b2f68352f896c..9d7277c1c2499 100644 --- a/tests/ui/moves/moves-based-on-type-capture-clause-bad.rs +++ b/tests/ui/moves/moves-based-on-type-capture-clause-bad.rs @@ -2,7 +2,7 @@ use std::thread; fn main() { let x = "Hello world!".to_string(); - thread::spawn(move|| { + thread::spawn(move || { println!("{}", x); }); println!("{}", x); //~ ERROR borrow of moved value diff --git a/tests/ui/moves/moves-based-on-type-capture-clause-bad.stderr b/tests/ui/moves/moves-based-on-type-capture-clause-bad.stderr index 5e527bf445ef4..c2b9aeab23748 100644 --- a/tests/ui/moves/moves-based-on-type-capture-clause-bad.stderr +++ b/tests/ui/moves/moves-based-on-type-capture-clause-bad.stderr @@ -3,8 +3,8 @@ error[E0382]: borrow of moved value: `x` | LL | let x = "Hello world!".to_string(); | - move occurs because `x` has type `String`, which does not implement the `Copy` trait -LL | thread::spawn(move|| { - | ------ value moved into closure here +LL | thread::spawn(move || { + | ------- value moved into closure here LL | println!("{}", x); | - variable moved due to use in closure LL | }); diff --git a/tests/ui/nll/closure-move-spans.fixed b/tests/ui/nll/closure-move-spans.fixed new file mode 100644 index 0000000000000..edd74e434e092 --- /dev/null +++ b/tests/ui/nll/closure-move-spans.fixed @@ -0,0 +1,23 @@ +// check that moves due to a closure capture give a special note +//@ run-rustfix +#![allow(unused_variables, unused_must_use, dead_code)] + +fn move_after_move(x: String) { + || x.clone(); + let y = x; //~ ERROR +} + +fn borrow_after_move(x: String) { + || x.clone(); + let y = &x; //~ ERROR +} + +fn borrow_mut_after_move(mut x: String) { + || x.clone(); + let y = &mut x; //~ ERROR +} + +fn fn_ref(f: F) -> F { f } +fn fn_mut(f: F) -> F { f } + +fn main() {} diff --git a/tests/ui/nll/closure-move-spans.rs b/tests/ui/nll/closure-move-spans.rs index bf2431870a942..bba5c3776e61c 100644 --- a/tests/ui/nll/closure-move-spans.rs +++ b/tests/ui/nll/closure-move-spans.rs @@ -1,4 +1,6 @@ // check that moves due to a closure capture give a special note +//@ run-rustfix +#![allow(unused_variables, unused_must_use, dead_code)] fn move_after_move(x: String) { || x; diff --git a/tests/ui/nll/closure-move-spans.stderr b/tests/ui/nll/closure-move-spans.stderr index 0446ef7b06657..0b1da57605c0b 100644 --- a/tests/ui/nll/closure-move-spans.stderr +++ b/tests/ui/nll/closure-move-spans.stderr @@ -1,5 +1,5 @@ error[E0382]: use of moved value: `x` - --> $DIR/closure-move-spans.rs:5:13 + --> $DIR/closure-move-spans.rs:7:13 | LL | fn move_after_move(x: String) { | - move occurs because `x` has type `String`, which does not implement the `Copy` trait @@ -9,9 +9,14 @@ LL | || x; | value moved into closure here LL | let y = x; | ^ value used here after move + | +help: consider cloning the value if the performance cost is acceptable + | +LL | || x.clone(); + | ++++++++ error[E0382]: borrow of moved value: `x` - --> $DIR/closure-move-spans.rs:10:13 + --> $DIR/closure-move-spans.rs:12:13 | LL | fn borrow_after_move(x: String) { | - move occurs because `x` has type `String`, which does not implement the `Copy` trait @@ -21,9 +26,14 @@ LL | || x; | value moved into closure here LL | let y = &x; | ^^ value borrowed here after move + | +help: consider cloning the value if the performance cost is acceptable + | +LL | || x.clone(); + | ++++++++ error[E0382]: borrow of moved value: `x` - --> $DIR/closure-move-spans.rs:15:13 + --> $DIR/closure-move-spans.rs:17:13 | LL | fn borrow_mut_after_move(mut x: String) { | ----- move occurs because `x` has type `String`, which does not implement the `Copy` trait @@ -33,6 +43,11 @@ LL | || x; | value moved into closure here LL | let y = &mut x; | ^^^^^^ value borrowed here after move + | +help: consider cloning the value if the performance cost is acceptable + | +LL | || x.clone(); + | ++++++++ error: aborting due to 3 previous errors diff --git a/tests/ui/nll/closures-in-loops.stderr b/tests/ui/nll/closures-in-loops.stderr index 2c1008c516c2d..050b220e62638 100644 --- a/tests/ui/nll/closures-in-loops.stderr +++ b/tests/ui/nll/closures-in-loops.stderr @@ -4,10 +4,16 @@ error[E0382]: use of moved value: `x` LL | fn repreated_move(x: String) { | - move occurs because `x` has type `String`, which does not implement the `Copy` trait LL | for i in 0..10 { + | -------------- inside of this loop LL | || x; | ^^ - use occurs due to use in closure | | | value moved into closure here, in previous iteration of loop + | +help: consider cloning the value if the performance cost is acceptable + | +LL | || x.clone(); + | ++++++++ error[E0499]: cannot borrow `x` as mutable more than once at a time --> $DIR/closures-in-loops.rs:13:16 diff --git a/tests/ui/nll/issue-27282-move-match-input-into-guard.stderr b/tests/ui/nll/issue-27282-move-match-input-into-guard.stderr index ae7978004576b..39ec45b20eae6 100644 --- a/tests/ui/nll/issue-27282-move-match-input-into-guard.stderr +++ b/tests/ui/nll/issue-27282-move-match-input-into-guard.stderr @@ -10,6 +10,11 @@ LL | _ if { (|| { let bar = b; *bar = false; })(); | -- - variable moved due to use in closure | | | value moved into closure here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | _ if { (|| { let bar = b.clone(); *bar = false; })(); + | ++++++++ error[E0382]: use of moved value: `b` --> $DIR/issue-27282-move-match-input-into-guard.rs:24:5 @@ -23,6 +28,11 @@ LL | (|| { let bar = b; *bar = false; })(); | -- - variable moved due to use in closure | | | value moved into closure here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | (|| { let bar = b.clone(); *bar = false; })(); + | ++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr b/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr index 5b995ff158536..cf4391311d03d 100644 --- a/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr +++ b/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr @@ -37,11 +37,6 @@ LL | let f = to_fn(move || drop(x)); | ------- ^ move occurs because `x` has type `Box`, which does not implement the `Copy` trait | | | captured by this `Fn` closure - | -help: consider cloning the value if the performance cost is acceptable - | -LL | let f = to_fn(move || drop(x.clone())); - | ++++++++ error[E0507]: cannot move out of `x`, a captured variable in an `FnMut` closure --> $DIR/unboxed-closure-illegal-move.rs:32:40 @@ -52,11 +47,6 @@ LL | let f = to_fn_mut(move || drop(x)); | ------- ^ move occurs because `x` has type `Box`, which does not implement the `Copy` trait | | | captured by this `FnMut` closure - | -help: consider cloning the value if the performance cost is acceptable - | -LL | let f = to_fn_mut(move || drop(x.clone())); - | ++++++++ error: aborting due to 4 previous errors From 4c7213c888174612250f77c29d929c16b331ade5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 12 Apr 2024 20:57:07 +0000 Subject: [PATCH 19/19] review comments Added comments and reworded messages --- .../src/diagnostics/conflict_errors.rs | 23 ++++++++++++------- .../rustc_borrowck/src/diagnostics/mod.rs | 21 +++++++++++++++++ .../borrowck-struct-update-with-dtor.stderr | 22 +++++++++--------- ...unctional-struct-update-noncopyable.stderr | 2 +- 4 files changed, 48 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index ae17caf75b67e..31bcb54c74b13 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1006,7 +1006,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { can_suggest_clone } - fn suggest_cloning_on_spread_operator( + /// We have `S { foo: val, ..base }`, and we suggest instead writing + /// `S { foo: val, bar: base.bar.clone(), .. }` when valid. + fn suggest_cloning_on_functional_record_update( &self, err: &mut Diag<'_>, ty: Ty<'tcx>, @@ -1046,7 +1048,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { for field in &variant.fields { // In practice unless there are more than one field with the same type, we'll be // suggesting a single field at a type, because we don't aggregate multiple borrow - // checker errors involving the spread operator into a single one. + // checker errors involving the functional record update sytnax into a single one. let field_ty = field.ty(self.infcx.tcx, args); let ident = field.ident(self.infcx.tcx); if field_ty == ty && fields.iter().all(|field| field.ident.name != ident.name) { @@ -1088,7 +1090,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { String::new() }; let msg = format!( - "{prefix}clone the value from the field instead of using the spread operator syntax", + "{prefix}clone the value from the field instead of using the functional record update \ + syntax", ); err.span_suggestion_verbose(span, msg, sugg, Applicability::MachineApplicable); } @@ -1105,7 +1108,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // `Location` that covers both the `S { ... }` literal, all of its fields and the // `base`. If the move happens because of `S { foo: val, bar: base.bar }` the `expr` // will already be correct. Instead, we see if we can suggest writing. - self.suggest_cloning_on_spread_operator(err, ty, expr); + self.suggest_cloning_on_functional_record_update(err, ty, expr); return; } @@ -1225,6 +1228,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { .must_apply_modulo_regions() } + /// Given an expression, check if it is a method call `foo.clone()`, where `foo` and + /// `foo.clone()` both have the same type, returning the span for `.clone()` if so. pub(crate) fn clone_on_reference(&self, expr: &hir::Expr<'_>) -> Option { let typeck_results = self.infcx.tcx.typeck(self.mir_def_id()); if let hir::ExprKind::MethodCall(segment, rcvr, args, span) = expr.kind @@ -1276,6 +1281,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { }; let mut sugg = Vec::with_capacity(2); let mut inner_expr = expr; + // Remove uses of `&` and `*` when suggesting `.clone()`. while let hir::ExprKind::AddrOf(.., inner) | hir::ExprKind::Unary(hir::UnOp::Deref, inner) = &inner_expr.kind { @@ -1841,13 +1847,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &self, err: &mut Diag<'_>, ty: Ty<'tcx>, - def_id: DefId, + trait_def_id: DefId, span: Span, ) { - self.suggest_adding_bounds(err, ty, def_id, span); + self.suggest_adding_bounds(err, ty, trait_def_id, span); if let ty::Adt(..) = ty.kind() { - // The type doesn't implement DefId. - let trait_ref = ty::Binder::dummy(ty::TraitRef::new(self.infcx.tcx, def_id, [ty])); + // The type doesn't implement the trait. + let trait_ref = + ty::Binder::dummy(ty::TraitRef::new(self.infcx.tcx, trait_def_id, [ty])); let obligation = Obligation::new( self.infcx.tcx, ObligationCause::dummy(), diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 53697b4b8b04a..dbea317e7bb26 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -1049,6 +1049,27 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { is_loop_message, }, ); + // Check if the move occurs on a value because of a call on a closure that comes + // from a type parameter `F: FnOnce()`. If so, we provide a targeted `note`: + // ``` + // error[E0382]: use of moved value: `blk` + // --> $DIR/once-cant-call-twice-on-heap.rs:8:5 + // | + // LL | fn foo(blk: F) { + // | --- move occurs because `blk` has type `F`, which does not implement the `Copy` trait + // LL | blk(); + // | ----- `blk` moved due to this call + // LL | blk(); + // | ^^^ value used here after move + // | + // note: `FnOnce` closures can only be called once + // --> $DIR/once-cant-call-twice-on-heap.rs:6:10 + // | + // LL | fn foo(blk: F) { + // | ^^^^^^^^ `F` is made to be an `FnOnce` closure here + // LL | blk(); + // | ----- this value implements `FnOnce`, which causes it to be moved when called + // ``` if let ty::Param(param_ty) = self_ty.kind() && let generics = self.infcx.tcx.generics_of(self.mir_def_id()) && let param = generics.type_param(param_ty, self.infcx.tcx) diff --git a/tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr b/tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr index d34b07fbcc6d9..bc11204acf288 100644 --- a/tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr +++ b/tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr @@ -12,7 +12,7 @@ note: `B` doesn't implement `Copy` or `Clone` | LL | struct B; | ^^^^^^^^ -help: if `B` implemented `Clone`, you could clone the value from the field instead of using the spread operator syntax +help: if `B` implemented `Clone`, you could clone the value from the field instead of using the functional record update syntax | LL | let _s2 = S { a: 2, b: s0.b.clone(), ..s0 }; | +++++++++++++++++ @@ -31,7 +31,7 @@ note: `B` doesn't implement `Copy` or `Clone` | LL | struct B; | ^^^^^^^^ -help: if `B` implemented `Clone`, you could clone the value from the field instead of using the spread operator syntax +help: if `B` implemented `Clone`, you could clone the value from the field instead of using the functional record update syntax | LL | let _s2 = S { a: 2, b: s0.b.clone(), c: s0.c.clone() }; | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -50,7 +50,7 @@ note: `B` doesn't implement `Copy` or `Clone` | LL | struct B; | ^^^^^^^^ -help: if `B` implemented `Clone`, you could clone the value from the field instead of using the spread operator syntax +help: if `B` implemented `Clone`, you could clone the value from the field instead of using the functional record update syntax | LL | let _s2 = S { a: 2, b: s0.b.clone(), c: s0.c.clone() }; | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -69,7 +69,7 @@ note: `B` doesn't implement `Copy` or `Clone` | LL | struct B; | ^^^^^^^^ -help: if `B` implemented `Clone`, you could clone the value from the field instead of using the spread operator syntax +help: if `B` implemented `Clone`, you could clone the value from the field instead of using the functional record update syntax | LL | let _s2 = S { a: 2, b: s0.b.clone(), ..s0 }; | +++++++++++++++++ @@ -83,7 +83,7 @@ LL | let _s2 = S { a: 2, ..s0 }; | cannot move out of here | move occurs because `s0.c` has type `K`, which does not implement the `Copy` trait | -help: clone the value from the field instead of using the spread operator syntax +help: clone the value from the field instead of using the functional record update syntax | LL | let _s2 = S { a: 2, c: s0.c.clone(), ..s0 }; | +++++++++++++++++ @@ -97,7 +97,7 @@ LL | let _s2 = T { a: 2, ..s0 }; | cannot move out of here | move occurs because `s0.b` has type `Box`, which does not implement the `Copy` trait | -help: clone the value from the field instead of using the spread operator syntax +help: clone the value from the field instead of using the functional record update syntax | LL | let _s2 = T { a: 2, b: s0.b.clone() }; | ~~~~~~~~~~~~~~~~~ @@ -111,7 +111,7 @@ LL | let _s2 = T { ..s0 }; | cannot move out of here | move occurs because `s0.b` has type `Box`, which does not implement the `Copy` trait | -help: clone the value from the field instead of using the spread operator syntax +help: clone the value from the field instead of using the functional record update syntax | LL | let _s2 = T { b: s0.b.clone(), ..s0 }; | ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -139,7 +139,7 @@ LL | let _s2 = V { a: 2, ..s0 }; | cannot move out of here | move occurs because `s0.b` has type `Box`, which does not implement the `Copy` trait | -help: clone the value from the field instead of using the spread operator syntax +help: clone the value from the field instead of using the functional record update syntax | LL | let _s2 = V { a: 2, b: s0.b.clone(), ..s0 }; | +++++++++++++++++ @@ -153,7 +153,7 @@ LL | let _s2 = V { a: 2, ..s0 }; | cannot move out of here | move occurs because `s0.c` has type `K`, which does not implement the `Copy` trait | -help: clone the value from the field instead of using the spread operator syntax +help: clone the value from the field instead of using the functional record update syntax | LL | let _s2 = V { a: 2, c: s0.c.clone(), ..s0 }; | +++++++++++++++++ @@ -167,7 +167,7 @@ LL | let _s2 = V { a: 2, ..s0 }; | cannot move out of here | move occurs because `s0.b` has type `Box`, which does not implement the `Copy` trait | -help: clone the value from the field instead of using the spread operator syntax +help: clone the value from the field instead of using the functional record update syntax | LL | let _s2 = V { a: 2, b: s0.b.clone(), ..s0 }; | +++++++++++++++++ @@ -181,7 +181,7 @@ LL | let _s2 = V { a: 2, ..s0 }; | cannot move out of here | move occurs because `s0.c` has type `Clonable`, which does not implement the `Copy` trait | -help: clone the value from the field instead of using the spread operator syntax +help: clone the value from the field instead of using the functional record update syntax | LL | let _s2 = V { a: 2, c: s0.c.clone(), ..s0 }; | +++++++++++++++++ diff --git a/tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr b/tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr index 3f07142eeb729..d167a60dad387 100644 --- a/tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr +++ b/tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr @@ -7,7 +7,7 @@ LL | let _b = A { y: Arc::new(3), ..a }; | cannot move out of here | move occurs because `a.x` has type `Arc`, which does not implement the `Copy` trait | -help: clone the value from the field instead of using the spread operator syntax +help: clone the value from the field instead of using the functional record update syntax | LL | let _b = A { y: Arc::new(3), x: a.x.clone() }; | ~~~~~~~~~~~~~~~~