From ef8af0f9a9a34c77029752bd8e54cb2491b887fe Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 21 Jun 2026 22:10:11 +0200 Subject: [PATCH 1/8] Move check_components types --- .../types/check_components/check_entries.rs | 99 ++++++++ .../src/types/check_components/check_entry.rs | 11 + .../src/types/check_components/mod.rs | 11 + .../src/types/check_components/table.rs | 100 ++++++++ .../src/types/check_components/tables.rs | 20 ++ .../check_components/type_with_generics.rs | 24 ++ crates/macros/cgp-macro-core/src/types/mod.rs | 1 + .../src/check_components/derive.rs | 8 +- .../src/entrypoints/check_components.rs | 4 +- .../delegate_and_check_components.rs | 7 +- .../src/parse/check_components.rs | 232 ------------------ crates/macros/cgp-macro-lib/src/parse/mod.rs | 2 - 12 files changed, 276 insertions(+), 243 deletions(-) create mode 100644 crates/macros/cgp-macro-core/src/types/check_components/check_entries.rs create mode 100644 crates/macros/cgp-macro-core/src/types/check_components/check_entry.rs create mode 100644 crates/macros/cgp-macro-core/src/types/check_components/mod.rs create mode 100644 crates/macros/cgp-macro-core/src/types/check_components/table.rs create mode 100644 crates/macros/cgp-macro-core/src/types/check_components/tables.rs create mode 100644 crates/macros/cgp-macro-core/src/types/check_components/type_with_generics.rs delete mode 100644 crates/macros/cgp-macro-lib/src/parse/check_components.rs diff --git a/crates/macros/cgp-macro-core/src/types/check_components/check_entries.rs b/crates/macros/cgp-macro-core/src/types/check_components/check_entries.rs new file mode 100644 index 00000000..05aefad4 --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/check_components/check_entries.rs @@ -0,0 +1,99 @@ +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; +use syn::spanned::Spanned; +use syn::token::{Bracket, Colon, Comma}; +use syn::{Type, bracketed}; + +use crate::types::check_components::{CheckEntry, TypeWithGenerics}; +use crate::types::generics::ImplGenerics; + +pub struct CheckEntries { + pub entries: Vec, +} + +struct ParseCheckEntries { + pub entries: Vec, +} + +impl Parse for CheckEntries { + fn parse(input: ParseStream) -> syn::Result { + let check_entries: Punctuated = + Punctuated::parse_terminated(input)?; + + let entries = check_entries + .into_iter() + .flat_map(|check_entry| check_entry.entries.into_iter()) + .collect(); + + Ok(Self { entries }) + } +} + +impl Parse for ParseCheckEntries { + fn parse(input: ParseStream) -> syn::Result { + let component_types: Vec = if input.peek(Bracket) { + let content; + bracketed!(content in input); + + let types: Punctuated = Punctuated::parse_terminated(&content)?; + Vec::from_iter(types) + } else { + let component_type: Type = input.parse()?; + vec![component_type] + }; + + let component_params: Vec = if input.peek(Colon) { + let _: Colon = input.parse()?; + + if input.peek(Bracket) { + let content; + bracketed!(content in input); + + let types: Punctuated = + Punctuated::parse_terminated(&content)?; + types.into_iter().collect() + } else { + vec![input.parse()?] + } + } else { + vec![] + }; + + let mut entries = Vec::new(); + + let component_types_count = component_types.len(); + + for component_type in component_types.iter() { + if component_params.is_empty() { + entries.push(CheckEntry { + component_type: component_type.clone(), + component_params: None, + span: component_type.span(), + generics: ImplGenerics::default(), + }) + } else { + let component_params_count = component_params.len(); + + for component_param in component_params.iter() { + let component_param_type = &component_param.ty; + let component_param_generics = &component_param.generics; + + let span = if component_types_count >= component_params_count { + component_type.span() + } else { + component_param_type.span() + }; + + entries.push(CheckEntry { + component_type: component_type.clone(), + component_params: Some(component_param_type.clone()), + span, + generics: component_param_generics.clone(), + }) + } + } + } + + Ok(Self { entries }) + } +} diff --git a/crates/macros/cgp-macro-core/src/types/check_components/check_entry.rs b/crates/macros/cgp-macro-core/src/types/check_components/check_entry.rs new file mode 100644 index 00000000..d91c0805 --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/check_components/check_entry.rs @@ -0,0 +1,11 @@ +use proc_macro2::Span; +use syn::Type; + +use crate::types::generics::ImplGenerics; + +pub struct CheckEntry { + pub component_type: Type, + pub component_params: Option, + pub span: Span, + pub generics: ImplGenerics, +} diff --git a/crates/macros/cgp-macro-core/src/types/check_components/mod.rs b/crates/macros/cgp-macro-core/src/types/check_components/mod.rs new file mode 100644 index 00000000..8d888565 --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/check_components/mod.rs @@ -0,0 +1,11 @@ +mod check_entries; +mod check_entry; +mod table; +mod tables; +mod type_with_generics; + +pub use check_entries::*; +pub use check_entry::*; +pub use table::*; +pub use tables::*; +pub use type_with_generics::*; 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 new file mode 100644 index 00000000..2d961938 --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/check_components/table.rs @@ -0,0 +1,100 @@ +use proc_macro2::Span; +use quote::ToTokens; +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; +use syn::spanned::Spanned; +use syn::token::{Comma, Lt, Pound, Where}; +use syn::{Attribute, Ident, Type, WhereClause, braced, parse2}; + +use crate::types::check_components::CheckEntries; +use crate::types::generics::ImplGenerics; +use crate::types::ident::IdentWithTypeArgs; + +pub struct CheckComponentsTable { + pub check_providers: Option>, + pub impl_generics: ImplGenerics, + pub trait_name: Ident, + pub context_type: Type, + pub where_clause: WhereClause, + pub check_entries: CheckEntries, +} + +impl Parse for CheckComponentsTable { + fn parse(input: ParseStream) -> syn::Result { + let mut check_providers: Option> = None; + let mut m_check_trait_name: Option = None; + + if input.peek(Pound) { + let attributes = input.call(Attribute::parse_outer)?; + + for attribute in attributes { + if attribute.path().is_ident("check_providers") { + let provider_types: Punctuated = + attribute.parse_args_with(Punctuated::parse_terminated)?; + + check_providers + .get_or_insert_default() + .extend(provider_types); + } else if attribute.path().is_ident("check_trait") { + let check_trait_name: Ident = attribute.parse_args()?; + + if m_check_trait_name.is_some() { + return Err(syn::Error::new( + attribute.span(), + "Multiple `#[check_trait]` attributes found. Expected at most one.", + )); + } + + m_check_trait_name = Some(check_trait_name); + } else { + return Err(syn::Error::new( + attribute.span(), + format!("Invalid attribute {}", attribute.to_token_stream()), + )); + } + } + }; + + let impl_generics = if input.peek(Lt) { + input.parse()? + } else { + Default::default() + }; + + let context_type: Type = input.parse()?; + + 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(), + ) + }; + + let where_clause = if input.peek(Where) { + input.parse()? + } else { + WhereClause { + where_token: Where(Span::call_site()), + predicates: Punctuated::default(), + } + }; + + let content; + braced!(content in input); + + let entries: CheckEntries = content.parse()?; + + Ok(Self { + check_providers, + impl_generics, + trait_name, + context_type, + where_clause, + check_entries: entries, + }) + } +} diff --git a/crates/macros/cgp-macro-core/src/types/check_components/tables.rs b/crates/macros/cgp-macro-core/src/types/check_components/tables.rs new file mode 100644 index 00000000..6bafc659 --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/check_components/tables.rs @@ -0,0 +1,20 @@ +use syn::parse::{Parse, ParseStream}; + +use crate::types::check_components::CheckComponentsTable; + +pub struct CheckComponentsTables { + pub specs: Vec, +} + +impl Parse for CheckComponentsTables { + fn parse(input: ParseStream) -> syn::Result { + let mut specs = Vec::new(); + + while !input.is_empty() { + let spec: CheckComponentsTable = input.parse()?; + specs.push(spec); + } + + Ok(Self { specs }) + } +} diff --git a/crates/macros/cgp-macro-core/src/types/check_components/type_with_generics.rs b/crates/macros/cgp-macro-core/src/types/check_components/type_with_generics.rs new file mode 100644 index 00000000..6ab58c11 --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/check_components/type_with_generics.rs @@ -0,0 +1,24 @@ +use syn::Type; +use syn::parse::{Parse, ParseStream}; +use syn::token::Lt; + +use crate::types::generics::ImplGenerics; + +pub struct TypeWithGenerics { + pub ty: Type, + pub generics: ImplGenerics, +} + +impl Parse for TypeWithGenerics { + fn parse(input: ParseStream) -> syn::Result { + let generics = if input.peek(Lt) { + input.parse()? + } else { + ImplGenerics::default() + }; + + let ty = input.parse()?; + + Ok(Self { ty, generics }) + } +} diff --git a/crates/macros/cgp-macro-core/src/types/mod.rs b/crates/macros/cgp-macro-core/src/types/mod.rs index e54d1834..83876489 100644 --- a/crates/macros/cgp-macro-core/src/types/mod.rs +++ b/crates/macros/cgp-macro-core/src/types/mod.rs @@ -6,6 +6,7 @@ pub mod cgp_fn; pub mod cgp_getter; pub mod cgp_impl; pub mod cgp_provider; +pub mod check_components; pub mod delegate_component; pub mod empty_struct; pub mod field; diff --git a/crates/macros/cgp-macro-lib/src/check_components/derive.rs b/crates/macros/cgp-macro-lib/src/check_components/derive.rs index 30cbe64d..032a942e 100644 --- a/crates/macros/cgp-macro-lib/src/check_components/derive.rs +++ b/crates/macros/cgp-macro-lib/src/check_components/derive.rs @@ -1,13 +1,15 @@ use cgp_macro_core::functions::merge_generics; +use cgp_macro_core::types::check_components::{CheckComponentsTable, CheckEntry}; use quote::quote; use syn::punctuated::Punctuated; use syn::token::Comma; use syn::{ItemImpl, ItemTrait, Type, parse2}; use crate::check_components::override_span; -use crate::parse::{CheckComponents, CheckEntry}; -pub fn derive_check_components(spec: &CheckComponents) -> syn::Result<(ItemTrait, Vec)> { +pub fn derive_check_components( + spec: &CheckComponentsTable, +) -> syn::Result<(ItemTrait, Vec)> { if let Some(check_providers) = &spec.check_providers { return derive_check_provider(spec, check_providers); } @@ -54,7 +56,7 @@ pub fn derive_check_components(spec: &CheckComponents) -> syn::Result<(ItemTrait } pub fn derive_check_provider( - spec: &CheckComponents, + spec: &CheckComponentsTable, providers: &Punctuated, ) -> syn::Result<(ItemTrait, Vec)> { let mut item_impls = Vec::new(); diff --git a/crates/macros/cgp-macro-lib/src/entrypoints/check_components.rs b/crates/macros/cgp-macro-lib/src/entrypoints/check_components.rs index 78afaeba..55d68819 100644 --- a/crates/macros/cgp-macro-lib/src/entrypoints/check_components.rs +++ b/crates/macros/cgp-macro-lib/src/entrypoints/check_components.rs @@ -1,12 +1,12 @@ +use cgp_macro_core::types::check_components::CheckComponentsTables; use proc_macro2::TokenStream; use quote::{ToTokens, TokenStreamExt}; use syn::parse2; use crate::check_components::derive_check_components; -use crate::parse::CheckComponentsSpecs; pub fn check_components(body: TokenStream) -> syn::Result { - let spec: CheckComponentsSpecs = parse2(body)?; + let spec: CheckComponentsTables = parse2(body)?; let mut out = TokenStream::new(); 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 4763b9f9..7cdd3271 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,3 +1,4 @@ +use cgp_macro_core::types::check_components::{CheckComponentsTable, CheckEntries, CheckEntry}; use cgp_macro_core::types::generics::ImplGenerics; use proc_macro2::{Span, TokenStream}; use quote::{ToTokens, TokenStreamExt}; @@ -8,9 +9,7 @@ use syn::{Type, WhereClause, parse2}; use crate::check_components::derive_check_components; use crate::delegate_components::impl_delegate_components; -use crate::parse::{ - CheckComponents, CheckEntries, CheckEntry, DelegateAndCheckSpec, DelegateEntry, DelegateKey, -}; +use crate::parse::{DelegateAndCheckSpec, DelegateEntry, DelegateKey}; pub fn delegate_and_check_components(body: TokenStream) -> syn::Result { let spec: DelegateAndCheckSpec = parse2(body)?; @@ -68,7 +67,7 @@ pub fn delegate_and_check_components(body: TokenStream) -> syn::Result, -} - -pub struct CheckComponents { - pub check_providers: Option>, - pub impl_generics: ImplGenerics, - pub trait_name: Ident, - pub context_type: Type, - pub where_clause: WhereClause, - pub check_entries: CheckEntries, -} - -pub struct CheckEntries { - pub entries: Vec, -} - -pub struct CheckEntry { - pub component_type: Type, - pub component_params: Option, - pub span: Span, - pub generics: ImplGenerics, -} - -struct ParseCheckEntries { - pub entries: Vec, -} - -impl Parse for CheckComponentsSpecs { - fn parse(input: ParseStream) -> syn::Result { - let mut specs = Vec::new(); - - while !input.is_empty() { - let spec: CheckComponents = input.parse()?; - specs.push(spec); - } - - Ok(Self { specs }) - } -} - -impl Parse for CheckComponents { - fn parse(input: ParseStream) -> syn::Result { - let mut check_providers: Option> = None; - let mut m_check_trait_name: Option = None; - - if input.peek(Pound) { - let attributes = input.call(Attribute::parse_outer)?; - - for attribute in attributes { - if attribute.path().is_ident("check_providers") { - let provider_types: Punctuated = - attribute.parse_args_with(Punctuated::parse_terminated)?; - - check_providers - .get_or_insert_default() - .extend(provider_types); - } else if attribute.path().is_ident("check_trait") { - let check_trait_name: Ident = attribute.parse_args()?; - - if m_check_trait_name.is_some() { - return Err(syn::Error::new( - attribute.span(), - "Multiple `#[check_trait]` attributes found. Expected at most one.", - )); - } - - m_check_trait_name = Some(check_trait_name); - } else { - return Err(syn::Error::new( - attribute.span(), - format!("Invalid attribute {}", attribute.to_token_stream()), - )); - } - } - }; - - let impl_generics = if input.peek(Lt) { - input.parse()? - } else { - Default::default() - }; - - let context_type: Type = input.parse()?; - - 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(), - ) - }; - - let where_clause = if input.peek(Where) { - input.parse()? - } else { - WhereClause { - where_token: Where(Span::call_site()), - predicates: Punctuated::default(), - } - }; - - let content; - braced!(content in input); - - let entries: CheckEntries = content.parse()?; - - Ok(Self { - check_providers, - impl_generics, - trait_name, - context_type, - where_clause, - check_entries: entries, - }) - } -} - -impl Parse for CheckEntries { - fn parse(input: ParseStream) -> syn::Result { - let check_entries: Punctuated = - Punctuated::parse_terminated(input)?; - - let entries = check_entries - .into_iter() - .flat_map(|check_entry| check_entry.entries.into_iter()) - .collect(); - - Ok(Self { entries }) - } -} - -impl Parse for ParseCheckEntries { - fn parse(input: ParseStream) -> syn::Result { - let component_types: Vec = if input.peek(Bracket) { - let content; - bracketed!(content in input); - - let types: Punctuated = Punctuated::parse_terminated(&content)?; - Vec::from_iter(types) - } else { - let component_type: Type = input.parse()?; - vec![component_type] - }; - - let component_params: Vec = if input.peek(Colon) { - let _: Colon = input.parse()?; - - if input.peek(Bracket) { - let content; - bracketed!(content in input); - - let types: Punctuated = - Punctuated::parse_terminated(&content)?; - types.into_iter().collect() - } else { - vec![input.parse()?] - } - } else { - vec![] - }; - - let mut entries = Vec::new(); - - let component_types_count = component_types.len(); - - for component_type in component_types.iter() { - if component_params.is_empty() { - entries.push(CheckEntry { - component_type: component_type.clone(), - component_params: None, - span: component_type.span(), - generics: ImplGenerics::default(), - }) - } else { - let component_params_count = component_params.len(); - - for component_param in component_params.iter() { - let component_param_type = &component_param.ty; - let component_param_generics = &component_param.generics; - - let span = if component_types_count >= component_params_count { - component_type.span() - } else { - component_param_type.span() - }; - - entries.push(CheckEntry { - component_type: component_type.clone(), - component_params: Some(component_param_type.clone()), - span, - generics: component_param_generics.clone(), - }) - } - } - } - - Ok(Self { entries }) - } -} - -pub struct TypeWithGenerics { - pub ty: Type, - pub generics: ImplGenerics, -} - -impl Parse for TypeWithGenerics { - fn parse(input: ParseStream) -> syn::Result { - let generics = if input.peek(Lt) { - input.parse()? - } else { - ImplGenerics::default() - }; - - let ty = input.parse()?; - - Ok(Self { ty, generics }) - } -} diff --git a/crates/macros/cgp-macro-lib/src/parse/mod.rs b/crates/macros/cgp-macro-lib/src/parse/mod.rs index 78d5bfce..a2690f75 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 check_components; mod define_preset; mod delegate_and_check_components; mod delegate_components; mod path; mod type_spec; -pub use check_components::*; pub use define_preset::*; pub use delegate_and_check_components::*; pub use delegate_components::*; From bb7d4d56cfcb0b6eb4cf494abfc56a21c7309eb2 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 21 Jun 2026 22:25:23 +0200 Subject: [PATCH 2/8] Draft implement new CheckEntry --- .../types/check_components/check_entries.rs | 11 ++-- .../src/types/check_components/entry.rs | 66 +++++++++++++++++++ ...heck_entry.rs => evaluated_check_entry.rs} | 2 +- .../src/types/check_components/key.rs | 33 ++++++++++ .../src/types/check_components/mod.rs | 10 ++- .../check_components/type_with_generics.rs | 1 + .../src/types/check_components/value.rs | 38 +++++++++++ .../src/check_components/derive.rs | 6 +- .../delegate_and_check_components.rs | 10 +-- 9 files changed, 162 insertions(+), 15 deletions(-) create mode 100644 crates/macros/cgp-macro-core/src/types/check_components/entry.rs rename crates/macros/cgp-macro-core/src/types/check_components/{check_entry.rs => evaluated_check_entry.rs} (86%) create mode 100644 crates/macros/cgp-macro-core/src/types/check_components/key.rs create mode 100644 crates/macros/cgp-macro-core/src/types/check_components/value.rs diff --git a/crates/macros/cgp-macro-core/src/types/check_components/check_entries.rs b/crates/macros/cgp-macro-core/src/types/check_components/check_entries.rs index 05aefad4..24734159 100644 --- a/crates/macros/cgp-macro-core/src/types/check_components/check_entries.rs +++ b/crates/macros/cgp-macro-core/src/types/check_components/check_entries.rs @@ -4,15 +4,15 @@ use syn::spanned::Spanned; use syn::token::{Bracket, Colon, Comma}; use syn::{Type, bracketed}; -use crate::types::check_components::{CheckEntry, TypeWithGenerics}; +use crate::types::check_components::{EvaluatedCheckEntry, TypeWithGenerics}; use crate::types::generics::ImplGenerics; pub struct CheckEntries { - pub entries: Vec, + pub entries: Vec, } struct ParseCheckEntries { - pub entries: Vec, + pub entries: Vec, } impl Parse for CheckEntries { @@ -51,6 +51,7 @@ impl Parse for ParseCheckEntries { let types: Punctuated = Punctuated::parse_terminated(&content)?; + types.into_iter().collect() } else { vec![input.parse()?] @@ -65,7 +66,7 @@ impl Parse for ParseCheckEntries { for component_type in component_types.iter() { if component_params.is_empty() { - entries.push(CheckEntry { + entries.push(EvaluatedCheckEntry { component_type: component_type.clone(), component_params: None, span: component_type.span(), @@ -84,7 +85,7 @@ impl Parse for ParseCheckEntries { component_param_type.span() }; - entries.push(CheckEntry { + entries.push(EvaluatedCheckEntry { component_type: component_type.clone(), component_params: Some(component_param_type.clone()), span, diff --git a/crates/macros/cgp-macro-core/src/types/check_components/entry.rs b/crates/macros/cgp-macro-core/src/types/check_components/entry.rs new file mode 100644 index 00000000..66ff18a5 --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/check_components/entry.rs @@ -0,0 +1,66 @@ +use syn::parse::{Parse, ParseStream}; +use syn::spanned::Spanned; +use syn::token::Colon; + +use crate::types::check_components::{CheckKey, CheckValue, EvaluatedCheckEntry}; +use crate::types::generics::ImplGenerics; + +pub struct CheckEntry { + pub key: CheckKey, + pub colon: Colon, + pub value: CheckValue, +} + +impl CheckEntry { + pub fn eval(&self) -> Vec { + let mut entries = Vec::new(); + + let keys = self.key.to_keys(); + let values = self.value.to_values(); + + let component_types_count = keys.len(); + + for component_type in keys.iter() { + if values.is_empty() { + entries.push(EvaluatedCheckEntry { + component_type: component_type.clone(), + component_params: None, + span: component_type.span(), + generics: ImplGenerics::default(), + }) + } else { + let component_params_count = values.len(); + + for component_param in values.iter() { + let component_param_type = &component_param.ty; + let component_param_generics = &component_param.generics; + + let span = if component_types_count >= component_params_count { + component_type.span() + } else { + component_param_type.span() + }; + + entries.push(EvaluatedCheckEntry { + component_type: component_type.clone(), + component_params: Some(component_param_type.clone()), + span, + generics: component_param_generics.clone(), + }) + } + } + } + + todo!() + } +} + +impl Parse for CheckEntry { + fn parse(input: ParseStream) -> syn::Result { + let key = input.parse()?; + let colon = input.parse()?; + let value = input.parse()?; + + Ok(Self { key, colon, value }) + } +} diff --git a/crates/macros/cgp-macro-core/src/types/check_components/check_entry.rs b/crates/macros/cgp-macro-core/src/types/check_components/evaluated_check_entry.rs similarity index 86% rename from crates/macros/cgp-macro-core/src/types/check_components/check_entry.rs rename to crates/macros/cgp-macro-core/src/types/check_components/evaluated_check_entry.rs index d91c0805..0407c25d 100644 --- a/crates/macros/cgp-macro-core/src/types/check_components/check_entry.rs +++ b/crates/macros/cgp-macro-core/src/types/check_components/evaluated_check_entry.rs @@ -3,7 +3,7 @@ use syn::Type; use crate::types::generics::ImplGenerics; -pub struct CheckEntry { +pub struct EvaluatedCheckEntry { pub component_type: Type, pub component_params: Option, pub span: Span, diff --git a/crates/macros/cgp-macro-core/src/types/check_components/key.rs b/crates/macros/cgp-macro-core/src/types/check_components/key.rs new file mode 100644 index 00000000..54973a73 --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/check_components/key.rs @@ -0,0 +1,33 @@ +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; +use syn::token::{Bracket, Comma}; +use syn::{Type, bracketed}; + +pub enum CheckKey { + Single(Type), + Multi(Punctuated), +} + +impl CheckKey { + pub fn to_keys(&self) -> Vec { + match self { + Self::Single(key) => vec![key.clone()], + Self::Multi(keys) => Vec::from_iter(keys.iter().cloned()), + } + } +} + +impl Parse for CheckKey { + fn parse(input: ParseStream) -> syn::Result { + if input.peek(Bracket) { + let content; + bracketed!(content in input); + + let keys: Punctuated = Punctuated::parse_terminated(&content)?; + Ok(Self::Multi(keys)) + } else { + let key: Type = input.parse()?; + Ok(Self::Single(key)) + } + } +} diff --git a/crates/macros/cgp-macro-core/src/types/check_components/mod.rs b/crates/macros/cgp-macro-core/src/types/check_components/mod.rs index 8d888565..7f4319ad 100644 --- a/crates/macros/cgp-macro-core/src/types/check_components/mod.rs +++ b/crates/macros/cgp-macro-core/src/types/check_components/mod.rs @@ -1,11 +1,17 @@ mod check_entries; -mod check_entry; +mod entry; +mod evaluated_check_entry; +mod key; mod table; mod tables; mod type_with_generics; +mod value; pub use check_entries::*; -pub use check_entry::*; +pub use entry::*; +pub use evaluated_check_entry::*; +pub use key::*; pub use table::*; pub use tables::*; pub use type_with_generics::*; +pub use value::*; diff --git a/crates/macros/cgp-macro-core/src/types/check_components/type_with_generics.rs b/crates/macros/cgp-macro-core/src/types/check_components/type_with_generics.rs index 6ab58c11..bbe8e477 100644 --- a/crates/macros/cgp-macro-core/src/types/check_components/type_with_generics.rs +++ b/crates/macros/cgp-macro-core/src/types/check_components/type_with_generics.rs @@ -4,6 +4,7 @@ use syn::token::Lt; use crate::types::generics::ImplGenerics; +#[derive(Clone)] pub struct TypeWithGenerics { pub ty: Type, pub generics: ImplGenerics, diff --git a/crates/macros/cgp-macro-core/src/types/check_components/value.rs b/crates/macros/cgp-macro-core/src/types/check_components/value.rs new file mode 100644 index 00000000..8e5d36c4 --- /dev/null +++ b/crates/macros/cgp-macro-core/src/types/check_components/value.rs @@ -0,0 +1,38 @@ +use syn::bracketed; +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; +use syn::token::{Bracket, Comma}; + +use crate::types::check_components::TypeWithGenerics; + +pub enum CheckValue { + Single(TypeWithGenerics), + Multi(Punctuated), +} + +impl CheckValue { + pub fn to_values(&self) -> Vec { + match self { + Self::Single(value) => vec![value.clone()], + Self::Multi(values) => Vec::from_iter(values.iter().cloned()), + } + } +} + +impl Parse for CheckValue { + fn parse(input: ParseStream) -> syn::Result { + if input.peek(Bracket) { + let content; + bracketed!(content in input); + + let values: Punctuated = + Punctuated::parse_terminated(&content)?; + + Ok(Self::Multi(values)) + } else { + let value = input.parse()?; + + Ok(Self::Single(value)) + } + } +} diff --git a/crates/macros/cgp-macro-lib/src/check_components/derive.rs b/crates/macros/cgp-macro-lib/src/check_components/derive.rs index 032a942e..e44327a5 100644 --- a/crates/macros/cgp-macro-lib/src/check_components/derive.rs +++ b/crates/macros/cgp-macro-lib/src/check_components/derive.rs @@ -1,5 +1,5 @@ use cgp_macro_core::functions::merge_generics; -use cgp_macro_core::types::check_components::{CheckComponentsTable, CheckEntry}; +use cgp_macro_core::types::check_components::{CheckComponentsTable, EvaluatedCheckEntry}; use quote::quote; use syn::punctuated::Punctuated; use syn::token::Comma; @@ -26,7 +26,7 @@ pub fn derive_check_components( trait #trait_name <__Component__, __Params__: ?Sized>: CanUseComponent<__Component__, __Params__> {} })?; - for CheckEntry { + for EvaluatedCheckEntry { component_type, component_params, span, @@ -71,7 +71,7 @@ pub fn derive_check_provider( trait #trait_name <__Component__, __Params__: ?Sized>: IsProviderFor<__Component__, #context_type, __Params__> {} })?; - for CheckEntry { + for EvaluatedCheckEntry { component_type, component_params, .. 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 7cdd3271..da67ea28 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,4 +1,6 @@ -use cgp_macro_core::types::check_components::{CheckComponentsTable, CheckEntries, CheckEntry}; +use cgp_macro_core::types::check_components::{ + CheckComponentsTable, CheckEntries, EvaluatedCheckEntry, +}; use cgp_macro_core::types::generics::ImplGenerics; use proc_macro2::{Span, TokenStream}; use quote::{ToTokens, TokenStreamExt}; @@ -14,7 +16,7 @@ use crate::parse::{DelegateAndCheckSpec, DelegateEntry, DelegateKey}; pub fn delegate_and_check_components(body: TokenStream) -> syn::Result { let spec: DelegateAndCheckSpec = parse2(body)?; - let check_entries: Vec = spec + let check_entries: Vec = spec .entries .iter() .flat_map(|entry| { @@ -25,14 +27,14 @@ pub fn delegate_and_check_components(body: TokenStream) -> syn::Result check_params .iter() - .map(|generic| CheckEntry { + .map(|generic| EvaluatedCheckEntry { component_type: component_type.clone(), component_params: Some(generic.clone()), span, generics: ImplGenerics::default(), }) .collect::>(), - None => vec![CheckEntry { + None => vec![EvaluatedCheckEntry { component_type: component_type.clone(), component_params: None, span, From 78754b11e989b8dd8c5215cbcfdfe046f4b5a51d Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 21 Jun 2026 22:48:31 +0200 Subject: [PATCH 3/8] Refactor to use new CheckEntry --- .../types/check_components/check_entries.rs | 95 +++---------------- .../src/types/check_components/entry.rs | 73 ++++++++------ .../check_components/type_with_generics.rs | 9 ++ .../src/check_components/derive.rs | 6 +- .../delegate_and_check_components.rs | 52 +++++----- 5 files changed, 94 insertions(+), 141 deletions(-) diff --git a/crates/macros/cgp-macro-core/src/types/check_components/check_entries.rs b/crates/macros/cgp-macro-core/src/types/check_components/check_entries.rs index 24734159..956ebc7f 100644 --- a/crates/macros/cgp-macro-core/src/types/check_components/check_entries.rs +++ b/crates/macros/cgp-macro-core/src/types/check_components/check_entries.rs @@ -1,99 +1,28 @@ use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; -use syn::spanned::Spanned; -use syn::token::{Bracket, Colon, Comma}; -use syn::{Type, bracketed}; +use syn::token::Comma; -use crate::types::check_components::{EvaluatedCheckEntry, TypeWithGenerics}; -use crate::types::generics::ImplGenerics; +use crate::types::check_components::{CheckEntry, EvaluatedCheckEntry}; pub struct CheckEntries { - pub entries: Vec, + pub entries: Punctuated, } -struct ParseCheckEntries { - pub entries: Vec, -} +impl CheckEntries { + pub fn eval(&self) -> Vec { + let mut evaluated_entries = Vec::new(); -impl Parse for CheckEntries { - fn parse(input: ParseStream) -> syn::Result { - let check_entries: Punctuated = - Punctuated::parse_terminated(input)?; - - let entries = check_entries - .into_iter() - .flat_map(|check_entry| check_entry.entries.into_iter()) - .collect(); + for entry in &self.entries { + evaluated_entries.extend(entry.eval()); + } - Ok(Self { entries }) + evaluated_entries } } -impl Parse for ParseCheckEntries { +impl Parse for CheckEntries { fn parse(input: ParseStream) -> syn::Result { - let component_types: Vec = if input.peek(Bracket) { - let content; - bracketed!(content in input); - - let types: Punctuated = Punctuated::parse_terminated(&content)?; - Vec::from_iter(types) - } else { - let component_type: Type = input.parse()?; - vec![component_type] - }; - - let component_params: Vec = if input.peek(Colon) { - let _: Colon = input.parse()?; - - if input.peek(Bracket) { - let content; - bracketed!(content in input); - - let types: Punctuated = - Punctuated::parse_terminated(&content)?; - - types.into_iter().collect() - } else { - vec![input.parse()?] - } - } else { - vec![] - }; - - let mut entries = Vec::new(); - - let component_types_count = component_types.len(); - - for component_type in component_types.iter() { - if component_params.is_empty() { - entries.push(EvaluatedCheckEntry { - component_type: component_type.clone(), - component_params: None, - span: component_type.span(), - generics: ImplGenerics::default(), - }) - } else { - let component_params_count = component_params.len(); - - for component_param in component_params.iter() { - let component_param_type = &component_param.ty; - let component_param_generics = &component_param.generics; - - let span = if component_types_count >= component_params_count { - component_type.span() - } else { - component_param_type.span() - }; - - entries.push(EvaluatedCheckEntry { - component_type: component_type.clone(), - component_params: Some(component_param_type.clone()), - span, - generics: component_param_generics.clone(), - }) - } - } - } + let entries: Punctuated = Punctuated::parse_terminated(input)?; Ok(Self { entries }) } diff --git a/crates/macros/cgp-macro-core/src/types/check_components/entry.rs b/crates/macros/cgp-macro-core/src/types/check_components/entry.rs index 66ff18a5..aa73d60d 100644 --- a/crates/macros/cgp-macro-core/src/types/check_components/entry.rs +++ b/crates/macros/cgp-macro-core/src/types/check_components/entry.rs @@ -7,8 +7,7 @@ use crate::types::generics::ImplGenerics; pub struct CheckEntry { pub key: CheckKey, - pub colon: Colon, - pub value: CheckValue, + pub value: Option, } impl CheckEntry { @@ -16,51 +15,69 @@ impl CheckEntry { let mut entries = Vec::new(); let keys = self.key.to_keys(); - let values = self.value.to_values(); let component_types_count = keys.len(); for component_type in keys.iter() { - if values.is_empty() { + if let Some(value) = &self.value { + let values = value.to_values(); + + if values.is_empty() { + entries.push(EvaluatedCheckEntry { + component_type: component_type.clone(), + component_params: None, + span: component_type.span(), + generics: ImplGenerics::default(), + }) + } else { + let component_params_count = values.len(); + + for component_param in values.iter() { + let component_param_type = &component_param.ty; + let component_param_generics = &component_param.generics; + + let span = if component_types_count >= component_params_count { + component_type.span() + } else { + component_param_type.span() + }; + + entries.push(EvaluatedCheckEntry { + component_type: component_type.clone(), + component_params: Some(component_param_type.clone()), + span, + generics: component_param_generics.clone(), + }) + } + } + } else { entries.push(EvaluatedCheckEntry { component_type: component_type.clone(), component_params: None, span: component_type.span(), generics: ImplGenerics::default(), }) - } else { - let component_params_count = values.len(); - - for component_param in values.iter() { - let component_param_type = &component_param.ty; - let component_param_generics = &component_param.generics; - - let span = if component_types_count >= component_params_count { - component_type.span() - } else { - component_param_type.span() - }; - - entries.push(EvaluatedCheckEntry { - component_type: component_type.clone(), - component_params: Some(component_param_type.clone()), - span, - generics: component_param_generics.clone(), - }) - } } } - todo!() + entries } } impl Parse for CheckEntry { fn parse(input: ParseStream) -> syn::Result { let key = input.parse()?; - let colon = input.parse()?; - let value = input.parse()?; - Ok(Self { key, colon, value }) + if input.peek(Colon) { + let _: Colon = input.parse()?; + let value = input.parse()?; + + Ok(Self { + key, + value: Some(value), + }) + } else { + Ok(Self { key, value: None }) + } } } diff --git a/crates/macros/cgp-macro-core/src/types/check_components/type_with_generics.rs b/crates/macros/cgp-macro-core/src/types/check_components/type_with_generics.rs index bbe8e477..d43bb6d5 100644 --- a/crates/macros/cgp-macro-core/src/types/check_components/type_with_generics.rs +++ b/crates/macros/cgp-macro-core/src/types/check_components/type_with_generics.rs @@ -23,3 +23,12 @@ impl Parse for TypeWithGenerics { Ok(Self { ty, generics }) } } + +impl From for TypeWithGenerics { + fn from(ty: Type) -> Self { + Self { + ty, + generics: ImplGenerics::default(), + } + } +} diff --git a/crates/macros/cgp-macro-lib/src/check_components/derive.rs b/crates/macros/cgp-macro-lib/src/check_components/derive.rs index e44327a5..10ab4e1d 100644 --- a/crates/macros/cgp-macro-lib/src/check_components/derive.rs +++ b/crates/macros/cgp-macro-lib/src/check_components/derive.rs @@ -31,11 +31,11 @@ pub fn derive_check_components( component_params, span, generics: check_generics, - } in spec.check_entries.entries.iter() + } in spec.check_entries.eval() { // Override the span of the context type so that any unsatisfied constraint // error is highlighted on the component type instead - let context_type = override_span(span, context_type)?; + let context_type = override_span(&span, context_type)?; let component_param = component_params.as_ref().unwrap_or(&unit); @@ -75,7 +75,7 @@ pub fn derive_check_provider( component_type, component_params, .. - } in spec.check_entries.entries.iter() + } in spec.check_entries.eval() { let component_param = component_params.as_ref().unwrap_or(&unit); 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 da67ea28..9af5742b 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,11 +1,10 @@ use cgp_macro_core::types::check_components::{ - CheckComponentsTable, CheckEntries, EvaluatedCheckEntry, + CheckComponentsTable, CheckEntries, CheckEntry, CheckKey, CheckValue, TypeWithGenerics, }; use cgp_macro_core::types::generics::ImplGenerics; use proc_macro2::{Span, TokenStream}; use quote::{ToTokens, TokenStreamExt}; use syn::punctuated::Punctuated; -use syn::spanned::Spanned; use syn::token::{Comma, Where}; use syn::{Type, WhereClause, parse2}; @@ -16,34 +15,33 @@ use crate::parse::{DelegateAndCheckSpec, DelegateEntry, DelegateKey}; pub fn delegate_and_check_components(body: TokenStream) -> syn::Result { let spec: DelegateAndCheckSpec = parse2(body)?; - let check_entries: Vec = spec - .entries - .iter() - .flat_map(|entry| { - entry.keys.iter().flat_map(|key| { - let component_type = &key.component_type; - let span = component_type.span(); + 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) => check_params + match &key.check_params { + Some(check_params) => { + let values = check_params .iter() - .map(|generic| EvaluatedCheckEntry { - component_type: component_type.clone(), - component_params: Some(generic.clone()), - span, - generics: ImplGenerics::default(), - }) - .collect::>(), - None => vec![EvaluatedCheckEntry { - component_type: component_type.clone(), - component_params: None, - span, - generics: ImplGenerics::default(), - }], + .cloned() + .map(TypeWithGenerics::from) + .collect(); + check_entries.push(CheckEntry { + key: CheckKey::Single(component_type.clone()), + value: Some(CheckValue::Multi(values)), + }); } - }) - }) - .collect(); + None => { + check_entries.push(CheckEntry { + key: CheckKey::Single(component_type.clone()), + value: None, + }); + } + } + } + } let delegate_entries: Punctuated, Comma> = spec .entries From d8219d3e083db75300ce795d8473eeb520e4c27f Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 21 Jun 2026 22:51:38 +0200 Subject: [PATCH 4/8] Fix clippy --- .../macros/cgp-macro-core/src/types/check_components/value.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/macros/cgp-macro-core/src/types/check_components/value.rs b/crates/macros/cgp-macro-core/src/types/check_components/value.rs index 8e5d36c4..67990567 100644 --- a/crates/macros/cgp-macro-core/src/types/check_components/value.rs +++ b/crates/macros/cgp-macro-core/src/types/check_components/value.rs @@ -6,14 +6,14 @@ use syn::token::{Bracket, Comma}; use crate::types::check_components::TypeWithGenerics; pub enum CheckValue { - Single(TypeWithGenerics), + Single(Box), Multi(Punctuated), } impl CheckValue { pub fn to_values(&self) -> Vec { match self { - Self::Single(value) => vec![value.clone()], + Self::Single(value) => vec![value.as_ref().clone()], Self::Multi(values) => Vec::from_iter(values.iter().cloned()), } } From d7bb675f757821484f04978c1a8f7e86293d1da3 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 21 Jun 2026 23:03:22 +0200 Subject: [PATCH 5/8] Fix span in delegate_and_check_components --- .../delegate_and_check_components.rs | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 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 9af5742b..959d36f2 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 @@ -23,15 +23,18 @@ pub fn delegate_and_check_components(body: TokenStream) -> syn::Result { - let values = check_params - .iter() - .cloned() - .map(TypeWithGenerics::from) - .collect(); - check_entries.push(CheckEntry { - key: CheckKey::Single(component_type.clone()), - value: Some(CheckValue::Multi(values)), - }); + // 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 { From a38f0cd1855084e22a0c14562eda0a32c82e9316 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 21 Jun 2026 23:13:22 +0200 Subject: [PATCH 6/8] Use TypeWithGenerics in EvaluatedCheckEntry --- .../src/types/check_components/entry.rs | 21 +++++++------ .../check_components/evaluated_check_entry.rs | 7 ++--- .../src/check_components/derive.rs | 30 +++++++++++++------ 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/crates/macros/cgp-macro-core/src/types/check_components/entry.rs b/crates/macros/cgp-macro-core/src/types/check_components/entry.rs index aa73d60d..430f1622 100644 --- a/crates/macros/cgp-macro-core/src/types/check_components/entry.rs +++ b/crates/macros/cgp-macro-core/src/types/check_components/entry.rs @@ -2,8 +2,7 @@ use syn::parse::{Parse, ParseStream}; use syn::spanned::Spanned; use syn::token::Colon; -use crate::types::check_components::{CheckKey, CheckValue, EvaluatedCheckEntry}; -use crate::types::generics::ImplGenerics; +use crate::types::check_components::{CheckKey, CheckValue, EvaluatedCheckEntry, TypeWithGenerics}; pub struct CheckEntry { pub key: CheckKey, @@ -24,10 +23,9 @@ impl CheckEntry { if values.is_empty() { entries.push(EvaluatedCheckEntry { - component_type: component_type.clone(), - component_params: None, + key: component_type.clone(), + value: None, span: component_type.span(), - generics: ImplGenerics::default(), }) } else { let component_params_count = values.len(); @@ -43,19 +41,20 @@ impl CheckEntry { }; entries.push(EvaluatedCheckEntry { - component_type: component_type.clone(), - component_params: Some(component_param_type.clone()), + key: component_type.clone(), + value: Some(TypeWithGenerics { + ty: component_param_type.clone(), + generics: component_param_generics.clone(), + }), span, - generics: component_param_generics.clone(), }) } } } else { entries.push(EvaluatedCheckEntry { - component_type: component_type.clone(), - component_params: None, + key: component_type.clone(), + value: None, span: component_type.span(), - generics: ImplGenerics::default(), }) } } diff --git a/crates/macros/cgp-macro-core/src/types/check_components/evaluated_check_entry.rs b/crates/macros/cgp-macro-core/src/types/check_components/evaluated_check_entry.rs index 0407c25d..ed621a36 100644 --- a/crates/macros/cgp-macro-core/src/types/check_components/evaluated_check_entry.rs +++ b/crates/macros/cgp-macro-core/src/types/check_components/evaluated_check_entry.rs @@ -1,11 +1,10 @@ use proc_macro2::Span; use syn::Type; -use crate::types::generics::ImplGenerics; +use crate::types::check_components::TypeWithGenerics; pub struct EvaluatedCheckEntry { - pub component_type: Type, - pub component_params: Option, + pub key: Type, + pub value: Option, pub span: Span, - pub generics: ImplGenerics, } diff --git a/crates/macros/cgp-macro-lib/src/check_components/derive.rs b/crates/macros/cgp-macro-lib/src/check_components/derive.rs index 10ab4e1d..cb95debe 100644 --- a/crates/macros/cgp-macro-lib/src/check_components/derive.rs +++ b/crates/macros/cgp-macro-lib/src/check_components/derive.rs @@ -1,5 +1,7 @@ use cgp_macro_core::functions::merge_generics; -use cgp_macro_core::types::check_components::{CheckComponentsTable, EvaluatedCheckEntry}; +use cgp_macro_core::types::check_components::{ + CheckComponentsTable, EvaluatedCheckEntry, TypeWithGenerics, +}; use quote::quote; use syn::punctuated::Punctuated; use syn::token::Comma; @@ -27,22 +29,26 @@ pub fn derive_check_components( })?; for EvaluatedCheckEntry { - component_type, - component_params, + key: component_type, + value: component_params, span, - generics: check_generics, } in spec.check_entries.eval() { // Override the span of the context type so that any unsatisfied constraint // error is highlighted on the component type instead let context_type = override_span(&span, context_type)?; - let component_param = component_params.as_ref().unwrap_or(&unit); + let TypeWithGenerics { + ty: component_param, + generics: check_generics, + } = component_params.unwrap_or_else(|| unit.clone().into()); let generics = merge_generics(&check_generics.generics, &impl_generics.generics); + let impl_generics = generics.split_for_impl().0; + let item_impl: ItemImpl = parse2(quote! { - impl #generics + impl #impl_generics #trait_name < #component_type, #component_param > for #context_type #where_clause @@ -72,12 +78,18 @@ pub fn derive_check_provider( })?; for EvaluatedCheckEntry { - component_type, - component_params, + key: component_type, + value: component_params, .. } in spec.check_entries.eval() { - let component_param = component_params.as_ref().unwrap_or(&unit); + let TypeWithGenerics { + ty: component_param, + generics: check_generics, + } = component_params.unwrap_or_else(|| unit.clone().into()); + + let generics = merge_generics(&check_generics.generics, &impl_generics.generics); + let impl_generics = generics.split_for_impl().0; for provider in providers { let item_impl: ItemImpl = parse2(quote! { From 0cc7ea1516a5efaa01ef8b47db491b1a00d80466 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 21 Jun 2026 23:27:10 +0200 Subject: [PATCH 7/8] Migrate derive_check_components --- .../{check_entries.rs => entries.rs} | 0 .../src/types/check_components/mod.rs | 4 +- .../src/types/check_components/table.rs | 85 ++++++++++++++++++- .../src/entrypoints/check_components.rs | 4 +- 4 files changed, 86 insertions(+), 7 deletions(-) rename crates/macros/cgp-macro-core/src/types/check_components/{check_entries.rs => entries.rs} (100%) diff --git a/crates/macros/cgp-macro-core/src/types/check_components/check_entries.rs b/crates/macros/cgp-macro-core/src/types/check_components/entries.rs similarity index 100% rename from crates/macros/cgp-macro-core/src/types/check_components/check_entries.rs rename to crates/macros/cgp-macro-core/src/types/check_components/entries.rs diff --git a/crates/macros/cgp-macro-core/src/types/check_components/mod.rs b/crates/macros/cgp-macro-core/src/types/check_components/mod.rs index 7f4319ad..8e8d5472 100644 --- a/crates/macros/cgp-macro-core/src/types/check_components/mod.rs +++ b/crates/macros/cgp-macro-core/src/types/check_components/mod.rs @@ -1,4 +1,4 @@ -mod check_entries; +mod entries; mod entry; mod evaluated_check_entry; mod key; @@ -7,7 +7,7 @@ mod tables; mod type_with_generics; mod value; -pub use check_entries::*; +pub use entries::*; pub use entry::*; pub use evaluated_check_entry::*; pub use key::*; 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 2d961938..43d4b3ba 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 @@ -4,9 +4,11 @@ use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::token::{Comma, Lt, Pound, Where}; -use syn::{Attribute, Ident, Type, WhereClause, braced, parse2}; +use syn::{Attribute, Ident, ItemImpl, ItemTrait, Type, WhereClause, braced, parse2}; -use crate::types::check_components::CheckEntries; +use crate::functions::merge_generics; +use crate::parse_internal; +use crate::types::check_components::{CheckEntries, EvaluatedCheckEntry, TypeWithGenerics}; use crate::types::generics::ImplGenerics; use crate::types::ident::IdentWithTypeArgs; @@ -19,6 +21,70 @@ pub struct CheckComponentsTable { pub check_entries: CheckEntries, } +impl CheckComponentsTable { + pub fn eval(&self) -> syn::Result<(ItemTrait, Vec)> { + let mut item_impls = Vec::new(); + let unit: Type = parse_internal!(()); + + let context_type = &self.context_type; + let trait_name = &self.trait_name; + let impl_generics = &self.impl_generics; + let where_clause = &self.where_clause; + + let item_trait: ItemTrait = if self.check_providers.is_some() { + parse_internal! { + trait #trait_name <__Component__, __Params__: ?Sized>: IsProviderFor<__Component__, #context_type, __Params__> {} + } + } else { + parse_internal! { + trait #trait_name <__Component__, __Params__: ?Sized>: CanUseComponent<__Component__, __Params__> {} + } + }; + + let evaluated_entries = self.check_entries.eval(); + + for entry in evaluated_entries { + let EvaluatedCheckEntry { + key: component_type, + value: component_params, + span, + } = entry; + + let self_types = if let Some(check_providers) = &self.check_providers { + Vec::from_iter(check_providers.iter().cloned()) + } else { + // Override the span of the context type so that any unsatisfied constraint + // error is highlighted on the component type instead + let context_type = override_span(&span, context_type)?; + vec![context_type] + }; + + let TypeWithGenerics { + ty: component_param, + generics: check_generics, + } = component_params.unwrap_or_else(|| unit.clone().into()); + + let generics = merge_generics(&check_generics.generics, &impl_generics.generics); + + let impl_generics = generics.split_for_impl().0; + + for self_type in self_types { + let item_impl: ItemImpl = parse_internal! { + impl #impl_generics + #trait_name < #component_type, #component_param > + for #self_type + #where_clause + {} + }; + + item_impls.push(item_impl); + } + } + + Ok((item_trait, item_impls)) + } +} + impl Parse for CheckComponentsTable { fn parse(input: ParseStream) -> syn::Result { let mut check_providers: Option> = None; @@ -98,3 +164,18 @@ impl Parse for CheckComponentsTable { }) } } + +fn override_span(span: &Span, body: &T) -> syn::Result +where + T: Parse + ToTokens, +{ + parse2( + body.to_token_stream() + .into_iter() + .map(|mut tree| { + tree.set_span(*span); + tree + }) + .collect(), + ) +} diff --git a/crates/macros/cgp-macro-lib/src/entrypoints/check_components.rs b/crates/macros/cgp-macro-lib/src/entrypoints/check_components.rs index 55d68819..43a606e5 100644 --- a/crates/macros/cgp-macro-lib/src/entrypoints/check_components.rs +++ b/crates/macros/cgp-macro-lib/src/entrypoints/check_components.rs @@ -3,15 +3,13 @@ use proc_macro2::TokenStream; use quote::{ToTokens, TokenStreamExt}; use syn::parse2; -use crate::check_components::derive_check_components; - pub fn check_components(body: TokenStream) -> syn::Result { let spec: CheckComponentsTables = parse2(body)?; let mut out = TokenStream::new(); for spec in spec.specs { - let (item_trait, item_impls) = derive_check_components(&spec)?; + let (item_trait, item_impls) = spec.eval()?; out.append_all(item_trait.to_token_stream()); out.append_all(item_impls); From 5f84816e4532246b2c4537896b85d515e1d090ad Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 21 Jun 2026 23:33:17 +0200 Subject: [PATCH 8/8] Fully migrate derive check components --- .../src/types/check_components/table.rs | 11 +- .../src/types/check_components/tables.rs | 21 +++- .../src/check_components/derive.rs | 108 ------------------ .../cgp-macro-lib/src/check_components/mod.rs | 5 - .../src/check_components/override_span.rs | 19 --- .../src/entrypoints/check_components.rs | 17 +-- .../delegate_and_check_components.rs | 10 +- crates/macros/cgp-macro-lib/src/lib.rs | 1 - 8 files changed, 38 insertions(+), 154 deletions(-) delete mode 100644 crates/macros/cgp-macro-lib/src/check_components/derive.rs delete mode 100644 crates/macros/cgp-macro-lib/src/check_components/mod.rs delete mode 100644 crates/macros/cgp-macro-lib/src/check_components/override_span.rs 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 43d4b3ba..bac27732 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 @@ -4,7 +4,7 @@ use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::token::{Comma, Lt, Pound, Where}; -use syn::{Attribute, Ident, ItemImpl, ItemTrait, Type, WhereClause, braced, parse2}; +use syn::{Attribute, Ident, Item, ItemImpl, ItemTrait, Type, WhereClause, braced, parse2}; use crate::functions::merge_generics; use crate::parse_internal; @@ -22,6 +22,15 @@ pub struct CheckComponentsTable { } impl CheckComponentsTable { + pub fn to_items(&self) -> syn::Result> { + let (item_trait, item_impls) = self.eval()?; + + let mut items = vec![item_trait.into()]; + items.extend(item_impls.into_iter().map(Into::into)); + + Ok(items) + } + pub fn eval(&self) -> syn::Result<(ItemTrait, Vec)> { let mut item_impls = Vec::new(); let unit: Type = parse_internal!(()); diff --git a/crates/macros/cgp-macro-core/src/types/check_components/tables.rs b/crates/macros/cgp-macro-core/src/types/check_components/tables.rs index 6bafc659..d2841381 100644 --- a/crates/macros/cgp-macro-core/src/types/check_components/tables.rs +++ b/crates/macros/cgp-macro-core/src/types/check_components/tables.rs @@ -1,20 +1,33 @@ +use syn::Item; use syn::parse::{Parse, ParseStream}; use crate::types::check_components::CheckComponentsTable; pub struct CheckComponentsTables { - pub specs: Vec, + pub tables: Vec, +} + +impl CheckComponentsTables { + pub fn to_items(&self) -> syn::Result> { + let mut items = Vec::new(); + + for table in &self.tables { + items.extend(table.to_items()?); + } + + Ok(items) + } } impl Parse for CheckComponentsTables { fn parse(input: ParseStream) -> syn::Result { - let mut specs = Vec::new(); + let mut tables = Vec::new(); while !input.is_empty() { let spec: CheckComponentsTable = input.parse()?; - specs.push(spec); + tables.push(spec); } - Ok(Self { specs }) + Ok(Self { tables }) } } diff --git a/crates/macros/cgp-macro-lib/src/check_components/derive.rs b/crates/macros/cgp-macro-lib/src/check_components/derive.rs deleted file mode 100644 index cb95debe..00000000 --- a/crates/macros/cgp-macro-lib/src/check_components/derive.rs +++ /dev/null @@ -1,108 +0,0 @@ -use cgp_macro_core::functions::merge_generics; -use cgp_macro_core::types::check_components::{ - CheckComponentsTable, EvaluatedCheckEntry, TypeWithGenerics, -}; -use quote::quote; -use syn::punctuated::Punctuated; -use syn::token::Comma; -use syn::{ItemImpl, ItemTrait, Type, parse2}; - -use crate::check_components::override_span; - -pub fn derive_check_components( - spec: &CheckComponentsTable, -) -> syn::Result<(ItemTrait, Vec)> { - if let Some(check_providers) = &spec.check_providers { - return derive_check_provider(spec, check_providers); - } - - let mut item_impls = Vec::new(); - let unit: Type = parse2(quote!(()))?; - - let context_type = &spec.context_type; - let trait_name = &spec.trait_name; - let impl_generics = &spec.impl_generics; - let where_clause = &spec.where_clause; - - let item_trait = parse2(quote! { - trait #trait_name <__Component__, __Params__: ?Sized>: CanUseComponent<__Component__, __Params__> {} - })?; - - for EvaluatedCheckEntry { - key: component_type, - value: component_params, - span, - } in spec.check_entries.eval() - { - // Override the span of the context type so that any unsatisfied constraint - // error is highlighted on the component type instead - let context_type = override_span(&span, context_type)?; - - let TypeWithGenerics { - ty: component_param, - generics: check_generics, - } = component_params.unwrap_or_else(|| unit.clone().into()); - - let generics = merge_generics(&check_generics.generics, &impl_generics.generics); - - let impl_generics = generics.split_for_impl().0; - - let item_impl: ItemImpl = parse2(quote! { - impl #impl_generics - #trait_name < #component_type, #component_param > - for #context_type - #where_clause - {} - })?; - - item_impls.push(item_impl); - } - - Ok((item_trait, item_impls)) -} - -pub fn derive_check_provider( - spec: &CheckComponentsTable, - providers: &Punctuated, -) -> syn::Result<(ItemTrait, Vec)> { - let mut item_impls = Vec::new(); - let unit: Type = parse2(quote!(()))?; - - let context_type = &spec.context_type; - let trait_name = &spec.trait_name; - let impl_generics = &spec.impl_generics; - let where_clause = &spec.where_clause; - - let item_trait = parse2(quote! { - trait #trait_name <__Component__, __Params__: ?Sized>: IsProviderFor<__Component__, #context_type, __Params__> {} - })?; - - for EvaluatedCheckEntry { - key: component_type, - value: component_params, - .. - } in spec.check_entries.eval() - { - let TypeWithGenerics { - ty: component_param, - generics: check_generics, - } = component_params.unwrap_or_else(|| unit.clone().into()); - - let generics = merge_generics(&check_generics.generics, &impl_generics.generics); - let impl_generics = generics.split_for_impl().0; - - for provider in providers { - let item_impl: ItemImpl = parse2(quote! { - impl #impl_generics - #trait_name < #component_type, #component_param > - for #provider - #where_clause - {} - })?; - - item_impls.push(item_impl); - } - } - - Ok((item_trait, item_impls)) -} diff --git a/crates/macros/cgp-macro-lib/src/check_components/mod.rs b/crates/macros/cgp-macro-lib/src/check_components/mod.rs deleted file mode 100644 index 783be357..00000000 --- a/crates/macros/cgp-macro-lib/src/check_components/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod derive; -mod override_span; - -pub use derive::*; -pub use override_span::*; diff --git a/crates/macros/cgp-macro-lib/src/check_components/override_span.rs b/crates/macros/cgp-macro-lib/src/check_components/override_span.rs deleted file mode 100644 index 9d3a2c79..00000000 --- a/crates/macros/cgp-macro-lib/src/check_components/override_span.rs +++ /dev/null @@ -1,19 +0,0 @@ -use proc_macro2::Span; -use quote::ToTokens; -use syn::parse::Parse; -use syn::parse2; - -pub fn override_span(span: &Span, body: &T) -> syn::Result -where - T: Parse + ToTokens, -{ - parse2( - body.to_token_stream() - .into_iter() - .map(|mut tree| { - tree.set_span(*span); - tree - }) - .collect(), - ) -} diff --git a/crates/macros/cgp-macro-lib/src/entrypoints/check_components.rs b/crates/macros/cgp-macro-lib/src/entrypoints/check_components.rs index 43a606e5..4835194d 100644 --- a/crates/macros/cgp-macro-lib/src/entrypoints/check_components.rs +++ b/crates/macros/cgp-macro-lib/src/entrypoints/check_components.rs @@ -1,19 +1,14 @@ use cgp_macro_core::types::check_components::CheckComponentsTables; use proc_macro2::TokenStream; -use quote::{ToTokens, TokenStreamExt}; +use quote::quote; use syn::parse2; pub fn check_components(body: TokenStream) -> syn::Result { - let spec: CheckComponentsTables = parse2(body)?; + let tables: CheckComponentsTables = parse2(body)?; - let mut out = TokenStream::new(); + let items = tables.to_items()?; - for spec in spec.specs { - let (item_trait, item_impls) = spec.eval()?; - - out.append_all(item_trait.to_token_stream()); - out.append_all(item_impls); - } - - Ok(out) + Ok(quote! { + #( #items )* + }) } 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 959d36f2..7f087056 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 @@ -3,12 +3,11 @@ use cgp_macro_core::types::check_components::{ }; use cgp_macro_core::types::generics::ImplGenerics; use proc_macro2::{Span, TokenStream}; -use quote::{ToTokens, TokenStreamExt}; +use quote::quote; use syn::punctuated::Punctuated; use syn::token::{Comma, Where}; use syn::{Type, WhereClause, parse2}; -use crate::check_components::derive_check_components; use crate::delegate_components::impl_delegate_components; use crate::parse::{DelegateAndCheckSpec, DelegateEntry, DelegateKey}; @@ -84,10 +83,11 @@ pub fn delegate_and_check_components(body: TokenStream) -> syn::Result