From a537eef98b8639e1222c9b8349a563ee5bf9e0fd Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Sun, 23 Feb 2025 00:59:12 +0200 Subject: [PATCH 1/2] feat(43950): support import types in jsdoc links --- src/compiler/checker.ts | 7 +- src/compiler/factory/nodeFactory.ts | 12 +-- src/compiler/parser.ts | 4 +- src/compiler/types.ts | 18 ++--- src/compiler/utilities.ts | 3 +- src/compiler/utilitiesPublic.ts | 4 +- tests/baselines/reference/api/typescript.d.ts | 18 ++--- tests/baselines/reference/jsdocLink7.baseline | 81 +++++++++++++++++++ .../reference/jsdocLinkTag10.symbols | 13 +++ .../baselines/reference/jsdocLinkTag10.types | 14 ++++ .../reference/jsdocLinkTag11.symbols | 13 +++ .../baselines/reference/jsdocLinkTag11.types | 15 ++++ .../cases/conformance/jsdoc/jsdocLinkTag10.ts | 11 +++ .../cases/conformance/jsdoc/jsdocLinkTag11.ts | 13 +++ tests/cases/fourslash/jsdocLink7.ts | 12 +++ 15 files changed, 209 insertions(+), 29 deletions(-) create mode 100644 tests/baselines/reference/jsdocLink7.baseline create mode 100644 tests/baselines/reference/jsdocLinkTag10.symbols create mode 100644 tests/baselines/reference/jsdocLinkTag10.types create mode 100644 tests/baselines/reference/jsdocLinkTag11.symbols create mode 100644 tests/baselines/reference/jsdocLinkTag11.types create mode 100644 tests/cases/conformance/jsdoc/jsdocLinkTag10.ts create mode 100644 tests/cases/conformance/jsdoc/jsdocLinkTag11.ts create mode 100644 tests/cases/fourslash/jsdocLink7.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7f18bbc1a1dec..7638c2527e6dd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -43407,7 +43407,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function checkJSDocLinkLikeTag(node: JSDocLink | JSDocLinkCode | JSDocLinkPlain) { if (node.name) { - resolveJSDocMemberName(node.name, /*ignoreErrors*/ true); + if (isImportTypeNode(node.name)) { + checkImportType(node.name); + } + else { + resolveJSDocMemberName(node.name, /*ignoreErrors*/ true); + } } } diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 694262eeeb54a..12b0668a3b6e8 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -5396,7 +5396,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode } // @api - function createJSDocLink(name: EntityName | JSDocMemberName | undefined, text: string): JSDocLink { + function createJSDocLink(name: EntityName | JSDocMemberName | ImportTypeNode | undefined, text: string): JSDocLink { const node = createBaseNode(SyntaxKind.JSDocLink); node.name = name; node.text = text; @@ -5404,14 +5404,14 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode } // @api - function updateJSDocLink(node: JSDocLink, name: EntityName | JSDocMemberName | undefined, text: string): JSDocLink { + function updateJSDocLink(node: JSDocLink, name: EntityName | JSDocMemberName | ImportTypeNode | undefined, text: string): JSDocLink { return node.name !== name ? update(createJSDocLink(name, text), node) : node; } // @api - function createJSDocLinkCode(name: EntityName | JSDocMemberName | undefined, text: string): JSDocLinkCode { + function createJSDocLinkCode(name: EntityName | JSDocMemberName | ImportTypeNode | undefined, text: string): JSDocLinkCode { const node = createBaseNode(SyntaxKind.JSDocLinkCode); node.name = name; node.text = text; @@ -5419,14 +5419,14 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode } // @api - function updateJSDocLinkCode(node: JSDocLinkCode, name: EntityName | JSDocMemberName | undefined, text: string): JSDocLinkCode { + function updateJSDocLinkCode(node: JSDocLinkCode, name: EntityName | JSDocMemberName | ImportTypeNode | undefined, text: string): JSDocLinkCode { return node.name !== name ? update(createJSDocLinkCode(name, text), node) : node; } // @api - function createJSDocLinkPlain(name: EntityName | JSDocMemberName | undefined, text: string): JSDocLinkPlain { + function createJSDocLinkPlain(name: EntityName | JSDocMemberName | ImportTypeNode | undefined, text: string): JSDocLinkPlain { const node = createBaseNode(SyntaxKind.JSDocLinkPlain); node.name = name; node.text = text; @@ -5434,7 +5434,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode } // @api - function updateJSDocLinkPlain(node: JSDocLinkPlain, name: EntityName | JSDocMemberName | undefined, text: string): JSDocLinkPlain { + function updateJSDocLinkPlain(node: JSDocLinkPlain, name: EntityName | JSDocMemberName | ImportTypeNode | undefined, text: string): JSDocLinkPlain { return node.name !== name ? update(createJSDocLinkPlain(name, text), node) : node; diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 8c69cccba1282..993a64bfcea48 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -9288,8 +9288,10 @@ namespace Parser { function parseJSDocLinkName() { if (tokenIsIdentifierOrKeyword(token())) { + if (token() === SyntaxKind.ImportKeyword) { + return parseImportType(); + } const pos = getNodePos(); - let name: EntityName | JSDocMemberName = parseIdentifierName(); while (parseOptional(SyntaxKind.DotToken)) { name = finishNode(factory.createQualifiedName(name, token() === SyntaxKind.PrivateIdentifier ? createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ false) : parseIdentifierName()), pos); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f6ca75a68e17d..67ff0fde99f2f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3951,19 +3951,19 @@ export interface JSDocTag extends Node { export interface JSDocLink extends Node { readonly kind: SyntaxKind.JSDocLink; - readonly name?: EntityName | JSDocMemberName; + readonly name?: EntityName | JSDocMemberName | ImportTypeNode; text: string; } export interface JSDocLinkCode extends Node { readonly kind: SyntaxKind.JSDocLinkCode; - readonly name?: EntityName | JSDocMemberName; + readonly name?: EntityName | JSDocMemberName | ImportTypeNode; text: string; } export interface JSDocLinkPlain extends Node { readonly kind: SyntaxKind.JSDocLinkPlain; - readonly name?: EntityName | JSDocMemberName; + readonly name?: EntityName | JSDocMemberName | ImportTypeNode; text: string; } @@ -9083,12 +9083,12 @@ export interface NodeFactory { updateJSDocNameReference(node: JSDocNameReference, name: EntityName | JSDocMemberName): JSDocNameReference; createJSDocMemberName(left: EntityName | JSDocMemberName, right: Identifier): JSDocMemberName; updateJSDocMemberName(node: JSDocMemberName, left: EntityName | JSDocMemberName, right: Identifier): JSDocMemberName; - createJSDocLink(name: EntityName | JSDocMemberName | undefined, text: string): JSDocLink; - updateJSDocLink(node: JSDocLink, name: EntityName | JSDocMemberName | undefined, text: string): JSDocLink; - createJSDocLinkCode(name: EntityName | JSDocMemberName | undefined, text: string): JSDocLinkCode; - updateJSDocLinkCode(node: JSDocLinkCode, name: EntityName | JSDocMemberName | undefined, text: string): JSDocLinkCode; - createJSDocLinkPlain(name: EntityName | JSDocMemberName | undefined, text: string): JSDocLinkPlain; - updateJSDocLinkPlain(node: JSDocLinkPlain, name: EntityName | JSDocMemberName | undefined, text: string): JSDocLinkPlain; + createJSDocLink(name: EntityName | JSDocMemberName | ImportTypeNode | undefined, text: string): JSDocLink; + updateJSDocLink(node: JSDocLink, name: EntityName | JSDocMemberName | ImportTypeNode | undefined, text: string): JSDocLink; + createJSDocLinkCode(name: EntityName | JSDocMemberName | ImportTypeNode | undefined, text: string): JSDocLinkCode; + updateJSDocLinkCode(node: JSDocLinkCode, name: EntityName | JSDocMemberName | ImportTypeNode | undefined, text: string): JSDocLinkCode; + createJSDocLinkPlain(name: EntityName | JSDocMemberName | ImportTypeNode | undefined, text: string): JSDocLinkPlain; + updateJSDocLinkPlain(node: JSDocLinkPlain, name: EntityName | JSDocMemberName | ImportTypeNode | undefined, text: string): JSDocLinkPlain; createJSDocTypeLiteral(jsDocPropertyTags?: readonly JSDocPropertyLikeTag[], isArrayType?: boolean): JSDocTypeLiteral; updateJSDocTypeLiteral(node: JSDocTypeLiteral, jsDocPropertyTags: readonly JSDocPropertyLikeTag[] | undefined, isArrayType: boolean | undefined): JSDocTypeLiteral; createJSDocSignature(typeParameters: readonly JSDocTemplateTag[] | undefined, parameters: readonly JSDocParameterTag[], type?: JSDocReturnTag): JSDocSignature; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 5755369134d8a..8e7a31022c329 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -12099,7 +12099,6 @@ export function forEachDynamicImportOrRequireCall { if (child.pos <= position && (position < child.end || (position === child.end && (child.kind === SyntaxKind.EndOfFileToken)))) { @@ -12107,7 +12106,7 @@ function getNodeAtPosition(sourceFile: SourceFile, position: number, includeJSDo } }; while (true) { - const child = isJavaScriptFile && includeJSDoc && hasJSDocNodes(current) && forEach(current.jsDoc, getContainingChild) || forEachChild(current, getContainingChild); + const child = includeJSDoc && hasJSDocNodes(current) && forEach(current.jsDoc, getContainingChild) || forEachChild(current, getContainingChild); if (!child) { return current; } diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index 02623678a0cf0..4c8f0f3c1f734 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -88,6 +88,7 @@ import { getLeadingCommentRanges, getLeadingCommentRangesOfNode, getSourceFileOfNode, + getTextOfNode, getTrailingCommentRanges, hasAccessorModifier, HasDecorators, @@ -133,6 +134,7 @@ import { isFunctionTypeNode, isIdentifier, isImportSpecifier, + isImportTypeNode, isInJSFile, isJSDoc, isJSDocAugmentsTag, @@ -1298,7 +1300,7 @@ function formatJSDocLink(link: JSDocLink | JSDocLinkCode | JSDocLinkPlain) { const kind = link.kind === SyntaxKind.JSDocLink ? "link" : link.kind === SyntaxKind.JSDocLinkCode ? "linkcode" : "linkplain"; - const name = link.name ? entityNameToString(link.name) : ""; + const name = link.name ? isImportTypeNode(link.name) ? getTextOfNode(link.name) : entityNameToString(link.name) : ""; const space = link.name && (link.text === "" || link.text.startsWith("://")) ? "" : " "; return `{@${kind} ${name}${space}${link.text}}`; } diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 2531c9ea0413d..1ba7c73b5fcd5 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -5723,17 +5723,17 @@ declare namespace ts { } interface JSDocLink extends Node { readonly kind: SyntaxKind.JSDocLink; - readonly name?: EntityName | JSDocMemberName; + readonly name?: EntityName | JSDocMemberName | ImportTypeNode; text: string; } interface JSDocLinkCode extends Node { readonly kind: SyntaxKind.JSDocLinkCode; - readonly name?: EntityName | JSDocMemberName; + readonly name?: EntityName | JSDocMemberName | ImportTypeNode; text: string; } interface JSDocLinkPlain extends Node { readonly kind: SyntaxKind.JSDocLinkPlain; - readonly name?: EntityName | JSDocMemberName; + readonly name?: EntityName | JSDocMemberName | ImportTypeNode; text: string; } type JSDocComment = JSDocText | JSDocLink | JSDocLinkCode | JSDocLinkPlain; @@ -7763,12 +7763,12 @@ declare namespace ts { updateJSDocNameReference(node: JSDocNameReference, name: EntityName | JSDocMemberName): JSDocNameReference; createJSDocMemberName(left: EntityName | JSDocMemberName, right: Identifier): JSDocMemberName; updateJSDocMemberName(node: JSDocMemberName, left: EntityName | JSDocMemberName, right: Identifier): JSDocMemberName; - createJSDocLink(name: EntityName | JSDocMemberName | undefined, text: string): JSDocLink; - updateJSDocLink(node: JSDocLink, name: EntityName | JSDocMemberName | undefined, text: string): JSDocLink; - createJSDocLinkCode(name: EntityName | JSDocMemberName | undefined, text: string): JSDocLinkCode; - updateJSDocLinkCode(node: JSDocLinkCode, name: EntityName | JSDocMemberName | undefined, text: string): JSDocLinkCode; - createJSDocLinkPlain(name: EntityName | JSDocMemberName | undefined, text: string): JSDocLinkPlain; - updateJSDocLinkPlain(node: JSDocLinkPlain, name: EntityName | JSDocMemberName | undefined, text: string): JSDocLinkPlain; + createJSDocLink(name: EntityName | JSDocMemberName | ImportTypeNode | undefined, text: string): JSDocLink; + updateJSDocLink(node: JSDocLink, name: EntityName | JSDocMemberName | ImportTypeNode | undefined, text: string): JSDocLink; + createJSDocLinkCode(name: EntityName | JSDocMemberName | ImportTypeNode | undefined, text: string): JSDocLinkCode; + updateJSDocLinkCode(node: JSDocLinkCode, name: EntityName | JSDocMemberName | ImportTypeNode | undefined, text: string): JSDocLinkCode; + createJSDocLinkPlain(name: EntityName | JSDocMemberName | ImportTypeNode | undefined, text: string): JSDocLinkPlain; + updateJSDocLinkPlain(node: JSDocLinkPlain, name: EntityName | JSDocMemberName | ImportTypeNode | undefined, text: string): JSDocLinkPlain; createJSDocTypeLiteral(jsDocPropertyTags?: readonly JSDocPropertyLikeTag[], isArrayType?: boolean): JSDocTypeLiteral; updateJSDocTypeLiteral(node: JSDocTypeLiteral, jsDocPropertyTags: readonly JSDocPropertyLikeTag[] | undefined, isArrayType: boolean | undefined): JSDocTypeLiteral; createJSDocSignature(typeParameters: readonly JSDocTemplateTag[] | undefined, parameters: readonly JSDocParameterTag[], type?: JSDocReturnTag): JSDocSignature; diff --git a/tests/baselines/reference/jsdocLink7.baseline b/tests/baselines/reference/jsdocLink7.baseline new file mode 100644 index 0000000000000..bb42e2b4423e1 --- /dev/null +++ b/tests/baselines/reference/jsdocLink7.baseline @@ -0,0 +1,81 @@ +// === QuickInfo === +=== /b.ts === +// /** +// * {@link import('./a').A} +// */ +// export function f() { } +// ^ +// | ---------------------------------------------------------------------- +// | function f(): void +// | {@link import('./a').A } +// | ---------------------------------------------------------------------- + +[ + { + "marker": { + "fileName": "/b.ts", + "position": 51, + "name": "" + }, + "item": { + "kind": "function", + "kindModifiers": "export", + "textSpan": { + "start": 51, + "length": 1 + }, + "displayParts": [ + { + "text": "function", + "kind": "keyword" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "f", + "kind": "functionName" + }, + { + "text": "(", + "kind": "punctuation" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "void", + "kind": "keyword" + } + ], + "documentation": [ + { + "text": "", + "kind": "text" + }, + { + "text": "{@link ", + "kind": "link" + }, + { + "text": "import('./a').A ", + "kind": "linkText" + }, + { + "text": "}", + "kind": "link" + } + ] + } + } +] \ No newline at end of file diff --git a/tests/baselines/reference/jsdocLinkTag10.symbols b/tests/baselines/reference/jsdocLinkTag10.symbols new file mode 100644 index 0000000000000..eecc9f49b4935 --- /dev/null +++ b/tests/baselines/reference/jsdocLinkTag10.symbols @@ -0,0 +1,13 @@ +//// [tests/cases/conformance/jsdoc/jsdocLinkTag10.ts] //// + +=== /a.ts === +export interface A {} +>A : Symbol(A, Decl(a.ts, 0, 0)) + +=== /b.ts === +/** + * {@link import('./a').A} + */ +export function fn() { } +>fn : Symbol(fn, Decl(b.ts, 0, 0)) + diff --git a/tests/baselines/reference/jsdocLinkTag10.types b/tests/baselines/reference/jsdocLinkTag10.types new file mode 100644 index 0000000000000..caef430d23695 --- /dev/null +++ b/tests/baselines/reference/jsdocLinkTag10.types @@ -0,0 +1,14 @@ +//// [tests/cases/conformance/jsdoc/jsdocLinkTag10.ts] //// + +=== /a.ts === + +export interface A {} + +=== /b.ts === +/** + * {@link import('./a').A} + */ +export function fn() { } +>fn : () => void +> : ^^^^^^^^^^ + diff --git a/tests/baselines/reference/jsdocLinkTag11.symbols b/tests/baselines/reference/jsdocLinkTag11.symbols new file mode 100644 index 0000000000000..31ed84cb9395b --- /dev/null +++ b/tests/baselines/reference/jsdocLinkTag11.symbols @@ -0,0 +1,13 @@ +//// [tests/cases/conformance/jsdoc/jsdocLinkTag11.ts] //// + +=== /a.js === +export class A {} +>A : Symbol(A, Decl(a.js, 0, 0)) + +=== /b.js === +/** + * {@link import('./a').A} + */ +export function fn() { } +>fn : Symbol(fn, Decl(b.js, 0, 0)) + diff --git a/tests/baselines/reference/jsdocLinkTag11.types b/tests/baselines/reference/jsdocLinkTag11.types new file mode 100644 index 0000000000000..353845bfcd01c --- /dev/null +++ b/tests/baselines/reference/jsdocLinkTag11.types @@ -0,0 +1,15 @@ +//// [tests/cases/conformance/jsdoc/jsdocLinkTag11.ts] //// + +=== /a.js === +export class A {} +>A : A +> : ^ + +=== /b.js === +/** + * {@link import('./a').A} + */ +export function fn() { } +>fn : () => void +> : ^^^^^^^^^^ + diff --git a/tests/cases/conformance/jsdoc/jsdocLinkTag10.ts b/tests/cases/conformance/jsdoc/jsdocLinkTag10.ts new file mode 100644 index 0000000000000..0671f4d497762 --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocLinkTag10.ts @@ -0,0 +1,11 @@ +// @noEmit: true +// @module: esnext + +// @filename: /a.ts +export interface A {} + +// @filename: /b.ts +/** + * {@link import('./a').A} + */ +export function fn() { } diff --git a/tests/cases/conformance/jsdoc/jsdocLinkTag11.ts b/tests/cases/conformance/jsdoc/jsdocLinkTag11.ts new file mode 100644 index 0000000000000..eb7ab988235a5 --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocLinkTag11.ts @@ -0,0 +1,13 @@ +// @checkJs: true +// @allowJs: true +// @target: esnext +// @noEmit: true + +// @filename: /a.js +export class A {} + +// @filename: /b.js +/** + * {@link import('./a').A} + */ +export function fn() { } diff --git a/tests/cases/fourslash/jsdocLink7.ts b/tests/cases/fourslash/jsdocLink7.ts new file mode 100644 index 0000000000000..15ec9259bb15c --- /dev/null +++ b/tests/cases/fourslash/jsdocLink7.ts @@ -0,0 +1,12 @@ +/// + +// @filename: /a.ts +////export function A() { }; + +// @Filename: /b.ts +/////** +//// * {@link import('./a').A} +//// */ +////export function /**/f() { } + +verify.baselineQuickInfo(); From d46eb0e26c110aa1dbdca8ef4fdab1df7ae93900 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Sun, 23 Feb 2025 01:21:03 +0200 Subject: [PATCH 2/2] update baseline --- tests/baselines/reference/jsdocLink7.baseline | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/baselines/reference/jsdocLink7.baseline b/tests/baselines/reference/jsdocLink7.baseline index bb42e2b4423e1..df2acd4df5893 100644 --- a/tests/baselines/reference/jsdocLink7.baseline +++ b/tests/baselines/reference/jsdocLink7.baseline @@ -7,7 +7,7 @@ // ^ // | ---------------------------------------------------------------------- // | function f(): void -// | {@link import('./a').A } +// | {@link import('./a').A} // | ---------------------------------------------------------------------- [ @@ -68,8 +68,15 @@ "kind": "link" }, { - "text": "import('./a').A ", - "kind": "linkText" + "text": "import('./a').A", + "kind": "linkName", + "target": { + "fileName": "/a.ts", + "textSpan": { + "start": 0, + "length": 24 + } + } }, { "text": "}",