emit unqualified enum members as qualified

This commit is contained in:
Vladimir Matveev 2016-04-06 23:49:14 -07:00
parent 1317d23a8a
commit 0e0182c1ea
4 changed files with 85 additions and 27 deletions

View file

@ -13,6 +13,8 @@ namespace ts {
NamespaceExports = 1 << 1,
/** Enables substitutions for async methods with `super` calls. */
AsyncMethodsWithSuper = 1 << 2,
/* Enables substitutions for unqualified enum members */
NonQualifiedEnumMembers = 1 << 3
}
export function transformTypeScript(context: TransformationContext) {
@ -65,7 +67,7 @@ namespace ts {
* Keeps track of whether we are within any containing namespaces when performing
* just-in-time substitution while printing an expression identifier.
*/
let isEnclosedInNamespace: boolean;
let applicableSubstitutions: TypeScriptSubstitutionFlags;
/**
* This keeps track of containers where `super` is valid, for use with
@ -2234,26 +2236,29 @@ namespace ts {
// ...
// })(x || (x = {}));
statements.push(
setOriginalNode(
createStatement(
createCall(
createFunctionExpression(
setNodeEmitFlags(
setOriginalNode(
createStatement(
createCall(
createFunctionExpression(
/*asteriskToken*/ undefined,
/*name*/ undefined,
[createParameter(localName)],
transformEnumBody(node, localName)
),
[createLogicalOr(
name,
createAssignment(
[createParameter(localName)],
transformEnumBody(node, localName)
),
[createLogicalOr(
name,
createObjectLiteral()
)
)]
),
createAssignment(
name,
createObjectLiteral()
)
)]
),
/*location*/ node
),
),
/*original*/ node
),
NodeEmitFlags.AdviseOnEmitNode
)
);
@ -2317,11 +2322,14 @@ namespace ts {
if (value !== undefined) {
return createLiteral(value);
}
else if (member.initializer) {
return visitNode(member.initializer, visitor, isExpression);
}
else {
return createVoidZero();
enableSubstitutionForNonQualifiedEnumMembers();
if (member.initializer) {
return visitNode(member.initializer, visitor, isExpression);
}
else {
return createVoidZero();
}
}
}
@ -2634,8 +2642,12 @@ namespace ts {
return getOriginalNode(node).kind === SyntaxKind.ModuleDeclaration;
}
function isTransformedEnumDeclaration(node: Node): boolean {
return getOriginalNode(node).kind === SyntaxKind.EnumDeclaration;
}
function onEmitNode(node: Node, emit: (node: Node) => void): void {
const savedIsEnclosedInNamespace = isEnclosedInNamespace;
const savedApplicableSubstitutions = applicableSubstitutions;
const savedCurrentSuperContainer = currentSuperContainer;
// If we need support substitutions for aliases for decorated classes,
@ -2651,7 +2663,10 @@ namespace ts {
}
if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && isTransformedModuleDeclaration(node)) {
isEnclosedInNamespace = true;
applicableSubstitutions |= TypeScriptSubstitutionFlags.NamespaceExports;
}
if (enabledSubstitutions & TypeScriptSubstitutionFlags.NonQualifiedEnumMembers && isTransformedEnumDeclaration(node)) {
applicableSubstitutions |= TypeScriptSubstitutionFlags.NonQualifiedEnumMembers;
}
previousOnEmitNode(node, emit);
@ -2660,7 +2675,7 @@ namespace ts {
currentDecoratedClassAliases[getOriginalNodeId(node)] = undefined;
}
isEnclosedInNamespace = savedIsEnclosedInNamespace;
applicableSubstitutions = savedApplicableSubstitutions;
currentSuperContainer = savedCurrentSuperContainer;
}
@ -2705,18 +2720,22 @@ namespace ts {
}
}
if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && isEnclosedInNamespace) {
if (enabledSubstitutions & applicableSubstitutions) {
// If we are nested within a namespace declaration, we may need to qualifiy
// an identifier that is exported from a merged namespace.
const original = getOriginalNode(node);
if (isIdentifier(original) && original.parent) {
const container = resolver.getReferencedExportContainer(original);
if (container && container.kind === SyntaxKind.ModuleDeclaration) {
return createPropertyAccess(getGeneratedNameForNode(container), node, /*location*/ node);
if (container) {
const substitute =
(applicableSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && container.kind === SyntaxKind.ModuleDeclaration) ||
(applicableSubstitutions & TypeScriptSubstitutionFlags.NonQualifiedEnumMembers && container.kind === SyntaxKind.EnumDeclaration);
if (substitute) {
return createPropertyAccess(getGeneratedNameForNode(container), node, /*location*/ node);
}
}
}
}
return node;
}
@ -2770,6 +2789,13 @@ namespace ts {
return node;
}
function enableSubstitutionForNonQualifiedEnumMembers() {
if ((enabledSubstitutions & TypeScriptSubstitutionFlags.NonQualifiedEnumMembers) === 0) {
enabledSubstitutions |= TypeScriptSubstitutionFlags.NonQualifiedEnumMembers;
context.enableExpressionSubstitution(SyntaxKind.Identifier);
}
}
function enableExpressionSubstitutionForAsyncMethodsWithSuper() {
if ((enabledSubstitutions & TypeScriptSubstitutionFlags.AsyncMethodsWithSuper) === 0) {
enabledSubstitutions |= TypeScriptSubstitutionFlags.AsyncMethodsWithSuper;

View file

@ -0,0 +1,12 @@
tests/cases/compiler/enumWithComputedMember.ts(4,5): error TS1061: Enum member must have initializer.
==== tests/cases/compiler/enumWithComputedMember.ts (1 errors) ====
enum A {
X = "".length,
Y = X,
Z
~
!!! error TS1061: Enum member must have initializer.
}

View file

@ -0,0 +1,15 @@
//// [enumWithComputedMember.ts]
enum A {
X = "".length,
Y = X,
Z
}
//// [enumWithComputedMember.js]
var A;
(function (A) {
A[A["X"] = "".length] = "X";
A[A["Y"] = A.X] = "Y";
A[A["Z"] = void 0] = "Z";
})(A || (A = {}));

View file

@ -0,0 +1,5 @@
enum A {
X = "".length,
Y = X,
Z
}