diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 1b9ccbd850bee..31360158e2b4e 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -10,9 +10,9 @@ use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_hir::def::Res;
 use rustc_session::parse::feature_err;
-use rustc_span::hygiene::ForLoopLoc;
 use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
 use rustc_span::symbol::{sym, Ident, Symbol};
+use rustc_span::{hygiene::ForLoopLoc, DUMMY_SP};
 use rustc_target::asm;
 use std::collections::hash_map::Entry;
 use std::fmt::Write;
@@ -102,6 +102,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         this.lower_block(body, false),
                         opt_label,
                         hir::LoopSource::Loop,
+                        DUMMY_SP,
                     )
                 }),
                 ExprKind::TryBlock(ref body) => self.lower_expr_try_block(body),
@@ -453,7 +454,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
             self.expr_match(span, scrutinee, arena_vec![self; then_arm, else_arm], desugar);
 
         // `[opt_ident]: loop { ... }`
-        hir::ExprKind::Loop(self.block_expr(self.arena.alloc(match_expr)), opt_label, source)
+        hir::ExprKind::Loop(
+            self.block_expr(self.arena.alloc(match_expr)),
+            opt_label,
+            source,
+            span.with_hi(cond.span.hi()),
+        )
     }
 
     /// Desugar `try { <stmts>; <expr> }` into `{ <stmts>; ::std::ops::Try::from_ok(<expr>) }`,
@@ -748,7 +754,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         // loop { .. }
         let loop_expr = self.arena.alloc(hir::Expr {
             hir_id: loop_hir_id,
-            kind: hir::ExprKind::Loop(loop_block, None, hir::LoopSource::Loop),
+            kind: hir::ExprKind::Loop(loop_block, None, hir::LoopSource::Loop, span),
             span,
             attrs: ThinVec::new(),
         });
@@ -1709,7 +1715,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
         );
 
         // `[opt_ident]: loop { ... }`
-        let kind = hir::ExprKind::Loop(loop_block, opt_label, hir::LoopSource::ForLoop);
+        let kind = hir::ExprKind::Loop(
+            loop_block,
+            opt_label,
+            hir::LoopSource::ForLoop,
+            e.span.with_hi(orig_head_span.hi()),
+        );
         let loop_expr = self.arena.alloc(hir::Expr {
             hir_id: self.lower_node_id(e.id),
             kind,
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 3673e5c8bf3a5..35170fa7c1d02 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1617,7 +1617,9 @@ pub enum ExprKind<'hir> {
     /// A conditionless loop (can be exited with `break`, `continue`, or `return`).
     ///
     /// I.e., `'label: loop { <block> }`.
-    Loop(&'hir Block<'hir>, Option<Label>, LoopSource),
+    ///
+    /// The `Span` is the loop header (`for x in y`/`while let pat = expr`).
+    Loop(&'hir Block<'hir>, Option<Label>, LoopSource, Span),
     /// A `match` block, with a source that indicates whether or not it is
     /// the result of a desugaring, and if so, which kind.
     Match(&'hir Expr<'hir>, &'hir [Arm<'hir>], MatchSource),
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index 8707a84a98fcc..6c1bee2335a00 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -1151,7 +1151,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
             visitor.visit_expr(then);
             walk_list!(visitor, visit_expr, else_opt);
         }
