about summary refs log tree commit diff
path: root/compiler/rustc_errors/src
diff options
context:
space:
mode:
authorThe Miri Conjob Bot <miri@cron.bot>2023-12-24 05:00:43 +0000
committerThe Miri Conjob Bot <miri@cron.bot>2023-12-24 05:00:43 +0000
commit29f25ee3f381922b39a67089bb07d70bfbe2f17e (patch)
treeb3d97623b0f43b549734a6730ef55f0adf29dbea /compiler/rustc_errors/src
parentf3db65df94bf9a086837f979b06de2c966125c07 (diff)
parentc350d3c5dd00ed0727ffe72fc33637d8c7537733 (diff)
downloadrust-29f25ee3f381922b39a67089bb07d70bfbe2f17e.tar.gz
rust-29f25ee3f381922b39a67089bb07d70bfbe2f17e.zip
Merge from rustc
Diffstat (limited to 'compiler/rustc_errors/src')
-rw-r--r--compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs2
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs79
-rw-r--r--compiler/rustc_errors/src/diagnostic_builder.rs268
-rw-r--r--compiler/rustc_errors/src/diagnostic_impls.rs53
-rw-r--r--compiler/rustc_errors/src/emitter.rs37
-rw-r--r--compiler/rustc_errors/src/json.rs10
-rw-r--r--compiler/rustc_errors/src/lib.rs403
7 files changed, 342 insertions, 510 deletions
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
index da266bf9c63..48e48f59a99 100644
--- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
+++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
@@ -60,7 +60,7 @@ impl Emitter for AnnotateSnippetEmitterWriter {
 
         self.emit_messages_default(
             &diag.level,
-            &diag.message,
+            &diag.messages,
             &fluent_args,
             &diag.code,
             &primary_span,
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index be506806065..c226b2d41bd 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -103,7 +103,7 @@ pub struct Diagnostic {
     // outside of what methods in this crate themselves allow.
     pub(crate) level: Level,
 
-    pub message: Vec<(DiagnosticMessage, Style)>,
+    pub messages: Vec<(DiagnosticMessage, Style)>,
     pub code: Option<DiagnosticId>,
     pub span: MultiSpan,
     pub children: Vec<SubDiagnostic>,
@@ -161,9 +161,8 @@ pub enum DiagnosticId {
 #[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
 pub struct SubDiagnostic {
     pub level: Level,
-    pub message: Vec<(DiagnosticMessage, Style)>,
+    pub messages: Vec<(DiagnosticMessage, Style)>,
     pub span: MultiSpan,
-    pub render_span: Option<MultiSpan>,
 }
 
 #[derive(Debug, PartialEq, Eq)]
@@ -216,14 +215,14 @@ impl StringPart {
 impl Diagnostic {
     #[track_caller]
     pub fn new<M: Into<DiagnosticMessage>>(level: Level, message: M) -> Self {
-        Diagnostic::new_with_code(level, None, message)
+        Diagnostic::new_with_messages(level, vec![(message.into(), Style::NoStyle)])
     }
 
     #[track_caller]
     pub fn new_with_messages(level: Level, messages: Vec<(DiagnosticMessage, Style)>) -> Self {
         Diagnostic {
             level,
-            message: messages,
+            messages,
             code: None,
             span: MultiSpan::new(),
             children: vec![],
@@ -235,26 +234,6 @@ impl Diagnostic {
         }
     }
 
-    #[track_caller]
-    pub(crate) fn new_with_code<M: Into<DiagnosticMessage>>(
-        level: Level,
-        code: Option<DiagnosticId>,
-        message: M,
-    ) -> Self {
-        Diagnostic {
-            level,
-            message: vec![(message.into(), Style::NoStyle)],
-            code,
-            span: MultiSpan::new(),
-            children: vec![],
-            suggestions: Ok(vec![]),
-            args: Default::default(),
-            sort_span: DUMMY_SP,
-            is_lint: false,
-            emitted_at: DiagnosticLocation::caller(),
-        }
-    }
-
     #[inline(always)]
     pub fn level(&self) -> Level {
         self.level
@@ -445,7 +424,7 @@ impl Diagnostic {
     /// Add a note attached to this diagnostic.
     #[rustc_lint_diagnostics]
     pub fn note(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
-        self.sub(Level::Note, msg, MultiSpan::new(), None);
+        self.sub(Level::Note, msg, MultiSpan::new());
         self
     }
 
@@ -453,14 +432,14 @@ impl Diagnostic {
         &mut self,
         msg: Vec<(M, Style)>,
     ) -> &mut Self {
-        self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None);
+        self.sub_with_highlights(Level::Note, msg, MultiSpan::new());
         self
     }
 
     /// Prints the span with a note above it.
     /// This is like [`Diagnostic::note()`], but it gets its own span.
     pub fn note_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
-        self.sub(Level::OnceNote, msg, MultiSpan::new(), None);
+        self.sub(Level::OnceNote, msg, MultiSpan::new());
         self
     }
 
@@ -472,7 +451,7 @@ impl Diagnostic {
         sp: S,
         msg: impl Into<SubdiagnosticMessage>,
     ) -> &mut Self {
-        self.sub(Level::Note, msg, sp.into(), None);
+        self.sub(Level::Note, msg, sp.into());
         self
     }
 
@@ -483,14 +462,14 @@ impl Diagnostic {
         sp: S,
         msg: impl Into<SubdiagnosticMessage>,
     ) -> &mut Self {
-        self.sub(Level::OnceNote, msg, sp.into(), None);
+        self.sub(Level::OnceNote, msg, sp.into());
         self
     }
 
     /// Add a warning attached to this diagnostic.
     #[rustc_lint_diagnostics]
     pub fn warn(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
-        self.sub(Level::Warning(None), msg, MultiSpan::new(), None);
+        self.sub(Level::Warning(None), msg, MultiSpan::new());
         self
     }
 
@@ -502,27 +481,27 @@ impl Diagnostic {
         sp: S,
         msg: impl Into<SubdiagnosticMessage>,
     ) -> &mut Self {
-        self.sub(Level::Warning(None), msg, sp.into(), None);
+        self.sub(Level::Warning(None), msg, sp.into());
         self
     }
 
     /// Add a help message attached to this diagnostic.
     #[rustc_lint_diagnostics]
     pub fn help(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
-        self.sub(Level::Help, msg, MultiSpan::new(), None);
+        self.sub(Level::Help, msg, MultiSpan::new());
         self
     }
 
     /// Prints the span with a help above it.
     /// This is like [`Diagnostic::help()`], but it gets its own span.
     pub fn help_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
-        self.sub(Level::OnceHelp, msg, MultiSpan::new(), None);
+        self.sub(Level::OnceHelp, msg, MultiSpan::new());
         self
     }
 
     /// Add a help message attached to this diagnostic with a customizable highlighted message.
     pub fn highlighted_help(&mut self, msg: Vec<(String, Style)>) -> &mut Self {
-        self.sub_with_highlights(Level::Help, msg, MultiSpan::new(), None);
+        self.sub_with_highlights(Level::Help, msg, MultiSpan::new());
         self
     }
 
@@ -534,7 +513,7 @@ impl Diagnostic {
         sp: S,
         msg: impl Into<SubdiagnosticMessage>,
     ) -> &mut Self {
-        self.sub(Level::Help, msg, sp.into(), None);
+        self.sub(Level::Help, msg, sp.into());
         self
     }
 
@@ -925,7 +904,7 @@ impl Diagnostic {
     }
 
     pub fn set_primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self {
-        self.message[0] = (msg.into(), Style::NoStyle);
+        self.messages[0] = (msg.into(), Style::NoStyle);
         self
     }
 
@@ -952,8 +931,8 @@ impl Diagnostic {
         self.args = args;
     }
 
-    pub fn styled_message(&self) -> &[(DiagnosticMessage, Style)] {
-        &self.message
+    pub fn messages(&self) -> &[(DiagnosticMessage, Style)] {
+        &self.messages
     }
 
     /// Helper function that takes a `SubdiagnosticMessage` and returns a `DiagnosticMessage` by
@@ -964,7 +943,7 @@ impl Diagnostic {
         attr: impl Into<SubdiagnosticMessage>,
     ) -> DiagnosticMessage {
         let msg =
-            self.message.iter().map(|(msg, _)| msg).next().expect("diagnostic with no messages");
+            self.messages.iter().map(|(msg, _)| msg).next().expect("diagnostic with no messages");
         msg.with_subdiagnostic_message(attr.into())
     }
 
@@ -972,21 +951,14 @@ impl Diagnostic {
     /// public methods above.
     ///
     /// Used by `proc_macro_server` for implementing `server::Diagnostic`.
-    pub fn sub(
-        &mut self,
-        level: Level,
-        message: impl Into<SubdiagnosticMessage>,
-        span: MultiSpan,
-        render_span: Option<MultiSpan>,
-    ) {
+    pub fn sub(&mut self, level: Level, message: impl Into<SubdiagnosticMessage>, span: MultiSpan) {
         let sub = SubDiagnostic {
             level,
-            message: vec![(
+            messages: vec![(
                 self.subdiagnostic_message_to_diagnostic_message(message),
                 Style::NoStyle,
             )],
             span,
-            render_span,
         };
         self.children.push(sub);
     }
@@ -996,15 +968,14 @@ impl Diagnostic {
     fn sub_with_highlights<M: Into<SubdiagnosticMessage>>(
         &mut self,
         level: Level,
-        message: Vec<(M, Style)>,
+        messages: Vec<(M, Style)>,
         span: MultiSpan,
-        render_span: Option<MultiSpan>,
     ) {
-        let message = message
+        let messages = messages
             .into_iter()
             .map(|m| (self.subdiagnostic_message_to_diagnostic_message(m.0), m.1))
             .collect();
-        let sub = SubDiagnostic { level, message, span, render_span };
+        let sub = SubDiagnostic { level, messages, span };
         self.children.push(sub);
     }
 
@@ -1022,7 +993,7 @@ impl Diagnostic {
     ) {
         (
             &self.level,
-            &self.message,
+            &self.messages,
             self.args().collect(),
             &self.code,
             &self.span,
diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
index 3f66af1fcff..4703e71523d 100644
--- a/compiler/rustc_errors/src/diagnostic_builder.rs
+++ b/compiler/rustc_errors/src/diagnostic_builder.rs
@@ -15,13 +15,13 @@ use std::ops::{Deref, DerefMut};
 use std::panic;
 use std::thread::panicking;
 
-/// Trait implemented by error types. This should not be implemented manually. Instead, use
+/// Trait implemented by error types. This is rarely implemented manually. Instead, use
 /// `#[derive(Diagnostic)]` -- see [rustc_macros::Diagnostic].
 #[rustc_diagnostic_item = "IntoDiagnostic"]
 pub trait IntoDiagnostic<'a, G: EmissionGuarantee = ErrorGuaranteed> {
     /// Write out as a diagnostic out of `DiagCtxt`.
     #[must_use]
-    fn into_diagnostic(self, dcx: &'a DiagCtxt) -> DiagnosticBuilder<'a, G>;
+    fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G>;
 }
 
 impl<'a, T, G> IntoDiagnostic<'a, G> for Spanned<T>
@@ -29,8 +29,8 @@ where
     T: IntoDiagnostic<'a, G>,
     G: EmissionGuarantee,
 {
-    fn into_diagnostic(self, dcx: &'a DiagCtxt) -> DiagnosticBuilder<'a, G> {
-        let mut diag = self.node.into_diagnostic(dcx);
+    fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> {
+        let mut diag = self.node.into_diagnostic(dcx, level);
         diag.set_span(self.span);
         diag
     }
@@ -43,24 +43,7 @@ where
 /// extending `DiagCtxtFlags`.
 #[must_use]
 #[derive(Clone)]
-pub struct DiagnosticBuilder<'a, G: EmissionGuarantee> {
-    inner: DiagnosticBuilderInner<'a>,
-    _marker: PhantomData<G>,
-}
-
-/// This type exists only for `DiagnosticBuilder::forget_guarantee`, because it:
-/// 1. lacks the `G` parameter and therefore `DiagnosticBuilder<G1>` can be
-///    converted into `DiagnosticBuilder<G2>` while reusing the `inner` field
-/// 2. can implement the `Drop` "bomb" instead of `DiagnosticBuilder`, as it
-///    contains all of the data (`state` + `diagnostic`) of `DiagnosticBuilder`
-///
-/// The `diagnostic` field is not `Copy` and can't be moved out of whichever
-/// type implements the `Drop` "bomb", but because of the above two facts, that
-/// never needs to happen - instead, the whole `inner: DiagnosticBuilderInner`
-/// can be moved out of a `DiagnosticBuilder` and into another.
-#[must_use]
-#[derive(Clone)]
-struct DiagnosticBuilderInner<'a> {
+pub struct DiagnosticBuilder<'a, G: EmissionGuarantee = ErrorGuaranteed> {
     state: DiagnosticBuilderState<'a>,
 
     /// `Diagnostic` is a large type, and `DiagnosticBuilder` is often used as a
@@ -68,6 +51,8 @@ struct DiagnosticBuilderInner<'a> {
     /// In theory, return value optimization (RVO) should avoid unnecessary
     /// copying. In practice, it does not (at the time of writing).
     diagnostic: Box<Diagnostic>,
+
+    _marker: PhantomData<G>,
 }
 
 #[derive(Clone)]
@@ -83,7 +68,7 @@ enum DiagnosticBuilderState<'a> {
     /// assumed that `.emit()` was previously called, to end up in this state.
     ///
     /// While this is also used by `.cancel()`, this state is only observed by
-    /// the `Drop` `impl` of `DiagnosticBuilderInner`, as `.cancel()` takes
+    /// the `Drop` `impl` of `DiagnosticBuilder`, because `.cancel()` takes
     /// `self` by-value specifically to prevent any attempts to `.emit()`.
     ///
     // FIXME(eddyb) currently this doesn't prevent extending the `Diagnostic`,
@@ -101,47 +86,50 @@ rustc_data_structures::static_assert_size!(
 /// Trait for types that `DiagnosticBuilder::emit` can return as a "guarantee"
 /// (or "proof") token that the emission happened.
 pub trait EmissionGuarantee: Sized {
+    /// This exists so that bugs and fatal errors can both result in `!` (an
+    /// abort) when emitted, but have different aborting behaviour.
+    type EmitResult = Self;
+
     /// Implementation of `DiagnosticBuilder::emit`, fully controlled by each
     /// `impl` of `EmissionGuarantee`, to make it impossible to create a value
-    /// of `Self` without actually performing the emission.
+    /// of `Self::EmitResult` without actually performing the emission.
     #[track_caller]
-    fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self;
-
-    /// Creates a new `DiagnosticBuilder` that will return this type of guarantee.
-    #[track_caller]
-    fn make_diagnostic_builder(
-        dcx: &DiagCtxt,
-        msg: impl Into<DiagnosticMessage>,
-    ) -> DiagnosticBuilder<'_, Self>;
+    fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult;
 }
 
-impl<'a> DiagnosticBuilder<'a, ErrorGuaranteed> {
-    /// Discard the guarantee `.emit()` would return, in favor of having the
-    /// type `DiagnosticBuilder<'a, ()>`. This may be necessary whenever there
-    /// is a common codepath handling both errors and warnings.
-    pub fn forget_guarantee(self) -> DiagnosticBuilder<'a, ()> {
-        DiagnosticBuilder { inner: self.inner, _marker: PhantomData }
+impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
+    /// Most `emit_producing_guarantee` functions use this as a starting point.
+    fn emit_producing_nothing(&mut self) {
+        match self.state {
+            // First `.emit()` call, the `&DiagCtxt` is still available.
+            DiagnosticBuilderState::Emittable(dcx) => {
+                self.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
+                dcx.emit_diagnostic_without_consuming(&mut self.diagnostic);
+            }
+            // `.emit()` was previously called, disallowed from repeating it.
+            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
+        }
     }
 }
 
 // FIXME(eddyb) make `ErrorGuaranteed` impossible to create outside `.emit()`.
 impl EmissionGuarantee for ErrorGuaranteed {
-    fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
-        match db.inner.state {
+    fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
+        // Contrast this with `emit_producing_nothing`.
+        match db.state {
             // First `.emit()` call, the `&DiagCtxt` is still available.
             DiagnosticBuilderState::Emittable(dcx) => {
-                db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
-
-                let guar = dcx.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
+                db.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
+                let guar = dcx.emit_diagnostic_without_consuming(&mut db.diagnostic);
 
                 // Only allow a guarantee if the `level` wasn't switched to a
                 // non-error - the field isn't `pub`, but the whole `Diagnostic`
                 // can be overwritten with a new one, thanks to `DerefMut`.
                 assert!(
-                    db.inner.diagnostic.is_error(),
+                    db.diagnostic.is_error(),
                     "emitted non-error ({:?}) diagnostic \
                      from `DiagnosticBuilder<ErrorGuaranteed>`",
-                    db.inner.diagnostic.level,
+                    db.diagnostic.level,
                 );
                 guar.unwrap()
             }
@@ -153,152 +141,58 @@ impl EmissionGuarantee for ErrorGuaranteed {
                 // non-error - the field isn't `pub`, but the whole `Diagnostic`
                 // can be overwritten with a new one, thanks to `DerefMut`.
                 assert!(
-                    db.inner.diagnostic.is_error(),
+                    db.diagnostic.is_error(),
                     "`DiagnosticBuilder<ErrorGuaranteed>`'s diagnostic \
                      became non-error ({:?}), after original `.emit()`",
-                    db.inner.diagnostic.level,
+                    db.diagnostic.level,
                 );
                 #[allow(deprecated)]
                 ErrorGuaranteed::unchecked_claim_error_was_emitted()
             }
         }
     }
-
-    #[track_caller]
-    fn make_diagnostic_builder(
-        dcx: &DiagCtxt,
-        msg: impl Into<DiagnosticMessage>,
-    ) -> DiagnosticBuilder<'_, Self> {
-        DiagnosticBuilder::new(dcx, Level::Error { lint: false }, msg)
-    }
 }
 
 // FIXME(eddyb) should there be a `Option<ErrorGuaranteed>` impl as well?
 impl EmissionGuarantee for () {
-    fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
-        match db.inner.state {
-            // First `.emit()` call, the `&DiagCtxt` is still available.
-            DiagnosticBuilderState::Emittable(dcx) => {
-                db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
-
-                dcx.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
-            }
-            // `.emit()` was previously called, disallowed from repeating it.
-            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
-        }
-    }
-
-    fn make_diagnostic_builder(
-        dcx: &DiagCtxt,
-        msg: impl Into<DiagnosticMessage>,
-    ) -> DiagnosticBuilder<'_, Self> {
-        DiagnosticBuilder::new(dcx, Level::Warning(None), msg)
-    }
-}
-
-/// Marker type which enables implementation of `create_note` and `emit_note` functions for
-/// note-without-error struct diagnostics.
-#[derive(Copy, Clone)]
-pub struct Noted;
-
-impl EmissionGuarantee for Noted {
-    fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
-        match db.inner.state {
-            // First `.emit()` call, the `&DiagCtxt` is still available.
-            DiagnosticBuilderState::Emittable(dcx) => {
-                db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
-                dcx.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
-            }
-            // `.emit()` was previously called, disallowed from repeating it.
-            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
-        }
-
-        Noted
-    }
-
-    fn make_diagnostic_builder(
-        dcx: &DiagCtxt,
-        msg: impl Into<DiagnosticMessage>,
-    ) -> DiagnosticBuilder<'_, Self> {
-        DiagnosticBuilder::new(dcx, Level::Note, msg)
+    fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
+        db.emit_producing_nothing();
     }
 }
 
 /// Marker type which enables implementation of `create_bug` and `emit_bug` functions for
-/// bug struct diagnostics.
+/// bug diagnostics.
 #[derive(Copy, Clone)]
-pub struct Bug;
+pub struct BugAbort;
 
-impl EmissionGuarantee for Bug {
-    fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
-        match db.inner.state {
-            // First `.emit()` call, the `&DiagCtxt` is still available.
-            DiagnosticBuilderState::Emittable(dcx) => {
-                db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
+impl EmissionGuarantee for BugAbort {
+    type EmitResult = !;
 
-                dcx.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
-            }
-            // `.emit()` was previously called, disallowed from repeating it.
-            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
-        }
-        // Then panic. No need to return the marker type.
+    fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
+        db.emit_producing_nothing();
         panic::panic_any(ExplicitBug);
     }
-
-    fn make_diagnostic_builder(
-        dcx: &DiagCtxt,
-        msg: impl Into<DiagnosticMessage>,
-    ) -> DiagnosticBuilder<'_, Self> {
-        DiagnosticBuilder::new(dcx, Level::Bug, msg)
-    }
 }
 
-impl EmissionGuarantee for ! {
-    fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
-        match db.inner.state {
-            // First `.emit()` call, the `&DiagCtxt` is still available.
-            DiagnosticBuilderState::Emittable(dcx) => {
-                db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
+/// Marker type which enables implementation of `create_fatal` and `emit_fatal` functions for
+/// fatal diagnostics.
+#[derive(Copy, Clone)]
+pub struct FatalAbort;
 
-                dcx.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
-            }
-            // `.emit()` was previously called, disallowed from repeating it.
-            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
-        }
-        // Then fatally error, returning `!`
-        crate::FatalError.raise()
-    }
+impl EmissionGuarantee for FatalAbort {
+    type EmitResult = !;
 
-    fn make_diagnostic_builder(
-        dcx: &DiagCtxt,
-        msg: impl Into<DiagnosticMessage>,
-    ) -> DiagnosticBuilder<'_, Self> {
-        DiagnosticBuilder::new(dcx, Level::Fatal, msg)
+    fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
+        db.emit_producing_nothing();
+        crate::FatalError.raise()
     }
 }
 
 impl EmissionGuarantee for rustc_span::fatal_error::FatalError {
-    fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
-        match db.inner.state {
-            // First `.emit()` call, the `&DiagCtxt` is still available.
-            DiagnosticBuilderState::Emittable(dcx) => {
-                db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
-
-                dcx.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
-            }
-            // `.emit()` was previously called, disallowed from repeating it.
-            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
-        }
-        // Then fatally error..
+    fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
+        db.emit_producing_nothing();
         rustc_span::fatal_error::FatalError
     }
-
-    fn make_diagnostic_builder(
-        dcx: &DiagCtxt,
-        msg: impl Into<DiagnosticMessage>,
-    ) -> DiagnosticBuilder<'_, Self> {
-        DiagnosticBuilder::new(dcx, Level::Fatal, msg)
-    }
 }
 
 /// In general, the `DiagnosticBuilder` uses deref to allow access to
@@ -318,7 +212,7 @@ macro_rules! forward {
         $(#[$attrs])*
         #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")]
         pub fn $n(&mut self, $($name: $ty),*) -> &mut Self {
-            self.inner.diagnostic.$n($($name),*);
+            self.diagnostic.$n($($name),*);
             self
         }
     };
@@ -328,27 +222,21 @@ impl<G: EmissionGuarantee> Deref for DiagnosticBuilder<'_, G> {
     type Target = Diagnostic;
 
     fn deref(&self) -> &Diagnostic {
-        &self.inner.diagnostic
+        &self.diagnostic
     }
 }
 
 impl<G: EmissionGuarantee> DerefMut for DiagnosticBuilder<'_, G> {
     fn deref_mut(&mut self) -> &mut Diagnostic {
-        &mut self.inner.diagnostic
+        &mut self.diagnostic
     }
 }
 
 impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
-    /// Convenience function for internal use, clients should use one of the
-    /// `struct_*` methods on [`DiagCtxt`].
+    #[rustc_lint_diagnostics]
     #[track_caller]
-    pub(crate) fn new<M: Into<DiagnosticMessage>>(
-        dcx: &'a DiagCtxt,
-        level: Level,
-        message: M,
-    ) -> Self {
-        let diagnostic = Diagnostic::new(level, message);
-        Self::new_diagnostic(dcx, diagnostic)
+    pub fn new<M: Into<DiagnosticMessage>>(dcx: &'a DiagCtxt, level: Level, message: M) -> Self {
+        Self::new_diagnostic(dcx, Diagnostic::new(level, message))
     }
 
     /// Creates a new `DiagnosticBuilder` with an already constructed
@@ -357,10 +245,8 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
     pub(crate) fn new_diagnostic(dcx: &'a DiagCtxt, diagnostic: Diagnostic) -> Self {
         debug!("Created new diagnostic");
         Self {
-            inner: DiagnosticBuilderInner {
-                state: DiagnosticBuilderState::Emittable(dcx),
-                diagnostic: Box::new(diagnostic),
-            },
+            state: DiagnosticBuilderState::Emittable(dcx),
+            diagnostic: Box::new(diagnostic),
             _marker: PhantomData,
         }
     }
@@ -369,8 +255,8 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
     /// but there are various places that rely on continuing to use `self`
     /// after calling `emit`.
     #[track_caller]
-    pub fn emit(&mut self) -> G {
-        G::diagnostic_builder_emit_producing_guarantee(self)
+    pub fn emit(&mut self) -> G::EmitResult {
+        G::emit_producing_guarantee(self)
     }
 
     /// Emit the diagnostic unless `delay` is true,
@@ -378,7 +264,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
     ///
     /// See `emit` and `delay_as_bug` for details.
     #[track_caller]
-    pub fn emit_unless(&mut self, delay: bool) -> G {
+    pub fn emit_unless(&mut self, delay: bool) -> G::EmitResult {
         if delay {
             self.downgrade_to_delayed_bug();
         }
@@ -392,7 +278,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
     /// which may be expected to *guarantee* the emission of an error, either
     /// at the time of the call, or through a prior `.emit()` call.
     pub fn cancel(mut self) {
-        self.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
+        self.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
         drop(self);
     }
 
@@ -400,17 +286,17 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
     /// later stage of the compiler. The diagnostic can be accessed with
     /// the provided `span` and `key` through [`DiagCtxt::steal_diagnostic()`].
     ///
-    /// As with `buffer`, this is unless the handler has disabled such buffering.
+    /// As with `buffer`, this is unless the dcx has disabled such buffering.
     pub fn stash(self, span: Span, key: StashKey) {
-        if let Some((diag, handler)) = self.into_diagnostic() {
-            handler.stash_diagnostic(span, key, diag);
+        if let Some((diag, dcx)) = self.into_diagnostic() {
+            dcx.stash_diagnostic(span, key, diag);
         }
     }
 
     /// Converts the builder to a `Diagnostic` for later emission,
-    /// unless handler has disabled such buffering, or `.emit()` was called.
+    /// unless dcx has disabled such buffering, or `.emit()` was called.
     pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a DiagCtxt)> {
-        let dcx = match self.inner.state {
+        let dcx = match self.state {
             // No `.emit()` calls, the `&DiagCtxt` is still available.
             DiagnosticBuilderState::Emittable(dcx) => dcx,
             // `.emit()` was previously called, nothing we can do.
@@ -428,7 +314,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
 
         // Take the `Diagnostic` by replacing it with a dummy.
         let dummy = Diagnostic::new(Level::Allow, DiagnosticMessage::from(""));
-        let diagnostic = std::mem::replace(&mut *self.inner.diagnostic, dummy);
+        let diagnostic = std::mem::replace(&mut *self.diagnostic, dummy);
 
         // Disable the ICE on `Drop`.
         self.cancel();
@@ -442,14 +328,14 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
 
     /// Retrieves the [`DiagCtxt`] if available
     pub fn dcx(&self) -> Option<&DiagCtxt> {
-        match self.inner.state {
+        match self.state {
             DiagnosticBuilderState::Emittable(dcx) => Some(dcx),
             DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => None,
         }
     }
 
     /// Buffers the diagnostic for later emission,
-    /// unless handler has disabled such buffering.
+    /// unless dcx has disabled such buffering.
     pub fn buffer(self, buffered_diagnostics: &mut Vec<Diagnostic>) {
         buffered_diagnostics.extend(self.into_diagnostic().map(|(diag, _)| diag));
     }
@@ -465,7 +351,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
     /// In the meantime, though, callsites are required to deal with the "bug"
     /// locally in whichever way makes the most sense.
     #[track_caller]
-    pub fn delay_as_bug(&mut self) -> G {
+    pub fn delay_as_bug(&mut self) -> G::EmitResult {
         self.downgrade_to_delayed_bug();
         self.emit()
     }
@@ -630,13 +516,13 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
 
 impl<G: EmissionGuarantee> Debug for DiagnosticBuilder<'_, G> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.inner.diagnostic.fmt(f)
+        self.diagnostic.fmt(f)
     }
 }
 
 /// Destructor bomb - a `DiagnosticBuilder` must be either emitted or cancelled
 /// or we emit a bug.
-impl Drop for DiagnosticBuilderInner<'_> {
+impl<G: EmissionGuarantee> Drop for DiagnosticBuilder<'_, G> {
     fn drop(&mut self) {
         match self.state {
             // No `.emit()` or `.cancel()` calls.
diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs
index 3e4b3ee758a..29cb304e8b5 100644
--- a/compiler/rustc_errors/src/diagnostic_impls.rs
+++ b/compiler/rustc_errors/src/diagnostic_impls.rs
@@ -1,10 +1,12 @@
 use crate::diagnostic::DiagnosticLocation;
 use crate::{fluent_generated as fluent, AddToDiagnostic};
-use crate::{DiagCtxt, DiagnosticArgValue, DiagnosticBuilder, IntoDiagnostic, IntoDiagnosticArg};
+use crate::{
+    DiagCtxt, DiagnosticArgValue, DiagnosticBuilder, EmissionGuarantee, IntoDiagnostic,
+    IntoDiagnosticArg, Level,
+};
 use rustc_ast as ast;
 use rustc_ast_pretty::pprust;
 use rustc_hir as hir;
-use rustc_lint_defs::Level;
 use rustc_span::edition::Edition;
 use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent, Symbol};
 use rustc_span::Span;
@@ -216,7 +218,7 @@ impl IntoDiagnosticArg for ast::Visibility {
     }
 }
 
-impl IntoDiagnosticArg for Level {
+impl IntoDiagnosticArg for rustc_lint_defs::Level {
     fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
         DiagnosticArgValue::Str(Cow::Borrowed(self.to_cmd_flag()))
     }
@@ -245,19 +247,20 @@ impl<Id> IntoDiagnosticArg for hir::def::Res<Id> {
     }
 }
 
-impl IntoDiagnostic<'_, !> for TargetDataLayoutErrors<'_> {
-    fn into_diagnostic(self, dcx: &DiagCtxt) -> DiagnosticBuilder<'_, !> {
+impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for TargetDataLayoutErrors<'_> {
+    fn into_diagnostic(self, dcx: &DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
         let mut diag;
         match self {
             TargetDataLayoutErrors::InvalidAddressSpace { addr_space, err, cause } => {
-                diag = dcx.struct_fatal(fluent::errors_target_invalid_address_space);
+                diag =
+                    DiagnosticBuilder::new(dcx, level, fluent::errors_target_invalid_address_space);
                 diag.set_arg("addr_space", addr_space);
                 diag.set_arg("cause", cause);
                 diag.set_arg("err", err);
                 diag
             }
             TargetDataLayoutErrors::InvalidBits { kind, bit, cause, err } => {
-                diag = dcx.struct_fatal(fluent::errors_target_invalid_bits);
+                diag = DiagnosticBuilder::new(dcx, level, fluent::errors_target_invalid_bits);
                 diag.set_arg("kind", kind);
                 diag.set_arg("bit", bit);
                 diag.set_arg("cause", cause);
@@ -265,31 +268,39 @@ impl IntoDiagnostic<'_, !> for TargetDataLayoutErrors<'_> {
                 diag
             }
             TargetDataLayoutErrors::MissingAlignment { cause } => {
-                diag = dcx.struct_fatal(fluent::errors_target_missing_alignment);
+                diag = DiagnosticBuilder::new(dcx, level, fluent::errors_target_missing_alignment);
                 diag.set_arg("cause", cause);
                 diag
             }
             TargetDataLayoutErrors::InvalidAlignment { cause, err } => {
-                diag = dcx.struct_fatal(fluent::errors_target_invalid_alignment);
+                diag = DiagnosticBuilder::new(dcx, level, fluent::errors_target_invalid_alignment);
                 diag.set_arg("cause", cause);
                 diag.set_arg("err_kind", err.diag_ident());
                 diag.set_arg("align", err.align());
                 diag
             }
             TargetDataLayoutErrors::InconsistentTargetArchitecture { dl, target } => {
-                diag = dcx.struct_fatal(fluent::errors_target_inconsistent_architecture);
+                diag = DiagnosticBuilder::new(
+                    dcx,
+                    level,
+                    fluent::errors_target_inconsistent_architecture,
+                );
                 diag.set_arg("dl", dl);
                 diag.set_arg("target", target);
                 diag
             }
             TargetDataLayoutErrors::InconsistentTargetPointerWidth { pointer_size, target } => {
-                diag = dcx.struct_fatal(fluent::errors_target_inconsistent_pointer_width);
+                diag = DiagnosticBuilder::new(
+                    dcx,
+                    level,
+                    fluent::errors_target_inconsistent_pointer_width,
+                );
                 diag.set_arg("pointer_size", pointer_size);
                 diag.set_arg("target", target);
                 diag
             }
             TargetDataLayoutErrors::InvalidBitsSize { err } => {
-                diag = dcx.struct_fatal(fluent::errors_target_invalid_bits_size);
+                diag = DiagnosticBuilder::new(dcx, level, fluent::errors_target_invalid_bits_size);
                 diag.set_arg("err", err);
                 diag
             }
@@ -301,25 +312,13 @@ impl IntoDiagnostic<'_, !> for TargetDataLayoutErrors<'_> {
 pub struct SingleLabelManySpans {
     pub spans: Vec<Span>,
     pub label: &'static str,
-    pub kind: LabelKind,
 }
 impl AddToDiagnostic for SingleLabelManySpans {
     fn add_to_diagnostic_with<F>(self, diag: &mut crate::Diagnostic, _: F) {
-        match self.kind {
-            LabelKind::Note => diag.span_note(self.spans, self.label),
-            LabelKind::Label => diag.span_labels(self.spans, self.label),
-            LabelKind::Help => diag.span_help(self.spans, self.label),
-        };
+        diag.span_labels(self.spans, self.label);
     }
 }
 
-/// The kind of label to attach when using [`SingleLabelManySpans`]
-pub enum LabelKind {
-    Note,
-    Label,
-    Help,
-}
-
 #[derive(Subdiagnostic)]
 #[label(errors_expected_lifetime_parameter)]
 pub struct ExpectedLifetimeParameter {
@@ -362,9 +361,9 @@ impl IntoDiagnosticArg for Backtrace {
 pub struct InvalidFlushedDelayedDiagnosticLevel {
     #[primary_span]
     pub span: Span,
-    pub level: rustc_errors::Level,
+    pub level: Level,
 }
-impl IntoDiagnosticArg for rustc_errors::Level {
+impl IntoDiagnosticArg for Level {
     fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
         DiagnosticArgValue::Str(Cow::from(self.to_string()))
     }
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 3fb993c3651..546159c9d13 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -350,9 +350,8 @@ pub trait Emitter: Translate {
 
                 children.push(SubDiagnostic {
                     level: Level::Note,
-                    message: vec![(DiagnosticMessage::from(msg), Style::NoStyle)],
+                    messages: vec![(DiagnosticMessage::from(msg), Style::NoStyle)],
                     span: MultiSpan::new(),
-                    render_span: None,
                 });
             }
         }
@@ -533,7 +532,7 @@ impl Emitter for EmitterWriter {
 
         self.emit_messages_default(
             &diag.level,
-            &diag.message,
+            &diag.messages,
             &fluent_args,
             &diag.code,
             &primary_span,
@@ -1228,10 +1227,10 @@ impl EmitterWriter {
 
     /// Adds a left margin to every line but the first, given a padding length and the label being
     /// displayed, keeping the provided highlighting.
-    fn msg_to_buffer(
+    fn msgs_to_buffer(
         &self,
         buffer: &mut StyledBuffer,
-        msg: &[(DiagnosticMessage, Style)],
+        msgs: &[(DiagnosticMessage, Style)],
         args: &FluentArgs<'_>,
         padding: usize,
         label: &str,
@@ -1267,7 +1266,7 @@ impl EmitterWriter {
 
         // Provided the following diagnostic message:
         //
-        //     let msg = vec![
+        //     let msgs = vec![
         //       ("
         //       ("highlighted multiline\nstring to\nsee how it ", Style::NoStyle),
         //       ("looks", Style::Highlight),
@@ -1284,7 +1283,7 @@ impl EmitterWriter {
         //                see how it *looks* with
         //                very *weird* formats
         //                see?
-        for (text, style) in msg.iter() {
+        for (text, style) in msgs.iter() {
             let text = self.translate_message(text, args).map_err(Report::new).unwrap();
             let text = &normalize_whitespace(&text);
             let lines = text.split('\n').collect::<Vec<_>>();
@@ -1303,10 +1302,10 @@ impl EmitterWriter {
     }
 
     #[instrument(level = "trace", skip(self, args), ret)]
-    fn emit_message_default(
+    fn emit_messages_default_inner(
         &mut self,
         msp: &MultiSpan,
-        msg: &[(DiagnosticMessage, Style)],
+        msgs: &[(DiagnosticMessage, Style)],
         args: &FluentArgs<'_>,
         code: &Option<DiagnosticId>,
         level: &Level,
@@ -1327,7 +1326,7 @@ impl EmitterWriter {
                 buffer.append(0, level.to_str(), Style::MainHeaderMsg);
                 buffer.append(0, ": ", Style::NoStyle);
             }
-            self.msg_to_buffer(&mut buffer, msg, args, max_line_num_len, "note", None);
+            self.msgs_to_buffer(&mut buffer, msgs, args, max_line_num_len, "note", None);
         } else {
             let mut label_width = 0;
             // The failure note level itself does not provide any useful diagnostic information
@@ -1360,7 +1359,7 @@ impl EmitterWriter {
                 buffer.append(0, ": ", header_style);
                 label_width += 2;
             }
-            for (text, _) in msg.iter() {
+            for (text, _) in msgs.iter() {
                 let text = self.translate_message(text, args).map_err(Report::new).unwrap();
                 // Account for newlines to align output to its label.
                 for (line, text) in normalize_whitespace(&text).lines().enumerate() {
@@ -1747,7 +1746,7 @@ impl EmitterWriter {
         buffer.append(0, level.to_str(), Style::Level(*level));
         buffer.append(0, ": ", Style::HeaderMsg);
 
-        self.msg_to_buffer(
+        self.msgs_to_buffer(
             &mut buffer,
             &[(suggestion.msg.to_owned(), Style::NoStyle)],
             args,
@@ -2074,7 +2073,7 @@ impl EmitterWriter {
     fn emit_messages_default(
         &mut self,
         level: &Level,
-        message: &[(DiagnosticMessage, Style)],
+        messages: &[(DiagnosticMessage, Style)],
         args: &FluentArgs<'_>,
         code: &Option<DiagnosticId>,
         span: &MultiSpan,
@@ -2089,9 +2088,9 @@ impl EmitterWriter {
             num_decimal_digits(n)
         };
 
-        match self.emit_message_default(
+        match self.emit_messages_default_inner(
             span,
-            message,
+            messages,
             args,
             code,
             level,
@@ -2118,10 +2117,10 @@ impl EmitterWriter {
                 }
                 if !self.short_message {
                     for child in children {
-                        let span = child.render_span.as_ref().unwrap_or(&child.span);
-                        if let Err(err) = self.emit_message_default(
+                        let span = &child.span;
+                        if let Err(err) = self.emit_messages_default_inner(
                             span,
-                            &child.message,
+                            &child.messages,
                             args,
                             &None,
                             &child.level,
@@ -2138,7 +2137,7 @@ impl EmitterWriter {
                                 // do not display this suggestion, it is meant only for tools
                             }
                             SuggestionStyle::HideCodeAlways => {
-                                if let Err(e) = self.emit_message_default(
+                                if let Err(e) = self.emit_messages_default_inner(
                                     &MultiSpan::new(),
                                     &[(sugg.msg.to_owned(), Style::HeaderMsg)],
                                     args,
diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs
index aa3749334d9..52fcb50e9fb 100644
--- a/compiler/rustc_errors/src/json.rs
+++ b/compiler/rustc_errors/src/json.rs
@@ -398,7 +398,7 @@ impl Diagnostic {
         let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap();
         let output = String::from_utf8(output).unwrap();
 
-        let translated_message = je.translate_messages(&diag.message, &args);
+        let translated_message = je.translate_messages(&diag.messages, &args);
         Diagnostic {
             message: translated_message.to_string(),
             code: DiagnosticCode::map_opt_string(diag.code.clone(), je),
@@ -419,16 +419,12 @@ impl Diagnostic {
         args: &FluentArgs<'_>,
         je: &JsonEmitter,
     ) -> Diagnostic {
-        let translated_message = je.translate_messages(&diag.message, args);
+        let translated_message = je.translate_messages(&diag.messages, args);
         Diagnostic {
             message: translated_message.to_string(),
             code: None,
             level: diag.level.to_str(),
-            spans: diag
-                .render_span
-                .as_ref()
-                .map(|sp| DiagnosticSpan::from_multispan(sp, args, je))
-                .unwrap_or_else(|| DiagnosticSpan::from_multispan(&diag.span, args, je)),
+            spans: DiagnosticSpan::from_multispan(&diag.span, args, je),
             children: vec![],
             rendered: None,
         }
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 959e26fec70..7a1faac04d3 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -6,6 +6,7 @@
 #![doc(rust_logo)]
 #![feature(rustdoc_internals)]
 #![feature(array_windows)]
+#![feature(associated_type_defaults)]
 #![feature(extract_if)]
 #![feature(if_let_guard)]
 #![feature(let_chains)]
@@ -26,26 +27,42 @@ extern crate tracing;
 
 extern crate self as rustc_errors;
 
+pub use diagnostic::{
+    AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId,
+    DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic,
+};
+pub use diagnostic_builder::{
+    BugAbort, DiagnosticBuilder, EmissionGuarantee, FatalAbort, IntoDiagnostic,
+};
+pub use diagnostic_impls::{
+    DiagnosticArgFromDisplay, DiagnosticSymbolList, ExpectedLifetimeParameter,
+    IndicateAnonymousLifetime, InvalidFlushedDelayedDiagnosticLevel, SingleLabelManySpans,
+};
 pub use emitter::ColorConfig;
+pub use rustc_error_messages::{
+    fallback_fluent_bundle, fluent_bundle, DelayDm, DiagnosticMessage, FluentBundle,
+    LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel, SubdiagnosticMessage,
+};
+pub use rustc_lint_defs::{pluralize, Applicability};
+pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker};
+pub use rustc_span::ErrorGuaranteed;
+pub use snippet::Style;
 
-use rustc_lint_defs::LintExpectationId;
-use Level::*;
+// Used by external projects such as `rust-gpu`.
+// See https://github.com/rust-lang/rust/pull/115393.
+pub use termcolor::{Color, ColorSpec, WriteColor};
 
+use crate::diagnostic_impls::{DelayedAtWithNewline, DelayedAtWithoutNewline};
 use emitter::{is_case_difference, DynEmitter, Emitter, EmitterWriter};
 use registry::Registry;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_data_structures::stable_hasher::{Hash128, StableHasher};
 use rustc_data_structures::sync::{Lock, Lrc};
 use rustc_data_structures::AtomicRef;
-pub use rustc_error_messages::{
-    fallback_fluent_bundle, fluent_bundle, DelayDm, DiagnosticMessage, FluentBundle,
-    LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel, SubdiagnosticMessage,
-};
-pub use rustc_lint_defs::{pluralize, Applicability};
+use rustc_lint_defs::LintExpectationId;
 use rustc_span::source_map::SourceMap;
-pub use rustc_span::ErrorGuaranteed;
 use rustc_span::{Loc, Span, DUMMY_SP};
-
+use std::backtrace::{Backtrace, BacktraceStatus};
 use std::borrow::Cow;
 use std::error::Report;
 use std::fmt;
@@ -55,9 +72,7 @@ use std::num::NonZeroUsize;
 use std::panic;
 use std::path::{Path, PathBuf};
 
-// Used by external projects such as `rust-gpu`.
-// See https://github.com/rust-lang/rust/pull/115393.
-pub use termcolor::{Color, ColorSpec, WriteColor};
+use Level::*;
 
 pub mod annotate_snippet_emitter_writer;
 mod diagnostic;
@@ -75,10 +90,7 @@ mod styled_buffer;
 mod tests;
 pub mod translation;
 
-pub use diagnostic_builder::IntoDiagnostic;
-pub use snippet::Style;
-
-pub type PErr<'a> = DiagnosticBuilder<'a, ErrorGuaranteed>;
+pub type PErr<'a> = DiagnosticBuilder<'a>;
 pub type PResult<'a, T> = Result<T, PErr<'a>>;
 
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
@@ -162,7 +174,7 @@ pub struct SubstitutionPart {
 /// Used to translate between `Span`s and byte positions within a single output line in highlighted
 /// code of structured suggestions.
 #[derive(Debug, Clone, Copy)]
-pub struct SubstitutionHighlight {
+pub(crate) struct SubstitutionHighlight {
     start: usize,
     end: usize,
 }
@@ -189,7 +201,7 @@ impl SubstitutionPart {
 impl CodeSuggestion {
     /// Returns the assembled code suggestions, whether they should be shown with an underline
     /// and whether the substitution only differs in capitalization.
-    pub fn splice_lines(
+    pub(crate) fn splice_lines(
         &self,
         sm: &SourceMap,
     ) -> Vec<(String, Vec<SubstitutionPart>, Vec<Vec<SubstitutionHighlight>>, bool)> {
@@ -386,8 +398,6 @@ impl CodeSuggestion {
     }
 }
 
-pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker};
-
 /// Signifies that the compiler died with an explicit call to `.bug`
 /// or `.span_bug` rather than a failed assertion, etc.
 pub struct ExplicitBug;
@@ -396,20 +406,7 @@ pub struct ExplicitBug;
 /// rather than a failed assertion, etc.
 pub struct DelayedBugPanic;
 
-use crate::diagnostic_impls::{DelayedAtWithNewline, DelayedAtWithoutNewline};
-pub use diagnostic::{
-    AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId,
-    DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic,
-};
-pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee, Noted};
-pub use diagnostic_impls::{
-    DiagnosticArgFromDisplay, DiagnosticSymbolList, ExpectedLifetimeParameter,
-    IndicateAnonymousLifetime, InvalidFlushedDelayedDiagnosticLevel, LabelKind,
-    SingleLabelManySpans,
-};
-use std::backtrace::{Backtrace, BacktraceStatus};
-
-/// A handler deals with errors and other compiler output.
+/// A `DiagCtxt` deals with errors and other compiler output.
 /// Certain errors (fatal, bug, unimpl) may cause immediate exit,
 /// others log errors for later reporting.
 pub struct DiagCtxt {
@@ -446,7 +443,7 @@ struct DiagCtxtInner {
     emitted_diagnostic_codes: FxIndexSet<DiagnosticId>,
 
     /// This set contains a hash of every diagnostic that has been emitted by
-    /// this handler. These hashes is used to avoid emitting the same error
+    /// this `DiagCtxt`. These hashes is used to avoid emitting the same error
     /// twice.
     emitted_diagnostics: FxHashSet<Hash128>,
 
@@ -676,7 +673,7 @@ impl DiagCtxt {
         let key = (span.with_parent(None), key);
 
         if diag.is_error() {
-            if matches!(diag.level, Level::Error { lint: true }) {
+            if matches!(diag.level, Error { lint: true }) {
                 inner.lint_err_count += 1;
             } else {
                 inner.err_count += 1;
@@ -700,7 +697,7 @@ impl DiagCtxt {
         let key = (span.with_parent(None), key);
         let diag = inner.stashed_diagnostics.remove(&key)?;
         if diag.is_error() {
-            if matches!(diag.level, Level::Error { lint: true }) {
+            if matches!(diag.level, Error { lint: true }) {
                 inner.lint_err_count -= 1;
             } else {
                 inner.err_count -= 1;
@@ -722,21 +719,6 @@ impl DiagCtxt {
         self.inner.borrow_mut().emit_stashed_diagnostics()
     }
 
-    /// Construct a builder with the `msg` at the level appropriate for the
-    /// specific `EmissionGuarantee`.
-    ///
-    /// Note: this is necessary for `derive(Diagnostic)`, but shouldn't be used
-    /// outside of that. Instead use `struct_err`, `struct_warn`, etc., which
-    /// make the diagnostic kind clearer.
-    #[rustc_lint_diagnostics]
-    #[track_caller]
-    pub fn struct_diagnostic<G: EmissionGuarantee>(
-        &self,
-        msg: impl Into<DiagnosticMessage>,
-    ) -> DiagnosticBuilder<'_, G> {
-        G::make_diagnostic_builder(self, msg)
-    }
-
     /// Construct a builder at the `Warning` level at the given `span` and with the `msg`.
     ///
     /// Attempting to `.emit()` the builder will only emit if either:
@@ -755,37 +737,6 @@ impl DiagCtxt {
     }
 
     /// Construct a builder at the `Warning` level at the given `span` and with the `msg`.
-    /// The `id` is used for lint emissions which should also fulfill a lint expectation.
-    ///
-    /// Attempting to `.emit()` the builder will only emit if either:
-    /// * `can_emit_warnings` is `true`
-    /// * `is_force_warn` was set in `DiagnosticId::Lint`
-    #[track_caller]
-    pub fn struct_span_warn_with_expectation(
-        &self,
-        span: impl Into<MultiSpan>,
-        msg: impl Into<DiagnosticMessage>,
-        id: LintExpectationId,
-    ) -> DiagnosticBuilder<'_, ()> {
-        let mut result = self.struct_warn_with_expectation(msg, id);
-        result.set_span(span);
-        result
-    }
-
-    /// Construct a builder at the `Allow` level at the given `span` and with the `msg`.
-    #[rustc_lint_diagnostics]
-    #[track_caller]
-    pub fn struct_span_allow(
-        &self,
-        span: impl Into<MultiSpan>,
-        msg: impl Into<DiagnosticMessage>,
-    ) -> DiagnosticBuilder<'_, ()> {
-        let mut result = self.struct_allow(msg);
-        result.set_span(span);
-        result
-    }
-
-    /// Construct a builder at the `Warning` level at the given `span` and with the `msg`.
     /// Also include a code.
     #[rustc_lint_diagnostics]
     #[track_caller]
@@ -808,29 +759,14 @@ impl DiagCtxt {
     #[rustc_lint_diagnostics]
     #[track_caller]
     pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
-        DiagnosticBuilder::new(self, Level::Warning(None), msg)
-    }
-
-    /// Construct a builder at the `Warning` level with the `msg`. The `id` is used for
-    /// lint emissions which should also fulfill a lint expectation.
-    ///
-    /// Attempting to `.emit()` the builder will only emit if either:
-    /// * `can_emit_warnings` is `true`
-    /// * `is_force_warn` was set in `DiagnosticId::Lint`
-    #[track_caller]
-    pub fn struct_warn_with_expectation(
-        &self,
-        msg: impl Into<DiagnosticMessage>,
-        id: LintExpectationId,
-    ) -> DiagnosticBuilder<'_, ()> {
-        DiagnosticBuilder::new(self, Level::Warning(Some(id)), msg)
+        DiagnosticBuilder::new(self, Warning(None), msg)
     }
 
     /// Construct a builder at the `Allow` level with the `msg`.
     #[rustc_lint_diagnostics]
     #[track_caller]
     pub fn struct_allow(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
-        DiagnosticBuilder::new(self, Level::Allow, msg)
+        DiagnosticBuilder::new(self, Allow, msg)
     }
 
     /// Construct a builder at the `Expect` level with the `msg`.
@@ -841,7 +777,7 @@ impl DiagCtxt {
         msg: impl Into<DiagnosticMessage>,
         id: LintExpectationId,
     ) -> DiagnosticBuilder<'_, ()> {
-        DiagnosticBuilder::new(self, Level::Expect(id), msg)
+        DiagnosticBuilder::new(self, Expect(id), msg)
     }
 
     /// Construct a builder at the `Error` level at the given `span` and with the `msg`.
@@ -851,7 +787,7 @@ impl DiagCtxt {
         &self,
         span: impl Into<MultiSpan>,
         msg: impl Into<DiagnosticMessage>,
-    ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'_> {
         let mut result = self.struct_err(msg);
         result.set_span(span);
         result
@@ -865,7 +801,7 @@ impl DiagCtxt {
         span: impl Into<MultiSpan>,
         msg: impl Into<DiagnosticMessage>,
         code: DiagnosticId,
-    ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'_> {
         let mut result = self.struct_span_err(span, msg);
         result.code(code);
         result
@@ -875,18 +811,8 @@ impl DiagCtxt {
     // FIXME: This method should be removed (every error should have an associated error code).
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn struct_err(
-        &self,
-        msg: impl Into<DiagnosticMessage>,
-    ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
-        DiagnosticBuilder::new(self, Level::Error { lint: false }, msg)
-    }
-
-    /// This should only be used by `rustc_middle::lint::struct_lint_level`. Do not use it for hard errors.
-    #[doc(hidden)]
-    #[track_caller]
-    pub fn struct_err_lint(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
-        DiagnosticBuilder::new(self, Level::Error { lint: true }, msg)
+    pub fn struct_err(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_> {
+        DiagnosticBuilder::new(self, Error { lint: false }, msg)
     }
 
     /// Construct a builder at the `Error` level with the `msg` and the `code`.
@@ -896,7 +822,7 @@ impl DiagCtxt {
         &self,
         msg: impl Into<DiagnosticMessage>,
         code: DiagnosticId,
-    ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+    ) -> DiagnosticBuilder<'_> {
         let mut result = self.struct_err(msg);
         result.code(code);
         result
@@ -922,7 +848,7 @@ impl DiagCtxt {
         &self,
         span: impl Into<MultiSpan>,
         msg: impl Into<DiagnosticMessage>,
-    ) -> DiagnosticBuilder<'_, !> {
+    ) -> DiagnosticBuilder<'_, FatalAbort> {
         let mut result = self.struct_fatal(msg);
         result.set_span(span);
         result
@@ -936,7 +862,7 @@ impl DiagCtxt {
         span: impl Into<MultiSpan>,
         msg: impl Into<DiagnosticMessage>,
         code: DiagnosticId,
-    ) -> DiagnosticBuilder<'_, !> {
+    ) -> DiagnosticBuilder<'_, FatalAbort> {
         let mut result = self.struct_span_fatal(span, msg);
         result.code(code);
         result
@@ -945,8 +871,11 @@ impl DiagCtxt {
     /// Construct a builder at the `Fatal` level with the `msg`.
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> {
-        DiagnosticBuilder::new(self, Level::Fatal, msg)
+    pub fn struct_fatal(
+        &self,
+        msg: impl Into<DiagnosticMessage>,
+    ) -> DiagnosticBuilder<'_, FatalAbort> {
+        DiagnosticBuilder::new(self, Fatal, msg)
     }
 
     /// Construct a builder at the `Fatal` level with the `msg`, that doesn't abort.
@@ -956,20 +885,40 @@ impl DiagCtxt {
         &self,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'_, FatalError> {
-        DiagnosticBuilder::new(self, Level::Fatal, msg)
+        DiagnosticBuilder::new(self, Fatal, msg)
     }
 
     /// Construct a builder at the `Help` level with the `msg`.
     #[rustc_lint_diagnostics]
     pub fn struct_help(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
-        DiagnosticBuilder::new(self, Level::Help, msg)
+        DiagnosticBuilder::new(self, Help, msg)
     }
 
     /// Construct a builder at the `Note` level with the `msg`.
     #[rustc_lint_diagnostics]
     #[track_caller]
     pub fn struct_note(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
-        DiagnosticBuilder::new(self, Level::Note, msg)
+        DiagnosticBuilder::new(self, Note, msg)
+    }
+
+    /// Construct a builder at the `Bug` level with the `msg`.
+    #[rustc_lint_diagnostics]
+    #[track_caller]
+    pub fn struct_bug(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, BugAbort> {
+        DiagnosticBuilder::new(self, Bug, msg)
+    }
+
+    /// Construct a builder at the `Bug` level at the given `span` with the `msg`.
+    #[rustc_lint_diagnostics]
+    #[track_caller]
+    pub fn struct_span_bug(
+        &self,
+        span: impl Into<MultiSpan>,
+        msg: impl Into<DiagnosticMessage>,
+    ) -> DiagnosticBuilder<'_, BugAbort> {
+        let mut result = self.struct_bug(msg);
+        result.set_span(span);
+        result
     }
 
     #[rustc_lint_diagnostics]
@@ -1028,7 +977,7 @@ impl DiagCtxt {
     }
 
     pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! {
-        self.inner.borrow_mut().span_bug(span, msg)
+        self.struct_span_bug(span, msg).emit()
     }
 
     /// For documentation on this, see `Session::span_delayed_bug`.
@@ -1041,20 +990,14 @@ impl DiagCtxt {
         sp: impl Into<MultiSpan>,
         msg: impl Into<DiagnosticMessage>,
     ) -> ErrorGuaranteed {
-        let mut inner = self.inner.borrow_mut();
-
-        // This is technically `self.treat_err_as_bug()` but `span_delayed_bug` is called before
-        // incrementing `err_count` by one, so we need to +1 the comparing.
-        // FIXME: Would be nice to increment err_count in a more coherent way.
-        if inner.flags.treat_err_as_bug.is_some_and(|c| {
-            inner.err_count + inner.lint_err_count + inner.delayed_bug_count() + 1 >= c.get()
-        }) {
+        let treat_next_err_as_bug = self.inner.borrow().treat_next_err_as_bug();
+        if treat_next_err_as_bug {
             // FIXME: don't abort here if report_delayed_bugs is off
-            inner.span_bug(sp, msg);
+            self.span_bug(sp, msg);
         }
-        let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg);
+        let mut diagnostic = Diagnostic::new(DelayedBug, msg);
         diagnostic.set_span(sp);
-        inner.emit_diagnostic(diagnostic).unwrap()
+        self.emit_diagnostic(diagnostic).unwrap()
     }
 
     // FIXME(eddyb) note the comment inside `impl Drop for DiagCtxtInner`, that's
@@ -1062,7 +1005,7 @@ impl DiagCtxt {
     pub fn good_path_delayed_bug(&self, msg: impl Into<DiagnosticMessage>) {
         let mut inner = self.inner.borrow_mut();
 
-        let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg);
+        let mut diagnostic = Diagnostic::new(DelayedBug, msg);
         if inner.flags.report_delayed_bugs {
             inner.emit_diagnostic_without_consuming(&mut diagnostic);
         }
@@ -1071,13 +1014,6 @@ impl DiagCtxt {
     }
 
     #[track_caller]
-    pub fn span_bug_no_panic(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) {
-        let mut diag = Diagnostic::new(Bug, msg);
-        diag.set_span(span);
-        self.emit_diagnostic(diag);
-    }
-
-    #[track_caller]
     #[rustc_lint_diagnostics]
     pub fn span_note(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) {
         self.struct_span_note(span, msg).emit()
@@ -1115,9 +1051,9 @@ impl DiagCtxt {
         self.struct_note(msg).emit()
     }
 
+    #[rustc_lint_diagnostics]
     pub fn bug(&self, msg: impl Into<DiagnosticMessage>) -> ! {
-        DiagnosticBuilder::<diagnostic_builder::Bug>::new(self, Bug, msg).emit();
-        panic::panic_any(ExplicitBug);
+        self.struct_bug(msg).emit()
     }
 
     #[inline]
@@ -1182,10 +1118,9 @@ impl DiagCtxt {
 
         match (errors.len(), warnings.len()) {
             (0, 0) => return,
-            (0, _) => inner.emitter.emit_diagnostic(&Diagnostic::new(
-                Level::Warning(None),
-                DiagnosticMessage::Str(warnings),
-            )),
+            (0, _) => inner
+                .emitter
+                .emit_diagnostic(&Diagnostic::new(Warning(None), DiagnosticMessage::Str(warnings))),
             (_, 0) => {
                 inner.emit_diagnostic(Diagnostic::new(Fatal, errors));
             }
@@ -1217,14 +1152,12 @@ impl DiagCtxt {
                         if error_codes.len() > 9 { "..." } else { "." }
                     ));
                     inner.failure_note(format!(
-                        "For more information about an error, try \
-                         `rustc --explain {}`.",
+                        "For more information about an error, try `rustc --explain {}`.",
                         &error_codes[0]
                     ));
                 } else {
                     inner.failure_note(format!(
-                        "For more information about this error, try \
-                         `rustc --explain {}`.",
+                        "For more information about this error, try `rustc --explain {}`.",
                         &error_codes[0]
                     ));
                 }
@@ -1274,18 +1207,16 @@ impl DiagCtxt {
         self.create_err(err).emit()
     }
 
-    pub fn create_err<'a>(
-        &'a self,
-        err: impl IntoDiagnostic<'a>,
-    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
-        err.into_diagnostic(self)
+    #[track_caller]
+    pub fn create_err<'a>(&'a self, err: impl IntoDiagnostic<'a>) -> DiagnosticBuilder<'a> {
+        err.into_diagnostic(self, Error { lint: false })
     }
 
     pub fn create_warning<'a>(
         &'a self,
         warning: impl IntoDiagnostic<'a, ()>,
     ) -> DiagnosticBuilder<'a, ()> {
-        warning.into_diagnostic(self)
+        warning.into_diagnostic(self, Warning(None))
     }
 
     pub fn emit_warning<'a>(&'a self, warning: impl IntoDiagnostic<'a, ()>) {
@@ -1296,7 +1227,7 @@ impl DiagCtxt {
         &'a self,
         fatal: impl IntoDiagnostic<'a, FatalError>,
     ) -> DiagnosticBuilder<'a, FatalError> {
-        fatal.into_diagnostic(self)
+        fatal.into_diagnostic(self, Fatal)
     }
 
     pub fn emit_almost_fatal<'a>(
@@ -1308,38 +1239,35 @@ impl DiagCtxt {
 
     pub fn create_fatal<'a>(
         &'a self,
-        fatal: impl IntoDiagnostic<'a, !>,
-    ) -> DiagnosticBuilder<'a, !> {
-        fatal.into_diagnostic(self)
+        fatal: impl IntoDiagnostic<'a, FatalAbort>,
+    ) -> DiagnosticBuilder<'a, FatalAbort> {
+        fatal.into_diagnostic(self, Fatal)
     }
 
-    pub fn emit_fatal<'a>(&'a self, fatal: impl IntoDiagnostic<'a, !>) -> ! {
+    pub fn emit_fatal<'a>(&'a self, fatal: impl IntoDiagnostic<'a, FatalAbort>) -> ! {
         self.create_fatal(fatal).emit()
     }
 
     pub fn create_bug<'a>(
         &'a self,
-        bug: impl IntoDiagnostic<'a, diagnostic_builder::Bug>,
-    ) -> DiagnosticBuilder<'a, diagnostic_builder::Bug> {
-        bug.into_diagnostic(self)
+        bug: impl IntoDiagnostic<'a, BugAbort>,
+    ) -> DiagnosticBuilder<'a, BugAbort> {
+        bug.into_diagnostic(self, Bug)
     }
 
-    pub fn emit_bug<'a>(
-        &'a self,
-        bug: impl IntoDiagnostic<'a, diagnostic_builder::Bug>,
-    ) -> diagnostic_builder::Bug {
+    pub fn emit_bug<'a>(&'a self, bug: impl IntoDiagnostic<'a, BugAbort>) -> ! {
         self.create_bug(bug).emit()
     }
 
-    pub fn emit_note<'a>(&'a self, note: impl IntoDiagnostic<'a, Noted>) -> Noted {
+    pub fn emit_note<'a>(&'a self, note: impl IntoDiagnostic<'a, ()>) {
         self.create_note(note).emit()
     }
 
     pub fn create_note<'a>(
         &'a self,
-        note: impl IntoDiagnostic<'a, Noted>,
-    ) -> DiagnosticBuilder<'a, Noted> {
-        note.into_diagnostic(self)
+        note: impl IntoDiagnostic<'a, ()>,
+    ) -> DiagnosticBuilder<'a, ()> {
+        note.into_diagnostic(self, Note)
     }
 
     pub fn emit_artifact_notification(&self, path: &Path, artifact_type: &str) {
@@ -1426,7 +1354,7 @@ impl DiagCtxtInner {
         for diag in diags {
             // Decrement the count tracking the stash; emitting will increment it.
             if diag.is_error() {
-                if matches!(diag.level, Level::Error { lint: true }) {
+                if matches!(diag.level, Error { lint: true }) {
                     self.lint_err_count -= 1;
                 } else {
                     self.err_count -= 1;
@@ -1457,9 +1385,8 @@ impl DiagCtxtInner {
         &mut self,
         diagnostic: &mut Diagnostic,
     ) -> Option<ErrorGuaranteed> {
-        if matches!(diagnostic.level, Level::Error { .. } | Level::Fatal) && self.treat_err_as_bug()
-        {
-            diagnostic.level = Level::Bug;
+        if matches!(diagnostic.level, Error { .. } | Fatal) && self.treat_err_as_bug() {
+            diagnostic.level = Bug;
         }
 
         // The `LintExpectationId` can be stable or unstable depending on when it was created.
@@ -1471,7 +1398,7 @@ impl DiagCtxtInner {
             return None;
         }
 
-        if diagnostic.level == Level::DelayedBug {
+        if diagnostic.level == DelayedBug {
             // FIXME(eddyb) this should check for `has_errors` and stop pushing
             // once *any* errors were emitted (and truncate `span_delayed_bugs`
             // when an error is first emitted, also), but maybe there's a case
@@ -1487,7 +1414,7 @@ impl DiagCtxtInner {
         }
 
         if diagnostic.has_future_breakage() {
-            // Future breakages aren't emitted if they're Level::Allowed,
+            // Future breakages aren't emitted if they're Level::Allow,
             // but they still need to be constructed and stashed below,
             // so they'll trigger the good-path bug check.
             self.suppressed_expected_diag = true;
@@ -1509,7 +1436,7 @@ impl DiagCtxtInner {
             return None;
         }
 
-        if matches!(diagnostic.level, Level::Expect(_) | Level::Allow) {
+        if matches!(diagnostic.level, Expect(_) | Allow) {
             (*TRACK_DIAGNOSTICS)(diagnostic, &mut |_| {});
             return None;
         }
@@ -1534,7 +1461,7 @@ impl DiagCtxtInner {
                 debug!(?self.emitted_diagnostics);
                 let already_emitted_sub = |sub: &mut SubDiagnostic| {
                     debug!(?sub);
-                    if sub.level != Level::OnceNote && sub.level != Level::OnceHelp {
+                    if sub.level != OnceNote && sub.level != OnceHelp {
                         return false;
                     }
                     let mut hasher = StableHasher::new();
@@ -1559,7 +1486,7 @@ impl DiagCtxtInner {
                 }
             }
             if diagnostic.is_error() {
-                if matches!(diagnostic.level, Level::Error { lint: true }) {
+                if matches!(diagnostic.level, Error { lint: true }) {
                     self.bump_lint_err_count();
                 } else {
                     self.bump_err_count();
@@ -1583,6 +1510,13 @@ impl DiagCtxtInner {
         })
     }
 
+    // Use this one before incrementing `err_count`.
+    fn treat_next_err_as_bug(&self) -> bool {
+        self.flags.treat_err_as_bug.is_some_and(|c| {
+            self.err_count + self.lint_err_count + self.delayed_bug_count() + 1 >= c.get()
+        })
+    }
+
     fn delayed_bug_count(&self) -> usize {
         self.span_delayed_bugs.len() + self.good_path_delayed_bugs.len()
     }
@@ -1591,27 +1525,22 @@ impl DiagCtxtInner {
         self.err_count > 0
     }
 
-    #[track_caller]
-    fn span_bug(&mut self, sp: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! {
-        let mut diag = Diagnostic::new(Bug, msg);
-        diag.set_span(sp);
-        self.emit_diagnostic(diag);
-        panic::panic_any(ExplicitBug);
-    }
-
     fn failure_note(&mut self, msg: impl Into<DiagnosticMessage>) {
         self.emit_diagnostic(Diagnostic::new(FailureNote, msg));
     }
 
     fn flush_delayed(
         &mut self,
-        bugs: impl IntoIterator<Item = DelayedDiagnostic>,
+        bugs: Vec<DelayedDiagnostic>,
         explanation: impl Into<DiagnosticMessage> + Copy,
     ) {
-        let mut no_bugs = true;
+        if bugs.is_empty() {
+            return;
+        }
+
         // If backtraces are enabled, also print the query stack
         let backtrace = std::env::var_os("RUST_BACKTRACE").map_or(true, |x| &x != "0");
-        for bug in bugs {
+        for (i, bug) in bugs.into_iter().enumerate() {
             if let Some(file) = self.ice_file.as_ref()
                 && let Ok(mut out) = std::fs::File::options().create(true).append(true).open(file)
             {
@@ -1619,25 +1548,25 @@ impl DiagCtxtInner {
                     &mut out,
                     "delayed span bug: {}\n{}\n",
                     bug.inner
-                        .styled_message()
+                        .messages()
                         .iter()
                         .filter_map(|(msg, _)| msg.as_str())
                         .collect::<String>(),
                     &bug.note
                 );
             }
-            let mut bug =
-                if backtrace || self.ice_file.is_none() { bug.decorate() } else { bug.inner };
 
-            if no_bugs {
+            if i == 0 {
                 // Put the overall explanation before the `DelayedBug`s, to
                 // frame them better (e.g. separate warnings from them).
                 self.emit_diagnostic(Diagnostic::new(Bug, explanation));
-                no_bugs = false;
             }
 
+            let mut bug =
+                if backtrace || self.ice_file.is_none() { bug.decorate() } else { bug.inner };
+
             // "Undelay" the `DelayedBug`s (into plain `Bug`s).
-            if bug.level != Level::DelayedBug {
+            if bug.level != DelayedBug {
                 // NOTE(eddyb) not panicking here because we're already producing
                 // an ICE, and the more information the merrier.
                 bug.subdiagnostic(InvalidFlushedDelayedDiagnosticLevel {
@@ -1645,15 +1574,13 @@ impl DiagCtxtInner {
                     level: bug.level,
                 });
             }
-            bug.level = Level::Bug;
+            bug.level = Bug;
 
             self.emit_diagnostic(bug);
         }
 
         // Panic with `DelayedBugPanic` to avoid "unexpected panic" messages.
-        if !no_bugs {
-            panic::panic_any(DelayedBugPanic);
-        }
+        panic::panic_any(DelayedBugPanic);
     }
 
     fn bump_lint_err_count(&mut self) {
@@ -1731,25 +1658,80 @@ impl DelayedDiagnostic {
 
 #[derive(Copy, PartialEq, Eq, Clone, Hash, Debug, Encodable, Decodable)]
 pub enum Level {
+    /// For bugs in the compiler. Manifests as an ICE (internal compiler error) panic.
+    ///
+    /// Its `EmissionGuarantee` is `BugAbort`.
     Bug,
+
+    /// This is a strange one: lets you register an error without emitting it. If compilation ends
+    /// without any other errors occurring, this will be emitted as a bug. Otherwise, it will be
+    /// silently dropped. I.e. "expect other errors are emitted" semantics. Useful on code paths
+    /// that should only be reached when compiling erroneous code.
+    ///
+    /// Its `EmissionGuarantee` is `ErrorGuaranteed`.
     DelayedBug,
+
+    /// An error that causes an immediate abort. Used for things like configuration errors,
+    /// internal overflows, some file operation errors.
+    ///
+    /// Its `EmissionGuarantee` is `FatalAbort`, except in the non-aborting "almost fatal" case
+    /// that is occasionaly used, where it is `FatalError`.
     Fatal,
+
+    /// An error in the code being compiled, which prevents compilation from finishing. This is the
+    /// most common case.
+    ///
+    /// Its `EmissionGuarantee` is `ErrorGuaranteed`.
     Error {
-        /// If this error comes from a lint, don't abort compilation even when abort_if_errors() is called.
+        /// If this error comes from a lint, don't abort compilation even when abort_if_errors() is
+        /// called.
         lint: bool,
     },
+
+    /// A warning about the code being compiled. Does not prevent compilation from finishing.
+    ///
     /// This [`LintExpectationId`] is used for expected lint diagnostics, which should
     /// also emit a warning due to the `force-warn` flag. In all other cases this should
     /// be `None`.
+    ///
+    /// Its `EmissionGuarantee` is `()`.
     Warning(Option<LintExpectationId>),
+
+    /// A message giving additional context. Rare, because notes are more commonly attached to other
+    /// diagnostics such as errors.
+    ///
+    /// Its `EmissionGuarantee` is `()`.
     Note,
-    /// A note that is only emitted once.
+
+    /// A note that is only emitted once. Rare, mostly used in circumstances relating to lints.
+    ///
+    /// Its `EmissionGuarantee` is `()`.
     OnceNote,
+
+    /// A message suggesting how to fix something. Rare, because help messages are more commonly
+    /// attached to other diagnostics such as errors.
+    ///
+    /// Its `EmissionGuarantee` is `()`.
     Help,
-    /// A help that is only emitted once.
+
+    /// A help that is only emitted once. Rare.
+    ///
+    /// Its `EmissionGuarantee` is `()`.
     OnceHelp,
+
+    /// Similar to `Note`, but used in cases where compilation has failed. Rare.
+    ///
+    /// Its `EmissionGuarantee` is `()`.
     FailureNote,
+
+    /// Only used for lints.
+    ///
+    /// Its `EmissionGuarantee` is `()`.
     Allow,
+
+    /// Only used for lints.
+    ///
+    /// Its `EmissionGuarantee` is `()`.
     Expect(LintExpectationId),
 }
 
@@ -1789,8 +1771,7 @@ impl Level {
             Note | OnceNote => "note",
             Help | OnceHelp => "help",
             FailureNote => "failure-note",
-            Allow => panic!("Shouldn't call on allowed error"),
-            Expect(_) => panic!("Shouldn't call on expected error"),
+            Allow | Expect(_) => unreachable!(),
         }
     }
 
@@ -1800,7 +1781,7 @@ impl Level {
 
     pub fn get_expectation_id(&self) -> Option<LintExpectationId> {
         match self {
-            Level::Expect(id) | Level::Warning(Some(id)) => Some(*id),
+            Expect(id) | Warning(Some(id)) => Some(*id),
             _ => None,
         }
     }