static boolean provablyDistinct(PsiType type1, PsiType type2, int level) {
    if (type1 instanceof PsiWildcardType) {
      if (type2 instanceof PsiWildcardType) {
        return provablyDistinct((PsiWildcardType)type1, (PsiWildcardType)type2, true, level);
      }

      if (level > 1) return true;
      if (type2 instanceof PsiCapturedWildcardType) {
        return provablyDistinct((PsiWildcardType)type1, ((PsiCapturedWildcardType)type2).getWildcard(), false, level);
      }

      if (type2 instanceof PsiClassType) {
        final PsiClass psiClass2 = PsiUtil.resolveClassInType(type2);
        if (psiClass2 == null) return false;

        if (((PsiWildcardType)type1).isExtends()) {
          final PsiType extendsBound = ((PsiWildcardType)type1).getExtendsBound();
          if (extendsBound instanceof PsiArrayType &&
              proveArrayTypeDistinct((PsiArrayType)extendsBound, type2)) return true;
          final PsiClass boundClass1 = PsiUtil.resolveClassInType(extendsBound);
          if (boundClass1 == null) return false;

          if (CommonClassNames.JAVA_LANG_OBJECT.equals(psiClass2.getQualifiedName()) && !(boundClass1 instanceof PsiTypeParameter)) {
            return !CommonClassNames.JAVA_LANG_OBJECT.equals(boundClass1.getQualifiedName());
          }

          return proveExtendsBoundsDistinct(type1, type2, boundClass1, psiClass2);
        }

        if (((PsiWildcardType)type1).isSuper()) {
          final PsiType superBound = ((PsiWildcardType)type1).getSuperBound();
          if (superBound instanceof PsiArrayType &&
              proveArrayTypeDistinct((PsiArrayType)superBound, type2)) return true;

          final PsiClass boundClass1 = PsiUtil.resolveClassInType(superBound);
          if (boundClass1 == null) return false;
          if (boundClass1 instanceof PsiTypeParameter) {
            final PsiClassType[] extendsListTypes = boundClass1.getExtendsListTypes();
            for (PsiClassType classType : extendsListTypes) {
              final PsiClass psiClass = classType.resolve();
              if (InheritanceUtil.isInheritorOrSelf(psiClass, psiClass2, true) || InheritanceUtil.isInheritorOrSelf(psiClass2, psiClass, true)) return false;
            }
            return extendsListTypes.length > 0;
          }

          return !InheritanceUtil.isInheritorOrSelf(boundClass1, psiClass2, true);
        }

        final PsiType bound = ((PsiWildcardType)type1).getBound();
        return bound != null && !bound.equals(type2);
      }

      if (type2 instanceof PsiArrayType) {
        return proveArrayTypeDistinct((PsiArrayType)type2, type1);
      }
    } else {

      if (type2 instanceof PsiWildcardType) return provablyDistinct(type2, type1, level);

      if (type1 instanceof PsiCapturedWildcardType) return provablyDistinct(((PsiCapturedWildcardType)type1).getWildcard(), type2, level);
      if (type2 instanceof PsiCapturedWildcardType) return provablyDistinct(type2, type1, level);
    }


    final PsiClassType.ClassResolveResult classResolveResult1 = PsiUtil.resolveGenericsClassInType(type1);
    final PsiClassType.ClassResolveResult classResolveResult2 = PsiUtil.resolveGenericsClassInType(type2);

    final PsiClass boundClass1 = classResolveResult1.getElement();
    final PsiClass boundClass2 = classResolveResult2.getElement();

    if (boundClass1 instanceof PsiTypeParameter && level < 2) {
      if (!distinguishFromTypeParam((PsiTypeParameter)boundClass1, type1, type2)) return false;
    }

    if (boundClass2 instanceof PsiTypeParameter && level < 2) {
      if (!distinguishFromTypeParam((PsiTypeParameter)boundClass2, type2, type1)) return false;
    }

    if (Comparing.equal(TypeConversionUtil.erasure(type1), TypeConversionUtil.erasure(type2)) &&
        !(boundClass1 instanceof PsiTypeParameter) &&
        !(boundClass2 instanceof PsiTypeParameter)) {
      final PsiSubstitutor substitutor1 = classResolveResult1.getSubstitutor();
      final PsiSubstitutor substitutor2 = classResolveResult2.getSubstitutor();
      for (PsiTypeParameter parameter : substitutor1.getSubstitutionMap().keySet()) {
        final PsiType substitutedType1 = substitutor1.substitute(parameter);
        final PsiType substitutedType2 = substitutor2.substitute(parameter);
        if (substitutedType1 == null && substitutedType2 == null){
          continue;
        }

        if (substitutedType1 == null) {
          if (type2 instanceof PsiClassType && ((PsiClassType)type2).hasParameters()) return true;
        }
        else if (substitutedType2 == null) {
          if (type1 instanceof PsiClassType && ((PsiClassType)type1).hasParameters()) return true;
        } else {
          if (provablyDistinct(substitutedType1, substitutedType2, level + 1)) return true;
        }
      }
      if (level < 2) return false;
    }

    if (boundClass1 == null || boundClass2 == null) {
      return type1 != null && type2 != null && !type1.equals(type2);
    }

    return type2 != null && type1 != null && !type1.equals(type2) &&
           !(level == 0 && boundClass1.isInterface() && boundClass2.isInterface()) &&
           (!InheritanceUtil.isInheritorOrSelf(boundClass1, boundClass2, true) ||
            !InheritanceUtil.isInheritorOrSelf(boundClass2, boundClass1, true));
  }
static boolean provablyDistinct(PsiType type1, PsiType type2, int level)