private PsiType rawTypeForTypeParameter(final PsiTypeParameter typeParameter) {
   final PsiClassType[] extendsTypes = typeParameter.getExtendsListTypes();
   if (extendsTypes.length > 0) {
     // First bound
     return substitute(extendsTypes[0]);
   }
   // Object
   return PsiType.getJavaLangObject(typeParameter.getManager(), typeParameter.getResolveScope());
 }
 @NotNull
 public static PsiClass[] getInterfaces(@NotNull PsiTypeParameter typeParameter) {
   final PsiClassType[] referencedTypes = typeParameter.getExtendsListTypes();
   if (referencedTypes.length == 0) {
     return PsiClass.EMPTY_ARRAY;
   }
   final List<PsiClass> result = new ArrayList<PsiClass>(referencedTypes.length);
   for (PsiClassType referencedType : referencedTypes) {
     final PsiClass psiClass = referencedType.resolve();
     if (psiClass != null && psiClass.isInterface()) {
       result.add(psiClass);
     }
   }
   return result.toArray(new PsiClass[result.size()]);
 }
  private PsiType addBounds(PsiType substituted, final PsiTypeParameter typeParameter) {
    PsiElement captureContext = null;
    if (substituted instanceof PsiCapturedWildcardType) {
      final PsiCapturedWildcardType captured = (PsiCapturedWildcardType) substituted;
      substituted = captured.getWildcard();
      captureContext = captured.getContext();
    }
    if (substituted instanceof PsiWildcardType && !((PsiWildcardType) substituted).isSuper()) {
      PsiType originalBound = ((PsiWildcardType) substituted).getBound();
      PsiManager manager = typeParameter.getManager();
      final PsiType[] boundTypes = typeParameter.getExtendsListTypes();
      for (PsiType boundType : boundTypes) {
        PsiType substitutedBoundType = boundType.accept(mySimpleSubstitutionVisitor);
        PsiWildcardType wildcardType = (PsiWildcardType) substituted;
        if (substitutedBoundType != null
            && !(substitutedBoundType instanceof PsiWildcardType)
            && !substitutedBoundType.equalsToText("java.lang.Object")) {
          if (originalBound == null
              || (!TypeConversionUtil.erasure(substitutedBoundType)
                      .isAssignableFrom(TypeConversionUtil.erasure(originalBound))
                  && !TypeConversionUtil.erasure(substitutedBoundType)
                      .isAssignableFrom(
                          originalBound))) { // erasure is essential to avoid infinite recursion
            if (wildcardType.isExtends()) {
              final PsiType glb =
                  GenericsUtil.getGreatestLowerBound(wildcardType.getBound(), substitutedBoundType);
              if (glb != null) {
                substituted = PsiWildcardType.createExtends(manager, glb);
              }
            } else {
              // unbounded
              substituted = PsiWildcardType.createExtends(manager, substitutedBoundType);
            }
          }
        }
      }
    }

    if (captureContext != null) {
      LOG.assertTrue(substituted instanceof PsiWildcardType);
      substituted = PsiCapturedWildcardType.create((PsiWildcardType) substituted, captureContext);
    }
    return substituted;
  }
  public static Pair<PsiTypeParameter, PsiType> findTypeParameterWithBoundError(
      final PsiTypeParameter[] typeParams,
      final PsiSubstitutor substitutor,
      final PsiElement context,
      final boolean allowUncheckedConversion) {
    nextTypeParam:
    for (PsiTypeParameter typeParameter : typeParams) {
      PsiType substituted = substitutor.substitute(typeParameter);
      if (substituted == null) return null;
      substituted = PsiUtil.captureToplevelWildcards(substituted, context);

      PsiClassType[] extendsTypes = typeParameter.getExtendsListTypes();
      for (PsiClassType type : extendsTypes) {
        PsiType extendsType = substitutor.substitute(type);
        if (substituted instanceof PsiWildcardType) {
          if (((PsiWildcardType) substituted).isSuper()) {
            continue;
          }
          final PsiType extendsBound = ((PsiWildcardType) substituted).getExtendsBound();
          if (acceptExtendsBound(extendsType, extendsBound)) {
            continue nextTypeParam;
          }
        } else if (substituted instanceof PsiIntersectionType) {
          for (PsiType extendsBound : ((PsiIntersectionType) substituted).getConjuncts()) {
            if (acceptExtendsBound(extendsType, extendsBound)) continue nextTypeParam;
          }
        } else if (substituted instanceof PsiCapturedWildcardType) {
          final PsiType extendsBound = ((PsiCapturedWildcardType) substituted).getUpperBound();
          if (acceptExtendsBound(extendsType, extendsBound)) {
            continue nextTypeParam;
          }
        }
        if (extendsType != null
            && !TypeConversionUtil.isAssignable(
                extendsType, substituted, allowUncheckedConversion)) {
          return Pair.create(typeParameter, extendsType);
        }
      }
    }
    return null;
  }
  @SuppressWarnings({"HardCodedStringLiteral"})
  private static String generateClassInfo(PsiClass aClass) {
    StringBuilder buffer = new StringBuilder();
    GroovyFile file = (GroovyFile) aClass.getContainingFile();

    String packageName = file.getPackageName();
    if (packageName.length() > 0) {
      buffer.append(packageName).append("\n");
    }

    final String classString =
        aClass.isInterface()
            ? "interface"
            : aClass instanceof PsiTypeParameter
                ? "type parameter"
                : aClass.isEnum() ? "enum" : "class";
    buffer.append(classString).append(" ").append(aClass.getName());

    if (aClass.hasTypeParameters()) {
      PsiTypeParameter[] typeParameters = aClass.getTypeParameters();

      buffer.append("<");

      for (int i = 0; i < typeParameters.length; i++) {
        if (i > 0) buffer.append(", ");

        PsiTypeParameter tp = typeParameters[i];

        buffer.append(tp.getName());

        PsiClassType[] refs = tp.getExtendsListTypes();

        if (refs.length > 0) {
          buffer.append(" extends ");

          for (int j = 0; j < refs.length; j++) {
            if (j > 0) buffer.append(" & ");
            appendTypeString(buffer, refs[j], aClass);
          }
        }
      }

      buffer.append(">");
    }

    PsiClassType[] refs = aClass.getExtendsListTypes();
    if (refs.length > 0
        || !aClass.isInterface()
            && !CommonClassNames.JAVA_LANG_OBJECT.equals(aClass.getQualifiedName())) {
      buffer.append(" extends ");
      if (refs.length == 0) {
        buffer.append("Object");
      } else {
        for (int i = 0; i < refs.length; i++) {
          if (i > 0) buffer.append(", ");
          appendTypeString(buffer, refs[i], aClass);
        }
      }
    }

    refs = aClass.getImplementsListTypes();
    if (refs.length > 0) {
      buffer.append("\nimplements ");
      for (int i = 0; i < refs.length; i++) {
        if (i > 0) buffer.append(", ");
        appendTypeString(buffer, refs[i], aClass);
      }
    }

    return buffer.toString();
  }