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