Skip to content
Permalink
Browse files

Update: Option to allow extra parens for cond assign (fixes #3317)

  • Loading branch information...
alberto committed Jan 10, 2016
1 parent 641ccd5 commit d487013a5a1b43ec5a4fda1fbfaa79b49059cfc3
Showing with 91 additions and 12 deletions.
  1. +31 −2 docs/rules/no-extra-parens.md
  2. +47 −10 lib/rules/no-extra-parens.js
  3. +13 −0 tests/lib/rules/no-extra-parens.js
@@ -13,7 +13,16 @@ A few cases of redundant parentheses are always allowed:

### Options

The default behavior of the rule is specified by `"all"` and it will report unnecessary parentheses around any expression. The following patterns are considered problems:
This rule takes 1 or 2 arguments. The first one is a string which must be one of the following:

* `"all"` (default): it will report unnecessary parentheses around any expression.
* `"functions"`: only function expressions will be checked for unnecessary parentheses.

The second one is an object for more fine-grained configuration when the first option is "all".

#### "all"

The following patterns are considered problems:

```js
/*eslint no-extra-parens: 2*/
@@ -41,7 +50,27 @@ The following patterns are not considered problems:
(/^a$/).test(x);
```

If the option is set to `"functions"`, only function expressions will be checked for unnecessary parentheses. The following patterns are considered problems:
##### Fine-grained control

When setting the first option as "all", an additional option can be added to allow extra parens for assignment in conditional statements.

The following patterns are not considered problems:

```js
/*eslint no-extra-parens: [2, "all", {"conditionalAssign": false}]*/
while ((foo = bar())) {}
if ((foo = bar())) {}
do; while ((foo = bar()))
for (;(a = b););
```

#### "functions"

The following patterns are considered problems:

```js
/*eslint no-extra-parens: [2, "functions"]*/
@@ -13,6 +13,7 @@
module.exports = function(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();

/**
@@ -76,6 +77,16 @@ module.exports = function(context) {
return ruleApplies(node) && isParenthesisedTwice(node);
}

/**
* Determines if a node test expression is allowed to have a parenthesised assignment
* @param {ASTNode} node - The node to be checked.
* @returns {boolean} True if the assignment can be parenthesised.
* @private
*/
function isCondAssignException(node) {
return EXCEPT_COND_ASSIGN && node.test.type === "AssignmentExpression";
}

/**
* Determines if a node following a [no LineTerminator here] restriction is
* surrounded by (potentially) invalid extra parentheses.
@@ -317,7 +328,7 @@ module.exports = function(context) {
return;
}

// Object literals *must* be parenthesized
// Object literals *must* be parenthesised
if (node.body.type === "ObjectExpression" && hasDoubleExcessParens(node.body)) {
report(node.body);
return;
@@ -343,7 +354,7 @@ module.exports = function(context) {
}
},
"DoWhileStatement": function(node) {
if (hasDoubleExcessParens(node.test)) {
if (hasDoubleExcessParens(node.test) && !isCondAssignException(node)) {
report(node.test);
}
},
@@ -384,7 +395,7 @@ module.exports = function(context) {
report(node.init);
}

if (node.test && hasExcessParens(node.test)) {
if (node.test && hasExcessParens(node.test) && !isCondAssignException(node)) {
report(node.test);
}

@@ -393,7 +404,7 @@ module.exports = function(context) {
}
},
"IfStatement": function(node) {
if (hasDoubleExcessParens(node.test)) {
if (hasDoubleExcessParens(node.test) && !isCondAssignException(node)) {
report(node.test);
}
},
@@ -479,7 +490,7 @@ module.exports = function(context) {
}
},
"WhileStatement": function(node) {
if (hasDoubleExcessParens(node.test)) {
if (hasDoubleExcessParens(node.test) && !isCondAssignException(node)) {
report(node.test);
}
},
@@ -505,8 +516,34 @@ module.exports = function(context) {

};

module.exports.schema = [
{
"enum": ["all", "functions"]
}
];
module.exports.schema = {
"anyOf": [
{
"type": "array",
"items": [
{
"enum": ["functions"]
}
],
"minItems": 0,
"maxItems": 1
},
{
"type": "array",
"items": [
{
"enum": ["all"]
},
{
"type": "object",
"properties": {
"conditionalAssign": {"type": "boolean"}
},
"additionalProperties": false
}
],
"minItems": 0,
"maxItems": 2
}
]
};
@@ -184,6 +184,12 @@ ruleTester.run("no-extra-parens", rule, {
{code: "var a = (b = c)", options: ["functions"]},
{code: "_ => (a = 0)", options: ["functions"], parserOptions: { ecmaVersion: 6 }},

// ["all", {conditionalAssign: false}] enables extra parens around conditional assignments
{code: "while ((foo = bar())) {}", options: ["all", {conditionalAssign: false}]},
{code: "if ((foo = bar())) {}", options: ["all", {conditionalAssign: false}]},
{code: "do; while ((foo = bar()))", options: ["all", {conditionalAssign: false}]},
{code: "for (;(a = b););", options: ["all", {conditionalAssign: false}]},

// https://github.com/eslint/eslint/issues/3653
"(function(){}).foo(), 1, 2;",
"(function(){}).foo++;",
@@ -308,6 +314,13 @@ ruleTester.run("no-extra-parens", rule, {
invalid("(_ => 0), 0", "ArrowFunctionExpression", 1, {options: ["functions"], parserOptions: { ecmaVersion: 6 }}),
invalid("a = (_ => 0)", "ArrowFunctionExpression", 1, {options: ["functions"], parserOptions: { ecmaVersion: 6 }}),


invalid("while ((foo = bar())) {}", "AssignmentExpression"),
invalid("while ((foo = bar())) {}", "AssignmentExpression", 1, {options: ["all", {conditionalAssign: true}]}),
invalid("if ((foo = bar())) {}", "AssignmentExpression"),
invalid("do; while ((foo = bar()))", "AssignmentExpression"),
invalid("for (;(a = b););", "AssignmentExpression"),

// https://github.com/eslint/eslint/issues/3653
invalid("((function(){})).foo();", "FunctionExpression"),
invalid("((function(){}).foo());", "CallExpression"),

0 comments on commit d487013

Please sign in to comment.
You can’t perform that action at this time.