Skip to content

Fix rustfmt giving up on formatting when multi-line strings exceed max_width - #6781

Open
thomasqueirozb wants to merge 7 commits into
rust-lang:mainfrom
thomasqueirozb:fix-multiline-string-formatting
Open

thomasqueirozb wants to merge 7 commits into
rust-lang:mainfrom
thomasqueirozb:fix-multiline-string-formatting

Conversation

@thomasqueirozb

@thomasqueirozb thomasqueirozb commented Jan 26, 2026 •

Copy link
Copy Markdown

Summary

Fix rustfmt giving up on formatting when multi-line strings exceed max_width.

rustfmt would refuse to format code containing multi-line string literals that exceed max_width. The problem occurred because filtered_str_fits() in src/utils.rs would reject valid formatting attempts when string content (which cannot be shortened) exceeded width limits.

Added test fixtures for both issues:

  • tests/source/issue-6769.rs / tests/target/issue-6769.rs - byte string literals in array tuples
  • tests/source/issue-6778.rs / tests/target/issue-6778.rs - multi-line strings in struct fields

References


Note that reverting commit fa5b1e4 "breaks" a bunch of fixtures for the better, allowing formatting of rust code even after very long strings.

Example:

Mismatch at tests/source/string_punctuation.rs:5:
         "ThisIsAReallyLongStringWithNoSpaces.It_should_prefer_to_break_onpunctuation:\
          Likethisssssssssssss"
     );
-    format!("{}__{}__{}ItShouldOnlyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyNoticeSemicolonsPeriodsColonsAndCommasAndResortToMid-CharBreaksAfterPunctuation{}{}",x,y,z,a,b);
+    format!(
+        "{}__{}__{}ItShouldOnlyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyNoticeSemicolonsPeriodsColonsAndCommasAndResortToMid-CharBreaksAfterPunctuation{}{}",
+        x, y, z, a, b
+    );

and

Mismatch at tests/source/issue-2179/one.rs:27:
         } else {
             for package in &opts.package {
                 if let None = ws.members().find(|x| x.name() == package) {
-                    warn!("cargo - couldn't find member package `{}` specified in `analyze_package` configuration", package);
+                    warn!(
+                        "cargo - couldn't find member package `{}` specified in `analyze_package` configuration",
+                        package
+                    );
                 }
             }
         }

It does, however, break tests/source/issue-1210/c.rs in a not so desirable way:

Mismatch at tests/source/issue-1210/c.rs:1:
 // rustfmt-format_strings: true
 // rustfmt-max_width: 50

-const foo: String =
-    "trailing_spaces!!
+const foo: String = "trailing_spaces!!
                 keep them! Amet neque. Praesent \
-     rhoncus eros non velit.";
+                     rhoncus eros non velit.";

I'd encourage someone to take a look at this and follow up if possible.


Note: This PR was generated with assistance from an LLM (Claude). While I'm happy to address minor feedback, I may not have bandwidth to drive major revisions or extensive back-and-forth due to time constraints. If this approach looks promising but needs significant changes, feel free to take over the branch or use this as a starting point. I mainly wanted to get a potential fix out there since these issues were affecting my workflow but since the issue was actually fixed I thought it was worth sharing these changes.

@rustbot rustbot added the S-waiting-on-author Status: awaiting some action (such as code changes or more information) from the author. label Jan 26, 2026
@thomasqueirozb
thomasqueirozb marked this pull request as ready for review January 26, 2026 20:00
@rustbot rustbot added S-waiting-on-review Status: awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: awaiting some action (such as code changes or more information) from the author. labels Jan 26, 2026
@ytmimi

ytmimi commented Jan 26, 2026

Copy link
Copy Markdown
Contributor

Just ran the Diff-Check, and its already started finding breaking formatting changes. There's no way we can move forward with the PR as is.

@ytmimi

ytmimi commented Jan 26, 2026

Copy link
Copy Markdown
Contributor

