Skip to content

Strip the r# prefix from raw identifiers in derive(Debug) - #402

Merged
Veetaha merged 1 commit into
elastio:masterfrom
MaxFreedomPollard:debug-raw-idents
Sep 7, 2026
Merged

Strip the r# prefix from raw identifiers in derive(Debug)#402
Veetaha merged 1 commit into
elastio:masterfrom
MaxFreedomPollard:debug-raw-idents

Conversation

@MaxFreedomPollard

Copy link
Copy Markdown
Contributor

#[builder(derive(Debug))] treats the r# escape as part of the name, so a builder whose type or members use raw identifiers prints the prefix:

#[derive(Builder)]
#[builder(builder_type = r#type, derive(Debug))]
struct Sut {
    #[builder(start_fn)]
    r#struct: u32,

    #[builder(field)]
    r#enum: u32,

    r#while: u32,
}

let builder = Sut::builder(1).r#while(2);

// On master:
assert_eq!(format!("{builder:?}"), "r#type { r#struct: 1, r#enum: 0, while: 2 }");

The std #[derive(Debug)] prints type, struct and enum for those same names, and bon already does it for the r#while member.

The cause is in bon-macros/src/builder/builder_gen/builder_derives/debug.rs. Named members are formatted with MemberName::snake_raw_str on line 40, which is the name with the prefix removed. The other three names come from Ident::to_string(), which keeps it: the builder struct name passed to Formatter::debug_struct on line 80, #[builder(start_fn)] members on line 14, and #[builder(field)] members on line 27.

All three now use IdentExt::raw_name(), the helper that snake_raw_str is itself built from. #[builder(name = ...)] cannot be combined with start_fn or with field, so for those members the original identifier is the only name they have and stripping the prefix is the whole fix.

The regression test is test_derive_debug in bon/tests/integration/builder/raw_idents.rs. It fails on master with r#type { r#struct: 1, r#enum: 0, while: 2 } and passes here with type { struct: 1, enum: 0, while: 2 }.

Verified on the pinned 1.98.0 toolchain: cargo fmt --check, cargo clippy -p bon -p bon-macros --all-features --all-targets under RUSTFLAGS=--deny warnings, cargo test -p bon-macros --all-features, cargo test -p bon --all-features --doc (7 passed) and cargo test -p bon --all-features --test integration (204 passed). No .stderr snapshot changed.

`#[builder(derive(Debug))]` printed the `r#` escape as if it were part of the
name. A builder whose type and members use raw identifiers formatted as
`r#type { r#struct: 1, r#enum: 0, while: 2 }`, while the std
`#[derive(Debug)]` prints those same names as `type`, `struct` and `enum`.

Named members were already correct: `derive_debug` in
`bon-macros/src/builder/builder_gen/builder_derives/debug.rs` formats them
with `MemberName::snake_raw_str`, which is the name with the prefix removed.
The three other names in the generated `fmt` came from `Ident::to_string()`,
which keeps it:

* the builder struct name passed to `Formatter::debug_struct`, reachable
  through `#[builder(builder_type = r#type)]`
* `#[builder(start_fn)]` member names
* `#[builder(field)]` member names

All three now use `IdentExt::raw_name()`, the helper `snake_raw_str` is itself
built from. `#[builder(name = ...)]` cannot be combined with `start_fn` or
with `field`, so for those members the original identifier is the only name
they can have and stripping the prefix is the whole fix.

@Veetaha Veetaha left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks, I'll make a patch for this

@Veetaha
Veetaha merged commit 300abd6 into elastio:master Sep 7, 2026
31 of 33 checks passed
@github-actions github-actions Bot mentioned this pull request Sep 7, 2026
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