Skip to content

AI-review CGP macro implementations, use. instead of :: as separator in #[use_type]#248

Merged
soareschen merged 10 commits into
mainfrom
ai-tests
Jul 2, 2026
Merged

AI-review CGP macro implementations, use. instead of :: as separator in #[use_type]#248
soareschen merged 10 commits into
mainfrom
ai-tests

Conversation

@soareschen

@soareschen soareschen commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

AI Summary

This PR is a systematic hardening pass over the CGP procedural macros, reviewing one construct at a time until each macro's implementation, tests, and documentation are correct, complete, and mutually consistent. It is not a feature branch — it is a quality campaign. Across nine commits it works through product!, #[cgp_component], delegate_components!, the open statement, #[cgp_impl], check_components!, #[cgp_fn], and #[use_type], fixing latent miscompilations and confusing diagnostics, closing test gaps, and bringing the four documentation views (reference, implementation, snapshots, and the bundled /cgp skill) back in sync with the code. The change touches 127 files, but the bulk of the line count is documentation propagation and new tests; the behavioral surface changes in a focused set of macro-core files.

High-level concepts

The campaign follows a standing workflow that this PR itself codifies. The single largest documentation addition is a new "Macro review workflow" section in cgp/CLAUDE.md that defines the review as a repeatable process: reload the CGP mental model and the prose convention, read the macro's documentation and implementation, harden the implementation and its tests, scrutinize the codegen against every way its input can be parsed and its output expanded, and keep all four documentation views in sync before verifying green. The section reads as the operating manual for the very changes in this PR, so future iterations of the same campaign can proceed identically.

The most consequential change to users is a breaking syntax change to #[use_type]. The separator between a trait and the associated type it imports moves from :: to ., so #[use_type(HasErrorType::Error)] becomes #[use_type(HasErrorType.Error)]. The motivation is disambiguation: a . unambiguously marks where the associated type begins, which lets the trait itself be a full path such as foo::bar::HasScalarType without a greedy path parser silently swallowing the trailing ::Error. The rewrite also drops the older <Trait> angle-bracket form and the pre-. braced form, standardizing on a single path-based grammar (the braced multi-import list after the ., HasFooType.{A, B}, is retained). Every call site inside the CGP crates — the error, handler, runner, and runtime traits — is migrated, and the duplicate-alias guard now also fires within a single braced list rather than only across separate attributes.

The remaining behavioral changes are corner-case corrections in the macro codegen. Each fixes a case where a macro either emitted invalid Rust, panicked the compiler, or silently dropped input, and each now ships with a behavioral test that pins the corrected behavior. The corrections are individually small but collectively remove a class of latent bugs that only surfaced on unusual-but-valid input.

Behavioral and syntax changes

The following changes alter what the macros accept or emit, and are the substance of the PR.

The #[use_type] trait/associated-type separator changes from :: to ., and the attribute now accepts a path-qualified trait, as described above (cgp/crates/macros/cgp-macro-core/src/types/attributes/use_type/attribute.rs). This is the one breaking change for downstream code.

The #[cgp_fn] implicit-argument access mode now follows each argument's own type rather than the receiver's mutability (cgp/crates/macros/cgp-macro-core/src/functions/implicits/parse.rs). Previously a &mut self receiver forced a mutable field read for every implicit and capped the count at one; now immutable implicits under a &mut self receiver read through the shared HasField/get_field path and any number of them may coexist, while the "must be the only implicit" restriction correctly applies only to a genuine &mut implicit whose exclusive borrow of the context would otherwise conflict.

The mut self owned receiver is now rewritten correctly by #[cgp_impl] (cgp/crates/macros/cgp-macro-core/src/visitors/replace_self/replace_self_receiver.rs). The mut keyword now binds the generated parameter (mut __context__: Context) instead of being emitted as the invalid type __context__: mut Context, so all four receiver shapes — &self, &mut self, self, and mut self — now round-trip.

A self:: module path inside a #[cgp_impl] body is now preserved rather than being rewritten to the context (cgp/crates/macros/cgp-macro-core/src/visitors/replace_self/replace_self_value.rs). Because token-level rewriting inside a macro invocation cannot see through the macro, the visitor now peeks for a trailing :: to tell a value self (rewritten) from a self:: module path (left intact).

Const generic parameters on a CGP component trait are now rejected with a clear spanned error instead of panicking the compiler (cgp/crates/macros/cgp-macro-core/src/functions/is_provider_params.rs). The IsProviderFor params tuple holds types and cannot key on a const value, so the previous unimplemented!() is replaced by a diagnostic that names the unsupported parameter.

The open statement now accepts a braceless single component, open A;, while the braced list open { A, B }; remains required for multiple components (cgp/crates/macros/cgp-macro-core/src/types/delegate_component/statement/open.rs).

