Repository: eslint/eslint
Pull request #12164 (2019-08-25 12:42:45)

Fix: no-extra-boolean-cast invalid autofix with yield before negation
<!--
    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)**

[X] Bug fix

<!--
    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.)
-->

**Tell us about your environment**

* **ESLint Version:** 6.2.2
* **Node Version:** 10.16.0
* **npm Version:** 6.9.0

**What parser (default, Babel-ESLint, etc.) are you using?**

default

**Please show your full configuration:**

<details>
<summary>Configuration</summary>

<!-- Paste your configuration below: -->
```js
module.exports = {
  parserOptions: {
    ecmaVersion: 2015,
  },
};
```

</details>

**What did you do? Please include the actual source code causing the issue.**

[Demo link](https://eslint.org/demo#eyJ0ZXh0IjoiLyplc2xpbnQgbm8tZXh0cmEtYm9vbGVhbi1jYXN0OiBcImVycm9yXCIqL1xuXG5mdW5jdGlvbiAqZm9vKCkge1xuICB5aWVsZCEhYSA/IGIgOiBjO1xufSIsIm9wdGlvbnMiOnsicGFyc2VyT3B0aW9ucyI6eyJlY21hVmVyc2lvbiI6Niwic291cmNlVHlwZSI6InNjcmlwdCIsImVjbWFGZWF0dXJlcyI6e319LCJydWxlcyI6eyJjb25zdHJ1Y3Rvci1zdXBlciI6MiwiZm9yLWRpcmVjdGlvbiI6MiwiZ2V0dGVyLXJldHVybiI6Miwibm8tYXN5bmMtcHJvbWlzZS1leGVjdXRvciI6Miwibm8tY2FzZS1kZWNsYXJhdGlvbnMiOjIsIm5vLWNsYXNzLWFzc2lnbiI6Miwibm8tY29tcGFyZS1uZWctemVybyI6Miwibm8tY29uZC1hc3NpZ24iOjIsIm5vLWNvbnN0LWFzc2lnbiI6Miwibm8tY29uc3RhbnQtY29uZGl0aW9uIjoyLCJuby1jb250cm9sLXJlZ2V4IjoyLCJuby1kZWJ1Z2dlciI6Miwibm8tZGVsZXRlLXZhciI6Miwibm8tZHVwZS1hcmdzIjoyLCJuby1kdXBlLWNsYXNzLW1lbWJlcnMiOjIsIm5vLWR1cGUta2V5cyI6Miwibm8tZHVwbGljYXRlLWNhc2UiOjIsIm5vLWVtcHR5IjoyLCJuby1lbXB0eS1jaGFyYWN0ZXItY2xhc3MiOjIsIm5vLWVtcHR5LXBhdHRlcm4iOjIsIm5vLWV4LWFzc2lnbiI6Miwibm8tZXh0cmEtYm9vbGVhbi1jYXN0IjoyLCJuby1leHRyYS1zZW1pIjoyLCJuby1mYWxsdGhyb3VnaCI6Miwibm8tZnVuYy1hc3NpZ24iOjIsIm5vLWdsb2JhbC1hc3NpZ24iOjIsIm5vLWlubmVyLWRlY2xhcmF0aW9ucyI6Miwibm8taW52YWxpZC1yZWdleHAiOjIsIm5vLWlycmVndWxhci13aGl0ZXNwYWNlIjoyLCJuby1taXNsZWFkaW5nLWNoYXJhY3Rlci1jbGFzcyI6Miwibm8tbWl4ZWQtc3BhY2VzLWFuZC10YWJzIjoyLCJuby1uZXctc3ltYm9sIjoyLCJuby1vYmotY2FsbHMiOjIsIm5vLW9jdGFsIjoyLCJuby1wcm90b3R5cGUtYnVpbHRpbnMiOjIsIm5vLXJlZGVjbGFyZSI6Miwibm8tcmVnZXgtc3BhY2VzIjoyLCJuby1zZWxmLWFzc2lnbiI6Miwibm8tc2hhZG93LXJlc3RyaWN0ZWQtbmFtZXMiOjIsIm5vLXNwYXJzZS1hcnJheXMiOjIsIm5vLXRoaXMtYmVmb3JlLXN1cGVyIjoyLCJuby11bmV4cGVjdGVkLW11bHRpbGluZSI6Miwibm8tdW5yZWFjaGFibGUiOjIsIm5vLXVuc2FmZS1maW5hbGx5IjoyLCJuby11bnNhZmUtbmVnYXRpb24iOjIsIm5vLXVudXNlZC1sYWJlbHMiOjIsIm5vLXVzZWxlc3MtY2F0Y2giOjIsIm5vLXVzZWxlc3MtZXNjYXBlIjoyLCJuby13aXRoIjoyLCJyZXF1aXJlLWF0b21pYy11cGRhdGVzIjoyLCJyZXF1aXJlLXlpZWxkIjoyLCJ1c2UtaXNuYW4iOjIsInZhbGlkLXR5cGVvZiI6Mn0sImVudiI6e319fQ==)

```js
/*eslint no-extra-boolean-cast: "error"*/

function *foo() {
  yield!!a ? b : c;
}
```

**What did you expect to happen?**

Fix to:

```js
/*eslint no-extra-boolean-cast: "error"*/

function *foo() {
  yield a ? b : c;
}
```

**What actually happened? Please include the actual, raw output from ESLint.**

Fixed to:

```js
/*eslint no-extra-boolean-cast: "error"*/

function *foo() {
  yielda ? b : c;
}
```

<!--
    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)**

Fixed the fixer to check can the tokens be adjacent.

**Is there anything you'd like reviewers to focus on?**

