Repository: mishoo/UglifyJS (JavaScript)

Issue:
ufuzz failure
```js
// original code
// (beautified)
var _calls_ = 10, a = 100, b = 10, c = 0;

function f0(a) {
    let c_1 = (c = c + 1) + !(((a = "a" ^ this) != (-4 != NaN)) % (-0 - false >= 23..toString() << 3));
    c = c + 1;
    {
        var brake2 = 5;
        while (a++ + (undefined => typeof arguments != "undefined" && arguments && arguments[0])() && --brake2 > 0) {
            return b--;
        }
    }
}

var b_2 = f0(((b = a) || 9).toString()[b += a], b_2 && typeof b_2.Infinity == "function" && --_calls_ >= 0 && b_2.Infinity(-3, 24..toString(), false));

console.log(null, a, b, c, Infinity, NaN, undefined);
```
```js
// uglified code
// (beautified)
var i = 10, f = 0, n = function f0(n) {
    if (f += 1, f += 1, +(n = "a" ^ this) + (void 0 !== arguments && arguments && arguments[0])) {
        return i--;
    }
}(("" + (i = 100))[i += 100], n && "function" == typeof n.Infinity && n.Infinity(-3, "24", !1));

console.log(null, 100, i, f, Infinity, NaN, void 0);
```
```
original result:
null 100 199 2 Infinity NaN undefined

uglified result:
null 100 200 2 Infinity NaN undefined
```
```js
// reduced test case (output will differ)

// (beautified)
function f0(a) {
    let c_1 = a = 0;
    console.log(a++ + arguments[0]);
}

f0(0);
// output: 1
// 
// minify: 0
// 
// options: {
//   "compress": {
//     "hoist_vars": true,
//     "keep_infinity": true,
//     "passes": 1000000,
//     "unsafe": true
//   },
//   "keep_fnames": true,
//   "toplevel": true,
//   "mangle": {
//     "v8": true
//   },
//   "output": {
//     "v8": true
//   },
//   "validate": true
// }
```
```
minify(options):
{
  "compress": {
    "hoist_vars": true,
    "keep_infinity": true,
    "passes": 1000000,
    "unsafe": true
  },
  "keep_fnames": true,
  "toplevel": true,
  "mangle": {
    "v8": true
  },
  "output": {
    "v8": true
  }
}

Suspicious compress options:
  dead_code
  loops
  passes
  reduce_vars
  unused

Suspicious options:
  keep_fnames
```


Discussion:

Patch:
diff --git a/lib/compress.js b/lib/compress.js
index 8975f47bddc..4f50f52a007 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -580,9 +580,12 @@ merge(Compressor.prototype, {
         }
 
         function safe_to_assign(tw, def, declare) {
-            if (!(declare || all(def.orig, function(sym) {
-                return !(sym instanceof AST_SymbolConst);
-            }))) return false;
+            if (!declare) {
+                if (is_funarg(def) && def.scope.uses_arguments && !tw.has_directive("use strict")) return false;
+                if (!all(def.orig, function(sym) {
+                    return !(sym instanceof AST_SymbolConst);
+                })) return false;
+            }
             if (def.fixed === undefined) return declare || all(def.orig, function(sym) {
                 return !(sym instanceof AST_SymbolLet);
             });


Test patch:
diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js
index 04d96b51949..33d59982d09 100644
--- a/test/compress/reduce_vars.js
+++ b/test/compress/reduce_vars.js
@@ -7722,3 +7722,23 @@ issue_4943_2: {
         "bar",
     ]
 }
+
+issue_4949: {
+    options = {
+        reduce_vars: true,
+        unused: true,
+    }
+    input: {
+        (function f(a) {
+            a = 0;
+            console.log(a++, arguments[0]);
+        })(0);
+    }
+    expect: {
+        (function(a) {
+            a = 0;
+            console.log(a++, arguments[0]);
+        })(0);
+    }
+    expect_stdout: "0 1"
+}