private Pair<PsiType, ConstraintType> inferMethodTypeParameterFromParent(final @NotNull PsiElement parent,
                                                                           @NotNull PsiExpression methodCall,
                                                                           @NotNull PsiTypeParameter typeParameter,
                                                                           @NotNull PsiSubstitutor substitutor,
                                                                           @NotNull ParameterTypeInferencePolicy policy) {
    Pair<PsiType, ConstraintType> constraint = null;
    PsiType expectedType = PsiTypesUtil.getExpectedTypeByParent(methodCall);

    if (expectedType == null) {
      if (parent instanceof PsiExpressionList) {
        final PsiElement pParent = parent.getParent();
        if (pParent instanceof PsiCallExpression && parent.equals(((PsiCallExpression)pParent).getArgumentList())) {
          constraint = policy.inferTypeConstraintFromCallContext(methodCall, (PsiExpressionList)parent, (PsiCallExpression)pParent, typeParameter);
        }
      }
    }

    final GlobalSearchScope scope = parent.getResolveScope();
    PsiType returnType = null;
    if (constraint == null) {
      if (expectedType == null) {
        expectedType = methodCall instanceof PsiCallExpression ? policy.getDefaultExpectedType((PsiCallExpression)methodCall) : null;
      }

      if (policy.requestForBoxingExplicitly() && TypeConversionUtil.isPrimitiveAndNotNull(expectedType)) {
        expectedType = ((PsiPrimitiveType)expectedType).getBoxedType(typeParameter);
      }

      returnType = ((PsiMethod)typeParameter.getOwner()).getReturnType();

      constraint =
        getSubstitutionForTypeParameterConstraint(typeParameter, returnType, expectedType, false, PsiUtil.getLanguageLevel(parent));

      if (constraint != null) {
        PsiType guess = constraint.getFirst();
        if (guess != null &&
            !guess.equals(PsiTypes.nullType()) &&
            constraint.getSecond() == ConstraintType.SUPERTYPE &&
            guess instanceof PsiIntersectionType &&
            !JavaVersionService.getInstance().isAtLeast(parent, JavaSdkVersion.JDK_1_7)) {
          for (PsiType conjuct : ((PsiIntersectionType)guess).getConjuncts()) {
            if (!conjuct.isAssignableFrom(expectedType)) {
              return FAILED_INFERENCE;
            }
          }
        }
      }
    }

    PsiType[] superTypes = typeParameter.getSuperTypes();
    final PsiType[] types = PsiType.createArray(superTypes.length);
    for (int i = 0; i < superTypes.length; i++) {
      PsiType superType = substitutor.substitute(superTypes[i]);
      if (superType instanceof PsiClassType && ((PsiClassType)superType).isRaw()) {
        superType = TypeConversionUtil.erasure(superType);
      }
      if (superType == null) superType = PsiType.getJavaLangObject(myManager, scope);
      types[i] = superType;
    }

    if (constraint == null) {
      if (methodCall instanceof PsiCallExpression) {
        if (types.length == 0) return null;
        return policy.getInferredTypeWithNoConstraint(myManager, PsiIntersectionType.createIntersection(types));
      }
      return null;
    }
    PsiType guess = constraint.getFirst();
    if (guess != null && types.length > 0) {
      guess = GenericsUtil.getGreatestLowerBound(guess, PsiIntersectionType.createIntersection(types));
    }
    guess = policy.adjustInferredType(myManager, guess, constraint.getSecond());

    //The following code is the result of deep thought, do not shit it out before discussing with [ven]
    if (returnType instanceof PsiClassType && typeParameter.equals(((PsiClassType)returnType).resolve())) {
      PsiClassType[] extendsTypes = typeParameter.getExtendsListTypes();
      PsiSubstitutor newSubstitutor = substitutor.put(typeParameter, guess);
      for (PsiClassType extendsType1 : extendsTypes) {
        PsiType extendsType = newSubstitutor.substitute(extendsType1);
        if (guess != null && extendsType != null && !extendsType.isAssignableFrom(guess)) {
          if (guess.isAssignableFrom(extendsType)) {
            guess = extendsType;
            newSubstitutor = substitutor.put(typeParameter, guess);
          }
          else {
            break;
          }
        }
      }
    }

    return Pair.create(guess, constraint.getSecond());
  }
private Pair<PsiType, ConstraintType> inferMethodTypeParameterFromParent(final @NotNull PsiElement parent,
                                                                           @NotNull PsiExpression methodCall,
                                                                           @NotNull PsiTypeParameter typeParameter,
                                                                           @NotNull PsiSubstitutor substitutor,
                                                                           @NotNull ParameterTypeInferencePolicy policy)