about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_builtin_macros/messages.ftl19
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs28
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs68
-rw-r--r--compiler/rustc_builtin_macros/src/test.rs16
-rw-r--r--compiler/rustc_builtin_macros/src/test_harness.rs6
-rw-r--r--compiler/rustc_codegen_ssa/messages.ftl13
-rw-r--r--compiler/rustc_codegen_ssa/src/codegen_attrs.rs16
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs34
-rw-r--r--compiler/rustc_codegen_ssa/src/target_features.rs13
-rw-r--r--compiler/rustc_expand/messages.ftl4
-rw-r--r--compiler/rustc_expand/src/errors.rs10
-rw-r--r--compiler/rustc_expand/src/mbe/macro_check.rs6
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl14
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/mod.rs9
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs46
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs10
-rw-r--r--compiler/rustc_hir_typeck/messages.ftl13
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs39
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs30
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs8
-rw-r--r--compiler/rustc_parse/messages.ftl30
-rw-r--r--compiler/rustc_parse/src/errors.rs89
-rw-r--r--compiler/rustc_parse/src/lexer/mod.rs6
-rw-r--r--compiler/rustc_parse/src/parser/generics.rs37
-rw-r--r--compiler/rustc_parse/src/parser/path.rs22
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs42
-rw-r--r--compiler/rustc_resolve/messages.ftl7
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs16
-rw-r--r--compiler/rustc_resolve/src/errors.rs22
-rw-r--r--compiler/rustc_resolve/src/macros.rs4
32 files changed, 491 insertions, 200 deletions
diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl
index fca6012a408..74049406426 100644
--- a/compiler/rustc_builtin_macros/messages.ftl
+++ b/compiler/rustc_builtin_macros/messages.ftl
@@ -149,6 +149,25 @@ builtin_macros_format_pos_mismatch = {$n} positional {$n ->
     [one] argument
     *[more] arguments
     } in format string, but {$desc}
+
 builtin_macros_offset_of_expected_field = expected field
 
 builtin_macros_offset_of_expected_two_args = expected 2 arguments
+
+builtin_macros_test_case_non_item = `#[test_case]` attribute is only allowed on items
+
+builtin_macros_test_bad_fn = {$kind} functions cannot be used for tests
+    .label = `{$kind}` because of this
+
+builtin_macros_asm_explicit_register_name = explicit register arguments cannot have names
+
+builtin_macros_asm_mutually_exclusive = the `{$opt1}` and `{$opt2}` options are mutually exclusive
+
+builtin_macros_asm_pure_combine = the `pure` option must be combined with either `nomem` or `readonly`
+
+builtin_macros_asm_pure_no_output = asm with the `pure` option must have at least one output
+
+builtin_macros_asm_modifier_invalid = asm template modifier must be a single character
+
+builtin_macros_test_runner_invalid = `test_runner` argument must be a path
+builtin_macros_test_runner_nargs = `#![test_runner(..)]` accepts exactly 1 argument
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index 8c1579baacb..bcdd58a0901 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -15,6 +15,8 @@ use rustc_span::{InnerSpan, Span};
 use rustc_target::asm::InlineAsmArch;
 use smallvec::smallvec;
 
+use crate::errors;
+
 pub struct AsmArgs {
     pub templates: Vec<P<ast::Expr>>,
     pub operands: Vec<(ast::InlineAsmOperand, Span)>,
@@ -205,7 +207,7 @@ pub fn parse_asm_args<'a>(
         // of the argument available.
         if explicit_reg {
             if name.is_some() {
-                diag.struct_span_err(span, "explicit register arguments cannot have names").emit();
+                diag.emit_err(errors::AsmExplicitRegisterName { span });
             }
             args.reg_args.insert(slot);
         } else if let Some(name) = name {
@@ -240,25 +242,19 @@ pub fn parse_asm_args<'a>(
         && args.options.contains(ast::InlineAsmOptions::READONLY)
     {
         let spans = args.options_spans.clone();
-        diag.struct_span_err(spans, "the `nomem` and `readonly` options are mutually exclusive")
-            .emit();
+        diag.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "nomem", opt2: "readonly" });
     }
     if args.options.contains(ast::InlineAsmOptions::PURE)
         && args.options.contains(ast::InlineAsmOptions::NORETURN)
     {
         let spans = args.options_spans.clone();
-        diag.struct_span_err(spans, "the `pure` and `noreturn` options are mutually exclusive")
-            .emit();
+        diag.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "pure", opt2: "noreturn" });
     }
     if args.options.contains(ast::InlineAsmOptions::PURE)
         && !args.options.intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY)
     {
         let spans = args.options_spans.clone();
-        diag.struct_span_err(
-            spans,
-            "the `pure` option must be combined with either `nomem` or `readonly`",
-        )
-        .emit();
+        diag.emit_err(errors::AsmPureCombine { spans });
     }
 
     let mut have_real_output = false;
