Repository: eslint/eslint (js)

Issue:
wrap-regex doesn't work in some expression
**Tell us about your environment**

* **ESLint Version:** 5.0.1
* **Node Version:** 8.11.3
* **npm Version:** 5.6.0

**What parser (default, Babel-ESLint, etc.) are you using?**

default

**Please show your full configuration:**

```js
module.exports = {
  rules: {
    'wrap-regex': 2,
  },
}
```

**What did you do? Please include the actual source code causing the issue, as well as the command that you used to run ESLint.**

```js
// sample.1
const bar = /abc/.test(foo)

// sample.2
if (/abc/.test(foo)){
  baz()
}
```

```bash
eslint --fix test.js
```

**What did you expect to happen?**

ESLint should report errors in both samples, but in fact, sample 2 passed the lint, no error reports.

**What actually happened? Please include the actual, raw output from ESLint.**

```js
// sample.1 fixed correct
const bar = (/abc/).test(foo)

// sample.2 not fixed
if (/abc/.test(foo)){
  baz()
}
```

**Online demo**
[demo](https://eslint.org/demo/#eyJ0ZXh0IjoiY29uc3QgYmFyID0gL2FiYy8udGVzdChmb28pXG5cbmlmICgvYWJjLy50ZXN0KGZvbykpe1xuICBiYXooKVxufSIsIm9wdGlvbnMiOnsicGFyc2VyT3B0aW9ucyI6eyJlY21hVmVyc2lvbiI6OSwic291cmNlVHlwZSI6Im1vZHVsZSIsImVjbWFGZWF0dXJlcyI6e319LCJydWxlcyI6eyJ3cmFwLXJlZ2V4IjoyfSwiZW52Ijp7fX19)

Pull request:
Fix: wrap-regex doesn't work in some expression(fixes #10573)
<!--
    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
-->

**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:

<!--
    If the item you've checked above has a template, please paste the template questions below and answer them. (If this pull request is addressing an issue, you can just paste a link to the issue here instead.)
-->

<!--
    Please ensure your pull request is ready:

    - Read the pull request guide (https://eslint.org/docs/developer-guide/contributing/pull-requests)
    - Include tests for this change
    - Update documentation for this change (if appropriate)
-->

<!--
    The following is required for all pull requests:
-->

**What changes did you make? (Give an overview)**
fixes #10573 

**Is there anything you'd like reviewers to focus on?**
no



Patch:
diff --git a/docs/rules/max-lines-per-function.md b/docs/rules/max-lines-per-function.md
index 8ce0e313e..22ffea79f 100644
--- a/docs/rules/max-lines-per-function.md
+++ b/docs/rules/max-lines-per-function.md
@@ -40,11 +40,11 @@ This rule has the following options that can be specified using an object:
 
 * `"max"` (default `50`) enforces a maximum number of lines in a function.
 
-* `"skipBlankLines": true` ignore lines made up purely of whitespace.
+* `"skipBlankLines"` (default `false`) ignore lines made up purely of whitespace.
 
-* `"skipComments": true` ignore lines containing just comments.
+* `"skipComments"` (default `false`) ignore lines containing just comments.
 
-* `"IIFEs": true` include any code included in IIFEs.
+* `"IIFEs"` (default `false`) include any code included in IIFEs.
 
 Alternatively, you may specify a single integer for the `max` option:
 
diff --git a/docs/rules/sort-keys.md b/docs/rules/sort-keys.md
index 43d3dc522..d3f135a9e 100644
--- a/docs/rules/sort-keys.md
+++ b/docs/rules/sort-keys.md
@@ -52,6 +52,9 @@ let obj = {a: 1, [c + d]: 3, b: 2};
 let obj = {a: 1, ["c" + "d"]: 3, b: 2};
 let obj = {a: 1, [`${c}`]: 3, b: 2};
 let obj = {a: 1, [tag`c`]: 3, b: 2};
+
+// This rule ignores objects that have a spread operator in them.
+let obj = {b: 1, ...c, a: 2};
 ```
 
 ## Options
diff --git a/lib/config/config-validator.js b/lib/config/config-validator.js
index 185d4650d..6b151f46a 100644
--- a/lib/config/config-validator.js
+++ b/lib/config/config-validator.js
@@ -23,10 +23,15 @@ const ruleValidators = new WeakMap();
 let validateSchema;
 
 // Defitions for deprecation warnings.
-const deprecationWarningMessages = Object.freeze({
+const deprecationWarningMessages = {
     ESLINT_LEGACY_ECMAFEATURES: "The 'ecmaFeatures' config file property is deprecated, and has no effect.",
     ESLINT_LEGACY_OBJECT_REST_SPREAD: "The 'parserOptions.ecmaFeatures.experimentalObjectRestSpread' option is deprecated. Use 'parserOptions.ecmaVersion' instead."
-});
+};
+const severityMap = {
+    error: 2,
+    warn: 1,
+    off: 0
+};
 
 /**
  * Gets a complete options schema for a rule.
@@ -65,12 +70,14 @@ function getRuleOptionsSchema(rule) {
  */
 function validateRuleSeverity(options) {
     const severity = Array.isArray(options) ? options[0] : options;
+    const normSeverity = typeof severity === "string" ? severityMap[severity.toLowerCase()] : severity;
 
-    if (severity !== 0 && severity !== 1 && severity !== 2 && !(typeof severity === "string" && /^(?:off|warn|error)$/i.test(severity))) {
-        throw new Error(`\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed '${util.inspect(severity).replace(/'/g, "\"").replace(/\n/g, "")}').\n`);
+    if (normSeverity === 0 || normSeverity === 1 || normSeverity === 2) {
+        return normSeverity;
     }
 
