Use TreeWalker for more accurate @const results and update tests

This commit is contained in:
Samuel Reed
2016-01-20 10:52:48 -06:00
parent 918c17bd88
commit f97da4294a
2 changed files with 73 additions and 8 deletions

View File

@@ -358,13 +358,31 @@ AST_Symbol.DEFMETHOD("global", function(){
}); });
AST_Symbol.DEFMETHOD("has_const_pragma", function() { AST_Symbol.DEFMETHOD("has_const_pragma", function() {
var token = this.scope.body[0] && this.scope.body[0].start; var symbol = this;
var comments = token && token.comments_before; var symbol_has_pragma = false;
if (comments && comments.length > 0) { var pragma_found = false;
var last = comments[comments.length - 1]; var found_symbol = false;
return /@const/.test(last.value); // 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.
return false; // 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){ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){

View File

@@ -97,6 +97,7 @@ dead_code_const_declaration: {
evaluate : true evaluate : true
}; };
input: { input: {
var unused;
const CONST_FOO = false; const CONST_FOO = false;
if (CONST_FOO) { if (CONST_FOO) {
console.log("unreachable"); console.log("unreachable");
@@ -105,6 +106,7 @@ dead_code_const_declaration: {
} }
} }
expect: { expect: {
var unused;
const CONST_FOO = !1; const CONST_FOO = !1;
var moo; var moo;
function bar() {} function bar() {}
@@ -120,7 +122,8 @@ dead_code_const_annotation: {
evaluate : true evaluate : true
}; };
input: { input: {
/** @const*/ var CONST_FOO_ANN = false; var unused;
/** @const */ var CONST_FOO_ANN = false;
if (CONST_FOO_ANN) { if (CONST_FOO_ANN) {
console.log("unreachable"); console.log("unreachable");
var moo; var moo;
@@ -128,8 +131,52 @@ dead_code_const_annotation: {
} }
} }
expect: { expect: {
var unused;
var CONST_FOO_ANN = !1; var CONST_FOO_ANN = !1;
var moo; var moo;
function bar() {} function bar() {}
} }
} }
dead_code_const_annotation_complex_scope: {
options = {
dead_code : true,
loops : true,
booleans : true,
conditionals : true,
evaluate : true
};
input: {
var unused_var;
/** @const */ var test = 'test';
/** @const */ var CONST_FOO_ANN = false;
var unused_var_2;
if (CONST_FOO_ANN) {
console.log("unreachable");
var moo;
function bar() {}
}
if (test === 'test') {
var beef = 'good';
/** @const */ var meat = 'beef';
var pork = 'bad';
if (meat === 'pork') {
console.log('also unreachable');
} else if (pork === 'good') {
console.log('reached, not const');
}
}
}
expect: {
var unused_var;
var test = 'test';
var CONST_FOO_ANN = !1;
var unused_var_2;
var moo;
function bar() {}
var beef = 'good';
var meat = 'beef';
var pork = 'bad';
'good' === pork && console.log('reached, not const');
}
}