about summary refs log tree commit diff
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
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>
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic.rs13
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs71
-rw-r--r--src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs47
-rw-r--r--src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr42
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`.