[CodeQuality] Add case class name fix

This commit is contained in:
TomasVotruba 2020-07-27 15:40:30 +02:00
parent e09bd79c72
commit 8c31bd27e4
11 changed files with 339 additions and 2 deletions

View File

@ -53,6 +53,7 @@ use Rector\CodeQuality\Rector\If_\SimplifyIfNotNullReturnRector;
use Rector\CodeQuality\Rector\If_\SimplifyIfReturnBoolRector;
use Rector\CodeQuality\Rector\Include_\AbsolutizeRequireAndIncludePathRector;
use Rector\CodeQuality\Rector\LogicalAnd\AndAssignsToSeparateLinesRector;
use Rector\CodeQuality\Rector\Name\FixClassCaseSensitivityNameRector;
use Rector\CodeQuality\Rector\NotEqual\CommonNotEqualRector;
use Rector\CodeQuality\Rector\Return_\SimplifyUselessVariableRector;
use Rector\CodeQuality\Rector\Ternary\ArrayKeyExistsTernaryThenValueToCoalescingRector;
@ -214,4 +215,6 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->set(LogicalToBooleanRector::class);
$services->set(VarToPublicPropertyRector::class);
$services->set(FixClassCaseSensitivityNameRector::class);
};

View File

@ -1,4 +1,4 @@
# All 536 Rectors Overview
# All 537 Rectors Overview
- [Projects](#projects)
- [General](#general)
@ -9,7 +9,7 @@
- [Architecture](#architecture) (4)
- [Autodiscovery](#autodiscovery) (4)
- [CakePHP](#cakephp) (6)
- [CodeQuality](#codequality) (57)
- [CodeQuality](#codequality) (58)
- [CodingStyle](#codingstyle) (36)
- [DeadCode](#deadcode) (40)
- [Decomplex](#decomplex) (1)
@ -814,6 +814,30 @@ Make if conditions more explicit
<br><br>
### `FixClassCaseSensitivityNameRector`
- class: [`Rector\CodeQuality\Rector\Name\FixClassCaseSensitivityNameRector`](/../master/rules/code-quality/src/Rector/Name/FixClassCaseSensitivityNameRector.php)
- [test fixtures](/../master/rules/code-quality/tests/Rector/Name/FixClassCaseSensitivityNameRector/Fixture)
Change miss-typed case sensitivity name to correct one
```diff
final class SomeClass
{
public function run()
{
- $anotherClass = new anotherclass;
+ $anotherClass = new AnotherClass;
}
}
final class AnotherClass
{
}
```
<br><br>
### `ForRepeatedCountToOwnVariableRector`
- class: [`Rector\CodeQuality\Rector\For_\ForRepeatedCountToOwnVariableRector`](/../master/rules/code-quality/src/Rector/For_/ForRepeatedCountToOwnVariableRector.php)

View File

@ -9,6 +9,7 @@ use Rector\Core\Configuration\Option;
use Rector\DeadCode\Rector\ClassConst\RemoveUnusedClassConstantRector;
use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
use Rector\Set\ValueObject\SetList;
use Rector\SymfonyPhpConfig\Rector\Closure\AddEmptyLineBetweenCallsInPhpConfigRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
@ -19,6 +20,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
TestCase::class => ['provideData', 'provideData*', 'dataProvider', 'dataProvider*'],
]);
$services->set(AddEmptyLineBetweenCallsInPhpConfigRector::class);
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::SETS, [

View File

@ -0,0 +1,143 @@
<?php
declare(strict_types=1);
namespace Rector\CodeQuality\Rector\Name;
use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\UseUse;
use PHPStan\Reflection\ReflectionProvider;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\NodeTypeResolver\Node\AttributeKey;
/**
* Mostly mimics source from
* @see https://github.com/phpstan/phpstan-src/blob/master/src/Rules/ClassCaseSensitivityCheck.php
*
* @see \Rector\CodeQuality\Tests\Rector\Name\FixClassCaseSensitivityNameRector\FixClassCaseSensitivityNameRectorTest
*/
final class FixClassCaseSensitivityNameRector extends AbstractRector
{
/**
* @var ReflectionProvider
*/
private $reflectionProvider;
public function __construct(ReflectionProvider $reflectionProvider)
{
$this->reflectionProvider = $reflectionProvider;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Change miss-typed case sensitivity name to correct one', [
new CodeSample(
<<<'PHP'
final class SomeClass
{
public function run()
{
$anotherClass = new anotherclass;
}
}
final class AnotherClass
{
}
PHP
,
<<<'PHP'
final class SomeClass
{
public function run()
{
$anotherClass = new AnotherClass;
}
}
final class AnotherClass
{
}
PHP
),
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [Name::class];
}
/**
* @param Name $node
*/
public function refactor(Node $node): ?Node
{
$fullyQualifiedName = $this->resolveFullyQualifiedName($node);
if ($fullyQualifiedName === null) {
return null;
}
if (! $this->reflectionProvider->hasClass($fullyQualifiedName)) {
return null;
}
$classReflection = $this->reflectionProvider->getClass($fullyQualifiedName);
if ($classReflection->isBuiltin()) {
// skip built-in classes
return null;
}
$realClassName = $classReflection->getName();
if (strtolower($realClassName) !== strtolower($fullyQualifiedName)) {
// skip class alias
return null;
}
if ($realClassName === $fullyQualifiedName) {
return null;
}
$parent = $node->getAttribute(AttributeKey::PARENT_NODE);
// do not FQN use imports
if ($parent instanceof UseUse) {
return new Name($realClassName);
}
return new FullyQualified($realClassName);
}
private function resolveFullyQualifiedName(Name $name): string
{
$parent = $name->getAttribute(AttributeKey::PARENT_NODE);
// for some reason, Param gets already corrected name
if (! $parent instanceof Param) {
return $this->getName($name);
}
/** @var Name|null $originalName */
$originalName = $name->getAttribute(AttributeKey::ORIGINAL_NAME);
if ($originalName === null) {
return $this->getName($name);
}
// replace parts from the old one
$originalReversedParts = array_reverse($originalName->parts);
$resolvedReversedParts = array_reverse($name->parts);
$mergedReversedParts = $originalReversedParts + $resolvedReversedParts;
$mergedParts = array_reverse($mergedReversedParts);
return implode('\\', $mergedParts);
}
}

View File

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Rector\CodeQuality\Tests\Rector\Name\FixClassCaseSensitivityNameRector;
use Iterator;
use Rector\CodeQuality\Rector\Name\FixClassCaseSensitivityNameRector;
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;
final class FixClassCaseSensitivityNameRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
{
// for PHPStan class reflection
require_once __DIR__ . '/Source/MissCaseTypedClass.php';
$this->doTestFileInfo($fileInfo);
}
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
protected function getRectorClass(): string
{
return FixClassCaseSensitivityNameRector::class;
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace Rector\CodeQuality\Tests\Rector\Name\FixClassCaseSensitivityNameRector\Fixture;
use Rector\CodeQuality\Tests\Rector\Name\FixClassCaseSensitivityNameRector\Source\Misscasetypedclass;
final class SomeClass
{
public function run()
{
$misscasetypedclass = new Misscasetypedclass();
}
}
?>
-----
<?php
namespace Rector\CodeQuality\Tests\Rector\Name\FixClassCaseSensitivityNameRector\Fixture;
use Rector\CodeQuality\Tests\Rector\Name\FixClassCaseSensitivityNameRector\Source\MissCaseTypedClass;
final class SomeClass
{
public function run()
{
$misscasetypedclass = new \Rector\CodeQuality\Tests\Rector\Name\FixClassCaseSensitivityNameRector\Source\MissCaseTypedClass();
}
}
?>

View File

@ -0,0 +1,31 @@
<?php
namespace Rector\CodeQuality\Tests\Rector\Name\FixClassCaseSensitivityNameRector\Fixture;
use Rector\CodeQuality\Tests\Rector\Name\FixClassCaseSensitivityNameRector\Source\MissCaseTypedClass;
final class NamespaceAndConstant
{
public function run()
{
$fatherSon = MissCaseTypedClass::SOME_CONST;
}
}
?>
-----
<?php
namespace Rector\CodeQuality\Tests\Rector\Name\FixClassCaseSensitivityNameRector\Fixture;
use Rector\CodeQuality\Tests\Rector\Name\FixClassCaseSensitivityNameRector\Source\MissCaseTypedClass;
final class NamespaceAndConstant
{
public function run()
{
$fatherSon = MissCaseTypedClass::SOME_CONST;
}
}
?>

View File

@ -0,0 +1,31 @@
<?php
namespace Rector\CodeQuality\Tests\Rector\Name\FixClassCaseSensitivityNameRector\Fixture;
use Rector\CodeQuality\Tests\Rector\Name\FixClassCaseSensitivityNameRector\Source\MissCaseTypedClass;
final class ParamType
{
public function run(misscasetypedclass $misscasetypedclass)
{
return $misscasetypedclass;
}
}
?>
-----
<?php
namespace Rector\CodeQuality\Tests\Rector\Name\FixClassCaseSensitivityNameRector\Fixture;
use Rector\CodeQuality\Tests\Rector\Name\FixClassCaseSensitivityNameRector\Source\MissCaseTypedClass;
final class ParamType
{
public function run(\Rector\CodeQuality\Tests\Rector\Name\FixClassCaseSensitivityNameRector\Source\MissCaseTypedClass $misscasetypedclass)
{
return $misscasetypedclass;
}
}
?>

View File

@ -0,0 +1,11 @@
<?php
namespace Rector\CodeQuality\Tests\Rector\Name\FixClassCaseSensitivityNameRector\Fixture;
final class SkipSelfParent
{
public function run()
{
$fatherSon = new self();
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace Rector\CodeQuality\Tests\Rector\Name\FixClassCaseSensitivityNameRector\Fixture;
use Rector;
final class SkipShortImport
{
public function run()
{
$misscasetypedclass = new Rector\CodeQuality\Tests\Rector\Name\FixClassCaseSensitivityNameRector\Source\MissCaseTypedClass();
}
}

View File

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace Rector\CodeQuality\Tests\Rector\Name\FixClassCaseSensitivityNameRector\Source;
final class MissCaseTypedClass
{
/**
* @var string
*/
public const SOME_CONST = 'some_constant';
}