Skip to content

Commit 5fdc908

Browse files
committed
Deferred type checking of function expressions to support circular definitions.
1 parent 5b1789b commit 5fdc908

File tree

1 file changed

+96
-16
lines changed

1 file changed

+96
-16
lines changed

src/compiler/checker.ts

Lines changed: 96 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4524,25 +4524,26 @@ module ts {
45244524
}
45254525
}
45264526
}
4527+
checkSignatureDeclaration(node);
45274528
}
45284529
}
4529-
if (fullTypeCheck && !(links.flags & NodeCheckFlags.TypeChecked)) {
4530-
checkSignatureDeclaration(node);
4530+
return type;
4531+
}
4532+
4533+
function checkFunctionExpressionBody(node: FunctionExpression) {
4534+
if (node.type) {
4535+
checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, getTypeFromTypeNode(node.type));
4536+
}
4537+
if (node.body.kind === SyntaxKind.FunctionBlock) {
4538+
checkSourceElement(node.body);
4539+
}
4540+
else {
4541+
var exprType = checkExpression(node.body);
45314542
if (node.type) {
4532-
checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, getTypeFromTypeNode(node.type));
4533-
}
4534-
if (node.body.kind === SyntaxKind.FunctionBlock) {
4535-
checkSourceElement(node.body);
4543+
checkTypeAssignableTo(exprType, getTypeFromTypeNode(node.type), node.body, undefined, undefined);
45364544
}
4537-
else {
4538-
var exprType = checkExpression(node.body);
4539-
if (node.type) {
4540-
checkTypeAssignableTo(exprType, getTypeFromTypeNode(node.type), node.body, undefined, undefined);
4541-
}
4542-
}
4543-
links.flags |= NodeCheckFlags.TypeChecked;
4545+
checkFunctionExpressionBodies(node.body);
45444546
}
4545-
return type;
45464547
}
45474548

45484549
function checkArithmeticOperandType(operand: Node, type: Type, diagnostic: DiagnosticMessage): boolean {
@@ -6414,9 +6415,10 @@ module ts {
64146415
case SyntaxKind.FunctionDeclaration:
64156416
return checkFunctionDeclaration(<FunctionDeclaration>node);
64166417
case SyntaxKind.Block:
6418+
return checkBlock(<Block>node);
64176419
case SyntaxKind.FunctionBlock:
64186420
case SyntaxKind.ModuleBlock:
6419-
return checkBlock(<Block>node);
6421+
return checkBody(<Block>node);
64206422
case SyntaxKind.VariableStatement:
64216423
return checkVariableStatement(<VariableStatement>node);
64226424
case SyntaxKind.ExpressionStatement:
@@ -6463,13 +6465,91 @@ module ts {
64636465
}
64646466
}
64656467

6468+
// Function expression bodies are checked after all statements in the enclosing body. This is to ensure
6469+
// constructs like the following are permitted:
6470+
// var foo = function () {
6471+
// var s = foo();
6472+
// return "hello";
6473+
// }
6474+
// Here, performing a full type check of the body of the function expression whilst in the process of
6475+
// determining the type of foo would cause foo to be given type any because of the recursive reference.
6476+
// Delaying the type check of the body ensures foo has been assigned a type.
6477+
function checkFunctionExpressionBodies(node: Node): void {
6478+
switch (node.kind) {
6479+
case SyntaxKind.FunctionExpression:
6480+
case SyntaxKind.ArrowFunction:
6481+
forEach((<FunctionDeclaration>node).parameters, checkFunctionExpressionBodies);
6482+
checkFunctionExpressionBody(<FunctionExpression>node);
6483+
break;
6484+
case SyntaxKind.Method:
6485+
case SyntaxKind.Constructor:
6486+
case SyntaxKind.GetAccessor:
6487+
case SyntaxKind.SetAccessor:
6488+
case SyntaxKind.FunctionDeclaration:
6489+
forEach((<FunctionDeclaration>node).parameters, checkFunctionExpressionBodies);
6490+
break;
6491+
case SyntaxKind.WithStatement:
6492+
checkFunctionExpressionBodies((<WithStatement>node).expression);
6493+
break;
6494+
case SyntaxKind.Parameter:
6495+
case SyntaxKind.Property:
6496+
case SyntaxKind.ArrayLiteral:
6497+
case SyntaxKind.ObjectLiteral:
6498+
case SyntaxKind.PropertyAssignment:
6499+
case SyntaxKind.PropertyAccess:
6500+
case SyntaxKind.IndexedAccess:
6501+
case SyntaxKind.CallExpression:
6502+
case SyntaxKind.NewExpression:
6503+
case SyntaxKind.TypeAssertion:
6504+
case SyntaxKind.ParenExpression:
6505+
case SyntaxKind.PrefixOperator:
6506+
case SyntaxKind.PostfixOperator:
6507+
case SyntaxKind.BinaryExpression:
6508+
case SyntaxKind.ConditionalExpression:
6509+
case SyntaxKind.Block:
6510+
case SyntaxKind.FunctionBlock:
6511+
case SyntaxKind.ModuleBlock:
6512+
case SyntaxKind.VariableStatement:
6513+
case SyntaxKind.ExpressionStatement:
6514+
case SyntaxKind.IfStatement:
6515+
case SyntaxKind.DoStatement:
6516+
case SyntaxKind.WhileStatement:
6517+
case SyntaxKind.ForStatement:
6518+
case SyntaxKind.ForInStatement:
6519+
case SyntaxKind.ContinueStatement:
6520+
case SyntaxKind.BreakStatement:
6521+
case SyntaxKind.ReturnStatement:
6522+
case SyntaxKind.SwitchStatement:
6523+
case SyntaxKind.CaseClause:
6524+
case SyntaxKind.DefaultClause:
6525+
case SyntaxKind.LabelledStatement:
6526+
case SyntaxKind.ThrowStatement:
6527+
case SyntaxKind.TryStatement:
6528+
case SyntaxKind.TryBlock:
6529+
case SyntaxKind.CatchBlock:
6530+
case SyntaxKind.FinallyBlock:
6531+
case SyntaxKind.VariableDeclaration:
6532+
case SyntaxKind.ClassDeclaration:
6533+
case SyntaxKind.EnumDeclaration:
6534+
case SyntaxKind.EnumMember:
6535+
case SyntaxKind.SourceFile:
6536+
forEachChild(node, checkFunctionExpressionBodies);
6537+
break;
6538+
}
6539+
}
6540+
6541+
function checkBody(node: Block) {
6542+
checkBlock(node);
6543+
checkFunctionExpressionBodies(node);
6544+
}
6545+
64666546
// Fully type check a source file and collect the relevant diagnostics.
64676547
function checkSourceFile(node: SourceFile) {
64686548
var links = getNodeLinks(node);
64696549
if (!(links.flags & NodeCheckFlags.TypeChecked)) {
64706550
emitExtends = false;
64716551
potentialThisCollisions.length = 0;
6472-
forEach(node.statements, checkSourceElement);
6552+
checkBody(node);
64736553
if (isExternalModule(node)) {
64746554
var symbol = getExportAssignmentSymbol(node.symbol);
64756555
if (symbol && symbol.flags & SymbolFlags.Import) {

0 commit comments

Comments
 (0)