about summary refs log tree commit diff
path: root/compiler/rustc_macros/src/session_diagnostic.rs
diff options
context:
space:
mode:
authorDavid Wood <david.wood@huawei.com>2022-03-31 10:24:46 +0100
committerDavid Wood <david.wood@huawei.com>2022-04-05 07:01:03 +0100
commita88717cef051b8ebbed0e74c57fc0a714bd893dc (patch)
treeda04f2a5e842261b84379a6b240700367f6127e5 /compiler/rustc_macros/src/session_diagnostic.rs
parent72dec56028d848ba18a7e1a9bf9da0b6faf5f7da (diff)
downloadrust-a88717cef051b8ebbed0e74c57fc0a714bd893dc.tar.gz
rust-a88717cef051b8ebbed0e74c57fc0a714bd893dc.zip
macros: support translatable labels
Extends support for generating `DiagnosticMessage::FluentIdentifier`
messages from `SessionDiagnostic` derive to `#[label]`.

Signed-off-by: David Wood <david.wood@huawei.com>
Diffstat (limited to 'compiler/rustc_macros/src/session_diagnostic.rs')
-rw-r--r--compiler/rustc_macros/src/session_diagnostic.rs109
1 files changed, 75 insertions, 34 deletions
diff --git a/compiler/rustc_macros/src/session_diagnostic.rs b/compiler/rustc_macros/src/session_diagnostic.rs
index 79086931d5f..efbffabbd03 100644
--- a/compiler/rustc_macros/src/session_diagnostic.rs
+++ b/compiler/rustc_macros/src/session_diagnostic.rs
@@ -561,6 +561,7 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> {
     ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> {
         let diag = &self.diag;
         let field_binding = &info.binding.binding;
+
         let name = attr.path.segments.last().unwrap().ident.to_string();
         let name = name.as_str();
 
@@ -573,46 +574,38 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> {
                     Ok(quote! {})
                 }
                 "primary_span" => {
-                    if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
-                        return Ok(quote! {
-                            #diag.set_span(*#field_binding);
-                        });
-                    } else {
-                        throw_span_err!(
-                            attr.span().unwrap(),
-                            "the `#[primary_span]` attribute can only be applied to fields of type `Span`"
-                        );
-                    }
+                    self.report_error_if_not_applied_to_span(attr, info)?;
+                    Ok(quote! {
+                        #diag.set_span(*#field_binding);
+                    })
+                }
+                "label" => {
+                    self.report_error_if_not_applied_to_span(attr, info)?;
+                    Ok(self.add_subdiagnostic(field_binding, name, "label"))
                 }
                 other => throw_span_err!(
                     attr.span().unwrap(),
                     &format!("`#[{}]` is not a valid `SessionDiagnostic` field attribute", other)
                 ),
             },
-            syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
-                let formatted_str = self.build_format(&s.value(), attr.span());
-                match name {
-                    "label" => {
-                        if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
-                            return Ok(quote! {
-                                #diag.span_label(*#field_binding, #formatted_str);
-                            });
-                        } else {
-                            throw_span_err!(
-                                attr.span().unwrap(),
-                                "the `#[label = ...]` attribute can only be applied to fields of type `Span`"
-                            );
-                        }
-                    }
-                    other => throw_span_err!(
-                        attr.span().unwrap(),
-                        &format!(
-                            "`#[{} = ...]` is not a valid `SessionDiagnostic` field attribute",
-                            other
-                        )
-                    ),
+            syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => match name {
+                "label" => {
+                    self.report_error_if_not_applied_to_span(attr, info)?;
+                    Ok(self.add_subdiagnostic(field_binding, name, &s.value()))
                 }
-            }
+                other => throw_span_err!(
+                    attr.span().unwrap(),
+                    &format!(
+                        "`#[{} = ...]` is not a valid `SessionDiagnostic` field attribute",
+                        other
+                    )
+                ),
+            },
+            syn::Meta::NameValue(_) => throw_span_err!(
+                attr.span().unwrap(),
+                &format!("`#[{} = ...]` is not a valid `SessionDiagnostic` field attribute", name),
+                |diag| diag.help("value must be a string")
+            ),
             syn::Meta::List(list) => {
                 match list.path.segments.iter().last().unwrap().ident.to_string().as_str() {
                     suggestion_kind @ "suggestion"
@@ -681,7 +674,55 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> {
                     ),
                 }
             }
-            _ => panic!("unhandled meta kind"),
+        }
+    }
+
+    /// Reports an error if the field's type is not `Span`.
+    fn report_error_if_not_applied_to_span(
+        &self,
+        attr: &syn::Attribute,
+        info: FieldInfo<'_>,
+    ) -> Result<(), SessionDiagnosticDeriveError> {
+        if !type_matches_path(&info.ty, &["rustc_span", "Span"]) {
+            let name = attr.path.segments.last().unwrap().ident.to_string();
+            let name = name.as_str();
+            let meta = attr.parse_meta()?;
+
+            throw_span_err!(
+                attr.span().unwrap(),
+                &format!(
+                    "the `#[{}{}]` attribute can only be applied to fields of type `Span`",
+                    name,
+                    match meta {
+                        syn::Meta::Path(_) => "",
+                        syn::Meta::NameValue(_) => " = ...",
+                        syn::Meta::List(_) => "(...)",
+                    }
+                )
+            );
+        }
+
+        Ok(())
+    }
+
+    /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug and
+    /// `fluent_attr_identifier`.
+    fn add_subdiagnostic(
+        &self,
+        field_binding: &proc_macro2::Ident,
+        kind: &str,
+        fluent_attr_identifier: &str,
+    ) -> proc_macro2::TokenStream {
+        let diag = &self.diag;
+
+        let slug =
+            self.slug.as_ref().map(|(slug, _)| slug.as_str()).unwrap_or_else(|| "missing-slug");
+        let fn_name = format_ident!("span_{}", kind);
+        quote! {
+            #diag.#fn_name(
+                *#field_binding,
+                rustc_errors::DiagnosticMessage::fluent_attr(#slug, #fluent_attr_identifier)
+            );
         }
     }