Merge pull request #15379 from Microsoft/fix14945
Fix class name emit in ES5
This commit is contained in:
commit
6756e3e44c
|
@ -2949,6 +2949,28 @@ namespace ts {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the internal name of a declaration. This is primarily used for declarations that can be
|
||||
* referred to by name in the body of an ES5 class function body. An internal name will *never*
|
||||
* be prefixed with an module or namespace export modifier like "exports." when emitted as an
|
||||
* expression. An internal name will also *never* be renamed due to a collision with a block
|
||||
* scoped variable.
|
||||
*
|
||||
* @param node The declaration.
|
||||
* @param allowComments A value indicating whether comments may be emitted for the name.
|
||||
* @param allowSourceMaps A value indicating whether source maps may be emitted for the name.
|
||||
*/
|
||||
export function getInternalName(node: Declaration, allowComments?: boolean, allowSourceMaps?: boolean) {
|
||||
return getName(node, allowComments, allowSourceMaps, EmitFlags.LocalName | EmitFlags.InternalName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether an identifier should only be referred to by its internal name.
|
||||
*/
|
||||
export function isInternalName(node: Identifier) {
|
||||
return (getEmitFlags(node) & EmitFlags.InternalName) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the local name of a declaration. This is primarily used for declarations that can be
|
||||
* referred to by name in the declaration's immediate scope (classes, enums, namespaces). A
|
||||
|
|
|
@ -813,7 +813,7 @@ namespace ts {
|
|||
|
||||
// Create a synthetic text range for the return statement.
|
||||
const closingBraceLocation = createTokenRange(skipTrivia(currentText, node.members.end), SyntaxKind.CloseBraceToken);
|
||||
const localName = getLocalName(node);
|
||||
const localName = getInternalName(node);
|
||||
|
||||
// The following partially-emitted expression exists purely to align our sourcemap
|
||||
// emit with the original emitter.
|
||||
|
@ -870,7 +870,7 @@ namespace ts {
|
|||
/*decorators*/ undefined,
|
||||
/*modifiers*/ undefined,
|
||||
/*asteriskToken*/ undefined,
|
||||
getDeclarationName(node),
|
||||
getInternalName(node),
|
||||
/*typeParameters*/ undefined,
|
||||
transformConstructorParameters(constructor, hasSynthesizedSuper),
|
||||
/*type*/ undefined,
|
||||
|
@ -3725,7 +3725,7 @@ namespace ts {
|
|||
function substituteIdentifier(node: Identifier) {
|
||||
// Only substitute the identifier if we have enabled substitutions for block-scoped
|
||||
// bindings.
|
||||
if (enabledSubstitutions & ES2015SubstitutionFlags.BlockScopedBindings) {
|
||||
if (enabledSubstitutions & ES2015SubstitutionFlags.BlockScopedBindings && !isInternalName(node)) {
|
||||
const original = getParseTreeNode(node, isIdentifier);
|
||||
if (original && isNameOfDeclarationWithCollidingName(original)) {
|
||||
return setTextRange(getGeneratedNameForNode(original), node);
|
||||
|
@ -3778,9 +3778,9 @@ namespace ts {
|
|||
* @param node An Identifier node.
|
||||
*/
|
||||
function substituteExpressionIdentifier(node: Identifier): Identifier {
|
||||
if (enabledSubstitutions & ES2015SubstitutionFlags.BlockScopedBindings) {
|
||||
if (enabledSubstitutions & ES2015SubstitutionFlags.BlockScopedBindings && !isInternalName(node)) {
|
||||
const declaration = resolver.getReferencedDeclarationWithCollidingName(node);
|
||||
if (declaration) {
|
||||
if (declaration && !(isClassLike(declaration) && isPartOfClassBody(declaration, node))) {
|
||||
return setTextRange(getGeneratedNameForNode(declaration.name), node);
|
||||
}
|
||||
}
|
||||
|
@ -3788,6 +3788,32 @@ namespace ts {
|
|||
return node;
|
||||
}
|
||||
|
||||
function isPartOfClassBody(declaration: ClassLikeDeclaration, node: Identifier) {
|
||||
let currentNode = getParseTreeNode(node);
|
||||
if (!currentNode || currentNode === declaration || currentNode.end <= declaration.pos || currentNode.pos >= declaration.end) {
|
||||
// if the node has no correlation to a parse tree node, its definitely not
|
||||
// part of the body.
|
||||
// if the node is outside of the document range of the declaration, its
|
||||
// definitely not part of the body.
|
||||
return false;
|
||||
}
|
||||
const blockScope = getEnclosingBlockScopeContainer(declaration);
|
||||
while (currentNode) {
|
||||
if (currentNode === blockScope || currentNode === declaration) {
|
||||
// if we are in the enclosing block scope of the declaration, we are definitely
|
||||
// not inside the class body.
|
||||
return false;
|
||||
}
|
||||
if (isClassElement(currentNode) && currentNode.parent === declaration) {
|
||||
// we are in the class body, but we treat static fields as outside of the class body
|
||||
return currentNode.kind !== SyntaxKind.PropertyDeclaration
|
||||
|| (getModifierFlags(currentNode) & ModifierFlags.Static) === 0;
|
||||
}
|
||||
currentNode = currentNode.parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes `this` when contained within an arrow function.
|
||||
*
|
||||
|
@ -3802,8 +3828,9 @@ namespace ts {
|
|||
}
|
||||
|
||||
function getClassMemberPrefix(node: ClassExpression | ClassDeclaration, member: ClassElement) {
|
||||
const expression = getLocalName(node);
|
||||
return hasModifier(member, ModifierFlags.Static) ? expression : createPropertyAccess(expression, "prototype");
|
||||
return hasModifier(member, ModifierFlags.Static)
|
||||
? getInternalName(node)
|
||||
: createPropertyAccess(getInternalName(node), "prototype");
|
||||
}
|
||||
|
||||
function hasSynthesizedDefaultSuperCall(constructor: ConstructorDeclaration, hasExtendsClause: boolean) {
|
||||
|
|
|
@ -3940,14 +3940,15 @@ namespace ts {
|
|||
HelperName = 1 << 12,
|
||||
ExportName = 1 << 13, // Ensure an export prefix is added for an identifier that points to an exported declaration with a local name (see SymbolFlags.ExportHasLocal).
|
||||
LocalName = 1 << 14, // Ensure an export prefix is not added for an identifier that points to an exported declaration.
|
||||
Indented = 1 << 15, // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter).
|
||||
NoIndentation = 1 << 16, // Do not indent the node.
|
||||
AsyncFunctionBody = 1 << 17,
|
||||
ReuseTempVariableScope = 1 << 18, // Reuse the existing temp variable scope during emit.
|
||||
CustomPrologue = 1 << 19, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed).
|
||||
NoHoisting = 1 << 20, // Do not hoist this declaration in --module system
|
||||
HasEndOfDeclarationMarker = 1 << 21, // Declaration has an associated NotEmittedStatement to mark the end of the declaration
|
||||
Iterator = 1 << 22, // The expression to a `yield*` should be treated as an Iterator when down-leveling, not an Iterable.
|
||||
InternalName = 1 << 15, // The name is internal to an ES5 class body function.
|
||||
Indented = 1 << 16, // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter).
|
||||
NoIndentation = 1 << 17, // Do not indent the node.
|
||||
AsyncFunctionBody = 1 << 18,
|
||||
ReuseTempVariableScope = 1 << 19, // Reuse the existing temp variable scope during emit.
|
||||
CustomPrologue = 1 << 20, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed).
|
||||
NoHoisting = 1 << 21, // Do not hoist this declaration in --module system
|
||||
HasEndOfDeclarationMarker = 1 << 22, // Declaration has an associated NotEmittedStatement to mark the end of the declaration
|
||||
Iterator = 1 << 23, // The expression to a `yield*` should be treated as an Iterator when down-leveling, not an Iterable.
|
||||
}
|
||||
|
||||
export interface EmitHelper {
|
||||
|
|
71
tests/baselines/reference/classBlockScoping.js
Normal file
71
tests/baselines/reference/classBlockScoping.js
Normal file
|
@ -0,0 +1,71 @@
|
|||
//// [classBlockScoping.ts]
|
||||
function f(b: boolean) {
|
||||
let Foo: any;
|
||||
if (b) {
|
||||
Foo = class Foo {
|
||||
static y = new Foo();
|
||||
|
||||
static x() {
|
||||
new Foo();
|
||||
}
|
||||
|
||||
m() {
|
||||
new Foo();
|
||||
}
|
||||
};
|
||||
|
||||
new Foo();
|
||||
}
|
||||
else {
|
||||
class Foo {
|
||||
static y = new Foo();
|
||||
|
||||
static x() {
|
||||
new Foo();
|
||||
}
|
||||
|
||||
m() {
|
||||
new Foo();
|
||||
}
|
||||
}
|
||||
|
||||
new Foo();
|
||||
}
|
||||
}
|
||||
|
||||
//// [classBlockScoping.js]
|
||||
function f(b) {
|
||||
var Foo;
|
||||
if (b) {
|
||||
Foo = (_a = (function () {
|
||||
function Foo() {
|
||||
}
|
||||
Foo.x = function () {
|
||||
new Foo();
|
||||
};
|
||||
Foo.prototype.m = function () {
|
||||
new Foo();
|
||||
};
|
||||
return Foo;
|
||||
}()),
|
||||
_a.y = new _a(),
|
||||
_a);
|
||||
new Foo();
|
||||
}
|
||||
else {
|
||||
var Foo_1 = (function () {
|
||||
function Foo() {
|
||||
}
|
||||
Foo.x = function () {
|
||||
new Foo();
|
||||
};
|
||||
Foo.prototype.m = function () {
|
||||
new Foo();
|
||||
};
|
||||
return Foo;
|
||||
}());
|
||||
Foo_1.y = new Foo_1();
|
||||
new Foo_1();
|
||||
}
|
||||
var _a;
|
||||
}
|
64
tests/baselines/reference/classBlockScoping.symbols
Normal file
64
tests/baselines/reference/classBlockScoping.symbols
Normal file
|
@ -0,0 +1,64 @@
|
|||
=== tests/cases/compiler/classBlockScoping.ts ===
|
||||
function f(b: boolean) {
|
||||
>f : Symbol(f, Decl(classBlockScoping.ts, 0, 0))
|
||||
>b : Symbol(b, Decl(classBlockScoping.ts, 0, 11))
|
||||
|
||||
let Foo: any;
|
||||
>Foo : Symbol(Foo, Decl(classBlockScoping.ts, 1, 5))
|
||||
|
||||
if (b) {
|
||||
>b : Symbol(b, Decl(classBlockScoping.ts, 0, 11))
|
||||
|
||||
Foo = class Foo {
|
||||
>Foo : Symbol(Foo, Decl(classBlockScoping.ts, 1, 5))
|
||||
>Foo : Symbol(Foo, Decl(classBlockScoping.ts, 3, 9))
|
||||
|
||||
static y = new Foo();
|
||||
>y : Symbol(Foo.y, Decl(classBlockScoping.ts, 3, 21))
|
||||
>Foo : Symbol(Foo, Decl(classBlockScoping.ts, 3, 9))
|
||||
|
||||
static x() {
|
||||
>x : Symbol(Foo.x, Decl(classBlockScoping.ts, 4, 27))
|
||||
|
||||
new Foo();
|
||||
>Foo : Symbol(Foo, Decl(classBlockScoping.ts, 3, 9))
|
||||
}
|
||||
|
||||
m() {
|
||||
>m : Symbol(Foo.m, Decl(classBlockScoping.ts, 8, 7))
|
||||
|
||||
new Foo();
|
||||
>Foo : Symbol(Foo, Decl(classBlockScoping.ts, 3, 9))
|
||||
}
|
||||
};
|
||||
|
||||
new Foo();
|
||||
>Foo : Symbol(Foo, Decl(classBlockScoping.ts, 1, 5))
|
||||
}
|
||||
else {
|
||||
class Foo {
|
||||
>Foo : Symbol(Foo, Decl(classBlockScoping.ts, 17, 8))
|
||||
|
||||
static y = new Foo();
|
||||
>y : Symbol(Foo.y, Decl(classBlockScoping.ts, 18, 15))
|
||||
>Foo : Symbol(Foo, Decl(classBlockScoping.ts, 17, 8))
|
||||
|
||||
static x() {
|
||||
>x : Symbol(Foo.x, Decl(classBlockScoping.ts, 19, 27))
|
||||
|
||||
new Foo();
|
||||
>Foo : Symbol(Foo, Decl(classBlockScoping.ts, 17, 8))
|
||||
}
|
||||
|
||||
m() {
|
||||
>m : Symbol(Foo.m, Decl(classBlockScoping.ts, 23, 7))
|
||||
|
||||
new Foo();
|
||||
>Foo : Symbol(Foo, Decl(classBlockScoping.ts, 17, 8))
|
||||
}
|
||||
}
|
||||
|
||||
new Foo();
|
||||
>Foo : Symbol(Foo, Decl(classBlockScoping.ts, 17, 8))
|
||||
}
|
||||
}
|
74
tests/baselines/reference/classBlockScoping.types
Normal file
74
tests/baselines/reference/classBlockScoping.types
Normal file
|
@ -0,0 +1,74 @@
|
|||
=== tests/cases/compiler/classBlockScoping.ts ===
|
||||
function f(b: boolean) {
|
||||
>f : (b: boolean) => void
|
||||
>b : boolean
|
||||
|
||||
let Foo: any;
|
||||
>Foo : any
|
||||
|
||||
if (b) {
|
||||
>b : boolean
|
||||
|
||||
Foo = class Foo {
|
||||
>Foo = class Foo { static y = new Foo(); static x() { new Foo(); } m() { new Foo(); } } : typeof Foo
|
||||
>Foo : any
|
||||
>class Foo { static y = new Foo(); static x() { new Foo(); } m() { new Foo(); } } : typeof Foo
|
||||
>Foo : typeof Foo
|
||||
|
||||
static y = new Foo();
|
||||
>y : Foo
|
||||
>new Foo() : Foo
|
||||
>Foo : typeof Foo
|
||||
|
||||
static x() {
|
||||
>x : () => void
|
||||
|
||||
new Foo();
|
||||
>new Foo() : Foo
|
||||
>Foo : typeof Foo
|
||||
}
|
||||
|
||||
m() {
|
||||
>m : () => void
|
||||
|
||||
new Foo();
|
||||
>new Foo() : Foo
|
||||
>Foo : typeof Foo
|
||||
}
|
||||
};
|
||||
|
||||
new Foo();
|
||||
>new Foo() : any
|
||||
>Foo : any
|
||||
}
|
||||
else {
|
||||
class Foo {
|
||||
>Foo : Foo
|
||||
|
||||
static y = new Foo();
|
||||
>y : Foo
|
||||
>new Foo() : Foo
|
||||
>Foo : typeof Foo
|
||||
|
||||
static x() {
|
||||
>x : () => void
|
||||
|
||||
new Foo();
|
||||
>new Foo() : Foo
|
||||
>Foo : typeof Foo
|
||||
}
|
||||
|
||||
m() {
|
||||
>m : () => void
|
||||
|
||||
new Foo();
|
||||
>new Foo() : Foo
|
||||
>Foo : typeof Foo
|
||||
}
|
||||
}
|
||||
|
||||
new Foo();
|
||||
>new Foo() : Foo
|
||||
>Foo : typeof Foo
|
||||
}
|
||||
}
|
|
@ -15,8 +15,8 @@ var C = (function () {
|
|||
}());
|
||||
{
|
||||
var C_1 = (function () {
|
||||
function C_1() {
|
||||
function C() {
|
||||
}
|
||||
return C_1;
|
||||
return C;
|
||||
}());
|
||||
}
|
||||
|
|
|
@ -19,9 +19,9 @@ function f() {
|
|||
var c1 = C;
|
||||
{
|
||||
var C_1 = (function () {
|
||||
function C_1() {
|
||||
function C() {
|
||||
}
|
||||
return C_1;
|
||||
return C;
|
||||
}());
|
||||
var c2 = C_1;
|
||||
}
|
||||
|
|
|
@ -206,9 +206,9 @@ function f3(b) {
|
|||
}
|
||||
else {
|
||||
var A_1 = (function () {
|
||||
function A_1() {
|
||||
function A() {
|
||||
}
|
||||
return A_1;
|
||||
return A;
|
||||
}());
|
||||
var c = [new A_1()];
|
||||
c[0].x = E.B;
|
||||
|
|
33
tests/cases/compiler/classBlockScoping.ts
Normal file
33
tests/cases/compiler/classBlockScoping.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
function f(b: boolean) {
|
||||
let Foo: any;
|
||||
if (b) {
|
||||
Foo = class Foo {
|
||||
static y = new Foo();
|
||||
|
||||
static x() {
|
||||
new Foo();
|
||||
}
|
||||
|
||||
m() {
|
||||
new Foo();
|
||||
}
|
||||
};
|
||||
|
||||
new Foo();
|
||||
}
|
||||
else {
|
||||
class Foo {
|
||||
static y = new Foo();
|
||||
|
||||
static x() {
|
||||
new Foo();
|
||||
}
|
||||
|
||||
m() {
|
||||
new Foo();
|
||||
}
|
||||
}
|
||||
|
||||
new Foo();
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue