Skip to content

Commit e7e54a8

Browse files
committed
[code-quality] Add TypeWillReturnCallableArrowFunctionRector
1 parent 33c2f56 commit e7e54a8

File tree

9 files changed

+564
-1
lines changed

9 files changed

+564
-1
lines changed

config/sets/phpunit-code-quality.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Rector\PHPUnit\CodeQuality\Rector\Class_\RemoveDataProviderParamKeysRector;
1010
use Rector\PHPUnit\CodeQuality\Rector\Class_\SingleMockPropertyTypeRector;
1111
use Rector\PHPUnit\CodeQuality\Rector\Class_\TestWithToDataProviderRector;
12+
use Rector\PHPUnit\CodeQuality\Rector\Class_\TypeWillReturnCallableArrowFunctionRector;
1213
use Rector\PHPUnit\CodeQuality\Rector\Class_\YieldDataProviderRector;
1314
use Rector\PHPUnit\CodeQuality\Rector\ClassMethod\AddInstanceofAssertForNullableInstanceRector;
1415
use Rector\PHPUnit\CodeQuality\Rector\ClassMethod\DataProviderArrayItemsNewLinedRector;
@@ -60,10 +61,12 @@
6061
NarrowSingleWillReturnCallbackRector::class,
6162
SingleWithConsecutiveToWithRector::class,
6263

64+
// type declarations
65+
TypeWillReturnCallableArrowFunctionRector::class,
66+
6367
NarrowUnusedSetUpDefinedPropertyRector::class,
6468

6569
// specific asserts
66-
6770
AssertCompareOnCountableWithMethodToAssertCountRector::class,
6871
AssertComparisonToSpecificMethodRector::class,
6972
AssertNotOperatorRector::class,
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\Class_\TypeWillReturnCallableArrowFunctionRector\Fixture;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use Rector\PHPUnit\Tests\CodeQuality\Rector\Class_\TypeWillReturnCallableArrowFunctionRector\Source\SomeMockedClass;
7+
8+
final class FillKnownParamType extends TestCase
9+
{
10+
public function test($value): void
11+
{
12+
$this->createMock(SomeMockedClass::class)
13+
->method('someMethod')
14+
->willReturnCallback(fn ($name) => $value);
15+
}
16+
}
17+
18+
?>
19+
-----
20+
<?php
21+
22+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\Class_\TypeWillReturnCallableArrowFunctionRector\Fixture;
23+
24+
use PHPUnit\Framework\TestCase;
25+
use Rector\PHPUnit\Tests\CodeQuality\Rector\Class_\TypeWillReturnCallableArrowFunctionRector\Source\SomeMockedClass;
26+
27+
final class FillKnownParamType extends TestCase
28+
{
29+
public function test($value): void
30+
{
31+
$this->createMock(SomeMockedClass::class)
32+
->method('someMethod')
33+
->willReturnCallback(fn (string $name): int => $value);
34+
}
35+
}
36+
37+
?>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\Class_\TypeWillReturnCallableArrowFunctionRector\Source;
4+
5+
// non final on purpose so PHPStan can analyze it
6+
class SomeMockedClass
7+
{
8+
public function someMethod(string $name): int
9+
{
10+
return 100;
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\Class_\TypeWillReturnCallableArrowFunctionRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class TypeWillReturnCallableArrowFunctionRectorTest extends AbstractRectorTestCase
12+
{
13+
#[DataProvider('provideData')]
14+
public function test(string $filePath): void
15+
{
16+
$this->doTestFile($filePath);
17+
}
18+
19+
public static function provideData(): Iterator
20+
{
21+
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
22+
}
23+
24+
public function provideConfigFilePath(): string
25+
{
26+
return __DIR__ . '/config/configured_rule.php';
27+
}
28+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Rector\Config\RectorConfig;
6+
use Rector\PHPUnit\CodeQuality\Rector\Class_\TypeWillReturnCallableArrowFunctionRector;
7+
8+
return static function (RectorConfig $rectorConfig): void {
9+
$rectorConfig->rule(TypeWillReturnCallableArrowFunctionRector::class);
10+
};
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\PHPUnit\CodeQuality\NodeAnalyser;
6+
7+
use PhpParser\Node\Expr\Assign;
8+
use PhpParser\Node\Expr\ClassConstFetch;
9+
use PhpParser\Node\Expr\MethodCall;
10+
use PhpParser\Node\Expr\PropertyFetch;
11+
use PhpParser\Node\Expr\Variable;
12+
use PhpParser\Node\Stmt\Class_;
13+
use PhpParser\Node\Stmt\ClassMethod;
14+
use PhpParser\Node\Stmt\Expression;
15+
use Rector\NodeNameResolver\NodeNameResolver;
16+
use Rector\ValueObject\MethodName;
17+
use Webmozart\Assert\Assert;
18+
19+
final readonly class SetUpAssignedMockTypesResolver
20+
{
21+
public function __construct(
22+
private NodeNameResolver $nodeNameResolver
23+
) {
24+
}
25+
26+
/**
27+
* @return array<string, string>
28+
*/
29+
public function resolveFromClass(Class_ $class): array
30+
{
31+
$setUpClassMethod = $class->getMethod(MethodName::SET_UP);
32+
if (! $setUpClassMethod instanceof ClassMethod) {
33+
return [];
34+
}
35+
36+
$propertyNameToMockedTypes = [];
37+
foreach ((array) $setUpClassMethod->stmts as $stmt) {
38+
if (! $stmt instanceof Expression) {
39+
continue;
40+
}
41+
42+
if (! $stmt->expr instanceof Assign) {
43+
continue;
44+
}
45+
46+
$assign = $stmt->expr;
47+
if (! $assign->expr instanceof MethodCall) {
48+
continue;
49+
}
50+
51+
if (! $this->nodeNameResolver->isNames($assign->expr->name, ['createMock', 'getMockBuilder'])) {
52+
continue;
53+
}
54+
55+
if (! $assign->var instanceof PropertyFetch && ! $assign->var instanceof Variable) {
56+
continue;
57+
}
58+
59+
$mockedClassNameExpr = $assign->expr->getArgs()[0]
60+
->value;
61+
if (! $mockedClassNameExpr instanceof ClassConstFetch) {
62+
continue;
63+
}
64+
65+
$propertyOrVariableName = $this->resolvePropertyOrVariableName($assign->var);
66+
$mockedClass = $this->nodeNameResolver->getName($mockedClassNameExpr->class);
67+
68+
Assert::string($mockedClass);
69+
70+
$propertyNameToMockedTypes[$propertyOrVariableName] = $mockedClass;
71+
}
72+
73+
return $propertyNameToMockedTypes;
74+
}
75+
76+
private function resolvePropertyOrVariableName(PropertyFetch|Variable $propertyFetchOrVariable): ?string
77+
{
78+
if ($propertyFetchOrVariable instanceof Variable) {
79+
return $this->nodeNameResolver->getName($propertyFetchOrVariable);
80+
}
81+
82+
return $this->nodeNameResolver->getName($propertyFetchOrVariable->name);
83+
}
84+
}

0 commit comments

Comments
 (0)