Rollup merge of #151338 - factor_slug, r=Kivooeo

Factor out diagnostic slug checking from `DiagnosticDerive`

Doing some cleanup work in preparation for https://github.com/rust-lang/compiler-team/issues/959

r? @Kivooeo
This commit is contained in:
Jonathan Brouwer 2026-01-19 08:31:32 +01:00 committed by GitHub
commit d2375c2edf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 67 additions and 84 deletions

View file

@ -4,12 +4,10 @@ use std::cell::RefCell;
use proc_macro2::TokenStream;
use quote::quote;
use syn::spanned::Spanned;
use synstructure::Structure;
use crate::diagnostics::diagnostic_builder::DiagnosticDeriveKind;
use crate::diagnostics::error::{DiagnosticDeriveError, span_err};
use crate::diagnostics::utils::SetOnce;
use crate::diagnostics::error::DiagnosticDeriveError;
/// The central struct for constructing the `into_diag` method from an annotated struct.
pub(crate) struct DiagnosticDerive<'a> {
@ -29,36 +27,16 @@ impl<'a> DiagnosticDerive<'a> {
let preamble = builder.preamble(variant);
let body = builder.body(variant);
let init = match builder.slug.value_ref() {
None => {
span_err(builder.span, "diagnostic slug not specified")
.help(
"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) => {
slugs.borrow_mut().push(slug.clone());
quote! {
let mut diag = rustc_errors::Diag::new(
dcx,
level,
crate::fluent_generated::#slug
);
}
}
let Some(slug) = builder.primary_message() else {
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
};
slugs.borrow_mut().push(slug.clone());
let init = quote! {
let mut diag = rustc_errors::Diag::new(
dcx,
level,
crate::fluent_generated::#slug
);
};
let formatting_init = &builder.formatting_init;
@ -113,32 +91,12 @@ impl<'a> LintDiagnosticDerive<'a> {
let preamble = builder.preamble(variant);
let body = builder.body(variant);
let primary_message = match builder.slug.value_ref() {
None => {
span_err(builder.span, "diagnostic slug not specified")
.help(
"specify the slug as the first argument to the attribute, such as \
`#[diag(compiletest_example)]`",
)
.emit();
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) => {
slugs.borrow_mut().push(slug.clone());
quote! {
diag.primary_message(crate::fluent_generated::#slug);
}
}
let Some(slug) = builder.primary_message() else {
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
};
slugs.borrow_mut().push(slug.clone());
let primary_message = quote! {
diag.primary_message(crate::fluent_generated::#slug);
};
let formatting_init = &builder.formatting_init;
@ -172,30 +130,6 @@ 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
}
}
}
/// Generates a `#[test]` that verifies that all referenced variables
/// exist on this structure.
fn generate_test(slug: &syn::Path, structure: &Structure<'_>) -> TokenStream {

View file

@ -110,6 +110,31 @@ impl DiagnosticDeriveKind {
}
impl DiagnosticDeriveVariantBuilder {
pub(crate) fn primary_message(&self) -> Option<&Path> {
match self.slug.value_ref() {
None => {
span_err(self.span, "diagnostic slug not specified")
.help(
"specify the slug as the first argument to the `#[diag(...)]` \
attribute, such as `#[diag(hir_analysis_example_error)]`",
)
.emit();
None
}
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();
None
}
Some(slug) => Some(slug),
}
}
/// 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 {
@ -504,3 +529,27 @@ impl DiagnosticDeriveVariantBuilder {
}
}
}
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) {
return None;
}
Some(Mismatch { slug_name, slug_prefix: slug_prefix.to_string(), crate_name })
}
}

View file

@ -384,7 +384,7 @@ error: derive(Diagnostic): diagnostic slug not specified
LL | #[lint(no_crate_example, code = E0123)]
| ^
|
= help: specify the slug as the first argument to the attribute, such as `#[diag(compiletest_example)]`
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
error: derive(Diagnostic): attribute specified multiple times
--> $DIR/diagnostic-derive.rs:613:53