private Binding unify(final PsiType x, final PsiType y, final Unifier unifier) {
    final int indicator =
        (x instanceof PsiTypeVariable ? 1 : 0) + (y instanceof PsiTypeVariable ? 2 : 0);

    switch (indicator) {
      case 0:
        if (x instanceof PsiWildcardType || y instanceof PsiWildcardType) {
          return unifier.unify(x, y);
        } else if (x instanceof PsiArrayType || y instanceof PsiArrayType) {
          final PsiType xType =
              x instanceof PsiArrayType ? ((PsiArrayType) x).getComponentType() : x;
          final PsiType yType =
              y instanceof PsiArrayType ? ((PsiArrayType) y).getComponentType() : y;

          return unify(xType, yType, unifier);
        } else if (x instanceof PsiClassType && y instanceof PsiClassType) {
          final PsiClassType.ClassResolveResult resultX = Util.resolveType(x);
          final PsiClassType.ClassResolveResult resultY = Util.resolveType(y);

          final PsiClass xClass = resultX.getElement();
          final PsiClass yClass = resultY.getElement();

          if (xClass != null && yClass != null) {
            final PsiSubstitutor ySubst = resultY.getSubstitutor();

            final PsiSubstitutor xSubst = resultX.getSubstitutor();

            if (!xClass.equals(yClass)) {
              return null;
            }

            Binding b = create();

            for (final PsiTypeParameter aParm : xSubst.getSubstitutionMap().keySet()) {
              final PsiType xType = xSubst.substitute(aParm);
              final PsiType yType = ySubst.substitute(aParm);

              final Binding b1 = unify(xType, yType, unifier);

              if (b1 == null) {
                return null;
              }

              b = b.compose(b1);
            }

            return b;
          }
        } else if (y instanceof Bottom) {
          return create();
        } else {
          return null;
        }

      default:
        return unifier.unify(x, y);
    }
  }
  @Nullable
  public static PsiType getExpectedClosureReturnType(GrClosableBlock closure) {
    final Set<PsiType> expectedTypes = getDefaultExpectedTypes(closure);

    List<PsiType> expectedReturnTypes = new ArrayList<PsiType>();
    for (PsiType expectedType : expectedTypes) {
      if (!(expectedType instanceof PsiClassType)) return null;

      final PsiClassType.ClassResolveResult resolveResult =
          ((PsiClassType) expectedType).resolveGenerics();
      final PsiClass resolved = resolveResult.getElement();
      if (resolved == null
          || !(GroovyCommonClassNames.GROOVY_LANG_CLOSURE.equals(resolved.getQualifiedName())))
        return null;

      final PsiTypeParameter[] typeParameters = resolved.getTypeParameters();
      if (typeParameters.length != 1) return null;

      final PsiTypeParameter expected = typeParameters[0];
      final PsiType expectedReturnType = resolveResult.getSubstitutor().substitute(expected);
      if (expectedReturnType == PsiType.VOID || expectedReturnType == null) return null;

      expectedReturnTypes.add(expectedReturnType);
    }

    return TypesUtil.getLeastUpperBoundNullable(expectedReturnTypes, closure.getManager());
  }
 @Nullable
 private static PsiType getExpectedTypeArg(
     PsiElement context,
     int index,
     PsiClassType.ClassResolveResult expectedType,
     PsiSubstitutor currentSubstitutor,
     PsiTypeParameter[] params) {
   PsiClass expectedClass = expectedType.getElement();
   assert expectedClass != null;
   for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(expectedClass)) {
     final PsiType argSubstitution = expectedType.getSubstitutor().substitute(parameter);
     final PsiType paramSubstitution = currentSubstitutor.substitute(parameter);
     final PsiType substitution =
         JavaPsiFacade.getInstance(context.getProject())
             .getResolveHelper()
             .getSubstitutionForTypeParameter(
                 params[index],
                 paramSubstitution,
                 argSubstitution,
                 false,
                 PsiUtil.getLanguageLevel(context));
     if (substitution != null && substitution != PsiType.NULL) {
       return substitution;
     }
   }
   return null;
 }
Example #4
0
 private void getVariantsFromQualifierType(
     @NotNull PsiType qualifierType, @NotNull Project project) {
   final ResolveState state = ResolveState.initial();
   if (qualifierType instanceof PsiClassType) {
     PsiClassType.ClassResolveResult result = ((PsiClassType) qualifierType).resolveGenerics();
     PsiClass qualifierClass = result.getElement();
     if (qualifierClass != null) {
       qualifierClass.processDeclarations(
           myProcessor, state.put(PsiSubstitutor.KEY, result.getSubstitutor()), null, myRefExpr);
     }
   } else if (qualifierType instanceof PsiArrayType) {
     final GrTypeDefinition arrayClass =
         GroovyPsiManager.getInstance(project)
             .getArrayClass(((PsiArrayType) qualifierType).getComponentType());
     if (arrayClass != null) {
       if (!arrayClass.processDeclarations(myProcessor, state, null, myRefExpr)) return;
     }
   } else if (qualifierType instanceof PsiIntersectionType) {
     for (PsiType conjunct : ((PsiIntersectionType) qualifierType).getConjuncts()) {
       getVariantsFromQualifierType(conjunct, project);
     }
     return;
   }
   ResolveUtil.processNonCodeMembers(qualifierType, myProcessor, myRefExpr, state);
 }
 @Nullable
 private static PsiType getTypeFromMapAccess(@NotNull GrReferenceExpressionImpl ref) {
   // map access
   GrExpression qualifier = ref.getQualifierExpression();
   if (qualifier != null) {
     PsiType qType = qualifier.getNominalType();
     if (qType instanceof PsiClassType) {
       PsiClassType.ClassResolveResult qResult = ((PsiClassType) qType).resolveGenerics();
       PsiClass clazz = qResult.getElement();
       if (clazz != null) {
         PsiClass mapClass =
             JavaPsiFacade.getInstance(ref.getProject())
                 .findClass(CommonClassNames.JAVA_UTIL_MAP, ref.getResolveScope());
         if (mapClass != null && mapClass.getTypeParameters().length == 2) {
           PsiSubstitutor substitutor =
               TypeConversionUtil.getClassSubstitutor(mapClass, clazz, qResult.getSubstitutor());
           if (substitutor != null) {
             return TypeConversionUtil.erasure(
                 substitutor.substitute(mapClass.getTypeParameters()[1]));
           }
         }
       }
     }
   }
   return null;
 }
 public static void register(
     final HighlightInfo highlightInfo, PsiExpression expression, final PsiType lType) {
   expression = PsiUtil.deparenthesizeExpression(expression);
   if (!(expression instanceof PsiNewExpression)) return;
   final PsiType rType = expression.getType();
   PsiType newType = lType;
   if (rType instanceof PsiClassType && newType instanceof PsiClassType) {
     final PsiClassType.ClassResolveResult rResolveResult =
         ((PsiClassType) rType).resolveGenerics();
     final PsiClass rClass = rResolveResult.getElement();
     if (rClass != null) {
       final PsiClassType.ClassResolveResult lResolveResult =
           ((PsiClassType) newType).resolveGenerics();
       final PsiClass lClass = lResolveResult.getElement();
       if (lClass != null) {
         PsiSubstitutor substitutor =
             getInheritorSubstitutorForNewExpression(
                 lClass, rClass, lResolveResult.getSubstitutor(), expression);
         if (substitutor != null) {
           newType =
               JavaPsiFacade.getInstance(lClass.getProject())
                   .getElementFactory()
                   .createType(rClass, substitutor);
         }
       }
     }
   }
   PsiNewExpression newExpression = (PsiNewExpression) expression;
   QuickFixAction.registerQuickFixAction(
       highlightInfo, new ChangeNewOperatorTypeFix(newType, newExpression));
 }
 @Nullable
 private static PsiType getInferredTypes(
     GrReferenceExpressionImpl refExpr, @Nullable PsiElement resolved) {
   final GrExpression qualifier = refExpr.getQualifier();
   if (qualifier == null && !(resolved instanceof PsiClass)) {
     return TypeInferenceHelper.getCurrentContext().getVariableType(refExpr);
   } else if (qualifier != null) {
     // map access
     PsiType qType = qualifier.getType();
     if (qType instanceof PsiClassType && !(qType instanceof GrMapType)) {
       PsiClassType.ClassResolveResult qResult = ((PsiClassType) qType).resolveGenerics();
       PsiClass clazz = qResult.getElement();
       if (clazz != null) {
         PsiClass mapClass =
             JavaPsiFacade.getInstance(refExpr.getProject())
                 .findClass(CommonClassNames.JAVA_UTIL_MAP, refExpr.getResolveScope());
         if (mapClass != null && mapClass.getTypeParameters().length == 2) {
           PsiSubstitutor substitutor =
               TypeConversionUtil.getClassSubstitutor(mapClass, clazz, qResult.getSubstitutor());
           if (substitutor != null) {
             return TypeConversionUtil.erasure(
                 substitutor.substitute(mapClass.getTypeParameters()[1]));
           }
         }
       }
     }
   }
   return null;
 }
    @Override
    public PsiType visitClassType(PsiClassType classType) {
      final PsiClassType.ClassResolveResult resolveResult = classType.resolveGenerics();
      final PsiClass aClass = resolveResult.getElement();
      if (aClass == null) return classType;

      PsiUtilCore.ensureValid(aClass);
      if (aClass instanceof PsiTypeParameter) {
        final PsiTypeParameter typeParameter = (PsiTypeParameter) aClass;
        if (containsInMap(typeParameter)) {
          PsiType result = substituteTypeParameter(typeParameter);
          if (result != null) {
            PsiUtil.ensureValidType(result);
          }
          return result;
        }
        return classType;
      }
      final Map<PsiTypeParameter, PsiType> hashMap = new HashMap<PsiTypeParameter, PsiType>(2);
      if (!processClass(aClass, resolveResult.getSubstitutor(), hashMap)) {
        return null;
      }
      PsiClassType result =
          JavaPsiFacade.getElementFactory(aClass.getProject())
              .createType(aClass, createSubstitutor(hashMap), classType.getLanguageLevel());
      PsiUtil.ensureValidType(result);
      return result;
    }
  @Override
  public PsiType visitClassType(final PsiClassType classType) {
    PsiClassType alreadyComputed = myResultMap.get(classType);
    if (alreadyComputed != null) {
      return alreadyComputed;
    }

    final PsiClassType.ClassResolveResult classResolveResult = classType.resolveGenerics();
    final PsiClass psiClass = classResolveResult.getElement();
    final PsiSubstitutor substitutor = classResolveResult.getSubstitutor();
    if (psiClass == null) return classType;

    PsiUtilCore.ensureValid(psiClass);

    final PsiClass mappedClass = mapClass(psiClass);
    if (mappedClass == null) return classType;

    PsiClassType mappedType =
        new PsiCorrectedClassType(
            classType.getLanguageLevel(),
            classType,
            new CorrectedResolveResult(psiClass, mappedClass, substitutor, classResolveResult));
    myResultMap.put(classType, mappedType);
    return mappedType;
  }
