diff --git a/lib/compress.js b/lib/compress.js index 4e45df92..7f3574de 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -1273,7 +1273,25 @@ merge(Compressor.prototype, { def(AST_Constant, return_false); def(AST_This, return_false); + function has_pure_annotation(node) { + if (node.pure !== undefined) return node.pure; + var pure = false; + var comments, last_comment; + if (node.start + && (comments = node.start.comments_before) + && comments.length + && /[@#]__PURE__/.test((last_comment = comments[comments.length - 1]).value)) { + last_comment.value = last_comment.value.replace(/[@#]__PURE__/g, ' '); + pure = true; + } + return node.pure = pure; + } + def(AST_Call, function(compressor){ + if (compressor.option("side_effects") && has_pure_annotation(this)) { + compressor.warn("Dropping __PURE__ call [{file}:{line},{col}]", this.start); + return false; + } var pure = compressor.option("pure_funcs"); if (!pure) return true; if (typeof pure == "function") return pure(this); diff --git a/test/compress/issue-1261.js b/test/compress/issue-1261.js new file mode 100644 index 00000000..eedf4d4f --- /dev/null +++ b/test/compress/issue-1261.js @@ -0,0 +1,60 @@ +pure_function_calls: { + options = { + evaluate : true, + conditionals : true, + comparisons : true, + side_effects : true, + booleans : true, + unused : true, + if_return : true, + join_vars : true, + cascade : true, + negate_iife : true, + } + input: { + // pure top-level IIFE will be dropped + // @__PURE__ - comment + (function() { + console.log("iife0"); + })(); + + // pure top-level IIFE assigned to unreferenced var will not be dropped + var iife1 = /*@__PURE__*/(function() { + console.log("iife1"); + function iife1() {} + return iife1; + })(); + + (function(){ + // pure IIFE in function scope assigned to unreferenced var will be dropped + var iife2 = /*#__PURE__*/(function() { + console.log("iife2"); + function iife2() {} + return iife2; + })(); + })(); + + // comment #__PURE__ comment + bar(), baz(), quux(); + a.b(), /* @__PURE__ */ c.d.e(), f.g(); + } + expect: { + var iife1 = function() { + console.log("iife1"); + function iife1() {} + return iife1; + }(); + + baz(), quux(); + a.b(), f.g(); + } + expect_warnings: [ + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:17,8]", + "WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:17,8]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:30,37]", + "WARN: Dropping unused variable iife2 [test/compress/issue-1261.js:30,16]", + "WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:28,8]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:38,8]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:39,31]" + ] +} diff --git a/test/mocha/minify.js b/test/mocha/minify.js index 70cf73ae..8fe1565f 100644 --- a/test/mocha/minify.js +++ b/test/mocha/minify.js @@ -95,4 +95,19 @@ describe("minify", function() { assert.strictEqual(code, "var a=function(n){return n};"); }); }); + + describe("#__PURE__", function() { + it("should drop #__PURE__ hint after use", function() { + var result = Uglify.minify('//@__PURE__ comment1 #__PURE__ comment2\n foo(), bar();', { + fromString: true, + output: { + comments: "all", + beautify: false, + } + }); + var code = result.code; + assert.strictEqual(code, "// comment1 comment2\nbar();"); + }); + }); + });