diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 756a1b85597..98bc128f896 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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: diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index d1d54d76a5c..de16c7c11cf 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -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 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 parseClassDeclarationOrExpression(getNodePos(), hasPrecedingJSDocComment(), /*decorators*/ undefined, /*modifiers*/ undefined, SyntaxKind.ClassExpression); + const pos = getNodePos(); + const hasJSDoc = hasPrecedingJSDocComment(); + const modifiers = parseModifiers(); + return parseClassDeclarationOrExpression(pos, hasJSDoc, /*decorators*/ undefined, modifiers, SyntaxKind.ClassExpression); } function parseClassDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray | undefined, modifiers: NodeArray | undefined): ClassDeclaration { diff --git a/src/services/documentHighlights.ts b/src/services/documentHighlights.ts index b0282393cce..04c56085c31 100644 --- a/src/services/documentHighlights.ts +++ b/src/services/documentHighlights.ts @@ -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 (container).statements; } case SyntaxKind.Constructor: case SyntaxKind.MethodDeclaration: case SyntaxKind.FunctionDeclaration: - return [...container.parameters, ...(isClassLike(container.parent) ? container.parent.members : [])]; + return [...(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 = (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; } }