Example #10
0
  @Nullable
  private static Boolean isAssignableForNativeTypes(
      @NotNull PsiType lType, @NotNull PsiClassType rType, @NotNull PsiElement context) {
    if (!(lType instanceof PsiClassType)) return null;
    final PsiClassType.ClassResolveResult leftResult = ((PsiClassType) lType).resolveGenerics();
    final PsiClassType.ClassResolveResult rightResult = rType.resolveGenerics();
    final PsiClass leftClass = leftResult.getElement();
    PsiClass rightClass = rightResult.getElement();
    if (rightClass == null || leftClass == null) return null;

    if (!InheritanceUtil.isInheritorOrSelf(rightClass, leftClass, true)) return Boolean.FALSE;

    PsiSubstitutor rightSubstitutor = rightResult.getSubstitutor();

    if (!leftClass.hasTypeParameters()) return Boolean.TRUE;
    PsiSubstitutor leftSubstitutor = leftResult.getSubstitutor();

    if (!leftClass.getManager().areElementsEquivalent(leftClass, rightClass)) {
      rightSubstitutor =
          TypeConversionUtil.getSuperClassSubstitutor(leftClass, rightClass, rightSubstitutor);
      rightClass = leftClass;
    } else if (!rightClass.hasTypeParameters()) return Boolean.TRUE;

    Iterator<PsiTypeParameter> li = PsiUtil.typeParametersIterator(leftClass);
    Iterator<PsiTypeParameter> ri = PsiUtil.typeParametersIterator(rightClass);
    while (li.hasNext()) {
      if (!ri.hasNext()) return Boolean.FALSE;
      PsiTypeParameter lp = li.next();
      PsiTypeParameter rp = ri.next();
      final PsiType typeLeft = leftSubstitutor.substitute(lp);
      if (typeLeft == null) continue;
      final PsiType typeRight = rightSubstitutor.substituteWithBoundsPromotion(rp);
      if (typeRight == null) {
        return Boolean.TRUE;
      }
      if (!isAssignableWithoutConversions(typeLeft, typeRight, context)) return Boolean.FALSE;
    }
    return Boolean.TRUE;
  }
    public PsiType substitute(final PsiType t) {
      if (t instanceof PsiWildcardType) {
        final PsiWildcardType wcType = (PsiWildcardType) t;
        final PsiType bound = wcType.getBound();

        if (bound == null) {
          return t;
        }

        final PsiManager manager = PsiManager.getInstance(myProject);
        final PsiType subst = substitute(bound);
        if (subst == null) return null;
        return subst instanceof PsiWildcardType
            ? subst
            : wcType.isExtends()
                ? PsiWildcardType.createExtends(manager, subst)
                : PsiWildcardType.createSuper(manager, subst);
      } else if (t instanceof PsiTypeVariable) {
        final PsiType b = apply(t);

        if (b instanceof Bottom || b instanceof PsiTypeVariable) {
          return null;
        }

        return substitute(b);
      } else if (t instanceof Bottom) {
        return null;
      } else if (t instanceof PsiArrayType) {
        return substitute(((PsiArrayType) t).getComponentType()).createArrayType();
      } else if (t instanceof PsiClassType) {
        final PsiClassType.ClassResolveResult result = ((PsiClassType) t).resolveGenerics();

        final PsiClass aClass = result.getElement();
        final PsiSubstitutor aSubst = result.getSubstitutor();

        if (aClass != null) {
          PsiSubstitutor theSubst = PsiSubstitutor.EMPTY;

          for (final PsiTypeParameter parm : aSubst.getSubstitutionMap().keySet()) {
            final PsiType type = aSubst.substitute(parm);

            theSubst = theSubst.put(parm, substitute(type));
          }

          return JavaPsiFacade.getInstance(aClass.getProject())
              .getElementFactory()
              .createType(aClass, theSubst);
        }
      }
      return t;
    }
  @NotNull
  @Override
  public PsiJavaCodeReferenceElement createReferenceElementByType(
      @NotNull final PsiClassType type) {
    if (type instanceof PsiClassReferenceType) {
      return ((PsiClassReferenceType) type).getReference();
    }

    final PsiClassType.ClassResolveResult resolveResult = type.resolveGenerics();
    final PsiClass refClass = resolveResult.getElement();
    assert refClass != null : type;
    return new LightClassReference(
        myManager, type.getCanonicalText(), refClass, resolveResult.getSubstitutor());
  }
  @Override
  public Result calculateResult(@NotNull Expression[] params, ExpressionContext context) {
    if (params.length != 1) return null;
    final Result result = params[0].calculateResult(context);
    if (result == null) return null;

    Project project = context.getProject();

    PsiExpression expr = MacroUtil.resultToPsiExpression(result, context);
    if (expr == null) return null;
    PsiType type = expr.getType();

    if (type instanceof PsiArrayType) {
      return new PsiTypeResult(((PsiArrayType) type).getComponentType(), project);
    }

    if (type instanceof PsiClassType) {
      PsiClassType.ClassResolveResult resolveResult = ((PsiClassType) type).resolveGenerics();
      PsiClass aClass = resolveResult.getElement();

      if (aClass != null) {
        PsiClass iterableClass =
            JavaPsiFacade.getInstance(project)
                .findClass("java.lang.Iterable", aClass.getResolveScope());
        if (iterableClass != null) {
          PsiSubstitutor substitutor =
              TypeConversionUtil.getClassSubstitutor(
                  iterableClass, aClass, resolveResult.getSubstitutor());
          if (substitutor != null) {
            PsiType parameterType = substitutor.substitute(iterableClass.getTypeParameters()[0]);
            if (parameterType instanceof PsiCapturedWildcardType) {
              parameterType = ((PsiCapturedWildcardType) parameterType).getWildcard();
            }
            if (parameterType != null) {
              if (parameterType instanceof PsiWildcardType) {
                if (((PsiWildcardType) parameterType).isExtends()) {
                  return new PsiTypeResult(((PsiWildcardType) parameterType).getBound(), project);
                } else return null;
              }
              return new PsiTypeResult(parameterType, project);
            }
          }
        }
      }
    }

    return null;
  }
  // uses hierarchy signature tree if available, traverses class structure by itself otherwise
  public static boolean processDirectSuperMethodsSmart(
      @NotNull PsiMethod method, @NotNull Processor<PsiMethod> superMethodProcessor) {
    // boolean old = PsiSuperMethodUtil.isSuperMethod(method, superMethod);

    PsiClass aClass = method.getContainingClass();
    if (aClass == null) return false;

    if (!canHaveSuperMethod(method, true, false)) return false;

    Map<MethodSignature, HierarchicalMethodSignature> cachedMap =
        SIGNATURES_KEY.getCachedValueOrNull(aClass);
    if (cachedMap != null) {
      HierarchicalMethodSignature signature =
          cachedMap.get(method.getSignature(PsiSubstitutor.EMPTY));
      if (signature != null) {
        List<HierarchicalMethodSignature> superSignatures = signature.getSuperSignatures();
        for (HierarchicalMethodSignature superSignature : superSignatures) {
          if (!superMethodProcessor.process(superSignature.getMethod())) return false;
        }
        return true;
      }
    }

    PsiClassType[] directSupers = aClass.getSuperTypes();
    for (PsiClassType directSuper : directSupers) {
      PsiClassType.ClassResolveResult resolveResult = directSuper.resolveGenerics();
      if (resolveResult.getSubstitutor() != PsiSubstitutor.EMPTY) {
        // generics
        break;
      }
      PsiClass directSuperClass = resolveResult.getElement();
      if (directSuperClass == null) continue;
      PsiMethod[] candidates = directSuperClass.findMethodsBySignature(method, false);
      for (PsiMethod candidate : candidates) {
        if (PsiUtil.canBeOverriden(candidate)) {
          if (!superMethodProcessor.process(candidate)) return false;
        }
      }
      return true;
    }

    List<HierarchicalMethodSignature> superSignatures =
        method.getHierarchicalMethodSignature().getSuperSignatures();
    for (HierarchicalMethodSignature superSignature : superSignatures) {
      if (!superMethodProcessor.process(superSignature.getMethod())) return false;
    }
    return true;
  }
  @Nullable
  public <T extends PsiType> T correctType(@NotNull T type) {
    if (type instanceof PsiClassType) {
      PsiClassType classType = (PsiClassType) type;
      if (classType.getParameterCount() == 0) {
        final PsiClassType.ClassResolveResult classResolveResult = classType.resolveGenerics();
        final PsiClass psiClass = classResolveResult.getElement();
        if (psiClass != null && classResolveResult.getSubstitutor() == PsiSubstitutor.EMPTY) {
          final PsiClass mappedClass = mapClass(psiClass);
          if (mappedClass == null || mappedClass == psiClass) return (T) classType;
        }
      }
    }

    return (T) type.accept(this);
  }
    public PsiType apply(final PsiType type) {
      if (type instanceof PsiTypeVariable) {
        final PsiType t = myBindings.get(((PsiTypeVariable) type).getIndex());
        return t == null ? type : t;
      } else if (type instanceof PsiArrayType) {
        return apply(((PsiArrayType) type).getComponentType()).createArrayType();
      } else if (type instanceof PsiClassType) {
        final PsiClassType.ClassResolveResult result = Util.resolveType(type);
        final PsiClass theClass = result.getElement();
        final PsiSubstitutor aSubst = result.getSubstitutor();

        PsiSubstitutor theSubst = PsiSubstitutor.EMPTY;

        if (theClass != null) {
          for (final PsiTypeParameter aParm : aSubst.getSubstitutionMap().keySet()) {
            final PsiType aType = aSubst.substitute(aParm);

            theSubst = theSubst.put(aParm, apply(aType));
          }

          return JavaPsiFacade.getInstance(theClass.getProject())
              .getElementFactory()
              .createType(theClass, theSubst);
        } else {
          return type;
        }
      } else if (type instanceof PsiWildcardType) {
        final PsiWildcardType wcType = (PsiWildcardType) type;
        final PsiType bound = wcType.getBound();

        if (bound != null) {
          final PsiType abound = apply(bound);

          if (abound instanceof PsiWildcardType) {
            return null;
          }

          return wcType.isExtends()
              ? PsiWildcardType.createExtends(PsiManager.getInstance(myProject), abound)
              : PsiWildcardType.createSuper(PsiManager.getInstance(myProject), abound);
        }

        return type;
      } else {
        return type;
      }
    }
  private static void processClassInner(
      PsiClassType type,
      PsiSubstitutor superClassSubstitutor,
      boolean shouldProcessDeprecated,
      List<PsiMethod> result,
      GrTypeDefinition classToDelegateTo,
      Set<PsiClass> processedWithoutDeprecated,
      Set<PsiClass> processedAll,
      boolean keepParameterAnnotationsNew) {
    final PsiClassType.ClassResolveResult resolveResult = type.resolveGenerics();
    final PsiClass psiClass = resolveResult.getElement();
    if (psiClass == null) return;

    final String qname = psiClass.getQualifiedName();
    if (CommonClassNames.JAVA_LANG_OBJECT.equals(qname)) return;
    if (GroovyCommonClassNames.GROOVY_OBJECT.equals(qname)) return;
    if (GroovyCommonClassNames.GROOVY_OBJECT_SUPPORT.equals(qname)) return;

    final PsiSubstitutor substitutor =
        TypesUtil.composeSubstitutors(resolveResult.getSubstitutor(), superClassSubstitutor);

    if (processedAll.contains(psiClass)) return;
    if (!shouldProcessDeprecated && processedWithoutDeprecated.contains(psiClass)) return;

    if (shouldProcessDeprecated) {
      processedAll.add(psiClass);
    } else {
      processedWithoutDeprecated.add(psiClass);
    }

    collectMethods(
        psiClass,
        substitutor,
        shouldProcessDeprecated,
        classToDelegateTo,
        result,
        keepParameterAnnotationsNew);
    process(
        psiClass,
        substitutor,
        shouldProcessDeprecated,
        processedWithoutDeprecated,
        processedAll,
        result,
        classToDelegateTo,
        keepParameterAnnotationsNew);
  }