Only these tests were failing, others are regression tests:

```js
function *foo() { yield!!a ? b : c }
function *foo() { yield!! a ? b : c }
function *foo() { yield! !a ? b : c }
```


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 000000000..0667729f8
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,63 @@
+name: CI
+on:
+  push:
+    branches: [master]
+  pull_request:
+    branches: [master]
+
+jobs:
+  verify_files:
+    name: Verify Files
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v1
+    - uses: actions/setup-node@v1
+    - name: Install Packages
+      run: npm install
+    - name: Lint Files
+      run: node Makefile lint
+    - name: Check Rule Files
+      run: node Makefile checkRuleFiles
+    - name: Check Licenses
+      run: node Makefile checkLicenses
+
+  test_on_node:
+    name: Test
+    strategy:
+      matrix:
+        os: [ubuntu-latest, windows-latest, macOS-latest]
+        node: [8.x, 10.x, 12.x]
+        exclude:
+        - os: windows-latest
+          node: 8.x
+        - os: windows-latest
+          node: 10.x
+        - os: macOS-latest
+          node: 8.x
+        - os: macOS-latest
+          node: 10.x
+    runs-on: ${{ matrix.os }}
+    steps:
+    - uses: actions/checkout@v1
+    - uses: actions/setup-node@v1
+      with:
+        version: ${{ matrix.node }}
+    - name: Install Packages
+      run: npm install
+    - name: Test
+      run: node Makefile mocha
+    - name: Fuzz Test
+      run: node Makefile fuzz
+
+  test_on_browser:
+    name: Browser Test
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v1
+    - uses: actions/setup-node@v1
+    - name: Install Packages
+      run: npm install
+    - name: Test
+      run: node Makefile karma
+    - name: Fuzz Test
+      run: node Makefile fuzz
diff --git a/Makefile.js b/Makefile.js
index 02386d2f4..a01d13bde 100644
--- a/Makefile.js
+++ b/Makefile.js
@@ -497,7 +497,7 @@ target.lint = function() {
     }
 };
 
