about summary refs log tree commit diff
diff options
context:
space:
mode:
authorjumbatm <jumbatm@gmail.com>2020-01-31 22:24:57 +1000
committerjumbatm <jumbatm@gmail.com>2020-02-11 19:47:40 +1000
commit7c58ffe874142e1943e2674d7e75989938a7d21b (patch)
tree1a8486c1492d3038d9cc6e4032c69f2aa10a4d99
parentdc4242d9052a42cdf329c3a2430d02a3b3d415cb (diff)
downloadrust-7c58ffe874142e1943e2674d7e75989938a7d21b.tar.gz
rust-7c58ffe874142e1943e2674d7e75989938a7d21b.zip
Invert control in struct_lint_level.
Caller now passes in a `decorate` function, which is only run if the
lint is allowed.
-rw-r--r--src/librustc/infer/error_reporting/mod.rs2
-rw-r--r--src/librustc/lint.rs41
-rw-r--r--src/librustc/middle/stability.rs19
-rw-r--r--src/librustc/mir/interpret/error.rs81
-rw-r--r--src/librustc/traits/object_safety.rs56
-rw-r--r--src/librustc/traits/specialize/mod.rs114
-rw-r--r--src/librustc/ty/context.rs24
-rw-r--r--src/librustc_attr/builtin.rs4
-rw-r--r--src/librustc_errors/diagnostic.rs1
-rw-r--r--src/librustc_errors/diagnostic_builder.rs3
-rw-r--r--src/librustc_expand/base.rs2
-rw-r--r--src/librustc_lint/array_into_iter.rs7
-rw-r--r--src/librustc_lint/builtin.rs392
-rw-r--r--src/librustc_lint/context.rs198
-rw-r--r--src/librustc_lint/internal.rs81
-rw-r--r--src/librustc_lint/levels.rs86
-rw-r--r--src/librustc_lint/non_ascii_idents.rs8
-rw-r--r--src/librustc_lint/nonstandard_style.rs43
-rw-r--r--src/librustc_lint/redundant_semicolon.rs28
-rw-r--r--src/librustc_lint/types.rs146
-rw-r--r--src/librustc_lint/unused.rs110
-rw-r--r--src/librustc_mir/borrow_check/mod.rs32
-rw-r--r--src/librustc_mir/const_eval/eval_queries.rs9
-rw-r--r--src/librustc_mir/interpret/intern.rs11
-rw-r--r--src/librustc_mir/transform/check_unsafety.rs40
-rw-r--r--src/librustc_mir/transform/const_prop.rs67
-rw-r--r--src/librustc_mir_build/hair/pattern/_match.rs30
-rw-r--r--src/librustc_mir_build/hair/pattern/check_match.rs49
-rw-r--r--src/librustc_mir_build/hair/pattern/const_to_pat.rs23
-rw-r--r--src/librustc_mir_build/lints.rs20
-rw-r--r--src/librustc_parse/config.rs7
-rw-r--r--src/librustc_parse/lexer/unescape_error_reporting.rs2
-rw-r--r--src/librustc_parse/parser/attr.rs4
-rw-r--r--src/librustc_parse/parser/diagnostics.rs2
-rw-r--r--src/librustc_parse/parser/mod.rs2
-rw-r--r--src/librustc_parse/parser/pat.rs2
-rw-r--r--src/librustc_parse/validate_attr.rs4
-rw-r--r--src/librustc_passes/check_attr.rs40
-rw-r--r--src/librustc_passes/dead.rs9
-rw-r--r--src/librustc_passes/liveness.rs72
-rw-r--r--src/librustc_passes/stability.rs14
-rw-r--r--src/librustc_privacy/lib.rs29
-rw-r--r--src/librustc_resolve/lifetimes.rs59
-rw-r--r--src/librustc_typeck/astconv.rs49
-rw-r--r--src/librustc_typeck/check/cast.rs30
-rw-r--r--src/librustc_typeck/check/method/probe.rs47
-rw-r--r--src/librustc_typeck/check/mod.rs18
-rw-r--r--src/librustc_typeck/check_unused.rs56
-rw-r--r--src/librustc_typeck/collect.rs8
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs238
-rw-r--r--src/librustdoc/passes/mod.rs10
51 files changed, 1299 insertions, 1130 deletions
diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs
index 57a52a991ed..01390f2c719 100644
--- a/src/librustc/infer/error_reporting/mod.rs
+++ b/src/librustc/infer/error_reporting/mod.rs
@@ -1701,7 +1701,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         sub: Region<'tcx>,
     ) {
         self.construct_generic_bound_failure(region_scope_tree, span, origin, bound_kind, sub)
-            .emit()
+            .emit();
     }
 
     pub fn construct_generic_bound_failure(
diff --git a/src/librustc/lint.rs b/src/librustc/lint.rs
index 8c18b1368a9..e7291d7a4ab 100644
--- a/src/librustc/lint.rs
+++ b/src/librustc/lint.rs
@@ -11,6 +11,7 @@ use rustc_span::hygiene::MacroKind;
 use rustc_span::source_map::{DesugaringKind, ExpnKind, MultiSpan};
 use rustc_span::{Span, Symbol};
 
+
 /// How a lint level was set.
 #[derive(Clone, Copy, PartialEq, Eq, HashStable)]
 pub enum LintSource {
@@ -174,20 +175,37 @@ impl<'a> HashStable<StableHashingContext<'a>> for LintLevelMap {
     }
 }
 
-pub fn struct_lint_level<'a>(
-    sess: &'a Session,
+
+pub struct LintDiagnosticBuilder<'a>(DiagnosticBuilder<'a>);
+
+impl<'a> LintDiagnosticBuilder<'a> {
+    /// Return the inner DiagnosticBuilder, first setting the primary message to `msg`.
+    pub fn build(mut self, msg: &str) -> DiagnosticBuilder<'a> {
+        self.0.set_primary_message(msg);
+        self.0
+    }
+
+    /// Create a LintDiagnosticBuilder from some existing DiagnosticBuilder.
+    pub fn new(err: DiagnosticBuilder<'a>) -> LintDiagnosticBuilder<'a>{
+        LintDiagnosticBuilder(err)
+    }
+}
+
+pub fn struct_lint_level<'s>(
+    sess: &'s Session,
     lint: &'static Lint,
     level: Level,
     src: LintSource,
     span: Option<MultiSpan>,
-    msg: &str,
-) -> DiagnosticBuilder<'a> {
+    decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>)) {
+
+    // FIXME: Move the guts of this function into a fn which takes dyn Fn to reduce code bloat.
     let mut err = match (level, span) {
-        (Level::Allow, _) => return sess.diagnostic().struct_dummy(),
-        (Level::Warn, Some(span)) => sess.struct_span_warn(span, msg),
-        (Level::Warn, None) => sess.struct_warn(msg),
-        (Level::Deny, Some(span)) | (Level::Forbid, Some(span)) => sess.struct_span_err(span, msg),
-        (Level::Deny, None) | (Level::Forbid, None) => sess.struct_err(msg),
+        (Level::Allow, _) => { return; },
+        (Level::Warn, Some(span)) => sess.struct_span_warn(span, ""),
+        (Level::Warn, None) => sess.struct_warn(""),
+        (Level::Deny, Some(span)) | (Level::Forbid, Some(span)) => sess.struct_span_err(span, ""),
+        (Level::Deny, None) | (Level::Forbid, None) => sess.struct_err(""),
     };
 
     // Check for future incompatibility lints and issue a stronger warning.
@@ -209,7 +227,7 @@ pub fn struct_lint_level<'a>(
             err.cancel();
             // Don't continue further, since we don't want to have
             // `diag_span_note_once` called for a diagnostic that isn't emitted.
-            return err;
+            return;
         }
     }
 
@@ -299,7 +317,8 @@ pub fn struct_lint_level<'a>(
         err.note(&citation);
     }
 
-    return err;
+    // Finally, run `decorate`. This function is also responsible for emitting the diagnostic.
+    decorate(LintDiagnosticBuilder::new(err));
 }
 
 /// Returns whether `span` originates in a foreign crate's external macro.
diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs
index 7cbe77b9e82..dd30fb23c2e 100644
--- a/src/librustc/middle/stability.rs
+++ b/src/librustc/middle/stability.rs
@@ -222,11 +222,13 @@ fn late_report_deprecation(
         return;
     }
 
-    let mut diag = tcx.struct_span_lint_hir(lint, hir_id, span, message);
-    if let hir::Node::Expr(_) = tcx.hir().get(hir_id) {
-        deprecation_suggestion(&mut diag, suggestion, span);
-    }
-    diag.emit();
+    tcx.struct_span_lint_hir(lint, hir_id, span, |lint| {
+        let mut diag = lint.build(message);
+        if let hir::Node::Expr(_) = tcx.hir().get(hir_id) {
+            deprecation_suggestion(&mut diag, suggestion, span);
+        }
+        diag.emit()
+    });
     if hir_id == hir::DUMMY_HIR_ID {
         span_bug!(span, "emitted a {} lint with dummy HIR id: {:?}", lint.name, def_id);
     }
