[TypeDeclaration] Do not add void on possibly overriden protected class method (#1921)

This commit is contained in:
Tomas Votruba 2022-03-11 14:15:13 +01:00 committed by GitHub
parent 07df2bc028
commit 36e81c09e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 75 additions and 1 deletions

View File

@ -0,0 +1,25 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddVoidReturnTypeWhereNoReturnRector\Fixture;
final class FinalProtectedMethod
{
protected function getValues()
{
}
}
?>
-----
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddVoidReturnTypeWhereNoReturnRector\Fixture;
final class FinalProtectedMethod
{
protected function getValues(): void
{
}
}
?>

View File

@ -0,0 +1,10 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddVoidReturnTypeWhereNoReturnRector\Fixture;
class SkipNonFinalProtectedMethod
{
protected function getValues()
{
}
}

View File

@ -9,12 +9,15 @@ use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Type\NeverType;
use PHPStan\Type\VoidType;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
use Rector\Core\Contract\Rector\AllowEmptyConfigurableRectorInterface;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\TypeDeclaration\TypeInferer\SilentVoidResolver;
use Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnVendorLockResolver;
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
@ -92,7 +95,7 @@ CODE_SAMPLE
return null;
}
if ($node instanceof ClassMethod && ($node->isMagic() || $node->isAbstract())) {
if ($this->shouldSkipClassMethod($node)) {
return null;
}
@ -140,4 +143,40 @@ CODE_SAMPLE
$this->phpDocTypeChanger->changeReturnType($phpDocInfo, new VoidType());
}
private function shouldSkipClassMethod(ClassMethod|Function_|Closure $functionLike): bool
{
if (! $functionLike instanceof ClassMethod) {
return false;
}
if ($functionLike->isMagic()) {
return true;
}
if ($functionLike->isAbstract()) {
return true;
}
if ($functionLike->isProtected()) {
return ! $this->isInsideFinalClass($functionLike);
}
return false;
}
private function isInsideFinalClass(ClassMethod $classMethod): bool
{
$scope = $classMethod->getAttribute(AttributeKey::SCOPE);
if (! $scope instanceof Scope) {
return false;
}
$classReflection = $scope->getClassReflection();
if (! $classReflection instanceof ClassReflection) {
return false;
}
return $classReflection->isFinal();
}
}