Rollup merge of #124313 - estebank:split-at-mut, r=fee1-dead
Detect borrow error involving sub-slices and suggest `split_at_mut` ``` error[E0499]: cannot borrow `foo` as mutable more than once at a time --> $DIR/suggest-split-at-mut.rs:13:18 | LL | let a = &mut foo[..2]; | --- first mutable borrow occurs here LL | let b = &mut foo[2..]; | ^^^ second mutable borrow occurs here LL | a[0] = 5; | ---- first borrow later used here | = help: use `.split_at_mut(position)` or similar method to obtain two mutable non-overlapping sub-slices ``` Address most of #58792. For follow up work, we should emit a structured suggestion for cases where we can identify the exact `let (a, b) = foo.split_at_mut(2);` call that is needed.
This commit is contained in:
commit
60c825f1e1
@ -1527,7 +1527,7 @@ pub(crate) fn report_conflicting_borrow(
|
||||
BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow },
|
||||
) => {
|
||||
first_borrow_desc = "mutable ";
|
||||
self.cannot_reborrow_already_borrowed(
|
||||
let mut err = self.cannot_reborrow_already_borrowed(
|
||||
span,
|
||||
&desc_place,
|
||||
&msg_place,
|
||||
@ -1537,7 +1537,15 @@ pub(crate) fn report_conflicting_borrow(
|
||||
"mutable",
|
||||
&msg_borrow,
|
||||
None,
|
||||
)
|
||||
);
|
||||
self.suggest_slice_method_if_applicable(
|
||||
&mut err,
|
||||
place,
|
||||
issued_borrow.borrowed_place,
|
||||
span,
|
||||
issued_span,
|
||||
);
|
||||
err
|
||||
}
|
||||
(
|
||||
BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow },
|
||||
@ -1555,6 +1563,13 @@ pub(crate) fn report_conflicting_borrow(
|
||||
&msg_borrow,
|
||||
None,
|
||||
);
|
||||
self.suggest_slice_method_if_applicable(
|
||||
&mut err,
|
||||
place,
|
||||
issued_borrow.borrowed_place,
|
||||
span,
|
||||
issued_span,
|
||||
);
|
||||
self.suggest_binding_for_closure_capture_self(&mut err, &issued_spans);
|
||||
self.suggest_using_closure_argument_instead_of_capture(
|
||||
&mut err,
|
||||
@ -1581,6 +1596,8 @@ pub(crate) fn report_conflicting_borrow(
|
||||
&mut err,
|
||||
place,
|
||||
issued_borrow.borrowed_place,
|
||||
span,
|
||||
issued_span,
|
||||
);
|
||||
self.suggest_using_closure_argument_instead_of_capture(
|
||||
&mut err,
|
||||
@ -2011,40 +2028,47 @@ fn suggest_slice_method_if_applicable(
|
||||
err: &mut Diag<'_>,
|
||||
place: Place<'tcx>,
|
||||
borrowed_place: Place<'tcx>,
|
||||
span: Span,
|
||||
issued_span: Span,
|
||||
) {
|
||||
let tcx = self.infcx.tcx;
|
||||
let hir = tcx.hir();
|
||||
|
||||
let has_split_at_mut = |ty: Ty<'tcx>| {
|
||||
let ty = ty.peel_refs();
|
||||
match ty.kind() {
|
||||
ty::Array(..) | ty::Slice(..) => true,
|
||||
ty::Adt(def, _) if tcx.get_diagnostic_item(sym::Vec) == Some(def.did()) => true,
|
||||
_ if ty == tcx.types.str_ => true,
|
||||
_ => false,
|
||||
}
|
||||
};
|
||||
if let ([ProjectionElem::Index(index1)], [ProjectionElem::Index(index2)])
|
||||
| (
|
||||
[ProjectionElem::Deref, ProjectionElem::Index(index1)],
|
||||
[ProjectionElem::Deref, ProjectionElem::Index(index2)],
|
||||
) = (&place.projection[..], &borrowed_place.projection[..])
|
||||
{
|
||||
let decl1 = &self.body.local_decls[*index1];
|
||||
let decl2 = &self.body.local_decls[*index2];
|
||||
|
||||
let mut note_default_suggestion = || {
|
||||
err.help(
|
||||
"consider using `.split_at_mut(position)` or similar method to obtain \
|
||||
two mutable non-overlapping sub-slices",
|
||||
"consider using `.split_at_mut(position)` or similar method to obtain two \
|
||||
mutable non-overlapping sub-slices",
|
||||
)
|
||||
.help("consider using `.swap(index_1, index_2)` to swap elements at the specified indices");
|
||||
.help(
|
||||
"consider using `.swap(index_1, index_2)` to swap elements at the specified \
|
||||
indices",
|
||||
);
|
||||
};
|
||||
|
||||
let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else {
|
||||
let Some(index1) = self.find_expr(decl1.source_info.span) else {
|
||||
note_default_suggestion();
|
||||
return;
|
||||
};
|
||||
|
||||
let mut expr_finder =
|
||||
FindExprBySpan::new(self.body.local_decls[*index1].source_info.span, tcx);
|
||||
expr_finder.visit_expr(hir.body(body_id).value);
|
||||
let Some(index1) = expr_finder.result else {
|
||||
note_default_suggestion();
|
||||
return;
|
||||
};
|
||||
|
||||
expr_finder = FindExprBySpan::new(self.body.local_decls[*index2].source_info.span, tcx);
|
||||
expr_finder.visit_expr(hir.body(body_id).value);
|
||||
let Some(index2) = expr_finder.result else {
|
||||
let Some(index2) = self.find_expr(decl2.source_info.span) else {
|
||||
note_default_suggestion();
|
||||
return;
|
||||
};
|
||||
@ -2092,7 +2116,13 @@ fn suggest_slice_method_if_applicable(
|
||||
None
|
||||
}
|
||||
}) else {
|
||||
note_default_suggestion();
|
||||
let hir::Node::Expr(parent) = tcx.parent_hir_node(index1.hir_id) else { return };
|
||||
let hir::ExprKind::Index(_, idx1, _) = parent.kind else { return };
|
||||
let hir::Node::Expr(parent) = tcx.parent_hir_node(index2.hir_id) else { return };
|
||||
let hir::ExprKind::Index(_, idx2, _) = parent.kind else { return };
|
||||
if !idx1.equivalent_for_indexing(idx2) {
|
||||
err.help("use `.split_at_mut(position)` to obtain two mutable non-overlapping sub-slices");
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
@ -2102,7 +2132,25 @@ fn suggest_slice_method_if_applicable(
|
||||
format!("{obj_str}.swap({index1_str}, {index2_str})"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
return;
|
||||
}
|
||||
let place_ty = PlaceRef::ty(&place.as_ref(), self.body, tcx).ty;
|
||||
let borrowed_place_ty = PlaceRef::ty(&borrowed_place.as_ref(), self.body, tcx).ty;
|
||||
if !has_split_at_mut(place_ty) && !has_split_at_mut(borrowed_place_ty) {
|
||||
// Only mention `split_at_mut` on `Vec`, array and slices.
|
||||
return;
|
||||
}
|
||||
let Some(index1) = self.find_expr(span) else { return };
|
||||
let hir::Node::Expr(parent) = tcx.parent_hir_node(index1.hir_id) else { return };
|
||||
let hir::ExprKind::Index(_, idx1, _) = parent.kind else { return };
|
||||
let Some(index2) = self.find_expr(issued_span) else { return };
|
||||
let hir::Node::Expr(parent) = tcx.parent_hir_node(index2.hir_id) else { return };
|
||||
let hir::ExprKind::Index(_, idx2, _) = parent.kind else { return };
|
||||
if idx1.equivalent_for_indexing(idx2) {
|
||||
// `let a = &mut foo[0]` and `let b = &mut foo[0]`? Don't mention `split_at_mut`
|
||||
return;
|
||||
}
|
||||
err.help("use `.split_at_mut(position)` to obtain two mutable non-overlapping sub-slices");
|
||||
}
|
||||
|
||||
/// Suggest using `while let` for call `next` on an iterator in a for loop.
|
||||
|
@ -1811,6 +1811,44 @@ pub fn is_approximately_pattern(&self) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether this and the `other` expression are the same for purposes of an indexing operation.
|
||||
///
|
||||
/// This is only used for diagnostics to see if we have things like `foo[i]` where `foo` is
|
||||
/// borrowed multiple times with `i`.
|
||||
pub fn equivalent_for_indexing(&self, other: &Expr<'_>) -> bool {
|
||||
match (self.kind, other.kind) {
|
||||
(ExprKind::Lit(lit1), ExprKind::Lit(lit2)) => lit1.node == lit2.node,
|
||||
(
|
||||
ExprKind::Path(QPath::LangItem(item1, _)),
|
||||
ExprKind::Path(QPath::LangItem(item2, _)),
|
||||
) => item1 == item2,
|
||||
(
|
||||
ExprKind::Path(QPath::Resolved(None, path1)),
|
||||
ExprKind::Path(QPath::Resolved(None, path2)),
|
||||
) => path1.res == path2.res,
|
||||
(
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, _), [val1], None),
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, _), [val2], None),
|
||||
)
|
||||
| (
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::RangeToInclusive, _), [val1], None),
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::RangeToInclusive, _), [val2], None),
|
||||
)
|
||||
| (
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, _), [val1], None),
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, _), [val2], None),
|
||||
) => val1.expr.equivalent_for_indexing(val2.expr),
|
||||
(
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::Range, _), [val1, val3], None),
|
||||
ExprKind::Struct(QPath::LangItem(LangItem::Range, _), [val2, val4], None),
|
||||
) => {
|
||||
val1.expr.equivalent_for_indexing(val2.expr)
|
||||
&& val3.expr.equivalent_for_indexing(val4.expr)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn method_ident(&self) -> Option<Ident> {
|
||||
match self.kind {
|
||||
ExprKind::MethodCall(receiver_method, ..) => Some(receiver_method.ident),
|
||||
|
@ -1,4 +1,4 @@
|
||||
fn main() {
|
||||
fn foo() {
|
||||
let mut foo = [1, 2, 3, 4];
|
||||
let a = &mut foo[2];
|
||||
let b = &mut foo[3]; //~ ERROR cannot borrow `foo[_]` as mutable more than once at a time
|
||||
@ -6,3 +6,57 @@ fn main() {
|
||||
*b = 6;
|
||||
println!("{:?} {:?}", a, b);
|
||||
}
|
||||
|
||||
fn bar() {
|
||||
let mut foo = [1,2,3,4];
|
||||
let a = &mut foo[..2];
|
||||
let b = &mut foo[2..]; //~ ERROR cannot borrow `foo` as mutable more than once at a time
|
||||
a[0] = 5;
|
||||
b[0] = 6;
|
||||
println!("{:?} {:?}", a, b);
|
||||
}
|
||||
|
||||
fn baz() {
|
||||
let mut foo = [1,2,3,4];
|
||||
let a = &foo[..2];
|
||||
let b = &mut foo[2..]; //~ ERROR cannot borrow `foo` as mutable because it is also borrowed as immutable
|
||||
b[0] = 6;
|
||||
println!("{:?} {:?}", a, b);
|
||||
}
|
||||
|
||||
fn qux() {
|
||||
let mut foo = [1,2,3,4];
|
||||
let a = &mut foo[..2];
|
||||
let b = &foo[2..]; //~ ERROR cannot borrow `foo` as immutable because it is also borrowed as mutable
|
||||
a[0] = 5;
|
||||
println!("{:?} {:?}", a, b);
|
||||
}
|
||||
|
||||
fn bad() {
|
||||
let mut foo = [1,2,3,4];
|
||||
let a = &foo[1];
|
||||
let b = &mut foo[2]; //~ ERROR cannot borrow `foo[_]` as mutable because it is also borrowed as immutable
|
||||
*b = 6;
|
||||
println!("{:?} {:?}", a, b);
|
||||
}
|
||||
|
||||
fn bat() {
|
||||
let mut foo = [1,2,3,4];
|
||||
let a = &mut foo[1];
|
||||
let b = &foo[2]; //~ ERROR cannot borrow `foo[_]` as immutable because it is also borrowed as mutable
|
||||
*a = 5;
|
||||
println!("{:?} {:?}", a, b);
|
||||
}
|
||||
|
||||
fn ang() {
|
||||
let mut foo = [1,2,3,4];
|
||||
let a = &mut foo[0..];
|
||||
let b = &foo[0..]; //~ ERROR cannot borrow `foo` as immutable because it is also borrowed as mutable
|
||||
a[0] = 5;
|
||||
println!("{:?} {:?}", a, b);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
foo();
|
||||
bar();
|
||||
}
|
||||
|
@ -8,9 +8,81 @@ LL | let b = &mut foo[3];
|
||||
LL | *a = 5;
|
||||
| ------ first borrow later used here
|
||||
|
|
||||
= help: consider using `.split_at_mut(position)` or similar method to obtain two mutable non-overlapping sub-slices
|
||||
= help: consider using `.swap(index_1, index_2)` to swap elements at the specified indices
|
||||
= help: use `.split_at_mut(position)` to obtain two mutable non-overlapping sub-slices
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
error[E0499]: cannot borrow `foo` as mutable more than once at a time
|
||||
--> $DIR/suggest-split-at-mut.rs:13:18
|
||||
|
|
||||
LL | let a = &mut foo[..2];
|
||||
| --- first mutable borrow occurs here
|
||||
LL | let b = &mut foo[2..];
|
||||
| ^^^ second mutable borrow occurs here
|
||||
LL | a[0] = 5;
|
||||
| ---- first borrow later used here
|
||||
|
|
||||
= help: use `.split_at_mut(position)` to obtain two mutable non-overlapping sub-slices
|
||||
|
||||
For more information about this error, try `rustc --explain E0499`.
|
||||
error[E0502]: cannot borrow `foo` as mutable because it is also borrowed as immutable
|
||||
--> $DIR/suggest-split-at-mut.rs:22:18
|
||||
|
|
||||
LL | let a = &foo[..2];
|
||||
| --- immutable borrow occurs here
|
||||
LL | let b = &mut foo[2..];
|
||||
| ^^^ mutable borrow occurs here
|
||||
LL | b[0] = 6;
|
||||
LL | println!("{:?} {:?}", a, b);
|
||||
| - immutable borrow later used here
|
||||
|
|
||||
= help: use `.split_at_mut(position)` to obtain two mutable non-overlapping sub-slices
|
||||
|
||||
error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable
|
||||
--> $DIR/suggest-split-at-mut.rs:30:14
|
||||
|
|
||||
LL | let a = &mut foo[..2];
|
||||
| --- mutable borrow occurs here
|
||||
LL | let b = &foo[2..];
|
||||
| ^^^ immutable borrow occurs here
|
||||
LL | a[0] = 5;
|
||||
| ---- mutable borrow later used here
|
||||
|
|
||||
= help: use `.split_at_mut(position)` to obtain two mutable non-overlapping sub-slices
|
||||
|
||||
error[E0502]: cannot borrow `foo[_]` as mutable because it is also borrowed as immutable
|
||||
--> $DIR/suggest-split-at-mut.rs:38:13
|
||||
|
|
||||
LL | let a = &foo[1];
|
||||
| ------- immutable borrow occurs here
|
||||
LL | let b = &mut foo[2];
|
||||
| ^^^^^^^^^^^ mutable borrow occurs here
|
||||
LL | *b = 6;
|
||||
LL | println!("{:?} {:?}", a, b);
|
||||
| - immutable borrow later used here
|
||||
|
|
||||
= help: use `.split_at_mut(position)` to obtain two mutable non-overlapping sub-slices
|
||||
|
||||
error[E0502]: cannot borrow `foo[_]` as immutable because it is also borrowed as mutable
|
||||
--> $DIR/suggest-split-at-mut.rs:46:13
|
||||
|
|
||||
LL | let a = &mut foo[1];
|
||||
| ----------- mutable borrow occurs here
|
||||
LL | let b = &foo[2];
|
||||
| ^^^^^^^ immutable borrow occurs here
|
||||
LL | *a = 5;
|
||||
| ------ mutable borrow later used here
|
||||
|
|
||||
= help: use `.split_at_mut(position)` to obtain two mutable non-overlapping sub-slices
|
||||
|
||||
error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable
|
||||
--> $DIR/suggest-split-at-mut.rs:54:14
|
||||
|
|
||||
LL | let a = &mut foo[0..];
|
||||
| --- mutable borrow occurs here
|
||||
LL | let b = &foo[0..];
|
||||
| ^^^ immutable borrow occurs here
|
||||
LL | a[0] = 5;
|
||||
| ---- mutable borrow later used here
|
||||
|
||||
error: aborting due to 7 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0499, E0502.
|
||||
For more information about an error, try `rustc --explain E0499`.
|
||||
|
Loading…
Reference in New Issue
Block a user