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 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] 131fn 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")] 185fn 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] 347fn 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] 357fn 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] 407fn 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] 464fn 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] 491fn 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] 518fn 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] 546fn 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] 572fn 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] 664fn 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] 745fn 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