]> git.lizzy.rs Git - rust.git/commitdiff
macros: support translatable labels
authorDavid Wood <david.wood@huawei.com>
Thu, 31 Mar 2022 09:24:46 +0000 (10:24 +0100)
committerDavid Wood <david.wood@huawei.com>
Tue, 5 Apr 2022 06:01:03 +0000 (07:01 +0100)
Extends support for generating `DiagnosticMessage::FluentIdentifier`
messages from `SessionDiagnostic` derive to `#[label]`.

Signed-off-by: David Wood <david.wood@huawei.com>
compiler/rustc_errors/src/diagnostic.rs
compiler/rustc_errors/src/diagnostic_builder.rs
compiler/rustc_macros/src/session_diagnostic.rs
compiler/rustc_typeck/src/errors.rs
src/test/ui-fulldeps/session-derive-errors.rs
src/test/ui-fulldeps/session-derive-errors.stderr
src/test/ui/error-codes/E0184.stderr
src/test/ui/exclusive-drop-and-copy.stderr

index 74abffa7dfac8e533f5a621d84a8da421f2d33af..ecb3cdd627cec71bf6c1d8f1ce9522f9070188ba 100644 (file)
@@ -277,7 +277,7 @@ pub fn downgrade_to_delayed_bug(&mut self) -> &mut Self {
     ///
     /// This span is *not* considered a ["primary span"][`MultiSpan`]; only
     /// the `Span` supplied when creating the diagnostic is primary.
-    pub fn span_label<T: Into<String>>(&mut self, span: Span, label: T) -> &mut Self {
+    pub fn span_label(&mut self, span: Span, label: impl Into<DiagnosticMessage>) -> &mut Self {
         self.span.push_span_label(span, label.into());
         self
     }
index 2cee1354cc74b1e9c923c1541e791a18e8d05418..74e0f7429461389194ae4f943e251750fafed9ac 100644 (file)
@@ -408,7 +408,7 @@ pub fn downgrade_to_delayed_bug(&mut self,) -> &mut Self
     /// the diagnostic was constructed. However, the label span is *not* considered a
     /// ["primary span"][`MultiSpan`]; only the `Span` supplied when creating the diagnostic is
     /// primary.
-    pub fn span_label(&mut self, span: Span, label: impl Into<String>) -> &mut Self);
+    pub fn span_label(&mut self, span: Span, label: impl Into<DiagnosticMessage>) -> &mut Self);
 
     forward!(
     /// Labels all the given spans with the provided label.
index 79086931d5fee6d91af3447147b3a6be74ef1644..efbffabbd03454244ab6947b0be1c9bec64ea630 100644 (file)
@@ -561,6 +561,7 @@ fn generate_non_option_field_code(
     ) -> 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 @@ fn generate_non_option_field_code(
                     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 @@ fn generate_non_option_field_code(
                     ),
                 }
             }
-            _ => 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)
+            );
         }
     }
 
index 7531a231e3951daaa0970e73f0ef82b5f5e0dad1..72733e087c282e553651e5baeb1de7e359800b51 100644 (file)
@@ -6,9 +6,9 @@
 #[error(code = "E0062", slug = "typeck-field-multiply-specified-in-initializer")]
 pub struct FieldMultiplySpecifiedInInitializer {
     #[primary_span]
-    #[label = "used more than once"]
+    #[label]
     pub span: Span,
-    #[label = "first use of `{ident}`"]
+    #[label = "previous-use-label"]
     pub prev_span: Span,
     pub ident: Ident,
 }
@@ -17,7 +17,7 @@ pub struct FieldMultiplySpecifiedInInitializer {
 #[error(code = "E0092", slug = "typeck-unrecognized-atomic-operation")]
 pub struct UnrecognizedAtomicOperation<'a> {
     #[primary_span]
-    #[label = "unrecognized atomic operation"]
+    #[label]
     pub span: Span,
     pub op: &'a str,
 }
@@ -26,7 +26,7 @@ pub struct UnrecognizedAtomicOperation<'a> {
 #[error(code = "E0094", slug = "typeck-wrong-number-of-generic-arguments-to-intrinsic")]
 pub struct WrongNumberOfGenericArgumentsToIntrinsic<'a> {
     #[primary_span]
-    #[label = "expected {expected} {descr} parameter{expected_pluralize}"]
+    #[label]
     pub span: Span,
     pub found: usize,
     pub expected: usize,
@@ -38,7 +38,7 @@ pub struct WrongNumberOfGenericArgumentsToIntrinsic<'a> {
 #[error(code = "E0093", slug = "typeck-unrecognized-intrinsic-function")]
 pub struct UnrecognizedIntrinsicFunction {
     #[primary_span]
-    #[label = "unrecognized intrinsic"]
+    #[label]
     pub span: Span,
     pub name: Symbol,
 }
