From aba0ef85a9b57ff9bc8e7fa2f4c19b48bdb3c0ca Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 11 Nov 2023 14:34:50 -0700 Subject: [PATCH] Detect duplicate nodes when discovering comments Closes #2359 Resolves #2437 Co-Authored-By: Sebastian Malton --- CHANGELOG.md | 2 ++ scripts/testcase.js | 6 +++++- src/lib/converter/comments/discovery.ts | 4 +++- src/lib/converter/symbols.ts | 15 ++++++++++----- src/test/converter2/issues/gh2437.ts | 18 ++++++++++++++++++ src/test/issues.c2.test.ts | 5 +++++ 6 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 src/test/converter2/issues/gh2437.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index bf4f21684..580b05317 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,12 @@ - Fixed default option values on options declared by plugins in packages mode, #2433. - `gitRevision` will now be replaced in `sourceLinkTemplate`, #2434. - Improved handling of function-modules created with `Object.assign`, #2436. +- TypeDoc will no longer warn about duplicate comments with warnings which point to a single comment, #2437 - Fixed an infinite loop when `skipLibCheck` is used to ignore some compiler errors, #2438. ### Thanks! +- @Nokel81 - @ocavue - @swarnpallav diff --git a/scripts/testcase.js b/scripts/testcase.js index 0431fd2c7..64c28e729 100644 --- a/scripts/testcase.js +++ b/scripts/testcase.js @@ -49,7 +49,11 @@ async function main() { const tokens = lexer.lex(data.body); const code = /** @type {marked.marked.Tokens.Code} */ ( - tokens.find((tok) => tok.type === "code") + tokens.find( + (tok) => + tok.type === "code" && + ["ts", "tsx", "js", "jsx"].includes(tok.lang || ""), + ) || tokens.find((tok) => tok.type === "code") ); if (!code) { console.log("No codeblock found"); diff --git a/src/lib/converter/comments/discovery.ts b/src/lib/converter/comments/discovery.ts index 45f085493..af3d655de 100644 --- a/src/lib/converter/comments/discovery.ts +++ b/src/lib/converter/comments/discovery.ts @@ -140,14 +140,16 @@ export function discoverComment( 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) { + if (!node || seen.has(node)) { continue; } + seen.add(node); // Special behavior here! We temporarily put the implementation comment // on the reflection which contains all the signatures. This lets us pull diff --git a/src/lib/converter/symbols.ts b/src/lib/converter/symbols.ts index 47705c852..be4eb0ed3 100644 --- a/src/lib/converter/symbols.ts +++ b/src/lib/converter/symbols.ts @@ -856,10 +856,11 @@ function convertVariable( exportSymbol?: ts.Symbol, ) { const declaration = symbol.getDeclarations()?.[0]; - assert(declaration); const comment = context.getComment(symbol, ReflectionKind.Variable); - const type = context.checker.getTypeOfSymbolAtLocation(symbol, declaration); + const type = declaration + ? context.checker.getTypeOfSymbolAtLocation(symbol, declaration) + : context.checker.getTypeOfSymbol(symbol); if ( isEnumLike(context.checker, type, declaration) && @@ -883,7 +884,7 @@ function convertVariable( ); let typeNode: ts.TypeNode | undefined; - if (ts.isVariableDeclaration(declaration)) { + if (declaration && ts.isVariableDeclaration(declaration)) { // Otherwise we might have destructuring typeNode = declaration.type; } @@ -902,8 +903,12 @@ function convertVariable( return ts.SymbolFlags.Property; } -function isEnumLike(checker: ts.TypeChecker, type: ts.Type, location: ts.Node) { - if (!hasAllFlags(type.flags, ts.TypeFlags.Object)) { +function isEnumLike( + checker: ts.TypeChecker, + type: ts.Type, + location?: ts.Node, +) { + if (!location || !hasAllFlags(type.flags, ts.TypeFlags.Object)) { return false; } diff --git a/src/test/converter2/issues/gh2437.ts b/src/test/converter2/issues/gh2437.ts new file mode 100644 index 000000000..7525abdab --- /dev/null +++ b/src/test/converter2/issues/gh2437.ts @@ -0,0 +1,18 @@ +export interface TemplatedTypeBase { + /** + * Doc here + */ + prop?: string[]; +} + +export interface One extends TemplatedTypeBase {} + +export interface Two extends TemplatedTypeBase {} + +export type Type = One | Two; + +export function isTemplateInstance( + type: Type, +): type is Type & { prop: string[] } { + return true; +} diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index cf6558642..a97127ee4 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -1213,6 +1213,11 @@ describe("Issue Tests", () => { ); }); + it("Does not warn due to the diamond problem in comment discovery #2437", () => { + convert(); + logger.expectNoOtherMessages(); + }); + it("Handles recursive aliases without looping infinitely #2438", () => { const bad = query(convert(), "Bad"); equal(bad.kind, ReflectionKind.Interface);