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