about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_attr/src/session_diagnostics.rs27
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs20
-rw-r--r--compiler/rustc_codegen_gcc/src/errors.rs14
-rw-r--r--compiler/rustc_codegen_llvm/src/errors.rs27
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs95
-rw-r--r--compiler/rustc_const_eval/src/errors.rs7
-rw-r--r--compiler/rustc_errors/src/diagnostic_builder.rs200
-rw-r--r--compiler/rustc_errors/src/diagnostic_impls.rs39
-rw-r--r--compiler/rustc_errors/src/lib.rs92
-rw-r--r--compiler/rustc_expand/src/base.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs16
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs19
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs31
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note.rs36
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic.rs60
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs70
-rw-r--r--compiler/rustc_macros/src/diagnostics/mod.rs5
-rw-r--r--compiler/rustc_macros/src/diagnostics/subdiagnostic.rs1
-rw-r--r--compiler/rustc_metadata/src/errors.rs31
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs11
-rw-r--r--compiler/rustc_mir_build/src/errors.rs17
-rw-r--r--compiler/rustc_mir_transform/src/errors.rs8
-rw-r--r--compiler/rustc_monomorphize/src/errors.rs13
-rw-r--r--compiler/rustc_parse/src/errors.rs80
-rw-r--r--compiler/rustc_parse/src/lexer/mod.rs6
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs7
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs22
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs66
-rw-r--r--compiler/rustc_parse/src/parser/item.rs16
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs9
-rw-r--r--compiler/rustc_parse/src/parser/nonterminal.rs22
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs11
-rw-r--r--compiler/rustc_parse/src/parser/path.rs7
-rw-r--r--compiler/rustc_passes/src/errors.rs74
-rw-r--r--compiler/rustc_query_system/src/query/job.rs6
-rw-r--r--compiler/rustc_session/src/errors.rs12
-rw-r--r--compiler/rustc_session/src/parse.rs27
-rw-r--r--compiler/rustc_session/src/session.rs27
-rw-r--r--compiler/rustc_symbol_mangling/src/errors.rs11
-rw-r--r--compiler/rustc_trait_selection/src/errors.rs12
41 files changed, 574 insertions, 688 deletions
diff --git a/compiler/rustc_attr/src/session_diagnostics.rs b/compiler/rustc_attr/src/session_diagnostics.rs
index ce8e04defb2..fd2b0866867 100644
--- a/compiler/rustc_attr/src/session_diagnostics.rs
+++ b/compiler/rustc_attr/src/session_diagnostics.rs
@@ -2,7 +2,8 @@ use std::num::IntErrorKind;
 
 use rustc_ast as ast;
 use rustc_errors::{
-    error_code, Applicability, DiagCtxt, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic,
+    error_code, Applicability, DiagCtxt, DiagnosticBuilder, EmissionGuarantee, IntoDiagnostic,
+    Level,
 };
 use rustc_macros::Diagnostic;
 use rustc_span::{Span, Symbol};
@@ -50,14 +51,12 @@ pub(crate) struct UnknownMetaItem<'a> {
 }
 
 // Manual implementation to be able to format `expected` items correctly.
-impl<'a> IntoDiagnostic<'a> for UnknownMetaItem<'_> {
-    fn into_diagnostic(self, dcx: &'a DiagCtxt) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for UnknownMetaItem<'_> {
+    fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> {
         let expected = self.expected.iter().map(|name| format!("`{name}`")).collect::<Vec<_>>();
-        let mut diag = dcx.struct_span_err_with_code(
-            self.span,
-            fluent::attr_unknown_meta_item,
-            error_code!(E0541),
-        );
+        let mut diag = DiagnosticBuilder::new(dcx, level, fluent::attr_unknown_meta_item);
+        diag.set_span(self.span);
+        diag.code(error_code!(E0541));
         diag.set_arg("item", self.item);
         diag.set_arg("expected", expected.join(", "));
         diag.span_label(self.span, fluent::attr_label);
@@ -200,10 +199,11 @@ pub(crate) struct UnsupportedLiteral {
     pub start_point_span: Span,
 }
 
-impl<'a> IntoDiagnostic<'a> for UnsupportedLiteral {
-    fn into_diagnostic(self, dcx: &'a DiagCtxt) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
-        let mut diag = dcx.struct_span_err_with_code(
-            self.span,
+impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for UnsupportedLiteral {
+    fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> {
+        let mut diag = DiagnosticBuilder::new(
+            dcx,
+            level,
             match self.reason {
                 UnsupportedLiteralReason::Generic => fluent::attr_unsupported_literal_generic,
                 UnsupportedLiteralReason::CfgString => fluent::attr_unsupported_literal_cfg_string,
@@ -214,8 +214,9 @@ impl<'a> IntoDiagnostic<'a> for UnsupportedLiteral {
                     fluent::attr_unsupported_literal_deprecated_kv_pair
                 }
             },
-            error_code!(E0565),
         );
+        diag.set_span(self.span);
+        diag.code(error_code!(E0565));
         if self.is_bytestr {
             diag.span_suggestion(
                 self.start_point_span,
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index 6ffeb401453..e07eb2e490b 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -1,6 +1,6 @@
 use rustc_errors::{
-    AddToDiagnostic, DiagCtxt, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, MultiSpan,
-    SingleLabelManySpans,
+    AddToDiagnostic, DiagCtxt, DiagnosticBuilder, EmissionGuarantee, IntoDiagnostic, Level,
+    MultiSpan, SingleLabelManySpans,
 };
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::{symbol::Ident, Span, Symbol};
@@ -446,14 +446,14 @@ pub(crate) struct EnvNotDefinedWithUserMessage {
 }
 
 // Hand-written implementation to support custom user messages.
-impl<'a> IntoDiagnostic<'a> for EnvNotDefinedWithUserMessage {
+impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for EnvNotDefinedWithUserMessage {
     #[track_caller]
-    fn into_diagnostic(self, dcx: &'a DiagCtxt) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+    fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> {
         #[expect(
             rustc::untranslatable_diagnostic,
             reason = "cannot translate user-provided messages"
         )]
-        let mut diag = dcx.struct_err(self.msg_from_user.to_string());
+        let mut diag = DiagnosticBuilder::new(dcx, level, self.msg_from_user.to_string());
         diag.set_span(self.span);
         diag
     }
@@ -801,9 +801,13 @@ pub(crate) struct AsmClobberNoReg {
     pub(crate) clobbers: Vec<Span>,
 }
 
-impl<'a> IntoDiagnostic<'a> for AsmClobberNoReg {
-    fn into_diagnostic(self, dcx: &'a DiagCtxt) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
-        let mut diag = dcx.struct_err(crate::fluent_generated::builtin_macros_asm_clobber_no_reg);
+impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for AsmClobberNoReg {
+    fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> {
+        let mut diag = DiagnosticBuilder::new(
+            dcx,
+            level,
+            crate::fluent_generated::builtin_macros_asm_clobber_no_reg,
+        );
         diag.set_span(self.spans.clone());
         // eager translation as `span_labels` takes `AsRef<str>`
         let lbl1 = dcx.eagerly_translate_to_string(
diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs
index 766d90cf724..1b1ed0b411c 100644
--- a/compiler/rustc_codegen_gcc/src/errors.rs
+++ b/compiler/rustc_codegen_gcc/src/errors.rs
@@ -1,6 +1,6 @@
 use rustc_errors::{
-    DiagCtxt, DiagnosticArgValue, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic,
-    IntoDiagnosticArg,
+    DiagCtxt, DiagnosticArgValue, DiagnosticBuilder, EmissionGuarantee, IntoDiagnostic,
+    IntoDiagnosticArg, Level,
 };
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::Span;
@@ -111,9 +111,13 @@ pub(crate) struct TargetFeatureDisableOrEnable<'a> {
 #[help(codegen_gcc_missing_features)]
 pub(crate) struct MissingFeatures;
 
-impl IntoDiagnostic<'_, ErrorGuaranteed> for TargetFeatureDisableOrEnable<'_> {
-    fn into_diagnostic(self, dcx: &'_ DiagCtxt) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = dcx.struct_err(fluent::codegen_gcc_target_feature_disable_or_enable);
+impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for TargetFeatureDisableOrEnable<'_> {
+    fn into_diagnostic(self, dcx: &'_ DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
+        let mut diag = DiagnosticBuilder::new(
+            dcx,
+            level,
+            fluent::codegen_gcc_target_feature_disable_or_enable
+        );
         if let Some(span) = self.span {
             diag.set_span(span);
         };
diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs
index 671a225259a..6b7e38a6cb1 100644
--- a/compiler/rustc_codegen_llvm/src/errors.rs
+++ b/compiler/rustc_codegen_llvm/src/errors.rs
@@ -4,9 +4,7 @@ use std::path::Path;
 
 use crate::fluent_generated as fluent;
 use rustc_data_structures::small_c_str::SmallCStr;
-use rustc_errors::{
-    DiagCtxt, DiagnosticBuilder, EmissionGuarantee, ErrorGuaranteed, FatalError, IntoDiagnostic,
-};
+use rustc_errors::{DiagCtxt, DiagnosticBuilder, EmissionGuarantee, IntoDiagnostic, Level};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::Span;
 
@@ -101,13 +99,14 @@ pub(crate) struct DynamicLinkingWithLTO;
 
 pub(crate) struct ParseTargetMachineConfig<'a>(pub LlvmError<'a>);
 
-impl IntoDiagnostic<'_, FatalError> for ParseTargetMachineConfig<'_> {
-    fn into_diagnostic(self, dcx: &'_ DiagCtxt) -> DiagnosticBuilder<'_, FatalError> {
-        let diag: DiagnosticBuilder<'_, FatalError> = self.0.into_diagnostic(dcx);
+impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for ParseTargetMachineConfig<'_> {
+    fn into_diagnostic(self, dcx: &'_ DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
+        let diag: DiagnosticBuilder<'_, G> = self.0.into_diagnostic(dcx, level);
         let (message, _) = diag.styled_message().first().expect("`LlvmError` with no message");
         let message = dcx.eagerly_translate_to_string(message.clone(), diag.args());
 
-        let mut diag = dcx.struct_almost_fatal(fluent::codegen_llvm_parse_target_machine_config);
+        let mut diag =
+            DiagnosticBuilder::new(dcx, level, fluent::codegen_llvm_parse_target_machine_config);
         diag.set_arg("error", message);
         diag
     }
@@ -123,9 +122,13 @@ pub(crate) struct TargetFeatureDisableOrEnable<'a> {
 #[help(codegen_llvm_missing_features)]
 pub(crate) struct MissingFeatures;
 
-impl IntoDiagnostic<'_, ErrorGuaranteed> for TargetFeatureDisableOrEnable<'_> {
-    fn into_diagnostic(self, dcx: &'_ DiagCtxt) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = dcx.struct_err(fluent::codegen_llvm_target_feature_disable_or_enable);
+impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for TargetFeatureDisableOrEnable<'_> {
+    fn into_diagnostic(self, dcx: &'_ DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
+        let mut diag = DiagnosticBuilder::new(
+            dcx,
+            level,
+            fluent::codegen_llvm_target_feature_disable_or_enable,
+        );
         if let Some(span) = self.span {
             diag.set_span(span);
         };
@@ -184,7 +187,7 @@ pub enum LlvmError<'a> {
 pub(crate) struct WithLlvmError<'a>(pub LlvmError<'a>, pub String);
 
 impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for WithLlvmError<'_> {
-    fn into_diagnostic(self, dcx: &'_ DiagCtxt) -> DiagnosticBuilder<'_, G> {
+    fn into_diagnostic(self, dcx: &'_ DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
         use LlvmError::*;
         let msg_with_llvm_err = match &self.0 {
             WriteOutput { .. } => fluent::codegen_llvm_write_output_with_llvm_err,
@@ -201,7 +204,7 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for WithLlvmError<'_> {
             PrepareThinLtoModule => fluent::codegen_llvm_prepare_thin_lto_module_with_llvm_err,
             ParseBitcode => fluent::codegen_llvm_parse_bitcode_with_llvm_err,
         };
-        let mut diag = self.0.into_diagnostic(dcx);
+        let mut diag = self.0.into_diagnostic(dcx, level);
         diag.set_primary_message(msg_with_llvm_err);
         diag.set_arg("llvm_err", self.1);
         diag
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index 668d39afbda..2b628d2aa69 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -4,8 +4,8 @@ use crate::assert_module_sources::CguReuse;
 use crate::back::command::Command;
 use crate::fluent_generated as fluent;
 use rustc_errors::{
-    DiagCtxt, DiagnosticArgValue, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic,
-    IntoDiagnosticArg,
+    DiagCtxt, DiagnosticArgValue, DiagnosticBuilder, EmissionGuarantee, IntoDiagnostic,
+    IntoDiagnosticArg, Level,
 };
 use rustc_macros::Diagnostic;
 use rustc_middle::ty::layout::LayoutError;
@@ -209,192 +209,193 @@ pub enum LinkRlibError {
 
 pub struct ThorinErrorWrapper(pub thorin::Error);
 
-impl IntoDiagnostic<'_> for ThorinErrorWrapper {
-    fn into_diagnostic(self, dcx: &DiagCtxt) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for ThorinErrorWrapper {
+    fn into_diagnostic(self, dcx: &DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
+        let build = |msg| DiagnosticBuilder::new(dcx, level, msg);
         let mut diag;
         match self.0 {
             thorin::Error::ReadInput(_) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_read_input_failure);
+                diag = build(fluent::codegen_ssa_thorin_read_input_failure);
                 diag
             }
             thorin::Error::ParseFileKind(_) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_parse_input_file_kind);
+                diag = build(fluent::codegen_ssa_thorin_parse_input_file_kind);
                 diag
             }
             thorin::Error::ParseObjectFile(_) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_parse_input_object_file);
+                diag = build(fluent::codegen_ssa_thorin_parse_input_object_file);
                 diag
             }
             thorin::Error::ParseArchiveFile(_) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_parse_input_archive_file);
+                diag = build(fluent::codegen_ssa_thorin_parse_input_archive_file);
                 diag
             }
             thorin::Error::ParseArchiveMember(_) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_parse_archive_member);
+                diag = build(fluent::codegen_ssa_thorin_parse_archive_member);
                 diag
             }
             thorin::Error::InvalidInputKind => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_invalid_input_kind);
+                diag = build(fluent::codegen_ssa_thorin_invalid_input_kind);
                 diag
             }
             thorin::Error::DecompressData(_) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_decompress_data);
+                diag = build(fluent::codegen_ssa_thorin_decompress_data);
                 diag
             }
             thorin::Error::NamelessSection(_, offset) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_section_without_name);
+                diag = build(fluent::codegen_ssa_thorin_section_without_name);
                 diag.set_arg("offset", format!("0x{offset:08x}"));
                 diag
             }
             thorin::Error::RelocationWithInvalidSymbol(section, offset) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_relocation_with_invalid_symbol);
+                diag = build(fluent::codegen_ssa_thorin_relocation_with_invalid_symbol);
                 diag.set_arg("section", section);
                 diag.set_arg("offset", format!("0x{offset:08x}"));
                 diag
             }
             thorin::Error::MultipleRelocations(section, offset) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_multiple_relocations);
+                diag = build(fluent::codegen_ssa_thorin_multiple_relocations);
                 diag.set_arg("section", section);
                 diag.set_arg("offset", format!("0x{offset:08x}"));
                 diag
             }
             thorin::Error::UnsupportedRelocation(section, offset) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_unsupported_relocation);
+                diag = build(fluent::codegen_ssa_thorin_unsupported_relocation);
                 diag.set_arg("section", section);
                 diag.set_arg("offset", format!("0x{offset:08x}"));
                 diag
             }
             thorin::Error::MissingDwoName(id) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_missing_dwo_name);
+                diag = build(fluent::codegen_ssa_thorin_missing_dwo_name);
                 diag.set_arg("id", format!("0x{id:08x}"));
                 diag
             }
             thorin::Error::NoCompilationUnits => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_no_compilation_units);
+                diag = build(fluent::codegen_ssa_thorin_no_compilation_units);
                 diag
             }
             thorin::Error::NoDie => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_no_die);
+                diag = build(fluent::codegen_ssa_thorin_no_die);
                 diag
             }
             thorin::Error::TopLevelDieNotUnit => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_top_level_die_not_unit);
