[DowngradePhp72] Allow empty config on DowngradeParameterTypeWideningRector (#1451)

* [DowngradePhp72] Allow empty config on DowngradeParameterTypeWideningRector

* [ci-review] Rector Rectify

* fix

* early check on empty unsafeTypesToMethods

* added back removed fixture

* final touch: eol

Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Abdul Malik Ikhsan 2021-12-11 00:57:03 +07:00 committed by GitHub
parent 8d6eb87e99
commit 4681067be5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 513 additions and 18 deletions

View File

@ -30,10 +30,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->set(DowngradeParameterTypeWideningRector::class)
->configure([
DowngradeParameterTypeWideningRector::UNSAFE_TYPES_TO_METHODS => [
LoaderInterface::class => ['load'],
Loader::class => ['import']
],
LoaderInterface::class => ['load'],
Loader::class => ['import'],
]);
};

View File

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector;
use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;
final class EmptyConfigTest extends AbstractRectorTestCase
{
/**
* @requires PHP 7.2
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
{
$this->doTestFileInfo($fileInfo);
}
/**
* @return Iterator<SmartFileInfo>
*/
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureEmptyConfig');
}
public function provideConfigFilePath(): string
{
return __DIR__ . '/config/empty_config.php';
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\FixtureEmptyConfig;
class SomeClass
{
public function hello(string $world = 'world') {
printf('Hello %s', $world);
}
}
class SomeOtherClassUsingAnAnonymousClass
{
public function doSomething(): void
{
$class = new class () extends SomeClass {
public function hello(string $world = 'world') {
printf('Hi %s', $world);
}
};
}
}
?>
-----
<?php
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\FixtureEmptyConfig;
class SomeClass
{
/**
* @param string $world
*/
public function hello($world = 'world') {
printf('Hello %s', $world);
}
}
class SomeOtherClassUsingAnAnonymousClass
{
public function doSomething(): void
{
$class = new class () extends SomeClass {
/**
* @param string $world
*/
public function hello($world = 'world') {
printf('Hi %s', $world);
}
};
}
}
?>

View File

@ -0,0 +1,65 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\FixtureEmptyConfig;
interface SomeInterface
{
public function hello(string $world = 'world');
}
class SomeClass2
{
public function doSomething(): void
{
$class = new class (function () {
return $this->doSomethingElse();
}) implements SomeInterface {
public function hello(string $world = 'world') {
printf('Hi %s', $world);
}
};
}
public function doSomethingElse(): void
{
print('Hello again');
}
}
?>
-----
<?php
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\FixtureEmptyConfig;
interface SomeInterface
{
/**
* @param string $world
*/
public function hello($world = 'world');
}
class SomeClass2
{
public function doSomething(): void
{
$class = new class (function () {
return $this->doSomethingElse();
}) implements SomeInterface {
/**
* @param string $world
*/
public function hello($world = 'world') {
printf('Hi %s', $world);
}
};
}
public function doSomethingElse(): void
{
print('Hello again');
}
}
?>

View File

@ -0,0 +1,57 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\FixtureEmptyConfig;
interface SomeInterface2
{
public function test(\SplFileInfo $file);
}
abstract class SomeAbstractClass implements SomeInterface2
{
public function test2(\SplFileInfo $file, string $world = 'world') {
printf('Hello %s', $world);
}
}
return new class extends SomeAbstractClass {
public function test(\SplFileInfo $file) {
$this->test2($file);
}
};
?>
-----
<?php
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\FixtureEmptyConfig;
interface SomeInterface2
{
/**
* @param \SplFileInfo $file
*/
public function test($file);
}
abstract class SomeAbstractClass implements SomeInterface2
{
/**
* @param \SplFileInfo $file
* @param string $world
*/
public function test2($file, $world = 'world') {
printf('Hello %s', $world);
}
}
return new class extends SomeAbstractClass {
/**
* @param \SplFileInfo $file
*/
public function test($file) {
$this->test2($file);
}
};
?>

View File

@ -0,0 +1,52 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\FixtureEmptyConfig;
interface SomeContainerInterface
{
public function has(string $id);
}
final class SomeContainerBuilder extends SomeUniqueContainer
{
public function has($id)
{
}
}
class SomeUniqueContainer implements SomeContainerInterface
{
public function has($id)
{
}
}
?>
-----
<?php
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\FixtureEmptyConfig;
interface SomeContainerInterface
{
/**
* @param string $id
*/
public function has($id);
}
final class SomeContainerBuilder extends SomeUniqueContainer
{
public function has($id)
{
}
}
class SomeUniqueContainer implements SomeContainerInterface
{
public function has($id)
{
}
}
?>

View File

@ -0,0 +1,55 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\FixtureEmptyConfig;
interface A
{
public function test(array $input);
}
class B implements A
{
public function test($input)
{
}
}
final class MostChild implements A
{
public function test(array $input)
{
}
}
?>
-----
<?php
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\FixtureEmptyConfig;
interface A
{
/**
* @param mixed[] $input
*/
public function test($input);
}
class B implements A
{
public function test($input)
{
}
}
final class MostChild implements A
{
/**
* @param mixed[] $input
*/
public function test($input)
{
}
}
?>

View File

@ -0,0 +1,42 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\FixtureEmptyConfig;
use Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Source\AnotherContainerInterface;
final class YetAnotherContainer implements AnotherContainerInterface
{
use AnotherServiceLocatorTrait;
}
trait AnotherServiceLocatorTrait
{
public function get(string $name)
{
}
}
?>
-----
<?php
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\FixtureEmptyConfig;
use Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\Source\AnotherContainerInterface;
final class YetAnotherContainer implements AnotherContainerInterface
{
use AnotherServiceLocatorTrait;
}
trait AnotherServiceLocatorTrait
{
/**
* @param string $name
*/
public function get($name)
{
}
}
?>

View File

@ -0,0 +1,42 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\FixtureEmptyConfig;
abstract class ParentClass implements \SplObserver
{
public function update(\SplSubject $subject): void
{
}
}
class IndirectImplementsNativeInterface extends ParentClass
{
public function update($subject): void
{
}
}
?>
-----
<?php
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\FixtureEmptyConfig;
abstract class ParentClass implements \SplObserver
{
/**
* @param \SplSubject $subject
*/
public function update($subject): void
{
}
}
class IndirectImplementsNativeInterface extends ParentClass
{
public function update($subject): void
{
}
}
?>

View File

@ -0,0 +1,48 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\FixtureEmptyConfig;
interface WhateverInterface
{
public function test(string $input);
}
abstract class AbstractSomeAncestorClass implements WhateverInterface
{
}
class SomeChildClass extends AbstractSomeAncestorClass
{
public function test($input) // type omitted for $input
{
/* ... */
}
}
?>
-----
<?php
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\FixtureEmptyConfig;
interface WhateverInterface
{
/**
* @param string $input
*/
public function test($input);
}
abstract class AbstractSomeAncestorClass implements WhateverInterface
{
}
class SomeChildClass extends AbstractSomeAncestorClass
{
public function test($input) // type omitted for $input
{
/* ... */
}
}
?>

View File

@ -0,0 +1,38 @@
<?php
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\FixtureEmptyConfig;
interface SomeAskingInterfaceWithNullable
{
public function ask(?callable $callable = null);
}
final class AskForMore implements SomeAskingInterfaceWithNullable
{
public function ask($callable = null)
{
}
}
?>
-----
<?php
namespace Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\FixtureEmptyConfig;
interface SomeAskingInterfaceWithNullable
{
/**
* @param callable|null $callable
*/
public function ask($callable = null);
}
final class AskForMore implements SomeAskingInterfaceWithNullable
{
public function ask($callable = null)
{
}
}
?>

View File

@ -12,9 +12,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->set(DowngradeParameterTypeWideningRector::class)
->configure([
DowngradeParameterTypeWideningRector::UNSAFE_TYPES_TO_METHODS => [
ContainerInterface::class => ['set', 'get', 'has', 'initialized'],
SomeContainerInterface::class => ['set', 'has'],
],
ContainerInterface::class => ['set', 'get', 'has', 'initialized'],
SomeContainerInterface::class => ['set', 'has'],
]);
};

