From bbfc893b8c811d2bb9e37710738066617502741e Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Mon, 22 Jun 2026 22:46:40 +0200 Subject: [PATCH 01/19] Slightly simplify delegate_and_check_components --- .../delegate_and_check_components.rs | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/crates/macros/cgp-macro-lib/src/entrypoints/delegate_and_check_components.rs b/crates/macros/cgp-macro-lib/src/entrypoints/delegate_and_check_components.rs index 7f087056..e87d0a6f 100644 --- a/crates/macros/cgp-macro-lib/src/entrypoints/delegate_and_check_components.rs +++ b/crates/macros/cgp-macro-lib/src/entrypoints/delegate_and_check_components.rs @@ -5,8 +5,8 @@ use cgp_macro_core::types::generics::ImplGenerics; use proc_macro2::{Span, TokenStream}; use quote::quote; use syn::punctuated::Punctuated; -use syn::token::{Comma, Where}; -use syn::{Type, WhereClause, parse2}; +use syn::token::Where; +use syn::{WhereClause, parse2}; use crate::delegate_components::impl_delegate_components; use crate::parse::{DelegateAndCheckSpec, DelegateEntry, DelegateKey}; @@ -45,26 +45,24 @@ pub fn delegate_and_check_components(body: TokenStream) -> syn::Result, Comma> = spec - .entries - .into_iter() - .map(|entry| { - let keys = entry - .keys - .into_iter() - .map(|key| DelegateKey { - ty: key.component_type, - generics: ImplGenerics::default(), - }) - .collect(); + let mut delegate_entries = Punctuated::new(); - DelegateEntry { - keys, - value: entry.value, - mode: entry.mode, - } + for entry in spec.entries { + let keys = entry + .keys + .into_iter() + .map(|key| DelegateKey { + ty: key.component_type, + generics: ImplGenerics::default(), + }) + .collect(); + + delegate_entries.push(DelegateEntry { + keys, + value: entry.value, + mode: entry.mode, }) - .collect(); + } let mut out = impl_delegate_components(&spec.context_type, &spec.impl_generics, &delegate_entries)?; From 009ad7cdc37a63a8a085c0a03eee601767c3ef7c Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Mon, 22 Jun 2026 23:05:50 +0200 Subject: [PATCH 02/19] Draft new delegate_and_check_components types --- .../check_params.rs | 39 +++++++++++++++++++ .../delegate_and_check_components/entry.rs | 6 +++ .../delegate_and_check_components/key.rs | 26 +++++++++++++ .../delegate_and_check_components/mod.rs | 9 +++++ .../single_key.rs | 18 +++++++++ crates/macros/cgp-macro-core/src/types/mod.rs | 1 + 6 files changed, 99 insertions(+) create mode 100644 crates/macros/cgp-macro-core/src/types/delegate_and_check_components/check_params.rs create mode 100644 crates/macros/cgp-macro-core/src/types/delegate_and_check_components/entry.rs create mode 100644 crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key.rs create mode 100644 crates/macros/cgp-macro-core/src/types/delegate_and_check_components/mod.rs create mode 100644 crates/macros/cgp-macro-core/src/types/delegate_and_check_components/single_key.rs diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/check_params.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/check_params.rs new file mode 100644 index 00000000..9daedb2b --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/check_params.rs @@ -0,0 +1,39 @@ +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; +use syn::spanned::Spanned; +use syn::token::{Comma, Pound}; +use syn::{Attribute, Type}; + +pub enum CheckParamsAttribute { + None, + Skip, + Multi(Punctuated), +} + +impl Parse for CheckParamsAttribute { + fn parse(input: ParseStream) -> syn::Result { + if !input.peek(Pound) { + return Ok(Self::None); + } + + let attributes = input.call(Attribute::parse_outer)?; + + let [attribute]: [Attribute; 1] = attributes + .try_into() + .map_err(|_| input.error("Expected exactly one key attribute"))?; + + if attribute.path().is_ident("check_params") { + let params = attribute.parse_args_with(Punctuated::parse_terminated)?; + Ok(CheckParamsAttribute::Multi(params)) + } else if attribute.path().is_ident("skip_check") { + // TODO: validate that the attribute args are empty + + Ok(CheckParamsAttribute::Skip) + } else { + Err(syn::Error::new( + attribute.span(), + "Expected either `#[skip_check]` or `#[check_params]` attribute for specifying the check generics", + )) + } + } +} diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/entry.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/entry.rs new file mode 100644 index 00000000..6bca33bd --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/entry.rs @@ -0,0 +1,6 @@ +use crate::types::delegate_and_check_components::{CheckParamsAttribute, DelegateAndCheckKey}; + +pub struct DelegateAndCheckEntry { + pub check_params: CheckParamsAttribute, + pub key: DelegateAndCheckKey, +} diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key.rs new file mode 100644 index 00000000..da043a7f --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key.rs @@ -0,0 +1,26 @@ +use syn::bracketed; +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; +use syn::token::{Bracket, Comma}; + +use crate::types::delegate_and_check_components::SingleDelegateAndCheckKey; + +pub enum DelegateAndCheckKey { + Single(SingleDelegateAndCheckKey), + Multi(Punctuated), +} + +impl Parse for DelegateAndCheckKey { + fn parse(input: ParseStream) -> syn::Result { + if input.peek(Bracket) { + let body; + bracketed!(body in input); + + let keys = Punctuated::parse_terminated(&body)?; + Ok(Self::Multi(keys)) + } else { + let key = input.parse()?; + Ok(Self::Single(key)) + } + } +} diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/mod.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/mod.rs new file mode 100644 index 00000000..3685820f --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/mod.rs @@ -0,0 +1,9 @@ +mod check_params; +mod entry; +mod key; +mod single_key; + +pub use check_params::*; +pub use entry::*; +pub use key::*; +pub use single_key::*; diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/single_key.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/single_key.rs new file mode 100644 index 00000000..bfce0389 --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/single_key.rs @@ -0,0 +1,18 @@ +use syn::parse::{Parse, ParseStream}; + +use crate::types::delegate_and_check_components::CheckParamsAttribute; +use crate::types::delegate_component::DelegateKey; + +pub struct SingleDelegateAndCheckKey { + pub check_params: CheckParamsAttribute, + pub key: DelegateKey, +} + +impl Parse for SingleDelegateAndCheckKey { + fn parse(input: ParseStream) -> syn::Result { + let check_params = input.parse()?; + let key = input.parse()?; + + Ok(Self { check_params, key }) + } +} diff --git a/crates/macros/cgp-macro-core/src/types/mod.rs b/crates/macros/cgp-macro-core/src/types/mod.rs index 83876489..024cae30 100644 --- a/crates/macros/cgp-macro-core/src/types/mod.rs +++ b/crates/macros/cgp-macro-core/src/types/mod.rs @@ -7,6 +7,7 @@ pub mod cgp_getter; pub mod cgp_impl; pub mod cgp_provider; pub mod check_components; +pub mod delegate_and_check_components; pub mod delegate_component; pub mod empty_struct; pub mod field; From 4d62702b807ee221b1cd5574a56d636928f92f79 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Mon, 22 Jun 2026 23:15:51 +0200 Subject: [PATCH 03/19] Allow attributes in delegate keys --- .../src/types/delegate_component/key/multi.rs | 7 +++++-- .../src/types/delegate_component/key/path.rs | 11 ++++++++++- .../src/types/delegate_component/key/single.rs | 10 ++++++++-- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/crates/macros/cgp-macro-core/src/types/delegate_component/key/multi.rs b/crates/macros/cgp-macro-core/src/types/delegate_component/key/multi.rs index c571ae0b..c6e86b3a 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_component/key/multi.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_component/key/multi.rs @@ -1,22 +1,25 @@ -use syn::bracketed; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::token::Comma; +use syn::{Attribute, bracketed}; use crate::types::delegate_component::{EvalDelegateKey, EvaluatedDelegateKey, SingleDelegateKey}; #[derive(Debug, Clone)] pub struct MultiDelegateKey { + pub attributes: Vec, pub keys: Punctuated, } impl Parse for MultiDelegateKey { fn parse(input: ParseStream) -> syn::Result { + let attributes = input.call(Attribute::parse_outer)?; + let body; bracketed!(body in input); let keys = Punctuated::parse_terminated(&body)?; - Ok(Self { keys }) + Ok(Self { attributes, keys }) } } diff --git a/crates/macros/cgp-macro-core/src/types/delegate_component/key/path.rs b/crates/macros/cgp-macro-core/src/types/delegate_component/key/path.rs index 1b5c426f..17702b92 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_component/key/path.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_component/key/path.rs @@ -1,3 +1,4 @@ +use syn::Attribute; use syn::parse::{Parse, ParseStream}; use syn::token::At; @@ -9,6 +10,7 @@ use crate::types::path::PathHead; #[derive(Debug, Clone)] pub struct PathDelegateKey { + pub attributes: Vec, pub generics: ImplGenerics, pub at: At, pub path: PathHead, @@ -16,11 +18,18 @@ pub struct PathDelegateKey { impl Parse for PathDelegateKey { fn parse(input: ParseStream) -> syn::Result { + let attributes = input.call(Attribute::parse_outer)?; + let generics = input.parse()?; let at = input.parse()?; let path = input.parse()?; - Ok(Self { generics, at, path }) + Ok(Self { + attributes, + generics, + at, + path, + }) } } diff --git a/crates/macros/cgp-macro-core/src/types/delegate_component/key/single.rs b/crates/macros/cgp-macro-core/src/types/delegate_component/key/single.rs index 6e234165..7655a038 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_component/key/single.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_component/key/single.rs @@ -1,21 +1,27 @@ -use syn::Type; use syn::parse::{Parse, ParseStream}; +use syn::{Attribute, Type}; use crate::types::delegate_component::{EvalDelegateKey, EvaluatedDelegateKey}; use crate::types::generics::ImplGenerics; #[derive(Debug, Clone)] pub struct SingleDelegateKey { + pub attributes: Vec, pub generics: ImplGenerics, pub ty: Type, } impl Parse for SingleDelegateKey { fn parse(input: ParseStream) -> syn::Result { + let attributes = input.call(Attribute::parse_outer)?; let generics = input.parse()?; let ty = input.parse()?; - Ok(Self { generics, ty }) + Ok(Self { + attributes, + generics, + ty, + }) } } From bcb1e91b7fbd9c0d5747a44c91c421f1a3dec6b9 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Mon, 22 Jun 2026 23:22:47 +0200 Subject: [PATCH 04/19] Fix peek in DelegateKey --- .../src/types/delegate_component/key/combined.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/macros/cgp-macro-core/src/types/delegate_component/key/combined.rs b/crates/macros/cgp-macro-core/src/types/delegate_component/key/combined.rs index 04df43d4..05b9e0db 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_component/key/combined.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_component/key/combined.rs @@ -1,3 +1,4 @@ +use syn::Attribute; use syn::parse::{Parse, ParseStream}; use syn::token::{At, Bracket}; @@ -16,12 +17,14 @@ pub enum DelegateKey { impl Parse for DelegateKey { fn parse(input: ParseStream) -> syn::Result { let fork = input.fork(); + + let _attributes = fork.call(Attribute::parse_outer)?; let _generics: ImplGenerics = fork.parse()?; let key = if fork.peek(At) { let path = input.parse()?; Self::Path(path) - } else if input.peek(Bracket) { + } else if fork.peek(Bracket) { let keys = input.parse()?; Self::Multi(keys) } else { From 203bba7c525864262006e6d9d92cf1a8574130ce Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Mon, 22 Jun 2026 23:27:27 +0200 Subject: [PATCH 05/19] Refactor CheckParamsAttribute and remove delegate and check constructs --- .../check_params.rs | 17 +++--------- .../delegate_and_check_components/entry.rs | 6 ----- .../delegate_and_check_components/key.rs | 26 ------------------- .../delegate_and_check_components/mod.rs | 6 ----- .../single_key.rs | 18 ------------- 5 files changed, 4 insertions(+), 69 deletions(-) delete mode 100644 crates/macros/cgp-macro-core/src/types/delegate_and_check_components/entry.rs delete mode 100644 crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key.rs delete mode 100644 crates/macros/cgp-macro-core/src/types/delegate_and_check_components/single_key.rs diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/check_params.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/check_params.rs index 9daedb2b..73135a5c 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/check_params.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/check_params.rs @@ -1,7 +1,6 @@ -use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; -use syn::token::{Comma, Pound}; +use syn::token::Comma; use syn::{Attribute, Type}; pub enum CheckParamsAttribute { @@ -10,17 +9,9 @@ pub enum CheckParamsAttribute { Multi(Punctuated), } -impl Parse for CheckParamsAttribute { - fn parse(input: ParseStream) -> syn::Result { - if !input.peek(Pound) { - return Ok(Self::None); - } - - let attributes = input.call(Attribute::parse_outer)?; - - let [attribute]: [Attribute; 1] = attributes - .try_into() - .map_err(|_| input.error("Expected exactly one key attribute"))?; +impl CheckParamsAttribute { + pub fn parse_attributes(attributes: &[Attribute]) -> syn::Result { + let attribute = &attributes[0]; if attribute.path().is_ident("check_params") { let params = attribute.parse_args_with(Punctuated::parse_terminated)?; diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/entry.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/entry.rs deleted file mode 100644 index 6bca33bd..00000000 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/entry.rs +++ /dev/null @@ -1,6 +0,0 @@ -use crate::types::delegate_and_check_components::{CheckParamsAttribute, DelegateAndCheckKey}; - -pub struct DelegateAndCheckEntry { - pub check_params: CheckParamsAttribute, - pub key: DelegateAndCheckKey, -} diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key.rs deleted file mode 100644 index da043a7f..00000000 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key.rs +++ /dev/null @@ -1,26 +0,0 @@ -use syn::bracketed; -use syn::parse::{Parse, ParseStream}; -use syn::punctuated::Punctuated; -use syn::token::{Bracket, Comma}; - -use crate::types::delegate_and_check_components::SingleDelegateAndCheckKey; - -pub enum DelegateAndCheckKey { - Single(SingleDelegateAndCheckKey), - Multi(Punctuated), -} - -impl Parse for DelegateAndCheckKey { - fn parse(input: ParseStream) -> syn::Result { - if input.peek(Bracket) { - let body; - bracketed!(body in input); - - let keys = Punctuated::parse_terminated(&body)?; - Ok(Self::Multi(keys)) - } else { - let key = input.parse()?; - Ok(Self::Single(key)) - } - } -} diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/mod.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/mod.rs index 3685820f..c97ada04 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/mod.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/mod.rs @@ -1,9 +1,3 @@ mod check_params; -mod entry; -mod key; -mod single_key; pub use check_params::*; -pub use entry::*; -pub use key::*; -pub use single_key::*; diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/single_key.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/single_key.rs deleted file mode 100644 index bfce0389..00000000 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/single_key.rs +++ /dev/null @@ -1,18 +0,0 @@ -use syn::parse::{Parse, ParseStream}; - -use crate::types::delegate_and_check_components::CheckParamsAttribute; -use crate::types::delegate_component::DelegateKey; - -pub struct SingleDelegateAndCheckKey { - pub check_params: CheckParamsAttribute, - pub key: DelegateKey, -} - -impl Parse for SingleDelegateAndCheckKey { - fn parse(input: ParseStream) -> syn::Result { - let check_params = input.parse()?; - let key = input.parse()?; - - Ok(Self { check_params, key }) - } -} From dc4f9967214a65e7fd7e2462c1679cf7ba507454 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Mon, 22 Jun 2026 23:37:20 +0200 Subject: [PATCH 06/19] Implement ToCheckEntries for SingleDelegateKey --- .../src/types/check_components/entries.rs | 1 + .../check_params.rs | 4 ++ .../delegate_and_check_components/key.rs | 39 +++++++++++++++++++ .../delegate_and_check_components/mod.rs | 3 ++ .../to_check_entries.rs | 5 +++ 5 files changed, 52 insertions(+) create mode 100644 crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key.rs create mode 100644 crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_check_entries.rs diff --git a/crates/macros/cgp-macro-core/src/types/check_components/entries.rs b/crates/macros/cgp-macro-core/src/types/check_components/entries.rs index 956ebc7f..8955c635 100644 --- a/crates/macros/cgp-macro-core/src/types/check_components/entries.rs +++ b/crates/macros/cgp-macro-core/src/types/check_components/entries.rs @@ -4,6 +4,7 @@ use syn::token::Comma; use crate::types::check_components::{CheckEntry, EvaluatedCheckEntry}; +#[derive(Default)] pub struct CheckEntries { pub entries: Punctuated, } diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/check_params.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/check_params.rs index 73135a5c..b7966778 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/check_params.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/check_params.rs @@ -11,6 +11,10 @@ pub enum CheckParamsAttribute { impl CheckParamsAttribute { pub fn parse_attributes(attributes: &[Attribute]) -> syn::Result { + if attributes.is_empty() { + return Ok(Self::None); + } + let attribute = &attributes[0]; if attribute.path().is_ident("check_params") { diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key.rs new file mode 100644 index 00000000..bef7cfb0 --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key.rs @@ -0,0 +1,39 @@ +use syn::punctuated::Punctuated; + +use crate::types::check_components::{ + CheckEntries, CheckEntry, CheckKey, CheckValue, TypeWithGenerics, +}; +use crate::types::delegate_and_check_components::{CheckParamsAttribute, ToCheckEntries}; +use crate::types::delegate_component::SingleDelegateKey; + +impl ToCheckEntries for SingleDelegateKey { + fn to_check_entries(&self) -> syn::Result { + let check_params = CheckParamsAttribute::parse_attributes(&self.attributes)?; + + match check_params { + CheckParamsAttribute::None => { + let entry = CheckEntry { + key: CheckKey::Single(self.ty.clone()), + value: None, + }; + + Ok(CheckEntries { + entries: Punctuated::from_iter([entry]), + }) + } + CheckParamsAttribute::Skip => Ok(CheckEntries::default()), + CheckParamsAttribute::Multi(params) => { + let mut entries = CheckEntries::default(); + + for param in params { + entries.entries.push(CheckEntry { + key: CheckKey::Single(self.ty.clone()), + value: Some(CheckValue::Single(Box::new(TypeWithGenerics::from(param)))), + }) + } + + Ok(entries) + } + } + } +} diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/mod.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/mod.rs index c97ada04..4c026aff 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/mod.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/mod.rs @@ -1,3 +1,6 @@ mod check_params; +mod key; +mod to_check_entries; pub use check_params::*; +pub use to_check_entries::*; diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_check_entries.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_check_entries.rs new file mode 100644 index 00000000..b8ceb445 --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_check_entries.rs @@ -0,0 +1,5 @@ +use crate::types::check_components::CheckEntries; + +pub trait ToCheckEntries { + fn to_check_entries(&self) -> syn::Result; +} From 09f1f294cfd7bdf1a285b5ab4beb5d6fca1414ed Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Mon, 22 Jun 2026 23:44:10 +0200 Subject: [PATCH 07/19] Implement KeyWithCheckParams --- .../delegate_and_check_components/key.rs | 8 +++- .../key_with_check_params.rs | 44 +++++++++++++++++++ .../delegate_and_check_components/mod.rs | 2 + 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key_with_check_params.rs diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key.rs index bef7cfb0..4a84f6c4 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key.rs @@ -4,7 +4,7 @@ use crate::types::check_components::{ CheckEntries, CheckEntry, CheckKey, CheckValue, TypeWithGenerics, }; use crate::types::delegate_and_check_components::{CheckParamsAttribute, ToCheckEntries}; -use crate::types::delegate_component::SingleDelegateKey; +use crate::types::delegate_component::{MultiDelegateKey, SingleDelegateKey}; impl ToCheckEntries for SingleDelegateKey { fn to_check_entries(&self) -> syn::Result { @@ -37,3 +37,9 @@ impl ToCheckEntries for SingleDelegateKey { } } } + +impl ToCheckEntries for MultiDelegateKey { + fn to_check_entries(&self) -> syn::Result { + todo!() + } +} diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key_with_check_params.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key_with_check_params.rs new file mode 100644 index 00000000..9fe07f63 --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key_with_check_params.rs @@ -0,0 +1,44 @@ +use syn::Type; +use syn::punctuated::Punctuated; + +use crate::types::check_components::{ + CheckEntries, CheckEntry, CheckKey, CheckValue, TypeWithGenerics, +}; +use crate::types::delegate_and_check_components::CheckParamsAttribute; + +pub struct KeyWithCheckParams { + pub key_type: Type, + pub check_params: CheckParamsAttribute, +} + +impl KeyWithCheckParams { + pub fn to_check_entry(&self) -> CheckEntries { + match &self.check_params { + CheckParamsAttribute::None => { + let entry = CheckEntry { + key: CheckKey::Single(self.key_type.clone()), + value: None, + }; + + CheckEntries { + entries: Punctuated::from_iter([entry]), + } + } + CheckParamsAttribute::Skip => CheckEntries::default(), + CheckParamsAttribute::Multi(params) => { + let mut entries = CheckEntries::default(); + + for param in params { + entries.entries.push(CheckEntry { + key: CheckKey::Single(self.key_type.clone()), + value: Some(CheckValue::Single(Box::new(TypeWithGenerics::from( + param.clone(), + )))), + }) + } + + entries + } + } + } +} diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/mod.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/mod.rs index 4c026aff..3aeef26b 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/mod.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/mod.rs @@ -1,6 +1,8 @@ mod check_params; mod key; +mod key_with_check_params; mod to_check_entries; pub use check_params::*; +pub use key_with_check_params::*; pub use to_check_entries::*; From cee020703a57659182ee253b5a419c77928ef496 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Mon, 22 Jun 2026 23:47:47 +0200 Subject: [PATCH 08/19] Implement ToKeysWithCheckParams for SingleDelegateKey --- .../delegate_and_check_components/key.rs | 45 ------------------- .../delegate_and_check_components/mod.rs | 5 +-- .../to_check_entries.rs | 5 --- .../to_keys_with_check_params.rs | 19 ++++++++ 4 files changed, 21 insertions(+), 53 deletions(-) delete mode 100644 crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key.rs delete mode 100644 crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_check_entries.rs create mode 100644 crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key.rs deleted file mode 100644 index 4a84f6c4..00000000 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key.rs +++ /dev/null @@ -1,45 +0,0 @@ -use syn::punctuated::Punctuated; - -use crate::types::check_components::{ - CheckEntries, CheckEntry, CheckKey, CheckValue, TypeWithGenerics, -}; -use crate::types::delegate_and_check_components::{CheckParamsAttribute, ToCheckEntries}; -use crate::types::delegate_component::{MultiDelegateKey, SingleDelegateKey}; - -impl ToCheckEntries for SingleDelegateKey { - fn to_check_entries(&self) -> syn::Result { - let check_params = CheckParamsAttribute::parse_attributes(&self.attributes)?; - - match check_params { - CheckParamsAttribute::None => { - let entry = CheckEntry { - key: CheckKey::Single(self.ty.clone()), - value: None, - }; - - Ok(CheckEntries { - entries: Punctuated::from_iter([entry]), - }) - } - CheckParamsAttribute::Skip => Ok(CheckEntries::default()), - CheckParamsAttribute::Multi(params) => { - let mut entries = CheckEntries::default(); - - for param in params { - entries.entries.push(CheckEntry { - key: CheckKey::Single(self.ty.clone()), - value: Some(CheckValue::Single(Box::new(TypeWithGenerics::from(param)))), - }) - } - - Ok(entries) - } - } - } -} - -impl ToCheckEntries for MultiDelegateKey { - fn to_check_entries(&self) -> syn::Result { - todo!() - } -} diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/mod.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/mod.rs index 3aeef26b..71b3f88f 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/mod.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/mod.rs @@ -1,8 +1,7 @@ mod check_params; -mod key; mod key_with_check_params; -mod to_check_entries; +mod to_keys_with_check_params; pub use check_params::*; pub use key_with_check_params::*; -pub use to_check_entries::*; +pub use to_keys_with_check_params::*; diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_check_entries.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_check_entries.rs deleted file mode 100644 index b8ceb445..00000000 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_check_entries.rs +++ /dev/null @@ -1,5 +0,0 @@ -use crate::types::check_components::CheckEntries; - -pub trait ToCheckEntries { - fn to_check_entries(&self) -> syn::Result; -} diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs new file mode 100644 index 00000000..5e2e529a --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs @@ -0,0 +1,19 @@ +use crate::types::delegate_and_check_components::{CheckParamsAttribute, KeyWithCheckParams}; +use crate::types::delegate_component::SingleDelegateKey; + +pub trait ToKeysWithCheckParams { + fn to_keys_with_check_params(&self) -> syn::Result>; +} + +impl ToKeysWithCheckParams for SingleDelegateKey { + fn to_keys_with_check_params(&self) -> syn::Result> { + let check_params = CheckParamsAttribute::parse_attributes(&self.attributes)?; + + let key = KeyWithCheckParams { + check_params, + key_type: self.ty.clone(), + }; + + Ok(vec![key]) + } +} From df0bc3901db6f2793d8423b43d6f28d85c77ad43 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Mon, 22 Jun 2026 23:53:54 +0200 Subject: [PATCH 09/19] Implement CheckParamsAttribute::merge --- .../check_params.rs | 32 +++++++++++++++++-- .../key_with_check_params.rs | 2 +- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/check_params.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/check_params.rs index b7966778..eaf9526e 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/check_params.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/check_params.rs @@ -1,18 +1,44 @@ use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::token::Comma; -use syn::{Attribute, Type}; +use syn::{Attribute, Error, Type}; +#[derive(Clone)] pub enum CheckParamsAttribute { - None, + Default, Skip, Multi(Punctuated), } impl CheckParamsAttribute { + pub fn merge(&self, other: &Self) -> syn::Result { + let res = match (self, other) { + (Self::Default, other) => other.clone(), + (other, Self::Default) => other.clone(), + (Self::Skip, Self::Skip) => Self::Skip, + (Self::Multi(params_a), Self::Multi(params_b)) => Self::Multi(Punctuated::from_iter( + params_a.iter().chain(params_b.iter()).cloned(), + )), + (Self::Skip, Self::Multi(params)) => { + return Err(Error::new( + params.span(), + "cannot combine #[skip] with #[check_params]", + )); + } + (Self::Multi(params), Self::Skip) => { + return Err(Error::new( + params.span(), + "cannot combine #[skip] with #[check_params]", + )); + } + }; + + Ok(res) + } + pub fn parse_attributes(attributes: &[Attribute]) -> syn::Result { if attributes.is_empty() { - return Ok(Self::None); + return Ok(Self::Default); } let attribute = &attributes[0]; diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key_with_check_params.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key_with_check_params.rs index 9fe07f63..8f1411ec 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key_with_check_params.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key_with_check_params.rs @@ -14,7 +14,7 @@ pub struct KeyWithCheckParams { impl KeyWithCheckParams { pub fn to_check_entry(&self) -> CheckEntries { match &self.check_params { - CheckParamsAttribute::None => { + CheckParamsAttribute::Default => { let entry = CheckEntry { key: CheckKey::Single(self.key_type.clone()), value: None, From 5fd5ea5548a4ed621688f736c46096d16d7f5c19 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Tue, 23 Jun 2026 21:35:42 +0200 Subject: [PATCH 10/19] Implement ToKeysWithCheckParams for MultiDelegateKey --- .../to_keys_with_check_params.rs | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs index 5e2e529a..d98e988d 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs @@ -1,5 +1,5 @@ use crate::types::delegate_and_check_components::{CheckParamsAttribute, KeyWithCheckParams}; -use crate::types::delegate_component::SingleDelegateKey; +use crate::types::delegate_component::{DelegateKey, MultiDelegateKey, SingleDelegateKey}; pub trait ToKeysWithCheckParams { fn to_keys_with_check_params(&self) -> syn::Result>; @@ -17,3 +17,34 @@ impl ToKeysWithCheckParams for SingleDelegateKey { Ok(vec![key]) } } + +impl ToKeysWithCheckParams for MultiDelegateKey { + fn to_keys_with_check_params(&self) -> syn::Result> { + let check_params = CheckParamsAttribute::parse_attributes(&self.attributes)?; + + let mut out = Vec::new(); + + for key in &self.keys { + let inner_res = key.to_keys_with_check_params()?; + for inner in inner_res { + let inner_params = check_params.merge(&inner.check_params)?; + out.push(KeyWithCheckParams { + key_type: inner.key_type, + check_params: inner_params, + }) + } + } + + Ok(out) + } +} + +impl ToKeysWithCheckParams for DelegateKey { + fn to_keys_with_check_params(&self) -> syn::Result> { + match self { + DelegateKey::Single(key) => key.to_keys_with_check_params(), + DelegateKey::Multi(key) => key.to_keys_with_check_params(), + DelegateKey::Path(_key) => Ok(Vec::new()), + } + } +} From f5f8263df6061f6708e8fae938c8a0feb75d888b Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Tue, 23 Jun 2026 21:43:33 +0200 Subject: [PATCH 11/19] Implement ToKeysWithCheckParams for DelegateEntries --- .../to_keys_with_check_params.rs | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs index d98e988d..961e7888 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs @@ -1,5 +1,7 @@ use crate::types::delegate_and_check_components::{CheckParamsAttribute, KeyWithCheckParams}; -use crate::types::delegate_component::{DelegateKey, MultiDelegateKey, SingleDelegateKey}; +use crate::types::delegate_component::{ + DelegateEntries, DelegateKey, DelegateMapping, MultiDelegateKey, SingleDelegateKey, +}; pub trait ToKeysWithCheckParams { fn to_keys_with_check_params(&self) -> syn::Result>; @@ -48,3 +50,25 @@ impl ToKeysWithCheckParams for DelegateKey { } } } + +impl ToKeysWithCheckParams for DelegateMapping { + fn to_keys_with_check_params(&self) -> syn::Result> { + match self { + DelegateMapping::Normal(mapping) => mapping.key.to_keys_with_check_params(), + DelegateMapping::Direct(_mapping) => Ok(Vec::new()), + DelegateMapping::Redirect(_mapping) => Ok(Vec::new()), + } + } +} + +impl ToKeysWithCheckParams for DelegateEntries { + fn to_keys_with_check_params(&self) -> syn::Result> { + let mut out = Vec::new(); + + for entry in &self.entries { + out.extend(entry.to_keys_with_check_params()?); + } + + Ok(out) + } +} From ef873dcd0bebe3dd17705c42ad683aa66bfc4f92 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Tue, 23 Jun 2026 22:00:14 +0200 Subject: [PATCH 12/19] Implement check_trait_ident --- .../delegate_and_check_components/item.rs | 52 +++++++++++++++++++ .../delegate_and_check_components/mod.rs | 2 + .../types/delegate_component/table/main.rs | 6 ++- 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 crates/macros/cgp-macro-core/src/types/delegate_and_check_components/item.rs diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/item.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/item.rs new file mode 100644 index 00000000..c8b03039 --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/item.rs @@ -0,0 +1,52 @@ +use syn::parse::{Parse, ParseStream}; +use syn::spanned::Spanned; +use syn::{Error, Ident}; + +use crate::parse_internal; +use crate::types::delegate_component::DelegateTable; +use crate::types::ident::IdentWithTypeArgs; + +pub struct ItemDelegateAndCheckComponents { + pub table: DelegateTable, +} + +impl ItemDelegateAndCheckComponents { + pub fn check_trait_ident(&self) -> syn::Result { + let attributes = &self.table.attributes; + + if attributes.is_empty() { + let context_type = &self.table.table_type; + let context_type: IdentWithTypeArgs = parse_internal!(#context_type); + + Ok(Ident::new( + &format!("__CanUse{}", context_type.ident), + context_type.span(), + )) + } else if attributes.len() > 1 { + Err(Error::new( + attributes[1].span(), + "Expected exactly one attribute for the check trait name", + )) + } else { + let attribute = &attributes[0]; + if !attribute.path().is_ident("check_trait") { + return Err(syn::Error::new( + attribute.span(), + "Expected `#[check_trait]` attribute for specifying the check trait name", + )); + } + + let ident = attribute.parse_args()?; + + Ok(ident) + } + } +} + +impl Parse for ItemDelegateAndCheckComponents { + fn parse(input: ParseStream) -> syn::Result { + let table = input.parse()?; + + Ok(Self { table }) + } +} diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/mod.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/mod.rs index 71b3f88f..a567e40a 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/mod.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/mod.rs @@ -1,7 +1,9 @@ mod check_params; +mod item; mod key_with_check_params; mod to_keys_with_check_params; pub use check_params::*; +pub use item::*; pub use key_with_check_params::*; pub use to_keys_with_check_params::*; diff --git a/crates/macros/cgp-macro-core/src/types/delegate_component/table/main.rs b/crates/macros/cgp-macro-core/src/types/delegate_component/table/main.rs index 5a2b8dcc..54aa2776 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_component/table/main.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_component/table/main.rs @@ -1,7 +1,7 @@ use proc_macro2::TokenStream; use quote::ToTokens; use syn::parse::{Parse, ParseStream}; -use syn::{ItemImpl, Type, braced}; +use syn::{Attribute, ItemImpl, Type, braced}; use crate::functions::parse_internal; use crate::traits::ParseOptionalKeyword; @@ -13,6 +13,7 @@ use crate::types::keyword::Keyword; use crate::types::keywords::New; pub struct DelegateTable { + pub attributes: Vec, pub impl_generics: ImplGenerics, pub new: Option>, pub table_type: Type, @@ -26,6 +27,8 @@ pub struct EvaluatedDelegateTable { impl Parse for DelegateTable { fn parse(input: ParseStream) -> syn::Result { + let attributes = input.call(Attribute::parse_outer)?; + let impl_generics = input.parse()?; let new = input.parse_optional_keyword()?; @@ -40,6 +43,7 @@ impl Parse for DelegateTable { }; Ok(Self { + attributes, impl_generics, new, table_type, From ed2eafac1287c913d572255695a439f20ebd4abf Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Tue, 23 Jun 2026 22:08:43 +0200 Subject: [PATCH 13/19] Implement ItemDelegateAndCheckComponents --- .../src/types/check_components/table.rs | 9 ++---- .../delegate_and_check_components/item.rs | 30 +++++++++++++++++++ .../key_with_check_params.rs | 2 +- .../delegate_and_check_components.rs | 10 ++----- 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/crates/macros/cgp-macro-core/src/types/check_components/table.rs b/crates/macros/cgp-macro-core/src/types/check_components/table.rs index bac27732..40120c19 100644 --- a/crates/macros/cgp-macro-core/src/types/check_components/table.rs +++ b/crates/macros/cgp-macro-core/src/types/check_components/table.rs @@ -17,7 +17,7 @@ pub struct CheckComponentsTable { pub impl_generics: ImplGenerics, pub trait_name: Ident, pub context_type: Type, - pub where_clause: WhereClause, + pub where_clause: Option, pub check_entries: CheckEntries, } @@ -150,12 +150,9 @@ impl Parse for CheckComponentsTable { }; let where_clause = if input.peek(Where) { - input.parse()? + Some(input.parse()?) } else { - WhereClause { - where_token: Where(Span::call_site()), - predicates: Punctuated::default(), - } + None }; let content; diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/item.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/item.rs index c8b03039..80482931 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/item.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/item.rs @@ -3,6 +3,8 @@ use syn::spanned::Spanned; use syn::{Error, Ident}; use crate::parse_internal; +use crate::types::check_components::{CheckComponentsTable, CheckEntries}; +use crate::types::delegate_and_check_components::ToKeysWithCheckParams; use crate::types::delegate_component::DelegateTable; use crate::types::ident::IdentWithTypeArgs; @@ -11,6 +13,34 @@ pub struct ItemDelegateAndCheckComponents { } impl ItemDelegateAndCheckComponents { + pub fn to_check_components(&self) -> syn::Result { + let trait_name = self.check_trait_ident()?; + let check_entries = self.to_check_entries()?; + + let check_table = CheckComponentsTable { + check_providers: None, + impl_generics: self.table.impl_generics.clone(), + trait_name, + context_type: self.table.table_type.clone(), + where_clause: None, + check_entries, + }; + + Ok(check_table) + } + + pub fn to_check_entries(&self) -> syn::Result { + let keys = self.table.entries.to_keys_with_check_params()?; + + let mut entries = CheckEntries::default(); + + for key in keys { + entries.entries.extend(key.to_check_entries().entries); + } + + Ok(entries) + } + pub fn check_trait_ident(&self) -> syn::Result { let attributes = &self.table.attributes; diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key_with_check_params.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key_with_check_params.rs index 8f1411ec..84c9c054 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key_with_check_params.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/key_with_check_params.rs @@ -12,7 +12,7 @@ pub struct KeyWithCheckParams { } impl KeyWithCheckParams { - pub fn to_check_entry(&self) -> CheckEntries { + pub fn to_check_entries(&self) -> CheckEntries { match &self.check_params { CheckParamsAttribute::Default => { let entry = CheckEntry { diff --git a/crates/macros/cgp-macro-lib/src/entrypoints/delegate_and_check_components.rs b/crates/macros/cgp-macro-lib/src/entrypoints/delegate_and_check_components.rs index e87d0a6f..91e9c7d0 100644 --- a/crates/macros/cgp-macro-lib/src/entrypoints/delegate_and_check_components.rs +++ b/crates/macros/cgp-macro-lib/src/entrypoints/delegate_and_check_components.rs @@ -2,11 +2,10 @@ use cgp_macro_core::types::check_components::{ CheckComponentsTable, CheckEntries, CheckEntry, CheckKey, CheckValue, TypeWithGenerics, }; use cgp_macro_core::types::generics::ImplGenerics; -use proc_macro2::{Span, TokenStream}; +use proc_macro2::TokenStream; use quote::quote; +use syn::parse2; use syn::punctuated::Punctuated; -use syn::token::Where; -use syn::{WhereClause, parse2}; use crate::delegate_components::impl_delegate_components; use crate::parse::{DelegateAndCheckSpec, DelegateEntry, DelegateKey}; @@ -72,10 +71,7 @@ pub fn delegate_and_check_components(body: TokenStream) -> syn::Result Date: Tue, 23 Jun 2026 22:11:56 +0200 Subject: [PATCH 14/19] Use ItemDelegateAndCheckComponents --- .../delegate_and_check_components.rs | 85 ++----------------- 1 file changed, 9 insertions(+), 76 deletions(-) diff --git a/crates/macros/cgp-macro-lib/src/entrypoints/delegate_and_check_components.rs b/crates/macros/cgp-macro-lib/src/entrypoints/delegate_and_check_components.rs index 91e9c7d0..8bba31bc 100644 --- a/crates/macros/cgp-macro-lib/src/entrypoints/delegate_and_check_components.rs +++ b/crates/macros/cgp-macro-lib/src/entrypoints/delegate_and_check_components.rs @@ -1,87 +1,20 @@ -use cgp_macro_core::types::check_components::{ - CheckComponentsTable, CheckEntries, CheckEntry, CheckKey, CheckValue, TypeWithGenerics, -}; -use cgp_macro_core::types::generics::ImplGenerics; +use cgp_macro_core::types::delegate_and_check_components::ItemDelegateAndCheckComponents; use proc_macro2::TokenStream; use quote::quote; use syn::parse2; -use syn::punctuated::Punctuated; - -use crate::delegate_components::impl_delegate_components; -use crate::parse::{DelegateAndCheckSpec, DelegateEntry, DelegateKey}; pub fn delegate_and_check_components(body: TokenStream) -> syn::Result { - let spec: DelegateAndCheckSpec = parse2(body)?; - - let mut check_entries = Punctuated::new(); - - for entry in &spec.entries { - for key in &entry.keys { - let component_type = &key.component_type; - - match &key.check_params { - Some(check_params) => { - // Emit one check entry per param so that a single-key/single-param - // entry resolves the error span to the component type (via eval()'s - // `component_types_count >= component_params_count` heuristic), and so - // that an empty param list (i.e. `#[skip_check]`) emits no check at all. - for check_param in check_params { - check_entries.push(CheckEntry { - key: CheckKey::Single(component_type.clone()), - value: Some(CheckValue::Single(Box::new(TypeWithGenerics::from( - check_param.clone(), - )))), - }); - } - } - None => { - check_entries.push(CheckEntry { - key: CheckKey::Single(component_type.clone()), - value: None, - }); - } - } - } - } - - let mut delegate_entries = Punctuated::new(); - - for entry in spec.entries { - let keys = entry - .keys - .into_iter() - .map(|key| DelegateKey { - ty: key.component_type, - generics: ImplGenerics::default(), - }) - .collect(); - - delegate_entries.push(DelegateEntry { - keys, - value: entry.value, - mode: entry.mode, - }) - } + let item: ItemDelegateAndCheckComponents = parse2(body)?; - let mut out = - impl_delegate_components(&spec.context_type, &spec.impl_generics, &delegate_entries)?; + let check_table = item.to_check_components()?; - let check_spec = CheckComponentsTable { - check_providers: None, - impl_generics: spec.impl_generics, - trait_name: spec.trait_name, - context_type: spec.context_type, - where_clause: None, - check_entries: CheckEntries { - entries: check_entries, - }, - }; + let evaluated_table = item.table.eval()?; - let items = check_spec.to_items()?; + let check_items = check_table.to_items()?; - out.extend(quote! { - #( #items )* - }); + Ok(quote! { + #evaluated_table - Ok(out) + #( #check_items )* + }) } From 27581aca6b23a9910d5394dd57e5e42af0503be1 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Tue, 23 Jun 2026 22:14:06 +0200 Subject: [PATCH 15/19] Remove obsolete constructs --- .../parse/delegate_and_check_components.rs | 158 ------------------ crates/macros/cgp-macro-lib/src/parse/mod.rs | 2 - .../cgp-tests/src/tests/check_components.rs | 8 +- .../src/tests/use_delegate/getter.rs | 28 ++-- 4 files changed, 18 insertions(+), 178 deletions(-) delete mode 100644 crates/macros/cgp-macro-lib/src/parse/delegate_and_check_components.rs diff --git a/crates/macros/cgp-macro-lib/src/parse/delegate_and_check_components.rs b/crates/macros/cgp-macro-lib/src/parse/delegate_and_check_components.rs deleted file mode 100644 index 0c11087d..00000000 --- a/crates/macros/cgp-macro-lib/src/parse/delegate_and_check_components.rs +++ /dev/null @@ -1,158 +0,0 @@ -use core::iter; - -use cgp_macro_core::types::generics::ImplGenerics; -use cgp_macro_core::types::ident::IdentWithTypeArgs; -use quote::ToTokens; -use syn::parse::{Parse, ParseStream}; -use syn::punctuated::Punctuated; -use syn::spanned::Spanned; -use syn::token::{Bracket, Comma, Lt, Pound}; -use syn::{Attribute, Ident, Type, braced, bracketed, parse2}; - -use crate::parse::{DelegateMode, DelegateValue}; - -pub struct DelegateAndCheckSpec { - pub impl_generics: ImplGenerics, - pub trait_name: Ident, - pub context_type: Type, - pub entries: Punctuated, -} - -#[derive(Clone)] -pub struct DelegateAndCheckEntry { - pub keys: Punctuated, - pub mode: DelegateMode, - pub value: DelegateValue, -} - -#[derive(Clone)] -pub struct DelegateAndCheckKey { - pub component_type: Type, - pub check_params: Option>, -} - -impl Parse for DelegateAndCheckSpec { - fn parse(input: ParseStream) -> syn::Result { - let impl_generics = if input.peek(Lt) { - input.parse()? - } else { - Default::default() - }; - - let m_trait_name = parse_check_trait_name(input)?; - - let context_type: Type = input.parse()?; - - let trait_name = match m_trait_name { - Some(ident) => ident, - None => { - let context_type: IdentWithTypeArgs = parse2(context_type.to_token_stream())?; - Ident::new( - &format!("__CanUse{}", context_type.ident), - context_type.span(), - ) - } - }; - - let entries = { - let body; - braced!(body in input); - Punctuated::parse_terminated(&body)? - }; - - Ok(Self { - impl_generics, - trait_name, - context_type, - entries, - }) - } -} - -impl Parse for DelegateAndCheckEntry { - fn parse(input: ParseStream) -> syn::Result { - let check_params = parse_check_params(input)?; - - let mut keys = if input.peek(Bracket) { - let body; - bracketed!(body in input); - Punctuated::parse_terminated(&body)? - } else { - let key: DelegateAndCheckKey = input.parse()?; - Punctuated::from_iter(iter::once(key)) - }; - - if let Some(check_params) = check_params { - for key in &mut keys { - key.check_params - .get_or_insert_default() - .extend(check_params.clone()); - } - } - - let mode = input.parse()?; - - let value = input.parse()?; - - Ok(Self { keys, mode, value }) - } -} - -impl Parse for DelegateAndCheckKey { - fn parse(input: ParseStream) -> syn::Result { - let check_params = parse_check_params(input)?; - - let component_type: Type = input.parse()?; - Ok(Self { - component_type, - check_params, - }) - } -} - -pub fn parse_check_trait_name(input: ParseStream) -> syn::Result> { - if input.peek(Pound) { - let attributes = input.call(Attribute::parse_outer)?; - - let [attribute]: [Attribute; 1] = attributes - .try_into() - .map_err(|_| input.error("Expected exactly one attribute for the check trait name"))?; - - if !attribute.path().is_ident("check_trait") { - return Err(syn::Error::new( - attribute.span(), - "Expected `#[check_trait]` attribute for specifying the check trait name", - )); - } - - let ident: Ident = attribute.parse_args()?; - Ok(Some(ident)) - } else { - Ok(None) - } -} - -pub fn parse_check_params(input: ParseStream) -> syn::Result>> { - if input.peek(Pound) { - let attributes = input.call(Attribute::parse_outer)?; - - let [attribute]: [Attribute; 1] = attributes - .try_into() - .map_err(|_| input.error("Expected exactly one key attribute"))?; - - let check_params = if attribute.path().is_ident("check_params") { - attribute.parse_args_with(Punctuated::parse_terminated)? - } else if attribute.path().is_ident("skip_check") { - Punctuated::new() - } else { - return Err(syn::Error::new( - attribute.span(), - "Expected either `#[skip_check]` or `#[check_params]` attribute for specifying the check generics", - )); - }; - - Ok(Some(check_params)) - } else { - Ok(None) - } -} diff --git a/crates/macros/cgp-macro-lib/src/parse/mod.rs b/crates/macros/cgp-macro-lib/src/parse/mod.rs index a2690f75..ea12a5f9 100644 --- a/crates/macros/cgp-macro-lib/src/parse/mod.rs +++ b/crates/macros/cgp-macro-lib/src/parse/mod.rs @@ -1,11 +1,9 @@ mod define_preset; -mod delegate_and_check_components; mod delegate_components; mod path; mod type_spec; pub use define_preset::*; -pub use delegate_and_check_components::*; pub use delegate_components::*; pub use path::*; pub use type_spec::*; diff --git a/crates/tests/cgp-tests/src/tests/check_components.rs b/crates/tests/cgp-tests/src/tests/check_components.rs index 5daf1824..d4841431 100644 --- a/crates/tests/cgp-tests/src/tests/check_components.rs +++ b/crates/tests/cgp-tests/src/tests/check_components.rs @@ -654,14 +654,14 @@ mod basic_check_components { >: CanUseComponent<__Component__, __Params__> {} impl __CanUseContext for Context {} impl __CanUseContext for Context {} - impl __CanUseContext> for Context {} - impl __CanUseContext> for Context {} impl __CanUseContext, Index<6>)> for Context {} impl __CanUseContext, Index<8>)> for Context {} - impl __CanUseContext, Index<1>)> for Context {} - impl __CanUseContext, Index<0>)> for Context {} + impl __CanUseContext> for Context {} + impl __CanUseContext> for Context {} impl __CanUseContext, Index<6>)> for Context {} impl __CanUseContext, Index<8>)> for Context {} + impl __CanUseContext, Index<1>)> for Context {} + impl __CanUseContext, Index<0>)> for Context {} "#) } } diff --git a/crates/tests/cgp-tests/src/tests/use_delegate/getter.rs b/crates/tests/cgp-tests/src/tests/use_delegate/getter.rs index f40b5064..d40017b1 100644 --- a/crates/tests/cgp-tests/src/tests/use_delegate/getter.rs +++ b/crates/tests/cgp-tests/src/tests/use_delegate/getter.rs @@ -496,6 +496,8 @@ mod derive_delegate { expand_my_context(output) { insta::assert_snapshot!(output, @r#" + pub struct FooTypes; + pub struct FooGetters; impl DelegateComponent for MyContext { type Delegate = UseDelegate; } @@ -508,7 +510,18 @@ mod derive_delegate { FooTypes, >: IsProviderFor, {} - pub struct FooTypes; + impl DelegateComponent for MyContext { + type Delegate = UseDelegate; + } + impl< + __Context__, + __Params__, + > IsProviderFor for MyContext + where + UseDelegate< + FooGetters, + >: IsProviderFor, + {} impl DelegateComponent> for FooTypes { type Delegate = UseType; } @@ -525,19 +538,6 @@ mod derive_delegate { where UseType: IsProviderFor, __Context__, __Params__>, {} - impl DelegateComponent for MyContext { - type Delegate = UseDelegate; - } - impl< - __Context__, - __Params__, - > IsProviderFor for MyContext - where - UseDelegate< - FooGetters, - >: IsProviderFor, - {} - pub struct FooGetters; impl DelegateComponent> for FooGetters { type Delegate = UseField; } From 42557b954f67c9c3c24ce769804137fda0723bb4 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Tue, 23 Jun 2026 22:26:09 +0200 Subject: [PATCH 16/19] Add ValidateAttributes --- .../to_keys_with_check_params.rs | 6 +- .../src/types/delegate_component/mod.rs | 2 + .../delegate_component/validate_attributes.rs | 55 +++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 crates/macros/cgp-macro-core/src/types/delegate_component/validate_attributes.rs diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs index 961e7888..76fd1e86 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs @@ -1,6 +1,7 @@ use crate::types::delegate_and_check_components::{CheckParamsAttribute, KeyWithCheckParams}; use crate::types::delegate_component::{ DelegateEntries, DelegateKey, DelegateMapping, MultiDelegateKey, SingleDelegateKey, + ValidateAttributes, }; pub trait ToKeysWithCheckParams { @@ -46,7 +47,10 @@ impl ToKeysWithCheckParams for DelegateKey { match self { DelegateKey::Single(key) => key.to_keys_with_check_params(), DelegateKey::Multi(key) => key.to_keys_with_check_params(), - DelegateKey::Path(_key) => Ok(Vec::new()), + DelegateKey::Path(key) => { + key.validate_attributes()?; + Ok(Vec::new()) + } } } } diff --git a/crates/macros/cgp-macro-core/src/types/delegate_component/mod.rs b/crates/macros/cgp-macro-core/src/types/delegate_component/mod.rs index 30440c2c..939e8f20 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_component/mod.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_component/mod.rs @@ -3,6 +3,7 @@ mod key; mod mapping; mod statement; mod table; +mod validate_attributes; mod value; pub use entries::*; @@ -10,4 +11,5 @@ pub use key::*; pub use mapping::*; pub use statement::*; pub use table::*; +pub use validate_attributes::*; pub use value::*; diff --git a/crates/macros/cgp-macro-core/src/types/delegate_component/validate_attributes.rs b/crates/macros/cgp-macro-core/src/types/delegate_component/validate_attributes.rs new file mode 100644 index 00000000..01168187 --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/delegate_component/validate_attributes.rs @@ -0,0 +1,55 @@ +use quote::ToTokens; +use syn::spanned::Spanned; +use syn::{Attribute, Error}; + +use crate::types::delegate_component::{MultiDelegateKey, PathDelegateKey, SingleDelegateKey}; + +/** + Validate that the attributes in the delegate table constructs are valid. + + At the moment, no attribute is supported, so all attributes are rejected. +*/ +pub trait ValidateAttributes { + fn validate_attributes(&self) -> syn::Result<()>; +} + +pub fn reject_non_empty_attributes(attributes: &[Attribute]) -> syn::Result<()> { + if !attributes.is_empty() { + let attribute = &attributes[0]; + Err(Error::new( + attribute.span(), + format!( + "unsupported attribute: {}", + attribute.path().to_token_stream() + ), + )) + } else { + Ok(()) + } +} + +impl ValidateAttributes for SingleDelegateKey { + fn validate_attributes(&self) -> syn::Result<()> { + reject_non_empty_attributes(&self.attributes) + } +} + +impl ValidateAttributes for MultiDelegateKey { + fn validate_attributes(&self) -> syn::Result<()> { + reject_non_empty_attributes(&self.attributes)?; + + for key in &self.keys { + key.validate_attributes()?; + } + + Ok(()) + } +} + +impl ValidateAttributes for PathDelegateKey { + fn validate_attributes(&self) -> syn::Result<()> { + reject_non_empty_attributes(&self.attributes)?; + + Ok(()) + } +} From aa551d49279de987b06398dfc8a8272d5f12e80e Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Tue, 23 Jun 2026 22:35:35 +0200 Subject: [PATCH 17/19] Add more attributes validation --- .../to_keys_with_check_params.rs | 10 ++++++++-- .../delegate_component/validate_attributes.rs | 20 ++++++++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs index 76fd1e86..555757ed 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs @@ -59,8 +59,14 @@ impl ToKeysWithCheckParams for DelegateMapping { fn to_keys_with_check_params(&self) -> syn::Result> { match self { DelegateMapping::Normal(mapping) => mapping.key.to_keys_with_check_params(), - DelegateMapping::Direct(_mapping) => Ok(Vec::new()), - DelegateMapping::Redirect(_mapping) => Ok(Vec::new()), + DelegateMapping::Direct(mapping) => { + mapping.key.validate_attributes()?; + Ok(Vec::new()) + } + DelegateMapping::Redirect(mapping) => { + mapping.key.validate_attributes()?; + Ok(Vec::new()) + } } } } diff --git a/crates/macros/cgp-macro-core/src/types/delegate_component/validate_attributes.rs b/crates/macros/cgp-macro-core/src/types/delegate_component/validate_attributes.rs index 01168187..beb3c803 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_component/validate_attributes.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_component/validate_attributes.rs @@ -2,7 +2,9 @@ use quote::ToTokens; use syn::spanned::Spanned; use syn::{Attribute, Error}; -use crate::types::delegate_component::{MultiDelegateKey, PathDelegateKey, SingleDelegateKey}; +use crate::types::delegate_component::{ + DelegateKey, DelegateTable, MultiDelegateKey, PathDelegateKey, SingleDelegateKey, +}; /** Validate that the attributes in the delegate table constructs are valid. @@ -53,3 +55,19 @@ impl ValidateAttributes for PathDelegateKey { Ok(()) } } + +impl ValidateAttributes for DelegateKey { + fn validate_attributes(&self) -> syn::Result<()> { + match self { + DelegateKey::Single(key) => key.validate_attributes(), + DelegateKey::Multi(key) => key.validate_attributes(), + DelegateKey::Path(key) => key.validate_attributes(), + } + } +} + +impl ValidateAttributes for DelegateTable { + fn validate_attributes(&self) -> syn::Result<()> { + reject_non_empty_attributes(&self.attributes) + } +} From 55c00f86c844d6b7ef55ba54142c95f985bac448 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Tue, 23 Jun 2026 23:09:13 +0200 Subject: [PATCH 18/19] AI-revise code --- .../src/types/check_components/table.rs | 19 ++++++++---- .../check_params.rs | 26 +++++++++------- .../delegate_and_check_components/item.rs | 14 +++------ .../to_keys_with_check_params.rs | 10 ++++--- .../delegate_component/validate_attributes.rs | 30 +++++++++++++++++-- .../src/entrypoints/delegate_components.rs | 6 +++- 6 files changed, 72 insertions(+), 33 deletions(-) diff --git a/crates/macros/cgp-macro-core/src/types/check_components/table.rs b/crates/macros/cgp-macro-core/src/types/check_components/table.rs index 40120c19..a8dad6d4 100644 --- a/crates/macros/cgp-macro-core/src/types/check_components/table.rs +++ b/crates/macros/cgp-macro-core/src/types/check_components/table.rs @@ -141,12 +141,7 @@ impl Parse for CheckComponentsTable { let trait_name = if let Some(check_trait_name) = m_check_trait_name { check_trait_name } else { - let context_type: IdentWithTypeArgs = parse2(context_type.to_token_stream())?; - - Ident::new( - &format!("__Check{}", context_type.ident), - context_type.span(), - ) + derive_check_trait_ident(&context_type, "__Check")? }; let where_clause = if input.peek(Where) { @@ -171,6 +166,18 @@ impl Parse for CheckComponentsTable { } } +/// Derive a check trait identifier from a context type by prepending `prefix` +/// to the context type's leading identifier, e.g. `__CheckPerson` or +/// `__CanUsePerson` for the context type `Person`. +pub fn derive_check_trait_ident(context_type: &Type, prefix: &str) -> syn::Result { + let context_type: IdentWithTypeArgs = parse2(context_type.to_token_stream())?; + + Ok(Ident::new( + &format!("{prefix}{}", context_type.ident), + context_type.span(), + )) +} + fn override_span(span: &Span, body: &T) -> syn::Result where T: Parse + ToTokens, diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/check_params.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/check_params.rs index eaf9526e..cd7b6d52 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/check_params.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/check_params.rs @@ -19,16 +19,10 @@ impl CheckParamsAttribute { (Self::Multi(params_a), Self::Multi(params_b)) => Self::Multi(Punctuated::from_iter( params_a.iter().chain(params_b.iter()).cloned(), )), - (Self::Skip, Self::Multi(params)) => { + (Self::Skip, Self::Multi(params)) | (Self::Multi(params), Self::Skip) => { return Err(Error::new( params.span(), - "cannot combine #[skip] with #[check_params]", - )); - } - (Self::Multi(params), Self::Skip) => { - return Err(Error::new( - params.span(), - "cannot combine #[skip] with #[check_params]", + "cannot combine #[skip_check] with #[check_params]", )); } }; @@ -41,17 +35,29 @@ impl CheckParamsAttribute { return Ok(Self::Default); } + if attributes.len() > 1 { + return Err(Error::new( + attributes[1].span(), + "Expected at most one `#[check_params]` or `#[skip_check]` attribute", + )); + } + let attribute = &attributes[0]; if attribute.path().is_ident("check_params") { let params = attribute.parse_args_with(Punctuated::parse_terminated)?; Ok(CheckParamsAttribute::Multi(params)) } else if attribute.path().is_ident("skip_check") { - // TODO: validate that the attribute args are empty + attribute.meta.require_path_only().map_err(|_| { + Error::new( + attribute.span(), + "`#[skip_check]` does not take any arguments", + ) + })?; Ok(CheckParamsAttribute::Skip) } else { - Err(syn::Error::new( + Err(Error::new( attribute.span(), "Expected either `#[skip_check]` or `#[check_params]` attribute for specifying the check generics", )) diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/item.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/item.rs index 80482931..1716338a 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/item.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/item.rs @@ -2,11 +2,11 @@ use syn::parse::{Parse, ParseStream}; use syn::spanned::Spanned; use syn::{Error, Ident}; -use crate::parse_internal; -use crate::types::check_components::{CheckComponentsTable, CheckEntries}; +use crate::types::check_components::{ + CheckComponentsTable, CheckEntries, derive_check_trait_ident, +}; use crate::types::delegate_and_check_components::ToKeysWithCheckParams; use crate::types::delegate_component::DelegateTable; -use crate::types::ident::IdentWithTypeArgs; pub struct ItemDelegateAndCheckComponents { pub table: DelegateTable, @@ -45,13 +45,7 @@ impl ItemDelegateAndCheckComponents { let attributes = &self.table.attributes; if attributes.is_empty() { - let context_type = &self.table.table_type; - let context_type: IdentWithTypeArgs = parse_internal!(#context_type); - - Ok(Ident::new( - &format!("__CanUse{}", context_type.ident), - context_type.span(), - )) + derive_check_trait_ident(&self.table.table_type, "__CanUse") } else if attributes.len() > 1 { Err(Error::new( attributes[1].span(), diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs index 555757ed..4c832873 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs @@ -59,11 +59,10 @@ impl ToKeysWithCheckParams for DelegateMapping { fn to_keys_with_check_params(&self) -> syn::Result> { match self { DelegateMapping::Normal(mapping) => mapping.key.to_keys_with_check_params(), - DelegateMapping::Direct(mapping) => { - mapping.key.validate_attributes()?; - Ok(Vec::new()) - } + DelegateMapping::Direct(mapping) => mapping.key.to_keys_with_check_params(), DelegateMapping::Redirect(mapping) => { + // Redirect mappings do not support check params yet, so reject any + // attribute on the key rather than silently ignoring it. mapping.key.validate_attributes()?; Ok(Vec::new()) } @@ -75,6 +74,9 @@ impl ToKeysWithCheckParams for DelegateEntries { fn to_keys_with_check_params(&self) -> syn::Result> { let mut out = Vec::new(); + // Note: statement forms (`for`/`namespace`/`open`) are intentionally not + // checked for now. They still produce delegate impls via `eval`, but no + // check entries are generated for them. for entry in &self.entries { out.extend(entry.to_keys_with_check_params()?); } diff --git a/crates/macros/cgp-macro-core/src/types/delegate_component/validate_attributes.rs b/crates/macros/cgp-macro-core/src/types/delegate_component/validate_attributes.rs index beb3c803..5c7b8860 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_component/validate_attributes.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_component/validate_attributes.rs @@ -3,7 +3,8 @@ use syn::spanned::Spanned; use syn::{Attribute, Error}; use crate::types::delegate_component::{ - DelegateKey, DelegateTable, MultiDelegateKey, PathDelegateKey, SingleDelegateKey, + DelegateEntries, DelegateKey, DelegateMapping, DelegateTable, MultiDelegateKey, + PathDelegateKey, SingleDelegateKey, }; /** @@ -66,8 +67,33 @@ impl ValidateAttributes for DelegateKey { } } +impl ValidateAttributes for DelegateMapping { + fn validate_attributes(&self) -> syn::Result<()> { + match self { + DelegateMapping::Normal(mapping) => mapping.key.validate_attributes(), + DelegateMapping::Direct(mapping) => mapping.key.validate_attributes(), + DelegateMapping::Redirect(mapping) => mapping.key.validate_attributes(), + } + } +} + +impl ValidateAttributes for DelegateEntries { + fn validate_attributes(&self) -> syn::Result<()> { + // Note: keys nested inside statement forms (`for`/`namespace`/`open`) are + // not validated here, matching the scope of the check-components handling. + for entry in &self.entries { + entry.validate_attributes()?; + } + + Ok(()) + } +} + impl ValidateAttributes for DelegateTable { fn validate_attributes(&self) -> syn::Result<()> { - reject_non_empty_attributes(&self.attributes) + reject_non_empty_attributes(&self.attributes)?; + self.entries.validate_attributes()?; + + Ok(()) } } diff --git a/crates/macros/cgp-macro-lib/src/entrypoints/delegate_components.rs b/crates/macros/cgp-macro-lib/src/entrypoints/delegate_components.rs index ba10cd0e..43dffa5a 100644 --- a/crates/macros/cgp-macro-lib/src/entrypoints/delegate_components.rs +++ b/crates/macros/cgp-macro-lib/src/entrypoints/delegate_components.rs @@ -1,4 +1,4 @@ -use cgp_macro_core::types::delegate_component::DelegateTable; +use cgp_macro_core::types::delegate_component::{DelegateTable, ValidateAttributes}; use proc_macro2::TokenStream; use quote::ToTokens; use syn::parse2; @@ -6,6 +6,10 @@ use syn::parse2; pub fn delegate_components(body: TokenStream) -> syn::Result { let table: DelegateTable = parse2(body.clone())?; + // `delegate_components!` does not support any attributes on the table or its + // keys, so reject them instead of silently parsing and discarding them. + table.validate_attributes()?; + let evaluated_table = table.eval()?; Ok(evaluated_table.to_token_stream()) From f0a83515ad6c4026c3a4c7432fb6e341b3451dc5 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Tue, 23 Jun 2026 23:36:35 +0200 Subject: [PATCH 19/19] AI revision --- .../to_keys_with_check_params.rs | 16 ++++++++-- .../delegate_component/validate_attributes.rs | 32 ++++++++++++++++--- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs index 4c832873..983ebf42 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_and_check_components/to_keys_with_check_params.rs @@ -12,6 +12,11 @@ impl ToKeysWithCheckParams for SingleDelegateKey { fn to_keys_with_check_params(&self) -> syn::Result> { let check_params = CheckParamsAttribute::parse_attributes(&self.attributes)?; + // Note: any per-key `ImplGenerics` (`self.generics`) are not carried into + // the check entry. The generated check impl only sees the table-level + // generics, so a key that introduces its own generic parameters would + // reference them unbound. Generic keys are therefore not yet supported in + // the check half; use `#[skip_check]` for such keys if needed. let key = KeyWithCheckParams { check_params, key_type: self.ty.clone(), @@ -74,9 +79,14 @@ impl ToKeysWithCheckParams for DelegateEntries { fn to_keys_with_check_params(&self) -> syn::Result> { let mut out = Vec::new(); - // Note: statement forms (`for`/`namespace`/`open`) are intentionally not - // checked for now. They still produce delegate impls via `eval`, but no - // check entries are generated for them. + // Statement forms (`for`/`namespace`/`open`) are intentionally not checked + // for now. They still produce delegate impls via `eval`, but no check + // entries are generated for them. Since they cannot carry check params, + // reject any attribute on their keys rather than silently ignoring it. + for statement in &self.statements { + statement.validate_attributes()?; + } + for entry in &self.entries { out.extend(entry.to_keys_with_check_params()?); } diff --git a/crates/macros/cgp-macro-core/src/types/delegate_component/validate_attributes.rs b/crates/macros/cgp-macro-core/src/types/delegate_component/validate_attributes.rs index 5c7b8860..2cd071ba 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_component/validate_attributes.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_component/validate_attributes.rs @@ -3,8 +3,8 @@ use syn::spanned::Spanned; use syn::{Attribute, Error}; use crate::types::delegate_component::{ - DelegateEntries, DelegateKey, DelegateMapping, DelegateTable, MultiDelegateKey, - PathDelegateKey, SingleDelegateKey, + DelegateEntries, DelegateKey, DelegateMapping, DelegateStatement, DelegateTable, + ForDelegateStatement, MultiDelegateKey, PathDelegateKey, SingleDelegateKey, }; /** @@ -77,10 +77,34 @@ impl ValidateAttributes for DelegateMapping { } } +impl ValidateAttributes for ForDelegateStatement { + fn validate_attributes(&self) -> syn::Result<()> { + for mapping in &self.mappings { + mapping.key.validate_attributes()?; + } + + Ok(()) + } +} + +impl ValidateAttributes for DelegateStatement { + fn validate_attributes(&self) -> syn::Result<()> { + match self { + // `namespace` and `open` statements carry no keys that can hold attributes. + DelegateStatement::Namespace(_) | DelegateStatement::Open(_) => Ok(()), + DelegateStatement::For(statement) => statement.validate_attributes(), + } + } +} + impl ValidateAttributes for DelegateEntries { fn validate_attributes(&self) -> syn::Result<()> { - // Note: keys nested inside statement forms (`for`/`namespace`/`open`) are - // not validated here, matching the scope of the check-components handling. + // Keys nested inside statement forms (`for`/`namespace`/`open`) do not + // support attributes, so reject any rather than silently discarding them. + for statement in &self.statements { + statement.validate_attributes()?; + } + for entry in &self.entries { entry.validate_attributes()?; }