Skip to content

Commit 29d667d

Browse files
author
Joseph Watts
committed
Signed-off-by: Joseph Watts <jwatts43@bloomberg.net>
1 parent b29c9cf commit 29d667d

File tree

4 files changed

+50
-3
lines changed

4 files changed

+50
-3
lines changed

src/compiler/checker.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17487,6 +17487,23 @@ namespace ts {
1748717487
const links = getNodeLinks(node.expression);
1748817488
if (!links.resolvedType) {
1748917489
links.resolvedType = checkExpression(node.expression);
17490+
17491+
if (languageVersion < ScriptTarget.ESNext && isPropertyDeclaration(node.parent) && isClassLike(node.parent.parent)) {
17492+
const container = getEnclosingBlockScopeContainer(node);
17493+
let current = container;
17494+
let containedInIterationStatement = false;
17495+
while (current && !nodeStartsNewLexicalEnvironment(current)) {
17496+
if (isIterationStatement(current, /*lookInLabeledStatements*/ false)) {
17497+
containedInIterationStatement = true;
17498+
break;
17499+
}
17500+
current = current.parent;
17501+
}
17502+
if (containedInIterationStatement) {
17503+
getNodeLinks(current).flags |= NodeCheckFlags.LoopWithCapturedBlockScopedBinding;
17504+
}
17505+
links.flags |= NodeCheckFlags.BlockScopedBindingInLoop;
17506+
}
1749017507
// This will allow types number, string, symbol or any. It will also allow enums, the unknown
1749117508
// type, and any union of these types (like string | number).
1749217509
if (links.resolvedType.flags & TypeFlags.Nullable ||

src/compiler/transformer.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ namespace ts {
8484
let lexicalEnvironmentFunctionDeclarations: FunctionDeclaration[];
8585
let lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = [];
8686
let lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = [];
87+
let lexicalEnvironmentScopingStack: LexicalEnvironmentScoping[] = [];
88+
let lexicalEnvironmentScoping: LexicalEnvironmentScoping;
8789
let lexicalEnvironmentStackOffset = 0;
8890
let lexicalEnvironmentSuspended = false;
8991
let emitHelpers: EmitHelper[] | undefined;
@@ -258,7 +260,7 @@ namespace ts {
258260
* Starts a new lexical environment. Any existing hoisted variable or function declarations
259261
* are pushed onto a stack, and the related storage variables are reset.
260262
*/
261-
function startLexicalEnvironment(): void {
263+
function startLexicalEnvironment(scoping: LexicalEnvironmentScoping = LexicalEnvironmentScoping.Function): void {
262264
Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the lexical environment during initialization.");
263265
Debug.assert(state < TransformationState.Completed, "Cannot modify the lexical environment after transformation has completed.");
264266
Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is suspended.");
@@ -267,11 +269,13 @@ namespace ts {
267269
// stack size variable. This allows us to reuse existing array slots we've
268270
// already allocated between transformations to avoid allocation and GC overhead during
269271
// transformation.
272+
lexicalEnvironmentScopingStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentScoping;
270273
lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentVariableDeclarations;
271274
lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentFunctionDeclarations;
272275
lexicalEnvironmentStackOffset++;
273276
lexicalEnvironmentVariableDeclarations = undefined!;
274277
lexicalEnvironmentFunctionDeclarations = undefined!;
278+
lexicalEnvironmentScoping = scoping;
275279
}
276280

277281
/** Suspends the current lexical environment, usually after visiting a parameter list. */
@@ -308,7 +312,10 @@ namespace ts {
308312
if (lexicalEnvironmentVariableDeclarations) {
309313
const statement = createVariableStatement(
310314
/*modifiers*/ undefined,
311-
createVariableDeclarationList(lexicalEnvironmentVariableDeclarations)
315+
createVariableDeclarationList(
316+
lexicalEnvironmentVariableDeclarations,
317+
lexicalEnvironmentScoping === LexicalEnvironmentScoping.Block ? NodeFlags.Let : undefined
318+
)
312319
);
313320

314321
if (!statements) {
@@ -324,9 +331,11 @@ namespace ts {
324331
lexicalEnvironmentStackOffset--;
325332
lexicalEnvironmentVariableDeclarations = lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset];
326333
lexicalEnvironmentFunctionDeclarations = lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset];
334+
lexicalEnvironmentScoping = lexicalEnvironmentScopingStack[lexicalEnvironmentStackOffset];
327335
if (lexicalEnvironmentStackOffset === 0) {
328336
lexicalEnvironmentVariableDeclarationsStack = [];
329337
lexicalEnvironmentFunctionDeclarationsStack = [];
338+
lexicalEnvironmentScopingStack = [];
330339
}
331340
return statements;
332341
}
@@ -358,6 +367,7 @@ namespace ts {
358367
lexicalEnvironmentVariableDeclarationsStack = undefined!;
359368
lexicalEnvironmentFunctionDeclarations = undefined!;
360369
lexicalEnvironmentFunctionDeclarationsStack = undefined!;
370+
lexicalEnvironmentScopingStack = undefined!;
361371
onSubstituteNode = undefined!;
362372
onEmitNode = undefined!;
363373
emitHelpers = undefined;

src/compiler/transformers/esnext.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ namespace ts {
1212

1313
export function transformESNext(context: TransformationContext) {
1414
const {
15+
startLexicalEnvironment,
1516
resumeLexicalEnvironment,
1617
endLexicalEnvironment,
1718
hoistVariableDeclaration
@@ -84,6 +85,8 @@ namespace ts {
8485
return node;
8586
}
8687
switch (node.kind) {
88+
case SyntaxKind.Block:
89+
return visitBlock(node as Block);
8790
case SyntaxKind.AwaitExpression:
8891
return visitAwaitExpression(node as AwaitExpression);
8992
case SyntaxKind.YieldExpression:
@@ -503,6 +506,18 @@ namespace ts {
503506
}
504507
}
505508

509+
function visitBlock(node: Block): Block {
510+
startLexicalEnvironment(LexicalEnvironmentScoping.Block);
511+
node = visitEachChild(node, visitor, context);
512+
const declarations = endLexicalEnvironment();
513+
if (some(declarations)) {
514+
return updateBlock(
515+
node,
516+
mergeLexicalEnvironment(node.statements, declarations)
517+
);
518+
}
519+
return node;
520+
}
506521

507522
function visitAwaitExpression(node: AwaitExpression): Expression {
508523
if (enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator) {

src/compiler/types.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5229,6 +5229,11 @@ namespace ts {
52295229
writeFile: WriteFileCallback;
52305230
}
52315231

5232+
export const enum LexicalEnvironmentScoping {
5233+
Function,
5234+
Block
5235+
};
5236+
52325237
export interface TransformationContext {
52335238
/*@internal*/ getEmitResolver(): EmitResolver;
52345239
/*@internal*/ getEmitHost(): EmitHost;
@@ -5237,7 +5242,7 @@ namespace ts {
52375242
getCompilerOptions(): CompilerOptions;
52385243

52395244
/** Starts a new lexical environment. */
5240-
startLexicalEnvironment(): void;
5245+
startLexicalEnvironment(scoping?: LexicalEnvironmentScoping): void;
52415246

52425247
/** Suspends the current lexical environment, usually after visiting a parameter list. */
52435248
suspendLexicalEnvironment(): void;

0 commit comments

Comments
 (0)