Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 6 additions & 12 deletions crates/cgp-macro-lib/src/cgp_fn/apply_type.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,24 @@
use quote::{ToTokens, quote};
use syn::punctuated::Punctuated;
use syn::token::Plus;
use syn::{ItemImpl, TypeParamBound, parse2};
use quote::ToTokens;
use syn::{ItemImpl, parse2};

use crate::cgp_fn::{UseTypeSpec, derive_use_type_trait_bounds, substitute_abstract_type};
use crate::cgp_fn::{UseTypeSpec, derive_use_type_predicates, substitute_abstract_types};

pub fn apply_use_type_attributes_to_item_impl(
item_impl: &ItemImpl,
use_type_specs: &[UseTypeSpec],
) -> syn::Result<ItemImpl> {
let mut item_impl: ItemImpl = parse2(substitute_abstract_type(
&quote! { Self },
let mut item_impl: ItemImpl = parse2(substitute_abstract_types(
use_type_specs,
item_impl.to_token_stream(),
))?;

let bounds = derive_use_type_trait_bounds(&quote! { Self }, use_type_specs)?;
let bounds = Punctuated::<TypeParamBound, Plus>::from_iter(bounds);
let predicates = derive_use_type_predicates(use_type_specs)?;

item_impl
.generics
.make_where_clause()
.predicates
.push(parse2(quote! {
Self: #bounds
})?);
.extend(predicates);

Ok(item_impl)
}
6 changes: 5 additions & 1 deletion crates/cgp-macro-lib/src/cgp_fn/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use core::mem;

use syn::punctuated::Punctuated;
use syn::token::Comma;
use syn::{Attribute, TypeParamBound};
use syn::{Attribute, TypeParamBound, WherePredicate};

use crate::cgp_fn::{FunctionAttributes, UseTypeSpec};
use crate::parse::SimpleType;
Expand All @@ -20,6 +20,10 @@ pub fn parse_function_attributes(
let extend_bound = attribute
.parse_args_with(Punctuated::<TypeParamBound, Comma>::parse_terminated)?;
parsed_attributes.extend.extend(extend_bound);
} else if ident == "extend_where" {
let where_predicates = attribute
.parse_args_with(Punctuated::<WherePredicate, Comma>::parse_terminated)?;
parsed_attributes.extend_where.extend(where_predicates);
} else if ident == "uses" {
let uses =
attribute.parse_args_with(Punctuated::<SimpleType, Comma>::parse_terminated)?;
Expand Down
8 changes: 8 additions & 0 deletions crates/cgp-macro-lib/src/cgp_fn/item_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ pub fn derive_item_impl(
}
}

if !attributes.extend_where.is_empty() {
item_impl
.generics
.make_where_clause()
.predicates
.extend(attributes.extend_where.clone());
}

if !implicit_args.is_empty() {
let where_clause = item_impl.generics.make_where_clause();
let bounds = build_implicit_args_bounds(implicit_args)?;
Expand Down
26 changes: 19 additions & 7 deletions crates/cgp-macro-lib/src/cgp_fn/item_trait.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use quote::{ToTokens, quote};
use syn::{Generics, Ident, ItemFn, ItemTrait, TraitItemFn, parse2};
use syn::{Generics, Ident, ItemFn, ItemTrait, TraitItemFn, parse_quote, parse2};

use crate::cgp_fn::{FunctionAttributes, UseTypeSpec, substitute_abstract_type};
use crate::cgp_fn::{FunctionAttributes, UseTypeSpec, substitute_abstract_types};

pub fn derive_item_trait(
trait_ident: &Ident,
Expand All @@ -16,16 +16,25 @@ pub fn derive_item_trait(
semi_token: None,
};

let (_, type_generics, _) = generics.split_for_impl();

let mut item_trait: ItemTrait = parse2(quote! {
pub trait #trait_ident #type_generics {
pub trait #trait_ident {
#trait_item_fn
}
})?;

item_trait.generics = generics.clone();
item_trait.generics.where_clause = None;

item_trait.supertraits.extend(attributes.extend.clone());

if !attributes.extend_where.is_empty() {
item_trait
.generics
.make_where_clause()
.predicates
.extend(attributes.extend_where.clone());
}

if !attributes.use_type.is_empty() {
item_trait = expand_use_type_attributes_on_trait(&item_trait, &attributes.use_type)?;
}
Expand All @@ -37,13 +46,16 @@ pub fn expand_use_type_attributes_on_trait(
item_trait: &ItemTrait,
use_type_specs: &[UseTypeSpec],
) -> syn::Result<ItemTrait> {
let mut item_trait: ItemTrait = parse2(substitute_abstract_type(
&quote! { Self },
let mut item_trait: ItemTrait = parse2(substitute_abstract_types(
use_type_specs,
item_trait.to_token_stream(),
))?;

for use_type in use_type_specs.iter() {
if use_type.context_type != parse_quote! { Self } {
continue;
}

item_trait
.supertraits
.push(parse2(use_type.trait_path.to_token_stream())?);
Expand Down
4 changes: 2 additions & 2 deletions crates/cgp-macro-lib/src/cgp_fn/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ mod item_trait;
mod parse_implicits;
mod spec;
mod substitute_type;
mod type_equality;
mod type_predicates;
mod use_type;

pub use apply_type::*;
Expand All @@ -20,5 +20,5 @@ pub use item_trait::*;
pub use parse_implicits::*;
pub use spec::*;
pub use substitute_type::*;
pub use type_equality::*;
pub use type_predicates::*;
pub use use_type::*;
3 changes: 2 additions & 1 deletion crates/cgp-macro-lib/src/cgp_fn/spec.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use syn::token::Mut;
use syn::{Ident, Type, TypeParamBound};
use syn::{Ident, Type, TypeParamBound, WherePredicate};

use crate::cgp_fn::UseTypeSpec;
use crate::derive_getter::FieldMode;
Expand All @@ -17,6 +17,7 @@ pub struct ImplicitArgField {
#[derive(Default)]
pub struct FunctionAttributes {
pub extend: Vec<TypeParamBound>,
pub extend_where: Vec<WherePredicate>,
pub uses: Vec<SimpleType>,
pub use_type: Vec<UseTypeSpec>,
}
42 changes: 19 additions & 23 deletions crates/cgp-macro-lib/src/cgp_fn/substitute_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ use quote::quote;

use crate::cgp_fn::UseTypeSpec;

pub fn substitute_abstract_type(
context_type: &TokenStream,
type_specs: &[UseTypeSpec],
body: TokenStream,
) -> TokenStream {
pub fn substitute_abstract_types(type_specs: &[UseTypeSpec], body: TokenStream) -> TokenStream {
let mut out = body;
for spec in type_specs.iter().rev() {
out = substitute_abstract_type(spec, out);
}
out
}

pub fn substitute_abstract_type(type_specs: &UseTypeSpec, body: TokenStream) -> TokenStream {
let mut out = TokenStream::new();
let mut last_token_was_colon = false;
let mut last_two_tokens_was_colon = false;
Expand All @@ -23,28 +27,20 @@ pub fn substitute_abstract_type(

match token_tree {
TokenTree::Group(group) => {
let new_stream = substitute_abstract_type(context_type, type_specs, group.stream());
let new_stream = substitute_abstract_type(type_specs, group.stream());
out.extend([TokenTree::Group(Group::new(group.delimiter(), new_stream))]);
}
TokenTree::Ident(ident) => {
let mut replaced_ident = false;

for type_spec in type_specs {
if !last_two_tokens_was_colon
&& let Some(replacement_ident) = type_spec.replace_ident(&ident)
{
let trait_path = &type_spec.trait_path;

out.extend(quote! {
< #context_type as #trait_path > :: #replacement_ident
});

replaced_ident = true;
break;
}
}
if !last_two_tokens_was_colon
&& let Some(replacement_ident) = type_specs.replace_ident(&ident)
{
let trait_path = &type_specs.trait_path;
let context_type = &type_specs.context_type;

if !replaced_ident {
out.extend(quote! {
< #context_type as #trait_path > :: #replacement_ident
});
} else {
out.extend([TokenTree::Ident(ident)]);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,29 @@ use proc_macro2::TokenStream;
use quote::{ToTokens, quote};
use syn::punctuated::Punctuated;
use syn::token::Comma;
use syn::{Ident, Type, TypeParamBound, parse2};
use syn::{Ident, Type, WherePredicate, parse_quote, parse2};

use crate::cgp_fn::{UseTypeIdent, UseTypeSpec};

pub fn derive_use_type_trait_bounds(
context_type: &TokenStream,
specs: &[UseTypeSpec],
) -> syn::Result<Vec<TypeParamBound>> {
let mut bounds = Vec::new();
pub fn derive_use_type_predicates(specs: &[UseTypeSpec]) -> syn::Result<Vec<WherePredicate>> {
let mut predicates = Vec::new();

for use_type in specs.iter() {
let type_equalities = find_type_equalities(use_type, context_type, specs)?;
let type_equalities = find_type_equalities(use_type, specs)?;

let trait_path = &use_type.trait_path;
let mut context_type = use_type.context_type.clone();

if context_type != parse_quote!(Self)
&& let Some(new_context_type) = find_type_alias(specs, &context_type)?
{
context_type = new_context_type;
}

if type_equalities.is_empty() {
bounds.push(parse2(use_type.trait_path.to_token_stream())?);
predicates.push(parse2(quote! {
#context_type: #trait_path
})?);
} else {
let mut constraints: Punctuated<TokenStream, Comma> = Punctuated::new();

Expand All @@ -26,31 +34,49 @@ pub fn derive_use_type_trait_bounds(
});
}

let trait_path = &use_type.trait_path;
let bound = quote! {
#trait_path < #constraints >
};
predicates.push(parse2(quote! {
#context_type: #trait_path < #constraints >
})?);
}
}

Ok(predicates)
}

fn find_type_alias(specs: &[UseTypeSpec], context_type: &Type) -> syn::Result<Option<Type>> {
let Ok(context_ident) = parse2::<Ident>(context_type.to_token_stream()) else {
return Ok(None);
};

for spec in specs {
for ident in spec.type_idents.iter() {
if ident.alias_ident() == &context_ident {
let new_context_type = &spec.context_type;
let type_ident = &ident.type_ident;
let trait_path = &spec.trait_path;

bounds.push(parse2(bound)?);
let new_type = parse2(quote! {
<#new_context_type as #trait_path>::#type_ident
})?;

return Ok(Some(new_type));
}
}
}

Ok(bounds)
Ok(None)
}

pub fn find_type_equalities(
current_spec: &UseTypeSpec,
context_type: &TokenStream,
specs: &[UseTypeSpec],
) -> syn::Result<Vec<(Ident, Type)>> {
let mut equalities = Vec::new();

for current_type_ident in current_spec.type_idents.iter() {
forbid_same_alias(current_type_ident, current_spec, specs)?;

if let Some(equality) =
find_type_equality(context_type, current_type_ident, current_spec, specs)?
{
if let Some(equality) = find_type_equality(current_type_ident, current_spec, specs)? {
equalities.push(equality);
}
}
Expand Down Expand Up @@ -83,7 +109,6 @@ fn forbid_same_alias(
}

fn find_type_equality(
context_type: &TokenStream,
current_ident: &UseTypeIdent,
current_spec: &UseTypeSpec,
specs: &[UseTypeSpec],
Expand All @@ -101,6 +126,7 @@ fn find_type_equality(
let trait_path = &spec.trait_path;
let current_type_ident = &current_ident.type_ident;
let match_type_ident = &match_use_type.type_ident;
let context_type = &spec.context_type;

let equal_target: Type = parse2(quote! {
<#context_type as #trait_path>::#match_type_ident
Expand Down
Loading
Loading