diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ad18aa47bf9b8..c34666992d699 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4524,25 +4524,26 @@ module ts { } } } + checkSignatureDeclaration(node); } } - if (fullTypeCheck && !(links.flags & NodeCheckFlags.TypeChecked)) { - checkSignatureDeclaration(node); + return type; + } + + function checkFunctionExpressionBody(node: FunctionExpression) { + if (node.type) { + checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, getTypeFromTypeNode(node.type)); + } + if (node.body.kind === SyntaxKind.FunctionBlock) { + checkSourceElement(node.body); + } + else { + var exprType = checkExpression(node.body); if (node.type) { - checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, getTypeFromTypeNode(node.type)); - } - if (node.body.kind === SyntaxKind.FunctionBlock) { - checkSourceElement(node.body); + checkTypeAssignableTo(exprType, getTypeFromTypeNode(node.type), node.body, undefined, undefined); } - else { - var exprType = checkExpression(node.body); - if (node.type) { - checkTypeAssignableTo(exprType, getTypeFromTypeNode(node.type), node.body, undefined, undefined); - } - } - links.flags |= NodeCheckFlags.TypeChecked; + checkFunctionExpressionBodies(node.body); } - return type; } function checkArithmeticOperandType(operand: Node, type: Type, diagnostic: DiagnosticMessage): boolean { @@ -6414,9 +6415,10 @@ module ts { case SyntaxKind.FunctionDeclaration: return checkFunctionDeclaration(node); case SyntaxKind.Block: + return checkBlock(node); case SyntaxKind.FunctionBlock: case SyntaxKind.ModuleBlock: - return checkBlock(node); + return checkBody(node); case SyntaxKind.VariableStatement: return checkVariableStatement(node); case SyntaxKind.ExpressionStatement: @@ -6463,13 +6465,91 @@ module ts { } } + // Function expression bodies are checked after all statements in the enclosing body. This is to ensure + // constructs like the following are permitted: + // var foo = function () { + // var s = foo(); + // return "hello"; + // } + // Here, performing a full type check of the body of the function expression whilst in the process of + // determining the type of foo would cause foo to be given type any because of the recursive reference. + // Delaying the type check of the body ensures foo has been assigned a type. + function checkFunctionExpressionBodies(node: Node): void { + switch (node.kind) { + case SyntaxKind.FunctionExpression: + case SyntaxKind.ArrowFunction: + forEach((node).parameters, checkFunctionExpressionBodies); + checkFunctionExpressionBody(node); + break; + case SyntaxKind.Method: + case SyntaxKind.Constructor: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.FunctionDeclaration: + forEach((node).parameters, checkFunctionExpressionBodies); + break; + case SyntaxKind.WithStatement: + checkFunctionExpressionBodies((node).expression); + break; + case SyntaxKind.Parameter: + case SyntaxKind.Property: + case SyntaxKind.ArrayLiteral: + case SyntaxKind.ObjectLiteral: + case SyntaxKind.PropertyAssignment: + case SyntaxKind.PropertyAccess: + case SyntaxKind.IndexedAccess: + case SyntaxKind.CallExpression: + case SyntaxKind.NewExpression: + case SyntaxKind.TypeAssertion: + case SyntaxKind.ParenExpression: + case SyntaxKind.PrefixOperator: + case SyntaxKind.PostfixOperator: + case SyntaxKind.BinaryExpression: + case SyntaxKind.ConditionalExpression: + case SyntaxKind.Block: + case SyntaxKind.FunctionBlock: + case SyntaxKind.ModuleBlock: + case SyntaxKind.VariableStatement: + case SyntaxKind.ExpressionStatement: + case SyntaxKind.IfStatement: + case SyntaxKind.DoStatement: + case SyntaxKind.WhileStatement: + case SyntaxKind.ForStatement: + case SyntaxKind.ForInStatement: + case SyntaxKind.ContinueStatement: + case SyntaxKind.BreakStatement: + case SyntaxKind.ReturnStatement: + case SyntaxKind.SwitchStatement: + case SyntaxKind.CaseClause: + case SyntaxKind.DefaultClause: + case SyntaxKind.LabelledStatement: + case SyntaxKind.ThrowStatement: + case SyntaxKind.TryStatement: + case SyntaxKind.TryBlock: + case SyntaxKind.CatchBlock: + case SyntaxKind.FinallyBlock: + case SyntaxKind.VariableDeclaration: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.EnumDeclaration: + case SyntaxKind.EnumMember: + case SyntaxKind.SourceFile: + forEachChild(node, checkFunctionExpressionBodies); + break; + } + } + + function checkBody(node: Block) { + checkBlock(node); + checkFunctionExpressionBodies(node); + } + // Fully type check a source file and collect the relevant diagnostics. function checkSourceFile(node: SourceFile) { var links = getNodeLinks(node); if (!(links.flags & NodeCheckFlags.TypeChecked)) { emitExtends = false; potentialThisCollisions.length = 0; - forEach(node.statements, checkSourceElement); + checkBody(node); if (isExternalModule(node)) { var symbol = getExportAssignmentSymbol(node.symbol); if (symbol && symbol.flags & SymbolFlags.Import) { diff --git a/tests/baselines/reference/declFileTypeofFunction.js b/tests/baselines/reference/declFileTypeofFunction.js index 67475c557d352..ea876e6dad891 100644 --- a/tests/baselines/reference/declFileTypeofFunction.js +++ b/tests/baselines/reference/declFileTypeofFunction.js @@ -73,6 +73,6 @@ declare function b1(): typeof b1; declare function foo(): typeof foo; declare var foo1: typeof foo; declare var foo2: typeof foo; -declare var foo3: any; -declare var x: any; +declare var foo3: () => any; +declare var x: () => any; declare function foo5(x: number): (x: number) => number; diff --git a/tests/baselines/reference/declFileTypeofFunction.types b/tests/baselines/reference/declFileTypeofFunction.types index 5f1cc26168f75..bede9ba254bb6 100644 --- a/tests/baselines/reference/declFileTypeofFunction.types +++ b/tests/baselines/reference/declFileTypeofFunction.types @@ -54,18 +54,18 @@ var foo2 = foo; >foo : () => typeof foo var foo3 = function () { ->foo3 : any +>foo3 : () => any >function () { return foo3;} : () => any return foo3; ->foo3 : any +>foo3 : () => any } var x = () => { ->x : any +>x : () => any >() => { return x;} : () => any return x; ->x : any +>x : () => any } function foo5(x: number) { diff --git a/tests/baselines/reference/defaultArgsForwardReferencing.errors.txt b/tests/baselines/reference/defaultArgsForwardReferencing.errors.txt index 7f317049695da..2b8250f6fb442 100644 --- a/tests/baselines/reference/defaultArgsForwardReferencing.errors.txt +++ b/tests/baselines/reference/defaultArgsForwardReferencing.errors.txt @@ -1,4 +1,4 @@ -==== tests/cases/compiler/defaultArgsForwardReferencing.ts (12 errors) ==== +==== tests/cases/compiler/defaultArgsForwardReferencing.ts (10 errors) ==== function left(a, b = a, c = b) { a; b; @@ -37,11 +37,7 @@ } function defaultArgFunction(a = function () { return b; }, b = 1) { } - ~ -!!! Initializer of parameter 'a' cannot reference identifier 'b' declared after it. function defaultArgArrow(a = () => () => b, b = 3) { } - ~ -!!! Initializer of parameter 'a' cannot reference identifier 'b' declared after it. class C { constructor(a = b, b = 1) { } diff --git a/tests/baselines/reference/namedFunctionExpressionCall.types b/tests/baselines/reference/namedFunctionExpressionCall.types index 018cf64afcda2..f70bc8c17dcb6 100644 --- a/tests/baselines/reference/namedFunctionExpressionCall.types +++ b/tests/baselines/reference/namedFunctionExpressionCall.types @@ -1,6 +1,6 @@ === tests/cases/compiler/namedFunctionExpressionCall.ts === var recurser = function foo() { ->recurser : any +>recurser : () => void >function foo() { // using the local name foo(); // using the globally visible name recurser();} : () => void >foo : () => void @@ -11,8 +11,8 @@ var recurser = function foo() { // using the globally visible name recurser(); ->recurser() : any ->recurser : any +>recurser() : void +>recurser : () => void }; diff --git a/tests/baselines/reference/parserStatementIsNotAMemberVariableDeclaration1.errors.txt b/tests/baselines/reference/parserStatementIsNotAMemberVariableDeclaration1.errors.txt index 0d8f7d23d3b06..534f3371ededa 100644 --- a/tests/baselines/reference/parserStatementIsNotAMemberVariableDeclaration1.errors.txt +++ b/tests/baselines/reference/parserStatementIsNotAMemberVariableDeclaration1.errors.txt @@ -1,4 +1,4 @@ -==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserStatementIsNotAMemberVariableDeclaration1.ts (1 errors) ==== +==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserStatementIsNotAMemberVariableDeclaration1.ts (2 errors) ==== return { ~~~~~~ !!! A 'return' statement can only be used within a function body. @@ -7,6 +7,8 @@ // 'private' should not be considered a member variable here. private[key] = value; + ~~~~~~~ +!!! Cannot find name 'private'. } diff --git a/tests/baselines/reference/recursiveInitializer.types b/tests/baselines/reference/recursiveInitializer.types index 84d9dbd8b1938..540553ea529ed 100644 --- a/tests/baselines/reference/recursiveInitializer.types +++ b/tests/baselines/reference/recursiveInitializer.types @@ -69,10 +69,10 @@ var b4 = (!b4) && b4; // expected boolean here. actually 'any' // (x:string) => any var f = (x: string) => f(x); ->f : any +>f : (x: string) => any >(x: string) => f(x) : (x: string) => any >x : string >f(x) : any ->f : any +>f : (x: string) => any >x : string diff --git a/tests/baselines/reference/underscoreTest1.types b/tests/baselines/reference/underscoreTest1.types index 15cf42b9c7b17..115bc09195dbe 100644 --- a/tests/baselines/reference/underscoreTest1.types +++ b/tests/baselines/reference/underscoreTest1.types @@ -601,7 +601,7 @@ $('#underscore_button').bind('click', buttonView.onClick); >onClick : () => void var fibonacci = _.memoize(function (n) { ->fibonacci : any +>fibonacci : (n: any) => any >_.memoize(function (n) { return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);}) : (n: any) => any >_.memoize : (func: T, hashFunction?: Function) => T >_ : Underscore.Static @@ -616,11 +616,11 @@ var fibonacci = _.memoize(function (n) { >n : any >fibonacci(n - 1) + fibonacci(n - 2) : any >fibonacci(n - 1) : any ->fibonacci : any +>fibonacci : (n: any) => any >n - 1 : number >n : any >fibonacci(n - 2) : any ->fibonacci : any +>fibonacci : (n: any) => any >n - 2 : number >n : any