public static boolean isReceiverType(
     PsiType functionalInterfaceType,
     PsiClass containingClass,
     @Nullable PsiMethod referencedMethod) {
   final PsiClassType.ClassResolveResult resolveResult =
       PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
   final MethodSignature function = LambdaUtil.getFunction(resolveResult.getElement());
   if (function != null) {
     final int interfaceMethodParamsLength = function.getParameterTypes().length;
     if (interfaceMethodParamsLength > 0) {
       final PsiType firstParamType =
           resolveResult.getSubstitutor().substitute(function.getParameterTypes()[0]);
       boolean isReceiver =
           isReceiverType(
               firstParamType,
               containingClass,
               PsiUtil.resolveGenericsClassInType(firstParamType).getSubstitutor());
       if (isReceiver) {
         if (referencedMethod == null) {
           if (interfaceMethodParamsLength == 1) return true;
           return false;
         }
         if (referencedMethod.getParameterList().getParametersCount()
             != interfaceMethodParamsLength - 1) {
           return false;
         }
         return true;
       }
     }
   }
   return false;
 }
 @Nullable
 public static PsiType getFunctionalInterfaceReturnType(
     @Nullable PsiType functionalInterfaceType) {
   final PsiClassType.ClassResolveResult resolveResult =
       PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
   final PsiClass psiClass = resolveResult.getElement();
   if (psiClass != null) {
     final MethodSignature methodSignature = getFunction(psiClass);
     if (methodSignature != null) {
       final PsiType returnType = getReturnType(psiClass, methodSignature);
       return resolveResult.getSubstitutor().substitute(returnType);
     }
   }
   return null;
 }
 @NotNull
 public static QualifierResolveResult getQualifierResolveResult(
     @NotNull PsiMethodReferenceExpression methodReferenceExpression) {
   PsiClass containingClass = null;
   PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
   final PsiExpression expression = methodReferenceExpression.getQualifierExpression();
   if (expression != null) {
     final PsiType expressionType = getExpandedType(expression.getType(), expression);
     PsiClassType.ClassResolveResult result = PsiUtil.resolveGenericsClassInType(expressionType);
     containingClass = result.getElement();
     if (containingClass != null) {
       substitutor = result.getSubstitutor();
     }
     if (containingClass == null && expression instanceof PsiReferenceExpression) {
       final JavaResolveResult resolveResult =
           ((PsiReferenceExpression) expression).advancedResolve(false);
       final PsiElement resolve = resolveResult.getElement();
       if (resolve instanceof PsiClass) {
         containingClass = (PsiClass) resolve;
         substitutor = resolveResult.getSubstitutor();
         return new QualifierResolveResult(containingClass, substitutor, true);
       }
     }
   } else {
     final PsiTypeElement typeElement = methodReferenceExpression.getQualifierType();
     if (typeElement != null) {
       PsiType type = getExpandedType(typeElement.getType(), typeElement);
       PsiClassType.ClassResolveResult result = PsiUtil.resolveGenericsClassInType(type);
       containingClass = result.getElement();
       if (containingClass != null) {
         return new QualifierResolveResult(containingClass, result.getSubstitutor(), true);
       }
     }
   }
   return new QualifierResolveResult(containingClass, substitutor, false);
 }
 private static PsiType getLambdaParameterFromType(
     int parameterIndex, PsiLambdaExpression lambdaExpression, PsiType conjunct) {
   final PsiClassType.ClassResolveResult resolveResult =
       PsiUtil.resolveGenericsClassInType(conjunct);
   if (resolveResult != null) {
     final PsiMethod method = getFunctionalInterfaceMethod(conjunct);
     if (method != null) {
       final PsiParameter[] parameters = method.getParameterList().getParameters();
       if (parameterIndex < parameters.length) {
         final PsiType psiType =
             getSubstitutor(method, resolveResult)
                 .substitute(parameters[parameterIndex].getType());
         if (!dependsOnTypeParams(psiType, conjunct, lambdaExpression)) {
           return GenericsUtil.eliminateWildcards(psiType);
         }
       }
     }
   }
   return null;
 }
 public static boolean isReceiverType(
     PsiType receiverType, @Nullable PsiClass containingClass, PsiSubstitutor psiSubstitutor) {
   if (containingClass != null) {
     receiverType = getExpandedType(receiverType, containingClass);
   }
   final PsiClassType.ClassResolveResult resolveResult =
       PsiUtil.resolveGenericsClassInType(receiverType);
   final PsiClass receiverClass = resolveResult.getElement();
   if (receiverClass != null && isReceiverType(receiverClass, containingClass)) {
     if (emptyOrRaw(containingClass, psiSubstitutor)) {
       return true;
     }
     final PsiSubstitutor derivedSubstitutor =
         TypeConversionUtil.getClassSubstitutor(containingClass, receiverClass, psiSubstitutor);
     return derivedSubstitutor != null
         && TypeConversionUtil.isAssignable(
             JavaPsiFacade.getElementFactory(containingClass.getProject())
                 .createType(containingClass, derivedSubstitutor),
             receiverType);
   }
   return false;
 }
 private static boolean hasReceiver(
     @NotNull PsiMethodReferenceExpression methodRef,
     @NotNull PsiMethod method,
     PsiType functionalInterfaceType) {
   final PsiClassType.ClassResolveResult resolveResult =
       PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
   final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
   final MethodSignature signature =
       interfaceMethod != null
           ? interfaceMethod.getSignature(
               LambdaUtil.getSubstitutor(interfaceMethod, resolveResult))
           : null;
   if (signature == null) {
     return false;
   }
   final PsiType[] parameterTypes = signature.getParameterTypes();
   final QualifierResolveResult qualifierResolveResult = getQualifierResolveResult(methodRef);
   return (method.getParameterList().getParametersCount() + 1 == parameterTypes.length
           || method.isVarArgs()
               && parameterTypes.length > 0
               && !method.hasModifierProperty(PsiModifier.STATIC))
       && hasReceiver(parameterTypes, qualifierResolveResult, methodRef);
 }
  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));
  }
 public TypeParamsChecker(PsiElement expression) {
   this(
       expression,
       PsiUtil.resolveGenericsClassInType(getFunctionalInterfaceType(expression, false))
           .getElement());
 }
 @Nullable
 public static PsiMethod getFunctionalInterfaceMethod(@Nullable PsiType functionalInterfaceType) {
   return getFunctionalInterfaceMethod(
       PsiUtil.resolveGenericsClassInType(functionalInterfaceType));
 }
  @Nullable
  public static PsiType getFunctionalInterfaceType(
      PsiElement expression, final boolean tryToSubstitute, int paramIdx) {
    PsiElement parent = expression.getParent();
    PsiElement element = expression;
    while (parent instanceof PsiParenthesizedExpression
        || parent instanceof PsiConditionalExpression) {
      if (parent instanceof PsiConditionalExpression
          && ((PsiConditionalExpression) parent).getThenExpression() != element
          && ((PsiConditionalExpression) parent).getElseExpression() != element) break;
      element = parent;
      parent = parent.getParent();
    }
    if (parent instanceof PsiArrayInitializerExpression) {
      final PsiType psiType = ((PsiArrayInitializerExpression) parent).getType();
      if (psiType instanceof PsiArrayType) {
        return ((PsiArrayType) psiType).getComponentType();
      }
    } else if (parent instanceof PsiTypeCastExpression) {
      final PsiType castType = ((PsiTypeCastExpression) parent).getType();
      if (castType instanceof PsiIntersectionType) {
        for (PsiType conjunctType : ((PsiIntersectionType) castType).getConjuncts()) {
          if (getFunctionalInterfaceMethod(conjunctType) != null) return conjunctType;
        }
      }
      return castType;
    } else if (parent instanceof PsiVariable) {
      return ((PsiVariable) parent).getType();
    } else if (parent instanceof PsiAssignmentExpression
        && expression instanceof PsiExpression
        && !PsiUtil.isOnAssignmentLeftHand((PsiExpression) expression)) {
      final PsiExpression lExpression = ((PsiAssignmentExpression) parent).getLExpression();
      return lExpression.getType();
    } else if (parent instanceof PsiExpressionList) {
      final PsiExpressionList expressionList = (PsiExpressionList) parent;
      final int lambdaIdx = getLambdaIdx(expressionList, expression);
      if (lambdaIdx > -1) {

        PsiType cachedType = null;
        final Pair<PsiMethod, PsiSubstitutor> method = MethodCandidateInfo.getCurrentMethod(parent);
        if (method != null) {
          final PsiParameter[] parameters = method.first.getParameterList().getParameters();
          cachedType =
              lambdaIdx < parameters.length
                  ? method.second.substitute(
                      getNormalizedType(
                          parameters[adjustLambdaIdx(lambdaIdx, method.first, parameters)]))
                  : null;
          if (!tryToSubstitute) return cachedType;
        }

        PsiElement gParent = expressionList.getParent();

        if (gParent instanceof PsiAnonymousClass) {
          gParent = gParent.getParent();
        }

        if (gParent instanceof PsiCall) {
          final PsiCall contextCall = (PsiCall) gParent;
          final JavaResolveResult resolveResult = contextCall.resolveMethodGenerics();
          final PsiElement resolve = resolveResult.getElement();
          if (resolve instanceof PsiMethod) {
            final PsiParameter[] parameters =
                ((PsiMethod) resolve).getParameterList().getParameters();
            final int finalLambdaIdx = adjustLambdaIdx(lambdaIdx, (PsiMethod) resolve, parameters);
            if (finalLambdaIdx < parameters.length) {
              if (!tryToSubstitute) return getNormalizedType(parameters[finalLambdaIdx]);
              if (cachedType != null && paramIdx > -1) {
                final PsiMethod interfaceMethod = getFunctionalInterfaceMethod(cachedType);
                if (interfaceMethod != null) {
                  final PsiClassType.ClassResolveResult cachedResult =
                      PsiUtil.resolveGenericsClassInType(cachedType);
                  final PsiType interfaceMethodParameterType =
                      interfaceMethod.getParameterList().getParameters()[paramIdx].getType();
                  if (!dependsOnTypeParams(
                      cachedResult.getSubstitutor().substitute(interfaceMethodParameterType),
                      cachedType,
                      expression)) {
                    return cachedType;
                  }
                }
              }
              return PsiResolveHelper.ourGuard.doPreventingRecursion(
                  expression,
                  true,
                  new Computable<PsiType>() {
                    @Override
                    public PsiType compute() {
                      return resolveResult
                          .getSubstitutor()
                          .substitute(getNormalizedType(parameters[finalLambdaIdx]));
                    }
                  });
            }
          }
          return null;
        }
      }
    } else if (parent instanceof PsiReturnStatement) {
      final PsiLambdaExpression gParent =
          PsiTreeUtil.getParentOfType(parent, PsiLambdaExpression.class);
      if (gParent != null) {
        return getFunctionalInterfaceTypeByContainingLambda(gParent);
      } else {
        final PsiMethod method = PsiTreeUtil.getParentOfType(parent, PsiMethod.class);
        if (method != null) {
          return method.getReturnType();
        }
      }
    } else if (parent instanceof PsiLambdaExpression) {
      return getFunctionalInterfaceTypeByContainingLambda((PsiLambdaExpression) parent);
    }
    return null;
  }
  public static boolean isAcceptable(
      PsiLambdaExpression lambdaExpression, final PsiType leftType, boolean checkReturnType) {
    if (leftType instanceof PsiIntersectionType) {
      for (PsiType conjunctType : ((PsiIntersectionType) leftType).getConjuncts()) {
        if (isAcceptable(lambdaExpression, conjunctType, checkReturnType)) return true;
      }
      return false;
    }
    final PsiClassType.ClassResolveResult resolveResult =
        PsiUtil.resolveGenericsClassInType(GenericsUtil.eliminateWildcards(leftType));
    final PsiClass psiClass = resolveResult.getElement();
    if (psiClass instanceof PsiAnonymousClass) {
      return isAcceptable(
          lambdaExpression, ((PsiAnonymousClass) psiClass).getBaseClassType(), checkReturnType);
    }
    final MethodSignature methodSignature = getFunction(psiClass);
    if (methodSignature == null) return false;
    final PsiParameter[] lambdaParameters = lambdaExpression.getParameterList().getParameters();
    final PsiType[] parameterTypes = methodSignature.getParameterTypes();
    if (lambdaParameters.length != parameterTypes.length) return false;
    for (int lambdaParamIdx = 0, length = lambdaParameters.length;
        lambdaParamIdx < length;
        lambdaParamIdx++) {
      PsiParameter parameter = lambdaParameters[lambdaParamIdx];
      final PsiTypeElement typeElement = parameter.getTypeElement();
      if (typeElement != null) {
        final PsiType lambdaFormalType = typeElement.getType();
        final PsiType methodParameterType = parameterTypes[lambdaParamIdx];
        if (lambdaFormalType instanceof PsiPrimitiveType) {
          if (methodParameterType instanceof PsiPrimitiveType)
            return methodParameterType.equals(lambdaFormalType);
          return false;
        }

        if (!TypeConversionUtil.erasure(lambdaFormalType)
            .isAssignableFrom(
                TypeConversionUtil.erasure(
                    GenericsUtil.eliminateWildcards(
                        resolveResult
                            .getSubstitutor()
                            .substitute(
                                methodSignature
                                    .getSubstitutor()
                                    .substitute(methodParameterType)))))) {
          return false;
        }
      }
    }
    if (checkReturnType) {
      final String uniqueVarName =
          JavaCodeStyleManager.getInstance(lambdaExpression.getProject())
              .suggestUniqueVariableName("l", lambdaExpression, true);
      String canonicalText = leftType.getCanonicalText();
      if (leftType instanceof PsiEllipsisType) {
        canonicalText = ((PsiEllipsisType) leftType).toArrayType().getCanonicalText();
      }
      final PsiStatement assignmentFromText =
          JavaPsiFacade.getElementFactory(lambdaExpression.getProject())
              .createStatementFromText(
                  canonicalText + " " + uniqueVarName + " = " + lambdaExpression.getText(),
                  lambdaExpression);
      final PsiLocalVariable localVariable =
          (PsiLocalVariable)
              ((PsiDeclarationStatement) assignmentFromText).getDeclaredElements()[0];
      LOG.assertTrue(psiClass != null);
      PsiType methodReturnType = getReturnType(psiClass, methodSignature);
      if (methodReturnType != null) {
        methodReturnType =
            resolveResult
                .getSubstitutor()
                .substitute(methodSignature.getSubstitutor().substitute(methodReturnType));
        return LambdaHighlightingUtil.checkReturnTypeCompatible(
                (PsiLambdaExpression) localVariable.getInitializer(), methodReturnType)
            == null;
      }
    }
    return true;
  }