about summary refs log tree commit diff
diff options
context:
space:
mode:
authorYan Chen <ychen2@futurewei.com>2022-07-22 09:53:39 -0700
committerYan Chen <ychen2@futurewei.com>2022-08-15 11:20:32 -0700
commit15713e1717219be78c46234d2255fb105f87c02c (patch)
tree5e21707d82618fbc7800c3c03a285348a7877fc0
parent4916e2b9e6ef8cee6f9c6abb75bd01ba9dc07e5c (diff)
downloadrust-15713e1717219be78c46234d2255fb105f87c02c.tar.gz
rust-15713e1717219be78c46234d2255fb105f87c02c.zip
Fix #95079 by adding help and suggestion for missing move in nested closure
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs37
-rw-r--r--src/test/ui/borrowck/borrowck-describe-lvalue.stderr4
-rw-r--r--src/test/ui/borrowck/issue-53432-nested-closure-outlives-borrowed-value.stderr4
-rw-r--r--src/test/ui/borrowck/issue-95079-missing-move-in-nested-closure.rs14
-rw-r--r--src/test/ui/borrowck/issue-95079-missing-move-in-nested-closure.stderr37
-rw-r--r--src/test/ui/issues/issue-40510-3.stderr4
-rw-r--r--src/test/ui/issues/issue-49824.stderr4
7 files changed, 104 insertions, 0 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index bd3a2a3d694..0dce5be953e 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -546,6 +546,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
              executing...",
         );
         diag.note("...therefore, they cannot allow references to captured variables to escape");
+        self.suggest_move_on_borrowing_closure(&mut diag);
 
         diag
     }
@@ -716,6 +717,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
 
         self.add_static_impl_trait_suggestion(&mut diag, *fr, fr_name, *outlived_fr);
         self.suggest_adding_lifetime_params(&mut diag, *fr, *outlived_fr);
+        self.suggest_move_on_borrowing_closure(&mut diag);
 
         diag
     }
@@ -901,4 +903,39 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
 
         suggest_adding_lifetime_params(self.infcx.tcx, sub, ty_sup, ty_sub, diag);
     }
+
+    fn suggest_move_on_borrowing_closure(&self, diag: &mut Diagnostic) {
+        let map = self.infcx.tcx.hir();
+        let body_id = map.body_owned_by(self.mir_def_id());
+        let expr = &map.body(body_id).value;
+        let mut closure_span = None::<rustc_span::Span>;
+        match expr.kind {
+            hir::ExprKind::MethodCall(.., args, _) => {
+                // only the first closre parameter of the method. args[0] is MethodCall PathSegment
+                for i in 1..args.len() {
+                    if let hir::ExprKind::Closure(..) = args[i].kind {
+                        closure_span = Some(args[i].span.shrink_to_lo());
+                        break;
+                    }
+                }
+            }
+            hir::ExprKind::Block(blk, _) => {
+                if let Some(ref expr) = blk.expr {
+                    // only when the block is a closure
+                    if let hir::ExprKind::Closure(..) = expr.kind {
+                        closure_span = Some(expr.span.shrink_to_lo());
+                    }
+                }
+            }
+            _ => {}
+        }
+        if let Some(closure_span) = closure_span {
+            diag.span_suggestion_verbose(
+                closure_span,
+                format!("consider adding 'move' keyword before the nested closure"),
+                "move ",
+                Applicability::MaybeIncorrect,
+            );
+        }
+    }
 }
diff --git a/src/test/ui/borrowck/borrowck-describe-lvalue.stderr b/src/test/ui/borrowck/borrowck-describe-lvalue.stderr
index f909dbc4082..cfcc62de438 100644
--- a/src/test/ui/borrowck/borrowck-describe-lvalue.stderr
+++ b/src/test/ui/borrowck/borrowck-describe-lvalue.stderr
@@ -36,6 +36,10 @@ LL | |                 }
    |
    = note: `FnMut` closures only have access to their captured variables while they are executing...
    = note: ...therefore, they cannot allow references to captured variables to escape
