From 9a65658b28ae4c1579b8ab2bf6e3352a2f802035 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Tue, 7 Jul 2020 08:30:42 -0700 Subject: [PATCH] Parse *= separately in types (#39457) * 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. * add test from #38551 * we :heart: semicolons --- 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.symbols | 7 ++++++ .../reference/jsdocParseStarEquals.types | 11 +++++++-- .../conformance/jsdoc/jsdocParseStarEquals.ts | 5 ++++ 7 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index c5cbb950f69..8f7ea52ebe9 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 b58ae9aee77..0a4e5126987 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 360aa065005..83b03093da4 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 d30b0b8f7ac..e85e4a8bf70 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.symbols b/tests/baselines/reference/jsdocParseStarEquals.symbols index a882cf23eb2..795d97f20b8 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 1142fb05dae..174e543dd7d 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 @@ -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 e5f07bb3d2f..ef93965e0fc 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) { +}