-    return severity;
+    throw new Error(`\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed '${util.inspect(severity).replace(/'/g, "\"").replace(/\n/g, "")}').\n`);
+
 }
 
 /**
@@ -116,7 +123,7 @@ function validateRuleOptions(rule, ruleId, options, source) {
     try {
         const severity = validateRuleSeverity(options);
 
-        if (severity !== 0 && !(typeof severity === "string" && severity.toLowerCase() === "off")) {
+        if (severity !== 0) {
             validateRuleSchema(rule, Array.isArray(options) ? options.slice(1) : []);
         }
     } catch (err) {
diff --git a/lib/formatters/stylish.js b/lib/formatters/stylish.js
index e586fe857..48d70ecb3 100644
--- a/lib/formatters/stylish.js
+++ b/lib/formatters/stylish.js
@@ -89,7 +89,7 @@ module.exports = function(results) {
 
         if (fixableErrorCount > 0 || fixableWarningCount > 0) {
             output += chalk[summaryColor].bold([
-                "  ", fixableErrorCount, pluralize(" error", fixableErrorCount), ", ",
+                "  ", fixableErrorCount, pluralize(" error", fixableErrorCount), " and ",
                 fixableWarningCount, pluralize(" warning", fixableWarningCount),
                 " potentially fixable with the `--fix` option.\n"
             ].join(""));
diff --git a/lib/rules/prefer-const.js b/lib/rules/prefer-const.js
index a7c46b34d..a784e2e31 100644
--- a/lib/rules/prefer-const.js
+++ b/lib/rules/prefer-const.js
@@ -170,7 +170,7 @@ function getIdentifierIfShouldBeConst(variable, ignoreReadBeforeAssign) {
                     const elements = leftNode.elements;
 
                     hasOuterVariables = elements
-                        .map(element => element.name)
+                        .map(element => element && element.name)
                         .some(name => isOuterVariableInDestructing(name, variable.scope));
                 }
                 if (hasOuterVariables) {
diff --git a/lib/rules/sort-keys.js b/lib/rules/sort-keys.js
index 9318e2fbb..85bfe56d5 100644
--- a/lib/rules/sort-keys.js
+++ b/lib/rules/sort-keys.js
@@ -125,7 +125,7 @@ module.exports = {
             },
 
             Property(node) {
-                if (node.parent.type === "ObjectPattern") {
+                if (node.parent.type === "ObjectPattern" || node.parent.properties.some(n => n.type === "SpreadElement")) {
                     return;
                 }
 
diff --git a/lib/rules/wrap-regex.js b/lib/rules/wrap-regex.js
index e58a4fe53..1816e0e9e 100644
--- a/lib/rules/wrap-regex.js
+++ b/lib/rules/wrap-regex.js
@@ -20,7 +20,10 @@ module.exports = {
 
         schema: [],
 
-        fixable: "code"
+        fixable: "code",
+        messages: {
+            requireParens: "Wrap the regexp literal in parens to disambiguate the slash."
+        }
     },
 
     create(context) {
@@ -33,15 +36,16 @@ module.exports = {
                     nodeType = token.type;
 
                 if (nodeType === "RegularExpression") {
-                    const source = sourceCode.getTokenBefore(node);
+                    const beforeToken = sourceCode.getTokenBefore(node);
+                    const afterToken = sourceCode.getTokenAfter(node);
                     const ancestors = context.getAncestors();
                     const grandparent = ancestors[ancestors.length - 1];
 
                     if (grandparent.type === "MemberExpression" && grandparent.object === node &&
-                        (!source || source.value !== "(")) {
+                        !(beforeToken && beforeToken.value === "(" && afterToken && afterToken.value === ")")) {
                         context.report({
                             node,
-                            message: "Wrap the regexp literal in parens to disambiguate the slash.",
+                            messageId: "requireParens",
                             fix: fixer => fixer.replaceText(node, `(${sourceCode.getText(node)})`)
                         });
                     }


Test patch:
diff --git a/tests/lib/formatters/stylish.js b/tests/lib/formatters/stylish.js
index 813788016..2a88a6dae 100644
--- a/tests/lib/formatters/stylish.js
+++ b/tests/lib/formatters/stylish.js
@@ -107,7 +107,7 @@ describe("formatter:stylish", () => {
             it("should return a string in the correct format", () => {
                 const result = formatter(code);
 
-                assert.strictEqual(result, "\nfoo.js\n  5:10  error  Unexpected foo  foo\n\n\u2716 1 problem (1 error, 0 warnings)\n  1 error, 0 warnings potentially fixable with the `--fix` option.\n");
+                assert.strictEqual(result, "\nfoo.js\n  5:10  error  Unexpected foo  foo\n\n\u2716 1 problem (1 error, 0 warnings)\n  1 error and 0 warnings potentially fixable with the `--fix` option.\n");
                 assert.strictEqual(chalkStub.yellow.bold.callCount, 0);
                 assert.strictEqual(chalkStub.red.bold.callCount, 2);
             });
@@ -146,7 +146,7 @@ describe("formatter:stylish", () => {
             it("should return a string in the correct format", () => {
                 const result = formatter(code);
 
-                assert.strictEqual(result, "\nfoo.js\n  5:10  warning  Unexpected foo  foo\n\n\u2716 1 problem (0 errors, 1 warning)\n  0 errors, 1 warning potentially fixable with the `--fix` option.\n");
+                assert.strictEqual(result, "\nfoo.js\n  5:10  warning  Unexpected foo  foo\n\n\u2716 1 problem (0 errors, 1 warning)\n  0 errors and 1 warning potentially fixable with the `--fix` option.\n");
                 assert.strictEqual(chalkStub.yellow.bold.callCount, 2);
                 assert.strictEqual(chalkStub.red.bold.callCount, 0);
             });
@@ -351,7 +351,7 @@ describe("formatter:stylish", () => {
 
             const result = formatter(code);
 
-            assert.include(result, "  1 error, 0 warnings potentially fixable with the `--fix` option.\n");
+            assert.include(result, "  1 error and 0 warnings potentially fixable with the `--fix` option.\n");
         });
 
         it("should output fixable problems message when warnings are fixable", () => {
@@ -368,7 +368,7 @@ describe("formatter:stylish", () => {
 
             const result = formatter(code);
 
-            assert.include(result, "  0 errors, 2 warnings potentially fixable with the `--fix` option.\n");
+            assert.include(result, "  0 errors and 2 warnings potentially fixable with the `--fix` option.\n");
         });
 
         it("should output the total number of fixable errors and warnings", () => {
@@ -394,7 +394,7 @@ describe("formatter:stylish", () => {
 
             const result = formatter(code);
 
-            assert.include(result, "  9 errors, 3 warnings potentially fixable with the `--fix` option.\n");
+            assert.include(result, "  9 errors and 3 warnings potentially fixable with the `--fix` option.\n");
         });
     });
 });
diff --git a/tests/lib/rules/prefer-const.js b/tests/lib/rules/prefer-const.js
index 59865ec86..c7b8a93c2 100644
--- a/tests/lib/rules/prefer-const.js
+++ b/tests/lib/rules/prefer-const.js
@@ -121,7 +121,11 @@ ruleTester.run("prefer-const", rule, {
         {
             code: "let x; function foo() { bar(x); } x = 0;",
             options: [{ ignoreReadBeforeAssign: true }]
-        }
+        },
+
+        // https://github.com/eslint/eslint/issues/10520
+        "const x = [1,2]; let y; [,y] = x; y = 0;",
+        "const x = [1,2,3]; let y, z; [y,,z] = x; y = 0; z = 0;"
     ],
     invalid: [
         {
@@ -361,6 +365,21 @@ ruleTester.run("prefer-const", rule, {
                 { message: "'foo' is never reassigned. Use 'const' instead.", type: "Identifier" },
                 { message: "'bar' is never reassigned. Use 'const' instead.", type: "Identifier" }
             ]
+        },
+
+        // https://github.com/eslint/eslint/issues/10520
+        {
+            code: "const x = [1,2]; let [,y] = x;",
+            output: "const x = [1,2]; const [,y] = x;",
+            errors: [{ message: "'y' is never reassigned. Use 'const' instead.", type: "Identifier" }]
+        },
+        {
+            code: "const x = [1,2,3]; let [y,,z] = x;",
+            output: "const x = [1,2,3]; const [y,,z] = x;",
+            errors: [
+                { message: "'y' is never reassigned. Use 'const' instead.", type: "Identifier" },
+                { message: "'z' is never reassigned. Use 'const' instead.", type: "Identifier" }
+            ]
         }
     ]
 });
diff --git a/tests/lib/rules/sort-keys.js b/tests/lib/rules/sort-keys.js
index c7cf8ecf4..5a3a0a057 100644
--- a/tests/lib/rules/sort-keys.js
+++ b/tests/lib/rules/sort-keys.js
@@ -35,6 +35,7 @@ ruleTester.run("sort-keys", rule, {
 
         // ignore spread properties.
         { code: "var obj = {a:1, ...z, b:1}", options: [], parserOptions: { ecmaVersion: 2018 } },
+        { code: "var obj = {b:1, ...z, a:1}", options: [], parserOptions: { ecmaVersion: 2018 } },
 
         // ignore destructuring patterns.
         { code: "let {a, b} = {}", options: [], parserOptions: { ecmaVersion: 6 } },
@@ -157,17 +158,6 @@ ruleTester.run("sort-keys", rule, {
             errors: ["Expected object keys to be in ascending order. 'a' should be before 'b'."]
         },
 
-        // ignore spred properties.
-        {
-            code: "var obj = {b:1, ...z, a:1}",
-            parserOptions: {
-                ecmaVersion: 2018
-            },
-            errors: [
-                "Expected object keys to be in ascending order. 'a' should be before 'b'."
-            ]
-        },
-
         // nested
         {
             code: "var obj = {a:1, c:{y:1, x:1}, b:1}",
diff --git a/tests/lib/rules/wrap-regex.js b/tests/lib/rules/wrap-regex.js
index 203f7852c..8e3fc2714 100644
--- a/tests/lib/rules/wrap-regex.js
+++ b/tests/lib/rules/wrap-regex.js
@@ -37,6 +37,13 @@ ruleTester.run("wrap-regex", rule, {
             code: "/foo/ig.test(bar);",
             output: "(/foo/ig).test(bar);",
             errors: [{ message: "Wrap the regexp literal in parens to disambiguate the slash.", type: "Literal" }]
+        },
+
+        // https://github.com/eslint/eslint/issues/10573
+        {
+            code: "if(/foo/ig.test(bar));",
+            output: "if((/foo/ig).test(bar));",
+            errors: [{ message: "Wrap the regexp literal in parens to disambiguate the slash.", type: "Literal" }]
         }
     ]
 });