@param Expr $left
@param Expr $right

@return BinOp
438374
public static function subtract(Expr $left, Expr $right) : BinOp
    {
        return new BinOp($left, BinOp::SUBTRACT, $right);
    }
1064576
public function onLogicalExpression($expressionType, $left, $right) {
        switch ($expressionType) {
            case ExpressionType::AND_LOGICAL:
//				return '$logicalAnd('.$left.', '.$right.')';
                return $this->_prepareBinaryExpression(self::LOGICAL_AND, $left, $right);
                break;
            case ExpressionType::OR_LOGICAL:
//				return '$logicalOr('.$left.', '.$right.')';
                return $this->_prepareBinaryExpression(self::LOGICAL_OR, $left, $right);
                break;
            default:
                throw new \InvalidArgumentException('onLogicalExpression');
        }
    }
1006438
public static ExprBoolean toExprBoolean(Expression left, Expression right, int operation) {
	return new OpDecision(left, right, operation);
    }
1119573
public function operate( Expression $left, Expression $right ) {
		$typeSpec = self::$opTypes[$this->name];

		$leftType = self::$typeSpecMap[$typeSpec[0]];
		$rightType = self::$typeSpecMap[$typeSpec[1]];
		$resultType = self::$typeSpecMap[$typeSpec[2]];

		$start = min( $this->pos, $left->pos, $right->pos );
		$end = max( $this->end, $left->end, $right->end );
		$length = $end - $start;

		$newExpr = new Expression( $this->parser, $resultType,
			"{$left->rpn} {$right->rpn} {$this->name}",
			$start, $length );

		if ( !$left->isType( $leftType ) ) {
			$newExpr->error( "invalid type for left operand: expected $leftType, got {$left->type}" );
		}

		if ( !$right->isType( $rightType ) ) {
			$newExpr->error( "invalid type for right operand: expected $rightType, got {$right->type}" );
		}

		return $newExpr;
	}
341872
protected Expression reOrder(Expression left, Expression right, BinaryOperation.Op op) {
        if (right instanceof BinaryOperation) {
            BinaryOperation rightOp = (BinaryOperation) right;
            if (!rightOp.isSealed() && rightOp.getOp().getPriority() == op.getPriority()) {
                replaceLeft(rightOp, left, op);
                return right;
            }
        }
        return new BinaryOperation(op, left, right);
    }