-target.fuzz = function({ amount = process.env.CI ? 1000 : 300, fuzzBrokenAutofixes = true } = {}) {
+target.fuzz = function({ amount = 1000, fuzzBrokenAutofixes = false } = {}) {
     const fuzzerRunner = require("./tools/fuzzer-runner");
     const fuzzResults = fuzzerRunner.run({ amount, fuzzBrokenAutofixes });
 
@@ -536,18 +536,13 @@ target.fuzz = function({ amount = process.env.CI ? 1000 : 300, fuzzBrokenAutofix
     }
 };
 
-target.test = function() {
-    target.lint();
-    target.checkRuleFiles();
+target.mocha = () => {
     let errors = 0,
         lastReturn;
 
     echo("Running unit tests");
 
-    // In CI (Azure Pipelines), use JUnit reporter.
-    const reporter = process.env.TF_BUILD ? "mocha-junit-reporter" : "progress";
-
-    lastReturn = exec(`${getBinFile("nyc")} -- ${MOCHA} -R ${reporter} -t ${MOCHA_TIMEOUT} -c ${TEST_FILES}`);
+    lastReturn = exec(`${getBinFile("nyc")} -- ${MOCHA} -R progress -t ${MOCHA_TIMEOUT} -c ${TEST_FILES}`);
     if (lastReturn.code !== 0) {
         errors++;
     }
@@ -557,6 +552,14 @@ target.test = function() {
         errors++;
     }
 
+    if (errors) {
+        exit(1);
+    }
+};
+
+target.karma = () => {
+    echo("Running unit tests on browsers");
+
     target.webpack();
 
     const browserFileLintOutput = new CLIEngine({
@@ -569,20 +572,22 @@ target.test = function() {
     if (browserFileLintOutput.errorCount > 0) {
         echo(`error: Failed to lint ${BUILD_DIR}/eslint.js as ES5 code`);
         echo(CLIEngine.getFormatter("stylish")(browserFileLintOutput.results));
-        errors++;
+        exit(1);
     }
 
-    lastReturn = exec(`${getBinFile("karma")} start karma.conf.js`);
-    if (lastReturn.code !== 0) {
-        errors++;
-    }
+    const lastReturn = exec(`${getBinFile("karma")} start karma.conf.js`);
 
-    if (errors) {
+    if (lastReturn.code !== 0) {
         exit(1);
     }
+};
 
+target.test = function() {
+    target.lint();
+    target.checkRuleFiles();
+    target.mocha();
+    target.karma();
     target.fuzz({ amount: 150, fuzzBrokenAutofixes: false });
-
     target.checkLicenses();
 };
 
diff --git a/README.md b/README.md
index 7ed0416c3..134c991d8 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,6 @@
 [![NPM version][npm-image]][npm-url]
 [![Build Status](https://dev.azure.com/eslint/eslint/_apis/build/status/eslint.eslint?branchName=master)](https://dev.azure.com/eslint/eslint/_build/latest?definitionId=1&branchName=master)
+[![Build Status](https://github.com/eslint/eslint/workflows/CI/badge.svg)](https://github.com/eslint/eslint/actions)
 [![Downloads][downloads-image]][downloads-url]
 [![Bountysource](https://www.bountysource.com/badge/tracker?tracker_id=282608)](https://www.bountysource.com/trackers/282608-eslint?utm_source=282608&utm_medium=shield&utm_campaign=TRACKER_BADGE)
 [![Join the chat at https://gitter.im/eslint/eslint](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/eslint/eslint?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
diff --git a/docs/rules/no-unsafe-negation.md b/docs/rules/no-unsafe-negation.md
index 8e1428ea9..880e9f644 100644
--- a/docs/rules/no-unsafe-negation.md
+++ b/docs/rules/no-unsafe-negation.md
@@ -39,10 +39,36 @@ if (!(key in object)) {
 if (!(obj instanceof Ctor)) {
     // obj is not an instance of Ctor
 }
+```
+
+### Exception
+
+For rare situations when negating the left operand is intended, this rule allows an exception.
+If the whole negation is explicitly wrapped in parentheses, the rule will not report a problem.
+
+Examples of **correct** code for this rule:
+
+```js
+/*eslint no-unsafe-negation: "error"*/
+
+if ((!foo) in object) {
+    // allowed, because the negation is explicitly wrapped in parentheses
+    // it is equivalent to (foo ? "false" : "true") in object
+    // this is allowed as an exception for rare situations when that is the intended meaning
+}
+
+if(("" + !foo) in object) {
+    // you can also make the intention more explicit, with type conversion
+}
+```
+
+Examples of **incorrect** code for this rule:
+
+```js
+/*eslint no-unsafe-negation: "error"*/
 
-if(("" + !key) in object) {
-    // make operator precedence and type conversion explicit
-    // in a rare situation when that is the intended meaning
+if (!(foo) in object) {
+    // this is not an allowed exception
 }
 ```
 
diff --git a/docs/rules/no-useless-constructor.md b/docs/rules/no-useless-constructor.md
index dbc0d8bce..cdfe5052f 100644
--- a/docs/rules/no-useless-constructor.md
+++ b/docs/rules/no-useless-constructor.md
@@ -8,7 +8,7 @@ class A {
     }
 }
 
-class A extends B {
+class B extends A {
     constructor (value) {
       super(value);
     }
@@ -32,7 +32,7 @@ class A {
     }
 }
 
-class A extends B {
+class B extends A {
     constructor (...args) {
       super(...args);
     }
@@ -52,13 +52,13 @@ class A {
     }
 }
 
-class A extends B {
+class B extends A {
     constructor() {
         super('foo');
     }
 }
 
-class A extends B {
+class B extends A {
     constructor() {
         super();
         doSomething();
diff --git a/lib/init/config-initializer.js b/lib/init/config-initializer.js
index 17aa96505..2e47e902c 100644
--- a/lib/init/config-initializer.js
+++ b/lib/init/config-initializer.js
@@ -120,6 +120,12 @@ function getModulesList(config, installESLint) {
         }
     }
 
+    const parser = config.parser || (config.parserOptions && config.parserOptions.parser);
+
+    if (parser) {
+        modules[parser] = "latest";
+    }
+
     if (installESLint === false) {
         delete modules.eslint;
     } else {
@@ -291,6 +297,20 @@ function processAnswers(answers) {
         config.extends.push("plugin:vue/essential");
     }
 
+    if (answers.typescript) {
+        if (answers.framework === "vue") {
+            config.parserOptions.parser = "@typescript-eslint/parser";
+        } else {
+            config.parser = "@typescript-eslint/parser";
+        }
+
+        if (Array.isArray(config.plugins)) {
+            config.plugins.push("@typescript-eslint");
+        } else {
+            config.plugins = ["@typescript-eslint"];
+        }
+    }
+
     // setup rules based on problems/style enforcement preferences
     if (answers.purpose === "problems") {
         config.extends.unshift("eslint:recommended");
@@ -306,6 +326,9 @@ function processAnswers(answers) {
             config = autoconfig.extendFromRecommended(config);
         }
     }
+    if (answers.typescript && config.extends.includes("eslint:recommended")) {
+        config.extends.push("plugin:@typescript-eslint/eslint-recommended");
+    }
 
     // normalize extends
     if (config.extends.length === 0) {
@@ -465,6 +488,12 @@ function promptUser() {
                 { name: "None of these", value: "none" }
             ]
         },
+        {
+            type: "confirm",
+            name: "typescript",
+            message: "Does your project use TypeScript?",
+            default: false
+        },
         {
             type: "checkbox",
             name: "env",
diff --git a/lib/rules/func-name-matching.js b/lib/rules/func-name-matching.js
index 3c4ee510d..83430ffad 100644
--- a/lib/rules/func-name-matching.js
+++ b/lib/rules/func-name-matching.js
@@ -118,6 +118,7 @@ module.exports = {
                 return false;
             }
             return node.type === "CallExpression" &&
+                node.callee.type === "MemberExpression" &&
                 node.callee.object.name === objName &&
                 node.callee.property.name === funcName;
         }
diff --git a/lib/rules/function-paren-newline.js b/lib/rules/function-paren-newline.js
index c9f09fdef..894c8e331 100644
--- a/lib/rules/function-paren-newline.js
+++ b/lib/rules/function-paren-newline.js
@@ -51,8 +51,8 @@ module.exports = {
             expectedBefore: "Expected newline before ')'.",
             expectedAfter: "Expected newline after '('.",
             expectedBetween: "Expected newline between arguments/params.",
-            unexpectedBefore: "Unexpected newline before '('.",
-            unexpectedAfter: "Unexpected newline after ')'."
+            unexpectedBefore: "Unexpected newline before ')'.",
+            unexpectedAfter: "Unexpected newline after '('."
         }
     },
 
diff --git a/lib/rules/no-extra-boolean-cast.js b/lib/rules/no-extra-boolean-cast.js
index 9ae9b5be6..9bbd5546e 100644
--- a/lib/rules/no-extra-boolean-cast.js
+++ b/lib/rules/no-extra-boolean-cast.js
@@ -102,7 +102,17 @@ module.exports = {
                             if (hasCommentsInside(parent)) {
                                 return null;
                             }
-                            return fixer.replaceText(parent, sourceCode.getText(node.argument));
+
+                            let prefix = "";
+                            const tokenBefore = sourceCode.getTokenBefore(parent);
+                            const firstReplacementToken = sourceCode.getFirstToken(node.argument);
+
+                            if (tokenBefore && tokenBefore.range[1] === parent.range[0] &&
+                                    !astUtils.canTokensBeAdjacent(tokenBefore, firstReplacementToken)) {
+                                prefix = " ";
+                            }
+
+                            return fixer.replaceText(parent, prefix + sourceCode.getText(node.argument));
                         }
                     });
                 }
diff --git a/lib/rules/no-self-assign.js b/lib/rules/no-self-assign.js
index 79fb50770..dc8bf9014 100644
--- a/lib/rules/no-self-assign.js
+++ b/lib/rules/no-self-assign.js
@@ -94,9 +94,19 @@ function eachSelfAssignment(left, right, props, report) {
         const end = Math.min(left.elements.length, right.elements.length);
 
         for (let i = 0; i < end; ++i) {
+            const leftElement = left.elements[i];
             const rightElement = right.elements[i];
 
-            eachSelfAssignment(left.elements[i], rightElement, props, report);
+            // Avoid cases such as [...a] = [...a, 1]
+            if (
+                leftElement &&
+                leftElement.type === "RestElement" &&
+                i < right.elements.length - 1
+            ) {
+                break;
+            }
+
+            eachSelfAssignment(leftElement, rightElement, props, report);
 
             // After a spread element, those indices are unknown.
             if (rightElement && rightElement.type === "SpreadElement") {
diff --git a/templates/blogpost.md.ejs b/templates/blogpost.md.ejs
index bbaa09fd6..a812a79d9 100644
--- a/templates/blogpost.md.ejs
+++ b/templates/blogpost.md.ejs
@@ -14,7 +14,7 @@ const RULE_REGEX = new RegExp(`\`?\\b(${ruleList.join("|")})\\b\`?`, "g");
 
 function linkify(line) {
     return line
-        .replace(/#(\d+)/g, "[#$1](https://github.com/eslint/eslint/issues/$1)")
+        .replace(/([^\s/]+[/][^\s/]+)?#(\d+)/g, (_, repo, num) => `[${repo || ""}#${num}](https://github.com/${repo || "eslint/eslint"}/issues/${num})`)
         .replace(RULE_REGEX, "[$&](/docs/rules/$1)");
 }
 
diff --git a/tools/eslint-fuzzer.js b/tools/eslint-fuzzer.js
index dce2b2352..dc0a65a8e 100644
--- a/tools/eslint-fuzzer.js
+++ b/tools/eslint-fuzzer.js
@@ -129,7 +129,7 @@ function fuzz(options) {
         const text = codeGenerator({ sourceType });
         const config = {
             rules: lodash.mapValues(ruleConfigs, lodash.sample),
-            parserOptions: { sourceType, ecmaVersion: 2018 }
+            parserOptions: { sourceType, ecmaVersion: 2020 }
         };
 
         let autofixResult;


Test patch:
diff --git a/tests/lib/init/config-initializer.js b/tests/lib/init/config-initializer.js
index 4ee6ff6b7..ed2388fd5 100644
--- a/tests/lib/init/config-initializer.js
+++ b/tests/lib/init/config-initializer.js
@@ -168,6 +168,24 @@ describe("configInitializer", () => {
                 assert.deepStrictEqual(config.extends, ["eslint:recommended", "plugin:vue/essential"]);
             });
 
+            it("should enable typescript parser and plugin", () => {
+                answers.typescript = true;
+                const config = init.processAnswers(answers);
+
+                assert.strictEqual(config.parser, "@typescript-eslint/parser");
+                assert.deepStrictEqual(config.plugins, ["@typescript-eslint"]);
+                assert.deepStrictEqual(config.extends, ["eslint:recommended", "plugin:@typescript-eslint/eslint-recommended"]);
+            });
+
+            it("should enable typescript parser and plugin with vue", () => {
+                answers.framework = "vue";
+                answers.typescript = true;
+                const config = init.processAnswers(answers);
+
+                assert.strictEqual(config.parserOptions.parser, "@typescript-eslint/parser");
+                assert.deepStrictEqual(config.plugins, ["vue", "@typescript-eslint"]);
+            });
+
             it("should extend eslint:recommended", () => {
                 const config = init.processAnswers(answers);
 
@@ -317,6 +335,28 @@ describe("configInitializer", () => {
                 assert.include(modules, "eslint-plugin-vue@latest");
                 assert.include(modules, "eslint-config-standard@latest");
             });
+
+            it("should support custom parser", () => {
+                const config = {
+                    parser: "@typescript-eslint/parser"
+                };
+                const modules = init.getModulesList(config);
+
+                assert.include(modules, "@typescript-eslint/parser@latest");
+            });
+
+            it("should support custom parser with Vue.js", () => {
+                const config = {
+
+                    // We should declare the parser at `parserOptions` when using with `eslint-plugin-vue`.
+                    parserOptions: {
+                        parser: "@typescript-eslint/parser"
+                    }
+                };
+                const modules = init.getModulesList(config);
+
+                assert.include(modules, "@typescript-eslint/parser@latest");
+            });
         });
 
         describe("auto", () => {
diff --git a/tests/lib/rules/callback-return.js b/tests/lib/rules/callback-return.js
index 1d5e29a63..1f17ef6cc 100644
--- a/tests/lib/rules/callback-return.js
+++ b/tests/lib/rules/callback-return.js
@@ -159,7 +159,7 @@ ruleTester.run("callback-return", rule, {
                 messageId: "missingReturn",
                 line: 1,
                 column: 30,
-                nodeType: "CallExpression"
+                type: "CallExpression"
             }]
         },
         {
@@ -168,7 +168,7 @@ ruleTester.run("callback-return", rule, {
                 messageId: "missingReturn",
                 line: 1,
                 column: 63,
-                nodeType: "CallExpression"
+                type: "CallExpression"
             }]
         },
         {
@@ -177,7 +177,7 @@ ruleTester.run("callback-return", rule, {
                 messageId: "missingReturn",
                 line: 1,
                 column: 61,
-                nodeType: "CallExpression"
+                type: "CallExpression"
             }]
         },
         {
@@ -186,7 +186,7 @@ ruleTester.run("callback-return", rule, {
                 messageId: "missingReturn",
                 line: 1,
                 column: 35,
-                nodeType: "CallExpression"
+                type: "CallExpression"
             }]
         },
         {
@@ -196,7 +196,7 @@ ruleTester.run("callback-return", rule, {
                 messageId: "missingReturn",
                 line: 1,
                 column: 31,
-                nodeType: "CallExpression"
+                type: "CallExpression"
             }]
         },
         {
@@ -206,7 +206,7 @@ ruleTester.run("callback-return", rule, {
                 messageId: "missingReturn",
                 line: 1,
                 column: 31,
-                nodeType: "CallExpression"
+                type: "CallExpression"
             }]
         },
         {
@@ -215,7 +215,7 @@ ruleTester.run("callback-return", rule, {
                 messageId: "missingReturn",
                 line: 3,
                 column: 2,
-                nodeType: "CallExpression"
+                type: "CallExpression"
             }]
         },
         {
@@ -225,7 +225,7 @@ ruleTester.run("callback-return", rule, {
                 messageId: "missingReturn",
                 line: 1,
                 column: 43,
-                nodeType: "CallExpression"
+                type: "CallExpression"
             }]
         },
         {
@@ -234,7 +234,7 @@ ruleTester.run("callback-return", rule, {
                 messageId: "missingReturn",
                 line: 1,
                 column: 19,
-                nodeType: "CallExpression"
+                type: "CallExpression"
             }]
         },
         {
@@ -243,7 +243,7 @@ ruleTester.run("callback-return", rule, {
                 messageId: "missingReturn",
                 line: 1,
                 column: 19,
-                nodeType: "CallExpression"
+                type: "CallExpression"
             }]
         },
         {
@@ -252,7 +252,7 @@ ruleTester.run("callback-return", rule, {
                 messageId: "missingReturn",
                 line: 1,
                 column: 30,
-                nodeType: "CallExpression"
+                type: "CallExpression"
             }]
         },
         {
@@ -262,7 +262,7 @@ ruleTester.run("callback-return", rule, {
                 messageId: "missingReturn",
                 line: 1,
                 column: 20,
-                nodeType: "CallExpression"
+                type: "CallExpression"
             }]
         },
         {
@@ -271,7 +271,7 @@ ruleTester.run("callback-return", rule, {
                 messageId: "missingReturn",
                 line: 1,
                 column: 30,
-                nodeType: "CallExpression"
+                type: "CallExpression"
             }]
         },
         {
@@ -280,7 +280,7 @@ ruleTester.run("callback-return", rule, {
                 messageId: "missingReturn",
                 line: 3,
                 column: 1,
-                nodeType: "CallExpression"
+                type: "CallExpression"
 
             }]
         },
@@ -291,7 +291,7 @@ ruleTester.run("callback-return", rule, {
                 messageId: "missingReturn",
                 line: 1,
                 column: 32,
-                nodeType: "CallExpression"
+                type: "CallExpression"
             }]
         },
 
@@ -303,12 +303,12 @@ ruleTester.run("callback-return", rule, {
                 messageId: "missingReturn",
                 line: 1,
                 column: 30,
-                nodeType: "CallExpression"
+                type: "CallExpression"
             }, {
                 messageId: "missingReturn",
                 line: 1,
                 column: 50,
-                nodeType: "CallExpression"
+                type: "CallExpression"
             }]
         },
         {
@@ -318,7 +318,7 @@ ruleTester.run("callback-return", rule, {
                     messageId: "missingReturn",
                     line: 1,
                     column: 52,
-                    nodeType: "CallExpression"
+                    type: "CallExpression"
                 }
             ]
         },
@@ -330,7 +330,7 @@ ruleTester.run("callback-return", rule, {
                     messageId: "missingReturn",
                     line: 1,
                     column: 18,
-                    nodeType: "CallExpression"
+                    type: "CallExpression"
                 }
             ]
         },
@@ -341,7 +341,7 @@ ruleTester.run("callback-return", rule, {
                     messageId: "missingReturn",
                     line: 1,
                     column: 42,
-                    nodeType: "CallExpression"
+                    type: "CallExpression"
                 }
             ]
         },
@@ -353,7 +353,7 @@ ruleTester.run("callback-return", rule, {
                     messageId: "missingReturn",
                     line: 1,
                     column: 42,
-                    nodeType: "CallExpression"
+                    type: "CallExpression"
                 }
             ]
         },
@@ -367,7 +367,7 @@ ruleTester.run("callback-return", rule, {
                     messageId: "missingReturn",
                     line: 1,
                     column: 33,
-                    nodeType: "CallExpression"
+                    type: "CallExpression"
                 }
             ]
         },
@@ -379,7 +379,7 @@ ruleTester.run("callback-return", rule, {
                     messageId: "missingReturn",
                     line: 1,
                     column: 47,
-                    nodeType: "CallExpression"
+                    type: "CallExpression"
                 }
             ]
         },
@@ -391,7 +391,7 @@ ruleTester.run("callback-return", rule, {
                     messageId: "missingReturn",
                     line: 1,
                     column: 51,
-                    nodeType: "CallExpression"
+                    type: "CallExpression"
                 }
             ]
         },
@@ -403,7 +403,7 @@ ruleTester.run("callback-return", rule, {
                     messageId: "missingReturn",
                     line: 1,
                     column: 30,
-                    nodeType: "CallExpression"
+                    type: "CallExpression"
                 }
             ]
         },
@@ -415,7 +415,7 @@ ruleTester.run("callback-return", rule, {
                     messageId: "missingReturn",
                     line: 1,
                     column: 30,
-                    nodeType: "CallExpression"
+                    type: "CallExpression"
                 }
             ]
         },
@@ -427,7 +427,7 @@ ruleTester.run("callback-return", rule, {
                     messageId: "missingReturn",
                     line: 1,
                     column: 30,
-                    nodeType: "CallExpression"
+                    type: "CallExpression"
                 }
             ]
         },
@@ -439,7 +439,7 @@ ruleTester.run("callback-return", rule, {
                     messageId: "missingReturn",
                     line: 1,
                     column: 41,
-                    nodeType: "CallExpression"
+                    type: "CallExpression"
                 }
             ]
         },
@@ -451,7 +451,7 @@ ruleTester.run("callback-return", rule, {
                     messageId: "missingReturn",
                     line: 2,
                     column: 1,
-                    nodeType: "CallExpression"
+                    type: "CallExpression"
                 }
             ]
         },
@@ -463,7 +463,7 @@ ruleTester.run("callback-return", rule, {
                     messageId: "missingReturn",
                     line: 1,
                     column: 30,
-                    nodeType: "CallExpression"
+                    type: "CallExpression"
                 }
             ]
         },
@@ -475,7 +475,7 @@ ruleTester.run("callback-return", rule, {
                     messageId: "missingReturn",
                     line: 1,
                     column: 30,
-                    nodeType: "CallExpression"
+                    type: "CallExpression"
                 }
             ]
         }
diff --git a/tests/lib/rules/func-name-matching.js b/tests/lib/rules/func-name-matching.js
index efb7987d4..72ee66b41 100644
--- a/tests/lib/rules/func-name-matching.js
+++ b/tests/lib/rules/func-name-matching.js
@@ -255,6 +255,10 @@ ruleTester.run("func-name-matching", rule, {
             code: "Reflect.defineProperty(foo, 'bar', { value() {} })",
             options: ["never", { considerPropertyDescriptor: true }],
             parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "foo({ value: function value() {} })",
+            options: ["always", { considerPropertyDescriptor: true }]
         }
     ],
     invalid: [
@@ -446,6 +450,13 @@ ruleTester.run("func-name-matching", rule, {
             errors: [
                 { messageId: "notMatchProperty", data: { funcName: "bar", name: "bar" } }
             ]
+        },
+        {
+            code: "foo({ value: function bar() {} })",
+            options: ["always", { considerPropertyDescriptor: true }],
+            errors: [
+                { messageId: "matchProperty", data: { funcName: "bar", name: "value" } }
+            ]
         }
     ]
 });
diff --git a/tests/lib/rules/no-extra-boolean-cast.js b/tests/lib/rules/no-extra-boolean-cast.js
index 129329ff0..1de914ca5 100644
--- a/tests/lib/rules/no-extra-boolean-cast.js
+++ b/tests/lib/rules/no-extra-boolean-cast.js
@@ -270,6 +270,68 @@ ruleTester.run("no-extra-boolean-cast", rule, {
         },
 
         // Adjacent tokens tests
+        {
+            code: "function *foo() { yield!!a ? b : c }",
+            output: "function *foo() { yield a ? b : c }",
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{
+                messageId: "unexpectedNegation",
+                type: "UnaryExpression"
+            }]
+        },
+        {
+            code: "function *foo() { yield!! a ? b : c }",
+            output: "function *foo() { yield a ? b : c }",
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{
+                messageId: "unexpectedNegation",
+                type: "UnaryExpression"
+            }]
+        },
+        {
+            code: "function *foo() { yield! !a ? b : c }",
+            output: "function *foo() { yield a ? b : c }",
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{
+                messageId: "unexpectedNegation",
+                type: "UnaryExpression"
+            }]
+        },
+        {
+            code: "function *foo() { yield !!a ? b : c }",
+            output: "function *foo() { yield a ? b : c }",
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{
+                messageId: "unexpectedNegation",
+                type: "UnaryExpression"
+            }]
+        },
+        {
+            code: "function *foo() { yield(!!a) ? b : c }",
+            output: "function *foo() { yield(a) ? b : c }",
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{
+                messageId: "unexpectedNegation",
+                type: "UnaryExpression"
+            }]
+        },
+        {
+            code: "function *foo() { yield/**/!!a ? b : c }",
+            output: "function *foo() { yield/**/a ? b : c }",
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{
+                messageId: "unexpectedNegation",
+                type: "UnaryExpression"
+            }]
+        },
+        {
+            code: "x=!!a ? b : c ",
+            output: "x=a ? b : c ",
+            errors: [{
+                messageId: "unexpectedNegation",
+                type: "UnaryExpression"
+            }]
+        },
         {
             code: "void!Boolean()",
             output: "void true",
diff --git a/tests/lib/rules/no-extra-semi.js b/tests/lib/rules/no-extra-semi.js
index f9e103bb5..913290aaf 100644
--- a/tests/lib/rules/no-extra-semi.js
+++ b/tests/lib/rules/no-extra-semi.js
@@ -47,68 +47,68 @@ ruleTester.run("no-extra-semi", rule, {
         {
             code: "var x = 5;;",
             output: "var x = 5;",
-            errors: [{ messgeId: "unexpected", type: "EmptyStatement" }]
+            errors: [{ messageId: "unexpected", type: "EmptyStatement" }]
         },
         {
             code: "function foo(){};",
             output: "function foo(){}",
-            errors: [{ messgeId: "unexpected", type: "EmptyStatement" }]
+            errors: [{ messageId: "unexpected", type: "EmptyStatement" }]
         },
         {
             code: "for(;;);;",
             output: "for(;;);",
-            errors: [{ messgeId: "unexpected", type: "EmptyStatement" }]
+            errors: [{ messageId: "unexpected", type: "EmptyStatement" }]
         },
         {
             code: "while(0);;",
             output: "while(0);",
-            errors: [{ messgeId: "unexpected", type: "EmptyStatement" }]
+            errors: [{ messageId: "unexpected", type: "EmptyStatement" }]
         },
         {
             code: "do;while(0);;",
             output: "do;while(0);",
-            errors: [{ messgeId: "unexpected", type: "EmptyStatement" }]
+            errors: [{ messageId: "unexpected", type: "EmptyStatement" }]
         },
         {
             code: "for(a in b);;",
             output: "for(a in b);",
-            errors: [{ messgeId: "unexpected", type: "EmptyStatement" }]
+            errors: [{ messageId: "unexpected", type: "EmptyStatement" }]
         },
         {
             code: "for(a of b);;",
             output: "for(a of b);",
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ messgeId: "unexpected", type: "EmptyStatement" }]
+            errors: [{ messageId: "unexpected", type: "EmptyStatement" }]
         },
         {
             code: "if(true);;",
             output: "if(true);",
-            errors: [{ messgeId: "unexpected", type: "EmptyStatement" }]
+            errors: [{ messageId: "unexpected", type: "EmptyStatement" }]
         },
         {
             code: "if(true){} else;;",
             output: "if(true){} else;",
-            errors: [{ messgeId: "unexpected", type: "EmptyStatement" }]
+            errors: [{ messageId: "unexpected", type: "EmptyStatement" }]
         },
         {
             code: "if(true){;} else {;}",
             output: "if(true){} else {}",
-            errors: [{ messgeId: "unexpected", type: "EmptyStatement" }, { messgeId: "unexpected", type: "EmptyStatement" }]
+            errors: [{ messageId: "unexpected", type: "EmptyStatement" }, { messageId: "unexpected", type: "EmptyStatement" }]
         },
         {
             code: "foo:;;",
             output: "foo:;",
-            errors: [{ messgeId: "unexpected", type: "EmptyStatement" }]
+            errors: [{ messageId: "unexpected", type: "EmptyStatement" }]
         },
         {
             code: "with(foo);;",
             output: "with(foo);",
-            errors: [{ messgeId: "unexpected", type: "EmptyStatement" }]
+            errors: [{ messageId: "unexpected", type: "EmptyStatement" }]
         },
         {
             code: "with(foo){;}",
             output: "with(foo){}",
-            errors: [{ messgeId: "unexpected", type: "EmptyStatement" }]
+            errors: [{ messageId: "unexpected", type: "EmptyStatement" }]
         },
 
         // Class body.
@@ -116,47 +116,47 @@ ruleTester.run("no-extra-semi", rule, {
             code: "class A { ; }",
             output: "class A {  }",
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ messgeId: "unexpected", type: "Punctuator", column: 11 }]
+            errors: [{ messageId: "unexpected", type: "Punctuator", column: 11 }]
         },
         {
             code: "class A { /*a*/; }",
             output: "class A { /*a*/ }",
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ messgeId: "unexpected", type: "Punctuator", column: 16 }]
+            errors: [{ messageId: "unexpected", type: "Punctuator", column: 16 }]
         },
         {
             code: "class A { ; a() {} }",
             output: "class A {  a() {} }",
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ messgeId: "unexpected", type: "Punctuator", column: 11 }]
+            errors: [{ messageId: "unexpected", type: "Punctuator", column: 11 }]
         },
         {
             code: "class A { a() {}; }",
             output: "class A { a() {} }",
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ messgeId: "unexpected", type: "Punctuator", column: 17 }]
+            errors: [{ messageId: "unexpected", type: "Punctuator", column: 17 }]
         },
         {
             code: "class A { a() {}; b() {} }",
             output: "class A { a() {} b() {} }",
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ messgeId: "unexpected", type: "Punctuator", column: 17 }]
+            errors: [{ messageId: "unexpected", type: "Punctuator", column: 17 }]
         },
         {
             code: "class A {; a() {}; b() {}; }",
             output: "class A { a() {} b() {} }",
             parserOptions: { ecmaVersion: 6 },
             errors: [
-                { messgeId: "unexpected", type: "Punctuator", column: 10 },
-                { messgeId: "unexpected", type: "Punctuator", column: 18 },
-                { messgeId: "unexpected", type: "Punctuator", column: 26 }
+                { messageId: "unexpected", type: "Punctuator", column: 10 },
+                { messageId: "unexpected", type: "Punctuator", column: 18 },
+                { messageId: "unexpected", type: "Punctuator", column: 26 }
             ]
         },
         {
             code: "class A { a() {}; get b() {} }",
             output: "class A { a() {} get b() {} }",
             parserOptions: { ecmaVersion: 6 },
-            errors: [{ messgeId: "unexpected", type: "Punctuator", column: 17 }]
+            errors: [{ messageId: "unexpected", type: "Punctuator", column: 17 }]
         }
     ]
 });
