]> git.proxmox.com Git - rustc.git/blobdiff - compiler/rustc_errors/src/diagnostic_builder.rs
New upstream version 1.68.2+dfsg1
[rustc.git] / compiler / rustc_errors / src / diagnostic_builder.rs
index d35b29248033fd75ac3dd6bc4b59f7fad2fe0dd1..cbfee582d871f2c5925b0e8449e60dad19749212 100644 (file)
@@ -1,12 +1,40 @@
-use crate::{Diagnostic, DiagnosticId, DiagnosticStyledString};
-use crate::{Handler, Level, StashKey};
+use crate::diagnostic::IntoDiagnosticArg;
+use crate::{
+    Diagnostic, DiagnosticId, DiagnosticMessage, DiagnosticStyledString, ErrorGuaranteed,
+    ExplicitBug, SubdiagnosticMessage,
+};
+use crate::{Handler, Level, MultiSpan, StashKey};
 use rustc_lint_defs::Applicability;
+use rustc_span::source_map::Spanned;
 
-use rustc_span::{MultiSpan, Span};
+use rustc_span::Span;
+use std::borrow::Cow;
 use std::fmt::{self, Debug};
+use std::marker::PhantomData;
 use std::ops::{Deref, DerefMut};
+use std::panic;
 use std::thread::panicking;
-use tracing::debug;
+
+/// Trait implemented by error types. This should not be implemented manually. Instead, use
+/// `#[derive(Diagnostic)]` -- see [rustc_macros::Diagnostic].
+#[rustc_diagnostic_item = "IntoDiagnostic"]
+pub trait IntoDiagnostic<'a, T: EmissionGuarantee = ErrorGuaranteed> {
+    /// Write out as a diagnostic out of `Handler`.
+    #[must_use]
+    fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, T>;
+}
+
+impl<'a, T, E> IntoDiagnostic<'a, E> for Spanned<T>
+where
+    T: IntoDiagnostic<'a, E>,
+    E: EmissionGuarantee,
+{
+    fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, E> {
+        let mut diag = self.node.into_diagnostic(handler);
+        diag.set_span(self.span);
+        diag
+    }
+}
 
 /// Used for emitting structured error messages and other diagnostic information.
 ///
@@ -15,19 +43,369 @@ use tracing::debug;
 /// extending `HandlerFlags`, accessed via `self.handler.flags`.
 #[must_use]
 #[derive(Clone)]
-pub struct DiagnosticBuilder<'a>(Box<DiagnosticBuilderInner<'a>>);
+pub struct DiagnosticBuilder<'a, G: EmissionGuarantee> {
+    inner: DiagnosticBuilderInner<'a>,
+    _marker: PhantomData<G>,
+}
 
