fix corner cases in `reduce_vars` & `unused` (#5247)
authorAlex Lam S.L <alexlamsl@gmail.com>
Thu, 30 Dec 2021 20:47:13 +0000 (20:47 +0000)
committerGitHub <noreply@github.com>
Thu, 30 Dec 2021 20:47:13 +0000 (04:47 +0800)
fixes #5246

lib/compress.js
test/compress/default-values.js
test/compress/rests.js
test/compress/yields.js

index 7a4c53e..c54a8f3 100644 (file)
@@ -784,8 +784,11 @@ Compressor.prototype.compress = function(node) {
                         if (save) fixed = compressor.option("rests") && function() {
                             var value = save();
                             if (!(value instanceof AST_Array)) return node;
+                            for (var i = 0, len = node.elements.length; i < len; i++) {
+                                if (value.elements[i] instanceof AST_Spread) return node;
+                            }
                             if (!fixed_node) fixed_node = make_node(AST_Array, node);
-                            fixed_node.elements = value.elements.slice(node.elements.length);
+                            fixed_node.elements = value.elements.slice(len);
                             return fixed_node;
                         };
                         node.rest.walk(scanner);
@@ -6662,8 +6665,28 @@ Compressor.prototype.compress = function(node) {
                     unused_fn_names.push(node);
                 }
                 if (!(node instanceof AST_Accessor)) {
-                    if (node.rest) {
-                        var rest = node.rest.transform(trimmer);
+                    var args, spread;
+                    if (parent instanceof AST_Call && parent.expression === node) {
+                        args = parent.args;
+                        for (spread = 0; spread < args.length; spread++) {
+                            if (args[spread] instanceof AST_Spread) break;
+                        }
+                    }
+                    var argnames = node.argnames;
+                    var rest = node.rest;
+                    if (rest) {
+                        if (!args || spread < argnames.length || rest instanceof AST_SymbolFunarg) {
+                            rest = rest.transform(trimmer);
+                        } else {
+                            var trimmed = trim_destructured(rest, make_node(AST_Array, parent, {
+                                elements: args.slice(argnames.length),
+                            }), function(node) {
+                                return node.definition().id in in_use_ids ? node : null;
+                            }, !node.uses_arguments, rest);
+                            rest = trimmed.name;
+                            args.length = argnames.length;
+                            if (trimmed.value.elements.length) [].push.apply(args, trimmed.value.elements);
+                        }
                         if (rest instanceof AST_Destructured && !rest.rest
                             && (!node.uses_arguments || tt.has_directive("use strict"))) {
                             if (rest instanceof AST_DestructuredArray) {
@@ -6674,37 +6697,43 @@ Compressor.prototype.compress = function(node) {
                         }
                         node.rest = rest;
                     }
-                    var argnames = node.argnames;
                     var trim = compressor.drop_fargs(node, parent) && !node.rest;
                     var default_length = trim ? -1 : node.length();
                     for (var i = argnames.length; --i >= 0;) {
                         var sym = argnames[i];
-                        if (!(sym instanceof AST_SymbolFunarg)) {
-                            var arg = sym.transform(trimmer);
-                            if (arg) {
+                        if (sym instanceof AST_SymbolFunarg) {
+                            var def = sym.definition();
+                            if (def.id in in_use_ids) {
+                                trim = false;
+                                if (indexOf_assign(def, sym) < 0) sym.unused = null;
+                            } else if (trim) {
+                                log(sym, "Dropping unused function argument {name}");
+                                argnames.pop();
+                            } else {
+                                sym.unused = true;
+                            }
+                        } else {
+                            var funarg;
+                            if (!args || spread < i) {
+                                funarg = sym.transform(trimmer);
+                            } else {
+                                funarg = trim_destructured(sym, args[i], function(node) {
+                                    return node.definition().id in in_use_ids ? node : null;
+                                }, !node.uses_arguments, sym).name;
+                            }
+                            if (funarg) {
                                 trim = false;
                             } else if (trim) {
-                                log(sym.name, "Dropping unused default argument {name}");
+                                log_default(sym, "Dropping unused default argument {name}");
                                 argnames.pop();
                             } else if (i > default_length) {
-                                log(sym.name, "Dropping unused default argument assignment {name}");
-                                sym.name.unused = true;
+                                log_default(sym, "Dropping unused default argument assignment {name}");
+                                if (sym.name instanceof AST_SymbolFunarg) sym.name.unused = true;
                                 argnames[i] = sym.name;
                             } else {
-                                log(sym.name, "Dropping unused default argument value {name}");
+                                log_default(sym, "Dropping unused default argument value {name}");
                                 sym.value = make_node(AST_Number, sym, { value: 0 });
                             }
-                            continue;
-                        }
-                        var def = sym.definition();
-                        if (def.id in in_use_ids) {
-                            trim = false;
-                            if (indexOf_assign(def, sym) < 0) sym.unused = null;
-                        } else if (trim) {
-                            log(sym, "Dropping unused function argument {name}");
-                            argnames.pop();
-                        } else {
-                            sym.unused = true;
                         }
                     }
                     fns_with_marked_args.push(node);
@@ -7049,6 +7078,19 @@ Compressor.prototype.compress = function(node) {
             AST_Node[sym.definition().references.length > 0 ? "info" : "warn"](text + " [{file}:{line},{col}]", template(sym));
         }
 
+        function log_default(node, text) {
+            if (node.name instanceof AST_SymbolFunarg) {
+                log(node.name, text);
+            } else {
+                AST_Node.info(text + " [{file}:{line},{col}]", {
+                    name: node.print_to_string(),
+                    file: node.start.file,
+                    line: node.start.line,
+                    col : node.start.col,
+                });
+            }
+        }
+
         function template(sym) {
             return {
                 name: sym.name,
@@ -7245,18 +7287,20 @@ Compressor.prototype.compress = function(node) {
             return node;
         }
 
-        function trim_destructured(node, value, process, drop) {
+        function trim_destructured(node, value, process, drop, root) {
             var trimmer = new TreeTransformer(function(node) {
                 if (node instanceof AST_DefaultValue) {
-                    if (compressor.option("default_values") && value && value.is_defined(compressor)) {
-                        node = node.name;
-                    } else {
+                    if (!(compressor.option("default_values") && value && value.is_defined(compressor))) {
                         var save_drop = drop;
                         drop = false;
                         var trimmed = trim_default(trimmer, node);
                         drop = save_drop;
                         if (!trimmed && drop && value) value = value.drop_side_effect_free(compressor);
                         return trimmed;
+                    } else if (node === root) {
+                        root = node = node.name;
+                    } else {
+                        node = node.name;
                     }
                 }
                 if (node instanceof AST_DestructuredArray) {
@@ -7319,10 +7363,12 @@ Compressor.prototype.compress = function(node) {
                     if (!node.rest && (value instanceof AST_Array
                         || value && value.is_string(compressor))) switch (elements.length) {
                       case 0:
+                        if (node === root) break;
                         if (drop) value = value.drop_side_effect_free(compressor);
                         return null;
                       case 1:
                         if (!drop) break;
+                        if (node === root) break;
                         var sym = elements[0];
                         if (sym.has_side_effects(compressor)) break;
                         if (value.has_side_effects(compressor) && sym.match_symbol(function(node) {
@@ -7446,11 +7492,13 @@ Compressor.prototype.compress = function(node) {
                     });
                     if (value && !node.rest) switch (properties.length) {
                       case 0:
+                        if (node === root) break;
                         if (value.may_throw_on_access(compressor, true)) break;
                         if (drop) value = value.drop_side_effect_free(compressor);
                         return null;
                       case 1:
                         if (!drop) break;
+                        if (node === root) break;
                         var prop = properties[0];
                         if (prop.key instanceof AST_Node) break;
                         if (prop.value.has_side_effects(compressor)) break;
index f43374b..53ebb19 100644 (file)
@@ -2038,3 +2038,59 @@ issue_5222: {
     expect_stdout: "PASS"
     node_version: ">=6"
 }
+
+issue_5246_1: {
+    options = {
+        pure_getters: true,
+        unused: true,
+    }
+    input: {
+        console.log(function({} = 42) {
+            return "PASS";
+        }("foo"));
+    }
+    expect: {
+        console.log(function({} = 0) {
+            return "PASS";
+        }("foo"));
+    }
+    expect_stdout: "PASS"
+    expect_warnings: [
+        "INFO: Dropping unused default argument value {}=42 [test/compress/default-values.js:1,29]",
+        "INFO: Dropping unused default argument value {}=0 [test/compress/default-values.js:1,29]",
+    ]
+    node_version: ">=6"
+}
+
+issue_5246_2: {
+    options = {
+        unused: true,
+    }
+    input: {
+        (function f(a = "FAIL", [] = 42) {
+            console.log(a);
+        })("PASS", []);
+    }
+    expect: {
+        (function(a = "FAIL", []) {
+            console.log(a);
+        })("PASS", []);
+    }
+    expect_stdout: "PASS"
+    node_version: ">=6"
+}
+
+issue_5246_3: {
+    options = {
+        default_values: true,
+        unused: true,
+    }
+    input: {
+        console.log(function f([ , {} ] = null){}([ , {} ]));
+    }
+    expect: {
+        console.log(function([ {} ]){}([ {} ]));
+    }
+    expect_stdout: "undefined"
+    node_version: ">=6"
+}
index ce27b6e..3358421 100644 (file)
@@ -359,7 +359,7 @@ retain_funarg_destructured_object_2: {
     expect: {
         console.log(function({ p: a, ... b }) {
             return b;
-        }({ p: "FAIL" }).p || "PASS");
+        }({}).p || "PASS");
     }
     expect_stdout: "PASS"
     node_version: ">=8.3.0"
@@ -1104,7 +1104,7 @@ issue_5108: {
     expect: {
         console.log(function([]) {
             return "PASS";
-        }([ "PASS", "FAIL" ]));
+        }([]));
     }
     expect_stdout: "PASS"
     node_version: ">=6"
@@ -1205,3 +1205,62 @@ issue_5165_2: {
     expect_stdout: "PASS"
     node_version: ">=6"
 }
+
+issue_5246_1: {
+    options = {
+        reduce_vars: true,
+        rests: true,
+        unused: true,
+    }
+    input: {
+        console.log(typeof function([ , ...a ]) {
+            return this && a;
+        }([ , function(){} ])[0]);
+    }
+    expect: {
+        console.log(typeof function([]) {
+            return this && [ function(){} ];
+        }([])[0]);
+    }
+    expect_stdout: "function"
+    node_version: ">=6"
+}
+
+issue_5246_2: {
+    options = {
+        reduce_vars: true,
+        rests: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        A = [ , "PASS", "FAIL" ];
+        var  [ , ...a ] = [ ... A ];
+        console.log(a[0]);
+    }
+    expect: {
+        A = [ , "PASS", "FAIL" ];
+        var  [ , ...a ] = [ ... A ];
+        console.log(a[0]);
+    }
+    expect_stdout: "PASS"
+    node_version: ">=6"
+}
+
+issue_5246_3: {
+    options = {
+        unused: true,
+    }
+    input: {
+        (function f(...[ [ a ] ]) {
+            console.log(a);
+        })([ "PASS" ]);
+    }
+    expect: {
+        (function(...[ a ]) {
+            console.log(a);
+        })([ "PASS" ][0]);
+    }
+    expect_stdout: "PASS"
+    node_version: ">=6"
+}
index 6e40f3e..0b90a5f 100644 (file)
@@ -1371,12 +1371,12 @@ issue_5034: {
     node_version: ">=4"
 }
 
-issue_5076: {
+issue_5076_1: {
     options = {
         evaluate: true,
         hoist_vars: true,
-        passes: 2,
         pure_getters: "strict",
+        sequences: true,
         side_effects: true,
         toplevel: true,
         unused: true,
@@ -1393,13 +1393,41 @@ issue_5076: {
     }
     expect: {
         var a;
-        console.log("PASS");
+        console.log("PASS"),
         a = 42["a"];
     }
     expect_stdout: "PASS"
     node_version: ">=6"
 }
 
+issue_5076_2: {
+    options = {
+        evaluate: true,
+        hoist_vars: true,
+        passes: 2,
+        pure_getters: "strict",
+        sequences: true,
+        side_effects: true,
+        toplevel: true,
+        unused: true,
+        yields: true,
+    }
+    input: {
+        var a;
+        console.log("PASS");
+        var b = function*({
+            p: {},
+        }) {}({
+            p: { a } = 42,
+        });
+    }
+    expect: {
+        console.log("PASS");
+    }
+    expect_stdout: "PASS"
+    node_version: ">=6"
+}
+
 issue_5177: {
     options = {
         properties: true,