about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-06-29 12:40:12 +0000
committerbors <bors@rust-lang.org>2018-06-29 12:40:12 +0000
commitab8a67c12adc6ba84b6143a539640d5962ef6cbe (patch)
tree369362bb7a54b0f6e4794cedb6062f9e7a3f740f
parent6e5e63a48c3be7de2faf914c2a9e194ff7cede9e (diff)
parent2cb0a0631a640318ae288ead20758db8508f6835 (diff)
downloadrust-ab8a67c12adc6ba84b6143a539640d5962ef6cbe.tar.gz
rust-ab8a67c12adc6ba84b6143a539640d5962ef6cbe.zip
Auto merge of #51729 - matthewjasper:move-errors, r=nikomatsakis
[NLL] Better move errors

Make a number of changes to improve the quality of NLL cannot move errors.

* Group errors that occur in the same `match` with the same cause.
* Suggest `ref`, `&` or removing `*` to avoid the move.
* Show the place being matched on.

Differences from AST borrowck:

* `&` is suggested over `ref` when matching on a place that can't be moved from.
* Removing `*` is suggested instead of adding `&` when applicable.
* Sub-pattern spans aren't used, this would probably need Spans on Places.

Closes #45699
Closes #46627
Closes #51187
Closes #51189

r? @pnkfelix
-rw-r--r--src/librustc/mir/mod.rs48
-rw-r--r--src/librustc_mir/borrow_check/mod.rs37
-rw-r--r--src/librustc_mir/borrow_check/move_errors.rs388
-rw-r--r--src/librustc_mir/build/block.rs17
-rw-r--r--src/librustc_mir/build/expr/as_temp.rs19
-rw-r--r--src/librustc_mir/build/expr/into.rs39
-rw-r--r--src/librustc_mir/build/expr/mod.rs2
-rw-r--r--src/librustc_mir/build/matches/mod.rs37
-rw-r--r--src/librustc_mir/build/mod.rs11
-rw-r--r--src/librustc_mir/dataflow/move_paths/builder.rs13
-rw-r--r--src/librustc_mir/dataflow/move_paths/mod.rs8
-rw-r--r--src/librustc_mir/hair/mod.rs9
-rw-r--r--src/test/compile-fail/borrowck/borrowck-move-out-of-struct-with-dtor.rs2
-rw-r--r--src/test/mir-opt/combine_array_len.rs8
-rw-r--r--src/test/mir-opt/copy_propagation.rs9
-rw-r--r--src/test/mir-opt/copy_propagation_arg.rs10
-rw-r--r--src/test/mir-opt/end_region_5.rs6
-rw-r--r--src/test/mir-opt/end_region_6.rs6
-rw-r--r--src/test/mir-opt/end_region_7.rs6
-rw-r--r--src/test/mir-opt/end_region_8.rs6
-rw-r--r--src/test/mir-opt/end_region_destruction_extents_1.rs100
-rw-r--r--src/test/mir-opt/inline-closure-borrows-arg.rs8
-rw-r--r--src/test/mir-opt/inline-closure.rs2
-rw-r--r--src/test/mir-opt/lower_128bit_debug_test.rs16
-rw-r--r--src/test/mir-opt/lower_128bit_test.rs16
-rw-r--r--src/test/mir-opt/nll/region-subtyping-basic.rs10
-rw-r--r--src/test/mir-opt/validate_1.rs5
-rw-r--r--src/test/ui/E0508.ast.nll.stderr9
-rw-r--r--src/test/ui/E0508.mir.stderr9
-rw-r--r--src/test/ui/E0508.rs6
-rw-r--r--src/test/ui/E0508.stderr (renamed from src/test/ui/E0508.ast.stderr)4
-rw-r--r--src/test/ui/borrowck/borrowck-move-error-with-note.nll.stderr52
-rw-r--r--src/test/ui/borrowck/borrowck-move-out-of-vec-tail.nll.stderr20
-rw-r--r--src/test/ui/borrowck/borrowck-vec-pattern-nesting.nll.stderr56
-rw-r--r--src/test/ui/borrowck/issue-51415.nll.stderr7
-rw-r--r--src/test/ui/codemap_tests/overlapping_spans.nll.stderr6
-rw-r--r--src/test/ui/error-codes/E0509.nll.stderr9
-rw-r--r--src/test/ui/issue-40402-ref-hints/issue-40402-1.nll.stderr5
-rw-r--r--src/test/ui/issue-40402-ref-hints/issue-40402-2.nll.stderr17
-rw-r--r--src/test/ui/moves-based-on-type-block-bad.nll.stderr10
-rw-r--r--src/test/ui/nll/move-errors.rs130
-rw-r--r--src/test/ui/nll/move-errors.stderr140
42 files changed, 997 insertions, 321 deletions
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index 2198e3f6b31..dec37bdf719 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -497,8 +497,8 @@ pub enum LocalKind {
     ReturnPointer,
 }
 
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
-pub struct VarBindingForm {
+#[derive(Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
+pub struct VarBindingForm<'tcx> {
     /// 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,
@@ -508,21 +508,49 @@ pub struct VarBindingForm {
     /// 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>,
+    /// Place of the RHS of the =, or the subject of the `match` where this
+    /// variable is initialized. None in the case of `let PATTERN;`.
+    /// Some((None, ..)) in the case of and `let [mut] x = ...` because
+    /// (a) the right-hand side isn't evaluated as a place expression.
+    /// (b) it gives a way to separate this case from the remaining cases
+    ///     for diagnostics.
+    pub opt_match_place: Option<(Option<Place<'tcx>>, Span)>,
 }
 
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
-pub enum BindingForm {
+#[derive(Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
+pub enum BindingForm<'tcx> {
     /// This is a binding for a non-`self` binding, or a `self` that has an explicit type.
-    Var(VarBindingForm),
+    Var(VarBindingForm<'tcx>),
     /// Binding for a `self`/`&self`/`&mut self` binding where the type is implicit.
     ImplicitSelf,
 }
 
-CloneTypeFoldableAndLiftImpls! { BindingForm, }
+CloneTypeFoldableAndLiftImpls! { BindingForm<'tcx>, }
 
-impl_stable_hash_for!(struct self::VarBindingForm { binding_mode, opt_ty_info });
+impl_stable_hash_for!(struct self::VarBindingForm<'tcx> {
+    binding_mode,
+    opt_ty_info,
+    opt_match_place
+});
+
+mod binding_form_impl {
+    use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult};
+    use ich::StableHashingContext;
 
-impl_stable_hash_for!(enum self::BindingForm { Var(binding), ImplicitSelf, });
+    impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for super::BindingForm<'tcx> {
+        fn hash_stable<W: StableHasherResult>(&self,
+                                            hcx: &mut StableHashingContext<'a>,
+                                            hasher: &mut StableHasher<W>) {
+            use super::BindingForm::*;
+            ::std::mem::discriminant(self).hash_stable(hcx, hasher);
+
+            match self {
+                Var(binding) => binding.hash_stable(hcx, hasher),
+                ImplicitSelf => (),
+            }
+        }
+    }
+}
 
 /// A MIR local.
 ///
@@ -542,7 +570,7 @@ pub struct LocalDecl<'tcx> {
     /// 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>>,
+    pub is_user_variable: Option<ClearCrossCrate<BindingForm<'tcx>>>,
 
     /// True if this is an internal local
     ///
@@ -670,6 +698,7 @@ impl<'tcx> LocalDecl<'tcx> {
             Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
                 binding_mode: ty::BindingMode::BindByValue(_),
                 opt_ty_info: _,
+                opt_match_place: _,
             }))) => true,
 
             // FIXME: might be able to thread the distinction between
@@ -688,6 +717,7 @@ impl<'tcx> LocalDecl<'tcx> {
             Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
                 binding_mode: ty::BindingMode::BindByValue(_),
                 opt_ty_info: _,
+                opt_match_place: _,
             }))) => true,
 
             Some(ClearCrossCrate::Set(BindingForm::ImplicitSelf)) => true,
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index 7b7568cfcfc..ea303cd4e6c 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -35,7 +35,6 @@ use syntax_pos::Span;
 
 use dataflow::indexes::BorrowIndex;
 use dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MovePathIndex};
-use dataflow::move_paths::{IllegalMoveOriginKind, MoveError};
 use dataflow::Borrows;
 use dataflow::DataflowResultsConsumer;
 use dataflow::FlowAtLocation;
@@ -62,6 +61,7 @@ mod path_utils;
 crate mod place_ext;
 mod prefixes;
 mod used_muts;
+mod move_errors;
 
 pub(crate) mod nll;
 
@@ -147,40 +147,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
     let move_data: MoveData<'tcx> = match MoveData::gather_moves(mir, tcx) {
         Ok(move_data) => move_data,
         Err((move_data, move_errors)) => {
-            for move_error in move_errors {
-                let (span, kind): (Span, IllegalMoveOriginKind) = match move_error {
-                    MoveError::UnionMove { .. } => {
-                        unimplemented!("don't know how to report union move errors yet.")
-                    }
-                    MoveError::IllegalMove {
-                        cannot_move_out_of: o,
-                    } => (o.span, o.kind),
-                };
-                let origin = Origin::Mir;
-                let mut err = match kind {
-                    IllegalMoveOriginKind::Static => {
-                        tcx.cannot_move_out_of(span, "static item", origin)
-                    }
-                    IllegalMoveOriginKind::BorrowedContent { target_ty: ty } => {
-                        // Inspect the type of the content behind the
-                        // borrow to provide feedback about why this
-                        // was a move rather than a copy.
-                        match ty.sty {
-                            ty::TyArray(..) | ty::TySlice(..) => {
-                                tcx.cannot_move_out_of_interior_noncopy(span, ty, None, origin)
-                            }
-                            _ => tcx.cannot_move_out_of(span, "borrowed content", origin),
-                        }
-                    }
-                    IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
-                        tcx.cannot_move_out_of_interior_of_drop(span, ty, origin)
-                    }
-                    IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => {
-                        tcx.cannot_move_out_of_interior_noncopy(span, ty, Some(is_index), origin)
-                    }
-                };
-                err.emit();
-            }
+            move_errors::report_move_errors(&mir, tcx, move_errors, &move_data);
             move_data
         }
     };
