Skip to content

Commit fb4503d

Browse files
authored
Drop scope on class extends/implements names, rename extends at Class_ level (#8788)
1 parent 688a462 commit fb4503d

2 files changed

Lines changed: 32 additions & 27 deletions

File tree

‎rules/Renaming/NodeManipulator/ClassRenamer.php‎

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocClassRenamer;
2121
use Rector\BetterPhpDocParser\ValueObject\NodeTypes;
2222
use Rector\Comments\NodeDocBlock\DocBlockUpdater;
23+
use Rector\NodeNameResolver\NodeNameResolver;
2324
use Rector\NodeTypeResolver\Node\AttributeKey;
2425
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockClassRenamer;
2526
use Rector\NodeTypeResolver\ValueObject\OldToNewType;
@@ -44,7 +45,8 @@ public function __construct(
4445
private readonly DocBlockClassRenamer $docBlockClassRenamer,
4546
private readonly ReflectionProvider $reflectionProvider,
4647
private readonly DocBlockUpdater $docBlockUpdater,
47-
private readonly RenamedNameCollector $renamedNameCollector
48+
private readonly RenamedNameCollector $renamedNameCollector,
49+
private readonly NodeNameResolver $nodeNameResolver
4850
) {
4951
}
5052

@@ -56,14 +58,14 @@ public function renameNode(Node $node, array $oldToNewClasses, ?Scope $scope): ?
5658
{
5759
// execute FullyQualified before Name on purpose so next Name check is pure Name node
5860
if ($node instanceof FullyQualified) {
59-
return $this->refactorName($node, $oldToNewClasses, $scope);
61+
return $this->refactorName($node, $oldToNewClasses);
6062
}
6163

6264
// Name as parent of FullyQualified executed for fallback annotation to attribute rename to Name
6365
if ($node instanceof Name) {
6466
$phpAttributeName = $node->getAttribute(AttributeKey::PHP_ATTRIBUTE_NAME);
6567
if (is_string($phpAttributeName)) {
66-
return $this->refactorName(new FullyQualified($phpAttributeName), $oldToNewClasses, $scope);
68+
return $this->refactorName(new FullyQualified($phpAttributeName), $oldToNewClasses);
6769
}
6870

6971
return null;
@@ -141,21 +143,25 @@ private function shouldSkip(string $newName, FullyQualified $fullyQualified): bo
141143
*/
142144
private function refactorName(
143145
FullyQualified $fullyQualified,
144-
array $oldToNewClasses,
145-
?Scope $scope
146+
array $oldToNewClasses
146147
): ?FullyQualified {
147148
if ($fullyQualified->getAttribute(AttributeKey::IS_FUNCCALL_NAME) === true) {
148149
return null;
149150
}
150151

152+
// class extends is handled on Class_ node level, to resolve self-extends without scope
153+
if ($fullyQualified->getAttribute(AttributeKey::IS_CLASS_EXTENDS) === true) {
154+
return null;
155+
}
156+
151157
$stringName = $fullyQualified->toString();
152158
$newName = $oldToNewClasses[$stringName] ?? null;
153159

154160
if ($newName === null) {
155161
return null;
156162
}
157163

158-
if (! $this->isClassToInterfaceValidChange($fullyQualified, $newName, $scope)) {
164+
if (! $this->isClassToInterfaceValidChange($fullyQualified, $newName)) {
159165
return null;
160166
}
161167

@@ -178,6 +184,22 @@ private function refactorClassLike(ClassLike $classLike, array $oldToNewClasses,
178184
}
179185

180186
$hasChanged = false;
187+
188+
if ($classLike->extends instanceof FullyQualified) {
189+
$extendsName = $classLike->extends->toString();
190+
$newName = $oldToNewClasses[$extendsName] ?? null;
191+
192+
// skip when the class would extend itself after rename; this might happen when you run a rename class on new child class that extends the deprecated one
193+
if ($newName !== null
194+
&& $newName !== $this->nodeNameResolver->getName($classLike)
195+
&& $this->isClassToInterfaceValidChange($classLike->extends, $newName)
196+
) {
197+
$this->renamedNameCollector->add($extendsName);
198+
$classLike->extends = new FullyQualified($newName);
199+
$hasChanged = true;
200+
}
201+
}
202+
181203
$classLike->implements = array_unique($classLike->implements);
182204
foreach ($classLike->implements as $key => $implementName) {
183205
$namespaceName = $scope instanceof Scope ? $scope->getNamespace() : null;
@@ -213,8 +235,7 @@ private function refactorClassLike(ClassLike $classLike, array $oldToNewClasses,
213235
*/
214236
private function isClassToInterfaceValidChange(
215237
FullyQualified $fullyQualified,
216-
string $newClassName,
217-
?Scope $scope
238+
string $newClassName
218239
): bool {
219240
if (! $this->reflectionProvider->hasClass($newClassName)) {
220241
return true;
@@ -223,14 +244,6 @@ private function isClassToInterfaceValidChange(
223244
$classReflection = $this->reflectionProvider->getClass($newClassName);
224245
// ensure new is not with interface
225246
if ($fullyQualified->getAttribute(AttributeKey::IS_NEW_INSTANCE_NAME) !== true) {
226-
if ($fullyQualified->getAttribute(AttributeKey::IS_CLASS_EXTENDS) === true && $scope instanceof Scope) {
227-
$currentClassReflection = $scope->getClassReflection();
228-
229-
if ($currentClassReflection instanceof ClassReflection && $currentClassReflection->getName() === $newClassName) {
230-
return false;
231-
}
232-
}
233-
234247
return $this->isValidClassNameChange($fullyQualified, $classReflection);
235248
}
236249

‎src/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php‎

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
use PhpParser\Node\Identifier;
5353
use PhpParser\Node\IntersectionType;
5454
use PhpParser\Node\Name;
55-
use PhpParser\Node\Name\FullyQualified;
5655
use PhpParser\Node\NullableType;
5756
use PhpParser\Node\Stmt;
5857
use PhpParser\Node\Stmt\Catch_;
@@ -166,22 +165,15 @@ private function fillScope(array $stmts, string $filePath, MutatingScope $scope)
166165
$mutatingScope = $mutatingScope->toWalkScope();
167166
}
168167

168+
// why not simply this everywhere? what would we lose?
169+
// $node->setAttribute(AttributeKey::SCOPE, $mutatingScope);
170+
169171
// the class reflection is resolved AFTER entering to class node
170172
// so we need to get it from the first after this one
171173
if ($node instanceof Class_ || $node instanceof Interface_ || $node instanceof Enum_) {
172174
$mutatingScope = $this->resolveClassOrInterfaceScope($node, $mutatingScope);
173175
$node->setAttribute(AttributeKey::SCOPE, $mutatingScope);
174176

175-
if ($node instanceof Class_) {
176-
if ($node->extends instanceof FullyQualified) {
177-
$node->extends->setAttribute(AttributeKey::SCOPE, $mutatingScope);
178-
}
179-
180-
foreach ($node->implements as $implement) {
181-
$implement->setAttribute(AttributeKey::SCOPE, $mutatingScope);
182-
}
183-
}
184-
185177
return;
186178
}
187179

0 commit comments

Comments
 (0)