Skip to content

Commit 1bb9655

Browse files
committed
Creates a class body scoped alias to the class to avoid class name double binding.
Fixes #5386.
1 parent 6648403 commit 1bb9655

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+771
-71
lines changed

src/compiler/checker.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7192,11 +7192,33 @@ namespace ts {
71927192
markAliasSymbolAsReferenced(symbol);
71937193
}
71947194

7195+
const localSymbol = getExportSymbolOfValueSymbolIfExported(symbol);
7196+
7197+
// Due to the emit for class decorators, any reference to the class from inside of the class body
7198+
// must instead be rewritten to point to a temporary variable to avoid issues with the double-bind
7199+
// behavior of class names in ES6.
7200+
if (languageVersion === ScriptTarget.ES6
7201+
&& localSymbol
7202+
&& localSymbol.valueDeclaration
7203+
&& localSymbol.valueDeclaration.kind === SyntaxKind.ClassDeclaration
7204+
&& nodeIsDecorated(localSymbol.valueDeclaration)) {
7205+
let container = getContainingClass(node);
7206+
while (container !== undefined) {
7207+
if (container === localSymbol.valueDeclaration && container.name !== node) {
7208+
getNodeLinks(container).flags |= NodeCheckFlags.ClassWithBodyScopedClassBinding;
7209+
getNodeLinks(node).flags |= NodeCheckFlags.BodyScopedClassBinding;
7210+
break;
7211+
}
7212+
7213+
container = getContainingClass(container);
7214+
}
7215+
}
7216+
71957217
checkCollisionWithCapturedSuperVariable(node, node);
71967218
checkCollisionWithCapturedThisVariable(node, node);
71977219
checkNestedBlockScopedBinding(node, symbol);
71987220

7199-
return getNarrowedTypeOfSymbol(getExportSymbolOfValueSymbolIfExported(symbol), node);
7221+
return getNarrowedTypeOfSymbol(localSymbol, node);
72007222
}
72017223

