Skip to content

Commit

Permalink
Split getSmallerType()/getGreaterType() into two methods each
Browse files Browse the repository at this point in the history
  • Loading branch information
jlherren authored and ondrejmirtes committed Jan 4, 2021
1 parent b7bf0c9 commit 377e49c
Show file tree
Hide file tree
Showing 10 changed files with 274 additions and 108 deletions.
32 changes: 28 additions & 4 deletions src/Analyser/TypeSpecifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -458,17 +458,41 @@ public function specifyTypesInCondition(

if ($context->truthy()) {
if (!$expr->left instanceof Node\Scalar) {
$result = $result->unionWith($this->create($expr->left, $rightType->getSmallerType($orEqual), TypeSpecifierContext::createTruthy()));
$result = $result->unionWith(
$this->create(
$expr->left,
$orEqual ? $rightType->getSmallerOrEqualType() : $rightType->getSmallerType(),
TypeSpecifierContext::createTruthy()
)
);
}
if (!$expr->right instanceof Node\Scalar) {
$result = $result->unionWith($this->create($expr->right, $leftType->getGreaterType($orEqual), TypeSpecifierContext::createTruthy()));
$result = $result->unionWith(
$this->create(
$expr->right,
$orEqual ? $leftType->getGreaterOrEqualType() : $leftType->getGreaterType(),
TypeSpecifierContext::createTruthy()
)
);
}
} elseif ($context->falsey()) {
if (!$expr->left instanceof Node\Scalar) {
$result = $result->unionWith($this->create($expr->left, $rightType->getGreaterType(!$orEqual), TypeSpecifierContext::createTruthy()));
$result = $result->unionWith(
$this->create(
$expr->left,
$orEqual ? $rightType->getGreaterType() : $rightType->getGreaterOrEqualType(),
TypeSpecifierContext::createTruthy()
)
);
}
if (!$expr->right instanceof Node\Scalar) {
$result = $result->unionWith($this->create($expr->right, $leftType->getSmallerType(!$orEqual), TypeSpecifierContext::createTruthy()));
$result = $result->unionWith(
$this->create(
$expr->right,
$orEqual ? $leftType->getSmallerType() : $leftType->getSmallerOrEqualType(),
TypeSpecifierContext::createTruthy()
)
);
}
}

Expand Down
30 changes: 15 additions & 15 deletions src/Type/Constant/ConstantBooleanType.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,38 +33,38 @@ public function describe(VerbosityLevel $level): string
return $this->value ? 'true' : 'false';
}

public function getSmallerType(bool $orEqual = false): Type
public function getSmallerType(): Type
{
if ($orEqual) {
if ($this->value) {
return new MixedType();
}
return StaticTypeFactory::falsey();
}

if ($this->value) {
return StaticTypeFactory::falsey();
}

return new NeverType();
}

