aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Fischer <martin@push-f.com>2021-11-19 10:35:56 +0100
committerMartin Fischer <martin@push-f.com>2021-11-19 10:35:56 +0100
commitd8a313dd422c78fb018cfe4249b526fe3e9dc851 (patch)
tree743e20edfd20058b6b784b9099544ff3878d3278
parenteb686879fdd7cde5d09837c202ebd25c9c9889a6 (diff)
allow same-named method generics if they have same bounds
-rw-r--r--README.md3
-rw-r--r--src/lib.rs25
-rw-r--r--tests/tests.rs8
3 files changed, 31 insertions, 5 deletions
diff --git a/README.md b/README.md
index 332de4f..967738e 100644
--- a/README.md
+++ b/README.md
@@ -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.
diff --git a/src/lib.rs b/src/lib.rs
index 6e88136..5385181 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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) = &params[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]