Skip to content

Commit a84d702

Browse files
Narrow object return type
1 parent b2e2d0e commit a84d702

7 files changed

Lines changed: 239 additions & 71 deletions

File tree

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture;
4+
5+
abstract class AbstractTalk
6+
{
7+
}
8+
9+
final class ConcreteConferenceTalk extends AbstractTalk
10+
{
11+
}
12+
13+
final class TalkFactory
14+
{
15+
public function create(): AbstractTalk
16+
{
17+
return new ConcreteConferenceTalk();
18+
}
19+
}
20+
21+
?>
22+
-----
23+
<?php
24+
25+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture;
26+
27+
abstract class AbstractTalk
28+
{
29+
}
30+
31+
final class ConcreteConferenceTalk extends AbstractTalk
32+
{
33+
}
34+
35+
final class TalkFactory
36+
{
37+
public function create(): \Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture\ConcreteConferenceTalk
38+
{
39+
return new ConcreteConferenceTalk();
40+
}
41+
}
42+
43+
?>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture;
4+
5+
interface TalkInterface
6+
{
7+
}
8+
9+
final class ConferenceTalkImplementation implements TalkInterface
10+
{
11+
}
12+
13+
final class TalkFactory
14+
{
15+
public function create(): TalkInterface
16+
{
17+
return new ConferenceTalkImplementation();
18+
}
19+
}
20+
21+
?>
22+
-----
23+
<?php
24+
25+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture;
26+
27+
interface TalkInterface
28+
{
29+
}
30+
31+
final class ConferenceTalkImplementation implements TalkInterface
32+
{
33+
}
34+
35+
final class TalkFactory
36+
{
37+
public function create(): \Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture\ConferenceTalkImplementation
38+
{
39+
return new ConferenceTalkImplementation();
40+
}
41+
}
42+
43+
?>
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture;
4+
5+
interface ProductInterface
6+
{
7+
}
8+
9+
final class ConcreteProduct implements ProductInterface
10+
{
11+
}
12+
13+
abstract class AbstractProductFactory
14+
{
15+
abstract public function build(): ProductInterface;
16+
}
17+
18+
final class ConcreteProductFactory extends AbstractProductFactory
19+
{
20+
public function build(): ProductInterface
21+
{
22+
return $this->createProduct();
23+
}
24+
25+
private function createProduct(): ConcreteProduct
26+
{
27+
return new ConcreteProduct();
28+
}
29+
}
30+
31+
?>
32+
-----
33+
<?php
34+
35+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture;
36+
37+
interface ProductInterface
38+
{
39+
}
40+
41+
final class ConcreteProduct implements ProductInterface
42+
{
43+
}
44+
45+
abstract class AbstractProductFactory
46+
{
47+
abstract public function build(): ProductInterface;
48+
}
49+
50+
final class ConcreteProductFactory extends AbstractProductFactory
51+
{
52+
public function build(): \Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture\ConcreteProduct
53+
{
54+
return $this->createProduct();
55+
}
56+
57+
private function createProduct(): ConcreteProduct
58+
{
59+
return new ConcreteProduct();
60+
}
61+
}
62+
63+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture;
4+
5+
interface PaymentInterface
6+
{
7+
}
8+
9+
final class StripePayment implements PaymentInterface
10+
{
11+
}
12+
13+
final class PaymentFactory
14+
{
15+
public function create(): PaymentInterface
16+
{
17+
return $this->createStripePayment();
18+
}
19+
20+
private function createStripePayment(): StripePayment
21+
{
22+
return new StripePayment();
23+
}
24+
}
25+
26+
?>
27+
-----
28+
<?php
29+
30+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture;
31+
32+
interface PaymentInterface
33+
{
34+
}
35+
36+
final class StripePayment implements PaymentInterface
37+
{
38+
}
39+
40+
final class PaymentFactory
41+
{
42+
public function create(): \Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture\StripePayment
43+
{
44+
return $this->createStripePayment();
45+
}
46+
47+
private function createStripePayment(): StripePayment
48+
{
49+
return new StripePayment();
50+
}
51+
}
52+
53+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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+
?>
18+
-----
19+
<?php
20+
21+
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Fixture;
22+
23+
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\AbstractTalkFactory;
24+
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\Talk;
25+
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\ConferenceTalkExtended;
26+
27+
final class ConcreteTalkFactory extends AbstractTalkFactory
28+
{
29+
public function build(): \Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\Source\ConferenceTalkExtended
30+
{
31+
return new ConferenceTalkExtended();
32+
}
33+
}
34+
35+
?>

