Repository: eslint/eslint (js)

Issue:
`no-unreachable` reports EmptyStatements
Thank you for a wonderful tool.  Most times I think I find a bug, instead I learn a JS horror detail.

Can't figure this one out yet.  Don't think it's env related.

* ESLint - 4.3.0
* Node - 8.1.3
* NPM - 5.3.0
* Parser - babel-eslint
* Exhibits on Mac, Win10, Travis CI's debian

**Please show your full configuration:**

<details>
<summary>Configuration</summary>

<!-- Paste your configuration below: -->
```js
John-Haugeland:jssm johnhaugeland$ cat .eslintrc 
{

  "parser"  : "babel-eslint",
  "plugins" : [ ],
  "env"     : { "es6": true, "commonjs": true },

  "extends" : "eslint-config-stonecypher"

}
```

`eslint-config-stonecypher` in turn configures every current eslint rule, plus `eslint-plugin-` ... `flowtype`, `fp`, `jsdoc`, `ava`, `unicorn`, `new-with-error`, and `promise`

</details>

**What did you do? Please include the actual source code causing the issue.**

The following code triggers "no-unreachable" at the end of the switch, even though it's the last action in a default, ostensibly because of the `throw`.

<!-- Paste the source code below: -->
```js
const arrow_direction = arrow => {

  switch (arrow) {

    case '->': case '=>': case '~>':
      return 'right';

    case '<-': case '<=': case '<~':
      return 'left';

    case '<->': case '<-=>': case '<-~>':
    case '<=>': case '<=->': case '<=~>':
    case '<~>': case '<~->': case '<~=>':
      return 'both';

    default:
      throw new Error(`arrow_direction: unknown arrow type ${arrow}`);

  };

}
```

**What did you expect to happen?**
Nothing.  No code follows the `throw`.

This similar code does not trigger the warning, I expect because the throw is now outside the switch.

```javascript
const arrow_direction = arrow => {

  switch (arrow) {

    case '->': case '=>': case '~>':
      return 'right';

    case '<-': case '<=': case '<~':
      return 'left';

    case '<->': case '<-=>': case '<-~>':
    case '<=>': case '<=->': case '<=~>':
    case '<~>': case '<~->': case '<~=>':
      return 'both';

  };

  throw new Error(`arrow_direction: unknown arrow type ${arrow}`);

}
```

**What actually happened? Please include the actual, raw output from ESLint.**
```
/Users/johnhaugeland/projects/jssm/src/js/jssm.js
   22:7    warning  'arrow_direction' is assigned a value but never used  no-unused-vars
   22:7    warning  Missing "arrow_direction" variable type annotation    flowtype/require-variable-type
   22:25   warning  Missing return type annotation                        flowtype/require-return-type
   22:25   warning  Missing "arrow" parameter type annotation             flowtype/require-parameter-type
   44:4    warning  Unnecessary semicolon                                 no-extra-semi
   44:4    error    Unreachable code                                      no-unreachable
...
```

It's not like it's a big deal, or anything.  I moved the throws outside of the switch afterwards anyway, because in retrospect it's sorta gross.

It's just surprising, and I wonder whether it's legitimately a subtle bug.

Please have a nice day.

Pull request:
Fix: Ignore empty statements in no-unreachable (fixes #9081)
<!--
    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)**

Made `no-unreachable` ignore `EmptyStatement` nodes.

**Is there anything you'd like reviewers to focus on?**

Was this the correct approach?



Patch:
diff --git a/lib/rules/no-unreachable.js b/lib/rules/no-unreachable.js
index 5f90500a0..8ea2583f7 100644
--- a/lib/rules/no-unreachable.js
+++ b/lib/rules/no-unreachable.js
@@ -182,7 +182,6 @@ module.exports = {
             ContinueStatement: reportIfUnreachable,
             DebuggerStatement: reportIfUnreachable,
             DoWhileStatement: reportIfUnreachable,
-            EmptyStatement: reportIfUnreachable,
             ExpressionStatement: reportIfUnreachable,
             ForInStatement: reportIfUnreachable,
             ForOfStatement: reportIfUnreachable,


Test patch:
diff --git a/tests/lib/rules/no-unreachable.js b/tests/lib/rules/no-unreachable.js
index 54575cc00..1e04d687c 100644
--- a/tests/lib/rules/no-unreachable.js
+++ b/tests/lib/rules/no-unreachable.js
@@ -32,6 +32,13 @@ ruleTester.run("no-unreachable", rule, {
         "while (true) { if (true) break; var x = 1; }",
         "while (true) continue;",
         "switch (foo) { case 1: break; var x; }",
+        "switch (foo) { case 1: break; var x; default: throw true; };",
+        {
+            code: "const arrow_direction = arrow => {  switch (arrow) { default: throw new Error();  };}",
+            parserOptions: {
+                ecmaVersion: 6
+            }
+        },
         "var x = 1; y = 2; throw 'uh oh'; var y;",
         "function foo() { var x = 1; if (x) { return; } x = 2; }",
         "function foo() { var x = 1; if (x) { } else { return; } x = 2; }",
@@ -63,6 +70,20 @@ ruleTester.run("no-unreachable", rule, {
         { code: "function foo() { var x = 1; while (x) { if (x) break; else continue; x = 2; } }", errors: [{ message: "Unreachable code.", type: "ExpressionStatement" }] },
         { code: "function foo() { var x = 1; for (;;) { if (x) continue; } x = 2; }", errors: [{ message: "Unreachable code.", type: "ExpressionStatement" }] },
         { code: "function foo() { var x = 1; while (true) { } x = 2; }", errors: [{ message: "Unreachable code.", type: "ExpressionStatement" }] },
+        {
+            code: "const arrow_direction = arrow => {  switch (arrow) { default: throw new Error();  }; g() }",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                {
+                    message: "Unreachable code.",
+                    type: "ExpressionStatement",
+                    line: 1,
+                    column: 86,
+                    endLine: 1,
+                    endColumn: 89
+                }
+            ]
+        },
 
         // Merge the warnings of continuous unreachable nodes.
         {