+                diag = build(fluent::codegen_ssa_thorin_top_level_die_not_unit);
                 diag
             }
             thorin::Error::MissingRequiredSection(section) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_missing_required_section);
+                diag = build(fluent::codegen_ssa_thorin_missing_required_section);
                 diag.set_arg("section", section);
                 diag
             }
             thorin::Error::ParseUnitAbbreviations(_) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_parse_unit_abbreviations);
+                diag = build(fluent::codegen_ssa_thorin_parse_unit_abbreviations);
                 diag
             }
             thorin::Error::ParseUnitAttribute(_) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_parse_unit_attribute);
+                diag = build(fluent::codegen_ssa_thorin_parse_unit_attribute);
                 diag
             }
             thorin::Error::ParseUnitHeader(_) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_parse_unit_header);
+                diag = build(fluent::codegen_ssa_thorin_parse_unit_header);
                 diag
             }
             thorin::Error::ParseUnit(_) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_parse_unit);
+                diag = build(fluent::codegen_ssa_thorin_parse_unit);
                 diag
             }
             thorin::Error::IncompatibleIndexVersion(section, format, actual) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_incompatible_index_version);
+                diag = build(fluent::codegen_ssa_thorin_incompatible_index_version);
                 diag.set_arg("section", section);
                 diag.set_arg("actual", actual);
                 diag.set_arg("format", format);
                 diag
             }
             thorin::Error::OffsetAtIndex(_, index) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_offset_at_index);
+                diag = build(fluent::codegen_ssa_thorin_offset_at_index);
                 diag.set_arg("index", index);
                 diag
             }
             thorin::Error::StrAtOffset(_, offset) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_str_at_offset);
+                diag = build(fluent::codegen_ssa_thorin_str_at_offset);
                 diag.set_arg("offset", format!("0x{offset:08x}"));
                 diag
             }
             thorin::Error::ParseIndex(_, section) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_parse_index);
+                diag = build(fluent::codegen_ssa_thorin_parse_index);
                 diag.set_arg("section", section);
                 diag
             }
             thorin::Error::UnitNotInIndex(unit) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_unit_not_in_index);
+                diag = build(fluent::codegen_ssa_thorin_unit_not_in_index);
                 diag.set_arg("unit", format!("0x{unit:08x}"));
                 diag
             }
             thorin::Error::RowNotInIndex(_, row) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_row_not_in_index);
+                diag = build(fluent::codegen_ssa_thorin_row_not_in_index);
                 diag.set_arg("row", row);
                 diag
             }
             thorin::Error::SectionNotInRow => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_section_not_in_row);
+                diag = build(fluent::codegen_ssa_thorin_section_not_in_row);
                 diag
             }
             thorin::Error::EmptyUnit(unit) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_empty_unit);
+                diag = build(fluent::codegen_ssa_thorin_empty_unit);
                 diag.set_arg("unit", format!("0x{unit:08x}"));
                 diag
             }
             thorin::Error::MultipleDebugInfoSection => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_multiple_debug_info_section);
+                diag = build(fluent::codegen_ssa_thorin_multiple_debug_info_section);
                 diag
             }
             thorin::Error::MultipleDebugTypesSection => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_multiple_debug_types_section);
+                diag = build(fluent::codegen_ssa_thorin_multiple_debug_types_section);
                 diag
             }
             thorin::Error::NotSplitUnit => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_not_split_unit);
+                diag = build(fluent::codegen_ssa_thorin_not_split_unit);
                 diag
             }
             thorin::Error::DuplicateUnit(unit) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_duplicate_unit);
+                diag = build(fluent::codegen_ssa_thorin_duplicate_unit);
                 diag.set_arg("unit", format!("0x{unit:08x}"));
                 diag
             }
             thorin::Error::MissingReferencedUnit(unit) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_missing_referenced_unit);
+                diag = build(fluent::codegen_ssa_thorin_missing_referenced_unit);
                 diag.set_arg("unit", format!("0x{unit:08x}"));
                 diag
             }
             thorin::Error::NoOutputObjectCreated => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_not_output_object_created);
+                diag = build(fluent::codegen_ssa_thorin_not_output_object_created);
                 diag
             }
             thorin::Error::MixedInputEncodings => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_mixed_input_encodings);
+                diag = build(fluent::codegen_ssa_thorin_mixed_input_encodings);
                 diag
             }
             thorin::Error::Io(e) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_io);
+                diag = build(fluent::codegen_ssa_thorin_io);
                 diag.set_arg("error", format!("{e}"));
                 diag
             }
             thorin::Error::ObjectRead(e) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_object_read);
+                diag = build(fluent::codegen_ssa_thorin_object_read);
                 diag.set_arg("error", format!("{e}"));
                 diag
             }
             thorin::Error::ObjectWrite(e) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_object_write);
+                diag = build(fluent::codegen_ssa_thorin_object_write);
                 diag.set_arg("error", format!("{e}"));
                 diag
             }
             thorin::Error::GimliRead(e) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_gimli_read);
+                diag = build(fluent::codegen_ssa_thorin_gimli_read);
                 diag.set_arg("error", format!("{e}"));
                 diag
             }
             thorin::Error::GimliWrite(e) => {
-                diag = dcx.struct_err(fluent::codegen_ssa_thorin_gimli_write);
+                diag = build(fluent::codegen_ssa_thorin_gimli_write);
                 diag.set_arg("error", format!("{e}"));
                 diag
             }
@@ -410,9 +411,9 @@ pub struct LinkingFailed<'a> {
     pub escaped_output: String,
 }
 
-impl IntoDiagnostic<'_> for LinkingFailed<'_> {
-    fn into_diagnostic(self, dcx: &DiagCtxt) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = dcx.struct_err(fluent::codegen_ssa_linking_failed);
+impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for LinkingFailed<'_> {
+    fn into_diagnostic(self, dcx: &DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
+        let mut diag = DiagnosticBuilder::new(dcx, level, fluent::codegen_ssa_linking_failed);
         diag.set_arg("linker_path", format!("{}", self.linker_path.display()));
         diag.set_arg("exit_status", format!("{}", self.exit_status));
 
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index adce1f5430f..eb9bf52676a 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -1,6 +1,6 @@
 use rustc_errors::{
     DiagCtxt, DiagnosticArgValue, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee,
-    IntoDiagnostic,
+    IntoDiagnostic, Level,
 };
 use rustc_hir::ConstContext;
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
@@ -875,7 +875,10 @@ impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> {
             | InvalidProgramInfo::AlreadyReported(_)
             | InvalidProgramInfo::ConstPropNonsense => {}
             InvalidProgramInfo::Layout(e) => {
-                let diag: DiagnosticBuilder<'_, ()> = e.into_diagnostic().into_diagnostic(dcx);
+                // The level doesn't matter, `diag` is consumed without it being used.
+                let dummy_level = Level::Bug;
+                let diag: DiagnosticBuilder<'_, ()> =
+                    e.into_diagnostic().into_diagnostic(dcx, dummy_level);
                 for (name, val) in diag.args() {
                     builder.set_arg(name.clone(), val.clone());
                 }
diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
index 3f66af1fcff..828560ec452 100644
--- a/compiler/rustc_errors/src/diagnostic_builder.rs
+++ b/compiler/rustc_errors/src/diagnostic_builder.rs
@@ -21,7 +21,7 @@ use std::thread::panicking;
 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
     }
@@ -101,18 +101,31 @@ 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;
+    fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult;
+}
 
-    /// 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>;
+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.inner.state {
+            // First `.emit()` call, the `&DiagCtxt` is still available.
+            DiagnosticBuilderState::Emittable(dcx) => {
+                self.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
+
+                dcx.emit_diagnostic_without_consuming(&mut self.inner.diagnostic);
+            }
+            // `.emit()` was previously called, disallowed from repeating it.
+            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
+        }
+    }
 }
 
 impl<'a> DiagnosticBuilder<'a, ErrorGuaranteed> {
@@ -126,7 +139,8 @@ impl<'a> DiagnosticBuilder<'a, ErrorGuaranteed> {
 
 // FIXME(eddyb) make `ErrorGuaranteed` impossible to create outside `.emit()`.
 impl EmissionGuarantee for ErrorGuaranteed {
-    fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
+    fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
+        // Contrast this with `emit_producing_nothing`.
         match db.inner.state {
             // First `.emit()` call, the `&DiagCtxt` is still available.
             DiagnosticBuilderState::Emittable(dcx) => {
@@ -163,142 +177,48 @@ impl EmissionGuarantee for ErrorGuaranteed {
             }
         }
     }
-
-    #[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
@@ -339,16 +259,10 @@ impl<G: EmissionGuarantee> DerefMut for DiagnosticBuilder<'_, G> {
 }
 
 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
@@ -369,8 +283,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 +292,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();
         }
@@ -400,15 +314,15 @@ 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 {
             // No `.emit()` calls, the `&DiagCtxt` is still available.
@@ -449,7 +363,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
     }
 
     /// 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 +379,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()
     }
diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs
index 3e4b3ee758a..ccc951543d8 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
             }
@@ -362,9 +373,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/lib.rs b/compiler/rustc_errors/src/lib.rs
index 959e26fec70..518a2ddb71d 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)]
@@ -401,7 +402,7 @@ pub use diagnostic::{
     AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId,
     DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic,
 };
-pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee, Noted};
+pub use diagnostic_builder::{BugAbort, DiagnosticBuilder, EmissionGuarantee, FatalAbort};
 pub use diagnostic_impls::{
     DiagnosticArgFromDisplay, DiagnosticSymbolList, ExpectedLifetimeParameter,
     IndicateAnonymousLifetime, InvalidFlushedDelayedDiagnosticLevel, LabelKind,
@@ -722,21 +723,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:
@@ -922,7 +908,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 +922,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,7 +931,10 @@ 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<'_, !> {
+    pub fn struct_fatal(
+        &self,
+        msg: impl Into<DiagnosticMessage>,
+    ) -> DiagnosticBuilder<'_, FatalAbort> {
         DiagnosticBuilder::new(self, Level::Fatal, msg)
     }
 
@@ -972,6 +961,26 @@ impl DiagCtxt {
         DiagnosticBuilder::new(self, Level::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, Level::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]
     #[track_caller]
     pub fn span_fatal(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! {
@@ -1071,13 +1080,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 +1117,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]
@@ -1274,18 +1276,19 @@ impl DiagCtxt {
         self.create_err(err).emit()
     }
 
+    #[track_caller]
     pub fn create_err<'a>(
         &'a self,
         err: impl IntoDiagnostic<'a>,
     ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
-        err.into_diagnostic(self)
+        err.into_diagnostic(self, Level::Error { lint: false })
     }
 
     pub fn create_warning<'a>(
         &'a self,
         warning: impl IntoDiagnostic<'a, ()>,
     ) -> DiagnosticBuilder<'a, ()> {
