Repository: eslint/eslint
File: lib/rules/no-extra-parens.js (JavaScript)

Code before:
        }

        /**
         * Checks whether a node is a MemberExpression at NewExpression's callee.
         * @param {ASTNode} node node to check.
         * @returns {boolean} True if the node is a MemberExpression at NewExpression's callee. false otherwise.
         */
        function isMemberExpInNewCallee(node) {
            if (node.type === "MemberExpression") {
                return node.parent.type === "NewExpression" && node.parent.callee === node
                    ? true
                    : node.parent.object === node && isMemberExpInNewCallee(node.parent);
            }
            return false;
        }

        /**
         * Checks if the left-hand side of an assignment is an identifier and the right-hand side is
         * an anonymous class or function.
         *
         * As per https://tc39.es/ecma262/#sec-assignment-operators-runtime-semantics-evaluation, an
         * assignment where the right-hand side is an anonymous class or function and the left-hand
         * side is an *unparenthesized* identifier has different semantics than other assignments.
         * Specifically, when an expression like `foo = function () {}` is evaluated, `foo.name`
         * will be set to the string "foo", i.e. the identifier name. The same thing does not happen
         * when evaluating `(foo) = () => {}`.
         * Since the parenthesizing of the identifier in the left-hand side is significant in this
         * special case, the parentheses, if present, should not be flagged as unnecessary.
         * @param {ASTNode} node an AssignmentExpression node.
         * @returns {boolean} `true` if the left-hand side of the assignment is an identifier and
         * the right-hand side is an anonymous class or function; otherwise, `false`.
         */
        function isAnonymousFunctionAssignmentException({ left, right }) {
            if (left.type === "Identifier") {
                const rhsType = right.type;

                if (rhsType === "ArrowFunctionExpression") {
                    return true;
                }
                if ((rhsType === "FunctionExpression" || rhsType === "ClassExpression") && !right.id) {
                    return true;
                }
            }
            return false;
        }

        return {
            ArrayExpression(node) {
                node.elements
                    .filter(e => e && hasExcessParensWithPrecedence(e, PRECEDENCE_OF_ASSIGNMENT_EXPR))
                    .forEach(report);

Review comment (suggestion):
```suggestion
         * Specifically, when an expression like `foo = function () {}` is evaluated, `foo.name`
         * will be set to the string "foo", i.e. the identifier name. The same thing does not happen
         * when evaluating `(foo) = function () {}`.
```

Since we are using a function expression in the first example, I think we should use it in the second example too, to make it clear that the parentheses are what makes the difference in semantics, not an arrow function in place of the function expression. 

Code after:
        }

        /**
         * Checks whether a node is a MemberExpression at NewExpression's callee.
         * @param {ASTNode} node node to check.
         * @returns {boolean} True if the node is a MemberExpression at NewExpression's callee. false otherwise.
         */
        function isMemberExpInNewCallee(node) {
            if (node.type === "MemberExpression") {
                return node.parent.type === "NewExpression" && node.parent.callee === node
                    ? true
                    : node.parent.object === node && isMemberExpInNewCallee(node.parent);
            }
            return false;
        }

        /**
         * Checks if the left-hand side of an assignment is an identifier, the operator is one of
         * `=`, `&&=`, `||=` or `??=` and the right-hand side is an anonymous class or function.
         *
         * As per https://tc39.es/ecma262/#sec-assignment-operators-runtime-semantics-evaluation, an
         * assignment involving one of the operators `=`, `&&=`, `||=` or `??=` where the right-hand
         * side is an anonymous class or function and the left-hand side is an *unparenthesized*
         * identifier has different semantics than other assignments.
         * Specifically, when an expression like `foo = function () {}` is evaluated, `foo.name`
         * will be set to the string "foo", i.e. the identifier name. The same thing does not happen
         * when evaluating `(foo) = function () {}`.
         * Since the parenthesizing of the identifier in the left-hand side is significant in this
         * special case, the parentheses, if present, should not be flagged as unnecessary.
         * @param {ASTNode} node an AssignmentExpression node.
         * @returns {boolean} `true` if the left-hand side of the assignment is an identifier, the
         * operator is one of `=`, `&&=`, `||=` or `??=` and the right-hand side is an anonymous
         * class or function; otherwise, `false`.
         */
        function isAnonymousFunctionAssignmentException({ left, operator, right }) {
            if (left.type === "Identifier" && ["=", "&&=", "||=", "??="].includes(operator)) {
                const rhsType = right.type;

                if (rhsType === "ArrowFunctionExpression") {
                    return true;
                }
                if ((rhsType === "FunctionExpression" || rhsType === "ClassExpression") && !right.id) {
                    return true;
                }
            }
            return false;
        }

        return {
            ArrayExpression(node) {
                node.elements