macros: allow setting applicability in attribute
In the initial implementation of the `SessionSubdiagnostic`, the `Applicability` of a suggestion can be set both as a field and as part of the attribute, this commit adds the same support to the original `SessionDiagnostic` derive. Signed-off-by: David Wood <david.wood@huawei.com>
This commit is contained in:
parent
e8ee0d7a20
commit
e5d9371b30
@ -5,12 +5,13 @@
|
||||
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 @@ fn generate_non_option_field_code(
|
||||
}),
|
||||
};
|
||||
|
||||
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 @@ fn generate_non_option_field_code(
|
||||
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 @@ fn generate_non_option_field_code(
|
||||
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 @@ fn generate_non_option_field_code(
|
||||
}
|
||||
}
|
||||
|
||||
let applicability = applicability
|
||||
.unwrap_or_else(|| quote!(rustc_errors::Applicability::Unspecified));
|
||||
|
||||
let method = format_ident!("span_{}", name);
|
||||
|
||||
let slug = self
|
||||
@ -475,7 +498,7 @@ fn generate_non_option_field_code(
|
||||
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 @@ fn add_subdiagnostic(
|
||||
fn span_and_applicability_of_ty(
|
||||
&self,
|
||||
info: FieldInfo<'_>,
|
||||
) -> Result<(TokenStream, TokenStream), SessionDiagnosticDeriveError> {
|
||||
) -> Result<(TokenStream, Option<TokenStream>), 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 @@ fn span_and_applicability_of_ty(
|
||||
.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| {
|
||||
|
@ -6,7 +6,7 @@
|
||||
};
|
||||
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 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<Self, Self::Err> {
|
||||
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 {
|
||||
|
@ -3,6 +3,7 @@
|
||||
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 @@ fn build_format(&self, input: &str, span: proc_macro2::Span) -> TokenStream {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `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<Self, Self::Err> {
|
||||
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 }
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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`.
|
||||
|
Loading…
Reference in New Issue
Block a user