about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDavid Wood <david.wood@huawei.com>2022-03-31 12:10:00 +0100
committerDavid Wood <david.wood@huawei.com>2022-04-05 07:01:03 +0100
commitb40ee88a28938891dd40d35a635d13373c3011cb (patch)
tree205667c818313077c5df996c430293376daeb726
parenta88717cef051b8ebbed0e74c57fc0a714bd893dc (diff)
downloadrust-b40ee88a28938891dd40d35a635d13373c3011cb.tar.gz
rust-b40ee88a28938891dd40d35a635d13373c3011cb.zip
macros: note/help in `SessionDiagnostic` derive
Signed-off-by: David Wood <david.wood@huawei.com>
-rw-r--r--compiler/rustc_macros/src/lib.rs2
-rw-r--r--compiler/rustc_macros/src/session_diagnostic.rs52
-rw-r--r--src/test/ui-fulldeps/session-derive-errors.rs88
-rw-r--r--src/test/ui-fulldeps/session-derive-errors.stderr26
4 files changed, 158 insertions, 10 deletions
diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs
index 67b56d2e9db..3589860eb0e 100644
--- a/compiler/rustc_macros/src/lib.rs
+++ b/compiler/rustc_macros/src/lib.rs
@@ -66,6 +66,8 @@ decl_derive!(
         // struct attributes
         warning,
         error,
+        note,
+        help,
         // field attributes
         skip_arg,
         primary_span,
diff --git a/compiler/rustc_macros/src/session_diagnostic.rs b/compiler/rustc_macros/src/session_diagnostic.rs
index efbffabbd03..c7ee72907b5 100644
--- a/compiler/rustc_macros/src/session_diagnostic.rs
+++ b/compiler/rustc_macros/src/session_diagnostic.rs
@@ -362,18 +362,52 @@ struct SessionDiagnosticDeriveBuilder<'a> {
 
 impl<'a> SessionDiagnosticDeriveBuilder<'a> {
     /// Establishes state in the `SessionDiagnosticDeriveBuilder` resulting from the struct
-    /// attributes like `#[error(..)#`, such as the diagnostic kind, slug and code.
-    ///
-    /// Returns a `proc_macro2::TokenStream` so that the `Err(..)` variant can be transformed into
-    /// the same type via `to_compile_error`.
+    /// attributes like `#[error(..)#`, such as the diagnostic kind and slug. Generates
+    /// diagnostic builder calls for setting error code and creating note/help messages.
     fn generate_structure_code(
         &mut self,
         attr: &syn::Attribute,
     ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> {
         let span = attr.span().unwrap();
+
         let name = attr.path.segments.last().unwrap().ident.to_string();
+        let name = name.as_str();
+        let meta = attr.parse_meta()?;
+
+        if matches!(name, "help" | "note")
+            && matches!(meta, syn::Meta::Path(_) | syn::Meta::NameValue(_))
+        {
+            let diag = &self.diag;
+            let slug = match &self.slug {
+                Some((slug, _)) => slug.as_str(),
+                None => throw_span_err!(
+                    span,
+                    &format!(
+                        "`#[{}{}]` must come after `#[error(..)]` or `#[warn(..)]`",
+                        name,
+                        match meta {
+                            syn::Meta::Path(_) => "",
+                            syn::Meta::NameValue(_) => " = ...",
+                            _ => unreachable!(),
+                        }
+                    )
+                ),
+            };
+            let id = match meta {
+                syn::Meta::Path(..) => quote! { #name },
+                syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
+                    quote! { #s }
+                }
+                _ => unreachable!(),
+            };
+            let fn_name = proc_macro2::Ident::new(name, attr.span());
+
+            return Ok(quote! {
+                #diag.#fn_name(rustc_errors::DiagnosticMessage::fluent_attr(#slug, #id));
+            });
+        }
 
-        let nested = match attr.parse_meta()? {
+        let nested = match meta {
             syn::Meta::List(syn::MetaList { nested, .. }) => nested,
             syn::Meta::Path(..) => throw_span_err!(
                 span,
@@ -385,7 +419,7 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> {
             ),
         };
 
-        let kind = match name.as_str() {
+        let kind = match name {
             "error" => SessionDiagnosticKind::Error,
             "warning" => SessionDiagnosticKind::Warn,
             other => throw_span_err!(
@@ -579,9 +613,9 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> {
                         #diag.set_span(*#field_binding);
                     })
                 }
-                "label" => {
+                "label" | "note" | "help" => {
                     self.report_error_if_not_applied_to_span(attr, info)?;
-                    Ok(self.add_subdiagnostic(field_binding, name, "label"))
+                    Ok(self.add_subdiagnostic(field_binding, name, name))
                 }
                 other => throw_span_err!(
                     attr.span().unwrap(),
@@ -589,7 +623,7 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> {
                 ),
             },
             syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => match name {
-                "label" => {
+                "label" | "note" | "help" => {
                     self.report_error_if_not_applied_to_span(attr, info)?;
                     Ok(self.add_subdiagnostic(field_binding, name, &s.value()))
                 }
diff --git a/src/test/ui-fulldeps/session-derive-errors.rs b/src/test/ui-fulldeps/session-derive-errors.rs
index 68f24f1f685..a3ae3c9a14d 100644
--- a/src/test/ui-fulldeps/session-derive-errors.rs
+++ b/src/test/ui-fulldeps/session-derive-errors.rs
@@ -339,3 +339,91 @@ struct ArgFieldWithSkip {
     #[skip_arg]
     other: Hello,
 }
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct ErrorWithSpannedNote {
+    #[note]
+    span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct ErrorWithSpannedNoteCustom {
+    #[note = "bar"]
+    span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+#[note]
+struct ErrorWithNote {
+    val: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+#[note = "bar"]
+struct ErrorWithNoteCustom {
+    val: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct ErrorWithSpannedHelp {
+    #[help]
+    span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct ErrorWithSpannedHelpCustom {
+    #[help = "bar"]
+    span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+#[help]
+struct ErrorWithHelp {
+    val: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+#[help = "bar"]
+struct ErrorWithHelpCustom {
+    val: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[help]
+//~^ ERROR `#[help]` must come after `#[error(..)]` or `#[warn(..)]`
+#[error(code = "E0123", slug = "foo")]
+struct ErrorWithHelpWrongOrder {
+    val: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[help = "bar"]
+//~^ ERROR `#[help = ...]` must come after `#[error(..)]` or `#[warn(..)]`
+#[error(code = "E0123", slug = "foo")]
+struct ErrorWithHelpCustomWrongOrder {
+    val: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[note]
+//~^ ERROR `#[note]` must come after `#[error(..)]` or `#[warn(..)]`
+#[error(code = "E0123", slug = "foo")]
+struct ErrorWithNoteWrongOrder {
+    val: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[note = "bar"]
+//~^ ERROR `#[note = ...]` must come after `#[error(..)]` or `#[warn(..)]`
+#[error(code = "E0123", slug = "foo")]
+struct ErrorWithNoteCustomWrongOrder {
+    val: String,
+}
diff --git a/src/test/ui-fulldeps/session-derive-errors.stderr b/src/test/ui-fulldeps/session-derive-errors.stderr
index 902bc785ce7..dc8e807cea3 100644
--- a/src/test/ui-fulldeps/session-derive-errors.stderr
+++ b/src/test/ui-fulldeps/session-derive-errors.stderr
@@ -249,6 +249,30 @@ error: invalid annotation list `#[label(...)]`
 LL |     #[label("bar")]
    |       ^^^^^^^^^^^^
 
+error: `#[help]` must come after `#[error(..)]` or `#[warn(..)]`
+  --> $DIR/session-derive-errors.rs:400:1
+   |
+LL | #[help]
+   | ^^^^^^^
+
+error: `#[help = ...]` must come after `#[error(..)]` or `#[warn(..)]`
+  --> $DIR/session-derive-errors.rs:408:1
+   |
+LL | #[help = "bar"]
+   | ^^^^^^^^^^^^^^^
+
+error: `#[note]` must come after `#[error(..)]` or `#[warn(..)]`
+  --> $DIR/session-derive-errors.rs:416:1
+   |
+LL | #[note]
+   | ^^^^^^^
+
+error: `#[note = ...]` must come after `#[error(..)]` or `#[warn(..)]`
+  --> $DIR/session-derive-errors.rs:424:1
+   |
+LL | #[note = "bar"]
+   | ^^^^^^^^^^^^^^^
+
 error: cannot find attribute `nonsense` in this scope
   --> $DIR/session-derive-errors.rs:51:3
    |
@@ -272,6 +296,6 @@ LL | #[derive(SessionDiagnostic)]
    |
    = note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 34 previous errors
+error: aborting due to 38 previous errors
 
 For more information about this error, try `rustc --explain E0599`.