diff --git a/config/sets/phpunit-code-quality.php b/config/sets/phpunit-code-quality.php index be67fa3fd..10aba745e 100644 --- a/config/sets/phpunit-code-quality.php +++ b/config/sets/phpunit-code-quality.php @@ -6,6 +6,7 @@ use Rector\PHPUnit\CodeQuality\Rector\CallLike\DirectInstanceOverMockArgRector; use Rector\PHPUnit\CodeQuality\Rector\Class_\AddParamTypeFromDependsRector; use Rector\PHPUnit\CodeQuality\Rector\Class_\AddReturnTypeToDependedRector; +use Rector\PHPUnit\CodeQuality\Rector\Class_\AddStubIntersectionVarToStubPropertyRector; use Rector\PHPUnit\CodeQuality\Rector\Class_\ConstructClassMethodToSetUpTestCaseRector; use Rector\PHPUnit\CodeQuality\Rector\Class_\InlineStubPropertyToCreateStubMethodCallRector; use Rector\PHPUnit\CodeQuality\Rector\Class_\NarrowUnusedSetUpDefinedPropertyRector; @@ -57,7 +58,6 @@ use Rector\PHPUnit\PHPUnit120\Rector\CallLike\CreateStubOverCreateMockArgRector; use Rector\PHPUnit\PHPUnit120\Rector\Class_\PropertyCreateMockToCreateStubRector; use Rector\PHPUnit\PHPUnit120\Rector\ClassMethod\ExpressionCreateMockToCreateStubRector; -use Rector\PHPUnit\PHPUnit120\Rector\Property\BareVarToStubIntersectionRector; use Rector\PHPUnit\PHPUnit120\Rector\Property\MockObjectVarToStubRector; use Rector\PHPUnit\PHPUnit60\Rector\MethodCall\GetMockBuilderGetMockToCreateMockRector; use Rector\PHPUnit\PHPUnit90\Rector\MethodCall\ReplaceAtMethodWithDesiredMatcherRector; @@ -139,7 +139,7 @@ ExpressionCreateMockToCreateStubRector::class, PropertyCreateMockToCreateStubRector::class, MockObjectVarToStubRector::class, - BareVarToStubIntersectionRector::class, + AddStubIntersectionVarToStubPropertyRector::class, InlineStubPropertyToCreateStubMethodCallRector::class, // @test first, enable later diff --git a/config/sets/phpunit-mock-to-stub.php b/config/sets/phpunit-mock-to-stub.php index 14a06212f..72cd48c85 100644 --- a/config/sets/phpunit-mock-to-stub.php +++ b/config/sets/phpunit-mock-to-stub.php @@ -4,11 +4,11 @@ use Rector\Config\RectorConfig; use Rector\PHPUnit\CodeQuality\Rector\Class_\AddIntersectionVarToMockObjectPropertyRector; +use Rector\PHPUnit\CodeQuality\Rector\Class_\AddStubIntersectionVarToStubPropertyRector; use Rector\PHPUnit\PHPUnit120\Rector\CallLike\CreateStubInCoalesceArgRector; use Rector\PHPUnit\PHPUnit120\Rector\CallLike\CreateStubOverCreateMockArgRector; use Rector\PHPUnit\PHPUnit120\Rector\Class_\PropertyCreateMockToCreateStubRector; use Rector\PHPUnit\PHPUnit120\Rector\ClassMethod\ExpressionCreateMockToCreateStubRector; -use Rector\PHPUnit\PHPUnit120\Rector\Property\BareVarToStubIntersectionRector; use Rector\PHPUnit\PHPUnit120\Rector\Property\MockObjectVarToStubRector; return static function (RectorConfig $rectorConfig): void { @@ -19,7 +19,7 @@ ExpressionCreateMockToCreateStubRector::class, PropertyCreateMockToCreateStubRector::class, MockObjectVarToStubRector::class, - BareVarToStubIntersectionRector::class, AddIntersectionVarToMockObjectPropertyRector::class, + AddStubIntersectionVarToStubPropertyRector::class, ]); }; diff --git a/rules-tests/CodeQuality/Rector/Class_/AddIntersectionVarToMockObjectPropertyRector/Fixture/skip_existing_intersection_var.php.inc b/rules-tests/CodeQuality/Rector/Class_/AddIntersectionVarToMockObjectPropertyRector/Fixture/skip_existing_intersection_var.php.inc new file mode 100644 index 000000000..1c221ad39 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/AddIntersectionVarToMockObjectPropertyRector/Fixture/skip_existing_intersection_var.php.inc @@ -0,0 +1,18 @@ +someServiceMock = $this->createMock(\stdClass::class); + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/AddIntersectionVarToMockObjectPropertyRector/Fixture/static_call.php.inc b/rules-tests/CodeQuality/Rector/Class_/AddIntersectionVarToMockObjectPropertyRector/Fixture/static_call.php.inc new file mode 100644 index 000000000..0624b3f19 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/AddIntersectionVarToMockObjectPropertyRector/Fixture/static_call.php.inc @@ -0,0 +1,38 @@ +someServiceMock = self::createMock(\stdClass::class); + } +} + +?> +----- +someServiceMock = self::createMock(\stdClass::class); + } +} + +?> diff --git a/rules-tests/PHPUnit120/Rector/Property/BareVarToStubIntersectionRector/BareVarToStubIntersectionRectorTest.php b/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/AddStubIntersectionVarToStubPropertyRectorTest.php similarity index 73% rename from rules-tests/PHPUnit120/Rector/Property/BareVarToStubIntersectionRector/BareVarToStubIntersectionRectorTest.php rename to rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/AddStubIntersectionVarToStubPropertyRectorTest.php index d2bb6f0f9..9b5996834 100644 --- a/rules-tests/PHPUnit120/Rector/Property/BareVarToStubIntersectionRector/BareVarToStubIntersectionRectorTest.php +++ b/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/AddStubIntersectionVarToStubPropertyRectorTest.php @@ -2,13 +2,13 @@ declare(strict_types=1); -namespace Rector\PHPUnit\Tests\PHPUnit120\Rector\Property\BareVarToStubIntersectionRector; +namespace Rector\PHPUnit\Tests\CodeQuality\Rector\Class_\AddStubIntersectionVarToStubPropertyRector; use Iterator; use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -final class BareVarToStubIntersectionRectorTest extends AbstractRectorTestCase +final class AddStubIntersectionVarToStubPropertyRectorTest extends AbstractRectorTestCase { #[DataProvider('provideData')] public function test(string $filePath): void diff --git a/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/Fixture/bare_var_without_setup.php.inc b/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/Fixture/bare_var_without_setup.php.inc new file mode 100644 index 000000000..f3556375d --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/Fixture/bare_var_without_setup.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/Fixture/fixture.php.inc new file mode 100644 index 000000000..a1e8791a0 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/Fixture/fixture.php.inc @@ -0,0 +1,38 @@ +someServiceStub = $this->createStub(\stdClass::class); + } +} + +?> +----- +someServiceStub = $this->createStub(\stdClass::class); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/Fixture/overwrite_existing_var.php.inc b/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/Fixture/overwrite_existing_var.php.inc new file mode 100644 index 000000000..30daa5515 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/Fixture/overwrite_existing_var.php.inc @@ -0,0 +1,41 @@ +someServiceStub = $this->createStub(\stdClass::class); + } +} + +?> +----- +someServiceStub = $this->createStub(\stdClass::class); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/Fixture/skip_existing_intersection_var.php.inc b/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/Fixture/skip_existing_intersection_var.php.inc new file mode 100644 index 000000000..257c56970 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/Fixture/skip_existing_intersection_var.php.inc @@ -0,0 +1,18 @@ +someServiceStub = $this->createStub(\stdClass::class); + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/Fixture/skip_mock_object_property.php.inc b/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/Fixture/skip_mock_object_property.php.inc new file mode 100644 index 000000000..a7015200e --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/Fixture/skip_mock_object_property.php.inc @@ -0,0 +1,15 @@ +someServiceMock = $this->createStub(\stdClass::class); + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/Fixture/skip_non_test_class.php.inc b/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/Fixture/skip_non_test_class.php.inc new file mode 100644 index 000000000..067e18adb --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/Fixture/skip_non_test_class.php.inc @@ -0,0 +1,13 @@ +someServiceStub = $this->createStub(\stdClass::class); + } +} diff --git a/rules-tests/PHPUnit120/Rector/Property/BareVarToStubIntersectionRector/Fixture/skip_stub_short_name.php.inc b/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/Fixture/skip_stub_short_name_var.php.inc similarity index 52% rename from rules-tests/PHPUnit120/Rector/Property/BareVarToStubIntersectionRector/Fixture/skip_stub_short_name.php.inc rename to rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/Fixture/skip_stub_short_name_var.php.inc index cc1a6fdef..0447056a0 100644 --- a/rules-tests/PHPUnit120/Rector/Property/BareVarToStubIntersectionRector/Fixture/skip_stub_short_name.php.inc +++ b/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/Fixture/skip_stub_short_name_var.php.inc @@ -1,11 +1,11 @@ someServiceStub = self::createStub(\stdClass::class); + } +} + +?> +----- +someServiceStub = self::createStub(\stdClass::class); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/config/configured_rule.php new file mode 100644 index 000000000..e74fe74d7 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([AddStubIntersectionVarToStubPropertyRector::class]); diff --git a/rules-tests/PHPUnit120/Rector/Property/BareVarToStubIntersectionRector/Fixture/bare_var.php.inc b/rules-tests/PHPUnit120/Rector/Property/BareVarToStubIntersectionRector/Fixture/bare_var.php.inc deleted file mode 100644 index c1b96e07f..000000000 --- a/rules-tests/PHPUnit120/Rector/Property/BareVarToStubIntersectionRector/Fixture/bare_var.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/PHPUnit120/Rector/Property/BareVarToStubIntersectionRector/Fixture/skip_already_intersection.php.inc b/rules-tests/PHPUnit120/Rector/Property/BareVarToStubIntersectionRector/Fixture/skip_already_intersection.php.inc deleted file mode 100644 index b8205c437..000000000 --- a/rules-tests/PHPUnit120/Rector/Property/BareVarToStubIntersectionRector/Fixture/skip_already_intersection.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -withRules(rules: [BareVarToStubIntersectionRector::class]); diff --git a/rules/CodeQuality/NodeAnalyser/MockObjectPropertyDetector.php b/rules/CodeQuality/NodeAnalyser/MockObjectPropertyDetector.php index 232bfbe76..46b0d567d 100644 --- a/rules/CodeQuality/NodeAnalyser/MockObjectPropertyDetector.php +++ b/rules/CodeQuality/NodeAnalyser/MockObjectPropertyDetector.php @@ -7,6 +7,7 @@ use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\PropertyFetch; +use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Expression; @@ -21,19 +22,19 @@ public function __construct( ) { } - public function detect(Property $property): bool + public function detect(Property $property, string $className = PHPUnitClassName::MOCK_OBJECT): bool { if (! $property->type instanceof FullyQualified) { return false; } - return $property->type->toString() === PHPUnitClassName::MOCK_OBJECT; + return $property->type->toString() === $className; } /** - * @return array + * @return array */ - public function collectFromClassMethod(ClassMethod $classMethod): array + public function collectFromClassMethod(ClassMethod $classMethod, string $methodName = 'createMock'): array { $propertyNamesToCreateMockMethodCalls = []; @@ -52,12 +53,13 @@ public function collectFromClassMethod(ClassMethod $classMethod): array continue; } - if (! $assign->expr instanceof MethodCall) { + // both $this->createMock() and self::createMock() + if (! $assign->expr instanceof MethodCall && ! $assign->expr instanceof StaticCall) { continue; } - $methodCall = $assign->expr; - if (! $this->nodeNameResolver->isName($methodCall->name, 'createMock')) { + $createCall = $assign->expr; + if (! $this->nodeNameResolver->isName($createCall->name, $methodName)) { continue; } @@ -68,7 +70,7 @@ public function collectFromClassMethod(ClassMethod $classMethod): array continue; } - $propertyNamesToCreateMockMethodCalls[$propertyName] = $methodCall; + $propertyNamesToCreateMockMethodCalls[$propertyName] = $createCall; } return $propertyNamesToCreateMockMethodCalls; diff --git a/rules/CodeQuality/Rector/Class_/AddIntersectionVarToMockObjectPropertyRector.php b/rules/CodeQuality/Rector/Class_/AddIntersectionVarToMockObjectPropertyRector.php index bc7864a0b..e67230bcf 100644 --- a/rules/CodeQuality/Rector/Class_/AddIntersectionVarToMockObjectPropertyRector.php +++ b/rules/CodeQuality/Rector/Class_/AddIntersectionVarToMockObjectPropertyRector.php @@ -7,10 +7,13 @@ use PhpParser\Node; use PhpParser\Node\Expr\ClassConstFetch; use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Property; +use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; +use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger; use Rector\BetterPhpDocParser\ValueObject\Type\BracketsAwareIntersectionTypeNode; @@ -85,6 +88,13 @@ public function refactor(Node $node): ?Class_ ]); $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); + + // already has an intersection @var, skip + $varTagValueNode = $phpDocInfo->getVarTagValueNode(); + if ($varTagValueNode instanceof VarTagValueNode && $varTagValueNode->type instanceof IntersectionTypeNode) { + continue; + } + $this->phpDocTypeChanger->changeVarTypeNode($property, $phpDocInfo, $intersectionTypeNode); $hasChanged = true; @@ -138,9 +148,9 @@ protected function setUp(): void ); } - private function resolveMockedClass(MethodCall $methodCall): ?string + private function resolveMockedClass(MethodCall|StaticCall $createMockCall): ?string { - $firstArg = $methodCall->getArgs()[0] ?? null; + $firstArg = $createMockCall->getArgs()[0] ?? null; if ($firstArg === null) { return null; } diff --git a/rules/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector.php b/rules/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector.php new file mode 100644 index 000000000..33e3b4121 --- /dev/null +++ b/rules/CodeQuality/Rector/Class_/AddStubIntersectionVarToStubPropertyRector.php @@ -0,0 +1,226 @@ +testsNodeAnalyzer->isInTestClass($node)) { + return null; + } + + $setUpClassMethod = $node->getMethod(MethodName::SET_UP); + $propertyNamesToCreateStubCalls = $setUpClassMethod instanceof ClassMethod + ? $this->mockObjectPropertyDetector->collectFromClassMethod($setUpClassMethod, 'createStub') + : []; + + $hasChanged = false; + + foreach ($node->getProperties() as $property) { + // only properties typed as a native Stub + if (! $this->mockObjectPropertyDetector->detect($property, PHPUnitClassName::STUB)) { + continue; + } + + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); + $varTagValueNode = $phpDocInfo->getVarTagValueNode(); + + // already has an intersection @var, skip + if ($varTagValueNode instanceof VarTagValueNode && $varTagValueNode->type instanceof IntersectionTypeNode) { + continue; + } + + $intersectionTypeNode = $this->resolveStubIntersection( + $property, + $propertyNamesToCreateStubCalls, + $varTagValueNode + ); + if (! $intersectionTypeNode instanceof BracketsAwareIntersectionTypeNode) { + continue; + } + + $this->phpDocTypeChanger->changeVarTypeNode($property, $phpDocInfo, $intersectionTypeNode); + + $hasChanged = true; + } + + if (! $hasChanged) { + return null; + } + + return $node; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + 'Add a Stub intersection @var docblock with the stubbed class to a native Stub property', + [ + new CodeSample( + <<<'CODE_SAMPLE' +use PHPUnit\Framework\TestCase; + +final class SomeTest extends TestCase +{ + private \PHPUnit\Framework\MockObject\Stub $someServiceStub; + + protected function setUp(): void + { + $this->someServiceStub = $this->createStub(SomeService::class); + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +use PHPUnit\Framework\TestCase; + +final class SomeTest extends TestCase +{ + /** + * @var \PHPUnit\Framework\MockObject\Stub&\SomeService + */ + private \PHPUnit\Framework\MockObject\Stub $someServiceStub; + + protected function setUp(): void + { + $this->someServiceStub = $this->createStub(SomeService::class); + } +} +CODE_SAMPLE + ), + new CodeSample( + <<<'CODE_SAMPLE' +use PHPUnit\Framework\TestCase; + +final class SomeTest extends TestCase +{ + /** + * @var SomeService + */ + private \PHPUnit\Framework\MockObject\Stub $someServiceStub; +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +use PHPUnit\Framework\TestCase; + +final class SomeTest extends TestCase +{ + /** + * @var \PHPUnit\Framework\MockObject\Stub&SomeService + */ + private \PHPUnit\Framework\MockObject\Stub $someServiceStub; +} +CODE_SAMPLE + ), + ] + ); + } + + /** + * @param array $propertyNamesToCreateStubCalls + */ + private function resolveStubIntersection( + Property $property, + array $propertyNamesToCreateStubCalls, + ?VarTagValueNode $varTagValueNode + ): ?BracketsAwareIntersectionTypeNode { + $propertyName = $property->props[0]->name->toString(); + + // 1. prefer the stubbed class from a setUp() createStub() call + $createStubCall = $propertyNamesToCreateStubCalls[$propertyName] ?? null; + if ($createStubCall !== null) { + $stubbedClass = $this->resolveStubbedClass($createStubCall); + if ($stubbedClass !== null) { + return new BracketsAwareIntersectionTypeNode([ + new IdentifierTypeNode('\\' . PHPUnitClassName::STUB), + new IdentifierTypeNode('\\' . $stubbedClass), + ]); + } + } + + // 2. fall back to a bare single-class @var docblock + if ($varTagValueNode instanceof VarTagValueNode && $varTagValueNode->type instanceof IdentifierTypeNode) { + // skip Stub/MockObject themselves, only real stubbed class types + if (in_array($this->resolveShortName($varTagValueNode->type->name), ['Stub', 'MockObject'], true)) { + return null; + } + + return new BracketsAwareIntersectionTypeNode([ + new IdentifierTypeNode('\\' . PHPUnitClassName::STUB), + $varTagValueNode->type, + ]); + } + + return null; + } + + private function resolveShortName(string $name): string + { + $lastBackslashPosition = strrpos($name, '\\'); + + return $lastBackslashPosition === false ? $name : substr($name, $lastBackslashPosition + 1); + } + + private function resolveStubbedClass(MethodCall|StaticCall $createStubCall): ?string + { + $firstArg = $createStubCall->getArgs()[0] ?? null; + if ($firstArg === null) { + return null; + } + + if (! $firstArg->value instanceof ClassConstFetch) { + return null; + } + + $className = $this->getName($firstArg->value->class); + if (! is_string($className)) { + return null; + } + + return $className; + } +} diff --git a/rules/CodeQuality/Rector/Class_/RemoveNeverUsedMockPropertyRector.php b/rules/CodeQuality/Rector/Class_/RemoveNeverUsedMockPropertyRector.php index a54b69dc5..42b3bc7c7 100644 --- a/rules/CodeQuality/Rector/Class_/RemoveNeverUsedMockPropertyRector.php +++ b/rules/CodeQuality/Rector/Class_/RemoveNeverUsedMockPropertyRector.php @@ -8,6 +8,7 @@ use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\PropertyFetch; +use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Expression; @@ -193,7 +194,7 @@ private function removePropertyFromClass(Class_ $class, string $propertyNameToRe } /** - * @param array $propertyNamesToCreateMockMethodCalls + * @param array $propertyNamesToCreateMockMethodCalls * @return string[] */ private function resolvePropertyNamesToRemove(array $propertyNamesToCreateMockMethodCalls, Class_ $class): array diff --git a/rules/PHPUnit120/Rector/Property/BareVarToStubIntersectionRector.php b/rules/PHPUnit120/Rector/Property/BareVarToStubIntersectionRector.php deleted file mode 100644 index 081df04fa..000000000 --- a/rules/PHPUnit120/Rector/Property/BareVarToStubIntersectionRector.php +++ /dev/null @@ -1,145 +0,0 @@ -testsNodeAnalyzer->isInTestClass($node)) { - return null; - } - - // only properties already converted to a Stub native type - if (! $this->isStubNativeType($node->type)) { - return null; - } - - $phpDocInfo = $this->phpDocInfoFactory->createFromNode($node); - if (! $phpDocInfo instanceof PhpDocInfo) { - return null; - } - - $varTagValueNode = $phpDocInfo->getVarTagValueNode(); - if (! $varTagValueNode instanceof VarTagValueNode) { - return null; - } - - if (! $this->addStubIntersection($varTagValueNode)) { - return null; - } - - $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); - - return $node; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition( - 'Add a &Stub intersection to a bare single-class @var docblock of a property changed to a Stub native type', - [ - new CodeSample( - <<<'CODE_SAMPLE' -/** - * @var FormBuilderInterface - */ -private \PHPUnit\Framework\MockObject\Stub $formBuilder; -CODE_SAMPLE - , - <<<'CODE_SAMPLE' -/** - * @var FormBuilderInterface&\PHPUnit\Framework\MockObject\Stub - */ -private \PHPUnit\Framework\MockObject\Stub $formBuilder; -CODE_SAMPLE - ), - ] - ); - } - - private function isStubNativeType(?Node $typeNode): bool - { - if (! $typeNode instanceof Node) { - return false; - } - - if ($typeNode instanceof IntersectionType) { - return array_any($typeNode->types, fn (Identifier|Name $innerType): bool => $this->isStubName($innerType)); - } - - return $this->isStubName($typeNode); - } - - private function isStubName(?Node $node): bool - { - return $node instanceof Node && $this->getName($node) === PHPUnitClassName::STUB; - } - - private function addStubIntersection(VarTagValueNode $varTagValueNode): bool - { - $typeNode = $varTagValueNode->type; - - // only a single bare class type, not already a union/intersection - if (! $typeNode instanceof IdentifierTypeNode) { - return false; - } - - // skip Stub/MockObject themselves, only mocked class types - if (in_array($this->resolveShortName($typeNode->name), ['Stub', 'MockObject'], true)) { - return false; - } - - $varTagValueNode->type = new BracketsAwareIntersectionTypeNode([ - $typeNode, - new IdentifierTypeNode('\\' . PHPUnitClassName::STUB), - ]); - - return true; - } - - private function resolveShortName(string $name): string - { - $lastBackslashPosition = strrpos($name, '\\'); - - return $lastBackslashPosition === false ? $name : substr($name, $lastBackslashPosition + 1); - } -}