about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDavid Wood <david.wood@huawei.com>2022-03-31 13:10:13 +0100
committerDavid Wood <david.wood@huawei.com>2022-04-05 07:01:03 +0100
commit22685b9607936ae1330e2f84773c04292f5cdd30 (patch)
tree2976e4e6b87293774e47a064e4e11d8677c0b331
parentb40ee88a28938891dd40d35a635d13373c3011cb (diff)
macros: support translatable suggestions
Extends support for generating `DiagnosticMessage::FluentIdentifier`
messages from `SessionDiagnostic` derive to `#[suggestion]`.

Signed-off-by: David Wood <david.wood@huawei.com>
-rw-r--r--compiler/rustc_macros/src/session_diagnostic.rs159
-rw-r--r--src/test/ui-fulldeps/session-derive-errors.rs45
-rw-r--r--src/test/ui-fulldeps/session-derive-errors.stderr60
3 files changed, 141 insertions, 123 deletions
diff --git a/compiler/rustc_macros/src/session_diagnostic.rs b/compiler/rustc_macros/src/session_diagnostic.rs
index c7ee72907b5..46f698f6f9b 100644
--- a/compiler/rustc_macros/src/session_diagnostic.rs
+++ b/compiler/rustc_macros/src/session_diagnostic.rs
@@ -594,6 +594,7 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> {
         info: FieldInfo<'_>,
     ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> {
         let diag = &self.diag;
+        let span = attr.span().unwrap();
         let field_binding = &info.binding.binding;
 
         let name = attr.path.segments.last().unwrap().ident.to_string();
@@ -618,7 +619,7 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> {
                     Ok(self.add_subdiagnostic(field_binding, name, name))
                 }
                 other => throw_span_err!(
-                    attr.span().unwrap(),
+                    span,
                     &format!("`#[{}]` is not a valid `SessionDiagnostic` field attribute", other)
                 ),
             },
@@ -628,7 +629,7 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> {
                     Ok(self.add_subdiagnostic(field_binding, name, &s.value()))
                 }
                 other => throw_span_err!(
-                    attr.span().unwrap(),
+                    span,
                     &format!(
                         "`#[{} = ...]` is not a valid `SessionDiagnostic` field attribute",
                         other
@@ -636,77 +637,103 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> {
                 ),
             },
             syn::Meta::NameValue(_) => throw_span_err!(
-                attr.span().unwrap(),
+                span,
                 &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"
-                    | suggestion_kind @ "suggestion_short"
-                    | suggestion_kind @ "suggestion_hidden"
-                    | suggestion_kind @ "suggestion_verbose" => {
-                        let (span, applicability) = self.span_and_applicability_of_ty(info)?;
-
-                        let mut msg = None;
-                        let mut code = None;
-
-                        for arg in list.nested.iter() {
-                            if let syn::NestedMeta::Meta(syn::Meta::NameValue(arg_name_value)) = arg
-                            {
-                                if let syn::MetaNameValue { lit: syn::Lit::Str(s), .. } =
-                                    arg_name_value
-                                {
-                                    let name = arg_name_value
-                                        .path
-                                        .segments
-                                        .last()
-                                        .unwrap()
-                                        .ident
-                                        .to_string();
-                                    let name = name.as_str();
-                                    let formatted_str = self.build_format(&s.value(), arg.span());
-                                    match name {
-                                        "message" => {
-                                            msg = Some(formatted_str);
-                                        }
-                                        "code" => {
-                                            code = Some(formatted_str);
-                                        }
-                                        other => throw_span_err!(
-                                            arg.span().unwrap(),
-                                            &format!(
-                                                "`{}` is not a valid key for `#[suggestion(...)]`",
-                                                other
-                                            )
-                                        ),
-                                    }
-                                }
-                            }
-                        }
-                        let msg = if let Some(msg) = msg {
-                            quote!(#msg.as_str())
-                        } else {
-                            throw_span_err!(
-                                list.span().unwrap(),
-                                "missing suggestion message",
-                                |diag| {
-                                    diag.help("provide a suggestion message using `#[suggestion(message = \"...\")]`")
-                                }
-                            );
-                        };
-                        let code = code.unwrap_or_else(|| quote! { String::new() });
+            syn::Meta::List(syn::MetaList { path, nested, .. }) => {
+                let name = path.segments.last().unwrap().ident.to_string();
+                let name = name.as_ref();
 
-                        let suggestion_method = format_ident!("span_{}", suggestion_kind);
-                        return Ok(quote! {
-                            #diag.#suggestion_method(#span, #msg, #code, #applicability);
-                        });
-                    }
+                match name {
+                    "suggestion" | "suggestion_short" | "suggestion_hidden"
+                    | "suggestion_verbose" => (),
                     other => throw_span_err!(
-                        list.span().unwrap(),
-                        &format!("invalid annotation list `#[{}(...)]`", other)
+                        span,
+                        &format!(
+                            "`#[{}(...)]` is not a valid `SessionDiagnostic` field attribute",
+                            other
+                        )
                     ),
+                };
+
+                let (span_, applicability) = self.span_and_applicability_of_ty(info)?;
+
+                let mut msg = None;
+                let mut code = None;
+
+                for attr in nested {
+                    let meta = match attr {
+                        syn::NestedMeta::Meta(meta) => meta,
+                        syn::NestedMeta::Lit(_) => throw_span_err!(
+                            span,
+                            &format!(
+                                "`#[{}(\"...\")]` is not a valid `SessionDiagnostic` field attribute",
+                                name
+                            )
+                        ),
+                    };
+
+                    let span = meta.span().unwrap();
+                    let nested_name = meta.path().segments.last().unwrap().ident.to_string();
+                    let nested_name = nested_name.as_str();
+
+                    match meta {
+                        syn::Meta::NameValue(syn::MetaNameValue {
+                            lit: syn::Lit::Str(s), ..
+                        }) => match nested_name {
+                            "message" => {
+                                msg = Some(s.value());
+                            }
+                            "code" => {
+                                let formatted_str = self.build_format(&s.value(), s.span());
+                                code = Some(formatted_str);
+                            }
+                            other => throw_span_err!(
+                                span,
+                                &format!(
+                                    "`#[{}({} = ...)]` is not a valid `SessionDiagnostic` field attribute",
+                                    name, other
+                                )
+                            ),
+                        },
+                        syn::Meta::NameValue(..) => throw_span_err!(
+                            span,
+                            &format!(
+                                "`#[{}({} = ...)]` is not a valid `SessionDiagnostic` struct attribute",
+                                name, nested_name
+                            ),
+                            |diag| diag.help("value must be a string")
+                        ),
+                        syn::Meta::Path(..) => throw_span_err!(
+                            span,
+                            &format!(
+                                "`#[{}({})]` is not a valid `SessionDiagnostic` struct attribute",
+                                name, nested_name
+                            )
+                        ),
+                        syn::Meta::List(..) => throw_span_err!(
+                            span,
+                            &format!(
+                                "`#[{}({}(...))]` is not a valid `SessionDiagnostic` struct attribute",
+                                name, nested_name
+                            )
+                        ),
+                    }
                 }
+
+                let method = format_ident!("span_{}", name);
+
+                let slug = self
+                    .slug
+                    .as_ref()
+                    .map(|(slug, _)| slug.as_str())
+                    .unwrap_or_else(|| "missing-slug");
+                let msg = msg.as_deref().unwrap_or("suggestion");
+                let msg = quote! { rustc_errors::DiagnosticMessage::fluent_attr(#slug, #msg) };
+                let code = code.unwrap_or_else(|| quote! { String::new() });
+
+                Ok(quote! { #diag.#method(#span_, #msg, #code, #applicability); })
             }
         }
     }
diff --git a/src/test/ui-fulldeps/session-derive-errors.rs b/src/test/ui-fulldeps/session-derive-errors.rs
index a3ae3c9a14d..adec548b390 100644
--- a/src/test/ui-fulldeps/session-derive-errors.rs
+++ b/src/test/ui-fulldeps/session-derive-errors.rs
@@ -83,7 +83,7 @@ struct InvalidNestedStructAttr3 {}
 #[derive(SessionDiagnostic)]
 #[error(code = "E0123", slug = "foo")]
 struct WrongPlaceField {
-    #[suggestion = "this is the wrong kind of attribute"]
+    #[suggestion = "bar"]
     //~^ ERROR `#[suggestion = ...]` is not a valid `SessionDiagnostic` field attribute
     sp: Span,
 }
@@ -154,7 +154,7 @@ struct ErrorWithMessageAppliedToField {
 #[derive(SessionDiagnostic)]
 #[error(code = "E0123", slug = "foo")]
 struct ErrorWithNonexistentField {
-    #[suggestion(message = "This is a suggestion", code = "{name}")]
+    #[suggestion(message = "bar", code = "{name}")]
     //~^ ERROR `name` doesn't refer to a field on this type
     suggestion: (Span, Applicability),
 }
@@ -163,7 +163,7 @@ struct ErrorWithNonexistentField {
 //~^ ERROR invalid format string: expected `'}'`
 #[error(code = "E0123", slug = "foo")]
 struct ErrorMissingClosingBrace {
-    #[suggestion(message = "This is a suggestion", code = "{name")]
+    #[suggestion(message = "bar", code = "{name")]
     suggestion: (Span, Applicability),
     name: String,
     val: usize,
@@ -173,7 +173,7 @@ struct ErrorMissingClosingBrace {
 //~^ ERROR invalid format string: unmatched `}`
 #[error(code = "E0123", slug = "foo")]
 struct ErrorMissingOpeningBrace {
-    #[suggestion(message = "This is a suggestion", code = "name}")]
+    #[suggestion(message = "bar", code = "name}")]
     suggestion: (Span, Applicability),
     name: String,
     val: usize,
@@ -197,55 +197,54 @@ struct LabelOnNonSpan {
 #[derive(SessionDiagnostic)]
 #[error(code = "E0123", slug = "foo")]
 struct Suggest {
-    #[suggestion(message = "This is a suggestion", code = "This is the suggested code")]
-    #[suggestion_short(message = "This is a suggestion", code = "This is the suggested code")]
-    #[suggestion_hidden(message = "This is a suggestion", code = "This is the suggested code")]
-    #[suggestion_verbose(message = "This is a suggestion", code = "This is the suggested code")]
+    #[suggestion(message = "bar", code = "This is the suggested code")]
+    #[suggestion_short(message = "qux", code = "This is the suggested code")]
+    #[suggestion_hidden(message = "foobar", code = "This is the suggested code")]
+    #[suggestion_verbose(message = "fooqux", code = "This is the suggested code")]
     suggestion: (Span, Applicability),
 }
 
 #[derive(SessionDiagnostic)]
 #[error(code = "E0123", slug = "foo")]
 struct SuggestWithoutCode {
-    #[suggestion(message = "This is a suggestion")]
+    #[suggestion(message = "bar")]
     suggestion: (Span, Applicability),
 }
 
 #[derive(SessionDiagnostic)]
 #[error(code = "E0123", slug = "foo")]
 struct SuggestWithBadKey {
-    #[suggestion(nonsense = "This is nonsense")]
-    //~^ ERROR `nonsense` is not a valid key for `#[suggestion(...)]`
+    #[suggestion(nonsense = "bar")]
+    //~^ ERROR `#[suggestion(nonsense = ...)]` is not a valid `SessionDiagnostic` field attribute
     suggestion: (Span, Applicability),
 }
 
 #[derive(SessionDiagnostic)]
 #[error(code = "E0123", slug = "foo")]
 struct SuggestWithShorthandMsg {
-    #[suggestion(msg = "This is a suggestion")]
-    //~^ ERROR `msg` is not a valid key for `#[suggestion(...)]`
+    #[suggestion(msg = "bar")]
+    //~^ ERROR `#[suggestion(msg = ...)]` is not a valid `SessionDiagnostic` field attribute
     suggestion: (Span, Applicability),
 }
 
 #[derive(SessionDiagnostic)]
 #[error(code = "E0123", slug = "foo")]
 struct SuggestWithoutMsg {
-    #[suggestion(code = "This is suggested code")]
-    //~^ ERROR missing suggestion message
+    #[suggestion(code = "bar")]
     suggestion: (Span, Applicability),
 }
 
 #[derive(SessionDiagnostic)]
 #[error(code = "E0123", slug = "foo")]
 struct SuggestWithTypesSwapped {
-    #[suggestion(message = "This is a message", code = "This is suggested code")]
+    #[suggestion(message = "bar", code = "This is suggested code")]
     suggestion: (Applicability, Span),
 }
 
 #[derive(SessionDiagnostic)]
 #[error(code = "E0123", slug = "foo")]
 struct SuggestWithWrongTypeApplicabilityOnly {
-    #[suggestion(message = "This is a message", code = "This is suggested code")]
+    #[suggestion(message = "bar", code = "This is suggested code")]
     //~^ ERROR wrong field type for suggestion
     suggestion: Applicability,
 }
@@ -253,14 +252,14 @@ struct SuggestWithWrongTypeApplicabilityOnly {
 #[derive(SessionDiagnostic)]
 #[error(code = "E0123", slug = "foo")]
 struct SuggestWithSpanOnly {
-    #[suggestion(message = "This is a message", code = "This is suggested code")]
+    #[suggestion(message = "bar", code = "This is suggested code")]
     suggestion: Span,
 }
 
 #[derive(SessionDiagnostic)]
 #[error(code = "E0123", slug = "foo")]
 struct SuggestWithDuplicateSpanAndApplicability {
-    #[suggestion(message = "This is a message", code = "This is suggested code")]
+    #[suggestion(message = "bar", code = "This is suggested code")]
     //~^ ERROR type of field annotated with `#[suggestion(...)]` contains more than one `Span`
     suggestion: (Span, Span, Applicability),
 }
@@ -268,7 +267,7 @@ struct SuggestWithDuplicateSpanAndApplicability {
 #[derive(SessionDiagnostic)]
 #[error(code = "E0123", slug = "foo")]
 struct SuggestWithDuplicateApplicabilityAndSpan {
-    #[suggestion(message = "This is a message", code = "This is suggested code")]
+    #[suggestion(message = "bar", code = "This is suggested code")]
     //~^ ERROR type of field annotated with `#[suggestion(...)]` contains more than one
     suggestion: (Applicability, Applicability, Span),
 }
@@ -277,7 +276,7 @@ struct SuggestWithDuplicateApplicabilityAndSpan {
 #[error(code = "E0123", slug = "foo")]
 struct WrongKindOfAnnotation {
     #[label("bar")]
-    //~^ ERROR invalid annotation list `#[label(...)]`
+    //~^ ERROR `#[label(...)]` is not a valid `SessionDiagnostic` field attribute
     z: Span,
 }
 
@@ -286,7 +285,7 @@ struct WrongKindOfAnnotation {
 struct OptionsInErrors {
     #[label = "bar"]
     label: Option<Span>,
-    #[suggestion(message = "suggestion message")]
+    #[suggestion(message = "bar")]
     opt_sugg: Option<(Span, Applicability)>,
 }
 
@@ -300,7 +299,7 @@ struct MoveOutOfBorrowError<'tcx> {
     span: Span,
     #[label = "qux"]
     other_span: Span,
-    #[suggestion(message = "consider cloning here", code = "{name}.clone()")]
+    #[suggestion(message = "bar", code = "{name}.clone()")]
     opt_sugg: Option<(Span, Applicability)>,
 }
 
diff --git a/src/test/ui-fulldeps/session-derive-errors.stderr b/src/test/ui-fulldeps/session-derive-errors.stderr
index dc8e807cea3..a528ae1607f 100644
--- a/src/test/ui-fulldeps/session-derive-errors.stderr
+++ b/src/test/ui-fulldeps/session-derive-errors.stderr
@@ -79,8 +79,8 @@ LL | #[error(nonsense = 4, code = "E0123", slug = "foo")]
 error: `#[suggestion = ...]` is not a valid `SessionDiagnostic` field attribute
   --> $DIR/session-derive-errors.rs:86:5
    |
-LL |     #[suggestion = "this is the wrong kind of attribute"]
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     #[suggestion = "bar"]
+   |     ^^^^^^^^^^^^^^^^^^^^^
 
 error: `error` specified multiple times
   --> $DIR/session-derive-errors.rs:93:1
@@ -166,10 +166,10 @@ LL |     #[label = "bar"]
    |     ^^^^^^^^^^^^^^^^
 
 error: `name` doesn't refer to a field on this type
-  --> $DIR/session-derive-errors.rs:157:52
+  --> $DIR/session-derive-errors.rs:157:42
    |
-LL |     #[suggestion(message = "This is a suggestion", code = "{name}")]
-   |                                                    ^^^^^^^^^^^^^^^
+LL |     #[suggestion(message = "bar", code = "{name}")]
+   |                                          ^^^^^^^^
 
 error: invalid format string: expected `'}'` but string was terminated
   --> $DIR/session-derive-errors.rs:162:16
@@ -197,30 +197,22 @@ error: the `#[label = ...]` attribute can only be applied to fields of type `Spa
 LL |     #[label = "bar"]
    |     ^^^^^^^^^^^^^^^^
 
-error: `nonsense` is not a valid key for `#[suggestion(...)]`
+error: `#[suggestion(nonsense = ...)]` is not a valid `SessionDiagnostic` field attribute
   --> $DIR/session-derive-errors.rs:217:18
    |
-LL |     #[suggestion(nonsense = "This is nonsense")]
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     #[suggestion(nonsense = "bar")]
+   |                  ^^^^^^^^^^^^^^^^
 
-error: `msg` is not a valid key for `#[suggestion(...)]`
+error: `#[suggestion(msg = ...)]` is not a valid `SessionDiagnostic` field attribute
   --> $DIR/session-derive-errors.rs:225:18
    |
-LL |     #[suggestion(msg = "This is a suggestion")]
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: missing suggestion message
-  --> $DIR/session-derive-errors.rs:233:7
-   |
-LL |     #[suggestion(code = "This is suggested code")]
-   |       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: provide a suggestion message using `#[suggestion(message = "...")]`
+LL |     #[suggestion(msg = "bar")]
+   |                  ^^^^^^^^^^^
 
 error: wrong field type for suggestion
-  --> $DIR/session-derive-errors.rs:248:5
+  --> $DIR/session-derive-errors.rs:247:5
    |
-LL | /     #[suggestion(message = "This is a message", code = "This is suggested code")]
+LL | /     #[suggestion(message = "bar", code = "This is suggested code")]
 LL | |
 LL | |     suggestion: Applicability,
    | |_____________________________^
@@ -228,47 +220,47 @@ LL | |     suggestion: Applicability,
    = help: `#[suggestion(...)]` should be applied to fields of type `Span` or `(Span, Applicability)`
 
 error: type of field annotated with `#[suggestion(...)]` contains more than one `Span`
-  --> $DIR/session-derive-errors.rs:263:5
+  --> $DIR/session-derive-errors.rs:262:5
    |
-LL | /     #[suggestion(message = "This is a message", code = "This is suggested code")]
+LL | /     #[suggestion(message = "bar", code = "This is suggested code")]
 LL | |
 LL | |     suggestion: (Span, Span, Applicability),
    | |___________________________________________^
 
 error: type of field annotated with `#[suggestion(...)]` contains more than one Applicability
-  --> $DIR/session-derive-errors.rs:271:5
+  --> $DIR/session-derive-errors.rs:270:5
    |
-LL | /     #[suggestion(message = "This is a message", code = "This is suggested code")]
+LL | /     #[suggestion(message = "bar", code = "This is suggested code")]
 LL | |
 LL | |     suggestion: (Applicability, Applicability, Span),
    | |____________________________________________________^
 
-error: invalid annotation list `#[label(...)]`
-  --> $DIR/session-derive-errors.rs:279:7
+error: `#[label(...)]` is not a valid `SessionDiagnostic` field attribute
+  --> $DIR/session-derive-errors.rs:278:5
    |
 LL |     #[label("bar")]
-   |       ^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^
 
 error: `#[help]` must come after `#[error(..)]` or `#[warn(..)]`
-  --> $DIR/session-derive-errors.rs:400:1
+  --> $DIR/session-derive-errors.rs:399:1
    |
 LL | #[help]
    | ^^^^^^^
 
 error: `#[help = ...]` must come after `#[error(..)]` or `#[warn(..)]`
-  --> $DIR/session-derive-errors.rs:408:1
+  --> $DIR/session-derive-errors.rs:407:1
    |
 LL | #[help = "bar"]
    | ^^^^^^^^^^^^^^^
 
 error: `#[note]` must come after `#[error(..)]` or `#[warn(..)]`
-  --> $DIR/session-derive-errors.rs:416:1
+  --> $DIR/session-derive-errors.rs:415:1
    |
 LL | #[note]
    | ^^^^^^^
 
 error: `#[note = ...]` must come after `#[error(..)]` or `#[warn(..)]`
-  --> $DIR/session-derive-errors.rs:424:1
+  --> $DIR/session-derive-errors.rs:423:1
    |
 LL | #[note = "bar"]
    | ^^^^^^^^^^^^^^^
@@ -286,7 +278,7 @@ LL |     #[nonsense]
    |       ^^^^^^^^
 
 error[E0599]: no method named `into_diagnostic_arg` found for struct `Hello` in the current scope
-  --> $DIR/session-derive-errors.rs:323:10
+  --> $DIR/session-derive-errors.rs:322:10
    |
 LL | struct Hello {}
    | ------------ method `into_diagnostic_arg` not found for this
@@ -296,6 +288,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 38 previous errors
+error: aborting due to 37 previous errors
 
 For more information about this error, try `rustc --explain E0599`.