about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-03-26 02:22:26 +0000
committerbors <bors@rust-lang.org>2020-03-26 02:22:26 +0000
commita17dd3608477a47741f97b907a61b6d81524ba4e (patch)
tree367485267991beccfc9abe073364aa3a629b48a5
parente4519e2b966aaf7e2eb0105d0a1593fdb6e0a3a5 (diff)
parent16e9d3f0ca61e820bbcb5945ff257b3a0fc8f55a (diff)
downloadrust-a17dd3608477a47741f97b907a61b6d81524ba4e.tar.gz
rust-a17dd3608477a47741f97b907a61b6d81524ba4e.zip
Auto merge of #70415 - Centril:rollup-1zttfvl, r=Centril
Rollup of 6 pull requests

Successful merges:

 - #69866 (Rename `def_span` to `guess_head_span`)
 - #69878 (Tweak chained operators diagnostic)
 - #70375 (avoid catching InterpError)
 - #70386 (typeck: minor pattern typing improvements)
 - #70389 (borrowck: prefer "value" over "`_`" in diagnostics)
 - #70395 (Update cargo.)

Failed merges:

r? @ghost
-rw-r--r--src/librustc/mir/interpret/allocation.rs16
-rw-r--r--src/librustc/traits/mod.rs4
-rw-r--r--src/librustc/ty/query/plumbing.rs5
-rw-r--r--src/librustc_ast_passes/ast_validation.rs4
-rw-r--r--src/librustc_ast_passes/feature_gate.rs2
-rw-r--r--src/librustc_builtin_macros/proc_macro_harness.rs10
-rw-r--r--src/librustc_expand/mbe/macro_rules.rs2
-rw-r--r--src/librustc_infer/infer/error_reporting/mod.rs6
-rw-r--r--src/librustc_infer/traits/error_reporting/mod.rs6
-rw-r--r--src/librustc_lint/builtin.rs16
-rw-r--r--src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs89
-rw-r--r--src/librustc_mir/borrow_check/diagnostics/mod.rs19
-rw-r--r--src/librustc_mir/borrow_check/diagnostics/move_errors.rs24
-rw-r--r--src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs5
-rw-r--r--src/librustc_mir/transform/check_unsafety.rs4
-rw-r--r--src/librustc_mir/util/borrowck_errors.rs35
-rw-r--r--src/librustc_mir_build/lints.rs2
-rw-r--r--src/librustc_parse/parser/diagnostics.rs155
-rw-r--r--src/librustc_parse/parser/item.rs2
-rw-r--r--src/librustc_passes/dead.rs4
-rw-r--r--src/librustc_resolve/diagnostics.rs6
-rw-r--r--src/librustc_resolve/imports.rs2
-rw-r--r--src/librustc_resolve/lib.rs5
-rw-r--r--src/librustc_span/source_map.rs9
-rw-r--r--src/librustc_trait_selection/traits/error_reporting/mod.rs19
-rw-r--r--src/librustc_trait_selection/traits/error_reporting/suggestions.rs2
-rw-r--r--src/librustc_trait_selection/traits/specialize/mod.rs4
-rw-r--r--src/librustc_trait_selection/traits/wf.rs2
-rw-r--r--src/librustc_typeck/astconv.rs2
-rw-r--r--src/librustc_typeck/check/_match.rs2
-rw-r--r--src/librustc_typeck/check/compare_method.rs6
-rw-r--r--src/librustc_typeck/check/method/suggest.rs18
-rw-r--r--src/librustc_typeck/check/mod.rs12
-rw-r--r--src/librustc_typeck/check/pat.rs39
-rw-r--r--src/librustc_typeck/coherence/mod.rs2
-rw-r--r--src/librustc_typeck/coherence/orphan.rs2
-rw-r--r--src/test/ui/did_you_mean/issue-40396.stderr22
-rw-r--r--src/test/ui/parser/chained-comparison-suggestion.rs13
-rw-r--r--src/test/ui/parser/chained-comparison-suggestion.stderr125
-rw-r--r--src/test/ui/parser/require-parens-for-chained-comparison.rs6
-rw-r--r--src/test/ui/parser/require-parens-for-chained-comparison.stderr47
-rw-r--r--src/test/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.rs8
-rw-r--r--src/test/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.stderr8
-rw-r--r--src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.rs18
-rw-r--r--src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr18
-rw-r--r--src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.rs14
-rw-r--r--src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.stderr12
-rw-r--r--src/test/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.rs2
-rw-r--r--src/test/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.stderr2
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/struct.stderr15
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/variant.stderr10
m---------src/tools/cargo0
52 files changed, 473 insertions, 389 deletions
diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs
index 26b9e1be2f5..ada02ceb5cb 100644
--- a/src/librustc/mir/interpret/allocation.rs
+++ b/src/librustc/mir/interpret/allocation.rs
@@ -367,7 +367,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> {
         let bytes = self.get_bytes_with_undef_and_ptr(cx, ptr, size)?;
         // Undef check happens *after* we established that the alignment is correct.
         // We must not return `Ok()` for unaligned pointers!
-        if self.check_defined(ptr, size).is_err() {
+        if self.is_defined(ptr, size).is_err() {
             // This inflates undefined bytes to the entire scalar, even if only a few
             // bytes are undefined.
             return Ok(ScalarMaybeUndef::Undef);
@@ -552,13 +552,19 @@ impl<'tcx, Tag: Copy, Extra> Allocation<Tag, Extra> {
 }
 
 /// Undefined bytes.
-impl<'tcx, Tag, Extra> Allocation<Tag, Extra> {
+impl<'tcx, Tag: Copy, Extra> Allocation<Tag, Extra> {
+    /// Checks whether the given range  is entirely defined.
+    ///
+    /// Returns `Ok(())` if it's defined. Otherwise returns the index of the byte
+    /// at which the first undefined access begins.
+    fn is_defined(&self, ptr: Pointer<Tag>, size: Size) -> Result<(), Size> {
+        self.undef_mask.is_range_defined(ptr.offset, ptr.offset + size) // `Size` addition
+    }
+
     /// Checks that a range of bytes is defined. If not, returns the `ReadUndefBytes`
     /// error which will report the first byte which is undefined.
-    #[inline]
     fn check_defined(&self, ptr: Pointer<Tag>, size: Size) -> InterpResult<'tcx> {
-        self.undef_mask
-            .is_range_defined(ptr.offset, ptr.offset + size) // `Size` addition
+        self.is_defined(ptr, size)
             .or_else(|idx| throw_ub!(InvalidUndefBytes(Some(Pointer::new(ptr.alloc_id, idx)))))
     }
 
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index 6ebcc8b0754..c129b574fd3 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -108,7 +108,9 @@ impl<'tcx> ObligationCause<'tcx> {
         match self.code {
             ObligationCauseCode::CompareImplMethodObligation { .. }
             | ObligationCauseCode::MainFunctionType
-            | ObligationCauseCode::StartFunctionType => tcx.sess.source_map().def_span(self.span),
+            | ObligationCauseCode::StartFunctionType => {
+                tcx.sess.source_map().guess_head_span(self.span)
+            }
             ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
                 arm_span,
                 ..
diff --git a/src/librustc/ty/query/plumbing.rs b/src/librustc/ty/query/plumbing.rs
index 69bcc934155..c0cc119c5a1 100644
--- a/src/librustc/ty/query/plumbing.rs
+++ b/src/librustc/ty/query/plumbing.rs
@@ -388,7 +388,7 @@ impl<'tcx> TyCtxt<'tcx> {
         assert!(!stack.is_empty());
 
         let fix_span = |span: Span, query: &Query<'tcx>| {
-            self.sess.source_map().def_span(query.default_span(self, span))
+            self.sess.source_map().guess_head_span(query.default_span(self, span))
         };
 
         // Disable naming impls with types in this path, since that
@@ -456,7 +456,8 @@ impl<'tcx> TyCtxt<'tcx> {
                             query_info.info.query.describe(icx.tcx)
                         ),
                     );
-                    diag.span = icx.tcx.sess.source_map().def_span(query_info.info.span).into();
+                    diag.span =
+                        icx.tcx.sess.source_map().guess_head_span(query_info.info.span).into();
                     handler.force_print_diagnostic(diag);
 
                     current_query = query_info.job.parent;
diff --git a/src/librustc_ast_passes/ast_validation.rs b/src/librustc_ast_passes/ast_validation.rs
index b4363778094..c03c44fc634 100644
--- a/src/librustc_ast_passes/ast_validation.rs
+++ b/src/librustc_ast_passes/ast_validation.rs
@@ -402,7 +402,7 @@ impl<'a> AstValidator<'a> {
 
     fn check_defaultness(&self, span: Span, defaultness: Defaultness) {
         if let Defaultness::Default(def_span) = defaultness {
-            let span = self.session.source_map().def_span(span);
+            let span = self.session.source_map().guess_head_span(span);
             self.err_handler()
                 .struct_span_err(span, "`default` is only allowed on items in `impl` definitions")
                 .span_label(def_span, "`default` because of this")
@@ -517,7 +517,7 @@ impl<'a> AstValidator<'a> {
     }
 
     fn current_extern_span(&self) -> Span {
-        self.session.source_map().def_span(self.extern_mod.unwrap().span)
+        self.session.source_map().guess_head_span(self.extern_mod.unwrap().span)
     }
 
     /// An `fn` in `extern { ... }` cannot have qualfiers, e.g. `async fn`.
diff --git a/src/librustc_ast_passes/feature_gate.rs b/src/librustc_ast_passes/feature_gate.rs
index 364c86bd99b..5cd7b58e14e 100644
--- a/src/librustc_ast_passes/feature_gate.rs
+++ b/src/librustc_ast_passes/feature_gate.rs
@@ -257,7 +257,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
             gate_feature_post!(
                 &self,
                 non_ascii_idents,
-                self.parse_sess.source_map().def_span(sp),
+                self.parse_sess.source_map().guess_head_span(sp),
                 "non-ascii idents are not fully supported"
             );
         }
diff --git a/src/librustc_builtin_macros/proc_macro_harness.rs b/src/librustc_builtin_macros/proc_macro_harness.rs
index 6540bcc4156..735de4f0a5b 100644
--- a/src/librustc_builtin_macros/proc_macro_harness.rs
+++ b/src/librustc_builtin_macros/proc_macro_harness.rs
@@ -198,7 +198,7 @@ impl<'a> CollectProcMacros<'a> {
             } else {
                 "functions tagged with `#[proc_macro_derive]` must be `pub`"
             };
-            self.handler.span_err(self.source_map.def_span(item.span), msg);
+            self.handler.span_err(self.source_map.guess_head_span(item.span), msg);
         }
     }
 
@@ -217,7 +217,7 @@ impl<'a> CollectProcMacros<'a> {
             } else {
                 "functions tagged with `#[proc_macro_attribute]` must be `pub`"
             };
-            self.handler.span_err(self.source_map.def_span(item.span), msg);
+            self.handler.span_err(self.source_map.guess_head_span(item.span), msg);
         }
     }
 
@@ -236,7 +236,7 @@ impl<'a> CollectProcMacros<'a> {
             } else {
                 "functions tagged with `#[proc_macro]` must be `pub`"
             };
-            self.handler.span_err(self.source_map.def_span(item.span), msg);
+            self.handler.span_err(self.source_map.guess_head_span(item.span), msg);
         }
     }
 }
@@ -247,7 +247,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
             if self.is_proc_macro_crate && attr::contains_name(&item.attrs, sym::macro_export) {
                 let msg =
                     "cannot export macro_rules! macros from a `proc-macro` crate type currently";
-                self.handler.span_err(self.source_map.def_span(item.span), msg);
+                self.handler.span_err(self.source_map.guess_head_span(item.span), msg);
             }
         }
 