@@ -285,11 +281,7 @@ pub fn parse_asm_args<'a>(
         }
     }
     if args.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output {
-        diag.struct_span_err(
-            args.options_spans.clone(),
-            "asm with the `pure` option must have at least one output",
-        )
-        .emit();
+        diag.emit_err(errors::AsmPureNoOutput { spans: args.options_spans.clone() });
     }
     if args.options.contains(ast::InlineAsmOptions::NORETURN) && !outputs_sp.is_empty() {
         let err = diag
@@ -705,11 +697,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl
                             .ty_span
                             .map(|sp| template_sp.from_inner(InnerSpan::new(sp.start, sp.end)))
                             .unwrap_or(template_sp);
-                        ecx.struct_span_err(
-                            span,
-                            "asm template modifier must be a single character",
-                        )
-                        .emit();
+                        ecx.emit_err(errors::AsmModifierInvalid { span });
                         modifier = None;
                     }
 
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index 630f9b87bc3..b146988a3c2 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -551,3 +551,71 @@ pub(crate) struct FormatPositionalMismatch {
     #[subdiagnostic]
     pub(crate) highlight: SingleLabelManySpans,
 }
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_test_case_non_item)]
+pub(crate) struct TestCaseNonItem {
+    #[primary_span]
+    pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_test_bad_fn)]
+pub(crate) struct TestBadFn {
+    #[primary_span]
+    pub(crate) span: Span,
+    #[label]
+    pub(crate) cause: Span,
+    pub(crate) kind: &'static str,
+}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_asm_explicit_register_name)]
+pub(crate) struct AsmExplicitRegisterName {
+    #[primary_span]
+    pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_asm_mutually_exclusive)]
+pub(crate) struct AsmMutuallyExclusive {
+    #[primary_span]
+    pub(crate) spans: Vec<Span>,
+    pub(crate) opt1: &'static str,
+    pub(crate) opt2: &'static str,
+}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_asm_pure_combine)]
+pub(crate) struct AsmPureCombine {
+    #[primary_span]
+    pub(crate) spans: Vec<Span>,
+}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_asm_pure_no_output)]
+pub(crate) struct AsmPureNoOutput {
+    #[primary_span]
+    pub(crate) spans: Vec<Span>,
+}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_asm_modifier_invalid)]
+pub(crate) struct AsmModifierInvalid {
+    #[primary_span]
+    pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_test_runner_invalid)]
+pub(crate) struct TestRunnerInvalid {
+    #[primary_span]
+    pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_test_runner_nargs)]
+pub(crate) struct TestRunnerNargs {
+    #[primary_span]
+    pub(crate) span: Span,
+}
diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs
index 79d8be2484b..49ee276af4e 100644
--- a/compiler/rustc_builtin_macros/src/test.rs
+++ b/compiler/rustc_builtin_macros/src/test.rs
@@ -1,3 +1,4 @@
+use crate::errors;
 /// The expansion from a test function to the appropriate test struct for libtest
 /// Ideally, this code would be in libtest but for efficiency and error messages it lives here.
 use crate::util::{check_builtin_macro_attribute, warn_on_duplicate_attribute};
@@ -40,12 +41,7 @@ pub fn expand_test_case(
             unreachable!()
         },
         _ => {
-            ecx.struct_span_err(
-                anno_item.span(),
-                "`#[test_case]` attribute is only allowed on items",
-            )
-            .emit();
-
+            ecx.emit_err(errors::TestCaseNonItem { span: anno_item.span() });
             return vec![];
         }
     };
@@ -533,15 +529,11 @@ fn has_test_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool {
     match &i.kind {
         ast::ItemKind::Fn(box ast::Fn { sig, generics, .. }) => {
             if let ast::Unsafe::Yes(span) = sig.header.unsafety {
-                sd.struct_span_err(i.span, "unsafe functions cannot be used for tests")
-                    .span_label(span, "`unsafe` because of this")
-                    .emit();
+                sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "unsafe" });
                 return false;
             }
             if let ast::Async::Yes { span, .. } = sig.header.asyncness {
-                sd.struct_span_err(i.span, "async functions cannot be used for tests")
-                    .span_label(span, "`async` because of this")
-                    .emit();
+                sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "async" });
                 return false;
             }
 
diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs
index 80f497333a6..be4ba66c082 100644
--- a/compiler/rustc_builtin_macros/src/test_harness.rs
+++ b/compiler/rustc_builtin_macros/src/test_harness.rs
@@ -19,6 +19,8 @@ use tracing::debug;
 
 use std::{iter, mem};
 
+use crate::errors;
+
 #[derive(Clone)]
 struct Test {
     span: Span,
@@ -385,11 +387,11 @@ fn get_test_runner(sd: &rustc_errors::Handler, krate: &ast::Crate) -> Option<ast
         [single] => match single.meta_item() {
             Some(meta_item) if meta_item.is_word() => return Some(meta_item.path.clone()),
             _ => {
-                sd.struct_span_err(span, "`test_runner` argument must be a path").emit();
+                sd.emit_err(errors::TestRunnerInvalid { span });
             }
         },
         _ => {
-            sd.struct_span_err(span, "`#![test_runner(..)]` accepts exactly 1 argument").emit();
+            sd.emit_err(errors::TestRunnerNargs { span });
         }
     }
     None
diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl
index 85a96e3e89c..375fdec1007 100644
--- a/compiler/rustc_codegen_ssa/messages.ftl
+++ b/compiler/rustc_codegen_ssa/messages.ftl
@@ -291,3 +291,16 @@ codegen_ssa_invalid_monomorphization_unsupported_cast = invalid monomorphization
 codegen_ssa_invalid_monomorphization_unsupported_operation = invalid monomorphization of `{$name}` intrinsic: unsupported operation on `{$in_ty}` with element `{$in_elem}`
 
 codegen_ssa_invalid_monomorphization_expected_vector_element_type = invalid monomorphization of `{$name}` intrinsic: expected element type `{$expected_element}` of vector type `{$vector_type}` to be a signed or unsigned integer type
+
+codegen_ssa_invalid_no_sanitize = invalid argument for `no_sanitize`
+    .note = expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread`
+
+codegen_ssa_invalid_link_ordinal_nargs = incorrect number of arguments to `#[link_ordinal]`
+    .note = the attribute requires exactly one argument
+
+codegen_ssa_illegal_link_ordinal_format = illegal ordinal format in `link_ordinal`
+    .note = an unsuffixed integer value, e.g., `1`, is expected
+
+codegen_ssa_target_feature_safe_trait = `#[target_feature(..)]` cannot be applied to safe trait method
+    .label = cannot be applied to safe trait method
+    .label_def = not an `unsafe` function
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index 9bfe426c007..5bd42622f2c 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -14,6 +14,7 @@ use rustc_span::symbol::Ident;
 use rustc_span::{sym, Span};
 use rustc_target::spec::{abi, SanitizerSet};
 
+use crate::errors;
 use crate::target_features::from_target_feature;
 use crate::{errors::ExpectedUsedSymbol, target_features::check_target_feature_trait_unsafe};
 
@@ -334,10 +335,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                                 codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS
                             }
                             _ => {
-                                tcx.sess
-                                    .struct_span_err(item.span(), "invalid argument for `no_sanitize`")
-                                    .note("expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread`")
-                                    .emit();
+                                tcx.sess.emit_err(errors::InvalidNoSanitize { span: item.span() });
                             }
                         }
                     }
@@ -608,10 +606,7 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> {
     let sole_meta_list = match meta_item_list {
         Some([item]) => item.lit(),
         Some(_) => {
-            tcx.sess
-                .struct_span_err(attr.span, "incorrect number of arguments to `#[link_ordinal]`")
-                .note("the attribute requires exactly one argument")
-                .emit();
+            tcx.sess.emit_err(errors::InvalidLinkOrdinalNargs { span: attr.span });
             return None;
         }
         _ => None,
@@ -642,10 +637,7 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> {
             None
         }
     } else {
-        tcx.sess
-            .struct_span_err(attr.span, "illegal ordinal format in `link_ordinal`")
-            .note("an unsuffixed integer value, e.g., `1`, is expected")
-            .emit();
+        tcx.sess.emit_err(errors::InvalidLinkOrdinalFormat { span: attr.span });
         None
     }
 }
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index 44931766678..cf4893b8226 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -981,3 +981,37 @@ impl IntoDiagnosticArg for ExpectedPointerMutability {
         }
     }
 }
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_invalid_no_sanitize)]
+#[note]
+pub struct InvalidNoSanitize {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_invalid_link_ordinal_nargs)]
+#[note]
+pub struct InvalidLinkOrdinalNargs {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_illegal_link_ordinal_format)]
+#[note]
+pub struct InvalidLinkOrdinalFormat {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_target_feature_safe_trait)]
+pub struct TargetFeatureSafeTrait {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[label(codegen_ssa_label_def)]
+    pub def: Span,
+}
diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs
index 611dd3d1cd1..a936b62dd4e 100644
--- a/compiler/rustc_codegen_ssa/src/target_features.rs
+++ b/compiler/rustc_codegen_ssa/src/target_features.rs
@@ -1,3 +1,4 @@
+use crate::errors;
 use rustc_ast::ast;
 use rustc_attr::InstructionSetAttr;
 use rustc_data_structures::fx::FxHashMap;
@@ -443,14 +444,10 @@ pub fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_s
     if let DefKind::AssocFn = tcx.def_kind(id) {
         let parent_id = tcx.local_parent(id);
         if let DefKind::Trait | DefKind::Impl { of_trait: true } = tcx.def_kind(parent_id) {
-            tcx.sess
-                .struct_span_err(
-                    attr_span,
-                    "`#[target_feature(..)]` cannot be applied to safe trait method",
-                )
-                .span_label(attr_span, "cannot be applied to safe trait method")
-                .span_label(tcx.def_span(id), "not an `unsafe` function")
-                .emit();
+            tcx.sess.emit_err(errors::TargetFeatureSafeTrait {
+                span: attr_span,
+                def: tcx.def_span(id),
+            });
         }
     }
 }
diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl
index 5d999d0db5d..70d2718b706 100644
--- a/compiler/rustc_expand/messages.ftl
+++ b/compiler/rustc_expand/messages.ftl
@@ -136,3 +136,7 @@ expand_proc_macro_panicked =
 
 expand_proc_macro_derive_tokens =
     proc-macro derive produced unparsable tokens
+
+expand_duplicate_matcher_binding = duplicate matcher binding
+    .label = duplicate binding
+    .label2 = previous binding
diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs
index e5102a952e7..e3a0ae3570e 100644
--- a/compiler/rustc_expand/src/errors.rs
+++ b/compiler/rustc_expand/src/errors.rs
@@ -397,3 +397,13 @@ pub struct ProcMacroDeriveTokens {
     #[primary_span]
     pub span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(expand_duplicate_matcher_binding)]
