import { Commands } from '../commands/commands' ; import { DiagnosticLevel } from '../common/configOptions' ; import { assert } from '../common/debug' ; import { Diagnostic , DiagnosticAddendum } from '../common/diagnostic' ; import { DiagnosticRule } from '../common/diagnosticRules' ; import { PythonVersion , versionToString } from '../common/pythonVersion' ; import { TextRange } from '../common/textRange' ; import { Localizer } from '../localization/localize' ; import { ArgumentCategory , AssertNode , AssignmentExpressionNode , AssignmentNode , AugmentedAssignmentNode , AwaitNode , BinaryOperationNode , CallNode , CaseNode , ClassNode , DelNode , DictionaryNode , ErrorNode , ExceptNode , FormatStringNode , ForNode , FunctionNode , GlobalNode , IfNode , ImportAsNode , ImportFromAsNode , ImportFromNode , IndexNode , isExpressionNode , LambdaNode , ListComprehensionNode , ListNode , MatchNode , MemberAccessNode , ModuleNode , NameNode , NonlocalNode , ParameterCategory , ParseNode , ParseNodeType , PatternClassNode , RaiseNode , ReturnNode , SetNode , SliceNode , StatementListNode , StatementNode , StringListNode , StringNode , SuiteNode , TernaryNode , TryNode , TupleNode , TypeAnnotationNode , UnaryOperationNode , UnpackNode , WhileNode , WithNode , YieldFromNode , YieldNode , } from '../parser/parseNodes' ; import { getUnescapedString , UnescapeError , UnescapeErrorType } from '../parser/stringTokenUtils' ; import { OperatorType } from '../parser/tokenizerTypes' ; import { AnalyzerFileInfo } from './analyzerFileInfo' ; import * as AnalyzerNodeInfo from './analyzerNodeInfo' ; import { Declaration , DeclarationType } from './declaration' ; import { isExplicitTypeAliasDeclaration , isFinalVariableDeclaration } from './declarationUtils' ; import { ImportType } from './importResult' ; import { getTopLevelImports } from './importStatementUtils' ; import * as ParseTreeUtils from './parseTreeUtils' ; import { ParseTreeWalker } from './parseTreeWalker' ; import { validateClassPattern } from './patternMatching' ; import { ScopeType } from './scope' ; import { getScopeForNode } from './scopeUtils' ; import { evaluateStaticBoolExpression } from './staticExpressions' ; import { Symbol } from './symbol' ; import * as SymbolNameUtils from './symbolNameUtils' ; import { getLastTypedDeclaredForSymbol , isFinalVariable } from './symbolUtils' ; import { TypeEvaluator } from './typeEvaluatorTypes' ; import { ClassType , combineTypes , FunctionType , isAnyOrUnknown , isClass , isClassInstance , isFunction , isInstantiableClass , isModule , isNever , isNoneInstance , isOverloadedFunction , isParamSpec , isPossiblyUnbound , isTypeSame , isTypeVar , isUnbound , isUnion , isUnknown , isVariadicTypeVar , NoneType , Type , TypeBase , TypeCategory , TypeVarType , UnknownType , Variance , } from './types' ; import { applySolvedTypeVars , CanAssignFlags , ClassMember , ClassMemberLookupFlags , convertToInstance , derivesFromAnyOrUnknown , derivesFromClassRecursive , doForEachSubtype , getClassFieldsRecursive , getDeclaredGeneratorReturnType , getGeneratorTypeArgs , getGeneratorYieldType , getParameterListDetails , getTypeVarArgumentsRecursive , getTypeVarScopeId , isEllipsisType , isLiteralType , isLiteralTypeOrUnion , isNoReturnType , isOpenEndedTupleClass , isPartlyUnknown , isProperty , isTupleClass , lookUpClassMember , mapSubtypes , partiallySpecializeType , removeNoReturnFromUnion , transformPossibleRecursiveTypeAlias , } from './typeUtils' ; import { TypeVarMap } from './typeVarMap' ; interface LocalTypeVarInfo { isExempt : boolean ; nodes : NameNode [ ] ; } interface DeprecatedForm { version : PythonVersion ; fullName : string ; replacementText : string ; } const deprecatedAliases = new Map < string , DeprecatedForm > ( [ [ 'Tuple' , { version : PythonVersion . V3_9 , fullName : 'builtins.tuple' , replacementText : 'tuple' } ] , [ 'List' , { version : PythonVersion . V3_9 , fullName : 'builtins.list' , replacementText : 'list' } ] , [ 'Dict' , { version : PythonVersion . V3_9 , fullName : 'builtins.dict' , replacementText : 'dict' } ] , [ 'Set' , { version : PythonVersion . V3_9 , fullName : 'builtins.set' , replacementText : 'set' } ] , [ 'FrozenSet' , { version : PythonVersion . V3_9 , fullName : 'builtins.frozenset' , replacementText : 'frozenset' } ] , [ 'Type' , { version : PythonVersion . V3_9 , fullName : 'builtins.type' , replacementText : 'type' } ] , [ 'Deque' , { version : PythonVersion . V3_9 , fullName : 'collections.deque' , replacementText : 'collections.deque' } ] , [ 'DefaultDict' , { version : PythonVersion . V3_9 , fullName : 'collections.defaultdict' , replacementText : 'collections.defaultdict' , } , ] , [ 'OrderedDict' , { version : PythonVersion . V3_9 , fullName : 'collections.OrderedDict' , replacementText : 'collections.OrderedDict' , } , ] , [ 'Counter' , { version : PythonVersion . V3_9 , fullName : 'collections.Counter' , replacementText : 'collections.Counter' } , ] , [ 'ChainMap' , { version : PythonVersion . V3_9 , fullName : 'collections.ChainMap' , replacementText : 'collections.ChainMap' } , ] , ] ) ; const deprecatedSpecialForms = new Map < string , DeprecatedForm > ( [ [ 'Optional' , { version : PythonVersion . V3_10 , fullName : 'typing.Optional' , replacementText : '| None' } ] , [ 'Union' , { version : PythonVersion . V3_10 , fullName : 'typing.Union' , replacementText : '|' } ] , ] ) ; export class Checker extends ParseTreeWalker { private readonly _moduleNode : ModuleNode ; private readonly _fileInfo : AnalyzerFileInfo ; private readonly _evaluator : TypeEvaluator ; private _isUnboundCheckSuppressed = false ; private _scopedNodes : AnalyzerNodeInfo . ScopedNode [ ] = [ ] ; constructor ( node , evaluator ) { super ( ) ; this . _moduleNode = node ; this . _fileInfo = AnalyzerNodeInfo . getFileInfo ( node ) ! ; this . _evaluator = evaluator ; } check ( ) { this . _scopedNodes . push ( this . _moduleNode ) ; this . _walkStatementsAndReportUnreachable ( this . _moduleNode . statements ) ; const dunderAllInfo = AnalyzerNodeInfo . getDunderAllInfo ( this . _moduleNode ) ; if ( dunderAllInfo ) { this . _evaluator . markNamesAccessed ( this . _moduleNode , dunderAllInfo . names ) ; this . _reportUnusedDunderAllSymbols ( dunderAllInfo . stringNodes ) ; } this . _validateSymbolTables ( ) ; this . _reportDuplicateImports ( ) ; } override walk ( node ) { if ( ! AnalyzerNodeInfo . isCodeUnreachable ( node ) ) { super . walk ( node ) ; } else { this . _evaluator . suppressDiagnostics ( node , ( ) => { super . walk ( node ) ; } ) ; } } override visitSuite ( node ) { this . _walkStatementsAndReportUnreachable ( node . statements ) ; return false ; } override visitStatementList ( node ) { node . statements . forEach ( ( statement ) => { if ( isExpressionNode ( statement ) ) { this . _evaluator . getType ( statement ) ; } } ) ; return true ; } override visitClass ( node ) { const classTypeResult = this . _evaluator . getTypeOfClass ( node ) ; this . walk ( node . suite ) ; this . walkMultiple ( node . decorators ) ; this . walkMultiple ( node . arguments ) ; if ( classTypeResult ) { if ( ClassType . isProtocolClass ( classTypeResult . classType ) ) { node . arguments . forEach ( ( arg ) => { if ( ! arg . name ) { const baseClassType = this . _evaluator . getType ( arg . valueExpression ) ; if ( baseClassType && isInstantiableClass ( baseClassType ) && ! ClassType . isBuiltIn ( baseClassType , 'Protocol' ) && ! ClassType . isBuiltIn ( baseClassType , 'Generic' ) ) { if ( ! ClassType . isProtocolClass ( baseClassType ) ) { this . _evaluator . addError ( Localizer . Diagnostic . protocolBaseClass ( ) . format ( { classType : this . _evaluator . printType ( classTypeResult . classType , false ) , baseType : this . _evaluator . printType ( baseClassType , false ) , } ) , arg . valueExpression ) ; } } } } ) ; this . _validateProtocolTypeParamVariance ( node , classTypeResult . classType ) ; } if ( ! this . _fileInfo . isStubFile ) { this . _validateBaseClassOverrides ( classTypeResult . classType ) ; this . _validateSlotsClassVarConflict ( classTypeResult . classType ) ; } this . _validateMultipleInheritanceCompatibility ( classTypeResult . classType , node . name ) ; this . _validateFinalMemberOverrides ( classTypeResult . classType ) ; this . _validateInstanceVariableInitialization ( classTypeResult . classType ) ; this . _validateFinalClassNotAbstract ( classTypeResult . classType , node ) ; this . _validateDataClassPostInit ( classTypeResult . classType , node ) ; this . _reportDuplicateEnumMembers ( classTypeResult . classType ) ; if ( ClassType . isTypedDictClass ( classTypeResult . classType ) ) { this . _validateTypedDictClassSuite ( node . suite ) ; } if ( ClassType . isEnumClass ( classTypeResult . classType ) ) { this . _validateEnumClassOverride ( node , classTypeResult . classType ) ; } } this . _scopedNodes . push ( node ) ; return false ; } override visitFunction ( node ) { const functionTypeResult = this . _evaluator . getTypeOfFunction ( node ) ; const containingClassNode = ParseTreeUtils . getEnclosingClass ( node , true ) ; if ( functionTypeResult ) { let sawParamSpecArgs = false ; node . parameters . forEach ( ( param , index ) => { if ( param . name ) { if ( param . category === ParameterCategory . VarArgList ) { const annotationExpr = param . typeAnnotation || param . typeAnnotationComment ; if ( annotationExpr && annotationExpr . nodeType === ParseNodeType . MemberAccess && annotationExpr . memberName . value === 'args' ) { const baseType = this . _evaluator . getType ( annotationExpr . leftExpression ) ; if ( baseType && isTypeVar ( baseType ) && baseType . details . isParamSpec ) { sawParamSpecArgs = true ; } } } else if ( param . category === ParameterCategory . VarArgDictionary ) { sawParamSpecArgs = false ; } } if ( param . name && param . category === ParameterCategory . Simple && sawParamSpecArgs ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . namedParamAfterParamSpecArgs ( ) . format ( { name : param . name . value } ) , param . name ) ; } if ( param . name && param . name . value !== '_' ) { if ( index < functionTypeResult . functionType . details . parameters . length ) { const paramType = functionTypeResult . functionType . details . parameters [ index ] . type ; if ( isUnknown ( paramType ) || ( isTypeVar ( paramType ) && paramType . details . isSynthesized && ! paramType . details . isSynthesizedSelf ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnknownParameterType , DiagnosticRule . reportUnknownParameterType , Localizer . Diagnostic . paramTypeUnknown ( ) . format ( { paramName : param . name . value } ) , param . name ) ; } else if ( isPartlyUnknown ( paramType ) ) { const diagAddendum = new DiagnosticAddendum ( ) ; diagAddendum . addMessage ( Localizer . DiagnosticAddendum . paramType ( ) . format ( { paramType : this . _evaluator . printType ( paramType , true ) , } ) ) ; this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnknownParameterType , DiagnosticRule . reportUnknownParameterType , Localizer . Diagnostic . paramTypePartiallyUnknown ( ) . format ( { paramName : param . name . value , } ) + diagAddendum . getString ( ) , param . name ) ; } let hasAnnotation = false ; if ( functionTypeResult . functionType . details . parameters [ index ] . typeAnnotation ) { hasAnnotation = true ; } else { if ( isTypeVar ( paramType ) && paramType . details . isSynthesizedSelf ) { hasAnnotation = true ; } } if ( ! hasAnnotation ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportMissingParameterType , DiagnosticRule . reportMissingParameterType , Localizer . Diagnostic . paramAnnotationMissing ( ) . format ( { name : param . name . value } ) , param . name ) ; } } } if ( param . defaultValue && this . _fileInfo . isStubFile ) { const defaultValueType = this . _evaluator . getType ( param . defaultValue ) ; if ( ! defaultValueType || ! isEllipsisType ( defaultValueType ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportInvalidStubStatement , DiagnosticRule . reportInvalidStubStatement , Localizer . Diagnostic . defaultValueNotEllipsis ( ) , param . defaultValue ) ; } } } ) ; const paramSpecParams = node . parameters . filter ( ( param , index ) => { const paramInfo = functionTypeResult . functionType . details . parameters [ index ] ; if ( paramInfo . typeAnnotation && isTypeVar ( paramInfo . type ) && isParamSpec ( paramInfo . type ) ) { if ( paramInfo . category !== ParameterCategory . Simple ) { const paramAnnotation = paramInfo . typeAnnotation . nodeType === ParseNodeType . StringList ? paramInfo . typeAnnotation . typeAnnotation : paramInfo . typeAnnotation ; if ( paramAnnotation ?. nodeType === ParseNodeType . MemberAccess ) { return true ; } } } return false ; } ) ; if ( paramSpecParams . length === 1 ) { this . _evaluator . addError ( Localizer . Diagnostic . paramSpecArgsKwargsUsage ( ) , paramSpecParams [ 0 ] . typeAnnotation || paramSpecParams [ 0 ] . typeAnnotationComment ! ) ; } if ( this . _fileInfo . isStubFile ) { const returnAnnotation = node . returnTypeAnnotation || node . functionAnnotationComment ?. returnTypeAnnotation ; if ( ! returnAnnotation ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnknownParameterType , DiagnosticRule . reportUnknownParameterType , Localizer . Diagnostic . returnTypeUnknown ( ) , node . name ) ; } } if ( containingClassNode ) { this . _validateMethod ( node , functionTypeResult . functionType , containingClassNode ) ; } } node . parameters . forEach ( ( param , index ) => { if ( param . defaultValue ) { this . walk ( param . defaultValue ) ; } if ( param . typeAnnotation ) { this . walk ( param . typeAnnotation ) ; } if ( param . typeAnnotationComment ) { this . walk ( param . typeAnnotationComment ) ; } if ( functionTypeResult ) { const annotationNode = param . typeAnnotation || param . typeAnnotationComment ; if ( annotationNode ) { const paramType = functionTypeResult . functionType . details . parameters [ index ] . type ; if ( isTypeVar ( paramType ) && paramType . details . variance === Variance . Covariant && ! paramType . details . isSynthesized && functionTypeResult . functionType . details . name !== '__init__' ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . paramTypeCovariant ( ) , annotationNode ) ; } } } } ) ; if ( node . returnTypeAnnotation ) { this . walk ( node . returnTypeAnnotation ) ; } if ( node . functionAnnotationComment ) { this . walk ( node . functionAnnotationComment ) ; } this . walkMultiple ( node . decorators ) ; node . parameters . forEach ( ( param ) => { if ( param . name ) { this . walk ( param . name ) ; } } ) ; this . walk ( node . suite ) ; if ( functionTypeResult ) { this . _validateFunctionReturn ( node , functionTypeResult . functionType ) ; this . _validateDunderSignatures ( node , functionTypeResult . functionType , containingClassNode !== undefined ) ; } if ( this . _fileInfo . isStubFile && node . name . value === '__getattr__' ) { const scope = getScopeForNode ( node ) ; if ( scope ?. type === ScopeType . Module ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportIncompleteStub , DiagnosticRule . reportIncompleteStub , Localizer . Diagnostic . stubUsesGetAttr ( ) , node . name ) ; } } this . _scopedNodes . push ( node ) ; this . _validateFunctionTypeVarUsage ( node ) ; if ( functionTypeResult && isOverloadedFunction ( functionTypeResult . decoratedType ) ) { const overloads = functionTypeResult . decoratedType . overloads ; if ( overloads . length > 1 ) { const maxOverloadConsistencyCheckLength = 100 ; if ( overloads . length < maxOverloadConsistencyCheckLength ) { this . _validateOverloadConsistency ( node , overloads [ overloads . length - 1 ] , overloads . slice ( 0 , overloads . length - 1 ) ) ; } } } return false ; } override visitLambda ( node ) { this . _evaluator . getType ( node ) ; this . walkMultiple ( [ ... node . parameters , node . expression ] ) ; node . parameters . forEach ( ( param ) => { if ( param . name ) { const paramType = this . _evaluator . getType ( param . name ) ; if ( paramType ) { if ( isUnknown ( paramType ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnknownLambdaType , DiagnosticRule . reportUnknownLambdaType , Localizer . Diagnostic . paramTypeUnknown ( ) . format ( { paramName : param . name . value } ) , param . name ) ; } else if ( isPartlyUnknown ( paramType ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnknownLambdaType , DiagnosticRule . reportUnknownLambdaType , Localizer . Diagnostic . paramTypePartiallyUnknown ( ) . format ( { paramName : param . name . value } ) , param . name ) ; } } } } ) ; const returnType = this . _evaluator . getType ( node . expression ) ; if ( returnType ) { if ( isUnknown ( returnType ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnknownLambdaType , DiagnosticRule . reportUnknownLambdaType , Localizer . Diagnostic . lambdaReturnTypeUnknown ( ) , node . expression ) ; } else if ( isPartlyUnknown ( returnType ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnknownLambdaType , DiagnosticRule . reportUnknownLambdaType , Localizer . Diagnostic . lambdaReturnTypePartiallyUnknown ( ) . format ( { returnType : this . _evaluator . printType ( returnType , true ) , } ) , node . expression ) ; } } this . _scopedNodes . push ( node ) ; return false ; } override visitCall ( node ) { this . _validateIsInstanceCall ( node ) ; this . _validateIllegalDefaultParamInitializer ( node ) ; if ( this . _fileInfo . diagnosticRuleSet . reportUnusedCallResult !== 'none' || this . _fileInfo . diagnosticRuleSet . reportUnusedCoroutine !== 'none' ) { if ( node . parent ?. nodeType === ParseNodeType . StatementList ) { const returnType = this . _evaluator . getType ( node ) ; if ( returnType && this . _isTypeValidForUnusedValueTest ( returnType ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnusedCallResult , DiagnosticRule . reportUnusedCallResult , Localizer . Diagnostic . unusedCallResult ( ) . format ( { type : this . _evaluator . printType ( returnType , false ) , } ) , node ) ; if ( isClassInstance ( returnType ) && ClassType . isBuiltIn ( returnType , 'Coroutine' ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnusedCoroutine , DiagnosticRule . reportUnusedCoroutine , Localizer . Diagnostic . unusedCoroutine ( ) , node ) ; } } } } return true ; } override visitAwait ( node ) { if ( this . _fileInfo . diagnosticRuleSet . reportUnusedCallResult !== 'none' ) { if ( node . parent ?. nodeType === ParseNodeType . StatementList && node . expression . nodeType === ParseNodeType . Call ) { const returnType = this . _evaluator . getType ( node ) ; if ( returnType && this . _isTypeValidForUnusedValueTest ( returnType ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnusedCallResult , DiagnosticRule . reportUnusedCallResult , Localizer . Diagnostic . unusedCallResult ( ) . format ( { type : this . _evaluator . printType ( returnType , false ) , } ) , node ) ; } } } return true ; } override visitFor ( node ) { this . _evaluator . evaluateTypesForStatement ( node ) ; return true ; } override visitList ( node ) { this . _validateIllegalDefaultParamInitializer ( node ) ; return true ; } override visitSet ( node ) { this . _validateIllegalDefaultParamInitializer ( node ) ; return true ; } override visitDictionary ( node ) { this . _validateIllegalDefaultParamInitializer ( node ) ; return true ; } override visitListComprehension ( node ) { this . _scopedNodes . push ( node ) ; return true ; } override visitIf ( node ) { this . _evaluator . getType ( node . testExpression ) ; return true ; } override visitWhile ( node ) { this . _evaluator . getType ( node . testExpression ) ; return true ; } override visitWith ( node ) { node . withItems . forEach ( ( item ) => { this . _evaluator . evaluateTypesForStatement ( item ) ; } ) ; return true ; } override visitReturn ( node ) { let returnType ; const enclosingFunctionNode = ParseTreeUtils . getEnclosingFunction ( node ) ; const declaredReturnType = enclosingFunctionNode ? this . _evaluator . getFunctionDeclaredReturnType ( enclosingFunctionNode ) : undefined ; if ( node . returnExpression ) { returnType = this . _evaluator . getType ( node . returnExpression ) || UnknownType . create ( ) ; } else { returnType = NoneType . createInstance ( ) ; } if ( this . _evaluator . isNodeReachable ( node , undefined ) && enclosingFunctionNode ) { if ( declaredReturnType ) { if ( isNoReturnType ( declaredReturnType ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . noReturnContainsReturn ( ) , node ) ; } else { const diagAddendum = new DiagnosticAddendum ( ) ; let returnTypeMatches = false ; if ( this . _evaluator . canAssignType ( declaredReturnType , returnType , diagAddendum , new TypeVarMap ( ) , CanAssignFlags . AllowBoolTypeGuard ) ) { returnTypeMatches = true ; } else { const uniqueTypeVars = getTypeVarArgumentsRecursive ( declaredReturnType ) ; if ( uniqueTypeVars && uniqueTypeVars . some ( ( typeVar ) => typeVar . details . constraints . length > 0 ) ) { const typeVarMap = new TypeVarMap ( ) ; for ( const typeVar of uniqueTypeVars ) { if ( typeVar . details . constraints . length > 0 ) { const narrowedType = this . _evaluator . narrowConstrainedTypeVar ( node , typeVar ) ; if ( narrowedType ) { typeVarMap . setTypeVarType ( typeVar , narrowedType ) ; typeVarMap . addSolveForScope ( getTypeVarScopeId ( typeVar ) ) ; } } } if ( ! typeVarMap . isEmpty ( ) ) { const adjustedReturnType = applySolvedTypeVars ( declaredReturnType , typeVarMap ) ; if ( this . _evaluator . canAssignType ( adjustedReturnType , returnType , diagAddendum , undefined , CanAssignFlags . AllowBoolTypeGuard ) ) { returnTypeMatches = true ; } } } } if ( ! returnTypeMatches ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . returnTypeMismatch ( ) . format ( { exprType : this . _evaluator . printType ( returnType , false ) , returnType : this . _evaluator . printType ( declaredReturnType , false ) , } ) + diagAddendum . getString ( ) , node . returnExpression ? node . returnExpression : node ) ; } } } if ( isUnknown ( returnType ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnknownVariableType , DiagnosticRule . reportUnknownVariableType , Localizer . Diagnostic . returnTypeUnknown ( ) , node . returnExpression ! ) ; } else if ( isPartlyUnknown ( returnType ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnknownVariableType , DiagnosticRule . reportUnknownVariableType , Localizer . Diagnostic . returnTypePartiallyUnknown ( ) . format ( { returnType : this . _evaluator . printType ( returnType , true ) , } ) , node . returnExpression ! ) ; } } return true ; } override visitYield ( node ) { const yieldType = node . expression ? this . _evaluator . getType ( node . expression ) : NoneType . createInstance ( ) ; this . _validateYieldType ( node , yieldType || UnknownType . create ( ) ) ; return true ; } override visitYieldFrom ( node ) { const yieldFromType = this . _evaluator . getType ( node . expression ) || UnknownType . create ( ) ; let yieldType = this . _evaluator . getTypeFromIterable ( yieldFromType , false , node ) || UnknownType . create ( ) ; const generatorTypeArgs = getGeneratorTypeArgs ( yieldType ) ; if ( generatorTypeArgs ) { yieldType = generatorTypeArgs . length >= 1 ? generatorTypeArgs [ 0 ] : UnknownType . create ( ) ; } else { yieldType = this . _evaluator . getTypeFromIterator ( yieldFromType , false , node ) || UnknownType . create ( ) ; } this . _validateYieldType ( node , yieldType ) ; return true ; } override visitRaise ( node ) { this . _evaluator . verifyRaiseExceptionType ( node ) ; if ( node . valueExpression ) { const baseExceptionType = this . _evaluator . getBuiltInType ( node , 'BaseException' ) as ClassType ; const exceptionType = this . _evaluator . getType ( node . valueExpression ) ; if ( exceptionType && baseExceptionType && isInstantiableClass ( baseExceptionType ) ) { const diagAddendum = new DiagnosticAddendum ( ) ; doForEachSubtype ( exceptionType , ( subtype ) => { subtype = this . _evaluator . makeTopLevelTypeVarsConcrete ( subtype ) ; if ( ! isAnyOrUnknown ( subtype ) && ! isNoneInstance ( subtype ) ) { if ( isClass ( subtype ) ) { if ( ! derivesFromClassRecursive ( subtype , baseExceptionType , false ) ) { diagAddendum . addMessage ( Localizer . Diagnostic . exceptionTypeIncorrect ( ) . format ( { type : this . _evaluator . printType ( subtype , false ) , } ) ) ; } } else { diagAddendum . addMessage ( Localizer . Diagnostic . exceptionTypeIncorrect ( ) . format ( { type : this . _evaluator . printType ( subtype , false ) , } ) ) ; } } } ) ; if ( ! diagAddendum . isEmpty ( ) ) { this . _evaluator . addError ( Localizer . Diagnostic . expectedExceptionObj ( ) + diagAddendum . getString ( ) , node . valueExpression ) ; } } } return true ; } override visitExcept ( node ) { if ( node . typeExpression ) { this . _evaluator . evaluateTypesForStatement ( node ) ; const exceptionType = this . _evaluator . getType ( node . typeExpression ) ; if ( exceptionType ) { this . _validateExceptionType ( exceptionType , node . typeExpression ) ; } } return true ; } override visitAssert ( node ) { if ( node . exceptionExpression ) { this . _evaluator . getType ( node . exceptionExpression ) ; } const type = this . _evaluator . getType ( node . testExpression ) ; if ( type && isClassInstance ( type ) ) { if ( isTupleClass ( type ) && type . tupleTypeArguments ) { if ( type . tupleTypeArguments . length > 0 ) { if ( ! isOpenEndedTupleClass ( type ) ) { this . _evaluator . addDiagnosticForTextRange ( this . _fileInfo , this . _fileInfo . diagnosticRuleSet . reportAssertAlwaysTrue , DiagnosticRule . reportAssertAlwaysTrue , Localizer . Diagnostic . assertAlwaysTrue ( ) , node . testExpression ) ; } } } } return true ; } override visitAssignment ( node ) { this . _evaluator . evaluateTypesForStatement ( node ) ; if ( node . typeAnnotationComment ) { this . _evaluator . getType ( node . typeAnnotationComment ) ; } return true ; } override visitAssignmentExpression ( node ) { this . _evaluator . getType ( node ) ; return true ; } override visitAugmentedAssignment ( node ) { this . _evaluator . evaluateTypesForStatement ( node ) ; return true ; } override visitIndex ( node ) { this . _evaluator . getType ( node ) ; const baseType = this . _evaluator . getType ( node . baseExpression ) ; if ( baseType && isClassInstance ( baseType ) && baseType . tupleTypeArguments && ! isOpenEndedTupleClass ( baseType ) ) { const tupleLength = baseType . tupleTypeArguments . length ; if ( node . items . length === 1 && ! node . trailingComma && node . items [ 0 ] . argumentCategory === ArgumentCategory . Simple && ! node . items [ 0 ] . name ) { const subscriptType = this . _evaluator . getType ( node . items [ 0 ] . valueExpression ) ; if ( subscriptType && isClassInstance ( subscriptType ) && ClassType . isBuiltIn ( subscriptType , 'int' ) && isLiteralType ( subscriptType ) ) { const subscriptValue = subscriptType . literalValue as number ; if ( ( subscriptValue >= 0 && subscriptValue >= tupleLength ) || ( subscriptValue < 0 && subscriptValue + tupleLength < 0 ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . tupleIndexOutOfRange ( ) . format ( { length : tupleLength , index : subscriptValue , } ) , node ) ; } } } } return true ; } override visitBinaryOperation ( node ) { if ( node . operator === OperatorType . Equals || node . operator === OperatorType . NotEquals ) { if ( ! ParseTreeUtils . isWithinAssertExpression ( node ) ) { this . _validateComparisonTypes ( node ) ; } } this . _evaluator . getType ( node ) ; return true ; } override visitSlice ( node ) { this . _evaluator . getType ( node ) ; return true ; } override visitUnpack ( node ) { this . _evaluator . getType ( node ) ; return true ; } override visitTuple ( node ) { this . _evaluator . getType ( node ) ; return true ; } override visitUnaryOperation ( node ) { this . _evaluator . getType ( node ) ; return true ; } override visitTernary ( node ) { this . _evaluator . getType ( node ) ; return true ; } override visitStringList ( node ) { for ( const stringNode of node . strings ) { if ( stringNode . hasUnescapeErrors ) { const unescapedResult = getUnescapedString ( stringNode . token ) ; unescapedResult . unescapeErrors . forEach ( ( error ) => { const start = stringNode . token . start + stringNode . token . prefixLength + stringNode . token . quoteMarkLength + error . offset ; const textRange = { start , length : error . length } ; if ( error . errorType === UnescapeErrorType . InvalidEscapeSequence ) { this . _evaluator . addDiagnosticForTextRange ( this . _fileInfo , this . _fileInfo . diagnosticRuleSet . reportInvalidStringEscapeSequence , DiagnosticRule . reportInvalidStringEscapeSequence , Localizer . Diagnostic . stringUnsupportedEscape ( ) , textRange ) ; } else if ( error . errorType === UnescapeErrorType . EscapeWithinFormatExpression ) { this . _evaluator . addDiagnosticForTextRange ( this . _fileInfo , 'error' , '' , Localizer . Diagnostic . formatStringEscape ( ) , textRange ) ; } else if ( error . errorType === UnescapeErrorType . SingleCloseBraceWithinFormatLiteral ) { this . _evaluator . addDiagnosticForTextRange ( this . _fileInfo , 'error' , '' , Localizer . Diagnostic . formatStringBrace ( ) , textRange ) ; } else if ( error . errorType === UnescapeErrorType . UnterminatedFormatExpression ) { this . _evaluator . addDiagnosticForTextRange ( this . _fileInfo , 'error' , '' , Localizer . Diagnostic . formatStringUnterminated ( ) , textRange ) ; } } ) ; } } if ( node . typeAnnotation ) { this . _evaluator . getType ( node ) ; } if ( node . strings . length > 1 && ! node . isParenthesized ) { this . _evaluator . addDiagnosticForTextRange ( this . _fileInfo , this . _fileInfo . diagnosticRuleSet . reportImplicitStringConcatenation , DiagnosticRule . reportImplicitStringConcatenation , Localizer . Diagnostic . implicitStringConcat ( ) , node ) ; } return true ; } override visitFormatString ( node ) { node . expressions . forEach ( ( formatExpr ) => { this . _evaluator . getType ( formatExpr ) ; } ) ; return true ; } override visitGlobal ( node ) { this . _suppressUnboundCheck ( ( ) => { node . nameList . forEach ( ( name ) => { this . _evaluator . getType ( name ) ; this . walk ( name ) ; } ) ; } ) ; return false ; } override visitNonlocal ( node ) { this . _suppressUnboundCheck ( ( ) => { node . nameList . forEach ( ( name ) => { this . _evaluator . getType ( name ) ; this . walk ( name ) ; } ) ; } ) ; return false ; } override visitName ( node ) { this . _conditionallyReportPrivateUsage ( node ) ; if ( ! this . _isUnboundCheckSuppressed ) { this . _reportUnboundName ( node ) ; } return true ; } override visitDel ( node ) { this . _suppressUnboundCheck ( ( ) => { node . expressions . forEach ( ( expr ) => { this . _evaluator . verifyDeleteExpression ( expr ) ; this . walk ( expr ) ; } ) ; } ) ; return false ; } override visitMemberAccess ( node ) { this . _evaluator . getType ( node ) ; this . _conditionallyReportPrivateUsage ( node . memberName ) ; this . walk ( node . leftExpression ) ; return false ; } override visitImportAs ( node ) { this . _evaluator . evaluateTypesForStatement ( node ) ; return false ; } override visitImportFrom ( node ) { if ( ! node . isWildcardImport ) { node . imports . forEach ( ( importAs ) => { this . _evaluator . evaluateTypesForStatement ( importAs ) ; } ) ; } else { const importInfo = AnalyzerNodeInfo . getImportInfo ( node . module ) ; if ( importInfo && importInfo . isImportFound && importInfo . importType !== ImportType . Local && ! this . _fileInfo . isStubFile ) { this . _evaluator . addDiagnosticForTextRange ( this . _fileInfo , this . _fileInfo . diagnosticRuleSet . reportWildcardImportFromLibrary , DiagnosticRule . reportWildcardImportFromLibrary , Localizer . Diagnostic . wildcardLibraryImport ( ) , node . wildcardToken || node ) ; } } return false ; } override visitTypeAnnotation ( node ) { this . _evaluator . getType ( node . typeAnnotation ) ; return true ; } override visitMatch ( node ) { this . _evaluator . getType ( node . subjectExpression ) ; return true ; } override visitCase ( node ) { if ( node . guardExpression ) { this . _evaluator . getType ( node . guardExpression ) ; } this . _evaluator . evaluateTypesForStatement ( node . pattern ) ; return true ; } override visitPatternClass ( node ) { validateClassPattern ( this . _evaluator , node ) ; return true ; } override visitTry ( node ) { this . _reportUnusedExceptStatements ( node ) ; return true ; } override visitError ( node ) { if ( node . child ) { this . _evaluator . getType ( node . child ) ; } return false ; } private _suppressUnboundCheck ( callback ) { const wasSuppressed = this . _isUnboundCheckSuppressed ; this . _isUnboundCheckSuppressed = true ; try { callback ( ) ; } finally { this . _isUnboundCheckSuppressed = wasSuppressed ; } } private _validateIllegalDefaultParamInitializer ( node ) { if ( this . _fileInfo . diagnosticRuleSet . reportCallInDefaultInitializer !== 'none' ) { if ( ParseTreeUtils . isWithinDefaultParamInitializer ( node ) && ! this . _fileInfo . isStubFile ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportCallInDefaultInitializer , DiagnosticRule . reportCallInDefaultInitializer , Localizer . Diagnostic . defaultValueContainsCall ( ) , node ) ; } } } private _validateComparisonTypes ( node ) { const leftType = this . _evaluator . getType ( node . leftExpression ) ; const rightType = this . _evaluator . getType ( node . rightExpression ) ; if ( ! leftType || ! rightType ) { return ; } if ( isLiteralTypeOrUnion ( rightType ) && isLiteralTypeOrUnion ( leftType ) ) { if ( evaluateStaticBoolExpression ( node , this . _fileInfo . executionEnvironment ) === undefined ) { let isPossiblyTrue = false ; doForEachSubtype ( leftType , ( leftSubtype ) => { if ( this . _evaluator . canAssignType ( rightType , leftSubtype ) ) { isPossiblyTrue = true ; } } ) ; if ( ! isPossiblyTrue ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnnecessaryComparison , DiagnosticRule . reportUnnecessaryComparison , Localizer . Diagnostic . comparisonAlwaysFalse ( ) . format ( { leftType : this . _evaluator . printType ( leftType , true ) , rightType : this . _evaluator . printType ( rightType , true ) , } ) , node ) ; } } } else { let isComparable = false ; doForEachSubtype ( leftType , ( leftSubtype ) => { if ( isComparable ) { return ; } leftSubtype = this . _evaluator . makeTopLevelTypeVarsConcrete ( leftSubtype ) ; doForEachSubtype ( rightType , ( rightSubtype ) => { if ( isComparable ) { return ; } rightSubtype = this . _evaluator . makeTopLevelTypeVarsConcrete ( rightSubtype ) ; if ( this . _isTypeComparable ( leftSubtype , rightSubtype ) ) { isComparable = true ; } } ) ; } ) ; if ( ! isComparable ) { const leftTypeText = this . _evaluator . printType ( leftType , true ) ; const rightTypeText = this . _evaluator . printType ( rightType , true ) ; const message = node . operator === OperatorType . Equals ? Localizer . Diagnostic . comparisonAlwaysFalse ( ) : Localizer . Diagnostic . comparisonAlwaysTrue ( ) ; this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnnecessaryComparison , DiagnosticRule . reportUnnecessaryComparison , message . format ( { leftType : leftTypeText , rightType : rightTypeText , } ) , node ) ; } } } private _isTypeComparable ( leftType , rightType ) { if ( isAnyOrUnknown ( leftType ) || isAnyOrUnknown ( rightType ) ) { return true ; } if ( isNever ( leftType ) || isNever ( rightType ) ) { return false ; } if ( isModule ( leftType ) || isModule ( rightType ) ) { return ! isTypeSame ( leftType , rightType ) ; } if ( isNoneInstance ( leftType ) || isNoneInstance ( rightType ) ) { return ! isTypeSame ( leftType , rightType ) ; } if ( isInstantiableClass ( leftType ) || ( isClassInstance ( leftType ) && ClassType . isBuiltIn ( leftType , 'type' ) ) ) { if ( isInstantiableClass ( rightType ) || ( isClassInstance ( rightType ) && ClassType . isBuiltIn ( rightType , 'type' ) ) ) { const genericLeftType = ClassType . cloneForSpecialization ( leftType , undefined , false ) ; const genericRightType = ClassType . cloneForSpecialization ( rightType , undefined , false ) ; if ( this . _evaluator . canAssignType ( genericLeftType , genericRightType ) || this . _evaluator . canAssignType ( genericRightType , genericLeftType ) ) { return true ; } } const metaclass = leftType . details . effectiveMetaclass ; if ( metaclass && isClass ( metaclass ) ) { if ( lookUpClassMember ( metaclass , '__eq__' , ClassMemberLookupFlags . SkipObjectBaseClass ) ) { return true ; } } return false ; } if ( isClassInstance ( leftType ) ) { if ( isClassInstance ( rightType ) ) { const genericLeftType = ClassType . cloneForSpecialization ( leftType , undefined , false ) ; const genericRightType = ClassType . cloneForSpecialization ( rightType , undefined , false ) ; if ( this . _evaluator . canAssignType ( genericLeftType , genericRightType ) || this . _evaluator . canAssignType ( genericRightType , genericLeftType ) ) { return true ; } } if ( lookUpClassMember ( ClassType . cloneAsInstantiable ( leftType ) , '__eq__' , ClassMemberLookupFlags . SkipObjectBaseClass ) ) { return true ; } return false ; } return true ; } private _isTypeValidForUnusedValueTest ( type ) { return ! isNoneInstance ( type ) && ! isNoReturnType ( type ) && ! isNever ( type ) && ! isAnyOrUnknown ( type ) ; } private _validateFunctionTypeVarUsage ( node ) { if ( this . _fileInfo . diagnosticRuleSet . reportInvalidTypeVarUse === 'none' ) { return ; } const localTypeVarUsage = new Map < string , LocalTypeVarInfo > ( ) ; let exemptBoundTypeVar = true ; const nameWalker = new ParseTreeUtils . NameNodeWalker ( ( nameNode , subscriptIndex , baseExpression ) => { const nameType = this . _evaluator . getType ( nameNode ) ; `` ; if ( nameType && isTypeVar ( nameType ) ) { if ( nameType . scopeId === this . _evaluator . getScopeIdForNode ( node ) ) { let isExempt = nameType . details . constraints . length > 0 || ( exemptBoundTypeVar && nameType . details . boundType !== undefined && subscriptIndex !== undefined ) || isParamSpec ( nameType ) ; if ( ! isExempt && baseExpression && subscriptIndex !== undefined ) { const baseType = this . _evaluator . getType ( baseExpression ) ; if ( baseType ?. typeAliasInfo && baseType . typeAliasInfo . typeParameters && subscriptIndex < baseType . typeAliasInfo . typeParameters . length ) { isExempt = true ; } } if ( ! localTypeVarUsage . has ( nameType . details . name ) ) { localTypeVarUsage . set ( nameType . details . name , { nodes : [ nameNode ] , isExempt , } ) ; } else { localTypeVarUsage . get ( nameType . details . name ) ! . nodes . push ( nameNode ) ; } } } } ) ; node . parameters . forEach ( ( param ) => { const annotation = param . typeAnnotation || param . typeAnnotationComment ; if ( annotation ) { nameWalker . walk ( annotation ) ; } } ) ; if ( node . returnTypeAnnotation ) { exemptBoundTypeVar = false ; nameWalker . walk ( node . returnTypeAnnotation ) ; } localTypeVarUsage . forEach ( ( usage ) => { if ( usage . nodes . length === 1 && ! usage . isExempt ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportInvalidTypeVarUse , DiagnosticRule . reportInvalidTypeVarUse , Localizer . Diagnostic . typeVarUsedOnlyOnce ( ) . format ( { name : usage . nodes [ 0 ] . value , } ) , usage . nodes [ 0 ] ) ; } } ) ; } private _validateOverloadConsistency ( node , functionType , prevOverloads ) { for ( let i = 0 ; i < prevOverloads . length ; i ++ ) { const prevOverload = prevOverloads [ i ] ; if ( FunctionType . isOverloaded ( functionType ) && FunctionType . isOverloaded ( prevOverload ) && this . _isOverlappingOverload ( functionType , prevOverload ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportOverlappingOverload , DiagnosticRule . reportOverlappingOverload , Localizer . Diagnostic . overlappingOverload ( ) . format ( { name : node . name . value , obscured : prevOverloads . length + 1 , obscuredBy : i + 1 , } ) , node . name ) ; break ; } } for ( let i = 0 ; i < prevOverloads . length ; i ++ ) { const prevOverload = prevOverloads [ i ] ; if ( FunctionType . isOverloaded ( functionType ) && FunctionType . isOverloaded ( prevOverload ) && this . _isOverlappingOverload ( prevOverload , functionType ) ) { const prevReturnType = FunctionType . getSpecializedReturnType ( prevOverload ) ; const returnType = FunctionType . getSpecializedReturnType ( functionType ) ; if ( prevReturnType && returnType && ! this . _evaluator . canAssignType ( returnType , prevReturnType , undefined , new TypeVarMap ( ) , CanAssignFlags . SkipSolveTypeVars ) ) { const altNode = this . _findNodeForOverload ( node , prevOverload ) ; this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportOverlappingOverload , DiagnosticRule . reportOverlappingOverload , Localizer . Diagnostic . overloadReturnTypeMismatch ( ) . format ( { name : node . name . value , newIndex : prevOverloads . length + 1 , prevIndex : i + 1 , } ) , ( altNode || node ) . name ) ; break ; } } } } private _findNodeForOverload ( functionNode , overloadType ) { const decls = this . _evaluator . getDeclarationsForNameNode ( functionNode . name ) ; if ( ! decls ) { return undefined ; } for ( const decl of decls ) { if ( decl . type === DeclarationType . Function ) { const functionType = this . _evaluator . getTypeOfFunction ( decl . node ) ; if ( functionType ?. functionType === overloadType ) { return decl . node ; } } } return undefined ; } private _isOverlappingOverload ( functionType , prevOverload ) { if ( FunctionType . isInstanceMethod ( functionType ) && functionType . details . name === '__get__' ) { return false ; } return this . _evaluator . canAssignType ( functionType , prevOverload , undefined , undefined , CanAssignFlags . SkipSolveTypeVars | CanAssignFlags . SkipFunctionReturnTypeCheck | CanAssignFlags . OverloadOverlapCheck ) ; } private _isLegalOverloadImplementation ( overload , implementation , diag ) { const typeVarMap = new TypeVarMap ( getTypeVarScopeId ( implementation ) ) ; let isLegal = this . _evaluator . canAssignType ( overload , implementation , diag , typeVarMap , CanAssignFlags . SkipFunctionReturnTypeCheck | CanAssignFlags . ReverseTypeVarMatching | CanAssignFlags . SkipSelfClsTypeCheck ) ; const overloadReturnType = removeNoReturnFromUnion ( overload . details . declaredReturnType || this . _evaluator . getFunctionInferredReturnType ( overload ) ) ; const implementationReturnType = removeNoReturnFromUnion ( applySolvedTypeVars ( implementation . details . declaredReturnType || this . _evaluator . getFunctionInferredReturnType ( implementation ) , typeVarMap ) ) ; const returnDiag = new DiagnosticAddendum ( ) ; if ( ! isNoReturnType ( overloadReturnType ) && ! this . _evaluator . canAssignType ( implementationReturnType , overloadReturnType , returnDiag . createAddendum ( ) , typeVarMap , CanAssignFlags . SkipSolveTypeVars ) ) { returnDiag . addMessage ( Localizer . DiagnosticAddendum . functionReturnTypeMismatch ( ) . format ( { sourceType : this . _evaluator . printType ( overloadReturnType , false ) , destType : this . _evaluator . printType ( implementationReturnType , false ) , } ) ) ; if ( diag ) { diag . addAddendum ( returnDiag ) ; } isLegal = false ; } return isLegal ; } private _walkStatementsAndReportUnreachable ( statements ) { let reportedUnreachable = false ; let prevStatement ; for ( const statement of statements ) { if ( ! reportedUnreachable ) { if ( ! this . _evaluator . isNodeReachable ( statement , prevStatement ) ) { const start = statement . start ; const lastStatement = statements [ statements . length - 1 ] ; const end = TextRange . getEnd ( lastStatement ) ; this . _evaluator . addUnusedCode ( statement , { start , length : end - start } ) ; reportedUnreachable = true ; } } if ( ! reportedUnreachable && this . _fileInfo . isStubFile ) { this . _validateStubStatement ( statement ) ; } this . walk ( statement ) ; prevStatement = statement ; } } private _validateStubStatement ( statement ) { switch ( statement . nodeType ) { case ParseNodeType . If : case ParseNodeType . Function : case ParseNodeType . Class : case ParseNodeType . Error : { break ; } case ParseNodeType . While : case ParseNodeType . For : case ParseNodeType . Try : case ParseNodeType . With : { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportInvalidStubStatement , DiagnosticRule . reportInvalidStubStatement , Localizer . Diagnostic . invalidStubStatement ( ) , statement ) ; break ; } case ParseNodeType . StatementList : { for ( const substatement of statement . statements ) { switch ( substatement . nodeType ) { case ParseNodeType . Assert : case ParseNodeType . AssignmentExpression : case ParseNodeType . AugmentedAssignment : case ParseNodeType . Await : case ParseNodeType . BinaryOperation : case ParseNodeType . Call : case ParseNodeType . Constant : case ParseNodeType . Del : case ParseNodeType . Dictionary : case ParseNodeType . Index : case ParseNodeType . For : case ParseNodeType . FormatString : case ParseNodeType . Global : case ParseNodeType . Lambda : case ParseNodeType . List : case ParseNodeType . MemberAccess : case ParseNodeType . Name : case ParseNodeType . Nonlocal : case ParseNodeType . Number : case ParseNodeType . Raise : case ParseNodeType . Return : case ParseNodeType . Set : case ParseNodeType . Slice : case ParseNodeType . Ternary : case ParseNodeType . Tuple : case ParseNodeType . Try : case ParseNodeType . UnaryOperation : case ParseNodeType . Unpack : case ParseNodeType . While : case ParseNodeType . With : case ParseNodeType . WithItem : case ParseNodeType . Yield : case ParseNodeType . YieldFrom : { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportInvalidStubStatement , DiagnosticRule . reportInvalidStubStatement , Localizer . Diagnostic . invalidStubStatement ( ) , substatement ) ; } } } } } } private _validateExceptionType ( exceptionType , errorNode ) { const baseExceptionType = this . _evaluator . getBuiltInType ( errorNode , 'BaseException' ) ; const derivesFromBaseException = ( classType ) => { if ( ! baseExceptionType || ! isInstantiableClass ( baseExceptionType ) ) { return true ; } return derivesFromClassRecursive ( classType , baseExceptionType , false ) ; } ; const diagAddendum = new DiagnosticAddendum ( ) ; let resultingExceptionType ; if ( isAnyOrUnknown ( exceptionType ) ) { resultingExceptionType = exceptionType ; } else { if ( isInstantiableClass ( exceptionType ) ) { if ( ! derivesFromBaseException ( exceptionType ) ) { diagAddendum . addMessage ( Localizer . Diagnostic . exceptionTypeIncorrect ( ) . format ( { type : this . _evaluator . printType ( exceptionType , false ) , } ) ) ; } resultingExceptionType = ClassType . cloneAsInstance ( exceptionType ) ; } else if ( isClassInstance ( exceptionType ) ) { const iterableType = this . _evaluator . getTypeFromIterator ( exceptionType , false , errorNode ) || UnknownType . create ( ) ; resultingExceptionType = mapSubtypes ( iterableType , ( subtype ) => { if ( isAnyOrUnknown ( subtype ) ) { return subtype ; } if ( isInstantiableClass ( subtype ) ) { if ( ! derivesFromBaseException ( subtype ) ) { diagAddendum . addMessage ( Localizer . Diagnostic . exceptionTypeIncorrect ( ) . format ( { type : this . _evaluator . printType ( exceptionType , false ) , } ) ) ; } return ClassType . cloneAsInstance ( subtype ) ; } diagAddendum . addMessage ( Localizer . Diagnostic . exceptionTypeIncorrect ( ) . format ( { type : this . _evaluator . printType ( exceptionType , false ) , } ) ) ; return UnknownType . create ( ) ; } ) ; } } if ( ! diagAddendum . isEmpty ( ) ) { this . _evaluator . addError ( Localizer . Diagnostic . exceptionTypeNotClass ( ) . format ( { type : this . _evaluator . printType ( exceptionType , false ) , } ) , errorNode ) ; } return resultingExceptionType || UnknownType . create ( ) ; } private _reportUnusedDunderAllSymbols ( nodes ) { if ( this . _fileInfo . diagnosticRuleSet . reportUnsupportedDunderAll === 'none' ) { return ; } const moduleScope = AnalyzerNodeInfo . getScope ( this . _moduleNode ) ; if ( ! moduleScope ) { return ; } nodes . forEach ( ( node ) => { if ( ! moduleScope . symbolTable . has ( node . value ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnsupportedDunderAll , DiagnosticRule . reportUnsupportedDunderAll , Localizer . Diagnostic . dunderAllSymbolNotPresent ( ) . format ( { name : node . value } ) , node ) ; } } ) ; } private _validateSymbolTables ( ) { for ( const scopedNode of this . _scopedNodes ) { const scope = AnalyzerNodeInfo . getScope ( scopedNode ) ; if ( scope ) { scope . symbolTable . forEach ( ( symbol , name ) => { this . _conditionallyReportUnusedSymbol ( name , symbol , scope . type ) ; this . _reportIncompatibleDeclarations ( name , symbol ) ; this . _reportMultipleFinalDeclarations ( name , symbol , scope . type ) ; this . _reportMultipleTypeAliasDeclarations ( name , symbol ) ; this . _reportInvalidOverload ( name , symbol ) ; } ) ; } } } private _reportInvalidOverload ( name , symbol ) { const typedDecls = symbol . getTypedDeclarations ( ) ; if ( typedDecls . length >= 1 ) { const primaryDecl = typedDecls [ 0 ] ; if ( primaryDecl . type === DeclarationType . Function ) { const type = this . _evaluator . getEffectiveTypeOfSymbol ( symbol ) ; const functions = isOverloadedFunction ( type ) ? type . overloads : isFunction ( type ) ? [ type ] : [ ] ; const overloadedFunctions = functions . filter ( ( func ) => FunctionType . isOverloaded ( func ) ) ; if ( overloadedFunctions . length === 1 ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . singleOverload ( ) . format ( { name } ) , primaryDecl . node . name ) ; } overloadedFunctions . forEach ( ( overload ) => { if ( overload . details . declaration && ! ParseTreeUtils . isFunctionSuiteEmpty ( overload . details . declaration . node ) ) { const diag = new DiagnosticAddendum ( ) ; diag . addMessage ( Localizer . DiagnosticAddendum . overloadWithImplementation ( ) ) ; this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . overloadWithImplementation ( ) . format ( { name } ) + diag . getString ( ) , overload . details . declaration . node . name ) ; } } ) ; if ( ! this . _fileInfo . isStubFile && overloadedFunctions . length > 0 ) { let implementationFunction ; if ( isOverloadedFunction ( type ) && ! FunctionType . isOverloaded ( type . overloads [ type . overloads . length - 1 ] ) ) { implementationFunction = type . overloads [ type . overloads . length - 1 ] ; } else if ( isFunction ( type ) && ! FunctionType . isOverloaded ( type ) ) { implementationFunction = type ; } if ( ! implementationFunction ) { let isProtocolMethod = false ; const containingClassNode = ParseTreeUtils . getEnclosingClassOrFunction ( primaryDecl . node ) ; if ( containingClassNode && containingClassNode . nodeType === ParseNodeType . Class ) { const classType = this . _evaluator . getTypeOfClass ( containingClassNode ) ; if ( classType && ClassType . isProtocolClass ( classType . classType ) ) { isProtocolMethod = true ; } } if ( ! isProtocolMethod ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . overloadWithoutImplementation ( ) . format ( { name : primaryDecl . node . name . value , } ) , primaryDecl . node . name ) ; } } else if ( isOverloadedFunction ( type ) ) { type . overloads . forEach ( ( overload , index ) => { if ( overload === implementationFunction || ! FunctionType . isOverloaded ( overload ) ) { return ; } const diag = new DiagnosticAddendum ( ) ; if ( ! this . _isLegalOverloadImplementation ( overload , implementationFunction ! , diag ) ) { if ( implementationFunction ! . details . declaration ) { const diagnostic = this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . overloadImplementationMismatch ( ) . format ( { name , index : index + 1 , } ) + diag . getString ( ) , implementationFunction ! . details . declaration . node . name ) ; if ( diagnostic && overload . details . declaration ) { diagnostic . addRelatedInfo ( Localizer . DiagnosticAddendum . overloadMethod ( ) , primaryDecl . path , primaryDecl . range ) ; } } } } ) ; } } } } } private _reportMultipleFinalDeclarations ( name , symbol , scopeType ) { if ( ! isFinalVariable ( symbol ) ) { return ; } const decls = symbol . getDeclarations ( ) ; let sawFinal = false ; let sawAssignment = false ; decls . forEach ( ( decl ) => { if ( isFinalVariableDeclaration ( decl ) ) { if ( sawFinal ) { this . _evaluator . addError ( Localizer . Diagnostic . finalRedeclaration ( ) . format ( { name } ) , decl . node ) ; } sawFinal = true ; } if ( decl . type === DeclarationType . Variable && decl . inferredTypeSource ) { if ( sawAssignment ) { if ( scopeType !== ScopeType . Class ) { this . _evaluator . addError ( Localizer . Diagnostic . finalReassigned ( ) . format ( { name } ) , decl . node ) ; } } sawAssignment = true ; } } ) ; if ( ! sawAssignment && ! this . _fileInfo . isStubFile ) { const firstDecl = decls . find ( ( decl ) => decl . type === DeclarationType . Variable && decl . isFinal ) ; if ( firstDecl ) { this . _evaluator . addError ( Localizer . Diagnostic . finalUnassigned ( ) . format ( { name } ) , firstDecl . node ) ; } } } private _reportMultipleTypeAliasDeclarations ( name , symbol ) { const decls = symbol . getDeclarations ( ) ; const typeAliasDecl = decls . find ( ( decl ) => isExplicitTypeAliasDeclaration ( decl ) ) ; if ( typeAliasDecl && decls . length > 1 ) { decls . forEach ( ( decl ) => { if ( decl !== typeAliasDecl ) { this . _evaluator . addError ( Localizer . Diagnostic . typeAliasRedeclared ( ) . format ( { name } ) , decl . node ) ; } } ) ; } } private _reportIncompatibleDeclarations ( name , symbol ) { const primaryDecl = getLastTypedDeclaredForSymbol ( symbol ) ; if ( ! primaryDecl ) { return ; } if ( name === '_' ) { return ; } let otherDecls = symbol . getDeclarations ( ) . filter ( ( decl ) => decl !== primaryDecl ) ; if ( primaryDecl . type === DeclarationType . Function ) { const primaryDeclTypeInfo = this . _evaluator . getTypeOfFunction ( primaryDecl . node ) ; otherDecls = otherDecls . filter ( ( decl ) => { if ( decl . type !== DeclarationType . Function ) { return true ; } const funcTypeInfo = this . _evaluator . getTypeOfFunction ( decl . node ) ; if ( ! funcTypeInfo ) { return true ; } if ( primaryDeclTypeInfo && isClassInstance ( primaryDeclTypeInfo . decoratedType ) && ClassType . isPropertyClass ( primaryDeclTypeInfo . decoratedType ) && isClassInstance ( funcTypeInfo . decoratedType ) && ClassType . isPropertyClass ( funcTypeInfo . decoratedType ) ) { return ( funcTypeInfo . decoratedType . details . typeSourceId !== primaryDeclTypeInfo ! . decoratedType . details . typeSourceId ) ; } return ! FunctionType . isOverloaded ( funcTypeInfo . functionType ) ; } ) ; } if ( otherDecls . length === 0 ) { return ; } let primaryDeclInfo ; if ( primaryDecl . type === DeclarationType . Function ) { if ( primaryDecl . isMethod ) { primaryDeclInfo = Localizer . DiagnosticAddendum . seeMethodDeclaration ( ) ; } else { primaryDeclInfo = Localizer . DiagnosticAddendum . seeFunctionDeclaration ( ) ; } } else if ( primaryDecl . type === DeclarationType . Class ) { primaryDeclInfo = Localizer . DiagnosticAddendum . seeClassDeclaration ( ) ; } else if ( primaryDecl . type === DeclarationType . Parameter ) { primaryDeclInfo = Localizer . DiagnosticAddendum . seeParameterDeclaration ( ) ; } else if ( primaryDecl . type === DeclarationType . Variable ) { primaryDeclInfo = Localizer . DiagnosticAddendum . seeVariableDeclaration ( ) ; } else { primaryDeclInfo = Localizer . DiagnosticAddendum . seeDeclaration ( ) ; } const addPrimaryDeclInfo = ( diag ? ) => { if ( diag ) { let primaryDeclNode ; if ( primaryDecl . type === DeclarationType . Function || primaryDecl . type === DeclarationType . Class ) { primaryDeclNode = primaryDecl . node . name ; } else if ( primaryDecl . type === DeclarationType . Variable ) { if ( primaryDecl . node . nodeType === ParseNodeType . Name ) { primaryDeclNode = primaryDecl . node ; } } else if ( primaryDecl . type === DeclarationType . Parameter ) { if ( primaryDecl . node . name ) { primaryDeclNode = primaryDecl . node . name ; } } if ( primaryDeclNode ) { diag . addRelatedInfo ( primaryDeclInfo , primaryDecl . path , primaryDecl . range ) ; } } } ; for ( const otherDecl of otherDecls ) { if ( otherDecl . type === DeclarationType . Class ) { const diag = this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . obscuredClassDeclaration ( ) . format ( { name } ) , otherDecl . node . name ) ; addPrimaryDeclInfo ( diag ) ; } else if ( otherDecl . type === DeclarationType . Function ) { const primaryType = this . _evaluator . getTypeForDeclaration ( primaryDecl ) ; if ( primaryType && isFunction ( primaryType ) ) { this . _evaluator . getFunctionInferredReturnType ( primaryType ) ; } let duplicateIsOk = false ; const otherType = this . _evaluator . getTypeForDeclaration ( otherDecl ) ; const suite1 = ParseTreeUtils . getEnclosingSuite ( primaryDecl . node ) ; const suite2 = ParseTreeUtils . getEnclosingSuite ( otherDecl . node ) ; const isInSameStatementList = suite1 === suite2 ; if ( otherType && isFunction ( otherType ) ) { this . _evaluator . getFunctionInferredReturnType ( otherType ) ; } if ( primaryType && otherType && isTypeSame ( primaryType , otherType ) ) { duplicateIsOk = true ; } if ( ! duplicateIsOk || isInSameStatementList ) { const diag = this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , otherDecl . isMethod ? Localizer . Diagnostic . obscuredMethodDeclaration ( ) . format ( { name } ) : Localizer . Diagnostic . obscuredFunctionDeclaration ( ) . format ( { name } ) , otherDecl . node . name ) ; addPrimaryDeclInfo ( diag ) ; } } else if ( otherDecl . type === DeclarationType . Parameter ) { if ( otherDecl . node . name ) { const diag = this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . obscuredParameterDeclaration ( ) . format ( { name } ) , otherDecl . node . name ) ; addPrimaryDeclInfo ( diag ) ; } } else if ( otherDecl . type === DeclarationType . Variable ) { const primaryType = this . _evaluator . getTypeForDeclaration ( primaryDecl ) ; if ( otherDecl . typeAnnotationNode ) { if ( otherDecl . node . nodeType === ParseNodeType . Name ) { let duplicateIsOk = false ; const otherType = this . _evaluator . getTypeForDeclaration ( otherDecl ) ; if ( primaryType && otherType && isTypeSame ( primaryType , otherType ) ) { duplicateIsOk = true ; } if ( ! duplicateIsOk ) { const diag = this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . obscuredVariableDeclaration ( ) . format ( { name } ) , otherDecl . node ) ; addPrimaryDeclInfo ( diag ) ; } } } else if ( primaryType && ! isProperty ( primaryType ) ) { if ( primaryDecl . type === DeclarationType . Function || primaryDecl . type === DeclarationType . Class ) { const diag = this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . obscuredVariableDeclaration ( ) . format ( { name } ) , otherDecl . node ) ; addPrimaryDeclInfo ( diag ) ; } } } } } private _conditionallyReportUnusedSymbol ( name , symbol , scopeType ) { const accessedSymbolMap = this . _fileInfo . accessedSymbolMap ; if ( symbol . isIgnoredForProtocolMatch ( ) || accessedSymbolMap . has ( symbol . id ) ) { return ; } if ( name === '_' ) { return ; } if ( SymbolNameUtils . isDunderName ( name ) ) { return ; } const decls = symbol . getDeclarations ( ) ; decls . forEach ( ( decl ) => { this . _conditionallyReportUnusedDeclaration ( decl , this . _isSymbolPrivate ( name , scopeType ) ) ; } ) ; } private _conditionallyReportUnusedDeclaration ( decl , isPrivate ) { let diagnosticLevel ; let nameNode ; let message ; let rule ; switch ( decl . type ) { case DeclarationType . Alias : diagnosticLevel = this . _fileInfo . diagnosticRuleSet . reportUnusedImport ; rule = DiagnosticRule . reportUnusedImport ; if ( decl . node . nodeType === ParseNodeType . ImportAs ) { if ( decl . node . alias ) { if ( ! this . _fileInfo . isStubFile ) { nameNode = decl . node . alias ; } } else { const nameParts = decl . node . module . nameParts ; if ( nameParts . length > 0 ) { const multipartName = nameParts . map ( ( np ) => np . value ) . join ( '.' ) ; const textRange = { start : nameParts [ 0 ] . start , length : nameParts [ 0 ] . length } ; TextRange . extend ( textRange , nameParts [ nameParts . length - 1 ] ) ; this . _fileInfo . diagnosticSink . addUnusedCodeWithTextRange ( Localizer . Diagnostic . unaccessedSymbol ( ) . format ( { name : multipartName } ) , textRange , { action : Commands . unusedImport } ) ; this . _evaluator . addDiagnosticForTextRange ( this . _fileInfo , this . _fileInfo . diagnosticRuleSet . reportUnusedImport , DiagnosticRule . reportUnusedImport , Localizer . Diagnostic . unaccessedImport ( ) . format ( { name : multipartName } ) , textRange ) ; return ; } } } else if ( decl . node . nodeType === ParseNodeType . ImportFromAs ) { const importFrom = decl . node . parent as ImportFromNode ; const isReexport = this . _fileInfo . isStubFile && decl . node . alias !== undefined ; const isFuture = importFrom . module . nameParts . length === 1 && importFrom . module . nameParts [ 0 ] . value === '__future__' ; if ( ! isReexport && ! isFuture ) { nameNode = decl . node . alias || decl . node . name ; } } if ( nameNode ) { message = Localizer . Diagnostic . unaccessedImport ( ) . format ( { name : nameNode . value } ) ; } break ; case DeclarationType . Variable : case DeclarationType . Parameter : if ( ! isPrivate ) { return ; } if ( this . _fileInfo . isStubFile ) { return ; } diagnosticLevel = this . _fileInfo . diagnosticRuleSet . reportUnusedVariable ; if ( decl . node . nodeType === ParseNodeType . Name ) { nameNode = decl . node ; if ( nameNode . value . startsWith ( '_' ) ) { diagnosticLevel = 'none' ; } } else if ( decl . node . nodeType === ParseNodeType . Parameter ) { nameNode = decl . node . name ; diagnosticLevel = 'none' ; } if ( nameNode ) { rule = DiagnosticRule . reportUnusedVariable ; message = Localizer . Diagnostic . unaccessedVariable ( ) . format ( { name : nameNode . value } ) ; } break ; case DeclarationType . Class : if ( ! isPrivate ) { return ; } if ( this . _fileInfo . isStubFile ) { return ; } diagnosticLevel = this . _fileInfo . diagnosticRuleSet . reportUnusedClass ; nameNode = decl . node . name ; rule = DiagnosticRule . reportUnusedClass ; message = Localizer . Diagnostic . unaccessedClass ( ) . format ( { name : nameNode . value } ) ; break ; case DeclarationType . Function : if ( ! isPrivate ) { return ; } if ( this . _fileInfo . isStubFile ) { return ; } diagnosticLevel = this . _fileInfo . diagnosticRuleSet . reportUnusedFunction ; nameNode = decl . node . name ; rule = DiagnosticRule . reportUnusedFunction ; message = Localizer . Diagnostic . unaccessedFunction ( ) . format ( { name : nameNode . value } ) ; break ; default : return ; } if ( nameNode && rule !== undefined && message ) { const action = rule === DiagnosticRule . reportUnusedImport ? { action : Commands . unusedImport } : undefined ; this . _fileInfo . diagnosticSink . addUnusedCodeWithTextRange ( Localizer . Diagnostic . unaccessedSymbol ( ) . format ( { name : nameNode . value } ) , nameNode , action ) ; this . _evaluator . addDiagnostic ( diagnosticLevel , rule , message , nameNode ) ; } } private _validateIsInstanceCall ( node ) { if ( node . leftExpression . nodeType !== ParseNodeType . Name || ( node . leftExpression . value !== 'isinstance' && node . leftExpression . value !== 'issubclass' ) || node . arguments . length !== 2 ) { return ; } const callName = node . leftExpression . value ; const isInstanceCheck = callName === 'isinstance' ; let arg0Type = this . _evaluator . getType ( node . arguments [ 0 ] . valueExpression ) ; if ( ! arg0Type ) { return ; } arg0Type = mapSubtypes ( arg0Type , ( subtype ) => { return transformPossibleRecursiveTypeAlias ( subtype ) ; } ) ; const arg1Type = this . _evaluator . getType ( node . arguments [ 1 ] . valueExpression ) ; if ( ! arg1Type ) { return ; } let isValidType = true ; doForEachSubtype ( arg1Type , ( arg1Subtype ) => { if ( isClassInstance ( arg1Subtype ) && ClassType . isTupleClass ( arg1Subtype ) && arg1Subtype . tupleTypeArguments ) { if ( arg1Subtype . tupleTypeArguments . some ( ( typeArg ) => ! this . _isTypeSupportedTypeForIsInstance ( typeArg , isInstanceCheck ) ) ) { isValidType = false ; } } else { if ( ! this . _isTypeSupportedTypeForIsInstance ( arg1Subtype , isInstanceCheck ) ) { isValidType = false ; } } } ) ; if ( ! isValidType ) { const diag = new DiagnosticAddendum ( ) ; diag . addMessage ( Localizer . DiagnosticAddendum . typeVarNotAllowed ( ) ) ; this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , isInstanceCheck ? Localizer . Diagnostic . isInstanceInvalidType ( ) . format ( { type : this . _evaluator . printType ( arg1Type , false ) , } ) + diag . getString ( ) : Localizer . Diagnostic . isSubclassInvalidType ( ) . format ( { type : this . _evaluator . printType ( arg1Type , false ) , } ) + diag . getString ( ) , node . arguments [ 1 ] ) ; } let curNode = node ; while ( curNode ) { if ( curNode . nodeType === ParseNodeType . Assert ) { return ; } curNode = curNode . parent ; } const nonstandardClassTypes = [ 'FunctionType' , 'LambdaType' , 'BuiltinFunctionType' , 'BuiltinMethodType' , 'type' , 'Type' , ] ; const classTypeList = [ ] ; let arg1IncludesSubclasses = false ; doForEachSubtype ( arg1Type , ( arg1Subtype ) => { if ( isClass ( arg1Subtype ) ) { if ( TypeBase . isInstantiable ( arg1Subtype ) ) { if ( arg1Subtype . literalValue === undefined ) { classTypeList . push ( arg1Subtype ) ; if ( ClassType . isBuiltIn ( arg1Subtype ) && nonstandardClassTypes . some ( ( name ) => name === arg1Subtype . details . name ) ) { isValidType = false ; } if ( arg1Subtype . includeSubclasses ) { arg1IncludesSubclasses = true ; } } } else { if ( isTupleClass ( arg1Subtype ) ) { if ( arg1Subtype . tupleTypeArguments ) { arg1Subtype . tupleTypeArguments . forEach ( ( typeArg ) => { if ( isInstantiableClass ( typeArg ) ) { classTypeList . push ( typeArg ) ; if ( typeArg . includeSubclasses ) { arg1IncludesSubclasses = true ; } } else { isValidType = false ; } } ) ; } } else { if ( arg1Subtype . includeSubclasses ) { arg1IncludesSubclasses = true ; } } if ( ClassType . isBuiltIn ( arg1Subtype ) && nonstandardClassTypes . some ( ( name ) => name === arg1Subtype . details . name ) ) { isValidType = false ; } } } else { isValidType = false ; } } ) ; if ( ! isValidType ) { return ; } if ( classTypeList . some ( ( type ) => ClassType . isProtocolClass ( type ) && ! ClassType . isRuntimeCheckable ( type ) ) ) { this . _evaluator . addError ( Localizer . Diagnostic . protocolUsedInCall ( ) . format ( { name : callName } ) , node . arguments [ 1 ] . valueExpression ) ; } if ( derivesFromAnyOrUnknown ( arg0Type ) ) { return ; } const finalizeFilteredTypeList = ( types ) : Type => { return combineTypes ( types ) ; } ; const filterType = ( varType ) : Type [ ] => { const filteredTypes = [ ] ; for ( const filterType of classTypeList ) { const filterIsSuperclass = ClassType . isDerivedFrom ( varType , filterType ) || ( isInstanceCheck && ClassType . isProtocolClass ( filterType ) && this . _evaluator . canAssignType ( filterType , varType ) ) || ( ClassType . isBuiltIn ( filterType , 'dict' ) && ClassType . isTypedDictClass ( varType ) ) ; const filterIsSubclass = ClassType . isDerivedFrom ( filterType , varType ) || ( isInstanceCheck && ClassType . isProtocolClass ( varType ) && this . _evaluator . canAssignType ( varType , filterType ) ) ; const isClassRelationshipIndeterminate = filterIsSubclass && filterIsSubclass && ! ClassType . isSameGenericClass ( varType , filterType ) ; if ( isClassRelationshipIndeterminate ) { filteredTypes . push ( UnknownType . create ( ) ) ; } else if ( filterIsSuperclass ) { filteredTypes . push ( varType ) ; } else if ( filterIsSubclass ) { filteredTypes . push ( filterType ) ; } } if ( ! isInstanceCheck ) { return filteredTypes ; } return filteredTypes . map ( ( t ) => ( isInstantiableClass ( t ) ? ClassType . cloneAsInstance ( t ) : t ) ) ; } ; let filteredType ; if ( isInstanceCheck && isClassInstance ( arg0Type ) ) { const remainingTypes = filterType ( ClassType . cloneAsInstantiable ( arg0Type ) ) ; filteredType = finalizeFilteredTypeList ( remainingTypes ) ; } else if ( ! isInstanceCheck && isInstantiableClass ( arg0Type ) ) { const remainingTypes = filterType ( arg0Type ) ; filteredType = finalizeFilteredTypeList ( remainingTypes ) ; } else if ( isUnion ( arg0Type ) ) { let remainingTypes = [ ] ; let foundAnyType = false ; doForEachSubtype ( arg0Type , ( subtype ) => { if ( isAnyOrUnknown ( subtype ) ) { foundAnyType = true ; } if ( isInstanceCheck && isClassInstance ( subtype ) ) { remainingTypes = remainingTypes . concat ( filterType ( ClassType . cloneAsInstantiable ( subtype ) ) ) ; } else if ( ! isInstanceCheck && isInstantiableClass ( subtype ) ) { remainingTypes = remainingTypes . concat ( filterType ( subtype ) ) ; } } ) ; filteredType = finalizeFilteredTypeList ( remainingTypes ) ; if ( foundAnyType ) { return ; } } else { return ; } const getTestType = ( ) => { const objTypeList = classTypeList . map ( ( t ) => ClassType . cloneAsInstance ( t ) ) ; return combineTypes ( objTypeList ) ; } ; if ( ! arg1IncludesSubclasses && isTypeSame ( filteredType , arg0Type , true ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnnecessaryIsInstance , DiagnosticRule . reportUnnecessaryIsInstance , isInstanceCheck ? Localizer . Diagnostic . unnecessaryIsInstanceAlways ( ) . format ( { testType : this . _evaluator . printType ( arg0Type , false ) , classType : this . _evaluator . printType ( getTestType ( ) , false ) , } ) : Localizer . Diagnostic . unnecessaryIsSubclassAlways ( ) . format ( { testType : this . _evaluator . printType ( arg0Type , false ) , classType : this . _evaluator . printType ( getTestType ( ) , false ) , } ) , node ) ; } } private _isTypeSupportedTypeForIsInstance ( type , isInstanceCheck ) { let isSupported = true ; doForEachSubtype ( type , ( subtype ) => { subtype = this . _evaluator . makeTopLevelTypeVarsConcrete ( subtype ) ; switch ( subtype . category ) { case TypeCategory . Any : case TypeCategory . Unknown : case TypeCategory . Unbound : break ; case TypeCategory . Class : if ( subtype . isTypeArgumentExplicit && ! subtype . includeSubclasses ) { isSupported = false ; } break ; case TypeCategory . None : if ( ! isInstanceCheck ) { isSupported = false ; } else { isSupported = TypeBase . isInstantiable ( subtype ) ; } break ; case TypeCategory . Function : isSupported = TypeBase . isInstantiable ( subtype ) ; break ; case TypeCategory . Union : isSupported = this . _isTypeSupportedTypeForIsInstance ( subtype , isInstanceCheck ) ; break ; default : isSupported = false ; break ; } } ) ; return isSupported ; } private _isSymbolPrivate ( nameValue , scopeType ) { if ( scopeType === ScopeType . Function || scopeType === ScopeType . ListComprehension ) { return true ; } if ( SymbolNameUtils . isPrivateName ( nameValue ) ) { return true ; } if ( SymbolNameUtils . isProtectedName ( nameValue ) ) { const isClassScope = scopeType === ScopeType . Class ; return ! isClassScope ; } return false ; } private _reportDeprecatedUse ( node ) { const deprecatedForm = deprecatedAliases . get ( node . value ) ?? deprecatedSpecialForms . get ( node . value ) ; if ( ! deprecatedForm ) { return ; } const type = this . _evaluator . getType ( node ) ; if ( ! type ) { return ; } if ( ! isInstantiableClass ( type ) || type . details . fullName !== deprecatedForm . fullName ) { return ; } if ( this . _fileInfo . executionEnvironment . pythonVersion >= deprecatedForm . version ) { this . _evaluator . addDeprecated ( Localizer . Diagnostic . deprecatedType ( ) . format ( { version : versionToString ( deprecatedForm . version ) , replacement : deprecatedForm . replacementText , } ) , node ) ; } } private _reportUnboundName ( node ) { if ( this . _fileInfo . diagnosticRuleSet . reportUnboundVariable === 'none' ) { return ; } if ( ! AnalyzerNodeInfo . isCodeUnreachable ( node ) ) { const type = this . _evaluator . getType ( node ) ; if ( type ) { if ( isUnbound ( type ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnboundVariable , DiagnosticRule . reportUnboundVariable , Localizer . Diagnostic . symbolIsUnbound ( ) . format ( { name : node . value } ) , node ) ; } else if ( isPossiblyUnbound ( type ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnboundVariable , DiagnosticRule . reportUnboundVariable , Localizer . Diagnostic . symbolIsPossiblyUnbound ( ) . format ( { name : node . value } ) , node ) ; } } } } private _conditionallyReportPrivateUsage ( node ) { if ( this . _fileInfo . diagnosticRuleSet . reportPrivateUsage === 'none' ) { return ; } if ( this . _fileInfo . isStubFile ) { return ; } if ( node . parent ?. nodeType === ParseNodeType . Argument && node . parent . name === node ) { return ; } const nameValue = node . value ; const isPrivateName = SymbolNameUtils . isPrivateName ( nameValue ) ; const isProtectedName = SymbolNameUtils . isProtectedName ( nameValue ) ; if ( ! isPrivateName && ! isProtectedName ) { return ; } const declarations = this . _evaluator . getDeclarationsForNameNode ( node ) ; let primaryDeclaration = declarations && declarations . length > 0 ? declarations [ declarations . length - 1 ] : undefined ; if ( ! primaryDeclaration || primaryDeclaration . node === node ) { return ; } if ( primaryDeclaration . type === DeclarationType . Alias ) { if ( primaryDeclaration . usesLocalName ) { return ; } const resolvedAliasInfo = this . _evaluator . resolveAliasDeclarationWithInfo ( primaryDeclaration , true ) ; if ( ! resolvedAliasInfo ) { return ; } primaryDeclaration = resolvedAliasInfo . declaration ; if ( ! resolvedAliasInfo . isPrivate ) { return ; } } if ( ! primaryDeclaration || primaryDeclaration . node === node ) { return ; } let classNode ; if ( primaryDeclaration . node ) { classNode = ParseTreeUtils . getEnclosingClass ( primaryDeclaration . node ) ; } if ( primaryDeclaration . node && primaryDeclaration . node . parent && primaryDeclaration . node . parent === classNode ) { classNode = ParseTreeUtils . getEnclosingClass ( classNode ) ; } let isProtectedAccess = false ; if ( classNode ) { if ( isProtectedName ) { const declClassTypeInfo = this . _evaluator . getTypeOfClass ( classNode ) ; if ( declClassTypeInfo && isInstantiableClass ( declClassTypeInfo . decoratedType ) ) { if ( ClassType . isDefinedInStub ( declClassTypeInfo . decoratedType ) ) { return ; } isProtectedAccess = true ; const enclosingClassNode = ParseTreeUtils . getEnclosingClass ( node ) ; if ( enclosingClassNode ) { const enclosingClassTypeInfo = this . _evaluator . getTypeOfClass ( enclosingClassNode ) ; if ( enclosingClassTypeInfo && isInstantiableClass ( enclosingClassTypeInfo . decoratedType ) ) { if ( derivesFromClassRecursive ( enclosingClassTypeInfo . decoratedType , declClassTypeInfo . decoratedType , true ) ) { return ; } } } } } } if ( classNode && ! ParseTreeUtils . isNodeContainedWithin ( node , classNode ) ) { if ( isProtectedAccess ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportPrivateUsage , DiagnosticRule . reportPrivateUsage , Localizer . Diagnostic . protectedUsedOutsideOfClass ( ) . format ( { name : nameValue } ) , node ) ; } else { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportPrivateUsage , DiagnosticRule . reportPrivateUsage , Localizer . Diagnostic . privateUsedOutsideOfClass ( ) . format ( { name : nameValue } ) , node ) ; } } } private _validateEnumClassOverride ( node , classType ) { classType . details . baseClasses . forEach ( ( baseClass , index ) => { if ( isClass ( baseClass ) && ClassType . isEnumClass ( baseClass ) ) { let baseEnumDefinesValue = false ; baseClass . details . fields . forEach ( ( symbol ) => { const symbolType = this . _evaluator . getEffectiveTypeOfSymbol ( symbol ) ; if ( isClassInstance ( symbolType ) && ClassType . isSameGenericClass ( symbolType , baseClass ) ) { baseEnumDefinesValue = true ; } } ) ; if ( baseEnumDefinesValue ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . enumClassOverride ( ) . format ( { name : baseClass . details . name } ) , node . arguments [ index ] ) ; } } } ) ; } private _validateTypedDictClassSuite ( suiteNode ) { const emitBadStatementError = ( node ) => { this . _evaluator . addError ( Localizer . Diagnostic . typedDictBadVar ( ) , node ) ; } ; suiteNode . statements . forEach ( ( statement ) => { if ( ! AnalyzerNodeInfo . isCodeUnreachable ( statement ) ) { if ( statement . nodeType === ParseNodeType . StatementList ) { for ( const substatement of statement . statements ) { if ( substatement . nodeType !== ParseNodeType . TypeAnnotation && substatement . nodeType !== ParseNodeType . Ellipsis && substatement . nodeType !== ParseNodeType . StringList && substatement . nodeType !== ParseNodeType . Pass ) { emitBadStatementError ( substatement ) ; } } } else { emitBadStatementError ( statement ) ; } } } ) ; } private _validateDunderSignatures ( node , functionType , isMethod ) { const functionName = functionType . details . name ; if ( isMethod && functionName === '__init__' ) { const returnAnnotation = node . returnTypeAnnotation || node . functionAnnotationComment ?. returnTypeAnnotation ; const declaredReturnType = functionType . details . declaredReturnType ; if ( returnAnnotation && declaredReturnType ) { if ( ! isNoneInstance ( declaredReturnType ) && ! isNoReturnType ( declaredReturnType ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . initMustReturnNone ( ) , returnAnnotation ) ; } } else { const inferredReturnType = this . _evaluator . getFunctionInferredReturnType ( functionType ) ; if ( ! isNoReturnType ( inferredReturnType ) && ! isNoneInstance ( inferredReturnType ) && ! isAnyOrUnknown ( inferredReturnType ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . initMustReturnNone ( ) , node . name ) ; } } } } private _validateFunctionReturn ( node , functionType ) { if ( this . _fileInfo . isStubFile ) { return ; } const returnAnnotation = node . returnTypeAnnotation || node . functionAnnotationComment ?. returnTypeAnnotation ; if ( returnAnnotation ) { const functionNeverReturns = ! this . _evaluator . isAfterNodeReachable ( node ) ; const implicitlyReturnsNone = this . _evaluator . isAfterNodeReachable ( node . suite ) ; let declaredReturnType = functionType . details . declaredReturnType ; if ( declaredReturnType ) { if ( isUnknown ( declaredReturnType ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnknownVariableType , DiagnosticRule . reportUnknownVariableType , Localizer . Diagnostic . declaredReturnTypeUnknown ( ) , returnAnnotation ) ; } else if ( isPartlyUnknown ( declaredReturnType ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnknownVariableType , DiagnosticRule . reportUnknownVariableType , Localizer . Diagnostic . declaredReturnTypePartiallyUnknown ( ) . format ( { returnType : this . _evaluator . printType ( declaredReturnType , true ) , } ) , returnAnnotation ) ; } const diag = new DiagnosticAddendum ( ) ; if ( isTypeVar ( declaredReturnType ) && declaredReturnType . details . variance === Variance . Contravariant ) { diag . addMessage ( Localizer . DiagnosticAddendum . typeVarIsContravariant ( ) . format ( { name : TypeVarType . getReadableName ( declaredReturnType ) , } ) ) ; this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . returnTypeContravariant ( ) + diag . getString ( ) , returnAnnotation ) ; } } if ( FunctionType . isGenerator ( functionType ) ) { declaredReturnType = getDeclaredGeneratorReturnType ( functionType ) ; } if ( declaredReturnType && ! functionNeverReturns && implicitlyReturnsNone ) { if ( isNoReturnType ( declaredReturnType ) ) { if ( ! ParseTreeUtils . isSuiteEmpty ( node . suite ) && ! FunctionType . isOverloaded ( functionType ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . noReturnReturnsNone ( ) , returnAnnotation ) ; } } else if ( ! FunctionType . isAbstractMethod ( functionType ) ) { const diagAddendum = new DiagnosticAddendum ( ) ; if ( ! this . _evaluator . canAssignType ( declaredReturnType , NoneType . createInstance ( ) , diagAddendum ) ) { if ( ! ParseTreeUtils . isSuiteEmpty ( node . suite ) && ! FunctionType . isOverloaded ( functionType ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . returnMissing ( ) . format ( { returnType : this . _evaluator . printType ( declaredReturnType , false ) , } ) + diagAddendum . getString ( ) , returnAnnotation ) ; } } } } } else { const inferredReturnType = this . _evaluator . getFunctionInferredReturnType ( functionType ) ; if ( isUnknown ( inferredReturnType ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnknownParameterType , DiagnosticRule . reportUnknownParameterType , Localizer . Diagnostic . returnTypeUnknown ( ) , node . name ) ; } else if ( isPartlyUnknown ( inferredReturnType ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUnknownParameterType , DiagnosticRule . reportUnknownParameterType , Localizer . Diagnostic . returnTypePartiallyUnknown ( ) . format ( { returnType : this . _evaluator . printType ( inferredReturnType , true ) , } ) , node . name ) ; } } } private _validateFinalMemberOverrides ( classType ) { classType . details . fields . forEach ( ( localSymbol , name ) => { const parentSymbol = lookUpClassMember ( classType , name , ClassMemberLookupFlags . SkipOriginalClass ) ; if ( parentSymbol && isInstantiableClass ( parentSymbol . classType ) && isFinalVariable ( parentSymbol . symbol ) && ! SymbolNameUtils . isPrivateName ( name ) ) { const decl = localSymbol . getDeclarations ( ) [ 0 ] ; this . _evaluator . addError ( Localizer . Diagnostic . finalRedeclarationBySubclass ( ) . format ( { name , className : parentSymbol . classType . details . name , } ) , decl . node ) ; } } ) ; } private _reportDuplicateEnumMembers ( classType ) { if ( ! ClassType . isEnumClass ( classType ) || ClassType . isBuiltIn ( classType ) ) { return ; } classType . details . fields . forEach ( ( symbol , name ) => { if ( symbol . getTypedDeclarations ( ) . length > 0 ) { return ; } const decls = symbol . getDeclarations ( ) ; if ( decls . length >= 2 && decls [ 0 ] . type === DeclarationType . Variable ) { this . _evaluator . addError ( Localizer . Diagnostic . duplicateEnumMember ( ) . format ( { name } ) , decls [ 1 ] . node ) ; } } ) ; } private _validateDataClassPostInit ( classType , errorNode ) { if ( ! ClassType . isDataClass ( classType ) ) { return ; } const postInitMember = lookUpClassMember ( classType , '__post_init__' , ClassMemberLookupFlags . SkipBaseClasses | ClassMemberLookupFlags . DeclaredTypesOnly ) ; if ( ! postInitMember ) { return ; } const initOnlySymbolMap = new Map < string , Symbol > ( ) ; classType . details . fields . forEach ( ( symbol , name ) => { if ( symbol . isInitVar ( ) ) { initOnlySymbolMap . set ( name , symbol ) ; } } ) ; const postInitType = this . _evaluator . getTypeOfMember ( postInitMember ) ; if ( ! isFunction ( postInitType ) || ! FunctionType . isInstanceMethod ( postInitType ) || ! postInitType . details . declaration ) { return ; } const paramListDetails = getParameterListDetails ( postInitType ) ; if ( paramListDetails . argsIndex !== undefined || paramListDetails . kwargsIndex !== undefined || paramListDetails . firstKeywordOnlyIndex !== undefined ) { return ; } const nonDefaultParams = paramListDetails . params . filter ( ( paramInfo ) => ! paramInfo . param . hasDefault ) ; const expectedParamCount = initOnlySymbolMap . size + 1 ; if ( expectedParamCount < nonDefaultParams . length || expectedParamCount > paramListDetails . params . length ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . dataClassPostInitParamCount ( ) . format ( { expected : initOnlySymbolMap . size } ) , postInitType . details . declaration . node . name ) ; } let paramIndex = 1 ; initOnlySymbolMap . forEach ( ( symbol , fieldName ) => { if ( paramIndex >= paramListDetails . params . length ) { return ; } const param = paramListDetails . params [ paramIndex ] . param ; if ( param . hasDeclaredType && param . typeAnnotation ) { const fieldType = this . _evaluator . getDeclaredTypeOfSymbol ( symbol ) ; const paramType = FunctionType . getEffectiveParameterType ( postInitType , paramListDetails . params [ paramIndex ] . index ) ; const canAssignDiag = new DiagnosticAddendum ( ) ; if ( fieldType && ! this . _evaluator . canAssignType ( paramType , fieldType , canAssignDiag ) ) { const diagnostic = this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . dataClassPostInitType ( ) . format ( { fieldName } ) + canAssignDiag . getString ( ) , param . typeAnnotation ) ; if ( diagnostic ) { const fieldDecls = symbol . getTypedDeclarations ( ) ; if ( fieldDecls . length > 0 ) { diagnostic . addRelatedInfo ( Localizer . DiagnosticAddendum . dataClassFieldLocation ( ) , fieldDecls [ 0 ] . path , fieldDecls [ 0 ] . range ) ; } } } } paramIndex ++ ; } ) ; } private _validateFinalClassNotAbstract ( classType , errorNode ) { if ( ! ClassType . isFinal ( classType ) ) { return ; } if ( ! ClassType . supportsAbstractMethods ( classType ) ) { return ; } const abstractMethods = this . _evaluator . getAbstractMethods ( classType ) ; if ( abstractMethods . length === 0 ) { return ; } const diagAddendum = new DiagnosticAddendum ( ) ; const errorsToDisplay = 2 ; abstractMethods . forEach ( ( abstractMethod , index ) => { if ( index === errorsToDisplay ) { diagAddendum . addMessage ( Localizer . DiagnosticAddendum . memberIsAbstractMore ( ) . format ( { count : abstractMethods . length - errorsToDisplay , } ) ) ; } else if ( index < errorsToDisplay ) { if ( isInstantiableClass ( abstractMethod . classType ) ) { const className = abstractMethod . classType . details . name ; diagAddendum . addMessage ( Localizer . DiagnosticAddendum . memberIsAbstract ( ) . format ( { type : className , name : abstractMethod . symbolName , } ) ) ; } } } ) ; this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . finalClassIsAbstract ( ) . format ( { type : classType . details . name , } ) + diagAddendum . getString ( ) , errorNode . name ) ; } private _validateInstanceVariableInitialization ( classType ) { if ( this . _fileInfo . diagnosticRuleSet . reportUninitializedInstanceVariable === 'none' ) { return ; } classType . details . fields . forEach ( ( localSymbol , name ) => { if ( ! localSymbol . isInstanceMember ( ) ) { return ; } const decls = localSymbol . getDeclarations ( ) ; if ( decls . find ( ( decl ) => { const containingClass = ParseTreeUtils . getEnclosingClassOrFunction ( decl . node ) ; if ( ! containingClass || containingClass . nodeType === ParseNodeType . Class ) { return true ; } if ( containingClass . name . value === '__init__' ) { return true ; } return false ; } ) ) { return ; } const parentSymbol = lookUpClassMember ( classType , name , ClassMemberLookupFlags . SkipOriginalClass ) ; if ( parentSymbol ) { return ; } this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportUninitializedInstanceVariable , DiagnosticRule . reportUninitializedInstanceVariable , Localizer . Diagnostic . uninitializedInstanceVariable ( ) . format ( { name : name } ) , decls [ 0 ] . node ) ; } ) ; } private _validateProtocolTypeParamVariance ( errorNode , classType ) { const origTypeParams = classType . details . typeParameters . filter ( ( typeParam ) => ! isParamSpec ( typeParam ) && ! isVariadicTypeVar ( typeParam ) ) ; if ( origTypeParams . length === 0 ) { return ; } const objectType = this . _evaluator . getBuiltInType ( errorNode , 'object' ) ; if ( ! isInstantiableClass ( objectType ) ) { return ; } const updatedTypeParams = origTypeParams . map ( ( typeParam ) => TypeVarType . cloneAsInvariant ( typeParam ) ) ; const updatedClassType = ClassType . cloneWithNewTypeParameters ( classType , updatedTypeParams ) ; const objectObject = ClassType . cloneAsInstance ( objectType ) ; const dummyTypeObject = ClassType . createInstantiable ( '__protocolVarianceDummy' , '' , '' , '' , 0 , 0 , undefined , undefined ) ; updatedTypeParams . forEach ( ( param , paramIndex ) => { const srcTypeArgs = updatedTypeParams . map ( ( _ , i ) => { return i === paramIndex ? objectObject : dummyTypeObject ; } ) ; const destTypeArgs = updatedTypeParams . map ( ( p , i ) => { return i === paramIndex ? p : dummyTypeObject ; } ) ; const srcType = ClassType . cloneForSpecialization ( updatedClassType , srcTypeArgs , true ) ; const destType = ClassType . cloneForSpecialization ( updatedClassType , destTypeArgs , true ) ; const isDestSubtypeOfSrc = this . _evaluator . canAssignProtocolClassToSelf ( srcType , destType ) ; let expectedVariance ; if ( isDestSubtypeOfSrc ) { expectedVariance = Variance . Covariant ; } else { const isSrcSubtypeOfDest = this . _evaluator . canAssignProtocolClassToSelf ( destType , srcType ) ; if ( isSrcSubtypeOfDest ) { expectedVariance = Variance . Contravariant ; } else { expectedVariance = Variance . Invariant ; } } if ( expectedVariance !== origTypeParams [ paramIndex ] . details . variance ) { let message ; if ( expectedVariance === Variance . Covariant ) { message = Localizer . Diagnostic . protocolVarianceCovariant ( ) . format ( { variable : param . details . name , class : classType . details . name , } ) ; } else if ( expectedVariance === Variance . Contravariant ) { message = Localizer . Diagnostic . protocolVarianceContravariant ( ) . format ( { variable : param . details . name , class : classType . details . name , } ) ; } else { message = Localizer . Diagnostic . protocolVarianceInvariant ( ) . format ( { variable : param . details . name , class : classType . details . name , } ) ; } this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportInvalidTypeVarUse , DiagnosticRule . reportInvalidTypeVarUse , message , errorNode . name ) ; } } ) ; } private _validateSlotsClassVarConflict ( classType ) { if ( ! classType . details . localSlotsNames ) { return ; } classType . details . fields . forEach ( ( symbol , name ) => { const decls = symbol . getDeclarations ( ) ; const isDefinedBySlots = decls . some ( ( decl ) => decl . type === DeclarationType . Variable && decl . isDefinedBySlots ) ; if ( isDefinedBySlots ) { decls . forEach ( ( decl ) => { if ( decl . type === DeclarationType . Variable && ! decl . isDefinedBySlots && ! decl . isDefinedByMemberAccess ) { if ( decl . node . nodeType === ParseNodeType . Name && ParseTreeUtils . isWriteAccess ( decl . node ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . slotsClassVarConflict ( ) . format ( { name } ) , decl . node ) ; } } } ) ; } } ) ; } private _validateMultipleInheritanceCompatibility ( classType , errorNode ) { if ( this . _fileInfo . diagnosticRuleSet . reportIncompatibleMethodOverride === 'none' ) { return ; } const baseClasses = [ ] ; classType . details . baseClasses . forEach ( ( baseClass ) => { if ( isClass ( baseClass ) && ! ClassType . isBuiltIn ( baseClass , 'Generic' ) && ! ClassType . isBuiltIn ( baseClass , 'Protocol' ) ) { baseClasses . push ( baseClass ) ; } } ) ; if ( baseClasses . length < 2 ) { return ; } const symbolMaps = baseClasses . map ( ( baseClass ) => { const specializedBaseClass = classType . details . mro . find ( ( c ) => isClass ( c ) && ClassType . isSameGenericClass ( c , baseClass ) ) ; if ( ! specializedBaseClass || ! isClass ( specializedBaseClass ) ) { return new Map < string , ClassMember > ( ) ; } return getClassFieldsRecursive ( specializedBaseClass ) ; } ) ; for ( let symbolMapBaseIndex = 1 ; symbolMapBaseIndex < symbolMaps . length ; symbolMapBaseIndex ++ ) { const baseSymbolMap = symbolMaps [ symbolMapBaseIndex ] ; for ( const [ name , baseClassAndSymbol ] of baseSymbolMap ) { if ( SymbolNameUtils . isDunderName ( name ) || SymbolNameUtils . isPrivateName ( name ) ) { continue ; } const baseClassType = baseClassAndSymbol . classType ; if ( ! isClass ( baseClassType ) ) { continue ; } for ( let symbolMapOverrideIndex = 0 ; symbolMapOverrideIndex < symbolMapBaseIndex ; symbolMapOverrideIndex ++ ) { const overrideSymbolMap = symbolMaps [ symbolMapOverrideIndex ] ; const overrideClassAndSymbol = overrideSymbolMap . get ( name ) ; if ( overrideClassAndSymbol ) { let baseType = this . _evaluator . getEffectiveTypeOfSymbol ( baseClassAndSymbol . symbol ) ; baseType = partiallySpecializeType ( baseType , baseClasses [ symbolMapBaseIndex ] ) ; let overrideType = this . _evaluator . getEffectiveTypeOfSymbol ( overrideClassAndSymbol . symbol ) ; overrideType = partiallySpecializeType ( overrideType , baseClasses [ symbolMapOverrideIndex ] ) ; if ( isFunction ( baseType ) || isOverloadedFunction ( baseType ) ) { const diagAddendum = new DiagnosticAddendum ( ) ; let overrideFunction ; if ( isFunction ( overrideType ) ) { overrideFunction = overrideType ; } else if ( isOverloadedFunction ( overrideType ) ) { overrideFunction = overrideType . overloads [ overrideType . overloads . length - 1 ] ; } if ( overrideFunction ) { if ( ! this . _evaluator . canOverrideMethod ( baseType , overrideFunction , diagAddendum , true ) ) { const decl = overrideFunction . details . declaration ; if ( decl && decl . type === DeclarationType . Function ) { const diag = this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportIncompatibleMethodOverride , DiagnosticRule . reportIncompatibleMethodOverride , Localizer . Diagnostic . baseClassMethodTypeIncompatible ( ) . format ( { classType : classType . details . name , name , } ) + diagAddendum . getString ( ) , errorNode ) ; const overrideDecl = getLastTypedDeclaredForSymbol ( overrideClassAndSymbol . symbol ) ; const baseDecl = getLastTypedDeclaredForSymbol ( baseClassAndSymbol . symbol ) ; if ( diag && overrideDecl && baseDecl ) { diag . addRelatedInfo ( Localizer . DiagnosticAddendum . baseClassProvidesType ( ) . format ( { baseClass : this . _evaluator . printType ( convertToInstance ( baseClasses [ symbolMapOverrideIndex ] ) ) , type : this . _evaluator . printType ( overrideType ) , } ) , overrideDecl . path , overrideDecl . range ) ; diag . addRelatedInfo ( Localizer . DiagnosticAddendum . baseClassProvidesType ( ) . format ( { baseClass : this . _evaluator . printType ( convertToInstance ( baseClasses [ symbolMapBaseIndex ] ) ) , type : this . _evaluator . printType ( baseType ) , } ) , baseDecl . path , baseDecl . range ) ; } } } } } } } } } } private _validateBaseClassOverrides ( classType ) { classType . details . fields . forEach ( ( symbol , name ) => { if ( SymbolNameUtils . isPrivateName ( name ) ) { return ; } if ( ! symbol . hasTypedDeclarations ( ) ) { return ; } const typeOfSymbol = this . _evaluator . getEffectiveTypeOfSymbol ( symbol ) ; if ( isAnyOrUnknown ( typeOfSymbol ) ) { return ; } for ( const baseClass of classType . details . baseClasses ) { if ( ! isClass ( baseClass ) ) { continue ; } const mroBaseClass = classType . details . mro . find ( ( mroClass ) => isClass ( mroClass ) && ClassType . isSameGenericClass ( mroClass , baseClass ) ) ; if ( ! mroBaseClass ) { continue ; } const baseClassAndSymbol = lookUpClassMember ( mroBaseClass , name , ClassMemberLookupFlags . Default ) ; if ( ! baseClassAndSymbol || ! isInstantiableClass ( baseClassAndSymbol . classType ) ) { continue ; } if ( ! baseClassAndSymbol . symbol . hasTypedDeclarations ( ) ) { continue ; } const baseClassSymbolType = partiallySpecializeType ( this . _evaluator . getEffectiveTypeOfSymbol ( baseClassAndSymbol . symbol ) , baseClassAndSymbol . classType ) ; if ( isFunction ( baseClassSymbolType ) || isOverloadedFunction ( baseClassSymbolType ) ) { const diagAddendum = new DiagnosticAddendum ( ) ; let overrideFunction ; if ( isFunction ( typeOfSymbol ) ) { overrideFunction = typeOfSymbol ; } else if ( isOverloadedFunction ( typeOfSymbol ) ) { overrideFunction = typeOfSymbol . overloads [ typeOfSymbol . overloads . length - 1 ] ; } if ( overrideFunction ) { const exemptMethods = [ '__init__' , '__new__' , '__init_subclass__' ] ; const enforceParamNameMatch = ! SymbolNameUtils . isDunderName ( name ) ; if ( ! exemptMethods . some ( ( exempt ) => exempt === name ) && ! SymbolNameUtils . isPrivateName ( name ) ) { if ( ! this . _evaluator . canOverrideMethod ( baseClassSymbolType , overrideFunction , diagAddendum , enforceParamNameMatch ) ) { const decl = overrideFunction . details . declaration ; if ( decl && decl . type === DeclarationType . Function ) { const diag = this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportIncompatibleMethodOverride , DiagnosticRule . reportIncompatibleMethodOverride , Localizer . Diagnostic . incompatibleMethodOverride ( ) . format ( { name , className : baseClassAndSymbol . classType . details . name , } ) + diagAddendum . getString ( ) , decl . node . name ) ; const origDecl = getLastTypedDeclaredForSymbol ( baseClassAndSymbol . symbol ) ; if ( diag && origDecl ) { diag . addRelatedInfo ( Localizer . DiagnosticAddendum . overriddenMethod ( ) , origDecl . path , origDecl . range ) ; } } } } if ( isFunction ( baseClassSymbolType ) ) { if ( ! SymbolNameUtils . isPrivateName ( name ) && FunctionType . isFinal ( baseClassSymbolType ) ) { const decl = getLastTypedDeclaredForSymbol ( symbol ) ; if ( decl && decl . type === DeclarationType . Function ) { const diag = this . _evaluator . addError ( Localizer . Diagnostic . finalMethodOverride ( ) . format ( { name , className : baseClassAndSymbol . classType . details . name , } ) , decl . node . name ) ; const origDecl = getLastTypedDeclaredForSymbol ( baseClassAndSymbol . symbol ) ; if ( diag && origDecl ) { diag . addRelatedInfo ( Localizer . DiagnosticAddendum . finalMethod ( ) , origDecl . path , origDecl . range ) ; } } } } } else if ( ! isAnyOrUnknown ( typeOfSymbol ) ) { if ( ! ClassType . isBuiltIn ( baseClassAndSymbol . classType , '_TypedDict' ) ) { const decls = symbol . getDeclarations ( ) ; if ( decls . length > 0 ) { const lastDecl = decls [ decls . length - 1 ] ; const diag = this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportIncompatibleMethodOverride , DiagnosticRule . reportIncompatibleMethodOverride , Localizer . Diagnostic . methodOverridden ( ) . format ( { name , className : baseClassAndSymbol . classType . details . name , type : this . _evaluator . printType ( typeOfSymbol , false ) , } ) , lastDecl . node ) ; const origDecl = getLastTypedDeclaredForSymbol ( baseClassAndSymbol . symbol ) ; if ( diag && origDecl ) { diag . addRelatedInfo ( Localizer . DiagnosticAddendum . overriddenMethod ( ) , origDecl . path , origDecl . range ) ; } } } } } else if ( isProperty ( baseClassSymbolType ) ) { if ( ! isProperty ( typeOfSymbol ) ) { const decls = symbol . getDeclarations ( ) ; if ( decls . length > 0 ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportIncompatibleMethodOverride , DiagnosticRule . reportIncompatibleMethodOverride , Localizer . Diagnostic . propertyOverridden ( ) . format ( { name , className : baseClassAndSymbol . classType . details . name , } ) , decls [ decls . length - 1 ] . node ) ; } } else { const basePropFields = ( baseClassSymbolType as ClassType ) . details . fields ; const subclassPropFields = ( typeOfSymbol as ClassType ) . details . fields ; const baseClassType = baseClassAndSymbol . classType ; [ 'fget' , 'fset' , 'fdel' ] . forEach ( ( methodName ) => { const diagAddendum = new DiagnosticAddendum ( ) ; const baseClassPropMethod = basePropFields . get ( methodName ) ; const subclassPropMethod = subclassPropFields . get ( methodName ) ; if ( baseClassPropMethod ) { const baseClassMethodType = partiallySpecializeType ( this . _evaluator . getEffectiveTypeOfSymbol ( baseClassPropMethod ) , baseClassType ) ; if ( isFunction ( baseClassMethodType ) ) { if ( ! subclassPropMethod ) { diagAddendum . addMessage ( Localizer . DiagnosticAddendum . propertyMethodMissing ( ) . format ( { name : methodName , } ) ) ; const decls = symbol . getDeclarations ( ) ; if ( decls . length > 0 ) { const diag = this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportIncompatibleMethodOverride , DiagnosticRule . reportIncompatibleMethodOverride , Localizer . Diagnostic . propertyOverridden ( ) . format ( { name , className : baseClassType . details . name , } ) + diagAddendum . getString ( ) , decls [ decls . length - 1 ] . node ) ; const origDecl = baseClassMethodType . details . declaration ; if ( diag && origDecl ) { diag . addRelatedInfo ( Localizer . DiagnosticAddendum . overriddenMethod ( ) , origDecl . path , origDecl . range ) ; } } } else { const subclassMethodType = partiallySpecializeType ( this . _evaluator . getEffectiveTypeOfSymbol ( subclassPropMethod ) , classType ) ; if ( isFunction ( subclassMethodType ) ) { if ( ! this . _evaluator . canOverrideMethod ( baseClassMethodType , subclassMethodType , diagAddendum . createAddendum ( ) ) ) { diagAddendum . addMessage ( Localizer . DiagnosticAddendum . propertyMethodIncompatible ( ) . format ( { name : methodName , } ) ) ; const decl = subclassMethodType . details . declaration ; if ( decl && decl . type === DeclarationType . Function ) { const diag = this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportIncompatibleMethodOverride , DiagnosticRule . reportIncompatibleMethodOverride , Localizer . Diagnostic . propertyOverridden ( ) . format ( { name , className : baseClassType . details . name , } ) + diagAddendum . getString ( ) , decl . node . name ) ; const origDecl = baseClassMethodType . details . declaration ; if ( diag && origDecl ) { diag . addRelatedInfo ( Localizer . DiagnosticAddendum . overriddenMethod ( ) , origDecl . path , origDecl . range ) ; } } } } } } } } ) ; } } else { if ( this . _fileInfo . diagnosticRuleSet . reportIncompatibleVariableOverride !== 'none' ) { const decls = symbol . getDeclarations ( ) ; if ( decls . length > 0 ) { const lastDecl = decls [ decls . length - 1 ] ; const diagAddendum = new DiagnosticAddendum ( ) ; if ( ! this . _evaluator . canAssignType ( baseClassSymbolType , typeOfSymbol , diagAddendum ) ) { const diag = this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportIncompatibleVariableOverride , DiagnosticRule . reportIncompatibleVariableOverride , Localizer . Diagnostic . symbolOverridden ( ) . format ( { name , className : baseClassAndSymbol . classType . details . name , } ) + diagAddendum . getString ( ) , lastDecl . node ) ; const origDecl = getLastTypedDeclaredForSymbol ( baseClassAndSymbol . symbol ) ; if ( diag && origDecl ) { diag . addRelatedInfo ( Localizer . DiagnosticAddendum . overriddenSymbol ( ) , origDecl . path , origDecl . range ) ; } } const isBaseClassVar = baseClassAndSymbol . symbol . isClassVar ( ) ; let isClassVar = symbol . isClassVar ( ) ; if ( isBaseClassVar && ! isClassVar ) { if ( ! symbol . hasTypedDeclarations ( ) ) { isClassVar = true ; } } if ( isBaseClassVar !== isClassVar ) { const unformattedMessage = symbol . isClassVar ( ) ? Localizer . Diagnostic . classVarOverridesInstanceVar ( ) : Localizer . Diagnostic . instanceVarOverridesClassVar ( ) ; const diag = this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportIncompatibleVariableOverride , DiagnosticRule . reportIncompatibleVariableOverride , unformattedMessage . format ( { name , className : baseClassAndSymbol . classType . details . name , } ) , lastDecl . node ) ; const origDecl = getLastTypedDeclaredForSymbol ( baseClassAndSymbol . symbol ) ; if ( diag && origDecl ) { diag . addRelatedInfo ( Localizer . DiagnosticAddendum . overriddenSymbol ( ) , origDecl . path , origDecl . range ) ; } } } } } } } ) ; } private _validateMethod ( node , functionType , classNode ) { const classTypeInfo = this . _evaluator . getTypeOfClass ( classNode ) ; const classType = classTypeInfo ?. classType ; if ( node . name && node . name . value === '__new__' ) { if ( node . parameters . length === 0 || ! node . parameters [ 0 ] . name || ! [ 'cls' , '_cls' , '__cls' , '__mcls' ] . some ( ( name ) => node . parameters [ 0 ] . name ! . value === name ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportSelfClsParameterName , DiagnosticRule . reportSelfClsParameterName , Localizer . Diagnostic . newClsParam ( ) , node . parameters . length > 0 ? node . parameters [ 0 ] : node . name ) ; } if ( classType ) { this . _validateClsSelfParameterType ( functionType , classType , true ) ; } } else if ( node . name && node . name . value === '__init_subclass__' ) { if ( node . parameters . length === 0 || ! node . parameters [ 0 ] . name || node . parameters [ 0 ] . name . value !== 'cls' ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportSelfClsParameterName , DiagnosticRule . reportSelfClsParameterName , Localizer . Diagnostic . initSubclassClsParam ( ) , node . parameters . length > 0 ? node . parameters [ 0 ] : node . name ) ; } if ( classType ) { this . _validateClsSelfParameterType ( functionType , classType , true ) ; } } else if ( node . name && node . name . value === '__class_getitem__' ) { if ( node . parameters . length === 0 || ! node . parameters [ 0 ] . name || node . parameters [ 0 ] . name . value !== 'cls' ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportSelfClsParameterName , DiagnosticRule . reportSelfClsParameterName , Localizer . Diagnostic . classGetItemClsParam ( ) , node . parameters . length > 0 ? node . parameters [ 0 ] : node . name ) ; } if ( classType ) { this . _validateClsSelfParameterType ( functionType , classType , true ) ; } } else if ( FunctionType . isStaticMethod ( functionType ) ) { if ( node . parameters . length > 0 && node . parameters [ 0 ] . name ) { const paramName = node . parameters [ 0 ] . name . value ; if ( paramName === 'self' || paramName === 'cls' ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportSelfClsParameterName , DiagnosticRule . reportSelfClsParameterName , Localizer . Diagnostic . staticClsSelfParam ( ) , node . parameters [ 0 ] . name ) ; } } } else if ( FunctionType . isClassMethod ( functionType ) ) { let paramName = '' ; if ( node . parameters . length > 0 && node . parameters [ 0 ] . name ) { paramName = node . parameters [ 0 ] . name . value ; } if ( paramName !== 'cls' ) { if ( ! this . _fileInfo . isStubFile || ( ! paramName . startsWith ( '_' ) && paramName !== 'metacls' ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportSelfClsParameterName , DiagnosticRule . reportSelfClsParameterName , Localizer . Diagnostic . classMethodClsParam ( ) , node . parameters . length > 0 ? node . parameters [ 0 ] : node . name ) ; } } if ( classType ) { this . _validateClsSelfParameterType ( functionType , classType , true ) ; } } else { if ( node . decorators . length === 0 ) { let paramName = '' ; let firstParamIsSimple = true ; if ( node . parameters . length > 0 ) { if ( node . parameters [ 0 ] . name ) { paramName = node . parameters [ 0 ] . name . value ; } if ( node . parameters [ 0 ] . category !== ParameterCategory . Simple ) { firstParamIsSimple = false ; } } if ( firstParamIsSimple && paramName !== 'self' ) { let isLegalMetaclassName = false ; if ( paramName === 'cls' ) { const classTypeInfo = this . _evaluator . getTypeOfClass ( classNode ) ; const typeType = this . _evaluator . getBuiltInType ( classNode , 'type' ) ; if ( typeType && isInstantiableClass ( typeType ) && classTypeInfo && isInstantiableClass ( classTypeInfo . classType ) ) { if ( derivesFromClassRecursive ( classTypeInfo . classType , typeType , true ) ) { isLegalMetaclassName = true ; } } } const isPrivateName = SymbolNameUtils . isPrivateOrProtectedName ( paramName ) ; if ( ! isLegalMetaclassName && ! isPrivateName ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportSelfClsParameterName , DiagnosticRule . reportSelfClsParameterName , Localizer . Diagnostic . instanceMethodSelfParam ( ) , node . parameters . length > 0 ? node . parameters [ 0 ] : node . name ) ; } } } if ( classType ) { this . _validateClsSelfParameterType ( functionType , classType , false ) ; } } } private _validateClsSelfParameterType ( functionType , classType , isCls ) { if ( functionType . details . parameters . length < 1 ) { return ; } const paramInfo = functionType . details . parameters [ 0 ] ; if ( ! paramInfo . typeAnnotation || ! paramInfo . name ) { return ; } if ( ClassType . isProtocolClass ( classType ) ) { return ; } const paramType = this . _evaluator . makeTopLevelTypeVarsConcrete ( paramInfo . type ) ; const expectedType = isCls ? classType : convertToInstance ( classType ) ; if ( isInstantiableClass ( paramType ) && ClassType . isProtocolClass ( paramType ) ) { return ; } if ( isClassInstance ( paramType ) && ClassType . isProtocolClass ( paramType ) ) { return ; } if ( FunctionType . isOverloaded ( functionType ) ) { return ; } if ( ! this . _evaluator . canAssignType ( paramType , expectedType ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . clsSelfParamTypeMismatch ( ) . format ( { name : paramInfo . name , classType : this . _evaluator . printType ( expectedType , false ) , } ) , paramInfo . typeAnnotation ) ; } } private _validateYieldType ( node , yieldType ) { let declaredReturnType ; let declaredYieldType ; const enclosingFunctionNode = ParseTreeUtils . getEnclosingFunction ( node ) ; if ( enclosingFunctionNode ) { const functionTypeResult = this . _evaluator . getTypeOfFunction ( enclosingFunctionNode ) ; if ( functionTypeResult ) { assert ( isFunction ( functionTypeResult . functionType ) ) ; declaredReturnType = FunctionType . getSpecializedReturnType ( functionTypeResult . functionType ) ; if ( declaredReturnType ) { declaredYieldType = getGeneratorYieldType ( declaredReturnType , ! ! enclosingFunctionNode . isAsync ) ; } if ( declaredReturnType && ! declaredYieldType && enclosingFunctionNode . returnTypeAnnotation ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , enclosingFunctionNode . isAsync ? Localizer . Diagnostic . generatorAsyncReturnType ( ) : Localizer . Diagnostic . generatorSyncReturnType ( ) , enclosingFunctionNode . returnTypeAnnotation ) ; } } } if ( this . _evaluator . isNodeReachable ( node , undefined ) ) { if ( declaredReturnType && isNoReturnType ( declaredReturnType ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . noReturnContainsYield ( ) , node ) ; } else if ( declaredYieldType ) { const diagAddendum = new DiagnosticAddendum ( ) ; if ( ! this . _evaluator . canAssignType ( declaredYieldType , yieldType , diagAddendum ) ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . yieldTypeMismatch ( ) . format ( { exprType : this . _evaluator . printType ( yieldType , false ) , yieldType : this . _evaluator . printType ( declaredYieldType , false ) , } ) + diagAddendum . getString ( ) , node . expression || node ) ; } } } } private _reportUnusedExceptStatements ( node ) { let sawUnknownOrAny = false ; const exceptionTypesSoFar = [ ] ; node . exceptClauses . forEach ( ( except ) => { if ( sawUnknownOrAny || except . isExceptGroup || ! except . typeExpression ) { return ; } const exceptionType = this . _evaluator . getType ( except . typeExpression ) ; if ( ! exceptionType || isAnyOrUnknown ( exceptionType ) ) { sawUnknownOrAny = true ; return ; } const typesForThisExcept = [ ] ; if ( isInstantiableClass ( exceptionType ) ) { if ( exceptionType . includeSubclasses ) { sawUnknownOrAny = true ; } typesForThisExcept . push ( exceptionType ) ; } else if ( isClassInstance ( exceptionType ) ) { const iterableType = this . _evaluator . getTypeFromIterator ( exceptionType , false , undefined ) || UnknownType . create ( ) ; doForEachSubtype ( iterableType , ( subtype ) => { if ( isAnyOrUnknown ( subtype ) ) { sawUnknownOrAny = true ; } if ( isInstantiableClass ( subtype ) ) { if ( subtype . includeSubclasses ) { sawUnknownOrAny = true ; } typesForThisExcept . push ( subtype ) ; } } ) ; } if ( exceptionTypesSoFar . length > 0 ) { const diagAddendum = new DiagnosticAddendum ( ) ; let overriddenExceptionCount = 0 ; typesForThisExcept . forEach ( ( thisExceptType ) => { const subtype = exceptionTypesSoFar . find ( ( previousExceptType ) => { return derivesFromClassRecursive ( thisExceptType , previousExceptType , true ) ; } ) ; if ( subtype ) { diagAddendum . addMessage ( Localizer . DiagnosticAddendum . unreachableExcept ( ) . format ( { exceptionType : this . _evaluator . printType ( convertToInstance ( thisExceptType ) ) , parentType : this . _evaluator . printType ( convertToInstance ( subtype ) ) , } ) ) ; overriddenExceptionCount ++ ; } } ) ; if ( typesForThisExcept . length === overriddenExceptionCount ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . unreachableExcept ( ) + diagAddendum . getString ( ) , except . typeExpression ) ; this . _evaluator . addUnusedCode ( except , except . exceptSuite ) ; } } exceptionTypesSoFar . push ( ... typesForThisExcept ) ; } ) ; } private _reportDuplicateImports ( ) { const importStatements = getTopLevelImports ( this . _moduleNode ) ; const importModuleMap = new Map < string , ImportAsNode > ( ) ; importStatements . orderedImports . forEach ( ( importStatement ) => { if ( importStatement . node . nodeType === ParseNodeType . ImportFrom ) { const symbolMap = new Map < string , ImportFromAsNode > ( ) ; importStatement . node . imports . forEach ( ( importFromAs ) => { if ( ! importFromAs . alias ) { const prevImport = symbolMap . get ( importFromAs . name . value ) ; if ( prevImport ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportDuplicateImport , DiagnosticRule . reportDuplicateImport , Localizer . Diagnostic . duplicateImport ( ) . format ( { importName : importFromAs . name . value } ) , importFromAs . name ) ; } else { symbolMap . set ( importFromAs . name . value , importFromAs ) ; } } } ) ; } else if ( importStatement . subnode ) { if ( ! importStatement . subnode . alias ) { const prevImport = importModuleMap . get ( importStatement . moduleName ) ; if ( prevImport ) { this . _evaluator . addDiagnostic ( this . _fileInfo . diagnosticRuleSet . reportDuplicateImport , DiagnosticRule . reportDuplicateImport , Localizer . Diagnostic . duplicateImport ( ) . format ( { importName : importStatement . moduleName } ) , importStatement . subnode ) ; } else { importModuleMap . set ( importStatement . moduleName , importStatement . subnode ) ; } } } } ) ; } }