Skip to content

Commit d4a1a6f

Browse files
committed
Make SetOnce nicer to use
1 parent efb20bc commit d4a1a6f

File tree

3 files changed

+39
-26
lines changed

3 files changed

+39
-26
lines changed

compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ use syn::{
1818
};
1919
use synstructure::{BindingInfo, Structure};
2020

21+
use super::utils::SpannedOption;
22+
2123
/// What kind of diagnostic is being derived - a fatal/error/warning or a lint?
2224
#[derive(Copy, Clone, PartialEq, Eq)]
2325
pub(crate) enum DiagnosticDeriveKind {
@@ -40,10 +42,10 @@ pub(crate) struct DiagnosticDeriveBuilder {
4042
pub kind: DiagnosticDeriveKind,
4143
/// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that
4244
/// has the actual diagnostic message.
43-
pub slug: Option<(Path, proc_macro::Span)>,
45+
pub slug: SpannedOption<Path>,
4446
/// Error codes are a optional part of the struct attribute - this is only set to detect
4547
/// multiple specifications.
46-
pub code: Option<(String, proc_macro::Span)>,
48+
pub code: SpannedOption<String>,
4749
}
4850

4951
impl HasFieldMap for DiagnosticDeriveBuilder {
@@ -191,7 +193,7 @@ impl DiagnosticDeriveBuilder {
191193
match nested_attr {
192194
NestedMeta::Meta(Meta::Path(path)) => {
193195
if is_diag {
194-
self.slug.set_once((path.clone(), span));
196+
self.slug.set_once(path.clone(), span);
195197
} else {
196198
let fn_name = proc_macro2::Ident::new(name, attr.span());
197199
return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); });
@@ -224,8 +226,8 @@ impl DiagnosticDeriveBuilder {
224226
let span = s.span().unwrap();
225227
match nested_name.as_str() {
226228
"code" => {
227-
self.code.set_once((s.value(), span));
228-
let code = &self.code.as_ref().map(|(v, _)| v);
229+
self.code.set_once(s.value(), span);
230+
let code = &self.code.value_ref();
229231
tokens.push(quote! {
230232
#diag.code(rustc_errors::DiagnosticId::Error(#code.to_string()));
231233
});
@@ -476,10 +478,10 @@ impl DiagnosticDeriveBuilder {
476478
match nested_name {
477479
"code" => {
478480
let formatted_str = self.build_format(&s.value(), s.span());
479-
code.set_once((formatted_str, span));
481+
code.set_once(formatted_str, span);
480482
}
481483
"applicability" => match Applicability::from_str(&s.value()) {
482-
Ok(v) => applicability.set_once((quote! { #v }, span)),
484+
Ok(v) => applicability.set_once(quote! { #v }, span),
483485
Err(()) => {
484486
span_err(span, "invalid applicability").emit();
485487
}
@@ -546,7 +548,7 @@ impl DiagnosticDeriveBuilder {
546548
fn span_and_applicability_of_ty(
547549
&self,
548550
info: FieldInfo<'_>,
549-
) -> Result<(TokenStream, Option<(TokenStream, proc_macro::Span)>), DiagnosticDeriveError> {
551+
) -> Result<(TokenStream, SpannedOption<TokenStream>), DiagnosticDeriveError> {
550552
match &info.ty {
551553
// If `ty` is `Span` w/out applicability, then use `Applicability::Unspecified`.
552554
ty @ Type::Path(..) if type_matches_path(ty, &["rustc_span", "Span"]) => {
@@ -570,9 +572,9 @@ impl DiagnosticDeriveBuilder {
570572

571573
for (idx, elem) in tup.elems.iter().enumerate() {
572574
if type_matches_path(elem, &["rustc_span", "Span"]) {
573-
span_idx.set_once((syn::Index::from(idx), elem.span().unwrap()));
575+
span_idx.set_once(syn::Index::from(idx), elem.span().unwrap());
574576
} else if type_matches_path(elem, &["rustc_errors", "Applicability"]) {
575-
applicability_idx.set_once((syn::Index::from(idx), elem.span().unwrap()));
577+
applicability_idx.set_once(syn::Index::from(idx), elem.span().unwrap());
576578
} else {
577579
type_err(&elem.span())?;
578580
}

compiler/rustc_macros/src/diagnostics/subdiagnostic.rs

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ use std::str::FromStr;
1515
use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path};
1616
use synstructure::{BindingInfo, Structure, VariantInfo};
1717

18+
use super::utils::SpannedOption;
19+
1820
/// Which kind of suggestion is being created?
1921
#[derive(Clone, Copy)]
2022
enum SubdiagnosticSuggestionKind {
@@ -195,10 +197,10 @@ struct SubdiagnosticDeriveBuilder<'a> {
195197
fields: HashMap<String, TokenStream>,
196198

197199
/// Identifier for the binding to the `#[primary_span]` field.
198-
span_field: Option<(proc_macro2::Ident, proc_macro::Span)>,
200+
span_field: SpannedOption<proc_macro2::Ident>,
199201
/// If a suggestion, the identifier for the binding to the `#[applicability]` field or a
200202
/// `rustc_errors::Applicability::*` variant directly.
201-
applicability: Option<(TokenStream, proc_macro::Span)>,
203+
applicability: SpannedOption<TokenStream>,
202204

203205
/// Set to true when a `#[suggestion_part]` field is encountered, used to generate an error
204206
/// during finalization if still `false`.
@@ -283,7 +285,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
283285
if let Some(nested_attr) = nested_iter.next() {
284286
match nested_attr {
285287
NestedMeta::Meta(Meta::Path(path)) => {
286-
slug.set_once((path.clone(), span));
288+
slug.set_once(path.clone(), span);
287289
}
288290
NestedMeta::Meta(meta @ Meta::NameValue(_))
289291
if matches!(
@@ -326,7 +328,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
326328
"code" => {
327329
if matches!(kind, SubdiagnosticKind::Suggestion { .. }) {
328330
let formatted_str = self.build_format(&value.value(), value.span());
329-
code.set_once((formatted_str, span));
331+
code.set_once(formatted_str, span);
330332
} else {
331333
span_err(
332334
span,
@@ -349,7 +351,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
349351
span_err(span, "invalid applicability").emit();
350352
Applicability::Unspecified
351353
});
352-
self.applicability.set_once((quote! { #value }, span));
354+
self.applicability.set_once(quote! { #value }, span);
353355
} else {
354356
span_err(
355357
span,
@@ -485,7 +487,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
485487
report_error_if_not_applied_to_span(attr, &info)?;
486488

487489
let binding = info.binding.binding.clone();
488-
self.span_field.set_once((binding, span));
490+
self.span_field.set_once(binding, span);
489491

490492
Ok(quote! {})
491493
}
@@ -509,7 +511,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
509511
report_error_if_not_applied_to_applicability(attr, &info)?;
510512

511513
let binding = info.binding.binding.clone();
512-
self.applicability.set_once((quote! { #binding }, span));
514+
self.applicability.set_once(quote! { #binding }, span);
513515
} else {
514516
span_err(span, "`#[applicability]` is only valid on suggestions").emit();
515517
}
@@ -577,7 +579,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
577579
match nested_name {
578580
"code" => {
579581
let formatted_str = self.build_format(&value.value(), value.span());
580-
code.set_once((formatted_str, span));
582+
code.set_once(formatted_str, span);
581583
}
582584
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
583585
diag.help("`code` is the only valid nested attribute")
@@ -635,11 +637,12 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
635637
.map(|binding| self.generate_field_attr_code(binding, kind_stats))
636638
.collect();
637639

638-
let span_field = self.span_field.as_ref().map(|(span, _)| span);
639-
let applicability = self.applicability.take().map_or_else(
640-
|| quote! { rustc_errors::Applicability::Unspecified },
641-
|(applicability, _)| applicability,
642-
);
640+
let span_field = self.span_field.value_ref();
641+
let applicability = self
642+
.applicability
643+
.take()
644+
.value()
645+
.unwrap_or_else(|| quote! { rustc_errors::Applicability::Unspecified });
643646

644647
let diag = &self.diag;
645648
let mut calls = TokenStream::new();

compiler/rustc_macros/src/diagnostics/utils.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,13 +172,17 @@ pub(crate) struct FieldInfo<'a> {
172172
/// Small helper trait for abstracting over `Option` fields that contain a value and a `Span`
173173
/// for error reporting if they are set more than once.
174174
pub(crate) trait SetOnce<T> {
175-
fn set_once(&mut self, _: (T, Span));
175+
fn set_once(&mut self, value: T, span: Span);
176176

177177
fn value(self) -> Option<T>;
178+
fn value_ref(&self) -> Option<&T>;
178179
}
179180

180-
impl<T> SetOnce<T> for Option<(T, Span)> {
181-
fn set_once(&mut self, (value, span): (T, Span)) {
181+
/// An [`Option<T>`] that keeps track of the span that caused it to be set; used with [`SetOnce`].
182+
pub(super) type SpannedOption<T> = Option<(T, Span)>;
183+
184+
impl<T> SetOnce<T> for SpannedOption<T> {
185+
fn set_once(&mut self, value: T, span: Span) {
182186
match self {
183187
None => {
184188
*self = Some((value, span));
@@ -194,6 +198,10 @@ impl<T> SetOnce<T> for Option<(T, Span)> {
194198
fn value(self) -> Option<T> {
195199
self.map(|(v, _)| v)
196200
}
201+
202+
fn value_ref(&self) -> Option<&T> {
203+
self.as_ref().map(|(v, _)| v)
204+
}
197205
}
198206

199207
pub(crate) trait HasFieldMap {

0 commit comments

Comments
 (0)