about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2020-02-03 16:08:45 -0800
committerEsteban Küber <esteban@kuber.com.ar>2020-02-03 18:02:44 -0800
commit109d5c189f4b5c3405a7d6cfb312e04d866c0c31 (patch)
tree8ce419b07ff2db20ec31baaf6ec6d9ed3df66199 /src
parent994e5e74653ae4bfe51094ce487a8b63f30ad3a4 (diff)
downloadrust-109d5c189f4b5c3405a7d6cfb312e04d866c0c31.tar.gz
rust-109d5c189f4b5c3405a7d6cfb312e04d866c0c31.zip
Tweak borrow error on `FnMut` when `Fn` is expected
Diffstat (limited to 'src')
-rw-r--r--src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs102
-rw-r--r--src/test/ui/borrowck/borrow-immutable-upvar-mutation.rs21
-rw-r--r--src/test/ui/borrowck/borrow-immutable-upvar-mutation.stderr123
-rw-r--r--src/test/ui/borrowck/borrow-raw-address-of-mutability.stderr32
-rw-r--r--src/test/ui/borrowck/mutability-errors.stderr128
-rw-r--r--src/test/ui/fn/fn-closure-mutable-capture.rs4
-rw-r--r--src/test/ui/fn/fn-closure-mutable-capture.stderr13
-rw-r--r--src/test/ui/issues/issue-21600.stderr33
-rw-r--r--src/test/ui/nll/closure-captures.stderr120
-rw-r--r--src/test/ui/unboxed-closures/unboxed-closures-mutate-upvar.stderr16
-rw-r--r--src/test/ui/unboxed-closures/unboxed-closures-mutated-upvar-from-fn-closure.stderr16
11 files changed, 370 insertions, 238 deletions
diff --git a/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs b/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs
index 563ff1112c3..4fc31b3a412 100644
--- a/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs
+++ b/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs
@@ -10,7 +10,7 @@ use rustc_span::Span;
 use crate::borrow_check::diagnostics::BorrowedContentSource;
 use crate::borrow_check::MirBorrowckCtxt;
 use crate::util::collect_writes::FindAssignments;
-use rustc_errors::Applicability;
+use rustc_errors::{Applicability, DiagnosticBuilder};
 
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
 pub(crate) enum AccessKind {
@@ -412,11 +412,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 projection: [ProjectionElem::Deref],
                 // FIXME document what is this 1 magic number about
             } if local == Local::new(1) && !self.upvars.is_empty() => {
-                err.span_label(span, format!("cannot {ACT}", ACT = act));
-                err.span_help(
-                    self.body.span,
-                    "consider changing this to accept closures that implement `FnMut`",
-                );
+                self.expected_fn_found_fn_mut_call(&mut err, span, act);
             }
 
             PlaceRef { local: _, projection: [.., ProjectionElem::Deref] } => {
@@ -448,6 +444,100 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
 
         err.buffer(&mut self.errors_buffer);
     }
