Support default method bodies in #[devirt] trait definitions#20
Conversation
Trait methods with default bodies (e.g. `fn is_large(&self) -> bool {
self.area() > 100.0 }`) are now accepted by the proc-macro attribute.
The default body is placed on the inner trait's `__spec_*` provided
method, with `self.method()` calls rewritten to `self.__spec_method()`
via `syn::visit_mut` so sibling calls resolve within the inner trait.
Macro invocations (format!, write!, etc.) are handled via token-level
rewriting since syn doesn't parse macro token streams.
https://claude.ai/code/session_01BJen3TbKCFohcjNFeYwBiA
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughSummary by CodeRabbit
WalkthroughThe PR enables trait methods with default bodies in the Changes
Sequence DiagramsequenceDiagram
participant User as User Code
participant Trait as Trait Def<br/>(with default methods)
participant Macro as devirt Macro
participant Inner as Inner Trait<br/>(__spec_*)
participant Impl as Type Impl
participant Runtime as Runtime Dispatch
User->>Trait: Define trait with default method calling `self.get()`
Trait->>Macro: #[devirt::devirt] annotation
Macro->>Macro: Parse trait & default bodies
Macro->>Macro: Rewrite `self.method()` → `self.__spec_method()` in default bodies
Macro->>Inner: Emit `__spec_*` declarations for required signatures
Macro->>Impl: Generate impls, rewriting impl blocks likewise
User->>Runtime: Create &dyn Trait from instance
Runtime->>Impl: Call default method
Impl->>Impl: Execute rewritten body using `self.__spec_*()`
Impl-->>Runtime: Return result
Runtime-->>User: Result delivered
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
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/tests/equivalence.rs`:
- Around line 149-206: The test attr_default_body_dispatch only exercises the
plain default-body path; add two additional runtime cases inside the same test:
(1) a case that exercises an overridden default implementation (create a new
struct e.g. DefOver with an impl Defaulted for DefOver that overrides is_big and
assert the overridden behavior via &dyn Defaulted), and (2) a case that
exercises a default that uses write!/format!-style code (create a struct e.g.
DefFmt that relies on the trait's default method which uses format!/write!
internally and assert its behavior via &dyn Defaulted and &(dyn Defaulted +
Send) to ensure macro-invocation rewriting is executed). Reference the existing
symbols Defaulted, DefHot/DefCold, and the test attr_default_body_dispatch when
adding these assertions.
In `@crates/macros/src/lib.rs`:
- Around line 320-328: expand_impl currently copies impl override method bodies
verbatim, so calls like self.area() inside an impl block don't get rewritten to
the generated sibling names; apply the same RewriteSelfCalls transformation used
in generate_spec_decls to those impl method blocks. Locate expand_impl and where
it captures the impl method body (the code that uses the impl method's
block/tokenstream), clone the Block into a mutable variable, instantiate
RewriteSelfCalls with the existing method_names collection, call
rewriter.visit_block_mut(&mut block) and then use the rewritten block when
quoting the generated __spec_* method instead of the original verbatim body so
sibling calls are renamed consistently.
🪄 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: e82e59df-f8b5-488b-a38f-e81c825d2693
📒 Files selected for processing (7)
Cargo.tomlcrates/core/tests/equivalence.rscrates/core/tests/ui_attr.rscrates/core/tests/ui_attr/attr_default_body.rscrates/core/tests/ui_attr/attr_default_override.rscrates/core/tests/ui_attr/attr_default_send.rscrates/macros/src/lib.rs
Apply the same RewriteSelfCalls transformation to impl method bodies in expand_impl, so self.method() inside a #[devirt] impl block is rewritten to self.__spec_method(). Without this, sibling calls in impl bodies fail to compile because the public trait is an empty marker. Extend attr_default_body_dispatch with three additional runtime cases: - DefOver: overrides the default is_big with a sibling call (self.get() > 1000), exercising impl-body rewriting - DefFmt: relies on the default describe() that uses format!() with self.get(), exercising macro-invocation token rewriting - Assertions via &dyn Defaulted and &(dyn Defaulted + Send) https://claude.ai/code/session_01BJen3TbKCFohcjNFeYwBiA
Trait methods with default bodies (e.g.
fn is_large(&self) -> bool { self.area() > 100.0 }) are now accepted by the proc-macro attribute.The default body is placed on the inner trait's
__spec_*providedmethod, with
self.method()calls rewritten toself.__spec_method()via
syn::visit_mutso sibling calls resolve within the inner trait.Macro invocations (format!, write!, etc.) are handled via token-level
rewriting since syn doesn't parse macro token streams.