diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 032e0cecbc7..c163f233b47 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -5,12 +5,13 @@ use crate::diagnostics::error::{ SessionDiagnosticDeriveError, }; use crate::diagnostics::utils::{ - option_inner_ty, report_error_if_not_applied_to_span, type_matches_path, FieldInfo, - HasFieldMap, SetOnce, + option_inner_ty, report_error_if_not_applied_to_span, type_matches_path, Applicability, + FieldInfo, HasFieldMap, SetOnce, }; use proc_macro2::TokenStream; use quote::{format_ident, quote}; use std::collections::HashMap; +use std::str::FromStr; use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, Type}; use synstructure::Structure; @@ -430,7 +431,7 @@ impl SessionDiagnosticDeriveBuilder { }), }; - let (span_, applicability) = self.span_and_applicability_of_ty(info)?; + let (span_field, mut applicability) = self.span_and_applicability_of_ty(info)?; let mut msg = None; let mut code = None; @@ -445,6 +446,7 @@ impl SessionDiagnosticDeriveBuilder { let nested_name = nested_name.as_str(); match meta { Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => { + let span = meta.span().unwrap(); match nested_name { "message" => { msg = Some(s.value()); @@ -453,9 +455,27 @@ impl SessionDiagnosticDeriveBuilder { let formatted_str = self.build_format(&s.value(), s.span()); code = Some(formatted_str); } + "applicability" => { + applicability = match applicability { + Some(v) => { + span_err( + span, + "applicability cannot be set in both the field and attribute" + ).emit(); + Some(v) + } + None => match Applicability::from_str(&s.value()) { + Ok(v) => Some(quote! { #v }), + Err(()) => { + span_err(span, "invalid applicability").emit(); + None + } + }, + } + } _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { diag.help( - "only `message` and `code` are valid field attributes", + "only `message`, `code` and `applicability` are valid field attributes", ) }), } @@ -464,6 +484,9 @@ impl SessionDiagnosticDeriveBuilder { } } + let applicability = applicability + .unwrap_or_else(|| quote!(rustc_errors::Applicability::Unspecified)); + let method = format_ident!("span_{}", name); let slug = self @@ -475,7 +498,7 @@ impl SessionDiagnosticDeriveBuilder { 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); }) + Ok(quote! { #diag.#method(#span_field, #msg, #code, #applicability); }) } _ => throw_invalid_attr!(attr, &meta), } @@ -505,12 +528,12 @@ impl SessionDiagnosticDeriveBuilder { fn span_and_applicability_of_ty( &self, info: FieldInfo<'_>, - ) -> Result<(TokenStream, TokenStream), SessionDiagnosticDeriveError> { + ) -> Result<(TokenStream, Option), SessionDiagnosticDeriveError> { match &info.ty { // If `ty` is `Span` w/out applicability, then use `Applicability::Unspecified`. ty @ Type::Path(..) if type_matches_path(ty, &["rustc_span", "Span"]) => { let binding = &info.binding.binding; - Ok((quote!(*#binding), quote!(rustc_errors::Applicability::Unspecified))) + Ok((quote!(*#binding), None)) } // If `ty` is `(Span, Applicability)` then return tokens accessing those. Type::Tuple(tup) => { @@ -546,7 +569,7 @@ impl SessionDiagnosticDeriveBuilder { .map(|applicability_idx| quote!(#binding.#applicability_idx)) .unwrap_or_else(|| quote!(rustc_errors::Applicability::Unspecified)); - return Ok((span, applicability)); + return Ok((span, Some(applicability))); } throw_span_err!(info.span.unwrap(), "wrong types for suggestion", |diag| { diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index e8c0bfd6651..961b42f424f 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -6,7 +6,7 @@ use crate::diagnostics::error::{ }; use crate::diagnostics::utils::{ option_inner_ty, report_error_if_not_applied_to_applicability, - report_error_if_not_applied_to_span, FieldInfo, HasFieldMap, SetOnce, + report_error_if_not_applied_to_span, Applicability, FieldInfo, HasFieldMap, SetOnce, }; use proc_macro2::TokenStream; use quote::{format_ident, quote}; @@ -16,48 +16,6 @@ use std::str::FromStr; use syn::{spanned::Spanned, Meta, MetaList, MetaNameValue}; use synstructure::{BindingInfo, Structure, VariantInfo}; -/// `Applicability` of a suggestion - mirrors `rustc_errors::Applicability` - and used to represent -/// the user's selection of applicability if specified in an attribute. -enum Applicability { - MachineApplicable, - MaybeIncorrect, - HasPlaceholders, - Unspecified, -} - -impl FromStr for Applicability { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "machine-applicable" => Ok(Applicability::MachineApplicable), - "maybe-incorrect" => Ok(Applicability::MaybeIncorrect), - "has-placeholders" => Ok(Applicability::HasPlaceholders), - "unspecified" => Ok(Applicability::Unspecified), - _ => Err(()), - } - } -} - -impl quote::ToTokens for Applicability { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - Applicability::MachineApplicable => { - quote! { rustc_errors::Applicability::MachineApplicable } - } - Applicability::MaybeIncorrect => { - quote! { rustc_errors::Applicability::MaybeIncorrect } - } - Applicability::HasPlaceholders => { - quote! { rustc_errors::Applicability::HasPlaceholders } - } - Applicability::Unspecified => { - quote! { rustc_errors::Applicability::Unspecified } - } - }); - } -} - /// Which kind of suggestion is being created? #[derive(Clone, Copy)] enum SubdiagnosticSuggestionKind { diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 6791a9e35b8..1f36af0a20b 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -3,6 +3,7 @@ use proc_macro::Span; use proc_macro2::TokenStream; use quote::{format_ident, quote}; use std::collections::BTreeSet; +use std::str::FromStr; use syn::{spanned::Spanned, Attribute, Meta, Type, Visibility}; use synstructure::BindingInfo; @@ -222,3 +223,45 @@ pub(crate) trait HasFieldMap { } } } + +/// `Applicability` of a suggestion - mirrors `rustc_errors::Applicability` - and used to represent +/// the user's selection of applicability if specified in an attribute. +pub(crate) enum Applicability { + MachineApplicable, + MaybeIncorrect, + HasPlaceholders, + Unspecified, +} + +impl FromStr for Applicability { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "machine-applicable" => Ok(Applicability::MachineApplicable), + "maybe-incorrect" => Ok(Applicability::MaybeIncorrect), + "has-placeholders" => Ok(Applicability::HasPlaceholders), + "unspecified" => Ok(Applicability::Unspecified), + _ => Err(()), + } + } +} + +impl quote::ToTokens for Applicability { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.extend(match self { + Applicability::MachineApplicable => { + quote! { rustc_errors::Applicability::MachineApplicable } + } + Applicability::MaybeIncorrect => { + quote! { rustc_errors::Applicability::MaybeIncorrect } + } + Applicability::HasPlaceholders => { + quote! { rustc_errors::Applicability::HasPlaceholders } + } + Applicability::Unspecified => { + quote! { rustc_errors::Applicability::Unspecified } + } + }); + } +} diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index df57a9f183f..ddcaa05e1e4 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -433,3 +433,33 @@ struct ErrorWithNoteWrongOrder { struct ErrorWithNoteCustomWrongOrder { val: String, } + +#[derive(SessionDiagnostic)] +#[error(code = "E0123", slug = "foo")] +struct ApplicabilityInBoth { + #[suggestion(message = "bar", code = "...", applicability = "maybe-incorrect")] + //~^ ERROR applicability cannot be set in both the field and attribute + suggestion: (Span, Applicability), +} + +#[derive(SessionDiagnostic)] +#[error(code = "E0123", slug = "foo")] +struct InvalidApplicability { + #[suggestion(message = "bar", code = "...", applicability = "batman")] + //~^ ERROR invalid applicability + suggestion: Span, +} + +#[derive(SessionDiagnostic)] +#[error(code = "E0123", slug = "foo")] +struct ValidApplicability { + #[suggestion(message = "bar", code = "...", applicability = "maybe-incorrect")] + suggestion: Span, +} + +#[derive(SessionDiagnostic)] +#[error(code = "E0123", slug = "foo")] +struct NoApplicability { + #[suggestion(message = "bar", code = "...")] + suggestion: Span, +} diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index 5a685ae43be..b26e805718a 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -257,7 +257,7 @@ error: `#[suggestion(nonsense = ...)]` is not a valid attribute LL | #[suggestion(nonsense = "bar")] | ^^^^^^^^^^^^^^^^ | - = help: only `message` and `code` are valid field attributes + = help: only `message`, `code` and `applicability` are valid field attributes error: `#[suggestion(msg = ...)]` is not a valid attribute --> $DIR/diagnostic-derive.rs:232:18 @@ -265,7 +265,7 @@ error: `#[suggestion(msg = ...)]` is not a valid attribute LL | #[suggestion(msg = "bar")] | ^^^^^^^^^^^ | - = help: only `message` and `code` are valid field attributes + = help: only `message`, `code` and `applicability` are valid field attributes error: wrong field type for suggestion --> $DIR/diagnostic-derive.rs:254:5 @@ -325,6 +325,18 @@ error: `#[note = ...]` must come after `#[error(..)]` or `#[warn(..)]` LL | #[note = "bar"] | ^^^^^^^^^^^^^^^ +error: applicability cannot be set in both the field and attribute + --> $DIR/diagnostic-derive.rs:440:49 + | +LL | #[suggestion(message = "bar", code = "...", applicability = "maybe-incorrect")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: invalid applicability + --> $DIR/diagnostic-derive.rs:448:49 + | +LL | #[suggestion(message = "bar", code = "...", applicability = "batman")] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + error: cannot find attribute `nonsense` in this scope --> $DIR/diagnostic-derive.rs:51:3 | @@ -348,6 +360,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 41 previous errors +error: aborting due to 43 previous errors For more information about this error, try `rustc --explain E0599`.