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

rust-lang/style-team#189: rhs-should-use-indent-of-last-line-of-lhs #6305

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

johnhuichen
Copy link
Contributor

@johnhuichen johnhuichen commented Sep 1, 2024

Pull Request Template

Checklist

  • Confirmed that cargo test passes

Related Issues/PRs

rust-lang/style-team#189

Changes

Make sure that the rhs is formatted using the indentation of the last line of lhs, as opposed to the first line

Testing

added a test case

@johnhuichen johnhuichen force-pushed the rust-lang/style-team#189-rhs-should-use-indent-of-last-line-of-lhs branch from e79db51 to fa8b854 Compare September 1, 2024 09:55
@johnhuichen johnhuichen changed the title Issue-5892: Type alias generic rewrite follows the same rule as trait rust-lang/style-team#189: rhs-should-use-indent-of-last-line-of-lhs Sep 1, 2024
@johnhuichen johnhuichen force-pushed the rust-lang/style-team#189-rhs-should-use-indent-of-last-line-of-lhs branch from 8e01441 to 23bfe0e Compare September 1, 2024 10:34
@johnhuichen johnhuichen marked this pull request as ready for review September 1, 2024 10:35
@johnhuichen johnhuichen force-pushed the rust-lang/style-team#189-rhs-should-use-indent-of-last-line-of-lhs branch 2 times, most recently from fb84edc to 6da81be Compare September 1, 2024 11:00
src/expr.rs Outdated Show resolved Hide resolved
Comment on lines +2107 to +2132
let new_shape = shape
.block_indent(tab_spaces)
.saturating_sub_width(tab_spaces);
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you explain the call to .saturating_sub_width(tab_spaces). I'm not sure if that's necessary.

Copy link
Contributor Author

@johnhuichen johnhuichen Sep 4, 2024

Choose a reason for hiding this comment

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

Without saturating_sub_width,

impl SomeType {
    fn method(&mut self) {
        self.array[array_index as usize]
            .as_mut()
            .expect("thing must exist")
            .extra_info =
            long_long_long_long_long_long_long_long_long_long_long_long_long_long_long;
    }
}

is formatted to

impl SomeType {
    fn method(&mut self) {
        self.array[array_index as usize]
            .as_mut()
            .expect("thing must exist")
            .extra_info = long_long_long_long_long_long_long_long_long_long_long_long_long_long_long;
    }
}

and throws

line formatted, but exceeded maximum width (maximum: 100 (see `max_width` option), found: 101)

rather than

impl SomeType {
    fn method(&mut self) {
        self.array[array_index as usize]
            .as_mut()
            .expect("thing must exist")
            .extra_info =
                long_long_long_long_long_long_long_long_long_long_long_long_long_long_long;
    }
}

I think the remaining width is not accounted for properly if I only use block_indent

Copy link
Contributor

Choose a reason for hiding this comment

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

Could you explain why we're using tab_spaces? What happens if we don't use .saturating_sub_width(tab_spaces), but the rhs was even longer? Does it wrap correctly then?

Part of me wonders if the shape is off to begin with.

Copy link
Contributor Author

@johnhuichen johnhuichen Sep 4, 2024

Choose a reason for hiding this comment

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

the shape that was used before adding extra block_indent is

old shape = Shape { width: 91, indent: Indent { block_indent: 8, alignment: 0 }, offset: 0 }

and the new shape with block_indent but without saturating_sub_width is