@@ -387,8 +389,11 @@ impl<'tcx> TyCtxt<'tcx> {
     /// Additionally, this function will also check if the item is deprecated. If so, and `id` is
     /// not `None`, a deprecated lint attached to `id` will be emitted.
     pub fn check_stability(self, def_id: DefId, id: Option<HirId>, span: Span) {
-        let soft_handler =
-            |lint, span, msg: &_| self.lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, msg);
+        let soft_handler = |lint, span, msg: &_| {
+            self.struct_span_lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, |lint| {
+                lint.build(msg).emit()
+            })
+        };
         match self.eval_stability(def_id, id, span) {
             EvalResult::Allow => {}
             EvalResult::Deny { feature, reason, issue, is_soft } => {
diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs
index 349dbd74ad1..bb84458cf7b 100644
--- a/src/librustc/mir/interpret/error.rs
+++ b/src/librustc/mir/interpret/error.rs
@@ -83,18 +83,15 @@ impl<'tcx> ConstEvalErr<'tcx> {
         &self,
         tcx: TyCtxtAt<'tcx>,
         message: &str,
-    ) -> Result<DiagnosticBuilder<'tcx>, ErrorHandled> {
-        self.struct_generic(tcx, message, None)
+        emit: impl FnOnce(DiagnosticBuilder<'_>),
+    ) -> Result<(), ErrorHandled> {
+        self.struct_generic(tcx, message, emit, None)
     }
 
     pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
-        let err = self.struct_error(tcx, message);
-        match err {
-            Ok(mut err) => {
-                err.emit();
-                ErrorHandled::Reported
-            }
-            Err(err) => err,
+        match self.struct_error(tcx, message, |mut e| e.emit()) {
+            Ok(_) => ErrorHandled::Reported,
+            Err(x) => x,
         }
     }
 
@@ -105,9 +102,11 @@ impl<'tcx> ConstEvalErr<'tcx> {
         lint_root: hir::HirId,
         span: Option<Span>,
     ) -> ErrorHandled {
-        let lint = self.struct_generic(tcx, message, Some(lint_root));
-        match lint {
-            Ok(mut lint) => {
+        match self.struct_generic(
+            tcx,
+            message,
+            |mut lint: DiagnosticBuilder<'_>| {
+                // Apply the span.
                 if let Some(span) = span {
                     let primary_spans = lint.span.primary_spans().to_vec();
                     // point at the actual error as the primary span
@@ -121,18 +120,26 @@ impl<'tcx> ConstEvalErr<'tcx> {
                     }
                 }
                 lint.emit();
+            }
+        , Some(lint_root)) {
+            Ok(_) => {
                 ErrorHandled::Reported
             }
             Err(err) => err,
         }
     }
 
+   /// Sets the message passed in via `message`, then adds the span labels for you, before applying
+   /// further modifications in `emit`. It's up to you to call emit(), stash(..), etc. within the
+   /// `emit` method. If you don't need to do any additional processing, just use
+   /// struct_generic.
     fn struct_generic(
         &self,
         tcx: TyCtxtAt<'tcx>,
         message: &str,
+        emit: impl FnOnce(DiagnosticBuilder<'_>),
         lint_root: Option<hir::HirId>,
-    ) -> Result<DiagnosticBuilder<'tcx>, ErrorHandled> {
+    ) -> Result<(), ErrorHandled> {
         let must_error = match self.error {
             InterpError::MachineStop(_) => bug!("CTFE does not stop"),
             err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
@@ -143,7 +150,22 @@ impl<'tcx> ConstEvalErr<'tcx> {
             _ => false,
         };
         trace!("reporting const eval failure at {:?}", self.span);
-        let mut err = if let (Some(lint_root), false) = (lint_root, must_error) {
+
+        let add_span_labels = |err: &mut DiagnosticBuilder<'_>| {
+            if !must_error {
+                err.span_label(self.span, self.error.to_string());
+            }
+            // Skip the last, which is just the environment of the constant.  The stacktrace
+            // is sometimes empty because we create "fake" eval contexts in CTFE to do work
+            // on constant values.
+            if self.stacktrace.len() > 0 {
+                for frame_info in &self.stacktrace[..self.stacktrace.len() - 1] {
+                    err.span_label(frame_info.call_site, frame_info.to_string());
+                }
+            }
+        };
+
+        if let (Some(lint_root), false) = (lint_root, must_error) {
             let hir_id = self
                 .stacktrace
                 .iter()
@@ -155,25 +177,22 @@ impl<'tcx> ConstEvalErr<'tcx> {
                 rustc_session::lint::builtin::CONST_ERR,
                 hir_id,
                 tcx.span,
-                message,
-            )
-        } else if must_error {
-            struct_error(tcx, &self.error.to_string())
+                |lint| {
+                    let mut err = lint.build(message);
+                    add_span_labels(&mut err);
+                    emit(err);
+                },
+            );
         } else {
-            struct_error(tcx, message)
+            let mut err = if must_error {
+                struct_error(tcx, &self.error.to_string())
+            } else {
+                struct_error(tcx, message)
+            };
+            add_span_labels(&mut err);
+            emit(err);
         };
-        if !must_error {
-            err.span_label(self.span, self.error.to_string());
-        }
-        // Skip the last, which is just the environment of the constant.  The stacktrace
-        // is sometimes empty because we create "fake" eval contexts in CTFE to do work
-        // on constant values.
-        if self.stacktrace.len() > 0 {
-            for frame_info in &self.stacktrace[..self.stacktrace.len() - 1] {
-                err.span_label(frame_info.call_site, frame_info.to_string());
-            }
-        }
-        Ok(err)
+        Ok(())
     }
 }
 
diff --git a/src/librustc/traits/object_safety.rs b/src/librustc/traits/object_safety.rs
index 3c886ce7f3e..0e6347897d2 100644
--- a/src/librustc/traits/object_safety.rs
+++ b/src/librustc/traits/object_safety.rs
@@ -227,37 +227,39 @@ fn object_safety_violations_for_trait(
             {
                 // Using `CRATE_NODE_ID` is wrong, but it's hard to get a more precise id.
                 // It's also hard to get a use site span, so we use the method definition span.
-                let mut err = tcx.struct_span_lint_hir(
+                tcx.struct_span_lint_hir(
                     WHERE_CLAUSES_OBJECT_SAFETY,
                     hir::CRATE_HIR_ID,
                     *span,
-                    &format!(
-                        "the trait `{}` cannot be made into an object",
-                        tcx.def_path_str(trait_def_id)
-                    ),
+                    |lint| {
+                        let mut err = lint.build(&format!(
+                            "the trait `{}` cannot be made into an object",
+                            tcx.def_path_str(trait_def_id)
+                        ));
+                        let node = tcx.hir().get_if_local(trait_def_id);
+                        let msg = if let Some(hir::Node::Item(item)) = node {
+                            err.span_label(item.ident.span, "this trait cannot be made into an object...");
+                            format!("...because {}", violation.error_msg())
+                        } else {
+                            format!(
+                                "the trait cannot be made into an object because {}",
+                                violation.error_msg()
+                            )
+                        };
+                        err.span_label(*span, &msg);
+                        match (node, violation.solution()) {
+                            (Some(_), Some((note, None))) => {
+                                err.help(&note);
+                            }
+                            (Some(_), Some((note, Some((sugg, span))))) => {
+                                err.span_suggestion(span, &note, sugg, Applicability::MachineApplicable);
+                            }
+                            // Only provide the help if its a local trait, otherwise it's not actionable.
+                            _ => {}
+                        }
+                        err.emit();
+                    },
                 );
-                let node = tcx.hir().get_if_local(trait_def_id);
-                let msg = if let Some(hir::Node::Item(item)) = node {
-                    err.span_label(item.ident.span, "this trait cannot be made into an object...");
-                    format!("...because {}", violation.error_msg())
-                } else {
-                    format!(
-                        "the trait cannot be made into an object because {}",
-                        violation.error_msg()
-                    )
-                };
-                err.span_label(*span, &msg);
-                match (node, violation.solution()) {
-                    (Some(_), Some((note, None))) => {
-                        err.help(&note);
-                    }
-                    (Some(_), Some((note, Some((sugg, span))))) => {
-                        err.span_suggestion(span, &note, sugg, Applicability::MachineApplicable);
-                    }
-                    // Only provide the help if its a local trait, otherwise it's not actionable.
-                    _ => {}
-                }
-                err.emit();
                 false
             } else {
                 true
diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs
index 071b5277dd9..33a78ae896b 100644
--- a/src/librustc/traits/specialize/mod.rs
+++ b/src/librustc/traits/specialize/mod.rs
@@ -22,6 +22,7 @@ use rustc_hir::def_id::DefId;
 use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK;
 use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS;
 use rustc_span::DUMMY_SP;
+use rustc::lint::LintDiagnosticBuilder;
 
 use super::util::impl_trait_ref_and_oblig;
 use super::{FulfillmentContext, SelectionContext};
@@ -317,22 +318,69 @@ pub(super) fn specialization_graph_provider(
             };
 
             if let Some(overlap) = overlap {
-                let msg = format!(
-                    "conflicting implementations of trait `{}`{}:{}",
-                    overlap.trait_desc,
-                    overlap
-                        .self_desc
-                        .clone()
-                        .map_or(String::new(), |ty| { format!(" for type `{}`", ty) }),
-                    match used_to_be_allowed {
-                        Some(FutureCompatOverlapErrorKind::Issue33140) => " (E0119)",
-                        _ => "",
-                    }
-                );
                 let impl_span =
                     tcx.sess.source_map().def_span(tcx.span_of_impl(impl_def_id).unwrap());
-                let mut err = match used_to_be_allowed {
-                    None => struct_span_err!(tcx.sess, impl_span, E0119, "{}", msg),
+
+                // Work to be done after we've built the DiagnosticBuilder. We have to define it
+                // now because the struct_lint methods don't return back the DiagnosticBuilder
+                // that's passed in.
+                let decorate = |err: LintDiagnosticBuilder<'_>| {
+                    let msg = format!(
+                        "conflicting implementations of trait `{}`{}:{}",
+                        overlap.trait_desc,
+                        overlap
+                            .self_desc
+                            .clone()
+                            .map_or(String::new(), |ty| { format!(" for type `{}`", ty) }),
+                        match used_to_be_allowed {
+                            Some(FutureCompatOverlapErrorKind::Issue33140) => " (E0119)",
+                            _ => "",
+                        }
+                    );
+                    let mut err = err.build(&msg);
+                    match tcx.span_of_impl(overlap.with_impl) {
+                        Ok(span) => {
+                            err.span_label(
+                                tcx.sess.source_map().def_span(span),
+                                "first implementation here".to_string(),
+                            );
+
+                            err.span_label(
+                                impl_span,
+                                format!(
+                                    "conflicting implementation{}",
+                                    overlap
+                                        .self_desc
+                                        .map_or(String::new(), |ty| format!(" for `{}`", ty))
+                                ),
+                            );
+                        }
+                        Err(cname) => {
+                            let msg = match to_pretty_impl_header(tcx, overlap.with_impl) {
+                                Some(s) => {
+                                    format!("conflicting implementation in crate `{}`:\n- {}", cname, s)
+                                }
+                                None => format!("conflicting implementation in crate `{}`", cname),
+                            };
+                            err.note(&msg);
+                        }
+                    }
+
+                    for cause in &overlap.intercrate_ambiguity_causes {
+                        cause.add_intercrate_ambiguity_hint(&mut err);
+                    }
+
+                    if overlap.involves_placeholder {
+                        coherence::add_placeholder_note(&mut err);
+                    }
+                    err.emit()
+                };
+
+                match used_to_be_allowed {
+                    None => {
+                        let err = struct_span_err!(tcx.sess, impl_span, E0119, "");
+                        decorate(LintDiagnosticBuilder::new(err));
+                    }
                     Some(kind) => {
                         let lint = match kind {
                             FutureCompatOverlapErrorKind::Issue33140 => {
@@ -344,47 +392,11 @@ pub(super) fn specialization_graph_provider(
                             lint,
                             tcx.hir().as_local_hir_id(impl_def_id).unwrap(),
                             impl_span,
-                            &msg,
+                            decorate,
                         )
                     }
                 };
 
-                match tcx.span_of_impl(overlap.with_impl) {
-                    Ok(span) => {
-                        err.span_label(
-                            tcx.sess.source_map().def_span(span),
-                            "first implementation here".to_string(),
-                        );
-                        err.span_label(
-                            impl_span,
-                            format!(
-                                "conflicting implementation{}",
-                                overlap
-                                    .self_desc
-                                    .map_or(String::new(), |ty| format!(" for `{}`", ty))
-                            ),
-                        );
-                    }
-                    Err(cname) => {
-                        let msg = match to_pretty_impl_header(tcx, overlap.with_impl) {
-                            Some(s) => {
-                                format!("conflicting implementation in crate `{}`:\n- {}", cname, s)
-                            }
-                            None => format!("conflicting implementation in crate `{}`", cname),
-                        };
-                        err.note(&msg);
-                    }
-                }
-
-                for cause in &overlap.intercrate_ambiguity_causes {
-                    cause.add_intercrate_ambiguity_hint(&mut err);
-                }
-
-                if overlap.involves_placeholder {
-                    coherence::add_placeholder_note(&mut err);
-                }
-
-                err.emit();
             }
         } else {
             let parent = tcx.impl_parent(impl_def_id).unwrap_or(trait_id);
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 92c5600362e..a885f9e9600 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -42,6 +42,7 @@ use crate::ty::{InferConst, ParamConst};
 use crate::ty::{List, TyKind, TyS};
 use crate::util::common::ErrorReported;
 use rustc_attr as attr;
+use rustc::lint::LintDiagnosticBuilder;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::profiling::SelfProfilerRef;
 use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap};
@@ -49,7 +50,6 @@ use rustc_data_structures::stable_hasher::{
     hash_stable_hashmap, HashStable, StableHasher, StableVec,
 };
 use rustc_data_structures::sync::{self, Lock, Lrc, WorkerLocal};
-use rustc_errors::DiagnosticBuilder;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, DefIndex, LOCAL_CRATE};
@@ -2551,16 +2551,6 @@ impl<'tcx> TyCtxt<'tcx> {
         iter.intern_with(|xs| self.intern_goals(xs))
     }
 
-    pub fn lint_hir(
-        self,
-        lint: &'static Lint,
-        hir_id: HirId,
-        span: impl Into<MultiSpan>,
-        msg: &str,
-    ) {
-        self.struct_span_lint_hir(lint, hir_id, span.into(), msg).emit()
-    }
-
     /// Walks upwards from `id` to find a node which might change lint levels with attributes.
     /// It stops at `bound` and just returns it if reached.
     pub fn maybe_lint_level_root_bounded(self, mut id: HirId, bound: HirId) -> HirId {
@@ -2604,20 +2594,20 @@ impl<'tcx> TyCtxt<'tcx> {
         lint: &'static Lint,
         hir_id: HirId,
         span: impl Into<MultiSpan>,
-        msg: &str,
-    ) -> DiagnosticBuilder<'tcx> {
+        decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>)
+    ) {
         let (level, src) = self.lint_level_at_node(lint, hir_id);
-        struct_lint_level(self.sess, lint, level, src, Some(span.into()), msg)
+        struct_lint_level(self.sess, lint, level, src, Some(span.into()), decorate);
     }
 
     pub fn struct_lint_node(
         self,
         lint: &'static Lint,
         id: HirId,
-        msg: &str,
-    ) -> DiagnosticBuilder<'tcx> {
+        decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>),
+    ) {
         let (level, src) = self.lint_level_at_node(lint, id);
-        struct_lint_level(self.sess, lint, level, src, None, msg)
+        struct_lint_level(self.sess, lint, level, src, None, decorate);
     }
 
     pub fn in_scope_traits(self, id: HirId) -> Option<&'tcx StableVec<TraitCandidate>> {
diff --git a/src/librustc_attr/builtin.rs b/src/librustc_attr/builtin.rs
index ab03297fffe..e5381a58dab 100644
--- a/src/librustc_attr/builtin.rs
+++ b/src/librustc_attr/builtin.rs
@@ -37,7 +37,7 @@ fn handle_errors(sess: &ParseSess, span: Span, error: AttrError) {
                 .span_label(span, format!("expected one of {}", expected.join(", ")))
                 .emit();
         }
-        AttrError::MissingSince => struct_span_err!(diag, span, E0542, "missing 'since'").emit(),
+        AttrError::MissingSince => { struct_span_err!(diag, span, E0542, "missing 'since'").emit(); },
         AttrError::MissingFeature => {
             struct_span_err!(diag, span, E0546, "missing 'feature'").emit();
         }
@@ -639,7 +639,7 @@ fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &ParseSess, features: &F
     let (cfg, feature, has_feature) = gated_cfg;
     if !has_feature(features) && !cfg_span.allows_unstable(*feature) {
         let explain = format!("`cfg({})` is experimental and subject to change", cfg);
-        feature_err(sess, *feature, cfg_span, &explain).emit()
+        feature_err(sess, *feature, cfg_span, &explain).emit();
     }
 }
 
diff --git a/src/librustc_errors/diagnostic.rs b/src/librustc_errors/diagnostic.rs
index 5286e8c19b4..189b5bd0f9e 100644
--- a/src/librustc_errors/diagnostic.rs
+++ b/src/librustc_errors/diagnostic.rs
@@ -194,6 +194,7 @@ impl Diagnostic {
         found_extra: &dyn fmt::Display,
     ) -> &mut Self {
         let expected_label = format!("expected {}", expected_label);
+
         let found_label = format!("found {}", found_label);
         let (found_padding, expected_padding) = if expected_label.len() > found_label.len() {
             (expected_label.len() - found_label.len(), 0)
diff --git a/src/librustc_errors/diagnostic_builder.rs b/src/librustc_errors/diagnostic_builder.rs
index 82bbae18a9c..84bfa07d267 100644
--- a/src/librustc_errors/diagnostic_builder.rs
+++ b/src/librustc_errors/diagnostic_builder.rs
@@ -106,7 +106,7 @@ impl<'a> DiagnosticBuilder<'a> {
     ///
     /// See `emit` and `delay_as_bug` for details.
     pub fn emit_unless(&mut self, delay: bool) {
-        if delay { self.delay_as_bug() } else { self.emit() }
+        if delay { self.delay_as_bug(); } else { self.emit(); }
     }
 
     /// Stashes diagnostic for possible later improvement in a different,
@@ -369,6 +369,7 @@ impl<'a> DiagnosticBuilder<'a> {
     /// Creates a new `DiagnosticBuilder` with an already constructed
     /// diagnostic.
     crate fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> DiagnosticBuilder<'a> {
+        debug!("Created new diagnostic");
         DiagnosticBuilder(Box::new(DiagnosticBuilderInner {
             handler,
             diagnostic,
diff --git a/src/librustc_expand/base.rs b/src/librustc_expand/base.rs
index e167089b93a..9e583d7e19d 100644
--- a/src/librustc_expand/base.rs
+++ b/src/librustc_expand/base.rs
@@ -1113,7 +1113,7 @@ pub fn expr_to_string(
     err_msg: &str,
 ) -> Option<(Symbol, ast::StrStyle)> {
     expr_to_spanned_string(cx, expr, err_msg)
-        .map_err(|err| err.map(|mut err| err.emit()))
+        .map_err(|err| err.map(|mut err| { err.emit(); }))
         .ok()
         .map(|(symbol, style, _)| (symbol, style))
 }
diff --git a/src/librustc_lint/array_into_iter.rs b/src/librustc_lint/array_into_iter.rs
index fb11b6771e9..a91d735622f 100644
--- a/src/librustc_lint/array_into_iter.rs
+++ b/src/librustc_lint/array_into_iter.rs
@@ -77,13 +77,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIntoIter {
                 // to an array or to a slice.
                 _ => bug!("array type coerced to something other than array or slice"),
             };
-            let msg = format!(
+            cx.struct_span_lint(ARRAY_INTO_ITER, *span, |lint| {
+                lint.build(&format!(
                 "this method call currently resolves to `<&{} as IntoIterator>::into_iter` (due \
                     to autoref coercions), but that might change in the future when \
                     `IntoIterator` impls for arrays are added.",
                 target,
-            );
-            cx.struct_span_lint(ARRAY_INTO_ITER, *span, &msg)
+                ))
                 .span_suggestion(
                     call.ident.span,
                     "use `.iter()` instead of `.into_iter()` to avoid ambiguity",
@@ -91,6 +91,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIntoIter {
                     Applicability::MachineApplicable,
                 )
                 .emit();
+            })
         }
     }
 }
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index ca66717ac5e..e84ef713b42 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -77,14 +77,16 @@ impl EarlyLintPass for WhileTrue {
                     if !lit.span.from_expansion() {
                         let msg = "denote infinite loops with `loop { ... }`";
                         let condition_span = cx.sess.source_map().def_span(e.span);
-                        cx.struct_span_lint(WHILE_TRUE, condition_span, msg)
-                            .span_suggestion_short(
+                        cx.struct_span_lint(WHILE_TRUE, condition_span, |lint| {
+                            lint.build(msg)
+                                .span_suggestion_short(
                                 condition_span,
                                 "use `loop`",
                                 "loop".to_owned(),
                                 Applicability::MachineApplicable,
                             )
                             .emit();
+                        })
                     }
                 }
             }
@@ -174,29 +176,31 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonShorthandFieldPatterns {
                     if cx.tcx.find_field_index(ident, &variant)
                         == Some(cx.tcx.field_index(fieldpat.hir_id, cx.tables))
                     {
-                        let mut err = cx.struct_span_lint(
+                        cx.struct_span_lint(
                             NON_SHORTHAND_FIELD_PATTERNS,
                             fieldpat.span,
-                            &format!("the `{}:` in this pattern is redundant", ident),
-                        );
-                        let binding = match binding_annot {
-                            hir::BindingAnnotation::Unannotated => None,
-                            hir::BindingAnnotation::Mutable => Some("mut"),
-                            hir::BindingAnnotation::Ref => Some("ref"),
-                            hir::BindingAnnotation::RefMut => Some("ref mut"),
-                        };
-                        let ident = if let Some(binding) = binding {
-                            format!("{} {}", binding, ident)
-                        } else {
-                            ident.to_string()
-                        };
-                        err.span_suggestion(
-                            fieldpat.span,
-                            "use shorthand field pattern",
-                            ident,
-                            Applicability::MachineApplicable,
+                            |lint| {
+                                let mut err = lint.build(&format!("the `{}:` in this pattern is redundant", ident));
+                                let binding = match binding_annot {
+                                    hir::BindingAnnotation::Unannotated => None,
+                                    hir::BindingAnnotation::Mutable => Some("mut"),
+                                    hir::BindingAnnotation::Ref => Some("ref"),
+                                    hir::BindingAnnotation::RefMut => Some("ref mut"),
+                                };
+                                let ident = if let Some(binding) = binding {
+                                    format!("{} {}", binding, ident)
+                                } else {
+                                    ident.to_string()
+                                };
+                                err.span_suggestion(
+                                    fieldpat.span,
+                                    "use shorthand field pattern",
+                                    ident,
+                                    Applicability::MachineApplicable,
+                                );
+                                err.emit();
+                            }
                         );
-                        err.emit();
                     }
                 }
             }
@@ -643,17 +647,19 @@ impl EarlyLintPass for AnonymousParameters {
                                 cx.struct_span_lint(
                                     ANONYMOUS_PARAMETERS,
                                     arg.pat.span,
-                                    "anonymous parameters are deprecated and will be \
-                                     removed in the next edition.",
-                                )
-                                .span_suggestion(
-                                    arg.pat.span,
-                                    "try naming the parameter or explicitly \
-                                    ignoring it",
-                                    format!("_: {}", ty_snip),
-                                    appl,
+                                    |lint| {
+                                    lint.build("anonymous parameters are deprecated and will be \
+                                     removed in the next edition.")
+                                        .span_suggestion(
+                                            arg.pat.span,
+                                            "try naming the parameter or explicitly \
+                                            ignoring it",
+                                            format!("_: {}", ty_snip),
+                                            appl,
+                                        )
+                                        .emit();
+                                    },
                                 )
-                                .emit();
                             }
                         }
                         _ => (),
@@ -687,14 +693,16 @@ fn lint_deprecated_attr(
     msg: &str,
     suggestion: Option<&str>,
 ) {
-    cx.struct_span_lint(DEPRECATED, attr.span, &msg)
-        .span_suggestion_short(
-            attr.span,
-            suggestion.unwrap_or("remove this attribute"),
-            String::new(),
-            Applicability::MachineApplicable,
-        )
-        .emit();
+    cx.struct_span_lint(DEPRECATED, attr.span, |lint| {
+        lint.build(msg)
+            .span_suggestion_short(
+                attr.span,
+                suggestion.unwrap_or("remove this attribute"),
+                String::new(),
+                Applicability::MachineApplicable,
+            )
+            .emit();
+    })
 }
 
 impl EarlyLintPass for DeprecatedAttr {
@@ -759,21 +767,20 @@ impl UnusedDocComment {
             let span = sugared_span.take().unwrap_or_else(|| attr.span);
 
             if attr.is_doc_comment() || attr.check_name(sym::doc) {
-                let mut err = cx.struct_span_lint(UNUSED_DOC_COMMENTS, span, "unused doc comment");
-
-                err.span_label(
-                    node_span,
-                    format!("rustdoc does not generate documentation for {}", node_kind),
-                );
-
-                if is_macro_expansion {
-                    err.help(
-                        "to document an item produced by a macro, \
-                              the macro must produce the documentation as part of its expansion",
+                cx.struct_span_lint(UNUSED_DOC_COMMENTS, span, |lint| {
+                    let mut err = lint.build("unused doc comment");
+                    err.span_label(
+                        node_span,
+                        format!("rustdoc does not generate documentation for {}", node_kind),
                     );
-                }
-
-                err.emit();
+                    if is_macro_expansion {
+                        err.help(
+                            "to document an item produced by a macro, \
+                                  the macro must produce the documentation as part of its expansion",
+                        );
+                    }
+                    err.emit();
+                });
             }
         }
     }
@@ -831,20 +838,22 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidNoMangleItems {
                         match param.kind {
                             GenericParamKind::Lifetime { .. } => {}
                             GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
-                                let mut err = cx.struct_span_lint(
+                                cx.struct_span_lint(
                                     NO_MANGLE_GENERIC_ITEMS,
                                     it.span,
-                                    "functions generic over types or consts must be mangled",
-                                );
-                                err.span_suggestion_short(
-                                    no_mangle_attr.span,
-                                    "remove this attribute",
-                                    String::new(),
-                                    // Use of `#[no_mangle]` suggests FFI intent; correct
-                                    // fix may be to monomorphize source by hand
-                                    Applicability::MaybeIncorrect,
+                                    |lint| {
+                                        lint.build("functions generic over types or consts must be mangled")
+                                            .span_suggestion_short(
+                                                no_mangle_attr.span,
+                                                "remove this attribute",
+                                                String::new(),
+                                                // Use of `#[no_mangle]` suggests FFI intent; correct
+                                                // fix may be to monomorphize source by hand
+                                                Applicability::MaybeIncorrect,
+                                            )
+                                            .emit();
+                                    },
                                 );
-                                err.emit();
                                 break;
                             }
                         }
@@ -856,25 +865,27 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidNoMangleItems {
                     // Const items do not refer to a particular location in memory, and therefore
                     // don't have anything to attach a symbol to
                     let msg = "const items should never be `#[no_mangle]`";
-                    let mut err = cx.struct_span_lint(NO_MANGLE_CONST_ITEMS, it.span, msg);
-
-                    // account for "pub const" (#45562)
-                    let start = cx
-                        .tcx
-                        .sess
-                        .source_map()
-                        .span_to_snippet(it.span)
-                        .map(|snippet| snippet.find("const").unwrap_or(0))
-                        .unwrap_or(0) as u32;
-                    // `const` is 5 chars
-                    let const_span = it.span.with_hi(BytePos(it.span.lo().0 + start + 5));
-                    err.span_suggestion(
-                        const_span,
-                        "try a static value",
-                        "pub static".to_owned(),
-                        Applicability::MachineApplicable,
-                    );
-                    err.emit();
+                    cx.struct_span_lint(NO_MANGLE_CONST_ITEMS, it.span, |lint| {
+                        let mut err = lint.build(msg);
+
+                        // account for "pub const" (#45562)
+                        let start = cx
+                            .tcx
+                            .sess
+                            .source_map()
+                            .span_to_snippet(it.span)
+                            .map(|snippet| snippet.find("const").unwrap_or(0))
+                            .unwrap_or(0) as u32;
+                        // `const` is 5 chars
+                        let const_span = it.span.with_hi(BytePos(it.span.lo().0 + start + 5));
+                        err.span_suggestion(
+                            const_span,
+                            "try a static value",
+                            "pub static".to_owned(),
+                            Applicability::MachineApplicable,
+                        );
+                        err.emit();
+                    });
                 }
             }
             _ => {}
@@ -984,28 +995,30 @@ impl UnreachablePub {
                     applicability = Applicability::MaybeIncorrect;
                 }
                 let def_span = cx.tcx.sess.source_map().def_span(span);
-                let mut err = cx.struct_span_lint(
+                cx.struct_span_lint(
                     UNREACHABLE_PUB,
                     def_span,
-                    &format!("unreachable `pub` {}", what),
-                );
-                let replacement = if cx.tcx.features().crate_visibility_modifier {
-                    "crate"
-                } else {
-                    "pub(crate)"
-                }
-                .to_owned();
+                    |lint| {
+                        let mut err = lint.build(&format!("unreachable `pub` {}", what));
+                        let replacement = if cx.tcx.features().crate_visibility_modifier {
+                            "crate"
+                        } else {
+                            "pub(crate)"
+                        }
+                        .to_owned();
 
-                err.span_suggestion(
-                    vis.span,
-                    "consider restricting its visibility",
-                    replacement,
-                    applicability,
+                        err.span_suggestion(
+                            vis.span,
+                            "consider restricting its visibility",
+                            replacement,
+                            applicability,
+                        );
+                        if exportable {
+                            err.help("or consider exporting it for use by other crates");
+                        }
+                        err.emit();
+                    }
                 );
-                if exportable {
-                    err.help("or consider exporting it for use by other crates");
-                }
-                err.emit();
             }
             _ => {}
         }
@@ -1120,22 +1133,24 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeAliasBounds {
                 .iter()
                 .map(|pred| pred.span())
                 .collect();
-            let mut err = cx.struct_span_lint(
+            cx.struct_span_lint(
                 TYPE_ALIAS_BOUNDS,
                 spans,
-                "where clauses are not enforced in type aliases",
-            );
-            err.span_suggestion(
-                type_alias_generics.where_clause.span_for_predicates_or_empty_place(),
-                "the clause will not be checked when the type alias is used, and should be removed",
-                String::new(),
-                Applicability::MachineApplicable,
+                |lint| {
+                    let mut err = lint.build("where clauses are not enforced in type aliases");
+                    err.span_suggestion(
+                        type_alias_generics.where_clause.span_for_predicates_or_empty_place(),
+                        "the clause will not be checked when the type alias is used, and should be removed",
+                        String::new(),
+                        Applicability::MachineApplicable,
+                    );
+                    if !suggested_changing_assoc_types {
+                        TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
+                        suggested_changing_assoc_types = true;
+                    }
+                    err.emit();
+                },
             );
-            if !suggested_changing_assoc_types {
-                TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
-                suggested_changing_assoc_types = true;
-            }
-            err.emit();
         }
         // The parameters must not have bounds
         for param in type_alias_generics.params.iter() {
@@ -1148,19 +1163,21 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeAliasBounds {
                 })
                 .collect();
             if !spans.is_empty() {
-                let mut err = cx.struct_span_lint(
+                cx.struct_span_lint(
                     TYPE_ALIAS_BOUNDS,
                     spans,
-                    "bounds on generic parameters are not enforced in type aliases",
+                    |lint| {
+                        let mut err = lint.build("bounds on generic parameters are not enforced in type aliases");
+                        let msg = "the bound will not be checked when the type alias is used, \
+                                   and should be removed";
+                        err.multipart_suggestion(&msg, suggestion, Applicability::MachineApplicable);
+                        if !suggested_changing_assoc_types {
+                            TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
+                            suggested_changing_assoc_types = true;
+                        }
+                        err.emit();
+                    },
                 );
-                let msg = "the bound will not be checked when the type alias is used, \
-                           and should be removed";
-                err.multipart_suggestion(&msg, suggestion, Applicability::MachineApplicable);
-                if !suggested_changing_assoc_types {
-                    TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
-                    suggested_changing_assoc_types = true;
-                }
-                err.emit();
             }
         }
     }
@@ -1322,23 +1339,27 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
                     Some(start) => format!("&({}..={})", expr_to_string(&start), end),
                     None => format!("&(..={})", end),
                 };
-                let mut err = cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, msg);
-                err.span_suggestion(
-                    pat.span,
-                    suggestion,
-                    replace,
-                    Applicability::MachineApplicable,
-                );
-                err.emit();
+                cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, |lint| {
+                    lint.build(msg)
+                        .span_suggestion(
+                            pat.span,
+                            suggestion,
+                            replace,
+                            Applicability::MachineApplicable,
+                        )
+                        .emit();
+                });
             } else {
-                let mut err = cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, join, msg);
-                err.span_suggestion_short(
-                    join,
-                    suggestion,
-                    "..=".to_owned(),
-                    Applicability::MachineApplicable,
-                );
-                err.emit();
+                cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, join, |lint| {
+                    lint.build(msg)
+                        .span_suggestion_short(
+                            join,
+                            suggestion,
+                            "..=".to_owned(),
+                            Applicability::MachineApplicable
+                        )
+                        .emit();
+                });
             };
         }
     }