+help: consider adding 'move' keyword before the nested closure
+   |
+LL |                move || {
+   |                ++++
 
 error[E0503]: cannot use `f.x` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:37:9
diff --git a/src/test/ui/borrowck/issue-53432-nested-closure-outlives-borrowed-value.stderr b/src/test/ui/borrowck/issue-53432-nested-closure-outlives-borrowed-value.stderr
index d98b3bae4e0..f0b57484632 100644
--- a/src/test/ui/borrowck/issue-53432-nested-closure-outlives-borrowed-value.stderr
+++ b/src/test/ui/borrowck/issue-53432-nested-closure-outlives-borrowed-value.stderr
@@ -10,6 +10,10 @@ LL |         || f() // The `nested` closure
    |         ^^^^^^ returning this value requires that `'1` must outlive `'2`
    |
    = note: closure implements `Fn`, so references to captured variables can't escape the closure
+help: consider adding 'move' keyword before the nested closure
+   |
+LL |         move || f() // The `nested` closure
+   |         ++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/borrowck/issue-95079-missing-move-in-nested-closure.rs b/src/test/ui/borrowck/issue-95079-missing-move-in-nested-closure.rs
new file mode 100644
index 00000000000..95847d8d301
--- /dev/null
+++ b/src/test/ui/borrowck/issue-95079-missing-move-in-nested-closure.rs
@@ -0,0 +1,14 @@
+fn foo1(s: &str) -> impl Iterator<Item = String> + '_ {
+    None.into_iter()
+        .flat_map(move |()| s.chars().map(|c| format!("{}{}", c, s)))
+        //~^ ERROR captured variable cannot escape `FnMut` closure body
+        //~| HELP consider adding 'move' keyword before the nested closure
+}
+
+fn foo2(s: &str) -> impl Sized + '_ {
+    move |()| s.chars().map(|c| format!("{}{}", c, s))
+    //~^ ERROR lifetime may not live long enough
+    //~| HELP consider adding 'move' keyword before the nested closure
+}
+
+fn main() {}
diff --git a/src/test/ui/borrowck/issue-95079-missing-move-in-nested-closure.stderr b/src/test/ui/borrowck/issue-95079-missing-move-in-nested-closure.stderr
new file mode 100644
index 00000000000..2eae614a2f5
--- /dev/null
+++ b/src/test/ui/borrowck/issue-95079-missing-move-in-nested-closure.stderr
@@ -0,0 +1,37 @@
+error: captured variable cannot escape `FnMut` closure body
+  --> $DIR/issue-95079-missing-move-in-nested-closure.rs:3:29
+   |
+LL | fn foo1(s: &str) -> impl Iterator<Item = String> + '_ {
+   |         - variable defined here
+LL |     None.into_iter()
+LL |         .flat_map(move |()| s.chars().map(|c| format!("{}{}", c, s)))
+   |                           - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                           | |
+   |                           | returns a reference to a captured variable which escapes the closure body
+   |                           | variable captured here
+   |                           inferred to be a `FnMut` closure
+   |
+   = note: `FnMut` closures only have access to their captured variables while they are executing...
+   = note: ...therefore, they cannot allow references to captured variables to escape
+help: consider adding 'move' keyword before the nested closure
+   |
+LL |         .flat_map(move |()| s.chars().map(move |c| format!("{}{}", c, s)))
+   |                                           ++++
+
+error: lifetime may not live long enough
+  --> $DIR/issue-95079-missing-move-in-nested-closure.rs:9:15
+   |
+LL |     move |()| s.chars().map(|c| format!("{}{}", c, s))
+   |     --------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
+   |     |       |
+   |     |       return type of closure `Map<Chars<'_>, [closure@$DIR/issue-95079-missing-move-in-nested-closure.rs:9:29: 9:32]>` contains a lifetime `'2`
+   |     lifetime `'1` represents this closure's body
+   |
+   = note: closure implements `Fn`, so references to captured variables can't escape the closure
+help: consider adding 'move' keyword before the nested closure
+   |
+LL |     move |()| s.chars().map(move |c| format!("{}{}", c, s))
+   |                             ++++
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/issues/issue-40510-3.stderr b/src/test/ui/issues/issue-40510-3.stderr
index 22186ba9a67..eb077415e6c 100644
--- a/src/test/ui/issues/issue-40510-3.stderr
+++ b/src/test/ui/issues/issue-40510-3.stderr
@@ -14,6 +14,10 @@ LL | |         }
    |
    = note: `FnMut` closures only have access to their captured variables while they are executing...
    = note: ...therefore, they cannot allow references to captured variables to escape
+help: consider adding 'move' keyword before the nested closure
+   |
+LL |         move || {
+   |         ++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-49824.stderr b/src/test/ui/issues/issue-49824.stderr
index 2fec482543d..14beadececb 100644
--- a/src/test/ui/issues/issue-49824.stderr
+++ b/src/test/ui/issues/issue-49824.stderr
@@ -14,6 +14,10 @@ LL | |         }
    |
    = note: `FnMut` closures only have access to their captured variables while they are executing...
    = note: ...therefore, they cannot allow references to captured variables to escape
+help: consider adding 'move' keyword before the nested closure
+   |
+LL |         move || {
+   |         ++++
 
 error: aborting due to previous error