-/// This is a large type, and often used as a return value, especially within
-/// the frequently-used `PResult` type. In theory, return value optimization
-/// (RVO) should avoid unnecessary copying. In practice, it does not (at the
-/// time of writing). The split between `DiagnosticBuilder` and
-/// `DiagnosticBuilderInner` exists to avoid many `memcpy` calls.
+/// 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> {
-    handler: &'a Handler,
-    diagnostic: Diagnostic,
-    allow_suggestions: bool,
+    state: DiagnosticBuilderState<'a>,
+
+    /// `Diagnostic` is a large type, and `DiagnosticBuilder` is often used as a
+    /// return value, especially within the frequently-used `PResult` type.
+    /// In theory, return value optimization (RVO) should avoid unnecessary
+    /// copying. In practice, it does not (at the time of writing).
+    diagnostic: Box<Diagnostic>,
+}
+
+#[derive(Clone)]
+enum DiagnosticBuilderState<'a> {
+    /// Initial state of a `DiagnosticBuilder`, before `.emit()` or `.cancel()`.
+    ///
+    /// The `Diagnostic` will be emitted through this `Handler`.
+    Emittable(&'a Handler),
+
+    /// State of a `DiagnosticBuilder`, after `.emit()` or *during* `.cancel()`.
+    ///
+    /// The `Diagnostic` will be ignored when calling `.emit()`, and it can be
+    /// 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
+    /// `self` by-value specifically to prevent any attempts to `.emit()`.
+    ///
+    // FIXME(eddyb) currently this doesn't prevent extending the `Diagnostic`,
+    // despite that being potentially lossy, if important information is added
+    // *after* the original `.emit()` call.
+    AlreadyEmittedOrDuringCancellation,
+}
+
+// `DiagnosticBuilderState` should be pointer-sized.
+rustc_data_structures::static_assert_size!(
+    DiagnosticBuilderState<'_>,
+    std::mem::size_of::<&Handler>()
+);
+
+/// Trait for types that `DiagnosticBuilder::emit` can return as a "guarantee"
+/// (or "proof") token that the emission happened.
+pub trait EmissionGuarantee: Sized {
+    /// 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.
+    #[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(
+        handler: &Handler,
+        msg: impl Into<DiagnosticMessage>,
+    ) -> DiagnosticBuilder<'_, Self>;
+}
+
+/// Private module for sealing the `IsError` helper trait.
+mod sealed_level_is_error {
+    use crate::Level;
+
+    /// Sealed helper trait for statically checking that a `Level` is an error.
+    pub(crate) trait IsError<const L: Level> {}
+
+    impl IsError<{ Level::Bug }> for () {}
+    impl IsError<{ Level::DelayedBug }> for () {}
+    impl IsError<{ Level::Fatal }> for () {}
+    // NOTE(eddyb) `Level::Error { lint: true }` is also an error, but lints
+    // don't need error guarantees, as their levels are always dynamic.
+    impl IsError<{ Level::Error { lint: false } }> for () {}
+}
+
+impl<'a> DiagnosticBuilder<'a, ErrorGuaranteed> {
+    /// Convenience function for internal use, clients should use one of the
+    /// `struct_*` methods on [`Handler`].
+    #[track_caller]
+    pub(crate) fn new_guaranteeing_error<M: Into<DiagnosticMessage>, const L: Level>(
+        handler: &'a Handler,
+        message: M,
+    ) -> Self
+    where
+        (): sealed_level_is_error::IsError<L>,
+    {
+        Self {
+            inner: DiagnosticBuilderInner {
+                state: DiagnosticBuilderState::Emittable(handler),
+                diagnostic: Box::new(Diagnostic::new_with_code(L, None, message)),
+            },
+            _marker: PhantomData,
+        }
+    }
+
+    /// 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 }
+    }
+}
+
+// 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 {
+            // First `.emit()` call, the `&Handler` is still available.
+            DiagnosticBuilderState::Emittable(handler) => {
+                db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
+
+                let guar = handler.emit_diagnostic(&mut db.inner.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(),
+                    "emitted non-error ({:?}) diagnostic \
+                     from `DiagnosticBuilder<ErrorGuaranteed>`",
+                    db.inner.diagnostic.level,
+                );
+                guar.unwrap()
+            }
+            // `.emit()` was previously called, disallowed from repeating it,
+            // but can take advantage of the previous `.emit()`'s guarantee
+            // still being applicable (i.e. as a form of idempotency).
+            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {
+                // 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(),
+                    "`DiagnosticBuilder<ErrorGuaranteed>`'s diagnostic \
+                     became non-error ({:?}), after original `.emit()`",
+                    db.inner.diagnostic.level,
+                );
+                ErrorGuaranteed::unchecked_claim_error_was_emitted()
+            }
+        }
+    }
+
+    #[track_caller]
+    fn make_diagnostic_builder(
+        handler: &Handler,
+        msg: impl Into<DiagnosticMessage>,
+    ) -> DiagnosticBuilder<'_, Self> {
+        DiagnosticBuilder::new_guaranteeing_error::<_, { Level::Error { lint: false } }>(
+            handler, msg,
+        )
+    }
+}
+
+impl<'a> DiagnosticBuilder<'a, ()> {
+    /// Convenience function for internal use, clients should use one of the
+    /// `struct_*` methods on [`Handler`].
+    #[track_caller]
+    pub(crate) fn new<M: Into<DiagnosticMessage>>(
+        handler: &'a Handler,
+        level: Level,
+        message: M,
+    ) -> Self {
+        let diagnostic = Diagnostic::new_with_code(level, None, message);
+        Self::new_diagnostic(handler, diagnostic)
+    }
+
+    /// Creates a new `DiagnosticBuilder` with an already constructed
+    /// diagnostic.
+    #[track_caller]
+    pub(crate) fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> Self {
+        debug!("Created new diagnostic");
+        Self {
+            inner: DiagnosticBuilderInner {
+                state: DiagnosticBuilderState::Emittable(handler),
+                diagnostic: Box::new(diagnostic),
+            },
+            _marker: PhantomData,
+        }
+    }
+}
+
+// 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 `&Handler` is still available.
+            DiagnosticBuilderState::Emittable(handler) => {
+                db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
+
+                handler.emit_diagnostic(&mut db.inner.diagnostic);
+            }
+            // `.emit()` was previously called, disallowed from repeating it.
+            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
+        }
+    }
+
+    fn make_diagnostic_builder(
+        handler: &Handler,
+        msg: impl Into<DiagnosticMessage>,
+    ) -> DiagnosticBuilder<'_, Self> {
+        DiagnosticBuilder::new(handler, 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<'a> DiagnosticBuilder<'a, Noted> {
+    /// Convenience function for internal use, clients should use one of the
+    /// `struct_*` methods on [`Handler`].
+    pub(crate) fn new_note(handler: &'a Handler, message: impl Into<DiagnosticMessage>) -> Self {
+        let diagnostic = Diagnostic::new_with_code(Level::Note, None, message);
+        Self::new_diagnostic_note(handler, diagnostic)
+    }
+
+    /// Creates a new `DiagnosticBuilder` with an already constructed
+    /// diagnostic.
+    pub(crate) fn new_diagnostic_note(handler: &'a Handler, diagnostic: Diagnostic) -> Self {
+        debug!("Created new diagnostic");
+        Self {
+            inner: DiagnosticBuilderInner {
+                state: DiagnosticBuilderState::Emittable(handler),
+                diagnostic: Box::new(diagnostic),
+            },
+            _marker: PhantomData,
+        }
+    }
+}
+
+impl EmissionGuarantee for Noted {
+    fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
+        match db.inner.state {
+            // First `.emit()` call, the `&Handler` is still available.
+            DiagnosticBuilderState::Emittable(handler) => {
+                db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
+                handler.emit_diagnostic(&mut db.inner.diagnostic);
+            }
+            // `.emit()` was previously called, disallowed from repeating it.
+            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
+        }
+
+        Noted
+    }
+
+    fn make_diagnostic_builder(
+        handler: &Handler,
+        msg: impl Into<DiagnosticMessage>,
+    ) -> DiagnosticBuilder<'_, Self> {
+        DiagnosticBuilder::new_note(handler, msg)
+    }
+}
+
+/// Marker type which enables implementation of `create_bug` and `emit_bug` functions for
+/// bug struct diagnostics.
+#[derive(Copy, Clone)]
+pub struct Bug;
+
+impl<'a> DiagnosticBuilder<'a, Bug> {
+    /// Convenience function for internal use, clients should use one of the
+    /// `struct_*` methods on [`Handler`].
+    #[track_caller]
+    pub(crate) fn new_bug(handler: &'a Handler, message: impl Into<DiagnosticMessage>) -> Self {
+        let diagnostic = Diagnostic::new_with_code(Level::Bug, None, message);
+        Self::new_diagnostic_bug(handler, diagnostic)
+    }
+
+    /// Creates a new `DiagnosticBuilder` with an already constructed
+    /// diagnostic.
+    pub(crate) fn new_diagnostic_bug(handler: &'a Handler, diagnostic: Diagnostic) -> Self {
+        debug!("Created new diagnostic bug");
+        Self {
+            inner: DiagnosticBuilderInner {
+                state: DiagnosticBuilderState::Emittable(handler),
+                diagnostic: Box::new(diagnostic),
+            },
+            _marker: PhantomData,
+        }
+    }
+}
+
+impl EmissionGuarantee for Bug {
+    fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
+        match db.inner.state {
+            // First `.emit()` call, the `&Handler` is still available.
+            DiagnosticBuilderState::Emittable(handler) => {
+                db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
+
+                handler.emit_diagnostic(&mut db.inner.diagnostic);
+            }
+            // `.emit()` was previously called, disallowed from repeating it.
+            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
+        }
+        // Then panic. No need to return the marker type.
+        panic::panic_any(ExplicitBug);
+    }
+
+    fn make_diagnostic_builder(
+        handler: &Handler,
+        msg: impl Into<DiagnosticMessage>,
+    ) -> DiagnosticBuilder<'_, Self> {
+        DiagnosticBuilder::new_bug(handler, msg)
+    }
+}
+
+impl<'a> DiagnosticBuilder<'a, !> {
+    /// Convenience function for internal use, clients should use one of the
+    /// `struct_*` methods on [`Handler`].
+    #[track_caller]
+    pub(crate) fn new_fatal(handler: &'a Handler, message: impl Into<DiagnosticMessage>) -> Self {
+        let diagnostic = Diagnostic::new_with_code(Level::Fatal, None, message);
+        Self::new_diagnostic_fatal(handler, diagnostic)
+    }
+
+    /// Creates a new `DiagnosticBuilder` with an already constructed
+    /// diagnostic.
+    pub(crate) fn new_diagnostic_fatal(handler: &'a Handler, diagnostic: Diagnostic) -> Self {
+        debug!("Created new diagnostic");
+        Self {
+            inner: DiagnosticBuilderInner {
+                state: DiagnosticBuilderState::Emittable(handler),
+                diagnostic: Box::new(diagnostic),
+            },
+            _marker: PhantomData,
+        }
+    }
+}
+
+impl EmissionGuarantee for ! {
+    fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
+        match db.inner.state {
+            // First `.emit()` call, the `&Handler` is still available.
+            DiagnosticBuilderState::Emittable(handler) => {
+                db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
+
+                handler.emit_diagnostic(&mut db.inner.diagnostic);
+            }
+            // `.emit()` was previously called, disallowed from repeating it.
+            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
+        }
+        // Then fatally error, returning `!`
+        crate::FatalError.raise()
+    }
+
+    fn make_diagnostic_builder(
+        handler: &Handler,
+        msg: impl Into<DiagnosticMessage>,
+    ) -> DiagnosticBuilder<'_, Self> {
+        DiagnosticBuilder::new_fatal(handler, msg)
+    }
 }
 
 /// In general, the `DiagnosticBuilder` uses deref to allow access to
