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