mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-11 20:37:46 -05:00
feat(47558): check JSDoc link tags in TypeScript/JavaScript files (#47822)
This commit is contained in:
@@ -37125,6 +37125,12 @@ namespace ts {
|
||||
checkSourceElement(node.typeExpression);
|
||||
}
|
||||
|
||||
function checkJSDocLinkLikeTag(node: JSDocLink | JSDocLinkCode | JSDocLinkPlain) {
|
||||
if (node.name) {
|
||||
resolveJSDocMemberName(node.name, /*ignoreErrors*/ true);
|
||||
}
|
||||
}
|
||||
|
||||
function checkJSDocParameterTag(node: JSDocParameterTag) {
|
||||
checkSourceElement(node.typeExpression);
|
||||
}
|
||||
@@ -41350,9 +41356,15 @@ namespace ts {
|
||||
}
|
||||
|
||||
function checkSourceElementWorker(node: Node): void {
|
||||
if (isInJSFile(node)) {
|
||||
forEach((node as JSDocContainer).jsDoc, ({ tags }) => forEach(tags, checkSourceElement));
|
||||
}
|
||||
forEach((node as JSDocContainer).jsDoc, ({ comment, tags }) => {
|
||||
checkJSDocCommentWorker(comment);
|
||||
forEach(tags, tag => {
|
||||
checkJSDocCommentWorker(tag.comment);
|
||||
if (isInJSFile(node)) {
|
||||
checkSourceElement(tag);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const kind = node.kind;
|
||||
if (cancellationToken) {
|
||||
@@ -41440,6 +41452,10 @@ namespace ts {
|
||||
return checkJSDocTemplateTag(node as JSDocTemplateTag);
|
||||
case SyntaxKind.JSDocTypeTag:
|
||||
return checkJSDocTypeTag(node as JSDocTypeTag);
|
||||
case SyntaxKind.JSDocLink:
|
||||
case SyntaxKind.JSDocLinkCode:
|
||||
case SyntaxKind.JSDocLinkPlain:
|
||||
return checkJSDocLinkLikeTag(node as JSDocLink | JSDocLinkCode | JSDocLinkPlain);
|
||||
case SyntaxKind.JSDocParameterTag:
|
||||
return checkJSDocParameterTag(node as JSDocParameterTag);
|
||||
case SyntaxKind.JSDocPropertyTag:
|
||||
@@ -41535,6 +41551,16 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function checkJSDocCommentWorker(node: string | readonly JSDocComment[] | undefined) {
|
||||
if (isArray(node)) {
|
||||
forEach(node, tag => {
|
||||
if (isJSDocLinkLike(tag)) {
|
||||
checkSourceElement(tag);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function checkJSDocTypeIsInJsFile(node: Node): void {
|
||||
if (!isInJSFile(node)) {
|
||||
grammarErrorOnNode(node, Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments);
|
||||
@@ -42168,7 +42194,7 @@ namespace ts {
|
||||
if (!result && isJSDoc) {
|
||||
const container = findAncestor(name, or(isClassLike, isInterfaceDeclaration));
|
||||
if (container) {
|
||||
return resolveJSDocMemberName(name, getSymbolOfNode(container));
|
||||
return resolveJSDocMemberName(name, /*ignoreErrors*/ false, getSymbolOfNode(container));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -42217,11 +42243,11 @@ namespace ts {
|
||||
*
|
||||
* For unqualified names, a container K may be provided as a second argument.
|
||||
*/
|
||||
function resolveJSDocMemberName(name: EntityName | JSDocMemberName, container?: Symbol): Symbol | undefined {
|
||||
function resolveJSDocMemberName(name: EntityName | JSDocMemberName, ignoreErrors?: boolean, container?: Symbol): Symbol | undefined {
|
||||
if (isEntityName(name)) {
|
||||
// resolve static values first
|
||||
const meaning = SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Value;
|
||||
let symbol = resolveEntityName(name, meaning, /*ignoreErrors*/ false, /*dontResolveAlias*/ true, getHostSignatureFromJSDoc(name));
|
||||
let symbol = resolveEntityName(name, meaning, ignoreErrors, /*dontResolveAlias*/ true, getHostSignatureFromJSDoc(name));
|
||||
if (!symbol && isIdentifier(name) && container) {
|
||||
symbol = getMergedSymbol(getSymbol(getExportsOfSymbol(container), name.escapedText, meaning));
|
||||
}
|
||||
@@ -42229,7 +42255,7 @@ namespace ts {
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
const left = isIdentifier(name) ? container : resolveJSDocMemberName(name.left);
|
||||
const left = isIdentifier(name) ? container : resolveJSDocMemberName(name.left, ignoreErrors, container);
|
||||
const right = isIdentifier(name) ? name.escapedText : name.right.escapedText;
|
||||
if (left) {
|
||||
const proto = left.flags & SymbolFlags.Value && getPropertyOfType(getTypeOfSymbol(left), "prototype" as __String);
|
||||
|
||||
18
tests/baselines/reference/jsdocLinkTag1.js
Normal file
18
tests/baselines/reference/jsdocLinkTag1.js
Normal file
@@ -0,0 +1,18 @@
|
||||
//// [tests/cases/conformance/jsdoc/jsdocLinkTag1.ts] ////
|
||||
|
||||
//// [a.ts]
|
||||
export interface A {}
|
||||
|
||||
//// [b.ts]
|
||||
import type { A } from "./a";
|
||||
|
||||
/** {@link A} */
|
||||
export interface B {}
|
||||
|
||||
|
||||
//// [a.js]
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
//// [b.js]
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
12
tests/baselines/reference/jsdocLinkTag1.symbols
Normal file
12
tests/baselines/reference/jsdocLinkTag1.symbols
Normal file
@@ -0,0 +1,12 @@
|
||||
=== /a.ts ===
|
||||
export interface A {}
|
||||
>A : Symbol(A, Decl(a.ts, 0, 0))
|
||||
|
||||
=== /b.ts ===
|
||||
import type { A } from "./a";
|
||||
>A : Symbol(A, Decl(b.ts, 0, 13))
|
||||
|
||||
/** {@link A} */
|
||||
export interface B {}
|
||||
>B : Symbol(B, Decl(b.ts, 0, 29))
|
||||
|
||||
10
tests/baselines/reference/jsdocLinkTag1.types
Normal file
10
tests/baselines/reference/jsdocLinkTag1.types
Normal file
@@ -0,0 +1,10 @@
|
||||
=== /a.ts ===
|
||||
export interface A {}
|
||||
No type information for this code.
|
||||
No type information for this code.=== /b.ts ===
|
||||
import type { A } from "./a";
|
||||
>A : A
|
||||
|
||||
/** {@link A} */
|
||||
export interface B {}
|
||||
|
||||
13
tests/baselines/reference/jsdocLinkTag2.symbols
Normal file
13
tests/baselines/reference/jsdocLinkTag2.symbols
Normal file
@@ -0,0 +1,13 @@
|
||||
=== /a.js ===
|
||||
export class A {}
|
||||
>A : Symbol(A, Decl(a.js, 0, 0))
|
||||
|
||||
=== /b.js ===
|
||||
import { A } from "./a";
|
||||
>A : Symbol(A, Decl(b.js, 0, 8))
|
||||
|
||||
/** {@link A} */
|
||||
export class B {}
|
||||
>B : Symbol(B, Decl(b.js, 0, 24))
|
||||
|
||||
|
||||
13
tests/baselines/reference/jsdocLinkTag2.types
Normal file
13
tests/baselines/reference/jsdocLinkTag2.types
Normal file
@@ -0,0 +1,13 @@
|
||||
=== /a.js ===
|
||||
export class A {}
|
||||
>A : A
|
||||
|
||||
=== /b.js ===
|
||||
import { A } from "./a";
|
||||
>A : typeof A
|
||||
|
||||
/** {@link A} */
|
||||
export class B {}
|
||||
>B : B
|
||||
|
||||
|
||||
26
tests/baselines/reference/jsdocLinkTag3.js
Normal file
26
tests/baselines/reference/jsdocLinkTag3.js
Normal file
@@ -0,0 +1,26 @@
|
||||
//// [tests/cases/conformance/jsdoc/jsdocLinkTag3.ts] ////
|
||||
|
||||
//// [a.ts]
|
||||
export interface A {}
|
||||
|
||||
//// [b.ts]
|
||||
import type { A } from "./a";
|
||||
|
||||
/**
|
||||
* @param {number} a - see {@link A}
|
||||
*/
|
||||
export function foo(a: string) {}
|
||||
|
||||
|
||||
//// [a.js]
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
//// [b.js]
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
exports.foo = void 0;
|
||||
/**
|
||||
* @param {number} a - see {@link A}
|
||||
*/
|
||||
function foo(a) { }
|
||||
exports.foo = foo;
|
||||
15
tests/baselines/reference/jsdocLinkTag3.symbols
Normal file
15
tests/baselines/reference/jsdocLinkTag3.symbols
Normal file
@@ -0,0 +1,15 @@
|
||||
=== /a.ts ===
|
||||
export interface A {}
|
||||
>A : Symbol(A, Decl(a.ts, 0, 0))
|
||||
|
||||
=== /b.ts ===
|
||||
import type { A } from "./a";
|
||||
>A : Symbol(A, Decl(b.ts, 0, 13))
|
||||
|
||||
/**
|
||||
* @param {number} a - see {@link A}
|
||||
*/
|
||||
export function foo(a: string) {}
|
||||
>foo : Symbol(foo, Decl(b.ts, 0, 29))
|
||||
>a : Symbol(a, Decl(b.ts, 5, 20))
|
||||
|
||||
14
tests/baselines/reference/jsdocLinkTag3.types
Normal file
14
tests/baselines/reference/jsdocLinkTag3.types
Normal file
@@ -0,0 +1,14 @@
|
||||
=== /a.ts ===
|
||||
export interface A {}
|
||||
No type information for this code.
|
||||
No type information for this code.=== /b.ts ===
|
||||
import type { A } from "./a";
|
||||
>A : A
|
||||
|
||||
/**
|
||||
* @param {number} a - see {@link A}
|
||||
*/
|
||||
export function foo(a: string) {}
|
||||
>foo : (a: string) => void
|
||||
>a : string
|
||||
|
||||
26
tests/baselines/reference/jsdocLinkTag4.js
Normal file
26
tests/baselines/reference/jsdocLinkTag4.js
Normal file
@@ -0,0 +1,26 @@
|
||||
//// [tests/cases/conformance/jsdoc/jsdocLinkTag4.ts] ////
|
||||
|
||||
//// [a.ts]
|
||||
export interface A {}
|
||||
|
||||
//// [b.ts]
|
||||
import * as a from "./a";
|
||||
|
||||
/**
|
||||
* @param {number} a - see {@link a.A}
|
||||
*/
|
||||
export function foo(a: string) {}
|
||||
|
||||
|
||||
//// [a.js]
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
//// [b.js]
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
exports.foo = void 0;
|
||||
/**
|
||||
* @param {number} a - see {@link a.A}
|
||||
*/
|
||||
function foo(a) { }
|
||||
exports.foo = foo;
|
||||
15
tests/baselines/reference/jsdocLinkTag4.symbols
Normal file
15
tests/baselines/reference/jsdocLinkTag4.symbols
Normal file
@@ -0,0 +1,15 @@
|
||||
=== /a.ts ===
|
||||
export interface A {}
|
||||
>A : Symbol(A, Decl(a.ts, 0, 0))
|
||||
|
||||
=== /b.ts ===
|
||||
import * as a from "./a";
|
||||
>a : Symbol(a, Decl(b.ts, 0, 6))
|
||||
|
||||
/**
|
||||
* @param {number} a - see {@link a.A}
|
||||
*/
|
||||
export function foo(a: string) {}
|
||||
>foo : Symbol(foo, Decl(b.ts, 0, 25))
|
||||
>a : Symbol(a, Decl(b.ts, 5, 20))
|
||||
|
||||
14
tests/baselines/reference/jsdocLinkTag4.types
Normal file
14
tests/baselines/reference/jsdocLinkTag4.types
Normal file
@@ -0,0 +1,14 @@
|
||||
=== /a.ts ===
|
||||
export interface A {}
|
||||
No type information for this code.
|
||||
No type information for this code.=== /b.ts ===
|
||||
import * as a from "./a";
|
||||
>a : typeof a
|
||||
|
||||
/**
|
||||
* @param {number} a - see {@link a.A}
|
||||
*/
|
||||
export function foo(a: string) {}
|
||||
>foo : (a: string) => void
|
||||
>a : string
|
||||
|
||||
8
tests/baselines/reference/jsdocLinkTag5.js
Normal file
8
tests/baselines/reference/jsdocLinkTag5.js
Normal file
@@ -0,0 +1,8 @@
|
||||
//// [a.ts]
|
||||
/** {@link UNRESOLVED_LINK} */
|
||||
export interface A {}
|
||||
|
||||
|
||||
//// [a.js]
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
5
tests/baselines/reference/jsdocLinkTag5.symbols
Normal file
5
tests/baselines/reference/jsdocLinkTag5.symbols
Normal file
@@ -0,0 +1,5 @@
|
||||
=== /a.ts ===
|
||||
/** {@link UNRESOLVED_LINK} */
|
||||
export interface A {}
|
||||
>A : Symbol(A, Decl(a.ts, 0, 0))
|
||||
|
||||
5
tests/baselines/reference/jsdocLinkTag5.types
Normal file
5
tests/baselines/reference/jsdocLinkTag5.types
Normal file
@@ -0,0 +1,5 @@
|
||||
=== /a.ts ===
|
||||
/** {@link UNRESOLVED_LINK} */
|
||||
No type information for this code.export interface A {}
|
||||
No type information for this code.
|
||||
No type information for this code.
|
||||
9
tests/cases/conformance/jsdoc/jsdocLinkTag1.ts
Normal file
9
tests/cases/conformance/jsdoc/jsdocLinkTag1.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
// @noUnusedLocals: true
|
||||
// @filename: /a.ts
|
||||
export interface A {}
|
||||
|
||||
// @filename: /b.ts
|
||||
import type { A } from "./a";
|
||||
|
||||
/** {@link A} */
|
||||
export interface B {}
|
||||
15
tests/cases/conformance/jsdoc/jsdocLinkTag2.ts
Normal file
15
tests/cases/conformance/jsdoc/jsdocLinkTag2.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
// @noUnusedLocals: true
|
||||
// @checkJs: true
|
||||
// @allowJs: true
|
||||
// @target: esnext
|
||||
// @noEmit: true
|
||||
|
||||
// @filename: /a.js
|
||||
export class A {}
|
||||
|
||||
// @filename: /b.js
|
||||
import { A } from "./a";
|
||||
|
||||
/** {@link A} */
|
||||
export class B {}
|
||||
|
||||
11
tests/cases/conformance/jsdoc/jsdocLinkTag3.ts
Normal file
11
tests/cases/conformance/jsdoc/jsdocLinkTag3.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
// @noUnusedLocals: true
|
||||
// @filename: /a.ts
|
||||
export interface A {}
|
||||
|
||||
// @filename: /b.ts
|
||||
import type { A } from "./a";
|
||||
|
||||
/**
|
||||
* @param {number} a - see {@link A}
|
||||
*/
|
||||
export function foo(a: string) {}
|
||||
11
tests/cases/conformance/jsdoc/jsdocLinkTag4.ts
Normal file
11
tests/cases/conformance/jsdoc/jsdocLinkTag4.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
// @noUnusedLocals: true
|
||||
// @filename: /a.ts
|
||||
export interface A {}
|
||||
|
||||
// @filename: /b.ts
|
||||
import * as a from "./a";
|
||||
|
||||
/**
|
||||
* @param {number} a - see {@link a.A}
|
||||
*/
|
||||
export function foo(a: string) {}
|
||||
5
tests/cases/conformance/jsdoc/jsdocLinkTag5.ts
Normal file
5
tests/cases/conformance/jsdoc/jsdocLinkTag5.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
// @noUnusedLocals: true
|
||||
// @filename: /a.ts
|
||||
|
||||
/** {@link UNRESOLVED_LINK} */
|
||||
export interface A {}
|
||||
Reference in New Issue
Block a user