@@ -47,9 +47,9 @@ pub struct UnrecognizedIntrinsicFunction {
 #[error(code = "E0195", slug = "typeck-lifetimes-or-bounds-mismatch-on-trait")]
 pub struct LifetimesOrBoundsMismatchOnTrait {
     #[primary_span]
-    #[label = "lifetimes do not match {item_kind} in trait"]
+    #[label]
     pub span: Span,
-    #[label = "lifetimes in impl do not match this {item_kind} in trait"]
+    #[label = "generics-label"]
     pub generics_span: Option<Span>,
     pub item_kind: &'static str,
     pub ident: Ident,
@@ -59,7 +59,7 @@ pub struct LifetimesOrBoundsMismatchOnTrait {
 #[error(code = "E0120", slug = "typeck-drop-impl-on-wrong-item")]
 pub struct DropImplOnWrongItem {
     #[primary_span]
-    #[label = "must be a struct, enum, or union"]
+    #[label]
     pub span: Span,
 }
 
@@ -68,9 +68,9 @@ pub struct DropImplOnWrongItem {
 pub struct FieldAlreadyDeclared {
     pub field_name: Ident,
     #[primary_span]
-    #[label = "field already declared"]
+    #[label]
     pub span: Span,
-    #[label = "`{field_name}` first declared here"]
+    #[label = "previous-decl-label"]
     pub prev_span: Span,
 }
 
@@ -78,7 +78,7 @@ pub struct FieldAlreadyDeclared {
 #[error(code = "E0184", slug = "typeck-copy-impl-on-type-with-dtor")]
 pub struct CopyImplOnTypeWithDtor {
     #[primary_span]
-    #[label = "Copy not allowed on types with destructors"]
+    #[label]
     pub span: Span,
 }
 
@@ -93,7 +93,7 @@ pub struct MultipleRelaxedDefaultBounds {
 #[error(code = "E0206", slug = "typeck-copy-impl-on-non-adt")]
 pub struct CopyImplOnNonAdt {
     #[primary_span]
-    #[label = "type is not a structure or enumeration"]
+    #[label]
     pub span: Span,
 }
 
@@ -115,7 +115,7 @@ pub struct AmbiguousLifetimeBound {
 #[error(code = "E0229", slug = "typeck-assoc-type-binding-not-allowed")]
 pub struct AssocTypeBindingNotAllowed {
     #[primary_span]
-    #[label = "associated type not allowed here"]
+    #[label]
     pub span: Span,
 }
 
@@ -130,7 +130,7 @@ pub struct FunctionalRecordUpdateOnNonStruct {
 #[error(code = "E0516", slug = "typeck-typeof-reserved-keyword-used")]
 pub struct TypeofReservedKeywordUsed {
     #[primary_span]
-    #[label = "reserved keyword"]
+    #[label]
     pub span: Span,
 }
 
@@ -139,9 +139,9 @@ pub struct TypeofReservedKeywordUsed {
 pub struct ReturnStmtOutsideOfFnBody {
     #[primary_span]
     pub span: Span,
-    #[label = "the return is part of this body..."]
+    #[label = "encl-body-label"]
     pub encl_body_span: Option<Span>,
-    #[label = "...not the enclosing function body"]
+    #[label = "encl-fn-label"]
     pub encl_fn_span: Option<Span>,
 }
 
@@ -171,9 +171,9 @@ pub struct MethodCallOnUnknownType {
 #[error(code = "E0719", slug = "typeck-value-of-associated-struct-already-specified")]
 pub struct ValueOfAssociatedStructAlreadySpecified {
     #[primary_span]
-    #[label = "re-bound here"]
+    #[label]
     pub span: Span,
-    #[label = "`{item_name}` bound here first"]
+    #[label = "previous-bound-label"]
     pub prev_span: Span,
     pub item_name: Ident,
     pub def_path: String,
@@ -183,6 +183,6 @@ pub struct ValueOfAssociatedStructAlreadySpecified {
 #[error(code = "E0745", slug = "typeck-address-of-temporary-taken")]
 pub struct AddressOfTemporaryTaken {
     #[primary_span]
-    #[label = "temporary value"]
+    #[label]
     pub span: Span,
 }
index 63912f0ad1326e8f0b3ce609235cb51817fa1357..68f24f1f68589159ddcb0dfee42318009a2fd5bf 100644 (file)
@@ -139,14 +139,14 @@ struct InvalidPathFieldAttr {
 #[error(code = "E0123", slug = "foo")]
 struct ErrorWithField {
     name: String,
-    #[label = "This error has a field, and references {name}"]
+    #[label = "bar"]
     span: Span,
 }
 
 #[derive(SessionDiagnostic)]
 #[error(code = "E0123", slug = "foo")]
 struct ErrorWithMessageAppliedToField {
-    #[label = "this message is applied to a String field"]
+    #[label = "bar"]
     //~^ ERROR the `#[label = ...]` attribute can only be applied to fields of type `Span`
     name: String,
 }
@@ -154,27 +154,27 @@ struct ErrorWithMessageAppliedToField {
 #[derive(SessionDiagnostic)]
 #[error(code = "E0123", slug = "foo")]
 struct ErrorWithNonexistentField {
-    #[label = "This error has a field, and references {name}"]
+    #[suggestion(message = "This is a suggestion", code = "{name}")]
     //~^ ERROR `name` doesn't refer to a field on this type
-    foo: Span,
+    suggestion: (Span, Applicability),
 }
 
 #[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
 //~^ ERROR invalid format string: expected `'}'`
+#[error(code = "E0123", slug = "foo")]
 struct ErrorMissingClosingBrace {
-    #[label = "This is missing a closing brace: {name"]
-    foo: Span,
+    #[suggestion(message = "This is a suggestion", code = "{name")]
+    suggestion: (Span, Applicability),
     name: String,
     val: usize,
 }
 
 #[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
 //~^ ERROR invalid format string: unmatched `}`
+#[error(code = "E0123", slug = "foo")]
 struct ErrorMissingOpeningBrace {
-    #[label = "This is missing an opening brace: name}"]
-    foo: Span,
+    #[suggestion(message = "This is a suggestion", code = "name}")]
+    suggestion: (Span, Applicability),
     name: String,
     val: usize,
 }
@@ -182,14 +182,14 @@ struct ErrorMissingOpeningBrace {
 #[derive(SessionDiagnostic)]
 #[error(code = "E0123", slug = "foo")]
 struct LabelOnSpan {
-    #[label = "See here"]
+    #[label = "bar"]
     sp: Span,
 }
 
 #[derive(SessionDiagnostic)]
 #[error(code = "E0123", slug = "foo")]
 struct LabelOnNonSpan {
-    #[label = "See here"]
+    #[label = "bar"]
     //~^ ERROR the `#[label = ...]` attribute can only be applied to fields of type `Span`
     id: u32,
 }
@@ -276,7 +276,7 @@ struct SuggestWithDuplicateApplicabilityAndSpan {
 #[derive(SessionDiagnostic)]
 #[error(code = "E0123", slug = "foo")]
 struct WrongKindOfAnnotation {
-    #[label("wrong kind of annotation for label")]
+    #[label("bar")]
     //~^ ERROR invalid annotation list `#[label(...)]`
     z: Span,
 }
@@ -284,7 +284,7 @@ struct WrongKindOfAnnotation {
 #[derive(SessionDiagnostic)]
 #[error(code = "E0123", slug = "foo")]
 struct OptionsInErrors {
-    #[label = "Label message"]
+    #[label = "bar"]
     label: Option<Span>,
     #[suggestion(message = "suggestion message")]
     opt_sugg: Option<(Span, Applicability)>,
@@ -296,9 +296,9 @@ struct MoveOutOfBorrowError<'tcx> {
     name: Ident,
     ty: Ty<'tcx>,
     #[primary_span]
-    #[label = "cannot move out of borrow"]
+    #[label = "bar"]
     span: Span,
-    #[label = "`{ty}` first borrowed here"]
+    #[label = "qux"]
     other_span: Span,
     #[suggestion(message = "consider cloning here", code = "{name}.clone()")]
     opt_sugg: Option<(Span, Applicability)>,
@@ -307,7 +307,15 @@ struct MoveOutOfBorrowError<'tcx> {
 #[derive(SessionDiagnostic)]
 #[error(code = "E0123", slug = "foo")]
 struct ErrorWithLifetime<'a> {
-    #[label = "Some message that references {name}"]
+    #[label = "bar"]
+    span: Span,
+    name: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct ErrorWithDefaultLabelAttr<'a> {
+    #[label]
     span: Span,
     name: &'a str,
 }
index 09b8050bf51b9bcbd75643d780d47db706fae048..902bc785ce7cc8cd6cf658256f14d21f8bf111ea 100644 (file)
@@ -162,35 +162,31 @@ LL |     #[nonsense]
 error: the `#[label = ...]` attribute can only be applied to fields of type `Span`
   --> $DIR/session-derive-errors.rs:149:5
    |
-LL |     #[label = "this message is applied to a String field"]
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     #[label = "bar"]
+   |     ^^^^^^^^^^^^^^^^
 
 error: `name` doesn't refer to a field on this type
-  --> $DIR/session-derive-errors.rs:157:5
+  --> $DIR/session-derive-errors.rs:157:52
    |
-LL |     #[label = "This error has a field, and references {name}"]
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     #[suggestion(message = "This is a suggestion", code = "{name}")]
+   |                                                    ^^^^^^^^^^^^^^^
 
 error: invalid format string: expected `'}'` but string was terminated
-  --> $DIR/session-derive-errors.rs:163:20
+  --> $DIR/session-derive-errors.rs:162:16
    |
 LL | #[derive(SessionDiagnostic)]
-   |          ----------------- in this derive macro expansion
-LL | #[error(code = "E0123", slug = "foo")]
-   |               -    ^ expected `'}'` in format string
-   |               |
-   |               because of this opening brace
+   |           -    ^ expected `'}'` in format string
+   |           |
+   |           because of this opening brace
    |
    = note: if you intended to print `{`, you can escape it using `{{`
    = note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: invalid format string: unmatched `}` found
-  --> $DIR/session-derive-errors.rs:173:20
+  --> $DIR/session-derive-errors.rs:172:15
    |
 LL | #[derive(SessionDiagnostic)]
-   |          ----------------- in this derive macro expansion
-LL | #[error(code = "E0123", slug = "foo")]
-   |                    ^ unmatched `}` in format string
+   |               ^ unmatched `}` in format string
    |
    = note: if you intended to print `}`, you can escape it using `}}`
    = note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
@@ -198,8 +194,8 @@ LL | #[error(code = "E0123", slug = "foo")]
 error: the `#[label = ...]` attribute can only be applied to fields of type `Span`
   --> $DIR/session-derive-errors.rs:192:5
    |
-LL |     #[label = "See here"]
-   |     ^^^^^^^^^^^^^^^^^^^^^
+LL |     #[label = "bar"]
+   |     ^^^^^^^^^^^^^^^^
 
 error: `nonsense` is not a valid key for `#[suggestion(...)]`
   --> $DIR/session-derive-errors.rs:217:18
@@ -250,8 +246,8 @@ LL | |     suggestion: (Applicability, Applicability, Span),
 error: invalid annotation list `#[label(...)]`
   --> $DIR/session-derive-errors.rs:279:7
    |
-LL |     #[label("wrong kind of annotation for label")]
-   |       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     #[label("bar")]
+   |       ^^^^^^^^^^^^
 
 error: cannot find attribute `nonsense` in this scope
   --> $DIR/session-derive-errors.rs:51:3
@@ -266,7 +262,7 @@ LL |     #[nonsense]
    |       ^^^^^^^^
 
 error[E0599]: no method named `into_diagnostic_arg` found for struct `Hello` in the current scope
-  --> $DIR/session-derive-errors.rs:315:10
+  --> $DIR/session-derive-errors.rs:323:10
    |
 LL | struct Hello {}
    | ------------ method `into_diagnostic_arg` not found for this
index 5bfeaa58bdf1432eaf654fa6ac901678224433ad..bb3017b6ec26adad6984cbc7e8d9c1fb1ec1bc79 100644 (file)
@@ -2,7 +2,7 @@ error[E0184]: the trait `Copy` may not be implemented for this type; the type ha
   --> $DIR/E0184.rs:1:10
    |
 LL | #[derive(Copy)]
-   |          ^^^^ Copy not allowed on types with destructors
+   |          ^^^^ `Copy` not allowed on types with destructors
    |
    = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info)
 
index 36ee6570e42cd8a363c5a055878e5102476865c3..8649c8abbfa7f9035ef618bb3e0d30cb88a8ebaa 100644 (file)
@@ -2,7 +2,7 @@ error[E0184]: the trait `Copy` may not be implemented for this type; the type ha
   --> $DIR/exclusive-drop-and-copy.rs:3:10
    |
 LL | #[derive(Copy, Clone)]
-   |          ^^^^ Copy not allowed on types with destructors
+   |          ^^^^ `Copy` not allowed on types with destructors
    |
    = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info)
 
@@ -10,7 +10,7 @@ error[E0184]: the trait `Copy` may not be implemented for this type; the type ha
   --> $DIR/exclusive-drop-and-copy.rs:10:10
    |
 LL | #[derive(Copy, Clone)]
-   |          ^^^^ Copy not allowed on types with destructors
+   |          ^^^^ `Copy` not allowed on types with destructors
    |
    = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info)