mirror of
https://github.com/rectorphp/rector.git
synced 2024-05-31 16:30:51 +00:00
[Downgrade] Add class method param to DowngradeEnumToConstantListClassRector (#2417)
* add param enum downgrade * [ci-review] Rector Rectify Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
parent
340f5b999c
commit
e368dabaed
|
@ -1,4 +1,4 @@
|
|||
# 513 Rules Overview
|
||||
# 516 Rules Overview
|
||||
|
||||
<br>
|
||||
|
||||
|
@ -38,10 +38,12 @@
|
|||
|
||||
- [DowngradePhp74](#downgradephp74) (12)
|
||||
|
||||
- [DowngradePhp80](#downgradephp80) (28)
|
||||
- [DowngradePhp80](#downgradephp80) (29)
|
||||
|
||||
- [DowngradePhp81](#downgradephp81) (9)
|
||||
|
||||
- [DowngradePhp82](#downgradephp82) (1)
|
||||
|
||||
- [EarlyReturn](#earlyreturn) (11)
|
||||
|
||||
- [MysqlToMysqli](#mysqltomysqli) (4)
|
||||
|
@ -70,7 +72,7 @@
|
|||
|
||||
- [Php74](#php74) (14)
|
||||
|
||||
- [Php80](#php80) (17)
|
||||
- [Php80](#php80) (18)
|
||||
|
||||
- [Php81](#php81) (9)
|
||||
|
||||
|
@ -2192,13 +2194,12 @@ Changes `$this->...` and static:: to self:: or vise versa for given types
|
|||
|
||||
```php
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Rector\CodingStyle\Enum\PreferenceSelfThis;
|
||||
use Rector\CodingStyle\Rector\MethodCall\PreferThisOrSelfMethodCallRector;
|
||||
use Rector\Config\RectorConfig;
|
||||
|
||||
return static function (RectorConfig $rectorConfig): void {
|
||||
$rectorConfig->ruleWithConfiguration(PreferThisOrSelfMethodCallRector::class, [
|
||||
TestCase::class => PreferenceSelfThis::PREFER_SELF(),
|
||||
TestCase::class => 'prefer_self',
|
||||
]);
|
||||
};
|
||||
```
|
||||
|
@ -5413,6 +5414,26 @@ Add parentheses around non-dereferenceable expressions.
|
|||
|
||||
<br>
|
||||
|
||||
### DowngradeEnumToConstantListClassRector
|
||||
|
||||
Downgrade enum to constant list class
|
||||
|
||||
- class: [`Rector\DowngradePhp80\Rector\Enum_\DowngradeEnumToConstantListClassRector`](../rules/DowngradePhp80/Rector/Enum_/DowngradeEnumToConstantListClassRector.php)
|
||||
|
||||
```diff
|
||||
-enum Direction
|
||||
+class Direction
|
||||
{
|
||||
- case LEFT;
|
||||
+ public const LEFT = 'left';
|
||||
|
||||
- case RIGHT;
|
||||
+ public const RIGHT = 'right';
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### DowngradeMatchToSwitchRector
|
||||
|
||||
Downgrade `match()` to `switch()`
|
||||
|
@ -6050,6 +6071,30 @@ Remove "readonly" property type, add a "@readonly" tag instead
|
|||
|
||||
<br>
|
||||
|
||||
## DowngradePhp82
|
||||
|
||||
### DowngradeReadonlyClassRector
|
||||
|
||||
Remove "readonly" class type, decorate all properties to "readonly"
|
||||
|
||||
- class: [`Rector\DowngradePhp82\Rector\Class_\DowngradeReadonlyClassRector`](../rules/DowngradePhp82/Rector/Class_/DowngradeReadonlyClassRector.php)
|
||||
|
||||
```diff
|
||||
-final readonly class SomeClass
|
||||
+final class SomeClass
|
||||
{
|
||||
- public string $foo;
|
||||
+ public readonly string $foo;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->foo = 'foo';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
## EarlyReturn
|
||||
|
||||
### ChangeAndIfToEarlyReturnRector
|
||||
|
@ -8178,6 +8223,26 @@ Change simple property init and assign to constructor promotion
|
|||
|
||||
<br>
|
||||
|
||||
### ConstantListClassToEnumRector
|
||||
|
||||
Upgrade constant list classes to full blown enum
|
||||
|
||||
- class: [`Rector\Php80\Rector\Class_\ConstantListClassToEnumRector`](../rules/Php80/Rector/Class_/ConstantListClassToEnumRector.php)
|
||||
|
||||
```diff
|
||||
-class Direction
|
||||
+enum Direction
|
||||
{
|
||||
- public const LEFT = 'left';
|
||||
+ case LEFT;
|
||||
|
||||
- public const RIGHT = 'right';
|
||||
+ case RIGHT;
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### DoctrineAnnotationClassToAttributeRector
|
||||
|
||||
Refactor Doctrine `@annotation` annotated class to a PHP 8.0 attribute class
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\DowngradePhp80\Rector\Enum_\DowngradeEnumToConstantListClassRector\Fixture;
|
||||
|
||||
use Rector\Tests\DowngradePhp80\Rector\Enum_\DowngradeEnumToConstantListClassRector\Source\AnythingYouWant;
|
||||
|
||||
final class NoTypeParamMethod
|
||||
{
|
||||
public function with(AnythingYouWant $anythingYouWant)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\DowngradePhp80\Rector\Enum_\DowngradeEnumToConstantListClassRector\Fixture;
|
||||
|
||||
use Rector\Tests\DowngradePhp80\Rector\Enum_\DowngradeEnumToConstantListClassRector\Source\AnythingYouWant;
|
||||
|
||||
final class NoTypeParamMethod
|
||||
{
|
||||
/**
|
||||
* @param \Rector\Tests\DowngradePhp80\Rector\Enum_\DowngradeEnumToConstantListClassRector\Source\AnythingYouWant::* $anythingYouWant
|
||||
*/
|
||||
public function with($anythingYouWant)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\DowngradePhp80\Rector\Enum_\DowngradeEnumToConstantListClassRector\Fixture;
|
||||
|
||||
use Rector\Tests\DowngradePhp80\Rector\Enum_\DowngradeEnumToConstantListClassRector\Source\GearValue;
|
||||
|
||||
final class ParamMethod
|
||||
{
|
||||
public function changeGear(GearValue $gearValue)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\DowngradePhp80\Rector\Enum_\DowngradeEnumToConstantListClassRector\Fixture;
|
||||
|
||||
use Rector\Tests\DowngradePhp80\Rector\Enum_\DowngradeEnumToConstantListClassRector\Source\GearValue;
|
||||
|
||||
final class ParamMethod
|
||||
{
|
||||
/**
|
||||
* @param \Rector\Tests\DowngradePhp80\Rector\Enum_\DowngradeEnumToConstantListClassRector\Source\GearValue::* $gearValue
|
||||
*/
|
||||
public function changeGear(string $gearValue)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\DowngradePhp80\Rector\Enum_\DowngradeEnumToConstantListClassRector\Source;
|
||||
|
||||
enum AnythingYouWant
|
||||
{
|
||||
const LEFT = 'left';
|
||||
|
||||
const TWO = 5;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\DowngradePhp80\Rector\Enum_\DowngradeEnumToConstantListClassRector\Source;
|
||||
|
||||
enum GearValue
|
||||
{
|
||||
case FIRST;
|
||||
|
||||
case SECOND;
|
||||
}
|
97
rules/DowngradePhp80/NodeAnalyzer/EnumAnalyzer.php
Normal file
97
rules/DowngradePhp80/NodeAnalyzer/EnumAnalyzer.php
Normal file
|
@ -0,0 +1,97 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\DowngradePhp80\NodeAnalyzer;
|
||||
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Stmt\Enum_;
|
||||
use PhpParser\Node\Stmt\EnumCase;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Type\FloatType;
|
||||
use PHPStan\Type\IntegerType;
|
||||
use PHPStan\Type\StringType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\PhpParser\AstResolver;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
|
||||
final class EnumAnalyzer
|
||||
{
|
||||
public function __construct(
|
||||
private readonly AstResolver $astResolver,
|
||||
private readonly NodeTypeResolver $nodeTypeResolver
|
||||
) {
|
||||
}
|
||||
|
||||
public function resolveType(ClassReflection $classReflection): ?Identifier
|
||||
{
|
||||
$class = $this->astResolver->resolveClassFromClassReflection($classReflection, $classReflection->getName());
|
||||
if (! $class instanceof Enum_) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
$scalarType = $class->scalarType;
|
||||
if ($scalarType instanceof Identifier) {
|
||||
// can be only int or string
|
||||
return $scalarType;
|
||||
}
|
||||
|
||||
$enumExprTypes = $this->resolveEnumExprTypes($class);
|
||||
|
||||
$enumExprTypeClasses = [];
|
||||
|
||||
foreach ($enumExprTypes as $enumExprType) {
|
||||
$enumExprTypeClasses[] = $enumExprType::class;
|
||||
}
|
||||
|
||||
$uniqueEnumExprTypeClasses = array_unique($enumExprTypeClasses);
|
||||
if (count($uniqueEnumExprTypeClasses) === 1) {
|
||||
$uniqueEnumExprTypeClass = $uniqueEnumExprTypeClasses[0];
|
||||
if (is_a($uniqueEnumExprTypeClass, StringType::class, true)) {
|
||||
return new Identifier('string');
|
||||
}
|
||||
|
||||
if (is_a($uniqueEnumExprTypeClass, IntegerType::class, true)) {
|
||||
return new Identifier('int');
|
||||
}
|
||||
|
||||
if (is_a($uniqueEnumExprTypeClass, FloatType::class, true)) {
|
||||
return new Identifier('float');
|
||||
}
|
||||
}
|
||||
|
||||
// unknown or multiple types
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Type[]
|
||||
*/
|
||||
private function resolveEnumExprTypes(Enum_ $enum): array
|
||||
{
|
||||
$enumExprTypes = [];
|
||||
|
||||
foreach ($enum->stmts as $classStmt) {
|
||||
if (! $classStmt instanceof EnumCase) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$enumExprTypes[] = $this->resolveEnumCaseType($classStmt);
|
||||
}
|
||||
|
||||
return $enumExprTypes;
|
||||
}
|
||||
|
||||
private function resolveEnumCaseType(EnumCase $enumCase): Type
|
||||
{
|
||||
$classExpr = $enumCase->expr;
|
||||
if ($classExpr instanceof Expr) {
|
||||
return $this->nodeTypeResolver->getType($classExpr);
|
||||
}
|
||||
|
||||
// in case of no value, fallback to string type
|
||||
return new StringType();
|
||||
}
|
||||
}
|
|
@ -5,9 +5,20 @@ declare(strict_types=1);
|
|||
namespace Rector\DowngradePhp80\Rector\Enum_;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Enum_;
|
||||
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\DowngradePhp80\NodeAnalyzer\EnumAnalyzer;
|
||||
use Rector\Php81\NodeFactory\ClassFromEnumFactory;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
|
@ -18,7 +29,9 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
|||
final class DowngradeEnumToConstantListClassRector extends AbstractRector
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ClassFromEnumFactory $classFromEnumFactory
|
||||
private readonly ClassFromEnumFactory $classFromEnumFactory,
|
||||
private readonly ReflectionProvider $reflectionProvider,
|
||||
private readonly EnumAnalyzer $enumAnalyzer,
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -53,14 +66,67 @@ CODE_SAMPLE
|
|||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [Enum_::class];
|
||||
return [Enum_::class, ClassMethod::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Enum_ $node
|
||||
* @param Enum_|ClassMethod $node
|
||||
*/
|
||||
public function refactor(Node $node): Class_
|
||||
public function refactor(Node $node): Class_|ClassMethod|null
|
||||
{
|
||||
return $this->classFromEnumFactory->createFromEnum($node);
|
||||
if ($node instanceof Enum_) {
|
||||
return $this->classFromEnumFactory->createFromEnum($node);
|
||||
}
|
||||
|
||||
$hasChanged = false;
|
||||
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
|
||||
|
||||
foreach ($node->params as $param) {
|
||||
if (! $param->type instanceof Name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// is enum type?
|
||||
$typeName = $this->getName($param->type);
|
||||
|
||||
if (! $this->reflectionProvider->hasClass($typeName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$classLikeReflection = $this->reflectionProvider->getClass($typeName);
|
||||
if (! $classLikeReflection->isEnum()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$param->type = $this->resolveParamType($classLikeReflection);
|
||||
$hasChanged = true;
|
||||
|
||||
$this->decorateParamDocType($classLikeReflection, $param, $phpDocInfo);
|
||||
}
|
||||
|
||||
if ($hasChanged) {
|
||||
return $node;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function resolveParamType(ClassReflection $classReflection): ?Identifier
|
||||
{
|
||||
return $this->enumAnalyzer->resolveType($classReflection);
|
||||
}
|
||||
|
||||
private function decorateParamDocType(
|
||||
ClassReflection $classReflection,
|
||||
Param $param,
|
||||
PhpDocInfo $phpDocInfo
|
||||
): void {
|
||||
$constFetchNode = new ConstFetchNode('\\' . $classReflection->getName(), '*');
|
||||
$constTypeNode = new ConstTypeNode($constFetchNode);
|
||||
$paramName = '$' . $this->getName($param);
|
||||
|
||||
$paramTagValueNode = new ParamTagValueNode($constTypeNode, false, $paramName, '');
|
||||
|
||||
$phpDocInfo->addTagValueNode($paramTagValueNode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ final readonly class SomeClass
|
|||
CODE_SAMPLE
|
||||
,
|
||||
<<<'CODE_SAMPLE'
|
||||
final readonly class SomeClass
|
||||
final class SomeClass
|
||||
{
|
||||
public readonly string $foo;
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Rector\Core\NodeAnalyzer;
|
||||
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassConst;
|
||||
use Rector\Core\PhpParser\Node\BetterNodeFinder;
|
||||
|
@ -27,7 +28,7 @@ final class EnumAnalyzer
|
|||
return false;
|
||||
}
|
||||
|
||||
if ($class->extends === null) {
|
||||
if (! $class->extends instanceof Name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ final class ClassLikeAstResolver
|
|||
* Parsing files is very heavy performance, so this will help to leverage it
|
||||
* The value can be also null, as the method might not exist in the class.
|
||||
*
|
||||
* @var array<class-string, Class_|Trait_|Interface_|null>
|
||||
* @var array<class-string, Class_|Trait_|Interface_|Enum_|null>
|
||||
*/
|
||||
private array $classLikesByName = [];
|
||||
|
||||
|
@ -32,7 +32,7 @@ final class ClassLikeAstResolver
|
|||
|
||||
public function resolveClassFromClassReflection(
|
||||
ClassReflection $classReflection,
|
||||
string $className
|
||||
string $desiredClassName
|
||||
): Trait_ | Class_ | Interface_ | Enum_ | null {
|
||||
if ($classReflection->isBuiltin()) {
|
||||
return null;
|
||||
|
@ -58,12 +58,12 @@ final class ClassLikeAstResolver
|
|||
return null;
|
||||
}
|
||||
|
||||
/** @var array<Class_|Trait_|Interface_> $classLikes */
|
||||
/** @var array<Class_|Trait_|Interface_|Enum_> $classLikes */
|
||||
$classLikes = $this->betterNodeFinder->findInstanceOf($stmts, ClassLike::class);
|
||||
|
||||
$reflectionClassName = $classReflection->getName();
|
||||
foreach ($classLikes as $classLike) {
|
||||
if ($reflectionClassName !== $className) {
|
||||
if ($reflectionClassName !== $desiredClassName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user