diff --git a/src/librustc_mir/borrow_check/move_errors.rs b/src/librustc_mir/borrow_check/move_errors.rs
new file mode 100644
index 00000000000..bc68708decb
--- /dev/null
+++ b/src/librustc_mir/borrow_check/move_errors.rs
@@ -0,0 +1,388 @@
+// Copyright 2018 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.
+
+use rustc::hir;
+use rustc::mir::*;
+use rustc::ty::{self, TyCtxt};
+use rustc_errors::DiagnosticBuilder;
+use syntax_pos::Span;
+
+use dataflow::move_paths::{IllegalMoveOrigin, IllegalMoveOriginKind, MoveData};
+use dataflow::move_paths::{LookupResult, MoveError, MovePathIndex};
+use util::borrowck_errors::{BorrowckErrors, Origin};
+
+pub(crate) fn report_move_errors<'gcx, 'tcx>(
+    mir: &Mir<'tcx>,
+    tcx: TyCtxt<'_, 'gcx, 'tcx>,
+    move_errors: Vec<MoveError<'tcx>>,
+    move_data: &MoveData<'tcx>,
+) {
+    MoveErrorCtxt {
+        mir,
+        tcx,
+        move_data,
+    }.report_errors(move_errors);
+}
+
+#[derive(Copy, Clone)]
+struct MoveErrorCtxt<'a, 'gcx: 'tcx, 'tcx: 'a> {
+    mir: &'a Mir<'tcx>,
+    tcx: TyCtxt<'a, 'gcx, 'tcx>,
+    move_data: &'a MoveData<'tcx>,
+}
+
+// Often when desugaring a pattern match we may have many individual moves in
+// MIR that are all part of one operation from the user's point-of-view. For
+// example:
+//
+// let (x, y) = foo()
+//
+// would move x from the 0 field of some temporary, and y from the 1 field. We
+// group such errors together for cleaner error reporting.
+//
+// Errors are kept separate if they are from places with different parent move
+// paths. For example, this generates two errors:
+//
+// let (&x, &y) = (&String::new(), &String::new());
+#[derive(Debug)]
+enum GroupedMoveError<'tcx> {
+    // Match place can't be moved from
+    // e.g. match x[0] { s => (), } where x: &[String]
+    MovesFromMatchPlace {
+        span: Span,
+        move_from: Place<'tcx>,
+        kind: IllegalMoveOriginKind<'tcx>,
+        binds_to: Vec<Local>,
+    },
+    // Part of a pattern can't be moved from,
+    // e.g. match &String::new() { &x => (), }
+    MovesFromPattern {
+        span: Span,
+        move_from: MovePathIndex,
+        kind: IllegalMoveOriginKind<'tcx>,
+        binds_to: Vec<Local>,
+    },
+    // Everything that isn't from pattern matching.
+    OtherIllegalMove {
+        span: Span,
+        kind: IllegalMoveOriginKind<'tcx>,
+    },
+}
+
+impl<'a, 'gcx, 'tcx> MoveErrorCtxt<'a, 'gcx, 'tcx> {
+    fn report_errors(self, move_errors: Vec<MoveError<'tcx>>) {
+        let grouped_errors = self.group_move_errors(move_errors);
+        for error in grouped_errors {
+            self.report(error);
+        }
+    }
+
+    fn group_move_errors(self, errors: Vec<MoveError<'tcx>>) -> Vec<GroupedMoveError<'tcx>> {
+        let mut grouped_errors = Vec::new();
+        for error in errors {
+            self.append_to_grouped_errors(&mut grouped_errors, error);
+        }
+        grouped_errors
+    }
+
+    fn append_to_grouped_errors(
+        self,
+        grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
+        error: MoveError<'tcx>,
+    ) {
+        match error {
+            MoveError::UnionMove { .. } => {
+                unimplemented!("don't know how to report union move errors yet.")
+            }
+            MoveError::IllegalMove {
+                cannot_move_out_of: IllegalMoveOrigin { location, kind },
+            } => {
+                let stmt_source_info = self.mir.source_info(location);
+                if let Some(StatementKind::Assign(
+                    Place::Local(local),
+                    Rvalue::Use(Operand::Move(move_from)),
+                )) = self.mir.basic_blocks()[location.block]
+                    .statements
+                    .get(location.statement_index)
+                    .map(|stmt| &stmt.kind)
+                {
+                    let local_decl = &self.mir.local_decls[*local];
+                    if let Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
+                        opt_match_place: Some((ref opt_match_place, match_span)),
+                        binding_mode: _,
+                        opt_ty_info: _,
+                    }))) = local_decl.is_user_variable
+                    {
+                        // opt_match_place is the
+                        // match_span is the span of the expression being matched on
+                        // match *x.y { ... }        match_place is Some(*x.y)
+                        //       ^^^^                match_span is the span of *x.y
+                        // opt_match_place is None for let [mut] x = ... statements,
+                        // whether or not the right-hand side is a place expression
+
+                        // HACK use scopes to determine if this assignment is
+                        // the initialization of a variable.
+                        // FIXME(matthewjasper) This would probably be more
+                        // reliable if it used the ever initialized dataflow
+                        // but move errors are currently reported before the
+                        // rest of borrowck has run.
+                        if self
+                            .mir
+                            .is_sub_scope(local_decl.source_info.scope, stmt_source_info.scope)
+                        {
+                            self.append_binding_error(
+                                grouped_errors,
+                                kind,
+                                move_from,
+                                *local,
+                                opt_match_place,
+                                match_span,
+                            );
+                        }
+                        return;
+                    }
+                }
+                grouped_errors.push(GroupedMoveError::OtherIllegalMove {
+                    span: stmt_source_info.span,
+                    kind,
+                });
+            }
+        }
+    }
+
+    fn append_binding_error(
+        self,
+        grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
+        kind: IllegalMoveOriginKind<'tcx>,
+        move_from: &Place<'tcx>,
+        bind_to: Local,
+        match_place: &Option<Place<'tcx>>,
+        match_span: Span,
+    ) {
+        debug!(
+            "append_to_grouped_errors(match_place={:?}, match_span={:?})",
+            match_place, match_span
+        );
+
+        let from_simple_let = match_place.is_none();
+        let match_place = match_place.as_ref().unwrap_or(move_from);
+
+        match self.move_data.rev_lookup.find(match_place) {
+            // Error with the match place
+            LookupResult::Parent(_) => {
+                for ge in &mut *grouped_errors {
+                    if let GroupedMoveError::MovesFromMatchPlace { span, binds_to, .. } = ge {
+                        if match_span == *span {
+                            debug!("appending local({:?}) to list", bind_to);
+                            if !binds_to.is_empty() {
+                                binds_to.push(bind_to);
+                            }
+                            return;
+                        }
+                    }
+                }
+                debug!("found a new move error location");
+
+                // Don't need to point to x in let x = ... .
+                let binds_to = if from_simple_let {
+                    vec![]
+                } else {
+                    vec![bind_to]
+                };
+                grouped_errors.push(GroupedMoveError::MovesFromMatchPlace {
+                    span: match_span,
+                    move_from: match_place.clone(),
+                    kind,
+                    binds_to,
+                });
+            }
+            // Error with the pattern
+            LookupResult::Exact(_) => {
+                let mpi = match self.move_data.rev_lookup.find(move_from) {
+                    LookupResult::Parent(Some(mpi)) => mpi,
+                    // move_from should be a projection from match_place.
+                    _ => unreachable!("Probably not unreachable..."),
+                };
+                for ge in &mut *grouped_errors {
+                    if let GroupedMoveError::MovesFromPattern {
+                        span,
+                        move_from: other_mpi,
+                        binds_to,
+                        ..
+                    } = ge
+                    {
+                        if match_span == *span && mpi == *other_mpi {
+                            debug!("appending local({:?}) to list", bind_to);
+                            binds_to.push(bind_to);
+                            return;
+                        }
+                    }
+                }
+                debug!("found a new move error location");
+                grouped_errors.push(GroupedMoveError::MovesFromPattern {
+                    span: match_span,
+                    move_from: mpi,
+                    kind,
+                    binds_to: vec![bind_to],
+                });
+            }
+        };
+    }
+
+    fn report(self, error: GroupedMoveError<'tcx>) {
+        let (mut err, err_span) = {
+            let (span, kind): (Span, &IllegalMoveOriginKind) = match error {
+                GroupedMoveError::MovesFromMatchPlace { span, ref kind, .. }
+                | GroupedMoveError::MovesFromPattern { span, ref kind, .. }
+                | GroupedMoveError::OtherIllegalMove { span, ref kind } => (span, kind),
+            };
+            let origin = Origin::Mir;
+            (
+                match kind {
+                    IllegalMoveOriginKind::Static => {
+                        self.tcx.cannot_move_out_of(span, "static item", origin)
+                    }
+                    IllegalMoveOriginKind::BorrowedContent { target_ty: ty } => {
+                        // Inspect the type of the content behind the
+                        // borrow to provide feedback about why this
+                        // was a move rather than a copy.
+                        match ty.sty {
+                            ty::TyArray(..) | ty::TySlice(..) => self
+                                .tcx
+                                .cannot_move_out_of_interior_noncopy(span, ty, None, origin),
+                            _ => self
+                                .tcx
+                                .cannot_move_out_of(span, "borrowed content", origin),
+                        }
+                    }
+                    IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
+                        self.tcx
+                            .cannot_move_out_of_interior_of_drop(span, ty, origin)
+                    }
+                    IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => self
+                        .tcx
+                        .cannot_move_out_of_interior_noncopy(span, ty, Some(*is_index), origin),
+                },
+                span,
+            )
+        };
+
+        self.add_move_hints(error, &mut err, err_span);
+        err.emit();
+    }
+
+    fn add_move_hints(
+        self,
+        error: GroupedMoveError<'tcx>,
+        err: &mut DiagnosticBuilder<'a>,
+        span: Span,
+    ) {
+        match error {
+            GroupedMoveError::MovesFromMatchPlace {
+                mut binds_to,
+                move_from,
+                ..
+            } => {
+                // Ok to suggest a borrow, since the target can't be moved from
+                // anyway.
+                if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) {
+                    match move_from {
+                        Place::Projection(ref proj)
+                            if self.suitable_to_remove_deref(proj, &snippet) =>
+                        {
+                            err.span_suggestion(
+                                span,
+                                "consider removing this dereference operator",
+                                format!("{}", &snippet[1..]),
+                            );
+                        }
+                        _ => {
+                            err.span_suggestion(
+                                span,
+                                "consider using a reference instead",
+                                format!("&{}", snippet),
+                            );
+                        }
+                    }
+
+                    binds_to.sort();
+                    binds_to.dedup();
+                    for local in binds_to {
+                        let bind_to = &self.mir.local_decls[local];
+                        let binding_span = bind_to.source_info.span;
+                        err.span_label(
+                            binding_span,
+                            format!(
+                                "move occurs because {} has type `{}`, \
+                                 which does not implement the `Copy` trait",
+                                bind_to.name.unwrap(),
+                                bind_to.ty
+                            ),
+                        );
+                    }
+                }
+            }
+            GroupedMoveError::MovesFromPattern { mut binds_to, .. } => {
+                // Suggest ref, since there might be a move in
+                // another match arm
+                binds_to.sort();
+                binds_to.dedup();
+                for local in binds_to {
+                    let bind_to = &self.mir.local_decls[local];
+                    let binding_span = bind_to.source_info.span;
+
+                    // Suggest ref mut when the user has already written mut.
+                    let ref_kind = match bind_to.mutability {
+                        Mutability::Not => "ref",
+                        Mutability::Mut => "ref mut",
+                    };
+                    match bind_to.name {
+                        Some(name) => {
+                            err.span_suggestion(
+                                binding_span,
+                                "to prevent move, use ref or ref mut",
+                                format!("{} {:?}", ref_kind, name),
+                            );
+                        }
+                        None => {
+                            err.span_label(
+                                span,
+                                format!("Local {:?} is not suitable for ref", bind_to),
+                            );
+                        }
+                    }
+                }
+            }
+            // Nothing to suggest.
+            GroupedMoveError::OtherIllegalMove { .. } => (),
+        }
+    }
+
+    fn suitable_to_remove_deref(self, proj: &PlaceProjection<'tcx>, snippet: &str) -> bool {
+        let is_shared_ref = |ty: ty::Ty| match ty.sty {
+            ty::TypeVariants::TyRef(.., hir::Mutability::MutImmutable) => true,
+            _ => false,
+        };
+
+        proj.elem == ProjectionElem::Deref && snippet.starts_with('*') && match proj.base {
+            Place::Local(local) => {
+                let local_decl = &self.mir.local_decls[local];
+                // If this is a temporary, then this could be from an
+                // overloaded * operator.
+                local_decl.is_user_variable.is_some() && is_shared_ref(local_decl.ty)
+            }
+            Place::Static(ref st) => is_shared_ref(st.ty),
+            Place::Projection(ref proj) => match proj.elem {
+                ProjectionElem::Field(_, ty) => is_shared_ref(ty),
+                _ => false,
+            },
+        }
+    }
+}
diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs
index 0fd55f752b8..bbbe757e96e 100644
--- a/src/librustc_mir/build/block.rs
+++ b/src/librustc_mir/build/block.rs
@@ -115,11 +115,21 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     // Declare the bindings, which may create a source scope.
                     let remainder_span = remainder_scope.span(this.hir.tcx(),
                                                               &this.hir.region_scope_tree);
