From 2326ed685246c2b64d5c0342c4e2b439d23ec38c Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Wed, 13 Jul 2022 13:35:35 +0300 Subject: [PATCH 1/4] feat(49323): add support throws jsdoc tag --- src/compiler/emitter.ts | 9 + src/compiler/factory/nodeFactory.ts | 19 ++ src/compiler/factory/nodeTests.ts | 4 + src/compiler/parser.ts | 18 ++ src/compiler/types.ts | 12 +- src/services/classifier.ts | 3 + src/services/jsDoc.ts | 3 +- src/testRunner/unittests/jsDocParsing.ts | 16 ++ ...ocComments.parsesCorrectly.throwsTag1.json | 45 ++++ ...ocComments.parsesCorrectly.throwsTag2.json | 31 +++ ...ocComments.parsesCorrectly.throwsTag3.json | 46 +++++ .../reference/api/tsserverlibrary.d.ts | 28 ++- tests/baselines/reference/api/typescript.d.ts | 28 ++- ...ThrowsTag_findAllReferences.baseline.jsonc | 64 ++++++ .../reference/jsdocThrowsTag_rename.baseline | 7 + tests/baselines/reference/parseThrowsTag.js | 8 + .../reference/parseThrowsTag.symbols | 5 + .../baselines/reference/parseThrowsTag.types | 5 + .../reference/quickInfoJsDocTags3.baseline | 10 +- .../reference/quickInfoThrowsTag.baseline | 193 ++++++++++++++++++ .../cases/conformance/jsdoc/parseThrowsTag.ts | 2 + .../fourslash/gotoDefinitionThrowsTag.ts | 11 + .../jsdocThrowsTag_findAllReferences.ts | 9 + .../cases/fourslash/jsdocThrowsTag_rename.ts | 9 + tests/cases/fourslash/quickInfoThrowsTag.ts | 25 +++ 25 files changed, 586 insertions(+), 24 deletions(-) create mode 100644 tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.throwsTag1.json create mode 100644 tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.throwsTag2.json create mode 100644 tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.throwsTag3.json create mode 100644 tests/baselines/reference/jsdocThrowsTag_findAllReferences.baseline.jsonc create mode 100644 tests/baselines/reference/jsdocThrowsTag_rename.baseline create mode 100644 tests/baselines/reference/parseThrowsTag.js create mode 100644 tests/baselines/reference/parseThrowsTag.symbols create mode 100644 tests/baselines/reference/parseThrowsTag.types create mode 100644 tests/baselines/reference/quickInfoThrowsTag.baseline create mode 100644 tests/cases/conformance/jsdoc/parseThrowsTag.ts create mode 100644 tests/cases/fourslash/gotoDefinitionThrowsTag.ts create mode 100644 tests/cases/fourslash/jsdocThrowsTag_findAllReferences.ts create mode 100644 tests/cases/fourslash/jsdocThrowsTag_rename.ts create mode 100644 tests/cases/fourslash/quickInfoThrowsTag.ts diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 8c7f218ca1012..f443b16dc3888 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1670,6 +1670,9 @@ namespace ts { return emitJSDocTypedefTag(node as JSDocTypedefTag); case SyntaxKind.JSDocSeeTag: return emitJSDocSeeTag(node as JSDocSeeTag); + case SyntaxKind.JSDocThrowsTag: + return emitJSDocThrowsTag(node as JSDocThrowsTag); + // SyntaxKind.JSDocPropertyTag (see JSDocParameterTag, above) // Transformation nodes @@ -3939,6 +3942,12 @@ namespace ts { emitJSDocComment(param.comment); } + function emitJSDocThrowsTag(tag: JSDocThrowsTag) { + emitJSDocTagName(tag.tagName); + emit(tag.name); + emitJSDocComment(tag.comment); + } + function emitJSDocTagName(tagName: Identifier) { writePunctuation("@"); emit(tagName); diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 76a8c405d722b..1aee262453eee 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -365,6 +365,9 @@ namespace ts { updateJSDocLinkCode, createJSDocLinkPlain, updateJSDocLinkPlain, + createJSDocThrowsTag, + updateJSDocThrowsTag, + // lazily load factory members for JSDoc tags with similar structure get createJSDocTypeTag() { return getJSDocTypeLikeTagCreateFunction(SyntaxKind.JSDocTypeTag); }, get updateJSDocTypeTag() { return getJSDocTypeLikeTagUpdateFunction(SyntaxKind.JSDocTypeTag); }, @@ -4731,6 +4734,22 @@ namespace ts { : node; } + // @api + function createJSDocThrowsTag(tagName: Identifier, name: JSDocNameReference | undefined, comment?: string | NodeArray): JSDocThrowsTag { + const node = createBaseJSDocTag(SyntaxKind.JSDocThrowsTag, tagName, comment); + node.name = name; + return node; + } + + // @api + function updateJSDocThrowsTag(node: JSDocThrowsTag, tagName: Identifier, name: JSDocNameReference | undefined, comment?: string | NodeArray): JSDocThrowsTag { + return node.tagName !== tagName + || node.name !== name + || node.comment !== comment + ? update(createJSDocThrowsTag(tagName, name, comment), node) + : node; + } + // @api // createJSDocAuthorTag // createJSDocClassTag diff --git a/src/compiler/factory/nodeTests.ts b/src/compiler/factory/nodeTests.ts index 903dea149a191..2eb7088553d37 100644 --- a/src/compiler/factory/nodeTests.ts +++ b/src/compiler/factory/nodeTests.ts @@ -942,6 +942,10 @@ namespace ts { 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 f9aa3e57498ed..b02b970adffbf 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -645,6 +645,10 @@ namespace ts { return visitNode(cbNode, (node as JSDocTag).tagName) || visitNode(cbNode, (node as JSDocReturnTag | JSDocTypeTag | JSDocThisTag | JSDocEnumTag).typeExpression) || (typeof (node as JSDoc).comment === "string" ? undefined : visitNodes(cbNode, cbNodes, (node as JSDoc).comment as NodeArray | undefined)); + case SyntaxKind.JSDocThrowsTag: + return visitNode(cbNode, (node as JSDocThrowsTag).tagName) || + visitNode(cbNode, (node as JSDocThrowsTag).name) || + (typeof (node as JSDoc).comment === "string" ? undefined : visitNodes(cbNode, cbNodes, (node as JSDoc).comment as NodeArray | undefined)); case SyntaxKind.JSDocSignature: return forEach((node as JSDocSignature).typeParameters, cbNode) || forEach((node as JSDocSignature).parameters, cbNode) || @@ -8200,6 +8204,9 @@ namespace ts { case "see": tag = parseSeeTag(start, tagName, margin, indentText); break; + case "throws": + tag = parseThrowsTag(start, tagName, margin, indentText); + break; default: tag = parseUnknownTag(start, tagName, margin, indentText); break; @@ -8400,6 +8407,11 @@ namespace ts { return token() === SyntaxKind.OpenBraceToken ? parseJSDocTypeExpression() : undefined; } + function tryParseJSDocNameReference(): JSDocNameReference | undefined { + skipWhitespaceOrAsterisk(); + return token() === SyntaxKind.OpenBraceToken ? parseJSDocNameReference() : undefined; + } + function parseBracketNameInPropertyAndParamTag(): { name: EntityName, isBracketed: boolean } { // Looking for something like '[foo]', 'foo', '[foo.bar]' or 'foo.bar' const isBracketed = parseOptionalJsdoc(SyntaxKind.OpenBracketToken); @@ -8505,6 +8517,12 @@ namespace ts { return finishNode(factory.createJSDocSeeTag(tagName, nameExpression, comments), start); } + function parseThrowsTag(start: number, tagName: Identifier, indent: number, indentText: string): JSDocThrowsTag { + const name = tryParseJSDocNameReference(); + const comment = parseTrailingTagComments(start, getNodePos(), indent, indentText); + return finishNode(factory.createJSDocThrowsTag(tagName, name, 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 f93f36c369157..64b66e7c41098 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -409,6 +409,7 @@ namespace ts { JSDocTypedefTag, JSDocSeeTag, JSDocPropertyTag, + JSDocThrowsTag, // Synthesized list SyntaxList, @@ -453,9 +454,9 @@ namespace ts { LastStatement = DebuggerStatement, FirstNode = QualifiedName, FirstJSDocNode = JSDocTypeExpression, - LastJSDocNode = JSDocPropertyTag, + LastJSDocNode = JSDocThrowsTag, FirstJSDocTagNode = JSDocTag, - LastJSDocTagNode = JSDocPropertyTag, + LastJSDocTagNode = JSDocThrowsTag, /* @internal */ FirstContextualKeyword = AbstractKeyword, /* @internal */ LastContextualKeyword = OfKeyword, } @@ -3573,6 +3574,11 @@ namespace ts { readonly typeExpression: JSDocSignature; } + export interface JSDocThrowsTag extends JSDocTag { + readonly kind: SyntaxKind.JSDocThrowsTag; + readonly name?: JSDocNameReference; + } + export interface JSDocSignature extends JSDocType, Declaration { readonly kind: SyntaxKind.JSDocSignature; readonly typeParameters?: readonly JSDocTemplateTag[]; @@ -7843,6 +7849,8 @@ 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, name?: JSDocNameReference, comment?: string | NodeArray): JSDocThrowsTag; + updateJSDocThrowsTag(node: JSDocThrowsTag, tagName: Identifier | undefined, name?: JSDocNameReference | 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 ed8445b8f3987..c3e6c1fcceff7 100644 --- a/src/services/classifier.ts +++ b/src/services/classifier.ts @@ -767,6 +767,9 @@ namespace ts { case SyntaxKind.JSDocImplementsTag: commentStart = (tag as JSDocImplementsTag | JSDocAugmentsTag).class.end; break; + case SyntaxKind.JSDocThrowsTag: + commentStart = (tag as JSDocThrowsTag).name?.end || commentStart; + break; } if (typeof tag.comment === "object") { pushCommentRange(tag.comment.pos, tag.comment.end - tag.comment.pos); diff --git a/src/services/jsDoc.ts b/src/services/jsDoc.ts index e85246e9eb5b9..b0242c5f0104c 100644 --- a/src/services/jsDoc.ts +++ b/src/services/jsDoc.ts @@ -196,12 +196,13 @@ namespace ts.JsDoc { return displayParts; case SyntaxKind.JSDocTypeTag: return withNode((tag as JSDocTypeTag).typeExpression); + case SyntaxKind.JSDocThrowsTag: case SyntaxKind.JSDocTypedefTag: case SyntaxKind.JSDocCallbackTag: case SyntaxKind.JSDocPropertyTag: case SyntaxKind.JSDocParameterTag: case SyntaxKind.JSDocSeeTag: - const { name } = tag as JSDocTypedefTag | JSDocCallbackTag | JSDocPropertyTag | JSDocParameterTag | JSDocSeeTag; + const { name } = tag as JSDocThrowsTag | JSDocTypedefTag | JSDocCallbackTag | JSDocPropertyTag | JSDocParameterTag | JSDocSeeTag; return name ? withNode(name) : comment === undefined ? undefined : getDisplayPartsFromComment(comment, checker); diff --git a/src/testRunner/unittests/jsDocParsing.ts b/src/testRunner/unittests/jsDocParsing.ts index 0e544e7f77c95..de311be120528 100644 --- a/src/testRunner/unittests/jsDocParsing.ts +++ b/src/testRunner/unittests/jsDocParsing.ts @@ -292,6 +292,22 @@ namespace ts { `/** * @param foo */`); + + parsesCorrectly("throwsTag1", + `/** + * @throws {Error} + */`); + + parsesCorrectly("throwsTag2", + `/** + * @throws free-form description + */`); + + parsesCorrectly("throwsTag3", + `/** + * @throws {Error} description + */`); + parsesCorrectly("typedefTagWithChildrenTags", `/** * @typedef People 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 0000000000000..37b0a1320f436 --- /dev/null +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.throwsTag1.json @@ -0,0 +1,45 @@ +{ + "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" + }, + "name": { + "kind": "JSDocNameReference", + "pos": 16, + "end": 23, + "modifierFlagsCache": 0, + "transformFlags": 0, + "name": { + "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 0000000000000..5f43e718a9220 --- /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 0000000000000..1b189cb66999f --- /dev/null +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.throwsTag3.json @@ -0,0 +1,46 @@ +{ + "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", + "name": { + "kind": "JSDocNameReference", + "pos": 16, + "end": 23, + "modifierFlagsCache": 0, + "transformFlags": 0, + "name": { + "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 23bfc30ee197c..b5f9aa3b7a14a 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -455,14 +455,15 @@ declare namespace ts { JSDocTypedefTag = 345, JSDocSeeTag = 346, JSDocPropertyTag = 347, - SyntaxList = 348, - NotEmittedStatement = 349, - PartiallyEmittedExpression = 350, - CommaListExpression = 351, - MergeDeclarationMarker = 352, - EndOfDeclarationMarker = 353, - SyntheticReferenceExpression = 354, - Count = 355, + JSDocThrowsTag = 348, + SyntaxList = 349, + NotEmittedStatement = 350, + PartiallyEmittedExpression = 351, + CommaListExpression = 352, + MergeDeclarationMarker = 353, + EndOfDeclarationMarker = 354, + SyntheticReferenceExpression = 355, + Count = 356, FirstAssignment = 63, LastAssignment = 78, FirstCompoundAssignment = 64, @@ -491,9 +492,9 @@ declare namespace ts { LastStatement = 253, FirstNode = 161, FirstJSDocNode = 309, - LastJSDocNode = 347, + LastJSDocNode = 348, FirstJSDocTagNode = 327, - LastJSDocTagNode = 347, + LastJSDocTagNode = 348, } export type TriviaSyntaxKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia | SyntaxKind.NewLineTrivia | SyntaxKind.WhitespaceTrivia | SyntaxKind.ShebangTrivia | SyntaxKind.ConflictMarkerTrivia; export type LiteralSyntaxKind = SyntaxKind.NumericLiteral | SyntaxKind.BigIntLiteral | SyntaxKind.StringLiteral | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.RegularExpressionLiteral | SyntaxKind.NoSubstitutionTemplateLiteral; @@ -1967,6 +1968,10 @@ declare namespace ts { readonly name?: Identifier; readonly typeExpression: JSDocSignature; } + export interface JSDocThrowsTag extends JSDocTag { + readonly kind: SyntaxKind.JSDocThrowsTag; + readonly name?: JSDocNameReference; + } export interface JSDocSignature extends JSDocType, Declaration { readonly kind: SyntaxKind.JSDocSignature; readonly typeParameters?: readonly JSDocTemplateTag[]; @@ -3750,6 +3755,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, name?: JSDocNameReference, comment?: string | NodeArray): JSDocThrowsTag; + updateJSDocThrowsTag(node: JSDocThrowsTag, tagName: Identifier | undefined, name?: JSDocNameReference | 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; @@ -4825,6 +4832,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; } declare namespace ts { function setTextRange(range: T, location: TextRange | undefined): T; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 18ef4ee23f826..e847a6b28d063 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -455,14 +455,15 @@ declare namespace ts { JSDocTypedefTag = 345, JSDocSeeTag = 346, JSDocPropertyTag = 347, - SyntaxList = 348, - NotEmittedStatement = 349, - PartiallyEmittedExpression = 350, - CommaListExpression = 351, - MergeDeclarationMarker = 352, - EndOfDeclarationMarker = 353, - SyntheticReferenceExpression = 354, - Count = 355, + JSDocThrowsTag = 348, + SyntaxList = 349, + NotEmittedStatement = 350, + PartiallyEmittedExpression = 351, + CommaListExpression = 352, + MergeDeclarationMarker = 353, + EndOfDeclarationMarker = 354, + SyntheticReferenceExpression = 355, + Count = 356, FirstAssignment = 63, LastAssignment = 78, FirstCompoundAssignment = 64, @@ -491,9 +492,9 @@ declare namespace ts { LastStatement = 253, FirstNode = 161, FirstJSDocNode = 309, - LastJSDocNode = 347, + LastJSDocNode = 348, FirstJSDocTagNode = 327, - LastJSDocTagNode = 347, + LastJSDocTagNode = 348, } export type TriviaSyntaxKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia | SyntaxKind.NewLineTrivia | SyntaxKind.WhitespaceTrivia | SyntaxKind.ShebangTrivia | SyntaxKind.ConflictMarkerTrivia; export type LiteralSyntaxKind = SyntaxKind.NumericLiteral | SyntaxKind.BigIntLiteral | SyntaxKind.StringLiteral | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.RegularExpressionLiteral | SyntaxKind.NoSubstitutionTemplateLiteral; @@ -1967,6 +1968,10 @@ declare namespace ts { readonly name?: Identifier; readonly typeExpression: JSDocSignature; } + export interface JSDocThrowsTag extends JSDocTag { + readonly kind: SyntaxKind.JSDocThrowsTag; + readonly name?: JSDocNameReference; + } export interface JSDocSignature extends JSDocType, Declaration { readonly kind: SyntaxKind.JSDocSignature; readonly typeParameters?: readonly JSDocTemplateTag[]; @@ -3750,6 +3755,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, name?: JSDocNameReference, comment?: string | NodeArray): JSDocThrowsTag; + updateJSDocThrowsTag(node: JSDocThrowsTag, tagName: Identifier | undefined, name?: JSDocNameReference | 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; @@ -4825,6 +4832,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; } declare namespace ts { function setTextRange(range: T, location: TextRange | undefined): T; diff --git a/tests/baselines/reference/jsdocThrowsTag_findAllReferences.baseline.jsonc b/tests/baselines/reference/jsdocThrowsTag_findAllReferences.baseline.jsonc new file mode 100644 index 0000000000000..d9807e1c86066 --- /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 0000000000000..8f8a2729314b1 --- /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 0000000000000..4d04f59c88df4 --- /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 0000000000000..ebcf6d2abbf7d --- /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 0000000000000..2bf758bdcc986 --- /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 ab71bea16976d..21140f9d20255 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 0000000000000..5ab73462e405f --- /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 0000000000000..90b69045f9265 --- /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 0000000000000..2597cf9a7255d --- /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/jsdocThrowsTag_findAllReferences.ts b/tests/cases/fourslash/jsdocThrowsTag_findAllReferences.ts new file mode 100644 index 0000000000000..143b75e8568ab --- /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 0000000000000..74c400eb85c6d --- /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 0000000000000..8742770978a98 --- /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(); From f9063f1982d8d20d410bb1a53338afc93077b2a5 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Thu, 28 Jul 2022 10:40:17 +0300 Subject: [PATCH 2/4] change "name" to "typeExpression". parse "exception" as a synonym for "throws" --- src/compiler/emitter.ts | 10 +--- src/compiler/factory/nodeFactory.ts | 22 ++------ src/compiler/parser.ts | 17 ++---- src/compiler/types.ts | 6 +-- src/services/classifier.ts | 4 +- src/services/completions.ts | 10 +++- src/services/jsDoc.ts | 3 +- src/testRunner/unittests/jsDocParsing.ts | 15 ++++++ ...omments.parsesCorrectly.exceptionTag1.json | 52 ++++++++++++++++++ ...omments.parsesCorrectly.exceptionTag2.json | 31 +++++++++++ ...omments.parsesCorrectly.exceptionTag3.json | 53 +++++++++++++++++++ ...ocComments.parsesCorrectly.throwsTag1.json | 19 ++++--- ...ocComments.parsesCorrectly.throwsTag3.json | 19 ++++--- .../reference/api/tsserverlibrary.d.ts | 6 +-- tests/baselines/reference/api/typescript.d.ts | 6 +-- .../reference/quickInfoJsDocTags3.baseline | 8 --- .../reference/quickInfoThrowsTag.baseline | 16 +----- .../fourslash/jsdocThrowsTagCompletion.ts | 10 ++++ 18 files changed, 219 insertions(+), 88 deletions(-) create mode 100644 tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.exceptionTag1.json create mode 100644 tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.exceptionTag2.json create mode 100644 tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.exceptionTag3.json create mode 100644 tests/cases/fourslash/jsdocThrowsTagCompletion.ts diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index f443b16dc3888..855693095ed0b 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1663,6 +1663,7 @@ namespace ts { 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); @@ -1670,9 +1671,6 @@ namespace ts { return emitJSDocTypedefTag(node as JSDocTypedefTag); case SyntaxKind.JSDocSeeTag: return emitJSDocSeeTag(node as JSDocSeeTag); - case SyntaxKind.JSDocThrowsTag: - return emitJSDocThrowsTag(node as JSDocThrowsTag); - // SyntaxKind.JSDocPropertyTag (see JSDocParameterTag, above) // Transformation nodes @@ -3942,12 +3940,6 @@ namespace ts { emitJSDocComment(param.comment); } - function emitJSDocThrowsTag(tag: JSDocThrowsTag) { - emitJSDocTagName(tag.tagName); - emit(tag.name); - emitJSDocComment(tag.comment); - } - function emitJSDocTagName(tagName: Identifier) { writePunctuation("@"); emit(tagName); diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 1aee262453eee..3b7fa3e6aee31 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -365,9 +365,6 @@ namespace ts { updateJSDocLinkCode, createJSDocLinkPlain, updateJSDocLinkPlain, - createJSDocThrowsTag, - updateJSDocThrowsTag, - // lazily load factory members for JSDoc tags with similar structure get createJSDocTypeTag() { return getJSDocTypeLikeTagCreateFunction(SyntaxKind.JSDocTypeTag); }, get updateJSDocTypeTag() { return getJSDocTypeLikeTagUpdateFunction(SyntaxKind.JSDocTypeTag); }, @@ -393,6 +390,9 @@ namespace ts { 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, @@ -4734,22 +4734,6 @@ namespace ts { : node; } - // @api - function createJSDocThrowsTag(tagName: Identifier, name: JSDocNameReference | undefined, comment?: string | NodeArray): JSDocThrowsTag { - const node = createBaseJSDocTag(SyntaxKind.JSDocThrowsTag, tagName, comment); - node.name = name; - return node; - } - - // @api - function updateJSDocThrowsTag(node: JSDocThrowsTag, tagName: Identifier, name: JSDocNameReference | undefined, comment?: string | NodeArray): JSDocThrowsTag { - return node.tagName !== tagName - || node.name !== name - || node.comment !== comment - ? update(createJSDocThrowsTag(tagName, name, comment), node) - : node; - } - // @api // createJSDocAuthorTag // createJSDocClassTag diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b02b970adffbf..f0bf4723715ab 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -642,12 +642,9 @@ namespace ts { case SyntaxKind.JSDocTypeTag: case SyntaxKind.JSDocThisTag: case SyntaxKind.JSDocEnumTag: - return visitNode(cbNode, (node as JSDocTag).tagName) || - visitNode(cbNode, (node as JSDocReturnTag | JSDocTypeTag | JSDocThisTag | JSDocEnumTag).typeExpression) || - (typeof (node as JSDoc).comment === "string" ? undefined : visitNodes(cbNode, cbNodes, (node as JSDoc).comment as NodeArray | undefined)); case SyntaxKind.JSDocThrowsTag: - return visitNode(cbNode, (node as JSDocThrowsTag).tagName) || - visitNode(cbNode, (node as JSDocThrowsTag).name) || + return visitNode(cbNode, (node as JSDocTag).tagName) || + visitNode(cbNode, (node as JSDocReturnTag | JSDocTypeTag | JSDocThisTag | JSDocEnumTag | JSDocThrowsTag).typeExpression) || (typeof (node as JSDoc).comment === "string" ? undefined : visitNodes(cbNode, cbNodes, (node as JSDoc).comment as NodeArray | undefined)); case SyntaxKind.JSDocSignature: return forEach((node as JSDocSignature).typeParameters, cbNode) || @@ -8204,6 +8201,7 @@ namespace ts { case "see": tag = parseSeeTag(start, tagName, margin, indentText); break; + case "exception": case "throws": tag = parseThrowsTag(start, tagName, margin, indentText); break; @@ -8407,11 +8405,6 @@ namespace ts { return token() === SyntaxKind.OpenBraceToken ? parseJSDocTypeExpression() : undefined; } - function tryParseJSDocNameReference(): JSDocNameReference | undefined { - skipWhitespaceOrAsterisk(); - return token() === SyntaxKind.OpenBraceToken ? parseJSDocNameReference() : undefined; - } - function parseBracketNameInPropertyAndParamTag(): { name: EntityName, isBracketed: boolean } { // Looking for something like '[foo]', 'foo', '[foo.bar]' or 'foo.bar' const isBracketed = parseOptionalJsdoc(SyntaxKind.OpenBracketToken); @@ -8518,9 +8511,9 @@ namespace ts { } function parseThrowsTag(start: number, tagName: Identifier, indent: number, indentText: string): JSDocThrowsTag { - const name = tryParseJSDocNameReference(); + const typeExpression = tryParseTypeExpression(); const comment = parseTrailingTagComments(start, getNodePos(), indent, indentText); - return finishNode(factory.createJSDocThrowsTag(tagName, name, comment), start); + return finishNode(factory.createJSDocThrowsTag(tagName, typeExpression, comment), start); } function parseAuthorTag(start: number, tagName: Identifier, indent: number, indentText: string): JSDocAuthorTag { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 64b66e7c41098..1106aa5e7e22a 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3576,7 +3576,7 @@ namespace ts { export interface JSDocThrowsTag extends JSDocTag { readonly kind: SyntaxKind.JSDocThrowsTag; - readonly name?: JSDocNameReference; + readonly typeExpression?: JSDocTypeExpression; } export interface JSDocSignature extends JSDocType, Declaration { @@ -7849,8 +7849,8 @@ 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, name?: JSDocNameReference, comment?: string | NodeArray): JSDocThrowsTag; - updateJSDocThrowsTag(node: JSDocThrowsTag, tagName: Identifier | undefined, name?: JSDocNameReference | undefined, comment?: string | NodeArray | undefined): JSDocThrowsTag; + 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 c3e6c1fcceff7..c8fe61f0710f4 100644 --- a/src/services/classifier.ts +++ b/src/services/classifier.ts @@ -768,7 +768,9 @@ namespace ts { commentStart = (tag as JSDocImplementsTag | JSDocAugmentsTag).class.end; break; case SyntaxKind.JSDocThrowsTag: - commentStart = (tag as JSDocThrowsTag).name?.end || commentStart; + processElement((tag as JSDocThrowsTag).typeExpression); + pos = tag.end; + commentStart = (tag as JSDocThrowsTag).typeExpression?.end || commentStart; break; } if (typeof tag.comment === "object") { diff --git a/src/services/completions.ts b/src/services/completions.ts index c24bc8dcaf165..e1f212d921b5f 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -2249,7 +2249,14 @@ namespace ts.Completions { 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) { @@ -2258,6 +2265,7 @@ namespace ts.Completions { 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 b0242c5f0104c..e85246e9eb5b9 100644 --- a/src/services/jsDoc.ts +++ b/src/services/jsDoc.ts @@ -196,13 +196,12 @@ namespace ts.JsDoc { return displayParts; case SyntaxKind.JSDocTypeTag: return withNode((tag as JSDocTypeTag).typeExpression); - case SyntaxKind.JSDocThrowsTag: case SyntaxKind.JSDocTypedefTag: case SyntaxKind.JSDocCallbackTag: case SyntaxKind.JSDocPropertyTag: case SyntaxKind.JSDocParameterTag: case SyntaxKind.JSDocSeeTag: - const { name } = tag as JSDocThrowsTag | JSDocTypedefTag | JSDocCallbackTag | JSDocPropertyTag | JSDocParameterTag | JSDocSeeTag; + const { name } = tag as JSDocTypedefTag | JSDocCallbackTag | JSDocPropertyTag | JSDocParameterTag | JSDocSeeTag; return name ? withNode(name) : comment === undefined ? undefined : getDisplayPartsFromComment(comment, checker); diff --git a/src/testRunner/unittests/jsDocParsing.ts b/src/testRunner/unittests/jsDocParsing.ts index de311be120528..4d636370f8574 100644 --- a/src/testRunner/unittests/jsDocParsing.ts +++ b/src/testRunner/unittests/jsDocParsing.ts @@ -306,6 +306,21 @@ namespace ts { 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 0000000000000..f2bd46a399511 --- /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 0000000000000..457ee3d3564fe --- /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 0000000000000..cfc0f066b9894 --- /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 index 37b0a1320f436..b5ba5725d70dc 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.throwsTag1.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.throwsTag1.json @@ -20,19 +20,26 @@ "transformFlags": 0, "escapedText": "throws" }, - "name": { - "kind": "JSDocNameReference", + "typeExpression": { + "kind": "JSDocTypeExpression", "pos": 16, "end": 23, "modifierFlagsCache": 0, "transformFlags": 0, - "name": { - "kind": "Identifier", + "type": { + "kind": "TypeReference", "pos": 17, "end": 22, "modifierFlagsCache": 0, - "transformFlags": 0, - "escapedText": "Error" + "transformFlags": 1, + "typeName": { + "kind": "Identifier", + "pos": 17, + "end": 22, + "modifierFlagsCache": 0, + "transformFlags": 0, + "escapedText": "Error" + } } } }, diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.throwsTag3.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.throwsTag3.json index 1b189cb66999f..2d48630897e6e 100644 --- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.throwsTag3.json +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.throwsTag3.json @@ -21,19 +21,26 @@ "escapedText": "throws" }, "comment": "description", - "name": { - "kind": "JSDocNameReference", + "typeExpression": { + "kind": "JSDocTypeExpression", "pos": 16, "end": 23, "modifierFlagsCache": 0, "transformFlags": 0, - "name": { - "kind": "Identifier", + "type": { + "kind": "TypeReference", "pos": 17, "end": 22, "modifierFlagsCache": 0, - "transformFlags": 0, - "escapedText": "Error" + "transformFlags": 1, + "typeName": { + "kind": "Identifier", + "pos": 17, + "end": 22, + "modifierFlagsCache": 0, + "transformFlags": 0, + "escapedText": "Error" + } } } }, diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index b5f9aa3b7a14a..5b162cd723dee 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -1970,7 +1970,7 @@ declare namespace ts { } export interface JSDocThrowsTag extends JSDocTag { readonly kind: SyntaxKind.JSDocThrowsTag; - readonly name?: JSDocNameReference; + readonly typeExpression?: JSDocTypeExpression; } export interface JSDocSignature extends JSDocType, Declaration { readonly kind: SyntaxKind.JSDocSignature; @@ -3755,8 +3755,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, name?: JSDocNameReference, comment?: string | NodeArray): JSDocThrowsTag; - updateJSDocThrowsTag(node: JSDocThrowsTag, tagName: Identifier | undefined, name?: JSDocNameReference | undefined, comment?: string | NodeArray | undefined): JSDocThrowsTag; + 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/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index e847a6b28d063..9336e4ac7f1a7 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -1970,7 +1970,7 @@ declare namespace ts { } export interface JSDocThrowsTag extends JSDocTag { readonly kind: SyntaxKind.JSDocThrowsTag; - readonly name?: JSDocNameReference; + readonly typeExpression?: JSDocTypeExpression; } export interface JSDocSignature extends JSDocType, Declaration { readonly kind: SyntaxKind.JSDocSignature; @@ -3755,8 +3755,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, name?: JSDocNameReference, comment?: string | NodeArray): JSDocThrowsTag; - updateJSDocThrowsTag(node: JSDocThrowsTag, tagName: Identifier | undefined, name?: JSDocNameReference | undefined, comment?: string | NodeArray | undefined): JSDocThrowsTag; + 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/tests/baselines/reference/quickInfoJsDocTags3.baseline b/tests/baselines/reference/quickInfoJsDocTags3.baseline index 21140f9d20255..82e0049993c78 100644 --- a/tests/baselines/reference/quickInfoJsDocTags3.baseline +++ b/tests/baselines/reference/quickInfoJsDocTags3.baseline @@ -132,14 +132,6 @@ { "name": "throws", "text": [ - { - "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 index 5ab73462e405f..c7230a04e0e90 100644 --- a/tests/baselines/reference/quickInfoThrowsTag.baseline +++ b/tests/baselines/reference/quickInfoThrowsTag.baseline @@ -49,13 +49,7 @@ "documentation": [], "tags": [ { - "name": "throws", - "text": [ - { - "text": "{E}", - "kind": "text" - } - ] + "name": "throws" } ] } @@ -112,14 +106,6 @@ { "name": "throws", "text": [ - { - "text": "{E}", - "kind": "text" - }, - { - "text": " ", - "kind": "space" - }, { "text": "description", "kind": "text" diff --git a/tests/cases/fourslash/jsdocThrowsTagCompletion.ts b/tests/cases/fourslash/jsdocThrowsTagCompletion.ts new file mode 100644 index 0000000000000..84184de808686 --- /dev/null +++ b/tests/cases/fourslash/jsdocThrowsTagCompletion.ts @@ -0,0 +1,10 @@ +/// + +/////** +//// * @throws {/**/} description +//// */ +////function fn() {} + +verify.completions( + { marker: "", exact: completion.globalTypes }, +); From 42943c4842642b2f6ebe54a42777ec110a85bba8 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Sat, 30 Jul 2022 10:21:27 +0300 Subject: [PATCH 3/4] include typeExpression from the throws tag in the quick info --- src/services/jsDoc.ts | 4 ++++ .../reference/quickInfoJsDocTags3.baseline | 8 ++++++++ .../reference/quickInfoThrowsTag.baseline | 16 +++++++++++++++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/services/jsDoc.ts b/src/services/jsDoc.ts index e85246e9eb5b9..229483a051705 100644 --- a/src/services/jsDoc.ts +++ b/src/services/jsDoc.ts @@ -168,6 +168,10 @@ namespace ts.JsDoc { 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/tests/baselines/reference/quickInfoJsDocTags3.baseline b/tests/baselines/reference/quickInfoJsDocTags3.baseline index 82e0049993c78..21140f9d20255 100644 --- a/tests/baselines/reference/quickInfoJsDocTags3.baseline +++ b/tests/baselines/reference/quickInfoJsDocTags3.baseline @@ -132,6 +132,14 @@ { "name": "throws", "text": [ + { + "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 index c7230a04e0e90..5ab73462e405f 100644 --- a/tests/baselines/reference/quickInfoThrowsTag.baseline +++ b/tests/baselines/reference/quickInfoThrowsTag.baseline @@ -49,7 +49,13 @@ "documentation": [], "tags": [ { - "name": "throws" + "name": "throws", + "text": [ + { + "text": "{E}", + "kind": "text" + } + ] } ] } @@ -106,6 +112,14 @@ { "name": "throws", "text": [ + { + "text": "{E}", + "kind": "text" + }, + { + "text": " ", + "kind": "space" + }, { "text": "description", "kind": "text" From 6abe62fb5d447f41a00ce664d663d44e60e8849b Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Sat, 27 Aug 2022 16:11:22 +0300 Subject: [PATCH 4/4] add JSDocThrowsTag to ForEachChildNodes --- src/compiler/parser.ts | 11 ++++++----- src/compiler/types.ts | 1 + 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 55197630ed881..0e560dbe88fc2 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -712,10 +712,11 @@ namespace ts { 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) || @@ -808,7 +809,7 @@ namespace ts { (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)); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 73ed396ae2c06..16f83e2bfcd7f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -932,6 +932,7 @@ namespace ts { | JSDocProtectedTag | JSDocReadonlyTag | JSDocDeprecatedTag + | JSDocThrowsTag ; /* @internal */