-
Notifications
You must be signed in to change notification settings - Fork 888
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
rustfmt formats different arms of the same match inconsistently #3995
Comments
More generally, I disagree with the way rustfmt eagerly turns pat =>
very_long_expr,
pat =>
short_expr into pat =>
very_long_expr,
pat => short_expr and it also turns pat => {
stmt;
expr
}
pat => {
expr
} into pat => {
stmt;
expr
}
pat => expr
// or expr might be on its own line if it is long enough In a long IMO, rustfmt should honor my choice to start Alternatively, if you want to enforce some rule, my proposal would be to only ever have |
=>
after a multi-line pattern=>
(Moved to separate issue: #4004) original commentAh, and I just realized that yet another formatting choice of rustfmt that I strongly dislike is also related: rustfmt will turn pat =>
fun(
a,
b,
c,
d,
) into pat => fun(
a,
b,
c,
d,
) I think this is a total no-go. In a long We also don't generate code like this if foo { fun(
a,
b,
c,
d,
)} so why is it okay to do the equivalent things for match arms? |
(Moved to separate issue: #4005) original commentAnother way in which rustfmt introduces inconsistency into long match ty.kind {
// Types without identity.
ty::Bool
| ty::Char
| ty::Int(_)
| ty::Uint(_)
| ty::Float(_)
| ty::Str
| ty::Array(_, _)
| ty::Slice(_)
| ty::RawPtr(_)
| ty::Ref(_, _, _)
| ty::FnPtr(_)
| ty::Never
| ty::Tuple(_)
| ty::Dynamic(_, _) => self.pretty_print_type(ty),
// Placeholders (all printed as `_` to uniformize them).
ty::Param(_) | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) | ty::Error => {
write!(self, "_")?;
Ok(self)
} Before rustfmt ran, this was neatly formatted with one pattern per line and never mixing patterns and code on the same line. Now it's all over the place. Should I open a separate bug for this? |
Here's an example diff of what rustfmt does where I disagree with every single change it is making: rust-lang/rust@ff5b102. These all introduce inconsistencies in match formatting. And here's how I manually formatted a long match, I had to disable rustfmt for that as there seems to be no way to make rustfmt not destroy the formatting: rust-lang/rust@e44f3e7 |
Regarding the third comment, isn't this incorrect behaviour as per the specification? I was under the impression that pat =>
fun(
a,
b,
c,
d,
) should be reformatted into pat => {
fun(
a,
b,
c,
d,
)
} as it's not a single-line expression. |
Turns out this was already a concern back when the formatting got implemented:
AFAIK, no such option exists yet. And indeed I agree entirely. I'd maybe add one thing: even when none of the branches are wrapped, there's still the choice of putting the expression on the same line as the |
=>
I moved formatting of pattern alternatives to a separate issue: #4005. So this one is now just about making the body of the various match arms of the same match more consistent. |
Here's another example of what I consider bad match formatting: let kind = match rvalue {
Rvalue::Ref(_, borrow_kind, _)
if borrow_kind.allows_two_phase_borrow() =>
{
RetagKind::TwoPhase
}
Rvalue::AddressOf(..) => RetagKind::Raw,
_ => RetagKind::Default,
}; Notice how all match arms are just an enum constructor, but one of them gets curly braces forcibly added around it. |
👍 This is my core complaint with rustfmt, I think. It seems to regularly lose consistency between items in sequences, be that array literals, function arguments, match arms, whatever. I'd definitely like to see it make consistent choices across them, rather than the ones that today seem to be made in isolation. |
Any update on that? Looking at the close issue above, it seems that the question is more at the level of the style guide rather than in the implementation of rustfmt, but nonetheless as a user I find it very annoying that different arms of match get formatted differently: this breaks symmetry and leads to less readable code IMHO. |
Just to add a differing viewpoint - I actually prefer in many cases that rustfmt does what it currently does re: making some statements on one line vs. making all multiline. For things like https://github.com/pluots/zspell/blob/71ae77932f2cc67f593d846a26a9ffd7cf6d0412/crates/zspell/src/affix.rs#L276-L367 for example, I agree with what rustfmt does. 61 matches fit on 91 lines this way, but forcing them to braced multiline would make the total double to 183 lines. (could have been smaller if I used My personal rule would be that the default should be whichever matches 50% of the statements; if a majority fit on one line, make that the default and have others be an exception. If a majority take >1 line, then make them all be that way. Now of course that can't actually be a rule (code churn...) But I don't think that 1 or 2 multiline expressions should change entire large match statements (I guess that would cause churn too). Haven't seen this discussed really though, so maybe it's not in the question anymore. Just wanted to add some 2¢. |
This would be a nice feature. Match blocks have always been the one feature about rust formatting that always felt off. |
I recently had a situation where I changed an enum variant and that changed formatting of all the other enum variants as well. Does that mean rustfmt already has the ability to take other variants/arms into account when formatting? I am not sure if the behavior I saw was a bug or precedent for doing something similar with match arms as well. |
Psycho behaviour by rustfmt: let (network_magic, port, seeds, filename) = match &args.network {
Some(network) => match network.as_str() {
"mainnet" => (MAGIC_MAINNET, PORT_MAINNET, SEEDS_MAINNET, "mainnet-nodes.txt"),
"testnet" => (MAGIC_TESTNET,PORT_TESTNET,SEEDS_TESTNET, "testnet-nodes.txt"),
"signet" => (MAGIC_SIGNET, PORT_SIGNET, SEEDS_SIGNET, "signet-nodes.txt"),
"regtest" => (MAGIC_REGTEST,PORT_REGTEST, &["localhost"][..], "regtest-nodes.txt"),
_ => std::process::exit(1),
},
None => (MAGIC_MAINNET,PORT_MAINNET,SEEDS_MAINNET,"mainnet-nodes.txt"),
}; becomes: let (network_magic, port, seeds, filename) = match &args.network {
Some(network) => match network.as_str() {
"mainnet" => (
MAGIC_MAINNET,
PORT_MAINNET,
SEEDS_MAINNET,
"mainnet-nodes.txt",
),
"testnet" => (
MAGIC_TESTNET,
PORT_TESTNET,
SEEDS_TESTNET,
"testnet-nodes.txt",
),
"signet" => (MAGIC_SIGNET, PORT_SIGNET, SEEDS_SIGNET, "signet-nodes.txt"),
"regtest" => (
MAGIC_REGTEST,
PORT_REGTEST,
&["localhost"][..],
"regtest-nodes.txt",
),
_ => std::process::exit(1),
},
None => (
MAGIC_MAINNET,
PORT_MAINNET,
SEEDS_MAINNET,
"mainnet-nodes.txt",
),
}; |
This seems like an unnecessary and unhelpful characterization. Your snippet isn't complete and directly reproducible so I'll assume you are running rustfmt with the default configuration values, but the behavior you are complaining about isn't related to match expression formatting regardless. To demonstrate, let's show the tuple without any match context: fn foo() {
let x = (MAGIC_TESTNET, PORT_TESTNET, SEEDS_TESTNET, "testnet-nodes.txt");
} And then let's consider the relevant rules dictated by the Rust Style Guide
So we look at the rules for function calls:
The threshold for whether a function call (or tuple) qualifies as "small" is controlled by the fn_call_width setting that has a default value of The Style Guide rules for match expressions does not have a clause that says "but ignore all the other rules in this style guide to keep visual consistency across match arms". There is nothing "psycho" here.. rustfmt is simply, and correctly, applying the Style Guide rules for tuples |
Is there a process to propose amending the style guide? I can imagine something like saying that all match arms should be of the formatting style of the biggest of them, in order to ensure consistency. |
I have opened an issue in the style-team repo: rust-lang/style-team#196.
That's not the right way to talk to other people, please rethink your choice of words. Maybe cool down for an hour or two before pressing the "submit" button when you are writing a bugreport in frustration.
I think most people will reasonably assume that the rustfmt issue tracker is the place to file problems with the way rustfmt formats, without caring much whether the source for this behavior is the style guide, or a rustfmt bug where it doesn't match the style guide. In the rustc repo, we don't use completely different issue trackers for "the compiler doesn't follow documented behavior" and "the compiler follows documented behavior but I think that is the wrong behavior". We generally can't expect users to even know which of these categories their issue falls into. So it's a bit surprising to see this split on the rustfmt/style side, and it will surely lead to users reporting issues in the wrong repo. |
Yes, see RFC 3338 for more details, and there's a number of changes coming as part of Rust's 2024 Edition Though in general I'd say that modifying the style guide and default behavior is a more difficult road than adding opt-in behavior to rustfmt via a config option. The behavior Ralf originally requested/reported in this issue of being able to have consistency around the match arms (not the contents of the arms as was unrelatedly requested by some others, but consistency across all arms on whether they are wrapped in braces or have newlines added/removed between the operator and the start arm body) is something that could absolutely be implemented within rustfmt and made available to users via a config option. I don't think it would be an exceptionally difficult nor large implementation; it's just not something that's gotten much attention.
t-style is plenty active, though like t-rustfmt and ostensibly most teams, focus is centered around the 2024 edition. I've nominated the issue you opened to get it added to future t-style meeting agenda's, though I doubt it's something we'll spend many cycles on until after we're done with 2024
Agreed, and I'll just add that we aren't feigning surprise on this, we aren't criticizing anyone for not knowing that, nor for making a reasonable, but incorrect assumption, that rustfmt is the final authority on style. It's common for people to not know there's a standard style, and it's common for us to inform/educate users that there is an official spec rustfmt adheres to, and that's okay; it's why we include links. However, at the same time I do think too many people come to the table with their own personal stylistic preferences, make an (imo unreasonable) assumption that rustfmt will always format exactly according to their personal preferences, and that any deviations from those subjective preferences trigger some rather emotive reactions |
The big rustc format made some code I care about much less readable. Here's an example:
The actual code running here for this branch got moved onto the same line as the last line of a pattern. That makes it quite hard to visually identify this code -- compared to the old version of the code, the visual separation of pattern and code got lost.
The text was updated successfully, but these errors were encountered: