Skip to content

Commit 5506285

Browse files
[CodeQuality] Skip return createMock() on TypeWillReturnCallableArrowFunctionRector (#515)
* [CodeQuality] Skip return createMock() on TypeWillReturnCallableArrowFunctionRector * Fix * [ci-review] Rector Rectify * Fix phpstan * use native --------- Co-authored-by: GitHub Action <[email protected]>
1 parent 84fe73a commit 5506285

File tree

5 files changed

+84
-22
lines changed

5 files changed

+84
-22
lines changed

phpstan.neon

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,7 @@ parameters:
5757
identifier: assign.propertyType
5858

5959
- '#::provideMinPhpVersion\(\) never returns \d+ so it can be removed from the return type#'
60+
61+
-
62+
message: '#Call to an undefined method PHPStan\\Type\\Type\:\:getClassReflection\(\)#'
63+
path: rules/CodeQuality/Reflection/MethodParametersAndReturnTypesResolver.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\Class_\TypeWillReturnCallableArrowFunctionRector\Fixture;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use Symfony\Component\Form\FormInterface;
7+
8+
final class SkipReturnCreateMock extends TestCase
9+
{
10+
public function test($value): void
11+
{
12+
$form = $this->createMock(FormInterface::class);
13+
14+
$form->method('add')
15+
->willReturnCallback(function ($name, $type, $options) use (&$calls) {
16+
$calls[] = compact('name', 'type', 'options');
17+
return $this->createMock(FormInterface::class);
18+
});
19+
}
20+
}

rules/CodeQuality/Rector/Class_/TypeWillReturnCallableArrowFunctionRector.php

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use PhpParser\Node\Expr\Variable;
1414
use PhpParser\Node\Scalar\String_;
1515
use PhpParser\Node\Stmt\Class_;
16+
use PHPStan\Reflection\ClassReflection;
1617
use PHPStan\Type\IntersectionType;
1718
use PHPStan\Type\MixedType;
1819
use PHPStan\Type\NeverType;
@@ -26,6 +27,7 @@
2627
use Rector\PHPUnit\CodeQuality\ValueObject\ParamTypesAndReturnType;
2728
use Rector\PHPUnit\NodeAnalyzer\TestsNodeAnalyzer;
2829
use Rector\Rector\AbstractRector;
30+
use Rector\Reflection\ReflectionResolver;
2931
use Rector\StaticTypeMapper\StaticTypeMapper;
3032
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
3133
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@@ -45,6 +47,7 @@ public function __construct(
4547
private readonly StaticTypeMapper $staticTypeMapper,
4648
private readonly SetUpAssignedMockTypesResolver $setUpAssignedMockTypesResolver,
4749
private readonly MethodParametersAndReturnTypesResolver $methodParametersAndReturnTypesResolver,
50+
private readonly ReflectionResolver $reflectionResolver
4851
) {
4952
}
5053

@@ -127,11 +130,17 @@ public function refactor(Node $node): ?Class_
127130

128131
$hasChanged = false;
129132

133+
$currentClassReflection = $this->reflectionResolver->resolveClassReflection($node);
134+
if (! $currentClassReflection instanceof ClassReflection) {
135+
return null;
136+
}
137+
130138
$propertyNameToMockedTypes = $this->setUpAssignedMockTypesResolver->resolveFromClass($node);
131139

132140
$this->traverseNodesWithCallable($node->getMethods(), function (Node $node) use (
133141
&$hasChanged,
134-
$propertyNameToMockedTypes
142+
$propertyNameToMockedTypes,
143+
$currentClassReflection
135144
) {
136145
if (! $node instanceof MethodCall || $node->isFirstClassCallable()) {
137146
return null;
@@ -188,7 +197,8 @@ public function refactor(Node $node): ?Class_
188197

189198
$parameterTypesAndReturnType = $this->methodParametersAndReturnTypesResolver->resolveFromReflection(
190199
$callerType,
191-
$methodName
200+
$methodName,
201+
$currentClassReflection
192202
);
193203

194204
if (! $parameterTypesAndReturnType instanceof ParamTypesAndReturnType) {
@@ -229,19 +239,15 @@ public function refactor(Node $node): ?Class_
229239
$hasChanged = true;
230240
}
231241

232-
if (! $innerArg->returnType instanceof Node) {
233-
$returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode(
234-
$parameterTypesAndReturnType->getReturnType(),
235-
TypeKind::RETURN
236-
);
242+
$returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode(
243+
$parameterTypesAndReturnType->getReturnType(),
244+
TypeKind::RETURN
245+
);
237246

238-
if ($returnTypeNode instanceof Node) {
239-
$innerArg->returnType = $returnTypeNode;
240-
$hasChanged = true;
241-
}
247+
if ($returnTypeNode instanceof Node) {
248+
$innerArg->returnType = $returnTypeNode;
249+
$hasChanged = true;
242250
}
243-
244-
$hasChanged = true;
245251
});
246252

247253
if (! $hasChanged) {

rules/CodeQuality/Reflection/MethodParametersAndReturnTypesResolver.php

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use PHPStan\Reflection\ExtendedMethodReflection;
99
use PHPStan\Reflection\ParametersAcceptorSelector;
1010
use PHPStan\Type\IntersectionType;
11+
use PHPStan\Type\MixedType;
1112
use PHPStan\Type\ObjectType;
1213
use PHPStan\Type\Type;
1314
use Rector\Enum\ClassName;
@@ -17,7 +18,8 @@ final class MethodParametersAndReturnTypesResolver
1718
{
1819
public function resolveFromReflection(
1920
IntersectionType $intersectionType,
20-
string $methodName
21+
string $methodName,
22+
ClassReflection $currentClassReflection
2123
): ?ParamTypesAndReturnType {
2224
foreach ($intersectionType->getTypes() as $intersectionedType) {
2325
if (! $intersectionedType instanceof ObjectType) {
@@ -39,8 +41,8 @@ public function resolveFromReflection(
3941

4042
$mockedMethodReflection = $classReflection->getNativeMethod($methodName);
4143

42-
$parameterTypes = $this->resolveParameterTypes($mockedMethodReflection);
43-
$returnType = $this->resolveReturnType($mockedMethodReflection);
44+
$parameterTypes = $this->resolveParameterTypes($mockedMethodReflection, $currentClassReflection);
45+
$returnType = $this->resolveReturnType($mockedMethodReflection, $currentClassReflection);
4446

4547
return new ParamTypesAndReturnType($parameterTypes, $returnType);
4648
}
@@ -51,26 +53,42 @@ public function resolveFromReflection(
5153
/**
5254
* @return Type[]
5355
*/
54-
private function resolveParameterTypes(ExtendedMethodReflection $extendedMethodReflection): array
55-
{
56+
private function resolveParameterTypes(
57+
ExtendedMethodReflection $extendedMethodReflection,
58+
ClassReflection $currentClassReflection
59+
): array {
5660
$extendedParametersAcceptor = ParametersAcceptorSelector::combineAcceptors(
5761
$extendedMethodReflection->getVariants()
5862
);
5963

6064
$parameterTypes = [];
6165
foreach ($extendedParametersAcceptor->getParameters() as $parameterReflection) {
62-
$parameterTypes[] = $parameterReflection->getType();
66+
$parameterType = $parameterReflection->getNativeType();
67+
68+
if ($parameterType->isObject()->yes() && $currentClassReflection->getName() !== $parameterType->getClassReflection()->getName()) {
69+
return [];
70+
}
71+
72+
$parameterTypes[] = $parameterType;
6373
}
6474

6575
return $parameterTypes;
6676
}
6777

68-
private function resolveReturnType(ExtendedMethodReflection $extendedMethodReflection): Type
69-
{
78+
private function resolveReturnType(
79+
ExtendedMethodReflection $extendedMethodReflection,
80+
ClassReflection $currentClassReflection
81+
): Type {
7082
$extendedParametersAcceptor = ParametersAcceptorSelector::combineAcceptors(
7183
$extendedMethodReflection->getVariants()
7284
);
7385

74-
return $extendedParametersAcceptor->getReturnType();
86+
$returnType = $extendedParametersAcceptor->getNativeReturnType();
87+
88+
if ($returnType->isObject()->yes() && $currentClassReflection->getName() !== $returnType->getClassReflection()->getName()) {
89+
return new MixedType();
90+
}
91+
92+
return $returnType;
7593
}
7694
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symfony\Component\Form;
6+
7+
if (interface_exists('Symfony\Component\Form\FormInterface')) {
8+
return;
9+
}
10+
11+
interface FormInterface
12+
{
13+
public function add(): static;
14+
}

0 commit comments

Comments
 (0)