From ab0da5eebfdb6bd4340dfae1136b4c189494f3bd Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sat, 31 Jan 2026 21:25:37 +0100 Subject: [PATCH 01/11] Parse optional associated type in auto getter trait --- .../src/derive_getter/blanket.rs | 20 ++++++++-- .../src/derive_getter/constraint.rs | 14 ++++--- .../cgp-macro-lib/src/derive_getter/parse.rs | 40 ++++++++++++++++++- .../src/derive_getter/use_field.rs | 2 +- .../src/derive_getter/use_fields.rs | 2 +- .../src/entrypoints/cgp_auto_getter.rs | 4 +- .../src/entrypoints/cgp_getter.rs | 2 +- 7 files changed, 68 insertions(+), 16 deletions(-) diff --git a/crates/cgp-macro-lib/src/derive_getter/blanket.rs b/crates/cgp-macro-lib/src/derive_getter/blanket.rs index bbe9e7cb..e887746b 100644 --- a/crates/cgp-macro-lib/src/derive_getter/blanket.rs +++ b/crates/cgp-macro-lib/src/derive_getter/blanket.rs @@ -14,12 +14,13 @@ pub fn derive_blanket_impl( context_type: &Ident, consumer_trait: &ItemTrait, fields: &[GetterField], + field_assoc_type: &Option, ) -> syn::Result { let consumer_name = &consumer_trait.ident; let supertrait_constraints = consumer_trait.supertraits.clone(); - let mut methods: TokenStream = TokenStream::new(); + let mut items: TokenStream = TokenStream::new(); let mut generics = consumer_trait.generics.clone(); @@ -27,6 +28,16 @@ pub fn derive_blanket_impl( .params .insert(0, parse2(context_type.to_token_stream())?); + if let Some(field_assoc_type) = field_assoc_type { + generics + .params + .push(parse2(field_assoc_type.to_token_stream())?); + + items.extend(quote! { + type #field_assoc_type = #field_assoc_type; + }); + } + let where_clause = generics.make_where_clause(); if !supertrait_constraints.is_empty() { @@ -53,9 +64,10 @@ pub fn derive_blanket_impl( None, ); - methods.extend(method); + items.extend(method); - let constraint = derive_getter_constraint(field, quote! { #field_symbol })?; + let constraint = + derive_getter_constraint(field, quote! { #field_symbol }, field_assoc_type)?; where_clause.predicates.push(parse2(quote! { #receiver_type: #constraint @@ -69,7 +81,7 @@ pub fn derive_blanket_impl( impl #impl_generics #consumer_name #type_generics for #context_type #where_clause { - #methods + #items } })?; diff --git a/crates/cgp-macro-lib/src/derive_getter/constraint.rs b/crates/cgp-macro-lib/src/derive_getter/constraint.rs index 8f977e6c..543c5d4d 100644 --- a/crates/cgp-macro-lib/src/derive_getter/constraint.rs +++ b/crates/cgp-macro-lib/src/derive_getter/constraint.rs @@ -1,28 +1,32 @@ use proc_macro2::TokenStream; use quote::quote; -use syn::{TypeParamBound, parse2}; +use syn::{Ident, TypeParamBound, parse_quote, parse2}; use crate::derive_getter::{FieldMode, GetterField}; pub fn derive_getter_constraint( spec: &GetterField, field_symbol: TokenStream, + field_assoc_type: &Option, ) -> syn::Result { - let provider_type = &spec.field_type; + let field_type = match field_assoc_type { + Some(field_assoc_type) => parse_quote! { #field_assoc_type }, + None => spec.field_type.clone(), + }; let constraint = if spec.field_mut.is_none() { if let FieldMode::Slice = spec.field_mode { quote! { - HasField< #field_symbol, Value: AsRef< [ #provider_type ] > + 'static > + HasField< #field_symbol, Value: AsRef< [ #field_type ] > + 'static > } } else { quote! { - HasField< #field_symbol, Value = #provider_type > + HasField< #field_symbol, Value = #field_type > } } } else { quote! { - HasFieldMut< #field_symbol, Value = #provider_type > + HasFieldMut< #field_symbol, Value = #field_type > } }; diff --git a/crates/cgp-macro-lib/src/derive_getter/parse.rs b/crates/cgp-macro-lib/src/derive_getter/parse.rs index d19a8e2b..e6b82f2b 100644 --- a/crates/cgp-macro-lib/src/derive_getter/parse.rs +++ b/crates/cgp-macro-lib/src/derive_getter/parse.rs @@ -16,8 +16,9 @@ use crate::replace_self::replace_self_type; pub fn parse_getter_fields( context_type: &Ident, consumer_trait: &ItemTrait, -) -> syn::Result> { +) -> syn::Result<(Vec, Option)> { let mut fields = Vec::new(); + let mut field_type = None; for item in consumer_trait.items.iter() { match item { @@ -26,6 +27,23 @@ pub fn parse_getter_fields( fields.push(getter_spec); } + TraitItem::Type(item_type) => { + if field_type.is_some() { + return Err(Error::new( + item_type.span(), + "at most one associated type is allowed in getter trait", + )); + } + + if item_type.generics.params.len() > 0 { + return Err(Error::new( + item_type.generics.params.span(), + "associated type in getter trait must not contain generic params", + )); + } + + field_type = Some(item_type.ident.clone()); + } _ => { return Err(Error::new( item.span(), @@ -35,7 +53,25 @@ pub fn parse_getter_fields( } } - Ok(fields) + match (&field_type, fields.first(), fields.len()) { + (None, _, _) => {} + (Some(field_type), Some(field), 1) => { + if field.field_type != parse_quote! { Self :: #field_type } { + return Err(Error::new( + field.field_type.span(), + "getter method return type must match the associated type", + )); + } + } + _ => { + return Err(Error::new( + consumer_trait.span(), + "if associated type is defined, exactly one getter method must be defined", + )); + } + } + + Ok((fields, field_type)) } fn parse_getter_method(context_type: &Ident, method: &TraitItemFn) -> syn::Result { diff --git a/crates/cgp-macro-lib/src/derive_getter/use_field.rs b/crates/cgp-macro-lib/src/derive_getter/use_field.rs index 740a777c..5bb00280 100644 --- a/crates/cgp-macro-lib/src/derive_getter/use_field.rs +++ b/crates/cgp-macro-lib/src/derive_getter/use_field.rs @@ -28,7 +28,7 @@ pub fn derive_use_field_impl( let method = derive_getter_method(&ContextArg::Ident(receiver_type.clone()), field, None, None); - let constraint = derive_getter_constraint(field, quote! { #tag_type })?; + let constraint = derive_getter_constraint(field, quote! { #tag_type }, &None)?; field_constraints.push(constraint); diff --git a/crates/cgp-macro-lib/src/derive_getter/use_fields.rs b/crates/cgp-macro-lib/src/derive_getter/use_fields.rs index 6a40cbd6..0052374c 100644 --- a/crates/cgp-macro-lib/src/derive_getter/use_fields.rs +++ b/crates/cgp-macro-lib/src/derive_getter/use_fields.rs @@ -42,7 +42,7 @@ pub fn derive_use_fields_impl( methods.extend(method); - let constraint = derive_getter_constraint(field, quote! { #field_symbol })?; + let constraint = derive_getter_constraint(field, quote! { #field_symbol }, &None)?; where_clause .predicates diff --git a/crates/cgp-macro-lib/src/entrypoints/cgp_auto_getter.rs b/crates/cgp-macro-lib/src/entrypoints/cgp_auto_getter.rs index c6100f0e..ba6a72c7 100644 --- a/crates/cgp-macro-lib/src/entrypoints/cgp_auto_getter.rs +++ b/crates/cgp-macro-lib/src/entrypoints/cgp_auto_getter.rs @@ -16,9 +16,9 @@ pub fn cgp_auto_getter(attr: TokenStream, body: TokenStream) -> syn::Result syn::Result Date: Sat, 31 Jan 2026 21:26:35 +0100 Subject: [PATCH 02/11] Move getter tests --- crates/cgp-tests/src/tests/mod.rs | 1 - crates/cgp-tests/tests/getter.rs | 1 + .../{src/tests/getter => tests/getter_tests}/abstract_type.rs | 0 .../{src/tests/getter => tests/getter_tests}/auto_generics.rs | 0 .../cgp-tests/{src/tests/getter => tests/getter_tests}/clone.rs | 0 crates/cgp-tests/{src/tests/getter => tests/getter_tests}/mod.rs | 0 .../cgp-tests/{src/tests/getter => tests/getter_tests}/mref.rs | 0 .../{src/tests/getter => tests/getter_tests}/non_self.rs | 0 .../{src/tests/getter => tests/getter_tests}/non_self_auto.rs | 0 .../cgp-tests/{src/tests/getter => tests/getter_tests}/option.rs | 0 .../cgp-tests/{src/tests/getter => tests/getter_tests}/slice.rs | 0 .../cgp-tests/{src/tests/getter => tests/getter_tests}/string.rs | 0 12 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 crates/cgp-tests/tests/getter.rs rename crates/cgp-tests/{src/tests/getter => tests/getter_tests}/abstract_type.rs (100%) rename crates/cgp-tests/{src/tests/getter => tests/getter_tests}/auto_generics.rs (100%) rename crates/cgp-tests/{src/tests/getter => tests/getter_tests}/clone.rs (100%) rename crates/cgp-tests/{src/tests/getter => tests/getter_tests}/mod.rs (100%) rename crates/cgp-tests/{src/tests/getter => tests/getter_tests}/mref.rs (100%) rename crates/cgp-tests/{src/tests/getter => tests/getter_tests}/non_self.rs (100%) rename crates/cgp-tests/{src/tests/getter => tests/getter_tests}/non_self_auto.rs (100%) rename crates/cgp-tests/{src/tests/getter => tests/getter_tests}/option.rs (100%) rename crates/cgp-tests/{src/tests/getter => tests/getter_tests}/slice.rs (100%) rename crates/cgp-tests/{src/tests/getter => tests/getter_tests}/string.rs (100%) diff --git a/crates/cgp-tests/src/tests/mod.rs b/crates/cgp-tests/src/tests/mod.rs index 86fdc838..cfb6745f 100644 --- a/crates/cgp-tests/src/tests/mod.rs +++ b/crates/cgp-tests/src/tests/mod.rs @@ -3,7 +3,6 @@ pub mod blanket_trait; pub mod check_components; pub mod compose; pub mod delegate_and_check_components; -pub mod getter; pub mod has_field; pub mod has_fields; pub mod monad; diff --git a/crates/cgp-tests/tests/getter.rs b/crates/cgp-tests/tests/getter.rs new file mode 100644 index 00000000..359a2dc2 --- /dev/null +++ b/crates/cgp-tests/tests/getter.rs @@ -0,0 +1 @@ +pub mod getter_tests; diff --git a/crates/cgp-tests/src/tests/getter/abstract_type.rs b/crates/cgp-tests/tests/getter_tests/abstract_type.rs similarity index 100% rename from crates/cgp-tests/src/tests/getter/abstract_type.rs rename to crates/cgp-tests/tests/getter_tests/abstract_type.rs diff --git a/crates/cgp-tests/src/tests/getter/auto_generics.rs b/crates/cgp-tests/tests/getter_tests/auto_generics.rs similarity index 100% rename from crates/cgp-tests/src/tests/getter/auto_generics.rs rename to crates/cgp-tests/tests/getter_tests/auto_generics.rs diff --git a/crates/cgp-tests/src/tests/getter/clone.rs b/crates/cgp-tests/tests/getter_tests/clone.rs similarity index 100% rename from crates/cgp-tests/src/tests/getter/clone.rs rename to crates/cgp-tests/tests/getter_tests/clone.rs diff --git a/crates/cgp-tests/src/tests/getter/mod.rs b/crates/cgp-tests/tests/getter_tests/mod.rs similarity index 100% rename from crates/cgp-tests/src/tests/getter/mod.rs rename to crates/cgp-tests/tests/getter_tests/mod.rs diff --git a/crates/cgp-tests/src/tests/getter/mref.rs b/crates/cgp-tests/tests/getter_tests/mref.rs similarity index 100% rename from crates/cgp-tests/src/tests/getter/mref.rs rename to crates/cgp-tests/tests/getter_tests/mref.rs diff --git a/crates/cgp-tests/src/tests/getter/non_self.rs b/crates/cgp-tests/tests/getter_tests/non_self.rs similarity index 100% rename from crates/cgp-tests/src/tests/getter/non_self.rs rename to crates/cgp-tests/tests/getter_tests/non_self.rs diff --git a/crates/cgp-tests/src/tests/getter/non_self_auto.rs b/crates/cgp-tests/tests/getter_tests/non_self_auto.rs similarity index 100% rename from crates/cgp-tests/src/tests/getter/non_self_auto.rs rename to crates/cgp-tests/tests/getter_tests/non_self_auto.rs diff --git a/crates/cgp-tests/src/tests/getter/option.rs b/crates/cgp-tests/tests/getter_tests/option.rs similarity index 100% rename from crates/cgp-tests/src/tests/getter/option.rs rename to crates/cgp-tests/tests/getter_tests/option.rs diff --git a/crates/cgp-tests/src/tests/getter/slice.rs b/crates/cgp-tests/tests/getter_tests/slice.rs similarity index 100% rename from crates/cgp-tests/src/tests/getter/slice.rs rename to crates/cgp-tests/tests/getter_tests/slice.rs diff --git a/crates/cgp-tests/src/tests/getter/string.rs b/crates/cgp-tests/tests/getter_tests/string.rs similarity index 100% rename from crates/cgp-tests/src/tests/getter/string.rs rename to crates/cgp-tests/tests/getter_tests/string.rs From 733dcc59a3ab9c1a5e7189f2802808f4b2774ea0 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sat, 31 Jan 2026 21:46:08 +0100 Subject: [PATCH 03/11] Auto getter with assoc type is working --- .../cgp-macro-lib/src/derive_getter/parse.rs | 41 +++++++++++++------ .../tests/getter_tests/assoc_type.rs | 8 ++++ crates/cgp-tests/tests/getter_tests/mod.rs | 1 + 3 files changed, 37 insertions(+), 13 deletions(-) create mode 100644 crates/cgp-tests/tests/getter_tests/assoc_type.rs diff --git a/crates/cgp-macro-lib/src/derive_getter/parse.rs b/crates/cgp-macro-lib/src/derive_getter/parse.rs index e6b82f2b..b86341df 100644 --- a/crates/cgp-macro-lib/src/derive_getter/parse.rs +++ b/crates/cgp-macro-lib/src/derive_getter/parse.rs @@ -18,17 +18,17 @@ pub fn parse_getter_fields( consumer_trait: &ItemTrait, ) -> syn::Result<(Vec, Option)> { let mut fields = Vec::new(); - let mut field_type = None; + let mut field_assoc_type = None; for item in consumer_trait.items.iter() { match item { TraitItem::Fn(method) => { - let getter_spec = parse_getter_method(context_type, method)?; + let getter_spec = parse_getter_method(context_type, method, &field_assoc_type)?; fields.push(getter_spec); } TraitItem::Type(item_type) => { - if field_type.is_some() { + if field_assoc_type.is_some() { return Err(Error::new( item_type.span(), "at most one associated type is allowed in getter trait", @@ -42,7 +42,7 @@ pub fn parse_getter_fields( )); } - field_type = Some(item_type.ident.clone()); + field_assoc_type = Some(item_type.ident.clone()); } _ => { return Err(Error::new( @@ -53,13 +53,20 @@ pub fn parse_getter_fields( } } - match (&field_type, fields.first(), fields.len()) { + match (&field_assoc_type, fields.first(), fields.len()) { (None, _, _) => {} - (Some(field_type), Some(field), 1) => { - if field.field_type != parse_quote! { Self :: #field_type } { + (Some(field_assoc_type), Some(field), 1) => { + let field_type = &field.field_type; + + if field_type != &parse_quote! { Self :: #field_assoc_type } + && field_type != &parse_quote! { #context_type :: #field_assoc_type } + { return Err(Error::new( field.field_type.span(), - "getter method return type must match the associated type", + format!( + "getter method return type must match the associated type. {}", + field_type.to_token_stream() + ), )); } } @@ -71,10 +78,14 @@ pub fn parse_getter_fields( } } - Ok((fields, field_type)) + Ok((fields, field_assoc_type)) } -fn parse_getter_method(context_type: &Ident, method: &TraitItemFn) -> syn::Result { +fn parse_getter_method( + context_type: &Ident, + method: &TraitItemFn, + field_assoc_type: &Option, +) -> syn::Result { let signature = &method.sig; validate_getter_method_signature(signature)?; @@ -85,7 +96,7 @@ fn parse_getter_method(context_type: &Ident, method: &TraitItemFn) -> syn::Resul let (receiver_mode, field_mut) = parse_receiver(context_type, arg)?; - let return_type = parse_return_type(context_type, &signature.output)?; + let return_type = parse_return_type(context_type, &signature.output, field_assoc_type)?; let (field_type, field_mode) = parse_field_type(&return_type, &field_mut)?; @@ -223,12 +234,16 @@ fn parse_receiver(context_ident: &Ident, arg: &FnArg) -> syn::Result<(ReceiverMo } } -fn parse_return_type(context_type: &Ident, return_type: &ReturnType) -> syn::Result { +fn parse_return_type( + context_type: &Ident, + return_type: &ReturnType, + field_assoc_type: &Option, +) -> syn::Result { match return_type { ReturnType::Type(_, ty) => parse2(replace_self_type( ty.to_token_stream(), context_type.to_token_stream(), - &Vec::new(), + &field_assoc_type.iter().cloned().collect::>(), )), _ => Err(Error::new( return_type.span(), diff --git a/crates/cgp-tests/tests/getter_tests/assoc_type.rs b/crates/cgp-tests/tests/getter_tests/assoc_type.rs new file mode 100644 index 00000000..0e98def4 --- /dev/null +++ b/crates/cgp-tests/tests/getter_tests/assoc_type.rs @@ -0,0 +1,8 @@ +use cgp::prelude::*; + +#[cgp_auto_getter] +pub trait HasName { + type Name; + + fn name(&self) -> &Self::Name; +} diff --git a/crates/cgp-tests/tests/getter_tests/mod.rs b/crates/cgp-tests/tests/getter_tests/mod.rs index 2d113d64..30fc034d 100644 --- a/crates/cgp-tests/tests/getter_tests/mod.rs +++ b/crates/cgp-tests/tests/getter_tests/mod.rs @@ -1,4 +1,5 @@ pub mod abstract_type; +pub mod assoc_type; pub mod auto_generics; pub mod clone; pub mod mref; From 73372cb8fff0a7e73e03b8eb0210547c0c12703c Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 1 Feb 2026 17:36:59 +0100 Subject: [PATCH 04/11] Support assoc type in `Allow abstract types to be defined in `#[cgp_getter]` --- .../src/derive_getter/use_field.rs | 23 +++++++++++--- .../src/derive_getter/use_fields.rs | 25 +++++++++++---- .../src/derive_getter/with_provider.rs | 31 ++++++++++++++----- .../src/entrypoints/cgp_getter.rs | 28 ++++++++++++----- .../auto_getter.rs} | 0 .../tests/getter_tests/assoc_type/getter.rs | 8 +++++ .../tests/getter_tests/assoc_type/mod.rs | 2 ++ 7 files changed, 91 insertions(+), 26 deletions(-) rename crates/cgp-tests/tests/getter_tests/{assoc_type.rs => assoc_type/auto_getter.rs} (100%) create mode 100644 crates/cgp-tests/tests/getter_tests/assoc_type/getter.rs create mode 100644 crates/cgp-tests/tests/getter_tests/assoc_type/mod.rs diff --git a/crates/cgp-macro-lib/src/derive_getter/use_field.rs b/crates/cgp-macro-lib/src/derive_getter/use_field.rs index 5bb00280..691cdf19 100644 --- a/crates/cgp-macro-lib/src/derive_getter/use_field.rs +++ b/crates/cgp-macro-lib/src/derive_getter/use_field.rs @@ -1,7 +1,7 @@ use quote::{ToTokens, quote}; use syn::punctuated::Punctuated; use syn::token::Plus; -use syn::{Generics, ItemImpl, ItemTrait, TypeParamBound, parse2}; +use syn::{Generics, Ident, ItemImpl, ItemTrait, TypeParamBound, parse2}; use crate::derive_getter::getter_field::GetterField; use crate::derive_getter::{ @@ -13,6 +13,7 @@ pub fn derive_use_field_impl( spec: &ComponentSpec, provider_trait: &ItemTrait, field: &GetterField, + field_assoc_type: &Option, ) -> syn::Result { let context_type = &spec.context_type; let provider_name = &provider_trait.ident; @@ -26,20 +27,32 @@ pub fn derive_use_field_impl( let tag_type = quote! { __Tag__ }; - let method = derive_getter_method(&ContextArg::Ident(receiver_type.clone()), field, None, None); + let mut items = + derive_getter_method(&ContextArg::Ident(receiver_type.clone()), field, None, None); - let constraint = derive_getter_constraint(field, quote! { #tag_type }, &None)?; + let constraint = derive_getter_constraint(field, quote! { #tag_type }, field_assoc_type)?; field_constraints.push(constraint); let mut provider_generics = provider_trait.generics.clone(); + if let Some(field_assoc_type) = field_assoc_type { + provider_generics + .params + .push(parse2(field_assoc_type.to_token_stream())?); + + items.extend(quote! { + type #field_assoc_type = #field_assoc_type; + }); + } + let mut where_clause = provider_generics.make_where_clause().clone(); where_clause .predicates .push(parse2(quote! { #receiver_type: #field_constraints })?); - let (impl_generics, type_generics, _) = provider_generics.split_for_impl(); + let (_, type_generics, _) = provider_trait.generics.split_for_impl(); + let (impl_generics, _, _) = provider_generics.split_for_impl(); let impl_generics = { let mut generics: Generics = parse2(impl_generics.to_token_stream())?; @@ -51,7 +64,7 @@ pub fn derive_use_field_impl( impl #impl_generics #provider_name #type_generics for UseField< #tag_type > #where_clause { - #method + #items } })?; diff --git a/crates/cgp-macro-lib/src/derive_getter/use_fields.rs b/crates/cgp-macro-lib/src/derive_getter/use_fields.rs index 0052374c..57682de3 100644 --- a/crates/cgp-macro-lib/src/derive_getter/use_fields.rs +++ b/crates/cgp-macro-lib/src/derive_getter/use_fields.rs @@ -2,7 +2,7 @@ use alloc::string::ToString; use proc_macro2::TokenStream; use quote::{ToTokens, quote}; -use syn::{ItemImpl, ItemTrait, parse2}; +use syn::{Ident, ItemImpl, ItemTrait, parse2}; use crate::derive_getter::getter_field::GetterField; use crate::derive_getter::{ @@ -15,16 +15,27 @@ pub fn derive_use_fields_impl( spec: &ComponentSpec, provider_trait: &ItemTrait, fields: &[GetterField], + field_assoc_type: &Option, ) -> syn::Result { let context_type = &spec.context_type; let provider_name = &spec.provider_name; - let mut methods: TokenStream = TokenStream::new(); + let mut items: TokenStream = TokenStream::new(); let mut provider_generics = provider_trait.generics.clone(); let mut where_clause = provider_generics.make_where_clause().clone(); + if let Some(field_assoc_type) = field_assoc_type { + provider_generics + .params + .push(parse2(field_assoc_type.to_token_stream())?); + + items.extend(quote! { + type #field_assoc_type = #field_assoc_type; + }); + } + for field in fields { let receiver_type = match &field.receiver_mode { ReceiverMode::SelfReceiver => context_type.to_token_stream(), @@ -40,22 +51,24 @@ pub fn derive_use_fields_impl( None, ); - methods.extend(method); + items.extend(method); - let constraint = derive_getter_constraint(field, quote! { #field_symbol }, &None)?; + let constraint = + derive_getter_constraint(field, quote! { #field_symbol }, field_assoc_type)?; where_clause .predicates .push(parse2(quote! { #receiver_type: #constraint })?); } - let (impl_generics, type_generics, _) = provider_generics.split_for_impl(); + let (_, type_generics, _) = provider_trait.generics.split_for_impl(); + let (impl_generics, _, _) = provider_generics.split_for_impl(); let out = parse2(quote! { impl #impl_generics #provider_name #type_generics for UseFields #where_clause { - #methods + #items } })?; diff --git a/crates/cgp-macro-lib/src/derive_getter/with_provider.rs b/crates/cgp-macro-lib/src/derive_getter/with_provider.rs index 6805fb45..1efe567f 100644 --- a/crates/cgp-macro-lib/src/derive_getter/with_provider.rs +++ b/crates/cgp-macro-lib/src/derive_getter/with_provider.rs @@ -1,6 +1,6 @@ use proc_macro2::Span; use quote::{ToTokens, quote}; -use syn::{Generics, Ident, ItemImpl, ItemTrait, parse2}; +use syn::{Generics, Ident, ItemImpl, ItemTrait, parse_quote, parse2}; use crate::derive_getter::getter_field::GetterField; use crate::derive_getter::{ContextArg, FieldMode, ReceiverMode, derive_getter_method}; @@ -10,6 +10,7 @@ pub fn derive_with_provider_impl( spec: &ComponentSpec, provider_trait: &ItemTrait, field: &GetterField, + field_assoc_type: &Option, ) -> syn::Result { let component_name = &spec.component_name; let component_params = &spec.component_params; @@ -22,7 +23,10 @@ pub fn derive_with_provider_impl( ReceiverMode::Type(ty) => ty.to_token_stream(), }; - let provider_type = &field.field_type; + let field_type = match field_assoc_type { + Some(field_assoc_type) => parse_quote! { #field_assoc_type }, + None => field.field_type.clone(), + }; let provider_ident = Ident::new("__Provider__", Span::call_site()); @@ -31,20 +35,20 @@ pub fn derive_with_provider_impl( let provider_constraint = if field.field_mut.is_none() { if let FieldMode::Slice = field.field_mode { quote! { - FieldGetter< #receiver_type, #component_type, Value: AsRef< [ #provider_type ] > + 'static > + FieldGetter< #receiver_type, #component_type, Value: AsRef< [ #field_type ] > + 'static > } } else { quote! { - FieldGetter< #receiver_type, #component_type , Value = #provider_type > + FieldGetter< #receiver_type, #component_type , Value = #field_type > } } } else { quote! { - MutFieldGetter< #receiver_type, #component_type, Value = #provider_type > + MutFieldGetter< #receiver_type, #component_type, Value = #field_type > } }; - let method = derive_getter_method( + let mut items = derive_getter_method( &ContextArg::Ident(receiver_type), field, None, @@ -53,12 +57,23 @@ pub fn derive_with_provider_impl( let mut provider_generics = provider_trait.generics.clone(); + if let Some(field_assoc_type) = field_assoc_type { + provider_generics + .params + .push(parse2(field_assoc_type.to_token_stream())?); + + items.extend(quote! { + type #field_assoc_type = #field_assoc_type; + }); + } + let mut where_clause = provider_generics.make_where_clause().clone(); where_clause .predicates .push(parse2(quote! { #provider_ident : #provider_constraint })?); - let (impl_generics, type_generics, _) = provider_generics.split_for_impl(); + let (_, type_generics, _) = provider_trait.generics.split_for_impl(); + let (impl_generics, _, _) = provider_generics.split_for_impl(); let impl_generics = { let mut generics: Generics = parse2(impl_generics.to_token_stream())?; @@ -70,7 +85,7 @@ pub fn derive_with_provider_impl( impl #impl_generics #provider_name #type_generics for WithProvider< #provider_ident > #where_clause { - #method + #items } })?; diff --git a/crates/cgp-macro-lib/src/entrypoints/cgp_getter.rs b/crates/cgp-macro-lib/src/entrypoints/cgp_getter.rs index fb9ca719..3d20f1f5 100644 --- a/crates/cgp-macro-lib/src/entrypoints/cgp_getter.rs +++ b/crates/cgp-macro-lib/src/entrypoints/cgp_getter.rs @@ -39,10 +39,14 @@ pub fn cgp_getter(attr: TokenStream, body: TokenStream) -> syn::Result syn::Result &Self::Name; +} diff --git a/crates/cgp-tests/tests/getter_tests/assoc_type/mod.rs b/crates/cgp-tests/tests/getter_tests/assoc_type/mod.rs new file mode 100644 index 00000000..98e2c237 --- /dev/null +++ b/crates/cgp-tests/tests/getter_tests/assoc_type/mod.rs @@ -0,0 +1,2 @@ +pub mod auto_getter; +pub mod getter; From f866bce477575c4dd25d3491a19d6eb7c7fa3b3d Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 1 Feb 2026 17:40:35 +0100 Subject: [PATCH 05/11] Rearrange generated assoc type definition --- .../src/derive_getter/use_field.rs | 19 +++++++---- .../src/derive_getter/with_provider.rs | 32 ++++++++++--------- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/crates/cgp-macro-lib/src/derive_getter/use_field.rs b/crates/cgp-macro-lib/src/derive_getter/use_field.rs index 691cdf19..f2846f2b 100644 --- a/crates/cgp-macro-lib/src/derive_getter/use_field.rs +++ b/crates/cgp-macro-lib/src/derive_getter/use_field.rs @@ -1,3 +1,4 @@ +use proc_macro2::TokenStream; use quote::{ToTokens, quote}; use syn::punctuated::Punctuated; use syn::token::Plus; @@ -27,12 +28,7 @@ pub fn derive_use_field_impl( let tag_type = quote! { __Tag__ }; - let mut items = - derive_getter_method(&ContextArg::Ident(receiver_type.clone()), field, None, None); - - let constraint = derive_getter_constraint(field, quote! { #tag_type }, field_assoc_type)?; - - field_constraints.push(constraint); + let mut items = TokenStream::new(); let mut provider_generics = provider_trait.generics.clone(); @@ -46,6 +42,17 @@ pub fn derive_use_field_impl( }); } + items.extend(derive_getter_method( + &ContextArg::Ident(receiver_type.clone()), + field, + None, + None, + )); + + let constraint = derive_getter_constraint(field, quote! { #tag_type }, field_assoc_type)?; + + field_constraints.push(constraint); + let mut where_clause = provider_generics.make_where_clause().clone(); where_clause .predicates diff --git a/crates/cgp-macro-lib/src/derive_getter/with_provider.rs b/crates/cgp-macro-lib/src/derive_getter/with_provider.rs index 1efe567f..275603b8 100644 --- a/crates/cgp-macro-lib/src/derive_getter/with_provider.rs +++ b/crates/cgp-macro-lib/src/derive_getter/with_provider.rs @@ -1,4 +1,4 @@ -use proc_macro2::Span; +use proc_macro2::{Span, TokenStream}; use quote::{ToTokens, quote}; use syn::{Generics, Ident, ItemImpl, ItemTrait, parse_quote, parse2}; @@ -32,6 +32,20 @@ pub fn derive_with_provider_impl( let component_type = quote! { #component_name < #component_params > }; + let mut items = TokenStream::new(); + + let mut provider_generics = provider_trait.generics.clone(); + + if let Some(field_assoc_type) = field_assoc_type { + provider_generics + .params + .push(parse2(field_assoc_type.to_token_stream())?); + + items.extend(quote! { + type #field_assoc_type = #field_assoc_type; + }); + } + let provider_constraint = if field.field_mut.is_none() { if let FieldMode::Slice = field.field_mode { quote! { @@ -48,24 +62,12 @@ pub fn derive_with_provider_impl( } }; - let mut items = derive_getter_method( + items.extend(derive_getter_method( &ContextArg::Ident(receiver_type), field, None, Some(provider_ident.clone()), - ); - - let mut provider_generics = provider_trait.generics.clone(); - - if let Some(field_assoc_type) = field_assoc_type { - provider_generics - .params - .push(parse2(field_assoc_type.to_token_stream())?); - - items.extend(quote! { - type #field_assoc_type = #field_assoc_type; - }); - } + )); let mut where_clause = provider_generics.make_where_clause().clone(); where_clause From 0efd65af418551963a1a0b341fe48f5a8e9f5cff Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 1 Feb 2026 17:54:14 +0100 Subject: [PATCH 06/11] Forward whole TraitItemType --- .../src/derive_getter/blanket.rs | 16 ++++++++++------ .../cgp-macro-lib/src/derive_getter/parse.rs | 19 ++++++++++++------- .../src/derive_getter/use_field.rs | 16 +++++++++++----- .../src/derive_getter/use_fields.rs | 17 +++++++++++------ .../src/derive_getter/with_provider.rs | 15 ++++++++++----- 5 files changed, 54 insertions(+), 29 deletions(-) diff --git a/crates/cgp-macro-lib/src/derive_getter/blanket.rs b/crates/cgp-macro-lib/src/derive_getter/blanket.rs index e887746b..b3d74528 100644 --- a/crates/cgp-macro-lib/src/derive_getter/blanket.rs +++ b/crates/cgp-macro-lib/src/derive_getter/blanket.rs @@ -2,7 +2,7 @@ use alloc::string::ToString; use proc_macro2::TokenStream; use quote::{ToTokens, quote}; -use syn::{Ident, ItemImpl, ItemTrait, parse2}; +use syn::{Ident, ItemImpl, ItemTrait, TraitItemType, parse2}; use crate::derive_getter::getter_field::GetterField; use crate::derive_getter::{ @@ -14,7 +14,7 @@ pub fn derive_blanket_impl( context_type: &Ident, consumer_trait: &ItemTrait, fields: &[GetterField], - field_assoc_type: &Option, + field_assoc_type: &Option, ) -> syn::Result { let consumer_name = &consumer_trait.ident; @@ -29,12 +29,13 @@ pub fn derive_blanket_impl( .insert(0, parse2(context_type.to_token_stream())?); if let Some(field_assoc_type) = field_assoc_type { + let field_assoc_type_ident = &field_assoc_type.ident; generics .params - .push(parse2(field_assoc_type.to_token_stream())?); + .push(parse2(field_assoc_type_ident.to_token_stream())?); items.extend(quote! { - type #field_assoc_type = #field_assoc_type; + type #field_assoc_type_ident = #field_assoc_type_ident; }); } @@ -66,8 +67,11 @@ pub fn derive_blanket_impl( items.extend(method); - let constraint = - derive_getter_constraint(field, quote! { #field_symbol }, field_assoc_type)?; + let constraint = derive_getter_constraint( + field, + quote! { #field_symbol }, + &field_assoc_type.as_ref().map(|item| item.ident.clone()), + )?; where_clause.predicates.push(parse2(quote! { #receiver_type: #constraint diff --git a/crates/cgp-macro-lib/src/derive_getter/parse.rs b/crates/cgp-macro-lib/src/derive_getter/parse.rs index b86341df..e05f84fc 100644 --- a/crates/cgp-macro-lib/src/derive_getter/parse.rs +++ b/crates/cgp-macro-lib/src/derive_getter/parse.rs @@ -6,7 +6,7 @@ use syn::spanned::Spanned; use syn::token::{Comma, Mut}; use syn::{ Error, FnArg, GenericArgument, Ident, ItemTrait, PathArguments, PathSegment, ReturnType, - Signature, TraitItem, TraitItemFn, Type, TypePath, parse_quote, parse2, + Signature, TraitItem, TraitItemFn, TraitItemType, Type, TypePath, parse_quote, parse2, }; use crate::derive_getter::getter_field::GetterField; @@ -16,14 +16,18 @@ use crate::replace_self::replace_self_type; pub fn parse_getter_fields( context_type: &Ident, consumer_trait: &ItemTrait, -) -> syn::Result<(Vec, Option)> { +) -> syn::Result<(Vec, Option)> { let mut fields = Vec::new(); - let mut field_assoc_type = None; + let mut field_assoc_type: Option = None; for item in consumer_trait.items.iter() { match item { TraitItem::Fn(method) => { - let getter_spec = parse_getter_method(context_type, method, &field_assoc_type)?; + let getter_spec = parse_getter_method( + context_type, + method, + &field_assoc_type.as_ref().map(|item| item.ident.clone()), + )?; fields.push(getter_spec); } @@ -42,7 +46,7 @@ pub fn parse_getter_fields( )); } - field_assoc_type = Some(item_type.ident.clone()); + field_assoc_type = Some(item_type.clone()); } _ => { return Err(Error::new( @@ -56,10 +60,11 @@ pub fn parse_getter_fields( match (&field_assoc_type, fields.first(), fields.len()) { (None, _, _) => {} (Some(field_assoc_type), Some(field), 1) => { + let field_assoc_type_ident = &field_assoc_type.ident; let field_type = &field.field_type; - if field_type != &parse_quote! { Self :: #field_assoc_type } - && field_type != &parse_quote! { #context_type :: #field_assoc_type } + if field_type != &parse_quote! { Self :: #field_assoc_type_ident } + && field_type != &parse_quote! { #context_type :: #field_assoc_type_ident } { return Err(Error::new( field.field_type.span(), diff --git a/crates/cgp-macro-lib/src/derive_getter/use_field.rs b/crates/cgp-macro-lib/src/derive_getter/use_field.rs index f2846f2b..55124412 100644 --- a/crates/cgp-macro-lib/src/derive_getter/use_field.rs +++ b/crates/cgp-macro-lib/src/derive_getter/use_field.rs @@ -2,7 +2,7 @@ use proc_macro2::TokenStream; use quote::{ToTokens, quote}; use syn::punctuated::Punctuated; use syn::token::Plus; -use syn::{Generics, Ident, ItemImpl, ItemTrait, TypeParamBound, parse2}; +use syn::{Generics, ItemImpl, ItemTrait, TraitItemType, TypeParamBound, parse2}; use crate::derive_getter::getter_field::GetterField; use crate::derive_getter::{ @@ -14,7 +14,7 @@ pub fn derive_use_field_impl( spec: &ComponentSpec, provider_trait: &ItemTrait, field: &GetterField, - field_assoc_type: &Option, + field_assoc_type: &Option, ) -> syn::Result { let context_type = &spec.context_type; let provider_name = &provider_trait.ident; @@ -33,12 +33,14 @@ pub fn derive_use_field_impl( let mut provider_generics = provider_trait.generics.clone(); if let Some(field_assoc_type) = field_assoc_type { + let field_assoc_type_ident = &field_assoc_type.ident; + provider_generics .params - .push(parse2(field_assoc_type.to_token_stream())?); + .push(parse2(field_assoc_type_ident.to_token_stream())?); items.extend(quote! { - type #field_assoc_type = #field_assoc_type; + type #field_assoc_type_ident = #field_assoc_type_ident; }); } @@ -49,7 +51,11 @@ pub fn derive_use_field_impl( None, )); - let constraint = derive_getter_constraint(field, quote! { #tag_type }, field_assoc_type)?; + let constraint = derive_getter_constraint( + field, + quote! { #tag_type }, + &field_assoc_type.as_ref().map(|item| item.ident.clone()), + )?; field_constraints.push(constraint); diff --git a/crates/cgp-macro-lib/src/derive_getter/use_fields.rs b/crates/cgp-macro-lib/src/derive_getter/use_fields.rs index 57682de3..4aa64185 100644 --- a/crates/cgp-macro-lib/src/derive_getter/use_fields.rs +++ b/crates/cgp-macro-lib/src/derive_getter/use_fields.rs @@ -2,7 +2,7 @@ use alloc::string::ToString; use proc_macro2::TokenStream; use quote::{ToTokens, quote}; -use syn::{Ident, ItemImpl, ItemTrait, parse2}; +use syn::{ItemImpl, ItemTrait, TraitItemType, parse2}; use crate::derive_getter::getter_field::GetterField; use crate::derive_getter::{ @@ -15,7 +15,7 @@ pub fn derive_use_fields_impl( spec: &ComponentSpec, provider_trait: &ItemTrait, fields: &[GetterField], - field_assoc_type: &Option, + field_assoc_type: &Option, ) -> syn::Result { let context_type = &spec.context_type; @@ -27,12 +27,14 @@ pub fn derive_use_fields_impl( let mut where_clause = provider_generics.make_where_clause().clone(); if let Some(field_assoc_type) = field_assoc_type { + let field_assoc_type_ident = &field_assoc_type.ident; + provider_generics .params - .push(parse2(field_assoc_type.to_token_stream())?); + .push(parse2(field_assoc_type_ident.to_token_stream())?); items.extend(quote! { - type #field_assoc_type = #field_assoc_type; + type #field_assoc_type_ident = #field_assoc_type_ident; }); } @@ -53,8 +55,11 @@ pub fn derive_use_fields_impl( items.extend(method); - let constraint = - derive_getter_constraint(field, quote! { #field_symbol }, field_assoc_type)?; + let constraint = derive_getter_constraint( + field, + quote! { #field_symbol }, + &field_assoc_type.as_ref().map(|item| item.ident.clone()), + )?; where_clause .predicates diff --git a/crates/cgp-macro-lib/src/derive_getter/with_provider.rs b/crates/cgp-macro-lib/src/derive_getter/with_provider.rs index 275603b8..f5f06f1b 100644 --- a/crates/cgp-macro-lib/src/derive_getter/with_provider.rs +++ b/crates/cgp-macro-lib/src/derive_getter/with_provider.rs @@ -1,6 +1,6 @@ use proc_macro2::{Span, TokenStream}; use quote::{ToTokens, quote}; -use syn::{Generics, Ident, ItemImpl, ItemTrait, parse_quote, parse2}; +use syn::{Generics, Ident, ItemImpl, ItemTrait, TraitItemType, parse_quote, parse2}; use crate::derive_getter::getter_field::GetterField; use crate::derive_getter::{ContextArg, FieldMode, ReceiverMode, derive_getter_method}; @@ -10,7 +10,7 @@ pub fn derive_with_provider_impl( spec: &ComponentSpec, provider_trait: &ItemTrait, field: &GetterField, - field_assoc_type: &Option, + field_assoc_type: &Option, ) -> syn::Result { let component_name = &spec.component_name; let component_params = &spec.component_params; @@ -24,7 +24,10 @@ pub fn derive_with_provider_impl( }; let field_type = match field_assoc_type { - Some(field_assoc_type) => parse_quote! { #field_assoc_type }, + Some(field_assoc_type) => { + let field_assoc_type_ident = &field_assoc_type.ident; + parse_quote! { #field_assoc_type_ident } + } None => field.field_type.clone(), }; @@ -37,12 +40,14 @@ pub fn derive_with_provider_impl( let mut provider_generics = provider_trait.generics.clone(); if let Some(field_assoc_type) = field_assoc_type { + let field_assoc_type_ident = &field_assoc_type.ident; + provider_generics .params - .push(parse2(field_assoc_type.to_token_stream())?); + .push(parse2(field_assoc_type_ident.to_token_stream())?); items.extend(quote! { - type #field_assoc_type = #field_assoc_type; + type #field_assoc_type_ident = #field_assoc_type_ident; }); } From 6836df28061c7e859fa969a1ff29c8e6098dd2fb Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 1 Feb 2026 18:01:07 +0100 Subject: [PATCH 07/11] Support associated type constraints --- crates/cgp-macro-lib/src/derive_getter/blanket.rs | 7 +++++++ .../cgp-macro-lib/src/derive_getter/use_field.rs | 9 +++++++++ .../cgp-macro-lib/src/derive_getter/use_fields.rs | 14 ++++++++++++-- .../src/derive_getter/with_provider.rs | 9 +++++++++ .../tests/getter_tests/assoc_type/auto_getter.rs | 4 +++- .../tests/getter_tests/assoc_type/getter.rs | 4 +++- 6 files changed, 43 insertions(+), 4 deletions(-) diff --git a/crates/cgp-macro-lib/src/derive_getter/blanket.rs b/crates/cgp-macro-lib/src/derive_getter/blanket.rs index b3d74528..db383649 100644 --- a/crates/cgp-macro-lib/src/derive_getter/blanket.rs +++ b/crates/cgp-macro-lib/src/derive_getter/blanket.rs @@ -30,6 +30,7 @@ pub fn derive_blanket_impl( if let Some(field_assoc_type) = field_assoc_type { let field_assoc_type_ident = &field_assoc_type.ident; + generics .params .push(parse2(field_assoc_type_ident.to_token_stream())?); @@ -37,6 +38,12 @@ pub fn derive_blanket_impl( items.extend(quote! { type #field_assoc_type_ident = #field_assoc_type_ident; }); + + let field_constraints = &field_assoc_type.bounds; + + generics.make_where_clause().predicates.push(parse2(quote! { + #field_assoc_type_ident: #field_constraints + })?); } let where_clause = generics.make_where_clause(); diff --git a/crates/cgp-macro-lib/src/derive_getter/use_field.rs b/crates/cgp-macro-lib/src/derive_getter/use_field.rs index 55124412..ac118dbf 100644 --- a/crates/cgp-macro-lib/src/derive_getter/use_field.rs +++ b/crates/cgp-macro-lib/src/derive_getter/use_field.rs @@ -42,6 +42,15 @@ pub fn derive_use_field_impl( items.extend(quote! { type #field_assoc_type_ident = #field_assoc_type_ident; }); + + let field_constraints = &field_assoc_type.bounds; + + provider_generics + .make_where_clause() + .predicates + .push(parse2(quote! { + #field_assoc_type_ident: #field_constraints + })?); } items.extend(derive_getter_method( diff --git a/crates/cgp-macro-lib/src/derive_getter/use_fields.rs b/crates/cgp-macro-lib/src/derive_getter/use_fields.rs index 4aa64185..d83bdce6 100644 --- a/crates/cgp-macro-lib/src/derive_getter/use_fields.rs +++ b/crates/cgp-macro-lib/src/derive_getter/use_fields.rs @@ -24,7 +24,6 @@ pub fn derive_use_fields_impl( let mut items: TokenStream = TokenStream::new(); let mut provider_generics = provider_trait.generics.clone(); - let mut where_clause = provider_generics.make_where_clause().clone(); if let Some(field_assoc_type) = field_assoc_type { let field_assoc_type_ident = &field_assoc_type.ident; @@ -36,8 +35,19 @@ pub fn derive_use_fields_impl( items.extend(quote! { type #field_assoc_type_ident = #field_assoc_type_ident; }); + + let field_constraints = &field_assoc_type.bounds; + + provider_generics + .make_where_clause() + .predicates + .push(parse2(quote! { + #field_assoc_type_ident: #field_constraints + })?); } + let where_clause = provider_generics.make_where_clause(); + for field in fields { let receiver_type = match &field.receiver_mode { ReceiverMode::SelfReceiver => context_type.to_token_stream(), @@ -67,7 +77,7 @@ pub fn derive_use_fields_impl( } let (_, type_generics, _) = provider_trait.generics.split_for_impl(); - let (impl_generics, _, _) = provider_generics.split_for_impl(); + let (impl_generics, _, where_clause) = provider_generics.split_for_impl(); let out = parse2(quote! { impl #impl_generics #provider_name #type_generics for UseFields diff --git a/crates/cgp-macro-lib/src/derive_getter/with_provider.rs b/crates/cgp-macro-lib/src/derive_getter/with_provider.rs index f5f06f1b..ee7c2fe5 100644 --- a/crates/cgp-macro-lib/src/derive_getter/with_provider.rs +++ b/crates/cgp-macro-lib/src/derive_getter/with_provider.rs @@ -49,6 +49,15 @@ pub fn derive_with_provider_impl( items.extend(quote! { type #field_assoc_type_ident = #field_assoc_type_ident; }); + + let field_constraints = &field_assoc_type.bounds; + + provider_generics + .make_where_clause() + .predicates + .push(parse2(quote! { + #field_assoc_type_ident: #field_constraints + })?); } let provider_constraint = if field.field_mut.is_none() { diff --git a/crates/cgp-tests/tests/getter_tests/assoc_type/auto_getter.rs b/crates/cgp-tests/tests/getter_tests/assoc_type/auto_getter.rs index 0e98def4..32f5438c 100644 --- a/crates/cgp-tests/tests/getter_tests/assoc_type/auto_getter.rs +++ b/crates/cgp-tests/tests/getter_tests/assoc_type/auto_getter.rs @@ -1,8 +1,10 @@ +use core::fmt::Display; + use cgp::prelude::*; #[cgp_auto_getter] pub trait HasName { - type Name; + type Name: Display; fn name(&self) -> &Self::Name; } diff --git a/crates/cgp-tests/tests/getter_tests/assoc_type/getter.rs b/crates/cgp-tests/tests/getter_tests/assoc_type/getter.rs index fcc84989..6497a445 100644 --- a/crates/cgp-tests/tests/getter_tests/assoc_type/getter.rs +++ b/crates/cgp-tests/tests/getter_tests/assoc_type/getter.rs @@ -1,8 +1,10 @@ +use core::fmt::Display; + use cgp::prelude::*; #[cgp_getter] pub trait HasName { - type Name; + type Name: Display; fn name(&self) -> &Self::Name; } From 2127cade25f1813e4289e47a7a4279d057a8ca6b Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 1 Feb 2026 18:04:45 +0100 Subject: [PATCH 08/11] Allow associated type to be defined after getter method --- .../cgp-macro-lib/src/derive_getter/parse.rs | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/crates/cgp-macro-lib/src/derive_getter/parse.rs b/crates/cgp-macro-lib/src/derive_getter/parse.rs index e05f84fc..b27beca2 100644 --- a/crates/cgp-macro-lib/src/derive_getter/parse.rs +++ b/crates/cgp-macro-lib/src/derive_getter/parse.rs @@ -20,17 +20,9 @@ pub fn parse_getter_fields( let mut fields = Vec::new(); let mut field_assoc_type: Option = None; + // Extract optional associated type first for item in consumer_trait.items.iter() { match item { - TraitItem::Fn(method) => { - let getter_spec = parse_getter_method( - context_type, - method, - &field_assoc_type.as_ref().map(|item| item.ident.clone()), - )?; - - fields.push(getter_spec); - } TraitItem::Type(item_type) => { if field_assoc_type.is_some() { return Err(Error::new( @@ -48,6 +40,24 @@ pub fn parse_getter_fields( field_assoc_type = Some(item_type.clone()); } + _ => {} + } + } + + for item in consumer_trait.items.iter() { + match item { + TraitItem::Fn(method) => { + let getter_spec = parse_getter_method( + context_type, + method, + &field_assoc_type.as_ref().map(|item| item.ident.clone()), + )?; + + fields.push(getter_spec); + } + TraitItem::Type(_) => { + // Already processed in the previous loop + } _ => { return Err(Error::new( item.span(), From d6800c7ac63bf17929b8b116dd074f8ac87d4b3e Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 1 Feb 2026 18:05:05 +0100 Subject: [PATCH 09/11] Fix clippy --- .../cgp-macro-lib/src/derive_getter/parse.rs | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/crates/cgp-macro-lib/src/derive_getter/parse.rs b/crates/cgp-macro-lib/src/derive_getter/parse.rs index b27beca2..fb37c983 100644 --- a/crates/cgp-macro-lib/src/derive_getter/parse.rs +++ b/crates/cgp-macro-lib/src/derive_getter/parse.rs @@ -22,25 +22,22 @@ pub fn parse_getter_fields( // Extract optional associated type first for item in consumer_trait.items.iter() { - match item { - TraitItem::Type(item_type) => { - if field_assoc_type.is_some() { - return Err(Error::new( - item_type.span(), - "at most one associated type is allowed in getter trait", - )); - } - - if item_type.generics.params.len() > 0 { - return Err(Error::new( - item_type.generics.params.span(), - "associated type in getter trait must not contain generic params", - )); - } - - field_assoc_type = Some(item_type.clone()); + if let TraitItem::Type(item_type) = item { + if field_assoc_type.is_some() { + return Err(Error::new( + item_type.span(), + "at most one associated type is allowed in getter trait", + )); + } + + if !item_type.generics.params.is_empty() { + return Err(Error::new( + item_type.generics.params.span(), + "associated type in getter trait must not contain generic params", + )); } - _ => {} + + field_assoc_type = Some(item_type.clone()); } } From 039476e7754381964e9fb60ea6ef26f76d74fac0 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 1 Feb 2026 19:16:28 +0100 Subject: [PATCH 10/11] Add more tests --- crates/cgp-macro-lib/src/derive_getter/parse.rs | 5 +---- .../tests/getter_tests/assoc_type/auto_getter.rs | 8 ++++++++ .../tests/getter_tests/assoc_type/getter.rs | 15 +++++++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/crates/cgp-macro-lib/src/derive_getter/parse.rs b/crates/cgp-macro-lib/src/derive_getter/parse.rs index fb37c983..947efecb 100644 --- a/crates/cgp-macro-lib/src/derive_getter/parse.rs +++ b/crates/cgp-macro-lib/src/derive_getter/parse.rs @@ -75,10 +75,7 @@ pub fn parse_getter_fields( { return Err(Error::new( field.field_type.span(), - format!( - "getter method return type must match the associated type. {}", - field_type.to_token_stream() - ), + "getter method return type must match the associated type", )); } } diff --git a/crates/cgp-tests/tests/getter_tests/assoc_type/auto_getter.rs b/crates/cgp-tests/tests/getter_tests/assoc_type/auto_getter.rs index 32f5438c..ad6c03a5 100644 --- a/crates/cgp-tests/tests/getter_tests/assoc_type/auto_getter.rs +++ b/crates/cgp-tests/tests/getter_tests/assoc_type/auto_getter.rs @@ -8,3 +8,11 @@ pub trait HasName { fn name(&self) -> &Self::Name; } + +#[derive(HasField)] +pub struct Person { + pub name: String, +} + +pub trait CheckHasName: HasName {} +impl CheckHasName for Person {} diff --git a/crates/cgp-tests/tests/getter_tests/assoc_type/getter.rs b/crates/cgp-tests/tests/getter_tests/assoc_type/getter.rs index 6497a445..441a4941 100644 --- a/crates/cgp-tests/tests/getter_tests/assoc_type/getter.rs +++ b/crates/cgp-tests/tests/getter_tests/assoc_type/getter.rs @@ -8,3 +8,18 @@ pub trait HasName { fn name(&self) -> &Self::Name; } + +#[derive(HasField)] +pub struct Person { + pub first_name: String, +} + +delegate_components! { + Person { + NameGetterComponent: + UseField, + } +} + +pub trait CheckHasName: HasName {} +impl CheckHasName for Person {} From ee5726e800d6124dabec0bdf6f56d846f035969a Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 1 Feb 2026 19:30:18 +0100 Subject: [PATCH 11/11] Fix clippy --- crates/cgp-tests/tests/getter.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/cgp-tests/tests/getter.rs b/crates/cgp-tests/tests/getter.rs index 359a2dc2..7bc4e98e 100644 --- a/crates/cgp-tests/tests/getter.rs +++ b/crates/cgp-tests/tests/getter.rs @@ -1 +1,3 @@ +#![allow(clippy::disallowed_names)] + pub mod getter_tests;