Skip to content

Commit

Permalink
Fix issue with associated type bounds being repeated in derived generics
Browse files Browse the repository at this point in the history
  • Loading branch information
althonos committed May 30, 2021
1 parent 37da89a commit 376d42f
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 8 deletions.
26 changes: 24 additions & 2 deletions src/derive/box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use syn::parse_quote;
use syn::spanned::Spanned;

use crate::utils::deref_expr;
use crate::utils::generics_declaration_to_generics;
use crate::utils::signature_to_method_call;
use crate::utils::trait_to_generic_ident;

Expand Down Expand Up @@ -47,14 +48,19 @@ pub fn derive(trait_: &syn::ItemTrait) -> syn::Result<syn::ItemImpl> {
let trait_generics = &trait_.generics;
let where_clause = &trait_.generics.where_clause;
let mut impl_generics = trait_generics.clone();

// we must however remove the generic type bounds, to avoid repeating them
let mut trait_generic_names = trait_generics.clone();
trait_generic_names.params = generics_declaration_to_generics(&trait_generics.params);

impl_generics.params.push(syn::GenericParam::Type(
parse_quote!(#generic_type: #trait_ident #trait_generics),
parse_quote!(#generic_type: #trait_ident #trait_generic_names),
));

// generate the impl block
Ok(parse_quote!(
#[automatically_derived]
impl #impl_generics #trait_ident #trait_generics for Box<#generic_type> #where_clause {
impl #impl_generics #trait_ident #trait_generic_names for Box<#generic_type> #where_clause {
#(#methods)*
}
))
Expand Down Expand Up @@ -169,5 +175,21 @@ mod tests {
)
);
}

#[test]
fn generics_bounded() {
let trait_ = parse_quote!(
trait MyTrait<T: 'static + Send> {}
);
let derived = super::super::derive(&trait_).unwrap();

assert_eq!(
derived,
parse_quote!(
#[automatically_derived]
impl<T: 'static + Send, MT: MyTrait<T>> MyTrait<T> for Box<MT> {}
)
);
}
}
}
26 changes: 24 additions & 2 deletions src/derive/mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use syn::parse_quote;
use syn::spanned::Spanned;

use crate::utils::deref_expr;
use crate::utils::generics_declaration_to_generics;
use crate::utils::signature_to_method_call;
use crate::utils::trait_to_generic_ident;

Expand Down Expand Up @@ -44,13 +45,18 @@ pub fn derive(trait_: &syn::ItemTrait) -> syn::Result<syn::ItemImpl> {
let trait_generics = &trait_.generics;
let where_clause = &trait_.generics.where_clause;
let mut impl_generics = trait_generics.clone();

// we must however remove the generic type bounds, to avoid repeating them
let mut trait_generic_names = trait_generics.clone();
trait_generic_names.params = generics_declaration_to_generics(&trait_generics.params);

impl_generics.params.push(syn::GenericParam::Type(
parse_quote!(#generic_type: #trait_ident #trait_generics + ?Sized),
parse_quote!(#generic_type: #trait_ident #trait_generic_names + ?Sized),
));

Ok(parse_quote!(
#[automatically_derived]
impl #impl_generics #trait_ident #trait_generics for &mut #generic_type #where_clause {
impl #impl_generics #trait_ident #trait_generic_names for &mut #generic_type #where_clause {
#(#methods)*
}
))
Expand Down Expand Up @@ -133,5 +139,21 @@ mod tests {
)
);
}

#[test]
fn generics_bounded() {
let trait_ = parse_quote!(
trait Trait<T: 'static + Send> {}
);
let derived = super::super::derive(&trait_).unwrap();

assert_eq!(
derived,
parse_quote!(
#[automatically_derived]
impl<T: 'static + Send, T_: Trait<T> + ?Sized> Trait<T> for &mut T_ {}
)
);
}
}
}
26 changes: 24 additions & 2 deletions src/derive/rc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use syn::parse_quote;
use syn::spanned::Spanned;

use crate::utils::deref_expr;
use crate::utils::generics_declaration_to_generics;
use crate::utils::signature_to_method_call;
use crate::utils::trait_to_generic_ident;

