import { Commands } from '../commands/commands' ; import { DiagnosticLevel } from '../common/configOptions' ; import { assert , assertNever , fail } from '../common/debug' ; import { CreateTypeStubFileAction , Diagnostic } from '../common/diagnostic' ; import { DiagnosticRule } from '../common/diagnosticRules' ; import { getFileName , stripFileExtension } from '../common/pathUtils' ; import { convertOffsetsToRange } from '../common/positionUtils' ; import { getEmptyRange } from '../common/textRange' ; import { TextRange } from '../common/textRange' ; import { Localizer } from '../localization/localize' ; import { ArgumentCategory , AssertNode , AssignmentExpressionNode , AssignmentNode , AugmentedAssignmentNode , AwaitNode , BinaryOperationNode , BreakNode , CallNode , CaseNode , ClassNode , ContinueNode , DelNode , ExceptNode , ExpressionNode , ForNode , FunctionNode , GlobalNode , IfNode , ImportAsNode , ImportFromNode , IndexNode , LambdaNode , ListComprehensionNode , MatchNode , MemberAccessNode , ModuleNameNode , ModuleNode , NameNode , NonlocalNode , ParseNode , ParseNodeType , PatternAsNode , PatternCaptureNode , PatternMappingExpandEntryNode , RaiseNode , ReturnNode , StatementNode , StringListNode , StringNode , SuiteNode , TernaryNode , TryNode , TypeAnnotationNode , UnaryOperationNode , WhileNode , WithNode , YieldFromNode , YieldNode , } from '../parser/parseNodes' ; import { KeywordType , OperatorType } from '../parser/tokenizerTypes' ; import { AnalyzerFileInfo , ImportLookupResult } from './analyzerFileInfo' ; import * as AnalyzerNodeInfo from './analyzerNodeInfo' ; import { CodeFlowReferenceExpressionNode , createKeyForReference , FlowAssignment , FlowAssignmentAlias , FlowBranchLabel , FlowCall , FlowCondition , FlowExhaustedMatch , FlowFlags , FlowLabel , FlowNarrowForPattern , FlowNode , FlowPostContextManagerLabel , FlowPostFinally , FlowPreFinallyGate , FlowVariableAnnotation , FlowWildcardImport , getUniqueFlowNodeId , isCodeFlowSupportedForReference , } from './codeFlowTypes' ; import { AliasDeclaration , ClassDeclaration , DeclarationType , FunctionDeclaration , IntrinsicType , ModuleLoaderActions , ParameterDeclaration , VariableDeclaration , } from './declaration' ; import { ImplicitImport , ImportResult , ImportType } from './importResult' ; import * as ParseTreeUtils from './parseTreeUtils' ; import { ParseTreeWalker } from './parseTreeWalker' ; import { NameBindingType , Scope , ScopeType } from './scope' ; import * as StaticExpressions from './staticExpressions' ; import { indeterminateSymbolId , Symbol , SymbolFlags } from './symbol' ; import { isConstantName , isPrivateName , isPrivateOrProtectedName } from './symbolNameUtils' ; interface MemberAccessInfo { classNode : ClassNode ; methodNode : FunctionNode ; classScope : Scope ; isInstanceMember : boolean ; } interface DeferredBindingTask { scope : Scope ; codeFlowExpressions : Set < string > ; callback : ( ) => void ; } interface FinalInfo { isFinal : boolean ; finalTypeNode : ExpressionNode | undefined ; } interface ClassVarInfo { isClassVar : boolean ; classVarTypeNode : ExpressionNode | undefined ; } export class Binder extends ParseTreeWalker { private readonly _fileInfo : AnalyzerFileInfo ; private _deferredBindingTasks : DeferredBindingTask [ ] = [ ] ; private _currentScope ! : Scope ; private _currentFlowNode : FlowNode | undefined ; private _targetFunctionDeclaration : FunctionDeclaration | undefined ; private _currentBreakTarget : FlowLabel | undefined ; private _currentContinueTarget : FlowLabel | undefined ; private _currentTrueTarget : FlowLabel | undefined ; private _currentFalseTarget : FlowLabel | undefined ; private _currentExceptTargets : FlowLabel [ ] = [ ] ; private _finallyTargets : FlowLabel [ ] = [ ] ; private _currentReturnTarget : FlowLabel | undefined ; private _currentScopeCodeFlowExpressions : Set < string > | undefined ; private _typingImportAliases : string [ ] = [ ] ; private _sysImportAliases : string [ ] = [ ] ; private _dataclassesImportAliases : string [ ] = [ ] ; private _typingSymbolAliases : Map < string , string > = new Map < string , string > ( ) ; private _dataclassesSymbolAliases : Map < string , string > = new Map < string , string > ( ) ; private _dunderAllNames : string [ ] | undefined ; private _dunderAllStringNodes : StringNode [ ] = [ ] ; private _usesUnsupportedDunderAllForm = false ; private static _unreachableFlowNode : FlowNode = { flags : FlowFlags . Unreachable , id : getUniqueFlowNodeId ( ) , } ; private _potentialHiddenSymbols = new Map < string , Symbol > ( ) ; private _potentialPrivateSymbols = new Map < string , Symbol > ( ) ; private _functionCodeFlowComplexity = 0 ; constructor ( fileInfo , private _moduleSymbolOnly = false ) { super ( ) ; this . _fileInfo = fileInfo ; } bindModule ( node ) { const isBuiltInModule = this . _fileInfo . builtinsScope === undefined ; this . _createNewScope ( isBuiltInModule ? ScopeType . Builtin : ScopeType . Module , this . _fileInfo . builtinsScope , ( ) => { AnalyzerNodeInfo . setScope ( node , this . _currentScope ) ; AnalyzerNodeInfo . setFlowNode ( node , this . _currentFlowNode ! ) ; this . _addBuiltInSymbolToCurrentScope ( '__doc__' , node , 'str | None' ) ; this . _addBuiltInSymbolToCurrentScope ( '__name__' , node , 'str' ) ; this . _addBuiltInSymbolToCurrentScope ( '__qualname__' , node , 'str' ) ; this . _addBuiltInSymbolToCurrentScope ( '__loader__' , node , 'Any' ) ; this . _addBuiltInSymbolToCurrentScope ( '__package__' , node , 'str' ) ; this . _addBuiltInSymbolToCurrentScope ( '__spec__' , node , 'Any' ) ; this . _addBuiltInSymbolToCurrentScope ( '__path__' , node , 'Iterable[str]' ) ; this . _addBuiltInSymbolToCurrentScope ( '__file__' , node , 'str' ) ; this . _addBuiltInSymbolToCurrentScope ( '__cached__' , node , 'str' ) ; this . _addBuiltInSymbolToCurrentScope ( '__dict__' , node , 'Dict[str, Any]' ) ; this . _addBuiltInSymbolToCurrentScope ( '__annotations__' , node , 'Dict[str, Any]' ) ; this . _addBuiltInSymbolToCurrentScope ( '__builtins__' , node , 'Any' ) ; this . _currentFlowNode = this . _createStartFlowNode ( ) ; this . _walkStatementsAndReportUnreachable ( node . statements ) ; AnalyzerNodeInfo . setCodeFlowExpressions ( node , this . _currentScopeCodeFlowExpressions ! ) ; AnalyzerNodeInfo . setAfterFlowNode ( node , this . _currentFlowNode ) ; } ) ; this . _bindDeferred ( ) ; this . _potentialHiddenSymbols . forEach ( ( symbol , name ) => { if ( ! this . _dunderAllNames ?. some ( ( sym ) => sym === name ) ) { if ( this . _fileInfo . isStubFile ) { symbol . setIsExternallyHidden ( ) ; } else { symbol . setPrivatePyTypedImport ( ) ; } } } ) ; this . _potentialPrivateSymbols . forEach ( ( symbol , name ) => { if ( ! this . _dunderAllNames ?. some ( ( sym ) => sym === name ) ) { symbol . setIsPrivateMember ( ) ; } } ) ; if ( this . _dunderAllNames ) { AnalyzerNodeInfo . setDunderAllInfo ( node , { names : this . _dunderAllNames , stringNodes : this . _dunderAllStringNodes , usesUnsupportedDunderAllForm : this . _usesUnsupportedDunderAllForm , } ) ; } else { AnalyzerNodeInfo . setDunderAllInfo ( node , undefined ) ; } const scope = AnalyzerNodeInfo . getScope ( node ) ; if ( scope && this . _dunderAllNames ) { for ( const name of this . _dunderAllNames ) { scope . symbolTable . get ( name ) ?. setIsInDunderAll ( ) ; } } } override visitModule ( node ) { fail ( 'We should never get here' ) ; return false ; } override visitSuite ( node ) { this . _walkStatementsAndReportUnreachable ( node . statements ) ; return false ; } override visitModuleName ( node ) { const importResult = AnalyzerNodeInfo . getImportInfo ( node ) ; assert ( importResult !== undefined ) ; if ( ! importResult || importResult . isNativeLib ) { return true ; } if ( ! importResult . isImportFound ) { this . _addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportMissingImports , DiagnosticRule . reportMissingImports , Localizer . Diagnostic . importResolveFailure ( ) . format ( { importName : importResult . importName } ) , node ) ; return true ; } if ( ! importResult . isStubFile && importResult . importType === ImportType . ThirdParty && ! importResult . pyTypedInfo ) { const diagnostic = this . _addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportMissingTypeStubs , DiagnosticRule . reportMissingTypeStubs , Localizer . Diagnostic . stubFileMissing ( ) . format ( { importName : importResult . importName } ) , node ) ; if ( diagnostic ) { const createTypeStubAction = { action : Commands . createTypeStub , moduleName : importResult . importName , } ; diagnostic . addAction ( createTypeStubAction ) ; } } if ( importResult . isStubFile && importResult . importType !== ImportType . BuiltIn && importResult . nonStubImportResult && ! importResult . nonStubImportResult . isImportFound ) { if ( ! this . _fileInfo . isStubFile ) { this . _addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportMissingModuleSource , DiagnosticRule . reportMissingModuleSource , Localizer . Diagnostic . importSourceResolveFailure ( ) . format ( { importName : importResult . importName , } ) , node ) ; } } return true ; } override visitClass ( node ) { this . walkMultiple ( node . decorators ) ; const classDeclaration = { type : DeclarationType . Class , node , path : this . _fileInfo . filePath , range : convertOffsetsToRange ( node . name . start , TextRange . getEnd ( node . name ) , this . _fileInfo . lines ) , moduleName : this . _fileInfo . moduleName , } ; const symbol = this . _bindNameToScope ( this . _currentScope , node . name . value ) ; if ( symbol ) { symbol . addDeclaration ( classDeclaration ) ; } AnalyzerNodeInfo . setDeclaration ( node , classDeclaration ) ; this . walkMultiple ( node . arguments ) ; let parentScope = this . _currentScope ; while ( parentScope . type === ScopeType . Class ) { parentScope = parentScope . parent ! ; } this . _createNewScope ( ScopeType . Class , parentScope , ( ) => { AnalyzerNodeInfo . setScope ( node , this . _currentScope ) ; this . _addBuiltInSymbolToCurrentScope ( '__doc__' , node , 'str | None' ) ; this . _addBuiltInSymbolToCurrentScope ( '__module__' , node , 'str' ) ; if ( ! this . _moduleSymbolOnly ) { this . walk ( node . suite ) ; } } ) ; this . _createAssignmentTargetFlowNodes ( node . name , false , false ) ; return false ; } override visitFunction ( node ) { AnalyzerNodeInfo . setFlowNode ( node , this . _currentFlowNode ! ) ; const symbol = this . _bindNameToScope ( this . _currentScope , node . name . value ) ; const containingClassNode = ParseTreeUtils . getEnclosingClass ( node , true ) ; const functionDeclaration = { type : DeclarationType . Function , node , isMethod : ! ! containingClassNode , isGenerator : false , path : this . _fileInfo . filePath , range : convertOffsetsToRange ( node . name . start , TextRange . getEnd ( node . name ) , this . _fileInfo . lines ) , moduleName : this . _fileInfo . moduleName , } ; if ( symbol ) { symbol . addDeclaration ( functionDeclaration ) ; } AnalyzerNodeInfo . setDeclaration ( node , functionDeclaration ) ; this . walkMultiple ( node . decorators ) ; node . parameters . forEach ( ( param ) => { if ( param . defaultValue ) { this . walk ( param . defaultValue ) ; } if ( param . typeAnnotation ) { this . walk ( param . typeAnnotation ) ; } if ( param . typeAnnotationComment ) { this . walk ( param . typeAnnotationComment ) ; } } ) ; if ( node . returnTypeAnnotation ) { this . walk ( node . returnTypeAnnotation ) ; } if ( node . functionAnnotationComment ) { this . walk ( node . functionAnnotationComment ) ; } let functionOrModuleNode = node . parent ; while ( functionOrModuleNode ) { if ( functionOrModuleNode . nodeType === ParseNodeType . Module || functionOrModuleNode . nodeType === ParseNodeType . Function ) { break ; } functionOrModuleNode = functionOrModuleNode . parent ; } assert ( functionOrModuleNode !== undefined ) ; const functionOrModuleScope = AnalyzerNodeInfo . getScope ( functionOrModuleNode ! ) ; assert ( functionOrModuleScope !== undefined ) ; this . _createNewScope ( ScopeType . Function , functionOrModuleScope , ( ) => { AnalyzerNodeInfo . setScope ( node , this . _currentScope ) ; const enclosingClass = ParseTreeUtils . getEnclosingClass ( node ) ; if ( enclosingClass ) { this . _addBuiltInSymbolToCurrentScope ( '__class__' , node , 'class' ) ; } this . _deferBinding ( ( ) => { this . _currentFlowNode = this . _createStartFlowNode ( ) ; this . _functionCodeFlowComplexity = 0 ; node . parameters . forEach ( ( paramNode ) => { if ( paramNode . name ) { const symbol = this . _bindNameToScope ( this . _currentScope , paramNode . name . value ) ; if ( symbol ) { const paramDeclaration = { type : DeclarationType . Parameter , node : paramNode , path : this . _fileInfo . filePath , range : convertOffsetsToRange ( paramNode . start , TextRange . getEnd ( paramNode ) , this . _fileInfo . lines ) , moduleName : this . _fileInfo . moduleName , } ; symbol . addDeclaration ( paramDeclaration ) ; AnalyzerNodeInfo . setDeclaration ( paramNode . name , paramDeclaration ) ; } this . _createFlowAssignment ( paramNode . name ) ; } } ) ; this . _targetFunctionDeclaration = functionDeclaration ; this . _currentReturnTarget = this . _createBranchLabel ( ) ; this . walk ( node . suite ) ; AnalyzerNodeInfo . setAfterFlowNode ( node . suite , this . _currentFlowNode ) ; this . _addAntecedent ( this . _currentReturnTarget , this . _currentFlowNode ) ; const returnFlowNode = this . _finishFlowLabel ( this . _currentReturnTarget ) ; AnalyzerNodeInfo . setAfterFlowNode ( node , returnFlowNode ) ; AnalyzerNodeInfo . setCodeFlowExpressions ( node , this . _currentScopeCodeFlowExpressions ! ) ; AnalyzerNodeInfo . setCodeFlowComplexity ( node , this . _functionCodeFlowComplexity ) ; } ) ; } ) ; this . _createAssignmentTargetFlowNodes ( node . name , false , false ) ; return false ; } override visitLambda ( node ) { AnalyzerNodeInfo . setFlowNode ( node , this . _currentFlowNode ! ) ; node . parameters . forEach ( ( param ) => { if ( param . defaultValue ) { this . walk ( param . defaultValue ) ; } } ) ; this . _createNewScope ( ScopeType . Function , this . _currentScope , ( ) => { AnalyzerNodeInfo . setScope ( node , this . _currentScope ) ; this . _deferBinding ( ( ) => { this . _currentFlowNode = this . _createStartFlowNode ( ) ; node . parameters . forEach ( ( paramNode ) => { if ( paramNode . name ) { const symbol = this . _bindNameToScope ( this . _currentScope , paramNode . name . value ) ; if ( symbol ) { const paramDeclaration = { type : DeclarationType . Parameter , node : paramNode , path : this . _fileInfo . filePath , range : convertOffsetsToRange ( paramNode . start , TextRange . getEnd ( paramNode ) , this . _fileInfo . lines ) , moduleName : this . _fileInfo . moduleName , } ; symbol . addDeclaration ( paramDeclaration ) ; AnalyzerNodeInfo . setDeclaration ( paramNode . name , paramDeclaration ) ; } this . _createFlowAssignment ( paramNode . name ) ; this . walk ( paramNode . name ) ; } } ) ; this . walk ( node . expression ) ; AnalyzerNodeInfo . setCodeFlowExpressions ( node , this . _currentScopeCodeFlowExpressions ! ) ; } ) ; } ) ; return false ; } override visitCall ( node ) { this . _disableTrueFalseTargets ( ( ) => { this . walk ( node . leftExpression ) ; this . walkMultiple ( node . arguments ) ; } ) ; this . _createCallFlowNode ( node ) ; if ( this . _currentScope . type === ScopeType . Module && node . leftExpression . nodeType === ParseNodeType . MemberAccess && node . leftExpression . leftExpression . nodeType === ParseNodeType . Name && node . leftExpression . leftExpression . value === '__all__' ) { let emitDunderAllWarning = true ; if ( node . leftExpression . memberName . value === 'extend' && node . arguments . length === 1 ) { const argExpr = node . arguments [ 0 ] . valueExpression ; if ( argExpr . nodeType === ParseNodeType . List ) { argExpr . entries . forEach ( ( listEntryNode ) => { if ( listEntryNode . nodeType === ParseNodeType . StringList && listEntryNode . strings . length === 1 && listEntryNode . strings [ 0 ] . nodeType === ParseNodeType . String ) { this . _dunderAllNames ?. push ( listEntryNode . strings [ 0 ] . value ) ; this . _dunderAllStringNodes ?. push ( listEntryNode . strings [ 0 ] ) ; emitDunderAllWarning = false ; } } ) ; } else if ( argExpr . nodeType === ParseNodeType . MemberAccess && argExpr . leftExpression . nodeType === ParseNodeType . Name && argExpr . memberName . value === '__all__' ) { const namesToAdd = this . _getDunderAllNamesFromImport ( argExpr . leftExpression . value ) ; if ( namesToAdd && namesToAdd . length > 0 ) { namesToAdd . forEach ( ( name ) => { this . _dunderAllNames ?. push ( name ) ; } ) ; emitDunderAllWarning = false ; } } } else if ( node . leftExpression . memberName . value === 'remove' && node . arguments . length === 1 ) { const argExpr = node . arguments [ 0 ] . valueExpression ; if ( argExpr . nodeType === ParseNodeType . StringList && argExpr . strings . length === 1 && argExpr . strings [ 0 ] . nodeType === ParseNodeType . String && this . _dunderAllNames ) { this . _dunderAllNames = this . _dunderAllNames . filter ( ( name ) => name !== argExpr . strings [ 0 ] . value ) ; this . _dunderAllStringNodes = this . _dunderAllStringNodes . filter ( ( node ) => node . value !== argExpr . strings [ 0 ] . value ) ; emitDunderAllWarning = false ; } } else if ( node . leftExpression . memberName . value === 'append' && node . arguments . length === 1 ) { const argExpr = node . arguments [ 0 ] . valueExpression ; if ( argExpr . nodeType === ParseNodeType . StringList && argExpr . strings . length === 1 && argExpr . strings [ 0 ] . nodeType === ParseNodeType . String ) { this . _dunderAllNames ?. push ( argExpr . strings [ 0 ] . value ) ; this . _dunderAllStringNodes ?. push ( argExpr . strings [ 0 ] ) ; emitDunderAllWarning = false ; } } if ( emitDunderAllWarning ) { this . _usesUnsupportedDunderAllForm = true ; this . _addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnsupportedDunderAll , DiagnosticRule . reportUnsupportedDunderAll , Localizer . Diagnostic . unsupportedDunderAllOperation ( ) , node ) ; } } return false ; } override visitAssignment ( node ) { if ( this . _handleTypingStubAssignmentOrAnnotation ( node ) ) { return false ; } this . _bindPossibleTupleNamedTarget ( node . leftExpression ) ; if ( node . typeAnnotationComment ) { this . walk ( node . typeAnnotationComment ) ; this . _addTypeDeclarationForVariable ( node . leftExpression , node . typeAnnotationComment ) ; } this . walk ( node . rightExpression ) ; let isPossibleTypeAlias = true ; if ( ParseTreeUtils . getEnclosingFunction ( node ) ) { isPossibleTypeAlias = false ; } else if ( node . rightExpression . nodeType === ParseNodeType . Call && this . _fileInfo . isTypingStubFile ) { isPossibleTypeAlias = false ; } else if ( ParseTreeUtils . isWithinLoop ( node ) ) { isPossibleTypeAlias = false ; } this . _addInferredTypeAssignmentForVariable ( node . leftExpression , node . rightExpression , isPossibleTypeAlias ) ; this . _createAssignmentTargetFlowNodes ( node . leftExpression , true , false ) ; if ( this . _currentScope . type === ScopeType . Module ) { if ( ( node . leftExpression . nodeType === ParseNodeType . Name && node . leftExpression . value === '__all__' ) || ( node . leftExpression . nodeType === ParseNodeType . TypeAnnotation && node . leftExpression . valueExpression . nodeType === ParseNodeType . Name && node . leftExpression . valueExpression . value === '__all__' ) ) { const expr = node . rightExpression ; this . _dunderAllNames = [ ] ; let emitDunderAllWarning = false ; if ( expr . nodeType === ParseNodeType . List ) { expr . entries . forEach ( ( listEntryNode ) => { if ( listEntryNode . nodeType === ParseNodeType . StringList && listEntryNode . strings . length === 1 && listEntryNode . strings [ 0 ] . nodeType === ParseNodeType . String ) { this . _dunderAllNames ! . push ( listEntryNode . strings [ 0 ] . value ) ; this . _dunderAllStringNodes . push ( listEntryNode . strings [ 0 ] ) ; } else { emitDunderAllWarning = true ; } } ) ; } else if ( expr . nodeType === ParseNodeType . Tuple ) { expr . expressions . forEach ( ( tupleEntryNode ) => { if ( tupleEntryNode . nodeType === ParseNodeType . StringList && tupleEntryNode . strings . length === 1 && tupleEntryNode . strings [ 0 ] . nodeType === ParseNodeType . String ) { this . _dunderAllNames ! . push ( tupleEntryNode . strings [ 0 ] . value ) ; this . _dunderAllStringNodes . push ( tupleEntryNode . strings [ 0 ] ) ; } else { emitDunderAllWarning = true ; } } ) ; } else { emitDunderAllWarning = true ; } if ( emitDunderAllWarning ) { this . _usesUnsupportedDunderAllForm = true ; this . _addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnsupportedDunderAll , DiagnosticRule . reportUnsupportedDunderAll , Localizer . Diagnostic . unsupportedDunderAllOperation ( ) , node ) ; } } } if ( this . _currentScope . type === ScopeType . Class ) { if ( ( node . leftExpression . nodeType === ParseNodeType . Name && node . leftExpression . value === '__slots__' ) || ( node . leftExpression . nodeType === ParseNodeType . TypeAnnotation && node . leftExpression . valueExpression . nodeType === ParseNodeType . Name && node . leftExpression . valueExpression . value === '__slots__' ) ) { const expr = node . rightExpression ; const dunderSlotsNames = [ ] ; let isExpressionUnderstood = true ; if ( expr . nodeType === ParseNodeType . StringList ) { dunderSlotsNames . push ( expr ) ; } else if ( expr . nodeType === ParseNodeType . List ) { expr . entries . forEach ( ( listEntryNode ) => { if ( listEntryNode . nodeType === ParseNodeType . StringList && listEntryNode . strings . length === 1 && listEntryNode . strings [ 0 ] . nodeType === ParseNodeType . String ) { dunderSlotsNames . push ( listEntryNode ) ; } else { isExpressionUnderstood = false ; } } ) ; } else if ( expr . nodeType === ParseNodeType . Tuple ) { expr . expressions . forEach ( ( tupleEntryNode ) => { if ( tupleEntryNode . nodeType === ParseNodeType . StringList && tupleEntryNode . strings . length === 1 && tupleEntryNode . strings [ 0 ] . nodeType === ParseNodeType . String ) { dunderSlotsNames . push ( tupleEntryNode ) ; } else { isExpressionUnderstood = false ; } } ) ; } else { isExpressionUnderstood = false ; } if ( isExpressionUnderstood ) { this . _addSlotsToCurrentScope ( dunderSlotsNames ) ; } } } return false ; } override visitAssignmentExpression ( node ) { this . _disableTrueFalseTargets ( ( ) => { this . walk ( node . rightExpression ) ; } ) ; const evaluationNode = ParseTreeUtils . getEvaluationNodeForAssignmentExpression ( node ) ; if ( ! evaluationNode ) { this . _addError ( Localizer . Diagnostic . assignmentExprContext ( ) , node ) ; this . walk ( node . name ) ; } else { const containerScope = AnalyzerNodeInfo . getScope ( evaluationNode ) ! ; let curScope = this . _currentScope ; while ( curScope && curScope !== containerScope ) { const localSymbol = curScope . lookUpSymbol ( node . name . value ) ; if ( localSymbol ) { this . _addError ( Localizer . Diagnostic . assignmentExprComprehension ( ) . format ( { name : node . name . value } ) , node . name ) ; break ; } curScope = curScope . parent ; } this . _bindNameToScope ( containerScope , node . name . value ) ; this . _addInferredTypeAssignmentForVariable ( node . name , node . rightExpression ) ; this . _createAssignmentTargetFlowNodes ( node . name , true , false ) ; } return false ; } override visitAugmentedAssignment ( node ) { this . walk ( node . leftExpression ) ; this . walk ( node . rightExpression ) ; this . _bindPossibleTupleNamedTarget ( node . destExpression ) ; this . _createAssignmentTargetFlowNodes ( node . destExpression , false , false ) ; if ( node . operator === OperatorType . AddEqual && this . _currentScope . type === ScopeType . Module && node . leftExpression . nodeType === ParseNodeType . Name && node . leftExpression . value === '__all__' ) { const expr = node . rightExpression ; let emitDunderAllWarning = true ; if ( expr . nodeType === ParseNodeType . List ) { expr . entries . forEach ( ( listEntryNode ) => { if ( listEntryNode . nodeType === ParseNodeType . StringList && listEntryNode . strings . length === 1 && listEntryNode . strings [ 0 ] . nodeType === ParseNodeType . String ) { this . _dunderAllNames ?. push ( listEntryNode . strings [ 0 ] . value ) ; this . _dunderAllStringNodes . push ( listEntryNode . strings [ 0 ] ) ; } } ) ; emitDunderAllWarning = false ; } else if ( expr . nodeType === ParseNodeType . MemberAccess && expr . leftExpression . nodeType === ParseNodeType . Name && expr . memberName . value === '__all__' ) { const namesToAdd = this . _getDunderAllNamesFromImport ( expr . leftExpression . value ) ; if ( namesToAdd ) { namesToAdd . forEach ( ( name ) => { this . _dunderAllNames ?. push ( name ) ; } ) ; emitDunderAllWarning = false ; } } if ( emitDunderAllWarning ) { this . _usesUnsupportedDunderAllForm = true ; this . _addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnsupportedDunderAll , DiagnosticRule . reportUnsupportedDunderAll , Localizer . Diagnostic . unsupportedDunderAllOperation ( ) , node ) ; } } return false ; } override visitDel ( node ) { node . expressions . forEach ( ( expr ) => { this . _bindPossibleTupleNamedTarget ( expr ) ; this . walk ( expr ) ; this . _createAssignmentTargetFlowNodes ( expr , false , true ) ; } ) ; return false ; } override visitTypeAnnotation ( node ) { if ( this . _handleTypingStubAssignmentOrAnnotation ( node ) ) { return false ; } this . walk ( node . typeAnnotation ) ; this . _createVariableAnnotationFlowNode ( ) ; this . _bindPossibleTupleNamedTarget ( node . valueExpression ) ; this . _addTypeDeclarationForVariable ( node . valueExpression , node . typeAnnotation ) ; const expressionList = [ ] ; if ( this . _isNarrowingExpression ( node . valueExpression , expressionList ) ) { expressionList . forEach ( ( expr ) => { const referenceKey = createKeyForReference ( expr ) ; this . _currentScopeCodeFlowExpressions ! . add ( referenceKey ) ; } ) ; } this . walk ( node . valueExpression ) ; return false ; } override visitFor ( node ) { this . _bindPossibleTupleNamedTarget ( node . targetExpression ) ; this . _addInferredTypeAssignmentForVariable ( node . targetExpression , node ) ; this . walk ( node . iterableExpression ) ; const preForLabel = this . _createLoopLabel ( ) ; const preElseLabel = this . _createBranchLabel ( ) ; const postForLabel = this . _createBranchLabel ( ) ; this . _addAntecedent ( preForLabel , this . _currentFlowNode ! ) ; this . _currentFlowNode = preForLabel ; this . _addAntecedent ( preElseLabel , this . _currentFlowNode ) ; this . _createAssignmentTargetFlowNodes ( node . targetExpression , true , false ) ; this . _bindLoopStatement ( preForLabel , postForLabel , ( ) => { this . walk ( node . forSuite ) ; this . _addAntecedent ( preForLabel , this . _currentFlowNode ! ) ; } ) ; this . _currentFlowNode = this . _finishFlowLabel ( preElseLabel ) ; if ( node . elseSuite ) { this . walk ( node . elseSuite ) ; } this . _addAntecedent ( postForLabel , this . _currentFlowNode ) ; this . _currentFlowNode = this . _finishFlowLabel ( postForLabel ) ; if ( node . asyncToken ) { const enclosingFunction = ParseTreeUtils . getEnclosingFunction ( node ) ; if ( ! enclosingFunction || ! enclosingFunction . isAsync ) { this . _addError ( Localizer . Diagnostic . asyncNotInAsyncFunction ( ) , node . asyncToken ) ; } } return false ; } override visitContinue ( node ) { if ( this . _currentContinueTarget ) { this . _addAntecedent ( this . _currentContinueTarget , this . _currentFlowNode ! ) ; } this . _currentFlowNode = Binder . _unreachableFlowNode ; return false ; } override visitBreak ( node ) { if ( this . _currentBreakTarget ) { this . _addAntecedent ( this . _currentBreakTarget , this . _currentFlowNode ! ) ; } this . _currentFlowNode = Binder . _unreachableFlowNode ; return false ; } override visitReturn ( node ) { if ( this . _targetFunctionDeclaration ) { if ( ! this . _targetFunctionDeclaration . returnStatements ) { this . _targetFunctionDeclaration . returnStatements = [ ] ; } this . _targetFunctionDeclaration . returnStatements . push ( node ) ; } if ( node . returnExpression ) { this . walk ( node . returnExpression ) ; } AnalyzerNodeInfo . setFlowNode ( node , this . _currentFlowNode ! ) ; if ( this . _currentReturnTarget ) { this . _addAntecedent ( this . _currentReturnTarget , this . _currentFlowNode ! ) ; } this . _finallyTargets . forEach ( ( target ) => { this . _addAntecedent ( target , this . _currentFlowNode ! ) ; } ) ; this . _currentFlowNode = Binder . _unreachableFlowNode ; return false ; } override visitYield ( node ) { if ( this . _isInListComprehension ( node ) ) { this . _addError ( Localizer . Diagnostic . yieldWithinListCompr ( ) , node ) ; } this . _bindYield ( node ) ; return false ; } override visitYieldFrom ( node ) { if ( this . _isInListComprehension ( node ) ) { this . _addError ( Localizer . Diagnostic . yieldWithinListCompr ( ) , node ) ; } this . _bindYield ( node ) ; return false ; } override visitMemberAccess ( node ) { AnalyzerNodeInfo . setFlowNode ( node , this . _currentFlowNode ! ) ; return true ; } override visitName ( node ) { AnalyzerNodeInfo . setFlowNode ( node , this . _currentFlowNode ! ) ; return false ; } override visitIndex ( node ) { AnalyzerNodeInfo . setFlowNode ( node , this . _currentFlowNode ! ) ; return true ; } override visitIf ( node ) { const preIfFlowNode = this . _currentFlowNode ! ; const thenLabel = this . _createBranchLabel ( ) ; const elseLabel = this . _createBranchLabel ( ) ; const postIfLabel = this . _createBranchLabel ( preIfFlowNode ) ; postIfLabel . affectedExpressions = this . _trackCodeFlowExpressions ( ( ) => { const constExprValue = StaticExpressions . evaluateStaticBoolLikeExpression ( node . testExpression , this . _fileInfo . executionEnvironment , this . _typingImportAliases , this . _sysImportAliases ) ; this . _bindConditional ( node . testExpression , thenLabel , elseLabel ) ; this . _currentFlowNode = constExprValue === false ? Binder . _unreachableFlowNode : this . _finishFlowLabel ( thenLabel ) ; this . walk ( node . ifSuite ) ; this . _addAntecedent ( postIfLabel , this . _currentFlowNode ) ; this . _currentFlowNode = constExprValue === true ? Binder . _unreachableFlowNode : this . _finishFlowLabel ( elseLabel ) ; if ( node . elseSuite ) { this . walk ( node . elseSuite ) ; } else { this . _bindNeverCondition ( node . testExpression , postIfLabel , false ) ; } this . _addAntecedent ( postIfLabel , this . _currentFlowNode ) ; this . _currentFlowNode = this . _finishFlowLabel ( postIfLabel ) ; } ) ; return false ; } override visitWhile ( node ) { const thenLabel = this . _createBranchLabel ( ) ; const elseLabel = this . _createBranchLabel ( ) ; const postWhileLabel = this . _createBranchLabel ( ) ; const constExprValue = StaticExpressions . evaluateStaticBoolLikeExpression ( node . testExpression , this . _fileInfo . executionEnvironment , this . _typingImportAliases , this . _sysImportAliases ) ; const preLoopLabel = this . _createLoopLabel ( ) ; this . _addAntecedent ( preLoopLabel , this . _currentFlowNode ! ) ; this . _currentFlowNode = preLoopLabel ; this . _bindConditional ( node . testExpression , thenLabel , elseLabel ) ; this . _currentFlowNode = constExprValue === false ? Binder . _unreachableFlowNode : this . _finishFlowLabel ( thenLabel ) ; this . _bindLoopStatement ( preLoopLabel , postWhileLabel , ( ) => { this . walk ( node . whileSuite ) ; } ) ; this . _addAntecedent ( preLoopLabel , this . _currentFlowNode ) ; this . _currentFlowNode = constExprValue === true ? Binder . _unreachableFlowNode : this . _finishFlowLabel ( elseLabel ) ; if ( node . elseSuite ) { this . walk ( node . elseSuite ) ; } this . _addAntecedent ( postWhileLabel , this . _currentFlowNode ) ; this . _currentFlowNode = this . _finishFlowLabel ( postWhileLabel ) ; return false ; } override visitAssert ( node ) { const assertTrueLabel = this . _createBranchLabel ( ) ; const assertFalseLabel = this . _createBranchLabel ( ) ; this . _bindConditional ( node . testExpression , assertTrueLabel , assertFalseLabel ) ; if ( node . exceptionExpression ) { this . _currentFlowNode = this . _finishFlowLabel ( assertFalseLabel ) ; this . walk ( node . exceptionExpression ) ; } this . _currentFlowNode = this . _finishFlowLabel ( assertTrueLabel ) ; return false ; } override visitExcept ( node ) { if ( node . typeExpression ) { this . walk ( node . typeExpression ) ; } if ( node . name ) { this . walk ( node . name ) ; const symbol = this . _bindNameToScope ( this . _currentScope , node . name . value ) ; this . _createAssignmentTargetFlowNodes ( node . name , true , false ) ; if ( symbol ) { const declaration = { type : DeclarationType . Variable , node : node . name , isConstant : isConstantName ( node . name . value ) , inferredTypeSource : node , path : this . _fileInfo . filePath , range : convertOffsetsToRange ( node . name . start , TextRange . getEnd ( node . name ) , this . _fileInfo . lines ) , moduleName : this . _fileInfo . moduleName , } ; symbol . addDeclaration ( declaration ) ; } } this . walk ( node . exceptSuite ) ; if ( node . name ) { this . _createFlowAssignment ( node . name , true ) ; } return false ; } override visitRaise ( node ) { if ( this . _targetFunctionDeclaration ) { if ( ! this . _targetFunctionDeclaration . raiseStatements ) { this . _targetFunctionDeclaration . raiseStatements = [ ] ; } this . _targetFunctionDeclaration . raiseStatements . push ( node ) ; } if ( node . typeExpression ) { this . walk ( node . typeExpression ) ; } if ( node . valueExpression ) { this . walk ( node . valueExpression ) ; } if ( node . tracebackExpression ) { this . walk ( node . tracebackExpression ) ; } this . _finallyTargets . forEach ( ( target ) => { this . _addAntecedent ( target , this . _currentFlowNode ! ) ; } ) ; this . _currentFlowNode = Binder . _unreachableFlowNode ; return false ; } override visitTry ( node ) { const preTryFlowNode = this . _currentFlowNode ! ; const curExceptTargets = node . exceptClauses . map ( ( ) => this . _createBranchLabel ( ) ) ; const preFinallyLabel = this . _createBranchLabel ( preTryFlowNode ) ; let isAfterElseAndExceptsReachable = false ; const preFinallyReturnOrRaiseLabel = this . _createBranchLabel ( preTryFlowNode ) ; const preFinallyGate = { flags : FlowFlags . PreFinallyGate , id : getUniqueFlowNodeId ( ) , antecedent : preFinallyReturnOrRaiseLabel , isGateClosed : false , } ; preFinallyLabel . affectedExpressions = this . _trackCodeFlowExpressions ( ( ) => { if ( node . finallySuite ) { this . _addAntecedent ( preFinallyLabel , preFinallyGate ) ; } const hasBareExceptClause = node . exceptClauses . some ( ( except ) => ! except . typeExpression ) ; if ( ! hasBareExceptClause ) { curExceptTargets . push ( preFinallyReturnOrRaiseLabel ) ; } curExceptTargets . forEach ( ( exceptLabel ) => { this . _addAntecedent ( exceptLabel , this . _currentFlowNode ! ) ; } ) ; if ( node . finallySuite ) { this . _finallyTargets . push ( preFinallyReturnOrRaiseLabel ) ; } this . _useExceptTargets ( curExceptTargets , ( ) => { this . walk ( node . trySuite ) ; } ) ; if ( node . elseSuite ) { this . walk ( node . elseSuite ) ; } this . _addAntecedent ( preFinallyLabel , this . _currentFlowNode ! ) ; if ( ! this . _isCodeUnreachable ( ) ) { isAfterElseAndExceptsReachable = true ; } node . exceptClauses . forEach ( ( exceptNode , index ) => { this . _currentFlowNode = this . _finishFlowLabel ( curExceptTargets [ index ] ) ; this . walk ( exceptNode ) ; this . _addAntecedent ( preFinallyLabel , this . _currentFlowNode ) ; if ( ! this . _isCodeUnreachable ( ) ) { isAfterElseAndExceptsReachable = true ; } } ) ; if ( node . finallySuite ) { this . _finallyTargets . pop ( ) ; } this . _currentFlowNode = this . _finishFlowLabel ( preFinallyLabel ) ; } ) ; if ( node . finallySuite ) { this . walk ( node . finallySuite ) ; const postFinallyNode = { flags : FlowFlags . PostFinally , id : getUniqueFlowNodeId ( ) , finallyNode : node . finallySuite , antecedent : this . _currentFlowNode ! , preFinallyGate , } ; this . _currentFlowNode = isAfterElseAndExceptsReachable ? postFinallyNode : Binder . _unreachableFlowNode ; } this . _functionCodeFlowComplexity += 4 ; return false ; } override visitAwait ( node ) { const enclosingFunction = ParseTreeUtils . getEnclosingFunction ( node ) ; if ( enclosingFunction === undefined || ! enclosingFunction . isAsync ) { if ( node . parent ?. nodeType !== ParseNodeType . ListComprehension ) { this . _addError ( Localizer . Diagnostic . awaitNotInAsync ( ) , node ) ; } } return true ; } override visitGlobal ( node ) { const globalScope = this . _currentScope . getGlobalScope ( ) ; node . nameList . forEach ( ( name ) => { const nameValue = name . value ; if ( this . _currentScope . getBindingType ( nameValue ) === NameBindingType . Nonlocal ) { this . _addError ( Localizer . Diagnostic . nonLocalRedefinition ( ) . format ( { name : nameValue } ) , name ) ; } const valueWithScope = this . _currentScope . lookUpSymbolRecursive ( nameValue ) ; if ( valueWithScope && valueWithScope . scope === this . _currentScope ) { this . _addError ( Localizer . Diagnostic . globalReassignment ( ) . format ( { name : nameValue } ) , name ) ; } this . _bindNameToScope ( globalScope , nameValue ) ; if ( this . _currentScope !== globalScope ) { this . _currentScope . setBindingType ( nameValue , NameBindingType . Global ) ; } } ) ; return true ; } override visitNonlocal ( node ) { const globalScope = this . _currentScope . getGlobalScope ( ) ; if ( this . _currentScope === globalScope ) { this . _addError ( Localizer . Diagnostic . nonLocalInModule ( ) , node ) ; } else { node . nameList . forEach ( ( name ) => { const nameValue = name . value ; if ( this . _currentScope . getBindingType ( nameValue ) === NameBindingType . Global ) { this . _addError ( Localizer . Diagnostic . globalRedefinition ( ) . format ( { name : nameValue } ) , name ) ; } const valueWithScope = this . _currentScope . lookUpSymbolRecursive ( nameValue ) ; if ( valueWithScope && valueWithScope . scope === this . _currentScope ) { this . _addError ( Localizer . Diagnostic . nonLocalReassignment ( ) . format ( { name : nameValue } ) , name ) ; } else if ( ! valueWithScope || valueWithScope . scope === globalScope ) { this . _addError ( Localizer . Diagnostic . nonLocalNoBinding ( ) . format ( { name : nameValue } ) , name ) ; } if ( valueWithScope ) { this . _currentScope . setBindingType ( nameValue , NameBindingType . Nonlocal ) ; } } ) ; } return true ; } override visitImportAs ( node ) { if ( node . module . nameParts . length > 0 ) { const firstNamePartValue = node . module . nameParts [ 0 ] . value ; let symbolName ; if ( node . alias ) { symbolName = node . alias . value ; } else { symbolName = firstNamePartValue ; } const symbol = this . _bindNameToScope ( this . _currentScope , symbolName ) ; if ( symbol && ( this . _currentScope . type === ScopeType . Module || this . _currentScope . type === ScopeType . Builtin ) && ( ! node . alias || node . module . nameParts . length !== 1 || node . module . nameParts [ 0 ] . value !== node . alias . value ) ) { if ( this . _fileInfo . isStubFile || this . _fileInfo . isInPyTypedPackage ) { this . _potentialHiddenSymbols . set ( symbolName , symbol ) ; } } const importInfo = AnalyzerNodeInfo . getImportInfo ( node . module ) ; assert ( importInfo !== undefined ) ; if ( symbol ) { this . _createAliasDeclarationForMultipartImportName ( node , node . alias , importInfo , symbol ) ; } this . _createFlowAssignment ( node . alias ? node . alias : node . module . nameParts [ 0 ] ) ; if ( node . module . nameParts . length === 1 ) { if ( firstNamePartValue === 'typing' || firstNamePartValue === 'typing_extensions' ) { this . _typingImportAliases . push ( node . alias ?. value ?? firstNamePartValue ) ; } else if ( firstNamePartValue === 'sys' ) { this . _sysImportAliases . push ( node . alias ?. value ?? firstNamePartValue ) ; } else if ( firstNamePartValue === 'dataclasses' ) { this . _dataclassesImportAliases . push ( node . alias ?. value ?? firstNamePartValue ) ; } } } return true ; } override visitImportFrom ( node ) { const typingSymbolsOfInterest = [ 'Final' , 'TypeAlias' , 'ClassVar' , 'Required' , 'NotRequired' ] ; const dataclassesSymbolsOfInterest = [ 'InitVar' ] ; const importInfo = AnalyzerNodeInfo . getImportInfo ( node . module ) ; let resolvedPath = '' ; if ( importInfo && importInfo . isImportFound && ! importInfo . isNativeLib ) { resolvedPath = importInfo . resolvedPaths [ importInfo . resolvedPaths . length - 1 ] ; } const fileName = stripFileExtension ( getFileName ( this . _fileInfo . filePath ) ) ; const isModuleInitFile = fileName === '__init__' && node . module . leadingDots === 1 && node . module . nameParts . length === 1 ; let isTypingImport = false ; let isDataclassesImport = false ; if ( node . module . nameParts . length === 1 ) { const firstNamePartValue = node . module . nameParts [ 0 ] . value ; if ( firstNamePartValue === 'typing' || firstNamePartValue === 'typing_extensions' ) { isTypingImport = true ; } if ( firstNamePartValue === 'dataclasses' ) { isDataclassesImport = true ; } } if ( node . isWildcardImport ) { if ( ParseTreeUtils . getEnclosingClass ( node ) || ParseTreeUtils . getEnclosingFunction ( node ) ) { this . _addError ( Localizer . Diagnostic . wildcardInFunction ( ) , node ) ; } if ( importInfo ) { const names = [ ] ; const lookupInfo = this . _fileInfo . importLookup ( resolvedPath ) ; if ( lookupInfo ) { const wildcardNames = this . _getWildcardImportNames ( lookupInfo ) ; if ( isModuleInitFile ) { const isImmediatelyReplaced = wildcardNames . some ( ( name ) => { return name === node . module . nameParts [ 0 ] . value ; } ) ; if ( ! isImmediatelyReplaced ) { this . _addImplicitFromImport ( node , importInfo ) ; } } wildcardNames . forEach ( ( name ) => { const localSymbol = this . _bindNameToScope ( this . _currentScope , name ) ; if ( localSymbol ) { const importedSymbol = lookupInfo . symbolTable . get ( name ) ! ; if ( importedSymbol ) { const aliasDecl = { type : DeclarationType . Alias , node , path : resolvedPath , loadSymbolsFromPath : true , range : getEmptyRange ( ) , usesLocalName : false , symbolName : name , moduleName : this . _fileInfo . moduleName , } ; localSymbol . addDeclaration ( aliasDecl ) ; names . push ( name ) ; } else { if ( importInfo && importInfo . filteredImplicitImports ) { const implicitImport = importInfo . filteredImplicitImports . find ( ( imp ) => imp . name === name ) ; if ( implicitImport ) { const submoduleFallback = { type : DeclarationType . Alias , node , path : implicitImport . path , loadSymbolsFromPath : true , range : getEmptyRange ( ) , usesLocalName : false , moduleName : this . _fileInfo . moduleName , } ; const aliasDecl = { type : DeclarationType . Alias , node , path : resolvedPath , loadSymbolsFromPath : true , usesLocalName : false , symbolName : name , submoduleFallback , range : getEmptyRange ( ) , moduleName : this . _fileInfo . moduleName , } ; localSymbol . addDeclaration ( aliasDecl ) ; } } } } } ) ; } this . _createFlowWildcardImport ( node , names ) ; if ( isTypingImport ) { typingSymbolsOfInterest . forEach ( ( s ) => { this . _typingSymbolAliases . set ( s , s ) ; } ) ; } if ( isDataclassesImport ) { dataclassesSymbolsOfInterest . forEach ( ( s ) => { this . _dataclassesSymbolAliases . set ( s , s ) ; } ) ; } } } else { if ( isModuleInitFile ) { this . _addImplicitFromImport ( node , importInfo ) ; } node . imports . forEach ( ( importSymbolNode ) => { const importedName = importSymbolNode . name . value ; const nameNode = importSymbolNode . alias || importSymbolNode . name ; const symbol = this . _bindNameToScope ( this . _currentScope , nameNode . value ) ; if ( symbol ) { if ( node . module . nameParts . length > 0 ) { if ( this . _currentScope . type === ScopeType . Module || this . _currentScope . type === ScopeType . Builtin ) { if ( ! importSymbolNode . alias || importSymbolNode . alias . value !== importSymbolNode . name . value ) { if ( this . _fileInfo . isStubFile || this . _fileInfo . isInPyTypedPackage ) { this . _potentialHiddenSymbols . set ( nameNode . value , symbol ) ; } } } } let implicitImport ; if ( importInfo && importInfo . filteredImplicitImports ) { implicitImport = importInfo . filteredImplicitImports . find ( ( imp ) => imp . name === importedName ) ; } let submoduleFallback ; let loadSymbolsFromPath = true ; if ( implicitImport ) { submoduleFallback = { type : DeclarationType . Alias , node : importSymbolNode , path : implicitImport . path , loadSymbolsFromPath : true , range : getEmptyRange ( ) , usesLocalName : false , moduleName : this . _fileInfo . moduleName , } ; if ( fileName === '__init__' && node . module . leadingDots === 1 && node . module . nameParts . length === 0 ) { loadSymbolsFromPath = false ; } } const aliasDecl = { type : DeclarationType . Alias , node : importSymbolNode , path : resolvedPath , loadSymbolsFromPath , usesLocalName : ! ! importSymbolNode . alias , symbolName : importedName , submoduleFallback , range : getEmptyRange ( ) , moduleName : this . _fileInfo . moduleName , isNativeLib : importInfo ?. isNativeLib , } ; symbol . addDeclaration ( aliasDecl ) ; this . _createFlowAssignment ( importSymbolNode . alias || importSymbolNode . name ) ; if ( isTypingImport ) { if ( typingSymbolsOfInterest . some ( ( s ) => s === importSymbolNode . name . value ) ) { this . _typingSymbolAliases . set ( nameNode . value , importSymbolNode . name . value ) ; } } if ( isDataclassesImport ) { if ( dataclassesSymbolsOfInterest . some ( ( s ) => s === importSymbolNode . name . value ) ) { this . _dataclassesSymbolAliases . set ( nameNode . value , importSymbolNode . name . value ) ; } } } } ) ; } return true ; } override visitWith ( node ) { node . withItems . forEach ( ( item ) => { this . walk ( item . expression ) ; if ( item . target ) { this . _bindPossibleTupleNamedTarget ( item . target ) ; this . _addInferredTypeAssignmentForVariable ( item . target , item ) ; this . _createAssignmentTargetFlowNodes ( item . target , true , false ) ; } } ) ; const contextManagerExceptionTarget = this . _createContextManagerLabel ( node . withItems . map ( ( item ) => item . expression ) , ! ! node . isAsync ) ; this . _addAntecedent ( contextManagerExceptionTarget , this . _currentFlowNode ! ) ; const preWithSuiteNode = this . _currentFlowNode ! ; const postContextManagerLabel = this . _createBranchLabel ( preWithSuiteNode ) ; this . _addAntecedent ( postContextManagerLabel , contextManagerExceptionTarget ! ) ; postContextManagerLabel . affectedExpressions = this . _trackCodeFlowExpressions ( ( ) => { this . _useExceptTargets ( [ contextManagerExceptionTarget ] , ( ) => { this . walk ( node . suite ) ; } ) ; this . _addAntecedent ( postContextManagerLabel , this . _currentFlowNode ! ) ; this . _currentFlowNode = postContextManagerLabel ; if ( ! this . _isCodeUnreachable ( ) ) { this . _addExceptTargets ( this . _currentFlowNode ! ) ; } if ( node . asyncToken ) { const enclosingFunction = ParseTreeUtils . getEnclosingFunction ( node ) ; if ( ! enclosingFunction || ! enclosingFunction . isAsync ) { this . _addError ( Localizer . Diagnostic . asyncNotInAsyncFunction ( ) , node . asyncToken ) ; } } } ) ; return false ; } override visitTernary ( node ) { const preTernaryFlowNode = this . _currentFlowNode ! ; const trueLabel = this . _createBranchLabel ( ) ; const falseLabel = this . _createBranchLabel ( ) ; const postExpressionLabel = this . _createBranchLabel ( preTernaryFlowNode ) ; postExpressionLabel . affectedExpressions = this . _trackCodeFlowExpressions ( ( ) => { this . _bindConditional ( node . testExpression , trueLabel , falseLabel ) ; this . _currentFlowNode = this . _finishFlowLabel ( trueLabel ) ; this . walk ( node . ifExpression ) ; this . _addAntecedent ( postExpressionLabel , this . _currentFlowNode ) ; this . _currentFlowNode = this . _finishFlowLabel ( falseLabel ) ; this . walk ( node . elseExpression ) ; this . _addAntecedent ( postExpressionLabel , this . _currentFlowNode ) ; this . _currentFlowNode = this . _finishFlowLabel ( postExpressionLabel ) ; } ) ; return false ; } override visitUnaryOperation ( node ) { if ( node . operator === OperatorType . Not && this . _currentFalseTarget && this . _currentTrueTarget ) { this . _bindConditional ( node . expression , this . _currentFalseTarget , this . _currentTrueTarget ) ; } else { this . _disableTrueFalseTargets ( ( ) => { this . walk ( node . expression ) ; } ) ; } return false ; } override visitBinaryOperation ( node ) { if ( node . operator === OperatorType . And || node . operator === OperatorType . Or ) { let trueTarget = this . _currentTrueTarget ; let falseTarget = this . _currentFalseTarget ; let postRightLabel ; if ( ! trueTarget || ! falseTarget ) { postRightLabel = this . _createBranchLabel ( ) ; trueTarget = falseTarget = postRightLabel ; } const preRightLabel = this . _createBranchLabel ( ) ; if ( node . operator === OperatorType . And ) { this . _bindConditional ( node . leftExpression , preRightLabel , falseTarget ) ; } else { this . _bindConditional ( node . leftExpression , trueTarget , preRightLabel ) ; } this . _currentFlowNode = this . _finishFlowLabel ( preRightLabel ) ; this . _bindConditional ( node . rightExpression , trueTarget , falseTarget ) ; if ( postRightLabel ) { this . _currentFlowNode = this . _finishFlowLabel ( postRightLabel ) ; } } else { this . _disableTrueFalseTargets ( ( ) => { this . walk ( node . leftExpression ) ; this . walk ( node . rightExpression ) ; } ) ; } return false ; } override visitListComprehension ( node ) { const enclosingFunction = ParseTreeUtils . getEnclosingFunction ( node ) ; this . _createNewScope ( ScopeType . ListComprehension , this . _currentScope , ( ) => { AnalyzerNodeInfo . setScope ( node , this . _currentScope ) ; const falseLabel = this . _createBranchLabel ( ) ; const boundSymbols = [ ] ; for ( let i = 0 ; i < node . comprehensions . length ; i ++ ) { const compr = node . comprehensions [ i ] ; const addedSymbols = new Map < string , Symbol > ( ) ; if ( compr . nodeType === ParseNodeType . ListComprehensionFor ) { this . _bindPossibleTupleNamedTarget ( compr . targetExpression , addedSymbols ) ; this . _addInferredTypeAssignmentForVariable ( compr . targetExpression , compr ) ; if ( compr . asyncToken ) { if ( ! enclosingFunction || ! enclosingFunction . isAsync ) { if ( node . parent ?. nodeType === ParseNodeType . List ) { this . _addError ( Localizer . Diagnostic . asyncNotInAsyncFunction ( ) , compr . asyncToken ) ; } } } } boundSymbols . push ( addedSymbols ) ; } for ( let i = 0 ; i < node . comprehensions . length ; i ++ ) { const compr = node . comprehensions [ i ] ; if ( compr . nodeType === ParseNodeType . ListComprehensionFor ) { const addedSymbols = boundSymbols [ i ] ; for ( const addedSymbol of addedSymbols ) { const aliasSymbol = this . _currentScope . parent ! . lookUpSymbol ( addedSymbol [ 0 ] ) ; if ( aliasSymbol ) { this . _createAssignmentAliasFlowNode ( addedSymbol [ 1 ] . id , aliasSymbol . id ) ; } } this . walk ( compr . iterableExpression ) ; this . _createAssignmentTargetFlowNodes ( compr . targetExpression , true , false ) ; } else { const trueLabel = this . _createBranchLabel ( ) ; this . _bindConditional ( compr . testExpression , trueLabel , falseLabel ) ; this . _currentFlowNode = this . _finishFlowLabel ( trueLabel ) ; } } this . walk ( node . expression ) ; this . _addAntecedent ( falseLabel , this . _currentFlowNode ! ) ; this . _currentFlowNode = this . _finishFlowLabel ( falseLabel ) ; } ) ; return false ; } override visitMatch ( node ) { this . walk ( node . subjectExpression ) ; const expressionList = [ ] ; const isSubjectNarrowable = this . _isNarrowingExpression ( node . subjectExpression , expressionList ) ; if ( isSubjectNarrowable ) { expressionList . forEach ( ( expr ) => { const referenceKey = createKeyForReference ( expr ) ; this . _currentScopeCodeFlowExpressions ! . add ( referenceKey ) ; } ) ; } const postMatchLabel = this . _createBranchLabel ( ) ; let foundIrrefutableCase = false ; node . cases . forEach ( ( caseStatement ) => { const postCaseLabel = this . _createBranchLabel ( ) ; const preGuardLabel = this . _createBranchLabel ( ) ; const preSuiteLabel = this . _createBranchLabel ( ) ; this . _addAntecedent ( preGuardLabel , this . _currentFlowNode ! ) ; if ( ! caseStatement . isIrrefutable ) { this . _addAntecedent ( postCaseLabel , this . _currentFlowNode ! ) ; } else if ( ! caseStatement . guardExpression ) { foundIrrefutableCase = true ; } this . _currentFlowNode = this . _finishFlowLabel ( preGuardLabel ) ; this . walk ( caseStatement . pattern ) ; if ( isSubjectNarrowable ) { this . _createFlowNarrowForPattern ( node . subjectExpression , caseStatement ) ; } if ( caseStatement . guardExpression ) { this . _bindConditional ( caseStatement . guardExpression , preSuiteLabel , postCaseLabel ) ; } else { this . _addAntecedent ( preSuiteLabel , this . _currentFlowNode ) ; } this . _currentFlowNode = this . _finishFlowLabel ( preSuiteLabel ) ; this . walk ( caseStatement . suite ) ; this . _addAntecedent ( postMatchLabel , this . _currentFlowNode ) ; this . _currentFlowNode = this . _finishFlowLabel ( postCaseLabel ) ; } ) ; if ( isSubjectNarrowable ) { this . _createFlowNarrowForPattern ( node . subjectExpression , node ) ; if ( ! foundIrrefutableCase ) { this . _createFlowExhaustedMatch ( node ) ; } } this . _addAntecedent ( postMatchLabel , this . _currentFlowNode ! ) ; this . _currentFlowNode = this . _finishFlowLabel ( postMatchLabel ) ; return false ; } override visitPatternAs ( node ) { const postOrLabel = this . _createBranchLabel ( ) ; node . orPatterns . forEach ( ( orPattern ) => { this . walk ( orPattern ) ; this . _addAntecedent ( postOrLabel , this . _currentFlowNode ! ) ; } ) ; this . _currentFlowNode = this . _finishFlowLabel ( postOrLabel ) ; if ( node . target ) { this . walk ( node . target ) ; const symbol = this . _bindNameToScope ( this . _currentScope , node . target . value ) ; this . _createAssignmentTargetFlowNodes ( node . target , false , false ) ; if ( symbol ) { const declaration = { type : DeclarationType . Variable , node : node . target , isConstant : isConstantName ( node . target . value ) , inferredTypeSource : node , path : this . _fileInfo . filePath , range : convertOffsetsToRange ( node . target . start , TextRange . getEnd ( node . target ) , this . _fileInfo . lines ) , moduleName : this . _fileInfo . moduleName , } ; symbol . addDeclaration ( declaration ) ; } } return false ; } override visitPatternCapture ( node ) { if ( ! node . isWildcard ) { this . _addPatternCaptureTarget ( node . target ) ; } return true ; } override visitPatternMappingExpandEntry ( node ) { if ( node . target . value !== '_' ) { this . _addPatternCaptureTarget ( node . target ) ; } return true ; } private _addSlotsToCurrentScope ( slotNameNodes ) { assert ( this . _currentScope . type === ScopeType . Class ) ; let slotsContainsDict = false ; for ( const slotNameNode of slotNameNodes ) { const slotName = slotNameNode . strings [ 0 ] . value ; if ( slotName === '__dict__' ) { slotsContainsDict = true ; continue ; } let symbol = this . _currentScope . lookUpSymbol ( slotName ) ; if ( ! symbol ) { symbol = this . _currentScope . addSymbol ( slotName , SymbolFlags . InitiallyUnbound | SymbolFlags . InstanceMember ) ; const honorPrivateNaming = this . _fileInfo . diagnosticRuleSet . reportPrivateUsage !== 'none' ; if ( isPrivateOrProtectedName ( slotName ) && honorPrivateNaming ) { symbol . setIsPrivateMember ( ) ; } } const declaration = { type : DeclarationType . Variable , node : slotNameNode , isConstant : isConstantName ( slotName ) , isDefinedBySlots : true , path : this . _fileInfo . filePath , range : convertOffsetsToRange ( slotNameNode . start , slotNameNode . start + slotNameNode . length , this . _fileInfo . lines ) , moduleName : this . _fileInfo . moduleName , } ; symbol . addDeclaration ( declaration ) ; } if ( ! slotsContainsDict ) { this . _currentScope . setSlotsNames ( slotNameNodes . map ( ( node ) => node . strings [ 0 ] . value ) ) ; } } private _isInListComprehension ( node ) { let curNode = node ; while ( curNode ) { if ( curNode . nodeType === ParseNodeType . ListComprehension ) { return true ; } curNode = curNode . parent ; } return false ; } private _addPatternCaptureTarget ( target ) { const symbol = this . _bindNameToScope ( this . _currentScope , target . value ) ; this . _createAssignmentTargetFlowNodes ( target , false , false ) ; if ( symbol ) { const declaration = { type : DeclarationType . Variable , node : target , isConstant : isConstantName ( target . value ) , inferredTypeSource : target . parent , path : this . _fileInfo . filePath , range : convertOffsetsToRange ( target . start , TextRange . getEnd ( target ) , this . _fileInfo . lines ) , moduleName : this . _fileInfo . moduleName , } ; symbol . addDeclaration ( declaration ) ; } } private _useExceptTargets ( targets , callback ) { const prevExceptTargets = this . _currentExceptTargets ; this . _currentExceptTargets = targets ; callback ( ) ; this . _currentExceptTargets = prevExceptTargets ; } private _getDunderAllNamesFromImport ( varName ) { const varSymbol = this . _currentScope . lookUpSymbol ( varName ) ; if ( ! varSymbol ) { return undefined ; } const aliasDecl = varSymbol . getDeclarations ( ) . find ( ( decl ) => decl . type === DeclarationType . Alias ) as AliasDeclaration | undefined ; const resolvedPath = aliasDecl ?. path && aliasDecl . loadSymbolsFromPath ? aliasDecl . path : aliasDecl ?. submoduleFallback ?. path && aliasDecl . submoduleFallback . loadSymbolsFromPath ? aliasDecl . submoduleFallback . path : undefined ; if ( ! resolvedPath ) { return undefined ; } const lookupInfo = this . _fileInfo . importLookup ( resolvedPath ) ; if ( ! lookupInfo ) { return undefined ; } return lookupInfo . dunderAllNames ; } private _addImplicitFromImport ( node , importInfo ? ) { const symbolName = node . module . nameParts [ 0 ] . value ; const symbol = this . _bindNameToScope ( this . _currentScope , symbolName ) ; if ( symbol ) { this . _createAliasDeclarationForMultipartImportName ( node , undefined , importInfo , symbol ) ; } this . _createFlowAssignment ( node . module . nameParts [ 0 ] ) ; } private _createAliasDeclarationForMultipartImportName ( node , importAlias , importInfo , symbol ) { const firstNamePartValue = node . module . nameParts [ 0 ] . value ; if ( importInfo && importInfo . isImportFound && ! importInfo . isNativeLib && importInfo . resolvedPaths . length > 0 ) { const existingDecl = symbol . getDeclarations ( ) . find ( ( decl ) => decl . type === DeclarationType . Alias && decl . firstNamePart === firstNamePartValue ) ; let newDecl ; if ( existingDecl ) { newDecl = existingDecl as AliasDeclaration ; } else { newDecl = { type : DeclarationType . Alias , node , path : importInfo . resolvedPaths [ importInfo . resolvedPaths . length - 1 ] , loadSymbolsFromPath : false , moduleName : importInfo . importName , range : getEmptyRange ( ) , firstNamePart : firstNamePartValue , usesLocalName : ! ! importAlias , } ; } if ( importAlias || node . module . nameParts . length === 1 ) { newDecl . path = importInfo . resolvedPaths [ importInfo . resolvedPaths . length - 1 ] ; newDecl . loadSymbolsFromPath = true ; this . _addImplicitImportsToLoaderActions ( importInfo , newDecl ) ; } else { let curLoaderActions = newDecl ; for ( let i = 1 ; i < node . module . nameParts . length ; i ++ ) { if ( i >= importInfo . resolvedPaths . length ) { break ; } const namePartValue = node . module . nameParts [ i ] . value ; let loaderActions = curLoaderActions . implicitImports ? curLoaderActions . implicitImports . get ( namePartValue ) : undefined ; if ( ! loaderActions ) { loaderActions = { path : importInfo . resolvedPaths [ i ] , loadSymbolsFromPath : false , implicitImports : new Map < string , ModuleLoaderActions > ( ) , } ; if ( ! curLoaderActions . implicitImports ) { curLoaderActions . implicitImports = new Map < string , ModuleLoaderActions > ( ) ; } curLoaderActions . implicitImports . set ( namePartValue , loaderActions ) ; } if ( i === node . module . nameParts . length - 1 ) { loaderActions . path = importInfo . resolvedPaths [ i ] ; loaderActions . loadSymbolsFromPath = true ; this . _addImplicitImportsToLoaderActions ( importInfo , loaderActions ) ; } curLoaderActions = loaderActions ; } } if ( ! existingDecl ) { symbol . addDeclaration ( newDecl ) ; } } else { const newDecl = { type : DeclarationType . Alias , node , path : '*** unresolved ***' , loadSymbolsFromPath : true , range : getEmptyRange ( ) , usesLocalName : ! ! importAlias , moduleName : '' , isUnresolved : true , } ; symbol . addDeclaration ( newDecl ) ; } } private _getWildcardImportNames ( lookupInfo ) { const namesToImport = [ ] ; if ( lookupInfo . dunderAllNames ) { if ( ! lookupInfo . usesUnsupportedDunderAllForm ) { return lookupInfo . dunderAllNames ; } namesToImport . push ( ... lookupInfo . dunderAllNames ) ; } lookupInfo . symbolTable . forEach ( ( symbol , name ) => { if ( ! symbol . isExternallyHidden ( ) ) { namesToImport ! . push ( name ) ; } } ) ; return namesToImport ; } private _walkStatementsAndReportUnreachable ( statements ) { let foundUnreachableStatement = false ; for ( const statement of statements ) { AnalyzerNodeInfo . setFlowNode ( statement , this . _currentFlowNode ! ) ; if ( ! foundUnreachableStatement ) { foundUnreachableStatement = this . _isCodeUnreachable ( ) ; } if ( ! foundUnreachableStatement ) { this . walk ( statement ) ; } else { if ( this . _targetFunctionDeclaration && ! this . _targetFunctionDeclaration . isGenerator ) { const yieldFinder = new YieldFinder ( ) ; if ( yieldFinder . checkContainsYield ( statement ) ) { this . _targetFunctionDeclaration . isGenerator = true ; } } } } return false ; } private _createStartFlowNode ( ) { const flowNode = { flags : FlowFlags . Start , id : getUniqueFlowNodeId ( ) , } ; return flowNode ; } private _createBranchLabel ( preBranchAntecedent ? ) { const flowNode = { flags : FlowFlags . BranchLabel , id : getUniqueFlowNodeId ( ) , antecedents : [ ] , preBranchAntecedent , affectedExpressions : undefined , } ; return flowNode ; } private _createFlowNarrowForPattern ( subjectExpression , statement ) { const flowNode = { flags : FlowFlags . NarrowForPattern , id : getUniqueFlowNodeId ( ) , subjectExpression , statement , antecedent : this . _currentFlowNode ! , } ; this . _currentFlowNode ! = flowNode ; } private _createContextManagerLabel ( expressions , isAsync ) { const flowNode = { flags : FlowFlags . PostContextManager | FlowFlags . BranchLabel , id : getUniqueFlowNodeId ( ) , antecedents : [ ] , expressions , affectedExpressions : undefined , isAsync , } ; return flowNode ; } private _createLoopLabel ( ) { const flowNode = { flags : FlowFlags . LoopLabel , id : getUniqueFlowNodeId ( ) , antecedents : [ ] , affectedExpressions : undefined , } ; return flowNode ; } private _finishFlowLabel ( node ) { if ( node . antecedents . length === 0 ) { return Binder . _unreachableFlowNode ; } if ( node . antecedents . length === 1 && node . flags === FlowFlags . BranchLabel ) { return node . antecedents [ 0 ] ; } this . _functionCodeFlowComplexity += node . antecedents . length ; return node ; } private _bindNeverCondition ( node , target , isPositiveTest ) { const expressionList = [ ] ; if ( node . nodeType === ParseNodeType . UnaryOperation && node . operator === OperatorType . Not ) { this . _bindNeverCondition ( node . expression , target , ! isPositiveTest ) ; } else if ( node . nodeType === ParseNodeType . BinaryOperation && ( node . operator === OperatorType . And || node . operator === OperatorType . Or ) ) { if ( node . operator === OperatorType . And ) { const savedCurrentFlowNode = this . _currentFlowNode ; this . _bindNeverCondition ( node . leftExpression , target , isPositiveTest ) ; this . _currentFlowNode = savedCurrentFlowNode ; this . _bindNeverCondition ( node . rightExpression , target , isPositiveTest ) ; } else { const initialCurrentFlowNode = this . _currentFlowNode ; const afterLabel = this . _createBranchLabel ( ) ; this . _bindNeverCondition ( node . leftExpression , afterLabel , isPositiveTest ) ; if ( initialCurrentFlowNode !== this . _currentFlowNode ) { this . _currentFlowNode = this . _finishFlowLabel ( afterLabel ) ; const prevCurrentNode = this . _currentFlowNode ; this . _bindNeverCondition ( node . rightExpression , target , isPositiveTest ) ; if ( prevCurrentNode === this . _currentFlowNode ) { this . _currentFlowNode = initialCurrentFlowNode ; } } } } else { if ( this . _isNarrowingExpression ( node , expressionList , true ) ) { const filteredExprList = expressionList . filter ( ( expr ) => expr . nodeType === ParseNodeType . Name ) ; if ( filteredExprList . length > 0 ) { this . _currentFlowNode = this . _createFlowConditional ( isPositiveTest ? FlowFlags . TrueNeverCondition : FlowFlags . FalseNeverCondition , this . _currentFlowNode ! , node ) ; } } this . _addAntecedent ( target , this . _currentFlowNode ! ) ; } } private _bindConditional ( node , trueTarget , falseTarget ) { this . _setTrueFalseTargets ( trueTarget , falseTarget , ( ) => { this . walk ( node ) ; } ) ; if ( ! this . _isLogicalExpression ( node ) ) { this . _addAntecedent ( trueTarget , this . _createFlowConditional ( FlowFlags . TrueCondition , this . _currentFlowNode ! , node ) ) ; this . _addAntecedent ( falseTarget , this . _createFlowConditional ( FlowFlags . FalseCondition , this . _currentFlowNode ! , node ) ) ; } } private _disableTrueFalseTargets ( callback ) { this . _setTrueFalseTargets ( undefined , undefined , callback ) ; } private _setTrueFalseTargets ( trueTarget , falseTarget , callback ) { const savedTrueTarget = this . _currentTrueTarget ; const savedFalseTarget = this . _currentFalseTarget ; this . _currentTrueTarget = trueTarget ; this . _currentFalseTarget = falseTarget ; callback ( ) ; this . _currentTrueTarget = savedTrueTarget ; this . _currentFalseTarget = savedFalseTarget ; } private _createFlowConditional ( flags , antecedent , expression ) { if ( antecedent . flags & FlowFlags . Unreachable ) { return antecedent ; } const staticValue = StaticExpressions . evaluateStaticBoolLikeExpression ( expression , this . _fileInfo . executionEnvironment , this . _typingImportAliases , this . _sysImportAliases ) ; if ( ( staticValue === true && flags & FlowFlags . FalseCondition ) || ( staticValue === false && flags & FlowFlags . TrueCondition ) ) { return Binder . _unreachableFlowNode ; } const expressionList = [ ] ; if ( ! this . _isNarrowingExpression ( expression , expressionList ) ) { return antecedent ; } expressionList . forEach ( ( expr ) => { const referenceKey = createKeyForReference ( expr ) ; this . _currentScopeCodeFlowExpressions ! . add ( referenceKey ) ; } ) ; const filteredExprList = expressionList . filter ( ( expr ) => expr . nodeType === ParseNodeType . Name ) ; const conditionalFlowNode = { flags , id : getUniqueFlowNodeId ( ) , reference : filteredExprList . length > 0 ? ( filteredExprList [ 0 ] as NameNode ) : undefined , expression , antecedent , } ; this . _addExceptTargets ( conditionalFlowNode ) ; return conditionalFlowNode ; } private _isLogicalExpression ( expression ) { switch ( expression . nodeType ) { case ParseNodeType . UnaryOperation : { return expression . operator === OperatorType . Not ; } case ParseNodeType . BinaryOperation : { return expression . operator === OperatorType . And || expression . operator === OperatorType . Or ; } } return false ; } private _isNarrowingExpression ( expression , expressionList , filterForNeverNarrowing = false , isComplexExpression = false ) { switch ( expression . nodeType ) { case ParseNodeType . Name : case ParseNodeType . MemberAccess : case ParseNodeType . Index : { if ( filterForNeverNarrowing ) { if ( expression . nodeType !== ParseNodeType . Name ) { return false ; } if ( ! isComplexExpression ) { return false ; } } if ( isCodeFlowSupportedForReference ( expression ) ) { expressionList . push ( expression ) ; return true ; } return false ; } case ParseNodeType . AssignmentExpression : { expressionList . push ( expression . name ) ; this . _isNarrowingExpression ( expression . rightExpression , expressionList , filterForNeverNarrowing , true ) ; return true ; } case ParseNodeType . BinaryOperation : { const isOrIsNotOperator = expression . operator === OperatorType . Is || expression . operator === OperatorType . IsNot ; const equalsOrNotEqualsOperator = expression . operator === OperatorType . Equals || expression . operator === OperatorType . NotEquals ; if ( isOrIsNotOperator || equalsOrNotEqualsOperator ) { if ( expression . rightExpression . nodeType === ParseNodeType . Constant && expression . rightExpression . constType === KeywordType . None ) { return this . _isNarrowingExpression ( expression . leftExpression , expressionList , filterForNeverNarrowing , true ) ; } if ( isOrIsNotOperator && expression . leftExpression . nodeType === ParseNodeType . Call && expression . leftExpression . leftExpression . nodeType === ParseNodeType . Name && expression . leftExpression . leftExpression . value === 'type' && expression . leftExpression . arguments . length === 1 && expression . leftExpression . arguments [ 0 ] . argumentCategory === ArgumentCategory . Simple ) { return this . _isNarrowingExpression ( expression . leftExpression . arguments [ 0 ] . valueExpression , expressionList , filterForNeverNarrowing , true ) ; } const isLeftNarrowing = this . _isNarrowingExpression ( expression . leftExpression , expressionList , filterForNeverNarrowing , true ) ; if ( isOrIsNotOperator ) { return isLeftNarrowing ; } if ( equalsOrNotEqualsOperator ) { const isRightNarrowing = this . _isNarrowingExpression ( expression . rightExpression , expressionList , filterForNeverNarrowing , true ) ; return isLeftNarrowing || isRightNarrowing ; } } if ( expression . operator === OperatorType . In || expression . operator === OperatorType . NotIn ) { if ( expression . leftExpression . nodeType === ParseNodeType . StringList && this . _isNarrowingExpression ( expression . rightExpression , expressionList , filterForNeverNarrowing , true ) ) { return true ; } } if ( expression . operator === OperatorType . In ) { return this . _isNarrowingExpression ( expression . leftExpression , expressionList , filterForNeverNarrowing , true ) ; } return false ; } case ParseNodeType . UnaryOperation : { return ( expression . operator === OperatorType . Not && this . _isNarrowingExpression ( expression . expression , expressionList , filterForNeverNarrowing , false ) ) ; } case ParseNodeType . AugmentedAssignment : { return this . _isNarrowingExpression ( expression . rightExpression , expressionList , filterForNeverNarrowing , true ) ; } case ParseNodeType . Call : { if ( expression . leftExpression . nodeType === ParseNodeType . Name && ( expression . leftExpression . value === 'isinstance' || expression . leftExpression . value === 'issubclass' ) && expression . arguments . length === 2 ) { return this . _isNarrowingExpression ( expression . arguments [ 0 ] . valueExpression , expressionList , filterForNeverNarrowing , true ) ; } if ( expression . leftExpression . nodeType === ParseNodeType . Name && expression . leftExpression . value === 'callable' && expression . arguments . length === 1 ) { return this . _isNarrowingExpression ( expression . arguments [ 0 ] . valueExpression , expressionList , filterForNeverNarrowing , true ) ; } if ( expression . arguments . length >= 1 ) { if ( filterForNeverNarrowing ) { return false ; } return this . _isNarrowingExpression ( expression . arguments [ 0 ] . valueExpression , expressionList , filterForNeverNarrowing , true ) ; } } } return false ; } private _createAssignmentTargetFlowNodes ( target , walkTargets , unbound ) { switch ( target . nodeType ) { case ParseNodeType . Name : case ParseNodeType . MemberAccess : { this . _createFlowAssignment ( target , unbound ) ; if ( walkTargets ) { this . walk ( target ) ; } break ; } case ParseNodeType . Index : { this . _createFlowAssignment ( target , unbound ) ; if ( walkTargets ) { this . walk ( target ) ; } break ; } case ParseNodeType . Tuple : { target . expressions . forEach ( ( expr ) => { this . _createAssignmentTargetFlowNodes ( expr , walkTargets , unbound ) ; } ) ; break ; } case ParseNodeType . TypeAnnotation : { this . _createAssignmentTargetFlowNodes ( target . valueExpression , false , unbound ) ; if ( walkTargets ) { this . walk ( target ) ; } break ; } case ParseNodeType . Unpack : { this . _createAssignmentTargetFlowNodes ( target . expression , false , unbound ) ; if ( walkTargets ) { this . walk ( target ) ; } break ; } case ParseNodeType . List : { target . entries . forEach ( ( entry ) => { this . _createAssignmentTargetFlowNodes ( entry , walkTargets , unbound ) ; } ) ; break ; } default : { if ( walkTargets ) { this . walk ( target ) ; } } } } private _createCallFlowNode ( node ) { if ( ! this . _isCodeUnreachable ( ) ) { const flowNode = { flags : FlowFlags . Call , id : getUniqueFlowNodeId ( ) , node , antecedent : this . _currentFlowNode ! , } ; this . _currentFlowNode = flowNode ; const isRevealTypeCall = node . leftExpression . nodeType === ParseNodeType . Name && node . leftExpression . value === 'reveal_type' ; if ( ! isRevealTypeCall && node . arguments . length > 0 && node . arguments [ 0 ] . argumentCategory === ArgumentCategory . Simple && ! node . arguments [ 0 ] . name ) { const argExpression = node . arguments [ 0 ] . valueExpression ; if ( argExpression . nodeType === ParseNodeType . Name || argExpression . nodeType === ParseNodeType . MemberAccess || argExpression . nodeType === ParseNodeType . Index ) { const expressionList = [ ] ; if ( this . _isNarrowingExpression ( argExpression , expressionList ) && expressionList . length === 1 ) { const referenceKey = createKeyForReference ( expressionList [ 0 ] ) ; this . _currentScopeCodeFlowExpressions ! . add ( referenceKey ) ; flowNode . typeGuardArgExpression = expressionList [ 0 ] ; } } } } AnalyzerNodeInfo . setFlowNode ( node , this . _currentFlowNode ! ) ; if ( ! this . _isCodeUnreachable ( ) ) { this . _addExceptTargets ( this . _currentFlowNode ! ) ; } } private _createAssignmentAliasFlowNode ( targetSymbolId , aliasSymbolId ) { if ( ! this . _isCodeUnreachable ( ) ) { const flowNode = { flags : FlowFlags . AssignmentAlias , id : getUniqueFlowNodeId ( ) , antecedent : this . _currentFlowNode ! , targetSymbolId , aliasSymbolId , } ; this . _currentFlowNode = flowNode ; } } private _createVariableAnnotationFlowNode ( ) { if ( ! this . _isCodeUnreachable ( ) ) { const flowNode = { flags : FlowFlags . VariableAnnotation , id : getUniqueFlowNodeId ( ) , antecedent : this . _currentFlowNode ! , } ; this . _currentFlowNode = flowNode ; } } private _createFlowAssignment ( node , unbound = false ) { let targetSymbolId = indeterminateSymbolId ; if ( node . nodeType === ParseNodeType . Name ) { const symbolWithScope = this . _currentScope . lookUpSymbolRecursive ( node . value ) ; assert ( symbolWithScope !== undefined ) ; targetSymbolId = symbolWithScope ! . symbol . id ; } const prevFlowNode = this . _currentFlowNode ! ; if ( ! this . _isCodeUnreachable ( ) && isCodeFlowSupportedForReference ( node ) ) { const flowNode = { flags : FlowFlags . Assignment , id : getUniqueFlowNodeId ( ) , node , antecedent : this . _currentFlowNode ! , targetSymbolId , } ; const referenceKey = createKeyForReference ( node ) ; this . _currentScopeCodeFlowExpressions ! . add ( referenceKey ) ; if ( unbound ) { flowNode . flags |= FlowFlags . Unbind ; } if ( node . nodeType === ParseNodeType . MemberAccess ) { this . _addExceptTargets ( flowNode ) ; } this . _currentFlowNode = flowNode ; } if ( ! unbound || AnalyzerNodeInfo . getFlowNode ( node ) === undefined ) { AnalyzerNodeInfo . setFlowNode ( node , unbound ? prevFlowNode : this . _currentFlowNode ! ) ; } } private _createFlowWildcardImport ( node , names ) { if ( ! this . _isCodeUnreachable ( ) ) { const flowNode = { flags : FlowFlags . WildcardImport , id : getUniqueFlowNodeId ( ) , node , names , antecedent : this . _currentFlowNode ! , } ; this . _addExceptTargets ( flowNode ) ; this . _currentFlowNode = flowNode ; } AnalyzerNodeInfo . setFlowNode ( node , this . _currentFlowNode ! ) ; } private _createFlowExhaustedMatch ( node ) { if ( ! this . _isCodeUnreachable ( ) ) { const flowNode = { flags : FlowFlags . ExhaustedMatch , id : getUniqueFlowNodeId ( ) , node , antecedent : this . _currentFlowNode ! , } ; this . _currentFlowNode = flowNode ; } AnalyzerNodeInfo . setFlowNode ( node , this . _currentFlowNode ! ) ; } private _isCodeUnreachable ( ) { return ! ! ( this . _currentFlowNode ! . flags & FlowFlags . Unreachable ) ; } private _addExceptTargets ( flowNode ) { if ( this . _currentExceptTargets ) { this . _currentExceptTargets . forEach ( ( label ) => { this . _addAntecedent ( label , flowNode ) ; } ) ; } } private _trackCodeFlowExpressions ( callback ) { const savedExpressions = this . _currentScopeCodeFlowExpressions ; this . _currentScopeCodeFlowExpressions = new Set < string > ( ) ; callback ( ) ; const scopedExpressions = this . _currentScopeCodeFlowExpressions ; if ( savedExpressions ) { this . _currentScopeCodeFlowExpressions . forEach ( ( value ) => { savedExpressions . add ( value ) ; } ) ; } this . _currentScopeCodeFlowExpressions = savedExpressions ; return scopedExpressions ; } private _bindLoopStatement ( preLoopLabel , postLoopLabel , callback ) { const savedContinueTarget = this . _currentContinueTarget ; const savedBreakTarget = this . _currentBreakTarget ; const savedCodeFlowComplexity = this . _functionCodeFlowComplexity ; this . _currentContinueTarget = preLoopLabel ; this . _currentBreakTarget = postLoopLabel ; this . _functionCodeFlowComplexity = 1 ; preLoopLabel . affectedExpressions = this . _trackCodeFlowExpressions ( callback ) ; this . _currentContinueTarget = savedContinueTarget ; this . _currentBreakTarget = savedBreakTarget ; this . _functionCodeFlowComplexity = this . _functionCodeFlowComplexity * 2 + savedCodeFlowComplexity ; } private _addAntecedent ( label , antecedent ) { if ( ! ( this . _currentFlowNode ! . flags & FlowFlags . Unreachable ) ) { if ( ! label . antecedents . some ( ( existing ) => existing . id === antecedent . id ) ) { label . antecedents . push ( antecedent ) ; } } } private _bindNameToScope ( scope , name , addedSymbols ? ) { const bindingType = this . _currentScope . getBindingType ( name ) ; if ( bindingType !== undefined ) { const scopeToUse = bindingType === NameBindingType . Nonlocal ? this . _currentScope . parent ! : this . _currentScope . getGlobalScope ( ) ; const symbolWithScope = scopeToUse . lookUpSymbolRecursive ( name ) ; if ( symbolWithScope ) { return symbolWithScope . symbol ; } } else { let symbol = scope . lookUpSymbol ( name ) ; if ( ! symbol ) { symbol = scope . addSymbol ( name , SymbolFlags . InitiallyUnbound | SymbolFlags . ClassMember ) ; if ( scope . type === ScopeType . Class ) { const aliasSymbol = scope . parent ! . lookUpSymbol ( name ) ; if ( aliasSymbol ) { this . _createAssignmentAliasFlowNode ( symbol . id , aliasSymbol . id ) ; } } if ( this . _currentScope . type === ScopeType . Module || this . _currentScope . type === ScopeType . Builtin ) { if ( isPrivateOrProtectedName ( name ) ) { if ( isPrivateName ( name ) ) { symbol . setIsExternallyHidden ( ) ; } else if ( this . _fileInfo . isStubFile || this . _fileInfo . isInPyTypedPackage ) { if ( this . _currentScope . type === ScopeType . Builtin ) { symbol . setIsExternallyHidden ( ) ; } else { this . _potentialPrivateSymbols . set ( name , symbol ) ; } } else { symbol . setIsPrivateMember ( ) ; } } } if ( addedSymbols ) { addedSymbols . set ( name , symbol ) ; } } return symbol ; } return undefined ; } private _bindPossibleTupleNamedTarget ( target , addedSymbols ? ) { switch ( target . nodeType ) { case ParseNodeType . Name : { this . _bindNameToScope ( this . _currentScope , target . value , addedSymbols ) ; break ; } case ParseNodeType . Tuple : { target . expressions . forEach ( ( expr ) => { this . _bindPossibleTupleNamedTarget ( expr , addedSymbols ) ; } ) ; break ; } case ParseNodeType . List : { target . entries . forEach ( ( expr ) => { this . _bindPossibleTupleNamedTarget ( expr , addedSymbols ) ; } ) ; break ; } case ParseNodeType . TypeAnnotation : { this . _bindPossibleTupleNamedTarget ( target . valueExpression , addedSymbols ) ; break ; } case ParseNodeType . Unpack : { this . _bindPossibleTupleNamedTarget ( target . expression , addedSymbols ) ; break ; } } } private _addBuiltInSymbolToCurrentScope ( nameValue , node , type ) { const symbol = this . _addSymbolToCurrentScope ( nameValue , false ) ; if ( symbol ) { symbol . addDeclaration ( { type : DeclarationType . Intrinsic , node , intrinsicType : type , path : this . _fileInfo . filePath , range : getEmptyRange ( ) , moduleName : this . _fileInfo . moduleName , } ) ; symbol . setIsIgnoredForProtocolMatch ( ) ; } } private _addSymbolToCurrentScope ( nameValue , isInitiallyUnbound ) { let symbol = this . _currentScope . lookUpSymbol ( nameValue ) ; if ( ! symbol ) { let symbolFlags = SymbolFlags . None ; if ( isInitiallyUnbound ) { symbolFlags |= SymbolFlags . InitiallyUnbound ; } if ( this . _currentScope . type === ScopeType . Class ) { symbolFlags |= SymbolFlags . ClassMember ; } if ( this . _fileInfo . isStubFile && isPrivateOrProtectedName ( nameValue ) ) { symbolFlags |= SymbolFlags . ExternallyHidden ; } symbol = this . _currentScope . addSymbol ( nameValue , symbolFlags ) ; } return symbol ; } private _createNewScope ( scopeType , parentScope , callback ) { const prevScope = this . _currentScope ; const newScope = new Scope ( scopeType , parentScope ) ; this . _currentScope = newScope ; const isExecutionScope = scopeType === ScopeType . Builtin || scopeType === ScopeType . Module || scopeType === ScopeType . Function ; const prevExpressions = this . _currentScopeCodeFlowExpressions ; if ( isExecutionScope ) { this . _currentScopeCodeFlowExpressions = new Set < string > ( ) ; } callback ( ) ; this . _currentScopeCodeFlowExpressions = prevExpressions ; this . _currentScope = prevScope ; return newScope ; } private _addInferredTypeAssignmentForVariable ( target , source , isPossibleTypeAlias = false ) { switch ( target . nodeType ) { case ParseNodeType . Name : { const name = target ; const symbolWithScope = this . _currentScope . lookUpSymbolRecursive ( name . value ) ; if ( symbolWithScope && symbolWithScope . symbol ) { const declaration = { type : DeclarationType . Variable , node : target , isConstant : isConstantName ( target . value ) , inferredTypeSource : source , typeAliasName : isPossibleTypeAlias ? target : undefined , path : this . _fileInfo . filePath , range : convertOffsetsToRange ( name . start , TextRange . getEnd ( name ) , this . _fileInfo . lines ) , moduleName : this . _fileInfo . moduleName , docString : this . _getVariableDocString ( target ) , } ; symbolWithScope . symbol . addDeclaration ( declaration ) ; } break ; } case ParseNodeType . MemberAccess : { const memberAccessInfo = this . _getMemberAccessInfo ( target ) ; if ( memberAccessInfo ) { const name = target . memberName ; let symbol = memberAccessInfo . classScope . lookUpSymbol ( name . value ) ; if ( ! symbol ) { symbol = memberAccessInfo . classScope . addSymbol ( name . value , SymbolFlags . InitiallyUnbound ) ; const honorPrivateNaming = this . _fileInfo . diagnosticRuleSet . reportPrivateUsage !== 'none' ; if ( isPrivateOrProtectedName ( name . value ) && honorPrivateNaming ) { symbol . setIsPrivateMember ( ) ; } } if ( memberAccessInfo . isInstanceMember ) { if ( ! symbol . isClassMember ( ) || ! symbol . getDeclarations ( ) . some ( ( decl ) => decl . type === DeclarationType . Function && decl . isMethod ) ) { symbol . setIsInstanceMember ( ) ; } } else { symbol . setIsClassMember ( ) ; } const declaration = { type : DeclarationType . Variable , node : target . memberName , isConstant : isConstantName ( name . value ) , inferredTypeSource : source , isDefinedByMemberAccess : true , path : this . _fileInfo . filePath , range : convertOffsetsToRange ( target . memberName . start , target . memberName . start + target . memberName . length , this . _fileInfo . lines ) , moduleName : this . _fileInfo . moduleName , docString : this . _getVariableDocString ( target ) , } ; symbol . addDeclaration ( declaration ) ; } break ; } case ParseNodeType . Tuple : { target . expressions . forEach ( ( expr ) => { this . _addInferredTypeAssignmentForVariable ( expr , source ) ; } ) ; break ; } case ParseNodeType . TypeAnnotation : { this . _addInferredTypeAssignmentForVariable ( target . valueExpression , source ) ; break ; } case ParseNodeType . Unpack : { this . _addInferredTypeAssignmentForVariable ( target . expression , source ) ; break ; } case ParseNodeType . List : { target . entries . forEach ( ( entry ) => { this . _addInferredTypeAssignmentForVariable ( entry , source ) ; } ) ; break ; } } } private _addTypeDeclarationForVariable ( target , typeAnnotation ) { let declarationHandled = false ; switch ( target . nodeType ) { case ParseNodeType . Name : { const name = target ; const symbolWithScope = this . _currentScope . lookUpSymbolRecursive ( name . value ) ; if ( symbolWithScope && symbolWithScope . symbol ) { const finalInfo = this . _isAnnotationFinal ( typeAnnotation ) ; const isExplicitTypeAlias = this . _isAnnotationTypeAlias ( typeAnnotation ) ; let typeAnnotationNode = typeAnnotation ; let innerTypeAnnotationNode = typeAnnotation ; if ( isExplicitTypeAlias ) { typeAnnotationNode = undefined ; innerTypeAnnotationNode = undefined ; if ( this . _currentScope . type !== ScopeType . Class && this . _currentScope . type !== ScopeType . Module && this . _currentScope . type !== ScopeType . Builtin ) { this . _addError ( Localizer . Diagnostic . typeAliasNotInModuleOrClass ( ) , typeAnnotation ) ; } } else if ( finalInfo . isFinal ) { innerTypeAnnotationNode = finalInfo . finalTypeNode ; if ( ! finalInfo . finalTypeNode ) { typeAnnotationNode = undefined ; } } let classVarInfo = this . _isAnnotationClassVar ( typeAnnotation ) ; if ( classVarInfo . isClassVar ) { innerTypeAnnotationNode = classVarInfo . classVarTypeNode ; if ( ! classVarInfo . classVarTypeNode ) { typeAnnotationNode = undefined ; } } if ( finalInfo . isFinal ) { const containingClass = ParseTreeUtils . getEnclosingClassOrFunction ( target ) ; if ( containingClass && containingClass . nodeType === ParseNodeType . Class ) { if ( target . parent ?. nodeType === ParseNodeType . Assignment || target . parent ?. parent ?. nodeType === ParseNodeType . Assignment ) { classVarInfo = { isClassVar : true , classVarTypeNode : undefined , } ; } } } const declaration = { type : DeclarationType . Variable , node : target , isConstant : isConstantName ( name . value ) , isFinal : finalInfo . isFinal , isClassVar : classVarInfo . isClassVar , isRequired : this . _isRequiredAnnotation ( innerTypeAnnotationNode ) , isNotRequired : this . _isNotRequiredAnnotation ( innerTypeAnnotationNode ) , typeAliasAnnotation : isExplicitTypeAlias ? typeAnnotation : undefined , typeAliasName : isExplicitTypeAlias ? target : undefined , path : this . _fileInfo . filePath , typeAnnotationNode , range : convertOffsetsToRange ( name . start , TextRange . getEnd ( name ) , this . _fileInfo . lines ) , moduleName : this . _fileInfo . moduleName , docString : this . _getVariableDocString ( target ) , } ; symbolWithScope . symbol . addDeclaration ( declaration ) ; if ( classVarInfo . isClassVar ) { symbolWithScope . symbol . setIsClassVar ( ) ; } else { symbolWithScope . symbol . setIsInstanceMember ( ) ; } if ( typeAnnotation . nodeType === ParseNodeType . Index && this . _isDataclassesAnnotation ( typeAnnotation . baseExpression , 'InitVar' ) ) { symbolWithScope . symbol . setIsInitVar ( ) ; } } declarationHandled = true ; break ; } case ParseNodeType . MemberAccess : { const memberAccessInfo = this . _getMemberAccessInfo ( target ) ; if ( memberAccessInfo ) { const name = target . memberName ; let symbol = memberAccessInfo . classScope . lookUpSymbol ( name . value ) ; if ( ! symbol ) { symbol = memberAccessInfo . classScope . addSymbol ( name . value , SymbolFlags . InitiallyUnbound ) ; const honorPrivateNaming = this . _fileInfo . diagnosticRuleSet . reportPrivateUsage !== 'none' ; if ( isPrivateOrProtectedName ( name . value ) && honorPrivateNaming ) { symbol . setIsPrivateMember ( ) ; } } if ( memberAccessInfo . isInstanceMember ) { symbol . setIsInstanceMember ( ) ; } else { symbol . setIsClassMember ( ) ; } const finalInfo = this . _isAnnotationFinal ( typeAnnotation ) ; const declaration = { type : DeclarationType . Variable , node : target . memberName , isConstant : isConstantName ( name . value ) , isDefinedByMemberAccess : true , isFinal : finalInfo . isFinal , path : this . _fileInfo . filePath , typeAnnotationNode : finalInfo . isFinal && ! finalInfo . finalTypeNode ? undefined : typeAnnotation , range : convertOffsetsToRange ( target . memberName . start , target . memberName . start + target . memberName . length , this . _fileInfo . lines ) , moduleName : this . _fileInfo . moduleName , docString : this . _getVariableDocString ( target ) , } ; symbol . addDeclaration ( declaration ) ; declarationHandled = true ; } break ; } } if ( ! declarationHandled ) { this . _addError ( Localizer . Diagnostic . annotationNotSupported ( ) , typeAnnotation ) ; } } private _isTypingAnnotation ( typeAnnotation , name ) { return this . _isKnownAnnotation ( typeAnnotation , name , this . _typingImportAliases , this . _typingSymbolAliases ) ; } private _isDataclassesAnnotation ( typeAnnotation , name ) { return this . _isKnownAnnotation ( typeAnnotation , name , this . _dataclassesImportAliases , this . _dataclassesSymbolAliases ) ; } private _isKnownAnnotation ( typeAnnotation , name , importAliases , symbolAliases ) { if ( typeAnnotation . nodeType === ParseNodeType . Name ) { const alias = symbolAliases . get ( typeAnnotation . value ) ; if ( alias === name ) { return true ; } } else if ( typeAnnotation . nodeType === ParseNodeType . MemberAccess ) { if ( typeAnnotation . leftExpression . nodeType === ParseNodeType . Name && typeAnnotation . memberName . value === name ) { const baseName = typeAnnotation . leftExpression . value ; return importAliases . some ( ( alias ) => alias === baseName ) ; } } return false ; } private _getVariableDocString ( node ) { let curNode = node ; let annotationNode ; while ( curNode ) { if ( curNode . nodeType === ParseNodeType . Assignment ) { break ; } if ( curNode . nodeType === ParseNodeType . TypeAnnotation && ! annotationNode ) { annotationNode = curNode ; } curNode = curNode . parent ; } if ( curNode ?. nodeType !== ParseNodeType . Assignment ) { if ( annotationNode ) { curNode = annotationNode ; } else { return undefined ; } } const parentNode = curNode . parent ; if ( parentNode ?. nodeType !== ParseNodeType . StatementList ) { return undefined ; } const suiteOrModule = parentNode . parent ; if ( ! suiteOrModule || ( suiteOrModule . nodeType !== ParseNodeType . Module && suiteOrModule . nodeType !== ParseNodeType . Suite ) ) { return undefined ; } const assignmentIndex = suiteOrModule . statements . findIndex ( ( node ) => node === parentNode ) ; if ( assignmentIndex < 0 || assignmentIndex === suiteOrModule . statements . length - 1 ) { return undefined ; } const nextStatement = suiteOrModule . statements [ assignmentIndex + 1 ] ; if ( nextStatement . nodeType !== ParseNodeType . StatementList || ! ParseTreeUtils . isDocString ( nextStatement ) ) { return undefined ; } let isValidContext = false ; if ( parentNode ?. parent ?. nodeType === ParseNodeType . Module ) { isValidContext = true ; } else if ( parentNode ?. parent ?. nodeType === ParseNodeType . Suite && parentNode ?. parent ?. parent ?. nodeType === ParseNodeType . Class ) { isValidContext = true ; } else { const func = ParseTreeUtils . getEnclosingFunction ( parentNode ) ; if ( func && func . name . value === '__init__' && ParseTreeUtils . getEnclosingClass ( func , true ) ) { isValidContext = true ; } } if ( ! isValidContext ) { return undefined ; } const strings = ( nextStatement . statements [ 0 ] as StringListNode ) . strings ; if ( strings . length === 1 ) { return strings [ 0 ] . value ; } return strings . map ( ( s ) => s . value ) . join ( '' ) ; } private _isAnnotationFinal ( typeAnnotation ) { let isFinal = false ; let finalTypeNode ; if ( typeAnnotation ) { if ( this . _isTypingAnnotation ( typeAnnotation , 'Final' ) ) { isFinal = true ; } else if ( typeAnnotation . nodeType === ParseNodeType . Index && typeAnnotation . items . length === 1 ) { const finalInfo = this . _isAnnotationFinal ( typeAnnotation . baseExpression ) ; if ( finalInfo . isFinal && typeAnnotation . items [ 0 ] . argumentCategory === ArgumentCategory . Simple && ! typeAnnotation . items [ 0 ] . name && ! typeAnnotation . trailingComma ) { isFinal = true ; finalTypeNode = typeAnnotation . items [ 0 ] . valueExpression ; } } } return { isFinal , finalTypeNode } ; } private _isAnnotationClassVar ( typeAnnotation ) { let isClassVar = false ; let classVarTypeNode ; if ( typeAnnotation ) { if ( this . _isTypingAnnotation ( typeAnnotation , 'ClassVar' ) ) { isClassVar = true ; } else if ( typeAnnotation . nodeType === ParseNodeType . Index && typeAnnotation . items . length === 1 ) { const finalInfo = this . _isAnnotationClassVar ( typeAnnotation . baseExpression ) ; if ( finalInfo . isClassVar && typeAnnotation . items [ 0 ] . argumentCategory === ArgumentCategory . Simple && ! typeAnnotation . items [ 0 ] . name && ! typeAnnotation . trailingComma ) { isClassVar = true ; classVarTypeNode = typeAnnotation . items [ 0 ] . valueExpression ; } } } return { isClassVar , classVarTypeNode } ; } private _isRequiredAnnotation ( typeAnnotation ) { if ( typeAnnotation && typeAnnotation . nodeType === ParseNodeType . Index && typeAnnotation . items . length === 1 ) { if ( this . _isTypingAnnotation ( typeAnnotation . baseExpression , 'Required' ) ) { return true ; } } return false ; } private _isNotRequiredAnnotation ( typeAnnotation ) { if ( typeAnnotation && typeAnnotation . nodeType === ParseNodeType . Index && typeAnnotation . items . length === 1 ) { if ( this . _isTypingAnnotation ( typeAnnotation . baseExpression , 'NotRequired' ) ) { return true ; } } return false ; } private _isAnnotationTypeAlias ( typeAnnotation ) { if ( ! typeAnnotation ) { return false ; } return this . _isTypingAnnotation ( typeAnnotation , 'TypeAlias' ) ; } private _getMemberAccessInfo ( node ) { if ( node . leftExpression . nodeType !== ParseNodeType . Name ) { return undefined ; } const leftSymbolName = node . leftExpression . value ; const methodNode = ParseTreeUtils . getEnclosingFunction ( node ) ; if ( ! methodNode ) { return undefined ; } const classNode = ParseTreeUtils . getEnclosingClass ( methodNode ) ; if ( ! classNode ) { return undefined ; } let isInstanceMember = false ; if ( methodNode . parameters . length < 1 || ! methodNode . parameters [ 0 ] . name ) { return undefined ; } const className = classNode . name . value ; const firstParamName = methodNode . parameters [ 0 ] . name . value ; if ( leftSymbolName === className ) { isInstanceMember = false ; } else { if ( leftSymbolName !== firstParamName ) { return undefined ; } if ( methodNode . name . value === '__new__' ) { isInstanceMember = false ; } else { isInstanceMember = true ; for ( const decorator of methodNode . decorators ) { if ( decorator . expression . nodeType === ParseNodeType . Name ) { const decoratorName = decorator . expression . value ; if ( decoratorName === 'staticmethod' ) { return undefined ; } else if ( decoratorName === 'classmethod' ) { isInstanceMember = false ; break ; } } } } } const classScope = AnalyzerNodeInfo . getScope ( classNode ) ! ; assert ( classScope !== undefined ) ; return { classNode , methodNode , classScope , isInstanceMember , } ; } private _addImplicitImportsToLoaderActions ( importResult , loaderActions ) { importResult . filteredImplicitImports . forEach ( ( implicitImport ) => { const existingLoaderAction = loaderActions . implicitImports ? loaderActions . implicitImports . get ( implicitImport . name ) : undefined ; if ( existingLoaderAction ) { existingLoaderAction . path = implicitImport . path ; existingLoaderAction . loadSymbolsFromPath = true ; } else { if ( ! loaderActions . implicitImports ) { loaderActions . implicitImports = new Map < string , ModuleLoaderActions > ( ) ; } loaderActions . implicitImports . set ( implicitImport . name , { path : implicitImport . path , loadSymbolsFromPath : true , implicitImports : new Map < string , ModuleLoaderActions > ( ) , } ) ; } } ) ; } private _handleTypingStubAssignmentOrAnnotation ( node ) { if ( ! this . _fileInfo . isTypingStubFile ) { return false ; } let annotationNode ; if ( node . nodeType === ParseNodeType . TypeAnnotation ) { annotationNode = node ; } else { if ( node . leftExpression . nodeType !== ParseNodeType . TypeAnnotation ) { return false ; } annotationNode = node . leftExpression ; } if ( annotationNode . valueExpression . nodeType !== ParseNodeType . Name ) { return false ; } const assignedNameNode = annotationNode . valueExpression ; const specialTypes = new Map ( [ [ 'Tuple' , true ] , [ 'Generic' , true ] , [ 'Protocol' , true ] , [ 'Callable' , true ] , [ 'Type' , true ] , [ 'ClassVar' , true ] , [ 'Final' , true ] , [ 'Literal' , true ] , [ 'TypedDict' , true ] , [ 'Union' , true ] , [ 'Optional' , true ] , [ 'Annotated' , true ] , [ 'TypeAlias' , true ] , [ 'OrderedDict' , true ] , [ 'Concatenate' , true ] , [ 'TypeGuard' , true ] , [ 'Unpack' , true ] , [ 'Self' , true ] , [ 'NoReturn' , true ] , ] ) ; const assignedName = assignedNameNode . value ; if ( ! specialTypes . has ( assignedName ) ) { return false ; } const symbol = this . _bindNameToScope ( this . _currentScope , assignedName ) ; if ( symbol ) { symbol . addDeclaration ( { type : DeclarationType . SpecialBuiltInClass , node : annotationNode , path : this . _fileInfo . filePath , range : convertOffsetsToRange ( annotationNode . start , TextRange . getEnd ( annotationNode ) , this . _fileInfo . lines ) , moduleName : this . _fileInfo . moduleName , } ) ; } return true ; } private _deferBinding ( callback ) { if ( this . _moduleSymbolOnly ) { return ; } this . _deferredBindingTasks . push ( { scope : this . _currentScope , codeFlowExpressions : this . _currentScopeCodeFlowExpressions ! , callback , } ) ; } private _bindDeferred ( ) { while ( this . _deferredBindingTasks . length > 0 ) { const nextItem = this . _deferredBindingTasks . shift ( ) ! ; this . _currentScope = nextItem . scope ; this . _currentScopeCodeFlowExpressions = nextItem . codeFlowExpressions ; nextItem . callback ( ) ; } } private _bindYield ( node ) { const functionNode = ParseTreeUtils . getEnclosingFunction ( node ) ; if ( ! functionNode ) { if ( ! ParseTreeUtils . getEnclosingLambda ( node ) ) { this . _addError ( Localizer . Diagnostic . yieldOutsideFunction ( ) , node ) ; } } else if ( functionNode . isAsync && node . nodeType === ParseNodeType . YieldFrom ) { this . _addError ( Localizer . Diagnostic . yieldFromOutsideAsync ( ) , node ) ; } if ( this . _targetFunctionDeclaration ) { if ( ! this . _targetFunctionDeclaration . yieldStatements ) { this . _targetFunctionDeclaration . yieldStatements = [ ] ; } this . _targetFunctionDeclaration . yieldStatements . push ( node ) ; this . _targetFunctionDeclaration . isGenerator = true ; } if ( node . expression ) { this . walk ( node . expression ) ; } AnalyzerNodeInfo . setFlowNode ( node , this . _currentFlowNode ! ) ; } private _addDiagnostic ( diagLevel , rule , message , textRange ) { let diagnostic ; switch ( diagLevel ) { case 'error' : diagnostic = this . _addError ( message , textRange ) ; break ; case 'warning' : diagnostic = this . _addWarning ( message , textRange ) ; break ; case 'information' : diagnostic = this . _addInformation ( message , textRange ) ; break ; case 'none' : break ; default : return assertNever ( diagLevel , `${ diagLevel } is not expected` ) ; } if ( diagnostic ) { diagnostic . setRule ( rule ) ; } return diagnostic ; } private _addError ( message , textRange ) { return this . _fileInfo . diagnosticSink . addDiagnosticWithTextRange ( 'error' , message , textRange ) ; } private _addWarning ( message , textRange ) { return this . _fileInfo . diagnosticSink . addDiagnosticWithTextRange ( 'warning' , message , textRange ) ; } private _addInformation ( message , textRange ) { return this . _fileInfo . diagnosticSink . addDiagnosticWithTextRange ( 'information' , message , textRange ) ; } } export class YieldFinder extends ParseTreeWalker { private _containsYield = false ; checkContainsYield ( node ) { this . walk ( node ) ; return this . _containsYield ; } override visitYield ( node ) { this . _containsYield = true ; return false ; } override visitYieldFrom ( node ) { this . _containsYield = true ; return false ; } } export class ReturnFinder extends ParseTreeWalker { private _containsReturn = false ; checkContainsReturn ( node ) { this . walk ( node ) ; return this . _containsReturn ; } override visitReturn ( node ) { this . _containsReturn = true ; return false ; } }