From b902a71e5f11af732a52a9adee9778b668c7b211 Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Thu, 25 Jul 2019 17:10:31 -0400 Subject: [PATCH 1/4] Handle namepaths inside JSDoc type expressions a bit better - fixes #31298 --- src/compiler/parser.ts | 8 +++ src/compiler/types.ts | 7 +++ .../reference/api/tsserverlibrary.d.ts | 57 ++++++++++--------- tests/baselines/reference/api/typescript.d.ts | 57 ++++++++++--------- .../noAssertForUnparseableTypedefs.errors.txt | 9 +-- .../fourslash/jsDocDontBreakWithNamespaces.ts | 17 ++++++ 6 files changed, 97 insertions(+), 58 deletions(-) create mode 100644 tests/cases/fourslash/jsDocDontBreakWithNamespaces.ts diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 254be93000b..9762302b7a2 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2429,8 +2429,16 @@ namespace ts { function parseJSDocType(): TypeNode { scanner.setInJSDocType(true); const dotdotdot = parseOptionalToken(SyntaxKind.DotDotDotToken); + const moduleSpecifier = parseOptionalToken(SyntaxKind.ModuleKeyword); let type = parseTypeOrTypePredicate(); scanner.setInJSDocType(false); + if (moduleSpecifier) { + const moduleTag = createNode(SyntaxKind.JSDocNamepathType, moduleSpecifier.pos) as JSDocNamepathType; + while (token() !== SyntaxKind.CloseBraceToken && token() !== SyntaxKind.EndOfFileToken) { + nextTokenJSDoc(); + } + type = finishNode(moduleTag); + } if (dotdotdot) { const variadic = createNode(SyntaxKind.JSDocVariadicType, dotdotdot.pos) as JSDocVariadicType; variadic.type = type; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 67c85147aed..e1461071e20 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -455,6 +455,8 @@ namespace ts { JSDocOptionalType, JSDocFunctionType, JSDocVariadicType, + // https://jsdoc.app/about-namepaths.html + JSDocNamepathType, JSDocComment, JSDocTypeLiteral, JSDocSignature, @@ -2430,6 +2432,11 @@ namespace ts { type: TypeNode; } + export interface JSDocNamepathType extends JSDocType { + kind: SyntaxKind.JSDocNamepathType; + type: TypeNode; + } + export type JSDocTypeReferencingNode = JSDocVariadicType | JSDocOptionalType | JSDocNullableType | JSDocNonNullableType; export interface JSDoc extends Node { diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index c2bad61505b..549cb6368f9 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -374,29 +374,30 @@ declare namespace ts { JSDocOptionalType = 294, JSDocFunctionType = 295, JSDocVariadicType = 296, - JSDocComment = 297, - JSDocTypeLiteral = 298, - JSDocSignature = 299, - JSDocTag = 300, - JSDocAugmentsTag = 301, - JSDocAuthorTag = 302, - JSDocClassTag = 303, - JSDocCallbackTag = 304, - JSDocEnumTag = 305, - JSDocParameterTag = 306, - JSDocReturnTag = 307, - JSDocThisTag = 308, - JSDocTypeTag = 309, - JSDocTemplateTag = 310, - JSDocTypedefTag = 311, - JSDocPropertyTag = 312, - SyntaxList = 313, - NotEmittedStatement = 314, - PartiallyEmittedExpression = 315, - CommaListExpression = 316, - MergeDeclarationMarker = 317, - EndOfDeclarationMarker = 318, - Count = 319, + JSDocNamepathType = 297, + JSDocComment = 298, + JSDocTypeLiteral = 299, + JSDocSignature = 300, + JSDocTag = 301, + JSDocAugmentsTag = 302, + JSDocAuthorTag = 303, + JSDocClassTag = 304, + JSDocCallbackTag = 305, + JSDocEnumTag = 306, + JSDocParameterTag = 307, + JSDocReturnTag = 308, + JSDocThisTag = 309, + JSDocTypeTag = 310, + JSDocTemplateTag = 311, + JSDocTypedefTag = 312, + JSDocPropertyTag = 313, + SyntaxList = 314, + NotEmittedStatement = 315, + PartiallyEmittedExpression = 316, + CommaListExpression = 317, + MergeDeclarationMarker = 318, + EndOfDeclarationMarker = 319, + Count = 320, FirstAssignment = 60, LastAssignment = 72, FirstCompoundAssignment = 61, @@ -423,9 +424,9 @@ declare namespace ts { LastBinaryOperator = 72, FirstNode = 149, FirstJSDocNode = 289, - LastJSDocNode = 312, - FirstJSDocTagNode = 300, - LastJSDocTagNode = 312, + LastJSDocNode = 313, + FirstJSDocTagNode = 301, + LastJSDocTagNode = 313, } enum NodeFlags { None = 0, @@ -1558,6 +1559,10 @@ declare namespace ts { kind: SyntaxKind.JSDocVariadicType; type: TypeNode; } + interface JSDocNamepathType extends JSDocType { + kind: SyntaxKind.JSDocNamepathType; + type: TypeNode; + } type JSDocTypeReferencingNode = JSDocVariadicType | JSDocOptionalType | JSDocNullableType | JSDocNonNullableType; interface JSDoc extends Node { kind: SyntaxKind.JSDocComment; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 7a2559bbc22..935aff356f8 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -374,29 +374,30 @@ declare namespace ts { JSDocOptionalType = 294, JSDocFunctionType = 295, JSDocVariadicType = 296, - JSDocComment = 297, - JSDocTypeLiteral = 298, - JSDocSignature = 299, - JSDocTag = 300, - JSDocAugmentsTag = 301, - JSDocAuthorTag = 302, - JSDocClassTag = 303, - JSDocCallbackTag = 304, - JSDocEnumTag = 305, - JSDocParameterTag = 306, - JSDocReturnTag = 307, - JSDocThisTag = 308, - JSDocTypeTag = 309, - JSDocTemplateTag = 310, - JSDocTypedefTag = 311, - JSDocPropertyTag = 312, - SyntaxList = 313, - NotEmittedStatement = 314, - PartiallyEmittedExpression = 315, - CommaListExpression = 316, - MergeDeclarationMarker = 317, - EndOfDeclarationMarker = 318, - Count = 319, + JSDocNamepathType = 297, + JSDocComment = 298, + JSDocTypeLiteral = 299, + JSDocSignature = 300, + JSDocTag = 301, + JSDocAugmentsTag = 302, + JSDocAuthorTag = 303, + JSDocClassTag = 304, + JSDocCallbackTag = 305, + JSDocEnumTag = 306, + JSDocParameterTag = 307, + JSDocReturnTag = 308, + JSDocThisTag = 309, + JSDocTypeTag = 310, + JSDocTemplateTag = 311, + JSDocTypedefTag = 312, + JSDocPropertyTag = 313, + SyntaxList = 314, + NotEmittedStatement = 315, + PartiallyEmittedExpression = 316, + CommaListExpression = 317, + MergeDeclarationMarker = 318, + EndOfDeclarationMarker = 319, + Count = 320, FirstAssignment = 60, LastAssignment = 72, FirstCompoundAssignment = 61, @@ -423,9 +424,9 @@ declare namespace ts { LastBinaryOperator = 72, FirstNode = 149, FirstJSDocNode = 289, - LastJSDocNode = 312, - FirstJSDocTagNode = 300, - LastJSDocTagNode = 312, + LastJSDocNode = 313, + FirstJSDocTagNode = 301, + LastJSDocTagNode = 313, } enum NodeFlags { None = 0, @@ -1558,6 +1559,10 @@ declare namespace ts { kind: SyntaxKind.JSDocVariadicType; type: TypeNode; } + interface JSDocNamepathType extends JSDocType { + kind: SyntaxKind.JSDocNamepathType; + type: TypeNode; + } type JSDocTypeReferencingNode = JSDocVariadicType | JSDocOptionalType | JSDocNullableType | JSDocNonNullableType; interface JSDoc extends Node { kind: SyntaxKind.JSDocComment; diff --git a/tests/baselines/reference/noAssertForUnparseableTypedefs.errors.txt b/tests/baselines/reference/noAssertForUnparseableTypedefs.errors.txt index b131c8adac0..a0b1f6210ba 100644 --- a/tests/baselines/reference/noAssertForUnparseableTypedefs.errors.txt +++ b/tests/baselines/reference/noAssertForUnparseableTypedefs.errors.txt @@ -1,14 +1,11 @@ -tests/cases/conformance/jsdoc/bug26693.js(1,15): error TS2580: Cannot find name 'module'. Do you need to install type definitions for node? Try `npm i @types/node`. -tests/cases/conformance/jsdoc/bug26693.js(1,21): error TS1005: '}' expected. +tests/cases/conformance/jsdoc/bug26693.js(1,21): error TS1110: Type expected. tests/cases/conformance/jsdoc/bug26693.js(2,22): error TS2307: Cannot find module 'nope'. -==== tests/cases/conformance/jsdoc/bug26693.js (3 errors) ==== +==== tests/cases/conformance/jsdoc/bug26693.js (2 errors) ==== /** @typedef {module:locale} hi */ - ~~~~~~ -!!! error TS2580: Cannot find name 'module'. Do you need to install type definitions for node? Try `npm i @types/node`. ~ -!!! error TS1005: '}' expected. +!!! error TS1110: Type expected. import { nope } from 'nope'; ~~~~~~ !!! error TS2307: Cannot find module 'nope'. diff --git a/tests/cases/fourslash/jsDocDontBreakWithNamespaces.ts b/tests/cases/fourslash/jsDocDontBreakWithNamespaces.ts new file mode 100644 index 00000000000..7807d5d30d1 --- /dev/null +++ b/tests/cases/fourslash/jsDocDontBreakWithNamespaces.ts @@ -0,0 +1,17 @@ +/// +// @allowJs: true +// @Filename: 31298.js +/////** +//// * @returns {module:@nodefuel/web~Webserver~wsServer#hello} Websocket server object +//// */ +////function foo() { } +////foo(''/**/); + +verify.signatureHelp({ + marker: "", + text: "foo(): any", + docComment: "", + tags: [ + { name: "returns", text: "Websocket server object" }, + ], +}); From 30aad9db8dc6fe09fe4e21fe973e984c35c201dc Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Mon, 29 Jul 2019 09:46:42 -0400 Subject: [PATCH 2/4] Support more terminators for parsing jsdoc filepaths --- src/compiler/parser.ts | 12 ++++++----- src/compiler/scanner.ts | 2 +- src/harness/fourslash.ts | 4 ++++ src/services/classifier.ts | 1 + .../fourslash/jsDocDontBreakWithNamespaces.ts | 20 ++++++++++++++++--- 5 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 9762302b7a2..b585adb04ac 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2428,17 +2428,19 @@ namespace ts { function parseJSDocType(): TypeNode { scanner.setInJSDocType(true); - const dotdotdot = parseOptionalToken(SyntaxKind.DotDotDotToken); const moduleSpecifier = parseOptionalToken(SyntaxKind.ModuleKeyword); - let type = parseTypeOrTypePredicate(); - scanner.setInJSDocType(false); if (moduleSpecifier) { const moduleTag = createNode(SyntaxKind.JSDocNamepathType, moduleSpecifier.pos) as JSDocNamepathType; - while (token() !== SyntaxKind.CloseBraceToken && token() !== SyntaxKind.EndOfFileToken) { + const terminators = [SyntaxKind.CloseBraceToken, SyntaxKind.EndOfFileToken, SyntaxKind.CommaToken, SyntaxKind.CloseParenToken]; + while (terminators.indexOf(token()) < 0) { nextTokenJSDoc(); } - type = finishNode(moduleTag); + return finishNode(moduleTag); } + + const dotdotdot = parseOptionalToken(SyntaxKind.DotDotDotToken); + let type = parseTypeOrTypePredicate(); + scanner.setInJSDocType(false); if (dotdotdot) { const variadic = createNode(SyntaxKind.JSDocVariadicType, dotdotdot.pos) as JSDocVariadicType; variadic.type = type; diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index f949893171a..3109da91788 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -1983,7 +1983,7 @@ namespace ts { // First non-whitespace character on this line. let firstNonWhitespace = 0; // These initial values are special because the first line is: - // firstNonWhitespace = 0 to indicate that we want leading whitspace, + // firstNonWhitespace = 0 to indicate that we want leading whitespace, while (pos < end) { char = text.charCodeAt(pos); diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index f12506be2fb..b59f4e15dc4 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1268,6 +1268,10 @@ namespace FourSlash { private verifySignatureHelpWorker(options: FourSlashInterface.VerifySignatureHelpOptions) { const help = this.getSignatureHelp({ triggerReason: options.triggerReason })!; + if (!help) { + this.raiseError("Could not get a help signature"); + } + const selectedItem = help.items[help.selectedItemIndex]; // Argument index may exceed number of parameters const currentParameter = selectedItem.parameters[help.argumentIndex] as ts.SignatureHelpParameter | undefined; diff --git a/src/services/classifier.ts b/src/services/classifier.ts index f85a69681de..5da0354dc13 100644 --- a/src/services/classifier.ts +++ b/src/services/classifier.ts @@ -1,4 +1,5 @@ namespace ts { + /** The classifier is used for syntactic highlighting in editors via the TSServer */ export function createClassifier(): Classifier { const scanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ false); diff --git a/tests/cases/fourslash/jsDocDontBreakWithNamespaces.ts b/tests/cases/fourslash/jsDocDontBreakWithNamespaces.ts index 7807d5d30d1..5be2f3e7b62 100644 --- a/tests/cases/fourslash/jsDocDontBreakWithNamespaces.ts +++ b/tests/cases/fourslash/jsDocDontBreakWithNamespaces.ts @@ -5,13 +5,27 @@ //// * @returns {module:@nodefuel/web~Webserver~wsServer#hello} Websocket server object //// */ ////function foo() { } -////foo(''/**/); +////foo(''/*foo*/); +//// +/////** +//// * @type {module:xxxxx} */ +//// */ +////function bar() { } +////bar(''/*bar*/); + verify.signatureHelp({ - marker: "", + marker: "foo", text: "foo(): any", docComment: "", tags: [ - { name: "returns", text: "Websocket server object" }, + { name: "returns", text: "Websocket server object" }, ], }); + +verify.signatureHelp({ + marker: "bar", + text: "bar(): void", + docComment: "", + tags: [], +}); From 77cdca2971faedc0030f8860c812833549de2a48 Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Tue, 6 Aug 2019 15:45:28 -0400 Subject: [PATCH 3/4] Adds another test around parsing jsdoc --- .../fourslash/jsDocDontBreakWithNamespaces.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/cases/fourslash/jsDocDontBreakWithNamespaces.ts b/tests/cases/fourslash/jsDocDontBreakWithNamespaces.ts index 5be2f3e7b62..12899d23df3 100644 --- a/tests/cases/fourslash/jsDocDontBreakWithNamespaces.ts +++ b/tests/cases/fourslash/jsDocDontBreakWithNamespaces.ts @@ -12,6 +12,10 @@ //// */ ////function bar() { } ////bar(''/*bar*/); +//// +/////** @type {function(module:xxxx, module:xxxx): module:xxxxx} */ +////function zee() { } +////zee(''/*zee*/); verify.signatureHelp({ @@ -29,3 +33,13 @@ verify.signatureHelp({ docComment: "", tags: [], }); + + +verify.signatureHelp({ + marker: "zee", + text: "zee(): any", + docComment: "", + tags: [ + { name: "type", text: "{function(module:xxxx, module:xxxx): module:xxxxx}" }, + ], +}); From 5294e92d27da2bb2f1aca162fb12bc0cb8646e73 Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Fri, 9 Aug 2019 13:48:07 -0400 Subject: [PATCH 4/4] Use switch instead of instatating an array and when looking inside a module --- src/compiler/parser.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 522f4974ddf..b51c90eb742 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2434,9 +2434,16 @@ namespace ts { const moduleSpecifier = parseOptionalToken(SyntaxKind.ModuleKeyword); if (moduleSpecifier) { const moduleTag = createNode(SyntaxKind.JSDocNamepathType, moduleSpecifier.pos) as JSDocNamepathType; - const terminators = [SyntaxKind.CloseBraceToken, SyntaxKind.EndOfFileToken, SyntaxKind.CommaToken, SyntaxKind.CloseParenToken, SyntaxKind.WhitespaceTrivia]; - while (terminators.indexOf(token()) < 0) { - nextTokenJSDoc(); + terminate: while (true) { + switch (token()) { + case SyntaxKind.CloseBraceToken: + case SyntaxKind.EndOfFileToken: + case SyntaxKind.CommaToken: + case SyntaxKind.WhitespaceTrivia: + break terminate; + default: + nextTokenJSDoc(); + } } scanner.setInJSDocType(false);