Skip to content

Commit

Permalink
Fix phpstan removing null on iterable value (#58)
Browse files Browse the repository at this point in the history
  • Loading branch information
bronek89 authored Jun 6, 2022
1 parent 1d94ad6 commit d6988a0
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 7 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ jobs:
run: ./bin/phpspec run

- name: "PHPStan"
run: ./bin/phpstan analyze -l 8 src examples tests/GenericCases
run: ./bin/phpstan analyze -l 8 src

- name: "PHPStan"
run: ./bin/phpstan analyze -l 9 tests/GenericCases examples

- name: "PHPUnit"
run: ./bin/phpunit tests
Expand Down
5 changes: 4 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@
}
},
"config": {
"bin-dir": "bin"
"bin-dir": "bin",
"allow-plugins": {
"phpstan/extension-installer": true
}
},
"extra": {
"phpstan": {
Expand Down
15 changes: 13 additions & 2 deletions src/PHPStan/IterableValueNotEmptyExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\DynamicMethodReturnTypeExtension;
use PHPStan\Type\Generic\GenericObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;

final class IterableValueNotEmptyExtension implements DynamicMethodReturnTypeExtension
{
use NotEmptyTypeRemover;

public function getClass(): string
{
return IterableValue::class;
Expand All @@ -30,4 +30,15 @@ public function getTypeFromMethodCall(
): Type {
return $this->removeNull($scope->getType($methodCall->var));
}

private function removeNull(Type $type): Type
{
if (!$type instanceof GenericObjectType) {
return $type;
}

$types = $type->getTypes();

return new GenericObjectType($type->getClassName(), [$types[0], TypeCombinator::removeNull($types[1])]);
}
}
3 changes: 1 addition & 2 deletions src/Stringable/ToStringValue.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@

final class ToStringValue
{
/** @param mixed $string */
public function __invoke($string): StringValue
public function __invoke(mixed $string): StringValue
{
if (is_scalar($string)) {
return Wrap::string((string)$string);
Expand Down
2 changes: 1 addition & 1 deletion tests/GenericCases/NotEmpty.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ function iterableValue3(): iterable
->notEmpty();
}

/** @return iterable<int, object[][]> */
/** @return iterable<int, object[]> */
function iterableValue4(): iterable
{
$numbersOrNulls = Wrap::iterable(['a' => 1, 'b' => 2, 'c' => 3, 'd' => null]);
Expand Down
34 changes: 34 additions & 0 deletions tests/GenericCases/NotEmptyIterable.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

namespace tests\GW\Value\GenericCases;

use GW\Value\IterableValue;
use GW\Value\Wrap;
use function random_int;

class Id {}

Expand Down Expand Up @@ -39,3 +41,35 @@ function iterableValue5(): void

iterate($chunks);
}

class RequireIterable
{
/** @param iterable<int> $it */
public function functionRequireIterable(iterable $it): void
{
}
/** @param iterable<int,int> $it */
public function functionRequireIterable2(iterable $it): void
{
}
/** @param IterableValue<int,int> $it */
public function functionRequireIterable3(IterableValue $it): void
{
}
/** @param array<int,Foo> $it */
public function functionRequireArray(array $it): void
{
}
}

$ri = new RequireIterable();
$ri->functionRequireIterable(Wrap::iterable([null, 1])->filterEmpty());
$ri->functionRequireIterable2(Wrap::iterable([null, 1])->filterEmpty());
$ri->functionRequireIterable3(Wrap::iterable([null, 1])->filterEmpty());

$ri->functionRequireIterable(Wrap::iterable([2, 1, 3])->map(fn(int $x): ?int => random_int(0, 2) ?: null)->filterEmpty());
$ri->functionRequireIterable2(Wrap::iterable([null, 1])->filterEmpty());
$ri->functionRequireIterable3(Wrap::iterable([null, 1])->filterEmpty());

$ia = Wrap::iterable([2, 1, 3])->map(fn(int $x): ?Foo => random_int(0, 2) ? new Foo() : null)->filterEmpty()->toArray();
$ri->functionRequireArray($ia);

0 comments on commit d6988a0

Please sign in to comment.