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>
This commit is contained in:
David Wood 2022-03-31 13:10:13 +01:00
parent b40ee88a28
commit 22685b9607
3 changed files with 141 additions and 123 deletions

View File

@ -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)?;
syn::Meta::List(syn::MetaList { path, nested, .. }) => {
let name = path.segments.last().unwrap().ident.to_string();
let name = name.as_ref();
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() });
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); })
}
}
}

View File

@ -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)>,
}

View File

@ -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`.