Skip to content

Commit

Permalink
feat(17227): add abstract JSDoc tag
Browse files Browse the repository at this point in the history
  • Loading branch information
a-tarasyuk committed Jan 3, 2021
1 parent dfe2342 commit 9dfbcfd
Show file tree
Hide file tree
Showing 29 changed files with 610 additions and 60 deletions.
24 changes: 18 additions & 6 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6800,6 +6800,7 @@ namespace ts {
...!length(baseTypes) ? [] : [factory.createHeritageClause(SyntaxKind.ExtendsKeyword, map(baseTypes, b => serializeBaseType(b, staticBaseType, localName)))],
...!length(implementsExpressions) ? [] : [factory.createHeritageClause(SyntaxKind.ImplementsKeyword, implementsExpressions)]
];
const modifiers = originalDecl && hasEffectiveModifier(originalDecl, ModifierFlags.Abstract) ? factory.createModifiersFromModifierFlags(ModifierFlags.Abstract) : undefined;
const symbolProps = getNonInterhitedProperties(classType, baseTypes, getPropertiesOfType(classType));
const publicSymbolProps = filter(symbolProps, s => {
// `valueDeclaration` could be undefined if inherited from
Expand Down Expand Up @@ -6846,7 +6847,7 @@ namespace ts {
context.enclosingDeclaration = oldEnclosing;
addResult(setTextRange(factory.createClassDeclaration(
/*decorators*/ undefined,
/*modifiers*/ undefined,
modifiers,
localName,
typeParamDecls,
heritageClauses,
Expand Down Expand Up @@ -28036,7 +28037,7 @@ namespace ts {
// In the case of a merged class-module or class-interface declaration,
// only the class declaration node will have the Abstract flag set.
const valueDecl = expressionType.symbol && getClassLikeDeclarationOfSymbol(expressionType.symbol);
if (valueDecl && hasSyntacticModifier(valueDecl, ModifierFlags.Abstract)) {
if (valueDecl && hasEffectiveModifier(valueDecl, ModifierFlags.Abstract)) {
error(node, Diagnostics.Cannot_create_an_instance_of_an_abstract_class);
return resolveErrorCall(node);
}
Expand Down Expand Up @@ -29531,7 +29532,7 @@ namespace ts {
if (type && type.flags & TypeFlags.Never) {
error(getEffectiveReturnTypeNode(func), Diagnostics.A_function_returning_never_cannot_have_a_reachable_end_point);
}
else if (type && !hasExplicitReturn) {
else if (type && !hasExplicitReturn && !hasEffectiveModifier(func, ModifierFlags.Abstract)) {
// minimal check: function has syntactic return type annotation and no explicit return statements in the body
// this function does not conform to the specification.
// NOTE: having returnType !== undefined is a precondition for entering this branch so func.type will always be present
Expand Down Expand Up @@ -31899,7 +31900,7 @@ namespace ts {

// Abstract methods cannot have an implementation.
// Extra checks are to avoid reporting multiple errors relating to the "abstractness" of the node.
if (hasSyntacticModifier(node, ModifierFlags.Abstract) && node.kind === SyntaxKind.MethodDeclaration && node.body) {
if (hasEffectiveModifier(node, ModifierFlags.Abstract) && node.kind === SyntaxKind.MethodDeclaration && node.body && !isInJSFile(node)) {
error(node, Diagnostics.Method_0_cannot_have_an_implementation_because_it_is_marked_abstract, declarationNameToString(node.name));
}
}
Expand Down Expand Up @@ -31996,7 +31997,7 @@ namespace ts {
checkSignatureDeclaration(node);
if (node.kind === SyntaxKind.GetAccessor) {
if (!(node.flags & NodeFlags.Ambient) && nodeIsPresent(node.body) && (node.flags & NodeFlags.HasImplicitReturn)) {
if (!(node.flags & NodeFlags.HasExplicitReturn)) {
if (!(node.flags & NodeFlags.HasExplicitReturn) && !(isInJSFile(node) && hasEffectiveModifier(node, ModifierFlags.Abstract))) {
error(node.name, Diagnostics.A_get_accessor_must_return_a_value);
}
}
Expand Down Expand Up @@ -33277,6 +33278,15 @@ namespace ts {
checkSignatureDeclaration(node);
}

function checkJSDocAbstractTag(node: JSDocAbstractTag): void {
const host = getEffectiveJSDocHost(node);
if (host && isClassElement(host)) {
if (!hasEffectiveModifier(host.parent, ModifierFlags.Abstract)) {
error(host, Diagnostics.Abstract_methods_can_only_appear_within_an_abstract_class);
}
}
}

function checkJSDocImplementsTag(node: JSDocImplementsTag): void {
const classLike = getEffectiveJSDocHost(node);
if (!classLike || !isClassDeclaration(classLike) && !isClassExpression(classLike)) {
Expand Down Expand Up @@ -36969,6 +36979,8 @@ namespace ts {
return checkImportType(<ImportTypeNode>node);
case SyntaxKind.NamedTupleMember:
return checkNamedTupleMember(<NamedTupleMember>node);
case SyntaxKind.JSDocAbstractTag:
return checkJSDocAbstractTag(<JSDocAbstractTag>node);
case SyntaxKind.JSDocAugmentsTag:
return checkJSDocAugmentsTag(node as JSDocAugmentsTag);
case SyntaxKind.JSDocImplementsTag:
Expand Down Expand Up @@ -39851,7 +39863,7 @@ namespace ts {
return grammarErrorAtPos(accessor, accessor.end - 1, ";".length, Diagnostics._0_expected, "{");
}
}
if (accessor.body && hasSyntacticModifier(accessor, ModifierFlags.Abstract)) {
if (accessor.body && hasEffectiveModifier(accessor, ModifierFlags.Abstract) && !isInJSFile(accessor)) {
return grammarErrorOnNode(accessor, Diagnostics.An_abstract_accessor_cannot_have_an_implementation);
}
if (accessor.typeParameters) {
Expand Down
3 changes: 3 additions & 0 deletions src/compiler/factory/nodeFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,8 @@ namespace ts {
get updateJSDocThisTag() { return getJSDocTypeLikeTagUpdateFunction<JSDocThisTag>(SyntaxKind.JSDocThisTag); },
get createJSDocEnumTag() { return getJSDocTypeLikeTagCreateFunction<JSDocEnumTag>(SyntaxKind.JSDocEnumTag); },
get updateJSDocEnumTag() { return getJSDocTypeLikeTagUpdateFunction<JSDocEnumTag>(SyntaxKind.JSDocEnumTag); },
get createJSDocAbstractTag() { return getJSDocSimpleTagCreateFunction<JSDocAbstractTag>(SyntaxKind.JSDocAbstractTag); },
get updateJSDocAbstractTag() { return getJSDocSimpleTagUpdateFunction<JSDocAbstractTag>(SyntaxKind.JSDocAbstractTag); },
get createJSDocAuthorTag() { return getJSDocSimpleTagCreateFunction<JSDocAuthorTag>(SyntaxKind.JSDocAuthorTag); },
get updateJSDocAuthorTag() { return getJSDocSimpleTagUpdateFunction<JSDocAuthorTag>(SyntaxKind.JSDocAuthorTag); },
get createJSDocClassTag() { return getJSDocSimpleTagCreateFunction<JSDocClassTag>(SyntaxKind.JSDocClassTag); },
Expand Down Expand Up @@ -5859,6 +5861,7 @@ namespace ts {
case SyntaxKind.JSDocReturnTag: return "returns";
case SyntaxKind.JSDocThisTag: return "this";
case SyntaxKind.JSDocEnumTag: return "enum";
case SyntaxKind.JSDocAbstractTag: return "abstract";
case SyntaxKind.JSDocAuthorTag: return "author";
case SyntaxKind.JSDocClassTag: return "class";
case SyntaxKind.JSDocPublicTag: return "public";
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 @@ -757,6 +757,10 @@ namespace ts {
return node.kind === SyntaxKind.JSDocAugmentsTag;
}

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

export function isJSDocAuthorTag(node: Node): node is JSDocAuthorTag {
return node.kind === SyntaxKind.JSDocAuthorTag;
}
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,7 @@ namespace ts {
case SyntaxKind.JSDocTypeLiteral:
return forEach((node as JSDocTypeLiteral).jsDocPropertyTags, cbNode);
case SyntaxKind.JSDocTag:
case SyntaxKind.JSDocAbstractTag:
case SyntaxKind.JSDocClassTag:
case SyntaxKind.JSDocPublicTag:
case SyntaxKind.JSDocPrivateTag:
Expand Down Expand Up @@ -7443,6 +7444,9 @@ namespace ts {

let tag: JSDocTag | undefined;
switch (tagName.escapedText) {
case "abstract":
tag = parseSimpleTag(start, factory.createJSDocAbstractTag, tagName, margin, indentText);
break;
case "author":
tag = parseAuthorTag(start, tagName, margin, indentText);
break;
Expand Down
7 changes: 7 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ namespace ts {
JSDocTag,
JSDocAugmentsTag,
JSDocImplementsTag,
JSDocAbstractTag,
JSDocAuthorTag,
JSDocDeprecatedTag,
JSDocClassTag,
Expand Down Expand Up @@ -3166,6 +3167,10 @@ namespace ts {
readonly class: ExpressionWithTypeArguments & { readonly expression: Identifier | PropertyAccessEntityNameExpression };
}

export interface JSDocAbstractTag extends JSDocTag {
readonly kind: SyntaxKind.JSDocAbstractTag;
}

export interface JSDocAuthorTag extends JSDocTag {
readonly kind: SyntaxKind.JSDocAuthorTag;
}
Expand Down Expand Up @@ -7160,6 +7165,8 @@ namespace ts {
updateJSDocAugmentsTag(node: JSDocAugmentsTag, tagName: Identifier | undefined, className: JSDocAugmentsTag["class"], comment: string | undefined): JSDocAugmentsTag;
createJSDocImplementsTag(tagName: Identifier | undefined, className: JSDocImplementsTag["class"], comment?: string): JSDocImplementsTag;
updateJSDocImplementsTag(node: JSDocImplementsTag, tagName: Identifier | undefined, className: JSDocImplementsTag["class"], comment: string | undefined): JSDocImplementsTag;
createJSDocAbstractTag(tagName: Identifier | undefined, comment?: string): JSDocAbstractTag;
updateJSDocAbstractTag(node: JSDocAbstractTag, tagName: Identifier | undefined, comment: string | undefined): JSDocAbstractTag;
createJSDocAuthorTag(tagName: Identifier | undefined, comment?: string): JSDocAuthorTag;
updateJSDocAuthorTag(node: JSDocAuthorTag, tagName: Identifier | undefined, comment: string | undefined): JSDocAuthorTag;
createJSDocClassTag(tagName: Identifier | undefined, comment?: string): JSDocClassTag;
Expand Down
1 change: 1 addition & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4719,6 +4719,7 @@ namespace ts {
if (getJSDocPrivateTagNoCache(node)) flags |= ModifierFlags.Private;
if (getJSDocProtectedTagNoCache(node)) flags |= ModifierFlags.Protected;
if (getJSDocReadonlyTagNoCache(node)) flags |= ModifierFlags.Readonly;
if (getJSDocAbstractTagNoCache(node)) flags |= ModifierFlags.Abstract;
}
if (getJSDocDeprecatedTagNoCache(node)) flags |= ModifierFlags.Deprecated;
}
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/utilitiesPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,10 @@ namespace ts {
return getFirstJSDocTag(node, isJSDocReadonlyTag, /*noCache*/ true);
}

export function getJSDocAbstractTagNoCache(node: Node): JSDocAbstractTag | undefined {
return getFirstJSDocTag(node, isJSDocAbstractTag, /*noCache*/ true);
}

/** Gets the JSDoc deprecated tag for the node if present */
export function getJSDocDeprecatedTag(node: Node): JSDocDeprecatedTag | undefined {
return getFirstJSDocTag(node, isJSDocDeprecatedTag);
Expand Down
62 changes: 35 additions & 27 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,31 +421,32 @@ declare namespace ts {
JSDocTag = 314,
JSDocAugmentsTag = 315,
JSDocImplementsTag = 316,
JSDocAuthorTag = 317,
JSDocDeprecatedTag = 318,
JSDocClassTag = 319,
JSDocPublicTag = 320,
JSDocPrivateTag = 321,
JSDocProtectedTag = 322,
JSDocReadonlyTag = 323,
JSDocCallbackTag = 324,
JSDocEnumTag = 325,
JSDocParameterTag = 326,
JSDocReturnTag = 327,
JSDocThisTag = 328,
JSDocTypeTag = 329,
JSDocTemplateTag = 330,
JSDocTypedefTag = 331,
JSDocSeeTag = 332,
JSDocPropertyTag = 333,
SyntaxList = 334,
NotEmittedStatement = 335,
PartiallyEmittedExpression = 336,
CommaListExpression = 337,
MergeDeclarationMarker = 338,
EndOfDeclarationMarker = 339,
SyntheticReferenceExpression = 340,
Count = 341,
JSDocAbstractTag = 317,
JSDocAuthorTag = 318,
JSDocDeprecatedTag = 319,
JSDocClassTag = 320,
JSDocPublicTag = 321,
JSDocPrivateTag = 322,
JSDocProtectedTag = 323,
JSDocReadonlyTag = 324,
JSDocCallbackTag = 325,
JSDocEnumTag = 326,
JSDocParameterTag = 327,
JSDocReturnTag = 328,
JSDocThisTag = 329,
JSDocTypeTag = 330,
JSDocTemplateTag = 331,
JSDocTypedefTag = 332,
JSDocSeeTag = 333,
JSDocPropertyTag = 334,
SyntaxList = 335,
NotEmittedStatement = 336,
PartiallyEmittedExpression = 337,
CommaListExpression = 338,
MergeDeclarationMarker = 339,
EndOfDeclarationMarker = 340,
SyntheticReferenceExpression = 341,
Count = 342,
FirstAssignment = 62,
LastAssignment = 77,
FirstCompoundAssignment = 63,
Expand Down Expand Up @@ -474,9 +475,9 @@ declare namespace ts {
LastStatement = 248,
FirstNode = 157,
FirstJSDocNode = 301,
LastJSDocNode = 333,
LastJSDocNode = 334,
FirstJSDocTagNode = 314,
LastJSDocTagNode = 333,
LastJSDocTagNode = 334,
}
export type TriviaSyntaxKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia | SyntaxKind.NewLineTrivia | SyntaxKind.WhitespaceTrivia | SyntaxKind.ShebangTrivia | SyntaxKind.ConflictMarkerTrivia;
export type LiteralSyntaxKind = SyntaxKind.NumericLiteral | SyntaxKind.BigIntLiteral | SyntaxKind.StringLiteral | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.RegularExpressionLiteral | SyntaxKind.NoSubstitutionTemplateLiteral;
Expand Down Expand Up @@ -1768,6 +1769,9 @@ declare namespace ts {
readonly expression: Identifier | PropertyAccessEntityNameExpression;
};
}
export interface JSDocAbstractTag extends JSDocTag {
readonly kind: SyntaxKind.JSDocAbstractTag;
}
export interface JSDocAuthorTag extends JSDocTag {
readonly kind: SyntaxKind.JSDocAuthorTag;
}
Expand Down Expand Up @@ -3479,6 +3483,8 @@ declare namespace ts {
updateJSDocAugmentsTag(node: JSDocAugmentsTag, tagName: Identifier | undefined, className: JSDocAugmentsTag["class"], comment: string | undefined): JSDocAugmentsTag;
createJSDocImplementsTag(tagName: Identifier | undefined, className: JSDocImplementsTag["class"], comment?: string): JSDocImplementsTag;
updateJSDocImplementsTag(node: JSDocImplementsTag, tagName: Identifier | undefined, className: JSDocImplementsTag["class"], comment: string | undefined): JSDocImplementsTag;
createJSDocAbstractTag(tagName: Identifier | undefined, comment?: string): JSDocAbstractTag;
updateJSDocAbstractTag(node: JSDocAbstractTag, tagName: Identifier | undefined, comment: string | undefined): JSDocAbstractTag;
createJSDocAuthorTag(tagName: Identifier | undefined, comment?: string): JSDocAuthorTag;
updateJSDocAuthorTag(node: JSDocAuthorTag, tagName: Identifier | undefined, comment: string | undefined): JSDocAuthorTag;
createJSDocClassTag(tagName: Identifier | undefined, comment?: string): JSDocClassTag;
Expand Down Expand Up @@ -4141,6 +4147,7 @@ declare namespace ts {
function getJSDocProtectedTag(node: Node): JSDocProtectedTag | undefined;
/** Gets the JSDoc protected tag for the node if present */
function getJSDocReadonlyTag(node: Node): JSDocReadonlyTag | undefined;
function getJSDocAbstractTagNoCache(node: Node): JSDocAbstractTag | undefined;
/** Gets the JSDoc deprecated tag for the node if present */
function getJSDocDeprecatedTag(node: Node): JSDocDeprecatedTag | undefined;
/** Gets the JSDoc enum tag for the node if present */
Expand Down Expand Up @@ -4499,6 +4506,7 @@ declare namespace ts {
function isJSDocTypeLiteral(node: Node): node is JSDocTypeLiteral;
function isJSDocSignature(node: Node): node is JSDocSignature;
function isJSDocAugmentsTag(node: Node): node is JSDocAugmentsTag;
function isJSDocAbstractTag(node: Node): node is JSDocAbstractTag;
function isJSDocAuthorTag(node: Node): node is JSDocAuthorTag;
function isJSDocClassTag(node: Node): node is JSDocClassTag;
function isJSDocCallbackTag(node: Node): node is JSDocCallbackTag;
Expand Down
Loading

0 comments on commit 9dfbcfd

Please sign in to comment.