public function visitBinaryAdd(Node $node) : UnionType
{
$left = UnionType::fromNode($this->context, $this->code_base, $node->children['left']);
$right = UnionType::fromNode($this->context, $this->code_base, $node->children['right']);
// fast-track common cases
if ($left->isType(IntType::instance()) && $right->isType(IntType::instance())) {
return IntType::instance()->asUnionType();
}
// If both left and right are arrays, then this is array
// concatenation.
if ($left->isGenericArray() && $right->isGenericArray()) {
if ($left->isEqualTo($right)) {
return $left;
}
return ArrayType::instance()->asUnionType();
}
if (($left->isType(IntType::instance()) || $left->isType(FloatType::instance())) && ($right->isType(IntType::instance()) || $right->isType(FloatType::instance()))) {
return FloatType::instance()->asUnionType();
}
$left_is_array = !$left->genericArrayElementTypes()->isEmpty() && $left->nonArrayTypes()->isEmpty() || $left->isType(ArrayType::instance());
$right_is_array = !$right->genericArrayElementTypes()->isEmpty() && $right->nonArrayTypes()->isEmpty() || $right->isType(ArrayType::instance());
if ($left_is_array && !$right->canCastToUnionType(ArrayType::instance()->asUnionType())) {
Issue::maybeEmit($this->code_base, $this->context, Issue::TypeInvalidRightOperand, $node->lineno ?? 0);
return new UnionType();
} elseif ($right_is_array && !$left->canCastToUnionType(ArrayType::instance()->asUnionType())) {
Issue::maybeEmit($this->code_base, $this->context, Issue::TypeInvalidLeftOperand, $node->lineno ?? 0);
return new UnionType();
} elseif ($left_is_array || $right_is_array) {
// If it is a '+' and we know one side is an array
// and the other is unknown, assume array
return ArrayType::instance()->asUnionType();
}
return new UnionType([IntType::instance(), FloatType::instance()]);
}