-        warning.into_diagnostic(self)
+        warning.into_diagnostic(self, Level::Warning(None))
     }
 
     pub fn emit_warning<'a>(&'a self, warning: impl IntoDiagnostic<'a, ()>) {
@@ -1296,7 +1299,7 @@ impl DiagCtxt {
         &'a self,
         fatal: impl IntoDiagnostic<'a, FatalError>,
     ) -> DiagnosticBuilder<'a, FatalError> {
-        fatal.into_diagnostic(self)
+        fatal.into_diagnostic(self, Level::Fatal)
     }
 
     pub fn emit_almost_fatal<'a>(
@@ -1308,38 +1311,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, Level::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, Level::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, Level::Note)
     }
 
     pub fn emit_artifact_notification(&self, path: &Path, artifact_type: &str) {
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index b63609c48e9..ee0b10edfda 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -1159,6 +1159,7 @@ impl<'a> ExtCtxt<'a> {
         // Fixme: does this result in errors?
         self.expansions.clear();
     }
+    #[rustc_lint_diagnostics]
     pub fn bug(&self, msg: &'static str) -> ! {
         self.sess.dcx().bug(msg);
     }
@@ -1204,11 +1205,10 @@ pub fn resolve_path(
                 .expect("attempting to resolve a file path in an external file"),
             FileName::DocTest(path, _) => path,
             other => {
-                return Err(errors::ResolveRelativePath {
+                return Err(parse_sess.dcx.create_err(errors::ResolveRelativePath {
                     span,
                     path: parse_sess.source_map().filename_for_diagnostics(&other).to_string(),
-                }
-                .into_diagnostic(&parse_sess.dcx));
+                }));
             }
         };
         result.pop();
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index f461b6a94ec..41f30057902 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -2,8 +2,8 @@
 
 use crate::fluent_generated as fluent;
 use rustc_errors::{
-    error_code, Applicability, DiagCtxt, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic,
-    MultiSpan,
+    error_code, Applicability, DiagCtxt, DiagnosticBuilder, EmissionGuarantee, IntoDiagnostic,
+    Level, MultiSpan,
 };
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
 use rustc_middle::ty::Ty;
@@ -315,14 +315,12 @@ pub struct MissingTypeParams {
 }
 
 // Manual implementation of `IntoDiagnostic` to be able to call `span_to_snippet`.
-impl<'a> IntoDiagnostic<'a> for MissingTypeParams {
+impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for MissingTypeParams {
     #[track_caller]
-    fn into_diagnostic(self, dcx: &'a DiagCtxt) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
-        let mut err = dcx.struct_span_err_with_code(
-            self.span,
-            fluent::hir_analysis_missing_type_params,
-            error_code!(E0393),
-        );
+    fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> {
+        let mut err = DiagnosticBuilder::new(dcx, level, fluent::hir_analysis_missing_type_params);
+        err.set_span(self.span);
+        err.code(error_code!(E0393));
         err.set_arg("parameterCount", self.missing_type_params.len());
         err.set_arg(
             "parameters",
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 7bd2c3f8b6b..5d5b5f39ccf 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -722,7 +722,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     if let [segment] = path.segments
                         && segment.ident.name == sym::rust
                     {
-                        fatally_break_rust(self.tcx);
+                        fatally_break_rust(self.tcx, expr.span);
                     }
                 }
             }
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index 13a24948611..c6d7650f745 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -52,7 +52,7 @@ use crate::expectation::Expectation;
 use crate::fn_ctxt::RawTy;
 use crate::gather_locals::GatherLocalsVisitor;
 use rustc_data_structures::unord::UnordSet;
-use rustc_errors::{struct_span_err, DiagnosticId, ErrorGuaranteed, MultiSpan};
+use rustc_errors::{struct_span_err, DiagnosticId, ErrorGuaranteed};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::intravisit::Visitor;
@@ -412,24 +412,25 @@ enum TupleArgumentsFlag {
     TupleArguments,
 }
 
-fn fatally_break_rust(tcx: TyCtxt<'_>) {
+fn fatally_break_rust(tcx: TyCtxt<'_>, span: Span) -> ! {
     let dcx = tcx.sess.dcx();
-    dcx.span_bug_no_panic(
-        MultiSpan::new(),
+    let mut diag = dcx.struct_span_bug(
+        span,
         "It looks like you're trying to break rust; would you like some ICE?",
     );
-    dcx.note("the compiler expectedly panicked. this is a feature.");
-    dcx.note(
+    diag.note("the compiler expectedly panicked. this is a feature.");
+    diag.note(
         "we would appreciate a joke overview: \
          https://github.com/rust-lang/rust/issues/43162#issuecomment-320764675",
     );
-    dcx.note(format!("rustc {} running on {}", tcx.sess.cfg_version, config::host_triple(),));
+    diag.note(format!("rustc {} running on {}", tcx.sess.cfg_version, config::host_triple(),));
     if let Some((flags, excluded_cargo_defaults)) = rustc_session::utils::extra_compiler_flags() {
-        dcx.note(format!("compiler flags: {}", flags.join(" ")));
+        diag.note(format!("compiler flags: {}", flags.join(" ")));
         if excluded_cargo_defaults {
-            dcx.note("some of the compiler flags provided by cargo are hidden");
+            diag.note("some of the compiler flags provided by cargo are hidden");
         }
     }
+    diag.emit()
 }
 
 /// `expected` here is the expected number of explicit generic arguments on the trait.
diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
index 817a4451dd1..700fb5d3510 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
@@ -5,7 +5,6 @@ use crate::errors::{
 use crate::infer::error_reporting::TypeErrCtxt;
 use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use crate::infer::InferCtxt;
-use rustc_errors::IntoDiagnostic;
 use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, IntoDiagnosticArg};
 use rustc_hir as hir;
 use rustc_hir::def::Res;
@@ -367,7 +366,7 @@ impl<'tcx> InferCtxt<'tcx> {
         let multi_suggestions = Vec::new();
         let bad_label = Some(arg_data.make_bad_error(span));
         match error_code {
-            TypeAnnotationNeeded::E0282 => AnnotationRequired {
+            TypeAnnotationNeeded::E0282 => self.tcx.sess.dcx().create_err(AnnotationRequired {
                 span,
                 source_kind,
                 source_name,
@@ -375,9 +374,8 @@ impl<'tcx> InferCtxt<'tcx> {
                 infer_subdiags,
                 multi_suggestions,
                 bad_label,
-            }
-            .into_diagnostic(self.tcx.sess.dcx()),
-            TypeAnnotationNeeded::E0283 => AmbiguousImpl {
+            }),
+            TypeAnnotationNeeded::E0283 => self.tcx.sess.dcx().create_err(AmbiguousImpl {
                 span,
                 source_kind,
                 source_name,
@@ -385,9 +383,8 @@ impl<'tcx> InferCtxt<'tcx> {
                 infer_subdiags,
                 multi_suggestions,
                 bad_label,
-            }
-            .into_diagnostic(self.tcx.sess.dcx()),
-            TypeAnnotationNeeded::E0284 => AmbiguousReturn {
+            }),
+            TypeAnnotationNeeded::E0284 => self.tcx.sess.dcx().create_err(AmbiguousReturn {
                 span,
                 source_kind,
                 source_name,
@@ -395,8 +392,7 @@ impl<'tcx> InferCtxt<'tcx> {
                 infer_subdiags,
                 multi_suggestions,
                 bad_label,
-            }
-            .into_diagnostic(self.tcx.sess.dcx()),
+            }),
         }
     }
 }
@@ -574,7 +570,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             }
         }
         match error_code {
-            TypeAnnotationNeeded::E0282 => AnnotationRequired {
+            TypeAnnotationNeeded::E0282 => self.tcx.sess.dcx().create_err(AnnotationRequired {
                 span,
                 source_kind,
                 source_name: &name,
@@ -582,9 +578,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 infer_subdiags,
                 multi_suggestions,
                 bad_label: None,
-            }
-            .into_diagnostic(self.tcx.sess.dcx()),
-            TypeAnnotationNeeded::E0283 => AmbiguousImpl {
+            }),
+            TypeAnnotationNeeded::E0283 => self.tcx.sess.dcx().create_err(AmbiguousImpl {
                 span,
                 source_kind,
                 source_name: &name,
@@ -592,9 +587,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 infer_subdiags,
                 multi_suggestions,
                 bad_label: None,
-            }
-            .into_diagnostic(self.tcx.sess.dcx()),
-            TypeAnnotationNeeded::E0284 => AmbiguousReturn {
+            }),
+            TypeAnnotationNeeded::E0284 => self.tcx.sess.dcx().create_err(AmbiguousReturn {
                 span,
                 source_kind,
                 source_name: &name,
@@ -602,8 +596,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 infer_subdiags,
                 multi_suggestions,
                 bad_label: None,
-            }
-            .into_diagnostic(self.tcx.sess.dcx()),
+            }),
         }
     }
 }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs
index 859c10ef142..1f1c2bc20e2 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs
@@ -5,9 +5,7 @@ use crate::errors::{
 use crate::fluent_generated as fluent;
 use crate::infer::error_reporting::{note_and_explain_region, TypeErrCtxt};
 use crate::infer::{self, SubregionOrigin};
-use rustc_errors::{
-    AddToDiagnostic, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic,
-};
+use rustc_errors::{AddToDiagnostic, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::traits::ObligationCauseCode;
 use rustc_middle::ty::error::TypeError;
@@ -136,11 +134,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     note_and_explain::PrefixKind::ContentValidFor,
                     note_and_explain::SuffixKind::Empty,
                 );
-                OutlivesContent {
+                self.tcx.sess.dcx().create_err(OutlivesContent {
                     span,
                     notes: reference_valid.into_iter().chain(content_valid).collect(),
-                }
-                .into_diagnostic(self.tcx.sess.dcx())
+                })
             }
             infer::RelateObjectBound(span) => {
                 let object_valid = note_and_explain::RegionExplanation::new(
@@ -157,11 +154,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     note_and_explain::PrefixKind::SourcePointerValidFor,
                     note_and_explain::SuffixKind::Empty,
                 );
-                OutlivesBound {
+                self.tcx.sess.dcx().create_err(OutlivesBound {
                     span,
                     notes: object_valid.into_iter().chain(pointer_valid).collect(),
-                }
-                .into_diagnostic(self.tcx.sess.dcx())
+                })
             }
             infer::RelateParamBound(span, ty, opt_span) => {
                 let prefix = match *sub {
@@ -176,8 +172,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 let note = note_and_explain::RegionExplanation::new(
                     self.tcx, sub, opt_span, prefix, suffix,
                 );
-                FulfillReqLifetime { span, ty: self.resolve_vars_if_possible(ty), note }
-                    .into_diagnostic(self.tcx.sess.dcx())
+                self.tcx.sess.dcx().create_err(FulfillReqLifetime {
+                    span,
+                    ty: self.resolve_vars_if_possible(ty),
+                    note,
+                })
             }
             infer::RelateRegionParamBound(span) => {
                 let param_instantiated = note_and_explain::RegionExplanation::new(
@@ -194,11 +193,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     note_and_explain::PrefixKind::LfParamMustOutlive,
                     note_and_explain::SuffixKind::Empty,
                 );
-                LfBoundNotSatisfied {
+                self.tcx.sess.dcx().create_err(LfBoundNotSatisfied {
                     span,
                     notes: param_instantiated.into_iter().chain(param_must_outlive).collect(),
-                }
-                .into_diagnostic(self.tcx.sess.dcx())
+                })
             }
             infer::ReferenceOutlivesReferent(ty, span) => {
                 let pointer_valid = note_and_explain::RegionExplanation::new(
@@ -215,12 +213,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     note_and_explain::PrefixKind::DataValidFor,
                     note_and_explain::SuffixKind::Empty,
                 );
-                RefLongerThanData {
+                self.tcx.sess.dcx().create_err(RefLongerThanData {
                     span,
                     ty: self.resolve_vars_if_possible(ty),
                     notes: pointer_valid.into_iter().chain(data_valid).collect(),
-                }
-                .into_diagnostic(self.tcx.sess.dcx())
+                })
             }
             infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => {
                 let mut err = self.report_extra_impl_obligation(
@@ -277,11 +274,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     note_and_explain::PrefixKind::LfMustOutlive,
                     note_and_explain::SuffixKind::Empty,
                 );
-                LfBoundNotSatisfied {
+                self.tcx.sess.dcx().create_err(LfBoundNotSatisfied {
                     span,
                     notes: instantiated.into_iter().chain(must_outlive).collect(),
-                }
-                .into_diagnostic(self.tcx.sess.dcx())
+                })
             }
         };
         if sub.is_error() || sup.is_error() {
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
index 5de0203fc1d..027c97330b1 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
@@ -2,7 +2,7 @@
 
 use std::cell::RefCell;
 
-use crate::diagnostics::diagnostic_builder::{DiagnosticDeriveBuilder, DiagnosticDeriveKind};
+use crate::diagnostics::diagnostic_builder::DiagnosticDeriveKind;
 use crate::diagnostics::error::{span_err, DiagnosticDeriveError};
 use crate::diagnostics::utils::SetOnce;
 use proc_macro2::TokenStream;
@@ -13,32 +13,21 @@ use synstructure::Structure;
 /// The central struct for constructing the `into_diagnostic` method from an annotated struct.
 pub(crate) struct DiagnosticDerive<'a> {
     structure: Structure<'a>,
-    builder: DiagnosticDeriveBuilder,
 }
 
 impl<'a> DiagnosticDerive<'a> {
-    pub(crate) fn new(diag: syn::Ident, dcx: syn::Ident, structure: Structure<'a>) -> Self {
-        Self {
-            builder: DiagnosticDeriveBuilder {
-                diag,
-                kind: DiagnosticDeriveKind::Diagnostic { dcx },
-            },
-            structure,
-        }
+    pub(crate) fn new(structure: Structure<'a>) -> Self {
+        Self { structure }
     }
 
     pub(crate) fn into_tokens(self) -> TokenStream {
-        let DiagnosticDerive { mut structure, mut builder } = self;
-
+        let DiagnosticDerive { mut structure } = self;
+        let kind = DiagnosticDeriveKind::Diagnostic;
         let slugs = RefCell::new(Vec::new());
-        let implementation = builder.each_variant(&mut structure, |mut builder, variant| {
+        let implementation = kind.each_variant(&mut structure, |mut builder, variant| {
             let preamble = builder.preamble(variant);
             let body = builder.body(variant);
 
-            let diag = &builder.parent.diag;
-            let DiagnosticDeriveKind::Diagnostic { dcx } = &builder.parent.kind else {
-                unreachable!()
-            };
             let init = match builder.slug.value_ref() {
                 None => {
                     span_err(builder.span, "diagnostic slug not specified")
@@ -62,7 +51,11 @@ impl<'a> DiagnosticDerive<'a> {
                 Some(slug) => {
                     slugs.borrow_mut().push(slug.clone());
                     quote! {
-                        let mut #diag = #dcx.struct_diagnostic(crate::fluent_generated::#slug);
+                        let mut diag = rustc_errors::DiagnosticBuilder::new(
+                            dcx,
+                            level,
+                            crate::fluent_generated::#slug
+                        );
                     }
                 }
             };
@@ -73,12 +66,10 @@ impl<'a> DiagnosticDerive<'a> {
                 #formatting_init
                 #preamble
                 #body
-                #diag
+                diag
             }
         });
 
-        let DiagnosticDeriveKind::Diagnostic { dcx } = &builder.kind else { unreachable!() };
-
         // A lifetime of `'a` causes conflicts, but `_sess` is fine.
         let mut imp = structure.gen_impl(quote! {
             gen impl<'_sess, G>
@@ -90,9 +81,9 @@ impl<'a> DiagnosticDerive<'a> {
                 #[track_caller]
                 fn into_diagnostic(
                     self,
-                    #dcx: &'_sess rustc_errors::DiagCtxt
+                    dcx: &'_sess rustc_errors::DiagCtxt,
+                    level: rustc_errors::Level
                 ) -> rustc_errors::DiagnosticBuilder<'_sess, G> {
-                    use rustc_errors::IntoDiagnosticArg;
                     #implementation
                 }
             }
@@ -107,36 +98,31 @@ impl<'a> DiagnosticDerive<'a> {
 /// The central struct for constructing the `decorate_lint` method from an annotated struct.
 pub(crate) struct LintDiagnosticDerive<'a> {
     structure: Structure<'a>,
-    builder: DiagnosticDeriveBuilder,
 }
 
 impl<'a> LintDiagnosticDerive<'a> {
-    pub(crate) fn new(diag: syn::Ident, structure: Structure<'a>) -> Self {
-        Self {
-            builder: DiagnosticDeriveBuilder { diag, kind: DiagnosticDeriveKind::LintDiagnostic },
-            structure,
-        }
+    pub(crate) fn new(structure: Structure<'a>) -> Self {
+        Self { structure }
     }
 
     pub(crate) fn into_tokens(self) -> TokenStream {
-        let LintDiagnosticDerive { mut structure, mut builder } = self;
-
-        let implementation = builder.each_variant(&mut structure, |mut builder, variant| {
+        let LintDiagnosticDerive { mut structure } = self;
+        let kind = DiagnosticDeriveKind::LintDiagnostic;
+        let implementation = kind.each_variant(&mut structure, |mut builder, variant| {
             let preamble = builder.preamble(variant);
             let body = builder.body(variant);
 
-            let diag = &builder.parent.diag;
             let formatting_init = &builder.formatting_init;
             quote! {
                 #preamble
                 #formatting_init
                 #body
-                #diag
+                diag
             }
         });
 
         let slugs = RefCell::new(Vec::new());
-        let msg = builder.each_variant(&mut structure, |mut builder, variant| {
+        let msg = kind.each_variant(&mut structure, |mut builder, variant| {
             // Collect the slug by generating the preamble.
             let _ = builder.preamble(variant);
 
@@ -169,15 +155,13 @@ impl<'a> LintDiagnosticDerive<'a> {
             }
         });
 
-        let diag = &builder.diag;
         let mut imp = structure.gen_impl(quote! {
             gen impl<'__a> rustc_errors::DecorateLint<'__a, ()> for @Self {
                 #[track_caller]
                 fn decorate_lint<'__b>(
                     self,
-                    #diag: &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()>
+                    diag: &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()>
                 ) {
-                    use rustc_errors::IntoDiagnosticArg;
                     #implementation;
                 }
 
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
index 511654d9949..f3e98d68b60 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
@@ -17,28 +17,18 @@ use synstructure::{BindingInfo, Structure, VariantInfo};
 use super::utils::SubdiagnosticVariant;
 
 /// What kind of diagnostic is being derived - a fatal/error/warning or a lint?
-#[derive(Clone, PartialEq, Eq)]
+#[derive(Clone, Copy, PartialEq, Eq)]
 pub(crate) enum DiagnosticDeriveKind {
-    Diagnostic { dcx: syn::Ident },
+    Diagnostic,
     LintDiagnostic,
 }
 
-/// Tracks persistent information required for the entire type when building up individual calls to
-/// diagnostic methods for generated diagnostic derives - both `Diagnostic` for
-/// fatal/errors/warnings and `LintDiagnostic` for lints.
-pub(crate) struct DiagnosticDeriveBuilder {
-    /// The identifier to use for the generated `DiagnosticBuilder` instance.
-    pub diag: syn::Ident,
-    /// Kind of diagnostic that should be derived.
-    pub kind: DiagnosticDeriveKind,
-}
-
 /// Tracks persistent information required for a specific variant when building up individual calls
 /// to diagnostic methods for generated diagnostic derives - both `Diagnostic` for
 /// fatal/errors/warnings and `LintDiagnostic` for lints.
-pub(crate) struct DiagnosticDeriveVariantBuilder<'parent> {
-    /// The parent builder for the entire type.
-    pub parent: &'parent DiagnosticDeriveBuilder,
+pub(crate) struct DiagnosticDeriveVariantBuilder {
+    /// The kind for the entire type.
+    pub kind: DiagnosticDeriveKind,
 
     /// Initialization of format strings for code suggestions.
     pub formatting_init: TokenStream,
@@ -59,19 +49,19 @@ pub(crate) struct DiagnosticDeriveVariantBuilder<'parent> {
     pub code: SpannedOption<()>,
 }
 
-impl<'a> HasFieldMap for DiagnosticDeriveVariantBuilder<'a> {
+impl HasFieldMap for DiagnosticDeriveVariantBuilder {
     fn get_field_binding(&self, field: &String) -> Option<&TokenStream> {
         self.field_map.get(field)
     }
 }
 
-impl DiagnosticDeriveBuilder {
+impl DiagnosticDeriveKind {
     /// Call `f` for the struct or for each variant of the enum, returning a `TokenStream` with the
     /// tokens from `f` wrapped in an `match` expression. Emits errors for use of derive on unions
     /// or attributes on the type itself when input is an enum.
-    pub(crate) fn each_variant<'s, F>(&mut self, structure: &mut Structure<'s>, f: F) -> TokenStream
+    pub(crate) fn each_variant<'s, F>(self, structure: &mut Structure<'s>, f: F) -> TokenStream
     where
-        F: for<'a, 'v> Fn(DiagnosticDeriveVariantBuilder<'a>, &VariantInfo<'v>) -> TokenStream,
+        F: for<'v> Fn(DiagnosticDeriveVariantBuilder, &VariantInfo<'v>) -> TokenStream,
     {
         let ast = structure.ast();
         let span = ast.span().unwrap();
@@ -101,7 +91,7 @@ impl DiagnosticDeriveBuilder {
                 _ => variant.ast().ident.span().unwrap(),
             };
             let builder = DiagnosticDeriveVariantBuilder {
-                parent: self,
+                kind: self,
                 span,
                 field_map: build_field_mapping(variant),
                 formatting_init: TokenStream::new(),
@@ -119,7 +109,7 @@ impl DiagnosticDeriveBuilder {
     }
 }
 
-impl<'a> DiagnosticDeriveVariantBuilder<'a> {
+impl DiagnosticDeriveVariantBuilder {
     /// Generates calls to `code` and similar functions based on the attributes on the type or
     /// variant.
     pub(crate) fn preamble(&mut self, variant: &VariantInfo<'_>) -> TokenStream {
@@ -184,8 +174,6 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
         &mut self,
         attr: &Attribute,
     ) -> Result<TokenStream, DiagnosticDeriveError> {
-        let diag = &self.parent.diag;
-
         // Always allow documentation comments.
         if is_doc_comment(attr) {
             return Ok(quote! {});
@@ -223,7 +211,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
 
                     let code = nested.parse::<syn::LitStr>()?;
                     tokens.extend(quote! {
-                        #diag.code(rustc_errors::DiagnosticId::Error(#code.to_string()));
+                        diag.code(rustc_errors::DiagnosticId::Error(#code.to_string()));
                     });
                 } else {
                     span_err(path.span().unwrap(), "unknown argument")
@@ -257,8 +245,6 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
     }
 
     fn generate_field_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream {
-        let diag = &self.parent.diag;
-
         let field = binding_info.ast();
         let mut field_binding = binding_info.binding.clone();
         field_binding.set_span(field.ty.span());
@@ -267,7 +253,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
         let ident = format_ident!("{}", ident); // strip `r#` prefix, if present
 
         quote! {
-            #diag.set_arg(
+            diag.set_arg(
                 stringify!(#ident),
                 #field_binding
             );
@@ -322,8 +308,6 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
         info: FieldInfo<'_>,
         binding: TokenStream,
     ) -> Result<TokenStream, DiagnosticDeriveError> {
-        let diag = &self.parent.diag;
-
         let ident = &attr.path().segments.last().unwrap().ident;
         let name = ident.to_string();
         match (&attr.meta, name.as_str()) {
@@ -331,12 +315,12 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
             // `set_arg` call will not be generated.
             (Meta::Path(_), "skip_arg") => return Ok(quote! {}),
             (Meta::Path(_), "primary_span") => {
-                match self.parent.kind {
-                    DiagnosticDeriveKind::Diagnostic { .. } => {
+                match self.kind {
+                    DiagnosticDeriveKind::Diagnostic => {
                         report_error_if_not_applied_to_span(attr, &info)?;
 
                         return Ok(quote! {
-                            #diag.set_span(#binding);
+                            diag.set_span(#binding);
                         });
                     }
                     DiagnosticDeriveKind::LintDiagnostic => {
@@ -348,13 +332,13 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
             }
             (Meta::Path(_), "subdiagnostic") => {
                 if FieldInnerTy::from_type(&info.binding.ast().ty).will_iterate() {
-                    let DiagnosticDeriveKind::Diagnostic { dcx } = &self.parent.kind else {
+                    let DiagnosticDeriveKind::Diagnostic = self.kind else {
                         // No eager translation for lints.
-                        return Ok(quote! { #diag.subdiagnostic(#binding); });
+                        return Ok(quote! { diag.subdiagnostic(#binding); });
                     };
-                    return Ok(quote! { #diag.eager_subdiagnostic(#dcx, #binding); });
+                    return Ok(quote! { diag.eager_subdiagnostic(dcx, #binding); });
                 } else {
-                    return Ok(quote! { #diag.subdiagnostic(#binding); });
+                    return Ok(quote! { diag.subdiagnostic(#binding); });
                 }
             }
             (Meta::List(meta_list), "subdiagnostic") => {
@@ -376,15 +360,15 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
                     return Ok(quote! {});
                 }
 
-                let dcx = match &self.parent.kind {
-                    DiagnosticDeriveKind::Diagnostic { dcx } => dcx,
+                match &self.kind {
+                    DiagnosticDeriveKind::Diagnostic => {}
                     DiagnosticDeriveKind::LintDiagnostic => {
                         throw_invalid_attr!(attr, |diag| {
                             diag.help("eager subdiagnostics are not supported on lints")
                         })
                     }
                 };
-                return Ok(quote! { #diag.eager_subdiagnostic(#dcx, #binding); });
+                return Ok(quote! { diag.eager_subdiagnostic(dcx, #binding); });
             }
             _ => (),
         }
@@ -442,7 +426,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
 
                 self.formatting_init.extend(code_init);
                 Ok(quote! {
-                    #diag.span_suggestions_with_style(
+                    diag.span_suggestions_with_style(
                         #span_field,
                         crate::fluent_generated::#slug,
                         #code_field,
@@ -463,10 +447,9 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
         kind: &Ident,
         fluent_attr_identifier: Path,
     ) -> TokenStream {
-        let diag = &self.parent.diag;
         let fn_name = format_ident!("span_{}", kind);
         quote! {
-            #diag.#fn_name(
+            diag.#fn_name(
                 #field_binding,
                 crate::fluent_generated::#fluent_attr_identifier
             );
@@ -476,9 +459,8 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
     /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug
     /// and `fluent_attr_identifier`.
     fn add_subdiagnostic(&self, kind: &Ident, fluent_attr_identifier: Path) -> TokenStream {
-        let diag = &self.parent.diag;
         quote! {
-            #diag.#kind(crate::fluent_generated::#fluent_attr_identifier);
+            diag.#kind(crate::fluent_generated::#fluent_attr_identifier);
         }
     }
 
diff --git a/compiler/rustc_macros/src/diagnostics/mod.rs b/compiler/rustc_macros/src/diagnostics/mod.rs
index a536eb3b04e..d3a4e7ba7d1 100644
--- a/compiler/rustc_macros/src/diagnostics/mod.rs
+++ b/compiler/rustc_macros/src/diagnostics/mod.rs
@@ -6,7 +6,6 @@ mod utils;
 
 use diagnostic::{DiagnosticDerive, LintDiagnosticDerive};
 use proc_macro2::TokenStream;
-use quote::format_ident;
 use subdiagnostic::SubdiagnosticDeriveBuilder;
 use synstructure::Structure;
 
@@ -57,7 +56,7 @@ use synstructure::Structure;
 /// See rustc dev guide for more examples on using the `#[derive(Diagnostic)]`:
 /// <https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-structs.html>
 pub fn session_diagnostic_derive(s: Structure<'_>) -> TokenStream {
-    DiagnosticDerive::new(format_ident!("diag"), format_ident!("handler"), s).into_tokens()
+    DiagnosticDerive::new(s).into_tokens()
 }
 
 /// Implements `#[derive(LintDiagnostic)]`, which allows for lints to be specified as a struct,
@@ -103,7 +102,7 @@ pub fn session_diagnostic_derive(s: Structure<'_>) -> TokenStream {
 /// See rustc dev guide for more examples on using the `#[derive(LintDiagnostic)]`:
 /// <https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-structs.html#reference>
 pub fn lint_diagnostic_derive(s: Structure<'_>) -> TokenStream {
-    LintDiagnosticDerive::new(format_ident!("diag"), s).into_tokens()
+    LintDiagnosticDerive::new(s).into_tokens()
 }
 
 /// Implements `#[derive(Subdiagnostic)]`, which allows for labels, notes, helps and
diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
index 0f9e68cdc50..663abecb67c 100644
--- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
@@ -94,7 +94,6 @@ impl SubdiagnosticDeriveBuilder {
                         rustc_errors::SubdiagnosticMessage
                     ) -> rustc_errors::SubdiagnosticMessage,
                 {
-                    use rustc_errors::{Applicability, IntoDiagnosticArg};
                     #implementation
                 }
             }
diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs
index 206c15edd78..e13068cb6f9 100644
--- a/compiler/rustc_metadata/src/errors.rs
+++ b/compiler/rustc_metadata/src/errors.rs
@@ -3,7 +3,9 @@ use std::{
     path::{Path, PathBuf},
 };
 
-use rustc_errors::{error_code, ErrorGuaranteed, IntoDiagnostic};
+use rustc_errors::{
+    error_code, DiagCtxt, DiagnosticBuilder, EmissionGuarantee, IntoDiagnostic, Level,
+};
 use rustc_macros::Diagnostic;
 use rustc_session::config;
 use rustc_span::{sym, Span, Symbol};
@@ -495,12 +497,9 @@ pub(crate) struct MultipleCandidates {
     pub candidates: Vec<PathBuf>,
 }
 
-impl IntoDiagnostic<'_> for MultipleCandidates {
-    fn into_diagnostic(
-        self,
-        dcx: &'_ rustc_errors::DiagCtxt,
-    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = dcx.struct_err(fluent::metadata_multiple_candidates);
+impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for MultipleCandidates {
+    fn into_diagnostic(self, dcx: &'_ DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
+        let mut diag = DiagnosticBuilder::new(dcx, level, fluent::metadata_multiple_candidates);
         diag.set_arg("crate_name", self.crate_name);
         diag.set_arg("flavor", self.flavor);
         diag.code(error_code!(E0464));
@@ -593,13 +592,10 @@ pub struct InvalidMetadataFiles {
     pub crate_rejections: Vec<String>,
 }
 
-impl IntoDiagnostic<'_> for InvalidMetadataFiles {
+impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for InvalidMetadataFiles {
     #[track_caller]
-    fn into_diagnostic(
-        self,
-        dcx: &'_ rustc_errors::DiagCtxt,
-    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = dcx.struct_err(fluent::metadata_invalid_meta_files);
+    fn into_diagnostic(self, dcx: &'_ DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
+        let mut diag = DiagnosticBuilder::new(dcx, level, fluent::metadata_invalid_meta_files);
         diag.set_arg("crate_name", self.crate_name);
         diag.set_arg("add_info", self.add_info);
         diag.code(error_code!(E0786));
@@ -623,13 +619,10 @@ pub struct CannotFindCrate {
     pub is_ui_testing: bool,
 }
 
-impl IntoDiagnostic<'_> for CannotFindCrate {
+impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for CannotFindCrate {
     #[track_caller]
-    fn into_diagnostic(
-        self,
-        dcx: &'_ rustc_errors::DiagCtxt,
-    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = dcx.struct_err(fluent::metadata_cannot_find_crate);
+    fn into_diagnostic(self, dcx: &'_ DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
+        let mut diag = DiagnosticBuilder::new(dcx, level, fluent::metadata_cannot_find_crate);
         diag.set_arg("crate_name", self.crate_name);
         diag.set_arg("current_crate", self.current_crate);
         diag.set_arg("add_info", self.add_info);
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 225dd217807..5ae758edfa3 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -5,7 +5,8 @@ use crate::ty::normalize_erasing_regions::NormalizationError;
 use crate::ty::{self, ConstKind, Ty, TyCtxt, TypeVisitableExt};
 use rustc_error_messages::DiagnosticMessage;
 use rustc_errors::{
-    DiagCtxt, DiagnosticArgValue, DiagnosticBuilder, IntoDiagnostic, IntoDiagnosticArg,
+    DiagCtxt, DiagnosticArgValue, DiagnosticBuilder, EmissionGuarantee, IntoDiagnostic,
+    IntoDiagnosticArg, Level,
 };
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
@@ -1272,14 +1273,14 @@ pub enum FnAbiError<'tcx> {
     AdjustForForeignAbi(call::AdjustForForeignAbiError),
 }
 
-impl<'a, 'b> IntoDiagnostic<'a, !> for FnAbiError<'b> {
-    fn into_diagnostic(self, dcx: &'a DiagCtxt) -> DiagnosticBuilder<'a, !> {
+impl<'a, 'b, G: EmissionGuarantee> IntoDiagnostic<'a, G> for FnAbiError<'b> {
+    fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> {
         match self {
-            Self::Layout(e) => e.into_diagnostic().into_diagnostic(dcx),
+            Self::Layout(e) => e.into_diagnostic().into_diagnostic(dcx, level),
             Self::AdjustForForeignAbi(call::AdjustForForeignAbiError::Unsupported {
                 arch,
                 abi,
-            }) => UnsupportedFnAbi { arch, abi: abi.name() }.into_diagnostic(dcx),
+            }) => UnsupportedFnAbi { arch, abi: abi.name() }.into_diagnostic(dcx, level),
         }
     }
 }
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index c66687330dc..22d0a665489 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -2,7 +2,7 @@ use crate::fluent_generated as fluent;
 use rustc_errors::DiagnosticArgValue;
 use rustc_errors::{
     error_code, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder,
-    ErrorGuaranteed, IntoDiagnostic, MultiSpan, SubdiagnosticMessage,
+    ErrorGuaranteed, IntoDiagnostic, Level, MultiSpan, SubdiagnosticMessage,
 };
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
 use rustc_middle::ty::{self, Ty};
@@ -461,13 +461,18 @@ pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> {
 }
 
 impl<'a> IntoDiagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> {
-    fn into_diagnostic(self, dcx: &'a DiagCtxt) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = dcx.struct_span_err_with_code(
-            self.span,
+    fn into_diagnostic(
+        self,
+        dcx: &'a DiagCtxt,
+        level: Level,
+    ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+        let mut diag = DiagnosticBuilder::new(
+            dcx,
+            level,
             fluent::mir_build_non_exhaustive_patterns_type_not_empty,
-            error_code!(E0004),
         );
-
+        diag.set_span(self.span);
+        diag.code(error_code!(E0004));
         let peeled_ty = self.ty.peel_refs();
         diag.set_arg("ty", self.ty);
         diag.set_arg("peeled_ty", peeled_ty);
diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs
index fd4af31501c..17916e16daf 100644
--- a/compiler/rustc_mir_transform/src/errors.rs
+++ b/compiler/rustc_mir_transform/src/errors.rs
@@ -2,7 +2,7 @@ use std::borrow::Cow;
 
 use rustc_errors::{
     Applicability, DecorateLint, DiagCtxt, DiagnosticArgValue, DiagnosticBuilder,
-    DiagnosticMessage, EmissionGuarantee, ErrorGuaranteed, IntoDiagnostic,
+    DiagnosticMessage, EmissionGuarantee, IntoDiagnostic, Level,
 };
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
 use rustc_middle::mir::{AssertKind, UnsafetyViolationDetails};
@@ -62,10 +62,10 @@ pub(crate) struct RequiresUnsafe {
 // so we need to eagerly translate the label here, which isn't supported by the derive API
 // We could also exhaustively list out the primary messages for all unsafe violations,
 // but this would result in a lot of duplication.
-impl<'sess> IntoDiagnostic<'sess> for RequiresUnsafe {
+impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for RequiresUnsafe {
     #[track_caller]
-    fn into_diagnostic(self, dcx: &'sess DiagCtxt) -> DiagnosticBuilder<'sess, ErrorGuaranteed> {
-        let mut diag = dcx.struct_err(fluent::mir_transform_requires_unsafe);
+    fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> {
+        let mut diag = DiagnosticBuilder::new(dcx, level, fluent::mir_transform_requires_unsafe);
         diag.code(rustc_errors::DiagnosticId::Error("E0133".to_string()));
         diag.set_span(self.span);
         diag.span_label(self.span, self.details.label());
diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs
index 247b2245583..592e71251b8 100644
--- a/compiler/rustc_monomorphize/src/errors.rs
+++ b/compiler/rustc_monomorphize/src/errors.rs
@@ -1,8 +1,7 @@
 use std::path::PathBuf;
 
 use crate::fluent_generated as fluent;
-use rustc_errors::ErrorGuaranteed;
-use rustc_errors::IntoDiagnostic;
+use rustc_errors::{DiagCtxt, DiagnosticBuilder, EmissionGuarantee, IntoDiagnostic, Level};
 use rustc_macros::{Diagnostic, LintDiagnostic};
 use rustc_span::{Span, Symbol};
 
@@ -47,13 +46,11 @@ pub struct UnusedGenericParamsHint {
     pub param_names: Vec<String>,
 }
 
-impl IntoDiagnostic<'_> for UnusedGenericParamsHint {
+impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for UnusedGenericParamsHint {
     #[track_caller]
-    fn into_diagnostic(
-        self,
-        dcx: &'_ rustc_errors::DiagCtxt,
-    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = dcx.struct_err(fluent::monomorphize_unused_generic_params);
+    fn into_diagnostic(self, dcx: &'_ DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
+        let mut diag =
+            DiagnosticBuilder::new(dcx, level, fluent::monomorphize_unused_generic_params);
         diag.set_span(self.span);
         for (span, name) in self.param_spans.into_iter().zip(self.param_names) {
             // FIXME: I can figure out how to do a label with a fluent string with a fixed message,
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 008adcc83d0..8ccfcb625a6 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -2,7 +2,10 @@ use std::borrow::Cow;
 
 use rustc_ast::token::Token;
 use rustc_ast::{Path, Visibility};
-use rustc_errors::{AddToDiagnostic, Applicability, ErrorGuaranteed, IntoDiagnostic};
+use rustc_errors::{
+    AddToDiagnostic, Applicability, DiagCtxt, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic,
+    Level, SubdiagnosticMessage,
+};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_span::edition::{Edition, LATEST_STABLE_EDITION};
@@ -1042,23 +1045,30 @@ impl<'a> IntoDiagnostic<'a> for ExpectedIdentifier {
     #[track_caller]
     fn into_diagnostic(
         self,
-        dcx: &'a rustc_errors::DiagCtxt,
-    ) -> rustc_errors::DiagnosticBuilder<'a, ErrorGuaranteed> {
+        dcx: &'a DiagCtxt,
+        level: Level,
+    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
         let token_descr = TokenDescription::from_token(&self.token);
 
-        let mut diag = dcx.struct_err(match token_descr {
-            Some(TokenDescription::ReservedIdentifier) => {
-                fluent::parse_expected_identifier_found_reserved_identifier_str
-            }
-            Some(TokenDescription::Keyword) => fluent::parse_expected_identifier_found_keyword_str,
-            Some(TokenDescription::ReservedKeyword) => {
-                fluent::parse_expected_identifier_found_reserved_keyword_str
-            }
-            Some(TokenDescription::DocComment) => {
-                fluent::parse_expected_identifier_found_doc_comment_str
-            }
-            None => fluent::parse_expected_identifier_found_str,
-        });
+        let mut diag = DiagnosticBuilder::new(
+            dcx,
+            level,
+            match token_descr {
+                Some(TokenDescription::ReservedIdentifier) => {
+                    fluent::parse_expected_identifier_found_reserved_identifier_str
+                }
+                Some(TokenDescription::Keyword) => {
+                    fluent::parse_expected_identifier_found_keyword_str
+                }
+                Some(TokenDescription::ReservedKeyword) => {
+                    fluent::parse_expected_identifier_found_reserved_keyword_str
+                }
+                Some(TokenDescription::DocComment) => {
+                    fluent::parse_expected_identifier_found_doc_comment_str
+                }
+                None => fluent::parse_expected_identifier_found_str,
+            },
+        );
         diag.set_span(self.span);
         diag.set_arg("token", self.token);
 
@@ -1099,21 +1109,28 @@ impl<'a> IntoDiagnostic<'a> for ExpectedSemi {
     #[track_caller]
     fn into_diagnostic(
         self,
-        dcx: &'a rustc_errors::DiagCtxt,
-    ) -> rustc_errors::DiagnosticBuilder<'a, ErrorGuaranteed> {
+        dcx: &'a DiagCtxt,
+        level: Level,
+    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
         let token_descr = TokenDescription::from_token(&self.token);
 
-        let mut diag = dcx.struct_err(match token_descr {
-            Some(TokenDescription::ReservedIdentifier) => {
-                fluent::parse_expected_semi_found_reserved_identifier_str
-            }
-            Some(TokenDescription::Keyword) => fluent::parse_expected_semi_found_keyword_str,
-            Some(TokenDescription::ReservedKeyword) => {
-                fluent::parse_expected_semi_found_reserved_keyword_str
-            }
-            Some(TokenDescription::DocComment) => fluent::parse_expected_semi_found_doc_comment_str,
-            None => fluent::parse_expected_semi_found_str,
-        });
+        let mut diag = DiagnosticBuilder::new(
+            dcx,
+            level,
+            match token_descr {
+                Some(TokenDescription::ReservedIdentifier) => {
+                    fluent::parse_expected_semi_found_reserved_identifier_str
+                }
+                Some(TokenDescription::Keyword) => fluent::parse_expected_semi_found_keyword_str,
+                Some(TokenDescription::ReservedKeyword) => {
+                    fluent::parse_expected_semi_found_reserved_keyword_str
+                }
+                Some(TokenDescription::DocComment) => {
+                    fluent::parse_expected_semi_found_doc_comment_str
+                }
+                None => fluent::parse_expected_semi_found_str,
+            },
+        );
         diag.set_span(self.span);
         diag.set_arg("token", self.token);
 
@@ -1436,10 +1453,7 @@ pub(crate) struct FnTraitMissingParen {
 impl AddToDiagnostic for FnTraitMissingParen {
     fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
     where
-        F: Fn(
-            &mut rustc_errors::Diagnostic,
-            rustc_errors::SubdiagnosticMessage,
-        ) -> rustc_errors::SubdiagnosticMessage,
+        F: Fn(&mut rustc_errors::Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
     {
         diag.span_label(self.span, crate::fluent_generated::parse_fn_trait_missing_paren);
         let applicability = if self.machine_applicable {
diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs
index c158edaac25..59bc0eeb1c5 100644
--- a/compiler/rustc_parse/src/lexer/mod.rs
+++ b/compiler/rustc_parse/src/lexer/mod.rs
@@ -7,7 +7,9 @@ use rustc_ast::ast::{self, AttrStyle};
 use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind};
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::util::unicode::contains_text_flow_control_chars;
-use rustc_errors::{error_code, Applicability, Diagnostic, DiagnosticBuilder, StashKey};
+use rustc_errors::{
+    error_code, Applicability, Diagnostic, DiagnosticBuilder, FatalAbort, StashKey,
+};
 use rustc_lexer::unescape::{self, EscapeError, Mode};
 use rustc_lexer::{Base, DocStyle, RawStrError};
 use rustc_lexer::{Cursor, LiteralKind};
@@ -344,7 +346,7 @@ impl<'a> StringReader<'a> {
         to_pos: BytePos,
         m: &str,
         c: char,
-    ) -> DiagnosticBuilder<'a, !> {
+    ) -> DiagnosticBuilder<'a, FatalAbort> {
         self.sess
             .dcx
             .struct_span_fatal(self.mk_sp(from_pos, to_pos), format!("{}: {}", m, escaped_char(c)))
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index 56e52baf98b..53b29eccc2c 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -5,7 +5,7 @@ use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle
 use rustc_ast as ast;
 use rustc_ast::attr;
 use rustc_ast::token::{self, Delimiter, Nonterminal};
-use rustc_errors::{error_code, Diagnostic, IntoDiagnostic, PResult};
+use rustc_errors::{error_code, Diagnostic, PResult};
 use rustc_span::{sym, BytePos, Span};
 use thin_vec::ThinVec;
 use tracing::debug;
@@ -416,8 +416,9 @@ impl<'a> Parser<'a> {
             Err(err) => err.cancel(),
         }
 
-        Err(InvalidMetaItem { span: self.token.span, token: self.token.clone() }
-            .into_diagnostic(self.dcx()))
+        Err(self
+            .dcx()
+            .create_err(InvalidMetaItem { span: self.token.span, token: self.token.clone() }))
     }
 }
 
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index c077e0a83a1..f19d420bb49 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -35,7 +35,7 @@ use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{
     pluralize, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder,
-    DiagnosticMessage, ErrorGuaranteed, FatalError, IntoDiagnostic, MultiSpan, PResult,
+    DiagnosticMessage, ErrorGuaranteed, FatalError, MultiSpan, PResult,
 };
 use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_span::source_map::Spanned;
@@ -280,11 +280,10 @@ impl<'a> Parser<'a> {
         recover: bool,
     ) -> PResult<'a, (Ident, /* is_raw */ bool)> {
         if let TokenKind::DocComment(..) = self.prev_token.kind {
-            return Err(DocCommentDoesNotDocumentAnything {
+            return Err(self.dcx().create_err(DocCommentDoesNotDocumentAnything {
                 span: self.prev_token.span,
                 missing_comma: None,
-            }
-            .into_diagnostic(self.dcx()));
+            }));
         }
 
         let valid_follow = &[
@@ -347,7 +346,7 @@ impl<'a> Parser<'a> {
             suggest_remove_comma,
             help_cannot_start_number,
         };
-        let mut err = err.into_diagnostic(self.dcx());
+        let mut err = self.dcx().create_err(err);
 
         // if the token we have is a `<`
         // it *might* be a misplaced generic
@@ -1432,7 +1431,7 @@ impl<'a> Parser<'a> {
                                 // Not entirely sure now, but we bubble the error up with the
                                 // suggestion.
                                 self.restore_snapshot(snapshot);
-                                Err(err.into_diagnostic(self.dcx()))
+                                Err(self.dcx().create_err(err))
                             }
                         }
                     } else if token::OpenDelim(Delimiter::Parenthesis) == self.token.kind {
@@ -1447,7 +1446,7 @@ impl<'a> Parser<'a> {
                         }
                         // Consume the fn call arguments.
                         match self.consume_fn_args() {
-                            Err(()) => Err(err.into_diagnostic(self.dcx())),
+                            Err(()) => Err(self.dcx().create_err(err)),
                             Ok(()) => {
                                 self.sess.emit_err(err);
                                 // FIXME: actually check that the two expressions in the binop are
@@ -1473,7 +1472,7 @@ impl<'a> Parser<'a> {
                             mk_err_expr(self, inner_op.span.to(self.prev_token.span))
                         } else {
                             // These cases cause too many knock-down errors, bail out (#61329).
-                            Err(err.into_diagnostic(self.dcx()))
+                            Err(self.dcx().create_err(err))
                         }
                     };
                 }
@@ -2561,7 +2560,7 @@ impl<'a> Parser<'a> {
             Ok(Some(GenericArg::Const(self.parse_const_arg()?)))
         } else {
             let after_kw_const = self.token.span;
-            self.recover_const_arg(after_kw_const, err.into_diagnostic(self.dcx())).map(Some)
+            self.recover_const_arg(after_kw_const, self.dcx().create_err(err)).map(Some)
         }
     }
 
@@ -2915,11 +2914,10 @@ impl<'a> Parser<'a> {
                 let (a_span, b_span) = (a.span(), b.span());
                 let between_span = a_span.shrink_to_hi().to(b_span.shrink_to_lo());
                 if self.span_to_snippet(between_span).as_deref() == Ok(":: ") {
-                    return Err(DoubleColonInBound {
+                    return Err(self.dcx().create_err(DoubleColonInBound {
                         span: path.span.shrink_to_hi(),
                         between: between_span,
-                    }
-                    .into_diagnostic(self.dcx()));
+                    }));
                 }
             }
         }
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index cd3e8b92f2f..a21a1533848 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -26,8 +26,8 @@ use rustc_ast::{ClosureBinder, MetaItemLit, StmtKind};
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::{
-    AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic,
-    PResult, StashKey,
+    AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, PResult,
+    StashKey,
 };
 use rustc_macros::Subdiagnostic;
 use rustc_session::errors::{report_lit_error, ExprParenthesesNeeded};
@@ -1255,21 +1255,21 @@ impl<'a> Parser<'a> {
                             // that of the open delim in `TokenTreesReader::parse_token_tree`, even if they are different.
                             self.span_to_snippet(close_paren).is_ok_and(|snippet| snippet == ")")
                         {
-                            let mut replacement_err = errors::ParenthesesWithStructFields {
-                                span,
-                                r#type: path,
-                                braces_for_struct: errors::BracesForStructLiteral {
-                                    first: open_paren,
-                                    second: close_paren,
-                                },
-                                no_fields_for_fn: errors::NoFieldsForFnCall {
-                                    fields: fields
-                                        .into_iter()
-                                        .map(|field| field.span.until(field.expr.span))
-                                        .collect(),
-                                },
-                            }
-                            .into_diagnostic(self.dcx());
+                            let mut replacement_err =
+                                self.dcx().create_err(errors::ParenthesesWithStructFields {
+                                    span,
+                                    r#type: path,
+                                    braces_for_struct: errors::BracesForStructLiteral {
+                                        first: open_paren,
+                                        second: close_paren,
+                                    },
+                                    no_fields_for_fn: errors::NoFieldsForFnCall {
+                                        fields: fields
+                                            .into_iter()
+                                            .map(|field| field.span.until(field.expr.span))
+                                            .collect(),
+                                    },
+                                });
                             replacement_err.emit();
 
                             let old_err = mem::replace(err, replacement_err);
@@ -1883,8 +1883,7 @@ impl<'a> Parser<'a> {
         self.bump(); // `#`
 
         let Some((ident, false)) = self.token.ident() else {
-            let err =
-                errors::ExpectedBuiltinIdent { span: self.token.span }.into_diagnostic(self.dcx());
+            let err = self.dcx().create_err(errors::ExpectedBuiltinIdent { span: self.token.span });
             return Err(err);
         };
         self.sess.gated_spans.gate(sym::builtin_syntax, ident.span);
@@ -1894,8 +1893,10 @@ impl<'a> Parser<'a> {
         let ret = if let Some(res) = parse(self, lo, ident)? {
             Ok(res)
         } else {
-            let err = errors::UnknownBuiltinConstruct { span: lo.to(ident.span), name: ident.name }
-                .into_diagnostic(self.dcx());
+            let err = self.dcx().create_err(errors::UnknownBuiltinConstruct {
+                span: lo.to(ident.span),
+                name: ident.name,
+            });
             return Err(err);
         };
         self.expect(&TokenKind::CloseDelim(Delimiter::Parenthesis))?;
@@ -1958,8 +1959,9 @@ impl<'a> Parser<'a> {
             && let token::NtExpr(e) | token::NtLiteral(e) = &nt.0
             && matches!(e.kind, ExprKind::Err)
         {
-            let mut err = errors::InvalidInterpolatedExpression { span: self.token.span }
-                .into_diagnostic(self.dcx());
+            let mut err = self
+                .dcx()
+                .create_err(errors::InvalidInterpolatedExpression { span: self.token.span });
             err.downgrade_to_delayed_bug();
             return Err(err);
         }
@@ -2168,10 +2170,10 @@ impl<'a> Parser<'a> {
                         .span_to_snippet(snapshot.token.span)
                         .is_ok_and(|snippet| snippet == "]") =>
                 {
-                    return Err(errors::MissingSemicolonBeforeArray {
+                    return Err(self.dcx().create_err(errors::MissingSemicolonBeforeArray {
                         open_delim: open_delim_span,
                         semicolon: prev_span.shrink_to_hi(),
-                    }.into_diagnostic(self.dcx()));
+                    }));
                 }
                 Ok(_) => (),
                 Err(err) => err.cancel(),
@@ -2318,8 +2320,9 @@ impl<'a> Parser<'a> {
             // Check for `move async` and recover
             if self.check_keyword(kw::Async) {
                 let move_async_span = self.token.span.with_lo(self.prev_token.span.data().lo);
-                Err(errors::AsyncMoveOrderIncorrect { span: move_async_span }
-                    .into_diagnostic(self.dcx()))
+                Err(self
+                    .dcx()
+                    .create_err(errors::AsyncMoveOrderIncorrect { span: move_async_span }))
             } else {
                 Ok(CaptureBy::Value { move_kw: move_kw_span })
             }
@@ -2509,7 +2512,7 @@ impl<'a> Parser<'a> {
             };
             if self.prev_token.kind == token::BinOp(token::Or) {
                 // This was part of a closure, the that part of the parser recover.
-                return Err(err.into_diagnostic(self.dcx()));
+                return Err(self.dcx().create_err(err));
             } else {
                 Some(self.sess.emit_err(err))
             }
@@ -3193,7 +3196,7 @@ impl<'a> Parser<'a> {
     fn parse_try_block(&mut self, span_lo: Span) -> PResult<'a, P<Expr>> {
         let (attrs, body) = self.parse_inner_attrs_and_block()?;
         if self.eat_keyword(kw::Catch) {
-            Err(errors::CatchAfterTry { span: self.prev_token.span }.into_diagnostic(self.dcx()))
+            Err(self.dcx().create_err(errors::CatchAfterTry { span: self.prev_token.span }))
         } else {
             let span = span_lo.to(body.span);
             self.sess.gated_spans.gate(sym::try_blocks, span);
@@ -3530,12 +3533,11 @@ impl<'a> Parser<'a> {
                         || t == &token::CloseDelim(Delimiter::Parenthesis)
                 });
             if is_wrong {
-                return Err(errors::ExpectedStructField {
+                return Err(this.dcx().create_err(errors::ExpectedStructField {
                     span: this.look_ahead(1, |t| t.span),
                     ident_span: this.token.span,
                     token: this.look_ahead(1, |t| t.clone()),
-                }
-                .into_diagnostic(&self.sess.dcx));
+                }));
             }
             let (ident, expr) = if is_shorthand {
                 // Mimic `x: x` for the `x` field shorthand.
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 09ee042ef6b..051f19f5a9d 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -10,10 +10,7 @@ use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
 use rustc_ast::util::case::Case;
 use rustc_ast::{self as ast};
 use rustc_ast_pretty::pprust;
-use rustc_errors::{
-    struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, PResult,
-    StashKey,
-};
+use rustc_errors::{struct_span_err, Applicability, PResult, StashKey};
 use rustc_span::edit_distance::edit_distance;
 use rustc_span::edition::Edition;
 use rustc_span::source_map;
@@ -438,7 +435,7 @@ impl<'a> Parser<'a> {
             None
         };
 
-        if let Some(err) = err { Err(err.into_diagnostic(self.dcx())) } else { Ok(()) }
+        if let Some(err) = err { Err(self.dcx().create_err(err)) } else { Ok(()) }
     }
 
     fn parse_item_builtin(&mut self) -> PResult<'a, Option<ItemInfo>> {
@@ -1373,8 +1370,7 @@ impl<'a> Parser<'a> {
         };
 
         let span = self.prev_token.span.shrink_to_hi();
-        let err: DiagnosticBuilder<'_, ErrorGuaranteed> =
-            errors::MissingConstType { span, colon, kind }.into_diagnostic(self.dcx());
+        let err = self.dcx().create_err(errors::MissingConstType { span, colon, kind });
         err.stash(span, StashKey::ItemNoType);
 
         // The user intended that the type be inferred,
@@ -1391,7 +1387,7 @@ impl<'a> Parser<'a> {
                 self.bump();
                 self.sess.emit_err(err);
             } else {
-                return Err(err.into_diagnostic(self.dcx()));
+                return Err(self.dcx().create_err(err));
             }
         }
 
@@ -1591,7 +1587,7 @@ impl<'a> Parser<'a> {
         } else {
             let err =
                 errors::UnexpectedTokenAfterStructName::new(self.token.span, self.token.clone());
-            return Err(err.into_diagnostic(self.dcx()));
+            return Err(self.dcx().create_err(err));
         };
 
         Ok((class_name, ItemKind::Struct(vdata, generics)))
@@ -1787,7 +1783,7 @@ impl<'a> Parser<'a> {
                         let sp = previous_span.shrink_to_hi();
                         err.missing_comma = Some(sp);
                     }
-                    return Err(err.into_diagnostic(self.dcx()));
+                    return Err(self.dcx().create_err(err));
                 }
             }
             _ => {
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index b91432f10c8..0ab2adb404a 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -32,9 +32,7 @@ use rustc_ast::{HasAttrs, HasTokens, Unsafe, Visibility, VisibilityKind};
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::PResult;
-use rustc_errors::{
-    Applicability, DiagnosticBuilder, ErrorGuaranteed, FatalError, IntoDiagnostic, MultiSpan,
-};
+use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, FatalError, MultiSpan};
 use rustc_session::parse::ParseSess;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{Span, DUMMY_SP};
@@ -1500,14 +1498,13 @@ pub(crate) fn make_unclosed_delims_error(
     if let Some(sp) = unmatched.unclosed_span {
         spans.push(sp);
     };
-    let err = MismatchedClosingDelimiter {
+    let err = sess.dcx.create_err(MismatchedClosingDelimiter {
         spans,
         delimiter: pprust::token_kind_to_string(&token::CloseDelim(found_delim)).to_string(),
         unmatched: unmatched.found_span,
         opening_candidate: unmatched.candidate_span,
         unclosed: unmatched.unclosed_span,
-    }
-    .into_diagnostic(&sess.dcx);
+    });
     Some(err)
 }
 
diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs
index 301a88cd077..071d6b72f3b 100644
--- a/compiler/rustc_parse/src/parser/nonterminal.rs
+++ b/compiler/rustc_parse/src/parser/nonterminal.rs
@@ -2,7 +2,6 @@ use rustc_ast::ptr::P;
 use rustc_ast::token::{self, Delimiter, Nonterminal::*, NonterminalKind, Token};
 use rustc_ast::HasTokens;
 use rustc_ast_pretty::pprust;
-use rustc_errors::IntoDiagnostic;
 use rustc_errors::PResult;
 use rustc_span::symbol::{kw, Ident};
 
@@ -114,9 +113,9 @@ impl<'a> Parser<'a> {
             NonterminalKind::Item => match self.parse_item(ForceCollect::Yes)? {
                 Some(item) => NtItem(item),
                 None => {
-                    return Err(
-                        UnexpectedNonterminal::Item(self.token.span).into_diagnostic(self.dcx())
-                    );
+                    return Err(self
+                        .dcx()
+                        .create_err(UnexpectedNonterminal::Item(self.token.span)));
                 }
             },
             NonterminalKind::Block => {
@@ -127,8 +126,9 @@ impl<'a> Parser<'a> {
             NonterminalKind::Stmt => match self.parse_stmt(ForceCollect::Yes)? {
                 Some(s) => NtStmt(P(s)),
                 None => {
-                    return Err(UnexpectedNonterminal::Statement(self.token.span)
-                        .into_diagnostic(self.dcx()));
+                    return Err(self
+                        .dcx()
+                        .create_err(UnexpectedNonterminal::Statement(self.token.span)));
                 }
             },
             NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr => {
@@ -160,11 +160,10 @@ impl<'a> Parser<'a> {
                 NtIdent(ident, is_raw)
             }
             NonterminalKind::Ident => {
-                return Err(UnexpectedNonterminal::Ident {
+                return Err(self.dcx().create_err(UnexpectedNonterminal::Ident {
                     span: self.token.span,
                     token: self.token.clone(),
-                }
-                .into_diagnostic(self.dcx()));
+                }));
             }
             NonterminalKind::Path => {
                 NtPath(P(self.collect_tokens_no_attrs(|this| this.parse_path(PathStyle::Type))?))
@@ -178,11 +177,10 @@ impl<'a> Parser<'a> {
                 if self.check_lifetime() {
                     NtLifetime(self.expect_lifetime().ident)
                 } else {
-                    return Err(UnexpectedNonterminal::Lifetime {
+                    return Err(self.dcx().create_err(UnexpectedNonterminal::Lifetime {
                         span: self.token.span,
                         token: self.token.clone(),
-                    }
-                    .into_diagnostic(self.dcx()));
+                    }));
                 }
             }
         };
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index 80233eddb9b..d32582fd43d 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -18,7 +18,7 @@ use rustc_ast::{
     PatField, PatKind, Path, QSelf, RangeEnd, RangeSyntax,
 };
 use rustc_ast_pretty::pprust;
-use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, PResult};
+use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult};
 use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_span::source_map::{respan, Spanned};
 use rustc_span::symbol::{kw, sym, Ident};
@@ -872,8 +872,9 @@ impl<'a> Parser<'a> {
         // binding mode then we do not end up here, because the lookahead
         // will direct us over to `parse_enum_variant()`.
         if self.token == token::OpenDelim(Delimiter::Parenthesis) {
-            return Err(EnumPatternInsteadOfIdentifier { span: self.prev_token.span }
-                .into_diagnostic(self.dcx()));
+            return Err(self
+                .dcx()
+                .create_err(EnumPatternInsteadOfIdentifier { span: self.prev_token.span }));
         }
 
         Ok(PatKind::Ident(binding_annotation, ident, sub))
@@ -986,8 +987,8 @@ impl<'a> Parser<'a> {
 
             // check that a comma comes after every field
             if !ate_comma {
-                let mut err = ExpectedCommaAfterPatternField { span: self.token.span }
-                    .into_diagnostic(self.dcx());
+                let mut err =
+                    self.dcx().create_err(ExpectedCommaAfterPatternField { span: self.token.span });
                 if let Some(mut delayed) = delayed_err {
                     delayed.emit();
                 }
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index 3b92a911983..8970a99f1b7 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -9,7 +9,7 @@ use rustc_ast::{
     AssocConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, ParenthesizedArgs,
     Path, PathSegment, QSelf,
 };
-use rustc_errors::{Applicability, IntoDiagnostic, PResult};
+use rustc_errors::{Applicability, PResult};
 use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::{BytePos, Span};
 use std::mem;
@@ -318,15 +318,14 @@ impl<'a> Parser<'a> {
                             })
                         {
                             err.cancel();
-                            err = PathSingleColon {
+                            err = self.dcx().create_err(PathSingleColon {
                                 span: self.token.span,
                                 type_ascription: self
                                     .sess
                                     .unstable_features
                                     .is_nightly_build()
                                     .then_some(()),
-                            }
-                            .into_diagnostic(self.dcx());
+                            });
                         }
                         // Attempt to find places where a missing `>` might belong.
                         else if let Some(arg) = args
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 8f8da211d31..856256a0641 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -6,8 +6,8 @@ use std::{
 use crate::fluent_generated as fluent;
 use rustc_ast::Label;
 use rustc_errors::{
-    error_code, AddToDiagnostic, Applicability, Diagnostic, DiagnosticSymbolList, ErrorGuaranteed,
-    IntoDiagnostic, MultiSpan,
+    error_code, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder,
+    DiagnosticSymbolList, EmissionGuarantee, IntoDiagnostic, Level, MultiSpan,
 };
 use rustc_hir::{self as hir, ExprKind, Target};
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
@@ -863,13 +863,11 @@ pub struct ItemFollowingInnerAttr {
     pub kind: &'static str,
 }
 
-impl IntoDiagnostic<'_> for InvalidAttrAtCrateLevel {
+impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for InvalidAttrAtCrateLevel {
     #[track_caller]
-    fn into_diagnostic(
-        self,
-        dcx: &'_ rustc_errors::DiagCtxt,
-    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = dcx.struct_err(fluent::passes_invalid_attr_at_crate_level);
+    fn into_diagnostic(self, dcx: &'_ DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
+        let mut diag =
+            DiagnosticBuilder::new(dcx, level, fluent::passes_invalid_attr_at_crate_level);
         diag.set_span(self.span);
         diag.set_arg("name", self.name);
         // Only emit an error with a suggestion if we can create a string out
@@ -879,7 +877,7 @@ impl IntoDiagnostic<'_> for InvalidAttrAtCrateLevel {
                 span,
                 fluent::passes_suggestion,
                 String::new(),
-                rustc_errors::Applicability::MachineApplicable,
+                Applicability::MachineApplicable,
             );
         }
         if let Some(item) = self.item {
@@ -1016,17 +1014,12 @@ pub struct BreakNonLoop<'a> {
     pub break_expr_span: Span,
 }
 
-impl<'a> IntoDiagnostic<'_> for BreakNonLoop<'a> {
+impl<'a, G: EmissionGuarantee> IntoDiagnostic<'_, G> for BreakNonLoop<'a> {
     #[track_caller]
-    fn into_diagnostic(
-        self,
-        dcx: &rustc_errors::DiagCtxt,
-    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = dcx.struct_span_err_with_code(
-            self.span,
-            fluent::passes_break_non_loop,
-            error_code!(E0571),
-        );
+    fn into_diagnostic(self, dcx: &DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
+        let mut diag = DiagnosticBuilder::new(dcx, level, fluent::passes_break_non_loop);
+        diag.set_span(self.span);
+        diag.code(error_code!(E0571));
         diag.set_arg("kind", self.kind);
         diag.span_label(self.span, fluent::passes_label);
         if let Some(head) = self.head {
@@ -1165,17 +1158,12 @@ pub struct NakedFunctionsAsmBlock {
     pub non_asms: Vec<Span>,
 }
 
-impl IntoDiagnostic<'_> for NakedFunctionsAsmBlock {
+impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for NakedFunctionsAsmBlock {
     #[track_caller]
-    fn into_diagnostic(
-        self,
-        dcx: &rustc_errors::DiagCtxt,
-    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = dcx.struct_span_err_with_code(
-            self.span,
-            fluent::passes_naked_functions_asm_block,
-            error_code!(E0787),
-        );
+    fn into_diagnostic(self, dcx: &DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
+        let mut diag = DiagnosticBuilder::new(dcx, level, fluent::passes_naked_functions_asm_block);
+        diag.set_span(self.span);
+        diag.code(error_code!(E0787));
         for span in self.multiple_asms.iter() {
             diag.span_label(*span, fluent::passes_label_multiple_asm);
         }
@@ -1281,17 +1269,12 @@ pub struct NoMainErr {
     pub add_teach_note: bool,
 }
 
-impl<'a> IntoDiagnostic<'a> for NoMainErr {
+impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for NoMainErr {
     #[track_caller]
-    fn into_diagnostic(
-        self,
-        dcx: &'a rustc_errors::DiagCtxt,
-    ) -> rustc_errors::DiagnosticBuilder<'a, ErrorGuaranteed> {
-        let mut diag = dcx.struct_span_err_with_code(
-            DUMMY_SP,
-            fluent::passes_no_main_function,
-            error_code!(E0601),
-        );
+    fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> {
+        let mut diag = DiagnosticBuilder::new(dcx, level, fluent::passes_no_main_function);
+        diag.set_span(DUMMY_SP);
+        diag.code(error_code!(E0601));
         diag.set_arg("crate_name", self.crate_name);
         diag.set_arg("filename", self.filename);
         diag.set_arg("has_filename", self.has_filename);
@@ -1344,20 +1327,19 @@ pub struct DuplicateLangItem {
     pub(crate) duplicate: Duplicate,
 }
 
-impl IntoDiagnostic<'_> for DuplicateLangItem {
+impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for DuplicateLangItem {
     #[track_caller]
-    fn into_diagnostic(
-        self,
-        dcx: &rustc_errors::DiagCtxt,
-    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = dcx.struct_err_with_code(
+    fn into_diagnostic(self, dcx: &DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
+        let mut diag = DiagnosticBuilder::new(
+            dcx,
+            level,
             match self.duplicate {
                 Duplicate::Plain => fluent::passes_duplicate_lang_item,
                 Duplicate::Crate => fluent::passes_duplicate_lang_item_crate,
                 Duplicate::CrateDepends => fluent::passes_duplicate_lang_item_crate_depends,
             },
-            error_code!(E0152),
         );
+        diag.code(error_code!(E0152));
         diag.set_arg("lang_item_name", self.lang_item_name);
         diag.set_arg("crate_name", self.crate_name);
         diag.set_arg("dependency_of", self.dependency_of);
diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs
index 2a34ffb75f2..0561fd147a9 100644
--- a/compiler/rustc_query_system/src/query/job.rs
+++ b/compiler/rustc_query_system/src/query/job.rs
@@ -4,9 +4,7 @@ use crate::query::plumbing::CycleError;
 use crate::query::DepKind;
 use crate::query::{QueryContext, QueryStackFrame};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{
-    DiagCtxt, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, Level,
-};
+use rustc_errors::{DiagCtxt, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, Level};
 use rustc_hir::def::DefKind;
 use rustc_session::Session;
 use rustc_span::Span;
@@ -604,7 +602,7 @@ pub(crate) fn report_cycle<'a>(
         note_span: (),
     };
 
-    cycle_diag.into_diagnostic(sess.dcx())
+    sess.dcx().create_err(cycle_diag)
 }
 
 pub fn print_query_stack<Qcx: QueryContext>(
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index c3360815ac9..8ab31f6f382 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -3,7 +3,10 @@ use std::num::NonZeroU32;
 use crate::parse::ParseSess;
 use rustc_ast::token;
 use rustc_ast::util::literal::LitError;
-use rustc_errors::{error_code, DiagnosticMessage, ErrorGuaranteed, IntoDiagnostic, MultiSpan};
+use rustc_errors::{
+    error_code, DiagCtxt, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed, IntoDiagnostic,
+    Level, MultiSpan,
+};
 use rustc_macros::Diagnostic;
 use rustc_span::{BytePos, Span, Symbol};
 use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTriple};
@@ -17,9 +20,10 @@ impl<'a> IntoDiagnostic<'a> for FeatureGateError {
     #[track_caller]
     fn into_diagnostic(
         self,
-        dcx: &'a rustc_errors::DiagCtxt,
-    ) -> rustc_errors::DiagnosticBuilder<'a, ErrorGuaranteed> {
-        let mut diag = dcx.struct_err(self.explain);
+        dcx: &'a DiagCtxt,
+        level: Level,
+    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+        let mut diag = DiagnosticBuilder::new(dcx, level, self.explain);
         diag.set_span(self.span);
         diag.code(error_code!(E0658));
         diag
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index 2cb47e3a932..b33cc83f9cf 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -14,7 +14,7 @@ use rustc_data_structures::sync::{AppendOnlyVec, Lock, Lrc};
 use rustc_errors::{emitter::SilentEmitter, DiagCtxt};
 use rustc_errors::{
     fallback_fluent_bundle, Diagnostic, DiagnosticBuilder, DiagnosticId, DiagnosticMessage,
-    ErrorGuaranteed, IntoDiagnostic, MultiSpan, Noted, StashKey,
+    ErrorGuaranteed, FatalAbort, IntoDiagnostic, Level, MultiSpan, StashKey,
 };
 use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures};
 use rustc_span::edition::Edition;
@@ -322,7 +322,7 @@ impl ParseSess {
         &'a self,
         err: impl IntoDiagnostic<'a>,
     ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
-        err.into_diagnostic(&self.dcx)
+        err.into_diagnostic(&self.dcx, Level::Error { lint: false })
     }
 
     #[track_caller]
@@ -335,7 +335,7 @@ impl ParseSess {
         &'a self,
         warning: impl IntoDiagnostic<'a, ()>,
     ) -> DiagnosticBuilder<'a, ()> {
-        warning.into_diagnostic(&self.dcx)
+        warning.into_diagnostic(&self.dcx, Level::Warning(None))
     }
 
     #[track_caller]
@@ -346,26 +346,26 @@ impl ParseSess {
     #[track_caller]
     pub fn create_note<'a>(
         &'a self,
-        note: impl IntoDiagnostic<'a, Noted>,
-    ) -> DiagnosticBuilder<'a, Noted> {
-        note.into_diagnostic(&self.dcx)
+        note: impl IntoDiagnostic<'a, ()>,
+    ) -> DiagnosticBuilder<'a, ()> {
+        note.into_diagnostic(&self.dcx, Level::Note)
     }
 
     #[track_caller]
-    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()
     }
 
     #[track_caller]
     pub fn create_fatal<'a>(
         &'a self,
-        fatal: impl IntoDiagnostic<'a, !>,
-    ) -> DiagnosticBuilder<'a, !> {
-        fatal.into_diagnostic(&self.dcx)
+        fatal: impl IntoDiagnostic<'a, FatalAbort>,
+    ) -> DiagnosticBuilder<'a, FatalAbort> {
+        fatal.into_diagnostic(&self.dcx, Level::Fatal)
     }
 
     #[track_caller]
-    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()
     }
 
@@ -386,7 +386,10 @@ impl ParseSess {
 
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> {
+    pub fn struct_fatal(
+        &self,
+        msg: impl Into<DiagnosticMessage>,
+    ) -> DiagnosticBuilder<'_, FatalAbort> {
         self.dcx.struct_fatal(msg)
     }
 }
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 7f168572f7d..c9f0f74b0b2 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -23,8 +23,8 @@ use rustc_errors::json::JsonEmitter;
 use rustc_errors::registry::Registry;
 use rustc_errors::{
     error_code, fallback_fluent_bundle, DiagCtxt, DiagnosticBuilder, DiagnosticId,
-    DiagnosticMessage, ErrorGuaranteed, FluentBundle, IntoDiagnostic, LazyFallbackBundle,
-    MultiSpan, Noted, TerminalUrl,
+    DiagnosticMessage, ErrorGuaranteed, FatalAbort, FluentBundle, IntoDiagnostic,
+    LazyFallbackBundle, MultiSpan, TerminalUrl,
 };
 use rustc_macros::HashStable_Generic;
 pub use rustc_span::def_id::StableCrateId;
@@ -428,7 +428,7 @@ impl Session {
         &self,
         sp: S,
         msg: impl Into<DiagnosticMessage>,
-    ) -> DiagnosticBuilder<'_, !> {
+    ) -> DiagnosticBuilder<'_, FatalAbort> {
         self.dcx().struct_span_fatal(sp, msg)
     }
     #[rustc_lint_diagnostics]
@@ -437,11 +437,14 @@ impl Session {
         sp: S,
         msg: impl Into<DiagnosticMessage>,
         code: DiagnosticId,
-    ) -> DiagnosticBuilder<'_, !> {
+    ) -> DiagnosticBuilder<'_, FatalAbort> {
         self.dcx().struct_span_fatal_with_code(sp, msg, code)
     }
     #[rustc_lint_diagnostics]
-    pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> {
+    pub fn struct_fatal(
+        &self,
+        msg: impl Into<DiagnosticMessage>,
+    ) -> DiagnosticBuilder<'_, FatalAbort> {
         self.dcx().struct_fatal(msg)
     }
 
@@ -525,23 +528,23 @@ impl Session {
     #[track_caller]
     pub fn create_note<'a>(
         &'a self,
-        note: impl IntoDiagnostic<'a, Noted>,
-    ) -> DiagnosticBuilder<'a, Noted> {
+        note: impl IntoDiagnostic<'a, ()>,
+    ) -> DiagnosticBuilder<'a, ()> {
         self.parse_sess.create_note(note)
     }
     #[track_caller]
-    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.parse_sess.emit_note(note)
     }
     #[track_caller]
     pub fn create_fatal<'a>(
         &'a self,
-        fatal: impl IntoDiagnostic<'a, !>,
-    ) -> DiagnosticBuilder<'a, !> {
+        fatal: impl IntoDiagnostic<'a, FatalAbort>,
+    ) -> DiagnosticBuilder<'a, FatalAbort> {
         self.parse_sess.create_fatal(fatal)
     }
     #[track_caller]
-    pub fn emit_fatal<'a>(&'a self, fatal: impl IntoDiagnostic<'a, !>) -> ! {
+    pub fn emit_fatal<'a>(&'a self, fatal: impl IntoDiagnostic<'a, FatalAbort>) -> ! {
         self.parse_sess.emit_fatal(fatal)
     }
     #[inline]
@@ -1780,7 +1783,7 @@ impl EarlyDiagCtxt {
     pub fn early_struct_error(
         &self,
         msg: impl Into<DiagnosticMessage>,
-    ) -> DiagnosticBuilder<'_, !> {
+    ) -> DiagnosticBuilder<'_, FatalAbort> {
         self.dcx.struct_fatal(msg)
     }
 
diff --git a/compiler/rustc_symbol_mangling/src/errors.rs b/compiler/rustc_symbol_mangling/src/errors.rs
index ff253b6f467..06a2b3ca9c4 100644
--- a/compiler/rustc_symbol_mangling/src/errors.rs
+++ b/compiler/rustc_symbol_mangling/src/errors.rs
@@ -1,6 +1,6 @@
 //! Errors emitted by symbol_mangling.
 
-use rustc_errors::{ErrorGuaranteed, IntoDiagnostic};
+use rustc_errors::{DiagCtxt, DiagnosticBuilder, EmissionGuarantee, IntoDiagnostic, Level};
 use rustc_span::Span;
 use std::fmt;
 
@@ -13,15 +13,12 @@ pub struct TestOutput {
 // This diagnostic doesn't need translation because (a) it doesn't contain any
 // natural language, and (b) it's only used in tests. So we construct it
 // manually and avoid the fluent machinery.
-impl IntoDiagnostic<'_> for TestOutput {
-    fn into_diagnostic(
-        self,
-        dcx: &'_ rustc_errors::DiagCtxt,
-    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
+impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for TestOutput {
+    fn into_diagnostic(self, dcx: &'_ DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
         let TestOutput { span, kind, content } = self;
 
         #[allow(rustc::untranslatable_diagnostic)]
-        let mut diag = dcx.struct_err(format!("{kind}({content})"));
+        let mut diag = DiagnosticBuilder::new(dcx, level, format!("{kind}({content})"));
         diag.set_span(span);
         diag
     }
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs
index b0ec8b3a4fa..bea6fbd6ac5 100644
--- a/compiler/rustc_trait_selection/src/errors.rs
+++ b/compiler/rustc_trait_selection/src/errors.rs
@@ -1,7 +1,7 @@
 use crate::fluent_generated as fluent;
 use rustc_errors::{
-    AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, ErrorGuaranteed, IntoDiagnostic,
-    SubdiagnosticMessage,
+    AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, EmissionGuarantee,
+    IntoDiagnostic, Level, SubdiagnosticMessage,
 };
 use rustc_macros::Diagnostic;
 use rustc_middle::ty::{self, ClosureKind, PolyTraitRef, Ty};
@@ -57,13 +57,15 @@ pub struct NegativePositiveConflict<'tcx> {
     pub positive_impl_span: Result<Span, Symbol>,
 }
 
-impl IntoDiagnostic<'_> for NegativePositiveConflict<'_> {
+impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for NegativePositiveConflict<'_> {
     #[track_caller]
     fn into_diagnostic(
         self,
         dcx: &DiagCtxt,
-    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = dcx.struct_err(fluent::trait_selection_negative_positive_conflict);
+        level: Level,
+    ) -> rustc_errors::DiagnosticBuilder<'_, G> {
+        let mut diag =
+            DiagnosticBuilder::new(dcx, level, fluent::trait_selection_negative_positive_conflict);
         diag.set_arg("trait_desc", self.trait_desc.print_only_trait_path().to_string());
         diag.set_arg(
             "self_desc",