Skip to content

Commit a88717c

Browse files
committed
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>
1 parent 72dec56 commit a88717c

File tree

8 files changed

+140
-95
lines changed

8 files changed

+140
-95
lines changed

compiler/rustc_errors/src/diagnostic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ impl Diagnostic {
277277
///
278278
/// This span is *not* considered a ["primary span"][`MultiSpan`]; only
279279
/// the `Span` supplied when creating the diagnostic is primary.
280-
pub fn span_label<T: Into<String>>(&mut self, span: Span, label: T) -> &mut Self {
280+
pub fn span_label(&mut self, span: Span, label: impl Into<DiagnosticMessage>) -> &mut Self {
281281
self.span.push_span_label(span, label.into());
282282
self
283283
}

compiler/rustc_errors/src/diagnostic_builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
408408
/// the diagnostic was constructed. However, the label span is *not* considered a
409409
/// ["primary span"][`MultiSpan`]; only the `Span` supplied when creating the diagnostic is
410410
/// primary.
411-
pub fn span_label(&mut self, span: Span, label: impl Into<String>) -> &mut Self);
411+
pub fn span_label(&mut self, span: Span, label: impl Into<DiagnosticMessage>) -> &mut Self);
412412

413413
forward!(
414414
/// Labels all the given spans with the provided label.

compiler/rustc_macros/src/session_diagnostic.rs

Lines changed: 75 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,7 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> {
561561
) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> {
562562
let diag = &self.diag;
563563
let field_binding = &info.binding.binding;
564+
564565
let name = attr.path.segments.last().unwrap().ident.to_string();
565566
let name = name.as_str();
566567

@@ -573,46 +574,38 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> {
573574
Ok(quote! {})
574575
}
575576
"primary_span" => {
576-
if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
577-
return Ok(quote! {
578-
#diag.set_span(*#field_binding);
579-
});
580-
} else {
581-
throw_span_err!(
582-
attr.span().unwrap(),
583-
"the `#[primary_span]` attribute can only be applied to fields of type `Span`"
584-
);
585-
}
577+
self.report_error_if_not_applied_to_span(attr, info)?;
578+
Ok(quote! {
579+
#diag.set_span(*#field_binding);
580+
})
581+
}
582+
"label" => {
583+
self.report_error_if_not_applied_to_span(attr, info)?;
584+
Ok(self.add_subdiagnostic(field_binding, name, "label"))
586585
}
587586
other => throw_span_err!(
588587
attr.span().unwrap(),
589588
&format!("`#[{}]` is not a valid `SessionDiagnostic` field attribute", other)
590589
),
591590
},
592-
syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
593-
let formatted_str = self.build_format(&s.value(), attr.span());
594-
match name {
595-
"label" => {
596-
if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
597-
return Ok(quote! {
598-
#diag.span_label(*#field_binding, #formatted_str);
599-
});
600-
} else {
601-
throw_span_err!(
602-
attr.span().unwrap(),
603-
"the `#[label = ...]` attribute can only be applied to fields of type `Span`"
604-
);
605-
}
606-
}
607-
other => throw_span_err!(
608-
attr.span().unwrap(),
609-
&format!(
610-
"`#[{} = ...]` is not a valid `SessionDiagnostic` field attribute",
611-
other
612-
)
613-
),
591+
syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => match name {
592+
"label" => {
593+
self.report_error_if_not_applied_to_span(attr, info)?;
594+
Ok(self.add_subdiagnostic(field_binding, name, &s.value()))
614595
}
615-
}
596+
other => throw_span_err!(
597+
attr.span().unwrap(),
598+
&format!(
599+
"`#[{} = ...]` is not a valid `SessionDiagnostic` field attribute",
600+
other
601+
)
602+
),
603+
},
604+
syn::Meta::NameValue(_) => throw_span_err!(
605+
attr.span().unwrap(),
606+
&format!("`#[{} = ...]` is not a valid `SessionDiagnostic` field attribute", name),
607+
|diag| diag.help("value must be a string")
608+
),
616609
syn::Meta::List(list) => {
617610
match list.path.segments.iter().last().unwrap().ident.to_string().as_str() {
618611
suggestion_kind @ "suggestion"
@@ -681,7 +674,55 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> {
681674
),
682675
}
683676
}
684-
_ => panic!("unhandled meta kind"),
677+
}
678+
}
679+
680+
/// Reports an error if the field's type is not `Span`.
681+
fn report_error_if_not_applied_to_span(
682+
&self,
683+
attr: &syn::Attribute,
684+
info: FieldInfo<'_>,
685+
) -> Result<(), SessionDiagnosticDeriveError> {
686+
if !type_matches_path(&info.ty, &["rustc_span", "Span"]) {
687+
let name = attr.path.segments.last().unwrap().ident.to_string();
688+
let name = name.as_str();
689+
let meta = attr.parse_meta()?;
690+
691+
throw_span_err!(
692+
attr.span().unwrap(),
693+
&format!(
694+
"the `#[{}{}]` attribute can only be applied to fields of type `Span`",
695+
name,
696+
match meta {
697+
syn::Meta::Path(_) => "",
698+
syn::Meta::NameValue(_) => " = ...",
699+
syn::Meta::List(_) => "(...)",
700+
}
701+
)
702+
);
703+
}
704+
705+
Ok(())
706+
}
707+
708+
/// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug and
709+
/// `fluent_attr_identifier`.
710+
fn add_subdiagnostic(
711+
&self,
712+
field_binding: &proc_macro2::Ident,
713+
kind: &str,
714+
fluent_attr_identifier: &str,
715+
) -> proc_macro2::TokenStream {
716+
let diag = &self.diag;
717+
718+
let slug =
719+
self.slug.as_ref().map(|(slug, _)| slug.as_str()).unwrap_or_else(|| "missing-slug");
720+
let fn_name = format_ident!("span_{}", kind);
721+
quote! {
722+
#diag.#fn_name(
723+
*#field_binding,
724+
rustc_errors::DiagnosticMessage::fluent_attr(#slug, #fluent_attr_identifier)
725+
);
685726
}
686727
}
687728