+pub struct DuplicateMatcherBinding {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[label(expand_label2)]
+    pub prev: Span,
+}
diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs
index 5be134f4e66..75b6396f0be 100644
--- a/compiler/rustc_expand/src/mbe/macro_check.rs
+++ b/compiler/rustc_expand/src/mbe/macro_check.rs
@@ -104,6 +104,7 @@
 //! Kleene operators under which a meta-variable is repeating is the concatenation of the stacks
 //! stored when entering a macro definition starting from the state in which the meta-variable is
 //! bound.
+use crate::errors;
 use crate::mbe::{KleeneToken, TokenTree};
 
 use rustc_ast::token::{Delimiter, Token, TokenKind};
@@ -281,10 +282,7 @@ fn check_binders(
                 // Duplicate binders at the top-level macro definition are errors. The lint is only
                 // for nested macro definitions.
                 sess.span_diagnostic
-                    .struct_span_err(span, "duplicate matcher binding")
-                    .span_label(span, "duplicate binding")
-                    .span_label(prev_info.span, "previous binding")
-                    .emit();
+                    .emit_err(errors::DuplicateMatcherBinding { span, prev: prev_info.span });
                 *valid = false;
             } else {
                 binders.insert(name, BinderInfo { span, ops: ops.into() });
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index f32ae509e33..5d45d09797b 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -262,3 +262,17 @@ hir_analysis_transparent_non_zero_sized_enum = the variant of a transparent {$de
 hir_analysis_transparent_non_zero_sized = transparent {$desc} needs at most one non-zero-sized field, but has {$field_count}
     .label = needs at most one non-zero-sized field, but has {$field_count}
     .labels = this field is non-zero-sized
+
+hir_analysis_too_large_static = extern static is too large for the current architecture
+
+hir_analysis_specialization_trait = implementing `rustc_specialization_trait` traits is unstable
+    .help = add `#![feature(min_specialization)]` to the crate attributes to enable
+
+hir_analysis_closure_implicit_hrtb = implicit types in closure signatures are forbidden when `for<...>` is present
+    .label = `for<...>` is here
+
+hir_analysis_const_specialize = cannot specialize on const impl with non-const impl
+
+hir_analysis_static_specialize = cannot specialize on `'static` lifetime
+
+hir_analysis_missing_tilde_const = missing `~const` qualifier for specialization
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index ad2624a5d2d..dcf92264bc4 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -170,9 +170,7 @@ fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) {
             if matches!(tcx.def_kind(def_id), DefKind::Static(_)
                 if tcx.def_kind(tcx.local_parent(def_id)) == DefKind::ForeignMod) =>
         {
-            tcx.sess
-                .struct_span_err(span, "extern static is too large for the current architecture")
-                .emit();
+            tcx.sess.emit_err(errors::TooLargeStatic { span });
             return;
         }
         // Generic statics are rejected, but we still reach this case.
diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs
index ac393ee15a6..cd2ec2bef20 100644
--- a/compiler/rustc_hir_analysis/src/coherence/mod.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs
@@ -5,6 +5,7 @@
 // done by the orphan and overlap modules. Then we build up various
 // mappings. That mapping code resides here.
 
+use crate::errors;
 use rustc_errors::{error_code, struct_span_err};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::ty::query::Providers;
@@ -67,13 +68,7 @@ fn enforce_trait_manually_implementable(
         tcx.trait_def(trait_def_id).specialization_kind
     {
         if !tcx.features().specialization && !tcx.features().min_specialization {
-            tcx.sess
-                .struct_span_err(
-                    impl_header_span,
-                    "implementing `rustc_specialization_trait` traits is unstable",
-                )
-                .help("add `#![feature(min_specialization)]` to the crate attributes to enable")
-                .emit();
+            tcx.sess.emit_err(errors::SpecializationTrait { span: impl_header_span });
             return;
         }
     }
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index 3cb217335bd..1c496f867a0 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -455,13 +455,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                     .collect::<Vec<_>>();
 
                 if !infer_spans.is_empty() {
-                    self.tcx.sess
-                        .struct_span_err(
-                            infer_spans,
-                            "implicit types in closure signatures are forbidden when `for<...>` is present",
-                        )
-                        .span_label(for_sp, "`for<...>` is here")
-                        .emit();
+                    self.tcx
+                        .sess
+                        .emit_err(errors::ClosureImplicitHrtb { spans: infer_spans, for_sp });
                 }
             }
 
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index cfce2463b18..f82169dee98 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -633,6 +633,7 @@ pub(crate) struct SIMDFFIHighlyExperimental {
 }
 
 #[derive(Diagnostic)]
+
 pub enum ImplNotMarkedDefault {
     #[diag(hir_analysis_impl_not_marked_default, code = "E0520")]
     #[note]
@@ -769,3 +770,48 @@ pub(crate) struct TransparentNonZeroSized<'a> {
     pub field_count: usize,
     pub desc: &'a str,
 }
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_too_large_static)]
+pub(crate) struct TooLargeStatic {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_specialization_trait)]
+#[help]
+pub(crate) struct SpecializationTrait {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_closure_implicit_hrtb)]
+pub(crate) struct ClosureImplicitHrtb {
+    #[primary_span]
+    pub spans: Vec<Span>,
+    #[label]
+    pub for_sp: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_const_specialize)]
+pub(crate) struct ConstSpecialize {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_static_specialize)]
+pub(crate) struct StaticSpecialize {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_missing_tilde_const)]
+pub(crate) struct MissingTildeConst {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
index eb2fc395223..56f456e5557 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
@@ -65,8 +65,8 @@
 //! cause use after frees with purely safe code in the same way as specializing
 //! on traits with methods can.
 
-use crate::constrained_generic_params as cgp;
 use crate::errors::SubstsOnOverriddenImpl;
+use crate::{constrained_generic_params as cgp, errors};
 
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
@@ -137,9 +137,7 @@ fn check_constness(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node,
 
     if let hir::Constness::Const = impl2_constness {
         if let hir::Constness::NotConst = impl1_constness {
-            tcx.sess
-                .struct_span_err(span, "cannot specialize on const impl with non-const impl")
-                .emit();
+            tcx.sess.emit_err(errors::ConstSpecialize { span });
         }
     }
 }
@@ -293,7 +291,7 @@ fn check_static_lifetimes<'tcx>(
     span: Span,
 ) {
     if tcx.any_free_region_meets(parent_substs, |r| r.is_static()) {
-        tcx.sess.struct_span_err(span, "cannot specialize on `'static` lifetime").emit();
+        tcx.sess.emit_err(errors::StaticSpecialize { span });
     }
 }
 
