summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--compiler/rustc_ast_pretty/src/pp.rs46
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs4
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs4
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs39
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs8
-rw-r--r--compiler/rustc_typeck/src/check/demand.rs184
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs4
-rw-r--r--library/alloc/src/vec/mod.rs10
-rw-r--r--library/core/src/cmp.rs76
-rw-r--r--library/core/src/intrinsics.rs2
-rw-r--r--library/core/src/ptr/const_ptr.rs2
-rw-r--r--library/core/src/ptr/mut_ptr.rs2
-rw-r--r--src/librustdoc/Cargo.toml1
-rw-r--r--src/librustdoc/html/render/mod.rs2
-rw-r--r--src/librustdoc/html/render/print_item.rs10
-rw-r--r--src/librustdoc/lib.rs56
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs10
-rw-r--r--src/test/rustdoc-gui/sidebar.goml7
-rw-r--r--src/test/rustdoc/source-version-separator.rs31
-rw-r--r--src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.stderr24
-rw-r--r--src/test/ui/did_you_mean/compatible-variants.rs8
-rw-r--r--src/test/ui/did_you_mean/compatible-variants.stderr31
-rw-r--r--src/test/ui/did_you_mean/issue-40396.stderr14
-rw-r--r--src/test/ui/inference/deref-suggestion.stderr2
-rw-r--r--src/test/ui/parser/require-parens-for-chained-comparison.rs24
-rw-r--r--src/test/ui/parser/require-parens-for-chained-comparison.stderr55
-rw-r--r--src/test/ui/suggestions/issue-82566-1.stderr6
-rw-r--r--src/test/ui/suggestions/issue-82566-2.stderr6
29 files changed, 416 insertions, 253 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 85a9f239ab5..18b85103202 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4443,6 +4443,7 @@ version = "0.0.0"
 dependencies = [
  "arrayvec",
  "askama",
+ "atty",
  "expect-test",
  "itertools 0.9.0",
  "minifier",
diff --git a/compiler/rustc_ast_pretty/src/pp.rs b/compiler/rustc_ast_pretty/src/pp.rs
index ea298d28e72..20954a322a5 100644
--- a/compiler/rustc_ast_pretty/src/pp.rs
+++ b/compiler/rustc_ast_pretty/src/pp.rs
@@ -222,29 +222,6 @@ struct PrintStackElem {
 
 const SIZE_INFINITY: isize = 0xffff;
 
-pub fn mk_printer() -> Printer {
-    let linewidth = 78;
-    // Yes 55, it makes the ring buffers big enough to never fall behind.
-    let n: usize = 55 * linewidth;
-    debug!("mk_printer {}", linewidth);
-    Printer {
-        out: String::new(),
-        buf_max_len: n,
-        margin: linewidth as isize,
-        space: linewidth as isize,
-        left: 0,
-        right: 0,
-        // Initialize a single entry; advance_right() will extend it on demand
-        // up to `buf_max_len` elements.
-        buf: vec![BufEntry::default()],
-        left_total: 0,
-        right_total: 0,
-        scan_stack: VecDeque::new(),
-        print_stack: Vec::new(),
-        pending_indentation: 0,
-    }
-}
-
 pub struct Printer {
     out: String,
     buf_max_len: usize,
@@ -288,6 +265,29 @@ impl Default for BufEntry {
 }
 
 impl Printer {
+    pub fn new() -> Self {
+        let linewidth = 78;
+        // Yes 55, it makes the ring buffers big enough to never fall behind.
+        let n: usize = 55 * linewidth;
+        debug!("Printer::new {}", linewidth);
+        Printer {
+            out: String::new(),
+            buf_max_len: n,
+            margin: linewidth as isize,
+            space: linewidth as isize,
+            left: 0,
+            right: 0,
+            // Initialize a single entry; advance_right() will extend it on demand
+            // up to `buf_max_len` elements.
+            buf: vec![BufEntry::default()],
+            left_total: 0,
+            right_total: 0,
+            scan_stack: VecDeque::new(),
+            print_stack: Vec::new(),
+            pending_indentation: 0,
+        }
+    }
+
     pub fn last_token(&self) -> Token {
         self.buf[self.right].token.clone()
     }
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 32a4a0751d8..015cd431deb 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -103,7 +103,7 @@ pub fn print_crate<'a>(
     edition: Edition,
 ) -> String {
     let mut s =
-        State { s: pp::mk_printer(), comments: Some(Comments::new(sm, filename, input)), ann };
+        State { s: pp::Printer::new(), comments: Some(Comments::new(sm, filename, input)), ann };
 
     if is_expanded && !krate.attrs.iter().any(|attr| attr.has_name(sym::no_core)) {
         // We need to print `#![no_std]` (and its feature gate) so that
@@ -910,7 +910,7 @@ impl<'a> PrintState<'a> for State<'a> {
 
 impl<'a> State<'a> {
     pub fn new() -> State<'a> {
-        State { s: pp::mk_printer(), comments: None, ann: &NoAnn }
+        State { s: pp::Printer::new(), comments: None, ann: &NoAnn }
     }
 
     crate fn commasep_cmnt<T, F, G>(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G)
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 1fd226291d1..dabab073551 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -170,7 +170,7 @@ impl<'a> State<'a> {
         ann: &'a dyn PpAnn,
     ) -> State<'a> {
         State {
-            s: pp::mk_printer(),
+            s: pp::Printer::new(),
             comments: Some(Comments::new(sm, filename, input)),
             attrs,
             ann,
@@ -186,7 +186,7 @@ pub fn to_string<F>(ann: &dyn PpAnn, f: F) -> String
 where
     F: FnOnce(&mut State<'_>),
 {
-    let mut printer = State { s: pp::mk_printer(), comments: None, attrs: &|_| &[], ann };
+    let mut printer = State { s: pp::Printer::new(), comments: None, attrs: &|_| &[], ann };
     f(&mut printer);
     printer.s.eof()
 }
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 612d4508565..c41f2d3299b 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -27,7 +27,7 @@ use std::mem::take;
 use tracing::{debug, trace};
 
 const TURBOFISH_SUGGESTION_STR: &str =
-    "use `::<...>` instead of `<...>` to specify type or const arguments";
+    "use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments";
 
 /// Creates a placeholder argument.
 pub(super) fn dummy_arg(ident: Ident) -> Param {
@@ -731,21 +731,28 @@ impl<'a> Parser<'a> {
                     match x {
                         Ok((_, _, false)) => {
                             if self.eat(&token::Gt) {
-                                match self.parse_expr() {
-                                    Ok(_) => {
-                                        e.span_suggestion_verbose(
-                                            binop.span.shrink_to_lo(),
-                                            TURBOFISH_SUGGESTION_STR,
-                                            "::".to_string(),
-                                            Applicability::MaybeIncorrect,
-                                        );
-                                        e.emit();
-                                        *expr =
-                                            self.mk_expr_err(expr.span.to(self.prev_token.span));
-                                        return Ok(());
-                                    }
-                                    Err(mut err) => {
-                                        err.cancel();
+                                let turbo_err = e.span_suggestion_verbose(
+                                    binop.span.shrink_to_lo(),
+                                    TURBOFISH_SUGGESTION_STR,
+                                    "::".to_string(),
+                                    Applicability::MaybeIncorrect,
+                                );
+                                if self.check(&TokenKind::Semi) {
+                                    turbo_err.emit();
+                                    *expr = self.mk_expr_err(expr.span);
+                                    return Ok(());
+                                } else {
+                                    match self.parse_expr() {
+                                        Ok(_) => {
+                                            turbo_err.emit();
+                                            *expr = self
+                                                .mk_expr_err(expr.span.to(self.prev_token.span));
+                                            return Ok(());
+                                        }
+                                        Err(mut err) => {
+                                            turbo_err.cancel();
+                                            err.cancel();
+                                        }
                                     }
                                 }
                             }
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index cd3846d5a22..192e87b4c01 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -1443,7 +1443,7 @@ impl<'a> Parser<'a> {
         &mut self,
         label: Label,
         attrs: AttrVec,
-        consume_colon: bool,
+        mut consume_colon: bool,
     ) -> PResult<'a, P<Expr>> {
         let lo = label.ident.span;
         let label = Some(label);
@@ -1456,6 +1456,12 @@ impl<'a> Parser<'a> {
             self.parse_loop_expr(label, lo, attrs)
         } else if self.check(&token::OpenDelim(token::Brace)) || self.token.is_whole_block() {
             self.parse_block_expr(label, lo, BlockCheckMode::Default, attrs)
+        } else if !ate_colon && (self.check(&TokenKind::Comma) || self.check(&TokenKind::Gt)) {
+            // We're probably inside of a `Path<'a>` that needs a turbofish, so suppress the
+            // "must be followed by a colon" error.
+            self.diagnostic().delay_span_bug(lo, "this label wasn't parsed correctly");
+            consume_colon = false;
+            Ok(self.mk_expr_err(lo))
         } else {
             let msg = "expected `while`, `for`, `loop` or `{` after a label";
             self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit();
diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs
index c351a9f7040..241dcbc64a6 100644
--- a/compiler/rustc_typeck/src/check/demand.rs
+++ b/compiler/rustc_typeck/src/check/demand.rs
@@ -13,7 +13,7 @@ use rustc_middle::ty::adjustment::AllowTwoPhase;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, AssocItem, Ty, TypeAndMut};
-use rustc_span::symbol::sym;
+use rustc_span::symbol::{sym, Symbol};
 use rustc_span::{BytePos, Span};
 
 use super::method::probe;
@@ -24,7 +24,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub fn emit_coerce_suggestions(
         &self,
         err: &mut DiagnosticBuilder<'_>,
-        expr: &hir::Expr<'_>,
+        expr: &hir::Expr<'tcx>,
         expr_ty: Ty<'tcx>,
         expected: Ty<'tcx>,
         expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
@@ -109,7 +109,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub fn demand_coerce(
         &self,
-        expr: &hir::Expr<'_>,
+        expr: &hir::Expr<'tcx>,
         checked_ty: Ty<'tcx>,
         expected: Ty<'tcx>,
         expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
@@ -129,7 +129,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// will be permitted if the diverges flag is currently "always".
     pub fn demand_coerce_diag(
         &self,
-        expr: &hir::Expr<'_>,
+        expr: &hir::Expr<'tcx>,
         checked_ty: Ty<'tcx>,
         expected: Ty<'tcx>,
         expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
@@ -338,31 +338,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 })
                 .collect();
 
-            if let [variant] = &compatible_variants[..] {
-                // Just a single matching variant.
-                err.multipart_suggestion(
-                    &format!("try wrapping the expression in `{}`", variant),
-                    vec![
-                        (expr.span.shrink_to_lo(), format!("{}(", variant)),
-                        (expr.span.shrink_to_hi(), ")".to_string()),
-                    ],
-                    Applicability::MaybeIncorrect,
-                );
-            } else if compatible_variants.len() > 1 {
-                // More than one matching variant.
-                err.multipart_suggestions(
-                    &format!(
-                        "try wrapping the expression in a variant of `{}`",
-                        self.tcx.def_path_str(expected_adt.did)
-                    ),
-                    compatible_variants.into_iter().map(|variant| {
+            let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
+                Some(ident) => format!("{}: ", ident),
+                None => format!(""),
+            };
+
+            match &compatible_variants[..] {
+                [] => { /* No variants to format */ }
+                [variant] => {
+                    // Just a single matching variant.
+                    err.multipart_suggestion_verbose(
+                        &format!("try wrapping the expression in `{}`", variant),
                         vec![
-                            (expr.span.shrink_to_lo(), format!("{}(", variant)),
+                            (expr.span.shrink_to_lo(), format!("{}{}(", prefix, variant)),
                             (expr.span.shrink_to_hi(), ")".to_string()),
-                        ]
-                    }),
-                    Applicability::MaybeIncorrect,
-                );
+                        ],
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+                _ => {
+                    // More than one matching variant.
+                    err.multipart_suggestions(
+                        &format!(
+                            "try wrapping the expression in a variant of `{}`",
+                            self.tcx.def_path_str(expected_adt.did)
+                        ),
+                        compatible_variants.into_iter().map(|variant| {
+                            vec![
+                                (expr.span.shrink_to_lo(), format!("{}{}(", prefix, variant)),
+                                (expr.span.shrink_to_hi(), ")".to_string()),
+                            ]
+                        }),
+                        Applicability::MaybeIncorrect,
+                    );
+                }
             }
         }
     }
@@ -483,33 +492,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    crate fn is_hir_id_from_struct_pattern_shorthand_field(
+    crate fn maybe_get_struct_pattern_shorthand_field(
         &self,
-        hir_id: hir::HirId,
-        sp: Span,
-    ) -> bool {
-        let sm = self.sess().source_map();
-        let parent_id = self.tcx.hir().get_parent_node(hir_id);
-        if let Some(parent) = self.tcx.hir().find(parent_id) {
-            // Account for fields
-            if let Node::Expr(hir::Expr { kind: hir::ExprKind::Struct(_, fields, ..), .. }) = parent
-            {
-                if let Ok(src) = sm.span_to_snippet(sp) {
-                    for field in *fields {
-                        if field.ident.as_str() == src && field.is_shorthand {
-                            return true;
-                        }
+        expr: &hir::Expr<'_>,
+    ) -> Option<Symbol> {
+        let hir = self.tcx.hir();
+        let local = match expr {
+            hir::Expr {
+                kind:
+                    hir::ExprKind::Path(hir::QPath::Resolved(
+                        None,
+                        hir::Path {
+                            res: hir::def::Res::Local(_),
+                            segments: [hir::PathSegment { ident, .. }],
+                            ..
+                        },
+                    )),
+                ..
+            } => Some(ident),
+            _ => None,
+        }?;
+
+        match hir.find(hir.get_parent_node(expr.hir_id))? {
+            Node::Expr(hir::Expr { kind: hir::ExprKind::Struct(_, fields, ..), .. }) => {
+                for field in *fields {
+                    if field.ident.name == local.name && field.is_shorthand {
+                        return Some(local.name);
                     }
                 }
             }
+            _ => {}
         }
-        false
+
+        None
     }
 
     /// If the given `HirId` corresponds to a block with a trailing expression, return that expression
-    crate fn maybe_get_block_expr(&self, hir_id: hir::HirId) -> Option<&'tcx hir::Expr<'tcx>> {
-        match self.tcx.hir().find(hir_id)? {
-            Node::Expr(hir::Expr { kind: hir::ExprKind::Block(block, ..), .. }) => block.expr,
+    crate fn maybe_get_block_expr(&self, expr: &hir::Expr<'tcx>) -> Option<&'tcx hir::Expr<'tcx>> {
+        match expr {
+            hir::Expr { kind: hir::ExprKind::Block(block, ..), .. } => block.expr,
             _ => None,
         }
     }
@@ -547,7 +568,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// `&mut`!".
     pub fn check_ref(
         &self,
-        expr: &hir::Expr<'_>,
+        expr: &hir::Expr<'tcx>,
         checked_ty: Ty<'tcx>,
         expected: Ty<'tcx>,
     ) -> Option<(Span, &'static str, String, Applicability, bool /* verbose */)> {
@@ -565,9 +586,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             s.strip_prefix(old).map(|stripped| new.to_string() + stripped)
         };
 
-        let is_struct_pat_shorthand_field =
-            self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, sp);
-
         // `ExprKind::DropTemps` is semantically irrelevant for these suggestions.
         let expr = expr.peel_drop_temps();
 
@@ -661,11 +679,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 false,
                             ));
                         }
-                        let field_name = if is_struct_pat_shorthand_field {
-                            format!("{}: ", sugg_expr)
-                        } else {
-                            String::new()
+
+                        let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
+                            Some(ident) => format!("{}: ", ident),
+                            None => format!(""),
                         };
+
                         if let Some(hir::Node::Expr(hir::Expr {
                             kind: hir::ExprKind::Assign(left_expr, ..),
                             ..
@@ -695,14 +714,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             hir::Mutability::Mut => (
                                 sp,
                                 "consider mutably borrowing here",
-                                format!("{}&mut {}", field_name, sugg_expr),
+                                format!("{}&mut {}", prefix, sugg_expr),
                                 Applicability::MachineApplicable,
                                 false,
                             ),
                             hir::Mutability::Not => (
                                 sp,
                                 "consider borrowing here",
-                                format!("{}&{}", field_name, sugg_expr),
+                                format!("{}&{}", prefix, sugg_expr),
                                 Applicability::MachineApplicable,
                                 false,
                             ),
@@ -846,32 +865,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         if self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp)
                             || checked_ty.is_box()
                         {
-                            if let Ok(code) = sm.span_to_snippet(expr.span) {
-                                let message = if checked_ty.is_box() {
-                                    "consider unboxing the value"
-                                } else if checked_ty.is_region_ptr() {
-                                    "consider dereferencing the borrow"
-                                } else {
-                                    "consider dereferencing the type"
-                                };
-                                let (span, suggestion) = if is_struct_pat_shorthand_field {
-                                    (expr.span, format!("{}: *{}", code, code))
-                                } else if self.is_else_if_block(expr) {
-                                    // Don't suggest nonsense like `else *if`
-                                    return None;
-                                } else if let Some(expr) = self.maybe_get_block_expr(expr.hir_id) {
-                                    (expr.span.shrink_to_lo(), "*".to_string())
-                                } else {
-                                    (expr.span.shrink_to_lo(), "*".to_string())
-                                };
-                                return Some((
-                                    span,
-                                    message,
-                                    suggestion,
-                                    Applicability::MachineApplicable,
-                                    true,
-                                ));
-                            }
+                            let message = if checked_ty.is_box() {
+                                "consider unboxing the value"
+                            } else if checked_ty.is_region_ptr() {
+                                "consider dereferencing the borrow"
+                            } else {
+                                "consider dereferencing the type"
+                            };
+                            let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
+                                Some(ident) => format!("{}: ", ident),
+                                None => format!(""),
+                            };
+                            let (span, suggestion) = if self.is_else_if_block(expr) {
+                                // Don't suggest nonsense like `else *if`
+                                return None;
+                            } else if let Some(expr) = self.maybe_get_block_expr(expr) {
+                                // prefix should be empty here..
+                                (expr.span.shrink_to_lo(), "*".to_string())
+                            } else {
+                                (expr.span.shrink_to_lo(), format!("{}*", prefix))
+                            };
+                            return Some((
+                                span,
+                                message,
+                                suggestion,
+                                Applicability::MachineApplicable,
+                                true,
+                            ));
                         }
                     }
                 }
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
index e8a0cc946b5..473c848ad8f 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
@@ -208,7 +208,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub fn suggest_deref_ref_or_into(
         &self,
         err: &mut DiagnosticBuilder<'_>,
-        expr: &hir::Expr<'_>,
+        expr: &hir::Expr<'tcx>,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
         expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
@@ -231,7 +231,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
         } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
             let is_struct_pat_shorthand_field =
-                self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, expr.span);
+                self.maybe_get_struct_pattern_shorthand_field(expr).is_some();
             let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
             if !methods.is_empty() {
                 if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) {
diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs
index 78f989e730d..16ba5c008cd 100644
--- a/library/alloc/src/vec/mod.rs
+++ b/library/alloc/src/vec/mod.rs
@@ -1372,9 +1372,12 @@ impl<T, A: Allocator> Vec<T, A> {
     ///
     /// Note: Because this shifts over the remaining elements, it has a
     /// worst-case performance of *O*(*n*). If you don't need the order of elements
-    /// to be preserved, use [`swap_remove`] instead.
+    /// to be preserved, use [`swap_remove`] instead. If you'd like to remove
+    /// elements from the beginning of the `Vec`, consider using
+    /// [`VecDeque::pop_front`] instead.
     ///
     /// [`swap_remove`]: Vec::swap_remove
+    /// [`VecDeque::pop_front`]: crate::collections::VecDeque::pop_front
     ///
     /// # Panics
     ///
@@ -1735,6 +1738,11 @@ impl<T, A: Allocator> Vec<T, A> {
     /// Removes the last element from a vector and returns it, or [`None`] if it
     /// is empty.
     ///
+    /// If you'd like to pop the first element, consider using
+    /// [`VecDeque::pop_front`] instead.
+    ///
+    /// [`VecDeque::pop_front`]: crate::collections::VecDeque::pop_front
+    ///
     /// # Examples
     ///
     /// ```
diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs
index deed9901cc9..f89cf812e97 100644
--- a/library/core/src/cmp.rs
+++ b/library/core/src/cmp.rs
@@ -661,20 +661,37 @@ impl<T: Clone> Clone for Reverse<T> {
 ///
 /// ## Derivable
 ///
-/// This trait can be used with `#[derive]`. When `derive`d on structs, it will produce a
-/// [lexicographic](https://en.wikipedia.org/wiki/Lexicographic_order) ordering based on the top-to-bottom declaration order of the struct's members.
-/// When `derive`d on enums, variants are ordered by their top-to-bottom discriminant order.
-/// This means variants at the top are less than variants at the bottom.
-/// Here's an example:
+/// This trait can be used with `#[derive]`.
+///
+/// When `derive`d on structs, it will produce a
+/// [lexicographic](https://en.wikipedia.org/wiki/Lexicographic_order) ordering
+/// based on the top-to-bottom declaration order of the struct's members.
+///
+/// When `derive`d on enums, variants are ordered by their discriminants.
+/// By default, the discriminant is smallest for variants at the top, and
+/// largest for variants at the bottom. Here's an example:
 ///
 /// ```
-/// #[derive(PartialEq, PartialOrd)]
-/// enum Size {
-///     Small,
-///     Large,
+/// #[derive(PartialEq, Eq, PartialOrd, Ord)]
+/// enum E {
+///     Top,
+///     Bottom,
 /// }
 ///
-/// assert!(Size::Small < Size::Large);
+/// assert!(E::Top < E::Bottom);
+/// ```
+///
+/// However, manually setting the discriminants can override this default
+/// behavior:
+///
+/// ```
+/// #[derive(PartialEq, Eq, PartialOrd, Ord)]
+/// enum E {
+///     Top = 2,
+///     Bottom = 1,
+/// }
+///
+/// assert!(E::Bottom < E::Top);
 /// ```
 ///
 /// ## Lexicographical comparison
@@ -895,9 +912,38 @@ impl PartialOrd for Ordering {
 ///
 /// ## Derivable
 ///
-/// This trait can be used with `#[derive]`. When `derive`d on structs, it will produce a
-/// lexicographic ordering based on the top-to-bottom declaration order of the struct's members.
-/// When `derive`d on enums, variants are ordered by their top-to-bottom discriminant order.
+/// This trait can be used with `#[derive]`.
+///
+/// When `derive`d on structs, it will produce a
+/// [lexicographic](https://en.wikipedia.org/wiki/Lexicographic_order) ordering
+/// based on the top-to-bottom declaration order of the struct's members.
+///
+/// When `derive`d on enums, variants are ordered by their discriminants.
+/// By default, the discriminant is smallest for variants at the top, and
+/// largest for variants at the bottom. Here's an example:
+///
+/// ```
+/// #[derive(PartialEq, PartialOrd)]
+/// enum E {
+///     Top,
+///     Bottom,
+/// }
+///
+/// assert!(E::Top < E::Bottom);
+/// ```
+///
+/// However, manually setting the discriminants can override this default
+/// behavior:
+///
+/// ```
+/// #[derive(PartialEq, PartialOrd)]
+/// enum E {
+///     Top = 2,
+///     Bottom = 1,
+/// }
+///
+/// assert!(E::Bottom < E::Top);
+/// ```
 ///
 /// ## How can I implement `PartialOrd`?
 ///
@@ -970,8 +1016,8 @@ impl PartialOrd for Ordering {
 /// # Examples
 ///
 /// ```
-/// let x : u32 = 0;
-/// let y : u32 = 1;
+/// let x: u32 = 0;
+/// let y: u32 = 1;
 ///
 /// assert_eq!(x < y, true);
 /// assert_eq!(x.lt(&y), true);
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index acbb612352b..9781dc320ed 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -1893,7 +1893,7 @@ extern "rust-intrinsic" {
     pub fn nontemporal_store<T>(ptr: *mut T, val: T);
 
     /// See documentation of `<*const T>::offset_from` for details.
-    #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "41079")]
+    #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "92980")]
     pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize;
 
     /// See documentation of `<*const T>::guaranteed_eq` for details.
diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index 7b826f921ca..485a5965f4c 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -439,7 +439,7 @@ impl<T: ?Sized> *const T {
     /// }
     /// ```
     #[stable(feature = "ptr_offset_from", since = "1.47.0")]
-    #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "41079")]
+    #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "92980")]
     #[inline]
     pub const unsafe fn offset_from(self, origin: *const T) -> isize
     where
diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs
index 6c50d405297..1412e836ebf 100644
--- a/library/core/src/ptr/mut_ptr.rs
+++ b/library/core/src/ptr/mut_ptr.rs
@@ -617,7 +617,7 @@ impl<T: ?Sized> *mut T {
     /// }
     /// ```
     #[stable(feature = "ptr_offset_from", since = "1.47.0")]
-    #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "41079")]
+    #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "92980")]
     #[inline(always)]
     pub const unsafe fn offset_from(self, origin: *const T) -> isize
     where
diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml
index bdf756df6fd..ccb844cd10b 100644
--- a/src/librustdoc/Cargo.toml
+++ b/src/librustdoc/Cargo.toml
@@ -9,6 +9,7 @@ path = "lib.rs"
 [dependencies]
 arrayvec = { version = "0.7", default-features = false }
 askama = { version = "0.11", default-features = false }
+atty = "0.2"
 pulldown-cmark = { version = "0.9", default-features = false }
 minifier = "0.0.41"
 rayon = "1.5.1"
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index f0cbe79bd06..7f3e5d15de1 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -1736,7 +1736,7 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
     {
         write!(
             buffer,
-            "<h2 class=\"location\">{}{}</h2>",
+            "<h2 class=\"location\"><a href=\"#\">{}{}</a></h2>",
             match *it.kind {
                 clean::StructItem(..) => "Struct ",
                 clean::TraitItem(..) => "Trait ",
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 67821f19a23..f2c111495ed 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -670,7 +670,11 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
         }
         write!(w, "<div id=\"{}\" class=\"method has-srclink\">", id);
         write!(w, "<div class=\"rightside\">");
-        render_stability_since(w, m, t, cx.tcx());
+
+        let has_stability = render_stability_since(w, m, t, cx.tcx());
+        if has_stability {
+            w.write_str(" · ");
+        }
         write_srclink(cx, m, w);
         write!(w, "</div>");
         write!(w, "<h4 class=\"code-header\">");
@@ -1457,14 +1461,14 @@ fn render_stability_since(
     item: &clean::Item,
     containing_item: &clean::Item,
     tcx: TyCtxt<'_>,
-) {
+) -> bool {
     render_stability_since_raw(
         w,
         item.stable_since(tcx),
         item.const_stability(tcx),
         containing_item.stable_since(tcx),
         containing_item.const_stable_since(tcx),
-    );
+    )
 }
 
 fn compare_impl<'a, 'b>(lhs: &'a &&Impl, rhs: &'b &&Impl, cx: &Context<'_>) -> Ordering {
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index a647a0fbfa5..d854aa86b3a 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -71,7 +71,8 @@ extern crate tikv_jemalloc_sys;
 use tikv_jemalloc_sys as jemalloc_sys;
 
 use std::default::Default;
-use std::env;
+use std::env::{self, VarError};
+use std::io;
 use std::process;
 
 use rustc_driver::{abort_on_err, describe_lints};
@@ -179,47 +180,20 @@ pub fn main() {
 }
 
 fn init_logging() {
-    use std::io;
-
-    // FIXME remove these and use winapi 0.3 instead
-    // Duplicates: bootstrap/compile.rs, librustc_errors/emitter.rs, rustc_driver/lib.rs
-    #[cfg(unix)]
-    fn stdout_isatty() -> bool {
-        extern crate libc;
-        unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 }
-    }
-
-    #[cfg(windows)]
-    fn stdout_isatty() -> bool {
-        extern crate winapi;
-        use winapi::um::consoleapi::GetConsoleMode;
-        use winapi::um::processenv::GetStdHandle;
-        use winapi::um::winbase::STD_OUTPUT_HANDLE;
-
-        unsafe {
-            let handle = GetStdHandle(STD_OUTPUT_HANDLE);
-            let mut out = 0;
-            GetConsoleMode(handle, &mut out) != 0
-        }
-    }
-
-    let color_logs = match std::env::var("RUSTDOC_LOG_COLOR") {
-        Ok(value) => match value.as_ref() {
-            "always" => true,
-            "never" => false,
-            "auto" => stdout_isatty(),
-            _ => early_error(
-                ErrorOutputType::default(),
-                &format!(
-                    "invalid log color value '{}': expected one of always, never, or auto",
-                    value
-                ),
-            ),
-        },
-        Err(std::env::VarError::NotPresent) => stdout_isatty(),
-        Err(std::env::VarError::NotUnicode(_value)) => early_error(
+    let color_logs = match std::env::var("RUSTDOC_LOG_COLOR").as_deref() {
+        Ok("always") => true,
+        Ok("never") => false,
+        Ok("auto") | Err(VarError::NotPresent) => atty::is(atty::Stream::Stdout),
+        Ok(value) => early_error(
+            ErrorOutputType::default(),
+            &format!("invalid log color value '{}': expected one of always, never, or auto", value),
+        ),
+        Err(VarError::NotUnicode(value)) => early_error(
             ErrorOutputType::default(),
-            "non-Unicode log color value: expected one of always, never, or auto",
+            &format!(
+                "invalid log color value '{}': expected one of always, never, or auto",
+                value.to_string_lossy()
+            ),
         ),
     };
     let filter = tracing_subscriber::EnvFilter::from_env("RUSTDOC_LOG");
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 1544fae962c..1a9794e75bc 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -436,8 +436,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
                 }
                 match tcx.type_of(did).kind() {
                     ty::Adt(def, _) if def.is_enum() => {
-                        if let Some(field) =
-                            def.all_fields().find(|f| f.ident(tcx).name == variant_field_name)
+                        if let Some(field) = def.all_fields().find(|f| f.name == variant_field_name)
                         {
                             Ok((ty_res, Some(ItemFragment(FragmentKind::VariantField, field.did))))
                         } else {
@@ -806,11 +805,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
                     ty::Adt(def, _) if !def.is_enum() => def,
                     _ => return None,
                 };
-                let field = def
-                    .non_enum_variant()
-                    .fields
-                    .iter()
-                    .find(|item| item.ident(tcx).name == item_name)?;
+                let field =
+                    def.non_enum_variant().fields.iter().find(|item| item.name == item_name)?;
                 Some((root_res, ItemFragment(FragmentKind::StructField, field.did)))
             }
             Res::Def(DefKind::Trait, did) => tcx
diff --git a/src/test/rustdoc-gui/sidebar.goml b/src/test/rustdoc-gui/sidebar.goml
index f9c707f81e7..9289244f6f8 100644
--- a/src/test/rustdoc-gui/sidebar.goml
+++ b/src/test/rustdoc-gui/sidebar.goml
@@ -22,6 +22,13 @@ click: "#structs + .item-table .item-left > a"
 assert-count: (".sidebar .location", 2)
 // We check that there is no crate listed outside of the top level.
 assert-false: ".sidebar-elems > .crate"
+
+click: ".sidebar-links a"
+assert-property: ("html", {"scrollTop": "389"})
+
+click: ".sidebar h2.location"
+assert-property: ("html", {"scrollTop": "0"})
+
 // We now go back to the crate page to click on the "lib2" crate link.
 goto: file://|DOC_PATH|/test_docs/index.html
 click: ".sidebar-elems .crate > ul > li:first-child > a"
diff --git a/src/test/rustdoc/source-version-separator.rs b/src/test/rustdoc/source-version-separator.rs
new file mode 100644
index 00000000000..45a555eaa15
--- /dev/null
+++ b/src/test/rustdoc/source-version-separator.rs
@@ -0,0 +1,31 @@
+#![stable(feature = "bar", since = "1.0")]
+#![crate_name = "foo"]
+
+#![feature(staged_api)]
+
+// @has foo/trait.Bar.html
+// @has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0· source · '
+#[stable(feature = "bar", since = "1.0")]
+pub trait Bar {
+    // @has - '//div[@id="tymethod.foo"]/*[@class="rightside"]' '3.0 · source'
+    #[stable(feature = "foobar", since = "3.0")]
+    fn foo();
+}
+
+// @has - '//div[@id="implementors-list"]//*[@class="rightside"]' '4.0 · source'
+
+// @has foo/struct.Foo.html
+// @has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0· source · '
+#[stable(feature = "baz", since = "1.0")]
+pub struct Foo;
+
+impl Foo {
+    // @has - '//div[@id="method.foofoo"]/*[@class="rightside"]' '3.0 · source'
+    #[stable(feature = "foobar", since = "3.0")]
+    pub fn foofoo() {}
+}
+
+#[stable(feature = "yolo", since = "4.0")]
+impl Bar for Foo {
+    fn foo() {}
+}
diff --git a/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.stderr b/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.stderr
index 11c3481f15a..a6825b84589 100644
--- a/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.stderr
+++ b/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.stderr
@@ -4,7 +4,7 @@ error: comparison operators cannot be chained
 LL |     foo<BAR + 3>();
    |        ^       ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     foo::<BAR + 3>();
    |        ++
@@ -15,7 +15,7 @@ error: comparison operators cannot be chained
 LL |     foo<BAR + BAR>();
    |        ^         ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     foo::<BAR + BAR>();
    |        ++
@@ -26,7 +26,7 @@ error: comparison operators cannot be chained
 LL |     foo<3 + 3>();
    |        ^     ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     foo::<3 + 3>();
    |        ++
@@ -37,7 +37,7 @@ error: comparison operators cannot be chained
 LL |     foo<BAR - 3>();
    |        ^       ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     foo::<BAR - 3>();
    |        ++
@@ -48,7 +48,7 @@ error: comparison operators cannot be chained
 LL |     foo<BAR - BAR>();
    |        ^         ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     foo::<BAR - BAR>();
    |        ++
@@ -59,7 +59,7 @@ error: comparison operators cannot be chained
 LL |     foo<100 - BAR>();
    |        ^         ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     foo::<100 - BAR>();
    |        ++
@@ -70,7 +70,7 @@ error: comparison operators cannot be chained
 LL |     foo<bar<i32>()>();
    |        ^   ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     foo::<bar<i32>()>();
    |        ++
@@ -87,7 +87,7 @@ error: comparison operators cannot be chained
 LL |     foo<bar::<i32>()>();
    |        ^            ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     foo::<bar::<i32>()>();
    |        ++
@@ -98,7 +98,7 @@ error: comparison operators cannot be chained
 LL |     foo<bar::<i32>() + BAR>();
    |        ^                  ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     foo::<bar::<i32>() + BAR>();
    |        ++
@@ -109,7 +109,7 @@ error: comparison operators cannot be chained
 LL |     foo<bar::<i32>() - BAR>();
    |        ^                  ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     foo::<bar::<i32>() - BAR>();
    |        ++
@@ -120,7 +120,7 @@ error: comparison operators cannot be chained
 LL |     foo<BAR - bar::<i32>()>();
    |        ^                  ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     foo::<BAR - bar::<i32>()>();
    |        ++
@@ -131,7 +131,7 @@ error: comparison operators cannot be chained
 LL |     foo<BAR - bar::<i32>()>();
    |        ^                  ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     foo::<BAR - bar::<i32>()>();
    |        ++
diff --git a/src/test/ui/did_you_mean/compatible-variants.rs b/src/test/ui/did_you_mean/compatible-variants.rs
index fb6b6a5673d..a70dda8386f 100644
--- a/src/test/ui/did_you_mean/compatible-variants.rs
+++ b/src/test/ui/did_you_mean/compatible-variants.rs
@@ -3,6 +3,10 @@ enum Hey<A, B> {
     B(B),
 }
 
+struct Foo {
+    bar: Option<i32>,
+}
+
 fn f() {}
 
 fn a() -> Option<()> {
@@ -40,4 +44,8 @@ fn main() {
     let _: Hey<i32, bool> = false;
     //~^ ERROR mismatched types
     //~| HELP try wrapping
+    let bar = 1i32;
+    let _ = Foo { bar };
+    //~^ ERROR mismatched types
+    //~| HELP try wrapping
 }
diff --git a/src/test/ui/did_you_mean/compatible-variants.stderr b/src/test/ui/did_you_mean/compatible-variants.stderr
index e77949687fc..0dfd8f5c128 100644
--- a/src/test/ui/did_you_mean/compatible-variants.stderr
+++ b/src/test/ui/did_you_mean/compatible-variants.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/compatible-variants.rs:9:5
+  --> $DIR/compatible-variants.rs:13:5
    |
 LL |   fn a() -> Option<()> {
    |             ---------- expected `Option<()>` because of return type
@@ -21,7 +21,7 @@ LL +     Some(())
    |
 
 error[E0308]: mismatched types
-  --> $DIR/compatible-variants.rs:17:5
+  --> $DIR/compatible-variants.rs:21:5
    |
 LL | fn b() -> Result<(), ()> {
    |           -------------- expected `Result<(), ()>` because of return type
@@ -37,7 +37,7 @@ LL +     Ok(())
    |
 
 error[E0308]: mismatched types
-  --> $DIR/compatible-variants.rs:23:25
+  --> $DIR/compatible-variants.rs:27:25
    |
 LL |     let _: Option<()> = while false {};
    |            ----------   ^^^^^^^^^^^^^^ expected enum `Option`, found `()`
@@ -52,7 +52,7 @@ LL |     let _: Option<()> = Some(while false {});
    |                         +++++              +
 
 error[E0308]: mismatched types
-  --> $DIR/compatible-variants.rs:27:9
+  --> $DIR/compatible-variants.rs:31:9
    |
 LL |         while false {}
    |         ^^^^^^^^^^^^^^ expected enum `Option`, found `()`
@@ -69,7 +69,7 @@ LL +         Some(())
    |
 
 error[E0308]: mismatched types
-  --> $DIR/compatible-variants.rs:31:31
+  --> $DIR/compatible-variants.rs:35:31
    |
 LL |     let _: Result<i32, i32> = 1;
    |            ----------------   ^ expected enum `Result`, found integer
@@ -86,7 +86,7 @@ LL |     let _: Result<i32, i32> = Err(1);
    |                               ++++ +
 
 error[E0308]: mismatched types
-  --> $DIR/compatible-variants.rs:34:26
+  --> $DIR/compatible-variants.rs:38:26
    |
 LL |     let _: Option<i32> = 1;
    |            -----------   ^ expected enum `Option`, found integer
@@ -101,7 +101,7 @@ LL |     let _: Option<i32> = Some(1);
    |                          +++++ +
 
 error[E0308]: mismatched types
-  --> $DIR/compatible-variants.rs:37:28
+  --> $DIR/compatible-variants.rs:41:28
    |
 LL |     let _: Hey<i32, i32> = 1;
    |            -------------   ^ expected enum `Hey`, found integer
@@ -118,7 +118,7 @@ LL |     let _: Hey<i32, i32> = Hey::B(1);
    |                            +++++++ +
 
 error[E0308]: mismatched types
-  --> $DIR/compatible-variants.rs:40:29
+  --> $DIR/compatible-variants.rs:44:29
    |
 LL |     let _: Hey<i32, bool> = false;
    |            --------------   ^^^^^ expected enum `Hey`, found `bool`
@@ -132,6 +132,19 @@ help: try wrapping the expression in `Hey::B`
 LL |     let _: Hey<i32, bool> = Hey::B(false);
    |                             +++++++     +
 
-error: aborting due to 8 previous errors
+error[E0308]: mismatched types
+  --> $DIR/compatible-variants.rs:48:19
+   |
+LL |     let _ = Foo { bar };
+   |                   ^^^ expected enum `Option`, found `i32`
+   |
+   = note: expected enum `Option<i32>`
+              found type `i32`
+help: try wrapping the expression in `Some`
+   |
+LL |     let _ = Foo { bar: Some(bar) };
+   |                   ++++++++++   +
+
+error: aborting due to 9 previous errors
 
 For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/did_you_mean/issue-40396.stderr b/src/test/ui/did_you_mean/issue-40396.stderr
index d2938435ece..d0249efd094 100644
--- a/src/test/ui/did_you_mean/issue-40396.stderr
+++ b/src/test/ui/did_you_mean/issue-40396.stderr
@@ -4,7 +4,7 @@ error: comparison operators cannot be chained
 LL |     (0..13).collect<Vec<i32>>();
    |                    ^   ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     (0..13).collect::<Vec<i32>>();
    |                    ++
@@ -15,7 +15,7 @@ error: comparison operators cannot be chained
 LL |     Vec<i32>::new();
    |        ^   ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     Vec::<i32>::new();
    |        ++
@@ -26,7 +26,7 @@ error: comparison operators cannot be chained
 LL |     (0..13).collect<Vec<i32>();
    |                    ^   ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     (0..13).collect::<Vec<i32>();
    |                    ++
@@ -37,7 +37,7 @@ error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, fo
 LL |     let x = std::collections::HashMap<i128, i128>::new();
    |                                           ^ expected one of 8 possible tokens
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     let x = std::collections::HashMap::<i128, i128>::new();
    |                                      ++
@@ -48,7 +48,7 @@ error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found
 LL |         std::collections::HashMap<i128, i128>::new()
    |                                       ^ expected one of 8 possible tokens
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |         std::collections::HashMap::<i128, i128>::new()
    |                                  ++
@@ -59,7 +59,7 @@ error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found
 LL |         std::collections::HashMap<i128, i128>::new();
    |                                       ^ expected one of 8 possible tokens
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |         std::collections::HashMap::<i128, i128>::new();
    |                                  ++
@@ -70,7 +70,7 @@ error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found
 LL |         std::collections::HashMap<i128, i128>::new(1, 2);
    |                                       ^ expected one of 8 possible tokens
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |         std::collections::HashMap::<i128, i128>::new(1, 2);
    |                                  ++
diff --git a/src/test/ui/inference/deref-suggestion.stderr b/src/test/ui/inference/deref-suggestion.stderr
index da11ba204cb..28c9afaa52c 100644
--- a/src/test/ui/inference/deref-suggestion.stderr
+++ b/src/test/ui/inference/deref-suggestion.stderr
@@ -87,7 +87,7 @@ LL |     let r = R { i };
 help: consider dereferencing the borrow
    |
 LL |     let r = R { i: *i };
-   |                 ~~~~~
+   |                 ++++
 
 error[E0308]: mismatched types
   --> $DIR/deref-suggestion.rs:46:20
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 e3ce6cd39bc..68636f6b907 100644
--- a/src/test/ui/parser/require-parens-for-chained-comparison.rs
+++ b/src/test/ui/parser/require-parens-for-chained-comparison.rs
@@ -1,6 +1,3 @@
-fn f<T>() {}
-struct X;
-
 fn main() {
     false == false == false;
     //~^ ERROR comparison operators cannot be chained
@@ -12,15 +9,26 @@ fn main() {
 
     f<X>();
     //~^ ERROR comparison operators cannot be chained
-    //~| HELP use `::<...>` instead of `<...>` to specify type or const arguments
+    //~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
 
     f<Result<Option<X>, Option<Option<X>>>(1, 2);
     //~^ ERROR comparison operators cannot be chained
-    //~| HELP use `::<...>` instead of `<...>` to specify type or const arguments
+    //~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
+
+    let _ = f<u8, i8>();
+    //~^ ERROR expected one of
+    //~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
+
+    let _ = f<'_, i8>();
+    //~^ ERROR expected one of
+    //~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
+
+    f<'_>();
+    //~^ comparison operators cannot be chained
+    //~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
 
-    use std::convert::identity;
-    let _ = identity<u8>;
+    let _ = f<u8>;
     //~^ ERROR comparison operators cannot be chained
-    //~| HELP use `::<...>` instead of `<...>` to specify type or const arguments
+    //~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
     //~| HELP or use `(...)` if you meant to specify fn arguments
 }
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 74429cb438c..cde6f8c674f 100644
--- a/src/test/ui/parser/require-parens-for-chained-comparison.stderr
+++ b/src/test/ui/parser/require-parens-for-chained-comparison.stderr
@@ -1,5 +1,5 @@
 error: comparison operators cannot be chained
-  --> $DIR/require-parens-for-chained-comparison.rs:5:11
+  --> $DIR/require-parens-for-chained-comparison.rs:2:11
    |
 LL |     false == false == false;
    |           ^^       ^^
@@ -10,7 +10,7 @@ LL |     false == false && false == false;
    |                    ++++++++
 
 error: comparison operators cannot be chained
-  --> $DIR/require-parens-for-chained-comparison.rs:9:11
+  --> $DIR/require-parens-for-chained-comparison.rs:6:11
    |
 LL |     false == 0 < 2;
    |           ^^   ^
@@ -21,35 +21,68 @@ LL |     false == (0 < 2);
    |              +     +
 
 error: comparison operators cannot be chained
-  --> $DIR/require-parens-for-chained-comparison.rs:13:6
+  --> $DIR/require-parens-for-chained-comparison.rs:10:6
    |
 LL |     f<X>();
    |      ^ ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     f::<X>();
    |      ++
 
 error: comparison operators cannot be chained
-  --> $DIR/require-parens-for-chained-comparison.rs:17:6
+  --> $DIR/require-parens-for-chained-comparison.rs:14:6
    |
 LL |     f<Result<Option<X>, Option<Option<X>>>(1, 2);
    |      ^      ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     f::<Result<Option<X>, Option<Option<X>>>(1, 2);
    |      ++
 
+error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,`
+  --> $DIR/require-parens-for-chained-comparison.rs:18:17
+   |
+LL |     let _ = f<u8, i8>();
+   |                 ^ expected one of 8 possible tokens
+   |
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
+   |
+LL |     let _ = f::<u8, i8>();
+   |              ++
+
+error: expected one of `.`, `:`, `;`, `?`, `else`, `for`, `loop`, `while`, `{`, or an operator, found `,`
+  --> $DIR/require-parens-for-chained-comparison.rs:22:17
+   |
+LL |     let _ = f<'_, i8>();
+   |                 ^ expected one of 10 possible tokens
+   |
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
+   |
+LL |     let _ = f::<'_, i8>();
+   |              ++
+
+error: comparison operators cannot be chained
+  --> $DIR/require-parens-for-chained-comparison.rs:26:6
+   |
+LL |     f<'_>();
+   |      ^  ^
+   |
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
+   |
+LL |     f::<'_>();
+   |      ++
+
 error: comparison operators cannot be chained
-  --> $DIR/require-parens-for-chained-comparison.rs:22:21
+  --> $DIR/require-parens-for-chained-comparison.rs:30:14
    |
-LL |     let _ = identity<u8>;
-   |                     ^  ^
+LL |     let _ = f<u8>;
+   |              ^  ^
    |
-   = help: use `::<...>` instead of `<...>` to specify type or const arguments
+   = help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    = help: or use `(...)` if you meant to specify fn arguments
 
-error: aborting due to 5 previous errors
+error: aborting due to 8 previous errors
 
diff --git a/src/test/ui/suggestions/issue-82566-1.stderr b/src/test/ui/suggestions/issue-82566-1.stderr
index a05c59c786b..72f0f45fb83 100644
--- a/src/test/ui/suggestions/issue-82566-1.stderr
+++ b/src/test/ui/suggestions/issue-82566-1.stderr
@@ -4,7 +4,7 @@ error: comparison operators cannot be chained
 LL |     T1<1>::C;
    |       ^ ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     T1::<1>::C;
    |       ++
@@ -15,7 +15,7 @@ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `,`
 LL |     T2<1, 2>::C;
    |         ^ expected one of `.`, `;`, `?`, `}`, or an operator
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     T2::<1, 2>::C;
    |       ++
@@ -26,7 +26,7 @@ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `,`
 LL |     T3<1, 2, 3>::C;
    |         ^ expected one of `.`, `;`, `?`, `}`, or an operator
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     T3::<1, 2, 3>::C;
    |       ++
diff --git a/src/test/ui/suggestions/issue-82566-2.stderr b/src/test/ui/suggestions/issue-82566-2.stderr
index 8f30a301bb8..ef9a414307c 100644
--- a/src/test/ui/suggestions/issue-82566-2.stderr
+++ b/src/test/ui/suggestions/issue-82566-2.stderr
@@ -4,7 +4,7 @@ error: comparison operators cannot be chained
 LL | fn foo1() -> [(); Foo1<10>::SUM] {
    |                       ^  ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL | fn foo1() -> [(); Foo1::<10>::SUM] {
    |                       ++
@@ -15,7 +15,7 @@ error: expected one of `.`, `?`, `]`, or an operator, found `,`
 LL | fn foo2() -> [(); Foo2<10, 20>::SUM] {
    |                          ^ expected one of `.`, `?`, `]`, or an operator
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL | fn foo2() -> [(); Foo2::<10, 20>::SUM] {
    |                       ++
@@ -26,7 +26,7 @@ error: expected one of `.`, `?`, `]`, or an operator, found `,`
 LL | fn foo3() -> [(); Foo3<10, 20, 30>::SUM] {
    |                          ^ expected one of `.`, `?`, `]`, or an operator
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL | fn foo3() -> [(); Foo3::<10, 20, 30>::SUM] {
    |                       ++