Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rollup of 8 pull requests #134988

Merged
merged 17 commits into from
Jan 1, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 118 additions & 39 deletions compiler/rustc_borrowck/src/diagnostics/move_errors.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]

use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Applicability, Diag};
use rustc_hir::intravisit::Visitor;
use rustc_hir::{CaptureBy, ExprKind, HirId, Node};
use rustc_hir::{self as hir, CaptureBy, ExprKind, HirId, Node};
use rustc_middle::bug;
use rustc_middle::mir::*;
use rustc_middle::ty::{self, Ty};
Expand Down Expand Up @@ -683,48 +684,126 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}

fn add_move_error_suggestions(&self, err: &mut Diag<'_>, binds_to: &[Local]) {
let mut suggestions: Vec<(Span, String, String)> = Vec::new();
/// A HIR visitor to associate each binding with a `&` or `&mut` that could be removed to
/// make it bind by reference instead (if possible)
struct BindingFinder<'tcx> {
typeck_results: &'tcx ty::TypeckResults<'tcx>,
hir: rustc_middle::hir::map::Map<'tcx>,
/// Input: the span of the pattern we're finding bindings in
pat_span: Span,
/// Input: the spans of the bindings we're providing suggestions for
binding_spans: Vec<Span>,
/// Internal state: have we reached the pattern we're finding bindings in?
found_pat: bool,
/// Internal state: the innermost `&` or `&mut` "above" the visitor
ref_pat: Option<&'tcx hir::Pat<'tcx>>,
/// Internal state: could removing a `&` give bindings unexpected types?
has_adjustments: bool,
/// Output: for each input binding, the `&` or `&mut` to remove to make it by-ref
ref_pat_for_binding: Vec<(Span, Option<&'tcx hir::Pat<'tcx>>)>,
/// Output: ref patterns that can't be removed straightforwardly
cannot_remove: FxHashSet<HirId>,
}
impl<'tcx> Visitor<'tcx> for BindingFinder<'tcx> {
type NestedFilter = rustc_middle::hir::nested_filter::OnlyBodies;

fn nested_visit_map(&mut self) -> Self::Map {
self.hir
}

fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) -> Self::Result {
// Don't walk into const patterns or anything else that might confuse this
if !self.found_pat {
hir::intravisit::walk_expr(self, ex)
}
}

fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) {
if p.span == self.pat_span {
self.found_pat = true;
}

let parent_has_adjustments = self.has_adjustments;
self.has_adjustments |=
self.typeck_results.pat_adjustments().contains_key(p.hir_id);

// Track the innermost `&` or `&mut` enclosing bindings, to suggest removing it.
let parent_ref_pat = self.ref_pat;
if let hir::PatKind::Ref(..) = p.kind {
self.ref_pat = Some(p);
// To avoid edition-dependent logic to figure out how many refs this `&` can
// peel off, simply don't remove the "parent" `&`.
self.cannot_remove.extend(parent_ref_pat.map(|r| r.hir_id));
if self.has_adjustments {
// Removing this `&` could give child bindings unexpected types, so don't.
self.cannot_remove.insert(p.hir_id);
// As long the `&` stays, child patterns' types should be as expected.
self.has_adjustments = false;
}
}

if let hir::PatKind::Binding(_, _, ident, _) = p.kind {
// the spans in `binding_spans` encompass both the ident and binding mode
if let Some(&bind_sp) =
self.binding_spans.iter().find(|bind_sp| bind_sp.contains(ident.span))
{
self.ref_pat_for_binding.push((bind_sp, self.ref_pat));
} else {
// we've encountered a binding that we're not reporting a move error for.
// we don't want to change its type, so don't remove the surrounding `&`.
if let Some(ref_pat) = self.ref_pat {
self.cannot_remove.insert(ref_pat.hir_id);
}
}
}

hir::intravisit::walk_pat(self, p);
self.ref_pat = parent_ref_pat;
self.has_adjustments = parent_has_adjustments;
}
}
let mut pat_span = None;
let mut binding_spans = Vec::new();
for local in binds_to {
let bind_to = &self.body.local_decls[*local];
if let LocalInfo::User(BindingForm::Var(VarBindingForm { pat_span, .. })) =
if let LocalInfo::User(BindingForm::Var(VarBindingForm { pat_span: pat_sp, .. })) =
*bind_to.local_info()
{
let Ok(pat_snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(pat_span)
else {
continue;
};
let Some(stripped) = pat_snippet.strip_prefix('&') else {
suggestions.push((
bind_to.source_info.span.shrink_to_lo(),
"consider borrowing the pattern binding".to_string(),
"ref ".to_string(),
));
continue;
};
let inner_pat_snippet = stripped.trim_start();
let (pat_span, suggestion, to_remove) = if inner_pat_snippet.starts_with("mut")
&& inner_pat_snippet["mut".len()..].starts_with(rustc_lexer::is_whitespace)
{
let inner_pat_snippet = inner_pat_snippet["mut".len()..].trim_start();
let pat_span = pat_span.with_hi(
pat_span.lo()
+ BytePos((pat_snippet.len() - inner_pat_snippet.len()) as u32),
);
(pat_span, String::new(), "mutable borrow")
} else {
let pat_span = pat_span.with_hi(
pat_span.lo()
+ BytePos(
(pat_snippet.len() - inner_pat_snippet.trim_start().len()) as u32,
),
);
(pat_span, String::new(), "borrow")
};
suggestions.push((
pat_span,
format!("consider removing the {to_remove}"),
suggestion,
));
pat_span = Some(pat_sp);
binding_spans.push(bind_to.source_info.span);
}
}
let Some(pat_span) = pat_span else { return };

let hir = self.infcx.tcx.hir();
let Some(body) = hir.maybe_body_owned_by(self.mir_def_id()) else { return };
let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
let mut finder = BindingFinder {
typeck_results,
hir,
pat_span,
binding_spans,
found_pat: false,
ref_pat: None,
has_adjustments: false,
ref_pat_for_binding: Vec::new(),
cannot_remove: FxHashSet::default(),
};
finder.visit_body(body);

let mut suggestions = Vec::new();
for (binding_span, opt_ref_pat) in finder.ref_pat_for_binding {
if let Some(ref_pat) = opt_ref_pat
&& !finder.cannot_remove.contains(&ref_pat.hir_id)
&& let hir::PatKind::Ref(subpat, mutbl) = ref_pat.kind
&& let Some(ref_span) = ref_pat.span.trim_end(subpat.span)
{
let mutable_str = if mutbl.is_mut() { "mutable " } else { "" };
let msg = format!("consider removing the {mutable_str}borrow");
suggestions.push((ref_span, msg, "".to_string()));
} else {
let msg = "consider borrowing the pattern binding".to_string();
suggestions.push((binding_span.shrink_to_lo(), msg, "ref ".to_string()));
}
}
suggestions.sort_unstable_by_key(|&(span, _, _)| span);
Expand Down
28 changes: 16 additions & 12 deletions tests/ui/issues/issue-12567.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ LL | (&[hd1, ..], &[hd2, ..])
| --- ...and here
|
= note: move occurs because these variables have types that don't implement the `Copy` trait
help: consider borrowing the pattern binding
help: consider removing the borrow
|
LL | (&[], &[ref hd, ..]) | (&[hd, ..], &[])
| +++
help: consider borrowing the pattern binding
LL - (&[], &[hd, ..]) | (&[hd, ..], &[])
LL + (&[], [hd, ..]) | (&[hd, ..], &[])
|
help: consider removing the borrow
|
LL - (&[hd1, ..], &[hd2, ..])
LL + (&[hd1, ..], [hd2, ..])
|
LL | (&[hd1, ..], &[ref hd2, ..])
| +++

error[E0508]: cannot move out of type `[T]`, a non-copy slice
--> $DIR/issue-12567.rs:2:11
Expand All @@ -33,14 +35,16 @@ LL | (&[hd1, ..], &[hd2, ..])
| --- ...and here
|
= note: move occurs because these variables have types that don't implement the `Copy` trait
help: consider borrowing the pattern binding
help: consider removing the borrow
|
LL - (&[], &[hd, ..]) | (&[hd, ..], &[])
LL + (&[], [hd, ..]) | (&[hd, ..], &[])
|
help: consider removing the borrow
|
LL | (&[], &[ref hd, ..]) | (&[hd, ..], &[])
| +++
help: consider borrowing the pattern binding
LL - (&[hd1, ..], &[hd2, ..])
LL + ([hd1, ..], &[hd2, ..])
|
LL | (&[ref hd1, ..], &[hd2, ..])
| +++

error: aborting due to 2 previous errors

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) {
| data moved here
| move occurs because `x` has type `&mut u32`, which does not implement the `Copy` trait
|
help: consider borrowing the pattern binding
help: consider removing the borrow
|
LL - if let Some(&Some(x)) = Some(&Some(&mut 0)) {
LL + if let Some(Some(x)) = Some(&Some(&mut 0)) {
|
LL | if let Some(&Some(ref x)) = Some(&Some(&mut 0)) {
| +++

error[E0596]: cannot borrow data in a `&` reference as mutable
--> $DIR/ref_pat_eat_one_layer_2024_fail2.rs:11:10
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//@ run-rustfix
//! diagnostic test for #132806: make sure the suggestion to bind by-reference in patterns doesn't
//! erroneously remove the wrong `&`

use std::collections::HashMap;

fn main() {
let _ = HashMap::<String, i32>::new().iter().filter(|&(_k, &_v)| { true });
//~^ ERROR cannot move out of a shared reference
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//@ run-rustfix
//! diagnostic test for #132806: make sure the suggestion to bind by-reference in patterns doesn't
//! erroneously remove the wrong `&`

use std::collections::HashMap;

fn main() {
let _ = HashMap::<String, i32>::new().iter().filter(|&(&_k, &_v)| { true });
//~^ ERROR cannot move out of a shared reference
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error[E0507]: cannot move out of a shared reference
--> $DIR/do-not-suggest-removing-wrong-ref-pattern-issue-132806.rs:8:58
|
LL | let _ = HashMap::<String, i32>::new().iter().filter(|&(&_k, &_v)| { true });
| ^^^--^^^^^^
| |
| data moved here
| move occurs because `_k` has type `String`, which does not implement the `Copy` trait
|
help: consider removing the borrow
|
LL - let _ = HashMap::<String, i32>::new().iter().filter(|&(&_k, &_v)| { true });
LL + let _ = HashMap::<String, i32>::new().iter().filter(|&(_k, &_v)| { true });
|

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0507`.
7 changes: 4 additions & 3 deletions tests/ui/nll/move-errors.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -209,10 +209,11 @@ LL | (D(s), &t) => (),
| data moved here
| move occurs because `t` has type `String`, which does not implement the `Copy` trait
|
help: consider borrowing the pattern binding
help: consider removing the borrow
|
LL - (D(s), &t) => (),
LL + (D(s), t) => (),
|
LL | (D(s), &ref t) => (),
| +++

error[E0509]: cannot move out of type `F`, which implements the `Drop` trait
--> $DIR/move-errors.rs:102:11
Expand Down
10 changes: 10 additions & 0 deletions tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ LL | deref!(x) => x,
| |
| data moved here
| move occurs because `x` has type `Struct`, which does not implement the `Copy` trait
|
help: consider borrowing the pattern binding
|
LL | deref!(ref x) => x,
| +++

error[E0507]: cannot move out of a shared reference
--> $DIR/cant_move_out_of_pattern.rs:17:11
Expand All @@ -21,6 +26,11 @@ LL | deref!(x) => x,
| |
| data moved here
| move occurs because `x` has type `Struct`, which does not implement the `Copy` trait
|
help: consider borrowing the pattern binding
|
LL | deref!(ref x) => x,
| +++

error: aborting due to 2 previous errors

Expand Down
22 changes: 11 additions & 11 deletions tests/ui/suggestions/dont-suggest-ref/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,42 +219,42 @@ pub fn main() {

let (&X(_t),) = (&x.clone(),);
//~^ ERROR cannot move
//~| HELP consider borrowing the pattern binding
//~| HELP consider removing the borrow
if let (&Either::One(_t),) = (&e.clone(),) { }
//~^ ERROR cannot move
//~| HELP consider borrowing the pattern binding
//~| HELP consider removing the borrow
while let (&Either::One(_t),) = (&e.clone(),) { }
//~^ ERROR cannot move
//~| HELP consider borrowing the pattern binding
//~| HELP consider removing the borrow
match (&e.clone(),) {
//~^ ERROR cannot move
(&Either::One(_t),)
//~^ HELP consider borrowing the pattern binding
//~^ HELP consider removing the borrow
| (&Either::Two(_t),) => (),
}
fn f3((&X(_t),): (&X,)) { }
//~^ ERROR cannot move
//~| HELP consider borrowing the pattern binding
//~| HELP consider removing the borrow

let (&mut X(_t),) = (&mut xm.clone(),);
//~^ ERROR cannot move
//~| HELP consider borrowing the pattern binding
//~| HELP consider removing the mutable borrow
if let (&mut Either::One(_t),) = (&mut em.clone(),) { }
//~^ ERROR cannot move
//~| HELP consider borrowing the pattern binding
//~| HELP consider removing the mutable borrow
while let (&mut Either::One(_t),) = (&mut em.clone(),) { }
//~^ ERROR cannot move
//~| HELP consider borrowing the pattern binding
//~| HELP consider removing the mutable borrow
match (&mut em.clone(),) {
//~^ ERROR cannot move
(&mut Either::One(_t),) => (),
//~^ HELP consider borrowing the pattern binding
//~^ HELP consider removing the mutable borrow
(&mut Either::Two(_t),) => (),
//~^ HELP consider borrowing the pattern binding
//~^ HELP consider removing the mutable borrow
}
fn f4((&mut X(_t),): (&mut X,)) { }
//~^ ERROR cannot move
//~| HELP consider borrowing the pattern binding
//~| HELP consider removing the mutable borrow

// move from &Either/&X value

Expand Down
Loading