From 291a4736d9300a9be79a5f105c54a06a2a4f1d1b Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 3 Oct 2022 14:24:17 +0100 Subject: [PATCH] macros: `#[subdiagnostic(eager)]` Add support for `eager` argument to the `subdiagnostic` attribute which generates a call to `eager_subdiagnostic`. Signed-off-by: David Wood --- .../src/diagnostics/diagnostic.rs | 13 ++-- .../src/diagnostics/diagnostic_builder.rs | 71 ++++++++++++++----- .../session-diagnostic/diagnostic-derive.rs | 47 ++++++++++++ .../diagnostic-derive.stderr | 42 ++++++++++- 4 files changed, 151 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 406a56cd4ae..84c57b3f64e 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -10,27 +10,31 @@ use synstructure::Structure; /// The central struct for constructing the `into_diagnostic` method from an annotated struct. pub(crate) struct DiagnosticDerive<'a> { structure: Structure<'a>, - handler: syn::Ident, builder: DiagnosticDeriveBuilder, } impl<'a> DiagnosticDerive<'a> { pub(crate) fn new(diag: syn::Ident, handler: syn::Ident, structure: Structure<'a>) -> Self { Self { - builder: DiagnosticDeriveBuilder { diag, kind: DiagnosticDeriveKind::Diagnostic }, - handler, + builder: DiagnosticDeriveBuilder { + diag, + kind: DiagnosticDeriveKind::Diagnostic { handler }, + }, structure, } } pub(crate) fn into_tokens(self) -> TokenStream { - let DiagnosticDerive { mut structure, handler, mut builder } = self; + 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 diag = &builder.parent.diag; + let DiagnosticDeriveKind::Diagnostic { handler } = &builder.parent.kind else { + unreachable!() + }; let init = match builder.slug.value_ref() { None => { span_err(builder.span, "diagnostic slug not specified") @@ -56,6 +60,7 @@ impl<'a> DiagnosticDerive<'a> { } }); + let DiagnosticDeriveKind::Diagnostic { handler } = &builder.kind else { unreachable!() }; structure.gen_impl(quote! { gen impl<'__diagnostic_handler_sess, G> rustc_errors::IntoDiagnostic<'__diagnostic_handler_sess, G> diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index 9e88dc7a913..df4e309086f 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -17,9 +17,9 @@ use syn::{ use synstructure::{BindingInfo, Structure, VariantInfo}; /// What kind of diagnostic is being derived - a fatal/error/warning or a lint? -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq)] pub(crate) enum DiagnosticDeriveKind { - Diagnostic, + Diagnostic { handler: syn::Ident }, LintDiagnostic, } @@ -340,18 +340,15 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { let diag = &self.parent.diag; let meta = attr.parse_meta()?; - if let Meta::Path(_) = meta { - let ident = &attr.path.segments.last().unwrap().ident; - let name = ident.to_string(); - let name = name.as_str(); - match name { - "skip_arg" => { - // Don't need to do anything - by virtue of the attribute existing, the - // `set_arg` call will not be generated. - return Ok(quote! {}); - } - "primary_span" => match self.parent.kind { - DiagnosticDeriveKind::Diagnostic => { + let ident = &attr.path.segments.last().unwrap().ident; + let name = ident.to_string(); + match (&meta, name.as_str()) { + // Don't need to do anything - by virtue of the attribute existing, the + // `set_arg` call will not be generated. + (Meta::Path(_), "skip_arg") => return Ok(quote! {}), + (Meta::Path(_), "primary_span") => { + match self.parent.kind { + DiagnosticDeriveKind::Diagnostic { .. } => { report_error_if_not_applied_to_span(attr, &info)?; return Ok(quote! { @@ -363,10 +360,50 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { diag.help("the `primary_span` field attribute is not valid for lint diagnostics") }) } - }, - "subdiagnostic" => return Ok(quote! { #diag.subdiagnostic(#binding); }), - _ => {} + } } + (Meta::Path(_), "subdiagnostic") => { + return Ok(quote! { #diag.subdiagnostic(#binding); }); + } + (Meta::NameValue(_), "subdiagnostic") => { + throw_invalid_attr!(attr, &meta, |diag| { + diag.help("`eager` is the only supported nested attribute for `subdiagnostic`") + }) + } + (Meta::List(MetaList { ref nested, .. }), "subdiagnostic") => { + if nested.len() != 1 { + throw_invalid_attr!(attr, &meta, |diag| { + diag.help( + "`eager` is the only supported nested attribute for `subdiagnostic`", + ) + }) + } + + let handler = match &self.parent.kind { + DiagnosticDeriveKind::Diagnostic { handler } => handler, + DiagnosticDeriveKind::LintDiagnostic => { + throw_invalid_attr!(attr, &meta, |diag| { + diag.help("eager subdiagnostics are not supported on lints") + }) + } + }; + + let nested_attr = nested.first().expect("pop failed for single element list"); + match nested_attr { + NestedMeta::Meta(meta @ Meta::Path(_)) + if meta.path().segments.last().unwrap().ident.to_string().as_str() + == "eager" => + { + return Ok(quote! { #diag.eager_subdiagnostic(#handler, #binding); }); + } + _ => { + throw_invalid_nested_attr!(attr, nested_attr, |diag| { + diag.help("`eager` is the only supported nested attribute for `subdiagnostic`") + }) + } + } + } + _ => (), } let (subdiag, slug) = self.parse_subdiag_attribute(attr)?; diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index 1dc71abc104..460af07a559 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -678,3 +678,50 @@ enum ExampleEnum { struct RawIdentDiagnosticArg { pub r#type: String, } + +#[derive(Diagnostic)] +#[diag(compiletest::example)] +struct SubdiagnosticBad { + #[subdiagnostic(bad)] +//~^ ERROR `#[subdiagnostic(bad)]` is not a valid attribute + note: Note, +} + +#[derive(Diagnostic)] +#[diag(compiletest::example)] +struct SubdiagnosticBadStr { + #[subdiagnostic = "bad"] +//~^ ERROR `#[subdiagnostic = ...]` is not a valid attribute + note: Note, +} + +#[derive(Diagnostic)] +#[diag(compiletest::example)] +struct SubdiagnosticBadTwice { + #[subdiagnostic(bad, bad)] +//~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute + note: Note, +} + +#[derive(Diagnostic)] +#[diag(compiletest::example)] +struct SubdiagnosticBadLitStr { + #[subdiagnostic("bad")] +//~^ ERROR `#[subdiagnostic("...")]` is not a valid attribute + note: Note, +} + +#[derive(LintDiagnostic)] +#[diag(compiletest::example)] +struct SubdiagnosticEagerLint { + #[subdiagnostic(eager)] +//~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute + note: Note, +} + +#[derive(Diagnostic)] +#[diag(compiletest::example)] +struct SubdiagnosticEagerCorrect { + #[subdiagnostic(eager)] + note: Note, +} diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index 167198b47f2..7a42d618707 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -533,6 +533,46 @@ LL | #[label] | = help: `#[label]` and `#[suggestion]` can only be applied to fields +error: `#[subdiagnostic(bad)]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:685:21 + | +LL | #[subdiagnostic(bad)] + | ^^^ + | + = help: `eager` is the only supported nested attribute for `subdiagnostic` + +error: `#[subdiagnostic = ...]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:693:5 + | +LL | #[subdiagnostic = "bad"] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `eager` is the only supported nested attribute for `subdiagnostic` + +error: `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:701:5 + | +LL | #[subdiagnostic(bad, bad)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `eager` is the only supported nested attribute for `subdiagnostic` + +error: `#[subdiagnostic("...")]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:709:21 + | +LL | #[subdiagnostic("bad")] + | ^^^^^ + | + = help: `eager` is the only supported nested attribute for `subdiagnostic` + +error: `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:717:5 + | +LL | #[subdiagnostic(eager)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: eager subdiagnostics are not supported on lints + error: cannot find attribute `nonsense` in this scope --> $DIR/diagnostic-derive.rs:55:3 | @@ -607,7 +647,7 @@ LL | arg: impl IntoDiagnosticArg, | ^^^^^^^^^^^^^^^^^ required by this bound in `DiagnosticBuilder::<'a, G>::set_arg` = note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 75 previous errors +error: aborting due to 80 previous errors Some errors have detailed explanations: E0277, E0425. For more information about an error, try `rustc --explain E0277`.