@@ -1384,7 +1405,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnnameableTestItems {
         }
 
         if let Some(attr) = attr::find_by_name(&it.attrs, sym::rustc_test_marker) {
-            cx.struct_span_lint(UNNAMEABLE_TEST_ITEMS, attr.span, "cannot test inner items").emit();
+            cx.struct_span_lint(UNNAMEABLE_TEST_ITEMS, attr.span, |lint| lint.build("cannot test inner items").emit());
         }
     }
 
@@ -1465,18 +1486,20 @@ impl KeywordIdents {
             return;
         }
 
-        let mut lint = cx.struct_span_lint(
+        cx.struct_span_lint(
             KEYWORD_IDENTS,
             ident.span,
-            &format!("`{}` is a keyword in the {} edition", ident, next_edition),
-        );
-        lint.span_suggestion(
-            ident.span,
-            "you can use a raw identifier to stay compatible",
-            format!("r#{}", ident),
-            Applicability::MachineApplicable,
+            |lint| {
+                lint.build(&format!("`{}` is a keyword in the {} edition", ident, next_edition))
+                    .span_suggestion(
+                        ident.span,
+                        "you can use a raw identifier to stay compatible",
+                        format!("r#{}", ident),
+                        Applicability::MachineApplicable,
+                    )
+                    .emit()
+            },
         );
-        lint.emit()
     }
 }
 
@@ -1780,17 +1803,19 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExplicitOutlivesRequirements {
             }
 
             if !lint_spans.is_empty() {
-                let mut err = cx.struct_span_lint(
+                cx.struct_span_lint(
                     EXPLICIT_OUTLIVES_REQUIREMENTS,
                     lint_spans.clone(),
-                    "outlives requirements can be inferred",
-                );
-                err.multipart_suggestion(
-                    if bound_count == 1 { "remove this bound" } else { "remove these bounds" },
-                    lint_spans.into_iter().map(|span| (span, "".to_owned())).collect::<Vec<_>>(),
-                    Applicability::MachineApplicable,
+                    |lint| {
+                        lint.build("outlives requirements can be inferred")
+                            .multipart_suggestion(
+                                if bound_count == 1 { "remove this bound" } else { "remove these bounds" },
+                                lint_spans.into_iter().map(|span| (span, "".to_owned())).collect::<Vec<_>>(),
+                                Applicability::MachineApplicable,
+                            )
+                            .emit();
+                    },
                 );
-                err.emit();
             }
         }
     }
@@ -1820,12 +1845,11 @@ impl EarlyLintPass for IncompleteFeatures {
                 cx.struct_span_lint(
                     INCOMPLETE_FEATURES,
                     span,
-                    &format!(
+                    |lint| lint.build(&format!(
                         "the feature `{}` is incomplete and may cause the compiler to crash",
                         name,
-                    ),
+                    )).emit(),
                 )
-                .emit();
             });
     }
 }
@@ -2015,30 +2039,32 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
             // We are extremely conservative with what we warn about.
             let conjured_ty = cx.tables.expr_ty(expr);
             if let Some((msg, span)) = ty_find_init_error(cx.tcx, conjured_ty, init) {
-                let mut err = cx.struct_span_lint(
+                cx.struct_span_lint(
                     INVALID_VALUE,
                     expr.span,
-                    &format!(
-                        "the type `{}` does not permit {}",
-                        conjured_ty,
-                        match init {
-                            InitKind::Zeroed => "zero-initialization",
-                            InitKind::Uninit => "being left uninitialized",
-                        },
-                    ),
-                );
-                err.span_label(expr.span, "this code causes undefined behavior when executed");
-                err.span_label(
-                    expr.span,
-                    "help: use `MaybeUninit<T>` instead, \
-                    and only call `assume_init` after initialization is done",
+                    |lint| {
+                        let mut err = lint.build(&format!(
+                            "the type `{}` does not permit {}",
+                            conjured_ty,
+                            match init {
+                                InitKind::Zeroed => "zero-initialization",
+                                InitKind::Uninit => "being left uninitialized",
+                            },
+                        ));
+                        err.span_label(expr.span, "this code causes undefined behavior when executed");
+                        err.span_label(
+                            expr.span,
+                            "help: use `MaybeUninit<T>` instead, \
+                            and only call `assume_init` after initialization is done",
+                        );
+                        if let Some(span) = span {
+                            err.span_note(span, &msg);
+                        } else {
+                            err.note(&msg);
+                        }
+                        err.emit();
+                    },
                 );
-                if let Some(span) = span {
-                    err.span_note(span, &msg);
-                } else {
-                    err.note(&msg);
-                }
-                err.emit();
             }
         }
     }
diff --git a/src/librustc_lint/context.rs b/src/librustc_lint/context.rs
index 3b8cce5635d..7b085f6c2b3 100644
--- a/src/librustc_lint/context.rs
+++ b/src/librustc_lint/context.rs
@@ -26,7 +26,8 @@ use rustc::ty::layout::{LayoutError, LayoutOf, TyLayout};
 use rustc::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync;
-use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_errors::{struct_span_err, Applicability};
+use rustc::lint::LintDiagnosticBuilder;
 use rustc_hir as hir;
 use rustc_hir::def_id::{CrateNum, DefId};
 use rustc_session::lint::BuiltinLintDiagnostics;
@@ -474,7 +475,7 @@ pub trait LintContext: Sized {
     fn lints(&self) -> &LintStore;
 
     fn lookup_and_emit<S: Into<MultiSpan>>(&self, lint: &'static Lint, span: Option<S>, msg: &str) {
-        self.lookup(lint, span, msg).emit();
+        self.lookup(lint, span, |lint| lint.build(msg).emit());
     }
 
     fn lookup_and_emit_with_diagnostics<S: Into<MultiSpan>>(
@@ -484,95 +485,96 @@ pub trait LintContext: Sized {
         msg: &str,
         diagnostic: BuiltinLintDiagnostics,
     ) {
-        let mut db = self.lookup(lint, span, msg);
-
-        let sess = self.sess();
-        match diagnostic {
-            BuiltinLintDiagnostics::Normal => (),
-            BuiltinLintDiagnostics::BareTraitObject(span, is_global) => {
-                let (sugg, app) = match sess.source_map().span_to_snippet(span) {
-                    Ok(s) if is_global => {
-                        (format!("dyn ({})", s), Applicability::MachineApplicable)
-                    }
-                    Ok(s) => (format!("dyn {}", s), Applicability::MachineApplicable),
-                    Err(_) => ("dyn <type>".to_string(), Applicability::HasPlaceholders),
-                };
-                db.span_suggestion(span, "use `dyn`", sugg, app);
-            }
-            BuiltinLintDiagnostics::AbsPathWithModule(span) => {
-                let (sugg, app) = match sess.source_map().span_to_snippet(span) {
-                    Ok(ref s) => {
-                        // FIXME(Manishearth) ideally the emitting code
-                        // can tell us whether or not this is global
-                        let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" };
-
-                        (format!("crate{}{}", opt_colon, s), Applicability::MachineApplicable)
-                    }
-                    Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders),
-                };
-                db.span_suggestion(span, "use `crate`", sugg, app);
-            }
-            BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(span) => {
-                db.span_label(
-                    span,
-                    "names from parent modules are not accessible without an explicit import",
-                );
-            }
-            BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => {
-                db.span_note(span_def, "the macro is defined here");
-            }
-            BuiltinLintDiagnostics::ElidedLifetimesInPaths(
-                n,
-                path_span,
-                incl_angl_brckt,
-                insertion_span,
-                anon_lts,
-            ) => {
-                add_elided_lifetime_in_path_suggestion(
-                    sess,
-                    &mut db,
+        self.lookup(lint, span, |lint| {
+            let mut db = lint.build(msg);
+            let sess = self.sess();
+            match diagnostic {
+                BuiltinLintDiagnostics::Normal => (),
+                BuiltinLintDiagnostics::BareTraitObject(span, is_global) => {
+                    let (sugg, app) = match sess.source_map().span_to_snippet(span) {
+                        Ok(s) if is_global => {
+                            (format!("dyn ({})", s), Applicability::MachineApplicable)
+                        }
+                        Ok(s) => (format!("dyn {}", s), Applicability::MachineApplicable),
+                        Err(_) => ("dyn <type>".to_string(), Applicability::HasPlaceholders),
+                    };
+                    db.span_suggestion(span, "use `dyn`", sugg, app);
+                }
+                BuiltinLintDiagnostics::AbsPathWithModule(span) => {
+                    let (sugg, app) = match sess.source_map().span_to_snippet(span) {
+                        Ok(ref s) => {
+                            // FIXME(Manishearth) ideally the emitting code
+                            // can tell us whether or not this is global
+                            let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" };
+
+                            (format!("crate{}{}", opt_colon, s), Applicability::MachineApplicable)
+                        }
+                        Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders),
+                    };
+                    db.span_suggestion(span, "use `crate`", sugg, app);
+                }
+                BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(span) => {
+                    db.span_label(
+                        span,
+                        "names from parent modules are not accessible without an explicit import",
+                    );
+                }
+                BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => {
+                    db.span_note(span_def, "the macro is defined here");
+                }
+                BuiltinLintDiagnostics::ElidedLifetimesInPaths(
                     n,
                     path_span,
                     incl_angl_brckt,
                     insertion_span,
                     anon_lts,
-                );
-            }
-            BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => {
-                db.span_suggestion(span, &note, sugg, Applicability::MaybeIncorrect);
-            }
-            BuiltinLintDiagnostics::UnusedImports(message, replaces) => {
-                if !replaces.is_empty() {
-                    db.tool_only_multipart_suggestion(
-                        &message,
-                        replaces,
-                        Applicability::MachineApplicable,
+                ) => {
+                    add_elided_lifetime_in_path_suggestion(
+                        sess,
+                        &mut db,
+                        n,
+                        path_span,
+                        incl_angl_brckt,
+                        insertion_span,
+                        anon_lts,
                     );
                 }
-            }
-            BuiltinLintDiagnostics::RedundantImport(spans, ident) => {
-                for (span, is_imported) in spans {
-                    let introduced = if is_imported { "imported" } else { "defined" };
-                    db.span_label(
-                        span,
-                        format!("the item `{}` is already {} here", ident, introduced),
-                    );
+                BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => {
+                    db.span_suggestion(span, &note, sugg, Applicability::MaybeIncorrect);
+                }
+                BuiltinLintDiagnostics::UnusedImports(message, replaces) => {
+                    if !replaces.is_empty() {
+                        db.tool_only_multipart_suggestion(
+                            &message,
+                            replaces,
+                            Applicability::MachineApplicable,
+                        );
+                    }
+                }
+                BuiltinLintDiagnostics::RedundantImport(spans, ident) => {
+                    for (span, is_imported) in spans {
+                        let introduced = if is_imported { "imported" } else { "defined" };
+                        db.span_label(
+                            span,
+                            format!("the item `{}` is already {} here", ident, introduced),
+                        );
+                    }
+                }
+                BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span) => {
+                    stability::deprecation_suggestion(&mut db, suggestion, span)
                 }
             }
-            BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span) => {
-                stability::deprecation_suggestion(&mut db, suggestion, span)
-            }
-        }
 
-        db.emit();
+            db.emit();
+        });
     }
 
     fn lookup<S: Into<MultiSpan>>(
         &self,
         lint: &'static Lint,
         span: Option<S>,
-        msg: &str,
-    ) -> DiagnosticBuilder<'_>;
+        decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>),
+    );
 
     /// Emit a lint at the appropriate level, for a particular span.
     fn span_lint<S: Into<MultiSpan>>(&self, lint: &'static Lint, span: S, msg: &str) {
@@ -583,9 +585,9 @@ pub trait LintContext: Sized {
         &self,
         lint: &'static Lint,
         span: S,
-        msg: &str,
-    ) -> DiagnosticBuilder<'_> {
-        self.lookup(lint, Some(span), msg)
+        decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>)
+    ) {
+        self.lookup(lint, Some(span), decorate);
     }
 
     /// Emit a lint and note at the appropriate level, for a particular span.
