Improve diagnostics for buffer reuse with borrowed references

This commit is contained in:
Yuki Okushi 2025-10-22 21:09:17 +09:00
parent b2ee1b333a
commit 61b26f9f60
7 changed files with 185 additions and 5 deletions

View file

@ -3084,6 +3084,39 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
});
explanation.add_explanation_to_diagnostic(&self, &mut err, "", Some(borrow_span), None);
// Detect buffer reuse pattern
if let BorrowExplanation::UsedLater(_dropped_local, _, _, _) = explanation {
// Check all locals at the borrow location to find Vec<&T> types
for (local, local_decl) in self.body.local_decls.iter_enumerated() {
if let ty::Adt(adt_def, args) = local_decl.ty.kind()
&& self.infcx.tcx.is_diagnostic_item(sym::Vec, adt_def.did())
&& args.len() > 0
{
let vec_inner_ty = args.type_at(0);
// Check if Vec contains references
if vec_inner_ty.is_ref() {
let local_place = local.into();
if let Some(local_name) = self.describe_place(local_place) {
err.span_label(
local_decl.source_info.span,
format!("variable `{local_name}` declared here"),
);
err.note(
format!(
"`{local_name}` is a collection that stores borrowed references, \
but {name} does not live long enough to be stored in it"
)
);
err.help(
"buffer reuse with borrowed references requires unsafe code or restructuring"
);
break;
}
}
}
}
}
}
err

View file

@ -0,0 +1,63 @@
fn process_data(_: &[&[u8]]) {}
fn test_buffer_cleared_after_use() {
let sources = vec![vec![1u8, 2, 3, 4, 5], vec![6, 7, 8, 9]];
let mut buffer: Vec<&[u8]> = vec![];
//~^ NOTE variable `buffer` declared here
for source in sources {
let data: Vec<u8> = source;
//~^ NOTE binding `data` declared here
buffer.extend(data.split(|x| *x == 3));
//~^ ERROR `data` does not live long enough
//~| NOTE borrowed value does not live long enough
//~| NOTE borrow later used here
//~| NOTE `buffer` is a collection that stores borrowed references, but `data` does not live long enough to be stored in it
//~| HELP buffer reuse with borrowed references requires unsafe code or restructuring
process_data(&buffer);
buffer.clear();
} //~ NOTE `data` dropped here while still borrowed
}
fn test_buffer_cleared_at_start() {
let sources = vec![vec![1u8, 2, 3, 4, 5], vec![6, 7, 8, 9]];
let mut buffer: Vec<&[u8]> = vec![];
//~^ NOTE variable `buffer` declared here
for source in sources {
buffer.clear();
//~^ NOTE borrow later used here
let data: Vec<u8> = source;
//~^ NOTE binding `data` declared here
buffer.extend(data.split(|x| *x == 3));
//~^ ERROR `data` does not live long enough
//~| NOTE borrowed value does not live long enough
//~| NOTE `buffer` is a collection that stores borrowed references, but `data` does not live long enough to be stored in it
//~| HELP buffer reuse with borrowed references requires unsafe code or restructuring
process_data(&buffer);
} //~ NOTE `data` dropped here while still borrowed
}
fn test_no_explicit_clear() {
let sources = vec![vec![1u8, 2, 3, 4, 5], vec![6, 7, 8, 9]];
let mut buffer: Vec<&[u8]> = vec![];
//~^ NOTE variable `buffer` declared here
for source in sources {
let data: Vec<u8> = source;
//~^ NOTE binding `data` declared here
buffer.extend(data.split(|x| *x == 3));
//~^ ERROR `data` does not live long enough
//~| NOTE borrowed value does not live long enough
//~| NOTE borrow later used here
//~| NOTE `buffer` is a collection that stores borrowed references, but `data` does not live long enough to be stored in it
//~| HELP buffer reuse with borrowed references requires unsafe code or restructuring
process_data(&buffer);
} //~ NOTE `data` dropped here while still borrowed
}
fn main() {
test_buffer_cleared_after_use();
test_buffer_cleared_at_start();
test_no_explicit_clear();
}

View file

