import * as AnalyzerNodeInfo from '../analyzer/analyzerNodeInfo' ; import { assertNever , fail } from '../common/debug' ; import { convertPositionToOffset , convertTextRangeToRange } from '../common/positionUtils' ; import { Position , Range } from '../common/textRange' ; import { TextRange } from '../common/textRange' ; import { TextRangeCollection } from '../common/textRangeCollection' ; import { ArgumentCategory , ArgumentNode , AssignmentExpressionNode , CallNode , ClassNode , EvaluationScopeNode , ExecutionScopeNode , ExpressionNode , FunctionNode , IndexNode , isExpressionNode , LambdaNode , MemberAccessNode , ModuleNode , NameNode , ParameterCategory , ParameterNode , ParseNode , ParseNodeType , StatementListNode , StatementNode , StringListNode , StringNode , SuiteNode , TypeAnnotationNode , } from '../parser/parseNodes' ; import { TokenizerOutput } from '../parser/tokenizer' ; import { KeywordType , OperatorType , StringToken , StringTokenFlags , Token , TokenType } from '../parser/tokenizerTypes' ; import { getScope } from './analyzerNodeInfo' ; import { ParseTreeWalker } from './parseTreeWalker' ; export const enum PrintExpressionFlags { None = 0 , ForwardDeclarations = 1 << 0 } export function getNodeDepth ( node ) { let depth = 0 ; let curNode = node ; while ( curNode ) { depth ++ ; curNode = curNode . parent ; } return depth ; } export function findNodeByPosition ( node , position , lines ) { const offset = convertPositionToOffset ( position , lines ) ; if ( offset === undefined ) { return undefined ; } return findNodeByOffset ( node , offset ) ; } export function findNodeByOffset ( node , offset ) { if ( offset < node . start || offset > TextRange . getEnd ( node ) ) { return undefined ; } const parseTreeWalker = new ParseTreeWalker ( ) ; const children = parseTreeWalker . visitNode ( node ) ; for ( const child of children ) { if ( child ) { const containingChild = findNodeByOffset ( child , offset ) ; if ( containingChild ) { return containingChild ; } } } return node ; } export function getClassFullName ( classNode , moduleName , className ) { const nameParts = [ className ] ; let curNode = classNode ; while ( curNode ) { curNode = getEnclosingClass ( curNode ) ; if ( curNode ) { nameParts . push ( curNode . name . value ) ; } } nameParts . push ( moduleName ) ; return nameParts . reverse ( ) . join ( '.' ) ; } export function getTypeSourceId ( node ) { return node . start ; } export function printArgument ( node , flags ) { let argStr = '' ; if ( node . argumentCategory === ArgumentCategory . UnpackedList ) { argStr = '*' ; } else if ( node . argumentCategory === ArgumentCategory . UnpackedDictionary ) { argStr = '**' ; } if ( node . name ) { argStr += node . name . value + '=' ; } argStr += printExpression ( node . valueExpression , flags ) ; return argStr ; } export function printExpression ( node , flags = PrintExpressionFlags . None ) { switch ( node . nodeType ) { case ParseNodeType . Name : { return node . value ; } case ParseNodeType . MemberAccess : { return printExpression ( node . leftExpression , flags ) + '.' + node . memberName . value ; } case ParseNodeType . Call : { return ( printExpression ( node . leftExpression , flags ) + '(' + node . arguments . map ( ( arg ) => printArgument ( arg , flags ) ) . join ( ', ' ) + ')' ) ; } case ParseNodeType . Index : { return ( printExpression ( node . baseExpression , flags ) + '[' + node . items . map ( ( item ) => printArgument ( item , flags ) ) . join ( ', ' ) + ']' + ( node . trailingComma ? ',' : '' ) ) ; } case ParseNodeType . UnaryOperation : { return printOperator ( node . operator ) + printExpression ( node . expression , flags ) ; } case ParseNodeType . BinaryOperation : { const exprStr = printExpression ( node . leftExpression , flags ) + ' ' + printOperator ( node . operator ) + ' ' + printExpression ( node . rightExpression , flags ) ; return node . parenthesized ? `(${ exprStr })` : exprStr ; } case ParseNodeType . Number : { let value = node . value . toString ( ) ; if ( node . isImaginary ) { value += 'j' ; } return value ; } case ParseNodeType . StringList : { if ( flags & PrintExpressionFlags . ForwardDeclarations && node . typeAnnotation ) { return printExpression ( node . typeAnnotation , flags ) ; } else { return node . strings . map ( ( str ) => { return printExpression ( str , flags ) ; } ) . join ( ' ' ) ; } } case ParseNodeType . String : { let exprString = '' ; if ( node . token . flags & StringTokenFlags . Raw ) { exprString += 'r' ; } if ( node . token . flags & StringTokenFlags . Unicode ) { exprString += 'u' ; } if ( node . token . flags & StringTokenFlags . Bytes ) { exprString += 'b' ; } if ( node . token . flags & StringTokenFlags . Format ) { exprString += 'f' ; } if ( node . token . flags & StringTokenFlags . Triplicate ) { if ( node . token . flags & StringTokenFlags . SingleQuote ) { exprString += `'''${ node . token . escapedValue }'''` ; } else { exprString += `"""${ node . token . escapedValue }"""` ; } } else { if ( node . token . flags & StringTokenFlags . SingleQuote ) { exprString += `'${ node . token . escapedValue }'` ; } else { exprString += `"${ node . token . escapedValue }"` ; } } return exprString ; } case ParseNodeType . Assignment : { return printExpression ( node . leftExpression , flags ) + ' = ' + printExpression ( node . rightExpression , flags ) ; } case ParseNodeType . AssignmentExpression : { return printExpression ( node . name , flags ) + ' := ' + printExpression ( node . rightExpression , flags ) ; } case ParseNodeType . TypeAnnotation : { return printExpression ( node . valueExpression , flags ) + ': ' + printExpression ( node . typeAnnotation , flags ) ; } case ParseNodeType . AugmentedAssignment : { return ( printExpression ( node . leftExpression , flags ) + ' ' + printOperator ( node . operator ) + ' ' + printExpression ( node . rightExpression , flags ) ) ; } case ParseNodeType . Await : { return 'await ' + printExpression ( node . expression , flags ) ; } case ParseNodeType . Ternary : { return ( printExpression ( node . ifExpression , flags ) + ' if ' + printExpression ( node . testExpression , flags ) + ' else ' + printExpression ( node . elseExpression , flags ) ) ; } case ParseNodeType . List : { const expressions = node . entries . map ( ( expr ) => { return printExpression ( expr , flags ) ; } ) ; return `[${ expressions . join ( ', ' ) }]` ; } case ParseNodeType . Unpack : { return '*' + printExpression ( node . expression , flags ) ; } case ParseNodeType . Tuple : { const expressions = node . expressions . map ( ( expr ) => { return printExpression ( expr , flags ) ; } ) ; if ( expressions . length === 1 ) { return `(${ expressions [ 0 ] }, )` ; } return `(${ expressions . join ( ', ' ) })` ; } case ParseNodeType . Yield : { if ( node . expression ) { return 'yield ' + printExpression ( node . expression , flags ) ; } else { return 'yield' ; } } case ParseNodeType . YieldFrom : { return 'yield from ' + printExpression ( node . expression , flags ) ; } case ParseNodeType . Ellipsis : { return '...' ; } case ParseNodeType . ListComprehension : { let listStr = '<ListExpression>' ; if ( isExpressionNode ( node . expression ) ) { listStr = printExpression ( node . expression as ExpressionNode , flags ) ; } else if ( node . expression . nodeType === ParseNodeType . DictionaryKeyEntry ) { const keyStr = printExpression ( node . expression . keyExpression , flags ) ; const valueStr = printExpression ( node . expression . valueExpression , flags ) ; listStr = `${ keyStr }: ${ valueStr }` ; } return ( listStr + ' ' + node . comprehensions . map ( ( expr ) => { if ( expr . nodeType === ParseNodeType . ListComprehensionFor ) { return ( `${ expr . isAsync ? 'async ' : '' }for ` + printExpression ( expr . targetExpression , flags ) + ` in ${ printExpression ( expr . iterableExpression , flags ) }` ) ; } else { return `if ${ printExpression ( expr . testExpression , flags ) }` ; } } ) . join ( ' ' ) ) ; } case ParseNodeType . Slice : { let result = '' ; if ( node . startValue ) { result += printExpression ( node . startValue , flags ) ; } if ( node . endValue ) { result += ': ' + printExpression ( node . endValue , flags ) ; } if ( node . stepValue ) { result += ': ' + printExpression ( node . stepValue , flags ) ; } return result ; } case ParseNodeType . Lambda : { return ( 'lambda ' + node . parameters . map ( ( param ) => { let paramStr = '' ; if ( param . category === ParameterCategory . VarArgList ) { paramStr += '*' ; } else if ( param . category === ParameterCategory . VarArgDictionary ) { paramStr += '**' ; } if ( param . name ) { paramStr += param . name . value ; } if ( param . defaultValue ) { paramStr += ' = ' + printExpression ( param . defaultValue , flags ) ; } return paramStr ; } ) . join ( ', ' ) + ': ' + printExpression ( node . expression , flags ) ) ; } case ParseNodeType . Constant : { if ( node . constType === KeywordType . True ) { return 'True' ; } else if ( node . constType === KeywordType . False ) { return 'False' ; } else if ( node . constType === KeywordType . Debug ) { return '__debug__' ; } else if ( node . constType === KeywordType . None ) { return 'None' ; } break ; } case ParseNodeType . Dictionary : { const dictContents = `${ node . entries . map ( ( entry ) => { if ( entry . nodeType === ParseNodeType . DictionaryKeyEntry ) { return ( `${ printExpression ( entry . keyExpression , flags ) }: ` + `${ printExpression ( entry . valueExpression , flags ) }` ) ; } else { return printExpression ( entry , flags ) ; } } ) }` ; if ( dictContents ) { return `{ ${ dictContents } }` ; } return '{}' ; } case ParseNodeType . DictionaryExpandEntry : { return `**${ printExpression ( node . expandExpression , flags ) }` ; } case ParseNodeType . Set : { return node . entries . map ( ( entry ) => printExpression ( entry , flags ) ) . join ( ', ' ) ; } case ParseNodeType . ArrowCallable : { const paramsText = node . parameters . map ( ( param ) => { let prefix = '' ; if ( param . category === ParameterCategory . VarArgList ) { prefix = '*' ; } else if ( param . category === ParameterCategory . VarArgDictionary ) { prefix = '**' ; } const typeText = printExpression ( param . typeAnnotation ) ; return `${ prefix }${ typeText }` ; } ) ; const optionalAsync = node . isAsync ? 'async ' : '' ; return `${ optionalAsync }(${ paramsText . join ( ', ' ) }) -> ${ printExpression ( node . returnTypeAnnotation ) }` ; } } return '<Expression>' ; } export function printOperator ( operator ) { const operatorMap = { [ OperatorType . Add ] : '+' , [ OperatorType . AddEqual ] : '+=' , [ OperatorType . Assign ] : '=' , [ OperatorType . BitwiseAnd ] : '&' , [ OperatorType . BitwiseAndEqual ] : '&=' , [ OperatorType . BitwiseInvert ] : '~' , [ OperatorType . BitwiseOr ] : '|' , [ OperatorType . BitwiseOrEqual ] : '|=' , [ OperatorType . BitwiseXor ] : '^' , [ OperatorType . BitwiseXorEqual ] : '^=' , [ OperatorType . Divide ] : '/' , [ OperatorType . DivideEqual ] : '/=' , [ OperatorType . Equals ] : '==' , [ OperatorType . FloorDivide ] : '//' , [ OperatorType . FloorDivideEqual ] : '//=' , [ OperatorType . GreaterThan ] : '>' , [ OperatorType . GreaterThanOrEqual ] : '>=' , [ OperatorType . LeftShift ] : '<<' , [ OperatorType . LeftShiftEqual ] : '<<=' , [ OperatorType . LessOrGreaterThan ] : '<>' , [ OperatorType . LessThan ] : '<' , [ OperatorType . LessThanOrEqual ] : '<=' , [ OperatorType . MatrixMultiply ] : '@' , [ OperatorType . MatrixMultiplyEqual ] : '@=' , [ OperatorType . Mod ] : '%' , [ OperatorType . ModEqual ] : '%=' , [ OperatorType . Multiply ] : '*' , [ OperatorType . MultiplyEqual ] : '*=' , [ OperatorType . NotEquals ] : '!=' , [ OperatorType . Power ] : '**' , [ OperatorType . PowerEqual ] : '**=' , [ OperatorType . RightShift ] : '>>' , [ OperatorType . RightShiftEqual ] : '>>=' , [ OperatorType . Subtract ] : '-' , [ OperatorType . SubtractEqual ] : '-=' , [ OperatorType . And ] : 'and' , [ OperatorType . Or ] : 'or' , [ OperatorType . Not ] : 'not' , [ OperatorType . Is ] : 'is' , [ OperatorType . IsNot ] : 'is not' , [ OperatorType . In ] : 'in' , [ OperatorType . NotIn ] : 'not in' , } ; if ( operatorMap [ operator ] ) { return operatorMap [ operator ] ; } return 'unknown' ; } export function getEnclosingSuite ( node ) { let curNode = node . parent ; while ( curNode ) { if ( curNode . nodeType === ParseNodeType . Suite ) { return curNode ; } curNode = curNode . parent ; } return undefined ; } export function getEnclosingClass ( node , stopAtFunction = false ) { let curNode = node . parent ; while ( curNode ) { if ( curNode . nodeType === ParseNodeType . Class ) { return curNode ; } if ( curNode . nodeType === ParseNodeType . Module ) { return undefined ; } if ( curNode . nodeType === ParseNodeType . Function ) { if ( stopAtFunction ) { return undefined ; } } curNode = curNode . parent ; } return undefined ; } export function getEnclosingModule ( node ) { let curNode = node . parent ; while ( curNode ) { if ( curNode . nodeType === ParseNodeType . Module ) { return curNode ; } curNode = curNode . parent ; } fail ( 'Module node not found' ) ; return undefined ! ; } export function getEnclosingClassOrModule ( node , stopAtFunction = false ) { let curNode = node . parent ; while ( curNode ) { if ( curNode . nodeType === ParseNodeType . Class ) { return curNode ; } if ( curNode . nodeType === ParseNodeType . Module ) { return curNode ; } if ( curNode . nodeType === ParseNodeType . Function ) { if ( stopAtFunction ) { return undefined ; } } curNode = curNode . parent ; } return undefined ; } export function getEnclosingFunction ( node ) { let curNode = node . parent ; while ( curNode ) { if ( curNode . nodeType === ParseNodeType . Function ) { return curNode ; } if ( curNode . nodeType === ParseNodeType . Class ) { return undefined ; } curNode = curNode . parent ; } return undefined ; } export function getEnclosingLambda ( node ) { let curNode = node . parent ; while ( curNode ) { if ( curNode . nodeType === ParseNodeType . Lambda ) { return curNode ; } if ( curNode . nodeType === ParseNodeType . Suite ) { return undefined ; } curNode = curNode . parent ; } return undefined ; } export function getEnclosingClassOrFunction ( node ) { let curNode = node . parent ; while ( curNode ) { if ( curNode . nodeType === ParseNodeType . Function ) { return curNode ; } if ( curNode . nodeType === ParseNodeType . Class ) { return curNode ; } curNode = curNode . parent ; } return undefined ; } export function getEnclosingSuiteOrModule ( node , stopAtFunction = false , stopAtLambda = true ) { let curNode = node . parent ; while ( curNode ) { if ( curNode . nodeType === ParseNodeType . Suite ) { return curNode ; } if ( curNode . nodeType === ParseNodeType . Module ) { return curNode ; } if ( curNode . nodeType === ParseNodeType . Lambda ) { if ( stopAtLambda ) { return undefined ; } } if ( curNode . nodeType === ParseNodeType . Function ) { if ( stopAtFunction ) { return undefined ; } } curNode = curNode . parent ; } return undefined ; } export function getEvaluationNodeForAssignmentExpression ( node ) { let sawListComprehension = false ; let curNode = getEvaluationScopeNode ( node ) ; while ( curNode !== undefined ) { switch ( curNode . nodeType ) { case ParseNodeType . Function : case ParseNodeType . Lambda : case ParseNodeType . Module : return curNode ; case ParseNodeType . Class : return sawListComprehension ? undefined : curNode ; case ParseNodeType . ListComprehension : sawListComprehension = true ; curNode = getEvaluationScopeNode ( curNode . parent ! ) ; break ; default : return undefined ; } } return undefined ; } export function getEvaluationScopeNode ( node ) { let prevNode ; let curNode = node ; let isParamNameNode = false ; while ( curNode ) { if ( curNode . nodeType === ParseNodeType . Parameter && prevNode === curNode . name ) { isParamNameNode = true ; } switch ( curNode . nodeType ) { case ParseNodeType . Function : { if ( curNode . parameters . some ( ( param ) => param === prevNode ) ) { if ( isParamNameNode ) { if ( getScope ( curNode ) !== undefined ) { return curNode ; } } } else if ( prevNode === curNode . suite ) { if ( getScope ( curNode ) !== undefined ) { return curNode ; } } break ; } case ParseNodeType . Lambda : { if ( curNode . parameters . some ( ( param ) => param === prevNode ) ) { if ( isParamNameNode ) { if ( getScope ( curNode ) !== undefined ) { return curNode ; } } } else if ( ! prevNode || prevNode === curNode . expression ) { if ( getScope ( curNode ) !== undefined ) { return curNode ; } } break ; } case ParseNodeType . Class : { if ( prevNode === curNode . suite ) { if ( getScope ( curNode ) !== undefined ) { return curNode ; } } break ; } case ParseNodeType . ListComprehension : case ParseNodeType . Module : { if ( getScope ( curNode ) !== undefined ) { return curNode ; } } } prevNode = curNode ; curNode = curNode . parent ; } fail ( 'Did not find evaluation scope' ) ; return undefined ! ; } export function getTypeVarScopeNode ( node , allowInFunctionSignature = false ) { let prevNode ; let curNode = node ; while ( curNode ) { switch ( curNode . nodeType ) { case ParseNodeType . Function : { if ( prevNode === curNode . suite || allowInFunctionSignature ) { if ( ! curNode . decorators . some ( ( decorator ) => decorator === prevNode ) ) { return curNode ; } } break ; } case ParseNodeType . Class : { if ( prevNode === curNode . suite ) { if ( ! curNode . decorators . some ( ( decorator ) => decorator === prevNode ) ) { return curNode ; } } break ; } } prevNode = curNode ; curNode = curNode . parent ; } return undefined ! ; } export function getExecutionScopeNode ( node ) { let evaluationScope = getEvaluationScopeNode ( node ) ; while ( evaluationScope . nodeType === ParseNodeType . Class || evaluationScope . nodeType === ParseNodeType . ListComprehension ) { evaluationScope = getEvaluationScopeNode ( evaluationScope . parent ! ) ; } return evaluationScope ; } export function getTypeAnnotationNode ( node ) { let prevNode = node ; let curNode = node . parent ; while ( curNode ) { if ( curNode . nodeType === ParseNodeType . TypeAnnotation ) { if ( curNode . typeAnnotation === prevNode ) { return curNode ; } break ; } prevNode = curNode ; curNode = curNode . parent ; } return undefined ; } export function isFinalAllowedForAssignmentTarget ( targetNode ) { if ( targetNode . nodeType === ParseNodeType . Name ) { return true ; } if ( targetNode . nodeType === ParseNodeType . MemberAccess ) { if ( targetNode . leftExpression . nodeType !== ParseNodeType . Name ) { return false ; } const classNode = getEnclosingClass ( targetNode ) ; if ( ! classNode ) { return false ; } const methodNode = getEnclosingFunction ( targetNode ) ; if ( ! methodNode ) { return false ; } if ( methodNode . name . value !== '__init__' ) { return false ; } return true ; } return false ; } export function isClassVarAllowedForAssignmentTarget ( targetNode ) { const classNode = getEnclosingClass ( targetNode , true ) ; if ( ! classNode ) { return false ; } return true ; } export function isNodeContainedWithin ( node , potentialContainer ) { let curNode = node ; while ( curNode ) { if ( curNode === potentialContainer ) { return true ; } curNode = curNode . parent ; } return false ; } export function isNodeContainedWithinNodeType ( node , containerType ) { let curNode = node ; while ( curNode ) { if ( curNode . nodeType === containerType ) { return true ; } curNode = curNode . parent ; } return false ; } export function isSuiteEmpty ( node ) { let sawEllipsis = false ; for ( const statement of node . statements ) { if ( statement . nodeType === ParseNodeType . StatementList ) { for ( const substatement of statement . statements ) { if ( substatement . nodeType === ParseNodeType . Ellipsis ) { sawEllipsis = true ; } else if ( substatement . nodeType === ParseNodeType . StringList ) { } else { return false ; } } } else { return false ; } } return sawEllipsis ; } export function isMatchingExpression ( reference , expression ) { if ( reference . nodeType === ParseNodeType . Name ) { if ( expression . nodeType === ParseNodeType . Name ) { return reference . value === expression . value ; } else if ( expression . nodeType === ParseNodeType . AssignmentExpression ) { return reference . value === expression . name . value ; } return false ; } else if ( reference . nodeType === ParseNodeType . MemberAccess && expression . nodeType === ParseNodeType . MemberAccess ) { return ( isMatchingExpression ( reference . leftExpression , expression . leftExpression ) && reference . memberName . value === expression . memberName . value ) ; } else if ( reference . nodeType === ParseNodeType . Index && expression . nodeType === ParseNodeType . Index ) { if ( ! isMatchingExpression ( reference . baseExpression , expression . baseExpression ) ) { return false ; } if ( expression . items . length !== 1 || expression . trailingComma || expression . items [ 0 ] . name || expression . items [ 0 ] . argumentCategory !== ArgumentCategory . Simple ) { return false ; } if ( reference . items [ 0 ] . valueExpression . nodeType === ParseNodeType . Number ) { const referenceNumberNode = reference . items [ 0 ] . valueExpression ; const subscriptNode = expression . items [ 0 ] . valueExpression ; if ( subscriptNode . nodeType !== ParseNodeType . Number || subscriptNode . isImaginary || ! subscriptNode . isInteger ) { return false ; } return referenceNumberNode . value === subscriptNode . value ; } if ( reference . items [ 0 ] . valueExpression . nodeType === ParseNodeType . StringList ) { const referenceStringListNode = reference . items [ 0 ] . valueExpression ; const subscriptNode = expression . items [ 0 ] . valueExpression ; if ( referenceStringListNode . strings . length === 1 && referenceStringListNode . strings [ 0 ] . nodeType === ParseNodeType . String && subscriptNode . nodeType === ParseNodeType . StringList && subscriptNode . strings . length === 1 && subscriptNode . strings [ 0 ] . nodeType === ParseNodeType . String ) { return referenceStringListNode . strings [ 0 ] . value === subscriptNode . strings [ 0 ] . value ; } } return false ; } return false ; } export function isPartialMatchingExpression ( reference , expression ) { if ( reference . nodeType === ParseNodeType . MemberAccess ) { return ( isMatchingExpression ( reference . leftExpression , expression ) || isPartialMatchingExpression ( reference . leftExpression , expression ) ) ; } else if ( reference . nodeType === ParseNodeType . Index ) { return ( isMatchingExpression ( reference . baseExpression , expression ) || isPartialMatchingExpression ( reference . baseExpression , expression ) ) ; } return false ; } export function isWithinDefaultParamInitializer ( node ) { let curNode = node ; let prevNode ; while ( curNode ) { if ( curNode . nodeType === ParseNodeType . Parameter && prevNode === curNode . defaultValue ) { return true ; } if ( curNode . nodeType === ParseNodeType . Lambda || curNode . nodeType === ParseNodeType . Function || curNode . nodeType === ParseNodeType . Class || curNode . nodeType === ParseNodeType . Module ) { return false ; } prevNode = curNode ; curNode = curNode . parent ; } return false ; } export function isWithinTypeAnnotation ( node , requireQuotedAnnotation ) { let curNode = node ; let prevNode ; let isQuoted = false ; while ( curNode ) { if ( curNode . nodeType === ParseNodeType . Parameter && ( prevNode === curNode . typeAnnotation || prevNode === curNode . typeAnnotationComment ) ) { return isQuoted || ! requireQuotedAnnotation ; } if ( curNode . nodeType === ParseNodeType . Function && prevNode === curNode . returnTypeAnnotation ) { return isQuoted || ! requireQuotedAnnotation ; } if ( curNode . nodeType === ParseNodeType . Function && prevNode === curNode . functionAnnotationComment ) { return true ; } if ( curNode . nodeType === ParseNodeType . TypeAnnotation && prevNode === curNode . typeAnnotation ) { return isQuoted || ! requireQuotedAnnotation ; } if ( curNode . nodeType === ParseNodeType . Assignment && prevNode === curNode . typeAnnotationComment ) { return true ; } if ( curNode . nodeType === ParseNodeType . StringList && prevNode === curNode . typeAnnotation ) { isQuoted = true ; } if ( curNode . nodeType === ParseNodeType . Lambda || curNode . nodeType === ParseNodeType . Function || curNode . nodeType === ParseNodeType . Class || curNode . nodeType === ParseNodeType . Module ) { return false ; } prevNode = curNode ; curNode = curNode . parent ; } return false ; } export function isWithinAnnotationComment ( node ) { let curNode = node ; let prevNode ; while ( curNode ) { if ( curNode . nodeType === ParseNodeType . Function && prevNode === curNode . functionAnnotationComment ) { return true ; } if ( curNode . nodeType === ParseNodeType . Assignment && prevNode === curNode . typeAnnotationComment ) { return true ; } if ( curNode . nodeType === ParseNodeType . Lambda || curNode . nodeType === ParseNodeType . Function || curNode . nodeType === ParseNodeType . Class || curNode . nodeType === ParseNodeType . Module ) { return false ; } prevNode = curNode ; curNode = curNode . parent ; } return false ; } export function isWithinLoop ( node ) { let curNode = node ; while ( curNode ) { switch ( curNode . nodeType ) { case ParseNodeType . For : case ParseNodeType . While : { return true ; } case ParseNodeType . Function : case ParseNodeType . Module : case ParseNodeType . Class : { break ; } } curNode = curNode . parent ; } return false ; } export function isWithinTryBlock ( node , treatWithAsTryBlock = false ) { let curNode = node ; let prevNode ; while ( curNode ) { switch ( curNode . nodeType ) { case ParseNodeType . Try : { return curNode . trySuite === prevNode ; } case ParseNodeType . With : { if ( treatWithAsTryBlock && curNode . suite === prevNode ) { return true ; } break ; } case ParseNodeType . Function : case ParseNodeType . Module : case ParseNodeType . Class : { return false ; } } prevNode = curNode ; curNode = curNode . parent ; } return false ; } export function isWithinAssertExpression ( node ) { let curNode = node ; let prevNode ; while ( curNode ) { switch ( curNode . nodeType ) { case ParseNodeType . Assert : { return curNode . testExpression === prevNode ; } } prevNode = curNode ; curNode = curNode . parent ; } return false ; } export function getDocString ( statements ) { if ( statements . length === 0 ) { return undefined ; } if ( statements [ 0 ] . nodeType !== ParseNodeType . StatementList ) { return undefined ; } if ( ! isDocString ( statements [ 0 ] ) ) { return undefined ; } const strings = ( statements [ 0 ] . statements [ 0 ] as StringListNode ) . strings ; if ( strings . length === 1 ) { return strings [ 0 ] . value ; } return strings . map ( ( s ) => s . value ) . join ( '' ) ; } export function isDocString ( statementList ) { if ( statementList . statements . length === 0 || statementList . statements [ 0 ] . nodeType !== ParseNodeType . StringList ) { return false ; } const strings = statementList . statements [ 0 ] . strings ; if ( strings . length === 0 ) { return false ; } if ( strings . some ( ( n ) => ( n . token . flags & StringTokenFlags . Format ) !== 0 ) ) { return false ; } return true ; } export function isAssignmentToDefaultsFollowingNamedTuple ( callNode ) { if ( callNode . nodeType !== ParseNodeType . Call || ! callNode . parent || callNode . parent . nodeType !== ParseNodeType . Assignment || callNode . parent . leftExpression . nodeType !== ParseNodeType . Name || ! callNode . parent . parent || callNode . parent . parent . nodeType !== ParseNodeType . StatementList ) { return false ; } const namedTupleAssignedName = callNode . parent . leftExpression . value ; const statementList = callNode . parent . parent ; if ( statementList . statements [ 0 ] !== callNode . parent || ! statementList . parent || ! ( statementList . parent . nodeType === ParseNodeType . Module || statementList . parent . nodeType === ParseNodeType . Suite ) ) { return false ; } const moduleOrSuite = statementList . parent ; let statementIndex = moduleOrSuite . statements . findIndex ( ( s ) => s === statementList ) ; if ( statementIndex < 0 ) { return false ; } statementIndex ++ ; while ( statementIndex < moduleOrSuite . statements . length ) { const nextStatement = moduleOrSuite . statements [ statementIndex ] ; if ( nextStatement . nodeType !== ParseNodeType . StatementList ) { break ; } if ( nextStatement . statements [ 0 ] ?. nodeType === ParseNodeType . StringList ) { statementIndex ++ ; continue ; } if ( nextStatement . statements [ 0 ] ?. nodeType === ParseNodeType . Assignment ) { const assignNode = nextStatement . statements [ 0 ] ; if ( assignNode . leftExpression . nodeType === ParseNodeType . MemberAccess && assignNode . leftExpression . memberName . value === '__defaults__' ) { const defaultTarget = assignNode . leftExpression . leftExpression ; if ( defaultTarget . nodeType === ParseNodeType . MemberAccess && defaultTarget . memberName . value === '__new__' && defaultTarget . leftExpression . nodeType === ParseNodeType . Name && defaultTarget . leftExpression . value === namedTupleAssignedName ) { return true ; } } } break ; } return false ; } export class NameNodeWalker extends ParseTreeWalker { private _subscriptIndex : number | undefined ; private _baseExpression : ExpressionNode | undefined ; constructor ( private _callback ) { super ( ) ; } override visitName ( node ) { this . _callback ( node , this . _subscriptIndex , this . _baseExpression ) ; return true ; } override visitIndex ( node ) { this . walk ( node . baseExpression ) ; const prevSubscriptIndex = this . _subscriptIndex ; const prevBaseExpression = this . _baseExpression ; this . _baseExpression = node . baseExpression ; node . items . forEach ( ( item , index ) => { this . _subscriptIndex = index ; this . walk ( item ) ; } ) ; this . _subscriptIndex = prevSubscriptIndex ; this . _baseExpression = prevBaseExpression ; return false ; } } export function getEnclosingParameter ( node ) { let curNode = node ; while ( curNode ) { if ( curNode . nodeType === ParseNodeType . Parameter ) { return curNode ; } curNode = curNode . parent ; } return undefined ; } export function getCallNodeAndActiveParameterIndex ( node , insertionOffset , tokens ) { let curNode = node ; let callNode ; while ( curNode !== undefined ) { if ( curNode . nodeType === ParseNodeType . Call ) { if ( isOffsetInsideCallArgs ( curNode , insertionOffset ) ) { callNode = curNode ; break ; } } curNode = curNode . parent ; } if ( ! callNode || ! callNode . arguments ) { return undefined ; } const endPosition = TextRange . getEnd ( callNode ) ; if ( insertionOffset > endPosition ) { return undefined ; } const tokenAtEnd = getTokenAt ( tokens , endPosition - 1 ) ; if ( insertionOffset === endPosition && tokenAtEnd ?. type === TokenType . CloseParenthesis ) { return undefined ; } let addedActive = false ; let activeIndex = - 1 ; let activeOrFake = false ; callNode . arguments . forEach ( ( arg , index ) => { if ( addedActive ) { return ; } let start = arg . start ; const startTokenIndex = tokens . getItemAtPosition ( start ) ; if ( startTokenIndex >= 0 ) { start = TextRange . getEnd ( tokens . getItemAt ( startTokenIndex - 1 ) ) ; } let end = TextRange . getEnd ( arg ) ; const endTokenIndex = tokens . getItemAtPosition ( end ) ; if ( endTokenIndex >= 0 ) { for ( let i = endTokenIndex ; i < tokens . count ; i ++ ) { const tok = tokens . getItemAt ( i ) ; switch ( tok . type ) { case TokenType . Comma : case TokenType . CloseParenthesis : break ; default : continue ; } end = TextRange . getEnd ( tok ) ; break ; } } if ( insertionOffset < end ) { activeIndex = index ; activeOrFake = insertionOffset >= start ; addedActive = true ; } } ) ; if ( ! addedActive ) { activeIndex = callNode . arguments . length + 1 ; } return { callNode , activeIndex , activeOrFake , } ; function isOffsetInsideCallArgs ( node , offset ) { let found = true ; const argumentStart = node . leftExpression . length > 0 ? TextRange . getEnd ( node . leftExpression ) - 1 : node . leftExpression . start ; const index = tokens . getItemAtPosition ( argumentStart ) ; if ( index >= 0 && index + 1 < tokens . count ) { const token = tokens . getItemAt ( index + 1 ) ; if ( token . type === TokenType . OpenParenthesis && insertionOffset < TextRange . getEnd ( token ) ) { found = false ; } } return found ; } } export function getTokenAt ( tokens , position ) { const index = tokens . getItemAtPosition ( position ) ; if ( index < 0 ) { return undefined ; } return tokens . getItemAt ( index ) ; } export function getTokenOverlapping ( tokens , position ) { const index = tokens . getItemAtPosition ( position ) ; if ( index < 0 ) { return undefined ; } const token = tokens . getItemAt ( index ) ; return TextRange . overlaps ( token , position ) ? token : undefined ; } export function printParseNodeType ( type ) { switch ( type ) { case ParseNodeType . Error : return 'Error' ; case ParseNodeType . Argument : return 'Argument' ; case ParseNodeType . Assert : return 'Assert' ; case ParseNodeType . Assignment : return 'Assignment' ; case ParseNodeType . AssignmentExpression : return 'AssignmentExpression' ; case ParseNodeType . AugmentedAssignment : return 'AugmentedAssignment' ; case ParseNodeType . Await : return 'Await' ; case ParseNodeType . BinaryOperation : return 'BinaryOperation' ; case ParseNodeType . Break : return 'Break' ; case ParseNodeType . Call : return 'Call' ; case ParseNodeType . Class : return 'Class' ; case ParseNodeType . Constant : return 'Constant' ; case ParseNodeType . Continue : return 'Continue' ; case ParseNodeType . Decorator : return 'Decorator' ; case ParseNodeType . Del : return 'Del' ; case ParseNodeType . Dictionary : return 'Dictionary' ; case ParseNodeType . DictionaryExpandEntry : return 'DictionaryExpandEntry' ; case ParseNodeType . DictionaryKeyEntry : return 'DictionaryKeyEntry' ; case ParseNodeType . Ellipsis : return 'Ellipsis' ; case ParseNodeType . If : return 'If' ; case ParseNodeType . Import : return 'Import' ; case ParseNodeType . ImportAs : return 'ImportAs' ; case ParseNodeType . ImportFrom : return 'ImportFrom' ; case ParseNodeType . ImportFromAs : return 'ImportFromAs' ; case ParseNodeType . Index : return 'Index' ; case ParseNodeType . Except : return 'Except' ; case ParseNodeType . For : return 'For' ; case ParseNodeType . FormatString : return 'FormatString' ; case ParseNodeType . Function : return 'Function' ; case ParseNodeType . Global : return 'Global' ; case ParseNodeType . Lambda : return 'Lambda' ; case ParseNodeType . List : return 'List' ; case ParseNodeType . ListComprehension : return 'ListComprehension' ; case ParseNodeType . ListComprehensionFor : return 'ListComprehensionFor' ; case ParseNodeType . ListComprehensionIf : return 'ListComprehensionIf' ; case ParseNodeType . MemberAccess : return 'MemberAccess' ; case ParseNodeType . Module : return 'Module' ; case ParseNodeType . ModuleName : return 'ModuleName' ; case ParseNodeType . Name : return 'Name' ; case ParseNodeType . Nonlocal : return 'Nonlocal' ; case ParseNodeType . Number : return 'Number' ; case ParseNodeType . Parameter : return 'Parameter' ; case ParseNodeType . Pass : return 'Pass' ; case ParseNodeType . Raise : return 'Raise' ; case ParseNodeType . Return : return 'Return' ; case ParseNodeType . Set : return 'Set' ; case ParseNodeType . Slice : return 'Slice' ; case ParseNodeType . StatementList : return 'StatementList' ; case ParseNodeType . StringList : return 'StringList' ; case ParseNodeType . String : return 'String' ; case ParseNodeType . Suite : return 'Suite' ; case ParseNodeType . Ternary : return 'Ternary' ; case ParseNodeType . Tuple : return 'Tuple' ; case ParseNodeType . Try : return 'Try' ; case ParseNodeType . TypeAnnotation : return 'TypeAnnotation' ; case ParseNodeType . UnaryOperation : return 'UnaryOperation' ; case ParseNodeType . Unpack : return 'Unpack' ; case ParseNodeType . While : return 'While' ; case ParseNodeType . With : return 'With' ; case ParseNodeType . WithItem : return 'WithItem' ; case ParseNodeType . Yield : return 'Yield' ; case ParseNodeType . YieldFrom : return 'YieldFrom' ; case ParseNodeType . FunctionAnnotation : return 'FunctionAnnotation' ; case ParseNodeType . Match : return 'Match' ; case ParseNodeType . Case : return 'Case' ; case ParseNodeType . PatternSequence : return 'PatternSequence' ; case ParseNodeType . PatternAs : return 'PatternAs' ; case ParseNodeType . PatternLiteral : return 'PatternLiteral' ; case ParseNodeType . PatternClass : return 'PatternClass' ; case ParseNodeType . PatternCapture : return 'PatternCapture' ; case ParseNodeType . PatternMapping : return 'PatternMapping' ; case ParseNodeType . PatternMappingKeyEntry : return 'PatternMappingKeyEntry' ; case ParseNodeType . PatternMappingExpandEntry : return 'PatternMappingExpandEntry' ; case ParseNodeType . PatternValue : return 'PatternValue' ; case ParseNodeType . PatternClassArgument : return 'PatternClassArgument' ; case ParseNodeType . ArrowCallable : return 'ArrowCallable' ; } assertNever ( type ) ; } export function isWriteAccess ( node ) { let prevNode = node ; let curNode = prevNode . parent ; while ( curNode ) { switch ( curNode . nodeType ) { case ParseNodeType . Assignment : { return prevNode === curNode . leftExpression ; } case ParseNodeType . AugmentedAssignment : { return prevNode === curNode . leftExpression ; } case ParseNodeType . AssignmentExpression : { return prevNode === curNode . name ; } case ParseNodeType . Del : { return true ; } case ParseNodeType . For : { return prevNode === curNode . targetExpression ; } case ParseNodeType . ImportAs : { return ( prevNode === curNode . alias || ( curNode . module . nameParts . length > 0 && prevNode === curNode . module . nameParts [ 0 ] ) ) ; } case ParseNodeType . ImportFromAs : { return prevNode === curNode . alias || ( ! curNode . alias && prevNode === curNode . name ) ; } case ParseNodeType . MemberAccess : { if ( prevNode !== curNode . memberName ) { return false ; } break ; } case ParseNodeType . Except : { return prevNode === curNode . name ; } case ParseNodeType . With : { return curNode . withItems . some ( ( item ) => item === prevNode ) ; } case ParseNodeType . ListComprehensionFor : { return prevNode === curNode . targetExpression ; } case ParseNodeType . TypeAnnotation : { if ( prevNode === curNode . typeAnnotation ) { return false ; } break ; } case ParseNodeType . Function : case ParseNodeType . Class : case ParseNodeType . Module : { return false ; } } prevNode = curNode ; curNode = curNode . parent ; } return false ; } export function getModuleNode ( node ) { let current = node ; while ( current && current . nodeType !== ParseNodeType . Module ) { current = current . parent ; } return current ; } export function getFileInfoFromNode ( node ) { const current = getModuleNode ( node ) ; return current ? AnalyzerNodeInfo . getFileInfo ( current ) : undefined ; } export function isFunctionSuiteEmpty ( node ) { let isEmpty = true ; node . suite . statements . forEach ( ( statement ) => { if ( statement . nodeType === ParseNodeType . Error ) { return ; } else if ( statement . nodeType === ParseNodeType . StatementList ) { statement . statements . forEach ( ( subStatement ) => { if ( subStatement . nodeType !== ParseNodeType . Ellipsis && subStatement . nodeType !== ParseNodeType . StringList && subStatement . nodeType !== ParseNodeType . Pass ) { isEmpty = false ; } } ) ; } else { isEmpty = false ; } } ) ; return isEmpty ; } export function isImportModuleName ( node ) { return getFirstAncestorOrSelfOfKind ( node , ParseNodeType . ModuleName ) ?. parent ?. nodeType === ParseNodeType . ImportAs ; } export function isImportAlias ( node ) { return node . parent ?. nodeType === ParseNodeType . ImportAs && node . parent . alias === node ; } export function isFromImportModuleName ( node ) { return getFirstAncestorOrSelfOfKind ( node , ParseNodeType . ModuleName ) ?. parent ?. nodeType === ParseNodeType . ImportFrom ; } export function isFromImportName ( node ) { return node . parent ?. nodeType === ParseNodeType . ImportFromAs && node . parent . name === node ; } export function isFromImportAlias ( node ) { return node . parent ?. nodeType === ParseNodeType . ImportFromAs && node . parent . alias === node ; } export function isLastNameOfModuleName ( node ) { if ( node . parent ?. nodeType !== ParseNodeType . ModuleName ) { return false ; } const module = node . parent ; if ( module . nameParts . length === 0 ) { return false ; } return module . nameParts [ module . nameParts . length - 1 ] === node ; } function * _getAncestorsIncludingSelf ( node ) { while ( node !== undefined ) { yield node ; node = node . parent ; } } type NodeForType < NT extends ParseNodeType , T extends ParseNode > = T extends ParseNode & { nodeType : NT ; } ? T : never ; export function getFirstAncestorOrSelfOfKind < NT extends ParseNodeType , T extends ParseNode > ( node , type ) { return getFirstAncestorOrSelf ( node , ( n ) => n . nodeType === type ) as NodeForType < NT , T > | undefined ; } export function getFirstAncestorOrSelf ( node , predicate ) { for ( const current of _getAncestorsIncludingSelf ( node ) ) { if ( predicate ( current ) ) { return current ; } } return undefined ; } export function getDottedNameWithGivenNodeAsLastName ( node ) { if ( node . parent ?. nodeType !== ParseNodeType . MemberAccess ) { return node ; } if ( node . parent . leftExpression === node ) { return node ; } return node . parent ; } export function getDottedName ( node ) { if ( node . nodeType === ParseNodeType . Name ) { return [ node ] ; } const names = [ ] ; if ( _getDottedName ( node , names ) ) { return names . reverse ( ) ; } return undefined ; function _getDottedName ( node , names ) { if ( node . nodeType === ParseNodeType . Name ) { names . push ( node ) ; return true ; } names . push ( node . memberName ) ; if ( node . leftExpression . nodeType === ParseNodeType . Name || node . leftExpression . nodeType === ParseNodeType . MemberAccess ) { return _getDottedName ( node . leftExpression , names ) ; } return false ; } } export function getFirstNameOfDottedName ( node ) { if ( node . nodeType === ParseNodeType . Name ) { return node ; } if ( node . leftExpression . nodeType === ParseNodeType . Name || node . leftExpression . nodeType === ParseNodeType . MemberAccess ) { return getFirstNameOfDottedName ( node . leftExpression ) ; } return undefined ; } export function isFirstNameOfDottedName ( node ) { if ( node . parent ?. nodeType !== ParseNodeType . MemberAccess ) { return true ; } if ( node . parent . leftExpression === node ) { return true ; } return false ; } export function isLastNameOfDottedName ( node ) { if ( node . parent ?. nodeType !== ParseNodeType . MemberAccess ) { return true ; } if ( node . parent . leftExpression . nodeType !== ParseNodeType . Name && node . parent . leftExpression . nodeType !== ParseNodeType . MemberAccess ) { return false ; } if ( node . parent . leftExpression === node ) { return false ; } return node . parent . parent ?. nodeType !== ParseNodeType . MemberAccess ; } export function getStringNodeValueRange ( node ) { return getStringValueRange ( node . token ) ; } export function getStringValueRange ( token ) { const length = token . quoteMarkLength ; const hasEnding = ! ( token . flags & StringTokenFlags . Unterminated ) ; return TextRange . create ( token . start + length , token . length - length - ( hasEnding ? length : 0 ) ) ; } export function getFullStatementRange ( statementNode , tokenizerOutput ) { const range = convertTextRangeToRange ( statementNode , tokenizerOutput . lines ) ; const endPosition = _getEndPositionIfMultipleStatementsAreOnSameLine ( range , TextRange . getEnd ( statementNode ) , tokenizerOutput ) ; if ( endPosition ) { return { start : range . start , end : endPosition } ; } if ( range . end . line === tokenizerOutput . lines . count - 1 ) { return range ; } return { start : range . start , end : { line : range . end . line + 1 , character : 0 } } ; } export function isUnannotatedFunction ( node ) { return ( node . returnTypeAnnotation === undefined && node . parameters . every ( ( param ) => param . typeAnnotation === undefined && param . typeAnnotationComment === undefined ) ) ; } function _getEndPositionIfMultipleStatementsAreOnSameLine ( range , tokenPosition , tokenizerOutput ) { const tokenIndex = tokenizerOutput . tokens . getItemAtPosition ( tokenPosition ) ; if ( tokenIndex < 0 ) { return undefined ; } let currentIndex = tokenIndex ; for ( ; currentIndex < tokenizerOutput . tokens . count ; currentIndex ++ ) { const token = tokenizerOutput . tokens . getItemAt ( currentIndex ) ; const tokenRange = convertTextRangeToRange ( token , tokenizerOutput . lines ) ; if ( range . end . line !== tokenRange . start . line ) { break ; } } for ( let index = tokenIndex ; index < currentIndex ; index ++ ) { const token = tokenizerOutput . tokens . getItemAt ( index ) ; if ( token . type === TokenType . Semicolon || token . type === TokenType . NewLine ) { continue ; } const tokenRange = convertTextRangeToRange ( token , tokenizerOutput . lines ) ; return tokenRange . start ; } return undefined ; }