mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-19 17:18:50 -06:00
Reflect effects of assertion calls in control flow analysis
This commit is contained in:
parent
4cc6618fc2
commit
e89acb6358
@ -706,6 +706,9 @@ namespace ts {
|
||||
case SyntaxKind.CaseClause:
|
||||
bindCaseClause(<CaseClause>node);
|
||||
break;
|
||||
case SyntaxKind.ExpressionStatement:
|
||||
bindExpressionStatement(<ExpressionStatement>node);
|
||||
break;
|
||||
case SyntaxKind.LabeledStatement:
|
||||
bindLabeledStatement(<LabeledStatement>node);
|
||||
break;
|
||||
@ -896,6 +899,11 @@ namespace ts {
|
||||
return flowNodeCreated({ flags: FlowFlags.Assignment, antecedent, node });
|
||||
}
|
||||
|
||||
function createFlowCall(antecedent: FlowNode, node: CallExpression): FlowNode {
|
||||
setFlowNodeReferenced(antecedent);
|
||||
return flowNodeCreated({ flags: FlowFlags.Call, antecedent, node });
|
||||
}
|
||||
|
||||
function createFlowArrayMutation(antecedent: FlowNode, node: CallExpression | BinaryExpression): FlowNode {
|
||||
setFlowNodeReferenced(antecedent);
|
||||
const res: FlowArrayMutation = flowNodeCreated({ flags: FlowFlags.ArrayMutation, antecedent, node });
|
||||
@ -1276,6 +1284,20 @@ namespace ts {
|
||||
activeLabels!.pop();
|
||||
}
|
||||
|
||||
function isDottedName(node: Expression) {
|
||||
return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PropertyAccessExpression && isQualifiedName((<PropertyAccessExpression>node).expression);
|
||||
}
|
||||
|
||||
function bindExpressionStatement(node: ExpressionStatement): void {
|
||||
bind(node.expression);
|
||||
if (node.expression.kind === SyntaxKind.CallExpression) {
|
||||
const call = <CallExpression>node.expression;
|
||||
if (isDottedName(call.expression) && call.arguments.length >= 1) {
|
||||
currentFlow = createFlowCall(currentFlow, call);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function bindLabeledStatement(node: LabeledStatement): void {
|
||||
const preStatementLabel = createLoopLabel();
|
||||
const postStatementLabel = createBranchLabel();
|
||||
|
||||
@ -16864,6 +16864,44 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
function getTypeOfDottedName(node: Expression) {
|
||||
if (node.kind === SyntaxKind.Identifier) {
|
||||
const symbol = getResolvedSymbol(<Identifier>node);
|
||||
const nonAliasSymbol = symbol.flags & SymbolFlags.Alias ? resolveAlias(symbol) : symbol;
|
||||
return nonAliasSymbol.flags & (SymbolFlags.Function | SymbolFlags.Class | SymbolFlags.ValueModule) ? getTypeOfSymbol(nonAliasSymbol) : undefined;
|
||||
}
|
||||
if (node.kind === SyntaxKind.PropertyAccessExpression) {
|
||||
const type = getTypeOfDottedName((<PropertyAccessExpression>node).expression);
|
||||
if (type) {
|
||||
const prop = getPropertyOfType(type, (<PropertyAccessExpression>node).name.escapedText);
|
||||
return prop && prop.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.ValueModule) ? getTypeOfSymbol(prop) : undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getIsAssertCall(node: CallExpression) {
|
||||
const type = getTypeOfDottedName(node.expression);
|
||||
if (type) {
|
||||
const signature = getSingleCallSignature(type);
|
||||
if (signature && signature.declaration) {
|
||||
const typeNode = getEffectiveReturnTypeNode(signature.declaration);
|
||||
if (typeNode && typeNode.kind === SyntaxKind.UnionType) {
|
||||
const types = (<UnionTypeNode>typeNode).types;
|
||||
return types.length === 2 && types[0].kind === SyntaxKind.VoidKeyword && types[1].kind === SyntaxKind.NeverKeyword;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isAssertCall(node: CallExpression) {
|
||||
const links = getNodeLinks(node);
|
||||
if (links.isAssertCall === undefined) {
|
||||
links.isAssertCall = getIsAssertCall(node);
|
||||
}
|
||||
return links.isAssertCall;
|
||||
}
|
||||
|
||||
function reportFlowControlError(node: Node) {
|
||||
const block = <Block | ModuleBlock | SourceFile>findAncestor(node, isFunctionOrModuleBlock);
|
||||
const sourceFile = getSourceFileOfNode(node);
|
||||
@ -16962,6 +17000,13 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (flags & FlowFlags.Call) {
|
||||
type = getTypeAtFlowCall(<FlowCall>flow);
|
||||
if (!type) {
|
||||
flow = (<FlowCall>flow).antecedent;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (flags & FlowFlags.Condition) {
|
||||
type = getTypeAtFlowCondition(<FlowCondition>flow);
|
||||
}
|
||||
@ -17057,6 +17102,32 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function narrowTypeByAssertion(type: Type, expr: Expression): Type {
|
||||
const node = skipParentheses(expr);
|
||||
if (node.kind === SyntaxKind.BinaryExpression) {
|
||||
if ((<BinaryExpression>node).operatorToken.kind === SyntaxKind.AmpersandAmpersandToken) {
|
||||
return narrowTypeByAssertion(narrowTypeByAssertion(type, (<BinaryExpression>node).left), (<BinaryExpression>node).right);
|
||||
}
|
||||
if ((<BinaryExpression>node).operatorToken.kind === SyntaxKind.BarBarToken) {
|
||||
return getUnionType([narrowTypeByAssertion(type, (<BinaryExpression>node).left), narrowTypeByAssertion(type, (<BinaryExpression>node).right)]);
|
||||
}
|
||||
}
|
||||
return narrowType(type, node, /*assumeTrue*/ true);
|
||||
}
|
||||
|
||||
function getTypeAtFlowCall(flow: FlowCall): FlowType | undefined {
|
||||
if (isAssertCall(flow.node)) {
|
||||
const flowType = getTypeAtFlowNode(flow.antecedent);
|
||||
const type = getTypeFromFlowType(flowType);
|
||||
const narrowedType = narrowTypeByAssertion(type, flow.node.arguments[0]);
|
||||
if (narrowedType === type) {
|
||||
return flowType;
|
||||
}
|
||||
return createFlowType(narrowedType, isIncomplete(flowType));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getTypeAtFlowArrayMutation(flow: FlowArrayMutation): FlowType | undefined {
|
||||
if (declaredType === autoType || declaredType === autoArrayType) {
|
||||
const node = flow.node;
|
||||
|
||||
@ -2550,12 +2550,13 @@ namespace ts {
|
||||
FalseCondition = 1 << 6, // Condition known to be false
|
||||
SwitchClause = 1 << 7, // Switch statement clause
|
||||
ArrayMutation = 1 << 8, // Potential array mutation
|
||||
Referenced = 1 << 9, // Referenced as antecedent once
|
||||
Shared = 1 << 10, // Referenced as antecedent more than once
|
||||
PreFinally = 1 << 11, // Injected edge that links pre-finally label and pre-try flow
|
||||
AfterFinally = 1 << 12, // Injected edge that links post-finally flow with the rest of the graph
|
||||
Call = 1 << 9, // Potential assertion call
|
||||
Referenced = 1 << 10, // Referenced as antecedent once
|
||||
Shared = 1 << 11, // Referenced as antecedent more than once
|
||||
PreFinally = 1 << 12, // Injected edge that links pre-finally label and pre-try flow
|
||||
AfterFinally = 1 << 13, // Injected edge that links post-finally flow with the rest of the graph
|
||||
/** @internal */
|
||||
Cached = 1 << 13, // Indicates that at least one cross-call cache entry exists for this node, even if not a loop participant
|
||||
Cached = 1 << 14, // Indicates that at least one cross-call cache entry exists for this node, even if not a loop participant
|
||||
Label = BranchLabel | LoopLabel,
|
||||
Condition = TrueCondition | FalseCondition
|
||||
}
|
||||
@ -2574,7 +2575,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
export type FlowNode =
|
||||
| AfterFinallyFlow | PreFinallyFlow | FlowStart | FlowLabel | FlowAssignment | FlowCondition | FlowSwitchClause | FlowArrayMutation;
|
||||
| AfterFinallyFlow | PreFinallyFlow | FlowStart | FlowLabel | FlowAssignment | FlowCall | FlowCondition | FlowSwitchClause | FlowArrayMutation;
|
||||
export interface FlowNodeBase {
|
||||
flags: FlowFlags;
|
||||
id?: number; // Node id used by flow type cache in checker
|
||||
@ -2599,6 +2600,11 @@ namespace ts {
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
|
||||
export interface FlowCall extends FlowNodeBase {
|
||||
node: CallExpression;
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
|
||||
// FlowCondition represents a condition that is known to be true or false at the
|
||||
// node's location in the control flow.
|
||||
export interface FlowCondition extends FlowNodeBase {
|
||||
@ -3902,6 +3908,7 @@ namespace ts {
|
||||
resolvedSymbol?: Symbol; // Cached name resolution result
|
||||
resolvedIndexInfo?: IndexInfo; // Cached indexing info resolution result
|
||||
maybeTypePredicate?: boolean; // Cached check whether call expression might reference a type predicate
|
||||
isAssertCall?: boolean;
|
||||
enumMemberValue?: string | number; // Constant value of enum member
|
||||
isVisible?: boolean; // Is this node visible
|
||||
containsArgumentsReference?: boolean; // Whether a function-like declaration contains an 'arguments' reference
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user