From 9895d0a8b4eefb1885b518312451da1d66bc800a Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Tue, 14 Jul 2026 11:10:08 +0200 Subject: [PATCH 1/2] [CodeQuality] Move AssertClassToThisAssertRector hook from Class_ to ClassMethod The rule only ever rewrote Assert::assert*() calls inside class methods, so hooking on ClassMethod is more precise and avoids re-traversing the whole class. Static methods are now skipped with an early return instead of a DONT_TRAVERSE_CURRENT_AND_CHILDREN check inside the traversal callback; static closures and arrow functions are still skipped during traversal. --- .../AssertClassToThisAssertRectorTest.php | 2 +- .../Fixture/basic.php.inc | 4 ++-- .../Fixture/fully_qualified.php.inc | 4 ++-- .../Fixture/skip_non_test_class.php.inc | 2 +- .../Fixture/skip_self_call.php.inc | 2 +- .../Fixture/skip_static_closure.php.inc | 2 +- .../Fixture/skip_static_method.php.inc | 2 +- .../config/configured_rule.php | 2 +- .../AssertClassToThisAssertRector.php | 16 +++++++++------- 9 files changed, 19 insertions(+), 17 deletions(-) rename rules-tests/CodeQuality/Rector/{Class_ => ClassMethod}/AssertClassToThisAssertRector/AssertClassToThisAssertRectorTest.php (86%) rename rules-tests/CodeQuality/Rector/{Class_ => ClassMethod}/AssertClassToThisAssertRector/Fixture/basic.php.inc (65%) rename rules-tests/CodeQuality/Rector/{Class_ => ClassMethod}/AssertClassToThisAssertRector/Fixture/fully_qualified.php.inc (66%) rename rules-tests/CodeQuality/Rector/{Class_ => ClassMethod}/AssertClassToThisAssertRector/Fixture/skip_non_test_class.php.inc (58%) rename rules-tests/CodeQuality/Rector/{Class_ => ClassMethod}/AssertClassToThisAssertRector/Fixture/skip_self_call.php.inc (61%) rename rules-tests/CodeQuality/Rector/{Class_ => ClassMethod}/AssertClassToThisAssertRector/Fixture/skip_static_closure.php.inc (67%) rename rules-tests/CodeQuality/Rector/{Class_ => ClassMethod}/AssertClassToThisAssertRector/Fixture/skip_static_method.php.inc (66%) rename rules-tests/CodeQuality/Rector/{Class_ => ClassMethod}/AssertClassToThisAssertRector/config/configured_rule.php (70%) rename rules/CodeQuality/Rector/{Class_ => ClassMethod}/AssertClassToThisAssertRector.php (84%) diff --git a/rules-tests/CodeQuality/Rector/Class_/AssertClassToThisAssertRector/AssertClassToThisAssertRectorTest.php b/rules-tests/CodeQuality/Rector/ClassMethod/AssertClassToThisAssertRector/AssertClassToThisAssertRectorTest.php similarity index 86% rename from rules-tests/CodeQuality/Rector/Class_/AssertClassToThisAssertRector/AssertClassToThisAssertRectorTest.php rename to rules-tests/CodeQuality/Rector/ClassMethod/AssertClassToThisAssertRector/AssertClassToThisAssertRectorTest.php index 0a94c042..ebf2bfaf 100644 --- a/rules-tests/CodeQuality/Rector/Class_/AssertClassToThisAssertRector/AssertClassToThisAssertRectorTest.php +++ b/rules-tests/CodeQuality/Rector/ClassMethod/AssertClassToThisAssertRector/AssertClassToThisAssertRectorTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\PHPUnit\Tests\CodeQuality\Rector\Class_\AssertClassToThisAssertRector; +namespace Rector\PHPUnit\Tests\CodeQuality\Rector\ClassMethod\AssertClassToThisAssertRector; use Iterator; use PHPUnit\Framework\Attributes\DataProvider; diff --git a/rules-tests/CodeQuality/Rector/Class_/AssertClassToThisAssertRector/Fixture/basic.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/AssertClassToThisAssertRector/Fixture/basic.php.inc similarity index 65% rename from rules-tests/CodeQuality/Rector/Class_/AssertClassToThisAssertRector/Fixture/basic.php.inc rename to rules-tests/CodeQuality/Rector/ClassMethod/AssertClassToThisAssertRector/Fixture/basic.php.inc index 19f03b86..afb7aa4d 100644 --- a/rules-tests/CodeQuality/Rector/Class_/AssertClassToThisAssertRector/Fixture/basic.php.inc +++ b/rules-tests/CodeQuality/Rector/ClassMethod/AssertClassToThisAssertRector/Fixture/basic.php.inc @@ -1,6 +1,6 @@ rule(AssertClassToThisAssertRector::class); diff --git a/rules/CodeQuality/Rector/Class_/AssertClassToThisAssertRector.php b/rules/CodeQuality/Rector/ClassMethod/AssertClassToThisAssertRector.php similarity index 84% rename from rules/CodeQuality/Rector/Class_/AssertClassToThisAssertRector.php rename to rules/CodeQuality/Rector/ClassMethod/AssertClassToThisAssertRector.php index 09d94fa6..ccde1581 100644 --- a/rules/CodeQuality/Rector/Class_/AssertClassToThisAssertRector.php +++ b/rules/CodeQuality/Rector/ClassMethod/AssertClassToThisAssertRector.php @@ -2,14 +2,13 @@ declare(strict_types=1); -namespace Rector\PHPUnit\CodeQuality\Rector\Class_; +namespace Rector\PHPUnit\CodeQuality\Rector\ClassMethod; use PhpParser\Node; use PhpParser\Node\Expr\ArrowFunction; use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\StaticCall; -use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\NodeVisitor; use Rector\PHPUnit\Enum\PHPUnitClassName; @@ -19,7 +18,7 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @see \Rector\PHPUnit\Tests\CodeQuality\Rector\Class_\AssertClassToThisAssertRector\AssertClassToThisAssertRectorTest + * @see \Rector\PHPUnit\Tests\CodeQuality\Rector\ClassMethod\AssertClassToThisAssertRector\AssertClassToThisAssertRectorTest */ final class AssertClassToThisAssertRector extends AbstractRector { @@ -66,14 +65,18 @@ public function run() */ public function getNodeTypes(): array { - return [Class_::class]; + return [ClassMethod::class]; } /** - * @param Class_ $node + * @param ClassMethod $node */ public function refactor(Node $node): ?Node { + if ($node->isStatic()) { + return null; + } + if (! $this->testsNodeAnalyzer->isInTestClass($node)) { return null; } @@ -81,8 +84,7 @@ public function refactor(Node $node): ?Node $hasChanged = false; $this->traverseNodesWithCallable($node, function (Node $node) use (&$hasChanged): int|null|MethodCall { - $isInsideStaticFunctionLike = ($node instanceof ClassMethod && $node->isStatic()) || (($node instanceof Closure || $node instanceof ArrowFunction) && $node->static); - if ($isInsideStaticFunctionLike) { + if (($node instanceof Closure || $node instanceof ArrowFunction) && $node->static) { return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN; } From cbe2fdd7cf69b3b00a87de3d6ecbef660a433f77 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Tue, 14 Jul 2026 11:12:22 +0200 Subject: [PATCH 2/2] Add fixture for Assert::assert*() inside foreach --- .../Fixture/inside_foreach.php.inc | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 rules-tests/CodeQuality/Rector/ClassMethod/AssertClassToThisAssertRector/Fixture/inside_foreach.php.inc diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/AssertClassToThisAssertRector/Fixture/inside_foreach.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/AssertClassToThisAssertRector/Fixture/inside_foreach.php.inc new file mode 100644 index 00000000..6944c58e --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/AssertClassToThisAssertRector/Fixture/inside_foreach.php.inc @@ -0,0 +1,65 @@ +createMock(SomeEntity::class); + $entityMock->method('getId') + ->willReturn(1); + $entities = [$entityMock]; + + $parameters = [ + ['id' => 1, 'foo' => 'bar'], + ]; + + $helper = new SomeHelper($parameters); + $orderedEntities = $helper->orderByOriginalKey($entities); + + $this->assertSame([0], array_keys($orderedEntities)); + + foreach ($parameters as $key => $contact) { + Assert::assertEquals($orderedEntities[$key]->getId(), $entities[$key]->getId()); + } + } +} + +?> +----- +createMock(SomeEntity::class); + $entityMock->method('getId') + ->willReturn(1); + $entities = [$entityMock]; + + $parameters = [ + ['id' => 1, 'foo' => 'bar'], + ]; + + $helper = new SomeHelper($parameters); + $orderedEntities = $helper->orderByOriginalKey($entities); + + $this->assertSame([0], array_keys($orderedEntities)); + + foreach ($parameters as $key => $contact) { + $this->assertEquals($orderedEntities[$key]->getId(), $entities[$key]->getId()); + } + } +} + +?>