Skip to content

Commit

Permalink
Add parser types emitter and pass the build
Browse files Browse the repository at this point in the history
  • Loading branch information
Kingwl committed Oct 8, 2020
1 parent 197ac80 commit 4968b81
Show file tree
Hide file tree
Showing 11 changed files with 142 additions and 29 deletions.
2 changes: 2 additions & 0 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2560,6 +2560,8 @@ namespace ts {
return bindPropertyOrMethodOrAccessor(<Declaration>node, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
case SyntaxKind.EnumMember:
return bindPropertyOrMethodOrAccessor(<Declaration>node, SymbolFlags.EnumMember, SymbolFlags.EnumMemberExcludes);
case SyntaxKind.SpreadEnumMember:
return undefined;

case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
Expand Down
49 changes: 33 additions & 16 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9432,7 +9432,7 @@ namespace ts {
for (const declaration of symbol.declarations) {
if (declaration.kind === SyntaxKind.EnumDeclaration) {
for (const member of (<EnumDeclaration>declaration).members) {
if (member.initializer && isStringLiteralLike(member.initializer)) {
if (isSpreadEnumMember(member) || member.initializer && isStringLiteralLike(member.initializer)) {
return links.enumKind = EnumKind.Literal;
}
if (!isLiteralEnumMember(member)) {
Expand All @@ -9448,24 +9448,39 @@ namespace ts {
return type.flags & TypeFlags.EnumLiteral && !(type.flags & TypeFlags.Union) ? getDeclaredTypeOfSymbol(getParentOfSymbol(type.symbol)!) : type;
}

function getDeclaredTypeOfEnum(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (links.declaredType) {
return links.declaredType;
}
if (getEnumKind(symbol) === EnumKind.Literal) {
enumCount++;
const memberTypeList: Type[] = [];
for (const declaration of symbol.declarations) {
if (declaration.kind === SyntaxKind.EnumDeclaration) {
for (const member of (<EnumDeclaration>declaration).members) {
function getEnumMemberTypeList(symbol: Symbol): Type[] {
const memberTypeList: Type[] = [];
for (const declaration of symbol.declarations) {
if (declaration.kind === SyntaxKind.EnumDeclaration) {
for (const member of (<EnumDeclaration>declaration).members) {
if (isEnumMember(member)) {
const value = getEnumMemberValue(member);
const memberType = getFreshTypeOfLiteralType(getLiteralType(value !== undefined ? value : 0, enumCount, getSymbolOfNode(member)));
getSymbolLinks(getSymbolOfNode(member)).declaredType = memberType;
memberTypeList.push(getRegularTypeOfLiteralType(memberType));
}
else {
const referencedEnumDeclaration = getSymbolOfNode(member.name);
if (referencedEnumDeclaration) {
const declaredType = getDeclaredTypeOfEnum(referencedEnumDeclaration);
const types = declaredType.flags & TypeFlags.Union ? (<UnionType>declaredType).types : [declaredType];
memberTypeList.push(...types);
}
}
}
}
}
return memberTypeList
}

function getDeclaredTypeOfEnum(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (links.declaredType) {
return links.declaredType;
}
if (getEnumKind(symbol) === EnumKind.Literal) {
enumCount++;
const memberTypeList = getEnumMemberTypeList(symbol);
if (memberTypeList.length) {
const enumType = getUnionType(memberTypeList, UnionReduction.Literal, symbol, /*aliasTypeArguments*/ undefined);
if (enumType.flags & TypeFlags.Union) {
Expand Down Expand Up @@ -35674,9 +35689,11 @@ namespace ts {
nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed;
let autoValue: number | undefined = 0;
for (const member of node.members) {
const value = computeMemberValue(member, autoValue);
getNodeLinks(member).enumMemberValue = value;
autoValue = typeof value === "number" ? value + 1 : undefined;
if (isEnumMember(member)) {
const value = computeMemberValue(member, autoValue);
getNodeLinks(member).enumMemberValue = value;
autoValue = typeof value === "number" ? value + 1 : undefined;
}
}
}
}
Expand Down Expand Up @@ -35888,7 +35905,7 @@ namespace ts {
}

const firstEnumMember = enumDeclaration.members[0];
if (!firstEnumMember.initializer) {
if (!isSpreadEnumMember(firstEnumMember) && !firstEnumMember.initializer) {
if (seenEnumMissingInitialInitializer) {
error(firstEnumMember.name, Diagnostics.In_an_enum_with_multiple_declarations_only_one_declaration_can_omit_an_initializer_for_its_first_enum_element);
}
Expand Down
7 changes: 7 additions & 0 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1534,6 +1534,8 @@ namespace ts {
// Enum
case SyntaxKind.EnumMember:
return emitEnumMember(<EnumMember>node);
case SyntaxKind.SpreadEnumMember:
return emitSpreadEnumMember(<SpreadEnumMember>node);

// JSDoc nodes (only used in codefixes currently)
case SyntaxKind.JSDocParameterTag:
Expand Down Expand Up @@ -3501,6 +3503,11 @@ namespace ts {
emitInitializer(node.initializer, node.name.end, node);
}

function emitSpreadEnumMember(node: SpreadEnumMember) {
emit(node.dotDotDotToken);
emitEntityName(node.name);
}

//
// JSDoc
//
Expand Down
26 changes: 24 additions & 2 deletions src/compiler/factory/nodeFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,8 @@ namespace ts {
updateSpreadAssignment,
createEnumMember,
updateEnumMember,
createSpreadEnumMember,
updateSpreadEnumMember,
createSourceFile,
updateSourceFile,
createBundle,
Expand Down Expand Up @@ -3624,7 +3626,7 @@ namespace ts {
decorators: readonly Decorator[] | undefined,
modifiers: readonly Modifier[] | undefined,
name: string | Identifier,
members: readonly EnumMember[]
members: readonly EnumMemberLike[]
) {
const node = createBaseNamedDeclaration<EnumDeclaration>(
SyntaxKind.EnumDeclaration,
Expand All @@ -3646,7 +3648,7 @@ namespace ts {
decorators: readonly Decorator[] | undefined,
modifiers: readonly Modifier[] | undefined,
name: Identifier,
members: readonly EnumMember[]) {
members: readonly EnumMemberLike[]) {
return node.decorators !== decorators
|| node.modifiers !== modifiers
|| node.name !== name
Expand Down Expand Up @@ -4848,6 +4850,26 @@ namespace ts {
: node;
}

// @api
function createSpreadEnumMember(dotDotDotToken: DotDotDotToken, name: EntityName) {
const node = createBaseNode<SpreadEnumMember>(SyntaxKind.SpreadEnumMember);
node.dotDotDotToken = dotDotDotToken;
node.name = name;
node.transformFlags |=
propagateChildFlags(node.dotDotDotToken) |
propagateChildFlags(node.name) |
TransformFlags.ContainsTypeScript;
return node;
}

// @api
function updateSpreadEnumMember(node: SpreadEnumMember, dotDotDotToken: DotDotDotToken, name: EntityName) {
return node.dotDotDotToken !== dotDotDotToken
|| node.name !== name
? update(createSpreadEnumMember(dotDotDotToken, name), node)
: node;
}

//
// Top-level nodes
//
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/factory/nodeTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,10 @@ namespace ts {
return node.kind === SyntaxKind.EnumMember;
}

export function isSpreadEnumMember(node: Node): node is SpreadEnumMember {
return node.kind === SyntaxKind.SpreadEnumMember;
}

// Unparsed

// TODO(rbuckton): isUnparsedPrologue
Expand Down
17 changes: 13 additions & 4 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,9 @@ namespace ts {
case SyntaxKind.EnumMember:
return visitNode(cbNode, (<EnumMember>node).name) ||
visitNode(cbNode, (<EnumMember>node).initializer);
case SyntaxKind.SpreadEnumMember:
return visitNode(cbNode, (<SpreadEnumMember>node).dotDotDotToken) ||
visitNode(cbNode, (<EnumMemberLike>node).name);
case SyntaxKind.ModuleDeclaration:
return visitNodes(cbNode, cbNodes, node.decorators) ||
visitNodes(cbNode, cbNodes, node.modifiers) ||
Expand Down Expand Up @@ -1839,7 +1842,7 @@ namespace ts {
case ParsingContext.EnumMembers:
// Include open bracket computed properties. This technically also lets in indexers,
// which would be a candidate for improved error reporting.
return token() === SyntaxKind.OpenBracketToken || isLiteralPropertyName();
return token() === SyntaxKind.OpenBracketToken || token() === SyntaxKind.DotDotDotToken || isLiteralPropertyName();
case ParsingContext.ObjectLiteralMembers:
switch (token()) {
case SyntaxKind.OpenBracketToken:
Expand Down Expand Up @@ -6752,9 +6755,15 @@ namespace ts {
// In a non-ambient declaration, the grammar allows uninitialized members only in a
// ConstantEnumMemberSection, which starts at the beginning of an enum declaration
// or any time an integer literal initializer is encountered.
function parseEnumMember(): EnumMember {
function parseEnumMember(): EnumMemberLike {
const pos = getNodePos();
const hasJSDoc = hasPrecedingJSDocComment();
const dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken);
if (dotDotDotToken) {
const name = parseEntityName(/*allowReservedWords*/ false);
return withJSDoc(finishNode(factory.createSpreadEnumMember(dotDotDotToken, name), pos), hasJSDoc);
}

const name = parsePropertyName();
const initializer = allowInAnd(parseInitializer);
return withJSDoc(finishNode(factory.createEnumMember(name, initializer), pos), hasJSDoc);
Expand All @@ -6763,13 +6772,13 @@ namespace ts {
function parseEnumDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): EnumDeclaration {
parseExpected(SyntaxKind.EnumKeyword);
const name = parseIdentifier();
let members;
let members: NodeArray<EnumMemberLike> | undefined;
if (parseExpected(SyntaxKind.OpenBraceToken)) {
members = doOutsideOfYieldAndAwaitContext(() => parseDelimitedList(ParsingContext.EnumMembers, parseEnumMember));
parseExpected(SyntaxKind.CloseBraceToken);
}
else {
members = createMissingList<EnumMember>();
members = createMissingList<EnumMemberLike>();
}
const node = factory.createEnumDeclaration(decorators, modifiers, name, members);
return withJSDoc(finishNode(node, pos), hasJSDoc);
Expand Down
10 changes: 7 additions & 3 deletions src/compiler/transformers/declarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1437,9 +1437,13 @@ namespace ts {
case SyntaxKind.EnumDeclaration: {
return cleanup(factory.updateEnumDeclaration(input, /*decorators*/ undefined, factory.createNodeArray(ensureModifiers(input)), input.name, factory.createNodeArray(mapDefined(input.members, m => {
if (shouldStripInternal(m)) return;
// Rewrite enum values to their constants, if available
const constValue = resolver.getConstantValue(m);
return preserveJsDoc(factory.updateEnumMember(m, m.name, constValue !== undefined ? typeof constValue === "string" ? factory.createStringLiteral(constValue) : factory.createNumericLiteral(constValue) : undefined), m);

if (isEnumMember(m)) {
// Rewrite enum values to their constants, if available
const constValue = resolver.getConstantValue(m);
return preserveJsDoc(factory.updateEnumMember(m, m.name, constValue !== undefined ? typeof constValue === "string" ? factory.createStringLiteral(constValue) : factory.createNumericLiteral(constValue) : undefined), m);
}
return m;
}))));
}
}
Expand Down
20 changes: 17 additions & 3 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ namespace ts {

// Enum
EnumMember,
SpreadEnumMember,
// Unparsed
UnparsedPrologue,
UnparsedPrepend,
Expand Down Expand Up @@ -883,6 +884,7 @@ namespace ts {
| InterfaceDeclaration
| TypeAliasDeclaration
| EnumMember
| SpreadEnumMember
| EnumDeclaration
| ModuleDeclaration
| ImportEqualsDeclaration
Expand Down Expand Up @@ -1355,6 +1357,7 @@ namespace ts {
| JsxAttribute
| ShorthandPropertyAssignment
| EnumMember
| SpreadEnumMember
| JSDocPropertyTag
| JSDocParameterTag;

Expand Down Expand Up @@ -2857,10 +2860,19 @@ namespace ts {
readonly initializer?: Expression;
}

export interface SpreadEnumMember extends Declaration, JSDocContainer {
readonly kind: SyntaxKind.SpreadEnumMember;
readonly parent: EnumDeclaration;
readonly dotDotDotToken: DotDotDotToken;
readonly name: EntityName;
}

export type EnumMemberLike = EnumMember | SpreadEnumMember;

export interface EnumDeclaration extends DeclarationStatement, JSDocContainer {
readonly kind: SyntaxKind.EnumDeclaration;
readonly name: Identifier;
readonly members: NodeArray<EnumMember>;
readonly members: NodeArray<EnumMemberLike>;
}

export type ModuleName =
Expand Down Expand Up @@ -6977,8 +6989,8 @@ namespace ts {
updateInterfaceDeclaration(node: InterfaceDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, name: Identifier, typeParameters: readonly TypeParameterDeclaration[] | undefined, heritageClauses: readonly HeritageClause[] | undefined, members: readonly TypeElement[]): InterfaceDeclaration;
createTypeAliasDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, name: string | Identifier, typeParameters: readonly TypeParameterDeclaration[] | undefined, type: TypeNode): TypeAliasDeclaration;
updateTypeAliasDeclaration(node: TypeAliasDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, name: Identifier, typeParameters: readonly TypeParameterDeclaration[] | undefined, type: TypeNode): TypeAliasDeclaration;
createEnumDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, name: string | Identifier, members: readonly EnumMember[]): EnumDeclaration;
updateEnumDeclaration(node: EnumDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, name: Identifier, members: readonly EnumMember[]): EnumDeclaration;
createEnumDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, name: string | Identifier, members: readonly EnumMemberLike[]): EnumDeclaration;
updateEnumDeclaration(node: EnumDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, name: Identifier, members: readonly EnumMemberLike[]): EnumDeclaration;
createModuleDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, name: ModuleName, body: ModuleBody | undefined, flags?: NodeFlags): ModuleDeclaration;
updateModuleDeclaration(node: ModuleDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, name: ModuleName, body: ModuleBody | undefined): ModuleDeclaration;
createModuleBlock(statements: readonly Statement[]): ModuleBlock;
Expand Down Expand Up @@ -7144,6 +7156,8 @@ namespace ts {

createEnumMember(name: string | PropertyName, initializer?: Expression): EnumMember;
updateEnumMember(node: EnumMember, name: PropertyName, initializer: Expression | undefined): EnumMember;
createSpreadEnumMember(dotDotDotToken: DotDotDotToken, name: EntityName): SpreadEnumMember;
updateSpreadEnumMember(node: SpreadEnumMember, dotDotDotToken: DotDotDotToken, name: EntityName): SpreadEnumMember;

//
// Top-level nodes
Expand Down
5 changes: 5 additions & 0 deletions src/compiler/visitorPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,11 @@ namespace ts {
return factory.updateEnumMember(<EnumMember>node,
nodeVisitor((<EnumMember>node).name, visitor, isPropertyName),
nodeVisitor((<EnumMember>node).initializer, visitor, isExpression));
case SyntaxKind.SpreadEnumMember:
return factory.updateSpreadEnumMember(<SpreadEnumMember>node,
nodeVisitor((<SpreadEnumMember>node).dotDotDotToken, tokenVisitor, isToken),
nodeVisitor((<SpreadEnumMember>node).name, visitor, isEntityName)
);

// Top-level nodes
case SyntaxKind.SourceFile:
Expand Down
2 changes: 1 addition & 1 deletion src/services/navigationBar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ namespace ts.NavigationBar {
case SyntaxKind.EnumDeclaration:
startNode(node);
for (const member of (<EnumDeclaration>node).members) {
if (!isComputedProperty(member)) {
if (isEnumMember(member) && !isComputedProperty(member)) {
addLeafNode(member);
}
}
Expand Down
29 changes: 29 additions & 0 deletions tests/cases/conformance/enums/spreadEnum1.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// @declaration: true

enum BasicEvents {
Start = "Start",
Finish = "Finish"
}

enum AdvEvents {
...BasicEvents,
Pause = "Pause",
Resume = "Resume"
}

declare let basic: BasicEvents;

declare let adv: AdvEvents;

adv = basic;

basic = BasicEvents.Start;
basic = BasicEvents.Finish;

adv = AdvEvents.Start;
adv = AdvEvents.Finish;
adv = AdvEvents.Pause;
adv = AdvEvents.Resume;

adv = BasicEvents.Start;
adv = BasicEvents.Finish;

0 comments on commit 4968b81

Please sign in to comment.