about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-06-19 21:31:36 +0000
committerbors <bors@rust-lang.org>2018-06-19 21:31:36 +0000
commitf28c7aef7fbff1909c2d0257186cd7a5f0c6aa4b (patch)
tree3ae6e0cd22a8003311fed08d61a54ef369b2ef9a /src
parentd692ab406ebab720f99f950ac3e9aba1e01296af (diff)
parent46846496ed6194d4cacf1fbd52b9c76b5c83b96c (diff)
downloadrust-f28c7aef7fbff1909c2d0257186cd7a5f0c6aa4b.tar.gz
rust-f28c7aef7fbff1909c2d0257186cd7a5f0c6aa4b.zip
Auto merge of #51275 - pnkfelix:nll-diagnostics-revise-check-access-permissions, r=nikomatsakis
NLL diagnostics: revise `fn check_access_permissions`

NLL: revise `fn check_access_permissions` so that its (still branchy) shares more code paths between the different cases, and also provide more diagnostics in more cases (though the added diagnostics still do not always meet the quality bar established by AST-borrowck)

----

Transcribing "checklist" suggested by Niko, except I am rendering it as a table to make it clear that I do not regard every item in the list to be a "must have" for landing this PR.

goal | does this PR do it?
-----|------------------------------
no suggestions for `ref mut` |  yes
suggestions for direct local assignment (`{ let x = 3; x = 4; }`) | yes (see commits at end)
suggestions for direct field assignment (`{ let x = (3, 4); x.0 = 5; }` | yes (see commits at end)
suggestions for upvars (`let x = 3; let c = \|\| { &mut x; }`) | yes

Note that I added support for a couple of rows via changes that are not strictly part of `fn check_access_permissions`. If desired I can remove those commits from this PR and leave them for a later PR.

Fix #51031
Fix #51032
(bug #51191 needs a little more investigation before closing.)
Fix #51578
Diffstat (limited to 'src')
-rw-r--r--src/librustc/hir/map/mod.rs42
-rw-r--r--src/librustc/mir/mod.rs100
-rw-r--r--src/librustc/ty/binding.rs2
-rw-r--r--src/librustc_mir/borrow_check/error_reporting.rs20
-rw-r--r--src/librustc_mir/borrow_check/mod.rs306
-rw-r--r--src/librustc_mir/build/block.rs2
-rw-r--r--src/librustc_mir/build/expr/into.rs2
-rw-r--r--src/librustc_mir/build/matches/mod.rs33
-rw-r--r--src/librustc_mir/build/mod.rs48
-rw-r--r--src/librustc_mir/shim.rs2
-rw-r--r--src/librustc_mir/transform/generator.rs6
-rw-r--r--src/test/compile-fail/assign-imm-local-twice.rs1
-rw-r--r--src/test/compile-fail/borrowck/borrowck-issue-14498.rs2
-rw-r--r--src/test/compile-fail/borrowck/borrowck-overloaded-index-ref-index.rs2
-rw-r--r--src/test/compile-fail/issue-45199.rs4
-rw-r--r--src/test/compile-fail/liveness-assign-imm-local-in-loop.rs1
-rw-r--r--src/test/compile-fail/liveness-assign-imm-local-in-op-eq.rs1
-rw-r--r--src/test/compile-fail/liveness-assign-imm-local-with-drop.rs1
-rw-r--r--src/test/compile-fail/liveness-assign-imm-local-with-init.rs1
-rw-r--r--src/test/compile-fail/unboxed-closures-mutated-upvar-from-fn-closure.rs2
-rw-r--r--src/test/ui/asm-out-assign-imm.nll.stderr14
-rw-r--r--src/test/ui/augmented-assignments.nll.stderr3
-rw-r--r--src/test/ui/borrowck/immutable-arg.stderr2
-rw-r--r--src/test/ui/borrowck/issue-45983.nll.stderr6
-rw-r--r--src/test/ui/borrowck/mut-borrow-of-mut-ref.nll.stderr2
-rw-r--r--src/test/ui/codemap_tests/huge_multispan_highlight.nll.stderr3
-rw-r--r--src/test/ui/command-line-diagnostics.nll.stderr4
-rw-r--r--src/test/ui/did_you_mean/issue-31424.nll.stderr2
-rw-r--r--src/test/ui/did_you_mean/issue-34126.nll.stderr5
-rw-r--r--src/test/ui/did_you_mean/issue-34337.nll.stderr5
-rw-r--r--src/test/ui/did_you_mean/issue-35937.nll.stderr13
-rw-r--r--src/test/ui/did_you_mean/issue-37139.nll.stderr5
-rw-r--r--src/test/ui/did_you_mean/issue-38147-1.nll.stderr6
-rw-r--r--src/test/ui/did_you_mean/issue-38147-4.nll.stderr6
-rw-r--r--src/test/ui/did_you_mean/issue-39544.nll.stderr70
-rw-r--r--src/test/ui/did_you_mean/issue-40823.nll.stderr4
-rw-r--r--src/test/ui/error-codes/E0389.nll.stderr2
-rw-r--r--src/test/ui/issue-36400.nll.stderr4
-rw-r--r--src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.nll.stderr2
-rw-r--r--src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr2
-rw-r--r--src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr2
-rw-r--r--src/test/ui/lifetime-errors/liveness-assign-imm-local-notes.stderr12
-rw-r--r--src/test/ui/macros/span-covering-argument-1.nll.stderr2
-rw-r--r--src/test/ui/nll/issue-47388.stderr2
-rw-r--r--src/test/ui/reassign-ref-mut.rs26
-rw-r--r--src/test/ui/reassign-ref-mut.stderr20
-rw-r--r--src/test/ui/rfc-2005-default-binding-mode/enum.nll.stderr18
-rw-r--r--src/test/ui/rfc-2005-default-binding-mode/explicit-mut.nll.stderr18
-rw-r--r--src/test/ui/span/borrowck-borrow-overloaded-auto-deref-mut.nll.stderr24
-rw-r--r--src/test/ui/span/borrowck-borrow-overloaded-deref-mut.nll.stderr12
-rw-r--r--src/test/ui/span/borrowck-call-is-borrow-issue-12224.nll.stderr10
-rw-r--r--src/test/ui/span/borrowck-call-method-from-mut-aliasable.nll.stderr5
-rw-r--r--src/test/ui/span/borrowck-fn-in-const-b.nll.stderr4
-rw-r--r--src/test/ui/span/borrowck-object-mutability.nll.stderr10
-rw-r--r--src/test/ui/span/mut-arg-hint.nll.stderr12
-rw-r--r--src/test/ui/suggestions/closure-immutable-outer-variable.nll.stderr2
-rw-r--r--src/test/ui/suggestions/fn-closure-mutable-capture.nll.stderr4
-rw-r--r--src/test/ui/trivial-bounds-inconsistent-copy-reborrow.nll.stderr12
58 files changed, 670 insertions, 263 deletions
diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs
index 9df55e52061..d2e04ef31c8 100644
--- a/src/librustc/hir/map/mod.rs
+++ b/src/librustc/hir/map/mod.rs
@@ -170,6 +170,40 @@ impl<'hir> MapEntry<'hir> {
         })
     }
 