-                    let scope = this.declare_bindings(None, remainder_span, lint_level, &pattern,
-                                                      ArmHasGuard(false));
+
+                    let scope;
 
                     // Evaluate the initializer, if present.
                     if let Some(init) = initializer {
+                        let initializer_span = init.span();
+
+                        scope = this.declare_bindings(
+                            None,
+                            remainder_span,
+                            lint_level,
+                            &pattern,
+                            ArmHasGuard(false),
+                            Some((None, initializer_span)),
+                        );
                         unpack!(block = this.in_opt_scope(
                             opt_destruction_scope.map(|de|(de, source_info)), block, |this| {
                                 let scope = (init_scope, source_info);
@@ -128,6 +138,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                                 })
                             }));
                     } else {
+                        scope = this.declare_bindings(None, remainder_span, lint_level, &pattern,
+                                                        ArmHasGuard(false), None);
+
                         // FIXME(#47184): We currently only insert `UserAssertTy` statements for
                         // patterns that are bindings, this is as we do not want to deconstruct
                         // the type being assertion to match the pattern.
diff --git a/src/librustc_mir/build/expr/as_temp.rs b/src/librustc_mir/build/expr/as_temp.rs
index 1fc608c52c6..d905b383316 100644
--- a/src/librustc_mir/build/expr/as_temp.rs
+++ b/src/librustc_mir/build/expr/as_temp.rs
@@ -11,7 +11,6 @@
 //! See docs in build/expr/mod.rs
 
 use build::{BlockAnd, BlockAndExtension, Builder};
-use build::expr::category::Category;
 use hair::*;
 use rustc::middle::region;
 use rustc::mir::*;
@@ -57,23 +56,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             });
         }
 
-        // Careful here not to cause an infinite cycle. If we always
-        // called `into`, then for places like `x.f`, it would
-        // eventually fallback to us, and we'd loop. There's a reason
-        // for this: `as_temp` is the point where we bridge the "by
-        // reference" semantics of `as_place` with the "by value"
-        // semantics of `into`, `as_operand`, `as_rvalue`, and (of
-        // course) `as_temp`.
-        match Category::of(&expr.kind).unwrap() {
-            Category::Place => {
-                let place = unpack!(block = this.as_place(block, expr));
-                let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
-                this.cfg.push_assign(block, source_info, &Place::Local(temp), rvalue);
-            }
-            _ => {
-                unpack!(block = this.into(&Place::Local(temp), block, expr));
-            }
-        }
+        unpack!(block = this.into(&Place::Local(temp), block, expr));
 
         // In constants, temp_lifetime is None. We should not need to drop
         // anything because no values with a destructor can be created in
diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs
index 94b387abe3c..59a7f49af80 100644
--- a/src/librustc_mir/build/expr/into.rs
+++ b/src/librustc_mir/build/expr/into.rs
@@ -288,6 +288,37 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 block.unit()
             }
 
+            // Avoid creating a temporary
+            ExprKind::VarRef { .. } |
+            ExprKind::SelfRef |
+            ExprKind::StaticRef { .. } => {
+                debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
+
+                let place = unpack!(block = this.as_place(block, expr));
+                let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
+                this.cfg.push_assign(block, source_info, destination, rvalue);
+                block.unit()
+            }
+            ExprKind::Index { .. } |
+            ExprKind::Deref { .. } |
+            ExprKind::Field { .. } => {
+                debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
+
+                // Create a "fake" temporary variable so that we check that the
+                // value is Sized. Usually, this is caught in type checking, but
+                // in the case of box expr there is no such check.
+                if let Place::Projection(..) = destination {
+                    this.local_decls.push(LocalDecl::new_temp(expr.ty, expr.span));
+                }
+
+                debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
+
+                let place = unpack!(block = this.as_place(block, expr));
+                let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
+                this.cfg.push_assign(block, source_info, destination, rvalue);
+                block.unit()
+            }
+
             // these are the cases that are more naturally handled by some other mode
             ExprKind::Unary { .. } |
             ExprKind::Binary { .. } |
@@ -300,18 +331,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             ExprKind::Unsize { .. } |
             ExprKind::Repeat { .. } |
             ExprKind::Borrow { .. } |
-            ExprKind::VarRef { .. } |
-            ExprKind::SelfRef |
-            ExprKind::StaticRef { .. } |
             ExprKind::Array { .. } |
             ExprKind::Tuple { .. } |
             ExprKind::Adt { .. } |
             ExprKind::Closure { .. } |
-            ExprKind::Index { .. } |
-            ExprKind::Deref { .. } |
             ExprKind::Literal { .. } |
-            ExprKind::Yield { .. } |
-            ExprKind::Field { .. } => {
+            ExprKind::Yield { .. } => {
                 debug_assert!(match Category::of(&expr.kind).unwrap() {
                     Category::Rvalue(RvalueFunc::Into) => false,
                     _ => true,
diff --git a/src/librustc_mir/build/expr/mod.rs b/src/librustc_mir/build/expr/mod.rs
index 0fd4b8e7e23..a63cf41f066 100644
--- a/src/librustc_mir/build/expr/mod.rs
+++ b/src/librustc_mir/build/expr/mod.rs
@@ -65,7 +65,7 @@
 //! which can fallback to `into`. So if one of the `ExprKind` variants is not, in fact,
 //! implemented in the category where it is supposed to be, there will be a problem.
 //!
-//! Of those fallbacks, the most interesting one is `as_temp`, because
+//! Of those fallbacks, the most interesting one is `into`, because
 //! it discriminates based on the category of the expression. This is
 //! basically the point where the "by value" operations are bridged
 //! over to the "by reference" mode (`as_place`).
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index b6af0ed2a4a..79dbdfefeb8 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -44,6 +44,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                       arms: Vec<Arm<'tcx>>)
                       -> BlockAnd<()> {
         let tcx = self.hir.tcx();
+        let discriminant_span = discriminant.span();
         let discriminant_place = unpack!(block = self.as_place(block, discriminant));
 
         // Matching on a `discriminant_place` with an uninhabited type doesn't
@@ -96,7 +97,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             let scope = self.declare_bindings(None, body.span,
                                               LintLevel::Inherited,
                                               &arm.patterns[0],
-                                              ArmHasGuard(arm.guard.is_some()));
+                                              ArmHasGuard(arm.guard.is_some()),
+                                              Some((Some(&discriminant_place), discriminant_span)));
             (body, scope.unwrap_or(self.source_scope))
         }).collect();
 
@@ -254,7 +256,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             }
             _ => {
                 let place = unpack!(block = self.as_place(block, initializer));
-                self.place_into_pattern(block, irrefutable_pat, &place)
+                self.place_into_pattern(block, irrefutable_pat, &place, true)
             }
         }
     }
@@ -262,7 +264,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     pub fn place_into_pattern(&mut self,
                                mut block: BasicBlock,
                                irrefutable_pat: Pattern<'tcx>,
-                               initializer: &Place<'tcx>)
+                               initializer: &Place<'tcx>,
+                               set_match_place: bool)
                                -> BlockAnd<()> {
         // create a dummy candidate
         let mut candidate = Candidate {
@@ -288,6 +291,25 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                       candidate.match_pairs);
         }
 
+        // for matches and function arguments, the place that is being matched
+        // can be set when creating the variables. But the place for
+        // let PATTERN = ... might not even exist until we do the assignment.
+        // so we set it here instead
+        if set_match_place {
+            for binding in &candidate.bindings {
+                let local = self.var_local_id(binding.var_id, OutsideGuard);
+
+                if let Some(ClearCrossCrate::Set(BindingForm::Var(
+                    VarBindingForm {opt_match_place: Some((ref mut match_place, _)), .. }
+                ))) = self.local_decls[local].is_user_variable
+                {
+                    *match_place = Some(initializer.clone());
+                } else {
+                    bug!("Let binding to non-user variable.")
+                }
+            }
+        }
+
         // now apply the bindings, which will also declare the variables
         self.bind_matched_candidate_for_arm_body(block, &candidate.bindings);
 
@@ -302,7 +324,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                             scope_span: Span,
                             lint_level: LintLevel,
                             pattern: &Pattern<'tcx>,
-                            has_guard: ArmHasGuard)
+                            has_guard: ArmHasGuard,
+                            opt_match_place: Option<(Option<&Place<'tcx>>, Span)>)
                             -> Option<SourceScope> {
         assert!(!(visibility_scope.is_some() && lint_level.is_explicit()),
                 "can't have both a visibility and a lint scope at the same time");
@@ -326,7 +349,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             };
             let visibility_scope = visibility_scope.unwrap();
             this.declare_binding(source_info, visibility_scope, mutability, name, mode, var,
-                                 ty, has_guard);
+                                 ty, has_guard, opt_match_place.map(|(x, y)| (x.cloned(), y)));
         });
         visibility_scope
     }
