Skip to content

Commit 40876ee

Browse files
Check if narrowing is valid from parent
1 parent e3c402b commit 40876ee

File tree

1 file changed

+54
-0
lines changed

1 file changed

+54
-0
lines changed

rules/TypeDeclaration/Rector/ClassMethod/NarrowObjectReturnTypeRector.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ public function refactor(Node $node): ?Node
146146
return null;
147147
}
148148

149+
if (! $this->isNarrowingValidFromParent($node, $actualReturnClass)) {
150+
return null;
151+
}
152+
149153
$node->returnType = new FullyQualified($actualReturnClass);
150154

151155
$this->updateDocblock($node, $actualReturnClass);
@@ -246,6 +250,56 @@ private function isNarrowingValid(ClassMethod $classMethod, string $declaredType
246250
return ! $returnType instanceof GenericObjectType;
247251
}
248252

253+
private function isNarrowingValidFromParent(ClassMethod $classMethod, string $actualReturnClass): bool
254+
{
255+
if ($classMethod->isPrivate()) {
256+
return true;
257+
}
258+
259+
$classReflection = $this->reflectionResolver->resolveClassReflection($classMethod);
260+
261+
if (! $classReflection instanceof ClassReflection) {
262+
return true;
263+
}
264+
265+
$ancestors = array_filter(
266+
$classReflection->getAncestors(),
267+
fn (ClassReflection $ancestorClassReflection): bool => $classReflection->getName() !== $ancestorClassReflection->getName()
268+
);
269+
270+
$methodName = $this->getName($classMethod);
271+
272+
foreach ($ancestors as $ancestor) {
273+
if ($ancestor->getFileName() === null) {
274+
continue;
275+
}
276+
277+
if (! $ancestor->hasNativeMethod($methodName)) {
278+
continue;
279+
}
280+
281+
$parentClassMethod = $this->astResolver->resolveClassMethod($ancestor->getName(), $methodName);
282+
283+
if (! $parentClassMethod instanceof ClassMethod) {
284+
continue;
285+
}
286+
287+
$parentReturnType = $parentClassMethod->returnType;
288+
289+
if (! $parentReturnType instanceof Node) {
290+
continue;
291+
}
292+
293+
$parentReturnTypeName = $parentReturnType->toString();
294+
295+
if (! $this->isNarrowingValid($parentReturnTypeName, $actualReturnClass)) {
296+
return false;
297+
}
298+
}
299+
300+
return true;
301+
}
302+
249303
private function getActualReturnClass(ClassMethod $classMethod): ?string
250304
{
251305
$returnStatements = $this->betterNodeFinder->findReturnsScoped($classMethod);

0 commit comments

Comments
 (0)