Skip to content

Commit 8220ef4

Browse files
committed
Inherit PHPDoc return type in child method with narrower native return type
1 parent 47364df commit 8220ef4

File tree

6 files changed

+111
-1
lines changed

6 files changed

+111
-1
lines changed

src/Analyser/NodeScopeResolver.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7025,6 +7025,23 @@ private function getPhpDocReturnType(ResolvedPhpDocBlock $resolvedPhpDoc, Type $
70257025
return $phpDocReturnType;
70267026
}
70277027

7028+
if ($phpDocReturnType instanceof UnionType) {
7029+
$types = [];
7030+
foreach ($phpDocReturnType->getTypes() as $innerType) {
7031+
if (!$nativeReturnType->isSuperTypeOf($innerType)->yes()) {
7032+
continue;
7033+
}
7034+
7035+
$types[] = $innerType;
7036+
}
7037+
7038+
if (count($types) === 0) {
7039+
return null;
7040+
}
7041+
7042+
return TypeCombinator::union(...$types);
7043+
}
7044+
70287045
return null;
70297046
}
70307047

src/Reflection/Php/PhpClassReflectionExtension.php

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
use PHPStan\Type\Type;
5454
use PHPStan\Type\TypeCombinator;
5555
use PHPStan\Type\TypehintHelper;
56+
use PHPStan\Type\UnionType;
5657
use function array_key_exists;
5758
use function array_keys;
5859
use function array_map;
@@ -1241,10 +1242,31 @@ private function getPhpDocReturnType(ClassReflection $phpDocBlockClassReflection
12411242
TemplateTypeVariance::createCovariant(),
12421243
);
12431244

1244-
if ($returnTag->isExplicit() || $nativeReturnType->isSuperTypeOf($phpDocReturnType)->yes()) {
1245+
if ($returnTag->isExplicit()) {
12451246
return $phpDocReturnType;
12461247
}
12471248

1249+
if ($nativeReturnType->isSuperTypeOf($phpDocReturnType)->yes()) {
1250+
return $phpDocReturnType;
1251+
}
1252+
1253+
if ($phpDocReturnType instanceof UnionType) {
1254+
$types = [];
1255+
foreach ($phpDocReturnType->getTypes() as $innerType) {
1256+
if (!$nativeReturnType->isSuperTypeOf($innerType)->yes()) {
1257+
continue;
1258+
}
1259+
1260+
$types[] = $innerType;
1261+
}
1262+
1263+
if (count($types) === 0) {
1264+
return null;
1265+
}
1266+
1267+
return TypeCombinator::union(...$types);
1268+
}
1269+
12481270
return null;
12491271
}
12501272

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ private static function findTestFiles(): iterable
172172
yield __DIR__ . '/../Rules/Arrays/data/slevomat-foreach-unset-bug.php';
173173
yield __DIR__ . '/../Rules/Arrays/data/slevomat-foreach-array-key-exists-bug.php';
174174

175+
yield __DIR__ . '/../Rules/Methods/data/inherit-phpdoc-return-type-with-narrower-native-return-type.php';
176+
175177
if (PHP_VERSION_ID >= 80000) {
176178
yield __DIR__ . '/../Rules/Comparison/data/bug-7898.php';
177179
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php // lint >= 8.0
2+
3+
namespace InheritPhpDocReturnTypeWithNarrowerNativeReturnTypePhp8;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class Foo
8+
{
9+
10+
/**
11+
* @return array<string>|positive-int|null
12+
*/
13+
public function doFoo(): array|int|null
14+
{
15+
16+
}
17+
18+
}
19+
20+
class Bar extends Foo
21+
{
22+
23+
public function doFoo(): array|int
24+
{
25+
26+
}
27+
28+
}
29+
30+
function (Bar $bar): void {
31+
assertType('array<string>|int<1, max>', $bar->doFoo());
32+
};

tests/PHPStan/Rules/Methods/MissingMethodReturnTypehintRuleTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,9 @@ public function testBug9657(): void
118118
$this->analyse([__DIR__ . '/data/bug-9657.php'], []);
119119
}
120120

121+
public function testInheritPhpDocReturnTypeWithNarrowerNativeReturnType(): void
122+
{
123+
$this->analyse([__DIR__ . '/data/inherit-phpdoc-return-type-with-narrower-native-return-type.php'], []);
124+
}
125+
121126
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace InheritPhpDocReturnTypeWithNarrowerNativeReturnType;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class Foo
8+
{
9+
10+
/**
11+
* @return array<string>|null
12+
*/
13+
public function doFoo(): ?array
14+
{
15+
16+
}
17+
18+
}
19+
20+
class Bar extends Foo
21+
{
22+
23+
public function doFoo(): array
24+
{
25+
26+
}
27+
28+
}
29+
30+
function (Bar $bar): void {
31+
assertType('array<string>', $bar->doFoo());
32+
};

0 commit comments

Comments
 (0)