+    fn fn_decl(&self) -> Option<&FnDecl> {
+        match self {
+            EntryItem(_, _, ref item) => {
+                match item.node {
+                    ItemFn(ref fn_decl, _, _, _, _, _) => Some(&fn_decl),
+                    _ => None,
+                }
+            }
+
+            EntryTraitItem(_, _, ref item) => {
+                match item.node {
+                    TraitItemKind::Method(ref method_sig, _) => Some(&method_sig.decl),
+                    _ => None
+                }
+            }
+
+            EntryImplItem(_, _, ref item) => {
+                match item.node {
+                    ImplItemKind::Method(ref method_sig, _) => Some(&method_sig.decl),
+                    _ => None,
+                }
+            }
+
+            EntryExpr(_, _, ref expr) => {
+                match expr.node {
+                    ExprClosure(_, ref fn_decl, ..) => Some(&fn_decl),
+                    _ => None,
+                }
+            }
+
+            _ => None
+        }
+    }
+
     fn associated_body(self) -> Option<BodyId> {
         match self {
             EntryItem(_, _, item) => {
@@ -502,6 +536,14 @@ impl<'hir> Map<'hir> {
         self.forest.krate.body(id)
     }
 
+    pub fn fn_decl(&self, node_id: ast::NodeId) -> Option<FnDecl> {
+        if let Some(entry) = self.find_entry(node_id) {
+            entry.fn_decl().map(|fd| fd.clone())
+        } else {
+            bug!("no entry for node_id `{}`", node_id)
+        }
+    }
+
     /// Returns the `NodeId` that corresponds to the definition of
     /// which this is the body of, i.e. a `fn`, `const` or `static`
     /// item (possibly associated), a closure, or a `hir::AnonConst`.
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index b2ff36cb87c..20a0df2a171 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -228,7 +228,7 @@ impl<'tcx> Mir<'tcx> {
     pub fn temps_iter<'a>(&'a self) -> impl Iterator<Item=Local> + 'a {
         (self.arg_count+1..self.local_decls.len()).filter_map(move |index| {
             let local = Local::new(index);
-            if self.local_decls[local].is_user_variable {
+            if self.local_decls[local].is_user_variable.is_some() {
                 None
             } else {
                 Some(local)
@@ -241,7 +241,7 @@ impl<'tcx> Mir<'tcx> {
     pub fn vars_iter<'a>(&'a self) -> impl Iterator<Item=Local> + 'a {
         (self.arg_count+1..self.local_decls.len()).filter_map(move |index| {
             let local = Local::new(index);
-            if self.local_decls[local].is_user_variable {
+            if self.local_decls[local].is_user_variable.is_some() {
                 Some(local)
             } else {
                 None
@@ -255,7 +255,7 @@ impl<'tcx> Mir<'tcx> {
         (1..self.local_decls.len()).filter_map(move |index| {
             let local = Local::new(index);
             let decl = &self.local_decls[local];
-            if (decl.is_user_variable || index < self.arg_count + 1)
+            if (decl.is_user_variable.is_some() || index < self.arg_count + 1)
                && decl.mutability == Mutability::Mut
             {
                 Some(local)
@@ -351,7 +351,7 @@ impl<'tcx> IndexMut<BasicBlock> for Mir<'tcx> {
     }
 }
 
-#[derive(Clone, Debug)]
+#[derive(Copy, Clone, Debug)]
 pub enum ClearCrossCrate<T> {
     Clear,
     Set(T)
@@ -382,6 +382,16 @@ pub enum Mutability {
     Not,
 }
 
+impl From<Mutability> for hir::Mutability {
+    fn from(m: Mutability) -> Self {
+        match m {
+            Mutability::Mut => hir::MutMutable,
+            Mutability::Not => hir::MutImmutable,
+        }
+    }
+}
+
+
 #[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
 pub enum BorrowKind {
     /// Data must be immutable and is aliasable.
@@ -463,6 +473,33 @@ pub enum LocalKind {
     ReturnPointer,
 }
 
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
+pub struct VarBindingForm {
+    /// Is variable bound via `x`, `mut x`, `ref x`, or `ref mut x`?
+    pub binding_mode: ty::BindingMode,
+    /// If an explicit type was provided for this variable binding,
+    /// this holds the source Span of that type.
+    ///
+    /// NOTE: If you want to change this to a `HirId`, be wary that
+    /// doing so breaks incremental compilation (as of this writing),
+    /// while a `Span` does not cause our tests to fail.
+    pub opt_ty_info: Option<Span>,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
+pub enum BindingForm {
+    /// This is a binding for a non-`self` binding, or a `self` that has an explicit type.
+    Var(VarBindingForm),
+    /// Binding for a `self`/`&self`/`&mut self` binding where the type is implicit.
+    ImplicitSelf,
+}
+
+CloneTypeFoldableAndLiftImpls! { BindingForm, }
+
+impl_stable_hash_for!(struct self::VarBindingForm { binding_mode, opt_ty_info });
+
+impl_stable_hash_for!(enum self::BindingForm { Var(binding), ImplicitSelf, });
+
 /// A MIR local.
 ///
 /// This can be a binding declared by the user, a temporary inserted by the compiler, a function
@@ -474,8 +511,14 @@ pub struct LocalDecl<'tcx> {
     /// Temporaries and the return place are always mutable.
     pub mutability: Mutability,
 
-    /// True if this corresponds to a user-declared local variable.
-    pub is_user_variable: bool,
+    /// Some(binding_mode) if this corresponds to a user-declared local variable.
+    ///
+    /// This is solely used for local diagnostics when generating
+    /// warnings/errors when compiling the current crate, and
+    /// therefore it need not be visible across crates. pnkfelix
+    /// currently hypothesized we *need* to wrap this in a
+    /// `ClearCrossCrate` as long as it carries as `HirId`.
+    pub is_user_variable: Option<ClearCrossCrate<BindingForm>>,
 
     /// True if this is an internal local
     ///
@@ -592,6 +635,45 @@ pub struct LocalDecl<'tcx> {
 }
 
 impl<'tcx> LocalDecl<'tcx> {
+    /// Returns true only if local is a binding that can itself be
+    /// made mutable via the addition of the `mut` keyword, namely
+    /// something like the occurrences of `x` in:
+    /// - `fn foo(x: Type) { ... }`,
+    /// - `let x = ...`,
+    /// - or `match ... { C(x) => ... }`
+    pub fn can_be_made_mutable(&self) -> bool
+    {
+        match self.is_user_variable {
+            Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
+                binding_mode: ty::BindingMode::BindByValue(_),
+                opt_ty_info: _,
+            }))) => true,
+
+            // FIXME: might be able to thread the distinction between
+            // `self`/`mut self`/`&self`/`&mut self` into the
+            // `BindingForm::ImplicitSelf` variant, (and then return
+            // true here for solely the first case).
+            _ => false,
+        }
+    }
+
+    /// Returns true if local is definitely not a `ref ident` or
+    /// `ref mut ident` binding. (Such bindings cannot be made into
+    /// mutable bindings, but the inverse does not necessarily hold).
+    pub fn is_nonref_binding(&self) -> bool
+    {
+        match self.is_user_variable {
+            Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
+                binding_mode: ty::BindingMode::BindByValue(_),
+                opt_ty_info: _,
+            }))) => true,
+
+            Some(ClearCrossCrate::Set(BindingForm::ImplicitSelf)) => true,
+
+            _ => false,
+        }
+    }
+
     /// Create a new `LocalDecl` for a temporary.
     #[inline]
     pub fn new_temp(ty: Ty<'tcx>, span: Span) -> Self {
@@ -605,7 +687,7 @@ impl<'tcx> LocalDecl<'tcx> {
             },
             visibility_scope: OUTERMOST_SOURCE_SCOPE,
             internal: false,
-            is_user_variable: false
+            is_user_variable: None,
         }
     }
 
@@ -622,7 +704,7 @@ impl<'tcx> LocalDecl<'tcx> {
             },
             visibility_scope: OUTERMOST_SOURCE_SCOPE,
             internal: true,
-            is_user_variable: false
+            is_user_variable: None,
         }
     }
 
@@ -641,7 +723,7 @@ impl<'tcx> LocalDecl<'tcx> {
             visibility_scope: OUTERMOST_SOURCE_SCOPE,
             internal: false,
             name: None,     // FIXME maybe we do want some name here?
-            is_user_variable: false
+            is_user_variable: None,
         }
     }
 }
diff --git a/src/librustc/ty/binding.rs b/src/librustc/ty/binding.rs
index 3db61b76cc5..971b3c3d14a 100644
--- a/src/librustc/ty/binding.rs
+++ b/src/librustc/ty/binding.rs
@@ -18,6 +18,8 @@ pub enum BindingMode {
     BindByValue(Mutability),
 }
 
+CloneTypeFoldableAndLiftImpls! { BindingMode, }
+
 impl BindingMode {
     pub fn convert(ba: BindingAnnotation) -> BindingMode {
         match ba {
diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs
index 9061af1b68c..38d1ac2cb4a 100644
--- a/src/librustc_mir/borrow_check/error_reporting.rs
+++ b/src/librustc_mir/borrow_check/error_reporting.rs
@@ -593,11 +593,18 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         err.emit();
     }
 
+    /// Reports an illegal reassignment; for example, an assignment to
+    /// (part of) a non-`mut` local that occurs potentially after that
+    /// local has already been initialized. `place` is the path being
+    /// assigned; `err_place` is a place providing a reason why
+    /// `place` is not mutable (e.g. the non-`mut` local `x` in an
+    /// assignment to `x.f`).
     pub(super) fn report_illegal_reassignment(
         &mut self,
         _context: Context,
         (place, span): (&Place<'tcx>, Span),
         assigned_span: Span,
+        err_place: &Place<'tcx>,
     ) {
         let is_arg = if let Place::Local(local) = place {
             if let LocalKind::Arg = self.mir.local_kind(*local) {
@@ -621,9 +628,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             "cannot assign twice to immutable variable"
         };
         if span != assigned_span {
-            if is_arg {
-                err.span_label(assigned_span, "argument not declared as `mut`");
-            } else {
+            if !is_arg {
                 let value_msg = match self.describe_place(place) {
                     Some(name) => format!("`{}`", name),
                     None => "value".to_owned(),
@@ -631,6 +636,15 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                 err.span_label(assigned_span, format!("first assignment to {}", value_msg));
             }
         }
+        if let Place::Local(local) = err_place {
+            let local_decl = &self.mir.local_decls[*local];
+            if let Some(name) = local_decl.name {
+                if local_decl.can_be_made_mutable() {
+                    err.span_label(local_decl.source_info.span,
+                                   format!("consider changing this to `mut {}`", name));
+                }
+            }
+        }
         err.span_label(span, msg);
         err.emit();
     }
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index c43ea0360ee..122b2df4766 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -18,7 +18,7 @@ use rustc::infer::InferCtxt;
 use rustc::ty::{self, ParamEnv, TyCtxt};
 use rustc::ty::query::Providers;
 use rustc::lint::builtin::UNUSED_MUT;
-use rustc::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind};
+use rustc::mir::{self, AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind};
 use rustc::mir::{ClearCrossCrate, Local, Location, Place, Mir, Mutability, Operand};
 use rustc::mir::{Projection, ProjectionElem, Rvalue, Field, Statement, StatementKind};
 use rustc::mir::{Terminator, TerminatorKind};
@@ -278,7 +278,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
     // to the set.
     let temporary_used_locals: FxHashSet<Local> =
         mbcx.used_mut.iter()
-            .filter(|&local| !mbcx.mir.local_decls[*local].is_user_variable)
+            .filter(|&local| !mbcx.mir.local_decls[*local].is_user_variable.is_some())
             .cloned()
             .collect();
 
@@ -1398,9 +1398,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
     ) {
         debug!("check_if_reassignment_to_immutable_state({:?})", place);
         // determine if this path has a non-mut owner (and thus needs checking).
-        if let Ok(..) = self.is_mutable(place, LocalMutationIsAllowed::No) {
-            return;
-        }
+        let err_place = match self.is_mutable(place, LocalMutationIsAllowed::No) {
+            Ok(..) => return,
+            Err(place) => place,
+        };
         debug!(
             "check_if_reassignment_to_immutable_state({:?}) - is an imm local",
             place
@@ -1410,7 +1411,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             let init = self.move_data.inits[i];
             let init_place = &self.move_data.move_paths[init.path].place;
             if places_conflict(self.tcx, self.mir, &init_place, place, Deep) {
-                self.report_illegal_reassignment(context, (place, span), init.span);
+                self.report_illegal_reassignment(context, (place, span), init.span, err_place);
                 break;
             }
         }
@@ -1658,36 +1659,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         }
     }
 
-    fn specialized_description(&self, place:&Place<'tcx>) -> Option<String>{
-        if let Some(_name) = self.describe_place(place) {
-            Some(format!("data in a `&` reference"))
-        } else {
-            None
-        }
-    }
-
-    fn get_default_err_msg(&self, place:&Place<'tcx>) -> String{
-        match self.describe_place(place) {
-            Some(name) => format!("immutable item `{}`", name),
-            None => "immutable item".to_owned(),
-        }
-    }
-
-    fn get_secondary_err_msg(&self, place:&Place<'tcx>) -> String{
-        match self.specialized_description(place) {
-            Some(_) => format!("data in a `&` reference"),
-            None => self.get_default_err_msg(place)
-        }
-    }
-
-    fn get_primary_err_msg(&self, place:&Place<'tcx>) -> String{
-        if let Some(name) = self.describe_place(place) {
-            format!("`{}` is a `&` reference, so the data it refers to cannot be written", name)
-        } else {
-            format!("cannot assign through `&`-reference")
-        }
-    }
-
     /// Check the permissions for the given place and read or write kind
     ///
     /// Returns true if an error is reported, false otherwise.
@@ -1702,7 +1673,15 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             "check_access_permissions({:?}, {:?}, {:?})",
             place, kind, is_local_mutation_allowed
         );
-        let mut error_reported = false;
+
+        #[derive(Copy, Clone, Debug)]
+        enum AccessKind {
+            MutableBorrow,
+            Mutate,
+        }
+        let error_access;
+        let the_place_err;
+
         match kind {
             Reservation(WriteKind::MutableBorrow(borrow_kind @ BorrowKind::Unique))
             | Reservation(WriteKind::MutableBorrow(borrow_kind @ BorrowKind::Mut { .. }))
@@ -1715,91 +1694,29 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                     BorrowKind::Shared => unreachable!(),
                 };
                 match self.is_mutable(place, is_local_mutation_allowed) {
-                    Ok(root_place) => self.add_used_mut(root_place, flow_state),
+                    Ok(root_place) => {
+                        self.add_used_mut(root_place, flow_state);
+                        return false;
+                    }
                     Err(place_err) => {
-                        error_reported = true;
-                        let item_msg = self.get_default_err_msg(place);
-                        let mut err = self.tcx
-                            .cannot_borrow_path_as_mutable(span, &item_msg, Origin::Mir);
-                        err.span_label(span, "cannot borrow as mutable");
-
-                        if place != place_err {
-                            if let Some(name) = self.describe_place(place_err) {
-                                err.note(&format!("the value which is causing this path not to be \
-                                    mutable is...: `{}`", name));
-                            }
-                        }
-
-                        err.emit();
+                        error_access = AccessKind::MutableBorrow;
+                        the_place_err = place_err;
                     }
                 }
             }
             Reservation(WriteKind::Mutate) | Write(WriteKind::Mutate) => {
                 match self.is_mutable(place, is_local_mutation_allowed) {
-                    Ok(root_place) => self.add_used_mut(root_place, flow_state),
+                    Ok(root_place) => {
+                        self.add_used_mut(root_place, flow_state);
+                        return false;
+                    }
                     Err(place_err) => {
-                        error_reported = true;
-
-                        let err_info = if let Place::Projection(
-                            box Projection {
-                                base: Place::Local(local),
-                                elem: ProjectionElem::Deref
-                            }
-                        ) = *place_err {
-                            let locations = self.mir.find_assignments(local);
-                            if locations.len() > 0 {
-                                let item_msg = if error_reported {
-                                    self.get_secondary_err_msg(&Place::Local(local))
-                                } else {
-                                    self.get_default_err_msg(place)
-                                };
-                                let sp = self.mir.source_info(locations[0]).span;
-                                let mut to_suggest_span = String::new();
-                                if let Ok(src) =
-                                    self.tcx.sess.codemap().span_to_snippet(sp) {
-                                        to_suggest_span = src[1..].to_string();
-                                };
-                                Some((sp,
-                                      "consider changing this to be a \
-                                      mutable reference",
-                                      to_suggest_span,
-                                      item_msg,
-                                      self.get_primary_err_msg(&Place::Local(local))))
-                            } else {
-                                None
-                            }
-                        } else {
-                            None
-                        };
-
-                        if let Some((err_help_span,
-                                     err_help_stmt,
-                                     to_suggest_span,
-                                     item_msg,
-                                     sec_span)) = err_info {
-                            let mut err = self.tcx.cannot_assign(span, &item_msg, Origin::Mir);
-                            err.span_suggestion(err_help_span,
-                                                err_help_stmt,
-                                                format!("&mut {}", to_suggest_span));
-                            if place != place_err {
-                                err.span_label(span, sec_span);
-                            }
-                            err.emit()
-                        } else {
-                            let item_msg = self.get_default_err_msg(place);
-                            let mut err = self.tcx.cannot_assign(span, &item_msg, Origin::Mir);
-                            err.span_label(span, "cannot mutate");
-                            if place != place_err {
-                                if let Some(name) = self.describe_place(place_err) {
-                                    err.note(&format!("the value which is causing this path not \
-                                                       to be mutable is...: `{}`", name));
-                                }
-                            }
-                            err.emit();
-                        }
+                        error_access = AccessKind::Mutate;
+                        the_place_err = place_err;
                     }
                 }
             }
+
             Reservation(WriteKind::Move)
             | Write(WriteKind::Move)
             | Reservation(WriteKind::StorageDeadOrDrop)
@@ -1815,15 +1732,174 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                         ),
                     );
                 }
+                return false;
+            }
+            Activation(..) => {
+                // permission checks are done at Reservation point.
+                return false;
             }
-            Activation(..) => {} // permission checks are done at Reservation point.
             Read(ReadKind::Borrow(BorrowKind::Unique))
             | Read(ReadKind::Borrow(BorrowKind::Mut { .. }))
             | Read(ReadKind::Borrow(BorrowKind::Shared))
-            | Read(ReadKind::Copy) => {} // Access authorized
+            | Read(ReadKind::Copy) => {
+                // Access authorized
+                return false;
+            }
         }
 
