[CodeQuality] Add InlineIsAInstanceOfRector (#2364)

This commit is contained in:
Tomas Votruba 2022-05-27 00:01:06 +02:00 committed by GitHub
parent 3da22b0231
commit f5a06553ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 244 additions and 19 deletions

View File

@ -32,6 +32,7 @@ use Rector\CodeQuality\Rector\FuncCall\ArrayMergeOfNonArraysToSimpleArrayRector;
use Rector\CodeQuality\Rector\FuncCall\CallUserFuncWithArrowFunctionToInlineRector;
use Rector\CodeQuality\Rector\FuncCall\ChangeArrayPushToArrayAssignRector;
use Rector\CodeQuality\Rector\FuncCall\CompactToVariablesRector;
use Rector\CodeQuality\Rector\FuncCall\InlineIsAInstanceOfRector;
use Rector\CodeQuality\Rector\FuncCall\IntvalToTypeCastRector;
use Rector\CodeQuality\Rector\FuncCall\IsAWithStringWithThirdArgumentRector;
use Rector\CodeQuality\Rector\FuncCall\RemoveSoleValueSprintfRector;
@ -162,23 +163,27 @@ return static function (RectorConfig $rectorConfig): void {
'mbstrrpos' => 'mb_strrpos',
'mbsubstr' => 'mb_substr',
]);
$rectorConfig->rule(SetTypeToCastRector::class);
$rectorConfig->rule(LogicalToBooleanRector::class);
$rectorConfig->rule(VarToPublicPropertyRector::class);
$rectorConfig->rule(IssetOnPropertyObjectToPropertyExistsRector::class);
$rectorConfig->rule(NewStaticToNewSelfRector::class);
$rectorConfig->rule(DateTimeToDateTimeInterfaceRector::class);
$rectorConfig->rule(UnwrapSprintfOneArgumentRector::class);
$rectorConfig->rule(SwitchNegatedTernaryRector::class);
$rectorConfig->rule(SingularSwitchToIfRector::class);
$rectorConfig->rule(SimplifyIfNullableReturnRector::class);
$rectorConfig->rule(NarrowUnionTypeDocRector::class);
$rectorConfig->rule(FuncGetArgsToVariadicParamRector::class);
$rectorConfig->rule(CallUserFuncToMethodCallRector::class);
$rectorConfig->rule(CallUserFuncWithArrowFunctionToInlineRector::class);
$rectorConfig->rule(CountArrayToEmptyArrayComparisonRector::class);
$rectorConfig->rule(FlipTypeControlToUseExclusiveTypeRector::class);
$rectorConfig->rule(ExplicitMethodCallOverMagicGetSetRector::class);
$rectorConfig->rule(DoWhileBreakFalseToIfElseRector::class);
$rectorConfig->rule(InlineArrayReturnAssignRector::class);
$rectorConfig->rules([
SetTypeToCastRector::class,
LogicalToBooleanRector::class,
VarToPublicPropertyRector::class,
IssetOnPropertyObjectToPropertyExistsRector::class,
NewStaticToNewSelfRector::class,
DateTimeToDateTimeInterfaceRector::class,
UnwrapSprintfOneArgumentRector::class,
SwitchNegatedTernaryRector::class,
SingularSwitchToIfRector::class,
SimplifyIfNullableReturnRector::class,
NarrowUnionTypeDocRector::class,
FuncGetArgsToVariadicParamRector::class,
CallUserFuncToMethodCallRector::class,
CallUserFuncWithArrowFunctionToInlineRector::class,
CountArrayToEmptyArrayComparisonRector::class,
FlipTypeControlToUseExclusiveTypeRector::class,
ExplicitMethodCallOverMagicGetSetRector::class,
DoWhileBreakFalseToIfElseRector::class,
InlineArrayReturnAssignRector::class,
InlineIsAInstanceOfRector::class,
]);
};

View File

