mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-14 19:16:17 -06:00
Track return statements in IIFE using a flow label
This commit is contained in:
parent
1647d20d90
commit
e8ecf0e615
@ -104,14 +104,15 @@ namespace ts {
|
||||
let seenThisKeyword: boolean;
|
||||
|
||||
// state used by reachability checks
|
||||
let hasExplicitReturn: boolean;
|
||||
let currentFlow: FlowNode;
|
||||
let currentBreakTarget: FlowLabel;
|
||||
let currentContinueTarget: FlowLabel;
|
||||
let currentReturnTarget: FlowLabel;
|
||||
let currentTrueTarget: FlowLabel;
|
||||
let currentFalseTarget: FlowLabel;
|
||||
let preSwitchCaseFlow: FlowNode;
|
||||
let activeLabels: ActiveLabel[];
|
||||
let hasExplicitReturn: boolean;
|
||||
|
||||
// state used for emit helpers
|
||||
let hasClassExtends: boolean;
|
||||
@ -156,13 +157,14 @@ namespace ts {
|
||||
blockScopeContainer = undefined;
|
||||
lastContainer = undefined;
|
||||
seenThisKeyword = false;
|
||||
hasExplicitReturn = false;
|
||||
currentFlow = undefined;
|
||||
currentBreakTarget = undefined;
|
||||
currentContinueTarget = undefined;
|
||||
currentReturnTarget = undefined;
|
||||
currentTrueTarget = undefined;
|
||||
currentFalseTarget = undefined;
|
||||
activeLabels = undefined;
|
||||
hasExplicitReturn = false;
|
||||
hasClassExtends = false;
|
||||
hasAsyncFunctions = false;
|
||||
hasDecorators = false;
|
||||
@ -443,20 +445,20 @@ namespace ts {
|
||||
blockScopeContainer.locals = undefined;
|
||||
}
|
||||
|
||||
let savedHasExplicitReturn: boolean;
|
||||
let savedCurrentFlow: FlowNode;
|
||||
let savedBreakTarget: FlowLabel;
|
||||
let savedContinueTarget: FlowLabel;
|
||||
let savedActiveLabels: ActiveLabel[];
|
||||
let saveCurrentFlow: FlowNode;
|
||||
let saveBreakTarget: FlowLabel;
|
||||
let saveContinueTarget: FlowLabel;
|
||||
let saveReturnTarget: FlowLabel;
|
||||
let saveActiveLabels: ActiveLabel[];
|
||||
let saveHasExplicitReturn: boolean;
|
||||
let isIIFE: boolean;
|
||||
|
||||
const kind = node.kind;
|
||||
let flags = node.flags;
|
||||
|
||||
// reset all reachability check related flags on node (for incremental scenarios)
|
||||
flags &= ~NodeFlags.ReachabilityCheckFlags;
|
||||
|
||||
// reset all emit helper flags on node (for incremental scenarios)
|
||||
flags &= ~NodeFlags.EmitHelperFlags;
|
||||
// Reset all reachability check related flags on node (for incremental scenarios)
|
||||
// Reset all emit helper flags on node (for incremental scenarios)
|
||||
flags &= ~NodeFlags.ReachabilityAndEmitFlags;
|
||||
|
||||
if (kind === SyntaxKind.InterfaceDeclaration) {
|
||||
seenThisKeyword = false;
|
||||
@ -464,23 +466,30 @@ namespace ts {
|
||||
|
||||
const saveState = kind === SyntaxKind.SourceFile || kind === SyntaxKind.ModuleBlock || isFunctionLikeKind(kind);
|
||||
if (saveState) {
|
||||
savedHasExplicitReturn = hasExplicitReturn;
|
||||
savedCurrentFlow = currentFlow;
|
||||
savedBreakTarget = currentBreakTarget;
|
||||
savedContinueTarget = currentContinueTarget;
|
||||
savedActiveLabels = activeLabels;
|
||||
|
||||
hasExplicitReturn = false;
|
||||
currentFlow = { flags: FlowFlags.Start };
|
||||
if (kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction) {
|
||||
(<FlowStart>currentFlow).container = <FunctionExpression | ArrowFunction>node;
|
||||
saveCurrentFlow = currentFlow;
|
||||
saveBreakTarget = currentBreakTarget;
|
||||
saveContinueTarget = currentContinueTarget;
|
||||
saveReturnTarget = currentReturnTarget;
|
||||
saveActiveLabels = activeLabels;
|
||||
saveHasExplicitReturn = hasExplicitReturn;
|
||||
isIIFE = (kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction) && !!getImmediatelyInvokedFunctionExpression(node);
|
||||
if (isIIFE) {
|
||||
currentReturnTarget = createBranchLabel();
|
||||
}
|
||||
else {
|
||||
currentFlow = { flags: FlowFlags.Start };
|
||||
if (kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction) {
|
||||
(<FlowStart>currentFlow).container = <FunctionExpression | ArrowFunction>node;
|
||||
}
|
||||
currentReturnTarget = undefined;
|
||||
}
|
||||
currentBreakTarget = undefined;
|
||||
currentContinueTarget = undefined;
|
||||
activeLabels = undefined;
|
||||
hasExplicitReturn = false;
|
||||
}
|
||||
|
||||
if (isInJavaScriptFile(node) && node.jsDocComment) {
|
||||
if (node.flags & NodeFlags.JavaScriptFile && node.jsDocComment) {
|
||||
bind(node.jsDocComment);
|
||||
}
|
||||
|
||||
@ -518,11 +527,18 @@ namespace ts {
|
||||
node.flags = flags;
|
||||
|
||||
if (saveState) {
|
||||
hasExplicitReturn = savedHasExplicitReturn;
|
||||
currentFlow = savedCurrentFlow;
|
||||
currentBreakTarget = savedBreakTarget;
|
||||
currentContinueTarget = savedContinueTarget;
|
||||
activeLabels = savedActiveLabels;
|
||||
if (isIIFE) {
|
||||
addAntecedent(currentReturnTarget, currentFlow);
|
||||
currentFlow = finishFlowLabel(currentReturnTarget);
|
||||
}
|
||||
else {
|
||||
currentFlow = saveCurrentFlow;
|
||||
}
|
||||
currentBreakTarget = saveBreakTarget;
|
||||
currentContinueTarget = saveContinueTarget;
|
||||
currentReturnTarget = saveReturnTarget;
|
||||
activeLabels = saveActiveLabels;
|
||||
hasExplicitReturn = saveHasExplicitReturn;
|
||||
}
|
||||
|
||||
container = saveContainer;
|
||||
@ -854,6 +870,9 @@ namespace ts {
|
||||
bind(node.expression);
|
||||
if (node.kind === SyntaxKind.ReturnStatement) {
|
||||
hasExplicitReturn = true;
|
||||
if (currentReturnTarget) {
|
||||
addAntecedent(currentReturnTarget, currentFlow);
|
||||
}
|
||||
}
|
||||
currentFlow = unreachableFlow;
|
||||
}
|
||||
@ -1105,7 +1124,6 @@ namespace ts {
|
||||
}
|
||||
|
||||
function bindCallExpressionFlow(node: CallExpression) {
|
||||
forEachChild(node, bind);
|
||||
// If the target of the call expression is a function expression or arrow function we have
|
||||
// an immediately invoked function expression (IIFE). Initialize the flowNode property to
|
||||
// the current control flow (which includes evaluation of the IIFE arguments).
|
||||
@ -1114,7 +1132,12 @@ namespace ts {
|
||||
expr = (<ParenthesizedExpression>expr).expression;
|
||||
}
|
||||
if (expr.kind === SyntaxKind.FunctionExpression || expr.kind === SyntaxKind.ArrowFunction) {
|
||||
node.flowNode = currentFlow;
|
||||
forEach(node.typeArguments, bind);
|
||||
forEach(node.arguments, bind);
|
||||
bind(node.expression);
|
||||
}
|
||||
else {
|
||||
forEachChild(node, bind);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -7695,20 +7695,11 @@ namespace ts {
|
||||
getTypeAtFlowLoopLabel(<FlowLabel>flow);
|
||||
}
|
||||
else if (flow.flags & FlowFlags.Start) {
|
||||
// Check if we should continue with the control flow of the containing function.
|
||||
const container = (<FlowStart>flow).container;
|
||||
if (container) {
|
||||
// If container is an IIFE continue with the control flow associated with the
|
||||
// call expression node.
|
||||
const iife = getImmediatelyInvokedFunctionExpression(<FunctionExpression>container);
|
||||
if (iife && iife.flowNode) {
|
||||
flow = iife.flowNode;
|
||||
continue;
|
||||
}
|
||||
// Check if we should continue with the control flow of the containing function.
|
||||
if (includeOuterFunctions && container.flowNode) {
|
||||
flow = container.flowNode;
|
||||
continue;
|
||||
}
|
||||
if (container && includeOuterFunctions) {
|
||||
flow = container.flowNode;
|
||||
continue;
|
||||
}
|
||||
// At the top of the flow we have the initial type.
|
||||
type = initialType;
|
||||
@ -8652,23 +8643,25 @@ namespace ts {
|
||||
function getContextuallyTypedParameterType(parameter: ParameterDeclaration): Type {
|
||||
const func = parameter.parent;
|
||||
if (isContextSensitiveFunctionOrObjectLiteralMethod(func)) {
|
||||
const iife = getImmediatelyInvokedFunctionExpression(func);
|
||||
if (iife) {
|
||||
const indexOfParameter = indexOf(func.parameters, parameter);
|
||||
if (iife.arguments && indexOfParameter < iife.arguments.length) {
|
||||
if (parameter.dotDotDotToken) {
|
||||
const restTypes: Type[] = [];
|
||||
for (let i = indexOfParameter; i < iife.arguments.length; i++) {
|
||||
restTypes.push(getTypeOfExpression(iife.arguments[i]));
|
||||
if (func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction) {
|
||||
const iife = getImmediatelyInvokedFunctionExpression(func);
|
||||
if (iife) {
|
||||
const indexOfParameter = indexOf(func.parameters, parameter);
|
||||
if (iife.arguments && indexOfParameter < iife.arguments.length) {
|
||||
if (parameter.dotDotDotToken) {
|
||||
const restTypes: Type[] = [];
|
||||
for (let i = indexOfParameter; i < iife.arguments.length; i++) {
|
||||
restTypes.push(getTypeOfExpression(iife.arguments[i]));
|
||||
}
|
||||
return createArrayType(getUnionType(restTypes));
|
||||
}
|
||||
return createArrayType(getUnionType(restTypes));
|
||||
const links = getNodeLinks(iife);
|
||||
const cached = links.resolvedSignature;
|
||||
links.resolvedSignature = anySignature;
|
||||
const type = checkExpression(iife.arguments[indexOfParameter]);
|
||||
links.resolvedSignature = cached;
|
||||
return type;
|
||||
}
|
||||
const links = getNodeLinks(iife);
|
||||
const cached = links.resolvedSignature;
|
||||
links.resolvedSignature = anySignature;
|
||||
const type = checkExpression(iife.arguments[indexOfParameter]);
|
||||
links.resolvedSignature = cached;
|
||||
return type;
|
||||
}
|
||||
}
|
||||
const contextualSignature = getContextualSignature(func);
|
||||
@ -8691,20 +8684,6 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getImmediatelyInvokedFunctionExpression(func: FunctionExpression | MethodDeclaration) {
|
||||
if (isFunctionExpressionOrArrowFunction(func)) {
|
||||
let prev: Node = func;
|
||||
let parent: Node = func.parent;
|
||||
while (parent.kind === SyntaxKind.ParenthesizedExpression) {
|
||||
prev = parent;
|
||||
parent = parent.parent;
|
||||
}
|
||||
if (parent.kind === SyntaxKind.CallExpression && (parent as CallExpression).expression === prev) {
|
||||
return parent as CallExpression;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// In a variable, parameter or property declaration with a type annotation,
|
||||
// the contextual type of an initializer expression is the type of the variable, parameter or property.
|
||||
// Otherwise, in a parameter declaration of a contextually typed function expression,
|
||||
|
||||
@ -416,6 +416,7 @@ namespace ts {
|
||||
|
||||
ReachabilityCheckFlags = HasImplicitReturn | HasExplicitReturn,
|
||||
EmitHelperFlags = HasClassExtends | HasDecorators | HasParamDecorators | HasAsyncFunctions,
|
||||
ReachabilityAndEmitFlags = ReachabilityCheckFlags | EmitHelperFlags,
|
||||
|
||||
// Parsing context flags
|
||||
ContextFlags = DisallowInContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile,
|
||||
|
||||
@ -987,6 +987,20 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function getImmediatelyInvokedFunctionExpression(func: Node): CallExpression {
|
||||
if (func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction) {
|
||||
let prev = func;
|
||||
let parent = func.parent;
|
||||
while (parent.kind === SyntaxKind.ParenthesizedExpression) {
|
||||
prev = parent;
|
||||
parent = parent.parent;
|
||||
}
|
||||
if (parent.kind === SyntaxKind.CallExpression && (parent as CallExpression).expression === prev) {
|
||||
return parent as CallExpression;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a node is a property or element access expression for super.
|
||||
*/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user