Skip to content

Commit 392fec1

Browse files
Narrow object return type (#7575)
* Narrow object return type When the actual return type of a method is a subtype of the declared return type, we can replace the declared return type with the actual return type * replace override attribute * skip when parent has specific class return type * apply feedback * skip non final classes * scope return and allow final methods * Handle generics using AST instead of PHPStan * remove dummy classes * use isFinalByKeyword * move to source * update description * use isFinalByKeyword * return if method is private * Resolve returned class via generics * Revert "Resolve returned class via generics" This reverts commit b6e08c8.
1 parent 817bbbc commit 392fec1

18 files changed

+592
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture;
4+
5+
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\ConferenceTalk;
6+
7+
class NonFinalClassWithFinalMethod
8+
{
9+
final public function create(): object
10+
{
11+
return new ConferenceTalk();
12+
}
13+
}
14+
15+
?>
16+
-----
17+
<?php
18+
19+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture;
20+
21+
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\ConferenceTalk;
22+
23+
class NonFinalClassWithFinalMethod
24+
{
25+
final public function create(): \Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\ConferenceTalk
26+
{
27+
return new ConferenceTalk();
28+
}
29+
}
30+
31+
?>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture;
4+
5+
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\AbstractFactory;
6+
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\ConferenceTalk;
7+
8+
final class TalkFactory extends AbstractFactory
9+
{
10+
protected function build(): object
11+
{
12+
return new ConferenceTalk();
13+
}
14+
}
15+
16+
?>
17+
-----
18+
<?php
19+
20+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture;
21+
22+
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\AbstractFactory;
23+
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\ConferenceTalk;
24+
25+
final class TalkFactory extends AbstractFactory
26+
{
27+
protected function build(): \Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\ConferenceTalk
28+
{
29+
return new ConferenceTalk();
30+
}
31+
}
32+
33+
?>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture;
4+
5+
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\Talk;
6+
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\ConferenceTalkExtended;
7+
8+
final class TalkFactoryExtended
9+
{
10+
public function createConferenceTalk(): Talk
11+
{
12+
return new ConferenceTalkExtended();
13+
}
14+
}
15+
16+
?>
17+
-----
18+
<?php
19+
20+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture;
21+
22+
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\Talk;
23+
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\ConferenceTalkExtended;
24+
25+
final class TalkFactoryExtended
26+
{
27+
public function createConferenceTalk(): \Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\ConferenceTalkExtended
28+
{
29+
return new ConferenceTalkExtended();
30+
}
31+
}
32+
33+
?>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture;
4+
5+
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\AbstractGenericFactory;
6+
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\ConferenceTalk;
7+
8+
/**
9+
* @extends AbstractGenericFactory<ConferenceTalk>
10+
*/
11+
final class ConcreteGenericFactory extends AbstractGenericFactory
12+
{
13+
public function build(): object
14+
{
15+
return new ConferenceTalk();
16+
}
17+
}
18+
19+
?>
20+
-----
21+
<?php
22+
23+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture;
24+
25+
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\AbstractGenericFactory;
26+
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\ConferenceTalk;
27+
28+
/**
29+
* @extends AbstractGenericFactory<ConferenceTalk>
30+
*/
31+
final class ConcreteGenericFactory extends AbstractGenericFactory
32+
{
33+
public function build(): \Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\ConferenceTalk
34+
{
35+
return new ConferenceTalk();
36+
}
37+
}
38+
39+
?>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture;
4+
5+
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\ConferenceTalk;
6+
7+
abstract class AbstractItemFactory
8+
{
9+
public function create(): object
10+
{
11+
return new ConferenceTalk();
12+
}
13+
}
14+
15+
?>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture;
4+
5+
final class FinalProductFactory
6+
{
7+
public function create(): \DateTime
8+
{
9+
return new \DateTime();
10+
}
11+
}
12+
13+
?>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture;
4+
5+
final class MixedFactory
6+
{
7+
public function create(): object
8+
{
9+
if (rand(0, 1)) {
10+
return new \DateTime();
11+
}
12+
13+
return new \stdClass();
14+
}
15+
}
16+
17+
?>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture;
4+
5+
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\ConferenceTalk;
6+
7+
class NonFinalFactory
8+
{
9+
public function create(): object
10+
{
11+
return new ConferenceTalk();
12+
}
13+
}
14+
15+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture;
4+
5+
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\AbstractTalkFactory;
6+
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\Talk;
7+
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\ConferenceTalkExtended;
8+
9+
final class ConcreteTalkFactory extends AbstractTalkFactory
10+
{
11+
public function build(): Talk
12+
{
13+
return new ConferenceTalkExtended();
14+
}
15+
}
16+
17+
?>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class NarrowObjectReturnTypeRectorTest extends AbstractRectorTestCase
12+
{
13+
#[DataProvider('provideData')]
14+
public function test(string $filePath): void
15+
{
16+
$this->doTestFile($filePath);
17+
}
18+
19+
public static function provideData(): Iterator
20+
{
21+
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
22+
}
23+
24+
public function provideConfigFilePath(): string
25+
{
26+
return __DIR__ . '/config/configured_rule.php';
27+
}
28+
}

0 commit comments

Comments
 (0)