fn need_parens()

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
        })
    }