fix corner case in `unused` (#3716)
authorAlex Lam S.L <alexlamsl@gmail.com>
Wed, 12 Feb 2020 23:46:16 +0000 (23:46 +0000)
committerGitHub <noreply@github.com>
Wed, 12 Feb 2020 23:46:16 +0000 (23:46 +0000)
lib/compress.js
test/compress/drop-unused.js
test/compress/functions.js
test/compress/keep_fargs.js

index 55e40ef..8a33b47 100644 (file)
@@ -4001,6 +4001,8 @@ merge(Compressor.prototype, {
         };
         // pass 3: we should drop declarations not in_use
         var unused_fn_names = [];
+        var calls_to_drop_args = [];
+        var fns_with_marked_args = [];
         var tt = new TreeTransformer(function(node, descend, in_list) {
             var parent = tt.parent();
             if (drop_vars) {
@@ -4041,6 +4043,7 @@ merge(Compressor.prototype, {
                     }
                 }
             }
+            if (node instanceof AST_Call) calls_to_drop_args.push(node);
             if (scope !== self) return;
             if (node instanceof AST_Function && node.name && drop_fn_name(node.name.definition())) {
                 unused_fn_names.push(node);
@@ -4059,6 +4062,7 @@ merge(Compressor.prototype, {
                         trim = false;
                     }
                 }
+                fns_with_marked_args.push(node);
             }
             if (drop_funcs && node instanceof AST_Defun && node !== self) {
                 var def = node.name.definition();
@@ -4252,6 +4256,9 @@ merge(Compressor.prototype, {
         unused_fn_names.forEach(function(fn) {
             fn.name = null;
         });
+        calls_to_drop_args.forEach(function(call) {
+            drop_unused_call_args(call, compressor, fns_with_marked_args);
+        });
 
         function log(sym, text, props) {
             AST_Node[sym.unreferenced() ? "warn" : "info"](text, props);
@@ -5448,6 +5455,62 @@ merge(Compressor.prototype, {
         return make_sequence(node, x);
     }
 
+    function drop_unused_call_args(call, compressor, fns_with_marked_args) {
+        var exp = call.expression;
+        var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
+        if (!(fn instanceof AST_Lambda)) return;
+        if (fn.uses_arguments) return;
+        if (fn.pinned()) return;
+        if (fns_with_marked_args && fns_with_marked_args.indexOf(fn) < 0) return;
+        var args = call.args;
+        var pos = 0, last = 0;
+        var drop_fargs = fn === exp && !fn.name && compressor.drop_fargs(fn, call);
+        var side_effects = [];
+        for (var i = 0; i < args.length; i++) {
+            var trim = i >= fn.argnames.length;
+            if (trim || fn.argnames[i].__unused) {
+                var node = args[i].drop_side_effect_free(compressor);
+                if (drop_fargs) {
+                    fn.argnames.splice(i, 1);
+                    args.splice(i, 1);
+                    if (node) side_effects.push(node);
+                    i--;
+                    continue;
+                } else if (node) {
+                    side_effects.push(node);
+                    args[pos++] = make_sequence(call, side_effects);
+                    side_effects = [];
+                } else if (!trim) {
+                    if (side_effects.length) {
+                        node = make_sequence(call, side_effects);
+                        side_effects = [];
+                    } else {
+                        node = make_node(AST_Number, args[i], {
+                            value: 0
+                        });
+                    }
+                    args[pos++] = node;
+                    continue;
+                }
+            } else {
+                side_effects.push(args[i]);
+                args[pos++] = make_sequence(call, side_effects);
+                side_effects = [];
+            }
+            last = pos;
+        }
+        if (drop_fargs) for (; i < fn.argnames.length; i++) {
+            if (fn.argnames[i].__unused) fn.argnames.splice(i--, 1);
+        }
+        args.length = last;
+        if (!side_effects.length) return;
+        var arg = make_sequence(call, side_effects);
+        args.push(args.length < fn.argnames.length ? make_node(AST_UnaryPrefix, call, {
+            operator: "void",
+            expression: arg
+        }) : arg);
+    }
+
     OPT(AST_Call, function(self, compressor) {
         var exp = self.expression;
         if (compressor.option("sequences")) {
@@ -5464,63 +5527,7 @@ merge(Compressor.prototype, {
                 if (seq !== self) return seq.optimize(compressor);
             }
         }
-        var fn = exp;
-        if (compressor.option("reduce_vars") && fn instanceof AST_SymbolRef) {
-            fn = fn.fixed_value();
-        }
-        var is_func = fn instanceof AST_Lambda;
-        if (compressor.option("unused")
-            && is_func
-            && !fn.uses_arguments
-            && !fn.pinned()) {
-            var pos = 0, last = 0;
-            var drop_fargs = exp === fn && !fn.name && compressor.drop_fargs(fn, self);
-            var side_effects = [];
-            for (var i = 0; i < self.args.length; i++) {
-                var trim = i >= fn.argnames.length;
-                if (trim || fn.argnames[i].__unused) {
-                    var node = self.args[i].drop_side_effect_free(compressor);
-                    if (drop_fargs) {
-                        fn.argnames.splice(i, 1);
-                        self.args.splice(i, 1);
-                        if (node) side_effects.push(node);
-                        i--;
-                        continue;
-                    } else if (node) {
-                        side_effects.push(node);
-                        self.args[pos++] = make_sequence(self, side_effects);
-                        side_effects = [];
-                    } else if (!trim) {
-                        if (side_effects.length) {
-                            node = make_sequence(self, side_effects);
-                            side_effects = [];
-                        } else {
-                            node = make_node(AST_Number, self.args[i], {
-                                value: 0
-                            });
-                        }
-                        self.args[pos++] = node;
-                        continue;
-                    }
-                } else {
-                    side_effects.push(self.args[i]);
-                    self.args[pos++] = make_sequence(self, side_effects);
-                    side_effects = [];
-                }
-                last = pos;
-            }
-            if (drop_fargs) for (; i < fn.argnames.length; i++) {
-                if (fn.argnames[i].__unused) fn.argnames.splice(i--, 1);
-            }
-            self.args.length = last;
-            if (side_effects.length) {
-                var arg = make_sequence(self, side_effects);
-                self.args.push(self.args.length < fn.argnames.length ? make_node(AST_UnaryPrefix, self, {
-                    operator: "void",
-                    expression: arg
-                }) : arg);
-            }
-        }
+        if (compressor.option("unused")) drop_unused_call_args(self, compressor);
         if (compressor.option("unsafe")) {
             if (is_undeclared_ref(exp)) switch (exp.name) {
               case "Array":
@@ -5786,6 +5793,8 @@ merge(Compressor.prototype, {
                 }
             }
         }
+        var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
+        var is_func = fn instanceof AST_Lambda;
         var stat = is_func && fn.first_statement();
         var can_inline = compressor.option("inline") && !self.is_expr_pure(compressor);
         if (exp === fn && can_inline && stat instanceof AST_Return) {
index 0691a18..2d2f3d4 100644 (file)
@@ -1191,10 +1191,10 @@ issue_2105_1: {
     input: {
         !function(factory) {
             factory();
-        }( function() {
+        }(function() {
             return function(fn) {
                 fn()().prop();
-            }( function() {
+            }(function() {
                 function bar() {
                     var quux = function() {
                         console.log("PASS");
@@ -1205,7 +1205,7 @@ issue_2105_1: {
                     return { prop: foo };
                 }
                 return bar;
-            } );
+            });
         });
     }
     expect: {
@@ -1235,10 +1235,10 @@ issue_2105_2: {
     input: {
         !function(factory) {
             factory();
-        }( function() {
+        }(function() {
             return function(fn) {
                 fn()().prop();
-            }( function() {
+            }(function() {
                 function bar() {
                     var quux = function() {
                         console.log("PASS");
@@ -1249,7 +1249,7 @@ issue_2105_2: {
                     return { prop: foo };
                 }
                 return bar;
-            } );
+            });
         });
     }
     expect: {
@@ -1258,6 +1258,44 @@ issue_2105_2: {
     expect_stdout: "PASS"
 }
 
+issue_2105_3: {
+    options = {
+        inline: true,
+        passes: 2,
+        reduce_vars: true,
+        unused: true,
+    }
+    input: {
+        !function(factory) {
+            factory();
+        }(function() {
+            return function(fn) {
+                fn()().prop();
+            }(function() {
+                function bar() {
+                    var quux = function() {
+                        console.log("PASS");
+                    }, foo = function() {
+                        console.log;
+                        quux();
+                    };
+                    return { prop: foo };
+                }
+                return bar;
+            });
+        });
+    }
+    expect: {
+        !void void {
+            prop: function() {
+                console.log;
+                void console.log("PASS");
+            }
+        }.prop();
+    }
+    expect_stdout: "PASS"
+}
+
 issue_2226_1: {
     options = {
         side_effects: true,
@@ -2330,7 +2368,7 @@ function_parameter_ie8: {
         (function() {
             (function f() {
                 console.log("PASS");
-            })(0);
+            })();
         })();
     }
     expect_stdout: "PASS"
index ef51af7..0cf0179 100644 (file)
@@ -1276,7 +1276,7 @@ issue_2630_3: {
         (function() {
             (function f1(a) {
                 f2();
-                --x >= 0 && f1({});
+                --x >= 0 && f1();
             })(a++);
             function f2() {
                 a++;
index a17a8f3..85fcb0b 100644 (file)
@@ -728,7 +728,7 @@ issue_2630_3: {
         (function() {
             (function f1() {
                 f2();
-                --x >= 0 && f1({});
+                --x >= 0 && f1();
             })(a++);
             function f2() {
                 a++;
@@ -1369,7 +1369,7 @@ recursive_iife_1: {
     }
     expect: {
         console.log(function f(a, b) {
-            return b || f("FAIL", "PASS");
+            return b || f(0, "PASS");
         }());
     }
     expect_stdout: "PASS"
@@ -1388,7 +1388,7 @@ recursive_iife_2: {
     }
     expect: {
         console.log(function f(a, b) {
-            return b || f("FAIL", "PASS");
+            return b || f(0, "PASS");
         }(0, 0));
     }
     expect_stdout: "PASS"
@@ -1416,7 +1416,7 @@ recursive_iife_3: {
         var a = 1, c = "PASS";
         (function() {
             (function f(b, d, e) {
-                a-- && f(null, 42, 0);
+                a-- && f(0, 42, 0);
                 e && (c = "FAIL");
                 d && d.p;
             })();