From: kzc Date: Sun, 10 Apr 2016 19:41:38 +0000 (-0400) Subject: Fix warnings for referenced non-hoisted functions. X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=3907a5e3b2c40904f2cb333ede23d6d59bb48c13;p=UglifyJS.git Fix warnings for referenced non-hoisted functions. Fixes #1034 Also added `expect_warnings` functionality to test framework. --- diff --git a/lib/compress.js b/lib/compress.js index d42f84d1..e47be97b 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -801,7 +801,9 @@ merge(Compressor.prototype, { }; 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){ if (node instanceof AST_Definitions) { compressor.warn("Declarations in unreachable code! [{file}:{line},{col}]", node.start); diff --git a/test/compress/issue-1034.js b/test/compress/issue-1034.js new file mode 100644 index 00000000..039af2ed --- /dev/null +++ b/test/compress/issue-1034.js @@ -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]" + ] +} + diff --git a/test/run-tests.js b/test/run-tests.js index fcb1b375..b4333b7a 100755 --- a/test/run-tests.js +++ b/test/run-tests.js @@ -88,6 +88,14 @@ function run_compress_tests() { var options = U.defaults(test.options, { 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 output_options = test.beautify || {}; var expect; @@ -117,6 +125,24 @@ function run_compress_tests() { failures++; 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)); for (var i in tests) if (tests.hasOwnProperty(i)) { @@ -168,7 +194,7 @@ function parse_test(file) { } if (node instanceof U.AST_LabeledStatement) { 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}]", { name: node.label.name, line: node.label.start.line,