Skip to content

Commit 33c2f56

Browse files
authored
Tidy up (#478)
* cs * add exception support to PreferPHPUnitSelfCallRector
1 parent c61efd3 commit 33c2f56

File tree

6 files changed

+121
-56
lines changed

6 files changed

+121
-56
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"php": ">=8.2"
88
},
99
"require-dev": {
10-
"phpecs/phpecs": "^2.0",
10+
"phpecs/phpecs": "^2.1.1",
1111
"phpstan/extension-installer": "^1.4",
1212
"phpstan/phpstan": "^2.1.8",
1313
"phpstan/phpstan-deprecation-rules": "^2.0",
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\Class_\PreferPHPUnitSelfCallRector\Fixture;
4+
5+
use PHPUnit\Framework\TestCase;
6+
7+
final class IncludeExceptions extends TestCase
8+
{
9+
public function testMe()
10+
{
11+
$this->expectException(\RuntimeException::class);
12+
$this->expectExceptionMessage('foo');
13+
$this->expectExceptionCode(123);
14+
}
15+
}
16+
17+
?>
18+
-----
19+
<?php
20+
21+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\Class_\PreferPHPUnitSelfCallRector\Fixture;
22+
23+
use PHPUnit\Framework\TestCase;
24+
25+
final class IncludeExceptions extends TestCase
26+
{
27+
public function testMe()
28+
{
29+
self::expectException(\RuntimeException::class);
30+
self::expectExceptionMessage('foo');
31+
self::expectExceptionCode(123);
32+
}
33+
}
34+
35+
?>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\PHPUnit\CodeQuality\Enum;
6+
7+
final class NonAssertStaticableMethods
8+
{
9+
/**
10+
* @var string[]
11+
*/
12+
public const ALL = [
13+
'createMock',
14+
'atLeast',
15+
'atLeastOnce',
16+
'once',
17+
'never',
18+
'expectException',
19+
'expectExceptionMessage',
20+
'expectExceptionCode',
21+
'expectExceptionMessageMatches',
22+
];
23+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\PHPUnit\CodeQuality\NodeAnalyser;
6+
7+
use PhpParser\Node\Expr\MethodCall;
8+
use PhpParser\Node\Expr\StaticCall;
9+
use PHPStan\Reflection\ClassReflection;
10+
use Rector\NodeNameResolver\NodeNameResolver;
11+
use Rector\PHPUnit\CodeQuality\Enum\NonAssertStaticableMethods;
12+
use Rector\PHPUnit\Enum\PHPUnitClassName;
13+
use Rector\Reflection\ReflectionResolver;
14+
15+
final readonly class AssertMethodAnalyzer
16+
{
17+
public function __construct(
18+
private NodeNameResolver $nodeNameResolver,
19+
private ReflectionResolver $reflectionResolver,
20+
) {
21+
}
22+
23+
public function detectTestCaseCall(MethodCall|StaticCall $call): bool
24+
{
25+
$methodName = $this->nodeNameResolver->getName($call->name);
26+
if (! str_starts_with((string) $methodName, 'assert') && ! in_array(
27+
$methodName,
28+
NonAssertStaticableMethods::ALL
29+
)) {
30+
return false;
31+
}
32+
33+
$classReflection = $this->reflectionResolver->resolveClassReflection($call);
34+
if (! $classReflection instanceof ClassReflection) {
35+
return false;
36+
}
37+
38+
if ($call instanceof StaticCall && ! $this->nodeNameResolver->isNames($call->class, ['static', 'self'])) {
39+
return false;
40+
}
41+
42+
$extendedMethodReflection = $classReflection->getNativeMethod($methodName);
43+
44+
// only handle methods in TestCase or Assert class classes
45+
$declaringClassName = $extendedMethodReflection->getDeclaringClass()
46+
->getName();
47+
48+
return in_array($declaringClassName, [PHPUnitClassName::TEST_CASE, PHPUnitClassName::ASSERT]);
49+
}
50+
}

rules/CodeQuality/Rector/Class_/PreferPHPUnitSelfCallRector.php

Lines changed: 8 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,20 @@
88
use PhpParser\Node\Expr\MethodCall;
99
use PhpParser\Node\Expr\StaticCall;
1010
use PhpParser\Node\Stmt\Class_;
11-
use PHPStan\Reflection\ClassReflection;
12-
use PHPStan\Type\ObjectType;
11+
use Rector\PHPUnit\CodeQuality\NodeAnalyser\AssertMethodAnalyzer;
1312
use Rector\PHPUnit\NodeAnalyzer\TestsNodeAnalyzer;
1413
use Rector\Rector\AbstractRector;
15-
use Rector\Reflection\ReflectionResolver;
1614
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
1715
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
1816

1917
/**
20-
* @see \Rector\PHPUnit\Tests\Rector\Class_\PreferPHPUnitSelfCallRector\PreferPHPUnitSelfCallRectorTest
18+
* @see \Rector\PHPUnit\Tests\CodeQuality\Rector\Class_\PreferPHPUnitSelfCallRector\PreferPHPUnitSelfCallRectorTest
2119
*/
2220
final class PreferPHPUnitSelfCallRector extends AbstractRector
2321
{
2422
public function __construct(
2523
private readonly TestsNodeAnalyzer $testsNodeAnalyzer,
26-
private readonly ReflectionResolver $reflectionResolver,
24+
private readonly AssertMethodAnalyzer $assertMethodAnalyzer,
2725
) {
2826
}
2927

@@ -81,39 +79,18 @@ public function refactor(Node $node): ?Node
8179
return null;
8280
}
8381

84-
$methodName = $this->getName($node->name);
85-
if (! is_string($methodName)) {
86-
return null;
87-
}
88-
89-
if (! str_starts_with($methodName, 'assert')) {
90-
return null;
91-
}
92-
93-
if (! $this->isName($node->var, 'this')) {
82+
if ($node->isFirstClassCallable()) {
9483
return null;
9584
}
9685

97-
if (! $this->isObjectType($node->var, new ObjectType('PHPUnit\Framework\TestCase'))) {
86+
if (! $this->assertMethodAnalyzer->detectTestCaseCall($node)) {
9887
return null;
9988
}
10089

101-
$classReflection = $this->reflectionResolver->resolveClassReflection($node);
102-
if ($classReflection instanceof ClassReflection && $classReflection->hasNativeMethod($methodName)) {
103-
$method = $classReflection->getNativeMethod($methodName);
104-
105-
if ($node->isFirstClassCallable()) {
106-
return null;
107-
}
108-
109-
if ($method->isStatic()) {
110-
$hasChanged = true;
111-
112-
return $this->nodeFactory->createStaticCall('self', $methodName, $node->getArgs());
113-
}
114-
}
90+
$methodName = $this->getName($node->name);
11591

116-
return null;
92+
$hasChanged = true;
93+
return $this->nodeFactory->createStaticCall('self', $methodName, $node->getArgs());
11794
});
11895

11996
if ($hasChanged) {

rules/CodeQuality/Rector/Class_/PreferPHPUnitThisCallRector.php

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use PhpParser\Node\Stmt\Class_;
1212
use PhpParser\Node\Stmt\ClassMethod;
1313
use PhpParser\NodeVisitor;
14+
use Rector\PHPUnit\CodeQuality\NodeAnalyser\AssertMethodAnalyzer;
1415
use Rector\PHPUnit\NodeAnalyzer\TestsNodeAnalyzer;
1516
use Rector\Rector\AbstractRector;
1617
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
@@ -21,23 +22,9 @@
2122
*/
2223
final class PreferPHPUnitThisCallRector extends AbstractRector
2324
{
24-
/**
25-
* @var string[]
26-
*/
27-
private const NON_ASSERT_STATIC_METHODS = [
28-
'createMock',
29-
'atLeast',
30-
'atLeastOnce',
31-
'once',
32-
'never',
33-
'expectException',
34-
'expectExceptionMessage',
35-
'expectExceptionCode',
36-
'expectExceptionMessageMatches',
37-
];
38-
3925
public function __construct(
4026
private readonly TestsNodeAnalyzer $testsNodeAnalyzer,
27+
private readonly AssertMethodAnalyzer $assertMethodAnalyzer
4128
) {
4229
}
4330

@@ -105,18 +92,11 @@ public function refactor(Node $node): ?Node
10592
return null;
10693
}
10794

108-
$methodName = $this->getName($node->name);
109-
if (! is_string($methodName)) {
110-
return null;
111-
}
112-
113-
if (! $this->isNames($node->class, ['static', 'self'])) {
95+
if (! $this->assertMethodAnalyzer->detectTestCaseCall($node)) {
11496
return null;
11597
}
11698

117-
if (! str_starts_with($methodName, 'assert') && ! in_array($methodName, self::NON_ASSERT_STATIC_METHODS)) {
118-
return null;
119-
}
99+
$methodName = $this->getName($node->name);
120100

121101
$hasChanged = true;
122102
return $this->nodeFactory->createMethodCall('this', $methodName, $node->getArgs());

0 commit comments

Comments
 (0)