New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve comparison operators and fix IntegerRangeType overflowing #372
Conversation
if ($a >= $b) {} | ||
} | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, would you care to rewrite this as a new NodeScopeResolverTest::testFileAssert's dataProvider? It would be much clearer to see assertType('false', 1.9 > 2.1)
instead of jumping between this file and rule test. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/Type/TypeComparator.php
Outdated
|
||
private static function smallerImpl(Type $leftType, Type $rightType, bool $orEqual): TrinaryLogic | ||
{ | ||
if ($leftType instanceof ConstantScalarType && $rightType instanceof ConstantScalarType) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a future scope, we should probably think about rewriting this as a method on Type. That way, we could also write implementation of ==
and also nicely write implementation between different ConstantArrayTypes for example.
Remember, I want to deprecate and get rid of most instanceof *Type
. But I'll merge this PR as it is once the other comment is resolved :)
Thank you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right! Actually, I can try to have a go at this tomorrow. So it would be a new method smallerThan()
and smallerThanOrEqualTo()
method on Type
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public function smallerThan(self $otherType): TrinaryLogic;
public function smallerThanOrEqual(self $otherType): TrinaryLogic;
Most of these method implementations have a special if condition for CompoundType
which are implemented by special types like UnionType
or IntersectionType
, it might look like this:
public function accepts(Type $type, bool $strictTypes): TrinaryLogic
{
if ($type instanceof static) {
return TrinaryLogic::createYes();
}
if ($type instanceof CompoundType) {
return $type->isAcceptedBy($this, $strictTypes);
}
return TrinaryLogic::createNo();
}
public function isSuperTypeOf(Type $type): TrinaryLogic
{
if ($type instanceof self) {
return TrinaryLogic::createYes();
}
if ($type instanceof CompoundType) {
return $type->isSubTypeOf($this);
}
return TrinaryLogic::createNo();
}
It's there so that each type can only show interest in types that it should care about. All of these CompoundType
s are handled by a single if in most ordinary Type implementations which makes things easier. I don't know how to name the relevant CompoundType method, maybe isGreaterThan
, as an inversion similar to accepts
/isAcceptedBy
? :) You'll see...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see the update, I hope it is somewhat how you intended it to be. I opted to only add one method, since in almost all implementations <
and <=
can be handled with the same code. If desired I could still add isSmallerThanOrEqual()
, which would then just call isSmallerThan(..., ..., true)
.
Also I added the is
prefix to the method names (IMHO method names should always include at least one verb). This makes it also more similar to a probable future isEqualTo()
, which needs to be clearly distinguished from equals()
.
Finally, I made IntegerRangeType
implement CompoundType
, I hope that makes sense, because it really needs to handle isGreaterThan()
.
In particular, this fixes wrong comparisions like: * -1 < null being treated as always true * 1.9 > 1 being treated as always false
Hold on with this... I noticed some weirdness. The following code:
Now produces:
While it originally produces saner output: https://phpstan.org/r/fca7522c-939b-4199-83ca-5db5970f0637 This means that pre/post increment/decrement did not have proper tests :) I will investigate this and add tests. |
Okay, that's fixed. I added the tests for pre/post inc/dec and noticed that some situations weren't even handled, I added those as well. |
Thank you! I wonder if the current |
I'm not entirely sure what you mean... Possibly it's what I'm doing right now: Making My current approach is to add |
What I mean is that in theory the original code should stay in MutatingScope: if ($rightType instanceof ConstantIntegerType) {
$rightValue = $rightType->getValue();
if ($node instanceof Expr\BinaryOp\Greater) {
$rightIntervalType = IntegerRangeType::fromInterval($rightValue + 1, null);
} elseif ($node instanceof Expr\BinaryOp\GreaterOrEqual) {
$rightIntervalType = IntegerRangeType::fromInterval($rightValue, null);
} elseif ($node instanceof Expr\BinaryOp\Smaller) {
$rightIntervalType = IntegerRangeType::fromInterval(null, $rightValue - 1);
} else {
$rightIntervalType = IntegerRangeType::fromInterval(null, $rightValue);
}
return $rightIntervalType->isSuperTypeOf($leftType->toInteger())->toBooleanType();
... Instead of adding new |
But then it would not work for Given |
Of course, you're right :) |
No description provided.