289 lines
9.4 KiB
Rust
289 lines
9.4 KiB
Rust
extern crate quote;
|
|
extern crate syn;
|
|
|
|
mod features;
|
|
|
|
#[macro_use]
|
|
mod macros;
|
|
|
|
use quote::quote;
|
|
use syn::{DeriveInput, ItemFn, TypeParamBound, WhereClause, WherePredicate};
|
|
|
|
#[test]
|
|
fn test_split_for_impl() {
|
|
let input = quote! {
|
|
struct S<'a, 'b: 'a, #[may_dangle] T: 'a = ()> where T: Debug;
|
|
};
|
|
|
|
snapshot!(input as DeriveInput, @r###"
|
|
⋮DeriveInput {
|
|
⋮ vis: Inherited,
|
|
⋮ ident: "S",
|
|
⋮ generics: Generics {
|
|
⋮ lt_token: Some,
|
|
⋮ params: [
|
|
⋮ Lifetime(LifetimeDef {
|
|
⋮ lifetime: Lifetime {
|
|
⋮ ident: "a",
|
|
⋮ },
|
|
⋮ }),
|
|
⋮ Lifetime(LifetimeDef {
|
|
⋮ lifetime: Lifetime {
|
|
⋮ ident: "b",
|
|
⋮ },
|
|
⋮ colon_token: Some,
|
|
⋮ bounds: [
|
|
⋮ Lifetime {
|
|
⋮ ident: "a",
|
|
⋮ },
|
|
⋮ ],
|
|
⋮ }),
|
|
⋮ Type(TypeParam {
|
|
⋮ attrs: [
|
|
⋮ Attribute {
|
|
⋮ style: Outer,
|
|
⋮ path: Path {
|
|
⋮ segments: [
|
|
⋮ PathSegment {
|
|
⋮ ident: "may_dangle",
|
|
⋮ arguments: None,
|
|
⋮ },
|
|
⋮ ],
|
|
⋮ },
|
|
⋮ tts: ``,
|
|
⋮ },
|
|
⋮ ],
|
|
⋮ ident: "T",
|
|
⋮ colon_token: Some,
|
|
⋮ bounds: [
|
|
⋮ Lifetime(Lifetime {
|
|
⋮ ident: "a",
|
|
⋮ }),
|
|
⋮ ],
|
|
⋮ eq_token: Some,
|
|
⋮ default: Some(Type::Tuple),
|
|
⋮ }),
|
|
⋮ ],
|
|
⋮ gt_token: Some,
|
|
⋮ where_clause: Some(WhereClause {
|
|
⋮ predicates: [
|
|
⋮ Type(PredicateType {
|
|
⋮ bounded_ty: Type::Path {
|
|
⋮ path: Path {
|
|
⋮ segments: [
|
|
⋮ PathSegment {
|
|
⋮ ident: "T",
|
|
⋮ arguments: None,
|
|
⋮ },
|
|
⋮ ],
|
|
⋮ },
|
|
⋮ },
|
|
⋮ bounds: [
|
|
⋮ Trait(TraitBound {
|
|
⋮ modifier: None,
|
|
⋮ path: Path {
|
|
⋮ segments: [
|
|
⋮ PathSegment {
|
|
⋮ ident: "Debug",
|
|
⋮ arguments: None,
|
|
⋮ },
|
|
⋮ ],
|
|
⋮ },
|
|
⋮ }),
|
|
⋮ ],
|
|
⋮ }),
|
|
⋮ ],
|
|
⋮ }),
|
|
⋮ },
|
|
⋮ data: Data::Struct {
|
|
⋮ fields: Unit,
|
|
⋮ semi_token: Some,
|
|
⋮ },
|
|
⋮}
|
|
"###);
|
|
|
|
let generics = input.generics;
|
|
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
|
|
|
let generated = quote! {
|
|
impl #impl_generics MyTrait for Test #ty_generics #where_clause {}
|
|
};
|
|
let expected = quote! {
|
|
impl<'a, 'b: 'a, #[may_dangle] T: 'a> MyTrait
|
|
for Test<'a, 'b, T>
|
|
where
|
|
T: Debug
|
|
{}
|
|
};
|
|
assert_eq!(generated.to_string(), expected.to_string());
|
|
|
|
let turbofish = ty_generics.as_turbofish();
|
|
let generated = quote! {
|
|
Test #turbofish
|
|
};
|
|
let expected = quote! {
|
|
Test::<'a, 'b, T>
|
|
};
|
|
assert_eq!(generated.to_string(), expected.to_string());
|
|
}
|
|
|
|
#[test]
|
|
fn test_ty_param_bound() {
|
|
let tokens = quote!('a);
|
|
snapshot!(tokens as TypeParamBound, @r###"
|
|
⋮Lifetime(Lifetime {
|
|
⋮ ident: "a",
|
|
⋮})
|
|
"###);
|
|
|
|
let tokens = quote!('_);
|
|
snapshot!(tokens as TypeParamBound, @r###"
|
|
⋮Lifetime(Lifetime {
|
|
⋮ ident: "_",
|
|
⋮})
|
|
"###);
|
|
|
|
let tokens = quote!(Debug);
|
|
snapshot!(tokens as TypeParamBound, @r###"
|
|
⋮Trait(TraitBound {
|
|
⋮ modifier: None,
|
|
⋮ path: Path {
|
|
⋮ segments: [
|
|
⋮ PathSegment {
|
|
⋮ ident: "Debug",
|
|
⋮ arguments: None,
|
|
⋮ },
|
|
⋮ ],
|
|
⋮ },
|
|
⋮})
|
|
"###);
|
|
|
|
let tokens = quote!(?Sized);
|
|
snapshot!(tokens as TypeParamBound, @r###"
|
|
⋮Trait(TraitBound {
|
|
⋮ modifier: Maybe,
|
|
⋮ path: Path {
|
|
⋮ segments: [
|
|
⋮ PathSegment {
|
|
⋮ ident: "Sized",
|
|
⋮ arguments: None,
|
|
⋮ },
|
|
⋮ ],
|
|
⋮ },
|
|
⋮})
|
|
"###);
|
|
}
|
|
|
|
#[test]
|
|
fn test_fn_precedence_in_where_clause() {
|
|
// This should parse as two separate bounds, `FnOnce() -> i32` and `Send` - not
|
|
// `FnOnce() -> (i32 + Send)`.
|
|
let input = quote! {
|
|
fn f<G>()
|
|
where
|
|
G: FnOnce() -> i32 + Send,
|
|
{
|
|
}
|
|
};
|
|
|
|
snapshot!(input as ItemFn, @r###"
|
|
⋮ItemFn {
|
|
⋮ vis: Inherited,
|
|
⋮ ident: "f",
|
|
⋮ decl: FnDecl {
|
|
⋮ generics: Generics {
|
|
⋮ lt_token: Some,
|
|
⋮ params: [
|
|
⋮ Type(TypeParam {
|
|
⋮ ident: "G",
|
|
⋮ }),
|
|
⋮ ],
|
|
⋮ gt_token: Some,
|
|
⋮ where_clause: Some(WhereClause {
|
|
⋮ predicates: [
|
|
⋮ Type(PredicateType {
|
|
⋮ bounded_ty: Type::Path {
|
|
⋮ path: Path {
|
|
⋮ segments: [
|
|
⋮ PathSegment {
|
|
⋮ ident: "G",
|
|
⋮ arguments: None,
|
|
⋮ },
|
|
⋮ ],
|
|
⋮ },
|
|
⋮ },
|
|
⋮ bounds: [
|
|
⋮ Trait(TraitBound {
|
|
⋮ modifier: None,
|
|
⋮ path: Path {
|
|
⋮ segments: [
|
|
⋮ PathSegment {
|
|
⋮ ident: "FnOnce",
|
|
⋮ arguments: PathArguments::Parenthesized {
|
|
⋮ output: Type(
|
|
⋮ Type::Path {
|
|
⋮ path: Path {
|
|
⋮ segments: [
|
|
⋮ PathSegment {
|
|
⋮ ident: "i32",
|
|
⋮ arguments: None,
|
|
⋮ },
|
|
⋮ ],
|
|
⋮ },
|
|
⋮ },
|
|
⋮ ),
|
|
⋮ },
|
|
⋮ },
|
|
⋮ ],
|
|
⋮ },
|
|
⋮ }),
|
|
⋮ Trait(TraitBound {
|
|
⋮ modifier: None,
|
|
⋮ path: Path {
|
|
⋮ segments: [
|
|
⋮ PathSegment {
|
|
⋮ ident: "Send",
|
|
⋮ arguments: None,
|
|
⋮ },
|
|
⋮ ],
|
|
⋮ },
|
|
⋮ }),
|
|
⋮ ],
|
|
⋮ }),
|
|
⋮ ],
|
|
⋮ }),
|
|
⋮ },
|
|
⋮ output: Default,
|
|
⋮ },
|
|
⋮ block: Block,
|
|
⋮}
|
|
"###);
|
|
|
|
let where_clause = input.decl.generics.where_clause.as_ref().unwrap();
|
|
assert_eq!(where_clause.predicates.len(), 1);
|
|
|
|
let predicate = match &where_clause.predicates[0] {
|
|
WherePredicate::Type(pred) => pred,
|
|
_ => panic!("wrong predicate kind"),
|
|
};
|
|
|
|
assert_eq!(predicate.bounds.len(), 2, "{:#?}", predicate.bounds);
|
|
|
|
let first_bound = &predicate.bounds[0];
|
|
assert_eq!(quote!(#first_bound).to_string(), "FnOnce ( ) -> i32");
|
|
|
|
let second_bound = &predicate.bounds[1];
|
|
assert_eq!(quote!(#second_bound).to_string(), "Send");
|
|
}
|
|
|
|
#[test]
|
|
fn test_where_clause_at_end_of_input() {
|
|
let input = quote! {
|
|
where
|
|
};
|
|
|
|
snapshot!(input as WhereClause, @"WhereClause");
|
|
|
|
assert_eq!(input.predicates.len(), 0);
|
|
}
|