Revision control
Copy as Markdown
Other Tools
#![allow(
clippy::manual_let_else,
clippy::needless_lifetimes,
clippy::too_many_lines,
clippy::uninlined_format_args
)]
#[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: Visibility::Inherited,
ident: "S",
generics: Generics {
lt_token: Some,
params: [
GenericParam::Lifetime(LifetimeParam {
lifetime: Lifetime {
ident: "a",
},
}),
Token![,],
GenericParam::Lifetime(LifetimeParam {
lifetime: Lifetime {
ident: "b",
},
colon_token: Some,
bounds: [
Lifetime {
ident: "a",
},
],
}),
Token![,],
GenericParam::Type(TypeParam {
attrs: [
Attribute {
style: AttrStyle::Outer,
meta: Meta::Path {
segments: [
PathSegment {
ident: "may_dangle",
},
],
},
},
],
ident: "T",
colon_token: Some,
bounds: [
TypeParamBound::Lifetime {
ident: "a",
},
],
eq_token: Some,
default: Some(Type::Tuple),
}),
],
gt_token: Some,
where_clause: Some(WhereClause {
predicates: [
WherePredicate::Type(PredicateType {
bounded_ty: Type::Path {
path: Path {
segments: [
PathSegment {
ident: "T",
},
],
},
},
bounds: [
TypeParamBound::Trait(TraitBound {
path: Path {
segments: [
PathSegment {
ident: "Debug",
},
],
},
}),
],
}),
],
}),
},
data: Data::Struct {
fields: 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#"
TypeParamBound::Lifetime {
ident: "a",
}
"#);
let tokens = quote!('_);
snapshot!(tokens as TypeParamBound, @r#"
TypeParamBound::Lifetime {
ident: "_",
}
"#);
let tokens = quote!(Debug);
snapshot!(tokens as TypeParamBound, @r#"
TypeParamBound::Trait(TraitBound {
path: Path {
segments: [
PathSegment {
ident: "Debug",
},
],
},
})
"#);
let tokens = quote!(?Sized);
snapshot!(tokens as TypeParamBound, @r#"
TypeParamBound::Trait(TraitBound {
modifier: TraitBoundModifier::Maybe,
path: Path {
segments: [
PathSegment {
ident: "Sized",
},
],
},
})
"#);
}
#[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: Visibility::Inherited,
sig: Signature {
ident: "f",
generics: Generics {
lt_token: Some,
params: [
GenericParam::Type(TypeParam {
ident: "G",
}),
],
gt_token: Some,
where_clause: Some(WhereClause {
predicates: [
WherePredicate::Type(PredicateType {
bounded_ty: Type::Path {
path: Path {
segments: [
PathSegment {
ident: "G",
},
],
},
},
bounds: [
TypeParamBound::Trait(TraitBound {
path: Path {
segments: [
PathSegment {
ident: "FnOnce",
arguments: PathArguments::Parenthesized {
output: ReturnType::Type(
Type::Path {
path: Path {
segments: [
PathSegment {
ident: "i32",
},
],
},
},
),
},
},
],
},
}),
Token![+],
TypeParamBound::Trait(TraitBound {
path: Path {
segments: [
PathSegment {
ident: "Send",
},
],
},
}),
],
}),
Token![,],
],
}),
},
output: ReturnType::Default,
},
block: Block {
stmts: [],
},
}
"#);
let where_clause = input.sig.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);
}