mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 06:44:35 +00:00
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 @@ use crate::diagnostics::error::{
|
|||||||
SessionDiagnosticDeriveError,
|
SessionDiagnosticDeriveError,
|
||||||
};
|
};
|
||||||
use crate::diagnostics::utils::{
|
use crate::diagnostics::utils::{
|
||||||
option_inner_ty, report_error_if_not_applied_to_span, type_matches_path, FieldInfo,
|
option_inner_ty, report_error_if_not_applied_to_span, type_matches_path, Applicability,
|
||||||
HasFieldMap, SetOnce,
|
FieldInfo, HasFieldMap, SetOnce,
|
||||||
};
|
};
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::str::FromStr;
|
||||||
use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, Type};
|
use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, Type};
|
||||||
use synstructure::Structure;
|
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 msg = None;
|
||||||
let mut code = None;
|
let mut code = None;
|
||||||
@ -445,6 +446,7 @@ impl SessionDiagnosticDeriveBuilder {
|
|||||||
let nested_name = nested_name.as_str();
|
let nested_name = nested_name.as_str();
|
||||||
match meta {
|
match meta {
|
||||||
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
|
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
|
||||||
|
let span = meta.span().unwrap();
|
||||||
match nested_name {
|
match nested_name {
|
||||||
"message" => {
|
"message" => {
|
||||||
msg = Some(s.value());
|
msg = Some(s.value());
|
||||||
@ -453,9 +455,27 @@ impl SessionDiagnosticDeriveBuilder {
|
|||||||
let formatted_str = self.build_format(&s.value(), s.span());
|
let formatted_str = self.build_format(&s.value(), s.span());
|
||||||
code = Some(formatted_str);
|
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| {
|
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
|
||||||
diag.help(
|
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 method = format_ident!("span_{}", name);
|
||||||
|
|
||||||
let slug = self
|
let slug = self
|
||||||
@ -475,7 +498,7 @@ impl SessionDiagnosticDeriveBuilder {
|
|||||||
let msg = quote! { rustc_errors::DiagnosticMessage::fluent_attr(#slug, #msg) };
|
let msg = quote! { rustc_errors::DiagnosticMessage::fluent_attr(#slug, #msg) };
|
||||||
let code = code.unwrap_or_else(|| quote! { String::new() });
|
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),
|
_ => throw_invalid_attr!(attr, &meta),
|
||||||
}
|
}
|
||||||
@ -505,12 +528,12 @@ impl SessionDiagnosticDeriveBuilder {
|
|||||||
fn span_and_applicability_of_ty(
|
fn span_and_applicability_of_ty(
|
||||||
&self,
|
&self,
|
||||||
info: FieldInfo<'_>,
|
info: FieldInfo<'_>,
|
||||||
) -> Result<(TokenStream, TokenStream), SessionDiagnosticDeriveError> {
|
) -> Result<(TokenStream, Option<TokenStream>), SessionDiagnosticDeriveError> {
|
||||||
match &info.ty {
|
match &info.ty {
|
||||||
// If `ty` is `Span` w/out applicability, then use `Applicability::Unspecified`.
|
// If `ty` is `Span` w/out applicability, then use `Applicability::Unspecified`.
|
||||||
ty @ Type::Path(..) if type_matches_path(ty, &["rustc_span", "Span"]) => {
|
ty @ Type::Path(..) if type_matches_path(ty, &["rustc_span", "Span"]) => {
|
||||||
let binding = &info.binding.binding;
|
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.
|
// If `ty` is `(Span, Applicability)` then return tokens accessing those.
|
||||||
Type::Tuple(tup) => {
|
Type::Tuple(tup) => {
|
||||||
@ -546,7 +569,7 @@ impl SessionDiagnosticDeriveBuilder {
|
|||||||
.map(|applicability_idx| quote!(#binding.#applicability_idx))
|
.map(|applicability_idx| quote!(#binding.#applicability_idx))
|
||||||
.unwrap_or_else(|| quote!(rustc_errors::Applicability::Unspecified));
|
.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| {
|
throw_span_err!(info.span.unwrap(), "wrong types for suggestion", |diag| {
|
||||||
|
@ -6,7 +6,7 @@ use crate::diagnostics::error::{
|
|||||||
};
|
};
|
||||||
use crate::diagnostics::utils::{
|
use crate::diagnostics::utils::{
|
||||||
option_inner_ty, report_error_if_not_applied_to_applicability,
|
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 proc_macro2::TokenStream;
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
@ -16,48 +16,6 @@ use std::str::FromStr;
|
|||||||
use syn::{spanned::Spanned, Meta, MetaList, MetaNameValue};
|
use syn::{spanned::Spanned, Meta, MetaList, MetaNameValue};
|
||||||
use synstructure::{BindingInfo, Structure, VariantInfo};
|
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?
|
/// Which kind of suggestion is being created?
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
enum SubdiagnosticSuggestionKind {
|
enum SubdiagnosticSuggestionKind {
|
||||||
|
@ -3,6 +3,7 @@ use proc_macro::Span;
|
|||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
use std::str::FromStr;
|
||||||
use syn::{spanned::Spanned, Attribute, Meta, Type, Visibility};
|
use syn::{spanned::Spanned, Attribute, Meta, Type, Visibility};
|
||||||
use synstructure::BindingInfo;
|
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<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 {
|
struct ErrorWithNoteCustomWrongOrder {
|
||||||
val: String,
|
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")]
|
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
|
error: `#[suggestion(msg = ...)]` is not a valid attribute
|
||||||
--> $DIR/diagnostic-derive.rs:232:18
|
--> $DIR/diagnostic-derive.rs:232:18
|
||||||
@ -265,7 +265,7 @@ error: `#[suggestion(msg = ...)]` is not a valid attribute
|
|||||||
LL | #[suggestion(msg = "bar")]
|
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
|
error: wrong field type for suggestion
|
||||||
--> $DIR/diagnostic-derive.rs:254:5
|
--> $DIR/diagnostic-derive.rs:254:5
|
||||||
@ -325,6 +325,18 @@ error: `#[note = ...]` must come after `#[error(..)]` or `#[warn(..)]`
|
|||||||
LL | #[note = "bar"]
|
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
|
error: cannot find attribute `nonsense` in this scope
|
||||||
--> $DIR/diagnostic-derive.rs:51:3
|
--> $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)
|
= 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`.
|
For more information about this error, try `rustc --explain E0599`.
|
||||||
|
Loading…
Reference in New Issue
Block a user