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 ❤️ semicolons
This commit is contained in:
Nathan Shively-Sanders
2020-07-07 08:30:42 -07:00
committed by GitHub
parent c12d431d7e
commit 9a65658b28
7 changed files with 39 additions and 17 deletions
+8 -15
View File
@@ -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:
+8
View File
@@ -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;
+1
View File
@@ -3902,6 +3902,7 @@ declare namespace ts {
isUnterminated(): boolean;
reScanGreaterToken(): SyntaxKind;
reScanSlashToken(): SyntaxKind;
reScanAsteriskEqualsToken(): SyntaxKind;
reScanTemplateToken(isTaggedTemplate: boolean): SyntaxKind;
reScanTemplateHeadOrNoSubstitutionTemplate(): SyntaxKind;
scanJsxIdentifier(): SyntaxKind;
+1
View File
@@ -3902,6 +3902,7 @@ declare namespace ts {
isUnterminated(): boolean;
reScanGreaterToken(): SyntaxKind;
reScanSlashToken(): SyntaxKind;
reScanAsteriskEqualsToken(): SyntaxKind;
reScanTemplateToken(isTaggedTemplate: boolean): SyntaxKind;
reScanTemplateHeadOrNoSubstitutionTemplate(): SyntaxKind;
scanJsxIdentifier(): SyntaxKind;
@@ -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))
}
@@ -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
}
@@ -12,3 +12,8 @@ function f(...args) {
/** @type *= */
var x;
/** @param {function():*=} f */
function cbf(f) {
}