diff --git a/composer.json b/composer.json index 2582bd0..341cc7c 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ "phpunit/phpunit": "^9.4.2", "slevomat/coding-standard": "^6.4.1", "squizlabs/php_codesniffer": "^3.5.0", - "bonami/collections": "^0.3" + "bonami/collections": "dev-jm-phpstan-1.0" }, "config": { "bin-dir": "bin" diff --git a/extension.neon b/extension.neon index 50c9956..8b236ae 100644 --- a/extension.neon +++ b/extension.neon @@ -1,4 +1,8 @@ services: + - + class: Bonami\Collection\Phpstan\ArrayListWithoutNullsReturnTypeExtension + tags: + - phpstan.broker.dynamicMethodReturnTypeExtension - class: Bonami\Collection\Phpstan\LateStaticBindingStaticMethodReturnTypeExtension factory: Bonami\Collection\Phpstan\LateStaticBindingStaticMethodReturnTypeExtension::forMethods('Bonami\Collection\ArrayList', ['fromEmpty', 'of', 'fill', 'fromIterable']) @@ -21,7 +25,7 @@ services: - phpstan.broker.dynamicMethodReturnTypeExtension - class: Bonami\Collection\Phpstan\LateStaticBindingStaticMethodReturnTypeExtension - factory: Bonami\Collection\Phpstan\LateStaticBindingStaticMethodReturnTypeExtension::forMethods('Bonami\Collection\LazyList', ['fill', 'fromEmpty', 'fromArray', 'fromTraversable', 'fromIterable', 'of']) + factory: Bonami\Collection\Phpstan\LateStaticBindingStaticMethodReturnTypeExtension::forMethods('Bonami\Collection\LazyList', ['fill', 'fromEmpty', 'fromArray', 'fromIterable', 'of']) tags: - phpstan.broker.dynamicStaticMethodReturnTypeExtension - diff --git a/phpstan.neon b/phpstan.neon index 43916cf..9cc4f44 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,9 +1,8 @@ includes: - extension.neon parameters: - inferPrivatePropertyTypeFromConstructor: true reportUnmatchedIgnoredErrors: true - level: 8 + level: 9 paths: - src/ - tests/ diff --git a/src/Bonami/Collection/Phpstan/ArrayListWithoutNullsReturnTypeExtension.php b/src/Bonami/Collection/Phpstan/ArrayListWithoutNullsReturnTypeExtension.php new file mode 100644 index 0000000..ebee986 --- /dev/null +++ b/src/Bonami/Collection/Phpstan/ArrayListWithoutNullsReturnTypeExtension.php @@ -0,0 +1,54 @@ +getName() === 'withoutNulls'; + } + + public function getTypeFromMethodCall( + MethodReflection $methodReflection, + MethodCall $methodCall, + Scope $scope + ): Type { + $type = $scope->getType($methodCall->var); + + $declaringClassReflection = $methodReflection->getDeclaringClass(); + $referencedClasses = $type->getReferencedClasses(); + + if (in_array(ArrayList::class, $referencedClasses, true)) { + $types = $declaringClassReflection->typeMapToList( + $declaringClassReflection->getTemplateTypeMap()->resolveToBounds() + ); + + return new GenericObjectType( + $declaringClassReflection->getName(), + array_map(static function (Type $type) { + return TypeCombinator::removeNull($type); + }, $types) + ); + } + + return $type; + } +} diff --git a/src/Bonami/Collection/Phpstan/GroupByMethodReturnTypeExtension.php b/src/Bonami/Collection/Phpstan/GroupByMethodReturnTypeExtension.php index 20bdae1..913e840 100644 --- a/src/Bonami/Collection/Phpstan/GroupByMethodReturnTypeExtension.php +++ b/src/Bonami/Collection/Phpstan/GroupByMethodReturnTypeExtension.php @@ -18,6 +18,7 @@ class GroupByMethodReturnTypeExtension implements DynamicMethodReturnTypeExtension { + /** @var string */ private $class; public function __construct(string $class) diff --git a/src/Bonami/Collection/Phpstan/LateStaticBindingMethodReturnTypeExtension.php b/src/Bonami/Collection/Phpstan/LateStaticBindingMethodReturnTypeExtension.php index 98b2903..3162ce4 100644 --- a/src/Bonami/Collection/Phpstan/LateStaticBindingMethodReturnTypeExtension.php +++ b/src/Bonami/Collection/Phpstan/LateStaticBindingMethodReturnTypeExtension.php @@ -12,6 +12,7 @@ class LateStaticBindingMethodReturnTypeExtension implements DynamicMethodReturnTypeExtension { + /** @var string */ private $class; /** @var array */ diff --git a/src/Bonami/Collection/Phpstan/LateStaticBindingStaticMethodReturnTypeExtension.php b/src/Bonami/Collection/Phpstan/LateStaticBindingStaticMethodReturnTypeExtension.php index 5c77507..de94871 100644 --- a/src/Bonami/Collection/Phpstan/LateStaticBindingStaticMethodReturnTypeExtension.php +++ b/src/Bonami/Collection/Phpstan/LateStaticBindingStaticMethodReturnTypeExtension.php @@ -15,6 +15,7 @@ class LateStaticBindingStaticMethodReturnTypeExtension implements DynamicStaticMethodReturnTypeExtension { + /** @var string */ private $class; /** @var array */ diff --git a/tests/Bonami/Collection/Phpstan/ArrayListTest.php b/tests/Bonami/Collection/Phpstan/ArrayListTest.php index b8ab0ed..da05b87 100644 --- a/tests/Bonami/Collection/Phpstan/ArrayListTest.php +++ b/tests/Bonami/Collection/Phpstan/ArrayListTest.php @@ -143,11 +143,15 @@ public function testSliceReturnType(): void public function testWithoutNullsReturnType(): void { - $genericList = ArrayList::fromIterable([new Foo()])->withoutNulls(); + /** @var iterable $iterable */ + $iterable = [new Foo(), null]; + /** @var ArrayList $list */ + $list = ArrayList::fromIterable($iterable); + $genericList = $list->withoutNulls(); $this->requireArrayListOfFoo($genericList->withoutNulls()); self::assertInstanceOf(ArrayList::class, $genericList); - $concreteList = FooArrayList::fromIterable([new Foo()])->withoutNulls(); + $concreteList = FooArrayList::fromIterable($iterable)->withoutNulls(); $this->requireFooList($concreteList->withoutNulls()); self::assertInstanceOf(FooArrayList::class, $concreteList); } diff --git a/tests/Bonami/Collection/Phpstan/LazyListTest.php b/tests/Bonami/Collection/Phpstan/LazyListTest.php index 16bfe8f..0e34a83 100644 --- a/tests/Bonami/Collection/Phpstan/LazyListTest.php +++ b/tests/Bonami/Collection/Phpstan/LazyListTest.php @@ -44,17 +44,6 @@ public function testFromArrayReturnType(): void self::assertInstanceOf(FooLazyList::class, $concreteList); } - public function testFromTraversableReturnType(): void - { - $genericList = LazyList::fromTraversable(new ArrayIterator([new Foo()])); - $this->requireLazyListOfFoo($genericList); - self::assertInstanceOf(LazyList::class, $genericList); - - $concreteList = FooLazyList::fromTraversable(new ArrayIterator([new Foo()])); - $this->requireFooList($concreteList); - self::assertInstanceOf(FooLazyList::class, $concreteList); - } - public function testFromIterableReturnType(): void { $genericList = LazyList::fromIterable([new Foo()]);