about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAaron Hill <aa1ronham@gmail.com>2020-07-25 07:04:13 -0400
committerAaron Hill <aa1ronham@gmail.com>2020-09-10 20:56:20 -0400
commitd18b4bb7a735add53cad717fd96ec5f4b89d752d (patch)
treebe055d03d7c3271fa3056d6a0c9df26a77755d22
parenta1947b3f9e2831e2060bc42f0c78e4a2bb67930a (diff)
downloadrust-d18b4bb7a735add53cad717fd96ec5f4b89d752d.tar.gz
rust-d18b4bb7a735add53cad717fd96ec5f4b89d752d.zip
Note when a a move/borrow error is caused by a deref coercion
Fixes #73268

When a deref coercion occurs, we may end up with a move error if the
base value has been partially moved out of. However, we do not indicate
anywhere that a deref coercion is occuring, resulting in an error
message with a confusing span.

This PR adds an explicit note to move errors when a deref coercion is
involved. We mention the name of the type that the deref-coercion
resolved to, as well as the `Deref::Target` associated type being used.
-rw-r--r--compiler/rustc_middle/src/ty/adjustment.rs4
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs7
-rw-r--r--compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs27
-rw-r--r--compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs2
-rw-r--r--compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs73
-rw-r--r--compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs6
-rw-r--r--compiler/rustc_mir/src/borrow_check/mod.rs5
-rw-r--r--compiler/rustc_mir/src/transform/check_const_item_mutation.rs2
-rw-r--r--compiler/rustc_mir/src/util/find_self_call.rs13
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs29
-rw-r--r--compiler/rustc_span/src/symbol.rs2
-rw-r--r--compiler/rustc_trait_selection/src/autoderef.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs2
-rw-r--r--compiler/rustc_typeck/src/check/autoderef.rs19
-rw-r--r--compiler/rustc_typeck/src/check/method/confirm.rs3
-rw-r--r--compiler/rustc_typeck/src/check/method/probe.rs7
-rw-r--r--compiler/rustc_typeck/src/check/place_op.rs2
-rw-r--r--library/core/src/ops/deref.rs2
-rw-r--r--src/test/ui/lint/lint-unconditional-recursion.stderr2
-rw-r--r--src/test/ui/moves/move-deref-coercion.rs33
-rw-r--r--src/test/ui/moves/move-deref-coercion.stderr35
-rw-r--r--src/test/ui/no-capture-arc.stderr11
-rw-r--r--src/test/ui/no-reuse-move-arc.stderr11
23 files changed, 250 insertions, 54 deletions
diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs
index 6a9bb8d6c28..46ef5ff7dd8 100644
--- a/compiler/rustc_middle/src/ty/adjustment.rs
+++ b/compiler/rustc_middle/src/ty/adjustment.rs
@@ -4,6 +4,7 @@ use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::lang_items::LangItem;
 use rustc_macros::HashStable;
+use rustc_span::Span;
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)]
 pub enum PointerCast {
@@ -113,6 +114,9 @@ pub enum Adjust<'tcx> {
 pub struct OverloadedDeref<'tcx> {
     pub region: ty::Region<'tcx>,
     pub mutbl: hir::Mutability,
+    /// The `Span` associated with the field access or method call
+    /// that triggered this overloaded deref.
+    pub span: Span,
 }
 
 impl<'tcx> OverloadedDeref<'tcx> {
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index f8627e2f1b6..daa8b649596 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -606,8 +606,11 @@ impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::Adjust<'a> {
 impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::OverloadedDeref<'a> {
     type Lifted = ty::adjustment::OverloadedDeref<'tcx>;
     fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
-        tcx.lift(&self.region)
-            .map(|region| ty::adjustment::OverloadedDeref { region, mutbl: self.mutbl })
+        tcx.lift(&self.region).map(|region| ty::adjustment::OverloadedDeref {
+            region,
+            mutbl: self.mutbl,
+            span: self.span,
+        })
     }
 }
 
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs
index 676065007b7..11122b195c0 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs
@@ -66,7 +66,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         let span = use_spans.args_or_use();
 
         let move_site_vec = self.get_moved_indexes(location, mpi);
