diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index f7c20e74d6717..991cab51c252a 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -145,7 +145,15 @@ namespace ts { loopOutParameters: LoopOutParameter[]; } - type LoopConverter = (node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convertedLoopBodyStatements: Statement[] | undefined, ancestorFacts: HierarchyFacts) => Statement; + type DirectLoopConverter = (node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convertedLoopBodyStatements: Statement[] | undefined, ancestorFacts: HierarchyFacts) => Statement; + type TransformedLoopConverter = (node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convertedLoopBodyStatements: Statement[] | undefined, ancestorFacts: HierarchyFacts, initializerFunction: IterationStatementPartFunction<VariableDeclarationList> | undefined, bodyFunction: IterationStatementPartFunction<Statement[]>) => Statement; + + interface IterationStatementPartFunction<T> { + functionName: Identifier; + functionDeclaration: Statement; + containsYield: boolean; + part: T; + } // Facts we track as we traverse the tree const enum HierarchyFacts { @@ -2284,9 +2292,9 @@ namespace ts { } } - function visitIterationStatementWithFacts(excludeFacts: HierarchyFacts, includeFacts: HierarchyFacts, node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convert?: LoopConverter) { + function visitIterationStatementWithFacts(excludeFacts: HierarchyFacts, includeFacts: HierarchyFacts, node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convertDirectly?: DirectLoopConverter, convertWithTransform?: TransformedLoopConverter) { const ancestorFacts = enterSubtree(excludeFacts, includeFacts); - const updated = convertIterationStatementBodyIfNecessary(node, outermostLabeledStatement, ancestorFacts, convert); + const updated = convertIterationStatementBodyIfNecessary(node, outermostLabeledStatement, ancestorFacts, convertDirectly, convertWithTransform); exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None); return updated; } @@ -2322,19 +2330,23 @@ namespace ts { HierarchyFacts.ForInOrForOfStatementExcludes, HierarchyFacts.ForInOrForOfStatementIncludes, node, - outermostLabeledStatement); + outermostLabeledStatement, + convertForInStatementForLoopDirectly, + convertForInStatementForLoopTransformed); } function visitForOfStatement(node: ForOfStatement, outermostLabeledStatement: LabeledStatement | undefined): VisitResult<Statement> { + const convert = compilerOptions.downlevelIteration ? convertForOfStatementForIterable : convertForOfStatementForArray; return visitIterationStatementWithFacts( HierarchyFacts.ForInOrForOfStatementExcludes, HierarchyFacts.ForInOrForOfStatementIncludes, node, outermostLabeledStatement, - compilerOptions.downlevelIteration ? convertForOfStatementForIterable : convertForOfStatementForArray); + convert, + convert); } - function convertForOfStatementHead(node: ForOfStatement, boundValue: Expression, convertedLoopBodyStatements: Statement[]) { + function convertForStatementHead(node: ForInStatement | ForOfStatement, boundValue: Expression, convertedLoopBodyStatements: Statement[]) { const statements: Statement[] = []; const initializer = node.initializer; if (isVariableDeclarationList(initializer)) { @@ -2433,6 +2445,77 @@ namespace ts { ); } + function convertForInStatementForLoopDirectly(node: ForInStatement, outermostLabeledStatement: LabeledStatement, convertedLoopBodyStatements: Statement[]): Statement { + // The following ES2015 code: + // + // for (var [a, b] in expr) { } + // + // should be emitted as + // + // for (var _a in expr) { + // var a = _a[0], b = _a[1]; + // } + // + // where _a is a temp emitted to capture the LHS binding. + const initializerBinding = getForInInitializerBindingDeclaration(node); + if (initializerBinding) { + return convertForInStatementWithInitializerBinding(node, convertedLoopBodyStatements, initializerBinding); + } + + return factory.restoreEnclosingLabel(visitEachChild(node, visitor, context), outermostLabeledStatement, convertedLoopState && resetLabel); + } + + function convertForInStatementForLoopTransformed(node: ForInStatement, outermostLabeledStatement: LabeledStatement, convertedLoopBodyStatements: Statement[], _ancestorFacts: HierarchyFacts, initializerFunction: IterationStatementPartFunction<VariableDeclarationList> | undefined, bodyFunction: IterationStatementPartFunction<Statement[]>): Statement { + // The following ES2015 code: + // + // for (let [a, b] in expr) { ... } + // + // should be emitted as + // + // var _loop_1 = function (_a) { + // var a = _a[0], b = _a[1]; + // ... + // } + // for (var _a in expr) { + // _loop_1(a); + // } + // + // where _a is a temp emitted to capture the LHS binding. + const initializerBinding = getForInInitializerBindingDeclaration(node); + if (initializerBinding) { + return convertForInStatementWithInitializerBinding(node, convertedLoopBodyStatements, initializerBinding); + } + + const clone = convertIterationStatementCore(node, initializerFunction, factory.createBlock(bodyFunction.part, /*multiLine*/ true)); + return factory.restoreEnclosingLabel(clone, outermostLabeledStatement, convertedLoopState && resetLabel); + } + + function convertForInStatementWithInitializerBinding(node: ForInStatement, convertedLoopBodyStatements: Statement[], _initializerBinding: VariableDeclaration) { + const tempInitializer = factory.createTempVariable(/*recordTempVariable*/ undefined); + + return factory.createForInStatement( + factory.createVariableDeclarationList([factory.createVariableDeclaration(tempInitializer)]), + node.expression, + convertForStatementHead( + node, + tempInitializer, + convertedLoopBodyStatements, + ) + ); + } + + function getForInInitializerBindingDeclaration(node: ForInStatement) { + if (!isVariableDeclarationList(node.initializer) || node.initializer.declarations.length === 0) { + return undefined; + } + + const declaration = node.initializer.declarations[0]; + + return isArrayBindingPattern(declaration.name) || isObjectBindingPattern(declaration.name) + ? declaration + : undefined; + } + function convertForOfStatementForArray(node: ForOfStatement, outermostLabeledStatement: LabeledStatement, convertedLoopBodyStatements: Statement[]): Statement { // The following ES6 code: // @@ -2488,7 +2571,7 @@ namespace ts { node.expression ), /*incrementor*/ setTextRange(factory.createPostfixIncrement(counter), node.expression), - /*statement*/ convertForOfStatementHead( + /*statement*/ convertForStatementHead( node, factory.createElementAccessExpression(rhsReference, counter), convertedLoopBodyStatements @@ -2536,7 +2619,7 @@ namespace ts { ), /*condition*/ factory.createLogicalNot(factory.createPropertyAccessExpression(result, "done")), /*incrementor*/ factory.createAssignment(result, next), - /*statement*/ convertForOfStatementHead( + /*statement*/ convertForStatementHead( node, factory.createPropertyAccessExpression(result, "value"), convertedLoopBodyStatements @@ -2731,7 +2814,7 @@ namespace ts { } } - function convertIterationStatementBodyIfNecessary(node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, ancestorFacts: HierarchyFacts, convert?: LoopConverter): VisitResult<Statement> { + function convertIterationStatementBodyIfNecessary(node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, ancestorFacts: HierarchyFacts, convertDirectly?: DirectLoopConverter, convertWithTransform?: TransformedLoopConverter): VisitResult<Statement> { if (!shouldConvertIterationStatement(node)) { let saveAllowedNonLabeledJumps: Jump | undefined; if (convertedLoopState) { @@ -2741,8 +2824,8 @@ namespace ts { convertedLoopState.allowedNonLabeledJumps = Jump.Break | Jump.Continue; } - const result = convert - ? convert(node, outermostLabeledStatement, /*convertedLoopBodyStatements*/ undefined, ancestorFacts) + const result = convertDirectly + ? convertDirectly(node, outermostLabeledStatement, /*convertedLoopBodyStatements*/ undefined, ancestorFacts) : factory.restoreEnclosingLabel( isForStatement(node) ? visitEachChildOfForStatement(node) : visitEachChild(node, visitor, context), outermostLabeledStatement, @@ -2776,8 +2859,8 @@ namespace ts { let loop: Statement; if (bodyFunction) { - if (convert) { - loop = convert(node, outermostLabeledStatement, bodyFunction.part, ancestorFacts); + if (convertWithTransform) { + loop = convertWithTransform(node, outermostLabeledStatement, bodyFunction.part, ancestorFacts, initializerFunction, bodyFunction); } else { const clone = convertIterationStatementCore(node, initializerFunction, factory.createBlock(bodyFunction.part, /*multiLine*/ true)); @@ -2981,13 +3064,6 @@ namespace ts { } } - interface IterationStatementPartFunction<T> { - functionName: Identifier; - functionDeclaration: Statement; - containsYield: boolean; - part: T; - } - function createOutVariable(p: LoopOutParameter) { return factory.createVariableDeclaration(p.originalName, /*exclamationToken*/ undefined, /*type*/ undefined, p.outParamName); } diff --git a/tests/baselines/reference/for-inStatementsDestructuring.errors.txt b/tests/baselines/reference/for-inStatementsDestructuring.errors.txt index e54f2c93272a3..f324d60738137 100644 --- a/tests/baselines/reference/for-inStatementsDestructuring.errors.txt +++ b/tests/baselines/reference/for-inStatementsDestructuring.errors.txt @@ -1,10 +1,44 @@ tests/cases/conformance/statements/for-inStatements/for-inStatementsDestructuring.ts(1,10): error TS2461: Type 'string' is not an array type. tests/cases/conformance/statements/for-inStatements/for-inStatementsDestructuring.ts(1,10): error TS2491: The left-hand side of a 'for...in' statement cannot be a destructuring pattern. +tests/cases/conformance/statements/for-inStatements/for-inStatementsDestructuring.ts(2,10): error TS2491: The left-hand side of a 'for...in' statement cannot be a destructuring pattern. +tests/cases/conformance/statements/for-inStatements/for-inStatementsDestructuring.ts(2,12): error TS2339: Property 'a' does not exist on type 'String'. +tests/cases/conformance/statements/for-inStatements/for-inStatementsDestructuring.ts(2,15): error TS2339: Property 'b' does not exist on type 'String'. +tests/cases/conformance/statements/for-inStatements/for-inStatementsDestructuring.ts(4,10): error TS2461: Type 'string' is not an array type. +tests/cases/conformance/statements/for-inStatements/for-inStatementsDestructuring.ts(4,10): error TS2491: The left-hand side of a 'for...in' statement cannot be a destructuring pattern. +tests/cases/conformance/statements/for-inStatements/for-inStatementsDestructuring.ts(8,10): error TS2491: The left-hand side of a 'for...in' statement cannot be a destructuring pattern. +tests/cases/conformance/statements/for-inStatements/for-inStatementsDestructuring.ts(8,12): error TS2339: Property 'a' does not exist on type 'String'. +tests/cases/conformance/statements/for-inStatements/for-inStatementsDestructuring.ts(8,15): error TS2339: Property 'b' does not exist on type 'String'. -==== tests/cases/conformance/statements/for-inStatements/for-inStatementsDestructuring.ts (2 errors) ==== +==== tests/cases/conformance/statements/for-inStatements/for-inStatementsDestructuring.ts (10 errors) ==== for (var [a, b] in []) {} ~~~~~~ !!! error TS2461: Type 'string' is not an array type. ~~~~~~ -!!! error TS2491: The left-hand side of a 'for...in' statement cannot be a destructuring pattern. \ No newline at end of file +!!! error TS2491: The left-hand side of a 'for...in' statement cannot be a destructuring pattern. + for (var { a, b } in []) {} + ~~~~~~~~ +!!! error TS2491: The left-hand side of a 'for...in' statement cannot be a destructuring pattern. + ~ +!!! error TS2339: Property 'a' does not exist on type 'String'. + ~ +!!! error TS2339: Property 'b' does not exist on type 'String'. + + for (let [a, b] in []) { + ~~~~~~ +!!! error TS2461: Type 'string' is not an array type. + ~~~~~~ +!!! error TS2491: The left-hand side of a 'for...in' statement cannot be a destructuring pattern. + (() => a + b); + } + + for (let { a, b } in []) { + ~~~~~~~~ +!!! error TS2491: The left-hand side of a 'for...in' statement cannot be a destructuring pattern. + ~ +!!! error TS2339: Property 'a' does not exist on type 'String'. + ~ +!!! error TS2339: Property 'b' does not exist on type 'String'. + (() => a + b); + } + \ No newline at end of file diff --git a/tests/baselines/reference/for-inStatementsDestructuring.js b/tests/baselines/reference/for-inStatementsDestructuring.js index e5e00d5b3ab56..f490e787b22ec 100644 --- a/tests/baselines/reference/for-inStatementsDestructuring.js +++ b/tests/baselines/reference/for-inStatementsDestructuring.js @@ -1,5 +1,34 @@ //// [for-inStatementsDestructuring.ts] -for (var [a, b] in []) {} +for (var [a, b] in []) {} +for (var { a, b } in []) {} + +for (let [a, b] in []) { + (() => a + b); +} + +for (let { a, b } in []) { + (() => a + b); +} + //// [for-inStatementsDestructuring.js] -for (var _a = void 0, a = _a[0], b = _a[1] in []) { } +for (var _a in []) { + var a = _a[0], b = _a[1]; +} +for (var _b in []) { + var a = _b.a, b = _b.b; +} +var _loop_1 = function (a_1, b_1) { + (function () { return a_1 + b_1; }); +}; +for (var _c in []) { + var a_1 = _c[0], b_1 = _c[1]; + _loop_1(a_1, b_1); +} +var _loop_2 = function (a_2, b_2) { + (function () { return a_2 + b_2; }); +}; +for (var _d in []) { + var a_2 = _d.a, b_2 = _d.b; + _loop_2(a_2, b_2); +} diff --git a/tests/baselines/reference/for-inStatementsDestructuring.symbols b/tests/baselines/reference/for-inStatementsDestructuring.symbols index fff70d63bd353..04c3a07077349 100644 --- a/tests/baselines/reference/for-inStatementsDestructuring.symbols +++ b/tests/baselines/reference/for-inStatementsDestructuring.symbols @@ -1,5 +1,27 @@ === tests/cases/conformance/statements/for-inStatements/for-inStatementsDestructuring.ts === for (var [a, b] in []) {} ->a : Symbol(a, Decl(for-inStatementsDestructuring.ts, 0, 10)) ->b : Symbol(b, Decl(for-inStatementsDestructuring.ts, 0, 12)) +>a : Symbol(a, Decl(for-inStatementsDestructuring.ts, 0, 10), Decl(for-inStatementsDestructuring.ts, 1, 10)) +>b : Symbol(b, Decl(for-inStatementsDestructuring.ts, 0, 12), Decl(for-inStatementsDestructuring.ts, 1, 13)) + +for (var { a, b } in []) {} +>a : Symbol(a, Decl(for-inStatementsDestructuring.ts, 0, 10), Decl(for-inStatementsDestructuring.ts, 1, 10)) +>b : Symbol(b, Decl(for-inStatementsDestructuring.ts, 0, 12), Decl(for-inStatementsDestructuring.ts, 1, 13)) + +for (let [a, b] in []) { +>a : Symbol(a, Decl(for-inStatementsDestructuring.ts, 3, 10)) +>b : Symbol(b, Decl(for-inStatementsDestructuring.ts, 3, 12)) + + (() => a + b); +>a : Symbol(a, Decl(for-inStatementsDestructuring.ts, 3, 10)) +>b : Symbol(b, Decl(for-inStatementsDestructuring.ts, 3, 12)) +} + +for (let { a, b } in []) { +>a : Symbol(a, Decl(for-inStatementsDestructuring.ts, 7, 10)) +>b : Symbol(b, Decl(for-inStatementsDestructuring.ts, 7, 13)) + + (() => a + b); +>a : Symbol(a, Decl(for-inStatementsDestructuring.ts, 7, 10)) +>b : Symbol(b, Decl(for-inStatementsDestructuring.ts, 7, 13)) +} diff --git a/tests/baselines/reference/for-inStatementsDestructuring.types b/tests/baselines/reference/for-inStatementsDestructuring.types index 547e54a574195..ce8518fcb60fd 100644 --- a/tests/baselines/reference/for-inStatementsDestructuring.types +++ b/tests/baselines/reference/for-inStatementsDestructuring.types @@ -4,3 +4,34 @@ for (var [a, b] in []) {} >b : any >[] : undefined[] +for (var { a, b } in []) {} +>a : any +>b : any +>[] : undefined[] + +for (let [a, b] in []) { +>a : any +>b : any +>[] : undefined[] + + (() => a + b); +>(() => a + b) : () => any +>() => a + b : () => any +>a + b : any +>a : any +>b : any +} + +for (let { a, b } in []) { +>a : any +>b : any +>[] : undefined[] + + (() => a + b); +>(() => a + b) : () => any +>() => a + b : () => any +>a + b : any +>a : any +>b : any +} + diff --git a/tests/baselines/reference/for-inStatementsDestructuring2.js b/tests/baselines/reference/for-inStatementsDestructuring2.js index 41a6d30919f50..5c384b1c9dfa9 100644 --- a/tests/baselines/reference/for-inStatementsDestructuring2.js +++ b/tests/baselines/reference/for-inStatementsDestructuring2.js @@ -2,4 +2,6 @@ for (var {a, b} in []) {} //// [for-inStatementsDestructuring2.js] -for (var _a = void 0, a = _a.a, b = _a.b in []) { } +for (var _a in []) { + var a = _a.a, b = _a.b; +} diff --git a/tests/baselines/reference/jsDeclarationsSubclassWithExplicitNoArgumentConstructor.js b/tests/baselines/reference/jsDeclarationsSubclassWithExplicitNoArgumentConstructor.js index 42cafa5e355a0..8abe7764586b0 100644 --- a/tests/baselines/reference/jsDeclarationsSubclassWithExplicitNoArgumentConstructor.js +++ b/tests/baselines/reference/jsDeclarationsSubclassWithExplicitNoArgumentConstructor.js @@ -23,6 +23,8 @@ var __extends = (this && this.__extends) || (function () { return extendStatics(d, b); }; return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); diff --git a/tests/cases/conformance/statements/for-inStatements/for-inStatementsDestructuring.ts b/tests/cases/conformance/statements/for-inStatements/for-inStatementsDestructuring.ts index 94868ef3bd62e..d73963a82b51e 100644 --- a/tests/cases/conformance/statements/for-inStatements/for-inStatementsDestructuring.ts +++ b/tests/cases/conformance/statements/for-inStatements/for-inStatementsDestructuring.ts @@ -1 +1,10 @@ -for (var [a, b] in []) {} \ No newline at end of file +for (var [a, b] in []) {} +for (var { a, b } in []) {} + +for (let [a, b] in []) { + (() => a + b); +} + +for (let { a, b } in []) { + (() => a + b); +}