+
+    /// Targetted error when encountering an `FnMut` closure where an `Fn` closure was expected.
+    fn expected_fn_found_fn_mut_call(&self, err: &mut DiagnosticBuilder<'_>, sp: Span, act: &str) {
+        err.span_label(sp, format!("cannot {ACT}", ACT = act));
+
+        let hir = self.infcx.tcx.hir();
+        let closure_id = hir.as_local_hir_id(self.mir_def_id).unwrap();
+        let fn_call_id = hir.get_parent_node(closure_id);
+        let node = hir.get(fn_call_id);
+        let item_id = hir.get_parent_item(fn_call_id);
+        let mut look_at_return = true;
+        // If we can detect the expression to be an `fn` call where the closure was an argument,
+        // we point at the `fn` definition argument...
+        match node {
+            hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Call(func, args), .. }) => {
+                let arg_pos = args
+                    .iter()
+                    .enumerate()
+                    .filter(|(_, arg)| arg.span == self.body.span)
+                    .map(|(pos, _)| pos)
+                    .next();
+                let def_id = hir.local_def_id(item_id);
+                let tables = self.infcx.tcx.typeck_tables_of(def_id);
+                if let Some(ty::FnDef(def_id, _)) =
+                    tables.node_type_opt(func.hir_id).as_ref().map(|ty| &ty.kind)
+                {
+                    let arg = match hir.get_if_local(*def_id) {
+                        Some(hir::Node::Item(hir::Item {
+                            ident,
+                            kind: hir::ItemKind::Fn(sig, ..),
+                            ..
+                        }))
+                        | Some(hir::Node::TraitItem(hir::TraitItem {
+                            ident,
+                            kind: hir::TraitItemKind::Method(sig, _),
+                            ..
+                        }))
+                        | Some(hir::Node::ImplItem(hir::ImplItem {
+                            ident,
+                            kind: hir::ImplItemKind::Method(sig, _),
+                            ..
+                        })) => Some(
+                            arg_pos
+                                .and_then(|pos| {
+                                    sig.decl.inputs.get(
+                                        pos + if sig.decl.implicit_self.has_implicit_self() {
+                                            1
+                                        } else {
+                                            0
+                                        },
+                                    )
+                                })
+                                .map(|arg| arg.span)
+                                .unwrap_or(ident.span),
+                        ),
+                        _ => None,
+                    };
+                    if let Some(span) = arg {
+                        err.span_label(span, "change this to accept `FnMut` instead of `Fn`");
+                        err.span_label(func.span, "expects `Fn` instead of `FnMut`");
+                        if self.infcx.tcx.sess.source_map().is_multiline(self.body.span) {
+                            err.span_label(self.body.span, "in this closure");
+                        }
+                        look_at_return = false;
+                    }
+                }
+            }
+            _ => {}
+        }
+        if look_at_return && hir.get_return_block(closure_id).is_some() {
+            // ...otherwise we are probably in the tail expression of the function, point at the
+            // return type.
+            match hir.get(hir.get_parent_item(fn_call_id)) {
+                hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(sig, ..), .. })
+                | hir::Node::TraitItem(hir::TraitItem {
+                    ident,
+                    kind: hir::TraitItemKind::Method(sig, _),
+                    ..
+                })
+                | hir::Node::ImplItem(hir::ImplItem {
+                    ident,
+                    kind: hir::ImplItemKind::Method(sig, _),
+                    ..
+                }) => {
+                    err.span_label(ident.span, "you might have to change this...");
+                    err.span_label(sig.decl.output.span(), "...to return `FnMut` instead of `Fn`");
+                    err.span_label(self.body.span, "in this closure");
+                }
+                parent => {
+                    err.note(&format!("parent {:?}", parent));
+                }
+            }
+        }
+    }
 }
 
 fn suggest_ampmut_self<'tcx>(
diff --git a/src/test/ui/borrowck/borrow-immutable-upvar-mutation.rs b/src/test/ui/borrowck/borrow-immutable-upvar-mutation.rs
index fed8bc95b6b..62e27bcf164 100644
--- a/src/test/ui/borrowck/borrow-immutable-upvar-mutation.rs
+++ b/src/test/ui/borrowck/borrow-immutable-upvar-mutation.rs
@@ -18,7 +18,10 @@ fn main() {
         let _g = to_fn(|| set(&mut y)); //~ ERROR cannot borrow
 
         let mut z = 0;
-        let _h = to_fn_mut(|| { set(&mut z); to_fn(|| z = 42); }); //~ ERROR cannot assign
+        let _h = to_fn_mut(|| {
+            set(&mut z);
+            to_fn(|| z = 42); //~ ERROR cannot assign
+        });
     }
 
     // By-value captures
@@ -33,3 +36,19 @@ fn main() {
         let _h = to_fn_mut(move || { set(&mut z); to_fn(move || z = 42); }); //~ ERROR cannot assign
     }
 }
+
+fn foo() -> Box<dyn Fn() -> usize> {
+    let mut x = 0;
+    Box::new(move || {
+        x += 1; //~ ERROR cannot assign
+        x
+    })
+}
+
+fn bar() -> impl Fn() -> usize {
+    let mut x = 0;
+    move || {
+        x += 1; //~ ERROR cannot assign
+        x
+    }
+}
diff --git a/src/test/ui/borrowck/borrow-immutable-upvar-mutation.stderr b/src/test/ui/borrowck/borrow-immutable-upvar-mutation.stderr
index 097e4c75065..a97d694685d 100644
--- a/src/test/ui/borrowck/borrow-immutable-upvar-mutation.stderr
+++ b/src/test/ui/borrowck/borrow-immutable-upvar-mutation.stderr
@@ -1,76 +1,101 @@
 error[E0594]: cannot assign to `x`, as it is a captured variable in a `Fn` closure
   --> $DIR/borrow-immutable-upvar-mutation.rs:15:27
    |
+LL | fn to_fn<A,F:Fn<A>>(f: F) -> F { f }
+   |                        - change this to accept `FnMut` instead of `Fn`
+...
 LL |         let _f = to_fn(|| x = 42);
