rector/rules/naming/src/Rector/Assign/RenameVariableToMatchGetMethodNameRector.php

211 lines
6.0 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
namespace Rector\Naming\Rector\Assign;
use PhpParser\Node;
2020-07-19 13:39:13 +00:00
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\Closure;
2020-07-19 12:30:07 +00:00
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
2020-07-19 09:53:35 +00:00
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
use PHPStan\Type\TypeWithClassName;
2020-07-19 09:53:35 +00:00
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\FamilyTree\Reflection\FamilyRelationsAnalyzer;
use Rector\Naming\Guard\BreakingVariableRenameGuard;
use Rector\Naming\Matcher\VariableAndCallAssignMatcher;
use Rector\Naming\Naming\ExpectedNameResolver;
2020-07-19 17:24:46 +00:00
use Rector\Naming\NamingConvention\NamingConventionAnalyzer;
use Rector\Naming\VariableRenamer;
2020-07-19 09:53:35 +00:00
use Rector\NodeTypeResolver\Node\AttributeKey;
/**
* @see \Rector\Naming\Tests\Rector\Assign\RenameVariableToMatchGetMethodNameRector\RenameVariableToMatchGetMethodNameRectorTest
*/
final class RenameVariableToMatchGetMethodNameRector extends AbstractRector
{
/**
* @var ExpectedNameResolver
*/
private $expectedNameResolver;
/**
* @var VariableRenamer
*/
private $variableRenamer;
/**
* @var BreakingVariableRenameGuard
*/
private $breakingVariableRenameGuard;
/**
* @var FamilyRelationsAnalyzer
*/
private $familyRelationsAnalyzer;
/**
* @var VariableAndCallAssignMatcher
*/
private $variableAndCallAssignMatcher;
2020-07-19 17:24:46 +00:00
/**
* @var NamingConventionAnalyzer
*/
private $namingConventionAnalyzer;
public function __construct(
ExpectedNameResolver $expectedNameResolver,
VariableRenamer $variableRenamer,
BreakingVariableRenameGuard $breakingVariableRenameGuard,
FamilyRelationsAnalyzer $familyRelationsAnalyzer,
2020-07-19 17:24:46 +00:00
VariableAndCallAssignMatcher $variableAndCallAssignMatcher,
NamingConventionAnalyzer $namingConventionAnalyzer
) {
$this->expectedNameResolver = $expectedNameResolver;
$this->variableRenamer = $variableRenamer;
$this->breakingVariableRenameGuard = $breakingVariableRenameGuard;
$this->familyRelationsAnalyzer = $familyRelationsAnalyzer;
$this->variableAndCallAssignMatcher = $variableAndCallAssignMatcher;
2020-07-19 17:24:46 +00:00
$this->namingConventionAnalyzer = $namingConventionAnalyzer;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Rename variable to match get method name', [
new CodeSample(
<<<'PHP'
class SomeClass
{
public function run()
{
$a = $this->getRunner();
}
}
PHP
,
<<<'PHP'
class SomeClass
{
public function run()
{
$runner = $this->getRunner();
}
}
PHP
),
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [Assign::class];
}
/**
* @param Assign $node
*/
public function refactor(Node $node): ?Node
{
$variableAndCallAssign = $this->variableAndCallAssignMatcher->match($node);
if ($variableAndCallAssign === null) {
return null;
}
2020-07-19 17:24:46 +00:00
$expectedName = $this->expectedNameResolver->resolveForGetCallExpr($variableAndCallAssign->getCall());
if ($expectedName === null || $this->isName($node, $expectedName)) {
return null;
}
2020-07-19 17:24:46 +00:00
if ($this->namingConventionAnalyzer->isCallMatchingVariableName(
$variableAndCallAssign->getCall(),
$variableAndCallAssign->getVariableName(),
2020-07-19 17:24:46 +00:00
$expectedName
)) {
2020-07-19 12:30:07 +00:00
return null;
}
2020-07-19 17:24:46 +00:00
if ($this->isClassTypeWithChildren($variableAndCallAssign->getCall())) {
return null;
}
if ($this->breakingVariableRenameGuard->shouldSkipVariable(
$variableAndCallAssign->getVariableName(),
2020-07-19 17:24:46 +00:00
$expectedName,
$variableAndCallAssign->getFunctionLike(),
$variableAndCallAssign->getVariable()
)) {
return null;
}
2020-07-19 17:24:46 +00:00
return $this->renameVariable($node, $expectedName, $variableAndCallAssign->getFunctionLike());
}
/**
* @param ClassMethod|Function_|Closure $functionLike
*/
private function renameVariable(Assign $assign, string $newName, FunctionLike $functionLike): Assign
{
/** @var Variable $variableNode */
2020-07-19 09:53:35 +00:00
$variableNode = $assign->var;
/** @var string $originalName */
$originalName = $variableNode->name;
$this->renameInDocComment($assign, $originalName, $newName);
$this->variableRenamer->renameVariableInFunctionLike($functionLike, $assign, $originalName, $newName);
return $assign;
}
/**
* @note variable rename is correct, but node printer doesn't see it as a changed text for some reason
*/
private function renameInDocComment(Node $node, string $originalName, string $newName): void
{
2020-07-19 09:53:35 +00:00
/** @var PhpDocInfo|null $phpDocInfo */
$phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO);
2020-07-19 09:53:35 +00:00
if ($phpDocInfo === null) {
2020-07-19 09:41:35 +00:00
return;
}
2020-07-19 09:41:35 +00:00
2020-07-19 09:53:35 +00:00
$varTagValueNode = $phpDocInfo->getByType(VarTagValueNode::class);
if ($varTagValueNode === null) {
return;
}
if ($varTagValueNode->variableName !== '$' . $originalName) {
return;
}
$varTagValueNode->variableName = '$' . $newName;
}
2020-07-19 13:39:13 +00:00
/**
2020-07-19 17:24:46 +00:00
* @param StaticCall|MethodCall|FuncCall $expr
2020-07-19 13:39:13 +00:00
*/
2020-07-19 17:24:46 +00:00
private function isClassTypeWithChildren(Expr $expr): bool
2020-07-19 13:39:13 +00:00
{
2020-07-19 17:24:46 +00:00
$callStaticType = $this->getStaticType($expr);
if (! $callStaticType instanceof TypeWithClassName) {
return false;
2020-07-19 13:39:13 +00:00
}
2020-07-19 17:24:46 +00:00
return $this->familyRelationsAnalyzer->isParentClass($callStaticType->getClassName());
2020-07-19 13:39:13 +00:00
}
}