mirror of https://github.com/rectorphp/rector.git
178 lines
5.5 KiB
PHP
178 lines
5.5 KiB
PHP
<?php
|
|
|
|
declare (strict_types=1);
|
|
namespace Rector\DeadCode\Rector\StmtsAwareInterface;
|
|
|
|
use PhpParser\Node;
|
|
use PhpParser\Node\Expr\ArrayDimFetch;
|
|
use PhpParser\Node\Expr\Assign;
|
|
use PhpParser\Node\Expr\BinaryOp\Concat;
|
|
use PhpParser\Node\Expr\Ternary;
|
|
use PhpParser\Node\Expr\Variable;
|
|
use PhpParser\Node\Stmt;
|
|
use PhpParser\Node\Stmt\Expression;
|
|
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
|
|
use Rector\BetterPhpDocParser\Comment\CommentsMerger;
|
|
use Rector\Core\Contract\PhpParser\Node\StmtsAwareInterface;
|
|
use Rector\Core\NodeAnalyzer\VariableAnalyzer;
|
|
use Rector\Core\Rector\AbstractRector;
|
|
use Rector\DeadCode\NodeAnalyzer\ExprUsedInNextNodeAnalyzer;
|
|
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
|
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
|
/**
|
|
* @see \Rector\Tests\DeadCode\Rector\StmtsAwareInterface\RemoveJustVariableAssignRector\RemoveJustVariableAssignRectorTest
|
|
*/
|
|
final class RemoveJustVariableAssignRector extends AbstractRector
|
|
{
|
|
/**
|
|
* @readonly
|
|
* @var \Rector\Core\NodeAnalyzer\VariableAnalyzer
|
|
*/
|
|
private $variableAnalyzer;
|
|
/**
|
|
* @readonly
|
|
* @var \Rector\DeadCode\NodeAnalyzer\ExprUsedInNextNodeAnalyzer
|
|
*/
|
|
private $exprUsedInNextNodeAnalyzer;
|
|
/**
|
|
* @readonly
|
|
* @var \Rector\BetterPhpDocParser\Comment\CommentsMerger
|
|
*/
|
|
private $commentsMerger;
|
|
public function __construct(VariableAnalyzer $variableAnalyzer, ExprUsedInNextNodeAnalyzer $exprUsedInNextNodeAnalyzer, CommentsMerger $commentsMerger)
|
|
{
|
|
$this->variableAnalyzer = $variableAnalyzer;
|
|
$this->exprUsedInNextNodeAnalyzer = $exprUsedInNextNodeAnalyzer;
|
|
$this->commentsMerger = $commentsMerger;
|
|
}
|
|
public function getRuleDefinition() : RuleDefinition
|
|
{
|
|
return new RuleDefinition('Remove variable just to assign value or return value', [new CodeSample(<<<'CODE_SAMPLE'
|
|
final class SomeClass
|
|
{
|
|
public function run()
|
|
{
|
|
$result = 100;
|
|
|
|
$this->temporaryValue = $result;
|
|
}
|
|
}
|
|
CODE_SAMPLE
|
|
, <<<'CODE_SAMPLE'
|
|
final class SomeClass
|
|
{
|
|
public function run()
|
|
{
|
|
$this->temporaryValue = 100;
|
|
}
|
|
}
|
|
CODE_SAMPLE
|
|
)]);
|
|
}
|
|
/**
|
|
* @return array<class-string<Node>>
|
|
*/
|
|
public function getNodeTypes() : array
|
|
{
|
|
return [StmtsAwareInterface::class];
|
|
}
|
|
/**
|
|
* @param StmtsAwareInterface $node
|
|
*/
|
|
public function refactor(Node $node) : ?Node
|
|
{
|
|
$stmts = (array) $node->stmts;
|
|
if ($stmts === []) {
|
|
return null;
|
|
}
|
|
$originalStmts = $stmts;
|
|
foreach ($stmts as $key => $stmt) {
|
|
$nextStmt = $stmts[$key + 1] ?? null;
|
|
if (!$nextStmt instanceof Stmt) {
|
|
continue;
|
|
}
|
|
$currentAssign = $this->matchExpressionAssign($stmt);
|
|
if (!$currentAssign instanceof Assign) {
|
|
continue;
|
|
}
|
|
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($stmt);
|
|
if ($phpDocInfo->getVarTagValueNode() instanceof VarTagValueNode) {
|
|
continue;
|
|
}
|
|
$nextAssign = $this->matchExpressionAssign($nextStmt);
|
|
if (!$nextAssign instanceof Assign) {
|
|
continue;
|
|
}
|
|
if ($this->areTooComplexAssignsToShorten($currentAssign, $nextAssign)) {
|
|
continue;
|
|
}
|
|
if (!$this->areTwoVariablesCrossAssign($currentAssign, $nextAssign)) {
|
|
continue;
|
|
}
|
|
// ...
|
|
$currentAssign->var = $nextAssign->var;
|
|
$this->commentsMerger->keepComments($stmt, [$stmts[$key + 1]]);
|
|
unset($stmts[$key + 1]);
|
|
}
|
|
if ($originalStmts === $stmts) {
|
|
return null;
|
|
}
|
|
$node->stmts = $stmts;
|
|
return $node;
|
|
}
|
|
/**
|
|
* This detects if two variables are cross assigned:
|
|
*
|
|
* $<some> = 1000;
|
|
* $this->value = $<some>;
|
|
*
|
|
* + not used $<some> below, so removal will not break it
|
|
*/
|
|
private function areTwoVariablesCrossAssign(Assign $currentAssign, Assign $nextAssign) : bool
|
|
{
|
|
// is just re-assign to variable
|
|
if (!$currentAssign->var instanceof Variable) {
|
|
return \false;
|
|
}
|
|
if (!$nextAssign->expr instanceof Variable) {
|
|
return \false;
|
|
}
|
|
if (!$this->nodeComparator->areNodesEqual($currentAssign->var, $nextAssign->expr)) {
|
|
return \false;
|
|
}
|
|
if ($this->variableAnalyzer->isUsedByReference($currentAssign->var)) {
|
|
return \false;
|
|
}
|
|
if ($this->variableAnalyzer->isUsedByReference($nextAssign->expr)) {
|
|
return \false;
|
|
}
|
|
if ($this->variableAnalyzer->isStaticOrGlobal($currentAssign->var)) {
|
|
return \false;
|
|
}
|
|
return !$this->exprUsedInNextNodeAnalyzer->isUsed($nextAssign->expr);
|
|
}
|
|
/**
|
|
* Shortening should not make code less readable.
|
|
*/
|
|
private function areTooComplexAssignsToShorten(Assign $currentAssign, Assign $nextAssign) : bool
|
|
{
|
|
if ($currentAssign->expr instanceof Ternary) {
|
|
return \true;
|
|
}
|
|
if ($currentAssign->expr instanceof Concat) {
|
|
return \true;
|
|
}
|
|
return $nextAssign->var instanceof ArrayDimFetch;
|
|
}
|
|
private function matchExpressionAssign(Stmt $stmt) : ?Assign
|
|
{
|
|
if (!$stmt instanceof Expression) {
|
|
return null;
|
|
}
|
|
if (!$stmt->expr instanceof Assign) {
|
|
return null;
|
|
}
|
|
return $stmt->expr;
|
|
}
|
|
}
|