-   |                           ^^^^^^ cannot assign
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/borrow-immutable-upvar-mutation.rs:15:24
-   |
-LL |         let _f = to_fn(|| x = 42);
-   |                        ^^^^^^^^^
+   |                  -----    ^^^^^^ cannot assign
+   |                  |
+   |                  expects `Fn` instead of `FnMut`
 
 error[E0596]: cannot borrow `y` as mutable, as it is a captured variable in a `Fn` closure
   --> $DIR/borrow-immutable-upvar-mutation.rs:18:31
    |
+LL | fn to_fn<A,F:Fn<A>>(f: F) -> F { f }
+   |                        - change this to accept `FnMut` instead of `Fn`
+...
 LL |         let _g = to_fn(|| set(&mut y));
-   |                               ^^^^^^ cannot borrow as mutable
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/borrow-immutable-upvar-mutation.rs:18:24
-   |
-LL |         let _g = to_fn(|| set(&mut y));
-   |                        ^^^^^^^^^^^^^^
+   |                  -----        ^^^^^^ cannot borrow as mutable
+   |                  |
+   |                  expects `Fn` instead of `FnMut`
 
 error[E0594]: cannot assign to `z`, as it is a captured variable in a `Fn` closure
-  --> $DIR/borrow-immutable-upvar-mutation.rs:21:55
-   |
-LL |         let _h = to_fn_mut(|| { set(&mut z); to_fn(|| z = 42); });
-   |                                                       ^^^^^^ cannot assign
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/borrow-immutable-upvar-mutation.rs:21:52
-   |
-LL |         let _h = to_fn_mut(|| { set(&mut z); to_fn(|| z = 42); });
-   |                                                    ^^^^^^^^^
+  --> $DIR/borrow-immutable-upvar-mutation.rs:23:22
+   |
+LL | fn to_fn<A,F:Fn<A>>(f: F) -> F { f }
+   |                        - change this to accept `FnMut` instead of `Fn`
+...
+LL |             to_fn(|| z = 42);
+   |             -----    ^^^^^^ cannot assign
+   |             |
+   |             expects `Fn` instead of `FnMut`
 
 error[E0594]: cannot assign to `x`, as it is a captured variable in a `Fn` closure
-  --> $DIR/borrow-immutable-upvar-mutation.rs:27:32
-   |
-LL |         let _f = to_fn(move || x = 42);
-   |                                ^^^^^^ cannot assign
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/borrow-immutable-upvar-mutation.rs:27:24
+  --> $DIR/borrow-immutable-upvar-mutation.rs:30:32
    |
+LL | fn to_fn<A,F:Fn<A>>(f: F) -> F { f }
+   |                        - change this to accept `FnMut` instead of `Fn`
+...
 LL |         let _f = to_fn(move || x = 42);
-   |                        ^^^^^^^^^^^^^^
+   |                  -----         ^^^^^^ cannot assign
+   |                  |
+   |                  expects `Fn` instead of `FnMut`
 
 error[E0596]: cannot borrow `y` as mutable, as it is a captured variable in a `Fn` closure
-  --> $DIR/borrow-immutable-upvar-mutation.rs:30:36
-   |
-LL |         let _g = to_fn(move || set(&mut y));
-   |                                    ^^^^^^ cannot borrow as mutable
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/borrow-immutable-upvar-mutation.rs:30:24
+  --> $DIR/borrow-immutable-upvar-mutation.rs:33:36
    |
+LL | fn to_fn<A,F:Fn<A>>(f: F) -> F { f }
+   |                        - change this to accept `FnMut` instead of `Fn`
+...
 LL |         let _g = to_fn(move || set(&mut y));
-   |                        ^^^^^^^^^^^^^^^^^^^
+   |                  -----             ^^^^^^ cannot borrow as mutable
+   |                  |
+   |                  expects `Fn` instead of `FnMut`
 
 error[E0594]: cannot assign to `z`, as it is a captured variable in a `Fn` closure
-  --> $DIR/borrow-immutable-upvar-mutation.rs:33:65
+  --> $DIR/borrow-immutable-upvar-mutation.rs:36:65
    |
+LL | fn to_fn<A,F:Fn<A>>(f: F) -> F { f }
+   |                        - change this to accept `FnMut` instead of `Fn`
+...
 LL |         let _h = to_fn_mut(move || { set(&mut z); to_fn(move || z = 42); });