@@ -1121,7 +1144,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                        mode: BindingMode,
                        var_id: NodeId,
                        var_ty: Ty<'tcx>,
-                       has_guard: ArmHasGuard)
+                       has_guard: ArmHasGuard,
+                       opt_match_place: Option<(Option<Place<'tcx>>, Span)>)
     {
         debug!("declare_binding(var_id={:?}, name={:?}, mode={:?}, var_ty={:?}, \
                 visibility_scope={:?}, source_info={:?})",
@@ -1146,6 +1170,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 // idents in pat; but complex w/ unclear UI payoff.
                 // Instead, just abandon providing diagnostic info.
                 opt_ty_info: None,
+                opt_match_place,
             }))),
         };
         let for_arm_body = self.local_decls.push(local.clone());
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index ab20b7bfea2..4db5c8e9278 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -705,6 +705,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
             if let Some(pattern) = pattern {
                 let pattern = self.hir.pattern_from_hir(pattern);
+                let span = pattern.span;
 
                 match *pattern.kind {
                     // Don't introduce extra copies for simple bindings
@@ -716,15 +717,19 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                             } else {
                                 let binding_mode = ty::BindingMode::BindByValue(mutability.into());
                                 Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
-                                    binding_mode, opt_ty_info })))
+                                    binding_mode,
+                                    opt_ty_info,
+                                    opt_match_place: Some((Some(place.clone()), span)),
+                                })))
                             };
                         self.var_indices.insert(var, LocalsForNode::One(local));
                     }
                     _ => {
                         scope = self.declare_bindings(scope, ast_body.span,
                                                       LintLevel::Inherited, &pattern,
-                                                      matches::ArmHasGuard(false));
-                        unpack!(block = self.place_into_pattern(block, pattern, &place));
+                                                      matches::ArmHasGuard(false),
+                                                      Some((Some(&place), span)));
+                        unpack!(block = self.place_into_pattern(block, pattern, &place, false));
                     }
                 }
             }
diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs
index 590f9917015..9ffbe21e1e2 100644
--- a/src/librustc_mir/dataflow/move_paths/builder.rs
+++ b/src/librustc_mir/dataflow/move_paths/builder.rs
@@ -109,8 +109,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
         match *place {
             Place::Local(local) => Ok(self.builder.data.rev_lookup.locals[local]),
             Place::Static(..) => {
-                let span = self.builder.mir.source_info(self.loc).span;
-                Err(MoveError::cannot_move_out_of(span, Static))
+                Err(MoveError::cannot_move_out_of(self.loc, Static))
             }
             Place::Projection(ref proj) => {
                 self.move_path_for_projection(place, proj)
@@ -133,13 +132,13 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
         let mir = self.builder.mir;
         let tcx = self.builder.tcx;
         let place_ty = proj.base.ty(mir, tcx).to_ty(tcx);
-        match place_ty.sty {
+ match place_ty.sty {
             ty::TyRef(..) | ty::TyRawPtr(..) =>
                 return Err(MoveError::cannot_move_out_of(
-                    mir.source_info(self.loc).span,
+                    self.loc,
                     BorrowedContent { target_ty: place.ty(mir, tcx).to_ty(tcx) })),
             ty::TyAdt(adt, _) if adt.has_dtor(tcx) && !adt.is_box() =>
-                return Err(MoveError::cannot_move_out_of(mir.source_info(self.loc).span,
+                return Err(MoveError::cannot_move_out_of(self.loc,
                                                          InteriorOfTypeWithDestructor {
                     container_ty: place_ty
                 })),
@@ -148,7 +147,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
                 return Err(MoveError::UnionMove { path: base }),
             ty::TySlice(_) =>
                 return Err(MoveError::cannot_move_out_of(
-                    mir.source_info(self.loc).span,
+                    self.loc,
                     InteriorOfSliceOrArray {
                         ty: place_ty, is_index: match proj.elem {
                             ProjectionElem::Index(..) => true,
@@ -158,7 +157,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
             ty::TyArray(..) => match proj.elem {
                 ProjectionElem::Index(..) =>
                     return Err(MoveError::cannot_move_out_of(
-                        mir.source_info(self.loc).span,
+                        self.loc,
                         InteriorOfSliceOrArray {
                             ty: place_ty, is_index: true
                         })),
diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs
index a73e47bc16a..3051a687eac 100644
--- a/src/librustc_mir/dataflow/move_paths/mod.rs
+++ b/src/librustc_mir/dataflow/move_paths/mod.rs
@@ -209,7 +209,7 @@ pub enum InitKind {
     Deep,
     /// Only does a shallow init
     Shallow,
-    /// This doesn't initialize the variabe on panic (and a panic is possible).
+    /// This doesn't initialize the variable on panic (and a panic is possible).
     NonPanicPathOnly,
 }
 
@@ -271,7 +271,7 @@ impl<'tcx> MovePathLookup<'tcx> {
 
 #[derive(Debug)]
 pub struct IllegalMoveOrigin<'tcx> {
-    pub(crate) span: Span,
+    pub(crate) location: Location,
     pub(crate) kind: IllegalMoveOriginKind<'tcx>,
 }
 
@@ -304,8 +304,8 @@ pub enum MoveError<'tcx> {
 }
 
 impl<'tcx> MoveError<'tcx> {
-    fn cannot_move_out_of(span: Span, kind: IllegalMoveOriginKind<'tcx>) -> Self {
-        let origin = IllegalMoveOrigin { span, kind };
+    fn cannot_move_out_of(location: Location, kind: IllegalMoveOriginKind<'tcx>) -> Self {
+        let origin = IllegalMoveOrigin { location, kind };
         MoveError::IllegalMove { cannot_move_out_of: origin }
     }
 }
diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs
index ab9acdc4950..a8d29df8690 100644
--- a/src/librustc_mir/hair/mod.rs
+++ b/src/librustc_mir/hair/mod.rs
@@ -315,6 +315,15 @@ pub enum LogicalOp {
     Or,
 }
 
+impl<'tcx> ExprRef<'tcx> {
+    pub fn span(&self) -> Span {
+        match self {
+            ExprRef::Hair(expr) => expr.span,
+            ExprRef::Mirror(expr) => expr.span,
+        }
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // The Mirror trait
 
diff --git a/src/test/compile-fail/borrowck/borrowck-move-out-of-struct-with-dtor.rs b/src/test/compile-fail/borrowck/borrowck-move-out-of-struct-with-dtor.rs
index 8e1f7c72914..4bb66ace026 100644
--- a/src/test/compile-fail/borrowck/borrowck-move-out-of-struct-with-dtor.rs
+++ b/src/test/compile-fail/borrowck/borrowck-move-out-of-struct-with-dtor.rs
@@ -18,9 +18,9 @@ impl Drop for S {
 
 fn move_in_match() {
     match (S {f:"foo".to_string()}) {
+        //[mir]~^ ERROR [E0509]
         S {f:_s} => {}
         //[ast]~^ ERROR cannot move out of type `S`, which implements the `Drop` trait [E0509]
-        //[mir]~^^ ERROR [E0509]
     }
 }
 
diff --git a/src/test/mir-opt/combine_array_len.rs b/src/test/mir-opt/combine_array_len.rs
index 136c3493fa4..440ad3518ae 100644
--- a/src/test/mir-opt/combine_array_len.rs
+++ b/src/test/mir-opt/combine_array_len.rs
@@ -21,13 +21,13 @@ fn main() {
 // END RUST SOURCE
 
 // START rustc.norm2.InstCombine.before.mir
-//     _5 = Len(_1);
+//     _4 = Len(_1);
 //     ...
-//     _10 = Len(_1);
+//     _8 = Len(_1);
 // END rustc.norm2.InstCombine.before.mir
 
 // START rustc.norm2.InstCombine.after.mir
-//     _5 = const 2usize;
+//     _4 = const 2usize;
 //     ...
-//     _10 = const 2usize;
+//     _8 = const 2usize;
 // END rustc.norm2.InstCombine.after.mir
diff --git a/src/test/mir-opt/copy_propagation.rs b/src/test/mir-opt/copy_propagation.rs
index 50d8a5154c4..067a937b0b3 100644
--- a/src/test/mir-opt/copy_propagation.rs
+++ b/src/test/mir-opt/copy_propagation.rs
@@ -22,12 +22,9 @@ fn main() {
 // START rustc.test.CopyPropagation.before.mir
 //  bb0: {
 //      ...
-//      _3 = _1;
+//      _2 = _1;
 //      ...
-//      _2 = move _3;
-//      ...
-//      _4 = _2;
-//      _0 = move _4;
+//      _0 = _2;
 //      ...
 //      return;
 //  }
@@ -35,7 +32,7 @@ fn main() {
 // START rustc.test.CopyPropagation.after.mir
 //  bb0: {
 //      ...
-//      _0 = move _1;
+//      _0 = _1;
 //      ...
 //      return;
 //  }
diff --git a/src/test/mir-opt/copy_propagation_arg.rs b/src/test/mir-opt/copy_propagation_arg.rs
index feadec6bbf7..dacaff8f2d9 100644
--- a/src/test/mir-opt/copy_propagation_arg.rs
+++ b/src/test/mir-opt/copy_propagation_arg.rs
@@ -117,13 +117,11 @@ fn main() {
 // START rustc.arg_src.CopyPropagation.before.mir
 // bb0: {
 //      ...
-//      _3 = _1;
-//      _2 = move _3;
+//      _2 = _1;
 //      ...
 //      _1 = const 123i32;
 //      ...
-//      _4 = _2;
-//      _0 = move _4;
+//      _0 = _2;
 //      ...
 //      return;
 //  }
@@ -131,11 +129,11 @@ fn main() {
 // START rustc.arg_src.CopyPropagation.after.mir
 // bb0: {
 //     ...
-//     _3 = _1;
+//     _2 = _1;
 //     ...
 //     _1 = const 123i32;
 //     ...
-//     _0 = move _3;
+//     _0 = _2;
 //     ...
 //     return;
 // }
diff --git a/src/test/mir-opt/end_region_5.rs b/src/test/mir-opt/end_region_5.rs
index 1da97a997a1..9496fd9e6c1 100644
--- a/src/test/mir-opt/end_region_5.rs
+++ b/src/test/mir-opt/end_region_5.rs
@@ -68,13 +68,9 @@ fn foo<F>(f: F) where F: FnOnce() -> i32 {
 // START rustc.main-{{closure}}.SimplifyCfg-qualify-consts.after.mir
 // fn main::{{closure}}(_1: [closure@NodeId(18) d:&'14s D]) -> i32 {
 //    let mut _0: i32;
-//    let mut _2: i32;
 //
 //    bb0: {
-//        StorageLive(_2);
-//        _2 = ((*(_1.0: &'14s D)).0: i32);
-//        _0 = move _2;
-//        StorageDead(_2);
+//        _0 = ((*(_1.0: &'14s D)).0: i32);
 //        return;
 //    }
 // END rustc.main-{{closure}}.SimplifyCfg-qualify-consts.after.mir
diff --git a/src/test/mir-opt/end_region_6.rs b/src/test/mir-opt/end_region_6.rs
index 30ba14c1bab..910362f65c8 100644
--- a/src/test/mir-opt/end_region_6.rs
+++ b/src/test/mir-opt/end_region_6.rs
@@ -70,14 +70,10 @@ fn foo<F>(f: F) where F: FnOnce() -> i32 {
 //     ...
 //     let _2: &'16_0rs D;
 //     ...
-//     let mut _3: i32;
 //     bb0: {
 //         StorageLive(_2);
 //         _2 = &'16_0rs (*(_1.0: &'19s D));
-//         StorageLive(_3);
-//         _3 = ((*_2).0: i32);
-//         _0 = move _3;
-//         StorageDead(_3);
+//         _0 = ((*_2).0: i32);
 //         EndRegion('16_0rs);
 //         StorageDead(_2);
 //         return;
diff --git a/src/test/mir-opt/end_region_7.rs b/src/test/mir-opt/end_region_7.rs
index 6d6afa25ae3..e44b41993aa 100644
--- a/src/test/mir-opt/end_region_7.rs
+++ b/src/test/mir-opt/end_region_7.rs
@@ -78,14 +78,10 @@ fn foo<F>(f: F) where F: FnOnce() -> i32 {
 //     ...
 //     let _2: &'16_0rs D;
 //     ...
-//     let mut _3: i32;
 //     bb0: {
 //         StorageLive(_2);
 //         _2 = &'16_0rs (_1.0: D);
-//         StorageLive(_3);
-//         _3 = ((*_2).0: i32);
-//         _0 = move _3;
-//         StorageDead(_3);
+//         _0 = ((*_2).0: i32);
 //         EndRegion('16_0rs);
 //         StorageDead(_2);
 //         drop(_1) -> [return: bb2, unwind: bb1];
diff --git a/src/test/mir-opt/end_region_8.rs b/src/test/mir-opt/end_region_8.rs
index 96a64f2eebf..7fdf971b3b9 100644
--- a/src/test/mir-opt/end_region_8.rs
+++ b/src/test/mir-opt/end_region_8.rs
@@ -76,13 +76,9 @@ fn foo<F>(f: F) where F: FnOnce() -> i32 {
 // START rustc.main-{{closure}}.SimplifyCfg-qualify-consts.after.mir
 // fn main::{{closure}}(_1: [closure@NodeId(22) r:&'19s D]) -> i32 {
 //     let mut _0: i32;
-//     let mut _2: i32;
 //
 //     bb0: {
-//         StorageLive(_2);
-//         _2 = ((*(_1.0: &'21_1rs D)).0: i32);
-//         _0 = move _2;
-//         StorageDead(_2);
+//         _0 = ((*(_1.0: &'21_1rs D)).0: i32);
 //         return;
 //     }
 // }
diff --git a/src/test/mir-opt/end_region_destruction_extents_1.rs b/src/test/mir-opt/end_region_destruction_extents_1.rs
index 15b104f6c2f..fab2f9eff5a 100644
--- a/src/test/mir-opt/end_region_destruction_extents_1.rs
+++ b/src/test/mir-opt/end_region_destruction_extents_1.rs
@@ -65,52 +65,45 @@ unsafe impl<'a, #[may_dangle] 'b> Drop for D1<'a, 'b> {
 
 // START rustc.main.QualifyAndPromoteConstants.before.mir
 // fn main() -> () {
-//     let mut _0: ();
+// let mut _0: ();
 //     let mut _1: &'12ds S1;
-//     let mut _2: &'12ds S1;
-//     let mut _3: D1<'12ds, '10s>;
+//     let mut _2: D1<'12ds, '10s>;
+//     let mut _3: &'12ds S1;
 //     let mut _4: &'12ds S1;
-//     let mut _5: &'12ds S1;
-//     let mut _6: S1;
+//     let mut _5: S1;
+//     let mut _6: &'10s S1;
 //     let mut _7: &'10s S1;
-//     let mut _8: &'10s S1;
-//     let mut _9: S1;
-//
+//     let mut _8: S1;
 //     bb0: {
 //         StorageLive(_2);
 //         StorageLive(_3);
 //         StorageLive(_4);
 //         StorageLive(_5);
+//         _5 = S1::{{constructor}}(const "ex1",);
+//         _4 = &'12ds _5;
+//         _3 = &'12ds (*_4);
 //         StorageLive(_6);
-//         _6 = S1::{{constructor}}(const "ex1",);
-//         _5 = &'12ds _6;
-//         _4 = &'12ds (*_5);
 //         StorageLive(_7);
 //         StorageLive(_8);
-//         StorageLive(_9);
-//         _9 = S1::{{constructor}}(const "dang1",);
-//         _8 = &'10s _9;
-//         _7 = &'10s (*_8);
-//         _3 = D1<'12ds, '10s>::{{constructor}}(move _4, move _7);
+//         _8 = S1::{{constructor}}(const "dang1",);
+//         _7 = &'10s _8;
+//         _6 = &'10s (*_7);
+//         _2 = D1<'12ds, '10s>::{{constructor}}(move _3, move _6);
 //         EndRegion('10s);
-//         StorageDead(_7);
-//         StorageDead(_4);
-//         _2 = (_3.0: &'12ds S1);
-//         _1 = move _2;
-//         StorageDead(_2);
-//         drop(_3) -> [return: bb2, unwind: bb1];
+//         StorageDead(_6);
+//         StorageDead(_3);
+//         _1 = (_2.0: &'12ds S1);
+//         drop(_2) -> [return: bb2, unwind: bb1];
 //     }
-//
 //     bb1: {
 //         resume;
 //     }
-//
 //     bb2: {
-//         StorageDead(_3);
+//         StorageDead(_2);
+//         StorageDead(_7);
 //         StorageDead(_8);
-//         StorageDead(_9);
+//         StorageDead(_4);
 //         StorageDead(_5);
-//         StorageDead(_6);
 //         EndRegion('12ds);
 //         _0 = ();
 //         return;
@@ -119,51 +112,44 @@ unsafe impl<'a, #[may_dangle] 'b> Drop for D1<'a, 'b> {
 // END rustc.main.QualifyAndPromoteConstants.before.mir
 
 // START rustc.main.QualifyAndPromoteConstants.after.mir
-// fn main() -> () {
+// fn main() -> (){
 //     let mut _0: ();
 //     let mut _1: &'12ds S1;
-//     let mut _2: &'12ds S1;
-//     let mut _3: D1<'12ds, '10s>;
+//     let mut _2: D1<'12ds, '10s>;
+//     let mut _3: &'12ds S1;
 //     let mut _4: &'12ds S1;
-//     let mut _5: &'12ds S1;
-//     let mut _6: S1;
+//     let mut _5: S1;
+//     let mut _6: &'10s S1;
 //     let mut _7: &'10s S1;
-//     let mut _8: &'10s S1;
-//     let mut _9: S1;
-//     let mut _10: &'10s S1;
-//     let mut _11: &'12ds S1;
-//
+//     let mut _8: S1;
+//     let mut _9: &'10s S1;
+//     let mut _10: &'12ds S1;
 //     bb0: {
 //         StorageLive(_2);
 //         StorageLive(_3);
 //         StorageLive(_4);
-//         StorageLive(_5);
-//         _11 = promoted[1];
-//         _5 = &'12ds (*_11);
-//         _4 = &'12ds (*_5);
+//         _10 = promoted[1];
+//         _4 = &'12ds (*_10);
+//         _3 = &'12ds (*_4);
+//         StorageLive(_6);
 //         StorageLive(_7);
-//         StorageLive(_8);
-//         _10 = promoted[0];
-//         _8 = &'10s (*_10);
-//         _7 = &'10s (*_8);
-//         _3 = D1<'12ds, '10s>::{{constructor}}(move _4, move _7);
+//         _9 = promoted[0];
+//         _7 = &'10s (*_9);
+//         _6 = &'10s (*_7);
+//         _2 = D1<'12ds, '10s>::{{constructor}}(move _3, move _6);
 //         EndRegion('10s);
-//         StorageDead(_7);
-//         StorageDead(_4);
-//         _2 = (_3.0: &'12ds S1);
-//         _1 = move _2;
-//         StorageDead(_2);
-//         drop(_3) -> [return: bb2, unwind: bb1];
+//         StorageDead(_6);
+//         StorageDead(_3);
+//         _1 = (_2.0: &'12ds S1);
+//         drop(_2) -> [return: bb2, unwind: bb1];
 //     }
-//
 //     bb1: {
 //         resume;
 //     }
-//
 //     bb2: {
-//         StorageDead(_3);
-//         StorageDead(_8);
-//         StorageDead(_5);
+//         StorageDead(_2);
+//         StorageDead(_7);
+//         StorageDead(_4);
 //         EndRegion('12ds);
 //         _0 = ();
 //         return;
diff --git a/src/test/mir-opt/inline-closure-borrows-arg.rs b/src/test/mir-opt/inline-closure-borrows-arg.rs
index 3fb54f90984..6ce51be3ec5 100644
--- a/src/test/mir-opt/inline-closure-borrows-arg.rs
+++ b/src/test/mir-opt/inline-closure-borrows-arg.rs
@@ -38,11 +38,9 @@ fn foo<T: Copy>(_t: T, q: &i32) -> i32 {
 //     ...
 //     _7 = &(*_2);
 //     _5 = (move _6, move _7);
-//     _9 = move (_5.0: &i32);
-//     _10 = move (_5.1: &i32);
-//     StorageLive(_8);
-//     _8 = (*_9);
-//     _0 = move _8;
+//     _8 = move (_5.0: &i32);
+//     _9 = move (_5.1: &i32);
+//     _0 = (*_8);
 //     ...
 //     return;
 // }
diff --git a/src/test/mir-opt/inline-closure.rs b/src/test/mir-opt/inline-closure.rs
index dc8ff13c03a..22e7de31e90 100644
--- a/src/test/mir-opt/inline-closure.rs
+++ b/src/test/mir-opt/inline-closure.rs
@@ -36,7 +36,7 @@ fn foo<T: Copy>(_t: T, q: i32) -> i32 {
 //     _5 = (move _6, move _7);
 //     _8 = move (_5.0: i32);
 //     _9 = move (_5.1: i32);
-//     _0 = move _8;
+//     _0 = _8;
 //     ...
 //     return;
 // }
diff --git a/src/test/mir-opt/lower_128bit_debug_test.rs b/src/test/mir-opt/lower_128bit_debug_test.rs
index d7586b1aa4b..646c4312fc2 100644
--- a/src/test/mir-opt/lower_128bit_debug_test.rs
+++ b/src/test/mir-opt/lower_128bit_debug_test.rs
@@ -182,8 +182,8 @@ fn main() {
 //     ...
 //     _1 = move (_13.0: i128);
 //     ...
-//     _17 = const 7i32 as u128 (Misc);
-//     _14 = const compiler_builtins::int::shift::rust_i128_shro(_1, move _17) -> bb16;
+//     _16 = const 7i32 as u128 (Misc);
+//     _14 = const compiler_builtins::int::shift::rust_i128_shro(_1, move _16) -> bb16;
 //     ...
 //     _1 = move (_14.0: i128);
 //     ...
@@ -195,8 +195,8 @@ fn main() {
 //     ...
 //     assert(!move (_13.1: bool), "attempt to shift left with overflow") -> bb8;
 //     ...
-//     _16 = const 6i32 as u128 (Misc);
-//     _13 = const compiler_builtins::int::shift::rust_i128_shlo(_1, move _16) -> bb14;
+//     _15 = const 6i32 as u128 (Misc);
+//     _13 = const compiler_builtins::int::shift::rust_i128_shlo(_1, move _15) -> bb14;
 //     ...
 //     assert(!move (_14.1: bool), "attempt to shift right with overflow") -> bb9;
 // END rustc.test_signed.Lower128Bit.after.mir
@@ -218,8 +218,8 @@ fn main() {
 //     ...
 //     _1 = move (_7.0: u128);
 //     ...
-//     _11 = const 7i32 as u128 (Misc);
-//     _8 = const compiler_builtins::int::shift::rust_u128_shro(_1, move _11) -> bb14;
+//     _10 = const 7i32 as u128 (Misc);
+//     _8 = const compiler_builtins::int::shift::rust_u128_shro(_1, move _10) -> bb14;
 //     ...
 //     _1 = move (_8.0: u128);
 //     ...
@@ -231,8 +231,8 @@ fn main() {
 //     ...
 //     assert(!move (_7.1: bool), "attempt to shift left with overflow") -> bb6;
 //     ...
-//     _10 = const 6i32 as u128 (Misc);
-//     _7 = const compiler_builtins::int::shift::rust_u128_shlo(_1, move _10) -> bb12;
+//     _9 = const 6i32 as u128 (Misc);
+//     _7 = const compiler_builtins::int::shift::rust_u128_shlo(_1, move _9) -> bb12;
 //     ...
 //     assert(!move (_8.1: bool), "attempt to shift right with overflow") -> bb7;
 // END rustc.test_unsigned.Lower128Bit.after.mir
diff --git a/src/test/mir-opt/lower_128bit_test.rs b/src/test/mir-opt/lower_128bit_test.rs
index 341682debeb..27446d6bd28 100644
--- a/src/test/mir-opt/lower_128bit_test.rs
+++ b/src/test/mir-opt/lower_128bit_test.rs
@@ -176,11 +176,11 @@ fn main() {
 //     ...
 //     _1 = const compiler_builtins::int::addsub::rust_i128_sub(_1, const 2i128) -> bb6;
 //     ...
-//     _11 = const 7i32 as u32 (Misc);
-//     _1 = const compiler_builtins::int::shift::rust_i128_shr(_1, move _11) -> bb9;
+//     _10 = const 7i32 as u32 (Misc);
+//     _1 = const compiler_builtins::int::shift::rust_i128_shr(_1, move _10) -> bb9;
 //     ...
-//     _12 = const 6i32 as u32 (Misc);
-//     _1 = const compiler_builtins::int::shift::rust_i128_shl(_1, move _12) -> bb10;
+//     _11 = const 6i32 as u32 (Misc);
+//     _1 = const compiler_builtins::int::shift::rust_i128_shl(_1, move _11) -> bb10;
 // END rustc.test_signed.Lower128Bit.after.mir
 
 // START rustc.test_unsigned.Lower128Bit.after.mir
@@ -194,9 +194,9 @@ fn main() {
 //     ...
 //     _1 = const compiler_builtins::int::addsub::rust_u128_sub(_1, const 2u128) -> bb4;
 //     ...
-//     _5 = const 7i32 as u32 (Misc);
-//     _1 = const compiler_builtins::int::shift::rust_u128_shr(_1, move _5) -> bb7;
+//     _4 = const 7i32 as u32 (Misc);
+//     _1 = const compiler_builtins::int::shift::rust_u128_shr(_1, move _4) -> bb7;
 //     ...
-//     _6 = const 6i32 as u32 (Misc);
-//     _1 = const compiler_builtins::int::shift::rust_u128_shl(_1, move _6) -> bb8;
+//     _5 = const 6i32 as u32 (Misc);
+//     _1 = const compiler_builtins::int::shift::rust_u128_shl(_1, move _5) -> bb8;
 // END rustc.test_unsigned.Lower128Bit.after.mir
diff --git a/src/test/mir-opt/nll/region-subtyping-basic.rs b/src/test/mir-opt/nll/region-subtyping-basic.rs
index 57a11d8dd25..a8dd6c73ef0 100644
--- a/src/test/mir-opt/nll/region-subtyping-basic.rs
+++ b/src/test/mir-opt/nll/region-subtyping-basic.rs
@@ -32,9 +32,9 @@ fn main() {
 
 // END RUST SOURCE
 // START rustc.main.nll.0.mir
-// | '_#2r    | {bb2[0..=6], bb3[0..=1]}
-// | '_#3r    | {bb2[1..=6], bb3[0..=1]}
-// | '_#4r    | {bb2[5..=6], bb3[0..=1]}
+// | '_#2r    | {bb2[0..=3], bb3[0..=1]}
+// | '_#3r    | {bb2[1..=3], bb3[0..=1]}
+// | '_#4r    | {bb2[3], bb3[0..=1]}
 // END rustc.main.nll.0.mir
 // START rustc.main.nll.0.mir
 // let _6: &'_#4r usize;
@@ -43,7 +43,5 @@ fn main() {
 // ...
 // _2 = &'_#2r _1[_3];
 // ...
-// _7 = _2;
-// ...
-// _6 = move _7;
+// _6 = _2;
 // END rustc.main.nll.0.mir
diff --git a/src/test/mir-opt/validate_1.rs b/src/test/mir-opt/validate_1.rs
index c331276aade..2d5c98ff0ed 100644
--- a/src/test/mir-opt/validate_1.rs
+++ b/src/test/mir-opt/validate_1.rs
@@ -67,10 +67,7 @@ fn main() {
 //         Validate(Suspend(ReScope(Remainder(BlockRemainder { block: ItemLocalId(25), first_statement_index: 0 }))), [(*_2): i32]);
 //         _3 = &ReErased (*_2);
 //         Validate(Acquire, [(*_3): i32/ReScope(Remainder(BlockRemainder { block: ItemLocalId(25), first_statement_index: 0 })) (imm)]);
-//         StorageLive(_4);
-//         _4 = (*_3);
-//         _0 = move _4;
-//         StorageDead(_4);
+//         _0 = (*_3);
 //         EndRegion(ReScope(Remainder(BlockRemainder { block: ItemLocalId(25), first_statement_index: 0 })));
 //         StorageDead(_3);
 //         return;
diff --git a/src/test/ui/E0508.ast.nll.stderr b/src/test/ui/E0508.ast.nll.stderr
deleted file mode 100644
index 28403644a23..00000000000
--- a/src/test/ui/E0508.ast.nll.stderr
+++ /dev/null
@@ -1,9 +0,0 @@
-error[E0508]: cannot move out of type `[NonCopy; 1]`, a non-copy array
-  --> $DIR/E0508.rs:18:18
-   |
-LL |     let _value = array[0];  //[ast]~ ERROR [E0508]
-   |                  ^^^^^^^^ cannot move out of here
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0508`.
diff --git a/src/test/ui/E0508.mir.stderr b/src/test/ui/E0508.mir.stderr
deleted file mode 100644
index 28403644a23..00000000000
--- a/src/test/ui/E0508.mir.stderr
+++ /dev/null
@@ -1,9 +0,0 @@
-error[E0508]: cannot move out of type `[NonCopy; 1]`, a non-copy array
-  --> $DIR/E0508.rs:18:18
-   |
-LL |     let _value = array[0];  //[ast]~ ERROR [E0508]
-   |                  ^^^^^^^^ cannot move out of here
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0508`.
diff --git a/src/test/ui/E0508.rs b/src/test/ui/E0508.rs
index 0c3dce6b034..86445ad8982 100644
--- a/src/test/ui/E0508.rs
+++ b/src/test/ui/E0508.rs
@@ -8,13 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// revisions: ast mir
-//[mir]compile-flags: -Z borrowck=mir
-
 struct NonCopy;
 
 fn main() {
     let array = [NonCopy; 1];
-    let _value = array[0];  //[ast]~ ERROR [E0508]
-                            //[mir]~^ ERROR [E0508]
+    let _value = array[0];  //~ ERROR [E0508]
 }
diff --git a/src/test/ui/E0508.ast.stderr b/src/test/ui/E0508.stderr
index 5878b795b77..28ce0d971c6 100644
--- a/src/test/ui/E0508.ast.stderr
+++ b/src/test/ui/E0508.stderr
@@ -1,7 +1,7 @@
 error[E0508]: cannot move out of type `[NonCopy; 1]`, a non-copy array
-  --> $DIR/E0508.rs:18:18
+  --> $DIR/E0508.rs:15:18
    |
-LL |     let _value = array[0];  //[ast]~ ERROR [E0508]
+LL |     let _value = array[0];  //~ ERROR [E0508]
    |                  ^^^^^^^^
    |                  |
    |                  cannot move out of here
diff --git a/src/test/ui/borrowck/borrowck-move-error-with-note.nll.stderr b/src/test/ui/borrowck/borrowck-move-error-with-note.nll.stderr
index c563a28b317..a34c97974da 100644
--- a/src/test/ui/borrowck/borrowck-move-error-with-note.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-move-error-with-note.nll.stderr
@@ -1,40 +1,46 @@
 error[E0507]: cannot move out of borrowed content
-  --> $DIR/borrowck-move-error-with-note.rs:23:19
+  --> $DIR/borrowck-move-error-with-note.rs:21:11
    |
+LL |     match *f {             //~ ERROR cannot move out of
+   |           ^^
+   |           |
+   |           cannot move out of borrowed content
+   |           help: consider removing this dereference operator: `f`
+LL |                            //~| cannot move out
 LL |         Foo::Foo1(num1,
-   |                   ^^^^ cannot move out of borrowed content
-
-error[E0507]: cannot move out of borrowed content
-  --> $DIR/borrowck-move-error-with-note.rs:24:19
-   |
+   |                   ---- move occurs because num1 has type `std::boxed::Box<u32>`, which does not implement the `Copy` trait
 LL |                   num2) => (),
-   |                   ^^^^ cannot move out of borrowed content
-
-error[E0507]: cannot move out of borrowed content
-  --> $DIR/borrowck-move-error-with-note.rs:25:19
-   |
+   |                   ---- move occurs because num2 has type `std::boxed::Box<u32>`, which does not implement the `Copy` trait
 LL |         Foo::Foo2(num) => (),
-   |                   ^^^ cannot move out of borrowed content
+   |                   --- move occurs because num has type `std::boxed::Box<u32>`, which does not implement the `Copy` trait
 
 error[E0509]: cannot move out of type `S`, which implements the `Drop` trait
-  --> $DIR/borrowck-move-error-with-note.rs:42:16
+  --> $DIR/borrowck-move-error-with-note.rs:39:11
    |
-LL |             f: _s,
-   |                ^^ cannot move out of here
-
-error[E0509]: cannot move out of type `S`, which implements the `Drop` trait
-  --> $DIR/borrowck-move-error-with-note.rs:43:16
+LL |     match (S {f: "foo".to_string(), g: "bar".to_string()}) {
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot move out of here
+help: to prevent move, use ref or ref mut
+   |
+LL |             f: ref _s,
+   |                ^^^^^^
+help: to prevent move, use ref or ref mut
    |
-LL |             g: _t
-   |                ^^ cannot move out of here
+LL |             g: ref _t
+   |                ^^^^^^
 
 error[E0507]: cannot move out of borrowed content
-  --> $DIR/borrowck-move-error-with-note.rs:59:9
+  --> $DIR/borrowck-move-error-with-note.rs:57:11
    |
+LL |     match a.a {           //~ ERROR cannot move out of
+   |           ^^^
+   |           |
+   |           cannot move out of borrowed content
+   |           help: consider using a reference instead: `&a.a`
+LL |                           //~| cannot move out
 LL |         n => {
-   |         ^ cannot move out of borrowed content
+   |         - move occurs because n has type `std::boxed::Box<isize>`, which does not implement the `Copy` trait
 
-error: aborting due to 6 previous errors
+error: aborting due to 3 previous errors
 
 Some errors occurred: E0507, E0509.
 For more information about an error, try `rustc --explain E0507`.
diff --git a/src/test/ui/borrowck/borrowck-move-out-of-vec-tail.nll.stderr b/src/test/ui/borrowck/borrowck-move-out-of-vec-tail.nll.stderr
index 96b376ea7ae..d01b24507d9 100644
--- a/src/test/ui/borrowck/borrowck-move-out-of-vec-tail.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-move-out-of-vec-tail.nll.stderr
@@ -1,15 +1,17 @@
 error[E0508]: cannot move out of type `[Foo]`, a non-copy slice
-  --> $DIR/borrowck-move-out-of-vec-tail.rs:30:33
+  --> $DIR/borrowck-move-out-of-vec-tail.rs:29:19
    |
-LL |                 &[Foo { string: a },
-   |                                 ^ cannot move out of here
-
-error[E0508]: cannot move out of type `[Foo]`, a non-copy slice
-  --> $DIR/borrowck-move-out-of-vec-tail.rs:34:33
+LL |             match tail {
+   |                   ^^^^ cannot move out of here
+help: to prevent move, use ref or ref mut
+   |
+LL |                 &[Foo { string: ref a },
+   |                                 ^^^^^
+help: to prevent move, use ref or ref mut
    |
-LL |                   Foo { string: b }] => {
-   |                                 ^ cannot move out of here
+LL |                   Foo { string: ref b }] => {
+   |                                 ^^^^^
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0508`.
diff --git a/src/test/ui/borrowck/borrowck-vec-pattern-nesting.nll.stderr b/src/test/ui/borrowck/borrowck-vec-pattern-nesting.nll.stderr
index 6d28a37463b..50ef3ba40e7 100644
--- a/src/test/ui/borrowck/borrowck-vec-pattern-nesting.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-vec-pattern-nesting.nll.stderr
@@ -23,54 +23,68 @@ LL |             _b.use_ref();
    |             -- borrow later used here
 
 error[E0508]: cannot move out of type `[std::boxed::Box<isize>]`, a non-copy slice
-  --> $DIR/borrowck-vec-pattern-nesting.rs:44:15
+  --> $DIR/borrowck-vec-pattern-nesting.rs:43:11
    |
+LL |     match vec {
+   |           ^^^ cannot move out of here
 LL |         &mut [_a, //~ ERROR cannot move out
-   |               ^^ cannot move out of here
+   |               -- help: to prevent move, use ref or ref mut: `ref _a`
 
 error[E0508]: cannot move out of type `[std::boxed::Box<isize>]`, a non-copy slice
   --> $DIR/borrowck-vec-pattern-nesting.rs:57:13
    |
 LL |     let a = vec[0]; //~ ERROR cannot move out
-   |             ^^^^^^ cannot move out of here
+   |             ^^^^^^
+   |             |
+   |             cannot move out of here
+   |             help: consider using a reference instead: `&vec[0]`
 
 error[E0508]: cannot move out of type `[std::boxed::Box<isize>]`, a non-copy slice
-  --> $DIR/borrowck-vec-pattern-nesting.rs:67:10
+  --> $DIR/borrowck-vec-pattern-nesting.rs:64:11
    |
+LL |     match vec {
+   |           ^^^ cannot move out of here
+...
 LL |          _b] => {}
-   |          ^^ cannot move out of here
+   |          -- help: to prevent move, use ref or ref mut: `ref _b`
 
 error[E0508]: cannot move out of type `[std::boxed::Box<isize>]`, a non-copy slice
   --> $DIR/borrowck-vec-pattern-nesting.rs:70:13
    |
 LL |     let a = vec[0]; //~ ERROR cannot move out
-   |             ^^^^^^ cannot move out of here
+   |             ^^^^^^
+   |             |
+   |             cannot move out of here
+   |             help: consider using a reference instead: `&vec[0]`
 
 error[E0508]: cannot move out of type `[std::boxed::Box<isize>]`, a non-copy slice
-  --> $DIR/borrowck-vec-pattern-nesting.rs:78:15
+  --> $DIR/borrowck-vec-pattern-nesting.rs:77:11
    |
-LL |         &mut [_a, _b, _c] => {}  //~ ERROR cannot move out
-   |               ^^ cannot move out of here
-
-error[E0508]: cannot move out of type `[std::boxed::Box<isize>]`, a non-copy slice
-  --> $DIR/borrowck-vec-pattern-nesting.rs:78:19
+LL |     match vec {
+   |           ^^^ cannot move out of here
+help: to prevent move, use ref or ref mut
    |
-LL |         &mut [_a, _b, _c] => {}  //~ ERROR cannot move out
-   |                   ^^ cannot move out of here
-
-error[E0508]: cannot move out of type `[std::boxed::Box<isize>]`, a non-copy slice
-  --> $DIR/borrowck-vec-pattern-nesting.rs:78:23
+LL |         &mut [ref _a, _b, _c] => {}  //~ ERROR cannot move out
+   |               ^^^^^^
+help: to prevent move, use ref or ref mut
+   |
+LL |         &mut [_a, ref _b, _c] => {}  //~ ERROR cannot move out
+   |                   ^^^^^^
+help: to prevent move, use ref or ref mut
    |
-LL |         &mut [_a, _b, _c] => {}  //~ ERROR cannot move out
-   |                       ^^ cannot move out of here
+LL |         &mut [_a, _b, ref _c] => {}  //~ ERROR cannot move out
+   |                       ^^^^^^
 
 error[E0508]: cannot move out of type `[std::boxed::Box<isize>]`, a non-copy slice
   --> $DIR/borrowck-vec-pattern-nesting.rs:82:13
    |
 LL |     let a = vec[0]; //~ ERROR cannot move out
-   |             ^^^^^^ cannot move out of here
+   |             ^^^^^^
+   |             |
+   |             cannot move out of here
+   |             help: consider using a reference instead: `&vec[0]`
 
-error: aborting due to 10 previous errors
+error: aborting due to 8 previous errors
 
 Some errors occurred: E0506, E0508.
 For more information about an error, try `rustc --explain E0506`.
diff --git a/src/test/ui/borrowck/issue-51415.nll.stderr b/src/test/ui/borrowck/issue-51415.nll.stderr
index 79454b63526..d872c7efe2b 100644
--- a/src/test/ui/borrowck/issue-51415.nll.stderr
+++ b/src/test/ui/borrowck/issue-51415.nll.stderr
@@ -1,8 +1,11 @@
 error[E0507]: cannot move out of borrowed content
-  --> $DIR/issue-51415.rs:16:47
+  --> $DIR/issue-51415.rs:16:42
    |
 LL |     let opt = a.iter().enumerate().find(|(_, &s)| {
-   |                                               ^ cannot move out of borrowed content
+   |                                          ^^^^^-^
+   |                                          |    |
+   |                                          |    help: to prevent move, use ref or ref mut: `ref s`
+   |                                          cannot move out of borrowed content
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/codemap_tests/overlapping_spans.nll.stderr b/src/test/ui/codemap_tests/overlapping_spans.nll.stderr
index b6630b2e666..34616a8de45 100644
--- a/src/test/ui/codemap_tests/overlapping_spans.nll.stderr
+++ b/src/test/ui/codemap_tests/overlapping_spans.nll.stderr
@@ -1,8 +1,10 @@
 error[E0509]: cannot move out of type `S`, which implements the `Drop` trait
-  --> $DIR/overlapping_spans.rs:21:14
+  --> $DIR/overlapping_spans.rs:20:11
    |
+LL |     match (S {f:"foo".to_string()}) {
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot move out of here
 LL |         S {f:_s} => {} //~ ERROR cannot move out
-   |              ^^ cannot move out of here
+   |              -- help: to prevent move, use ref or ref mut: `ref _s`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/error-codes/E0509.nll.stderr b/src/test/ui/error-codes/E0509.nll.stderr
deleted file mode 100644
index 56d970494a0..00000000000
--- a/src/test/ui/error-codes/E0509.nll.stderr
+++ /dev/null
@@ -1,9 +0,0 @@
-error[E0509]: cannot move out of type `DropStruct`, which implements the `Drop` trait
-  --> $DIR/E0509.rs:26:23
-   |
-LL |     let fancy_field = drop_struct.fancy; //~ ERROR E0509
-   |                       ^^^^^^^^^^^^^^^^^ cannot move out of here
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0509`.
diff --git a/src/test/ui/issue-40402-ref-hints/issue-40402-1.nll.stderr b/src/test/ui/issue-40402-ref-hints/issue-40402-1.nll.stderr
index 48e27014792..162e43abc0a 100644
--- a/src/test/ui/issue-40402-ref-hints/issue-40402-1.nll.stderr
+++ b/src/test/ui/issue-40402-ref-hints/issue-40402-1.nll.stderr
@@ -2,7 +2,10 @@ error[E0507]: cannot move out of borrowed content
   --> $DIR/issue-40402-1.rs:19:13
    |
 LL |     let e = f.v[0]; //~ ERROR cannot move out of indexed content
-   |             ^^^^^^ cannot move out of borrowed content
+   |             ^^^^^^
+   |             |
+   |             cannot move out of borrowed content
+   |             help: consider using a reference instead: `&f.v[0]`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issue-40402-ref-hints/issue-40402-2.nll.stderr b/src/test/ui/issue-40402-ref-hints/issue-40402-2.nll.stderr
index 0b907c5acf1..cd75be24589 100644
--- a/src/test/ui/issue-40402-ref-hints/issue-40402-2.nll.stderr
+++ b/src/test/ui/issue-40402-ref-hints/issue-40402-2.nll.stderr
@@ -1,15 +1,14 @@
 error[E0507]: cannot move out of borrowed content
-  --> $DIR/issue-40402-2.rs:15:10
+  --> $DIR/issue-40402-2.rs:15:18
    |
 LL |     let (a, b) = x[0]; //~ ERROR cannot move out of indexed content
-   |          ^ cannot move out of borrowed content
+   |          -  -    ^^^^
+   |          |  |    |
+   |          |  |    cannot move out of borrowed content
+   |          |  |    help: consider using a reference instead: `&x[0]`
+   |          |  move occurs because b has type `std::string::String`, which does not implement the `Copy` trait
+   |          move occurs because a has type `std::string::String`, which does not implement the `Copy` trait
 
-error[E0507]: cannot move out of borrowed content
-  --> $DIR/issue-40402-2.rs:15:13
-   |
-LL |     let (a, b) = x[0]; //~ ERROR cannot move out of indexed content
-   |             ^ cannot move out of borrowed content
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/moves-based-on-type-block-bad.nll.stderr b/src/test/ui/moves-based-on-type-block-bad.nll.stderr
index 942d9816c4e..94f2074b45d 100644
--- a/src/test/ui/moves-based-on-type-block-bad.nll.stderr
+++ b/src/test/ui/moves-based-on-type-block-bad.nll.stderr
@@ -1,8 +1,14 @@
 error[E0507]: cannot move out of borrowed content
-  --> $DIR/moves-based-on-type-block-bad.rs:37:28
+  --> $DIR/moves-based-on-type-block-bad.rs:34:19
    |
+LL |             match hellothere.x { //~ ERROR cannot move out
+   |                   ^^^^^^^^^^^^
+   |                   |
+   |                   cannot move out of borrowed content
+   |                   help: consider using a reference instead: `&hellothere.x`
+...
 LL |                 box E::Bar(x) => println!("{}", x.to_string()),
-   |                            ^ cannot move out of borrowed content
+   |                            - move occurs because x has type `std::boxed::Box<isize>`, which does not implement the `Copy` trait
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/nll/move-errors.rs b/src/test/ui/nll/move-errors.rs
new file mode 100644
index 00000000000..6445f166b57
--- /dev/null
+++ b/src/test/ui/nll/move-errors.rs
@@ -0,0 +1,130 @@
+// Copyright 2018 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.
+
+#![allow(unused)]
+#![feature(nll)]
+
+struct A(String);
+struct C(D);
+
+fn suggest_remove_deref() {
+    let a = &A("".to_string());
+    let b = *a;
+    //~^ ERROR
+}
+
+fn suggest_borrow() {
+    let a = [A("".to_string())];
+    let b = a[0];
+    //~^ ERROR
+}
+
+fn suggest_borrow2() {
+    let mut a = A("".to_string());
+    let r = &&mut a;
+    let s = **r;
+    //~^ ERROR
+}
+
+fn suggest_borrow3() {
+    use std::rc::Rc;
+    let mut a = A("".to_string());
+    let r = Rc::new(a);
+    let s = *r;
+    //~^ ERROR
+}
+
+fn suggest_borrow4() {
+    let a = [A("".to_string())][0];
+    //~^ ERROR
+}
+
+fn suggest_borrow5() {
+    let a = &A("".to_string());
+    let A(s) = *a;
+    //~^ ERROR
+}
+
+fn suggest_ref() {
+    let c = C(D(String::new()));
+    let C(D(s)) = c;
+    //~^ ERROR
+}
+
+fn suggest_nothing() {
+    let a = &A("".to_string());
+    let b;
+    b = *a;
+    //~^ ERROR
+}
+
+enum B {
+    V(String),
+    U(D),
+}
+
+struct D(String);
+
+impl Drop for D {
+    fn drop(&mut self) {}
+}
+
+struct F(String, String);
+
+impl Drop for F {
+    fn drop(&mut self) {}
+}
+
+fn probably_suggest_borrow() {
+    let x = [B::V(String::new())];
+    match x[0] {
+    //~^ ERROR
+        B::U(d) => (),
+        B::V(s) => (),
+    }
+}
+
+fn have_to_suggest_ref() {
+    let x = B::V(String::new());
+    match x {
+    //~^ ERROR
+        B::V(s) => drop(s),
+        B::U(D(s)) => (),
+    };
+}
+
+fn two_separate_errors() {
+    let x = (D(String::new()), &String::new());
+    match x {
+    //~^ ERROR
+    //~^^ ERROR
+        (D(s), &t) => (),
+        _ => (),
+    }
+}
+
+fn have_to_suggest_double_ref() {
+    let x = F(String::new(), String::new());
+    match x {
+    //~^ ERROR
+        F(s, mut t) => (),
+        _ => (),
+    }
+}
+
+fn double_binding(x: &Result<String, String>) {
+    match *x {
+    //~^ ERROR
+        Ok(s) | Err(s) => (),
+    }
+}
+
+fn main() {
+}
diff --git a/src/test/ui/nll/move-errors.stderr b/src/test/ui/nll/move-errors.stderr
new file mode 100644
index 00000000000..3f2c651ae3a
--- /dev/null
+++ b/src/test/ui/nll/move-errors.stderr
@@ -0,0 +1,140 @@
+error[E0507]: cannot move out of borrowed content
+  --> $DIR/move-errors.rs:19:13
+   |
+LL |     let b = *a;
+   |             ^^
+   |             |
+   |             cannot move out of borrowed content
+   |             help: consider removing this dereference operator: `a`
+
+error[E0508]: cannot move out of type `[A; 1]`, a non-copy array
+  --> $DIR/move-errors.rs:25:13
+   |
+LL |     let b = a[0];
+   |             ^^^^
+   |             |
+   |             cannot move out of here
+   |             help: consider using a reference instead: `&a[0]`
+
+error[E0507]: cannot move out of borrowed content
+  --> $DIR/move-errors.rs:32:13
+   |
+LL |     let s = **r;
+   |             ^^^
+   |             |
+   |             cannot move out of borrowed content
+   |             help: consider using a reference instead: `&**r`
+
+error[E0507]: cannot move out of borrowed content
+  --> $DIR/move-errors.rs:40:13
+   |
+LL |     let s = *r;
+   |             ^^
+   |             |
+   |             cannot move out of borrowed content
+   |             help: consider using a reference instead: `&*r`
+
+error[E0508]: cannot move out of type `[A; 1]`, a non-copy array
+  --> $DIR/move-errors.rs:45:13
+   |
+LL |     let a = [A("".to_string())][0];
+   |             ^^^^^^^^^^^^^^^^^^^^^^
+   |             |
+   |             cannot move out of here
+   |             help: consider using a reference instead: `&[A("".to_string())][0]`
+
+error[E0507]: cannot move out of borrowed content
+  --> $DIR/move-errors.rs:51:16
+   |
+LL |     let A(s) = *a;
+   |           -    ^^
+   |           |    |
+   |           |    cannot move out of borrowed content
+   |           |    help: consider removing this dereference operator: `a`
+   |           move occurs because s has type `std::string::String`, which does not implement the `Copy` trait
+
+error[E0509]: cannot move out of type `D`, which implements the `Drop` trait
+  --> $DIR/move-errors.rs:57:19
+   |
+LL |     let C(D(s)) = c;
+   |             -     ^ cannot move out of here
+   |             |
+   |             help: to prevent move, use ref or ref mut: `ref s`
+
+error[E0507]: cannot move out of borrowed content
+  --> $DIR/move-errors.rs:64:9
+   |
+LL |     b = *a;
+   |         ^^ cannot move out of borrowed content
+
+error[E0508]: cannot move out of type `[B; 1]`, a non-copy array
+  --> $DIR/move-errors.rs:87:11
+   |
+LL |     match x[0] {
+   |           ^^^^
+   |           |
+   |           cannot move out of here
+   |           help: consider using a reference instead: `&x[0]`
+LL |     //~^ ERROR
+LL |         B::U(d) => (),
+   |              - move occurs because d has type `D`, which does not implement the `Copy` trait
+LL |         B::V(s) => (),
+   |              - move occurs because s has type `std::string::String`, which does not implement the `Copy` trait
+
+error[E0509]: cannot move out of type `D`, which implements the `Drop` trait
+  --> $DIR/move-errors.rs:96:11
+   |
+LL |     match x {
+   |           ^ cannot move out of here
+...
+LL |         B::U(D(s)) => (),
+   |                - help: to prevent move, use ref or ref mut: `ref s`
+
+error[E0509]: cannot move out of type `D`, which implements the `Drop` trait
+  --> $DIR/move-errors.rs:105:11
+   |
+LL |     match x {
+   |           ^ cannot move out of here
+...
+LL |         (D(s), &t) => (),
+   |            - help: to prevent move, use ref or ref mut: `ref s`
+
+error[E0507]: cannot move out of borrowed content
+  --> $DIR/move-errors.rs:105:11
+   |
+LL |     match x {
+   |           ^ cannot move out of borrowed content
+...
+LL |         (D(s), &t) => (),
+   |                 - help: to prevent move, use ref or ref mut: `ref t`
+
+error[E0509]: cannot move out of type `F`, which implements the `Drop` trait
+  --> $DIR/move-errors.rs:115:11
+   |
+LL |     match x {
+   |           ^ cannot move out of here
+help: to prevent move, use ref or ref mut
+   |
+LL |         F(ref s, mut t) => (),
+   |           ^^^^^
+help: to prevent move, use ref or ref mut
+   |
+LL |         F(s, ref mut t) => (),
+   |              ^^^^^^^^^
+
+error[E0507]: cannot move out of borrowed content
+  --> $DIR/move-errors.rs:123:11
+   |
+LL |     match *x {
+   |           ^^
+   |           |
+   |           cannot move out of borrowed content
+   |           help: consider removing this dereference operator: `x`
+LL |     //~^ ERROR
+LL |         Ok(s) | Err(s) => (),
+   |            - move occurs because s has type `std::string::String`, which does not implement the `Copy` trait
+
+error: aborting due to 14 previous errors
+
+Some errors occurred: E0507, E0508, E0509.
+For more information about an error, try `rustc --explain E0507`.