Skip to content

Commit 9c12240

Browse files
joeywattsJoey Watts
authored and
Joey Watts
committed
Fix block-scope capturing with prop initializers
1 parent 9f5db6a commit 9c12240

File tree

9 files changed

+169
-5
lines changed

9 files changed

+169
-5
lines changed

Diff for: src/compiler/checker.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -22254,8 +22254,10 @@ namespace ts {
2225422254
return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
2225522255
}
2225622256

22257-
function isInsideFunction(node: Node, threshold: Node): boolean {
22258-
return !!findAncestor(node, n => n === threshold ? "quit" : isFunctionLike(n));
22257+
function isInsideFunctionOrInstancePropertyInitializer(node: Node, threshold: Node): boolean {
22258+
return !!findAncestor(node, n => n === threshold ? "quit" : isFunctionLike(n) || (
22259+
n.parent && isPropertyDeclaration(n.parent) && !hasStaticModifier(n.parent) && n.parent.initializer === n
22260+
));
2225922261
}
2226022262

2226122263
function getPartOfForStatementContainingNode(node: Node, container: ForStatement) {
@@ -22280,11 +22282,11 @@ namespace ts {
2228022282
// if there is an iteration statement in between declaration and boundary (is binding/class declared inside iteration statement)
2228122283

2228222284
const container = getEnclosingBlockScopeContainer(symbol.valueDeclaration);
22283-
const usedInFunction = isInsideFunction(node.parent, container);
22285+
const isCaptured = isInsideFunctionOrInstancePropertyInitializer(node, container);
2228422286

2228522287
const enclosingIterationStatement = getEnclosingIterationStatement(container);
2228622288
if (enclosingIterationStatement) {
22287-
if (usedInFunction) {
22289+
if (isCaptured) {
2228822290
// mark iteration statement as containing block-scoped binding captured in some function
2228922291
let capturesBlockScopeBindingInLoopBody = true;
2229022292
if (isForStatement(container)) {
@@ -22322,7 +22324,7 @@ namespace ts {
2232222324
getNodeLinks(symbol.valueDeclaration).flags |= NodeCheckFlags.BlockScopedBindingInLoop;
2232322325
}
2232422326

22325-
if (usedInFunction) {
22327+
if (isCaptured) {
2232622328
getNodeLinks(symbol.valueDeclaration).flags |= NodeCheckFlags.CapturedBlockScopedBinding;
2232722329
}
2232822330
}

Diff for: tests/baselines/reference/classDeclarationLoop.js

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//// [classDeclarationLoop.ts]
2+
const arr = [];
3+
for (let i = 0; i < 10; ++i) {
4+
class C {
5+
prop = i;
6+
}
7+
arr.push(C);
8+
}
9+
10+
11+
//// [classDeclarationLoop.js]
12+
var arr = [];
13+
var _loop_1 = function (i) {
14+
var C = /** @class */ (function () {
15+
function C() {
16+
this.prop = i;
17+
}
18+
return C;
19+
}());
20+
arr.push(C);
21+
};
22+
for (var i = 0; i < 10; ++i) {
23+
_loop_1(i);
24+
}
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
=== tests/cases/conformance/classes/classDeclarations/classDeclarationLoop.ts ===
2+
const arr = [];
3+
>arr : Symbol(arr, Decl(classDeclarationLoop.ts, 0, 5))
4+
5+
for (let i = 0; i < 10; ++i) {
6+
>i : Symbol(i, Decl(classDeclarationLoop.ts, 1, 8))
7+
>i : Symbol(i, Decl(classDeclarationLoop.ts, 1, 8))
8+
>i : Symbol(i, Decl(classDeclarationLoop.ts, 1, 8))
9+
10+
class C {
11+
>C : Symbol(C, Decl(classDeclarationLoop.ts, 1, 30))
12+
13+
prop = i;
14+
>prop : Symbol(C.prop, Decl(classDeclarationLoop.ts, 2, 13))
15+
>i : Symbol(i, Decl(classDeclarationLoop.ts, 1, 8))
16+
}
17+
arr.push(C);
18+
>arr.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
19+
>arr : Symbol(arr, Decl(classDeclarationLoop.ts, 0, 5))
20+
>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
21+
>C : Symbol(C, Decl(classDeclarationLoop.ts, 1, 30))
22+
}
23+

Diff for: tests/baselines/reference/classDeclarationLoop.types

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
=== tests/cases/conformance/classes/classDeclarations/classDeclarationLoop.ts ===
2+
const arr = [];
3+
>arr : any[]
4+
>[] : undefined[]
5+
6+
for (let i = 0; i < 10; ++i) {
7+
>i : number
8+
>0 : 0
9+
>i < 10 : boolean
10+
>i : number
11+
>10 : 10
12+
>++i : number
13+
>i : number
14+
15+
class C {
16+
>C : C
17+
18+
prop = i;
19+
>prop : number
20+
>i : number
21+
}
22+
arr.push(C);
23+
>arr.push(C) : number
24+
>arr.push : (...items: any[]) => number
25+
>arr : any[]
26+
>push : (...items: any[]) => number
27+
>C : typeof C
28+
}
29+

Diff for: tests/baselines/reference/classExpressionLoop.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//// [classExpressionLoop.ts]
2+
let arr = [];
3+
for (let i = 0; i < 10; ++i) {
4+
arr.push(class C {
5+
prop = i;
6+
});
7+
}
8+
9+
10+
//// [classExpressionLoop.js]
11+
var arr = [];
12+
var _loop_1 = function (i) {
13+
arr.push(/** @class */ (function () {
14+
function C() {
15+
this.prop = i;
16+
}
17+
return C;
18+
}()));
19+
};
20+
for (var i = 0; i < 10; ++i) {
21+
_loop_1(i);
22+
}
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
=== tests/cases/conformance/classes/classExpressions/classExpressionLoop.ts ===
2+
let arr = [];
3+
>arr : Symbol(arr, Decl(classExpressionLoop.ts, 0, 3))
4+
5+
for (let i = 0; i < 10; ++i) {
6+
>i : Symbol(i, Decl(classExpressionLoop.ts, 1, 8))
7+
>i : Symbol(i, Decl(classExpressionLoop.ts, 1, 8))
8+
>i : Symbol(i, Decl(classExpressionLoop.ts, 1, 8))
9+
10+
arr.push(class C {
11+
>arr.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
12+
>arr : Symbol(arr, Decl(classExpressionLoop.ts, 0, 3))
13+
>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
14+
>C : Symbol(C, Decl(classExpressionLoop.ts, 2, 13))
15+
16+
prop = i;
17+
>prop : Symbol(C.prop, Decl(classExpressionLoop.ts, 2, 22))
18+
>i : Symbol(i, Decl(classExpressionLoop.ts, 1, 8))
19+
20+
});
21+
}
22+

Diff for: tests/baselines/reference/classExpressionLoop.types

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
=== tests/cases/conformance/classes/classExpressions/classExpressionLoop.ts ===
2+
let arr = [];
3+
>arr : any[]
4+
>[] : undefined[]
5+
6+
for (let i = 0; i < 10; ++i) {
7+
>i : number
8+
>0 : 0
9+
>i < 10 : boolean
10+
>i : number
11+
>10 : 10
12+
>++i : number
13+
>i : number
14+
15+
arr.push(class C {
16+
>arr.push(class C { prop = i; }) : number
17+
>arr.push : (...items: any[]) => number
18+
>arr : any[]
19+
>push : (...items: any[]) => number
20+
>class C { prop = i; } : typeof C
21+
>C : typeof C
22+
23+
prop = i;
24+
>prop : number
25+
>i : number
26+
27+
});
28+
}
29+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const arr = [];
2+
for (let i = 0; i < 10; ++i) {
3+
class C {
4+
prop = i;
5+
}
6+
arr.push(C);
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
let arr = [];
2+
for (let i = 0; i < 10; ++i) {
3+
arr.push(class C {
4+
prop = i;
5+
});
6+
}

0 commit comments

Comments
 (0)