View File

@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
use Rector\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(DowngradeParameterTypeWideningRector::class);
};

View File

@ -9,7 +9,7 @@ use PhpParser\Node\Param;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Reflection\ClassReflection;
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
use Rector\Core\Contract\Rector\AllowEmptyConfigurableRectorInterface;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\Reflection\ReflectionResolver;
use Rector\DowngradePhp72\NodeAnalyzer\BuiltInMethodAnalyzer;
@ -27,13 +27,8 @@ use Webmozart\Assert\Assert;
*
* @see \Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\DowngradeParameterTypeWideningRectorTest
*/
final class DowngradeParameterTypeWideningRector extends AbstractRector implements ConfigurableRectorInterface
final class DowngradeParameterTypeWideningRector extends AbstractRector implements AllowEmptyConfigurableRectorInterface
{
/**
* @var string
*/
final public const UNSAFE_TYPES_TO_METHODS = 'unsafe_types_to_methods';
/**
* @var array<string, string[]>
*/
@ -85,7 +80,8 @@ final class SomeClass implements SomeInterface
CODE_SAMPLE
,
[
self::UNSAFE_TYPES_TO_METHODS => [],
'ContainerInterface' => ['set', 'get', 'has', 'initialized'],
'SomeContainerInterface' => ['set', 'has'],
]
),
]);
@ -128,8 +124,8 @@ CODE_SAMPLE
*/
public function configure(array $configuration): void
{
$unsafeTypesToMethods = $configuration[self::UNSAFE_TYPES_TO_METHODS] ?? [];
Assert::isArray($unsafeTypesToMethods);
$unsafeTypesToMethods = $configuration;
foreach ($unsafeTypesToMethods as $key => $value) {
Assert::string($key);
Assert::allString($value);
@ -212,6 +208,10 @@ CODE_SAMPLE
private function isSafeType(ClassReflection $classReflection, ClassMethod $classMethod): bool
{
if ($this->unsafeTypesToMethods === []) {
return false;
}
$classReflectionName = $classReflection->getName();
foreach ($this->unsafeTypesToMethods as $unsafeType => $unsafeMethods) {
if (! $this->isNames($classMethod, $unsafeMethods)) {