diff --git a/composer.json b/composer.json index c045e92f..48c53a35 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ "php": ">=8.2" }, "require-dev": { - "phpecs/phpecs": "^2.0", + "phpecs/phpecs": "^2.1.1", "phpstan/extension-installer": "^1.4", "phpstan/phpstan": "^2.1.8", "phpstan/phpstan-deprecation-rules": "^2.0", diff --git a/rules-tests/CodeQuality/Rector/Class_/PreferPHPUnitSelfCallRector/Fixture/include_exceptions.php.inc b/rules-tests/CodeQuality/Rector/Class_/PreferPHPUnitSelfCallRector/Fixture/include_exceptions.php.inc new file mode 100644 index 00000000..eab2cb99 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/PreferPHPUnitSelfCallRector/Fixture/include_exceptions.php.inc @@ -0,0 +1,35 @@ +expectException(\RuntimeException::class); + $this->expectExceptionMessage('foo'); + $this->expectExceptionCode(123); + } +} + +?> +----- + diff --git a/rules/CodeQuality/Enum/NonAssertStaticableMethods.php b/rules/CodeQuality/Enum/NonAssertStaticableMethods.php new file mode 100644 index 00000000..608cf520 --- /dev/null +++ b/rules/CodeQuality/Enum/NonAssertStaticableMethods.php @@ -0,0 +1,23 @@ +nodeNameResolver->getName($call->name); + if (! str_starts_with((string) $methodName, 'assert') && ! in_array( + $methodName, + NonAssertStaticableMethods::ALL + )) { + return false; + } + + $classReflection = $this->reflectionResolver->resolveClassReflection($call); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + if ($call instanceof StaticCall && ! $this->nodeNameResolver->isNames($call->class, ['static', 'self'])) { + return false; + } + + $extendedMethodReflection = $classReflection->getNativeMethod($methodName); + + // only handle methods in TestCase or Assert class classes + $declaringClassName = $extendedMethodReflection->getDeclaringClass() + ->getName(); + + return in_array($declaringClassName, [PHPUnitClassName::TEST_CASE, PHPUnitClassName::ASSERT]); + } +} diff --git a/rules/CodeQuality/Rector/Class_/PreferPHPUnitSelfCallRector.php b/rules/CodeQuality/Rector/Class_/PreferPHPUnitSelfCallRector.php index 3aba6489..fe9e38ab 100644 --- a/rules/CodeQuality/Rector/Class_/PreferPHPUnitSelfCallRector.php +++ b/rules/CodeQuality/Rector/Class_/PreferPHPUnitSelfCallRector.php @@ -8,22 +8,20 @@ use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Stmt\Class_; -use PHPStan\Reflection\ClassReflection; -use PHPStan\Type\ObjectType; +use Rector\PHPUnit\CodeQuality\NodeAnalyser\AssertMethodAnalyzer; use Rector\PHPUnit\NodeAnalyzer\TestsNodeAnalyzer; use Rector\Rector\AbstractRector; -use Rector\Reflection\ReflectionResolver; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @see \Rector\PHPUnit\Tests\Rector\Class_\PreferPHPUnitSelfCallRector\PreferPHPUnitSelfCallRectorTest + * @see \Rector\PHPUnit\Tests\CodeQuality\Rector\Class_\PreferPHPUnitSelfCallRector\PreferPHPUnitSelfCallRectorTest */ final class PreferPHPUnitSelfCallRector extends AbstractRector { public function __construct( private readonly TestsNodeAnalyzer $testsNodeAnalyzer, - private readonly ReflectionResolver $reflectionResolver, + private readonly AssertMethodAnalyzer $assertMethodAnalyzer, ) { } @@ -81,39 +79,18 @@ public function refactor(Node $node): ?Node return null; } - $methodName = $this->getName($node->name); - if (! is_string($methodName)) { - return null; - } - - if (! str_starts_with($methodName, 'assert')) { - return null; - } - - if (! $this->isName($node->var, 'this')) { + if ($node->isFirstClassCallable()) { return null; } - if (! $this->isObjectType($node->var, new ObjectType('PHPUnit\Framework\TestCase'))) { + if (! $this->assertMethodAnalyzer->detectTestCaseCall($node)) { return null; } - $classReflection = $this->reflectionResolver->resolveClassReflection($node); - if ($classReflection instanceof ClassReflection && $classReflection->hasNativeMethod($methodName)) { - $method = $classReflection->getNativeMethod($methodName); - - if ($node->isFirstClassCallable()) { - return null; - } - - if ($method->isStatic()) { - $hasChanged = true; - - return $this->nodeFactory->createStaticCall('self', $methodName, $node->getArgs()); - } - } + $methodName = $this->getName($node->name); - return null; + $hasChanged = true; + return $this->nodeFactory->createStaticCall('self', $methodName, $node->getArgs()); }); if ($hasChanged) { diff --git a/rules/CodeQuality/Rector/Class_/PreferPHPUnitThisCallRector.php b/rules/CodeQuality/Rector/Class_/PreferPHPUnitThisCallRector.php index 9b3269ac..fab8ae12 100644 --- a/rules/CodeQuality/Rector/Class_/PreferPHPUnitThisCallRector.php +++ b/rules/CodeQuality/Rector/Class_/PreferPHPUnitThisCallRector.php @@ -11,6 +11,7 @@ use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\NodeVisitor; +use Rector\PHPUnit\CodeQuality\NodeAnalyser\AssertMethodAnalyzer; use Rector\PHPUnit\NodeAnalyzer\TestsNodeAnalyzer; use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; @@ -21,23 +22,9 @@ */ final class PreferPHPUnitThisCallRector extends AbstractRector { - /** - * @var string[] - */ - private const NON_ASSERT_STATIC_METHODS = [ - 'createMock', - 'atLeast', - 'atLeastOnce', - 'once', - 'never', - 'expectException', - 'expectExceptionMessage', - 'expectExceptionCode', - 'expectExceptionMessageMatches', - ]; - public function __construct( private readonly TestsNodeAnalyzer $testsNodeAnalyzer, + private readonly AssertMethodAnalyzer $assertMethodAnalyzer ) { } @@ -105,18 +92,11 @@ public function refactor(Node $node): ?Node return null; } - $methodName = $this->getName($node->name); - if (! is_string($methodName)) { - return null; - } - - if (! $this->isNames($node->class, ['static', 'self'])) { + if (! $this->assertMethodAnalyzer->detectTestCaseCall($node)) { return null; } - if (! str_starts_with($methodName, 'assert') && ! in_array($methodName, self::NON_ASSERT_STATIC_METHODS)) { - return null; - } + $methodName = $this->getName($node->name); $hasChanged = true; return $this->nodeFactory->createMethodCall('this', $methodName, $node->getArgs());