Rollup merge of #147974 - JohnTitor:diag-detect-buf-reuse-pattern, r=estebank

Improve diagnostics for buffer reuse with borrowed references

Addresses rust-lang/rust#147694

I'm not sure the current note wording is the best so I appreciate any feedback.
This commit is contained in:
Stuart Cook
2025-11-12 12:26:36 +11:00
committed by GitHub
7 changed files with 185 additions and 5 deletions
@@ -3087,6 +3087,39 @@ fn report_local_value_does_not_live_long_enough(
});
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
@@ -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();
}
@@ -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`.
@@ -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
@@ -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
@@ -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
@@ -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