about summary refs log tree commit diff
path: root/compiler/rustc_macros/src
diff options
context:
space:
mode:
authorDavid Wood <david.wood@huawei.com>2022-10-03 14:24:17 +0100
committerDavid Wood <david.wood@huawei.com>2022-10-10 14:20:16 +0100
commit291a4736d9300a9be79a5f105c54a06a2a4f1d1b (patch)
tree5d7be56c9834c4ef34608082b09bd096f997c7c6 /compiler/rustc_macros/src
parent540b203bf9fe05e572f1baa938317d4c10df3528 (diff)
downloadrust-291a4736d9300a9be79a5f105c54a06a2a4f1d1b.tar.gz
rust-291a4736d9300a9be79a5f105c54a06a2a4f1d1b.zip
macros: `#[subdiagnostic(eager)]`
Add support for `eager` argument to the `subdiagnostic` attribute which
generates a call to `eager_subdiagnostic`.

Signed-off-by: David Wood <david.wood@huawei.com>
Diffstat (limited to 'compiler/rustc_macros/src')
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic.rs13
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs71
2 files changed, 63 insertions, 21 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)?;