public function getGreaterType(bool $orEqual = false): Type
public function getSmallerOrEqualType(): Type
{
if ($orEqual) {
if ($this->value) {
return StaticTypeFactory::truthy();
}
if ($this->value) {
return new MixedType();
}
return StaticTypeFactory::falsey();
}

public function getGreaterType(): Type
{
if ($this->value) {
return new NeverType();
}

return StaticTypeFactory::truthy();
}

public function getGreaterOrEqualType(): Type
{
if ($this->value) {
return StaticTypeFactory::truthy();
}
return new MixedType();
}

public function toBoolean(): BooleanType
{
return $this;
Expand Down
46 changes: 33 additions & 13 deletions src/Type/Constant/ConstantStringType.php
Original file line number Diff line number Diff line change
Expand Up @@ -302,45 +302,65 @@ public function generalize(): Type
return new StringType();
}
public function getSmallerType(bool $orEqual = false): Type
public function getSmallerType(): Type
{
$subtractedTypes = [
IntegerRangeType::createAllGreaterThan((float) $this->value, !$orEqual),
new ConstantBooleanType(true),
IntegerRangeType::createAllGreaterThanOrEqualTo((float) $this->value),
];
if ($this->value === '' && !$orEqual) {
if ($this->value === '') {
$subtractedTypes[] = new NullType();
$subtractedTypes[] = new StringType();
}

$boolValue = (bool) $this->value;
if (!$boolValue && !$orEqual) {
if (!(bool) $this->value) {
$subtractedTypes[] = new ConstantBooleanType(false);
}
if (!$boolValue || !$orEqual) {

return TypeCombinator::remove(new MixedType(), TypeCombinator::union(...$subtractedTypes));
}

public function getSmallerOrEqualType(): Type
{
$subtractedTypes = [
IntegerRangeType::createAllGreaterThan((float) $this->value),
];

if (!(bool) $this->value) {
$subtractedTypes[] = new ConstantBooleanType(true);
}

return TypeCombinator::remove(new MixedType(), TypeCombinator::union(...$subtractedTypes));
}

public function getGreaterType(bool $orEqual = false): Type
public function getGreaterType(): Type
{
$subtractedTypes = [
IntegerRangeType::createAllSmallerThan((float) $this->value, !$orEqual),
new ConstantBooleanType(false),
IntegerRangeType::createAllSmallerThanOrEqualTo((float) $this->value),
];

$boolValue = (bool) $this->value;
if ($boolValue || !$orEqual) {
$subtractedTypes[] = new ConstantBooleanType(false);
}
if ($boolValue && !$orEqual) {
if ((bool) $this->value) {
$subtractedTypes[] = new ConstantBooleanType(true);
}

return TypeCombinator::remove(new MixedType(), TypeCombinator::union(...$subtractedTypes));
}

public function getGreaterOrEqualType(): Type
{
$subtractedTypes = [
IntegerRangeType::createAllSmallerThan((float) $this->value),
];

if ((bool) $this->value) {
$subtractedTypes[] = new ConstantBooleanType(false);
}

return TypeCombinator::remove(new MixedType(), TypeCombinator::union(...$subtractedTypes));
}

/**
* @param mixed[] $properties
* @return Type
Expand Down
117 changes: 87 additions & 30 deletions src/Type/IntegerRangeType.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,61 +48,97 @@ protected static function isDisjoint(?int $minA, ?int $maxA, ?int $minB, ?int $m
}

/**
* Return the range of integers smaller than (or equal to) the given value
* Return the range of integers smaller than the given value
*
* @param int|float $value
* @param bool $orEqual
* @return Type
*/
public static function createAllSmallerThan($value, bool $orEqual = false): Type
public static function createAllSmallerThan($value): Type
{
if (is_int($value)) {
return self::fromInterval(null, $value, $orEqual ? 0 : -1);
return self::fromInterval(null, $value, -1);
}

if ($value > PHP_INT_MAX || $value >= PHP_INT_MAX && $orEqual) {
if ($value > PHP_INT_MAX) {
return new IntegerType();
}

if ($value < PHP_INT_MIN || $value <= PHP_INT_MIN && !$orEqual) {
if ($value <= PHP_INT_MIN) {
return new NeverType();
}

if ($orEqual) {
return self::fromInterval(null, (int) floor($value));
}

return self::fromInterval(null, (int) ceil($value), -1);
}

/**
* Return the range of integers greater than (or equal to) the given value
* Return the range of integers smaller than or equal to the given value
*
* @param int|float $value
* @param bool $orEqual
* @return Type
*/
public static function createAllGreaterThan($value, bool $orEqual = false): Type
public static function createAllSmallerThanOrEqualTo($value): Type
{
if (is_int($value)) {
return self::fromInterval($value, null, $orEqual ? 0 : 1);
return self::fromInterval(null, $value);
}

if ($value < PHP_INT_MIN || $value <= PHP_INT_MIN && $orEqual) {
if ($value >= PHP_INT_MAX) {
return new IntegerType();
}

if ($value > PHP_INT_MAX || $value >= PHP_INT_MAX && !$orEqual) {
if ($value < PHP_INT_MIN) {
return new NeverType();
}

if ($orEqual) {
return self::fromInterval((int) ceil($value), null);
return self::fromInterval(null, (int) floor($value));
}

/**
* Return the range of integers greater than the given value
*
* @param int|float $value
* @return Type
*/
public static function createAllGreaterThan($value): Type
{
if (is_int($value)) {
return self::fromInterval($value, null, 1);
}

if ($value < PHP_INT_MIN) {
return new IntegerType();
}

if ($value >= PHP_INT_MAX) {
return new NeverType();
}

return self::fromInterval((int) floor($value), null, 1);
}

/**
* Return the range of integers greater than or equal to the given value
*
* @param int|float $value
* @return Type
*/
public static function createAllGreaterThanOrEqualTo($value): Type
{
if (is_int($value)) {
return self::fromInterval($value, null);
}

if ($value <= PHP_INT_MIN) {
return new IntegerType();
}

if ($value > PHP_INT_MAX) {
return new NeverType();
}

return self::fromInterval((int) ceil($value), null);
}

public function getMin(): ?int
{
return $this->min;
Expand Down Expand Up @@ -269,40 +305,61 @@ public function isGreaterThan(Type $otherType, bool $orEqual = false): TrinaryLo
return TrinaryLogic::extremeIdentity($minIsSmaller, $maxIsSmaller);
}

public function getSmallerType(bool $orEqual = false): Type
public function getSmallerType(): Type
{
$subtractedTypes = [
new ConstantBooleanType(true),
];

if ($this->max !== null) {
$subtractedTypes[] = self::createAllGreaterThanOrEqualTo($this->max);
}

return TypeCombinator::remove(new MixedType(), TypeCombinator::union(...$subtractedTypes));
}

public function getSmallerOrEqualType(): Type
{
$subtractedTypes = [];

if ($this->max !== null) {
$subtractedTypes[] = self::createAllGreaterThan($this->max, !$orEqual);
$subtractedTypes[] = self::createAllGreaterThan($this->max);
}

if (!$orEqual) {
return TypeCombinator::remove(new MixedType(), TypeCombinator::union(...$subtractedTypes));
}

public function getGreaterType(): Type
{
$subtractedTypes = [
new NullType(),
new ConstantBooleanType(false),
];

if ($this->min !== null) {
$subtractedTypes[] = self::createAllSmallerThanOrEqualTo($this->min);
}

if ($this->min !== null && $this->min > 0 || $this->max !== null && $this->max < 0) {
$subtractedTypes[] = new ConstantBooleanType(true);
}

return TypeCombinator::remove(new MixedType(), TypeCombinator::union(...$subtractedTypes));
}

public function getGreaterType(bool $orEqual = false): Type
public function getGreaterOrEqualType(): Type
{
$subtractedTypes = [];

if ($this->min !== null) {
$subtractedTypes[] = self::createAllSmallerThan($this->min, !$orEqual);
$subtractedTypes[] = self::createAllSmallerThan($this->min);
}

$alwaysTruthy = $this->min !== null && $this->min > 0 || $this->max !== null && $this->max < 0;

if ($alwaysTruthy || !$orEqual) {
if ($this->min !== null && $this->min > 0 || $this->max !== null && $this->max < 0) {
$subtractedTypes[] = new NullType();
$subtractedTypes[] = new ConstantBooleanType(false);
}

if ($alwaysTruthy && !$orEqual) {
$subtractedTypes[] = new ConstantBooleanType(true);
}

return TypeCombinator::remove(new MixedType(), TypeCombinator::union(...$subtractedTypes));
}

Expand Down
Loading

0 comments on commit 377e49c

Please sign in to comment.