Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 57 additions & 41 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
/// <reference path="scanner.ts"/>

namespace ts {
const enum SignatureFlags {
None = 0,
Yield = 1 << 0,
Await = 1 << 1,
Type = 1 << 2,
RequireCompleteParameterList = 1 << 3,
IgnoreMissingOpenBrace = 1 << 4,
}

let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let TokenConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let IdentifierConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
Expand Down Expand Up @@ -2218,25 +2227,32 @@ namespace ts {

function fillSignature(
returnToken: SyntaxKind.ColonToken | SyntaxKind.EqualsGreaterThanToken,
yieldContext: boolean,
awaitContext: boolean,
requireCompleteParameterList: boolean,
flags: SignatureFlags,
signature: SignatureDeclaration): void {

const returnTokenRequired = returnToken === SyntaxKind.EqualsGreaterThanToken;
signature.typeParameters = parseTypeParameters();
signature.parameters = parseParameterList(yieldContext, awaitContext, requireCompleteParameterList);
signature.parameters = parseParameterList(flags);

const returnTokenRequired = returnToken === SyntaxKind.EqualsGreaterThanToken;
if (returnTokenRequired) {
parseExpected(returnToken);
signature.type = parseTypeOrTypePredicate();
}
else if (parseOptional(returnToken)) {
signature.type = parseTypeOrTypePredicate();
}
else if (flags & SignatureFlags.Type) {
const start = scanner.getTokenPos();
const length = scanner.getTextPos() - start;
const backwardToken = parseOptional(returnToken === SyntaxKind.ColonToken ? SyntaxKind.EqualsGreaterThanToken : SyntaxKind.ColonToken);
if (backwardToken) {
// This is easy to get backward, especially in type contexts, so parse the type anyway
signature.type = parseTypeOrTypePredicate();
parseErrorAtPosition(start, length, Diagnostics._0_expected, tokenToString(returnToken));
}
}
}

function parseParameterList(yieldContext: boolean, awaitContext: boolean, requireCompleteParameterList: boolean) {
function parseParameterList(flags: SignatureFlags) {
// FormalParameters [Yield,Await]: (modified)
// [empty]
// FormalParameterList[?Yield,Await]
Expand All @@ -2254,15 +2270,15 @@ namespace ts {
const savedYieldContext = inYieldContext();
const savedAwaitContext = inAwaitContext();

setYieldContext(yieldContext);
setAwaitContext(awaitContext);
setYieldContext(!!(flags & SignatureFlags.Yield));
setAwaitContext(!!(flags & SignatureFlags.Await));

const result = parseDelimitedList(ParsingContext.Parameters, parseParameter);

setYieldContext(savedYieldContext);
setAwaitContext(savedAwaitContext);

if (!parseExpected(SyntaxKind.CloseParenToken) && requireCompleteParameterList) {
if (!parseExpected(SyntaxKind.CloseParenToken) && (flags & SignatureFlags.RequireCompleteParameterList)) {
// Caller insisted that we had to end with a ) We didn't. So just return
// undefined here.
return undefined;
Expand All @@ -2274,7 +2290,7 @@ namespace ts {
// We didn't even have an open paren. If the caller requires a complete parameter list,
// we definitely can't provide that. However, if they're ok with an incomplete one,
// then just return an empty set of parameters.
return requireCompleteParameterList ? undefined : createMissingList<ParameterDeclaration>();
return (flags & SignatureFlags.RequireCompleteParameterList) ? undefined : createMissingList<ParameterDeclaration>();
}

function parseTypeMemberSemicolon() {
Expand All @@ -2293,7 +2309,7 @@ namespace ts {
if (kind === SyntaxKind.ConstructSignature) {
parseExpected(SyntaxKind.NewKeyword);
}
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ false, /*awaitContext*/ false, /*requireCompleteParameterList*/ false, node);
fillSignature(SyntaxKind.ColonToken, SignatureFlags.Type, node);
parseTypeMemberSemicolon();
return addJSDocComment(finishNode(node));
}
Expand Down Expand Up @@ -2383,7 +2399,7 @@ namespace ts {

// Method signatures don't exist in expression contexts. So they have neither
// [Yield] nor [Await]
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ false, /*awaitContext*/ false, /*requireCompleteParameterList*/ false, method);
fillSignature(SyntaxKind.ColonToken, SignatureFlags.Type, method);
parseTypeMemberSemicolon();
return addJSDocComment(finishNode(method));
}
Expand Down Expand Up @@ -2527,7 +2543,7 @@ namespace ts {
if (kind === SyntaxKind.ConstructorType) {
parseExpected(SyntaxKind.NewKeyword);
}
fillSignature(SyntaxKind.EqualsGreaterThanToken, /*yieldContext*/ false, /*awaitContext*/ false, /*requireCompleteParameterList*/ false, node);
fillSignature(SyntaxKind.EqualsGreaterThanToken, SignatureFlags.Type, node);
return finishNode(node);
}

Expand Down Expand Up @@ -3254,7 +3270,7 @@ namespace ts {
function parseParenthesizedArrowFunctionExpressionHead(allowAmbiguity: boolean): ArrowFunction {
const node = <ArrowFunction>createNode(SyntaxKind.ArrowFunction);
node.modifiers = parseModifiersForArrowFunction();
const isAsync = !!(getModifierFlags(node) & ModifierFlags.Async);
const isAsync = (getModifierFlags(node) & ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;

// Arrow functions are never generators.
//
Expand All @@ -3263,7 +3279,7 @@ namespace ts {
// a => (b => c)
// And think that "(b =>" was actually a parenthesized arrow function with a missing
// close paren.
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ false, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ !allowAmbiguity, node);
fillSignature(SyntaxKind.ColonToken, isAsync | (allowAmbiguity ? SignatureFlags.None : SignatureFlags.RequireCompleteParameterList), node);

// If we couldn't get parameters, we definitely could not parse out an arrow function.
if (!node.parameters) {
Expand All @@ -3288,7 +3304,7 @@ namespace ts {

function parseArrowFunctionExpressionBody(isAsync: boolean): Block | Expression {
if (token() === SyntaxKind.OpenBraceToken) {
return parseFunctionBlock(/*allowYield*/ false, /*allowAwait*/ isAsync, /*ignoreMissingOpenBrace*/ false);
return parseFunctionBlock(isAsync ? SignatureFlags.Await : SignatureFlags.None);
}

if (token() !== SyntaxKind.SemicolonToken &&
Expand All @@ -3309,8 +3325,8 @@ namespace ts {
// try to recover better. If we don't do this, then the next close curly we see may end
// up preemptively closing the containing construct.
//
// Note: even when 'ignoreMissingOpenBrace' is passed as true, parseBody will still error.
return parseFunctionBlock(/*allowYield*/ false, /*allowAwait*/ isAsync, /*ignoreMissingOpenBrace*/ true);
// Note: even when 'IgnoreMissingOpenBrace' is passed, parseBody will still error.
return parseFunctionBlock(SignatureFlags.IgnoreMissingOpenBrace | (isAsync ? SignatureFlags.Await : SignatureFlags.None));
}

return isAsync
Expand Down Expand Up @@ -4386,16 +4402,16 @@ namespace ts {
parseExpected(SyntaxKind.FunctionKeyword);
node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken);

const isGenerator = !!node.asteriskToken;
const isAsync = !!(getModifierFlags(node) & ModifierFlags.Async);
const isGenerator = node.asteriskToken ? SignatureFlags.Yield : SignatureFlags.None;
const isAsync = (getModifierFlags(node) & ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
node.name =
isGenerator && isAsync ? doInYieldAndAwaitContext(parseOptionalIdentifier) :
isGenerator ? doInYieldContext(parseOptionalIdentifier) :
isAsync ? doInAwaitContext(parseOptionalIdentifier) :
parseOptionalIdentifier();

fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ isGenerator, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ false, node);
node.body = parseFunctionBlock(/*allowYield*/ isGenerator, /*allowAwait*/ isAsync, /*ignoreMissingOpenBrace*/ false);
fillSignature(SyntaxKind.ColonToken, isGenerator | isAsync, node);
node.body = parseFunctionBlock(isGenerator | isAsync);

if (saveDecoratorContext) {
setDecoratorContext(/*val*/ true);
Expand Down Expand Up @@ -4444,12 +4460,12 @@ namespace ts {
return finishNode(node);
}

function parseFunctionBlock(allowYield: boolean, allowAwait: boolean, ignoreMissingOpenBrace: boolean, diagnosticMessage?: DiagnosticMessage): Block {
function parseFunctionBlock(flags: SignatureFlags, diagnosticMessage?: DiagnosticMessage): Block {
const savedYieldContext = inYieldContext();
setYieldContext(allowYield);
setYieldContext(!!(flags & SignatureFlags.Yield));

const savedAwaitContext = inAwaitContext();
setAwaitContext(allowAwait);
setAwaitContext(!!(flags & SignatureFlags.Await));

// We may be in a [Decorator] context when parsing a function expression or
// arrow function. The body of the function is not in [Decorator] context.
Expand All @@ -4458,7 +4474,7 @@ namespace ts {
setDecoratorContext(/*val*/ false);
}

const block = parseBlock(ignoreMissingOpenBrace, diagnosticMessage);
const block = parseBlock(!!(flags & SignatureFlags.IgnoreMissingOpenBrace), diagnosticMessage);

if (saveDecoratorContext) {
setDecoratorContext(/*val*/ true);
Expand Down Expand Up @@ -5005,13 +5021,13 @@ namespace ts {
return !scanner.hasPrecedingLineBreak() && (isIdentifier() || token() === SyntaxKind.StringLiteral);
}

function parseFunctionBlockOrSemicolon(isGenerator: boolean, isAsync: boolean, diagnosticMessage?: DiagnosticMessage): Block {
function parseFunctionBlockOrSemicolon(flags: SignatureFlags, diagnosticMessage?: DiagnosticMessage): Block {
if (token() !== SyntaxKind.OpenBraceToken && canParseSemicolon()) {
parseSemicolon();
return;
}

return parseFunctionBlock(isGenerator, isAsync, /*ignoreMissingOpenBrace*/ false, diagnosticMessage);
return parseFunctionBlock(flags, diagnosticMessage);
}

// DECLARATIONS
Expand Down Expand Up @@ -5146,10 +5162,10 @@ namespace ts {
parseExpected(SyntaxKind.FunctionKeyword);
node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken);
node.name = hasModifier(node, ModifierFlags.Default) ? parseOptionalIdentifier() : parseIdentifier();
const isGenerator = !!node.asteriskToken;
const isAsync = hasModifier(node, ModifierFlags.Async);
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ isGenerator, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ false, node);
node.body = parseFunctionBlockOrSemicolon(isGenerator, isAsync, Diagnostics.or_expected);
const isGenerator = node.asteriskToken ? SignatureFlags.Yield : SignatureFlags.None;
const isAsync = hasModifier(node, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
fillSignature(SyntaxKind.ColonToken, isGenerator | isAsync, node);
node.body = parseFunctionBlockOrSemicolon(isGenerator | isAsync, Diagnostics.or_expected);
return addJSDocComment(finishNode(node));
}

Expand All @@ -5158,8 +5174,8 @@ namespace ts {
node.decorators = decorators;
node.modifiers = modifiers;
parseExpected(SyntaxKind.ConstructorKeyword);
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ false, /*awaitContext*/ false, /*requireCompleteParameterList*/ false, node);
node.body = parseFunctionBlockOrSemicolon(/*isGenerator*/ false, /*isAsync*/ false, Diagnostics.or_expected);
fillSignature(SyntaxKind.ColonToken, SignatureFlags.None, node);
node.body = parseFunctionBlockOrSemicolon(SignatureFlags.None, Diagnostics.or_expected);
return addJSDocComment(finishNode(node));
}

Expand All @@ -5170,10 +5186,10 @@ namespace ts {
method.asteriskToken = asteriskToken;
method.name = name;
method.questionToken = questionToken;
const isGenerator = !!asteriskToken;
const isAsync = hasModifier(method, ModifierFlags.Async);
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ isGenerator, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ false, method);
method.body = parseFunctionBlockOrSemicolon(isGenerator, isAsync, diagnosticMessage);
const isGenerator = asteriskToken ? SignatureFlags.Yield : SignatureFlags.None;
const isAsync = hasModifier(method, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
fillSignature(SyntaxKind.ColonToken, isGenerator | isAsync, method);
method.body = parseFunctionBlockOrSemicolon(isGenerator | isAsync, diagnosticMessage);
return addJSDocComment(finishNode(method));
}

Expand Down Expand Up @@ -5226,8 +5242,8 @@ namespace ts {
node.decorators = decorators;
node.modifiers = modifiers;
node.name = parsePropertyName();
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ false, /*awaitContext*/ false, /*requireCompleteParameterList*/ false, node);
node.body = parseFunctionBlockOrSemicolon(/*isGenerator*/ false, /*isAsync*/ false);
fillSignature(SyntaxKind.ColonToken, SignatureFlags.None, node);
node.body = parseFunctionBlockOrSemicolon(SignatureFlags.None);
return addJSDocComment(finishNode(node));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
tests/cases/compiler/parseErrorIncorrectReturnToken.ts(2,17): error TS1005: ':' expected.
tests/cases/compiler/parseErrorIncorrectReturnToken.ts(4,22): error TS1005: '=>' expected.
tests/cases/compiler/parseErrorIncorrectReturnToken.ts(4,24): error TS2693: 'string' only refers to a type, but is being used as a value here.
tests/cases/compiler/parseErrorIncorrectReturnToken.ts(9,18): error TS1005: '{' expected.
tests/cases/compiler/parseErrorIncorrectReturnToken.ts(9,21): error TS2693: 'string' only refers to a type, but is being used as a value here.
tests/cases/compiler/parseErrorIncorrectReturnToken.ts(9,28): error TS1005: ';' expected.
tests/cases/compiler/parseErrorIncorrectReturnToken.ts(12,1): error TS1128: Declaration or statement expected.


==== tests/cases/compiler/parseErrorIncorrectReturnToken.ts (7 errors) ====
type F1 = {
(n: number) => string; // should be : not =>
~~
!!! error TS1005: ':' expected.
}
type F2 = (n: number): string; // should be => not :
~
!!! error TS1005: '=>' expected.
~~~~~~
!!! error TS2693: 'string' only refers to a type, but is being used as a value here.

// doesn't work in non-type contexts, where the return type is optional
let f = (n: number) => string => n.toString();
let o = {
m(n: number) => string {
~~
!!! error TS1005: '{' expected.
~~~~~~
!!! error TS2693: 'string' only refers to a type, but is being used as a value here.
~
!!! error TS1005: ';' expected.
return n.toString();
}
};
~
!!! error TS1128: Declaration or statement expected.

27 changes: 27 additions & 0 deletions tests/baselines/reference/parseErrorIncorrectReturnToken.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//// [parseErrorIncorrectReturnToken.ts]
type F1 = {
(n: number) => string; // should be : not =>
}
type F2 = (n: number): string; // should be => not :

// doesn't work in non-type contexts, where the return type is optional
let f = (n: number) => string => n.toString();
let o = {
m(n: number) => string {
return n.toString();
}
};


//// [parseErrorIncorrectReturnToken.js]
string; // should be => not :
// doesn't work in non-type contexts, where the return type is optional
var f = function (n) { return function (string) { return n.toString(); }; };
var o = {
m: function (n) { }
};
string;
{
return n.toString();
}
;
13 changes: 13 additions & 0 deletions tests/cases/compiler/parseErrorIncorrectReturnToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

type F1 = {
(n: number) => string; // should be : not =>
}
type F2 = (n: number): string; // should be => not :

// doesn't work in non-type contexts, where the return type is optional
let f = (n: number) => string => n.toString();
let o = {
m(n: number) => string {
return n.toString();
}
};