1 use std::{fmt::Display, str::FromStr as _};
2 
3 use proc_macro2::{Ident, Span, TokenStream};
4 use quote::{quote, quote_spanned};
5 use syn::{
6     parse::Parser as _, punctuated::Punctuated, spanned::Spanned as _, Error, Result,
7 };
8 
9 use crate::utils;
10 use utils::{HashMap, HashSet};
11 
12 /// Provides the hook to expand `#[derive(Display)]` into an implementation of `From`
expand(input: &syn::DeriveInput, trait_name: &str) -> Result<TokenStream>13 pub fn expand(input: &syn::DeriveInput, trait_name: &str) -> Result<TokenStream> {
14     let trait_name = trait_name.trim_end_matches("Custom");
15     let trait_ident = syn::Ident::new(trait_name, Span::call_site());
16     let trait_path = &quote!(::core::fmt::#trait_ident);
17     let trait_attr = trait_name_to_attribute_name(trait_name);
18     let type_params = input
19         .generics
20         .type_params()
21         .map(|t| t.ident.clone())
22         .collect();
23 
24     let ParseResult {
25         arms,
26         bounds,
27         requires_helper,
28     } = State {
29         trait_path,
30         trait_attr,
31         input,
32         type_params,
33     }
34     .get_match_arms_and_extra_bounds()?;
35 
36     let generics = if !bounds.is_empty() {
37         let bounds: Vec<_> = bounds
38             .into_iter()
39             .map(|(ty, trait_names)| {
40                 let bounds: Vec<_> = trait_names
41                     .into_iter()
42                     .map(|bound| quote!(#bound))
43                     .collect();
44                 quote!(#ty: #(#bounds)+*)
45             })
46             .collect();
47         let where_clause = quote_spanned!(input.span()=> where #(#bounds),*);
48         utils::add_extra_where_clauses(&input.generics, where_clause)
49     } else {
50         input.generics.clone()
51     };
52     let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
53     let name = &input.ident;
54 
55     let helper_struct = if requires_helper {
56         display_as_helper_struct()
57     } else {
58         TokenStream::new()
59     };
60 
61     Ok(quote! {
62         impl #impl_generics #trait_path for #name #ty_generics #where_clause
63         {
64             #[allow(unused_variables)]
65             #[inline]
66             fn fmt(&self, _derive_more_display_formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
67                 #helper_struct
68 
69                 match self {
70                     #arms
71                     _ => Ok(()) // This is needed for empty enums
72                 }
73             }
74         }
75     })
76 }
77 
trait_name_to_attribute_name(trait_name: &str) -> &'static str78 fn trait_name_to_attribute_name(trait_name: &str) -> &'static str {
79     match trait_name {
80         "Display" => "display",
81         "Binary" => "binary",
82         "Octal" => "octal",
83         "LowerHex" => "lower_hex",
84         "UpperHex" => "upper_hex",
85         "LowerExp" => "lower_exp",
86         "UpperExp" => "upper_exp",
87         "Pointer" => "pointer",
88         "Debug" => "debug",
89         _ => unimplemented!(),
90     }
91 }
92 
attribute_name_to_trait_name(attribute_name: &str) -> &'static str93 fn attribute_name_to_trait_name(attribute_name: &str) -> &'static str {
94     match attribute_name {
95         "display" => "Display",
96         "binary" => "Binary",
97         "octal" => "Octal",
98         "lower_hex" => "LowerHex",
99         "upper_hex" => "UpperHex",
100         "lower_exp" => "LowerExp",
101         "upper_exp" => "UpperExp",
102         "pointer" => "Pointer",
103         _ => unreachable!(),
104     }
105 }
106 
trait_name_to_trait_bound(trait_name: &str) -> syn::TraitBound107 fn trait_name_to_trait_bound(trait_name: &str) -> syn::TraitBound {
108     let path_segments_iterator = vec!["core", "fmt", trait_name]
109         .into_iter()
110         .map(|segment| syn::PathSegment::from(Ident::new(segment, Span::call_site())));
111 
112     syn::TraitBound {
113         lifetimes: None,
114         modifier: syn::TraitBoundModifier::None,
115         paren_token: None,
116         path: syn::Path {
117             leading_colon: Some(syn::Token![::](Span::call_site())),
118             segments: path_segments_iterator.collect(),
119         },
120     }
121 }
122 
123 /// Create a helper struct that is required by some `Display` impls.
124 ///
125 /// The struct is necessary in cases where `Display` is derived for an enum
126 /// with an outer `#[display(fmt = "...")]` attribute and if that outer
127 /// format-string contains a single placeholder. In that case, we have to
128 /// format twice:
129 ///
130 /// - we need to format each variant according to its own, optional
131 ///   format-string,
132 /// - we then need to insert this formatted variant into the outer
133 ///   format-string.
134 ///
135 /// This helper struct solves this as follows:
136 /// - formatting the whole object inserts the helper struct into the outer
137 ///   format string,
138 /// - upon being formatted, the helper struct calls an inner closure to produce
139 ///   its formatted result,
140 /// - the closure in turn uses the inner, optional format-string to produce its
141 ///   result. If there is no inner format-string, it falls back to plain
142 ///   `$trait::fmt()`.
display_as_helper_struct() -> TokenStream143 fn display_as_helper_struct() -> TokenStream {
144     quote! {
145         struct _derive_more_DisplayAs<F>(F)
146         where
147             F: ::core::ops::Fn(&mut ::core::fmt::Formatter) -> ::core::fmt::Result;
148 
149         const _derive_more_DisplayAs_impl: () = {
150             impl<F> ::core::fmt::Display for _derive_more_DisplayAs<F>
151             where
152                 F: ::core::ops::Fn(&mut ::core::fmt::Formatter) -> ::core::fmt::Result
153             {
154                 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
155                     (self.0)(f)
156                 }
157             }
158         };
159     }
160 }
161 
162 /// Result type of `State::get_match_arms_and_extra_bounds()`.
163 #[derive(Default)]
164 struct ParseResult {
165     /// The match arms destructuring `self`.
166     arms: TokenStream,
167     /// Any trait bounds that may be required.
168     bounds: HashMap<syn::Type, HashSet<syn::TraitBound>>,
169     /// `true` if the Display impl requires the `DisplayAs` helper struct.
170     requires_helper: bool,
171 }
172 
173 struct State<'a, 'b> {
174     trait_path: &'b TokenStream,
175     trait_attr: &'static str,
176     input: &'a syn::DeriveInput,
177     type_params: HashSet<Ident>,
178 }
179 
180 impl<'a, 'b> State<'a, 'b> {
get_proper_fmt_syntax(&self) -> impl Display181     fn get_proper_fmt_syntax(&self) -> impl Display {
182         format!(
183             r#"Proper syntax: #[{}(fmt = "My format", "arg1", "arg2")]"#,
184             self.trait_attr
185         )
186     }
get_proper_bound_syntax(&self) -> impl Display187     fn get_proper_bound_syntax(&self) -> impl Display {
188         format!(
189             "Proper syntax: #[{}(bound = \"T, U: Trait1 + Trait2, V: Trait3\")]",
190             self.trait_attr
191         )
192     }
193 
get_matcher(&self, fields: &syn::Fields) -> TokenStream194     fn get_matcher(&self, fields: &syn::Fields) -> TokenStream {
195         match fields {
196             syn::Fields::Unit => TokenStream::new(),
197             syn::Fields::Unnamed(fields) => {
198                 let fields: TokenStream = (0..fields.unnamed.len())
199                     .map(|n| {
200                         let i = Ident::new(&format!("_{}", n), Span::call_site());
201                         quote!(#i,)
202                     })
203                     .collect();
204                 quote!((#fields))
205             }
206             syn::Fields::Named(fields) => {
207                 let fields: TokenStream = fields
208                     .named
209                     .iter()
210                     .map(|f| {
211                         let i = f.ident.as_ref().unwrap();
212                         quote!(#i,)
213                     })
214                     .collect();
215                 quote!({#fields})
216             }
217         }
218     }
find_meta( &self, attrs: &[syn::Attribute], meta_key: &str, ) -> Result<Option<syn::Meta>>219     fn find_meta(
220         &self,
221         attrs: &[syn::Attribute],
222         meta_key: &str,
223     ) -> Result<Option<syn::Meta>> {
224         let mut metas = Vec::new();
225         for meta in attrs.iter().filter_map(|attr| attr.parse_meta().ok()) {
226             let meta_list = match &meta {
227                 syn::Meta::List(meta) => meta,
228                 _ => continue,
229             };
230 
231             if !meta_list.path.is_ident(self.trait_attr) {
232                 continue;
233             }
234 
235             use syn::{Meta, NestedMeta};
236             let meta_nv = match meta_list.nested.first() {
237                 Some(NestedMeta::Meta(Meta::NameValue(meta_nv))) => meta_nv,
238                 _ => {
239                     // If the given attribute is not MetaNameValue, it most likely implies that the
240                     // user is writing an incorrect format. For example:
241                     // - `#[display()]`
242                     // - `#[display("foo")]`
243                     // - `#[display(foo)]`
244                     return Err(Error::new(
245                         meta.span(),
246                         format!(
247                             r#"The format for this attribute cannot be parsed. Correct format: `#[{}({} = "...")]`"#,
248                             self.trait_attr, meta_key
249                         ),
250                     ));
251                 }
252             };
253 
254             if meta_nv.path.is_ident(meta_key) {
255                 metas.push(meta);
256             }
257         }
258 
259         let mut iter = metas.into_iter();
260         let meta = iter.next();
261         if iter.next().is_none() {
262             Ok(meta)
263         } else {
264             Err(Error::new(meta.span(), "Too many attributes specified"))
265         }
266     }
parse_meta_bounds( &self, bounds: &syn::LitStr, ) -> Result<HashMap<syn::Type, HashSet<syn::TraitBound>>>267     fn parse_meta_bounds(
268         &self,
269         bounds: &syn::LitStr,
270     ) -> Result<HashMap<syn::Type, HashSet<syn::TraitBound>>> {
271         let span = bounds.span();
272 
273         let input = bounds.value();
274         let tokens = TokenStream::from_str(&input)?;
275         let parser = Punctuated::<syn::GenericParam, syn::Token![,]>::parse_terminated;
276 
277         let generic_params = parser
278             .parse2(tokens)
279             .map_err(|error| Error::new(span, error.to_string()))?;
280 
281         if generic_params.is_empty() {
282             return Err(Error::new(span, "No bounds specified"));
283         }
284 
285         let mut bounds = HashMap::default();
286 
287         for generic_param in generic_params {
288             let type_param = match generic_param {
289                 syn::GenericParam::Type(type_param) => type_param,
290                 _ => return Err(Error::new(span, "Only trait bounds allowed")),
291             };
292 
293             if !self.type_params.contains(&type_param.ident) {
294                 return Err(Error::new(
295                     span,
296                     "Unknown generic type argument specified",
297                 ));
298             } else if !type_param.attrs.is_empty() {
299                 return Err(Error::new(span, "Attributes aren't allowed"));
300             } else if type_param.eq_token.is_some() || type_param.default.is_some() {
301                 return Err(Error::new(span, "Default type parameters aren't allowed"));
302             }
303 
304             let ident = type_param.ident.to_string();
305 
306             let ty = syn::Type::Path(syn::TypePath {
307                 qself: None,
308                 path: type_param.ident.into(),
309             });
310             let bounds = bounds.entry(ty).or_insert_with(HashSet::default);
311 
312             for bound in type_param.bounds {
313                 let bound = match bound {
314                     syn::TypeParamBound::Trait(bound) => bound,
315                     _ => return Err(Error::new(span, "Only trait bounds allowed")),
316                 };
317 
318                 if bound.lifetimes.is_some() {
319                     return Err(Error::new(
320                         span,
321                         "Higher-rank trait bounds aren't allowed",
322                     ));
323                 }
324 
325                 bounds.insert(bound);
326             }
327 
328             if bounds.is_empty() {
329                 return Err(Error::new(
330                     span,
331                     format!("No bounds specified for type parameter {}", ident),
332                 ));
333             }
334         }
335 
336         Ok(bounds)
337     }
parse_meta_fmt( &self, meta: &syn::Meta, outer_enum: bool, ) -> Result<(TokenStream, bool)>338     fn parse_meta_fmt(
339         &self,
340         meta: &syn::Meta,
341         outer_enum: bool,
342     ) -> Result<(TokenStream, bool)> {
343         let list = match meta {
344             syn::Meta::List(list) => list,
345             _ => {
346                 return Err(Error::new(meta.span(), self.get_proper_fmt_syntax()));
347             }
348         };
349 
350         match &list.nested[0] {
351             syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
352                 path,
353                 lit: syn::Lit::Str(fmt),
354                 ..
355             })) => match path {
356                 op if op.segments.first().expect("path shouldn't be empty").ident
357                     == "fmt" =>
358                 {
359                     let expected_affix_usage = "outer `enum` `fmt` is an affix spec that expects no args and at most 1 placeholder for inner variant display";
360                     if outer_enum {
361                         if list.nested.iter().skip(1).count() != 0 {
362                             return Err(Error::new(
363                                 list.nested[1].span(),
364                                 expected_affix_usage,
365                             ));
366                         }
367                         // TODO: Check for a single `Display` group?
368                         let fmt_string = match &list.nested[0] {
369                             syn::NestedMeta::Meta(syn::Meta::NameValue(
370                                 syn::MetaNameValue {
371                                     path,
372                                     lit: syn::Lit::Str(s),
373                                     ..
374                                 },
375                             )) if path
376                                 .segments
377                                 .first()
378                                 .expect("path shouldn't be empty")
379                                 .ident
380                                 == "fmt" =>
381                             {
382                                 s.value()
383                             }
384                             // This one has been checked already in get_meta_fmt() method.
385                             _ => unreachable!(),
386                         };
387 
388                         let num_placeholders =
389                             Placeholder::parse_fmt_string(&fmt_string).len();
390                         if num_placeholders > 1 {
391                             return Err(Error::new(
392                                 list.nested[1].span(),
393                                 expected_affix_usage,
394                             ));
395                         }
396                         if num_placeholders == 1 {
397                             return Ok((quote_spanned!(fmt.span()=> #fmt), true));
398                         }
399                     }
400                     let args = list
401                         .nested
402                         .iter()
403                         .skip(1) // skip fmt = "..."
404                         .try_fold(TokenStream::new(), |args, arg| {
405                             let arg = match arg {
406                                 syn::NestedMeta::Lit(syn::Lit::Str(s)) => s,
407                                 syn::NestedMeta::Meta(syn::Meta::Path(i)) => {
408                                     return Ok(quote_spanned!(list.span()=> #args #i,));
409                                 }
410                                 _ => {
411                                     return Err(Error::new(
412                                         arg.span(),
413                                         self.get_proper_fmt_syntax(),
414                                     ))
415                                 }
416                             };
417                             let arg: TokenStream =
418                                 arg.parse().map_err(|e| Error::new(arg.span(), e))?;
419                             Ok(quote_spanned!(list.span()=> #args #arg,))
420                         })?;
421 
422                     Ok((
423                         quote_spanned!(meta.span()=> write!(_derive_more_display_formatter, #fmt, #args)),
424                         false,
425                     ))
426                 }
427                 _ => Err(Error::new(
428                     list.nested[0].span(),
429                     self.get_proper_fmt_syntax(),
430                 )),
431             },
432             _ => Err(Error::new(
433                 list.nested[0].span(),
434                 self.get_proper_fmt_syntax(),
435             )),
436         }
437     }
infer_fmt(&self, fields: &syn::Fields, name: &Ident) -> Result<TokenStream>438     fn infer_fmt(&self, fields: &syn::Fields, name: &Ident) -> Result<TokenStream> {
439         let fields = match fields {
440             syn::Fields::Unit => {
441                 return Ok(quote!(
442                     _derive_more_display_formatter.write_str(stringify!(#name))
443                 ))
444             }
445             syn::Fields::Named(fields) => &fields.named,
446             syn::Fields::Unnamed(fields) => &fields.unnamed,
447         };
448         if fields.is_empty() {
449             return Ok(quote!(
450                 _derive_more_display_formatter.write_str(stringify!(#name))
451             ));
452         } else if fields.len() > 1 {
453             return Err(Error::new(
454                 fields.span(),
455                 "Cannot automatically infer format for types with more than 1 field",
456             ));
457         }
458 
459         let trait_path = self.trait_path;
460         if let Some(ident) = &fields.iter().next().as_ref().unwrap().ident {
461             Ok(quote!(#trait_path::fmt(#ident, _derive_more_display_formatter)))
462         } else {
463             Ok(quote!(#trait_path::fmt(_0, _derive_more_display_formatter)))
464         }
465     }
get_match_arms_and_extra_bounds(&self) -> Result<ParseResult>466     fn get_match_arms_and_extra_bounds(&self) -> Result<ParseResult> {
467         let result: Result<_> = match &self.input.data {
468             syn::Data::Enum(e) => {
469                 match self
470                     .find_meta(&self.input.attrs, "fmt")
471                     .and_then(|m| m.map(|m| self.parse_meta_fmt(&m, true)).transpose())?
472                 {
473                     // #[display(fmt = "no placeholder")] on whole enum.
474                     Some((fmt, false)) => {
475                         e.variants.iter().try_for_each(|v| {
476                             if let Some(meta) = self.find_meta(&v.attrs, "fmt")? {
477                                 Err(Error::new(
478                                     meta.span(),
479                                     "`fmt` cannot be used on variant when the whole enum has a format string without a placeholder, maybe you want to add a placeholder?",
480                                 ))
481                             } else {
482                                 Ok(())
483                             }
484                         })?;
485 
486                         Ok(ParseResult {
487                             arms: quote_spanned!(self.input.span()=> _ => #fmt,),
488                             bounds: HashMap::default(),
489                             requires_helper: false,
490                         })
491                     }
492                     // #[display(fmt = "one placeholder: {}")] on whole enum.
493                     Some((outer_fmt, true)) => {
494                         let fmt: Result<TokenStream> = e.variants.iter().try_fold(TokenStream::new(), |arms, v| {
495                             let matcher = self.get_matcher(&v.fields);
496                             let fmt = if let Some(meta) = self.find_meta(&v.attrs, "fmt")? {
497                                 self.parse_meta_fmt(&meta, false)?.0
498                             } else {
499                                 self.infer_fmt(&v.fields, &v.ident)?
500                             };
501                             let name = &self.input.ident;
502                             let v_name = &v.ident;
503                             Ok(quote_spanned!(fmt.span()=> #arms #name::#v_name #matcher => write!(
504                                 _derive_more_display_formatter,
505                                 #outer_fmt,
506                                 _derive_more_DisplayAs(|_derive_more_display_formatter| #fmt)
507                             ),))
508                         });
509                         let fmt = fmt?;
510                         Ok(ParseResult {
511                             arms: quote_spanned!(self.input.span()=> #fmt),
512                             bounds: HashMap::default(),
513                             requires_helper: true,
514                         })
515                     }
516                     // No format attribute on whole enum.
517                     None => e.variants.iter().try_fold(ParseResult::default(), |result, v| {
518                         let ParseResult{ arms, mut bounds, requires_helper } = result;
519                         let matcher = self.get_matcher(&v.fields);
520                         let name = &self.input.ident;
521                         let v_name = &v.ident;
522                         let fmt: TokenStream;
523                         let these_bounds: HashMap<_, _>;
524 
525                         if let Some(meta) = self.find_meta(&v.attrs, "fmt")? {
526                             fmt = self.parse_meta_fmt(&meta, false)?.0;
527                             these_bounds = self.get_used_type_params_bounds(&v.fields, &meta);
528                         } else {
529                             fmt = self.infer_fmt(&v.fields, v_name)?;
530                             these_bounds = self.infer_type_params_bounds(&v.fields);
531                         };
532                         these_bounds.into_iter().for_each(|(ty, trait_names)| {
533                             bounds.entry(ty).or_default().extend(trait_names)
534                         });
535                         let arms = quote_spanned!(self.input.span()=> #arms #name::#v_name #matcher => #fmt,);
536 
537                         Ok(ParseResult{ arms, bounds, requires_helper })
538                     }),
539                 }
540             }
541             syn::Data::Struct(s) => {
542                 let matcher = self.get_matcher(&s.fields);
543                 let name = &self.input.ident;
544                 let fmt: TokenStream;
545                 let bounds: HashMap<_, _>;
546 
547                 if let Some(meta) = self.find_meta(&self.input.attrs, "fmt")? {
548                     fmt = self.parse_meta_fmt(&meta, false)?.0;
549                     bounds = self.get_used_type_params_bounds(&s.fields, &meta);
550                 } else {
551                     fmt = self.infer_fmt(&s.fields, name)?;
552                     bounds = self.infer_type_params_bounds(&s.fields);
553                 }
554 
555                 Ok(ParseResult {
556                     arms: quote_spanned!(self.input.span()=> #name #matcher => #fmt,),
557                     bounds,
558                     requires_helper: false,
559                 })
560             }
561             syn::Data::Union(_) => {
562                 let meta =
563                     self.find_meta(&self.input.attrs, "fmt")?.ok_or_else(|| {
564                         Error::new(
565                             self.input.span(),
566                             "Cannot automatically infer format for unions",
567                         )
568                     })?;
569                 let fmt = self.parse_meta_fmt(&meta, false)?.0;
570 
571                 Ok(ParseResult {
572                     arms: quote_spanned!(self.input.span()=> _ => #fmt,),
573                     bounds: HashMap::default(),
574                     requires_helper: false,
575                 })
576             }
577         };
578 
579         let mut result = result?;
580 
581         let meta = match self.find_meta(&self.input.attrs, "bound")? {
582             Some(meta) => meta,
583             _ => return Ok(result),
584         };
585 
586         let span = meta.span();
587 
588         let meta = match meta {
589             syn::Meta::List(meta) => meta.nested,
590             _ => return Err(Error::new(span, self.get_proper_bound_syntax())),
591         };
592 
593         if meta.len() != 1 {
594             return Err(Error::new(span, self.get_proper_bound_syntax()));
595         }
596 
597         let meta = match &meta[0] {
598             syn::NestedMeta::Meta(syn::Meta::NameValue(meta)) => meta,
599             _ => return Err(Error::new(span, self.get_proper_bound_syntax())),
600         };
601 
602         let extra_bounds = match &meta.lit {
603             syn::Lit::Str(extra_bounds) => extra_bounds,
604             _ => return Err(Error::new(span, self.get_proper_bound_syntax())),
605         };
606 
607         let extra_bounds = self.parse_meta_bounds(extra_bounds)?;
608 
609         extra_bounds.into_iter().for_each(|(ty, trait_names)| {
610             result.bounds.entry(ty).or_default().extend(trait_names)
611         });
612 
613         Ok(result)
614     }
get_used_type_params_bounds( &self, fields: &syn::Fields, meta: &syn::Meta, ) -> HashMap<syn::Type, HashSet<syn::TraitBound>>615     fn get_used_type_params_bounds(
616         &self,
617         fields: &syn::Fields,
618         meta: &syn::Meta,
619     ) -> HashMap<syn::Type, HashSet<syn::TraitBound>> {
620         if self.type_params.is_empty() {
621             return HashMap::default();
622         }
623 
624         let fields_type_params: HashMap<syn::Path, _> = fields
625             .iter()
626             .enumerate()
627             .filter_map(|(i, field)| {
628                 utils::get_if_type_parameter_used_in_type(&self.type_params, &field.ty)
629                     .map(|ty| {
630                         (
631                             field
632                                 .ident
633                                 .clone()
634                                 .unwrap_or_else(|| {
635                                     Ident::new(&format!("_{}", i), Span::call_site())
636                                 })
637                                 .into(),
638                             ty,
639                         )
640                     })
641             })
642             .collect();
643         if fields_type_params.is_empty() {
644             return HashMap::default();
645         }
646 
647         let list = match meta {
648             syn::Meta::List(list) => list,
649             // This one has been checked already in get_meta_fmt() method.
650             _ => unreachable!(),
651         };
652         let fmt_args: HashMap<_, _> = list
653             .nested
654             .iter()
655             .skip(1) // skip fmt = "..."
656             .enumerate()
657             .filter_map(|(i, arg)| match arg {
658                 syn::NestedMeta::Lit(syn::Lit::Str(ref s)) => {
659                     syn::parse_str(&s.value()).ok().map(|id| (i, id))
660                 }
661                 syn::NestedMeta::Meta(syn::Meta::Path(ref id)) => Some((i, id.clone())),
662                 // This one has been checked already in get_meta_fmt() method.
663                 _ => unreachable!(),
664             })
665             .collect();
666         if fmt_args.is_empty() {
667             return HashMap::default();
668         }
669         let fmt_string = match &list.nested[0] {
670             syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
671                 path,
672                 lit: syn::Lit::Str(s),
673                 ..
674             })) if path
675                 .segments
676                 .first()
677                 .expect("path shouldn't be empty")
678                 .ident
679                 == "fmt" =>
680             {
681                 s.value()
682             }
683             // This one has been checked already in get_meta_fmt() method.
684             _ => unreachable!(),
685         };
686 
687         Placeholder::parse_fmt_string(&fmt_string).into_iter().fold(
688             HashMap::default(),
689             |mut bounds, pl| {
690                 if let Some(arg) = fmt_args.get(&pl.position) {
691                     if fields_type_params.contains_key(arg) {
692                         bounds
693                             .entry(fields_type_params[arg].clone())
694                             .or_insert_with(HashSet::default)
695                             .insert(trait_name_to_trait_bound(pl.trait_name));
696                     }
697                 }
698                 bounds
699             },
700         )
701     }
infer_type_params_bounds( &self, fields: &syn::Fields, ) -> HashMap<syn::Type, HashSet<syn::TraitBound>>702     fn infer_type_params_bounds(
703         &self,
704         fields: &syn::Fields,
705     ) -> HashMap<syn::Type, HashSet<syn::TraitBound>> {
706         if self.type_params.is_empty() {
707             return HashMap::default();
708         }
709         if let syn::Fields::Unit = fields {
710             return HashMap::default();
711         }
712         // infer_fmt() uses only first field.
713         fields
714             .iter()
715             .take(1)
716             .filter_map(|field| {
717                 utils::get_if_type_parameter_used_in_type(&self.type_params, &field.ty)
718                     .map(|ty| {
719                         (
720                             ty,
721                             [trait_name_to_trait_bound(attribute_name_to_trait_name(
722                                 self.trait_attr,
723                             ))]
724                             .iter()
725                             .cloned()
726                             .collect(),
727                         )
728                     })
729             })
730             .collect()
731     }
732 }
733 
734 /// Representation of formatting placeholder.
735 #[derive(Debug, PartialEq)]
736 struct Placeholder {
737     /// Position of formatting argument to be used for this placeholder.
738     position: usize,
739     /// Name of [`std::fmt`] trait to be used for rendering this placeholder.
740     trait_name: &'static str,
741 }
742 
743 impl Placeholder {
744     /// Parses [`Placeholder`]s from a given formatting string.
parse_fmt_string(s: &str) -> Vec<Placeholder>745     fn parse_fmt_string(s: &str) -> Vec<Placeholder> {
746         let mut n = 0;
747         crate::parsing::all_placeholders(s)
748             .into_iter()
749             .flatten()
750             .map(|m| {
751                 let (maybe_arg, maybe_typ) = crate::parsing::format(m).unwrap();
752                 let position = maybe_arg.unwrap_or_else(|| {
753                     // Assign "the next argument".
754                     // https://doc.rust-lang.org/stable/std/fmt/index.html#positional-parameters
755                     n += 1;
756                     n - 1
757                 });
758                 let typ = maybe_typ.unwrap_or_default();
759                 let trait_name = match typ {
760                     "" => "Display",
761                     "?" | "x?" | "X?" => "Debug",
762                     "o" => "Octal",
763                     "x" => "LowerHex",
764                     "X" => "UpperHex",
765                     "p" => "Pointer",
766                     "b" => "Binary",
767                     "e" => "LowerExp",
768                     "E" => "UpperExp",
769                     _ => unreachable!(),
770                 };
771                 Placeholder {
772                     position,
773                     trait_name,
774                 }
775             })
776             .collect()
777     }
778 }
779 
780 #[cfg(test)]
781 mod regex_maybe_placeholder_spec {
782 
783     #[test]
parses_placeholders_and_omits_escaped()784     fn parses_placeholders_and_omits_escaped() {
785         let fmt_string = "{}, {:?}, {{}}, {{{1:0$}}}";
786         let placeholders: Vec<_> = crate::parsing::all_placeholders(&fmt_string)
787             .into_iter()
788             .flatten()
789             .collect();
790         assert_eq!(placeholders, vec!["{}", "{:?}", "{1:0$}"]);
791     }
792 }
793 
794 #[cfg(test)]
795 mod regex_placeholder_format_spec {
796 
797     #[test]
detects_type()798     fn detects_type() {
799         for (p, expected) in vec![
800             ("{}", ""),
801             ("{:?}", "?"),
802             ("{:x?}", "x?"),
803             ("{:X?}", "X?"),
804             ("{:o}", "o"),
805             ("{:x}", "x"),
806             ("{:X}", "X"),
807             ("{:p}", "p"),
808             ("{:b}", "b"),
809             ("{:e}", "e"),
810             ("{:E}", "E"),
811             ("{:.*}", ""),
812             ("{8}", ""),
813             ("{:04}", ""),
814             ("{1:0$}", ""),
815             ("{:width$}", ""),
816             ("{9:>8.*}", ""),
817             ("{2:.1$x}", "x"),
818         ] {
819             let typ = crate::parsing::format(p).unwrap().1.unwrap_or_default();
820             assert_eq!(typ, expected);
821         }
822     }
823 
824     #[test]
detects_arg()825     fn detects_arg() {
826         for (p, expected) in vec![
827             ("{}", ""),
828             ("{0:?}", "0"),
829             ("{12:x?}", "12"),
830             ("{3:X?}", "3"),
831             ("{5:o}", "5"),
832             ("{6:x}", "6"),
833             ("{:X}", ""),
834             ("{8}", "8"),
835             ("{:04}", ""),
836             ("{1:0$}", "1"),
837             ("{:width$}", ""),
838             ("{9:>8.*}", "9"),
839             ("{2:.1$x}", "2"),
840         ] {
841             let arg = crate::parsing::format(p)
842                 .unwrap()
843                 .0
844                 .map(|s| s.to_string())
845                 .unwrap_or_default();
846             assert_eq!(arg, String::from(expected));
847         }
848     }
849 }
850 
851 #[cfg(test)]
852 mod placeholder_parse_fmt_string_spec {
853     use super::*;
854 
855     #[test]
indicates_position_and_trait_name_for_each_fmt_placeholder()856     fn indicates_position_and_trait_name_for_each_fmt_placeholder() {
857         let fmt_string = "{},{:?},{{}},{{{1:0$}}}-{2:.1$x}{0:#?}{:width$}";
858         assert_eq!(
859             Placeholder::parse_fmt_string(&fmt_string),
860             vec![
861                 Placeholder {
862                     position: 0,
863                     trait_name: "Display",
864                 },
865                 Placeholder {
866                     position: 1,
867                     trait_name: "Debug",
868                 },
869                 Placeholder {
870                     position: 1,
871                     trait_name: "Display",
872                 },
873                 Placeholder {
874                     position: 2,
875                     trait_name: "LowerHex",
876                 },
877                 Placeholder {
878                     position: 0,
879                     trait_name: "Debug",
880                 },
881                 Placeholder {
882                     position: 2,
883                     trait_name: "Display",
884                 },
885             ],
886         )
887     }
888 }
889