DioxusLabs / DioxusLabs/dioxus
`dx fmt` grows indentation of multi-line raw string literals inside deeply nested closures on every run (non-idempotent)
- Dominant language
- Rust
- Stars
- 39.1k
- Forks
- 1.9k
- Avg merge
- 4d 10h
- Merged PRs (30d)
- 4
Description
### Bug description
`dx fmt` is not idempotent when a multi-line raw string literal (`r#"..."#`) appears inside a deeply nested block (e.g. a closure passed to `spawn`, inside a `match` arm, inside an `rsx!` element handler). Running `dx fmt` repeatedly on an already-formatted file keeps increasing the indentation of the string's content on every single run, instead of converging to a stable output. Since the content between the raw string's newlines is real string data (this pattern is commonly used to build up a JS snippet passed to `document::eval`), this also silently corrupts the string's *value*, not just its on-screen indentation.
We noticed this because our team's Claude Code PostToolUse hook runs `dx fmt -f ` after every edit. Over many edits to the same file, one particular raw-string literal grew to 1000+ characters of leading whitespace before we noticed.
### Steps to reproduce
1. Save the file below as `repro.rs` (no Cargo project needed — reproduces even with a bare `dx fmt -f`).
2. Run `dx fmt -f repro.rs` three times in a row, diffing after each run.
```rust
use dioxus::prelude::*;
#[component]
fn App() -> Element {
rsx! {
div {
for _ in 0..1 {
button {
onclick: move |_evt| {
spawn(async move {
match Ok::<(), ()>(()) {
Ok(()) => {
let script = format!(
r#"
requestAnimationFrame(() => {{
console.log("restore");
}});
"#
);
let _ = script;
}
Err(_) => {}
}
});
},
"click"
}
}
}
}
}
```
### Actual behavior
The indentation of the raw string's body grows on every single run (measured as line length in characters, including leading whitespace):
| | `requestAnimationFrame(() => {{` line length | `console.log(...)` line length |
|---|---|---|
| run 1 | 86 | 83 |
| run 2 | 102 | 99 |
| run 3 | 118 | 115 |
(+16 characters per run, unbounded — it never converges.)
### Expected behavior
`dx fmt` should be idempotent: formatting an already-formatted file should produce no diff. At minimum, it should not keep growing indentation without bound across repeated runs.
### Environment
- `dx --version`: `dioxus 0.7.10 (57d6794)`
- `rustc --version`: `rustc 1.95.0`
- OS: Linux x86_64
### Additional notes
We also observed a related (but distinct) non-idempotency bug in 0.7.5 involving multi-line continuations of a comparison inside an `rsx!` attribute value (e.g. `checked: some_call()\n== Other::Variant,`), which appears to have been fixed between 0.7.5 and 0.7.10. The raw-string case above is still present in 0.7.10.
As a workaround, we refactored the affected code to extract the raw string literal into a shallowly-nested top-level function instead of inlining it inside the deeply nested closure, which avoids triggering the bug.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with the dx fmt entry point and reproduce the issue using the bare repro.rs example, running dx fmt -f three times and comparing each result. Trace formatting of the multi-line raw string inside the nested closure; done means repeated formatting produces no diff and does not change the string value.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100