@@ -39,19 +417,6 @@ struct DiagnosticBuilderInner<'a> {
 /// instead of a `&DiagnosticBuilder<'a>`. This `forward!` macro makes
 /// it easy to declare such methods on the builder.
 macro_rules! forward {
-    // Forward pattern for &self -> &Self
-    (
-        $(#[$attrs:meta])*
-        pub fn $n:ident(&self, $($name:ident: $ty:ty),* $(,)?) -> &Self
-    ) => {
-        $(#[$attrs])*
-        #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")]
-        pub fn $n(&self, $($name: $ty),*) -> &Self {
-            self.diagnostic.$n($($name),*);
-            self
-        }
-    };
-
     // Forward pattern for &mut self -> &mut Self
     (
         $(#[$attrs:meta])*
@@ -60,60 +425,54 @@ macro_rules! forward {
         $(#[$attrs])*
         #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")]
         pub fn $n(&mut self, $($name: $ty),*) -> &mut Self {
-            self.0.diagnostic.$n($($name),*);
-            self
-        }
-    };
-
-    // Forward pattern for &mut self -> &mut Self, with generic parameters.
-    (
-        $(#[$attrs:meta])*
-        pub fn $n:ident<$($generic:ident: $bound:path),*>(
-            &mut self,
-            $($name:ident: $ty:ty),*
-            $(,)?
-        ) -> &mut Self
-    ) => {
-        $(#[$attrs])*
-        #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")]
-        pub fn $n<$($generic: $bound),*>(&mut self, $($name: $ty),*) -> &mut Self {
-            self.0.diagnostic.$n($($name),*);
+            self.inner.diagnostic.$n($($name),*);
             self
         }
     };
 }
 
-impl<'a> Deref for DiagnosticBuilder<'a> {
+impl<G: EmissionGuarantee> Deref for DiagnosticBuilder<'_, G> {
     type Target = Diagnostic;
 
     fn deref(&self) -> &Diagnostic {
-        &self.0.diagnostic
+        &self.inner.diagnostic
     }
 }
 
-impl<'a> DerefMut for DiagnosticBuilder<'a> {
+impl<G: EmissionGuarantee> DerefMut for DiagnosticBuilder<'_, G> {
     fn deref_mut(&mut self) -> &mut Diagnostic {
-        &mut self.0.diagnostic
+        &mut self.inner.diagnostic
     }
 }
 
-impl<'a> DiagnosticBuilder<'a> {
+impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
     /// Emit the diagnostic.
-    pub fn emit(&mut self) {
-        self.0.handler.emit_diagnostic(&self);
-        self.cancel();
+    #[track_caller]
+    pub fn emit(&mut self) -> G {
+        G::diagnostic_builder_emit_producing_guarantee(self)
     }
 
     /// Emit the diagnostic unless `delay` is true,
     /// in which case the emission will be delayed as a bug.
     ///
     /// See `emit` and `delay_as_bug` for details.
-    pub fn emit_unless(&mut self, delay: bool) {
+    #[track_caller]
+    pub fn emit_unless(&mut self, delay: bool) -> G {
         if delay {
-            self.delay_as_bug();
-        } else {
-            self.emit();
+            self.downgrade_to_delayed_bug();
         }
+        self.emit()
+    }
+
+    /// Cancel the diagnostic (a structured diagnostic must either be emitted or
+    /// cancelled or it will panic when dropped).
+    ///
+    /// This method takes `self` by-value to disallow calling `.emit()` on it,
+    /// 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;
+        drop(self);
     }
 
     /// Stashes diagnostic for possible later improvement in a different,
@@ -128,21 +487,28 @@ impl<'a> DiagnosticBuilder<'a> {
     }
 
     /// Converts the builder to a `Diagnostic` for later emission,
-    /// unless handler has disabled such buffering.
+    /// unless handler has disabled such buffering, or `.emit()` was called.
     pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a Handler)> {
-        if self.0.handler.flags.dont_buffer_diagnostics
-            || self.0.handler.flags.treat_err_as_bug.is_some()
-        {
+        let handler = match self.inner.state {
+            // No `.emit()` calls, the `&Handler` is still available.
+            DiagnosticBuilderState::Emittable(handler) => handler,
+            // `.emit()` was previously called, nothing we can do.
+            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {
+                return None;
+            }
+        };
+
+        if handler.flags.dont_buffer_diagnostics || handler.flags.treat_err_as_bug.is_some() {
             self.emit();
             return None;
         }
 
-        let handler = self.0.handler;
+        // Take the `Diagnostic` by replacing it with a dummy.
+        let dummy = Diagnostic::new(Level::Allow, DiagnosticMessage::Str("".to_string()));
+        let diagnostic = std::mem::replace(&mut *self.inner.diagnostic, dummy);
 
-        // We must use `Level::Cancelled` for `dummy` to avoid an ICE about an
-        // unused diagnostic.
-        let dummy = Diagnostic::new(Level::Cancelled, "");
-        let diagnostic = std::mem::replace(&mut self.0.diagnostic, dummy);
+        // Disable the ICE on `Drop`.
+        self.cancel();
 
         // Logging here is useful to help track down where in logs an error was
         // actually emitted.
@@ -167,12 +533,18 @@ impl<'a> DiagnosticBuilder<'a> {
     ///
     /// In the meantime, though, callsites are required to deal with the "bug"
     /// locally in whichever way makes the most sense.
-    pub fn delay_as_bug(&mut self) {
-        self.level = Level::Bug;
-        self.0.handler.delay_as_bug(self.0.diagnostic.clone());
-        self.cancel();
+    #[track_caller]
+    pub fn delay_as_bug(&mut self) -> G {
+        self.downgrade_to_delayed_bug();
+        self.emit()
     }
 
+    forward!(
+        #[track_caller]
+        pub fn downgrade_to_delayed_bug(&mut self,) -> &mut Self
+    );
+
+    forward!(
     /// Appends a labeled span to the diagnostic.
     ///
     /// Labels are used to convey additional context for the diagnostic's primary span. They will
@@ -185,24 +557,16 @@ impl<'a> DiagnosticBuilder<'a> {
     /// the diagnostic was constructed. However, the label span is *not* considered a
     /// ["primary span"][`MultiSpan`]; only the `Span` supplied when creating the diagnostic is
     /// primary.
-    pub fn span_label(&mut self, span: Span, label: impl Into<String>) -> &mut Self {
-        self.0.diagnostic.span_label(span, label);
-        self
-    }
+    pub fn span_label(&mut self, span: Span, label: impl Into<SubdiagnosticMessage>) -> &mut Self);
 
+    forward!(
     /// Labels all the given spans with the provided label.
     /// See [`Diagnostic::span_label()`] for more information.
     pub fn span_labels(
         &mut self,
         spans: impl IntoIterator<Item = Span>,
         label: impl AsRef<str>,
-    ) -> &mut Self {
-        let label = label.as_ref();
-        for span in spans {
-            self.0.diagnostic.span_label(span, label);
-        }
-        self
-    }
+    ) -> &mut Self);
 
     forward!(pub fn note_expected_found(
         &mut self,
@@ -228,214 +592,144 @@ impl<'a> DiagnosticBuilder<'a> {
         found: DiagnosticStyledString,
     ) -> &mut Self);
 
-    forward!(pub fn note(&mut self, msg: &str) -> &mut Self);
-    forward!(pub fn span_note<S: Into<MultiSpan>>(
+    forward!(pub fn note(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self);
+    forward!(pub fn note_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self);
+    forward!(pub fn span_note(
+        &mut self,
+        sp: impl Into<MultiSpan>,
+        msg: impl Into<SubdiagnosticMessage>,
+    ) -> &mut Self);
+    forward!(pub fn span_note_once(
+        &mut self,
+        sp: impl Into<MultiSpan>,
+        msg: impl Into<SubdiagnosticMessage>,
+    ) -> &mut Self);
+    forward!(pub fn warn(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self);
+    forward!(pub fn span_warn(
         &mut self,
-        sp: S,
-        msg: &str,
+        sp: impl Into<MultiSpan>,
+        msg: impl Into<SubdiagnosticMessage>,
     ) -> &mut Self);
-    forward!(pub fn warn(&mut self, msg: &str) -> &mut Self);
-    forward!(pub fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self);
-    forward!(pub fn help(&mut self, msg: &str) -> &mut Self);
-    forward!(pub fn span_help<S: Into<MultiSpan>>(
+    forward!(pub fn help(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self);
+    forward!(pub fn span_help(
         &mut self,
-        sp: S,
-        msg: &str,
+        sp: impl Into<MultiSpan>,
+        msg: impl Into<SubdiagnosticMessage>,
     ) -> &mut Self);
+    forward!(pub fn help_use_latest_edition(&mut self,) -> &mut Self);
+    forward!(pub fn set_is_lint(&mut self,) -> &mut Self);
+
+    forward!(pub fn disable_suggestions(&mut self,) -> &mut Self);
+    forward!(pub fn clear_suggestions(&mut self,) -> &mut Self);
 
-    /// See [`Diagnostic::multipart_suggestion()`].
-    pub fn multipart_suggestion(
+    forward!(pub fn multipart_suggestion(
         &mut self,
-        msg: &str,
+        msg: impl Into<SubdiagnosticMessage>,
         suggestion: Vec<(Span, String)>,
         applicability: Applicability,
-    ) -> &mut Self {
-        if !self.0.allow_suggestions {
-            return self;
-        }
-        self.0.diagnostic.multipart_suggestion(msg, suggestion, applicability);
-        self
-    }
-
-    /// See [`Diagnostic::tool_only_multipart_suggestion()`].
-    pub fn tool_only_multipart_suggestion(
+    ) -> &mut Self);
+    forward!(pub fn multipart_suggestion_verbose(
         &mut self,
-        msg: &str,
+        msg: impl Into<SubdiagnosticMessage>,
         suggestion: Vec<(Span, String)>,
         applicability: Applicability,
-    ) -> &mut Self {
-        if !self.0.allow_suggestions {
-            return self;
-        }
-        self.0.diagnostic.tool_only_multipart_suggestion(msg, suggestion, applicability);
-        self
-    }
-
-    /// See [`Diagnostic::span_suggestion()`].
-    pub fn span_suggestion(
+    ) -> &mut Self);
+    forward!(pub fn tool_only_multipart_suggestion(
+        &mut self,
+        msg: impl Into<SubdiagnosticMessage>,
+        suggestion: Vec<(Span, String)>,
+        applicability: Applicability,
+    ) -> &mut Self);
+    forward!(pub fn span_suggestion(
         &mut self,
         sp: Span,
-        msg: &str,
-        suggestion: String,
+        msg: impl Into<SubdiagnosticMessage>,
+        suggestion: impl ToString,
         applicability: Applicability,
-    ) -> &mut Self {
-        if !self.0.allow_suggestions {
-            return self;
-        }
-        self.0.diagnostic.span_suggestion(sp, msg, suggestion, applicability);
-        self
-    }
-
-    /// See [`Diagnostic::span_suggestions()`].
-    pub fn span_suggestions(
+    ) -> &mut Self);
+    forward!(pub fn span_suggestions(
         &mut self,
         sp: Span,
-        msg: &str,
-        suggestions: impl Iterator<Item = String>,
+        msg: impl Into<SubdiagnosticMessage>,
+        suggestions: impl IntoIterator<Item = String>,
         applicability: Applicability,
-    ) -> &mut Self {
-        if !self.0.allow_suggestions {
-            return self;
-        }
-        self.0.diagnostic.span_suggestions(sp, msg, suggestions, applicability);
-        self
-    }
-
-    /// See [`Diagnostic::multipart_suggestions()`].
-    pub fn multipart_suggestions(
+    ) -> &mut Self);
+    forward!(pub fn multipart_suggestions(
         &mut self,
-        msg: &str,
-        suggestions: impl Iterator<Item = Vec<(Span, String)>>,
+        msg: impl Into<SubdiagnosticMessage>,
+        suggestions: impl IntoIterator<Item = Vec<(Span, String)>>,
         applicability: Applicability,
-    ) -> &mut Self {
-        if !self.0.allow_suggestions {
-            return self;
-        }
-        self.0.diagnostic.multipart_suggestions(msg, suggestions, applicability);
-        self
-    }
-
-    /// See [`Diagnostic::span_suggestion_short()`].
-    pub fn span_suggestion_short(
+    ) -> &mut Self);
+    forward!(pub fn span_suggestion_short(
         &mut self,
         sp: Span,
-        msg: &str,
-        suggestion: String,
+        msg: impl Into<SubdiagnosticMessage>,
+        suggestion: impl ToString,
         applicability: Applicability,
-    ) -> &mut Self {
-        if !self.0.allow_suggestions {
-            return self;
-        }
-        self.0.diagnostic.span_suggestion_short(sp, msg, suggestion, applicability);
-        self
-    }
-
-    /// See [`Diagnostic::span_suggestion_verbose()`].
-    pub fn span_suggestion_verbose(
+    ) -> &mut Self);
+    forward!(pub fn span_suggestion_verbose(
         &mut self,
         sp: Span,
-        msg: &str,
-        suggestion: String,
+        msg: impl Into<SubdiagnosticMessage>,
+        suggestion: impl ToString,
         applicability: Applicability,
-    ) -> &mut Self {
-        if !self.0.allow_suggestions {
-            return self;
-        }
-        self.0.diagnostic.span_suggestion_verbose(sp, msg, suggestion, applicability);
-        self
-    }
-
-    /// See [`Diagnostic::span_suggestion_hidden()`].
-    pub fn span_suggestion_hidden(
+    ) -> &mut Self);
+    forward!(pub fn span_suggestion_hidden(
         &mut self,
         sp: Span,
-        msg: &str,
-        suggestion: String,
+        msg: impl Into<SubdiagnosticMessage>,
+        suggestion: impl ToString,
         applicability: Applicability,
-    ) -> &mut Self {
-        if !self.0.allow_suggestions {
-            return self;
-        }
-        self.0.diagnostic.span_suggestion_hidden(sp, msg, suggestion, applicability);
-        self
-    }
-
-    /// See [`Diagnostic::tool_only_span_suggestion()`] for more information.
-    pub fn tool_only_span_suggestion(
+    ) -> &mut Self);
+    forward!(pub fn tool_only_span_suggestion(
         &mut self,
         sp: Span,
-        msg: &str,
-        suggestion: String,
+        msg: impl Into<SubdiagnosticMessage>,
+        suggestion: impl ToString,
         applicability: Applicability,
-    ) -> &mut Self {
-        if !self.0.allow_suggestions {
-            return self;
-        }
-        self.0.diagnostic.tool_only_span_suggestion(sp, msg, suggestion, applicability);
-        self
-    }
+    ) -> &mut Self);
 
-    forward!(pub fn set_primary_message<M: Into<String>>(&mut self, msg: M) -> &mut Self);
-    forward!(pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self);
+    forward!(pub fn set_primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self);
+    forward!(pub fn set_span(&mut self, sp: impl Into<MultiSpan>) -> &mut Self);
     forward!(pub fn code(&mut self, s: DiagnosticId) -> &mut Self);
+    forward!(pub fn set_arg(
+        &mut self,
+        name: impl Into<Cow<'static, str>>,
+        arg: impl IntoDiagnosticArg,
+    ) -> &mut Self);
 
-    /// Allow attaching suggestions this diagnostic.
-    /// If this is set to `false`, then any suggestions attached with the `span_suggestion_*`
-    /// methods after this is set to `false` will be ignored.
-    pub fn allow_suggestions(&mut self, allow: bool) -> &mut Self {
-        self.0.allow_suggestions = allow;
-        self
-    }
-
-    /// Convenience function for internal use, clients should use one of the
-    /// `struct_*` methods on [`Handler`].
-    crate fn new(handler: &'a Handler, level: Level, message: &str) -> DiagnosticBuilder<'a> {
-        DiagnosticBuilder::new_with_code(handler, level, None, message)
-    }
-
-    /// Convenience function for internal use, clients should use one of the
-    /// `struct_*` methods on [`Handler`].
-    crate fn new_with_code(
-        handler: &'a Handler,
-        level: Level,
-        code: Option<DiagnosticId>,
-        message: &str,
-    ) -> DiagnosticBuilder<'a> {
-        let diagnostic = Diagnostic::new_with_code(level, code, message);
-        DiagnosticBuilder::new_diagnostic(handler, diagnostic)
-    }
-
-    /// 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,
-            allow_suggestions: true,
-        }))
-    }
+    forward!(pub fn subdiagnostic(
+        &mut self,
+        subdiagnostic: impl crate::AddToDiagnostic
+    ) -> &mut Self);
 }
 
-impl<'a> Debug for DiagnosticBuilder<'a> {
+impl<G: EmissionGuarantee> Debug for DiagnosticBuilder<'_, G> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.0.diagnostic.fmt(f)
+        self.inner.diagnostic.fmt(f)
     }
 }
 
-/// Destructor bomb - a `DiagnosticBuilder` must be either emitted or canceled
+/// Destructor bomb - a `DiagnosticBuilder` must be either emitted or cancelled
 /// or we emit a bug.
-impl<'a> Drop for DiagnosticBuilder<'a> {
+impl Drop for DiagnosticBuilderInner<'_> {
     fn drop(&mut self) {
-        if !panicking() && !self.cancelled() {
-            let mut db = DiagnosticBuilder::new(
-                self.0.handler,
-                Level::Bug,
-                "the following error was constructed but not emitted",
-            );
-            db.emit();
-            self.emit();
-            panic!();
+        match self.state {
+            // No `.emit()` or `.cancel()` calls.
+            DiagnosticBuilderState::Emittable(handler) => {
+                if !panicking() {
+                    handler.emit_diagnostic(&mut Diagnostic::new(
+                        Level::Bug,
+                        DiagnosticMessage::Str(
+                            "the following error was constructed but not emitted".to_string(),
+                        ),
+                    ));
+                    handler.emit_diagnostic(&mut self.diagnostic);
+                    panic!("error was constructed but not emitted");
+                }
+            }
+            // `.emit()` was previously called, or maybe we're during `.cancel()`.
+            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
         }
     }
 }