From 1be6f1ec4d430d44dc3f5e545bfe2075aefe4331 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 27 Sep 2026 03:31:04 +0000 Subject: [PATCH 1/5] Test the FinalizeOptional missing-field error Pin that finalize_optional names a missing field, and that with several unset it names the last one in declaration order. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01HEMw6zp1hjNDZyxaKsszMj --- .../extensible_records/optional_builder.rs | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/crates/tests/cgp-tests/tests/extensible_records/optional_builder.rs b/crates/tests/cgp-tests/tests/extensible_records/optional_builder.rs index aa5e9f01d..2a29d7782 100644 --- a/crates/tests/cgp-tests/tests/extensible_records/optional_builder.rs +++ b/crates/tests/cgp-tests/tests/extensible_records/optional_builder.rs @@ -3,8 +3,9 @@ //! Besides the required-field builder pinned by `record_derive`, a `CgpData` //! record also supports an `optional_builder()` where fields start absent and are //! filled with `set`. `set` returns any previously-set value, `finalize_optional` -//! succeeds only once every field is present, and `finalize_with_default` fills -//! any still-absent field with its `Default`. +//! succeeds only once every field is present (otherwise it returns the name of a +//! missing field, checking fields from last to first), and `finalize_with_default` +//! fills any still-absent field with its `Default`. //! //! The snapshot in this file is the `#[derive(CgpData)]` expansion for the //! two-field record it exercises; the derive expansion itself is owned by this @@ -243,3 +244,22 @@ fn test_optional_and_default_fields() { assert_eq!(context.foo, "foo"); assert_eq!(context.bar, 0); } + +#[test] +fn test_finalize_optional_missing_field() { + // A missing field fails the finalize and names the field. + let result = Context::optional_builder() + .set(PhantomData::, "foo".to_owned()) + .finalize_optional(); + assert_eq!(result.err(), Some("bar")); + + // The fields are checked from last to first, so with several unset the error + // names the last one in declaration order. + let result = Context::optional_builder().finalize_optional(); + assert_eq!(result.err(), Some("bar")); + + let result = Context::optional_builder() + .set(PhantomData::, 42) + .finalize_optional(); + assert_eq!(result.err(), Some("foo")); +} From efd78a9d5d69ddad9496bbffe4c1bdb12f926e01 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 27 Sep 2026 03:54:56 +0000 Subject: [PATCH 2/5] Add macro rejection and snapshot tests found missing in the KB review Parser rejections for #[cgp_type], #[cgp_computer], #[cgp_producer], #[cgp_auto_dispatch], and #[blanket_trait], with cgp-macro-tests gaining a dev-dependency on cgp-extra-macro-lib. New snapshots cover the mutable &mut T and Option<&mut str> auto getters, a #[blanket_trait(Ctx)] with an associated constant, and a #[skip_check] entry in delegate_and_check_components!. A type-equality test covers the bare Path! macro. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01HEMw6zp1hjNDZyxaKsszMj --- Cargo.lock | 1 + crates/tests/cgp-macro-tests/Cargo.toml | 1 + .../tests/parser_rejections/blanket_trait.rs | 53 +++++++++++ .../parser_rejections/cgp_auto_dispatch.rs | 56 +++++++++++ .../tests/parser_rejections/cgp_computer.rs | 41 ++++++++ .../tests/parser_rejections/cgp_producer.rs | 67 +++++++++++++ .../tests/parser_rejections/cgp_type.rs | 95 +++++++++++++++++++ .../tests/parser_rejections/mod.rs | 14 ++- .../blanket_traits/const_custom_context.rs | 59 ++++++++++++ .../cgp-tests/tests/blanket_traits/mod.rs | 3 + .../tests/checking/delegate_and_check_skip.rs | 80 ++++++++++++++++ crates/tests/cgp-tests/tests/checking/mod.rs | 6 +- crates/tests/cgp-tests/tests/getters/mod.rs | 2 + .../tests/getters/mut_option_str_auto.rs | 58 +++++++++++ .../cgp-tests/tests/getters/mut_ref_auto.rs | 56 +++++++++++ .../tests/cgp-tests/tests/namespaces/mod.rs | 3 +- .../cgp-tests/tests/namespaces/path_macro.rs | 38 ++++++++ 17 files changed, 626 insertions(+), 7 deletions(-) create mode 100644 crates/tests/cgp-macro-tests/tests/parser_rejections/blanket_trait.rs create mode 100644 crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_auto_dispatch.rs create mode 100644 crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_computer.rs create mode 100644 crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_producer.rs create mode 100644 crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_type.rs create mode 100644 crates/tests/cgp-tests/tests/blanket_traits/const_custom_context.rs create mode 100644 crates/tests/cgp-tests/tests/checking/delegate_and_check_skip.rs create mode 100644 crates/tests/cgp-tests/tests/getters/mut_option_str_auto.rs create mode 100644 crates/tests/cgp-tests/tests/getters/mut_ref_auto.rs create mode 100644 crates/tests/cgp-tests/tests/namespaces/path_macro.rs diff --git a/Cargo.lock b/Cargo.lock index 91afa0919..01c19d792 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -244,6 +244,7 @@ name = "cgp-macro-tests" version = "0.8.0-alpha" dependencies = [ "cgp", + "cgp-extra-macro-lib", "cgp-macro-core", "cgp-macro-lib", "cgp-macro-test-util", diff --git a/crates/tests/cgp-macro-tests/Cargo.toml b/crates/tests/cgp-macro-tests/Cargo.toml index 5b95bbc9d..53d72a8ea 100644 --- a/crates/tests/cgp-macro-tests/Cargo.toml +++ b/crates/tests/cgp-macro-tests/Cargo.toml @@ -18,6 +18,7 @@ cgp-macro-test-util = { workspace = true } insta = { version = "1.48.0" } [dev-dependencies] +cgp-extra-macro-lib = { workspace = true } cgp-macro-core = { workspace = true } cgp-macro-lib = { workspace = true } cgp-macro-test-util-lib = { workspace = true } diff --git a/crates/tests/cgp-macro-tests/tests/parser_rejections/blanket_trait.rs b/crates/tests/cgp-macro-tests/tests/parser_rejections/blanket_trait.rs new file mode 100644 index 000000000..f0d4fe4ce --- /dev/null +++ b/crates/tests/cgp-macro-tests/tests/parser_rejections/blanket_trait.rs @@ -0,0 +1,53 @@ +//! `#[blanket_trait]` rejects trait items it cannot forward into the blanket +//! impl: a method without a default body, a constant without a default +//! expression, and an item that is not a type, method, or constant. +//! +//! See cgp-knowledge-base/cgp/implementation/entrypoints/blanket_trait.md (Tests) for these +//! failure cases, and cgp-knowledge-base/cgp/reference/macros/blanket_trait.md for the +//! user-facing semantics. + +use quote::quote; + +use super::assert_macro_rejects; + +#[test] +fn rejects_method_without_default() { + assert_macro_rejects("blanket_trait with a bodiless method", || { + cgp_macro_lib::blanket_trait( + quote!(), + quote!( + pub trait FooBar: Foo { + fn foo_bar(&self); + } + ), + ) + }); +} + +#[test] +fn rejects_const_without_default() { + assert_macro_rejects("blanket_trait with a valueless const", || { + cgp_macro_lib::blanket_trait( + quote!(), + quote!( + pub trait HasLimit: Foo { + const LIMIT: u32; + } + ), + ) + }); +} + +#[test] +fn rejects_macro_item() { + assert_macro_rejects("blanket_trait with a macro item", || { + cgp_macro_lib::blanket_trait( + quote!(), + quote!( + pub trait FooBar: Foo { + some_macro!(); + } + ), + ) + }); +} diff --git a/crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_auto_dispatch.rs b/crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_auto_dispatch.rs new file mode 100644 index 000000000..81e18c399 --- /dev/null +++ b/crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_auto_dispatch.rs @@ -0,0 +1,56 @@ +//! `#[cgp_auto_dispatch]` rejects dispatch traits it cannot lower: a non-method +//! trait item, a method without a `self` receiver, and a method with a type +//! generic parameter, which the blanket impl could only bound with a quantified +//! constraint Rust does not have. +//! +//! See cgp-knowledge-base/cgp/implementation/entrypoints/cgp_auto_dispatch.md (Tests) for +//! these failure cases, and cgp-knowledge-base/cgp/reference/macros/cgp_auto_dispatch.md for +//! the user-facing semantics. + +use quote::quote; + +use super::assert_macro_rejects; + +#[test] +fn rejects_associated_type() { + assert_macro_rejects("cgp_auto_dispatch with an associated type", || { + cgp_extra_macro_lib::cgp_auto_dispatch( + quote!(), + quote!( + pub trait HasArea { + type Area; + + fn area(&self) -> f64; + } + ), + ) + }); +} + +#[test] +fn rejects_method_without_receiver() { + assert_macro_rejects("cgp_auto_dispatch on a method without self", || { + cgp_extra_macro_lib::cgp_auto_dispatch( + quote!(), + quote!( + pub trait HasArea { + fn unit_area() -> f64; + } + ), + ) + }); +} + +#[test] +fn rejects_generic_method() { + assert_macro_rejects("cgp_auto_dispatch on a generic method", || { + cgp_extra_macro_lib::cgp_auto_dispatch( + quote!(), + quote!( + pub trait CanScale { + fn scale>(&mut self, factor: T); + } + ), + ) + }); +} diff --git a/crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_computer.rs b/crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_computer.rs new file mode 100644 index 000000000..8b641a279 --- /dev/null +++ b/crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_computer.rs @@ -0,0 +1,41 @@ +//! `#[cgp_computer]` rejects a function with a `self` receiver, since a handler +//! provider has no receiver, and a one-argument `Result` alias, which its +//! syntactic `Result` detection cannot parse. +//! +//! See cgp-knowledge-base/cgp/implementation/entrypoints/cgp_computer.md (Tests) for these +//! failure cases, and cgp-knowledge-base/cgp/reference/macros/cgp_computer.md for the +//! user-facing semantics. + +use quote::quote; + +use super::assert_macro_rejects; + +#[test] +fn rejects_self_receiver() { + assert_macro_rejects("cgp_computer on a method with a receiver", || { + cgp_extra_macro_lib::cgp_computer( + quote!(), + quote!( + fn add(&self, b: u64) -> u64 { + b + } + ), + ) + }); +} + +#[test] +fn rejects_one_argument_result_alias() { + // Recorded as a Known issue: `Result` starts with `Result`, so the parser + // demands a second argument and fails with "expected `,`". + assert_macro_rejects("cgp_computer returning a one-argument Result alias", || { + cgp_extra_macro_lib::cgp_computer( + quote!(), + quote!( + fn parse(value: String) -> Result { + todo!() + } + ), + ) + }); +} diff --git a/crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_producer.rs b/crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_producer.rs new file mode 100644 index 000000000..59e07e809 --- /dev/null +++ b/crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_producer.rs @@ -0,0 +1,67 @@ +//! `#[cgp_producer]` rejects every signature that is not a synchronous, +//! non-generic function with no parameters: a parameter, a `self` receiver, an +//! `async` function, and a generic parameter. +//! +//! See cgp-knowledge-base/cgp/implementation/entrypoints/cgp_producer.md (Tests) for these +//! failure cases, and cgp-knowledge-base/cgp/reference/macros/cgp_producer.md for the +//! user-facing semantics. + +use quote::quote; + +use super::assert_macro_rejects; + +#[test] +fn rejects_parameter() { + assert_macro_rejects("cgp_producer with a parameter", || { + cgp_extra_macro_lib::cgp_producer( + quote!(), + quote!( + fn magic_number(seed: u64) -> u64 { + seed + } + ), + ) + }); +} + +#[test] +fn rejects_self_receiver() { + assert_macro_rejects("cgp_producer with a receiver", || { + cgp_extra_macro_lib::cgp_producer( + quote!(), + quote!( + fn magic_number(&self) -> u64 { + 42 + } + ), + ) + }); +} + +#[test] +fn rejects_async() { + assert_macro_rejects("cgp_producer on an async function", || { + cgp_extra_macro_lib::cgp_producer( + quote!(), + quote!( + async fn magic_number() -> u64 { + 42 + } + ), + ) + }); +} + +#[test] +fn rejects_generic_parameter() { + assert_macro_rejects("cgp_producer with a generic parameter", || { + cgp_extra_macro_lib::cgp_producer( + quote!(), + quote!( + fn default_value() -> T { + T::default() + } + ), + ) + }); +} diff --git a/crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_type.rs b/crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_type.rs new file mode 100644 index 000000000..7b3033555 --- /dev/null +++ b/crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_type.rs @@ -0,0 +1,95 @@ +//! `#[cgp_type]` rejects any trait whose body is not exactly one plain associated +//! type: an empty trait, a trait with a method, a trait with two associated types, +//! and a generic or `where`-bounded associated type. +//! +//! See cgp-knowledge-base/cgp/implementation/entrypoints/cgp_type.md (Tests) for these failure +//! cases, and cgp-knowledge-base/cgp/reference/macros/cgp_type.md for the user-facing +//! semantics. + +use quote::quote; + +use super::assert_macro_rejects; + +#[test] +fn rejects_non_trait_item() { + assert_macro_rejects("cgp_type on a struct", || { + cgp_macro_lib::cgp_type( + quote!(), + quote!( + pub struct NotATrait; + ), + ) + }); +} + +#[test] +fn rejects_empty_trait() { + assert_macro_rejects("cgp_type on an empty trait", || { + cgp_macro_lib::cgp_type( + quote!(), + quote!( + pub trait HasNothing {} + ), + ) + }); +} + +#[test] +fn rejects_method_item() { + // The single item must be an associated type, not a method. + assert_macro_rejects("cgp_type on a trait with a method", || { + cgp_macro_lib::cgp_type( + quote!(), + quote!( + pub trait HasScalar { + fn scalar(&self) -> f64; + } + ), + ) + }); +} + +#[test] +fn rejects_two_associated_types() { + assert_macro_rejects("cgp_type on a trait with two associated types", || { + cgp_macro_lib::cgp_type( + quote!(), + quote!( + pub trait HasTypes { + type Left; + type Right; + } + ), + ) + }); +} + +#[test] +fn rejects_generic_associated_type() { + assert_macro_rejects("cgp_type on a generic associated type", || { + cgp_macro_lib::cgp_type( + quote!(), + quote!( + pub trait HasContainer { + type Container; + } + ), + ) + }); +} + +#[test] +fn rejects_associated_type_where_clause() { + assert_macro_rejects("cgp_type on an associated type with a where clause", || { + cgp_macro_lib::cgp_type( + quote!(), + quote!( + pub trait HasScalar { + type Scalar + where + Self: Sized; + } + ), + ) + }); +} diff --git a/crates/tests/cgp-macro-tests/tests/parser_rejections/mod.rs b/crates/tests/cgp-macro-tests/tests/parser_rejections/mod.rs index b61d00cde..877862c2a 100644 --- a/crates/tests/cgp-macro-tests/tests/parser_rejections/mod.rs +++ b/crates/tests/cgp-macro-tests/tests/parser_rejections/mod.rs @@ -1,9 +1,10 @@ //! Failure cases: inputs the CGP macros must reject. //! -//! A rejection test drives a `cgp-macro-lib` entrypoint (or a `cgp-macro-core` -//! parser) with an invalid input and asserts it returns `Err` rather than -//! producing tokens. This is how we pin down which code CGP deliberately refuses, -//! and catch regressions where a macro starts accepting something it should not. +//! A rejection test drives a `cgp-macro-lib` or `cgp-extra-macro-lib` +//! entrypoint (or a `cgp-macro-core` parser) with an invalid input and asserts it +//! returns `Err` rather than producing tokens. This is how we pin down which code +//! CGP deliberately refuses, and catch regressions where a macro starts accepting +//! something it should not. //! //! To add a case: //! 1. call the entrypoint, e.g. `cgp_macro_lib::cgp_component(attr, body)`; @@ -22,11 +23,16 @@ pub fn assert_macro_rejects(label: &str, run: impl FnOnce() -> syn::Result &str; +} + +snapshot_blanket_trait! { + #[blanket_trait(Ctx)] + pub trait HasGreeting: HasName { + const GREETING: &'static str = "Hello"; + + fn greet(&self) -> String { + format!("{}, {}!", Self::GREETING, self.name()) + } + } + + expand_has_greeting(output) { + insta::assert_snapshot!(output, @r#" + pub trait HasGreeting: HasName { + const GREETING: &'static str = "Hello"; + fn greet(&self) -> String { + format!("{}, {}!", Self::GREETING, self.name()) + } + } + impl HasGreeting for Ctx + where + Ctx: HasName, + { + const GREETING: &'static str = "Hello"; + fn greet(&self) -> String { + format!("{}, {}!", Self::GREETING, self.name()) + } + } + "#) + } +} + +pub struct Context; + +impl HasName for Context { + fn name(&self) -> &str { + "Alice" + } +} + +#[test] +fn test_blanket_trait_const() { + assert_eq!(Context::GREETING, "Hello"); + assert_eq!(Context.greet(), "Hello, Alice!"); +} diff --git a/crates/tests/cgp-tests/tests/blanket_traits/mod.rs b/crates/tests/cgp-tests/tests/blanket_traits/mod.rs index 7b03871e6..49bb563b3 100644 --- a/crates/tests/cgp-tests/tests/blanket_traits/mod.rs +++ b/crates/tests/cgp-tests/tests/blanket_traits/mod.rs @@ -16,3 +16,6 @@ pub mod associated_type; // Same, but the associated type carries a bound (`: Clone`). pub mod associated_type_bounded; + +// Blanket trait forwarding an associated constant, under a custom context name. +pub mod const_custom_context; diff --git a/crates/tests/cgp-tests/tests/checking/delegate_and_check_skip.rs b/crates/tests/cgp-tests/tests/checking/delegate_and_check_skip.rs new file mode 100644 index 000000000..b613bdb46 --- /dev/null +++ b/crates/tests/cgp-tests/tests/checking/delegate_and_check_skip.rs @@ -0,0 +1,80 @@ +//! `delegate_and_check_components!` with a `#[skip_check]` entry beside checked +//! entries: the skipped key still gets its `DelegateComponent` and `IsProviderFor` +//! impls, but no check impl. `MyContext` has no `age` field, so checking +//! `AgeGetterComponent` would fail; the table compiling is what shows the skip +//! took effect, while the name entries are still checked. +//! +//! See cgp-knowledge-base/cgp/reference/macros/delegate_and_check_components.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_delegate_and_check_components; + +#[cgp_type] +pub trait HasNameType { + type Name; +} + +#[cgp_getter] +pub trait HasName: HasNameType { + fn name(&self) -> &Self::Name; +} + +#[cgp_getter] +pub trait HasAge { + fn age(&self) -> &u8; +} + +#[derive(HasField)] +pub struct MyContext { + pub name: String, +} + +snapshot_delegate_and_check_components! { + delegate_and_check_components! { + MyContext { + NameTypeProviderComponent: UseType, + NameGetterComponent: UseField, + #[skip_check] + AgeGetterComponent: UseField, + } + } + + expand_my_context(output) { + insta::assert_snapshot!(output, @r#" + impl DelegateComponent for MyContext { + type Delegate = UseType; + } + impl< + __Context__, + __Params__, + > IsProviderFor for MyContext + where + UseType: IsProviderFor, + {} + impl DelegateComponent for MyContext { + type Delegate = UseField; + } + impl<__Context__, __Params__> IsProviderFor + for MyContext + where + UseField< + Symbol!("name"), + >: IsProviderFor, + {} + impl DelegateComponent for MyContext { + type Delegate = UseField; + } + impl<__Context__, __Params__> IsProviderFor + for MyContext + where + UseField: IsProviderFor, + {} + trait __CanUseMyContext< + __Component__, + __Params__: ?Sized, + >: CanUseComponent<__Component__, __Params__> {} + impl __CanUseMyContext for MyContext {} + impl __CanUseMyContext for MyContext {} + "#) + } +} diff --git a/crates/tests/cgp-tests/tests/checking/mod.rs b/crates/tests/cgp-tests/tests/checking/mod.rs index 91a55bf4c..f5971c7fc 100644 --- a/crates/tests/cgp-tests/tests/checking/mod.rs +++ b/crates/tests/cgp-tests/tests/checking/mod.rs @@ -4,12 +4,14 @@ // `delegate_and_check_components!` snapshots (this concept owns the macro's // expansion): the basic wire-and-check step, its generic-context form, the -// generic-parameter `#[check_params]` / array-key form, and the per-key generic -// form whose key generics are threaded onto the derived check impl. +// generic-parameter `#[check_params]` / array-key form, the per-key generic +// form whose key generics are threaded onto the derived check impl, and a +// `#[skip_check]` entry beside checked ones. pub mod delegate_and_check_basic; pub mod delegate_and_check_generic; pub mod delegate_and_check_generic_key; pub mod delegate_and_check_params; +pub mod delegate_and_check_skip; // `check_components!` snapshots (this concept owns the macro's expansion): the // standalone check with `#[check_trait(...)]` overrides and per-entry parameter diff --git a/crates/tests/cgp-tests/tests/getters/mod.rs b/crates/tests/cgp-tests/tests/getters/mod.rs index 5893d1647..31ea84c09 100644 --- a/crates/tests/cgp-tests/tests/getters/mod.rs +++ b/crates/tests/cgp-tests/tests/getters/mod.rs @@ -10,6 +10,8 @@ pub mod auto_getter_generic; pub mod clone_auto; pub mod mref_auto; pub mod mut_option_auto; +pub mod mut_option_str_auto; +pub mod mut_ref_auto; pub mod mut_slice_auto; pub mod mut_str_auto; pub mod non_self_auto; diff --git a/crates/tests/cgp-tests/tests/getters/mut_option_str_auto.rs b/crates/tests/cgp-tests/tests/getters/mut_option_str_auto.rs new file mode 100644 index 000000000..fa425acd2 --- /dev/null +++ b/crates/tests/cgp-tests/tests/getters/mut_option_str_auto.rs @@ -0,0 +1,58 @@ +//! `#[cgp_auto_getter]` with a `&mut self` receiver returning `Option<&mut str>`: +//! the blanket impl reads the `Option` field mutably through +//! `get_field_mut` (bounded by `HasFieldMut`) and calls `.as_deref_mut()`, the +//! mutable mirror of `option_str_auto`. +//! +//! See cgp-knowledge-base/cgp/reference/macros/cgp_auto_getter.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_auto_getter; + +snapshot_cgp_auto_getter! { + #[cgp_auto_getter] + pub trait HasName { + fn name(&mut self) -> Option<&mut str>; + } + + expand_has_name(output) { + insta::assert_snapshot!(output, @" + pub trait HasName { + fn name(&mut self) -> Option<&mut str>; + } + impl<__Context__> HasName for __Context__ + where + __Context__: HasFieldMut< + Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, + Value = Option, + >, + { + fn name(&mut self) -> Option<&mut str> { + self.get_field_mut( + ::core::marker::PhantomData::< + Symbol<4, Chars<'n', Chars<'a', Chars<'m', Chars<'e', Nil>>>>>, + >, + ) + .as_deref_mut() + } + } + ") + } +} + +#[derive(HasField)] +pub struct App { + pub name: Option, +} + +#[test] +pub fn test_mut_option_str_auto_getter() { + let mut context = App { + name: Some("abc".to_owned()), + }; + + if let Some(name) = context.name() { + name.make_ascii_uppercase(); + } + + assert_eq!(context.name.as_deref(), Some("ABC")); +} diff --git a/crates/tests/cgp-tests/tests/getters/mut_ref_auto.rs b/crates/tests/cgp-tests/tests/getters/mut_ref_auto.rs new file mode 100644 index 000000000..1e565a9a5 --- /dev/null +++ b/crates/tests/cgp-tests/tests/getters/mut_ref_auto.rs @@ -0,0 +1,56 @@ +//! `#[cgp_auto_getter]` with a `&mut self` receiver returning a plain `&mut u32`: +//! the blanket impl reads the field mutably through `get_field_mut` (bounded by +//! `HasFieldMut`) and returns the borrow with no conversion, the mutable mirror of +//! a plain `&T` getter. +//! +//! See cgp-knowledge-base/cgp/reference/macros/cgp_auto_getter.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_auto_getter; + +snapshot_cgp_auto_getter! { + #[cgp_auto_getter] + pub trait HasCount { + fn count(&mut self) -> &mut u32; + } + + expand_has_count(output) { + insta::assert_snapshot!(output, @" + pub trait HasCount { + fn count(&mut self) -> &mut u32; + } + impl<__Context__> HasCount for __Context__ + where + __Context__: HasFieldMut< + Symbol<5, Chars<'c', Chars<'o', Chars<'u', Chars<'n', Chars<'t', Nil>>>>>>, + Value = u32, + >, + { + fn count(&mut self) -> &mut u32 { + self.get_field_mut( + ::core::marker::PhantomData::< + Symbol< + 5, + Chars<'c', Chars<'o', Chars<'u', Chars<'n', Chars<'t', Nil>>>>>, + >, + >, + ) + } + } + ") + } +} + +#[derive(HasField)] +pub struct App { + pub count: u32, +} + +#[test] +pub fn test_mut_ref_auto_getter() { + let mut context = App { count: 1 }; + + *context.count() += 1; + + assert_eq!(context.count, 2); +} diff --git a/crates/tests/cgp-tests/tests/namespaces/mod.rs b/crates/tests/cgp-tests/tests/namespaces/mod.rs index ea9f6c4e1..0af648dd8 100644 --- a/crates/tests/cgp-tests/tests/namespaces/mod.rs +++ b/crates/tests/cgp-tests/tests/namespaces/mod.rs @@ -4,11 +4,12 @@ // `cgp_namespace!` expansion snapshots (this concept owns the macro): the basic // form, the two path-segment shapes (`Type` vs. `symbol`), and multiple -// namespaces attached to one component. +// namespaces attached to one component; plus the bare `Path!` macro itself. pub mod namespace_basic; pub mod namespace_multi; pub mod namespace_symbol_path; pub mod namespace_type_path; +pub mod path_macro; // `#[prefix(...)]` + `namespace`/`@`-path wiring snapshots (this concept owns the // namespace forms of `delegate_components!`): attaching components to diff --git a/crates/tests/cgp-tests/tests/namespaces/path_macro.rs b/crates/tests/cgp-tests/tests/namespaces/path_macro.rs new file mode 100644 index 000000000..08c749e1b --- /dev/null +++ b/crates/tests/cgp-tests/tests/namespaces/path_macro.rs @@ -0,0 +1,38 @@ +//! The bare `Path!` macro, written directly rather than embedded in a namespace +//! entry or `#[prefix]` attribute. Each `assert_same_type` call compiles only if +//! the two types are identical, pinning the segment classification: a lowercase +//! segment becomes a `Symbol!`, a capitalized segment stays a type, a primitive +//! name such as `u32` stays the primitive, and the chain ends in `Nil`. +//! +//! See cgp-knowledge-base/cgp/reference/macros/path.md. + +use core::marker::PhantomData; + +use cgp::prelude::*; + +pub struct ErrorRaiserComponent; + +fn assert_same_type(_: PhantomData, _: PhantomData) {} + +#[test] +fn test_path_macro_segments() { + assert_same_type( + PhantomData::, + PhantomData::< + PathCons< + Symbol!("app"), + PathCons>, + >, + >, + ); + + assert_same_type( + PhantomData::, + PhantomData::>, + ); + + assert_same_type( + PhantomData::, + PhantomData::>>>, + ); +} From bc5febb235593623d1c7d0a17b3e4a60be0aea52 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 27 Sep 2026 04:16:32 +0000 Subject: [PATCH 3/5] Add component snapshots and argument rejections found in the KB review Snapshot a component trait with an associated const and one with an async method under #[async_trait], pinning const projection and .await forwarding in every generated impl. Pin the #[cgp_component] rejections of a repeated key, an unknown key, and a keyed form with no provider. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01HEMw6zp1hjNDZyxaKsszMj --- .../tests/parser_rejections/cgp_component.rs | 47 ++++++- .../tests/async_and_send/component_async.rs | 97 +++++++++++++ .../cgp-tests/tests/async_and_send/mod.rs | 4 + .../basic_delegation/component_assoc_const.rs | 127 ++++++++++++++++++ .../cgp-tests/tests/basic_delegation/mod.rs | 1 + 5 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 crates/tests/cgp-tests/tests/async_and_send/component_async.rs create mode 100644 crates/tests/cgp-tests/tests/basic_delegation/component_assoc_const.rs diff --git a/crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_component.rs b/crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_component.rs index ffd0f669e..862f00c62 100644 --- a/crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_component.rs +++ b/crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_component.rs @@ -1,5 +1,6 @@ //! `#[cgp_component]` rejects inputs it cannot lower into a component: a -//! non-trait item, and a trait carrying a const generic parameter. +//! non-trait item, a trait carrying a const generic parameter, and attribute +//! arguments with a repeated key, an unknown key, or no `provider` key. //! //! See cgp-knowledge-base/cgp/implementation/entrypoints/cgp_component.md (Tests) for these failure //! cases, and cgp-knowledge-base/cgp/reference/macros/cgp_component.md for the user-facing @@ -38,3 +39,47 @@ fn rejects_const_generic_parameter() { ) }); } + +#[test] +fn rejects_duplicate_key() { + // Each of `provider`, `context`, and `name` may appear at most once. + assert_macro_rejects("cgp_component with a repeated key", || { + cgp_macro_lib::cgp_component( + quote!(provider: FooProvider, provider: BarProvider), + quote!( + pub trait CanFoo { + fn foo(&self); + } + ), + ) + }); +} + +#[test] +fn rejects_unknown_key() { + assert_macro_rejects("cgp_component with an unknown key", || { + cgp_macro_lib::cgp_component( + quote!(provider: FooProvider, component: FooComponent), + quote!( + pub trait CanFoo { + fn foo(&self); + } + ), + ) + }); +} + +#[test] +fn rejects_missing_provider_key() { + // The keyed form has no default for `provider`. + assert_macro_rejects("cgp_component without a provider key", || { + cgp_macro_lib::cgp_component( + quote!(context: Ctx), + quote!( + pub trait CanFoo { + fn foo(&self); + } + ), + ) + }); +} diff --git a/crates/tests/cgp-tests/tests/async_and_send/component_async.rs b/crates/tests/cgp-tests/tests/async_and_send/component_async.rs new file mode 100644 index 000000000..e134a50c5 --- /dev/null +++ b/crates/tests/cgp-tests/tests/async_and_send/component_async.rs @@ -0,0 +1,97 @@ +//! `#[cgp_component]` on a trait with an `async` method, stacked with +//! `#[async_trait]`. +//! +//! The forwarding impls keep the `async` signature and append `.await` to each +//! delegated call, and `#[async_trait]` is forwarded onto every generated trait +//! and impl, where it rewrites the trait declarations and leaves the impls alone. +//! +//! See cgp-knowledge-base/cgp/implementation/functions/derive/delegated_impls.md and +//! cgp-knowledge-base/cgp/reference/macros/async_trait.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_component; + +snapshot_cgp_component! { + #[cgp_component(Fetcher)] + #[async_trait] + pub trait CanFetch { + async fn fetch(&self, id: u32) -> String; + } + + expand_can_fetch(output) { + insta::assert_snapshot!(output, @" + #[async_trait] + pub trait CanFetch { + async fn fetch(&self, id: u32) -> String; + } + #[async_trait] + impl<__Context__> CanFetch for __Context__ + where + __Context__: Fetcher<__Context__>, + { + async fn fetch(&self, id: u32) -> String { + __Context__::fetch(self, id).await + } + } + #[async_trait] + pub trait Fetcher<__Context__>: IsProviderFor { + async fn fetch(__context__: &__Context__, id: u32) -> String; + } + #[async_trait] + impl<__Provider__, __Context__> Fetcher<__Context__> for __Provider__ + where + __Provider__: DelegateComponent + + IsProviderFor, + <__Provider__ as DelegateComponent< + FetcherComponent, + >>::Delegate: Fetcher<__Context__>, + { + async fn fetch(__context__: &__Context__, id: u32) -> String { + <__Provider__ as DelegateComponent< + FetcherComponent, + >>::Delegate::fetch(__context__, id) + .await + } + } + pub struct FetcherComponent; + #[async_trait] + impl<__Context__> Fetcher<__Context__> for UseContext + where + __Context__: CanFetch, + { + async fn fetch(__context__: &__Context__, id: u32) -> String { + __Context__::fetch(__context__, id).await + } + } + impl<__Context__> IsProviderFor for UseContext + where + __Context__: CanFetch, + {} + #[async_trait] + impl<__Context__, __Components__, __Path__> Fetcher<__Context__> + for RedirectLookup<__Components__, __Path__> + where + __Components__: DelegateComponent<__Path__>, + <__Components__ as DelegateComponent<__Path__>>::Delegate: Fetcher<__Context__>, + { + async fn fetch(__context__: &__Context__, id: u32) -> String { + <__Components__ as DelegateComponent<__Path__>>::Delegate::fetch(__context__, id) + .await + } + } + impl< + __Context__, + __Components__, + __Path__, + > IsProviderFor + for RedirectLookup<__Components__, __Path__> + where + __Components__: DelegateComponent<__Path__>, + <__Components__ as DelegateComponent< + __Path__, + >>::Delegate: IsProviderFor + + Fetcher<__Context__>, + {} + ") + } +} diff --git a/crates/tests/cgp-tests/tests/async_and_send/mod.rs b/crates/tests/cgp-tests/tests/async_and_send/mod.rs index dfacf513f..0791df477 100644 --- a/crates/tests/cgp-tests/tests/async_and_send/mod.rs +++ b/crates/tests/cgp-tests/tests/async_and_send/mod.rs @@ -8,6 +8,10 @@ // proxy `SendRunner` impl on the concrete context. pub mod spawn; +// Async `#[cgp_component]` expansion: the forwarding impls keep `async` and +// append `.await`, with `#[async_trait]` forwarded onto every generated item. +pub mod component_async; + // Async `#[cgp_fn]` expansion: this concept owns the async variant of the // `#[cgp_fn]` macro snapshot (the `#[async_trait]` handling is the feature). pub mod cgp_fn_async; diff --git a/crates/tests/cgp-tests/tests/basic_delegation/component_assoc_const.rs b/crates/tests/cgp-tests/tests/basic_delegation/component_assoc_const.rs new file mode 100644 index 000000000..6c76f8288 --- /dev/null +++ b/crates/tests/cgp-tests/tests/basic_delegation/component_assoc_const.rs @@ -0,0 +1,127 @@ +//! `#[cgp_component]` on a trait carrying an associated const beside a method. +//! +//! Every forwarding impl re-exposes the const as a projection through the trait +//! on the other side: the consumer blanket impl reads it from the provider trait, +//! the provider blanket impl from the delegate's provider trait, and `UseContext` +//! from the consumer trait. The context below reads the const through wiring. +//! +//! See cgp-knowledge-base/cgp/implementation/functions/derive/delegated_impls.md and +//! cgp-knowledge-base/cgp/reference/macros/cgp_component.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_cgp_component; + +snapshot_cgp_component! { + #[cgp_component(LimitProvider)] + pub trait HasLimit { + const LIMIT: u32; + + fn limit(&self) -> u32; + } + + expand_has_limit(output) { + insta::assert_snapshot!(output, @" + pub trait HasLimit { + const LIMIT: u32; + fn limit(&self) -> u32; + } + impl<__Context__> HasLimit for __Context__ + where + __Context__: LimitProvider<__Context__>, + { + const LIMIT: u32 = <__Context__ as LimitProvider<__Context__>>::LIMIT; + fn limit(&self) -> u32 { + __Context__::limit(self) + } + } + pub trait LimitProvider< + __Context__, + >: IsProviderFor { + const LIMIT: u32; + fn limit(__context__: &__Context__) -> u32; + } + impl<__Provider__, __Context__> LimitProvider<__Context__> for __Provider__ + where + __Provider__: DelegateComponent + + IsProviderFor, + <__Provider__ as DelegateComponent< + LimitProviderComponent, + >>::Delegate: LimitProvider<__Context__>, + { + const LIMIT: u32 = <<__Provider__ as DelegateComponent< + LimitProviderComponent, + >>::Delegate as LimitProvider<__Context__>>::LIMIT; + fn limit(__context__: &__Context__) -> u32 { + <__Provider__ as DelegateComponent< + LimitProviderComponent, + >>::Delegate::limit(__context__) + } + } + pub struct LimitProviderComponent; + impl<__Context__> LimitProvider<__Context__> for UseContext + where + __Context__: HasLimit, + { + const LIMIT: u32 = <__Context__ as HasLimit>::LIMIT; + fn limit(__context__: &__Context__) -> u32 { + __Context__::limit(__context__) + } + } + impl<__Context__> IsProviderFor for UseContext + where + __Context__: HasLimit, + {} + impl<__Context__, __Components__, __Path__> LimitProvider<__Context__> + for RedirectLookup<__Components__, __Path__> + where + __Components__: DelegateComponent<__Path__>, + <__Components__ as DelegateComponent< + __Path__, + >>::Delegate: LimitProvider<__Context__>, + { + const LIMIT: u32 = <<__Components__ as DelegateComponent< + __Path__, + >>::Delegate as LimitProvider<__Context__>>::LIMIT; + fn limit(__context__: &__Context__) -> u32 { + <__Components__ as DelegateComponent<__Path__>>::Delegate::limit(__context__) + } + } + impl< + __Context__, + __Components__, + __Path__, + > IsProviderFor + for RedirectLookup<__Components__, __Path__> + where + __Components__: DelegateComponent<__Path__>, + <__Components__ as DelegateComponent< + __Path__, + >>::Delegate: IsProviderFor + + LimitProvider<__Context__>, + {} + ") + } +} + +#[cgp_impl(new TenLimit)] +impl LimitProvider { + const LIMIT: u32 = 10; + + fn limit(&self) -> u32 { + 10 + } +} + +pub struct App; + +delegate_components! { + App { + LimitProviderComponent: TenLimit, + } +} + +#[test] +fn test_component_assoc_const() { + assert_eq!(::LIMIT, 10); + assert_eq!(App.limit(), 10); +} diff --git a/crates/tests/cgp-tests/tests/basic_delegation/mod.rs b/crates/tests/cgp-tests/tests/basic_delegation/mod.rs index a0a4d6a02..34d9d2763 100644 --- a/crates/tests/cgp-tests/tests/basic_delegation/mod.rs +++ b/crates/tests/cgp-tests/tests/basic_delegation/mod.rs @@ -4,6 +4,7 @@ // The macro-expansion snapshots that this concept owns: the canonical // `#[cgp_component]`, `#[cgp_impl]`, and `delegate_components!` output. +pub mod component_assoc_const; pub mod component_macro; pub mod delegate_array_key; pub mod delegate_components_macro; From 32b4d0bfb3c39ba522225f8b20ec672e93b261a0 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Sun, 27 Sep 2026 10:43:22 +0000 Subject: [PATCH 4/5] Fix the #[blanket_trait] rustdoc example The example used the pre-release #[trait_alias] name and the removed Async trait. Show a current #[blanket_trait] with the __Context__ parameter the macro emits. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01HEMw6zp1hjNDZyxaKsszMj --- crates/macros/cgp-macro/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/macros/cgp-macro/src/lib.rs b/crates/macros/cgp-macro/src/lib.rs index aef7ae3a2..4f428aa3a 100644 --- a/crates/macros/cgp-macro/src/lib.rs +++ b/crates/macros/cgp-macro/src/lib.rs @@ -709,16 +709,16 @@ pub fn cgp_type(attrs: TokenStream, body: TokenStream) -> TokenStream { Given the following: ```rust,ignore - #[trait_alias] - pub trait HasErrorType: Async + HasErrorType {} + #[blanket_trait] + pub trait HasSendError: HasErrorType {} ``` automatically generates the following blanket implementation: ```rust,ignore - impl HasErrorType for Context + impl<__Context__> HasSendError for __Context__ where - Context: Async + HasErrorType {} + __Context__: HasErrorType {} ``` */ #[proc_macro_attribute] From 8d64edaf90f163c706b55874fb7a5ab97ee92cd9 Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Mon, 28 Sep 2026 13:38:02 +0000 Subject: [PATCH 5/5] Fix the five defects the reference revision deferred - #[cgp_component] rejects a name: parameter the trait does not declare, with a spanned error instead of E0425 in the generated impls. - #[check_providers] takes the context as a trait parameter, so a generic table's parameters stay on the impls that declare them. - The forwarding IsProviderFor impl of delegate_components! declares __Params__: ?Sized, so a component used at an unsized parameter resolves through a table. - A table struct delegate_components! declares keeps a const parameter's kind, for a new target and for a nested inner table. - #[cgp_auto_dispatch] names its per-variant helper __compute_{method}__ and builds both generated names from the unrawed method identifier. Each fix has a regression test, and the affected snapshots are re-accepted. Co-Authored-By: Claude Opus 5.5 --- .../src/entrypoints/cgp_auto_dispatch.rs | 33 +++-- .../src/types/cgp_component/item.rs | 49 ++++++- .../src/types/check_components/table.rs | 17 ++- .../types/delegate_component/mapping/eval.rs | 6 +- .../types/delegate_component/table/main.rs | 28 +++- .../delegate_component/value/inner_table.rs | 7 +- .../src/types/generics/type_generics.rs | 19 ++- .../tests/parser_rejections/cgp_component.rs | 38 +++++- .../basic_delegation/delegate_array_key.rs | 6 +- .../delegate_components_macro.rs | 8 +- .../delegate_generic_table.rs | 6 +- .../tests/checking/check_providers.rs | 54 ++++---- .../tests/checking/check_providers_generic.rs | 60 ++++++++ .../checking/delegate_and_check_basic.rs | 8 +- .../checking/delegate_and_check_generic.rs | 4 +- .../delegate_and_check_generic_key.rs | 6 +- .../checking/delegate_and_check_params.rs | 8 +- .../tests/checking/delegate_and_check_skip.rs | 14 +- crates/tests/cgp-tests/tests/checking/mod.rs | 1 + .../auto_dispatch_method_name_in_scope.rs | 39 ++++++ .../auto_dispatch_raw_method_name.rs | 40 ++++++ .../tests/dispatching/const_generic_tables.rs | 129 ++++++++++++++++++ .../tests/cgp-tests/tests/dispatching/mod.rs | 10 +- .../tests/dispatching/use_delegate_getter.rs | 24 ++-- .../cgp-tests/tests/generic_components/mod.rs | 5 + .../generic_components/unsized_parameter.rs | 58 ++++++++ .../tests/namespaces/combined_forms.rs | 18 +-- .../tests/namespaces/default_impls_wiring.rs | 16 +-- .../namespaces/extended_namespace_wiring.rs | 12 +- .../tests/namespaces/for_where_clause.rs | 2 +- .../tests/namespaces/multi_param_namespace.rs | 6 +- .../tests/namespaces/multi_param_open.rs | 6 +- .../tests/namespaces/namespace_basic.rs | 6 +- .../tests/namespaces/namespace_group.rs | 10 +- .../tests/namespaces/namespace_multi.rs | 12 +- .../namespaces/namespace_nested_table.rs | 6 +- .../tests/namespaces/namespace_symbol_path.rs | 6 +- .../tests/namespaces/namespace_type_path.rs | 6 +- .../tests/namespaces/open_dispatch.rs | 16 +-- .../tests/namespaces/open_later_param.rs | 10 +- .../namespaces/prefix_default_namespace.rs | 6 +- .../tests/namespaces/redirect_lookup.rs | 4 +- .../tests/namespaces/redirect_mapping.rs | 14 +- 43 files changed, 667 insertions(+), 166 deletions(-) create mode 100644 crates/tests/cgp-tests/tests/checking/check_providers_generic.rs create mode 100644 crates/tests/cgp-tests/tests/dispatching/auto_dispatch_method_name_in_scope.rs create mode 100644 crates/tests/cgp-tests/tests/dispatching/auto_dispatch_raw_method_name.rs create mode 100644 crates/tests/cgp-tests/tests/dispatching/const_generic_tables.rs create mode 100644 crates/tests/cgp-tests/tests/generic_components/unsized_parameter.rs diff --git a/crates/macros/cgp-extra-macro-lib/src/entrypoints/cgp_auto_dispatch.rs b/crates/macros/cgp-extra-macro-lib/src/entrypoints/cgp_auto_dispatch.rs index 072f3439d..1fef7a776 100644 --- a/crates/macros/cgp-extra-macro-lib/src/entrypoints/cgp_auto_dispatch.rs +++ b/crates/macros/cgp-extra-macro-lib/src/entrypoints/cgp_auto_dispatch.rs @@ -3,6 +3,7 @@ use std::collections::BTreeSet; use cgp_macro_core::functions::to_camel_case_str; use proc_macro2::{Span, TokenStream}; use quote::quote; +use syn::ext::IdentExt; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::token::Comma; @@ -64,10 +65,7 @@ fn derive_blanket_impl(item_trait: &ItemTrait) -> syn::Result { let method_ident = &signature.ident; let mut hrtbs: BTreeSet = BTreeSet::new(); - let computer_ident = Ident::new( - &format!("Compute{}", to_camel_case_str(&method_ident.to_string())), - method_ident.span(), - ); + let computer_ident = derive_computer_ident(method_ident); for generic_param in signature.generics.params.iter() { match generic_param { @@ -396,10 +394,7 @@ fn derive_method_computer( TokenStream::new() }; - let computer_ident = Ident::new( - &format!("Compute{}", to_camel_case_str(&method_ident.to_string())), - method_ident.span(), - ); + let computer_ident = derive_computer_ident(method_ident); let method_generics = { let method_generics = method @@ -419,9 +414,16 @@ fn derive_method_computer( let (impl_generics, _, where_clause) = generics.split_for_impl(); + // The helper function takes a reserved name rather than the method's own, so it + // cannot collide with an item of the same name already in the caller's module. + let helper_ident = Ident::new( + &format!("__compute_{}__", method_ident.unraw()), + method_ident.span(), + ); + Ok(quote! { #[cgp_computer( #computer_ident )] - #async_token fn #method_ident #impl_generics ( + #async_token fn #helper_ident #impl_generics ( #context_ident: #context_type, #arg_params ) #return_type @@ -431,3 +433,16 @@ fn derive_method_computer( } }) } + +/// The per-variant computer's name, `Compute` plus the method name in PascalCase. The +/// method name is unrawed first, so `r#type` yields `ComputeType` rather than an +/// invalid identifier. +fn derive_computer_ident(method_ident: &Ident) -> Ident { + Ident::new( + &format!( + "Compute{}", + to_camel_case_str(&method_ident.unraw().to_string()) + ), + method_ident.span(), + ) +} diff --git a/crates/macros/cgp-macro-core/src/types/cgp_component/item.rs b/crates/macros/cgp-macro-core/src/types/cgp_component/item.rs index daeefb0a6..fdfc80714 100644 --- a/crates/macros/cgp-macro-core/src/types/cgp_component/item.rs +++ b/crates/macros/cgp-macro-core/src/types/cgp_component/item.rs @@ -1,7 +1,8 @@ -use syn::ItemTrait; +use syn::{Error, GenericParam, ItemTrait}; use crate::types::attributes::CgpComponentAttributes; use crate::types::cgp_component::{CgpComponentArgs, PreprocessedCgpComponent}; +use crate::types::ident::TypeGenericParam; /// Raw input stage: the parsed attribute args and trait, before CGP attributes /// are stripped. First stage of the `#[cgp_component]` pipeline. @@ -13,6 +14,8 @@ pub struct ItemCgpComponent { impl ItemCgpComponent { /// Split the CGP modifier attributes off the trait, yielding the next stage. pub fn preprocess(&self) -> syn::Result { + self.check_component_name_params()?; + let (attributes, item_trait) = CgpComponentAttributes::preprocess(&self.item_trait)?; Ok(PreprocessedCgpComponent { @@ -21,4 +24,48 @@ impl ItemCgpComponent { attributes, }) } + + /// Reject a `name:` parameter the trait does not declare. The marker struct is + /// declared with the name's parameters and the generated impls name it with the + /// trait's, so an undeclared one would otherwise fail downstream with `E0425`. + fn check_component_name_params(&self) -> syn::Result<()> { + let trait_generics = &self.item_trait.generics; + + for param in self.args.component_name.type_generics.params.iter() { + let (declared, name) = match param { + TypeGenericParam::Lifetime(life) => ( + trait_generics.params.iter().any( + |p| matches!(p, GenericParam::Lifetime(l) if l.lifetime.ident == life.ident), + ), + life.to_string(), + ), + TypeGenericParam::Type(ident) => ( + trait_generics + .params + .iter() + .any(|p| matches!(p, GenericParam::Type(t) if &t.ident == ident)), + ident.to_string(), + ), + TypeGenericParam::Const(param) => ( + trait_generics + .params + .iter() + .any(|p| matches!(p, GenericParam::Const(c) if c.ident == param.ident)), + param.ident.to_string(), + ), + }; + + if !declared { + return Err(Error::new_spanned( + param, + format!( + "the component name's parameter `{name}` is not a generic parameter of the trait `{}`", + self.item_trait.ident, + ), + )); + } + } + + Ok(()) + } } 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 b9c08c286..d5b67504e 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 @@ -1,4 +1,4 @@ -use quote::ToTokens; +use quote::{ToTokens, quote}; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; @@ -40,9 +40,13 @@ impl CheckComponentsTable { let impl_generics = &self.impl_generics; let where_clause = &self.where_clause; + // The `#[check_providers]` form takes the context as a trait parameter rather + // than naming it in the supertrait, so a generic table's parameters (` + // Wrapper`) stay on the impls that declare them instead of appearing, + // undeclared, in the trait header. let item_trait: ItemTrait = if self.check_providers.is_some() { parse_internal! { - trait #trait_name <__Component__, __Params__: ?Sized>: #IsProviderFor<__Component__, #context_type, __Params__> {} + trait #trait_name <__Component__, __Context__, __Params__: ?Sized>: #IsProviderFor<__Component__, __Context__, __Params__> {} } } else { parse_internal! { @@ -78,9 +82,16 @@ impl CheckComponentsTable { let impl_generics = generics.split_for_impl().0; for self_type in self_types { + // Only the `#[check_providers]` trait takes the context as an argument. + let trait_args = if self.check_providers.is_some() { + quote! { #component_type, #context_type, #component_param } + } else { + quote! { #component_type, #component_param } + }; + let item_impl: ItemImpl = parse_internal! { impl #impl_generics - #trait_name < #component_type, #component_param > + #trait_name < #trait_args > for #self_type #where_clause {} diff --git a/crates/macros/cgp-macro-core/src/types/delegate_component/mapping/eval.rs b/crates/macros/cgp-macro-core/src/types/delegate_component/mapping/eval.rs index fd4c1c62d..dc83203c9 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_component/mapping/eval.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_component/mapping/eval.rs @@ -58,7 +58,9 @@ impl EvaluatedDelegateEntry { /// Emit the forwarding `IsProviderFor` impl, /// bounded on the value being a provider for the same key so a missing - /// transitive dependency stays diagnosable. + /// transitive dependency stays diagnosable. `__Params__` is `?Sized`, as on + /// the trait, so a component used at an unsized parameter (`str`, making the + /// params tuple `(Life<'a>, str)` unsized) still resolves through the table. pub fn build_is_provider_for_impl(&self, outer_generics: &Generics) -> syn::Result { let table_type = &self.table_type; @@ -68,7 +70,7 @@ impl EvaluatedDelegateEntry { let value = &self.value; generics.params.push(parse_internal!(__Context__)); - generics.params.push(parse_internal!(__Params__)); + generics.params.push(parse_internal!(__Params__: ?Sized)); generics .make_where_clause() 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 b98d50229..ae05aae2d 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::{Attribute, ItemImpl, Type, braced}; +use syn::{Attribute, GenericParam, ItemImpl, Type, braced}; use crate::functions::parse_internal; use crate::traits::ParseOptionalKeyword; @@ -67,9 +67,33 @@ impl DelegateTable { if self.new.is_some() { let struct_type: IdentWithTypeGenerics = parse_internal(self.table_type.to_token_stream())?; + + // The target's type arguments name each parameter without its kind, so a + // parameter the table's generic list declares `const` is restored as a + // const parameter of the struct rather than read as a type parameter. + let mut generics = struct_type.type_generics.to_generics(); + for param in generics.params.iter_mut() { + if let GenericParam::Type(type_param) = param + && let Some(const_param) = + self.impl_generics + .generics + .params + .iter() + .find_map(|p| match p { + GenericParam::Const(c) if c.ident == type_param.ident => Some(c), + _ => None, + }) + { + let mut const_param = const_param.clone(); + const_param.eq_token = None; + const_param.default = None; + *param = GenericParam::Const(const_param); + } + } + item_structs.push(EmptyStruct { ident: struct_type.ident, - generics: struct_type.type_generics.to_generics(), + generics, }); } diff --git a/crates/macros/cgp-macro-core/src/types/delegate_component/value/inner_table.rs b/crates/macros/cgp-macro-core/src/types/delegate_component/value/inner_table.rs index 8da510d5e..2f417be06 100644 --- a/crates/macros/cgp-macro-core/src/types/delegate_component/value/inner_table.rs +++ b/crates/macros/cgp-macro-core/src/types/delegate_component/value/inner_table.rs @@ -42,10 +42,11 @@ impl Parse for DelegateValueWithInnerTable { impl EvalDelegateValue for DelegateValueWithInnerTable { fn eval(&self) -> syn::Result { let wrapper_ident = &self.wrapper_ident; - let struct_ident = &self.inner_table.table_ident; - let struct_generics = &self.inner_table.table_generics; + // The table's type arguments, not its definition list, so a + // `const N: usize` parameter is written as `N` in the value. + let table_type = self.inner_table.build_table_type()?; - let ty = parse_internal!( #wrapper_ident < #struct_ident #struct_generics > ); + let ty = parse_internal!( #wrapper_ident < #table_type > ); Ok(ty) } } diff --git a/crates/macros/cgp-macro-core/src/types/generics/type_generics.rs b/crates/macros/cgp-macro-core/src/types/generics/type_generics.rs index 474a05d98..d62d11d59 100644 --- a/crates/macros/cgp-macro-core/src/types/generics/type_generics.rs +++ b/crates/macros/cgp-macro-core/src/types/generics/type_generics.rs @@ -3,7 +3,7 @@ use core::ops::{Deref, DerefMut}; use proc_macro2::TokenStream; use quote::ToTokens; use syn::parse::{Parse, ParseStream}; -use syn::{Error, Generics}; +use syn::{Error, GenericParam, Generics}; use crate::functions::parse_internal; @@ -39,14 +39,25 @@ impl DerefMut for TypeGenerics { } impl Parse for TypeGenerics { + /// Accept a definition-site list whose parameters carry no bounds and no + /// defaults: bare lifetimes, bare type parameters, and `const N: T` parameters, + /// whose kind is kept so a declared table struct can be const-generic. fn parse(input: ParseStream) -> syn::Result { let generics: Generics = input.parse()?; - let (_, type_generics, _) = generics.split_for_impl(); + for param in generics.params.iter() { + let valid = match param { + GenericParam::Lifetime(life) => life.colon_token.is_none(), + GenericParam::Type(ty) => ty.colon_token.is_none() && ty.default.is_none(), + GenericParam::Const(konst) => konst.default.is_none(), + }; - let generics2: Generics = parse_internal(type_generics.to_token_stream())?; + if !valid { + return Err(Error::new_spanned(param, "invalid type generics syntax")); + } + } - if generics != generics2 { + if generics.where_clause.is_some() { return Err(Error::new_spanned(generics, "invalid type generics syntax")); } diff --git a/crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_component.rs b/crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_component.rs index 862f00c62..5f88ad0f1 100644 --- a/crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_component.rs +++ b/crates/tests/cgp-macro-tests/tests/parser_rejections/cgp_component.rs @@ -1,6 +1,7 @@ //! `#[cgp_component]` rejects inputs it cannot lower into a component: a -//! non-trait item, a trait carrying a const generic parameter, and attribute -//! arguments with a repeated key, an unknown key, or no `provider` key. +//! non-trait item, a trait carrying a const generic parameter, attribute +//! arguments with a repeated key, an unknown key, or no `provider` key, and a +//! `name` whose parameters the trait does not declare. //! //! See cgp-knowledge-base/cgp/implementation/entrypoints/cgp_component.md (Tests) for these failure //! cases, and cgp-knowledge-base/cgp/reference/macros/cgp_component.md for the user-facing @@ -40,6 +41,39 @@ fn rejects_const_generic_parameter() { }); } +#[test] +fn rejects_component_name_parameter_the_trait_lacks() { + // The marker struct is declared with the name's parameters while the generated + // impls name it with the trait's, so a parameter only the name carries would + // fail downstream (`E0425`); the macro rejects it with a spanned error instead. + assert_macro_rejects("cgp_component with an undeclared name parameter", || { + cgp_macro_lib::cgp_component( + quote!(provider: AreaCalculator, name: AreaCalculatorComponent), + quote!( + pub trait CanCalculateArea { + fn area(&self) -> f64; + } + ), + ) + }); +} + +#[test] +fn accepts_component_name_parameter_the_trait_declares() { + // The same `name` form with a parameter the trait does declare expands. + assert!( + cgp_macro_lib::cgp_component( + quote!(provider: AreaCalculator, name: AreaCalculatorComponent), + quote!( + pub trait CanCalculateArea { + fn area(&self, shape: &Shape) -> f64; + } + ), + ) + .is_ok() + ); +} + #[test] fn rejects_duplicate_key() { // Each of `provider`, `context`, and `name` may appear at most once. diff --git a/crates/tests/cgp-tests/tests/basic_delegation/delegate_array_key.rs b/crates/tests/cgp-tests/tests/basic_delegation/delegate_array_key.rs index 895820b78..d07a684a4 100644 --- a/crates/tests/cgp-tests/tests/basic_delegation/delegate_array_key.rs +++ b/crates/tests/cgp-tests/tests/basic_delegation/delegate_array_key.rs @@ -34,7 +34,7 @@ snapshot_delegate_components! { impl DelegateComponent for Components { type Delegate = FooValue; } - impl<__Context__, __Params__> IsProviderFor + impl<__Context__, __Params__: ?Sized> IsProviderFor for Components where FooValue: IsProviderFor, @@ -42,7 +42,7 @@ snapshot_delegate_components! { impl DelegateComponent for Components { type Delegate = BarValue; } - impl<__Context__, __Params__> IsProviderFor + impl<__Context__, __Params__: ?Sized> IsProviderFor for Components where BarValue: IsProviderFor, @@ -50,7 +50,7 @@ snapshot_delegate_components! { impl DelegateComponent for Components { type Delegate = BarValue; } - impl<__Context__, __Params__> IsProviderFor + impl<__Context__, __Params__: ?Sized> IsProviderFor for Components where BarValue: IsProviderFor, diff --git a/crates/tests/cgp-tests/tests/basic_delegation/delegate_components_macro.rs b/crates/tests/cgp-tests/tests/basic_delegation/delegate_components_macro.rs index 243e6e700..d3917dd87 100644 --- a/crates/tests/cgp-tests/tests/basic_delegation/delegate_components_macro.rs +++ b/crates/tests/cgp-tests/tests/basic_delegation/delegate_components_macro.rs @@ -25,7 +25,7 @@ snapshot_delegate_components! { impl DelegateComponent> for FooComponents { type Delegate = u64; } - impl<__Context__, __Params__> IsProviderFor, __Context__, __Params__> + impl<__Context__, __Params__: ?Sized> IsProviderFor, __Context__, __Params__> for FooComponents where u64: IsProviderFor, __Context__, __Params__>, @@ -33,7 +33,7 @@ snapshot_delegate_components! { impl DelegateComponent> for FooComponents { type Delegate = String; } - impl<__Context__, __Params__> IsProviderFor, __Context__, __Params__> + impl<__Context__, __Params__: ?Sized> IsProviderFor, __Context__, __Params__> for FooComponents where String: IsProviderFor, __Context__, __Params__>, @@ -60,7 +60,7 @@ snapshot_delegate_components! { impl DelegateComponent> for BarComponents { type Delegate = FooComponents; } - impl<__Context__, __Params__> IsProviderFor, __Context__, __Params__> + impl<__Context__, __Params__: ?Sized> IsProviderFor, __Context__, __Params__> for BarComponents where FooComponents: IsProviderFor, __Context__, __Params__>, @@ -71,7 +71,7 @@ snapshot_delegate_components! { { type Delegate = >>::Delegate; } - impl<__Context__, __Params__> IsProviderFor, __Context__, __Params__> + impl<__Context__, __Params__: ?Sized> IsProviderFor, __Context__, __Params__> for BarComponents where FooComponents: DelegateComponent>, diff --git a/crates/tests/cgp-tests/tests/basic_delegation/delegate_generic_table.rs b/crates/tests/cgp-tests/tests/basic_delegation/delegate_generic_table.rs index 29dd1d191..9da79c0d6 100644 --- a/crates/tests/cgp-tests/tests/basic_delegation/delegate_generic_table.rs +++ b/crates/tests/cgp-tests/tests/basic_delegation/delegate_generic_table.rs @@ -46,7 +46,7 @@ snapshot_delegate_components! { 'a, T1: Clone, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor, __Context__, __Params__> for Components where FooValue: IsProviderFor, __Context__, __Params__>, @@ -58,7 +58,7 @@ snapshot_delegate_components! { 'a, T1: Clone, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor, __Context__, __Params__> for Components where BarValue: IsProviderFor, __Context__, __Params__>, @@ -71,7 +71,7 @@ snapshot_delegate_components! { T1: Clone, T2, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor, __Context__, __Params__> for Components where BarValue: IsProviderFor, __Context__, __Params__>, diff --git a/crates/tests/cgp-tests/tests/checking/check_providers.rs b/crates/tests/cgp-tests/tests/checking/check_providers.rs index 52b691e53..8a07907e4 100644 --- a/crates/tests/cgp-tests/tests/checking/check_providers.rs +++ b/crates/tests/cgp-tests/tests/checking/check_providers.rs @@ -1,9 +1,11 @@ //! `check_components!` with `#[check_providers(...)]`: instead of checking that the //! context can use each component through its wiring, this form checks that each //! *listed provider* is a valid provider of each component for the context. The -//! generated check trait therefore supertraits `IsProviderFor<_, Context, _>` and -//! is implemented `for` each provider (here two `UseField` variants), rather than -//! `for` the context. This concept owns the macro's expansion snapshot. +//! generated check trait therefore takes the context as a `__Context__` parameter, +//! supertraits `IsProviderFor<_, __Context__, _>`, and is implemented `for` each +//! provider (here two `UseField` variants) at `Context`, rather than `for` the +//! context. This concept owns the macro's expansion snapshot; `check_providers_generic` +//! pins the generic-table variant. //! //! See cgp-knowledge-base/cgp/reference/macros/check_components.md and //! cgp-knowledge-base/cgp/reference/traits/can_use_component.md. @@ -91,44 +93,48 @@ snapshot_check_components! { insta::assert_snapshot!(output, @r#" trait CanUseDummyField< __Component__, + __Context__, __Params__: ?Sized, - >: IsProviderFor<__Component__, Context, __Params__> {} - impl CanUseDummyField> for UseField {} - impl CanUseDummyField> + >: IsProviderFor<__Component__, __Context__, __Params__> {} + impl CanUseDummyField> + for UseField {} + impl CanUseDummyField> for UseField {} - impl CanUseDummyField> for UseField {} - impl CanUseDummyField> + impl CanUseDummyField> + for UseField {} + impl CanUseDummyField> for UseField {} - impl CanUseDummyField> for UseField {} - impl CanUseDummyField> + impl CanUseDummyField> + for UseField {} + impl CanUseDummyField> for UseField {} - impl CanUseDummyField, Index<1>)> + impl CanUseDummyField, Index<1>)> for UseField {} - impl CanUseDummyField, Index<1>)> + impl CanUseDummyField, Index<1>)> for UseField {} - impl CanUseDummyField, Index<0>)> + impl CanUseDummyField, Index<0>)> for UseField {} - impl CanUseDummyField, Index<0>)> + impl CanUseDummyField, Index<0>)> for UseField {} - impl CanUseDummyField, Index<4>)> + impl CanUseDummyField, Index<4>)> for UseField {} - impl CanUseDummyField, Index<4>)> + impl CanUseDummyField, Index<4>)> for UseField {} - impl CanUseDummyField, Index<6>)> + impl CanUseDummyField, Index<6>)> for UseField {} - impl CanUseDummyField, Index<6>)> + impl CanUseDummyField, Index<6>)> for UseField {} - impl CanUseDummyField, Index<8>)> + impl CanUseDummyField, Index<8>)> for UseField {} - impl CanUseDummyField, Index<8>)> + impl CanUseDummyField, Index<8>)> for UseField {} - impl CanUseDummyField, Index<6>)> + impl CanUseDummyField, Index<6>)> for UseField {} - impl CanUseDummyField, Index<6>)> + impl CanUseDummyField, Index<6>)> for UseField {} - impl CanUseDummyField, Index<8>)> + impl CanUseDummyField, Index<8>)> for UseField {} - impl CanUseDummyField, Index<8>)> + impl CanUseDummyField, Index<8>)> for UseField {} "#) } diff --git a/crates/tests/cgp-tests/tests/checking/check_providers_generic.rs b/crates/tests/cgp-tests/tests/checking/check_providers_generic.rs new file mode 100644 index 000000000..1c99428a4 --- /dev/null +++ b/crates/tests/cgp-tests/tests/checking/check_providers_generic.rs @@ -0,0 +1,60 @@ +//! `check_components!` with `#[check_providers(...)]` over a *generic* table +//! (` Wrapper`). The check trait takes the context as a parameter, so the +//! table's `T` stays on the generated impls that declare it rather than appearing, +//! undeclared, in the trait's supertrait. The snapshot pins the `impl` impl the +//! generic table produces, and the test checks that the checked provider serves the +//! context at run time. +//! +//! See cgp-knowledge-base/cgp/implementation/entrypoints/check_components.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_check_components; + +#[cgp_getter] +pub trait HasName { + fn name(&self) -> &str; +} + +#[derive(HasField)] +pub struct Wrapper { + pub name: String, + pub inner: T, +} + +delegate_components! { + Wrapper { + NameGetterComponent: UseField, + } +} + +snapshot_check_components! { + check_components! { + #[check_trait(CheckWrapperProviders)] + #[check_providers(UseField)] + Wrapper { + NameGetterComponent, + } + } + + expand_generic_check_providers(output) { + insta::assert_snapshot!(output, @r#" + trait CheckWrapperProviders< + __Component__, + __Context__, + __Params__: ?Sized, + >: IsProviderFor<__Component__, __Context__, __Params__> {} + impl CheckWrapperProviders, ()> + for UseField {} + "#) + } +} + +#[test] +fn test_check_providers_over_a_generic_table() { + let wrapper = Wrapper { + name: "wrapped".to_owned(), + inner: 5u8, + }; + assert_eq!(wrapper.name(), "wrapped"); + assert_eq!(wrapper.inner, 5); +} diff --git a/crates/tests/cgp-tests/tests/checking/delegate_and_check_basic.rs b/crates/tests/cgp-tests/tests/checking/delegate_and_check_basic.rs index f732a8d60..195ed0361 100644 --- a/crates/tests/cgp-tests/tests/checking/delegate_and_check_basic.rs +++ b/crates/tests/cgp-tests/tests/checking/delegate_and_check_basic.rs @@ -40,7 +40,7 @@ snapshot_delegate_and_check_components! { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for MyContext where UseType: IsProviderFor, @@ -48,8 +48,10 @@ snapshot_delegate_and_check_components! { impl DelegateComponent for MyContext { type Delegate = UseField; } - impl<__Context__, __Params__> IsProviderFor - for MyContext + impl< + __Context__, + __Params__: ?Sized, + > IsProviderFor for MyContext where UseField< Symbol!("name"), diff --git a/crates/tests/cgp-tests/tests/checking/delegate_and_check_generic.rs b/crates/tests/cgp-tests/tests/checking/delegate_and_check_generic.rs index 6a0648484..f0fc97199 100644 --- a/crates/tests/cgp-tests/tests/checking/delegate_and_check_generic.rs +++ b/crates/tests/cgp-tests/tests/checking/delegate_and_check_generic.rs @@ -41,7 +41,7 @@ snapshot_delegate_and_check_components! { impl< T, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for MyContext where UseType: IsProviderFor, @@ -52,7 +52,7 @@ snapshot_delegate_and_check_components! { impl< T, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for MyContext where UseField< diff --git a/crates/tests/cgp-tests/tests/checking/delegate_and_check_generic_key.rs b/crates/tests/cgp-tests/tests/checking/delegate_and_check_generic_key.rs index c6e217889..6f812898e 100644 --- a/crates/tests/cgp-tests/tests/checking/delegate_and_check_generic_key.rs +++ b/crates/tests/cgp-tests/tests/checking/delegate_and_check_generic_key.rs @@ -67,7 +67,7 @@ snapshot_delegate_and_check_components! { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for Context where UseType<()>: IsProviderFor, @@ -77,7 +77,7 @@ snapshot_delegate_and_check_components! { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for Context where UseType<()>: IsProviderFor, @@ -88,7 +88,7 @@ snapshot_delegate_and_check_components! { impl< I, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor, __Context__, __Params__> for Context where UseField< diff --git a/crates/tests/cgp-tests/tests/checking/delegate_and_check_params.rs b/crates/tests/cgp-tests/tests/checking/delegate_and_check_params.rs index 5d6d2788d..e8c35a317 100644 --- a/crates/tests/cgp-tests/tests/checking/delegate_and_check_params.rs +++ b/crates/tests/cgp-tests/tests/checking/delegate_and_check_params.rs @@ -79,7 +79,7 @@ snapshot_delegate_and_check_components! { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for Context where UseType<()>: IsProviderFor, @@ -89,7 +89,7 @@ snapshot_delegate_and_check_components! { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for Context where UseType<()>: IsProviderFor, @@ -99,7 +99,7 @@ snapshot_delegate_and_check_components! { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for Context where UseField< @@ -111,7 +111,7 @@ snapshot_delegate_and_check_components! { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for Context where UseField< diff --git a/crates/tests/cgp-tests/tests/checking/delegate_and_check_skip.rs b/crates/tests/cgp-tests/tests/checking/delegate_and_check_skip.rs index b613bdb46..db3b25eef 100644 --- a/crates/tests/cgp-tests/tests/checking/delegate_and_check_skip.rs +++ b/crates/tests/cgp-tests/tests/checking/delegate_and_check_skip.rs @@ -46,7 +46,7 @@ snapshot_delegate_and_check_components! { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for MyContext where UseType: IsProviderFor, @@ -54,8 +54,10 @@ snapshot_delegate_and_check_components! { impl DelegateComponent for MyContext { type Delegate = UseField; } - impl<__Context__, __Params__> IsProviderFor - for MyContext + impl< + __Context__, + __Params__: ?Sized, + > IsProviderFor for MyContext where UseField< Symbol!("name"), @@ -64,8 +66,10 @@ snapshot_delegate_and_check_components! { impl DelegateComponent for MyContext { type Delegate = UseField; } - impl<__Context__, __Params__> IsProviderFor - for MyContext + impl< + __Context__, + __Params__: ?Sized, + > IsProviderFor for MyContext where UseField: IsProviderFor, {} diff --git a/crates/tests/cgp-tests/tests/checking/mod.rs b/crates/tests/cgp-tests/tests/checking/mod.rs index f5971c7fc..3a987cf6a 100644 --- a/crates/tests/cgp-tests/tests/checking/mod.rs +++ b/crates/tests/cgp-tests/tests/checking/mod.rs @@ -20,4 +20,5 @@ pub mod delegate_and_check_skip; pub mod check_generic; pub mod check_path_context; pub mod check_providers; +pub mod check_providers_generic; pub mod check_trait; diff --git a/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_method_name_in_scope.rs b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_method_name_in_scope.rs new file mode 100644 index 000000000..6ff773ba5 --- /dev/null +++ b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_method_name_in_scope.rs @@ -0,0 +1,39 @@ +//! `#[cgp_auto_dispatch]` in a module that already holds a free function with the +//! same name as the trait's method. The per-variant helper function the macro emits +//! takes a reserved name (`__compute_area__`) rather than the method's own, so it +//! does not collide with the module's `area` (`E0428`). +//! +//! See cgp-knowledge-base/cgp/implementation/entrypoints/cgp_auto_dispatch.md. + +use cgp::prelude::*; + +#[derive(CgpVariant)] +pub enum Shape { + Square(Square), +} + +pub struct Square { + pub side: f64, +} + +// A free function named like the trait's method, in the same module. +pub fn area(side: f64) -> f64 { + side * side +} + +#[cgp_auto_dispatch] +pub trait HasArea { + fn area(&self) -> f64; +} + +impl HasArea for Square { + fn area(&self) -> f64 { + area(self.side) + } +} + +#[test] +fn test_method_name_beside_a_free_function() { + let shape = Shape::Square(Square { side: 3.0 }); + assert_eq!(shape.area(), 9.0); +} diff --git a/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_raw_method_name.rs b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_raw_method_name.rs new file mode 100644 index 000000000..e7340545a --- /dev/null +++ b/crates/tests/cgp-tests/tests/dispatching/auto_dispatch_raw_method_name.rs @@ -0,0 +1,40 @@ +//! A dispatch trait whose method has a raw identifier as its name, `r#type`. The +//! generated computer and helper are named from the unrawed name (`ComputeType` and +//! `__compute_type__`), since `r#` cannot appear inside a longer identifier. +//! +//! See cgp-knowledge-base/cgp/implementation/entrypoints/cgp_auto_dispatch.md. + +use cgp::prelude::*; + +#[derive(CgpData)] +pub enum Shape { + Circle(Circle), + Square(Square), +} + +pub struct Circle; + +pub struct Square; + +#[cgp_auto_dispatch] +pub trait HasKind { + fn r#type(&self) -> &'static str; +} + +impl HasKind for Circle { + fn r#type(&self) -> &'static str { + "circle" + } +} + +impl HasKind for Square { + fn r#type(&self) -> &'static str { + "square" + } +} + +#[test] +fn test_raw_method_name_dispatches() { + assert_eq!(Shape::Circle(Circle).r#type(), "circle"); + assert_eq!(Shape::Square(Square).r#type(), "square"); +} diff --git a/crates/tests/cgp-tests/tests/dispatching/const_generic_tables.rs b/crates/tests/cgp-tests/tests/dispatching/const_generic_tables.rs new file mode 100644 index 000000000..f91dd8a8a --- /dev/null +++ b/crates/tests/cgp-tests/tests/dispatching/const_generic_tables.rs @@ -0,0 +1,129 @@ +//! Table structs that `delegate_components!` declares, carrying a `const` +//! parameter. A `new` target (` new ConstantTable { … }`) keeps +//! `N` as a const parameter of the declared struct, although the target's type +//! arguments name it without its kind, and a nested inner table +//! (`UseDelegate { … }>`) accepts a const +//! parameter in its own generic list. The two snapshots pin the declared structs. +//! +//! See cgp-knowledge-base/cgp/implementation/entrypoints/delegate_components.md. + +use cgp::prelude::*; +use cgp_macro_test_util::snapshot_delegate_components; + +#[cgp_component(ConstantGetter)] +pub trait HasConstant { + const CONSTANT: u64; +} + +pub struct UseConstant; + +#[cgp_provider] +impl ConstantGetter for UseConstant { + const CONSTANT: u64 = CONSTANT; +} + +// A `new` target generic over a const: the declared struct is `ConstantTable`. +snapshot_delegate_components! { + delegate_components! { + new ConstantTable { + ConstantGetterComponent: UseConstant, + } + } + expand_constant_table(output) { + insta::assert_snapshot!(output, @" + pub struct ConstantTable(pub ::core::marker::PhantomData<()>); + impl DelegateComponent for ConstantTable { + type Delegate = UseConstant; + } + impl< + const N: u64, + __Context__, + __Params__: ?Sized, + > IsProviderFor for ConstantTable + where + UseConstant: IsProviderFor, + {} + ") + } +} + +pub struct App; + +delegate_components! { + App { + ConstantGetterComponent: ConstantTable<7>, + } +} + +check_components! { + App { + ConstantGetterComponent, + } +} + +#[cgp_component(Counter)] +#[derive_delegate(UseDelegate)] +pub trait CanCount { + fn count(&self, value: &T) -> usize; +} + +pub struct TimesN; + +#[cgp_provider] +impl Counter for TimesN { + fn count(_context: &Context, _value: &T) -> usize { + N + } +} + +pub struct Wrapper; + +// A nested inner table whose own generic list declares a const parameter. +snapshot_delegate_components! { + delegate_components! { + Wrapper { + CounterComponent: UseDelegate { + String: TimesN, + }>, + } + } + expand_count_table(output) { + insta::assert_snapshot!(output, @" + pub struct CountTable(pub ::core::marker::PhantomData<()>); + impl DelegateComponent for Wrapper { + type Delegate = UseDelegate>; + } + impl< + const N: usize, + __Context__, + __Params__: ?Sized, + > IsProviderFor for Wrapper + where + UseDelegate>: IsProviderFor, + {} + impl DelegateComponent for CountTable { + type Delegate = TimesN; + } + impl< + const N: usize, + __Context__, + __Params__: ?Sized, + > IsProviderFor for CountTable + where + TimesN: IsProviderFor, + {} + ") + } +} + +check_components! { + Wrapper { + CounterComponent: String, + } +} + +#[test] +fn test_const_generic_tables() { + assert_eq!(::CONSTANT, 7); + assert_eq!(Wrapper::<3>.count(&"x".to_owned()), 3); +} diff --git a/crates/tests/cgp-tests/tests/dispatching/mod.rs b/crates/tests/cgp-tests/tests/dispatching/mod.rs index f9d3221cd..1720e6f9e 100644 --- a/crates/tests/cgp-tests/tests/dispatching/mod.rs +++ b/crates/tests/cgp-tests/tests/dispatching/mod.rs @@ -10,12 +10,16 @@ pub mod types; // `#[cgp_auto_dispatch]` shape coverage: one method-shape per file. Each defines -// per-variant impls and dispatches them over an extensible-data enum. +// per-variant impls and dispatches them over an extensible-data enum. Two files cover +// the generated names instead: a module item sharing a method's name, and a method +// named with a raw identifier. pub mod auto_dispatch_generics; +pub mod auto_dispatch_method_name_in_scope; pub mod auto_dispatch_multi_args; pub mod auto_dispatch_multi_args_owned_self; pub mod auto_dispatch_multi_args_ref; pub mod auto_dispatch_multi_methods; +pub mod auto_dispatch_raw_method_name; pub mod auto_dispatch_self_mut_only; pub mod auto_dispatch_self_only; pub mod auto_dispatch_self_ref_only; @@ -41,3 +45,7 @@ pub mod use_delegate_getter; // Composing handler/computer providers. pub mod compose; + +// Table structs declared by `delegate_components!` that carry a `const` parameter: +// a const-generic `new` target and a nested inner table with a const generic list. +pub mod const_generic_tables; diff --git a/crates/tests/cgp-tests/tests/dispatching/use_delegate_getter.rs b/crates/tests/cgp-tests/tests/dispatching/use_delegate_getter.rs index 4c3f33c3c..6a2d342e2 100644 --- a/crates/tests/cgp-tests/tests/dispatching/use_delegate_getter.rs +++ b/crates/tests/cgp-tests/tests/dispatching/use_delegate_getter.rs @@ -502,7 +502,7 @@ mod derive_delegate { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for MyContext where UseDelegate< @@ -514,7 +514,7 @@ mod derive_delegate { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for MyContext where UseDelegate< @@ -524,7 +524,7 @@ mod derive_delegate { impl DelegateComponent> for FooTypes { type Delegate = UseType; } - impl<__Context__, __Params__> IsProviderFor, __Context__, __Params__> + impl<__Context__, __Params__: ?Sized> IsProviderFor, __Context__, __Params__> for FooTypes where UseType: IsProviderFor, __Context__, __Params__>, @@ -532,7 +532,7 @@ mod derive_delegate { impl DelegateComponent> for FooTypes { type Delegate = UseType; } - impl<__Context__, __Params__> IsProviderFor, __Context__, __Params__> + impl<__Context__, __Params__: ?Sized> IsProviderFor, __Context__, __Params__> for FooTypes where UseType: IsProviderFor, __Context__, __Params__>, @@ -540,7 +540,7 @@ mod derive_delegate { impl DelegateComponent> for FooGetters { type Delegate = UseField; } - impl<__Context__, __Params__> IsProviderFor, __Context__, __Params__> + impl<__Context__, __Params__: ?Sized> IsProviderFor, __Context__, __Params__> for FooGetters where UseField: IsProviderFor, __Context__, __Params__>, @@ -548,7 +548,7 @@ mod derive_delegate { impl DelegateComponent> for FooGetters { type Delegate = UseField; } - impl<__Context__, __Params__> IsProviderFor, __Context__, __Params__> + impl<__Context__, __Params__: ?Sized> IsProviderFor, __Context__, __Params__> for FooGetters where UseField: IsProviderFor, __Context__, __Params__>, @@ -628,7 +628,7 @@ mod derive_delegate2 { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for MyContext where UseDelegate2< @@ -640,7 +640,7 @@ mod derive_delegate2 { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for MyContext where UseDelegate2< @@ -652,7 +652,7 @@ mod derive_delegate2 { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor<(Index<1>, Index<0>), __Context__, __Params__> for FooTypes where UseType: IsProviderFor<(Index<1>, Index<0>), __Context__, __Params__>, @@ -662,7 +662,7 @@ mod derive_delegate2 { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor<(Index<0>, Index<1>), __Context__, __Params__> for FooTypes where UseType: IsProviderFor<(Index<0>, Index<1>), __Context__, __Params__>, @@ -672,7 +672,7 @@ mod derive_delegate2 { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor<(Index<1>, Index<0>), __Context__, __Params__> for FooGetters where UseField< @@ -684,7 +684,7 @@ mod derive_delegate2 { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor<(Index<0>, Index<1>), __Context__, __Params__> for FooGetters where UseField< diff --git a/crates/tests/cgp-tests/tests/generic_components/mod.rs b/crates/tests/cgp-tests/tests/generic_components/mod.rs index 8dbe09951..b28f98ee0 100644 --- a/crates/tests/cgp-tests/tests/generic_components/mod.rs +++ b/crates/tests/cgp-tests/tests/generic_components/mod.rs @@ -16,3 +16,8 @@ pub mod component_const; pub mod component_generic_const; pub mod component_lifetime; pub mod component_type_param; + +// A `?Sized` component parameter used at an unsized argument and called through +// `delegate_components!` wiring, which needs the table's forwarding impl to accept +// an unsized params tuple. +pub mod unsized_parameter; diff --git a/crates/tests/cgp-tests/tests/generic_components/unsized_parameter.rs b/crates/tests/cgp-tests/tests/generic_components/unsized_parameter.rs new file mode 100644 index 000000000..905388933 --- /dev/null +++ b/crates/tests/cgp-tests/tests/generic_components/unsized_parameter.rs @@ -0,0 +1,58 @@ +//! A component whose type parameter is `?Sized`, wired through `delegate_components!` +//! at an unsized argument and then *called*, not only checked. +//! +//! `HasReference<'a, T: 'a + ?Sized>` used at `str` makes the `IsProviderFor` params +//! tuple `(Life<'a>, str)`, which is unsized. The provider blanket impl requires the +//! context itself to implement `IsProviderFor` through the table's forwarding impl, so +//! that impl must declare its `__Params__` parameter `?Sized` for the call to resolve; +//! `check_components!` alone would pass either way, since it asks the provider's own +//! `IsProviderFor` impl rather than the table's. +//! +//! See cgp-knowledge-base/cgp/implementation/entrypoints/delegate_components.md. + +use cgp::prelude::*; + +#[cgp_component(ReferenceGetter)] +pub trait HasReference<'a, T: 'a + ?Sized> { + fn get_reference(&self) -> &'a T; +} + +#[cgp_impl(new GetName)] +#[uses(HasField)] +impl<'a> ReferenceGetter<'a, str> { + fn get_reference(&self) -> &'a str { + self.get_field(PhantomData::) + } +} + +#[derive(HasField)] +pub struct Borrowed<'a> { + pub name: &'a str, +} + +delegate_components! { + <'a> Borrowed<'a> { + ReferenceGetterComponent: GetName, + } +} + +check_components! { + <'a> Borrowed<'a> { + ReferenceGetterComponent: (Life<'a>, str), + } +} + +// Generic code bounded on the consumer trait at the unsized argument. +fn read<'a, Context: HasReference<'a, str>>(context: &Context) -> &'a str { + context.get_reference() +} + +#[test] +fn test_unsized_parameter_resolves_through_the_table() { + let text = String::from("hello"); + let borrowed = Borrowed { name: &text }; + + let name: &str = borrowed.get_reference(); + assert_eq!(name, "hello"); + assert_eq!(read(&borrowed), "hello"); +} diff --git a/crates/tests/cgp-tests/tests/namespaces/combined_forms.rs b/crates/tests/cgp-tests/tests/namespaces/combined_forms.rs index c89c69ed3..098e8fdad 100644 --- a/crates/tests/cgp-tests/tests/namespaces/combined_forms.rs +++ b/crates/tests/cgp-tests/tests/namespaces/combined_forms.rs @@ -125,7 +125,7 @@ snapshot_delegate_components! { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for App where RedirectLookup< @@ -141,7 +141,7 @@ snapshot_delegate_components! { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for App where Bundle: DelegateComponent, @@ -154,7 +154,7 @@ snapshot_delegate_components! { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for App where DummyBaz: IsProviderFor, @@ -164,7 +164,7 @@ snapshot_delegate_components! { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for App where DummyBaz: IsProviderFor, @@ -178,7 +178,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons>, __Context__, @@ -200,7 +200,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons>, __Context__, @@ -222,7 +222,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons>, __Context__, @@ -244,7 +244,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons>, __Context__, @@ -270,7 +270,7 @@ snapshot_delegate_components! { T, __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons>, __Context__, diff --git a/crates/tests/cgp-tests/tests/namespaces/default_impls_wiring.rs b/crates/tests/cgp-tests/tests/namespaces/default_impls_wiring.rs index 9319f7d73..5abe70895 100644 --- a/crates/tests/cgp-tests/tests/namespaces/default_impls_wiring.rs +++ b/crates/tests/cgp-tests/tests/namespaces/default_impls_wiring.rs @@ -50,7 +50,7 @@ snapshot_delegate_components! { __Key__, __Value__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor<__Key__, __Context__, __Params__> for AppA where __Key__: DefaultNamespace, @@ -76,7 +76,7 @@ snapshot_delegate_components! { T, Provider, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol<4, Chars<'t', Chars<'e', Chars<'s', Chars<'t', Nil>>>>>, @@ -109,7 +109,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol<4, Chars<'t', Chars<'e', Chars<'s', Chars<'t', Nil>>>>>, @@ -166,7 +166,7 @@ snapshot_delegate_components! { __Key__, __Value__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor<__Key__, __Context__, __Params__> for AppB where __Key__: DefaultNamespace, @@ -192,7 +192,7 @@ snapshot_delegate_components! { T, Provider, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol<4, Chars<'t', Chars<'e', Chars<'s', Chars<'t', Nil>>>>>, @@ -253,7 +253,7 @@ snapshot_delegate_components! { __Key__, __Value__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor<__Key__, __Context__, __Params__> for AppC where __Key__: ExtendedNamespace, @@ -279,7 +279,7 @@ snapshot_delegate_components! { T, Provider, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol<4, Chars<'t', Chars<'e', Chars<'s', Chars<'t', Nil>>>>>, @@ -312,7 +312,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol<4, Chars<'t', Chars<'e', Chars<'s', Chars<'t', Nil>>>>>, diff --git a/crates/tests/cgp-tests/tests/namespaces/extended_namespace_wiring.rs b/crates/tests/cgp-tests/tests/namespaces/extended_namespace_wiring.rs index 3240b5857..5fe2f2fa3 100644 --- a/crates/tests/cgp-tests/tests/namespaces/extended_namespace_wiring.rs +++ b/crates/tests/cgp-tests/tests/namespaces/extended_namespace_wiring.rs @@ -52,7 +52,7 @@ snapshot_delegate_components! { __Key__, __Value__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor<__Key__, __Context__, __Params__> for App where __Key__: ExtendedNamespace, @@ -71,7 +71,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, @@ -105,7 +105,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, @@ -137,7 +137,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, @@ -169,7 +169,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, @@ -219,7 +219,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol<3, Chars<'c', Chars<'g', Chars<'p', Nil>>>>, diff --git a/crates/tests/cgp-tests/tests/namespaces/for_where_clause.rs b/crates/tests/cgp-tests/tests/namespaces/for_where_clause.rs index 4fa7b785d..f4930f1b7 100644 --- a/crates/tests/cgp-tests/tests/namespaces/for_where_clause.rs +++ b/crates/tests/cgp-tests/tests/namespaces/for_where_clause.rs @@ -60,7 +60,7 @@ snapshot_delegate_components! { T, Provider, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol<4, Chars<'t', Chars<'e', Chars<'s', Chars<'t', Nil>>>>>, diff --git a/crates/tests/cgp-tests/tests/namespaces/multi_param_namespace.rs b/crates/tests/cgp-tests/tests/namespaces/multi_param_namespace.rs index 4be10b51f..82ff65b23 100644 --- a/crates/tests/cgp-tests/tests/namespaces/multi_param_namespace.rs +++ b/crates/tests/cgp-tests/tests/namespaces/multi_param_namespace.rs @@ -53,7 +53,7 @@ snapshot_delegate_components! { __Key__, __Value__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor<__Key__, __Context__, __Params__> for AppB where __Key__: DefaultNamespace, @@ -72,7 +72,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, @@ -106,7 +106,7 @@ snapshot_delegate_components! { T, __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, diff --git a/crates/tests/cgp-tests/tests/namespaces/multi_param_open.rs b/crates/tests/cgp-tests/tests/namespaces/multi_param_open.rs index 6b0f97ae2..034f65fa3 100644 --- a/crates/tests/cgp-tests/tests/namespaces/multi_param_open.rs +++ b/crates/tests/cgp-tests/tests/namespaces/multi_param_open.rs @@ -45,7 +45,7 @@ snapshot_delegate_components! { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for AppA where RedirectLookup< @@ -63,7 +63,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons>>, __Context__, @@ -88,7 +88,7 @@ snapshot_delegate_components! { T, __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons>>, __Context__, diff --git a/crates/tests/cgp-tests/tests/namespaces/namespace_basic.rs b/crates/tests/cgp-tests/tests/namespaces/namespace_basic.rs index b7b132076..22ac50184 100644 --- a/crates/tests/cgp-tests/tests/namespaces/namespace_basic.rs +++ b/crates/tests/cgp-tests/tests/namespaces/namespace_basic.rs @@ -169,7 +169,7 @@ snapshot_delegate_components! { __Key__, __Value__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor<__Key__, __Context__, __Params__> for App where __Key__: MyNamespace, @@ -181,7 +181,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor, __Context__, __Params__> for App where DummyFoo: IsProviderFor< @@ -196,7 +196,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor, __Context__, __Params__> for App where DummyBar: IsProviderFor< diff --git a/crates/tests/cgp-tests/tests/namespaces/namespace_group.rs b/crates/tests/cgp-tests/tests/namespaces/namespace_group.rs index 1025b50c8..5ef455932 100644 --- a/crates/tests/cgp-tests/tests/namespaces/namespace_group.rs +++ b/crates/tests/cgp-tests/tests/namespaces/namespace_group.rs @@ -64,7 +64,7 @@ snapshot_delegate_components! { __Key__, __Value__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor<__Key__, __Context__, __Params__> for App where __Key__: DefaultNamespace, @@ -83,7 +83,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, @@ -115,7 +115,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, @@ -147,7 +147,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, @@ -179,7 +179,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol<3, Chars<'a', Chars<'p', Chars<'p', Nil>>>>, diff --git a/crates/tests/cgp-tests/tests/namespaces/namespace_multi.rs b/crates/tests/cgp-tests/tests/namespaces/namespace_multi.rs index aafc5227b..3450588b0 100644 --- a/crates/tests/cgp-tests/tests/namespaces/namespace_multi.rs +++ b/crates/tests/cgp-tests/tests/namespaces/namespace_multi.rs @@ -224,7 +224,7 @@ snapshot_delegate_components! { __Key__, __Value__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor<__Key__, __Context__, __Params__> for App where __Key__: MyNamespace, @@ -238,7 +238,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons>, __Context__, @@ -259,7 +259,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons>, __Context__, @@ -309,7 +309,7 @@ snapshot_delegate_components! { __Key__, __Value__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor<__Key__, __Context__, __Params__> for OtherApp where __Key__: OtherNamespace, @@ -331,7 +331,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol< @@ -375,7 +375,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol< diff --git a/crates/tests/cgp-tests/tests/namespaces/namespace_nested_table.rs b/crates/tests/cgp-tests/tests/namespaces/namespace_nested_table.rs index b2c3ae3b7..12611ab69 100644 --- a/crates/tests/cgp-tests/tests/namespaces/namespace_nested_table.rs +++ b/crates/tests/cgp-tests/tests/namespaces/namespace_nested_table.rs @@ -59,14 +59,16 @@ snapshot_cgp_namespace! { impl DelegateComponent for FooTable { type Delegate = DummyFoo; } - impl<__Context__, __Params__> IsProviderFor for FooTable + impl<__Context__, __Params__: ?Sized> IsProviderFor + for FooTable where DummyFoo: IsProviderFor, {} impl DelegateComponent for FooTable { type Delegate = DummyFoo; } - impl<__Context__, __Params__> IsProviderFor for FooTable + impl<__Context__, __Params__: ?Sized> IsProviderFor + for FooTable where DummyFoo: IsProviderFor, {} diff --git a/crates/tests/cgp-tests/tests/namespaces/namespace_symbol_path.rs b/crates/tests/cgp-tests/tests/namespaces/namespace_symbol_path.rs index 69e2ba6a6..4cae79e3f 100644 --- a/crates/tests/cgp-tests/tests/namespaces/namespace_symbol_path.rs +++ b/crates/tests/cgp-tests/tests/namespaces/namespace_symbol_path.rs @@ -189,7 +189,7 @@ snapshot_delegate_components! { __Key__, __Value__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor<__Key__, __Context__, __Params__> for App where __Key__: MyNamespace, @@ -211,7 +211,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol< @@ -255,7 +255,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol< diff --git a/crates/tests/cgp-tests/tests/namespaces/namespace_type_path.rs b/crates/tests/cgp-tests/tests/namespaces/namespace_type_path.rs index 5d94432fd..cb9930fba 100644 --- a/crates/tests/cgp-tests/tests/namespaces/namespace_type_path.rs +++ b/crates/tests/cgp-tests/tests/namespaces/namespace_type_path.rs @@ -173,7 +173,7 @@ snapshot_delegate_components! { __Key__, __Value__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor<__Key__, __Context__, __Params__> for App where __Key__: MyNamespace, @@ -187,7 +187,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons>, __Context__, @@ -208,7 +208,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons>, __Context__, diff --git a/crates/tests/cgp-tests/tests/namespaces/open_dispatch.rs b/crates/tests/cgp-tests/tests/namespaces/open_dispatch.rs index 650bc6c15..ac3186db1 100644 --- a/crates/tests/cgp-tests/tests/namespaces/open_dispatch.rs +++ b/crates/tests/cgp-tests/tests/namespaces/open_dispatch.rs @@ -64,7 +64,7 @@ snapshot_delegate_components! { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for App where RedirectLookup< @@ -77,7 +77,7 @@ snapshot_delegate_components! { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for App where RedirectLookup< @@ -94,7 +94,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons>, __Context__, @@ -116,7 +116,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons>, __Context__, @@ -138,7 +138,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons>, __Context__, @@ -160,7 +160,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons>, __Context__, @@ -182,7 +182,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons>, __Context__, @@ -204,7 +204,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons>, __Context__, diff --git a/crates/tests/cgp-tests/tests/namespaces/open_later_param.rs b/crates/tests/cgp-tests/tests/namespaces/open_later_param.rs index 26c4931be..757ff5c8e 100644 --- a/crates/tests/cgp-tests/tests/namespaces/open_later_param.rs +++ b/crates/tests/cgp-tests/tests/namespaces/open_later_param.rs @@ -60,8 +60,10 @@ snapshot_delegate_components! { impl DelegateComponent for App { type Delegate = RedirectLookup>; } - impl<__Context__, __Params__> IsProviderFor - for App + impl< + __Context__, + __Params__: ?Sized, + > IsProviderFor for App where RedirectLookup< App, @@ -80,7 +82,7 @@ snapshot_delegate_components! { Code, __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons>>, __Context__, @@ -105,7 +107,7 @@ snapshot_delegate_components! { Code, __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons>>, __Context__, diff --git a/crates/tests/cgp-tests/tests/namespaces/prefix_default_namespace.rs b/crates/tests/cgp-tests/tests/namespaces/prefix_default_namespace.rs index 046ab3b4d..7d9f9c2db 100644 --- a/crates/tests/cgp-tests/tests/namespaces/prefix_default_namespace.rs +++ b/crates/tests/cgp-tests/tests/namespaces/prefix_default_namespace.rs @@ -136,7 +136,7 @@ snapshot_delegate_components! { __Key__, __Value__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor<__Key__, __Context__, __Params__> for App where __Key__: DefaultNamespace, @@ -164,7 +164,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol<3, Chars<'c', Chars<'g', Chars<'p', Nil>>>>, @@ -225,7 +225,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol<3, Chars<'c', Chars<'g', Chars<'p', Nil>>>>, diff --git a/crates/tests/cgp-tests/tests/namespaces/redirect_lookup.rs b/crates/tests/cgp-tests/tests/namespaces/redirect_lookup.rs index 05d3a2f18..3f08c13c9 100644 --- a/crates/tests/cgp-tests/tests/namespaces/redirect_lookup.rs +++ b/crates/tests/cgp-tests/tests/namespaces/redirect_lookup.rs @@ -140,7 +140,7 @@ snapshot_delegate_components! { __Key__, __Value__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor<__Key__, __Context__, __Params__> for App where __Key__: DefaultNamespace, @@ -159,7 +159,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol<3, Chars<'b', Chars<'a', Chars<'r', Nil>>>>, diff --git a/crates/tests/cgp-tests/tests/namespaces/redirect_mapping.rs b/crates/tests/cgp-tests/tests/namespaces/redirect_mapping.rs index e61cb7228..2b8727169 100644 --- a/crates/tests/cgp-tests/tests/namespaces/redirect_mapping.rs +++ b/crates/tests/cgp-tests/tests/namespaces/redirect_mapping.rs @@ -82,7 +82,7 @@ snapshot_delegate_components! { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for App where RedirectLookup< @@ -99,7 +99,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons>, __Context__, @@ -129,7 +129,7 @@ snapshot_delegate_components! { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for App where RedirectLookup< @@ -163,7 +163,7 @@ snapshot_delegate_components! { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for App where RedirectLookup< @@ -196,7 +196,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons< Symbol< @@ -257,7 +257,7 @@ snapshot_delegate_components! { } impl< __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor for OpenApp where RedirectLookup< @@ -274,7 +274,7 @@ snapshot_delegate_components! { impl< __Wildcard__, __Context__, - __Params__, + __Params__: ?Sized, > IsProviderFor< PathCons>, __Context__,