compress empty for-in loops (#3590)
authorAlex Lam S.L <alexlamsl@gmail.com>
Sat, 16 Nov 2019 18:36:42 +0000 (02:36 +0800)
committerGitHub <noreply@github.com>
Sat, 16 Nov 2019 18:36:42 +0000 (02:36 +0800)
lib/compress.js
test/compress/loops.js

index 9366091..e85cf5d 100644 (file)
@@ -3912,19 +3912,6 @@ merge(Compressor.prototype, {
                 scope = save_scope;
                 return node;
             }
-
-            function log(sym, text, props) {
-                AST_Node[sym.unreferenced() ? "warn" : "info"](text, props);
-            }
-
-            function template(sym) {
-                return {
-                    name : sym.name,
-                    file : sym.start.file,
-                    line : sym.start.line,
-                    col  : sym.start.col
-                };
-            }
         }, function(node, in_list) {
             if (node instanceof AST_For) {
                 // Certain combination of unused name + side effect leads to invalid AST:
@@ -3952,6 +3939,21 @@ merge(Compressor.prototype, {
                     node.init = null;
                 }
                 return !block ? node : in_list ? MAP.splice(block.body) : block;
+            } else if (node instanceof AST_ForIn) {
+                if (!drop_vars || !compressor.option("loops")) return;
+                if (!(node.init instanceof AST_Definitions)) return;
+                var sym = node.init.definitions[0].name;
+                if (sym.definition().id in in_use_ids) return;
+                if (!is_empty(node.body)) return;
+                log(sym, "Dropping unused loop variable {name} [{file}:{line},{col}]", template(sym));
+                var value = node.object.drop_side_effect_free(compressor);
+                if (value) {
+                    AST_Node.warn("Side effects in object of for-in loop [{file}:{line},{col}]", template(sym));
+                    return make_node(AST_SimpleStatement, node, {
+                        body: value
+                    });
+                }
+                return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
             } else if (node instanceof AST_Sequence) {
                 if (node.expressions.length == 1) return node.expressions[0];
             }
@@ -3962,6 +3964,19 @@ merge(Compressor.prototype, {
             fn.name = null;
         });
 
+        function log(sym, text, props) {
+            AST_Node[sym.unreferenced() ? "warn" : "info"](text, props);
+        }
+
+        function template(sym) {
+            return {
+                name: sym.name,
+                file: sym.start.file,
+                line: sym.start.line,
+                col : sym.start.col
+            };
+        }
+
         function verify_safe_usage(def, read, modified) {
             if (def.id in in_use_ids) return;
             if (read && modified) {
index d835080..7b6002a 100644 (file)
@@ -689,3 +689,67 @@ step: {
     }
     expect_stdout: "42"
 }
+
+empty_for_in: {
+    options = {
+        loops: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        for (var a in [ 1, 2, 3 ]) {
+            var b = a + 1;
+        }
+    }
+    expect: {}
+    expect_warnings: [
+        "WARN: Dropping unused variable b [test/compress/loops.js:2,16]",
+        "INFO: Dropping unused loop variable a [test/compress/loops.js:1,17]",
+    ]
+}
+
+empty_for_in_used: {
+    options = {
+        loops: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        for (var a in [ 1, 2, 3 ]) {
+            var b = a + 1;
+        }
+        console.log(a);
+    }
+    expect: {
+        for (var a in [ 1, 2, 3 ]);
+        console.log(a);
+    }
+    expect_stdout: "2"
+    expect_warnings: [
+        "WARN: Dropping unused variable b [test/compress/loops.js:2,16]",
+    ]
+}
+
+empty_for_in_side_effects: {
+    options = {
+        loops: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        for (var a in {
+            foo: console.log("PASS")
+        }) {
+            var b = a + "bar";
+        }
+    }
+    expect: {
+        console.log("PASS");
+    }
+    expect_stdout: "PASS"
+    expect_warnings: [
+        "WARN: Dropping unused variable b [test/compress/loops.js:4,16]",
+        "INFO: Dropping unused loop variable a [test/compress/loops.js:1,17]",
+        "WARN: Side effects in object of for-in loop [test/compress/loops.js:1,17]",
+    ]
+}