Repository: babel/minify (js)

Issue:
simplify's loop rewriting incorrectly removes preceding variable declarations
<!---
Thanks for filing an issue 😄! Before you submit, please read the following:

Search open/closed issues before submitting since someone might have asked the same thing before!

If you have a support request or question please submit them to the #minify channel on babeljs slack: https://babeljs.slack.com/messages/minify

Also have a look at the Debugging guidelines: https://github.com/babel/minify/blob/master/CONTRIBUTING.md#debugging

Things required:

For a bug:

1. Minimal code to reproduce the bug
2. Stack Trace if there is an Error thrown during minification
3. Configuration - babel-minify configuration or babelrc
4. Whether you used it with other presets/plugins - like es2015 or env
5. Environment: gulp, rollup, webpack, babel-cli, etc...?

-->

#### Input Code

```js
let foo;
while (0) {}
console.log(foo);
```

#### Actual Output
<!-- If an error is thrown, please provide the stack trace here -->

```js
'use strict';console.log(foo);
```



#### Details

Minimal repro in the sandbox [here](https://babeljs.io/repl/#?babili=true&browsers=&build=&builtIns=false&code_lz=DYUwLgBAZg9jEF4IHJY2QbgFAHcAWAlqBABQAMAlBAN4C-WAxjAHYDOMoAdMDAOYloKGIA&debug=false&forceAllTransforms=false&shippedProposals=false&circleciRepo=&evaluate=true&fileSize=false&lineWrap=false&presets=es2015%2Ces2016%2Ces2017%2Cstage-2%2Cstage-3%2Cbabili&prettier=false&targets=&version=6.26.0&envVersion=).

Passing `simplify: false` to `babel-preset-minify` resolves the issue.

It looks like what's happening is that the loop is rewritten to a for loop, then preceding statements are sometimes moved into the initialization segment of the for loop. But if the initialization segment is empty, or if the entire for loop is removed, then the declarations are lost. 

Note that this does not require the loop to be completely deleted, and that more than one preceding statement can be lost. Our original code that prompted this looks roughly like:

[Sandbox](https://babeljs.io/repl/#?babili=true&browsers=&build=&builtIns=false&code_lz=MYewdgzgLgBAZiEMC8MDkCRoNwChSSwBGAhgE4rqlk64Bu5MpAXpQI4CuAHgBQCUeAO4ALAJYAbAKY8WfGAG9cMJiVapOvAbgC--cBBBSAdOJABzHphgBqFWS1A&debug=false&forceAllTransforms=false&shippedProposals=false&circleciRepo=&evaluate=true&fileSize=false&lineWrap=false&presets=es2015%2Ces2016%2Ces2017%2Cstage-2%2Cstage-3%2Cbabili&prettier=false&targets=&version=6.26.0&envVersion=)
```js
const foo = 'foo';
const bar = 'bar';
let baz = qux();
while(baz) {
  baz = qux();
}
console.log(foo + bar);
```

And we're seeing the declarations for both foo and bar deleted.

Pull request:
fix(deadcode): account for impure paths inside for statements
+ fix #824 



Patch:
diff --git a/packages/babel-plugin-minify-dead-code-elimination/src/index.js b/packages/babel-plugin-minify-dead-code-elimination/src/index.js
index 448fa7d..7f2f6f6 100644
--- a/packages/babel-plugin-minify-dead-code-elimination/src/index.js
+++ b/packages/babel-plugin-minify-dead-code-elimination/src/index.js
@@ -678,7 +678,12 @@ module.exports = ({ types: t, traverse }) => {
         if (result.value) {
           test.remove();
         } else {
-          path.remove();
+          const init = path.get("init");
+          if (init.node && !init.isPure()) {
+            path.replaceWith(init);
+          } else {
+            path.remove();
+          }
         }
       }
     },


Test patch:
diff --git a/packages/babel-plugin-minify-dead-code-elimination/__tests__/fixtures/loops/actual.js b/packages/babel-plugin-minify-dead-code-elimination/__tests__/fixtures/loops/actual.js
index 89d57fd..bcf4a24 100644
--- a/packages/babel-plugin-minify-dead-code-elimination/__tests__/fixtures/loops/actual.js
+++ b/packages/babel-plugin-minify-dead-code-elimination/__tests__/fixtures/loops/actual.js
@@ -19,6 +19,10 @@ for (; true; ) {
 for (; false; ) {
   baz();
 }
+for (var j; false; ) {
+  blah();
+}
+for (0; false; ) {}
 
 // do_while
 do {
diff --git a/packages/babel-plugin-minify-dead-code-elimination/__tests__/fixtures/loops/expected.js b/packages/babel-plugin-minify-dead-code-elimination/__tests__/fixtures/loops/expected.js
index 4e95ee2..4215535 100644
--- a/packages/babel-plugin-minify-dead-code-elimination/__tests__/fixtures/loops/expected.js
+++ b/packages/babel-plugin-minify-dead-code-elimination/__tests__/fixtures/loops/expected.js
@@ -16,6 +16,8 @@ for (;;) {
   bar();
 }
 
+var j;
+
 // do_while
 do {
   foo();
diff --git a/packages/babel-preset-minify/__tests__/minify-env-tests.js b/packages/babel-preset-minify/__tests__/minify-env-tests.js
index 55f7d1c..b175e8b 100644
--- a/packages/babel-preset-minify/__tests__/minify-env-tests.js
+++ b/packages/babel-preset-minify/__tests__/minify-env-tests.js
@@ -183,4 +183,17 @@ describe("preset along with env", () => {
     })();
   `
   );
+
+  thePlugin(
+    "should fix issue#824 simplify + deadcode",
+    `
+      let foo;
+      while (0) {}
+      console.log(foo);
+    `,
+    `
+      var foo;
+      console.log(foo);
+    `
+  );
 });