compiler/rustc_typeck/src/errors.rs

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ use rustc_span::{symbol::Ident, Span, Symbol};
66
#[error(code = "E0062", slug = "typeck-field-multiply-specified-in-initializer")]
77
pub struct FieldMultiplySpecifiedInInitializer {
88
#[primary_span]
9-
#[label = "used more than once"]
9+
#[label]
1010
pub span: Span,
11-
#[label = "first use of `{ident}`"]
11+
#[label = "previous-use-label"]
1212
pub prev_span: Span,
1313
pub ident: Ident,
1414
}
@@ -17,7 +17,7 @@ pub struct FieldMultiplySpecifiedInInitializer {
1717
#[error(code = "E0092", slug = "typeck-unrecognized-atomic-operation")]
1818
pub struct UnrecognizedAtomicOperation<'a> {
1919
#[primary_span]
20-
#[label = "unrecognized atomic operation"]
20+
#[label]
2121
pub span: Span,
2222
pub op: &'a str,
2323
}
@@ -26,7 +26,7 @@ pub struct UnrecognizedAtomicOperation<'a> {
2626
#[error(code = "E0094", slug = "typeck-wrong-number-of-generic-arguments-to-intrinsic")]
2727
pub struct WrongNumberOfGenericArgumentsToIntrinsic<'a> {
2828
#[primary_span]
29-
#[label = "expected {expected} {descr} parameter{expected_pluralize}"]
29+
#[label]
3030
pub span: Span,
3131
pub found: usize,
3232
pub expected: usize,
@@ -38,7 +38,7 @@ pub struct WrongNumberOfGenericArgumentsToIntrinsic<'a> {
3838
#[error(code = "E0093", slug = "typeck-unrecognized-intrinsic-function")]
3939
pub struct UnrecognizedIntrinsicFunction {
4040
#[primary_span]
41-
#[label = "unrecognized intrinsic"]
41+
#[label]
4242
pub span: Span,
4343
pub name: Symbol,
4444
}
@@ -47,9 +47,9 @@ pub struct UnrecognizedIntrinsicFunction {
4747
#[error(code = "E0195", slug = "typeck-lifetimes-or-bounds-mismatch-on-trait")]
4848
pub struct LifetimesOrBoundsMismatchOnTrait {
4949
#[primary_span]
50-
#[label = "lifetimes do not match {item_kind} in trait"]
50+
#[label]
5151
pub span: Span,
52-
#[label = "lifetimes in impl do not match this {item_kind} in trait"]
52+
#[label = "generics-label"]
5353
pub generics_span: Option<Span>,
5454
pub item_kind: &'static str,
5555
pub ident: Ident,
@@ -59,7 +59,7 @@ pub struct LifetimesOrBoundsMismatchOnTrait {
5959
#[error(code = "E0120", slug = "typeck-drop-impl-on-wrong-item")]
6060
pub struct DropImplOnWrongItem {
6161
#[primary_span]
62-
#[label = "must be a struct, enum, or union"]
62+
#[label]
6363
pub span: Span,
6464
}
6565

@@ -68,17 +68,17 @@ pub struct DropImplOnWrongItem {
6868
pub struct FieldAlreadyDeclared {
6969
pub field_name: Ident,
7070
#[primary_span]
71-
#[label = "field already declared"]
71+
#[label]
7272
pub span: Span,
73-
#[label = "`{field_name}` first declared here"]
73+
#[label = "previous-decl-label"]
7474
pub prev_span: Span,
7575
}
7676

7777
#[derive(SessionDiagnostic)]
7878
#[error(code = "E0184", slug = "typeck-copy-impl-on-type-with-dtor")]
7979
pub struct CopyImplOnTypeWithDtor {
8080
#[primary_span]
81-
#[label = "Copy not allowed on types with destructors"]
81+
#[label]
8282
pub span: Span,
8383
}
8484

@@ -93,7 +93,7 @@ pub struct MultipleRelaxedDefaultBounds {
9393
#[error(code = "E0206", slug = "typeck-copy-impl-on-non-adt")]
9494
pub struct CopyImplOnNonAdt {
9595
#[primary_span]
96-
#[label = "type is not a structure or enumeration"]
96+
#[label]
9797
pub span: Span,
9898
}
9999

@@ -115,7 +115,7 @@ pub struct AmbiguousLifetimeBound {
115115
#[error(code = "E0229", slug = "typeck-assoc-type-binding-not-allowed")]
116116
pub struct AssocTypeBindingNotAllowed {
117117
#[primary_span]
118-
#[label = "associated type not allowed here"]
118+
#[label]
119119
pub span: Span,
120120
}
121121

@@ -130,7 +130,7 @@ pub struct FunctionalRecordUpdateOnNonStruct {
130130
#[error(code = "E0516", slug = "typeck-typeof-reserved-keyword-used")]
131131
pub struct TypeofReservedKeywordUsed {
132132
#[primary_span]
133-
#[label = "reserved keyword"]
133+
#[label]
134134
pub span: Span,
135135
}
136136

@@ -139,9 +139,9 @@ pub struct TypeofReservedKeywordUsed {
139139
pub struct ReturnStmtOutsideOfFnBody {
140140
#[primary_span]
141141
pub span: Span,
142-
#[label = "the return is part of this body..."]
142+
#[label = "encl-body-label"]
143143
pub encl_body_span: Option<Span>,
144-
#[label = "...not the enclosing function body"]
144+
#[label = "encl-fn-label"]
145145
pub encl_fn_span: Option<Span>,
146146
}
147147

@@ -171,9 +171,9 @@ pub struct MethodCallOnUnknownType {
171171
#[error(code = "E0719", slug = "typeck-value-of-associated-struct-already-specified")]
172172
pub struct ValueOfAssociatedStructAlreadySpecified {
173173
#[primary_span]
174-
#[label = "re-bound here"]
174+
#[label]
175175
pub span: Span,
176-
#[label = "`{item_name}` bound here first"]
176+
#[label = "previous-bound-label"]
177177
pub prev_span: Span,
178178
pub item_name: Ident,
179179
pub def_path: String,
@@ -183,6 +183,6 @@ pub struct ValueOfAssociatedStructAlreadySpecified {
183183
#[error(code = "E0745", slug = "typeck-address-of-temporary-taken")]
184184
pub struct AddressOfTemporaryTaken {
185185
#[primary_span]
186-
#[label = "temporary value"]
186+
#[label]
187187
pub span: Span,
188188
}

0 commit comments

Comments
 (0)