-   |                                                                 ^^^^^^ cannot assign
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/borrow-immutable-upvar-mutation.rs:33:57
-   |
-LL |         let _h = to_fn_mut(move || { set(&mut z); to_fn(move || z = 42); });
-   |                                                         ^^^^^^^^^^^^^^
+   |                                                   -----         ^^^^^^ cannot assign
+   |                                                   |
+   |                                                   expects `Fn` instead of `FnMut`
+
+error[E0594]: cannot assign to `x`, as it is a captured variable in a `Fn` closure
+  --> $DIR/borrow-immutable-upvar-mutation.rs:43:9
+   |
+LL |   fn foo() -> Box<dyn Fn() -> usize> {
+   |      ---      ---------------------- ...to return `FnMut` instead of `Fn`
+   |      |
+   |      you might have to change this...
+LL |       let mut x = 0;
+LL |       Box::new(move || {
+   |  ______________-
+LL | |         x += 1;
+   | |         ^^^^^^ cannot assign
+LL | |         x
+LL | |     })
+   | |_____- in this closure
+
+error[E0594]: cannot assign to `x`, as it is a captured variable in a `Fn` closure
+  --> $DIR/borrow-immutable-upvar-mutation.rs:51:9
+   |
+LL |   fn bar() -> impl Fn() -> usize {
+   |      ---      ------------------ ...to return `FnMut` instead of `Fn`
+   |      |
+   |      you might have to change this...
+LL |       let mut x = 0;
+LL | /     move || {
+LL | |         x += 1;
+   | |         ^^^^^^ cannot assign
+LL | |         x
+LL | |     }
+   | |_____- in this closure
 
-error: aborting due to 6 previous errors
+error: aborting due to 8 previous errors
 
 Some errors have detailed explanations: E0594, E0596.
 For more information about an error, try `rustc --explain E0594`.
diff --git a/src/test/ui/borrowck/borrow-raw-address-of-mutability.stderr b/src/test/ui/borrowck/borrow-raw-address-of-mutability.stderr
index cf01c362d50..44dde0fd80b 100644
--- a/src/test/ui/borrowck/borrow-raw-address-of-mutability.stderr
+++ b/src/test/ui/borrowck/borrow-raw-address-of-mutability.stderr
@@ -27,32 +27,32 @@ LL |     f();
 error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
   --> $DIR/borrow-raw-address-of-mutability.rs:29:17
    |
-LL |         let y = &raw mut x;
-   |                 ^^^^^^^^^^ cannot borrow as mutable
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/borrow-raw-address-of-mutability.rs:28:21
-   |
+LL |   fn make_fn<F: Fn()>(f: F) -> F { f }
+   |                          - change this to accept `FnMut` instead of `Fn`
+...
 LL |       let f = make_fn(|| {
-   |  _____________________^
+   |  _____________-------_-
+   | |             |
+   | |             expects `Fn` instead of `FnMut`
 LL | |         let y = &raw mut x;
+   | |                 ^^^^^^^^^^ cannot borrow as mutable
 LL | |     });
-   | |_____^
+   | |_____- in this closure
 
 error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
   --> $DIR/borrow-raw-address-of-mutability.rs:37:17
    |
-LL |         let y = &raw mut x;
-   |                 ^^^^^^^^^^ cannot borrow as mutable
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/borrow-raw-address-of-mutability.rs:36:21
-   |
+LL |   fn make_fn<F: Fn()>(f: F) -> F { f }
+   |                          - change this to accept `FnMut` instead of `Fn`
+...
 LL |       let f = make_fn(move || {
-   |  _____________________^
+   |  _____________-------_-
+   | |             |
+   | |             expects `Fn` instead of `FnMut`
 LL | |         let y = &raw mut x;
+   | |                 ^^^^^^^^^^ cannot borrow as mutable
 LL | |     });
-   | |_____^
+   | |_____- in this closure
 
 error: aborting due to 5 previous errors
 
diff --git a/src/test/ui/borrowck/mutability-errors.stderr b/src/test/ui/borrowck/mutability-errors.stderr
index 72547a40352..5361ebe3916 100644
--- a/src/test/ui/borrowck/mutability-errors.stderr
+++ b/src/test/ui/borrowck/mutability-errors.stderr
@@ -119,146 +119,146 @@ LL |     &mut (*f()).0;
 error[E0594]: cannot assign to `x`, as it is a captured variable in a `Fn` closure
   --> $DIR/mutability-errors.rs:40:9
    |
-LL |         x = (1,);
-   |         ^^^^^^^^ cannot assign
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/mutability-errors.rs:39:12
-   |
+LL |   fn fn_ref<F: Fn()>(f: F) -> F { f }
+   |                         - change this to accept `FnMut` instead of `Fn`
+...
 LL |       fn_ref(|| {
-   |  ____________^
+   |  _____------_-
+   | |     |
+   | |     expects `Fn` instead of `FnMut`
 LL | |         x = (1,);
+   | |         ^^^^^^^^ cannot assign
 LL | |         x.0 = 1;
 LL | |         &mut x;
 LL | |         &mut x.0;
 LL | |     });
-   | |_____^
+   | |_____- in this closure
 
 error[E0594]: cannot assign to `x.0`, as `Fn` closures cannot mutate their captured variables
   --> $DIR/mutability-errors.rs:41:9
    |
-LL |         x.0 = 1;
-   |         ^^^^^^^ cannot assign
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/mutability-errors.rs:39:12
-   |
+LL |   fn fn_ref<F: Fn()>(f: F) -> F { f }
+   |                         - change this to accept `FnMut` instead of `Fn`
+...
 LL |       fn_ref(|| {
-   |  ____________^
+   |  _____------_-
+   | |     |
+   | |     expects `Fn` instead of `FnMut`
 LL | |         x = (1,);
 LL | |         x.0 = 1;
+   | |         ^^^^^^^ cannot assign
 LL | |         &mut x;
 LL | |         &mut x.0;
 LL | |     });
-   | |_____^
+   | |_____- in this closure
 
 error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
   --> $DIR/mutability-errors.rs:42:9
    |
-LL |         &mut x;
-   |         ^^^^^^ cannot borrow as mutable
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/mutability-errors.rs:39:12
-   |
+LL |   fn fn_ref<F: Fn()>(f: F) -> F { f }
+   |                         - change this to accept `FnMut` instead of `Fn`
+...
 LL |       fn_ref(|| {
-   |  ____________^
+   |  _____------_-
+   | |     |
+   | |     expects `Fn` instead of `FnMut`
 LL | |         x = (1,);
 LL | |         x.0 = 1;
 LL | |         &mut x;
+   | |         ^^^^^^ cannot borrow as mutable
 LL | |         &mut x.0;
 LL | |     });
-   | |_____^
+   | |_____- in this closure
 
 error[E0596]: cannot borrow `x.0` as mutable, as `Fn` closures cannot mutate their captured variables
   --> $DIR/mutability-errors.rs:43:9
    |
-LL |         &mut x.0;
-   |         ^^^^^^^^ cannot borrow as mutable
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/mutability-errors.rs:39:12
-   |
+LL |   fn fn_ref<F: Fn()>(f: F) -> F { f }
+   |                         - change this to accept `FnMut` instead of `Fn`
+...
 LL |       fn_ref(|| {
-   |  ____________^
+   |  _____------_-
+   | |     |
+   | |     expects `Fn` instead of `FnMut`
 LL | |         x = (1,);
 LL | |         x.0 = 1;
 LL | |         &mut x;
 LL | |         &mut x.0;
+   | |         ^^^^^^^^ cannot borrow as mutable
 LL | |     });
-   | |_____^
+   | |_____- in this closure
 
 error[E0594]: cannot assign to `x`, as it is a captured variable in a `Fn` closure
   --> $DIR/mutability-errors.rs:46:9
    |
-LL |         x = (1,);
-   |         ^^^^^^^^ cannot assign
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/mutability-errors.rs:45:12
-   |
+LL |   fn fn_ref<F: Fn()>(f: F) -> F { f }
+   |                         - change this to accept `FnMut` instead of `Fn`
+...
 LL |       fn_ref(move || {
-   |  ____________^
+   |  _____------_-
+   | |     |
+   | |     expects `Fn` instead of `FnMut`
 LL | |         x = (1,);
+   | |         ^^^^^^^^ cannot assign
 LL | |         x.0 = 1;
 LL | |         &mut x;
 LL | |         &mut x.0;
 LL | |     });
-   | |_____^
+   | |_____- in this closure
 
 error[E0594]: cannot assign to `x.0`, as `Fn` closures cannot mutate their captured variables
   --> $DIR/mutability-errors.rs:47:9
    |
-LL |         x.0 = 1;
-   |         ^^^^^^^ cannot assign
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/mutability-errors.rs:45:12
-   |
+LL |   fn fn_ref<F: Fn()>(f: F) -> F { f }
+   |                         - change this to accept `FnMut` instead of `Fn`
+...
 LL |       fn_ref(move || {
-   |  ____________^
+   |  _____------_-
+   | |     |
+   | |     expects `Fn` instead of `FnMut`
 LL | |         x = (1,);
 LL | |         x.0 = 1;
+   | |         ^^^^^^^ cannot assign
 LL | |         &mut x;
 LL | |         &mut x.0;
 LL | |     });
-   | |_____^
+   | |_____- in this closure
 
 error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
   --> $DIR/mutability-errors.rs:48:9
    |
-LL |         &mut x;
-   |         ^^^^^^ cannot borrow as mutable
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/mutability-errors.rs:45:12
-   |
+LL |   fn fn_ref<F: Fn()>(f: F) -> F { f }
+   |                         - change this to accept `FnMut` instead of `Fn`
+...
 LL |       fn_ref(move || {
-   |  ____________^
+   |  _____------_-
+   | |     |
+   | |     expects `Fn` instead of `FnMut`
 LL | |         x = (1,);
 LL | |         x.0 = 1;
 LL | |         &mut x;
+   | |         ^^^^^^ cannot borrow as mutable
 LL | |         &mut x.0;
 LL | |     });
-   | |_____^
+   | |_____- in this closure
 
 error[E0596]: cannot borrow `x.0` as mutable, as `Fn` closures cannot mutate their captured variables
   --> $DIR/mutability-errors.rs:49:9
    |
-LL |         &mut x.0;
-   |         ^^^^^^^^ cannot borrow as mutable
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/mutability-errors.rs:45:12
-   |
+LL |   fn fn_ref<F: Fn()>(f: F) -> F { f }
+   |                         - change this to accept `FnMut` instead of `Fn`
+...
 LL |       fn_ref(move || {
-   |  ____________^
+   |  _____------_-
+   | |     |
+   | |     expects `Fn` instead of `FnMut`
 LL | |         x = (1,);
 LL | |         x.0 = 1;
 LL | |         &mut x;
 LL | |         &mut x.0;
+   | |         ^^^^^^^^ cannot borrow as mutable
 LL | |     });
-   | |_____^
+   | |_____- in this closure
 
 error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
   --> $DIR/mutability-errors.rs:54:5
diff --git a/src/test/ui/fn/fn-closure-mutable-capture.rs b/src/test/ui/fn/fn-closure-mutable-capture.rs
index 81376af091b..0e427b9cf31 100644
--- a/src/test/ui/fn/fn-closure-mutable-capture.rs
+++ b/src/test/ui/fn/fn-closure-mutable-capture.rs
@@ -1,11 +1,11 @@
-pub fn bar<F: Fn()>(_f: F) {}
+pub fn bar<F: Fn()>(_f: F) {} //~ NOTE change this to accept `FnMut` instead of `Fn`
 
 pub fn foo() {
     let mut x = 0;
     bar(move || x = 1);
     //~^ ERROR cannot assign to `x`, as it is a captured variable in a `Fn` closure
     //~| NOTE cannot assign
-    //~| HELP consider changing this to accept closures that implement `FnMut`
+    //~| NOTE expects `Fn` instead of `FnMut`
 }
 
 fn main() {}
diff --git a/src/test/ui/fn/fn-closure-mutable-capture.stderr b/src/test/ui/fn/fn-closure-mutable-capture.stderr
index f7ab56da8de..d23c363ae15 100644
--- a/src/test/ui/fn/fn-closure-mutable-capture.stderr
+++ b/src/test/ui/fn/fn-closure-mutable-capture.stderr
@@ -1,14 +1,13 @@
 error[E0594]: cannot assign to `x`, as it is a captured variable in a `Fn` closure
   --> $DIR/fn-closure-mutable-capture.rs:5:17
    |
+LL | pub fn bar<F: Fn()>(_f: F) {}
+   |                         - change this to accept `FnMut` instead of `Fn`
+...
 LL |     bar(move || x = 1);
-   |                 ^^^^^ cannot assign
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/fn-closure-mutable-capture.rs:5:9
-   |
-LL |     bar(move || x = 1);
-   |         ^^^^^^^^^^^^^
+   |     ---         ^^^^^ cannot assign
+   |     |
+   |     expects `Fn` instead of `FnMut`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-21600.stderr b/src/test/ui/issues/issue-21600.stderr
index 9c534809dbe..84c7106e890 100644
--- a/src/test/ui/issues/issue-21600.stderr
+++ b/src/test/ui/issues/issue-21600.stderr
@@ -1,34 +1,33 @@
 error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
   --> $DIR/issue-21600.rs:14:20
    |
+LL | fn call_it<F>(f: F) where F: Fn() { f(); }
+   |                  - change this to accept `FnMut` instead of `Fn`
+...
 LL |         call_it(|| x.gen_mut());
-   |                    ^ cannot borrow as mutable
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/issue-21600.rs:14:17
-   |
-LL |         call_it(|| x.gen_mut());
-   |                 ^^^^^^^^^^^^^^
+   |         -------    ^ cannot borrow as mutable
+   |         |
+   |         expects `Fn` instead of `FnMut`
 
 error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
   --> $DIR/issue-21600.rs:14:17
    |
-LL |         call_it(|| x.gen_mut());
-   |                 ^^ - mutable borrow occurs due to use of `x` in closure
-   |                 |
-   |                 cannot borrow as mutable
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/issue-21600.rs:12:13
-   |
+LL |   fn call_it<F>(f: F) where F: Fn() { f(); }
+   |                    - change this to accept `FnMut` instead of `Fn`
+...
 LL |       call_it(|| {
-   |  _____________^
+   |  _____-------_-
+   | |     |
+   | |     expects `Fn` instead of `FnMut`
 LL | |         call_it(|| x.gen());
 LL | |         call_it(|| x.gen_mut());
+   | |                 ^^ - mutable borrow occurs due to use of `x` in closure
+   | |                 |
+   | |                 cannot borrow as mutable
 LL | |
 LL | |
 LL | |     });
-   | |_____^
+   | |_____- in this closure
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/nll/closure-captures.stderr b/src/test/ui/nll/closure-captures.stderr
index b8f5cc86500..dd5f32ef4f5 100644
--- a/src/test/ui/nll/closure-captures.stderr
+++ b/src/test/ui/nll/closure-captures.stderr
@@ -37,36 +37,36 @@ LL |         x = 1;
 error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
   --> $DIR/closure-captures.rs:27:9
    |
-LL |         ||
-   |         ^^ cannot borrow as mutable
-LL |          x = 1;}
-   |          - mutable borrow occurs due to use of `x` in closure
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/closure-captures.rs:26:12
-   |
+LL |   fn fn_ref<F: Fn()>(f: F) -> F { f }
+   |                         - change this to accept `FnMut` instead of `Fn`
+...
 LL |       fn_ref(|| {
-   |  ____________^
+   |  _____------_-
+   | |     |
+   | |     expects `Fn` instead of `FnMut`
 LL | |         ||
+   | |         ^^ cannot borrow as mutable
 LL | |          x = 1;}
-   | |________________^
+   | |__________-_____- in this closure
+   |            |
+   |            mutable borrow occurs due to use of `x` in closure
 
 error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
   --> $DIR/closure-captures.rs:31:9
    |
-LL |         ||
-   |         ^^ cannot borrow as mutable
-LL |     x = 1;});
-   |     - mutable borrow occurs due to use of `x` in closure
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/closure-captures.rs:30:12
-   |
+LL |   fn fn_ref<F: Fn()>(f: F) -> F { f }
+   |                         - change this to accept `FnMut` instead of `Fn`
+...
 LL |       fn_ref(move || {
-   |  ____________^
+   |  _____------_-
+   | |     |
+   | |     expects `Fn` instead of `FnMut`
 LL | |         ||
+   | |         ^^ cannot borrow as mutable
 LL | |     x = 1;});