The check_components! macro now accepts a path-qualified context type such as inner::Context, deriving the check-trait name from the final path segment (cgp/crates/macros/cgp-macro-core/src/types/check_components/table.rs). This mirrors delegate_components!, which already uses the context type verbatim, so a context defined in another module can now be both wired and checked by its path. The same file also hardens #[check_providers(...)], which now errors on an empty list and on more than one occurrence rather than silently accumulating.

The delegate_components! attribute validation now recurses into nested inner tables (cgp/crates/macros/cgp-macro-core/src/types/delegate_component/validate_attributes.rs). Attributes placed on keys inside a Normal or Direct mapping's inner table, or inside a for-loop statement's mappings, were previously dropped without warning; they are now validated so an unsupported attribute fails with a spanned error instead of vanishing.

The value-level product! macro now parses its items as expressions rather than types (cgp/crates/macros/cgp-macro-core/src/types/product/product_expr.rs), so it can build a runtime product from value expressions and not only type names.

Finally, a small diagnostic improvement renames the missing-argument error on #[cgp_component] from "provider_ident key must be given" to the user-facing "the provider key must be given" (cgp/crates/macros/cgp-macro-core/src/types/cgp_component/args/component_args.rs).

Structural changes

The test suite grows from three jobs to four with a new crate. cgp/crates/tests/cgp-compile-fail-tests/ is a new library crate holding compile_fail doctests — the tool for input a macro accepts but whose expansion then fails to compile, such as two delegate_components! blocks that delegate the same key, which the macro cannot reject without a whole-program view. It is a library crate on purpose, because doctests are collected only from a library target; it currently ships as a documented scaffold with no cases enumerated yet, ready for the first case a future iteration captures. The crate is registered in the workspace cgp/Cargo.toml and cgp/Cargo.lock.

The macro-internals suite gains a new parser-rejection layer. New files under cgp/crates/tests/cgp-macro-tests/tests/parser_rejections/ drive the macro entrypoints directly with an assert_macro_rejects helper to pin the rejections for cgp_fn, check_components, delegate_components, and use_type, and extend the existing cgp_component rejections. These verify that malformed input fails at the macro's own parse stage with a clear message.

The behavioral suite gains one test per corrected corner case. New files in cgp/crates/tests/cgp-tests/tests/ cover the owned/mut self receiver, the self::-in-macro distinction, the path-qualified check_components! context, a type-parameter-only generic component, &mut self with all-immutable implicits, and the path-qualified #[use_type] trait. Several are snapshot tests that also pin the exact macro expansion in the owning target.

Inline documentation is filled in across the macro-core AST modules. Many public structs, traits, and functions in the cgp_impl, cgp_component, delegate_component, check_components, and use_type module trees gain a /// doc comment, and the two test-suite guides — cgp/crates/tests/CLAUDE.md alongside the workflow section in cgp/CLAUDE.md — are rewritten to document the four-target structure and when to reach for each kind of failure test.

The published documentation and the bundled skill are swept for consistency. The ::. change and the other corrections propagate across cgp/docs/reference/, cgp/docs/implementation/, cgp/docs/concepts/, cgp/docs/examples/, and the vendored copy of the CGP skill under cgp/docs/skills/cgp/, keeping every view aligned with the code.

Impacts

The impacts below follow from the changes above, ordered from the most disruptive to the most local.

Downstream code using #[use_type] must migrate from :: to .. Any existing #[use_type(SomeTrait::Assoc)] will no longer parse and must become #[use_type(SomeTrait.Assoc)]. The removed <Trait> angle-bracket and pre-. braced forms must be rewritten to the path form as well. Everything inside the CGP crates is already migrated, so only external callers are affected.

CI must run doctests separately. The new cgp-compile-fail-tests crate is not exercised by cargo nextest, which does not run doctests; verifying it requires cargo test --doc -p cgp-compile-fail-tests, and this is now documented in the test guide.

Several previously-broken or panicking inputs now compile or fail cleanly. Providers with a mut self receiver, #[cgp_impl] bodies referencing self:: module paths, #[cgp_fn] with a &mut self receiver plus immutable implicits, path-qualified contexts in check_components!, path-qualified traits in #[use_type], and the braceless open A; all work now. A const generic parameter on a component trait now produces a readable error instead of aborting the compiler.

Diagnostics improve in several macros. Empty or duplicated #[check_providers], unsupported attributes on nested delegate_components! keys, duplicate #[use_type] aliases within one braced list, and the missing provider key on #[cgp_component] now surface as clear spanned errors rather than being silently accepted, dropped, or reported with an internal identifier.

product! can now take value expressions, broadening what the value-level macro accepts beyond bare type names.

No public runtime API of the CGP crates changes. The library-crate edits in cgp-error, cgp-handler, cgp-run, and cgp-runtime are all the #[use_type] separator migration on trait definitions; the generated traits and their semantics are unchanged, so the impact is confined to the macro grammar, diagnostics, tests, and documentation.

@soareschen soareschen merged commit 134cbf4 into main Jul 2, 2026
5 checks passed
@soareschen soareschen deleted the ai-tests branch July 2, 2026 22:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant