From 5cc97f5690a17fbdfb947c983c3aeb5db7b1940f Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Wed, 6 Mar 2024 20:37:05 +0000 Subject: [PATCH] Updated Rector to commit 643814dc92ceacb159adbac79c1ab6fb6e5cc2ee https://github.com/rectorphp/rector-src/commit/643814dc92ceacb159adbac79c1ab6fb6e5cc2ee [TypeDeclaration] Add ChildDoctrineRepositoryClassTypeRector (#5695) --- ...ChildDoctrineRepositoryClassTypeRector.php | 188 ++++++++++++++++++ src/Application/VersionResolver.php | 4 +- src/Config/Level/TypeDeclarationLevel.php | 2 + vendor/composer/autoload_classmap.php | 1 + vendor/composer/autoload_static.php | 1 + 5 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 rules/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector.php diff --git a/rules/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector.php b/rules/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector.php new file mode 100644 index 00000000000..65305fc818d --- /dev/null +++ b/rules/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector.php @@ -0,0 +1,188 @@ +phpDocInfoFactory = $phpDocInfoFactory; + $this->nodeFinder = $nodeFinder; + $this->docBlockUpdater = $docBlockUpdater; + } + public function getRuleDefinition() : RuleDefinition + { + return new RuleDefinition('Add return type to classes that extend Doctrine\\ORM\\EntityRepository', [new CodeSample(<<<'CODE_SAMPLE' +use Doctrine\ORM\EntityRepository; + +/** + * @extends EntityRepository + */ +final class SomeRepository extends EntityRepository +{ + public function getActiveItem() + { + return $this->findOneBy([ + 'something' + ]); + } +} +CODE_SAMPLE +, <<<'CODE_SAMPLE' +use Doctrine\ORM\EntityRepository; + +/** + * @extends EntityRepository + */ +final class SomeRepository extends EntityRepository +{ + public function getActiveItem(): ?SomeType + { + return $this->findOneBy([ + 'something' + ]); + } +} +CODE_SAMPLE +)]); + } + /** + * @return array> + */ + public function getNodeTypes() : array + { + return [Class_::class]; + } + /** + * @param Class_ $node + */ + public function refactor(Node $node) : ?Node + { + if (!$this->isObjectType($node, new ObjectType('Doctrine\\ORM\\EntityRepository'))) { + return null; + } + $entityClassName = $this->resolveEntityClassnameFromPhpDoc($node); + if ($entityClassName === null) { + return null; + } + $hasChanged = \false; + foreach ($node->getMethods() as $classMethod) { + if ($this->shouldSkipClassMethod($classMethod)) { + continue; + } + if ($this->containsMethodCallNamed($classMethod, 'findOneBy')) { + $classMethod->returnType = $this->createNullableType($entityClassName); + } + if ($this->containsMethodCallNamed($classMethod, 'findBy')) { + $classMethod->returnType = new Identifier('array'); + // add docblock with type + $classMethodPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); + $arrayTypeNode = new ArrayTypeNode(new IdentifierTypeNode($entityClassName)); + $classMethodPhpDocInfo->addTagValueNode(new ReturnTagValueNode($arrayTypeNode, '')); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($classMethod); + } + $hasChanged = \true; + // try to figure out the return type + } + if ($hasChanged) { + return $node; + } + return null; + } + private function resolveEntityClassnameFromPhpDoc(Class_ $class) : ?string + { + $classPhpDocInfo = $this->phpDocInfoFactory->createFromNode($class); + // we need a way to resolve entity type... 1st idea is from @extends docblock + if (!$classPhpDocInfo instanceof PhpDocInfo) { + return null; + } + $extendsTagValuePhpDocNodes = $classPhpDocInfo->getTagsByName('extends'); + if ($extendsTagValuePhpDocNodes === []) { + return null; + } + $extendsTagValueNode = $extendsTagValuePhpDocNodes[0]->value; + if (!$extendsTagValueNode instanceof ExtendsTagValueNode) { + return null; + } + // we look for generic type class + if (!$extendsTagValueNode->type instanceof GenericTypeNode) { + return null; + } + $genericTypeNode = $extendsTagValueNode->type; + if ($genericTypeNode->type->name !== 'EntityRepository') { + return null; + } + $entityGenericType = $genericTypeNode->genericTypes[0]; + if (!$entityGenericType instanceof IdentifierTypeNode) { + return null; + } + return $entityGenericType->name; + } + private function containsMethodCallNamed(ClassMethod $classMethod, string $desiredMethodName) : bool + { + return (bool) $this->nodeFinder->findFirst((array) $classMethod->stmts, static function (Node $node) use($desiredMethodName) : bool { + if (!$node instanceof MethodCall) { + return \false; + } + if (!$node->name instanceof Identifier) { + return \false; + } + $currentMethodCallName = $node->name->toString(); + return $currentMethodCallName === $desiredMethodName; + }); + } + private function shouldSkipClassMethod(ClassMethod $classMethod) : bool + { + if (!$classMethod->isPublic()) { + return \true; + } + if ($classMethod->isStatic()) { + return \true; + } + return $classMethod->returnType instanceof Node; + } + private function createNullableType(string $entityClassName) : NullableType + { + $name = new Name($entityClassName); + return new NullableType($name); + } +} diff --git a/src/Application/VersionResolver.php b/src/Application/VersionResolver.php index 36e2b74f9b6..b51ffbb6b26 100644 --- a/src/Application/VersionResolver.php +++ b/src/Application/VersionResolver.php @@ -19,12 +19,12 @@ final class VersionResolver * @api * @var string */ - public const PACKAGE_VERSION = '84639e6aa9c5daa4958121e9aa19e36b5b6bb9b6'; + public const PACKAGE_VERSION = '643814dc92ceacb159adbac79c1ab6fb6e5cc2ee'; /** * @api * @var string */ - public const RELEASE_DATE = '2024-03-06 05:46:53'; + public const RELEASE_DATE = '2024-03-06 20:34:50'; /** * @var int */ diff --git a/src/Config/Level/TypeDeclarationLevel.php b/src/Config/Level/TypeDeclarationLevel.php index d52eb3f2735..2354f05810b 100644 --- a/src/Config/Level/TypeDeclarationLevel.php +++ b/src/Config/Level/TypeDeclarationLevel.php @@ -6,6 +6,7 @@ namespace Rector\Config\Level; use Rector\Contract\Rector\RectorInterface; use Rector\TypeDeclaration\Rector\ArrowFunction\AddArrowFunctionReturnTypeRector; use Rector\TypeDeclaration\Rector\Class_\AddTestsVoidReturnTypeWhereNoReturnRector; +use Rector\TypeDeclaration\Rector\Class_\ChildDoctrineRepositoryClassTypeRector; use Rector\TypeDeclaration\Rector\Class_\MergeDateTimePropertyTypeDeclarationRector; use Rector\TypeDeclaration\Rector\Class_\PropertyTypeFromStrictSetterGetterRector; use Rector\TypeDeclaration\Rector\Class_\ReturnTypeFromStrictTernaryRector; @@ -78,6 +79,7 @@ final class TypeDeclarationLevel TypedPropertyFromStrictSetUpRector::class, ReturnTypeFromStrictNativeCallRector::class, ReturnTypeFromStrictTypedCallRector::class, + ChildDoctrineRepositoryClassTypeRector::class, // param AddMethodCallBasedStrictParamTypeRector::class, ParamTypeByParentCallTypeRector::class, diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 94eedb03876..e391a0f21c9 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -2385,6 +2385,7 @@ return array( 'Rector\\TypeDeclaration\\Rector\\ClassMethod\\StrictArrayParamDimFetchRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector.php', 'Rector\\TypeDeclaration\\Rector\\ClassMethod\\StrictStringParamConcatRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector.php', 'Rector\\TypeDeclaration\\Rector\\Class_\\AddTestsVoidReturnTypeWhereNoReturnRector' => $baseDir . '/rules/TypeDeclaration/Rector/Class_/AddTestsVoidReturnTypeWhereNoReturnRector.php', + 'Rector\\TypeDeclaration\\Rector\\Class_\\ChildDoctrineRepositoryClassTypeRector' => $baseDir . '/rules/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector.php', 'Rector\\TypeDeclaration\\Rector\\Class_\\MergeDateTimePropertyTypeDeclarationRector' => $baseDir . '/rules/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector.php', 'Rector\\TypeDeclaration\\Rector\\Class_\\PropertyTypeFromStrictSetterGetterRector' => $baseDir . '/rules/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector.php', 'Rector\\TypeDeclaration\\Rector\\Class_\\ReturnTypeFromStrictTernaryRector' => $baseDir . '/rules/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector.php', diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 3d3b990f32f..047a525686d 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -2604,6 +2604,7 @@ class ComposerStaticInit2d887a2f87c676eb32b3e04612865e54 'Rector\\TypeDeclaration\\Rector\\ClassMethod\\StrictArrayParamDimFetchRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector.php', 'Rector\\TypeDeclaration\\Rector\\ClassMethod\\StrictStringParamConcatRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector.php', 'Rector\\TypeDeclaration\\Rector\\Class_\\AddTestsVoidReturnTypeWhereNoReturnRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/Class_/AddTestsVoidReturnTypeWhereNoReturnRector.php', + 'Rector\\TypeDeclaration\\Rector\\Class_\\ChildDoctrineRepositoryClassTypeRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector.php', 'Rector\\TypeDeclaration\\Rector\\Class_\\MergeDateTimePropertyTypeDeclarationRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector.php', 'Rector\\TypeDeclaration\\Rector\\Class_\\PropertyTypeFromStrictSetterGetterRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector.php', 'Rector\\TypeDeclaration\\Rector\\Class_\\ReturnTypeFromStrictTernaryRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector.php',