72027224
function isInsideFunction(node: Node, threshold: Node): boolean {
@@ -7238,7 +7260,7 @@ namespace ts {
72387260

72397261
if (containedInIterationStatement) {
72407262
if (usedInFunction) {
7241-
// mark iteration statement as containing block-scoped binding captured in some function
7263+
// mark iteration statement as containing block-scoped binding captured in some function
72427264
getNodeLinks(current).flags |= NodeCheckFlags.LoopWithCapturedBlockScopedBinding;
72437265
}
72447266
// set 'declared inside loop' bit on the block-scoped binding
@@ -15734,7 +15756,7 @@ namespace ts {
1573415756
// - binding is not top level - top level bindings never collide with anything
1573515757
// AND
1573615758
// - binding is not declared in loop, should be renamed to avoid name reuse across siblings
15737-
// let a, b
15759+
// let a, b
1573815760
// { let x = 1; a = () => x; }
1573915761
// { let x = 100; b = () => x; }
1574015762
// console.log(a()); // should print '1'

src/compiler/emitter.ts

Lines changed: 121 additions & 59 deletions
Large diffs are not rendered by default.

src/compiler/types.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2068,8 +2068,10 @@ namespace ts {
20682068
EnumValuesComputed = 0x00004000,
20692069
LexicalModuleMergesWithClass = 0x00008000, // Instantiated lexical module declaration is merged with a previous class declaration.
20702070
LoopWithCapturedBlockScopedBinding = 0x00010000, // Loop that contains block scoped variable captured in closure
2071-
CapturedBlockScopedBinding = 0x00020000, // Block-scoped binding that is captured in some function
2072-
BlockScopedBindingInLoop = 0x00040000, // Block-scoped binding with declaration nested inside iteration statement
2071+
CapturedBlockScopedBinding = 0x00020000, // Block-scoped binding that is captured in some function
2072+
BlockScopedBindingInLoop = 0x00040000, // Block-scoped binding with declaration nested inside iteration statement
2073+
ClassWithBodyScopedClassBinding = 0x00080000, // Decorated class that contains a binding to itself inside of the class body.
2074+
BodyScopedClassBinding = 0x00100000, // Binding to a decorated class inside of the class's body.
20732075
}
20742076

20752077
/* @internal */

tests/baselines/reference/decoratedClassFromExternalModule.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
1717
return c > 3 && r && Object.defineProperty(target, key, r), r;
1818
};
1919
function decorate(target) { }
20-
let Decorated = class {
20+
let Decorated = class Decorated {
2121
};
2222
Decorated = __decorate([
2323
decorate

tests/baselines/reference/decoratedDefaultExportsGetExportedAmd.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
2323
define(["require", "exports"], function (require, exports) {
2424
"use strict";
2525
var decorator;
26-
let Foo = class {
26+
let Foo = class Foo {
2727
};
2828
Foo = __decorate([
2929
decorator

tests/baselines/reference/decoratedDefaultExportsGetExportedCommonjs.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
2222
return c > 3 && r && Object.defineProperty(target, key, r), r;
2323
};
2424
var decorator;
25-
let Foo = class {
25+
let Foo = class Foo {
2626
};
2727
Foo = __decorate([
2828
decorator

tests/baselines/reference/decoratedDefaultExportsGetExportedSystem.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ System.register([], function(exports_1) {
2525
return {
2626
setters:[],
2727
execute: function() {
28-
let Foo = class {
28+
let Foo = class Foo {
2929
};
3030
Foo = __decorate([
3131
decorator

tests/baselines/reference/decoratedDefaultExportsGetExportedUmd.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
3030
})(function (require, exports) {
3131
"use strict";
3232
var decorator;
33-
let Foo = class {
33+
let Foo = class Foo {
3434
};
3535
Foo = __decorate([
3636
decorator
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//// [decoratorOnClass1.es6.ts]
2+
declare function dec<T>(target: T): T;
3+
4+
@dec
5+
class C {
6+
}
7+
8+
let c = new C();
9+
10+
//// [decoratorOnClass1.es6.js]
11+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
12+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
13+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
14+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
15+
return c > 3 && r && Object.defineProperty(target, key, r), r;
16+
};
17+
let C = class C {
18+
};
19+
C = __decorate([
20+
dec
21+
], C);
22+
let c = new C();
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
=== tests/cases/conformance/es6/decorators/class/decoratorOnClass1.es6.ts ===
2+
declare function dec<T>(target: T): T;
3+
>dec : Symbol(dec, Decl(decoratorOnClass1.es6.ts, 0, 0))
4+
>T : Symbol(T, Decl(decoratorOnClass1.es6.ts, 0, 21))
5+
>target : Symbol(target, Decl(decoratorOnClass1.es6.ts, 0, 24))
6+
>T : Symbol(T, Decl(decoratorOnClass1.es6.ts, 0, 21))
7+
>T : Symbol(T, Decl(decoratorOnClass1.es6.ts, 0, 21))
8+
9+
@dec
10+
>dec : Symbol(dec, Decl(decoratorOnClass1.es6.ts, 0, 0))
11+
12+
class C {
13+
>C : Symbol(C, Decl(decoratorOnClass1.es6.ts, 0, 38))
14+
}
15+
16+
let c = new C();
17+
>c : Symbol(c, Decl(decoratorOnClass1.es6.ts, 6, 3))
18+
>C : Symbol(C, Decl(decoratorOnClass1.es6.ts, 0, 38))
19+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
=== tests/cases/conformance/es6/decorators/class/decoratorOnClass1.es6.ts ===
2+
declare function dec<T>(target: T): T;
3+
>dec : <T>(target: T) => T
4+
>T : T
5+
>target : T
6+
>T : T
7+
>T : T
8+
9+
@dec
10+
>dec : <T>(target: T) => T
11+
12+
class C {
13+
>C : C
14+
}
15+
16+
let c = new C();
17+
>c : C
18+
>new C() : C
19+
>C : typeof C
20+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//// [decoratorOnClass2.es6.ts]
2+
declare function dec<T>(target: T): T;
3+
4+
@dec
5+
export class C {
6+
}
7+
8+
let c = new C();
9+
10+
//// [decoratorOnClass2.es6.js]
11+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
12+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
13+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
14+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
15+
return c > 3 && r && Object.defineProperty(target, key, r), r;
16+
};
17+
export let C = class C {
18+
};
19+
C = __decorate([
20+
dec
21+
], C);
22+
let c = new C();
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
=== tests/cases/conformance/es6/decorators/class/decoratorOnClass2.es6.ts ===
2+
declare function dec<T>(target: T): T;
3+
>dec : Symbol(dec, Decl(decoratorOnClass2.es6.ts, 0, 0))
4+
>T : Symbol(T, Decl(decoratorOnClass2.es6.ts, 0, 21))
5+
>target : Symbol(target, Decl(decoratorOnClass2.es6.ts, 0, 24))
6+
>T : Symbol(T, Decl(decoratorOnClass2.es6.ts, 0, 21))
7+
>T : Symbol(T, Decl(decoratorOnClass2.es6.ts, 0, 21))
8+
9+
@dec
10+
>dec : Symbol(dec, Decl(decoratorOnClass2.es6.ts, 0, 0))
11+
12+
export class C {
13+
>C : Symbol(C, Decl(decoratorOnClass2.es6.ts, 0, 38))
14+
}
15+
16+
let c = new C();
17+
>c : Symbol(c, Decl(decoratorOnClass2.es6.ts, 6, 3))
18+
>C : Symbol(C, Decl(decoratorOnClass2.es6.ts, 0, 38))
19+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
=== tests/cases/conformance/es6/decorators/class/decoratorOnClass2.es6.ts ===
2+
declare function dec<T>(target: T): T;
3+
>dec : <T>(target: T) => T
4+
>T : T
5+
>target : T
6+
>T : T
7+
>T : T
8+
9+
@dec
10+
>dec : <T>(target: T) => T
11+
12+
export class C {
13+
>C : C
14+
}
15+
16+
let c = new C();
17+
>c : C
18+
>new C() : C
19+
>C : typeof C
20+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//// [decoratorOnClass3.es6.ts]
2+
declare function dec<T>(target: T): T;
3+
4+
@dec
5+
export default class C {
6+
}
7+
8+
let c = new C();
9+
10+
//// [decoratorOnClass3.es6.js]
11+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
12+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
13+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
14+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
15+
return c > 3 && r && Object.defineProperty(target, key, r), r;
16+
};
17+
let C = class C {
18+
};
19+
C = __decorate([
20+
dec
21+
], C);
22+
export default C;
23+
let c = new C();
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
=== tests/cases/conformance/es6/decorators/class/decoratorOnClass3.es6.ts ===
2+
declare function dec<T>(target: T): T;
3+
>dec : Symbol(dec, Decl(decoratorOnClass3.es6.ts, 0, 0))
4+
>T : Symbol(T, Decl(decoratorOnClass3.es6.ts, 0, 21))
5+
>target : Symbol(target, Decl(decoratorOnClass3.es6.ts, 0, 24))
6+
>T : Symbol(T, Decl(decoratorOnClass3.es6.ts, 0, 21))
7+
>T : Symbol(T, Decl(decoratorOnClass3.es6.ts, 0, 21))
8+
9+
@dec
10+
>dec : Symbol(dec, Decl(decoratorOnClass3.es6.ts, 0, 0))
11+
12+
export default class C {
13+
>C : Symbol(C, Decl(decoratorOnClass3.es6.ts, 0, 38))
14+
}
15+
16+
let c = new C();
17+
>c : Symbol(c, Decl(decoratorOnClass3.es6.ts, 6, 3))
18+
>C : Symbol(C, Decl(decoratorOnClass3.es6.ts, 0, 38))
19+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
=== tests/cases/conformance/es6/decorators/class/decoratorOnClass3.es6.ts ===
2+
declare function dec<T>(target: T): T;
3+
>dec : <T>(target: T) => T
4+
>T : T
5+
>target : T
6+
>T : T
7+
>T : T
8+
9+
@dec
10+
>dec : <T>(target: T) => T
11+
12+
export default class C {
13+
>C : C
14+
}
15+
16+
let c = new C();
17+
>c : C
18+
>new C() : C
19+
>C : typeof C
20+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//// [decoratorOnClass4.es6.ts]
2+
declare function dec<T>(target: T): T;
3+
4+
@dec
5+
export default class {
6+
}
7+
8+
//// [decoratorOnClass4.es6.js]
9+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
10+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
11+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
12+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
13+
return c > 3 && r && Object.defineProperty(target, key, r), r;
14+
};
15+
let default_1 = class {
16+
};
17+
default_1 = __decorate([
18+
dec
19+
], default_1);
20+
export default default_1;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
=== tests/cases/conformance/es6/decorators/class/decoratorOnClass4.es6.ts ===
2+
declare function dec<T>(target: T): T;
3+
>dec : Symbol(dec, Decl(decoratorOnClass4.es6.ts, 0, 0))
4+
>T : Symbol(T, Decl(decoratorOnClass4.es6.ts, 0, 21))
5+
>target : Symbol(target, Decl(decoratorOnClass4.es6.ts, 0, 24))
6+
>T : Symbol(T, Decl(decoratorOnClass4.es6.ts, 0, 21))
7+
>T : Symbol(T, Decl(decoratorOnClass4.es6.ts, 0, 21))
8+
9+
@dec
10+
>dec : Symbol(dec, Decl(decoratorOnClass4.es6.ts, 0, 0))
11+
12+
export default class {
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
=== tests/cases/conformance/es6/decorators/class/decoratorOnClass4.es6.ts ===
2+
declare function dec<T>(target: T): T;
3+
>dec : <T>(target: T) => T
4+
>T : T
5+
>target : T
6+
>T : T
7+
>T : T
8+
9+
@dec
10+
>dec : <T>(target: T) => T
11+
12+
export default class {
13+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//// [decoratorOnClass5.es6.ts]
2+
declare function dec<T>(target: T): T;
3+
4+
@dec
5+
class C {
6+
static x() { return C.y; }
7+
static y = 1;
8+
}
9+
10+
let c = new C();
11+
12+
//// [decoratorOnClass5.es6.js]
13+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
14+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
15+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
16+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
17+
return c > 3 && r && Object.defineProperty(target, key, r), r;
18+
};
19+
let C_1;
20+
let C = C_1 = class C {
21+
static x() { return C_1.y; }
22+
};
23+
C.y = 1;
24+
C = C_1 = __decorate([
25+
dec
26+
], C);
27+
let c = new C();

0 commit comments

Comments
 (0)