Skip to content

Commit e157763

Browse files
committed
Merge pull request #610 from Microsoft/circularFunctionExpressions
Circular function expressions
2 parents edbc41b + 314a0d6 commit e157763

8 files changed

+114
-36
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 {
@@ -6438,9 +6439,10 @@ module ts {
64386439
case SyntaxKind.FunctionDeclaration:
64396440
return checkFunctionDeclaration(<FunctionDeclaration>node);
64406441
case SyntaxKind.Block:
6442+
return checkBlock(<Block>node);
64416443
case SyntaxKind.FunctionBlock:
64426444
case SyntaxKind.ModuleBlock:
6443-
return checkBlock(<Block>node);
6445+
return checkBody(<Block>node);
64446446
case SyntaxKind.VariableStatement:
64456447
return checkVariableStatement(<VariableStatement>node);
64466448
case SyntaxKind.ExpressionStatement:
@@ -6487,13 +6489,91 @@ module ts {
64876489
}
64886490
}
64896491

6492+
// Function expression bodies are checked after all statements in the enclosing body. This is to ensure
6493+
// constructs like the following are permitted:
6494+
// var foo = function () {
6495+
// var s = foo();
6496+
// return "hello";
6497+
// }
6498+
// Here, performing a full type check of the body of the function expression whilst in the process of
6499+
// determining the type of foo would cause foo to be given type any because of the recursive reference.
6500+
// Delaying the type check of the body ensures foo has been assigned a type.
6501+
function checkFunctionExpressionBodies(node: Node): void {
6502+
switch (node.kind) {
6503+
case SyntaxKind.FunctionExpression:
6504+
case SyntaxKind.ArrowFunction:
6505+
forEach((<FunctionDeclaration>node).parameters, checkFunctionExpressionBodies);
6506+
checkFunctionExpressionBody(<FunctionExpression>node);
6507+
break;
6508+
case SyntaxKind.Method:
6509+
case SyntaxKind.Constructor:
6510+
case SyntaxKind.GetAccessor:
6511+
case SyntaxKind.SetAccessor:
6512+
case SyntaxKind.FunctionDeclaration:
6513+
forEach((<FunctionDeclaration>node).parameters, checkFunctionExpressionBodies);
6514+
break;
6515+
case SyntaxKind.WithStatement:
6516+
checkFunctionExpressionBodies((<WithStatement>node).expression);
6517+
break;
6518+
case SyntaxKind.Parameter:
6519+
case SyntaxKind.Property:
6520+
case SyntaxKind.ArrayLiteral:
6521+
case SyntaxKind.ObjectLiteral:
6522+
case SyntaxKind.PropertyAssignment:
6523+
case SyntaxKind.PropertyAccess:
6524+
case SyntaxKind.IndexedAccess:
6525+
case SyntaxKind.CallExpression:
6526+
case SyntaxKind.NewExpression:
6527+
case SyntaxKind.TypeAssertion:
6528+
case SyntaxKind.ParenExpression:
6529+
case SyntaxKind.PrefixOperator:
6530+
case SyntaxKind.PostfixOperator:
6531+
case SyntaxKind.BinaryExpression:
6532+
case SyntaxKind.ConditionalExpression:
6533+
case SyntaxKind.Block:
6534+
case SyntaxKind.FunctionBlock:
6535+
case SyntaxKind.ModuleBlock:
6536+
case SyntaxKind.VariableStatement:
6537+
case SyntaxKind.ExpressionStatement:
6538+
case SyntaxKind.IfStatement:
6539+
case SyntaxKind.DoStatement:
6540+
case SyntaxKind.WhileStatement:
6541+
case SyntaxKind.ForStatement:
6542+
case SyntaxKind.ForInStatement:
6543+
case SyntaxKind.ContinueStatement:
6544+
case SyntaxKind.BreakStatement:
6545+
case SyntaxKind.ReturnStatement:
6546+
case SyntaxKind.SwitchStatement:
6547+
case SyntaxKind.CaseClause:
6548+
case SyntaxKind.DefaultClause:
6549+
case SyntaxKind.LabelledStatement:
6550+
case SyntaxKind.ThrowStatement:
6551+
case SyntaxKind.TryStatement:
6552+
case SyntaxKind.TryBlock:
6553+
case SyntaxKind.CatchBlock:
6554+
case SyntaxKind.FinallyBlock:
6555+
case SyntaxKind.VariableDeclaration:
6556+
case SyntaxKind.ClassDeclaration:
6557+
case SyntaxKind.EnumDeclaration:
6558+
case SyntaxKind.EnumMember:
6559+
case SyntaxKind.SourceFile:
6560+
forEachChild(node, checkFunctionExpressionBodies);
6561+
break;
6562+
}
6563+
}
6564+
6565+
function checkBody(node: Block) {
6566+
checkBlock(node);
6567+
checkFunctionExpressionBodies(node);
6568+
}
6569+
64906570
// Fully type check a source file and collect the relevant diagnostics.
64916571
function checkSourceFile(node: SourceFile) {
64926572
var links = getNodeLinks(node);
64936573
if (!(links.flags & NodeCheckFlags.TypeChecked)) {
64946574
emitExtends = false;
64956575
potentialThisCollisions.length = 0;
6496-
forEach(node.statements, checkSourceElement);
6576+
checkBody(node);
64976577
if (isExternalModule(node)) {
64986578
var symbol = getExportAssignmentSymbol(node.symbol);
64996579
if (symbol && symbol.flags & SymbolFlags.Import) {

tests/baselines/reference/declFileTypeofFunction.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,6 @@ declare function b1(): typeof b1;
7373
declare function foo(): typeof foo;
7474
declare var foo1: typeof foo;
7575
declare var foo2: typeof foo;
76-
declare var foo3: any;
77-
declare var x: any;
76+
declare var foo3: () => any;
77+
declare var x: () => any;
7878
declare function foo5(x: number): (x: number) => number;

tests/baselines/reference/declFileTypeofFunction.types

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,18 +54,18 @@ var foo2 = foo;
5454
>foo : () => typeof foo
5555

5656
var foo3 = function () {
57-
>foo3 : any
57+
>foo3 : () => any
5858
>function () { return foo3;} : () => any
5959

6060
return foo3;
61-
>foo3 : any
61+
>foo3 : () => any
6262
}
6363
var x = () => {
64-
>x : any
64+
>x : () => any
6565
>() => { return x;} : () => any
6666

6767
return x;
68-
>x : any
68+
>x : () => any
6969
}
7070

7171
function foo5(x: number) {

tests/baselines/reference/defaultArgsForwardReferencing.errors.txt

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
==== tests/cases/compiler/defaultArgsForwardReferencing.ts (12 errors) ====
1+
==== tests/cases/compiler/defaultArgsForwardReferencing.ts (10 errors) ====
22
function left(a, b = a, c = b) {
33
a;
44
b;
@@ -37,11 +37,7 @@
3737
}
3838

3939
function defaultArgFunction(a = function () { return b; }, b = 1) { }
40-
~
41-
!!! Initializer of parameter 'a' cannot reference identifier 'b' declared after it.
4240
function defaultArgArrow(a = () => () => b, b = 3) { }
43-
~
44-
!!! Initializer of parameter 'a' cannot reference identifier 'b' declared after it.
4541

4642
class C {
4743
constructor(a = b, b = 1) { }

tests/baselines/reference/namedFunctionExpressionCall.types

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
=== tests/cases/compiler/namedFunctionExpressionCall.ts ===
22
var recurser = function foo() {
3-
>recurser : any
3+
>recurser : () => void
44
>function foo() { // using the local name foo(); // using the globally visible name recurser();} : () => void
55
>foo : () => void
66

@@ -11,8 +11,8 @@ var recurser = function foo() {
1111

1212
// using the globally visible name
1313
recurser();
14-
>recurser() : any
15-
>recurser : any
14+
>recurser() : void
15+
>recurser : () => void
1616

1717
};
1818

tests/baselines/reference/parserStatementIsNotAMemberVariableDeclaration1.errors.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserStatementIsNotAMemberVariableDeclaration1.ts (1 errors) ====
1+
==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/parserStatementIsNotAMemberVariableDeclaration1.ts (2 errors) ====
22
return {
33
~~~~~~
44
!!! A 'return' statement can only be used within a function body.
@@ -7,6 +7,8 @@
77

88
// 'private' should not be considered a member variable here.
99
private[key] = value;
10+
~~~~~~~
11+
!!! Cannot find name 'private'.
1012

1113
}
1214

tests/baselines/reference/recursiveInitializer.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,10 @@ var b4 = (!b4) && b4; // expected boolean here. actually 'any'
6969

7070
// (x:string) => any
7171
var f = (x: string) => f(x);
72-
>f : any
72+
>f : (x: string) => any
7373
>(x: string) => f(x) : (x: string) => any
7474
>x : string
7575
>f(x) : any
76-
>f : any
76+
>f : (x: string) => any
7777
>x : string
7878

tests/baselines/reference/underscoreTest1.types

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,7 @@ $('#underscore_button').bind('click', buttonView.onClick);
601601
>onClick : () => void
602602

603603
var fibonacci = _.memoize(function (n) {
604-
>fibonacci : any
604+
>fibonacci : (n: any) => any
605605
>_.memoize(function (n) { return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);}) : (n: any) => any
606606
>_.memoize : <T extends Function>(func: T, hashFunction?: Function) => T
607607
>_ : Underscore.Static
@@ -616,11 +616,11 @@ var fibonacci = _.memoize(function (n) {
616616
>n : any
617617
>fibonacci(n - 1) + fibonacci(n - 2) : any
618618
>fibonacci(n - 1) : any
619-
>fibonacci : any
619+
>fibonacci : (n: any) => any
620620
>n - 1 : number
621621
>n : any
622622
>fibonacci(n - 2) : any
623-
>fibonacci : any
623+
>fibonacci : (n: any) => any
624624
>n - 2 : number
625625
>n : any
626626

0 commit comments

Comments
 (0)