diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 06980e19187..b36ded65dc4 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1386,6 +1386,7 @@ module ts { if (inGeneratorParameterContext()) { setYieldContext(false); } + node.expression = allowInAnd(parseExpression); if (inGeneratorParameterContext()) { setYieldContext(yieldContext); @@ -1405,15 +1406,17 @@ module ts { } function parseAnyContextualModifier(): boolean { - return isModifier(token) && tryParse(() => { - if (token === SyntaxKind.ConstKeyword) { - // 'const' is only a modifier if followed by 'enum'. - return nextToken() === SyntaxKind.EnumKeyword; - } + return isModifier(token) && tryParse(nextTokenCanFollowContextualModifier); + } - nextToken(); - return canFollowModifier(); - }); + function nextTokenCanFollowContextualModifier() { + if (token === SyntaxKind.ConstKeyword) { + // 'const' is only a modifier if followed by 'enum'. + return nextToken() === SyntaxKind.EnumKeyword; + } + + nextToken(); + return canFollowModifier(); } function canFollowModifier(): boolean { @@ -2531,56 +2534,55 @@ module ts { function parseSimpleArrowFunctionExpression(identifier: Identifier): Expression { Debug.assert(token === SyntaxKind.EqualsGreaterThanToken, "parseSimpleArrowFunctionExpression should only have been called if we had a =>"); + + var node = createNode(SyntaxKind.ArrowFunction, identifier.pos); + var parameter = createNode(SyntaxKind.Parameter, identifier.pos); - parseExpected(SyntaxKind.EqualsGreaterThanToken); parameter.name = identifier; finishNode(parameter); - var parameters = >[]; - parameters.push(parameter); - parameters.pos = parameter.pos; - parameters.end = parameter.end; + node.parameters = >[parameter]; + node.parameters.pos = parameter.pos; + node.parameters.end = parameter.end; - var signature = { parameters: parameters }; + parseExpected(SyntaxKind.EqualsGreaterThanToken); + node.body = parseArrowFunctionExpressionBody(); - return parseArrowExpressionTail(identifier.pos, signature); + return finishNode(node); } function tryParseParenthesizedArrowFunctionExpression(): Expression { - // Indicates whether we are certain that we should parse an arrow expression. var triState = isParenthesizedArrowFunctionExpression(); if (triState === Tristate.False) { + // It's definitely not a parenthesized arrow function expression. return undefined; } - var pos = getNodePos(); + // If we definitely have an arrow function, then we can just parse one, not requiring a + // following => or { token. Otherwise, we *might* have an arrow function. Try to parse + // it out, but don't allow any ambiguity, and return 'undefined' if this could be an + // expression instead. + var arrowFunction = triState === Tristate.True + ? parseParenthesizedArrowFunctionExpressionHead(/*allowAmbiguity:*/ true) + : tryParse(parsePossibleParenthesizedArrowFunctionExpressionHead); - if (triState === Tristate.True) { - // Arrow function are never generators. - var sig = parseSignature(/*yieldAndGeneratorParameterContext:*/ false); - - // If we have an arrow, then try to parse the body. - // Even if not, try to parse if we have an opening brace, just in case we're in an error state. - if (parseExpected(SyntaxKind.EqualsGreaterThanToken) || token === SyntaxKind.OpenBraceToken) { - return parseArrowExpressionTail(pos, sig); - } - else { - // If not, we're probably better off bailing out and returning a bogus function expression. - return makeFunctionExpression(SyntaxKind.ArrowFunction, pos, /*asteriskToken:*/ undefined, /*name:*/ undefined, sig, parseIdentifier(Diagnostics.Expression_expected)); - } + if (!arrowFunction) { + // Didn't appear to actually be a parenthesized arrow function. Just bail out. + return undefined; } - - // *Maybe* we had an arrow function and we need to try to parse it out, - // rolling back and trying other parses if we fail. - var sig = tryParseSignatureIfArrowOrBraceFollows(); - if (sig) { - parseExpected(SyntaxKind.EqualsGreaterThanToken); - return parseArrowExpressionTail(pos, sig); + + // If we have an arrow, then try to parse the body. Even if not, try to parse if we + // have an opening brace, just in case we're in an error state. + if (parseExpected(SyntaxKind.EqualsGreaterThanToken) || token === SyntaxKind.OpenBraceToken) { + arrowFunction.body = parseArrowFunctionExpressionBody(); } else { - return undefined; + // If not, we're probably better off bailing out and returning a bogus function expression. + arrowFunction.body = parseIdentifier(); } + + return finishNode(arrowFunction); } // True -> We definitely expect a parenthesized arrow function here. @@ -2662,34 +2664,37 @@ module ts { } } - function tryParseSignatureIfArrowOrBraceFollows(): ParsedSignature { - return tryParse(() => { - // Arrow functions are never generators. - var sig = parseSignature(/*yieldAndGeneratorParameterContext:*/ false); - - // Parsing a signature isn't enough. - // Parenthesized arrow signatures often look like other valid expressions. - // For instance: - // - "(x = 10)" is an assignment expression parsed as a signature with a default parameter value. - // - "(x,y)" is a comma expression parsed as a signature with two parameters. - // - "a ? (b): c" will have "(b):" parsed as a signature with a return type annotation. - // - // So we need just a bit of lookahead to ensure that it can only be a signature. - if (token === SyntaxKind.EqualsGreaterThanToken || token === SyntaxKind.OpenBraceToken) { - return sig; - } - - return undefined; - }); + function parsePossibleParenthesizedArrowFunctionExpressionHead() { + return parseParenthesizedArrowFunctionExpressionHead(/*allowAmbiguity:*/ false); } - function parseArrowExpressionTail(pos: number, sig: ParsedSignature): FunctionExpression { - var body: Block | Expression; + function parseParenthesizedArrowFunctionExpressionHead(allowAmbiguity: boolean): FunctionExpression { + var node = createNode(SyntaxKind.ArrowFunction); + // Arrow functions are never generators. + fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, node); - if (token === SyntaxKind.OpenBraceToken) { - body = parseFunctionBlock(/*allowYield:*/ false, /* ignoreMissingOpenBrace */ false); + // Parsing a signature isn't enough. + // Parenthesized arrow signatures often look like other valid expressions. + // For instance: + // - "(x = 10)" is an assignment expression parsed as a signature with a default parameter value. + // - "(x,y)" is a comma expression parsed as a signature with two parameters. + // - "a ? (b): c" will have "(b):" parsed as a signature with a return type annotation. + // + // So we need just a bit of lookahead to ensure that it can only be a signature. + if (!allowAmbiguity && token !== SyntaxKind.EqualsGreaterThanToken && token !== SyntaxKind.OpenBraceToken) { + // Returning undefined here will cause our caller to rewind to where we started from. + return undefined; } - else if (isStatement(/* inErrorRecovery */ true) && !isStartOfExpressionStatement() && token !== SyntaxKind.FunctionKeyword) { + + return node; + } + + function parseArrowFunctionExpressionBody(): Block | Expression { + if (token === SyntaxKind.OpenBraceToken) { + return parseFunctionBlock(/*allowYield:*/ false, /* ignoreMissingOpenBrace */ false); + } + + if (isStatement(/* inErrorRecovery */ true) && !isStartOfExpressionStatement() && token !== SyntaxKind.FunctionKeyword) { // Check if we got a plain statement (i.e. no expression-statements, no functions expressions/declarations) // // Here we try to recover from a potential error situation in the case where the @@ -2704,13 +2709,10 @@ module ts { // up preemptively closing the containing construct. // // Note: even when 'ignoreMissingOpenBrace' is passed as true, parseBody will still error. - body = parseFunctionBlock(/*allowYield:*/ false, /* ignoreMissingOpenBrace */ true); - } - else { - body = parseAssignmentExpressionOrHigher(); + return parseFunctionBlock(/*allowYield:*/ false, /* ignoreMissingOpenBrace */ true); } - return makeFunctionExpression(SyntaxKind.ArrowFunction, pos, /*asteriskToken:*/ undefined, /*name:*/ undefined, sig, body); + return parseAssignmentExpressionOrHigher(); } function parseConditionalExpressionRest(leftOperand: Expression): Expression { @@ -3233,15 +3235,13 @@ module ts { // function * BindingIdentifier[Yield]opt (FormalParameters[Yield, GeneratorParameter]) { GeneratorBody[Yield] } // FunctionExpression: // function BindingIdentifieropt(FormalParameters) { FunctionBody } - - var pos = getNodePos(); + var node = createNode(SyntaxKind.FunctionExpression); parseExpected(SyntaxKind.FunctionKeyword); - var asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); - var name = asteriskToken ? doInYieldContext(parseOptionalIdentifier) : parseOptionalIdentifier(); - var sig = parseSignature(/*yieldAndGeneratorParameterContext:*/ !!asteriskToken); - - var body = parseFunctionBlock(/*allowYield:*/ !!asteriskToken, /* ignoreMissingOpenBrace */ false); - return makeFunctionExpression(SyntaxKind.FunctionExpression, pos, asteriskToken, name, sig, body); + node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); + node.name = node.asteriskToken ? doInYieldContext(parseOptionalIdentifier) : parseOptionalIdentifier(); + fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ !!node.asteriskToken, node); + node.body = parseFunctionBlock(/*allowYield:*/ !!node.asteriskToken, /* ignoreMissingOpenBrace */ false); + return finishNode(node); } function parseOptionalIdentifier() { diff --git a/tests/baselines/reference/contextualTypeWithUnionTypeCallSignatures.js b/tests/baselines/reference/contextualTypeWithUnionTypeCallSignatures.js index 2e21235ea0a..e29d4720cbc 100644 --- a/tests/baselines/reference/contextualTypeWithUnionTypeCallSignatures.js +++ b/tests/baselines/reference/contextualTypeWithUnionTypeCallSignatures.js @@ -45,42 +45,6 @@ var x = function (a) { return a.toString(); }; var x2 = function (a) { return a.toString(); }; // Like iWithCallSignatures var x2 = function (a) { return a; }; // Like iWithCallSignatures2 // With call signatures of mismatching parameter type -var x3 = function (a /*here a should be any*/) { return a.toString(); }; +var x3 = function (a) { return a.toString(); }; // With call signature count mismatch -var x4 = function (a //When used as a contextual type, a union type U has those members that are present in any of -// its constituent types, with types that are unions of the respective members in the constituent types. - -// Let S be the set of types in U that have call signatures. -// If S is not empty and the sets of call signatures of the types in S are identical ignoring return types, -// U has the same set of call signatures, but with return types that are unions of the return types of the respective call signatures from each type in S. - -interface IWithNoCallSignatures { - foo: string; -} -interface IWithCallSignatures { - (a: number): string; -} -interface IWithCallSignatures2 { - (a: number): number; -} -interface IWithCallSignatures3 { - (b: string): number; -} -interface IWithCallSignatures4 { - (a: number): string; - (a: string, b: number): number; -} - -// With no call signature | callSignatures -var x: IWithNoCallSignatures | IWithCallSignatures = a => a.toString(); - -// With call signatures with different return type -var x2: IWithCallSignatures | IWithCallSignatures2 = a => a.toString(); // Like iWithCallSignatures -var x2: IWithCallSignatures | IWithCallSignatures2 = a => a; // Like iWithCallSignatures2 - -// With call signatures of mismatching parameter type -var x3: IWithCallSignatures | IWithCallSignatures3 = a => /*here a should be any*/ a.toString(); - -// With call signature count mismatch -var x4: IWithCallSignatures | IWithCallSignatures4 = a => - ) { return a.toString(); }; +var x4 = function (a) { return a.toString(); };