Make SetOnce nicer to use
This commit is contained in:
parent
efb20bc855
commit
d4a1a6f698
@ -18,6 +18,8 @@
|
|||||||
};
|
};
|
||||||
use synstructure::{BindingInfo, Structure};
|
use synstructure::{BindingInfo, Structure};
|
||||||
|
|
||||||
|
use super::utils::SpannedOption;
|
||||||
|
|
||||||
/// What kind of diagnostic is being derived - a fatal/error/warning or a lint?
|
/// What kind of diagnostic is being derived - a fatal/error/warning or a lint?
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
pub(crate) enum DiagnosticDeriveKind {
|
pub(crate) enum DiagnosticDeriveKind {
|
||||||
@ -40,10 +42,10 @@ pub(crate) struct DiagnosticDeriveBuilder {
|
|||||||
pub kind: DiagnosticDeriveKind,
|
pub kind: DiagnosticDeriveKind,
|
||||||
/// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that
|
/// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that
|
||||||
/// has the actual diagnostic message.
|
/// has the actual diagnostic message.
|
||||||
pub slug: Option<(Path, proc_macro::Span)>,
|
pub slug: SpannedOption<Path>,
|
||||||
/// Error codes are a optional part of the struct attribute - this is only set to detect
|
/// Error codes are a optional part of the struct attribute - this is only set to detect
|
||||||
/// multiple specifications.
|
/// multiple specifications.
|
||||||
pub code: Option<(String, proc_macro::Span)>,
|
pub code: SpannedOption<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasFieldMap for DiagnosticDeriveBuilder {
|
impl HasFieldMap for DiagnosticDeriveBuilder {
|
||||||
@ -191,7 +193,7 @@ fn generate_structure_code_for_attr(
|
|||||||
match nested_attr {
|
match nested_attr {
|
||||||
NestedMeta::Meta(Meta::Path(path)) => {
|
NestedMeta::Meta(Meta::Path(path)) => {
|
||||||
if is_diag {
|
if is_diag {
|
||||||
self.slug.set_once((path.clone(), span));
|
self.slug.set_once(path.clone(), span);
|
||||||
} else {
|
} else {
|
||||||
let fn_name = proc_macro2::Ident::new(name, attr.span());
|
let fn_name = proc_macro2::Ident::new(name, attr.span());
|
||||||
return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); });
|
return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); });
|
||||||
@ -224,8 +226,8 @@ fn generate_structure_code_for_attr(
|
|||||||
let span = s.span().unwrap();
|
let span = s.span().unwrap();
|
||||||
match nested_name.as_str() {
|
match nested_name.as_str() {
|
||||||
"code" => {
|
"code" => {
|
||||||
self.code.set_once((s.value(), span));
|
self.code.set_once(s.value(), span);
|
||||||
let code = &self.code.as_ref().map(|(v, _)| v);
|
let code = &self.code.value_ref();
|
||||||
tokens.push(quote! {
|
tokens.push(quote! {
|
||||||
#diag.code(rustc_errors::DiagnosticId::Error(#code.to_string()));
|
#diag.code(rustc_errors::DiagnosticId::Error(#code.to_string()));
|
||||||
});
|
});
|
||||||
@ -476,10 +478,10 @@ fn generate_inner_field_code_suggestion(
|
|||||||
match nested_name {
|
match nested_name {
|
||||||
"code" => {
|
"code" => {
|
||||||
let formatted_str = self.build_format(&s.value(), s.span());
|
let formatted_str = self.build_format(&s.value(), s.span());
|
||||||
code.set_once((formatted_str, span));
|
code.set_once(formatted_str, span);
|
||||||
}
|
}
|
||||||
"applicability" => match Applicability::from_str(&s.value()) {
|
"applicability" => match Applicability::from_str(&s.value()) {
|
||||||
Ok(v) => applicability.set_once((quote! { #v }, span)),
|
Ok(v) => applicability.set_once(quote! { #v }, span),
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
span_err(span, "invalid applicability").emit();
|
span_err(span, "invalid applicability").emit();
|
||||||
}
|
}
|
||||||
@ -546,7 +548,7 @@ fn add_subdiagnostic(&self, kind: &Ident, fluent_attr_identifier: Path) -> Token
|
|||||||
fn span_and_applicability_of_ty(
|
fn span_and_applicability_of_ty(
|
||||||
&self,
|
&self,
|
||||||
info: FieldInfo<'_>,
|
info: FieldInfo<'_>,
|
||||||
) -> Result<(TokenStream, Option<(TokenStream, proc_macro::Span)>), DiagnosticDeriveError> {
|
) -> Result<(TokenStream, SpannedOption<TokenStream>), DiagnosticDeriveError> {
|
||||||
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"]) => {
|
||||||
@ -570,9 +572,9 @@ fn type_err(span: &Span) -> Result<!, DiagnosticDeriveError> {
|
|||||||
|
|
||||||
for (idx, elem) in tup.elems.iter().enumerate() {
|
for (idx, elem) in tup.elems.iter().enumerate() {
|
||||||
if type_matches_path(elem, &["rustc_span", "Span"]) {
|
if type_matches_path(elem, &["rustc_span", "Span"]) {
|
||||||
span_idx.set_once((syn::Index::from(idx), elem.span().unwrap()));
|
span_idx.set_once(syn::Index::from(idx), elem.span().unwrap());
|
||||||
} else if type_matches_path(elem, &["rustc_errors", "Applicability"]) {
|
} else if type_matches_path(elem, &["rustc_errors", "Applicability"]) {
|
||||||
applicability_idx.set_once((syn::Index::from(idx), elem.span().unwrap()));
|
applicability_idx.set_once(syn::Index::from(idx), elem.span().unwrap());
|
||||||
} else {
|
} else {
|
||||||
type_err(&elem.span())?;
|
type_err(&elem.span())?;
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path};
|
use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path};
|
||||||
use synstructure::{BindingInfo, Structure, VariantInfo};
|
use synstructure::{BindingInfo, Structure, VariantInfo};
|
||||||
|
|
||||||
|
use super::utils::SpannedOption;
|
||||||
|
|
||||||
/// Which kind of suggestion is being created?
|
/// Which kind of suggestion is being created?
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
enum SubdiagnosticSuggestionKind {
|
enum SubdiagnosticSuggestionKind {
|
||||||
@ -195,10 +197,10 @@ struct SubdiagnosticDeriveBuilder<'a> {
|
|||||||
fields: HashMap<String, TokenStream>,
|
fields: HashMap<String, TokenStream>,
|
||||||
|
|
||||||
/// Identifier for the binding to the `#[primary_span]` field.
|
/// Identifier for the binding to the `#[primary_span]` field.
|
||||||
span_field: Option<(proc_macro2::Ident, proc_macro::Span)>,
|
span_field: SpannedOption<proc_macro2::Ident>,
|
||||||
/// If a suggestion, the identifier for the binding to the `#[applicability]` field or a
|
/// If a suggestion, the identifier for the binding to the `#[applicability]` field or a
|
||||||
/// `rustc_errors::Applicability::*` variant directly.
|
/// `rustc_errors::Applicability::*` variant directly.
|
||||||
applicability: Option<(TokenStream, proc_macro::Span)>,
|
applicability: SpannedOption<TokenStream>,
|
||||||
|
|
||||||
/// Set to true when a `#[suggestion_part]` field is encountered, used to generate an error
|
/// Set to true when a `#[suggestion_part]` field is encountered, used to generate an error
|
||||||
/// during finalization if still `false`.
|
/// during finalization if still `false`.
|
||||||
@ -283,7 +285,7 @@ fn identify_kind(&mut self) -> Result<Vec<(SubdiagnosticKind, Path)>, Diagnostic
|
|||||||
if let Some(nested_attr) = nested_iter.next() {
|
if let Some(nested_attr) = nested_iter.next() {
|
||||||
match nested_attr {
|
match nested_attr {
|
||||||
NestedMeta::Meta(Meta::Path(path)) => {
|
NestedMeta::Meta(Meta::Path(path)) => {
|
||||||
slug.set_once((path.clone(), span));
|
slug.set_once(path.clone(), span);
|
||||||
}
|
}
|
||||||
NestedMeta::Meta(meta @ Meta::NameValue(_))
|
NestedMeta::Meta(meta @ Meta::NameValue(_))
|
||||||
if matches!(
|
if matches!(
|
||||||
@ -326,7 +328,7 @@ fn identify_kind(&mut self) -> Result<Vec<(SubdiagnosticKind, Path)>, Diagnostic
|
|||||||
"code" => {
|
"code" => {
|
||||||
if matches!(kind, SubdiagnosticKind::Suggestion { .. }) {
|
if matches!(kind, SubdiagnosticKind::Suggestion { .. }) {
|
||||||
let formatted_str = self.build_format(&value.value(), value.span());
|
let formatted_str = self.build_format(&value.value(), value.span());
|
||||||
code.set_once((formatted_str, span));
|
code.set_once(formatted_str, span);
|
||||||
} else {
|
} else {
|
||||||
span_err(
|
span_err(
|
||||||
span,
|
span,
|
||||||
@ -349,7 +351,7 @@ fn identify_kind(&mut self) -> Result<Vec<(SubdiagnosticKind, Path)>, Diagnostic
|
|||||||
span_err(span, "invalid applicability").emit();
|
span_err(span, "invalid applicability").emit();
|
||||||
Applicability::Unspecified
|
Applicability::Unspecified
|
||||||
});
|
});
|
||||||
self.applicability.set_once((quote! { #value }, span));
|
self.applicability.set_once(quote! { #value }, span);
|
||||||
} else {
|
} else {
|
||||||
span_err(
|
span_err(
|
||||||
span,
|
span,
|
||||||
@ -485,7 +487,7 @@ fn generate_field_code_inner_path(
|
|||||||
report_error_if_not_applied_to_span(attr, &info)?;
|
report_error_if_not_applied_to_span(attr, &info)?;
|
||||||
|
|
||||||
let binding = info.binding.binding.clone();
|
let binding = info.binding.binding.clone();
|
||||||
self.span_field.set_once((binding, span));
|
self.span_field.set_once(binding, span);
|
||||||
|
|
||||||
Ok(quote! {})
|
Ok(quote! {})
|
||||||
}
|
}
|
||||||
@ -509,7 +511,7 @@ fn generate_field_code_inner_path(
|
|||||||
report_error_if_not_applied_to_applicability(attr, &info)?;
|
report_error_if_not_applied_to_applicability(attr, &info)?;
|
||||||
|
|
||||||
let binding = info.binding.binding.clone();
|
let binding = info.binding.binding.clone();
|
||||||
self.applicability.set_once((quote! { #binding }, span));
|
self.applicability.set_once(quote! { #binding }, span);
|
||||||
} else {
|
} else {
|
||||||
span_err(span, "`#[applicability]` is only valid on suggestions").emit();
|
span_err(span, "`#[applicability]` is only valid on suggestions").emit();
|
||||||
}
|
}
|
||||||
@ -577,7 +579,7 @@ fn generate_field_code_inner_list(
|
|||||||
match nested_name {
|
match nested_name {
|
||||||
"code" => {
|
"code" => {
|
||||||
let formatted_str = self.build_format(&value.value(), value.span());
|
let formatted_str = self.build_format(&value.value(), value.span());
|
||||||
code.set_once((formatted_str, span));
|
code.set_once(formatted_str, span);
|
||||||
}
|
}
|
||||||
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
|
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
|
||||||
diag.help("`code` is the only valid nested attribute")
|
diag.help("`code` is the only valid nested attribute")
|
||||||
@ -635,11 +637,12 @@ pub fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> {
|
|||||||
.map(|binding| self.generate_field_attr_code(binding, kind_stats))
|
.map(|binding| self.generate_field_attr_code(binding, kind_stats))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let span_field = self.span_field.as_ref().map(|(span, _)| span);
|
let span_field = self.span_field.value_ref();
|
||||||
let applicability = self.applicability.take().map_or_else(
|
let applicability = self
|
||||||
|| quote! { rustc_errors::Applicability::Unspecified },
|
.applicability
|
||||||
|(applicability, _)| applicability,
|
.take()
|
||||||
);
|
.value()
|
||||||
|
.unwrap_or_else(|| quote! { rustc_errors::Applicability::Unspecified });
|
||||||
|
|
||||||
let diag = &self.diag;
|
let diag = &self.diag;
|
||||||
let mut calls = TokenStream::new();
|
let mut calls = TokenStream::new();
|
||||||
|
@ -172,13 +172,17 @@ pub(crate) struct FieldInfo<'a> {
|
|||||||
/// Small helper trait for abstracting over `Option` fields that contain a value and a `Span`
|
/// Small helper trait for abstracting over `Option` fields that contain a value and a `Span`
|
||||||
/// for error reporting if they are set more than once.
|
/// for error reporting if they are set more than once.
|
||||||
pub(crate) trait SetOnce<T> {
|
pub(crate) trait SetOnce<T> {
|
||||||
fn set_once(&mut self, _: (T, Span));
|
fn set_once(&mut self, value: T, span: Span);
|
||||||
|
|
||||||
fn value(self) -> Option<T>;
|
fn value(self) -> Option<T>;
|
||||||
|
fn value_ref(&self) -> Option<&T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> SetOnce<T> for Option<(T, Span)> {
|
/// An [`Option<T>`] that keeps track of the span that caused it to be set; used with [`SetOnce`].
|
||||||
fn set_once(&mut self, (value, span): (T, Span)) {
|
pub(super) type SpannedOption<T> = Option<(T, Span)>;
|
||||||
|
|
||||||
|
impl<T> SetOnce<T> for SpannedOption<T> {
|
||||||
|
fn set_once(&mut self, value: T, span: Span) {
|
||||||
match self {
|
match self {
|
||||||
None => {
|
None => {
|
||||||
*self = Some((value, span));
|
*self = Some((value, span));
|
||||||
@ -194,6 +198,10 @@ fn set_once(&mut self, (value, span): (T, Span)) {
|
|||||||
fn value(self) -> Option<T> {
|
fn value(self) -> Option<T> {
|
||||||
self.map(|(v, _)| v)
|
self.map(|(v, _)| v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn value_ref(&self) -> Option<&T> {
|
||||||
|
self.as_ref().map(|(v, _)| v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait HasFieldMap {
|
pub(crate) trait HasFieldMap {
|
||||||
|
Loading…
Reference in New Issue
Block a user