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