2020-07-13 21:13:40 +00:00
< ? php
2021-05-09 20:15:43 +00:00
declare ( strict_types = 1 );
2022-06-06 17:12:56 +00:00
namespace Rector\Naming\Rector\Assign ;
2020-07-13 21:13:40 +00:00
2022-06-06 17:12:56 +00:00
use PhpParser\Node ;
use PhpParser\Node\Expr\Assign ;
use PhpParser\Node\Expr\FuncCall ;
use PhpParser\Node\Expr\MethodCall ;
use PhpParser\Node\Expr\StaticCall ;
use PhpParser\Node\Stmt\ClassLike ;
use PHPStan\Reflection\ReflectionProvider ;
use PHPStan\Type\ObjectType ;
use Rector\Core\Rector\AbstractRector ;
use Rector\FamilyTree\Reflection\FamilyRelationsAnalyzer ;
use Rector\Naming\Guard\BreakingVariableRenameGuard ;
use Rector\Naming\Matcher\VariableAndCallAssignMatcher ;
use Rector\Naming\Naming\ExpectedNameResolver ;
use Rector\Naming\NamingConvention\NamingConventionAnalyzer ;
use Rector\Naming\PhpDoc\VarTagValueNodeRenamer ;
use Rector\Naming\ValueObject\VariableAndCallAssign ;
use Rector\Naming\VariableRenamer ;
use Rector\NodeTypeResolver\Node\AttributeKey ;
use Rector\PHPStanStaticTypeMapper\Utils\TypeUnwrapper ;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample ;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition ;
2020-07-13 21:13:40 +00:00
/**
2021-03-12 22:20:25 +00:00
* @ see \Rector\Tests\Naming\Rector\Assign\RenameVariableToMatchMethodCallReturnTypeRector\RenameVariableToMatchMethodCallReturnTypeRectorTest
2020-07-13 21:13:40 +00:00
*/
2022-06-06 17:12:56 +00:00
final class RenameVariableToMatchMethodCallReturnTypeRector extends \Rector\Core\Rector\AbstractRector
2020-07-13 21:13:40 +00:00
{
/**
2021-12-04 12:47:17 +00:00
* @ readonly
2021-05-10 23:39:21 +00:00
* @ var \Rector\Naming\Guard\BreakingVariableRenameGuard
2020-07-13 21:13:40 +00:00
*/
2021-05-10 23:39:21 +00:00
private $breakingVariableRenameGuard ;
2020-07-19 10:16:20 +00:00
/**
2021-12-04 12:47:17 +00:00
* @ readonly
2021-05-10 23:39:21 +00:00
* @ var \Rector\Naming\Naming\ExpectedNameResolver
2020-07-19 10:16:20 +00:00
*/
2021-05-10 23:39:21 +00:00
private $expectedNameResolver ;
2020-07-19 10:25:24 +00:00
/**
2021-12-04 12:47:17 +00:00
* @ readonly
2021-05-10 23:39:21 +00:00
* @ var \Rector\Naming\NamingConvention\NamingConventionAnalyzer
2020-07-19 10:25:24 +00:00
*/
2021-05-10 23:39:21 +00:00
private $namingConventionAnalyzer ;
2020-07-19 16:11:59 +00:00
/**
2021-12-04 12:47:17 +00:00
* @ readonly
2021-05-10 23:39:21 +00:00
* @ var \Rector\Naming\PhpDoc\VarTagValueNodeRenamer
2020-07-19 16:11:59 +00:00
*/
2021-05-10 23:39:21 +00:00
private $varTagValueNodeRenamer ;
2020-07-19 17:24:46 +00:00
/**
2021-12-04 12:47:17 +00:00
* @ readonly
2021-05-10 23:39:21 +00:00
* @ var \Rector\Naming\Matcher\VariableAndCallAssignMatcher
2020-07-19 17:24:46 +00:00
*/
2021-05-10 23:39:21 +00:00
private $variableAndCallAssignMatcher ;
2020-07-19 17:42:16 +00:00
/**
2021-12-04 12:47:17 +00:00
* @ readonly
2021-05-10 23:39:21 +00:00
* @ var \Rector\Naming\VariableRenamer
2020-07-19 17:42:16 +00:00
*/
2021-05-10 23:39:21 +00:00
private $variableRenamer ;
2021-02-09 01:52:35 +00:00
/**
2021-12-04 12:47:17 +00:00
* @ readonly
2021-05-10 23:39:21 +00:00
* @ var \Rector\PHPStanStaticTypeMapper\Utils\TypeUnwrapper
2021-02-09 01:52:35 +00:00
*/
private $typeUnwrapper ;
2021-02-28 07:47:48 +00:00
/**
2021-12-04 12:47:17 +00:00
* @ readonly
2021-08-23 00:20:32 +00:00
* @ var \PHPStan\Reflection\ReflectionProvider
2021-02-28 07:47:48 +00:00
*/
private $reflectionProvider ;
/**
2021-12-04 12:47:17 +00:00
* @ readonly
2021-05-10 23:39:21 +00:00
* @ var \Rector\FamilyTree\Reflection\FamilyRelationsAnalyzer
2021-02-28 07:47:48 +00:00
*/
private $familyRelationsAnalyzer ;
2022-06-06 17:12:56 +00:00
public function __construct ( \Rector\Naming\Guard\BreakingVariableRenameGuard $breakingVariableRenameGuard , \Rector\Naming\Naming\ExpectedNameResolver $expectedNameResolver , \Rector\Naming\NamingConvention\NamingConventionAnalyzer $namingConventionAnalyzer , \Rector\Naming\PhpDoc\VarTagValueNodeRenamer $varTagValueNodeRenamer , \Rector\Naming\Matcher\VariableAndCallAssignMatcher $variableAndCallAssignMatcher , \Rector\Naming\VariableRenamer $variableRenamer , \Rector\PHPStanStaticTypeMapper\Utils\TypeUnwrapper $typeUnwrapper , \PHPStan\Reflection\ReflectionProvider $reflectionProvider , \Rector\FamilyTree\Reflection\FamilyRelationsAnalyzer $familyRelationsAnalyzer )
2021-05-09 20:15:43 +00:00
{
2020-07-19 10:25:24 +00:00
$this -> breakingVariableRenameGuard = $breakingVariableRenameGuard ;
2021-05-10 23:39:21 +00:00
$this -> expectedNameResolver = $expectedNameResolver ;
2020-07-19 17:24:46 +00:00
$this -> namingConventionAnalyzer = $namingConventionAnalyzer ;
2020-07-19 17:42:16 +00:00
$this -> varTagValueNodeRenamer = $varTagValueNodeRenamer ;
2021-05-10 23:39:21 +00:00
$this -> variableAndCallAssignMatcher = $variableAndCallAssignMatcher ;
$this -> variableRenamer = $variableRenamer ;
2021-02-09 01:52:35 +00:00
$this -> typeUnwrapper = $typeUnwrapper ;
2021-02-28 07:47:48 +00:00
$this -> reflectionProvider = $reflectionProvider ;
$this -> familyRelationsAnalyzer = $familyRelationsAnalyzer ;
2020-07-13 21:13:40 +00:00
}
2022-06-06 17:12:56 +00:00
public function getRuleDefinition () : \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
2020-07-13 21:13:40 +00:00
{
2022-06-06 17:12:56 +00:00
return new \Symplify\RuleDocGenerator\ValueObject\RuleDefinition ( 'Rename variable to match method return type' , [ new \Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample ( <<< 'CODE_SAMPLE'
2020-07-19 11:52:01 +00:00
class SomeClass
{
2021-02-20 00:14:58 +00:00
public function run ()
{
$a = $this -> getRunner ();
}
2020-08-30 19:01:33 +00:00
2021-02-20 00:14:58 +00:00
public function getRunner () : Runner
{
return new Runner ();
}
2020-07-13 21:13:40 +00:00
}
2020-09-15 08:23:13 +00:00
CODE_SAMPLE
2021-05-09 20:15:43 +00:00
, <<< 'CODE_SAMPLE'
2020-07-19 11:52:01 +00:00
class SomeClass
{
2021-02-20 00:14:58 +00:00
public function run ()
{
$runner = $this -> getRunner ();
}
2020-08-30 19:01:33 +00:00
2021-02-20 00:14:58 +00:00
public function getRunner () : Runner
{
return new Runner ();
}
2020-07-13 21:13:40 +00:00
}
2020-09-15 08:23:13 +00:00
CODE_SAMPLE
2021-05-09 20:15:43 +00:00
)]);
2020-07-13 21:13:40 +00:00
}
/**
2021-02-27 00:06:15 +00:00
* @ return array < class - string < Node >>
2020-07-13 21:13:40 +00:00
*/
2021-05-09 20:15:43 +00:00
public function getNodeTypes () : array
2020-07-13 21:13:40 +00:00
{
2022-06-06 17:12:56 +00:00
return [ \PhpParser\Node\Expr\Assign :: class ];
2020-07-13 21:13:40 +00:00
}
/**
2021-12-10 10:22:23 +00:00
* @ param Assign $node
2020-07-13 21:13:40 +00:00
*/
2022-06-06 17:12:56 +00:00
public function refactor ( \PhpParser\Node $node ) : ? \PhpParser\Node
2020-07-13 21:13:40 +00:00
{
2020-07-19 16:11:59 +00:00
$variableAndCallAssign = $this -> variableAndCallAssignMatcher -> match ( $node );
2022-06-06 17:12:56 +00:00
if ( ! $variableAndCallAssign instanceof \Rector\Naming\ValueObject\VariableAndCallAssign ) {
2020-07-13 21:13:40 +00:00
return null ;
}
2020-12-18 22:18:17 +00:00
$call = $variableAndCallAssign -> getCall ();
if ( $this -> isMultipleCall ( $call )) {
return null ;
}
$expectedName = $this -> expectedNameResolver -> resolveForCall ( $call );
2020-12-24 16:28:56 +00:00
if ( $expectedName === null ) {
return null ;
}
if ( $this -> isName ( $node -> var , $expectedName )) {
2020-07-13 21:13:40 +00:00
return null ;
}
2020-07-19 17:42:16 +00:00
if ( $this -> shouldSkip ( $variableAndCallAssign , $expectedName )) {
2020-07-19 10:25:24 +00:00
return null ;
}
2020-07-19 18:19:25 +00:00
$this -> renameVariable ( $variableAndCallAssign , $expectedName );
return $node ;
2020-07-13 21:13:40 +00:00
}
2020-12-18 22:18:17 +00:00
/**
2022-04-26 08:13:18 +00:00
* @ param \PhpParser\Node\Expr\FuncCall | \PhpParser\Node\Expr\StaticCall | \PhpParser\Node\Expr\MethodCall $callNode
2020-12-18 22:18:17 +00:00
*/
2021-06-29 14:24:45 +00:00
private function isMultipleCall ( $callNode ) : bool
2020-12-18 22:18:17 +00:00
{
2022-06-06 17:12:56 +00:00
$parentNode = $callNode -> getAttribute ( \Rector\NodeTypeResolver\Node\AttributeKey :: PARENT_NODE );
2021-05-09 20:15:43 +00:00
$callNodeClass = \get_class ( $callNode );
2022-06-06 17:12:56 +00:00
while ( $parentNode instanceof \PhpParser\Node ) {
$usedNodes = $this -> betterNodeFinder -> find ( $parentNode , function ( \PhpParser\Node $node ) use ( $callNodeClass , $callNode ) : bool {
2021-05-09 20:15:43 +00:00
$nodeClass = \get_class ( $node );
2021-03-14 13:11:24 +00:00
if ( $callNodeClass !== $nodeClass ) {
2021-05-09 20:15:43 +00:00
return \false ;
2020-12-18 22:18:17 +00:00
}
2022-06-06 17:12:56 +00:00
$usedNodeOriginalNode = $callNode -> getAttribute ( \Rector\NodeTypeResolver\Node\AttributeKey :: ORIGINAL_NODE );
if ( ! $usedNodeOriginalNode instanceof \PhpParser\Node ) {
2022-04-15 17:12:35 +00:00
return \false ;
}
if ( \get_class ( $usedNodeOriginalNode ) !== \get_class ( $callNode )) {
return \false ;
}
2020-12-21 02:12:42 +00:00
/** @var FuncCall|StaticCall|MethodCall $node */
2021-02-09 01:52:35 +00:00
$passedNode = clone $node ;
2022-04-15 17:12:35 +00:00
/** @var FuncCall|StaticCall|MethodCall $usedNodeOriginalNode */
$usedNode = clone $usedNodeOriginalNode ;
2020-12-21 02:12:42 +00:00
/** @var FuncCall|StaticCall|MethodCall $passedNode */
2020-12-18 22:18:17 +00:00
$passedNode -> args = [];
2020-12-21 02:12:42 +00:00
/** @var FuncCall|StaticCall|MethodCall $usedNode */
2020-12-18 22:18:17 +00:00
$usedNode -> args = [];
2021-02-19 12:01:23 +00:00
return $this -> nodeComparator -> areNodesEqual ( $passedNode , $usedNode );
2021-02-09 01:52:35 +00:00
});
2021-05-09 20:15:43 +00:00
if ( \count ( $usedNodes ) > 1 ) {
return \true ;
2020-12-18 22:18:17 +00:00
}
2022-06-06 17:12:56 +00:00
$parentNode = $parentNode -> getAttribute ( \Rector\NodeTypeResolver\Node\AttributeKey :: PARENT_NODE );
2020-12-18 22:18:17 +00:00
}
2021-05-09 20:15:43 +00:00
return \false ;
2020-12-18 22:18:17 +00:00
}
2022-06-06 17:12:56 +00:00
private function shouldSkip ( \Rector\Naming\ValueObject\VariableAndCallAssign $variableAndCallAssign , string $expectedName ) : bool
2020-08-05 20:45:36 +00:00
{
2021-05-09 20:15:43 +00:00
if ( $this -> namingConventionAnalyzer -> isCallMatchingVariableName ( $variableAndCallAssign -> getCall (), $variableAndCallAssign -> getVariableName (), $expectedName )) {
return \true ;
2020-08-05 20:45:36 +00:00
}
if ( $this -> isClassTypeWithChildren ( $variableAndCallAssign -> getCall ())) {
2021-05-09 20:15:43 +00:00
return \true ;
2020-08-05 20:45:36 +00:00
}
2021-05-09 20:15:43 +00:00
return $this -> breakingVariableRenameGuard -> shouldSkipVariable ( $variableAndCallAssign -> getVariableName (), $expectedName , $variableAndCallAssign -> getFunctionLike (), $variableAndCallAssign -> getVariable ());
2020-08-05 20:45:36 +00:00
}
2022-06-06 17:12:56 +00:00
private function renameVariable ( \Rector\Naming\ValueObject\VariableAndCallAssign $variableAndCallAssign , string $expectedName ) : void
2020-07-13 21:13:40 +00:00
{
2021-01-19 23:29:52 +00:00
$assign = $variableAndCallAssign -> getAssign ();
$assignPhpDocInfo = $this -> phpDocInfoFactory -> createFromNodeOrEmpty ( $assign );
2021-05-09 20:15:43 +00:00
$this -> varTagValueNodeRenamer -> renameAssignVarTagVariableName ( $assignPhpDocInfo , $variableAndCallAssign -> getVariableName (), $expectedName );
2021-05-29 22:10:59 +00:00
$this -> variableRenamer -> renameVariableInFunctionLike ( $variableAndCallAssign -> getFunctionLike (), $variableAndCallAssign -> getVariableName (), $expectedName , $variableAndCallAssign -> getAssign ());
2020-07-13 21:13:40 +00:00
}
2020-07-19 13:39:13 +00:00
/**
2022-04-26 08:13:18 +00:00
* @ param \PhpParser\Node\Expr\StaticCall | \PhpParser\Node\Expr\MethodCall | \PhpParser\Node\Expr\FuncCall $expr
2020-07-19 13:39:13 +00:00
*/
2021-06-29 14:24:45 +00:00
private function isClassTypeWithChildren ( $expr ) : bool
2020-07-19 13:39:13 +00:00
{
2021-10-07 17:46:41 +00:00
$callStaticType = $this -> getType ( $expr );
2020-12-06 00:27:00 +00:00
$callStaticType = $this -> typeUnwrapper -> unwrapNullableType ( $callStaticType );
2022-06-06 17:12:56 +00:00
if ( ! $callStaticType instanceof \PHPStan\Type\ObjectType ) {
2021-05-09 20:15:43 +00:00
return \false ;
2020-07-19 13:39:13 +00:00
}
2022-06-06 17:12:56 +00:00
if ( \is_a ( $callStaticType -> getClassName (), \PhpParser\Node\Stmt\ClassLike :: class , \true )) {
2021-05-09 20:15:43 +00:00
return \false ;
2020-07-19 18:19:25 +00:00
}
2021-05-09 20:15:43 +00:00
if ( ! $this -> reflectionProvider -> hasClass ( $callStaticType -> getClassName ())) {
return \false ;
2021-02-28 07:47:48 +00:00
}
$classReflection = $this -> reflectionProvider -> getClass ( $callStaticType -> getClassName ());
$childrenClassReflections = $this -> familyRelationsAnalyzer -> getChildrenOfClassReflection ( $classReflection );
return $childrenClassReflections !== [];
2020-07-19 13:39:13 +00:00
}
2020-07-13 21:13:40 +00:00
}