about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLeón Orell Valerian Liehr <me@fmease.dev>2025-06-15 23:51:58 +0200
committerGitHub <noreply@github.com>2025-06-15 23:51:58 +0200
commit07048643ddcb2245ba6251fc65403f308e6c38c4 (patch)
tree161db3474e8c9a541c54f01f460100a913b3020a
parentb83fb800a7ffc321c63ec2f716cef3e15ee6f81b (diff)
parent6ff3713e0fc60146a36774b447204677ab7b8d5a (diff)
downloadrust-07048643ddcb2245ba6251fc65403f308e6c38c4.tar.gz
rust-07048643ddcb2245ba6251fc65403f308e6c38c4.zip
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`
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs10
-rw-r--r--tests/ui/borrowck/span-semicolon-issue-139049.fixed52
-rw-r--r--tests/ui/borrowck/span-semicolon-issue-139049.rs52
-rw-r--r--tests/ui/borrowck/span-semicolon-issue-139049.stderr47
-rw-r--r--tests/ui/macros/format-args-temporaries-in-write.stderr8
5 files changed, 166 insertions, 3 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index a845431faca..c4b0f503664 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -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,
                                 );
diff --git a/tests/ui/borrowck/span-semicolon-issue-139049.fixed b/tests/ui/borrowck/span-semicolon-issue-139049.fixed
new file mode 100644
index 00000000000..0b263b22296
--- /dev/null
+++ b/tests/ui/borrowck/span-semicolon-issue-139049.fixed
@@ -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
+    };
+}
diff --git a/tests/ui/borrowck/span-semicolon-issue-139049.rs b/tests/ui/borrowck/span-semicolon-issue-139049.rs
new file mode 100644
index 00000000000..a92742ac94b
--- /dev/null
+++ b/tests/ui/borrowck/span-semicolon-issue-139049.rs
@@ -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
+    };
+}
diff --git a/tests/ui/borrowck/span-semicolon-issue-139049.stderr b/tests/ui/borrowck/span-semicolon-issue-139049.stderr
new file mode 100644
index 00000000000..123bdf4bc67
--- /dev/null
+++ b/tests/ui/borrowck/span-semicolon-issue-139049.stderr
@@ -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`.
diff --git a/tests/ui/macros/format-args-temporaries-in-write.stderr b/tests/ui/macros/format-args-temporaries-in-write.stderr
index e05246cfbe3..e58a43383f6 100644
--- a/tests/ui/macros/format-args-temporaries-in-write.stderr
+++ b/tests/ui/macros/format-args-temporaries-in-write.stderr
@@ -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