diff --git a/crates/oxc_formatter/src/parentheses/expression.rs b/crates/oxc_formatter/src/parentheses/expression.rs index c70014eff2623..e4af967873ae6 100644 --- a/crates/oxc_formatter/src/parentheses/expression.rs +++ b/crates/oxc_formatter/src/parentheses/expression.rs @@ -1,3 +1,5 @@ +use std::ptr; + use oxc_allocator::Address; use oxc_ast::ast::*; use oxc_data_structures::stack; @@ -313,9 +315,8 @@ impl<'a> NeedsParentheses<'a> for AstNode<'a, CallExpression<'a>> { // when the leftmost expression is not a class expression or a function expression callee_span != leftmost.span() && matches!( - leftmost, - ExpressionLeftSide::Expression(e) - if matches!(e.as_ref(), Expression::ClassExpression(_) | Expression::FunctionExpression(_)) + leftmost.as_ref(), + Expression::ClassExpression(_) | Expression::FunctionExpression(_) ) } _ => false, diff --git a/crates/oxc_formatter/src/write/arrow_function_expression.rs b/crates/oxc_formatter/src/write/arrow_function_expression.rs index 98f57df25e49e..7e5f66a0e4998 100644 --- a/crates/oxc_formatter/src/write/arrow_function_expression.rs +++ b/crates/oxc_formatter/src/write/arrow_function_expression.rs @@ -710,12 +710,18 @@ impl<'a, 'b> From<&'b AstNode<'a, SimpleAssignmentTarget<'a>>> for ExpressionLef } impl<'a, 'b> ExpressionLeftSide<'a, 'b> { - pub fn leftmost(expression: &'b AstNode<'a, Expression<'a>>) -> Self { + pub fn leftmost( + expression: &'b AstNode<'a, Expression<'a>>, + ) -> &'b AstNode<'a, Expression<'a>> { let mut current: Self = expression.into(); loop { match current.left_expression() { None => { - break current; + break if let ExpressionLeftSide::Expression(expression) = current { + expression + } else { + unreachable!() + }; } Some(left) => { current = left; @@ -820,14 +826,10 @@ fn should_add_parens(body: &AstNode<'_, FunctionBody<'_>>) -> bool { // case and added by the object expression itself if matches!(&stmt.expression, Expression::ConditionalExpression(_)) { !matches!( - ExpressionLeftSide::leftmost(stmt.expression()), - ExpressionLeftSide::Expression( - e - ) if matches!(e.as_ref(), - Expression::ObjectExpression(_) + ExpressionLeftSide::leftmost(stmt.expression()).as_ref(), + Expression::ObjectExpression(_) | Expression::FunctionExpression(_) | Expression::ClassExpression(_) - ) ) } else { false diff --git a/crates/oxc_formatter/src/write/mod.rs b/crates/oxc_formatter/src/write/mod.rs index c1b1f994037e6..64dbb6fc66459 100644 --- a/crates/oxc_formatter/src/write/mod.rs +++ b/crates/oxc_formatter/src/write/mod.rs @@ -420,7 +420,6 @@ impl<'a> FormatWrite<'a> for AstNode<'a, AwaitExpression<'a>> { if is_callee_or_object { let mut parent = self.parent.parent(); - let mut ancestor_is_await = false; loop { match parent { AstNodes::AwaitExpression(_) @@ -436,7 +435,9 @@ impl<'a> FormatWrite<'a> for AstNode<'a, AwaitExpression<'a>> { let indented = format_with(|f| write!(f, [soft_block_indent(&format_inner)])); return if let AstNodes::AwaitExpression(expr) = parent { - if !expr.needs_parentheses(f) { + if !expr.needs_parentheses(f) + && ExpressionLeftSide::leftmost(expr.argument()).span() != self.span() + { return write!(f, [group(&indented)]); } diff --git a/crates/oxc_formatter/tests/fixtures/js/awaits/nested.js b/crates/oxc_formatter/tests/fixtures/js/awaits/nested.js new file mode 100644 index 0000000000000..e689b49d3786e --- /dev/null +++ b/crates/oxc_formatter/tests/fixtures/js/awaits/nested.js @@ -0,0 +1,5 @@ +vite = await ( + await import('vite') +).createServer({ + appType +}) \ No newline at end of file diff --git a/crates/oxc_formatter/tests/fixtures/js/awaits/nested.js.snap b/crates/oxc_formatter/tests/fixtures/js/awaits/nested.js.snap new file mode 100644 index 0000000000000..2a445c6df648c --- /dev/null +++ b/crates/oxc_formatter/tests/fixtures/js/awaits/nested.js.snap @@ -0,0 +1,17 @@ +--- +source: crates/oxc_formatter/tests/fixtures/mod.rs +--- +==================== Input ==================== +vite = await ( + await import('vite') +).createServer({ + appType +}) +==================== Output ==================== +vite = await ( + await import("vite") +).createServer({ + appType, +}); + +===================== End ===================== diff --git a/crates/oxc_formatter/tests/fixtures/js/calls/template-literal-argument.js b/crates/oxc_formatter/tests/fixtures/js/calls/template-literal-argument.js new file mode 100644 index 0000000000000..dc08ed561a92f --- /dev/null +++ b/crates/oxc_formatter/tests/fixtures/js/calls/template-literal-argument.js @@ -0,0 +1,8 @@ +expect(genCode(createVNodeCall(null, `"div"`, mockProps))) + .toMatchInlineSnapshot(` + `) + +expect(genCode(createVNodeCall(null, `"div"`, mockProps))) + .toMatchInlineSnapshot(` + `).toMatchInlineSnapshot(` + `) diff --git a/crates/oxc_formatter/tests/fixtures/js/calls/template-literal-argument.js.snap b/crates/oxc_formatter/tests/fixtures/js/calls/template-literal-argument.js.snap new file mode 100644 index 0000000000000..3a4b3f4b727e4 --- /dev/null +++ b/crates/oxc_formatter/tests/fixtures/js/calls/template-literal-argument.js.snap @@ -0,0 +1,26 @@ +--- +source: crates/oxc_formatter/tests/fixtures/mod.rs +--- +==================== Input ==================== +expect(genCode(createVNodeCall(null, `"div"`, mockProps))) + .toMatchInlineSnapshot(` + `) + +expect(genCode(createVNodeCall(null, `"div"`, mockProps))) + .toMatchInlineSnapshot(` + `).toMatchInlineSnapshot(` + `) + +==================== Output ==================== +expect( + genCode(createVNodeCall(null, `"div"`, mockProps)), +).toMatchInlineSnapshot(` + `); + +expect(genCode(createVNodeCall(null, `"div"`, mockProps))) + .toMatchInlineSnapshot(` + `) + .toMatchInlineSnapshot(` + `); + +===================== End =====================