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