From ce1f08477fd5d03484c2081ff4bd0b67eb23b4b4 Mon Sep 17 00:00:00 2001 From: jackyho112 Date: Thu, 14 Sep 2017 19:08:27 -0700 Subject: [PATCH] Update: fix MemberExpression handling in no-extra-parens (fixes #9156) --- lib/rules/no-extra-parens.js | 45 +++++++++++++++++++++++++++--- tests/lib/rules/no-extra-parens.js | 11 ++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/lib/rules/no-extra-parens.js b/lib/rules/no-extra-parens.js index 7e350d29eda..9c8fe70f030 100644 --- a/lib/rules/no-extra-parens.js +++ b/lib/rules/no-extra-parens.js @@ -336,6 +336,23 @@ module.exports = { } } + /** + * Check if a member expression contains a call expression + * @param {ASTNode} node MemberExpression node to evaluate + * @returns {boolean} true if found, false if not + */ + function doesMemberExpressionContainCallExpression(node) { + let currentNode = node.object; + let currentNodeType = node.object.type; + + while (currentNodeType === "MemberExpression") { + currentNode = currentNode.object; + currentNodeType = currentNode.type; + } + + return currentNodeType === "CallExpression"; + } + /** * Evaluate a new call * @param {ASTNode} node node to evaluate @@ -343,10 +360,21 @@ module.exports = { * @private */ function checkCallNew(node) { - if (hasExcessParens(node.callee) && precedence(node.callee) >= precedence(node)) { - const hasNewParensException = node.callee.type === "NewExpression" && !isNewExpressionWithParens(node.callee); + const callee = node.callee; - if (hasDoubleExcessParens(node.callee) || !isIIFE(node) && !hasNewParensException) { + if (hasExcessParens(callee) && precedence(callee) >= precedence(node)) { + const hasNewParensException = callee.type === "NewExpression" && !isNewExpressionWithParens(callee); + + if ( + hasDoubleExcessParens(callee) || + !isIIFE(node) && !hasNewParensException && !( + + // Allow extra parens around a new expression if + // there are intervening parentheses. + callee.type === "MemberExpression" && + doesMemberExpressionContainCallExpression(callee) + ) + ) { report(node.callee); } } @@ -574,8 +602,10 @@ module.exports = { LogicalExpression: checkBinaryLogical, MemberExpression(node) { + const nodeObjHasExcessParens = hasExcessParens(node.object); + if ( - hasExcessParens(node.object) && + nodeObjHasExcessParens && precedence(node.object) >= precedence(node) && ( node.computed || @@ -589,6 +619,13 @@ module.exports = { ) { report(node.object); } + + if (nodeObjHasExcessParens && + node.object.type === "CallExpression" && + node.parent.type !== "NewExpression") { + report(node.object); + } + if (node.computed && hasExcessParens(node.property)) { report(node.property); } diff --git a/tests/lib/rules/no-extra-parens.js b/tests/lib/rules/no-extra-parens.js index 4958fb6acb3..7753c7d6682 100644 --- a/tests/lib/rules/no-extra-parens.js +++ b/tests/lib/rules/no-extra-parens.js @@ -89,6 +89,12 @@ ruleTester.run("no-extra-parens", rule, { "a(b = c, (d, e))", "(++a)(b); (c++)(d);", "new (A())", + "new (foo.Baz().foo)", + "new (foo.baz.bar().foo.baz)", + "new ({}.baz.bar.foo().baz)", + "new (doSomething().baz.bar().foo)", + "new ([][0].baz.foo().bar.foo)", + "new (foo\n.baz\n.bar()\n.foo.baz)", "new A()()", "(new A)()", "(new (Foo || Bar))()", @@ -516,11 +522,16 @@ ruleTester.run("no-extra-parens", rule, { invalid("new (\nfunction(){}\n)", "new \nfunction(){}\n", "FunctionExpression", 1), invalid("((function foo() {return 1;}))()", "(function foo() {return 1;})()", "FunctionExpression"), invalid("((function(){ return bar(); })())", "(function(){ return bar(); })()", "CallExpression"), + invalid("(foo()).bar", "foo().bar", "CallExpression"), + invalid("(foo.bar()).baz", "foo.bar().baz", "CallExpression"), + invalid("(foo\n.bar())\n.baz", "foo\n.bar()\n.baz", "CallExpression"), invalid("new (A)", "new A", "Identifier"), invalid("(new A())()", "new A()()", "NewExpression"), invalid("(new A(1))()", "new A(1)()", "NewExpression"), invalid("((new A))()", "(new A)()", "NewExpression"), + invalid("new (foo\n.baz\n.bar\n.foo.baz)", "new foo\n.baz\n.bar\n.foo.baz", "MemberExpression"), + invalid("new (foo.baz.bar.baz)", "new foo.baz.bar.baz", "MemberExpression"), invalid("0, (_ => 0)", "0, _ => 0", "ArrowFunctionExpression", 1), invalid("(_ => 0), 0", "_ => 0, 0", "ArrowFunctionExpression", 1),