about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2024-04-18 20:41:43 +0000
committerEsteban Küber <esteban@kuber.com.ar>2024-04-24 22:21:13 +0000
commit4aba2c55e6bbe6c09132ff19360d302269dca775 (patch)
treec7d83085f07dbdbc30cb34a437db3f6f85d2c5d7
parentef8b9dcf23700f2e2265317611460d3a65c19eff (diff)
downloadrust-4aba2c55e6bbe6c09132ff19360d302269dca775.tar.gz
rust-4aba2c55e6bbe6c09132ff19360d302269dca775.zip
Modify `find_expr` from `Span` to better account for closures
Start pointing to where bindings were declared when they are captured in closures:

```
error[E0597]: `x` does not live long enough
  --> $DIR/suggest-return-closure.rs:23:9
   |
LL |     let x = String::new();
   |         - binding `x` declared here
...
LL |     |c| {
   |     --- value captured here
LL |         x.push(c);
   |         ^ borrowed value does not live long enough
...
LL | }
   | -- borrow later used here
   | |
   | `x` dropped here while still borrowed
```

Suggest cloning in more cases involving closures:

```
error[E0507]: cannot move out of `foo` in pattern guard
  --> $DIR/issue-27282-move-ref-mut-into-guard.rs:11:19
   |
LL |             if { (|| { let mut bar = foo; bar.take() })(); false } => {},
   |                   ^^                 --- move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait
   |                   |
   |                   `foo` is moved here
   |
   = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
help: consider cloning the value if the performance cost is acceptable
   |
LL |             if { (|| { let mut bar = foo.clone(); bar.take() })(); false } => {},
   |                                         ++++++++
```
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs6
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs11
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs2
-rw-r--r--tests/ui/coroutine/borrowing.stderr2
-rw-r--r--tests/ui/coroutine/dropck.stderr3
-rw-r--r--tests/ui/fn/suggest-return-closure.rs1
-rw-r--r--tests/ui/fn/suggest-return-closure.stderr9
-rw-r--r--tests/ui/nll/closure-borrow-spans.stderr6
-rw-r--r--tests/ui/nll/closure-requirements/escape-upvar-nested.stderr3
-rw-r--r--tests/ui/nll/closure-requirements/escape-upvar-ref.stderr2
-rw-r--r--tests/ui/nll/closure-requirements/propagate-multiple-requirements.stderr2
-rw-r--r--tests/ui/nll/issue-27282-move-ref-mut-into-guard.fixed23
-rw-r--r--tests/ui/nll/issue-27282-move-ref-mut-into-guard.rs6
-rw-r--r--tests/ui/nll/issue-27282-move-ref-mut-into-guard.stderr16
-rw-r--r--tests/ui/nll/issue-27282-mutation-in-guard.stderr8
-rw-r--r--tests/ui/nll/issue-42574-diagnostic-in-nested-closure.stderr2
-rw-r--r--tests/ui/nll/match-guards-always-borrow.fixed66
-rw-r--r--tests/ui/nll/match-guards-always-borrow.rs6
-rw-r--r--tests/ui/nll/match-guards-always-borrow.stderr20
-rw-r--r--tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.nll.stderr2
-rw-r--r--tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.polonius.stderr2
-rw-r--r--tests/ui/nll/user-annotations/method-ufcs-1.stderr4
-rw-r--r--tests/ui/nll/user-annotations/method-ufcs-2.stderr4
-rw-r--r--tests/ui/regions/regions-addr-of-upvar-self.stderr2
-rw-r--r--tests/ui/regions/regions-nested-fns-2.stderr3
-rw-r--r--tests/ui/regions/regions-nested-fns.stderr3
-rw-r--r--tests/ui/regions/regions-steal-closure.stderr1
-rw-r--r--tests/ui/span/send-is-not-static-ensures-scoping.stderr3
-rw-r--r--tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.stderr7
31 files changed, 202 insertions, 31 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 4878da530b0..ed8c9bd51cf 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -1964,7 +1964,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     pub(crate) fn find_expr(&self, span: Span) -> Option<&hir::Expr<'_>> {
         let tcx = self.infcx.tcx;
         let body_id = tcx.hir_node(self.mir_hir_id()).body_id()?;
-        let mut expr_finder = FindExprBySpan::new(span);
+        let mut expr_finder = FindExprBySpan::new(span, tcx);
         expr_finder.visit_expr(tcx.hir().body(body_id).value);
         expr_finder.result
     }
