Skip to content

Commit 9d2af5b

Browse files
Narrow object return type
1 parent dfa1141 commit 9d2af5b

File tree

7 files changed

+240
-74
lines changed

7 files changed

+240
-74
lines changed
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: 3 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
2828

2929
/**
30-
* Narrows return type from generic object to specific class in final classes/methods.
30+
* Narrows return type from generic object or parent class to specific class in final classes/methods.
3131
*
3232
* @see \Rector\Tests\TypeDeclaration\Rector\ClassMethod\NarrowObjectReturnTypeRector\NarrowObjectReturnTypeRectorTest
3333
*/
@@ -47,7 +47,7 @@ public function __construct(
4747
public function getRuleDefinition(): RuleDefinition
4848
{
4949
return new RuleDefinition(
50-
'Narrows return type from generic object to specific class in final classes/methods',
50+
'Narrows return type from generic object or parent class to specific class in final classes/methods',
5151
[
5252
new CodeSample(
5353
<<<'CODE_SAMPLE'
@@ -123,10 +123,6 @@ public function refactor(Node $node): ?Node
123123
return null;
124124
}
125125

126-
if ($this->hasParentMethodWithNonObjectReturn($node)) {
127-
return null;
128-
}
129-
130126
$actualReturnClass = $this->getActualReturnClass($node);
131127

132128
if ($actualReturnClass === null) {
@@ -240,57 +236,7 @@ private function isNarrowingValid(string $declaredType, string $actualType): boo
240236
return $declaredObjectType->isSuperTypeOf($actualObjectType)
241237
->yes();
242238
}
243-
244-
private function hasParentMethodWithNonObjectReturn(ClassMethod $classMethod): bool
245-
{
246-
if ($classMethod->isPrivate()) {
247-
return false;
248-
}
249-
250-
$classReflection = $this->reflectionResolver->resolveClassReflection($classMethod);
251-
252-
if (! $classReflection instanceof ClassReflection) {
253-
return false;
254-
}
255-
256-
$ancestors = array_filter(
257-
$classReflection->getAncestors(),
258-
fn (ClassReflection $ancestorClassReflection): bool => $classReflection->getName() !== $ancestorClassReflection->getName()
259-
);
260-
261-
$methodName = $this->getName($classMethod);
262-
263-
foreach ($ancestors as $ancestor) {
264-
if ($ancestor->getFileName() === null) {
265-
continue;
266-
}
267-
268-
if (! $ancestor->hasNativeMethod($methodName)) {
269-
continue;
270-
}
271-
272-
$parentClassMethod = $this->astResolver->resolveClassMethod($ancestor->getName(), $methodName);
273-
274-
if (! $parentClassMethod instanceof ClassMethod) {
275-
continue;
276-
}
277-
278-
$parentReturnType = $parentClassMethod->returnType;
279-
280-
if (! $parentReturnType instanceof Node) {
281-
continue;
282-
}
283-
284-
if ($parentReturnType instanceof Identifier && $parentReturnType->name === 'object') {
285-
continue;
286-
}
287-
288-
return true;
289-
}
290-
291-
return false;
292-
}
293-
239+
294240
private function getActualReturnClass(ClassMethod $classMethod): ?string
295241
{
296242
$returnStatements = $this->betterNodeFinder->findReturnsScoped($classMethod);

0 commit comments

Comments
 (0)