-        ExprKind::Loop(ref block, ref opt_label, _) => {
+        ExprKind::Loop(ref block, ref opt_label, _, _) => {
             walk_list!(visitor, visit_label, opt_label);
             visitor.visit_block(block);
         }
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index a3ab1d96e1dfe..f1c2a6b7e6e85 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -1396,7 +1396,7 @@ impl<'a> State<'a> {
             hir::ExprKind::If(ref test, ref blk, ref elseopt) => {
                 self.print_if(&test, &blk, elseopt.as_ref().map(|e| &**e));
             }
-            hir::ExprKind::Loop(ref blk, opt_label, _) => {
+            hir::ExprKind::Loop(ref blk, opt_label, _, _) => {
                 if let Some(label) = opt_label {
                     self.print_ident(label.ident);
                     self.word_space(":");
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 8cdb33ea3175f..b37660e4a90d3 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -96,18 +96,24 @@ fn pierce_parens(mut expr: &ast::Expr) -> &ast::Expr {
 
 impl EarlyLintPass for WhileTrue {
     fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
-        if let ast::ExprKind::While(cond, ..) = &e.kind {
+        if let ast::ExprKind::While(cond, _, label) = &e.kind {
             if let ast::ExprKind::Lit(ref lit) = pierce_parens(cond).kind {
                 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().guess_head_span(e.span);
+                        let condition_span = e.span.with_hi(cond.span.hi());
                         cx.struct_span_lint(WHILE_TRUE, condition_span, |lint| {
                             lint.build(msg)
                                 .span_suggestion_short(
                                     condition_span,
                                     "use `loop`",
-                                    "loop".to_owned(),
+                                    format!(
+                                        "{}loop",
+                                        label.map_or_else(String::new, |label| format!(
+                                            "{}: ",
+                                            label.ident,
+                                        ))
+                                    ),
                                     Applicability::MachineApplicable,
                                 )
                                 .emit();
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index 595458702e9c9..2962cbe8157f6 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -546,9 +546,7 @@ fn make_mirror_unadjusted<'a, 'tcx>(
             scrutinee: discr.to_ref(),
             arms: arms.iter().map(|a| convert_arm(cx, a)).collect(),
         },
-        hir::ExprKind::Loop(ref body, _, _) => {
-            ExprKind::Loop { body: block::to_expr_ref(cx, body) }
-        }
+        hir::ExprKind::Loop(ref body, ..) => ExprKind::Loop { body: block::to_expr_ref(cx, body) },
         hir::ExprKind::Field(ref source, ..) => ExprKind::Field {
             lhs: source.to_ref(),
             name: Field::new(cx.tcx.field_index(expr.hir_id, cx.typeck_results)),
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 47869f775fe6e..cfd7ad48222a2 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -585,7 +585,7 @@ impl<'a> Parser<'a> {
         lhs_span: Span,
         expr_kind: fn(P<Expr>, P<Ty>) -> ExprKind,
     ) -> PResult<'a, P<Expr>> {
-        let mk_expr = |this: &mut Self, rhs: P<Ty>| {
+        let mk_expr = |this: &mut Self, lhs: P<Expr>, rhs: P<Ty>| {
             this.mk_expr(
                 this.mk_expr_sp(&lhs, lhs_span, rhs.span),
                 expr_kind(lhs, rhs),
@@ -597,13 +597,49 @@ impl<'a> Parser<'a> {
         // LessThan comparison after this cast.
         let parser_snapshot_before_type = self.clone();
         let cast_expr = match self.parse_ty_no_plus() {
-            Ok(rhs) => mk_expr(self, rhs),
+            Ok(rhs) => mk_expr(self, lhs, rhs),
             Err(mut type_err) => {
                 // Rewind to before attempting to parse the type with generics, to recover
                 // from situations like `x as usize < y` in which we first tried to parse
                 // `usize < y` as a type with generic arguments.
                 let parser_snapshot_after_type = mem::replace(self, parser_snapshot_before_type);
 
+                // Check for typo of `'a: loop { break 'a }` with a missing `'`.
+                match (&lhs.kind, &self.token.kind) {
+                    (
+                        // `foo: `
+                        ExprKind::Path(None, ast::Path { segments, .. }),
+                        TokenKind::Ident(kw::For | kw::Loop | kw::While, false),
+                    ) if segments.len() == 1 => {
+                        let snapshot = self.clone();
+                        let label = Label {
+                            ident: Ident::from_str_and_span(
+                                &format!("'{}", segments[0].ident),
+                                segments[0].ident.span,
+                            ),
+                        };
+                        match self.parse_labeled_expr(label, AttrVec::new(), false) {
+                            Ok(expr) => {
+                                type_err.cancel();
+                                self.struct_span_err(label.ident.span, "malformed loop label")
+                                    .span_suggestion(
+                                        label.ident.span,
+                                        "use the correct loop label format",
+                                        label.ident.to_string(),
+                                        Applicability::MachineApplicable,
+                                    )
+                                    .emit();
+                                return Ok(expr);
+                            }
+                            Err(mut err) => {
+                                err.cancel();
+                                *self = snapshot;
+                            }
+                        }
+                    }
+                    _ => {}
+                }
+
                 match self.parse_path(PathStyle::Expr) {
                     Ok(path) => {
                         let (op_noun, op_verb) = match self.token.kind {
@@ -630,7 +666,8 @@ impl<'a> Parser<'a> {
                             op_noun,
                         );
                         let span_after_type = parser_snapshot_after_type.token.span;
-                        let expr = mk_expr(self, self.mk_ty(path.span, TyKind::Path(None, path)));
+                        let expr =
+                            mk_expr(self, lhs, self.mk_ty(path.span, TyKind::Path(None, path)));
 
                         let expr_str = self
                             .span_to_snippet(expr.span)
@@ -1067,7 +1104,7 @@ impl<'a> Parser<'a> {
         } else if self.eat_keyword(kw::While) {
             self.parse_while_expr(None, self.prev_token.span, attrs)
         } else if let Some(label) = self.eat_label() {
-            self.parse_labeled_expr(label, attrs)
+            self.parse_labeled_expr(label, attrs, true)
         } else if self.eat_keyword(kw::Loop) {
             self.parse_loop_expr(None, self.prev_token.span, attrs)
         } else if self.eat_keyword(kw::Continue) {
@@ -1228,7 +1265,12 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse `'label: $expr`. The label is already parsed.
-    fn parse_labeled_expr(&mut self, label: Label, attrs: AttrVec) -> PResult<'a, P<Expr>> {
+    fn parse_labeled_expr(
+        &mut self,
+        label: Label,
+        attrs: AttrVec,
+        consume_colon: bool,
+    ) -> PResult<'a, P<Expr>> {
         let lo = label.ident.span;
         let label = Some(label);
         let ate_colon = self.eat(&token::Colon);
@@ -1247,7 +1289,7 @@ impl<'a> Parser<'a> {
             self.parse_expr()
         }?;
 
-        if !ate_colon {
+        if !ate_colon && consume_colon {
             self.error_labeled_expr_must_be_followed_by_colon(lo, expr.span);
         }
 
diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs
index c887a860303b5..8950f9b33b6da 100644
--- a/compiler/rustc_passes/src/check_const.rs
+++ b/compiler/rustc_passes/src/check_const.rs
@@ -199,7 +199,7 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> {
             // Skip the following checks if we are not currently in a const context.
             _ if self.const_kind.is_none() => {}
 
-            hir::ExprKind::Loop(_, _, source) => {
+            hir::ExprKind::Loop(_, _, source, _) => {
                 self.const_check_violated(NonConstExpr::Loop(*source), e.span);
             }
 
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index 6202cc312fc0e..c11dc231d482c 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -844,7 +844,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
 
             // Note that labels have been resolved, so we don't need to look
             // at the label ident
-            hir::ExprKind::Loop(ref blk, _, _) => self.propagate_through_loop(expr, &blk, succ),
+            hir::ExprKind::Loop(ref blk, ..) => self.propagate_through_loop(expr, &blk, succ),
 
             hir::ExprKind::If(ref cond, ref then, ref else_opt) => {
                 //
diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs
index 9b4da71e5e961..4bfac1b72983e 100644
--- a/compiler/rustc_passes/src/loops.rs
+++ b/compiler/rustc_passes/src/loops.rs
@@ -53,7 +53,7 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
 
     fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
         match e.kind {
-            hir::ExprKind::Loop(ref b, _, source) => {
+            hir::ExprKind::Loop(ref b, _, source, _) => {
                 self.with_context(Loop(source), |v| v.visit_block(&b));
             }
             hir::ExprKind::Closure(_, ref function_decl, b, span, movability) => {
@@ -68,18 +68,18 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
             hir::ExprKind::Block(ref b, Some(_label)) => {
                 self.with_context(LabeledBlock, |v| v.visit_block(&b));
             }
-            hir::ExprKind::Break(label, ref opt_expr) => {
+            hir::ExprKind::Break(break_label, ref opt_expr) => {
                 if let Some(e) = opt_expr {
                     self.visit_expr(e);
                 }
 
-                if self.require_label_in_labeled_block(e.span, &label, "break") {
+                if self.require_label_in_labeled_block(e.span, &break_label, "break") {
                     // If we emitted an error about an unlabeled break in a labeled
                     // block, we don't need any further checking for this break any more
                     return;
                 }
 
-                let loop_id = match label.target_id {
+                let loop_id = match break_label.target_id {
                     Ok(loop_id) => Some(loop_id),
                     Err(hir::LoopIdError::OutsideLoopScope) => None,
                     Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => {
@@ -89,49 +89,89 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
                     Err(hir::LoopIdError::UnresolvedLabel) => None,
                 };
 
-                if let Some(loop_id) = loop_id {
-                    if let Node::Block(_) = self.hir_map.find(loop_id).unwrap() {
-                        return;
-                    }
+                if let Some(Node::Block(_)) = loop_id.and_then(|id| self.hir_map.find(id)) {
+                    return;
                 }
 
-                if opt_expr.is_some() {
-                    let loop_kind = if let Some(loop_id) = loop_id {
-                        Some(match self.hir_map.expect_expr(loop_id).kind {
-                            hir::ExprKind::Loop(_, _, source) => source,
+                if let Some(break_expr) = opt_expr {
+                    let (head, loop_label, loop_kind) = if let Some(loop_id) = loop_id {
+                        match self.hir_map.expect_expr(loop_id).kind {
+                            hir::ExprKind::Loop(_, label, source, sp) => {
+                                (Some(sp), label, Some(source))
+                            }
                             ref r => {
                                 span_bug!(e.span, "break label resolved to a non-loop: {:?}", r)
                             }
-                        })
+                        }
                     } else {
-                        None
+                        (None, None, None)
                     };
                     match loop_kind {
                         None | Some(hir::LoopSource::Loop) => (),
                         Some(kind) => {
-                            struct_span_err!(
+                            let mut err = struct_span_err!(
                                 self.sess,
                                 e.span,
                                 E0571,
                                 "`break` with value from a `{}` loop",
                                 kind.name()
-                            )
-                            .span_label(
+                            );
+                            err.span_label(
                                 e.span,
-                                "can only break with a value inside \
-                                            `loop` or breakable block",
-                            )
-                            .span_suggestion(
+                                "can only break with a value inside `loop` or breakable block",
+                            );
+                            if let Some(head) = head {
+                                err.span_label(
+                                    head,
+                                    &format!(
+                                        "you can't `break` with a value in a `{}` loop",
+                                        kind.name()
+                                    ),
+                                );
+                            }
+                            err.span_suggestion(
                                 e.span,
                                 &format!(
-                                    "instead, use `break` on its own \
-                                        without a value inside this `{}` loop",
-                                    kind.name()
+                                    "use `break` on its own without a value inside this `{}` loop",
+                                    kind.name(),
+                                ),
+                                format!(
+                                    "break{}",
+                                    break_label
+                                        .label
+                                        .map_or_else(String::new, |l| format!(" {}", l.ident))
                                 ),
-                                "break".to_string(),
                                 Applicability::MaybeIncorrect,
-                            )
-                            .emit();
+                            );
+                            if let (Some(label), None) = (loop_label, break_label.label) {
+                                match break_expr.kind {
+                                    hir::ExprKind::Path(hir::QPath::Resolved(
+                                        None,
+                                        hir::Path {
+                                            segments: [segment],
+                                            res: hir::def::Res::Err,
+                                            ..
+                                        },
+                                    )) if label.ident.to_string()
+                                        == format!("'{}", segment.ident) =>
+                                    {
+                                        // This error is redundant, we will have already emitted a
+                                        // suggestion to use the label when `segment` wasn't found
+                                        // (hence the `Res::Err` check).
+                                        err.delay_as_bug();
+                                    }
+                                    _ => {
+                                        err.span_suggestion(
+                                            break_expr.span,
+                                            "alternatively, you might have meant to use the \
+                                             available loop label",
+                                            label.ident.to_string(),
+                                            Applicability::MaybeIncorrect,
+                                        );
+                                    }
+                                }
+                            }
+                            err.emit();
                         }
                     }
                 }
diff --git a/compiler/rustc_passes/src/region.rs b/compiler/rustc_passes/src/region.rs
index 91421d7f5f22b..64356f73f6c65 100644
--- a/compiler/rustc_passes/src/region.rs
+++ b/compiler/rustc_passes/src/region.rs
@@ -252,7 +252,7 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
                 terminating(then.hir_id.local_id);
             }
 
-            hir::ExprKind::Loop(ref body, _, _) => {
+            hir::ExprKind::Loop(ref body, _, _, _) => {
                 terminating(body.hir_id.local_id);
             }
 
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index fff14747e5729..eaeb28388d4f0 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -2266,6 +2266,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                 visit::walk_expr(self, expr);
             }
 
+            ExprKind::Break(None, Some(ref e)) => {
+                // We use this instead of `visit::walk_expr` to keep the parent expr around for
+                // better diagnostics.
+                self.resolve_expr(e, Some(&expr));
+            }
+
             ExprKind::Let(ref pat, ref scrutinee) => {
                 self.visit_expr(scrutinee);
                 self.resolve_pattern_top(pat, PatternSource::Let);
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 3945afb4724a8..bed7a350ea86d 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -545,17 +545,23 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
         if let Some(err_code) = &err.code {
             if err_code == &rustc_errors::error_code!(E0425) {
                 for label_rib in &self.label_ribs {
-                    for (label_ident, _) in &label_rib.bindings {
+                    for (label_ident, node_id) in &label_rib.bindings {
                         if format!("'{}", ident) == label_ident.to_string() {
-                            let msg = "a label with a similar name exists";
-                            // FIXME: consider only emitting this suggestion if a label would be valid here
-                            // which is pretty much only the case for `break` expressions.
-                            err.span_suggestion(
-                                span,
-                                &msg,
-                                label_ident.name.to_string(),
-                                Applicability::MaybeIncorrect,
-                            );
+                            err.span_label(label_ident.span, "a label with a similar name exists");
+                            if let PathSource::Expr(Some(Expr {
+                                kind: ExprKind::Break(None, Some(_)),
+                                ..
+                            })) = source
+                            {
+                                err.span_suggestion(
+                                    span,
+                                    "use the similarly named label",
+                                    label_ident.name.to_string(),
+                                    Applicability::MaybeIncorrect,
+                                );
+                                // Do not lint against unused label when we suggest them.
+                                self.diagnostic_metadata.unused_labels.remove(node_id);
+                            }
                         }
                     }
                 }
diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs
index 64cc113dffe7e..95ac2a31dd321 100644
--- a/compiler/rustc_resolve/src/late/lifetimes.rs
+++ b/compiler/rustc_resolve/src/late/lifetimes.rs
@@ -1173,7 +1173,7 @@ fn extract_labels(ctxt: &mut LifetimeContext<'_, '_>, body: &hir::Body<'_>) {
     }
 
     fn expression_label(ex: &hir::Expr<'_>) -> Option<Ident> {
-        if let hir::ExprKind::Loop(_, Some(label), _) = ex.kind { Some(label.ident) } else { None }
+        if let hir::ExprKind::Loop(_, Some(label), ..) = ex.kind { Some(label.ident) } else { None }
     }
 
     fn check_if_label_shadows_lifetime(tcx: TyCtxt<'_>, mut scope: ScopeRef<'_>, label: Ident) {
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index 8aa6c6d924a53..8f463af73c477 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -266,7 +266,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
             }
             ExprKind::Ret(ref expr_opt) => self.check_expr_return(expr_opt.as_deref(), expr),
-            ExprKind::Loop(ref body, _, source) => {
+            ExprKind::Loop(ref body, _, source, _) => {
                 self.check_expr_loop(body, source, expected, expr)
             }
             ExprKind::Match(ref discrim, ref arms, match_src) => {
diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs
index 01519e4c8f7c4..24364c6954e49 100644
--- a/compiler/rustc_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_typeck/src/expr_use_visitor.rs
@@ -289,7 +289,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
             | hir::ExprKind::ConstBlock(..)
             | hir::ExprKind::Err => {}
 
-            hir::ExprKind::Loop(ref blk, _, _) => {
+            hir::ExprKind::Loop(ref blk, ..) => {
                 self.walk_block(blk);
             }
 
diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs
index 8d721ed7487ae..d7ae353282e79 100644
--- a/library/alloc/src/lib.rs
+++ b/library/alloc/src/lib.rs
@@ -189,11 +189,4 @@ pub mod vec;
 #[unstable(feature = "liballoc_internals", issue = "none", reason = "implementation detail")]
 pub mod __export {
     pub use core::format_args;
-
-    /// Force AST node to an expression to improve diagnostics in pattern position.
-    #[rustc_macro_transparency = "semitransparent"]
-    #[unstable(feature = "liballoc_internals", issue = "none", reason = "implementation detail")]
-    pub macro force_expr($e:expr) {
-        $e
-    }
 }
diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs
index 3a46763c3f608..f1e3ee97ccc39 100644
--- a/library/alloc/src/macros.rs
+++ b/library/alloc/src/macros.rs
@@ -40,13 +40,13 @@
 #[allow_internal_unstable(box_syntax, liballoc_internals)]
 macro_rules! vec {
     () => (
-        $crate::__export::force_expr!($crate::vec::Vec::new())
+        $crate::__rust_force_expr!($crate::vec::Vec::new())
     );
     ($elem:expr; $n:expr) => (
-        $crate::__export::force_expr!($crate::vec::from_elem($elem, $n))
+        $crate::__rust_force_expr!($crate::vec::from_elem($elem, $n))
     );
     ($($x:expr),+ $(,)?) => (
-        $crate::__export::force_expr!(<[_]>::into_vec(box [$($x),+]))
+        $crate::__rust_force_expr!(<[_]>::into_vec(box [$($x),+]))
     );
 }
 
@@ -111,3 +111,13 @@ macro_rules! format {
         res
     }}
 }
+
+/// Force AST node to an expression to improve diagnostics in pattern position.
+#[doc(hidden)]
+#[macro_export]
+#[unstable(feature = "liballoc_internals", issue = "none", reason = "implementation detail")]
+macro_rules! __rust_force_expr {
+    ($e:expr) => {
+        $e
+    };
+}
diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs
index 36b7efc33a874..36e2d18d3ddfb 100644
--- a/library/alloc/src/raw_vec.rs
+++ b/library/alloc/src/raw_vec.rs
@@ -114,6 +114,19 @@ impl<T> RawVec<T, Global> {
 }
 
 impl<T, A: Allocator> RawVec<T, A> {
+    // Tiny Vecs are dumb. Skip to:
+    // - 8 if the element size is 1, because any heap allocators is likely
+    //   to round up a request of less than 8 bytes to at least 8 bytes.
+    // - 4 if elements are moderate-sized (<= 1 KiB).
+    // - 1 otherwise, to avoid wasting too much space for very short Vecs.
+    const MIN_NON_ZERO_CAP: usize = if mem::size_of::<T>() == 1 {
+        8
+    } else if mem::size_of::<T>() <= 1024 {
+        4
+    } else {
+        1
+    };
+
     /// Like `new`, but parameterized over the choice of allocator for
     /// the returned `RawVec`.
     #[rustc_allow_const_fn_unstable(const_fn)]
@@ -399,22 +412,7 @@ impl<T, A: Allocator> RawVec<T, A> {
         // This guarantees exponential growth. The doubling cannot overflow
         // because `cap <= isize::MAX` and the type of `cap` is `usize`.
         let cap = cmp::max(self.cap * 2, required_cap);
-
-        // Tiny Vecs are dumb. Skip to:
-        // - 8 if the element size is 1, because any heap allocators is likely
-        //   to round up a request of less than 8 bytes to at least 8 bytes.
-        // - 4 if elements are moderate-sized (<= 1 KiB).
-        // - 1 otherwise, to avoid wasting too much space for very short Vecs.
-        // Note that `min_non_zero_cap` is computed statically.
-        let elem_size = mem::size_of::<T>();
-        let min_non_zero_cap = if elem_size == 1 {
-            8
-        } else if elem_size <= 1024 {
-            4
-        } else {
-            1
-        };
-        let cap = cmp::max(min_non_zero_cap, cap);
+        let cap = cmp::max(Self::MIN_NON_ZERO_CAP, cap);
 
         let new_layout = Layout::array::<T>(cap);
 
diff --git a/library/core/src/iter/adapters/intersperse.rs b/library/core/src/iter/adapters/intersperse.rs
index b1170f32ba161..d8bbd424cf258 100644
--- a/library/core/src/iter/adapters/intersperse.rs
+++ b/library/core/src/iter/adapters/intersperse.rs
@@ -151,12 +151,10 @@ where
 {
     let (lo, hi) = iter.size_hint();
     let next_is_elem = !needs_sep;
-    let lo = lo.saturating_sub(next_is_elem as usize).saturating_add(lo);
-    let hi = match hi {
-        Some(hi) => hi.saturating_sub(next_is_elem as usize).checked_add(hi),
-        None => None,
-    };
-    (lo, hi)
+    (
+        lo.saturating_sub(next_is_elem as usize).saturating_add(lo),
+        hi.and_then(|hi| hi.saturating_sub(next_is_elem as usize).checked_add(hi)),
+    )
 }
 
 fn intersperse_fold<I, B, F, G>(
diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs
index a07cb90f9f2af..9f7ced829b0ac 100644
--- a/library/core/src/iter/traits/iterator.rs
+++ b/library/core/src/iter/traits/iterator.rs
@@ -567,9 +567,10 @@ pub trait Iterator {
         Zip::new(self, other.into_iter())
     }
 
-    /// Places a copy of `separator` between all elements.
+    /// Creates a new iterator which places a copy of `separator` between adjacent
+    /// items of the original iterator.
     ///
-    /// In case the separator does not implement [`Clone`] or needs to be
+    /// In case `separator` does not implement [`Clone`] or needs to be
     /// computed every time, use [`intersperse_with`].
     ///
     /// # Examples
@@ -579,6 +580,19 @@ pub trait Iterator {
     /// ```
     /// #![feature(iter_intersperse)]
     ///
+    /// let mut a = [0, 1, 2].iter().intersperse(&100);
+    /// assert_eq!(a.next(), Some(&0));   // The first element from `a`.
+    /// assert_eq!(a.next(), Some(&100)); // The separator.
+    /// assert_eq!(a.next(), Some(&1));   // The next element from `a`.
+    /// assert_eq!(a.next(), Some(&100)); // The separator.
+    /// assert_eq!(a.next(), Some(&2));   // The last element from `a`.
+    /// assert_eq!(a.next(), None);       // The iterator is finished.
+    /// ```
+    ///
+    /// `intersperse` can be very useful to join an iterator's items using a common element:
+    /// ```
+    /// #![feature(iter_intersperse)]
+    ///
     /// let hello = ["Hello", "World", "!"].iter().copied().intersperse(" ").collect::<String>();
     /// assert_eq!(hello, "Hello World !");
     /// ```
@@ -595,7 +609,16 @@ pub trait Iterator {
         Intersperse::new(self, separator)
     }
 
-    /// Places an element generated by `separator` between all elements.
+    /// Creates a new iterator which places an item generated by `separator`
+    /// between adjacent items of the original iterator.
+    ///
+    /// The closure will be called exactly once each time an item is placed
+    /// between two adjacent items from the underlying iterator; specifically,
+    /// the closure is not called if the underlying iterator yields less than
+    /// two items and after the last item is yielded.
+    ///
+    /// If the iterator's item implements [`Clone`], it may be easier to use
+    /// [`intersperse`].
     ///
     /// # Examples
     ///
@@ -604,14 +627,36 @@ pub trait Iterator {
     /// ```
     /// #![feature(iter_intersperse)]
     ///
+    /// #[derive(PartialEq, Debug)]
+    /// struct NotClone(usize);
+    ///
+    /// let v = vec![NotClone(0), NotClone(1), NotClone(2)];
+    /// let mut it = v.into_iter().intersperse_with(|| NotClone(99));
+    ///
+    /// assert_eq!(it.next(), Some(NotClone(0)));  // The first element from `v`.
+    /// assert_eq!(it.next(), Some(NotClone(99))); // The separator.
+    /// assert_eq!(it.next(), Some(NotClone(1)));  // The next element from `v`.
+    /// assert_eq!(it.next(), Some(NotClone(99))); // The separator.
+    /// assert_eq!(it.next(), Some(NotClone(2)));  // The last element from from `v`.
+    /// assert_eq!(it.next(), None);               // The iterator is finished.
+    /// ```
+    ///
+    /// `intersperse_with` can be used in situations where the separator needs
+    /// to be computed:
+    /// ```
+    /// #![feature(iter_intersperse)]
+    ///
     /// let src = ["Hello", "to", "all", "people", "!!"].iter().copied();
     ///
+    /// // The closure mutably borrows its context to generate an item.
     /// let mut happy_emojis = [" ❤️ ", " 😀 "].iter().copied();
     /// let separator = || happy_emojis.next().unwrap_or(" 🦀 ");
     ///
     /// let result = src.intersperse_with(separator).collect::<String>();
     /// assert_eq!(result, "Hello ❤️ to 😀 all 🦀 people 🦀 !!");
     /// ```
+    /// [`Clone`]: crate::clone::Clone
+    /// [`intersperse`]: Iterator::intersperse
     #[inline]
     #[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")]
     fn intersperse_with<G>(self, separator: G) -> IntersperseWith<Self, G>
diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs
index 5a70aa070e870..e466f3151524c 100644
--- a/library/std/src/macros.rs
+++ b/library/std/src/macros.rs
@@ -282,6 +282,10 @@ macro_rules! eprintln {
 #[macro_export]
 #[stable(feature = "dbg_macro", since = "1.32.0")]
 macro_rules! dbg {
+    // NOTE: We cannot use `concat!` to make a static string as a format argument
+    // of `eprintln!` because `file!` could contain a `{` or
+    // `$val` expression could be a block (`{ .. }`), in which case the `eprintln!`
+    // will be malformed.
     () => {
         $crate::eprintln!("[{}:{}]", $crate::file!(), $crate::line!());
     };
diff --git a/library/std/src/net/ip.rs b/library/std/src/net/ip.rs
index d33b772633d29..84449e4876718 100644
--- a/library/std/src/net/ip.rs
+++ b/library/std/src/net/ip.rs
@@ -1610,11 +1610,11 @@ impl fmt::Display for Ipv6Addr {
                 /// Write a colon-separated part of the address
                 #[inline]
                 fn fmt_subslice(f: &mut fmt::Formatter<'_>, chunk: &[u16]) -> fmt::Result {
-                    if let Some(first) = chunk.first() {
-                        fmt::LowerHex::fmt(first, f)?;
-                        for segment in &chunk[1..] {
+                    if let Some((first, tail)) = chunk.split_first() {
+                        write!(f, "{:x}", first)?;
+                        for segment in tail {
                             f.write_char(':')?;
-                            fmt::LowerHex::fmt(segment, f)?;
+                            write!(f, "{:x}", segment)?;
                         }
                     }
                     Ok(())
diff --git a/library/std/src/net/ip/tests.rs b/library/std/src/net/ip/tests.rs
index 44fb3adf07023..ef0d4edc43473 100644
--- a/library/std/src/net/ip/tests.rs
+++ b/library/std/src/net/ip/tests.rs
@@ -166,6 +166,9 @@ fn ipv6_addr_to_string() {
 
     // two runs of zeros, equal length
     assert_eq!("1::4:5:0:0:8", Ipv6Addr::new(1, 0, 0, 4, 5, 0, 0, 8).to_string());
+
+    // don't prefix `0x` to each segment in `dbg!`.
+    assert_eq!("1::4:5:0:0:8", &format!("{:#?}", Ipv6Addr::new(1, 0, 0, 4, 5, 0, 0, 8)));
 }
 
 #[test]
diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs
index d18b94b6c1aef..0f568da459bef 100644
--- a/library/std/src/panic.rs
+++ b/library/std/src/panic.rs
@@ -31,9 +31,9 @@ pub use core::panic::{Location, PanicInfo};
 /// accessed later using [`PanicInfo::payload`].
 ///
 /// See the [`panic!`] macro for more information about panicking.
-#[unstable(feature = "panic_any", issue = "78500")]
+#[stable(feature = "panic_any", since = "1.51.0")]
 #[inline]
-pub fn panic_any<M: Any + Send>(msg: M) -> ! {
+pub fn panic_any<M: 'static + Any + Send>(msg: M) -> ! {
     crate::panicking::begin_panic(msg);
 }
 
diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs
index c19bb536ce83c..6626fead774d6 100644
--- a/src/bootstrap/check.rs
+++ b/src/bootstrap/check.rs
@@ -320,6 +320,13 @@ macro_rules! tool_check_step {
                     cargo.arg("--all-targets");
                 }
 
+                // Enable internal lints for clippy and rustdoc
+                // NOTE: this intentionally doesn't enable lints for any other tools,
+                // see https://github.com/rust-lang/rust/pull/80573#issuecomment-754010776
+                if $path == "src/tools/rustdoc" || $path == "src/tools/clippy" {
+                    cargo.rustflag("-Zunstable-options");
+                }
+
                 builder.info(&format!(
                     "Checking stage{} {} artifacts ({} -> {})",
                     builder.top_stage,
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 916684baf855d..1f9e7f8ae5cd4 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -5,7 +5,7 @@ use std::iter::once;
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
-use rustc_hir::def::{CtorKind, DefKind, Res};
+use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
 use rustc_hir::Mutability;
 use rustc_metadata::creader::LoadedMacro;
@@ -17,7 +17,6 @@ use rustc_span::Span;
 
 use crate::clean::{self, Attributes, GetDefId, ToSource, TypeKind};
 use crate::core::DocContext;
-use crate::doctree;
 
 use super::Clean;
 
@@ -246,11 +245,7 @@ fn build_struct(cx: &DocContext<'_>, did: DefId) -> clean::Struct {
     let variant = cx.tcx.adt_def(did).non_enum_variant();
 
     clean::Struct {
-        struct_type: match variant.ctor_kind {
-            CtorKind::Fictive => doctree::Plain,
-            CtorKind::Fn => doctree::Tuple,
-            CtorKind::Const => doctree::Unit,
-        },
+        struct_type: variant.ctor_kind,
         generics: (cx.tcx.generics_of(did), predicates).clean(cx),
         fields: variant.fields.clean(cx),
         fields_stripped: false,
@@ -262,7 +257,6 @@ fn build_union(cx: &DocContext<'_>, did: DefId) -> clean::Union {
     let variant = cx.tcx.adt_def(did).non_enum_variant();
 
     clean::Union {
-        struct_type: doctree::Plain,
         generics: (cx.tcx.generics_of(did), predicates).clean(cx),
         fields: variant.fields.clean(cx),
         fields_stripped: false,
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 3ddb2adbf0aa7..8fa60fa7178ae 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1827,7 +1827,7 @@ impl Clean<Visibility> for ty::Visibility {
 impl Clean<VariantStruct> for rustc_hir::VariantData<'_> {
     fn clean(&self, cx: &DocContext<'_>) -> VariantStruct {
         VariantStruct {
-            struct_type: doctree::struct_type_from_def(self),
+            struct_type: CtorKind::from_hir(self),
             fields: self.fields().iter().map(|x| x.clean(cx)).collect(),
             fields_stripped: false,
         }
@@ -1842,7 +1842,7 @@ impl Clean<Item> for ty::VariantDef {
                 self.fields.iter().map(|f| cx.tcx.type_of(f.did).clean(cx)).collect(),
             ),
             CtorKind::Fictive => Variant::Struct(VariantStruct {
-                struct_type: doctree::Plain,
+                struct_type: CtorKind::Fictive,
                 fields_stripped: false,
                 fields: self
                     .fields
@@ -1996,13 +1996,12 @@ impl Clean<Vec<Item>> for (&hir::Item<'_>, Option<Symbol>) {
                     bounds: bounds.clean(cx),
                 }),
                 ItemKind::Union(ref variant_data, ref generics) => UnionItem(Union {
-                    struct_type: doctree::struct_type_from_def(&variant_data),
                     generics: generics.clean(cx),
                     fields: variant_data.fields().clean(cx),
                     fields_stripped: false,
                 }),
                 ItemKind::Struct(ref variant_data, ref generics) => StructItem(Struct {
-                    struct_type: doctree::struct_type_from_def(&variant_data),
+                    struct_type: CtorKind::from_hir(variant_data),
                     generics: generics.clean(cx),
                     fields: variant_data.fields().clean(cx),
                     fields_stripped: false,
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 666b11b5f806d..c767b9dd85bf9 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -16,7 +16,7 @@ use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_feature::UnstableFeatures;
 use rustc_hir as hir;
-use rustc_hir::def::Res;
+use rustc_hir::def::{CtorKind, Res};
 use rustc_hir::def_id::{CrateNum, DefId};
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::Mutability;
@@ -37,7 +37,6 @@ use crate::clean::inline;
 use crate::clean::types::Type::{QPath, ResolvedPath};
 use crate::clean::Clean;
 use crate::core::DocContext;
-use crate::doctree;
 use crate::formats::cache::cache;
 use crate::formats::item_type::ItemType;
 use crate::html::render::cache::ExternalLocation;
@@ -1685,7 +1684,7 @@ impl Visibility {
 
 #[derive(Clone, Debug)]
 crate struct Struct {
-    crate struct_type: doctree::StructType,
+    crate struct_type: CtorKind,
     crate generics: Generics,
     crate fields: Vec<Item>,
     crate fields_stripped: bool,
@@ -1693,7 +1692,6 @@ crate struct Struct {
 
 #[derive(Clone, Debug)]
 crate struct Union {
-    crate struct_type: doctree::StructType,
     crate generics: Generics,
     crate fields: Vec<Item>,
     crate fields_stripped: bool,
@@ -1704,7 +1702,7 @@ crate struct Union {
 /// only as a variant in an enum.
 #[derive(Clone, Debug)]
 crate struct VariantStruct {
-    crate struct_type: doctree::StructType,
+    crate struct_type: CtorKind,
     crate fields: Vec<Item>,
     crate fields_stripped: bool,
 }
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index e43ea965c0423..8006c3d277164 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -1,4 +1,4 @@
-use std::collections::{BTreeMap, HashMap};
+use std::collections::BTreeMap;
 use std::convert::TryFrom;
 use std::ffi::OsStr;
 use std::fmt;
@@ -219,7 +219,7 @@ crate struct RenderOptions {
     crate extern_html_root_urls: BTreeMap<String, String>,
     /// A map of the default settings (values are as for DOM storage API). Keys should lack the
     /// `rustdoc-` prefix.
-    crate default_settings: HashMap<String, String>,
+    crate default_settings: FxHashMap<String, String>,
     /// If present, suffix added to CSS/JavaScript files when referencing them in generated pages.
     crate resource_suffix: String,
     /// Whether to run the static CSS/JavaScript through a minifier when outputting them. `true` by
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index c87ab833f7a5e..bbf4de5cdc9a8 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -1,4 +1,5 @@
 use rustc_ast as ast;
+use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::{ColorConfig, ErrorReported};
 use rustc_hir as hir;
@@ -16,7 +17,6 @@ use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP};
 use rustc_target::spec::TargetTriple;
 use tempfile::Builder as TempFileBuilder;
 
-use std::collections::HashMap;
 use std::env;
 use std::io::{self, Write};
 use std::panic;
@@ -704,7 +704,7 @@ crate struct Collector {
     position: Span,
     source_map: Option<Lrc<SourceMap>>,
     filename: Option<PathBuf>,
-    visited_tests: HashMap<(String, usize), usize>,
+    visited_tests: FxHashMap<(String, usize), usize>,
 }
 
 impl Collector {
@@ -728,7 +728,7 @@ impl Collector {
             position: DUMMY_SP,
             source_map,
             filename,
-            visited_tests: HashMap::new(),
+            visited_tests: FxHashMap::default(),
         }
     }
 
@@ -1010,7 +1010,7 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> {
                 self.codes,
                 self.collector.enable_per_target_ignores,
                 Some(&crate::html::markdown::ExtraInfo::new(
-                    &self.tcx,
+                    self.tcx,
                     hir_id,
                     span_of_attrs(&attrs).unwrap_or(sp),
                 )),
diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs
index f90623c03118b..645b2bb193ec2 100644
--- a/src/librustdoc/doctree.rs
+++ b/src/librustdoc/doctree.rs
@@ -1,7 +1,5 @@
 //! This module is used to store stuff from Rust's AST in a more convenient
 //! manner (and with prettier names) before cleaning.
-crate use self::StructType::*;
-
 use rustc_span::{self, Span, Symbol};
 
 use rustc_hir as hir;
@@ -34,21 +32,3 @@ impl Module<'hir> {
         }
     }
 }
-
-#[derive(Debug, Clone, Copy)]
-crate enum StructType {
-    /// A braced struct
-    Plain,
-    /// A tuple struct
-    Tuple,
-    /// A unit struct
-    Unit,
-}
-
-crate fn struct_type_from_def(vdata: &hir::VariantData<'_>) -> StructType {
-    match *vdata {
-        hir::VariantData::Struct(..) => Plain,
-        hir::VariantData::Tuple(..) => Tuple,
-        hir::VariantData::Unit(..) => Unit,
-    }
-}
diff --git a/src/librustdoc/formats/renderer.rs b/src/librustdoc/formats/renderer.rs
index c91d6decc0b67..5c0f5e50c9e2c 100644
--- a/src/librustdoc/formats/renderer.rs
+++ b/src/librustdoc/formats/renderer.rs
@@ -1,6 +1,6 @@
 use std::sync::Arc;
 
-use rustc_middle::ty;
+use rustc_middle::ty::TyCtxt;
 use rustc_span::edition::Edition;
 
 use crate::clean;
@@ -20,7 +20,7 @@ crate trait FormatRenderer<'tcx>: Clone {
         render_info: RenderInfo,
         edition: Edition,
         cache: &mut Cache,
-        tcx: ty::TyCtxt<'tcx>,
+        tcx: TyCtxt<'tcx>,
     ) -> Result<(Self, clean::Crate), Error>;
 
     /// Renders a single non-module item. This means no recursive sub-item rendering is required.
@@ -55,7 +55,7 @@ crate fn run_format<'tcx, T: FormatRenderer<'tcx>>(
     render_info: RenderInfo,
     diag: &rustc_errors::Handler,
     edition: Edition,
-    tcx: ty::TyCtxt<'tcx>,
+    tcx: TyCtxt<'tcx>,
 ) -> Result<(), Error> {
     let (krate, mut cache) = Cache::from_krate(
         render_info.clone(),
diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs
index 4458eea95f3e1..c6ff4b57a6e59 100644
--- a/src/librustdoc/html/layout.rs
+++ b/src/librustdoc/html/layout.rs
@@ -1,6 +1,7 @@
-use std::collections::HashMap;
 use std::path::PathBuf;
 
+use rustc_data_structures::fx::FxHashMap;
+
 use crate::externalfiles::ExternalHtml;
 use crate::html::escape::Escape;
 use crate::html::format::{Buffer, Print};
@@ -11,7 +12,7 @@ crate struct Layout {
     crate logo: String,
     crate favicon: String,
     crate external_html: ExternalHtml,
-    crate default_settings: HashMap<String, String>,
+    crate default_settings: FxHashMap<String, String>,
     crate krate: String,
     /// The given user css file which allow to customize the generated
     /// documentation theme.
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index cfa6cd96595d6..7c8b76be374a8 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -620,7 +620,7 @@ crate fn find_testable_code<T: doctest::Tester>(
     tests: &mut T,
     error_codes: ErrorCodes,
     enable_per_target_ignores: bool,
-    extra_info: Option<&ExtraInfo<'_, '_>>,
+    extra_info: Option<&ExtraInfo<'_>>,
 ) {
     let mut parser = Parser::new(doc).into_offset_iter();
     let mut prev_offset = 0;
@@ -681,19 +681,19 @@ crate fn find_testable_code<T: doctest::Tester>(
     }
 }
 
-crate struct ExtraInfo<'a, 'b> {
+crate struct ExtraInfo<'tcx> {
     hir_id: Option<HirId>,
     item_did: Option<DefId>,
     sp: Span,
-    tcx: &'a TyCtxt<'b>,
+    tcx: TyCtxt<'tcx>,
 }
 
-impl<'a, 'b> ExtraInfo<'a, 'b> {
-    crate fn new(tcx: &'a TyCtxt<'b>, hir_id: HirId, sp: Span) -> ExtraInfo<'a, 'b> {
+impl<'tcx> ExtraInfo<'tcx> {
+    crate fn new(tcx: TyCtxt<'tcx>, hir_id: HirId, sp: Span) -> ExtraInfo<'tcx> {
         ExtraInfo { hir_id: Some(hir_id), item_did: None, sp, tcx }
     }
 
-    crate fn new_did(tcx: &'a TyCtxt<'b>, did: DefId, sp: Span) -> ExtraInfo<'a, 'b> {
+    crate fn new_did(tcx: TyCtxt<'tcx>, did: DefId, sp: Span) -> ExtraInfo<'tcx> {
         ExtraInfo { hir_id: None, item_did: Some(did), sp, tcx }
     }
 
@@ -775,7 +775,7 @@ impl LangString {
         string: &str,
         allow_error_code_check: ErrorCodes,
         enable_per_target_ignores: bool,
-        extra: Option<&ExtraInfo<'_, '_>>,
+        extra: Option<&ExtraInfo<'_>>,
     ) -> LangString {
         let allow_error_code_check = allow_error_code_check.as_bool();
         let mut seen_rust_tags = false;
@@ -1208,7 +1208,7 @@ crate struct RustCodeBlock {
 
 /// Returns a range of bytes for each code block in the markdown that is tagged as `rust` or
 /// untagged (and assumed to be rust).
-crate fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_, '_>) -> Vec<RustCodeBlock> {
+crate fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec<RustCodeBlock> {
     let mut code_blocks = vec![];
 
     if md.is_empty() {
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 26afd705740b2..6167b75ee50d3 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -52,10 +52,10 @@ use rustc_attr::{Deprecation, StabilityLevel};
 use rustc_data_structures::flock;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir as hir;
+use rustc_hir::def::CtorKind;
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
 use rustc_hir::Mutability;
 use rustc_middle::middle::stability;
-use rustc_middle::ty;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
 use rustc_span::edition::Edition;
@@ -68,7 +68,6 @@ use serde::{Serialize, Serializer};
 use crate::clean::{self, AttributesExt, GetDefId, RenderedLink, SelfTy, TypeKind};
 use crate::config::{RenderInfo, RenderOptions};
 use crate::docfs::{DocFS, PathError};
-use crate::doctree;
 use crate::error::Error;
 use crate::formats::cache::{cache, Cache};
 use crate::formats::item_type::ItemType;
@@ -390,7 +389,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
         _render_info: RenderInfo,
         edition: Edition,
         cache: &mut Cache,
-        tcx: ty::TyCtxt<'tcx>,
+        tcx: TyCtxt<'tcx>,
     ) -> Result<(Self, clean::Crate), Error> {
         // need to save a copy of the options for rendering the index page
         let md_opts = options.clone();
@@ -3104,7 +3103,7 @@ fn item_struct(
             _ => None,
         })
         .peekable();
-    if let doctree::Plain = s.struct_type {
+    if let CtorKind::Fictive = s.struct_type {
         if fields.peek().is_some() {
             write!(
                 w,
@@ -3354,7 +3353,7 @@ fn render_struct(
     w: &mut Buffer,
     it: &clean::Item,
     g: Option<&clean::Generics>,
-    ty: doctree::StructType,
+    ty: CtorKind,
     fields: &[clean::Item],
     tab: &str,
     structhead: bool,
@@ -3371,7 +3370,7 @@ fn render_struct(
         write!(w, "{}", g.print())
     }
     match ty {
-        doctree::Plain => {
+        CtorKind::Fictive => {
             if let Some(g) = g {
                 write!(w, "{}", WhereClause { gens: g, indent: 0, end_newline: true })
             }
@@ -3403,7 +3402,7 @@ fn render_struct(
             }
             write!(w, "}}");
         }
-        doctree::Tuple => {
+        CtorKind::Fn => {
             write!(w, "(");
             for (i, field) in fields.iter().enumerate() {
                 if i > 0 {
@@ -3428,7 +3427,7 @@ fn render_struct(
             }
             write!(w, ";");
         }
-        doctree::Unit => {
+        CtorKind::Const => {
             // Needed for PhantomData.
             if let Some(g) = g {
                 write!(w, "{}", WhereClause { gens: g, indent: 0, end_newline: false })
@@ -4463,7 +4462,7 @@ fn sidebar_struct(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, s: &clea
     let fields = get_struct_fields_name(&s.fields);
 
     if !fields.is_empty() {
-        if let doctree::Plain = s.struct_type {
+        if let CtorKind::Fictive = s.struct_type {
             sidebar.push_str(&format!(
                 "<a class=\"sidebar-title\" href=\"#fields\">Fields</a>\
                  <div class=\"sidebar-links\">{}</div>",
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 7d05cb016b67c..bfd2141d9a174 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -5,11 +5,11 @@
 use std::convert::From;
 
 use rustc_ast::ast;
+use rustc_hir::def::CtorKind;
 use rustc_span::def_id::{DefId, CRATE_DEF_INDEX};
 use rustc_span::Pos;
 
 use crate::clean;
-use crate::doctree;
 use crate::formats::item_type::ItemType;
 use crate::json::types::*;
 use crate::json::JsonRenderer;
@@ -27,7 +27,7 @@ impl JsonRenderer<'_> {
                 name: name.map(|sym| sym.to_string()),
                 source: self.convert_span(source),
                 visibility: self.convert_visibility(visibility),
-                docs: attrs.collapsed_doc_value().unwrap_or_default(),
+                docs: attrs.collapsed_doc_value(),
                 links: attrs
                     .links
                     .into_iter()
@@ -210,9 +210,9 @@ impl From<clean::Struct> for Struct {
 
 impl From<clean::Union> for Struct {
     fn from(struct_: clean::Union) -> Self {
-        let clean::Union { struct_type, generics, fields, fields_stripped } = struct_;
+        let clean::Union { generics, fields, fields_stripped } = struct_;
         Struct {
-            struct_type: struct_type.into(),
+            struct_type: StructType::Union,
             generics: generics.into(),
             fields_stripped,
             fields: ids(fields),
@@ -221,13 +221,12 @@ impl From<clean::Union> for Struct {
     }
 }
 
-impl From<doctree::StructType> for StructType {
-    fn from(struct_type: doctree::StructType) -> Self {
-        use doctree::StructType::*;
+impl From<CtorKind> for StructType {
+    fn from(struct_type: CtorKind) -> Self {
         match struct_type {
-            Plain => StructType::Plain,
-            Tuple => StructType::Tuple,
-            Unit => StructType::Unit,
+            CtorKind::Fictive => StructType::Plain,
+            CtorKind::Fn => StructType::Tuple,
+            CtorKind::Const => StructType::Unit,
         }
     }
 }
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index 64500c1d91161..dc50c8a76b24f 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -13,7 +13,7 @@ use std::path::PathBuf;
 use std::rc::Rc;
 
 use rustc_data_structures::fx::FxHashMap;
-use rustc_middle::ty;
+use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
 use rustc_span::edition::Edition;
 
@@ -26,7 +26,7 @@ use crate::html::render::cache::ExternalLocation;
 
 #[derive(Clone)]
 crate struct JsonRenderer<'tcx> {
-    tcx: ty::TyCtxt<'tcx>,
+    tcx: TyCtxt<'tcx>,
     /// A mapping of IDs that contains all local items for this crate which gets output as a top
     /// level field of the JSON blob.
     index: Rc<RefCell<FxHashMap<types::Id, types::Item>>>,
@@ -131,7 +131,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
         _render_info: RenderInfo,
         _edition: Edition,
         _cache: &mut Cache,
-        tcx: ty::TyCtxt<'tcx>,
+        tcx: TyCtxt<'tcx>,
     ) -> Result<(Self, clean::Crate), Error> {
         debug!("Initializing json renderer");
         Ok((
@@ -241,7 +241,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
                     )
                 })
                 .collect(),
-            format_version: 1,
+            format_version: 2,
         };
         let mut p = self.out_path.clone();
         p.push(output.index.get(&output.root).unwrap().name.clone().unwrap());
diff --git a/src/librustdoc/json/types.rs b/src/librustdoc/json/types.rs
index 9335fe9be1a4b..66cf12954dd0b 100644
--- a/src/librustdoc/json/types.rs
+++ b/src/librustdoc/json/types.rs
@@ -68,8 +68,9 @@ pub struct Item {
     /// By default all documented items are public, but you can tell rustdoc to output private items
     /// so this field is needed to differentiate.
     pub visibility: Visibility,
-    /// The full markdown docstring of this item.
-    pub docs: String,
+    /// The full markdown docstring of this item. Absent if there is no documentation at all,
+    /// Some("") if there is some documentation but it is empty (EG `#[doc = ""]`).
+    pub docs: Option<String>,
     /// This mapping resolves [intra-doc links](https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md) from the docstring to their IDs
     pub links: FxHashMap<String, Id>,
     /// Stringified versions of the attributes on this item (e.g. `"#[inline]"`)
@@ -269,6 +270,7 @@ pub enum StructType {
     Plain,
     Tuple,
     Unit,
+    Union,
 }
 
 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 719aca612f50d..d17189b416dd4 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -18,6 +18,7 @@
 #![feature(str_split_once)]
 #![feature(iter_intersperse)]
 #![recursion_limit = "256"]
+#![deny(rustc::internal)]
 
 #[macro_use]
 extern crate lazy_static;
@@ -65,7 +66,7 @@ use std::process;
 use rustc_driver::abort_on_err;
 use rustc_errors::ErrorReported;
 use rustc_interface::interface;
-use rustc_middle::ty;
+use rustc_middle::ty::TyCtxt;
 use rustc_session::config::{make_crate_type_option, ErrorOutputType, RustcOptGroup};
 use rustc_session::getopts;
 use rustc_session::{early_error, early_warn};
@@ -471,7 +472,7 @@ fn run_renderer<'tcx, T: formats::FormatRenderer<'tcx>>(
     render_info: config::RenderInfo,
     diag: &rustc_errors::Handler,
     edition: rustc_span::edition::Edition,
-    tcx: ty::TyCtxt<'tcx>,
+    tcx: TyCtxt<'tcx>,
 ) -> MainResult {
     match formats::run_format::<T>(krate, renderopts, render_info, &diag, edition, tcx) {
         Ok(_) => Ok(()),
diff --git a/src/librustdoc/passes/check_code_block_syntax.rs b/src/librustdoc/passes/check_code_block_syntax.rs
index 554392c213e24..9516130034b59 100644
--- a/src/librustdoc/passes/check_code_block_syntax.rs
+++ b/src/librustdoc/passes/check_code_block_syntax.rs
@@ -108,7 +108,7 @@ impl<'a, 'tcx> DocFolder for SyntaxChecker<'a, 'tcx> {
     fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
         if let Some(dox) = &item.attrs.collapsed_doc_value() {
             let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span());
-            let extra = crate::html::markdown::ExtraInfo::new_did(&self.cx.tcx, item.def_id, sp);
+            let extra = crate::html::markdown::ExtraInfo::new_did(self.cx.tcx, item.def_id, sp);
             for code_block in markdown::rust_code_blocks(&dox, &extra) {
                 self.check_rust_syntax(&item, &dox, code_block);
             }
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 2694450a520c9..002d8938f694d 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -13,6 +13,7 @@ use rustc_hir::def::{
     PerNS,
 };
 use rustc_hir::def_id::{CrateNum, DefId};
+use rustc_middle::ty::TyCtxt;
 use rustc_middle::{bug, ty};
 use rustc_resolve::ParentScope;
 use rustc_session::lint::{
@@ -85,7 +86,7 @@ impl Res {
         }
     }
 
-    fn name(self, tcx: ty::TyCtxt<'_>) -> String {
+    fn name(self, tcx: TyCtxt<'_>) -> String {
         match self {
             Res::Def(_, id) => tcx.item_name(id).to_string(),
             Res::Primitive(prim) => prim.as_str().to_string(),
@@ -865,12 +866,11 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
 
         // FIXME(jynelson): this shouldn't go through stringification, rustdoc should just use the DefId directly
         let self_name = self_id.and_then(|self_id| {
-            use ty::TyKind;
             if matches!(self.cx.tcx.def_kind(self_id), DefKind::Impl) {
                 // using `ty.to_string()` (or any variant) has issues with raw idents
                 let ty = self.cx.tcx.type_of(self_id);
                 let name = match ty.kind() {
-                    TyKind::Adt(def, _) => Some(self.cx.tcx.item_name(def.did).to_string()),
+                    ty::Adt(def, _) => Some(self.cx.tcx.item_name(def.did).to_string()),
                     other if other.is_primitive() => Some(ty.to_string()),
                     _ => None,
                 };
diff --git a/src/test/ui/issues/issue-1962.fixed b/src/test/ui/issues/issue-1962.fixed
index b810a90ef37f9..897fd172b29f3 100644
--- a/src/test/ui/issues/issue-1962.fixed
+++ b/src/test/ui/issues/issue-1962.fixed
@@ -3,8 +3,8 @@
 
 fn main() {
     let mut i = 0;
-    loop { //~ ERROR denote infinite loops with `loop
+    'a: loop { //~ ERROR denote infinite loops with `loop
         i += 1;
-        if i == 5 { break; }
+        if i == 5 { break 'a; }
     }
 }
diff --git a/src/test/ui/issues/issue-1962.rs b/src/test/ui/issues/issue-1962.rs
index 00d2bbd28506e..71e874100874f 100644
--- a/src/test/ui/issues/issue-1962.rs
+++ b/src/test/ui/issues/issue-1962.rs
@@ -3,8 +3,8 @@
 
 fn main() {
     let mut i = 0;
-    while true { //~ ERROR denote infinite loops with `loop
+    'a: while true { //~ ERROR denote infinite loops with `loop
         i += 1;
-        if i == 5 { break; }
+        if i == 5 { break 'a; }
     }
 }
diff --git a/src/test/ui/issues/issue-1962.stderr b/src/test/ui/issues/issue-1962.stderr
index 17142912696a7..4c32a4cf3dd59 100644
--- a/src/test/ui/issues/issue-1962.stderr
+++ b/src/test/ui/issues/issue-1962.stderr
@@ -1,8 +1,8 @@
 error: denote infinite loops with `loop { ... }`
   --> $DIR/issue-1962.rs:6:5
    |
-LL |     while true {
-   |     ^^^^^^^^^^ help: use `loop`
+LL |     'a: while true {
+   |     ^^^^^^^^^^^^^^ help: use `loop`
    |
    = note: requested on the command line with `-D while-true`
 
diff --git a/src/test/ui/issues/issue-27042.stderr b/src/test/ui/issues/issue-27042.stderr
index 7dee1a6a5f044..59ef28481d0e6 100644
--- a/src/test/ui/issues/issue-27042.stderr
+++ b/src/test/ui/issues/issue-27042.stderr
@@ -4,7 +4,7 @@ warning: denote infinite loops with `loop { ... }`
 LL | /         'b:
 LL | |
 LL | |         while true { break }; // but here we cite the whole loop
-   | |____________________________^ help: use `loop`
+   | |__________________^ help: use `loop`
    |
    = note: `#[warn(while_true)]` on by default
 
diff --git a/src/test/ui/label/label_misspelled.rs b/src/test/ui/label/label_misspelled.rs
index ebfd5642c9fa8..e3180b06ecb2b 100644
--- a/src/test/ui/label/label_misspelled.rs
+++ b/src/test/ui/label/label_misspelled.rs
@@ -1,18 +1,62 @@
+#![warn(unused_labels)]
+
 fn main() {
+    'while_loop: while true { //~ WARN denote infinite loops with
+        //~^ WARN unused label
+        while_loop;
+        //~^ ERROR cannot find value `while_loop` in this scope
+    };
+    'while_let: while let Some(_) = Some(()) {
+        //~^ WARN unused label
+        while_let;
+        //~^ ERROR cannot find value `while_let` in this scope
+    }
+    'for_loop: for _ in 0..3 {
+        //~^ WARN unused label
+        for_loop;
+        //~^ ERROR cannot find value `for_loop` in this scope
+    };
     'LOOP: loop {
+        //~^ WARN unused label
         LOOP;
         //~^ ERROR cannot find value `LOOP` in this scope
     };
+}
+
+fn foo() {
+    'LOOP: loop {
+        break LOOP;
+        //~^ ERROR cannot find value `LOOP` in this scope
+    };
     'while_loop: while true { //~ WARN denote infinite loops with
-        while_loop;
+        break while_loop;
         //~^ ERROR cannot find value `while_loop` in this scope
     };
     'while_let: while let Some(_) = Some(()) {
-        while_let;
+        break while_let;
         //~^ ERROR cannot find value `while_let` in this scope
     }
     'for_loop: for _ in 0..3 {
-        for_loop;
+        break for_loop;
         //~^ ERROR cannot find value `for_loop` in this scope
     };
 }
+
+fn bar() {
+    let foo = ();
+    'while_loop: while true { //~ WARN denote infinite loops with
+        //~^ WARN unused label
+        break foo;
+        //~^ ERROR `break` with value from a `while` loop
+    };
+    'while_let: while let Some(_) = Some(()) {
+        //~^ WARN unused label
+        break foo;
+        //~^ ERROR `break` with value from a `while` loop
+    }
+    'for_loop: for _ in 0..3 {
+        //~^ WARN unused label
+        break foo;
+        //~^ ERROR `break` with value from a `for` loop
+    };
+}
diff --git a/src/test/ui/label/label_misspelled.stderr b/src/test/ui/label/label_misspelled.stderr
index 1368ca4126cdd..b09695787a443 100644
--- a/src/test/ui/label/label_misspelled.stderr
+++ b/src/test/ui/label/label_misspelled.stderr
@@ -1,47 +1,206 @@
-error[E0425]: cannot find value `LOOP` in this scope
-  --> $DIR/label_misspelled.rs:3:9
-   |
-LL |         LOOP;
-   |         ^^^^
-   |         |
-   |         not found in this scope
-   |         help: a label with a similar name exists: `'LOOP`
-
 error[E0425]: cannot find value `while_loop` in this scope
-  --> $DIR/label_misspelled.rs:7:9
+  --> $DIR/label_misspelled.rs:6:9
    |
+LL |     'while_loop: while true {
+   |     ----------- a label with a similar name exists
+LL |
 LL |         while_loop;
-   |         ^^^^^^^^^^
-   |         |
-   |         not found in this scope
-   |         help: a label with a similar name exists: `'while_loop`
+   |         ^^^^^^^^^^ not found in this scope
 
 error[E0425]: cannot find value `while_let` in this scope
   --> $DIR/label_misspelled.rs:11:9
    |
+LL |     'while_let: while let Some(_) = Some(()) {
+   |     ---------- a label with a similar name exists
+LL |
 LL |         while_let;
-   |         ^^^^^^^^^
-   |         |
-   |         not found in this scope
-   |         help: a label with a similar name exists: `'while_let`
+   |         ^^^^^^^^^ not found in this scope
 
 error[E0425]: cannot find value `for_loop` in this scope
-  --> $DIR/label_misspelled.rs:15:9
+  --> $DIR/label_misspelled.rs:16:9
    |
+LL |     'for_loop: for _ in 0..3 {
+   |     --------- a label with a similar name exists
+LL |
 LL |         for_loop;
-   |         ^^^^^^^^
-   |         |
-   |         not found in this scope
-   |         help: a label with a similar name exists: `'for_loop`
+   |         ^^^^^^^^ not found in this scope
+
+error[E0425]: cannot find value `LOOP` in this scope
+  --> $DIR/label_misspelled.rs:21:9
+   |
+LL |     'LOOP: loop {
+   |     ----- a label with a similar name exists
+LL |
+LL |         LOOP;
+   |         ^^^^ not found in this scope
+
+error[E0425]: cannot find value `LOOP` in this scope
+  --> $DIR/label_misspelled.rs:28:15
+   |
+LL |     'LOOP: loop {
+   |     ----- a label with a similar name exists
+LL |         break LOOP;
+   |               ^^^^
+   |               |
+   |               not found in this scope
+   |               help: use the similarly named label: `'LOOP`
+
+error[E0425]: cannot find value `while_loop` in this scope
+  --> $DIR/label_misspelled.rs:32:15
+   |
+LL |     'while_loop: while true {
+   |     ----------- a label with a similar name exists
+LL |         break while_loop;
+   |               ^^^^^^^^^^
+   |               |
+   |               not found in this scope
+   |               help: use the similarly named label: `'while_loop`
+
+error[E0425]: cannot find value `while_let` in this scope
+  --> $DIR/label_misspelled.rs:36:15
+   |
+LL |     'while_let: while let Some(_) = Some(()) {
+   |     ---------- a label with a similar name exists
+LL |         break while_let;
+   |               ^^^^^^^^^
+   |               |
+   |               not found in this scope
+   |               help: use the similarly named label: `'while_let`
+
+error[E0425]: cannot find value `for_loop` in this scope
+  --> $DIR/label_misspelled.rs:40:15
+   |
+LL |     'for_loop: for _ in 0..3 {
+   |     --------- a label with a similar name exists
+LL |         break for_loop;
+   |               ^^^^^^^^
+   |               |
+   |               not found in this scope
+   |               help: use the similarly named label: `'for_loop`
+
+warning: unused label
+  --> $DIR/label_misspelled.rs:4:5
+   |
+LL |     'while_loop: while true {
+   |     ^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/label_misspelled.rs:1:9
+   |
+LL | #![warn(unused_labels)]
+   |         ^^^^^^^^^^^^^
 
 warning: denote infinite loops with `loop { ... }`
-  --> $DIR/label_misspelled.rs:6:5
+  --> $DIR/label_misspelled.rs:4:5
    |
 LL |     'while_loop: while true {
    |     ^^^^^^^^^^^^^^^^^^^^^^^ help: use `loop`
    |
    = note: `#[warn(while_true)]` on by default
 
-error: aborting due to 4 previous errors; 1 warning emitted
+warning: unused label
+  --> $DIR/label_misspelled.rs:9:5
+   |
+LL |     'while_let: while let Some(_) = Some(()) {
+   |     ^^^^^^^^^^
+
+warning: unused label
+  --> $DIR/label_misspelled.rs:14:5
+   |
+LL |     'for_loop: for _ in 0..3 {
+   |     ^^^^^^^^^
+
+warning: unused label
+  --> $DIR/label_misspelled.rs:19:5
+   |
+LL |     'LOOP: loop {
+   |     ^^^^^
+
+warning: denote infinite loops with `loop { ... }`
+  --> $DIR/label_misspelled.rs:31:5
+   |
+LL |     'while_loop: while true {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ help: use `loop`
+
+warning: unused label
+  --> $DIR/label_misspelled.rs:47:5
+   |
+LL |     'while_loop: while true {
+   |     ^^^^^^^^^^^
+
+warning: denote infinite loops with `loop { ... }`
+  --> $DIR/label_misspelled.rs:47:5
+   |
+LL |     'while_loop: while true {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ help: use `loop`
+
+warning: unused label
+  --> $DIR/label_misspelled.rs:52:5
+   |
+LL |     'while_let: while let Some(_) = Some(()) {
+   |     ^^^^^^^^^^
+
+warning: unused label
+  --> $DIR/label_misspelled.rs:57:5
+   |
+LL |     'for_loop: for _ in 0..3 {
+   |     ^^^^^^^^^
+
+error[E0571]: `break` with value from a `while` loop
+  --> $DIR/label_misspelled.rs:49:9
+   |
+LL |     'while_loop: while true {
+   |     ----------------------- you can't `break` with a value in a `while` loop
+LL |
+LL |         break foo;
+   |         ^^^^^^^^^ can only break with a value inside `loop` or breakable block
+   |
+help: use `break` on its own without a value inside this `while` loop
+   |
+LL |         break;
+   |         ^^^^^
+help: alternatively, you might have meant to use the available loop label
+   |
+LL |         break 'while_loop;
+   |               ^^^^^^^^^^^
+
+error[E0571]: `break` with value from a `while` loop
+  --> $DIR/label_misspelled.rs:54:9
+   |
+LL |     'while_let: while let Some(_) = Some(()) {
+   |     ---------------------------------------- you can't `break` with a value in a `while` loop
+LL |
+LL |         break foo;
+   |         ^^^^^^^^^ can only break with a value inside `loop` or breakable block
+   |
+help: use `break` on its own without a value inside this `while` loop
+   |
+LL |         break;
+   |         ^^^^^
+help: alternatively, you might have meant to use the available loop label
+   |
+LL |         break 'while_let;
+   |               ^^^^^^^^^^
+
+error[E0571]: `break` with value from a `for` loop
+  --> $DIR/label_misspelled.rs:59:9
+   |
+LL |     'for_loop: for _ in 0..3 {
+   |     ------------------------ you can't `break` with a value in a `for` loop
+LL |
+LL |         break foo;
+   |         ^^^^^^^^^ can only break with a value inside `loop` or breakable block
+   |
+help: use `break` on its own without a value inside this `for` loop
+   |
+LL |         break;
+   |         ^^^^^
+help: alternatively, you might have meant to use the available loop label
+   |
+LL |         break 'for_loop;
+   |               ^^^^^^^^^
+
+error: aborting due to 11 previous errors; 10 warnings emitted
 
-For more information about this error, try `rustc --explain E0425`.
+Some errors have detailed explanations: E0425, E0571.
+For more information about an error, try `rustc --explain E0425`.
diff --git a/src/test/ui/label/label_misspelled_2.rs b/src/test/ui/label/label_misspelled_2.rs
new file mode 100644
index 0000000000000..55bbe6b30a593
--- /dev/null
+++ b/src/test/ui/label/label_misspelled_2.rs
@@ -0,0 +1,16 @@
+#![warn(unused_labels)]
+
+fn main() {
+    'a: for _ in 0..1 {
+        break 'a;
+    }
+    'b: for _ in 0..1 {
+        break b; //~ ERROR cannot find value `b` in this scope
+    }
+    c: for _ in 0..1 { //~ ERROR malformed loop label
+        break 'c;
+    }
+    d: for _ in 0..1 { //~ ERROR malformed loop label
+        break d; //~ ERROR cannot find value `d` in this scope
+    }
+}
diff --git a/src/test/ui/label/label_misspelled_2.stderr b/src/test/ui/label/label_misspelled_2.stderr
new file mode 100644
index 0000000000000..960646d9894d1
--- /dev/null
+++ b/src/test/ui/label/label_misspelled_2.stderr
@@ -0,0 +1,37 @@
+error: malformed loop label
+  --> $DIR/label_misspelled_2.rs:10:5
+   |
+LL |     c: for _ in 0..1 {
+   |     ^ help: use the correct loop label format: `'c`
+
+error: malformed loop label
+  --> $DIR/label_misspelled_2.rs:13:5
+   |
+LL |     d: for _ in 0..1 {
+   |     ^ help: use the correct loop label format: `'d`
+
+error[E0425]: cannot find value `b` in this scope
+  --> $DIR/label_misspelled_2.rs:8:15
+   |
+LL |     'b: for _ in 0..1 {
+   |     -- a label with a similar name exists
+LL |         break b;
+   |               ^
+   |               |
+   |               not found in this scope
+   |               help: use the similarly named label: `'b`
+
+error[E0425]: cannot find value `d` in this scope
+  --> $DIR/label_misspelled_2.rs:14:15
+   |
+LL |     d: for _ in 0..1 {
+   |     - a label with a similar name exists
+LL |         break d;
+   |               ^
+   |               |
+   |               not found in this scope
+   |               help: use the similarly named label: `'d`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0425`.
diff --git a/src/test/ui/loops/loop-break-value-no-repeat.stderr b/src/test/ui/loops/loop-break-value-no-repeat.stderr
index ff93e9220e986..1c0d39a6e5ad7 100644
--- a/src/test/ui/loops/loop-break-value-no-repeat.stderr
+++ b/src/test/ui/loops/loop-break-value-no-repeat.stderr
@@ -1,10 +1,12 @@
 error[E0571]: `break` with value from a `for` loop
   --> $DIR/loop-break-value-no-repeat.rs:12:9
    |
+LL |     for _ in &[1,2,3] {
+   |     ----------------- you can't `break` with a value in a `for` loop
 LL |         break 22
    |         ^^^^^^^^ can only break with a value inside `loop` or breakable block
    |
-help: instead, use `break` on its own without a value inside this `for` loop
+help: use `break` on its own without a value inside this `for` loop
    |
 LL |         break
    |         ^^^^^
diff --git a/src/test/ui/loops/loop-break-value.rs b/src/test/ui/loops/loop-break-value.rs
index 8a080cfdf494a..51c9a36a03956 100644
--- a/src/test/ui/loops/loop-break-value.rs
+++ b/src/test/ui/loops/loop-break-value.rs
@@ -94,6 +94,5 @@ fn main() {
     'LOOP: for _ in 0 .. 9 {
         break LOOP;
         //~^ ERROR cannot find value `LOOP` in this scope
-        //~| ERROR `break` with value from a `for` loop
     }
 }
diff --git a/src/test/ui/loops/loop-break-value.stderr b/src/test/ui/loops/loop-break-value.stderr
index 0237435c8b46a..adb099f9b1769 100644
--- a/src/test/ui/loops/loop-break-value.stderr
+++ b/src/test/ui/loops/loop-break-value.stderr
@@ -1,11 +1,13 @@
 error[E0425]: cannot find value `LOOP` in this scope
   --> $DIR/loop-break-value.rs:95:15
    |
+LL |     'LOOP: for _ in 0 .. 9 {
+   |     ----- a label with a similar name exists
 LL |         break LOOP;
    |               ^^^^
    |               |
    |               not found in this scope
-   |               help: a label with a similar name exists: `'LOOP`
+   |               help: use the similarly named label: `'LOOP`
 
 warning: denote infinite loops with `loop { ... }`
   --> $DIR/loop-break-value.rs:26:5
@@ -18,32 +20,44 @@ LL |     'while_loop: while true {
 error[E0571]: `break` with value from a `while` loop
   --> $DIR/loop-break-value.rs:28:9
    |
+LL |     'while_loop: while true {
+   |     ----------------------- you can't `break` with a value in a `while` loop
+LL |         break;
 LL |         break ();
    |         ^^^^^^^^ can only break with a value inside `loop` or breakable block
    |
-help: instead, use `break` on its own without a value inside this `while` loop
+help: use `break` on its own without a value inside this `while` loop
    |
 LL |         break;
    |         ^^^^^
+help: alternatively, you might have meant to use the available loop label
+   |
+LL |         break 'while_loop;
+   |               ^^^^^^^^^^^
 
 error[E0571]: `break` with value from a `while` loop
   --> $DIR/loop-break-value.rs:30:13
    |
+LL |     'while_loop: while true {
+   |     ----------------------- you can't `break` with a value in a `while` loop
+...
 LL |             break 'while_loop 123;
    |             ^^^^^^^^^^^^^^^^^^^^^ can only break with a value inside `loop` or breakable block
    |
-help: instead, use `break` on its own without a value inside this `while` loop
+help: use `break` on its own without a value inside this `while` loop
    |
-LL |             break;
-   |             ^^^^^
+LL |             break 'while_loop;
+   |             ^^^^^^^^^^^^^^^^^
 
 error[E0571]: `break` with value from a `while` loop
   --> $DIR/loop-break-value.rs:38:12
    |
+LL |     while let Some(_) = Some(()) {
+   |     ---------------------------- you can't `break` with a value in a `while` loop
 LL |         if break () {
    |            ^^^^^^^^ can only break with a value inside `loop` or breakable block
    |
-help: instead, use `break` on its own without a value inside this `while` loop
+help: use `break` on its own without a value inside this `while` loop
    |
 LL |         if break {
    |            ^^^^^
@@ -51,10 +65,12 @@ LL |         if break {
 error[E0571]: `break` with value from a `while` loop
   --> $DIR/loop-break-value.rs:43:9
    |
+LL |     while let Some(_) = Some(()) {
+   |     ---------------------------- you can't `break` with a value in a `while` loop
 LL |         break None;
    |         ^^^^^^^^^^ can only break with a value inside `loop` or breakable block
    |
-help: instead, use `break` on its own without a value inside this `while` loop
+help: use `break` on its own without a value inside this `while` loop
    |
 LL |         break;
    |         ^^^^^
@@ -62,21 +78,26 @@ LL |         break;
 error[E0571]: `break` with value from a `while` loop
   --> $DIR/loop-break-value.rs:49:13
    |
+LL |     'while_let_loop: while let Some(_) = Some(()) {
+   |     --------------------------------------------- you can't `break` with a value in a `while` loop
+LL |         loop {
 LL |             break 'while_let_loop "nope";
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can only break with a value inside `loop` or breakable block
    |
-help: instead, use `break` on its own without a value inside this `while` loop
+help: use `break` on its own without a value inside this `while` loop
    |
-LL |             break;
-   |             ^^^^^
+LL |             break 'while_let_loop;
+   |             ^^^^^^^^^^^^^^^^^^^^^
 
 error[E0571]: `break` with value from a `for` loop
   --> $DIR/loop-break-value.rs:56:9
    |
+LL |     for _ in &[1,2,3] {
+   |     ----------------- you can't `break` with a value in a `for` loop
 LL |         break ();
    |         ^^^^^^^^ can only break with a value inside `loop` or breakable block
    |
-help: instead, use `break` on its own without a value inside this `for` loop
+help: use `break` on its own without a value inside this `for` loop
    |
 LL |         break;
    |         ^^^^^
@@ -84,10 +105,13 @@ LL |         break;
 error[E0571]: `break` with value from a `for` loop
   --> $DIR/loop-break-value.rs:57:9
    |
+LL |     for _ in &[1,2,3] {
+   |     ----------------- you can't `break` with a value in a `for` loop
+LL |         break ();
 LL |         break [()];
    |         ^^^^^^^^^^ can only break with a value inside `loop` or breakable block
    |
-help: instead, use `break` on its own without a value inside this `for` loop
+help: use `break` on its own without a value inside this `for` loop
    |
 LL |         break;
    |         ^^^^^
@@ -95,24 +119,16 @@ LL |         break;
 error[E0571]: `break` with value from a `for` loop
   --> $DIR/loop-break-value.rs:64:13
    |
+LL |     'for_loop: for _ in &[1,2,3] {
+   |     ---------------------------- you can't `break` with a value in a `for` loop
+...
 LL |             break 'for_loop Some(17);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^ can only break with a value inside `loop` or breakable block
    |
-help: instead, use `break` on its own without a value inside this `for` loop
-   |
-LL |             break;
-   |             ^^^^^
-
-error[E0571]: `break` with value from a `for` loop
-  --> $DIR/loop-break-value.rs:95:9
-   |
-LL |         break LOOP;
-   |         ^^^^^^^^^^ can only break with a value inside `loop` or breakable block
+help: use `break` on its own without a value inside this `for` loop
    |
-help: instead, use `break` on its own without a value inside this `for` loop
-   |
-LL |         break;
-   |         ^^^^^
+LL |             break 'for_loop;
+   |             ^^^^^^^^^^^^^^^
 
 error[E0308]: mismatched types
   --> $DIR/loop-break-value.rs:4:31
@@ -171,7 +187,7 @@ LL |         break;
    |         expected integer, found `()`
    |         help: give it a value of the expected type: `break value`
 
-error: aborting due to 18 previous errors; 1 warning emitted
+error: aborting due to 17 previous errors; 1 warning emitted
 
 Some errors have detailed explanations: E0308, E0425, E0571.
 For more information about an error, try `rustc --explain E0308`.
diff --git a/src/tools/clippy/clippy_lints/src/loops.rs b/src/tools/clippy/clippy_lints/src/loops.rs
index 1c5ab2874b048..bbcea387de2cb 100644
--- a/src/tools/clippy/clippy_lints/src/loops.rs
+++ b/src/tools/clippy/clippy_lints/src/loops.rs
@@ -533,7 +533,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
         }
 
         // check for never_loop
-        if let ExprKind::Loop(ref block, _, _) = expr.kind {
+        if let ExprKind::Loop(ref block, _, _, _) = expr.kind {
             match never_loop_block(block, expr.hir_id) {
                 NeverLoopResult::AlwaysBreak => span_lint(cx, NEVER_LOOP, expr.span, "this loop never actually loops"),
                 NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (),
@@ -543,7 +543,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
         // check for `loop { if let {} else break }` that could be `while let`
         // (also matches an explicit "match" instead of "if let")
         // (even if the "match" or "if let" is used for declaration)
-        if let ExprKind::Loop(ref block, _, LoopSource::Loop) = expr.kind {
+        if let ExprKind::Loop(ref block, _, LoopSource::Loop, _) = expr.kind {
             // also check for empty `loop {}` statements, skipping those in #[panic_handler]
             if block.stmts.is_empty() && block.expr.is_none() && !is_in_panic_handler(cx, expr) {
                 let msg = "empty `loop {}` wastes CPU cycles";
@@ -738,7 +738,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
         | ExprKind::Assign(ref e1, ref e2, _)
         | ExprKind::AssignOp(_, ref e1, ref e2)
         | ExprKind::Index(ref e1, ref e2) => never_loop_expr_all(&mut [&**e1, &**e2].iter().cloned(), main_loop_id),
-        ExprKind::Loop(ref b, _, _) => {
+        ExprKind::Loop(ref b, _, _, _) => {
             // Break can come from the inner loop so remove them.
             absorb_break(&never_loop_block(b, main_loop_id))
         },
@@ -1314,7 +1314,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> {
     fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
         match &expr.kind {
             // Non-determinism may occur ... don't give a lint
-            ExprKind::Loop(_, _, _) | ExprKind::Match(_, _, _) => self.should_lint = false,
+            ExprKind::Loop(..) | ExprKind::Match(..) => self.should_lint = false,
             ExprKind::Block(block, _) => self.visit_block(block),
             _ => {},
         }
diff --git a/src/tools/clippy/clippy_lints/src/needless_continue.rs b/src/tools/clippy/clippy_lints/src/needless_continue.rs
index a971d041ca661..603071a5f4ac4 100644
--- a/src/tools/clippy/clippy_lints/src/needless_continue.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_continue.rs
@@ -221,7 +221,7 @@ where
 {
     if let ast::ExprKind::While(_, loop_block, label)
     | ast::ExprKind::ForLoop(_, _, loop_block, label)
-    | ast::ExprKind::Loop(loop_block, label) = &expr.kind
+    | ast::ExprKind::Loop(loop_block, label, ..) = &expr.kind
     {
         func(loop_block, label.as_ref());
     }
diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs
index 24da056770c9d..d5b1767e945b9 100644
--- a/src/tools/clippy/clippy_lints/src/shadow.rs
+++ b/src/tools/clippy/clippy_lints/src/shadow.rs
@@ -325,7 +325,7 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut
         | ExprKind::Field(ref e, _)
         | ExprKind::AddrOf(_, _, ref e)
         | ExprKind::Box(ref e) => check_expr(cx, e, bindings),
-        ExprKind::Block(ref block, _) | ExprKind::Loop(ref block, _, _) => check_block(cx, block, bindings),
+        ExprKind::Block(ref block, _) | ExprKind::Loop(ref block, ..) => check_block(cx, block, bindings),
         // ExprKind::Call
         // ExprKind::MethodCall
         ExprKind::Array(v) | ExprKind::Tup(v) => {
diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs
index 43afa65de3e55..ca60d335262b3 100644
--- a/src/tools/clippy/clippy_lints/src/utils/author.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/author.rs
@@ -317,7 +317,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor {
                 self.current = cast_pat;
                 self.visit_expr(expr);
             },
-            ExprKind::Loop(ref body, _, desugaring) => {
+            ExprKind::Loop(ref body, _, desugaring, _) => {
                 let body_pat = self.next("body");
                 let des = loop_desugaring_name(desugaring);
                 let label_pat = self.next("label");
diff --git a/src/tools/clippy/clippy_lints/src/utils/higher.rs b/src/tools/clippy/clippy_lints/src/utils/higher.rs
index 9b3585865da32..42ab9a1e7d247 100644
--- a/src/tools/clippy/clippy_lints/src/utils/higher.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/higher.rs
@@ -142,7 +142,7 @@ pub fn for_loop<'tcx>(
         if let hir::ExprKind::Match(ref iterexpr, ref arms, hir::MatchSource::ForLoopDesugar) = expr.kind;
         if let hir::ExprKind::Call(_, ref iterargs) = iterexpr.kind;
         if iterargs.len() == 1 && arms.len() == 1 && arms[0].guard.is_none();
-        if let hir::ExprKind::Loop(ref block, _, _) = arms[0].body.kind;
+        if let hir::ExprKind::Loop(ref block, ..) = arms[0].body.kind;
         if block.expr.is_none();
         if let [ _, _, ref let_stmt, ref body ] = *block.stmts;
         if let hir::StmtKind::Local(ref local) = let_stmt.kind;
@@ -158,7 +158,7 @@ pub fn for_loop<'tcx>(
 /// `while cond { body }` becomes `(cond, body)`.
 pub fn while_loop<'tcx>(expr: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>)> {
     if_chain! {
-        if let hir::ExprKind::Loop(block, _, hir::LoopSource::While) = &expr.kind;
+        if let hir::ExprKind::Loop(block, _, hir::LoopSource::While, _) = &expr.kind;
         if let hir::Block { expr: Some(expr), .. } = &**block;
         if let hir::ExprKind::Match(cond, arms, hir::MatchSource::WhileDesugar) = &expr.kind;
         if let hir::ExprKind::DropTemps(cond) = &cond.kind;
diff --git a/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs b/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs
index 10120a8805db2..6066383f2ef42 100644
--- a/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs
@@ -123,7 +123,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
                 self.eq_expr(lc, rc) && self.eq_expr(&**lt, &**rt) && both(le, re, |l, r| self.eq_expr(l, r))
             },
             (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node,
-            (&ExprKind::Loop(ref lb, ref ll, ref lls), &ExprKind::Loop(ref rb, ref rl, ref rls)) => {
+            (&ExprKind::Loop(ref lb, ref ll, ref lls, _), &ExprKind::Loop(ref rb, ref rl, ref rls, _)) => {
                 lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name)
             },
             (&ExprKind::Match(ref le, ref la, ref ls), &ExprKind::Match(ref re, ref ra, ref rs)) => {
@@ -560,7 +560,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
             ExprKind::Lit(ref l) => {
                 l.node.hash(&mut self.s);
             },
-            ExprKind::Loop(ref b, ref i, _) => {
+            ExprKind::Loop(ref b, ref i, ..) => {
                 self.hash_block(b);
                 if let Some(i) = *i {
                     self.hash_name(i.ident.name);