Use TreeWalker for more accurate @const results and update tests
authorSamuel Reed <samuel.trace.reed@gmail.com>
Wed, 20 Jan 2016 16:52:48 +0000 (10:52 -0600)
committerSamuel Reed <samuel.trace.reed@gmail.com>
Wed, 20 Jan 2016 16:54:00 +0000 (10:54 -0600)
lib/scope.js
test/compress/dead-code.js

index 22fb150..144ae48 100644 (file)
@@ -358,13 +358,31 @@ AST_Symbol.DEFMETHOD("global", function(){
 });
 
 AST_Symbol.DEFMETHOD("has_const_pragma", function() {
-    var token = this.scope.body[0] && this.scope.body[0].start;
-    var comments = token && token.comments_before;
-    if (comments && comments.length > 0) {
-        var last = comments[comments.length - 1];
-        return /@const/.test(last.value);
-    }
-    return false;
+    var symbol = this;
+    var symbol_has_pragma = false;
+    var pragma_found = false;
+    var found_symbol = false;
+    // Walk the current scope, looking for a comment with the @const pragma.
+    // If it exists, mark a bool that will remain true only for the next iteration.
+    // If the next iteration is this symbol, then we return true.
+    // Otherwise we stop descending and get out of here.
+    var tw = new TreeWalker(function(node, descend){
+        // This is our symbol. Was the pragma before this?
+        if (node.name === symbol) {
+            found_symbol = true;
+            symbol_has_pragma = pragma_found;
+        }
+
+        // Look for the /** @const */ pragma
+        var comments_before = node.start && node.start.comments_before;
+        var lastComment = comments_before && comments_before[comments_before.length - 1];
+        pragma_found = lastComment && /@const/.test(lastComment.value);
+
+        // no need to descend after finding our node
+        return found_symbol;
+    });
+    this.scope.walk(tw);
+    return symbol_has_pragma;
 })
 
 AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
index f79b04d..8aad336 100644 (file)
@@ -97,6 +97,7 @@ dead_code_const_declaration: {
         evaluate     : true\r
     };\r
     input: {\r
+        var unused;\r
         const CONST_FOO = false;\r
         if (CONST_FOO) {\r
             console.log("unreachable");\r
@@ -105,6 +106,7 @@ dead_code_const_declaration: {
         }\r
     }\r
     expect: {\r
+        var unused;\r
         const CONST_FOO = !1;\r
         var moo;\r
         function bar() {}\r
@@ -120,7 +122,8 @@ dead_code_const_annotation: {
         evaluate     : true\r
     };\r
     input: {\r
-        /** @const*/ var CONST_FOO_ANN = false;\r
+        var unused;\r
+        /** @const */ var CONST_FOO_ANN = false;\r
         if (CONST_FOO_ANN) {\r
             console.log("unreachable");\r
             var moo;\r
@@ -128,8 +131,52 @@ dead_code_const_annotation: {
         }\r
     }\r
     expect: {\r
+        var unused;\r
         var CONST_FOO_ANN = !1;\r
         var moo;\r
         function bar() {}\r
     }\r
 }\r
+\r
+dead_code_const_annotation_complex_scope: {\r
+    options = {\r
+        dead_code    : true,\r
+        loops        : true,\r
+        booleans     : true,\r
+        conditionals : true,\r
+        evaluate     : true\r
+    };\r
+    input: {\r
+        var unused_var;\r
+        /** @const */ var test = 'test';\r
+        /** @const */ var CONST_FOO_ANN = false;\r
+        var unused_var_2;\r
+        if (CONST_FOO_ANN) {\r
+            console.log("unreachable");\r
+            var moo;\r
+            function bar() {}\r
+        }\r
+        if (test === 'test') {\r
+            var beef = 'good';\r
+            /** @const */ var meat = 'beef';\r
+            var pork = 'bad';\r
+            if (meat === 'pork') {\r
+                console.log('also unreachable');\r
+            } else if (pork === 'good') {\r
+                console.log('reached, not const');\r
+            }\r
+        }\r
+    }\r
+    expect: {\r
+        var unused_var;\r
+        var test = 'test';\r
+        var CONST_FOO_ANN = !1;\r
+        var unused_var_2;\r
+        var moo;\r
+        function bar() {}\r
+        var beef = 'good';\r
+        var meat = 'beef';\r
+        var pork = 'bad';\r
+        'good' === pork && console.log('reached, not const');\r
+    }\r
+}\r