@Override
    public void visitBinaryExpression(GrBinaryExpression expression) {
      final IElementType type = expression.getOperationTokenType();
      final GrExpression left = expression.getLeftOperand();
      final GrExpression right = expression.getRightOperand();

      if (type == mREGEX_FIND || type == mREGEX_MATCH) {
        final PsiClassType string =
            TypesUtil.createType(CommonClassNames.JAVA_LANG_STRING, expression);
        myResult = createSimpleSubTypeResult(string);
        return;
      }

      final GrExpression other = myExpression == left ? right : left;
      final PsiType otherType = other != null ? other.getType() : null;

      if (otherType == null) return;

      if (type == mPLUS && otherType.equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
        final PsiClassType obj = TypesUtil.getJavaLangObject(expression);
        myResult = createSimpleSubTypeResult(obj);
        return;
      }

      myResult = createSimpleSubTypeResult(otherType);
    }
  /** @return replaced expression or null if expression is not replaced */
  @Nullable
  private static GrExpression addParenthesesIfNeeded(
      GrExpression newExpr, GrExpression oldExpr, GrExpression oldParent) {
    GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(oldExpr.getProject());

    int parentPriorityLevel = getExprPriorityLevel(oldParent);
    int newPriorityLevel = getExprPriorityLevel(newExpr);

    if (parentPriorityLevel > newPriorityLevel) {
      newExpr = factory.createParenthesizedExpr(newExpr);
    } else if (parentPriorityLevel == newPriorityLevel && parentPriorityLevel != 0) {
      if (oldParent instanceof GrBinaryExpression) {
        GrBinaryExpression binaryExpression = (GrBinaryExpression) oldParent;
        if (isNotAssociative(binaryExpression)
            && oldExpr.equals(binaryExpression.getRightOperand())) {
          newExpr = factory.createParenthesizedExpr(newExpr);
        }
      }
    }
    return newExpr;
  }
 private static boolean isNotAssociative(GrBinaryExpression binaryExpression) {
   final IElementType opToken = binaryExpression.getOperationTokenType();
   if (binaryExpression instanceof GrMultiplicativeExpressionImpl) {
     return opToken != mSTAR;
   }
   if (binaryExpression instanceof GrAdditiveExpressionImpl) {
     return opToken == mMINUS;
   }
   return RELATIONS.contains(opToken)
       || opToken == mCOMPARE_TO
       || opToken == mREGEX_FIND
       || opToken == mREGEX_MATCH
       || SHIFT_SIGNS.contains(opToken)
       || opToken == mSTAR;
 }
  @Override
  public void visitBinaryExpression(GrBinaryExpression expression) {
    final GrExpression left = expression.getLeftOperand();
    final GrExpression right = expression.getRightOperand();
    final IElementType opType = expression.getOperationTokenType();

    if (ControlFlowBuilderUtil.isInstanceOfBinary(expression)) {
      expression.getLeftOperand().accept(this);
      processInstanceOf(expression);
      return;
    }

    if (opType != mLOR && opType != mLAND && opType != kIN) {
      left.accept(this);
      if (right != null) {
        right.accept(this);
      }
      visitCall(expression);
      return;
    }

    ConditionInstruction condition = new ConditionInstruction(expression);
    addNodeAndCheckPending(condition);
    registerCondition(condition);

    left.accept(this);

    if (right == null) return;

    final List<GotoInstruction> negations = collectAndRemoveAllPendingNegations(expression);

    visitCall(expression);

    if (opType == mLAND) {
      InstructionImpl head = myHead;
      if (negations.isEmpty()) {
        addNode(new NegatingGotoInstruction(expression, condition));
        handlePossibleReturn(expression);
        addPendingEdge(expression, myHead);
      } else {
        for (GotoInstruction negation : negations) {
          myHead = negation;
          handlePossibleReturn(expression);
          addPendingEdge(expression, myHead);
        }
      }
      myHead = head;
    } else /*if (opType == mLOR)*/ {
      final InstructionImpl instruction =
          addNodeAndCheckPending(
              new InstructionImpl(expression)); // collect all pending edges from left argument
      handlePossibleReturn(expression);
      addPendingEdge(expression, myHead);
      myHead = instruction;

      InstructionImpl head = reduceAllNegationsIntoInstruction(expression, negations);
      if (head != null) myHead = head;
      // addNode(new NegatingGotoInstruction(expression, myInstructionNumber++, condition));
    }
    myConditions.removeFirstOccurrence(condition);

    right.accept(this);
  }