mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-25 04:12:36 +00:00
[Transform][Php81] Add configurable ReturnTypeWillChangeRector (#1548)
Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
parent
5af50aff13
commit
35ef77b4f2
|
@ -33,12 +33,12 @@ final class PhpDocFromTypeDeclarationDecorator
|
|||
/**
|
||||
* @var class-string<ReturnTypeWillChange>
|
||||
*/
|
||||
private const RETURN_TYPE_WILL_CHANGE_ATTRIBUTE = 'ReturnTypeWillChange';
|
||||
final public const RETURN_TYPE_WILL_CHANGE_ATTRIBUTE = 'ReturnTypeWillChange';
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, string[]>>
|
||||
*/
|
||||
private const ADD_RETURN_TYPE_WILL_CHANGE = [
|
||||
final public const ADD_RETURN_TYPE_WILL_CHANGE = [
|
||||
'PHPStan\Type\MixedType' => [
|
||||
'ArrayAccess' => ['offsetGet'],
|
||||
],
|
||||
|
|
|
@ -120,7 +120,9 @@ parameters:
|
|||
|
||||
-
|
||||
message: '#Nested foreach with empty statement is not allowed#'
|
||||
path: packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/TagValueNodeReprintTest.php
|
||||
paths:
|
||||
- packages-tests/BetterPhpDocParser/PhpDocParser/TagValueNodeReprint/TagValueNodeReprintTest.php
|
||||
- rules/Transform/Rector/ClassMethod/ReturnTypeWillChangeRector.php
|
||||
|
||||
-
|
||||
message: '#Function "dump\(\)" cannot be used/left in the code#'
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Transform\Rector\ClassMethod\ReturnTypeWillChangeRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
final class CustomConfigTest extends AbstractRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData()
|
||||
*/
|
||||
public function test(SmartFileInfo $fileInfo): void
|
||||
{
|
||||
$this->doTestFileInfo($fileInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<SmartFileInfo>
|
||||
*/
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureCustomConfig');
|
||||
}
|
||||
|
||||
public function provideConfigFilePath(): string
|
||||
{
|
||||
return __DIR__ . '/config/custom_config.php';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\Transform\Rector\ClassMethod\ReturnTypeWillChangeRector\Fixture;
|
||||
|
||||
use Rector\Tests\Transform\Rector\ClassMethod\ReturnTypeWillChangeRector\Source\Options;
|
||||
|
||||
class SomeClass extends Options
|
||||
{
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\Transform\Rector\ClassMethod\ReturnTypeWillChangeRector\Fixture;
|
||||
|
||||
use Rector\Tests\Transform\Rector\ClassMethod\ReturnTypeWillChangeRector\Source\Options;
|
||||
|
||||
class SomeClass extends Options
|
||||
{
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\Transform\Rector\ClassMethod\ReturnTypeWillChangeRector\FixtureCustomConfig;
|
||||
|
||||
use Rector\Tests\Transform\Rector\ClassMethod\ReturnTypeWillChangeRector\Source\Options;
|
||||
|
||||
class SomeClass extends Options
|
||||
{
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
}
|
||||
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\Transform\Rector\ClassMethod\ReturnTypeWillChangeRector\FixtureCustomConfig;
|
||||
|
||||
use Rector\Tests\Transform\Rector\ClassMethod\ReturnTypeWillChangeRector\Source\Options;
|
||||
|
||||
class SomeClass extends Options
|
||||
{
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Transform\Rector\ClassMethod\ReturnTypeWillChangeRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
final class ReturnTypeWillChangeRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData()
|
||||
*/
|
||||
public function test(SmartFileInfo $fileInfo): void
|
||||
{
|
||||
$this->doTestFileInfo($fileInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<SmartFileInfo>
|
||||
*/
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
|
||||
}
|
||||
|
||||
public function provideConfigFilePath(): string
|
||||
{
|
||||
return __DIR__ . '/config/configured_rule.php';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Transform\Rector\ClassMethod\ReturnTypeWillChangeRector\Source;
|
||||
|
||||
use ArrayAccess;
|
||||
|
||||
abstract class Options implements ArrayAccess
|
||||
{
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Transform\Rector\ClassMethod\ReturnTypeWillChangeRector;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
$services->set(ReturnTypeWillChangeRector::class);
|
||||
};
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Transform\Rector\ClassMethod\ReturnTypeWillChangeRector;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
$services->set(ReturnTypeWillChangeRector::class)
|
||||
->configure([
|
||||
'ArrayAccess' => ['offsetExists'],
|
||||
]);
|
||||
};
|
|
@ -0,0 +1,156 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Transform\Rector\ClassMethod;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Interface_;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use Rector\BetterPhpDocParser\PhpDocParser\PhpDocFromTypeDeclarationDecorator;
|
||||
use Rector\Core\Contract\Rector\AllowEmptyConfigurableRectorInterface;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\ValueObject\PhpVersionFeature;
|
||||
use Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer;
|
||||
use Rector\PhpAttribute\Printer\PhpAttributeGroupFactory;
|
||||
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
|
||||
/**
|
||||
* @see \Rector\Tests\Transform\Rector\ClassMethod\ReturnTypeWillChangeRector\ReturnTypeWillChangeRectorTest
|
||||
*/
|
||||
final class ReturnTypeWillChangeRector extends AbstractRector implements AllowEmptyConfigurableRectorInterface, MinPhpVersionInterface
|
||||
{
|
||||
/**
|
||||
* @var array<string, string[]>
|
||||
*/
|
||||
private array $classMethodsOfClass = [];
|
||||
|
||||
public function __construct(
|
||||
private readonly PhpAttributeAnalyzer $phpAttributeAnalyzer,
|
||||
private readonly PhpAttributeGroupFactory $phpAttributeGroupFactory
|
||||
) {
|
||||
}
|
||||
|
||||
public function getRuleDefinition(): RuleDefinition
|
||||
{
|
||||
return new RuleDefinition('Add #[\ReturnTypeWillChange] attribute to configured instanceof class with methods', [
|
||||
new ConfiguredCodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
class SomeClass implements ArrayAccess
|
||||
{
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
,
|
||||
<<<'CODE_SAMPLE'
|
||||
class SomeClass implements ArrayAccess
|
||||
{
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
,
|
||||
[
|
||||
'ArrayAccess' => ['offsetGet'],
|
||||
]
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<class-string<Node>>
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [ClassMethod::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if ($this->phpAttributeAnalyzer->hasPhpAttribute($node, 'ReturnTypeWillChange')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($node->returnType !== null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$classLike = $this->betterNodeFinder->findParentByTypes($node, [Class_::class, Interface_::class]);
|
||||
if (! $classLike instanceof ClassLike) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var array<string, string[]> $classMethodsOfClass */
|
||||
$classMethodsOfClass = array_merge_recursive($this->resolveDefaultConfig(), $this->classMethodsOfClass);
|
||||
$className = (string) $this->nodeNameResolver->getName($classLike);
|
||||
$objectType = new ObjectType($className);
|
||||
$methodName = $this->nodeNameResolver->getName($node);
|
||||
$hasChanged = false;
|
||||
|
||||
foreach ($classMethodsOfClass as $class => $methods) {
|
||||
$configuredClassObjectType = new ObjectType($class);
|
||||
if (! $configuredClassObjectType->isSuperTypeOf($objectType)->yes()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! in_array($methodName, $methods, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$attributeGroup = $this->phpAttributeGroupFactory->createFromClass(
|
||||
PhpDocFromTypeDeclarationDecorator::RETURN_TYPE_WILL_CHANGE_ATTRIBUTE
|
||||
);
|
||||
$node->attrGroups[] = $attributeGroup;
|
||||
$hasChanged = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (! $hasChanged) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$this->classMethodsOfClass = $configuration;
|
||||
}
|
||||
|
||||
public function provideMinPhpVersion(): int
|
||||
{
|
||||
return PhpVersionFeature::RETURN_TYPE_WILL_CHANGE_ATTRIBUTE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string[]>
|
||||
*/
|
||||
private function resolveDefaultConfig(): array
|
||||
{
|
||||
$configuration = [];
|
||||
|
||||
foreach (PhpDocFromTypeDeclarationDecorator::ADD_RETURN_TYPE_WILL_CHANGE as $classWithMethods) {
|
||||
foreach ($classWithMethods as $class => $methods) {
|
||||
$configuration[$class] = array_merge($configuration[$class] ?? [], $methods);
|
||||
}
|
||||
}
|
||||
|
||||
return $configuration;
|
||||
}
|
||||
}
|
|
@ -555,6 +555,12 @@ final class PhpVersionFeature
|
|||
*/
|
||||
final public const ARRAY_SPREAD_STRING_KEYS = PhpVersion::PHP_81;
|
||||
|
||||
/**
|
||||
* @see https://wiki.php.net/rfc/internal_method_return_types
|
||||
* @var int
|
||||
*/
|
||||
final public const RETURN_TYPE_WILL_CHANGE_ATTRIBUTE = PhpVersion::PHP_81;
|
||||
|
||||
/**
|
||||
* @see https://wiki.php.net/rfc/deprecate_dynamic_properties
|
||||
* @var int
|
||||
|
|
Loading…
Reference in New Issue
Block a user