Skip to content
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

phpstan-assert with recursive count() results in type loss #2812

Merged
merged 1 commit into from
Jun 4, 2024

Conversation

staabm
Copy link
Contributor

@staabm staabm commented Dec 8, 2023

simliar to #2811

@staabm staabm marked this pull request as ready for review December 8, 2023 14:42
@phpstan-bot
Copy link
Collaborator

This pull request has been marked as ready for review.

@ondrejmirtes
Copy link
Member

There's a conflict, please rebase :)

Comment on lines 37 to 38
} elseif (count($items, COUNT_RECURSIVE) === 5) {
assertType('array{int, int, int, int, int}', $items);
Copy link
Contributor Author

@staabm staabm Dec 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm.. I copied this over from the above tests, but I am not sure whether thats correct in the recursive case..

Copy link
Contributor Author

@staabm staabm Dec 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aahh since the type of the list is int its correct (count cannot find more elements by recursing).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah but it's not going to be correct if one of the elements is an array in which case this assumption should not proceed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you think its worth checking for the array case, or is c5beb9c good enough?

Copy link
Contributor Author

@staabm staabm Dec 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of adding inference of the count("non-recursive-type", COUNT_RECURSIVE)-case I think it would make more sense to create a new rule which errors when COUNT_RECURSIVE is used on a type which is not recursive?

@staabm staabm marked this pull request as draft December 8, 2023 15:27
function recursiveCount(array $items):void {
assertType('list<array<int>|int>', $items);
if (count($items, COUNT_RECURSIVE) === 3) {
assertType('non-empty-list<array<int>|int>', $items);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can't judge for the constant array case when recursive - therefore separate expectations here

@staabm staabm marked this pull request as ready for review December 8, 2023 16:47
@phpstan-bot
Copy link
Collaborator

This pull request has been marked as ready for review.

$isNormalCount = (new ConstantIntegerType(COUNT_NORMAL))->isSuperTypeOf($mode)->yes();

if (!$isNormalCount) {
$isNormalCount = $argType->getIterableValueType()->isScalar()->yes();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is too restrictive. For example objects cannot be counted recursively, not even countables: https://3v4l.org/3GCpt

So you need to ask isArray and work with uncertainty - TrinaryLogic. Think about the result when it's definitely not an array, when it can be an array and when it's definitely an array.

$isNormalCount = (new ConstantIntegerType(COUNT_NORMAL))->isSuperTypeOf($mode)->yes();

if (!$isNormalCount) {
$isNormalCount = $argType->getIterableValueType()->isArray()->no();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this still misses some scenarios.

We should have different results for $isNormalCount yes/maybe/no.
And also different results for $argType->getIterableValueType()->isArray() yes/maybe/no.

If you create a matrix:

no + no
no + maybe
no + yes
maybe + no
maybe + maybe
maybe + yes
yes + no
yes + maybe
yes + yes

You'll see that some scenarios aren't covered correctly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, I added tests covering this testmatrix in

tests/PHPStan/Analyser/data/count-maybe.php

function doBar3(float $notCountable, float $invalidMode): void
{
if (count($notCountable, $invalidMode) > 0) {
assertType('float', $notCountable);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we resolve to a *ERROR* on invalid mode? I was not sure, as we already have a error at the count level because of the invalid argument type...?

@staabm staabm force-pushed the count-fix branch 2 times, most recently from de4d77b to f831bf4 Compare May 30, 2024 15:15
@staabm staabm marked this pull request as draft May 30, 2024 15:16
@staabm staabm changed the base branch from 1.10.x to 1.11.x May 31, 2024 05:38
@staabm staabm marked this pull request as ready for review May 31, 2024 05:46
@phpstan-bot
Copy link
Collaborator

This pull request has been marked as ready for review.

@staabm
Copy link
Contributor Author

staabm commented May 31, 2024

squashed, rebased against 1.11 and re-targetted the PR. not sure why CI thinks there is a merge commit contained

Infer constant array type from specifying recursive count() on a list type

fix inference with smaller/smaller equal on recursive count()

add another test

fix

fix

more tests

more tests

support recursive count on non-recursive element

count recursive based on array type

more tests

fix

test lists

fix built

support countables

more assertions

cs
@ondrejmirtes ondrejmirtes merged commit dc84e97 into phpstan:1.11.x Jun 4, 2024
444 of 446 checks passed
@ondrejmirtes
Copy link
Member

Thank you.

@staabm staabm deleted the count-fix branch June 4, 2024 07:20
@thg2k
Copy link
Contributor

thg2k commented Jun 9, 2024

@staabm @ondrejmirtes I don't know if you are aware of this, but this exact commit also fixes this bug:

/** @param array<mixed>|false $a */
function foo($a): void
{
  while (count($a) > 0) {
    array_shift($a);
  }
}

Before this commit:
NO ERROR (wrong)

After this commit:
Parameter #1 $value of function count expects array|Countable, array|false given.

I thought it was worth bringing this up in case it's a side effect. Maybe we should add it as a test case?

@staabm
Copy link
Contributor Author

staabm commented Jun 10, 2024

I will have a look. Thanks for noticing

@staabm staabm mentioned this pull request Jun 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants