Merge pull request #5412 from Microsoft/classesInLoop

treat local classes as block scoped variables
This commit is contained in:
Vladimir Matveev 2015-10-27 10:23:16 -07:00
commit ea054f7cbb
9 changed files with 244 additions and 13 deletions

View File

@ -6581,26 +6581,34 @@ namespace ts {
function checkBlockScopedBindingCapturedInLoop(node: Identifier, symbol: Symbol): void {
if (languageVersion >= ScriptTarget.ES6 ||
(symbol.flags & SymbolFlags.BlockScopedVariable) === 0 ||
(symbol.flags & (SymbolFlags.BlockScopedVariable | SymbolFlags.Class)) === 0 ||
symbol.valueDeclaration.parent.kind === SyntaxKind.CatchClause) {
return;
}
// - check if binding is used in some function
// (stop the walk when reaching container of binding declaration)
// - if first check succeeded - check if variable is declared inside the loop
// 1. walk from the use site up to the declaration and check
// if there is anything function like between declaration and use-site (is binding/class is captured in function).
// 2. walk from the declaration up to the boundary of lexical environment and check
// if there is an iteration statement in between declaration and boundary (is binding/class declared inside iteration statement)
// nesting structure:
// (variable declaration or binding element) -> variable declaration list -> container
let container: Node = symbol.valueDeclaration;
while (container.kind !== SyntaxKind.VariableDeclarationList) {
container = container.parent;
let container: Node;
if (symbol.flags & SymbolFlags.Class) {
// get parent of class declaration
container = getClassLikeDeclarationOfSymbol(symbol).parent;
}
// get the parent of variable declaration list
container = container.parent;
if (container.kind === SyntaxKind.VariableStatement) {
// if parent is variable statement - get its parent
else {
// nesting structure:
// (variable declaration or binding element) -> variable declaration list -> container
container = symbol.valueDeclaration;
while (container.kind !== SyntaxKind.VariableDeclarationList) {
container = container.parent;
}
// get the parent of variable declaration list
container = container.parent;
if (container.kind === SyntaxKind.VariableStatement) {
// if parent is variable statement - get its parent
container = container.parent;
}
}
let inFunction = isInsideFunction(node.parent, container);

View File

@ -0,0 +1,27 @@
//// [localClassesInLoop.ts]
declare function use(a: any);
"use strict"
var data = [];
for (let x = 0; x < 2; ++x) {
class C { }
data.push(() => C);
}
use(data[0]() === data[1]());
//// [localClassesInLoop.js]
"use strict";
var data = [];
var _loop_1 = function(x) {
var C = (function () {
function C() {
}
return C;
})();
data.push(function () { return C; });
};
for (var x = 0; x < 2; ++x) {
_loop_1(x);
}
use(data[0]() === data[1]());

View File

@ -0,0 +1,29 @@
=== tests/cases/compiler/localClassesInLoop.ts ===
declare function use(a: any);
>use : Symbol(use, Decl(localClassesInLoop.ts, 0, 0))
>a : Symbol(a, Decl(localClassesInLoop.ts, 0, 21))
"use strict"
var data = [];
>data : Symbol(data, Decl(localClassesInLoop.ts, 3, 3))
for (let x = 0; x < 2; ++x) {
>x : Symbol(x, Decl(localClassesInLoop.ts, 4, 8))
>x : Symbol(x, Decl(localClassesInLoop.ts, 4, 8))
>x : Symbol(x, Decl(localClassesInLoop.ts, 4, 8))
class C { }
>C : Symbol(C, Decl(localClassesInLoop.ts, 4, 29))
data.push(() => C);
>data.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>data : Symbol(data, Decl(localClassesInLoop.ts, 3, 3))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>C : Symbol(C, Decl(localClassesInLoop.ts, 4, 29))
}
use(data[0]() === data[1]());
>use : Symbol(use, Decl(localClassesInLoop.ts, 0, 0))
>data : Symbol(data, Decl(localClassesInLoop.ts, 3, 3))
>data : Symbol(data, Decl(localClassesInLoop.ts, 3, 3))

View File

@ -0,0 +1,46 @@
=== tests/cases/compiler/localClassesInLoop.ts ===
declare function use(a: any);
>use : (a: any) => any
>a : any
"use strict"
>"use strict" : string
var data = [];
>data : any[]
>[] : undefined[]
for (let x = 0; x < 2; ++x) {
>x : number
>0 : number
>x < 2 : boolean
>x : number
>2 : number
>++x : number
>x : number
class C { }
>C : C
data.push(() => C);
>data.push(() => C) : number
>data.push : (...items: any[]) => number
>data : any[]
>push : (...items: any[]) => number
>() => C : () => typeof C
>C : typeof C
}
use(data[0]() === data[1]());
>use(data[0]() === data[1]()) : any
>use : (a: any) => any
>data[0]() === data[1]() : boolean
>data[0]() : any
>data[0] : any
>data : any[]
>0 : number
>data[1]() : any
>data[1] : any
>data : any[]
>1 : number

View File

@ -0,0 +1,22 @@
//// [localClassesInLoop_ES6.ts]
declare function use(a: any);
"use strict"
var data = [];
for (let x = 0; x < 2; ++x) {
class C { }
data.push(() => C);
}
use(data[0]() === data[1]());
//// [localClassesInLoop_ES6.js]
"use strict";
var data = [];
for (let x = 0; x < 2; ++x) {
class C {
}
data.push(() => C);
}
use(data[0]() === data[1]());

View File

@ -0,0 +1,30 @@
=== tests/cases/compiler/localClassesInLoop_ES6.ts ===
declare function use(a: any);
>use : Symbol(use, Decl(localClassesInLoop_ES6.ts, 0, 0))
>a : Symbol(a, Decl(localClassesInLoop_ES6.ts, 1, 21))
"use strict"
var data = [];
>data : Symbol(data, Decl(localClassesInLoop_ES6.ts, 4, 3))
for (let x = 0; x < 2; ++x) {
>x : Symbol(x, Decl(localClassesInLoop_ES6.ts, 5, 8))
>x : Symbol(x, Decl(localClassesInLoop_ES6.ts, 5, 8))
>x : Symbol(x, Decl(localClassesInLoop_ES6.ts, 5, 8))
class C { }
>C : Symbol(C, Decl(localClassesInLoop_ES6.ts, 5, 29))
data.push(() => C);
>data.push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>data : Symbol(data, Decl(localClassesInLoop_ES6.ts, 4, 3))
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
>C : Symbol(C, Decl(localClassesInLoop_ES6.ts, 5, 29))
}
use(data[0]() === data[1]());
>use : Symbol(use, Decl(localClassesInLoop_ES6.ts, 0, 0))
>data : Symbol(data, Decl(localClassesInLoop_ES6.ts, 4, 3))
>data : Symbol(data, Decl(localClassesInLoop_ES6.ts, 4, 3))

View File

@ -0,0 +1,47 @@
=== tests/cases/compiler/localClassesInLoop_ES6.ts ===
declare function use(a: any);
>use : (a: any) => any
>a : any
"use strict"
>"use strict" : string
var data = [];
>data : any[]
>[] : undefined[]
for (let x = 0; x < 2; ++x) {
>x : number
>0 : number
>x < 2 : boolean
>x : number
>2 : number
>++x : number
>x : number
class C { }
>C : C
data.push(() => C);
>data.push(() => C) : number
>data.push : (...items: any[]) => number
>data : any[]
>push : (...items: any[]) => number
>() => C : () => typeof C
>C : typeof C
}
use(data[0]() === data[1]());
>use(data[0]() === data[1]()) : any
>use : (a: any) => any
>data[0]() === data[1]() : boolean
>data[0]() : any
>data[0] : any
>data : any[]
>0 : number
>data[1]() : any
>data[1] : any
>data : any[]
>1 : number

View File

@ -0,0 +1,10 @@
declare function use(a: any);
"use strict"
var data = [];
for (let x = 0; x < 2; ++x) {
class C { }
data.push(() => C);
}
use(data[0]() === data[1]());

View File

@ -0,0 +1,12 @@
// @target: ES6
declare function use(a: any);
"use strict"
var data = [];
for (let x = 0; x < 2; ++x) {
class C { }
data.push(() => C);
}
use(data[0]() === data[1]());