Permit abstract modifier on class expressions

This commit is contained in:
Anders Hejlsberg
2020-11-23 08:35:28 -10:00
parent 4a39972490
commit a2c1adf60d
3 changed files with 21 additions and 26 deletions

View File

@@ -38980,7 +38980,6 @@ namespace ts {
if (flags & ModifierFlags.Abstract) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "abstract");
}
// An abstract modifier is permitted on a class expression in a 'typeof abstract class {}' type
if (node.kind !== SyntaxKind.ClassDeclaration && node.kind !== SyntaxKind.ClassExpression) {
if (node.kind !== SyntaxKind.MethodDeclaration &&
node.kind !== SyntaxKind.PropertyDeclaration &&
@@ -38988,7 +38987,7 @@ namespace ts {
node.kind !== SyntaxKind.SetAccessor) {
return grammarErrorOnNode(modifier, Diagnostics.abstract_modifier_can_only_appear_on_a_class_method_or_property_declaration);
}
if (!(node.parent.kind === SyntaxKind.ClassDeclaration && hasSyntacticModifier(node.parent, ModifierFlags.Abstract))) {
if (!((node.parent.kind === SyntaxKind.ClassDeclaration || node.parent.kind === SyntaxKind.ClassExpression) && hasSyntacticModifier(node.parent, ModifierFlags.Abstract))) {
return grammarErrorOnNode(modifier, Diagnostics.Abstract_methods_can_only_appear_within_an_abstract_class);
}
if (flags & ModifierFlags.Static) {
@@ -39096,7 +39095,6 @@ namespace ts {
return nodeHasAnyModifiersExcept(node, SyntaxKind.AsyncKeyword);
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
// An abstract modifier is permitted on a class expression in a 'typeof abstract class {}' type
return nodeHasAnyModifiersExcept(node, SyntaxKind.AbstractKeyword);
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.VariableStatement:

View File

@@ -2861,22 +2861,14 @@ namespace ts {
}
function isStartOfTypeofClassExpression() {
return token() === SyntaxKind.ClassKeyword ||
token() === SyntaxKind.AbstractKeyword && lookAhead(() => nextToken() === SyntaxKind.ClassKeyword && !scanner.hasPrecedingLineBreak());
}
function parseTypeofClassExpression(): ClassExpression {
const pos = getNodePos();
const hasJSDoc = hasPrecedingJSDocComment();
const modifiers = parseModifiers();
return <ClassExpression>parseClassDeclarationOrExpression(pos, hasJSDoc, /*decorators*/ undefined, modifiers, SyntaxKind.ClassExpression);
return token() === SyntaxKind.ClassKeyword || token() === SyntaxKind.AbstractKeyword && lookAhead(nextTokenIsClassKeywordOnSameLine);
}
function parseTypeQuery(): TypeQueryNode {
const pos = getNodePos();
parseExpected(SyntaxKind.TypeOfKeyword);
return finishNode(factory.createTypeQueryNode(isStartOfTypeofClassExpression() ?
doInsideOfContext(NodeFlags.Ambient, parseTypeofClassExpression) :
doInsideOfContext(NodeFlags.Ambient, parseClassExpression) :
parseEntityName(/*allowReservedWords*/ true)), pos);
}
@@ -5323,6 +5315,11 @@ namespace ts {
}
return parseFunctionExpression();
case SyntaxKind.AbstractKeyword:
if (!lookAhead(nextTokenIsClassKeywordOnSameLine)) {
break;
}
// Fall through
case SyntaxKind.ClassKeyword:
return parseClassExpression();
case SyntaxKind.FunctionKeyword:
@@ -6654,7 +6651,10 @@ namespace ts {
}
function parseClassExpression(): ClassExpression {
return <ClassExpression>parseClassDeclarationOrExpression(getNodePos(), hasPrecedingJSDocComment(), /*decorators*/ undefined, /*modifiers*/ undefined, SyntaxKind.ClassExpression);
const pos = getNodePos();
const hasJSDoc = hasPrecedingJSDocComment();
const modifiers = parseModifiers();
return <ClassExpression>parseClassDeclarationOrExpression(pos, hasJSDoc, /*decorators*/ undefined, modifiers, SyntaxKind.ClassExpression);
}
function parseClassDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray<Decorator> | undefined, modifiers: NodeArray<Modifier> | undefined): ClassDeclaration {

View File

@@ -204,7 +204,7 @@ namespace ts {
function getNodesToSearchForModifier(declaration: Node, modifierFlag: ModifierFlags): readonly Node[] | undefined {
// Types of node whose children might have modifiers.
const container = declaration.parent as ModuleBlock | SourceFile | Block | CaseClause | DefaultClause | ConstructorDeclaration | MethodDeclaration | FunctionDeclaration | ObjectTypeDeclaration | ObjectLiteralExpression;
const container = declaration.parent;
switch (container.kind) {
case SyntaxKind.ModuleBlock:
case SyntaxKind.SourceFile:
@@ -216,22 +216,21 @@ namespace ts {
return [...declaration.members, declaration];
}
else {
return container.statements;
return (<ModuleBlock | SourceFile | Block | CaseClause | DefaultClause>container).statements;
}
case SyntaxKind.Constructor:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.FunctionDeclaration:
return [...container.parameters, ...(isClassLike(container.parent) ? container.parent.members : [])];
return [...(<ConstructorDeclaration | MethodDeclaration | FunctionDeclaration>container).parameters, ...(isClassLike(container.parent) ? container.parent.members : [])];
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.TypeLiteral:
const nodes = container.members;
const nodes = (<ClassDeclaration | ClassExpression | InterfaceDeclaration | TypeLiteralNode>container).members;
// If we're an accessibility modifier, we're in an instance member and should search
// the constructor's parameter list for instance members as well.
if (modifierFlag & (ModifierFlags.AccessibilityModifier | ModifierFlags.Readonly)) {
const constructor = find(container.members, isConstructorDeclaration);
const constructor = find(nodes, isConstructorDeclaration);
if (constructor) {
return [...nodes, ...constructor.parameters];
}
@@ -240,13 +239,11 @@ namespace ts {
return [...nodes, container];
}
return nodes;
// Syntactically invalid positions that the parser might produce anyway
case SyntaxKind.ObjectLiteralExpression:
return undefined;
default:
Debug.assertNever(container, "Invalid container kind.");
if (modifierFlag & ModifierFlags.Abstract && isClassExpression(declaration)) {
return [...declaration.members, declaration];
}
return undefined;
}
}