-   | |___________^
+   | |_____-_____- in this closure
+   |       |
+   |       mutable borrow occurs due to use of `x` in closure
 
 error[E0594]: cannot assign to `x`, as it is not declared as mutable
   --> $DIR/closure-captures.rs:39:10
@@ -80,19 +80,19 @@ LL |          x = 1;}
 error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
   --> $DIR/closure-captures.rs:38:9
    |
-LL |         ||
-   |         ^^ cannot borrow as mutable
-LL |          x = 1;}
-   |          - mutable borrow occurs due to use of `x` in closure
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/closure-captures.rs:37:12
-   |
+LL |   fn fn_ref<F: Fn()>(f: F) -> F { f }
+   |                         - change this to accept `FnMut` instead of `Fn`
+...
 LL |       fn_ref(|| {
-   |  ____________^
+   |  _____------_-
+   | |     |
+   | |     expects `Fn` instead of `FnMut`
 LL | |         ||
+   | |         ^^ cannot borrow as mutable
 LL | |          x = 1;}
-   | |________________^
+   | |__________-_____- in this closure
+   |            |
+   |            mutable borrow occurs due to use of `x` in closure
 
 error[E0594]: cannot assign to `x`, as it is not declared as mutable
   --> $DIR/closure-captures.rs:43:5
@@ -106,53 +106,53 @@ LL |     x = 1;});
 error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
   --> $DIR/closure-captures.rs:42:9
    |