56605
public static function analyzeConcatOp(
        StatementsAnalyzer $statements_analyzer,
        PhpParser\Node\Expr $left,
        PhpParser\Node\Expr $right,
        Context $context,
        Type\Union &$result_type = null
    ) {
        $codebase = $statements_analyzer->getCodebase();

        $left_type = isset($left->inferredType) ? $left->inferredType : null;
        $right_type = isset($right->inferredType) ? $right->inferredType : null;
        $config = Config::getInstance();

        if ($context->infer_types
            && $left_type
            && $right_type
            && ($left_type->isMixed() || $right_type->isMixed())
        ) {
            $source_analyzer = $statements_analyzer->getSource();

            if ($source_analyzer instanceof FunctionLikeAnalyzer) {
                $function_storage = $source_analyzer->getFunctionLikeStorage($statements_analyzer);

                if ($left instanceof PhpParser\Node\Expr\Variable
                    && is_string($left->name)
                    && isset($left->inferredType)
                ) {
                    $context->inferType(
                        $left->name,
                        $function_storage,
                        $left->inferredType,
                        new Type\Union([new Type\Atomic\TString, new Type\Atomic\TInt]),
                        $codebase
                    );
                }

                if ($right instanceof PhpParser\Node\Expr\Variable
                    && is_string($right->name)
                    && isset($right->inferredType)
                ) {
                    $context->inferType(
                        $right->name,
                        $function_storage,
                        $right->inferredType,
                        new Type\Union([new Type\Atomic\TString, new Type\Atomic\TInt]),
                        $codebase
                    );
                }
            }
        }

        if ($left_type && $right_type) {
            $result_type = Type::getString();

            if ($left_type->hasMixed() || $right_type->hasMixed()) {
                if (!$context->collect_initializations
                    && !$context->collect_mutations
                    && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
                    && (!(($parent_source = $statements_analyzer->getSource())
                            instanceof \Psalm\Internal\Analyzer\FunctionLikeAnalyzer)
                        || !$parent_source->getSource() instanceof \Psalm\Internal\Analyzer\TraitAnalyzer)
                ) {
                    $codebase->analyzer->incrementMixedCount($statements_analyzer->getFilePath());
                }

                if ($left_type->hasMixed()) {
                    if (IssueBuffer::accepts(
                        new MixedOperand(
                            'Left operand cannot be mixed',
                            new CodeLocation($statements_analyzer->getSource(), $left)
                        ),
                        $statements_analyzer->getSuppressedIssues()
                    )) {
                        // fall through
                    }
                } else {
                    if (IssueBuffer::accepts(
                        new MixedOperand(
                            'Right operand cannot be mixed',
                            new CodeLocation($statements_analyzer->getSource(), $right)
                        ),
                        $statements_analyzer->getSuppressedIssues()
                    )) {
                        // fall through
                    }
                }

                return;
            }

            if (!$context->collect_initializations
                && !$context->collect_mutations
                && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
                && (!(($parent_source = $statements_analyzer->getSource())
                        instanceof \Psalm\Internal\Analyzer\FunctionLikeAnalyzer)
                    || !$parent_source->getSource() instanceof \Psalm\Internal\Analyzer\TraitAnalyzer)
            ) {
                $codebase->analyzer->incrementNonMixedCount($statements_analyzer->getFilePath());
            }

            if ($left_type->isNull()) {
                if (IssueBuffer::accepts(
                    new NullOperand(
                        'Cannot concatenate with a ' . $left_type,
                        new CodeLocation($statements_analyzer->getSource(), $left)
                    ),
                    $statements_analyzer->getSuppressedIssues()
                )) {
                    // fall through
                }

                return;
            }

            if ($right_type->isNull()) {
                if (IssueBuffer::accepts(
                    new NullOperand(
                        'Cannot concatenate with a ' . $right_type,
                        new CodeLocation($statements_analyzer->getSource(), $right)
                    ),
                    $statements_analyzer->getSuppressedIssues()
                )) {
                    // fall through
                }

                return;
            }

            if ($left_type->isFalse()) {
                if (IssueBuffer::accepts(
                    new FalseOperand(
                        'Cannot concatenate with a ' . $left_type,
                        new CodeLocation($statements_analyzer->getSource(), $left)
                    ),
                    $statements_analyzer->getSuppressedIssues()
                )) {
                    // fall through
                }

                return;
            }

            if ($right_type->isFalse()) {
                if (IssueBuffer::accepts(
                    new FalseOperand(
                        'Cannot concatenate with a ' . $right_type,
                        new CodeLocation($statements_analyzer->getSource(), $right)
                    ),
                    $statements_analyzer->getSuppressedIssues()
                )) {
                    // fall through
                }

                return;
            }

            if ($left_type->isNullable() && !$left_type->ignore_nullable_issues) {
                if (IssueBuffer::accepts(
                    new PossiblyNullOperand(
                        'Cannot concatenate with a possibly null ' . $left_type,
                        new CodeLocation($statements_analyzer->getSource(), $left)
                    ),
                    $statements_analyzer->getSuppressedIssues()
                )) {
                    // fall through
                }
            }

            if ($right_type->isNullable() && !$right_type->ignore_nullable_issues) {
                if (IssueBuffer::accepts(
                    new PossiblyNullOperand(
                        'Cannot concatenate with a possibly null ' . $right_type,
                        new CodeLocation($statements_analyzer->getSource(), $right)
                    ),
                    $statements_analyzer->getSuppressedIssues()
                )) {
                    // fall through
                }
            }

            if ($left_type->isFalsable() && !$left_type->ignore_falsable_issues) {
                if (IssueBuffer::accepts(
                    new PossiblyFalseOperand(
                        'Cannot concatenate with a possibly false ' . $left_type,
                        new CodeLocation($statements_analyzer->getSource(), $left)
                    ),
                    $statements_analyzer->getSuppressedIssues()
                )) {
                    // fall through
                }
            }

            if ($right_type->isFalsable() && !$right_type->ignore_falsable_issues) {
                if (IssueBuffer::accepts(
                    new PossiblyFalseOperand(
                        'Cannot concatenate with a possibly false ' . $right_type,
                        new CodeLocation($statements_analyzer->getSource(), $right)
                    ),
                    $statements_analyzer->getSuppressedIssues()
                )) {
                    // fall through
                }
            }

            $left_type_match = true;
            $right_type_match = true;

            $left_has_scalar_match = false;
            $right_has_scalar_match = false;

            $has_valid_left_operand = false;
            $has_valid_right_operand = false;

            foreach ($left_type->getTypes() as $left_type_part) {
                if ($left_type_part instanceof Type\Atomic\TTemplateParam) {
                    if (IssueBuffer::accepts(
                        new MixedOperand(
                            'Left operand cannot be mixed',
                            new CodeLocation($statements_analyzer->getSource(), $left)
                        ),
                        $statements_analyzer->getSuppressedIssues()
                    )) {
                        // fall through
                    }

                    return;
                }

                if ($left_type_part instanceof Type\Atomic\TNull || $left_type_part instanceof Type\Atomic\TFalse) {
                    continue;
                }

                $left_type_part_match = TypeAnalyzer::isAtomicContainedBy(
                    $codebase,
                    $left_type_part,
                    new Type\Atomic\TString,
                    false,
                    false,
                    $left_has_scalar_match,
                    $left_type_coerced,
                    $left_type_coerced_from_mixed,
                    $left_to_string_cast
                );

                $left_type_match = $left_type_match && $left_type_part_match;

                $has_valid_left_operand = $has_valid_left_operand || $left_type_part_match;

                if ($left_to_string_cast && $config->strict_binary_operands) {
                    if (IssueBuffer::accepts(
                        new ImplicitToStringCast(
                            'Left side of concat op expects string, '
                                . '\'' . $left_type . '\' provided with a __toString method',
                            new CodeLocation($statements_analyzer->getSource(), $left)
                        ),
                        $statements_analyzer->getSuppressedIssues()
                    )) {
                        // fall through
                    }
                }
            }

            foreach ($right_type->getTypes() as $right_type_part) {
                if ($right_type_part instanceof Type\Atomic\TTemplateParam) {
                    if (IssueBuffer::accepts(
                        new MixedOperand(
                            'Right operand cannot be a template param',
                            new CodeLocation($statements_analyzer->getSource(), $right)
                        ),
                        $statements_analyzer->getSuppressedIssues()
                    )) {
                        // fall through
                    }

                    return;
                }

                if ($right_type_part instanceof Type\Atomic\TNull || $right_type_part instanceof Type\Atomic\TFalse) {
                    continue;
                }

                $right_type_part_match = TypeAnalyzer::isAtomicContainedBy(
                    $codebase,
                    $right_type_part,
                    new Type\Atomic\TString,
                    false,
                    false,
                    $right_has_scalar_match,
                    $right_type_coerced,
                    $right_type_coerced_from_mixed,
                    $right_to_string_cast
                );

                $right_type_match = $right_type_match && $right_type_part_match;

                $has_valid_right_operand = $has_valid_right_operand || $right_type_part_match;

                if ($right_to_string_cast && $config->strict_binary_operands) {
                    if (IssueBuffer::accepts(
                        new ImplicitToStringCast(
                            'Right side of concat op expects string, '
                                . '\'' . $right_type . '\' provided with a __toString method',
                            new CodeLocation($statements_analyzer->getSource(), $right)
                        ),
                        $statements_analyzer->getSuppressedIssues()
                    )) {
                        // fall through
                    }
                }
            }

            if (!$left_type_match && (!$left_has_scalar_match || $config->strict_binary_operands)) {
                if ($has_valid_left_operand) {
                    if (IssueBuffer::accepts(
                        new PossiblyInvalidOperand(
                            'Cannot concatenate with a ' . $left_type,
                            new CodeLocation($statements_analyzer->getSource(), $left)
                        ),
                        $statements_analyzer->getSuppressedIssues()
                    )) {
                        // fall through
                    }
                } else {
                    if (IssueBuffer::accepts(
                        new InvalidOperand(
                            'Cannot concatenate with a ' . $left_type,
                            new CodeLocation($statements_analyzer->getSource(), $left)
                        ),
                        $statements_analyzer->getSuppressedIssues()
                    )) {
                        // fall through
                    }
                }
            }

            if (!$right_type_match && (!$right_has_scalar_match || $config->strict_binary_operands)) {
                if ($has_valid_right_operand) {
                    if (IssueBuffer::accepts(
                        new PossiblyInvalidOperand(
                            'Cannot concatenate with a ' . $right_type,
                            new CodeLocation($statements_analyzer->getSource(), $right)
                        ),
                        $statements_analyzer->getSuppressedIssues()
                    )) {
                        // fall through
                    }
                } else {
                    if (IssueBuffer::accepts(
                        new InvalidOperand(
                            'Cannot concatenate with a ' . $right_type,
                            new CodeLocation($statements_analyzer->getSource(), $right)
                        ),
                        $statements_analyzer->getSuppressedIssues()
                    )) {
                        // fall through
                    }
                }
            }
        }
        // When concatenating two known string literals (with only one possibility),
        // put the concatenated string into $result_type
        if ($left_type && $right_type && $left_type->isSingleStringLiteral() && $right_type->isSingleStringLiteral()) {
            $literal = $left_type->getSingleStringLiteral()->value . $right_type->getSingleStringLiteral()->value;
            if (strlen($literal) <= 1000) {
                // Limit these to 10000 bytes to avoid extremely large union types from repeated concatenations, etc
                $result_type = Type::getString($literal);
            }
        }
    }
