diff --git a/crates/rome_js_formatter/src/js/expressions/call_arguments.rs b/crates/rome_js_formatter/src/js/expressions/call_arguments.rs index f64b053bab0..aa6a4becd42 100644 --- a/crates/rome_js_formatter/src/js/expressions/call_arguments.rs +++ b/crates/rome_js_formatter/src/js/expressions/call_arguments.rs @@ -1,6 +1,7 @@ use crate::builders::{format_close_delimiter, format_open_delimiter}; +use crate::parentheses::resolve_expression_parent; use crate::prelude::*; -use crate::utils::{is_call_like_expression, write_arguments_multi_line}; +use crate::utils::{is_call_like_expression, resolve_expression, write_arguments_multi_line}; use rome_formatter::{format_args, write}; use rome_js_syntax::{ JsAnyCallArgument, JsAnyExpression, JsAnyFunctionBody, JsAnyLiteralExpression, JsAnyName, @@ -44,7 +45,7 @@ impl FormatNodeRule for FormatJsCallArguments { let second_argument = second_argument?; let is_framework_test_call = if let Some(call_expression) = - node.syntax().parent().and_then(JsCallExpression::cast) + resolve_expression_parent(node.syntax()).and_then(JsCallExpression::cast) { let callee = call_expression.callee()?; @@ -260,30 +261,31 @@ fn should_group_first_argument(list: &JsCallArgumentList) -> SyntaxResult let has_comments = first.syntax().has_comments_direct(); - let is_function_like = if let JsAnyCallArgument::JsAnyExpression(expression) = first { - match expression { - JsAnyExpression::JsFunctionExpression(_) => true, - JsAnyExpression::JsArrowFunctionExpression(arrow) => { - matches!(arrow.body()?, JsAnyFunctionBody::JsFunctionBody(_)) - } - _ => false, + let is_function_like = match resolve_call_argument_expression(&first) { + Some(JsAnyExpression::JsFunctionExpression(_)) => true, + Some(JsAnyExpression::JsArrowFunctionExpression(arrow)) => { + matches!(arrow.body()?, JsAnyFunctionBody::JsFunctionBody(_)) } - } else { - false + _ => false, }; let second_arg_is_function_like = matches!( - second, - JsAnyCallArgument::JsAnyExpression( + resolve_call_argument_expression(&second), + Some( JsAnyExpression::JsFunctionExpression(_) | JsAnyExpression::JsArrowFunctionExpression(_) | JsAnyExpression::JsConditionalExpression(_) ) ); - Ok(!has_comments - && is_function_like - && !second_arg_is_function_like - && !could_group_argument(&second, false)?) + + let can_group = match &second { + JsAnyCallArgument::JsAnyExpression(expression) => { + could_group_expression_argument(expression, false)? + } + _ => false, + }; + + Ok(!has_comments && is_function_like && !second_arg_is_function_like && !can_group) } /// Checks if the last group requires grouping @@ -310,63 +312,64 @@ fn should_group_last_argument(list: &JsCallArgumentList) -> SyntaxResult { true }; - Ok(!last.syntax().has_comments_direct() - && could_group_argument(&last, false)? - && check_with_penultimate) + let can_group = match &last { + JsAnyCallArgument::JsAnyExpression(expression) => { + could_group_expression_argument(expression, false)? + } + _ => false, + }; + + Ok(!last.syntax().has_comments_direct() && can_group && check_with_penultimate) } else { Ok(false) } } /// Checks if the current argument could be grouped -fn could_group_argument( - argument: &JsAnyCallArgument, +fn could_group_expression_argument( + argument: &JsAnyExpression, is_arrow_recursion: bool, ) -> SyntaxResult { - let result = if let JsAnyCallArgument::JsAnyExpression(argument) = argument { - match argument { - JsAnyExpression::JsObjectExpression(object_expression) => { - object_expression.members().len() > 0 - || object_expression - .syntax() - .first_or_last_token_have_comments() - } + let result = match resolve_expression(argument.clone()) { + JsAnyExpression::JsObjectExpression(object_expression) => { + object_expression.members().len() > 0 + || object_expression + .syntax() + .first_or_last_token_have_comments() + } - JsAnyExpression::JsArrayExpression(array_expression) => { - array_expression.elements().len() > 0 - || array_expression - .syntax() - .first_or_last_token_have_comments() - } - JsAnyExpression::TsTypeAssertionExpression(assertion_expression) => { - could_group_argument( - &JsAnyCallArgument::JsAnyExpression(assertion_expression.expression()?), - false, - )? - } + JsAnyExpression::JsArrayExpression(array_expression) => { + array_expression.elements().len() > 0 + || array_expression + .syntax() + .first_or_last_token_have_comments() + } + JsAnyExpression::TsTypeAssertionExpression(assertion_expression) => { + could_group_expression_argument(&assertion_expression.expression()?, false)? + } - JsAnyExpression::TsAsExpression(as_expression) => could_group_argument( - &JsAnyCallArgument::JsAnyExpression(as_expression.expression()?), - false, - )?, - JsAnyExpression::JsArrowFunctionExpression(arrow_function) => { - let body = arrow_function.body()?; - let return_type_annotation = arrow_function.return_type_annotation(); - - // Handles cases like: - // - // app.get("/", (req, res): void => { - // res.send("Hello World!"); - // }); - // - // export class Thing implements OtherThing { - // do: (type: Type) => Provider = memoize( - // (type: ObjectType): Provider => {} - // ); - // } - let can_group_type = !return_type_annotation.and_then(|rty| rty.ty().ok()).map_or( - false, - |any_type| { + JsAnyExpression::TsAsExpression(as_expression) => { + could_group_expression_argument(&as_expression.expression()?, false)? + } + JsAnyExpression::JsArrowFunctionExpression(arrow_function) => { + let body = arrow_function.body()?; + let return_type_annotation = arrow_function.return_type_annotation(); + + // Handles cases like: + // + // app.get("/", (req, res): void => { + // res.send("Hello World!"); + // }); + // + // export class Thing implements OtherThing { + // do: (type: Type) => Provider = memoize( + // (type: ObjectType): Provider => {} + // ); + // } + let can_group_type = + !return_type_annotation + .and_then(|rty| rty.ty().ok()) + .map_or(false, |any_type| { TsReferenceType::can_cast(any_type.syntax().kind()) || if let JsAnyFunctionBody::JsFunctionBody(function_body) = &body { function_body @@ -376,61 +379,48 @@ fn could_group_argument( } else { true } - }, - ); - - let body_is_delimited = matches!( - body, - JsAnyFunctionBody::JsFunctionBody(_) - | JsAnyFunctionBody::JsAnyExpression(JsAnyExpression::JsObjectExpression( - _ - )) - | JsAnyFunctionBody::JsAnyExpression(JsAnyExpression::JsArrayExpression(_)) - ); - - if let JsAnyFunctionBody::JsAnyExpression(any_expression) = body.clone() { - let is_nested_arrow_function = - if let JsAnyExpression::JsArrowFunctionExpression( - arrow_function_expression, - ) = &any_expression - { - arrow_function_expression - .body() - .ok() - .and_then(|body| body.as_js_any_expression().cloned()) - .and_then(|body| { - could_group_argument( - &JsAnyCallArgument::JsAnyExpression(body), - true, - ) - .ok() - }) - .unwrap_or(false) - } else { - false - }; - - body_is_delimited - && is_nested_arrow_function - && can_group_type - && (!is_arrow_recursion - && (is_call_like_expression(&any_expression) - || matches!( - body, - JsAnyFunctionBody::JsAnyExpression( - JsAnyExpression::JsConditionalExpression(_) - ) - ))) - } else { - body_is_delimited && can_group_type - } - } + }); - JsAnyExpression::JsFunctionExpression(_) => true, - _ => false, + let body_is_delimited = matches!( + body, + JsAnyFunctionBody::JsFunctionBody(_) + | JsAnyFunctionBody::JsAnyExpression(JsAnyExpression::JsObjectExpression(_)) + | JsAnyFunctionBody::JsAnyExpression(JsAnyExpression::JsArrayExpression(_)) + ); + + if let JsAnyFunctionBody::JsAnyExpression(any_expression) = body.clone() { + let is_nested_arrow_function = + if let JsAnyExpression::JsArrowFunctionExpression(arrow_function_expression) = + &any_expression + { + arrow_function_expression + .body() + .ok() + .and_then(|body| body.as_js_any_expression().cloned()) + .and_then(|body| could_group_expression_argument(&body, true).ok()) + .unwrap_or(false) + } else { + false + }; + + body_is_delimited + && is_nested_arrow_function + && can_group_type + && (!is_arrow_recursion + && (is_call_like_expression(&any_expression) + || matches!( + body, + JsAnyFunctionBody::JsAnyExpression( + JsAnyExpression::JsConditionalExpression(_) + ) + ))) + } else { + body_is_delimited && can_group_type + } } - } else { - false + + JsAnyExpression::JsFunctionExpression(_) => true, + _ => false, }; Ok(result) @@ -445,9 +435,16 @@ fn is_react_hook_with_deps_array( first_argument: &JsAnyCallArgument, second_argument: &JsAnyCallArgument, ) -> SyntaxResult { - let first_node_matches = if let JsAnyCallArgument::JsAnyExpression( - JsAnyExpression::JsArrowFunctionExpression(arrow_function), - ) = first_argument + let first_expression = match first_argument { + JsAnyCallArgument::JsAnyExpression(expression) => { + Some(resolve_expression(expression.clone())) + } + _ => None, + }; + + let first_node_matches = if let Some(JsAnyExpression::JsArrowFunctionExpression( + arrow_function, + )) = first_expression { let no_parameters = arrow_function.parameters()?.is_empty(); let body = arrow_function.body()?; @@ -528,9 +525,19 @@ fn is_framework_test_call(payload: IsTestFrameworkCallPayload) -> SyntaxResult resolve_call_argument_expression(&argument), + _ => None, + }); + let first_argument_is_literal_like = matches!( - first_argument, - JsAnyCallArgument::JsAnyExpression( + first_argument_expression, + Some( JsAnyExpression::JsAnyLiteralExpression( JsAnyLiteralExpression::JsStringLiteralExpression(_) ) | JsAnyExpression::JsTemplate(_) @@ -538,35 +545,31 @@ fn is_framework_test_call(payload: IsTestFrameworkCallPayload) -> SyntaxResult { ... }, 2500)` - if let Some(Ok(third_argument)) = third_argument { - if !matches!( - third_argument, - JsAnyCallArgument::JsAnyExpression(JsAnyExpression::JsAnyLiteralExpression( - JsAnyLiteralExpression::JsNumberLiteralExpression(_) - )) - ) { - return Ok(false); - } - } - if arguments_len == 2 { Ok(matches!( - second_argument, - JsAnyCallArgument::JsAnyExpression( + second_argument_expression, + Some( JsAnyExpression::JsArrowFunctionExpression(_) | JsAnyExpression::JsFunctionExpression(_) ) )) } else { - let result = match second_argument { - JsAnyCallArgument::JsAnyExpression(JsAnyExpression::JsFunctionExpression(node)) => { + // if the third argument is not a numeric literal, we bail + // example: `it("name", () => { ... }, 2500)` + if !matches!( + third_argument_expression, + Some(JsAnyExpression::JsAnyLiteralExpression( + JsAnyLiteralExpression::JsNumberLiteralExpression(_) + )) + ) { + return Ok(false); + } + + let result = match second_argument_expression { + Some(JsAnyExpression::JsFunctionExpression(node)) => { node.parameters()?.items().len() <= 1 } - JsAnyCallArgument::JsAnyExpression(JsAnyExpression::JsArrowFunctionExpression( - node, - )) => { + Some(JsAnyExpression::JsArrowFunctionExpression(node)) => { let body = node.body()?; let has_enough_parameters = node.parameters()?.len() <= 1; matches!(body, JsAnyFunctionBody::JsFunctionBody(_)) && has_enough_parameters @@ -580,6 +583,17 @@ fn is_framework_test_call(payload: IsTestFrameworkCallPayload) -> SyntaxResult Option { + match argument { + JsAnyCallArgument::JsAnyExpression(expression) => { + Some(resolve_expression(expression.clone())) + } + _ => None, + } +} + /// This function checks if a call expressions has one of the following members: /// - `it` /// - `it.only` @@ -665,24 +679,30 @@ fn matches_test_call(callee: &JsAnyExpression) -> SyntaxResult { - let value = name.value_token()?; - test_call.push(value.token_text_trimmed()); - current_node = member_expression.object()?; + current_node = match current_node { + JsAnyExpression::JsIdentifierExpression(identifier) => { + let value_token = identifier.name()?.value_token()?; + let value = value_token.token_text_trimmed(); + test_call.push(value); + break; + } + JsAnyExpression::JsStaticMemberExpression(member_expression) => { + match member_expression.member()? { + JsAnyName::JsName(name) => { + let value = name.value_token()?; + test_call.push(value.token_text_trimmed()); + member_expression.object()? + } + _ => break, } - _ => break, - }; - } else { - break; - } + } + JsAnyExpression::JsParenthesizedExpression(parenthesized) => { + parenthesized.expression()? + } + _ => break, + }; } test_call.reverse(); Ok(test_call) diff --git a/crates/rome_js_formatter/src/js/expressions/computed_member_expression.rs b/crates/rome_js_formatter/src/js/expressions/computed_member_expression.rs index c8cf5acb2e7..d28c53441c7 100644 --- a/crates/rome_js_formatter/src/js/expressions/computed_member_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/computed_member_expression.rs @@ -1,6 +1,6 @@ use crate::prelude::*; -use crate::js::expressions::static_member_expression::memberish_needs_parens; +use crate::js::expressions::static_member_expression::member_chain_callee_needs_parens; use crate::parentheses::NeedsParentheses; use rome_formatter::{format_args, write}; use rome_js_syntax::{JsComputedMemberExpression, JsSyntaxNode}; @@ -49,7 +49,7 @@ impl NeedsParentheses for JsComputedMemberExpression { return true; } - memberish_needs_parens(self.clone().into(), parent) + member_chain_callee_needs_parens(self.clone().into(), parent) } } diff --git a/crates/rome_js_formatter/src/js/expressions/conditional_expression.rs b/crates/rome_js_formatter/src/js/expressions/conditional_expression.rs index 6ae8fda6a87..fef5cbf04e3 100644 --- a/crates/rome_js_formatter/src/js/expressions/conditional_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/conditional_expression.rs @@ -2,7 +2,7 @@ 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_binary_like_left_or_right, is_conditional_test, is_in_left_hand_side_position, is_spread, NeedsParentheses, }; use rome_js_syntax::{JsConditionalExpression, JsSyntaxKind, JsSyntaxNode}; @@ -28,9 +28,7 @@ impl NeedsParentheses for JsConditionalExpression { fn needs_parentheses_with_parent(&self, parent: &JsSyntaxNode) -> bool { match parent.kind() { JsSyntaxKind::JS_UNARY_EXPRESSION - | JsSyntaxKind::JS_SPREAD | JsSyntaxKind::JS_AWAIT_EXPRESSION - | JsSyntaxKind::JSX_SPREAD_ATTRIBUTE | JsSyntaxKind::TS_TYPE_ASSERTION_EXPRESSION | JsSyntaxKind::TS_AS_EXPRESSION => true, @@ -38,6 +36,7 @@ impl NeedsParentheses for JsConditionalExpression { is_conditional_test(self.syntax(), parent) || is_in_left_hand_side_position(self.syntax(), parent) || is_binary_like_left_or_right(self.syntax(), parent) + || is_spread(self.syntax(), parent) } } } diff --git a/crates/rome_js_formatter/src/js/expressions/parenthesized_expression.rs b/crates/rome_js_formatter/src/js/expressions/parenthesized_expression.rs index b302280096f..e22370002a2 100644 --- a/crates/rome_js_formatter/src/js/expressions/parenthesized_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/parenthesized_expression.rs @@ -155,6 +155,7 @@ pub(crate) fn is_expression_handling_parens(expression: &JsAnyExpression) -> boo | JsComputedMemberExpression(_) | TsNonNullAssertionExpression(_) | JsxTagExpression(_) + | TsAsExpression(_) ) } } diff --git a/crates/rome_js_formatter/src/js/expressions/static_member_expression.rs b/crates/rome_js_formatter/src/js/expressions/static_member_expression.rs index 862962bc349..a75c890a304 100644 --- a/crates/rome_js_formatter/src/js/expressions/static_member_expression.rs +++ b/crates/rome_js_formatter/src/js/expressions/static_member_expression.rs @@ -112,21 +112,15 @@ impl NeedsParentheses for JsStaticMemberExpression { return true; } - memberish_needs_parens(self.clone().into(), parent) + member_chain_callee_needs_parens(self.clone().into(), parent) } } -pub(crate) fn memberish_needs_parens(node: JsAnyExpression, parent: &JsSyntaxNode) -> bool { +pub(crate) fn member_chain_callee_needs_parens( + node: JsAnyExpression, + parent: &JsSyntaxNode, +) -> bool { use JsAnyExpression::*; - debug_assert!( - matches!( - node, - JsStaticMemberExpression(_) - | JsComputedMemberExpression(_) - | TsNonNullAssertionExpression(_) - ), - "Expected node to be a member expression" - ); match parent.kind() { // `new (test().a) diff --git a/crates/rome_js_formatter/src/js/expressions/template.rs b/crates/rome_js_formatter/src/js/expressions/template.rs index c7be5b36a0b..ef0c57576e7 100644 --- a/crates/rome_js_formatter/src/js/expressions/template.rs +++ b/crates/rome_js_formatter/src/js/expressions/template.rs @@ -1,6 +1,7 @@ use crate::prelude::*; use rome_formatter::write; +use crate::js::expressions::static_member_expression::member_chain_callee_needs_parens; use crate::parentheses::NeedsParentheses; use rome_js_syntax::JsTemplateFields; use rome_js_syntax::{JsSyntaxNode, JsTemplate}; @@ -38,11 +39,11 @@ impl FormatNodeRule for FormatJsTemplate { /// `TemplateLiteral`'s are `PrimaryExpression's that never need parentheses. impl NeedsParentheses for JsTemplate { - fn needs_parentheses(&self) -> bool { - false - } - - fn needs_parentheses_with_parent(&self, _parent: &JsSyntaxNode) -> bool { - false + fn needs_parentheses_with_parent(&self, parent: &JsSyntaxNode) -> bool { + if self.tag().is_some() { + member_chain_callee_needs_parens(self.clone().into(), parent) + } else { + false + } } } diff --git a/crates/rome_js_formatter/src/lib.rs b/crates/rome_js_formatter/src/lib.rs index c0ba28cd24d..4d2a05548f8 100644 --- a/crates/rome_js_formatter/src/lib.rs +++ b/crates/rome_js_formatter/src/lib.rs @@ -718,17 +718,12 @@ mod test { // use this test check if your snippet prints as you wish, without using a snapshot fn quick_test() { let src = r#" -let bifornCringerMoshedPerplexSawder= -askTrovenaBeenaDependsRowans= -glimseGlyphsHazardNoopsTieTie= -averredBathersBoxroomBuggyNurl; - -let bifornCringerMoshedPerplexSawder = - (askTrovenaBeenaDependsRowans = glimseGlyphsHazardNoopsTieTie = - averredBathersBoxroomBuggyNurl); - - - +it(`handles + some + newlines + does something really long and complicated so I have to write a very long name for the test`, () => { + console.log("hello!"); +}, 2500); "#; let syntax = SourceType::tsx(); let tree = parse(src, 0, syntax); diff --git a/crates/rome_js_formatter/src/parentheses.rs b/crates/rome_js_formatter/src/parentheses.rs index 3f5cd3647bd..86310b533b1 100644 --- a/crates/rome_js_formatter/src/parentheses.rs +++ b/crates/rome_js_formatter/src/parentheses.rs @@ -299,19 +299,11 @@ pub(crate) fn is_in_left_hand_side_position( } } -pub(crate) fn is_in_assignment_expression_position( - parent: &JsSyntaxNode, - _expression: &JsSyntaxNode, -) -> bool { - matches!(parent.kind(), JsSyntaxKind::JS_SPREAD) -} - pub(crate) fn unary_expression_needs_parentheses( expression: &JsSyntaxNode, parent: &JsSyntaxNode, ) -> bool { - is_in_left_hand_side_position(expression, parent) - || is_in_assignment_expression_position(parent, expression) + is_in_left_hand_side_position(expression, parent) || is_spread(expression, parent) } /// Returns `true` if `node< is the `object` of a [JsStaticMemberExpression] or [JsComputedMemberExpression] @@ -368,6 +360,8 @@ pub(crate) fn is_conditional_test(node: &JsSyntaxNode, parent: &JsSyntaxNode) -> /// Returns `true` if `node` is the `tag` of a [JsTemplate] expression pub(crate) fn is_tag(node: &JsSyntaxNode, parent: &JsSyntaxNode) -> bool { + debug_assert_is_expression(node); + match parent.kind() { JsSyntaxKind::JS_TEMPLATE => { let template = JsTemplate::unwrap_cast(parent.clone()); @@ -378,6 +372,18 @@ pub(crate) fn is_tag(node: &JsSyntaxNode, parent: &JsSyntaxNode) -> bool { } } +/// Returns `true` if `node` is a spread `...node` +pub(crate) fn is_spread(node: &JsSyntaxNode, parent: &JsSyntaxNode) -> bool { + debug_assert_is_expression(node); + + matches!( + parent.kind(), + JsSyntaxKind::JSX_SPREAD_CHILD + | JsSyntaxKind::JS_SPREAD + | JsSyntaxKind::JSX_SPREAD_ATTRIBUTE + ) +} + pub(crate) fn is_binary_like_left_or_right(node: &JsSyntaxNode, parent: &JsSyntaxNode) -> bool { debug_assert_is_expression(node); diff --git a/crates/rome_js_formatter/src/ts/expressions/as_expression.rs b/crates/rome_js_formatter/src/ts/expressions/as_expression.rs index 55bd2596738..8ee5a7f3bea 100644 --- a/crates/rome_js_formatter/src/ts/expressions/as_expression.rs +++ b/crates/rome_js_formatter/src/ts/expressions/as_expression.rs @@ -1,8 +1,11 @@ use crate::prelude::*; +use crate::parentheses::{ + is_binary_like_left_or_right, is_callee, is_member_object, is_spread, is_tag, NeedsParentheses, +}; use rome_formatter::write; -use rome_js_syntax::TsAsExpression; use rome_js_syntax::TsAsExpressionFields; +use rome_js_syntax::{JsSyntaxKind, JsSyntaxNode, TsAsExpression}; #[derive(Debug, Clone, Default)] pub struct FormatTsAsExpression; @@ -26,4 +29,85 @@ impl FormatNodeRule for FormatTsAsExpression { ] ] } + + fn needs_parentheses(&self, item: &TsAsExpression) -> bool { + item.needs_parentheses() + } +} + +impl NeedsParentheses for TsAsExpression { + fn needs_parentheses_with_parent(&self, parent: &JsSyntaxNode) -> bool { + match parent.kind() { + JsSyntaxKind::JS_CONDITIONAL_EXPRESSION + | JsSyntaxKind::JS_EXTENDS_CLAUSE + | JsSyntaxKind::TS_TYPE_ASSERTION_EXPRESSION + | JsSyntaxKind::JS_UNARY_EXPRESSION + | JsSyntaxKind::JS_AWAIT_EXPRESSION + | JsSyntaxKind::TS_NON_NULL_ASSERTION_EXPRESSION => true, + + _ => { + is_callee(self.syntax(), parent) + || is_tag(self.syntax(), parent) + || is_spread(self.syntax(), parent) + || is_member_object(self.syntax(), parent) + || is_binary_like_left_or_right(self.syntax(), parent) + } + } + } +} + +#[cfg(test)] +mod tests { + + use crate::{assert_needs_parentheses, assert_not_needs_parentheses}; + use rome_js_syntax::{SourceType, TsAsExpression}; + + #[test] + fn needs_parentheses() { + assert_needs_parentheses!("5 as number ? true : false", TsAsExpression); + assert_needs_parentheses!("cond ? x as number : false", TsAsExpression); + assert_needs_parentheses!("cond ? true : x as number", TsAsExpression); + + assert_needs_parentheses!("class X extends (B as number) {}", TsAsExpression); + + assert_needs_parentheses!("(x as Function)()", TsAsExpression); + assert_needs_parentheses!("(x as Function)?.()", TsAsExpression); + assert_needs_parentheses!("new (x as Function)()", TsAsExpression); + + assert_needs_parentheses!("(x as any)", TsAsExpression); + assert_needs_parentheses!("(x as any)`template`", TsAsExpression); + assert_needs_parentheses!("!(x as any)", TsAsExpression); + assert_needs_parentheses!("[...(x as any)]", TsAsExpression); + assert_needs_parentheses!("({...(x as any)})", TsAsExpression); + assert_needs_parentheses!( + "", + TsAsExpression, + SourceType::tsx() + ); + assert_needs_parentheses!( + "{...(x as any)}", + TsAsExpression, + SourceType::tsx() + ); + assert_needs_parentheses!("await (x as any)", TsAsExpression); + assert_needs_parentheses!("(x as any)!", TsAsExpression); + + assert_needs_parentheses!("(x as any).member", TsAsExpression); + assert_needs_parentheses!("(x as any)[member]", TsAsExpression); + assert_not_needs_parentheses!("object[x as any]", TsAsExpression); + + assert_needs_parentheses!("(x as any) + (y as any)", TsAsExpression[0]); + assert_needs_parentheses!("(x as any) + (y as any)", TsAsExpression[1]); + + assert_needs_parentheses!("(x as any) && (y as any)", TsAsExpression[0]); + assert_needs_parentheses!("(x as any) && (y as any)", TsAsExpression[1]); + + assert_needs_parentheses!("(x as any) in (y as any)", TsAsExpression[0]); + assert_needs_parentheses!("(x as any) in (y as any)", TsAsExpression[1]); + + assert_needs_parentheses!("(x as any) instanceof (y as any)", TsAsExpression[0]); + assert_needs_parentheses!("(x as any) instanceof (y as any)", TsAsExpression[1]); + + assert_not_needs_parentheses!("x as number as string", TsAsExpression[1]); + } } diff --git a/crates/rome_js_formatter/src/ts/expressions/non_null_assertion_expression.rs b/crates/rome_js_formatter/src/ts/expressions/non_null_assertion_expression.rs index bd02f661ac6..7ada1b0bb9f 100644 --- a/crates/rome_js_formatter/src/ts/expressions/non_null_assertion_expression.rs +++ b/crates/rome_js_formatter/src/ts/expressions/non_null_assertion_expression.rs @@ -1,6 +1,6 @@ use crate::prelude::*; -use crate::js::expressions::static_member_expression::memberish_needs_parens; +use crate::js::expressions::static_member_expression::member_chain_callee_needs_parens; use crate::parentheses::NeedsParentheses; use rome_formatter::write; use rome_js_syntax::{JsSyntaxKind, TsNonNullAssertionExpressionFields}; @@ -31,6 +31,6 @@ impl FormatNodeRule for FormatTsNonNullAssertionEx impl NeedsParentheses for TsNonNullAssertionExpression { fn needs_parentheses_with_parent(&self, parent: &JsSyntaxNode) -> bool { matches!(parent.kind(), JsSyntaxKind::JS_EXTENDS_CLAUSE) - || memberish_needs_parens(self.clone().into(), parent) + || member_chain_callee_needs_parens(self.clone().into(), parent) } } diff --git a/crates/rome_js_formatter/src/ts/expressions/type_assertion_expression.rs b/crates/rome_js_formatter/src/ts/expressions/type_assertion_expression.rs index 6b613a64af1..e229596012a 100644 --- a/crates/rome_js_formatter/src/ts/expressions/type_assertion_expression.rs +++ b/crates/rome_js_formatter/src/ts/expressions/type_assertion_expression.rs @@ -1,8 +1,9 @@ use crate::prelude::*; +use crate::parentheses::NeedsParentheses; use rome_formatter::write; -use rome_js_syntax::TsTypeAssertionExpression; -use rome_js_syntax::TsTypeAssertionExpressionFields; +use rome_js_syntax::{JsSyntaxKind, TsAsExpression, TsTypeAssertionExpressionFields}; +use rome_js_syntax::{JsSyntaxNode, TsTypeAssertionExpression}; #[derive(Debug, Clone, Default)] pub struct FormatTsTypeAssertionExpression; diff --git a/crates/rome_js_formatter/src/utils/member_chain/groups.rs b/crates/rome_js_formatter/src/utils/member_chain/groups.rs index 33a32d59a6b..c86fc243fbb 100644 --- a/crates/rome_js_formatter/src/utils/member_chain/groups.rs +++ b/crates/rome_js_formatter/src/utils/member_chain/groups.rs @@ -2,11 +2,9 @@ use crate::context::TabWidth; use crate::parentheses::NeedsParentheses; use crate::prelude::*; use crate::utils::member_chain::chain_member::{ChainEntry, ChainMember}; -use crate::utils::member_chain::simple_argument::SimpleArgument; -use crate::utils::member_chain::MemberChain; use rome_formatter::write; use rome_js_syntax::JsCallExpression; -use rome_rowan::{AstSeparatedList, SyntaxResult}; +use rome_rowan::SyntaxResult; use std::mem; pub(super) struct MemberChainGroupsBuilder { diff --git a/crates/rome_js_formatter/tests/specs/prettier/typescript/argument-expansion/argument_expansion.ts.snap b/crates/rome_js_formatter/tests/specs/prettier/typescript/argument-expansion/argument_expansion.ts.snap deleted file mode 100644 index 74317f1e2a4..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/typescript/argument-expansion/argument_expansion.ts.snap +++ /dev/null @@ -1,137 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -const bar1 = [1,2,3].reduce((carry, value) => { - return [...carry, value]; -}, ([] as unknown) as number[]); - -const bar2 = [1,2,3].reduce((carry, value) => { - return [...carry, value]; -}, >[]); - -const bar3 = [1,2,3].reduce((carry, value) => { - return [...carry, value]; -}, ([1, 2, 3] as unknown) as number[]); - -const bar4 = [1,2,3].reduce((carry, value) => { - return [...carry, value]; -}, >[1, 2, 3]); - -const bar5 = [1,2,3].reduce((carry, value) => { - return {...carry, [value]: true}; -}, ({} as unknown) as {[key: number]: boolean}); - -const bar6 = [1,2,3].reduce((carry, value) => { - return {...carry, [value]: true}; -}, <{[key: number]: boolean}>{}); - -const bar7 = [1,2,3].reduce((carry, value) => { - return {...carry, [value]: true}; -}, ({1: true} as unknown) as {[key: number]: boolean}); - -const bar8 = [1,2,3].reduce((carry, value) => { - return {...carry, [value]: true}; -}, <{[key: number]: boolean}>{1: true}); -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -1,17 +1,14 @@ - const bar1 = [1, 2, 3].reduce((carry, value) => { - return [...carry, value]; --}, [] as unknown as number[]); -+}, ([] as unknown) as number[]); - - const bar2 = [1, 2, 3].reduce((carry, value) => { - return [...carry, value]; - }, >[]); - --const bar3 = [1, 2, 3].reduce( -- (carry, value) => { -- return [...carry, value]; -- }, -- [1, 2, 3] as unknown as number[], --); -+const bar3 = [1, 2, 3].reduce((carry, value) => { -+ return [...carry, value]; -+}, ([1, 2, 3] as unknown) as number[]); - - const bar4 = [1, 2, 3].reduce( - (carry, value) => { -@@ -22,18 +19,15 @@ - - const bar5 = [1, 2, 3].reduce((carry, value) => { - return { ...carry, [value]: true }; --}, {} as unknown as { [key: number]: boolean }); -+}, ({} as unknown) as { [key: number]: boolean }); - - const bar6 = [1, 2, 3].reduce((carry, value) => { - return { ...carry, [value]: true }; - }, <{ [key: number]: boolean }>{}); - --const bar7 = [1, 2, 3].reduce( -- (carry, value) => { -- return { ...carry, [value]: true }; -- }, -- { 1: true } as unknown as { [key: number]: boolean }, --); -+const bar7 = [1, 2, 3].reduce((carry, value) => { -+ return { ...carry, [value]: true }; -+}, ({ 1: true } as unknown) as { [key: number]: boolean }); - - const bar8 = [1, 2, 3].reduce( - (carry, value) => { -``` - -# Output - -```js -const bar1 = [1, 2, 3].reduce((carry, value) => { - return [...carry, value]; -}, ([] as unknown) as number[]); - -const bar2 = [1, 2, 3].reduce((carry, value) => { - return [...carry, value]; -}, >[]); - -const bar3 = [1, 2, 3].reduce((carry, value) => { - return [...carry, value]; -}, ([1, 2, 3] as unknown) as number[]); - -const bar4 = [1, 2, 3].reduce( - (carry, value) => { - return [...carry, value]; - }, - >[1, 2, 3], -); - -const bar5 = [1, 2, 3].reduce((carry, value) => { - return { ...carry, [value]: true }; -}, ({} as unknown) as { [key: number]: boolean }); - -const bar6 = [1, 2, 3].reduce((carry, value) => { - return { ...carry, [value]: true }; -}, <{ [key: number]: boolean }>{}); - -const bar7 = [1, 2, 3].reduce((carry, value) => { - return { ...carry, [value]: true }; -}, ({ 1: true } as unknown) as { [key: number]: boolean }); - -const bar8 = [1, 2, 3].reduce( - (carry, value) => { - return { ...carry, [value]: true }; - }, - <{ [key: number]: boolean }>{ 1: true }, -); -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/typescript/as/as.ts.snap b/crates/rome_js_formatter/tests/specs/prettier/typescript/as/as.ts.snap index 2f1489b636c..012c2ea0a99 100644 --- a/crates/rome_js_formatter/tests/specs/prettier/typescript/as/as.ts.snap +++ b/crates/rome_js_formatter/tests/specs/prettier/typescript/as/as.ts.snap @@ -1,8 +1,5 @@ --- source: crates/rome_js_formatter/tests/prettier_tests.rs -assertion_line: 271 -info: - test_file: typescript/as/as.ts --- # Input @@ -56,31 +53,8 @@ const iter2 = createIterator(self.controller, child, self.tag as SyncFunctionCom ```diff --- Prettier +++ Rome -@@ -3,29 +3,31 @@ - (originalError - ? wrappedError(errMsg, originalError) - : Error(errMsg)) as InjectionError; --"current" in (props.pagination as Object); -+"current" in props.pagination as Object; - ("current" in props.pagination) as Object; - start + (yearSelectTotal as number); - (start + yearSelectTotal) as number; - scrollTop > (visibilityHeight as number); - (scrollTop > visibilityHeight) as number; --export default class Column extends (RcTable.Column as React.ComponentClass< -- ColumnProps, -- ColumnProps, -- ColumnProps, -- ColumnProps -->) {} -+export default class Column extends ( -+ RcTable.Column as React.ComponentClass< -+ ColumnProps, -+ ColumnProps, -+ ColumnProps, -+ ColumnProps -+ > -+) {} +@@ -17,15 +17,15 @@ + >) {} export const MobxTypedForm = class extends (Form as { new (): any }) {}; export abstract class MobxTypedForm1 extends (Form as { new (): any }) {} -({} as {}); @@ -98,7 +72,7 @@ const iter2 = createIterator(self.controller, child, self.tag as SyncFunctionCom const state = JSON.stringify({ next: window.location.href, nonce, -@@ -41,9 +43,9 @@ +@@ -41,9 +41,9 @@ thisIsAReallyReallyReallyReallyReallyLongIdentifier as SomeInterface; const value2 = thisIsAnIdentifier as thisIsAReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyLongInterface; @@ -121,20 +95,18 @@ this.isTabActionBar((e.target || e.srcElement) as HTMLElement); (originalError ? wrappedError(errMsg, originalError) : Error(errMsg)) as InjectionError; -"current" in props.pagination as Object; +"current" in (props.pagination as Object); ("current" in props.pagination) as Object; start + (yearSelectTotal as number); (start + yearSelectTotal) as number; scrollTop > (visibilityHeight as number); (scrollTop > visibilityHeight) as number; -export default class Column extends ( - RcTable.Column as React.ComponentClass< - ColumnProps, - ColumnProps, - ColumnProps, - ColumnProps - > -) {} +export default class Column extends (RcTable.Column as React.ComponentClass< + ColumnProps, + ColumnProps, + ColumnProps, + ColumnProps +>) {} export const MobxTypedForm = class extends (Form as { new (): any }) {}; export abstract class MobxTypedForm1 extends (Form as { new (): any }) {} ({}) as {}; @@ -190,7 +162,7 @@ const iter2 = createIterator( # Lines exceeding max width of 80 characters ``` - 45: thisIsAnIdentifier as thisIsAReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyLongInterface; - 55: thisIsAReallyReallyReallyReallyReallyReallyReallyReallyReallyLongIdentifier as [ + 43: thisIsAnIdentifier as thisIsAReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyLongInterface; + 53: thisIsAReallyReallyReallyReallyReallyReallyReallyReallyReallyLongIdentifier as [ ``` diff --git a/crates/rome_js_formatter/tests/specs/prettier/typescript/as/assignment.ts.snap b/crates/rome_js_formatter/tests/specs/prettier/typescript/as/assignment.ts.snap deleted file mode 100644 index ab2e6a4d1bf..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/typescript/as/assignment.ts.snap +++ /dev/null @@ -1,115 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -export const LOG_LEVEL = { - EMERGENCY: 0, - ALERT: 1, - CRITICAL: 2, - ERROR: 3, - WARNING: 4, - NOTICE: 5, - INFO: 6, - DEBUG: 7, -} as const; - -const TYPE_MAP = { - 'character device': 'special', - 'character special file': 'special', - directory: 'directory', - 'regular file': 'file', - socket: 'socket', - 'symbolic link': 'link', -} as Foo; - -this.previewPlayerHandle = (setInterval(async () => { - if (this.previewIsPlaying) { - await this.fetchNextPreviews(); - this.currentPreviewIndex++; - } -}, this.refreshDelay) as unknown) as number; - -this.intervalID = (setInterval(() => { - self.step(); -}, 30) as unknown) as number; -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -18,13 +18,17 @@ - "symbolic link": "link", - } as Foo; - --this.previewPlayerHandle = setInterval(async () => { -- if (this.previewIsPlaying) { -- await this.fetchNextPreviews(); -- this.currentPreviewIndex++; -- } --}, this.refreshDelay) as unknown as number; -+this.previewPlayerHandle = ( -+ setInterval(async () => { -+ if (this.previewIsPlaying) { -+ await this.fetchNextPreviews(); -+ this.currentPreviewIndex++; -+ } -+ }, this.refreshDelay) as unknown -+) as number; - --this.intervalID = setInterval(() => { -- self.step(); --}, 30) as unknown as number; -+this.intervalID = ( -+ setInterval(() => { -+ self.step(); -+ }, 30) as unknown -+) as number; -``` - -# Output - -```js -export const LOG_LEVEL = { - EMERGENCY: 0, - ALERT: 1, - CRITICAL: 2, - ERROR: 3, - WARNING: 4, - NOTICE: 5, - INFO: 6, - DEBUG: 7, -} as const; - -const TYPE_MAP = { - "character device": "special", - "character special file": "special", - directory: "directory", - "regular file": "file", - socket: "socket", - "symbolic link": "link", -} as Foo; - -this.previewPlayerHandle = ( - setInterval(async () => { - if (this.previewIsPlaying) { - await this.fetchNextPreviews(); - this.currentPreviewIndex++; - } - }, this.refreshDelay) as unknown -) as number; - -this.intervalID = ( - setInterval(() => { - self.step(); - }, 30) as unknown -) as number; -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/typescript/as/export_default_as.ts.snap b/crates/rome_js_formatter/tests/specs/prettier/typescript/as/export_default_as.ts.snap index dcd1fc3e1c3..7fd5bfbdf72 100644 --- a/crates/rome_js_formatter/tests/specs/prettier/typescript/as/export_default_as.ts.snap +++ b/crates/rome_js_formatter/tests/specs/prettier/typescript/as/export_default_as.ts.snap @@ -16,13 +16,13 @@ export default (function log() {} as typeof console.log) +++ Rome @@ -1 +1 @@ -export default (function log() {} as typeof console.log); -+export default ((function log() {}) as typeof console.log); ++export default (function log() {}) as typeof console.log; ``` # Output ```js -export default ((function log() {}) as typeof console.log); +export default (function log() {}) as typeof console.log; ``` diff --git a/crates/rome_js_formatter/tests/specs/prettier/typescript/as/ternary.ts.snap b/crates/rome_js_formatter/tests/specs/prettier/typescript/as/ternary.ts.snap index 9fb6991ccae..da26a739dab 100644 --- a/crates/rome_js_formatter/tests/specs/prettier/typescript/as/ternary.ts.snap +++ b/crates/rome_js_formatter/tests/specs/prettier/typescript/as/ternary.ts.snap @@ -53,43 +53,34 @@ bifornCringerMoshedPerplexSawder = ```diff --- Prettier +++ Rome -@@ -29,22 +29,23 @@ +@@ -29,22 +29,17 @@ } function foo() { - void (( - coooooooooooooooooooooooooooooooooooooooooooooooooooond -+ void ( -+ (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar +- ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz - ) as Fooooooooooo); -+ : baaaaaaaaaaaaaaaaaaaaaz) as Fooooooooooo -+ ); ++ void ((coooooooooooooooooooooooooooooooooooooooooooooooooooond ++ ? baaaaaaaaaaaaaaaaaaaaar ++ : baaaaaaaaaaaaaaaaaaaaaz) as Fooooooooooo); } bifornCringerMoshedPerplexSawder = - askTrovenaBeenaDependsRowans + - ((glimseGlyphsHazardNoopsTieTie === 0 -- ? averredBathersBoxroomBuggyNurl -- : anodyneCondosMalateOverateRetinol) as AnnularCooeedSplicesWalksWayWay); -+ askTrovenaBeenaDependsRowans + ( -+ (glimseGlyphsHazardNoopsTieTie === 0 -+ ? averredBathersBoxroomBuggyNurl -+ : anodyneCondosMalateOverateRetinol) as AnnularCooeedSplicesWalksWayWay -+ ); ++ askTrovenaBeenaDependsRowans + ((glimseGlyphsHazardNoopsTieTie === 0 + ? averredBathersBoxroomBuggyNurl + : anodyneCondosMalateOverateRetinol) as AnnularCooeedSplicesWalksWayWay); bifornCringerMoshedPerplexSawder = - askTrovenaBeenaDependsRowans + - ((glimseGlyphsHazardNoopsTieTie === 0 && - kochabCooieGameOnOboleUnweave === Math.PI -- ? averredBathersBoxroomBuggyNurl -- : anodyneCondosMalateOverateRetinol) as AnnularCooeedSplicesWalksWayWay); -+ askTrovenaBeenaDependsRowans + ( -+ (glimseGlyphsHazardNoopsTieTie === 0 && kochabCooieGameOnOboleUnweave === Math.PI -+ ? averredBathersBoxroomBuggyNurl -+ : anodyneCondosMalateOverateRetinol) as AnnularCooeedSplicesWalksWayWay -+ ); ++ askTrovenaBeenaDependsRowans + ((glimseGlyphsHazardNoopsTieTie === 0 && kochabCooieGameOnOboleUnweave === Math.PI + ? averredBathersBoxroomBuggyNurl + : anodyneCondosMalateOverateRetinol) as AnnularCooeedSplicesWalksWayWay); ``` # Output @@ -126,31 +117,25 @@ function foo() { } function foo() { - void ( - (coooooooooooooooooooooooooooooooooooooooooooooooooooond - ? baaaaaaaaaaaaaaaaaaaaar - : baaaaaaaaaaaaaaaaaaaaaz) as Fooooooooooo - ); + void ((coooooooooooooooooooooooooooooooooooooooooooooooooooond + ? baaaaaaaaaaaaaaaaaaaaar + : baaaaaaaaaaaaaaaaaaaaaz) as Fooooooooooo); } bifornCringerMoshedPerplexSawder = - askTrovenaBeenaDependsRowans + ( - (glimseGlyphsHazardNoopsTieTie === 0 - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol) as AnnularCooeedSplicesWalksWayWay - ); + askTrovenaBeenaDependsRowans + ((glimseGlyphsHazardNoopsTieTie === 0 + ? averredBathersBoxroomBuggyNurl + : anodyneCondosMalateOverateRetinol) as AnnularCooeedSplicesWalksWayWay); bifornCringerMoshedPerplexSawder = - askTrovenaBeenaDependsRowans + ( - (glimseGlyphsHazardNoopsTieTie === 0 && kochabCooieGameOnOboleUnweave === Math.PI - ? averredBathersBoxroomBuggyNurl - : anodyneCondosMalateOverateRetinol) as AnnularCooeedSplicesWalksWayWay - ); + askTrovenaBeenaDependsRowans + ((glimseGlyphsHazardNoopsTieTie === 0 && kochabCooieGameOnOboleUnweave === Math.PI + ? averredBathersBoxroomBuggyNurl + : anodyneCondosMalateOverateRetinol) as AnnularCooeedSplicesWalksWayWay); ``` # Lines exceeding max width of 80 characters ``` - 48: (glimseGlyphsHazardNoopsTieTie === 0 && kochabCooieGameOnOboleUnweave === Math.PI + 43: askTrovenaBeenaDependsRowans + ((glimseGlyphsHazardNoopsTieTie === 0 && kochabCooieGameOnOboleUnweave === Math.PI ``` diff --git a/crates/rome_js_formatter/tests/specs/prettier/typescript/cast/as-const.ts.snap b/crates/rome_js_formatter/tests/specs/prettier/typescript/cast/as-const.ts.snap deleted file mode 100644 index 7d87ae5fab2..00000000000 --- a/crates/rome_js_formatter/tests/specs/prettier/typescript/cast/as-const.ts.snap +++ /dev/null @@ -1,42 +0,0 @@ ---- -source: crates/rome_js_formatter/tests/prettier_tests.rs ---- - -# Input - -```js -let x = '123' as const; - -// https://github.com/babel/babel/pull/11912 -x as boolean <= y; // (x as boolean) <= y; -x as boolean ?? y; // (x as boolean) ?? y; -``` - - -# Prettier differences - -```diff ---- Prettier -+++ Rome -@@ -1,5 +1,5 @@ - let x = "123" as const; - - // https://github.com/babel/babel/pull/11912 --(x as boolean) <= y; // (x as boolean) <= y; --(x as boolean) ?? y; // (x as boolean) ?? y; -+x as boolean <= y; // (x as boolean) <= y; -+x as boolean ?? y; // (x as boolean) ?? y; -``` - -# Output - -```js -let x = "123" as const; - -// https://github.com/babel/babel/pull/11912 -x as boolean <= y; // (x as boolean) <= y; -x as boolean ?? y; // (x as boolean) ?? y; -``` - - - diff --git a/crates/rome_js_formatter/tests/specs/prettier/typescript/export-default/function_as.ts.snap b/crates/rome_js_formatter/tests/specs/prettier/typescript/export-default/function_as.ts.snap index be35afa50ae..8ea53add4b4 100644 --- a/crates/rome_js_formatter/tests/specs/prettier/typescript/export-default/function_as.ts.snap +++ b/crates/rome_js_formatter/tests/specs/prettier/typescript/export-default/function_as.ts.snap @@ -16,13 +16,13 @@ export default (function log(){} as typeof console.log); +++ Rome @@ -1 +1 @@ -export default (function log() {} as typeof console.log); -+export default ((function log() {}) as typeof console.log); ++export default (function log() {}) as typeof console.log; ``` # Output ```js -export default ((function log() {}) as typeof console.log); +export default (function log() {}) as typeof console.log; ```