Skip to main content

displaydoc/
expand.rs

1use super::attr::AttrsHelper;
2use proc_macro2::{Span, TokenStream};
3use quote::{format_ident, quote};
4use syn::{
5    punctuated::Punctuated,
6    token::{Colon, Comma, PathSep, Plus, Where},
7    Data, DataEnum, DataStruct, DeriveInput, Error, Fields, Generics, Ident, Path, PathArguments,
8    PathSegment, PredicateType, Result, TraitBound, TraitBoundModifiers, Type, TypeParam,
9    TypeParamBound, TypePath, WhereClause, WherePredicate,
10};
11
12use std::collections::BTreeMap;
13
14pub(crate) fn derive(input: &DeriveInput) -> Result<TokenStream> {
15    let impls = match &input.data {
16        Data::Struct(data) => impl_struct(input, data),
17        Data::Enum(data) => impl_enum(input, data),
18        Data::Union(_) => Err(Error::new_spanned(input, "Unions are not supported")),
19    }?;
20
21    let helpers = specialization();
22    Ok({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_pound(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Bracket,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "allow");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s,
                        "non_upper_case_globals");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_ident(&mut _s,
                        "unused_attributes");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_ident(&mut _s,
                        "unused_qualifications");
                    _s
                });
            _s
        });
    ::quote::__private::push_ident(&mut _s, "const");
    ::quote::__private::push_underscore(&mut _s);
    ::quote::__private::push_colon(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        ::quote::__private::TokenStream::new());
    ::quote::__private::push_eq(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&helpers, &mut _s);
            ::quote::ToTokens::to_tokens(&impls, &mut _s);
            _s
        });
    ::quote::__private::push_semi(&mut _s);
    _s
}quote! {
23        #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
24        const _: () = {
25            #helpers
26            #impls
27        };
28    })
29}
30
31#[cfg(feature = "std")]
32fn specialization() -> TokenStream {
33    quote! {
34        trait DisplayToDisplayDoc {
35            fn __displaydoc_display(&self) -> Self;
36        }
37
38        impl<T: ::core::fmt::Display> DisplayToDisplayDoc for &T {
39            fn __displaydoc_display(&self) -> Self {
40                self
41            }
42        }
43
44        // If the `std` feature gets enabled we want to ensure that any crate
45        // using displaydoc can still reference the std crate, which is already
46        // being compiled in by whoever enabled the `std` feature in
47        // `displaydoc`, even if the crates using displaydoc are no_std.
48        extern crate std;
49
50        trait PathToDisplayDoc {
51            fn __displaydoc_display(&self) -> std::path::Display<'_>;
52        }
53
54        impl PathToDisplayDoc for std::path::Path {
55            fn __displaydoc_display(&self) -> std::path::Display<'_> {
56                self.display()
57            }
58        }
59
60        impl PathToDisplayDoc for std::path::PathBuf {
61            fn __displaydoc_display(&self) -> std::path::Display<'_> {
62                self.display()
63            }
64        }
65    }
66}
67
68#[cfg(not(feature = "std"))]
69fn specialization() -> TokenStream {
70    ::quote::__private::TokenStream::new();quote! {}
71}
72
73fn impl_struct(input: &DeriveInput, data: &DataStruct) -> Result<TokenStream> {
74    let ty = &input.ident;
75    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
76    let where_clause = generate_where_clause(&input.generics, where_clause);
77
78    let helper = AttrsHelper::new(&input.attrs);
79
80    let display = helper.display(&input.attrs)?.map(|display| {
81        let pat = match &data.fields {
82            Fields::Named(fields) => {
83                let var = fields.named.iter().map(|field| &field.ident);
84                {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "Self");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            {
                use ::quote::__private::ext::*;
                let mut _first = true;
                let has_iter = ::quote::__private::HasIterator::<false>;
                #[allow(unused_mut)]
                let (mut var, i) = var.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let var =
                        match var.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    if !_first { ::quote::__private::push_comma(&mut _s); }
                    _first = false;
                    ::quote::ToTokens::to_tokens(&var, &mut _s);
                }
            }
            _s
        });
    _s
}quote!(Self { #(#var),* })
85            }
86            Fields::Unnamed(fields) => {
87                let var = (0..fields.unnamed.len()).map(|i| match ::quote::__private::IdentFragmentAdapter(&i) {
    arg =>
        ::quote::__private::mk_ident(&::alloc::__export::must_use({
                        ::alloc::fmt::format(format_args!("_{0}", arg))
                    }), ::quote::__private::Option::None.or(arg.span())),
}format_ident!("_{}", i));
88                {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "Self");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            {
                use ::quote::__private::ext::*;
                let mut _first = true;
                let has_iter = ::quote::__private::HasIterator::<false>;
                #[allow(unused_mut)]
                let (mut var, i) = var.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let var =
                        match var.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    if !_first { ::quote::__private::push_comma(&mut _s); }
                    _first = false;
                    ::quote::ToTokens::to_tokens(&var, &mut _s);
                }
            }
            _s
        });
    _s
}quote!(Self(#(#var),*))
89            }
90            Fields::Unit => {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_underscore(&mut _s);
    _s
}quote!(_),
91        };
92        {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "impl");
    ::quote::ToTokens::to_tokens(&impl_generics, &mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "core");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "fmt");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "Display");
    ::quote::__private::push_ident(&mut _s, "for");
    ::quote::ToTokens::to_tokens(&ty, &mut _s);
    ::quote::ToTokens::to_tokens(&ty_generics, &mut _s);
    ::quote::ToTokens::to_tokens(&where_clause, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "fmt");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_and(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "self");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "formatter");
                    ::quote::__private::push_colon(&mut _s);
                    ::quote::__private::push_and(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "mut");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "core");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "fmt");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "Formatter");
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "fmt");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Result");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_pound(&mut _s);
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Bracket,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "allow");
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::__private::push_ident(&mut _s, "unused_variables");
                                    ::quote::__private::push_comma(&mut _s);
                                    ::quote::__private::push_ident(&mut _s,
                                        "unused_assignments");
                                    _s
                                });
                            _s
                        });
                    ::quote::__private::push_ident(&mut _s, "let");
                    ::quote::ToTokens::to_tokens(&pat, &mut _s);
                    ::quote::__private::push_eq(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "self");
                    ::quote::__private::push_semi(&mut _s);
                    ::quote::ToTokens::to_tokens(&display, &mut _s);
                    _s
                });
            _s
        });
    _s
}quote! {
93            impl #impl_generics ::core::fmt::Display for #ty #ty_generics #where_clause {
94                fn fmt(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
95                    // NB: This destructures the fields of `self` into named
96                    // variables (for unnamed fields, it uses _0, _1, etc as
97                    // above). The `#[allow(unused_variables, unused_assignments)]`
98                    // section means it doesn't have to parse the individual field
99                    // references out of the docstring.
100                    #[allow(unused_variables, unused_assignments)]
101                    let #pat = self;
102                    #display
103                }
104            }
105        }
106    });
107
108    Ok({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&display, &mut _s);
    _s
}quote! { #display })
109}
110
111/// Create a `where` predicate for `ident`, without any [bound][TypeParamBound]s yet.
112fn new_empty_where_type_predicate(ident: Ident) -> PredicateType {
113    let mut path_segments = Punctuated::<PathSegment, PathSep>::new();
114    path_segments.push_value(PathSegment {
115        ident,
116        arguments: PathArguments::None,
117    });
118    PredicateType {
119        attrs: Vec::new(),
120        lifetimes: None,
121        bounded_ty: Type::Path(TypePath {
122            attrs: Vec::new(),
123            qself: None,
124            path: Path {
125                leading_colon: None,
126                segments: path_segments,
127            },
128        }),
129        colon_token: Colon {
130            spans: [Span::call_site()],
131        },
132        bounds: Punctuated::<TypeParamBound, Plus>::new(),
133    }
134}
135
136/// Create a `where` clause that we can add [WherePredicate]s to.
137fn new_empty_where_clause() -> WhereClause {
138    WhereClause {
139        where_token: Where {
140            span: Span::call_site(),
141        },
142        predicates: Punctuated::<WherePredicate, Comma>::new(),
143    }
144}
145
146enum UseGlobalPrefix {
147    LeadingColon,
148    #[allow(dead_code)]
149    NoLeadingColon,
150}
151
152/// Create a path with segments composed of [Idents] *without* any [PathArguments].
153fn join_paths(name_segments: &[&str], use_global_prefix: UseGlobalPrefix) -> Path {
154    let mut segments = Punctuated::<PathSegment, PathSep>::new();
155    if !!name_segments.is_empty() {
    ::core::panicking::panic("assertion failed: !name_segments.is_empty()")
};assert!(!name_segments.is_empty());
156    segments.push_value(PathSegment {
157        ident: Ident::new(name_segments[0], Span::call_site()),
158        arguments: PathArguments::None,
159    });
160    for name in name_segments[1..].iter() {
161        segments.push_punct(PathSep {
162            spans: [Span::call_site(), Span::mixed_site()],
163        });
164        segments.push_value(PathSegment {
165            ident: Ident::new(name, Span::call_site()),
166            arguments: PathArguments::None,
167        });
168    }
169    Path {
170        leading_colon: match use_global_prefix {
171            UseGlobalPrefix::LeadingColon => Some(PathSep {
172                spans: [Span::call_site(), Span::mixed_site()],
173            }),
174            UseGlobalPrefix::NoLeadingColon => None,
175        },
176        segments,
177    }
178}
179
180/// Push `new_type_predicate` onto the end of `where_clause`.
181fn append_where_clause_type_predicate(
182    where_clause: &mut WhereClause,
183    new_type_predicate: PredicateType,
184) {
185    // Push a comma at the end if there are already any `where` predicates.
186    if !where_clause.predicates.is_empty() {
187        where_clause.predicates.push_punct(Comma {
188            spans: [Span::call_site()],
189        });
190    }
191    where_clause
192        .predicates
193        .push_value(WherePredicate::Type(new_type_predicate));
194}
195
196/// Add a requirement for [core::fmt::Display] to a `where` predicate for some type.
197fn add_display_constraint_to_type_predicate(
198    predicate_that_needs_a_display_impl: &mut PredicateType,
199) {
200    // Create a `Path` of `::core::fmt::Display`.
201    let display_path = join_paths(&["core", "fmt", "Display"], UseGlobalPrefix::LeadingColon);
202
203    let display_bound = TypeParamBound::Trait(TraitBound {
204        paren_token: None,
205        lifetimes: None,
206        modifiers: TraitBoundModifiers::default(),
207        maybe: None,
208        path: display_path,
209    });
210    if !predicate_that_needs_a_display_impl.bounds.is_empty() {
211        predicate_that_needs_a_display_impl.bounds.push_punct(Plus {
212            spans: [Span::call_site()],
213        });
214    }
215
216    predicate_that_needs_a_display_impl
217        .bounds
218        .push_value(display_bound);
219}
220
221/// Map each declared generic type parameter to the set of all trait boundaries declared on it.
222///
223/// These boundaries may come from the declaration site:
224///     pub enum E<T: MyTrait> { ... }
225/// or a `where` clause after the parameter declarations:
226///     pub enum E<T> where T: MyTrait { ... }
227/// This method will return the boundaries from both of those cases.
228fn extract_trait_constraints_from_source(
229    where_clause: &WhereClause,
230    type_params: &[&TypeParam],
231) -> BTreeMap<Ident, Vec<TraitBound>> {
232    // Add trait bounds provided at the declaration site of type parameters for the struct/enum.
233    let mut param_constraint_mapping: BTreeMap<Ident, Vec<TraitBound>> = type_params
234        .iter()
235        .map(|type_param| {
236            let trait_bounds: Vec<TraitBound> = type_param
237                .bounds
238                .iter()
239                .flat_map(|bound| match bound {
240                    TypeParamBound::Trait(trait_bound) => Some(trait_bound),
241                    _ => None,
242                })
243                .cloned()
244                .collect();
245            (type_param.ident.clone(), trait_bounds)
246        })
247        .collect();
248
249    // Add trait bounds from `where` clauses, which may be type parameters or types containing
250    // those parameters.
251    for predicate in where_clause.predicates.iter() {
252        // We only care about type and not lifetime constraints here.
253        if let WherePredicate::Type(ref pred_ty) = predicate {
254            let ident = match &pred_ty.bounded_ty {
255                Type::Path(TypePath {
256                    path,
257                    qself: None,
258                    attrs: _,
259                }) => match path.get_ident() {
260                    None => continue,
261                    Some(ident) => ident,
262                },
263                _ => continue,
264            };
265            // We ignore any type constraints that aren't direct references to type
266            // parameters of the current enum of struct definition. No types can be
267            // constrained in a `where` clause unless they are a type parameter or a generic
268            // type instantiated with one of the type parameters, so by only allowing single
269            // identifiers, we can be sure that the constrained type is a type parameter
270            // that is contained in `param_constraint_mapping`.
271            if let Some((_, ref mut known_bounds)) = param_constraint_mapping
272                .iter_mut()
273                .find(|(id, _)| *id == ident)
274            {
275                for bound in pred_ty.bounds.iter() {
276                    // We only care about trait bounds here.
277                    if let TypeParamBound::Trait(ref bound) = bound {
278                        known_bounds.push(bound.clone());
279                    }
280                }
281            }
282        }
283    }
284
285    param_constraint_mapping
286}
287
288/// Hygienically add `where _: Display` to the set of [TypeParamBound]s for `ident`, creating such
289/// a set if necessary.
290fn ensure_display_in_where_clause_for_type(where_clause: &mut WhereClause, ident: Ident) {
291    for pred_ty in where_clause
292        .predicates
293        .iter_mut()
294        // Find the `where` predicate constraining the current type param, if it exists.
295        .flat_map(|predicate| match predicate {
296            WherePredicate::Type(pred_ty) => Some(pred_ty),
297            // We're looking through type constraints, not lifetime constraints.
298            _ => None,
299        })
300    {
301        // Do a complicated destructuring in order to check if the type being constrained in this
302        // `where` clause is the type we're looking for, so we can use the mutable reference to
303        // `pred_ty` if so.
304        let matches_desired_type = #[allow(non_exhaustive_omitted_patterns)] match &pred_ty.bounded_ty {
    Type::Path(TypePath { path, .. }) if Some(&ident) == path.get_ident() =>
        true,
    _ => false,
}matches!(
305            &pred_ty.bounded_ty,
306            Type::Path(TypePath { path, .. }) if Some(&ident) == path.get_ident());
307        if matches_desired_type {
308            add_display_constraint_to_type_predicate(pred_ty);
309            return;
310        }
311    }
312
313    // If there is no `where` predicate for the current type param, we will construct one.
314    let mut new_type_predicate = new_empty_where_type_predicate(ident);
315    add_display_constraint_to_type_predicate(&mut new_type_predicate);
316    append_where_clause_type_predicate(where_clause, new_type_predicate);
317}
318
319/// For all declared type parameters, add a [core::fmt::Display] constraint, unless the type
320/// parameter already has any type constraint.
321fn ensure_where_clause_has_display_for_all_unconstrained_members(
322    where_clause: &mut WhereClause,
323    type_params: &[&TypeParam],
324) {
325    let param_constraint_mapping = extract_trait_constraints_from_source(where_clause, type_params);
326
327    for (ident, known_bounds) in param_constraint_mapping.into_iter() {
328        // If the type parameter has any constraints already, we don't want to touch it, to avoid
329        // breaking use cases where a type parameter only needs to impl `Debug`, for example.
330        if known_bounds.is_empty() {
331            ensure_display_in_where_clause_for_type(where_clause, ident);
332        }
333    }
334}
335
336/// Generate a `where` clause that ensures all generic type parameters `impl`
337/// [core::fmt::Display] unless already constrained.
338///
339/// This approach allows struct/enum definitions deriving [crate::Display] to avoid hardcoding
340/// a [core::fmt::Display] constraint into every type parameter.
341///
342/// If the type parameter isn't already constrained, we add a `where _: Display` clause to our
343/// display implementation to expect to be able to format every enum case or struct member.
344///
345/// In fact, we would preferably only require `where _: Display` or `where _: Debug` where the
346/// format string actually requires it. However, while [`std::fmt` defines a formal syntax for
347/// `format!()`][format syntax], it *doesn't* expose the actual logic to parse the format string,
348/// which appears to live in [`rustc_parse_format`]. While we use the [`syn`] crate to parse rust
349/// syntax, it also doesn't currently provide any method to introspect a `format!()` string. It
350/// would be nice to contribute this upstream in [`syn`].
351///
352/// [format syntax]: std::fmt#syntax
353/// [`rustc_parse_format`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse_format/index.html
354fn generate_where_clause(generics: &Generics, where_clause: Option<&WhereClause>) -> WhereClause {
355    let mut where_clause = where_clause.cloned().unwrap_or_else(new_empty_where_clause);
356    let type_params: Vec<&TypeParam> = generics.type_params().collect();
357    ensure_where_clause_has_display_for_all_unconstrained_members(&mut where_clause, &type_params);
358    where_clause
359}
360
361fn impl_enum(input: &DeriveInput, data: &DataEnum) -> Result<TokenStream> {
362    let ty = &input.ident;
363    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
364    let where_clause = generate_where_clause(&input.generics, where_clause);
365
366    let helper = AttrsHelper::new(&input.attrs);
367
368    let displays = data
369        .variants
370        .iter()
371        .map(|variant| helper.display_with_input(&input.attrs, &variant.attrs))
372        .collect::<Result<Vec<_>>>()?;
373
374    if data.variants.is_empty() {
375        Ok({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "impl");
    ::quote::ToTokens::to_tokens(&impl_generics, &mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "core");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "fmt");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "Display");
    ::quote::__private::push_ident(&mut _s, "for");
    ::quote::ToTokens::to_tokens(&ty, &mut _s);
    ::quote::ToTokens::to_tokens(&ty_generics, &mut _s);
    ::quote::ToTokens::to_tokens(&where_clause, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "fmt");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_and(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "self");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "formatter");
                    ::quote::__private::push_colon(&mut _s);
                    ::quote::__private::push_and(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "mut");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "core");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "fmt");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "Formatter");
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "fmt");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Result");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "unreachable");
                    ::quote::__private::push_bang(&mut _s);
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::parse(&mut _s,
                                "\"empty enums cannot be instantiated and thus cannot be printed\"");
                            _s
                        });
                    _s
                });
            _s
        });
    _s
}quote! {
376            impl #impl_generics ::core::fmt::Display for #ty #ty_generics #where_clause {
377                fn fmt(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
378                    unreachable!("empty enums cannot be instantiated and thus cannot be printed")
379                }
380            }
381        })
382    } else if displays.iter().any(Option::is_some) {
383        let arms = data
384            .variants
385            .iter()
386            .zip(displays)
387            .map(|(variant, display)| {
388                let display =
389                    display.ok_or_else(|| Error::new_spanned(variant, "missing doc comment"))?;
390                let ident = &variant.ident;
391                Ok(match &variant.fields {
392                    Fields::Named(fields) => {
393                        let var = fields.named.iter().map(|field| &field.ident);
394                        {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "Self");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::ToTokens::to_tokens(&ident, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            {
                use ::quote::__private::ext::*;
                let mut _first = true;
                let has_iter = ::quote::__private::HasIterator::<false>;
                #[allow(unused_mut)]
                let (mut var, i) = var.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let var =
                        match var.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    if !_first { ::quote::__private::push_comma(&mut _s); }
                    _first = false;
                    ::quote::ToTokens::to_tokens(&var, &mut _s);
                }
            }
            _s
        });
    ::quote::__private::push_fat_arrow(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&display, &mut _s);
            _s
        });
    _s
}quote!(Self::#ident { #(#var),* } => { #display })
395                    }
396                    Fields::Unnamed(fields) => {
397                        let var = (0..fields.unnamed.len()).map(|i| match ::quote::__private::IdentFragmentAdapter(&i) {
    arg =>
        ::quote::__private::mk_ident(&::alloc::__export::must_use({
                        ::alloc::fmt::format(format_args!("_{0}", arg))
                    }), ::quote::__private::Option::None.or(arg.span())),
}format_ident!("_{}", i));
398                        {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "Self");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::ToTokens::to_tokens(&ident, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            {
                use ::quote::__private::ext::*;
                let mut _first = true;
                let has_iter = ::quote::__private::HasIterator::<false>;
                #[allow(unused_mut)]
                let (mut var, i) = var.quote_into_iter();
                let has_iter = has_iter | i;
                <_ as
                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                while true {
                    let var =
                        match var.next() {
                            Some(_x) => ::quote::__private::RepInterp(_x),
                            None => break,
                        };
                    if !_first { ::quote::__private::push_comma(&mut _s); }
                    _first = false;
                    ::quote::ToTokens::to_tokens(&var, &mut _s);
                }
            }
            _s
        });
    ::quote::__private::push_fat_arrow(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&display, &mut _s);
            _s
        });
    _s
}quote!(Self::#ident(#(#var),*) => { #display })
399                    }
400                    Fields::Unit => {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "Self");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::ToTokens::to_tokens(&ident, &mut _s);
    ::quote::__private::push_fat_arrow(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&display, &mut _s);
            _s
        });
    _s
}quote!(Self::#ident => { #display }),
401                })
402            })
403            .collect::<Result<Vec<_>>>()?;
404        Ok({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "impl");
    ::quote::ToTokens::to_tokens(&impl_generics, &mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "core");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "fmt");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "Display");
    ::quote::__private::push_ident(&mut _s, "for");
    ::quote::ToTokens::to_tokens(&ty, &mut _s);
    ::quote::ToTokens::to_tokens(&ty_generics, &mut _s);
    ::quote::ToTokens::to_tokens(&where_clause, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "fmt");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_and(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "self");
                    ::quote::__private::push_comma(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "formatter");
                    ::quote::__private::push_colon(&mut _s);
                    ::quote::__private::push_and(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "mut");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "core");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "fmt");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "Formatter");
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "core");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "fmt");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Result");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_pound(&mut _s);
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Bracket,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "allow");
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::__private::push_ident(&mut _s, "unused_variables");
                                    ::quote::__private::push_comma(&mut _s);
                                    ::quote::__private::push_ident(&mut _s,
                                        "unused_assignments");
                                    _s
                                });
                            _s
                        });
                    ::quote::__private::push_ident(&mut _s, "match");
                    ::quote::__private::push_ident(&mut _s, "self");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Brace,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            {
                                use ::quote::__private::ext::*;
                                let has_iter = ::quote::__private::HasIterator::<false>;
                                #[allow(unused_mut)]
                                let (mut arms, i) = arms.quote_into_iter();
                                let has_iter = has_iter | i;
                                <_ as
                                        ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                                while true {
                                    let arms =
                                        match arms.next() {
                                            Some(_x) => ::quote::__private::RepInterp(_x),
                                            None => break,
                                        };
                                    ::quote::ToTokens::to_tokens(&arms, &mut _s);
                                    ::quote::__private::push_comma(&mut _s);
                                }
                            }
                            _s
                        });
                    _s
                });
            _s
        });
    _s
}quote! {
405            impl #impl_generics ::core::fmt::Display for #ty #ty_generics #where_clause {
406                fn fmt(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
407                    #[allow(unused_variables, unused_assignments)]
408                    match self {
409                        #(#arms,)*
410                    }
411                }
412            }
413        })
414    } else {
415        Err(Error::new_spanned(input, "Missing doc comments"))
416    }
417}