diff options
Diffstat (limited to 'tools/eslint/lib/rules/no-extra-parens.js')
-rw-r--r-- | tools/eslint/lib/rules/no-extra-parens.js | 63 |
1 files changed, 47 insertions, 16 deletions
diff --git a/tools/eslint/lib/rules/no-extra-parens.js b/tools/eslint/lib/rules/no-extra-parens.js index 763e9235bd..50f5160b20 100644 --- a/tools/eslint/lib/rules/no-extra-parens.js +++ b/tools/eslint/lib/rules/no-extra-parens.js @@ -10,8 +10,10 @@ // Rule Definition //------------------------------------------------------------------------------ -module.exports = function(context) { +var astUtils = require("../ast-utils.js"); +module.exports = function(context) { + var isParenthesised = astUtils.isParenthesised.bind(astUtils, context); var ALL_NODES = context.options[0] !== "functions"; var EXCEPT_COND_ASSIGN = ALL_NODES && context.options[1] && context.options[1].conditionalAssign === false; var sourceCode = context.getSourceCode(); @@ -27,21 +29,6 @@ module.exports = function(context) { } /** - * Determines if a node is surrounded by parentheses. - * @param {ASTNode} node - The node to be checked. - * @returns {boolean} True if the node is parenthesised. - * @private - */ - function isParenthesised(node) { - var previousToken = context.getTokenBefore(node), - nextToken = context.getTokenAfter(node); - - return previousToken && nextToken && - previousToken.value === "(" && previousToken.range[1] <= node.range[0] && - nextToken.value === ")" && nextToken.range[0] >= node.range[1]; - } - - /** * Determines if a node is surrounded by parentheses twice. * @param {ASTNode} node - The node to be checked. * @returns {boolean} True if the node is doubly parenthesised. @@ -110,6 +97,7 @@ module.exports = function(context) { */ function isHeadOfExpressionStatement(node) { var parent = node.parent; + while (parent) { switch (parent.type) { case "SequenceExpression": @@ -191,11 +179,14 @@ module.exports = function(context) { return 4; case "&&": return 5; + // no default } /* falls through */ + case "BinaryExpression": + switch (node.operator) { case "|": return 6; @@ -226,21 +217,29 @@ module.exports = function(context) { case "/": case "%": return 13; + // no default } + /* falls through */ + case "UnaryExpression": return 14; + case "UpdateExpression": return 15; + case "CallExpression": + // IIFE is allowed to have parens in any position (#655) if (node.callee.type === "FunctionExpression") { return -1; } return 16; + case "NewExpression": return 17; + // no default } return 18; @@ -254,6 +253,7 @@ module.exports = function(context) { */ function report(node) { var previousToken = context.getTokenBefore(node); + context.report(node, previousToken.loc.start, "Gratuitous parentheses around expression."); } @@ -279,6 +279,7 @@ module.exports = function(context) { if (hasExcessParens(node.callee) && precedence(node.callee) >= precedence(node) && !( node.type === "CallExpression" && node.callee.type === "FunctionExpression" && + // One set of parentheses are allowed for a function expression !hasDoubleExcessParens(node.callee) )) { @@ -305,6 +306,7 @@ module.exports = function(context) { */ function dryBinaryLogical(node) { var prec = precedence(node); + if (hasExcessParens(node.left) && precedence(node.left) >= prec) { report(node.left); } @@ -321,6 +323,7 @@ module.exports = function(context) { } }); }, + "ArrowFunctionExpression": function(node) { if (node.body.type !== "BlockStatement") { if (node.body.type !== "ObjectExpression" && hasExcessParens(node.body) && precedence(node.body) >= precedence({type: "AssignmentExpression"})) { @@ -335,13 +338,16 @@ module.exports = function(context) { } } }, + "AssignmentExpression": function(node) { if (hasExcessParens(node.right) && precedence(node.right) >= precedence(node)) { report(node.right); } }, + "BinaryExpression": dryBinaryLogical, "CallExpression": dryCallNew, + "ConditionalExpression": function(node) { if (hasExcessParens(node.test) && precedence(node.test) >= precedence({type: "LogicalExpression", operator: "||"})) { report(node.test); @@ -353,13 +359,16 @@ module.exports = function(context) { report(node.alternate); } }, + "DoWhileStatement": function(node) { if (hasDoubleExcessParens(node.test) && !isCondAssignException(node)) { report(node.test); } }, + "ExpressionStatement": function(node) { var firstToken, secondToken, firstTokens; + if (hasExcessParens(node.expression)) { firstTokens = context.getFirstTokens(node.expression, 2); firstToken = firstTokens[0]; @@ -380,16 +389,19 @@ module.exports = function(context) { } } }, + "ForInStatement": function(node) { if (hasExcessParens(node.right)) { report(node.right); } }, + "ForOfStatement": function(node) { if (hasExcessParens(node.right)) { report(node.right); } }, + "ForStatement": function(node) { if (node.init && hasExcessParens(node.init)) { report(node.init); @@ -403,12 +415,15 @@ module.exports = function(context) { report(node.update); } }, + "IfStatement": function(node) { if (hasDoubleExcessParens(node.test) && !isCondAssignException(node)) { report(node.test); } }, + "LogicalExpression": dryBinaryLogical, + "MemberExpression": function(node) { if ( hasExcessParens(node.object) && @@ -420,6 +435,7 @@ module.exports = function(context) { typeof node.object.value === "number" && /^[0-9]+$/.test(context.getFirstToken(node.object).value)) || + // RegExp literal is allowed to have parens (#1589) (node.object.type === "Literal" && node.object.regex) ) @@ -436,25 +452,31 @@ module.exports = function(context) { report(node.property); } }, + "NewExpression": dryCallNew, + "ObjectExpression": function(node) { [].forEach.call(node.properties, function(e) { var v = e.value; + if (v && hasExcessParens(v) && precedence(v) >= precedence({type: "AssignmentExpression"})) { report(v); } }); }, + "ReturnStatement": function(node) { var returnToken = sourceCode.getFirstToken(node); if (node.argument && hasExcessParensNoLineTerminator(returnToken, node.argument) && + // RegExp literal is allowed to have parens (#1589) !(node.argument.type === "Literal" && node.argument.regex)) { report(node.argument); } }, + "SequenceExpression": function(node) { [].forEach.call(node.expressions, function(e) { if (hasExcessParens(e) && precedence(e) >= precedence(node)) { @@ -462,16 +484,19 @@ module.exports = function(context) { } }); }, + "SwitchCase": function(node) { if (node.test && hasExcessParens(node.test)) { report(node.test); } }, + "SwitchStatement": function(node) { if (hasDoubleExcessParens(node.discriminant)) { report(node.discriminant); } }, + "ThrowStatement": function(node) { var throwToken = sourceCode.getFirstToken(node); @@ -479,26 +504,32 @@ module.exports = function(context) { report(node.argument); } }, + "UnaryExpression": dryUnaryUpdate, "UpdateExpression": dryUnaryUpdate, + "VariableDeclarator": function(node) { if (node.init && hasExcessParens(node.init) && precedence(node.init) >= precedence({type: "AssignmentExpression"}) && + // RegExp literal is allowed to have parens (#1589) !(node.init.type === "Literal" && node.init.regex)) { report(node.init); } }, + "WhileStatement": function(node) { if (hasDoubleExcessParens(node.test) && !isCondAssignException(node)) { report(node.test); } }, + "WithStatement": function(node) { if (hasDoubleExcessParens(node.object)) { report(node.object); } }, + "YieldExpression": function(node) { var yieldToken; |