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