Skip to content

Commit

Permalink
Fix: associated_types
Browse files Browse the repository at this point in the history
  • Loading branch information
not-elm committed Jul 1, 2024
1 parent da7ecfe commit 3300a14
Show file tree
Hide file tree
Showing 11 changed files with 219 additions and 50 deletions.
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,14 @@ path = "tests/struct/progress.rs"
name = "generics"
path = "tests/generics/progress.rs"

[[test]]
name = "associated_type"
path = "tests/associated_type/progress.rs"

[[test]]
name = "async_trait"
path = "tests/async_trait/progress.rs"


[dependencies]
auto-delegate-impl = { version = "0.1.1", path = "impl" }

Expand Down
122 changes: 78 additions & 44 deletions impl/src/delegate_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use proc_macro::TokenStream;

use proc_macro2::Span;
use quote::quote;
use syn::{Attribute, GenericParam, ItemTrait, LifetimeParam, TraitItem, TypeParam, TypeParamBound};
use syn::{Attribute, GenericParam, ItemTrait, LifetimeParam, parse2, TraitItem, TypeParam, TypeParamBound};
use syn::__private::TokenStream2;
use syn::punctuated::Punctuated;
use syn::token::Plus;
Expand All @@ -14,7 +14,7 @@ use crate::syn::syn_generics::{
expand_generic_param_without_bound,
expand_where_bound_without_where_token,
};
use crate::trait_item::functions::TraitFunctions;
use crate::trait_meta::functions::TraitFunctions;

pub fn expand_delegate_trait(_attr: TokenStream, input: TokenStream) -> TokenStream2 {
match try_expand_delegate_trait(input) {
Expand All @@ -23,56 +23,72 @@ pub fn expand_delegate_trait(_attr: TokenStream, input: TokenStream) -> TokenStr
}
}


fn try_expand_delegate_trait(input: TokenStream) -> syn::Result<TokenStream2> {
let mut item = syn::parse::<syn::ItemTrait>(input)?;
expand_impl_trait(&mut item)
}


