diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index d6da4d7d1f4..447da12e438 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -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); diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 4e872d7babb..bfe41875a84 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -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(SyntaxKind.JSDocOverrideTag); }, get createJSDocDeprecatedTag() { return getJSDocSimpleTagCreateFunction(SyntaxKind.JSDocDeprecatedTag); }, get updateJSDocDeprecatedTag() { return getJSDocSimpleTagUpdateFunction(SyntaxKind.JSDocDeprecatedTag); }, + get createJSDocThrowsTag() { return getJSDocTypeLikeTagCreateFunction(SyntaxKind.JSDocThrowsTag); }, + get updateJSDocThrowsTag() { return getJSDocTypeLikeTagUpdateFunction(SyntaxKind.JSDocThrowsTag); }, createJSDocUnknownTag, updateJSDocUnknownTag, createJSDocText, diff --git a/src/compiler/factory/nodeTests.ts b/src/compiler/factory/nodeTests.ts index 14e296610c5..89c05748a9e 100644 --- a/src/compiler/factory/nodeTests.ts +++ b/src/compiler/factory/nodeTests.ts @@ -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 */ diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index f1f8bd002e8..49faf20bed2 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -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(node: JSDocSignature, cbNode: (node: Node) => T | undefined, _cbNodes?: (nodes: NodeArray) => T | undefined): T | undefined { return forEach(node.typeParameters, cbNode) || forEach(node.parameters, cbNode) || @@ -1197,7 +1199,7 @@ function forEachChildInJSDocParameterOrPropertyTag(node: JSDocParameterTag | (typeof node.comment === "string" ? undefined : visitNodes(cbNode, cbNodes, node.comment)); } -function forEachChildInJSDocReturnTag(node: JSDocReturnTag | JSDocTypeTag | JSDocThisTag | JSDocEnumTag, cbNode: (node: Node) => T | undefined, cbNodes?: (nodes: NodeArray) => T | undefined): T | undefined { +function forEachChildInJSDocTypeLikeTag(node: JSDocReturnTag | JSDocTypeTag | JSDocThisTag | JSDocEnumTag | JSDocThrowsTag, cbNode: (node: Node) => T | undefined, cbNodes?: (nodes: NodeArray) => 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(); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f8b0f5ef6de..fcfb9401520 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -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): JSDocDeprecatedTag; createJSDocOverrideTag(tagName: Identifier, comment?: string | NodeArray): JSDocOverrideTag; updateJSDocOverrideTag(node: JSDocOverrideTag, tagName: Identifier, comment?: string | NodeArray): JSDocOverrideTag; + createJSDocThrowsTag(tagName: Identifier, typeExpression: JSDocTypeExpression | undefined, comment?: string | NodeArray): JSDocThrowsTag; + updateJSDocThrowsTag(node: JSDocThrowsTag, tagName: Identifier | undefined, typeExpression: JSDocTypeExpression | undefined, comment?: string | NodeArray | undefined): JSDocThrowsTag; createJSDocText(text: string): JSDocText; updateJSDocText(node: JSDocText, text: string): JSDocText; createJSDocComment(comment?: string | NodeArray | undefined, tags?: readonly JSDocTag[] | undefined): JSDoc; diff --git a/src/services/classifier.ts b/src/services/classifier.ts index 40a37c99a62..74450102e34 100644 --- a/src/services/classifier.ts +++ b/src/services/classifier.ts @@ -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); diff --git a/src/services/completions.ts b/src/services/completions.ts index ef0be7f0fd6..c9de7d2d194 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -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; diff --git a/src/services/jsDoc.ts b/src/services/jsDoc.ts index 45e9d80f673..8e26719167b 100644 --- a/src/services/jsDoc.ts +++ b/src/services/jsDoc.ts @@ -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: diff --git a/src/testRunner/unittests/jsDocParsing.ts b/src/testRunner/unittests/jsDocParsing.ts index 21f461439c2..63701cf0c16 100644 --- a/src/testRunner/unittests/jsDocParsing.ts +++ b/src/testRunner/unittests/jsDocParsing.ts @@ -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", `/** diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.exceptionTag1.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.exceptionTag1.json new file mode 100644 index 00000000000..f2bd46a3995 --- /dev/null +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.exceptionTag1.json @@ -0,0 +1,52 @@ +{ + "kind": "JSDoc", + "pos": 0, + "end": 31, + "flags": "JSDoc", + "modifierFlagsCache": 0, + "transformFlags": 0, + "tags": { + "0": { + "kind": "JSDocThrowsTag", + "pos": 8, + "end": 29, + "modifierFlagsCache": 0, + "transformFlags": 0, + "tagName": { + "kind": "Identifier", + "pos": 9, + "end": 18, + "modifierFlagsCache": 0, + "transformFlags": 0, + "escapedText": "exception" + }, + "typeExpression": { + "kind": "JSDocTypeExpression", + "pos": 19, + "end": 26, + "modifierFlagsCache": 0, + "transformFlags": 0, + "type": { + "kind": "TypeReference", + "pos": 20, + "end": 25, + "modifierFlagsCache": 0, + "transformFlags": 1, + "typeName": { + "kind": "Identifier", + "pos": 20, + "end": 25, + "modifierFlagsCache": 0, + "transformFlags": 0, + "escapedText": "Error" + } + } + } + }, + "length": 1, + "pos": 8, + "end": 29, + "hasTrailingComma": false, + "transformFlags": 0 + } +} \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.exceptionTag2.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.exceptionTag2.json new file mode 100644 index 00000000000..457ee3d3564 --- /dev/null +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.exceptionTag2.json @@ -0,0 +1,31 @@ +{ + "kind": "JSDoc", + "pos": 0, + "end": 45, + "flags": "JSDoc", + "modifierFlagsCache": 0, + "transformFlags": 0, + "tags": { + "0": { + "kind": "JSDocThrowsTag", + "pos": 8, + "end": 43, + "modifierFlagsCache": 0, + "transformFlags": 0, + "tagName": { + "kind": "Identifier", + "pos": 9, + "end": 18, + "modifierFlagsCache": 0, + "transformFlags": 0, + "escapedText": "exception" + }, + "comment": "free-form description" + }, + "length": 1, + "pos": 8, + "end": 43, + "hasTrailingComma": false, + "transformFlags": 0 + } +} \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.exceptionTag3.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.exceptionTag3.json new file mode 100644 index 00000000000..cfc0f066b98 --- /dev/null +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.exceptionTag3.json @@ -0,0 +1,53 @@ +{ + "kind": "JSDoc", + "pos": 0, + "end": 43, + "flags": "JSDoc", + "modifierFlagsCache": 0, + "transformFlags": 0, + "tags": { + "0": { + "kind": "JSDocThrowsTag", + "pos": 8, + "end": 41, + "modifierFlagsCache": 0, + "transformFlags": 0, + "tagName": { + "kind": "Identifier", + "pos": 9, + "end": 18, + "modifierFlagsCache": 0, + "transformFlags": 0, + "escapedText": "exception" + }, + "comment": "description", + "typeExpression": { + "kind": "JSDocTypeExpression", + "pos": 19, + "end": 26, + "modifierFlagsCache": 0, + "transformFlags": 0, + "type": { + "kind": "TypeReference", + "pos": 20, + "end": 25, + "modifierFlagsCache": 0, + "transformFlags": 1, + "typeName": { + "kind": "Identifier", + "pos": 20, + "end": 25, + "modifierFlagsCache": 0, + "transformFlags": 0, + "escapedText": "Error" + } + } + } + }, + "length": 1, + "pos": 8, + "end": 41, + "hasTrailingComma": false, + "transformFlags": 0 + } +} \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.throwsTag1.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.throwsTag1.json new file mode 100644 index 00000000000..b5ba5725d70 --- /dev/null +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.throwsTag1.json @@ -0,0 +1,52 @@ +{ + "kind": "JSDoc", + "pos": 0, + "end": 28, + "flags": "JSDoc", + "modifierFlagsCache": 0, + "transformFlags": 0, + "tags": { + "0": { + "kind": "JSDocThrowsTag", + "pos": 8, + "end": 26, + "modifierFlagsCache": 0, + "transformFlags": 0, + "tagName": { + "kind": "Identifier", + "pos": 9, + "end": 15, + "modifierFlagsCache": 0, + "transformFlags": 0, + "escapedText": "throws" + }, + "typeExpression": { + "kind": "JSDocTypeExpression", + "pos": 16, + "end": 23, + "modifierFlagsCache": 0, + "transformFlags": 0, + "type": { + "kind": "TypeReference", + "pos": 17, + "end": 22, + "modifierFlagsCache": 0, + "transformFlags": 1, + "typeName": { + "kind": "Identifier", + "pos": 17, + "end": 22, + "modifierFlagsCache": 0, + "transformFlags": 0, + "escapedText": "Error" + } + } + } + }, + "length": 1, + "pos": 8, + "end": 26, + "hasTrailingComma": false, + "transformFlags": 0 + } +} \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.throwsTag2.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.throwsTag2.json new file mode 100644 index 00000000000..5f43e718a92 --- /dev/null +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.throwsTag2.json @@ -0,0 +1,31 @@ +{ + "kind": "JSDoc", + "pos": 0, + "end": 42, + "flags": "JSDoc", + "modifierFlagsCache": 0, + "transformFlags": 0, + "tags": { + "0": { + "kind": "JSDocThrowsTag", + "pos": 8, + "end": 40, + "modifierFlagsCache": 0, + "transformFlags": 0, + "tagName": { + "kind": "Identifier", + "pos": 9, + "end": 15, + "modifierFlagsCache": 0, + "transformFlags": 0, + "escapedText": "throws" + }, + "comment": "free-form description" + }, + "length": 1, + "pos": 8, + "end": 40, + "hasTrailingComma": false, + "transformFlags": 0 + } +} \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.throwsTag3.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.throwsTag3.json new file mode 100644 index 00000000000..2d48630897e --- /dev/null +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.throwsTag3.json @@ -0,0 +1,53 @@ +{ + "kind": "JSDoc", + "pos": 0, + "end": 40, + "flags": "JSDoc", + "modifierFlagsCache": 0, + "transformFlags": 0, + "tags": { + "0": { + "kind": "JSDocThrowsTag", + "pos": 8, + "end": 38, + "modifierFlagsCache": 0, + "transformFlags": 0, + "tagName": { + "kind": "Identifier", + "pos": 9, + "end": 15, + "modifierFlagsCache": 0, + "transformFlags": 0, + "escapedText": "throws" + }, + "comment": "description", + "typeExpression": { + "kind": "JSDocTypeExpression", + "pos": 16, + "end": 23, + "modifierFlagsCache": 0, + "transformFlags": 0, + "type": { + "kind": "TypeReference", + "pos": 17, + "end": 22, + "modifierFlagsCache": 0, + "transformFlags": 1, + "typeName": { + "kind": "Identifier", + "pos": 17, + "end": 22, + "modifierFlagsCache": 0, + "transformFlags": 0, + "escapedText": "Error" + } + } + } + }, + "length": 1, + "pos": 8, + "end": 38, + "hasTrailingComma": false, + "transformFlags": 0 + } +} \ No newline at end of file diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index f2133809e1a..e7d7ff77215 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -4347,14 +4347,15 @@ declare namespace ts { JSDocTypedefTag = 348, JSDocSeeTag = 349, JSDocPropertyTag = 350, - SyntaxList = 351, - NotEmittedStatement = 352, - PartiallyEmittedExpression = 353, - CommaListExpression = 354, - MergeDeclarationMarker = 355, - EndOfDeclarationMarker = 356, - SyntheticReferenceExpression = 357, - Count = 358, + JSDocThrowsTag = 351, + SyntaxList = 352, + NotEmittedStatement = 353, + PartiallyEmittedExpression = 354, + CommaListExpression = 355, + MergeDeclarationMarker = 356, + EndOfDeclarationMarker = 357, + SyntheticReferenceExpression = 358, + Count = 359, FirstAssignment = 63, LastAssignment = 78, FirstCompoundAssignment = 64, @@ -4383,9 +4384,9 @@ declare namespace ts { LastStatement = 256, FirstNode = 163, FirstJSDocNode = 312, - LastJSDocNode = 350, + LastJSDocNode = 351, FirstJSDocTagNode = 330, - LastJSDocTagNode = 350 + LastJSDocTagNode = 351 } type TriviaSyntaxKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia | SyntaxKind.NewLineTrivia | SyntaxKind.WhitespaceTrivia | SyntaxKind.ShebangTrivia | SyntaxKind.ConflictMarkerTrivia; type LiteralSyntaxKind = SyntaxKind.NumericLiteral | SyntaxKind.BigIntLiteral | SyntaxKind.StringLiteral | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.RegularExpressionLiteral | SyntaxKind.NoSubstitutionTemplateLiteral; @@ -5938,6 +5939,10 @@ declare namespace ts { readonly name?: Identifier; readonly typeExpression: JSDocSignature; } + interface JSDocThrowsTag extends JSDocTag { + readonly kind: SyntaxKind.JSDocThrowsTag; + readonly typeExpression?: JSDocTypeExpression; + } interface JSDocSignature extends JSDocType, Declaration { readonly kind: SyntaxKind.JSDocSignature; readonly typeParameters?: readonly JSDocTemplateTag[]; @@ -7799,6 +7804,8 @@ declare namespace ts { updateJSDocDeprecatedTag(node: JSDocDeprecatedTag, tagName: Identifier, comment?: string | NodeArray): JSDocDeprecatedTag; createJSDocOverrideTag(tagName: Identifier, comment?: string | NodeArray): JSDocOverrideTag; updateJSDocOverrideTag(node: JSDocOverrideTag, tagName: Identifier, comment?: string | NodeArray): JSDocOverrideTag; + createJSDocThrowsTag(tagName: Identifier, typeExpression: JSDocTypeExpression | undefined, comment?: string | NodeArray): JSDocThrowsTag; + updateJSDocThrowsTag(node: JSDocThrowsTag, tagName: Identifier | undefined, typeExpression: JSDocTypeExpression | undefined, comment?: string | NodeArray | undefined): JSDocThrowsTag; createJSDocText(text: string): JSDocText; updateJSDocText(node: JSDocText, text: string): JSDocText; createJSDocComment(comment?: string | NodeArray | undefined, tags?: readonly JSDocTag[] | undefined): JSDoc; @@ -9039,6 +9046,7 @@ declare namespace ts { function isJSDocUnknownTag(node: Node): node is JSDocUnknownTag; function isJSDocPropertyTag(node: Node): node is JSDocPropertyTag; function isJSDocImplementsTag(node: Node): node is JSDocImplementsTag; + function isJSDocThrowsTag(node: Node): node is JSDocThrowsTag; function setTextRange(range: T, location: TextRange | undefined): T; function canHaveModifiers(node: Node): node is HasModifiers; function canHaveDecorators(node: Node): node is HasDecorators; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 5504da395bb..8ce49d6142f 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -413,14 +413,15 @@ declare namespace ts { JSDocTypedefTag = 348, JSDocSeeTag = 349, JSDocPropertyTag = 350, - SyntaxList = 351, - NotEmittedStatement = 352, - PartiallyEmittedExpression = 353, - CommaListExpression = 354, - MergeDeclarationMarker = 355, - EndOfDeclarationMarker = 356, - SyntheticReferenceExpression = 357, - Count = 358, + JSDocThrowsTag = 351, + SyntaxList = 352, + NotEmittedStatement = 353, + PartiallyEmittedExpression = 354, + CommaListExpression = 355, + MergeDeclarationMarker = 356, + EndOfDeclarationMarker = 357, + SyntheticReferenceExpression = 358, + Count = 359, FirstAssignment = 63, LastAssignment = 78, FirstCompoundAssignment = 64, @@ -449,9 +450,9 @@ declare namespace ts { LastStatement = 256, FirstNode = 163, FirstJSDocNode = 312, - LastJSDocNode = 350, + LastJSDocNode = 351, FirstJSDocTagNode = 330, - LastJSDocTagNode = 350 + LastJSDocTagNode = 351 } type TriviaSyntaxKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia | SyntaxKind.NewLineTrivia | SyntaxKind.WhitespaceTrivia | SyntaxKind.ShebangTrivia | SyntaxKind.ConflictMarkerTrivia; type LiteralSyntaxKind = SyntaxKind.NumericLiteral | SyntaxKind.BigIntLiteral | SyntaxKind.StringLiteral | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.RegularExpressionLiteral | SyntaxKind.NoSubstitutionTemplateLiteral; @@ -2004,6 +2005,10 @@ declare namespace ts { readonly name?: Identifier; readonly typeExpression: JSDocSignature; } + interface JSDocThrowsTag extends JSDocTag { + readonly kind: SyntaxKind.JSDocThrowsTag; + readonly typeExpression?: JSDocTypeExpression; + } interface JSDocSignature extends JSDocType, Declaration { readonly kind: SyntaxKind.JSDocSignature; readonly typeParameters?: readonly JSDocTemplateTag[]; @@ -3865,6 +3870,8 @@ declare namespace ts { updateJSDocDeprecatedTag(node: JSDocDeprecatedTag, tagName: Identifier, comment?: string | NodeArray): JSDocDeprecatedTag; createJSDocOverrideTag(tagName: Identifier, comment?: string | NodeArray): JSDocOverrideTag; updateJSDocOverrideTag(node: JSDocOverrideTag, tagName: Identifier, comment?: string | NodeArray): JSDocOverrideTag; + createJSDocThrowsTag(tagName: Identifier, typeExpression: JSDocTypeExpression | undefined, comment?: string | NodeArray): JSDocThrowsTag; + updateJSDocThrowsTag(node: JSDocThrowsTag, tagName: Identifier | undefined, typeExpression: JSDocTypeExpression | undefined, comment?: string | NodeArray | undefined): JSDocThrowsTag; createJSDocText(text: string): JSDocText; updateJSDocText(node: JSDocText, text: string): JSDocText; createJSDocComment(comment?: string | NodeArray | undefined, tags?: readonly JSDocTag[] | undefined): JSDoc; @@ -5105,6 +5112,7 @@ declare namespace ts { function isJSDocUnknownTag(node: Node): node is JSDocUnknownTag; function isJSDocPropertyTag(node: Node): node is JSDocPropertyTag; function isJSDocImplementsTag(node: Node): node is JSDocImplementsTag; + function isJSDocThrowsTag(node: Node): node is JSDocThrowsTag; function setTextRange(range: T, location: TextRange | undefined): T; function canHaveModifiers(node: Node): node is HasModifiers; function canHaveDecorators(node: Node): node is HasDecorators; diff --git a/tests/baselines/reference/jsdocThrowsTag_findAllReferences.baseline.jsonc b/tests/baselines/reference/jsdocThrowsTag_findAllReferences.baseline.jsonc new file mode 100644 index 00000000000..d9807e1c860 --- /dev/null +++ b/tests/baselines/reference/jsdocThrowsTag_findAllReferences.baseline.jsonc @@ -0,0 +1,64 @@ +// === /tests/cases/fourslash/jsdocThrowsTag_findAllReferences.ts === +// class /*FIND ALL REFS*/[|E|] extends Error {} +// /** +// * @throws {[|E|]} +// */ +// function f() {} + +[ + { + "definition": { + "containerKind": "", + "containerName": "", + "fileName": "/tests/cases/fourslash/jsdocThrowsTag_findAllReferences.ts", + "kind": "class", + "name": "class E", + "textSpan": { + "start": 6, + "length": 1 + }, + "displayParts": [ + { + "text": "class", + "kind": "keyword" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "E", + "kind": "className" + } + ], + "contextSpan": { + "start": 0, + "length": 24 + } + }, + "references": [ + { + "textSpan": { + "start": 6, + "length": 1 + }, + "fileName": "/tests/cases/fourslash/jsdocThrowsTag_findAllReferences.ts", + "contextSpan": { + "start": 0, + "length": 24 + }, + "isWriteAccess": true, + "isDefinition": true + }, + { + "textSpan": { + "start": 41, + "length": 1 + }, + "fileName": "/tests/cases/fourslash/jsdocThrowsTag_findAllReferences.ts", + "isWriteAccess": false, + "isDefinition": false + } + ] + } +] \ No newline at end of file diff --git a/tests/baselines/reference/jsdocThrowsTag_rename.baseline b/tests/baselines/reference/jsdocThrowsTag_rename.baseline new file mode 100644 index 00000000000..8f8a2729314 --- /dev/null +++ b/tests/baselines/reference/jsdocThrowsTag_rename.baseline @@ -0,0 +1,7 @@ +/*====== /tests/cases/fourslash/jsdocThrowsTag_rename.ts ======*/ + +class [|RENAME|] extends Error {} +/** + * @throws {RENAME} + */ +function f() {} diff --git a/tests/baselines/reference/parseThrowsTag.js b/tests/baselines/reference/parseThrowsTag.js new file mode 100644 index 00000000000..4d04f59c88d --- /dev/null +++ b/tests/baselines/reference/parseThrowsTag.js @@ -0,0 +1,8 @@ +//// [parseThrowsTag.ts] +/** @throws {Error} comment */ +function f() {} + + +//// [parseThrowsTag.js] +/** @throws {Error} comment */ +function f() { } diff --git a/tests/baselines/reference/parseThrowsTag.symbols b/tests/baselines/reference/parseThrowsTag.symbols new file mode 100644 index 00000000000..ebcf6d2abbf --- /dev/null +++ b/tests/baselines/reference/parseThrowsTag.symbols @@ -0,0 +1,5 @@ +=== tests/cases/conformance/jsdoc/parseThrowsTag.ts === +/** @throws {Error} comment */ +function f() {} +>f : Symbol(f, Decl(parseThrowsTag.ts, 0, 0)) + diff --git a/tests/baselines/reference/parseThrowsTag.types b/tests/baselines/reference/parseThrowsTag.types new file mode 100644 index 00000000000..2bf758bdcc9 --- /dev/null +++ b/tests/baselines/reference/parseThrowsTag.types @@ -0,0 +1,5 @@ +=== tests/cases/conformance/jsdoc/parseThrowsTag.ts === +/** @throws {Error} comment */ +function f() {} +>f : () => void + diff --git a/tests/baselines/reference/quickInfoJsDocTags3.baseline b/tests/baselines/reference/quickInfoJsDocTags3.baseline index ab71bea1697..21140f9d202 100644 --- a/tests/baselines/reference/quickInfoJsDocTags3.baseline +++ b/tests/baselines/reference/quickInfoJsDocTags3.baseline @@ -133,7 +133,15 @@ "name": "throws", "text": [ { - "text": "{Error} comment", + "text": "{Error}", + "kind": "text" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "comment", "kind": "text" } ] diff --git a/tests/baselines/reference/quickInfoThrowsTag.baseline b/tests/baselines/reference/quickInfoThrowsTag.baseline new file mode 100644 index 00000000000..5ab73462e40 --- /dev/null +++ b/tests/baselines/reference/quickInfoThrowsTag.baseline @@ -0,0 +1,193 @@ +[ + { + "marker": { + "fileName": "/tests/cases/fourslash/quickInfoThrowsTag.ts", + "position": 170, + "name": "1" + }, + "quickInfo": { + "kind": "function", + "kindModifiers": "", + "textSpan": { + "start": 168, + "length": 2 + }, + "displayParts": [ + { + "text": "function", + "kind": "keyword" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "f1", + "kind": "functionName" + }, + { + "text": "(", + "kind": "punctuation" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "void", + "kind": "keyword" + } + ], + "documentation": [], + "tags": [ + { + "name": "throws", + "text": [ + { + "text": "{E}", + "kind": "text" + } + ] + } + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/quickInfoThrowsTag.ts", + "position": 175, + "name": "2" + }, + "quickInfo": { + "kind": "function", + "kindModifiers": "", + "textSpan": { + "start": 173, + "length": 2 + }, + "displayParts": [ + { + "text": "function", + "kind": "keyword" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "f2", + "kind": "functionName" + }, + { + "text": "(", + "kind": "punctuation" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "void", + "kind": "keyword" + } + ], + "documentation": [], + "tags": [ + { + "name": "throws", + "text": [ + { + "text": "{E}", + "kind": "text" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "description", + "kind": "text" + } + ] + } + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/quickInfoThrowsTag.ts", + "position": 180, + "name": "3" + }, + "quickInfo": { + "kind": "function", + "kindModifiers": "", + "textSpan": { + "start": 178, + "length": 2 + }, + "displayParts": [ + { + "text": "function", + "kind": "keyword" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "f3", + "kind": "functionName" + }, + { + "text": "(", + "kind": "punctuation" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "void", + "kind": "keyword" + } + ], + "documentation": [], + "tags": [ + { + "name": "throws", + "text": [ + { + "text": "description", + "kind": "text" + } + ] + } + ] + } + } +] \ No newline at end of file diff --git a/tests/cases/conformance/jsdoc/parseThrowsTag.ts b/tests/cases/conformance/jsdoc/parseThrowsTag.ts new file mode 100644 index 00000000000..90b69045f92 --- /dev/null +++ b/tests/cases/conformance/jsdoc/parseThrowsTag.ts @@ -0,0 +1,2 @@ +/** @throws {Error} comment */ +function f() {} diff --git a/tests/cases/fourslash/gotoDefinitionThrowsTag.ts b/tests/cases/fourslash/gotoDefinitionThrowsTag.ts new file mode 100644 index 00000000000..2597cf9a725 --- /dev/null +++ b/tests/cases/fourslash/gotoDefinitionThrowsTag.ts @@ -0,0 +1,11 @@ +/// + +////class [|/*def*/E|] extends Error {} +//// +/////** +//// * @throws {/*use*/[|E|]} +//// */ +////function f() {} + +goTo.marker("use"); +verify.goToDefinitionIs("def"); diff --git a/tests/cases/fourslash/jsdocThrowsTagCompletion.ts b/tests/cases/fourslash/jsdocThrowsTagCompletion.ts new file mode 100644 index 00000000000..84184de8086 --- /dev/null +++ b/tests/cases/fourslash/jsdocThrowsTagCompletion.ts @@ -0,0 +1,10 @@ +/// + +/////** +//// * @throws {/**/} description +//// */ +////function fn() {} + +verify.completions( + { marker: "", exact: completion.globalTypes }, +); diff --git a/tests/cases/fourslash/jsdocThrowsTag_findAllReferences.ts b/tests/cases/fourslash/jsdocThrowsTag_findAllReferences.ts new file mode 100644 index 00000000000..143b75e8568 --- /dev/null +++ b/tests/cases/fourslash/jsdocThrowsTag_findAllReferences.ts @@ -0,0 +1,9 @@ +/// + +////class /**/E extends Error {} +/////** +//// * @throws {E} +//// */ +////function f() {} + +verify.baselineFindAllReferences(""); diff --git a/tests/cases/fourslash/jsdocThrowsTag_rename.ts b/tests/cases/fourslash/jsdocThrowsTag_rename.ts new file mode 100644 index 00000000000..74c400eb85c --- /dev/null +++ b/tests/cases/fourslash/jsdocThrowsTag_rename.ts @@ -0,0 +1,9 @@ +/// + +////class /**/E extends Error {} +/////** +//// * @throws {E} +//// */ +////function f() {} + +verify.baselineRename("", {}); diff --git a/tests/cases/fourslash/quickInfoThrowsTag.ts b/tests/cases/fourslash/quickInfoThrowsTag.ts new file mode 100644 index 00000000000..8742770978a --- /dev/null +++ b/tests/cases/fourslash/quickInfoThrowsTag.ts @@ -0,0 +1,25 @@ +/// + +////class E extends Error {} +//// +/////** +//// * @throws {E} +//// */ +////function f1() {} +//// +/////** +//// * @throws {E} description +//// */ +////function f2() {} +//// +/////** +//// * @throws description +//// */ +////function f3() {} + +////f1/*1*/() +////f2/*2*/() +////f3/*3*/() + +verify.noErrors() +verify.baselineQuickInfo();