From a57d9a1d3df01e9289ecd44dc635d4aeb6385732 Mon Sep 17 00:00:00 2001 From: Sean Barag Date: Thu, 31 Aug 2017 08:49:57 -0700 Subject: [PATCH 1/8] Use documentation comments from inherited properties when @inheritDoc is present The JSDoc `@ineheritDoc` [tag](http://usejsdoc.org/tags-inheritdoc.html) "indicates that a symbol should inherit its documentation from its parent class". In the case of a TypeScript file, this also includes implemented interfaces and parent interfaces. With this change, a class method or property (or an interface property) with the `@inheritDoc` tag in its JSDoc comment will automatically use the comments from its nearest ancestor that has no `@inheritDoc` tag. To prevent breaking backwards compatibility, `Symbol.getDocumentationComment` now accepts an optional `TypeChecker` instance to support this feature. fixes #8912 --- src/compiler/parser.ts | 10 + src/compiler/types.ts | 9 +- src/services/jsDoc.ts | 1 + src/services/services.ts | 77 +++- src/services/signatureHelp.ts | 6 +- src/services/symbolDisplay.ts | 6 +- src/services/types.ts | 4 +- .../reference/api/tsserverlibrary.d.ts | 26 +- tests/baselines/reference/api/typescript.d.ts | 26 +- tests/baselines/reference/inheritDoc.baseline | 356 ++++++++++++++++++ tests/cases/fourslash/jsDocInheritDoc.ts | 49 +++ 11 files changed, 535 insertions(+), 35 deletions(-) create mode 100644 tests/baselines/reference/inheritDoc.baseline create mode 100644 tests/cases/fourslash/jsDocInheritDoc.ts diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 648f3e30d1c24..03548f83f20a6 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -6426,6 +6426,9 @@ namespace ts { case "constructor": tag = parseClassTag(atToken, tagName); break; + case "inheritDoc": + tag = parseInheritDocTag(atToken, tagName); + break; case "arg": case "argument": case "param": @@ -6657,6 +6660,13 @@ namespace ts { return finishNode(result); } + function parseInheritDocTag(atToken: AtToken, tagName: Identifier): JSDocInheritDocTag { + const tag = createNode(SyntaxKind.JSDocInheritDocTag, atToken.pos); + tag.atToken = atToken; + tag.tagName = tagName; + return finishNode(tag); + } + function parseAugmentsTag(atToken: AtToken, tagName: Identifier): JSDocAugmentsTag { const result = createNode(SyntaxKind.JSDocAugmentsTag, atToken.pos); result.atToken = atToken; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 549c0f8d52fa5..d1c88cf5887a1 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -375,6 +375,7 @@ namespace ts { JSDocTemplateTag, JSDocTypedefTag, JSDocPropertyTag, + JSDocInheritDocTag, // Synthesized list SyntaxList, @@ -416,9 +417,9 @@ namespace ts { LastBinaryOperator = CaretEqualsToken, FirstNode = QualifiedName, FirstJSDocNode = JSDocTypeExpression, - LastJSDocNode = JSDocPropertyTag, + LastJSDocNode = JSDocInheritDocTag, FirstJSDocTagNode = JSDocTag, - LastJSDocTagNode = JSDocPropertyTag + LastJSDocTagNode = JSDocInheritDocTag } export const enum NodeFlags { @@ -2197,6 +2198,10 @@ namespace ts { kind: SyntaxKind.JSDocClassTag; } + export interface JSDocInheritDocTag extends JSDocTag { + kind: SyntaxKind.JSDocInheritDocTag; + } + export interface JSDocTemplateTag extends JSDocTag { kind: SyntaxKind.JSDocTemplateTag; typeParameters: NodeArray; diff --git a/src/services/jsDoc.ts b/src/services/jsDoc.ts index 9c8b085d19848..d60b9a0bcc7d7 100644 --- a/src/services/jsDoc.ts +++ b/src/services/jsDoc.ts @@ -19,6 +19,7 @@ namespace ts.JsDoc { "fileOverview", "function", "ignore", + "inheritDoc", "inner", "lends", "link", diff --git a/src/services/services.ts b/src/services/services.ts index cd6fe8677113a..83b9d8152526b 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -345,9 +345,19 @@ namespace ts { return this.declarations; } - getDocumentationComment(): SymbolDisplayPart[] { + getDocumentationComment(typeChecker?: TypeChecker): SymbolDisplayPart[] { if (this.documentationComment === undefined) { this.documentationComment = JsDoc.getJsDocCommentsFromDeclarations(this.declarations); + + if (this.documentationComment.length === 0 && hasJSDocInheritDocTag(this) && typeChecker) { + for (const declaration of this.getDeclarations()) { + const inheritedDocs = findInheritedJSDocComments(declaration, this.getName(), typeChecker); + if (inheritedDocs.length > 0) { + this.documentationComment = inheritedDocs; + break; + } + } + } } return this.documentationComment; @@ -474,9 +484,13 @@ namespace ts { return this.checker.getReturnTypeOfSignature(this); } - getDocumentationComment(): SymbolDisplayPart[] { + getDocumentationComment(typeChecker?: TypeChecker): SymbolDisplayPart[] { if (this.documentationComment === undefined) { this.documentationComment = this.declaration ? JsDoc.getJsDocCommentsFromDeclarations([this.declaration]) : []; + + if (this.declaration && this.documentationComment.length === 0 && hasJSDocInheritDocTag(this) && typeChecker) { + this.documentationComment = findInheritedJSDocComments(this.declaration, this.declaration.symbol.getName(), typeChecker); + } } return this.documentationComment; @@ -491,6 +505,63 @@ namespace ts { } } + /** + * Returns whether or not the given symbol or signature has a JSDoc "inheritDoc" tag on it. + * @param symbol the Symbol or Signature in question. + * @returns `true` if `symbol` has a JSDoc "inheritDoc" tag on it, otherwise `false`. + */ + function hasJSDocInheritDocTag(symbol: Signature | Symbol) { + return !!find(symbol.getJsDocTags(), tag => tag.name === "inheritDoc"); + } + + /** + * Attempts to find JSDoc comments for possibly-inherited properties. Checks superclasses then traverses + * implemented interfaces until a symbol is found with the same name and with documentation. + * @param declaration The possibly-inherited declaration to find comments for. + * @param propertyName The name of the possibly-inherited property. + * @param typeChecker A TypeChecker, used to find inherited properties. + * @returns A filled array of documentation comments if any were found, otherwise an empty array. + */ + function findInheritedJSDocComments(declaration: Declaration, propertyName: string, typeChecker: TypeChecker): SymbolDisplayPart[] { + let documentationComment: SymbolDisplayPart[] = []; + + if (isClassDeclaration(declaration.parent) || isInterfaceDeclaration(declaration.parent)) { + const container: ClassDeclaration | InterfaceDeclaration = declaration.parent; + const baseTypeNode = getClassExtendsHeritageClauseElement(container); + + if (baseTypeNode) { + const baseType = typeChecker.getTypeAtLocation(baseTypeNode); + + // First check superclasses for a property of the same name + let baseProperty = typeChecker.getPropertyOfType(baseType, propertyName); + let baseDocs = baseProperty ? baseProperty.getDocumentationComment(typeChecker) : []; + if (baseDocs.length > 0) { + documentationComment = baseDocs; + } + + // If there's nothing in the superclass, walk through implemented interfaces left-to-right + if (documentationComment.length === 0) { + const implementedInterfaces = map( + getClassImplementsHeritageClauseElements(container as ClassLikeDeclaration), + interfaceNode => typeChecker.getTypeAtLocation(interfaceNode) + ); + + for (const implementedInterface of implementedInterfaces) { + // Use the docs from the first implemented interface to have this property and documentation + baseProperty = typeChecker.getPropertyOfType(implementedInterface, propertyName); + baseDocs = baseProperty ? baseProperty.getDocumentationComment(typeChecker) : []; + if (baseDocs.length > 0) { + documentationComment = baseDocs; + break; + } + } + } + } + } + + return documentationComment; + } + class SourceFileObject extends NodeObject implements SourceFile { public kind: SyntaxKind.SourceFile; public _declarationBrand: any; @@ -1390,7 +1461,7 @@ namespace ts { kindModifiers: ScriptElementKindModifier.none, textSpan: createTextSpan(node.getStart(), node.getWidth()), displayParts: typeToDisplayParts(typeChecker, type, getContainerNode(node)), - documentation: type.symbol ? type.symbol.getDocumentationComment() : undefined, + documentation: type.symbol ? type.symbol.getDocumentationComment(typeChecker) : undefined, tags: type.symbol ? type.symbol.getJsDocTags() : undefined }; } diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index 7e8e748bb172d..36c83f5f4af38 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -400,7 +400,7 @@ namespace ts.SignatureHelp { suffixDisplayParts, separatorDisplayParts: [punctuationPart(SyntaxKind.CommaToken), spacePart()], parameters: signatureHelpParameters, - documentation: candidateSignature.getDocumentationComment(), + documentation: candidateSignature.getDocumentationComment(typeChecker), tags: candidateSignature.getJsDocTags() }; }); @@ -420,7 +420,7 @@ namespace ts.SignatureHelp { return { name: parameter.name, - documentation: parameter.getDocumentationComment(), + documentation: parameter.getDocumentationComment(typeChecker), displayParts, isOptional: typeChecker.isOptionalParameter(parameter.valueDeclaration) }; @@ -438,4 +438,4 @@ namespace ts.SignatureHelp { }; } } -} \ No newline at end of file +} diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index d210a04551377..c3a1465197d25 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -438,7 +438,7 @@ namespace ts.SymbolDisplay { } if (!documentation) { - documentation = symbol.getDocumentationComment(); + documentation = symbol.getDocumentationComment(typeChecker); tags = symbol.getJsDocTags(); if (documentation.length === 0 && symbolFlags & SymbolFlags.Property) { // For some special property access expressions like `exports.foo = foo` or `module.exports.foo = foo` @@ -455,7 +455,7 @@ namespace ts.SymbolDisplay { continue; } - documentation = rhsSymbol.getDocumentationComment(); + documentation = rhsSymbol.getDocumentationComment(typeChecker); tags = rhsSymbol.getJsDocTags(); if (documentation.length > 0) { break; @@ -522,7 +522,7 @@ namespace ts.SymbolDisplay { displayParts.push(textPart(allSignatures.length === 2 ? "overload" : "overloads")); displayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); } - documentation = signature.getDocumentationComment(); + documentation = signature.getDocumentationComment(typeChecker); tags = signature.getJsDocTags(); } diff --git a/src/services/types.ts b/src/services/types.ts index 2d629041d1f8f..8f0fff62d6d9e 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -32,7 +32,7 @@ namespace ts { getEscapedName(): __String; getName(): string; getDeclarations(): Declaration[] | undefined; - getDocumentationComment(): SymbolDisplayPart[]; + getDocumentationComment(typeChecker?: TypeChecker): SymbolDisplayPart[]; getJsDocTags(): JSDocTagInfo[]; } @@ -55,7 +55,7 @@ namespace ts { getTypeParameters(): TypeParameter[] | undefined; getParameters(): Symbol[]; getReturnType(): Type; - getDocumentationComment(): SymbolDisplayPart[]; + getDocumentationComment(typeChecker?: TypeChecker): SymbolDisplayPart[]; getJsDocTags(): JSDocTagInfo[]; } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 7fbff83009fc0..7e89dfb5a66ab 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -349,13 +349,14 @@ declare namespace ts { JSDocTemplateTag = 286, JSDocTypedefTag = 287, JSDocPropertyTag = 288, - SyntaxList = 289, - NotEmittedStatement = 290, - PartiallyEmittedExpression = 291, - CommaListExpression = 292, - MergeDeclarationMarker = 293, - EndOfDeclarationMarker = 294, - Count = 295, + JSDocInheritDocTag = 289, + SyntaxList = 290, + NotEmittedStatement = 291, + PartiallyEmittedExpression = 292, + CommaListExpression = 293, + MergeDeclarationMarker = 294, + EndOfDeclarationMarker = 295, + Count = 296, FirstAssignment = 58, LastAssignment = 70, FirstCompoundAssignment = 59, @@ -382,9 +383,9 @@ declare namespace ts { LastBinaryOperator = 70, FirstNode = 143, FirstJSDocNode = 270, - LastJSDocNode = 288, + LastJSDocNode = 289, FirstJSDocTagNode = 280, - LastJSDocTagNode = 288, + LastJSDocTagNode = 289, } enum NodeFlags { None = 0, @@ -1472,6 +1473,9 @@ declare namespace ts { interface JSDocClassTag extends JSDocTag { kind: SyntaxKind.JSDocClassTag; } + interface JSDocInheritDocTag extends JSDocTag { + kind: SyntaxKind.JSDocInheritDocTag; + } interface JSDocTemplateTag extends JSDocTag { kind: SyntaxKind.JSDocTemplateTag; typeParameters: NodeArray; @@ -3817,7 +3821,7 @@ declare namespace ts { getEscapedName(): __String; getName(): string; getDeclarations(): Declaration[] | undefined; - getDocumentationComment(): SymbolDisplayPart[]; + getDocumentationComment(typeChecker?: TypeChecker): SymbolDisplayPart[]; getJsDocTags(): JSDocTagInfo[]; } interface Type { @@ -3838,7 +3842,7 @@ declare namespace ts { getTypeParameters(): TypeParameter[] | undefined; getParameters(): Symbol[]; getReturnType(): Type; - getDocumentationComment(): SymbolDisplayPart[]; + getDocumentationComment(typeChecker?: TypeChecker): SymbolDisplayPart[]; getJsDocTags(): JSDocTagInfo[]; } interface SourceFile { diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 73d069757dfef..8ef34bd28519f 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -349,13 +349,14 @@ declare namespace ts { JSDocTemplateTag = 286, JSDocTypedefTag = 287, JSDocPropertyTag = 288, - SyntaxList = 289, - NotEmittedStatement = 290, - PartiallyEmittedExpression = 291, - CommaListExpression = 292, - MergeDeclarationMarker = 293, - EndOfDeclarationMarker = 294, - Count = 295, + JSDocInheritDocTag = 289, + SyntaxList = 290, + NotEmittedStatement = 291, + PartiallyEmittedExpression = 292, + CommaListExpression = 293, + MergeDeclarationMarker = 294, + EndOfDeclarationMarker = 295, + Count = 296, FirstAssignment = 58, LastAssignment = 70, FirstCompoundAssignment = 59, @@ -382,9 +383,9 @@ declare namespace ts { LastBinaryOperator = 70, FirstNode = 143, FirstJSDocNode = 270, - LastJSDocNode = 288, + LastJSDocNode = 289, FirstJSDocTagNode = 280, - LastJSDocTagNode = 288, + LastJSDocTagNode = 289, } enum NodeFlags { None = 0, @@ -1472,6 +1473,9 @@ declare namespace ts { interface JSDocClassTag extends JSDocTag { kind: SyntaxKind.JSDocClassTag; } + interface JSDocInheritDocTag extends JSDocTag { + kind: SyntaxKind.JSDocInheritDocTag; + } interface JSDocTemplateTag extends JSDocTag { kind: SyntaxKind.JSDocTemplateTag; typeParameters: NodeArray; @@ -3817,7 +3821,7 @@ declare namespace ts { getEscapedName(): __String; getName(): string; getDeclarations(): Declaration[] | undefined; - getDocumentationComment(): SymbolDisplayPart[]; + getDocumentationComment(typeChecker?: TypeChecker): SymbolDisplayPart[]; getJsDocTags(): JSDocTagInfo[]; } interface Type { @@ -3838,7 +3842,7 @@ declare namespace ts { getTypeParameters(): TypeParameter[] | undefined; getParameters(): Symbol[]; getReturnType(): Type; - getDocumentationComment(): SymbolDisplayPart[]; + getDocumentationComment(typeChecker?: TypeChecker): SymbolDisplayPart[]; getJsDocTags(): JSDocTagInfo[]; } interface SourceFile { diff --git a/tests/baselines/reference/inheritDoc.baseline b/tests/baselines/reference/inheritDoc.baseline new file mode 100644 index 0000000000000..df7e6c9b8b362 --- /dev/null +++ b/tests/baselines/reference/inheritDoc.baseline @@ -0,0 +1,356 @@ +[ + { + "marker": { + "fileName": "/tests/cases/fourslash/inheritDoc.ts", + "position": 832 + }, + "quickInfo": { + "kind": "constructor", + "kindModifiers": "", + "textSpan": { + "start": 829, + "length": 3 + }, + "displayParts": [ + { + "text": "constructor", + "kind": "keyword" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "Bar", + "kind": "className" + }, + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "value", + "kind": "parameterName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "number", + "kind": "keyword" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "Bar", + "kind": "className" + } + ], + "documentation": [], + "tags": [ + { + "name": "inheritDoc", + "text": "" + } + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/inheritDoc.ts", + "position": 846 + }, + "quickInfo": { + "kind": "method", + "kindModifiers": "", + "textSpan": { + "start": 839, + "length": 7 + }, + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "method", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "Bar", + "kind": "className" + }, + { + "text": ".", + "kind": "punctuation" + }, + { + "text": "method2", + "kind": "methodName" + }, + { + "text": "(", + "kind": "punctuation" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "void", + "kind": "keyword" + } + ], + "documentation": [ + { + "text": "Foo#method2 documentation", + "kind": "text" + } + ], + "tags": [ + { + "name": "inheritDoc", + "text": "" + } + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/inheritDoc.ts", + "position": 861 + }, + "quickInfo": { + "kind": "method", + "kindModifiers": "static", + "textSpan": { + "start": 854, + "length": 7 + }, + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "method", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "Bar", + "kind": "className" + }, + { + "text": ".", + "kind": "punctuation" + }, + { + "text": "method1", + "kind": "methodName" + }, + { + "text": "(", + "kind": "punctuation" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "void", + "kind": "keyword" + } + ], + "documentation": [], + "tags": [ + { + "name": "inheritDoc", + "text": "" + } + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/inheritDoc.ts", + "position": 887 + }, + "quickInfo": { + "kind": "property", + "kindModifiers": "", + "textSpan": { + "start": 878, + "length": 9 + }, + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "property", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "Bar", + "kind": "className" + }, + { + "text": ".", + "kind": "punctuation" + }, + { + "text": "property1", + "kind": "propertyName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "string", + "kind": "keyword" + } + ], + "documentation": [ + { + "text": "Foo#property1 documentation", + "kind": "text" + } + ], + "tags": [ + { + "name": "inheritDoc", + "text": "" + } + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/inheritDoc.ts", + "position": 911 + }, + "quickInfo": { + "kind": "property", + "kindModifiers": "", + "textSpan": { + "start": 902, + "length": 9 + }, + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "property", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "Bar", + "kind": "className" + }, + { + "text": ".", + "kind": "punctuation" + }, + { + "text": "property2", + "kind": "propertyName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "object", + "kind": "keyword" + } + ], + "documentation": [ + { + "text": "Baz#property2 documentation ", + "kind": "text" + } + ], + "tags": [ + { + "name": "inheritDoc", + "text": "" + } + ] + } + } +] \ No newline at end of file diff --git a/tests/cases/fourslash/jsDocInheritDoc.ts b/tests/cases/fourslash/jsDocInheritDoc.ts new file mode 100644 index 0000000000000..44ebb93a066c4 --- /dev/null +++ b/tests/cases/fourslash/jsDocInheritDoc.ts @@ -0,0 +1,49 @@ +/// +// @Filename: inheritDoc.ts +////class Foo { +//// /** +//// * Foo constructor documentation +//// */ +//// constructor(value: number) {} +//// /** +//// * Foo#method1 documentation +//// */ +//// static method1() {} +//// /** +//// * Foo#method2 documentation +//// */ +//// method2() {} +//// /** +//// * Foo#property1 documentation +//// */ +//// property1: string; +////} +////interface Baz { +//// /** Baz#property1 documentation */ +//// property1: string; +//// /** Baz#property2 documentation */ +//// property2: object; +////} +////class Bar extends Foo implements Baz { +//// ctorValue: number; +//// /** @inheritDoc */ +//// constructor(value: number) { +//// super(value); +//// this.ctorValue = value; +//// } +//// /** @inheritDoc */ +//// static method1() {} +//// /** @inheritDoc */ +//// method2() {} +//// /** @inheritDoc */ +//// property1: string; +//// /** @inheritDoc */ +//// property2: object; +////} +////const b = new Bar/*1*/(5); +////b.method2/*2*/(); +////Bar.method1/*3*/(); +////const p1 = b.property1/*4*/; +////const p2 = b.property2/*5*/; + +verify.baselineQuickInfo(); From 632bbfdfcd2636b443e5c17745558cb264b3de9a Mon Sep 17 00:00:00 2001 From: Sean Barag Date: Thu, 26 Oct 2017 09:05:09 -0700 Subject: [PATCH 2/8] Use ts.getJSDocTags as per @andy-ms 's recommendation --- src/services/services.ts | 108 +++++++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 45 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 83b9d8152526b..bc859f0c29c40 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -345,19 +345,24 @@ namespace ts { return this.declarations; } - getDocumentationComment(typeChecker?: TypeChecker): SymbolDisplayPart[] { + getDocumentationComment(checker: TypeChecker): SymbolDisplayPart[] { if (this.documentationComment === undefined) { - this.documentationComment = JsDoc.getJsDocCommentsFromDeclarations(this.declarations); - - if (this.documentationComment.length === 0 && hasJSDocInheritDocTag(this) && typeChecker) { - for (const declaration of this.getDeclarations()) { - const inheritedDocs = findInheritedJSDocComments(declaration, this.getName(), typeChecker); - if (inheritedDocs.length > 0) { - this.documentationComment = inheritedDocs; - break; + if (this.declarations) { + this.documentationComment = JsDoc.getJsDocCommentsFromDeclarations(this.declarations); + + if (this.documentationComment.length === 0 || this.declarations.some(dec => hasJSDocInheritDocTag(dec))) { + for (const declaration of this.declarations) { + const inheritedDocs = findInheritedJSDocComments(declaration, this.getName(), checker); + if (inheritedDocs.length > 0) { + this.documentationComment = inheritedDocs; + break; + } } } } + else { + this.documentationComment = []; + } } return this.documentationComment; @@ -484,12 +489,17 @@ namespace ts { return this.checker.getReturnTypeOfSignature(this); } - getDocumentationComment(typeChecker?: TypeChecker): SymbolDisplayPart[] { + getDocumentationComment(): SymbolDisplayPart[] { if (this.documentationComment === undefined) { - this.documentationComment = this.declaration ? JsDoc.getJsDocCommentsFromDeclarations([this.declaration]) : []; + if (this.declaration) { + this.documentationComment = JsDoc.getJsDocCommentsFromDeclarations([this.declaration]); - if (this.declaration && this.documentationComment.length === 0 && hasJSDocInheritDocTag(this) && typeChecker) { - this.documentationComment = findInheritedJSDocComments(this.declaration, this.declaration.symbol.getName(), typeChecker); + if (this.documentationComment.length === 0) { + this.documentationComment = findInheritedJSDocComments(this.declaration, this.declaration.symbol.getName(), this.checker); + } + } + else { + this.documentationComment = []; } } @@ -506,12 +516,12 @@ namespace ts { } /** - * Returns whether or not the given symbol or signature has a JSDoc "inheritDoc" tag on it. - * @param symbol the Symbol or Signature in question. - * @returns `true` if `symbol` has a JSDoc "inheritDoc" tag on it, otherwise `false`. + * Returns whether or not the given node has a JSDoc "inheritDoc" tag on it. + * @param node the Node in question. + * @returns `true` if `node` has a JSDoc "inheritDoc" tag on it, otherwise `false`. */ - function hasJSDocInheritDocTag(symbol: Signature | Symbol) { - return !!find(symbol.getJsDocTags(), tag => tag.name === "inheritDoc"); + function hasJSDocInheritDocTag(node: Node) { + return ts.getJSDocTags(node).some(tag => tag.tagName.text === "inheritDoc"); } /** @@ -525,41 +535,49 @@ namespace ts { function findInheritedJSDocComments(declaration: Declaration, propertyName: string, typeChecker: TypeChecker): SymbolDisplayPart[] { let documentationComment: SymbolDisplayPart[] = []; - if (isClassDeclaration(declaration.parent) || isInterfaceDeclaration(declaration.parent)) { - const container: ClassDeclaration | InterfaceDeclaration = declaration.parent; + const container = declaration.parent; + + if (!container || (!isClassDeclaration(container) && !isInterfaceDeclaration(container))) { + return documentationComment; + } + else { const baseTypeNode = getClassExtendsHeritageClauseElement(container); + if (!baseTypeNode) { + return documentationComment; + } - if (baseTypeNode) { - const baseType = typeChecker.getTypeAtLocation(baseTypeNode); + const baseType = typeChecker.getTypeAtLocation(baseTypeNode); + if (!baseType) { + return documentationComment; + } - // First check superclasses for a property of the same name - let baseProperty = typeChecker.getPropertyOfType(baseType, propertyName); - let baseDocs = baseProperty ? baseProperty.getDocumentationComment(typeChecker) : []; - if (baseDocs.length > 0) { - documentationComment = baseDocs; - } + // First check superclasses for a property of the same name + let baseProperty = typeChecker.getPropertyOfType(baseType, propertyName); + let baseDocs = baseProperty ? baseProperty.getDocumentationComment(typeChecker) : []; + if (baseDocs.length > 0) { + documentationComment = baseDocs; + } - // If there's nothing in the superclass, walk through implemented interfaces left-to-right - if (documentationComment.length === 0) { - const implementedInterfaces = map( - getClassImplementsHeritageClauseElements(container as ClassLikeDeclaration), - interfaceNode => typeChecker.getTypeAtLocation(interfaceNode) - ); - - for (const implementedInterface of implementedInterfaces) { - // Use the docs from the first implemented interface to have this property and documentation - baseProperty = typeChecker.getPropertyOfType(implementedInterface, propertyName); - baseDocs = baseProperty ? baseProperty.getDocumentationComment(typeChecker) : []; - if (baseDocs.length > 0) { - documentationComment = baseDocs; - break; - } + // If there's nothing in the superclass, walk through implemented interfaces left-to-right + if (documentationComment.length === 0) { + const implementedInterfaces = map( + getClassImplementsHeritageClauseElements(container as ClassLikeDeclaration), + interfaceNode => typeChecker.getTypeAtLocation(interfaceNode) + ) || []; + + for (const implementedInterface of implementedInterfaces) { + // Use the docs from the first implemented interface to have this property and documentation + baseProperty = typeChecker.getPropertyOfType(implementedInterface, propertyName); + baseDocs = baseProperty ? baseProperty.getDocumentationComment(typeChecker) : []; + if (baseDocs.length > 0) { + documentationComment = baseDocs; + break; } } } - } - return documentationComment; + return documentationComment; + } } class SourceFileObject extends NodeObject implements SourceFile { From 37678c3ceec1184b020de9d002d39dd13b2ff879 Mon Sep 17 00:00:00 2001 From: Sean Barag Date: Thu, 26 Oct 2017 10:55:36 -0700 Subject: [PATCH 3/8] Convert @inheritDoc tests to verify.quickInfoAt --- tests/baselines/reference/inheritDoc.baseline | 356 ------------------ tests/cases/fourslash/jsDocInheritDoc.ts | 12 +- 2 files changed, 8 insertions(+), 360 deletions(-) delete mode 100644 tests/baselines/reference/inheritDoc.baseline diff --git a/tests/baselines/reference/inheritDoc.baseline b/tests/baselines/reference/inheritDoc.baseline deleted file mode 100644 index df7e6c9b8b362..0000000000000 --- a/tests/baselines/reference/inheritDoc.baseline +++ /dev/null @@ -1,356 +0,0 @@ -[ - { - "marker": { - "fileName": "/tests/cases/fourslash/inheritDoc.ts", - "position": 832 - }, - "quickInfo": { - "kind": "constructor", - "kindModifiers": "", - "textSpan": { - "start": 829, - "length": 3 - }, - "displayParts": [ - { - "text": "constructor", - "kind": "keyword" - }, - { - "text": " ", - "kind": "space" - }, - { - "text": "Bar", - "kind": "className" - }, - { - "text": "(", - "kind": "punctuation" - }, - { - "text": "value", - "kind": "parameterName" - }, - { - "text": ":", - "kind": "punctuation" - }, - { - "text": " ", - "kind": "space" - }, - { - "text": "number", - "kind": "keyword" - }, - { - "text": ")", - "kind": "punctuation" - }, - { - "text": ":", - "kind": "punctuation" - }, - { - "text": " ", - "kind": "space" - }, - { - "text": "Bar", - "kind": "className" - } - ], - "documentation": [], - "tags": [ - { - "name": "inheritDoc", - "text": "" - } - ] - } - }, - { - "marker": { - "fileName": "/tests/cases/fourslash/inheritDoc.ts", - "position": 846 - }, - "quickInfo": { - "kind": "method", - "kindModifiers": "", - "textSpan": { - "start": 839, - "length": 7 - }, - "displayParts": [ - { - "text": "(", - "kind": "punctuation" - }, - { - "text": "method", - "kind": "text" - }, - { - "text": ")", - "kind": "punctuation" - }, - { - "text": " ", - "kind": "space" - }, - { - "text": "Bar", - "kind": "className" - }, - { - "text": ".", - "kind": "punctuation" - }, - { - "text": "method2", - "kind": "methodName" - }, - { - "text": "(", - "kind": "punctuation" - }, - { - "text": ")", - "kind": "punctuation" - }, - { - "text": ":", - "kind": "punctuation" - }, - { - "text": " ", - "kind": "space" - }, - { - "text": "void", - "kind": "keyword" - } - ], - "documentation": [ - { - "text": "Foo#method2 documentation", - "kind": "text" - } - ], - "tags": [ - { - "name": "inheritDoc", - "text": "" - } - ] - } - }, - { - "marker": { - "fileName": "/tests/cases/fourslash/inheritDoc.ts", - "position": 861 - }, - "quickInfo": { - "kind": "method", - "kindModifiers": "static", - "textSpan": { - "start": 854, - "length": 7 - }, - "displayParts": [ - { - "text": "(", - "kind": "punctuation" - }, - { - "text": "method", - "kind": "text" - }, - { - "text": ")", - "kind": "punctuation" - }, - { - "text": " ", - "kind": "space" - }, - { - "text": "Bar", - "kind": "className" - }, - { - "text": ".", - "kind": "punctuation" - }, - { - "text": "method1", - "kind": "methodName" - }, - { - "text": "(", - "kind": "punctuation" - }, - { - "text": ")", - "kind": "punctuation" - }, - { - "text": ":", - "kind": "punctuation" - }, - { - "text": " ", - "kind": "space" - }, - { - "text": "void", - "kind": "keyword" - } - ], - "documentation": [], - "tags": [ - { - "name": "inheritDoc", - "text": "" - } - ] - } - }, - { - "marker": { - "fileName": "/tests/cases/fourslash/inheritDoc.ts", - "position": 887 - }, - "quickInfo": { - "kind": "property", - "kindModifiers": "", - "textSpan": { - "start": 878, - "length": 9 - }, - "displayParts": [ - { - "text": "(", - "kind": "punctuation" - }, - { - "text": "property", - "kind": "text" - }, - { - "text": ")", - "kind": "punctuation" - }, - { - "text": " ", - "kind": "space" - }, - { - "text": "Bar", - "kind": "className" - }, - { - "text": ".", - "kind": "punctuation" - }, - { - "text": "property1", - "kind": "propertyName" - }, - { - "text": ":", - "kind": "punctuation" - }, - { - "text": " ", - "kind": "space" - }, - { - "text": "string", - "kind": "keyword" - } - ], - "documentation": [ - { - "text": "Foo#property1 documentation", - "kind": "text" - } - ], - "tags": [ - { - "name": "inheritDoc", - "text": "" - } - ] - } - }, - { - "marker": { - "fileName": "/tests/cases/fourslash/inheritDoc.ts", - "position": 911 - }, - "quickInfo": { - "kind": "property", - "kindModifiers": "", - "textSpan": { - "start": 902, - "length": 9 - }, - "displayParts": [ - { - "text": "(", - "kind": "punctuation" - }, - { - "text": "property", - "kind": "text" - }, - { - "text": ")", - "kind": "punctuation" - }, - { - "text": " ", - "kind": "space" - }, - { - "text": "Bar", - "kind": "className" - }, - { - "text": ".", - "kind": "punctuation" - }, - { - "text": "property2", - "kind": "propertyName" - }, - { - "text": ":", - "kind": "punctuation" - }, - { - "text": " ", - "kind": "space" - }, - { - "text": "object", - "kind": "keyword" - } - ], - "documentation": [ - { - "text": "Baz#property2 documentation ", - "kind": "text" - } - ], - "tags": [ - { - "name": "inheritDoc", - "text": "" - } - ] - } - } -] \ No newline at end of file diff --git a/tests/cases/fourslash/jsDocInheritDoc.ts b/tests/cases/fourslash/jsDocInheritDoc.ts index 44ebb93a066c4..ff636f9bbc91b 100644 --- a/tests/cases/fourslash/jsDocInheritDoc.ts +++ b/tests/cases/fourslash/jsDocInheritDoc.ts @@ -21,7 +21,9 @@ ////interface Baz { //// /** Baz#property1 documentation */ //// property1: string; -//// /** Baz#property2 documentation */ +//// /** +//// * Baz#property2 documentation +//// */ //// property2: object; ////} ////class Bar extends Foo implements Baz { @@ -33,11 +35,9 @@ //// } //// /** @inheritDoc */ //// static method1() {} -//// /** @inheritDoc */ //// method2() {} //// /** @inheritDoc */ //// property1: string; -//// /** @inheritDoc */ //// property2: object; ////} ////const b = new Bar/*1*/(5); @@ -46,4 +46,8 @@ ////const p1 = b.property1/*4*/; ////const p2 = b.property2/*5*/; -verify.baselineQuickInfo(); +verify.quickInfoAt("1", "constructor Bar(value: number): Bar", undefined); // constructors aren't actually inherited +verify.quickInfoAt("2", "(method) Bar.method2(): void", "Foo#method2 documentation"); +verify.quickInfoAt("3", "(method) Bar.method1(): void", undefined); // statics aren't actually inherited +verify.quickInfoAt("4", "(property) Bar.property1: string", "Foo#property1 documentation"); +verify.quickInfoAt("5", "(property) Bar.property2: object", "Baz#property2 documentation"); From 7e6c026b9c2f26d4ab7cc0f88545add9b9749138 Mon Sep 17 00:00:00 2001 From: Sean Barag Date: Thu, 26 Oct 2017 16:24:13 -0700 Subject: [PATCH 4/8] Concatenate inherited and local docs when @inheritDoc is present --- src/compiler/parser.ts | 10 --- src/compiler/types.ts | 9 +- src/services/services.ts | 87 +++++++++---------- tests/baselines/reference/api/typescript.d.ts | 22 ++--- tests/cases/fourslash/commentsInheritance.ts | 10 +-- tests/cases/fourslash/jsDocInheritDoc.ts | 10 ++- 6 files changed, 65 insertions(+), 83 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 03548f83f20a6..648f3e30d1c24 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -6426,9 +6426,6 @@ namespace ts { case "constructor": tag = parseClassTag(atToken, tagName); break; - case "inheritDoc": - tag = parseInheritDocTag(atToken, tagName); - break; case "arg": case "argument": case "param": @@ -6660,13 +6657,6 @@ namespace ts { return finishNode(result); } - function parseInheritDocTag(atToken: AtToken, tagName: Identifier): JSDocInheritDocTag { - const tag = createNode(SyntaxKind.JSDocInheritDocTag, atToken.pos); - tag.atToken = atToken; - tag.tagName = tagName; - return finishNode(tag); - } - function parseAugmentsTag(atToken: AtToken, tagName: Identifier): JSDocAugmentsTag { const result = createNode(SyntaxKind.JSDocAugmentsTag, atToken.pos); result.atToken = atToken; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d1c88cf5887a1..549c0f8d52fa5 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -375,7 +375,6 @@ namespace ts { JSDocTemplateTag, JSDocTypedefTag, JSDocPropertyTag, - JSDocInheritDocTag, // Synthesized list SyntaxList, @@ -417,9 +416,9 @@ namespace ts { LastBinaryOperator = CaretEqualsToken, FirstNode = QualifiedName, FirstJSDocNode = JSDocTypeExpression, - LastJSDocNode = JSDocInheritDocTag, + LastJSDocNode = JSDocPropertyTag, FirstJSDocTagNode = JSDocTag, - LastJSDocTagNode = JSDocInheritDocTag + LastJSDocTagNode = JSDocPropertyTag } export const enum NodeFlags { @@ -2198,10 +2197,6 @@ namespace ts { kind: SyntaxKind.JSDocClassTag; } - export interface JSDocInheritDocTag extends JSDocTag { - kind: SyntaxKind.JSDocInheritDocTag; - } - export interface JSDocTemplateTag extends JSDocTag { kind: SyntaxKind.JSDocTemplateTag; typeParameters: NodeArray; diff --git a/src/services/services.ts b/src/services/services.ts index bc859f0c29c40..9849f27c2ea0b 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -350,11 +350,14 @@ namespace ts { if (this.declarations) { this.documentationComment = JsDoc.getJsDocCommentsFromDeclarations(this.declarations); - if (this.documentationComment.length === 0 || this.declarations.some(dec => hasJSDocInheritDocTag(dec))) { + if (this.documentationComment.length === 0 || this.declarations.some(hasJSDocInheritDocTag)) { for (const declaration of this.declarations) { const inheritedDocs = findInheritedJSDocComments(declaration, this.getName(), checker); if (inheritedDocs.length > 0) { - this.documentationComment = inheritedDocs; + if (this.documentationComment.length > 0) { + inheritedDocs.push(ts.lineBreakPart()); + } + this.documentationComment = concatenate(inheritedDocs, this.documentationComment); break; } } @@ -494,8 +497,15 @@ namespace ts { if (this.declaration) { this.documentationComment = JsDoc.getJsDocCommentsFromDeclarations([this.declaration]); - if (this.documentationComment.length === 0) { - this.documentationComment = findInheritedJSDocComments(this.declaration, this.declaration.symbol.getName(), this.checker); + if (this.documentationComment.length === 0 || hasJSDocInheritDocTag(this.declaration)) { + const inheritedDocs = findInheritedJSDocComments(this.declaration, this.declaration.symbol.getName(), this.checker); + if (this.documentationComment.length > 0) { + inheritedDocs.push(ts.lineBreakPart()); + } + this.documentationComment = concatenate( + inheritedDocs, + this.documentationComment + ); } } else { @@ -533,51 +543,38 @@ namespace ts { * @returns A filled array of documentation comments if any were found, otherwise an empty array. */ function findInheritedJSDocComments(declaration: Declaration, propertyName: string, typeChecker: TypeChecker): SymbolDisplayPart[] { - let documentationComment: SymbolDisplayPart[] = []; + let foundDocs = false; + return flatMap(getAllSuperTypeNodes(declaration), superTypeNode => { + if (foundDocs) { + return emptyArray; + } + const superType = typeChecker.getTypeAtLocation(superTypeNode); + if (!superType) { + return emptyArray; + } + const baseProperty = typeChecker.getPropertyOfType(superType, propertyName); + if (!baseProperty) { + return emptyArray; + } + const inheritedDocs = baseProperty.getDocumentationComment(typeChecker); + foundDocs = inheritedDocs.length > 0; + return inheritedDocs; + }); + } + /** + * Finds and returns the `TypeNode` for all super classes and implemented interfaces given a declaration. + * @param declaration The possibly-inherited declaration. + * @returns A filled array of `TypeNode`s containing all super classes and implemented interfaces if any exist, otherwise an empty array. + */ + function getAllSuperTypeNodes(declaration: Declaration): ReadonlyArray { const container = declaration.parent; - if (!container || (!isClassDeclaration(container) && !isInterfaceDeclaration(container))) { - return documentationComment; - } - else { - const baseTypeNode = getClassExtendsHeritageClauseElement(container); - if (!baseTypeNode) { - return documentationComment; - } - - const baseType = typeChecker.getTypeAtLocation(baseTypeNode); - if (!baseType) { - return documentationComment; - } - - // First check superclasses for a property of the same name - let baseProperty = typeChecker.getPropertyOfType(baseType, propertyName); - let baseDocs = baseProperty ? baseProperty.getDocumentationComment(typeChecker) : []; - if (baseDocs.length > 0) { - documentationComment = baseDocs; - } - - // If there's nothing in the superclass, walk through implemented interfaces left-to-right - if (documentationComment.length === 0) { - const implementedInterfaces = map( - getClassImplementsHeritageClauseElements(container as ClassLikeDeclaration), - interfaceNode => typeChecker.getTypeAtLocation(interfaceNode) - ) || []; - - for (const implementedInterface of implementedInterfaces) { - // Use the docs from the first implemented interface to have this property and documentation - baseProperty = typeChecker.getPropertyOfType(implementedInterface, propertyName); - baseDocs = baseProperty ? baseProperty.getDocumentationComment(typeChecker) : []; - if (baseDocs.length > 0) { - documentationComment = baseDocs; - break; - } - } - } - - return documentationComment; + return emptyArray; } + const extended = getClassExtendsHeritageClauseElement(container); + const types = extended ? [extended] : emptyArray; + return isClassLike(container) ? concatenate(types, getClassImplementsHeritageClauseElements(container)) : types; } class SourceFileObject extends NodeObject implements SourceFile { diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 8ef34bd28519f..3a2ec3163759f 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -349,14 +349,13 @@ declare namespace ts { JSDocTemplateTag = 286, JSDocTypedefTag = 287, JSDocPropertyTag = 288, - JSDocInheritDocTag = 289, - SyntaxList = 290, - NotEmittedStatement = 291, - PartiallyEmittedExpression = 292, - CommaListExpression = 293, - MergeDeclarationMarker = 294, - EndOfDeclarationMarker = 295, - Count = 296, + SyntaxList = 289, + NotEmittedStatement = 290, + PartiallyEmittedExpression = 291, + CommaListExpression = 292, + MergeDeclarationMarker = 293, + EndOfDeclarationMarker = 294, + Count = 295, FirstAssignment = 58, LastAssignment = 70, FirstCompoundAssignment = 59, @@ -383,9 +382,9 @@ declare namespace ts { LastBinaryOperator = 70, FirstNode = 143, FirstJSDocNode = 270, - LastJSDocNode = 289, + LastJSDocNode = 288, FirstJSDocTagNode = 280, - LastJSDocTagNode = 289, + LastJSDocTagNode = 288, } enum NodeFlags { None = 0, @@ -1473,9 +1472,6 @@ declare namespace ts { interface JSDocClassTag extends JSDocTag { kind: SyntaxKind.JSDocClassTag; } - interface JSDocInheritDocTag extends JSDocTag { - kind: SyntaxKind.JSDocInheritDocTag; - } interface JSDocTemplateTag extends JSDocTag { kind: SyntaxKind.JSDocTemplateTag; typeParameters: NodeArray; diff --git a/tests/cases/fourslash/commentsInheritance.ts b/tests/cases/fourslash/commentsInheritance.ts index 7afe18f400ce4..985c55c7947bb 100644 --- a/tests/cases/fourslash/commentsInheritance.ts +++ b/tests/cases/fourslash/commentsInheritance.ts @@ -263,8 +263,8 @@ verify.quickInfos({ }); goTo.marker('6'); -verify.completionListContains("i1_p1", "(property) c1.i1_p1: number", ""); -verify.completionListContains("i1_f1", "(method) c1.i1_f1(): void", ""); +verify.completionListContains("i1_p1", "(property) c1.i1_p1: number", "i1_p1"); +verify.completionListContains("i1_f1", "(method) c1.i1_f1(): void", "i1_f1"); verify.completionListContains("i1_l1", "(property) c1.i1_l1: () => void", ""); verify.completionListContains("i1_nc_p1", "(property) c1.i1_nc_p1: number", ""); 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" verify.completionListContains("nc_f1", "(method) c1.nc_f1(): void", "c1_nc_f1"); verify.completionListContains("nc_l1", "(property) c1.nc_l1: () => void", ""); goTo.marker('7'); -verify.currentSignatureHelpDocCommentIs(""); +verify.currentSignatureHelpDocCommentIs("i1_f1"); goTo.marker('8'); verify.currentSignatureHelpDocCommentIs(""); goTo.marker('9'); @@ -294,7 +294,7 @@ verify.currentSignatureHelpDocCommentIs(""); verify.quickInfos({ "6iq": "var c1_i: c1", - "7q": "(method) c1.i1_f1(): void", + "7q": ["(method) c1.i1_f1(): void", "i1_f1"], "8q": "(method) c1.i1_nc_f1(): void", "9q": ["(method) c1.f1(): void", "c1_f1"], "10q": ["(method) c1.nc_f1(): void", "c1_nc_f1"], @@ -515,7 +515,7 @@ verify.quickInfos({ "39q": ["(method) i2.f1(): void", "i2 f1"], "40q": "(method) i2.nc_f1(): void", "l37q": "(property) i2.i2_l1: () => void", - "l38q": "(property) i2.i2_nc_l1: () => void", + "l38q": "(property) i2.i2_nc_l1: () => void", "l39q": "(property) i2.l1: () => void", "l40q": "(property) i2.nc_l1: () => void", }); diff --git a/tests/cases/fourslash/jsDocInheritDoc.ts b/tests/cases/fourslash/jsDocInheritDoc.ts index ff636f9bbc91b..8a19bd0c14e26 100644 --- a/tests/cases/fourslash/jsDocInheritDoc.ts +++ b/tests/cases/fourslash/jsDocInheritDoc.ts @@ -38,6 +38,10 @@ //// method2() {} //// /** @inheritDoc */ //// property1: string; +//// /** +//// * Bar#property2 +//// * @inheritDoc +//// */ //// property2: object; ////} ////const b = new Bar/*1*/(5); @@ -47,7 +51,7 @@ ////const p2 = b.property2/*5*/; verify.quickInfoAt("1", "constructor Bar(value: number): Bar", undefined); // constructors aren't actually inherited -verify.quickInfoAt("2", "(method) Bar.method2(): void", "Foo#method2 documentation"); +verify.quickInfoAt("2", "(method) Bar.method2(): void", "Foo#method2 documentation"); // use inherited docs only verify.quickInfoAt("3", "(method) Bar.method1(): void", undefined); // statics aren't actually inherited -verify.quickInfoAt("4", "(property) Bar.property1: string", "Foo#property1 documentation"); -verify.quickInfoAt("5", "(property) Bar.property2: object", "Baz#property2 documentation"); +verify.quickInfoAt("4", "(property) Bar.property1: string", "Foo#property1 documentation"); // use inherited docs only +verify.quickInfoAt("5", "(property) Bar.property2: object", "Baz#property2 documentation\nBar#property2"); // include local and inherited docs From 1a1ff5524a895397de7ad50f59938affb861cec5 Mon Sep 17 00:00:00 2001 From: Sean Barag Date: Thu, 26 Oct 2017 16:34:57 -0700 Subject: [PATCH 5/8] Make typeChecker param explicitly `TypeChecker | undefined` --- src/services/types.ts | 4 ++-- tests/baselines/reference/api/typescript.d.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/services/types.ts b/src/services/types.ts index 8f0fff62d6d9e..63138b33a1e89 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -32,7 +32,7 @@ namespace ts { getEscapedName(): __String; getName(): string; getDeclarations(): Declaration[] | undefined; - getDocumentationComment(typeChecker?: TypeChecker): SymbolDisplayPart[]; + getDocumentationComment(typeChecker: TypeChecker | undefined): SymbolDisplayPart[]; getJsDocTags(): JSDocTagInfo[]; } @@ -55,7 +55,7 @@ namespace ts { getTypeParameters(): TypeParameter[] | undefined; getParameters(): Symbol[]; getReturnType(): Type; - getDocumentationComment(typeChecker?: TypeChecker): SymbolDisplayPart[]; + getDocumentationComment(typeChecker: TypeChecker | undefined): SymbolDisplayPart[]; getJsDocTags(): JSDocTagInfo[]; } diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 3a2ec3163759f..0b1d46490b64f 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3817,7 +3817,7 @@ declare namespace ts { getEscapedName(): __String; getName(): string; getDeclarations(): Declaration[] | undefined; - getDocumentationComment(typeChecker?: TypeChecker): SymbolDisplayPart[]; + getDocumentationComment(typeChecker: TypeChecker | undefined): SymbolDisplayPart[]; getJsDocTags(): JSDocTagInfo[]; } interface Type { @@ -3838,7 +3838,7 @@ declare namespace ts { getTypeParameters(): TypeParameter[] | undefined; getParameters(): Symbol[]; getReturnType(): Type; - getDocumentationComment(typeChecker?: TypeChecker): SymbolDisplayPart[]; + getDocumentationComment(typeChecker: TypeChecker | undefined): SymbolDisplayPart[]; getJsDocTags(): JSDocTagInfo[]; } interface SourceFile { From 3e1c39fd1098d5cca41c214466a71d6e033b832f Mon Sep 17 00:00:00 2001 From: Sean Barag Date: Thu, 26 Oct 2017 20:00:19 -0700 Subject: [PATCH 6/8] Re-accept baseline after switch to explicit `| undefined` --- tests/baselines/reference/api/tsserverlibrary.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 7e89dfb5a66ab..746b6388ab77b 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -3821,7 +3821,7 @@ declare namespace ts { getEscapedName(): __String; getName(): string; getDeclarations(): Declaration[] | undefined; - getDocumentationComment(typeChecker?: TypeChecker): SymbolDisplayPart[]; + getDocumentationComment(typeChecker: TypeChecker | undefined): SymbolDisplayPart[]; getJsDocTags(): JSDocTagInfo[]; } interface Type { @@ -3842,7 +3842,7 @@ declare namespace ts { getTypeParameters(): TypeParameter[] | undefined; getParameters(): Symbol[]; getReturnType(): Type; - getDocumentationComment(typeChecker?: TypeChecker): SymbolDisplayPart[]; + getDocumentationComment(typeChecker: TypeChecker | undefined): SymbolDisplayPart[]; getJsDocTags(): JSDocTagInfo[]; } interface SourceFile { From 733ece2443b4d7e5f3203a3c96c7fe3b1492c331 Mon Sep 17 00:00:00 2001 From: Sean Barag Date: Fri, 27 Oct 2017 08:58:26 -0700 Subject: [PATCH 7/8] Update APISample_jsodc.ts to match new getDocumentationComment signature --- src/services/services.ts | 18 ++++++++++-------- tests/baselines/reference/APISample_jsdoc.js | 4 ++-- tests/cases/compiler/APISample_jsdoc.ts | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 9849f27c2ea0b..37dd2a183053d 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -345,20 +345,22 @@ namespace ts { return this.declarations; } - getDocumentationComment(checker: TypeChecker): SymbolDisplayPart[] { + getDocumentationComment(checker: TypeChecker | undefined): SymbolDisplayPart[] { if (this.documentationComment === undefined) { if (this.declarations) { this.documentationComment = JsDoc.getJsDocCommentsFromDeclarations(this.declarations); if (this.documentationComment.length === 0 || this.declarations.some(hasJSDocInheritDocTag)) { - for (const declaration of this.declarations) { - const inheritedDocs = findInheritedJSDocComments(declaration, this.getName(), checker); - if (inheritedDocs.length > 0) { - if (this.documentationComment.length > 0) { - inheritedDocs.push(ts.lineBreakPart()); + if (checker) { + for (const declaration of this.declarations) { + const inheritedDocs = findInheritedJSDocComments(declaration, this.getName(), checker); + if (inheritedDocs.length > 0) { + if (this.documentationComment.length > 0) { + inheritedDocs.push(ts.lineBreakPart()); + } + this.documentationComment = concatenate(inheritedDocs, this.documentationComment); + break; } - this.documentationComment = concatenate(inheritedDocs, this.documentationComment); - break; } } } diff --git a/tests/baselines/reference/APISample_jsdoc.js b/tests/baselines/reference/APISample_jsdoc.js index c74e188f38b22..f28b16d88b60c 100644 --- a/tests/baselines/reference/APISample_jsdoc.js +++ b/tests/baselines/reference/APISample_jsdoc.js @@ -21,7 +21,7 @@ function parseCommentsIntoDefinition(this: any, } // the comments for a symbol - let comments = symbol.getDocumentationComment(); + let comments = symbol.getDocumentationComment(undefined); if (comments.length) { definition.description = comments.map(comment => comment.kind === "lineBreak" ? comment.text : comment.text.trim().replace(/\r\n/g, "\n")).join(""); @@ -131,7 +131,7 @@ function parseCommentsIntoDefinition(symbol, definition, otherAnnotations) { return; } // the comments for a symbol - var comments = symbol.getDocumentationComment(); + var comments = symbol.getDocumentationComment(undefined); if (comments.length) { definition.description = comments.map(function (comment) { return comment.kind === "lineBreak" ? comment.text : comment.text.trim().replace(/\r\n/g, "\n"); }).join(""); } diff --git a/tests/cases/compiler/APISample_jsdoc.ts b/tests/cases/compiler/APISample_jsdoc.ts index 2f4e08931d619..d40673f1d9ed0 100644 --- a/tests/cases/compiler/APISample_jsdoc.ts +++ b/tests/cases/compiler/APISample_jsdoc.ts @@ -25,7 +25,7 @@ function parseCommentsIntoDefinition(this: any, } // the comments for a symbol - let comments = symbol.getDocumentationComment(); + let comments = symbol.getDocumentationComment(undefined); if (comments.length) { definition.description = comments.map(comment => comment.kind === "lineBreak" ? comment.text : comment.text.trim().replace(/\r\n/g, "\n")).join(""); From 5ba044aa58eceaa3ae33dc5a48bdf4c915deee03 Mon Sep 17 00:00:00 2001 From: Sean Barag Date: Wed, 1 Nov 2017 16:40:14 -0700 Subject: [PATCH 8/8] Re-accept baselines after rebasing --- .../reference/api/tsserverlibrary.d.ts | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 746b6388ab77b..4f46b4f3be4b9 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -349,14 +349,13 @@ declare namespace ts { JSDocTemplateTag = 286, JSDocTypedefTag = 287, JSDocPropertyTag = 288, - JSDocInheritDocTag = 289, - SyntaxList = 290, - NotEmittedStatement = 291, - PartiallyEmittedExpression = 292, - CommaListExpression = 293, - MergeDeclarationMarker = 294, - EndOfDeclarationMarker = 295, - Count = 296, + SyntaxList = 289, + NotEmittedStatement = 290, + PartiallyEmittedExpression = 291, + CommaListExpression = 292, + MergeDeclarationMarker = 293, + EndOfDeclarationMarker = 294, + Count = 295, FirstAssignment = 58, LastAssignment = 70, FirstCompoundAssignment = 59, @@ -383,9 +382,9 @@ declare namespace ts { LastBinaryOperator = 70, FirstNode = 143, FirstJSDocNode = 270, - LastJSDocNode = 289, + LastJSDocNode = 288, FirstJSDocTagNode = 280, - LastJSDocTagNode = 289, + LastJSDocTagNode = 288, } enum NodeFlags { None = 0, @@ -1473,9 +1472,6 @@ declare namespace ts { interface JSDocClassTag extends JSDocTag { kind: SyntaxKind.JSDocClassTag; } - interface JSDocInheritDocTag extends JSDocTag { - kind: SyntaxKind.JSDocInheritDocTag; - } interface JSDocTemplateTag extends JSDocTag { kind: SyntaxKind.JSDocTemplateTag; typeParameters: NodeArray;