private static PsiType doGetType(PsiBinaryExpressionImpl param) {
    PsiExpression lOperand = param.getLOperand();
    PsiExpression rOperand = param.getROperand();
    if (rOperand == null) return null;
    PsiType rType = rOperand.getType();
    IElementType sign = param.getOperationTokenType();
    // optimization: if we can calculate type based on right type only
    PsiType type = TypeConversionUtil.calcTypeForBinaryExpression(null, rType, sign, false);
    if (type != TypeConversionUtil.NULL_TYPE) return type;

    if (lOperand instanceof PsiBinaryExpressionImpl
        && !JavaResolveCache.getInstance(param.getProject()).isTypeCached(lOperand)) {
      // cache all intermediate expression types from bottom up
      PsiBinaryExpressionImpl topLevel = param;
      PsiElement element = param;
      while (element instanceof PsiBinaryExpressionImpl) {
        topLevel = (PsiBinaryExpressionImpl) element;
        element = element.getParent();
      }
      topLevel.accept(
          new JavaRecursiveElementWalkingVisitor() {
            @Override
            protected void elementFinished(PsiElement element) {
              if (element instanceof PsiExpression) {
                ProgressIndicatorProvider.checkCanceled();
                ((PsiExpression) element).getType();
              }
            }
          });
    }
    PsiType lType = lOperand.getType();
    return TypeConversionUtil.calcTypeForBinaryExpression(lType, rType, sign, true);
  }