1#![allow(clippy::assertions_on_result_states, clippy::too_many_lines)]
2
3#[macro_use]
4mod macros;
5
6use quote::quote;
7use syn::{Data, DeriveInput};
8
9#[test]
10fn 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]
29fn 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]
142fn 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")]
196fn 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]
358fn 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]
400fn 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]
436fn 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]
489fn 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]
549fn 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]
568fn 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]
596fn 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]
624fn 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]
653fn 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]
679fn 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]
773fn 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]
857fn 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