@@ -597,21 +599,25 @@ pub trait LintContext: Sized {
         note_span: Span,
         note: &str,
     ) {
-        let mut err = self.lookup(lint, Some(span), msg);
-        if note_span == span {
-            err.note(note);
-        } else {
-            err.span_note(note_span, note);
-        }
-        err.emit();
+        self.lookup(lint, Some(span), |lint| {
+            let mut err = lint.build(msg);
+            if note_span == span {
+                err.note(note);
+            } else {
+                err.span_note(note_span, note);
+            }
+            err.emit();
+        });
     }
 
     /// Emit a lint and help at the appropriate level, for a particular span.
     fn span_lint_help(&self, lint: &'static Lint, span: Span, msg: &str, help: &str) {
-        let mut err = self.lookup(lint, Some(span), msg);
-        self.span_lint(lint, span, msg);
-        err.span_help(span, help);
-        err.emit();
+        self.lookup(lint, Some(span), |err| {
+            let mut err = err.build(msg);
+            self.span_lint(lint, span, msg);
+            err.span_help(span, help);
+            err.emit();
+        });
     }
 
     /// Emit a lint at the appropriate level, with no associated span.
@@ -654,13 +660,13 @@ impl LintContext for LateContext<'_, '_> {
         &self,
         lint: &'static Lint,
         span: Option<S>,
-        msg: &str,
-    ) -> DiagnosticBuilder<'_> {
+        decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>),
+    ) {
         let hir_id = self.last_node_with_lint_attrs;
 
         match span {
-            Some(s) => self.tcx.struct_span_lint_hir(lint, hir_id, s, msg),
-            None => self.tcx.struct_lint_node(lint, hir_id, msg),
+            Some(s) => self.tcx.struct_span_lint_hir(lint, hir_id, s, decorate),
+            None => self.tcx.struct_lint_node(lint, hir_id, decorate),
         }
     }
 }
@@ -681,9 +687,9 @@ impl LintContext for EarlyContext<'_> {
         &self,
         lint: &'static Lint,
         span: Option<S>,
-        msg: &str,
-    ) -> DiagnosticBuilder<'_> {
-        self.builder.struct_lint(lint, span.map(|s| s.into()), msg)
+        decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>),
+    ) {
+        self.builder.struct_lint(lint, span.map(|s| s.into()), decorate)
     }
 }
 
diff --git a/src/librustc_lint/internal.rs b/src/librustc_lint/internal.rs
index 8480c85075d..19778a044a4 100644
--- a/src/librustc_lint/internal.rs
+++ b/src/librustc_lint/internal.rs
@@ -37,16 +37,19 @@ impl_lint_pass!(DefaultHashTypes => [DEFAULT_HASH_TYPES]);
 impl EarlyLintPass for DefaultHashTypes {
     fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) {
         if let Some(replace) = self.map.get(&ident.name) {
+            // FIXME: We can avoid a copy here. Would require us to take String instead of &str.
             let msg = format!("Prefer {} over {}, it has better performance", replace, ident);
-            let mut db = cx.struct_span_lint(DEFAULT_HASH_TYPES, ident.span, &msg);
-            db.span_suggestion(
-                ident.span,
-                "use",
-                replace.to_string(),
-                Applicability::MaybeIncorrect, // FxHashMap, ... needs another import
-            );
-            db.note(&format!("a `use rustc_data_structures::fx::{}` may be necessary", replace))
-                .emit();
+            cx.struct_span_lint(DEFAULT_HASH_TYPES, ident.span, |lint| {
+                lint.build(&msg)
+                    .span_suggestion(
+                        ident.span,
+                        "use",
+                        replace.to_string(),
+                        Applicability::MaybeIncorrect, // FxHashMap, ... needs another import
+                    )
+                    .note(&format!("a `use rustc_data_structures::fx::{}` may be necessary", replace))
+                    .emit();
+            });
         }
     }
 }
@@ -85,7 +88,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind {
         if let Some(last) = segments.last() {
             let span = path.span.with_hi(last.ident.span.hi());
             if lint_ty_kind_usage(cx, last) {
-                cx.struct_span_lint(USAGE_OF_TY_TYKIND, span, "usage of `ty::TyKind::<kind>`")
+                cx.struct_span_lint(USAGE_OF_TY_TYKIND, span, |lint| {
+                    lint.build("usage of `ty::TyKind::<kind>`")
                     .span_suggestion(
                         span,
                         "try using ty::<kind> directly",
@@ -93,6 +97,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind {
                         Applicability::MaybeIncorrect, // ty maybe needs an import
                     )
                     .emit();
+                })
             }
         }
     }
@@ -106,10 +111,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind {
                             cx.struct_span_lint(
                                 USAGE_OF_TY_TYKIND,
                                 path.span,
-                                "usage of `ty::TyKind`",
+                                |lint| {
+                                    lint.build("usage of `ty::TyKind`")
+                                        .help("try using `Ty` instead")
+                                        .emit();
+                                },
                             )
-                            .help("try using `Ty` instead")
-                            .emit();
                         } else {
                             if ty.span.from_expansion() {
                                 return;
@@ -119,16 +126,18 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind {
                                     cx.struct_span_lint(
                                         USAGE_OF_QUALIFIED_TY,
                                         path.span,
-                                        &format!("usage of qualified `ty::{}`", t),
+                                        |lint| {
+                                            lint.build(&format!("usage of qualified `ty::{}`", t))
+                                                .span_suggestion(
+                                                    path.span,
+                                                    "try using it unqualified",
+                                                    t,
+                                                    // The import probably needs to be changed
+                                                    Applicability::MaybeIncorrect,
+                                                )
+                                                .emit();
+                                        },
                                     )
-                                    .span_suggestion(
-                                        path.span,
-                                        "try using it unqualified",
-                                        t,
-                                        // The import probably needs to be changed
-                                        Applicability::MaybeIncorrect,
-                                    )
-                                    .emit();
                                 }
                             }
                         }
@@ -145,16 +154,18 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind {
                     cx.struct_span_lint(
                         TY_PASS_BY_REFERENCE,
                         ty.span,
-                        &format!("passing `{}` by reference", t),
-                    )
-                    .span_suggestion(
-                        ty.span,
-                        "try passing by value",
-                        t,
-                        // Changing type of function argument
-                        Applicability::MaybeIncorrect,
+                        |lint| {
+                            lint.build(&format!("passing `{}` by reference", t))
+                            .span_suggestion(
+                                ty.span,
+                                "try passing by value",
+                                t,
+                                // Changing type of function argument
+                                Applicability::MaybeIncorrect,
+                            )
+                            .emit();
+                        },
                     )
-                    .emit();
                 }
             }
             _ => {}
@@ -234,10 +245,12 @@ impl EarlyLintPass for LintPassImpl {
                         cx.struct_span_lint(
                             LINT_PASS_IMPL_WITHOUT_MACRO,
                             lint_pass.path.span,
-                            "implementing `LintPass` by hand",
+                            |lint| {
+                                lint.build("implementing `LintPass` by hand")
+                                    .help("try using `declare_lint_pass!` or `impl_lint_pass!` instead")
+                                    .emit();
+                            },
                         )
-                        .help("try using `declare_lint_pass!` or `impl_lint_pass!` instead")
-                        .emit();
                     }
                 }
             }
diff --git a/src/librustc_lint/levels.rs b/src/librustc_lint/levels.rs
index 4f30d2b2226..6dd899d1461 100644
--- a/src/librustc_lint/levels.rs
+++ b/src/librustc_lint/levels.rs
@@ -6,7 +6,8 @@ use rustc::ty::query::Providers;
 use rustc::ty::TyCtxt;
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_errors::{struct_span_err, Applicability};
+use rustc::lint::LintDiagnosticBuilder;
 use rustc_hir as hir;
 use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
 use rustc_hir::{intravisit, HirId};
@@ -39,8 +40,8 @@ fn lint_levels(tcx: TyCtxt<'_>, cnum: CrateNum) -> &LintLevelMap {
     tcx.arena.alloc(builder.levels.build_map())
 }
 
-pub struct LintLevelsBuilder<'a> {
-    sess: &'a Session,
+pub struct LintLevelsBuilder<'s> {
+    sess: &'s Session,
     sets: LintLevelSets,
     id_to_set: FxHashMap<HirId, u32>,
     cur: u32,
@@ -52,8 +53,8 @@ pub struct BuilderPush {
     pub changed: bool,
 }
 
-impl<'a> LintLevelsBuilder<'a> {
-    pub fn new(sess: &'a Session, warn_about_weird_lints: bool, store: &LintStore) -> Self {
+impl<'s> LintLevelsBuilder<'s> {
+    pub fn new(sess: &'s Session, warn_about_weird_lints: bool, store: &LintStore) -> Self {
         let mut builder = LintLevelsBuilder {
             sess,
             sets: LintLevelSets::new(),
@@ -245,15 +246,17 @@ impl<'a> LintLevelsBuilder<'a> {
                                     lvl,
                                     src,
                                     Some(li.span().into()),
-                                    &msg,
-                                )
-                                .span_suggestion(
-                                    li.span(),
-                                    "change it to",
-                                    new_lint_name.to_string(),
-                                    Applicability::MachineApplicable,
-                                )
-                                .emit();
+                                    |lint| {
+                                        lint.build(&msg)
+                                        .span_suggestion(
+                                            li.span(),
+                                            "change it to",
+                                            new_lint_name.to_string(),
+                                            Applicability::MachineApplicable,
+                                        )
+                                        .emit();
+                                    },
+                                );
 
                                 let src = LintSource::Node(
                                     Symbol::intern(&new_lint_name),
@@ -279,48 +282,51 @@ impl<'a> LintLevelsBuilder<'a> {
                         let lint = builtin::RENAMED_AND_REMOVED_LINTS;
                         let (level, src) =
                             self.sets.get_lint_level(lint, self.cur, Some(&specs), &sess);
-                        let mut err = struct_lint_level(
+                        struct_lint_level(
                             self.sess,
                             lint,
                             level,
                             src,
                             Some(li.span().into()),
-                            &msg,
+                            |lint| {
+                                let mut err = lint.build(&msg);
+                                if let Some(new_name) = renamed {
+                                    err.span_suggestion(
+                                        li.span(),
+                                        "use the new name",
+                                        new_name,
+                                        Applicability::MachineApplicable,
+                                    );
+                                }
+                                err.emit();
+                            },
                         );
-                        if let Some(new_name) = renamed {
-                            err.span_suggestion(
-                                li.span(),
-                                "use the new name",
-                                new_name,
-                                Applicability::MachineApplicable,
-                            );
-                        }
-                        err.emit();
                     }
                     CheckLintNameResult::NoLint(suggestion) => {
                         let lint = builtin::UNKNOWN_LINTS;
                         let (level, src) =
                             self.sets.get_lint_level(lint, self.cur, Some(&specs), self.sess);
                         let msg = format!("unknown lint: `{}`", name);
-                        let mut db = struct_lint_level(
+                        struct_lint_level(
                             self.sess,
                             lint,
                             level,
                             src,
                             Some(li.span().into()),
-                            &msg,
+                            |lint| {
+                                let mut db = lint.build(&msg);
+                                if let Some(suggestion) = suggestion {
+                                    db.span_suggestion(
+                                        li.span(),
+                                        "did you mean",
+                                        suggestion.to_string(),
+                                        Applicability::MachineApplicable,
+                                    );
+                                }
+                                db.emit();
+                            },
                         );
 
-                        if let Some(suggestion) = suggestion {
-                            db.span_suggestion(
-                                li.span(),
-                                "did you mean",
-                                suggestion.to_string(),
-                                Applicability::MachineApplicable,
-                            );
-                        }
-
-                        db.emit();
                     }
                 }
             }
@@ -390,10 +396,10 @@ impl<'a> LintLevelsBuilder<'a> {
         &self,
         lint: &'static Lint,
         span: Option<MultiSpan>,
-        msg: &str,
-    ) -> DiagnosticBuilder<'a> {
+        decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>),
+    ) {
         let (level, src) = self.sets.get_lint_level(lint, self.cur, None, self.sess);
-        struct_lint_level(self.sess, lint, level, src, span, msg)
+        struct_lint_level(self.sess, lint, level, src, span, decorate)
     }
 
     /// Registers the ID provided with the current set of lints stored in
diff --git a/src/librustc_lint/non_ascii_idents.rs b/src/librustc_lint/non_ascii_idents.rs
index 3c85a1b31b2..df91df8f8b5 100644
--- a/src/librustc_lint/non_ascii_idents.rs
+++ b/src/librustc_lint/non_ascii_idents.rs
@@ -25,16 +25,14 @@ impl EarlyLintPass for NonAsciiIdents {
         cx.struct_span_lint(
             NON_ASCII_IDENTS,
             ident.span,
-            "identifier contains non-ASCII characters",
-        )
-        .emit();
+            |lint| lint.build("identifier contains non-ASCII characters").emit(),
+        );
         if !name_str.chars().all(GeneralSecurityProfile::identifier_allowed) {
             cx.struct_span_lint(
                 UNCOMMON_CODEPOINTS,
                 ident.span,
-                "identifier contains uncommon Unicode codepoints",
+                |lint| lint.build("identifier contains uncommon Unicode codepoints").emit(),
             )
-            .emit();
         }
     }
 }
diff --git a/src/librustc_lint/nonstandard_style.rs b/src/librustc_lint/nonstandard_style.rs
index 6fdbfea7f03..9e291d2cb74 100644
--- a/src/librustc_lint/nonstandard_style.rs
+++ b/src/librustc_lint/nonstandard_style.rs
@@ -108,14 +108,14 @@ impl NonCamelCaseTypes {
 
         if !is_camel_case(name) {
             let msg = format!("{} `{}` should have an upper camel case name", sort, name);
-            cx.struct_span_lint(NON_CAMEL_CASE_TYPES, ident.span, &msg)
-                .span_suggestion(
+            cx.struct_span_lint(NON_CAMEL_CASE_TYPES, ident.span, |lint| {
+                lint.build(&msg).span_suggestion(
                     ident.span,
                     "convert the identifier to upper camel case",
                     to_camel_case(name),
                     Applicability::MaybeIncorrect,
-                )
-                .emit();
+                ).emit()
+            })
         }
     }
 }
@@ -226,22 +226,24 @@ impl NonSnakeCase {
             let sc = NonSnakeCase::to_snake_case(name);
 
             let msg = format!("{} `{}` should have a snake case name", sort, name);
-            let mut err = cx.struct_span_lint(NON_SNAKE_CASE, ident.span, &msg);
+            cx.struct_span_lint(NON_SNAKE_CASE, ident.span, |lint| {
+                let mut err = lint.build(&msg);
+                // We have a valid span in almost all cases, but we don't have one when linting a crate
+                // name provided via the command line.
+                if !ident.span.is_dummy() {
+                    err.span_suggestion(
+                        ident.span,
+                        "convert the identifier to snake case",
+                        sc,
+                        Applicability::MaybeIncorrect,
+                    );
+                } else {
+                    err.help(&format!("convert the identifier to snake case: `{}`", sc));
+                }
 
-            // We have a valid span in almost all cases, but we don't have one when linting a crate
-            // name provided via the command line.
-            if !ident.span.is_dummy() {
-                err.span_suggestion(
-                    ident.span,
-                    "convert the identifier to snake case",
-                    sc,
-                    Applicability::MaybeIncorrect,
-                );
-            } else {
-                err.help(&format!("convert the identifier to snake case: `{}`", sc));
-            }
+                err.emit();
+            });
 
-            err.emit();
         }
     }
 }