-LL |         ||
-   |         ^^ cannot borrow as mutable
-LL |     x = 1;});
-   |     - mutable borrow occurs due to use of `x` in closure
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/closure-captures.rs:41:12
-   |
+LL |   fn fn_ref<F: Fn()>(f: F) -> F { f }
+   |                         - change this to accept `FnMut` instead of `Fn`
+...
 LL |       fn_ref(move || {
-   |  ____________^
+   |  _____------_-
+   | |     |
+   | |     expects `Fn` instead of `FnMut`
 LL | |         ||
+   | |         ^^ cannot borrow as mutable
 LL | |     x = 1;});
-   | |___________^
+   | |_____-_____- in this closure
+   |       |
+   |       mutable borrow occurs due to use of `x` in closure
 
 error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
   --> $DIR/closure-captures.rs:48:9
    |
-LL |         ||
-   |         ^^ cannot borrow as mutable
-LL |         *x = 1;});
-   |          - mutable borrow occurs due to use of `x` in closure
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/closure-captures.rs:47:12
-   |
+LL |   fn fn_ref<F: Fn()>(f: F) -> F { f }
+   |                         - change this to accept `FnMut` instead of `Fn`
+...
 LL |       fn_ref(|| {
-   |  ____________^
+   |  _____------_-
+   | |     |
+   | |     expects `Fn` instead of `FnMut`
 LL | |         ||
+   | |         ^^ cannot borrow as mutable
 LL | |         *x = 1;});
