Skip to content

Commit efe76c4

Browse files
committed
Fix per-iteration bindings in for-loop head
1 parent e710645 commit efe76c4

File tree

7 files changed

+813
-273
lines changed

7 files changed

+813
-273
lines changed

src/compiler/checker.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15620,6 +15620,10 @@ namespace ts {
1562015620
return !!findAncestor(node, n => n === threshold ? "quit" : isFunctionLike(n));
1562115621
}
1562215622

15623+
function getPartOfForStatementContainingNode(node: Node, container: ForStatement) {
15624+
return findAncestor(node, n => n === container ? "quit" : n === container.initializer || n === container.condition || n === container.incrementor || n === container.statement);
15625+
}
15626+
1562315627
function checkNestedBlockScopedBinding(node: Identifier, symbol: Symbol): void {
1562415628
if (languageVersion >= ScriptTarget.ES2015 ||
1562515629
(symbol.flags & (SymbolFlags.BlockScopedVariable | SymbolFlags.Class)) === 0 ||
@@ -15648,7 +15652,25 @@ namespace ts {
1564815652
if (containedInIterationStatement) {
1564915653
if (usedInFunction) {
1565015654
// mark iteration statement as containing block-scoped binding captured in some function
15651-
getNodeLinks(current).flags |= NodeCheckFlags.LoopWithCapturedBlockScopedBinding;
15655+
let capturesBlockScopeBindingInLoopBody = true;
15656+
if (isForStatement(container) &&
15657+
getAncestor(symbol.valueDeclaration, SyntaxKind.VariableDeclarationList)!.parent === container) {
15658+
const part = getPartOfForStatementContainingNode(node.parent, container);
15659+
if (part) {
15660+
const links = getNodeLinks(part);
15661+
links.flags |= NodeCheckFlags.ContainsCapturedBlockScopeBinding;
15662+
15663+
const capturedBindings = links.capturedBlockScopeBindings || (links.capturedBlockScopeBindings = []);
15664+
pushIfUnique(capturedBindings, symbol);
15665+
15666+
if (part === container.initializer) {
15667+
capturesBlockScopeBindingInLoopBody = false; // Initializer is outside of loop body
15668+
}
15669+
}
15670+
}
15671+
if (capturesBlockScopeBindingInLoopBody) {
15672+
getNodeLinks(current).flags |= NodeCheckFlags.LoopWithCapturedBlockScopedBinding;
15673+
}
1565215674
}
1565315675

1565415676
// mark variables that are declared in loop initializer and reassigned inside the body of ForStatement.
@@ -15668,6 +15690,11 @@ namespace ts {
1566815690
}
1566915691
}
1567015692

15693+
function isBindingCapturedByNode(node: Node, decl: VariableDeclaration | BindingElement) {
15694+
const links = getNodeLinks(node);
15695+
return !!links && contains(links.capturedBlockScopeBindings, getSymbolOfNode(decl));
15696+
}
15697+
1567115698
function isAssignedInBodyOfForStatement(node: Identifier, container: ForStatement): boolean {
1567215699
// skip parenthesized nodes
1567315700
let current: Node = node;
@@ -28498,7 +28525,12 @@ namespace ts {
2849828525
getAccessor
2849928526
};
2850028527
},
28501-
getSymbolOfExternalModuleSpecifier: moduleName => resolveExternalModuleNameWorker(moduleName, moduleName, /*moduleNotFoundError*/ undefined)
28528+
getSymbolOfExternalModuleSpecifier: moduleName => resolveExternalModuleNameWorker(moduleName, moduleName, /*moduleNotFoundError*/ undefined),
28529+
isBindingCapturedByNode: (node, decl) => {
28530+
const parseNode = getParseTreeNode(node);
28531+
const parseDecl = getParseTreeNode(decl);
28532+
return !!parseNode && !!parseDecl && (isVariableDeclaration(parseDecl) || isBindingElement(parseDecl)) && isBindingCapturedByNode(parseNode, parseDecl);
28533+
}
2850228534
};
2850328535

2850428536
function isInHeritageClause(node: PropertyAccessEntityNameExpression) {

0 commit comments

Comments
 (0)