Skip to content

Emit proc-macro expansion directly via quote! instead of delegating to __devirt_define!#16

Merged
Kab1r merged 2 commits intomainfrom
claude/direct-proc-macro-expansion-1bMmc
Apr 12, 2026
Merged

Emit proc-macro expansion directly via quote! instead of delegating to __devirt_define!#16
Kab1r merged 2 commits intomainfrom
claude/direct-proc-macro-expansion-1bMmc

Conversation

@Kab1r
Copy link
Copy Markdown
Owner

@Kab1r Kab1r commented Apr 12, 2026

Rewrites expand_trait and expand_impl to generate the dispatch code
directly, eliminating the macro_rules pattern-matching bottleneck.
This immediately enables support for unsafe trait/fn, method lifetime
params, method attributes (#[must_use], doc comments), supertraits,
extern "ABI" fn, and method where clauses — all parsed by syn and
emitted faithfully via quote!.

The declarative macro path (__devirt_define!) is unchanged and keeps
its current limited feature set as the default-features=false fallback.

New trybuild tests: attr_unsafe_fn, attr_method_lifetimes,
attr_supertraits, attr_must_use (pass); attr_unsafe_missing_on_impl
(compile-fail). Extended equivalence test with supertraits, method
lifetimes, and #[must_use] coverage.

…o __devirt_define!

Rewrites expand_trait and expand_impl to generate the dispatch code
directly, eliminating the macro_rules pattern-matching bottleneck.
This immediately enables support for unsafe trait/fn, method lifetime
params, method attributes (#[must_use], doc comments), supertraits,
extern "ABI" fn, and method where clauses — all parsed by syn and
emitted faithfully via quote!.

The declarative macro path (__devirt_define!) is unchanged and keeps
its current limited feature set as the default-features=false fallback.

New trybuild tests: attr_unsafe_fn, attr_method_lifetimes,
attr_supertraits, attr_must_use (pass); attr_unsafe_missing_on_impl
(compile-fail). Extended equivalence test with supertraits, method
lifetimes, and #[must_use] coverage.

https://claude.ai/code/session_01KeSis2aTgupBqgw9RtWXXZ
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 12, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 050e2490-659f-4345-90ea-9cf2a6e3897f

📥 Commits

Reviewing files that changed from the base of the PR and between a6acac8 and 692525e.

📒 Files selected for processing (4)
  • CLAUDE.md
  • crates/core/tests/ui_attr.rs
  • crates/core/tests/ui_attr/attr_must_use_unused.rs
  • crates/core/tests/ui_attr/attr_must_use_unused.stderr

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Proc-macro now supports a broader range of method signatures (lifetimes, unsafe methods, supertraits, extern ABIs, method attributes).
  • Tests

    • Added extensive tests and UI cases covering unsafe methods, method lifetimes, supertraits, must-use handling, and related compile-fail scenarios.
  • Documentation

    • Clarified distinctions and limitations between the proc-macro expansion path and the declarative macro path.

Walkthrough

Proc-macro #[devirt] was changed to emit dispatch code directly (using quote!) and to support extended method signatures (lifetimes, unsafe fn, supertraits, method attributes). The declarative devirt! macro remains a thin dispatcher to __devirt_define!, which is documented as supporting only plain fn signatures. Several UI and equivalence tests were added to cover these cases.

Changes

Cohort / File(s) Summary
Crate docs & internal macro doc
crates/core/src/lib.rs, CLAUDE.md
Clarified crate-level docs: attribute proc-macro emits full dispatch via quote!; declarative devirt! delegates to __devirt_define!. Added #[doc(hidden)] docs for __devirt_define! describing its limited signature support.
Proc-macro implementation
crates/macros/src/lib.rs
Proc-macro refactor: stops delegating to __devirt_define!, directly generates trait dispatch code (hidden inner trait, marker trait, inherent dyn Trait helpers, vtable/data extraction, hot-type vtable matching, unsafe forwarding). Adds trait validation helpers and rewrites wildcard patterns for forwarding. Updated rustdoc example.
Trybuild registration
crates/core/tests/ui_attr.rs
Registered new trybuild tests (pass and compile-fail) for extended signature forms and error cases.
UI tests: extended signatures
crates/core/tests/ui_attr/attr_unsafe_fn.rs, .../attr_method_lifetimes.rs, .../attr_supertraits.rs, .../attr_must_use.rs, .../attr_must_use_unused.rs
New UI test files exercising unsafe trait methods, method lifetimes, supertraits, #[must_use] on methods, and the unused-must-use lint behavior.
UI tests: error expectations
crates/core/tests/ui_attr/attr_unsafe_missing_on_impl.rs, .../attr_unsafe_missing_on_impl.stderr, .../attr_must_use_unused.stderr
Added failing test cases and expected stderr outputs validating compiler diagnostics for missing unsafe impl and unused #[must_use] returns.
Equivalence tests
crates/core/tests/equivalence.rs
Added attr_extended module with ExtHot/ExtCold and Inspectable: fmt::Debug (includes #[must_use], lifetime-parameterized method, and &mut method) to exercise dynamic dispatch and mutable trait-object calls.
Misc docs
CLAUDE.md
Reworked documentation to describe two macro expansion paths and scope “Dispatch Arms” to the declarative path.

Sequence Diagram(s)

sequenceDiagram
  participant Dev as Developer source
  participant Proc as Proc-macro (#[devirt])
  participant Decl as Declarative macro (devirt!)
  participant Gen as Generated dispatch code
  participant Runtime as Runtime (vtable/data)

  Dev->>Proc: annotate trait/impl with #[devirt]
  Proc->>Gen: emit full dispatch code via quote! (inner trait, marker, dyn helpers)
  Dev->>Decl: use devirt! macro (older path)
  Decl->>Gen: expand to __devirt_define! (limited plain-fn signatures)
  Gen->>Runtime: runtime uses vtable/data extraction and hot-type matching
  Runtime->>Gen: call forwarding to concrete impl (__spec_* methods)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 I hopped through traits both safe and bold,

Lifetimes and unsafe tales I told,
Macros now weave direct dispatch art,
Vtables dance — each method plays its part,
Nose twitch, stamp — new tests give joy to my heart.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: rewriting the proc-macro to emit dispatch code directly via quote! instead of delegating to __devirt_define!.
Description check ✅ Passed The description clearly explains the changeset: rewrites expand_trait and expand_impl for direct code generation, enabling support for unsafe traits/functions, method lifetimes, attributes, supertraits, and other features, with new test coverage.
Docstring Coverage ✅ Passed Docstring coverage is 80.56% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/direct-proc-macro-expansion-1bMmc

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/core/src/lib.rs`:
- Around line 33-38: The doc comment claims the proc-macro attribute `#[devirt]`
emits dispatch code directly, which violates the repo rule that all dispatch
expansion must be centralized in the hidden, exported declarative macro
`__devirt_define!`; fix by restoring delegation so `#[devirt]` and the `devirt!`
declarative macro both call `__devirt_define!` (which must have `#[doc(hidden)]`
and `#[macro_export]` and support the `@trait` and `@impl` entry points), or
alternatively update this comment to explicitly state the new architecture and
add/update `__devirt_define!` to match the documented contract — ensure
references to `#[devirt]`, `devirt!`, and `__devirt_define!` in the code and
docs are consistent.

In `@crates/core/tests/ui_attr/attr_must_use.rs`:
- Around line 11-13: The test currently masks must_use warnings by consuming
return values; add a new compile-fail case that ensures #[must_use] on the trait
method compute is preserved through macro expansion by creating a test variant
with #![deny(unused_must_use)] and an expression that calls compute() without
assigning or using its result (i.e., omit the let _ = ...) so the compiler must
emit an unused_must_use error; reference the trait/method name compute and the
existing attr_must_use.rs test when adding this negative test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e4373048-3888-4ae2-89b9-e08073d10667

📥 Commits

Reviewing files that changed from the base of the PR and between 31658ec and a6acac8.

📒 Files selected for processing (10)
  • crates/core/src/lib.rs
  • crates/core/tests/equivalence.rs
  • crates/core/tests/ui_attr.rs
  • crates/core/tests/ui_attr/attr_method_lifetimes.rs
  • crates/core/tests/ui_attr/attr_must_use.rs
  • crates/core/tests/ui_attr/attr_supertraits.rs
  • crates/core/tests/ui_attr/attr_unsafe_fn.rs
  • crates/core/tests/ui_attr/attr_unsafe_missing_on_impl.rs
  • crates/core/tests/ui_attr/attr_unsafe_missing_on_impl.stderr
  • crates/macros/src/lib.rs

Comment thread crates/core/src/lib.rs
Comment on lines +33 to +38
//! The proc-macro attribute (`#[devirt]`) emits the dispatch code
//! directly via `quote!`, supporting the full range of method
//! signatures that `syn` can parse (lifetimes, `unsafe fn`,
//! supertraits, attributes, `extern "ABI" fn`, etc.).
//! The declarative macro (`devirt!`) delegates to `__devirt_define!`,
//! which has more limited syntax support.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Architecture contract drift from the repository macro-expansion rule.

Line 33 through Line 38 document direct proc-macro dispatch emission instead of routing through __devirt_define!. That introduces two expansion engines and diverges from the repo’s stated single-source expansion contract; please either restore delegation or update the project rule/docs in the same change set.

As per coding guidelines "crates/core/src/**/*.rs: Place all dispatch expansion logic in __devirt_define! macro with #[doc(hidden)], #[macro_export] attributes, and implement two entry points: @trait ... and @impl ...".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/core/src/lib.rs` around lines 33 - 38, The doc comment claims the
proc-macro attribute `#[devirt]` emits dispatch code directly, which violates
the repo rule that all dispatch expansion must be centralized in the hidden,
exported declarative macro `__devirt_define!`; fix by restoring delegation so
`#[devirt]` and the `devirt!` declarative macro both call `__devirt_define!`
(which must have `#[doc(hidden)]` and `#[macro_export]` and support the `@trait`
and `@impl` entry points), or alternatively update this comment to explicitly
state the new architecture and add/update `__devirt_define!` to match the
documented contract — ensure references to `#[devirt]`, `devirt!`, and
`__devirt_define!` in the code and docs are consistent.

Comment thread crates/core/tests/ui_attr/attr_must_use.rs
…ompile-fail test

- CLAUDE.md: Document that the proc-macro attribute emits directly via
  quote! while only the declarative macro delegates to __devirt_define!.
  Update Macro Structure section to describe two independent expansion
  paths and clarify Dispatch Arms apply to the declarative-macro path
  only.

- Add attr_must_use_unused.rs compile-fail test: verifies #[must_use]
  propagates through macro expansion by calling compute() without using
  its result under #![deny(unused_must_use)], proving the attribute
  reaches the inherent dispatch method on dyn Trait.

https://claude.ai/code/session_01KeSis2aTgupBqgw9RtWXXZ
@Kab1r Kab1r merged commit bfb3eba into main Apr 12, 2026
15 checks passed
@Kab1r Kab1r deleted the claude/direct-proc-macro-expansion-1bMmc branch April 12, 2026 14:27
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.

2 participants