From 4968b81a76bb9490a7573b484e6c91582177edf3 Mon Sep 17 00:00:00 2001 From: kingwl Date: Fri, 9 Oct 2020 01:24:36 +0800 Subject: [PATCH] Add parser types emitter and pass the build --- src/compiler/binder.ts | 2 + src/compiler/checker.ts | 49 +++++++++++++------- src/compiler/emitter.ts | 7 +++ src/compiler/factory/nodeFactory.ts | 26 ++++++++++- src/compiler/factory/nodeTests.ts | 4 ++ src/compiler/parser.ts | 17 +++++-- src/compiler/transformers/declarations.ts | 10 ++-- src/compiler/types.ts | 20 ++++++-- src/compiler/visitorPublic.ts | 5 ++ src/services/navigationBar.ts | 2 +- tests/cases/conformance/enums/spreadEnum1.ts | 29 ++++++++++++ 11 files changed, 142 insertions(+), 29 deletions(-) create mode 100644 tests/cases/conformance/enums/spreadEnum1.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 5e7695ffef366..2abf750aaf681 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2560,6 +2560,8 @@ namespace ts { return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property, SymbolFlags.PropertyExcludes); case SyntaxKind.EnumMember: return bindPropertyOrMethodOrAccessor(node, SymbolFlags.EnumMember, SymbolFlags.EnumMemberExcludes); + case SyntaxKind.SpreadEnumMember: + return undefined; case SyntaxKind.CallSignature: case SyntaxKind.ConstructSignature: diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 159ca30a18898..40bfa37326c45 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9432,7 +9432,7 @@ namespace ts { for (const declaration of symbol.declarations) { if (declaration.kind === SyntaxKind.EnumDeclaration) { for (const member of (declaration).members) { - if (member.initializer && isStringLiteralLike(member.initializer)) { + if (isSpreadEnumMember(member) || member.initializer && isStringLiteralLike(member.initializer)) { return links.enumKind = EnumKind.Literal; } if (!isLiteralEnumMember(member)) { @@ -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 (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 (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 ? (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) { @@ -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; + } } } } @@ -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); } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 237d3fee9cdc5..54f9e89d0645e 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1534,6 +1534,8 @@ namespace ts { // Enum case SyntaxKind.EnumMember: return emitEnumMember(node); + case SyntaxKind.SpreadEnumMember: + return emitSpreadEnumMember(node); // JSDoc nodes (only used in codefixes currently) case SyntaxKind.JSDocParameterTag: @@ -3501,6 +3503,11 @@ namespace ts { emitInitializer(node.initializer, node.name.end, node); } + function emitSpreadEnumMember(node: SpreadEnumMember) { + emit(node.dotDotDotToken); + emitEntityName(node.name); + } + // // JSDoc // diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 3d1b6fdb9a7d3..586150230ef09 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -410,6 +410,8 @@ namespace ts { updateSpreadAssignment, createEnumMember, updateEnumMember, + createSpreadEnumMember, + updateSpreadEnumMember, createSourceFile, updateSourceFile, createBundle, @@ -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( SyntaxKind.EnumDeclaration, @@ -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 @@ -4848,6 +4850,26 @@ namespace ts { : node; } + // @api + function createSpreadEnumMember(dotDotDotToken: DotDotDotToken, name: EntityName) { + const node = createBaseNode(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 // diff --git a/src/compiler/factory/nodeTests.ts b/src/compiler/factory/nodeTests.ts index 2603094717507..87f9a147f1ed6 100644 --- a/src/compiler/factory/nodeTests.ts +++ b/src/compiler/factory/nodeTests.ts @@ -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 diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index a815a12db9a53..55091e693ef6c 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -380,6 +380,9 @@ namespace ts { case SyntaxKind.EnumMember: return visitNode(cbNode, (node).name) || visitNode(cbNode, (node).initializer); + case SyntaxKind.SpreadEnumMember: + return visitNode(cbNode, (node).dotDotDotToken) || + visitNode(cbNode, (node).name); case SyntaxKind.ModuleDeclaration: return visitNodes(cbNode, cbNodes, node.decorators) || visitNodes(cbNode, cbNodes, node.modifiers) || @@ -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: @@ -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); @@ -6763,13 +6772,13 @@ namespace ts { function parseEnumDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray | undefined, modifiers: NodeArray | undefined): EnumDeclaration { parseExpected(SyntaxKind.EnumKeyword); const name = parseIdentifier(); - let members; + let members: NodeArray | undefined; if (parseExpected(SyntaxKind.OpenBraceToken)) { members = doOutsideOfYieldAndAwaitContext(() => parseDelimitedList(ParsingContext.EnumMembers, parseEnumMember)); parseExpected(SyntaxKind.CloseBraceToken); } else { - members = createMissingList(); + members = createMissingList(); } const node = factory.createEnumDeclaration(decorators, modifiers, name, members); return withJSDoc(finishNode(node, pos), hasJSDoc); diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index 43539cb06ad71..5f07744b709b9 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -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; })))); } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 2c37d20298dca..e5403aa6876df 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -345,6 +345,7 @@ namespace ts { // Enum EnumMember, + SpreadEnumMember, // Unparsed UnparsedPrologue, UnparsedPrepend, @@ -883,6 +884,7 @@ namespace ts { | InterfaceDeclaration | TypeAliasDeclaration | EnumMember + | SpreadEnumMember | EnumDeclaration | ModuleDeclaration | ImportEqualsDeclaration @@ -1355,6 +1357,7 @@ namespace ts { | JsxAttribute | ShorthandPropertyAssignment | EnumMember + | SpreadEnumMember | JSDocPropertyTag | JSDocParameterTag; @@ -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; + readonly members: NodeArray; } export type ModuleName = @@ -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; @@ -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 diff --git a/src/compiler/visitorPublic.ts b/src/compiler/visitorPublic.ts index 8a06e110552dc..4a80db985e6cd 100644 --- a/src/compiler/visitorPublic.ts +++ b/src/compiler/visitorPublic.ts @@ -1083,6 +1083,11 @@ namespace ts { return factory.updateEnumMember(node, nodeVisitor((node).name, visitor, isPropertyName), nodeVisitor((node).initializer, visitor, isExpression)); + case SyntaxKind.SpreadEnumMember: + return factory.updateSpreadEnumMember(node, + nodeVisitor((node).dotDotDotToken, tokenVisitor, isToken), + nodeVisitor((node).name, visitor, isEntityName) + ); // Top-level nodes case SyntaxKind.SourceFile: diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index 4704e86846b90..9b32be1be7436 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -287,7 +287,7 @@ namespace ts.NavigationBar { case SyntaxKind.EnumDeclaration: startNode(node); for (const member of (node).members) { - if (!isComputedProperty(member)) { + if (isEnumMember(member) && !isComputedProperty(member)) { addLeafNode(member); } } diff --git a/tests/cases/conformance/enums/spreadEnum1.ts b/tests/cases/conformance/enums/spreadEnum1.ts new file mode 100644 index 0000000000000..4760e292c51cf --- /dev/null +++ b/tests/cases/conformance/enums/spreadEnum1.ts @@ -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; \ No newline at end of file