diff --git a/tests/lib/rules/no-inline-comments.js b/tests/lib/rules/no-inline-comments.js
index 916c83657..6af30d12b 100644
--- a/tests/lib/rules/no-inline-comments.js
+++ b/tests/lib/rules/no-inline-comments.js
@@ -17,11 +17,11 @@ const rule = require("../../../lib/rules/no-inline-comments"),
 
 const ruleTester = new RuleTester(),
     lineError = {
-        messsage: "Unexpected comment inline with code.",
+        message: "Unexpected comment inline with code.",
         type: "Line"
     },
     blockError = {
-        messsage: "Unexpected comment inline with code.",
+        message: "Unexpected comment inline with code.",
         type: "Block"
     };
 
diff --git a/tests/lib/rules/no-self-assign.js b/tests/lib/rules/no-self-assign.js
index a344e8084..fbf52dcba 100644
--- a/tests/lib/rules/no-self-assign.js
+++ b/tests/lib/rules/no-self-assign.js
@@ -32,6 +32,8 @@ ruleTester.run("no-self-assign", rule, {
         { code: "[a, b] = [b, a]", parserOptions: { ecmaVersion: 6 } },
         { code: "[a,, b] = [, b, a]", parserOptions: { ecmaVersion: 6 } },
         { code: "[x, a] = [...x, a]", parserOptions: { ecmaVersion: 6 } },
+        { code: "[...a] = [...a, 1]", parserOptions: { ecmaVersion: 6 } },
+        { code: "[a, ...b] = [0, ...b, 1]", parserOptions: { ecmaVersion: 6 } },
         { code: "[a, b] = {a, b}", parserOptions: { ecmaVersion: 6 } },
         { code: "({a} = a)", parserOptions: { ecmaVersion: 6 } },
         { code: "({a = 1} = {a})", parserOptions: { ecmaVersion: 6 } },
diff --git a/tests/lib/rules/space-infix-ops.js b/tests/lib/rules/space-infix-ops.js
index e94d6fe32..85d61d449 100644
--- a/tests/lib/rules/space-infix-ops.js
+++ b/tests/lib/rules/space-infix-ops.js
@@ -312,12 +312,12 @@ ruleTester.run("space-infix-ops", rule, {
                 message: "Operator '=' must be spaced.",
                 line: 1,
                 column: 7,
-                nodeType: "AssignmentPattern"
+                type: "AssignmentPattern"
             }, {
                 message: "Operator '=' must be spaced.",
                 line: 1,
                 column: 10,
-                nodeType: "VariableDeclarator"
+                type: "VariableDeclarator"
             }]
         },
         {
@@ -328,7 +328,7 @@ ruleTester.run("space-infix-ops", rule, {
                 message: "Operator '=' must be spaced.",
                 line: 1,
                 column: 15,
-                nodeType: "AssignmentPattern"
+                type: "AssignmentPattern"
             }]
         },
         {
@@ -339,7 +339,7 @@ ruleTester.run("space-infix-ops", rule, {
                 message: "Operator '**' must be spaced.",
                 line: 1,
                 column: 2,
-                nodeType: "BinaryExpression"
+                type: "BinaryExpression"
             }]
         },
         {
@@ -349,7 +349,7 @@ ruleTester.run("space-infix-ops", rule, {
                 message: "Operator 'in' must be spaced.",
                 line: 1,
                 column: 6,
-                nodeType: "BinaryExpression"
+                type: "BinaryExpression"
             }]
         },
         {
@@ -359,7 +359,7 @@ ruleTester.run("space-infix-ops", rule, {
                 message: "Operator 'instanceof' must be spaced.",
                 line: 1,
                 column: 6,
-                nodeType: "BinaryExpression"
+                type: "BinaryExpression"
             }]
         },
 
@@ -385,7 +385,7 @@ ruleTester.run("space-infix-ops", rule, {
                 message: "Operator '=' must be spaced.",
                 line: 1,
                 column: 23,
-                nodeType: "AssignmentPattern"
+                type: "AssignmentPattern"
             }]
         }
     ]
