diff --git a/src/Type/Accessory/NonEmptyArrayType.php b/src/Type/Accessory/NonEmptyArrayType.php index 885270d689..a379f3d842 100644 --- a/src/Type/Accessory/NonEmptyArrayType.php +++ b/src/Type/Accessory/NonEmptyArrayType.php @@ -25,7 +25,6 @@ use PHPStan\Type\Type; use PHPStan\Type\UnionType; use PHPStan\Type\VerbosityLevel; -use function sprintf; class NonEmptyArrayType implements CompoundType, AccessoryType { @@ -86,17 +85,8 @@ public function acceptsWithReason(Type $type, bool $strictTypes): AcceptsResult $isArray = $type->isArray(); $isIterableAtLeastOnce = $type->isIterableAtLeastOnce(); - $reasons = []; - if ($isArray->yes() && !$isIterableAtLeastOnce->yes()) { - $verbosity = VerbosityLevel::getRecommendedLevelByType($this, $type); - $reasons[] = sprintf( - '%s %s empty.', - $type->describe($verbosity), - $isIterableAtLeastOnce->no() ? 'is' : 'might be', - ); - } - return new AcceptsResult($isArray->and($isIterableAtLeastOnce), $reasons); + return new AcceptsResult($isArray->and($isIterableAtLeastOnce), []); } public function isSuperTypeOf(Type $type): TrinaryLogic diff --git a/src/Type/IntersectionType.php b/src/Type/IntersectionType.php index 8c923359ce..bea6393171 100644 --- a/src/Type/IntersectionType.php +++ b/src/Type/IntersectionType.php @@ -191,15 +191,27 @@ public function acceptsWithReason(Type $otherType, bool $strictTypes): AcceptsRe if (!$result->yes()) { $isList = $otherType->isList(); + $reasons = []; + $verbosity = VerbosityLevel::getRecommendedLevelByType($this, $otherType); if ($this->isList()->yes() && !$isList->yes()) { - $verbosity = VerbosityLevel::getRecommendedLevelByType($this, $otherType); - return new AcceptsResult($result->result, [ - sprintf( - '%s %s a list.', - $otherType->describe($verbosity), - $isList->no() ? 'is not' : 'might not be', - ), - ]); + $reasons[] = sprintf( + '%s %s a list.', + $otherType->describe($verbosity), + $isList->no() ? 'is not' : 'might not be', + ); + } + + $isNonEmpty = $otherType->isIterableAtLeastOnce(); + if ($this->isIterableAtLeastOnce()->yes() && !$isNonEmpty->yes()) { + $reasons[] = sprintf( + '%s %s empty.', + $otherType->describe($verbosity), + $isNonEmpty->no() ? 'is' : 'might be', + ); + } + + if (count($reasons) > 0) { + return new AcceptsResult($result->result, $reasons); } } diff --git a/tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php b/tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php index 45c44f84be..d80182cc75 100644 --- a/tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php +++ b/tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php @@ -962,6 +962,15 @@ public function testWrongListTip(): void 'Method WrongListTip\Test::doFoo() should return list but returns list.', 23, ], + [ + 'Method WrongListTip\Test2::doFoo() should return non-empty-array but returns non-empty-array.', + 44, + ], + [ + 'Method WrongListTip\Test3::doFoo() should return non-empty-list but returns array.', + 67, + "• array might not be a list.\n• array might be empty.", + ], ]); } diff --git a/tests/PHPStan/Rules/Methods/data/wrong-list-tip.php b/tests/PHPStan/Rules/Methods/data/wrong-list-tip.php index 38f198279f..827cf9738e 100644 --- a/tests/PHPStan/Rules/Methods/data/wrong-list-tip.php +++ b/tests/PHPStan/Rules/Methods/data/wrong-list-tip.php @@ -32,3 +32,47 @@ public function listOfBars(): array } } + +class Test2 +{ + + /** + * @return non-empty-array + */ + public function doFoo(): array + { + return $this->nonEmptyArrayOfBars(); + } + + /** + * @return non-empty-array + */ + public function nonEmptyArrayOfBars(): array + { + /** @var Bar $b */ + $b = doFoo(); + return [$b]; + } + +} + +class Test3 +{ + + /** + * @return non-empty-list + */ + public function doFoo(): array + { + return $this->nonEmptyArrayOfBars(); + } + + /** + * @return array + */ + public function nonEmptyArrayOfBars(): array + { + return []; + } + +}