Skip to content

Commit

Permalink
[PHP 8.0] Add return type support to ConstantListClassToEnumRector (#…
Browse files Browse the repository at this point in the history
…2420)

* add return type support

* lock to last stable phpstan

* [ci-review] Rector Rectify

* [ci-review] Rector Rectify

Co-authored-by: GitHub Action <action@github.com>
  • Loading branch information
TomasVotruba and actions-user committed Jun 3, 2022
1 parent e368dab commit 7722a57
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 33 deletions.
2 changes: 1 addition & 1 deletion build/target-repository/composer.json
Expand Up @@ -7,7 +7,7 @@
],
"require": {
"php": "^7.2|^8.0",
"phpstan/phpstan": "^1.7.6"
"phpstan/phpstan": "1.7.8"
},
"autoload": {
"files": [
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Expand Up @@ -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",
Expand Down
3 changes: 3 additions & 0 deletions phpstan.neon
Expand Up @@ -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
@@ -0,0 +1,34 @@
<?php

namespace Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Fixture;

use Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Source\Gear;

final class ChangeReturnType
{
/**
* @return Gear::* $gear
*/
public function changeGear(): string
{
return Gear::FIRST;
}
}

?>
-----
<?php

namespace Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Fixture;

use Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Source\Gear;

final class ChangeReturnType
{
public function changeGear(): \Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Source\Gear
{
return Gear::FIRST;
}
}

?>
38 changes: 26 additions & 12 deletions rules/Php80/NodeAnalyzer/EnumParamAnalyzer.php
Expand Up @@ -6,9 +6,10 @@

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;
Expand All @@ -24,33 +25,46 @@ public function __construct(
) {
}

public function matchClassName(ParameterReflection $parameterReflection, PhpDocInfo $phpDocInfo): ?string
public function matchParameterClassName(ParameterReflection $parameterReflection, PhpDocInfo $phpDocInfo): ?string
{
if (! $parameterReflection instanceof PhpParameterReflection) {
$paramTagValueNode = $phpDocInfo->getParamTagValueByName($parameterReflection->getName());
if (! $paramTagValueNode instanceof ParamTagValueNode) {
return null;
}

$paramTagValueNode = $phpDocInfo->getParamTagValueByName($parameterReflection->getName());
if (! $paramTagValueNode instanceof ParamTagValueNode) {
$className = $this->resolveClassFromConstType($paramTagValueNode->type);
if ($className === null) {
return null;
}

if (! $paramTagValueNode->type instanceof ConstTypeNode) {
if (! $this->reflectionProvider->hasClass($className)) {
return null;
}

$constTypeNode = $paramTagValueNode->type;
if (! $constTypeNode->constExpr instanceof ConstFetchNode) {
return $className;
}

public function matchReturnClassName(PhpDocInfo $phpDocInfo): ?string
{
$returnTagValueNode = $phpDocInfo->getReturnTagValue();
if (! $returnTagValueNode instanceof ReturnTagValueNode) {
return null;
}

$constExpr = $constTypeNode->constExpr;
$className = $constExpr->getAttribute(PhpDocAttributeKey::RESOLVED_CLASS);
return $this->resolveClassFromConstType($returnTagValueNode->type);
}

if (! $this->reflectionProvider->hasClass($className)) {
private function resolveClassFromConstType(TypeNode $typeNode): ?string
{
if (! $typeNode instanceof ConstTypeNode) {
return null;
}

return $className;
if (! $typeNode->constExpr instanceof ConstFetchNode) {
return null;
}

$constExpr = $typeNode->constExpr;
return $constExpr->getAttribute(PhpDocAttributeKey::RESOLVED_CLASS);
}
}
66 changes: 47 additions & 19 deletions rules/Php80/Rector/Class_/ConstantListClassToEnumRector.php
Expand Up @@ -10,6 +10,7 @@
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;
Expand Down Expand Up @@ -88,10 +89,6 @@ public function refactor(Node $node): ?Node

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) {
Expand All @@ -103,12 +100,44 @@ private function refactorClassMethod(ClassMethod $classMethod): ?ClassMethod
return null;
}

// refactor params
$haveParamsChanged = $this->refactorParams($methodReflection, $phpDocInfo, $classMethod);

$hasReturnChanged = $this->refactorReturn($phpDocInfo, $classMethod);
if ($haveParamsChanged) {
return $classMethod;
}

if ($hasReturnChanged) {
return $classMethod;
}

return null;
}

private function getParamByName(ClassMethod $classMethod, string $desiredParamName): ?Param
{
foreach ($classMethod->params as $param) {
if (! $this->nodeNameResolver->isName($param, $desiredParamName)) {
continue;
}

return $param;
}

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->matchClassName($parameterReflection, $phpDocInfo);
$enumLikeClass = $this->enumParamAnalyzer->matchParameterClassName($parameterReflection, $phpDocInfo);
if ($enumLikeClass === null) {
continue;
}
Expand All @@ -127,23 +156,22 @@ private function refactorClassMethod(ClassMethod $classMethod): ?ClassMethod
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $paramTagValueNode);
}

if ($hasNodeChanged) {
return $classMethod;
}

return null;
return $hasNodeChanged;
}

private function getParamByName(ClassMethod $classMethod, string $desiredParamName): ?Param
private function refactorReturn(PhpDocInfo $phpDocInfo, ClassMethod $classMethod): bool
{
foreach ($classMethod->params as $param) {
if (! $this->nodeNameResolver->isName($param, $desiredParamName)) {
continue;
}

return $param;
$returnType = $this->enumParamAnalyzer->matchReturnClassName($phpDocInfo);
if ($returnType === null) {
return false;
}

return null;
$classMethod->returnType = new FullyQualified($returnType);

/** @var ReturnTagValueNode $returnTagValueNode */
$returnTagValueNode = $phpDocInfo->getReturnTagValue();
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $returnTagValueNode);

return true;
}
}

0 comments on commit 7722a57

Please sign in to comment.