Example #18
0
 private Pair<PsiVariable, PsiType> findVariable(final PsiElement element) {
   PsiVariable variable = null;
   if (element instanceof PsiIdentifier) {
     if (element.getParent() instanceof PsiVariable) {
       variable = (PsiVariable) element.getParent();
     }
   } else if (element instanceof PsiJavaToken) {
     final PsiJavaToken token = (PsiJavaToken) element;
     if (token.getTokenType() != JavaTokenType.EQ) return null;
     if (token.getParent() instanceof PsiVariable) {
       variable = (PsiVariable) token.getParent();
     }
   }
   if (variable == null) {
     return null;
   }
   variableName = variable.getName();
   final PsiExpression initializer = variable.getInitializer();
   if (initializer == null) return null;
   final PsiType variableType = variable.getType();
   final PsiType initializerType = initializer.getType();
   if (!(variableType instanceof PsiClassType)) return null;
   final PsiClassType variableClassType = (PsiClassType) variableType;
   if (!variableClassType.isRaw()) return null;
   if (!(initializerType instanceof PsiClassType)) return null;
   final PsiClassType initializerClassType = (PsiClassType) initializerType;
   if (initializerClassType.isRaw()) return null;
   final PsiClassType.ClassResolveResult variableResolveResult =
       variableClassType.resolveGenerics();
   final PsiClassType.ClassResolveResult initializerResolveResult =
       initializerClassType.resolveGenerics();
   if (initializerResolveResult.getElement() == null) return null;
   final PsiSubstitutor targetSubstitutor =
       TypeConversionUtil.getClassSubstitutor(
           variableResolveResult.getElement(),
           initializerResolveResult.getElement(),
           initializerResolveResult.getSubstitutor());
   if (targetSubstitutor == null) return null;
   PsiType type =
       JavaPsiFacade.getInstance(variable.getProject())
           .getElementFactory()
           .createType(variableResolveResult.getElement(), targetSubstitutor);
   newTypeName = type.getCanonicalText();
   return Pair.create(variable, type);
 }
  @Nullable
  public static PsiType getSubstitutedType(@Nullable PsiParameter parameter) {
    if (parameter == null) return null;

    final PsiType type = getType(parameter);

    if (type instanceof PsiArrayType) {
      return type;
    }

    final PsiClassType.ClassResolveResult result = PsiUtil.resolveGenericsClassInType(type);
    final PsiClass psiClass = result.getElement();
    if (psiClass == null) return type;

    final Set<PsiTypeParameter> usedTypeParameters = new HashSet<PsiTypeParameter>();
    RefactoringUtil.collectTypeParameters(usedTypeParameters, parameter);
    for (Iterator<PsiTypeParameter> iterator = usedTypeParameters.iterator();
        iterator.hasNext(); ) {
      PsiTypeParameter usedTypeParameter = iterator.next();
      if (parameter.getDeclarationScope() != usedTypeParameter.getOwner()) {
        iterator.remove();
      }
    }

    PsiSubstitutor subst = PsiSubstitutor.EMPTY;
    for (PsiTypeParameter usedTypeParameter : usedTypeParameters) {
      subst =
          subst.put(usedTypeParameter, TypeConversionUtil.typeParameterErasure(usedTypeParameter));
    }

    PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
    final Map<PsiTypeParameter, PsiType> typeMap = result.getSubstitutor().getSubstitutionMap();
    for (PsiTypeParameter typeParameter : typeMap.keySet()) {
      final PsiType psiType = typeMap.get(typeParameter);
      substitutor =
          substitutor.put(typeParameter, psiType != null ? subst.substitute(psiType) : null);
    }

    if (psiClass instanceof PsiTypeParameter) {
      return subst.substitute((PsiTypeParameter) psiClass);
    } else {
      return JavaPsiFacade.getElementFactory(parameter.getProject())
          .createType(psiClass, substitutor);
    }
  }
  @Nullable
  private static PsiType getCollectionComponentType(PsiType type, Project project) {
    if (!(type instanceof PsiClassType)) return null;
    PsiClassType classType = (PsiClassType) type;
    PsiClassType.ClassResolveResult result = classType.resolveGenerics();
    PsiClass clazz = result.getElement();
    if (clazz == null) return null;
    JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
    @SuppressWarnings({"ConstantConditions"})
    PsiClass collectionClass = facade.findClass("java.util.Collection", type.getResolveScope());
    if (collectionClass == null || collectionClass.getTypeParameters().length != 1) return null;
    PsiSubstitutor substitutor =
        TypeConversionUtil.getClassSubstitutor(collectionClass, clazz, result.getSubstitutor());

    if (substitutor == null) return null;
    PsiType componentType = substitutor.substitute(collectionClass.getTypeParameters()[0]);
    return componentType instanceof PsiIntersectionType ? null : componentType;
  }
  private static PsiTypeLookupItem doCreateItem(
      final PsiType type,
      PsiElement context,
      int bracketsCount,
      boolean diamond,
      InsertHandler<PsiTypeLookupItem> importFixer) {
    if (type instanceof PsiClassType) {
      PsiClassType.ClassResolveResult classResolveResult = ((PsiClassType) type).resolveGenerics();
      final PsiClass psiClass = classResolveResult.getElement();

      if (psiClass != null) {
        String name = psiClass.getName();
        if (name != null) {
          final PsiSubstitutor substitutor = classResolveResult.getSubstitutor();

          PsiClass resolved =
              JavaPsiFacade.getInstance(psiClass.getProject())
                  .getResolveHelper()
                  .resolveReferencedClass(name, context);

          Set<String> allStrings = new HashSet<String>();
          allStrings.add(name);
          if (!psiClass.getManager().areElementsEquivalent(resolved, psiClass)
              && !PsiUtil.isInnerClass(psiClass)) {
            // inner class name should be shown qualified if its not accessible by single name
            PsiClass aClass = psiClass.getContainingClass();
            while (aClass != null && !PsiUtil.isInnerClass(aClass) && aClass.getName() != null) {
              name = aClass.getName() + '.' + name;
              allStrings.add(name);
              aClass = aClass.getContainingClass();
            }
          }

          PsiTypeLookupItem item =
              new PsiTypeLookupItem(
                  psiClass, name, diamond, bracketsCount, importFixer, substitutor);
          item.addLookupStrings(ArrayUtil.toStringArray(allStrings));
          return item;
        }
      }
    }
    return new PsiTypeLookupItem(
        type, type.getPresentableText(), false, bracketsCount, importFixer, PsiSubstitutor.EMPTY);
  }
 private static boolean processSuperTypes(
     @NotNull PsiClass aClass,
     @NotNull PsiScopeProcessor processor,
     @Nullable Set<PsiClass> visited,
     PsiElement last,
     @NotNull PsiElement place,
     @NotNull ResolveState state,
     boolean isRaw,
     @NotNull PsiElementFactory factory,
     @NotNull LanguageLevel languageLevel) {
   boolean resolved = false;
   for (final PsiClassType superType : aClass.getSuperTypes()) {
     final PsiClassType.ClassResolveResult superTypeResolveResult = superType.resolveGenerics();
     PsiClass superClass = superTypeResolveResult.getElement();
     if (superClass == null) continue;
     PsiSubstitutor finalSubstitutor =
         obtainFinalSubstitutor(
             superClass,
             superTypeResolveResult.getSubstitutor(),
             aClass,
             state.get(PsiSubstitutor.KEY),
             factory,
             languageLevel);
     if (aClass instanceof PsiTypeParameter
         && PsiUtil.isRawSubstitutor(superClass, finalSubstitutor)) {
       finalSubstitutor = PsiSubstitutor.EMPTY;
     }
     if (!processDeclarationsInClass(
         superClass,
         processor,
         state.put(PsiSubstitutor.KEY, finalSubstitutor),
         visited,
         last,
         place,
         isRaw)) {
       resolved = true;
     }
   }
   return !resolved;
 }
        private boolean hasTypeParametersToInfer(PsiClassType classType) {
          final PsiClassType.ClassResolveResult resolveResult = classType.resolveGenerics();
          PsiClass aClass = resolveResult.getElement();
          if (aClass == null) return false;

          final Iterable<PsiTypeParameter> iterable =
              com.intellij.psi.util.PsiUtil.typeParametersIterable(aClass);
          if (!iterable.iterator().hasNext()) {
            return false;
          }

          for (PsiTypeParameter parameter : iterable) {
            PsiType type = resolveResult.getSubstitutor().substitute(parameter);
            if (type != null) {
              if (!(type instanceof PsiWildcardType)
                  || ((PsiWildcardType) type).getBound() != null) {
                return false;
              }
            }
          }
          return true;
        }
  private static LookupItem doCreateItem(PsiType type, PsiElement context) {
    if (type instanceof PsiClassType) {
      PsiClassType.ClassResolveResult classResolveResult = ((PsiClassType) type).resolveGenerics();
      final PsiClass psiClass = classResolveResult.getElement();
      final PsiSubstitutor substitutor = classResolveResult.getSubstitutor();
      final String text = type.getCanonicalText();
      String typeString = text;
      String typeParams = "";
      if (text.indexOf('<') > 0 && text.endsWith(">")) {
        typeString = text.substring(0, text.indexOf('<'));
        typeParams = text.substring(text.indexOf('<'));
      }

      String lookupString = text.substring(typeString.lastIndexOf('.') + 1);
      if (psiClass != null) {
        PsiClass resolved =
            JavaPsiFacade.getInstance(psiClass.getProject())
                .getResolveHelper()
                .resolveReferencedClass(psiClass.getName(), context);
        if (!psiClass.getManager().areElementsEquivalent(resolved, psiClass)) {
          // inner class name should be shown qualified if its not accessible by single name
          PsiClass aClass = psiClass;
          lookupString = "";
          while (aClass != null) {
            lookupString = aClass.getName() + (lookupString == "" ? "" : ".") + lookupString;
            aClass = aClass.getContainingClass();
          }
          lookupString += typeParams;
        }
        LookupItem item = new PsiTypeLookupItem(psiClass, lookupString);
        item.setAttribute(SUBSTITUTOR, substitutor);
        return item;
      }
    }
    return new LookupItem(type, type.getPresentableText());
  }