-   | |________________^
+   | |__________-_____- in this closure
+   |            |
+   |            mutable borrow occurs due to use of `x` in closure
 
 error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
   --> $DIR/closure-captures.rs:51:9
    |
-LL |         ||
-   |         ^^ cannot borrow as mutable
-LL |         *x = 1;});
-   |          - mutable borrow occurs due to use of `x` in closure
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/closure-captures.rs:50:12
-   |
+LL |   fn fn_ref<F: Fn()>(f: F) -> F { f }
+   |                         - change this to accept `FnMut` instead of `Fn`
+...
 LL |       fn_ref(move || {
-   |  ____________^
+   |  _____------_-
+   | |     |
+   | |     expects `Fn` instead of `FnMut`
 LL | |         ||
+   | |         ^^ cannot borrow as mutable
 LL | |         *x = 1;});
-   | |________________^
+   | |__________-_____- in this closure
+   |            |
+   |            mutable borrow occurs due to use of `x` in closure
 
 error: aborting due to 12 previous errors
 
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-mutate-upvar.stderr b/src/test/ui/unboxed-closures/unboxed-closures-mutate-upvar.stderr
index 6bba38510b6..48ec620d92e 100644
--- a/src/test/ui/unboxed-closures/unboxed-closures-mutate-upvar.stderr
+++ b/src/test/ui/unboxed-closures/unboxed-closures-mutate-upvar.stderr
@@ -28,17 +28,17 @@ LL |         n += 1;
 error[E0594]: cannot assign to `n`, as it is a captured variable in a `Fn` closure
   --> $DIR/unboxed-closures-mutate-upvar.rs:53:9
    |