-        error_reported
+        // at this point, we have set up the error reporting state.
+
+        let mut err;
+        let item_msg = match self.describe_place(place) {
+            Some(name) => format!("immutable item `{}`", name),
+            None => "immutable item".to_owned(),
+        };
+
+        // `act` and `acted_on` are strings that let us abstract over
+        // the verbs used in some diagnostic messages.
+        let act; let acted_on;
+
+        match error_access {
+            AccessKind::Mutate => {
+                let item_msg = match the_place_err {
+                    Place::Projection(box Projection {
+                        base: _,
+                        elem: ProjectionElem::Deref }
+                    ) => match self.describe_place(place) {
+                        Some(description) =>
+                            format!("`{}` which is behind a `&` reference", description),
+                        None => format!("data in a `&` reference"),
+                    },
+                    _ => item_msg,
+                };
+                err = self.tcx.cannot_assign(span, &item_msg, Origin::Mir);
+                act = "assign"; acted_on = "written";
+            }
+            AccessKind::MutableBorrow => {
+                err = self.tcx
+                    .cannot_borrow_path_as_mutable(span, &item_msg, Origin::Mir);
+                act = "borrow as mutable"; acted_on = "borrowed as mutable";
+            }
+        }
+
+        match the_place_err {
+            // We want to suggest users use `let mut` for local (user
+            // variable) mutations...
+            Place::Local(local) if self.mir.local_decls[*local].can_be_made_mutable() => {
+                // ... but it doesn't make sense to suggest it on
+                // variables that are `ref x`, `ref mut x`, `&self`,
+                // or `&mut self` (such variables are simply not
+                // mutable)..
+                let local_decl = &self.mir.local_decls[*local];
+                assert_eq!(local_decl.mutability, Mutability::Not);
+
+                err.span_label(span, format!("cannot {ACT}", ACT=act));
+                err.span_suggestion(local_decl.source_info.span,
+                                    "consider changing this to be mutable",
+                                    format!("mut {}", local_decl.name.unwrap()));
+            }
+
+            // complete hack to approximate old AST-borrowck
+            // diagnostic: if the span starts with a mutable borrow of
+            // a local variable, then just suggest the user remove it.
+            Place::Local(_) if {
+                if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) {
+                    snippet.starts_with("&mut ")
+                } else {
+                    false
+                }
+            } => {
+                err.span_label(span, format!("cannot {ACT}", ACT=act));
+                err.span_label(span, "try removing `&mut` here");
+            }
+
+            // We want to point out when a `&` can be readily replaced
+            // with an `&mut`.
+            //
+            // FIXME: can this case be generalized to work for an
+            // arbitrary base for the projection?
+            Place::Projection(box Projection { base: Place::Local(local),
+                                               elem: ProjectionElem::Deref })
+                if self.mir.local_decls[*local].is_nonref_binding() =>
+            {
+                let (err_help_span, suggested_code) =
+                    find_place_to_suggest_ampmut(self.tcx, self.mir, *local);
+                err.span_suggestion(err_help_span,
+                                    "consider changing this to be a mutable reference",
+                                    suggested_code);
+
+                let local_decl = &self.mir.local_decls[*local];
+                if let Some(name) = local_decl.name {
+                    err.span_label(
+                        span, format!("`{NAME}` is a `&` reference, \
+                                       so the data it refers to cannot be {ACTED_ON}",
+                                      NAME=name, ACTED_ON=acted_on));
+                } else {
+                    err.span_label(span, format!("cannot {ACT} through `&`-reference", ACT=act));
+                }
+            }
+
+            _ => {
+                err.span_label(span, format!("cannot {ACT}", ACT=act));
+            }
+        }
+
+        err.emit();
+        return true;
+
+        // Returns the span to highlight and the associated text to
+        // present when suggesting that the user use an `&mut`.
+        //
+        // When we want to suggest a user change a local variable to be a `&mut`, there
+        // are three potential "obvious" things to highlight:
+        //
+        // let ident [: Type] [= RightHandSideExresssion];
+        //     ^^^^^    ^^^^     ^^^^^^^^^^^^^^^^^^^^^^^
+        //     (1.)     (2.)              (3.)
+        //
+        // We can always fallback on highlighting the first. But chances are good that
+        // the user experience will be better if we highlight one of the others if possible;
+        // for example, if the RHS is present and the Type is not, then the type is going to
+        // be inferred *from* the RHS, which means we should highlight that (and suggest
+        // that they borrow the RHS mutably).
+        fn find_place_to_suggest_ampmut<'cx, 'gcx, 'tcx>(tcx: TyCtxt<'cx, 'gcx, 'tcx>,
+                                                         mir: &Mir<'tcx>,
+                                                         local: Local) -> (Span, String)
+        {
+            // This implementation attempts to emulate AST-borrowck prioritization
+            // by trying (3.), then (2.) and finally falling back on (1.).
+            let locations = mir.find_assignments(local);
+            if locations.len() > 0 {
+                let assignment_rhs_span = mir.source_info(locations[0]).span;
+                let snippet = tcx.sess.codemap().span_to_snippet(assignment_rhs_span);
+                if let Ok(src) = snippet {
+                    // pnkfelix inherited code; believes intention is
+                    // highlighted text will always be `&<expr>` and
+                    // thus can transform to `&mut` by slicing off
+                    // first ASCII character and prepending "&mut ".
+                    let borrowed_expr = src[1..].to_string();
+                    return (assignment_rhs_span, format!("&mut {}", borrowed_expr));
+                }
+            }
+
+            let local_decl = &mir.local_decls[local];
+            let highlight_span = match local_decl.is_user_variable {
+                // if this is a variable binding with an explicit type,
+                // try to highlight that for the suggestion.
+                Some(ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
+                    opt_ty_info: Some(ty_span), .. }))) => ty_span,
+
+                Some(ClearCrossCrate::Clear) => bug!("saw cleared local state"),
+
+                // otherwise, just highlight the span associated with
+                // the (MIR) LocalDecl.
+                _ => local_decl.source_info.span,
+            };
+
+            let ty_mut = local_decl.ty.builtin_deref(true).unwrap();
+            assert_eq!(ty_mut.mutbl, hir::MutImmutable);
+            return (highlight_span, format!("&mut {}", ty_mut.ty));
+        }
     }
 
     /// Adds the place into the used mutable variables set
diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs
index 7096f91dc1d..0fd55f752b8 100644
--- a/src/librustc_mir/build/block.rs
+++ b/src/librustc_mir/build/block.rs
@@ -137,7 +137,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                             }
                         }
 
-                        this.visit_bindings(&pattern, &mut |this, _, _, node, span, _| {
+                        this.visit_bindings(&pattern, &mut |this, _, _, _, node, span, _| {
                             this.storage_live_binding(block, node, span, OutsideGuard);
                             this.schedule_drop_for_binding(node, span, OutsideGuard);
                         })
diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs
index fbd771fbe50..94b387abe3c 100644
--- a/src/librustc_mir/build/expr/into.rs
+++ b/src/librustc_mir/build/expr/into.rs
@@ -249,7 +249,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                         source_info,
                         visibility_scope: source_info.scope,
                         internal: true,
-                        is_user_variable: false
+                        is_user_variable: None,
                     });
                     let ptr_temp = Place::Local(ptr_temp);
                     let block = unpack!(this.into(&ptr_temp, block, ptr));
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index 062a48a8c43..b6af0ed2a4a 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -307,7 +307,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         assert!(!(visibility_scope.is_some() && lint_level.is_explicit()),
                 "can't have both a visibility and a lint scope at the same time");
         let mut scope = self.source_scope;
