Skip to content

Commit e8ecf0e

Browse files
committed
Track return statements in IIFE using a flow label
1 parent 1647d20 commit e8ecf0e

4 files changed

Lines changed: 89 additions & 72 deletions

File tree

src/compiler/binder.ts

Lines changed: 53 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,15 @@ namespace ts {
104104
let seenThisKeyword: boolean;
105105

106106
// state used by reachability checks
107-
let hasExplicitReturn: boolean;
108107
let currentFlow: FlowNode;
109108
let currentBreakTarget: FlowLabel;
110109
let currentContinueTarget: FlowLabel;
110+
let currentReturnTarget: FlowLabel;
111111
let currentTrueTarget: FlowLabel;
112112
let currentFalseTarget: FlowLabel;
113113
let preSwitchCaseFlow: FlowNode;
114114
let activeLabels: ActiveLabel[];
115+
let hasExplicitReturn: boolean;
115116

116117
// state used for emit helpers
117118
let hasClassExtends: boolean;
@@ -156,13 +157,14 @@ namespace ts {
156157
blockScopeContainer = undefined;
157158
lastContainer = undefined;
158159
seenThisKeyword = false;
159-
hasExplicitReturn = false;
160160
currentFlow = undefined;
161161
currentBreakTarget = undefined;
162162
currentContinueTarget = undefined;
163+
currentReturnTarget = undefined;
163164
currentTrueTarget = undefined;
164165
currentFalseTarget = undefined;
165166
activeLabels = undefined;
167+
hasExplicitReturn = false;
166168
hasClassExtends = false;
167169
hasAsyncFunctions = false;
168170
hasDecorators = false;
@@ -443,44 +445,51 @@ namespace ts {
443445
blockScopeContainer.locals = undefined;
444446
}
445447

446-
let savedHasExplicitReturn: boolean;
447-
let savedCurrentFlow: FlowNode;
448-
let savedBreakTarget: FlowLabel;
449-
let savedContinueTarget: FlowLabel;
450-
let savedActiveLabels: ActiveLabel[];
448+
let saveCurrentFlow: FlowNode;
449+
let saveBreakTarget: FlowLabel;
450+
let saveContinueTarget: FlowLabel;
451+
let saveReturnTarget: FlowLabel;
452+
let saveActiveLabels: ActiveLabel[];
453+
let saveHasExplicitReturn: boolean;
454+
let isIIFE: boolean;
451455

452456
const kind = node.kind;
453457
let flags = node.flags;
454458

455-
// reset all reachability check related flags on node (for incremental scenarios)
456-
flags &= ~NodeFlags.ReachabilityCheckFlags;
457-
458-
// reset all emit helper flags on node (for incremental scenarios)
459-
flags &= ~NodeFlags.EmitHelperFlags;
459+
// Reset all reachability check related flags on node (for incremental scenarios)
460+
// Reset all emit helper flags on node (for incremental scenarios)
461+
flags &= ~NodeFlags.ReachabilityAndEmitFlags;
460462

461463
if (kind === SyntaxKind.InterfaceDeclaration) {
462464
seenThisKeyword = false;
463465
}
464466

465467
const saveState = kind === SyntaxKind.SourceFile || kind === SyntaxKind.ModuleBlock || isFunctionLikeKind(kind);
466468
if (saveState) {
467-
savedHasExplicitReturn = hasExplicitReturn;
468-
savedCurrentFlow = currentFlow;
469-
savedBreakTarget = currentBreakTarget;
470-
savedContinueTarget = currentContinueTarget;
471-
savedActiveLabels = activeLabels;
472-
473-
hasExplicitReturn = false;
474-
currentFlow = { flags: FlowFlags.Start };
475-
if (kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction) {
476-
(<FlowStart>currentFlow).container = <FunctionExpression | ArrowFunction>node;
469+
saveCurrentFlow = currentFlow;
470+
saveBreakTarget = currentBreakTarget;
471+
saveContinueTarget = currentContinueTarget;
472+
saveReturnTarget = currentReturnTarget;
473+
saveActiveLabels = activeLabels;
474+
saveHasExplicitReturn = hasExplicitReturn;
475+
isIIFE = (kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction) && !!getImmediatelyInvokedFunctionExpression(node);
476+
if (isIIFE) {
477+
currentReturnTarget = createBranchLabel();
478+
}
479+
else {
480+
currentFlow = { flags: FlowFlags.Start };
481+
if (kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction) {
482+
(<FlowStart>currentFlow).container = <FunctionExpression | ArrowFunction>node;
483+
}
484+
currentReturnTarget = undefined;
477485
}
478486
currentBreakTarget = undefined;
479487
currentContinueTarget = undefined;
480488
activeLabels = undefined;
489+
hasExplicitReturn = false;
481490
}
482491

483-
if (isInJavaScriptFile(node) && node.jsDocComment) {
492+
if (node.flags & NodeFlags.JavaScriptFile && node.jsDocComment) {
484493
bind(node.jsDocComment);
485494
}
486495

@@ -518,11 +527,18 @@ namespace ts {
518527
node.flags = flags;
519528

520529
if (saveState) {
521-
hasExplicitReturn = savedHasExplicitReturn;
522-
currentFlow = savedCurrentFlow;
523-
currentBreakTarget = savedBreakTarget;
524-
currentContinueTarget = savedContinueTarget;
525-
activeLabels = savedActiveLabels;
530+
if (isIIFE) {
531+
addAntecedent(currentReturnTarget, currentFlow);
532+
currentFlow = finishFlowLabel(currentReturnTarget);
533+
}
534+
else {
535+
currentFlow = saveCurrentFlow;
536+
}
537+
currentBreakTarget = saveBreakTarget;
538+
currentContinueTarget = saveContinueTarget;
539+
currentReturnTarget = saveReturnTarget;
540+
activeLabels = saveActiveLabels;
541+
hasExplicitReturn = saveHasExplicitReturn;
526542
}
527543

528544
container = saveContainer;
@@ -854,6 +870,9 @@ namespace ts {
854870
bind(node.expression);
855871
if (node.kind === SyntaxKind.ReturnStatement) {
856872
hasExplicitReturn = true;
873+
if (currentReturnTarget) {
874+
addAntecedent(currentReturnTarget, currentFlow);
875+
}
857876
}
858877
currentFlow = unreachableFlow;
859878
}
@@ -1105,7 +1124,6 @@ namespace ts {
11051124
}
11061125

11071126
function bindCallExpressionFlow(node: CallExpression) {
1108-
forEachChild(node, bind);
11091127
// If the target of the call expression is a function expression or arrow function we have
11101128
// an immediately invoked function expression (IIFE). Initialize the flowNode property to
11111129
// the current control flow (which includes evaluation of the IIFE arguments).
@@ -1114,7 +1132,12 @@ namespace ts {
11141132
expr = (<ParenthesizedExpression>expr).expression;
11151133
}
11161134
if (expr.kind === SyntaxKind.FunctionExpression || expr.kind === SyntaxKind.ArrowFunction) {
1117-
node.flowNode = currentFlow;
1135+
forEach(node.typeArguments, bind);
1136+
forEach(node.arguments, bind);
1137+
bind(node.expression);
1138+
}
1139+
else {
1140+
forEachChild(node, bind);
11181141
}
11191142
}
11201143

src/compiler/checker.ts

Lines changed: 21 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7695,20 +7695,11 @@ namespace ts {
76957695
getTypeAtFlowLoopLabel(<FlowLabel>flow);
76967696
}
76977697
else if (flow.flags & FlowFlags.Start) {
7698+
// Check if we should continue with the control flow of the containing function.
76987699
const container = (<FlowStart>flow).container;
7699-
if (container) {
7700-
// If container is an IIFE continue with the control flow associated with the
7701-
// call expression node.
7702-
const iife = getImmediatelyInvokedFunctionExpression(<FunctionExpression>container);
7703-
if (iife && iife.flowNode) {
7704-
flow = iife.flowNode;
7705-
continue;
7706-
}
7707-
// Check if we should continue with the control flow of the containing function.
7708-
if (includeOuterFunctions && container.flowNode) {
7709-
flow = container.flowNode;
7710-
continue;
7711-
}
7700+
if (container && includeOuterFunctions) {
7701+
flow = container.flowNode;
7702+
continue;
77127703
}
77137704
// At the top of the flow we have the initial type.
77147705
type = initialType;
@@ -8652,23 +8643,25 @@ namespace ts {
86528643
function getContextuallyTypedParameterType(parameter: ParameterDeclaration): Type {
86538644
const func = parameter.parent;
86548645
if (isContextSensitiveFunctionOrObjectLiteralMethod(func)) {
8655-
const iife = getImmediatelyInvokedFunctionExpression(func);
8656-
if (iife) {
8657-
const indexOfParameter = indexOf(func.parameters, parameter);
8658-
if (iife.arguments && indexOfParameter < iife.arguments.length) {
8659-
if (parameter.dotDotDotToken) {
8660-
const restTypes: Type[] = [];
8661-
for (let i = indexOfParameter; i < iife.arguments.length; i++) {
8662-
restTypes.push(getTypeOfExpression(iife.arguments[i]));
8646+
if (func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction) {
8647+
const iife = getImmediatelyInvokedFunctionExpression(func);
8648+
if (iife) {
8649+
const indexOfParameter = indexOf(func.parameters, parameter);
8650+
if (iife.arguments && indexOfParameter < iife.arguments.length) {
8651+
if (parameter.dotDotDotToken) {
8652+
const restTypes: Type[] = [];
8653+
for (let i = indexOfParameter; i < iife.arguments.length; i++) {
8654+
restTypes.push(getTypeOfExpression(iife.arguments[i]));
8655+
}
8656+
return createArrayType(getUnionType(restTypes));
86638657
}
8664-
return createArrayType(getUnionType(restTypes));
8658+
const links = getNodeLinks(iife);
8659+
const cached = links.resolvedSignature;
8660+
links.resolvedSignature = anySignature;
8661+
const type = checkExpression(iife.arguments[indexOfParameter]);
8662+
links.resolvedSignature = cached;
8663+
return type;
86658664
}
8666-
const links = getNodeLinks(iife);
8667-
const cached = links.resolvedSignature;
8668-
links.resolvedSignature = anySignature;
8669-
const type = checkExpression(iife.arguments[indexOfParameter]);
8670-
links.resolvedSignature = cached;
8671-
return type;
86728665
}
86738666
}
86748667
const contextualSignature = getContextualSignature(func);
@@ -8691,20 +8684,6 @@ namespace ts {
86918684
return undefined;
86928685
}
86938686

8694-
function getImmediatelyInvokedFunctionExpression(func: FunctionExpression | MethodDeclaration) {
8695-
if (isFunctionExpressionOrArrowFunction(func)) {
8696-
let prev: Node = func;
8697-
let parent: Node = func.parent;
8698-
while (parent.kind === SyntaxKind.ParenthesizedExpression) {
8699-
prev = parent;
8700-
parent = parent.parent;
8701-
}
8702-
if (parent.kind === SyntaxKind.CallExpression && (parent as CallExpression).expression === prev) {
8703-
return parent as CallExpression;
8704-
}
8705-
}
8706-
}
8707-
87088687
// In a variable, parameter or property declaration with a type annotation,
87098688
// the contextual type of an initializer expression is the type of the variable, parameter or property.
87108689
// Otherwise, in a parameter declaration of a contextually typed function expression,

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ namespace ts {
416416

417417
ReachabilityCheckFlags = HasImplicitReturn | HasExplicitReturn,
418418
EmitHelperFlags = HasClassExtends | HasDecorators | HasParamDecorators | HasAsyncFunctions,
419+
ReachabilityAndEmitFlags = ReachabilityCheckFlags | EmitHelperFlags,
419420

420421
// Parsing context flags
421422
ContextFlags = DisallowInContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile,

src/compiler/utilities.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -987,6 +987,20 @@ namespace ts {
987987
}
988988
}
989989

990+
export function getImmediatelyInvokedFunctionExpression(func: Node): CallExpression {
991+
if (func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction) {
992+
let prev = func;
993+
let parent = func.parent;
994+
while (parent.kind === SyntaxKind.ParenthesizedExpression) {
995+
prev = parent;
996+
parent = parent.parent;
997+
}
998+
if (parent.kind === SyntaxKind.CallExpression && (parent as CallExpression).expression === prev) {
999+
return parent as CallExpression;
1000+
}
1001+
}
1002+
}
1003+
9901004
/**
9911005
* Determines whether a node is a property or element access expression for super.
9921006
*/

0 commit comments

Comments
 (0)