Example #25
0
  public static void processSubTypes(
      PsiType psiType,
      final PsiElement context,
      boolean getRawSubtypes,
      @NotNull final PrefixMatcher matcher,
      Consumer<PsiType> consumer) {
    int arrayDim = psiType.getArrayDimensions();

    psiType = psiType.getDeepComponentType();
    if (!(psiType instanceof PsiClassType)) return;

    final Condition<String> shortNameCondition =
        new Condition<String>() {
          @Override
          public boolean value(String s) {
            return matcher.prefixMatches(s);
          }
        };

    final PsiClassType baseType = (PsiClassType) psiType;
    final PsiClassType.ClassResolveResult baseResult =
        ApplicationManager.getApplication()
            .runReadAction(
                new Computable<PsiClassType.ClassResolveResult>() {
                  @Override
                  public PsiClassType.ClassResolveResult compute() {
                    return JavaCompletionUtil.originalize(baseType).resolveGenerics();
                  }
                });
    final PsiClass baseClass = baseResult.getElement();
    final PsiSubstitutor baseSubstitutor = baseResult.getSubstitutor();
    if (baseClass == null) return;

    final GlobalSearchScope scope =
        ApplicationManager.getApplication()
            .runReadAction(
                new Computable<GlobalSearchScope>() {
                  @Override
                  public GlobalSearchScope compute() {
                    return context.getResolveScope();
                  }
                });

    final Processor<PsiClass> inheritorsProcessor =
        createInheritorsProcessor(
            context, baseType, arrayDim, getRawSubtypes, consumer, baseClass, baseSubstitutor);
    if (matcher.getPrefix().length() > 2) {
      AllClassesGetter.processJavaClasses(
          matcher,
          context.getProject(),
          scope,
          new Processor<PsiClass>() {
            @Override
            public boolean process(PsiClass psiClass) {
              if (psiClass.isInheritor(baseClass, true)) {
                return inheritorsProcessor.process(psiClass);
              }
              return true;
            }
          });
    } else {
      final Query<PsiClass> baseQuery =
          ClassInheritorsSearch.search(
              new ClassInheritorsSearch.SearchParameters(
                  baseClass, scope, true, false, false, shortNameCondition));
      final Query<PsiClass> query =
          new FilteredQuery<PsiClass>(
              baseQuery,
              new Condition<PsiClass>() {
                @Override
                public boolean value(final PsiClass psiClass) {
                  return !(psiClass instanceof PsiTypeParameter);
                }
              });
      query.forEach(inheritorsProcessor);
    }
  }
  @Nullable
  public <T extends PsiExpression> PsiType getType(
      @NotNull T expr, @NotNull Function<T, PsiType> f) {
    PsiType type = getCachedType(expr);
    if (type == null) {
      final RecursionGuard.StackStamp dStackStamp = PsiDiamondType.ourDiamondGuard.markStack();
      final RecursionGuard.StackStamp gStackStamp = PsiResolveHelper.ourGraphGuard.markStack();
      type = f.fun(expr);
      if (!dStackStamp.mayCacheNow() || !gStackStamp.mayCacheNow()) {
        return type;
      }
      if (type == null) type = TypeConversionUtil.NULL_TYPE;
      Reference<PsiType> ref = new SoftReference<PsiType>(type);
      myCalculatedTypes.put(expr, ref);

      if (type instanceof PsiClassReferenceType) {
        // convert reference-based class type to the PsiImmediateClassType, since the reference may
        // become invalid
        PsiClassType.ClassResolveResult result = ((PsiClassReferenceType) type).resolveGenerics();
        PsiClass psiClass = result.getElement();
        type =
            psiClass == null
                ? type // for type with unresolved reference, leave it in the cache
                // for clients still might be able to retrieve its getCanonicalText() from the
                // reference text
                : new PsiImmediateClassType(
                    psiClass,
                    result.getSubstitutor(),
                    ((PsiClassReferenceType) type).getLanguageLevel(),
                    type.getAnnotations());
      }
    }

    if (!type.isValid()) {
      if (expr.isValid()) {
        PsiJavaCodeReferenceElement refInside =
            type instanceof PsiClassReferenceType
                ? ((PsiClassReferenceType) type).getReference()
                : null;
        @NonNls
        String typeinfo =
            type
                + " ("
                + type.getClass()
                + ")"
                + (refInside == null
                    ? ""
                    : "; ref inside: "
                        + refInside
                        + " ("
                        + refInside.getClass()
                        + ") valid:"
                        + refInside.isValid());
        LOG.error(
            "Type is invalid: "
                + typeinfo
                + "; expr: '"
                + expr
                + "' ("
                + expr.getClass()
                + ") is valid");
      } else {
        LOG.error("Expression: '" + expr + "' is invalid, must not be used for getType()");
      }
    }

    return type == TypeConversionUtil.NULL_TYPE ? null : type;
  }
  private static Map<MethodSignature, HierarchicalMethodSignature> buildMethodHierarchy(
      PsiClass aClass,
      PsiSubstitutor substitutor,
      final boolean includePrivates,
      final Set<PsiClass> visited,
      boolean isInRawContext) {
    Map<MethodSignature, HierarchicalMethodSignature> result =
        new LinkedHashMap<MethodSignature, HierarchicalMethodSignature>();
    final Map<MethodSignature, List<PsiMethod>> sameParameterErasureMethods =
        new THashMap<MethodSignature, List<PsiMethod>>(
            MethodSignatureUtil.METHOD_PARAMETERS_ERASURE_EQUALITY);

    Map<MethodSignature, HierarchicalMethodSignatureImpl> map =
        new THashMap<MethodSignature, HierarchicalMethodSignatureImpl>(
            new TObjectHashingStrategy<MethodSignature>() {
              @Override
              public int computeHashCode(MethodSignature signature) {
                return MethodSignatureUtil.METHOD_PARAMETERS_ERASURE_EQUALITY.computeHashCode(
                    signature);
              }

              @Override
              public boolean equals(MethodSignature o1, MethodSignature o2) {
                if (!MethodSignatureUtil.METHOD_PARAMETERS_ERASURE_EQUALITY.equals(o1, o2))
                  return false;
                List<PsiMethod> list = sameParameterErasureMethods.get(o1);
                boolean toCheckReturnType = list != null && list.size() > 1;
                if (!toCheckReturnType) return true;
                PsiType returnType1 =
                    ((MethodSignatureBackedByPsiMethod) o1).getMethod().getReturnType();
                PsiType returnType2 =
                    ((MethodSignatureBackedByPsiMethod) o2).getMethod().getReturnType();
                if (returnType1 == null && returnType2 == null) return true;
                if (returnType1 == null || returnType2 == null) return false;

                PsiType erasure1 =
                    TypeConversionUtil.erasure(o1.getSubstitutor().substitute(returnType1));
                PsiType erasure2 =
                    TypeConversionUtil.erasure(o2.getSubstitutor().substitute(returnType2));
                return erasure1.equals(erasure2);
              }
            });

    for (PsiMethod method : aClass.getMethods()) {
      if (!method.isValid()) {
        throw new PsiInvalidElementAccessException(
            method, "class.valid=" + aClass.isValid() + "; name=" + method.getName());
      }
      if (!includePrivates && method.hasModifierProperty(PsiModifier.PRIVATE)) continue;
      final MethodSignatureBackedByPsiMethod signature =
          MethodSignatureBackedByPsiMethod.create(method, substitutor, isInRawContext);
      HierarchicalMethodSignatureImpl newH = new HierarchicalMethodSignatureImpl(signature);

      List<PsiMethod> list = sameParameterErasureMethods.get(signature);
      if (list == null) {
        list = new SmartList<PsiMethod>();
        sameParameterErasureMethods.put(signature, list);
      }
      list.add(method);

      LOG.assertTrue(newH.getMethod().isValid());
      result.put(signature, newH);
      map.put(signature, newH);
    }

    for (PsiClassType superType : aClass.getSuperTypes()) {
      PsiClassType.ClassResolveResult superTypeResolveResult = superType.resolveGenerics();
      PsiClass superClass = superTypeResolveResult.getElement();
      if (superClass == null) continue;
      if (!visited.add(superClass)) continue; // cyclic inheritance
      final PsiSubstitutor superSubstitutor = superTypeResolveResult.getSubstitutor();
      PsiSubstitutor finalSubstitutor =
          obtainFinalSubstitutor(superClass, superSubstitutor, substitutor, isInRawContext);

      final boolean isInRawContextSuper =
          (isInRawContext || PsiUtil.isRawSubstitutor(superClass, superSubstitutor))
              && superClass.getTypeParameters().length != 0;
      Map<MethodSignature, HierarchicalMethodSignature> superResult =
          buildMethodHierarchy(superClass, finalSubstitutor, false, visited, isInRawContextSuper);
      visited.remove(superClass);

      List<Pair<MethodSignature, HierarchicalMethodSignature>> flattened =
          new ArrayList<Pair<MethodSignature, HierarchicalMethodSignature>>();
      for (Map.Entry<MethodSignature, HierarchicalMethodSignature> entry : superResult.entrySet()) {
        HierarchicalMethodSignature hms = entry.getValue();
        MethodSignature signature = entry.getKey();
        PsiClass containingClass = hms.getMethod().getContainingClass();
        List<HierarchicalMethodSignature> supers =
            new ArrayList<HierarchicalMethodSignature>(hms.getSuperSignatures());
        for (HierarchicalMethodSignature aSuper : supers) {
          PsiClass superContainingClass = aSuper.getMethod().getContainingClass();
          if (containingClass != null
              && superContainingClass != null
              && !containingClass.isInheritor(superContainingClass, true)) {
            // methods must be inherited from unrelated classes, so flatten hierarchy here
            // class C implements SAM1, SAM2 { void methodimpl() {} }
            // hms.getSuperSignatures().remove(aSuper);
            flattened.add(
                new Pair<MethodSignature, HierarchicalMethodSignature>(signature, aSuper));
          }
        }
        putInMap(aClass, result, map, hms, signature);
      }
      for (Pair<MethodSignature, HierarchicalMethodSignature> pair : flattened) {
        putInMap(aClass, result, map, pair.second, pair.first);
      }
    }

    for (Map.Entry<MethodSignature, HierarchicalMethodSignatureImpl> entry : map.entrySet()) {
      HierarchicalMethodSignatureImpl hierarchicalMethodSignature = entry.getValue();
      MethodSignature methodSignature = entry.getKey();
      if (result.get(methodSignature) == null
          && PsiUtil.isAccessible(
              aClass.getProject(), hierarchicalMethodSignature.getMethod(), aClass, aClass)) {
        LOG.assertTrue(hierarchicalMethodSignature.getMethod().isValid());
        result.put(methodSignature, hierarchicalMethodSignature);
      }
    }

    return result;
  }
  // uses hierarchy signature tree if available, traverses class structure by itself otherwise
  public static boolean isSuperMethodSmart(
      @NotNull PsiMethod method, @NotNull PsiMethod superMethod) {
    // boolean old = PsiSuperMethodUtil.isSuperMethod(method, superMethod);

    if (method == superMethod) return false;
    PsiClass aClass = method.getContainingClass();
    PsiClass superClass = superMethod.getContainingClass();

    if (aClass == null || superClass == null || superClass == aClass) return false;

    if (!canHaveSuperMethod(method, true, false)) return false;

    PsiMethod[] superMethods = null;
    Map<MethodSignature, HierarchicalMethodSignature> cachedMap =
        SIGNATURES_KEY.getCachedValueOrNull(aClass);
    if (cachedMap != null) {
      HierarchicalMethodSignature signature =
          cachedMap.get(method.getSignature(PsiSubstitutor.EMPTY));
      if (signature != null) {
        superMethods =
            MethodSignatureUtil.convertMethodSignaturesToMethods(signature.getSuperSignatures());
      }
    }
    if (superMethods == null) {
      PsiClassType[] directSupers = aClass.getSuperTypes();
      List<PsiMethod> found = null;
      boolean canceled = false;
      for (PsiClassType directSuper : directSupers) {
        PsiClassType.ClassResolveResult resolveResult = directSuper.resolveGenerics();
        if (resolveResult.getSubstitutor() != PsiSubstitutor.EMPTY) {
          // generics
          canceled = true;
          break;
        }
        PsiClass directSuperClass = resolveResult.getElement();
        if (directSuperClass == null) continue;
        PsiMethod[] candidates = directSuperClass.findMethodsBySignature(method, false);
        if (candidates.length != 0) {
          if (found == null) found = new ArrayList<PsiMethod>();
          for (PsiMethod candidate : candidates) {
            if (PsiUtil.canBeOverriden(candidate)) found.add(candidate);
          }
        }
      }
      superMethods =
          canceled
              ? null
              : found == null ? PsiMethod.EMPTY_ARRAY : found.toArray(new PsiMethod[found.size()]);
    }
    if (superMethods == null) {
      superMethods =
          MethodSignatureUtil.convertMethodSignaturesToMethods(
              method.getHierarchicalMethodSignature().getSuperSignatures());
    }

    for (PsiMethod superCandidate : superMethods) {
      if (superMethod.equals(superCandidate) || isSuperMethodSmart(superCandidate, superMethod))
        return true;
    }
    return false;
  }
  @NotNull
  private static LookupElement castQualifier(
      @NotNull LookupElement item,
      @Nullable final PsiTypeLookupItem castTypeItem,
      @Nullable PsiType plainQualifier,
      JavaCompletionProcessor processor) {
    if (castTypeItem == null) {
      return item;
    }
    if (plainQualifier != null) {
      Object o = item.getObject();
      if (o instanceof PsiMethod) {
        PsiType castType = castTypeItem.getType();
        if (plainQualifier instanceof PsiClassType && castType instanceof PsiClassType) {
          PsiMethod method = (PsiMethod) o;
          PsiClassType.ClassResolveResult plainResult =
              ((PsiClassType) plainQualifier).resolveGenerics();
          PsiClass plainClass = plainResult.getElement();
          if (plainClass != null && plainClass.findMethodBySignature(method, true) != null) {
            PsiClass castClass = ((PsiClassType) castType).resolveGenerics().getElement();

            if (castClass == null || !castClass.isInheritor(plainClass, true)) {
              return item;
            }

            PsiSubstitutor plainSub = plainResult.getSubstitutor();
            PsiSubstitutor castSub =
                TypeConversionUtil.getSuperClassSubstitutor(plainClass, (PsiClassType) castType);
            PsiType returnType = method.getReturnType();
            if (method.getSignature(plainSub).equals(method.getSignature(castSub))) {
              PsiType typeAfterCast = toRaw(castSub.substitute(returnType));
              PsiType typeDeclared = toRaw(plainSub.substitute(returnType));
              if (typeAfterCast != null
                  && typeDeclared != null
                  && typeAfterCast.isAssignableFrom(typeDeclared)
                  && processor.isAccessible(plainClass.findMethodBySignature(method, true))) {
                return item;
              }
            }
          }
        }
      } else if (containsMember(plainQualifier, o)) {
        return item;
      }
    }

    return LookupElementDecorator.withInsertHandler(
        item,
        new InsertHandlerDecorator<LookupElement>() {
          @Override
          public void handleInsert(
              InsertionContext context, LookupElementDecorator<LookupElement> item) {
            final Document document = context.getEditor().getDocument();
            context.commitDocument();
            final PsiFile file = context.getFile();
            final PsiJavaCodeReferenceElement ref =
                PsiTreeUtil.findElementOfClassAtOffset(
                    file, context.getStartOffset(), PsiJavaCodeReferenceElement.class, false);
            if (ref != null) {
              final PsiElement qualifier = ref.getQualifier();
              if (qualifier != null) {
                final CommonCodeStyleSettings settings = context.getCodeStyleSettings();

                final String parenSpace = settings.SPACE_WITHIN_PARENTHESES ? " " : "";
                document.insertString(qualifier.getTextRange().getEndOffset(), parenSpace + ")");

                final String spaceWithin = settings.SPACE_WITHIN_CAST_PARENTHESES ? " " : "";
                final String prefix = "(" + parenSpace + "(" + spaceWithin;
                final String spaceAfter = settings.SPACE_AFTER_TYPE_CAST ? " " : "";
                final int exprStart = qualifier.getTextRange().getStartOffset();
                document.insertString(exprStart, prefix + spaceWithin + ")" + spaceAfter);

                CompletionUtil.emulateInsertion(context, exprStart + prefix.length(), castTypeItem);
                PsiDocumentManager.getInstance(file.getProject())
                    .doPostponedOperationsAndUnblockDocument(document);
                context.getEditor().getCaretModel().moveToOffset(context.getTailOffset());
              }
            }

            item.getDelegate().handleInsert(context);
          }
        });
  }
  private static boolean provablyDistinct(PsiType type1, PsiType type2, int level) {
    if (type1 instanceof PsiClassType
        && ((PsiClassType) type1).resolve() instanceof PsiTypeParameter) return false;
    if (type2 instanceof PsiClassType
        && ((PsiClassType) type2).resolve() instanceof PsiTypeParameter) return false;
    if (type1 instanceof PsiWildcardType) {
      if (type2 instanceof PsiWildcardType) {
        return provablyDistinct((PsiWildcardType) type1, (PsiWildcardType) type2);
      }

      if (type2 instanceof PsiCapturedWildcardType) {
        return ((PsiWildcardType) type1).isExtends() && level > 0
            || provablyDistinct(
                (PsiWildcardType) type1, ((PsiCapturedWildcardType) type2).getWildcard());
      }

      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(
                  ((PsiWildcardType) type1).getManager().getProject(),
                  (PsiArrayType) extendsBound,
                  type2)) return true;
          final PsiClass boundClass1 = PsiUtil.resolveClassInType(extendsBound);
          if (boundClass1 == null) return false;
          return proveExtendsBoundsDistinct(type1, type2, boundClass1, psiClass2);
        }

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

          final PsiClass boundClass1 = PsiUtil.resolveClassInType(superBound);
          if (boundClass1 == null || boundClass1 instanceof PsiTypeParameter) return false;
          return !InheritanceUtil.isInheritorOrSelf(boundClass1, psiClass2, true);
        }

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

      if (type2 instanceof PsiArrayType) {
        return proveArrayTypeDistinct(
            ((PsiWildcardType) type1).getManager().getProject(), (PsiArrayType) type2, type1);
      }
    }
    if (type1 instanceof PsiCapturedWildcardType)
      return provablyDistinct(((PsiCapturedWildcardType) type1).getWildcard(), type2, level);

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

    final PsiClassType.ClassResolveResult classResolveResult1 =
        PsiUtil.resolveGenericsClassInType(type1);
    final PsiClassType.ClassResolveResult classResolveResult2 =
        PsiUtil.resolveGenericsClassInType(type2);
    if (Comparing.equal(TypeConversionUtil.erasure(type1), TypeConversionUtil.erasure(type2))) {
      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) return false;
        if (substitutedType1 == null || substitutedType2 == null) {
          return true;
        } else {
          if (provablyDistinct(substitutedType1, substitutedType2, level + 1)) return true;
          if (substitutedType1 instanceof PsiWildcardType
              && !((PsiWildcardType) substitutedType1).isBounded()) return true;
        }
      }
      return false;
    }

    final PsiClass boundClass1 = classResolveResult1.getElement();
    final PsiClass boundClass2 = classResolveResult2.getElement();
    return type2 != null
        && type1 != null
        && !type1.equals(type2)
        && (!InheritanceUtil.isInheritorOrSelf(boundClass1, boundClass2, true)
            || !InheritanceUtil.isInheritorOrSelf(boundClass2, boundClass1, true));
  }