Rollup merge of #142543 - Urgau:span-borrowck-semicolon, r=fmease

Suggest adding semicolon in user code rather than macro impl details

This PR tries to find the right span (by peeling expansion) so that the suggestion for adding a semicolon is suggested in user code rather than in the expanded code (in the example a macro impl).

Fixes rust-lang/rust#139049
r? `@fmease`
This commit is contained in:
León Orell Valerian Liehr 2025-06-15 23:51:58 +02:00 committed by GitHub
commit 07048643dd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 166 additions and 3 deletions

View file

@ -342,6 +342,10 @@ impl<'tcx> BorrowExplanation<'tcx> {
}
}
} else if let LocalInfo::BlockTailTemp(info) = local_decl.local_info() {
let sp = info
.span
.find_ancestor_in_same_ctxt(local_decl.source_info.span)
.unwrap_or(info.span);
if info.tail_result_is_ignored {
// #85581: If the first mutable borrow's scope contains
// the second borrow, this suggestion isn't helpful.
@ -349,7 +353,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
old.to(info.span.shrink_to_hi()).contains(new)
}) {
err.span_suggestion_verbose(
info.span.shrink_to_hi(),
sp.shrink_to_hi(),
"consider adding semicolon after the expression so its \
temporaries are dropped sooner, before the local variables \
declared by the block are dropped",
@ -368,8 +372,8 @@ impl<'tcx> BorrowExplanation<'tcx> {
local variable `x` and then make `x` be the expression at the \
end of the block",
vec![
(info.span.shrink_to_lo(), "let x = ".to_string()),
(info.span.shrink_to_hi(), "; x".to_string()),
(sp.shrink_to_lo(), "let x = ".to_string()),
(sp.shrink_to_hi(), "; x".to_string()),
],
Applicability::MaybeIncorrect,
);

View file

@ -0,0 +1,52 @@
// Make sure the generated suggestion suggest editing the user
// code instead of the std macro implementation
//@ run-rustfix
#![allow(dead_code)]
use std::fmt::{self, Display};
struct Mutex;
impl Mutex {
fn lock(&self) -> MutexGuard<'_> {
MutexGuard(self)
}
}
struct MutexGuard<'a>(&'a Mutex);
impl<'a> Drop for MutexGuard<'a> {
fn drop(&mut self) {}
}
struct Out;
impl Out {
fn write_fmt(&self, _args: fmt::Arguments) {}
}
impl<'a> Display for MutexGuard<'a> {
fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
Ok(())
}
}
fn main() {
let _write = {
let mutex = Mutex;
write!(Out, "{}", mutex.lock());
//~^ ERROR `mutex` does not live long enough
//~| SUGGESTION ;
};
let _write = {
use std::io::Write as _;
let mutex = Mutex;
let x = write!(std::io::stdout(), "{}", mutex.lock()); x
//~^ ERROR `mutex` does not live long enough
//~| SUGGESTION let x
};
}

View file

@ -0,0 +1,52 @@
// Make sure the generated suggestion suggest editing the user
// code instead of the std macro implementation
//@ run-rustfix
#![allow(dead_code)]
use std::fmt::{self, Display};
struct Mutex;
impl Mutex {
fn lock(&self) -> MutexGuard<'_> {
MutexGuard(self)
}
}
struct MutexGuard<'a>(&'a Mutex);
impl<'a> Drop for MutexGuard<'a> {
fn drop(&mut self) {}
}
struct Out;
impl Out {
fn write_fmt(&self, _args: fmt::Arguments) {}
}
impl<'a> Display for MutexGuard<'a> {
fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
Ok(())
}
}
fn main() {
let _write = {
let mutex = Mutex;
write!(Out, "{}", mutex.lock())
//~^ ERROR `mutex` does not live long enough
//~| SUGGESTION ;
};
let _write = {
use std::io::Write as _;
let mutex = Mutex;
write!(std::io::stdout(), "{}", mutex.lock())
//~^ ERROR `mutex` does not live long enough
//~| SUGGESTION let x
};
}

View file

@ -0,0 +1,47 @@
error[E0597]: `mutex` does not live long enough
--> $DIR/span-semicolon-issue-139049.rs:39:27
|
LL | let mutex = Mutex;
| ----- binding `mutex` declared here
LL | write!(Out, "{}", mutex.lock())
| ^^^^^-------
| |
| borrowed value does not live long enough
| a temporary with access to the borrow is created here ...
...
LL | };
| -- ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `MutexGuard`
| |
| `mutex` dropped here while still borrowed
|
help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
|
LL | write!(Out, "{}", mutex.lock());
| +
error[E0597]: `mutex` does not live long enough
--> $DIR/span-semicolon-issue-139049.rs:48:41
|
LL | let mutex = Mutex;
| ----- binding `mutex` declared here
LL | write!(std::io::stdout(), "{}", mutex.lock())
| ^^^^^-------
| |
| borrowed value does not live long enough
| a temporary with access to the borrow is created here ...
...
LL | };
| -- ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `MutexGuard`
| |
| `mutex` dropped here while still borrowed
|
= note: the temporary is part of an expression at the end of a block;
consider forcing this temporary to be dropped sooner, before the block's local variables are dropped
help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block
|
LL | let x = write!(std::io::stdout(), "{}", mutex.lock()); x
| +++++++ +++
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0597`.

View file

@ -14,6 +14,10 @@ LL | };
| |
| `mutex` dropped here while still borrowed
|
help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
|
LL | write!(Out, "{}", mutex.lock()); /* no semicolon */
| +
error[E0597]: `mutex` does not live long enough
--> $DIR/format-args-temporaries-in-write.rs:47:29
@ -31,6 +35,10 @@ LL | };
| |
| `mutex` dropped here while still borrowed
|
help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
|
LL | writeln!(Out, "{}", mutex.lock()); /* no semicolon */
| +
error: aborting due to 2 previous errors