@ -0,0 +1,64 @@
error[E0597]: `data` does not live long enough
--> $DIR/buffer-reuse-pattern-issue-147694.rs:11:23
|
LL | let mut buffer: Vec<&[u8]> = vec![];
| ---------- variable `buffer` declared here
...
LL | let data: Vec<u8> = source;
| ---- binding `data` declared here
LL |
LL | buffer.extend(data.split(|x| *x == 3));
| ------ ^^^^ borrowed value does not live long enough
| |
| borrow later used here
...
LL | }
| - `data` dropped here while still borrowed
|
= note: `buffer` is a collection that stores borrowed references, but `data` does not live long enough to be stored in it
= help: buffer reuse with borrowed references requires unsafe code or restructuring
error[E0597]: `data` does not live long enough
--> $DIR/buffer-reuse-pattern-issue-147694.rs:32:23
|
LL | let mut buffer: Vec<&[u8]> = vec![];
| ---------- variable `buffer` declared here
...
LL | buffer.clear();
| ------ borrow later used here
LL |
LL | let data: Vec<u8> = source;
| ---- binding `data` declared here
LL |
LL | buffer.extend(data.split(|x| *x == 3));
| ^^^^ borrowed value does not live long enough
...
LL | }
| - `data` dropped here while still borrowed
|
= note: `buffer` is a collection that stores borrowed references, but `data` does not live long enough to be stored in it
= help: buffer reuse with borrowed references requires unsafe code or restructuring
error[E0597]: `data` does not live long enough
--> $DIR/buffer-reuse-pattern-issue-147694.rs:49:23
|
LL | let mut buffer: Vec<&[u8]> = vec![];
| ---------- variable `buffer` declared here
...
LL | let data: Vec<u8> = source;
| ---- binding `data` declared here
LL |
LL | buffer.extend(data.split(|x| *x == 3));
| ------ ^^^^ borrowed value does not live long enough
| |
| borrow later used here
...
LL | }
| - `data` dropped here while still borrowed
|
= note: `buffer` is a collection that stores borrowed references, but `data` does not live long enough to be stored in it
= help: buffer reuse with borrowed references requires unsafe code or restructuring
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0597`.

View file

@ -4,13 +4,18 @@ error[E0597]: `line` does not live long enough
LL | for line in vec!["123456789".to_string(), "12345678".to_string()] {
| ---- binding `line` declared here
LL | let v: Vec<&str> = line.split_whitespace().collect();
| ^^^^ borrowed value does not live long enough
| - ^^^^ borrowed value does not live long enough
| |
| variable `v` declared here
...
LL | acc += cnt2;
| --- borrow later used here
...
LL | }
| - `line` dropped here while still borrowed
|
= note: `v` is a collection that stores borrowed references, but `line` does not live long enough to be stored in it
= help: buffer reuse with borrowed references requires unsafe code or restructuring
error: aborting due to 1 previous error

View file

@ -3,6 +3,7 @@ fn id<T>(x: T) -> T { x }
fn f() {
let old = ['o']; // statement 0
let mut v1 = Vec::new(); // statement 1
//~^ NOTE variable `v1` declared here
let mut v2 = Vec::new(); // statement 2
@ -13,6 +14,8 @@ fn f() {
v2.push(&young[0]); // statement 4
//~^ ERROR `young[_]` does not live long enough
//~| NOTE borrowed value does not live long enough
//~| NOTE `v1` is a collection that stores borrowed references, but `young[_]` does not live long enough to be stored in it
//~| HELP buffer reuse with borrowed references requires unsafe code or restructuring
} //~ NOTE `young[_]` dropped here while still borrowed
let mut v3 = Vec::new(); // statement 5

View file

@ -1,6 +1,9 @@
error[E0597]: `young[_]` does not live long enough
--> $DIR/borrowck-let-suggestion-suffixes.rs:13:17
--> $DIR/borrowck-let-suggestion-suffixes.rs:14:17
|
LL | let mut v1 = Vec::new(); // statement 1
| ------ variable `v1` declared here
...
LL | let young = ['y']; // statement 3
| ----- binding `young` declared here
...
@ -12,9 +15,12 @@ LL | }
...
LL | (v1, v2, v3, /* v4 is above. */ v5).use_ref();
| -- borrow later used here
|
= note: `v1` is a collection that stores borrowed references, but `young[_]` does not live long enough to be stored in it
= help: buffer reuse with borrowed references requires unsafe code or restructuring
error[E0716]: temporary value dropped while borrowed
--> $DIR/borrowck-let-suggestion-suffixes.rs:20:14
--> $DIR/borrowck-let-suggestion-suffixes.rs:23:14
|
LL | v3.push(&id('x')); // statement 6
| ^^^^^^^ - temporary value is freed at the end of this statement
@ -31,7 +37,7 @@ LL ~ v3.push(&binding); // statement 6
|
error[E0716]: temporary value dropped while borrowed
--> $DIR/borrowck-let-suggestion-suffixes.rs:30:18
--> $DIR/borrowck-let-suggestion-suffixes.rs:33:18
|
LL | v4.push(&id('y'));
| ^^^^^^^ - temporary value is freed at the end of this statement
@ -44,7 +50,7 @@ LL | v4.use_ref();
= note: consider using a `let` binding to create a longer lived value
error[E0716]: temporary value dropped while borrowed
--> $DIR/borrowck-let-suggestion-suffixes.rs:41:14
--> $DIR/borrowck-let-suggestion-suffixes.rs:44:14
|
LL | v5.push(&id('z'));
| ^^^^^^^ - temporary value is freed at the end of this statement

View file

@ -23,6 +23,9 @@ LL | _y.push(&mut z);
error[E0597]: `z` does not live long enough
--> $DIR/regions-escape-loop-via-vec.rs:7:17
|
LL | let mut _y = vec![&mut x];
| ------ variable `_y` declared here
LL | while x < 10 {
LL | let mut z = x;
| ----- binding `z` declared here
LL | _y.push(&mut z);
@ -32,6 +35,9 @@ LL | _y.push(&mut z);
...
LL | }
| - `z` dropped here while still borrowed
|
= note: `_y` is a collection that stores borrowed references, but `z` does not live long enough to be stored in it
= help: buffer reuse with borrowed references requires unsafe code or restructuring
error[E0503]: cannot use `x` because it was mutably borrowed
--> $DIR/regions-escape-loop-via-vec.rs:9:9