Skip to content

Commit bc91292

Browse files
authored
Fix Computed Property Name Bindings (#17)
* Fix microsoft#27864 Signed-off-by: Joseph Watts <[email protected]> * Tests for computed field scope fix Signed-off-by: Joseph Watts <[email protected]>
1 parent aa3734c commit bc91292

19 files changed

+350
-9
lines changed

src/compiler/checker.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17834,6 +17834,23 @@ namespace ts {
1783417834
const links = getNodeLinks(node.expression);
1783517835
if (!links.resolvedType) {
1783617836
links.resolvedType = checkExpression(node.expression);
17837+
17838+
if (isPropertyDeclaration(node.parent) && isClassLike(node.parent.parent)) {
17839+
const container = getEnclosingBlockScopeContainer(node);
17840+
let current = container;
17841+
let containedInIterationStatement = false;
17842+
while (current && !nodeStartsNewLexicalEnvironment(current)) {
17843+
if (isIterationStatement(current, /*lookInLabeledStatements*/ false)) {
17844+
containedInIterationStatement = true;
17845+
break;
17846+
}
17847+
current = current.parent;
17848+
}
17849+
if (containedInIterationStatement) {
17850+
getNodeLinks(current).flags |= NodeCheckFlags.LoopWithCapturedBlockScopedBinding;
17851+
}
17852+
links.flags |= NodeCheckFlags.BlockScopedBindingInLoop;
17853+
}
1783717854
// This will allow types number, string, symbol or any. It will also allow enums, the unknown
1783817855
// type, and any union of these types (like string | number).
1783917856
if (links.resolvedType.flags & TypeFlags.Nullable ||

src/compiler/transformer.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ namespace ts {
9292
let lexicalEnvironmentFunctionDeclarations: FunctionDeclaration[];
9393
let lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = [];
9494
let lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = [];
95+
let lexicalEnvironmentScopingStack: LexicalEnvironmentScoping[] = [];
96+
let lexicalEnvironmentScoping: LexicalEnvironmentScoping;
9597
let lexicalEnvironmentStackOffset = 0;
9698
let lexicalEnvironmentSuspended = false;
9799
let emitHelpers: EmitHelper[] | undefined;
@@ -266,7 +268,7 @@ namespace ts {
266268
* Starts a new lexical environment. Any existing hoisted variable or function declarations
267269
* are pushed onto a stack, and the related storage variables are reset.
268270
*/
269-
function startLexicalEnvironment(): void {
271+
function startLexicalEnvironment(scoping: LexicalEnvironmentScoping = LexicalEnvironmentScoping.Function): void {
270272
Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the lexical environment during initialization.");
271273
Debug.assert(state < TransformationState.Completed, "Cannot modify the lexical environment after transformation has completed.");
272274
Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is suspended.");
@@ -275,11 +277,13 @@ namespace ts {
275277
// stack size variable. This allows us to reuse existing array slots we've
276278
// already allocated between transformations to avoid allocation and GC overhead during
277279
// transformation.
280+
lexicalEnvironmentScopingStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentScoping;
278281
lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentVariableDeclarations;
279282
lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentFunctionDeclarations;
280283
lexicalEnvironmentStackOffset++;
281284
lexicalEnvironmentVariableDeclarations = undefined!;
282285
lexicalEnvironmentFunctionDeclarations = undefined!;
286+
lexicalEnvironmentScoping = scoping;
283287
}
284288

285289
/** Suspends the current lexical environment, usually after visiting a parameter list. */
@@ -316,7 +320,10 @@ namespace ts {
316320
if (lexicalEnvironmentVariableDeclarations) {
317321
const statement = createVariableStatement(
318322
/*modifiers*/ undefined,
319-
createVariableDeclarationList(lexicalEnvironmentVariableDeclarations)
323+
createVariableDeclarationList(
324+
lexicalEnvironmentVariableDeclarations,
325+
lexicalEnvironmentScoping === LexicalEnvironmentScoping.Block ? NodeFlags.Let : undefined
326+
)
320327
);
321328

322329
if (!statements) {
@@ -332,9 +339,11 @@ namespace ts {
332339
lexicalEnvironmentStackOffset--;
333340
lexicalEnvironmentVariableDeclarations = lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset];
334341
lexicalEnvironmentFunctionDeclarations = lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset];
342+
lexicalEnvironmentScoping = lexicalEnvironmentScopingStack[lexicalEnvironmentStackOffset];
335343
if (lexicalEnvironmentStackOffset === 0) {
336344
lexicalEnvironmentVariableDeclarationsStack = [];
337345
lexicalEnvironmentFunctionDeclarationsStack = [];
346+
lexicalEnvironmentScopingStack = [];
338347
}
339348
return statements;
340349
}
@@ -366,6 +375,7 @@ namespace ts {
366375
lexicalEnvironmentVariableDeclarationsStack = undefined!;
367376
lexicalEnvironmentFunctionDeclarations = undefined!;
368377
lexicalEnvironmentFunctionDeclarationsStack = undefined!;
378+
lexicalEnvironmentScopingStack = undefined!;
369379
onSubstituteNode = undefined!;
370380
onEmitNode = undefined!;
371381
emitHelpers = undefined;

src/compiler/transformers/ts.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,10 @@ namespace ts {
208208
* @param node The node to visit.
209209
*/
210210
function visitorWorker(node: Node): VisitResult<Node> {
211-
if (node.transformFlags & TransformFlags.TypeScript) {
211+
if (node.kind === SyntaxKind.Block && node.transformFlags & TransformFlags.AssertTypeScript) {
212+
return visitBlock(node as Block);
213+
}
214+
else if (node.transformFlags & TransformFlags.TypeScript) {
212215
// This node is explicitly marked as TypeScript, so we should transform the node.
213216
return visitTypeScript(node);
214217
}
@@ -560,6 +563,19 @@ namespace ts {
560563
}
561564
}
562565

566+
function visitBlock(node: Block): Block {
567+
startLexicalEnvironment(LexicalEnvironmentScoping.Block);
568+
node = visitEachChild(node, visitor, context);
569+
const declarations = endLexicalEnvironment();
570+
if (some(declarations)) {
571+
return updateBlock(
572+
node,
573+
mergeLexicalEnvironment(node.statements, declarations)
574+
);
575+
}
576+
return node;
577+
}
578+
563579
function visitSourceFile(node: SourceFile) {
564580
const alwaysStrict = getStrictOptionValue(compilerOptions, "alwaysStrict") &&
565581
!(isExternalModule(node) && moduleKind >= ModuleKind.ES2015) &&

src/compiler/types.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5241,6 +5241,11 @@ namespace ts {
52415241
writeFile: WriteFileCallback;
52425242
}
52435243

5244+
export const enum LexicalEnvironmentScoping {
5245+
Function,
5246+
Block
5247+
}
5248+
52445249
export interface TransformationContext {
52455250
/*@internal*/ getEmitResolver(): EmitResolver;
52465251
/*@internal*/ getEmitHost(): EmitHost;
@@ -5249,7 +5254,7 @@ namespace ts {
52495254
getCompilerOptions(): CompilerOptions;
52505255

52515256
/** Starts a new lexical environment. */
5252-
startLexicalEnvironment(): void;
5257+
startLexicalEnvironment(scoping?: LexicalEnvironmentScoping): void;
52535258

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

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2756,11 +2756,15 @@ declare namespace ts {
27562756
Unspecified = 4,
27572757
EmbeddedStatement = 5
27582758
}
2759+
enum LexicalEnvironmentScoping {
2760+
Function = 0,
2761+
Block = 1
2762+
}
27592763
interface TransformationContext {
27602764
/** Gets the compiler options supplied to the transformer. */
27612765
getCompilerOptions(): CompilerOptions;
27622766
/** Starts a new lexical environment. */
2763-
startLexicalEnvironment(): void;
2767+
startLexicalEnvironment(scoping?: LexicalEnvironmentScoping): void;
27642768
/** Suspends the current lexical environment, usually after visiting a parameter list. */
27652769
suspendLexicalEnvironment(): void;
27662770
/** Resumes a suspended lexical environment, usually before visiting a function body. */

tests/baselines/reference/api/typescript.d.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2756,11 +2756,15 @@ declare namespace ts {
27562756
Unspecified = 4,
27572757
EmbeddedStatement = 5
27582758
}
2759+
enum LexicalEnvironmentScoping {
2760+
Function = 0,
2761+
Block = 1
2762+
}
27592763
interface TransformationContext {
27602764
/** Gets the compiler options supplied to the transformer. */
27612765
getCompilerOptions(): CompilerOptions;
27622766
/** Starts a new lexical environment. */
2763-
startLexicalEnvironment(): void;
2767+
startLexicalEnvironment(scoping?: LexicalEnvironmentScoping): void;
27642768
/** Suspends the current lexical environment, usually after visiting a parameter list. */
27652769
suspendLexicalEnvironment(): void;
27662770
/** Resumes a suspended lexical environment, usually before visiting a function body. */

tests/baselines/reference/classBlockScoping.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ function f(b: boolean) {
3535

3636
//// [classBlockScoping.js]
3737
function f(b) {
38-
var _a;
3938
var Foo;
4039
if (b) {
40+
var _a = void 0;
4141
Foo = (_a = /** @class */ (function () {
4242
function Foo() {
4343
}

tests/baselines/reference/classExpressionWithStaticProperties3.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ for (let i = 0; i < 3; i++) {
1010
arr.forEach(C => console.log(C.y()));
1111

1212
//// [classExpressionWithStaticProperties3.js]
13-
var _a;
1413
var arr = [];
1514
var _loop_1 = function (i) {
15+
var _a = void 0;
1616
arr.push((_a = /** @class */ (function () {
1717
function C() {
1818
}

tests/baselines/reference/classExpressionWithStaticPropertiesES63.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ for (let i = 0; i < 3; i++) {
1010
arr.forEach(C => console.log(C.y()));
1111

1212
//// [classExpressionWithStaticPropertiesES63.js]
13-
var _a;
1413
const arr = [];
1514
for (let i = 0; i < 3; i++) {
15+
let _a;
1616
arr.push((_a = class C {
1717
},
1818
_a.x = i,
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
tests/cases/conformance/es6/computedProperties/computedPropertyNames52_ES5.ts(5,13): error TS1166: A computed property name in a class property declaration must refer to an expression whose type is a literal type or a 'unique symbol' type.
2+
3+
4+
==== tests/cases/conformance/es6/computedProperties/computedPropertyNames52_ES5.ts (1 errors) ====
5+
const classes = [];
6+
for (let i = 0; i <= 10; ++i) {
7+
classes.push(
8+
class A {
9+
[i] = "my property";
10+
~~~
11+
!!! error TS1166: A computed property name in a class property declaration must refer to an expression whose type is a literal type or a 'unique symbol' type.
12+
}
13+
);
14+
}
15+
for (const clazz of classes) {
16+
console.log(Object.getOwnPropertyNames(new clazz()));
17+
}

0 commit comments

Comments
 (0)