import { assert } from '../common/debug' ; import { DiagnosticRule } from '../common/diagnosticRules' ; import { Localizer } from '../localization/localize' ; import { ExpressionNode , ParseNode , ParseNodeType , PatternAsNode , PatternAtomNode , PatternClassArgumentNode , PatternClassNode , PatternLiteralNode , PatternMappingNode , PatternSequenceNode , PatternValueNode , } from '../parser/parseNodes' ; import { getFileInfo } from './analyzerNodeInfo' ; import { getTypedDictMembersForClass } from './typedDicts' ; import { EvaluatorFlags , TypeEvaluator } from './typeEvaluatorTypes' ; import { enumerateLiteralsForType } from './typeGuards' ; import { AnyType , ClassType , combineTypes , isAnyOrUnknown , isClass , isClassInstance , isInstantiableClass , isNever , isSameWithoutLiteralValue , isUnknown , NeverType , Type , TypeBase , UnknownType , } from './types' ; import { addConditionToType , applySolvedTypeVars , convertToInstance , doForEachSubtype , getTypeCondition , getTypeVarScopeId , isLiteralType , isOpenEndedTupleClass , isTupleClass , lookUpClassMember , mapSubtypes , partiallySpecializeType , specializeClassType , specializeTupleClass , stripLiteralValue , } from './typeUtils' ; import { TypeVarMap } from './typeVarMap' ; const classPatternSpecialCases = [ 'builtins.bool' , 'builtins.bytearray' , 'builtins.bytes' , 'builtins.dict' , 'builtins.float' , 'builtins.frozenset' , 'builtins.int' , 'builtins.list' , 'builtins.set' , 'builtins.str' , 'builtins.tuple' , ] ; interface SequencePatternInfo { subtype : Type ; entryTypes : Type [ ] ; isIndeterminateLength ? : boolean ; isTuple ? : boolean ; isObject ? : boolean ; } interface MappingPatternInfo { subtype : Type ; typedDict ? : ClassType ; dictTypeArgs ? : { key : Type ; value : Type ; } ; } export function narrowTypeBasedOnPattern ( evaluator , type , pattern , isPositiveTest ) { switch ( pattern . nodeType ) { case ParseNodeType . PatternSequence : { return narrowTypeBasedOnSequencePattern ( evaluator , type , pattern , isPositiveTest ) ; } case ParseNodeType . PatternLiteral : { return narrowTypeBasedOnLiteralPattern ( evaluator , type , pattern , isPositiveTest ) ; } case ParseNodeType . PatternClass : { return narrowTypeBasedOnClassPattern ( evaluator , type , pattern , isPositiveTest ) ; } case ParseNodeType . PatternAs : { return narrowTypeBasedOnAsPattern ( evaluator , type , pattern , isPositiveTest ) ; } case ParseNodeType . PatternMapping : { return narrowTypeBasedOnMappingPattern ( evaluator , type , pattern , isPositiveTest ) ; } case ParseNodeType . PatternValue : { return narrowTypeBasedOnValuePattern ( evaluator , type , pattern , isPositiveTest ) ; } case ParseNodeType . PatternCapture : { return isPositiveTest ? type : NeverType . create ( ) ; } case ParseNodeType . Error : { return type ; } } } function narrowTypeBasedOnSequencePattern ( evaluator , type , pattern , isPositiveTest ) { if ( ! isPositiveTest ) { return type ; } let sequenceInfo = getSequencePatternInfo ( evaluator , type , pattern . entries . length , pattern . starEntryIndex ) ; sequenceInfo = sequenceInfo . filter ( ( entry ) => { let isPlausibleMatch = true ; const narrowedEntryTypes = [ ] ; let canNarrowTuple = entry . isTuple ; pattern . entries . forEach ( ( sequenceEntry , index ) => { const entryType = getTypeForPatternSequenceEntry ( evaluator , pattern , entry , index , pattern . entries . length , pattern . starEntryIndex , true , false ) ; const narrowedEntryType = narrowTypeBasedOnPattern ( evaluator , entryType , sequenceEntry , true ) ; if ( index === pattern . starEntryIndex ) { if ( isClassInstance ( narrowedEntryType ) && narrowedEntryType . tupleTypeArguments && ! isOpenEndedTupleClass ( narrowedEntryType ) && narrowedEntryType . tupleTypeArguments ) { narrowedEntryTypes . push ( ... narrowedEntryType . tupleTypeArguments ) ; } else { narrowedEntryTypes . push ( narrowedEntryType ) ; canNarrowTuple = false ; } } else { narrowedEntryTypes . push ( narrowedEntryType ) ; if ( isNever ( narrowedEntryType ) ) { isPlausibleMatch = false ; } } } ) ; if ( isPlausibleMatch ) { if ( canNarrowTuple ) { const tupleClassType = evaluator . getBuiltInType ( pattern , 'tuple' ) ; if ( tupleClassType && isInstantiableClass ( tupleClassType ) ) { entry . subtype = ClassType . cloneAsInstance ( specializeTupleClass ( tupleClassType , narrowedEntryTypes ) ) ; } } if ( entry . isObject ) { const sequenceType = evaluator . getTypingType ( pattern , 'Sequence' ) ; if ( sequenceType && isInstantiableClass ( sequenceType ) ) { entry . subtype = ClassType . cloneAsInstance ( ClassType . cloneForSpecialization ( sequenceType , [ stripLiteralValue ( combineTypes ( narrowedEntryTypes ) ) ] , true ) ) ; } } } return isPlausibleMatch ; } ) ; return combineTypes ( sequenceInfo . map ( ( entry ) => entry . subtype ) ) ; } function narrowTypeBasedOnAsPattern ( evaluator , type , pattern , isPositiveTest ) { let remainingType = type ; if ( ! isPositiveTest ) { pattern . orPatterns . forEach ( ( subpattern ) => { remainingType = narrowTypeBasedOnPattern ( evaluator , remainingType , subpattern , false ) ; } ) ; return remainingType ; } const narrowedTypes = pattern . orPatterns . map ( ( subpattern ) => { const narrowedSubtype = narrowTypeBasedOnPattern ( evaluator , remainingType , subpattern , true ) ; remainingType = narrowTypeBasedOnPattern ( evaluator , remainingType , subpattern , false ) ; return narrowedSubtype ; } ) ; return combineTypes ( narrowedTypes ) ; } function narrowTypeBasedOnMappingPattern ( evaluator , type , pattern , isPositiveTest ) { if ( ! isPositiveTest ) { return type ; } let mappingInfo = getMappingPatternInfo ( evaluator , type ) ; mappingInfo = mappingInfo . filter ( ( mappingSubtypeInfo ) => { let isPlausibleMatch = true ; pattern . entries . forEach ( ( mappingEntry ) => { if ( mappingSubtypeInfo . typedDict ) { if ( mappingEntry . nodeType === ParseNodeType . PatternMappingKeyEntry ) { const narrowedKeyType = narrowTypeBasedOnPattern ( evaluator , evaluator . getBuiltInObject ( pattern , 'str' ) , mappingEntry . keyPattern , isPositiveTest ) ; if ( isNever ( narrowedKeyType ) ) { isPlausibleMatch = false ; } const valueType = mapSubtypes ( narrowedKeyType , ( keySubtype ) => { if ( isAnyOrUnknown ( keySubtype ) ) { return keySubtype ; } if ( isClassInstance ( keySubtype ) && ClassType . isBuiltIn ( keySubtype , 'str' ) ) { if ( ! isLiteralType ( keySubtype ) ) { return UnknownType . create ( ) ; } const tdEntries = getTypedDictMembersForClass ( evaluator , mappingSubtypeInfo . typedDict ! ) ; const valueEntry = tdEntries . get ( keySubtype . literalValue as string ) ; if ( valueEntry ) { const narrowedValueType = narrowTypeBasedOnPattern ( evaluator , valueEntry . valueType , mappingEntry . valuePattern , true ) ; if ( ! isNever ( narrowedValueType ) ) { return narrowedValueType ; } } } return undefined ; } ) ; if ( isNever ( valueType ) ) { isPlausibleMatch = false ; } } } else if ( mappingSubtypeInfo . dictTypeArgs ) { if ( mappingEntry . nodeType === ParseNodeType . PatternMappingKeyEntry ) { const narrowedKeyType = narrowTypeBasedOnPattern ( evaluator , mappingSubtypeInfo . dictTypeArgs . key , mappingEntry . keyPattern , isPositiveTest ) ; const narrowedValueType = narrowTypeBasedOnPattern ( evaluator , mappingSubtypeInfo . dictTypeArgs . value , mappingEntry . valuePattern , isPositiveTest ) ; if ( isNever ( narrowedKeyType ) || isNever ( narrowedValueType ) ) { isPlausibleMatch = false ; } } } } ) ; return isPlausibleMatch ; } ) ; return combineTypes ( mappingInfo . map ( ( entry ) => entry . subtype ) ) ; } function getPositionalMatchArgNames ( evaluator , type ) { const matchArgsMemberInfo = lookUpClassMember ( type , '__match_args__' ) ; if ( matchArgsMemberInfo ) { const matchArgsType = evaluator . getTypeOfMember ( matchArgsMemberInfo ) ; if ( isClassInstance ( matchArgsType ) && isTupleClass ( matchArgsType ) && ! isOpenEndedTupleClass ( matchArgsType ) && matchArgsType . tupleTypeArguments ) { const tupleArgs = matchArgsType . tupleTypeArguments ; if ( ! tupleArgs . some ( ( argType ) => ! isClassInstance ( argType ) || ! ClassType . isBuiltIn ( argType , 'str' ) || ! isLiteralType ( argType ) ) ) { return tupleArgs . map ( ( argType ) => ( argType as ClassType ) . literalValue as string ) ; } } } return [ ] ; } function narrowTypeBasedOnLiteralPattern ( evaluator , type , pattern , isPositiveTest ) { const literalType = evaluator . getTypeOfExpression ( pattern . expression ) . type ; if ( ! isPositiveTest ) { return mapSubtypes ( type , ( subtype ) => { if ( isClassInstance ( literalType ) && isLiteralType ( literalType ) && isClassInstance ( subtype ) && isLiteralType ( subtype ) && evaluator . canAssignType ( literalType , subtype ) ) { return undefined ; } if ( isClassInstance ( subtype ) && ClassType . isBuiltIn ( subtype , 'bool' ) && subtype . literalValue === undefined && isClassInstance ( literalType ) && ClassType . isBuiltIn ( literalType , 'bool' ) && literalType . literalValue !== undefined ) { return ClassType . cloneWithLiteral ( literalType , ! ( literalType . literalValue as boolean ) ) ; } return subtype ; } ) ; } return mapSubtypes ( type , ( subtype ) => { if ( evaluator . canAssignType ( subtype , literalType ) ) { return literalType ; } return undefined ; } ) ; } function narrowTypeBasedOnClassPattern ( evaluator , type , pattern , isPositiveTest ) { let exprType = evaluator . getTypeOfExpression ( pattern . className , undefined , EvaluatorFlags . DoNotSpecialize ) . type ; if ( isClass ( exprType ) && ! exprType . typeAliasInfo ) { exprType = specializeClassType ( exprType ) ; } const classType = exprType ; if ( ! isPositiveTest ) { if ( ! isInstantiableClass ( classType ) ) { return type ; } let hasArguments = pattern . arguments . length > 0 ; if ( pattern . arguments . length === 1 && ! pattern . arguments [ 0 ] . name && classPatternSpecialCases . some ( ( className ) => classType . details . fullName === className ) ) { hasArguments = false ; } if ( hasArguments ) { return type ; } if ( classType . details . typeParameters . length > 0 ) { return type ; } const classInstance = convertToInstance ( classType ) ; return mapSubtypes ( type , ( subtype ) => { if ( evaluator . canAssignType ( classInstance , subtype ) ) { return undefined ; } return subtype ; } ) ; } if ( ! TypeBase . isInstantiable ( classType ) ) { evaluator . addDiagnostic ( getFileInfo ( pattern ) . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . DiagnosticAddendum . typeNotClass ( ) . format ( { type : evaluator . printType ( classType ) } ) , pattern . className ) ; return NeverType . create ( ) ; } return evaluator . mapSubtypesExpandTypeVars ( classType , undefined , ( expandedSubtype , unexpandedSubtype ) => { if ( isAnyOrUnknown ( expandedSubtype ) ) { return unexpandedSubtype ; } if ( isInstantiableClass ( expandedSubtype ) ) { return evaluator . mapSubtypesExpandTypeVars ( type , undefined , ( matchSubtype ) => { const concreteSubtype = evaluator . makeTopLevelTypeVarsConcrete ( matchSubtype ) ; if ( isAnyOrUnknown ( concreteSubtype ) ) { return convertToInstance ( unexpandedSubtype ) ; } if ( isClassInstance ( concreteSubtype ) ) { let resultType ; if ( evaluator . canAssignType ( expandedSubtype , ClassType . cloneAsInstantiable ( concreteSubtype ) ) ) { resultType = matchSubtype ; } else if ( evaluator . canAssignType ( ClassType . cloneAsInstantiable ( concreteSubtype ) , expandedSubtype ) ) { resultType = addConditionToType ( convertToInstance ( unexpandedSubtype ) , getTypeCondition ( matchSubtype ) ) ; if ( isInstantiableClass ( unexpandedSubtype ) && isClassInstance ( matchSubtype ) ) { if ( ClassType . isSpecialBuiltIn ( unexpandedSubtype ) || unexpandedSubtype . details . typeParameters . length > 0 ) { const typeVarMap = new TypeVarMap ( getTypeVarScopeId ( unexpandedSubtype ) ) ; const unspecializedMatchType = ClassType . cloneForSpecialization ( unexpandedSubtype , undefined , false ) ; if ( evaluator . populateTypeVarMapBasedOnExpectedType ( unspecializedMatchType , matchSubtype , typeVarMap , [ ] ) ) { resultType = applySolvedTypeVars ( ClassType . cloneAsInstance ( unspecializedMatchType ) , typeVarMap , true ) as ClassType ; } } } } else { return undefined ; } let positionalArgNames = [ ] ; if ( pattern . arguments . some ( ( arg ) => ! arg . name ) ) { positionalArgNames = getPositionalMatchArgNames ( evaluator , expandedSubtype ) ; } let isMatchValid = true ; pattern . arguments . forEach ( ( arg , index ) => { const narrowedArgType = narrowTypeOfClassPatternArgument ( evaluator , arg , index , positionalArgNames , resultType ) ; if ( isNever ( narrowedArgType ) ) { isMatchValid = false ; } } ) ; if ( isMatchValid ) { return resultType ; } } return undefined ; } ) ; } return undefined ; } ) ; } function narrowTypeOfClassPatternArgument ( evaluator , arg , argIndex , positionalArgNames , matchType ) { let argName ; if ( arg . name ) { argName = arg . name . value ; } else if ( argIndex < positionalArgNames . length ) { argName = positionalArgNames [ argIndex ] ; } if ( isAnyOrUnknown ( matchType ) ) { return matchType ; } if ( ! isClass ( matchType ) ) { return UnknownType . create ( ) ; } const useSelfForPattern = isClass ( matchType ) && classPatternSpecialCases . some ( ( className ) => matchType . details . fullName === className ) && argIndex === 0 && ! arg . name ; let argType ; if ( useSelfForPattern ) { argType = ClassType . cloneAsInstance ( matchType ) ; } else { if ( argName ) { argType = evaluator . useSpeculativeMode ( arg , ( ) => evaluator . getTypeFromObjectMember ( arg as any as ExpressionNode , ClassType . cloneAsInstance ( matchType ) , argName ! ) ) ?. type ; } if ( ! argType ) { if ( ClassType . isFinal ( matchType ) ) { return NeverType . create ( ) ; } argType = UnknownType . create ( ) ; } } return narrowTypeBasedOnPattern ( evaluator , argType , arg . pattern , true ) ; } function narrowTypeBasedOnValuePattern ( evaluator , subjectType , pattern , isPositiveTest ) { const valueType = evaluator . getTypeOfExpression ( pattern . expression ) . type ; const narrowedSubtypes = [ ] ; evaluator . mapSubtypesExpandTypeVars ( valueType , undefined , ( valueSubtypeExpanded , valueSubtypeUnexpanded ) => { narrowedSubtypes . push ( evaluator . mapSubtypesExpandTypeVars ( subjectType , getTypeCondition ( valueSubtypeExpanded ) , ( _ , subjectSubtypeUnexpanded ) => { if ( ! isPositiveTest ) { if ( isClassInstance ( subjectSubtypeUnexpanded ) && ClassType . isEnumClass ( subjectSubtypeUnexpanded ) && ! isLiteralType ( subjectSubtypeUnexpanded ) && isClassInstance ( valueSubtypeUnexpanded ) && isSameWithoutLiteralValue ( subjectSubtypeUnexpanded , valueSubtypeUnexpanded ) && isLiteralType ( valueSubtypeUnexpanded ) ) { const allEnumTypes = enumerateLiteralsForType ( evaluator , subjectSubtypeUnexpanded ) ; if ( allEnumTypes ) { return combineTypes ( allEnumTypes . filter ( ( enumType ) => ! ClassType . isLiteralValueSame ( valueSubtypeUnexpanded , enumType ) ) ) ; } } else if ( isClassInstance ( subjectSubtypeUnexpanded ) && isClassInstance ( valueSubtypeUnexpanded ) && ClassType . isLiteralValueSame ( valueSubtypeUnexpanded , subjectSubtypeUnexpanded ) ) { return undefined ; } return subjectSubtypeUnexpanded ; } if ( isNever ( valueSubtypeExpanded ) || isNever ( subjectSubtypeUnexpanded ) ) { return NeverType . create ( ) ; } if ( isAnyOrUnknown ( valueSubtypeExpanded ) || isAnyOrUnknown ( subjectSubtypeUnexpanded ) ) { return isUnknown ( valueSubtypeExpanded ) || isUnknown ( subjectSubtypeUnexpanded ) ? UnknownType . create ( ) : AnyType . create ( ) ; } const returnType = evaluator . useSpeculativeMode ( pattern . expression , ( ) => evaluator . getTypeFromMagicMethodReturn ( valueSubtypeExpanded , [ subjectSubtypeUnexpanded ] , '__eq__' , pattern . expression , undefined ) ) ; return returnType ? valueSubtypeUnexpanded : undefined ; } ) ) ; return undefined ; } ) ; return combineTypes ( narrowedSubtypes ) ; } function getMappingPatternInfo ( evaluator , type ) { const mappingInfo = [ ] ; doForEachSubtype ( type , ( subtype ) => { const concreteSubtype = evaluator . makeTopLevelTypeVarsConcrete ( subtype ) ; if ( isAnyOrUnknown ( concreteSubtype ) ) { mappingInfo . push ( { subtype , dictTypeArgs : { key : concreteSubtype , value : concreteSubtype , } , } ) ; } else if ( isClassInstance ( concreteSubtype ) ) { if ( ClassType . isTypedDictClass ( concreteSubtype ) ) { mappingInfo . push ( { subtype , typedDict : concreteSubtype , } ) ; } else { let mroClassToSpecialize ; for ( const mroClass of concreteSubtype . details . mro ) { if ( isInstantiableClass ( mroClass ) && ClassType . isBuiltIn ( mroClass , 'Mapping' ) ) { mroClassToSpecialize = mroClass ; break ; } } if ( mroClassToSpecialize ) { const specializedMapping = partiallySpecializeType ( mroClassToSpecialize , concreteSubtype ) as ClassType ; if ( specializedMapping . typeArguments && specializedMapping . typeArguments . length >= 2 ) { mappingInfo . push ( { subtype , dictTypeArgs : { key : specializedMapping . typeArguments [ 0 ] , value : specializedMapping . typeArguments [ 1 ] , } , } ) ; } } } } } ) ; return mappingInfo ; } function getSequencePatternInfo ( evaluator , type , entryCount , starEntryIndex ) { const sequenceInfo = [ ] ; const minEntryCount = starEntryIndex === undefined ? entryCount : entryCount - 1 ; doForEachSubtype ( type , ( subtype ) => { const concreteSubtype = evaluator . makeTopLevelTypeVarsConcrete ( subtype ) ; let mroClassToSpecialize ; if ( isAnyOrUnknown ( concreteSubtype ) ) { sequenceInfo . push ( { subtype , entryTypes : [ concreteSubtype ] , isIndeterminateLength : true , } ) ; return ; } if ( isClassInstance ( concreteSubtype ) ) { if ( ClassType . isBuiltIn ( concreteSubtype , 'object' ) ) { sequenceInfo . push ( { subtype , entryTypes : [ convertToInstance ( concreteSubtype ) ] , isIndeterminateLength : true , isObject : true , } ) ; return ; } for ( const mroClass of concreteSubtype . details . mro ) { if ( ! isInstantiableClass ( mroClass ) ) { break ; } if ( ClassType . isBuiltIn ( mroClass , 'str' ) || ClassType . isBuiltIn ( mroClass , 'bytes' ) || ClassType . isBuiltIn ( mroClass , 'bytearray' ) ) { break ; } if ( ClassType . isBuiltIn ( mroClass , 'Sequence' ) ) { mroClassToSpecialize = mroClass ; break ; } if ( isTupleClass ( mroClass ) ) { mroClassToSpecialize = mroClass ; break ; } } if ( mroClassToSpecialize ) { const specializedSequence = partiallySpecializeType ( mroClassToSpecialize , concreteSubtype ) as ClassType ; if ( isTupleClass ( specializedSequence ) ) { if ( specializedSequence . tupleTypeArguments ) { if ( isOpenEndedTupleClass ( specializedSequence ) ) { sequenceInfo . push ( { subtype , entryTypes : [ specializedSequence . tupleTypeArguments [ 0 ] ] , isIndeterminateLength : true , isTuple : true , } ) ; } else { if ( specializedSequence . tupleTypeArguments . length >= minEntryCount && ( starEntryIndex !== undefined || specializedSequence . tupleTypeArguments . length === minEntryCount ) ) { sequenceInfo . push ( { subtype , entryTypes : specializedSequence . tupleTypeArguments , isIndeterminateLength : false , isTuple : true , } ) ; } } } } else { sequenceInfo . push ( { subtype , entryTypes : [ specializedSequence . typeArguments && specializedSequence . typeArguments . length > 0 ? specializedSequence . typeArguments [ 0 ] : UnknownType . create ( ) , ] , isIndeterminateLength : true , } ) ; } } } } ) ; return sequenceInfo ; } function getTypeForPatternSequenceEntry ( evaluator , node , sequenceInfo , entryIndex , entryCount , starEntryIndex , unpackStarEntry , isSubjectObject ) { if ( sequenceInfo . isIndeterminateLength ) { let entryType = sequenceInfo . entryTypes [ 0 ] ; if ( isSubjectObject ) { const objectType = evaluator . getBuiltInObject ( node , 'object' ) ; if ( objectType && isClassInstance ( objectType ) ) { entryType = objectType ; } } if ( ! unpackStarEntry && entryIndex === starEntryIndex && ! isNever ( entryType ) ) { entryType = wrapTypeInList ( evaluator , node , entryType ) ; } return entryType ; } if ( starEntryIndex === undefined || entryIndex < starEntryIndex ) { return sequenceInfo . entryTypes [ entryIndex ] ; } if ( entryIndex === starEntryIndex ) { const starEntryTypes = sequenceInfo . entryTypes . slice ( starEntryIndex , starEntryIndex + sequenceInfo . entryTypes . length - entryCount + 1 ) . map ( ( type ) => stripLiteralValue ( type ) ) ; let entryType = combineTypes ( starEntryTypes ) ; if ( ! unpackStarEntry ) { entryType = wrapTypeInList ( evaluator , node , entryType ) ; } return entryType ; } const itemIndex = sequenceInfo . entryTypes . length - ( entryCount - entryIndex ) ; assert ( itemIndex >= 0 && itemIndex < sequenceInfo . entryTypes . length ) ; return sequenceInfo . entryTypes [ itemIndex ] ; } export function assignTypeToPatternTargets ( evaluator , type , isTypeIncomplete , isSubjectObject , pattern ) { type = narrowTypeBasedOnPattern ( evaluator , type , pattern , true ) ; switch ( pattern . nodeType ) { case ParseNodeType . PatternSequence : { const sequenceInfo = getSequencePatternInfo ( evaluator , type , pattern . entries . length , pattern . starEntryIndex ) ; pattern . entries . forEach ( ( entry , index ) => { const entryType = combineTypes ( sequenceInfo . map ( ( info ) => getTypeForPatternSequenceEntry ( evaluator , pattern , info , index , pattern . entries . length , pattern . starEntryIndex , false , isSubjectObject ) ) ) ; assignTypeToPatternTargets ( evaluator , entryType , isTypeIncomplete , false , entry ) ; } ) ; break ; } case ParseNodeType . PatternAs : { if ( pattern . target ) { evaluator . assignTypeToExpression ( pattern . target , type , isTypeIncomplete , pattern . target ) ; } pattern . orPatterns . forEach ( ( orPattern ) => { assignTypeToPatternTargets ( evaluator , type , isTypeIncomplete , isSubjectObject , orPattern ) ; type = narrowTypeBasedOnPattern ( evaluator , type , orPattern , false ) ; } ) ; break ; } case ParseNodeType . PatternCapture : { evaluator . assignTypeToExpression ( pattern . target , pattern . isWildcard ? AnyType . create ( ) : type , isTypeIncomplete , pattern . target ) ; break ; } case ParseNodeType . PatternMapping : { const mappingInfo = getMappingPatternInfo ( evaluator , type ) ; pattern . entries . forEach ( ( mappingEntry ) => { const keyTypes = [ ] ; const valueTypes = [ ] ; mappingInfo . forEach ( ( mappingSubtypeInfo ) => { if ( mappingSubtypeInfo . typedDict ) { if ( mappingEntry . nodeType === ParseNodeType . PatternMappingKeyEntry ) { const keyType = narrowTypeBasedOnPattern ( evaluator , evaluator . getBuiltInObject ( pattern , 'str' ) , mappingEntry . keyPattern , true ) ; keyTypes . push ( keyType ) ; doForEachSubtype ( keyType , ( keySubtype ) => { if ( isClassInstance ( keySubtype ) && ClassType . isBuiltIn ( keySubtype , 'str' ) && isLiteralType ( keySubtype ) ) { const tdEntries = getTypedDictMembersForClass ( evaluator , mappingSubtypeInfo . typedDict ! ) ; const valueInfo = tdEntries . get ( keySubtype . literalValue as string ) ; valueTypes . push ( valueInfo ? valueInfo . valueType : UnknownType . create ( ) ) ; } else { valueTypes . push ( UnknownType . create ( ) ) ; } } ) ; } else if ( mappingEntry . nodeType === ParseNodeType . PatternMappingExpandEntry ) { keyTypes . push ( evaluator . getBuiltInObject ( pattern , 'str' ) ) ; valueTypes . push ( UnknownType . create ( ) ) ; } } else if ( mappingSubtypeInfo . dictTypeArgs ) { if ( mappingEntry . nodeType === ParseNodeType . PatternMappingKeyEntry ) { const keyType = narrowTypeBasedOnPattern ( evaluator , mappingSubtypeInfo . dictTypeArgs . key , mappingEntry . keyPattern , true ) ; keyTypes . push ( keyType ) ; valueTypes . push ( narrowTypeBasedOnPattern ( evaluator , mappingSubtypeInfo . dictTypeArgs . value , mappingEntry . valuePattern , true ) ) ; } else if ( mappingEntry . nodeType === ParseNodeType . PatternMappingExpandEntry ) { keyTypes . push ( mappingSubtypeInfo . dictTypeArgs . key ) ; valueTypes . push ( mappingSubtypeInfo . dictTypeArgs . value ) ; } } } ) ; const keyType = combineTypes ( keyTypes ) ; const valueType = combineTypes ( valueTypes ) ; if ( mappingEntry . nodeType === ParseNodeType . PatternMappingKeyEntry ) { assignTypeToPatternTargets ( evaluator , keyType , isTypeIncomplete , false , mappingEntry . keyPattern ) ; assignTypeToPatternTargets ( evaluator , valueType , isTypeIncomplete , false , mappingEntry . valuePattern ) ; } else if ( mappingEntry . nodeType === ParseNodeType . PatternMappingExpandEntry ) { const dictClass = evaluator . getBuiltInType ( pattern , 'dict' ) ; const strType = evaluator . getBuiltInObject ( pattern , 'str' ) ; const dictType = dictClass && isInstantiableClass ( dictClass ) && isClassInstance ( strType ) ? ClassType . cloneAsInstance ( ClassType . cloneForSpecialization ( dictClass , [ keyType , valueType ] , true ) ) : UnknownType . create ( ) ; evaluator . assignTypeToExpression ( mappingEntry . target , dictType , isTypeIncomplete , mappingEntry . target ) ; } } ) ; break ; } case ParseNodeType . PatternClass : { const argTypes = pattern . arguments . map ( ( arg ) => [ ] ) ; evaluator . mapSubtypesExpandTypeVars ( type , undefined , ( expandedSubtype ) => { if ( isClassInstance ( expandedSubtype ) ) { doForEachSubtype ( type , ( matchSubtype ) => { const concreteSubtype = evaluator . makeTopLevelTypeVarsConcrete ( matchSubtype ) ; if ( isAnyOrUnknown ( concreteSubtype ) ) { pattern . arguments . forEach ( ( arg , index ) => { argTypes [ index ] . push ( concreteSubtype ) ; } ) ; } else if ( isClassInstance ( concreteSubtype ) ) { let positionalArgNames = [ ] ; if ( pattern . arguments . some ( ( arg ) => ! arg . name ) ) { positionalArgNames = getPositionalMatchArgNames ( evaluator , ClassType . cloneAsInstantiable ( expandedSubtype ) ) ; } pattern . arguments . forEach ( ( arg , index ) => { const narrowedArgType = narrowTypeOfClassPatternArgument ( evaluator , arg , index , positionalArgNames , ClassType . cloneAsInstantiable ( expandedSubtype ) ) ; argTypes [ index ] . push ( narrowedArgType ) ; } ) ; } } ) ; } else { pattern . arguments . forEach ( ( arg , index ) => { argTypes [ index ] . push ( UnknownType . create ( ) ) ; } ) ; } return undefined ; } ) ; pattern . arguments . forEach ( ( arg , index ) => { assignTypeToPatternTargets ( evaluator , combineTypes ( argTypes [ index ] ) , isTypeIncomplete , false , arg . pattern ) ; } ) ; break ; } case ParseNodeType . PatternLiteral : case ParseNodeType . PatternValue : case ParseNodeType . Error : { break ; } } } function wrapTypeInList ( evaluator , node , type ) { if ( isNever ( type ) ) { return type ; } const listObjectType = convertToInstance ( evaluator . getBuiltInObject ( node , 'list' ) ) ; if ( listObjectType && isClassInstance ( listObjectType ) ) { return ClassType . cloneForSpecialization ( listObjectType , [ type ] , true ) ; } return UnknownType . create ( ) ; } export function validateClassPattern ( evaluator , pattern ) { const exprType = evaluator . getTypeOfExpression ( pattern . className , undefined , EvaluatorFlags . DoNotSpecialize ) . type ; if ( isAnyOrUnknown ( exprType ) ) { return ; } if ( exprType . typeAliasInfo && isInstantiableClass ( exprType ) && exprType . typeArguments && exprType . isTypeArgumentExplicit ) { evaluator . addDiagnostic ( getFileInfo ( pattern ) . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . classPatternTypeAlias ( ) . format ( { type : evaluator . printType ( exprType ) } ) , pattern . className ) ; } else if ( ! isInstantiableClass ( exprType ) || exprType . includeSubclasses ) { evaluator . addDiagnostic ( getFileInfo ( pattern ) . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . DiagnosticAddendum . typeNotClass ( ) . format ( { type : evaluator . printType ( exprType ) } ) , pattern . className ) ; } else { const isBuiltIn = classPatternSpecialCases . some ( ( className ) => exprType . details . fullName === className ) ; if ( isBuiltIn ) { if ( pattern . arguments . length > 1 ) { evaluator . addDiagnostic ( getFileInfo ( pattern ) . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . classPatternBuiltInArgCount ( ) , pattern . arguments [ 1 ] ) ; } else if ( pattern . arguments . length === 1 && pattern . arguments [ 0 ] . name ) { evaluator . addDiagnostic ( getFileInfo ( pattern ) . diagnosticRuleSet . reportGeneralTypeIssues , DiagnosticRule . reportGeneralTypeIssues , Localizer . Diagnostic . classPatternBuiltInArgPositional ( ) , pattern . arguments [ 0 ] . name ) ; } } } }