@ -0,0 +1,13 @@
<?php
namespace Rector\Tests\CodeQuality\Rector\FuncCall\InlineIsAInstanceOfRector\Fixture;
use Rector\Tests\CodeQuality\Rector\FuncCall\InlineIsAInstanceOfRector\Source\SomeType;
final class SkipCheckedTypeString
{
public function run(string $stringType)
{
return is_a($stringType, SomeType::class);
}
}

View File

@ -0,0 +1,11 @@
<?php
namespace Rector\Tests\CodeQuality\Rector\FuncCall\InlineIsAInstanceOfRector\Fixture;
final class SkipString
{
public function run(object $object)
{
return is_a($object, 'SomeType');
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace Rector\Tests\CodeQuality\Rector\FuncCall\InlineIsAInstanceOfRector\Fixture;
use Rector\Tests\CodeQuality\Rector\FuncCall\InlineIsAInstanceOfRector\Source\SomeType;
class SomeClass
{
public function run(object $object)
{
return is_a($object, SomeType::class);
}
}
?>
-----
<?php
namespace Rector\Tests\CodeQuality\Rector\FuncCall\InlineIsAInstanceOfRector\Fixture;
use Rector\Tests\CodeQuality\Rector\FuncCall\InlineIsAInstanceOfRector\Source\SomeType;
class SomeClass
{
public function run(object $object)
{
return $object instanceof \Rector\Tests\CodeQuality\Rector\FuncCall\InlineIsAInstanceOfRector\Source\SomeType;
}
}
?>

View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\CodeQuality\Rector\FuncCall\InlineIsAInstanceOfRector;
use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;
final class InlineIsAInstanceOfRectorTest 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';
}
}

View File

@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\CodeQuality\Rector\FuncCall\InlineIsAInstanceOfRector\Source;
final class SomeType
{
}

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
use Rector\CodeQuality\Rector\FuncCall\InlineIsAInstanceOfRector;
use Rector\Config\RectorConfig;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rule(InlineIsAInstanceOfRector::class);
};

View File

@ -0,0 +1,113 @@
<?php
declare(strict_types=1);
namespace Rector\CodeQuality\Rector\FuncCall;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\Instanceof_;
use PhpParser\Node\Name\FullyQualified;
use PHPStan\Type\Generic\GenericClassStringType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\ObjectWithoutClassType;
use PHPStan\Type\TypeWithClassName;
use Rector\Core\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Tests\CodeQuality\Rector\FuncCall\InlineIsAInstanceOfRector\InlineIsAInstanceOfRectorTest
*/
final class InlineIsAInstanceOfRector extends AbstractRector
{
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Change is_a() with object and class name check to instanceof', [
new CodeSample(
<<<'CODE_SAMPLE'
class SomeClass
{
public function run(object $object)
{
return is_a($object, SomeType::class);
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
class SomeClass
{
public function run(object $object)
{
return $object instanceof SomeType;
}
}
CODE_SAMPLE
),
]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [FuncCall::class];
}
/**
* @param FuncCall $node
*/
public function refactor(Node $node): ?Node
{
if (! $this->isName($node->name, 'is_a')) {
return null;
}
$args = $node->getArgs();
$firstArgValue = $args[0]->value;
if (! $this->isFirstObjectType($firstArgValue)) {
return null;
}
$className = $this->resolveClassName($args[1]->value);
if ($className === null) {
return null;
}
return new Instanceof_($firstArgValue, new FullyQualified($className));
}
private function resolveClassName(Expr $expr): ?string
{
if (! $expr instanceof ClassConstFetch) {
return null;
}
$type = $this->getType($expr);
if ($type instanceof GenericClassStringType) {
$type = $type->getGenericType();
}
if (! $type instanceof TypeWithClassName) {
return null;
}
return $type->getClassName();
}
private function isFirstObjectType(Expr $expr): bool
{
$exprType = $this->getType($expr);
if ($exprType instanceof ObjectWithoutClassType) {
return true;
}
return $exprType instanceof ObjectType;
}
}