Skip to content
This repository has been archived by the owner on Aug 31, 2023. It is now read-only.

fix(rome_js_formatter): use parenthesis #2538

Closed
wants to merge 5 commits into from

Conversation

ematipico
Copy link
Contributor

Summary

This is part of #2449

It's a long list so it won't be fixed in one single PR. Given this file: https://github.com/prettier/prettier/blob/a9de2a128cc8eea84ddd90efdc210378a894ab6b/src/language-js/needs-parens.js

One thing to note about the above file, is that it contains logic of syntax that is not official yet. Also, it contains some weird cases that are syntactically wrong, probably because they needed to cover some new syntax case. Given that, the progression on this task takes longer then expected

I fixed cases for:

  • classes
  • parenthesized expressions (removal of unnecessary parenthesis)
  • await expressions
  • some edge case with sequence expressions

I also did some work for the conditional expressions, although it can't be completed because at the moment we don't correctly format them.

Once we do, can add parenthesis to this case

Test Plan

I progressively added new test cases every time I covered a new case.

@ematipico ematipico temporarily deployed to aws May 5, 2022 11:47 Inactive
@github-actions
Copy link

github-actions bot commented May 5, 2022

Parser conformance results on ubuntu-latest

js/262

Test result main count This PR count Difference
Total 45878 45878 0
Passed 44938 44938 0
Failed 940 940 0
Panics 0 0 0
Coverage 97.95% 97.95% 0.00%

jsx/babel

Test result main count This PR count Difference
Total 39 39 0
Passed 36 36 0
Failed 3 3 0
Panics 0 0 0
Coverage 92.31% 92.31% 0.00%

ts/babel

Test result main count This PR count Difference
Total 588 588 0
Passed 519 519 0
Failed 69 69 0
Panics 0 0 0
Coverage 88.27% 88.27% 0.00%

ts/microsoft

Test result main count This PR count Difference
Total 16257 16257 0
Passed 12391 12391 0
Failed 3866 3866 0
Panics 0 0 0
Coverage 76.22% 76.22% 0.00%

@github-actions
Copy link

github-actions bot commented May 5, 2022

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented May 5, 2022

Deploying with  Cloudflare Pages  Cloudflare Pages

Latest commit: 7c5cdf2
Status: ✅  Deploy successful!
Preview URL: https://58c3fb5f.tools-8rn.pages.dev

View logs

@ematipico ematipico temporarily deployed to aws May 5, 2022 12:31 Inactive
@ematipico ematipico temporarily deployed to aws May 5, 2022 14:52 Inactive
@ematipico ematipico requested a review from MichaReiser May 5, 2022 14:52
Comment on lines +585 to +593
let parent_kind = node.parent().map(|p| p.kind());
if matches!(
parent_kind,
Some(JsSyntaxKind::JS_FOR_STATEMENT | JsSyntaxKind::JS_RETURN_STATEMENT)
) {
FormatPrecedence::Low
} else {
FormatPrecedence::High
}
Copy link
Contributor Author

@ematipico ematipico May 5, 2022

Choose a reason for hiding this comment

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

This piece of logic covers:

for ((i = 0), (len = arr.length); i < len; i++) {
//     ^^^^^^^^^^^^^^^^^^
  console.log(arr[i])
}

Comment on lines +16 to +30
let super_class = super_class?;
let needs_parens = matches!(
super_class.syntax().kind(),
JsSyntaxKind::JS_NEW_EXPRESSION
| JsSyntaxKind::JS_YIELD_EXPRESSION
| JsSyntaxKind::JS_OBJECT_EXPRESSION
| JsSyntaxKind::TS_NON_NULL_ASSERTION_EXPRESSION
);
let super_class = super_class.format_with(formatter, |super_class| {
if needs_parens {
format_elements![token("("), super_class, token(")")]
} else {
super_class
}
})?;
Copy link
Contributor

Choose a reason for hiding this comment

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

Adding parentheses to a sub-expression seems to be a common pattern. Could we introduce a format_sub_expression(expression: JsAnyExpression, formatter; &Formatter) -> FormatResult<FormatElement> method instead that internally uses a match similar to prettier's to determine if parentheses for this expression are needed.

This would reduce the boilerplate code that now had to be added to every place where we format a sub expression.

Copy link
Contributor Author

@ematipico ematipico May 6, 2022

Choose a reason for hiding this comment

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

I did try this approach at first, but it didn't work for some particular cases.

  • prettier's approach orbits around their architecture of formatting things, which is different from ours; by default their approach is elide parenthesis as much as they can, and then they use that monster switch to refrain the algorithm from doing so; (I didn't study it in detail, but I saw some issue where prettier was removing parenthesis where it should not, and they added some cases there... we don't have this problem here)
  • their AST is different from ours, and sometimes it didn't work inside a big match; I found myself moving code from the generic function and put it inside the specific node;
  • adding parenthesis requires great context, which would lead to the same issue I introduced in the sequence expressions, where I added a context but it wouldn't work for formatting range;

@ematipico ematipico requested a review from MichaReiser May 6, 2022 12:09
Copy link
Contributor

@NicholasLYang NicholasLYang left a comment

Choose a reason for hiding this comment

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

Unrealistic situation, but any reason this doesn't match prettier?

@@ -27,10 +29,18 @@ impl FormatNode for JsUnaryExpression {
);

if is_keyword_operator {
let needs_parenthesis = JsAwaitExpression::can_cast(argument.syntax().kind());
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: we can do a match on argument directly:

Suggested change
let needs_parenthesis = JsAwaitExpression::can_cast(argument.syntax().kind());
let needs_parenthesis = matches!(argument, JsAnyExpression::JsAwaitExpression(_));

_ => false,
};

dbg!(&is_ambiguous_expression);
Copy link
Contributor

Choose a reason for hiding this comment

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

Any reason to keep this?

@MichaReiser
Copy link
Contributor

I think we need a more general approach to handling parentheses rather than doing it on a per-node level as discussed on discord. Do you want to explore if it would be feasible to handle parentheses inside the FormatNodeRule?

@ematipico
Copy link
Contributor Author

ematipico commented May 27, 2022

I think we need a more general approach to handling parentheses rather than doing it on a per-node level as discussed on discord. Do you want to explore if it would be feasible to handle parentheses inside the FormatNodeRule?

If you feel strong about this, then I am happy to delegate the implementation to someone else. As I explained in #2538 (comment), I already explored this approach and it didn't work as intended.

@MichaReiser
Copy link
Contributor

I think we need a more general approach to handling parentheses rather than doing it on a per-node level as discussed on discord. Do you want to explore if it would be feasible to handle parentheses inside the FormatNodeRule?

If you feel strong about this, then I am happy to delegate the implementation to someone else. As I explained in #2538 (comment), I already explored this approach and it didn't work as intended.

What I'm looking for in an implementation is that it is possible to keep the removal of parentheses in the parenthesized expression formatting rule and the addition of parentheses in the case where they are needed in sync.

@ematipico ematipico closed this Jul 16, 2022
@sebmck sebmck deleted the feature/needs-parenthesis branch November 4, 2022 20:37
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants