derivative/
attr.rs

1use proc_macro2;
2use syn;
3use syn::spanned::Spanned;
4
5/// Represent the `derivative` attributes on the input type (`struct`/`enum`).
6#[derive(Debug, Default)]
7pub struct Input {
8    /// Whether `Clone` is present and its specific attributes.
9    pub clone: Option<InputClone>,
10    /// Whether `Copy` is present and its specific attributes.
11    pub copy: Option<InputCopy>,
12    /// Whether `Debug` is present and its specific attributes.
13    pub debug: Option<InputDebug>,
14    /// Whether `Default` is present and its specific attributes.
15    pub default: Option<InputDefault>,
16    /// Whether `Eq` is present and its specific attributes.
17    pub eq: Option<InputEq>,
18    /// Whether `Hash` is present and its specific attributes.
19    pub hash: Option<InputHash>,
20    /// Whether `PartialEq` is present and its specific attributes.
21    pub partial_eq: Option<InputPartialEq>,
22    /// Whether `PartialOrd` is present and its specific attributes.
23    pub partial_ord: Option<InputPartialOrd>,
24    /// Whether `Ord` is present and its specific attributes.
25    pub ord: Option<InputOrd>,
26    pub is_packed: bool,
27}
28
29#[derive(Debug, Default)]
30/// Represent the `derivative` attributes on a field.
31pub struct Field {
32    /// The parameters for `Clone`.
33    clone: FieldClone,
34    /// The parameters for `Copy`.
35    copy_bound: Option<Vec<syn::WherePredicate>>,
36    /// The parameters for `Debug`.
37    debug: FieldDebug,
38    /// The parameters for `Default`.
39    default: FieldDefault,
40    /// The parameters for `Eq`.
41    eq_bound: Option<Vec<syn::WherePredicate>>,
42    /// The parameters for `Hash`.
43    hash: FieldHash,
44    /// The parameters for `PartialEq`.
45    partial_eq: FieldPartialEq,
46    /// The parameters for `PartialOrd`.
47    partial_ord: FieldPartialOrd,
48    /// The parameters for `Ord`.
49    ord: FieldOrd,
50}
51
52#[derive(Debug, Default)]
53/// Represent the `derivative(Clone(…))` attributes on an input.
54pub struct InputClone {
55    /// The `bound` attribute if present and the corresponding bounds.
56    bounds: Option<Vec<syn::WherePredicate>>,
57    /// Whether the implementation should have an explicit `clone_from`.
58    pub clone_from: bool,
59}
60
61#[derive(Debug, Default)]
62/// Represent the `derivative(Clone(…))` attributes on an input.
63pub struct InputCopy {
64    /// The `bound` attribute if present and the corresponding bounds.
65    bounds: Option<Vec<syn::WherePredicate>>,
66}
67
68#[derive(Debug, Default)]
69/// Represent the `derivative(Debug(…))` attributes on an input.
70pub struct InputDebug {
71    /// The `bound` attribute if present and the corresponding bounds.
72    bounds: Option<Vec<syn::WherePredicate>>,
73    /// Whether the type is marked `transparent`.
74    pub transparent: bool,
75}
76
77#[derive(Debug, Default)]
78/// Represent the `derivative(Default(…))` attributes on an input.
79pub struct InputDefault {
80    /// The `bound` attribute if present and the corresponding bounds.
81    bounds: Option<Vec<syn::WherePredicate>>,
82    /// Whether the type is marked with `new`.
83    pub new: bool,
84}
85
86#[derive(Debug, Default)]
87/// Represent the `derivative(Eq(…))` attributes on an input.
88pub struct InputEq {
89    /// The `bound` attribute if present and the corresponding bounds.
90    bounds: Option<Vec<syn::WherePredicate>>,
91}
92
93#[derive(Debug, Default)]
94/// Represent the `derivative(Hash(…))` attributes on an input.
95pub struct InputHash {
96    /// The `bound` attribute if present and the corresponding bounds.
97    bounds: Option<Vec<syn::WherePredicate>>,
98}
99
100#[derive(Debug, Default)]
101/// Represent the `derivative(PartialEq(…))` attributes on an input.
102pub struct InputPartialEq {
103    /// The `bound` attribute if present and the corresponding bounds.
104    bounds: Option<Vec<syn::WherePredicate>>,
105}
106
107#[derive(Debug, Default)]
108/// Represent the `derivative(PartialOrd(…))` attributes on an input.
109pub struct InputPartialOrd {
110    /// The `bound` attribute if present and the corresponding bounds.
111    bounds: Option<Vec<syn::WherePredicate>>,
112    /// Allow `derivative(PartialOrd)` on enums:
113    on_enum: bool,
114}
115
116#[derive(Debug, Default)]
117/// Represent the `derivative(Ord(…))` attributes on an input.
118pub struct InputOrd {
119    /// The `bound` attribute if present and the corresponding bounds.
120    bounds: Option<Vec<syn::WherePredicate>>,
121    /// Allow `derivative(Ord)` on enums:
122    on_enum: bool,
123}
124
125#[derive(Debug, Default)]
126/// Represents the `derivative(Clone(…))` attributes on a field.
127pub struct FieldClone {
128    /// The `bound` attribute if present and the corresponding bounds.
129    bounds: Option<Vec<syn::WherePredicate>>,
130    /// The `clone_with` attribute if present and the path to the cloning function.
131    clone_with: Option<syn::Path>,
132}
133
134#[derive(Debug, Default)]
135/// Represents the `derivative(Debug(…))` attributes on a field.
136pub struct FieldDebug {
137    /// The `bound` attribute if present and the corresponding bounds.
138    bounds: Option<Vec<syn::WherePredicate>>,
139    /// The `format_with` attribute if present and the path to the formatting function.
140    format_with: Option<syn::Path>,
141    /// Whether the field is to be ignored from output.
142    ignore: bool,
143}
144
145#[derive(Debug, Default)]
146/// Represent the `derivative(Default(…))` attributes on a field.
147pub struct FieldDefault {
148    /// The `bound` attribute if present and the corresponding bounds.
149    bounds: Option<Vec<syn::WherePredicate>>,
150    /// The default value for the field if present.
151    pub value: Option<proc_macro2::TokenStream>,
152}
153
154#[derive(Debug, Default)]
155/// Represents the `derivative(Hash(…))` attributes on a field.
156pub struct FieldHash {
157    /// The `bound` attribute if present and the corresponding bounds.
158    bounds: Option<Vec<syn::WherePredicate>>,
159    /// The `hash_with` attribute if present and the path to the hashing function.
160    hash_with: Option<syn::Path>,
161    /// Whether the field is to be ignored when hashing.
162    ignore: bool,
163}
164
165#[derive(Debug, Default)]
166/// Represent the `derivative(PartialEq(…))` attributes on a field.
167pub struct FieldPartialEq {
168    /// The `bound` attribute if present and the corresponding bounds.
169    bounds: Option<Vec<syn::WherePredicate>>,
170    /// The `compare_with` attribute if present and the path to the comparison function.
171    compare_with: Option<syn::Path>,
172    /// Whether the field is to be ignored when comparing.
173    ignore: bool,
174}
175
176#[derive(Debug, Default)]
177/// Represent the `derivative(PartialOrd(…))` attributes on a field.
178pub struct FieldPartialOrd {
179    /// The `bound` attribute if present and the corresponding bounds.
180    bounds: Option<Vec<syn::WherePredicate>>,
181    /// The `compare_with` attribute if present and the path to the comparison function.
182    compare_with: Option<syn::Path>,
183    /// Whether the field is to be ignored when comparing.
184    ignore: bool,
185}
186
187#[derive(Debug, Default)]
188/// Represent the `derivative(Ord(…))` attributes on a field.
189pub struct FieldOrd {
190    /// The `bound` attribute if present and the corresponding bounds.
191    bounds: Option<Vec<syn::WherePredicate>>,
192    /// The `compare_with` attribute if present and the path to the comparison function.
193    compare_with: Option<syn::Path>,
194    /// Whether the field is to be ignored when comparing.
195    ignore: bool,
196}
197
198macro_rules! for_all_attr {
199    ($errors:ident; for ($name:ident, $value:ident) in $attrs:expr; $($body:tt)*) => {
200        for meta_items in $attrs.iter() {
201            let meta_items = derivative_attribute(meta_items, $errors);
202            if let Some(meta_items) = meta_items {
203                for meta_item in meta_items.iter() {
204                    let meta_item = read_items(meta_item, $errors);
205                    let MetaItem($name, $value) = try!(meta_item);
206                    match $name.to_string().as_ref() {
207                        $($body)*
208                    }
209                }
210            }
211        }
212    };
213}
214
215macro_rules! match_attributes {
216    ($errors:ident for $trait:expr; let Some($name:ident) = $unwrapped:expr; for $value:ident in $values:expr; $($body:tt)* ) => {
217        let mut $name = $unwrapped.take().unwrap_or_default();
218
219        match_attributes! {
220            $errors for $trait;
221            for $value in $values;
222            $($body)*
223        }
224
225        $unwrapped = Some($name);
226    };
227
228    ($errors:ident for $trait:expr; for $value:ident in $values:expr; $($body:tt)* ) => {
229        for (name, $value) in $values {
230            match name {
231                Some(ident) => {
232                    match ident.to_string().as_ref() {
233                        $($body)*
234                        unknown => {
235                            let message = format!("Unknown attribute `{}` for trait `{}`", unknown, $trait);
236                            $errors.extend(quote_spanned! {ident.span()=>
237                                compile_error!(#message);
238                            });
239                        }
240                    }
241                }
242                None => {
243                    let value = $value.expect("Expected value to be passed");
244                    match value.value().as_ref() {
245                        $($body)*
246                        unknown => {
247                            let message = format!("Unknown attribute `{}` for trait `{}`", unknown, $trait);
248                            let span = value.span();
249                            $errors.extend(quote_spanned! {span=>
250                                compile_error!(#message);
251                            });
252                        }
253                    }
254                }
255            }
256        }
257    };
258}
259
260impl Input {
261    /// Parse the `derivative` attributes on a type.
262    #[allow(clippy::cognitive_complexity)] // mostly macros
263    pub fn from_ast(
264        attrs: &[syn::Attribute],
265        errors: &mut proc_macro2::TokenStream,
266    ) -> Result<Input, ()> {
267        let mut input = Input {
268            is_packed: attrs.iter().any(has_repr_packed_attr),
269            ..Default::default()
270        };
271
272        for_all_attr! {
273            errors;
274            for (name, values) in attrs;
275            "Clone" => {
276                match_attributes! {
277                    errors for "Clone";
278                    let Some(clone) = input.clone;
279                    for value in values;
280                    "bound" => parse_bound(&mut clone.bounds, value, errors),
281                    "clone_from" => {
282                        clone.clone_from = parse_boolean_meta_item(value, true, "clone_from", errors);
283                    }
284                }
285            }
286            "Copy" => {
287                match_attributes! {
288                    errors for "Copy";
289                    let Some(copy) = input.copy;
290                    for value in values;
291                    "bound" => parse_bound(&mut copy.bounds, value, errors),
292                }
293            }
294            "Debug" => {
295                match_attributes! {
296                    errors for "Debug";
297                    let Some(debug) = input.debug;
298                    for value in values;
299                    "bound" => parse_bound(&mut debug.bounds, value, errors),
300                    "transparent" => {
301                        debug.transparent = parse_boolean_meta_item(value, true, "transparent", errors);
302                    }
303                }
304            }
305            "Default" => {
306                match_attributes! {
307                    errors for "Default";
308                    let Some(default) = input.default;
309                    for value in values;
310                    "bound" => parse_bound(&mut default.bounds, value, errors),
311                    "new" => {
312                        default.new = parse_boolean_meta_item(value, true, "new", errors);
313                    }
314                }
315            }
316            "Eq" => {
317                match_attributes! {
318                    errors for "Eq";
319                    let Some(eq) = input.eq;
320                    for value in values;
321                    "bound" => parse_bound(&mut eq.bounds, value, errors),
322                }
323            }
324            "Hash" => {
325                match_attributes! {
326                    errors for "Hash";
327                    let Some(hash) = input.hash;
328                    for value in values;
329                    "bound" => parse_bound(&mut hash.bounds, value, errors),
330                }
331            }
332            "PartialEq" => {
333                match_attributes! {
334                    errors for "PartialEq";
335                    let Some(partial_eq) = input.partial_eq;
336                    for value in values;
337                    "bound" => parse_bound(&mut partial_eq.bounds, value, errors),
338                    "feature_allow_slow_enum" => (), // backward compatibility, now unnecessary
339                }
340            }
341            "PartialOrd" => {
342                match_attributes! {
343                    errors for "PartialOrd";
344                    let Some(partial_ord) = input.partial_ord;
345                    for value in values;
346                    "bound" => parse_bound(&mut partial_ord.bounds, value, errors),
347                    "feature_allow_slow_enum" => {
348                        partial_ord.on_enum = parse_boolean_meta_item(value, true, "feature_allow_slow_enum", errors);
349                    }
350                }
351            }
352            "Ord" => {
353                match_attributes! {
354                    errors for "Ord";
355                    let Some(ord) = input.ord;
356                    for value in values;
357                    "bound" => parse_bound(&mut ord.bounds, value, errors),
358                    "feature_allow_slow_enum" => {
359                        ord.on_enum = parse_boolean_meta_item(value, true, "feature_allow_slow_enum", errors);
360                    }
361                }
362            }
363            unknown => {
364                let message = format!("deriving `{}` is not supported by derivative", unknown);
365                errors.extend(quote_spanned! {name.span()=>
366                    compile_error!(#message);
367                });
368            }
369        }
370
371        Ok(input)
372    }
373
374    pub fn clone_bound(&self) -> Option<&[syn::WherePredicate]> {
375        self.clone
376            .as_ref()
377            .and_then(|d| d.bounds.as_ref().map(Vec::as_slice))
378    }
379
380    pub fn clone_from(&self) -> bool {
381        self.clone.as_ref().map_or(false, |d| d.clone_from)
382    }
383
384    pub fn copy_bound(&self) -> Option<&[syn::WherePredicate]> {
385        self.copy
386            .as_ref()
387            .and_then(|d| d.bounds.as_ref().map(Vec::as_slice))
388    }
389
390    pub fn debug_bound(&self) -> Option<&[syn::WherePredicate]> {
391        self.debug
392            .as_ref()
393            .and_then(|d| d.bounds.as_ref().map(Vec::as_slice))
394    }
395
396    pub fn debug_transparent(&self) -> bool {
397        self.debug.as_ref().map_or(false, |d| d.transparent)
398    }
399
400    pub fn default_bound(&self) -> Option<&[syn::WherePredicate]> {
401        self.default
402            .as_ref()
403            .and_then(|d| d.bounds.as_ref().map(Vec::as_slice))
404    }
405
406    pub fn eq_bound(&self) -> Option<&[syn::WherePredicate]> {
407        self.eq
408            .as_ref()
409            .and_then(|d| d.bounds.as_ref().map(Vec::as_slice))
410    }
411
412    pub fn hash_bound(&self) -> Option<&[syn::WherePredicate]> {
413        self.hash
414            .as_ref()
415            .and_then(|d| d.bounds.as_ref().map(Vec::as_slice))
416    }
417
418    pub fn partial_eq_bound(&self) -> Option<&[syn::WherePredicate]> {
419        self.partial_eq
420            .as_ref()
421            .and_then(|d| d.bounds.as_ref().map(Vec::as_slice))
422    }
423
424    pub fn partial_ord_bound(&self) -> Option<&[syn::WherePredicate]> {
425        self.partial_ord
426            .as_ref()
427            .and_then(|d| d.bounds.as_ref().map(Vec::as_slice))
428    }
429
430    pub fn ord_bound(&self) -> Option<&[syn::WherePredicate]> {
431        self.ord
432            .as_ref()
433            .and_then(|d| d.bounds.as_ref().map(Vec::as_slice))
434    }
435
436    pub fn partial_ord_on_enum(&self) -> bool {
437        self.partial_ord.as_ref().map_or(false, |d| d.on_enum)
438    }
439
440    pub fn ord_on_enum(&self) -> bool {
441        self.ord.as_ref().map_or(false, |d| d.on_enum)
442    }
443}
444
445impl Field {
446    /// Parse the `derivative` attributes on a type.
447    #[allow(clippy::cognitive_complexity)] // mostly macros
448    pub fn from_ast(
449        field: &syn::Field,
450        errors: &mut proc_macro2::TokenStream,
451    ) -> Result<Field, ()> {
452        let mut out = Field::default();
453
454        for_all_attr! {
455            errors;
456            for (name, values) in field.attrs;
457            "Clone" => {
458                match_attributes! {
459                    errors for "Clone";
460                    for value in values;
461                    "bound" => parse_bound(&mut out.clone.bounds, value, errors),
462                    "clone_with" => {
463                        let path = value.expect("`clone_with` needs a value");
464                        out.clone.clone_with = parse_str_lit(&path, errors).ok();
465                    }
466                }
467            }
468            "Debug" => {
469                match_attributes! {
470                    errors for "Debug";
471                    for value in values;
472                    "bound" => parse_bound(&mut out.debug.bounds, value, errors),
473                    "format_with" => {
474                        let path = value.expect("`format_with` needs a value");
475                        out.debug.format_with = parse_str_lit(&path, errors).ok();
476                    }
477                    "ignore" => {
478                        out.debug.ignore = parse_boolean_meta_item(value, true, "ignore", errors);
479                    }
480                }
481            }
482            "Default" => {
483                match_attributes! {
484                    errors for "Default";
485                    for value in values;
486                    "bound" => parse_bound(&mut out.default.bounds, value, errors),
487                    "value" => {
488                        let value = value.expect("`value` needs a value");
489                        out.default.value = parse_str_lit(&value, errors).ok();
490                    }
491                }
492            }
493            "Eq" => {
494                match_attributes! {
495                    errors for "Eq";
496                    for value in values;
497                    "bound" => parse_bound(&mut out.eq_bound, value, errors),
498                }
499            }
500            "Hash" => {
501                match_attributes! {
502                    errors for "Hash";
503                    for value in values;
504                    "bound" => parse_bound(&mut out.hash.bounds, value, errors),
505                    "hash_with" => {
506                        let path = value.expect("`hash_with` needs a value");
507                        out.hash.hash_with = parse_str_lit(&path, errors).ok();
508                    }
509                    "ignore" => {
510                        out.hash.ignore = parse_boolean_meta_item(value, true, "ignore", errors);
511                    }
512                }
513            }
514            "PartialEq" => {
515                match_attributes! {
516                    errors for "PartialEq";
517                    for value in values;
518                    "bound" => parse_bound(&mut out.partial_eq.bounds, value, errors),
519                    "compare_with" => {
520                        let path = value.expect("`compare_with` needs a value");
521                        out.partial_eq.compare_with = parse_str_lit(&path, errors).ok();
522                    }
523                    "ignore" => {
524                        out.partial_eq.ignore = parse_boolean_meta_item(value, true, "ignore", errors);
525                    }
526                }
527            }
528            "PartialOrd" => {
529                match_attributes! {
530                    errors for "PartialOrd";
531                    for value in values;
532                    "bound" => parse_bound(&mut out.partial_ord.bounds, value, errors),
533                    "compare_with" => {
534                        let path = value.expect("`compare_with` needs a value");
535                        out.partial_ord.compare_with = parse_str_lit(&path, errors).ok();
536                    }
537                    "ignore" => {
538                        out.partial_ord.ignore = parse_boolean_meta_item(value, true, "ignore", errors);
539                    }
540                }
541            }
542            "Ord" => {
543                match_attributes! {
544                    errors for "Ord";
545                    for value in values;
546                    "bound" => parse_bound(&mut out.ord.bounds, value, errors),
547                    "compare_with" => {
548                        let path = value.expect("`compare_with` needs a value");
549                        out.ord.compare_with = parse_str_lit(&path, errors).ok();
550                    }
551                    "ignore" => {
552                        out.ord.ignore = parse_boolean_meta_item(value, true, "ignore", errors);
553                    }
554                }
555            }
556            unknown => {
557                let message = format!("deriving `{}` is not supported by derivative", unknown);
558                errors.extend(quote_spanned! {name.span()=>
559                    compile_error!(#message);
560                });
561            }
562        }
563
564        Ok(out)
565    }
566
567    pub fn clone_bound(&self) -> Option<&[syn::WherePredicate]> {
568        self.clone.bounds.as_ref().map(Vec::as_slice)
569    }
570
571    pub fn clone_with(&self) -> Option<&syn::Path> {
572        self.clone.clone_with.as_ref()
573    }
574
575    pub fn copy_bound(&self) -> Option<&[syn::WherePredicate]> {
576        self.copy_bound.as_ref().map(Vec::as_slice)
577    }
578
579    pub fn debug_bound(&self) -> Option<&[syn::WherePredicate]> {
580        self.debug.bounds.as_ref().map(Vec::as_slice)
581    }
582
583    pub fn debug_format_with(&self) -> Option<&syn::Path> {
584        self.debug.format_with.as_ref()
585    }
586
587    pub fn ignore_debug(&self) -> bool {
588        self.debug.ignore
589    }
590
591    pub fn ignore_hash(&self) -> bool {
592        self.hash.ignore
593    }
594
595    pub fn default_bound(&self) -> Option<&[syn::WherePredicate]> {
596        self.default.bounds.as_ref().map(Vec::as_slice)
597    }
598
599    pub fn default_value(&self) -> Option<&proc_macro2::TokenStream> {
600        self.default.value.as_ref()
601    }
602
603    pub fn eq_bound(&self) -> Option<&[syn::WherePredicate]> {
604        self.eq_bound.as_ref().map(Vec::as_slice)
605    }
606
607    pub fn hash_bound(&self) -> Option<&[syn::WherePredicate]> {
608        self.hash.bounds.as_ref().map(Vec::as_slice)
609    }
610
611    pub fn hash_with(&self) -> Option<&syn::Path> {
612        self.hash.hash_with.as_ref()
613    }
614
615    pub fn partial_eq_bound(&self) -> Option<&[syn::WherePredicate]> {
616        self.partial_eq.bounds.as_ref().map(Vec::as_slice)
617    }
618
619    pub fn partial_ord_bound(&self) -> Option<&[syn::WherePredicate]> {
620        self.partial_ord.bounds.as_ref().map(Vec::as_slice)
621    }
622
623    pub fn ord_bound(&self) -> Option<&[syn::WherePredicate]> {
624        self.ord.bounds.as_ref().map(Vec::as_slice)
625    }
626
627    pub fn partial_eq_compare_with(&self) -> Option<&syn::Path> {
628        self.partial_eq.compare_with.as_ref()
629    }
630
631    pub fn partial_ord_compare_with(&self) -> Option<&syn::Path> {
632        self.partial_ord.compare_with.as_ref()
633    }
634
635    pub fn ord_compare_with(&self) -> Option<&syn::Path> {
636        self.ord.compare_with.as_ref()
637    }
638
639    pub fn ignore_partial_eq(&self) -> bool {
640        self.partial_eq.ignore
641    }
642
643    pub fn ignore_partial_ord(&self) -> bool {
644        self.partial_ord.ignore
645    }
646
647    pub fn ignore_ord(&self) -> bool {
648        self.ord.ignore
649    }
650}
651
652/// Represent an attribute.
653///
654/// We only have a limited set of possible attributes:
655///
656/// * `#[derivative(Debug)]` is represented as `(Debug, [])`;
657/// * `#[derivative(Debug="foo")]` is represented as `(Debug, [(None, Some("foo"))])`;
658/// * `#[derivative(Debug(foo="bar")]` is represented as `(Debug, [(Some(foo), Some("bar"))])`.
659struct MetaItem<'a>(
660    &'a syn::Ident,
661    Vec<(Option<&'a syn::Ident>, Option<&'a syn::LitStr>)>,
662);
663
664/// Parse an arbitrary item for our limited `MetaItem` subset.
665fn read_items<'a>(item: &'a syn::NestedMeta, errors: &mut proc_macro2::TokenStream) -> Result<MetaItem<'a>, ()> {
666    let item = match *item {
667        syn::NestedMeta::Meta(ref item) => item,
668        syn::NestedMeta::Lit(ref lit) => {
669            errors.extend(quote_spanned! {lit.span()=>
670                compile_error!("expected meta-item but found literal");
671            });
672
673            return Err(());
674        }
675    };
676    match *item {
677        syn::Meta::Path(ref path) => match path.get_ident() {
678            Some(name) => Ok(MetaItem(name, Vec::new())),
679            None => {
680                errors.extend(quote_spanned! {path.span()=>
681                    compile_error!("expected derivative attribute to be a string, but found a path");
682                });
683
684                Err(())
685            }
686        },
687        syn::Meta::List(syn::MetaList {
688            ref path,
689            nested: ref values,
690            ..
691        }) => {
692            let values = values
693                .iter()
694                .map(|value| {
695                    if let syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
696                        ref path,
697                        lit: ref value,
698                        ..
699                    })) = *value
700                    {
701                        let (name, value) = ensure_str_lit(&path, &value, errors)?;
702
703                        Ok((Some(name), Some(value)))
704                    } else {
705                        errors.extend(quote_spanned! {value.span()=>
706                            compile_error!("expected named value");
707                        });
708
709                        Err(())
710                    }
711                })
712                .collect::<Result<_, _>>()?;
713
714            let name = match path.get_ident() {
715                Some(name) => name,
716                None => {
717                    errors.extend(quote_spanned! {path.span()=>
718                        compile_error!("expected derivative attribute to be a string, but found a path");
719                    });
720
721                    return Err(());
722                }
723            };
724
725            Ok(MetaItem(name, values))
726        }
727        syn::Meta::NameValue(syn::MetaNameValue {
728            ref path,
729            lit: ref value,
730            ..
731        }) => {
732            let (name, value) = ensure_str_lit(&path, &value, errors)?;
733
734            Ok(MetaItem(name, vec![(None, Some(value))]))
735        }
736    }
737}
738
739/// Filter the `derivative` items from an attribute.
740fn derivative_attribute(
741    attribute: &syn::Attribute,
742    errors: &mut proc_macro2::TokenStream,
743) -> Option<syn::punctuated::Punctuated<syn::NestedMeta, syn::token::Comma>> {
744    if !attribute.path.is_ident("derivative") {
745        return None;
746    }
747    match attribute.parse_meta() {
748        Ok(syn::Meta::List(meta_list)) => Some(meta_list.nested),
749        Ok(_) => None,
750        Err(e) => {
751            let message = format!("invalid attribute: {}", e);
752            errors.extend(quote_spanned! {e.span()=>
753                compile_error!(#message);
754            });
755
756            None
757        }
758    }
759}
760
761/// Parse an item value as a boolean. Accepted values are the string literal `"true"` and
762/// `"false"`. The `default` parameter specifies what the value of the boolean is when only its
763/// name is specified (eg. `Debug="ignore"` is equivalent to `Debug(ignore="true")`). The `name`
764/// parameter is used for error reporting.
765fn parse_boolean_meta_item(
766    item: Option<&syn::LitStr>,
767    default: bool,
768    name: &str,
769    errors: &mut proc_macro2::TokenStream,
770) -> bool {
771    if let Some(item) = item.as_ref() {
772        match item.value().as_ref() {
773            "true" => true,
774            "false" => false,
775            val => {
776                if val == name {
777                    true
778                } else {
779                    let message = format!(
780                        r#"expected `"true"` or `"false"` for `{}`, got `{}`"#,
781                        name, val
782                    );
783                    errors.extend(quote_spanned! {item.span()=>
784                        compile_error!(#message);
785                    });
786
787                    default
788                }
789            }
790        }
791    } else {
792        default
793    }
794}
795
796/// Parse a `bound` item.
797fn parse_bound(
798    opt_bounds: &mut Option<Vec<syn::WherePredicate>>,
799    value: Option<&syn::LitStr>,
800    errors: &mut proc_macro2::TokenStream,
801) {
802    let bound = value.expect("`bound` needs a value");
803    let bound_value = bound.value();
804
805    *opt_bounds = if !bound_value.is_empty() {
806        let where_string = syn::LitStr::new(&format!("where {}", bound_value), bound.span());
807
808        let bounds = parse_str_lit::<syn::WhereClause>(&where_string, errors)
809            .map(|wh| wh.predicates.into_iter().collect());
810
811        match bounds {
812            Ok(bounds) => Some(bounds),
813            Err(_) => {
814                errors.extend(quote_spanned! {where_string.span()=>
815                    compile_error!("could not parse bound");
816                });
817
818                None
819            }
820        }
821    } else {
822        Some(vec![])
823    };
824}
825
826fn parse_str_lit<T>(value: &syn::LitStr, errors: &mut proc_macro2::TokenStream) -> Result<T, ()>
827where
828    T: syn::parse::Parse,
829{
830    match value.parse() {
831        Ok(value) => Ok(value),
832        Err(e) => {
833            let message = format!("could not parse string literal: {}", e);
834            errors.extend(quote_spanned! {value.span()=>
835                compile_error!(#message);
836            });
837            Err(())
838        }
839    }
840}
841
842fn ensure_str_lit<'a>(
843    attr_path: &'a syn::Path,
844    lit: &'a syn::Lit,
845    errors: &mut proc_macro2::TokenStream,
846) -> Result<(&'a syn::Ident, &'a syn::LitStr), ()> {
847    let attr_name = match attr_path.get_ident() {
848        Some(attr_name) => attr_name,
849        None => {
850            errors.extend(quote_spanned! {attr_path.span()=>
851                compile_error!("expected derivative attribute to be a string, but found a path");
852            });
853            return Err(());
854        }
855    };
856
857    if let syn::Lit::Str(ref lit) = *lit {
858        Ok((attr_name, lit))
859    } else {
860        let message = format!(
861            "expected derivative {} attribute to be a string: `{} = \"...\"`",
862            attr_name, attr_name
863        );
864        errors.extend(quote_spanned! {lit.span()=>
865            compile_error!(#message);
866        });
867        Err(())
868    }
869}
870
871pub fn has_repr_packed_attr(attr: &syn::Attribute) -> bool {
872    if let Ok(attr) = attr.parse_meta() {
873        if attr.path().get_ident().map(|i| i == "repr") == Some(true) {
874            if let syn::Meta::List(items) = attr {
875                for item in items.nested {
876                    if let syn::NestedMeta::Meta(item) = item {
877                        if item.path().get_ident().map(|i| i == "packed") == Some(true) {
878                            return true;
879                        }
880                    }
881                }
882            }
883        }
884    }
885
886    false
887}