diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 1568b792e..adb68b5b5 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -17,6 +17,6 @@ proc-macro = true [dependencies] proc-macro2 = "1" quote = "1" -syn = { version = "1", features = ["full", "extra-traits"] } +syn = { version = "1", features = ["full", "extra-traits", "visit-mut"] } Inflector = { version = "0.11", default-features = false } termcolor = { version = "1", optional = true } diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 9de50c875..8ebe2e7c5 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -112,30 +112,26 @@ impl DerivedTS { fn generate_impl(ty: &Ident, generics: &Generics) -> TokenStream { use GenericParam::*; - let bounds = generics.params.iter().map(|param| match param { + let bounds = generics.params.iter().filter_map(|param| match param { Type(TypeParam { ident, colon_token, bounds, .. - }) => quote!(#ident #colon_token #bounds), - Lifetime(LifetimeDef { - lifetime, - colon_token, - bounds, - .. - }) => quote!(#lifetime #colon_token #bounds), + }) => Some(quote!(#ident #colon_token #bounds)), + // Filter out all lifetime param quantifiers, because we replace them with 'static. + Lifetime(LifetimeDef { .. }) => None, Const(ConstParam { const_token, ident, colon_token, ty, .. - }) => quote!(#const_token #ident #colon_token #ty), + }) => Some(quote!(#const_token #ident #colon_token #ty)), }); let type_args = generics.params.iter().map(|param| match param { Type(TypeParam { ident, .. }) | Const(ConstParam { ident, .. }) => quote!(#ident), - Lifetime(LifetimeDef { lifetime, .. }) => quote!(#lifetime), + Lifetime(LifetimeDef { .. }) => quote!('static), }); let where_bound = add_ts_to_where_clause(generics); diff --git a/macros/src/types/generics.rs b/macros/src/types/generics.rs index 8e715e094..2c24a5e7a 100644 --- a/macros/src/types/generics.rs +++ b/macros/src/types/generics.rs @@ -27,11 +27,44 @@ pub fn format_generics(deps: &mut Dependencies, generics: &Generics) -> TokenStr _ => None, }); - let comma_separated = quote!(vec![#(#expanded_params),*].join(", ")); + let comma_separated = quote!({ + let v: Vec = vec![#(#expanded_params),*]; + v.join(", ") + }); quote!(format!("<{}>", #comma_separated)) } pub fn format_type(ty: &Type, dependencies: &mut Dependencies, generics: &Generics) -> TokenStream { + // Remap all lifetimes to 'static in ty. + struct Visitor; + impl syn::visit_mut::VisitMut for Visitor { + fn visit_type_mut(&mut self, ty: &mut Type) { + match ty { + Type::Reference(ref_type) => { + ref_type.lifetime = ref_type + .lifetime + .as_ref() + .map(|_| syn::parse2(quote!('static)).unwrap()); + } + _ => {} + } + syn::visit_mut::visit_type_mut(self, ty); + } + + fn visit_generic_argument_mut(&mut self, ga: &mut GenericArgument) { + match ga { + GenericArgument::Lifetime(lt) => { + *lt = syn::parse2(quote!('static)).unwrap(); + } + _ => {} + } + syn::visit_mut::visit_generic_argument_mut(self, ga); + } + } + use syn::visit_mut::VisitMut; + let mut ty = ty.clone(); + Visitor.visit_type_mut(&mut ty); + // If the type matches one of the generic parameters, just pass the identifier: if let Some(generic_ident) = generics .params @@ -42,7 +75,7 @@ pub fn format_type(ty: &Type, dependencies: &mut Dependencies, generics: &Generi }) .find(|type_param| { matches!( - ty, + &ty, Type::Path(type_path) if type_path.qself.is_none() && type_path.path.is_ident(&type_param.ident) @@ -69,7 +102,7 @@ pub fn format_type(ty: &Type, dependencies: &mut Dependencies, generics: &Generi let tuple_struct = super::type_def( &StructAttr::default(), &format_ident!("_"), - &tuple_type_to_tuple_struct(tuple).fields, + &tuple_type_to_tuple_struct(&tuple).fields, generics, ) .unwrap(); @@ -80,8 +113,8 @@ pub fn format_type(ty: &Type, dependencies: &mut Dependencies, generics: &Generi _ => (), }; - dependencies.push_or_append_from(ty); - match extract_type_args(ty) { + dependencies.push_or_append_from(&ty); + match extract_type_args(&ty) { None => quote!(<#ty as ts_rs::TS>::name()), Some(type_args) => { let args = type_args diff --git a/ts-rs/tests/generics.rs b/ts-rs/tests/generics.rs index 93bd70905..e996a154a 100644 --- a/ts-rs/tests/generics.rs +++ b/ts-rs/tests/generics.rs @@ -212,3 +212,26 @@ fn trait_bounds() { assert_eq!(D::<&str, 41>::decl(), "interface D { t: Array, }") } + +#[test] +fn nonstatic_lifetimes() { + #[derive(TS)] + struct A<'a> { + t: &'a str, + } + assert_eq!(A::decl(), "interface A<> { t: string, }"); +} + +#[test] +fn nonstatic_lifetimes_with_child() { + #[derive(TS)] + struct A<'a> { + t: &'a str, + } + + #[derive(TS)] + struct B<'a> { + t: A<'a>, + } + assert_eq!(B::decl(), "interface B<> { t: A, }"); +}