Fix warnings for referenced non-hoisted functions.

Fixes #1034

Also added `expect_warnings` functionality to test framework.
This commit is contained in:
kzc
2016-04-10 15:41:38 -04:00
committed by Richard van Velzen
parent b434b75b36
commit 3907a5e3b2
3 changed files with 167 additions and 2 deletions

View File

@@ -801,7 +801,9 @@ merge(Compressor.prototype, {
}; };
function extract_declarations_from_unreachable_code(compressor, stat, target) { function extract_declarations_from_unreachable_code(compressor, stat, target) {
compressor.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start); if (!(stat instanceof AST_Defun)) {
compressor.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
}
stat.walk(new TreeWalker(function(node){ stat.walk(new TreeWalker(function(node){
if (node instanceof AST_Definitions) { if (node instanceof AST_Definitions) {
compressor.warn("Declarations in unreachable code! [{file}:{line},{col}]", node.start); compressor.warn("Declarations in unreachable code! [{file}:{line},{col}]", node.start);

137
test/compress/issue-1034.js Normal file
View File

@@ -0,0 +1,137 @@
non_hoisted_function_after_return: {
options = {
hoist_funs: false, dead_code: true, conditionals: true, comparisons: true,
evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true,
if_return: true, join_vars: true, cascade: true, side_effects: true
}
input: {
function foo(x) {
if (x) {
return bar();
not_called1();
} else {
return baz();
not_called2();
}
function bar() { return 7; }
return not_reached;
function UnusedFunction() {}
function baz() { return 8; }
}
}
expect: {
function foo(x) {
return x ? bar() : baz();
function bar() { return 7 }
function baz() { return 8 }
}
}
expect_warnings: [
'WARN: Dropping unreachable code [test/compress/issue-1034.js:11,16]',
"WARN: Dropping unreachable code [test/compress/issue-1034.js:14,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:17,12]",
"WARN: Dropping unused function UnusedFunction [test/compress/issue-1034.js:18,21]"
]
}
non_hoisted_function_after_return_2a: {
options = {
hoist_funs: false, dead_code: true, conditionals: true, comparisons: true,
evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true,
if_return: true, join_vars: true, cascade: true, side_effects: true,
collapse_vars: false
}
input: {
function foo(x) {
if (x) {
return bar(1);
var a = not_called(1);
} else {
return bar(2);
var b = not_called(2);
}
var c = bar(3);
function bar(x) { return 7 - x; }
function nope() {}
return b || c;
}
}
expect: {
// NOTE: Output is correct, but suboptimal. Not a regression. Can be improved in future.
// This output is run through non_hoisted_function_after_return_2b with same flags.
function foo(x) {
if (x) {
return bar(1);
} else {
return bar(2);
var b;
}
var c = bar(3);
function bar(x) {
return 7 - x;
}
return b || c;
}
}
expect_warnings: [
"WARN: Dropping unreachable code [test/compress/issue-1034.js:48,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:48,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:51,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:51,16]",
"WARN: Dropping unused variable a [test/compress/issue-1034.js:48,20]",
"WARN: Dropping unused function nope [test/compress/issue-1034.js:55,21]"
]
}
non_hoisted_function_after_return_2b: {
options = {
hoist_funs: false, dead_code: true, conditionals: true, comparisons: true,
evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true,
if_return: true, join_vars: true, cascade: true, side_effects: true,
collapse_vars: false
}
input: {
// Note: output of test non_hoisted_function_after_return_2a run through compress again
function foo(x) {
if (x) {
return bar(1);
} else {
return bar(2);
var b;
}
var c = bar(3);
function bar(x) {
return 7 - x;
}
return b || c;
}
}
expect: {
// the output we would have liked to see from non_hoisted_function_after_return_2a
function foo(x) {
return bar(x ? 1 : 2);
function bar(x) { return 7 - x; }
}
}
expect_warnings: [
// Notice that some warnings are repeated by multiple compress passes.
// Not a regression. There is room for improvement here.
// Warnings should be cached and only output if unique.
"WARN: Dropping unreachable code [test/compress/issue-1034.js:100,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:100,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:100,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:100,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:100,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:100,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:100,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:100,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:102,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:102,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:106,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:100,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:100,16]",
"WARN: Dropping unused variable b [test/compress/issue-1034.js:100,20]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:102,16]"
]
}

View File

@@ -88,6 +88,14 @@ function run_compress_tests() {
var options = U.defaults(test.options, { var options = U.defaults(test.options, {
warnings: false warnings: false
}); });
var warnings_emitted = [];
var original_warn_function = U.AST_Node.warn_function;
if (test.expect_warnings) {
U.AST_Node.warn_function = function(text) {
warnings_emitted.push("WARN: " + text);
};
options.warnings = true;
}
var cmp = new U.Compressor(options, true); var cmp = new U.Compressor(options, true);
var output_options = test.beautify || {}; var output_options = test.beautify || {};
var expect; var expect;
@@ -117,6 +125,24 @@ function run_compress_tests() {
failures++; failures++;
failed_files[file] = 1; failed_files[file] = 1;
} }
else if (test.expect_warnings) {
U.AST_Node.warn_function = original_warn_function;
var expected_warnings = make_code(test.expect_warnings, {
beautify: false,
quote_style: 2, // force double quote to match JSON
});
var actual_warnings = JSON.stringify(warnings_emitted);
actual_warnings = actual_warnings.split(process.cwd() + "/").join("");
if (expected_warnings != actual_warnings) {
log("!!! failed\n---INPUT---\n{input}\n---EXPECTED WARNINGS---\n{expected_warnings}\n---ACTUAL WARNINGS---\n{actual_warnings}\n\n", {
input: input_code,
expected_warnings: expected_warnings,
actual_warnings: actual_warnings,
});
failures++;
failed_files[file] = 1;
}
}
} }
var tests = parse_test(path.resolve(dir, file)); var tests = parse_test(path.resolve(dir, file));
for (var i in tests) if (tests.hasOwnProperty(i)) { for (var i in tests) if (tests.hasOwnProperty(i)) {
@@ -168,7 +194,7 @@ function parse_test(file) {
} }
if (node instanceof U.AST_LabeledStatement) { if (node instanceof U.AST_LabeledStatement) {
assert.ok( assert.ok(
node.label.name == "input" || node.label.name == "expect" || node.label.name == "expect_exact", ["input", "expect", "expect_exact", "expect_warnings"].indexOf(node.label.name) >= 0,
tmpl("Unsupported label {name} [{line},{col}]", { tmpl("Unsupported label {name} [{line},{col}]", {
name: node.label.name, name: node.label.name,
line: node.label.start.line, line: node.label.start.line,