@@ -390,8 +392,8 @@ impl NonUpperCaseGlobals {
         if name.chars().any(|c| c.is_lowercase()) {
             let uc = NonSnakeCase::to_snake_case(&name).to_uppercase();
 
-            let msg = format!("{} `{}` should have an upper case name", sort, name);
-            cx.struct_span_lint(NON_UPPER_CASE_GLOBALS, ident.span, &msg)
+            cx.struct_span_lint(NON_UPPER_CASE_GLOBALS, ident.span, |lint| {
+                lint.build(&format!("{} `{}` should have an upper case name", sort, name))
                 .span_suggestion(
                     ident.span,
                     "convert the identifier to upper case",
@@ -399,6 +401,7 @@ impl NonUpperCaseGlobals {
                     Applicability::MaybeIncorrect,
                 )
                 .emit();
+            })
         }
     }
 }
diff --git a/src/librustc_lint/redundant_semicolon.rs b/src/librustc_lint/redundant_semicolon.rs
index 21b244ad75d..68cb0b54f56 100644
--- a/src/librustc_lint/redundant_semicolon.rs
+++ b/src/librustc_lint/redundant_semicolon.rs
@@ -26,19 +26,21 @@ impl EarlyLintPass for RedundantSemicolon {
                             } else {
                                 "unnecessary trailing semicolon"
                             };
-                            let mut err = cx.struct_span_lint(REDUNDANT_SEMICOLON, stmt.span, &msg);
-                            let suggest_msg = if multiple {
-                                "remove these semicolons"
-                            } else {
-                                "remove this semicolon"
-                            };
-                            err.span_suggestion(
-                                stmt.span,
-                                &suggest_msg,
-                                String::new(),
-                                Applicability::MaybeIncorrect,
-                            );
-                            err.emit();
+                            cx.struct_span_lint(REDUNDANT_SEMICOLON, stmt.span, |lint| {
+                                let mut err = lint.build(&msg);
+                                let suggest_msg = if multiple {
+                                    "remove these semicolons"
+                                } else {
+                                    "remove this semicolon"
+                                };
+                                err.span_suggestion(
+                                    stmt.span,
+                                    &suggest_msg,
+                                    String::new(),
+                                    Applicability::MaybeIncorrect,
+                                );
+                                err.emit();
+                            });
                         }
                     }
                 }
diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs
index 6bc6f58f3e7..30a903a2a30 100644
--- a/src/librustc_lint/types.rs
+++ b/src/librustc_lint/types.rs
@@ -67,6 +67,7 @@ fn lint_overflowing_range_endpoint<'a, 'tcx>(
 ) -> bool {
     // We only want to handle exclusive (`..`) ranges,
     // which are represented as `ExprKind::Struct`.
+    let mut overwritten = false;
     if let ExprKind::Struct(_, eps, _) = &parent_expr.kind {
         if eps.len() != 2 {
             return false;
@@ -75,35 +76,36 @@ fn lint_overflowing_range_endpoint<'a, 'tcx>(
         // (`..=`) instead only if it is the `end` that is
         // overflowing and only by 1.
         if eps[1].expr.hir_id == expr.hir_id && lit_val - 1 == max {
-            let mut err = cx.struct_span_lint(
+            cx.struct_span_lint(
                 OVERFLOWING_LITERALS,
                 parent_expr.span,
-                &format!("range endpoint is out of range for `{}`", ty),
+                |lint| {
+                    let mut err = lint.build(&format!("range endpoint is out of range for `{}`", ty));
+                    if let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) {
+                        use ast::{LitIntType, LitKind};
+                        // We need to preserve the literal's suffix,
+                        // as it may determine typing information.
+                        let suffix = match lit.node {
+                            LitKind::Int(_, LitIntType::Signed(s)) => format!("{}", s.name_str()),
+                            LitKind::Int(_, LitIntType::Unsigned(s)) => format!("{}", s.name_str()),
+                            LitKind::Int(_, LitIntType::Unsuffixed) => "".to_owned(),
+                            _ => bug!(),
+                        };
+                        let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix);
+                        err.span_suggestion(
+                            parent_expr.span,
+                            &"use an inclusive range instead",
+                            suggestion,
+                            Applicability::MachineApplicable,
+                        );
+                        err.emit();
+                        overwritten = true;
+                    }
+                },
             );
-            if let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) {
-                use ast::{LitIntType, LitKind};
-                // We need to preserve the literal's suffix,
-                // as it may determine typing information.
-                let suffix = match lit.node {
-                    LitKind::Int(_, LitIntType::Signed(s)) => format!("{}", s.name_str()),
-                    LitKind::Int(_, LitIntType::Unsigned(s)) => format!("{}", s.name_str()),
-                    LitKind::Int(_, LitIntType::Unsuffixed) => "".to_owned(),
-                    _ => bug!(),
-                };
-                let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix);
-                err.span_suggestion(
-                    parent_expr.span,
-                    &"use an inclusive range instead",
-                    suggestion,
-                    Applicability::MachineApplicable,
-                );
-                err.emit();
-                return true;
-            }
         }
     }
-
-    false
+    overwritten
 }
 
 // For `isize` & `usize`, be conservative with the warnings, so that the
@@ -163,31 +165,32 @@ fn report_bin_hex_error(
             (t.name_str(), actually.to_string())
         }
     };
-    let mut err = cx.struct_span_lint(
+    cx.struct_span_lint(
         OVERFLOWING_LITERALS,
         expr.span,
-        &format!("literal out of range for {}", t),
+        |lint| {
+            let mut err = lint.build(&format!("literal out of range for {}", t));
+            err.note(&format!(
+                "the literal `{}` (decimal `{}`) does not fit into \
+                    an `{}` and will become `{}{}`",
+                repr_str, val, t, actually, t
+            ));
+            if let Some(sugg_ty) = get_type_suggestion(&cx.tables.node_type(expr.hir_id), val, negative) {
+                if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
+                    let (sans_suffix, _) = repr_str.split_at(pos);
+                    err.span_suggestion(
+                        expr.span,
+                        &format!("consider using `{}` instead", sugg_ty),
+                        format!("{}{}", sans_suffix, sugg_ty),
+                        Applicability::MachineApplicable,
+                    );
+                } else {
+                    err.help(&format!("consider using `{}` instead", sugg_ty));
+                }
+            }
+            err.emit();
+        },
     );
-    err.note(&format!(
-        "the literal `{}` (decimal `{}`) does not fit into \
-            an `{}` and will become `{}{}`",
-        repr_str, val, t, actually, t
-    ));
-    if let Some(sugg_ty) = get_type_suggestion(&cx.tables.node_type(expr.hir_id), val, negative) {
-        if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
-            let (sans_suffix, _) = repr_str.split_at(pos);
-            err.span_suggestion(
-                expr.span,
-                &format!("consider using `{}` instead", sugg_ty),
-                format!("{}{}", sans_suffix, sugg_ty),
-                Applicability::MachineApplicable,
-            );
-        } else {
-            err.help(&format!("consider using `{}` instead", sugg_ty));
-        }
-    }
-
-    err.emit();
 }
 
 // This function finds the next fitting type and generates a suggestion string.
@@ -298,18 +301,16 @@ fn lint_uint_literal<'a, 'tcx>(
             match par_e.kind {
                 hir::ExprKind::Cast(..) => {
                     if let ty::Char = cx.tables.expr_ty(par_e).kind {
-                        let mut err = cx.struct_span_lint(
-                            OVERFLOWING_LITERALS,
-                            par_e.span,
-                            "only `u8` can be cast into `char`",
-                        );
-                        err.span_suggestion(
-                            par_e.span,
-                            &"use a `char` literal instead",
-                            format!("'\\u{{{:X}}}'", lit_val),
-                            Applicability::MachineApplicable,
-                        );
-                        err.emit();
+                        cx.struct_span_lint(OVERFLOWING_LITERALS, par_e.span, |lint| {
+                            lint.build("only `u8` can be cast into `char`")
+                                .span_suggestion(
+                                    par_e.span,
+                                    &"use a `char` literal instead",
+                                    format!("'\\u{{{:X}}}'", lit_val),
+                                    Applicability::MachineApplicable,
+                                )
+                                .emit();
+                        });
                         return;
                     }
                 }
@@ -883,22 +884,21 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
         note: &str,
         help: Option<&str>,
     ) {
-        let mut diag = self.cx.struct_span_lint(
-            IMPROPER_CTYPES,
-            sp,
-            &format!("`extern` block uses type `{}`, which is not FFI-safe", ty),
-        );
-        diag.span_label(sp, "not FFI-safe");
-        if let Some(help) = help {
-            diag.help(help);
-        }
-        diag.note(note);
-        if let ty::Adt(def, _) = ty.kind {
-            if let Some(sp) = self.cx.tcx.hir().span_if_local(def.did) {
-                diag.span_note(sp, "the type is defined here");
+        self.cx.struct_span_lint(IMPROPER_CTYPES, sp, |lint| {
+            let mut diag =
+                lint.build(&format!("`extern` block uses type `{}`, which is not FFI-safe", ty));
+            diag.span_label(sp, "not FFI-safe");
+            if let Some(help) = help {
+                diag.help(help);
             }
-        }
-        diag.emit();
+            diag.note(note);
+            if let ty::Adt(def, _) = ty.kind {
+                if let Some(sp) = self.cx.tcx.hir().span_if_local(def.did) {
+                    diag.span_note(sp, "the type is defined here");
+                }
+            }
+            diag.emit();
+        });
     }
 
     fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs
index 5490e5e2b4d..2f4a9572a83 100644
--- a/src/librustc_lint/unused.rs
+++ b/src/librustc_lint/unused.rs
@@ -213,18 +213,20 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
         ) -> bool {
             for attr in cx.tcx.get_attrs(def_id).iter() {
                 if attr.check_name(sym::must_use) {
-                    let msg = format!(
-                        "unused {}`{}`{} that must be used",
-                        descr_pre_path,
-                        cx.tcx.def_path_str(def_id),
-                        descr_post_path
-                    );
-                    let mut err = cx.struct_span_lint(UNUSED_MUST_USE, span, &msg);
-                    // check for #[must_use = "..."]
-                    if let Some(note) = attr.value_str() {
-                        err.note(&note.as_str());
-                    }
-                    err.emit();
+                    cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
+                        let msg = format!(
+                            "unused {}`{}`{} that must be used",
+                            descr_pre_path,
+                            cx.tcx.def_path_str(def_id),
+                            descr_post_path
+                        );
+                        let mut err = lint.build(&msg);
+                        // check for #[must_use = "..."]
+                        if let Some(note) = attr.value_str() {
+                            err.note(&note.as_str());
+                        }
+                        err.emit();
+                    });
                     return true;
                 }
             }
@@ -406,52 +408,54 @@ impl UnusedParens {
         msg: &str,
         keep_space: (bool, bool),
     ) {
-        let span_msg = format!("unnecessary parentheses around {}", msg);
-        let mut err = cx.struct_span_lint(UNUSED_PARENS, span, &span_msg);
-        let mut ate_left_paren = false;
-        let mut ate_right_paren = false;
-        let parens_removed = pattern.trim_matches(|c| match c {
-            '(' => {
-                if ate_left_paren {
-                    false
-                } else {
-                    ate_left_paren = true;
-                    true
+        cx.struct_span_lint(UNUSED_PARENS, span, |lint| {
+            let span_msg = format!("unnecessary parentheses around {}", msg);
+            let mut err = lint.build(&span_msg);
+            let mut ate_left_paren = false;
+            let mut ate_right_paren = false;
+            let parens_removed = pattern.trim_matches(|c| match c {
+                '(' => {
+                    if ate_left_paren {
+                        false
+                    } else {
+                        ate_left_paren = true;
+                        true
+                    }
                 }
-            }
-            ')' => {
-                if ate_right_paren {
-                    false
-                } else {
-                    ate_right_paren = true;
-                    true
+                ')' => {
+                    if ate_right_paren {
+                        false
+                    } else {
+                        ate_right_paren = true;
+                        true
+                    }
                 }
-            }
-            _ => false,
-        });
+                _ => false,
+            });
 
-        let replace = {
-            let mut replace = if keep_space.0 {
-                let mut s = String::from(" ");
-                s.push_str(parens_removed);
-                s
-            } else {
-                String::from(parens_removed)
-            };
+            let replace = {
+                let mut replace = if keep_space.0 {
+                    let mut s = String::from(" ");
+                    s.push_str(parens_removed);
+                    s
+                } else {
+                    String::from(parens_removed)
+                };
 
-            if keep_space.1 {
-                replace.push(' ');
-            }
-            replace
-        };
+                if keep_space.1 {
+                    replace.push(' ');
+                }
+                replace
+            };
 
-        err.span_suggestion_short(
-            span,
-            "remove these parentheses",
-            replace,
-            Applicability::MachineApplicable,
-        );
-        err.emit();
+            err.span_suggestion_short(
+                span,
+                "remove these parentheses",
+                replace,
+                Applicability::MachineApplicable,
+            );
+            err.emit();
+        });
     }
 }
 
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index e528159fcef..25f1a495ad3 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -319,18 +319,20 @@ fn do_mir_borrowck<'a, 'tcx>(
         };
 
         // Span and message don't matter; we overwrite them below anyway
-        let mut diag = mbcx.infcx.tcx.struct_span_lint_hir(
+        mbcx.infcx.tcx.struct_span_lint_hir(
             MUTABLE_BORROW_RESERVATION_CONFLICT,
             lint_root,
             DUMMY_SP,
-            "",
-        );
+            |lint| {
+                let mut diag = lint.build("");
 
-        diag.message = initial_diag.styled_message().clone();
-        diag.span = initial_diag.span.clone();
+                diag.message = initial_diag.styled_message().clone();
+                diag.span = initial_diag.span.clone();
 
+                diag.buffer(&mut mbcx.errors_buffer);
+            },
+        );
         initial_diag.cancel();
-        diag.buffer(&mut mbcx.errors_buffer);
     }
 
     // For each non-user used mutable variable, check if it's been assigned from
@@ -381,15 +383,17 @@ fn do_mir_borrowck<'a, 'tcx>(
             UNUSED_MUT,
             lint_root,
             span,
-            "variable does not need to be mutable",
-        )
-        .span_suggestion_short(
-            mut_span,
-            "remove this `mut`",
-            String::new(),
-            Applicability::MachineApplicable,
+            |lint| {
+                lint.build("variable does not need to be mutable")
+                .span_suggestion_short(
+                    mut_span,
+                    "remove this `mut`",
+                    String::new(),
+                    Applicability::MachineApplicable,
+                )
+                .emit();
+            },
         )
-        .emit();
     }
 
     // Buffer any move errors that we collected and de-duplicated.
