Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 17 additions & 83 deletions compiler/rustc_macros/src/diagnostics/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down
49 changes: 49 additions & 0 deletions compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 })
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading