/* * Copyright 2014 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import static com.google.javascript.jscomp.TypeValidator.TYPE_MISMATCH_WARNING; import com.google.javascript.jscomp.CompilerOptions.LanguageMode; /** * Test case for {@link Es6RewriteBlockScopedDeclaration}. * * @author moz@google.com (Michael Zhou) */ public final class Es6RewriteBlockScopedDeclarationTest extends CompilerTestCase { @Override public void setUp() { setAcceptedLanguage(LanguageMode.ECMASCRIPT_2015); runTypeCheckAfterProcessing = true; compareJsDoc = true; } @Override protected CompilerOptions getOptions() { CompilerOptions options = super.getOptions(); options.setLanguageOut(LanguageMode.ECMASCRIPT3); return options; } @Override protected CompilerPass getProcessor(Compiler compiler) { return new Es6RewriteBlockScopedDeclaration(compiler); } @Override protected int getNumRepetitions() { return 1; } public void testSimple() { test("let x = 3;", "var x = 3;"); test("const x = 3;", "/** @const */ var x = 3;"); test("const x = 1, y = 2;", "/** @const */ var x = 1; /** @const */ var y = 2;"); test("const a = 0; a;", "/** @const */ var a = 0; a;"); test("if (a) { let x; }", "if (a) { var x; }"); test("function f() { const x = 3; }", "function f() { /** @const */ var x = 3; }"); } public void testLetShadowing() { test( LINE_JOINER.join( "function f() {", " var x = 1;", " if (a) {", " let x = 2;", " x = function() { return x; };", " }", " return x;", "}"), LINE_JOINER.join( "function f() {", " var x = 1;", " if (a) {", " var x$0 = 2;", " x$0 = function() { return x$0; };", " }", " return x;", "}")); test( LINE_JOINER.join( "function f() {", " const x = 3;", " if (true) {", " let x;", " }", "}"), LINE_JOINER.join( "function f() {", " /** @const */ var x = 3;", " if (true) {", " var x$0;", " }", "}")); test( LINE_JOINER.join( "function f() {", " var x = 1;", " if (a) {", " var g = function() { return x; };", " let x = 2;", " return g();", " }", "}"), LINE_JOINER.join( "function f() {", " var x = 1;", " if (a) {", " var g = function() { return x$0; };", " var x$0 = 2;", " return g();", " }", "}")); test( LINE_JOINER.join( "var x = 2;", "function f() {", " x = 1;", " if (a) {", " let x = 2;", " }", "}"), LINE_JOINER.join( "var x = 2;", "function f() {", " x = 1;", " if (a) {", " var x$0 = 2;", " }", "}")); test( LINE_JOINER.join( "function f() {", " {", " let inner = 2;", " }", " use(inner)", "}"), LINE_JOINER.join( "function f() {", " {", " var inner$0 = 2;", " }", " use(inner)", "}")); } public void testNonUniqueLet() { test( LINE_JOINER.join( "function f() {", " var x = 1;", " if (a) {", " let x = 2;", " assert(x === 2);", " }", " if (b) {", " let x;", " assert(x === undefined);", " }", " assert(x === 1);", "}"), LINE_JOINER.join( "function f() {", " var x = 1;", " if (a) {", " var x$0 = 2;", " assert(x$0 === 2);", " }", " if (b) {", " var x$1;", " assert(x$1 === undefined);", " }", " assert(x === 1);", "}")); test( LINE_JOINER.join( "function f() {", " if (a) {", " let x = 2;", " assert(x === 2);", " if (b) {", " let x;", " assert(x === undefined);", " }", " }", "}"), LINE_JOINER.join( "function f() {", " if (a) {", " var x = 2;", " assert(x === 2);", " if (b) {", " var x$0;", " assert(x$0 === undefined);", " }", " }", "}")); } public void testRenameConflict() { test( LINE_JOINER.join( "function f() {", " let x = 1;", " let x$0 = 2;", " {", " let x = 3;", " }", "}"), LINE_JOINER.join( "function f() {", " var x = 1;", " var x$0 = 2;", " {", " var x$1 = 3;", " }", "}")); } public void testForOfLoop() { test( LINE_JOINER.join( "function f() {", " let x = 5;", " for (let x of [1,2,3]) {", " console.log(x);", " }", " console.log(x);", "}"), LINE_JOINER.join( "function f() {", " var x = 5;", " for(var x$0 of [1,2,3]) {", " console.log(x$0);", " }", " console.log(x);", "}")); test( LINE_JOINER.join( "function f() {", " let x = 5;", " for (let x of [1,2,3]) {", " let x = 123;", " console.log(x);", " }", " console.log(x);", "}"), LINE_JOINER.join( "function f() {", " var x = 5;", " for(var x$0 of [1,2,3]) {", " var x$1 = 123;", " console.log(x$1);", " }", " console.log(x);", "}")); } public void testForLoop() { test( LINE_JOINER.join( "function f() {", " const y = 0;", " for (let x = 0; x < 10; x++) {", " const y = x * 2;", " const z = y;", " }", " console.log(y);", "}"), LINE_JOINER.join( "function f() {", " /** @const */ var y = 0;", " for (var x = 0; x < 10; x++) {", " /** @const */ var y$0 = x * 2;", " /** @const */ var z = y$0;", " }", " console.log(y);", "}")); test( LINE_JOINER.join( "for (let i in [0, 1]) {", " function f() {", " let i = 0;", " if (true) {", " let i = 1;", " }", " }", "}"), LINE_JOINER.join( "for (var i in [0, 1]) {", " var f = function() {", " var i = 0;", " if (true) {", " var i$0 = 1;", " }", " }", "}")); test( "for (let i = 0;;) { let i; }", "for (var i = 0;;) { var i$0 = undefined; }"); test( "for (let i = 0;;) {} let i;", "for (var i$0 = 0;;) {} var i;"); test( LINE_JOINER.join( "for (var x in y) {", " /** @type {number} */", " let i;", "}"), LINE_JOINER.join( "for (var x in y) {", " /** @type {number} */", " var i = /** @type {?} */ (undefined);", "}")); test(LINE_JOINER.join( "for (const i in [0, 1]) {", " function f() {", " let i = 0;", " if (true) {", " let i = 1;", " }", " }", "}"), LINE_JOINER.join( "for (var i in [0, 1]) {", " var f = function() {", " var i = 0;", " if (true) {", " var i$0 = 1;", " }", " }", "}")); } public void testFunctionInLoop() { test( LINE_JOINER.join( "for (var x of y) {", " function f() {", " let z;", " }", "}"), LINE_JOINER.join( "for (var x of y) {", " var f = function() {", " var z;", " };", "}")); } public void testLoopClosure() { test( LINE_JOINER.join( "const arr = [];", "for (let i = 0; i < 10; i++) {", " arr.push(function() { return i; });", "}"), LINE_JOINER.join( "/** @const */ var arr = [];", "var $jscomp$loop$0 = {};", "$jscomp$loop$0.i = 0;", "for (; $jscomp$loop$0.i < 10;", " $jscomp$loop$0 = {i: $jscomp$loop$0.i}, $jscomp$loop$0.i++) {", " arr.push((function($jscomp$loop$0) {", " return function() { return $jscomp$loop$0.i; };", " })($jscomp$loop$0));", "}")); test( LINE_JOINER.join( "const arr = [];", "for (let i = 0; i < 10; i++) {", " let y = i;", " arr.push(function() { return y; });", "}"), LINE_JOINER.join( "/** @const */ var arr = [];", "var $jscomp$loop$0 = {};", "var i = 0;", "for (; i < 10; $jscomp$loop$0 = {y: $jscomp$loop$0.y}, i++) {", " $jscomp$loop$0.y = i;", " arr.push((function($jscomp$loop$0) {", " return function() { return $jscomp$loop$0.y; };", " })($jscomp$loop$0));", "}")); test( LINE_JOINER.join( "const arr = [];", "while (true) {", " let i = 0;", " arr.push(function() { return i; });", "}"), LINE_JOINER.join( "/** @const */ var arr = [];", "var $jscomp$loop$0 = {}", "while (true) {", " $jscomp$loop$0.i = 0;", " arr.push((function($jscomp$loop$0) {", " return function() { return $jscomp$loop$0.i; };", " })($jscomp$loop$0));", " $jscomp$loop$0 = {i: $jscomp$loop$0.i}", "}")); test( LINE_JOINER.join( "const arr = [];", "for (let i = 0; i < 10; i++) {", " let y = i;", " arr.push(function() { return y + i; });", "}"), LINE_JOINER.join( "/** @const */ var arr = [];", "var $jscomp$loop$0 = {};", "$jscomp$loop$0.i = 0;", "for (; $jscomp$loop$0.i < 10;", " $jscomp$loop$0 = {y: $jscomp$loop$0.y, i: $jscomp$loop$0.i},", " $jscomp$loop$0.i++) {", " $jscomp$loop$0.y = $jscomp$loop$0.i;", " arr.push((function($jscomp$loop$0) {", " return function() {", " return $jscomp$loop$0.y + $jscomp$loop$0.i;", " };", " }($jscomp$loop$0)));", "}")); // Renamed inner i test( LINE_JOINER.join( "const arr = [];", "let x = 0", "for (let i = 0; i < 10; i++) {", " let i = x + 1;", " arr.push(function() { return i + i; });", " x++;", "}"), LINE_JOINER.join( "/** @const */ var arr = [];", "var x = 0", "var $jscomp$loop$1 = {};", "var i = 0;", "for (; i < 10; $jscomp$loop$1 = {i$0: $jscomp$loop$1.i$0}, i++) {", " $jscomp$loop$1.i$0 = x + 1;", " arr.push((function($jscomp$loop$1) {", " return function() {", " return $jscomp$loop$1.i$0 + $jscomp$loop$1.i$0;", " };", " }($jscomp$loop$1)));", " x++;", "}")); // Renamed, but both closures reference the inner i test( LINE_JOINER.join( "const arr = [];", "let x = 0", "for (let i = 0; i < 10; i++) {", " arr.push(function() { return i + i; });", " let i = x + 1;", " arr.push(function() { return i + i; });", " x++;", "}"), LINE_JOINER.join( "/** @const */ var arr = [];", "var x = 0", "var $jscomp$loop$1 = {};", "var i = 0;", "for (; i < 10; $jscomp$loop$1 = {i$0: $jscomp$loop$1.i$0}, i++) {", " arr.push((function($jscomp$loop$1) {", " return function() {", " return $jscomp$loop$1.i$0 + $jscomp$loop$1.i$0;", " };", " }($jscomp$loop$1)));", " $jscomp$loop$1.i$0 = x + 1;", " arr.push((function($jscomp$loop$1) {", " return function() {", " return $jscomp$loop$1.i$0 + $jscomp$loop$1.i$0;", " };", " }($jscomp$loop$1)));", " x++;", "}")); // Renamed distinct captured variables test( LINE_JOINER.join( "for (let i = 0; i < 10; i++) {", " if (true) {", " let i = x - 1;", " arr.push(function() { return i + i; });", " }", " let i = x + 1;", " arr.push(function() { return i + i; });", " x++;", "}"), LINE_JOINER.join( "var $jscomp$loop$2 = {};", "var i = 0;", "for (; i < 10;", " $jscomp$loop$2 = {i$0: $jscomp$loop$2.i$0, i$1: $jscomp$loop$2.i$1}, i++) {", " if (true) {", " $jscomp$loop$2.i$0 = x - 1;", " arr.push((function($jscomp$loop$2) {", " return function() { return $jscomp$loop$2.i$0 + $jscomp$loop$2.i$0; };", " })($jscomp$loop$2));", " }", " $jscomp$loop$2.i$1 = x + 1;", " arr.push((function($jscomp$loop$2) {", " return function() { return $jscomp$loop$2.i$1 + $jscomp$loop$2.i$1; };", " })($jscomp$loop$2));", " x++;", "}")); // Preserve type annotation test( "for (;;) { /** @type {number} */ let x = 3; var f = function() { return x; } }", LINE_JOINER.join( "var $jscomp$loop$0 = {};", "for (;;$jscomp$loop$0 = {x: $jscomp$loop$0.x}) {", " /** @type {number} */ $jscomp$loop$0.x = 3;", " var f = function($jscomp$loop$0) {", " return function() { return $jscomp$loop$0.x}", " }($jscomp$loop$0);", "}")); // Preserve inline type annotation test( "for (;;) { let /** number */ x = 3; var f = function() { return x; } }", LINE_JOINER.join( "var $jscomp$loop$0 = {};", "for (;;$jscomp$loop$0 = {x: $jscomp$loop$0.x}) {", " /** @type {number} */ $jscomp$loop$0.x = 3;", " var f = function($jscomp$loop$0) {", " return function() { return $jscomp$loop$0.x}", " }($jscomp$loop$0);", "}")); // Preserve inline type annotation and constancy test( "for (;;) { const /** number */ x = 3; var f = function() { return x; } }", LINE_JOINER.join( "var $jscomp$loop$0 = {};", "for (;;$jscomp$loop$0 = {x: $jscomp$loop$0.x}) {", " /** @const @type {number} */ $jscomp$loop$0.x = 3;", " var f = function($jscomp$loop$0) {", " return function() { return $jscomp$loop$0.x}", " }($jscomp$loop$0);", "}")); // Preserve inline type annotation on declaration lists test(LINE_JOINER.join( "for (;;) { let /** number */ x = 3, /** number */ y = 4;", "var f = function() { return x + y; } }"), LINE_JOINER.join( "var $jscomp$loop$0 = {};", "for (;;$jscomp$loop$0 = {x: $jscomp$loop$0.x, y: $jscomp$loop$0.y}) {", " /** @type {number} */ $jscomp$loop$0.x = 3;", " /** @type {number} */ $jscomp$loop$0.y = 4;", " var f = function($jscomp$loop$0) {", " return function() { return $jscomp$loop$0.x + $jscomp$loop$0.y}", " }($jscomp$loop$0);", "}")); // Preserve inline type annotation and constancy on declaration lists test(LINE_JOINER.join( "for (;;) { const /** number */ x = 3, /** number */ y = 4;", "var f = function() { return x + y; } }"), LINE_JOINER.join( "var $jscomp$loop$0 = {};", "for (;;$jscomp$loop$0 = {x: $jscomp$loop$0.x, y: $jscomp$loop$0.y}) {", " /** @const @type {number} */ $jscomp$loop$0.x = 3;", " /** @const @type {number} */ $jscomp$loop$0.y = 4;", " var f = function($jscomp$loop$0) {", " return function() { return $jscomp$loop$0.x + $jscomp$loop$0.y}", " }($jscomp$loop$0);", "}")); // No-op, vars don't need transpilation testSame("for (;;) { var /** number */ x = 3; var f = function() { return x; } }"); test( LINE_JOINER.join( "var i;", "for (i = 0;;) {", " let x = 0;", " var f = function() { x; };", "}"), LINE_JOINER.join( "var i;", "var $jscomp$loop$0={};", "i = 0;", "for(;;$jscomp$loop$0 = {x: $jscomp$loop$0.x}) {", " $jscomp$loop$0.x = 0;", " var f = (function($jscomp$loop$0) {", " return function() { $jscomp$loop$0.x; };", " })($jscomp$loop$0);", "}")); test( LINE_JOINER.join("for (foo();;) {", " let x = 0;", " var f = function() { x; };", "}"), LINE_JOINER.join( "var $jscomp$loop$0={};", "foo();", "for(;;$jscomp$loop$0 = {x: $jscomp$loop$0.x}) {", " $jscomp$loop$0.x = 0;", " var f = (function($jscomp$loop$0) {", " return function() { $jscomp$loop$0.x; };", " })($jscomp$loop$0);", "}")); test( LINE_JOINER.join( "for (function foo() {};;) {", " let x = 0;", " var f = function() { x; };", "}"), LINE_JOINER.join( "var $jscomp$loop$0={};", "(function foo() {});", "for(;;$jscomp$loop$0 = {x: $jscomp$loop$0.x}) {", " $jscomp$loop$0.x = 0;", " var f = (function($jscomp$loop$0) {", " return function() { $jscomp$loop$0.x; };", " })($jscomp$loop$0);", "}")); test( LINE_JOINER.join( "for (;;) {", " let x;", " foo(function() { return x; });", " x = 5;", "}"), LINE_JOINER.join( "var $jscomp$loop$0 = {};", "for(;;$jscomp$loop$0 = {x: $jscomp$loop$0.x}) {", " $jscomp$loop$0.x = undefined;", " foo(function($jscomp$loop$0) {", " return function() {", " return $jscomp$loop$0.x;", " };", " }($jscomp$loop$0));", " $jscomp$loop$0.x=5;", "}")); } public void testLoopClosureCommaInBody() { test( LINE_JOINER.join( "const arr = [];", "let j = 0;", "for (let i = 0; i < 10; i++) {", " let i, j = 0;", " arr.push(function() { return i + j; });", "}"), LINE_JOINER.join( "/** @const */ var arr = [];", "var j = 0;", "var $jscomp$loop$1 = {};", "var i = 0;", "for (; i < 10; $jscomp$loop$1 = {i$0: $jscomp$loop$1.i$0,", " j: $jscomp$loop$1.j}, i++) {", " $jscomp$loop$1.i$0 = undefined;", " $jscomp$loop$1.j = 0;", " arr.push((function($jscomp$loop$1) {", " return function() { return $jscomp$loop$1.i$0 + $jscomp$loop$1.j; };", " })($jscomp$loop$1));", "}")); } public void testLoopClosureCommaInIncrement() { test( LINE_JOINER.join( "const arr = [];", "let j = 0;", "for (let i = 0; i < 10; i++, j++) {", " arr.push(function() { return i + j; });", "}"), LINE_JOINER.join( "/** @const */ var arr = [];", "var j = 0;", "var $jscomp$loop$0 = {};", "$jscomp$loop$0.i = 0;", "for (; $jscomp$loop$0.i < 10;", " $jscomp$loop$0 = {i: $jscomp$loop$0.i}, ($jscomp$loop$0.i++, j++)) {", " arr.push((function($jscomp$loop$0) {", " return function() { return $jscomp$loop$0.i + j; };", " })($jscomp$loop$0));", "}")); } public void testLoopClosureCommaInInitializerAndIncrement() { test( LINE_JOINER.join( "const arr = [];", "for (let i = 0, j = 0; i < 10; i++, j++) {", " arr.push(function() { return i + j; });", "}"), LINE_JOINER.join( "/** @const */ var arr = [];", "var $jscomp$loop$0 = {};", "$jscomp$loop$0.i = 0;", "$jscomp$loop$0.j = 0;", "for (; $jscomp$loop$0.i < 10;", " $jscomp$loop$0 = {i: $jscomp$loop$0.i, j : $jscomp$loop$0.j},", " ($jscomp$loop$0.i++, $jscomp$loop$0.j++)) {", " arr.push((function($jscomp$loop$0) {", " return function() { return $jscomp$loop$0.i + $jscomp$loop$0.j; };", " })($jscomp$loop$0));", "}")); test( LINE_JOINER.join( "const arr = [];", "for (let i = 0, j = 0; i < 10; i++, j++) {", " arr.push(function() { return j; });", "}"), LINE_JOINER.join( "/** @const */ var arr = [];", "var $jscomp$loop$0 = {};", "var i = 0;", "$jscomp$loop$0.j = 0;", "for (; i < 10; $jscomp$loop$0 = {j : $jscomp$loop$0.j},", " (i++, $jscomp$loop$0.j++)) {", " arr.push((function($jscomp$loop$0) {", " return function() { return $jscomp$loop$0.j; };", " })($jscomp$loop$0));", "}")); } public void testLoopClosureMutated() { test( LINE_JOINER.join( "const arr = [];", "for (let i = 0; i < 10; i++) {", " arr.push(function() { return ++i; });", "}"), LINE_JOINER.join( "/** @const */ var arr = [];", "var $jscomp$loop$0 = {};", "$jscomp$loop$0.i = 0;", "for (; $jscomp$loop$0.i < 10;", " $jscomp$loop$0 = {i: $jscomp$loop$0.i}, $jscomp$loop$0.i++) {", " arr.push((function($jscomp$loop$0) {", " return function() {", " return ++$jscomp$loop$0.i;", " };", " }($jscomp$loop$0)));", "}")); test( LINE_JOINER.join( "const arr = [];", "for (let i = 0; i < 10; i++) {", " arr.push(function() { return i; });", " i += 100;", "}"), LINE_JOINER.join( "/** @const */ var arr = [];", "var $jscomp$loop$0 = {};", "$jscomp$loop$0.i = 0;", "for (; $jscomp$loop$0.i < 10;", " $jscomp$loop$0 = {i: $jscomp$loop$0.i}, $jscomp$loop$0.i++) {", " arr.push((function($jscomp$loop$0) {", " return function() {", " return $jscomp$loop$0.i;", " };", " }($jscomp$loop$0)));", " $jscomp$loop$0.i += 100;", "}")); } public void testLoopClosureWithNestedInnerFunctions() { test(LINE_JOINER.join( "for (let i = 0; i < 10; i++) {", " later(function(ctr) {", " (function() { return use(i); })();", " });", "}"), LINE_JOINER.join( "var $jscomp$loop$0 = {};", "$jscomp$loop$0.i = 0;", "for (; $jscomp$loop$0.i < 10;", " $jscomp$loop$0 = {i: $jscomp$loop$0.i}, $jscomp$loop$0.i++) {", " later((function($jscomp$loop$0) {", " return function(ctr) {", " (function() { return use($jscomp$loop$0.i); })();", " };", " })($jscomp$loop$0));", "}")); test(LINE_JOINER.join( "for (let i = 0; i < 10; i++) {", " var f = function() {", " return function() {", " return i;", " };", " };", "}"), LINE_JOINER.join( "var $jscomp$loop$0 = {};", "$jscomp$loop$0.i = 0;", "for (; $jscomp$loop$0.i < 10;", " $jscomp$loop$0 = {i: $jscomp$loop$0.i}, $jscomp$loop$0.i++) {", " var f = function($jscomp$loop$0) {", " return function() {", " return function() {", " return $jscomp$loop$0.i;", " };", " };", " }($jscomp$loop$0);", "}")); test(LINE_JOINER.join( "use(function() {", " later(function(ctr) {", " for (let i = 0; i < 10; i++) {", " (function() { return use(i); })();", " }", " });", "});"), LINE_JOINER.join( "use(function() {", " later(function(ctr) {", " var $jscomp$loop$0 = {};", " $jscomp$loop$0.i = 0;", " for (; $jscomp$loop$0.i < 10;", " $jscomp$loop$0 = {i: $jscomp$loop$0.i}, $jscomp$loop$0.i++) {", " (function($jscomp$loop$0) {", " return function() { return use($jscomp$loop$0.i); }", " })($jscomp$loop$0)();", " }", " });", "});")); } public void testNestedLoop() { test( LINE_JOINER.join( "function f() {", " let arr = [];", " for (let i = 0; i < 10; i++) {", " for (let j = 0; j < 10; j++) {", " arr.push(function() { return j++ + i++; });", " arr.push(function() { return j++ + i++; });", " }", " }", "}"), LINE_JOINER.join( "function f() {", " var arr = [];", " var $jscomp$loop$1 = {};", " $jscomp$loop$1.i = 0;", " for (; $jscomp$loop$1.i < 10;", " $jscomp$loop$1 = {i: $jscomp$loop$1.i}, $jscomp$loop$1.i++) {", " var $jscomp$loop$0 = {};", " $jscomp$loop$0.j = 0;", " for (; $jscomp$loop$0.j < 10;", " $jscomp$loop$0 = {j: $jscomp$loop$0.j}, $jscomp$loop$0.j++) {", " arr.push((function($jscomp$loop$0, $jscomp$loop$1) {", " return function() {", " return $jscomp$loop$0.j++ + $jscomp$loop$1.i++;", " };", " }($jscomp$loop$0, $jscomp$loop$1)));", " arr.push((function($jscomp$loop$0, $jscomp$loop$1) {", " return function() {", " return $jscomp$loop$0.j++ + $jscomp$loop$1.i++;", " };", " }($jscomp$loop$0, $jscomp$loop$1)));", " }", " }", "}")); // Renamed inner i test( LINE_JOINER.join( "function f() {", " let arr = [];", " for (let i = 0; i < 10; i++) {", " arr.push(function() { return i++ + i++; });", " for (let i = 0; i < 10; i++) {", " arr.push(function() { return i++ + i++; });", " }", " }", "}"), LINE_JOINER.join( "function f() {", " var arr = [];", " var $jscomp$loop$1 = {};", " $jscomp$loop$1.i = 0;", " for (; $jscomp$loop$1.i < 10;", " $jscomp$loop$1 = {i: $jscomp$loop$1.i}, $jscomp$loop$1.i++) {", " arr.push((function($jscomp$loop$1) {", " return function() {", " return $jscomp$loop$1.i++ + $jscomp$loop$1.i++;", " };", " }($jscomp$loop$1)));", " var $jscomp$loop$2 = {};", " $jscomp$loop$2.i$0 = 0;", " for (; $jscomp$loop$2.i$0 < 10;", " $jscomp$loop$2 = {i$0: $jscomp$loop$2.i$0}, $jscomp$loop$2.i$0++) {", " arr.push((function($jscomp$loop$2) {", " return function() {", " return $jscomp$loop$2.i$0++ + $jscomp$loop$2.i$0++;", " };", " }($jscomp$loop$2)));", " }", " }", "}")); } public void testLabeledLoop() { test( LINE_JOINER.join( "label1:", "label2:", "for (let x = 1;;) {", " function f() {", " return x;", " }", "}"), LINE_JOINER.join( "var $jscomp$loop$0 = {};", "$jscomp$loop$0.x = 1;", "label1:", "label2:", "for (;; $jscomp$loop$0 = {x: $jscomp$loop$0.x}) {", " var f = function($jscomp$loop$0) {", " return function f() {", " return $jscomp$loop$0.x;", " }", " }($jscomp$loop$0);", "}")); } public void testForInAndForOf() { test( LINE_JOINER.join( "const arr = [];", "for (let i in [0, 1]) {", " arr.push(function() { return i; });", "}"), LINE_JOINER.join( "/** @const */ var arr = [];", "var $jscomp$loop$0 = {};", "for (var i in [0, 1]) {", " $jscomp$loop$0.i = i;", " arr.push((function($jscomp$loop$0) {", " return function() { return $jscomp$loop$0.i; };", " })($jscomp$loop$0));", " $jscomp$loop$0 = {i: $jscomp$loop$0.i};", "}")); test( LINE_JOINER.join( "const arr = [];", "for (let i of [0, 1]) {", " let i = 0;", " arr.push(function() { return i; });", "}"), LINE_JOINER.join( "/** @const */ var arr = [];", "var $jscomp$loop$1 = {};", "for (var i of [0, 1]) {", " $jscomp$loop$1.i$0 = 0;", " arr.push((function($jscomp$loop$1) {", " return function() { return $jscomp$loop$1.i$0; };", " })($jscomp$loop$1));", " $jscomp$loop$1 = {i$0: $jscomp$loop$1.i$0}", "}")); test( LINE_JOINER.join( "for (;;) {", " let a = getArray();", " f = function() {", " for (var x in use(a)) {", " f(a);", " a.push(x);", " return x;", " }", " }", "}"), LINE_JOINER.join( "var $jscomp$loop$0 = {};", "for (;; $jscomp$loop$0 = {a: $jscomp$loop$0.a}) {", " $jscomp$loop$0.a = getArray();", " f = (function($jscomp$loop$0) {", " return function() {", " for (var x in use($jscomp$loop$0.a)) {", " f($jscomp$loop$0.a);", " $jscomp$loop$0.a.push(x);", " return x;", " }", " };", " }($jscomp$loop$0));", "}")); } public void testDoWhileForOfCapturedLet() { test( LINE_JOINER.join( "const arr = [];", "do {", " let special = 99;", " for (let i of [0, 1, special, 3, 4, 5]) {", " i = Number(i);", " arr.push(function() { return i++; });", " arr.push(function() { return i + special; });", " }", "} while (false);"), LINE_JOINER.join( "/** @const */ var arr = [];", "var $jscomp$loop$1 = {};", "do {", " $jscomp$loop$1.special = 99;", " var $jscomp$loop$0 = {};", " for (var i of [0, 1, $jscomp$loop$1.special, 3, 4, 5]) {", " $jscomp$loop$0.i = i", " $jscomp$loop$0.i = Number($jscomp$loop$0.i);", " arr.push((function($jscomp$loop$0) {", " return function() { return $jscomp$loop$0.i++; };", " }($jscomp$loop$0)));", " arr.push((function($jscomp$loop$0, $jscomp$loop$1) {", " return function() { return $jscomp$loop$0.i + $jscomp$loop$1.special; };", " }($jscomp$loop$0, $jscomp$loop$1)));", " $jscomp$loop$0 = {i: $jscomp$loop$0.i};", " }", " $jscomp$loop$1 = {special: $jscomp$loop$1.special};", "} while (false);")); } // https://github.com/google/closure-compiler/issues/1124 public void testFunctionsInLoop() { test( LINE_JOINER.join( "while (true) {", " let x = null;", " var f = function() {", " x();", " }", "}"), LINE_JOINER.join( "var $jscomp$loop$0 = {};", "while (true) {", " $jscomp$loop$0.x = null;", " var f = function($jscomp$loop$0) {", " return function() {", " ($jscomp$loop$0.x)();", " };", " }($jscomp$loop$0);", " $jscomp$loop$0 = {x:$jscomp$loop$0.x};", "}")); test( LINE_JOINER.join( "while (true) {", " let x = null;", " function f() {", " x();", " }", "}"), LINE_JOINER.join( "var $jscomp$loop$0 = {};", "while (true) {", " $jscomp$loop$0.x = null;", " var f = function($jscomp$loop$0) {", " return function f() {", " ($jscomp$loop$0.x)();", " };", " }($jscomp$loop$0);", " $jscomp$loop$0 = {x:$jscomp$loop$0.x};", "}")); test( LINE_JOINER.join( "while (true) {", " let x = null;", " (function() {", " x();", " })();", "}"), LINE_JOINER.join( "var $jscomp$loop$0 = {};", "while (true) {", " $jscomp$loop$0.x = null;", " (function($jscomp$loop$0) {", " return function () {", " ($jscomp$loop$0.x)();", " };", " })($jscomp$loop$0)();", " $jscomp$loop$0 = {x:$jscomp$loop$0.x};", "}")); } // https://github.com/google/closure-compiler/issues/1557 public void testNormalizeDeclarations() { test(LINE_JOINER.join( "while(true) {", " let x, y;", " function f() {", " x = 1;", " y = 2;", " }", "}"), LINE_JOINER.join( "var $jscomp$loop$0 = {};", "while(true) {", " $jscomp$loop$0.x = undefined;", " var f = function($jscomp$loop$0) {", " return function f() {", " $jscomp$loop$0.x = 1;", " $jscomp$loop$0.y = 2;", " }", " }($jscomp$loop$0);", " $jscomp$loop$0 = {x: $jscomp$loop$0.x, y: $jscomp$loop$0.y};", "}")); test(LINE_JOINER.join( "while(true) {", " let x, y;", " function f() {", " y = 2;", " x = 1;", " }", "}"), LINE_JOINER.join( "var $jscomp$loop$0 = {};", "while(true) {", " $jscomp$loop$0.x = undefined;", " var f = function($jscomp$loop$0) {", " return function f() {", " $jscomp$loop$0.y = 2;", " $jscomp$loop$0.x = 1;", " }", " }($jscomp$loop$0);", " $jscomp$loop$0 = {y: $jscomp$loop$0.y, x: $jscomp$loop$0.x};", "}")); } public void testTypeAnnotationsOnLetConst() { enableTypeCheck(); testWarning("/** @type {number} */ let x = 5; x = 'str';", TYPE_MISMATCH_WARNING); testWarning("let /** number */ x = 5; x = 'str';", TYPE_MISMATCH_WARNING); testWarning("let /** @type {number} */ x = 5; x = 'str';", TYPE_MISMATCH_WARNING); testWarning("/** @type {number} */ const x = 'str';", TYPE_MISMATCH_WARNING); testWarning("const /** number */ x = 'str';", TYPE_MISMATCH_WARNING); testWarning("const /** @type {number} */ x = 'str';", TYPE_MISMATCH_WARNING); testWarning( "const /** @type {string} */ x = 3, /** @type {number} */ y = 3;", TYPE_MISMATCH_WARNING); testWarning( "const /** @type {string} */ x = 'str', /** @type {string} */ y = 3;", TYPE_MISMATCH_WARNING); } public void testDoWhileForOfCapturedLetAnnotated() { enableTypeCheck(); test( LINE_JOINER.join( "while (true) {", " /** @type {number} */ let x = 5;", " (function() { x++; })();", " x = 7;", "}"), null); test( LINE_JOINER.join( "for (/** @type {number} */ let x = 5;;) {", " (function() { x++; })();", " x = 7;", "}"), null); testWarning( LINE_JOINER.join( "while (true) {", " /** @type {number} */ let x = 5;", " (function() { x++; })();", " x = 'str';", "}"), TYPE_MISMATCH_WARNING); testWarning( LINE_JOINER.join( "for (/** @type {number} */ let x = 5;;) {", " (function() { x++; })();", " x = 'str';", "}"), TYPE_MISMATCH_WARNING); } public void testLetForInitializers() { test( LINE_JOINER.join( "{", " let l = [];", " for (var vx = 1, vy = 2, vz = 3; vx < 10; vx++) {", " let lx = vx, ly = vy, lz = vz;", " l.push(function() { return [ lx, ly, lz ]; });", " }", "}"), LINE_JOINER.join( "{", " var l = [];", " var $jscomp$loop$0 = {};", " var vx = 1, vy = 2, vz = 3;", " for (; vx < 10; $jscomp$loop$0 = {lx: $jscomp$loop$0.lx,", " ly: $jscomp$loop$0.ly, lz: $jscomp$loop$0.lz}, vx++){", " $jscomp$loop$0.lx = vx;", " $jscomp$loop$0.ly = vy;", " $jscomp$loop$0.lz = vz;", " l.push(function($jscomp$loop$0) {", " return function() {", " return [ $jscomp$loop$0.lx, $jscomp$loop$0.ly, $jscomp$loop$0.lz ];", " };", " }($jscomp$loop$0));", " }", "}")); } public void testBlockScopedFunctionDeclaration() { test( LINE_JOINER.join( "function f() {", " var x = 1;", " if (a) {", " label0: label1: label2:", " function x() { return x; }", " }", " return x;", "}"), LINE_JOINER.join( "function f() {", " var x = 1;", " if (a) {", " label0: label1: label2:", " var x$0 = function() { return x$0; };", " }", " return x;", "}")); } public void testClass() { test(LINE_JOINER.join( "class C {}", "var c1 = C;", "{", " class C {}", " var c2 = C;", "}", "C === c1;"), LINE_JOINER.join( "class C {}", "var c1 = C;", "{", " class C$0 {}", " var c2 = C$0;", "}", "C === c1;")); } public void testRenameJsDoc() { test(LINE_JOINER.join( "function f() {", " let Foo = 4;", " if (Foo > 2) {", " /** @constructor */", " let Foo = function(){};", " let /** Foo */ f = new Foo;", " return f;", " }", "}"), LINE_JOINER.join( "function f() {", " var Foo = 4;", " if (Foo > 2) {", " /** @constructor */", " var Foo$0 = function(){};", " var /** Foo$0 */ f$1 = new Foo$0;", " return f$1;", " }", "}")); test(LINE_JOINER.join( "function f() {", " let Foo = 4;", " if (Foo > 2) {", " /** @constructor */", " let Foo = function(){};", " let f = /** @param {Foo} p1 @return {Foo} */ function(p1) {", " return new Foo;", " };", " return f;", " }", "}"), LINE_JOINER.join( "function f() {", " var Foo = 4;", " if (Foo > 2) {", " /** @constructor */", " var Foo$0 = function(){};", " var f$1 = /** @param {Foo$0} p1 @return {Foo$0} */ function(p1) {", " return new Foo$0;", " };", " return f$1;", " }", "}")); test(LINE_JOINER.join( "function f() {", " let Foo = 4;", " let Bar = 5;", " if (Foo > 2) {", " /** @constructor */ let Foo = function(){};", " /** @constructor */ let Bar = function(){};", " let /** Foo | Bar */ f = new Foo;", " return f;", " }", "}"), LINE_JOINER.join( "function f() {", " var Foo = 4;", " var Bar = 5;", " if (Foo > 2) {", " /** @constructor */ var Foo$0 = function(){};", " /** @constructor */ var Bar$1 = function(){};", " var /** Foo$0 | Bar$1 */ f$2 = new Foo$0;", " return f$2;", " }", "}")); } public void testCatch() { test("function f(e) { try {} catch (e) { throw e; } }", "function f(e) { try {} catch (e$0) { throw e$0; } }"); test(LINE_JOINER.join( "function f(e) {", " try {", " function f(e) {", " try {} catch (e) { e++; }", " }", " } catch (e) { e--; }", "}"), LINE_JOINER.join( "function f(e) {", " try {", " var f$1 = function(e) {", " try {} catch (e$0) { e$0++; }", " }", " } catch (e$2) { e$2--; }", "}")); } public void testExterns() { testExternChanges("let x;", "", "var x;"); } }