diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 89e008f06ebc8..49a1817311627 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -678,7 +678,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diag<'_>, span: Span) { match error { GroupedMoveError::MovesFromPlace { mut binds_to, move_from, .. } => { - self.add_borrow_suggestions(err, span); + self.add_borrow_suggestions(err, span, !binds_to.is_empty()); if binds_to.is_empty() { let place_ty = move_from.ty(self.body, self.infcx.tcx).ty; let place_desc = match self.describe_place(move_from.as_ref()) { @@ -787,29 +787,67 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } } - fn add_borrow_suggestions(&self, err: &mut Diag<'_>, span: Span) { + fn add_borrow_suggestions( + &self, + err: &mut Diag<'_>, + span: Span, + is_destructuring_pattern_move: bool, + ) { match self.infcx.tcx.sess.source_map().span_to_snippet(span) { Ok(snippet) if snippet.starts_with('*') => { let sp = span.with_lo(span.lo() + BytePos(1)); let inner = self.find_expr(sp); let mut is_raw_ptr = false; + let mut is_ref = false; + let mut is_destructuring_assignment = false; + let mut is_nested_deref = false; if let Some(inner) = inner { + is_nested_deref = + matches!(inner.kind, hir::ExprKind::Unary(hir::UnOp::Deref, _)); let typck_result = self.infcx.tcx.typeck(self.mir_def_id()); if let Some(inner_type) = typck_result.node_type_opt(inner.hir_id) { if matches!(inner_type.kind(), ty::RawPtr(..)) { is_raw_ptr = true; + } else if matches!(inner_type.kind(), ty::Ref(..)) { + is_ref = true; } } + is_destructuring_assignment = + self.infcx.tcx.hir_parent_iter(inner.hir_id).any(|(_, node)| { + matches!( + node, + hir::Node::LetStmt(&hir::LetStmt { + source: hir::LocalSource::AssignDesugar, + .. + }) + ) + }); } // If the `inner` is a raw pointer, do not suggest removing the "*", see #126863 // FIXME: need to check whether the assigned object can be a raw pointer, see `tests/ui/borrowck/issue-20801.rs`. - if !is_raw_ptr { + if is_raw_ptr { + return; + } + + if !is_destructuring_pattern_move || is_ref { err.span_suggestion_verbose( span.with_hi(span.lo() + BytePos(1)), "consider removing the dereference here", String::new(), Applicability::MaybeIncorrect, ); + } else if !is_destructuring_assignment && !is_nested_deref { + err.span_suggestion_verbose( + span.shrink_to_lo(), + "consider borrowing here", + '&', + Applicability::MaybeIncorrect, + ); + } else { + err.span_help( + span, + "destructuring assignment cannot borrow from this expression; consider using a `let` binding instead", + ); } } _ => { diff --git a/tests/ui/suggestions/borrow-from-overloaded-deref-assign-issue-154826.rs b/tests/ui/suggestions/borrow-from-overloaded-deref-assign-issue-154826.rs new file mode 100644 index 0000000000000..77bd286697ebf --- /dev/null +++ b/tests/ui/suggestions/borrow-from-overloaded-deref-assign-issue-154826.rs @@ -0,0 +1,12 @@ +// Regression test for #154826. + +use std::rc::Rc; + +struct NonCopy; + +fn main() { + let b: NonCopy; + (b,) = *Rc::new((NonCopy,)); + //~^ ERROR cannot move out of an `Rc` + //~| HELP destructuring assignment cannot borrow from this expression; consider using a `let` binding instead +} diff --git a/tests/ui/suggestions/borrow-from-overloaded-deref-assign-issue-154826.stderr b/tests/ui/suggestions/borrow-from-overloaded-deref-assign-issue-154826.stderr new file mode 100644 index 0000000000000..8022e7384f3e1 --- /dev/null +++ b/tests/ui/suggestions/borrow-from-overloaded-deref-assign-issue-154826.stderr @@ -0,0 +1,25 @@ +error[E0507]: cannot move out of an `Rc` + --> $DIR/borrow-from-overloaded-deref-assign-issue-154826.rs:9:12 + | +LL | (b,) = *Rc::new((NonCopy,)); + | - ^^^^^^^^^^^^^^^^^^^^ + | | + | data moved here because the place has type `NonCopy`, which does not implement the `Copy` trait + | +help: destructuring assignment cannot borrow from this expression; consider using a `let` binding instead + --> $DIR/borrow-from-overloaded-deref-assign-issue-154826.rs:9:12 + | +LL | (b,) = *Rc::new((NonCopy,)); + | ^^^^^^^^^^^^^^^^^^^^ +note: if `NonCopy` implemented `Clone`, you could clone the value + --> $DIR/borrow-from-overloaded-deref-assign-issue-154826.rs:5:1 + | +LL | struct NonCopy; + | ^^^^^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | (b,) = *Rc::new((NonCopy,)); + | - you could clone this value + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0507`. diff --git a/tests/ui/suggestions/borrow-from-overloaded-deref-issue-154826.rs b/tests/ui/suggestions/borrow-from-overloaded-deref-issue-154826.rs new file mode 100644 index 0000000000000..2d5df0ca214c3 --- /dev/null +++ b/tests/ui/suggestions/borrow-from-overloaded-deref-issue-154826.rs @@ -0,0 +1,12 @@ +// Regression test for #154826. + +use std::sync::LazyLock; + +static V: LazyLock<(Vec,)> = LazyLock::new(|| (vec![],)); + +fn main() { + let (v,) = *V; + //~^ ERROR cannot move out of dereference of `LazyLock<(Vec,)>` + //~| HELP consider borrowing here + let _: &Vec<_> = &v; +} diff --git a/tests/ui/suggestions/borrow-from-overloaded-deref-issue-154826.stderr b/tests/ui/suggestions/borrow-from-overloaded-deref-issue-154826.stderr new file mode 100644 index 0000000000000..2ae3cf143fc57 --- /dev/null +++ b/tests/ui/suggestions/borrow-from-overloaded-deref-issue-154826.stderr @@ -0,0 +1,16 @@ +error[E0507]: cannot move out of dereference of `LazyLock<(Vec,)>` + --> $DIR/borrow-from-overloaded-deref-issue-154826.rs:8:16 + | +LL | let (v,) = *V; + | - ^^ + | | + | data moved here because `v` has type `Vec`, which does not implement the `Copy` trait + | +help: consider borrowing here + | +LL | let (v,) = &*V; + | + + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0507`. diff --git a/tests/ui/suggestions/issue-102892.stderr b/tests/ui/suggestions/issue-102892.stderr index 38f19b3321887..ed7a88c6422e2 100644 --- a/tests/ui/suggestions/issue-102892.stderr +++ b/tests/ui/suggestions/issue-102892.stderr @@ -61,12 +61,12 @@ LL | let (a, b) = **arc; // suggests putting `&**arc` here; with that, fixed | | ...and here | data moved here | - = note: move occurs because these variables have types that don't implement the `Copy` trait -help: consider removing the dereference here - | -LL - let (a, b) = **arc; // suggests putting `&**arc` here; with that, fixed! -LL + let (a, b) = *arc; // suggests putting `&**arc` here; with that, fixed! +help: destructuring assignment cannot borrow from this expression; consider using a `let` binding instead + --> $DIR/issue-102892.rs:11:18 | +LL | let (a, b) = **arc; // suggests putting `&**arc` here; with that, fixed! + | ^^^^^ + = note: move occurs because these variables have types that don't implement the `Copy` trait error: aborting due to 4 previous errors