@@ -438,7 +436,7 @@ fn trait_predicates_eq<'tcx>(
     // the one on the base.
     match (trait_pred2.constness, trait_pred1.constness) {
         (ty::BoundConstness::ConstIfConst, ty::BoundConstness::NotConst) => {
-            tcx.sess.struct_span_err(span, "missing `~const` qualifier for specialization").emit();
+            tcx.sess.emit_err(errors::MissingTildeConst { span });
         }
         _ => {}
     }
diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl
index 6d40df7d0cc..603ea1440e9 100644
--- a/compiler/rustc_hir_typeck/messages.ftl
+++ b/compiler/rustc_hir_typeck/messages.ftl
@@ -62,3 +62,16 @@ hir_typeck_fru_suggestion =
         [NONE]{""}
         *[other] {" "}from `{$expr}`
     }, separate the last named field with a comma
+
+hir_typeck_const_select_must_be_const = this argument must be a `const fn`
+    .help = consult the documentation on `const_eval_select` for more information
+
+hir_typeck_const_select_must_be_fn = this argument must be a function item
+    .note = expected a function item, found {$ty}
+    .help = consult the documentation on `const_eval_select` for more information
+
+hir_typeck_union_pat_multiple_fields = union patterns should have exactly one field
+hir_typeck_union_pat_dotdot = `..` cannot be used in union patterns
+
+hir_typeck_arg_mismatch_indeterminate = argument type mismatch was detected, but rustc had trouble determining where
+    .note = we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index 5be78416e61..48c40d21603 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -228,3 +228,42 @@ impl HelpUseLatestEdition {
         }
     }
 }
+
+#[derive(Diagnostic)]
+#[diag(hir_typeck_const_select_must_be_const)]
+#[help]
+pub struct ConstSelectMustBeConst {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_typeck_const_select_must_be_fn)]
+#[note]
+#[help]
+pub struct ConstSelectMustBeFn<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub ty: Ty<'a>,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_typeck_union_pat_multiple_fields)]
+pub struct UnionPatMultipleFields {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_typeck_union_pat_dotdot)]
+pub struct UnionPatDotDot {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_typeck_arg_mismatch_indeterminate)]
+pub struct ArgMismatchIndeterminate {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 955463c1434..f42c825d9e8 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -2,8 +2,8 @@ use crate::coercion::CoerceMany;
 use crate::fn_ctxt::arg_matrix::{ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx};
 use crate::gather_locals::Declaration;
 use crate::method::MethodCallee;
-use crate::Expectation::*;
 use crate::TupleArgumentsFlag::*;
+use crate::{errors, Expectation::*};
 use crate::{
     struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, Needs, RawTy,
     TupleArgumentsFlag,
@@ -283,19 +283,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     if idx == 1 && !self.tcx.is_const_fn_raw(*def_id) {
                         self.tcx
                             .sess
-                            .struct_span_err(provided_arg.span, "this argument must be a `const fn`")
-                            .help("consult the documentation on `const_eval_select` for more information")
-                            .emit();
+                            .emit_err(errors::ConstSelectMustBeConst { span: provided_arg.span });
                     }
                 } else {
-                    self.tcx
-                        .sess
-                        .struct_span_err(provided_arg.span, "this argument must be a function item")
-                        .note(format!("expected a function item, found {checked_ty}"))
-                        .help(
-                            "consult the documentation on `const_eval_select` for more information",
-                        )
-                        .emit();
+                    self.tcx.sess.emit_err(errors::ConstSelectMustBeFn {
+                        span: provided_arg.span,
+                        ty: checked_ty,
+                    });
                 }
             }
 
