From f1dca6a61f577bfb83dbc91d2e064057880df209 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Mon, 4 Jan 2021 17:33:26 +0200 Subject: [PATCH] fix(42019): include the jsdoc tags from the base declaration (#42098) --- src/compiler/utilities.ts | 2 +- src/services/services.ts | 36 ++--- .../reference/quickInfoJsDocTags1.baseline | 110 ++++++++++++++ .../reference/quickInfoJsDocTags3.baseline | 94 ++++++++++++ .../reference/quickInfoJsDocTags4.baseline | 134 +++++++++++++++++ .../reference/quickInfoJsDocTags5.js | 134 +++++++++++++++++ .../reference/quickInfoJsDocTags6.js | 137 ++++++++++++++++++ tests/cases/fourslash/quickInfoJsDocTags.ts | 18 --- tests/cases/fourslash/quickInfoJsDocTags1.ts | 25 +++- tests/cases/fourslash/quickInfoJsDocTags2.ts | 7 + tests/cases/fourslash/quickInfoJsDocTags3.ts | 22 +++ tests/cases/fourslash/quickInfoJsDocTags4.ts | 25 ++++ tests/cases/fourslash/quickInfoJsDocTags5.ts | 28 ++++ tests/cases/fourslash/quickInfoJsDocTags6.ts | 29 ++++ 14 files changed, 758 insertions(+), 43 deletions(-) create mode 100644 tests/baselines/reference/quickInfoJsDocTags1.baseline create mode 100644 tests/baselines/reference/quickInfoJsDocTags3.baseline create mode 100644 tests/baselines/reference/quickInfoJsDocTags4.baseline create mode 100644 tests/baselines/reference/quickInfoJsDocTags5.js create mode 100644 tests/baselines/reference/quickInfoJsDocTags6.js delete mode 100644 tests/cases/fourslash/quickInfoJsDocTags.ts create mode 100644 tests/cases/fourslash/quickInfoJsDocTags2.ts create mode 100644 tests/cases/fourslash/quickInfoJsDocTags3.ts create mode 100644 tests/cases/fourslash/quickInfoJsDocTags4.ts create mode 100644 tests/cases/fourslash/quickInfoJsDocTags5.ts create mode 100644 tests/cases/fourslash/quickInfoJsDocTags6.ts diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 12b815995cf..a50ca22095d 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2992,7 +2992,7 @@ namespace ts { } export function getEffectiveImplementsTypeNodes(node: ClassLikeDeclaration): undefined | readonly ExpressionWithTypeArguments[]{ - if(isInJSFile(node)) { + if (isInJSFile(node)) { return getJSDocImplementsTags(node).map(n => n.class); } else { diff --git a/src/services/services.ts b/src/services/services.ts index e2ef969a7b2..3b4ce096834 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -550,9 +550,8 @@ namespace ts { getJsDocTags(): JSDocTagInfo[] { if (this.jsDocTags === undefined) { - this.jsDocTags = this.declaration ? JsDoc.getJsDocTagsFromDeclarations([this.declaration]) : []; + this.jsDocTags = this.declaration ? getJsDocTags([this.declaration], this.checker) : []; } - return this.jsDocTags; } } @@ -566,13 +565,26 @@ namespace ts { return getJSDocTags(node).some(tag => tag.tagName.text === "inheritDoc"); } + function getJsDocTags(declarations: Declaration[], checker: TypeChecker): JSDocTagInfo[] { + let tags = JsDoc.getJsDocTagsFromDeclarations(declarations); + if (tags.length === 0 || declarations.some(hasJSDocInheritDocTag)) { + forEachUnique(declarations, declaration => { + const inheritedTags = findBaseOfDeclaration(checker, declaration, symbol => symbol.getJsDocTags()); + if (inheritedTags) { + tags = [...inheritedTags, ...tags]; + } + }); + } + return tags; + } + function getDocumentationComment(declarations: readonly Declaration[] | undefined, checker: TypeChecker | undefined): SymbolDisplayPart[] { if (!declarations) return emptyArray; let doc = JsDoc.getJsDocCommentsFromDeclarations(declarations); - if (doc.length === 0 || declarations.some(hasJSDocInheritDocTag)) { + if (checker && (doc.length === 0 || declarations.some(hasJSDocInheritDocTag))) { forEachUnique(declarations, declaration => { - const inheritedDocs = findInheritedJSDocComments(declaration, declaration.symbol.name, checker!); // TODO: GH#18217 + const inheritedDocs = findBaseOfDeclaration(checker, declaration, symbol => symbol.getDocumentationComment(checker)); // TODO: GH#16312 Return a ReadonlyArray, avoid copying inheritedDocs if (inheritedDocs) doc = doc.length === 0 ? inheritedDocs.slice() : inheritedDocs.concat(lineBreakPart(), doc); }); @@ -580,20 +592,10 @@ namespace ts { return doc; } - /** - * 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): readonly SymbolDisplayPart[] | undefined { + function findBaseOfDeclaration(checker: TypeChecker, declaration: Declaration, cb: (symbol: Symbol) => T[]): T[] | undefined { return firstDefined(declaration.parent ? getAllSuperTypeNodes(declaration.parent) : emptyArray, superTypeNode => { - const superType = typeChecker.getTypeAtLocation(superTypeNode); - const baseProperty = superType && typeChecker.getPropertyOfType(superType, propertyName); - const inheritedDocs = baseProperty && baseProperty.getDocumentationComment(typeChecker); - return inheritedDocs && inheritedDocs.length ? inheritedDocs : undefined; + const symbol = checker.getPropertyOfType(checker.getTypeAtLocation(superTypeNode), declaration.symbol.name); + return symbol ? cb(symbol) : undefined; }); } diff --git a/tests/baselines/reference/quickInfoJsDocTags1.baseline b/tests/baselines/reference/quickInfoJsDocTags1.baseline new file mode 100644 index 00000000000..dfb5b740e3c --- /dev/null +++ b/tests/baselines/reference/quickInfoJsDocTags1.baseline @@ -0,0 +1,110 @@ +[ + { + "marker": { + "fileName": "/tests/cases/fourslash/quickInfoJsDocTags1.ts", + "position": 298 + }, + "quickInfo": { + "kind": "function", + "kindModifiers": "", + "textSpan": { + "start": 298, + "length": 3 + }, + "displayParts": [ + { + "text": "function", + "kind": "keyword" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "foo", + "kind": "functionName" + }, + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "x", + "kind": "parameterName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "any", + "kind": "keyword" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "void", + "kind": "keyword" + } + ], + "documentation": [ + { + "text": "Doc", + "kind": "text" + } + ], + "tags": [ + { + "name": "author", + "text": "Me " + }, + { + "name": "augments", + "text": "C Augments it" + }, + { + "name": "template", + "text": "T A template" + }, + { + "name": "type", + "text": "{number | string} A type" + }, + { + "name": "typedef", + "text": "NumOrStr" + }, + { + "name": "property", + "text": "{number} x The prop" + }, + { + "name": "param", + "text": "x The param" + }, + { + "name": "returns", + "text": "The result" + }, + { + "name": "see", + "text": "x (the parameter)" + } + ] + } + } +] \ No newline at end of file diff --git a/tests/baselines/reference/quickInfoJsDocTags3.baseline b/tests/baselines/reference/quickInfoJsDocTags3.baseline new file mode 100644 index 00000000000..454a54169bf --- /dev/null +++ b/tests/baselines/reference/quickInfoJsDocTags3.baseline @@ -0,0 +1,94 @@ +[ + { + "marker": { + "fileName": "/tests/cases/fourslash/quickInfoJsDocTags3.ts", + "position": 290 + }, + "quickInfo": { + "kind": "method", + "kindModifiers": "", + "textSpan": { + "start": 290, + "length": 6 + }, + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "method", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "Bar", + "kind": "className" + }, + { + "text": ".", + "kind": "punctuation" + }, + { + "text": "method", + "kind": "methodName" + }, + { + "text": "(", + "kind": "punctuation" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "void", + "kind": "keyword" + } + ], + "documentation": [ + { + "text": "comment", + "kind": "text" + } + ], + "tags": [ + { + "name": "author", + "text": "Me " + }, + { + "name": "see", + "text": "x (the parameter)" + }, + { + "name": "param", + "text": "x - x comment" + }, + { + "name": "param", + "text": "y - y comment" + }, + { + "name": "throws", + "text": "{Error} comment" + } + ] + } + } +] \ No newline at end of file diff --git a/tests/baselines/reference/quickInfoJsDocTags4.baseline b/tests/baselines/reference/quickInfoJsDocTags4.baseline new file mode 100644 index 00000000000..45d4cf54d33 --- /dev/null +++ b/tests/baselines/reference/quickInfoJsDocTags4.baseline @@ -0,0 +1,134 @@ +[ + { + "marker": { + "fileName": "/tests/cases/fourslash/quickInfoJsDocTags4.ts", + "position": 309 + }, + "quickInfo": { + "kind": "method", + "kindModifiers": "", + "textSpan": { + "start": 309, + "length": 6 + }, + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "method", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "Bar", + "kind": "className" + }, + { + "text": ".", + "kind": "punctuation" + }, + { + "text": "method", + "kind": "methodName" + }, + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "x", + "kind": "parameterName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "number", + "kind": "keyword" + }, + { + "text": ",", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "y", + "kind": "parameterName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "number", + "kind": "keyword" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "number", + "kind": "keyword" + } + ], + "documentation": [ + { + "text": "comment", + "kind": "text" + } + ], + "tags": [ + { + "name": "author", + "text": "Me " + }, + { + "name": "see", + "text": "x (the parameter)" + }, + { + "name": "param", + "text": "x - x comment" + }, + { + "name": "param", + "text": "y - y comment" + }, + { + "name": "returns", + "text": "The result" + } + ] + } + } +] \ No newline at end of file diff --git a/tests/baselines/reference/quickInfoJsDocTags5.js b/tests/baselines/reference/quickInfoJsDocTags5.js new file mode 100644 index 00000000000..f2bfaa7d4e8 --- /dev/null +++ b/tests/baselines/reference/quickInfoJsDocTags5.js @@ -0,0 +1,134 @@ +[ + { + "marker": { + "fileName": "/tests/cases/fourslash/quickInfoJsDocTags5.js", + "position": 285 + }, + "quickInfo": { + "kind": "method", + "kindModifiers": "", + "textSpan": { + "start": 285, + "length": 6 + }, + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "method", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "Bar", + "kind": "className" + }, + { + "text": ".", + "kind": "punctuation" + }, + { + "text": "method", + "kind": "methodName" + }, + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "x", + "kind": "parameterName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "any", + "kind": "keyword" + }, + { + "text": ",", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "y", + "kind": "parameterName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "any", + "kind": "keyword" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "number", + "kind": "keyword" + } + ], + "documentation": [ + { + "text": "comment", + "kind": "text" + } + ], + "tags": [ + { + "name": "author", + "text": "Me " + }, + { + "name": "see", + "text": "x (the parameter)" + }, + { + "name": "param", + "text": "x - x comment" + }, + { + "name": "param", + "text": "y - y comment" + }, + { + "name": "returns", + "text": "The result" + } + ] + } + } +] \ No newline at end of file diff --git a/tests/baselines/reference/quickInfoJsDocTags6.js b/tests/baselines/reference/quickInfoJsDocTags6.js new file mode 100644 index 00000000000..01acc30d9bc --- /dev/null +++ b/tests/baselines/reference/quickInfoJsDocTags6.js @@ -0,0 +1,137 @@ +[ + { + "marker": { + "fileName": "/tests/cases/fourslash/quickInfoJsDocTags6.js", + "position": 308 + }, + "quickInfo": { + "kind": "method", + "kindModifiers": "", + "textSpan": { + "start": 308, + "length": 6 + }, + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "method", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "Bar", + "kind": "className" + }, + { + "text": ".", + "kind": "punctuation" + }, + { + "text": "method", + "kind": "methodName" + }, + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "x", + "kind": "parameterName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "any", + "kind": "keyword" + }, + { + "text": ",", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "y", + "kind": "parameterName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "any", + "kind": "keyword" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "number", + "kind": "keyword" + } + ], + "documentation": [ + { + "text": "comment", + "kind": "text" + } + ], + "tags": [ + { + "name": "author", + "text": "Me " + }, + { + "name": "see", + "text": "x (the parameter)" + }, + { + "name": "param", + "text": "x - x comment" + }, + { + "name": "param", + "text": "y - y comment" + }, + { + "name": "returns", + "text": "The result" + }, + { + "name": "inheritDoc" + } + ] + } + } +] \ No newline at end of file diff --git a/tests/cases/fourslash/quickInfoJsDocTags.ts b/tests/cases/fourslash/quickInfoJsDocTags.ts deleted file mode 100644 index 107277b53a4..00000000000 --- a/tests/cases/fourslash/quickInfoJsDocTags.ts +++ /dev/null @@ -1,18 +0,0 @@ -/// - -// @Filename: quickInfoJsDocTags.ts -/////** -//// * Doc -//// * @author Me -//// * @augments {C} Augments it -//// * @template T A template -//// * @type {number | string} A type -//// * @typedef {number | string} NumOrStr -//// * @property {number} x The prop -//// * @param {number} x The param -//// * @returns The result -//// * @see x (the parameter) -//// */ -////function /**/foo(x) {} - -verify.baselineQuickInfo(); diff --git a/tests/cases/fourslash/quickInfoJsDocTags1.ts b/tests/cases/fourslash/quickInfoJsDocTags1.ts index a4af938ad8a..7e68cf23632 100644 --- a/tests/cases/fourslash/quickInfoJsDocTags1.ts +++ b/tests/cases/fourslash/quickInfoJsDocTags1.ts @@ -1,7 +1,18 @@ -/// - -// @Filename: quickInfoJsDocTags1.ts -/////** Doc */ -////const /**/x = 0; - -verify.quickInfoAt("", "const x: 0", "Doc"); +/// + +// @Filename: quickInfoJsDocTags1.ts +/////** +//// * Doc +//// * @author Me +//// * @augments {C} Augments it +//// * @template T A template +//// * @type {number | string} A type +//// * @typedef {number | string} NumOrStr +//// * @property {number} x The prop +//// * @param {number} x The param +//// * @returns The result +//// * @see x (the parameter) +//// */ +////function /**/foo(x) {} + +verify.baselineQuickInfo(); diff --git a/tests/cases/fourslash/quickInfoJsDocTags2.ts b/tests/cases/fourslash/quickInfoJsDocTags2.ts new file mode 100644 index 00000000000..e2cc88de7d3 --- /dev/null +++ b/tests/cases/fourslash/quickInfoJsDocTags2.ts @@ -0,0 +1,7 @@ +/// + +// @Filename: quickInfoJsDocTags2.ts +/////** Doc */ +////const /**/x = 0; + +verify.quickInfoAt("", "const x: 0", "Doc"); diff --git a/tests/cases/fourslash/quickInfoJsDocTags3.ts b/tests/cases/fourslash/quickInfoJsDocTags3.ts new file mode 100644 index 00000000000..7696d41360f --- /dev/null +++ b/tests/cases/fourslash/quickInfoJsDocTags3.ts @@ -0,0 +1,22 @@ +/// + +// @Filename: quickInfoJsDocTags3.ts +////interface Foo { +//// /** +//// * comment +//// * @author Me +//// * @see x (the parameter) +//// * @param {number} x - x comment +//// * @param {number} y - y comment +//// * @throws {Error} comment +//// */ +//// method(x: number, y: number): void; +////} +//// +////class Bar implements Foo { +//// /**/method(): void { +//// throw new Error("Method not implemented."); +//// } +////} + +verify.baselineQuickInfo(); diff --git a/tests/cases/fourslash/quickInfoJsDocTags4.ts b/tests/cases/fourslash/quickInfoJsDocTags4.ts new file mode 100644 index 00000000000..784555da112 --- /dev/null +++ b/tests/cases/fourslash/quickInfoJsDocTags4.ts @@ -0,0 +1,25 @@ +/// + +// @Filename: quickInfoJsDocTags4.ts +////class Foo { +//// /** +//// * comment +//// * @author Me +//// * @see x (the parameter) +//// * @param {number} x - x comment +//// * @param {number} y - y comment +//// * @returns The result +//// */ +//// method(x: number, y: number): number { +//// return x + y; +//// } +////} +//// +////class Bar extends Foo { +//// /**/method(x: number, y: number): number { +//// const res = super.method(x, y) + 100; +//// return res; +//// } +////} + +verify.baselineQuickInfo(); diff --git a/tests/cases/fourslash/quickInfoJsDocTags5.ts b/tests/cases/fourslash/quickInfoJsDocTags5.ts new file mode 100644 index 00000000000..a0dcaa24e51 --- /dev/null +++ b/tests/cases/fourslash/quickInfoJsDocTags5.ts @@ -0,0 +1,28 @@ +/// + +// @noEmit: true +// @allowJs: true + +// @Filename: quickInfoJsDocTags5.js +////class Foo { +//// /** +//// * comment +//// * @author Me +//// * @see x (the parameter) +//// * @param {number} x - x comment +//// * @param {number} y - y comment +//// * @returns The result +//// */ +//// method(x, y) { +//// return x + y; +//// } +////} +//// +////class Bar extends Foo { +//// /**/method(x, y) { +//// const res = super.method(x, y) + 100; +//// return res; +//// } +////} + +verify.baselineQuickInfo(); diff --git a/tests/cases/fourslash/quickInfoJsDocTags6.ts b/tests/cases/fourslash/quickInfoJsDocTags6.ts new file mode 100644 index 00000000000..ecdcc89bd17 --- /dev/null +++ b/tests/cases/fourslash/quickInfoJsDocTags6.ts @@ -0,0 +1,29 @@ +/// + +// @noEmit: true +// @allowJs: true + +// @Filename: quickInfoJsDocTags6.js +////class Foo { +//// /** +//// * comment +//// * @author Me +//// * @see x (the parameter) +//// * @param {number} x - x comment +//// * @param {number} y - y comment +//// * @returns The result +//// */ +//// method(x, y) { +//// return x + y; +//// } +////} +//// +////class Bar extends Foo { +//// /** @inheritDoc */ +//// /**/method(x, y) { +//// const res = super.method(x, y) + 100; +//// return res; +//// } +////} + +verify.baselineQuickInfo();