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

Commit

Permalink
feat(rome_js_formatter): parenthesise TsTypeAssertionExpression
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaReiser committed Aug 12, 2022
1 parent 8c76c2a commit b9a84c7
Show file tree
Hide file tree
Showing 29 changed files with 1,002 additions and 375 deletions.
17 changes: 16 additions & 1 deletion crates/rome_js_formatter/src/js/auxiliary/new_target.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::prelude::*;

use crate::parentheses::NeedsParentheses;
use rome_formatter::write;
use rome_js_syntax::NewTarget;
use rome_js_syntax::NewTargetFields;
use rome_js_syntax::{JsSyntaxNode, NewTarget};

#[derive(Debug, Clone, Default)]
pub struct FormatNewTarget;
Expand All @@ -24,4 +25,18 @@ impl FormatNodeRule<NewTarget> for FormatNewTarget {
]
]
}

fn needs_parentheses(&self, item: &NewTarget) -> bool {
item.needs_parentheses()
}
}

impl NeedsParentheses for NewTarget {
fn needs_parentheses(&self) -> bool {
false
}

fn needs_parentheses_with_parent(&self, _parent: &JsSyntaxNode) -> bool {
false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use crate::prelude::*;
use rome_formatter::{format_args, write};

use crate::parentheses::{
is_binary_like_left_or_right, is_conditional_test, is_in_left_hand_side_position,
NeedsParentheses,
is_binary_like_left_or_right, is_conditional_test,
update_or_lower_expression_needs_parentheses, NeedsParentheses,
};
use crate::utils::{
resolve_expression, resolve_left_most_expression, JsAnyBinaryLikeLeftExpression,
Expand Down Expand Up @@ -178,7 +178,7 @@ impl NeedsParentheses for JsArrowFunctionExpression {

_ => {
is_conditional_test(self.syntax(), parent)
|| is_in_left_hand_side_position(self.syntax(), parent)
|| update_or_lower_expression_needs_parentheses(self.syntax(), parent)
|| is_binary_like_left_or_right(self.syntax(), parent)
}
}
Expand Down
13 changes: 10 additions & 3 deletions crates/rome_js_formatter/src/js/expressions/await_expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use crate::prelude::*;
use rome_formatter::write;

use crate::parentheses::{
is_binary_like_left_or_right, is_conditional_test, unary_expression_needs_parentheses,
NeedsParentheses,
is_binary_like_left_or_right, is_conditional_test, is_spread,
update_or_lower_expression_needs_parentheses, NeedsParentheses,
};

use rome_js_syntax::{JsAwaitExpression, JsSyntaxNode};
Expand Down Expand Up @@ -34,12 +34,19 @@ impl NeedsParentheses for JsAwaitExpression {
}

pub(super) fn await_or_yield_needs_parens(parent: &JsSyntaxNode, node: &JsSyntaxNode) -> bool {
debug_assert!(matches!(
node.kind(),
JsSyntaxKind::JS_AWAIT_EXPRESSION | JsSyntaxKind::JS_YIELD_EXPRESSION
));

match parent.kind() {
JsSyntaxKind::JS_UNARY_EXPRESSION | JsSyntaxKind::TS_AS_EXPRESSION => true,

_ => {
let expression = node;
is_conditional_test(node, parent)
|| unary_expression_needs_parentheses(node, parent)
|| update_or_lower_expression_needs_parentheses(expression, parent)
|| is_spread(expression, parent)
|| is_binary_like_left_or_right(node, parent)
}
}
Expand Down
69 changes: 67 additions & 2 deletions crates/rome_js_formatter/src/js/expressions/binary_expression.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use crate::prelude::*;
use crate::utils::{format_binary_like_expression, JsAnyBinaryLikeExpression};
use crate::utils::{
format_binary_like_expression, needs_binary_like_parentheses, JsAnyBinaryLikeExpression,
};

use rome_js_syntax::JsBinaryExpression;
use crate::parentheses::NeedsParentheses;
use rome_js_syntax::{JsBinaryExpression, JsSyntaxNode};

#[derive(Debug, Clone, Default)]
pub struct FormatJsBinaryExpression;
Expand All @@ -18,3 +21,65 @@ impl FormatNodeRule<JsBinaryExpression> for FormatJsBinaryExpression {
)
}
}

impl NeedsParentheses for JsBinaryExpression {
fn needs_parentheses_with_parent(&self, parent: &JsSyntaxNode) -> bool {
needs_binary_like_parentheses(&JsAnyBinaryLikeExpression::from(self.clone()), parent)
}
}

#[cfg(test)]
mod tests {
use crate::{assert_needs_parentheses, assert_not_needs_parentheses};
use rome_js_syntax::{JsBinaryExpression, SourceType};

#[test]
fn needs_parentheses() {
assert_needs_parentheses!("class X extends (4 + 4) {}", JsBinaryExpression);

assert_needs_parentheses!("(4 + 4) as number", JsBinaryExpression);
assert_needs_parentheses!("<number>(4 + 4)", JsBinaryExpression);
assert_needs_parentheses!("!(4 + 4)", JsBinaryExpression);
assert_needs_parentheses!("await (4 + 4)", JsBinaryExpression);
assert_needs_parentheses!("(4 + 4)!", JsBinaryExpression);

assert_needs_parentheses!("(4 + 4)()", JsBinaryExpression);
assert_needs_parentheses!("(4 + 4)?.()", JsBinaryExpression);
assert_needs_parentheses!("new (4 + 4)()", JsBinaryExpression);
assert_needs_parentheses!("(4 + 4)`template`", JsBinaryExpression);
assert_needs_parentheses!("[...(4 + 4)]", JsBinaryExpression);
assert_needs_parentheses!("({...(4 + 4)})", JsBinaryExpression);
assert_needs_parentheses!(
"<test {...(4 + 4)} />",
JsBinaryExpression,
SourceType::tsx()
);
assert_needs_parentheses!(
"<test>{...(4 + 4)}</test>",
JsBinaryExpression,
SourceType::tsx()
);

assert_needs_parentheses!("(4 + 4).member", JsBinaryExpression);
assert_needs_parentheses!("(4 + 4)[member]", JsBinaryExpression);
assert_not_needs_parentheses!("object[4 + 4]", JsBinaryExpression);

assert_needs_parentheses!("(4 + 4) * 3", JsBinaryExpression[1]);
assert_not_needs_parentheses!("(4 + 4) * 3", JsBinaryExpression[0]);

assert_needs_parentheses!("a ** b ** c", JsBinaryExpression[1]);
assert_not_needs_parentheses!("a ** b ** c", JsBinaryExpression[0]);

assert_needs_parentheses!("a * r >> 5", JsBinaryExpression[1]);
assert_not_needs_parentheses!("a * r >> 5", JsBinaryExpression[0]);

assert_needs_parentheses!("a * r | 4", JsBinaryExpression[1]);
assert_not_needs_parentheses!("a * r | 5", JsBinaryExpression[0]);

assert_needs_parentheses!("a % 4 + 4", JsBinaryExpression[1]);
assert_not_needs_parentheses!("a % 4 + 4", JsBinaryExpression[0]);

assert_needs_parentheses!("a == b == c", JsBinaryExpression[1]);
assert_not_needs_parentheses!("a == b == c", JsBinaryExpression[0]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,7 @@ impl FormatNodeRule<JsCallExpression> for FormatJsCallExpression {

impl NeedsParentheses for JsCallExpression {
fn needs_parentheses_with_parent(&self, parent: &JsSyntaxNode) -> bool {
match parent.kind() {
JsSyntaxKind::JS_NEW_EXPRESSION => true,

_ => false,
}
matches!(parent.kind(), JsSyntaxKind::JS_NEW_EXPRESSION)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use crate::prelude::*;
use crate::utils::JsAnyConditional;

use crate::parentheses::{
is_binary_like_left_or_right, is_conditional_test, is_in_left_hand_side_position, is_spread,
NeedsParentheses,
is_binary_like_left_or_right, is_conditional_test, is_spread,
update_or_lower_expression_needs_parentheses, NeedsParentheses,
};
use rome_js_syntax::{JsConditionalExpression, JsSyntaxKind, JsSyntaxNode};

Expand Down Expand Up @@ -34,7 +34,7 @@ impl NeedsParentheses for JsConditionalExpression {

_ => {
is_conditional_test(self.syntax(), parent)
|| is_in_left_hand_side_position(self.syntax(), parent)
|| update_or_lower_expression_needs_parentheses(self.syntax(), parent)
|| is_binary_like_left_or_right(self.syntax(), parent)
|| is_spread(self.syntax(), parent)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::prelude::*;

use crate::parentheses::NeedsParentheses;
use rome_formatter::write;
use rome_js_syntax::JsImportCallExpression;
use rome_js_syntax::JsImportCallExpressionFields;
use rome_js_syntax::{JsImportCallExpression, JsSyntaxKind, JsSyntaxNode};

#[derive(Debug, Clone, Default)]
pub struct FormatJsImportCallExpression;
Expand All @@ -16,4 +17,14 @@ impl FormatNodeRule<JsImportCallExpression> for FormatJsImportCallExpression {

write![f, [import_token.format(), arguments.format(),]]
}

fn needs_parentheses(&self, item: &JsImportCallExpression) -> bool {
item.needs_parentheses()
}
}

impl NeedsParentheses for JsImportCallExpression {
fn needs_parentheses_with_parent(&self, parent: &JsSyntaxNode) -> bool {
matches!(parent.kind(), JsSyntaxKind::JS_NEW_EXPRESSION)
}
}
115 changes: 113 additions & 2 deletions crates/rome_js_formatter/src/js/expressions/in_expression.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
use crate::prelude::*;
use crate::utils::{format_binary_like_expression, JsAnyBinaryLikeExpression};
use crate::utils::{
format_binary_like_expression, needs_binary_like_parentheses, JsAnyBinaryLikeExpression,
};

use rome_js_syntax::JsInExpression;
use crate::parentheses::NeedsParentheses;

use rome_js_syntax::{JsAnyStatement, JsForStatement, JsInExpression, JsSyntaxNode};
use rome_rowan::AstNode;

#[derive(Debug, Clone, Default)]
pub struct FormatJsInExpression;
Expand All @@ -14,3 +19,109 @@ impl FormatNodeRule<JsInExpression> for FormatJsInExpression {
)
}
}

impl NeedsParentheses for JsInExpression {
fn needs_parentheses_with_parent(&self, parent: &JsSyntaxNode) -> bool {
if is_in_for_initializer(self) {
return true;
}

needs_binary_like_parentheses(&JsAnyBinaryLikeExpression::from(self.clone()), parent)
}
}

/// Add parentheses if the `in` is inside of a `for` initializer (see tests).
fn is_in_for_initializer(expression: &JsInExpression) -> bool {
let mut current = expression.clone().into_syntax();

while let Some(parent) = current.parent() {
current = match JsForStatement::try_cast(parent) {
Ok(for_statement) => {
return for_statement
.initializer()
.map(AstNode::into_syntax)
.as_ref()
== Some(&current);
}
Err(parent) => {
if JsAnyStatement::can_cast(parent.kind()) {
// Don't cross statement boundaries
break;
}

parent
}
}
}

false
}

#[cfg(test)]
mod tests {
use crate::{assert_needs_parentheses, assert_not_needs_parentheses};
use rome_js_syntax::{JsInExpression, SourceType};

#[test]
fn needs_parentheses() {
assert_needs_parentheses!("class X extends (a in b) {}", JsInExpression);

assert_needs_parentheses!("(a in b) as number", JsInExpression);
assert_needs_parentheses!("<number>(a in b)", JsInExpression);
assert_needs_parentheses!("!(a in b)", JsInExpression);
assert_needs_parentheses!("await (a in b)", JsInExpression);
assert_needs_parentheses!("(a in b)!", JsInExpression);

assert_needs_parentheses!("(a in b)()", JsInExpression);
assert_needs_parentheses!("(a in b)?.()", JsInExpression);
assert_needs_parentheses!("new (a in b)()", JsInExpression);
assert_needs_parentheses!("(a in b)`template`", JsInExpression);
assert_needs_parentheses!("[...(a in b)]", JsInExpression);
assert_needs_parentheses!("({...(a in b)})", JsInExpression);
assert_needs_parentheses!("<test {...(a in b)} />", JsInExpression, SourceType::tsx());
assert_needs_parentheses!(
"<test>{...(a in b)}</test>",
JsInExpression,
SourceType::tsx()
);

assert_needs_parentheses!("(a in b).member", JsInExpression);
assert_needs_parentheses!("(a in b)[member]", JsInExpression);
assert_not_needs_parentheses!("object[a in b]", JsInExpression);

assert_needs_parentheses!("(a in b) + c", JsInExpression);

assert_not_needs_parentheses!("a in b > c", JsInExpression);
assert_not_needs_parentheses!("a in b instanceof C", JsInExpression);
assert_not_needs_parentheses!("a in b in c", JsInExpression[0]);
assert_not_needs_parentheses!("a in b in c", JsInExpression[1]);
}

#[test]
fn for_in_needs_parentheses() {
assert_needs_parentheses!("for (let a = (b in c);;);", JsInExpression);
assert_needs_parentheses!("for (a && (b in c);;);", JsInExpression);
assert_needs_parentheses!("for (a => (b in c);;);", JsInExpression);
assert_needs_parentheses!(
"function* g() {
for (yield (a in b);;);
}",
JsInExpression
);
assert_needs_parentheses!(
"async function f() {
for (await (a in b);;);
}",
JsInExpression
);

assert_not_needs_parentheses!("for (;a in b;);", JsInExpression);
assert_not_needs_parentheses!("for (;;a in b);", JsInExpression);
assert_not_needs_parentheses!(
r#"
for (function () { a in b }();;);
"#,
JsInExpression
);
}
}
Loading

0 comments on commit b9a84c7

Please sign in to comment.