/**
  * Refines an array type with the information about the used key, and assigned element type.
  *
  * This also defines the type for the item key if available and non-dynamic.
  *
  * @param PhpType $varType
  * @param PhpType $resultType
  * @param \PHPParser_Node_Expr_ArrayDimFetch $node
  *
  * @return ArrayType
  */
 private function getRefinedArrayType(PhpType $varType, PhpType $resultType, \PHPParser_Node_Expr_ArrayDimFetch $node)
 {
     $usedKeyType = $this->getKeyTypeForDimFetch($node);
     $keyType = $varType->getKeyType();
     if ($keyType === $this->typeRegistry->getNativeType('generic_array_key')) {
         $refinedKeyType = $usedKeyType;
     } else {
         $refinedKeyType = $keyType->getLeastSupertype($usedKeyType);
     }
     $elementType = $varType->getElementType();
     if ($elementType === $this->typeRegistry->getNativeType('generic_array_value')) {
         $refinedElementType = $resultType;
     } else {
         $refinedElementType = $elementType->getLeastSupertype($resultType);
     }
     $refinedType = $this->typeRegistry->getArrayType($refinedElementType, $refinedKeyType);
     // Infer the type of the key if a concrete value is available.
     NodeUtil::getValue($node->dim)->map(function ($keyName) use(&$refinedType, $resultType) {
         $refinedType = $refinedType->inferItemType($keyName, $resultType);
     });
     return $refinedType;
 }
 private function getStringRepr(PhpType $type)
 {
     switch (true) {
         case $type instanceof AllType:
             return TypeRegistry::NATIVE_ALL;
             // This handles the generic array type specially.
         // This handles the generic array type specially.
         case $type === self::$typeRegistry->getNativeType('array'):
             return 'array';
         case $type instanceof ArrayType:
             $itemTypes = $type->getItemTypes();
             if (empty($itemTypes)) {
                 return TypeRegistry::NATIVE_ARRAY . '<' . $this->getStringRepr($type->getKeyType()) . ',' . $this->getStringRepr($type->getElementType()) . '>';
             }
             return sprintf('array<%s,%s,%s>', $this->getStringRepr($type->getKeyType()), $this->getStringRepr($type->getElementType()), $this->dumpJsonLike($itemTypes, true));
         case $type instanceof FalseType:
             return TypeRegistry::NATIVE_BOOLEAN_FALSE;
         case $type instanceof BooleanType:
             return TypeRegistry::NATIVE_BOOLEAN;
         case $type instanceof CallableType:
             return TypeRegistry::NATIVE_CALLABLE;
         case $type instanceof ResourceType:
             return TypeRegistry::NATIVE_RESOURCE;
         case $type instanceof DoubleType:
             return TypeRegistry::NATIVE_DOUBLE;
         case $type instanceof IntegerType:
             return TypeRegistry::NATIVE_INTEGER;
         case $type instanceof ThisType:
             return 'this<' . $type->getReferenceName() . '>';
         case $type instanceof NamedType:
             // If this type has been resolved, we can get the representation
             // of the resolved type instead of using the reference name.
             if (!$type->isNoResolvedType()) {
                 return $this->getStringRepr($type->getReferencedType());
             }
             return 'object<' . $type->getReferenceName() . '>';
         case $type instanceof NoObjectType:
             return TypeRegistry::NATIVE_OBJECT;
         case $type instanceof NoType:
             return TypeRegistry::NATIVE_NONE;
         case $type instanceof NullType:
             return TypeRegistry::NATIVE_NULL;
         case $type instanceof ObjectType:
             return 'object<' . $type->getName() . '>';
         case $type instanceof StringType:
             return TypeRegistry::NATIVE_STRING;
         case $type instanceof UnionType:
             $alt = array();
             foreach ($type->getAlternates() as $t) {
                 $alt[] = $this->getStringRepr($t);
             }
             return implode('|', $alt);
         case $type instanceof UnknownType:
             return $type->isChecked() ? TypeRegistry::NATIVE_UNKNOWN_CHECKED : TypeRegistry::NATIVE_UNKNOWN;
     }
     throw new \InvalidArgumentException(sprintf('The SWITCH statement is exhaustive, but got "%s".', get_class($type)));
 }