diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e0ea6bac95f5b..71c3cc1fde669 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23485,14 +23485,20 @@ namespace ts { return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType; } - function isInsideFunction(node: Node, threshold: Node): boolean { - return !!findAncestor(node, n => n === threshold ? "quit" : isFunctionLike(n)); + function isInsideFunctionOrInstancePropertyInitializer(node: Node, threshold: Node): boolean { + return !!findAncestor(node, n => n === threshold ? "quit" : isFunctionLike(n) || ( + n.parent && isPropertyDeclaration(n.parent) && !hasStaticModifier(n.parent) && n.parent.initializer === n + )); } function getPartOfForStatementContainingNode(node: Node, container: ForStatement) { return findAncestor(node, n => n === container ? "quit" : n === container.initializer || n === container.condition || n === container.incrementor || n === container.statement); } + function getEnclosingIterationStatement(node: Node): Node | undefined { + return findAncestor(node, n => (!n || nodeStartsNewLexicalEnvironment(n)) ? "quit" : isIterationStatement(n, /*lookInLabeledStatements*/ false)); + } + function checkNestedBlockScopedBinding(node: Identifier, symbol: Symbol): void { if (languageVersion >= ScriptTarget.ES2015 || (symbol.flags & (SymbolFlags.BlockScopedVariable | SymbolFlags.Class)) === 0 || @@ -23507,20 +23513,11 @@ namespace ts { // if there is an iteration statement in between declaration and boundary (is binding/class declared inside iteration statement) const container = getEnclosingBlockScopeContainer(symbol.valueDeclaration); - const usedInFunction = isInsideFunction(node.parent, container); - let current = container; - - let containedInIterationStatement = false; - while (current && !nodeStartsNewLexicalEnvironment(current)) { - if (isIterationStatement(current, /*lookInLabeledStatements*/ false)) { - containedInIterationStatement = true; - break; - } - current = current.parent; - } + const isCaptured = isInsideFunctionOrInstancePropertyInitializer(node, container); - if (containedInIterationStatement) { - if (usedInFunction) { + const enclosingIterationStatement = getEnclosingIterationStatement(container); + if (enclosingIterationStatement) { + if (isCaptured) { // mark iteration statement as containing block-scoped binding captured in some function let capturesBlockScopeBindingInLoopBody = true; if (isForStatement(container)) { @@ -23541,7 +23538,7 @@ namespace ts { } } if (capturesBlockScopeBindingInLoopBody) { - getNodeLinks(current).flags |= NodeCheckFlags.LoopWithCapturedBlockScopedBinding; + getNodeLinks(enclosingIterationStatement).flags |= NodeCheckFlags.LoopWithCapturedBlockScopedBinding; } } @@ -23558,7 +23555,7 @@ namespace ts { getNodeLinks(symbol.valueDeclaration).flags |= NodeCheckFlags.BlockScopedBindingInLoop; } - if (usedInFunction) { + if (isCaptured) { getNodeLinks(symbol.valueDeclaration).flags |= NodeCheckFlags.CapturedBlockScopedBinding; } } @@ -25151,6 +25148,20 @@ namespace ts { const links = getNodeLinks(node.expression); if (!links.resolvedType) { links.resolvedType = checkExpression(node.expression); + // The computed property name of a non-static class field within a loop must be stored in a block-scoped binding. + // (It needs to be bound at class evaluation time.) + if (isPropertyDeclaration(node.parent) && !hasStaticModifier(node.parent) && isClassExpression(node.parent.parent)) { + const container = getEnclosingBlockScopeContainer(node.parent.parent); + const enclosingIterationStatement = getEnclosingIterationStatement(container); + if (enclosingIterationStatement) { + // The computed field name will use a block scoped binding which can be unique for each iteration of the loop. + getNodeLinks(enclosingIterationStatement).flags |= NodeCheckFlags.LoopWithCapturedBlockScopedBinding; + // The generated variable which stores the computed field name must be block-scoped. + getNodeLinks(node).flags |= NodeCheckFlags.BlockScopedBindingInLoop; + // The generated variable which stores the class must be block-scoped. + getNodeLinks(node.parent.parent).flags |= NodeCheckFlags.BlockScopedBindingInLoop; + } + } // This will allow types number, string, symbol or any. It will also allow enums, the unknown // type, and any union of these types (like string | number). if (links.resolvedType.flags & TypeFlags.Nullable || @@ -32269,6 +32280,16 @@ namespace ts { for (let lexicalScope = getEnclosingBlockScopeContainer(node); !!lexicalScope; lexicalScope = getEnclosingBlockScopeContainer(lexicalScope)) { getNodeLinks(lexicalScope).flags |= NodeCheckFlags.ContainsClassWithPrivateIdentifiers; } + + // If this is a private field in a class expression inside the body of a loop, + // then we must use a block-scoped binding to store the WeakMap. + if (isClassExpression(node.parent)) { + const enclosingIterationStatement = getEnclosingIterationStatement(node.parent); + if (enclosingIterationStatement) { + getNodeLinks(node.name).flags |= NodeCheckFlags.BlockScopedBindingInLoop; + getNodeLinks(enclosingIterationStatement).flags |= NodeCheckFlags.LoopWithCapturedBlockScopedBinding; + } + } } } diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 4de469afc5724..62a8f38a78e68 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -155,6 +155,9 @@ namespace ts { let lexicalEnvironmentFlagsStack: LexicalEnvironmentFlags[] = []; let lexicalEnvironmentStackOffset = 0; let lexicalEnvironmentSuspended = false; + let blockScopedVariableDeclarationsStack: Identifier[][] = []; + let blockScopeStackOffset = 0; + let blockScopedVariableDeclarations: Identifier[]; let emitHelpers: EmitHelper[] | undefined; let onSubstituteNode: TransformationContext["onSubstituteNode"] = noEmitSubstitution; let onEmitNode: TransformationContext["onEmitNode"] = noEmitNotification; @@ -178,6 +181,9 @@ namespace ts { hoistVariableDeclaration, hoistFunctionDeclaration, addInitializationStatement, + startBlockScope, + endBlockScope, + addBlockScopedVariable, requestEmitHelper, readEmitHelpers, enableSubstitution, @@ -469,6 +475,46 @@ namespace ts { return lexicalEnvironmentFlags; } + /** + * Starts a block scope. Any existing block hoisted variables are pushed onto the stack and the related storage variables are reset. + */ + function startBlockScope() { + Debug.assert(state > TransformationState.Uninitialized, "Cannot start a block scope during initialization."); + Debug.assert(state < TransformationState.Completed, "Cannot start a block scope after transformation has completed."); + blockScopedVariableDeclarationsStack[blockScopeStackOffset] = blockScopedVariableDeclarations; + blockScopeStackOffset++; + blockScopedVariableDeclarations = undefined!; + } + + /** + * Ends a block scope. The previous set of block hoisted variables are restored. Any hoisted declarations are returned. + */ + function endBlockScope() { + Debug.assert(state > TransformationState.Uninitialized, "Cannot end a block scope during initialization."); + Debug.assert(state < TransformationState.Completed, "Cannot end a block scope after transformation has completed."); + const statements: Statement[] | undefined = some(blockScopedVariableDeclarations) ? + [ + factory.createVariableStatement( + /*modifiers*/ undefined, + factory.createVariableDeclarationList( + blockScopedVariableDeclarations.map(identifier => factory.createVariableDeclaration(identifier)), + NodeFlags.Let + ) + ) + ] : undefined; + blockScopeStackOffset--; + blockScopedVariableDeclarations = blockScopedVariableDeclarationsStack[blockScopeStackOffset]; + if (blockScopeStackOffset === 0) { + blockScopedVariableDeclarationsStack = []; + } + return statements; + } + + function addBlockScopedVariable(name: Identifier): void { + Debug.assert(blockScopeStackOffset > 0, "Cannot add a block scoped variable outside of an iteration body."); + (blockScopedVariableDeclarations || (blockScopedVariableDeclarations = [])).push(name); + } + function requestEmitHelper(helper: EmitHelper): void { Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the transformation context during initialization."); Debug.assert(state < TransformationState.Completed, "Cannot modify the transformation context after transformation has completed."); @@ -535,5 +581,8 @@ namespace ts { startLexicalEnvironment: noop, suspendLexicalEnvironment: noop, addDiagnostic: noop, + startBlockScope: noop, + endBlockScope: returnUndefined, + addBlockScopedVariable: noop }; } diff --git a/src/compiler/transformers/classFields.ts b/src/compiler/transformers/classFields.ts index 96ba926dd17db..0e239bacb5ae6 100644 --- a/src/compiler/transformers/classFields.ts +++ b/src/compiler/transformers/classFields.ts @@ -36,7 +36,8 @@ namespace ts { factory, hoistVariableDeclaration, endLexicalEnvironment, - resumeLexicalEnvironment + resumeLexicalEnvironment, + addBlockScopedVariable } = context; const resolver = context.getEmitResolver(); const compilerOptions = context.getCompilerOptions(); @@ -310,7 +311,7 @@ namespace ts { visitNode(node.initializer, visitor, isForInitializer), visitNode(node.condition, visitor, isExpression), visitPostfixUnaryExpression(node.incrementor, /*valueIsDiscarded*/ true), - visitNode(node.statement, visitor, isStatement) + visitIterationBody(node.statement, visitor, context) ); } return visitEachChild(node, visitor, context); @@ -540,8 +541,10 @@ namespace ts { } else { const expressions: Expression[] = []; - const isClassWithConstructorReference = resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithConstructorReference; - const temp = factory.createTempVariable(hoistVariableDeclaration, !!isClassWithConstructorReference); + const classCheckFlags = resolver.getNodeCheckFlags(node); + const isClassWithConstructorReference = classCheckFlags & NodeCheckFlags.ClassWithConstructorReference; + const requiresBlockScopedVar = classCheckFlags & NodeCheckFlags.BlockScopedBindingInLoop; + const temp = factory.createTempVariable(requiresBlockScopedVar ? addBlockScopedVariable : hoistVariableDeclaration, !!isClassWithConstructorReference); if (isClassWithConstructorReference) { // record an alias as the class name is not in scope for statics. enableSubstitutionForClassAliases(); @@ -869,7 +872,6 @@ namespace ts { return undefined; } - /** * If the name is a computed property, this function transforms it, then either returns an expression which caches the * value of the result or the expression itself if the value is either unused or safe to inline into multiple locations @@ -883,7 +885,12 @@ namespace ts { const alreadyTransformed = isAssignmentExpression(innerExpression) && isGeneratedIdentifier(innerExpression.left); if (!alreadyTransformed && !inlinable && shouldHoist) { const generatedName = factory.getGeneratedNameForNode(name); - hoistVariableDeclaration(generatedName); + if (resolver.getNodeCheckFlags(name) & NodeCheckFlags.BlockScopedBindingInLoop) { + addBlockScopedVariable(generatedName); + } + else { + hoistVariableDeclaration(generatedName); + } return factory.createAssignment(generatedName, expression); } return (inlinable || isIdentifier(innerExpression)) ? undefined : expression; @@ -910,7 +917,12 @@ namespace ts { function addPrivateIdentifierToEnvironment(name: PrivateIdentifier) { const text = getTextOfPropertyName(name) as string; const weakMapName = factory.createUniqueName("_" + text.substring(1), GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.ReservedInNestedScopes); - hoistVariableDeclaration(weakMapName); + if (resolver.getNodeCheckFlags(name) & NodeCheckFlags.BlockScopedBindingInLoop) { + addBlockScopedVariable(weakMapName); + } + else { + hoistVariableDeclaration(weakMapName); + } getPrivateIdentifierEnvironment().set(name.escapedText, { placement: PrivateIdentifierPlacement.InstanceField, weakMapName }); getPendingExpressions().push( factory.createAssignment( diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts index 6c3c8c1332acf..a2d2985e75b55 100644 --- a/src/compiler/transformers/es2017.ts +++ b/src/compiler/transformers/es2017.ts @@ -225,7 +225,7 @@ namespace ts { ? visitVariableDeclarationListWithCollidingNames(node.initializer, /*hasReceiver*/ true)! : visitNode(node.initializer, visitor, isForInitializer), visitNode(node.expression, visitor, isExpression), - visitNode(node.statement, asyncBodyVisitor, isStatement, factory.liftToBlock) + visitIterationBody(node.statement, asyncBodyVisitor, context) ); } @@ -237,7 +237,7 @@ namespace ts { ? visitVariableDeclarationListWithCollidingNames(node.initializer, /*hasReceiver*/ true)! : visitNode(node.initializer, visitor, isForInitializer), visitNode(node.expression, visitor, isExpression), - visitNode(node.statement, asyncBodyVisitor, isStatement, factory.liftToBlock) + visitIterationBody(node.statement, asyncBodyVisitor, context) ); } @@ -250,7 +250,7 @@ namespace ts { : visitNode(node.initializer, visitor, isForInitializer), visitNode(node.condition, visitor, isExpression), visitNode(node.incrementor, visitor, isExpression), - visitNode(node.statement, asyncBodyVisitor, isStatement, factory.liftToBlock) + visitIterationBody(node.statement, asyncBodyVisitor, context) ); } diff --git a/src/compiler/transformers/es2018.ts b/src/compiler/transformers/es2018.ts index 41fcc6c25d9b9..b7b04f191cf62 100644 --- a/src/compiler/transformers/es2018.ts +++ b/src/compiler/transformers/es2018.ts @@ -575,7 +575,7 @@ namespace ts { visitNode(node.initializer, visitorWithUnusedExpressionResult, isForInitializer), visitNode(node.condition, visitor, isExpression), visitNode(node.incrementor, visitorWithUnusedExpressionResult, isExpression), - visitNode(node.statement, visitor, isStatement) + visitIterationBody(node.statement, visitor, context) ); } @@ -648,7 +648,7 @@ namespace ts { let bodyLocation: TextRange | undefined; let statementsLocation: TextRange | undefined; const statements: Statement[] = [visitNode(binding, visitor, isStatement)]; - const statement = visitNode(node.statement, visitor, isStatement); + const statement = visitIterationBody(node.statement, visitor, context); if (isBlock(statement)) { addRange(statements, statement.statements); bodyLocation = statement; diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index 6ae08275e3a02..fca06a8ce22ca 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -1502,7 +1502,7 @@ namespace ts { : undefined, visitNode(node.condition, visitor, isExpression), visitNode(node.incrementor, visitor, isExpression), - visitNode(node.statement, visitor, isStatement, factory.liftToBlock) + visitIterationBody(node.statement, visitor, context) ); } else { diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index a5e0c9bbbf4ef..ad111c6204eca 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -1231,7 +1231,7 @@ namespace ts { node.initializer && visitForInitializer(node.initializer), visitNode(node.condition, destructuringAndImportCallVisitor, isExpression), visitNode(node.incrementor, destructuringAndImportCallVisitor, isExpression), - visitNode(node.statement, nestedElementVisitor, isStatement) + visitIterationBody(node.statement, nestedElementVisitor, context) ); enclosingBlockScopedContainer = savedEnclosingBlockScopedContainer; @@ -1251,7 +1251,7 @@ namespace ts { node, visitForInitializer(node.initializer), visitNode(node.expression, destructuringAndImportCallVisitor, isExpression), - visitNode(node.statement, nestedElementVisitor, isStatement, factory.liftToBlock) + visitIterationBody(node.statement, nestedElementVisitor, context) ); enclosingBlockScopedContainer = savedEnclosingBlockScopedContainer; @@ -1272,7 +1272,7 @@ namespace ts { node.awaitModifier, visitForInitializer(node.initializer), visitNode(node.expression, destructuringAndImportCallVisitor, isExpression), - visitNode(node.statement, nestedElementVisitor, isStatement, factory.liftToBlock) + visitIterationBody(node.statement, nestedElementVisitor, context) ); enclosingBlockScopedContainer = savedEnclosingBlockScopedContainer; @@ -1320,7 +1320,7 @@ namespace ts { function visitDoStatement(node: DoStatement): VisitResult { return factory.updateDoStatement( node, - visitNode(node.statement, nestedElementVisitor, isStatement, factory.liftToBlock), + visitIterationBody(node.statement, nestedElementVisitor, context), visitNode(node.expression, destructuringAndImportCallVisitor, isExpression) ); } @@ -1334,7 +1334,7 @@ namespace ts { return factory.updateWhileStatement( node, visitNode(node.expression, destructuringAndImportCallVisitor, isExpression), - visitNode(node.statement, nestedElementVisitor, isStatement, factory.liftToBlock) + visitIterationBody(node.statement, nestedElementVisitor, context) ); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 3585ff50a8f3b..8929061cc1089 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -7492,6 +7492,12 @@ namespace ts { /** Hoists a variable declaration to the containing scope. */ hoistVariableDeclaration(node: Identifier): void; + /*@internal*/ startBlockScope(): void; + + /*@internal*/ endBlockScope(): Statement[] | undefined; + + /*@internal*/ addBlockScopedVariable(node: Identifier): void; + /** Adds an initialization statement to the top of the lexical environment. */ /* @internal */ addInitializationStatement(node: Statement): void; diff --git a/src/compiler/visitorPublic.ts b/src/compiler/visitorPublic.ts index 958b7b1bdd511..91e92f82a8028 100644 --- a/src/compiler/visitorPublic.ts +++ b/src/compiler/visitorPublic.ts @@ -313,6 +313,24 @@ namespace ts { return updated; } + /** + * Visits an iteration body, adding any block-scoped variables required by the transformation. + */ + export function visitIterationBody(body: Statement, visitor: Visitor, context: TransformationContext): Statement { + context.startBlockScope(); + const updated = visitNode(body, visitor, isStatement, context.factory.liftToBlock); + const declarations = context.endBlockScope(); + if (some(declarations)) { + if (isBlock(updated)) { + declarations.push(...updated.statements); + return context.factory.updateBlock(updated, declarations); + } + declarations.push(updated); + return context.factory.createBlock(declarations); + } + return updated; + } + /** * Visits each child of a Node using the supplied visitor, possibly returning a new Node of the same kind in its place. * @@ -795,33 +813,33 @@ namespace ts { case SyntaxKind.DoStatement: return factory.updateDoStatement(node, - nodeVisitor((node).statement, visitor, isStatement, factory.liftToBlock), + visitIterationBody((node).statement, visitor, context), nodeVisitor((node).expression, visitor, isExpression)); case SyntaxKind.WhileStatement: return factory.updateWhileStatement(node, nodeVisitor((node).expression, visitor, isExpression), - nodeVisitor((node).statement, visitor, isStatement, factory.liftToBlock)); + visitIterationBody((node).statement, visitor, context)); case SyntaxKind.ForStatement: return factory.updateForStatement(node, nodeVisitor((node).initializer, visitor, isForInitializer), nodeVisitor((node).condition, visitor, isExpression), nodeVisitor((node).incrementor, visitor, isExpression), - nodeVisitor((node).statement, visitor, isStatement, factory.liftToBlock)); + visitIterationBody((node).statement, visitor, context)); case SyntaxKind.ForInStatement: return factory.updateForInStatement(node, nodeVisitor((node).initializer, visitor, isForInitializer), nodeVisitor((node).expression, visitor, isExpression), - nodeVisitor((node).statement, visitor, isStatement, factory.liftToBlock)); + visitIterationBody((node).statement, visitor, context)); case SyntaxKind.ForOfStatement: return factory.updateForOfStatement(node, nodeVisitor((node).awaitModifier, tokenVisitor, isToken), nodeVisitor((node).initializer, visitor, isForInitializer), nodeVisitor((node).expression, visitor, isExpression), - nodeVisitor((node).statement, visitor, isStatement, factory.liftToBlock)); + visitIterationBody((node).statement, visitor, context)); case SyntaxKind.ContinueStatement: return factory.updateContinueStatement(node, diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 1850919780d16..760ff3f933b80 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -4748,6 +4748,10 @@ declare namespace ts { * environment and merging hoisted declarations upon completion. */ function visitFunctionBody(node: ConciseBody, visitor: Visitor, context: TransformationContext): ConciseBody; + /** + * Visits an iteration body, adding any block-scoped variables required by the transformation. + */ + function visitIterationBody(body: Statement, visitor: Visitor, context: TransformationContext): Statement; /** * Visits each child of a Node using the supplied visitor, possibly returning a new Node of the same kind in its place. * diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 1fb8c099d502e..01e6638a4338d 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -4748,6 +4748,10 @@ declare namespace ts { * environment and merging hoisted declarations upon completion. */ function visitFunctionBody(node: ConciseBody, visitor: Visitor, context: TransformationContext): ConciseBody; + /** + * Visits an iteration body, adding any block-scoped variables required by the transformation. + */ + function visitIterationBody(body: Statement, visitor: Visitor, context: TransformationContext): Statement; /** * Visits each child of a Node using the supplied visitor, possibly returning a new Node of the same kind in its place. * diff --git a/tests/baselines/reference/classDeclarationLoop.js b/tests/baselines/reference/classDeclarationLoop.js new file mode 100644 index 0000000000000..3de9f46826948 --- /dev/null +++ b/tests/baselines/reference/classDeclarationLoop.js @@ -0,0 +1,24 @@ +//// [classDeclarationLoop.ts] +const arr = []; +for (let i = 0; i < 10; ++i) { + class C { + prop = i; + } + arr.push(C); +} + + +//// [classDeclarationLoop.js] +var arr = []; +var _loop_1 = function (i) { + var C = /** @class */ (function () { + function C() { + this.prop = i; + } + return C; + }()); + arr.push(C); +}; +for (var i = 0; i < 10; ++i) { + _loop_1(i); +} diff --git a/tests/baselines/reference/classDeclarationLoop.symbols b/tests/baselines/reference/classDeclarationLoop.symbols new file mode 100644 index 0000000000000..1f76f1f3efa83 --- /dev/null +++ b/tests/baselines/reference/classDeclarationLoop.symbols @@ -0,0 +1,23 @@ +=== tests/cases/conformance/classes/classDeclarations/classDeclarationLoop.ts === +const arr = []; +>arr : Symbol(arr, Decl(classDeclarationLoop.ts, 0, 5)) + +for (let i = 0; i < 10; ++i) { +>i : Symbol(i, Decl(classDeclarationLoop.ts, 1, 8)) +>i : Symbol(i, Decl(classDeclarationLoop.ts, 1, 8)) +>i : Symbol(i, Decl(classDeclarationLoop.ts, 1, 8)) + + class C { +>C : Symbol(C, Decl(classDeclarationLoop.ts, 1, 30)) + + prop = i; +>prop : Symbol(C.prop, Decl(classDeclarationLoop.ts, 2, 13)) +>i : Symbol(i, Decl(classDeclarationLoop.ts, 1, 8)) + } + arr.push(C); +>arr.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>arr : Symbol(arr, Decl(classDeclarationLoop.ts, 0, 5)) +>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>C : Symbol(C, Decl(classDeclarationLoop.ts, 1, 30)) +} + diff --git a/tests/baselines/reference/classDeclarationLoop.types b/tests/baselines/reference/classDeclarationLoop.types new file mode 100644 index 0000000000000..0c398e3eb1ed8 --- /dev/null +++ b/tests/baselines/reference/classDeclarationLoop.types @@ -0,0 +1,29 @@ +=== tests/cases/conformance/classes/classDeclarations/classDeclarationLoop.ts === +const arr = []; +>arr : any[] +>[] : undefined[] + +for (let i = 0; i < 10; ++i) { +>i : number +>0 : 0 +>i < 10 : boolean +>i : number +>10 : 10 +>++i : number +>i : number + + class C { +>C : C + + prop = i; +>prop : number +>i : number + } + arr.push(C); +>arr.push(C) : number +>arr.push : (...items: any[]) => number +>arr : any[] +>push : (...items: any[]) => number +>C : typeof C +} + diff --git a/tests/baselines/reference/classExpressionLoop.js b/tests/baselines/reference/classExpressionLoop.js new file mode 100644 index 0000000000000..0013924d85d27 --- /dev/null +++ b/tests/baselines/reference/classExpressionLoop.js @@ -0,0 +1,22 @@ +//// [classExpressionLoop.ts] +let arr = []; +for (let i = 0; i < 10; ++i) { + arr.push(class C { + prop = i; + }); +} + + +//// [classExpressionLoop.js] +var arr = []; +var _loop_1 = function (i) { + arr.push(/** @class */ (function () { + function C() { + this.prop = i; + } + return C; + }())); +}; +for (var i = 0; i < 10; ++i) { + _loop_1(i); +} diff --git a/tests/baselines/reference/classExpressionLoop.symbols b/tests/baselines/reference/classExpressionLoop.symbols new file mode 100644 index 0000000000000..bd438712eb697 --- /dev/null +++ b/tests/baselines/reference/classExpressionLoop.symbols @@ -0,0 +1,22 @@ +=== tests/cases/conformance/classes/classExpressions/classExpressionLoop.ts === +let arr = []; +>arr : Symbol(arr, Decl(classExpressionLoop.ts, 0, 3)) + +for (let i = 0; i < 10; ++i) { +>i : Symbol(i, Decl(classExpressionLoop.ts, 1, 8)) +>i : Symbol(i, Decl(classExpressionLoop.ts, 1, 8)) +>i : Symbol(i, Decl(classExpressionLoop.ts, 1, 8)) + + arr.push(class C { +>arr.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>arr : Symbol(arr, Decl(classExpressionLoop.ts, 0, 3)) +>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>C : Symbol(C, Decl(classExpressionLoop.ts, 2, 13)) + + prop = i; +>prop : Symbol(C.prop, Decl(classExpressionLoop.ts, 2, 22)) +>i : Symbol(i, Decl(classExpressionLoop.ts, 1, 8)) + + }); +} + diff --git a/tests/baselines/reference/classExpressionLoop.types b/tests/baselines/reference/classExpressionLoop.types new file mode 100644 index 0000000000000..a53cc219ec424 --- /dev/null +++ b/tests/baselines/reference/classExpressionLoop.types @@ -0,0 +1,29 @@ +=== tests/cases/conformance/classes/classExpressions/classExpressionLoop.ts === +let arr = []; +>arr : any[] +>[] : undefined[] + +for (let i = 0; i < 10; ++i) { +>i : number +>0 : 0 +>i < 10 : boolean +>i : number +>10 : 10 +>++i : number +>i : number + + arr.push(class C { +>arr.push(class C { prop = i; }) : number +>arr.push : (...items: any[]) => number +>arr : any[] +>push : (...items: any[]) => number +>class C { prop = i; } : typeof C +>C : typeof C + + prop = i; +>prop : number +>i : number + + }); +} + diff --git a/tests/baselines/reference/classExpressionWithStaticProperties3.js b/tests/baselines/reference/classExpressionWithStaticProperties3.js index d71f02f22cb0b..120651d43c431 100644 --- a/tests/baselines/reference/classExpressionWithStaticProperties3.js +++ b/tests/baselines/reference/classExpressionWithStaticProperties3.js @@ -10,9 +10,9 @@ for (let i = 0; i < 3; i++) { arr.forEach(C => console.log(C.y())); //// [classExpressionWithStaticProperties3.js] -var _a; var arr = []; var _loop_1 = function (i) { + var _a = void 0; arr.push((_a = /** @class */ (function () { function C() { } diff --git a/tests/baselines/reference/computedPropertyNames52(target=es2015).js b/tests/baselines/reference/computedPropertyNames52(target=es2015).js new file mode 100644 index 0000000000000..7ba48d8120da6 --- /dev/null +++ b/tests/baselines/reference/computedPropertyNames52(target=es2015).js @@ -0,0 +1,25 @@ +//// [computedPropertyNames52.js] +const array = []; +for (let i = 0; i < 10; ++i) { + array.push(class C { + [i] = () => C; + static [i] = 100; + }) +} + + +//// [computedPropertyNames52-emit.js] +var _a; +const array = []; +for (let i = 0; i < 10; ++i) { + let _b, _c; + array.push((_c = class C { + constructor() { + this[_b] = () => C; + } + }, + _b = i, + _a = i, + _c[_a] = 100, + _c)); +} diff --git a/tests/baselines/reference/computedPropertyNames52(target=es2015).symbols b/tests/baselines/reference/computedPropertyNames52(target=es2015).symbols new file mode 100644 index 0000000000000..c36c9f73f9255 --- /dev/null +++ b/tests/baselines/reference/computedPropertyNames52(target=es2015).symbols @@ -0,0 +1,27 @@ +=== tests/cases/conformance/es6/computedProperties/computedPropertyNames52.js === +const array = []; +>array : Symbol(array, Decl(computedPropertyNames52.js, 0, 5)) + +for (let i = 0; i < 10; ++i) { +>i : Symbol(i, Decl(computedPropertyNames52.js, 1, 8)) +>i : Symbol(i, Decl(computedPropertyNames52.js, 1, 8)) +>i : Symbol(i, Decl(computedPropertyNames52.js, 1, 8)) + + array.push(class C { +>array.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>array : Symbol(array, Decl(computedPropertyNames52.js, 0, 5)) +>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>C : Symbol(C, Decl(computedPropertyNames52.js, 2, 15)) + + [i] = () => C; +>[i] : Symbol(C[i], Decl(computedPropertyNames52.js, 2, 24)) +>i : Symbol(i, Decl(computedPropertyNames52.js, 1, 8)) +>C : Symbol(C, Decl(computedPropertyNames52.js, 2, 15)) + + static [i] = 100; +>[i] : Symbol(C[i], Decl(computedPropertyNames52.js, 3, 22)) +>i : Symbol(i, Decl(computedPropertyNames52.js, 1, 8)) + + }) +} + diff --git a/tests/baselines/reference/computedPropertyNames52(target=es2015).types b/tests/baselines/reference/computedPropertyNames52(target=es2015).types new file mode 100644 index 0000000000000..7c7841402f351 --- /dev/null +++ b/tests/baselines/reference/computedPropertyNames52(target=es2015).types @@ -0,0 +1,36 @@ +=== tests/cases/conformance/es6/computedProperties/computedPropertyNames52.js === +const array = []; +>array : any[] +>[] : undefined[] + +for (let i = 0; i < 10; ++i) { +>i : number +>0 : 0 +>i < 10 : boolean +>i : number +>10 : 10 +>++i : number +>i : number + + array.push(class C { +>array.push(class C { [i] = () => C; static [i] = 100; }) : number +>array.push : (...items: any[]) => number +>array : any[] +>push : (...items: any[]) => number +>class C { [i] = () => C; static [i] = 100; } : typeof C +>C : typeof C + + [i] = () => C; +>[i] : () => typeof C +>i : number +>() => C : () => typeof C +>C : typeof C + + static [i] = 100; +>[i] : number +>i : number +>100 : 100 + + }) +} + diff --git a/tests/baselines/reference/computedPropertyNames52(target=es5).js b/tests/baselines/reference/computedPropertyNames52(target=es5).js new file mode 100644 index 0000000000000..5d9be9ec99826 --- /dev/null +++ b/tests/baselines/reference/computedPropertyNames52(target=es5).js @@ -0,0 +1,29 @@ +//// [computedPropertyNames52.js] +const array = []; +for (let i = 0; i < 10; ++i) { + array.push(class C { + [i] = () => C; + static [i] = 100; + }) +} + + +//// [computedPropertyNames52-emit.js] +var _a; +var array = []; +var _loop_1 = function (i) { + var _b = void 0, _c = void 0; + array.push((_c = /** @class */ (function () { + function C() { + this[_b] = function () { return C; }; + } + return C; + }()), + _b = i, + _a = i, + _c[_a] = 100, + _c)); +}; +for (var i = 0; i < 10; ++i) { + _loop_1(i); +} diff --git a/tests/baselines/reference/computedPropertyNames52(target=es5).symbols b/tests/baselines/reference/computedPropertyNames52(target=es5).symbols new file mode 100644 index 0000000000000..c36c9f73f9255 --- /dev/null +++ b/tests/baselines/reference/computedPropertyNames52(target=es5).symbols @@ -0,0 +1,27 @@ +=== tests/cases/conformance/es6/computedProperties/computedPropertyNames52.js === +const array = []; +>array : Symbol(array, Decl(computedPropertyNames52.js, 0, 5)) + +for (let i = 0; i < 10; ++i) { +>i : Symbol(i, Decl(computedPropertyNames52.js, 1, 8)) +>i : Symbol(i, Decl(computedPropertyNames52.js, 1, 8)) +>i : Symbol(i, Decl(computedPropertyNames52.js, 1, 8)) + + array.push(class C { +>array.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>array : Symbol(array, Decl(computedPropertyNames52.js, 0, 5)) +>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>C : Symbol(C, Decl(computedPropertyNames52.js, 2, 15)) + + [i] = () => C; +>[i] : Symbol(C[i], Decl(computedPropertyNames52.js, 2, 24)) +>i : Symbol(i, Decl(computedPropertyNames52.js, 1, 8)) +>C : Symbol(C, Decl(computedPropertyNames52.js, 2, 15)) + + static [i] = 100; +>[i] : Symbol(C[i], Decl(computedPropertyNames52.js, 3, 22)) +>i : Symbol(i, Decl(computedPropertyNames52.js, 1, 8)) + + }) +} + diff --git a/tests/baselines/reference/computedPropertyNames52(target=es5).types b/tests/baselines/reference/computedPropertyNames52(target=es5).types new file mode 100644 index 0000000000000..7c7841402f351 --- /dev/null +++ b/tests/baselines/reference/computedPropertyNames52(target=es5).types @@ -0,0 +1,36 @@ +=== tests/cases/conformance/es6/computedProperties/computedPropertyNames52.js === +const array = []; +>array : any[] +>[] : undefined[] + +for (let i = 0; i < 10; ++i) { +>i : number +>0 : 0 +>i < 10 : boolean +>i : number +>10 : 10 +>++i : number +>i : number + + array.push(class C { +>array.push(class C { [i] = () => C; static [i] = 100; }) : number +>array.push : (...items: any[]) => number +>array : any[] +>push : (...items: any[]) => number +>class C { [i] = () => C; static [i] = 100; } : typeof C +>C : typeof C + + [i] = () => C; +>[i] : () => typeof C +>i : number +>() => C : () => typeof C +>C : typeof C + + static [i] = 100; +>[i] : number +>i : number +>100 : 100 + + }) +} + diff --git a/tests/baselines/reference/privateNameClassExpressionLoop.js b/tests/baselines/reference/privateNameClassExpressionLoop.js new file mode 100644 index 0000000000000..bd83a8c9db829 --- /dev/null +++ b/tests/baselines/reference/privateNameClassExpressionLoop.js @@ -0,0 +1,22 @@ +//// [privateNameClassExpressionLoop.ts] +const array = []; +for (let i = 0; i < 10; ++i) { + array.push(class C { + #myField = "hello"; + }); +} + + +//// [privateNameClassExpressionLoop.js] +var _a; +const array = []; +for (let i = 0; i < 10; ++i) { + let _myField; + array.push((_a = class C { + constructor() { + _myField.set(this, "hello"); + } + }, + _myField = new WeakMap(), + _a)); +} diff --git a/tests/baselines/reference/privateNameClassExpressionLoop.symbols b/tests/baselines/reference/privateNameClassExpressionLoop.symbols new file mode 100644 index 0000000000000..ac503fc6c9c08 --- /dev/null +++ b/tests/baselines/reference/privateNameClassExpressionLoop.symbols @@ -0,0 +1,21 @@ +=== tests/cases/conformance/classes/members/privateNames/privateNameClassExpressionLoop.ts === +const array = []; +>array : Symbol(array, Decl(privateNameClassExpressionLoop.ts, 0, 5)) + +for (let i = 0; i < 10; ++i) { +>i : Symbol(i, Decl(privateNameClassExpressionLoop.ts, 1, 8)) +>i : Symbol(i, Decl(privateNameClassExpressionLoop.ts, 1, 8)) +>i : Symbol(i, Decl(privateNameClassExpressionLoop.ts, 1, 8)) + + array.push(class C { +>array.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>array : Symbol(array, Decl(privateNameClassExpressionLoop.ts, 0, 5)) +>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>C : Symbol(C, Decl(privateNameClassExpressionLoop.ts, 2, 15)) + + #myField = "hello"; +>#myField : Symbol(C.#myField, Decl(privateNameClassExpressionLoop.ts, 2, 24)) + + }); +} + diff --git a/tests/baselines/reference/privateNameClassExpressionLoop.types b/tests/baselines/reference/privateNameClassExpressionLoop.types new file mode 100644 index 0000000000000..5775f9fd042d8 --- /dev/null +++ b/tests/baselines/reference/privateNameClassExpressionLoop.types @@ -0,0 +1,29 @@ +=== tests/cases/conformance/classes/members/privateNames/privateNameClassExpressionLoop.ts === +const array = []; +>array : any[] +>[] : undefined[] + +for (let i = 0; i < 10; ++i) { +>i : number +>0 : 0 +>i < 10 : boolean +>i : number +>10 : 10 +>++i : number +>i : number + + array.push(class C { +>array.push(class C { #myField = "hello"; }) : number +>array.push : (...items: any[]) => number +>array : any[] +>push : (...items: any[]) => number +>class C { #myField = "hello"; } : typeof C +>C : typeof C + + #myField = "hello"; +>#myField : string +>"hello" : "hello" + + }); +} + diff --git a/tests/cases/conformance/classes/classDeclarations/classDeclarationLoop.ts b/tests/cases/conformance/classes/classDeclarations/classDeclarationLoop.ts new file mode 100644 index 0000000000000..122d56897960c --- /dev/null +++ b/tests/cases/conformance/classes/classDeclarations/classDeclarationLoop.ts @@ -0,0 +1,7 @@ +const arr = []; +for (let i = 0; i < 10; ++i) { + class C { + prop = i; + } + arr.push(C); +} diff --git a/tests/cases/conformance/classes/classExpressions/classExpressionLoop.ts b/tests/cases/conformance/classes/classExpressions/classExpressionLoop.ts new file mode 100644 index 0000000000000..ff039f80940c4 --- /dev/null +++ b/tests/cases/conformance/classes/classExpressions/classExpressionLoop.ts @@ -0,0 +1,6 @@ +let arr = []; +for (let i = 0; i < 10; ++i) { + arr.push(class C { + prop = i; + }); +} diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameClassExpressionLoop.ts b/tests/cases/conformance/classes/members/privateNames/privateNameClassExpressionLoop.ts new file mode 100644 index 0000000000000..222be787d0e67 --- /dev/null +++ b/tests/cases/conformance/classes/members/privateNames/privateNameClassExpressionLoop.ts @@ -0,0 +1,7 @@ +// @target: es2015 +const array = []; +for (let i = 0; i < 10; ++i) { + array.push(class C { + #myField = "hello"; + }); +} diff --git a/tests/cases/conformance/es6/computedProperties/computedPropertyNames52.ts b/tests/cases/conformance/es6/computedProperties/computedPropertyNames52.ts new file mode 100644 index 0000000000000..f6afef7fec3f0 --- /dev/null +++ b/tests/cases/conformance/es6/computedProperties/computedPropertyNames52.ts @@ -0,0 +1,11 @@ +// @filename: computedPropertyNames52.js +// @out: computedPropertyNames52-emit.js +// @allowJs: true +// @target: es5, es2015 +const array = []; +for (let i = 0; i < 10; ++i) { + array.push(class C { + [i] = () => C; + static [i] = 100; + }) +}