fn expand_impl_trait(item: &mut ItemTrait) -> syn::Result<TokenStream2> {
let trait_name = expand_trait_name(item);
let trait_name = expand_trait_name_with_generics(item, false);
let delegatable = delegatable_ident_with_generics(item.ident.clone());

let impl_generic = proc_macro2::Ident::new("DelegateImpl", Span::call_site());
let trait_functions = trait_functions(item.clone())?;

let mut generics = item.generics.clone();
generics.params.push(GenericParam::Type(TypeParam::from(impl_generic.clone())));
item.items.iter().filter_map(|item| {
match item {
TraitItem::Type(ty) => {
let ident = &ty.ident;
let bounds = &ty.bounds;
if bounds.is_empty() {
Some(quote! {#ident})
} else {
Some(quote! {#ident: #bounds})
}
}
_ => None
}
})
.for_each(|ty| {
generics.params.push(GenericParam::Type(parse2::<TypeParam>(ty).unwrap()));
});

let lifetime_bound = expand_lifetimes_bound(item);
let trait_bound = expand_trait_bound(item);
let super_traits = super_traits_bound(&item.supertraits);
let where_generics = expand_where_bound_without_where_token(&item.generics);
let async_trait_attr = async_trait_attr(item);
let send_and_sync = send_and_sync_bound(item, &async_trait_attr);
let associated_types = associated_types(item, &trait_name, &delegatable);

Ok(quote::quote! {
#item

#async_trait_attr
impl #generics #trait_name for #impl_generic
where #impl_generic: #delegatable #super_traits #send_and_sync,
<#impl_generic as #delegatable>::A : #lifetime_bound,
<#impl_generic as #delegatable>::B : #lifetime_bound,
<#impl_generic as #delegatable>::C : #lifetime_bound,
<#impl_generic as #delegatable>::D : #lifetime_bound,
<#impl_generic as #delegatable>::D : #lifetime_bound,
<#impl_generic as #delegatable>::E : #lifetime_bound,
<#impl_generic as #delegatable>::F : #lifetime_bound,
<#impl_generic as #delegatable>::G : #lifetime_bound,
<#impl_generic as #delegatable>::H : #lifetime_bound,
<#impl_generic as #delegatable>::I : #lifetime_bound,
<#impl_generic as #delegatable>::J : #lifetime_bound,
<#impl_generic as #delegatable>::K : #lifetime_bound,
<#impl_generic as #delegatable>::L : #lifetime_bound,
<#impl_generic as #delegatable>::A : #trait_bound,
<#impl_generic as #delegatable>::B : #trait_bound,
<#impl_generic as #delegatable>::C : #trait_bound,
<#impl_generic as #delegatable>::D : #trait_bound,
<#impl_generic as #delegatable>::D : #trait_bound,
<#impl_generic as #delegatable>::E : #trait_bound,
<#impl_generic as #delegatable>::F : #trait_bound,
<#impl_generic as #delegatable>::G : #trait_bound,
<#impl_generic as #delegatable>::H : #trait_bound,
<#impl_generic as #delegatable>::I : #trait_bound,
<#impl_generic as #delegatable>::J : #trait_bound,
<#impl_generic as #delegatable>::K : #trait_bound,
<#impl_generic as #delegatable>::L : #trait_bound,
#where_generics
{
#associated_types
#(#trait_functions)*
}
})
}


fn async_trait_attr(item: &ItemTrait) -> Option<&Attribute> {
item.attrs.iter().find(|attr| {
attr
Expand All @@ -92,9 +108,9 @@ fn send_and_sync_bound(item: &ItemTrait, async_trait_attr: &Option<&Attribute>)
} else {
Some(quote!(+ Send))
}
}else if has_immutable_self_receiver(item.items.clone()) {
Some(quote ! ( + Send + Sync))
}else {
} else if has_immutable_self_receiver(item.items.clone()) {
Some(quote!( + Send + Sync))
} else {
Some(quote!(+ Send))
}
}
Expand All @@ -107,12 +123,10 @@ fn super_traits_bound(super_traits: &Punctuated<TypeParamBound, Plus>) -> Option
}
}


fn has_immutable_self_receiver(items: Vec<TraitItem>) -> bool {
TraitFunctions::new(items).any(|meta| meta.has_immutable_self_ref_receiver())
}


fn trait_functions(item: ItemTrait) -> syn::Result<Vec<TokenStream2>> {
let mut trait_fn: Vec<TokenStream2> = Vec::new();
let delegatable_ident = delegatable_ident_with_generics(item.ident);
Expand All @@ -123,37 +137,58 @@ fn trait_functions(item: ItemTrait) -> syn::Result<Vec<TokenStream2>> {
Ok(trait_fn)
}

fn associated_types(
item: &ItemTrait,
trait_name_with_generics: &TokenStream2,
delegatable: &TokenStream2,
) -> TokenStream2 {
let associated_types = item.items.iter().filter_map(|item| {
match item {
TraitItem::Type(ty) => Some(ty.ident.clone()),
_ => None
}
})
.map(|ty| quote! {
type #ty = <<Self as #delegatable>::A as #trait_name_with_generics>::#ty;
});
quote!(#(#associated_types)*)
}

fn expand_trait_name(item: &ItemTrait) -> TokenStream2 {
let generics: Vec<TokenStream2> = intersperse(
quote::quote!(,),
item
.generics
.params
.iter()
.map(expand_generic_param_without_bound),
);
fn expand_trait_name_with_generics(item: &ItemTrait, has_associated_types: bool) -> TokenStream2 {
let generics: Vec<TokenStream2> = item
.generics
.params
.iter()
.map(expand_generic_param_without_bound)
.collect::<Vec<_>>();
let associated_types = if has_associated_types {
item.items.iter().filter_map(|item| match item {
TraitItem::Type(ty) => {
let ident = &ty.ident;
Some(quote! {#ident=#ident,})
}
_ => None
}).collect::<Vec<_>>()
} else {
Vec::new()
};

let generics_brackets = if generics.is_empty() {
let generics_brackets = if generics.is_empty() && associated_types.is_empty() {
None
} else {
Some(quote::quote! {
<#(#generics)*>
<#(#generics)* #(#associated_types)*>
})
};

let trait_ident = &item.ident;

quote::quote! {
#trait_ident #generics_brackets
}
}


fn expand_lifetimes_bound(item_trait: &ItemTrait) -> TokenStream2 {
let trait_name = expand_trait_name(item_trait);

let lifetime_params: Vec<&LifetimeParam> = item_trait
fn expand_trait_bound(item: &ItemTrait) -> TokenStream2 {
let trait_name = expand_trait_name_with_generics(item, true);
let lifetime_params: Vec<&LifetimeParam> = item
.generics
.lifetimes()
.collect();
Expand All @@ -169,7 +204,6 @@ fn expand_lifetimes_bound(item_trait: &ItemTrait) -> TokenStream2 {
.iter()
.map(|lifetime| quote::quote!(#lifetime)),
);

quote::quote! {
#trait_name + #(#lifetimes_bound)*
}
Expand Down
2 changes: 1 addition & 1 deletion impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ mod delegate_trait;
mod ident;
mod delegatable;
mod syn;
mod trait_item;
mod trait_meta;
mod attribute;
mod async_trait;

Expand Down
4 changes: 2 additions & 2 deletions impl/src/syn/syn_generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ pub fn expand_generic_param_without_bound(param: &GenericParam) -> TokenStream2
match param {
GenericParam::Type(ty) => {
let param_name = &ty.ident;
quote::quote!(#param_name)
quote::quote!(#param_name,)
}
_ => quote::quote!(#param)
_ => quote::quote!(#param,)
}
}

Expand Down
2 changes: 1 addition & 1 deletion impl/src/trait_item.rs → impl/src/trait_meta.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@

pub mod functions;
pub mod fn_meta;

File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use syn::TraitItem;

use crate::trait_item::fn_meta::TraitFnMeta;
use crate::trait_meta::fn_meta::TraitFnMeta;

pub struct TraitFunctions {
items: Box<dyn Iterator<Item = TraitItem>>,
Expand Down
14 changes: 14 additions & 0 deletions tests/associated_type/progress.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#![allow(unused)]

mod test01_associated_type;
mod test02_associated_types_with_generics;
mod test03_associated_types_with_lifetimes;

#[cfg(test)]
#[test]
fn tests() {
let t = trybuild::TestCases::new();
t.pass("tests/associated_type/test01_associated_type.rs");
t.pass("tests/associated_type/test02_associated_types_with_generics.rs");
t.pass("tests/associated_type/test03_associated_types_with_lifetimes.rs");
}
52 changes: 52 additions & 0 deletions tests/associated_type/test01_associated_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@

use std::ops::Add;
use auto_delegate_impl::{delegate, Delegate};


#[delegate]
trait Calc{
type Num: Add;

fn calc(&self, x1: Self::Num, x2: Self::Num) -> Self::Num;
}

#[delegate]
trait NoBound{
type Num;

fn method();
}

struct Usize;

impl Calc for Usize {
type Num = usize;

fn calc(&self, x1: Self::Num, x2: Self::Num) -> Self::Num {
x1 + x2
}
}

struct Isize;

impl Calc for Isize {
type Num = isize;

fn calc(&self, x1: Self::Num, x2: Self::Num) -> Self::Num {
x1 + x2
}
}


#[derive(Delegate)]
struct Parent{
#[to(Calc)]
child: Usize
}

fn main(){
let parent = Parent{
child: Usize
};
assert_eq!(parent.calc(1, 2), 3);
}
33 changes: 33 additions & 0 deletions tests/associated_type/test02_associated_types_with_generics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@

use std::ops::Add;
use auto_delegate_impl::{delegate, Delegate};


#[delegate]
trait Calc<Num: Add>{
type Out;

fn calc(&self, x1: Num, x2: Num) -> Self::Out;
}

struct Usize;
impl Calc<usize> for Usize{
type Out = u8;

fn calc(&self, x1: usize, x2: usize) -> Self::Out {
(x1 + x2) as u8
}
}

#[derive(Delegate)]
struct Parent{
#[to(Calc)]
child: Usize
}

fn main(){
let parent = Parent{
child: Usize
};
assert_eq!(parent.calc(1, 2), 3);
}
Loading

0 comments on commit 3300a14

Please sign in to comment.