diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index 4a979fb3e6..089bff53a0 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -2,6 +2,15 @@
///
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;
@@ -2218,15 +2227,12 @@ namespace ts {
function fillSignature(
returnToken: SyntaxKind.ColonToken | SyntaxKind.EqualsGreaterThanToken,
- yieldContext: boolean,
- awaitContext: boolean,
- requireCompleteParameterList: boolean,
+ flags: SignatureFlags,
signature: SignatureDeclaration): void {
+ signature.typeParameters = parseTypeParameters();
+ signature.parameters = parseParameterList(flags);
const returnTokenRequired = returnToken === SyntaxKind.EqualsGreaterThanToken;
- signature.typeParameters = parseTypeParameters();
- signature.parameters = parseParameterList(yieldContext, awaitContext, requireCompleteParameterList);
-
if (returnTokenRequired) {
parseExpected(returnToken);
signature.type = parseTypeOrTypePredicate();
@@ -2234,9 +2240,19 @@ namespace ts {
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]
@@ -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;
@@ -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();
+ return (flags & SignatureFlags.RequireCompleteParameterList) ? undefined : createMissingList();
}
function parseTypeMemberSemicolon() {
@@ -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));
}
@@ -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));
}
@@ -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);
}
@@ -3254,7 +3270,7 @@ namespace ts {
function parseParenthesizedArrowFunctionExpressionHead(allowAmbiguity: boolean): ArrowFunction {
const node = 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.
//
@@ -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) {
@@ -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 &&
@@ -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
@@ -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);
@@ -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.
@@ -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);
@@ -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
@@ -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));
}
@@ -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));
}
@@ -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));
}
@@ -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));
}
diff --git a/tests/baselines/reference/parseErrorIncorrectReturnToken.errors.txt b/tests/baselines/reference/parseErrorIncorrectReturnToken.errors.txt
new file mode 100644
index 0000000000..2cf728848e
--- /dev/null
+++ b/tests/baselines/reference/parseErrorIncorrectReturnToken.errors.txt
@@ -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.
+
\ No newline at end of file
diff --git a/tests/baselines/reference/parseErrorIncorrectReturnToken.js b/tests/baselines/reference/parseErrorIncorrectReturnToken.js
new file mode 100644
index 0000000000..3d2a1d17a4
--- /dev/null
+++ b/tests/baselines/reference/parseErrorIncorrectReturnToken.js
@@ -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();
+}
+;
diff --git a/tests/cases/compiler/parseErrorIncorrectReturnToken.ts b/tests/cases/compiler/parseErrorIncorrectReturnToken.ts
new file mode 100644
index 0000000000..0f45c38a3b
--- /dev/null
+++ b/tests/cases/compiler/parseErrorIncorrectReturnToken.ts
@@ -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();
+ }
+};