686677
public function expr($leftCond, $operator, $rightCond)
    {
        if (!$this->currentExpressionGroup) {
            throw new RuntimeException($this->translate(
                'Cannot add expression. Expression group is not started.'
            ));
        }
        $this->currentExpressionGroup->addExpression($leftCond, $operator, $rightCond);

        return $this;
    }
471210
private function prepareBinaryExpression($operator, $left, $right)
    {
        return self::OPEN_BRACKET . $left . ' ' . $operator . ' ' . $right . self::CLOSE_BRACKET;
    }
410757
public static Predicate or(Predicate left, Predicate right) {
        left = (Predicate) extract(left);
        right = (Predicate) extract(right);
        if (left == null) {
            return right;
        } else if (right == null) {
            return left;
        } else {
            return predicate(Ops.OR, left, right);
        }
    }
1231854
public function where($left, $right, $comparator = Cmp::EQ, $conjunction = Cmp::CONJUNCTION_AND)
    {
        $left = ($left instanceof Expression) ? $left : new Expr\Column($left);
        $right = ($right instanceof Expression) ? $right : new Expr\Parameter($right);
        
        return $this->condition($left, $right, $comparator, $conjunction);
    }
39034
protected function pExpr_BinaryOp_Plus(BinaryOp\Plus $node)
    {
        return $this->pInfixOp('Expr_BinaryOp_Plus', $node->left, ' + ', $node->right);
    }
632151
public static Boolean or(Object left, Object right) {
        return (Boolean) InfixOpNode.or(left, right, null);
    }
99970
def binary(left, operator, right)
      parentheses do
        left.visit(self)
        append(" #{operator} ")
        right.visit(self)
      end
    end
765125
public BooleanExpression or(@Nullable Predicate right) {
        right = (Predicate) ExpressionUtils.extract(right);
        if (right != null) {
            return Expressions.booleanOperation(Ops.OR, mixin, right);
        } else {
            return this;
        }
    }
1358150
def on_binop(self, node):    # ('left', 'op', 'right')
        """"""
        return op2func(node.op)(self.run(node.left),
                                self.run(node.right))
640681
public static ExpressionArithmeticOp add(Expression left, Expression right) {
		return new ExpressionArithmeticOp(ArithmeticOperation.ADD, left, right);
	}
655771
public static function strContainsCaseInsensitive(Expr $left, Expr $right) : BinOp
    {
        return new BinOp($left, BinOp::STR_CONTAINS_CASE_INSENSITIVE, $right);
    }