private PsiWildcardType rebound(PsiWildcardType type, PsiType newBound) {
      LOG.assertTrue(type.getBound() != null);
      LOG.assertTrue(newBound.isValid());

      if (type.isExtends()) {
        if (newBound.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) {
          return PsiWildcardType.createUnbounded(type.getManager());
        } else {
          return PsiWildcardType.createExtends(type.getManager(), newBound);
        }
      } else {
        return PsiWildcardType.createSuper(type.getManager(), newBound);
      }
    }
  public static boolean provablyDistinct(PsiWildcardType type1, PsiWildcardType type2) {
    if (type1.isSuper() && type2.isSuper()) return false;
    if (type1.isExtends() && type2.isExtends()) {
      final PsiType extendsBound1 = type1.getExtendsBound();
      final PsiType extendsBound2 = type2.getExtendsBound();
      if (extendsBound1 instanceof PsiArrayType
              && proveArrayTypeDistinct(
                  type1.getManager().getProject(), (PsiArrayType) extendsBound1, extendsBound2)
          || extendsBound2 instanceof PsiArrayType
              && proveArrayTypeDistinct(
                  type1.getManager().getProject(), (PsiArrayType) extendsBound2, extendsBound1))
        return true;

      final PsiClass boundClass1 = PsiUtil.resolveClassInType(extendsBound1);
      final PsiClass boundClass2 = PsiUtil.resolveClassInType(extendsBound2);
      if (boundClass1 != null && boundClass2 != null) {
        return proveExtendsBoundsDistinct(type1, type2, boundClass1, boundClass2);
      }
      return provablyDistinct(extendsBound1, extendsBound2, 1);
    }
    if (type2.isExtends()) return provablyDistinct(type2, type1);
    if (type1.isExtends() && type2.isSuper()) {
      final PsiType extendsBound = type1.getExtendsBound();
      final PsiType superBound = type2.getSuperBound();
      if (extendsBound instanceof PsiArrayType
              && proveArrayTypeDistinct(
                  type1.getManager().getProject(), (PsiArrayType) extendsBound, superBound)
          || superBound instanceof PsiArrayType
              && proveArrayTypeDistinct(
                  type1.getManager().getProject(), (PsiArrayType) superBound, extendsBound))
        return true;

      final PsiClass extendsBoundClass = PsiUtil.resolveClassInType(extendsBound);
      final PsiClass superBoundClass = PsiUtil.resolveClassInType(superBound);
      if (extendsBoundClass != null && superBoundClass != null) {
        if (extendsBoundClass instanceof PsiTypeParameter) {
          return try2ProveTypeParameterDistinct(type2, extendsBoundClass);
        }
        if (superBoundClass instanceof PsiTypeParameter) return false;
        return !InheritanceUtil.isInheritorOrSelf(superBoundClass, extendsBoundClass, true);
      }
      return true;
    }

    if (!type1.isBounded() || !type2.isBounded()) {
      return false;
    }
    return !type1.equals(type2);
  }
 private static PsiType handleBoundComposition(
     PsiWildcardType wildcardType, PsiWildcardType bound) {
   if (bound.isExtends() == wildcardType.isExtends()) {
     final PsiType newBoundBound = bound.getBound();
     if (newBoundBound != null) {
       return PsiWildcardType.changeBound(wildcardType, newBoundBound);
     }
   }
   return PsiWildcardType.createUnbounded(wildcardType.getManager());
 }
    @Override
    public PsiType visitWildcardType(PsiWildcardType wildcardType) {
      final PsiType bound = wildcardType.getBound();
      if (bound == null) {
        return wildcardType;
      } else {
        final PsiType newBound = bound.accept(this);
        if (newBound == null) {
          return null;
        }
        assert newBound.isValid() : newBound.getClass() + "; " + bound.isValid();
        if (newBound instanceof PsiWildcardType) {
          final PsiType newBoundBound = ((PsiWildcardType) newBound).getBound();
          return !((PsiWildcardType) newBound).isBounded()
              ? PsiWildcardType.createUnbounded(wildcardType.getManager())
              : rebound(wildcardType, newBoundBound);
        }

        return newBound == PsiType.NULL ? newBound : rebound(wildcardType, newBound);
      }
    }