Skip to content

Commit 22685b9

Browse files
committed
macros: support translatable suggestions
Extends support for generating `DiagnosticMessage::FluentIdentifier` messages from `SessionDiagnostic` derive to `#[suggestion]`. Signed-off-by: David Wood <[email protected]>
1 parent b40ee88 commit 22685b9

File tree

3 files changed

+141
-123
lines changed

3 files changed

+141
-123
lines changed

compiler/rustc_macros/src/session_diagnostic.rs

Lines changed: 93 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,7 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> {
594594
info: FieldInfo<'_>,
595595
) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> {
596596
let diag = &self.diag;
597+
let span = attr.span().unwrap();
597598
let field_binding = &info.binding.binding;
598599

599600
let name = attr.path.segments.last().unwrap().ident.to_string();
@@ -618,7 +619,7 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> {
618619
Ok(self.add_subdiagnostic(field_binding, name, name))
619620
}
620621
other => throw_span_err!(
621-
attr.span().unwrap(),
622+
span,
622623
&format!("`#[{}]` is not a valid `SessionDiagnostic` field attribute", other)
623624
),
624625
},
@@ -628,85 +629,111 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> {
628629
Ok(self.add_subdiagnostic(field_binding, name, &s.value()))
629630
}
630631
other => throw_span_err!(
631-
attr.span().unwrap(),
632+
span,
632633
&format!(
633634
"`#[{} = ...]` is not a valid `SessionDiagnostic` field attribute",
634635
other
635636
)
636637
),
637638
},
638639
syn::Meta::NameValue(_) => throw_span_err!(
639-
attr.span().unwrap(),
640+
span,
640641
&format!("`#[{} = ...]` is not a valid `SessionDiagnostic` field attribute", name),
641642
|diag| diag.help("value must be a string")
642643
),
643-
syn::Meta::List(list) => {
644-
match list.path.segments.iter().last().unwrap().ident.to_string().as_str() {
645-
suggestion_kind @ "suggestion"
646-
| suggestion_kind @ "suggestion_short"
647-
| suggestion_kind @ "suggestion_hidden"
648-
| suggestion_kind @ "suggestion_verbose" => {
649-
let (span, applicability) = self.span_and_applicability_of_ty(info)?;
650-
651-
let mut msg = None;
652-
let mut code = None;
653-
654-
for arg in list.nested.iter() {
655-
if let syn::NestedMeta::Meta(syn::Meta::NameValue(arg_name_value)) = arg
656-
{
657-
if let syn::MetaNameValue { lit: syn::Lit::Str(s), .. } =
658-
arg_name_value
659-
{
660-
let name = arg_name_value
661-
.path
662-
.segments
663-
.last()
664-
.unwrap()
665-
.ident
666-
.to_string();
667-
let name = name.as_str();
668-
let formatted_str = self.build_format(&s.value(), arg.span());
669-
match name {
670-
"message" => {
671-
msg = Some(formatted_str);
672-
}
673-
"code" => {
674-
code = Some(formatted_str);
675-
}
676-
other => throw_span_err!(
677-
arg.span().unwrap(),
678-
&format!(
679-
"`{}` is not a valid key for `#[suggestion(...)]`",
680-
other
681-
)
682-
),
683-
}
684-
}
685-
}
686-
}
687-
let msg = if let Some(msg) = msg {
688-
quote!(#msg.as_str())
689-
} else {
690-
throw_span_err!(
691-
list.span().unwrap(),
692-
"missing suggestion message",
693-
|diag| {
694-
diag.help("provide a suggestion message using `#[suggestion(message = \"...\")]`")
695-
}
696-
);
697-
};
698-
let code = code.unwrap_or_else(|| quote! { String::new() });
644+
syn::Meta::List(syn::MetaList { path, nested, .. }) => {
645+
let name = path.segments.last().unwrap().ident.to_string();
646+
let name = name.as_ref();
699647

700-
let suggestion_method = format_ident!("span_{}", suggestion_kind);
701-
return Ok(quote! {
702-
#diag.#suggestion_method(#span, #msg, #code, #applicability);
703-
});
704-
}
648+
match name {
649+
"suggestion" | "suggestion_short" | "suggestion_hidden"
650+
| "suggestion_verbose" => (),
705651
other => throw_span_err!(
706-
list.span().unwrap(),
707-
&format!("invalid annotation list `#[{}(...)]`", other)
652+
span,
653+
&format!(
654+
"`#[{}(...)]` is not a valid `SessionDiagnostic` field attribute",
655+
other
656+
)
708657
),
658+
};
659+
660+
let (span_, applicability) = self.span_and_applicability_of_ty(info)?;
661+
662+
let mut msg = None;
663+
let mut code = None;
664+
665+
for attr in nested {
666+
let meta = match attr {
667+
syn::NestedMeta::Meta(meta) => meta,
668+
syn::NestedMeta::Lit(_) => throw_span_err!(
669+
span,
670+
&format!(
671+
"`#[{}(\"...\")]` is not a valid `SessionDiagnostic` field attribute",
672+
name
673+
)
674+
),
675+
};
676+
677+
let span = meta.span().unwrap();
678+
let nested_name = meta.path().segments.last().unwrap().ident.to_string();
679+
let nested_name = nested_name.as_str();
680+
681+
match meta {
682+
syn::Meta::NameValue(syn::MetaNameValue {
683+
lit: syn::Lit::Str(s), ..
684+
}) => match nested_name {
685+
"message" => {
686+
msg = Some(s.value());
687+
}
688+
"code" => {
689+
let formatted_str = self.build_format(&s.value(), s.span());
690+
code = Some(formatted_str);
691+
}
692+
other => throw_span_err!(
693+
span,
694+
&format!(
695+
"`#[{}({} = ...)]` is not a valid `SessionDiagnostic` field attribute",
696+
name, other
697+
)
698+
),
699+
},
700+
syn::Meta::NameValue(..) => throw_span_err!(
701+
span,
702+
&format!(
703+
"`#[{}({} = ...)]` is not a valid `SessionDiagnostic` struct attribute",
704+
name, nested_name
705+
),
706+
|diag| diag.help("value must be a string")
707+
),
708+
syn::Meta::Path(..) => throw_span_err!(
709+
span,
710+
&format!(
711+
"`#[{}({})]` is not a valid `SessionDiagnostic` struct attribute",
712+
name, nested_name
713+
)
714+
),
715+
syn::Meta::List(..) => throw_span_err!(
716+
span,
717+
&format!(
718+
"`#[{}({}(...))]` is not a valid `SessionDiagnostic` struct attribute",
719+
name, nested_name
720+
)
721+
),
722+
}
709723
}
724+
725+
let method = format_ident!("span_{}", name);
726+
727+
let slug = self
728+
.slug
729+
.as_ref()
730+
.map(|(slug, _)| slug.as_str())
731+
.unwrap_or_else(|| "missing-slug");
732+
let msg = msg.as_deref().unwrap_or("suggestion");
733+
let msg = quote! { rustc_errors::DiagnosticMessage::fluent_attr(#slug, #msg) };
734+
let code = code.unwrap_or_else(|| quote! { String::new() });
735+
736+
Ok(quote! { #diag.#method(#span_, #msg, #code, #applicability); })
710737
}
711738
}
712739
}

src/test/ui-fulldeps/session-derive-errors.rs

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ struct InvalidNestedStructAttr3 {}
8383
#[derive(SessionDiagnostic)]
8484
#[error(code = "E0123", slug = "foo")]
8585
struct WrongPlaceField {
86-
#[suggestion = "this is the wrong kind of attribute"]
86+
#[suggestion = "bar"]
8787
//~^ ERROR `#[suggestion = ...]` is not a valid `SessionDiagnostic` field attribute
8888
sp: Span,
8989
}
@@ -154,7 +154,7 @@ struct ErrorWithMessageAppliedToField {
154154
#[derive(SessionDiagnostic)]
155155
#[error(code = "E0123", slug = "foo")]
156156
struct ErrorWithNonexistentField {
157-
#[suggestion(message = "This is a suggestion", code = "{name}")]
157+
#[suggestion(message = "bar", code = "{name}")]
158158
//~^ ERROR `name` doesn't refer to a field on this type
159159
suggestion: (Span, Applicability),
160160
}
@@ -163,7 +163,7 @@ struct ErrorWithNonexistentField {
163163
//~^ ERROR invalid format string: expected `'}'`
164164
#[error(code = "E0123", slug = "foo")]
165165
struct ErrorMissingClosingBrace {
166-
#[suggestion(message = "This is a suggestion", code = "{name")]
166+
#[suggestion(message = "bar", code = "{name")]
167167
suggestion: (Span, Applicability),
168168
name: String,
169169
val: usize,
@@ -173,7 +173,7 @@ struct ErrorMissingClosingBrace {
173173
//~^ ERROR invalid format string: unmatched `}`
174174
#[error(code = "E0123", slug = "foo")]
175175
struct ErrorMissingOpeningBrace {
176-
#[suggestion(message = "This is a suggestion", code = "name}")]
176+
#[suggestion(message = "bar", code = "name}")]
177177
suggestion: (Span, Applicability),
178178
name: String,
179179
val: usize,
@@ -197,78 +197,77 @@ struct LabelOnNonSpan {
197197
#[derive(SessionDiagnostic)]
198198
#[error(code = "E0123", slug = "foo")]
199199
struct Suggest {
200-
#[suggestion(message = "This is a suggestion", code = "This is the suggested code")]
201-
#[suggestion_short(message = "This is a suggestion", code = "This is the suggested code")]
202-
#[suggestion_hidden(message = "This is a suggestion", code = "This is the suggested code")]
203-
#[suggestion_verbose(message = "This is a suggestion", code = "This is the suggested code")]
200+
#[suggestion(message = "bar", code = "This is the suggested code")]
201+
#[suggestion_short(message = "qux", code = "This is the suggested code")]
202+
#[suggestion_hidden(message = "foobar", code = "This is the suggested code")]
203+
#[suggestion_verbose(message = "fooqux", code = "This is the suggested code")]
204204
suggestion: (Span, Applicability),
205205
}
206206

207207
#[derive(SessionDiagnostic)]
208208
#[error(code = "E0123", slug = "foo")]
209209
struct SuggestWithoutCode {
210-
#[suggestion(message = "This is a suggestion")]
210+
#[suggestion(message = "bar")]
211211
suggestion: (Span, Applicability),
212212
}
213213

214214
#[derive(SessionDiagnostic)]
215215
#[error(code = "E0123", slug = "foo")]
216216
struct SuggestWithBadKey {
217-
#[suggestion(nonsense = "This is nonsense")]
218-
//~^ ERROR `nonsense` is not a valid key for `#[suggestion(...)]`
217+
#[suggestion(nonsense = "bar")]
218+
//~^ ERROR `#[suggestion(nonsense = ...)]` is not a valid `SessionDiagnostic` field attribute
219219
suggestion: (Span, Applicability),
220220
}
221221

222222
#[derive(SessionDiagnostic)]
223223
#[error(code = "E0123", slug = "foo")]
224224
struct SuggestWithShorthandMsg {
225-
#[suggestion(msg = "This is a suggestion")]
226-
//~^ ERROR `msg` is not a valid key for `#[suggestion(...)]`
225+
#[suggestion(msg = "bar")]
226+
//~^ ERROR `#[suggestion(msg = ...)]` is not a valid `SessionDiagnostic` field attribute
227227
suggestion: (Span, Applicability),
228228
}
229229

230230
#[derive(SessionDiagnostic)]
231231
#[error(code = "E0123", slug = "foo")]
232232
struct SuggestWithoutMsg {
233-
#[suggestion(code = "This is suggested code")]
234-
//~^ ERROR missing suggestion message
233+
#[suggestion(code = "bar")]
235234
suggestion: (Span, Applicability),
236235
}
237236

238237
#[derive(SessionDiagnostic)]
239238
#[error(code = "E0123", slug = "foo")]
240239
struct SuggestWithTypesSwapped {
241-
#[suggestion(message = "This is a message", code = "This is suggested code")]
240+
#[suggestion(message = "bar", code = "This is suggested code")]
242241
suggestion: (Applicability, Span),
243242
}
244243

245244
#[derive(SessionDiagnostic)]
246245
#[error(code = "E0123", slug = "foo")]
247246
struct SuggestWithWrongTypeApplicabilityOnly {
248-
#[suggestion(message = "This is a message", code = "This is suggested code")]
247+
#[suggestion(message = "bar", code = "This is suggested code")]
249248
//~^ ERROR wrong field type for suggestion
250249
suggestion: Applicability,
251250
}
252251

253252
#[derive(SessionDiagnostic)]
254253
#[error(code = "E0123", slug = "foo")]
255254
struct SuggestWithSpanOnly {
256-
#[suggestion(message = "This is a message", code = "This is suggested code")]
255+
#[suggestion(message = "bar", code = "This is suggested code")]
257256
suggestion: Span,
258257
}
259258

260259
#[derive(SessionDiagnostic)]
261260
#[error(code = "E0123", slug = "foo")]
262261
struct SuggestWithDuplicateSpanAndApplicability {
263-
#[suggestion(message = "This is a message", code = "This is suggested code")]
262+
#[suggestion(message = "bar", code = "This is suggested code")]
264263
//~^ ERROR type of field annotated with `#[suggestion(...)]` contains more than one `Span`
265264
suggestion: (Span, Span, Applicability),
266265
}
267266

268267
#[derive(SessionDiagnostic)]
269268
#[error(code = "E0123", slug = "foo")]
270269
struct SuggestWithDuplicateApplicabilityAndSpan {
271-
#[suggestion(message = "This is a message", code = "This is suggested code")]
270+
#[suggestion(message = "bar", code = "This is suggested code")]
272271
//~^ ERROR type of field annotated with `#[suggestion(...)]` contains more than one
273272
suggestion: (Applicability, Applicability, Span),
274273
}
@@ -277,7 +276,7 @@ struct SuggestWithDuplicateApplicabilityAndSpan {
277276
#[error(code = "E0123", slug = "foo")]
278277
struct WrongKindOfAnnotation {
279278
#[label("bar")]
280-
//~^ ERROR invalid annotation list `#[label(...)]`
279+
//~^ ERROR `#[label(...)]` is not a valid `SessionDiagnostic` field attribute
281280
z: Span,
282281
}
283282

@@ -286,7 +285,7 @@ struct WrongKindOfAnnotation {
286285
struct OptionsInErrors {
287286
#[label = "bar"]
288287
label: Option<Span>,
289-
#[suggestion(message = "suggestion message")]
288+
#[suggestion(message = "bar")]
290289
opt_sugg: Option<(Span, Applicability)>,
291290
}
292291

@@ -300,7 +299,7 @@ struct MoveOutOfBorrowError<'tcx> {
300299
span: Span,
301300
#[label = "qux"]
302301
other_span: Span,
303-
#[suggestion(message = "consider cloning here", code = "{name}.clone()")]
302+
#[suggestion(message = "bar", code = "{name}.clone()")]
304303
opt_sugg: Option<(Span, Applicability)>,
305304
}
306305

0 commit comments

Comments
 (0)