Merge pull request #1584 from rectorphp/dead-zero

[DeadCode] Add RemoveDeadZeroAndOneOperationRector
This commit is contained in:
Tomáš Votruba 2019-06-09 19:22:27 +02:00 committed by GitHub
commit 7ad34fc4ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 340 additions and 3 deletions

View File

@ -20,3 +20,4 @@ services:
Rector\DeadCode\Rector\Concat\RemoveConcatAutocastRector: ~ Rector\DeadCode\Rector\Concat\RemoveConcatAutocastRector: ~
Rector\CodeQuality\Rector\Return_\SimplifyUselessVariableRector: ~ Rector\CodeQuality\Rector\Return_\SimplifyUselessVariableRector: ~
Rector\DeadCode\Rector\Plus\RemoveZeroAndOneBinaryRector: ~

View File

@ -0,0 +1,181 @@
<?php declare(strict_types=1);
namespace Rector\DeadCode\Rector\Plus;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\AssignOp;
use PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp\Div;
use PhpParser\Node\Expr\BinaryOp\Minus;
use PhpParser\Node\Expr\BinaryOp\Mul;
use PhpParser\Node\Expr\BinaryOp\Plus;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
/**
* @see https://3v4l.org/I0BGs
*/
final class RemoveDeadZeroAndOneOperationRector extends AbstractRector
{
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('', [
new CodeSample(
<<<'CODE_SAMPLE'
class SomeClass
{
public function run()
{
$value = 5 * 1;
$value = 5 + 0;
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
class SomeClass
{
public function run()
{
$value = 5;
$value = 5;
}
}
CODE_SAMPLE
),
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [
Plus::class,
Minus::class,
Mul::class,
Div::class,
Node\Expr\AssignOp\Plus::class,
Node\Expr\AssignOp\Minus::class,
Node\Expr\AssignOp\Mul::class,
Node\Expr\AssignOp\Div::class,
];
}
/**
* @param AssignOp|BinaryOp $node
*/
public function refactor(Node $node): ?Node
{
$changedNode = null;
$previousNode = $node;
if ($node instanceof AssignOp) {
$changedNode = $this->processAssignOp($node);
}
// -, +
if ($node instanceof BinaryOp) {
$changedNode = $this->processBinaryOp($node);
}
// recurse nested combinations
while ($changedNode !== null && ! $this->areNodesEqual($previousNode, $changedNode)) {
$previousNode = $changedNode;
if ($changedNode instanceof BinaryOp || $changedNode instanceof AssignOp) {
$changedNode = $this->refactor($changedNode);
}
// nothing more to change, return last node
if ($changedNode === null) {
return $previousNode;
}
}
return $changedNode;
}
/**
* @param Plus|Minus $binaryOp
*/
private function processBinaryPlusAndMinus(BinaryOp $binaryOp): ?Expr
{
if ($this->isValue($binaryOp->left, 0)) {
if ($this->isNumberType($binaryOp->right)) {
return $binaryOp->right;
}
}
if ($this->isValue($binaryOp->right, 0)) {
if ($this->isNumberType($binaryOp->left)) {
return $binaryOp->left;
}
}
return null;
}
/**
* @param Mul|Div $binaryOp
*/
private function processBinaryMulAndDiv(BinaryOp $binaryOp): ?Expr
{
if ($this->isValue($binaryOp->left, 1)) {
if ($this->isNumberType($binaryOp->right)) {
return $binaryOp->right;
}
}
if ($this->isValue($binaryOp->right, 1)) {
if ($this->isNumberType($binaryOp->left)) {
return $binaryOp->left;
}
}
return null;
}
private function processAssignOp(Node $node): ?Expr
{
// +=, -=
if ($node instanceof Node\Expr\AssignOp\Plus || $node instanceof Node\Expr\AssignOp\Minus) {
if (! $this->isValue($node->expr, 0)) {
return null;
}
if ($this->isNumberType($node->var)) {
return $node->var;
}
}
// *, /
if ($node instanceof Node\Expr\AssignOp\Mul || $node instanceof Node\Expr\AssignOp\Div) {
if (! $this->isValue($node->expr, 1)) {
return null;
}
if ($this->isNumberType($node->var)) {
return $node->var;
}
}
return null;
}
private function processBinaryOp(Node $node): ?Expr
{
if ($node instanceof Plus || $node instanceof Minus) {
return $this->processBinaryPlusAndMinus($node);
}
// *, /
if ($node instanceof Mul || $node instanceof Div) {
return $this->processBinaryMulAndDiv($node);
}
return null;
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace Rector\DeadCode\Tests\Rector\Plus\RemoveDeadZeroAndOneOperationRector\Fixture;
class Assigns
{
public function run(int $value)
{
$value += 0;
$value *= 1;
}
}
?>
-----
<?php
namespace Rector\DeadCode\Tests\Rector\Plus\RemoveDeadZeroAndOneOperationRector\Fixture;
class Assigns
{
public function run(int $value)
{
$value;
$value;
}
}
?>

View File

@ -0,0 +1,33 @@
<?php
namespace Rector\DeadCode\Tests\Rector\Plus\RemoveDeadZeroAndOneOperationRector\Fixture;
class SomeClass
{
public function run()
{
$value = 5 * 1;
$value = 1 * 5;
$value = 5 + 0;
}
}
?>
-----
<?php
namespace Rector\DeadCode\Tests\Rector\Plus\RemoveDeadZeroAndOneOperationRector\Fixture;
class SomeClass
{
public function run()
{
$value = 5;
$value = 5;
$value = 5;
}
}
?>

View File

@ -0,0 +1,29 @@
<?php
namespace Rector\DeadCode\Tests\Rector\Plus\RemoveDeadZeroAndOneOperationRector\Fixture;
class Multiple
{
public function run()
{
$value = 0 + 5 + 0;
$value = 5 + 0 + 5 + 0;
}
}
?>
-----
<?php
namespace Rector\DeadCode\Tests\Rector\Plus\RemoveDeadZeroAndOneOperationRector\Fixture;
class Multiple
{
public function run()
{
$value = 5;
$value = 5 + 5;
}
}
?>

View File

@ -0,0 +1,15 @@
<?php
namespace Rector\DeadCode\Tests\Rector\Plus\RemoveDeadZeroAndOneOperationRector\Fixture;
class SkipFloats
{
public function run()
{
$value = 5;
var_dump($value); // 5
$value = $value * 1.0;
var_dump($value); // 5.0
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace Rector\DeadCode\Tests\Rector\Plus\RemoveDeadZeroAndOneOperationRector\Fixture;
class SkipTypeChnge
{
public function runBinary()
{
$value = '5';
return $value + 0;
}
public function runAssign()
{
$value = '5';
$value += 0;
return $value;
}
}

View File

@ -0,0 +1,25 @@
<?php declare(strict_types=1);
namespace Rector\DeadCode\Tests\Rector\Plus\RemoveDeadZeroAndOneOperationRector;
use Rector\DeadCode\Rector\Plus\RemoveDeadZeroAndOneOperationRector;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
final class RemoveDeadZeroAndOneOperationRectorTest extends AbstractRectorTestCase
{
public function test(): void
{
$this->doTestFiles([
__DIR__ . '/Fixture/fixture.php.inc',
__DIR__ . '/Fixture/multiple.php.inc',
__DIR__ . '/Fixture/assigns.php.inc',
__DIR__ . '/Fixture/skip_type_change.php.inc',
__DIR__ . '/Fixture/skip_floats.php.inc',
]);
}
protected function getRectorClass(): string
{
return RemoveDeadZeroAndOneOperationRector::class;
}
}

View File

@ -12,6 +12,7 @@ use PhpParser\Node\Expr\Cast;
use PhpParser\Node\Expr\ClassConstFetch; use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Scalar\String_; use PhpParser\Node\Scalar\String_;
@ -414,7 +415,7 @@ final class NodeTypeResolver
} }
// skip anonymous classes, ref https://github.com/rectorphp/rector/issues/1574 // skip anonymous classes, ref https://github.com/rectorphp/rector/issues/1574
if ($node instanceof Expr\New_) { if ($node instanceof New_) {
$className = $this->nameResolver->resolve($node->class); $className = $this->nameResolver->resolve($node->class);
if ($className === null || Strings::contains($className, 'AnonymousClass')) { if ($className === null || Strings::contains($className, 'AnonymousClass')) {
return []; return [];

View File

@ -4,6 +4,8 @@ namespace Rector\Rector\ClassMethod;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Expression;
use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\Node\AttributeKey;
@ -103,7 +105,7 @@ CODE_SAMPLE
/** /**
* Looks for "parent::<methodName> * Looks for "parent::<methodName>
*/ */
private function hasParentCallOfMethod(Node\Stmt\ClassMethod $classMethod, string $method): bool private function hasParentCallOfMethod(ClassMethod $classMethod, string $method): bool
{ {
return (bool) $this->betterNodeFinder->findFirst((array) $classMethod->stmts, function (Node $node) use ( return (bool) $this->betterNodeFinder->findFirst((array) $classMethod->stmts, function (Node $node) use (
$method $method
@ -122,7 +124,7 @@ CODE_SAMPLE
private function createParentStaticCall(string $method): Expression private function createParentStaticCall(string $method): Expression
{ {
$parentStaticCall = new StaticCall(new Node\Name('parent'), new Node\Identifier($method)); $parentStaticCall = new StaticCall(new Name('parent'), new Identifier($method));
return new Expression($parentStaticCall); return new Expression($parentStaticCall);
} }