-        self.visit_bindings(pattern, &mut |this, mutability, name, var, span, ty| {
+        self.visit_bindings(pattern, &mut |this, mutability, name, mode, var, span, ty| {
             if visibility_scope.is_none() {
                 visibility_scope = Some(this.new_source_scope(scope_span,
                                                            LintLevel::Inherited,
@@ -325,7 +325,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 scope,
             };
             let visibility_scope = visibility_scope.unwrap();
-            this.declare_binding(source_info, visibility_scope, mutability, name, var,
+            this.declare_binding(source_info, visibility_scope, mutability, name, mode, var,
                                  ty, has_guard);
         });
         visibility_scope
@@ -359,11 +359,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     }
 
     pub fn visit_bindings<F>(&mut self, pattern: &Pattern<'tcx>, f: &mut F)
-        where F: FnMut(&mut Self, Mutability, Name, NodeId, Span, Ty<'tcx>)
+        where F: FnMut(&mut Self, Mutability, Name, BindingMode, NodeId, Span, Ty<'tcx>)
     {
         match *pattern.kind {
-            PatternKind::Binding { mutability, name, var, ty, ref subpattern, .. } => {
-                f(self, mutability, name, var, pattern.span, ty);
+            PatternKind::Binding { mutability, name, mode, var, ty, ref subpattern, .. } => {
+                f(self, mutability, name, mode, var, pattern.span, ty);
                 if let Some(subpattern) = subpattern.as_ref() {
                     self.visit_bindings(subpattern, f);
                 }
@@ -1118,15 +1118,20 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                        visibility_scope: SourceScope,
                        mutability: Mutability,
                        name: Name,
+                       mode: BindingMode,
                        var_id: NodeId,
                        var_ty: Ty<'tcx>,
                        has_guard: ArmHasGuard)
     {
-        debug!("declare_binding(var_id={:?}, name={:?}, var_ty={:?}, visibility_scope={:?}, \
-                source_info={:?})",
-               var_id, name, var_ty, visibility_scope, source_info);
+        debug!("declare_binding(var_id={:?}, name={:?}, mode={:?}, var_ty={:?}, \
+                visibility_scope={:?}, source_info={:?})",
+               var_id, name, mode, var_ty, visibility_scope, source_info);
 
         let tcx = self.hir.tcx();
+        let binding_mode = match mode {
+            BindingMode::ByValue => ty::BindingMode::BindByValue(mutability.into()),
+            BindingMode::ByRef { .. } => ty::BindingMode::BindByReference(mutability.into()),
+        };
         let local = LocalDecl::<'tcx> {
             mutability,
             ty: var_ty.clone(),
@@ -1134,7 +1139,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             source_info,
             visibility_scope,
             internal: false,
-            is_user_variable: true,
+            is_user_variable: Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
+                binding_mode,
+                // hypothetically, `visit_bindings` could try to unzip
+                // an outermost hir::Ty as we descend, matching up
+                // idents in pat; but complex w/ unclear UI payoff.
+                // Instead, just abandon providing diagnostic info.
+                opt_ty_info: None,
+            }))),
         };
         let for_arm_body = self.local_decls.push(local.clone());
         let locals = if has_guard.0 && tcx.all_pat_vars_are_implicit_refs_within_guards() {
@@ -1145,8 +1157,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 name: Some(name),
                 source_info,
                 visibility_scope,
+                // FIXME: should these secretly injected ref_for_guard's be marked as `internal`?
                 internal: false,
-                is_user_variable: true,
+                is_user_variable: None,
             });
             LocalsForNode::Three { val_for_guard, ref_for_guard, for_arm_body }
         } else {
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index ca7a2daf39d..85671414618 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -70,11 +70,11 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t
                     // HACK(eddyb) Avoid having RustCall on closures,
                     // as it adds unnecessary (and wrong) auto-tupling.
                     abi = Abi::Rust;
-                    Some((liberated_closure_env_ty(tcx, id, body_id), None))
+                    Some(ArgInfo(liberated_closure_env_ty(tcx, id, body_id), None, None, None))
                 }
                 ty::TyGenerator(..) => {
                     let gen_ty = tcx.body_tables(body_id).node_id_to_type(fn_hir_id);
-                    Some((gen_ty, None))
+                    Some(ArgInfo(gen_ty, None, None, None))
                 }
                 _ => None,
             };
@@ -91,7 +91,23 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t
                     .iter()
                     .enumerate()
                     .map(|(index, arg)| {
-                        (fn_sig.inputs()[index], Some(&*arg.pat))
+                        let owner_id = tcx.hir.body_owner(body_id);
+                        let opt_ty_info;
+                        let self_arg;
+                        if let Some(ref fn_decl) = tcx.hir.fn_decl(owner_id) {
+                            let ty_hir_id = fn_decl.inputs[index].hir_id;
+                            let ty_span = tcx.hir.span(tcx.hir.hir_to_node_id(ty_hir_id));
+                            opt_ty_info = Some(ty_span);
+                            self_arg = if index == 0 && fn_decl.has_implicit_self {
+                                Some(ImplicitSelfBinding)
+                            } else {
+                                None
+                            };
+                        } else {
+                            opt_ty_info = None;
+                            self_arg = None;
+                        }
+                        ArgInfo(fn_sig.inputs()[index], opt_ty_info, Some(&*arg.pat), self_arg)
                     });
 
             let arguments = implicit_argument.into_iter().chain(explicit_arguments);
@@ -433,6 +449,13 @@ fn should_abort_on_panic<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
 ///////////////////////////////////////////////////////////////////////////
 /// the main entry point for building MIR for a function
 
+struct ImplicitSelfBinding;
+
+struct ArgInfo<'gcx>(Ty<'gcx>,
+                     Option<Span>,
+                     Option<&'gcx hir::Pat>,
+                     Option<ImplicitSelfBinding>);
+
 fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
                                    fn_id: ast::NodeId,
                                    arguments: A,
@@ -442,7 +465,7 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
                                    yield_ty: Option<Ty<'gcx>>,
                                    body: &'gcx hir::Body)
                                    -> Mir<'tcx>
-    where A: Iterator<Item=(Ty<'gcx>, Option<&'gcx hir::Pat>)>
+    where A: Iterator<Item=ArgInfo<'gcx>>
 {
     let arguments: Vec<_> = arguments.collect();
 
@@ -642,13 +665,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
     fn args_and_body(&mut self,
                      mut block: BasicBlock,
-                     arguments: &[(Ty<'gcx>, Option<&'gcx hir::Pat>)],
+                     arguments: &[ArgInfo<'gcx>],
                      argument_scope: region::Scope,
                      ast_body: &'gcx hir::Expr)
                      -> BlockAnd<()>
     {
         // Allocate locals for the function arguments
-        for &(ty, pattern) in arguments.iter() {
+        for &ArgInfo(ty, _, pattern, _) in arguments.iter() {
             // If this is a simple binding pattern, give the local a nice name for debuginfo.
             let mut name = None;
             if let Some(pat) = pattern {
@@ -668,16 +691,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 visibility_scope: source_info.scope,
                 name,
                 internal: false,
-                is_user_variable: false,
+                is_user_variable: None,
             });
         }
 
         let mut scope = None;
         // Bind the argument patterns
-        for (index, &(ty, pattern)) in arguments.iter().enumerate() {
+        for (index, arg_info) in arguments.iter().enumerate() {
             // Function arguments always get the first Local indices after the return place
             let local = Local::new(index + 1);
             let place = Place::Local(local);
+            let &ArgInfo(ty, opt_ty_info, pattern, ref self_binding) = arg_info;
 
             if let Some(pattern) = pattern {
                 let pattern = self.hir.pattern_from_hir(pattern);
@@ -686,6 +710,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     // Don't introduce extra copies for simple bindings
                     PatternKind::Binding { mutability, var, mode: BindingMode::ByValue, .. } => {
                         self.local_decls[local].mutability = mutability;
+                        self.local_decls[local].is_user_variable =
+                            if let Some(ImplicitSelfBinding) = self_binding {
+                                Some(ClearCrossCrate::Set(BindingForm::ImplicitSelf))
+                            } else {
+                                let binding_mode = ty::BindingMode::BindByValue(mutability.into());
+                                Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
+                                    binding_mode, opt_ty_info })))
+                            };
                         self.var_indices.insert(var, LocalsForNode::One(local));
                     }
                     _ => {
diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs
index f11d80201c2..c703486560d 100644
--- a/src/librustc_mir/shim.rs
+++ b/src/librustc_mir/shim.rs
@@ -144,7 +144,7 @@ fn temp_decl(mutability: Mutability, ty: Ty, span: Span) -> LocalDecl {
         source_info,
         visibility_scope: source_info.scope,
         internal: false,
-        is_user_variable: false
+        is_user_variable: None,
     }
 }
 
diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs
index 7ac3b7beb06..a6017fafcc8 100644
--- a/src/librustc_mir/transform/generator.rs
+++ b/src/librustc_mir/transform/generator.rs
@@ -303,7 +303,7 @@ fn replace_result_variable<'tcx>(ret_ty: Ty<'tcx>,
         source_info,
         visibility_scope: source_info.scope,
         internal: false,
-        is_user_variable: false,
+        is_user_variable: None,
     };
     let new_ret_local = Local::new(mir.local_decls.len());
     mir.local_decls.push(new_ret);
@@ -644,7 +644,7 @@ fn create_generator_drop_shim<'a, 'tcx>(
         source_info,
         visibility_scope: source_info.scope,
         internal: false,
-        is_user_variable: false,
+        is_user_variable: None,
     };
 
     make_generator_state_argument_indirect(tcx, def_id, &mut mir);
@@ -660,7 +660,7 @@ fn create_generator_drop_shim<'a, 'tcx>(
         source_info,
         visibility_scope: source_info.scope,
         internal: false,
-        is_user_variable: false,
+        is_user_variable: None,
     };
 
     no_landing_pads(tcx, &mut mir);
diff --git a/src/test/compile-fail/assign-imm-local-twice.rs b/src/test/compile-fail/assign-imm-local-twice.rs
index d5e412c3745..ee9dafdd49e 100644
--- a/src/test/compile-fail/assign-imm-local-twice.rs
+++ b/src/test/compile-fail/assign-imm-local-twice.rs
@@ -13,6 +13,7 @@
 
 fn test() {
     let v: isize;
+    //[mir]~^ NOTE consider changing this to `mut v`
     v = 1; //[ast]~ NOTE first assignment
            //[mir]~^ NOTE first assignment
     println!("v={}", v);
diff --git a/src/test/compile-fail/borrowck/borrowck-issue-14498.rs b/src/test/compile-fail/borrowck/borrowck-issue-14498.rs
index fbdd013024d..3c5f932af47 100644
--- a/src/test/compile-fail/borrowck/borrowck-issue-14498.rs
+++ b/src/test/compile-fail/borrowck/borrowck-issue-14498.rs
@@ -27,7 +27,7 @@ fn indirect_write_to_imm_box() {
     let y: Box<_> = box &mut x;
     let p = &y;
     ***p = 2; //[ast]~ ERROR cannot assign to data in a `&` reference
-              //[mir]~^ ERROR cannot assign to data in a `&` reference
+              //[mir]~^ ERROR cannot assign to `***p`
     drop(p);
 }
 
diff --git a/src/test/compile-fail/borrowck/borrowck-overloaded-index-ref-index.rs b/src/test/compile-fail/borrowck/borrowck-overloaded-index-ref-index.rs
index 109fe3d1128..f4f40e0407f 100644
--- a/src/test/compile-fail/borrowck/borrowck-overloaded-index-ref-index.rs
+++ b/src/test/compile-fail/borrowck/borrowck-overloaded-index-ref-index.rs
@@ -70,6 +70,6 @@ fn main() {
     };
     s[2] = 20;
     //[ast]~^ ERROR cannot assign to immutable indexed content
-    //[mir]~^^ ERROR cannot assign to immutable item
+    //[mir]~^^ ERROR cannot assign to data in a `&` reference
     drop(rs);
 }
diff --git a/src/test/compile-fail/issue-45199.rs b/src/test/compile-fail/issue-45199.rs
index ecddb4c101f..61e19ffc0d2 100644
--- a/src/test/compile-fail/issue-45199.rs
+++ b/src/test/compile-fail/issue-45199.rs
@@ -13,6 +13,7 @@
 
 fn test_drop_replace() {
     let b: Box<isize>;
+    //[mir]~^ NOTE consider changing this to `mut b`
     b = Box::new(1);    //[ast]~ NOTE first assignment
                         //[mir]~^ NOTE first assignment
     b = Box::new(2);    //[ast]~ ERROR cannot assign twice to immutable variable
@@ -24,6 +25,7 @@ fn test_drop_replace() {
 fn test_call() {
     let b = Box::new(1);    //[ast]~ NOTE first assignment
                             //[mir]~^ NOTE first assignment
+                            //[mir]~| NOTE consider changing this to `mut b`
     b = Box::new(2);        //[ast]~ ERROR cannot assign twice to immutable variable
                             //[mir]~^ ERROR cannot assign twice to immutable variable `b`
                             //[ast]~| NOTE cannot assign twice to immutable
@@ -31,7 +33,7 @@ fn test_call() {
 }
 
 fn test_args(b: Box<i32>) {  //[ast]~ NOTE first assignment
-                                //[mir]~^ NOTE argument not declared as `mut`
+                                //[mir]~^ NOTE consider changing this to `mut b`
     b = Box::new(2);            //[ast]~ ERROR cannot assign twice to immutable variable
                                 //[mir]~^ ERROR cannot assign to immutable argument `b`
                                 //[ast]~| NOTE cannot assign twice to immutable
diff --git a/src/test/compile-fail/liveness-assign-imm-local-in-loop.rs b/src/test/compile-fail/liveness-assign-imm-local-in-loop.rs
index f28906ddb95..7bc3680ca77 100644
--- a/src/test/compile-fail/liveness-assign-imm-local-in-loop.rs
+++ b/src/test/compile-fail/liveness-assign-imm-local-in-loop.rs
@@ -13,6 +13,7 @@
 
 fn test() {
     let v: isize;
+    //[mir]~^ NOTE consider changing this to `mut v`
     loop {
         v = 1; //[ast]~ ERROR cannot assign twice to immutable variable
                //[mir]~^ ERROR cannot assign twice to immutable variable `v`
diff --git a/src/test/compile-fail/liveness-assign-imm-local-in-op-eq.rs b/src/test/compile-fail/liveness-assign-imm-local-in-op-eq.rs
index 594cc076121..7812cdd8684 100644
--- a/src/test/compile-fail/liveness-assign-imm-local-in-op-eq.rs
+++ b/src/test/compile-fail/liveness-assign-imm-local-in-op-eq.rs
@@ -13,6 +13,7 @@
 
 fn test() {
     let v: isize;
+    //[mir]~^ NOTE consider changing this to `mut v`
     v = 2;  //[ast]~ NOTE first assignment
             //[mir]~^ NOTE first assignment
     v += 1; //[ast]~ ERROR cannot assign twice to immutable variable
diff --git a/src/test/compile-fail/liveness-assign-imm-local-with-drop.rs b/src/test/compile-fail/liveness-assign-imm-local-with-drop.rs
index b4fb33ca15e..bb61a9037d9 100644
--- a/src/test/compile-fail/liveness-assign-imm-local-with-drop.rs
+++ b/src/test/compile-fail/liveness-assign-imm-local-with-drop.rs
@@ -14,6 +14,7 @@
 fn test() {
     let b = Box::new(1); //[ast]~ NOTE first assignment
                          //[mir]~^ NOTE first assignment
+                         //[mir]~| NOTE consider changing this to `mut b`
     drop(b);
     b = Box::new(2); //[ast]~ ERROR cannot assign twice to immutable variable
                      //[mir]~^ ERROR cannot assign twice to immutable variable `b`
diff --git a/src/test/compile-fail/liveness-assign-imm-local-with-init.rs b/src/test/compile-fail/liveness-assign-imm-local-with-init.rs
index 7204b5d5f2e..672e9fab837 100644
--- a/src/test/compile-fail/liveness-assign-imm-local-with-init.rs
+++ b/src/test/compile-fail/liveness-assign-imm-local-with-init.rs
@@ -14,6 +14,7 @@
 fn test() {
     let v: isize = 1; //[ast]~ NOTE first assignment
                       //[mir]~^ NOTE first assignment
+                      //[mir]~| NOTE consider changing this to `mut v`
     v.clone();
     v = 2; //[ast]~ ERROR cannot assign twice to immutable variable
            //[mir]~^ ERROR cannot assign twice to immutable variable `v`
diff --git a/src/test/compile-fail/unboxed-closures-mutated-upvar-from-fn-closure.rs b/src/test/compile-fail/unboxed-closures-mutated-upvar-from-fn-closure.rs
index 0dbd61413e0..f20719ec7fa 100644
--- a/src/test/compile-fail/unboxed-closures-mutated-upvar-from-fn-closure.rs
+++ b/src/test/compile-fail/unboxed-closures-mutated-upvar-from-fn-closure.rs
@@ -23,6 +23,6 @@ fn main() {
     call(|| {
         counter += 1;
         //[ast]~^ ERROR cannot assign to data in a captured outer variable in an `Fn` closure
-        //[mir]~^^ ERROR cannot assign to immutable item `counter`
+        //[mir]~^^ ERROR cannot assign to `counter`
     });
 }
diff --git a/src/test/ui/asm-out-assign-imm.nll.stderr b/src/test/ui/asm-out-assign-imm.nll.stderr
new file mode 100644
index 00000000000..fbd58cd4c22
--- /dev/null
+++ b/src/test/ui/asm-out-assign-imm.nll.stderr
@@ -0,0 +1,14 @@
+error[E0384]: cannot assign twice to immutable variable `x`
+  --> $DIR/asm-out-assign-imm.rs:33:9
+   |
+LL |     let x: isize;
+   |         - consider changing this to `mut x`
+LL |     x = 1;
+   |     ----- first assignment to `x`
+...
+LL |         asm!("mov $1, $0" : "=r"(x) : "r"(5));
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot assign twice to immutable variable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0384`.
diff --git a/src/test/ui/augmented-assignments.nll.stderr b/src/test/ui/augmented-assignments.nll.stderr
index deb2e7ed4a3..592f666eff8 100644
--- a/src/test/ui/augmented-assignments.nll.stderr
+++ b/src/test/ui/augmented-assignments.nll.stderr
@@ -17,6 +17,9 @@ LL | |     x;  //~ value moved here
 error[E0596]: cannot borrow immutable item `y` as mutable
   --> $DIR/augmented-assignments.rs:30:5
    |
+LL |     let y = Int(2);
+   |         - help: consider changing this to be mutable: `mut y`
+LL |     //~^ consider changing this to `mut y`
 LL |     y   //~ error: cannot borrow immutable local variable `y` as mutable
    |     ^ cannot borrow as mutable
 
diff --git a/src/test/ui/borrowck/immutable-arg.stderr b/src/test/ui/borrowck/immutable-arg.stderr
index b7506849b81..1cf68795bf0 100644
--- a/src/test/ui/borrowck/immutable-arg.stderr
+++ b/src/test/ui/borrowck/immutable-arg.stderr
@@ -10,7 +10,7 @@ error[E0384]: cannot assign to immutable argument `_x` (Mir)
   --> $DIR/immutable-arg.rs:14:5
    |
 LL | fn foo(_x: u32) {
-   |        -- argument not declared as `mut`
+   |        -- consider changing this to `mut _x`
 LL |     _x = 4;
    |     ^^^^^^ cannot assign to immutable argument
 
diff --git a/src/test/ui/borrowck/issue-45983.nll.stderr b/src/test/ui/borrowck/issue-45983.nll.stderr
index 1aec71fee34..a008a408d97 100644
--- a/src/test/ui/borrowck/issue-45983.nll.stderr
+++ b/src/test/ui/borrowck/issue-45983.nll.stderr
@@ -14,13 +14,13 @@ error[E0594]: cannot assign to immutable item `x`
   --> $DIR/issue-45983.rs:17:18
    |
 LL |     give_any(|y| x = Some(y));
-   |                  ^^^^^^^^^^^ cannot mutate
-   |
-   = note: the value which is causing this path not to be mutable is...: `x`
+   |                  ^^^^^^^^^^^ cannot assign
 
 error[E0596]: cannot borrow immutable item `x` as mutable
   --> $DIR/issue-45983.rs:17:14
    |
+LL |     let x = None;
+   |         - help: consider changing this to be mutable: `mut x`
 LL |     give_any(|y| x = Some(y));
    |              ^^^^^^^^^^^^^^^ cannot borrow as mutable
 
diff --git a/src/test/ui/borrowck/mut-borrow-of-mut-ref.nll.stderr b/src/test/ui/borrowck/mut-borrow-of-mut-ref.nll.stderr
index f8b84bce04e..0674c823016 100644
--- a/src/test/ui/borrowck/mut-borrow-of-mut-ref.nll.stderr
+++ b/src/test/ui/borrowck/mut-borrow-of-mut-ref.nll.stderr
@@ -1,6 +1,8 @@
 error[E0596]: cannot borrow immutable item `b` as mutable
   --> $DIR/mut-borrow-of-mut-ref.rs:18:7
    |
+LL | fn f(b: &mut i32) {
+   |      - help: consider changing this to be mutable: `mut b`
 LL |     g(&mut b) //~ ERROR cannot borrow
    |       ^^^^^^ cannot borrow as mutable
 
diff --git a/src/test/ui/codemap_tests/huge_multispan_highlight.nll.stderr b/src/test/ui/codemap_tests/huge_multispan_highlight.nll.stderr
index 4526616e488..da0b201f5d4 100644
--- a/src/test/ui/codemap_tests/huge_multispan_highlight.nll.stderr
+++ b/src/test/ui/codemap_tests/huge_multispan_highlight.nll.stderr
@@ -1,6 +1,9 @@
 error[E0596]: cannot borrow immutable item `x` as mutable
   --> $DIR/huge_multispan_highlight.rs:100:13
    |
+LL |     let x = "foo";
+   |         - help: consider changing this to be mutable: `mut x`
+...
 LL |     let y = &mut x; //~ ERROR cannot borrow
    |             ^^^^^^ cannot borrow as mutable
 
diff --git a/src/test/ui/command-line-diagnostics.nll.stderr b/src/test/ui/command-line-diagnostics.nll.stderr
index 2d5a31f0586..10dcf7d0e65 100644
--- a/src/test/ui/command-line-diagnostics.nll.stderr
+++ b/src/test/ui/command-line-diagnostics.nll.stderr
@@ -2,7 +2,9 @@ error[E0384]: cannot assign twice to immutable variable `x`
   --> $DIR/command-line-diagnostics.rs:16:5
    |
 LL |     let x = 42;
-   |             -- first assignment to `x`
+   |         -   -- first assignment to `x`
+   |         |
+   |         consider changing this to `mut x`
 LL |     x = 43;
    |     ^^^^^^ cannot assign twice to immutable variable
 
diff --git a/src/test/ui/did_you_mean/issue-31424.nll.stderr b/src/test/ui/did_you_mean/issue-31424.nll.stderr
index 6b63f64c699..21c5e3608c0 100644
--- a/src/test/ui/did_you_mean/issue-31424.nll.stderr
+++ b/src/test/ui/did_you_mean/issue-31424.nll.stderr
@@ -7,6 +7,8 @@ LL |         (&mut self).bar(); //~ ERROR cannot borrow
 error[E0596]: cannot borrow immutable item `self` as mutable
   --> $DIR/issue-31424.rs:23:9
    |
+LL |     fn bar(self: &mut Self) {
+   |            ---- help: consider changing this to be mutable: `mut self`
 LL |         (&mut self).bar(); //~ ERROR cannot borrow
    |         ^^^^^^^^^^^ cannot borrow as mutable
 
diff --git a/src/test/ui/did_you_mean/issue-34126.nll.stderr b/src/test/ui/did_you_mean/issue-34126.nll.stderr
index 81f858f6bfc..8dedb6ec4db 100644
--- a/src/test/ui/did_you_mean/issue-34126.nll.stderr
+++ b/src/test/ui/did_you_mean/issue-34126.nll.stderr
@@ -2,7 +2,10 @@ error[E0596]: cannot borrow immutable item `self` as mutable
   --> $DIR/issue-34126.rs:16:18
    |
 LL |         self.run(&mut self); //~ ERROR cannot borrow
-   |                  ^^^^^^^^^ cannot borrow as mutable
+   |                  ^^^^^^^^^
+   |                  |
+   |                  cannot borrow as mutable
+   |                  try removing `&mut` here
 
 error[E0502]: cannot borrow `self` as mutable because it is also borrowed as immutable
   --> $DIR/issue-34126.rs:16:18
diff --git a/src/test/ui/did_you_mean/issue-34337.nll.stderr b/src/test/ui/did_you_mean/issue-34337.nll.stderr
index 258e1bb1ad7..d2271e8e7de 100644
--- a/src/test/ui/did_you_mean/issue-34337.nll.stderr
+++ b/src/test/ui/did_you_mean/issue-34337.nll.stderr
@@ -2,7 +2,10 @@ error[E0596]: cannot borrow immutable item `key` as mutable
   --> $DIR/issue-34337.rs:16:9
    |
 LL |     get(&mut key); //~ ERROR cannot borrow
-   |         ^^^^^^^^ cannot borrow as mutable
+   |         ^^^^^^^^
+   |         |
+   |         cannot borrow as mutable
+   |         try removing `&mut` here
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/did_you_mean/issue-35937.nll.stderr b/src/test/ui/did_you_mean/issue-35937.nll.stderr
index 40b640b63cf..0c1dcb29d4d 100644
--- a/src/test/ui/did_you_mean/issue-35937.nll.stderr
+++ b/src/test/ui/did_you_mean/issue-35937.nll.stderr
@@ -1,16 +1,18 @@
 error[E0596]: cannot borrow immutable item `f.v` as mutable
   --> $DIR/issue-35937.rs:17:5
    |
+LL |     let f = Foo { v: Vec::new() };
+   |         - help: consider changing this to be mutable: `mut f`
 LL |     f.v.push("cat".to_string()); //~ ERROR cannot borrow
    |     ^^^ cannot borrow as mutable
-   |
-   = note: the value which is causing this path not to be mutable is...: `f`
 
 error[E0384]: cannot assign twice to immutable variable `s.x`
   --> $DIR/issue-35937.rs:26:5
    |
 LL |     let s = S { x: 42 };
-   |             ----------- first assignment to `s.x`
+   |         -   ----------- first assignment to `s.x`
+   |         |
+   |         consider changing this to `mut s`
 LL |     s.x += 1; //~ ERROR cannot assign
    |     ^^^^^^^^ cannot assign twice to immutable variable
 
@@ -18,7 +20,10 @@ error[E0384]: cannot assign twice to immutable variable `s.x`
   --> $DIR/issue-35937.rs:30:5
    |
 LL | fn bar(s: S) {
-   |        - first assignment to `s.x`
+   |        -
+   |        |
+   |        first assignment to `s.x`
+   |        consider changing this to `mut s`
 LL |     s.x += 1; //~ ERROR cannot assign
    |     ^^^^^^^^ cannot assign twice to immutable variable
 
diff --git a/src/test/ui/did_you_mean/issue-37139.nll.stderr b/src/test/ui/did_you_mean/issue-37139.nll.stderr
index 29c7192a98b..e51a1baad25 100644
--- a/src/test/ui/did_you_mean/issue-37139.nll.stderr
+++ b/src/test/ui/did_you_mean/issue-37139.nll.stderr
@@ -2,7 +2,10 @@ error[E0596]: cannot borrow immutable item `x` as mutable
   --> $DIR/issue-37139.rs:22:18
    |
 LL |             test(&mut x); //~ ERROR cannot borrow immutable
-   |                  ^^^^^^ cannot borrow as mutable
+   |                  ^^^^^^
+   |                  |
+   |                  cannot borrow as mutable
+   |                  try removing `&mut` here
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/did_you_mean/issue-38147-1.nll.stderr b/src/test/ui/did_you_mean/issue-38147-1.nll.stderr
index 8e442677951..76b8c8ebf60 100644
--- a/src/test/ui/did_you_mean/issue-38147-1.nll.stderr
+++ b/src/test/ui/did_you_mean/issue-38147-1.nll.stderr
@@ -1,10 +1,10 @@
 error[E0596]: cannot borrow immutable item `*self.s` as mutable
   --> $DIR/issue-38147-1.rs:27:9
    |
+LL |     fn f(&self) {
+   |          ----- help: consider changing this to be a mutable reference: `&mut Foo<'_>`
 LL |         self.s.push('x'); //~ ERROR cannot borrow data mutably
-   |         ^^^^^^ cannot borrow as mutable
-   |
-   = note: the value which is causing this path not to be mutable is...: `*self`
+   |         ^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/did_you_mean/issue-38147-4.nll.stderr b/src/test/ui/did_you_mean/issue-38147-4.nll.stderr
index 6808222cc32..c875957623b 100644
--- a/src/test/ui/did_you_mean/issue-38147-4.nll.stderr
+++ b/src/test/ui/did_you_mean/issue-38147-4.nll.stderr
@@ -1,10 +1,10 @@
 error[E0596]: cannot borrow immutable item `*f.s` as mutable
   --> $DIR/issue-38147-4.rs:16:5
    |
+LL | fn f(x: usize, f: &Foo) {
+   |                   ---- help: consider changing this to be a mutable reference: `&mut Foo<'_>`
 LL |     f.s.push('x'); //~ ERROR cannot borrow data mutably
-   |     ^^^ cannot borrow as mutable
-   |
-   = note: the value which is causing this path not to be mutable is...: `*f`
+   |     ^^^ `f` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/did_you_mean/issue-39544.nll.stderr b/src/test/ui/did_you_mean/issue-39544.nll.stderr
index f5f5b675e77..02c1debca69 100644
--- a/src/test/ui/did_you_mean/issue-39544.nll.stderr
+++ b/src/test/ui/did_you_mean/issue-39544.nll.stderr
@@ -1,96 +1,100 @@
 error[E0596]: cannot borrow immutable item `z.x` as mutable
   --> $DIR/issue-39544.rs:21:13
    |
+LL |     let z = Z { x: X::Y };
+   |         - help: consider changing this to be mutable: `mut z`
 LL |     let _ = &mut z.x; //~ ERROR cannot borrow
    |             ^^^^^^^^ cannot borrow as mutable
-   |
-   = note: the value which is causing this path not to be mutable is...: `z`
 
 error[E0596]: cannot borrow immutable item `self.x` as mutable
   --> $DIR/issue-39544.rs:26:17
    |
+LL |     fn foo<'z>(&'z self) {
+   |                -------- help: consider changing this to be a mutable reference: `&mut Z`
 LL |         let _ = &mut self.x; //~ ERROR cannot borrow
-   |                 ^^^^^^^^^^^ cannot borrow as mutable
-   |
-   = note: the value which is causing this path not to be mutable is...: `*self`
+   |                 ^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error[E0596]: cannot borrow immutable item `self.x` as mutable
   --> $DIR/issue-39544.rs:30:17
    |
+LL |     fn foo1(&self, other: &Z) {
+   |             ----- help: consider changing this to be a mutable reference: `&mut Z`
 LL |         let _ = &mut self.x; //~ ERROR cannot borrow
-   |                 ^^^^^^^^^^^ cannot borrow as mutable
-   |
-   = note: the value which is causing this path not to be mutable is...: `*self`
+   |                 ^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error[E0596]: cannot borrow immutable item `other.x` as mutable
   --> $DIR/issue-39544.rs:31:17
    |
+LL |     fn foo1(&self, other: &Z) {
+   |                           -- help: consider changing this to be a mutable reference: `&mut Z`
+LL |         let _ = &mut self.x; //~ ERROR cannot borrow
 LL |         let _ = &mut other.x; //~ ERROR cannot borrow
-   |                 ^^^^^^^^^^^^ cannot borrow as mutable
-   |
-   = note: the value which is causing this path not to be mutable is...: `*other`
+   |                 ^^^^^^^^^^^^ `other` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error[E0596]: cannot borrow immutable item `self.x` as mutable
   --> $DIR/issue-39544.rs:35:17
    |
+LL |     fn foo2<'a>(&'a self, other: &Z) {
+   |                 -------- help: consider changing this to be a mutable reference: `&mut Z`
 LL |         let _ = &mut self.x; //~ ERROR cannot borrow
-   |                 ^^^^^^^^^^^ cannot borrow as mutable
-   |
-   = note: the value which is causing this path not to be mutable is...: `*self`
+   |                 ^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error[E0596]: cannot borrow immutable item `other.x` as mutable
   --> $DIR/issue-39544.rs:36:17
    |
+LL |     fn foo2<'a>(&'a self, other: &Z) {
+   |                                  -- help: consider changing this to be a mutable reference: `&mut Z`
+LL |         let _ = &mut self.x; //~ ERROR cannot borrow
 LL |         let _ = &mut other.x; //~ ERROR cannot borrow
-   |                 ^^^^^^^^^^^^ cannot borrow as mutable
-   |
-   = note: the value which is causing this path not to be mutable is...: `*other`
+   |                 ^^^^^^^^^^^^ `other` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error[E0596]: cannot borrow immutable item `self.x` as mutable
   --> $DIR/issue-39544.rs:40:17
    |
+LL |     fn foo3<'a>(self: &'a Self, other: &Z) {
+   |                       -------- help: consider changing this to be a mutable reference: `&mut Z`
 LL |         let _ = &mut self.x; //~ ERROR cannot borrow
-   |                 ^^^^^^^^^^^ cannot borrow as mutable
-   |
-   = note: the value which is causing this path not to be mutable is...: `*self`
+   |                 ^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error[E0596]: cannot borrow immutable item `other.x` as mutable
   --> $DIR/issue-39544.rs:41:17
    |
+LL |     fn foo3<'a>(self: &'a Self, other: &Z) {
+   |                                        -- help: consider changing this to be a mutable reference: `&mut Z`
+LL |         let _ = &mut self.x; //~ ERROR cannot borrow
 LL |         let _ = &mut other.x; //~ ERROR cannot borrow
-   |                 ^^^^^^^^^^^^ cannot borrow as mutable
-   |
-   = note: the value which is causing this path not to be mutable is...: `*other`
+   |                 ^^^^^^^^^^^^ `other` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error[E0596]: cannot borrow immutable item `other.x` as mutable
   --> $DIR/issue-39544.rs:45:17
    |
+LL |     fn foo4(other: &Z) {
+   |                    -- help: consider changing this to be a mutable reference: `&mut Z`
 LL |         let _ = &mut other.x; //~ ERROR cannot borrow
-   |                 ^^^^^^^^^^^^ cannot borrow as mutable
-   |
-   = note: the value which is causing this path not to be mutable is...: `*other`
+   |                 ^^^^^^^^^^^^ `other` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error[E0596]: cannot borrow immutable item `z.x` as mutable
   --> $DIR/issue-39544.rs:51:13
    |
+LL | pub fn with_arg(z: Z, w: &Z) {
+   |                 - help: consider changing this to be mutable: `mut z`
 LL |     let _ = &mut z.x; //~ ERROR cannot borrow
    |             ^^^^^^^^ cannot borrow as mutable
-   |
-   = note: the value which is causing this path not to be mutable is...: `z`
 
 error[E0596]: cannot borrow immutable item `w.x` as mutable
   --> $DIR/issue-39544.rs:52:13
    |
+LL | pub fn with_arg(z: Z, w: &Z) {
+   |                          -- help: consider changing this to be a mutable reference: `&mut Z`
+LL |     let _ = &mut z.x; //~ ERROR cannot borrow
 LL |     let _ = &mut w.x; //~ ERROR cannot borrow
-   |             ^^^^^^^^ cannot borrow as mutable
-   |
-   = note: the value which is causing this path not to be mutable is...: `*w`
+   |             ^^^^^^^^ `w` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
-error[E0594]: cannot assign to immutable item `*x.0`
+error[E0594]: cannot assign to `*x.0` which is behind a `&` reference
   --> $DIR/issue-39544.rs:58:5
    |
 LL |     *x.0 = 1;
-   |     ^^^^^^^^ cannot mutate
+   |     ^^^^^^^^ cannot assign
 
 error: aborting due to 12 previous errors
 
diff --git a/src/test/ui/did_you_mean/issue-40823.nll.stderr b/src/test/ui/did_you_mean/issue-40823.nll.stderr
index 489e1c39c46..1fd75de8128 100644
--- a/src/test/ui/did_you_mean/issue-40823.nll.stderr
+++ b/src/test/ui/did_you_mean/issue-40823.nll.stderr
@@ -1,8 +1,10 @@
 error[E0596]: cannot borrow immutable item `*buf` as mutable
   --> $DIR/issue-40823.rs:13:5
    |
+LL |     let mut buf = &[1, 2, 3, 4];
+   |                   ------------- help: consider changing this to be a mutable reference: `&mut [1, 2, 3, 4]`
 LL |     buf.iter_mut(); //~ ERROR cannot borrow immutable borrowed content
-   |     ^^^ cannot borrow as mutable
+   |     ^^^ `buf` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/error-codes/E0389.nll.stderr b/src/test/ui/error-codes/E0389.nll.stderr
index 0525e16239d..66e46dfe29d 100644
--- a/src/test/ui/error-codes/E0389.nll.stderr
+++ b/src/test/ui/error-codes/E0389.nll.stderr
@@ -1,4 +1,4 @@
-error[E0594]: cannot assign to data in a `&` reference
+error[E0594]: cannot assign to `fancy_ref.num` which is behind a `&` reference
   --> $DIR/E0389.rs:18:5
    |
 LL |     let fancy_ref = &(&mut fancy);
diff --git a/src/test/ui/issue-36400.nll.stderr b/src/test/ui/issue-36400.nll.stderr
index 80459937479..be10b1d517b 100644
--- a/src/test/ui/issue-36400.nll.stderr
+++ b/src/test/ui/issue-36400.nll.stderr
@@ -1,10 +1,10 @@
 error[E0596]: cannot borrow immutable item `*x` as mutable
   --> $DIR/issue-36400.rs:15:7
    |
+LL |     let x = Box::new(3);
+   |         - help: consider changing this to be mutable: `mut x`
 LL |     f(&mut *x); //~ ERROR cannot borrow immutable
    |       ^^^^^^^ cannot borrow as mutable
-   |
-   = note: the value which is causing this path not to be mutable is...: `x`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.nll.stderr
index 9d6d68f518d..93f2837c0fc 100644
--- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.nll.stderr
+++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.nll.stderr
@@ -18,7 +18,7 @@ error[E0384]: cannot assign to immutable argument `y`
   --> $DIR/ex3-both-anon-regions-one-is-struct-2.rs:14:5
    |
 LL | fn foo(mut x: Ref, y: &u32) {
-   |                    - argument not declared as `mut`
+   |                    - consider changing this to `mut y`
 LL |     y = x.b; //~ ERROR lifetime mismatch
    |     ^^^^^^^ cannot assign to immutable argument
 
diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr
index f58f33c9a9a..01e26980323 100644
--- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr
+++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr
@@ -15,6 +15,8 @@ LL |   y.push(z); //~ ERROR lifetime mismatch
 error[E0596]: cannot borrow immutable item `y` as mutable
   --> $DIR/ex3-both-anon-regions-using-fn-items.rs:11:3
    |
+LL | fn foo(x:fn(&u8, &u8), y: Vec<&u8>, z: &u8) {
+   |                        - help: consider changing this to be mutable: `mut y`
 LL |   y.push(z); //~ ERROR lifetime mismatch
    |   ^ cannot borrow as mutable
 
diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr
index 4bfb4ac2833..a61d49fc953 100644
--- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr
+++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr
@@ -15,6 +15,8 @@ LL |   y.push(z); //~ ERROR lifetime mismatch
 error[E0596]: cannot borrow immutable item `y` as mutable
   --> $DIR/ex3-both-anon-regions-using-trait-objects.rs:11:3
    |
+LL | fn foo(x:Box<Fn(&u8, &u8)> , y: Vec<&u8>, z: &u8) {
+   |                              - help: consider changing this to be mutable: `mut y`
 LL |   y.push(z); //~ ERROR lifetime mismatch
    |   ^ cannot borrow as mutable
 
diff --git a/src/test/ui/lifetime-errors/liveness-assign-imm-local-notes.stderr b/src/test/ui/lifetime-errors/liveness-assign-imm-local-notes.stderr
index 43743118966..0620235371a 100644
--- a/src/test/ui/lifetime-errors/liveness-assign-imm-local-notes.stderr
+++ b/src/test/ui/lifetime-errors/liveness-assign-imm-local-notes.stderr
@@ -32,6 +32,9 @@ LL |             x = 2;      //~ ERROR (Ast) [E0384]
 error[E0384]: cannot assign twice to immutable variable `x` (Mir)
   --> $DIR/liveness-assign-imm-local-notes.rs:23:9
    |
+LL |     let x;
+   |         - consider changing this to `mut x`
+...
 LL |         x = 2;
    |         ----- first assignment to `x`
 LL |         x = 3;      //~ ERROR (Ast) [E0384]
@@ -40,6 +43,9 @@ LL |         x = 3;      //~ ERROR (Ast) [E0384]
 error[E0384]: cannot assign twice to immutable variable `x` (Mir)
   --> $DIR/liveness-assign-imm-local-notes.rs:35:13
    |
+LL |         let x;
+   |             - consider changing this to `mut x`
+...
 LL |             x = 2;
    |             ----- first assignment to `x`
 LL |             x = 3;      //~ ERROR (Ast) [E0384]
@@ -48,12 +54,18 @@ LL |             x = 3;      //~ ERROR (Ast) [E0384]
 error[E0384]: cannot assign twice to immutable variable `x` (Mir)
   --> $DIR/liveness-assign-imm-local-notes.rs:45:13
    |
+LL |     let x;
+   |         - consider changing this to `mut x`
+...
 LL |             x = 1;      //~ ERROR (Ast) [E0384]
    |             ^^^^^ cannot assign twice to immutable variable
 
 error[E0384]: cannot assign twice to immutable variable `x` (Mir)
   --> $DIR/liveness-assign-imm-local-notes.rs:48:13
    |
+LL |     let x;
+   |         - consider changing this to `mut x`
+...
 LL |             x = 1;      //~ ERROR (Ast) [E0384]
    |             ----- first assignment to `x`
 ...
diff --git a/src/test/ui/macros/span-covering-argument-1.nll.stderr b/src/test/ui/macros/span-covering-argument-1.nll.stderr
index a12baab4159..213eddee720 100644
--- a/src/test/ui/macros/span-covering-argument-1.nll.stderr
+++ b/src/test/ui/macros/span-covering-argument-1.nll.stderr
@@ -1,6 +1,8 @@
 error[E0596]: cannot borrow immutable item `foo` as mutable
   --> $DIR/span-covering-argument-1.rs:15:14
    |
+LL |             let $s = 0;
+   |                 -- help: consider changing this to be mutable: `mut foo`
 LL |             *&mut $s = 0;
    |              ^^^^^^^ cannot borrow as mutable
 ...
diff --git a/src/test/ui/nll/issue-47388.stderr b/src/test/ui/nll/issue-47388.stderr
index f3952c49a2a..96e9f155414 100644
--- a/src/test/ui/nll/issue-47388.stderr
+++ b/src/test/ui/nll/issue-47388.stderr
@@ -1,4 +1,4 @@
-error[E0594]: cannot assign to data in a `&` reference
+error[E0594]: cannot assign to `fancy_ref.num` which is behind a `&` reference
   --> $DIR/issue-47388.rs:18:5
    |
 LL |     let fancy_ref = &(&mut fancy);
diff --git a/src/test/ui/reassign-ref-mut.rs b/src/test/ui/reassign-ref-mut.rs
new file mode 100644
index 00000000000..c045720a840
--- /dev/null
+++ b/src/test/ui/reassign-ref-mut.rs
@@ -0,0 +1,26 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Tests how we behave when the user attempts to mutate an immutable
+// binding that was introduced by either `ref` or `ref mut`
+// patterns.
+//
+// Such bindings cannot be made mutable via the mere addition of the
+// `mut` keyword, and thus we want to check that the compiler does not
+// suggest doing so.
+
+fn main() {
+    let (mut one_two, mut three_four) = ((1, 2), (3, 4));
+    let &mut (ref a, ref mut b) = &mut one_two;
+    a = &three_four.0;
+    //~^ ERROR cannot assign twice to immutable variable `a` [E0384]
+    b = &mut three_four.1;
+    //~^ ERROR cannot assign twice to immutable variable `b` [E0384]
+}
diff --git a/src/test/ui/reassign-ref-mut.stderr b/src/test/ui/reassign-ref-mut.stderr
new file mode 100644
index 00000000000..bf627dde720
--- /dev/null
+++ b/src/test/ui/reassign-ref-mut.stderr
@@ -0,0 +1,20 @@
+error[E0384]: cannot assign twice to immutable variable `a`
+  --> $DIR/reassign-ref-mut.rs:22:5
+   |
+LL |     let &mut (ref a, ref mut b) = &mut one_two;
+   |               ----- first assignment to `a`
+LL |     a = &three_four.0;
+   |     ^^^^^^^^^^^^^^^^^ cannot assign twice to immutable variable
+
+error[E0384]: cannot assign twice to immutable variable `b`
+  --> $DIR/reassign-ref-mut.rs:24:5
+   |
+LL |     let &mut (ref a, ref mut b) = &mut one_two;
+   |                      --------- first assignment to `b`
+...
+LL |     b = &mut three_four.1;
+   |     ^^^^^^^^^^^^^^^^^^^^^ cannot assign twice to immutable variable
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0384`.
diff --git a/src/test/ui/rfc-2005-default-binding-mode/enum.nll.stderr b/src/test/ui/rfc-2005-default-binding-mode/enum.nll.stderr
index b97bdeea409..8aa7e8a417c 100644
--- a/src/test/ui/rfc-2005-default-binding-mode/enum.nll.stderr
+++ b/src/test/ui/rfc-2005-default-binding-mode/enum.nll.stderr
@@ -1,26 +1,20 @@
-error[E0594]: cannot assign to data in a `&` reference
+error[E0594]: cannot assign to `*x` which is behind a `&` reference
   --> $DIR/enum.rs:19:5
    |
-LL |     let Wrap(x) = &Wrap(3);
-   |              - help: consider changing this to be a mutable reference: `&mut`
 LL |     *x += 1; //~ ERROR cannot assign to immutable
-   |     ^^^^^^^
+   |     ^^^^^^^ cannot assign
 
-error[E0594]: cannot assign to data in a `&` reference
+error[E0594]: cannot assign to `*x` which is behind a `&` reference
   --> $DIR/enum.rs:23:9
    |
-LL |     if let Some(x) = &Some(3) {
-   |                 - help: consider changing this to be a mutable reference: `&mut`
 LL |         *x += 1; //~ ERROR cannot assign to immutable
-   |         ^^^^^^^
+   |         ^^^^^^^ cannot assign
 
-error[E0594]: cannot assign to data in a `&` reference
+error[E0594]: cannot assign to `*x` which is behind a `&` reference
   --> $DIR/enum.rs:29:9
    |
-LL |     while let Some(x) = &Some(3) {
-   |                    - help: consider changing this to be a mutable reference: `&mut`
 LL |         *x += 1; //~ ERROR cannot assign to immutable
-   |         ^^^^^^^
+   |         ^^^^^^^ cannot assign
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/rfc-2005-default-binding-mode/explicit-mut.nll.stderr b/src/test/ui/rfc-2005-default-binding-mode/explicit-mut.nll.stderr
index 3ee4dc07bb8..4e00dec7616 100644
--- a/src/test/ui/rfc-2005-default-binding-mode/explicit-mut.nll.stderr
+++ b/src/test/ui/rfc-2005-default-binding-mode/explicit-mut.nll.stderr
@@ -1,26 +1,20 @@
-error[E0594]: cannot assign to data in a `&` reference
+error[E0594]: cannot assign to `*n` which is behind a `&` reference
   --> $DIR/explicit-mut.rs:17:13
    |
-LL |         Some(n) => {
-   |              - help: consider changing this to be a mutable reference: `&mut`
 LL |             *n += 1; //~ ERROR cannot assign to immutable
-   |             ^^^^^^^
+   |             ^^^^^^^ cannot assign
 
-error[E0594]: cannot assign to data in a `&` reference
+error[E0594]: cannot assign to `*n` which is behind a `&` reference
   --> $DIR/explicit-mut.rs:25:13
    |
-LL |         Some(n) => {
-   |              - help: consider changing this to be a mutable reference: `&mut`
 LL |             *n += 1; //~ ERROR cannot assign to immutable
-   |             ^^^^^^^
+   |             ^^^^^^^ cannot assign
 
-error[E0594]: cannot assign to data in a `&` reference
+error[E0594]: cannot assign to `*n` which is behind a `&` reference
   --> $DIR/explicit-mut.rs:33:13
    |
-LL |         Some(n) => {
-   |              - help: consider changing this to be a mutable reference: `&mut`
 LL |             *n += 1; //~ ERROR cannot assign to immutable
-   |             ^^^^^^^
+   |             ^^^^^^^ cannot assign
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/span/borrowck-borrow-overloaded-auto-deref-mut.nll.stderr b/src/test/ui/span/borrowck-borrow-overloaded-auto-deref-mut.nll.stderr
index 172828b9a40..3282fbba6c5 100644
--- a/src/test/ui/span/borrowck-borrow-overloaded-auto-deref-mut.nll.stderr
+++ b/src/test/ui/span/borrowck-borrow-overloaded-auto-deref-mut.nll.stderr
@@ -1,50 +1,66 @@
 error[E0596]: cannot borrow immutable item `x` as mutable
   --> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:63:24
    |
+LL | fn deref_mut_field1(x: Own<Point>) {
+   |                     - help: consider changing this to be mutable: `mut x`
 LL |     let __isize = &mut x.y; //~ ERROR cannot borrow
    |                        ^ cannot borrow as mutable
 
 error[E0596]: cannot borrow immutable item `*x` as mutable
   --> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:75:10
    |
+LL | fn deref_extend_mut_field1(x: &Own<Point>) -> &mut isize {
+   |                               ----------- help: consider changing this to be a mutable reference: `&mut Own<Point>`
 LL |     &mut x.y //~ ERROR cannot borrow
-   |          ^ cannot borrow as mutable
+   |          ^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error[E0596]: cannot borrow immutable item `x` as mutable
   --> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:98:5
    |
+LL | fn assign_field1<'a>(x: Own<Point>) {
+   |                      - help: consider changing this to be mutable: `mut x`
 LL |     x.y = 3; //~ ERROR cannot borrow
    |     ^ cannot borrow as mutable
 
 error[E0596]: cannot borrow immutable item `*x` as mutable
   --> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:102:5
    |
+LL | fn assign_field2<'a>(x: &'a Own<Point>) {
+   |                         -------------- help: consider changing this to be a mutable reference: `&mut Own<Point>`
 LL |     x.y = 3; //~ ERROR cannot borrow
-   |     ^ cannot borrow as mutable
+   |     ^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error[E0596]: cannot borrow immutable item `x` as mutable
   --> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:119:5
    |
+LL | fn deref_mut_method1(x: Own<Point>) {
+   |                      - help: consider changing this to be mutable: `mut x`
 LL |     x.set(0, 0); //~ ERROR cannot borrow
    |     ^ cannot borrow as mutable
 
 error[E0596]: cannot borrow immutable item `*x` as mutable
   --> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:131:5
    |
+LL | fn deref_extend_mut_method1(x: &Own<Point>) -> &mut isize {
+   |                                ----------- help: consider changing this to be a mutable reference: `&mut Own<Point>`
 LL |     x.y_mut() //~ ERROR cannot borrow
-   |     ^ cannot borrow as mutable
+   |     ^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error[E0596]: cannot borrow immutable item `x` as mutable
   --> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:139:6
    |
+LL | fn assign_method1<'a>(x: Own<Point>) {
+   |                       - help: consider changing this to be mutable: `mut x`
 LL |     *x.y_mut() = 3; //~ ERROR cannot borrow
    |      ^ cannot borrow as mutable
 
 error[E0596]: cannot borrow immutable item `*x` as mutable
   --> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:143:6
    |
+LL | fn assign_method2<'a>(x: &'a Own<Point>) {
+   |                          -------------- help: consider changing this to be a mutable reference: `&mut Own<Point>`
 LL |     *x.y_mut() = 3; //~ ERROR cannot borrow
-   |      ^ cannot borrow as mutable
+   |      ^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error: aborting due to 8 previous errors
 
diff --git a/src/test/ui/span/borrowck-borrow-overloaded-deref-mut.nll.stderr b/src/test/ui/span/borrowck-borrow-overloaded-deref-mut.nll.stderr
index 24abe85de76..0b1bfd8cee6 100644
--- a/src/test/ui/span/borrowck-borrow-overloaded-deref-mut.nll.stderr
+++ b/src/test/ui/span/borrowck-borrow-overloaded-deref-mut.nll.stderr
@@ -1,26 +1,34 @@
 error[E0596]: cannot borrow immutable item `x` as mutable
   --> $DIR/borrowck-borrow-overloaded-deref-mut.rs:39:25
    |
+LL | fn deref_mut1(x: Own<isize>) {
+   |               - help: consider changing this to be mutable: `mut x`
 LL |     let __isize = &mut *x; //~ ERROR cannot borrow
    |                         ^ cannot borrow as mutable
 
 error[E0596]: cannot borrow immutable item `*x` as mutable
   --> $DIR/borrowck-borrow-overloaded-deref-mut.rs:51:11
    |
+LL | fn deref_extend_mut1<'a>(x: &'a Own<isize>) -> &'a mut isize {
+   |                             -------------- help: consider changing this to be a mutable reference: `&mut Own<isize>`
 LL |     &mut **x //~ ERROR cannot borrow
-   |           ^^ cannot borrow as mutable
+   |           ^^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error[E0596]: cannot borrow immutable item `x` as mutable
   --> $DIR/borrowck-borrow-overloaded-deref-mut.rs:59:6
    |
+LL | fn assign1<'a>(x: Own<isize>) {
+   |                - help: consider changing this to be mutable: `mut x`
 LL |     *x = 3; //~ ERROR cannot borrow
    |      ^ cannot borrow as mutable
 
 error[E0596]: cannot borrow immutable item `*x` as mutable
   --> $DIR/borrowck-borrow-overloaded-deref-mut.rs:63:6
    |
+LL | fn assign2<'a>(x: &'a Own<isize>) {
+   |                   -------------- help: consider changing this to be a mutable reference: `&mut Own<isize>`
 LL |     **x = 3; //~ ERROR cannot borrow
-   |      ^^ cannot borrow as mutable
+   |      ^^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/span/borrowck-call-is-borrow-issue-12224.nll.stderr b/src/test/ui/span/borrowck-call-is-borrow-issue-12224.nll.stderr
index 26e9ea4dc0b..c4bdef21de4 100644
--- a/src/test/ui/span/borrowck-call-is-borrow-issue-12224.nll.stderr
+++ b/src/test/ui/span/borrowck-call-is-borrow-issue-12224.nll.stderr
@@ -15,16 +15,18 @@ LL | |     }));
 error[E0596]: cannot borrow immutable item `*f` as mutable
   --> $DIR/borrowck-call-is-borrow-issue-12224.rs:35:5
    |
+LL | fn test2<F>(f: &F) where F: FnMut() {
+   |                -- help: consider changing this to be a mutable reference: `&mut F`
 LL |     (*f)();
-   |     ^^^^ cannot borrow as mutable
+   |     ^^^^ `f` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error[E0596]: cannot borrow immutable item `*f.f` as mutable
   --> $DIR/borrowck-call-is-borrow-issue-12224.rs:44:5
    |
+LL | fn test4(f: &Test) {
+   |             ----- help: consider changing this to be a mutable reference: `&mut Test<'_>`
 LL |     f.f.call_mut(())
-   |     ^^^ cannot borrow as mutable
-   |
-   = note: the value which is causing this path not to be mutable is...: `*f`
+   |     ^^^ `f` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error[E0507]: cannot move out of borrowed content
   --> $DIR/borrowck-call-is-borrow-issue-12224.rs:66:13
diff --git a/src/test/ui/span/borrowck-call-method-from-mut-aliasable.nll.stderr b/src/test/ui/span/borrowck-call-method-from-mut-aliasable.nll.stderr
index 43934bf4aee..0bc614589e3 100644
--- a/src/test/ui/span/borrowck-call-method-from-mut-aliasable.nll.stderr
+++ b/src/test/ui/span/borrowck-call-method-from-mut-aliasable.nll.stderr
@@ -1,8 +1,11 @@
 error[E0596]: cannot borrow immutable item `*x` as mutable
   --> $DIR/borrowck-call-method-from-mut-aliasable.rs:27:5
    |
+LL | fn b(x: &Foo) {
+   |         ---- help: consider changing this to be a mutable reference: `&mut Foo`
+LL |     x.f();
 LL |     x.h(); //~ ERROR cannot borrow
-   |     ^ cannot borrow as mutable
+   |     ^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/span/borrowck-fn-in-const-b.nll.stderr b/src/test/ui/span/borrowck-fn-in-const-b.nll.stderr
index d3c6fd66599..1cb2c92833c 100644
--- a/src/test/ui/span/borrowck-fn-in-const-b.nll.stderr
+++ b/src/test/ui/span/borrowck-fn-in-const-b.nll.stderr
@@ -1,8 +1,10 @@
 error[E0596]: cannot borrow immutable item `*x` as mutable
   --> $DIR/borrowck-fn-in-const-b.rs:17:9
    |
+LL |     fn broken(x: &Vec<String>) {
+   |                  ------------ help: consider changing this to be a mutable reference: `&mut std::vec::Vec<std::string::String>`
 LL |         x.push(format!("this is broken"));
-   |         ^ cannot borrow as mutable
+   |         ^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/span/borrowck-object-mutability.nll.stderr b/src/test/ui/span/borrowck-object-mutability.nll.stderr
index 9b5e084bd37..cf615eed556 100644
--- a/src/test/ui/span/borrowck-object-mutability.nll.stderr
+++ b/src/test/ui/span/borrowck-object-mutability.nll.stderr
@@ -1,16 +1,20 @@
 error[E0596]: cannot borrow immutable item `*x` as mutable
   --> $DIR/borrowck-object-mutability.rs:19:5
    |
+LL | fn borrowed_receiver(x: &Foo) {
+   |                         ---- help: consider changing this to be a mutable reference: `&mut Foo`
+LL |     x.borrowed();
 LL |     x.borrowed_mut(); //~ ERROR cannot borrow
-   |     ^ cannot borrow as mutable
+   |     ^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error[E0596]: cannot borrow immutable item `*x` as mutable
   --> $DIR/borrowck-object-mutability.rs:29:5
    |
+LL | fn owned_receiver(x: Box<Foo>) {
+   |                   - help: consider changing this to be mutable: `mut x`
+LL |     x.borrowed();
 LL |     x.borrowed_mut(); //~ ERROR cannot borrow
    |     ^ cannot borrow as mutable
-   |
-   = note: the value which is causing this path not to be mutable is...: `x`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/span/mut-arg-hint.nll.stderr b/src/test/ui/span/mut-arg-hint.nll.stderr
index 8e1cb9720e2..cd19059fdae 100644
--- a/src/test/ui/span/mut-arg-hint.nll.stderr
+++ b/src/test/ui/span/mut-arg-hint.nll.stderr
@@ -1,20 +1,26 @@
 error[E0596]: cannot borrow immutable item `*a` as mutable
   --> $DIR/mut-arg-hint.rs:13:9
    |
+LL |     fn foo(mut a: &String) {
+   |                   ------- help: consider changing this to be a mutable reference: `&mut std::string::String`
 LL |         a.push_str("bar"); //~ ERROR cannot borrow immutable borrowed content
-   |         ^ cannot borrow as mutable
+   |         ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error[E0596]: cannot borrow immutable item `*a` as mutable
   --> $DIR/mut-arg-hint.rs:18:5
    |
+LL | pub fn foo<'a>(mut a: &'a String) {
+   |                       ---------- help: consider changing this to be a mutable reference: `&mut std::string::String`
 LL |     a.push_str("foo"); //~ ERROR cannot borrow immutable borrowed content
-   |     ^ cannot borrow as mutable
+   |     ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error[E0596]: cannot borrow immutable item `*a` as mutable
   --> $DIR/mut-arg-hint.rs:25:9
    |
+LL |     pub fn foo(mut a: &String) {
+   |                       ------- help: consider changing this to be a mutable reference: `&mut std::string::String`
 LL |         a.push_str("foo"); //~ ERROR cannot borrow immutable borrowed content
-   |         ^ cannot borrow as mutable
+   |         ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/suggestions/closure-immutable-outer-variable.nll.stderr b/src/test/ui/suggestions/closure-immutable-outer-variable.nll.stderr
index bc655114c2b..335ccefe8a0 100644
--- a/src/test/ui/suggestions/closure-immutable-outer-variable.nll.stderr
+++ b/src/test/ui/suggestions/closure-immutable-outer-variable.nll.stderr
@@ -2,7 +2,7 @@ error[E0594]: cannot assign to immutable item `y`
   --> $DIR/closure-immutable-outer-variable.rs:21:26
    |
 LL |     foo(Box::new(move || y = false) as Box<_>); //~ ERROR cannot assign to captured outer variable
-   |                          ^^^^^^^^^ cannot mutate
+   |                          ^^^^^^^^^ cannot assign
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/suggestions/fn-closure-mutable-capture.nll.stderr b/src/test/ui/suggestions/fn-closure-mutable-capture.nll.stderr
index ed691843f9b..7ef21d3720d 100644
--- a/src/test/ui/suggestions/fn-closure-mutable-capture.nll.stderr
+++ b/src/test/ui/suggestions/fn-closure-mutable-capture.nll.stderr
@@ -1,8 +1,8 @@
-error[E0594]: cannot assign to immutable item `x`
+error[E0594]: cannot assign to `x` which is behind a `&` reference
   --> $DIR/fn-closure-mutable-capture.rs:15:17
    |
 LL |     bar(move || x = 1);
-   |                 ^^^^^ cannot mutate
+   |                 ^^^^^ cannot assign
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/trivial-bounds-inconsistent-copy-reborrow.nll.stderr b/src/test/ui/trivial-bounds-inconsistent-copy-reborrow.nll.stderr
index 66547863db2..1a36b0c4839 100644
--- a/src/test/ui/trivial-bounds-inconsistent-copy-reborrow.nll.stderr
+++ b/src/test/ui/trivial-bounds-inconsistent-copy-reborrow.nll.stderr
@@ -1,18 +1,18 @@
 error[E0596]: cannot borrow immutable item `**t` as mutable
   --> $DIR/trivial-bounds-inconsistent-copy-reborrow.rs:16:5
    |
+LL | fn reborrow_mut<'a>(t: &'a &'a mut i32) -> &'a mut i32 where &'a mut i32: Copy {
+   |                        --------------- help: consider changing this to be a mutable reference: `&mut &mut i32`
 LL |     *t //~ ERROR
-   |     ^^ cannot borrow as mutable
-   |
-   = note: the value which is causing this path not to be mutable is...: `*t`
+   |     ^^ `t` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error[E0596]: cannot borrow immutable item `**t` as mutable
   --> $DIR/trivial-bounds-inconsistent-copy-reborrow.rs:20:6
    |
+LL | fn copy_reborrow_mut<'a>(t: &'a &'a mut i32) -> &'a mut i32 where &'a mut i32: Copy {
+   |                             --------------- help: consider changing this to be a mutable reference: `&mut &mut i32`
 LL |     {*t} //~ ERROR
-   |      ^^ cannot borrow as mutable
-   |
-   = note: the value which is causing this path not to be mutable is...: `*t`
+   |      ^^ `t` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error: aborting due to 2 previous errors