diff --git a/tests/lib/rules/spaced-comment.js b/tests/lib/rules/spaced-comment.js
index c4c729b2e..fd6b2e510 100644
--- a/tests/lib/rules/spaced-comment.js
+++ b/tests/lib/rules/spaced-comment.js
@@ -313,7 +313,7 @@ ruleTester.run("spaced-comment", rule, {
             output: "// An invalid comment NOT starting with space\nvar a = 1;",
             options: ["always"],
             errors: [{
-                messsage: "Expected space or tab after '//' in comment.",
+                message: "Expected space or tab after '//' in comment.",
                 type: "Line"
             }]
         },
diff --git a/tests/lib/rules/switch-colon-spacing.js b/tests/lib/rules/switch-colon-spacing.js
index dfaf357b7..8ceb3bdf7 100644
--- a/tests/lib/rules/switch-colon-spacing.js
+++ b/tests/lib/rules/switch-colon-spacing.js
@@ -18,10 +18,10 @@ const rule = require("../../../lib/rules/switch-colon-spacing"),
 
 const ruleTester = new RuleTester();
 
-const expectedBeforeError = { messagesId: "expectedBefore" };
-const expectedAfterError = { messagesId: "expectedAfter" };
-const unexpectedBeforeError = { messagesId: "unexpectedBefore" };
-const unexpectedAfterError = { messagesId: "unexpectedAfter" };
+const expectedBeforeError = { messageId: "expectedBefore" };
+const expectedAfterError = { messageId: "expectedAfter" };
+const unexpectedBeforeError = { messageId: "unexpectedBefore" };
+const unexpectedAfterError = { messageId: "unexpectedAfter" };
 
 ruleTester.run("switch-colon-spacing", rule, {
     valid: [