Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions crates/oxc_formatter/src/ast_nodes/impls/ast_nodes.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! Implementations of methods for [`AstNodes`].

use oxc_span::{GetSpan, Span};

use crate::ast_nodes::AstNodes;

impl<'a> AstNodes<'a> {
Expand Down Expand Up @@ -34,4 +36,13 @@ impl<'a> AstNodes<'a> {
_ => self,
}
}

/// Check if the passing span is the callee of a CallExpression or NewExpression
pub fn is_call_like_callee_span(&self, span: Span) -> bool {
match self {
AstNodes::CallExpression(expr) => expr.callee.span() == span,
AstNodes::NewExpression(expr) => expr.callee.span() == span,
_ => false,
}
}
}
42 changes: 42 additions & 0 deletions crates/oxc_formatter/src/ast_nodes/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,24 @@ impl<'a> AstNode<'a, Program<'a>> {
}
}

impl<T: GetSpan> AstNode<'_, T> {
/// Check if this node is the callee of a CallExpression or NewExpression
pub fn is_call_like_callee(&self) -> bool {
let callee = match self.parent {
AstNodes::CallExpression(call) => &call.callee,
AstNodes::NewExpression(new) => &new.callee,
_ => return false,
};

callee.span() == self.span()
}

/// Check if this node is the callee of a NewExpression
pub fn is_new_callee(&self) -> bool {
matches!(self.parent, AstNodes::NewExpression(new) if new.callee.span() == self.span())
}
}

impl<'a> AstNode<'a, ExpressionStatement<'a>> {
/// Check if this ExpressionStatement is the body of an arrow function expression
///
Expand Down Expand Up @@ -170,3 +188,27 @@ impl<'a> AstNode<'a, ImportExpression<'a>> {
})
}
}

impl<'a> AstNode<'a, CallExpression<'a>> {
/// Check if the passing span is the callee of this CallExpression
pub fn is_callee_span(&self, span: Span) -> bool {
self.inner.callee.span() == span
}

/// Check if the passing span is an argument of this CallExpression
pub fn is_argument_span(&self, span: Span) -> bool {
!self.is_callee_span(span)
}
}

impl<'a> AstNode<'a, NewExpression<'a>> {
/// Check if the passing span is the callee of this NewExpression
pub fn is_callee_span(&self, span: Span) -> bool {
self.inner.callee.span() == span
}

/// Check if the passing span is an argument of this NewExpression
pub fn is_argument_span(&self, span: Span) -> bool {
!self.is_callee_span(span)
}
}
90 changes: 34 additions & 56 deletions crates/oxc_formatter/src/parentheses/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,8 +261,7 @@ impl NeedsParentheses<'_> for AstNode<'_, MemberExpression<'_>> {

impl NeedsParentheses<'_> for AstNode<'_, ComputedMemberExpression<'_>> {
fn needs_parentheses(&self, f: &Formatter<'_, '_>) -> bool {
matches!(self.parent, AstNodes::NewExpression(_))
&& (self.optional || member_chain_callee_needs_parens(&self.object))
self.is_new_callee() && (self.optional || member_chain_callee_needs_parens(&self.object))
}
}

Expand All @@ -272,7 +271,7 @@ impl NeedsParentheses<'_> for AstNode<'_, StaticMemberExpression<'_>> {
return false;
}

matches!(self.parent, AstNodes::NewExpression(_)) && {
self.is_new_callee() && {
ExpressionLeftSide::Expression(self.object()).iter().any(|expr| {
matches!(expr, ExpressionLeftSide::Expression(e) if
matches!(e.as_ref(), Expression::CallExpression(_))
Expand All @@ -296,7 +295,6 @@ impl NeedsParentheses<'_> for AstNode<'_, CallExpression<'_>> {
}

match self.parent {
AstNodes::NewExpression(_) => true,
AstNodes::ExportDefaultDeclaration(_) => {
let callee = &self.callee();
let callee_span = callee.span();
Expand All @@ -309,7 +307,7 @@ impl NeedsParentheses<'_> for AstNode<'_, CallExpression<'_>> {
Expression::ClassExpression(_) | Expression::FunctionExpression(_)
)
}
_ => false,
_ => self.is_new_callee(),
}
}
}
Expand Down Expand Up @@ -488,16 +486,13 @@ impl NeedsParentheses<'_> for AstNode<'_, Function<'_>> {
}