diff --git a/src/librustc_mir/const_eval/eval_queries.rs b/src/librustc_mir/const_eval/eval_queries.rs
index 428db8356b1..99d3b281945 100644
--- a/src/librustc_mir/const_eval/eval_queries.rs
+++ b/src/librustc_mir/const_eval/eval_queries.rs
@@ -209,10 +209,11 @@ fn validate_and_turn_into_const<'tcx>(
 
     val.map_err(|error| {
         let err = error_to_const_error(&ecx, error);
-        match err.struct_error(ecx.tcx, "it is undefined behavior to use this value") {
-            Ok(mut diag) => {
-                diag.note(note_on_undefined_behavior_error());
-                diag.emit();
+        match err.struct_error(ecx.tcx, "it is undefined behavior to use this value", |mut diag| {
+            diag.note(note_on_undefined_behavior_error());
+            diag.emit();
+        }) {
+            Ok(_) => {
                 ErrorHandled::Reported
             }
             Err(err) => err,
diff --git a/src/librustc_mir/interpret/intern.rs b/src/librustc_mir/interpret/intern.rs
index 0c65b77a382..9fea82d78c9 100644
--- a/src/librustc_mir/interpret/intern.rs
+++ b/src/librustc_mir/interpret/intern.rs
@@ -326,12 +326,15 @@ pub fn intern_const_alloc_recursive<M: CompileTimeMachine<'mir, 'tcx>>(
             // to read enum discriminants in order to find references in enum variant fields.
             if let err_unsup!(ValidationFailure(_)) = error.kind {
                 let err = crate::const_eval::error_to_const_error(&ecx, error);
-                match err.struct_error(ecx.tcx, "it is undefined behavior to use this value") {
-                    Ok(mut diag) => {
+                match err.struct_error(
+                    ecx.tcx,
+                    "it is undefined behavior to use this value",
+                    |mut diag| {
                         diag.note(crate::const_eval::note_on_undefined_behavior_error());
                         diag.emit();
-                    }
-                    Err(ErrorHandled::TooGeneric) | Err(ErrorHandled::Reported) => {}
+                    },
+                ) {
+                    Ok(()) | Err(ErrorHandled::TooGeneric) | Err(ErrorHandled::Reported) => {}
                 }
             }
         }
diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs
index 8dc185cd82b..c019fa57fb7 100644
--- a/src/librustc_mir/transform/check_unsafety.rs
+++ b/src/librustc_mir/transform/check_unsafety.rs
@@ -527,7 +527,9 @@ fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: DefId) {
          does not derive Copy (error E0133)"
             .to_string()
     };
-    tcx.lint_hir(SAFE_PACKED_BORROWS, lint_hir_id, tcx.def_span(def_id), &message);
+    tcx.struct_span_lint_hir(SAFE_PACKED_BORROWS, lint_hir_id, tcx.def_span(def_id), |lint| {
+        lint.build(&message).emit()
+    });
 }
 
 /// Returns the `HirId` for an enclosing scope that is also `unsafe`.
@@ -559,15 +561,17 @@ fn is_enclosed(
 fn report_unused_unsafe(tcx: TyCtxt<'_>, used_unsafe: &FxHashSet<hir::HirId>, id: hir::HirId) {
     let span = tcx.sess.source_map().def_span(tcx.hir().span(id));
     let msg = "unnecessary `unsafe` block";
-    let mut db = tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, msg);
-    db.span_label(span, msg);
-    if let Some((kind, id)) = is_enclosed(tcx, used_unsafe, id) {
-        db.span_label(
-            tcx.sess.source_map().def_span(tcx.hir().span(id)),
-            format!("because it's nested under this `unsafe` {}", kind),
-        );
-    }
-    db.emit();
+    tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, |lint| {
+        let mut db = lint.build(msg);
+        db.span_label(span, msg);
+        if let Some((kind, id)) = is_enclosed(tcx, used_unsafe, id) {
+            db.span_label(
+                tcx.sess.source_map().def_span(tcx.hir().span(id)),
+                format!("because it's nested under this `unsafe` {}", kind),
+            );
+        }
+        db.emit();
+    });
 }
 
 fn builtin_derive_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> {
@@ -619,13 +623,17 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) {
                         SAFE_PACKED_BORROWS,
                         lint_hir_id,
                         source_info.span,
-                        &format!(
-                            "{} is unsafe and requires unsafe function or block (error E0133)",
-                            description
-                        ),
+                        |lint| {
+                            lint.build(
+                                &format!(
+                                    "{} is unsafe and requires unsafe function or block (error E0133)",
+                                    description
+                                )
+                            )
+                            .note(&details.as_str())
+                            .emit()
+                        }
                     )
-                    .note(&details.as_str())
-                    .emit();
                 }
             }
         }
diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs
index d645f6cf183..aeeca52ba3b 100644
--- a/src/librustc_mir/transform/const_prop.rs
+++ b/src/librustc_mir/transform/const_prop.rs
@@ -557,11 +557,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
             if r_bits.map_or(false, |b| b >= left_bits as u128) {
                 let lint_root = self.lint_root(source_info)?;
                 let dir = if op == BinOp::Shr { "right" } else { "left" };
-                self.tcx.lint_hir(
+                self.tcx.struct_span_lint_hir(
                     ::rustc::lint::builtin::EXCEEDING_BITSHIFTS,
                     lint_root,
                     source_info.span,
-                    &format!("attempt to shift {} with overflow", dir),
+                    |lint| lint.build(&format!("attempt to shift {} with overflow", dir)).emit(),
                 );
                 return None;
             }
@@ -912,35 +912,42 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
                             .hir()
                             .as_local_hir_id(self.source.def_id())
                             .expect("some part of a failing const eval must be local");
-                        let msg = match msg {
-                            PanicInfo::Overflow(_)
-                            | PanicInfo::OverflowNeg
-                            | PanicInfo::DivisionByZero
-                            | PanicInfo::RemainderByZero => msg.description().to_owned(),
-                            PanicInfo::BoundsCheck { ref len, ref index } => {
-                                let len =
-                                    self.eval_operand(len, source_info).expect("len must be const");
-                                let len = match self.ecx.read_scalar(len) {
-                                    Ok(ScalarMaybeUndef::Scalar(Scalar::Raw { data, .. })) => data,
-                                    other => bug!("const len not primitive: {:?}", other),
-                                };
-                                let index = self
-                                    .eval_operand(index, source_info)
-                                    .expect("index must be const");
-                                let index = match self.ecx.read_scalar(index) {
-                                    Ok(ScalarMaybeUndef::Scalar(Scalar::Raw { data, .. })) => data,
-                                    other => bug!("const index not primitive: {:?}", other),
+                        self.tcx.struct_span_lint_hir(
+                            ::rustc::lint::builtin::CONST_ERR,
+                            hir_id,
+                            span,
+                            |lint| {
+                                let msg = match msg {
+                                    PanicInfo::Overflow(_)
+                                    | PanicInfo::OverflowNeg
+                                    | PanicInfo::DivisionByZero
+                                    | PanicInfo::RemainderByZero => msg.description().to_owned(),
+                                    PanicInfo::BoundsCheck { ref len, ref index } => {
+                                        let len =
+                                            self.eval_operand(len, source_info).expect("len must be const");
+                                        let len = match self.ecx.read_scalar(len) {
+                                            Ok(ScalarMaybeUndef::Scalar(Scalar::Raw { data, .. })) => data,
+                                            other => bug!("const len not primitive: {:?}", other),
+                                        };
+                                        let index = self
+                                            .eval_operand(index, source_info)
+                                            .expect("index must be const");
+                                        let index = match self.ecx.read_scalar(index) {
+                                            Ok(ScalarMaybeUndef::Scalar(Scalar::Raw { data, .. })) => data,
+                                            other => bug!("const index not primitive: {:?}", other),
+                                        };
+                                        format!(
+                                            "index out of bounds: \
+                                            the len is {} but the index is {}",
+                                            len, index,
+                                        )
+                                    }
+                                    // Need proper const propagator for these
+                                    _ => return,
                                 };
-                                format!(
-                                    "index out of bounds: \
-                                    the len is {} but the index is {}",
-                                    len, index,
-                                )
-                            }
-                            // Need proper const propagator for these
-                            _ => return,
-                        };
-                        self.tcx.lint_hir(::rustc::lint::builtin::CONST_ERR, hir_id, span, &msg);
+                                lint.build(&msg).emit()
+                            },
+                        );
                     } else {
                         if self.should_const_prop(value) {
                             if let ScalarMaybeUndef::Scalar(scalar) = value_const {
diff --git a/src/librustc_mir_build/hair/pattern/_match.rs b/src/librustc_mir_build/hair/pattern/_match.rs
index 4f0e5bb4582..7ed6c81eb63 100644
--- a/src/librustc_mir_build/hair/pattern/_match.rs
+++ b/src/librustc_mir_build/hair/pattern/_match.rs
@@ -2235,24 +2235,26 @@ fn lint_overlapping_patterns<'tcx>(
     overlaps: Vec<IntRange<'tcx>>,
 ) {
     if let (true, Some(hir_id)) = (!overlaps.is_empty(), hir_id) {
-        let mut err = tcx.struct_span_lint_hir(
+        tcx.struct_span_lint_hir(
             lint::builtin::OVERLAPPING_PATTERNS,
             hir_id,
             ctor_range.span,
-            "multiple patterns covering the same range",
+            |lint| {
+                let mut err = lint.build("multiple patterns covering the same range");
+                err.span_label(ctor_range.span, "overlapping patterns");
+                for int_range in overlaps {
+                    // Use the real type for user display of the ranges:
+                    err.span_label(
+                        int_range.span,
+                        &format!(
+                            "this range overlaps on `{}`",
+                            IntRange { range: int_range.range, ty, span: DUMMY_SP }.to_pat(tcx),
+                        ),
+                    );
+                }
+                err.emit();
+            },
         );
-        err.span_label(ctor_range.span, "overlapping patterns");
-        for int_range in overlaps {
-            // Use the real type for user display of the ranges:
-            err.span_label(
-                int_range.span,
-                &format!(
-                    "this range overlaps on `{}`",
-                    IntRange { range: int_range.range, ty, span: DUMMY_SP }.to_pat(tcx),
-                ),
-            );
-        }
-        err.emit();
     }
 }
 
diff --git a/src/librustc_mir_build/hair/pattern/check_match.rs b/src/librustc_mir_build/hair/pattern/check_match.rs
index b77bd4ecb8e..421f7958e5f 100644
--- a/src/librustc_mir_build/hair/pattern/check_match.rs
+++ b/src/librustc_mir_build/hair/pattern/check_match.rs
@@ -292,20 +292,25 @@ fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pa
                                 BINDINGS_WITH_VARIANT_NAME,
                                 p.hir_id,
                                 p.span,
-                                &format!(
-                                    "pattern binding `{}` is named the same as one \
-                                    of the variants of the type `{}`",
-                                    ident, ty_path
-                                ),
+                                |lint| {
+                                    lint
+                                        .build(
+                                            &format!(
+                                                "pattern binding `{}` is named the same as one \
+                                                of the variants of the type `{}`",
+                                                ident, ty_path
+                                            )
+                                        )
+                                        .code(error_code!(E0170))
+                                        .span_suggestion(
+                                            p.span,
+                                            "to match on the variant, qualify the path",
+                                            format!("{}::{}", ty_path, ident),
+                                            Applicability::MachineApplicable,
+                                        )
+                                        .emit();
+                                },
                             )
-                            .code(error_code!(E0170))
-                            .span_suggestion(
-                                p.span,
-                                "to match on the variant, qualify the path",
-                                format!("{}::{}", ty_path, ident),
-                                Applicability::MachineApplicable,
-                            )
-                            .emit();
                     }
                 }
             }
@@ -325,13 +330,15 @@ fn pat_is_catchall(pat: &super::Pat<'_>) -> bool {
 }
 
 fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<Span>) {
-    let mut err = tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, "unreachable pattern");
-    if let Some(catchall) = catchall {
-        // We had a catchall pattern, hint at that.
-        err.span_label(span, "unreachable pattern");
-        err.span_label(catchall, "matches any value");
-    }
-    err.emit();
+    tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, |lint| {
+        let mut err = lint.build("unreachable pattern");
+        if let Some(catchall) = catchall {
+            // We had a catchall pattern, hint at that.
+            err.span_label(span, "unreachable pattern");
+            err.span_label(catchall, "matches any value");
+        }
+        err.emit();
+    });
 }
 
 fn irrefutable_let_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, source: hir::MatchSource) {
@@ -340,7 +347,7 @@ fn irrefutable_let_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, source: hir::
         hir::MatchSource::WhileLetDesugar => "irrefutable while-let pattern",
         _ => bug!(),
     };
-    tcx.lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, msg);
+    tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, |lint| lint.build(msg).emit());
 }
 
 /// Check for unreachable patterns.
diff --git a/src/librustc_mir_build/hair/pattern/const_to_pat.rs b/src/librustc_mir_build/hair/pattern/const_to_pat.rs
index a21a0ee8a1b..5fbe764430c 100644
--- a/src/librustc_mir_build/hair/pattern/const_to_pat.rs
+++ b/src/librustc_mir_build/hair/pattern/const_to_pat.rs
@@ -109,11 +109,14 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
                     }
                 };
                 let path = self.tcx().def_path_str(adt_def.did);
-                let msg = format!(
-                    "to use a constant of type `{}` in a pattern, \
-                     `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
-                    path, path,
-                );
+
+                let make_msg = || -> String {
+                    format!(
+                        "to use a constant of type `{}` in a pattern, \
+                         `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
+                        path, path,
+                    )
+                };
 
                 // double-check there even *is* a semantic `PartialEq` to dispatch to.
                 //
@@ -143,13 +146,13 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
 
                 if !ty_is_partial_eq {
                     // span_fatal avoids ICE from resolution of non-existent method (rare case).
-                    self.tcx().sess.span_fatal(self.span, &msg);
+                    self.tcx().sess.span_fatal(self.span, &make_msg());
                 } else {
-                    self.tcx().lint_hir(
+                    self.tcx().struct_span_lint_hir(
                         lint::builtin::INDIRECT_STRUCTURAL_MATCH,
                         self.id,
                         self.span,
-                        &msg,
+                        |lint| lint.build(&make_msg()).emit(),
                     );
                 }
             }
@@ -177,11 +180,11 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
 
         let kind = match cv.ty.kind {
             ty::Float(_) => {
-                tcx.lint_hir(
+                tcx.struct_span_lint_hir(
                     ::rustc::lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
                     id,
                     span,
-                    "floating-point types cannot be used in patterns",
+                    |lint| lint.build("floating-point types cannot be used in patterns").emit(),
                 );
                 PatKind::Constant { value: cv }
             }
diff --git a/src/librustc_mir_build/lints.rs b/src/librustc_mir_build/lints.rs
index 4244e1b8d80..4704f8d034d 100644
--- a/src/librustc_mir_build/lints.rs
+++ b/src/librustc_mir_build/lints.rs
@@ -124,18 +124,20 @@ fn check_fn_for_unconditional_recursion<'tcx>(
     if !reached_exit_without_self_call && !self_call_locations.is_empty() {
         let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
         let sp = tcx.sess.source_map().def_span(tcx.hir().span(hir_id));
-        let mut db = tcx.struct_span_lint_hir(
+        tcx.struct_span_lint_hir(
             UNCONDITIONAL_RECURSION,
             hir_id,
             sp,
-            "function cannot return without recursing",
+            |lint| {
+                let mut db = lint.build("function cannot return without recursing");
+                db.span_label(sp, "cannot return without recursing");
+                // offer some help to the programmer.
+                for location in &self_call_locations {
+                    db.span_label(location.span, "recursive call site");
+                }
+                db.help("a `loop` may express intention better if this is on purpose");
+                db.emit();
+            },
         );
-        db.span_label(sp, "cannot return without recursing");
-        // offer some help to the programmer.
-        for location in &self_call_locations {
-            db.span_label(location.span, "recursive call site");
-        }
-        db.help("a `loop` may express intention better if this is on purpose");
-        db.emit();
     }
 }
diff --git a/src/librustc_parse/config.rs b/src/librustc_parse/config.rs
index 0edd56680f9..cd935a37f7d 100644
--- a/src/librustc_parse/config.rs
+++ b/src/librustc_parse/config.rs
@@ -315,10 +315,11 @@ impl<'a> StripUnconfigured<'a> {
                 validate_attr::check_meta_bad_delim(self.sess, dspan, delim, msg);
                 match parse_in(self.sess, tts.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) {
                     Ok(r) => return Some(r),
-                    Err(mut e) => e
-                        .help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP))
+                    Err(mut e) => {
+                        e.help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP))
                         .note(CFG_ATTR_NOTE_REF)
-                        .emit(),
+                        .emit();
+                    },
                 }
             }
             _ => self.error_malformed_cfg_attr_missing(attr.span),
diff --git a/src/librustc_parse/lexer/unescape_error_reporting.rs b/src/librustc_parse/lexer/unescape_error_reporting.rs
index 88762dabd8a..d41775a143a 100644
--- a/src/librustc_parse/lexer/unescape_error_reporting.rs
+++ b/src/librustc_parse/lexer/unescape_error_reporting.rs
@@ -69,7 +69,7 @@ pub(crate) fn emit_unescape_error(
                     format!("\"{}\"", lit),
                     Applicability::MachineApplicable,
                 )
-                .emit()
+                .emit();
         }
         EscapeError::EscapeOnlyChar => {
             let (c, _span) = last_char();
diff --git a/src/librustc_parse/parser/attr.rs b/src/librustc_parse/parser/attr.rs
index e58eb9ffc51..905f6bef58e 100644
--- a/src/librustc_parse/parser/attr.rs
+++ b/src/librustc_parse/parser/attr.rs
@@ -149,7 +149,7 @@ impl<'a> Parser<'a> {
                                    source files. Outer attributes, like `#[test]`, annotate the \
                                    item following them.",
                             )
-                            .emit()
+                            .emit();
                     }
                 }
 
@@ -239,7 +239,7 @@ impl<'a> Parser<'a> {
                                     (`1u8`, `1.0f32`, etc.), use an unsuffixed version \
                                     (`1`, `1.0`, etc.)",
                 )
-                .emit()
+                .emit();
         }
 
         Ok(lit)
diff --git a/src/librustc_parse/parser/diagnostics.rs b/src/librustc_parse/parser/diagnostics.rs
index 237b3cc13d3..018aef3c13c 100644
--- a/src/librustc_parse/parser/diagnostics.rs
+++ b/src/librustc_parse/parser/diagnostics.rs
@@ -1014,7 +1014,7 @@ impl<'a> Parser<'a> {
                     String::new(),
                     Applicability::MachineApplicable,
                 )
-                .emit()
+                .emit();
         }
     }
 
diff --git a/src/librustc_parse/parser/mod.rs b/src/librustc_parse/parser/mod.rs
index 7131eb1144e..ba97f3feb80 100644
--- a/src/librustc_parse/parser/mod.rs
+++ b/src/librustc_parse/parser/mod.rs
@@ -1407,6 +1407,6 @@ pub fn emit_unclosed_delims(unclosed_delims: &mut Vec<UnmatchedBrace>, sess: &Pa
     *sess.reached_eof.borrow_mut() |=
         unclosed_delims.iter().any(|unmatched_delim| unmatched_delim.found_delim.is_none());
     for unmatched in unclosed_delims.drain(..) {
-        make_unclosed_delims_error(unmatched, sess).map(|mut e| e.emit());
+        make_unclosed_delims_error(unmatched, sess).map(|mut e| { e.emit(); });
     }
 }