@@ -1998,14 +1998,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             };
 
             let mut expr_finder =
-                FindExprBySpan::new(self.body.local_decls[*index1].source_info.span);
+                FindExprBySpan::new(self.body.local_decls[*index1].source_info.span, tcx);
             expr_finder.visit_expr(hir.body(body_id).value);
             let Some(index1) = expr_finder.result else {
                 note_default_suggestion();
                 return;
             };
 
-            expr_finder = FindExprBySpan::new(self.body.local_decls[*index2].source_info.span);
+            expr_finder = FindExprBySpan::new(self.body.local_decls[*index2].source_info.span, tcx);
             expr_finder.visit_expr(hir.body(body_id).value);
             let Some(index2) = expr_finder.result else {
                 note_default_suggestion();
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index 418eabe3ae2..5ebdb69050b 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -76,7 +76,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
                 && let Some(body_id) = node.body_id()
             {
                 let body = tcx.hir().body(body_id);
-                let mut expr_finder = FindExprBySpan::new(span);
+                let mut expr_finder = FindExprBySpan::new(span, tcx);
                 expr_finder.visit_expr(body.value);
                 if let Some(mut expr) = expr_finder.result {
                     while let hir::ExprKind::AddrOf(_, _, inner)
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 837b784f272..4731f11ad32 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -50,15 +50,22 @@ pub struct FindExprBySpan<'hir> {
     pub span: Span,
     pub result: Option<&'hir hir::Expr<'hir>>,
     pub ty_result: Option<&'hir hir::Ty<'hir>>,
+    pub tcx: TyCtxt<'hir>,
 }
 
 impl<'hir> FindExprBySpan<'hir> {
-    pub fn new(span: Span) -> Self {
-        Self { span, result: None, ty_result: None }
+    pub fn new(span: Span, tcx: TyCtxt<'hir>) -> Self {
+        Self { span, result: None, ty_result: None, tcx }
     }
 }
 
 impl<'v> Visitor<'v> for FindExprBySpan<'v> {
+    type NestedFilter = rustc_middle::hir::nested_filter::OnlyBodies;
+
+    fn nested_visit_map(&mut self) -> Self::Map {
+        self.tcx.hir()
+    }
+
     fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
         if self.span == ex.span {
             self.result = Some(ex);
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 9d3caaa01ab..cc879c42ce9 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -901,7 +901,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             // Remove all the desugaring and macro contexts.
             span.remove_mark();
         }
-        let mut expr_finder = FindExprBySpan::new(span);
+        let mut expr_finder = FindExprBySpan::new(span, self.tcx);
         let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else {
             return;
         };
@@ -1367,7 +1367,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                             return false;
                         };
                         let body = self.tcx.hir().body(body_id);
-                        let mut expr_finder = FindExprBySpan::new(span);
+                        let mut expr_finder = FindExprBySpan::new(span, self.tcx);
                         expr_finder.visit_expr(body.value);
                         let Some(expr) = expr_finder.result else {
                             return false;
@@ -1469,7 +1469,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             // Remove all the hir desugaring contexts while maintaining the macro contexts.
             span.remove_mark();
         }
-        let mut expr_finder = super::FindExprBySpan::new(span);
+        let mut expr_finder = super::FindExprBySpan::new(span, self.tcx);
         let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else {
             return false;
         };
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
index 0e309689680..1971136e54c 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
@@ -2457,7 +2457,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     && let Some(body_id) =
                         self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id)
                 {
-                    let mut expr_finder = FindExprBySpan::new(span);
+                    let mut expr_finder = FindExprBySpan::new(span, self.tcx);
                     expr_finder.visit_expr(self.tcx.hir().body(body_id).value);
 
                     if let Some(hir::Expr {
diff --git a/tests/ui/coroutine/borrowing.stderr b/tests/ui/coroutine/borrowing.stderr
index 9132e5d84ed..4f8b9737777 100644
--- a/tests/ui/coroutine/borrowing.stderr
+++ b/tests/ui/coroutine/borrowing.stderr
@@ -4,6 +4,7 @@ error[E0597]: `a` does not live long enough
 LL |     let _b = {
    |         -- borrow later stored here
 LL |         let a = 3;
+   |             - binding `a` declared here
 LL |         Pin::new(&mut #[coroutine] || yield &a).resume(())
    |                                    --        ^ borrowed value does not live long enough
    |                                    |
@@ -18,6 +19,7 @@ error[E0597]: `a` does not live long enough
 LL |     let _b = {
    |         -- borrow later stored here
 LL |         let a = 3;
+   |             - binding `a` declared here
 LL |         #[coroutine] || {
    |                      -- value captured here by coroutine
 LL |             yield &a
diff --git a/tests/ui/coroutine/dropck.stderr b/tests/ui/coroutine/dropck.stderr
index d6cd7c4cd47..78fdeec972f 100644
--- a/tests/ui/coroutine/dropck.stderr
+++ b/tests/ui/coroutine/dropck.stderr
@@ -18,6 +18,9 @@ LL | }
 error[E0597]: `ref_` does not live long enough
   --> $DIR/dropck.rs:16:18
    |
+LL |     let ref_ = Box::leak(Box::new(Some(cell.borrow_mut())));
+   |         ---- binding `ref_` declared here
+...
 LL |     || {
    |     -- value captured here by coroutine
 LL |         // but the coroutine can use it to drop a `Ref<'a, i32>`.
diff --git a/tests/ui/fn/suggest-return-closure.rs b/tests/ui/fn/suggest-return-closure.rs
index 81f20272867..30e25ca8edc 100644
--- a/tests/ui/fn/suggest-return-closure.rs
+++ b/tests/ui/fn/suggest-return-closure.rs
@@ -18,6 +18,7 @@ fn fn_mut() -> _ {
     //~| NOTE for more information on `Fn` traits and closure types
     let x = String::new();
     //~^ HELP: consider changing this to be mutable
+    //~| NOTE binding `x` declared here
     |c| { //~ NOTE: value captured here
         x.push(c);
         //~^ ERROR: does not live long enough
diff --git a/tests/ui/fn/suggest-return-closure.stderr b/tests/ui/fn/suggest-return-closure.stderr
index 8e80a11fe1b..d276ce8be2b 100644
--- a/tests/ui/fn/suggest-return-closure.stderr
+++ b/tests/ui/fn/suggest-return-closure.stderr
@@ -21,7 +21,7 @@ LL | fn fn_mut() -> _ {
    = note: for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html
 
 error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
-  --> $DIR/suggest-return-closure.rs:31:13
+  --> $DIR/suggest-return-closure.rs:32:13
    |
 LL | fn fun() -> _ {
    |             ^
@@ -32,7 +32,7 @@ LL | fn fun() -> _ {
    = note: for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html
 
 error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
-  --> $DIR/suggest-return-closure.rs:22:9
+  --> $DIR/suggest-return-closure.rs:23:9
    |
 LL |     let x = String::new();
    |         - help: consider changing this to be mutable: `mut x`
@@ -41,8 +41,11 @@ LL |         x.push(c);
    |         ^ cannot borrow as mutable
 
 error[E0597]: `x` does not live long enough
-  --> $DIR/suggest-return-closure.rs:22:9
+  --> $DIR/suggest-return-closure.rs:23:9
    |
+LL |     let x = String::new();
+   |         - binding `x` declared here
+...
 LL |     |c| {
    |     --- value captured here
 LL |         x.push(c);
diff --git a/tests/ui/nll/closure-borrow-spans.stderr b/tests/ui/nll/closure-borrow-spans.stderr
index cac22c2ecda..c466cad25d2 100644
--- a/tests/ui/nll/closure-borrow-spans.stderr
+++ b/tests/ui/nll/closure-borrow-spans.stderr
@@ -25,6 +25,8 @@ LL |     f.use_ref();
 error[E0597]: `x` does not live long enough
   --> $DIR/closure-borrow-spans.rs:19:16
    |
+LL |         let x = 1;
+   |             - binding `x` declared here
 LL |         f = || x;
    |             -- ^ borrowed value does not live long enough
    |             |
@@ -85,6 +87,8 @@ LL |     f.use_ref();
 error[E0597]: `x` does not live long enough
   --> $DIR/closure-borrow-spans.rs:52:16
    |
+LL |         let mut x = 1;
+   |             ----- binding `x` declared here
 LL |         f = || x = 0;
    |             -- ^ borrowed value does not live long enough
    |             |
@@ -145,6 +149,8 @@ LL |     f.use_ref();
 error[E0597]: `x` does not live long enough
   --> $DIR/closure-borrow-spans.rs:86:16
    |
+LL |         let x = &mut z;
+   |             - binding `x` declared here
 LL |         f = || *x = 0;
    |             -- ^^ borrowed value does not live long enough
    |             |
diff --git a/tests/ui/nll/closure-requirements/escape-upvar-nested.stderr b/tests/ui/nll/closure-requirements/escape-upvar-nested.stderr
index aa73e91cc77..8e47ab780f2 100644
--- a/tests/ui/nll/closure-requirements/escape-upvar-nested.stderr
+++ b/tests/ui/nll/closure-requirements/escape-upvar-nested.stderr
@@ -37,6 +37,9 @@ LL | fn test() {
 error[E0597]: `y` does not live long enough
   --> $DIR/escape-upvar-nested.rs:21:40
    |
+LL |         let y = 22;
+   |             - binding `y` declared here
+LL |
 LL |         let mut closure = || {
    |                           -- value captured here
 LL |             let mut closure1 = || p = &y;
diff --git a/tests/ui/nll/closure-requirements/escape-upvar-ref.stderr b/tests/ui/nll/closure-requirements/escape-upvar-ref.stderr
index 949dcc78703..c428150aa2f 100644
--- a/tests/ui/nll/closure-requirements/escape-upvar-ref.stderr
+++ b/tests/ui/nll/closure-requirements/escape-upvar-ref.stderr
@@ -23,6 +23,8 @@ LL | fn test() {
 error[E0597]: `y` does not live long enough
   --> $DIR/escape-upvar-ref.rs:23:35
    |
+LL |         let y = 22;
+   |             - binding `y` declared here
 LL |         let mut closure = || p = &y;
    |                           --      ^ borrowed value does not live long enough
    |                           |
diff --git a/tests/ui/nll/closure-requirements/propagate-multiple-requirements.stderr b/tests/ui/nll/closure-requirements/propagate-multiple-requirements.stderr
index 81b5f09b041..15f48d88c37 100644
--- a/tests/ui/nll/closure-requirements/propagate-multiple-requirements.stderr
+++ b/tests/ui/nll/closure-requirements/propagate-multiple-requirements.stderr
@@ -1,6 +1,8 @@
 error[E0597]: `local_arr` does not live long enough
   --> $DIR/propagate-multiple-requirements.rs:15:14
    |
+LL |     let local_arr = other_local_arr;
+   |         --------- binding `local_arr` declared here
 LL |     let mut out: &mut &'static [i32] = &mut (&[1] as _);
    |                  ------------------- type annotation requires that `local_arr` is borrowed for `'static`
 LL |     once(|mut z: &[i32], mut out_val: &mut &[i32]| {
diff --git a/tests/ui/nll/issue-27282-move-ref-mut-into-guard.fixed b/tests/ui/nll/issue-27282-move-ref-mut-into-guard.fixed
new file mode 100644
index 00000000000..7692be7ccc8
--- /dev/null
+++ b/tests/ui/nll/issue-27282-move-ref-mut-into-guard.fixed
@@ -0,0 +1,23 @@
+// Issue 27282: Example 1: This sidesteps the AST checks disallowing
+// mutable borrows in match guards by hiding the mutable borrow in a
+// guard behind a move (of the ref mut pattern id) within a closure.
+//@ run-rustfix
+#![feature(if_let_guard)]
+
+fn main() {
+    match Some(&4) {
+        None => {},
+        ref mut foo
+            if { (|| { let mut bar = foo.clone(); bar.take() })(); false } => {},
+        //~^ ERROR cannot move out of `foo` in pattern guard [E0507]
+        Some(s) => std::process::exit(*s),
+    }
+
+    match Some(&4) {
+        None => {},
+        ref mut foo
+            if let Some(()) = { (|| { let mut bar = foo.clone(); bar.take() })(); None } => {},
+        //~^ ERROR cannot move out of `foo` in pattern guard [E0507]
+        Some(s) => std::process::exit(*s),
+    }
+}
diff --git a/tests/ui/nll/issue-27282-move-ref-mut-into-guard.rs b/tests/ui/nll/issue-27282-move-ref-mut-into-guard.rs
index 833ca8afd61..f3d0a184e03 100644
--- a/tests/ui/nll/issue-27282-move-ref-mut-into-guard.rs
+++ b/tests/ui/nll/issue-27282-move-ref-mut-into-guard.rs
@@ -1,14 +1,14 @@
 // Issue 27282: Example 1: This sidesteps the AST checks disallowing
 // mutable borrows in match guards by hiding the mutable borrow in a
 // guard behind a move (of the ref mut pattern id) within a closure.
-
+//@ run-rustfix
 #![feature(if_let_guard)]
 
 fn main() {
     match Some(&4) {
         None => {},
         ref mut foo
-            if { (|| { let bar = foo; bar.take() })(); false } => {},
+            if { (|| { let mut bar = foo; bar.take() })(); false } => {},
         //~^ ERROR cannot move out of `foo` in pattern guard [E0507]
         Some(s) => std::process::exit(*s),
     }
@@ -16,7 +16,7 @@ fn main() {
     match Some(&4) {
         None => {},
         ref mut foo
-            if let Some(()) = { (|| { let bar = foo; bar.take() })(); None } => {},
+            if let Some(()) = { (|| { let mut bar = foo; bar.take() })(); None } => {},
         //~^ ERROR cannot move out of `foo` in pattern guard [E0507]
         Some(s) => std::process::exit(*s),
     }
diff --git a/tests/ui/nll/issue-27282-move-ref-mut-into-guard.stderr b/tests/ui/nll/issue-27282-move-ref-mut-into-guard.stderr
index 4a512560c87..7781e77894b 100644
--- a/tests/ui/nll/issue-27282-move-ref-mut-into-guard.stderr
+++ b/tests/ui/nll/issue-27282-move-ref-mut-into-guard.stderr
@@ -1,22 +1,30 @@
 error[E0507]: cannot move out of `foo` in pattern guard
   --> $DIR/issue-27282-move-ref-mut-into-guard.rs:11:19
    |
-LL |             if { (|| { let bar = foo; bar.take() })(); false } => {},
-   |                   ^^             --- move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait
+LL |             if { (|| { let mut bar = foo; bar.take() })(); false } => {},
+   |                   ^^                 --- move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait
    |                   |
    |                   `foo` is moved here
    |
    = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |             if { (|| { let mut bar = foo.clone(); bar.take() })(); false } => {},
+   |                                         ++++++++
 
 error[E0507]: cannot move out of `foo` in pattern guard
   --> $DIR/issue-27282-move-ref-mut-into-guard.rs:19:34
    |
-LL |             if let Some(()) = { (|| { let bar = foo; bar.take() })(); None } => {},
-   |                                  ^^             --- move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait
+LL |             if let Some(()) = { (|| { let mut bar = foo; bar.take() })(); None } => {},
+   |                                  ^^                 --- move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait
    |                                  |
    |                                  `foo` is moved here
    |
    = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |             if let Some(()) = { (|| { let mut bar = foo.clone(); bar.take() })(); None } => {},
+   |                                                        ++++++++
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/nll/issue-27282-mutation-in-guard.stderr b/tests/ui/nll/issue-27282-mutation-in-guard.stderr
index 0b5d723172c..f73e4aaa489 100644
--- a/tests/ui/nll/issue-27282-mutation-in-guard.stderr
+++ b/tests/ui/nll/issue-27282-mutation-in-guard.stderr
@@ -7,6 +7,10 @@ LL |                 (|| { let bar = foo; bar.take() })();
    |                  `foo` is moved here
    |
    = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |                 (|| { let bar = foo.clone(); bar.take() })();
+   |                                    ++++++++
 
 error[E0507]: cannot move out of `foo` in pattern guard
   --> $DIR/issue-27282-mutation-in-guard.rs:20:18
@@ -17,6 +21,10 @@ LL |                 (|| { let bar = foo; bar.take() })();
    |                  `foo` is moved here
    |
    = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |                 (|| { let bar = foo.clone(); bar.take() })();
+   |                                    ++++++++
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/nll/issue-42574-diagnostic-in-nested-closure.stderr b/tests/ui/nll/issue-42574-diagnostic-in-nested-closure.stderr
index f7a525ee9b0..e3f44467550 100644
--- a/tests/ui/nll/issue-42574-diagnostic-in-nested-closure.stderr
+++ b/tests/ui/nll/issue-42574-diagnostic-in-nested-closure.stderr
@@ -11,6 +11,8 @@ LL |     || doit(data);
 error[E0597]: `data` does not live long enough
   --> $DIR/issue-42574-diagnostic-in-nested-closure.rs:6:13
    |
+LL | fn doit(data: &'static mut ()) {
+   |         ---- binding `data` declared here
 LL |     || doit(data);
    |     -- -----^^^^-
    |     |  |    |
diff --git a/tests/ui/nll/match-guards-always-borrow.fixed b/tests/ui/nll/match-guards-always-borrow.fixed
new file mode 100644
index 00000000000..56e743bf196
--- /dev/null
+++ b/tests/ui/nll/match-guards-always-borrow.fixed
@@ -0,0 +1,66 @@
+#![feature(if_let_guard)]
+#![allow(unused_mut)]
+//@ run-rustfix
+
+// Here is arielb1's basic example from rust-lang/rust#27282
+// that AST borrowck is flummoxed by:
+
+fn should_reject_destructive_mutate_in_guard() {
+    match Some(&4) {
+        None => {},
+        ref mut foo if {
+            (|| { let mut bar = foo.clone(); bar.take() })();
+            //~^ ERROR cannot move out of `foo` in pattern guard [E0507]
+            false } => { },
+        Some(s) => std::process::exit(*s),
+    }
+
+    match Some(&4) {
+        None => {},
+        ref mut foo if let Some(()) = {
+            (|| { let mut bar = foo.clone(); bar.take() })();
+            //~^ ERROR cannot move out of `foo` in pattern guard [E0507]
+            None } => { },
+        Some(s) => std::process::exit(*s),
+    }
+}
+
+// Here below is a case that needs to keep working: we only use the
+// binding via immutable-borrow in the guard, and we mutate in the arm
+// body.
+fn allow_mutate_in_arm_body() {
+    match Some(&4) {
+        None => {},
+        ref mut foo if foo.is_some() => { foo.take(); () }
+        Some(s) => std::process::exit(*s),
+    }
+
+    match Some(&4) {
+        None => {},
+        ref mut foo if let Some(_) = foo => { foo.take(); () }
+        Some(s) => std::process::exit(*s),
+    }
+}
+
+// Here below is a case that needs to keep working: we only use the
+// binding via immutable-borrow in the guard, and we move into the arm
+// body.
+fn allow_move_into_arm_body() {
+    match Some(&4) {
+        None => {},
+        mut foo if foo.is_some() => { foo.unwrap(); () }
+        Some(s) => std::process::exit(*s),
+    }
+
+    match Some(&4) {
+        None => {},
+        mut foo if let Some(_) = foo => { foo.unwrap(); () }
+        Some(s) => std::process::exit(*s),
+    }
+}
+
+fn main() {
+    should_reject_destructive_mutate_in_guard();
+    allow_mutate_in_arm_body();
+    allow_move_into_arm_body();
+}
diff --git a/tests/ui/nll/match-guards-always-borrow.rs b/tests/ui/nll/match-guards-always-borrow.rs
index ff63cc09273..927d55c42a6 100644
--- a/tests/ui/nll/match-guards-always-borrow.rs
+++ b/tests/ui/nll/match-guards-always-borrow.rs
@@ -1,4 +1,6 @@
 #![feature(if_let_guard)]
+#![allow(unused_mut)]
+//@ run-rustfix
 
 // Here is arielb1's basic example from rust-lang/rust#27282
 // that AST borrowck is flummoxed by:
@@ -7,7 +9,7 @@ fn should_reject_destructive_mutate_in_guard() {
     match Some(&4) {
         None => {},
         ref mut foo if {
-            (|| { let bar = foo; bar.take() })();
+            (|| { let mut bar = foo; bar.take() })();
             //~^ ERROR cannot move out of `foo` in pattern guard [E0507]
             false } => { },
         Some(s) => std::process::exit(*s),
@@ -16,7 +18,7 @@ fn should_reject_destructive_mutate_in_guard() {
     match Some(&4) {
         None => {},
         ref mut foo if let Some(()) = {
-            (|| { let bar = foo; bar.take() })();
+            (|| { let mut bar = foo; bar.take() })();
             //~^ ERROR cannot move out of `foo` in pattern guard [E0507]
             None } => { },
         Some(s) => std::process::exit(*s),
diff --git a/tests/ui/nll/match-guards-always-borrow.stderr b/tests/ui/nll/match-guards-always-borrow.stderr
index afd853c403e..bb0c5bd4c97 100644
--- a/tests/ui/nll/match-guards-always-borrow.stderr
+++ b/tests/ui/nll/match-guards-always-borrow.stderr
@@ -1,22 +1,30 @@
 error[E0507]: cannot move out of `foo` in pattern guard
-  --> $DIR/match-guards-always-borrow.rs:10:14
+  --> $DIR/match-guards-always-borrow.rs:12:14
    |
-LL |             (|| { let bar = foo; bar.take() })();
-   |              ^^             --- move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait
+LL |             (|| { let mut bar = foo; bar.take() })();
+   |              ^^                 --- move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait
    |              |
    |              `foo` is moved here
    |
    = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |             (|| { let mut bar = foo.clone(); bar.take() })();
+   |                                    ++++++++
 
 error[E0507]: cannot move out of `foo` in pattern guard
-  --> $DIR/match-guards-always-borrow.rs:19:14
+  --> $DIR/match-guards-always-borrow.rs:21:14
    |
-LL |             (|| { let bar = foo; bar.take() })();
-   |              ^^             --- move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait
+LL |             (|| { let mut bar = foo; bar.take() })();
+   |              ^^                 --- move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait
    |              |
    |              `foo` is moved here
    |
    = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |             (|| { let mut bar = foo.clone(); bar.take() })();
+   |                                    ++++++++
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.nll.stderr b/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.nll.stderr
index 5227ca8ec17..1d086c658df 100644
--- a/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.nll.stderr
+++ b/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.nll.stderr
@@ -1,6 +1,8 @@
 error[E0597]: `a` does not live long enough
   --> $DIR/location-insensitive-scopes-issue-117146.rs:10:18
    |
+LL |     let a = ();
+   |         - binding `a` declared here
 LL |     let b = |_| &a;
    |             --- -^
    |             |   ||
diff --git a/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.polonius.stderr b/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.polonius.stderr
index 5227ca8ec17..1d086c658df 100644
--- a/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.polonius.stderr
+++ b/tests/ui/nll/polonius/location-insensitive-scopes-issue-117146.polonius.stderr
@@ -1,6 +1,8 @@
 error[E0597]: `a` does not live long enough
   --> $DIR/location-insensitive-scopes-issue-117146.rs:10:18
    |
+LL |     let a = ();
+   |         - binding `a` declared here
 LL |     let b = |_| &a;
    |             --- -^
    |             |   ||
diff --git a/tests/ui/nll/user-annotations/method-ufcs-1.stderr b/tests/ui/nll/user-annotations/method-ufcs-1.stderr
index c7c08c948ab..c42ea0172cf 100644
--- a/tests/ui/nll/user-annotations/method-ufcs-1.stderr
+++ b/tests/ui/nll/user-annotations/method-ufcs-1.stderr
@@ -33,7 +33,9 @@ error[E0597]: `a` does not live long enough
    |
 LL | fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) {
    |                                              -- lifetime `'a` defined here
-...
+LL |     let a = 22;
+   |         - binding `a` declared here
+LL |     let b = 44;
 LL |     let _closure = || {
    |                    -- value captured here
 LL |         let c = 66;
diff --git a/tests/ui/nll/user-annotations/method-ufcs-2.stderr b/tests/ui/nll/user-annotations/method-ufcs-2.stderr
index b7861a3bd06..287337c7d52 100644
--- a/tests/ui/nll/user-annotations/method-ufcs-2.stderr
+++ b/tests/ui/nll/user-annotations/method-ufcs-2.stderr
@@ -34,7 +34,9 @@ error[E0597]: `b` does not live long enough
    |
 LL | fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) {
    |                                              -- lifetime `'a` defined here
-...
+LL |     let a = 22;
+LL |     let b = 44;
+   |         - binding `b` declared here
 LL |     let _closure = || {
    |                    -- value captured here
 LL |         let c = 66;
diff --git a/tests/ui/regions/regions-addr-of-upvar-self.stderr b/tests/ui/regions/regions-addr-of-upvar-self.stderr
index c16a6f8585b..3a028cc9e21 100644
--- a/tests/ui/regions/regions-addr-of-upvar-self.stderr
+++ b/tests/ui/regions/regions-addr-of-upvar-self.stderr
@@ -20,6 +20,8 @@ LL |             let p: &'static mut usize = &mut self.food;
 error[E0597]: `self` does not live long enough
   --> $DIR/regions-addr-of-upvar-self.rs:8:46
    |
+LL |     pub fn chase_cat(&mut self) {
+   |                      --------- binding `self` declared here
 LL |         let _f = || {
    |                  -- value captured here
 LL |             let p: &'static mut usize = &mut self.food;
diff --git a/tests/ui/regions/regions-nested-fns-2.stderr b/tests/ui/regions/regions-nested-fns-2.stderr
index 254497639a1..02359fe1213 100644
--- a/tests/ui/regions/regions-nested-fns-2.stderr
+++ b/tests/ui/regions/regions-nested-fns-2.stderr
@@ -1,6 +1,9 @@
 error[E0597]: `y` does not live long enough
   --> $DIR/regions-nested-fns-2.rs:7:25
    |
+LL |     let y = 3;
+   |         - binding `y` declared here
+LL |     ignore(
 LL |         |z| {
    |         --- value captured here
 LL |             if false { &y } else { z }
diff --git a/tests/ui/regions/regions-nested-fns.stderr b/tests/ui/regions/regions-nested-fns.stderr
index ee43f9fa572..23b3f78dd4e 100644
--- a/tests/ui/regions/regions-nested-fns.stderr
+++ b/tests/ui/regions/regions-nested-fns.stderr
@@ -27,6 +27,9 @@ LL | }
 error[E0597]: `y` does not live long enough
   --> $DIR/regions-nested-fns.rs:10:15
    |
+LL |     let y = 3;
+   |         - binding `y` declared here
+...
 LL |     ignore::<Box<dyn for<'z> FnMut(&'z isize)>>(Box::new(|z| {
    |                                                          --- value captured here
 LL |         ay = x;
diff --git a/tests/ui/regions/regions-steal-closure.stderr b/tests/ui/regions/regions-steal-closure.stderr
index 9324eb892a6..50068b32fa3 100644
--- a/tests/ui/regions/regions-steal-closure.stderr
+++ b/tests/ui/regions/regions-steal-closure.stderr
@@ -4,6 +4,7 @@ error[E0597]: `i` does not live long enough
 LL |     let mut cl_box = {
    |         ---------- borrow later stored here
 LL |         let mut i = 3;
+   |             ----- binding `i` declared here
 LL |         box_it(Box::new(|| i += 1))
    |                         -- ^ borrowed value does not live long enough
    |                         |
diff --git a/tests/ui/span/send-is-not-static-ensures-scoping.stderr b/tests/ui/span/send-is-not-static-ensures-scoping.stderr
index bae0befcaca..c15547e8412 100644
--- a/tests/ui/span/send-is-not-static-ensures-scoping.stderr
+++ b/tests/ui/span/send-is-not-static-ensures-scoping.stderr
@@ -16,6 +16,9 @@ error[E0597]: `y` does not live long enough
    |
 LL |     let bad = {
    |         --- borrow later stored here
+LL |         let x = 1;
+LL |         let y = &x;
+   |             - binding `y` declared here
 ...
 LL |         scoped(|| {
    |                -- value captured here
diff --git a/tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.stderr b/tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.stderr
index 23aa18d7156..fc6f610ddd4 100644
--- a/tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.stderr
+++ b/tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.stderr
@@ -1,6 +1,9 @@
 error[E0597]: `factorial` does not live long enough
   --> $DIR/unboxed-closures-failed-recursive-fn-1.rs:15:17
    |
+LL |     let mut factorial: Option<Box<dyn Fn(u32) -> u32>> = None;
+   |         ------------- binding `factorial` declared here
+LL |
 LL |     let f = |x: u32| -> u32 {
    |             --------------- value captured here
 LL |         let g = factorial.as_ref().unwrap();
@@ -30,7 +33,9 @@ error[E0597]: `factorial` does not live long enough
   --> $DIR/unboxed-closures-failed-recursive-fn-1.rs:28:17
    |
 LL |     let mut factorial: Option<Box<dyn Fn(u32) -> u32 + 'static>> = None;
-   |                        ----------------------------------------- type annotation requires that `factorial` is borrowed for `'static`
+   |         -------------  ----------------------------------------- type annotation requires that `factorial` is borrowed for `'static`
+   |         |
+   |         binding `factorial` declared here
 LL |
 LL |     let f = |x: u32| -> u32 {
    |             --------------- value captured here