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]
9 mod macros;
10 
11 use quote::quote;
12 use syn::{Data, DeriveInput};
13 
14 #[test]
test_unit()15 fn 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]
test_struct()34 fn 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]
test_union()131 fn 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")]
test_enum()185 fn 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]
test_attr_with_non_mod_style_path()347 fn 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]
test_attr_with_mod_style_path_with_self()357 fn 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]
test_pub_restricted()407 fn 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]
test_pub_restricted_crate()464 fn 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]
test_pub_restricted_super()491 fn 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]
test_pub_restricted_in_super()518 fn 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]
test_fields_on_unit_struct()546 fn 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]
test_fields_on_named_struct()572 fn 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]
test_fields_on_tuple_struct()664 fn 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]
test_ambiguous_crate()745 fn 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