From d56948d6957d255b9000fb3c47f5ba1aa2a9cc61 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 18 Feb 2026 21:06:23 +0100 Subject: [PATCH 1/9] Reorganize #[cgp_impl] constructs --- crates/cgp-macro-lib/src/cgp_impl/derive.rs | 63 +++++++ crates/cgp-macro-lib/src/cgp_impl/mod.rs | 7 + crates/cgp-macro-lib/src/cgp_impl/spec.rs | 41 +++++ .../cgp-macro-lib/src/cgp_impl/transform.rs | 76 ++++++++ .../cgp-macro-lib/src/entrypoints/cgp_impl.rs | 172 +----------------- crates/cgp-macro-lib/src/lib.rs | 1 + 6 files changed, 193 insertions(+), 167 deletions(-) create mode 100644 crates/cgp-macro-lib/src/cgp_impl/derive.rs create mode 100644 crates/cgp-macro-lib/src/cgp_impl/mod.rs create mode 100644 crates/cgp-macro-lib/src/cgp_impl/spec.rs create mode 100644 crates/cgp-macro-lib/src/cgp_impl/transform.rs diff --git a/crates/cgp-macro-lib/src/cgp_impl/derive.rs b/crates/cgp-macro-lib/src/cgp_impl/derive.rs new file mode 100644 index 00000000..54c79065 --- /dev/null +++ b/crates/cgp-macro-lib/src/cgp_impl/derive.rs @@ -0,0 +1,63 @@ +use proc_macro2::TokenStream; +use quote::{ToTokens, quote}; +use syn::{ItemImpl, parse_quote, parse2}; + +use crate::cgp_impl::{ImplProviderSpec, transform_impl_trait}; +use crate::derive_provider::{ + derive_component_name_from_provider_impl, derive_is_provider_for, derive_provider_struct, +}; + +pub fn derive_cgp_impl( + spec: ImplProviderSpec, + mut item_impl: ItemImpl, +) -> syn::Result { + let provider_impl = match &item_impl.trait_ { + Some((_, path, _)) => { + let consumer_trait_path = parse2(path.to_token_stream())?; + let context_type = item_impl.self_ty.as_ref(); + transform_impl_trait( + &item_impl, + &consumer_trait_path, + &spec.provider_type, + context_type, + )? + } + None => { + let consumer_trait_path = parse2(item_impl.self_ty.to_token_stream())?; + let context_type = parse_quote! { __Context__ }; + + item_impl + .generics + .params + .insert(0, parse_quote! { __Context__ }); + + transform_impl_trait( + &item_impl, + &consumer_trait_path, + &spec.provider_type, + &context_type, + )? + } + }; + + let component_type = match &spec.component_type { + Some(component_type) => component_type.clone(), + None => derive_component_name_from_provider_impl(&provider_impl)?, + }; + + let is_provider_for_impl: ItemImpl = derive_is_provider_for(&component_type, &provider_impl)?; + + let provider_struct = if spec.new_struct { + Some(derive_provider_struct(&provider_impl)?) + } else { + None + }; + + Ok(quote! { + #provider_struct + + #provider_impl + + #is_provider_for_impl + }) +} diff --git a/crates/cgp-macro-lib/src/cgp_impl/mod.rs b/crates/cgp-macro-lib/src/cgp_impl/mod.rs new file mode 100644 index 00000000..fc68d4d8 --- /dev/null +++ b/crates/cgp-macro-lib/src/cgp_impl/mod.rs @@ -0,0 +1,7 @@ +mod derive; +mod spec; +mod transform; + +pub use derive::*; +pub use spec::*; +pub use transform::*; diff --git a/crates/cgp-macro-lib/src/cgp_impl/spec.rs b/crates/cgp-macro-lib/src/cgp_impl/spec.rs new file mode 100644 index 00000000..5c7dcc34 --- /dev/null +++ b/crates/cgp-macro-lib/src/cgp_impl/spec.rs @@ -0,0 +1,41 @@ +use syn::parse::discouraged::Speculative; +use syn::parse::{Parse, ParseStream}; +use syn::token::Colon; +use syn::{Ident, Type}; + +pub struct ImplProviderSpec { + pub new_struct: bool, + pub provider_type: Type, + pub component_type: Option, +} + +impl Parse for ImplProviderSpec { + fn parse(input: ParseStream) -> syn::Result { + let new_struct = { + let fork = input.fork(); + let new_ident: Option = fork.parse().ok(); + match new_ident { + Some(new_ident) if new_ident == "new" => { + input.advance_to(&fork); + true + } + _ => false, + } + }; + + let provider_type = input.parse()?; + + let component_type = if let Some(_colon) = input.parse::>()? { + let component_type: Type = input.parse()?; + Some(component_type) + } else { + None + }; + + Ok(ImplProviderSpec { + new_struct, + provider_type, + component_type, + }) + } +} diff --git a/crates/cgp-macro-lib/src/cgp_impl/transform.rs b/crates/cgp-macro-lib/src/cgp_impl/transform.rs new file mode 100644 index 00000000..7fda1cb8 --- /dev/null +++ b/crates/cgp-macro-lib/src/cgp_impl/transform.rs @@ -0,0 +1,76 @@ +use proc_macro2::Span; +use quote::{ToTokens, quote}; +use syn::token::For; +use syn::{FnArg, Ident, ImplItem, ItemImpl, Type, parse2}; + +use crate::parse::SimpleType; +use crate::replace_self::{ + replace_self_receiver, replace_self_type, replace_self_var, to_snake_case_ident, +}; + +pub fn transform_impl_trait( + item_impl: &ItemImpl, + consumer_trait_path: &SimpleType, + provider_type: &Type, + context_type: &Type, +) -> syn::Result { + let context_var = if let Ok(ident) = parse2::(context_type.to_token_stream()) { + to_snake_case_ident(&ident) + } else { + Ident::new("__context__", Span::call_site()) + }; + + let local_assoc_types: Vec = item_impl + .items + .iter() + .filter_map(|item| { + if let ImplItem::Type(assoc_type) = item { + Some(assoc_type.ident.clone()) + } else { + None + } + }) + .collect(); + + let raw_out_impl = replace_self_type( + item_impl.to_token_stream(), + context_type.to_token_stream(), + &local_assoc_types, + ); + + let mut out_impl: ItemImpl = parse2(raw_out_impl)?; + out_impl.self_ty = Box::new(provider_type.clone()); + + let mut provider_trait_path: SimpleType = consumer_trait_path.clone(); + + match &mut provider_trait_path.generics { + Some(generics) => { + generics + .args + .insert(0, parse2(context_type.to_token_stream())?); + } + None => { + provider_trait_path.generics = Some(parse2(quote! { < #context_type > })?); + } + } + + out_impl.trait_ = Some(( + None, + parse2(provider_trait_path.to_token_stream())?, + For(Span::call_site()), + )); + + for item in out_impl.items.iter_mut() { + if let ImplItem::Fn(item_fn) = item + && let Some(arg) = item_fn.sig.inputs.first_mut() + && let FnArg::Receiver(receiver) = arg + { + *arg = replace_self_receiver(receiver, &context_var, context_type.to_token_stream()); + + let replaced_block = replace_self_var(item_fn.block.to_token_stream(), &context_var); + item_fn.block = parse2(replaced_block)?; + } + } + + Ok(out_impl) +} diff --git a/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs b/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs index b2652d0e..199b9029 100644 --- a/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs +++ b/crates/cgp-macro-lib/src/entrypoints/cgp_impl.rs @@ -1,173 +1,11 @@ -use proc_macro2::{Span, TokenStream}; -use quote::{ToTokens, quote}; -use syn::parse::discouraged::Speculative; -use syn::parse::{Parse, ParseStream}; -use syn::token::{Colon, For}; -use syn::{FnArg, Ident, ImplItem, ItemImpl, Type, parse_quote, parse2}; +use proc_macro2::TokenStream; +use syn::{ItemImpl, parse2}; -use crate::derive_provider::{ - derive_component_name_from_provider_impl, derive_is_provider_for, derive_provider_struct, -}; -use crate::parse::SimpleType; -use crate::replace_self::{ - replace_self_receiver, replace_self_type, replace_self_var, to_snake_case_ident, -}; +use crate::cgp_impl::{ImplProviderSpec, derive_cgp_impl}; pub fn cgp_impl(attr: TokenStream, body: TokenStream) -> syn::Result { let spec: ImplProviderSpec = parse2(attr)?; - let mut item_impl: ItemImpl = parse2(body)?; + let item_impl: ItemImpl = parse2(body)?; - let provider_impl = match &item_impl.trait_ { - Some((_, path, _)) => { - let consumer_trait_path = parse2(path.to_token_stream())?; - let context_type = item_impl.self_ty.as_ref(); - transform_impl_trait( - &item_impl, - &consumer_trait_path, - &spec.provider_type, - context_type, - )? - } - None => { - let consumer_trait_path = parse2(item_impl.self_ty.to_token_stream())?; - let context_type = parse_quote! { __Context__ }; - - item_impl - .generics - .params - .insert(0, parse_quote! { __Context__ }); - - transform_impl_trait( - &item_impl, - &consumer_trait_path, - &spec.provider_type, - &context_type, - )? - } - }; - - let component_type = match &spec.component_type { - Some(component_type) => component_type.clone(), - None => derive_component_name_from_provider_impl(&provider_impl)?, - }; - - let is_provider_for_impl: ItemImpl = derive_is_provider_for(&component_type, &provider_impl)?; - - let provider_struct = if spec.new_struct { - Some(derive_provider_struct(&provider_impl)?) - } else { - None - }; - - Ok(quote! { - #provider_struct - - #provider_impl - - #is_provider_for_impl - }) -} - -pub struct ImplProviderSpec { - pub new_struct: bool, - pub provider_type: Type, - pub component_type: Option, -} - -impl Parse for ImplProviderSpec { - fn parse(input: ParseStream) -> syn::Result { - let new_struct = { - let fork = input.fork(); - let new_ident: Option = fork.parse().ok(); - match new_ident { - Some(new_ident) if new_ident == "new" => { - input.advance_to(&fork); - true - } - _ => false, - } - }; - - let provider_type = input.parse()?; - - let component_type = if let Some(_colon) = input.parse::>()? { - let component_type: Type = input.parse()?; - Some(component_type) - } else { - None - }; - - Ok(ImplProviderSpec { - new_struct, - provider_type, - component_type, - }) - } -} - -pub fn transform_impl_trait( - item_impl: &ItemImpl, - consumer_trait_path: &SimpleType, - provider_type: &Type, - context_type: &Type, -) -> syn::Result { - let context_var = if let Ok(ident) = parse2::(context_type.to_token_stream()) { - to_snake_case_ident(&ident) - } else { - Ident::new("__context__", Span::call_site()) - }; - - let local_assoc_types: Vec = item_impl - .items - .iter() - .filter_map(|item| { - if let ImplItem::Type(assoc_type) = item { - Some(assoc_type.ident.clone()) - } else { - None - } - }) - .collect(); - - let raw_out_impl = replace_self_type( - item_impl.to_token_stream(), - context_type.to_token_stream(), - &local_assoc_types, - ); - - let mut out_impl: ItemImpl = parse2(raw_out_impl)?; - out_impl.self_ty = Box::new(provider_type.clone()); - - let mut provider_trait_path: SimpleType = consumer_trait_path.clone(); - - match &mut provider_trait_path.generics { - Some(generics) => { - generics - .args - .insert(0, parse2(context_type.to_token_stream())?); - } - None => { - provider_trait_path.generics = Some(parse2(quote! { < #context_type > })?); - } - } - - out_impl.trait_ = Some(( - None, - parse2(provider_trait_path.to_token_stream())?, - For(Span::call_site()), - )); - - for item in out_impl.items.iter_mut() { - if let ImplItem::Fn(item_fn) = item - && let Some(arg) = item_fn.sig.inputs.first_mut() - && let FnArg::Receiver(receiver) = arg - { - *arg = replace_self_receiver(receiver, &context_var, context_type.to_token_stream()); - - let replaced_block = replace_self_var(item_fn.block.to_token_stream(), &context_var); - item_fn.block = parse2(replaced_block)?; - } - } - - Ok(out_impl) + derive_cgp_impl(spec, item_impl) } diff --git a/crates/cgp-macro-lib/src/lib.rs b/crates/cgp-macro-lib/src/lib.rs index 15fe3b25..ff9bd1fb 100644 --- a/crates/cgp-macro-lib/src/lib.rs +++ b/crates/cgp-macro-lib/src/lib.rs @@ -9,6 +9,7 @@ extern crate alloc; pub(crate) mod blanket_trait; pub(crate) mod cgp_fn; +pub(crate) mod cgp_impl; pub(crate) mod check_components; pub(crate) mod delegate_components; pub(crate) mod derive_builder; From c9c0add166f55b75934424afe868567671fb681c Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 18 Feb 2026 21:10:34 +0100 Subject: [PATCH 2/9] Add derive_provider_impl helper --- crates/cgp-macro-lib/src/cgp_impl/derive.rs | 40 +++---------------- crates/cgp-macro-lib/src/cgp_impl/mod.rs | 2 + .../src/cgp_impl/provider_impl.rs | 38 ++++++++++++++++++ 3 files changed, 45 insertions(+), 35 deletions(-) create mode 100644 crates/cgp-macro-lib/src/cgp_impl/provider_impl.rs diff --git a/crates/cgp-macro-lib/src/cgp_impl/derive.rs b/crates/cgp-macro-lib/src/cgp_impl/derive.rs index 54c79065..61507b83 100644 --- a/crates/cgp-macro-lib/src/cgp_impl/derive.rs +++ b/crates/cgp-macro-lib/src/cgp_impl/derive.rs @@ -1,44 +1,14 @@ use proc_macro2::TokenStream; -use quote::{ToTokens, quote}; -use syn::{ItemImpl, parse_quote, parse2}; +use quote::quote; +use syn::ItemImpl; -use crate::cgp_impl::{ImplProviderSpec, transform_impl_trait}; +use crate::cgp_impl::{ImplProviderSpec, derive_provider_impl}; use crate::derive_provider::{ derive_component_name_from_provider_impl, derive_is_provider_for, derive_provider_struct, }; -pub fn derive_cgp_impl( - spec: ImplProviderSpec, - mut item_impl: ItemImpl, -) -> syn::Result { - let provider_impl = match &item_impl.trait_ { - Some((_, path, _)) => { - let consumer_trait_path = parse2(path.to_token_stream())?; - let context_type = item_impl.self_ty.as_ref(); - transform_impl_trait( - &item_impl, - &consumer_trait_path, - &spec.provider_type, - context_type, - )? - } - None => { - let consumer_trait_path = parse2(item_impl.self_ty.to_token_stream())?; - let context_type = parse_quote! { __Context__ }; - - item_impl - .generics - .params - .insert(0, parse_quote! { __Context__ }); - - transform_impl_trait( - &item_impl, - &consumer_trait_path, - &spec.provider_type, - &context_type, - )? - } - }; +pub fn derive_cgp_impl(spec: ImplProviderSpec, item_impl: ItemImpl) -> syn::Result { + let provider_impl = derive_provider_impl(&spec.provider_type, item_impl)?; let component_type = match &spec.component_type { Some(component_type) => component_type.clone(), diff --git a/crates/cgp-macro-lib/src/cgp_impl/mod.rs b/crates/cgp-macro-lib/src/cgp_impl/mod.rs index fc68d4d8..8ce7d35a 100644 --- a/crates/cgp-macro-lib/src/cgp_impl/mod.rs +++ b/crates/cgp-macro-lib/src/cgp_impl/mod.rs @@ -1,7 +1,9 @@ mod derive; +mod provider_impl; mod spec; mod transform; pub use derive::*; +pub use provider_impl::*; pub use spec::*; pub use transform::*; diff --git a/crates/cgp-macro-lib/src/cgp_impl/provider_impl.rs b/crates/cgp-macro-lib/src/cgp_impl/provider_impl.rs new file mode 100644 index 00000000..c357ec8d --- /dev/null +++ b/crates/cgp-macro-lib/src/cgp_impl/provider_impl.rs @@ -0,0 +1,38 @@ +use quote::ToTokens; +use syn::{ItemImpl, Type, parse_quote, parse2}; + +use crate::cgp_impl::transform_impl_trait; + +pub fn derive_provider_impl( + provider_type: &Type, + mut item_impl: ItemImpl, +) -> syn::Result { + match &item_impl.trait_ { + Some((_, path, _)) => { + let consumer_trait_path = parse2(path.to_token_stream())?; + let context_type = item_impl.self_ty.as_ref(); + transform_impl_trait( + &item_impl, + &consumer_trait_path, + provider_type, + context_type, + ) + } + None => { + let consumer_trait_path = parse2(item_impl.self_ty.to_token_stream())?; + let context_type = parse_quote! { __Context__ }; + + item_impl + .generics + .params + .insert(0, parse_quote! { __Context__ }); + + transform_impl_trait( + &item_impl, + &consumer_trait_path, + provider_type, + &context_type, + ) + } + } +} From ba8aa5cb84f4515d80d0a2ee99ed9a1b3fee4dd8 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 18 Feb 2026 21:27:16 +0100 Subject: [PATCH 3/9] Refactor extract_and_parse_implicit_args --- crates/cgp-macro-lib/src/cgp_fn/derive.rs | 14 ++------------ crates/cgp-macro-lib/src/cgp_fn/parse_implicits.rs | 14 ++++++++++++-- crates/cgp-macro-lib/src/cgp_impl/derive.rs | 5 ++++- crates/cgp-macro-lib/src/cgp_impl/implicit_args.rs | 11 +++++++++++ crates/cgp-macro-lib/src/cgp_impl/mod.rs | 2 ++ 5 files changed, 31 insertions(+), 15 deletions(-) create mode 100644 crates/cgp-macro-lib/src/cgp_impl/implicit_args.rs diff --git a/crates/cgp-macro-lib/src/cgp_fn/derive.rs b/crates/cgp-macro-lib/src/cgp_fn/derive.rs index 85fe16af..14c1dd3f 100644 --- a/crates/cgp-macro-lib/src/cgp_fn/derive.rs +++ b/crates/cgp-macro-lib/src/cgp_fn/derive.rs @@ -7,23 +7,13 @@ use syn::{Ident, ItemFn, Visibility}; use crate::cgp_fn::fn_body::inject_implicit_args; use crate::cgp_fn::item_impl::derive_item_impl; use crate::cgp_fn::item_trait::derive_item_trait; -use crate::cgp_fn::{extract_implicits_args, parse_function_attributes}; +use crate::cgp_fn::{extract_and_parse_implicit_args, parse_function_attributes}; pub fn derive_cgp_fn(trait_ident: &Ident, mut item_fn: ItemFn) -> syn::Result { - let receiver = match item_fn.sig.inputs.first() { - Some(syn::FnArg::Receiver(receiver)) => receiver.clone(), - _ => { - return Err(syn::Error::new_spanned( - &item_fn.sig.inputs, - "First argument must be self", - )); - } - }; - let visibility = item_fn.vis.clone(); item_fn.vis = Visibility::Inherited; - let implicit_args = extract_implicits_args(&receiver, &mut item_fn.sig.inputs)?; + let implicit_args = extract_and_parse_implicit_args(&mut item_fn.sig.inputs)?; let attributes = parse_function_attributes(&mut item_fn.attrs)?; diff --git a/crates/cgp-macro-lib/src/cgp_fn/parse_implicits.rs b/crates/cgp-macro-lib/src/cgp_fn/parse_implicits.rs index dbcd6253..e195ba9b 100644 --- a/crates/cgp-macro-lib/src/cgp_fn/parse_implicits.rs +++ b/crates/cgp-macro-lib/src/cgp_fn/parse_implicits.rs @@ -8,12 +8,22 @@ use syn::{Attribute, FnArg, Meta, Pat, PatIdent, PatType, Receiver}; use crate::cgp_fn::ImplicitArgField; use crate::derive_getter::parse_field_type; -pub fn extract_implicits_args( - receiver: &Receiver, +pub fn extract_and_parse_implicit_args( args: &mut Punctuated, ) -> syn::Result> { let implicit_fn_args = extract_implicit_args(args); + if implicit_fn_args.is_empty() { + return Ok(Vec::new()); + } + + let Some(FnArg::Receiver(receiver)) = args.first() else { + return Err(syn::Error::new_spanned( + &args, + "The first argument of a function with implicit arguments must be `self`", + )); + }; + if receiver.mutability.is_some() && implicit_fn_args.len() > 1 { return Err(syn::Error::new_spanned( &args, diff --git a/crates/cgp-macro-lib/src/cgp_impl/derive.rs b/crates/cgp-macro-lib/src/cgp_impl/derive.rs index 61507b83..5727ad72 100644 --- a/crates/cgp-macro-lib/src/cgp_impl/derive.rs +++ b/crates/cgp-macro-lib/src/cgp_impl/derive.rs @@ -7,7 +7,10 @@ use crate::derive_provider::{ derive_component_name_from_provider_impl, derive_is_provider_for, derive_provider_struct, }; -pub fn derive_cgp_impl(spec: ImplProviderSpec, item_impl: ItemImpl) -> syn::Result { +pub fn derive_cgp_impl( + spec: ImplProviderSpec, + mut item_impl: ItemImpl, +) -> syn::Result { let provider_impl = derive_provider_impl(&spec.provider_type, item_impl)?; let component_type = match &spec.component_type { diff --git a/crates/cgp-macro-lib/src/cgp_impl/implicit_args.rs b/crates/cgp-macro-lib/src/cgp_impl/implicit_args.rs new file mode 100644 index 00000000..ade79d01 --- /dev/null +++ b/crates/cgp-macro-lib/src/cgp_impl/implicit_args.rs @@ -0,0 +1,11 @@ +use syn::punctuated::Punctuated; +use syn::token::Comma; +use syn::{FnArg, ImplItem, ImplItemFn, ItemImpl}; + +use crate::cgp_fn::{ImplicitArgField, extract_implicit_args, parse_implicit_arg}; + +pub fn extract_implicit_args_from_item_impl(item_impl: &mut ItemImpl) { + for item in item_impl.items.iter_mut() { + if let ImplItem::Fn(method) = item {} + } +} diff --git a/crates/cgp-macro-lib/src/cgp_impl/mod.rs b/crates/cgp-macro-lib/src/cgp_impl/mod.rs index 8ce7d35a..5cf3e955 100644 --- a/crates/cgp-macro-lib/src/cgp_impl/mod.rs +++ b/crates/cgp-macro-lib/src/cgp_impl/mod.rs @@ -1,9 +1,11 @@ mod derive; +mod implicit_args; mod provider_impl; mod spec; mod transform; pub use derive::*; +pub use implicit_args::*; pub use provider_impl::*; pub use spec::*; pub use transform::*; From b587faa2d5c89f5252127aeec489f57c956b8858 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 18 Feb 2026 21:42:31 +0100 Subject: [PATCH 4/9] Draft implement implicit args in #[cgp_impl] --- crates/cgp-macro-lib/src/cgp_fn/bounds.rs | 30 +++++++++++++++++++ crates/cgp-macro-lib/src/cgp_fn/item_impl.rs | 24 ++++----------- crates/cgp-macro-lib/src/cgp_fn/mod.rs | 3 ++ crates/cgp-macro-lib/src/cgp_fn/spec.rs | 1 + crates/cgp-macro-lib/src/cgp_impl/derive.rs | 16 ++++++++-- .../src/cgp_impl/implicit_args.rs | 27 ++++++++++++----- crates/cgp-macro-lib/src/cgp_impl/mod.rs | 1 - .../src/derive_getter/getter_field.rs | 1 + 8 files changed, 75 insertions(+), 28 deletions(-) create mode 100644 crates/cgp-macro-lib/src/cgp_fn/bounds.rs diff --git a/crates/cgp-macro-lib/src/cgp_fn/bounds.rs b/crates/cgp-macro-lib/src/cgp_fn/bounds.rs new file mode 100644 index 00000000..76ec19c0 --- /dev/null +++ b/crates/cgp-macro-lib/src/cgp_fn/bounds.rs @@ -0,0 +1,30 @@ +use quote::ToTokens; +use syn::TypeParamBound; +use syn::punctuated::Punctuated; +use syn::token::Plus; + +use crate::cgp_fn::ImplicitArgField; +use crate::derive_getter::derive_getter_constraint; +use crate::symbol::symbol_from_string; + +pub fn build_implicit_args_bounds( + implicit_args: &[ImplicitArgField], +) -> syn::Result> { + let mut constraints: Punctuated = Punctuated::new(); + + for arg in implicit_args { + let field_symbol = symbol_from_string(&arg.field_name.to_string()); + + let constraint = derive_getter_constraint( + &arg.field_type, + &arg.field_mut, + &arg.field_mode, + field_symbol.to_token_stream(), + &None, + )?; + + constraints.push(constraint); + } + + Ok(constraints) +} diff --git a/crates/cgp-macro-lib/src/cgp_fn/item_impl.rs b/crates/cgp-macro-lib/src/cgp_fn/item_impl.rs index 23be35c9..9b62e23d 100644 --- a/crates/cgp-macro-lib/src/cgp_fn/item_impl.rs +++ b/crates/cgp-macro-lib/src/cgp_fn/item_impl.rs @@ -4,10 +4,9 @@ use syn::token::Plus; use syn::{Generics, Ident, ItemFn, ItemImpl, TypeParamBound, parse2}; use crate::cgp_fn::{ - FunctionAttributes, ImplicitArgField, derive_use_type_trait_bounds, substitute_abstract_type, + FunctionAttributes, ImplicitArgField, build_implicit_args_bounds, derive_use_type_trait_bounds, + substitute_abstract_type, }; -use crate::derive_getter::derive_getter_constraint; -use crate::symbol::symbol_from_string; pub fn derive_item_impl( trait_ident: &Ident, @@ -51,22 +50,11 @@ pub fn derive_item_impl( { let where_clause = item_impl.generics.make_where_clause(); + let bounds = build_implicit_args_bounds(implicit_args)?; - for arg in implicit_args { - let field_symbol = symbol_from_string(&arg.field_name.to_string()); - - let constraint = derive_getter_constraint( - &arg.field_type, - &arg.field_mut, - &arg.field_mode, - field_symbol.to_token_stream(), - &None, - )?; - - where_clause.predicates.push(parse2(quote! { - Self: #constraint - })?); - } + where_clause.predicates.push(parse2(quote! { + Self: #bounds + })?); } if !attributes.use_type.is_empty() { diff --git a/crates/cgp-macro-lib/src/cgp_fn/mod.rs b/crates/cgp-macro-lib/src/cgp_fn/mod.rs index a33875ba..803a2a9c 100644 --- a/crates/cgp-macro-lib/src/cgp_fn/mod.rs +++ b/crates/cgp-macro-lib/src/cgp_fn/mod.rs @@ -1,4 +1,5 @@ mod attributes; +mod bounds; mod derive; mod fn_body; mod item_impl; @@ -10,7 +11,9 @@ mod type_equality; mod use_type; pub use attributes::*; +pub use bounds::*; pub use derive::*; +pub use fn_body::*; pub use parse_implicits::*; pub use spec::*; pub use substitute_type::*; diff --git a/crates/cgp-macro-lib/src/cgp_fn/spec.rs b/crates/cgp-macro-lib/src/cgp_fn/spec.rs index 8b52c87e..a6bdd61d 100644 --- a/crates/cgp-macro-lib/src/cgp_fn/spec.rs +++ b/crates/cgp-macro-lib/src/cgp_fn/spec.rs @@ -5,6 +5,7 @@ use crate::cgp_fn::UseTypeSpec; use crate::derive_getter::FieldMode; use crate::parse::SimpleType; +#[derive(Clone, Eq, PartialEq)] pub struct ImplicitArgField { pub field_name: Ident, pub field_type: Type, diff --git a/crates/cgp-macro-lib/src/cgp_impl/derive.rs b/crates/cgp-macro-lib/src/cgp_impl/derive.rs index 5727ad72..ea9795fc 100644 --- a/crates/cgp-macro-lib/src/cgp_impl/derive.rs +++ b/crates/cgp-macro-lib/src/cgp_impl/derive.rs @@ -1,8 +1,9 @@ use proc_macro2::TokenStream; use quote::quote; -use syn::ItemImpl; +use syn::{ItemImpl, parse2}; -use crate::cgp_impl::{ImplProviderSpec, derive_provider_impl}; +use crate::cgp_fn::build_implicit_args_bounds; +use crate::cgp_impl::{ImplProviderSpec, derive_provider_impl, implicit_args}; use crate::derive_provider::{ derive_component_name_from_provider_impl, derive_is_provider_for, derive_provider_struct, }; @@ -11,6 +12,17 @@ pub fn derive_cgp_impl( spec: ImplProviderSpec, mut item_impl: ItemImpl, ) -> syn::Result { + let implicit_args = implicit_args::extract_implicit_args_from_impl_items(&mut item_impl.items)?; + + if !implicit_args.is_empty() { + let where_clause = item_impl.generics.make_where_clause(); + let bounds = build_implicit_args_bounds(&implicit_args)?; + + where_clause.predicates.push(parse2(quote! { + Self: #bounds + })?); + } + let provider_impl = derive_provider_impl(&spec.provider_type, item_impl)?; let component_type = match &spec.component_type { diff --git a/crates/cgp-macro-lib/src/cgp_impl/implicit_args.rs b/crates/cgp-macro-lib/src/cgp_impl/implicit_args.rs index ade79d01..8f351bdc 100644 --- a/crates/cgp-macro-lib/src/cgp_impl/implicit_args.rs +++ b/crates/cgp-macro-lib/src/cgp_impl/implicit_args.rs @@ -1,11 +1,24 @@ -use syn::punctuated::Punctuated; -use syn::token::Comma; -use syn::{FnArg, ImplItem, ImplItemFn, ItemImpl}; +use syn::ImplItem; -use crate::cgp_fn::{ImplicitArgField, extract_implicit_args, parse_implicit_arg}; +use crate::cgp_fn::{ImplicitArgField, extract_and_parse_implicit_args, inject_implicit_args}; -pub fn extract_implicit_args_from_item_impl(item_impl: &mut ItemImpl) { - for item in item_impl.items.iter_mut() { - if let ImplItem::Fn(method) = item {} +pub fn extract_implicit_args_from_impl_items( + impl_items: &mut [ImplItem], +) -> syn::Result> { + let mut all_implicit_args = Vec::new(); + + for item in impl_items { + if let ImplItem::Fn(method) = item { + let implicit_args = extract_and_parse_implicit_args(&mut method.sig.inputs)?; + inject_implicit_args(&implicit_args, &mut method.block)?; + + for implicit_arg in implicit_args { + if !all_implicit_args.contains(&implicit_arg) { + all_implicit_args.push(implicit_arg); + } + } + } } + + Ok(all_implicit_args) } diff --git a/crates/cgp-macro-lib/src/cgp_impl/mod.rs b/crates/cgp-macro-lib/src/cgp_impl/mod.rs index 5cf3e955..631ad54b 100644 --- a/crates/cgp-macro-lib/src/cgp_impl/mod.rs +++ b/crates/cgp-macro-lib/src/cgp_impl/mod.rs @@ -5,7 +5,6 @@ mod spec; mod transform; pub use derive::*; -pub use implicit_args::*; pub use provider_impl::*; pub use spec::*; pub use transform::*; diff --git a/crates/cgp-macro-lib/src/derive_getter/getter_field.rs b/crates/cgp-macro-lib/src/derive_getter/getter_field.rs index 8fec551f..547b9c17 100644 --- a/crates/cgp-macro-lib/src/derive_getter/getter_field.rs +++ b/crates/cgp-macro-lib/src/derive_getter/getter_field.rs @@ -11,6 +11,7 @@ pub struct GetterField { pub receiver_mode: ReceiverMode, } +#[derive(Clone, Eq, PartialEq)] pub enum FieldMode { Reference, OptionRef, From 06bcfd9691f3ae6bcc7c5d5cf68838840761f117 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 18 Feb 2026 21:46:30 +0100 Subject: [PATCH 5/9] Basic implicit test working --- .../cgp_impl/implicit_args/basic.rs | 27 +++++++++++++++++++ .../cgp_impl/implicit_args/mod.rs | 1 + .../tests/component_tests/cgp_impl/mod.rs | 1 + 3 files changed, 29 insertions(+) create mode 100644 crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/basic.rs create mode 100644 crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/mod.rs diff --git a/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/basic.rs b/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/basic.rs new file mode 100644 index 00000000..a80440a0 --- /dev/null +++ b/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/basic.rs @@ -0,0 +1,27 @@ +use cgp::prelude::*; + +#[cgp_component(AreaCalculator)] +pub trait CanCalculateArea { + fn area(&self) -> f64; +} + +#[cgp_impl(new RectangleArea)] +impl AreaCalculator { + fn area(&self, #[implicit] width: f64, #[implicit] height: f64) -> f64 { + width * height + } +} + +#[derive(HasField)] +pub struct Rectangle { + pub width: f64, + pub height: f64, +} + +delegate_and_check_components! { + CanUseRectangle for Rectangle; + Rectangle { + AreaCalculatorComponent: + RectangleArea, + } +} diff --git a/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/mod.rs b/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/mod.rs new file mode 100644 index 00000000..38883ee0 --- /dev/null +++ b/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/mod.rs @@ -0,0 +1 @@ +pub mod basic; diff --git a/crates/cgp-tests/tests/component_tests/cgp_impl/mod.rs b/crates/cgp-tests/tests/component_tests/cgp_impl/mod.rs index a1da2f99..46750cc2 100644 --- a/crates/cgp-tests/tests/component_tests/cgp_impl/mod.rs +++ b/crates/cgp-tests/tests/component_tests/cgp_impl/mod.rs @@ -1,2 +1,3 @@ pub mod basic; +pub mod implicit_args; pub mod implicit_context; From 977651560bdf6a7695300b6a76b552ef6fbd6d5f Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 18 Feb 2026 23:04:29 +0100 Subject: [PATCH 6/9] Draft support #[use_type] in #[cgp_impl] --- crates/cgp-macro-lib/src/cgp_fn/apply_type.rs | 30 ++++++++++++++ crates/cgp-macro-lib/src/cgp_fn/item_impl.rs | 23 ++--------- crates/cgp-macro-lib/src/cgp_fn/mod.rs | 2 + .../cgp-macro-lib/src/cgp_impl/attributes.rs | 40 +++++++++++++++++++ crates/cgp-macro-lib/src/cgp_impl/derive.rs | 9 ++++- crates/cgp-macro-lib/src/cgp_impl/mod.rs | 1 + 6 files changed, 85 insertions(+), 20 deletions(-) create mode 100644 crates/cgp-macro-lib/src/cgp_fn/apply_type.rs create mode 100644 crates/cgp-macro-lib/src/cgp_impl/attributes.rs diff --git a/crates/cgp-macro-lib/src/cgp_fn/apply_type.rs b/crates/cgp-macro-lib/src/cgp_fn/apply_type.rs new file mode 100644 index 00000000..894fac04 --- /dev/null +++ b/crates/cgp-macro-lib/src/cgp_fn/apply_type.rs @@ -0,0 +1,30 @@ +use quote::{ToTokens, quote}; +use syn::punctuated::Punctuated; +use syn::token::Plus; +use syn::{ItemImpl, TypeParamBound, parse2}; + +use crate::cgp_fn::{UseTypeSpec, derive_use_type_trait_bounds, substitute_abstract_type}; + +pub fn apply_use_type_attributes_to_item_impl( + item_impl: &ItemImpl, + use_type_specs: &[UseTypeSpec], +) -> syn::Result { + let mut item_impl: ItemImpl = parse2(substitute_abstract_type( + "e! { Self }, + &use_type_specs, + item_impl.to_token_stream(), + ))?; + + let bounds = derive_use_type_trait_bounds("e! { Self }, &use_type_specs)?; + let bounds = Punctuated::::from_iter(bounds); + + item_impl + .generics + .make_where_clause() + .predicates + .push(parse2(quote! { + Self: #bounds + })?); + + Ok(item_impl) +} diff --git a/crates/cgp-macro-lib/src/cgp_fn/item_impl.rs b/crates/cgp-macro-lib/src/cgp_fn/item_impl.rs index 9b62e23d..49a1f3ff 100644 --- a/crates/cgp-macro-lib/src/cgp_fn/item_impl.rs +++ b/crates/cgp-macro-lib/src/cgp_fn/item_impl.rs @@ -1,11 +1,11 @@ -use quote::{ToTokens, quote}; +use quote::quote; use syn::punctuated::Punctuated; use syn::token::Plus; use syn::{Generics, Ident, ItemFn, ItemImpl, TypeParamBound, parse2}; use crate::cgp_fn::{ - FunctionAttributes, ImplicitArgField, build_implicit_args_bounds, derive_use_type_trait_bounds, - substitute_abstract_type, + FunctionAttributes, ImplicitArgField, apply_use_type_attributes_to_item_impl, + build_implicit_args_bounds, }; pub fn derive_item_impl( @@ -58,22 +58,7 @@ pub fn derive_item_impl( } if !attributes.use_type.is_empty() { - item_impl = parse2(substitute_abstract_type( - "e! { Self }, - &attributes.use_type, - item_impl.to_token_stream(), - ))?; - - let bounds = derive_use_type_trait_bounds("e! { Self }, &attributes.use_type)?; - let bounds = Punctuated::::from_iter(bounds); - - item_impl - .generics - .make_where_clause() - .predicates - .push(parse2(quote! { - Self: #bounds - })?); + item_impl = apply_use_type_attributes_to_item_impl(&item_impl, &attributes.use_type)?; } Ok(item_impl) diff --git a/crates/cgp-macro-lib/src/cgp_fn/mod.rs b/crates/cgp-macro-lib/src/cgp_fn/mod.rs index 803a2a9c..d4946081 100644 --- a/crates/cgp-macro-lib/src/cgp_fn/mod.rs +++ b/crates/cgp-macro-lib/src/cgp_fn/mod.rs @@ -1,3 +1,4 @@ +mod apply_type; mod attributes; mod bounds; mod derive; @@ -10,6 +11,7 @@ mod substitute_type; mod type_equality; mod use_type; +pub use apply_type::*; pub use attributes::*; pub use bounds::*; pub use derive::*; diff --git a/crates/cgp-macro-lib/src/cgp_impl/attributes.rs b/crates/cgp-macro-lib/src/cgp_impl/attributes.rs new file mode 100644 index 00000000..a492d491 --- /dev/null +++ b/crates/cgp-macro-lib/src/cgp_impl/attributes.rs @@ -0,0 +1,40 @@ +use core::mem; + +use syn::Attribute; +use syn::punctuated::Punctuated; +use syn::token::Comma; + +use crate::cgp_fn::UseTypeSpec; +use crate::parse::SimpleType; + +pub fn parse_impl_attributes(attributes: &mut Vec) -> syn::Result { + let mut parsed_attributes = ImplAttributes::default(); + + let in_attributes = mem::take(attributes); + + for attribute in in_attributes.into_iter() { + if let Some(ident) = attribute.path().get_ident() { + if ident == "uses" { + let uses = + attribute.parse_args_with(Punctuated::::parse_terminated)?; + parsed_attributes.uses.extend(uses); + } else if ident == "use_type" { + let use_type = attribute + .parse_args_with(Punctuated::::parse_terminated)?; + parsed_attributes.use_type.extend(use_type); + } else { + attributes.push(attribute); + } + } else { + attributes.push(attribute); + } + } + + Ok(parsed_attributes) +} + +#[derive(Default)] +pub struct ImplAttributes { + pub uses: Vec, + pub use_type: Vec, +} diff --git a/crates/cgp-macro-lib/src/cgp_impl/derive.rs b/crates/cgp-macro-lib/src/cgp_impl/derive.rs index ea9795fc..91590982 100644 --- a/crates/cgp-macro-lib/src/cgp_impl/derive.rs +++ b/crates/cgp-macro-lib/src/cgp_impl/derive.rs @@ -2,7 +2,8 @@ use proc_macro2::TokenStream; use quote::quote; use syn::{ItemImpl, parse2}; -use crate::cgp_fn::build_implicit_args_bounds; +use crate::cgp_fn::{apply_use_type_attributes_to_item_impl, build_implicit_args_bounds}; +use crate::cgp_impl::attributes::parse_impl_attributes; use crate::cgp_impl::{ImplProviderSpec, derive_provider_impl, implicit_args}; use crate::derive_provider::{ derive_component_name_from_provider_impl, derive_is_provider_for, derive_provider_struct, @@ -12,6 +13,8 @@ pub fn derive_cgp_impl( spec: ImplProviderSpec, mut item_impl: ItemImpl, ) -> syn::Result { + let attributes = parse_impl_attributes(&mut item_impl.attrs)?; + let implicit_args = implicit_args::extract_implicit_args_from_impl_items(&mut item_impl.items)?; if !implicit_args.is_empty() { @@ -23,6 +26,10 @@ pub fn derive_cgp_impl( })?); } + if !attributes.use_type.is_empty() { + item_impl = apply_use_type_attributes_to_item_impl(&item_impl, &attributes.use_type)?; + } + let provider_impl = derive_provider_impl(&spec.provider_type, item_impl)?; let component_type = match &spec.component_type { diff --git a/crates/cgp-macro-lib/src/cgp_impl/mod.rs b/crates/cgp-macro-lib/src/cgp_impl/mod.rs index 631ad54b..8a286d1c 100644 --- a/crates/cgp-macro-lib/src/cgp_impl/mod.rs +++ b/crates/cgp-macro-lib/src/cgp_impl/mod.rs @@ -1,3 +1,4 @@ +mod attributes; mod derive; mod implicit_args; mod provider_impl; From 01e812893b80049855d37dc234b2d62c4ddce391 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 18 Feb 2026 23:10:28 +0100 Subject: [PATCH 7/9] Add basic test for `#[use_type]` in `#[cgp_impl]` --- .../cgp_impl/abstract_types/basic.rs | 41 +++++++++++++++++++ .../cgp_impl/abstract_types/mod.rs | 1 + .../cgp_impl/implicit_args/mod.rs | 2 +- .../tests/component_tests/cgp_impl/mod.rs | 1 + 4 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 crates/cgp-tests/tests/component_tests/cgp_impl/abstract_types/basic.rs create mode 100644 crates/cgp-tests/tests/component_tests/cgp_impl/abstract_types/mod.rs diff --git a/crates/cgp-tests/tests/component_tests/cgp_impl/abstract_types/basic.rs b/crates/cgp-tests/tests/component_tests/cgp_impl/abstract_types/basic.rs new file mode 100644 index 00000000..d2af83ee --- /dev/null +++ b/crates/cgp-tests/tests/component_tests/cgp_impl/abstract_types/basic.rs @@ -0,0 +1,41 @@ +use std::ops::Mul; + +use cgp::prelude::*; + +#[cgp_type] +pub trait HasScalarType { + type Scalar; +} + +#[cgp_component(AreaCalculator)] +pub trait CanCalculateArea: HasScalarType { + fn area(&self) -> Self::Scalar; +} + +#[cgp_impl(new RectangleArea)] +#[use_type(HasScalarType::Scalar)] +impl AreaCalculator +where + Scalar: Mul + Clone, +{ + fn area(&self, #[implicit] width: Scalar, #[implicit] height: Scalar) -> Scalar { + width * height + } +} + + +#[derive(HasField)] +pub struct Rectangle { + pub width: f64, + pub height: f64, +} + +delegate_and_check_components! { + CanUseRectangle for Rectangle; + Rectangle { + ScalarTypeProviderComponent: + UseType, + AreaCalculatorComponent: + RectangleArea, + } +} diff --git a/crates/cgp-tests/tests/component_tests/cgp_impl/abstract_types/mod.rs b/crates/cgp-tests/tests/component_tests/cgp_impl/abstract_types/mod.rs new file mode 100644 index 00000000..6ef0f7c6 --- /dev/null +++ b/crates/cgp-tests/tests/component_tests/cgp_impl/abstract_types/mod.rs @@ -0,0 +1 @@ +pub mod basic; \ No newline at end of file diff --git a/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/mod.rs b/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/mod.rs index 38883ee0..6ef0f7c6 100644 --- a/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/mod.rs +++ b/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/mod.rs @@ -1 +1 @@ -pub mod basic; +pub mod basic; \ No newline at end of file diff --git a/crates/cgp-tests/tests/component_tests/cgp_impl/mod.rs b/crates/cgp-tests/tests/component_tests/cgp_impl/mod.rs index 46750cc2..89b4aa2a 100644 --- a/crates/cgp-tests/tests/component_tests/cgp_impl/mod.rs +++ b/crates/cgp-tests/tests/component_tests/cgp_impl/mod.rs @@ -1,3 +1,4 @@ pub mod basic; pub mod implicit_args; pub mod implicit_context; +pub mod abstract_types; \ No newline at end of file From e7d8f573c8ddbf7c0c296f2401daf9595a08551d Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 18 Feb 2026 23:20:54 +0100 Subject: [PATCH 8/9] Implement and test #[uses] with #[cgp_impl] --- crates/cgp-macro-lib/src/cgp_fn/item_impl.rs | 2 +- crates/cgp-macro-lib/src/cgp_impl/derive.rs | 20 ++++++++++- .../cgp_impl/abstract_types/basic.rs | 1 - .../cgp_impl/abstract_types/mod.rs | 2 +- .../cgp_impl/implicit_args/import.rs | 33 +++++++++++++++++++ .../cgp_impl/implicit_args/mod.rs | 3 +- .../tests/component_tests/cgp_impl/mod.rs | 2 +- 7 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/import.rs diff --git a/crates/cgp-macro-lib/src/cgp_fn/item_impl.rs b/crates/cgp-macro-lib/src/cgp_fn/item_impl.rs index 49a1f3ff..a8868f01 100644 --- a/crates/cgp-macro-lib/src/cgp_fn/item_impl.rs +++ b/crates/cgp-macro-lib/src/cgp_fn/item_impl.rs @@ -48,7 +48,7 @@ pub fn derive_item_impl( } } - { + if !implicit_args.is_empty() { let where_clause = item_impl.generics.make_where_clause(); let bounds = build_implicit_args_bounds(implicit_args)?; diff --git a/crates/cgp-macro-lib/src/cgp_impl/derive.rs b/crates/cgp-macro-lib/src/cgp_impl/derive.rs index 91590982..d8df9edb 100644 --- a/crates/cgp-macro-lib/src/cgp_impl/derive.rs +++ b/crates/cgp-macro-lib/src/cgp_impl/derive.rs @@ -1,6 +1,8 @@ use proc_macro2::TokenStream; use quote::quote; -use syn::{ItemImpl, parse2}; +use syn::punctuated::Punctuated; +use syn::token::Plus; +use syn::{ItemImpl, TypeParamBound, parse2}; use crate::cgp_fn::{apply_use_type_attributes_to_item_impl, build_implicit_args_bounds}; use crate::cgp_impl::attributes::parse_impl_attributes; @@ -30,6 +32,22 @@ pub fn derive_cgp_impl( item_impl = apply_use_type_attributes_to_item_impl(&item_impl, &attributes.use_type)?; } + if !attributes.uses.is_empty() { + let mut bounds: Punctuated = Punctuated::default(); + + for import in attributes.uses.iter() { + bounds.push(parse2(quote! { #import })?); + } + + item_impl + .generics + .make_where_clause() + .predicates + .push(parse2(quote! { + Self: #bounds + })?); + } + let provider_impl = derive_provider_impl(&spec.provider_type, item_impl)?; let component_type = match &spec.component_type { diff --git a/crates/cgp-tests/tests/component_tests/cgp_impl/abstract_types/basic.rs b/crates/cgp-tests/tests/component_tests/cgp_impl/abstract_types/basic.rs index d2af83ee..4df1090b 100644 --- a/crates/cgp-tests/tests/component_tests/cgp_impl/abstract_types/basic.rs +++ b/crates/cgp-tests/tests/component_tests/cgp_impl/abstract_types/basic.rs @@ -23,7 +23,6 @@ where } } - #[derive(HasField)] pub struct Rectangle { pub width: f64, diff --git a/crates/cgp-tests/tests/component_tests/cgp_impl/abstract_types/mod.rs b/crates/cgp-tests/tests/component_tests/cgp_impl/abstract_types/mod.rs index 6ef0f7c6..38883ee0 100644 --- a/crates/cgp-tests/tests/component_tests/cgp_impl/abstract_types/mod.rs +++ b/crates/cgp-tests/tests/component_tests/cgp_impl/abstract_types/mod.rs @@ -1 +1 @@ -pub mod basic; \ No newline at end of file +pub mod basic; diff --git a/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/import.rs b/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/import.rs new file mode 100644 index 00000000..d7692e2e --- /dev/null +++ b/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/import.rs @@ -0,0 +1,33 @@ +use cgp::prelude::*; + +#[cgp_component(AreaCalculator)] +pub trait CanCalculateArea { + fn area(&self) -> f64; +} + +#[cgp_impl(new RectangleAreaCalculator)] +#[uses(RectangleArea)] +impl AreaCalculator { + fn area(&self) -> f64 { + self.rectangle_area() + } +} + +#[cgp_fn] +pub fn rectangle_area(&self, #[implicit] width: f64, #[implicit] height: f64) -> f64 { + width * height +} + +#[derive(HasField)] +pub struct Rectangle { + pub width: f64, + pub height: f64, +} + +delegate_and_check_components! { + CanUseRectangle for Rectangle; + Rectangle { + AreaCalculatorComponent: + RectangleAreaCalculator, + } +} diff --git a/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/mod.rs b/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/mod.rs index 6ef0f7c6..65c0dea3 100644 --- a/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/mod.rs +++ b/crates/cgp-tests/tests/component_tests/cgp_impl/implicit_args/mod.rs @@ -1 +1,2 @@ -pub mod basic; \ No newline at end of file +pub mod basic; +pub mod import; diff --git a/crates/cgp-tests/tests/component_tests/cgp_impl/mod.rs b/crates/cgp-tests/tests/component_tests/cgp_impl/mod.rs index 89b4aa2a..ebe001d3 100644 --- a/crates/cgp-tests/tests/component_tests/cgp_impl/mod.rs +++ b/crates/cgp-tests/tests/component_tests/cgp_impl/mod.rs @@ -1,4 +1,4 @@ +pub mod abstract_types; pub mod basic; pub mod implicit_args; pub mod implicit_context; -pub mod abstract_types; \ No newline at end of file From 674ff6667cc6f7b14797b2c53f5b241872ba7311 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Wed, 18 Feb 2026 23:23:18 +0100 Subject: [PATCH 9/9] Fix clippy --- crates/cgp-macro-lib/src/cgp_fn/apply_type.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/cgp-macro-lib/src/cgp_fn/apply_type.rs b/crates/cgp-macro-lib/src/cgp_fn/apply_type.rs index 894fac04..22542627 100644 --- a/crates/cgp-macro-lib/src/cgp_fn/apply_type.rs +++ b/crates/cgp-macro-lib/src/cgp_fn/apply_type.rs @@ -11,11 +11,11 @@ pub fn apply_use_type_attributes_to_item_impl( ) -> syn::Result { let mut item_impl: ItemImpl = parse2(substitute_abstract_type( "e! { Self }, - &use_type_specs, + use_type_specs, item_impl.to_token_stream(), ))?; - let bounds = derive_use_type_trait_bounds("e! { Self }, &use_type_specs)?; + let bounds = derive_use_type_trait_bounds("e! { Self }, use_type_specs)?; let bounds = Punctuated::::from_iter(bounds); item_impl