@@ -298,7 +298,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
 
         let attr = match found_attr {
             None => {
-                self.check_not_pub_in_root(&item.vis, self.source_map.def_span(item.span));
+                self.check_not_pub_in_root(&item.vis, self.source_map.guess_head_span(item.span));
                 let prev_in_root = mem::replace(&mut self.in_root, false);
                 visit::walk_item(self, item);
                 self.in_root = prev_in_root;
diff --git a/src/librustc_expand/mbe/macro_rules.rs b/src/librustc_expand/mbe/macro_rules.rs
index 859362b5e29..b9477be57dd 100644
--- a/src/librustc_expand/mbe/macro_rules.rs
+++ b/src/librustc_expand/mbe/macro_rules.rs
@@ -326,7 +326,7 @@ fn generic_extension<'cx>(
     let mut err = cx.struct_span_err(span, &parse_failure_msg(&token));
     err.span_label(span, label);
     if !def_span.is_dummy() && !cx.source_map().is_imported(def_span) {
-        err.span_label(cx.source_map().def_span(def_span), "when calling this macro");
+        err.span_label(cx.source_map().guess_head_span(def_span), "when calling this macro");
     }
 
     // Check whether there's a missing comma in this macro call, like `println!("{}" a);`
diff --git a/src/librustc_infer/infer/error_reporting/mod.rs b/src/librustc_infer/infer/error_reporting/mod.rs
index 9c22c38583c..129bc9e22a6 100644
--- a/src/librustc_infer/infer/error_reporting/mod.rs
+++ b/src/librustc_infer/infer/error_reporting/mod.rs
@@ -200,7 +200,7 @@ fn msg_span_from_early_bound_and_free_regions(
     };
     let (prefix, span) = match *region {
         ty::ReEarlyBound(ref br) => {
-            let mut sp = sm.def_span(tcx.hir().span(node));
+            let mut sp = sm.guess_head_span(tcx.hir().span(node));
             if let Some(param) =
                 tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name))
             {
@@ -209,7 +209,7 @@ fn msg_span_from_early_bound_and_free_regions(
             (format!("the lifetime `{}` as defined on", br.name), sp)
         }
         ty::ReFree(ty::FreeRegion { bound_region: ty::BoundRegion::BrNamed(_, name), .. }) => {
-            let mut sp = sm.def_span(tcx.hir().span(node));
+            let mut sp = sm.guess_head_span(tcx.hir().span(node));
             if let Some(param) =
                 tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(name))
             {
@@ -223,7 +223,7 @@ fn msg_span_from_early_bound_and_free_regions(
             }
             _ => (
                 format!("the lifetime `{}` as defined on", region),
-                sm.def_span(tcx.hir().span(node)),
+                sm.guess_head_span(tcx.hir().span(node)),
             ),
         },
         _ => bug!(),
diff --git a/src/librustc_infer/traits/error_reporting/mod.rs b/src/librustc_infer/traits/error_reporting/mod.rs
index 8943ce4e6c5..47d3cdb7a4e 100644
--- a/src/librustc_infer/traits/error_reporting/mod.rs
+++ b/src/librustc_infer/traits/error_reporting/mod.rs
@@ -20,12 +20,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         requirement: &dyn fmt::Display,
     ) -> DiagnosticBuilder<'tcx> {
         let msg = "impl has stricter requirements than trait";
-        let sp = self.tcx.sess.source_map().def_span(error_span);
+        let sp = self.tcx.sess.source_map().guess_head_span(error_span);
 
         let mut err = struct_span_err!(self.tcx.sess, sp, E0276, "{}", msg);
 
         if let Some(trait_item_span) = self.tcx.hir().span_if_local(trait_item_def_id) {
-            let span = self.tcx.sess.source_map().def_span(trait_item_span);
+            let span = self.tcx.sess.source_map().guess_head_span(trait_item_span);
             err.span_label(span, format!("definition of `{}` from trait", item_name));
         }
 
@@ -46,7 +46,7 @@ pub fn report_object_safety_error(
         hir::Node::Item(item) => Some(item.ident.span),
         _ => None,
     });
-    let span = tcx.sess.source_map().def_span(span);
+    let span = tcx.sess.source_map().guess_head_span(span);
     let mut err = struct_span_err!(
         tcx.sess,
         span,
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 66d9fe7e149..906e46edfe7 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -76,7 +76,7 @@ impl EarlyLintPass for WhileTrue {
                 if let ast::LitKind::Bool(true) = lit.kind {
                     if !lit.span.from_expansion() {
                         let msg = "denote infinite loops with `loop { ... }`";
-                        let condition_span = cx.sess.source_map().def_span(e.span);
+                        let condition_span = cx.sess.source_map().guess_head_span(e.span);
                         cx.struct_span_lint(WHILE_TRUE, condition_span, |lint| {
                             lint.build(msg)
                                 .span_suggestion_short(
@@ -374,9 +374,13 @@ impl MissingDoc {
 
         let has_doc = attrs.iter().any(|a| has_doc(a));
         if !has_doc {
-            cx.struct_span_lint(MISSING_DOCS, cx.tcx.sess.source_map().def_span(sp), |lint| {
-                lint.build(&format!("missing documentation for {} {}", article, desc)).emit()
-            });
+            cx.struct_span_lint(
+                MISSING_DOCS,
+                cx.tcx.sess.source_map().guess_head_span(sp),
+                |lint| {
+                    lint.build(&format!("missing documentation for {} {}", article, desc)).emit()
+                },
+            );
         }
     }
 }
@@ -406,7 +410,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc {
             if !has_doc {
                 cx.struct_span_lint(
                     MISSING_DOCS,
-                    cx.tcx.sess.source_map().def_span(macro_def.span),
+                    cx.tcx.sess.source_map().guess_head_span(macro_def.span),
                     |lint| lint.build("missing documentation for macro").emit(),
                 );
             }
@@ -978,7 +982,7 @@ impl UnreachablePub {
                 if span.from_expansion() {
                     applicability = Applicability::MaybeIncorrect;
                 }
-                let def_span = cx.tcx.sess.source_map().def_span(span);
+                let def_span = cx.tcx.sess.source_map().guess_head_span(span);
                 cx.struct_span_lint(UNREACHABLE_PUB, def_span, |lint| {
                     let mut err = lint.build(&format!("unreachable `pub` {}", what));
                     let replacement = if cx.tcx.features().crate_visibility_modifier {
diff --git a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs b/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs
index 27a86169397..8f18fb4a30e 100644
--- a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs
+++ b/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs
@@ -256,14 +256,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
             location, place, span, borrow
         );
-        let value_msg = match self.describe_place(place.as_ref()) {
-            Some(name) => format!("`{}`", name),
-            None => "value".to_owned(),
-        };
-        let borrow_msg = match self.describe_place(borrow.borrowed_place.as_ref()) {
-            Some(name) => format!("`{}`", name),
-            None => "value".to_owned(),
-        };
+        let value_msg = self.describe_any_place(place.as_ref());
+        let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref());
 
         let borrow_spans = self.retrieve_borrow_spans(borrow);
         let borrow_span = borrow_spans.args_or_use();
@@ -271,10 +265,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         let move_spans = self.move_spans(place.as_ref(), location);
         let span = move_spans.args_or_use();
 
-        let mut err = self.cannot_move_when_borrowed(
-            span,
-            &self.describe_place(place.as_ref()).unwrap_or_else(|| "_".to_owned()),
-        );
+        let mut err =
+            self.cannot_move_when_borrowed(span, &self.describe_any_place(place.as_ref()));
         err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg));
         err.span_label(span, format!("move out of {} occurs here", value_msg));
 
@@ -314,16 +306,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
         let mut err = self.cannot_use_when_mutably_borrowed(
             span,
-            &self.describe_place(place.as_ref()).unwrap_or_else(|| "_".to_owned()),
+            &self.describe_any_place(place.as_ref()),
             borrow_span,
-            &self.describe_place(borrow.borrowed_place.as_ref()).unwrap_or_else(|| "_".to_owned()),
+            &self.describe_any_place(borrow.borrowed_place.as_ref()),
         );
 
         borrow_spans.var_span_label(&mut err, {
             let place = &borrow.borrowed_place;
-            let desc_place = self.describe_place(place.as_ref()).unwrap_or_else(|| "_".to_owned());
-
-            format!("borrow occurs due to use of `{}`{}", desc_place, borrow_spans.describe())
+            let desc_place = self.describe_any_place(place.as_ref());
+            format!("borrow occurs due to use of {}{}", desc_place, borrow_spans.describe())
         });
 
         self.explain_why_borrow_contains_point(location, borrow, None)
@@ -433,7 +424,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     borrow_spans.var_span_label(
                         &mut err,
                         format!(
-                            "borrow occurs due to use of `{}`{}",
+                            "borrow occurs due to use of {}{}",
                             desc_place,
                             borrow_spans.describe(),
                         ),
@@ -511,16 +502,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         if issued_spans == borrow_spans {
             borrow_spans.var_span_label(
                 &mut err,
-                format!("borrows occur due to use of `{}`{}", desc_place, borrow_spans.describe()),
+                format!("borrows occur due to use of {}{}", desc_place, borrow_spans.describe()),
             );
         } else {
             let borrow_place = &issued_borrow.borrowed_place;
-            let borrow_place_desc =
-                self.describe_place(borrow_place.as_ref()).unwrap_or_else(|| "_".to_owned());
+            let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
             issued_spans.var_span_label(
                 &mut err,
                 format!(
-                    "first borrow occurs due to use of `{}`{}",
+                    "first borrow occurs due to use of {}{}",
                     borrow_place_desc,
                     issued_spans.describe(),
                 ),
@@ -529,7 +519,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             borrow_spans.var_span_label(
                 &mut err,
                 format!(
-                    "second borrow occurs due to use of `{}`{}",
+                    "second borrow occurs due to use of {}{}",
                     desc_place,
                     borrow_spans.describe(),
                 ),
@@ -538,7 +528,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
         if union_type_name != "" {
             err.note(&format!(
-                "`{}` is a field of the union `{}`, so it overlaps the field `{}`",
+                "{} is a field of the union `{}`, so it overlaps the field {}",
                 msg_place, union_type_name, msg_borrow,
             ));
         }
@@ -606,7 +596,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             let ty = Place::ty_from(place_base, place_projection, *self.body, self.infcx.tcx).ty;
             ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
         };
-        let describe_place = |place| self.describe_place(place).unwrap_or_else(|| "_".to_owned());
 
         // Start with an empty tuple, so we can use the functions on `Option` to reduce some
         // code duplication (particularly around returning an empty description in the failure
@@ -645,30 +634,25 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             .and_then(|(target_base, target_field)| {
                 // With the place of a union and a field access into it, we traverse the second
                 // borrowed place and look for a access to a different field of the same union.
-                let Place { local, projection } = second_borrowed_place;
+                let Place { local, ref projection } = *second_borrowed_place;
 
                 let mut cursor = &projection[..];
                 while let [proj_base @ .., elem] = cursor {
                     cursor = proj_base;
 
                     if let ProjectionElem::Field(field, _) = elem {
-                        if let Some(union_ty) = union_ty(*local, proj_base) {
+                        if let Some(union_ty) = union_ty(local, proj_base) {
                             if field != target_field
-                                && *local == target_base.local
+                                && local == target_base.local
                                 && proj_base == target_base.projection
                             {
-                                // FIXME when we avoid clone reuse describe_place closure
-                                let describe_base_place = self
-                                    .describe_place(PlaceRef {
-                                        local: *local,
-                                        projection: proj_base,
-                                    })
-                                    .unwrap_or_else(|| "_".to_owned());
-
                                 return Some((
-                                    describe_base_place,
-                                    describe_place(first_borrowed_place.as_ref()),
-                                    describe_place(second_borrowed_place.as_ref()),
+                                    self.describe_any_place(PlaceRef {
+                                        local,
+                                        projection: proj_base,
+                                    }),
+                                    self.describe_any_place(first_borrowed_place.as_ref()),
+                                    self.describe_any_place(second_borrowed_place.as_ref()),
                                     union_ty.to_string(),
                                 ));
                             }
@@ -681,7 +665,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 // If we didn't find a field access into a union, or both places match, then
                 // only return the description of the first place.
                 (
-                    describe_place(first_borrowed_place.as_ref()),
+                    self.describe_any_place(first_borrowed_place.as_ref()),
                     "".to_string(),
                     "".to_string(),
                     "".to_string(),
@@ -1404,12 +1388,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         let loan_spans = self.retrieve_borrow_spans(loan);
         let loan_span = loan_spans.args_or_use();
 
+        let descr_place = self.describe_any_place(place.as_ref());
         if loan.kind == BorrowKind::Shallow {
             if let Some(section) = self.classify_immutable_section(&loan.assigned_place) {
                 let mut err = self.cannot_mutate_in_immutable_section(
                     span,
                     loan_span,
-                    &self.describe_place(place.as_ref()).unwrap_or_else(|| "_".to_owned()),
+                    &descr_place,
                     section,
                     "assign",
                 );
@@ -1424,11 +1409,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             }
         }
 
-        let mut err = self.cannot_assign_to_borrowed(
-            span,
-            loan_span,
-            &self.describe_place(place.as_ref()).unwrap_or_else(|| "_".to_owned()),
-        );
+        let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
 
         loan_spans
             .var_span_label(&mut err, format!("borrow occurs due to use{}", loan_spans.describe()));
@@ -1482,15 +1463,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             })
             | Some(LocalDecl { local_info: LocalInfo::StaticRef { .. }, .. })
             | Some(LocalDecl { local_info: LocalInfo::Other, .. })
-            | None => (self.describe_place(place.as_ref()), assigned_span),
-            Some(decl) => (self.describe_place(err_place.as_ref()), decl.source_info.span),
+            | None => (self.describe_any_place(place.as_ref()), assigned_span),
+            Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span),
         };
 
-        let mut err = self.cannot_reassign_immutable(
-            span,
-            place_description.as_ref().map(AsRef::as_ref).unwrap_or("_"),
-            from_arg,
-        );
+        let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg);
         let msg = if from_arg {
             "cannot assign to immutable argument"
         } else {
@@ -1498,11 +1475,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         };
         if span != assigned_span {
             if !from_arg {
-                let value_msg = match place_description {
-                    Some(name) => format!("`{}`", name),
-                    None => "value".to_owned(),
-                };
-                err.span_label(assigned_span, format!("first assignment to {}", value_msg));
+                err.span_label(assigned_span, format!("first assignment to {}", place_description));
             }
         }
         if let Some(decl) = local_decl {
diff --git a/src/librustc_mir/borrow_check/diagnostics/mod.rs b/src/librustc_mir/borrow_check/diagnostics/mod.rs
index 7110a4a3058..605093d8aca 100644
--- a/src/librustc_mir/borrow_check/diagnostics/mod.rs
+++ b/src/librustc_mir/borrow_check/diagnostics/mod.rs
@@ -137,8 +137,23 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         }
     }
 
-    /// End-user visible description of `place` if one can be found. If the
-    /// place is a temporary for instance, None will be returned.
+    /// End-user visible description of `place` if one can be found.
+    /// If the place is a temporary for instance, `"value"` will be returned.
+    pub(super) fn describe_any_place(&self, place_ref: PlaceRef<'tcx>) -> String {
+        match self.describe_place(place_ref) {
+            Some(mut descr) => {
+                // Surround descr with `backticks`.
+                descr.reserve(2);
+                descr.insert_str(0, "`");
+                descr.push_str("`");
+                descr
+            }
+            None => "value".to_string(),
+        }
+    }
+
+    /// End-user visible description of `place` if one can be found.
+    /// If the place is a temporary for instance, None will be returned.
     pub(super) fn describe_place(&self, place_ref: PlaceRef<'tcx>) -> Option<String> {
         self.describe_place_with_options(place_ref, IncludingDowncast(false))
     }
diff --git a/src/librustc_mir/borrow_check/diagnostics/move_errors.rs b/src/librustc_mir/borrow_check/diagnostics/move_errors.rs
index 83bc9849caf..2cdc1ced0bb 100644
--- a/src/librustc_mir/borrow_check/diagnostics/move_errors.rs
+++ b/src/librustc_mir/borrow_check/diagnostics/move_errors.rs
@@ -272,14 +272,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         span: Span,
     ) -> DiagnosticBuilder<'a> {
         let description = if place.projection.len() == 1 {
-            format!("static item `{}`", self.describe_place(place.as_ref()).unwrap())
+            format!("static item {}", self.describe_any_place(place.as_ref()))
         } else {
             let base_static = PlaceRef { local: place.local, projection: &[ProjectionElem::Deref] };
 
             format!(
-                "`{:?}` as `{:?}` is a static item",
-                self.describe_place(place.as_ref()).unwrap(),
-                self.describe_place(base_static).unwrap(),
+                "{} as {} is a static item",
+                self.describe_any_place(place.as_ref()),
+                self.describe_any_place(base_static),
             )
         };
 
@@ -349,16 +349,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 let upvar_name = upvar.name;
                 let upvar_span = self.infcx.tcx.hir().span(upvar_hir_id);
 
-                let place_name = self.describe_place(move_place.as_ref()).unwrap();
+                let place_name = self.describe_any_place(move_place.as_ref());
 
-                let place_description = if self
-                    .is_upvar_field_projection(move_place.as_ref())
-                    .is_some()
-                {
-                    format!("`{}`, a {}", place_name, capture_description)
-                } else {
-                    format!("`{}`, as `{}` is a {}", place_name, upvar_name, capture_description,)
-                };
+                let place_description =
+                    if self.is_upvar_field_projection(move_place.as_ref()).is_some() {
+                        format!("{}, a {}", place_name, capture_description)
+                    } else {
+                        format!("{}, as `{}` is a {}", place_name, upvar_name, capture_description)
+                    };
 
                 debug!(
                     "report: closure_kind_ty={:?} closure_kind={:?} place_description={:?}",
diff --git a/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs b/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs
index ee654431d88..f224041270d 100644
--- a/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs
+++ b/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs
@@ -169,9 +169,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 borrow_spans.var_span_label(
                     &mut err,
                     format!(
-                        "mutable borrow occurs due to use of `{}` in closure",
-                        // always Some() if the message is printed.
-                        self.describe_place(access_place.as_ref()).unwrap_or_default(),
+                        "mutable borrow occurs due to use of {} in closure",
+                        self.describe_any_place(access_place.as_ref()),
                     ),
                 );
                 borrow_span
diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs
index 437a154a9b8..3ba60e69041 100644
--- a/src/librustc_mir/transform/check_unsafety.rs
+++ b/src/librustc_mir/transform/check_unsafety.rs
@@ -565,14 +565,14 @@ fn is_enclosed(
 }
 
 fn report_unused_unsafe(tcx: TyCtxt<'_>, used_unsafe: &FxHashSet<hir::HirId>, id: hir::HirId) {
-    let span = tcx.sess.source_map().def_span(tcx.hir().span(id));
+    let span = tcx.sess.source_map().guess_head_span(tcx.hir().span(id));
     tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, |lint| {
         let msg = "unnecessary `unsafe` block";
         let mut db = lint.build(msg);
         db.span_label(span, msg);
         if let Some((kind, id)) = is_enclosed(tcx, used_unsafe, id) {
             db.span_label(
-                tcx.sess.source_map().def_span(tcx.hir().span(id)),
+                tcx.sess.source_map().guess_head_span(tcx.hir().span(id)),
                 format!("because it's nested under this `unsafe` {}", kind),
             );
         }
diff --git a/src/librustc_mir/util/borrowck_errors.rs b/src/librustc_mir/util/borrowck_errors.rs
index d8ee059f1a6..6e6bbabd35b 100644
--- a/src/librustc_mir/util/borrowck_errors.rs
+++ b/src/librustc_mir/util/borrowck_errors.rs
@@ -4,7 +4,7 @@ use rustc_span::{MultiSpan, Span};
 
 impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> {
     crate fn cannot_move_when_borrowed(&self, span: Span, desc: &str) -> DiagnosticBuilder<'cx> {
-        struct_span_err!(self, span, E0505, "cannot move out of `{}` because it is borrowed", desc,)
+        struct_span_err!(self, span, E0505, "cannot move out of {} because it is borrowed", desc,)
     }
 
     crate fn cannot_use_when_mutably_borrowed(
@@ -18,12 +18,12 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> {
             self,
             span,
             E0503,
-            "cannot use `{}` because it was mutably borrowed",
+            "cannot use {} because it was mutably borrowed",
             desc,
         );
 
-        err.span_label(borrow_span, format!("borrow of `{}` occurs here", borrow_desc));
-        err.span_label(span, format!("use of borrowed `{}`", borrow_desc));
+        err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_desc));
+        err.span_label(span, format!("use of borrowed {}", borrow_desc));
         err
     }
 
@@ -53,12 +53,12 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> {
         old_load_end_span: Option<Span>,
     ) -> DiagnosticBuilder<'cx> {
         let via =
-            |msg: &str| if msg.is_empty() { msg.to_string() } else { format!(" (via `{}`)", msg) };
+            |msg: &str| if msg.is_empty() { "".to_string() } else { format!(" (via {})", msg) };
         let mut err = struct_span_err!(
             self,
             new_loan_span,
             E0499,
-            "cannot borrow `{}`{} as mutable more than once at a time",
+            "cannot borrow {}{} as mutable more than once at a time",
             desc,
             via(opt_via),
         );
@@ -103,7 +103,7 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> {
             self,
             new_loan_span,
             E0524,
-            "two closures require unique access to `{}` at the same time",
+            "two closures require unique access to {} at the same time",
             desc,
         );
         if old_loan_span == new_loan_span {
@@ -136,7 +136,7 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> {
             self,
             new_loan_span,
             E0500,
-            "closure requires unique access to `{}` but {} is already borrowed{}",
+            "closure requires unique access to {} but {} is already borrowed{}",
             desc_new,
             noun_old,
             old_opt_via,
@@ -168,7 +168,7 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> {
             self,
             new_loan_span,
             E0501,
-            "cannot borrow `{}`{} as {} because previous closure \
+            "cannot borrow {}{} as {} because previous closure \
              requires unique access",
             desc_new,
             opt_via,
@@ -201,13 +201,12 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> {
         old_load_end_span: Option<Span>,
     ) -> DiagnosticBuilder<'cx> {
         let via =
-            |msg: &str| if msg.is_empty() { msg.to_string() } else { format!(" (via `{}`)", msg) };
+            |msg: &str| if msg.is_empty() { "".to_string() } else { format!(" (via {})", msg) };
         let mut err = struct_span_err!(
             self,
             span,
             E0502,
-            "cannot borrow `{}`{} as {} because {} is also borrowed \
-             as {}{}",
+            "cannot borrow {}{} as {} because {} is also borrowed as {}{}",
             desc_new,
             via(msg_new),
             kind_new,
@@ -225,7 +224,7 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> {
             err.span_label(
                 span,
                 format!(
-                    "{} borrow of `{}` -- which overlaps with `{}` -- occurs here",
+                    "{} borrow of {} -- which overlaps with {} -- occurs here",
                     kind_new, msg_new, msg_old,
                 ),
             );
@@ -248,12 +247,12 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> {
             self,
             span,
             E0506,
-            "cannot assign to `{}` because it is borrowed",
+            "cannot assign to {} because it is borrowed",
             desc,
         );
 
-        err.span_label(borrow_span, format!("borrow of `{}` occurs here", desc));
-        err.span_label(span, format!("assignment to borrowed `{}` occurs here", desc));
+        err.span_label(borrow_span, format!("borrow of {} occurs here", desc));
+        err.span_label(span, format!("assignment to borrowed {} occurs here", desc));
         err
     }
 
@@ -264,7 +263,7 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> {
         is_arg: bool,
     ) -> DiagnosticBuilder<'cx> {
         let msg = if is_arg { "to immutable argument" } else { "twice to immutable variable" };
-        struct_span_err!(self, span, E0384, "cannot assign {} `{}`", msg, desc,)
+        struct_span_err!(self, span, E0384, "cannot assign {} {}", msg, desc)
     }
 
     crate fn cannot_assign(&self, span: Span, desc: &str) -> DiagnosticBuilder<'cx> {
@@ -362,7 +361,7 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> {
             self,
             mutate_span,
             E0510,
-            "cannot {} `{}` in {}",
+            "cannot {} {} in {}",
             action,
             immutable_place,
             immutable_section,
diff --git a/src/librustc_mir_build/lints.rs b/src/librustc_mir_build/lints.rs
index 8b1ddf7461a..3f927422616 100644
--- a/src/librustc_mir_build/lints.rs
+++ b/src/librustc_mir_build/lints.rs
@@ -123,7 +123,7 @@ fn check_fn_for_unconditional_recursion<'tcx>(
     // recurs.
     if !reached_exit_without_self_call && !self_call_locations.is_empty() {
         let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
-        let sp = tcx.sess.source_map().def_span(tcx.hir().span(hir_id));
+        let sp = tcx.sess.source_map().guess_head_span(tcx.hir().span(hir_id));
         tcx.struct_span_lint_hir(UNCONDITIONAL_RECURSION, hir_id, sp, |lint| {
             let mut db = lint.build("function cannot return without recursing");
             db.span_label(sp, "cannot return without recursing");
diff --git a/src/librustc_parse/parser/diagnostics.rs b/src/librustc_parse/parser/diagnostics.rs
index 87255386b9e..c4546dedfcd 100644
--- a/src/librustc_parse/parser/diagnostics.rs
+++ b/src/librustc_parse/parser/diagnostics.rs
@@ -17,7 +17,6 @@ use rustc_span::symbol::kw;
 use rustc_span::{MultiSpan, Span, SpanSnippetError, DUMMY_SP};
 
 use log::{debug, trace};
-use std::mem;
 
 const TURBOFISH: &str = "use `::<...>` instead of `<...>` to specify type arguments";
 
@@ -459,9 +458,28 @@ impl<'a> Parser<'a> {
         err: &mut DiagnosticBuilder<'_>,
         inner_op: &Expr,
         outer_op: &Spanned<AssocOp>,
-    ) {
+    ) -> bool /* advanced the cursor */ {
         if let ExprKind::Binary(op, ref l1, ref r1) = inner_op.kind {
-            match (op.node, &outer_op.node) {
+            if let ExprKind::Field(_, ident) = l1.kind {
+                if ident.as_str().parse::<i32>().is_err() && !matches!(r1.kind, ExprKind::Lit(_)) {
+                    // The parser has encountered `foo.bar<baz`, the likelihood of the turbofish
+                    // suggestion being the only one to apply is high.
+                    return false;
+                }
+            }
+            let mut enclose = |left: Span, right: Span| {
+                err.multipart_suggestion(
+                    "parenthesize the comparison",
+                    vec![
+                        (left.shrink_to_lo(), "(".to_string()),
+                        (right.shrink_to_hi(), ")".to_string()),
+                    ],
+                    Applicability::MaybeIncorrect,
+                );
+            };
+            return match (op.node, &outer_op.node) {
+                // `x == y == z`
+                (BinOpKind::Eq, AssocOp::Equal) |
                 // `x < y < z` and friends.
                 (BinOpKind::Lt, AssocOp::Less) | (BinOpKind::Lt, AssocOp::LessEqual) |
                 (BinOpKind::Le, AssocOp::LessEqual) | (BinOpKind::Le, AssocOp::Less) |
@@ -472,35 +490,55 @@ impl<'a> Parser<'a> {
                         self.span_to_snippet(e.span)
                             .unwrap_or_else(|_| pprust::expr_to_string(&e))
                     };
-                    err.span_suggestion(
-                        inner_op.span.to(outer_op.span),
-                        "split the comparison into two...",
-                        format!(
-                            "{} {} {} && {} {}",
-                            expr_to_str(&l1),
-                            op.node.to_string(),
-                            expr_to_str(&r1),
-                            expr_to_str(&r1),
-                            outer_op.node.to_ast_binop().unwrap().to_string(),
-                        ),
-                        Applicability::MaybeIncorrect,
-                    );
-                    err.span_suggestion(
-                        inner_op.span.to(outer_op.span),
-                        "...or parenthesize one of the comparisons",
-                        format!(
-                            "({} {} {}) {}",
-                            expr_to_str(&l1),
-                            op.node.to_string(),
-                            expr_to_str(&r1),
-                            outer_op.node.to_ast_binop().unwrap().to_string(),
-                        ),
+                    err.span_suggestion_verbose(
+                            inner_op.span.shrink_to_hi(),
+                        "split the comparison into two",
+                        format!(" && {}", expr_to_str(&r1)),
                         Applicability::MaybeIncorrect,
                     );
+                    false // Keep the current parse behavior, where the AST is `(x < y) < z`.
                 }
-                _ => {}
-            }
+                // `x == y < z`
+                (BinOpKind::Eq, AssocOp::Less) | (BinOpKind::Eq, AssocOp::LessEqual) |
+                (BinOpKind::Eq, AssocOp::Greater) | (BinOpKind::Eq, AssocOp::GreaterEqual) => {
+                    // Consume `z`/outer-op-rhs.
+                    let snapshot = self.clone();
+                    match self.parse_expr() {
+                        Ok(r2) => {
+                            // We are sure that outer-op-rhs could be consumed, the suggestion is
+                            // likely correct.
+                            enclose(r1.span, r2.span);
+                            true
+                        }
+                        Err(mut expr_err) => {
+                            expr_err.cancel();
+                            *self = snapshot;
+                            false
+                        }
+                    }
+                }
+                // `x > y == z`
+                (BinOpKind::Lt, AssocOp::Equal) | (BinOpKind::Le, AssocOp::Equal) |
+                (BinOpKind::Gt, AssocOp::Equal) | (BinOpKind::Ge, AssocOp::Equal) => {
+                    let snapshot = self.clone();
+                    // At this point it is always valid to enclose the lhs in parentheses, no
+                    // further checks are necessary.
+                    match self.parse_expr() {
+                        Ok(_) => {
+                            enclose(l1.span, r1.span);
+                            true
+                        }
+                        Err(mut expr_err) => {
+                            expr_err.cancel();
+                            *self = snapshot;
+                            false
+                        }
+                    }
+                }
+                _ => false,
+            };
         }
+        false
     }
 
     /// Produces an error if comparison operators are chained (RFC #558).
@@ -534,31 +572,26 @@ impl<'a> Parser<'a> {
             |this: &Self, span| Ok(Some(this.mk_expr(span, ExprKind::Err, AttrVec::new())));
 
         match inner_op.kind {
-            ExprKind::Binary(op, _, _) if op.node.is_comparison() => {
-                // Respan to include both operators.
-                let op_span = op.span.to(self.prev_token.span);
-                let mut err =
-                    self.struct_span_err(op_span, "comparison operators cannot be chained");
-
-                // If it looks like a genuine attempt to chain operators (as opposed to a
-                // misformatted turbofish, for instance), suggest a correct form.
-                self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op);
+            ExprKind::Binary(op, ref l1, ref r1) if op.node.is_comparison() => {
+                let mut err = self.struct_span_err(
+                    vec![op.span, self.prev_token.span],
+                    "comparison operators cannot be chained",
+                );
 
                 let suggest = |err: &mut DiagnosticBuilder<'_>| {
                     err.span_suggestion_verbose(
-                        op_span.shrink_to_lo(),
+                        op.span.shrink_to_lo(),
                         TURBOFISH,
                         "::".to_string(),
                         Applicability::MaybeIncorrect,
                     );
                 };
 
-                if op.node == BinOpKind::Lt &&
-                    outer_op.node == AssocOp::Less ||  // Include `<` to provide this recommendation
-                    outer_op.node == AssocOp::Greater
-                // even in a case like the following:
+                // Include `<` to provide this recommendation even in a case like
+                // `Foo<Bar<Baz<Qux, ()>>>`
+                if op.node == BinOpKind::Lt && outer_op.node == AssocOp::Less
+                    || outer_op.node == AssocOp::Greater
                 {
-                    //     Foo<Bar<Baz<Qux, ()>>>
                     if outer_op.node == AssocOp::Less {
                         let snapshot = self.clone();
                         self.bump();
@@ -572,7 +605,7 @@ impl<'a> Parser<'a> {
                         {
                             // We don't have `foo< bar >(` or `foo< bar >::`, so we rewind the
                             // parser and bail out.
-                            mem::replace(self, snapshot.clone());
+                            *self = snapshot.clone();
                         }
                     }
                     return if token::ModSep == self.token.kind {
@@ -597,7 +630,7 @@ impl<'a> Parser<'a> {
                                 expr_err.cancel();
                                 // Not entirely sure now, but we bubble the error up with the
                                 // suggestion.
-                                mem::replace(self, snapshot);
+                                *self = snapshot;
                                 Err(err)
                             }
                         }
@@ -617,15 +650,33 @@ impl<'a> Parser<'a> {
                             }
                         }
                     } else {
-                        // All we know is that this is `foo < bar >` and *nothing* else. Try to
-                        // be helpful, but don't attempt to recover.
-                        err.help(TURBOFISH);
-                        err.help("or use `(...)` if you meant to specify fn arguments");
-                        // These cases cause too many knock-down errors, bail out (#61329).
-                        Err(err)
+                        if !matches!(l1.kind, ExprKind::Lit(_))
+                            && !matches!(r1.kind, ExprKind::Lit(_))
+                        {
+                            // All we know is that this is `foo < bar >` and *nothing* else. Try to
+                            // be helpful, but don't attempt to recover.
+                            err.help(TURBOFISH);
+                            err.help("or use `(...)` if you meant to specify fn arguments");
+                        }
+
+                        // If it looks like a genuine attempt to chain operators (as opposed to a
+                        // misformatted turbofish, for instance), suggest a correct form.
+                        if self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op)
+                        {
+                            err.emit();
+                            mk_err_expr(self, inner_op.span.to(self.prev_token.span))
+                        } else {
+                            // These cases cause too many knock-down errors, bail out (#61329).
+                            Err(err)
+                        }
                     };
                 }
+                let recover =
+                    self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op);
                 err.emit();
+                if recover {
+                    return mk_err_expr(self, inner_op.span.to(self.prev_token.span));
+                }
             }
             _ => {}
         }
@@ -643,7 +694,7 @@ impl<'a> Parser<'a> {
 
         if self.token.kind == token::Eof {
             // Not entirely sure that what we consumed were fn arguments, rollback.
-            mem::replace(self, snapshot);
+            *self = snapshot;
             Err(())
         } else {
             // 99% certain that the suggestion is correct, continue parsing.
diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs
index 7a4f7680415..cc88464d774 100644
--- a/src/librustc_parse/parser/item.rs
+++ b/src/librustc_parse/parser/item.rs
@@ -907,7 +907,7 @@ impl<'a> Parser<'a> {
     }
 
     fn error_bad_item_kind<T>(&self, span: Span, kind: &ItemKind, ctx: &str) -> Option<T> {
-        let span = self.sess.source_map().def_span(span);
+        let span = self.sess.source_map().guess_head_span(span);
         let msg = format!("{} is not supported in {}", kind.descr(), ctx);
         self.struct_span_err(span, &msg).emit();
         None
diff --git a/src/librustc_passes/dead.rs b/src/librustc_passes/dead.rs
index 4466dea89b0..2781c5c125d 100644
--- a/src/librustc_passes/dead.rs
+++ b/src/librustc_passes/dead.rs
@@ -590,7 +590,7 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> {
                     // We should probably annotate ident.span with the macro
                     // context, but that's a larger change.
                     if item.span.source_callee().is_some() {
-                        self.tcx.sess.source_map().def_span(item.span)
+                        self.tcx.sess.source_map().guess_head_span(item.span)
                     } else {
                         item.ident.span
                     }
@@ -663,7 +663,7 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> {
             }
             hir::ImplItemKind::Fn(_, body_id) => {
                 if !self.symbol_is_live(impl_item.hir_id) {
-                    let span = self.tcx.sess.source_map().def_span(impl_item.span);
+                    let span = self.tcx.sess.source_map().guess_head_span(impl_item.span);
                     self.warn_dead_code(
                         impl_item.hir_id,
                         span,
diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs
index 9cd066b629c..78b7e256de2 100644
--- a/src/librustc_resolve/diagnostics.rs
+++ b/src/librustc_resolve/diagnostics.rs
@@ -791,12 +791,12 @@ impl<'a> Resolver<'a> {
                 _ => Some(
                     self.session
                         .source_map()
-                        .def_span(self.cstore().get_span_untracked(def_id, self.session)),
+                        .guess_head_span(self.cstore().get_span_untracked(def_id, self.session)),
                 ),
             });
             if let Some(span) = def_span {
                 err.span_label(
-                    self.session.source_map().def_span(span),
+                    self.session.source_map().guess_head_span(span),
                     &format!(
                         "similarly named {} `{}` defined here",
                         suggestion.res.descr(),
@@ -986,7 +986,7 @@ impl<'a> Resolver<'a> {
                 which = if first { "" } else { " which" },
                 dots = if next_binding.is_some() { "..." } else { "" },
             );
-            let def_span = self.session.source_map().def_span(binding.span);
+            let def_span = self.session.source_map().guess_head_span(binding.span);
             let mut note_span = MultiSpan::from_span(def_span);
             if !first && binding.vis == ty::Visibility::Public {
                 note_span.push_span_label(def_span, "consider importing it directly".into());
diff --git a/src/librustc_resolve/imports.rs b/src/librustc_resolve/imports.rs
index d375ae4a447..8c7ab8f5b1a 100644
--- a/src/librustc_resolve/imports.rs
+++ b/src/librustc_resolve/imports.rs
@@ -1441,7 +1441,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                     let enum_resolution = resolutions.get(&key).expect("resolution should exist");
                     let enum_span =
                         enum_resolution.borrow().binding.expect("binding should exist").span;
-                    let enum_def_span = this.session.source_map().def_span(enum_span);
+                    let enum_def_span = this.session.source_map().guess_head_span(enum_span);
                     let enum_def_snippet = this
                         .session
                         .source_map()
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index ed304572a06..9d5121cbad5 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -2517,7 +2517,8 @@ impl<'a> Resolver<'a> {
             false => "defined",
         };
 
-        let (name, span) = (ident.name, self.session.source_map().def_span(new_binding.span));
+        let (name, span) =
+            (ident.name, self.session.source_map().guess_head_span(new_binding.span));
 
         if let Some(s) = self.name_already_seen.get(&name) {
             if s == &span {
@@ -2558,7 +2559,7 @@ impl<'a> Resolver<'a> {
 
         err.span_label(span, format!("`{}` re{} here", name, new_participle));
         err.span_label(
-            self.session.source_map().def_span(old_binding.span),
+            self.session.source_map().guess_head_span(old_binding.span),
             format!("previous {} of the {} `{}` here", old_noun, old_kind, name),
         );
 
diff --git a/src/librustc_span/source_map.rs b/src/librustc_span/source_map.rs
index fa5baffbe48..57e68320f3f 100644
--- a/src/librustc_span/source_map.rs
+++ b/src/librustc_span/source_map.rs
@@ -733,7 +733,14 @@ impl SourceMap {
         }
     }
 
-    pub fn def_span(&self, sp: Span) -> Span {
+    /// Given a `Span`, return a span ending in the closest `{`. This is useful when you have a
+    /// `Span` enclosing a whole item but we need to point at only the head (usually the first
+    /// line) of that item.
+    ///
+    /// *Only suitable for diagnostics.*
+    pub fn guess_head_span(&self, sp: Span) -> Span {
+        // FIXME: extend the AST items to have a head span, or replace callers with pointing at
+        // the item's ident when appropriate.
         self.span_until_char(sp, '{')
     }
 
diff --git a/src/librustc_trait_selection/traits/error_reporting/mod.rs b/src/librustc_trait_selection/traits/error_reporting/mod.rs
index b9ee991aa02..12939519fc2 100644
--- a/src/librustc_trait_selection/traits/error_reporting/mod.rs
+++ b/src/librustc_trait_selection/traits/error_reporting/mod.rs
@@ -482,11 +482,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
 
                     ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => {
                         let found_kind = self.closure_kind(closure_substs).unwrap();
-                        let closure_span = self
-                            .tcx
-                            .sess
-                            .source_map()
-                            .def_span(self.tcx.hir().span_if_local(closure_def_id).unwrap());
+                        let closure_span =
+                            self.tcx.sess.source_map().guess_head_span(
+                                self.tcx.hir().span_if_local(closure_def_id).unwrap(),
+                            );
                         let hir_id = self.tcx.hir().as_local_hir_id(closure_def_id).unwrap();
                         let mut err = struct_span_err!(
                             self.tcx.sess,
@@ -580,7 +579,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
 
                 let found_span = found_did
                     .and_then(|did| self.tcx.hir().span_if_local(did))
-                    .map(|sp| self.tcx.sess.source_map().def_span(sp)); // the sp could be an fn def
+                    .map(|sp| self.tcx.sess.source_map().guess_head_span(sp)); // the sp could be an fn def
 
                 if self.reported_closure_mismatch.borrow().contains(&(span, found_span)) {
                     // We check closures twice, with obligations flowing in different directions,
@@ -680,7 +679,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                 kind: hir::ExprKind::Closure(_, ref _decl, id, span, _),
                 ..
             }) => (
-                self.tcx.sess.source_map().def_span(span),
+                self.tcx.sess.source_map().guess_head_span(span),
                 self.tcx
                     .hir()
                     .body(id)
@@ -723,7 +722,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                 kind: hir::TraitItemKind::Fn(ref sig, _),
                 ..
             }) => (
-                self.tcx.sess.source_map().def_span(span),
+                self.tcx.sess.source_map().guess_head_span(span),
                 sig.decl
                     .inputs
                     .iter()
@@ -741,7 +740,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                     .ctor_hir_id()
                     .map(|hir_id| self.tcx.hir().span(hir_id))
                     .unwrap_or(DUMMY_SP);
-                let span = self.tcx.sess.source_map().def_span(span);
+                let span = self.tcx.sess.source_map().guess_head_span(span);
 
                 (span, vec![ArgKind::empty(); variant_data.fields().len()])
             }
@@ -1624,7 +1623,7 @@ pub fn recursive_type_with_infinite_size_error(
 ) -> DiagnosticBuilder<'tcx> {
     assert!(type_def_id.is_local());
     let span = tcx.hir().span_if_local(type_def_id).unwrap();
-    let span = tcx.sess.source_map().def_span(span);
+    let span = tcx.sess.source_map().guess_head_span(span);
     let mut err = struct_span_err!(
         tcx.sess,
         span,
diff --git a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs
index 22b8d058396..a4be70df122 100644
--- a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs
+++ b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs
@@ -1381,7 +1381,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                 let msg = format!("required by `{}`", item_name);
 
                 if let Some(sp) = tcx.hir().span_if_local(item_def_id) {
-                    let sp = tcx.sess.source_map().def_span(sp);
+                    let sp = tcx.sess.source_map().guess_head_span(sp);
                     err.span_label(sp, &msg);
                 } else {
                     err.note(&msg);
diff --git a/src/librustc_trait_selection/traits/specialize/mod.rs b/src/librustc_trait_selection/traits/specialize/mod.rs
index b763851b86e..0cc5032d74e 100644
--- a/src/librustc_trait_selection/traits/specialize/mod.rs
+++ b/src/librustc_trait_selection/traits/specialize/mod.rs
@@ -325,7 +325,7 @@ pub(super) fn specialization_graph_provider(
 
             if let Some(overlap) = overlap {
                 let impl_span =
-                    tcx.sess.source_map().def_span(tcx.span_of_impl(impl_def_id).unwrap());
+                    tcx.sess.source_map().guess_head_span(tcx.span_of_impl(impl_def_id).unwrap());
 
                 // Work to be done after we've built the DiagnosticBuilder. We have to define it
                 // now because the struct_lint methods don't return back the DiagnosticBuilder
@@ -347,7 +347,7 @@ pub(super) fn specialization_graph_provider(
                     match tcx.span_of_impl(overlap.with_impl) {
                         Ok(span) => {
                             err.span_label(
-                                tcx.sess.source_map().def_span(span),
+                                tcx.sess.source_map().guess_head_span(span),
                                 "first implementation here".to_string(),
                             );
 
diff --git a/src/librustc_trait_selection/traits/wf.rs b/src/librustc_trait_selection/traits/wf.rs
index ac2da006df3..aa129d2b81c 100644
--- a/src/librustc_trait_selection/traits/wf.rs
+++ b/src/librustc_trait_selection/traits/wf.rs
@@ -186,7 +186,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
                     _ => (None, None),
                 };
 
-                let item_span = item.map(|i| tcx.sess.source_map().def_span(i.span));
+                let item_span = item.map(|i| tcx.sess.source_map().guess_head_span(i.span));
                 match pred {
                     ty::Predicate::Projection(proj) => {
                         // The obligation comes not from the current `impl` nor the `trait` being
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index 3bf144e9643..54c646b8557 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -2269,7 +2269,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     }
 
                     if let Some(sp) = tcx.hir().span_if_local(adt_def.did) {
-                        let sp = tcx.sess.source_map().def_span(sp);
+                        let sp = tcx.sess.source_map().guess_head_span(sp);
                         err.span_label(sp, format!("variant `{}` not found here", assoc_ident));
                     }
 
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index 20737b44e7c..a34389b7d89 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -332,7 +332,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 //   | |_____^ expected integer, found `()`
                 // ```
                 if outer_sp.is_some() {
-                    outer_sp = Some(self.tcx.sess.source_map().def_span(span));
+                    outer_sp = Some(self.tcx.sess.source_map().guess_head_span(span));
                 }
                 else_expr.span
             }
diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs
index f666ef72d52..412f687b43e 100644
--- a/src/librustc_typeck/check/compare_method.rs
+++ b/src/librustc_typeck/check/compare_method.rs
@@ -35,7 +35,7 @@ crate fn compare_impl_method<'tcx>(
 ) {
     debug!("compare_impl_method(impl_trait_ref={:?})", impl_trait_ref);
 
-    let impl_m_span = tcx.sess.source_map().def_span(impl_m_span);
+    let impl_m_span = tcx.sess.source_map().guess_head_span(impl_m_span);
 
     if let Err(ErrorReported) = compare_self_type(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref)
     {
@@ -363,7 +363,7 @@ fn check_region_bounds_on_impl_item<'tcx>(
     // the moment, give a kind of vague error message.
     if trait_params != impl_params {
         let item_kind = assoc_item_kind_str(impl_m);
-        let def_span = tcx.sess.source_map().def_span(span);
+        let def_span = tcx.sess.source_map().guess_head_span(span);
         let span = tcx.hir().get_generics(impl_m.def_id).map(|g| g.span).unwrap_or(def_span);
         let mut err = struct_span_err!(
             tcx.sess,
@@ -375,7 +375,7 @@ fn check_region_bounds_on_impl_item<'tcx>(
         );
         err.span_label(span, &format!("lifetimes do not match {} in trait", item_kind));
         if let Some(sp) = tcx.hir().span_if_local(trait_m.def_id) {
-            let def_sp = tcx.sess.source_map().def_span(sp);
+            let def_sp = tcx.sess.source_map().guess_head_span(sp);
             let sp = tcx.hir().get_generics(trait_m.def_id).map(|g| g.span).unwrap_or(def_sp);
             err.span_label(
                 sp,
diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs
index 68996f5aaf9..e940ecce0b9 100644
--- a/src/librustc_typeck/check/method/suggest.rs
+++ b/src/librustc_typeck/check/method/suggest.rs
@@ -147,7 +147,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         if let Some(note_span) = note_span {
                             // We have a span pointing to the method. Show note with snippet.
                             err.span_note(
-                                self.tcx.sess.source_map().def_span(note_span),
+                                self.tcx.sess.source_map().guess_head_span(note_span),
                                 &note_str,
                             );
                         } else {
@@ -189,8 +189,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 Some(item) => item,
                                 None => continue,
                             };
-                        let item_span =
-                            self.tcx.sess.source_map().def_span(self.tcx.def_span(item.def_id));
+                        let item_span = self
+                            .tcx
+                            .sess
+                            .source_map()
+                            .guess_head_span(self.tcx.def_span(item.def_id));
                         let idx = if sources.len() > 1 {
                             let msg = &format!(
                                 "candidate #{} is defined in the trait `{}`",
@@ -397,7 +400,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                 if let Some(def) = actual.ty_adt_def() {
                     if let Some(full_sp) = tcx.hir().span_if_local(def.did) {
-                        let def_sp = tcx.sess.source_map().def_span(full_sp);
+                        let def_sp = tcx.sess.source_map().guess_head_span(full_sp);
                         err.span_label(
                             def_sp,
                             format!(
@@ -537,8 +540,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                 let mut restrict_type_params = false;
                 if !unsatisfied_predicates.is_empty() {
-                    let def_span =
-                        |def_id| self.tcx.sess.source_map().def_span(self.tcx.def_span(def_id));
+                    let def_span = |def_id| {
+                        self.tcx.sess.source_map().guess_head_span(self.tcx.def_span(def_id))
+                    };
                     let mut type_params = FxHashMap::default();
                     let mut bound_spans = vec![];
                     let mut collect_type_param_suggestions =
@@ -1117,7 +1121,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 if let [trait_info] = &candidates[..] {
                     if let Some(span) = self.tcx.hir().span_if_local(trait_info.def_id) {
                         err.span_note(
-                            self.tcx.sess.source_map().def_span(span),
+                            self.tcx.sess.source_map().guess_head_span(span),
                             &format!(
                                 "`{}` defines an item `{}`, perhaps you need to {} it",
                                 self.tcx.def_path_str(trait_info.def_id),
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 085510452c4..e7ba00ac245 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1479,7 +1479,7 @@ fn check_fn<'a, 'tcx>(
                         }
                     }
                 } else {
-                    let span = sess.source_map().def_span(span);
+                    let span = sess.source_map().guess_head_span(span);
                     sess.span_err(span, "function should have one argument");
                 }
             } else {
@@ -1520,7 +1520,7 @@ fn check_fn<'a, 'tcx>(
                         }
                     }
                 } else {
-                    let span = sess.source_map().def_span(span);
+                    let span = sess.source_map().guess_head_span(span);
                     sess.span_err(span, "function should have one argument");
                 }
             } else {
@@ -1962,7 +1962,7 @@ fn check_impl_items_against_trait<'tcx>(
     impl_trait_ref: ty::TraitRef<'tcx>,
     impl_item_refs: &[hir::ImplItemRef<'_>],
 ) {
-    let impl_span = tcx.sess.source_map().def_span(full_impl_span);
+    let impl_span = tcx.sess.source_map().guess_head_span(full_impl_span);
 
     // If the trait reference itself is erroneous (so the compilation is going
     // to fail), skip checking the items here -- the `impl_item` table in `tcx`
@@ -2508,7 +2508,7 @@ fn check_transparent(tcx: TyCtxt<'_>, sp: Span, def_id: DefId) {
     if !adt.repr.transparent() {
         return;
     }
-    let sp = tcx.sess.source_map().def_span(sp);
+    let sp = tcx.sess.source_map().guess_head_span(sp);
 
     if adt.is_union() && !tcx.features().transparent_unions {
         feature_err(
@@ -3875,7 +3875,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 );
             }
 
-            if let Some(def_s) = def_span.map(|sp| tcx.sess.source_map().def_span(sp)) {
+            if let Some(def_s) = def_span.map(|sp| tcx.sess.source_map().guess_head_span(sp)) {
                 err.span_label(def_s, "defined here");
             }
             if sugg_unit {
@@ -4966,7 +4966,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             (&found.kind, self.suggest_fn_call(err, expr, expected, found))
         {
             if let Some(sp) = self.tcx.hir().span_if_local(*def_id) {
-                let sp = self.sess().source_map().def_span(sp);
+                let sp = self.sess().source_map().guess_head_span(sp);
                 err.span_label(sp, &format!("{} defined here", found));
             }
         } else if !self.check_for_cast(err, expr, found, expected) {
diff --git a/src/librustc_typeck/check/pat.rs b/src/librustc_typeck/check/pat.rs
index 0d38fa98bd7..0c4a05e6181 100644
--- a/src/librustc_typeck/check/pat.rs
+++ b/src/librustc_typeck/check/pat.rs
@@ -835,7 +835,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 on_error();
                 return tcx.types.err;
             }
-            Res::Def(DefKind::AssocConst, _) | Res::Def(DefKind::AssocFn, _) => {
+            Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) => {
                 report_unexpected_res(res);
                 return tcx.types.err;
             }
@@ -1020,7 +1020,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             ty::Adt(adt, substs) => (substs, adt),
             _ => span_bug!(pat.span, "struct pattern is not an ADT"),
         };
-        let kind_name = adt.variant_descr();
 
         // Index the struct fields' types.
         let field_map = variant
@@ -1074,7 +1073,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         if !inexistent_fields.is_empty() && !variant.recovered {
             self.error_inexistent_fields(
-                kind_name,
+                adt.variant_descr(),
                 &inexistent_fields,
                 &mut unmentioned_fields,
                 variant,
@@ -1083,18 +1082,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // Require `..` if struct has non_exhaustive attribute.
         if variant.is_field_list_non_exhaustive() && !adt.did.is_local() && !etc {
-            struct_span_err!(
-                tcx.sess,
-                pat.span,
-                E0638,
-                "`..` required with {} marked as non-exhaustive",
-                kind_name
-            )
-            .emit();
+            self.error_foreign_non_exhaustive_spat(pat, adt.variant_descr(), fields.is_empty());
         }
 
         // Report an error if incorrect number of the fields were specified.
-        if kind_name == "union" {
+        if adt.is_union() {
             if fields.len() != 1 {
                 tcx.sess
                     .struct_span_err(pat.span, "union patterns should have exactly one field")
@@ -1109,6 +1101,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         no_field_errors
     }
 
+    fn error_foreign_non_exhaustive_spat(&self, pat: &Pat<'_>, descr: &str, no_fields: bool) {
+        let sess = self.tcx.sess;
+        let sm = sess.source_map();
+        let sp_brace = sm.end_point(pat.span);
+        let sp_comma = sm.end_point(pat.span.with_hi(sp_brace.hi()));
+        let sugg = if no_fields || sp_brace != sp_comma { ".. }" } else { ", .. }" };
+
+        let mut err = struct_span_err!(
+            sess,
+            pat.span,
+            E0638,
+            "`..` required with {} marked as non-exhaustive",
+            descr
+        );
+        err.span_suggestion_verbose(
+            sp_comma,
+            "add `..` at the end of the field list to ignore all other fields",
+            sugg.to_string(),
+            Applicability::MachineApplicable,
+        );
+        err.emit();
+    }
+
     fn error_field_already_bound(&self, span: Span, ident: ast::Ident, other_field: Span) {
         struct_span_err!(
             self.tcx.sess,
diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs
index 27b2c19499c..6ed4f62c4be 100644
--- a/src/librustc_typeck/coherence/mod.rs
+++ b/src/librustc_typeck/coherence/mod.rs
@@ -20,7 +20,7 @@ mod unsafety;
 
 /// Obtains the span of just the impl header of `impl_def_id`.
 fn impl_header_span(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Span {
-    tcx.sess.source_map().def_span(tcx.span_of_impl(impl_def_id).unwrap())
+    tcx.sess.source_map().guess_head_span(tcx.span_of_impl(impl_def_id).unwrap())
 }
 
 fn check_impl(tcx: TyCtxt<'_>, impl_def_id: DefId, trait_ref: ty::TraitRef<'_>) {
diff --git a/src/librustc_typeck/coherence/orphan.rs b/src/librustc_typeck/coherence/orphan.rs
index fc77aad8688..cc0ad364177 100644
--- a/src/librustc_typeck/coherence/orphan.rs
+++ b/src/librustc_typeck/coherence/orphan.rs
@@ -34,7 +34,7 @@ impl ItemLikeVisitor<'v> for OrphanChecker<'tcx> {
             let trait_ref = self.tcx.impl_trait_ref(def_id).unwrap();
             let trait_def_id = trait_ref.def_id;
             let sm = self.tcx.sess.source_map();
-            let sp = sm.def_span(item.span);
+            let sp = sm.guess_head_span(item.span);
             match traits::orphan_check(self.tcx, def_id) {
                 Ok(()) => {}
                 Err(traits::OrphanCheckErr::NonLocalInputType(tys)) => {
diff --git a/src/test/ui/did_you_mean/issue-40396.stderr b/src/test/ui/did_you_mean/issue-40396.stderr
index f952136a7bf..10972697f9f 100644
--- a/src/test/ui/did_you_mean/issue-40396.stderr
+++ b/src/test/ui/did_you_mean/issue-40396.stderr
@@ -2,16 +2,8 @@ error: comparison operators cannot be chained
   --> $DIR/issue-40396.rs:2:20
    |
 LL |     (0..13).collect<Vec<i32>>();
-   |                    ^^^^^
+   |                    ^   ^
    |
-help: split the comparison into two...
-   |
-LL |     (0..13).collect < Vec && Vec <i32>>();
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-help: ...or parenthesize one of the comparisons
-   |
-LL |     ((0..13).collect < Vec) <i32>>();
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 help: use `::<...>` instead of `<...>` to specify type arguments
    |
 LL |     (0..13).collect::<Vec<i32>>();
@@ -21,7 +13,7 @@ error: comparison operators cannot be chained
   --> $DIR/issue-40396.rs:4:8
    |
 LL |     Vec<i32>::new();
-   |        ^^^^^
+   |        ^   ^
    |
 help: use `::<...>` instead of `<...>` to specify type arguments
    |
@@ -32,16 +24,8 @@ error: comparison operators cannot be chained
   --> $DIR/issue-40396.rs:6:20
    |
 LL |     (0..13).collect<Vec<i32>();
-   |                    ^^^^^
-   |
-help: split the comparison into two...
-   |
-LL |     (0..13).collect < Vec && Vec <i32>();
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-help: ...or parenthesize one of the comparisons
+   |                    ^   ^
    |
-LL |     ((0..13).collect < Vec) <i32>();
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 help: use `::<...>` instead of `<...>` to specify type arguments
    |
 LL |     (0..13).collect::<Vec<i32>();
diff --git a/src/test/ui/parser/chained-comparison-suggestion.rs b/src/test/ui/parser/chained-comparison-suggestion.rs
index 0431196f174..bbd46082c9f 100644
--- a/src/test/ui/parser/chained-comparison-suggestion.rs
+++ b/src/test/ui/parser/chained-comparison-suggestion.rs
@@ -37,4 +37,17 @@ fn comp8() {
     //~^ ERROR mismatched types
 }
 
+fn comp9() {
+    1 == 2 < 3; //~ ERROR comparison operators cannot be chained
+}
+
+fn comp10() {
+    1 > 2 == false; //~ ERROR comparison operators cannot be chained
+}
+
+fn comp11() {
+    1 == 2 == 3; //~ ERROR comparison operators cannot be chained
+    //~^ ERROR mismatched types
+}
+
 fn main() {}
diff --git a/src/test/ui/parser/chained-comparison-suggestion.stderr b/src/test/ui/parser/chained-comparison-suggestion.stderr
index 5c10a4599dd..067920d12f4 100644
--- a/src/test/ui/parser/chained-comparison-suggestion.stderr
+++ b/src/test/ui/parser/chained-comparison-suggestion.stderr
@@ -2,127 +2,122 @@ error: comparison operators cannot be chained
   --> $DIR/chained-comparison-suggestion.rs:4:7
    |
 LL |     1 < 2 <= 3;
-   |       ^^^^^^
+   |       ^   ^^
    |
-help: split the comparison into two...
+help: split the comparison into two
    |
 LL |     1 < 2 && 2 <= 3;
-   |     ^^^^^^^^^^^^^
-help: ...or parenthesize one of the comparisons
-   |
-LL |     (1 < 2) <= 3;
-   |     ^^^^^^^^^^
+   |           ^^^^
 
 error: comparison operators cannot be chained
   --> $DIR/chained-comparison-suggestion.rs:9:7
    |
 LL |     1 < 2 < 3;
-   |       ^^^^^
+   |       ^   ^
    |
-   = help: use `::<...>` instead of `<...>` to specify type arguments
-   = help: or use `(...)` if you meant to specify fn arguments
-help: split the comparison into two...
+help: split the comparison into two
    |
 LL |     1 < 2 && 2 < 3;
-   |     ^^^^^^^^^^^^
-help: ...or parenthesize one of the comparisons
-   |
-LL |     (1 < 2) < 3;
-   |     ^^^^^^^^^
+   |           ^^^^
 
 error: comparison operators cannot be chained
   --> $DIR/chained-comparison-suggestion.rs:13:7
    |
 LL |     1 <= 2 < 3;
-   |       ^^^^^^
+   |       ^^   ^
    |
-help: split the comparison into two...
+help: split the comparison into two
    |
 LL |     1 <= 2 && 2 < 3;
-   |     ^^^^^^^^^^^^^
-help: ...or parenthesize one of the comparisons
-   |
-LL |     (1 <= 2) < 3;
-   |     ^^^^^^^^^^
+   |            ^^^^
 
 error: comparison operators cannot be chained
   --> $DIR/chained-comparison-suggestion.rs:18:7
    |
 LL |     1 <= 2 <= 3;
-   |       ^^^^^^^
+   |       ^^   ^^
    |
-help: split the comparison into two...
+help: split the comparison into two
    |
 LL |     1 <= 2 && 2 <= 3;
-   |     ^^^^^^^^^^^^^^
-help: ...or parenthesize one of the comparisons
-   |
-LL |     (1 <= 2) <= 3;
-   |     ^^^^^^^^^^^
+   |            ^^^^
 
 error: comparison operators cannot be chained
   --> $DIR/chained-comparison-suggestion.rs:23:7
    |
 LL |     1 > 2 >= 3;
-   |       ^^^^^^
+   |       ^   ^^
    |
-help: split the comparison into two...
+help: split the comparison into two
    |
 LL |     1 > 2 && 2 >= 3;
-   |     ^^^^^^^^^^^^^
-help: ...or parenthesize one of the comparisons
-   |
-LL |     (1 > 2) >= 3;
-   |     ^^^^^^^^^^
+   |           ^^^^
 
 error: comparison operators cannot be chained
   --> $DIR/chained-comparison-suggestion.rs:28:7
    |
 LL |     1 > 2 > 3;
-   |       ^^^^^
+   |       ^   ^
    |
-   = help: use `::<...>` instead of `<...>` to specify type arguments
-   = help: or use `(...)` if you meant to specify fn arguments
-help: split the comparison into two...
+help: split the comparison into two
    |
 LL |     1 > 2 && 2 > 3;
-   |     ^^^^^^^^^^^^
-help: ...or parenthesize one of the comparisons
-   |
-LL |     (1 > 2) > 3;
-   |     ^^^^^^^^^
+   |           ^^^^
 
 error: comparison operators cannot be chained
   --> $DIR/chained-comparison-suggestion.rs:32:7
    |
 LL |     1 >= 2 > 3;
-   |       ^^^^^^
+   |       ^^   ^
    |
-   = help: use `::<...>` instead of `<...>` to specify type arguments
-   = help: or use `(...)` if you meant to specify fn arguments
-help: split the comparison into two...
+help: split the comparison into two
    |
 LL |     1 >= 2 && 2 > 3;
-   |     ^^^^^^^^^^^^^
-help: ...or parenthesize one of the comparisons
-   |
-LL |     (1 >= 2) > 3;
-   |     ^^^^^^^^^^
+   |            ^^^^
 
 error: comparison operators cannot be chained
   --> $DIR/chained-comparison-suggestion.rs:36:7
    |
 LL |     1 >= 2 >= 3;
-   |       ^^^^^^^
+   |       ^^   ^^
    |
-help: split the comparison into two...
+help: split the comparison into two
    |
 LL |     1 >= 2 && 2 >= 3;
-   |     ^^^^^^^^^^^^^^
-help: ...or parenthesize one of the comparisons
+   |            ^^^^
+
+error: comparison operators cannot be chained
+  --> $DIR/chained-comparison-suggestion.rs:41:7
+   |
+LL |     1 == 2 < 3;
+   |       ^^   ^
    |
-LL |     (1 >= 2) >= 3;
-   |     ^^^^^^^^^^^
+help: parenthesize the comparison
+   |
+LL |     1 == (2 < 3);
+   |          ^     ^
+
+error: comparison operators cannot be chained
+  --> $DIR/chained-comparison-suggestion.rs:45:7
+   |
+LL |     1 > 2 == false;
+   |       ^   ^^
+   |
+help: parenthesize the comparison
+   |
+LL |     (1 > 2) == false;
+   |     ^     ^
+
+error: comparison operators cannot be chained
+  --> $DIR/chained-comparison-suggestion.rs:49:7
+   |
+LL |     1 == 2 == 3;
+   |       ^^   ^^
+   |
+help: split the comparison into two
+   |
+LL |     1 == 2 && 2 == 3;
+   |            ^^^^
 
 error[E0308]: mismatched types
   --> $DIR/chained-comparison-suggestion.rs:4:14
@@ -154,6 +149,12 @@ error[E0308]: mismatched types
 LL |     1 >= 2 >= 3;
    |               ^ expected `bool`, found integer
 
-error: aborting due to 13 previous errors
+error[E0308]: mismatched types
+  --> $DIR/chained-comparison-suggestion.rs:49:15
+   |
+LL |     1 == 2 == 3;
+   |               ^ expected `bool`, found integer
+
+error: aborting due to 17 previous errors
 
 For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/parser/require-parens-for-chained-comparison.rs b/src/test/ui/parser/require-parens-for-chained-comparison.rs
index e27b03dddc5..4e97904ed6d 100644
--- a/src/test/ui/parser/require-parens-for-chained-comparison.rs
+++ b/src/test/ui/parser/require-parens-for-chained-comparison.rs
@@ -4,11 +4,11 @@ struct X;
 fn main() {
     false == false == false;
     //~^ ERROR comparison operators cannot be chained
+    //~| HELP split the comparison into two
 
     false == 0 < 2;
     //~^ ERROR comparison operators cannot be chained
-    //~| ERROR mismatched types
-    //~| ERROR mismatched types
+    //~| HELP parenthesize the comparison
 
     f<X>();
     //~^ ERROR comparison operators cannot be chained
@@ -16,8 +16,6 @@ fn main() {
 
     f<Result<Option<X>, Option<Option<X>>>(1, 2);
     //~^ ERROR comparison operators cannot be chained
-    //~| HELP split the comparison into two...
-    //~| ...or parenthesize one of the comparisons
     //~| HELP use `::<...>` instead of `<...>` to specify type arguments
 
     use std::convert::identity;
diff --git a/src/test/ui/parser/require-parens-for-chained-comparison.stderr b/src/test/ui/parser/require-parens-for-chained-comparison.stderr
index 44edf2de7f8..7001aa8e8a1 100644
--- a/src/test/ui/parser/require-parens-for-chained-comparison.stderr
+++ b/src/test/ui/parser/require-parens-for-chained-comparison.stderr
@@ -2,19 +2,29 @@ error: comparison operators cannot be chained
   --> $DIR/require-parens-for-chained-comparison.rs:5:11
    |
 LL |     false == false == false;
-   |           ^^^^^^^^^^^
+   |           ^^       ^^
+   |
+help: split the comparison into two
+   |
+LL |     false == false && false == false;
+   |                    ^^^^^^^^
 
 error: comparison operators cannot be chained
-  --> $DIR/require-parens-for-chained-comparison.rs:8:11
+  --> $DIR/require-parens-for-chained-comparison.rs:9:11
    |
 LL |     false == 0 < 2;
-   |           ^^^^^^
+   |           ^^   ^
+   |
+help: parenthesize the comparison
+   |
+LL |     false == (0 < 2);
+   |              ^     ^
 
 error: comparison operators cannot be chained
   --> $DIR/require-parens-for-chained-comparison.rs:13:6
    |
 LL |     f<X>();
-   |      ^^^
+   |      ^ ^
    |
 help: use `::<...>` instead of `<...>` to specify type arguments
    |
@@ -25,42 +35,21 @@ error: comparison operators cannot be chained
   --> $DIR/require-parens-for-chained-comparison.rs:17:6
    |
 LL |     f<Result<Option<X>, Option<Option<X>>>(1, 2);
-   |      ^^^^^^^^
-   |
-help: split the comparison into two...
-   |
-LL |     f < Result && Result <Option<X>, Option<Option<X>>>(1, 2);
-   |     ^^^^^^^^^^^^^^^^^^^^^^
-help: ...or parenthesize one of the comparisons
+   |      ^      ^
    |
-LL |     (f < Result) <Option<X>, Option<Option<X>>>(1, 2);
-   |     ^^^^^^^^^^^^^^
 help: use `::<...>` instead of `<...>` to specify type arguments
    |
 LL |     f::<Result<Option<X>, Option<Option<X>>>(1, 2);
    |      ^^
 
 error: comparison operators cannot be chained
-  --> $DIR/require-parens-for-chained-comparison.rs:24:21
+  --> $DIR/require-parens-for-chained-comparison.rs:22:21
    |
 LL |     let _ = identity<u8>;
-   |                     ^^^^
+   |                     ^  ^
    |
    = help: use `::<...>` instead of `<...>` to specify type arguments
    = help: or use `(...)` if you meant to specify fn arguments
 
-error[E0308]: mismatched types
-  --> $DIR/require-parens-for-chained-comparison.rs:8:14
-   |
-LL |     false == 0 < 2;
-   |              ^ expected `bool`, found integer
-
-error[E0308]: mismatched types
-  --> $DIR/require-parens-for-chained-comparison.rs:8:18
-   |
-LL |     false == 0 < 2;
-   |                  ^ expected `bool`, found integer
-
-error: aborting due to 7 previous errors
+error: aborting due to 5 previous errors
 
-For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.rs b/src/test/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.rs
index 32c638bcbcc..f1680e9e888 100644
--- a/src/test/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.rs
+++ b/src/test/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.rs
@@ -45,19 +45,19 @@ fn main() {
     *b = NC;
     let ref a @ box ref mut b = Box::new(NC);
     //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable
-    //~| ERROR cannot borrow `_` as mutable because it is also borrowed as immutable
+    //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable
     *b = NC;
     drop(a);
 
     let ref mut a @ box ref b = Box::new(NC);
     //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable
-    //~| ERROR cannot borrow `_` as immutable because it is also borrowed as mutable
+    //~| ERROR cannot borrow value as immutable because it is also borrowed as mutable
     *a = Box::new(NC);
     drop(b);
 
     fn f5(ref mut a @ box ref b: Box<NC>) {
         //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable
-        //~| ERROR cannot borrow `_` as immutable because it is also borrowed as mutable
+        //~| ERROR cannot borrow value as immutable because it is also borrowed as mutable
         *a = Box::new(NC);
         drop(b);
     }
@@ -65,7 +65,7 @@ fn main() {
     match Box::new(nc()) {
         ref mut a @ box ref b => {
             //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable
-            //~| ERROR cannot borrow `_` as immutable because it is also borrowed as mutable
+            //~| ERROR cannot borrow value as immutable because it is also borrowed as mutable
             *a = Box::new(NC);
             drop(b);
         }
diff --git a/src/test/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.stderr b/src/test/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.stderr
index 5534d0a75e6..5ce546f08bf 100644
--- a/src/test/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.stderr
+++ b/src/test/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.stderr
@@ -99,7 +99,7 @@ LL |         a @ box b => {}
    |         |       value used here after move
    |         value moved here
 
-error[E0502]: cannot borrow `_` as mutable because it is also borrowed as immutable
+error[E0502]: cannot borrow value as mutable because it is also borrowed as immutable
   --> $DIR/borrowck-pat-at-and-box.rs:46:21
    |
 LL |     let ref a @ box ref mut b = Box::new(NC);
@@ -111,7 +111,7 @@ LL |     let ref a @ box ref mut b = Box::new(NC);
 LL |     drop(a);
    |          - immutable borrow later used here
 
-error[E0502]: cannot borrow `_` as immutable because it is also borrowed as mutable
+error[E0502]: cannot borrow value as immutable because it is also borrowed as mutable
   --> $DIR/borrowck-pat-at-and-box.rs:52:25
    |
 LL |     let ref mut a @ box ref b = Box::new(NC);
@@ -123,7 +123,7 @@ LL |     let ref mut a @ box ref b = Box::new(NC);
 LL |     *a = Box::new(NC);
    |     -- mutable borrow later used here
 
-error[E0502]: cannot borrow `_` as immutable because it is also borrowed as mutable
+error[E0502]: cannot borrow value as immutable because it is also borrowed as mutable
   --> $DIR/borrowck-pat-at-and-box.rs:66:25
    |
 LL |         ref mut a @ box ref b => {
@@ -155,7 +155,7 @@ LL |     fn f2(a @ box b: Box<C>) {}
    |           value moved here
    |           move occurs because value has type `std::boxed::Box<C>`, which does not implement the `Copy` trait
 
-error[E0502]: cannot borrow `_` as immutable because it is also borrowed as mutable
+error[E0502]: cannot borrow value as immutable because it is also borrowed as mutable
   --> $DIR/borrowck-pat-at-and-box.rs:58:27
    |
 LL |     fn f5(ref mut a @ box ref b: Box<NC>) {
diff --git a/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.rs b/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.rs
index 58d4a9b018c..2b5e339c639 100644
--- a/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.rs
+++ b/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.rs
@@ -10,7 +10,7 @@ fn main() {
     match &mut Some(1) {
         ref mut z @ &mut Some(ref a) => {
         //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable
-        //~| ERROR cannot borrow `_` as immutable because it is also borrowed as mutable
+        //~| ERROR cannot borrow value as immutable because it is also borrowed as mutable
             **z = None;
             println!("{}", *a);
         }
@@ -47,12 +47,12 @@ fn main() {
 
     let ref mut a @ ref b = u();
     //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable
-    //~| ERROR cannot borrow `_` as immutable because it is also borrowed as mutable
+    //~| ERROR cannot borrow value as immutable because it is also borrowed as mutable
     *a = u();
     drop(b);
     let ref a @ ref mut b = u();
     //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable
-    //~| ERROR cannot borrow `_` as mutable because it is also borrowed as immutable
+    //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable
     *b = u();
     drop(a);
 
@@ -78,8 +78,8 @@ fn main() {
         ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) => {
             //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable
             //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable
-            //~| ERROR cannot borrow `_` as mutable because it is also borrowed as immutable
-            //~| ERROR cannot borrow `_` as mutable because it is also borrowed as immutable
+            //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable
+            //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable
             *b = U;
             drop(a);
         }
@@ -123,15 +123,15 @@ fn main() {
 
     let ref a @ (ref mut b, ref mut c) = (U, U);
     //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable
-    //~| ERROR cannot borrow `_` as mutable because it is also borrowed as immutable
-    //~| ERROR cannot borrow `_` as mutable because it is also borrowed as immutable
+    //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable
+    //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable
     *b = U;
     drop(a);
 
     let ref a @ (ref mut b, ref mut c) = (U, U);
     //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable
-    *b = U; //~| ERROR cannot borrow `_` as mutable because it is also borrowed as immutable
-    *c = U; //~| ERROR cannot borrow `_` as mutable because it is also borrowed as immutable
+    *b = U; //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable
+    *c = U; //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable
     drop(a);
     let ref mut a @ (ref b, ref c) = (U, U);
     //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable
diff --git a/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr b/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr
index 8c6ca888e07..b161054414a 100644
--- a/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr
+++ b/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr
@@ -294,7 +294,7 @@ LL |     fn f4_also_moved(ref a @ ref mut b @ c: U) {}
    |                              |           value moved into `c` here
    |                              value borrowed, by `b`, here
 
-error[E0502]: cannot borrow `_` as immutable because it is also borrowed as mutable
+error[E0502]: cannot borrow value as immutable because it is also borrowed as mutable
   --> $DIR/borrowck-pat-ref-mut-and-ref.rs:11:31
    |
 LL |         ref mut z @ &mut Some(ref a) => {
@@ -306,7 +306,7 @@ LL |         ref mut z @ &mut Some(ref a) => {
 LL |             **z = None;
    |             ---------- mutable borrow later used here
 
-error[E0502]: cannot borrow `_` as immutable because it is also borrowed as mutable
+error[E0502]: cannot borrow value as immutable because it is also borrowed as mutable
   --> $DIR/borrowck-pat-ref-mut-and-ref.rs:48:21
    |
 LL |     let ref mut a @ ref b = u();
@@ -318,7 +318,7 @@ LL |     let ref mut a @ ref b = u();
 LL |     *a = u();
    |     -------- mutable borrow later used here
 
-error[E0502]: cannot borrow `_` as mutable because it is also borrowed as immutable
+error[E0502]: cannot borrow value as mutable because it is also borrowed as immutable
   --> $DIR/borrowck-pat-ref-mut-and-ref.rs:53:17
    |
 LL |     let ref a @ ref mut b = u();
@@ -330,7 +330,7 @@ LL |     let ref a @ ref mut b = u();
 LL |     drop(a);
    |          - immutable borrow later used here
 
-error[E0502]: cannot borrow `_` as mutable because it is also borrowed as immutable
+error[E0502]: cannot borrow value as mutable because it is also borrowed as immutable
   --> $DIR/borrowck-pat-ref-mut-and-ref.rs:78:20
    |
 LL |         ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) => {
@@ -342,7 +342,7 @@ LL |         ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) => {
 LL |             drop(a);
    |                  - immutable borrow later used here
 
-error[E0502]: cannot borrow `_` as mutable because it is also borrowed as immutable
+error[E0502]: cannot borrow value as mutable because it is also borrowed as immutable
   --> $DIR/borrowck-pat-ref-mut-and-ref.rs:78:45
    |
 LL |         ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) => {
@@ -402,7 +402,7 @@ LL |         ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) if { drop(a); false
    |
    = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
 
-error[E0502]: cannot borrow `_` as mutable because it is also borrowed as immutable
+error[E0502]: cannot borrow value as mutable because it is also borrowed as immutable
   --> $DIR/borrowck-pat-ref-mut-and-ref.rs:124:18
    |
 LL |     let ref a @ (ref mut b, ref mut c) = (U, U);
@@ -414,7 +414,7 @@ LL |     let ref a @ (ref mut b, ref mut c) = (U, U);
 LL |     drop(a);
    |          - immutable borrow later used here
 
-error[E0502]: cannot borrow `_` as mutable because it is also borrowed as immutable
+error[E0502]: cannot borrow value as mutable because it is also borrowed as immutable
   --> $DIR/borrowck-pat-ref-mut-and-ref.rs:124:29
    |
 LL |     let ref a @ (ref mut b, ref mut c) = (U, U);
@@ -426,7 +426,7 @@ LL |     let ref a @ (ref mut b, ref mut c) = (U, U);
 LL |     drop(a);
    |          - immutable borrow later used here
 
-error[E0502]: cannot borrow `_` as mutable because it is also borrowed as immutable
+error[E0502]: cannot borrow value as mutable because it is also borrowed as immutable
   --> $DIR/borrowck-pat-ref-mut-and-ref.rs:131:18
    |
 LL |     let ref a @ (ref mut b, ref mut c) = (U, U);
@@ -438,7 +438,7 @@ LL |     let ref a @ (ref mut b, ref mut c) = (U, U);
 LL |     drop(a);
    |          - immutable borrow later used here
 
-error[E0502]: cannot borrow `_` as mutable because it is also borrowed as immutable
+error[E0502]: cannot borrow value as mutable because it is also borrowed as immutable
   --> $DIR/borrowck-pat-ref-mut-and-ref.rs:131:29
    |
 LL |     let ref a @ (ref mut b, ref mut c) = (U, U);
diff --git a/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.rs b/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.rs
index f5c39a7ac52..a208d0087ff 100644
--- a/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.rs
+++ b/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.rs
@@ -27,7 +27,7 @@ fn main() {
 
     let ref mut a @ ref mut b = U;
     //~^ ERROR cannot borrow value as mutable more than once at a time
-    //~| ERROR cannot borrow `_` as mutable more than once at a time
+    //~| ERROR cannot borrow value as mutable more than once at a time
     drop(a);
     let ref mut a @ ref mut b = U;
     //~^ ERROR cannot borrow value as mutable more than once at a time
@@ -37,7 +37,7 @@ fn main() {
 
     let ref mut a @ ref mut b = U;
     //~^ ERROR cannot borrow value as mutable more than once at a time
-    //~| ERROR cannot borrow `_` as mutable more than once at a time
+    //~| ERROR cannot borrow value as mutable more than once at a time
     *a = U;
     let ref mut a @ ref mut b = U;
     //~^ ERROR cannot borrow value as mutable more than once at a time
@@ -95,11 +95,11 @@ fn main() {
         ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
             //~^ ERROR cannot borrow value as mutable more than once at a time
             //~| ERROR cannot borrow value as mutable more than once at a time
-            //~| ERROR cannot borrow `_` as mutable more than once at a time
-            //~| ERROR cannot borrow `_` as mutable more than once at a time
+            //~| ERROR cannot borrow value as mutable more than once at a time
+            //~| ERROR cannot borrow value as mutable more than once at a time
             *a = Err(U);
 
-            // FIXME: The binding name `_` used above makes for problematic diagnostics.
+            // FIXME: The binding name value used above makes for problematic diagnostics.
             // Resolve that somehow...
         }
     }
@@ -107,8 +107,8 @@ fn main() {
         ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
             //~^ ERROR cannot borrow value as mutable more than once at a time
             //~| ERROR cannot borrow value as mutable more than once at a time
-            //~| ERROR cannot borrow `_` as mutable more than once at a time
-            //~| ERROR cannot borrow `_` as mutable more than once at a time
+            //~| ERROR cannot borrow value as mutable more than once at a time
+            //~| ERROR cannot borrow value as mutable more than once at a time
             drop(a);
         }
     }
diff --git a/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.stderr b/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.stderr
index e74f227b5e4..ae7c8f38e1e 100644
--- a/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.stderr
+++ b/src/test/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.stderr
@@ -258,7 +258,7 @@ LL |     fn f4_also_moved(ref mut a @ ref mut b @ c: U) {}
    |                                  |           value moved into `c` here
    |                                  value borrowed, by `b`, here
 
-error[E0499]: cannot borrow `_` as mutable more than once at a time
+error[E0499]: cannot borrow value as mutable more than once at a time
   --> $DIR/borrowck-pat-ref-mut-twice.rs:28:21
    |
 LL |     let ref mut a @ ref mut b = U;
@@ -270,7 +270,7 @@ LL |     let ref mut a @ ref mut b = U;
 LL |     drop(a);
    |          - first borrow later used here
 
-error[E0499]: cannot borrow `_` as mutable more than once at a time
+error[E0499]: cannot borrow value as mutable more than once at a time
   --> $DIR/borrowck-pat-ref-mut-twice.rs:38:21
    |
 LL |     let ref mut a @ ref mut b = U;
@@ -318,7 +318,7 @@ LL |     let a @ &mut (ref mut b, ref mut c) = &mut (U, U);
    |         |                    value borrowed here after move
    |         value moved here
 
-error[E0499]: cannot borrow `_` as mutable more than once at a time
+error[E0499]: cannot borrow value as mutable more than once at a time
   --> $DIR/borrowck-pat-ref-mut-twice.rs:95:24
    |
 LL |         ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
@@ -330,7 +330,7 @@ LL |         ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
 LL |             *a = Err(U);
    |             ----------- first borrow later used here
 
-error[E0499]: cannot borrow `_` as mutable more than once at a time
+error[E0499]: cannot borrow value as mutable more than once at a time
   --> $DIR/borrowck-pat-ref-mut-twice.rs:95:53
    |
 LL |         ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
@@ -342,7 +342,7 @@ LL |         ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
 LL |             *a = Err(U);
    |             ----------- first borrow later used here
 
-error[E0499]: cannot borrow `_` as mutable more than once at a time
+error[E0499]: cannot borrow value as mutable more than once at a time
   --> $DIR/borrowck-pat-ref-mut-twice.rs:107:24
    |
 LL |         ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
@@ -354,7 +354,7 @@ LL |         ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
 LL |             drop(a);
    |                  - first borrow later used here
 
-error[E0499]: cannot borrow `_` as mutable more than once at a time
+error[E0499]: cannot borrow value as mutable more than once at a time
   --> $DIR/borrowck-pat-ref-mut-twice.rs:107:53
    |
 LL |         ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
diff --git a/src/test/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.rs b/src/test/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.rs
index b40c3e3358a..a45497229ac 100644
--- a/src/test/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.rs
+++ b/src/test/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.rs
@@ -29,7 +29,7 @@ fn main() {
     let _a: &NotCopy = a;
     let _b: NotCopy = b;
     let ref mut a @ b = NotCopy; //~ ERROR cannot move out of value because it is borrowed
-    //~^ ERROR cannot move out of `_` because it is borrowed
+    //~^ ERROR cannot move out of value because it is borrowed
     let _a: &NotCopy = a;
     let _b: NotCopy = b;
     match Ok(NotCopy) {
diff --git a/src/test/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.stderr b/src/test/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.stderr
index 19e815a1ae8..141d667c746 100644
--- a/src/test/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.stderr
+++ b/src/test/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.stderr
@@ -44,7 +44,7 @@ LL |         ref a @ b => {
    |         |       value moved into `b` here
    |         value borrowed, by `a`, here
 
-error[E0505]: cannot move out of `_` because it is borrowed
+error[E0505]: cannot move out of value because it is borrowed
   --> $DIR/default-binding-modes-both-sides-independent.rs:31:21
    |
 LL |     let ref mut a @ b = NotCopy;
diff --git a/src/test/ui/rfc-2008-non-exhaustive/struct.stderr b/src/test/ui/rfc-2008-non-exhaustive/struct.stderr
index 4e91e7bff34..3bc38830537 100644
--- a/src/test/ui/rfc-2008-non-exhaustive/struct.stderr
+++ b/src/test/ui/rfc-2008-non-exhaustive/struct.stderr
@@ -62,18 +62,33 @@ error[E0638]: `..` required with struct marked as non-exhaustive
    |
 LL |     let NormalStruct { first_field, second_field } = ns;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add `..` at the end of the field list to ignore all other fields
+   |
+LL |     let NormalStruct { first_field, second_field , .. } = ns;
+   |                                                  ^^^^^^
 
 error[E0638]: `..` required with struct marked as non-exhaustive
   --> $DIR/struct.rs:26:9
    |
 LL |     let TupleStruct { 0: first_field, 1: second_field } = ts;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add `..` at the end of the field list to ignore all other fields
+   |
+LL |     let TupleStruct { 0: first_field, 1: second_field , .. } = ts;
+   |                                                       ^^^^^^
 
 error[E0638]: `..` required with struct marked as non-exhaustive
   --> $DIR/struct.rs:35:9
    |
 LL |     let UnitStruct { } = us;
    |         ^^^^^^^^^^^^^^
+   |
+help: add `..` at the end of the field list to ignore all other fields
+   |
+LL |     let UnitStruct { .. } = us;
+   |                      ^^^^
 
 error: aborting due to 9 previous errors
 
diff --git a/src/test/ui/rfc-2008-non-exhaustive/variant.stderr b/src/test/ui/rfc-2008-non-exhaustive/variant.stderr
index ae4f6aff11a..fbdbb0c9930 100644
--- a/src/test/ui/rfc-2008-non-exhaustive/variant.stderr
+++ b/src/test/ui/rfc-2008-non-exhaustive/variant.stderr
@@ -69,12 +69,22 @@ error[E0638]: `..` required with variant marked as non-exhaustive
    |
 LL |         NonExhaustiveVariants::Struct { field } => ""
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add `..` at the end of the field list to ignore all other fields
+   |
+LL |         NonExhaustiveVariants::Struct { field , .. } => ""
+   |                                               ^^^^^^
 
 error[E0638]: `..` required with variant marked as non-exhaustive
   --> $DIR/variant.rs:30:12
    |
 LL |     if let NonExhaustiveVariants::Struct { field } = variant_struct {
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add `..` at the end of the field list to ignore all other fields
+   |
+LL |     if let NonExhaustiveVariants::Struct { field , .. } = variant_struct {
+   |                                                  ^^^^^^
 
 error: aborting due to 8 previous errors
 
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject 7019b3ed3d539db7429d10a343b69be8c426b57
+Subproject 8a0d4d9c9abc74fd670353094387d62028b40ae