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;
 }
  // 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;
  }
  public static PsiSubstitutor getSubstitutor(
      @NotNull PsiMethod method, @NotNull PsiClassType.ClassResolveResult resolveResult) {
    final PsiClass derivedClass = resolveResult.getElement();
    LOG.assertTrue(derivedClass != null);

    final PsiClass methodContainingClass = method.getContainingClass();
    LOG.assertTrue(methodContainingClass != null);
    PsiSubstitutor initialSubst = resolveResult.getSubstitutor();
    final PsiSubstitutor superClassSubstitutor =
        TypeConversionUtil.getSuperClassSubstitutor(
            methodContainingClass, derivedClass, PsiSubstitutor.EMPTY);
    for (PsiTypeParameter param : superClassSubstitutor.getSubstitutionMap().keySet()) {
      final PsiType substitute = superClassSubstitutor.substitute(param);
      if (substitute != null) {
        initialSubst = initialSubst.put(param, initialSubst.substitute(substitute));
      }
    }
    return initialSubst;
  }
 @Nullable
 public static PsiMethod getFunctionalInterfaceMethod(PsiClassType.ClassResolveResult result) {
   final PsiClass psiClass = result.getElement();
   if (psiClass != null) {
     final MethodSignature methodSignature = getFunction(psiClass);
     if (methodSignature != null) {
       return getMethod(psiClass, methodSignature);
     }
   }
   return null;
 }
 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;
        }
 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;
 }
 @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);
 }
  @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);
          }
        });
  }
  // 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;
  }
  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;
  }
  @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;
  }