diff --git a/CHANGELOG.md b/CHANGELOG.md index 8624e486e..b352c5294 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,14 @@ - Added a `--suppressCommentWarningsInDeclarationFiles` option to disable warnings from parsing comments in declaration files, #2611. +- Improved comment discovery to more closely match TypeScript's discovery when getting comments + for members of interfaces/classes, #2084, #2545. ### Bug Fixes - The `text` non-highlighted language no longer causes warnings when rendering, #2610. +- If a comment on a method is inherited from a parent class, and the child class does not + use an `@param` tag from the parent, TypeDoc will no longer warn about the `@param` tag. ## v0.26.1 (2024-06-22) diff --git a/src/lib/converter/comments/discovery.ts b/src/lib/converter/comments/discovery.ts index cc5487b90..9811fb982 100644 --- a/src/lib/converter/comments/discovery.ts +++ b/src/lib/converter/comments/discovery.ts @@ -4,6 +4,7 @@ import { assertNever, type Logger } from "../../utils"; import { CommentStyle } from "../../utils/options/declaration"; import { nicePath } from "../../utils/paths"; import { ok } from "assert"; +import { filter, firstDefined } from "../../utils/array"; const variablePropertyKinds = [ ts.SyntaxKind.PropertyDeclaration, @@ -117,12 +118,13 @@ export interface DiscoveredComment { file: ts.SourceFile; ranges: ts.CommentRange[]; jsDoc: ts.JSDoc | undefined; + inheritedFromParentDeclaration: boolean; } export function discoverFileComments( node: ts.SourceFile, commentStyle: CommentStyle, -) { +): DiscoveredComment[] { const text = node.text; const comments = collectCommentRanges( @@ -138,6 +140,7 @@ export function discoverFileComments( file: node, ranges, jsDoc: findJsDocForComment(node, ranges), + inheritedFromParentDeclaration: false, }; }); } @@ -161,97 +164,142 @@ export function discoverNodeComment( file: node.getSourceFile(), ranges: selectedDocComment, jsDoc: findJsDocForComment(node, selectedDocComment), + inheritedFromParentDeclaration: false, }; } } +function checkCommentDeclarations( + commentNodes: ReadonlyArray<{ + node: ts.Node; + inheritedFromParentDeclaration: boolean; + }>, + reverse: boolean, + commentStyle: CommentStyle, +) { + const discovered: DiscoveredComment[] = []; + + for (const { node, inheritedFromParentDeclaration } of commentNodes) { + const text = node.getSourceFile().text; + + const comments = collectCommentRanges( + ts.getLeadingCommentRanges(text, node.pos), + ); + + if (reverse) { + comments.reverse(); + } + + const selectedDocComment = comments.find((ranges) => + permittedRange(text, ranges, commentStyle), + ); + + if (selectedDocComment) { + discovered.push({ + file: node.getSourceFile(), + ranges: selectedDocComment, + jsDoc: findJsDocForComment(node, selectedDocComment), + inheritedFromParentDeclaration, + }); + } + } + + return discovered; +} + export function discoverComment( symbol: ts.Symbol, kind: ReflectionKind, logger: Logger, commentStyle: CommentStyle, + checker: ts.TypeChecker, ): DiscoveredComment | undefined { // For a module comment, we want the first one defined in the file, // not the last one, since that will apply to the import or declaration. const reverse = !symbol.declarations?.some(ts.isSourceFile); - const discovered: DiscoveredComment[] = []; - const seen = new Set(); - - for (const decl of symbol.declarations || []) { - const text = decl.getSourceFile().text; - if (wantedKinds[kind].includes(decl.kind)) { - const node = declarationToCommentNode(decl); - if (!node || seen.has(node)) { - continue; - } - seen.add(node); - - // Special behavior here! - // Signatures and symbols have two distinct discovery methods as of TypeDoc 0.26. - // This method discovers comments for symbols, and function-likes will only have - // a symbol comment if there is more than one signature (== more than one declaration) - // and there is a comment on the implementation signature. - if ( - kind & ReflectionKind.ContainsCallSignatures && - [ - ts.SyntaxKind.FunctionDeclaration, - ts.SyntaxKind.MethodDeclaration, - ts.SyntaxKind.Constructor, - ].includes(node.kind) && - (symbol.declarations!.filter((d) => - wantedKinds[kind].includes(d.kind), - ).length === 1 || - !(node as ts.FunctionDeclaration).body) - ) { - continue; - } - - const comments = collectCommentRanges( - ts.getLeadingCommentRanges(text, node.pos), - ); + const wantedDeclarations = filter(symbol.declarations, (decl) => + wantedKinds[kind].includes(decl.kind), + ); - if (reverse) { - comments.reverse(); - } + const commentNodes = wantedDeclarations.flatMap((decl) => + declarationToCommentNodes(decl, checker), + ); - const selectedDocComment = comments.find((ranges) => - permittedRange(text, ranges, commentStyle), + // Special behavior here! + // Signatures and symbols have two distinct discovery methods as of TypeDoc 0.26. + // This method discovers comments for symbols, and function-likes will only have + // a symbol comment if there is more than one signature (== more than one declaration) + // and there is a comment on the implementation signature. + if (kind & ReflectionKind.ContainsCallSignatures) { + const canHaveOverloads = wantedDeclarations.some((node) => + [ + ts.SyntaxKind.FunctionDeclaration, + ts.SyntaxKind.MethodDeclaration, + ts.SyntaxKind.Constructor, + ].includes(node.kind), + ); + + const isOverloaded = canHaveOverloads && wantedDeclarations.length > 1; + + if (isOverloaded) { + commentNodes.length = 0; + + const implementationNode = wantedDeclarations.find( + (node) => (node as ts.FunctionDeclaration).body, ); - - if (selectedDocComment) { - discovered.push({ - file: decl.getSourceFile(), - ranges: selectedDocComment, - jsDoc: findJsDocForComment(node, selectedDocComment), + if (implementationNode) { + commentNodes.push({ + node: implementationNode, + inheritedFromParentDeclaration: false, }); } + } else if (canHaveOverloads) { + // Single signature function, function reflection doesn't get a comment, + // the signatures do. + commentNodes.length = 0; + } else { + // Variable declaration which happens to include signatures. } } + const discovered = checkCommentDeclarations( + commentNodes, + reverse, + commentStyle, + ); + switch (discovered.length) { case 0: return undefined; case 1: return discovered[0]; default: { - logger.warn( - logger.i18n.symbol_0_has_multiple_declarations_with_comment( - symbol.name, - ), - ); - const locations = discovered.map(({ file, ranges: [{ pos }] }) => { - const path = nicePath(file.fileName); - const line = - ts.getLineAndCharacterOfPosition(file, pos).line + 1; - return `${path}:${line}`; - }); - logger.info( - logger.i18n.comments_for_0_are_declared_at_1( - symbol.name, - locations.join("\n\t"), - ), - ); + if ( + discovered.filter((n) => !n.inheritedFromParentDeclaration) + .length > 1 + ) { + logger.warn( + logger.i18n.symbol_0_has_multiple_declarations_with_comment( + symbol.name, + ), + ); + const locations = discovered.map( + ({ file, ranges: [{ pos }] }) => { + const path = nicePath(file.fileName); + const line = + ts.getLineAndCharacterOfPosition(file, pos).line + + 1; + return `${path}:${line}`; + }, + ); + logger.info( + logger.i18n.comments_for_0_are_declared_at_1( + symbol.name, + locations.join("\n\t"), + ), + ); + } return discovered[0]; } } @@ -259,46 +307,49 @@ export function discoverComment( export function discoverSignatureComment( declaration: ts.SignatureDeclaration | ts.JSDocSignature, + checker: ts.TypeChecker, commentStyle: CommentStyle, ): DiscoveredComment | undefined { - const node = declarationToCommentNode(declaration); - if (!node) { - return; - } - - if (ts.isJSDocSignature(node)) { - const comment = node.parent.parent; - ok(ts.isJSDoc(comment)); - - return { - file: node.getSourceFile(), - ranges: [ - { - kind: ts.SyntaxKind.MultiLineCommentTrivia, - pos: comment.pos, - end: comment.end, - }, - ], - jsDoc: comment, - }; - } - - const text = node.getSourceFile().text; - - const comments = collectCommentRanges( - ts.getLeadingCommentRanges(text, node.pos), - ); - comments.reverse(); + for (const { + node, + inheritedFromParentDeclaration, + } of declarationToCommentNodes(declaration, checker)) { + if (ts.isJSDocSignature(node)) { + const comment = node.parent.parent; + ok(ts.isJSDoc(comment)); + + return { + file: node.getSourceFile(), + ranges: [ + { + kind: ts.SyntaxKind.MultiLineCommentTrivia, + pos: comment.pos, + end: comment.end, + }, + ], + jsDoc: comment, + inheritedFromParentDeclaration, + }; + } - const comment = comments.find((ranges) => - permittedRange(text, ranges, commentStyle), - ); - if (comment) { - return { - file: node.getSourceFile(), - ranges: comment, - jsDoc: findJsDocForComment(node, comment), - }; + const text = node.getSourceFile().text; + + const comments = collectCommentRanges( + ts.getLeadingCommentRanges(text, node.pos), + ); + comments.reverse(); + + const comment = comments.find((ranges) => + permittedRange(text, ranges, commentStyle), + ); + if (comment) { + return { + file: node.getSourceFile(), + ranges: comment, + jsDoc: findJsDocForComment(node, comment), + inheritedFromParentDeclaration, + }; + } } } @@ -355,7 +406,9 @@ function getRootModuleDeclaration(node: ts.ModuleDeclaration): ts.Node { return node; } -function declarationToCommentNode(node: ts.Declaration): ts.Node | undefined { +function declarationToCommentNodeIgnoringParents( + node: ts.Declaration, +): ts.Node | undefined { // ts.SourceFile is a counterexample if (!node.parent) return node; @@ -403,8 +456,80 @@ function declarationToCommentNode(node: ts.Declaration): ts.Node | undefined { if (ts.SyntaxKind.NamespaceExport === node.kind) { return node.parent; } +} - return node; +function declarationToCommentNodes( + node: ts.Declaration, + checker: ts.TypeChecker, +): ReadonlyArray<{ node: ts.Node; inheritedFromParentDeclaration: boolean }> { + const commentNode = declarationToCommentNodeIgnoringParents(node); + + if (commentNode) { + return [ + { + node: commentNode, + inheritedFromParentDeclaration: false, + }, + ]; + } + + const result: { node: ts.Node; inheritedFromParentDeclaration: boolean }[] = + [ + { + node, + inheritedFromParentDeclaration: false, + }, + ]; + + const seenSymbols = new Set(); + const bases = findBaseOfDeclaration(checker, node, (symbol) => { + if (!seenSymbols.has(symbol)) { + seenSymbols.add(symbol); + return symbol.declarations?.map( + (node) => declarationToCommentNodeIgnoringParents(node) || node, + ); + } + }); + + for (const parentCommentNode of bases || []) { + result.push({ + node: parentCommentNode, + inheritedFromParentDeclaration: true, + }); + } + + return result; +} + +// Lifted from the TS source, with a couple minor modifications +function findBaseOfDeclaration( + checker: ts.TypeChecker, + declaration: ts.Declaration, + cb: (symbol: ts.Symbol) => T[] | undefined, +): T[] | undefined { + const classOrInterfaceDeclaration = + declaration.parent?.kind === ts.SyntaxKind.Constructor + ? declaration.parent.parent + : declaration.parent; + if (!classOrInterfaceDeclaration) return; + + const isStaticMember = + ts.getCombinedModifierFlags(declaration) & ts.ModifierFlags.Static; + return firstDefined( + ts.getAllSuperTypeNodes(classOrInterfaceDeclaration), + (superTypeNode) => { + const baseType = checker.getTypeAtLocation(superTypeNode); + const type = + isStaticMember && baseType.symbol + ? checker.getTypeOfSymbol(baseType.symbol) + : baseType; + const symbol = checker.getPropertyOfType( + type, + declaration.symbol!.name, + ); + return symbol ? cb(symbol) : undefined; + }, + ); } /** diff --git a/src/lib/converter/comments/index.ts b/src/lib/converter/comments/index.ts index 09614a7c8..391acb811 100644 --- a/src/lib/converter/comments/index.ts +++ b/src/lib/converter/comments/index.ts @@ -23,6 +23,8 @@ export interface CommentParserConfig { modifierTags: Set; jsDocCompatibility: JsDocCompatibility; suppressCommentWarningsInDeclarationFiles: boolean; + useTsLinkResolution: boolean; + commentStyle: CommentStyle; } const jsDocCommentKinds = [ @@ -56,7 +58,10 @@ function getCommentWithCache( const { file, ranges, jsDoc } = discovered; const cache = commentCache.get(file) || new Map(); if (cache.has(ranges[0].pos)) { - return cache.get(ranges[0].pos)!.clone(); + const clone = cache.get(ranges[0].pos)!.clone(); + clone.inheritedFromParentDeclaration = + discovered.inheritedFromParentDeclaration; + return clone; } let comment: Comment; @@ -90,6 +95,8 @@ function getCommentWithCache( } comment.discoveryId = ++commentDiscoveryId; + comment.inheritedFromParentDeclaration = + discovered.inheritedFromParentDeclaration; cache.set(ranges[0].pos, comment); commentCache.set(file, cache); @@ -108,7 +115,7 @@ function getCommentImpl( commentSource, config, logger, - checker, + config.useTsLinkResolution ? checker : undefined, files, ); @@ -145,8 +152,7 @@ export function getComment( kind: ReflectionKind, config: CommentParserConfig, logger: Logger, - commentStyle: CommentStyle, - checker: ts.TypeChecker | undefined, + checker: ts.TypeChecker, files: FileRegistry, ): Comment | undefined { const declarations = symbol.declarations || []; @@ -166,7 +172,7 @@ export function getComment( const sf = declarations.find(ts.isSourceFile); if (sf) { - return getFileComment(sf, config, logger, commentStyle, checker, files); + return getFileComment(sf, config, logger, checker, files); } const isModule = declarations.some((decl) => { @@ -177,7 +183,7 @@ export function getComment( }); const comment = getCommentImpl( - discoverComment(symbol, kind, logger, commentStyle), + discoverComment(symbol, kind, logger, config.commentStyle, checker), config, logger, isModule, @@ -190,7 +196,6 @@ export function getComment( symbol, config, logger, - commentStyle, checker, files, ); @@ -204,12 +209,11 @@ export function getNodeComment( moduleComment: boolean, config: CommentParserConfig, logger: Logger, - commentStyle: CommentStyle, checker: ts.TypeChecker | undefined, files: FileRegistry, ) { return getCommentImpl( - discoverNodeComment(node, commentStyle), + discoverNodeComment(node, config.commentStyle), config, logger, moduleComment, @@ -222,16 +226,18 @@ export function getFileComment( file: ts.SourceFile, config: CommentParserConfig, logger: Logger, - commentStyle: CommentStyle, checker: ts.TypeChecker | undefined, files: FileRegistry, ): Comment | undefined { - for (const commentSource of discoverFileComments(file, commentStyle)) { + for (const commentSource of discoverFileComments( + file, + config.commentStyle, + )) { const comment = getCommentWithCache( commentSource, config, logger, - checker, + config.useTsLinkResolution ? checker : undefined, files, ); @@ -253,22 +259,14 @@ function getConstructorParamPropertyComment( symbol: ts.Symbol, config: CommentParserConfig, logger: Logger, - commentStyle: CommentStyle, - checker: ts.TypeChecker | undefined, + checker: ts.TypeChecker, files: FileRegistry, ): Comment | undefined { const decl = symbol.declarations?.find(ts.isParameter); if (!decl) return; const ctor = decl.parent; - const comment = getSignatureComment( - ctor, - config, - logger, - commentStyle, - checker, - files, - ); + const comment = getSignatureComment(ctor, config, logger, checker, files); const paramTag = comment?.getIdentifiedTag(symbol.name, "@param"); if (paramTag) { @@ -282,12 +280,11 @@ export function getSignatureComment( declaration: ts.SignatureDeclaration | ts.JSDocSignature, config: CommentParserConfig, logger: Logger, - commentStyle: CommentStyle, - checker: ts.TypeChecker | undefined, + checker: ts.TypeChecker, files: FileRegistry, ): Comment | undefined { return getCommentImpl( - discoverSignatureComment(declaration, commentStyle), + discoverSignatureComment(declaration, checker, config.commentStyle), config, logger, false, @@ -328,10 +325,11 @@ export function getJsDocComment( }, ], jsDoc: parent, + inheritedFromParentDeclaration: false, }, config, logger, - checker, + config.useTsLinkResolution ? checker : undefined, files, )!; diff --git a/src/lib/converter/context.ts b/src/lib/converter/context.ts index 526836da1..9d4cebaef 100644 --- a/src/lib/converter/context.ts +++ b/src/lib/converter/context.ts @@ -273,8 +273,7 @@ export class Context { kind, this.converter.config, this.logger, - this.converter.commentStyle, - this.converter.useTsLinkResolution ? this.checker : undefined, + this.checker, this.project.files, ); } @@ -285,8 +284,7 @@ export class Context { moduleComment, this.converter.config, this.logger, - this.converter.commentStyle, - this.converter.useTsLinkResolution ? this.checker : undefined, + this.checker, this.project.files, ); } @@ -296,8 +294,7 @@ export class Context { node, this.converter.config, this.logger, - this.converter.commentStyle, - this.converter.useTsLinkResolution ? this.checker : undefined, + this.checker, this.project.files, ); } @@ -314,7 +311,7 @@ export class Context { declaration, this.converter.config, this.logger, - this.converter.useTsLinkResolution ? this.checker : undefined, + this.checker, this.project.files, ); } @@ -326,8 +323,7 @@ export class Context { declaration, this.converter.config, this.logger, - this.converter.commentStyle, - this.converter.useTsLinkResolution ? this.checker : undefined, + this.checker, this.project.files, ); } diff --git a/src/lib/converter/converter.ts b/src/lib/converter/converter.ts index 26722d372..ed695ee97 100644 --- a/src/lib/converter/converter.ts +++ b/src/lib/converter/converter.ts @@ -132,10 +132,6 @@ export class Converter extends ChildableComponent< Record >; - /** @internal */ - @Option("useTsLinkResolution") - accessor useTsLinkResolution!: boolean; - /** @internal */ @Option("preserveLinkText") accessor preserveLinkText!: boolean; @@ -697,6 +693,10 @@ export class Converter extends ChildableComponent< this.application.options.getValue( "suppressCommentWarningsInDeclarationFiles", ), + useTsLinkResolution: this.application.options.getValue( + "useTsLinkResolution", + ), + commentStyle: this.application.options.getValue("commentStyle"), }; // Can't be included in options because the TSDoc parser blows up if we do. diff --git a/src/lib/converter/plugins/CommentPlugin.ts b/src/lib/converter/plugins/CommentPlugin.ts index 997a5a9b5..d227b87ae 100644 --- a/src/lib/converter/plugins/CommentPlugin.ts +++ b/src/lib/converter/plugins/CommentPlugin.ts @@ -682,7 +682,7 @@ export class CommentPlugin extends ConverterComponent { moveNestedParamTags(/* in-out */ paramTags, params, comment.sourcePath); - if (paramTags.length) { + if (!comment.inheritedFromParentDeclaration) { for (const tag of paramTags) { this.application.logger.warn( this.application.i18n.signature_0_has_unused_param_with_name_1( diff --git a/src/lib/models/comments/comment.ts b/src/lib/models/comments/comment.ts index fbd7cf2de..e074ed8a2 100644 --- a/src/lib/models/comments/comment.ts +++ b/src/lib/models/comments/comment.ts @@ -397,6 +397,15 @@ export class Comment { @NonEnumerable discoveryId?: number; + /** + * If the comment was inherited from a different "parent" declaration + * (see #2545), then it is desirable to know this as any `@param` tags + * which do not apply should not cause warnings. This is not serialized, + * and only set when the comment was created from a `ts.CommentRange`. + */ + @NonEnumerable + inheritedFromParentDeclaration?: boolean; + /** * Creates a new Comment instance. */ @@ -422,6 +431,8 @@ export class Comment { ); comment.discoveryId = this.discoveryId; comment.sourcePath = this.sourcePath; + comment.inheritedFromParentDeclaration = + this.inheritedFromParentDeclaration; return comment; } diff --git a/src/lib/types/ts-internal/index.d.ts b/src/lib/types/ts-internal/index.d.ts index 31b66c0c6..dfee92d49 100644 --- a/src/lib/types/ts-internal/index.d.ts +++ b/src/lib/types/ts-internal/index.d.ts @@ -28,6 +28,12 @@ declare module "typescript" { noCache?: boolean, ): readonly (JSDoc | JSDocTag)[]; + export function getInterfaceBaseTypeNodes( + node: InterfaceDeclaration, + ): NodeArray | undefined; + + export function getAllSuperTypeNodes(node: ts.Node): readonly ts.TypeNode[]; + export interface Signature { thisParameter?: ts.Symbol; } diff --git a/src/lib/utils/array.ts b/src/lib/utils/array.ts index f9d27da9a..ff79d13aa 100644 --- a/src/lib/utils/array.ts +++ b/src/lib/utils/array.ts @@ -1,3 +1,5 @@ +export const emptyArray: readonly [] = []; + /** * Inserts an item into an array sorted by priority. If two items have the same priority, * the item will be inserted later will be placed earlier in the array. @@ -144,3 +146,27 @@ export function filterMap( return result; } + +export function firstDefined( + array: readonly T[] | undefined, + callback: (element: T, index: number) => U | undefined, +): U | undefined { + if (array === undefined) { + return undefined; + } + + for (let i = 0; i < array.length; i++) { + const result = callback(array[i], i); + if (result !== undefined) { + return result; + } + } + return undefined; +} + +export function filter( + array: readonly T[] | undefined, + predicate: (value: T, index: number, array: readonly T[]) => boolean, +): readonly T[] { + return array ? array.filter(predicate) : emptyArray; +} diff --git a/src/test/comments.test.ts b/src/test/comments.test.ts index 734f0d7ef..005b5f10f 100644 --- a/src/test/comments.test.ts +++ b/src/test/comments.test.ts @@ -1095,6 +1095,8 @@ describe("Comment Parser", () => { inheritDocTag: false, }, suppressCommentWarningsInDeclarationFiles: false, + useTsLinkResolution: false, + commentStyle: "jsdoc", }; it("Should recognize @defaultValue as code", () => { diff --git a/src/test/converter/inherit-param-doc/inherit-param-doc.ts b/src/test/converter/inherit-param-doc/inherit-param-doc.ts index 50ece38d1..58fc3f69b 100644 --- a/src/test/converter/inherit-param-doc/inherit-param-doc.ts +++ b/src/test/converter/inherit-param-doc/inherit-param-doc.ts @@ -4,11 +4,18 @@ export interface Base { * @param b - Parameter B. */ method1(a: number, b: string): void; + /** + * @param a - Parameter A. + * @param b - Parameter B. + */ + method2(a: number, b: string): void; } export class Class1 implements Base { /** @inheritDoc */ method1(a: number, b: string): void {} + /** @inheritDoc */ + method2(): void {} } export class Class2 implements Base { @@ -18,6 +25,12 @@ export class Class2 implements Base { * @param a - Custom parameter A doc. */ method1(a: number, b: string): void {} + /** + * @inheritDoc + * + * @param a - Custom parameter A doc. + */ + method2(a: number): void {} } export class Class3 implements Base { @@ -27,4 +40,5 @@ export class Class3 implements Base { * @param c - Custom second parameter doc with name change. */ method1(a: number, c: string): void {} + method2(a: number): void {} } diff --git a/src/test/converter/inherit-param-doc/specs.json b/src/test/converter/inherit-param-doc/specs.json index 997c71b61..264c557bf 100644 --- a/src/test/converter/inherit-param-doc/specs.json +++ b/src/test/converter/inherit-param-doc/specs.json @@ -6,28 +6,28 @@ "flags": {}, "children": [ { - "id": 6, + "id": 10, "name": "Class1", "variant": "declaration", "kind": 128, "flags": {}, "children": [ { - "id": 7, + "id": 11, "name": "constructor", "variant": "declaration", "kind": 512, "flags": {}, "signatures": [ { - "id": 8, + "id": 12, "name": "new Class1", "variant": "signature", "kind": 16384, "flags": {}, "type": { "type": "reference", - "target": 6, + "target": 10, "name": "Class1", "package": "typedoc" } @@ -35,7 +35,7 @@ ] }, { - "id": 9, + "id": 13, "name": "method1", "variant": "declaration", "kind": 2048, @@ -43,14 +43,14 @@ "sources": [ { "fileName": "inherit-param-doc.ts", - "line": 11, + "line": 16, "character": 4, - "url": "typedoc://inherit-param-doc.ts#L11" + "url": "typedoc://inherit-param-doc.ts#L16" } ], "signatures": [ { - "id": 10, + "id": 14, "name": "method1", "variant": "signature", "kind": 4096, @@ -58,14 +58,14 @@ "sources": [ { "fileName": "inherit-param-doc.ts", - "line": 11, + "line": 16, "character": 4, - "url": "typedoc://inherit-param-doc.ts#L11" + "url": "typedoc://inherit-param-doc.ts#L16" } ], "parameters": [ { - "id": 11, + "id": 15, "name": "a", "variant": "param", "kind": 32768, @@ -84,7 +84,7 @@ } }, { - "id": 12, + "id": 16, "name": "b", "variant": "param", "kind": 32768, @@ -119,28 +119,75 @@ "target": 2, "name": "Base.method1" } + }, + { + "id": 17, + "name": "method2", + "variant": "declaration", + "kind": 2048, + "flags": {}, + "sources": [ + { + "fileName": "inherit-param-doc.ts", + "line": 18, + "character": 4, + "url": "typedoc://inherit-param-doc.ts#L18" + } + ], + "signatures": [ + { + "id": 18, + "name": "method2", + "variant": "signature", + "kind": 4096, + "flags": {}, + "sources": [ + { + "fileName": "inherit-param-doc.ts", + "line": 18, + "character": 4, + "url": "typedoc://inherit-param-doc.ts#L18" + } + ], + "type": { + "type": "intrinsic", + "name": "void" + }, + "implementationOf": { + "type": "reference", + "target": 7, + "name": "Base.method2" + } + } + ], + "implementationOf": { + "type": "reference", + "target": 6, + "name": "Base.method2" + } } ], "groups": [ { "title": "Constructors", "children": [ - 7 + 11 ] }, { "title": "Methods", "children": [ - 9 + 13, + 17 ] } ], "sources": [ { "fileName": "inherit-param-doc.ts", - "line": 9, + "line": 14, "character": 13, - "url": "typedoc://inherit-param-doc.ts#L9" + "url": "typedoc://inherit-param-doc.ts#L14" } ], "implementedTypes": [ @@ -153,28 +200,28 @@ ] }, { - "id": 13, + "id": 19, "name": "Class2", "variant": "declaration", "kind": 128, "flags": {}, "children": [ { - "id": 14, + "id": 20, "name": "constructor", "variant": "declaration", "kind": 512, "flags": {}, "signatures": [ { - "id": 15, + "id": 21, "name": "new Class2", "variant": "signature", "kind": 16384, "flags": {}, "type": { "type": "reference", - "target": 13, + "target": 19, "name": "Class2", "package": "typedoc" } @@ -182,7 +229,7 @@ ] }, { - "id": 16, + "id": 22, "name": "method1", "variant": "declaration", "kind": 2048, @@ -190,14 +237,14 @@ "sources": [ { "fileName": "inherit-param-doc.ts", - "line": 20, + "line": 27, "character": 4, - "url": "typedoc://inherit-param-doc.ts#L20" + "url": "typedoc://inherit-param-doc.ts#L27" } ], "signatures": [ { - "id": 17, + "id": 23, "name": "method1", "variant": "signature", "kind": 4096, @@ -205,14 +252,14 @@ "sources": [ { "fileName": "inherit-param-doc.ts", - "line": 20, + "line": 27, "character": 4, - "url": "typedoc://inherit-param-doc.ts#L20" + "url": "typedoc://inherit-param-doc.ts#L27" } ], "parameters": [ { - "id": 18, + "id": 24, "name": "a", "variant": "param", "kind": 32768, @@ -231,7 +278,7 @@ } }, { - "id": 19, + "id": 25, "name": "b", "variant": "param", "kind": 32768, @@ -266,28 +313,96 @@ "target": 2, "name": "Base.method1" } + }, + { + "id": 26, + "name": "method2", + "variant": "declaration", + "kind": 2048, + "flags": {}, + "sources": [ + { + "fileName": "inherit-param-doc.ts", + "line": 33, + "character": 4, + "url": "typedoc://inherit-param-doc.ts#L33" + } + ], + "signatures": [ + { + "id": 27, + "name": "method2", + "variant": "signature", + "kind": 4096, + "flags": {}, + "sources": [ + { + "fileName": "inherit-param-doc.ts", + "line": 33, + "character": 4, + "url": "typedoc://inherit-param-doc.ts#L33" + } + ], + "parameters": [ + { + "id": 28, + "name": "a", + "variant": "param", + "kind": 32768, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Custom parameter A doc." + } + ] + }, + "type": { + "type": "intrinsic", + "name": "number" + } + } + ], + "type": { + "type": "intrinsic", + "name": "void" + }, + "implementationOf": { + "type": "reference", + "target": 7, + "name": "Base.method2" + } + } + ], + "implementationOf": { + "type": "reference", + "target": 6, + "name": "Base.method2" + } } ], "groups": [ { "title": "Constructors", "children": [ - 14 + 20 ] }, { "title": "Methods", "children": [ - 16 + 22, + 26 ] } ], "sources": [ { "fileName": "inherit-param-doc.ts", - "line": 14, + "line": 21, "character": 13, - "url": "typedoc://inherit-param-doc.ts#L14" + "url": "typedoc://inherit-param-doc.ts#L21" } ], "implementedTypes": [ @@ -300,28 +415,28 @@ ] }, { - "id": 20, + "id": 29, "name": "Class3", "variant": "declaration", "kind": 128, "flags": {}, "children": [ { - "id": 21, + "id": 30, "name": "constructor", "variant": "declaration", "kind": 512, "flags": {}, "signatures": [ { - "id": 22, + "id": 31, "name": "new Class3", "variant": "signature", "kind": 16384, "flags": {}, "type": { "type": "reference", - "target": 20, + "target": 29, "name": "Class3", "package": "typedoc" } @@ -329,7 +444,7 @@ ] }, { - "id": 23, + "id": 32, "name": "method1", "variant": "declaration", "kind": 2048, @@ -337,14 +452,14 @@ "sources": [ { "fileName": "inherit-param-doc.ts", - "line": 29, + "line": 42, "character": 4, - "url": "typedoc://inherit-param-doc.ts#L29" + "url": "typedoc://inherit-param-doc.ts#L42" } ], "signatures": [ { - "id": 24, + "id": 33, "name": "method1", "variant": "signature", "kind": 4096, @@ -352,14 +467,14 @@ "sources": [ { "fileName": "inherit-param-doc.ts", - "line": 29, + "line": 42, "character": 4, - "url": "typedoc://inherit-param-doc.ts#L29" + "url": "typedoc://inherit-param-doc.ts#L42" } ], "parameters": [ { - "id": 25, + "id": 34, "name": "a", "variant": "param", "kind": 32768, @@ -378,7 +493,7 @@ } }, { - "id": 26, + "id": 35, "name": "c", "variant": "param", "kind": 32768, @@ -413,28 +528,96 @@ "target": 2, "name": "Base.method1" } + }, + { + "id": 36, + "name": "method2", + "variant": "declaration", + "kind": 2048, + "flags": {}, + "sources": [ + { + "fileName": "inherit-param-doc.ts", + "line": 43, + "character": 4, + "url": "typedoc://inherit-param-doc.ts#L43" + } + ], + "signatures": [ + { + "id": 37, + "name": "method2", + "variant": "signature", + "kind": 4096, + "flags": {}, + "sources": [ + { + "fileName": "inherit-param-doc.ts", + "line": 43, + "character": 4, + "url": "typedoc://inherit-param-doc.ts#L43" + } + ], + "parameters": [ + { + "id": 38, + "name": "a", + "variant": "param", + "kind": 32768, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Parameter A." + } + ] + }, + "type": { + "type": "intrinsic", + "name": "number" + } + } + ], + "type": { + "type": "intrinsic", + "name": "void" + }, + "implementationOf": { + "type": "reference", + "target": 7, + "name": "Base.method2" + } + } + ], + "implementationOf": { + "type": "reference", + "target": 6, + "name": "Base.method2" + } } ], "groups": [ { "title": "Constructors", "children": [ - 21 + 30 ] }, { "title": "Methods", "children": [ - 23 + 32, + 36 ] } ], "sources": [ { "fileName": "inherit-param-doc.ts", - "line": 23, + "line": 36, "character": 13, - "url": "typedoc://inherit-param-doc.ts#L23" + "url": "typedoc://inherit-param-doc.ts#L36" } ], "implementedTypes": [ @@ -528,13 +711,90 @@ } } ] + }, + { + "id": 6, + "name": "method2", + "variant": "declaration", + "kind": 2048, + "flags": {}, + "sources": [ + { + "fileName": "inherit-param-doc.ts", + "line": 11, + "character": 4, + "url": "typedoc://inherit-param-doc.ts#L11" + } + ], + "signatures": [ + { + "id": 7, + "name": "method2", + "variant": "signature", + "kind": 4096, + "flags": {}, + "sources": [ + { + "fileName": "inherit-param-doc.ts", + "line": 11, + "character": 4, + "url": "typedoc://inherit-param-doc.ts#L11" + } + ], + "parameters": [ + { + "id": 8, + "name": "a", + "variant": "param", + "kind": 32768, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Parameter A." + } + ] + }, + "type": { + "type": "intrinsic", + "name": "number" + } + }, + { + "id": 9, + "name": "b", + "variant": "param", + "kind": 32768, + "flags": {}, + "comment": { + "summary": [ + { + "kind": "text", + "text": "Parameter B." + } + ] + }, + "type": { + "type": "intrinsic", + "name": "string" + } + } + ], + "type": { + "type": "intrinsic", + "name": "void" + } + } + ] } ], "groups": [ { "title": "Methods", "children": [ - 2 + 2, + 6 ] } ], @@ -549,17 +809,17 @@ "implementedBy": [ { "type": "reference", - "target": 6, + "target": 10, "name": "Class1" }, { "type": "reference", - "target": 13, + "target": 19, "name": "Class2" }, { "type": "reference", - "target": 20, + "target": 29, "name": "Class3" } ] @@ -569,9 +829,9 @@ { "title": "Classes", "children": [ - 6, - 13, - 20 + 10, + 19, + 29 ] }, { @@ -609,63 +869,111 @@ }, "6": { "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", - "qualifiedName": "Class1" + "qualifiedName": "Base.method2" + }, + "7": { + "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", + "qualifiedName": "Base.method2" + }, + "8": { + "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", + "qualifiedName": "a" }, "9": { "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", - "qualifiedName": "Class1.method1" + "qualifiedName": "b" }, "10": { + "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", + "qualifiedName": "Class1" + }, + "13": { "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", "qualifiedName": "Class1.method1" }, - "11": { + "14": { + "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", + "qualifiedName": "Class1.method1" + }, + "15": { "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", "qualifiedName": "a" }, - "12": { + "16": { "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", "qualifiedName": "b" }, - "13": { + "17": { + "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", + "qualifiedName": "Class1.method2" + }, + "18": { + "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", + "qualifiedName": "Class1.method2" + }, + "19": { "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", "qualifiedName": "Class2" }, - "16": { + "22": { "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", "qualifiedName": "Class2.method1" }, - "17": { + "23": { "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", "qualifiedName": "Class2.method1" }, - "18": { + "24": { "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", "qualifiedName": "a" }, - "19": { + "25": { "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", "qualifiedName": "b" }, - "20": { + "26": { + "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", + "qualifiedName": "Class2.method2" + }, + "27": { + "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", + "qualifiedName": "Class2.method2" + }, + "28": { + "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", + "qualifiedName": "a" + }, + "29": { "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", "qualifiedName": "Class3" }, - "23": { + "32": { "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", "qualifiedName": "Class3.method1" }, - "24": { + "33": { "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", "qualifiedName": "Class3.method1" }, - "25": { + "34": { "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", "qualifiedName": "a" }, - "26": { + "35": { "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", "qualifiedName": "c" + }, + "36": { + "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", + "qualifiedName": "Class3.method2" + }, + "37": { + "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", + "qualifiedName": "Class3.method2" + }, + "38": { + "sourceFileName": "src/test/converter/inherit-param-doc/inherit-param-doc.ts", + "qualifiedName": "a" } }, "files": { diff --git a/src/test/converter2/issues/gh2545.ts b/src/test/converter2/issues/gh2545.ts index 873efa373..3061484fb 100644 --- a/src/test/converter2/issues/gh2545.ts +++ b/src/test/converter2/issues/gh2545.ts @@ -1,27 +1,28 @@ +/** Parent docs */ abstract class Parent { - /** - * notAbstract docs - */ - notAbstract(): string { - return "hello"; - } - /** - * notAbstract2 docs - */ - notAbstract2(): string { - return "hello"; - } - /** - * isAbstract docs - */ - abstract isAbstract(): string; + /** notAbstract docs */ + notAbstract(): void {} + /** notAbstract2 docs */ + notAbstract2(): void {} + /** isAbstract docs */ + abstract isAbstract(): void; + /** abstractProperty docs */ + abstract abstractProperty: string; } export class Child extends Parent { - override notAbstract2(): string { - return "foo"; - } - override isAbstract(): string { - return "bar"; - } + override notAbstract2(): void {} + override isAbstract(): void {} + + override abstractProperty = ""; +} + +// #2084 +export class Foo { + /** @internal*/ + isInternal() {} +} + +export class Bar extends Foo { + isInternal() {} // also internal } diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index 2544bb944..22e90d525 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -1486,13 +1486,27 @@ describe("Issue Tests", () => { equal(getSigComment(project, "fooWithComment", 1), "Overload 2"); }); - it.skip("#2545 discovers comments from non-exported 'parent' methods", () => { - // Currently failing + it("#2545 discovers comments from non-exported 'parent' methods", () => { const project = convert(); - equal(getComment(project, "Child.notAbstract"), "notAbstract docs"); - equal(getComment(project, "Child.notAbstract2"), "notAbstract2 docs"); - equal(getComment(project, "Child.isAbstract"), "isAbstract docs"); + equal(getSigComment(project, "Child.notAbstract"), "notAbstract docs"); + equal( + getSigComment(project, "Child.notAbstract2"), + "notAbstract2 docs", + ); + equal(getSigComment(project, "Child.isAbstract"), "isAbstract docs"); + equal( + getComment(project, "Child.abstractProperty"), + "abstractProperty docs", + ); + + // #2084 + equal( + querySig(project, "Bar.isInternal").comment?.hasModifier( + "@internal", + ), + true, + ); }); it("#2552 Ignores @license and @import comments, ", () => {