-LL |         n += 1;
-   |         ^^^^^^ cannot assign
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/unboxed-closures-mutate-upvar.rs:52:23
-   |
+LL |   fn to_fn<A,F:Fn<A>>(f: F) -> F { f }
+   |                          - change this to accept `FnMut` instead of `Fn`
+...
 LL |       let mut f = to_fn(move || {
-   |  _______________________^
+   |  _________________-----_-
+   | |                 |
+   | |                 expects `Fn` instead of `FnMut`
 LL | |         n += 1;
+   | |         ^^^^^^ cannot assign
 LL | |     });
-   | |_____^
+   | |_____- in this closure
 
 error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-mutated-upvar-from-fn-closure.stderr b/src/test/ui/unboxed-closures/unboxed-closures-mutated-upvar-from-fn-closure.stderr
index a38c612e1de..80e84fb7cad 100644
--- a/src/test/ui/unboxed-closures/unboxed-closures-mutated-upvar-from-fn-closure.stderr
+++ b/src/test/ui/unboxed-closures/unboxed-closures-mutated-upvar-from-fn-closure.stderr
@@ -1,18 +1,18 @@
 error[E0594]: cannot assign to `counter`, as it is a captured variable in a `Fn` closure
   --> $DIR/unboxed-closures-mutated-upvar-from-fn-closure.rs:11:9
    |
-LL |         counter += 1;
-   |         ^^^^^^^^^^^^ cannot assign
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/unboxed-closures-mutated-upvar-from-fn-closure.rs:10:10
-   |
+LL |   fn call<F>(f: F) where F : Fn() {
+   |                 - change this to accept `FnMut` instead of `Fn`
+...
 LL |       call(|| {
-   |  __________^
+   |  _____----_-
+   | |     |
+   | |     expects `Fn` instead of `FnMut`
 LL | |         counter += 1;
+   | |         ^^^^^^^^^^^^ cannot assign
 LL | |
 LL | |     });
-   | |_____^
+   | |_____- in this closure
 
 error: aborting due to previous error