1#![allow(
2    clippy::assertions_on_result_states,
3    clippy::manual_let_else,
4    clippy::too_many_lines,
5    clippy::uninlined_format_args
6)]
7
8#[macro_use]
9mod macros;
10
11use quote::quote;
12use syn::{Data, DeriveInput};
13
14#[test]
15fn test_unit() {
16    let input = quote! {
17        struct Unit;
18    };
19
20    snapshot!(input as DeriveInput, @r###"
21    DeriveInput {
22        vis: Visibility::Inherited,
23        ident: "Unit",
24        generics: Generics,
25        data: Data::Struct {
26            fields: Fields::Unit,
27            semi_token: Some,
28        },
29    }
30    "###);
31}
32
33#[test]
34fn test_struct() {
35    let input = quote! {
36        #[derive(Debug, Clone)]
37        pub struct Item {
38            pub ident: Ident,
39            pub attrs: Vec<Attribute>
40        }
41    };
42
43    snapshot!(input as DeriveInput, @r###"
44    DeriveInput {
45        attrs: [
46            Attribute {
47                style: AttrStyle::Outer,
48                meta: Meta::List {
49                    path: Path {
50                        segments: [
51                            PathSegment {
52                                ident: "derive",
53                            },
54                        ],
55                    },
56                    delimiter: MacroDelimiter::Paren,
57                    tokens: TokenStream(`Debug , Clone`),
58                },
59            },
60        ],
61        vis: Visibility::Public,
62        ident: "Item",
63        generics: Generics,
64        data: Data::Struct {
65            fields: Fields::Named {
66                named: [
67                    Field {
68                        vis: Visibility::Public,
69                        ident: Some("ident"),
70                        colon_token: Some,
71                        ty: Type::Path {
72                            path: Path {
73                                segments: [
74                                    PathSegment {
75                                        ident: "Ident",
76                                    },
77                                ],
78                            },
79                        },
80                    },
81                    Token![,],
82                    Field {
83                        vis: Visibility::Public,
84                        ident: Some("attrs"),
85                        colon_token: Some,
86                        ty: Type::Path {
87                            path: Path {
88                                segments: [
89                                    PathSegment {
90                                        ident: "Vec",
91                                        arguments: PathArguments::AngleBracketed {
92                                            args: [
93                                                GenericArgument::Type(Type::Path {
94                                                    path: Path {
95                                                        segments: [
96                                                            PathSegment {
97                                                                ident: "Attribute",
98                                                            },
99                                                        ],
100                                                    },
101                                                }),
102                                            ],
103                                        },
104                                    },
105                                ],
106                            },
107                        },
108                    },
109                ],
110            },
111        },
112    }
113    "###);
114
115    snapshot!(&input.attrs[0].meta, @r###"
116    Meta::List {
117        path: Path {
118            segments: [
119                PathSegment {
120                    ident: "derive",
121                },
122            ],
123        },
124        delimiter: MacroDelimiter::Paren,
125        tokens: TokenStream(`Debug , Clone`),
126    }
127    "###);
128}
129
130#[test]
131fn test_union() {
132    let input = quote! {
133        union MaybeUninit<T> {
134            uninit: (),
135            value: T
136        }
137    };
138
139    snapshot!(input as DeriveInput, @r###"
140    DeriveInput {
141        vis: Visibility::Inherited,
142        ident: "MaybeUninit",
143        generics: Generics {
144            lt_token: Some,
145            params: [
146                GenericParam::Type(TypeParam {
147                    ident: "T",
148                }),
149            ],
150            gt_token: Some,
151        },
152        data: Data::Union {
153            fields: FieldsNamed {
154                named: [
155                    Field {
156                        vis: Visibility::Inherited,
157                        ident: Some("uninit"),
158                        colon_token: Some,
159                        ty: Type::Tuple,
160                    },
161                    Token![,],
162                    Field {
163                        vis: Visibility::Inherited,
164                        ident: Some("value"),
165                        colon_token: Some,
166                        ty: Type::Path {
167                            path: Path {
168                                segments: [
169                                    PathSegment {
170                                        ident: "T",
171                                    },
172                                ],
173                            },
174                        },
175                    },
176                ],
177            },
178        },
179    }
180    "###);
181}
182
183#[test]
184#[cfg(feature = "full")]
185fn test_enum() {
186    let input = quote! {
187        /// See the std::result module documentation for details.
188        #[must_use]
189        pub enum Result<T, E> {
190            Ok(T),
191            Err(E),
192            Surprise = 0isize,
193
194            // Smuggling data into a proc_macro_derive,
195            // in the style of https://github.com/dtolnay/proc-macro-hack
196            ProcMacroHack = (0, "data").0
197        }
198    };
199
200    snapshot!(input as DeriveInput, @r###"
201    DeriveInput {
202        attrs: [
203            Attribute {
204                style: AttrStyle::Outer,
205                meta: Meta::NameValue {
206                    path: Path {
207                        segments: [
208                            PathSegment {
209                                ident: "doc",
210                            },
211                        ],
212                    },
213                    value: Expr::Lit {
214                        lit: " See the std::result module documentation for details.",
215                    },
216                },
217            },
218            Attribute {
219                style: AttrStyle::Outer,
220                meta: Meta::Path {
221                    segments: [
222                        PathSegment {
223                            ident: "must_use",
224                        },
225                    ],
226                },
227            },
228        ],
229        vis: Visibility::Public,
230        ident: "Result",
231        generics: Generics {
232            lt_token: Some,
233            params: [
234                GenericParam::Type(TypeParam {
235                    ident: "T",
236                }),
237                Token![,],
238                GenericParam::Type(TypeParam {
239                    ident: "E",
240                }),
241            ],
242            gt_token: Some,
243        },
244        data: Data::Enum {
245            variants: [
246                Variant {
247                    ident: "Ok",
248                    fields: Fields::Unnamed {
249                        unnamed: [
250                            Field {
251                                vis: Visibility::Inherited,
252                                ty: Type::Path {
253                                    path: Path {
254                                        segments: [
255                                            PathSegment {
256                                                ident: "T",
257                                            },
258                                        ],
259                                    },
260                                },
261                            },
262                        ],
263                    },
264                },
265                Token![,],
266                Variant {
267                    ident: "Err",
268                    fields: Fields::Unnamed {
269                        unnamed: [
270                            Field {
271                                vis: Visibility::Inherited,
272                                ty: Type::Path {
273                                    path: Path {
274                                        segments: [
275                                            PathSegment {
276                                                ident: "E",
277                                            },
278                                        ],
279                                    },
280                                },
281                            },
282                        ],
283                    },
284                },
285                Token![,],
286                Variant {
287                    ident: "Surprise",
288                    fields: Fields::Unit,
289                    discriminant: Some(Expr::Lit {
290                        lit: 0isize,
291                    }),
292                },
293                Token![,],
294                Variant {
295                    ident: "ProcMacroHack",
296                    fields: Fields::Unit,
297                    discriminant: Some(Expr::Field {
298                        base: Expr::Tuple {
299                            elems: [
300                                Expr::Lit {
301                                    lit: 0,
302                                },
303                                Token![,],
304                                Expr::Lit {
305                                    lit: "data",
306                                },
307                            ],
308                        },
309                        member: Member::Unnamed(Index {
310                            index: 0,
311                        }),
312                    }),
313                },
314            ],
315        },
316    }
317    "###);
318
319    let meta_items: Vec<_> = input.attrs.into_iter().map(|attr| attr.meta).collect();
320
321    snapshot!(meta_items, @r###"
322    [
323        Meta::NameValue {
324            path: Path {
325                segments: [
326                    PathSegment {
327                        ident: "doc",
328                    },
329                ],
330            },
331            value: Expr::Lit {
332                lit: " See the std::result module documentation for details.",
333            },
334        },
335        Meta::Path {
336            segments: [
337                PathSegment {
338                    ident: "must_use",
339                },
340            ],
341        },
342    ]
343    "###);
344}
345
346#[test]
347fn test_attr_with_non_mod_style_path() {
348    let input = quote! {
349        #[inert <T>]
350        struct S;
351    };
352
353    syn::parse2::<DeriveInput>(input).unwrap_err();
354}
355
356#[test]
357fn test_attr_with_mod_style_path_with_self() {
358    let input = quote! {
359        #[foo::self]
360        struct S;
361    };
362
363    snapshot!(input as DeriveInput, @r###"
364    DeriveInput {
365        attrs: [
366            Attribute {
367                style: AttrStyle::Outer,
368                meta: Meta::Path {
369                    segments: [
370                        PathSegment {
371                            ident: "foo",
372                        },
373                        Token![::],
374                        PathSegment {
375                            ident: "self",
376                        },
377                    ],
378                },
379            },
380        ],
381        vis: Visibility::Inherited,
382        ident: "S",
383        generics: Generics,
384        data: Data::Struct {
385            fields: Fields::Unit,
386            semi_token: Some,
387        },
388    }
389    "###);
390
391    snapshot!(&input.attrs[0].meta, @r###"
392    Meta::Path {
393        segments: [
394            PathSegment {
395                ident: "foo",
396            },
397            Token![::],
398            PathSegment {
399                ident: "self",
400            },
401        ],
402    }
403    "###);
404}
405
406#[test]
407fn test_pub_restricted() {
408    // Taken from tests/rust/src/test/ui/resolve/auxiliary/privacy-struct-ctor.rs
409    let input = quote! {
410        pub(in m) struct Z(pub(in m::n) u8);
411    };
412
413    snapshot!(input as DeriveInput, @r###"
414    DeriveInput {
415        vis: Visibility::Restricted {
416            in_token: Some,
417            path: Path {
418                segments: [
419                    PathSegment {
420                        ident: "m",
421                    },
422                ],
423            },
424        },
425        ident: "Z",
426        generics: Generics,
427        data: Data::Struct {
428            fields: Fields::Unnamed {
429                unnamed: [
430                    Field {
431                        vis: Visibility::Restricted {
432                            in_token: Some,
433                            path: Path {
434                                segments: [
435                                    PathSegment {
436                                        ident: "m",
437                                    },
438                                    Token![::],
439                                    PathSegment {
440                                        ident: "n",
441                                    },
442                                ],
443                            },
444                        },
445                        ty: Type::Path {
446                            path: Path {
447                                segments: [
448                                    PathSegment {
449                                        ident: "u8",
450                                    },
451                                ],
452                            },
453                        },
454                    },
455                ],
456            },
457            semi_token: Some,
458        },
459    }
460    "###);
461}
462
463#[test]
464fn test_pub_restricted_crate() {
465    let input = quote! {
466        pub(crate) struct S;
467    };
468
469    snapshot!(input as DeriveInput, @r###"
470    DeriveInput {
471        vis: Visibility::Restricted {
472            path: Path {
473                segments: [
474                    PathSegment {
475                        ident: "crate",
476                    },
477                ],
478            },
479        },
480        ident: "S",
481        generics: Generics,
482        data: Data::Struct {
483            fields: Fields::Unit,
484            semi_token: Some,
485        },
486    }
487    "###);
488}
489
490#[test]
491fn test_pub_restricted_super() {
492    let input = quote! {
493        pub(super) struct S;
494    };
495
496    snapshot!(input as DeriveInput, @r###"
497    DeriveInput {
498        vis: Visibility::Restricted {
499            path: Path {
500                segments: [
501                    PathSegment {
502                        ident: "super",
503                    },
504                ],
505            },
506        },
507        ident: "S",
508        generics: Generics,
509        data: Data::Struct {
510            fields: Fields::Unit,
511            semi_token: Some,
512        },
513    }
514    "###);
515}
516
517#[test]
518fn test_pub_restricted_in_super() {
519    let input = quote! {
520        pub(in super) struct S;
521    };
522
523    snapshot!(input as DeriveInput, @r###"
524    DeriveInput {
525        vis: Visibility::Restricted {
526            in_token: Some,
527            path: Path {
528                segments: [
529                    PathSegment {
530                        ident: "super",
531                    },
532                ],
533            },
534        },
535        ident: "S",
536        generics: Generics,
537        data: Data::Struct {
538            fields: Fields::Unit,
539            semi_token: Some,
540        },
541    }
542    "###);
543}
544
545#[test]
546fn test_fields_on_unit_struct() {
547    let input = quote! {
548        struct S;
549    };
550
551    snapshot!(input as DeriveInput, @r###"
552    DeriveInput {
553        vis: Visibility::Inherited,
554        ident: "S",
555        generics: Generics,
556        data: Data::Struct {
557            fields: Fields::Unit,
558            semi_token: Some,
559        },
560    }
561    "###);
562
563    let data = match input.data {
564        Data::Struct(data) => data,
565        _ => panic!("expected a struct"),
566    };
567
568    assert_eq!(0, data.fields.iter().count());
569}
570
571#[test]
572fn test_fields_on_named_struct() {
573    let input = quote! {
574        struct S {
575            foo: i32,
576            pub bar: String,
577        }
578    };
579
580    snapshot!(input as DeriveInput, @r###"
581    DeriveInput {
582        vis: Visibility::Inherited,
583        ident: "S",
584        generics: Generics,
585        data: Data::Struct {
586            fields: Fields::Named {
587                named: [
588                    Field {
589                        vis: Visibility::Inherited,
590                        ident: Some("foo"),
591                        colon_token: Some,
592                        ty: Type::Path {
593                            path: Path {
594                                segments: [
595                                    PathSegment {
596                                        ident: "i32",
597                                    },
598                                ],
599                            },
600                        },
601                    },
602                    Token![,],
603                    Field {
604                        vis: Visibility::Public,
605                        ident: Some("bar"),
606                        colon_token: Some,
607                        ty: Type::Path {
608                            path: Path {
609                                segments: [
610                                    PathSegment {
611                                        ident: "String",
612                                    },
613                                ],
614                            },
615                        },
616                    },
617                    Token![,],
618                ],
619            },
620        },
621    }
622    "###);
623
624    let data = match input.data {
625        Data::Struct(data) => data,
626        _ => panic!("expected a struct"),
627    };
628
629    snapshot!(data.fields.into_iter().collect::<Vec<_>>(), @r###"
630    [
631        Field {
632            vis: Visibility::Inherited,
633            ident: Some("foo"),
634            colon_token: Some,
635            ty: Type::Path {
636                path: Path {
637                    segments: [
638                        PathSegment {
639                            ident: "i32",
640                        },
641                    ],
642                },
643            },
644        },
645        Field {
646            vis: Visibility::Public,
647            ident: Some("bar"),
648            colon_token: Some,
649            ty: Type::Path {
650                path: Path {
651                    segments: [
652                        PathSegment {
653                            ident: "String",
654                        },
655                    ],
656                },
657            },
658        },
659    ]
660    "###);
661}
662
663#[test]
664fn test_fields_on_tuple_struct() {
665    let input = quote! {
666        struct S(i32, pub String);
667    };
668
669    snapshot!(input as DeriveInput, @r###"
670    DeriveInput {
671        vis: Visibility::Inherited,
672        ident: "S",
673        generics: Generics,
674        data: Data::Struct {
675            fields: Fields::Unnamed {
676                unnamed: [
677                    Field {
678                        vis: Visibility::Inherited,
679                        ty: Type::Path {
680                            path: Path {
681                                segments: [
682                                    PathSegment {
683                                        ident: "i32",
684                                    },
685                                ],
686                            },
687                        },
688                    },
689                    Token![,],
690                    Field {
691                        vis: Visibility::Public,
692                        ty: Type::Path {
693                            path: Path {
694                                segments: [
695                                    PathSegment {
696                                        ident: "String",
697                                    },
698                                ],
699                            },
700                        },
701                    },
702                ],
703            },
704            semi_token: Some,
705        },
706    }
707    "###);
708
709    let data = match input.data {
710        Data::Struct(data) => data,
711        _ => panic!("expected a struct"),
712    };
713
714    snapshot!(data.fields.iter().collect::<Vec<_>>(), @r###"
715    [
716        Field {
717            vis: Visibility::Inherited,
718            ty: Type::Path {
719                path: Path {
720                    segments: [
721                        PathSegment {
722                            ident: "i32",
723                        },
724                    ],
725                },
726            },
727        },
728        Field {
729            vis: Visibility::Public,
730            ty: Type::Path {
731                path: Path {
732                    segments: [
733                        PathSegment {
734                            ident: "String",
735                        },
736                    ],
737                },
738            },
739        },
740    ]
741    "###);
742}
743
744#[test]
745fn test_ambiguous_crate() {
746    let input = quote! {
747        // The field type is `(crate::X)` not `crate (::X)`.
748        struct S(crate::X);
749    };
750
751    snapshot!(input as DeriveInput, @r###"
752    DeriveInput {
753        vis: Visibility::Inherited,
754        ident: "S",
755        generics: Generics,
756        data: Data::Struct {
757            fields: Fields::Unnamed {
758                unnamed: [
759                    Field {
760                        vis: Visibility::Inherited,
761                        ty: Type::Path {
762                            path: Path {
763                                segments: [
764                                    PathSegment {
765                                        ident: "crate",
766                                    },
767                                    Token![::],
768                                    PathSegment {
769                                        ident: "X",
770                                    },
771                                ],
772                            },
773                        },
774                    },
775                ],
776            },
777            semi_token: Some,
778        },
779    }
780    "###);
781}
782