diff --git a/src/librustc_parse/parser/pat.rs b/src/librustc_parse/parser/pat.rs
index 985185230f2..ec6d4db6102 100644
--- a/src/librustc_parse/parser/pat.rs
+++ b/src/librustc_parse/parser/pat.rs
@@ -572,7 +572,7 @@ impl<'a> Parser<'a> {
         self.struct_span_err(span, problem)
             .span_suggestion(span, suggestion, fix, Applicability::MachineApplicable)
             .note("`mut` may be followed by `variable` and `variable @ pattern`")
-            .emit()
+            .emit();
     }
 
     /// Eat any extraneous `mut`s and error + recover if we ate any.
diff --git a/src/librustc_parse/validate_attr.rs b/src/librustc_parse/validate_attr.rs
index 84562fbb46f..f6d5da68be3 100644
--- a/src/librustc_parse/validate_attr.rs
+++ b/src/librustc_parse/validate_attr.rs
@@ -27,7 +27,7 @@ pub fn check_meta(sess: &ParseSess, attr: &Attribute) {
         _ => {
             if let MacArgs::Eq(..) = attr.get_normal_item().args {
                 // All key-value attributes are restricted to meta-item syntax.
-                parse_meta(sess, attr).map_err(|mut err| err.emit()).ok();
+                parse_meta(sess, attr).map_err(|mut err| { err.emit(); }).ok();
             }
         }
     }
@@ -152,6 +152,6 @@ pub fn check_builtin_attribute(
                 }
             }
         }
-        Err(mut err) => err.emit(),
+        Err(mut err) => { err.emit(); },
     }
 }
diff --git a/src/librustc_passes/check_attr.rs b/src/librustc_passes/check_attr.rs
index a877c1de175..6bae61d787b 100644
--- a/src/librustc_passes/check_attr.rs
+++ b/src/librustc_passes/check_attr.rs
@@ -97,9 +97,8 @@ impl CheckAttrVisitor<'tcx> {
                         UNUSED_ATTRIBUTES,
                         hir_id,
                         attr.span,
-                        "`#[inline]` is ignored on function prototypes",
-                    )
-                    .emit();
+                        |lint| lint.build("`#[inline]` is ignored on function prototypes").emit(),
+                    );
                 true
             }
             // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with
@@ -112,18 +111,19 @@ impl CheckAttrVisitor<'tcx> {
                         UNUSED_ATTRIBUTES,
                         hir_id,
                         attr.span,
-                        "`#[inline]` is ignored on constants",
-                    )
-                    .warn(
-                        "this was previously accepted by the compiler but is \
-                       being phased out; it will become a hard error in \
-                       a future release!",
-                    )
-                    .note(
-                        "see issue #65833 <https://github.com/rust-lang/rust/issues/65833> \
-                         for more information",
-                    )
-                    .emit();
+                        |lint| {
+                            lint.build("`#[inline]` is ignored on constants")
+                            .warn(
+                                "this was previously accepted by the compiler but is \
+                               being phased out; it will become a hard error in \
+                               a future release!",
+                            )
+                            .note(
+                                "see issue #65833 <https://github.com/rust-lang/rust/issues/65833> \
+                                 for more information",
+                            )
+                            .emit();
+                        });
                 true
             }
             _ => {
@@ -336,10 +336,12 @@ impl CheckAttrVisitor<'tcx> {
                     CONFLICTING_REPR_HINTS,
                     hir_id,
                     hint_spans.collect::<Vec<Span>>(),
-                    "conflicting representation hints",
-                )
-                .code(rustc_errors::error_code!(E0566))
-                .emit();
+                    |lint| {
+                        lint.build("conflicting representation hints")
+                            .code(rustc_errors::error_code!(E0566))
+                            .emit();
+                    }
+                );
         }
     }
 
diff --git a/src/librustc_passes/dead.rs b/src/librustc_passes/dead.rs
index 9367909d10c..25b8b8fcd44 100644
--- a/src/librustc_passes/dead.rs
+++ b/src/librustc_passes/dead.rs
@@ -554,12 +554,9 @@ impl DeadVisitor<'tcx> {
         participle: &str,
     ) {
         if !name.as_str().starts_with("_") {
-            self.tcx.lint_hir(
-                lint::builtin::DEAD_CODE,
-                id,
-                span,
-                &format!("{} is never {}: `{}`", node_type, participle, name),
-            );
+            self.tcx.struct_span_lint_hir(lint::builtin::DEAD_CODE, id, span, |lint| {
+                lint.build(&format!("{} is never {}: `{}`", node_type, participle, name)).emit()
+            });
         }
     }
 }
diff --git a/src/librustc_passes/liveness.rs b/src/librustc_passes/liveness.rs
index b355a47c394..27ad953e4fa 100644
--- a/src/librustc_passes/liveness.rs
+++ b/src/librustc_passes/liveness.rs
@@ -1527,39 +1527,41 @@ impl<'tcx> Liveness<'_, 'tcx> {
                         lint::builtin::UNUSED_VARIABLES,
                         hir_id,
                         spans,
-                        &format!("variable `{}` is assigned to, but never used", name),
+                        |lint| {
+                            lint.build(&format!("variable `{}` is assigned to, but never used", name))
+                                .note(&format!("consider using `_{}` instead", name))
+                                .emit();
+                        },
                     )
-                    .note(&format!("consider using `_{}` instead", name))
-                    .emit();
             } else {
-                let mut err = self.ir.tcx.struct_span_lint_hir(
+                self.ir.tcx.struct_span_lint_hir(
                     lint::builtin::UNUSED_VARIABLES,
                     hir_id,
                     spans.clone(),
-                    &format!("unused variable: `{}`", name),
+                    |lint| {
+                        let mut err = lint.build(&format!("unused variable: `{}`", name));
+                        if self.ir.variable_is_shorthand(var) {
+                            if let Node::Binding(pat) = self.ir.tcx.hir().get(hir_id) {
+                                // Handle `ref` and `ref mut`.
+                                let spans =
+                                    spans.iter().map(|_span| (pat.span, format!("{}: _", name))).collect();
+
+                                err.multipart_suggestion(
+                                    "try ignoring the field",
+                                    spans,
+                                    Applicability::MachineApplicable,
+                                );
+                            }
+                        } else {
+                            err.multipart_suggestion(
+                                "consider prefixing with an underscore",
+                                spans.iter().map(|span| (*span, format!("_{}", name))).collect(),
+                                Applicability::MachineApplicable,
+                            );
+                        }
+                        err.emit()
+                    },
                 );
-
-                if self.ir.variable_is_shorthand(var) {
-                    if let Node::Binding(pat) = self.ir.tcx.hir().get(hir_id) {
-                        // Handle `ref` and `ref mut`.
-                        let spans =
-                            spans.iter().map(|_span| (pat.span, format!("{}: _", name))).collect();
-
-                        err.multipart_suggestion(
-                            "try ignoring the field",
-                            spans,
-                            Applicability::MachineApplicable,
-                        );
-                    }
-                } else {
-                    err.multipart_suggestion(
-                        "consider prefixing with an underscore",
-                        spans.iter().map(|span| (*span, format!("_{}", name))).collect(),
-                        Applicability::MachineApplicable,
-                    );
-                }
-
-                err.emit()
             }
         }
     }
@@ -1579,10 +1581,12 @@ impl<'tcx> Liveness<'_, 'tcx> {
                         lint::builtin::UNUSED_ASSIGNMENTS,
                         hir_id,
                         spans,
-                        &format!("value passed to `{}` is never read", name),
+                        |lint| {
+                            lint.build(&format!("value passed to `{}` is never read", name))
+                                .help("maybe it is overwritten before being read?")
+                                .emit();
+                        },
                     )
-                    .help("maybe it is overwritten before being read?")
-                    .emit();
             } else {
                 self.ir
                     .tcx
@@ -1590,10 +1594,12 @@ impl<'tcx> Liveness<'_, 'tcx> {
                         lint::builtin::UNUSED_ASSIGNMENTS,
                         hir_id,
                         spans,
-                        &format!("value assigned to `{}` is never read", name),
+                        |lint| {
+                            lint.build(&format!("value assigned to `{}` is never read", name))
+                                .help("maybe it is overwritten before being read?")
+                                .emit();
+                        },
                     )
-                    .help("maybe it is overwritten before being read?")
-                    .emit();
             }
         }
     }
diff --git a/src/librustc_passes/stability.rs b/src/librustc_passes/stability.rs
index 12debfb66a4..374b7d1cf7a 100644
--- a/src/librustc_passes/stability.rs
+++ b/src/librustc_passes/stability.rs
@@ -604,15 +604,13 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
 }
 
 fn unnecessary_stable_feature_lint(tcx: TyCtxt<'_>, span: Span, feature: Symbol, since: Symbol) {
-    tcx.lint_hir(
-        lint::builtin::STABLE_FEATURES,
-        hir::CRATE_HIR_ID,
-        span,
-        &format!(
+    tcx.struct_span_lint_hir(lint::builtin::STABLE_FEATURES, hir::CRATE_HIR_ID, span, |lint| {
+        lint.build(&format!(
             "the feature `{}` has been stable since {} and no longer requires \
-                  an attribute to enable",
-            feature, since
-        ),
+                      an attribute to enable",
+                feature, since
+            )).emit();
+        }
     );
 }
 
diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index 74bb72d6fad..43f92ae69c4 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -1781,17 +1781,21 @@ impl SearchInterfaceForPrivateItemsVisitor<'tcx> {
 
     fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool {
         if self.leaks_private_dep(def_id) {
-            self.tcx.lint_hir(
+            self.tcx.struct_span_lint_hir(
                 lint::builtin::EXPORTED_PRIVATE_DEPENDENCIES,
                 self.item_id,
                 self.span,
-                &format!(
-                    "{} `{}` from private dependency '{}' in public \
-                                        interface",
-                    kind,
-                    descr,
-                    self.tcx.crate_name(def_id.krate)
-                ),
+                |lint| {
+                    lint.build(
+                            &format!(
+                            "{} `{}` from private dependency '{}' in public \
+                                                interface",
+                            kind,
+                            descr,
+                            self.tcx.crate_name(def_id.krate)
+                            )
+                    ).emit()
+                },
             );
         }
 
@@ -1814,12 +1818,9 @@ impl SearchInterfaceForPrivateItemsVisitor<'tcx> {
                 err.emit();
             } else {
                 let err_code = if kind == "trait" { "E0445" } else { "E0446" };
-                self.tcx.lint_hir(
-                    lint::builtin::PRIVATE_IN_PUBLIC,
-                    hir_id,
-                    self.span,
-                    &format!("{} (error {})", msg, err_code),
-                );
+                self.tcx.struct_span_lint_hir(lint::builtin::PRIVATE_IN_PUBLIC, hir_id, self.span, |lint| {
+                    lint.build(&format!("{} (error {})", msg, err_code)).emit()
+                });
             }
         }
 
diff --git a/src/librustc_resolve/lifetimes.rs b/src/librustc_resolve/lifetimes.rs
index 87522d28d1e..cef5e025741 100644
--- a/src/librustc_resolve/lifetimes.rs
+++ b/src/librustc_resolve/lifetimes.rs
@@ -1575,22 +1575,23 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                             }
                         }
 
-                        let mut err = self.tcx.struct_span_lint_hir(
+                        self.tcx.struct_span_lint_hir(
                             lint::builtin::SINGLE_USE_LIFETIMES,
                             id,
                             span,
-                            &format!("lifetime parameter `{}` only used once", name),
+                            |lint| {
+                                let mut err = lint.build(&format!("lifetime parameter `{}` only used once", name));
+                                if span == lifetime.span {
+                                    // spans are the same for in-band lifetime declarations
+                                    err.span_label(span, "this lifetime is only used here");
+                                } else {
+                                    err.span_label(span, "this lifetime...");
+                                    err.span_label(lifetime.span, "...is used only here");
+                                }
+                                self.suggest_eliding_single_use_lifetime(&mut err, def_id, lifetime);
+                                err.emit();
+                            },
                         );
-
-                        if span == lifetime.span {
-                            // spans are the same for in-band lifetime declarations
-                            err.span_label(span, "this lifetime is only used here");
-                        } else {
-                            err.span_label(span, "this lifetime...");
-                            err.span_label(lifetime.span, "...is used only here");
-                        }
-                        self.suggest_eliding_single_use_lifetime(&mut err, def_id, lifetime);
-                        err.emit();
                     }
                 }
                 Some(LifetimeUseSet::Many) => {
@@ -1610,26 +1611,28 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                         _ => None,
                     } {
                         debug!("id ={:?} span = {:?} name = {:?}", id, span, name);
-                        let mut err = self.tcx.struct_span_lint_hir(
+                        self.tcx.struct_span_lint_hir(
                             lint::builtin::UNUSED_LIFETIMES,
                             id,
                             span,
-                            &format!("lifetime parameter `{}` never used", name),
-                        );
-                        if let Some(parent_def_id) = self.tcx.parent(def_id) {
-                            if let Some(generics) = self.tcx.hir().get_generics(parent_def_id) {
-                                let unused_lt_span = self.lifetime_deletion_span(name, generics);
-                                if let Some(span) = unused_lt_span {
-                                    err.span_suggestion(
-                                        span,
-                                        "elide the unused lifetime",
-                                        String::new(),
-                                        Applicability::MachineApplicable,
-                                    );
+                            |lint| {
+                                let mut err = lint.build(&format!("lifetime parameter `{}` never used", name));
+                                if let Some(parent_def_id) = self.tcx.parent(def_id) {
+                                    if let Some(generics) = self.tcx.hir().get_generics(parent_def_id) {
+                                        let unused_lt_span = self.lifetime_deletion_span(name, generics);
+                                        if let Some(span) = unused_lt_span {
+                                            err.span_suggestion(
+                                                span,
+                                                "elide the unused lifetime",
+                                                String::new(),
+                                                Applicability::MachineApplicable,
+                                            );
+                                        }
+                                    }
                                 }
-                            }
-                        }
-                        err.emit();
+                                err.emit();
+                            },
+                        );
                     }
                 }
             }
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index 2909d0f8c54..c26b47c3130 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -331,11 +331,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 } else {
                     let mut multispan = MultiSpan::from_span(span);
                     multispan.push_span_label(span_late, note.to_string());
-                    tcx.lint_hir(
+                    tcx.struct_span_lint_hir(
                         lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS,
                         args.args[0].id(),
                         multispan,
-                        msg,
+                        |lint| lint.build(msg).emit(),
                     );
                     reported_late_bound_region_err = Some(false);
                 }
@@ -2216,34 +2216,31 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         tcx.check_stability(item.def_id, Some(hir_ref_id), span);
 
         if let Some(variant_def_id) = variant_resolution {
-            let mut err = tcx.struct_span_lint_hir(
-                AMBIGUOUS_ASSOCIATED_ITEMS,
-                hir_ref_id,
-                span,
-                "ambiguous associated item",
-            );
+            tcx.struct_span_lint_hir(AMBIGUOUS_ASSOCIATED_ITEMS, hir_ref_id, span, |lint| {
+                let mut err = lint.build("ambiguous associated item");
+                let mut could_refer_to = |kind: DefKind, def_id, also| {
+                    let note_msg = format!(
+                        "`{}` could{} refer to the {} defined here",
+                        assoc_ident,
+                        also,
+                        kind.descr(def_id)
+                    );
+                    err.span_note(tcx.def_span(def_id), &note_msg);
+                };
 
-            let mut could_refer_to = |kind: DefKind, def_id, also| {
-                let note_msg = format!(
-                    "`{}` could{} refer to the {} defined here",
-                    assoc_ident,
-                    also,
-                    kind.descr(def_id)
+                could_refer_to(DefKind::Variant, variant_def_id, "");
+                could_refer_to(kind, item.def_id, " also");
+
+                err.span_suggestion(
+                    span,
+                    "use fully-qualified syntax",
+                    format!("<{} as {}>::{}", qself_ty, tcx.item_name(trait_did), assoc_ident),
+                    Applicability::MachineApplicable,
                 );
-                err.span_note(tcx.def_span(def_id), &note_msg);
-            };
-            could_refer_to(DefKind::Variant, variant_def_id, "");
-            could_refer_to(kind, item.def_id, " also");
 
-            err.span_suggestion(
-                span,
-                "use fully-qualified syntax",
-                format!("<{} as {}>::{}", qself_ty, tcx.item_name(trait_did), assoc_ident),
-                Applicability::MachineApplicable,
-            )
-            .emit();
+                err.emit();
+            });
         }
-
         Ok((ty, kind, item.def_id))
     }
 
diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs
index d254a84df72..02da2766ec6 100644
--- a/src/librustc_typeck/check/cast.rs
+++ b/src/librustc_typeck/check/cast.rs
@@ -468,23 +468,27 @@ impl<'a, 'tcx> CastCheck<'tcx> {
         } else {
             ("", lint::builtin::TRIVIAL_CASTS)
         };
