From 3b493f020690a028d455082f6513fb87f191b68b Mon Sep 17 00:00:00 2001 From: Naja Melan Date: Sat, 5 Jun 2021 11:27:36 +0000 Subject: [PATCH] Implement Associated Types. (#6) * Implement Associated Types. * Copy over attributes on associated types. * cargo fmt --- src/derive/arc.rs | 105 +++++++++++++++++++++-- src/derive/box.rs | 103 +++++++++++++++++++++- src/derive/mut.rs | 103 +++++++++++++++++++++- src/derive/rc.rs | 103 +++++++++++++++++++++- src/derive/ref.rs | 103 +++++++++++++++++++++- tests/derive_arc/successes/assoc_type.rs | 30 +++++++ tests/derive_box/successes/assoc_type.rs | 29 +++++++ tests/derive_mut/successes/assoc_type.rs | 29 +++++++ tests/derive_rc/successes/assoc_type.rs | 30 +++++++ tests/derive_ref/successes/assoc_type.rs | 29 +++++++ 10 files changed, 643 insertions(+), 21 deletions(-) create mode 100644 tests/derive_arc/successes/assoc_type.rs create mode 100644 tests/derive_box/successes/assoc_type.rs create mode 100644 tests/derive_mut/successes/assoc_type.rs create mode 100644 tests/derive_rc/successes/assoc_type.rs create mode 100644 tests/derive_ref/successes/assoc_type.rs diff --git a/src/derive/arc.rs b/src/derive/arc.rs index 0d87e76..5bf3ff9 100644 --- a/src/derive/arc.rs +++ b/src/derive/arc.rs @@ -7,8 +7,13 @@ use crate::utils::signature_to_method_call; use crate::utils::trait_to_generic_ident; pub fn derive(trait_: &syn::ItemTrait) -> syn::Result { - // build the methods + // build an identifier for the generic type used for the implementation + let trait_ident = &trait_.ident; + let generic_type = trait_to_generic_ident(&trait_); + + // build the methods & associated types. let mut methods: Vec = Vec::new(); + let mut assoc_types: Vec = Vec::new(); for item in trait_.items.iter() { if let syn::TraitItem::Method(ref m) = item { if let Some(receiver) = m.sig.receiver() { @@ -36,11 +41,14 @@ pub fn derive(trait_: &syn::ItemTrait) -> syn::Result { let item = parse_quote!(#[inline] #signature { #call }); methods.push(item) } - } - // build an identifier for the generic type used for the implementation - let trait_ident = &trait_.ident; - let generic_type = trait_to_generic_ident(&trait_); + if let syn::TraitItem::Type(t) = item { + let t_ident = &t.ident; + let attrs = &t.attrs; + let item = parse_quote!( #(#attrs)* type #t_ident = <#generic_type as #trait_ident>::#t_ident ; ); + assoc_types.push(item); + } + } // build the generics for the impl block: // we use the same generics as the trait itself, plus @@ -61,6 +69,7 @@ pub fn derive(trait_: &syn::ItemTrait) -> syn::Result { Ok(parse_quote!( #[automatically_derived] impl #impl_generics #trait_ident #trait_generic_names for std::sync::Arc<#generic_type> #where_clause { + #(#assoc_types)* #(#methods)* } )) @@ -187,5 +196,91 @@ mod tests { ) ); } + + #[test] + fn associated_types() { + let trait_ = parse_quote!( + trait MyTrait { + type Return; + } + ); + let derived = super::super::derive(&trait_).unwrap(); + + assert_eq!( + derived, + parse_quote!( + #[automatically_derived] + impl MyTrait for std::sync::Arc { + type Return = ::Return; + } + ) + ); + } + + #[test] + fn associated_types_bound() { + let trait_ = parse_quote!( + trait MyTrait { + type Return: Clone; + } + ); + let derived = super::super::derive(&trait_).unwrap(); + + assert_eq!( + derived, + parse_quote!( + #[automatically_derived] + impl MyTrait for std::sync::Arc { + type Return = ::Return; + } + ) + ); + } + + #[test] + fn associated_types_dodgy_name() { + let trait_ = parse_quote!( + trait MyTrait { + type r#type; + } + ); + let derived = super::super::derive(&trait_).unwrap(); + + assert_eq!( + derived, + parse_quote!( + #[automatically_derived] + impl MyTrait for std::sync::Arc { + type r#type = ::r#type; + } + ) + ); + } + + #[test] + fn associated_types_attrs() { + let trait_ = parse_quote!( + trait MyTrait { + #[cfg(target_arch = "wasm32")] + type Return; + #[cfg(not(target_arch = "wasm32"))] + type Return: Send; + } + ); + let derived = super::super::derive(&trait_).unwrap(); + + assert_eq!( + derived, + parse_quote!( + #[automatically_derived] + impl MyTrait for std::sync::Arc { + #[cfg(target_arch = "wasm32")] + type Return = ::Return; + #[cfg(not(target_arch = "wasm32"))] + type Return = ::Return; + } + ) + ); + } } } diff --git a/src/derive/box.rs b/src/derive/box.rs index dd42eaa..7b633f8 100644 --- a/src/derive/box.rs +++ b/src/derive/box.rs @@ -5,8 +5,13 @@ use crate::utils::{ }; pub fn derive(trait_: &syn::ItemTrait) -> syn::Result { + // build an identifier for the generic type used for the implementation + let trait_ident = &trait_.ident; + let generic_type = trait_to_generic_ident(&trait_); + // build the methods let mut methods: Vec = Vec::new(); + let mut assoc_types: Vec = Vec::new(); for item in trait_.items.iter() { if let syn::TraitItem::Method(ref m) = item { let signature = &m.sig; @@ -33,11 +38,14 @@ pub fn derive(trait_: &syn::ItemTrait) -> syn::Result { let item = parse_quote!(#[inline] #signature { #call }); methods.push(item) } - } - // build an identifier for the generic type used for the implementation - let trait_ident = &trait_.ident; - let generic_type = trait_to_generic_ident(&trait_); + if let syn::TraitItem::Type(t) = item { + let t_ident = &t.ident; + let attrs = &t.attrs; + let item = parse_quote!( #(#attrs)* type #t_ident = <#generic_type as #trait_ident>::#t_ident ; ); + assoc_types.push(item); + } + } // build the generics for the impl block: // we use the same generics as the trait itself, plus @@ -59,6 +67,7 @@ pub fn derive(trait_: &syn::ItemTrait) -> syn::Result { Ok(parse_quote!( #[automatically_derived] impl #impl_generics #trait_ident #trait_generic_names for Box<#generic_type> #where_clause { + #(#assoc_types)* #(#methods)* } )) @@ -205,5 +214,91 @@ mod tests { ) ); } + + #[test] + fn associated_types() { + let trait_ = parse_quote!( + trait MyTrait { + type Return; + } + ); + let derived = super::super::derive(&trait_).unwrap(); + + assert_eq!( + derived, + parse_quote!( + #[automatically_derived] + impl MyTrait for Box { + type Return = ::Return; + } + ) + ); + } + + #[test] + fn associated_types_bound() { + let trait_ = parse_quote!( + trait MyTrait { + type Return: Clone; + } + ); + let derived = super::super::derive(&trait_).unwrap(); + + assert_eq!( + derived, + parse_quote!( + #[automatically_derived] + impl MyTrait for Box { + type Return = ::Return; + } + ) + ); + } + + #[test] + fn associated_types_dodgy_name() { + let trait_ = parse_quote!( + trait MyTrait { + type r#type; + } + ); + let derived = super::super::derive(&trait_).unwrap(); + + assert_eq!( + derived, + parse_quote!( + #[automatically_derived] + impl MyTrait for Box { + type r#type = ::r#type; + } + ) + ); + } + + #[test] + fn associated_types_attrs() { + let trait_ = parse_quote!( + trait MyTrait { + #[cfg(target_arch = "wasm32")] + type Return; + #[cfg(not(target_arch = "wasm32"))] + type Return: Send; + } + ); + let derived = super::super::derive(&trait_).unwrap(); + + assert_eq!( + derived, + parse_quote!( + #[automatically_derived] + impl MyTrait for Box { + #[cfg(target_arch = "wasm32")] + type Return = ::Return; + #[cfg(not(target_arch = "wasm32"))] + type Return = ::Return; + } + ) + ); + } } } diff --git a/src/derive/mut.rs b/src/derive/mut.rs index 09a4f18..a3c18ac 100644 --- a/src/derive/mut.rs +++ b/src/derive/mut.rs @@ -5,8 +5,13 @@ use crate::utils::{ }; pub fn derive(trait_: &syn::ItemTrait) -> syn::Result { + // build an identifier for the generic type used for the implementation + let trait_ident = &trait_.ident; + let generic_type = trait_to_generic_ident(&trait_); + // build the methods let mut methods: Vec = Vec::new(); + let mut assoc_types: Vec = Vec::new(); for item in trait_.items.iter() { if let syn::TraitItem::Method(ref m) = item { if let Some(receiver) = m.sig.receiver() { @@ -30,11 +35,14 @@ pub fn derive(trait_: &syn::ItemTrait) -> syn::Result { let item = parse_quote!(#[inline] #signature { #call }); methods.push(item) } - } - // build an identifier for the generic type used for the implementation - let trait_ident = &trait_.ident; - let generic_type = trait_to_generic_ident(&trait_); + if let syn::TraitItem::Type(t) = item { + let t_ident = &t.ident; + let attrs = &t.attrs; + let item = parse_quote!( #(#attrs)* type #t_ident = <#generic_type as #trait_ident>::#t_ident ; ); + assoc_types.push(item); + } + } // build the generics for the impl block: // we use the same generics as the trait itself, plus @@ -55,6 +63,7 @@ pub fn derive(trait_: &syn::ItemTrait) -> syn::Result { Ok(parse_quote!( #[automatically_derived] impl #impl_generics #trait_ident #trait_generic_names for &mut #generic_type #where_clause { + #(#assoc_types)* #(#methods)* } )) @@ -172,5 +181,91 @@ mod tests { ) ); } + + #[test] + fn associated_types() { + let trait_ = parse_quote!( + trait MyTrait { + type Return; + } + ); + let derived = super::super::derive(&trait_).unwrap(); + + assert_eq!( + derived, + parse_quote!( + #[automatically_derived] + impl MyTrait for &mut MT { + type Return = ::Return; + } + ) + ); + } + + #[test] + fn associated_types_bound() { + let trait_ = parse_quote!( + trait MyTrait { + type Return: Clone; + } + ); + let derived = super::super::derive(&trait_).unwrap(); + + assert_eq!( + derived, + parse_quote!( + #[automatically_derived] + impl MyTrait for &mut MT { + type Return = ::Return; + } + ) + ); + } + + #[test] + fn associated_types_dodgy_name() { + let trait_ = parse_quote!( + trait MyTrait { + type r#type; + } + ); + let derived = super::super::derive(&trait_).unwrap(); + + assert_eq!( + derived, + parse_quote!( + #[automatically_derived] + impl MyTrait for &mut MT { + type r#type = ::r#type; + } + ) + ); + } + + #[test] + fn associated_types_attrs() { + let trait_ = parse_quote!( + trait MyTrait { + #[cfg(target_arch = "wasm32")] + type Return; + #[cfg(not(target_arch = "wasm32"))] + type Return: Send; + } + ); + let derived = super::super::derive(&trait_).unwrap(); + + assert_eq!( + derived, + parse_quote!( + #[automatically_derived] + impl MyTrait for &mut MT { + #[cfg(target_arch = "wasm32")] + type Return = ::Return; + #[cfg(not(target_arch = "wasm32"))] + type Return = ::Return; + } + ) + ); + } } } diff --git a/src/derive/rc.rs b/src/derive/rc.rs index 47ed3ae..b95764f 100644 --- a/src/derive/rc.rs +++ b/src/derive/rc.rs @@ -5,8 +5,13 @@ use crate::utils::{ }; pub fn derive(trait_: &syn::ItemTrait) -> syn::Result { + // build an identifier for the generic type used for the implementation + let trait_ident = &trait_.ident; + let generic_type = trait_to_generic_ident(&trait_); + // build the methods let mut methods: Vec = Vec::new(); + let mut assoc_types: Vec = Vec::new(); for item in trait_.items.iter() { if let syn::TraitItem::Method(ref m) = item { if let Some(receiver) = m.sig.receiver() { @@ -34,11 +39,14 @@ pub fn derive(trait_: &syn::ItemTrait) -> syn::Result { let item = parse_quote!(#[inline] #signature { #call }); methods.push(item) } - } - // build an identifier for the generic type used for the implementation - let trait_ident = &trait_.ident; - let generic_type = trait_to_generic_ident(&trait_); + if let syn::TraitItem::Type(t) = item { + let t_ident = &t.ident; + let attrs = &t.attrs; + let item = parse_quote!( #(#attrs)* type #t_ident = <#generic_type as #trait_ident>::#t_ident ; ); + assoc_types.push(item); + } + } // build the generics for the impl block: // we use the same generics as the trait itself, plus @@ -59,6 +67,7 @@ pub fn derive(trait_: &syn::ItemTrait) -> syn::Result { Ok(parse_quote!( #[automatically_derived] impl #impl_generics #trait_ident #trait_generic_names for std::rc::Rc<#generic_type> #where_clause { + #(#assoc_types)* #(#methods)* } )) @@ -185,5 +194,91 @@ mod tests { ) ); } + + #[test] + fn associated_types() { + let trait_ = parse_quote!( + trait MyTrait { + type Return; + } + ); + let derived = super::super::derive(&trait_).unwrap(); + + assert_eq!( + derived, + parse_quote!( + #[automatically_derived] + impl MyTrait for std::rc::Rc { + type Return = ::Return; + } + ) + ); + } + + #[test] + fn associated_types_bound() { + let trait_ = parse_quote!( + trait MyTrait { + type Return: Clone; + } + ); + let derived = super::super::derive(&trait_).unwrap(); + + assert_eq!( + derived, + parse_quote!( + #[automatically_derived] + impl MyTrait for std::rc::Rc { + type Return = ::Return; + } + ) + ); + } + + #[test] + fn associated_types_dodgy_name() { + let trait_ = parse_quote!( + trait MyTrait { + type r#type; + } + ); + let derived = super::super::derive(&trait_).unwrap(); + + assert_eq!( + derived, + parse_quote!( + #[automatically_derived] + impl MyTrait for std::rc::Rc { + type r#type = ::r#type; + } + ) + ); + } + + #[test] + fn associated_types_attrs() { + let trait_ = parse_quote!( + trait MyTrait { + #[cfg(target_arch = "wasm32")] + type Return; + #[cfg(not(target_arch = "wasm32"))] + type Return: Send; + } + ); + let derived = super::super::derive(&trait_).unwrap(); + + assert_eq!( + derived, + parse_quote!( + #[automatically_derived] + impl MyTrait for std::rc::Rc { + #[cfg(target_arch = "wasm32")] + type Return = ::Return; + #[cfg(not(target_arch = "wasm32"))] + type Return = ::Return; + } + ) + ); + } } } diff --git a/src/derive/ref.rs b/src/derive/ref.rs index 8cd1412..c39a715 100644 --- a/src/derive/ref.rs +++ b/src/derive/ref.rs @@ -5,8 +5,13 @@ use crate::utils::{ }; pub fn derive(trait_: &syn::ItemTrait) -> syn::Result { + // build an identifier for the generic type used for the implementation + let trait_ident = &trait_.ident; + let generic_type = trait_to_generic_ident(&trait_); + // build the methods let mut methods: Vec = Vec::new(); + let mut assoc_types: Vec = Vec::new(); for item in trait_.items.iter() { if let syn::TraitItem::Method(ref m) = item { if let Some(receiver) = m.sig.receiver() { @@ -34,11 +39,14 @@ pub fn derive(trait_: &syn::ItemTrait) -> syn::Result { let item = parse_quote!(#[inline] #signature { #call }); methods.push(item) } - } - // build an identifier for the generic type used for the implementation - let trait_ident = &trait_.ident; - let generic_type = trait_to_generic_ident(&trait_); + if let syn::TraitItem::Type(t) = item { + let t_ident = &t.ident; + let attrs = &t.attrs; + let item = parse_quote!( #(#attrs)* type #t_ident = <#generic_type as #trait_ident>::#t_ident ; ); + assoc_types.push(item); + } + } // build the generics for the impl block: // we use the same generics as the trait itself, plus @@ -59,6 +67,7 @@ pub fn derive(trait_: &syn::ItemTrait) -> syn::Result { Ok(parse_quote!( #[automatically_derived] impl #impl_generics #trait_ident #trait_generic_names for &#generic_type #where_clause { + #(#assoc_types)* #(#methods)* } )) @@ -185,5 +194,91 @@ mod tests { ) ); } + + #[test] + fn associated_types() { + let trait_ = parse_quote!( + trait MyTrait { + type Return; + } + ); + let derived = super::super::derive(&trait_).unwrap(); + + assert_eq!( + derived, + parse_quote!( + #[automatically_derived] + impl MyTrait for &MT { + type Return = ::Return; + } + ) + ); + } + + #[test] + fn associated_types_bound() { + let trait_ = parse_quote!( + trait MyTrait { + type Return: Clone; + } + ); + let derived = super::super::derive(&trait_).unwrap(); + + assert_eq!( + derived, + parse_quote!( + #[automatically_derived] + impl MyTrait for &MT { + type Return = ::Return; + } + ) + ); + } + + #[test] + fn associated_types_dodgy_name() { + let trait_ = parse_quote!( + trait MyTrait { + type r#type; + } + ); + let derived = super::super::derive(&trait_).unwrap(); + + assert_eq!( + derived, + parse_quote!( + #[automatically_derived] + impl MyTrait for &MT { + type r#type = ::r#type; + } + ) + ); + } + + #[test] + fn associated_types_attrs() { + let trait_ = parse_quote!( + trait MyTrait { + #[cfg(target_arch = "wasm32")] + type Return; + #[cfg(not(target_arch = "wasm32"))] + type Return: Send; + } + ); + let derived = super::super::derive(&trait_).unwrap(); + + assert_eq!( + derived, + parse_quote!( + #[automatically_derived] + impl MyTrait for &MT { + #[cfg(target_arch = "wasm32")] + type Return = ::Return; + #[cfg(not(target_arch = "wasm32"))] + type Return = ::Return; + } + ) + ); + } } } diff --git a/tests/derive_arc/successes/assoc_type.rs b/tests/derive_arc/successes/assoc_type.rs new file mode 100644 index 0000000..6dd955b --- /dev/null +++ b/tests/derive_arc/successes/assoc_type.rs @@ -0,0 +1,30 @@ +use std::sync::Arc; +use std::sync::atomic::AtomicU8; +use std::sync::atomic::Ordering; + +use blanket::blanket; +use impls::impls; + +#[blanket(derive(Arc))] +pub trait Counter { + type Return: Clone; // <- verify this + fn increment(&self) -> Self::Return; +} + +#[derive(Default)] +struct AtomicCounter { + count: AtomicU8, +} + +impl Counter for AtomicCounter { + // Generate something like `type Return = ::Return;`. + type Return = u8; + fn increment(&self) -> u8 { + self.count.fetch_add(1, Ordering::SeqCst) + } +} + +fn main() { + assert!(impls!(AtomicCounter: Counter)); + assert!(impls!(Arc: Counter)); +} diff --git a/tests/derive_box/successes/assoc_type.rs b/tests/derive_box/successes/assoc_type.rs new file mode 100644 index 0000000..82cd68f --- /dev/null +++ b/tests/derive_box/successes/assoc_type.rs @@ -0,0 +1,29 @@ +use std::sync::atomic::AtomicU8; +use std::sync::atomic::Ordering; + +use blanket::blanket; +use impls::impls; + +#[blanket(derive(Box))] +pub trait Counter { + type Return: Clone; // <- verify this + fn increment(&self) -> Self::Return; +} + +#[derive(Default)] +struct AtomicCounter { + count: AtomicU8, +} + +impl Counter for AtomicCounter { + // Generate something like `type Return = ::Return;`. + type Return = u8; + fn increment(&self) -> u8 { + self.count.fetch_add(1, Ordering::SeqCst) + } +} + +fn main() { + assert!(impls!(AtomicCounter: Counter)); + assert!(impls!(Box: Counter)); +} diff --git a/tests/derive_mut/successes/assoc_type.rs b/tests/derive_mut/successes/assoc_type.rs new file mode 100644 index 0000000..a69c2b6 --- /dev/null +++ b/tests/derive_mut/successes/assoc_type.rs @@ -0,0 +1,29 @@ +use std::sync::atomic::AtomicU8; +use std::sync::atomic::Ordering; + +use blanket::blanket; +use impls::impls; + +#[blanket(derive(Mut))] +pub trait Counter { + type Return: Clone; // <- verify this + fn increment(&mut self) -> Self::Return; +} + +#[derive(Default)] +struct AtomicCounter { + count: AtomicU8, +} + +impl Counter for AtomicCounter { + // Generate something like `type Return = ::Return;`. + type Return = u8; + fn increment(&mut self) -> u8 { + self.count.fetch_add(1, Ordering::SeqCst) + } +} + +fn main() { + assert!(impls!(AtomicCounter: Counter)); + assert!(impls!(&mut AtomicCounter: Counter)); +} diff --git a/tests/derive_rc/successes/assoc_type.rs b/tests/derive_rc/successes/assoc_type.rs new file mode 100644 index 0000000..f0012d3 --- /dev/null +++ b/tests/derive_rc/successes/assoc_type.rs @@ -0,0 +1,30 @@ +use std::rc::Rc; +use std::sync::atomic::AtomicU8; +use std::sync::atomic::Ordering; + +use blanket::blanket; +use impls::impls; + +#[blanket(derive(Rc))] +pub trait Counter { + type Return: Clone; // <- verify this + fn increment(&self) -> Self::Return; +} + +#[derive(Default)] +struct AtomicCounter { + count: AtomicU8, +} + +impl Counter for AtomicCounter { + // Generate something like `type Return = ::Return;`. + type Return = u8; + fn increment(&self) -> u8 { + self.count.fetch_add(1, Ordering::SeqCst) + } +} + +fn main() { + assert!(impls!(AtomicCounter: Counter)); + assert!(impls!(Rc: Counter)); +} diff --git a/tests/derive_ref/successes/assoc_type.rs b/tests/derive_ref/successes/assoc_type.rs new file mode 100644 index 0000000..000cb4c --- /dev/null +++ b/tests/derive_ref/successes/assoc_type.rs @@ -0,0 +1,29 @@ +use std::sync::atomic::AtomicU8; +use std::sync::atomic::Ordering; + +use blanket::blanket; +use impls::impls; + +#[blanket(derive(Ref))] +pub trait Counter { + type Return: Clone; // <- verify this + fn increment(&self) -> Self::Return; +} + +#[derive(Default)] +struct AtomicCounter { + count: AtomicU8, +} + +impl Counter for AtomicCounter { + // Generate something like `type Return = ::Return;`. + type Return = u8; + fn increment(&self) -> u8 { + self.count.fetch_add(1, Ordering::SeqCst) + } +} + +fn main() { + assert!(impls!(AtomicCounter: Counter)); + assert!(impls!(&AtomicCounter: Counter)); +}