#! [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 );
}
Messung V0.5 in Prozent C=79 H=91 G=84
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-06-19)
¤
*© Formatika GbR, Deutschland