-        debug!("report_use_of_moved_or_uninitialized: move_site_vec={:?}", move_site_vec);
+        debug!(
+            "report_use_of_moved_or_uninitialized: move_site_vec={:?} use_spans={:?}",
+            move_site_vec, use_spans
+        );
         let move_out_indices: Vec<_> =
             move_site_vec.iter().map(|move_site| move_site.moi).collect();
 
@@ -229,6 +232,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                                     );
                                 }
                             }
+                            // Deref::deref takes &self, which cannot cause a move
+                            FnSelfUseKind::DerefCoercion { .. } => unreachable!(),
                         }
                     } else {
                         err.span_label(
@@ -355,6 +360,20 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 self.note_type_does_not_implement_copy(&mut err, &note_msg, ty, span, partial_str);
             }
 
+            if let UseSpans::FnSelfUse {
+                kind: FnSelfUseKind::DerefCoercion { deref_target, deref_target_ty },
+                ..
+            } = use_spans
+            {
+                err.note(&format!(
+                    "{} occurs due to deref coercion to `{}`",
+                    desired_action.as_noun(),
+                    deref_target_ty
+                ));
+
+                err.span_note(deref_target, "deref defined here");
+            }
+
             if let Some((_, mut old_err)) =
                 self.move_error_reported.insert(move_out_indices, (used_place, err))
             {
@@ -945,7 +964,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         name: &str,
         borrow: &BorrowData<'tcx>,
         drop_span: Span,
-        borrow_spans: UseSpans,
+        borrow_spans: UseSpans<'tcx>,
         explanation: BorrowExplanation,
     ) -> DiagnosticBuilder<'cx> {
         debug!(
@@ -1146,7 +1165,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         location: Location,
         borrow: &BorrowData<'tcx>,
         drop_span: Span,
-        borrow_spans: UseSpans,
+        borrow_spans: UseSpans<'tcx>,
         proper_span: Span,
         explanation: BorrowExplanation,
     ) -> DiagnosticBuilder<'cx> {
@@ -1274,7 +1293,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
     fn report_escaping_closure_capture(
         &mut self,
-        use_span: UseSpans,
+        use_span: UseSpans<'tcx>,
         var_span: Span,
         fr_name: &RegionName,
         category: ConstraintCategory,
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs
index eaaf202f3bd..eccb6168229 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs
@@ -501,7 +501,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     fn later_use_kind(
         &self,
         borrow: &BorrowData<'tcx>,
-        use_spans: UseSpans,
+        use_spans: UseSpans<'tcx>,
         location: Location,
     ) -> (LaterUseKind, Span) {
         match use_spans {
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
index 3cee32834be..4256f6e39d5 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
@@ -11,7 +11,7 @@ use rustc_middle::mir::{
     PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
 };
 use rustc_middle::ty::print::Print;
-use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt};
+use rustc_middle::ty::{self, DefIdTree, Instance, Ty, TyCtxt};
 use rustc_span::{
     hygiene::{DesugaringKind, ForLoopLoc},
     symbol::sym,
@@ -538,7 +538,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
 /// The span(s) associated to a use of a place.
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub(super) enum UseSpans {
+pub(super) enum UseSpans<'tcx> {
     /// The access is caused by capturing a variable for a closure.
     ClosureUse {
         /// This is true if the captured variable was from a generator.
@@ -558,7 +558,7 @@ pub(super) enum UseSpans {
         fn_call_span: Span,
         /// The definition span of the method being called
         fn_span: Span,
-        kind: FnSelfUseKind,
+        kind: FnSelfUseKind<'tcx>,
     },
     /// This access is caused by a `match` or `if let` pattern.
     PatUse(Span),
@@ -567,22 +567,32 @@ pub(super) enum UseSpans {
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub(super) enum FnSelfUseKind {
+pub(super) enum FnSelfUseKind<'tcx> {
     /// A normal method call of the form `receiver.foo(a, b, c)`
     Normal { self_arg: Ident, implicit_into_iter: bool },
     /// A call to `FnOnce::call_once`, desugared from `my_closure(a, b, c)`
     FnOnceCall,
     /// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`)
     Operator { self_arg: Ident },
+    DerefCoercion {
+        /// The `Span` of the `Target` associated type
+        /// in the `Deref` impl we are using.
+        deref_target: Span,
+        /// The type `T::Deref` we are dereferencing to
+        deref_target_ty: Ty<'tcx>,
+    },
 }
 
-impl UseSpans {
+impl UseSpans<'_> {
     pub(super) fn args_or_use(self) -> Span {
         match self {
             UseSpans::ClosureUse { args_span: span, .. }
             | UseSpans::PatUse(span)
-            | UseSpans::FnSelfUse { var_span: span, .. }
             | UseSpans::OtherUse(span) => span,
+            UseSpans::FnSelfUse {
+                fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, ..
+            } => fn_call_span,
+            UseSpans::FnSelfUse { var_span, .. } => var_span,
         }
     }
 
@@ -590,8 +600,11 @@ impl UseSpans {
         match self {
             UseSpans::ClosureUse { var_span: span, .. }
             | UseSpans::PatUse(span)
-            | UseSpans::FnSelfUse { var_span: span, .. }
             | UseSpans::OtherUse(span) => span,
+            UseSpans::FnSelfUse {
+                fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, ..
+            } => fn_call_span,
+            UseSpans::FnSelfUse { var_span, .. } => var_span,
         }
     }
 
@@ -754,7 +767,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         &self,
         moved_place: PlaceRef<'tcx>, // Could also be an upvar.
         location: Location,
-    ) -> UseSpans {
+    ) -> UseSpans<'tcx> {
         use self::UseSpans::*;
 
         let stmt = match self.body[location.block].statements.get(location.statement_index) {
@@ -809,36 +822,64 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             kind: TerminatorKind::Call { fn_span, from_hir_call, .. }, ..
         }) = &self.body[location.block].terminator
         {
-            let method_did = if let Some(method_did) =
+            let (method_did, method_substs) = if let Some(info) =
                 crate::util::find_self_call(self.infcx.tcx, &self.body, target_temp, location.block)
             {
-                method_did
+                info
             } else {
                 return normal_ret;
             };
 
             let tcx = self.infcx.tcx;
-
             let parent = tcx.parent(method_did);
             let is_fn_once = parent == tcx.lang_items().fn_once_trait();
             let is_operator = !from_hir_call
                 && parent.map_or(false, |p| tcx.lang_items().group(LangItemGroup::Op).contains(&p));
+            let is_deref = !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did);
             let fn_call_span = *fn_span;
 
             let self_arg = tcx.fn_arg_names(method_did)[0];
 
+            debug!(
+                "terminator = {:?} from_hir_call={:?}",
+                self.body[location.block].terminator, from_hir_call
+            );
+
+            // Check for a 'special' use of 'self' -
+            // an FnOnce call, an operator (e.g. `<<`), or a
+            // deref coercion.
             let kind = if is_fn_once {
-                FnSelfUseKind::FnOnceCall
+                Some(FnSelfUseKind::FnOnceCall)
             } else if is_operator {
-                FnSelfUseKind::Operator { self_arg }
+                Some(FnSelfUseKind::Operator { self_arg })
+            } else if is_deref {
+                let deref_target =
+                    tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
+                        Instance::resolve(tcx, self.param_env, deref_target, method_substs)
+                            .transpose()
+                    });
+                if let Some(Ok(instance)) = deref_target {
+                    let deref_target_ty = instance.ty(tcx, self.param_env);
+                    Some(FnSelfUseKind::DerefCoercion {
+                        deref_target: tcx.def_span(instance.def_id()),
+                        deref_target_ty,
+                    })
+                } else {
+                    None
+                }
             } else {
+                None
+            };
+
+            let kind = kind.unwrap_or_else(|| {
+                // This isn't a 'special' use of `self`
                 debug!("move_spans: method_did={:?}, fn_call_span={:?}", method_did, fn_call_span);
                 let implicit_into_iter = matches!(
                     fn_call_span.desugaring_kind(),
                     Some(DesugaringKind::ForLoop(ForLoopLoc::IntoIter))
                 );
                 FnSelfUseKind::Normal { self_arg, implicit_into_iter }
-            };
+            });
 
             return FnSelfUse {
                 var_span: stmt.source_info.span,
@@ -859,7 +900,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     /// and its usage of the local assigned at `location`.
     /// This is done by searching in statements succeeding `location`
     /// and originating from `maybe_closure_span`.
-    pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans {
+    pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans<'tcx> {
         use self::UseSpans::*;
         debug!("borrow_spans: use_span={:?} location={:?}", use_span, location);
 
@@ -963,7 +1004,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
     /// Helper to retrieve span(s) of given borrow from the current MIR
     /// representation
-    pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans {
+    pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans<'tcx> {
         let span = self.body.source_info(borrow.reserve_location).span;
         self.borrow_spans(span, borrow.reserve_location)
     }
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs
index 6cf1cf20b5a..e256fb55b12 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs
@@ -47,7 +47,7 @@ enum GroupedMoveError<'tcx> {
     // Everything that isn't from pattern matching.
     OtherIllegalMove {
         original_path: Place<'tcx>,
-        use_spans: UseSpans,
+        use_spans: UseSpans<'tcx>,
         kind: IllegalMoveOriginKind<'tcx>,
     },
 }
@@ -222,7 +222,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         let (mut err, err_span) = {
             let (span, use_spans, original_path, kind): (
                 Span,
-                Option<UseSpans>,
+                Option<UseSpans<'tcx>>,
                 Place<'tcx>,
                 &IllegalMoveOriginKind<'_>,
             ) = match error {
@@ -291,7 +291,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         move_place: Place<'tcx>,
         deref_target_place: Place<'tcx>,
         span: Span,
-        use_spans: Option<UseSpans>,
+        use_spans: Option<UseSpans<'tcx>>,
     ) -> DiagnosticBuilder<'a> {
         // Inspect the type of the content behind the
         // borrow to provide feedback about why this
diff --git a/compiler/rustc_mir/src/borrow_check/mod.rs b/compiler/rustc_mir/src/borrow_check/mod.rs
index acd9e3dcf3f..64ad0627720 100644
--- a/compiler/rustc_mir/src/borrow_check/mod.rs
+++ b/compiler/rustc_mir/src/borrow_check/mod.rs
@@ -17,7 +17,7 @@ use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind
 use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind};
 use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind};
 use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::{self, InstanceDef, RegionVid, TyCtxt};
+use rustc_middle::ty::{self, InstanceDef, ParamEnv, RegionVid, TyCtxt};
 use rustc_session::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT, UNUSED_MUT};
 use rustc_span::{Span, Symbol, DUMMY_SP};
 
@@ -287,6 +287,7 @@ fn do_mir_borrowck<'a, 'tcx>(
         if let Err((move_data, move_errors)) = move_data_results {
             let mut promoted_mbcx = MirBorrowckCtxt {
                 infcx,
+                param_env,
                 body: promoted_body,
                 mir_def_id: def.did,
                 move_data: &move_data,
@@ -320,6 +321,7 @@ fn do_mir_borrowck<'a, 'tcx>(
 
     let mut mbcx = MirBorrowckCtxt {
         infcx,
+        param_env,
         body,
         mir_def_id: def.did,
         move_data: &mdpe.move_data,
@@ -473,6 +475,7 @@ fn do_mir_borrowck<'a, 'tcx>(
 
 crate struct MirBorrowckCtxt<'cx, 'tcx> {
     crate infcx: &'cx InferCtxt<'cx, 'tcx>,
+    param_env: ParamEnv<'tcx>,
     body: &'cx Body<'tcx>,
     mir_def_id: LocalDefId,
     move_data: &'cx MoveData<'tcx>,
diff --git a/compiler/rustc_mir/src/transform/check_const_item_mutation.rs b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs
index 589268e39bd..70c1aed0957 100644
--- a/compiler/rustc_mir/src/transform/check_const_item_mutation.rs
+++ b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs
@@ -101,7 +101,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ConstMutationChecker<'a, 'tcx> {
                         .note("each usage of a `const` item creates a new temporary")
                         .note("the mutable reference will refer to this temporary, not the original `const` item");
 
-                    if let Some(method_did) = method_did {
+                    if let Some((method_did, _substs)) = method_did {
                         lint.span_note(self.tcx.def_span(method_did), "mutable reference created due to call to this method");
                     }
 
diff --git a/compiler/rustc_mir/src/util/find_self_call.rs b/compiler/rustc_mir/src/util/find_self_call.rs
index 049b5f01214..5b146eeb87c 100644
--- a/compiler/rustc_mir/src/util/find_self_call.rs
+++ b/compiler/rustc_mir/src/util/find_self_call.rs
@@ -1,30 +1,31 @@
 use rustc_middle::mir::*;
+use rustc_middle::ty::subst::SubstsRef;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::def_id::DefId;
 
 /// Checks if the specified `local` is used as the `self` prameter of a method call
 /// in the provided `BasicBlock`. If it is, then the `DefId` of the called method is
 /// returned.
-pub fn find_self_call(
-    tcx: TyCtxt<'_>,
-    body: &Body<'_>,
+pub fn find_self_call<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    body: &Body<'tcx>,
     local: Local,
     block: BasicBlock,
-) -> Option<DefId> {
+) -> Option<(DefId, SubstsRef<'tcx>)> {
     debug!("find_self_call(local={:?}): terminator={:?}", local, &body[block].terminator);
     if let Some(Terminator { kind: TerminatorKind::Call { func, args, .. }, .. }) =
         &body[block].terminator
     {
         debug!("find_self_call: func={:?}", func);
         if let Operand::Constant(box Constant { literal: ty::Const { ty, .. }, .. }) = func {
-            if let ty::FnDef(def_id, _) = *ty.kind() {
+            if let ty::FnDef(def_id, substs) = *ty.kind() {
                 if let Some(ty::AssocItem { fn_has_self_parameter: true, .. }) =
                     tcx.opt_associated_item(def_id)
                 {
                     debug!("find_self_call: args={:?}", args);
                     if let [Operand::Move(self_place) | Operand::Copy(self_place), ..] = **args {
                         if self_place.as_local() == Some(local) {
-                            return Some(def_id);
+                            return Some((def_id, substs));
                         }
                     }
                 }
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index 70c7abc2654..13e69474cfb 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -117,7 +117,14 @@ fn apply_adjustment<'a, 'tcx>(
                 },
             };
 
-            overloaded_place(cx, hir_expr, adjustment.target, Some(call), vec![expr.to_ref()])
+            overloaded_place(
+                cx,
+                hir_expr,
+                adjustment.target,
+                Some(call),
+                vec![expr.to_ref()],
+                deref.span,
+            )
         }
         Adjust::Borrow(AutoBorrow::Ref(_, m)) => {
             ExprKind::Borrow { borrow_kind: m.to_borrow_kind(), arg: expr.to_ref() }
@@ -277,7 +284,14 @@ fn make_mirror_unadjusted<'a, 'tcx>(
 
         hir::ExprKind::Index(ref lhs, ref index) => {
             if cx.typeck_results().is_method_call(expr) {
-                overloaded_place(cx, expr, expr_ty, None, vec![lhs.to_ref(), index.to_ref()])
+                overloaded_place(
+                    cx,
+                    expr,
+                    expr_ty,
+                    None,
+                    vec![lhs.to_ref(), index.to_ref()],
+                    expr.span,
+                )
             } else {
                 ExprKind::Index { lhs: lhs.to_ref(), index: index.to_ref() }
             }
@@ -285,7 +299,7 @@ fn make_mirror_unadjusted<'a, 'tcx>(
 
         hir::ExprKind::Unary(hir::UnOp::UnDeref, ref arg) => {
             if cx.typeck_results().is_method_call(expr) {
-                overloaded_place(cx, expr, expr_ty, None, vec![arg.to_ref()])
+                overloaded_place(cx, expr, expr_ty, None, vec![arg.to_ref()], expr.span)
             } else {
                 ExprKind::Deref { arg: arg.to_ref() }
             }
@@ -1025,6 +1039,7 @@ fn overloaded_place<'a, 'tcx>(
     place_ty: Ty<'tcx>,
     overloaded_callee: Option<(DefId, SubstsRef<'tcx>)>,
     args: Vec<ExprRef<'tcx>>,
+    span: Span,
 ) -> ExprKind<'tcx> {
     // For an overloaded *x or x[y] expression of type T, the method
     // call returns an &T and we must add the deref so that the types
@@ -1040,24 +1055,24 @@ fn overloaded_place<'a, 'tcx>(
     // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`.
     let (region, mutbl) = match *recv_ty.kind() {
         ty::Ref(region, _, mutbl) => (region, mutbl),
-        _ => span_bug!(expr.span, "overloaded_place: receiver is not a reference"),
+        _ => span_bug!(span, "overloaded_place: receiver is not a reference"),
     };
     let ref_ty = cx.tcx.mk_ref(region, ty::TypeAndMut { ty: place_ty, mutbl });
 
     // construct the complete expression `foo()` for the overloaded call,
     // which will yield the &T type
     let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id);
-    let fun = method_callee(cx, expr, expr.span, overloaded_callee);
+    let fun = method_callee(cx, expr, span, overloaded_callee);
     let ref_expr = Expr {
         temp_lifetime,
         ty: ref_ty,
-        span: expr.span,
+        span,
         kind: ExprKind::Call {
             ty: fun.ty,
             fun: fun.to_ref(),
             args,
             from_hir_call: false,
-            fn_span: expr.span,
+            fn_span: span,
         },
     };
 
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 407663e5757..94f795b11ea 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -416,7 +416,9 @@ symbols! {
         deny,
         deprecated,
         deref,
+        deref_method,
         deref_mut,
+        deref_target,
         derive,
         diagnostic,
         direct,
diff --git a/compiler/rustc_trait_selection/src/autoderef.rs b/compiler/rustc_trait_selection/src/autoderef.rs
index 02eefe56223..b9c5123e49a 100644
--- a/compiler/rustc_trait_selection/src/autoderef.rs
+++ b/compiler/rustc_trait_selection/src/autoderef.rs
@@ -27,6 +27,7 @@ pub struct Autoderef<'a, 'tcx> {
     // Meta infos:
     infcx: &'a InferCtxt<'a, 'tcx>,
     span: Span,
+    overloaded_span: Span,
     body_id: hir::HirId,
     param_env: ty::ParamEnv<'tcx>,
 
@@ -98,10 +99,12 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
         body_id: hir::HirId,
         span: Span,
         base_ty: Ty<'tcx>,
+        overloaded_span: Span,
     ) -> Autoderef<'a, 'tcx> {
         Autoderef {
             infcx,
             span,
+            overloaded_span,
             body_id,
             param_env,
             state: AutoderefSnapshot {
@@ -190,6 +193,10 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
         self.span
     }
 
+    pub fn overloaded_span(&self) -> Span {
+        self.overloaded_span
+    }
+
     pub fn reached_recursion_limit(&self) -> bool {
         self.state.reached_recursion_limit
     }
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 82e809d014d..90a8d9634ae 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -483,7 +483,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         };
 
         if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() {
-            let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty);
+            let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty, span);
             if let Some(steps) = autoderef.find_map(|(ty, steps)| {
                 // Re-add the `&`
                 let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl });
diff --git a/compiler/rustc_typeck/src/check/autoderef.rs b/compiler/rustc_typeck/src/check/autoderef.rs
index 17364897bde..59c366ad7d7 100644
--- a/compiler/rustc_typeck/src/check/autoderef.rs
+++ b/compiler/rustc_typeck/src/check/autoderef.rs
@@ -12,7 +12,18 @@ use std::iter;
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'tcx> {
-        Autoderef::new(self, self.param_env, self.body_id, span, base_ty)
+        Autoderef::new(self, self.param_env, self.body_id, span, base_ty, span)
+    }
+
+    /// Like `autoderef`, but provides a custom `Span` to use for calls to
+    /// an overloaded `Deref` operator
+    pub fn autoderef_overloaded_span(
+        &'a self,
+        span: Span,
+        base_ty: Ty<'tcx>,
+        overloaded_span: Span,
+    ) -> Autoderef<'a, 'tcx> {
+        Autoderef::new(self, self.param_env, self.body_id, span, base_ty, overloaded_span)
     }
 
     pub fn try_overloaded_deref(
@@ -44,7 +55,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         |InferOk { value: method, obligations: o }| {
                             obligations.extend(o);
                             if let ty::Ref(region, _, mutbl) = *method.sig.output().kind() {
-                                Some(OverloadedDeref { region, mutbl })
+                                Some(OverloadedDeref {
+                                    region,
+                                    mutbl,
+                                    span: autoderef.overloaded_span(),
+                                })
                             } else {
                                 None
                             }
diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_typeck/src/check/method/confirm.rs
index ae338cccf79..fd2700b85e2 100644
--- a/compiler/rustc_typeck/src/check/method/confirm.rs
+++ b/compiler/rustc_typeck/src/check/method/confirm.rs
@@ -137,7 +137,8 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
     ) -> Ty<'tcx> {
         // Commit the autoderefs by calling `autoderef` again, but this
         // time writing the results into the various typeck results.
-        let mut autoderef = self.autoderef(self.span, unadjusted_self_ty);
+        let mut autoderef =
+            self.autoderef_overloaded_span(self.span, unadjusted_self_ty, self.call_expr.span);
         let (_, n) = match autoderef.nth(pick.autoderefs) {
             Some(n) => n,
             None => {
diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs
index 14d80fded71..37e8e82e60a 100644
--- a/compiler/rustc_typeck/src/check/method/probe.rs
+++ b/compiler/rustc_typeck/src/check/method/probe.rs
@@ -446,9 +446,10 @@ fn method_autoderef_steps<'tcx>(
     tcx.infer_ctxt().enter_with_canonical(DUMMY_SP, &goal, |ref infcx, goal, inference_vars| {
         let ParamEnvAnd { param_env, value: self_ty } = goal;
 
-        let mut autoderef = Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty)
-            .include_raw_pointers()
-            .silence_errors();
+        let mut autoderef =
+            Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty, DUMMY_SP)
+                .include_raw_pointers()
+                .silence_errors();
         let mut reached_raw_pointer = false;
         let mut steps: Vec<_> = autoderef
             .by_ref()
diff --git a/compiler/rustc_typeck/src/check/place_op.rs b/compiler/rustc_typeck/src/check/place_op.rs
index aed2af20e52..502cb562385 100644
--- a/compiler/rustc_typeck/src/check/place_op.rs
+++ b/compiler/rustc_typeck/src/check/place_op.rs
@@ -242,7 +242,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         ) {
                             let method = self.register_infer_ok_obligations(ok);
                             if let ty::Ref(region, _, mutbl) = *method.sig.output().kind() {
-                                *deref = OverloadedDeref { region, mutbl };
+                                *deref = OverloadedDeref { region, mutbl, span: deref.span };
                             }
                             // If this is a union field, also throw an error for `DerefMut` of `ManuallyDrop` (see RFC 2514).
                             // This helps avoid accidental drops.
diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs
index d6c097eee17..245152e5490 100644
--- a/library/core/src/ops/deref.rs
+++ b/library/core/src/ops/deref.rs
@@ -63,11 +63,13 @@
 pub trait Deref {
     /// The resulting type after dereferencing.
     #[stable(feature = "rust1", since = "1.0.0")]
+    #[rustc_diagnostic_item = "deref_target"]
     type Target: ?Sized;
 
     /// Dereferences the value.
     #[must_use]
     #[stable(feature = "rust1", since = "1.0.0")]
+    #[rustc_diagnostic_item = "deref_method"]
     fn deref(&self) -> &Self::Target;
 }
 
diff --git a/src/test/ui/lint/lint-unconditional-recursion.stderr b/src/test/ui/lint/lint-unconditional-recursion.stderr
index 1770d71e2e2..fb884e31299 100644
--- a/src/test/ui/lint/lint-unconditional-recursion.stderr
+++ b/src/test/ui/lint/lint-unconditional-recursion.stderr
@@ -149,7 +149,7 @@ error: function cannot return without recursing
 LL |     fn deref(&self) -> &Baz {
    |     ^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
 LL |         self.as_ref()
-   |         ---- recursive call site
+   |         ------------- recursive call site
    |
    = help: a `loop` may express intention better if this is on purpose
 
diff --git a/src/test/ui/moves/move-deref-coercion.rs b/src/test/ui/moves/move-deref-coercion.rs
new file mode 100644
index 00000000000..41154388f56
--- /dev/null
+++ b/src/test/ui/moves/move-deref-coercion.rs
@@ -0,0 +1,33 @@
+use std::ops::Deref;
+
+struct NotCopy {
+    inner: bool
+}
+
+impl NotCopy {
+    fn inner_method(&self) {}
+}
+
+struct Foo {
+    first: NotCopy,
+    second: NotCopy
+}
+
+impl Deref for Foo {
+    type Target = NotCopy;
+    fn deref(&self) -> &NotCopy {
+        &self.second
+    }
+}
+
+fn use_field(val: Foo) {
+    let _val = val.first;
+    val.inner; //~ ERROR borrow of
+}
+
+fn use_method(val: Foo) {
+    let _val = val.first;
+    val.inner_method(); //~ ERROR borrow of
+}
+
+fn main() {}
diff --git a/src/test/ui/moves/move-deref-coercion.stderr b/src/test/ui/moves/move-deref-coercion.stderr
new file mode 100644
index 00000000000..e3bdf6d7832
--- /dev/null
+++ b/src/test/ui/moves/move-deref-coercion.stderr
@@ -0,0 +1,35 @@
+error[E0382]: borrow of partially moved value: `val`
+  --> $DIR/move-deref-coercion.rs:25:5
+   |
+LL |     let _val = val.first;
+   |                --------- value partially moved here
+LL |     val.inner;
+   |     ^^^^^^^^^ value borrowed here after partial move
+   |
+   = note: partial move occurs because `val.first` has type `NotCopy`, which does not implement the `Copy` trait
+   = note: borrow occurs due to deref coercion to `NotCopy`
+note: deref defined here
+  --> $DIR/move-deref-coercion.rs:17:5
+   |
+LL |     type Target = NotCopy;
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0382]: borrow of partially moved value: `val`
+  --> $DIR/move-deref-coercion.rs:30:5
+   |
+LL |     let _val = val.first;
+   |                --------- value partially moved here
+LL |     val.inner_method();
+   |     ^^^^^^^^^^^^^^^^^^ value borrowed here after partial move
+   |
+   = note: partial move occurs because `val.first` has type `NotCopy`, which does not implement the `Copy` trait
+   = note: borrow occurs due to deref coercion to `NotCopy`
+note: deref defined here
+  --> $DIR/move-deref-coercion.rs:17:5
+   |
+LL |     type Target = NotCopy;
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/no-capture-arc.stderr b/src/test/ui/no-capture-arc.stderr
index 0081841fec0..37032e73f19 100644
--- a/src/test/ui/no-capture-arc.stderr
+++ b/src/test/ui/no-capture-arc.stderr
@@ -1,5 +1,5 @@
 error[E0382]: borrow of moved value: `arc_v`
-  --> $DIR/no-capture-arc.rs:14:18
+  --> $DIR/no-capture-arc.rs:14:16
    |
 LL |     let arc_v = Arc::new(v);
    |         ----- move occurs because `arc_v` has type `Arc<Vec<i32>>`, which does not implement the `Copy` trait
@@ -10,7 +10,14 @@ LL |         assert_eq!((*arc_v)[3], 4);
    |                      ----- variable moved due to use in closure
 ...
 LL |     assert_eq!((*arc_v)[2], 3);
-   |                  ^^^^^ value borrowed here after move
+   |                ^^^^^^^^ value borrowed here after move
+   |
+   = note: borrow occurs due to deref coercion to `Vec<i32>`
+note: deref defined here
+  --> $SRC_DIR/alloc/src/sync.rs:LL:COL
+   |
+LL |     type Target = T;
+   |     ^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/no-reuse-move-arc.stderr b/src/test/ui/no-reuse-move-arc.stderr
index bb4e0871224..6f37d4c9d86 100644
--- a/src/test/ui/no-reuse-move-arc.stderr
+++ b/src/test/ui/no-reuse-move-arc.stderr
@@ -1,5 +1,5 @@
 error[E0382]: borrow of moved value: `arc_v`
-  --> $DIR/no-reuse-move-arc.rs:12:18
+  --> $DIR/no-reuse-move-arc.rs:12:16
    |
 LL |     let arc_v = Arc::new(v);
    |         ----- move occurs because `arc_v` has type `Arc<Vec<i32>>`, which does not implement the `Copy` trait
@@ -10,7 +10,14 @@ LL |         assert_eq!((*arc_v)[3], 4);
    |                      ----- variable moved due to use in closure
 ...
 LL |     assert_eq!((*arc_v)[2], 3);
-   |                  ^^^^^ value borrowed here after move
+   |                ^^^^^^^^ value borrowed here after move
+   |
+   = note: borrow occurs due to deref coercion to `Vec<i32>`
+note: deref defined here
+  --> $SRC_DIR/alloc/src/sync.rs:LL:COL
+   |
+LL |     type Target = T;
+   |     ^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error