Skip to content

dx fmt grows indentation of multi-line raw string literals inside deeply nested closures on every run (non-idempotent) #5804

Description

@Dicklessgreat

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 <file> 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.
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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions