Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] feat(43950): support import types in jsdoc links #61248

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}

Expand Down
12 changes: 6 additions & 6 deletions src/compiler/factory/nodeFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5396,45 +5396,45 @@ 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<JSDocLink>(SyntaxKind.JSDocLink);
node.name = name;
node.text = text;
return node;
}

// @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<JSDocLinkCode>(SyntaxKind.JSDocLinkCode);
node.name = name;
node.text = text;
return node;
}

// @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<JSDocLinkPlain>(SyntaxKind.JSDocLinkPlain);
node.name = name;
node.text = text;
return node;
}

// @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;
Expand Down
4 changes: 3 additions & 1 deletion src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9288,8 +9288,10 @@ namespace Parser {

function parseJSDocLinkName() {
if (tokenIsIdentifierOrKeyword(token())) {
if (token() === SyntaxKind.ImportKeyword) {
return parseImportType();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sandersn should we reuse parseImportType, or would creating a new node to handle this case be better?

}
const pos = getNodePos();

let name: EntityName | JSDocMemberName = parseIdentifierName();
while (parseOptional(SyntaxKind.DotToken)) {
name = finishNode(factory.createQualifiedName(name, token() === SyntaxKind.PrivateIdentifier ? createMissingNode<Identifier>(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ false) : parseIdentifierName()), pos);
Expand Down
18 changes: 9 additions & 9 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down
3 changes: 1 addition & 2 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12099,15 +12099,14 @@ export function forEachDynamicImportOrRequireCall<IncludeTypeSpaceImports extend

/** Returns a token if position is in [start-of-leading-trivia, end), includes JSDoc only in JS files */
function getNodeAtPosition(sourceFile: SourceFile, position: number, includeJSDoc: boolean): Node {
const isJavaScriptFile = isInJSFile(sourceFile);
let current: Node = sourceFile;
const getContainingChild = (child: Node) => {
if (child.pos <= position && (position < child.end || (position === child.end && (child.kind === SyntaxKind.EndOfFileToken)))) {
return child;
}
};
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;
}
Expand Down
4 changes: 3 additions & 1 deletion src/compiler/utilitiesPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ import {
getLeadingCommentRanges,
getLeadingCommentRangesOfNode,
getSourceFileOfNode,
getTextOfNode,
getTrailingCommentRanges,
hasAccessorModifier,
HasDecorators,
Expand Down Expand Up @@ -133,6 +134,7 @@ import {
isFunctionTypeNode,
isIdentifier,
isImportSpecifier,
isImportTypeNode,
isInJSFile,
isJSDoc,
isJSDocAugmentsTag,
Expand Down Expand Up @@ -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}}`;
}
Expand Down
18 changes: 9 additions & 9 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
88 changes: 88 additions & 0 deletions tests/baselines/reference/jsdocLink7.baseline
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// === 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": "linkName",
"target": {
"fileName": "/a.ts",
"textSpan": {
"start": 0,
"length": 24
}
}
},
{
"text": "}",
"kind": "link"
}
]
}
}
]
13 changes: 13 additions & 0 deletions tests/baselines/reference/jsdocLinkTag10.symbols
Original file line number Diff line number Diff line change
@@ -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))

14 changes: 14 additions & 0 deletions tests/baselines/reference/jsdocLinkTag10.types
Original file line number Diff line number Diff line change
@@ -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
> : ^^^^^^^^^^

13 changes: 13 additions & 0 deletions tests/baselines/reference/jsdocLinkTag11.symbols
Original file line number Diff line number Diff line change
@@ -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))

15 changes: 15 additions & 0 deletions tests/baselines/reference/jsdocLinkTag11.types
Original file line number Diff line number Diff line change
@@ -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
> : ^^^^^^^^^^

11 changes: 11 additions & 0 deletions tests/cases/conformance/jsdoc/jsdocLinkTag10.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// @noEmit: true
// @module: esnext

// @filename: /a.ts
export interface A {}

// @filename: /b.ts
/**
* {@link import('./a').A}
*/
export function fn() { }
13 changes: 13 additions & 0 deletions tests/cases/conformance/jsdoc/jsdocLinkTag11.ts
Original file line number Diff line number Diff line change
@@ -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() { }
12 changes: 12 additions & 0 deletions tests/cases/fourslash/jsdocLink7.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/// <reference path='fourslash.ts' />

// @filename: /a.ts
////export function A() { };

// @Filename: /b.ts
/////**
//// * {@link import('./a').A}
//// */
////export function /**/f() { }

verify.baselineQuickInfo();