Skip to content

Commit 35efe4a

Browse files
committed
Concatenate inherited and local docs when @inheritdoc is present
1 parent 6683e1f commit 35efe4a

File tree

5 files changed

+56
-70
lines changed

5 files changed

+56
-70
lines changed

src/compiler/parser.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6380,9 +6380,6 @@ namespace ts {
63806380
case "constructor":
63816381
tag = parseClassTag(atToken, tagName);
63826382
break;
6383-
case "inheritDoc":
6384-
tag = parseInheritDocTag(atToken, tagName);
6385-
break;
63866383
case "arg":
63876384
case "argument":
63886385
case "param":
@@ -6614,13 +6611,6 @@ namespace ts {
66146611
return finishNode(result);
66156612
}
66166613

6617-
function parseInheritDocTag(atToken: AtToken, tagName: Identifier): JSDocInheritDocTag {
6618-
const tag = <JSDocInheritDocTag>createNode(SyntaxKind.JSDocInheritDocTag, atToken.pos);
6619-
tag.atToken = atToken;
6620-
tag.tagName = tagName;
6621-
return finishNode(tag);
6622-
}
6623-
66246614
function parseAugmentsTag(atToken: AtToken, tagName: Identifier): JSDocAugmentsTag {
66256615
const result = <JSDocAugmentsTag>createNode(SyntaxKind.JSDocAugmentsTag, atToken.pos);
66266616
result.atToken = atToken;

src/compiler/types.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,6 @@ namespace ts {
372372
JSDocTypedefTag,
373373
JSDocPropertyTag,
374374
JSDocTypeLiteral,
375-
JSDocInheritDocTag,
376375

377376
// Synthesized list
378377
SyntaxList,
@@ -414,9 +413,9 @@ namespace ts {
414413
LastBinaryOperator = CaretEqualsToken,
415414
FirstNode = QualifiedName,
416415
FirstJSDocNode = JSDocTypeExpression,
417-
LastJSDocNode = JSDocInheritDocTag,
416+
LastJSDocNode = JSDocTypeLiteral,
418417
FirstJSDocTagNode = JSDocTag,
419-
LastJSDocTagNode = JSDocInheritDocTag
418+
LastJSDocTagNode = JSDocTypeLiteral
420419
}
421420

422421
export const enum NodeFlags {
@@ -2175,10 +2174,6 @@ namespace ts {
21752174
kind: SyntaxKind.JSDocClassTag;
21762175
}
21772176

2178-
export interface JSDocInheritDocTag extends JSDocTag {
2179-
kind: SyntaxKind.JSDocInheritDocTag;
2180-
}
2181-
21822177
export interface JSDocTemplateTag extends JSDocTag {
21832178
kind: SyntaxKind.JSDocTemplateTag;
21842179
typeParameters: NodeArray<TypeParameterDeclaration>;

src/services/services.ts

Lines changed: 42 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -347,11 +347,14 @@ namespace ts {
347347
if (this.declarations) {
348348
this.documentationComment = JsDoc.getJsDocCommentsFromDeclarations(this.declarations);
349349

350-
if (this.documentationComment.length === 0 || this.declarations.some(dec => hasJSDocInheritDocTag(dec))) {
350+
if (this.documentationComment.length === 0 || this.declarations.some(hasJSDocInheritDocTag)) {
351351
for (const declaration of this.declarations) {
352352
const inheritedDocs = findInheritedJSDocComments(declaration, this.getName(), checker);
353353
if (inheritedDocs.length > 0) {
354-
this.documentationComment = inheritedDocs;
354+
if (this.documentationComment.length > 0) {
355+
inheritedDocs.push(ts.lineBreakPart());
356+
}
357+
this.documentationComment = concatenate(inheritedDocs, this.documentationComment);
355358
break;
356359
}
357360
}
@@ -491,8 +494,15 @@ namespace ts {
491494
if (this.declaration) {
492495
this.documentationComment = JsDoc.getJsDocCommentsFromDeclarations([this.declaration]);
493496

494-
if (this.documentationComment.length === 0) {
495-
this.documentationComment = findInheritedJSDocComments(this.declaration, this.declaration.symbol.getName(), this.checker);
497+
if (this.documentationComment.length === 0 || hasJSDocInheritDocTag(this.declaration)) {
498+
const inheritedDocs = findInheritedJSDocComments(this.declaration, this.declaration.symbol.getName(), this.checker);
499+
if (this.documentationComment.length > 0) {
500+
inheritedDocs.push(ts.lineBreakPart());
501+
}
502+
this.documentationComment = concatenate(
503+
inheritedDocs,
504+
this.documentationComment
505+
);
496506
}
497507
}
498508
else {
@@ -530,51 +540,38 @@ namespace ts {
530540
* @returns A filled array of documentation comments if any were found, otherwise an empty array.
531541
*/
532542
function findInheritedJSDocComments(declaration: Declaration, propertyName: string, typeChecker: TypeChecker): SymbolDisplayPart[] {
533-
let documentationComment: SymbolDisplayPart[] = [];
543+
let foundDocs = false;
544+
return flatMap(getAllSuperTypeNodes(declaration), superTypeNode => {
545+
if (foundDocs) {
546+
return emptyArray;
547+
}
548+
const superType = typeChecker.getTypeAtLocation(superTypeNode);
549+
if (!superType) {
550+
return emptyArray;
551+
}
552+
const baseProperty = typeChecker.getPropertyOfType(superType, propertyName);
553+
if (!baseProperty) {
554+
return emptyArray;
555+
}
556+
const inheritedDocs = baseProperty.getDocumentationComment(typeChecker);
557+
foundDocs = inheritedDocs.length > 0;
558+
return inheritedDocs;
559+
});
560+
}
534561

562+
/**
563+
* Finds and returns the `TypeNode` for all super classes and implemented interfaces given a declaration.
564+
* @param declaration The possibly-inherited declaration.
565+
* @returns A filled array of `TypeNode`s containing all super classes and implemented interfaces if any exist, otherwise an empty array.
566+
*/
567+
function getAllSuperTypeNodes(declaration: Declaration): ReadonlyArray<TypeNode> {
535568
const container = declaration.parent;
536-
537569
if (!container || (!isClassDeclaration(container) && !isInterfaceDeclaration(container))) {
538-
return documentationComment;
539-
}
540-
else {
541-
const baseTypeNode = getClassExtendsHeritageClauseElement(container);
542-
if (!baseTypeNode) {
543-
return documentationComment;
544-
}
545-
546-
const baseType = typeChecker.getTypeAtLocation(baseTypeNode);
547-
if (!baseType) {
548-
return documentationComment;
549-
}
550-
551-
// First check superclasses for a property of the same name
552-
let baseProperty = typeChecker.getPropertyOfType(baseType, propertyName);
553-
let baseDocs = baseProperty ? baseProperty.getDocumentationComment(typeChecker) : [];
554-
if (baseDocs.length > 0) {
555-
documentationComment = baseDocs;
556-
}
557-
558-
// If there's nothing in the superclass, walk through implemented interfaces left-to-right
559-
if (documentationComment.length === 0) {
560-
const implementedInterfaces = map(
561-
getClassImplementsHeritageClauseElements(container as ClassLikeDeclaration),
562-
interfaceNode => typeChecker.getTypeAtLocation(interfaceNode)
563-
) || [];
564-
565-
for (const implementedInterface of implementedInterfaces) {
566-
// Use the docs from the first implemented interface to have this property and documentation
567-
baseProperty = typeChecker.getPropertyOfType(implementedInterface, propertyName);
568-
baseDocs = baseProperty ? baseProperty.getDocumentationComment(typeChecker) : [];
569-
if (baseDocs.length > 0) {
570-
documentationComment = baseDocs;
571-
break;
572-
}
573-
}
574-
}
575-
576-
return documentationComment;
570+
return emptyArray;
577571
}
572+
const extended = getClassExtendsHeritageClauseElement(container);
573+
const types = extended ? [extended] : emptyArray;
574+
return isClassLike(container) ? concatenate(types, getClassImplementsHeritageClauseElements(container)) : types;
578575
}
579576

580577
class SourceFileObject extends NodeObject implements SourceFile {

tests/cases/fourslash/commentsInheritance.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -263,8 +263,8 @@ verify.quickInfos({
263263
});
264264

265265
goTo.marker('6');
266-
verify.completionListContains("i1_p1", "(property) c1.i1_p1: number", "");
267-
verify.completionListContains("i1_f1", "(method) c1.i1_f1(): void", "");
266+
verify.completionListContains("i1_p1", "(property) c1.i1_p1: number", "i1_p1");
267+
verify.completionListContains("i1_f1", "(method) c1.i1_f1(): void", "i1_f1");
268268
verify.completionListContains("i1_l1", "(property) c1.i1_l1: () => void", "");
269269
verify.completionListContains("i1_nc_p1", "(property) c1.i1_nc_p1: number", "");
270270
verify.completionListContains("i1_nc_f1", "(method) c1.i1_nc_f1(): void", "");
@@ -276,7 +276,7 @@ verify.completionListContains("nc_p1", "(property) c1.nc_p1: number", "c1_nc_p1"
276276
verify.completionListContains("nc_f1", "(method) c1.nc_f1(): void", "c1_nc_f1");
277277
verify.completionListContains("nc_l1", "(property) c1.nc_l1: () => void", "");
278278
goTo.marker('7');
279-
verify.currentSignatureHelpDocCommentIs("");
279+
verify.currentSignatureHelpDocCommentIs("i1_f1");
280280
goTo.marker('8');
281281
verify.currentSignatureHelpDocCommentIs("");
282282
goTo.marker('9');
@@ -294,7 +294,7 @@ verify.currentSignatureHelpDocCommentIs("");
294294

295295
verify.quickInfos({
296296
"6iq": "var c1_i: c1",
297-
"7q": "(method) c1.i1_f1(): void",
297+
"7q": ["(method) c1.i1_f1(): void", "i1_f1"],
298298
"8q": "(method) c1.i1_nc_f1(): void",
299299
"9q": ["(method) c1.f1(): void", "c1_f1"],
300300
"10q": ["(method) c1.nc_f1(): void", "c1_nc_f1"],
@@ -515,7 +515,7 @@ verify.quickInfos({
515515
"39q": ["(method) i2.f1(): void", "i2 f1"],
516516
"40q": "(method) i2.nc_f1(): void",
517517
"l37q": "(property) i2.i2_l1: () => void",
518-
"l38q": "(property) i2.i2_nc_l1: () => void",
518+
"l38q": "(property) i2.i2_nc_l1: () => void",
519519
"l39q": "(property) i2.l1: () => void",
520520
"l40q": "(property) i2.nc_l1: () => void",
521521
});

tests/cases/fourslash/jsDocInheritDoc.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@
3838
//// method2() {}
3939
//// /** @inheritDoc */
4040
//// property1: string;
41+
//// /**
42+
//// * Bar#property2
43+
//// * @inheritDoc
44+
//// */
4145
//// property2: object;
4246
////}
4347
////const b = new Bar/*1*/(5);
@@ -47,7 +51,7 @@
4751
////const p2 = b.property2/*5*/;
4852

4953
verify.quickInfoAt("1", "constructor Bar(value: number): Bar", undefined); // constructors aren't actually inherited
50-
verify.quickInfoAt("2", "(method) Bar.method2(): void", "Foo#method2 documentation");
54+
verify.quickInfoAt("2", "(method) Bar.method2(): void", "Foo#method2 documentation"); // use inherited docs only
5155
verify.quickInfoAt("3", "(method) Bar.method1(): void", undefined); // statics aren't actually inherited
52-
verify.quickInfoAt("4", "(property) Bar.property1: string", "Foo#property1 documentation");
53-
verify.quickInfoAt("5", "(property) Bar.property2: object", "Baz#property2 documentation");
56+
verify.quickInfoAt("4", "(property) Bar.property1: string", "Foo#property1 documentation"); // use inherited docs only
57+
verify.quickInfoAt("5", "(property) Bar.property2: object", "Baz#property2 documentation\nBar#property2"); // include local and inherited docs

0 commit comments

Comments
 (0)