Skip to content

Commit 386236f

Browse files
committed
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.
1 parent 6a9758d commit 386236f

File tree

4 files changed

+57
-18
lines changed

4 files changed

+57
-18
lines changed

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

+25-16
Original file line numberDiff line numberDiff line change
@@ -1581,6 +1581,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
15811581
&mut err,
15821582
place,
15831583
issued_borrow.borrowed_place,
1584+
span,
1585+
issued_span,
15841586
);
15851587
self.suggest_using_closure_argument_instead_of_capture(
15861588
&mut err,
@@ -2011,10 +2013,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
20112013
err: &mut Diag<'_>,
20122014
place: Place<'tcx>,
20132015
borrowed_place: Place<'tcx>,
2016+
span: Span,
2017+
issued_span: Span,
20142018
) {
20152019
let tcx = self.infcx.tcx;
20162020
let hir = tcx.hir();
2017-
20182021
if let ([ProjectionElem::Index(index1)], [ProjectionElem::Index(index2)])
20192022
| (
20202023
[ProjectionElem::Deref, ProjectionElem::Index(index1)],
@@ -2023,28 +2026,23 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
20232026
{
20242027
let mut note_default_suggestion = || {
20252028
err.help(
2026-
"consider using `.split_at_mut(position)` or similar method to obtain \
2027-
two mutable non-overlapping sub-slices",
2029+
"consider using `.split_at_mut(position)` or similar method to obtain two \
2030+
mutable non-overlapping sub-slices",
20282031
)
2029-
.help("consider using `.swap(index_1, index_2)` to swap elements at the specified indices");
2030-
};
2031-
2032-
let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else {
2033-
note_default_suggestion();
2034-
return;
2032+
.help(
2033+
"consider using `.swap(index_1, index_2)` to swap elements at the specified \
2034+
indices",
2035+
);
20352036
};
20362037

2037-
let mut expr_finder =
2038-
FindExprBySpan::new(self.body.local_decls[*index1].source_info.span, tcx);
2039-
expr_finder.visit_expr(hir.body(body_id).value);
2040-
let Some(index1) = expr_finder.result else {
2038+
let Some(index1) = self.find_expr(self.body.local_decls[*index1].source_info.span)
2039+
else {
20412040
note_default_suggestion();
20422041
return;
20432042
};
20442043

2045-
expr_finder = FindExprBySpan::new(self.body.local_decls[*index2].source_info.span, tcx);
2046-
expr_finder.visit_expr(hir.body(body_id).value);
2047-
let Some(index2) = expr_finder.result else {
2044+
let Some(index2) = self.find_expr(self.body.local_decls[*index2].source_info.span)
2045+
else {
20482046
note_default_suggestion();
20492047
return;
20502048
};
@@ -2102,7 +2100,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
21022100
format!("{obj_str}.swap({index1_str}, {index2_str})"),
21032101
Applicability::MachineApplicable,
21042102
);
2103+
return;
21052104
}
2105+
let Some(index1) = self.find_expr(span) else { return };
2106+
let hir::Node::Expr(parent) = tcx.parent_hir_node(index1.hir_id) else { return };
2107+
let hir::ExprKind::Index(..) = parent.kind else { return };
2108+
let Some(index2) = self.find_expr(issued_span) else { return };
2109+
let hir::Node::Expr(parent) = tcx.parent_hir_node(index2.hir_id) else { return };
2110+
let hir::ExprKind::Index(..) = parent.kind else { return };
2111+
err.help(
2112+
"use `.split_at_mut(position)` or similar method to obtain two mutable non-overlapping \
2113+
sub-slices",
2114+
);
21062115
}
21072116

21082117
/// Suggest using `while let` for call `next` on an iterator in a for loop.

tests/ui/borrowck/borrowck-overloaded-index-autoderef.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ LL | let q = &mut f[&s];
1717
| ^ second mutable borrow occurs here
1818
LL | p.use_mut();
1919
| - first borrow later used here
20+
|
21+
= help: use `.split_at_mut(position)` or similar method to obtain two mutable non-overlapping sub-slices
2022

2123
error[E0499]: cannot borrow `f.foo` as mutable more than once at a time
2224
--> $DIR/borrowck-overloaded-index-autoderef.rs:53:18
@@ -27,6 +29,8 @@ LL | let q = &mut f.foo[&s];
2729
| ^^^^^ second mutable borrow occurs here
2830
LL | p.use_mut();
2931
| - first borrow later used here
32+
|
33+
= help: use `.split_at_mut(position)` or similar method to obtain two mutable non-overlapping sub-slices
3034

3135
error[E0502]: cannot borrow `f.foo` as mutable because it is also borrowed as immutable
3236
--> $DIR/borrowck-overloaded-index-autoderef.rs:65:18
+15-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,22 @@
1-
fn main() {
1+
fn foo() {
22
let mut foo = [1, 2, 3, 4];
33
let a = &mut foo[2];
44
let b = &mut foo[3]; //~ ERROR cannot borrow `foo[_]` as mutable more than once at a time
55
*a = 5;
66
*b = 6;
77
println!("{:?} {:?}", a, b);
88
}
9+
10+
fn bar() {
11+
let mut foo = [1,2,3,4];
12+
let a = &mut foo[..2];
13+
let b = &mut foo[2..]; //~ ERROR cannot borrow `foo` as mutable more than once at a time
14+
a[0] = 5;
15+
b[0] = 6;
16+
println!("{:?} {:?}", a, b);
17+
}
18+
19+
fn main() {
20+
foo();
21+
bar();
22+
}

tests/ui/suggestions/suggest-split-at-mut.stderr

+13-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@ LL | *a = 5;
1111
= help: consider using `.split_at_mut(position)` or similar method to obtain two mutable non-overlapping sub-slices
1212
= help: consider using `.swap(index_1, index_2)` to swap elements at the specified indices
1313

14-
error: aborting due to 1 previous error
14+
error[E0499]: cannot borrow `foo` as mutable more than once at a time
15+
--> $DIR/suggest-split-at-mut.rs:13:18
16+
|
17+
LL | let a = &mut foo[..2];
18+
| --- first mutable borrow occurs here
19+
LL | let b = &mut foo[2..];
20+
| ^^^ second mutable borrow occurs here
21+
LL | a[0] = 5;
22+
| ---- first borrow later used here
23+
|
24+
= help: use `.split_at_mut(position)` or similar method to obtain two mutable non-overlapping sub-slices
25+
26+
error: aborting due to 2 previous errors
1527

1628
For more information about this error, try `rustc --explain E0499`.

0 commit comments

Comments
 (0)