import { assert , fail } from '../common/debug' ; import { ArgumentCategory , CallNode , CaseNode , ExpressionNode , ImportFromNode , IndexNode , MatchNode , MemberAccessNode , NameNode , NumberNode , ParseNodeType , StringNode , SuiteNode , } from '../parser/parseNodes' ; export enum FlowFlags { Unreachable = 1 << 0 , Start = 1 << 1 , BranchLabel = 1 << 2 , LoopLabel = 1 << 3 , Assignment = 1 << 4 , Unbind = 1 << 5 , WildcardImport = 1 << 6 , TrueCondition = 1 << 7 , FalseCondition = 1 << 9 , Call = 1 << 10 , PreFinallyGate = 1 << 11 , PostFinally = 1 << 12 , AssignmentAlias = 1 << 13 , VariableAnnotation = 1 << 14 , PostContextManager = 1 << 15 , TrueNeverCondition = 1 << 16 , FalseNeverCondition = 1 << 17 , NarrowForPattern = 1 << 18 , ExhaustedMatch = 1 << 19 } let _nextFlowNodeId = 1 ; export type CodeFlowReferenceExpressionNode = NameNode | MemberAccessNode | IndexNode ; export function getUniqueFlowNodeId ( ) { return _nextFlowNodeId ++ ; } export interface FlowNode { flags : FlowFlags ; id : number ; } export interface FlowLabel extends FlowNode { antecedents : FlowNode [ ] ; affectedExpressions : Set < string > | undefined ; } export interface FlowBranchLabel extends FlowLabel { preBranchAntecedent : FlowNode | undefined ; } export interface FlowAssignment extends FlowNode { node : CodeFlowReferenceExpressionNode ; antecedent : FlowNode ; targetSymbolId : number ; } export interface FlowAssignmentAlias extends FlowNode { antecedent : FlowNode ; targetSymbolId : number ; aliasSymbolId : number ; } export interface FlowVariableAnnotation extends FlowNode { antecedent : FlowNode ; } export interface FlowWildcardImport extends FlowNode { node : ImportFromNode ; names : string [ ] ; antecedent : FlowNode ; } export interface FlowCondition extends FlowNode { expression : ExpressionNode ; reference ? : NameNode | undefined ; antecedent : FlowNode ; } export interface FlowNarrowForPattern extends FlowNode { subjectExpression : ExpressionNode ; statement : CaseNode | MatchNode ; antecedent : FlowNode ; } export interface FlowExhaustedMatch extends FlowNode { node : MatchNode ; antecedent : FlowNode ; } export interface FlowCall extends FlowNode { node : CallNode ; antecedent : FlowNode ; typeGuardArgExpression ? : ExpressionNode ; } export interface FlowPreFinallyGate extends FlowNode { antecedent : FlowNode ; isGateClosed : boolean ; } export interface FlowPostFinally extends FlowNode { antecedent : FlowNode ; finallyNode : SuiteNode ; preFinallyGate : FlowPreFinallyGate ; } export interface FlowPostContextManagerLabel extends FlowLabel { expressions : ExpressionNode [ ] ; isAsync : boolean ; } export function isCodeFlowSupportedForReference ( reference ) { if ( reference . nodeType === ParseNodeType . Name ) { return true ; } if ( reference . nodeType === ParseNodeType . MemberAccess ) { return isCodeFlowSupportedForReference ( reference . leftExpression ) ; } if ( reference . nodeType === ParseNodeType . Index ) { if ( reference . items . length !== 1 || reference . trailingComma || reference . items [ 0 ] . name !== undefined || reference . items [ 0 ] . argumentCategory !== ArgumentCategory . Simple ) { return false ; } const subscriptNode = reference . items [ 0 ] . valueExpression ; const isIntegerIndex = subscriptNode . nodeType === ParseNodeType . Number && ! subscriptNode . isImaginary && subscriptNode . isInteger ; const isStringIndex = subscriptNode . nodeType === ParseNodeType . StringList && subscriptNode . strings . length === 1 && subscriptNode . strings [ 0 ] . nodeType === ParseNodeType . String ; if ( ! isIntegerIndex && ! isStringIndex ) { return false ; } return isCodeFlowSupportedForReference ( reference . baseExpression ) ; } return false ; } export function createKeyForReference ( reference ) { let key ; if ( reference . nodeType === ParseNodeType . Name ) { key = reference . value ; } else if ( reference . nodeType === ParseNodeType . MemberAccess ) { const leftKey = createKeyForReference ( reference . leftExpression as CodeFlowReferenceExpressionNode ) ; key = `${ leftKey }.${ reference . memberName . value }` ; } else if ( reference . nodeType === ParseNodeType . Index ) { const leftKey = createKeyForReference ( reference . baseExpression as CodeFlowReferenceExpressionNode ) ; assert ( reference . items . length === 1 ) ; if ( reference . items [ 0 ] . valueExpression . nodeType === ParseNodeType . Number ) { key = `${ leftKey }[${ ( reference . items [ 0 ] . valueExpression as NumberNode ) . value . toString ( ) }]` ; } else if ( reference . items [ 0 ] . valueExpression . nodeType === ParseNodeType . StringList ) { const valExpr = reference . items [ 0 ] . valueExpression ; assert ( valExpr . strings . length === 1 && valExpr . strings [ 0 ] . nodeType === ParseNodeType . String ) ; key = `${ leftKey }["${ ( valExpr . strings [ 0 ] as StringNode ) . value }"]` ; } else { fail ( 'createKeyForReference received unexpected index type' ) ; } } else { fail ( 'createKeyForReference received unexpected expression type' ) ; } return key ; } export function createKeysForReferenceSubexpressions ( reference ) { if ( reference . nodeType === ParseNodeType . Name ) { return [ createKeyForReference ( reference ) ] ; } if ( reference . nodeType === ParseNodeType . MemberAccess ) { return [ ... createKeysForReferenceSubexpressions ( reference . leftExpression as CodeFlowReferenceExpressionNode ) , createKeyForReference ( reference ) , ] ; } if ( reference . nodeType === ParseNodeType . Index ) { return [ ... createKeysForReferenceSubexpressions ( reference . baseExpression as CodeFlowReferenceExpressionNode ) , createKeyForReference ( reference ) , ] ; } fail ( 'createKeyForReference received unexpected expression type' ) ; }