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, }, } "###); }