new shape = Shape { width: 91, indent: Indent { block_indent: 12, alignment: 0 }, offset: 0

Copy link
Contributor Author

@johnhuichen johnhuichen Sep 4, 2024

Choose a reason for hiding this comment

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

the new shape is also the shape of the last line of lhs

            .extra_info = long_long_long_long_long_long_long_long_long_long_long_long_long_long_long;

has 12 spaces before .extra_info and .extra_info = long_long_long_long_long_long_long_long_long_long_long_long_long_long_long; is 89 chars. So the remaining width shouldn't be 91, but 87

i.e. when indent was 8 spaces, width = 91, max width = 100, so
width = max - indent - 1

when indent is 12, width = 100 - 12 - 1 =87

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ytmimi I think the above should answer your last question in this PR. Can you take a look?

Copy link
Contributor

@ytmimi ytmimi Sep 9, 2024

Choose a reason for hiding this comment

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

Thanks for the detailed explanation. I didn't have time to revisit this PR over the weekend, but I plan on giving this another look later this week

src/expr.rs Outdated
Comment on lines 2110 to 2111
let extra_indent_string = new_shape.to_string(&context.config).to_string();
if last_line.starts_with(&extra_indent_string) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We shouldn't need to allocate a string with to_string(). We should be able to call as_ref():

Suggested change
let extra_indent_string = new_shape.to_string(&context.config).to_string();
if last_line.starts_with(&extra_indent_string) {
let extra_indent_string = new_shape.to_string(&context.config);
if last_line.starts_with(extra_indent_string.as_ref()) {

src/expr.rs Outdated
}
};
let shape = if context.config.style_edition() >= StyleEdition::Edition2024 {
get_lhs_last_line_shape()
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm wondering if get_lhs_last_line_shape is the right name. Technically what we're doing here is getting the shape used to format the rhs, but it's based on the lhs indentation. If you think you can come up with a succinct name that conveys that meaning that would be great!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

in that case I will just call it get_rhs_shape, leave the implementation details out of the naming

@@ -278,6 +278,12 @@ impl Shape {
offset_indent.to_string_inner(config, 0)
}

pub(crate) fn to_string(&self, config: &Config) -> Cow<'static, str> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably worth adding a doc comment so that we know that this function gives us a single line of indentation.

@johnhuichen johnhuichen force-pushed the rust-lang/style-team#189-rhs-should-use-indent-of-last-line-of-lhs branch from 6da81be to 24509fe Compare September 4, 2024 22:47
src/expr.rs Outdated
use rustc_ast::{ForLoopKind, MatchKind, ast, ptr, token};
use rustc_ast::{ast, ptr, token, ForLoopKind, MatchKind};
Copy link
Contributor

@ytmimi ytmimi Sep 4, 2024

Choose a reason for hiding this comment

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

I don't think this was rebased correctly. The imports should not have changed.

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 know, I am resolving some merge conflicts. Give me a few minutes

Copy link
Contributor Author

@johnhuichen johnhuichen Sep 4, 2024

Choose a reason for hiding this comment

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

So the mismatch was due to my formatter (Neoformat using neovim) using a different version of rustfmt than nightly the current master

@johnhuichen johnhuichen force-pushed the rust-lang/style-team#189-rhs-should-use-indent-of-last-line-of-lhs branch from f389fa1 to 58ca556 Compare September 4, 2024 22:55
@calebcartwright
Copy link
Member

Thank you both for the work on this, just noting I've added some labels to indicate the criticality of this one as it ties into 2024 deliverables.

I can help with the review if beneficial

@johnhuichen
Copy link
Contributor Author

Thank you both for the work on this, just noting I've added some labels to indicate the criticality of this one as it ties into 2024 deliverables.

I can help with the review if beneficial

Hey @calebcartwright now I am just waiting for a second look from @ytmimi. Do let me know if you see any changes I should make

Copy link
Contributor

Choose a reason for hiding this comment

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

There are a few other test cases that I think we should add:

impl SomeType {
    fn method(&mut self) {
        self.array[array_index as usize]
            .as_mut()
            .expect("thing must exist")
            .extra_info = Some(ExtraInfo {
                parent,
                count: count as u16,
                children: children.into_boxed_slice(),
            }) + Some(ExtraInfo {
                parent,
                count: count as u16,
                children: children.into_boxed_slice(),
            });
    }
}

impl SomeType {
    fn method(&mut self) {
        self.array[array_index as usize]
            .as_mut()
            .expect("thing must exist")
            .extra_info =
                long_long_long_long_long_long_long_long_long_long_long_long_long_long_long
                    .as_mut()
                    .another_call()
                    .get_extra_info();
    }
}

impl SomeType {
    fn method(&mut self) {
        self.array[array_index as usize]
            .as_mut()
            .expect("thing must exist")
            .extra_info =
                long_long_long_long_long_long_long_long_long_long_long_long_long_long_long
                    .as_mut()
                    .another_call()
                    .get_extra_info() + Some(ExtraInfo {
                        parent,
                        count: count as u16,
                        children: children.into_boxed_slice(),
                    });
    }
}

@calebcartwright
Copy link
Member

just checking for a status on this one, what's pending?

@ytmimi
Copy link
Contributor

ytmimi commented Oct 22, 2024

just checking for a status on this one, what's pending?

For me, I'd like to confirm the formatting for the extra test cases mentioned in #6305 (comment).

Assuming those get formatted as expected I think we're good to go for assignments.

@calebcartwright we may also need to tweak how we format binary expressions, but I'm not 100% sure. Just going based off of rust-lang/style-team#189 (comment). For example:

impl SomeType {
    fn method(&mut self) {
        self.array[array_index as usize]
            .as_mut()
            .expect("thing must exist")
            .extra_info +
                long_long_long_long_long_long_long_long_long_long_long_long_long_long_long
                    .as_mut()
                    .another_call()
                    .get_extra_info();

             // ^^^ is this the right level of indentation to use for the `rhs` of the binary expression?
    }
}

If we need to make updates to binary expression formatting, that might be better to do in a follow up PR.

@calebcartwright
Copy link
Member

if there's a set of snippets where we'd like style to give a perspective then i'm happy to bring that before the team, and we do have a meeting later today.

Do the last two comments cover all the remaining questions or are there others?

@ytmimi
Copy link
Contributor

ytmimi commented Oct 23, 2024

@calebcartwright
Copy link
Member

We reviewed this (the formatting improvements provided by this PR) in the t-style meeting yesterday and overall were pleased with the results. There were some thoughts on potential future improvements, and one potential issue/bug observed with the way this proposed implementation formats one of the snippets in

because this input 👇

impl SomeType {
    fn method(&mut self) {
        self.array[array_index as usize]
            .as_mut()
            .expect("thing must exist")
            .extra_info =
                long_long_long_long_long_long_long_long_long_long_long_long_long_long_long
                    .as_mut()
                    .another_call()
                    .get_extra_info() + Some(ExtraInfo {
                        parent,
                        count: count as u16,
                        children: children.into_boxed_slice(),
                    });
    }
}

is getting formatted without indentation on the ExtraInfo body:

impl SomeType {
    fn method(&mut self) {
        self.array[array_index as usize]
            .as_mut()
            .expect("thing must exist")
            .extra_info = long_long_long_long_long_long_long_long_long_long_long_long_long_long_long
                .as_mut()
                .another_call()
                .get_extra_info() + Some(ExtraInfo {
                parent,
                count: count as u16,
                children: children.into_boxed_slice(),
            });
    }
}

that's something I'd like to see if we can get addressed ASAP so we can get this merged and into nightly

@calebcartwright
Copy link
Member

We've been discussing this item quite a bit more in the style team meetings, and reached a decision that we only want to move forward with the indent change around assignment operators (so for our purposes within rustfmt we'd only need to look at assignment and assignmentop expressions).

I had extended what was done here to also directly support binary expressions, and we found that the results were mixed in more complex binary expression trees and felt we'd need some more nuanced rules that better account for expression tree structure and/or accounted for operator precedence rules.

We are hitting crunch time for the edition items, so if we're a bit stalled here then I may grab some commits from this PR branch and/or add co-authors on commits on a PR of my own to ensure we get it through

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a-2024-edition Style Edition 2024 p-high
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants