From da5e218de8a4ffb056fd842dee1843f945256a83 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sun, 20 Apr 2025 09:29:25 +0700 Subject: [PATCH 1/2] [DowngradePhp80] Handle union implements method from interface on DowngradeUnionTypeDeclarationRector --- .../Fixture/override_from_interface.php.inc | 38 +++++++++++++++++++ .../Source/Foo1.php | 7 ++++ .../Source/Foo2.php | 7 ++++ .../Source/FooInterface.php | 7 ++++ .../Source/SomeInterface.php | 8 ++++ 5 files changed, 67 insertions(+) create mode 100644 rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Fixture/override_from_interface.php.inc create mode 100644 rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Source/Foo1.php create mode 100644 rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Source/Foo2.php create mode 100644 rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Source/FooInterface.php create mode 100644 rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Source/SomeInterface.php diff --git a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Fixture/override_from_interface.php.inc b/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Fixture/override_from_interface.php.inc new file mode 100644 index 00000000..5f7e9bac --- /dev/null +++ b/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Fixture/override_from_interface.php.inc @@ -0,0 +1,38 @@ + +----- + diff --git a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Source/Foo1.php b/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Source/Foo1.php new file mode 100644 index 00000000..60f839f0 --- /dev/null +++ b/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Source/Foo1.php @@ -0,0 +1,7 @@ + Date: Sun, 20 Apr 2025 09:36:13 +0700 Subject: [PATCH 2/2] Fix --- .../Fixture/override_from_interface.php.inc | 2 +- .../PhpDocFromTypeDeclarationDecorator.php | 29 ++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Fixture/override_from_interface.php.inc b/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Fixture/override_from_interface.php.inc index 5f7e9bac..b3f8791f 100644 --- a/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Fixture/override_from_interface.php.inc +++ b/rules-tests/DowngradePhp80/Rector/FunctionLike/DowngradeUnionTypeDeclarationRector/Fixture/override_from_interface.php.inc @@ -29,7 +29,7 @@ class OverrideFromInterface implements SomeInterface /** * @return \Rector\Tests\DowngradePhp80\Rector\FunctionLike\DowngradeUnionTypeDeclarationRector\Source\Foo1|\Rector\Tests\DowngradePhp80\Rector\FunctionLike\DowngradeUnionTypeDeclarationRector\Source\Foo2 */ - public function run(): \Rector\Tests\DowngradePhp80\Rector\FunctionLike\DowngradeUnionTypeDeclarationRector\Source\SomeInterface + public function run(): \Rector\Tests\DowngradePhp80\Rector\FunctionLike\DowngradeUnionTypeDeclarationRector\Source\FooInterface { return new Foo1(); } diff --git a/src/PhpDocDecorator/PhpDocFromTypeDeclarationDecorator.php b/src/PhpDocDecorator/PhpDocFromTypeDeclarationDecorator.php index c3918105..927411fc 100644 --- a/src/PhpDocDecorator/PhpDocFromTypeDeclarationDecorator.php +++ b/src/PhpDocDecorator/PhpDocFromTypeDeclarationDecorator.php @@ -10,7 +10,9 @@ use PhpParser\Node\Expr\Closure; use PhpParser\Node\Identifier; use PhpParser\Node\Name; +use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Param; +use PhpParser\Node\Stmt\ClassLike; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Function_; use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; @@ -27,6 +29,7 @@ use Rector\Php\PhpVersionProvider; use Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer; use Rector\PhpAttribute\NodeFactory\PhpAttributeGroupFactory; +use Rector\PhpParser\AstResolver; use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; use Rector\Reflection\ReflectionResolver; use Rector\StaticTypeMapper\StaticTypeMapper; @@ -52,7 +55,8 @@ public function __construct( private readonly PhpAttributeGroupFactory $phpAttributeGroupFactory, private readonly ReflectionResolver $reflectionResolver, private readonly PhpAttributeAnalyzer $phpAttributeAnalyzer, - private readonly PhpVersionProvider $phpVersionProvider + private readonly PhpVersionProvider $phpVersionProvider, + private readonly AstResolver $astResolver, ) { $this->classMethodWillChangeReturnTypes = [ // @todo how to make list complete? is the method list needed or can we use just class names? @@ -96,6 +100,29 @@ public function decorateReturn(ClassMethod|Function_|Closure|ArrowFunction $func return; } + $ancestors = array_filter( + $classReflection->getAncestors(), + static fn (ClassReflection $ancestor): bool => $classReflection->getName() !== $ancestor->getName() + ); + + foreach ($ancestors as $ancestor) { + $classLike = $this->astResolver->resolveClassFromClassReflection($ancestor); + if (! $classLike instanceof ClassLike) { + continue; + } + + $classMethod = $classLike->getMethod($functionLike->name->toString()); + if (! $classMethod instanceof ClassMethod) { + continue; + } + + $returnType = $classMethod->returnType; + if ($returnType instanceof Node && $returnType instanceof FullyQualified) { + $functionLike->returnType = new FullyQualified($returnType->toString()); + break; + } + } + if (! $this->isRequireReturnTypeWillChange($classReflection, $functionLike)) { return; }