Skip to content
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

Implement let-else formatting #5690

Merged
merged 8 commits into from
Jun 20, 2023

Conversation

ytmimi
Copy link
Contributor

@ytmimi ytmimi commented Feb 12, 2023

ref #4914

r? @calebcartwright

Implements let-else formatting based on the rules outlined in rust-lang/rust#107312. The examples listed in the style guide are included as simple test cases to validate let-else formatting.

Note This PR does not implement a "short" config for let-else.

@ytmimi
Copy link
Contributor Author

ytmimi commented Feb 12, 2023

I think the PR is good to go, but I plan to add some additional test cases with comments between the initializer expression and the else block as well as test out how the formatting is changed when users set control_brace_style.

@calebcartwright calebcartwright self-assigned this Feb 16, 2023
@calebcartwright
Copy link
Member

test out how the formatting is changed when users set control_brace_style.

This should not have an impact given the current applicable domains, as control_brace_style is only intended to apply to control flow expressions.

I recognize that there will likely be some non-zero portion of the user base that would like to force the brace onto the next line, but I think that needs to be considered against the more holistic lines of the posture outlined in #3376 (comment); I don't want to try to shoehorn yet another AST context into the existing config options

@ytmimi
Copy link
Contributor Author

ytmimi commented Feb 16, 2023

@calebcartwright, given the way I implemented let-else it's possible to use control_brace_style to configure where the else will be placed. For example, you can run rustfmt on the following input:

fn main() {
    let Some(x) = opt else {
        return;
    };
}

with control_brace_style=ClosingNextLine:

fn main() {
    let Some(x) = opt
    else {
        return;
    };
}

or with control_brace_style=AlwaysNextLine:

fn main() {
    let Some(x) = opt
    else
    {
        return;
    };
}

I implemented the else block rewriting for let-else by reusing existing functionality from if-else. If you'd prefer let-else formatting to not be influenced by control_brace_style I can certainly make an update to the PR. I thought it would be worth writing some tests for that functionality given that it's currently built in.

@calebcartwright
Copy link
Member

If you'd prefer let-else formatting to not be influenced by control_brace_style I can certainly make an update to the PR. I thought it would be worth writing some tests for that functionality given that it's currently built in.

To clarify, I was speaking about expected formatting behavior, and not current/actual behavior of the proposed implementation. I recognize that there's a... control flow aspect to this given the divergent nature of the else block (certainly more so than other types of statement, item, etc. contexts), but I'm still iffy on whether or not we should tie control_brace_style to this

@fgsch
Copy link

fgsch commented Mar 20, 2023

Hi. Is there anything we outsiders can help with to get this moving forward?

@ytmimi
Copy link
Contributor Author

ytmimi commented Mar 23, 2023

Hi. Is there anything we outsiders can help with to get this moving forward?

@fgsch thanks for asking! I always appreciate feedback, so if anyone has thoughts on simplifying the implementation or adding more clarity to the code please let me know. Additionally, if anyone has ideas for test cases that haven't already been covered that would be a great help.

@calebcartwright
Copy link
Member

Echo the appreciation. My assumption was that it was more of a bump to gauge status, so also want to note that this is one we'd discussed offline in chat/team meetings. I owned the action item to recap those discussions and remaining elements back here on the PR and obviously failed there, so apologies for the lack of transparency.

I do think we're just about sorted implementation wise, but also want to note that there will be at least one more implementation PR and then some release/communication work to follow before this actually gets into user's hands.

let Some(x) = opt else {
// nope
return;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

I think there's a missing case of

let Some(x) = opt else {
    return
}

Also I don't see any examples with long patterns after let. Probably good to add variation to the types of patterns in general.

Copy link
Contributor

Choose a reason for hiding this comment

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

In particular, struct patterns, tuples and arrays can raise some unique formatting concerns.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think there's a missing case of

let Some(x) = opt else {
    return
}

I can add a test case for that!

Also I don't see any examples with long patterns after let

Now that you bring it up I should probably add one or two to make sure that we wrap the else block onto its own line if the pattern is multi-lined.

@B-Reif
Copy link

B-Reif commented Apr 12, 2023

Another quick bump here :) Please let us outsiders know if we can be of help.

@calebcartwright
Copy link
Member

Another quick bump here :) Please let us outsiders know if we can be of help.

Appreciate the offer, but there's nothing needed from the community nor anything the community can do to help (unless you happen to review the changes and think you've spotted an issue).

Similarly, there's no need for any continued bumps on the thread. This is not forgotten, this is currently our top priority. However, as I alluded to in #5690 (comment), there's a lot of moving parts required to get to a point where let-else support will be available in released versions of rustfmt, the majority of which are unrelated to this particular PR and which are not going to be reflected here.

It's intentional that we've not merged this PR yet, and this PR being merged vs. unmerged does not accurately portray status.

@calebcartwright
Copy link
Member

@ytmimi we should be all set now on being able to merge, so if you could rebase whenever time permits that'd be most appreciated

The function properly handles recovering comments before and after the
`else` keyword, and properly handles how to write the else when users
configure `control_brace_style`.
This will make it easier to format the divergent blocks of `let-else`
statements since it'll be easier to prevent the block from being
formatted on a single line if the preconditions aren't met.
`rewrite_let_else_block` gives us more control over allowing the `else`
block to be formatted on a single line than
`<ast::Block as Rewrite>::rewrite`.
These test cases try to cover various edge cases. For example, comments
around the else keyword and long, unbreakable, single-line initializer
expressions, and long patterns.
This helps to prevent max width errors.
@calebcartwright calebcartwright merged commit 9386b32 into rust-lang:master Jun 20, 2023
27 checks passed
@calebcartwright calebcartwright added the release-notes Needs an associated changelog entry label Jun 20, 2023
@ytmimi ytmimi deleted the let_else_formatting branch June 20, 2023 13:42
Comment on lines +15 to +18
let Some(x) = opt else {
// nope
return;
};
Copy link

Choose a reason for hiding this comment

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

as described here, this should have been

    let Some(x) = opt else {
        // nope
        return
    };

without ; after return to make sure that a single expression is not formatted on a single line if it contains a comment

Indy2222 added a commit to Indy2222/de that referenced this pull request Jul 2, 2023
Rustfmt version 1.6.0 released on 2023-07-02 added support for let-else
statement (PR rust-lang/rustfmt#5690). Make the
code compliant.
Indy2222 added a commit to DigitalExtinction/Game that referenced this pull request Jul 2, 2023
Rustfmt version 1.6.0 released on 2023-07-02 added support for let-else
statement (PR rust-lang/rustfmt#5690). Make the
code compliant.
@ytmimi ytmimi removed the release-notes Needs an associated changelog entry label Oct 5, 2023
Molter73 added a commit to stackrox/berserker that referenced this pull request Oct 5, 2023
Formatting for this kind of statements was added by
rust-lang/rustfmt#5690. We had statements prior
to this being available, so we should now fix those.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants