From c59fcfc87282721089988a61fe510338014dd98e Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Mon, 6 Jul 2020 16:47:35 -0700 Subject: [PATCH 1/3] Parse *= separately in types Previously, when the scanner produced `*=` as a single token, the type parser ran special-case code to produce an "optional all type", which only makes sense when the `=` really should be attached to the `*`. This is often not the case. The correct solution, which I missed when I first wrote this code, is to have the scanner go back and produce a separate `=` token, which is what this PR does. --- src/compiler/parser.ts | 23 +++++++------------ src/compiler/scanner.ts | 8 +++++++ .../reference/api/tsserverlibrary.d.ts | 1 + tests/baselines/reference/api/typescript.d.ts | 1 + .../reference/jsdocParseStarEquals.types | 4 ++-- 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index c5cbb950f69d7..99bd0fbc9ee87 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2701,19 +2701,10 @@ namespace ts { return finishNode(factory.createThisTypeNode(), pos); } - function parseJSDocAllType(postFixEquals: boolean): JSDocAllType | JSDocOptionalType { + function parseJSDocAllType(): JSDocAllType | JSDocOptionalType { const pos = getNodePos(); nextToken(); - - const node = factory.createJSDocAllType(); - if (postFixEquals) { - // Trim the trailing `=` from the `*=` token - const end = Math.max(getNodePos() - 1, pos); - return finishNode(factory.createJSDocOptionalType(finishNode(node, pos, end)), pos); - } - else { - return finishNode(node, pos); - } + return finishNode(factory.createJSDocAllType(), pos); } function parseJSDocNonNullableType(): TypeNode { @@ -3396,12 +3387,14 @@ namespace ts { case SyntaxKind.ObjectKeyword: // If these are followed by a dot, then parse these out as a dotted type reference instead. return tryParse(parseKeywordAndNoDot) || parseTypeReference(); - case SyntaxKind.AsteriskToken: - return parseJSDocAllType(/*postfixEquals*/ false); case SyntaxKind.AsteriskEqualsToken: - return parseJSDocAllType(/*postfixEquals*/ true); + // If there is '*=', treat it as * followed by postfix = + scanner.reScanAsteriskEqualsToken() + // falls through + case SyntaxKind.AsteriskToken: + return parseJSDocAllType(); case SyntaxKind.QuestionQuestionToken: - // If there is '??', consider that is prefix '?' in JSDoc type. + // If there is '??', treat it as prefix-'?' in JSDoc type. scanner.reScanQuestionToken(); // falls through case SyntaxKind.QuestionToken: diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index b58ae9aee773c..0a4e5126987eb 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -34,6 +34,7 @@ namespace ts { getTokenFlags(): TokenFlags; reScanGreaterToken(): SyntaxKind; reScanSlashToken(): SyntaxKind; + reScanAsteriskEqualsToken(): SyntaxKind; reScanTemplateToken(isTaggedTemplate: boolean): SyntaxKind; reScanTemplateHeadOrNoSubstitutionTemplate(): SyntaxKind; scanJsxIdentifier(): SyntaxKind; @@ -954,6 +955,7 @@ namespace ts { getNumericLiteralFlags: () => tokenFlags & TokenFlags.NumericLiteralFlags, getTokenFlags: () => tokenFlags, reScanGreaterToken, + reScanAsteriskEqualsToken, reScanSlashToken, reScanTemplateToken, reScanTemplateHeadOrNoSubstitutionTemplate, @@ -2086,6 +2088,12 @@ namespace ts { return token; } + function reScanAsteriskEqualsToken(): SyntaxKind { + Debug.assert(token === SyntaxKind.AsteriskEqualsToken, "'reScanAsteriskEqualsToken' should only be called on a '*='"); + pos = tokenPos + 1; + return token = SyntaxKind.EqualsToken; + } + function reScanSlashToken(): SyntaxKind { if (token === SyntaxKind.SlashToken || token === SyntaxKind.SlashEqualsToken) { let p = tokenPos + 1; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 360aa0650053d..83b03093da4b0 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -3902,6 +3902,7 @@ declare namespace ts { isUnterminated(): boolean; reScanGreaterToken(): SyntaxKind; reScanSlashToken(): SyntaxKind; + reScanAsteriskEqualsToken(): SyntaxKind; reScanTemplateToken(isTaggedTemplate: boolean): SyntaxKind; reScanTemplateHeadOrNoSubstitutionTemplate(): SyntaxKind; scanJsxIdentifier(): SyntaxKind; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index d30b0b8f7ac84..e85e4a8bf70b2 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3902,6 +3902,7 @@ declare namespace ts { isUnterminated(): boolean; reScanGreaterToken(): SyntaxKind; reScanSlashToken(): SyntaxKind; + reScanAsteriskEqualsToken(): SyntaxKind; reScanTemplateToken(isTaggedTemplate: boolean): SyntaxKind; reScanTemplateHeadOrNoSubstitutionTemplate(): SyntaxKind; scanJsxIdentifier(): SyntaxKind; diff --git a/tests/baselines/reference/jsdocParseStarEquals.types b/tests/baselines/reference/jsdocParseStarEquals.types index 1142fb05daea3..b2bf600b83a7f 100644 --- a/tests/baselines/reference/jsdocParseStarEquals.types +++ b/tests/baselines/reference/jsdocParseStarEquals.types @@ -2,8 +2,8 @@ /** @param {...*=} args @return {*=} */ function f(...args) { ->f : (...args: (any | undefined)[]) => any | undefined ->args : any[] +>f : (...args?: any[] | undefined) => any | undefined +>args : any return null >null : null From 93b160586083fc033e069951916ac408466401f9 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Mon, 6 Jul 2020 17:01:20 -0700 Subject: [PATCH 2/3] add test from #38551 --- tests/baselines/reference/jsdocParseStarEquals.symbols | 7 +++++++ tests/baselines/reference/jsdocParseStarEquals.types | 7 +++++++ tests/cases/conformance/jsdoc/jsdocParseStarEquals.ts | 5 +++++ 3 files changed, 19 insertions(+) diff --git a/tests/baselines/reference/jsdocParseStarEquals.symbols b/tests/baselines/reference/jsdocParseStarEquals.symbols index a882cf23eb21c..795d97f20b853 100644 --- a/tests/baselines/reference/jsdocParseStarEquals.symbols +++ b/tests/baselines/reference/jsdocParseStarEquals.symbols @@ -12,3 +12,10 @@ function f(...args) { var x; >x : Symbol(x, Decl(a.js, 7, 3)) + +/** @param {function():*=} f */ +function cbf(f) { +>cbf : Symbol(cbf, Decl(a.js, 7, 6)) +>f : Symbol(f, Decl(a.js, 11, 13)) +} + diff --git a/tests/baselines/reference/jsdocParseStarEquals.types b/tests/baselines/reference/jsdocParseStarEquals.types index b2bf600b83a7f..174e543dd7d92 100644 --- a/tests/baselines/reference/jsdocParseStarEquals.types +++ b/tests/baselines/reference/jsdocParseStarEquals.types @@ -13,3 +13,10 @@ function f(...args) { var x; >x : any + +/** @param {function():*=} f */ +function cbf(f) { +>cbf : (f?: (() => any) | undefined) => void +>f : (() => any) | undefined +} + diff --git a/tests/cases/conformance/jsdoc/jsdocParseStarEquals.ts b/tests/cases/conformance/jsdoc/jsdocParseStarEquals.ts index e5f07bb3d2f6d..ef93965e0fc87 100644 --- a/tests/cases/conformance/jsdoc/jsdocParseStarEquals.ts +++ b/tests/cases/conformance/jsdoc/jsdocParseStarEquals.ts @@ -12,3 +12,8 @@ function f(...args) { /** @type *= */ var x; + + +/** @param {function():*=} f */ +function cbf(f) { +} From 5a15565b694b5230435305209188a12f08ddc994 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Tue, 7 Jul 2020 08:02:12 -0700 Subject: [PATCH 3/3] we :heart: semicolons --- src/compiler/parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 99bd0fbc9ee87..8f7ea52ebe94b 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3389,7 +3389,7 @@ namespace ts { return tryParse(parseKeywordAndNoDot) || parseTypeReference(); case SyntaxKind.AsteriskEqualsToken: // If there is '*=', treat it as * followed by postfix = - scanner.reScanAsteriskEqualsToken() + scanner.reScanAsteriskEqualsToken(); // falls through case SyntaxKind.AsteriskToken: return parseJSDocAllType();