From f5103aaa9ebe050689b7c4f7870563bb2de33f92 Mon Sep 17 00:00:00 2001 From: Can Vural Date: Thu, 29 Dec 2022 11:58:10 +0100 Subject: [PATCH] fix: keep original type in collect function in some cases (#1495) --- src/ReturnTypes/Helpers/CollectExtension.php | 2 +- src/Support/CollectionHelper.php | 28 ++++++++----------- .../collection-generic-static-methods.php | 2 +- tests/Type/data/collection-helper.php | 8 ++++-- 4 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/ReturnTypes/Helpers/CollectExtension.php b/src/ReturnTypes/Helpers/CollectExtension.php index c9d3567e8..8ea4594ba 100644 --- a/src/ReturnTypes/Helpers/CollectExtension.php +++ b/src/ReturnTypes/Helpers/CollectExtension.php @@ -38,7 +38,7 @@ public function getTypeFromFunctionCall( FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope - ): Type { + ): ?Type { if (count($functionCall->getArgs()) < 1) { return new GenericObjectType(Collection::class, [new BenevolentUnionType([new IntegerType(), new StringType()]), new MixedType()]); } diff --git a/src/Support/CollectionHelper.php b/src/Support/CollectionHelper.php index 0b09f8c93..1c20e6ab0 100644 --- a/src/Support/CollectionHelper.php +++ b/src/Support/CollectionHelper.php @@ -9,23 +9,20 @@ use Illuminate\Support\Enumerable; use Iterator; use IteratorAggregate; -use PHPStan\Type\GeneralizePrecision; +use PHPStan\Type\BenevolentUnionType; use PHPStan\Type\Generic\GenericObjectType; use PHPStan\Type\IntegerType; use PHPStan\Type\MixedType; use PHPStan\Type\ObjectType; use PHPStan\Type\StringType; use PHPStan\Type\Type; -use PHPStan\Type\TypeCombinator; use PHPStan\Type\TypeWithClassName; use Traversable; final class CollectionHelper { - public function determineGenericCollectionTypeFromType(Type $type): GenericObjectType + public function determineGenericCollectionTypeFromType(Type $type): ?GenericObjectType { - $keyType = TypeCombinator::union(new IntegerType(), new StringType()); - if ($type instanceof TypeWithClassName) { if ((new ObjectType(Enumerable::class))->isSuperTypeOf($type)->yes()) { return $this->getTypeFromEloquentCollection($type); @@ -45,23 +42,20 @@ public function determineGenericCollectionTypeFromType(Type $type): GenericObjec } if ($type->isIterableAtLeastOnce()->no()) { - return new GenericObjectType(Collection::class, [$keyType, new MixedType()]); + return new GenericObjectType(Collection::class, [new BenevolentUnionType([new IntegerType(), new StringType()]), new MixedType()]); } - return new GenericObjectType(Collection::class, [ - $type->getIterableKeyType()->generalize(GeneralizePrecision::lessSpecific()), - $type->getIterableValueType()->generalize(GeneralizePrecision::lessSpecific()), - ]); + return null; } - private function getTypeFromEloquentCollection(TypeWithClassName $valueType): GenericObjectType + private function getTypeFromEloquentCollection(TypeWithClassName $valueType): ?GenericObjectType { - $keyType = TypeCombinator::union(new IntegerType(), new StringType()); + $keyType = new BenevolentUnionType([new IntegerType(), new StringType()]); $classReflection = $valueType->getClassReflection(); if ($classReflection === null) { - return new GenericObjectType(Collection::class, [$keyType, new MixedType()]); + return null; } $innerValueType = $classReflection->getActiveTemplateTypeMap()->getType('TModel'); @@ -74,17 +68,17 @@ private function getTypeFromEloquentCollection(TypeWithClassName $valueType): Ge return new GenericObjectType(Collection::class, [$keyType, $innerValueType]); } - return new GenericObjectType(Collection::class, [$keyType, new MixedType()]); + return null; } - private function getTypeFromIterator(TypeWithClassName $valueType): GenericObjectType + private function getTypeFromIterator(TypeWithClassName $valueType): ?GenericObjectType { - $keyType = TypeCombinator::union(new IntegerType(), new StringType()); + $keyType = new BenevolentUnionType([new IntegerType(), new StringType()]); $classReflection = $valueType->getClassReflection(); if ($classReflection === null) { - return new GenericObjectType(Collection::class, [$keyType, new MixedType()]); + return null; } $templateTypes = array_values($classReflection->getActiveTemplateTypeMap()->getTypes()); diff --git a/tests/Type/data/collection-generic-static-methods.php b/tests/Type/data/collection-generic-static-methods.php index 033ebd05c..dbe13d6ba 100644 --- a/tests/Type/data/collection-generic-static-methods.php +++ b/tests/Type/data/collection-generic-static-methods.php @@ -174,7 +174,7 @@ }) ); -assertType('Illuminate\Support\Collection<(int|string), Illuminate\Support\Collection<(int|string), non-empty-array>>', collect([ +assertType('Illuminate\Support\Collection<(int|string), Illuminate\Support\Collection<(int|string), array{id: int, type: string}>>', collect([ [ 'id' => 1, 'type' => 'A', diff --git a/tests/Type/data/collection-helper.php b/tests/Type/data/collection-helper.php index 561946159..630393c4c 100644 --- a/tests/Type/data/collection-helper.php +++ b/tests/Type/data/collection-helper.php @@ -12,13 +12,15 @@ assertType('Illuminate\Support\Collection<0, \'foo\'>', collect('foo')); assertType('Illuminate\Support\Collection<0, 3.14>', collect(3.14)); assertType('Illuminate\Support\Collection<0, true>', collect(true)); -assertType('Illuminate\Support\Collection', collect([])); +assertType('Illuminate\Support\Collection<(int|string), mixed>', collect([])); assertType('Illuminate\Support\Collection', collect([1, 2, 3])); assertType('Illuminate\Support\Collection', collect(['foo', 'bar', 'baz'])); assertType('Illuminate\Support\Collection', collect([1.0, 2.0, 3.0])); assertType('Illuminate\Support\Collection', collect([1, 'foo', 1.0])); -assertType("Illuminate\Support\Collection>", collect([['a', 'b', 'c']])); -assertType("Illuminate\Support\Collection>", collect([['a', 'b', 'c']])->push(array_fill(0, 3, 'x'))); +assertType("Illuminate\Support\Collection", collect([['a', 'b', 'c']])); +assertType("Illuminate\Support\Collection", collect([['a', 'b', 'c']])->push(array_fill(0, 3, 'x'))); +assertType("Illuminate\Support\Collection", collect([new User, new User])); +assertType("Illuminate\Support\Collection", collect([[new User, new User, new User]])); /** @phpstan-param EloquentCollection $eloquentCollection */ function eloquentCollectionInteger(EloquentCollection $eloquentCollection): void