-        let mut err = fcx.tcx.struct_span_lint_hir(
+        fcx.tcx.struct_span_lint_hir(
             lint,
             self.expr.hir_id,
             self.span,
-            &format!(
-                "trivial {}cast: `{}` as `{}`",
-                adjective,
-                fcx.ty_to_string(t_expr),
-                fcx.ty_to_string(t_cast)
-            ),
+            |err| {
+                err.build(
+                    &format!(
+                        "trivial {}cast: `{}` as `{}`",
+                        adjective,
+                        fcx.ty_to_string(t_expr),
+                        fcx.ty_to_string(t_cast)
+                    )
+                )
+                .help(&format!(
+                    "cast can be replaced by coercion; this might \
+                                   require {}a temporary variable",
+                    type_asc_or
+                ))
+                .emit();
+            },
         );
-        err.help(&format!(
-            "cast can be replaced by coercion; this might \
-                           require {}a temporary variable",
-            type_asc_or
-        ));
-        err.emit();
     }
 
     pub fn check(mut self, fcx: &FnCtxt<'a, 'tcx>) {
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index 497a401a031..599f7dfe2d4 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -382,11 +382,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     )
                     .emit();
                 } else {
-                    self.tcx.lint_hir(
+                    self.tcx.struct_span_lint_hir(
                         lint::builtin::TYVAR_BEHIND_RAW_POINTER,
                         scope_expr_id,
                         span,
-                        "type annotations needed",
+                        |lint| lint.build("type annotations needed").emit(),
                     );
                 }
             } else {
@@ -1280,33 +1280,34 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         stable_pick: &Pick<'_>,
         unstable_candidates: &[(&Candidate<'tcx>, Symbol)],
     ) {
-        let mut diag = self.tcx.struct_span_lint_hir(
+        self.tcx.struct_span_lint_hir(
             lint::builtin::UNSTABLE_NAME_COLLISIONS,
             self.fcx.body_id,
             self.span,
-            "a method with this name may be added to the standard library in the future",
-        );
-
-        // FIXME: This should be a `span_suggestion` instead of `help`
-        // However `self.span` only
-        // highlights the method name, so we can't use it. Also consider reusing the code from
-        // `report_method_error()`.
-        diag.help(&format!(
-            "call with fully qualified syntax `{}(...)` to keep using the current method",
-            self.tcx.def_path_str(stable_pick.item.def_id),
-        ));
-
-        if nightly_options::is_nightly_build() {
-            for (candidate, feature) in unstable_candidates {
+            |lint| {
+                let mut diag = lint.build("a method with this name may be added to the standard library in the future");
+                // FIXME: This should be a `span_suggestion` instead of `help`
+                // However `self.span` only
+                // highlights the method name, so we can't use it. Also consider reusing the code from
+                // `report_method_error()`.
                 diag.help(&format!(
-                    "add `#![feature({})]` to the crate attributes to enable `{}`",
-                    feature,
-                    self.tcx.def_path_str(candidate.item.def_id),
+                    "call with fully qualified syntax `{}(...)` to keep using the current method",
+                    self.tcx.def_path_str(stable_pick.item.def_id),
                 ));
-            }
-        }
 
-        diag.emit();
+                if nightly_options::is_nightly_build() {
+                    for (candidate, feature) in unstable_candidates {
+                        diag.help(&format!(
+                            "add `#![feature({})]` to the crate attributes to enable `{}`",
+                            feature,
+                            self.tcx.def_path_str(candidate.item.def_id),
+                        ));
+                    }
+                }
+
+                diag.emit();
+            },
+        );
     }
 
     fn select_trait_candidate(
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index b82a83171d1..2fff5106dd1 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -2895,15 +2895,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                 debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
 
-                let msg = format!("unreachable {}", kind);
                 self.tcx()
-                    .struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, &msg)
-                    .span_label(span, &msg)
-                    .span_label(
-                        orig_span,
-                        custom_note.unwrap_or("any code following this expression is unreachable"),
-                    )
-                    .emit();
+                    .struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, |lint| {
+                        let msg = format!("unreachable {}", kind);
+                        lint.build(&msg)
+                            .span_label(span, &msg)
+                            .span_label(
+                                orig_span,
+                                custom_note.unwrap_or("any code following this expression is unreachable"),
+                            )
+                            .emit();
+                    })
             }
         }
     }
diff --git a/src/librustc_typeck/check_unused.rs b/src/librustc_typeck/check_unused.rs
index ec098c1d896..71ce5ce0f02 100644
--- a/src/librustc_typeck/check_unused.rs
+++ b/src/librustc_typeck/check_unused.rs
@@ -55,12 +55,14 @@ impl CheckVisitor<'tcx> {
             return;
         }
 
-        let msg = if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
-            format!("unused import: `{}`", snippet)
-        } else {
-            "unused import".to_owned()
-        };
-        self.tcx.lint_hir(lint::builtin::UNUSED_IMPORTS, id, span, &msg);
+        self.tcx.struct_span_lint_hir(lint::builtin::UNUSED_IMPORTS, id, span, |lint| {
+            let msg = if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
+                format!("unused import: `{}`", snippet)
+            } else {
+                "unused import".to_owned()
+            };
+            lint.build(&msg).emit();
+        });
     }
 }
 
@@ -130,14 +132,16 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) {
                     .map(|attr| attr.span)
                     .fold(span, |acc, attr_span| acc.to(attr_span));
 
-                tcx.struct_span_lint_hir(lint, id, span, msg)
-                    .span_suggestion_short(
-                        span_with_attrs,
-                        "remove it",
-                        String::new(),
-                        Applicability::MachineApplicable,
-                    )
-                    .emit();
+                tcx.struct_span_lint_hir(lint, id, span, |lint| {
+                    lint.build(msg)
+                        .span_suggestion_short(
+                            span_with_attrs,
+                            "remove it",
+                            String::new(),
+                            Applicability::MachineApplicable,
+                        )
+                        .emit();
+                });
                 continue;
             }
         }
@@ -170,21 +174,25 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) {
         }
 
         // Otherwise, we can convert it into a `use` of some kind.
-        let msg = "`extern crate` is not idiomatic in the new edition";
-        let help = format!("convert it to a `{}`", visibility_qualified(&item.vis, "use"));
         let base_replacement = match extern_crate.orig_name {
             Some(orig_name) => format!("use {} as {};", orig_name, item.ident.name),
             None => format!("use {};", item.ident.name),
         };
         let replacement = visibility_qualified(&item.vis, base_replacement);
-        tcx.struct_span_lint_hir(lint, id, extern_crate.span, msg)
-            .span_suggestion_short(
-                extern_crate.span,
-                &help,
-                replacement,
-                Applicability::MachineApplicable,
-            )
-            .emit();
+        tcx.struct_span_lint_hir(lint, id, extern_crate.span, |lint| {
+
+            let msg = "`extern crate` is not idiomatic in the new edition";
+            let help = format!("convert it to a `{}`", visibility_qualified(&item.vis, "use"));
+
+            lint.build(msg)
+                .span_suggestion_short(
+                    extern_crate.span,
+                    &help,
+                    replacement,
+                    Applicability::MachineApplicable,
+                )
+                .emit();
+        })
     }
 }
 
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 1a3016cd9d3..d26a4cdc5ca 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -1151,14 +1151,16 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::Generics {
             GenericParamKind::Type { ref default, synthetic, .. } => {
                 if !allow_defaults && default.is_some() {
                     if !tcx.features().default_type_parameter_fallback {
-                        tcx.lint_hir(
+                        tcx.struct_span_lint_hir(
                             lint::builtin::INVALID_TYPE_PARAM_DEFAULT,
                             param.hir_id,
                             param.span,
-                            &format!(
+                            |lint| {
+                                lint.build(&format!(
                                 "defaults for type parameters are only allowed in \
                                         `struct`, `enum`, `type`, or `trait` definitions."
-                            ),
+                                )).emit();
+                            },
                         );
                     }
                 }
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 9ecf6d53129..dfb75c5a98e 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -669,39 +669,41 @@ fn build_diagnostic(
     let attrs = &item.attrs;
     let sp = span_of_attrs(attrs).unwrap_or(item.source.span());
 
-    let mut diag = cx.tcx.struct_span_lint_hir(
+    cx.tcx.struct_span_lint_hir(
         lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE,
         hir_id,
         sp,
-        &format!("`[{}]` {}", path_str, err_msg),
+        |lint| {
+            let mut diag = lint.build(&format!("`[{}]` {}", path_str, err_msg));
+            if let Some(link_range) = link_range {
+                if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs) {
+                    diag.set_span(sp);
+                    diag.span_label(sp, short_err_msg);
+                } else {
+                    // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
+                    //                       ^     ~~~~
+                    //                       |     link_range
+                    //                       last_new_line_offset
+                    let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
+                    let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
+
+                    // Print the line containing the `link_range` and manually mark it with '^'s.
+                    diag.note(&format!(
+                        "the link appears in this line:\n\n{line}\n\
+                         {indicator: <before$}{indicator:^<found$}",
+                        line = line,
+                        indicator = "",
+                        before = link_range.start - last_new_line_offset,
+                        found = link_range.len(),
+                    ));
+                }
+            };
+            if let Some(help_msg) = help_msg {
+                diag.help(help_msg);
+            }
+            diag.emit();
+        },
     );
-    if let Some(link_range) = link_range {
-        if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs) {
-            diag.set_span(sp);
-            diag.span_label(sp, short_err_msg);
-        } else {
-            // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
-            //                       ^     ~~~~
-            //                       |     link_range
-            //                       last_new_line_offset
-            let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
-            let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
-
-            // Print the line containing the `link_range` and manually mark it with '^'s.
-            diag.note(&format!(
-                "the link appears in this line:\n\n{line}\n\
-                 {indicator: <before$}{indicator:^<found$}",
-                line = line,
-                indicator = "",
-                before = link_range.start - last_new_line_offset,
-                found = link_range.len(),
-            ));
-        }
-    };
-    if let Some(help_msg) = help_msg {
-        diag.help(help_msg);
-    }
-    diag.emit();
 }
 
 /// Reports a resolution failure diagnostic.
@@ -766,105 +768,107 @@ fn ambiguity_error(
     let attrs = &item.attrs;
     let sp = span_of_attrs(attrs).unwrap_or(item.source.span());
 
-    let mut msg = format!("`{}` is ", path_str);
-
-    let candidates = [TypeNS, ValueNS, MacroNS]
-        .iter()
-        .filter_map(|&ns| candidates[ns].map(|res| (res, ns)))
-        .collect::<Vec<_>>();
-    match candidates.as_slice() {
-        [(first_def, _), (second_def, _)] => {
-            msg += &format!(
-                "both {} {} and {} {}",
-                first_def.article(),
-                first_def.descr(),
-                second_def.article(),
-                second_def.descr(),
-            );
-        }
-        _ => {
-            let mut candidates = candidates.iter().peekable();
-            while let Some((res, _)) = candidates.next() {
-                if candidates.peek().is_some() {
-                    msg += &format!("{} {}, ", res.article(), res.descr());
-                } else {
-                    msg += &format!("and {} {}", res.article(), res.descr());
-                }
-            }
-        }
-    }
-
-    let mut diag = cx.tcx.struct_span_lint_hir(
+    cx.tcx.struct_span_lint_hir(
         lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE,
         hir_id,
         sp,
-        &msg,
-    );
+        |lint| {
+            let mut msg = format!("`{}` is ", path_str);
 
-    if let Some(link_range) = link_range {
-        if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs) {
-            diag.set_span(sp);
-            diag.span_label(sp, "ambiguous link");
-
-            for (res, ns) in candidates {
-                let (action, mut suggestion) = match res {
-                    Res::Def(DefKind::Method, _) | Res::Def(DefKind::Fn, _) => {
-                        ("add parentheses", format!("{}()", path_str))
-                    }
-                    Res::Def(DefKind::Macro(..), _) => {
-                        ("add an exclamation mark", format!("{}!", path_str))
+            let candidates = [TypeNS, ValueNS, MacroNS]
+                .iter()
+                .filter_map(|&ns| candidates[ns].map(|res| (res, ns)))
+                .collect::<Vec<_>>();
+            match candidates.as_slice() {
+                [(first_def, _), (second_def, _)] => {
+                    msg += &format!(
+                        "both {} {} and {} {}",
+                        first_def.article(),
+                        first_def.descr(),
+                        second_def.article(),
+                        second_def.descr(),
+                    );
+                }
+                _ => {
+                    let mut candidates = candidates.iter().peekable();
+                    while let Some((res, _)) = candidates.next() {
+                        if candidates.peek().is_some() {
+                            msg += &format!("{} {}, ", res.article(), res.descr());
+                        } else {
+                            msg += &format!("and {} {}", res.article(), res.descr());
+                        }
                     }
-                    _ => {
-                        let type_ = match (res, ns) {
-                            (Res::Def(DefKind::Const, _), _) => "const",
-                            (Res::Def(DefKind::Static, _), _) => "static",
-                            (Res::Def(DefKind::Struct, _), _) => "struct",
-                            (Res::Def(DefKind::Enum, _), _) => "enum",
-                            (Res::Def(DefKind::Union, _), _) => "union",
-                            (Res::Def(DefKind::Trait, _), _) => "trait",
-                            (Res::Def(DefKind::Mod, _), _) => "module",
-                            (_, TypeNS) => "type",
-                            (_, ValueNS) => "value",
-                            (_, MacroNS) => "macro",
+                }
+            }
+
+            let mut diag = lint.build(&msg);
+
+            if let Some(link_range) = link_range {
+                if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs) {
+                    diag.set_span(sp);
+                    diag.span_label(sp, "ambiguous link");
+
+                    for (res, ns) in candidates {
+                        let (action, mut suggestion) = match res {
+                            Res::Def(DefKind::Method, _) | Res::Def(DefKind::Fn, _) => {
+                                ("add parentheses", format!("{}()", path_str))
+                            }
+                            Res::Def(DefKind::Macro(..), _) => {
+                                ("add an exclamation mark", format!("{}!", path_str))
+                            }
+                            _ => {
+                                let type_ = match (res, ns) {
+                                    (Res::Def(DefKind::Const, _), _) => "const",
+                                    (Res::Def(DefKind::Static, _), _) => "static",
+                                    (Res::Def(DefKind::Struct, _), _) => "struct",
+                                    (Res::Def(DefKind::Enum, _), _) => "enum",
+                                    (Res::Def(DefKind::Union, _), _) => "union",
+                                    (Res::Def(DefKind::Trait, _), _) => "trait",
+                                    (Res::Def(DefKind::Mod, _), _) => "module",
+                                    (_, TypeNS) => "type",
+                                    (_, ValueNS) => "value",
+                                    (_, MacroNS) => "macro",
+                                };
+
+                                // FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
+                                ("prefix with the item type", format!("{}@{}", type_, path_str))
+                            }
                         };
 
-                        // FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
-                        ("prefix with the item type", format!("{}@{}", type_, path_str))
-                    }
-                };
+                        if dox.bytes().nth(link_range.start) == Some(b'`') {
+                            suggestion = format!("`{}`", suggestion);
+                        }
 
-                if dox.bytes().nth(link_range.start) == Some(b'`') {
-                    suggestion = format!("`{}`", suggestion);
+                        diag.span_suggestion(
+                            sp,
+                            &format!("to link to the {}, {}", res.descr(), action),
+                            suggestion,
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                } else {
+                    // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
+                    //                       ^     ~~~~
+                    //                       |     link_range
+                    //                       last_new_line_offset
+                    let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
+                    let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
+
+                    // Print the line containing the `link_range` and manually mark it with '^'s.
+                    diag.note(&format!(
+                        "the link appears in this line:\n\n{line}\n\
+                         {indicator: <before$}{indicator:^<found$}",
+                        line = line,
+                        indicator = "",
+                        before = link_range.start - last_new_line_offset,
+                        found = link_range.len(),
+                    ));
                 }
-
-                diag.span_suggestion(
-                    sp,
-                    &format!("to link to the {}, {}", res.descr(), action),
-                    suggestion,
-                    Applicability::MaybeIncorrect,
-                );
             }
-        } else {
-            // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
-            //                       ^     ~~~~
-            //                       |     link_range
-            //                       last_new_line_offset
-            let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
-            let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
-
-            // Print the line containing the `link_range` and manually mark it with '^'s.
-            diag.note(&format!(
-                "the link appears in this line:\n\n{line}\n\
-                 {indicator: <before$}{indicator:^<found$}",
-                line = line,
-                indicator = "",
-                before = link_range.start - last_new_line_offset,
-                found = link_range.len(),
-            ));
-        }
-    }
+            diag.emit();
+        },
+    );
 
-    diag.emit();
 }
 
 /// Given an enum variant's res, return the res of its enum and the associated fragment.
diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs
index 355ea15223b..a5213fe8635 100644
--- a/src/librustdoc/passes/mod.rs
+++ b/src/librustdoc/passes/mod.rs
@@ -342,24 +342,22 @@ pub fn look_for_tests<'tcx>(
 
     if check_missing_code == true && tests.found_tests == 0 {
         let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span());
-        let mut diag = cx.tcx.struct_span_lint_hir(
+        cx.tcx.struct_span_lint_hir(
             lint::builtin::MISSING_DOC_CODE_EXAMPLES,
             hir_id,
             sp,
-            "missing code example in this documentation",
+            |lint| lint.build("missing code example in this documentation").emit(),
         );
-        diag.emit();
     } else if check_missing_code == false
         && tests.found_tests > 0
         && !cx.renderinfo.borrow().access_levels.is_public(item.def_id)
     {
-        let mut diag = cx.tcx.struct_span_lint_hir(
+        cx.tcx.struct_span_lint_hir(
             lint::builtin::PRIVATE_DOC_TESTS,
             hir_id,
             span_of_attrs(&item.attrs).unwrap_or(item.source.span()),
-            "documentation test in private item",
+            |lint| lint.build("documentation test in private item").emit(),
         );
-        diag.emit();
     }
 }