Expand Down Expand Up @@ -48,13 +49,18 @@ pub fn derive(trait_: &syn::ItemTrait) -> syn::Result<syn::ItemImpl> {
let trait_generics = &trait_.generics;
let where_clause = &trait_.generics.where_clause;
let mut impl_generics = trait_generics.clone();

// we must however remove the generic type bounds, to avoid repeating them
let mut trait_generic_names = trait_generics.clone();
trait_generic_names.params = generics_declaration_to_generics(&trait_generics.params);

impl_generics.params.push(syn::GenericParam::Type(
parse_quote!(#generic_type: #trait_ident #trait_generics + ?Sized),
parse_quote!(#generic_type: #trait_ident #trait_generic_names + ?Sized),
));

Ok(parse_quote!(
#[automatically_derived]
impl #impl_generics #trait_ident #trait_generics for std::rc::Rc<#generic_type> #where_clause {
impl #impl_generics #trait_ident #trait_generic_names for std::rc::Rc<#generic_type> #where_clause {
#(#methods)*
}
))
Expand Down Expand Up @@ -146,5 +152,21 @@ mod tests {
)
);
}

#[test]
fn generics_bounded() {
let trait_ = parse_quote!(
trait MyTrait<T: 'static + Send> {}
);
let derived = super::super::derive(&trait_).unwrap();

assert_eq!(
derived,
parse_quote!(
#[automatically_derived]
impl<T: 'static + Send, MT: MyTrait<T> + ?Sized> MyTrait<T> for std::rc::Rc<MT> {}
)
);
}
}
}
26 changes: 24 additions & 2 deletions src/derive/ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use syn::parse_quote;
use syn::spanned::Spanned;

use crate::utils::deref_expr;
use crate::utils::generics_declaration_to_generics;
use crate::utils::signature_to_method_call;
use crate::utils::trait_to_generic_ident;

Expand Down Expand Up @@ -48,13 +49,18 @@ pub fn derive(trait_: &syn::ItemTrait) -> syn::Result<syn::ItemImpl> {
let trait_generics = &trait_.generics;
let where_clause = &trait_.generics.where_clause;
let mut impl_generics = trait_generics.clone();

// we must however remove the generic type bounds, to avoid repeating them
let mut trait_generic_names = trait_generics.clone();
trait_generic_names.params = generics_declaration_to_generics(&trait_generics.params);

impl_generics.params.push(syn::GenericParam::Type(
parse_quote!(#generic_type: #trait_ident #trait_generics + ?Sized),
parse_quote!(#generic_type: #trait_ident #trait_generic_names + ?Sized),
));

Ok(parse_quote!(
#[automatically_derived]
impl #impl_generics #trait_ident #trait_generics for &#generic_type #where_clause {
impl #impl_generics #trait_ident #trait_generic_names for &#generic_type #where_clause {
#(#methods)*
}
))
Expand Down Expand Up @@ -146,5 +152,21 @@ mod tests {
)
);
}

#[test]
fn generics_bounded() {
let trait_ = parse_quote!(
trait MyTrait<T: 'static + Send> {}
);
let derived = super::super::derive(&trait_).unwrap();

assert_eq!(
derived,
parse_quote!(
#[automatically_derived]
impl<T: 'static + Send, MT: MyTrait<T> + ?Sized> MyTrait<T> for &MT {}
)
);
}
}
}
27 changes: 27 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use quote::quote_spanned;
use syn::parse_quote;
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::GenericParam;
use syn::Token;

/// Convert a function signature to a function call with the same arguments.
pub fn signature_to_function_call(sig: &syn::Signature) -> syn::Result<syn::ExprCall> {
Expand Down Expand Up @@ -138,6 +140,31 @@ pub fn trait_to_generic_ident(trait_: &syn::ItemTrait) -> syn::Ident {
syn::Ident::new(&raw, trait_.ident.span())
}

/// Convert a generic type declaration to a generic with the same arguments.
///
/// Given a generic section `<T: 'static + Send>`, get simply `<T>`.
pub fn generics_declaration_to_generics(
generics: &Punctuated<GenericParam, Token![,]>,
) -> Punctuated<GenericParam, Token![,]> {
generics
.iter()
.filter_map(|gen| match gen {
syn::GenericParam::Type(t) => Some(t),
syn::GenericParam::Lifetime(_) => None,
syn::GenericParam::Const(_) => None,
})
.map(|t| syn::TypeParam {
attrs: t.attrs.clone(),
ident: t.ident.clone(),
colon_token: None,
bounds: syn::punctuated::Punctuated::new(),
eq_token: None,
default: None,
})
.map(|t| syn::GenericParam::Type(t))
.collect::<_>()
}

#[cfg(test)]
mod tests {

Expand Down

0 comments on commit 376d42f

Please sign in to comment.