public function specifyTypesInCondition(SpecifiedTypes $types, Scope $scope, Node $expr, bool $negated = false, int $source = self::SOURCE_UNKNOWN) : SpecifiedTypes
{
if ($expr instanceof Instanceof_ && $expr->class instanceof Name) {
$class = (string) $expr->class;
if ($class === 'static') {
return $types;
}
if ($class === 'self' && $scope->getClass() !== null) {
$class = $scope->getClass();
}
$printedExpr = $this->printer->prettyPrintExpr($expr->expr);
$objectType = new ObjectType($class, false);
if ($negated) {
if ($source === self::SOURCE_FROM_AND) {
return $types;
}
return $types->addSureNotType($expr->expr, $printedExpr, $objectType);
}
return $types->addSureType($expr->expr, $printedExpr, $objectType);
} elseif ($expr instanceof FuncCall && $expr->name instanceof Name && isset($expr->args[0])) {
$functionName = (string) $expr->name;
$argumentExpression = $expr->args[0]->value;
$specifiedType = null;
if (in_array($functionName, ['is_int', 'is_integer', 'is_long'], true)) {
$specifiedType = new IntegerType(false);
} elseif (in_array($functionName, ['is_float', 'is_double', 'is_real'], true)) {
$specifiedType = new FloatType(false);
} elseif ($functionName === 'is_null') {
$specifiedType = new NullType();
} elseif ($functionName === 'is_array') {
$specifiedType = new ArrayType(new MixedType(true), false);
} elseif ($functionName === 'is_bool') {
$specifiedType = new BooleanType(false);
} elseif ($functionName === 'is_callable') {
$specifiedType = new CallableType(false);
} elseif ($functionName === 'is_resource') {
$specifiedType = new ResourceType(false);
} elseif ($functionName === 'is_iterable') {
$specifiedType = new IterableIterableType(new MixedType(true), false);
}
if ($specifiedType !== null) {
$printedExpr = $this->printer->prettyPrintExpr($argumentExpression);
if ($negated) {
return $types->addSureNotType($argumentExpression, $printedExpr, $specifiedType);
}
return $types->addSureType($argumentExpression, $printedExpr, $specifiedType);
}
} elseif ($expr instanceof BooleanAnd) {
if ($source !== self::SOURCE_UNKNOWN && $source !== self::SOURCE_FROM_AND) {
return $types;
}
$types = $this->specifyTypesInCondition($types, $scope, $expr->left, $negated, self::SOURCE_FROM_AND);
$types = $this->specifyTypesInCondition($types, $scope, $expr->right, $negated, self::SOURCE_FROM_AND);
} elseif ($expr instanceof BooleanOr) {
if ($negated) {
return $types;
}
$types = $this->specifyTypesInCondition($types, $scope, $expr->left, $negated, self::SOURCE_FROM_OR);
$types = $this->specifyTypesInCondition($types, $scope, $expr->right, $negated, self::SOURCE_FROM_OR);
} elseif ($expr instanceof Node\Expr\BooleanNot) {
if ($source === self::SOURCE_FROM_AND) {
return $types;
}
$types = $this->specifyTypesInCondition($types, $scope, $expr->expr, !$negated, $source);
}
return $types;
}