Skip to content

Commit 9f8a28b

Browse files
committed
Updated Rector to commit 82f51819fd199d651798f94e588056f1b2d1377b
rectorphp/rector-src@82f5181 [fix] avoid changing generic interface in NarrowObjectReturnTypeRector, focus on objects as in definition (#7700)
1 parent 5b48d9e commit 9f8a28b

File tree

2 files changed

+28
-40
lines changed

2 files changed

+28
-40
lines changed

rules/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector.php

Lines changed: 26 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
use PhpParser\Node\Name\FullyQualified;
99
use PhpParser\Node\Stmt\ClassMethod;
1010
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
11-
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
1211
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
1312
use PHPStan\Reflection\ClassReflection;
13+
use PHPStan\Reflection\ReflectionProvider;
1414
use PHPStan\Type\Generic\GenericObjectType;
1515
use PHPStan\Type\ObjectType;
1616
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
@@ -60,7 +60,11 @@ final class NarrowObjectReturnTypeRector extends AbstractRector
6060
* @readonly
6161
*/
6262
private DocBlockUpdater $docBlockUpdater;
63-
public function __construct(BetterNodeFinder $betterNodeFinder, ReflectionResolver $reflectionResolver, AstResolver $astResolver, StaticTypeMapper $staticTypeMapper, TypeComparator $typeComparator, PhpDocInfoFactory $phpDocInfoFactory, DocBlockUpdater $docBlockUpdater)
63+
/**
64+
* @readonly
65+
*/
66+
private ReflectionProvider $reflectionProvider;
67+
public function __construct(BetterNodeFinder $betterNodeFinder, ReflectionResolver $reflectionResolver, AstResolver $astResolver, StaticTypeMapper $staticTypeMapper, TypeComparator $typeComparator, PhpDocInfoFactory $phpDocInfoFactory, DocBlockUpdater $docBlockUpdater, ReflectionProvider $reflectionProvider)
6468
{
6569
$this->betterNodeFinder = $betterNodeFinder;
6670
$this->reflectionResolver = $reflectionResolver;
@@ -69,10 +73,11 @@ public function __construct(BetterNodeFinder $betterNodeFinder, ReflectionResolv
6973
$this->typeComparator = $typeComparator;
7074
$this->phpDocInfoFactory = $phpDocInfoFactory;
7175
$this->docBlockUpdater = $docBlockUpdater;
76+
$this->reflectionProvider = $reflectionProvider;
7277
}
7378
public function getRuleDefinition(): RuleDefinition
7479
{
75-
return new RuleDefinition('Narrows return type from generic object or parent class to specific class in final classes/methods', [new CodeSample(<<<'CODE_SAMPLE'
80+
return new RuleDefinition('Narrows return type from generic `object` or parent class to specific class in final classes/methods', [new CodeSample(<<<'CODE_SAMPLE'
7681
final class TalkFactory extends AbstractFactory
7782
{
7883
protected function build(): object
@@ -133,19 +138,29 @@ public function refactor(Node $node): ?Node
133138
if (!$classReflection->isFinalByKeyword() && !$node->isFinal()) {
134139
return null;
135140
}
136-
$actualReturnClass = $this->getActualReturnClass($node);
141+
$actualReturnClass = $this->getActualReturnedClass($node);
137142
if ($actualReturnClass === null) {
138143
return null;
139144
}
140145
$declaredType = $returnType->toString();
146+
// already most narrow type
141147
if ($declaredType === $actualReturnClass) {
142148
return null;
143149
}
144-
if ($this->isDeclaredTypeFinal($declaredType)) {
145-
return null;
146-
}
147-
if ($this->isActualTypeAnonymous($actualReturnClass)) {
148-
return null;
150+
// non-existing class
151+
if ($declaredType !== 'object') {
152+
if (!$this->reflectionProvider->hasClass($declaredType)) {
153+
return null;
154+
}
155+
$declaredTypeClassReflection = $this->reflectionProvider->getClass($declaredType);
156+
// already last final object
157+
if ($declaredTypeClassReflection->isFinalByKeyword()) {
158+
return null;
159+
}
160+
// this rule narrows only object or class types, not interfaces
161+
if (!$declaredTypeClassReflection->isClass()) {
162+
return null;
163+
}
149164
}
150165
if (!$this->isNarrowingValid($node, $declaredType, $actualReturnClass)) {
151166
return null;
@@ -169,8 +184,6 @@ private function updateDocblock(ClassMethod $classMethod, string $actualReturnCl
169184
}
170185
if ($returnTagValueNode->type instanceof IdentifierTypeNode) {
171186
$oldType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($returnTagValueNode->type, $classMethod);
172-
} elseif ($returnTagValueNode->type instanceof GenericTypeNode) {
173-
$oldType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($returnTagValueNode->type->type, $classMethod);
174187
} else {
175188
return;
176189
}
@@ -180,34 +193,9 @@ private function updateDocblock(ClassMethod $classMethod, string $actualReturnCl
180193
return;
181194
}
182195
}
183-
if ($returnTagValueNode->type instanceof IdentifierTypeNode) {
184-
$returnTagValueNode->type = new FullyQualifiedIdentifierTypeNode($actualReturnClass);
185-
} else {
186-
$returnTagValueNode->type->type = new FullyQualifiedIdentifierTypeNode($actualReturnClass);
187-
}
196+
$returnTagValueNode->type = new FullyQualifiedIdentifierTypeNode($actualReturnClass);
188197
$this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($classMethod);
189198
}
190-
private function isDeclaredTypeFinal(string $declaredType): bool
191-
{
192-
if ($declaredType === 'object') {
193-
return \false;
194-
}
195-
$declaredObjectType = new ObjectType($declaredType);
196-
$classReflection = $declaredObjectType->getClassReflection();
197-
if (!$classReflection instanceof ClassReflection) {
198-
return \false;
199-
}
200-
return $classReflection->isFinalByKeyword();
201-
}
202-
private function isActualTypeAnonymous(string $actualType): bool
203-
{
204-
$actualObjectType = new ObjectType($actualType);
205-
$classReflection = $actualObjectType->getClassReflection();
206-
if (!$classReflection instanceof ClassReflection) {
207-
return \false;
208-
}
209-
return $classReflection->isAnonymous();
210-
}
211199
private function isNarrowingValid(ClassMethod $classMethod, string $declaredType, string $actualType): bool
212200
{
213201
if ($declaredType === 'object') {
@@ -258,7 +246,7 @@ private function isNarrowingValidFromParent(ClassMethod $classMethod, string $ac
258246
}
259247
return \true;
260248
}
261-
private function getActualReturnClass(ClassMethod $classMethod): ?string
249+
private function getActualReturnedClass(ClassMethod $classMethod): ?string
262250
{
263251
$returnStatements = $this->betterNodeFinder->findReturnsScoped($classMethod);
264252
if ($returnStatements === []) {

src/Application/VersionResolver.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ final class VersionResolver
1919
* @api
2020
* @var string
2121
*/
22-
public const PACKAGE_VERSION = 'bd510e6e343aaadd95a50e36fecfdbd349af2af8';
22+
public const PACKAGE_VERSION = '82f51819fd199d651798f94e588056f1b2d1377b';
2323
/**
2424
* @api
2525
* @var string
2626
*/
27-
public const RELEASE_DATE = '2025-12-02 07:32:41';
27+
public const RELEASE_DATE = '2025-12-02 08:38:48';
2828
/**
2929
* @var int
3030
*/

0 commit comments

Comments
 (0)