Skip to content

Commit 9cf201c

Browse files
authored
Merge pull request microsoft#27204 from Microsoft/fixPerIteration
Fix per-iteration bindings in for-loop head
2 parents 7bf382e + efe76c4 commit 9cf201c

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
@@ -15752,6 +15752,10 @@ namespace ts {
1575215752
return !!findAncestor(node, n => n === threshold ? "quit" : isFunctionLike(n));
1575315753
}
1575415754

15755+
function getPartOfForStatementContainingNode(node: Node, container: ForStatement) {
15756+
return findAncestor(node, n => n === container ? "quit" : n === container.initializer || n === container.condition || n === container.incrementor || n === container.statement);
15757+
}
15758+
1575515759
function checkNestedBlockScopedBinding(node: Identifier, symbol: Symbol): void {
1575615760
if (languageVersion >= ScriptTarget.ES2015 ||
1575715761
(symbol.flags & (SymbolFlags.BlockScopedVariable | SymbolFlags.Class)) === 0 ||
@@ -15780,7 +15784,25 @@ namespace ts {
1578015784
if (containedInIterationStatement) {
1578115785
if (usedInFunction) {
1578215786
// mark iteration statement as containing block-scoped binding captured in some function
15783-
getNodeLinks(current).flags |= NodeCheckFlags.LoopWithCapturedBlockScopedBinding;
15787+
let capturesBlockScopeBindingInLoopBody = true;
15788+
if (isForStatement(container) &&
15789+
getAncestor(symbol.valueDeclaration, SyntaxKind.VariableDeclarationList)!.parent === container) {
15790+
const part = getPartOfForStatementContainingNode(node.parent, container);
15791+
if (part) {
15792+
const links = getNodeLinks(part);
15793+
links.flags |= NodeCheckFlags.ContainsCapturedBlockScopeBinding;
15794+
15795+
const capturedBindings = links.capturedBlockScopeBindings || (links.capturedBlockScopeBindings = []);
15796+
pushIfUnique(capturedBindings, symbol);
15797+
15798+
if (part === container.initializer) {
15799+
capturesBlockScopeBindingInLoopBody = false; // Initializer is outside of loop body
15800+
}
15801+
}
15802+
}
15803+
if (capturesBlockScopeBindingInLoopBody) {
15804+
getNodeLinks(current).flags |= NodeCheckFlags.LoopWithCapturedBlockScopedBinding;
15805+
}
1578415806
}
1578515807

1578615808
// mark variables that are declared in loop initializer and reassigned inside the body of ForStatement.
@@ -15800,6 +15822,11 @@ namespace ts {
1580015822
}
1580115823
}
1580215824

15825+
function isBindingCapturedByNode(node: Node, decl: VariableDeclaration | BindingElement) {
15826+
const links = getNodeLinks(node);
15827+
return !!links && contains(links.capturedBlockScopeBindings, getSymbolOfNode(decl));
15828+
}
15829+
1580315830
function isAssignedInBodyOfForStatement(node: Identifier, container: ForStatement): boolean {
1580415831
// skip parenthesized nodes
1580515832
let current: Node = node;
@@ -28686,7 +28713,12 @@ namespace ts {
2868628713
getAccessor
2868728714
};
2868828715
},
28689-
getSymbolOfExternalModuleSpecifier: moduleName => resolveExternalModuleNameWorker(moduleName, moduleName, /*moduleNotFoundError*/ undefined)
28716+
getSymbolOfExternalModuleSpecifier: moduleName => resolveExternalModuleNameWorker(moduleName, moduleName, /*moduleNotFoundError*/ undefined),
28717+
isBindingCapturedByNode: (node, decl) => {
28718+
const parseNode = getParseTreeNode(node);
28719+
const parseDecl = getParseTreeNode(decl);
28720+
return !!parseNode && !!parseDecl && (isVariableDeclaration(parseDecl) || isBindingElement(parseDecl)) && isBindingCapturedByNode(parseNode, parseDecl);
28721+
}
2869028722
};
2869128723

2869228724
function isInHeritageClause(node: PropertyAccessEntityNameExpression) {

0 commit comments

Comments
 (0)