@@ -744,17 +738,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             if cfg!(debug_assertions) {
                 span_bug!(error_span, "expected errors from argument matrix");
             } else {
-                tcx.sess
-                    .struct_span_err(
-                        error_span,
-                        "argument type mismatch was detected, \
-                        but rustc had trouble determining where",
-                    )
-                    .note(
-                        "we would appreciate a bug report: \
-                        https://github.com/rust-lang/rust/issues/new",
-                    )
-                    .emit();
+                tcx.sess.emit_err(errors::ArgMismatchIndeterminate { span: error_span });
             }
             return;
         }
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 7160d1c67b2..d69a16d45ae 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -1,4 +1,4 @@
-use crate::{FnCtxt, RawTy};
+use crate::{errors, FnCtxt, RawTy};
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{
@@ -1410,12 +1410,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // Report an error if an incorrect number of fields was specified.
         if adt.is_union() {
             if fields.len() != 1 {
-                tcx.sess
-                    .struct_span_err(pat.span, "union patterns should have exactly one field")
-                    .emit();
+                tcx.sess.emit_err(errors::UnionPatMultipleFields { span: pat.span });
             }
             if has_rest_pat {
-                tcx.sess.struct_span_err(pat.span, "`..` cannot be used in union patterns").emit();
+                tcx.sess.emit_err(errors::UnionPatDotDot { span: pat.span });
             }
         } else if !unmentioned_fields.is_empty() {
             let accessible_unmentioned_fields: Vec<_> = unmentioned_fields
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index f11d0ed0f01..d45fa90a11b 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -742,3 +742,33 @@ parse_bad_return_type_notation_output =
 parse_bad_return_type_notation_dotdot =
     return type notation uses `()` instead of `(..)` for elided arguments
     .suggestion = remove the `..`
+
+parse_bad_assoc_type_bounds = bounds on associated types do not belong here
+    .label = belongs in `where` clause
+
+parse_attr_after_generic = trailing attribute after generic parameter
+    .label = attributes must go before parameters
+
+parse_attr_without_generics = attribute without generic parameters
+    .label = attributes are only permitted when preceding parameters
+
+parse_where_generics = generic parameters on `where` clauses are reserved for future use
+    .label = currently unsupported
+
+parse_generics_in_path = unexpected generic arguments in path
+
+parse_assoc_lifetime = associated lifetimes are not supported
+    .label = the lifetime is given here
+    .help = if you meant to specify a trait object, write `dyn Trait + 'lifetime`
+
+parse_tilde_const_lifetime = `~const` may only modify trait bounds, not lifetime bounds
+
+parse_maybe_lifetime = `?` may only modify trait bounds, not lifetime bounds
+
+parse_parenthesized_lifetime = parenthesized lifetime bounds are not supported
+    .suggestion = remove the parentheses
+
+parse_const_bounds_missing_tilde = const bounds must start with `~`
+    .suggestion = add `~`
+
+parse_underscore_literal_suffix = underscore literal suffix is not allowed
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 069217165fa..b0e1189851a 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -2332,3 +2332,92 @@ pub(crate) struct BadReturnTypeNotationDotDot {
     #[suggestion(code = "", applicability = "maybe-incorrect")]
     pub span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(parse_bad_assoc_type_bounds)]
+pub(crate) struct BadAssocTypeBounds {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_attr_after_generic)]
+pub(crate) struct AttrAfterGeneric {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_attr_without_generics)]
+pub(crate) struct AttrWithoutGenerics {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_where_generics)]
+pub(crate) struct WhereOnGenerics {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_generics_in_path)]
+pub(crate) struct GenericsInPath {
+    #[primary_span]
+    pub span: Vec<Span>,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_assoc_lifetime)]
+#[help]
+pub(crate) struct AssocLifetime {
+    #[primary_span]
+    pub span: Span,
+    #[label]
+    pub lifetime: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_tilde_const_lifetime)]
+pub(crate) struct TildeConstLifetime {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_maybe_lifetime)]
+pub(crate) struct MaybeLifetime {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_parenthesized_lifetime)]
+pub(crate) struct ParenthesizedLifetime {
+    #[primary_span]
+    pub span: Span,
+    #[suggestion(style = "short", applicability = "machine-applicable", code = "{snippet}")]
+    pub sugg: Option<Span>,
+    pub snippet: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_const_bounds_missing_tilde)]
+pub(crate) struct ConstMissingTilde {
+    #[primary_span]
+    pub span: Span,
+    #[suggestion(code = "~", applicability = "machine-applicable")]
+    pub start: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_underscore_literal_suffix)]
+pub(crate) struct UnderscoreLiteralSuffix {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs
index ad9b20f9c76..a4a75fcb969 100644
--- a/compiler/rustc_parse/src/lexer/mod.rs
+++ b/compiler/rustc_parse/src/lexer/mod.rs
@@ -209,11 +209,7 @@ impl<'a> StringReader<'a> {
                         if string == "_" {
                             self.sess
                                 .span_diagnostic
-                                .struct_span_err(
-                                    self.mk_sp(suffix_start, self.pos),
-                                    "underscore literal suffix is not allowed",
-                                )
-                                .emit();
+                                .emit_err(errors::UnderscoreLiteralSuffix { span: self.mk_sp(suffix_start, self.pos) });
                             None
                         } else {
                             Some(Symbol::intern(string))
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index f8ef1307c98..61a7ae93bfa 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -1,5 +1,5 @@
 use crate::errors::{
-    MultipleWhereClauses, UnexpectedDefaultValueForLifetimeInGenericParameters,
+    self, MultipleWhereClauses, UnexpectedDefaultValueForLifetimeInGenericParameters,
     UnexpectedSelfInGenericParameters, WhereClauseBeforeTupleStructBody,
     WhereClauseBeforeTupleStructBodySugg,
 };
@@ -181,12 +181,9 @@ impl<'a> Parser<'a> {
                         let snapshot = this.create_snapshot_for_diagnostic();
                         match this.parse_ty_where_predicate() {
                             Ok(where_predicate) => {
-                                this.struct_span_err(
-                                    where_predicate.span(),
-                                    "bounds on associated types do not belong here",
-                                )
-                                .span_label(where_predicate.span(), "belongs in `where` clause")
-                                .emit();
+                                this.sess.emit_err(errors::BadAssocTypeBounds {
+                                    span: where_predicate.span(),
+                                });
                                 // FIXME - try to continue parsing other generics?
                                 return Ok((None, TrailingToken::None));
                             }
@@ -201,22 +198,11 @@ impl<'a> Parser<'a> {
                         // Check for trailing attributes and stop parsing.
                         if !attrs.is_empty() {
                             if !params.is_empty() {
-                                this.struct_span_err(
-                                    attrs[0].span,
-                                    "trailing attribute after generic parameter",
-                                )
-                                .span_label(attrs[0].span, "attributes must go before parameters")
-                                .emit();
+                                this.sess
+                                    .emit_err(errors::AttrAfterGeneric { span: attrs[0].span });
                             } else {
-                                this.struct_span_err(
-                                    attrs[0].span,
-                                    "attribute without generic parameters",
-                                )
-                                .span_label(
-                                    attrs[0].span,
-                                    "attributes are only permitted when preceding parameters",
-                                )
-                                .emit();
+                                this.sess
+                                    .emit_err(errors::AttrWithoutGenerics { span: attrs[0].span });
                             }
                         }
                         return Ok((None, TrailingToken::None));
@@ -304,12 +290,7 @@ impl<'a> Parser<'a> {
         // change we parse those generics now, but report an error.
         if self.choose_generics_over_qpath(0) {
             let generics = self.parse_generics()?;
-            self.struct_span_err(
-                generics.span,
-                "generic parameters on `where` clauses are reserved for future use",
-            )
-            .span_label(generics.span, "currently unsupported")
-            .emit();
+            self.sess.emit_err(errors::WhereOnGenerics { span: generics.span });
         }
 
         loop {
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index c25c23d849f..6cceb47ff83 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -150,16 +150,13 @@ impl<'a> Parser<'a> {
             //
             if style == PathStyle::Mod && path.segments.iter().any(|segment| segment.args.is_some())
             {
-                parser
-                    .struct_span_err(
-                        path.segments
-                            .iter()
-                            .filter_map(|segment| segment.args.as_ref())
-                            .map(|arg| arg.span())
-                            .collect::<Vec<_>>(),
-                        "unexpected generic arguments in path",
-                    )
-                    .emit();
+                let span = path
+                    .segments
+                    .iter()
+                    .filter_map(|segment| segment.args.as_ref())
+                    .map(|arg| arg.span())
+                    .collect::<Vec<_>>();
+                parser.sess.emit_err(errors::GenericsInPath { span });
             }
         };
 
@@ -620,10 +617,7 @@ impl<'a> Parser<'a> {
                 c.into()
             }
             Some(GenericArg::Lifetime(lt)) => {
-                self.struct_span_err(span, "associated lifetimes are not supported")
-                    .span_label(lt.ident.span, "the lifetime is given here")
-                    .help("if you meant to specify a trait object, write `dyn Trait + 'lifetime`")
-                    .emit();
+                self.sess.emit_err(errors::AssocLifetime { span, lifetime: lt.ident.span });
                 self.mk_ty(span, ast::TyKind::Err).into()
             }
             None => {
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 400c8dbe9bc..f5f6788362b 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -1,7 +1,7 @@
 use super::{Parser, PathStyle, TokenType};
 
 use crate::errors::{
-    DynAfterMut, ExpectedFnPathFoundFnKeyword, ExpectedMutOrConstInRawPointerType,
+    self, DynAfterMut, ExpectedFnPathFoundFnKeyword, ExpectedMutOrConstInRawPointerType,
     FnPointerCannotBeAsync, FnPointerCannotBeConst, FnPtrWithGenerics, FnPtrWithGenericsSugg,
     InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime,
     NegativeBoundsNotSupported, NegativeBoundsNotSupportedSugg, NestedCVariadicType,
@@ -807,16 +807,11 @@ impl<'a> Parser<'a> {
     /// Emits an error if any trait bound modifiers were present.
     fn error_lt_bound_with_modifiers(&self, modifiers: BoundModifiers) {
         if let Some(span) = modifiers.maybe_const {
-            self.struct_span_err(
-                span,
-                "`~const` may only modify trait bounds, not lifetime bounds",
-            )
-            .emit();
+            self.sess.emit_err(errors::TildeConstLifetime { span });
         }
 
         if let Some(span) = modifiers.maybe {
-            self.struct_span_err(span, "`?` may only modify trait bounds, not lifetime bounds")
-                .emit();
+            self.sess.emit_err(errors::MaybeLifetime { span });
         }
     }
 
@@ -824,19 +819,14 @@ impl<'a> Parser<'a> {
     fn recover_paren_lifetime(&mut self, lo: Span, inner_lo: Span) -> PResult<'a, ()> {
         let inner_span = inner_lo.to(self.prev_token.span);
         self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
-        let mut err = self.struct_span_err(
-            lo.to(self.prev_token.span),
-            "parenthesized lifetime bounds are not supported",
-        );
-        if let Ok(snippet) = self.span_to_snippet(inner_span) {
-            err.span_suggestion_short(
-                lo.to(self.prev_token.span),
-                "remove the parentheses",
-                snippet,
-                Applicability::MachineApplicable,
-            );
-        }
-        err.emit();
+        let span = lo.to(self.prev_token.span);
+        let (sugg, snippet) = if let Ok(snippet) = self.span_to_snippet(inner_span) {
+            (Some(span), snippet)
+        } else {
+            (None, String::new())
+        };
+
+        self.sess.emit_err(errors::ParenthesizedLifetime { span, sugg, snippet });
         Ok(())
     }
 
@@ -857,15 +847,7 @@ impl<'a> Parser<'a> {
         } else if self.eat_keyword(kw::Const) {
             let span = self.prev_token.span;
             self.sess.gated_spans.gate(sym::const_trait_impl, span);
-
-            self.struct_span_err(span, "const bounds must start with `~`")
-                .span_suggestion(
-                    span.shrink_to_lo(),
-                    "add `~`",
-                    "~",
-                    Applicability::MachineApplicable,
-                )
-                .emit();
+            self.sess.emit_err(errors::ConstMissingTilde { span, start: span.shrink_to_lo() });
 
             Some(span)
         } else {
diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl
index 192badcbc37..32409499047 100644
--- a/compiler/rustc_resolve/messages.ftl
+++ b/compiler/rustc_resolve/messages.ftl
@@ -226,3 +226,10 @@ resolve_add_as_non_derive =
 
 resolve_proc_macro_same_crate = can't use a procedural macro from the same crate that defines it
     .help = you can define integration tests in a directory named `tests`
+
+resolve_imported_crate = `$crate` may not be imported
+
+resolve_macro_use_extern_crate_self = `#[macro_use]` is not supported on `extern crate self`
+
+resolve_accessible_unsure = not sure whether the path is accessible or not
+    .note = the type may have associated items, but we are currently not checking them
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 967c9e22fb2..3799679cb1e 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -9,7 +9,9 @@ use crate::def_collector::collect_definitions;
 use crate::imports::{Import, ImportKind};
 use crate::macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef};
 use crate::Namespace::{self, MacroNS, TypeNS, ValueNS};
-use crate::{Determinacy, ExternPreludeEntry, Finalize, Module, ModuleKind, ModuleOrUniformRoot};
+use crate::{
+    errors, Determinacy, ExternPreludeEntry, Finalize, Module, ModuleKind, ModuleOrUniformRoot,
+};
 use crate::{
     MacroData, NameBinding, NameBindingKind, ParentScope, PathResult, PerNS, ResolutionError,
 };
@@ -523,11 +525,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
                             ident.name = crate_name;
                         }
 
-                        self.r
-                            .tcx
-                            .sess
-                            .struct_span_err(item.span, "`$crate` may not be imported")
-                            .emit();
+                        self.r.tcx.sess.emit_err(errors::CrateImported { span: item.span });
                     }
                 }
 
@@ -1028,11 +1026,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
                         self.r
                             .tcx
                             .sess
-                            .struct_span_err(
-                                attr.span,
-                                "`#[macro_use]` is not supported on `extern crate self`",
-                            )
-                            .emit();
+                            .emit_err(errors::MacroUseExternCrateSelf { span: attr.span });
                     }
                 }
                 let ill_formed = |span| {
diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs
index 6197af105a9..4f9f1c7e856 100644
--- a/compiler/rustc_resolve/src/errors.rs
+++ b/compiler/rustc_resolve/src/errors.rs
@@ -517,3 +517,25 @@ pub(crate) struct ProcMacroSameCrate {
     #[help]
     pub(crate) is_test: bool,
 }
+
+#[derive(Diagnostic)]
+#[diag(resolve_imported_crate)]
+pub(crate) struct CrateImported {
+    #[primary_span]
+    pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_macro_use_extern_crate_self)]
+pub(crate) struct MacroUseExternCrateSelf {
+    #[primary_span]
+    pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_accessible_unsure)]
+#[note]
+pub(crate) struct CfgAccessibleUnsure {
+    #[primary_span]
+    pub(crate) span: Span,
+}
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 2211fb56ccd..b30c1cd226c 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -436,9 +436,7 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
                 // HACK(Urgau): This shouldn't be necessary
                 PathResult::Failed { is_error_from_last_segment: false, .. } => {
                     self.tcx.sess
-                        .struct_span_err(span, "not sure whether the path is accessible or not")
-                        .note("the type may have associated items, but we are currently not checking them")
-                        .emit();
+                        .emit_err(errors::CfgAccessibleUnsure { span });
 
                     // If we get a partially resolved NonModule in one namespace, we should get the
                     // same result in any other namespaces, so we can return early.