diff --git a/rules-tests/CodeQuality/Rector/MethodCall/CallbackSingleAssertToSimplerRector/Fixture/flipped_param_order.php.inc b/rules-tests/CodeQuality/Rector/MethodCall/CallbackSingleAssertToSimplerRector/Fixture/flipped_param_order.php.inc new file mode 100644 index 00000000..73fefbcc --- /dev/null +++ b/rules-tests/CodeQuality/Rector/MethodCall/CallbackSingleAssertToSimplerRector/Fixture/flipped_param_order.php.inc @@ -0,0 +1,43 @@ +getMockBuilder('AnyType')->getMock(); + + $builder->expects($this->exactly(2)) + ->method('add') + ->with($this->callback(function ($type): bool { + $this->assertSame($type, TextType::class); + + return true; + })); + } +} + +?> +----- +getMockBuilder('AnyType')->getMock(); + + $builder->expects($this->exactly(2)) + ->method('add') + ->with($this->equalTo(TextType::class)); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/MethodCall/CallbackSingleAssertToSimplerRector/Fixture/skip_nested_compare.php.inc b/rules-tests/CodeQuality/Rector/MethodCall/CallbackSingleAssertToSimplerRector/Fixture/skip_nested_compare.php.inc new file mode 100644 index 00000000..c19d0537 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/MethodCall/CallbackSingleAssertToSimplerRector/Fixture/skip_nested_compare.php.inc @@ -0,0 +1,21 @@ +getMockBuilder('AnyType')->getMock(); + + $builder->expects($this->exactly(2)) + ->method('add') + ->with($this->callback(function (array $args): bool { + $this->assertSame($args['label'], 'Test Label'); + + return true; + })); + } +} diff --git a/rules-tests/CodeQuality/Rector/MethodCall/CallbackSingleAssertToSimplerRector/Fixture/skip_nested_compare_expected_left.php.inc b/rules-tests/CodeQuality/Rector/MethodCall/CallbackSingleAssertToSimplerRector/Fixture/skip_nested_compare_expected_left.php.inc new file mode 100644 index 00000000..3703c47d --- /dev/null +++ b/rules-tests/CodeQuality/Rector/MethodCall/CallbackSingleAssertToSimplerRector/Fixture/skip_nested_compare_expected_left.php.inc @@ -0,0 +1,21 @@ +getMockBuilder('AnyType')->getMock(); + + $builder->expects($this->exactly(2)) + ->method('add') + ->with($this->callback(function (array $args): bool { + $this->assertSame('Test Label', $args['label']); + + return true; + })); + } +} diff --git a/rules/CodeQuality/Rector/MethodCall/CallbackSingleAssertToSimplerRector.php b/rules/CodeQuality/Rector/MethodCall/CallbackSingleAssertToSimplerRector.php index 95db43ad..67fd6c7a 100644 --- a/rules/CodeQuality/Rector/MethodCall/CallbackSingleAssertToSimplerRector.php +++ b/rules/CodeQuality/Rector/MethodCall/CallbackSingleAssertToSimplerRector.php @@ -9,6 +9,7 @@ use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\ConstFetch; use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\Variable; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Return_; use Rector\PHPUnit\NodeAnalyzer\TestsNodeAnalyzer; @@ -153,8 +154,43 @@ private function matchCallbackSoleAssertSameExpected(Expr $expr): ?Expr return null; } - return $firstStmtExpr->getArgs()[0] - ->value; + $assertSameArgs = $firstStmtExpr->getArgs(); + if (count($assertSameArgs) !== 2) { + return null; + } + + $firstValue = $assertSameArgs[0]->value; + $secondValue = $assertSameArgs[1]->value; + + // one side must be the whole closure parameter; the other side is the expected value + // nested access (e.g. $args['label']) is skipped, as equalTo() matches the whole argument + if ($this->isClosureSoleParam($innerClosure, $secondValue)) { + return $firstValue; + } + + if ($this->isClosureSoleParam($innerClosure, $firstValue)) { + return $secondValue; + } + + return null; + } + + private function isClosureSoleParam(Closure $closure, Expr $expr): bool + { + if (count($closure->params) !== 1) { + return false; + } + + if (! $expr instanceof Variable) { + return false; + } + + $soleParam = $closure->params[0]; + if (! $soleParam->var instanceof Variable) { + return false; + } + + return $this->nodeComparator->areNodesEqual($soleParam->var, $expr); } private function isTrueReturn(Return_ $return): bool