let parent = self.parent;
matches!(
parent,
AstNodes::CallExpression(_)
| AstNodes::NewExpression(_)
| AstNodes::TaggedTemplateExpression(_)
) || is_first_in_statement(
self.span,
parent,
FirstInStatementMode::ExpressionOrExportDefault,
)
matches!(parent, AstNodes::TaggedTemplateExpression(_))
|| self.is_call_like_callee()
|| is_first_in_statement(
self.span,
parent,
FirstInStatementMode::ExpressionOrExportDefault,
)
}
}

Expand Down Expand Up @@ -614,11 +609,11 @@ impl NeedsParentheses<'_> for AstNode<'_, ChainExpression<'_>> {
}

match self.parent {
AstNodes::NewExpression(_) => true,
AstNodes::CallExpression(call) => !call.optional,
AstNodes::NewExpression(new) => new.is_callee_span(self.span),
AstNodes::CallExpression(call) => call.is_callee_span(self.span) && !call.optional,
AstNodes::StaticMemberExpression(member) => !member.optional,
AstNodes::ComputedMemberExpression(member) => {
!member.optional && member.object.span() == self.span()
!member.optional && member.object.span() == self.span
}
_ => false,
}
Expand All @@ -636,20 +631,14 @@ impl NeedsParentheses<'_> for AstNode<'_, Class<'_>> {
}

let parent = self.parent;
match parent {
AstNodes::CallExpression(_)
| AstNodes::NewExpression(_)
| AstNodes::ExportDefaultDeclaration(_) => true,

_ => {
(is_class_extends(self.span, self.parent) && !self.decorators.is_empty())
|| is_first_in_statement(
self.span,
parent,
FirstInStatementMode::ExpressionOrExportDefault,
)
}
}
matches!(parent, AstNodes::TaggedTemplateExpression(_))
|| self.is_call_like_callee()
|| (is_class_extends(self.span, self.parent) && !self.decorators.is_empty())
|| is_first_in_statement(
self.span,
parent,
FirstInStatementMode::ExpressionOrExportDefault,
)
}
}

Expand Down Expand Up @@ -704,7 +693,7 @@ impl NeedsParentheses<'_> for AstNode<'_, ImportExpression<'_>> {
return false;
}

matches!(self.parent, AstNodes::NewExpression(_))
self.is_new_callee()
}
}

Expand Down Expand Up @@ -768,9 +757,6 @@ fn type_cast_like_needs_parens(span: Span, parent: &AstNodes<'_>) -> bool {
| AstNodes::UnaryExpression(_)
| AstNodes::AwaitExpression(_)
| AstNodes::TSNonNullExpression(_)
// Callee
| AstNodes::CallExpression(_)
| AstNodes::NewExpression(_)
// template tag
| AstNodes::TaggedTemplateExpression(_)
// in spread
Expand All @@ -787,16 +773,15 @@ fn type_cast_like_needs_parens(span: Span, parent: &AstNodes<'_>) -> bool {
AstNodes::AssignmentExpression(assignment) => {
assignment.left.span() == span
}
_ => is_class_extends(span, parent),
_ => parent.is_call_like_callee_span(span) || is_class_extends(span, parent),
}
}

impl NeedsParentheses<'_> for AstNode<'_, TSNonNullExpression<'_>> {
fn needs_parentheses(&self, f: &Formatter<'_, '_>) -> bool {
let parent = self.parent;
is_class_extends(self.span, parent)
|| (matches!(parent, AstNodes::NewExpression(_))
&& member_chain_callee_needs_parens(&self.expression))
|| (self.is_new_callee() && member_chain_callee_needs_parens(&self.expression))
}
}

