Repository navigation
Fix five macro defects and close test gaps found in the knowledge-base review - #273
Merged
Merged
Conversation
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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HEMw6zp1hjNDZyxaKsszMj
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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HEMw6zp1hjNDZyxaKsszMj
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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HEMw6zp1hjNDZyxaKsszMj
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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HEMw6zp1hjNDZyxaKsszMj
- #[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 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR fixes five defects in CGP's macros and adds the tests that a review of the knowledge base
found missing. Each fix replaces a confusing downstream compiler error, or a failure to compile
valid code, with either working code or a spanned error at the macro input. Every fix comes with a
regression test. The rest of the branch pins behavior that already worked but had no test, so a
later change cannot alter it silently.
The branch holds five commits on top of
main(367f066), touching 64 files with 1,587 insertionsand 173 deletions. Most of the line count is new tests. The source changes are confined to
cgp-macro-core,cgp-extra-macro-lib, and one rustdoc comment incgp-macro.Defect fixes
Each fix below states the failure it removes and the behavior that replaces it. Most of the old
failures surfaced as compiler errors inside generated code rather than on the input the user wrote,
which made them hard to trace back to their cause.
#[cgp_component]rejects aname:parameter the trait does not declareA keyed
#[cgp_component]can give the component marker generic parameters, as inname: ShapeComponent<T>. The marker struct is declared with those parameters, but the generatedimpls name it using the trait's generics. A parameter that appears only on the name therefore
reached impl positions where nothing bound it, and compilation failed with
E0425: cannot find type `T` in this scopeplus follow-on errors inside generated code.ItemCgpComponent::preprocessnow checks every lifetime, type, and const parameter of the nameagainst the trait's generics before generating anything. An undeclared parameter produces an error
spanned on that parameter:
A
constparameter in the name used to fail with an internal parse error,failed to parse internal tokens to type `syn::generics::TypeParamBound`. It now gets auser-facing error. On a trait that lacks the parameter, the error is the one above. On a trait that
declares it, the error is
const generic parameters are not supported on CGP component traits,spanned on the trait's parameter.
#[check_providers]works on a generic tablecheck_components!with#[check_providers(...)]generated a check trait whose supertrait namedthe context directly:
IsProviderFor<__Component__, Wrapper<T>, __Params__>. For a generic tablesuch as
<T> Wrapper<T>, theTappeared in the trait header, where it is undeclared, and thecheck failed with
E0425and anE0207on the impl.The check trait now takes the context as a parameter:
The table's generics stay on the impls that declare them. The plain
check_components!form, whichasserts
CanUseComponenton the context, is unchanged.delegate_components!resolves a component used at an unsized parameterThe forwarding
IsProviderForimpl thatdelegate_components!emits for each entry declared__Params__without?Sized, although the trait itself declares it?Sized. A component used atan unsized argument, such as
HasReference<'a, str>, has the params tuple(Life<'a>, str), whichis unsized. Such a component passed
check_components!, which consults the provider's own impl.But every call through the context failed, because the provider blanket impl goes through the
table's forwarding impl:
The forwarding impl now declares
__Params__: ?Sized. This relaxes a bound, so it accepts strictlymore code. The new bound appears in every
delegate_components!expansion snapshot, so it accountsfor the small edits under
crates/tests/cgp-tests/tests/namespaces/andbasic_delegation/, in thedelegate_and_check_*files underchecking/, and indispatching/use_delegate_getter.rs. Some ofthose edits are rustfmt rewrapping the longer impl headers.
Table structs declared by
delegate_components!keepconstparametersdelegate_components!declares a struct in two places: for anewtarget, and for a nested innertable such as
UseDelegate<new CountTable<...> { ... }>. Neither could carry a const parameter:newtarget:<const N: u64> new ConstantTable<N> { ... }namesNin argument position,without its kind, so the declared struct got a type parameter
N, and any entry that passedNas a const failed with
E0747.TypeGenericsrejectedconst N: usizein the table's own generic list. Theparser tries the nested-table form speculatively and falls back to a plain type, so the user saw
expected `,`at the inner table's name. Writing a bareNinstead declared a type parameterand failed with
E0747, as for anewtarget. The wiring value was also built from thedefinition list (
CountTable<const N: usize>) rather than the argument list (CountTable<N>),which would have broken once the parse succeeded.
DelegateTablenow restores a parameter's const kind from the table's generic list before declaringthe struct.
TypeGenericsnow accepts bare lifetimes, bare type parameters, andconst N: Tparameters, and it still rejects bounds, defaults, and
whereclauses. The inner table's value isbuilt through
build_table_type, so it uses the argument form.#[cgp_auto_dispatch]generates names that cannot collide or break#[cgp_auto_dispatch]emits one private free function per method, and it named that function afterthe method itself. A module that already held an item of the same name, such as a free function
areabeside a trait methodarea, failed withE0428: the name `area` is defined multiple timesand further errors from the clash. A method named with a raw identifier, such as
r#type, alsomade the macro panic. The computer's name was built from the method name with its
r#prefix,giving
ComputeR#type, whichIdent::newrejects.The helper is now named
__compute_{method}__, which follows the reserved double-underscoreconvention of the other generated identifiers. Both the helper and the
Compute{Method}computerare built from the unrawed method name, so
r#typeyields__compute_type__andComputeType.One clash remains possible: two
#[cgp_auto_dispatch]traits in one module with the same methodname. That limitation is recorded in the knowledge base and left for a separate change, since
fixing it would rename the
Compute{Method}provider.Test coverage added
The review found constructs whose behavior was documented but not pinned by any test. The new tests
fall into three groups.
Parser rejections in
cgp-macro-testspin inputs that these macros refuse.cgp-macro-testsgains adev-dependency on
cgp-extra-macro-libso it can drive the extra macros directly.#[cgp_type]: a non-trait item, an empty trait, a method, two associated types, and a genericor
where-bounded associated type.#[cgp_computer]: aselfreceiver, and a one-argumentResult<T>alias, which the syntacticResultdetection cannot parse (a known limitation, now pinned).#[cgp_producer]: a parameter, aselfreceiver, anasyncfunction, and a genericparameter.
#[cgp_auto_dispatch]: an associated type, a method without a receiver, and a generic method.#[blanket_trait]: a method without a body, a constant without a value, and a macro item.#[cgp_component]: a repeated key, an unknown key, a keyed form withoutprovider, and anundeclared
name:parameter, plus a positive case for a declared one.Expansion snapshots pin generated code that had no snapshot:
component_assoc_const: a component trait with an associated const, projected through everyforwarding impl.
component_async: a component with anasyncmethod under#[async_trait], with.awaitforwarding.
const_custom_context:#[blanket_trait(Ctx)]with an associated constant and a renamed contextparameter.
delegate_and_check_skip: a#[skip_check]entry beside checked entries.mut_ref_autoandmut_option_str_auto:#[cgp_auto_getter]returning&mut TandOption<&mut str>.Behavioral tests cover the remaining gaps:
path_macro: the barePath!macro's segment classification, by type equality.optional_builder:finalize_optionalreturns the name of a missing field, and with severalunset it reports the last in declaration order.
The regression tests for the defect fixes are
check_providers_generic,unsized_parameter,const_generic_tables,auto_dispatch_method_name_in_scope,auto_dispatch_raw_method_name, andthe new
#[cgp_component]name-parameter cases.Documentation
The
#[blanket_trait]rustdoc example incgp-macro/src/lib.rsused the pre-release#[trait_alias]name and the removedAsynctrait. It now shows a current#[blanket_trait]andthe
__Context__parameter the macro actually emits.The knowledge base is updated in companion commit
da168beofcgp-knowledge-base. That commit removes the Knownissues these fixes close, documents the replacement behavior in the reference and implementation
documents, and indexes the new tests and snapshots.
Compatibility
None of the changes alters the public API of a runtime crate. The observable differences are in
generated code:
#[check_providers]check trait gains a__Context__parameter. The trait exists only tomake the compile-time assertion, so code outside the
check_components!block has no reason toname it.
__Params__: ?Sizedon the forwarding impl only relaxes a bound.#[cgp_auto_dispatch]helper function is private and now has a reserved name. Code in thesame module that called the old helper by the method's name would stop compiling, but that helper
was never documented as callable.
Verification
cargo test --workspacepasses: 540 tests, 0 failures.cargo clippy --workspace --all-targetsreports no warnings. The re-accepted snapshots differ from
mainonly by the?Sizedbound, the#[check_providers]trait parameter, and rustfmt rewrapping.The
name:parameter errors were also checked in a separate crate. An undeclared lifetime or constparameter gets the spanned error described above, and a trait that declares a const parameter gets
the const-generic refusal. The new parser-rejection test covers the undeclared type parameter.