Repository: mishoo/UglifyJS (JavaScript)

Issue:
ufuzz failure
```js
// original code
// (beautified)
var _calls_ = 10, a = 100, b = 10, c = 0;

{
    var expr1 = b += a;
    for (var key1 in expr1) {
        c = 1 + c;
        var a_2 = expr1[key1];
    }
}

var b = function() {}, a_2 = typeof (a_2 && (a_2.var = NaN >= [] || Infinity < 0) && 38..toString() == 1 & (this || -0));

console.log(null, a, b, c, Infinity, NaN, undefined);
```
```js
// uglified code
// (beautified)
var o = 10, a = 0, n = o += 100;

for (var r in n) {
    a = 1 + a;
    var v = n[r];
}

function o() {}

v = typeof (v && (v.var = !1) && !1 & (this || -0)), console.log(null, 100, o, a, 1 / 0, NaN, void 0);
```
```
original result:
null 100 'function(){}' 0 Infinity NaN undefined

uglified result:
null 100 110 0 Infinity NaN undefined
```
```js
// reduced test case (output will differ)

// (beautified)
var b = 0;

b += 0;

var b = function() {}, a_2 = console.log();

console.log(b);
// output: 
// function(){}
// 
// minify: 
// 0
// 
// options: {
//   "compress": {
//     "passes": 1000000,
//     "unsafe": true
//   },
//   "toplevel": true
// }
```
```
minify(options):
{
  "compress": {
    "passes": 1000000,
    "unsafe": true
  },
  "toplevel": true
}

Suspicious compress options:
  functions
  reduce_vars
  unused

Suspicious options:
  toplevel
```


Discussion:

Patch:
diff --git a/lib/compress.js b/lib/compress.js
index b0e25ddea44..6742aef343a 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -4151,22 +4151,17 @@ merge(Compressor.prototype, {
                             def.value = def.value.drop_side_effect_free(compressor);
                         }
                         var var_defs = var_defs_by_id.get(sym.id);
-                        if (var_defs.length > 1) {
-                            if (!def.value) {
+                        if (!def.value) {
+                            if (var_defs.length > 1) {
                                 AST_Node.warn("Dropping duplicated declaration of variable {name} [{file}:{line},{col}]", template(def.name));
                                 remove(var_defs, def);
                                 sym.eliminated++;
-                                return;
-                            }
-                            if (sym.orig.indexOf(def.name) > sym.eliminated) {
-                                remove(var_defs, def);
-                                duplicated++;
+                            } else {
+                                head.push(def);
                             }
-                        }
-                        if (!def.value) {
-                            head.push(def);
                         } else if (compressor.option("functions")
                             && !compressor.option("ie8")
+                            && var_defs.length == 1
                             && def.value === def.name.fixed_value()
                             && def.value instanceof AST_Function
                             && !(def.value.name && def.value.name.definition().assignments)
@@ -4188,6 +4183,10 @@ merge(Compressor.prototype, {
                             }
                             body.push(defun);
                         } else {
+                            if (var_defs.length > 1 && sym.orig.indexOf(def.name) > sym.eliminated) {
+                                remove(var_defs, def);
+                                duplicated++;
+                            }
                             if (side_effects.length > 0) {
                                 if (tail.length > 0) {
                                     side_effects.push(def.value);


Test patch:
diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js
index 3a61f24e62e..a39dd48078e 100644
--- a/test/compress/drop-unused.js
+++ b/test/compress/drop-unused.js
@@ -2488,3 +2488,25 @@ drop_duplicated_var_catch: {
         }
     }
 }
+
+issue_3802: {
+    options = {
+        functions: true,
+        reduce_vars: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        var a = 0;
+        a += 0;
+        var a = function() {};
+        console.log(typeof a);
+    }
+    expect: {
+        var a = 0;
+        a += 0;
+        a = function() {};
+        console.log(typeof a);
+    }
+    expect_stdout: "function"
+}