From 520461f1fb2730f8edb17922f3bcc74fccdc52d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 19 Jun 2020 22:45:09 -0700 Subject: [PATCH] Provide suggestions for some moved value errors When encountering an used moved value where the previous move happened in a `match` or `if let` pattern, suggest using `ref`. Fix #63988. When encountering a `&mut` value that is used in multiple iterations of a loop, suggest reborrowing it with `&mut *`. Fix #62112. --- .../diagnostics/conflict_errors.rs | 39 +++++++++++++++++-- .../diagnostics/explain_borrow.rs | 2 +- .../borrow_check/diagnostics/mod.rs | 20 +++++++--- src/test/ui/borrowck/issue-41962.stderr | 4 ++ src/test/ui/borrowck/move-in-pattern-mut.rs | 23 +++++++++++ .../ui/borrowck/move-in-pattern-mut.stderr | 33 ++++++++++++++++ src/test/ui/borrowck/move-in-pattern.fixed | 24 ++++++++++++ src/test/ui/borrowck/move-in-pattern.rs | 24 ++++++++++++ src/test/ui/borrowck/move-in-pattern.stderr | 33 ++++++++++++++++ .../ui/borrowck/mut-borrow-in-loop-2.fixed | 35 +++++++++++++++++ src/test/ui/borrowck/mut-borrow-in-loop-2.rs | 35 +++++++++++++++++ .../ui/borrowck/mut-borrow-in-loop-2.stderr | 17 ++++++++ ...sed-on-type-cyclic-types-issue-4821.stderr | 4 ++ src/test/ui/nll/issue-53807.stderr | 4 ++ ...can-live-while-the-other-survives-1.stderr | 8 ++++ ...orrowck-pat-by-move-and-ref-inverse.stderr | 16 ++++++++ src/test/ui/ref-suggestion.stderr | 4 ++ 17 files changed, 315 insertions(+), 10 deletions(-) create mode 100644 src/test/ui/borrowck/move-in-pattern-mut.rs create mode 100644 src/test/ui/borrowck/move-in-pattern-mut.stderr create mode 100644 src/test/ui/borrowck/move-in-pattern.fixed create mode 100644 src/test/ui/borrowck/move-in-pattern.rs create mode 100644 src/test/ui/borrowck/move-in-pattern.stderr create mode 100644 src/test/ui/borrowck/mut-borrow-in-loop-2.fixed create mode 100644 src/test/ui/borrowck/mut-borrow-in-loop-2.rs create mode 100644 src/test/ui/borrowck/mut-borrow-in-loop-2.stderr diff --git a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs b/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs index 8d7944004c75e..60a1fe0b19870 100644 --- a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs @@ -156,6 +156,20 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { format!("variable moved due to use{}", move_spans.describe()), ); } + if let UseSpans::PatUse(span) = move_spans { + err.span_suggestion_verbose( + span.shrink_to_lo(), + &format!( + "borrow this field in the pattern to avoid moving {}", + self.describe_place(moved_place.as_ref()) + .map(|n| format!("`{}`", n)) + .unwrap_or_else(|| "the value".to_string()) + ), + "ref ".to_string(), + Applicability::MachineApplicable, + ); + } + if Some(DesugaringKind::ForLoop) == move_span.desugaring_kind() { let sess = self.infcx.tcx.sess; if let Ok(snippet) = sess.source_map().span_to_snippet(move_span) { @@ -198,11 +212,28 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { _ => true, }; - if needs_note { - let mpi = self.move_data.moves[move_out_indices[0]].path; - let place = &self.move_data.move_paths[mpi].place; + let mpi = self.move_data.moves[move_out_indices[0]].path; + let place = &self.move_data.move_paths[mpi].place; + let ty = place.ty(self.body, self.infcx.tcx).ty; + + if is_loop_move { + if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind { + // We have a `&mut` ref, we need to reborrow on each iteration (#62112). + err.span_suggestion_verbose( + span.shrink_to_lo(), + &format!( + "consider creating a fresh reborrow of {} here", + self.describe_place(moved_place) + .map(|n| format!("`{}`", n)) + .unwrap_or_else(|| "the mutable reference".to_string()), + ), + "&mut *".to_string(), + Applicability::MachineApplicable, + ); + } + } - let ty = place.ty(self.body, self.infcx.tcx).ty; + if needs_note { let opt_name = self.describe_place_with_options(place.as_ref(), IncludingDowncast(true)); let note_msg = match opt_name { diff --git a/src/librustc_mir/borrow_check/diagnostics/explain_borrow.rs b/src/librustc_mir/borrow_check/diagnostics/explain_borrow.rs index 5253acbba7f1c..849fd63998db4 100644 --- a/src/librustc_mir/borrow_check/diagnostics/explain_borrow.rs +++ b/src/librustc_mir/borrow_check/diagnostics/explain_borrow.rs @@ -509,7 +509,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // Used in a closure. (LaterUseKind::ClosureCapture, var_span) } - UseSpans::OtherUse(span) => { + UseSpans::PatUse(span) | UseSpans::OtherUse(span) => { let block = &self.body.basic_blocks()[location.block]; let kind = if let Some(&Statement { diff --git a/src/librustc_mir/borrow_check/diagnostics/mod.rs b/src/librustc_mir/borrow_check/diagnostics/mod.rs index ca8e54ea28649..388076a9d60af 100644 --- a/src/librustc_mir/borrow_check/diagnostics/mod.rs +++ b/src/librustc_mir/borrow_check/diagnostics/mod.rs @@ -542,20 +542,26 @@ pub(super) enum UseSpans { // The span of the first use of the captured variable inside the closure. var_span: Span, }, - // This access has a single span associated to it: common case. + /// This access is caused by a `match` or `if let` pattern. + PatUse(Span), + /// This access has a single span associated to it: common case. OtherUse(Span), } impl UseSpans { pub(super) fn args_or_use(self) -> Span { match self { - UseSpans::ClosureUse { args_span: span, .. } | UseSpans::OtherUse(span) => span, + UseSpans::ClosureUse { args_span: span, .. } + | UseSpans::PatUse(span) + | UseSpans::OtherUse(span) => span, } } pub(super) fn var_or_use(self) -> Span { match self { - UseSpans::ClosureUse { var_span: span, .. } | UseSpans::OtherUse(span) => span, + UseSpans::ClosureUse { var_span: span, .. } + | UseSpans::PatUse(span) + | UseSpans::OtherUse(span) => span, } } @@ -624,7 +630,7 @@ impl UseSpans { { match self { closure @ UseSpans::ClosureUse { .. } => closure, - UseSpans::OtherUse(_) => if_other(), + UseSpans::PatUse(_) | UseSpans::OtherUse(_) => if_other(), } } } @@ -741,7 +747,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } - OtherUse(stmt.source_info.span) + if moved_place.projection.iter().any(|p| matches!(p, ProjectionElem::Downcast(..))) { + PatUse(stmt.source_info.span) + } else { + OtherUse(stmt.source_info.span) + } } /// Finds the span of arguments of a closure (within `maybe_closure_span`) diff --git a/src/test/ui/borrowck/issue-41962.stderr b/src/test/ui/borrowck/issue-41962.stderr index 422d1605aa46b..604143b4e7efd 100644 --- a/src/test/ui/borrowck/issue-41962.stderr +++ b/src/test/ui/borrowck/issue-41962.stderr @@ -5,6 +5,10 @@ LL | if let Some(thing) = maybe { | ^^^^^ value moved here, in previous iteration of loop | = note: move occurs because value has type `std::vec::Vec`, which does not implement the `Copy` trait +help: borrow this field in the pattern to avoid moving `maybe.0` + | +LL | if let Some(ref thing) = maybe { + | ^^^ error: aborting due to previous error diff --git a/src/test/ui/borrowck/move-in-pattern-mut.rs b/src/test/ui/borrowck/move-in-pattern-mut.rs new file mode 100644 index 0000000000000..175eb3b7a04d1 --- /dev/null +++ b/src/test/ui/borrowck/move-in-pattern-mut.rs @@ -0,0 +1,23 @@ +// Issue #63988 +#[derive(Debug)] +struct S; +fn foo(_: Option) {} + +enum E { + V { + s: S, + } +} +fn bar(_: E) {} + +fn main() { + let s = Some(S); + if let Some(mut x) = s { + x = S; + } + foo(s); //~ ERROR use of moved value: `s` + let mut e = E::V { s: S }; + let E::V { s: mut x } = e; + x = S; + bar(e); //~ ERROR use of moved value: `e` +} diff --git a/src/test/ui/borrowck/move-in-pattern-mut.stderr b/src/test/ui/borrowck/move-in-pattern-mut.stderr new file mode 100644 index 0000000000000..391638444c3bd --- /dev/null +++ b/src/test/ui/borrowck/move-in-pattern-mut.stderr @@ -0,0 +1,33 @@ +error[E0382]: use of moved value: `s` + --> $DIR/move-in-pattern-mut.rs:18:9 + | +LL | if let Some(mut x) = s { + | ----- value moved here +... +LL | foo(s); + | ^ value used here after partial move + | + = note: move occurs because value has type `S`, which does not implement the `Copy` trait +help: borrow this field in the pattern to avoid moving `s.0` + | +LL | if let Some(ref mut x) = s { + | ^^^ + +error[E0382]: use of moved value: `e` + --> $DIR/move-in-pattern-mut.rs:22:9 + | +LL | let E::V { s: mut x } = e; + | ----- value moved here +LL | x = S; +LL | bar(e); + | ^ value used here after partial move + | + = note: move occurs because value has type `S`, which does not implement the `Copy` trait +help: borrow this field in the pattern to avoid moving `e.s` + | +LL | let E::V { s: ref mut x } = e; + | ^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/borrowck/move-in-pattern.fixed b/src/test/ui/borrowck/move-in-pattern.fixed new file mode 100644 index 0000000000000..f55fdcc5f90e8 --- /dev/null +++ b/src/test/ui/borrowck/move-in-pattern.fixed @@ -0,0 +1,24 @@ +// run-rustfix +// Issue #63988 +#[derive(Debug)] +struct S; +fn foo(_: Option) {} + +enum E { + V { + s: S, + } +} +fn bar(_: E) {} + +fn main() { + let s = Some(S); + if let Some(ref x) = s { + let _ = x; + } + foo(s); //~ ERROR use of moved value: `s` + let e = E::V { s: S }; + let E::V { s: ref x } = e; + let _ = x; + bar(e); //~ ERROR use of moved value: `e` +} diff --git a/src/test/ui/borrowck/move-in-pattern.rs b/src/test/ui/borrowck/move-in-pattern.rs new file mode 100644 index 0000000000000..7ad04b9490c25 --- /dev/null +++ b/src/test/ui/borrowck/move-in-pattern.rs @@ -0,0 +1,24 @@ +// run-rustfix +// Issue #63988 +#[derive(Debug)] +struct S; +fn foo(_: Option) {} + +enum E { + V { + s: S, + } +} +fn bar(_: E) {} + +fn main() { + let s = Some(S); + if let Some(x) = s { + let _ = x; + } + foo(s); //~ ERROR use of moved value: `s` + let e = E::V { s: S }; + let E::V { s: x } = e; + let _ = x; + bar(e); //~ ERROR use of moved value: `e` +} diff --git a/src/test/ui/borrowck/move-in-pattern.stderr b/src/test/ui/borrowck/move-in-pattern.stderr new file mode 100644 index 0000000000000..c5cb24455eb61 --- /dev/null +++ b/src/test/ui/borrowck/move-in-pattern.stderr @@ -0,0 +1,33 @@ +error[E0382]: use of moved value: `s` + --> $DIR/move-in-pattern.rs:19:9 + | +LL | if let Some(x) = s { + | - value moved here +... +LL | foo(s); + | ^ value used here after partial move + | + = note: move occurs because value has type `S`, which does not implement the `Copy` trait +help: borrow this field in the pattern to avoid moving `s.0` + | +LL | if let Some(ref x) = s { + | ^^^ + +error[E0382]: use of moved value: `e` + --> $DIR/move-in-pattern.rs:23:9 + | +LL | let E::V { s: x } = e; + | - value moved here +LL | let _ = x; +LL | bar(e); + | ^ value used here after partial move + | + = note: move occurs because value has type `S`, which does not implement the `Copy` trait +help: borrow this field in the pattern to avoid moving `e.s` + | +LL | let E::V { s: ref x } = e; + | ^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/borrowck/mut-borrow-in-loop-2.fixed b/src/test/ui/borrowck/mut-borrow-in-loop-2.fixed new file mode 100644 index 0000000000000..ceeba30a90f29 --- /dev/null +++ b/src/test/ui/borrowck/mut-borrow-in-loop-2.fixed @@ -0,0 +1,35 @@ +// run-rustfix +#![allow(dead_code)] + +struct Events(R); + +struct Other; + +pub trait Trait { + fn handle(value: T) -> Self; +} + +// Blanket impl. (If you comment this out, compiler figures out that it +// is passing an `&mut` to a method that must be expecting an `&mut`, +// and injects an auto-reborrow.) +impl Trait for T where T: From { + fn handle(_: U) -> Self { unimplemented!() } +} + +impl<'a, R> Trait<&'a mut Events> for Other { + fn handle(_: &'a mut Events) -> Self { unimplemented!() } +} + +fn this_compiles<'a, R>(value: &'a mut Events) { + for _ in 0..3 { + Other::handle(&mut *value); + } +} + +fn this_does_not<'a, R>(value: &'a mut Events) { + for _ in 0..3 { + Other::handle(&mut *value); //~ ERROR use of moved value: `value` + } +} + +fn main() {} diff --git a/src/test/ui/borrowck/mut-borrow-in-loop-2.rs b/src/test/ui/borrowck/mut-borrow-in-loop-2.rs new file mode 100644 index 0000000000000..d13fb7e567939 --- /dev/null +++ b/src/test/ui/borrowck/mut-borrow-in-loop-2.rs @@ -0,0 +1,35 @@ +// run-rustfix +#![allow(dead_code)] + +struct Events(R); + +struct Other; + +pub trait Trait { + fn handle(value: T) -> Self; +} + +// Blanket impl. (If you comment this out, compiler figures out that it +// is passing an `&mut` to a method that must be expecting an `&mut`, +// and injects an auto-reborrow.) +impl Trait for T where T: From { + fn handle(_: U) -> Self { unimplemented!() } +} + +impl<'a, R> Trait<&'a mut Events> for Other { + fn handle(_: &'a mut Events) -> Self { unimplemented!() } +} + +fn this_compiles<'a, R>(value: &'a mut Events) { + for _ in 0..3 { + Other::handle(&mut *value); + } +} + +fn this_does_not<'a, R>(value: &'a mut Events) { + for _ in 0..3 { + Other::handle(value); //~ ERROR use of moved value: `value` + } +} + +fn main() {} diff --git a/src/test/ui/borrowck/mut-borrow-in-loop-2.stderr b/src/test/ui/borrowck/mut-borrow-in-loop-2.stderr new file mode 100644 index 0000000000000..fa1b741394acb --- /dev/null +++ b/src/test/ui/borrowck/mut-borrow-in-loop-2.stderr @@ -0,0 +1,17 @@ +error[E0382]: use of moved value: `value` + --> $DIR/mut-borrow-in-loop-2.rs:31:23 + | +LL | fn this_does_not<'a, R>(value: &'a mut Events) { + | ----- move occurs because `value` has type `&mut Events`, which does not implement the `Copy` trait +LL | for _ in 0..3 { +LL | Other::handle(value); + | ^^^^^ value moved here, in previous iteration of loop + | +help: consider creating a fresh reborrow of `value` here + | +LL | Other::handle(&mut *value); + | ^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.stderr b/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.stderr index fb8562d00ead1..952985fcddee6 100644 --- a/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.stderr +++ b/src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.stderr @@ -8,6 +8,10 @@ LL | consume(node) + r | ^^^^ value used here after partial move | = note: move occurs because value has type `std::boxed::Box`, which does not implement the `Copy` trait +help: borrow this field in the pattern to avoid moving `node.next.0` + | +LL | Some(ref right) => consume(right), + | ^^^ error: aborting due to previous error diff --git a/src/test/ui/nll/issue-53807.stderr b/src/test/ui/nll/issue-53807.stderr index 2b15da3710e62..4f36a4ccab28f 100644 --- a/src/test/ui/nll/issue-53807.stderr +++ b/src/test/ui/nll/issue-53807.stderr @@ -5,6 +5,10 @@ LL | if let Some(thing) = maybe { | ^^^^^ value moved here, in previous iteration of loop | = note: move occurs because value has type `std::vec::Vec`, which does not implement the `Copy` trait +help: borrow this field in the pattern to avoid moving `maybe.0` + | +LL | if let Some(ref thing) = maybe { + | ^^^ error: aborting due to previous error diff --git a/src/test/ui/pattern/bindings-after-at/bind-by-move-neither-can-live-while-the-other-survives-1.stderr b/src/test/ui/pattern/bindings-after-at/bind-by-move-neither-can-live-while-the-other-survives-1.stderr index f2186b9298e68..8a6ea8e91a25a 100644 --- a/src/test/ui/pattern/bindings-after-at/bind-by-move-neither-can-live-while-the-other-survives-1.stderr +++ b/src/test/ui/pattern/bindings-after-at/bind-by-move-neither-can-live-while-the-other-survives-1.stderr @@ -46,6 +46,10 @@ LL | Some(_z @ ref _y) => {} | value moved here | = note: move occurs because value has type `X`, which does not implement the `Copy` trait +help: borrow this field in the pattern to avoid moving `x.0` + | +LL | Some(ref _z @ ref _y) => {} + | ^^^ error[E0382]: borrow of moved value --> $DIR/bind-by-move-neither-can-live-while-the-other-survives-1.rs:35:19 @@ -57,6 +61,10 @@ LL | Some(_z @ ref mut _y) => {} | value moved here | = note: move occurs because value has type `X`, which does not implement the `Copy` trait +help: borrow this field in the pattern to avoid moving `x.0` + | +LL | Some(ref _z @ ref mut _y) => {} + | ^^^ error: aborting due to 6 previous errors diff --git a/src/test/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.stderr b/src/test/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.stderr index f819e671436ec..5058998f2a7c1 100644 --- a/src/test/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.stderr +++ b/src/test/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.stderr @@ -357,6 +357,10 @@ LL | a @ Some((mut b @ ref mut c, d @ ref e)) => {} | value moved here | = note: move occurs because value has type `main::U`, which does not implement the `Copy` trait +help: borrow this field in the pattern to avoid moving the value + | +LL | a @ Some((ref mut b @ ref mut c, d @ ref e)) => {} + | ^^^ error[E0382]: use of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:61:38 @@ -379,6 +383,10 @@ LL | a @ Some((mut b @ ref mut c, d @ ref e)) => {} | value moved here | = note: move occurs because value has type `main::U`, which does not implement the `Copy` trait +help: borrow this field in the pattern to avoid moving the value + | +LL | a @ Some((mut b @ ref mut c, ref d @ ref e)) => {} + | ^^^ error[E0382]: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:71:30 @@ -412,6 +420,10 @@ LL | a @ Some((mut b @ ref mut c, d @ ref e)) => {} | value moved here | = note: move occurs because value has type `main::U`, which does not implement the `Copy` trait +help: borrow this field in the pattern to avoid moving the value + | +LL | a @ Some((ref mut b @ ref mut c, d @ ref e)) => {} + | ^^^ error[E0382]: use of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:83:38 @@ -434,6 +446,10 @@ LL | a @ Some((mut b @ ref mut c, d @ ref e)) => {} | value moved here | = note: move occurs because value has type `main::U`, which does not implement the `Copy` trait +help: borrow this field in the pattern to avoid moving the value + | +LL | a @ Some((mut b @ ref mut c, ref d @ ref e)) => {} + | ^^^ error[E0382]: borrow of moved value --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:93:30 diff --git a/src/test/ui/ref-suggestion.stderr b/src/test/ui/ref-suggestion.stderr index 9ff8e21bb58bd..97d2c174d9adb 100644 --- a/src/test/ui/ref-suggestion.stderr +++ b/src/test/ui/ref-suggestion.stderr @@ -28,6 +28,10 @@ LL | x; | ^ value used here after partial move | = note: move occurs because value has type `std::vec::Vec`, which does not implement the `Copy` trait +help: borrow this field in the pattern to avoid moving `x.0.0` + | +LL | (Some(ref y), ()) => {}, + | ^^^ error: aborting due to 3 previous errors