These changes would need to be style_edition gated, but even then I'm not sure if the formatting changes that they introduce are worth it.

@rustbot author

@rustbot rustbot added S-waiting-on-author Status: awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: awaiting review from the assignee but also interested parties. labels Jan 26, 2026
@rustbot

rustbot commented Jan 26, 2026

Copy link
Copy Markdown
Collaborator

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@thomasqueirozb

thomasqueirozb commented Jan 26, 2026 •

Copy link
Copy Markdown
Author

This is unfortunate. All checks passed locally so I assumed this was fine but I wasn't aware of diff-check. Thanks for looking into this. I think I'll close this PR then, but someone can probably use this as a starting point.

@rustbot rustbot removed the S-waiting-on-author Status: awaiting some action (such as code changes or more information) from the author. label Jan 26, 2026
@thomasqueirozb

thomasqueirozb commented Jan 27, 2026 •

Copy link
Copy Markdown
Author

@ytmimi just took a closer look at that check and it looks like most changes are not due to rustfmt working on those lines where it previously wasn't.
Example:

2026-01-26T21:37:12.231227Z ERROR check_diff: Diff found in 'mdBook' when formatting mdBook/tests/testsuite/markdown.rs
--- original
+++ modified
@@ -8,13 +8,15 @@
 // Checks custom header id and classes.
 #[test]
 fn custom_header_attributes() {
-    BookTest::from_dir("markdown/custom_header_attributes")
-        .check_main_file("book/custom_header_attributes.html", str![[r##"
+    BookTest::from_dir("markdown/custom_header_attributes").check_main_file(
+        "book/custom_header_attributes.html",
+        str![[r##"
 <h1 id="attrs"><a class="header" href="#attrs">Heading Attributes</a></h1>
 <h2 class="class1 class2" id="heading-with-classes"><a class="header" href="#heading-with-classes">Heading with classes</a></h2>
 <h2 id="both" class="class1 class2"><a class="header" href="#both">Heading with id and classes</a></h2>
 <h2 myattr="" otherattr="value" id="myh3" class="myclass1 myclass2"><a class="header" href="#myh3">Heading with attribute</a></h2>
-"##]]);
+"##]],
+    );
 }

If you run rustfmt on the current code block it'd format it correctly (how the + lines show it) IF you make the <h1>/<h2> strings shorter.

The only change I'd think is maybe undesirable is this one (and even then I'm not sure if this is a case of "rustfmt now works when previously it gave up").

2026-01-26T21:46:34.084616Z ERROR check_diff: Diff found in 'rust' when formatting rust/compiler/rustc_errors/src/tests.rs
--- original
+++ modified
@@ -31,11 +31,13 @@

 #[test]
 fn wellformed_fluent() {
-    let translator = make_translator("mir_build_borrow_of_moved_value = borrow of moved value
+    let translator = make_translator(
+        "mir_build_borrow_of_moved_value = borrow of moved value

What do you think?


Update I found the source code for the last one:

#[test]
fn wellformed_fluent() {
    let translator = make_translator("mir_build_borrow_of_moved_value = borrow of moved value
    .label = value moved into `{$name}` here
    .occurs_because_label = move occurs because `{$name}` has type `{$ty}` which does not implement the `Copy` trait
    .value_borrowed_label = value borrowed here after move
    .suggestion = borrow this binding in the pattern to avoid moving the value");
}

If you remove some chars from the 3rd line ( .occurs_because_label = move...) to make it in total 100 chars or less rustfmt will format it like so:

#[test]
fn wellformed_fluent() {
    let translator = make_translator(
        "mir_build_borrow_of_moved_value = borrow of moved value
    .label = value moved into `{$name}` here
    .occurs_because_label = move occurs because `{$name}` has type `{$ty}` which does not implement 
    .value_borrowed_label = value borrowed here after move
    .suggestion = borrow this binding in the pattern to avoid moving the value",
    );
}

(I removed everything after implement to make it 100 chars total)

Therefore this is indeed a case of "rustfmt now works when previously it gave up".

I don't think this should be style_edition gated since the behavior is the same, it's just properly working now

@rustbot rustbot added the S-waiting-on-review Status: awaiting review from the assignee but also interested parties. label Jan 27, 2026
@ytmimi

ytmimi commented Jan 27, 2026

Copy link
Copy Markdown
Contributor

I haven't re-reviewed the change but as I mentioned in my earlier comment these changes would need to be style_edition gated before we could consider merging the PR. I'd also want to review the diff more carefully to see what changes are getting produced.

@thomasqueirozb

thomasqueirozb commented Jan 27, 2026 •

Copy link
Copy Markdown
Author

these changes would need to be style_edition gated

would that be the case even if this is a bug fix and is just restoring expected rustfmt behavior? I think I can add a gate if that's the case

@ytmimi

ytmimi commented Jan 28, 2026

Copy link
Copy Markdown
Contributor

Yes, there's a strong stability guarantee so these have to be gated

@ytmimi

ytmimi commented Jan 29, 2026

Copy link
Copy Markdown
Contributor

@rustbot author

@rustbot rustbot added S-waiting-on-author Status: awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: awaiting review from the assignee but also interested parties. labels Jan 29, 2026
@jieyouxu jieyouxu added B-next-style-edition-gate Blocked: can only be done over a style edition; needs a style edition gate X-requires-next-style-edition Expected formatting impact: can only be done over a style edition (caution) X-impacts-stable-formatted-code Expected formatting impact: affects stable formatted code (caution) labels Feb 12, 2026
@jieyouxu jieyouxu added the X-impacts-stable-default-format Expected formatting impact: affects stable default format configuration (caution) label Feb 20, 2026
@rustbot

rustbot commented Feb 25, 2026

Copy link
Copy Markdown
Collaborator

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

Comment thread src/utils.rs
pub(crate) fn wrap_str(s: String, max_width: usize, shape: Shape) -> Option<String> {
if filtered_str_fits(&s, max_width, shape) {
pub(crate) fn wrap_str(
style_edition: StyleEdition,

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Wasn't sure if it was better to pass in StyleEdition or Context<'_> here. Since max_width was already present I opted for StyleEdition

@@ -0,0 +1,28 @@
// rustfmt-style_edition: 2027

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Verified that the tests breaks without this (git revert d46365d25325954cbc1c1475d840657f519c6818 && cargo test)

@thomasqueirozb

Copy link
Copy Markdown
Author

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: awaiting some action (such as code changes or more information) from the author. labels Feb 25, 2026
@ytmimi ytmimi added the A-style-edition-2027 Area: style edition 2027 label Feb 25, 2026
@jieyouxu jieyouxu removed the B-next-style-edition-gate Blocked: can only be done over a style edition; needs a style edition gate label Mar 3, 2026
@thomasqueirozb

Copy link
Copy Markdown
Author

👀 I read that the rustfmt team is backlogged on reviews. Is there anything I can do to facilitate the review process? Thanks!

@ytmimi

ytmimi commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

@thomasqueirozb At the moment I don't think there's much that can be done to move this PR along. Thank you for reaching out though. We will take a look at this when we've got more time.

@rustbot

rustbot commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator

☔ The latest upstream changes (possibly #7124) made this pull request unmergeable. Please resolve the merge conflicts.

This branch has not been deployed

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

Labels

A-style-edition-2027 Area: style edition 2027 S-waiting-on-review Status: awaiting review from the assignee but also interested parties. X-impacts-stable-default-format Expected formatting impact: affects stable default format configuration (caution) X-impacts-stable-formatted-code Expected formatting impact: affects stable formatted code (caution) X-requires-next-style-edition Expected formatting impact: can only be done over a style edition (caution)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: rustfmt silently breaks on this sample rustfmt gives up if second part of multiline string is >= 100 chars long

4 participants