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
Fix constant-string handling in union-types #2134
Conversation
src/Type/UnionTypeHelper.php
Outdated
@@ -70,6 +70,10 @@ public static function getConstantStrings(array $types): array | |||
$strings[] = $type->getConstantStrings(); | |||
} | |||
|
|||
if ($strings === []) { |
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.
php <= 7.3 doesn't like array_merge with an variadic empty array
7) PHPStan\Rules\Properties\TypesAssignedToPropertiesRuleTest::testTypesAssignedToStaticPropertiesExpressionNames
ArgumentCountError: array_merge() expects at least 1 parameter, 0 given
37482dd
to
f49a35f
Compare
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 show and fix the problems with the other methods as well. It's also about getArrays and getConstantArrays. Feel free to write unit tests.
- Do no longer call UnionTypeHelper at all. Obviously the implementation has to be different for UnionType and IntersectionType, this just complicates and obfuscates things.
- Please also verify these methods work correctly in IntersectionType. As I said - do not call UnionTypeHelper in UnionType/IntersectionType for getArrays, getConstantArrays, getConstantStrings at all.
d1958cd
to
62f2afa
Compare
src/Type/UnionType.php
Outdated
return UnionTypeHelper::getConstantArrays($this->getTypes()); | ||
$constantArrays = []; | ||
foreach ($this->getTypesOfClass(ConstantArrayType::class) as $type) { | ||
$constantArrays[] = $type->getConstantArrays(); |
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.
This logic is wrong. For example if I have (array{1?: int, 2?: string}&non-empty-array)|(array{Foo, Bar}
, these two arrays should still be returned.
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.
But if I have array{1?: int, 2?: string}|int
, this method should return an empty array.
Basically - the function should return something if all the types in the union return a non-empty array from getConstantArrays
. If one of them returns an empty array, an empty array should be returned. The getTypesOfClass
method is backwards - it returns doing instanceof *Type
which is something that no longer works.
src/Type/UnionType.php
Outdated
} | ||
|
||
public function getConstantStrings(): array | ||
{ | ||
return UnionTypeHelper::getConstantStrings($this->getTypes()); | ||
$strings = []; | ||
foreach ($this->getTypesOfClass(ConstantStringType::class) as $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.
Same logic as with getConstantArrays.
You could test this with:
''|numeric-string
- should return empty array- Create a fake denormalized type and test that
getConstantStrings
returns an array of 'foo' and 'bar':
new UnionType([
new IntersectionType([new ConstantStringType('foo'), new AccessoryLiteralStringType()]),
new IntersectionType([new ConstantStringType('bar'), new AccessoryLiteralStringType()]),
])
(In the future there could be a valid intersection like this - for example if we add nominal types and people are able to define new types like email-string
, we could have 'ondrej@mirtes.cz'&email-string
.
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.
thx for the detailled feedback. I couldn't have come up with such tests on my own.
95ff433
to
434e55d
Compare
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.
Otherwise 👍
29273e7
to
31269b3
Compare
3070b29
to
918ff36
Compare
Pushed some fixes. There's one remaining failing test (
So it seems like the test case is very brittle. Can you please investigate what types are involved on top of 1.9.x (so that there are no errors) and what types are involved in your PR, so that we can decide what should be done there? Thanks. |
918ff36
to
7d779e9
Compare
@@ -4200,15 +4200,15 @@ private function generalizeVariableTypeHolders( | |||
|
|||
$variableTypeHolders[$variableExprString] = new ExpressionTypeHolder( | |||
$variableTypeHolder->getExpr(), | |||
self::generalizeType($variableTypeHolder->getType(), $otherVariableTypeHolders[$variableExprString]->getType()), | |||
self::generalizeType($variableTypeHolder->getType(), $otherVariableTypeHolders[$variableExprString]->getType(), 0), |
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.
I printed the types after generalization at this line in this PR vs. 1.9.x-dev at 9128321
most types are identical. the only different are:
- string(71) "non-empty-array<literal-string&non-falsy-string, mixed>&oversized-array"
+ string(206) "non-empty-array<literal-string&non-falsy-string, non-empty-array<int|string, 0|(non-empty-array<int|string, 0|(non-empty-array<int|string, *ERROR*>&oversized-array)|float>&oversized-array)>>&oversized-array"
so after this PR we no longer get MixedType and therefore we get the errors
$type = $temp; | ||
} | ||
$type = $temp; | ||
$arrays = TypeUtils::getAnyArrays($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.
I think our problem is that getAnyArrays
does not return arrays for the types in question because we are working with a union of intersection types
and TypeUtils::getAnyArrays
does not recursivly check the intersections within unions
This pull request has been marked as ready for review. |
66f63d3
to
58d0472
Compare
Thank you. |
yeah, thx for fixing this! :) |
closes phpstan/phpstan#8568
closes phpstan/phpstan#8562