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

Avoid re-parenthesizing call chains whose inner values are parenthesized #7373

Merged
merged 2 commits into from
Sep 14, 2023

Conversation

charliermarsh
Copy link
Member

@charliermarsh charliermarsh commented Sep 14, 2023

Summary

Given a statement like:

result = (
    f(111111111111111111111111111111111111111111111111111111111111111111111111111111111)
    + 1
)()

When we go to parenthesize the target of the assignment, we use maybe_parenthesize_expression with Parenthesize::IfBreaks. This then checks if the call on the right-hand side needs to be parenthesized, the implementation of which looks like:

impl NeedsParentheses for ExprCall {
    fn needs_parentheses(
        &self,
        _parent: AnyNodeRef,
        context: &PyFormatContext,
    ) -> OptionalParentheses {
        if CallChainLayout::from_expression(self.into(), context.source())
            == CallChainLayout::Fluent
        {
            OptionalParentheses::Multiline
        } else if context.comments().has_dangling(self) {
            OptionalParentheses::Always
        } else {
            self.func.needs_parentheses(self.into(), context)
        }
    }
}

Checking for self.func.needs_parentheses(self.into(), context) is problematic, since, as in the example above, self.func may already be parenthesized -- in which case, we don't want to parenthesize the entire expression. If we do, we end up with this non-ideal formatting:

result = (
    (
        f(
            111111111111111111111111111111111111111111111111111111111111111111111111111111111
        )
        + 1
    )()
)

This PR modifies the NeedsParentheses implementations for call chain expressions to return Never if the inner expression has its own parentheses, in which case, the formatting implementations for those expressions will preserve them anyway.

Closes #7370.

Test Plan

Zulip improves a bit, everything else is unchanged.

Before:

project similarity index total files changed files
cpython 0.76083 1789 1632
django 0.99981 2760 40
transformers 0.99944 2587 413
twine 1.00000 33 0
typeshed 0.99983 3496 18
warehouse 0.99834 648 20
zulip 0.99956 1437 23

After:

project similarity index total files changed files
cpython 0.76083 1789 1632
django 0.99981 2760 40
transformers 0.99944 2587 413
twine 1.00000 33 0
typeshed 0.99983 3496 18
warehouse 0.99834 648 20
zulip 0.99962 1437 22

@charliermarsh charliermarsh added the formatter Related to the formatter label Sep 14, 2023
Copy link
Member

@MichaReiser MichaReiser left a comment

Choose a reason for hiding this comment

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

🎸

@charliermarsh charliermarsh merged commit 11287f9 into main Sep 14, 2023
16 checks passed
@charliermarsh charliermarsh deleted the charlie/parens branch September 14, 2023 09:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
formatter Related to the formatter
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Formatter undocumented deviation: long expression wrapped in extra parens and indented
3 participants