diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index c3772475769..be9821c00f5 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -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. @@ -45,10 +46,19 @@ impl<'a> DiagnosticDerive<'a> { .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) => { - let check = make_check(slug); quote! { - #check let mut #diag = #handler.struct_diagnostic(rustc_errors::fluent::#slug); } } @@ -130,11 +140,19 @@ impl<'a> LintDiagnosticDerive<'a> { .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) => { - let check = make_check(slug); - quote! { - #check rustc_errors::fluent::#slug.into() } } @@ -161,53 +179,26 @@ impl<'a> LintDiagnosticDerive<'a> { } } -/// Checks whether the slug starts with the crate name it's in. -fn make_check(slug: &syn::Path) -> TokenStream { - quote! { - const _: () = { - const krate_str: &str = match option_env!("CARGO_CRATE_NAME") { - Some(c) => c, - None => "", - }; - const krate: &[u8] = krate_str.as_bytes(); +struct Mismatch { + slug_name: String, + crate_name: String, + slug_prefix: String, +} - if krate.len() > 6 - && krate[0] == b'r' - && krate[1] == b'u' - && krate[2] == b's' - && krate[3] == b't' - && krate[4] == b'c' - && krate[5] == b'_' - { - let slug = stringify!(#slug).as_bytes(); +impl Mismatch { + /// Checks whether the slug starts with the crate name it's in. + fn check(slug: &syn::Path) -> Option { + // If this is missing we're probably in a test, so bail. + let crate_name = std::env::var("CARGO_CRATE_NAME").ok()?; - let mut pos = 0; - loop { - let b = slug[pos]; - if krate.len() == pos + 6 { - if b != b'_' { - panic!(concat!( - "slug \"", - stringify!(#slug), - "\" does not match the crate it is in" - )); - } - break; - } - let a = krate[pos + 6]; + // If we're not in a "rustc_" crate, bail. + let Some(("rustc", slug_prefix)) = crate_name.split_once("_") else { return None }; - if a != b { - panic!(concat!( - "slug \"", - stringify!(#slug), - "\" does not match the crate it is in" - )); - } - pos += 1; - } - } else { - // Crate does not start with "rustc_" - } - }; + 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 + } } } diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 36bda3e0f6b..a2a01b66690 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -1,4 +1,5 @@ #![feature(allow_internal_unstable)] +#![feature(if_let_guard)] #![feature(never_type)] #![feature(proc_macro_diagnostic)] #![feature(proc_macro_span)] diff --git a/src/test/ui-fulldeps/session-diagnostic/enforce_slug_naming.rs b/src/test/ui-fulldeps/session-diagnostic/enforce_slug_naming.rs new file mode 100644 index 00000000000..a0a8114e0c5 --- /dev/null +++ b/src/test/ui-fulldeps/session-diagnostic/enforce_slug_naming.rs @@ -0,0 +1,24 @@ +// rustc-env:CARGO_CRATE_NAME=rustc_dummy + +#![feature(rustc_private)] +#![crate_type = "lib"] + +extern crate rustc_span; +use rustc_span::symbol::Ident; +use rustc_span::Span; + +extern crate rustc_macros; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; + +extern crate rustc_middle; +use rustc_middle::ty::Ty; + +extern crate rustc_errors; +use rustc_errors::{Applicability, MultiSpan}; + +extern crate rustc_session; + +#[derive(Diagnostic)] +#[diag(compiletest_example, code = "E0123")] +//~^ ERROR diagnostic slug and crate name do not match +struct Hello {} diff --git a/src/test/ui-fulldeps/session-diagnostic/enforce_slug_naming.stderr b/src/test/ui-fulldeps/session-diagnostic/enforce_slug_naming.stderr new file mode 100644 index 00000000000..dcf4af5df50 --- /dev/null +++ b/src/test/ui-fulldeps/session-diagnostic/enforce_slug_naming.stderr @@ -0,0 +1,11 @@ +error: diagnostic slug and crate name do not match + --> $DIR/enforce_slug_naming.rs:22:8 + | +LL | #[diag(compiletest_example, code = "E0123")] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: slug is `compiletest_example` but the crate name is `rustc_dummy` + = help: expected a slug starting with `dummy_...` + +error: aborting due to previous error +