diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl
index 2d1269e1b6ad8..b56bfa98357b3 100644
--- a/compiler/rustc_builtin_macros/messages.ftl
+++ b/compiler/rustc_builtin_macros/messages.ftl
@@ -17,6 +17,9 @@ builtin_macros_asm_expected_other = expected operand, {$is_global_asm ->
     *[false] clobber_abi, options
     }, or additional template string
 
+builtin_macros_asm_expected_string_literal = expected string literal
+    .label = not a string literal
+
 builtin_macros_asm_explicit_register_name = explicit register arguments cannot have names
 
 builtin_macros_asm_mayunwind = asm labels are not allowed with the `may_unwind` option
@@ -25,6 +28,8 @@ builtin_macros_asm_modifier_invalid = asm template modifier must be a single cha
 
 builtin_macros_asm_mutually_exclusive = the `{$opt1}` and `{$opt2}` options are mutually exclusive
 
+builtin_macros_asm_no_matched_argument_name = there is no argument named `{$name}`
+
 builtin_macros_asm_noreturn = asm outputs are not allowed with the `noreturn` option
 
 builtin_macros_asm_opt_already_provided = the `{$symbol}` option was already provided
@@ -228,10 +233,16 @@ builtin_macros_only_one_argument = {$name} takes 1 argument
 
 builtin_macros_proc_macro = `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]`
 
+builtin_macros_proc_macro_attribute_only_be_used_on_bare_functions = the `#[{$path}]` attribute may only be used on bare functions
+
+builtin_macros_proc_macro_attribute_only_usable_with_crate_type = the `#[{$path}]` attribute is only usable with crates of the `proc-macro` crate type
+
 builtin_macros_requires_cfg_pattern =
     macro requires a cfg-pattern as an argument
     .label = cfg-pattern required
 
+builtin_macros_source_uitls_expected_item = expected item, found `{$token}`
+
 builtin_macros_takes_no_arguments = {$name} takes no arguments
 
 builtin_macros_test_bad_fn = {$kind} functions cannot be used for tests
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index 64238e81b2666..dd0f9aaf22104 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -390,9 +390,7 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a,
             }
             Err(opt_lit) => {
                 let span = opt_lit.map_or(p.token.span, |lit| lit.span);
-                let mut err = p.dcx().struct_span_err(span, "expected string literal");
-                err.span_label(span, "not a string literal");
-                return Err(err);
+                return Err(p.dcx().create_err(errors::AsmExpectedStringLiteral { span }));
             }
         };
 
@@ -639,14 +637,13 @@ fn expand_preparsed_asm(
                             match args.named_args.get(&Symbol::intern(name)) {
                                 Some(&idx) => Some(idx),
                                 None => {
-                                    let msg = format!("there is no argument named `{name}`");
                                     let span = arg.position_span;
                                     ecx.dcx()
-                                        .struct_span_err(
-                                            template_span
+                                        .create_err(errors::AsmNoMatchedArgumentName {
+                                            name: name.to_owned(),
+                                            span: template_span
                                                 .from_inner(InnerSpan::new(span.start, span.end)),
-                                            msg,
-                                        )
+                                        })
                                         .emit();
                                     None
                                 }
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index ed2f98f2a393c..49d640436c2f3 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -728,6 +728,14 @@ pub(crate) struct AsmExpectedComma {
     pub(crate) span: Span,
 }
 
+#[derive(Diagnostic)]
+#[diag(builtin_macros_asm_expected_string_literal)]
+pub(crate) struct AsmExpectedStringLiteral {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+}
+
 #[derive(Diagnostic)]
 #[diag(builtin_macros_asm_underscore_input)]
 pub(crate) struct AsmUnderscoreInput {
@@ -781,6 +789,14 @@ pub(crate) struct AsmNoReturn {
     pub(crate) outputs_sp: Vec<Span>,
 }
 
+#[derive(Diagnostic)]
+#[diag(builtin_macros_asm_no_matched_argument_name)]
+pub(crate) struct AsmNoMatchedArgumentName {
+    pub(crate) name: String,
+    #[primary_span]
+    pub(crate) span: Span,
+}
+
 #[derive(Diagnostic)]
 #[diag(builtin_macros_asm_mayunwind)]
 pub(crate) struct AsmMayUnwind {
@@ -872,3 +888,27 @@ pub(crate) struct TakesNoArguments<'a> {
     pub span: Span,
     pub name: &'a str,
 }
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_proc_macro_attribute_only_be_used_on_bare_functions)]
+pub(crate) struct AttributeOnlyBeUsedOnBareFunctions<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub path: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_proc_macro_attribute_only_usable_with_crate_type)]
+pub(crate) struct AttributeOnlyUsableWithCrateType<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub path: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_source_uitls_expected_item)]
+pub(crate) struct ExpectedItem<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub token: &'a str,
+}
diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
index 99d0191958d68..a8a595ea5796f 100644
--- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
+++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
@@ -214,12 +214,12 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
         };
 
         if !is_fn {
-            let msg = format!(
-                "the `#[{}]` attribute may only be used on bare functions",
-                pprust::path_to_string(&attr.get_normal_item().path),
-            );
-
-            self.dcx.span_err(attr.span, msg);
+            self.dcx
+                .create_err(errors::AttributeOnlyBeUsedOnBareFunctions {
+                    span: attr.span,
+                    path: &pprust::path_to_string(&attr.get_normal_item().path),
+                })
+                .emit();
             return;
         }
 
@@ -228,12 +228,12 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
         }
 
         if !self.is_proc_macro_crate {
-            let msg = format!(
-                "the `#[{}]` attribute is only usable with crates of the `proc-macro` crate type",
-                pprust::path_to_string(&attr.get_normal_item().path),
-            );
-
-            self.dcx.span_err(attr.span, msg);
+            self.dcx
+                .create_err(errors::AttributeOnlyUsableWithCrateType {
+                    span: attr.span,
+                    path: &pprust::path_to_string(&attr.get_normal_item().path),
+                })
+                .emit();
             return;
         }
 
diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs
index dc1d82df0c395..44db12cf69502 100644
--- a/compiler/rustc_builtin_macros/src/source_util.rs
+++ b/compiler/rustc_builtin_macros/src/source_util.rs
@@ -1,3 +1,4 @@
+use crate::errors;
 use crate::util::{
     check_zero_tts, get_single_str_from_tts, get_single_str_spanned_from_tts, parse_expr,
 };
@@ -165,9 +166,13 @@ pub(crate) fn expand_include<'cx>(
                     Ok(Some(item)) => ret.push(item),
                     Ok(None) => {
                         if self.p.token != token::Eof {
-                            let token = pprust::token_to_string(&self.p.token);
-                            let msg = format!("expected item, found `{token}`");
-                            self.p.dcx().span_err(self.p.token.span, msg);
+                            self.p
+                                .dcx()
+                                .create_err(errors::ExpectedItem {
+                                    span: self.p.token.span,
+                                    token: &pprust::token_to_string(&self.p.token),
+                                })
+                                .emit();
                         }
 
                         break;