in unsupported/juno/crates/juno/src/gen_js.rs [3530:3653]
fn need_parens<'gc>(
&self,
ctx: &'gc GCLock,
path: Path<'gc>,
child: &'gc Node<'gc>,
child_pos: ChildPos,
) -> NeedParens {
#[allow(clippy::if_same_then_else)]
if matches!(path.parent, Node::ArrowFunctionExpression(_)) {
// (x) => ({x: 10}) needs parens to avoid confusing it with a block and a
// labelled statement.
if child_pos == ChildPos::Right && matches!(child, Node::ObjectExpression(_)) {
return NeedParens::Yes;
}
} else if matches!(path.parent, Node::ForStatement(_)) {
// for((a in b);..;..) needs parens to avoid confusing it with for(a in b).
return NeedParens::from(match &child {
Node::BinaryExpression(BinaryExpression {
metadata: _,
operator,
..
}) => *operator == BinaryExpressionOperator::In,
_ => false,
});
} else if matches!(path.parent, Node::NewExpression(_)) {
// `new(fn())` needs parens to avoid confusing it with `new fn()`.
// Need to check the entire subtree to ensure there isn't a call anywhere in it,
// because if there is, it would take precedence and terminate the `new` early.
// As an example, see the difference between
// `new(foo().bar)` (which gets `bar` on `foo()`)
// and
// `new foo().bar` (which gets `bar` on `new foo()`)
if child_pos == ChildPos::Left && contains_call(ctx, child) {
return NeedParens::Yes;
}
} else if matches!(path.parent, Node::ExpressionStatement(_)) {
// Expression statement like (function () {} + 1) needs parens.
return NeedParens::from(self.root_starts_with(ctx, child, |kind| -> bool {
matches!(
kind,
Node::FunctionExpression(_)
| Node::ClassExpression(_)
| Node::ObjectExpression(_)
| Node::ObjectPattern(_)
)
}));
} else if (is_unary_op(path.parent, UnaryExpressionOperator::Minus)
&& self.root_starts_with(ctx, child, check_minus))
|| (is_unary_op(path.parent, UnaryExpressionOperator::Plus)
&& self.root_starts_with(ctx, child, check_plus))
|| (child_pos == ChildPos::Right
&& is_binary_op(path.parent, BinaryExpressionOperator::Minus)
&& self.root_starts_with(ctx, child, check_minus))
|| (child_pos == ChildPos::Right
&& is_binary_op(path.parent, BinaryExpressionOperator::Plus)
&& self.root_starts_with(ctx, child, check_plus))
{
// -(-x) or -(--x) or -(-5)
// +(+x) or +(++x)
// a-(-x) or a-(--x) or a-(-5)
// a+(+x) or a+(++x)
return if self.pretty == Pretty::Yes {
NeedParens::Yes
} else {
NeedParens::Space
};
} else if matches!(
path.parent,
Node::MemberExpression(_) | Node::CallExpression(_)
) && matches!(
child,
Node::OptionalMemberExpression(_) | Node::OptionalCallExpression(_)
) && child_pos == ChildPos::Left
{
// When optional chains are terminated by non-optional member/calls,
// we need the left hand side to be pathhesized.
// Avoids confusing `(a?.b).c` with `a?.b.c`.
return NeedParens::Yes;
} else if (check_and_or(path.parent) && check_nullish(child))
|| (check_nullish(path.parent) && check_and_or(child))
{
// Nullish coalescing always requires parens when mixed with any
// other logical operations.
return NeedParens::Yes;
} else if matches!(
path.parent,
Node::CallExpression(_) | Node::OptionalCallExpression(_)
) && matches!(child, Node::SpreadElement(_))
{
// It's illegal to place parens around spread arguments.
return NeedParens::No;
}
let (child_prec, _child_assoc) = self.get_precedence(child);
if child_prec == precedence::ALWAYS_PAREN {
return NeedParens::Yes;
}
let (path_prec, path_assoc) = self.get_precedence(path.parent);
if child_prec < path_prec {
// Child is definitely a danger.
return NeedParens::Yes;
}
if child_prec > path_prec {
// Definitely cool.
return NeedParens::No;
}
// Equal precedence, so associativity (rtl/ltr) is what matters.
if child_pos == ChildPos::Anywhere {
// Child could be anywhere, so always paren.
return NeedParens::Yes;
}
if child_prec == precedence::TOP {
// Both precedences are safe.
return NeedParens::No;
}
// Check if child is on the dangerous side.
NeedParens::from(if path_assoc == Assoc::Rtl {
child_pos == ChildPos::Left
} else {
child_pos == ChildPos::Right
})
}