extern crate quote; extern crate syn; mod features; #[macro_use] mod macros; use quote::quote; use syn::{Data, DeriveInput}; #[test] fn test_unit() { let input = quote! { struct Unit; }; snapshot!(input as DeriveInput, @r###" ⋮DeriveInput { ⋮ vis: Inherited, ⋮ ident: "Unit", ⋮ generics: Generics, ⋮ data: Data::Struct { ⋮ fields: Unit, ⋮ semi_token: Some, ⋮ }, ⋮} "###); } #[test] fn test_struct() { let input = quote! { #[derive(Debug, Clone)] pub struct Item { pub ident: Ident, pub attrs: Vec } }; snapshot!(input as DeriveInput, @r###" ⋮DeriveInput { ⋮ attrs: [ ⋮ Attribute { ⋮ style: Outer, ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "derive", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ tokens: `( Debug , Clone )`, ⋮ }, ⋮ ], ⋮ vis: Visibility::Public, ⋮ ident: "Item", ⋮ generics: Generics, ⋮ data: Data::Struct { ⋮ fields: Fields::Named { ⋮ named: [ ⋮ Field { ⋮ vis: Visibility::Public, ⋮ ident: Some("ident"), ⋮ colon_token: Some, ⋮ ty: Type::Path { ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "Ident", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮ }, ⋮ Field { ⋮ vis: Visibility::Public, ⋮ ident: Some("attrs"), ⋮ colon_token: Some, ⋮ ty: Type::Path { ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "Vec", ⋮ arguments: PathArguments::AngleBracketed { ⋮ args: [ ⋮ Type(Type::Path { ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "Attribute", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ }), ⋮ ], ⋮ }, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮} "###); snapshot!(input.attrs[0].parse_meta().unwrap(), @r###" ⋮Meta::List { ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "derive", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ nested: [ ⋮ Meta(Path(Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "Debug", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ })), ⋮ Meta(Path(Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "Clone", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ })), ⋮ ], ⋮} "###); } #[test] fn test_union() { let input = quote! { union MaybeUninit { uninit: (), value: T } }; snapshot!(input as DeriveInput, @r###" ⋮DeriveInput { ⋮ vis: Inherited, ⋮ ident: "MaybeUninit", ⋮ generics: Generics { ⋮ lt_token: Some, ⋮ params: [ ⋮ Type(TypeParam { ⋮ ident: "T", ⋮ }), ⋮ ], ⋮ gt_token: Some, ⋮ }, ⋮ data: Data::Union { ⋮ fields: FieldsNamed { ⋮ named: [ ⋮ Field { ⋮ vis: Inherited, ⋮ ident: Some("uninit"), ⋮ colon_token: Some, ⋮ ty: Type::Tuple, ⋮ }, ⋮ Field { ⋮ vis: Inherited, ⋮ ident: Some("value"), ⋮ colon_token: Some, ⋮ ty: Type::Path { ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "T", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮} "###); } #[test] #[cfg(feature = "full")] fn test_enum() { let input = quote! { /// See the std::result module documentation for details. #[must_use] pub enum Result { Ok(T), Err(E), Surprise = 0isize, // Smuggling data into a proc_macro_derive, // in the style of https://github.com/dtolnay/proc-macro-hack ProcMacroHack = (0, "data").0 } }; snapshot!(input as DeriveInput, @r###" ⋮DeriveInput { ⋮ attrs: [ ⋮ Attribute { ⋮ style: Outer, ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "doc", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ tokens: `= r" See the std::result module documentation for details."`, ⋮ }, ⋮ Attribute { ⋮ style: Outer, ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "must_use", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ tokens: ``, ⋮ }, ⋮ ], ⋮ vis: Visibility::Public, ⋮ ident: "Result", ⋮ generics: Generics { ⋮ lt_token: Some, ⋮ params: [ ⋮ Type(TypeParam { ⋮ ident: "T", ⋮ }), ⋮ Type(TypeParam { ⋮ ident: "E", ⋮ }), ⋮ ], ⋮ gt_token: Some, ⋮ }, ⋮ data: Data::Enum { ⋮ variants: [ ⋮ Variant { ⋮ ident: "Ok", ⋮ fields: Fields::Unnamed { ⋮ unnamed: [ ⋮ Field { ⋮ vis: Inherited, ⋮ ty: Type::Path { ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "T", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮ Variant { ⋮ ident: "Err", ⋮ fields: Fields::Unnamed { ⋮ unnamed: [ ⋮ Field { ⋮ vis: Inherited, ⋮ ty: Type::Path { ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "E", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮ Variant { ⋮ ident: "Surprise", ⋮ fields: Unit, ⋮ discriminant: Some(Expr::Lit { ⋮ lit: 0isize, ⋮ }), ⋮ }, ⋮ Variant { ⋮ ident: "ProcMacroHack", ⋮ fields: Unit, ⋮ discriminant: Some(Expr::Field { ⋮ base: Expr::Tuple { ⋮ elems: [ ⋮ Expr::Lit { ⋮ lit: 0, ⋮ }, ⋮ Expr::Lit { ⋮ lit: "data", ⋮ }, ⋮ ], ⋮ }, ⋮ member: Unnamed(Index { ⋮ index: 0, ⋮ }), ⋮ }), ⋮ }, ⋮ ], ⋮ }, ⋮} "###); let meta_items: Vec<_> = input .attrs .into_iter() .map(|attr| attr.parse_meta().unwrap()) .collect(); snapshot!(meta_items, @r###" ⋮[ ⋮ Meta::NameValue { ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "doc", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ lit: " See the std::result module documentation for details.", ⋮ }, ⋮ Path(Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "must_use", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }), ⋮] "###); } #[test] fn test_attr_with_path() { let input = quote! { #[::attr_args::identity fn main() { assert_eq!(foo(), "Hello, world!"); }] struct Dummy; }; snapshot!(input as DeriveInput, @r###" ⋮DeriveInput { ⋮ attrs: [ ⋮ Attribute { ⋮ style: Outer, ⋮ path: Path { ⋮ leading_colon: Some, ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "attr_args", ⋮ arguments: None, ⋮ }, ⋮ PathSegment { ⋮ ident: "identity", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ tokens: `fn main ( ) { assert_eq ! ( foo ( ) , "Hello, world!" ) ; }`, ⋮ }, ⋮ ], ⋮ vis: Inherited, ⋮ ident: "Dummy", ⋮ generics: Generics, ⋮ data: Data::Struct { ⋮ fields: Unit, ⋮ semi_token: Some, ⋮ }, ⋮} "###); assert!(input.attrs[0].parse_meta().is_err()); } #[test] fn test_attr_with_non_mod_style_path() { let input = quote! { #[inert ] struct S; }; snapshot!(input as DeriveInput, @r###" ⋮DeriveInput { ⋮ attrs: [ ⋮ Attribute { ⋮ style: Outer, ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "inert", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ tokens: `< T >`, ⋮ }, ⋮ ], ⋮ vis: Inherited, ⋮ ident: "S", ⋮ generics: Generics, ⋮ data: Data::Struct { ⋮ fields: Unit, ⋮ semi_token: Some, ⋮ }, ⋮} "###); assert!(input.attrs[0].parse_meta().is_err()); } #[test] fn test_attr_with_mod_style_path_with_self() { let input = quote! { #[foo::self] struct S; }; snapshot!(input as DeriveInput, @r###" ⋮DeriveInput { ⋮ attrs: [ ⋮ Attribute { ⋮ style: Outer, ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "foo", ⋮ arguments: None, ⋮ }, ⋮ PathSegment { ⋮ ident: "self", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ tokens: ``, ⋮ }, ⋮ ], ⋮ vis: Inherited, ⋮ ident: "S", ⋮ generics: Generics, ⋮ data: Data::Struct { ⋮ fields: Unit, ⋮ semi_token: Some, ⋮ }, ⋮} "###); snapshot!(input.attrs[0].parse_meta().unwrap(), @r###" ⋮Path(Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "foo", ⋮ arguments: None, ⋮ }, ⋮ PathSegment { ⋮ ident: "self", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮}) "###); } #[test] fn test_pub_restricted() { // Taken from tests/rust/src/test/ui/resolve/auxiliary/privacy-struct-ctor.rs let input = quote! { pub(in m) struct Z(pub(in m::n) u8); }; snapshot!(input as DeriveInput, @r###" ⋮DeriveInput { ⋮ vis: Visibility::Restricted { ⋮ in_token: Some, ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "m", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮ ident: "Z", ⋮ generics: Generics, ⋮ data: Data::Struct { ⋮ fields: Fields::Unnamed { ⋮ unnamed: [ ⋮ Field { ⋮ vis: Visibility::Restricted { ⋮ in_token: Some, ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "m", ⋮ arguments: None, ⋮ }, ⋮ PathSegment { ⋮ ident: "n", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮ ty: Type::Path { ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "u8", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮ }, ⋮ ], ⋮ }, ⋮ semi_token: Some, ⋮ }, ⋮} "###); } #[test] fn test_vis_crate() { let input = quote! { crate struct S; }; snapshot!(input as DeriveInput, @r###" ⋮DeriveInput { ⋮ vis: Visibility::Crate, ⋮ ident: "S", ⋮ generics: Generics, ⋮ data: Data::Struct { ⋮ fields: Unit, ⋮ semi_token: Some, ⋮ }, ⋮} "###); } #[test] fn test_pub_restricted_crate() { let input = quote! { pub(crate) struct S; }; snapshot!(input as DeriveInput, @r###" ⋮DeriveInput { ⋮ vis: Visibility::Restricted { ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "crate", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮ ident: "S", ⋮ generics: Generics, ⋮ data: Data::Struct { ⋮ fields: Unit, ⋮ semi_token: Some, ⋮ }, ⋮} "###); } #[test] fn test_pub_restricted_super() { let input = quote! { pub(super) struct S; }; snapshot!(input as DeriveInput, @r###" ⋮DeriveInput { ⋮ vis: Visibility::Restricted { ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "super", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮ ident: "S", ⋮ generics: Generics, ⋮ data: Data::Struct { ⋮ fields: Unit, ⋮ semi_token: Some, ⋮ }, ⋮} "###); } #[test] fn test_pub_restricted_in_super() { let input = quote! { pub(in super) struct S; }; snapshot!(input as DeriveInput, @r###" ⋮DeriveInput { ⋮ vis: Visibility::Restricted { ⋮ in_token: Some, ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "super", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮ ident: "S", ⋮ generics: Generics, ⋮ data: Data::Struct { ⋮ fields: Unit, ⋮ semi_token: Some, ⋮ }, ⋮} "###); } #[test] fn test_fields_on_unit_struct() { let input = quote! { struct S; }; snapshot!(input as DeriveInput, @r###" ⋮DeriveInput { ⋮ vis: Inherited, ⋮ ident: "S", ⋮ generics: Generics, ⋮ data: Data::Struct { ⋮ fields: Unit, ⋮ semi_token: Some, ⋮ }, ⋮} "###); let data = match input.data { Data::Struct(data) => data, _ => panic!("expected a struct"), }; assert_eq!(0, data.fields.iter().count()); } #[test] fn test_fields_on_named_struct() { let input = quote! { struct S { foo: i32, pub bar: String, } }; snapshot!(input as DeriveInput, @r###" ⋮DeriveInput { ⋮ vis: Inherited, ⋮ ident: "S", ⋮ generics: Generics, ⋮ data: Data::Struct { ⋮ fields: Fields::Named { ⋮ named: [ ⋮ Field { ⋮ vis: Inherited, ⋮ ident: Some("foo"), ⋮ colon_token: Some, ⋮ ty: Type::Path { ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "i32", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮ }, ⋮ Field { ⋮ vis: Visibility::Public, ⋮ ident: Some("bar"), ⋮ colon_token: Some, ⋮ ty: Type::Path { ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "String", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮} "###); let data = match input.data { Data::Struct(data) => data, _ => panic!("expected a struct"), }; snapshot!(data.fields.into_iter().collect::>(), @r###" ⋮[ ⋮ Field { ⋮ vis: Inherited, ⋮ ident: Some("foo"), ⋮ colon_token: Some, ⋮ ty: Type::Path { ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "i32", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮ }, ⋮ Field { ⋮ vis: Visibility::Public, ⋮ ident: Some("bar"), ⋮ colon_token: Some, ⋮ ty: Type::Path { ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "String", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮ }, ⋮] "###); } #[test] fn test_fields_on_tuple_struct() { let input = quote! { struct S(i32, pub String); }; snapshot!(input as DeriveInput, @r###" ⋮DeriveInput { ⋮ vis: Inherited, ⋮ ident: "S", ⋮ generics: Generics, ⋮ data: Data::Struct { ⋮ fields: Fields::Unnamed { ⋮ unnamed: [ ⋮ Field { ⋮ vis: Inherited, ⋮ ty: Type::Path { ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "i32", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮ }, ⋮ Field { ⋮ vis: Visibility::Public, ⋮ ty: Type::Path { ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "String", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮ }, ⋮ ], ⋮ }, ⋮ semi_token: Some, ⋮ }, ⋮} "###); let data = match input.data { Data::Struct(data) => data, _ => panic!("expected a struct"), }; snapshot!(data.fields.iter().collect::>(), @r###" ⋮[ ⋮ Field { ⋮ vis: Inherited, ⋮ ty: Type::Path { ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "i32", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮ }, ⋮ Field { ⋮ vis: Visibility::Public, ⋮ ty: Type::Path { ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "String", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮ }, ⋮] "###); } #[test] fn test_ambiguous_crate() { let input = quote! { // The field type is `(crate::X)` not `crate (::X)`. struct S(crate::X); }; snapshot!(input as DeriveInput, @r###" ⋮DeriveInput { ⋮ vis: Inherited, ⋮ ident: "S", ⋮ generics: Generics, ⋮ data: Data::Struct { ⋮ fields: Fields::Unnamed { ⋮ unnamed: [ ⋮ Field { ⋮ vis: Inherited, ⋮ ty: Type::Path { ⋮ path: Path { ⋮ segments: [ ⋮ PathSegment { ⋮ ident: "crate", ⋮ arguments: None, ⋮ }, ⋮ PathSegment { ⋮ ident: "X", ⋮ arguments: None, ⋮ }, ⋮ ], ⋮ }, ⋮ }, ⋮ }, ⋮ ], ⋮ }, ⋮ semi_token: Some, ⋮ }, ⋮} "###); }