diff --git a/lib/ast-utils.js b/lib/ast-utils.js index c88e3496713..e65284ff790 100644 --- a/lib/ast-utils.js +++ b/lib/ast-utils.js @@ -808,19 +808,14 @@ module.exports = { return 17; case "CallExpression": - - // IIFE is allowed to have parens in any position (#655) - if (node.callee.type === "FunctionExpression") { - return -1; - } return 18; case "NewExpression": return 19; - // no default + default: + return 20; } - return 20; }, /** diff --git a/lib/rules/no-extra-parens.js b/lib/rules/no-extra-parens.js index 5b0c483e70a..7e350d29eda 100644 --- a/lib/rules/no-extra-parens.js +++ b/lib/rules/no-extra-parens.js @@ -276,6 +276,15 @@ module.exports = { !astUtils.canTokensBeAdjacent(tokenBeforeRightParen, tokenAfterRightParen); } + /** + * Determines if a given expression node is an IIFE + * @param {ASTNode} node The node to check + * @returns {boolean} `true` if the given node is an IIFE + */ + function isIIFE(node) { + return node.type === "CallExpression" && node.callee.type === "FunctionExpression"; + } + /** * Report the node * @param {ASTNode} node node to evaluate @@ -286,8 +295,14 @@ module.exports = { const leftParenToken = sourceCode.getTokenBefore(node); const rightParenToken = sourceCode.getTokenAfter(node); - if (tokensToIgnore.has(sourceCode.getFirstToken(node)) && !isParenthesisedTwice(node)) { - return; + if (!isParenthesisedTwice(node)) { + if (tokensToIgnore.has(sourceCode.getFirstToken(node))) { + return; + } + + if (isIIFE(node) && !isParenthesised(node.callee)) { + return; + } } context.report({ @@ -328,15 +343,12 @@ module.exports = { * @private */ function checkCallNew(node) { - if (hasExcessParens(node.callee) && precedence(node.callee) >= precedence(node) && !( - node.type === "CallExpression" && - (node.callee.type === "FunctionExpression" || - node.callee.type === "NewExpression" && !isNewExpressionWithParens(node.callee)) && - - // One set of parentheses are allowed for a function expression - !hasDoubleExcessParens(node.callee) - )) { - report(node.callee); + if (hasExcessParens(node.callee) && precedence(node.callee) >= precedence(node)) { + const hasNewParensException = node.callee.type === "NewExpression" && !isNewExpressionWithParens(node.callee); + + if (hasDoubleExcessParens(node.callee) || !isIIFE(node) && !hasNewParensException) { + report(node.callee); + } } if (node.arguments.length === 1) { if (hasDoubleExcessParens(node.arguments[0]) && precedence(node.arguments[0]) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) { diff --git a/tests/lib/rules/no-extra-parens.js b/tests/lib/rules/no-extra-parens.js index d9345960e05..4958fb6acb3 100644 --- a/tests/lib/rules/no-extra-parens.js +++ b/tests/lib/rules/no-extra-parens.js @@ -175,6 +175,7 @@ ruleTester.run("no-extra-parens", rule, { "var o = { foo: (function() { return bar(); })() };", "o.foo = (function(){ return bar(); })();", "(function(){ return bar(); })(), (function(){ return bar(); })()", + "function foo() { return (function(){}()); }", // parens are required around yield "var foo = (function*() { if ((yield foo()) + 1) { return; } }())",