Expand All @@ -823,8 +808,6 @@ fn binary_like_needs_parens(binary_like: BinaryLikeExpression<'_, '_>) -> bool {
| AstNodes::TSNonNullExpression(_)
| AstNodes::SpreadElement(_)
| AstNodes::JSXSpreadAttribute(_)
| AstNodes::CallExpression(_)
| AstNodes::NewExpression(_)
| AstNodes::ChainExpression(_)
| AstNodes::StaticMemberExpression(_)
| AstNodes::TaggedTemplateExpression(_) => return true,
Expand All @@ -838,6 +821,7 @@ fn binary_like_needs_parens(binary_like: BinaryLikeExpression<'_, '_>) -> bool {
}
AstNodes::BinaryExpression(binary) => BinaryLikeExpression::BinaryExpression(binary),
AstNodes::LogicalExpression(logical) => BinaryLikeExpression::LogicalExpression(logical),
parent if parent.is_call_like_callee_span(binary_like.span()) => return true,
_ => return false,
};

Expand Down Expand Up @@ -921,22 +905,18 @@ fn unary_like_expression_needs_parens(node: UnaryLike<'_, '_>) -> bool {
///
/// This is generally the case if the expression is used in a left hand side, or primary expression context.
fn update_or_lower_expression_needs_parens(span: Span, parent: &AstNodes<'_>) -> bool {
if matches!(
parent,
match parent {
AstNodes::TSNonNullExpression(_)
| AstNodes::CallExpression(_)
| AstNodes::NewExpression(_)
| AstNodes::StaticMemberExpression(_)
| AstNodes::TaggedTemplateExpression(_)
) || is_class_extends(span, parent)
{
return true;
| AstNodes::StaticMemberExpression(_)
| AstNodes::TaggedTemplateExpression(_) => return true,
_ if is_class_extends(span, parent) || parent.is_call_like_callee_span(span) => {
return true;
}
_ => {}
}

if let AstNodes::ComputedMemberExpression(computed_member_expr) = parent {
return computed_member_expr.object.span() == span;
}

false
}

Expand Down Expand Up @@ -985,8 +965,6 @@ fn is_first_in_statement(
AstNodes::StaticMemberExpression(_)
| AstNodes::TaggedTemplateExpression(_)
| AstNodes::ChainExpression(_)
| AstNodes::CallExpression(_)
| AstNodes::NewExpression(_)
| AstNodes::TSAsExpression(_)
| AstNodes::TSSatisfiesExpression(_)
| AstNodes::TSNonNullExpression(_) => {}
Expand Down Expand Up @@ -1025,6 +1003,7 @@ fn is_first_in_statement(
{
return !is_not_first_iteration;
}
_ if ancestor.is_call_like_callee_span(current_span) => {}
_ => break,
}
current_span = ancestor.span();
Expand Down Expand Up @@ -1098,11 +1077,10 @@ fn jsx_element_or_fragment_needs_paren(span: Span, parent: &AstNodes<'_>) -> boo
| AstNodes::UnaryExpression(_)
| AstNodes::TSNonNullExpression(_)
| AstNodes::SpreadElement(_)
| AstNodes::CallExpression(_)
| AstNodes::NewExpression(_)
| AstNodes::TaggedTemplateExpression(_)
| AstNodes::JSXSpreadAttribute(_)
| AstNodes::JSXSpreadChild(_) => true,
_ if parent.is_call_like_callee_span(span) => true,
_ => false,
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_formatter/src/utils/call_expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub fn is_test_call_expression(call: &AstNode<CallExpression<'_>>) -> bool {
match (args.next(), args.next(), args.next()) {
(Some(argument), None, None) if arguments.len() == 1 => {
if is_angular_test_wrapper(call) && {
if let AstNodes::CallExpression(call) = call.grand_parent() {
if let AstNodes::CallExpression(call) = call.parent {
is_test_call_expression(call)
} else {
false
Expand Down
38 changes: 0 additions & 38 deletions crates/oxc_formatter/src/utils/jsx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,44 +71,6 @@ pub enum WrapState {
WrapOnBreak,
}

/// Checks if a JSX Element should be wrapped in parentheses. Returns a [WrapState] which
/// indicates when the element should be wrapped in parentheses.
pub fn get_wrap_state(parent: &AstNodes<'_>) -> WrapState {
// Call site has ensures that only non-nested JSX elements are passed.
debug_assert!(!matches!(parent, AstNodes::JSXElement(_) | AstNodes::JSXFragment(_)));

match parent {
AstNodes::ArrayExpression(_)
| AstNodes::JSXAttribute(_)
| AstNodes::JSXExpressionContainer(_)
| AstNodes::ConditionalExpression(_) => WrapState::NoWrap,
AstNodes::StaticMemberExpression(member) => {
if member.optional {
WrapState::NoWrap
} else {
WrapState::WrapOnBreak
}
}
// TODO: Figure out if no `AstNodes::Argument` exists
// AstNodes::Argument(argument) if matches!(argument.parent, AstNodes::CallExpression(_)) => {
// WrapState::NoWrap
// }
AstNodes::ExpressionStatement(stmt) => {
// `() => <div></div>`
// ^^^^^^^^^^^
if stmt.is_arrow_function_body() { WrapState::WrapOnBreak } else { WrapState::NoWrap }
}
AstNodes::ComputedMemberExpression(member) => {
if member.optional {
WrapState::NoWrap
} else {
WrapState::WrapOnBreak
}
}
_ => WrapState::WrapOnBreak,
}
}

/// Creates either a space using an expression child and a string literal,
/// or a regular space, depending on whether the group breaks or not.
///
Expand Down
4 changes: 3 additions & 1 deletion crates/oxc_formatter/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ use crate::{
/// `connect(a, b, c)(d)`
/// ```
pub fn is_long_curried_call(call: &AstNode<'_, CallExpression<'_>>) -> bool {
if let AstNodes::CallExpression(parent_call) = call.parent {
if let AstNodes::CallExpression(parent_call) = call.parent
&& parent_call.is_callee_span(call.span)
{
return call.arguments().len() > parent_call.arguments().len()
&& !parent_call.arguments().is_empty();
}
Expand Down
7 changes: 3 additions & 4 deletions crates/oxc_formatter/src/write/arrow_function_expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,6 @@ impl<'a> Format<'a> for ArrowChain<'a, '_> {
fn fmt(&self, f: &mut Formatter<'_, 'a>) -> FormatResult<()> {
let ArrowChain { tail, expand_signatures, .. } = self;

let head_parent = self.head.parent;
let tail_body = tail.body();
let is_assignment_rhs = self.options.assignment_layout.is_some();
let is_grouped_call_arg_layout = self.options.call_arg_layout.is_some();
Expand All @@ -436,8 +435,7 @@ impl<'a> Format<'a> for ArrowChain<'a, '_> {
// () => () =>
// a
// )();
let is_callee =
matches!(head_parent, AstNodes::CallExpression(_) | AstNodes::NewExpression(_));
let is_callee = self.head.is_call_like_callee();

// With arrays, objects, sequence expressions, and block function bodies,
// the opening brace gives a convenient boundary to insert a line break,
Expand Down Expand Up @@ -627,7 +625,8 @@ impl<'a> Format<'a> for ArrowChain<'a, '_> {

let format_tail_body = format_with(|f| {
// if it's inside a JSXExpression (e.g. an attribute) we should align the expression's closing } with the line with the opening {.
let should_add_soft_line = matches!(head_parent, AstNodes::JSXExpressionContainer(_));
let should_add_soft_line =
matches!(self.head.parent, AstNodes::JSXExpressionContainer(_));

if body_on_separate_line {
write!(
Expand Down
Loading
Loading