From 7722a57d8076577fcd7f8e2b60632fa0f5a38890 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Fri, 3 Jun 2022 09:15:40 +0200 Subject: [PATCH] [PHP 8.0] Add return type support to ConstantListClassToEnumRector (#2420) * add return type support * lock to last stable phpstan * [ci-review] Rector Rectify * [ci-review] Rector Rectify Co-authored-by: GitHub Action --- build/target-repository/composer.json | 2 +- composer.json | 2 +- phpstan.neon | 3 + .../Fixture/change_return_type.php.inc | 34 ++++++++ .../Php80/NodeAnalyzer/EnumParamAnalyzer.php | 44 ++++++---- .../Class_/ConstantListClassToEnumRector.php | 80 +++++++++++++------ 6 files changed, 122 insertions(+), 43 deletions(-) create mode 100644 rules-tests/Php80/Rector/Class_/ConstantListClassToEnumRector/Fixture/change_return_type.php.inc diff --git a/build/target-repository/composer.json b/build/target-repository/composer.json index 37f9ef4133a..5e891e9ea77 100644 --- a/build/target-repository/composer.json +++ b/build/target-repository/composer.json @@ -7,7 +7,7 @@ ], "require": { "php": "^7.2|^8.0", - "phpstan/phpstan": "^1.7.6" + "phpstan/phpstan": "1.7.8" }, "autoload": { "files": [ diff --git a/composer.json b/composer.json index 87334a8e833..c9302de6451 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "nikic/php-parser": "^4.14.0", "ondram/ci-detector": "^4.1", "phpstan/phpdoc-parser": "^1.5.1", - "phpstan/phpstan": "^1.7.6", + "phpstan/phpstan": "1.7.8", "phpstan/phpstan-phpunit": "^1.0", "react/child-process": "^0.6.4", "react/event-loop": "^1.3", diff --git a/phpstan.neon b/phpstan.neon index b8faf77878f..4e1eb6da075 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -709,3 +709,6 @@ parameters: - '#Register "Rector\\Php80\\Rector\\Class_\\ConstantListClassToEnumRector" service to "php80\.php" config set#' - '#Rule Rector\\Php80\\Rector\\Class_\\ConstantListClassToEnumRector must implements Rector\\VersionBonding\\Contract\\MinPhpVersionInterface#' - '#Register "Rector\\DowngradePhp80\\Rector\\Enum_\\DowngradeEnumToConstantListClassRector" service to "downgrade\-php80\.php" config set#' + - + message: '#Method "refactor(Params|Return)\(\)" returns bool type, so the name should start with is/has/was#' + path: rules/Php80/Rector/Class_/ConstantListClassToEnumRector.php diff --git a/rules-tests/Php80/Rector/Class_/ConstantListClassToEnumRector/Fixture/change_return_type.php.inc b/rules-tests/Php80/Rector/Class_/ConstantListClassToEnumRector/Fixture/change_return_type.php.inc new file mode 100644 index 00000000000..f6a25692b06 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ConstantListClassToEnumRector/Fixture/change_return_type.php.inc @@ -0,0 +1,34 @@ + +----- + diff --git a/rules/Php80/NodeAnalyzer/EnumParamAnalyzer.php b/rules/Php80/NodeAnalyzer/EnumParamAnalyzer.php index 91783b73cc0..d9d513ecbab 100644 --- a/rules/Php80/NodeAnalyzer/EnumParamAnalyzer.php +++ b/rules/Php80/NodeAnalyzer/EnumParamAnalyzer.php @@ -6,9 +6,10 @@ namespace Rector\Php80\NodeAnalyzer; use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode; use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; +use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode; +use PHPStan\PhpDocParser\Ast\Type\TypeNode; use PHPStan\Reflection\ParameterReflection; -use PHPStan\Reflection\Php\PhpParameterReflection; use PHPStan\Reflection\ReflectionProvider; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey; @@ -24,33 +25,46 @@ final class EnumParamAnalyzer ) { } - public function matchClassName(ParameterReflection $parameterReflection, PhpDocInfo $phpDocInfo): ?string + public function matchParameterClassName(ParameterReflection $parameterReflection, PhpDocInfo $phpDocInfo): ?string { - if (! $parameterReflection instanceof PhpParameterReflection) { - return null; - } - $paramTagValueNode = $phpDocInfo->getParamTagValueByName($parameterReflection->getName()); if (! $paramTagValueNode instanceof ParamTagValueNode) { return null; } - if (! $paramTagValueNode->type instanceof ConstTypeNode) { + $className = $this->resolveClassFromConstType($paramTagValueNode->type); + if ($className === null) { return null; } - $constTypeNode = $paramTagValueNode->type; - if (! $constTypeNode->constExpr instanceof ConstFetchNode) { - return null; - } - - $constExpr = $constTypeNode->constExpr; - $className = $constExpr->getAttribute(PhpDocAttributeKey::RESOLVED_CLASS); - if (! $this->reflectionProvider->hasClass($className)) { return null; } return $className; } + + public function matchReturnClassName(PhpDocInfo $phpDocInfo): ?string + { + $returnTagValueNode = $phpDocInfo->getReturnTagValue(); + if (! $returnTagValueNode instanceof ReturnTagValueNode) { + return null; + } + + return $this->resolveClassFromConstType($returnTagValueNode->type); + } + + private function resolveClassFromConstType(TypeNode $typeNode): ?string + { + if (! $typeNode instanceof ConstTypeNode) { + return null; + } + + if (! $typeNode->constExpr instanceof ConstFetchNode) { + return null; + } + + $constExpr = $typeNode->constExpr; + return $constExpr->getAttribute(PhpDocAttributeKey::RESOLVED_CLASS); + } } diff --git a/rules/Php80/Rector/Class_/ConstantListClassToEnumRector.php b/rules/Php80/Rector/Class_/ConstantListClassToEnumRector.php index 73d3106c4e6..c70875864a9 100644 --- a/rules/Php80/Rector/Class_/ConstantListClassToEnumRector.php +++ b/rules/Php80/Rector/Class_/ConstantListClassToEnumRector.php @@ -10,6 +10,7 @@ use PhpParser\Node\Param; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; +use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\ParametersAcceptorSelector; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; @@ -88,10 +89,6 @@ CODE_SAMPLE private function refactorClassMethod(ClassMethod $classMethod): ?ClassMethod { - if ($classMethod->params === []) { - return null; - } - // enum param types doc requires a docblock $phpDocInfo = $this->phpDocInfoFactory->createFromNode($classMethod); if (! $phpDocInfo instanceof PhpDocInfo) { @@ -103,31 +100,15 @@ CODE_SAMPLE return null; } - $hasNodeChanged = false; + // refactor params + $haveParamsChanged = $this->refactorParams($methodReflection, $phpDocInfo, $classMethod); - $parametersAcceptor = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants()); - - foreach ($parametersAcceptor->getParameters() as $parameterReflection) { - $enumLikeClass = $this->enumParamAnalyzer->matchClassName($parameterReflection, $phpDocInfo); - if ($enumLikeClass === null) { - continue; - } - - $param = $this->getParamByName($classMethod, $parameterReflection->getName()); - if (! $param instanceof Param) { - continue; - } - - // change and remove - $param->type = new FullyQualified($enumLikeClass); - $hasNodeChanged = true; - - /** @var ParamTagValueNode $paramTagValueNode */ - $paramTagValueNode = $phpDocInfo->getParamTagValueByName($parameterReflection->getName()); - $this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $paramTagValueNode); + $hasReturnChanged = $this->refactorReturn($phpDocInfo, $classMethod); + if ($haveParamsChanged) { + return $classMethod; } - if ($hasNodeChanged) { + if ($hasReturnChanged) { return $classMethod; } @@ -146,4 +127,51 @@ CODE_SAMPLE return null; } + + private function refactorParams( + MethodReflection $methodReflection, + PhpDocInfo $phpDocInfo, + ClassMethod $classMethod + ): bool { + $hasNodeChanged = false; + + $parametersAcceptor = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants()); + foreach ($parametersAcceptor->getParameters() as $parameterReflection) { + $enumLikeClass = $this->enumParamAnalyzer->matchParameterClassName($parameterReflection, $phpDocInfo); + if ($enumLikeClass === null) { + continue; + } + + $param = $this->getParamByName($classMethod, $parameterReflection->getName()); + if (! $param instanceof Param) { + continue; + } + + // change and remove + $param->type = new FullyQualified($enumLikeClass); + $hasNodeChanged = true; + + /** @var ParamTagValueNode $paramTagValueNode */ + $paramTagValueNode = $phpDocInfo->getParamTagValueByName($parameterReflection->getName()); + $this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $paramTagValueNode); + } + + return $hasNodeChanged; + } + + private function refactorReturn(PhpDocInfo $phpDocInfo, ClassMethod $classMethod): bool + { + $returnType = $this->enumParamAnalyzer->matchReturnClassName($phpDocInfo); + if ($returnType === null) { + return false; + } + + $classMethod->returnType = new FullyQualified($returnType); + + /** @var ReturnTagValueNode $returnTagValueNode */ + $returnTagValueNode = $phpDocInfo->getReturnTagValue(); + $this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $returnTagValueNode); + + return true; + } }