import { assert } from '../common/debug' ; import { AnyType , ClassType , maxTypeRecursionCount , ParamSpecValue , Type , TypeCategory , TypeVarScopeId , TypeVarType , WildcardTypeVarScopeId , } from './types' ; import { doForEachSubtype } from './typeUtils' ; export interface TypeVarMapEntry { typeVar : TypeVarType ; narrowBound ? : Type | undefined ; wideBound ? : Type | undefined ; retainLiteral ? : boolean | undefined ; } export interface ParamSpecMapEntry { paramSpec : TypeVarType ; type : ParamSpecValue ; } export interface VariadicTypeVarMapEntry { typeVar : TypeVarType ; types : Type [ ] ; } export class TypeVarMap { private _solveForScopes : TypeVarScopeId [ ] | undefined ; private _typeVarMap : Map < string , TypeVarMapEntry > ; private _variadicTypeVarMap : Map < string , VariadicTypeVarMapEntry > | undefined ; private _paramSpecMap : Map < string , ParamSpecMapEntry > ; private _isLocked = false ; constructor ( solveForScopes ? ) { if ( Array . isArray ( solveForScopes ) ) { this . _solveForScopes = solveForScopes ; } else if ( solveForScopes !== undefined ) { this . _solveForScopes = [ solveForScopes ] ; } else { this . _solveForScopes = undefined ; } this . _typeVarMap = new Map < string , TypeVarMapEntry > ( ) ; this . _paramSpecMap = new Map < string , ParamSpecMapEntry > ( ) ; } clone ( ) { const newTypeVarMap = new TypeVarMap ( ) ; if ( this . _solveForScopes ) { newTypeVarMap . _solveForScopes = [ ... this . _solveForScopes ] ; } this . _typeVarMap . forEach ( ( value ) => { newTypeVarMap . setTypeVarType ( value . typeVar , value . narrowBound , value . wideBound , value . retainLiteral ) ; } ) ; this . _paramSpecMap . forEach ( ( value ) => { newTypeVarMap . setParamSpec ( value . paramSpec , value . type ) ; } ) ; if ( this . _variadicTypeVarMap ) { this . _variadicTypeVarMap . forEach ( ( value ) => { newTypeVarMap . setVariadicTypeVar ( value . typeVar , value . types ) ; } ) ; } newTypeVarMap . _isLocked = this . _isLocked ; return newTypeVarMap ; } copyFromClone ( clone ) { this . _typeVarMap = clone . _typeVarMap ; this . _paramSpecMap = clone . _paramSpecMap ; this . _variadicTypeVarMap = clone . _variadicTypeVarMap ; this . _isLocked = clone . _isLocked ; } getSolveForScopes ( ) { return this . _solveForScopes ; } hasSolveForScope ( scopeId ) { return ( scopeId !== undefined && this . _solveForScopes !== undefined && this . _solveForScopes . some ( ( s ) => s === scopeId || s === WildcardTypeVarScopeId ) ) ; } setSolveForScopes ( scopeIds ) { this . _solveForScopes = scopeIds ; } addSolveForScope ( scopeId ? ) { if ( scopeId !== undefined && ! this . hasSolveForScope ( scopeId ) ) { if ( ! this . _solveForScopes ) { this . _solveForScopes = [ ] ; } this . _solveForScopes . push ( scopeId ) ; } } isEmpty ( ) { return this . _typeVarMap . size === 0 && this . _paramSpecMap . size === 0 ; } getScore ( ) { let score = 0 ; this . _typeVarMap . forEach ( ( value ) => { score += 1 ; const typeVarType = this . getTypeVarType ( value . typeVar ) ! ; score += 1.0 - this . _getComplexityScoreForType ( typeVarType ) ; } ) ; score += this . _paramSpecMap . size ; return score ; } hasTypeVar ( reference ) { return this . _typeVarMap . has ( this . _getKey ( reference ) ) ; } getTypeVarType ( reference , useNarrowBoundOnly = false ) { const entry = this . _typeVarMap . get ( this . _getKey ( reference ) ) ; if ( ! entry ) { return undefined ; } if ( entry . narrowBound ) { return entry . narrowBound ; } if ( ! useNarrowBoundOnly ) { return entry . wideBound ; } return undefined ; } setTypeVarType ( reference , narrowBound , wideBound ? , retainLiteral ? ) { assert ( ! this . _isLocked ) ; const key = this . _getKey ( reference ) ; this . _typeVarMap . set ( key , { typeVar : reference , narrowBound , wideBound , retainLiteral } ) ; } getVariadicTypeVar ( reference ) { return this . _variadicTypeVarMap ?. get ( this . _getKey ( reference ) ) ?. types ; } setVariadicTypeVar ( reference , types ) { assert ( ! this . _isLocked ) ; const key = this . _getKey ( reference ) ; if ( ! this . _variadicTypeVarMap ) { this . _variadicTypeVarMap = new Map < string , VariadicTypeVarMapEntry > ( ) ; } this . _variadicTypeVarMap . set ( key , { typeVar : reference , types } ) ; } getTypeVar ( reference ) { const key = this . _getKey ( reference ) ; return this . _typeVarMap . get ( key ) ; } getTypeVars ( ) { const entries = [ ] ; this . _typeVarMap . forEach ( ( entry ) => { entries . push ( entry ) ; } ) ; return entries ; } hasParamSpec ( reference ) { return this . _paramSpecMap . has ( this . _getKey ( reference ) ) ; } getParamSpec ( reference ) { return this . _paramSpecMap . get ( this . _getKey ( reference ) ) ?. type ; } setParamSpec ( reference , type ) { assert ( ! this . _isLocked ) ; this . _paramSpecMap . set ( this . _getKey ( reference ) , { paramSpec : reference , type } ) ; } typeVarCount ( ) { return this . _typeVarMap . size ; } getWideTypeBound ( reference ) { const entry = this . _typeVarMap . get ( this . _getKey ( reference ) ) ; if ( entry ) { return entry . wideBound ; } return undefined ; } getRetainLiterals ( reference ) { const entry = this . _typeVarMap . get ( this . _getKey ( reference ) ) ; return ! ! entry ?. retainLiteral ; } lock ( ) { assert ( ! this . _isLocked ) ; this . _isLocked = true ; } unlock ( ) { this . _isLocked = false ; } isLocked ( ) { return this . _isLocked ; } private _getKey ( reference ) { return TypeVarType . getNameWithScope ( reference ) ; } private _getComplexityScoreForType ( type , recursionCount = 0 ) { if ( recursionCount > maxTypeRecursionCount ) { return 1 ; } switch ( type . category ) { case TypeCategory . Unknown : case TypeCategory . Any : case TypeCategory . None : case TypeCategory . Function : case TypeCategory . OverloadedFunction : case TypeCategory . TypeVar : { return 0.5 ; } case TypeCategory . Unbound : case TypeCategory . Never : return 1.0 ; case TypeCategory . Union : { let maxScore = 0 ; if ( type . subtypes . length < 16 ) { doForEachSubtype ( type , ( subtype ) => { const subtypeScore = this . _getComplexityScoreForType ( subtype , recursionCount + 1 ) ; maxScore = Math . max ( maxScore , subtypeScore ) ; } ) ; } return 0.75 + maxScore / 4 ; } case TypeCategory . Class : { return this . _getComplexityScoreForClass ( type , recursionCount + 1 ) ; } } return 0 ; } private _getComplexityScoreForClass ( classType , recursionCount ) { let typeArgScoreSum = 0 ; let typeArgCount = 0 ; if ( classType . tupleTypeArguments ) { classType . tupleTypeArguments . forEach ( ( type ) => { typeArgScoreSum += this . _getComplexityScoreForType ( type , recursionCount + 1 ) ; typeArgCount ++ ; } ) ; } else if ( classType . typeArguments ) { classType . typeArguments . forEach ( ( type ) => { typeArgScoreSum += this . _getComplexityScoreForType ( type , recursionCount + 1 ) ; typeArgCount ++ ; } ) ; } else if ( classType . details . typeParameters ) { classType . details . typeParameters . forEach ( ( type ) => { typeArgScoreSum += this . _getComplexityScoreForType ( AnyType . create ( ) , recursionCount + 1 ) ; typeArgCount ++ ; } ) ; } const averageTypeArgComplexity = typeArgCount > 0 ? typeArgScoreSum / typeArgCount : 0 ; return 0.5 + averageTypeArgComplexity * 0.25 ; } }