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<Attribute>
}
};
snapshot!(input as DeriveInput, @r###"
⋮DeriveInput {
⋮ attrs: [
⋮ Attribute {
⋮ style: Outer,
⋮ path: Path {
⋮ segments: [
⋮ PathSegment {
⋮ ident: "derive",
⋮ arguments: None,
⋮ },
⋮ ],
⋮ },
⋮ tts: `( 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].interpret_meta().unwrap(), @r###"
⋮Meta::List {
⋮ ident: "derive",
⋮ nested: [
⋮ Meta(Word("Debug")),
⋮ Meta(Word("Clone")),
⋮ ],
⋮}
"###);
}
#[test]
fn test_union() {
let input = quote! {
union MaybeUninit<T> {
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<T, E> {
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,
⋮ },
⋮ ],
⋮ },
⋮ tts: `= r" See the std::result module documentation for details."`,
⋮ },
⋮ Attribute {
⋮ style: Outer,
⋮ path: Path {
⋮ segments: [
⋮ PathSegment {
⋮ ident: "must_use",
⋮ arguments: None,
⋮ },
⋮ ],
⋮ },
⋮ tts: ``,
⋮ },
⋮ ],
⋮ 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: 0,
⋮ }),
⋮ },
⋮ 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.interpret_meta().unwrap())
.collect();
snapshot!(meta_items, @r###"
⋮[
⋮ Meta::NameValue {
⋮ ident: "doc",
⋮ lit: " See the std::result module documentation for details.",
⋮ },
⋮ Word("must_use"),
⋮]
"###);
}
#[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,
⋮ },
⋮ ],
⋮ },
⋮ tts: `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].interpret_meta().is_none());
}
#[test]
fn test_attr_with_non_mod_style_path() {
let input = quote! {
#[inert <T>]
struct S;
};
snapshot!(input as DeriveInput, @r###"
⋮DeriveInput {
⋮ attrs: [
⋮ Attribute {
⋮ style: Outer,
⋮ path: Path {
⋮ segments: [
⋮ PathSegment {
⋮ ident: "inert",
⋮ arguments: None,
⋮ },
⋮ ],
⋮ },
⋮ tts: `< T >`,
⋮ },
⋮ ],
⋮ vis: Inherited,
⋮ ident: "S",
⋮ generics: Generics,
⋮ data: Data::Struct {
⋮ fields: Unit,
⋮ semi_token: Some,
⋮ },
⋮}
"###);
assert!(input.attrs[0].interpret_meta().is_none());
}
#[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,
⋮ },
⋮ ],
⋮ },
⋮ tts: ``,
⋮ },
⋮ ],
⋮ vis: Inherited,
⋮ ident: "S",
⋮ generics: Generics,
⋮ data: Data::Struct {
⋮ fields: Unit,
⋮ semi_token: Some,
⋮ },
⋮}
"###);
assert!(input.attrs[0].interpret_meta().is_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::<Vec<_>>(), @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::<Vec<_>>(), @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,
⋮ },
⋮}
"###);
}