diff options
author | Martin Fischer <martin@push-f.com> | 2021-11-19 10:35:56 +0100 |
---|---|---|
committer | Martin Fischer <martin@push-f.com> | 2021-11-19 10:35:56 +0100 |
commit | d8a313dd422c78fb018cfe4249b526fe3e9dc851 (patch) | |
tree | 743e20edfd20058b6b784b9099544ff3878d3278 | |
parent | eb686879fdd7cde5d09837c202ebd25c9c9889a6 (diff) |
allow same-named method generics if they have same bounds
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | src/lib.rs | 25 | ||||
-rw-r--r-- | tests/tests.rs | 8 |
3 files changed, 31 insertions, 5 deletions
@@ -117,3 +117,6 @@ trait TraitWithCallback { Note that since in order to be object-safe methods must not have generics, dynamize simply moves the generic from the method to the trait definition. + +If two method type parameters have the same name, dynamize enforces that they +also have the same bounds and only adds the parameter once to the trait. @@ -1,5 +1,7 @@ #![doc = include_str!("../README.md")] +use std::collections::HashMap; + use proc_macro::TokenStream; use proc_macro2::Group; use quote::format_ident; @@ -8,6 +10,7 @@ use quote::quote_spanned; use quote::ToTokens; use syn::parse_macro_input; use syn::punctuated::Punctuated; +use syn::spanned::Spanned; use syn::token::Brace; use syn::token::Gt; use syn::token::Lt; @@ -171,6 +174,14 @@ pub fn dynamize(_attr: TokenStream, input: TokenStream) -> TokenStream { items: Vec::new(), }; + let mut generic_map = HashMap::new(); + + for generic in &dyn_trait.generics.params { + if let GenericParam::Type(type_param) = generic { + generic_map.insert(type_param.ident.clone(), type_param.bounds.clone()); + } + } + for (signature, parsed_method) in objectifiable_methods { let mut new_method = TraitItemMethod { attrs: Vec::new(), @@ -235,8 +246,18 @@ pub fn dynamize(_attr: TokenStream, input: TokenStream) -> TokenStream { // FUTURE: use Vec::drain_filter once it's stable let mut i = 0; while i < params.len() { - if matches!(params[i], GenericParam::Type(_)) { - dyn_trait.generics.params.push(params.remove(i)); + if let GenericParam::Type(type_param) = ¶ms[i] { + if let Some(bounds) = generic_map.get(&type_param.ident) { + if *bounds == type_param.bounds { + params.remove(i); + continue; + } else { + return abort!(type_param.span(), "dynamize failure: there exists a same-named method generic with different bounds"); + } + } else { + generic_map.insert(type_param.ident.clone(), type_param.bounds.clone()); + dyn_trait.generics.params.push(params.remove(i)); + } } else { i += 1; } diff --git a/tests/tests.rs b/tests/tests.rs index 6bccc04..4d09847 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -100,12 +100,14 @@ fn test1<T: Bar1<X>, X, A, B, C>(some: T) { trait Buz<X> { type C: Into<String>; - fn foobar<A>(&self, x: X) -> Result<A, Self::C>; + fn foobar1<A>(&self, x: X) -> Result<A, Self::C>; + fn foobar2<A>(&self) -> Result<A, Self::C>; } -fn test2<T: Buz<X>, X, A>(some: T, x: X) -> Result<A, String> { +fn test2<T: Buz<X>, X, A>(some: T, x: X) { let dyn_trait: &dyn DynBuz<X, A> = &some; - dyn_trait.foobar(x) + let _: Result<A, String> = dyn_trait.foobar1(x); + let _: Result<A, String> = dyn_trait.foobar2(); } #[dynamize::dynamize] |