rustfmt: RustFmt fails to format file with long line within scope of lambda passed to member function

fn f() {
    something.do_stuff(|| {
                {
                    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
                }
            }   );
}

I tried to reduce it more, but any change caused it to work again.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 18
  • Comments: 17 (7 by maintainers)

Most upvoted comments

There is no good formatting here because the string lit is too long for the max width. SO I think in this case, doing nothing and let the user fix it is the right thing to do.

I faced this issue today, and it tooks me 5 hours to understand the problem was coming from my string which was too long, but rustfmt gave me no warning nor error. Considering adding a warning/an hard error for such cases instead would be feasible ?

What’s the status of this bug? Will it be fixed?

@calebcartwright I apologize for my poor behavior. It was wrong of me to express my frustration here. Thank you for the gracious manner in which you responded to my not-so-gracious comment. Also, thanks for your hard work on rustfmt that has immensely benefited me and thousands of others.

I’m not sure why the rest of the module was not being formatted (it was actually the rest of the file, not the module–my tests in a separate module in the same file also were not being formatted using the command-line, not an editor plugin), but with an adjustment to a long string it is being formatted correctly now.

I’ll leave my comment on this issue unedited as an example of how not to post comments.

@scampi Indeed, I checked again and it does trigger the bug as well. I think I got confused because the behavior around tuples and whitespaces is really weird:

fn main() {
    foo.bar({
underindented();
{(  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,)}
    });
}

triggers the bug, but if you do any of the following, it no longer triggers:

  • Remove one x
  • Remove one space before the xs
  • Remove the comma
  • Replace the comma with an x
  • Put the spaces before the opening paren ({ (x...).

BTW I will reiterate that rustfmt works properly with :: instead of .:

fn main() {
    foo::bar({
underindented();
{"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
    });
}

gets formatted into

fn main() {
    foo::bar({
        underindented();
        {
            "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
        }
    });
}

which is arguably the “least bad formatting”, even if no “good” ones exist.

No worries, we’ve all been there 😄

I realize this issue has been open longer, but I’m going to close in favor of #3863 because they’re duplicative reports but the latter has more focused/actionable discussion.

That thread covers the expected/default behavior, existing workarounds and config options, options you’d need to enable to receive warnings/errors, and request for feedback on some new options under consideration.

For the non-chain related snippets that were tacked on to this thread:

  • singular calls (including macro calls) can set version=Two in their config files if they want those to be formatted
  • macro calls can really only be formatted if all of their args are valid rust code; otherwise the calls are mostly left as-is

@ted-tanner I appreciate that you’re frustrated, but want to note that expressing that frustration across multiple issues doesn’t really help move anything forward.

That is no excuse for why rustfmt wouldn’t format anything in the entire module

This doesn’t sound right. Are you sure you’re running rustfmt or cargo fmt directly, as opposed to editor/IDE driven formatting? I don’t want to repeat what’s already been shared above and in the linked issue, but the main behavior of not reformatting the chain with long content is specific to the chain itself; rustfmt will absolutely format other content. Please note that certain editor/IDE plugins opted to not format anything in the file in these cases, but that’s a decision those unrelated solutions made and not anything rustfmt controls.

You can see rustfmt’s behavior in action via this simple playground snippet. If you’re still seeing the behavior you described then please open a new issue with a complete MCVE that can be reproduced.

Considering adding a warning/an hard error for such cases instead would be feasible ? (and was completely silent about it) A warning about the length of the string would also help.

Already covered in https://github.com/rust-lang/rustfmt/issues/3863#issuecomment-700441739, tl;dr the ability to do so exists but it’s turned off by default.

This one fails to split a macro (instead of the long string):

fn f() {
    something.do_stuff
    (
    |
    | 
    {
        error!(context.logger, "metric_production_abcdefgere"; "reason_abcdefg" => format!("{}", e));
    }
    );
}

Making the line any shorter will format the rest of the code normally, but still not split the macro:

fn f() {
    something.do_stuff(|| {
        error!(context.logger, "metric_production_abcdefgere"; "reason_abcde" => format!("{}", e));
    });
}

I hit this today, so here is another example with more info. Notes from my experiments:

  • Rustfmt only fails to format the block containing the long line
  • There needs to be a method call. If calling bar(..) instead of foo.bar(..) things work as expected. Using Foo::bar is a workaround.
  • There needs to be a block inside the method call (so you need foo.bar({ .. }), not just foo.bar(..)).
  • The “long token” doesn’t need to be a string, but it needs to be inside some other expression. Curly brackets {xxx} or a function call f(xxx) both trigger the bug; a tuple (xxx, ) does not.
fn main() {
    rustfmt.will(properly + reformat).this();
    foo.bar({
rustfmt        .
will(NOT
+
touch
                        )
.this()
;
{"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
    });
}

Edit: By “Using Foo::bar is a workaround”, I mean that you can get the right formatting by temporarily using foo::bar instead of foo.bar, running rustfmt, then reverting back to foo.bar. Since rustfmt won’t touch the code in the foo.bar case you get to keep the good formatting even after changing back to foo.bar.