mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
feat(49323): Render JSDoc @throws {type} as a link (#49891)
* feat(49323): add support throws jsdoc tag * change "name" to "typeExpression". parse "exception" as a synonym for "throws" * include typeExpression from the throws tag in the quick info * add JSDocThrowsTag to ForEachChildNodes
This commit is contained in:
@@ -2122,6 +2122,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
|
||||
case SyntaxKind.JSDocReturnTag:
|
||||
case SyntaxKind.JSDocThisTag:
|
||||
case SyntaxKind.JSDocTypeTag:
|
||||
case SyntaxKind.JSDocThrowsTag:
|
||||
return emitJSDocSimpleTypedTag(node as JSDocTypeTag);
|
||||
case SyntaxKind.JSDocTemplateTag:
|
||||
return emitJSDocTemplateTag(node as JSDocTemplateTag);
|
||||
|
||||
@@ -255,6 +255,7 @@ import {
|
||||
JSDocTemplateTag,
|
||||
JSDocText,
|
||||
JSDocThisTag,
|
||||
JSDocThrowsTag,
|
||||
JSDocType,
|
||||
JSDocTypedefTag,
|
||||
JSDocTypeExpression,
|
||||
@@ -871,6 +872,8 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
|
||||
get updateJSDocOverrideTag() { return getJSDocSimpleTagUpdateFunction<JSDocOverrideTag>(SyntaxKind.JSDocOverrideTag); },
|
||||
get createJSDocDeprecatedTag() { return getJSDocSimpleTagCreateFunction<JSDocDeprecatedTag>(SyntaxKind.JSDocDeprecatedTag); },
|
||||
get updateJSDocDeprecatedTag() { return getJSDocSimpleTagUpdateFunction<JSDocDeprecatedTag>(SyntaxKind.JSDocDeprecatedTag); },
|
||||
get createJSDocThrowsTag() { return getJSDocTypeLikeTagCreateFunction<JSDocThrowsTag>(SyntaxKind.JSDocThrowsTag); },
|
||||
get updateJSDocThrowsTag() { return getJSDocTypeLikeTagUpdateFunction<JSDocThrowsTag>(SyntaxKind.JSDocThrowsTag); },
|
||||
createJSDocUnknownTag,
|
||||
updateJSDocUnknownTag,
|
||||
createJSDocText,
|
||||
|
||||
@@ -109,6 +109,7 @@ import {
|
||||
JSDocSignature,
|
||||
JSDocTemplateTag,
|
||||
JSDocThisTag,
|
||||
JSDocThrowsTag,
|
||||
JSDocTypedefTag,
|
||||
JSDocTypeExpression,
|
||||
JSDocTypeLiteral,
|
||||
@@ -1176,6 +1177,10 @@ export function isJSDocImplementsTag(node: Node): node is JSDocImplementsTag {
|
||||
return node.kind === SyntaxKind.JSDocImplementsTag;
|
||||
}
|
||||
|
||||
export function isJSDocThrowsTag(node: Node): node is JSDocThrowsTag {
|
||||
return node.kind === SyntaxKind.JSDocThrowsTag;
|
||||
}
|
||||
|
||||
// Synthesized list
|
||||
|
||||
/** @internal */
|
||||
|
||||
+17
-5
@@ -196,6 +196,7 @@ import {
|
||||
JSDocTemplateTag,
|
||||
JSDocText,
|
||||
JSDocThisTag,
|
||||
JSDocThrowsTag,
|
||||
JSDocTypedefTag,
|
||||
JSDocTypeExpression,
|
||||
JSDocTypeLiteral,
|
||||
@@ -1100,10 +1101,11 @@ const forEachChildTable: ForEachChildTable = {
|
||||
visitNode(cbNode, node.typeExpression) ||
|
||||
(typeof node.comment === "string" ? undefined : visitNodes(cbNode, cbNodes, node.comment));
|
||||
},
|
||||
[SyntaxKind.JSDocReturnTag]: forEachChildInJSDocReturnTag,
|
||||
[SyntaxKind.JSDocTypeTag]: forEachChildInJSDocReturnTag,
|
||||
[SyntaxKind.JSDocThisTag]: forEachChildInJSDocReturnTag,
|
||||
[SyntaxKind.JSDocEnumTag]: forEachChildInJSDocReturnTag,
|
||||
[SyntaxKind.JSDocReturnTag]: forEachChildInJSDocTypeLikeTag,
|
||||
[SyntaxKind.JSDocTypeTag]: forEachChildInJSDocTypeLikeTag,
|
||||
[SyntaxKind.JSDocThisTag]: forEachChildInJSDocTypeLikeTag,
|
||||
[SyntaxKind.JSDocEnumTag]: forEachChildInJSDocTypeLikeTag,
|
||||
[SyntaxKind.JSDocThrowsTag]: forEachChildInJSDocTypeLikeTag,
|
||||
[SyntaxKind.JSDocSignature]: function forEachChildInJSDocSignature<T>(node: JSDocSignature, cbNode: (node: Node) => T | undefined, _cbNodes?: (nodes: NodeArray<Node>) => T | undefined): T | undefined {
|
||||
return forEach(node.typeParameters, cbNode) ||
|
||||
forEach(node.parameters, cbNode) ||
|
||||
@@ -1197,7 +1199,7 @@ function forEachChildInJSDocParameterOrPropertyTag<T>(node: JSDocParameterTag |
|
||||
(typeof node.comment === "string" ? undefined : visitNodes(cbNode, cbNodes, node.comment));
|
||||
}
|
||||
|
||||
function forEachChildInJSDocReturnTag<T>(node: JSDocReturnTag | JSDocTypeTag | JSDocThisTag | JSDocEnumTag, cbNode: (node: Node) => T | undefined, cbNodes?: (nodes: NodeArray<Node>) => T | undefined): T | undefined {
|
||||
function forEachChildInJSDocTypeLikeTag<T>(node: JSDocReturnTag | JSDocTypeTag | JSDocThisTag | JSDocEnumTag | JSDocThrowsTag, cbNode: (node: Node) => T | undefined, cbNodes?: (nodes: NodeArray<Node>) => T | undefined): T | undefined {
|
||||
return visitNode(cbNode, node.tagName) ||
|
||||
visitNode(cbNode, node.typeExpression) ||
|
||||
(typeof node.comment === "string" ? undefined : visitNodes(cbNode, cbNodes, node.comment));
|
||||
@@ -8785,6 +8787,10 @@ namespace Parser {
|
||||
case "see":
|
||||
tag = parseSeeTag(start, tagName, margin, indentText);
|
||||
break;
|
||||
case "exception":
|
||||
case "throws":
|
||||
tag = parseThrowsTag(start, tagName, margin, indentText);
|
||||
break;
|
||||
default:
|
||||
tag = parseUnknownTag(start, tagName, margin, indentText);
|
||||
break;
|
||||
@@ -9090,6 +9096,12 @@ namespace Parser {
|
||||
return finishNode(factory.createJSDocSeeTag(tagName, nameExpression, comments), start);
|
||||
}
|
||||
|
||||
function parseThrowsTag(start: number, tagName: Identifier, indent: number, indentText: string): JSDocThrowsTag {
|
||||
const typeExpression = tryParseTypeExpression();
|
||||
const comment = parseTrailingTagComments(start, getNodePos(), indent, indentText);
|
||||
return finishNode(factory.createJSDocThrowsTag(tagName, typeExpression, comment), start);
|
||||
}
|
||||
|
||||
function parseAuthorTag(start: number, tagName: Identifier, indent: number, indentText: string): JSDocAuthorTag {
|
||||
const commentStart = getNodePos();
|
||||
const textOnly = parseAuthorNameAndEmail();
|
||||
|
||||
+11
-2
@@ -431,6 +431,7 @@ export const enum SyntaxKind {
|
||||
JSDocTypedefTag,
|
||||
JSDocSeeTag,
|
||||
JSDocPropertyTag,
|
||||
JSDocThrowsTag,
|
||||
|
||||
// Synthesized list
|
||||
SyntaxList,
|
||||
@@ -475,9 +476,9 @@ export const enum SyntaxKind {
|
||||
LastStatement = DebuggerStatement,
|
||||
FirstNode = QualifiedName,
|
||||
FirstJSDocNode = JSDocTypeExpression,
|
||||
LastJSDocNode = JSDocPropertyTag,
|
||||
LastJSDocNode = JSDocThrowsTag,
|
||||
FirstJSDocTagNode = JSDocTag,
|
||||
LastJSDocTagNode = JSDocPropertyTag,
|
||||
LastJSDocTagNode = JSDocThrowsTag,
|
||||
/** @internal */ FirstContextualKeyword = AbstractKeyword,
|
||||
/** @internal */ LastContextualKeyword = OfKeyword,
|
||||
}
|
||||
@@ -954,6 +955,7 @@ export type ForEachChildNodes =
|
||||
| JSDocProtectedTag
|
||||
| JSDocReadonlyTag
|
||||
| JSDocDeprecatedTag
|
||||
| JSDocThrowsTag
|
||||
| JSDocOverrideTag
|
||||
;
|
||||
|
||||
@@ -3827,6 +3829,11 @@ export interface JSDocCallbackTag extends JSDocTag, NamedDeclaration {
|
||||
readonly typeExpression: JSDocSignature;
|
||||
}
|
||||
|
||||
export interface JSDocThrowsTag extends JSDocTag {
|
||||
readonly kind: SyntaxKind.JSDocThrowsTag;
|
||||
readonly typeExpression?: JSDocTypeExpression;
|
||||
}
|
||||
|
||||
export interface JSDocSignature extends JSDocType, Declaration {
|
||||
readonly kind: SyntaxKind.JSDocSignature;
|
||||
readonly typeParameters?: readonly JSDocTemplateTag[];
|
||||
@@ -8257,6 +8264,8 @@ export interface NodeFactory {
|
||||
updateJSDocDeprecatedTag(node: JSDocDeprecatedTag, tagName: Identifier, comment?: string | NodeArray<JSDocComment>): JSDocDeprecatedTag;
|
||||
createJSDocOverrideTag(tagName: Identifier, comment?: string | NodeArray<JSDocComment>): JSDocOverrideTag;
|
||||
updateJSDocOverrideTag(node: JSDocOverrideTag, tagName: Identifier, comment?: string | NodeArray<JSDocComment>): JSDocOverrideTag;
|
||||
createJSDocThrowsTag(tagName: Identifier, typeExpression: JSDocTypeExpression | undefined, comment?: string | NodeArray<JSDocComment>): JSDocThrowsTag;
|
||||
updateJSDocThrowsTag(node: JSDocThrowsTag, tagName: Identifier | undefined, typeExpression: JSDocTypeExpression | undefined, comment?: string | NodeArray<JSDocComment> | undefined): JSDocThrowsTag;
|
||||
createJSDocText(text: string): JSDocText;
|
||||
updateJSDocText(node: JSDocText, text: string): JSDocText;
|
||||
createJSDocComment(comment?: string | NodeArray<JSDocComment> | undefined, tags?: readonly JSDocTag[] | undefined): JSDoc;
|
||||
|
||||
@@ -47,6 +47,7 @@ import {
|
||||
JSDocSeeTag,
|
||||
JSDocTemplateTag,
|
||||
JSDocThisTag,
|
||||
JSDocThrowsTag,
|
||||
JSDocTypedefTag,
|
||||
JSDocTypeTag,
|
||||
JsxAttribute,
|
||||
@@ -847,6 +848,11 @@ export function getEncodedSyntacticClassifications(cancellationToken: Cancellati
|
||||
case SyntaxKind.JSDocImplementsTag:
|
||||
commentStart = (tag as JSDocImplementsTag | JSDocAugmentsTag).class.end;
|
||||
break;
|
||||
case SyntaxKind.JSDocThrowsTag:
|
||||
processElement((tag as JSDocThrowsTag).typeExpression);
|
||||
pos = tag.end;
|
||||
commentStart = (tag as JSDocThrowsTag).typeExpression?.end || commentStart;
|
||||
break;
|
||||
}
|
||||
if (typeof tag.comment === "object") {
|
||||
pushCommentRange(tag.comment.pos, tag.comment.end - tag.comment.pos);
|
||||
|
||||
@@ -237,6 +237,7 @@ import {
|
||||
JSDocTag,
|
||||
JSDocTagInfo,
|
||||
JSDocTemplateTag,
|
||||
JSDocThrowsTag,
|
||||
JSDocTypedefTag,
|
||||
JSDocTypeExpression,
|
||||
JSDocTypeTag,
|
||||
@@ -2955,7 +2956,14 @@ function getCompletionData(
|
||||
flags,
|
||||
};
|
||||
|
||||
type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag | JSDocTemplateTag;
|
||||
type JSDocTagWithTypeExpression =
|
||||
| JSDocParameterTag
|
||||
| JSDocPropertyTag
|
||||
| JSDocReturnTag
|
||||
| JSDocTypeTag
|
||||
| JSDocTypedefTag
|
||||
| JSDocTemplateTag
|
||||
| JSDocThrowsTag;
|
||||
|
||||
function isTagWithTypeExpression(tag: JSDocTag): tag is JSDocTagWithTypeExpression {
|
||||
switch (tag.kind) {
|
||||
@@ -2964,6 +2972,7 @@ function getCompletionData(
|
||||
case SyntaxKind.JSDocReturnTag:
|
||||
case SyntaxKind.JSDocTypeTag:
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
case SyntaxKind.JSDocThrowsTag:
|
||||
return true;
|
||||
case SyntaxKind.JSDocTemplateTag:
|
||||
return !!(tag as JSDocTemplateTag).constraint;
|
||||
|
||||
@@ -56,6 +56,7 @@ import {
|
||||
JSDocTag,
|
||||
JSDocTagInfo,
|
||||
JSDocTemplateTag,
|
||||
JSDocThrowsTag,
|
||||
JSDocTypedefTag,
|
||||
JSDocTypeTag,
|
||||
lastOrUndefined,
|
||||
@@ -258,6 +259,10 @@ function getCommentDisplayParts(tag: JSDocTag, checker?: TypeChecker): SymbolDis
|
||||
const { comment, kind } = tag;
|
||||
const namePart = getTagNameDisplayPart(kind);
|
||||
switch (kind) {
|
||||
case SyntaxKind.JSDocThrowsTag:
|
||||
const typeExpression = (tag as JSDocThrowsTag).typeExpression;
|
||||
return typeExpression ? withNode(typeExpression) :
|
||||
comment === undefined ? undefined : getDisplayPartsFromComment(comment, checker);
|
||||
case SyntaxKind.JSDocImplementsTag:
|
||||
return withNode((tag as JSDocImplementsTag).class);
|
||||
case SyntaxKind.JSDocAugmentsTag:
|
||||
|
||||
@@ -294,6 +294,35 @@ describe("unittests:: JSDocParsing", () => {
|
||||
parsesCorrectly("paramWithoutType",
|
||||
`/**
|
||||
* @param foo
|
||||
*/`);
|
||||
parsesCorrectly("throwsTag1",
|
||||
`/**
|
||||
* @throws {Error}
|
||||
*/`);
|
||||
|
||||
parsesCorrectly("throwsTag2",
|
||||
`/**
|
||||
* @throws free-form description
|
||||
*/`);
|
||||
|
||||
parsesCorrectly("throwsTag3",
|
||||
`/**
|
||||
* @throws {Error} description
|
||||
*/`);
|
||||
|
||||
parsesCorrectly("exceptionTag1",
|
||||
`/**
|
||||
* @exception {Error}
|
||||
*/`);
|
||||
|
||||
parsesCorrectly("exceptionTag2",
|
||||
`/**
|
||||
* @exception free-form description
|
||||
*/`);
|
||||
|
||||
parsesCorrectly("exceptionTag3",
|
||||
`/**
|
||||
* @exception {Error} description
|
||||
*/`);
|
||||
parsesCorrectly("typedefTagWithChildrenTags",
|
||||
`/**
|
||||
|
||||
Reference in New Issue
Block a user