public static @Nullable PsiType getFunctionalInterfaceType(PsiElement expression, boolean tryToSubstitute) {
    PsiElement parent = expression.getParent();
    PsiElement element = expression;
    while (parent instanceof PsiParenthesizedExpression || parent instanceof PsiConditionalExpression) {
      if (parent instanceof PsiConditionalExpression && ((PsiConditionalExpression)parent).getCondition() == element) {
        return PsiTypes.booleanType();
      }
      element = parent;
      parent = parent.getParent();
    }

    PsiType type = ThreadLocalTypes.getElementType(expression);
    if (type == null) type = ThreadLocalTypes.getElementType(element);
    if (type != null) {
      return type;
    }

    if (parent instanceof PsiArrayInitializerExpression) {
      final PsiType psiType = ((PsiArrayInitializerExpression)parent).getType();
      if (psiType instanceof PsiArrayType) {
        return ((PsiArrayType)psiType).getComponentType();
      }
    }
    else if (parent instanceof PsiTypeCastExpression) {
      // Ensure no capture is performed to target type of cast expression, from 15.16 "Cast Expressions":
      // Casts can be used to explicitly "tag" a lambda expression or a method reference expression with a particular target type.
      // To provide an appropriate degree of flexibility, the target type may be a list of types denoting an intersection type,
      // provided the intersection induces a functional interface (p9.8).
      final PsiTypeElement castTypeElement = ((PsiTypeCastExpression)parent).getCastType();
      final PsiType castType = castTypeElement != null ? castTypeElement.getType() : null;
      if (castType instanceof PsiIntersectionType) {
        final PsiType conjunct = extractFunctionalConjunct((PsiIntersectionType)castType);
        if (conjunct != null) return conjunct;
      }
      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 >= 0) {
        PsiElement gParent = expressionList.getParent();

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

        if (gParent instanceof PsiCall) {
          final PsiCall contextCall = (PsiCall)gParent;
          JavaResolveResult resolveResult = PsiDiamondType.getDiamondsAwareResolveResult(contextCall);
          PsiElement resultElement = resolveResult.getElement();
          LOG.assertTrue(!(MethodCandidateInfo.isOverloadCheck(contextCall.getArgumentList()) &&
                           resultElement instanceof PsiMethod &&
                           ((PsiMethod)resultElement).hasTypeParameters() &&
                           contextCall instanceof PsiCallExpression && ((PsiCallExpression)contextCall).getTypeArguments().length == 0));
          return getSubstitutedType(expression, tryToSubstitute, lambdaIdx, resolveResult);
        }
      }
    }
    else if (parent instanceof PsiReturnStatement) {
      return PsiTypesUtil.getMethodReturnType(parent);
    }
    else if (parent instanceof PsiLambdaExpression) {
      return getFunctionalInterfaceTypeByContainingLambda((PsiLambdaExpression)parent);
    }
    PsiSwitchExpression switchExpression = PsiTreeUtil.getParentOfType(element, PsiSwitchExpression.class);
    if (switchExpression != null && PsiUtil.getSwitchResultExpressions(switchExpression).contains(element)) {
      return getFunctionalInterfaceType(switchExpression, tryToSubstitute);
    }
    return null;
  }
public static @Nullable PsiType getFunctionalInterfaceType(PsiElement expression, boolean tryToSubstitute)