diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index ad3f80710db..bbd4edc8507 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2436,20 +2436,24 @@ namespace ts { return finishNode(node); } + /** + * @returns If return type parsing succeeds + */ function fillSignature( returnToken: SyntaxKind.ColonToken | SyntaxKind.EqualsGreaterThanToken, flags: SignatureFlags, - signature: SignatureDeclaration): void { + signature: SignatureDeclaration): boolean { if (!(flags & SignatureFlags.JSDoc)) { signature.typeParameters = parseTypeParameters(); } signature.parameters = parseParameterList(flags); - signature.type = parseReturnType(returnToken, !!(flags & SignatureFlags.Type)); + if (shouldParseReturnType(returnToken, !!(flags & SignatureFlags.Type))) { + signature.type = parseTypeOrTypePredicate(); + return signature.type !== undefined; + } + return true; } - function parseReturnType(returnToken: SyntaxKind.ColonToken | SyntaxKind.EqualsGreaterThanToken, isType: boolean): TypeNode | undefined { - return shouldParseReturnType(returnToken, isType) ? parseTypeOrTypePredicate() : undefined; - } function shouldParseReturnType(returnToken: SyntaxKind.ColonToken | SyntaxKind.EqualsGreaterThanToken, isType: boolean): boolean { if (returnToken === SyntaxKind.EqualsGreaterThanToken) { parseExpected(returnToken); @@ -2758,6 +2762,9 @@ namespace ts { const node = createNode(SyntaxKind.ParenthesizedType); parseExpected(SyntaxKind.OpenParenToken); node.type = parseType(); + if (!node.type) { + return undefined; + } parseExpected(SyntaxKind.CloseParenToken); return finishNode(node); } @@ -2767,7 +2774,12 @@ namespace ts { if (kind === SyntaxKind.ConstructorType) { parseExpected(SyntaxKind.NewKeyword); } - fillSignature(SyntaxKind.EqualsGreaterThanToken, SignatureFlags.Type, node); + if (!fillSignature(SyntaxKind.EqualsGreaterThanToken, SignatureFlags.Type | (sourceFile.languageVariant === LanguageVariant.JSX ? SignatureFlags.RequireCompleteParameterList : 0), node)) { + return undefined; + } + if (!node.parameters) { + return undefined; + } return finishNode(node); } @@ -3598,7 +3610,9 @@ namespace ts { // a => (b => c) // And think that "(b =>" was actually a parenthesized arrow function with a missing // close paren. - fillSignature(SyntaxKind.ColonToken, isAsync | (allowAmbiguity ? SignatureFlags.None : SignatureFlags.RequireCompleteParameterList), node); + if (!fillSignature(SyntaxKind.ColonToken, isAsync | (allowAmbiguity ? SignatureFlags.None : SignatureFlags.RequireCompleteParameterList), node)) { + return undefined; + } // If we couldn't get parameters, we definitely could not parse out an arrow function. if (!node.parameters) { diff --git a/tests/baselines/reference/jsxNestedWithinTernaryParsesCorrectly.js b/tests/baselines/reference/jsxNestedWithinTernaryParsesCorrectly.js new file mode 100644 index 00000000000..f431e1224cc --- /dev/null +++ b/tests/baselines/reference/jsxNestedWithinTernaryParsesCorrectly.js @@ -0,0 +1,25 @@ +//// [jsxNestedWithinTernaryParsesCorrectly.tsx] +const emptyMessage = null as any; +const a = ( +
+ {0 ? ( + emptyMessage // must be identifier? + ) : ( + // must be exactly two expression holes + + {0}{0} + + )} +
+); + +//// [jsxNestedWithinTernaryParsesCorrectly.jsx] +var emptyMessage = null; +var a = (
+ {0 ? (emptyMessage // must be identifier? +) : ( +// must be exactly two expression holes + + {0}{0} + )} +
); diff --git a/tests/baselines/reference/jsxNestedWithinTernaryParsesCorrectly.symbols b/tests/baselines/reference/jsxNestedWithinTernaryParsesCorrectly.symbols new file mode 100644 index 00000000000..73ee27251ae --- /dev/null +++ b/tests/baselines/reference/jsxNestedWithinTernaryParsesCorrectly.symbols @@ -0,0 +1,20 @@ +=== tests/cases/compiler/jsxNestedWithinTernaryParsesCorrectly.tsx === +const emptyMessage = null as any; +>emptyMessage : Symbol(emptyMessage, Decl(jsxNestedWithinTernaryParsesCorrectly.tsx, 0, 5)) + +const a = ( +>a : Symbol(a, Decl(jsxNestedWithinTernaryParsesCorrectly.tsx, 1, 5)) + +
+ {0 ? ( + emptyMessage // must be identifier? +>emptyMessage : Symbol(emptyMessage, Decl(jsxNestedWithinTernaryParsesCorrectly.tsx, 0, 5)) + + ) : ( + // must be exactly two expression holes + + {0}{0} + + )} +
+); diff --git a/tests/baselines/reference/jsxNestedWithinTernaryParsesCorrectly.types b/tests/baselines/reference/jsxNestedWithinTernaryParsesCorrectly.types new file mode 100644 index 00000000000..0daa04fc39f --- /dev/null +++ b/tests/baselines/reference/jsxNestedWithinTernaryParsesCorrectly.types @@ -0,0 +1,42 @@ +=== tests/cases/compiler/jsxNestedWithinTernaryParsesCorrectly.tsx === +const emptyMessage = null as any; +>emptyMessage : any +>null as any : any +>null : null + +const a = ( +>a : any +>(
{0 ? ( emptyMessage // must be identifier? ) : ( // must be exactly two expression holes {0}{0} )}
) : any + +
+>
{0 ? ( emptyMessage // must be identifier? ) : ( // must be exactly two expression holes {0}{0} )}
: any +>div : any + + {0 ? ( +>0 ? ( emptyMessage // must be identifier? ) : ( // must be exactly two expression holes {0}{0} ) : any +>0 : 0 +>( emptyMessage // must be identifier? ) : any + + emptyMessage // must be identifier? +>emptyMessage : any + + ) : ( +>( // must be exactly two expression holes {0}{0} ) : any + + // must be exactly two expression holes + +> {0}{0} : any +>span : any + + {0}{0} +>0 : 0 +>0 : 0 + + +>span : any + + )} +
+>div : any + +); diff --git a/tests/cases/compiler/jsxNestedWithinTernaryParsesCorrectly.tsx b/tests/cases/compiler/jsxNestedWithinTernaryParsesCorrectly.tsx new file mode 100644 index 00000000000..4e69092aa8a --- /dev/null +++ b/tests/cases/compiler/jsxNestedWithinTernaryParsesCorrectly.tsx @@ -0,0 +1,14 @@ +// @jsx: preserve +const emptyMessage = null as any; +const a = ( +
+ {0 ? ( + emptyMessage // must be identifier? + ) : ( + // must be exactly two expression holes + + {0}{0} + + )} +
+); \ No newline at end of file