]> git.proxmox.com Git - rustc.git/blobdiff - compiler/rustc_macros/src/diagnostics/diagnostic.rs
New upstream version 1.67.1+dfsg1
[rustc.git] / compiler / rustc_macros / src / diagnostics / diagnostic.rs
index ef1985b960e361ac2e065a60a5f8beec6ba7c3be..684835d8c5c86b88e60713d8639ab1962a4eb0da 100644 (file)
@@ -5,6 +5,7 @@ use crate::diagnostics::error::{span_err, DiagnosticDeriveError};
 use crate::diagnostics::utils::SetOnce;
 use proc_macro2::TokenStream;
 use quote::quote;
+use syn::spanned::Spanned;
 use synstructure::Structure;
 
 /// The central struct for constructing the `into_diagnostic` method from an annotated struct.
@@ -28,8 +29,8 @@ impl<'a> DiagnosticDerive<'a> {
         let DiagnosticDerive { mut structure, mut builder } = self;
 
         let implementation = builder.each_variant(&mut structure, |mut builder, variant| {
-            let preamble = builder.preamble(&variant);
-            let body = builder.body(&variant);
+            let preamble = builder.preamble(variant);
+            let body = builder.body(variant);
 
             let diag = &builder.parent.diag;
             let DiagnosticDeriveKind::Diagnostic { handler } = &builder.parent.kind else {
@@ -38,13 +39,24 @@ impl<'a> DiagnosticDerive<'a> {
             let init = match builder.slug.value_ref() {
                 None => {
                     span_err(builder.span, "diagnostic slug not specified")
-                        .help(&format!(
+                        .help(format!(
                             "specify the slug as the first argument to the `#[diag(...)]` \
                             attribute, such as `#[diag(hir_analysis_example_error)]`",
                         ))
                         .emit();
                     return DiagnosticDeriveError::ErrorHandled.to_compile_error();
                 }
+                Some(slug) if let Some( Mismatch { slug_name, crate_name, slug_prefix }) = Mismatch::check(slug) => {
+                    span_err(slug.span().unwrap(), "diagnostic slug and crate name do not match")
+                        .note(format!(
+                            "slug is `{slug_name}` but the crate name is `{crate_name}`"
+                        ))
+                        .help(format!(
+                            "expected a slug starting with `{slug_prefix}_...`"
+                        ))
+                        .emit();
+                    return DiagnosticDeriveError::ErrorHandled.to_compile_error();
+                }
                 Some(slug) => {
                     quote! {
                         let mut #diag = #handler.struct_diagnostic(rustc_errors::fluent::#slug);
@@ -69,6 +81,8 @@ impl<'a> DiagnosticDerive<'a> {
                     for @Self
                 where G: rustc_errors::EmissionGuarantee
             {
+
+                #[track_caller]
                 fn into_diagnostic(
                     self,
                     #handler: &'__diagnostic_handler_sess rustc_errors::Handler
@@ -99,8 +113,8 @@ impl<'a> LintDiagnosticDerive<'a> {
         let LintDiagnosticDerive { mut structure, mut builder } = self;
 
         let implementation = builder.each_variant(&mut structure, |mut builder, variant| {
-            let preamble = builder.preamble(&variant);
-            let body = builder.body(&variant);
+            let preamble = builder.preamble(variant);
+            let body = builder.body(variant);
 
             let diag = &builder.parent.diag;
             let formatting_init = &builder.formatting_init;
@@ -114,25 +128,41 @@ impl<'a> LintDiagnosticDerive<'a> {
 
         let msg = builder.each_variant(&mut structure, |mut builder, variant| {
             // Collect the slug by generating the preamble.
-            let _ = builder.preamble(&variant);
+            let _ = builder.preamble(variant);
 
             match builder.slug.value_ref() {
                 None => {
                     span_err(builder.span, "diagnostic slug not specified")
-                        .help(&format!(
+                        .help(format!(
                             "specify the slug as the first argument to the attribute, such as \
                             `#[diag(compiletest_example)]`",
                         ))
                         .emit();
-                    return DiagnosticDeriveError::ErrorHandled.to_compile_error();
+                    DiagnosticDeriveError::ErrorHandled.to_compile_error()
+                }
+                Some(slug) if let Some( Mismatch { slug_name, crate_name, slug_prefix }) = Mismatch::check(slug) => {
+                    span_err(slug.span().unwrap(), "diagnostic slug and crate name do not match")
+                        .note(format!(
+                            "slug is `{slug_name}` but the crate name is `{crate_name}`"
+                        ))
+                        .help(format!(
+                            "expected a slug starting with `{slug_prefix}_...`"
+                        ))
+                        .emit();
+                    DiagnosticDeriveError::ErrorHandled.to_compile_error()
+                }
+                Some(slug) => {
+                    quote! {
+                        rustc_errors::fluent::#slug.into()
+                    }
                 }
-                Some(slug) => quote! { rustc_errors::fluent::#slug.into() },
             }
         });
 
         let diag = &builder.diag;
         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, ()>
@@ -148,3 +178,27 @@ impl<'a> LintDiagnosticDerive<'a> {
         })
     }
 }
+
+struct Mismatch {
+    slug_name: String,
+    crate_name: String,
+    slug_prefix: String,
+}
+
+impl Mismatch {
+    /// Checks whether the slug starts with the crate name it's in.
+    fn check(slug: &syn::Path) -> Option<Mismatch> {
+        // If this is missing we're probably in a test, so bail.
+        let crate_name = std::env::var("CARGO_CRATE_NAME").ok()?;
+
+        // If we're not in a "rustc_" crate, bail.
+        let Some(("rustc", slug_prefix)) = crate_name.split_once("_") else { return None };
+
+        let slug_name = slug.segments.first()?.ident.to_string();
+        if !slug_name.starts_with(slug_prefix) {
+            Some(Mismatch { slug_name, slug_prefix: slug_prefix.to_string(), crate_name })
+        } else {
+            None
+        }
+    }
+}