From 00adfaa7d1cb427262577df705264701b7de9670 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 14 Nov 2023 09:52:45 +0100 Subject: [PATCH] Fix wrong tip about returning a list Closes https://github.com/phpstan/phpstan/issues/10077 --- src/Type/Accessory/AccessoryArrayListType.php | 12 +--- src/Type/IntersectionType.php | 14 +++++ .../Rules/Functions/ReturnTypeRuleTest.php | 12 ++++ .../Rules/Functions/data/bug-10077.php | 57 +++++++++++++++++++ .../Rules/Methods/ReturnTypeRuleTest.php | 10 ++++ .../Rules/Methods/data/wrong-list-tip.php | 34 +++++++++++ .../TypesAssignedToPropertiesRuleTest.php | 2 +- 7 files changed, 129 insertions(+), 12 deletions(-) create mode 100644 tests/PHPStan/Rules/Functions/data/bug-10077.php create mode 100644 tests/PHPStan/Rules/Methods/data/wrong-list-tip.php diff --git a/src/Type/Accessory/AccessoryArrayListType.php b/src/Type/Accessory/AccessoryArrayListType.php index 15892d562b..bad40674cb 100644 --- a/src/Type/Accessory/AccessoryArrayListType.php +++ b/src/Type/Accessory/AccessoryArrayListType.php @@ -26,7 +26,6 @@ use PHPStan\Type\TypeCombinator; use PHPStan\Type\UnionType; use PHPStan\Type\VerbosityLevel; -use function sprintf; /** @api */ class AccessoryArrayListType implements CompoundType, AccessoryType @@ -89,17 +88,8 @@ public function acceptsWithReason(Type $type, bool $strictTypes): AcceptsResult $isArray = $type->isArray(); $isList = $type->isList(); - $reasons = []; - if ($isArray->yes() && !$isList->yes()) { - $verbosity = VerbosityLevel::getRecommendedLevelByType($this, $type); - $reasons[] = sprintf( - '%s %s a list.', - $type->describe($verbosity), - $isList->no() ? 'is not' : 'might not be', - ); - } - return new AcceptsResult($isArray->and($isList), $reasons); + return new AcceptsResult($isArray->and($isList), []); } public function isSuperTypeOf(Type $type): TrinaryLogic diff --git a/src/Type/IntersectionType.php b/src/Type/IntersectionType.php index 3ec2c8a1b9..8c923359ce 100644 --- a/src/Type/IntersectionType.php +++ b/src/Type/IntersectionType.php @@ -189,6 +189,20 @@ public function acceptsWithReason(Type $otherType, bool $strictTypes): AcceptsRe $result = $result->and($type->acceptsWithReason($otherType, $strictTypes)); } + if (!$result->yes()) { + $isList = $otherType->isList(); + 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', + ), + ]); + } + } + return $result; } diff --git a/tests/PHPStan/Rules/Functions/ReturnTypeRuleTest.php b/tests/PHPStan/Rules/Functions/ReturnTypeRuleTest.php index 13aac0c229..bfce5b0a94 100644 --- a/tests/PHPStan/Rules/Functions/ReturnTypeRuleTest.php +++ b/tests/PHPStan/Rules/Functions/ReturnTypeRuleTest.php @@ -231,4 +231,16 @@ public function testBug8846(): void $this->analyse([__DIR__ . '/data/bug-8846.php'], []); } + public function testBug10077(): void + { + $this->checkExplicitMixed = true; + $this->checkNullables = true; + $this->analyse([__DIR__ . '/data/bug-10077.php'], [ + [ + 'Function Bug10077\mergeMediaQueries() should return list|null but returns list.', + 56, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Functions/data/bug-10077.php b/tests/PHPStan/Rules/Functions/data/bug-10077.php new file mode 100644 index 0000000000..58e32641b4 --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/bug-10077.php @@ -0,0 +1,57 @@ +|null + */ +function mergeMediaQueries(array $queries1, array $queries2): ?array +{ + $queries = []; + + foreach ($queries1 as $query1) { + foreach ($queries2 as $query2) { + $result = $query1->merge($query2); + + if ($result === MediaQuerySingletonMergeResult::empty) { + continue; + } + + if ($result === MediaQuerySingletonMergeResult::unrepresentable) { + return null; + } + + $queries[] = $result; + } + } + + return $queries; +} diff --git a/tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php b/tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php index 105a2f5ae0..45c44f84be 100644 --- a/tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php +++ b/tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php @@ -955,4 +955,14 @@ public function testBug9766(): void $this->analyse([__DIR__ . '/data/bug-9766.php'], []); } + public function testWrongListTip(): void + { + $this->analyse([__DIR__ . '/data/wrong-list-tip.php'], [ + [ + 'Method WrongListTip\Test::doFoo() should return list but returns list.', + 23, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Methods/data/wrong-list-tip.php b/tests/PHPStan/Rules/Methods/data/wrong-list-tip.php new file mode 100644 index 0000000000..38f198279f --- /dev/null +++ b/tests/PHPStan/Rules/Methods/data/wrong-list-tip.php @@ -0,0 +1,34 @@ + + */ + public function doFoo(): array + { + return $this->listOfBars(); + } + + /** + * @return list + */ + public function listOfBars(): array + { + return []; + } + +} diff --git a/tests/PHPStan/Rules/Properties/TypesAssignedToPropertiesRuleTest.php b/tests/PHPStan/Rules/Properties/TypesAssignedToPropertiesRuleTest.php index 2e8bb09784..b1922a3ab3 100644 --- a/tests/PHPStan/Rules/Properties/TypesAssignedToPropertiesRuleTest.php +++ b/tests/PHPStan/Rules/Properties/TypesAssignedToPropertiesRuleTest.php @@ -515,7 +515,7 @@ public function testBug3311b(): void [ 'Property Bug3311b\Foo::$bar (list) does not accept non-empty-array, string>.', 16, - 'array, string> might not be a list.', + 'non-empty-array, string> might not be a list.', ], ]); }