rules-tests/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector/Fixture/skip_parent_has_specific_return_type.php.inc

Lines changed: 0 additions & 17 deletions
This file was deleted.

rules/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector.php

Lines changed: 2 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,14 @@
1010
use PhpParser\Node\Stmt\ClassMethod;
1111
use PHPStan\Reflection\ClassReflection;
1212
use PHPStan\Type\ObjectType;
13-
use Rector\PhpParser\AstResolver;
1413
use Rector\PhpParser\Node\BetterNodeFinder;
1514
use Rector\Rector\AbstractRector;
1615
use Rector\Reflection\ReflectionResolver;
1716
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
1817
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
1918

2019
/**
21-
* Narrows return type from generic object to specific class in final classes/methods.
20+
* Narrows return type from generic object or parent class to specific class in final classes/methods.
2221
*
2322
* @see \Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\NarrowObjectReturnTypeRectorTest
2423
*/
@@ -27,14 +26,13 @@ final class NarrowObjectReturnTypeRector extends AbstractRector
2726
public function __construct(
2827
private readonly BetterNodeFinder $betterNodeFinder,
2928
private readonly ReflectionResolver $reflectionResolver,
30-
private readonly AstResolver $astResolver,
3129
) {
3230
}
3331

3432
public function getRuleDefinition(): RuleDefinition
3533
{
3634
return new RuleDefinition(
37-
'Narrows return type from generic object to specific class in final classes/methods',
35+
'Narrows return type from generic object or parent class to specific class in final classes/methods',
3836
[
3937
new CodeSample(
4038
<<<'CODE_SAMPLE'
@@ -114,10 +112,6 @@ public function refactor(Node $node): ?Node
114112
return null;
115113
}
116114

117-
if ($this->hasParentMethodWithNonObjectReturn($node)) {
118-
return null;
119-
}
120-
121115
$actualReturnClass = $this->getActualReturnClass($node);
122116

123117
if ($actualReturnClass === null) {
@@ -188,52 +182,6 @@ private function isNarrowingValid(string $declaredType, string $actualType): boo
188182
->yes();
189183
}
190184

191-
private function hasParentMethodWithNonObjectReturn(ClassMethod $classMethod): bool
192-
{
193-
$classReflection = $this->reflectionResolver->resolveClassReflection($classMethod);
194-
195-
if (! $classReflection instanceof ClassReflection) {
196-
return false;
197-
}
198-
199-
$ancestors = array_filter(
200-
$classReflection->getAncestors(),
201-
fn (ClassReflection $ancestorClassReflection): bool => $classReflection->getName() !== $ancestorClassReflection->getName()
202-
);
203-
204-
$methodName = $this->getName($classMethod);
205-
206-
foreach ($ancestors as $ancestor) {
207-
if ($ancestor->getFileName() === null) {
208-
continue;
209-
}
210-
211-
if (! $ancestor->hasNativeMethod($methodName)) {
212-
continue;
213-
}
214-
215-
$parentClassMethod = $this->astResolver->resolveClassMethod($ancestor->getName(), $methodName);
216-
217-
if ($parentClassMethod === null) {
218-
continue;
219-
}
220-
221-
$parentReturnType = $parentClassMethod->returnType;
222-
223-
if ($parentReturnType === null) {
224-
continue;
225-
}
226-
227-
if ($parentReturnType instanceof Identifier && $parentReturnType->name === 'object') {
228-
continue;
229-
}
230-
231-
return true;
232-
}
233-
234-
return false;
235-
}
236-
237185
private function getActualReturnClass(ClassMethod $node): ?string
238186
{
239187
$returnStatements = $this->betterNodeFinder->findReturnsScoped($node);

0 commit comments

Comments
 (0)