Repository: eslint/eslint
Pull request #8679 (2017-06-02 02:48:54)

Update: Add support for parens on left side for-loops (fixes: #8393) 
**What is the purpose of this pull request? (put an "X" next to item)**

[ ] Documentation update
[ X] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/bug-report.md))
[ ] New rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-proposal.md))
[ ] Changes an existing rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-change-proposal.md))
[ ] Add autofixing to a rule
[ ] Add a CLI option
[ ] Add something to the core
[ ] Other, please explain:

**What changes did you make? (Give an overview)**
Added a fix around supporting parens support for for-in and for-of loops which weren't giving off warnings. I updated no-extra-parens.js with supporting tests.

**Is there anything you'd like reviewers to focus on?**
I wrote a function requiresAfterSpace to determine if a space is required after a node. I didn't know a good way to test this and wasn't sure if what I have is optimal (not sure what I don't know). If you could provide some tips on what a good approach would be to fixing these issues, that would be great. I followed the docs from http://eslint.org/docs/developer-guide/working-with-rules and 



eslintbot: LGTM
eslintbot: LGTM
eslintbot: LGTM
eslintbot: LGTM
VictorHom: @not-an-aardvark thanks

I added the example you provided as a test if that is alright:
`for ((foo['bar'])of baz);`

I updated to check for adjacent nodes as you suggested. Let me know if there are other updates!

Patch:
diff --git a/docs/rules/no-mixed-requires.md b/docs/rules/no-mixed-requires.md
index b507b6c5e..7ecf63770 100644
--- a/docs/rules/no-mixed-requires.md
+++ b/docs/rules/no-mixed-requires.md
@@ -76,7 +76,7 @@ Examples of **incorrect** code for this rule with the `{ "grouping": true }` opt
 ```js
 /*eslint no-mixed-requires: ["error", { "grouping": true }]*/
 
-// invalid because of mixed types "core" and "file"
+// invalid because of mixed types "core" and "module"
 var fs = require('fs'),
     async = require('async');
 
diff --git a/lib/rules/no-extra-parens.js b/lib/rules/no-extra-parens.js
index 0a6a0d0f1..d0d79c6a3 100644
--- a/lib/rules/no-extra-parens.js
+++ b/lib/rules/no-extra-parens.js
@@ -255,6 +255,23 @@ module.exports = {
                 !astUtils.canTokensBeAdjacent(tokenBeforeLeftParen, firstToken);
         }
 
+        /**
+         * Determines whether a node should be followed by an additional space when removing parens
+         * @param {ASTNode} node node to evaluate; must be surrounded by parentheses
+         * @returns {boolean} `true` if a space should be inserted after the node
+         * @private
+         */
+        function requiresTrailingSpace(node) {
+            const nextTwoTokens = sourceCode.getTokensAfter(node, { count: 2 });
+            const rightParenToken = nextTwoTokens[0];
+            const tokenAfterRightParen = nextTwoTokens[1];
+            const tokenBeforeRightParen = sourceCode.getLastToken(node);
+
+            return rightParenToken && tokenAfterRightParen &&
+                !sourceCode.isSpaceBetweenTokens(rightParenToken, tokenAfterRightParen) &&
+                !astUtils.canTokensBeAdjacent(tokenBeforeRightParen, tokenAfterRightParen);
+        }
+
         /**
          * Report the node
          * @param {ASTNode} node node to evaluate
@@ -279,7 +296,7 @@ module.exports = {
                     return fixer.replaceTextRange([
                         leftParenToken.range[0],
                         rightParenToken.range[1]
-                    ], (requiresLeadingSpace(node) ? " " : "") + parenthesizedSource);
+                    ], (requiresLeadingSpace(node) ? " " : "") + parenthesizedSource + (requiresTrailingSpace(node) ? " " : ""));
                 }
             });
         }
@@ -488,12 +505,18 @@ module.exports = {
                 if (hasExcessParens(node.right)) {
                     report(node.right);
                 }
+                if (hasExcessParens(node.left)) {
+                    report(node.left);
+                }
             },
 
             ForOfStatement(node) {
                 if (hasExcessParens(node.right)) {
                     report(node.right);
                 }
+                if (hasExcessParens(node.left)) {
+                    report(node.left);
+                }
             },
 
             ForStatement(node) {
diff --git a/lib/rules/prefer-const.js b/lib/rules/prefer-const.js
index 595bb8a53..1395e0a8a 100644
--- a/lib/rules/prefer-const.js
+++ b/lib/rules/prefer-const.js
@@ -85,17 +85,6 @@ function getIdentifierIfShouldBeConst(variable, ignoreReadBeforeAssign) {
         return null;
     }
 
-    /*
-     * Due to a bug in acorn, code such as `let foo = 1; let foo = 2;` will not throw a syntax error. As a sanity
-     * check, make sure that the variable only has one declaration. After the parsing bug is fixed, this check
-     * will no longer be necessary, because variables declared with `let` or `const` should always have exactly one
-     * declaration.
-     * https://github.com/ternjs/acorn/issues/487
-     */
-    if (variable.defs.length > 1) {
-        return null;
-    }
-
     // Finds the unique WriteReference.
     let writer = null;
     let isReadBeforeInit = false;


Test patch:
diff --git a/tests/lib/rules/no-extra-parens.js b/tests/lib/rules/no-extra-parens.js
index b30c42d86..fb69eb713 100644
--- a/tests/lib/rules/no-extra-parens.js
+++ b/tests/lib/rules/no-extra-parens.js
@@ -41,7 +41,6 @@ function invalid(code, output, type, line, config) {
     if (line) {
         result.errors[0].line = line;
     }
-
     return result;
 }
 
@@ -996,6 +995,27 @@ ruleTester.run("no-extra-parens", rule, {
             1,
             { parserOptions: { ecmaVersion: 2015 } }
         ),
+        invalid(
+            "for ((foo) of bar);",
+            "for (foo of bar);",
+            "Identifier",
+            1,
+            { parserOptions: { ecmaVersion: 2015 } }
+        ),
+        invalid(
+            "for ((foo)in bar);",
+            "for (foo in bar);",
+            "Identifier",
+            1,
+            { parserOptions: { ecmaVersion: 2015 } }
+        ),
+        invalid(
+            "for ((foo['bar'])of baz);",
+            "for (foo['bar']of baz);",
+            "MemberExpression",
+            1,
+            { parserOptions: { ecmaVersion: 2015 } }
+        ),
         invalid(
             "() => (({ foo: 1 }).foo)",
             "() => ({ foo: 1 }).foo",