From f338082c6fb2e52454758a0349f9245d1bcb30ef Mon Sep 17 00:00:00 2001 From: mikolaj Date: Fri, 30 Jan 2026 15:55:22 +0100 Subject: [PATCH 1/7] Fixed PHPStan rule error identifiers --- rules/RequireClosureReturnTypeRule.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rules/RequireClosureReturnTypeRule.php b/rules/RequireClosureReturnTypeRule.php index 5a9975e..6f54f17 100644 --- a/rules/RequireClosureReturnTypeRule.php +++ b/rules/RequireClosureReturnTypeRule.php @@ -23,6 +23,9 @@ public function getNodeType(): string return Node\Expr::class; } + /** + * @return list<\PHPStan\Rules\IdentifierRuleError> + */ public function processNode(Node $node, Scope $scope): array { if (!$node instanceof Node\Expr\Closure && !$node instanceof Node\Expr\ArrowFunction) { @@ -35,7 +38,7 @@ public function processNode(Node $node, Scope $scope): array return [ RuleErrorBuilder::message( sprintf('%s is missing a return type declaration', $nodeType) - )->build(), + )->identifier('phpstan.requireClosureReturnType')->build(), ]; } From e492dc28d8dbf4368acf7ceb18ea347c1777af51 Mon Sep 17 00:00:00 2001 From: mikolaj Date: Fri, 30 Jan 2026 16:28:25 +0100 Subject: [PATCH 2/7] Updated identifier for closure return type rule error message --- rules/RequireClosureReturnTypeRule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/RequireClosureReturnTypeRule.php b/rules/RequireClosureReturnTypeRule.php index 6f54f17..ace2ca5 100644 --- a/rules/RequireClosureReturnTypeRule.php +++ b/rules/RequireClosureReturnTypeRule.php @@ -38,7 +38,7 @@ public function processNode(Node $node, Scope $scope): array return [ RuleErrorBuilder::message( sprintf('%s is missing a return type declaration', $nodeType) - )->identifier('phpstan.requireClosureReturnType')->build(), + )->identifier('Ibexa.requireClosureReturnType')->build(), ]; } From 332ae1aa4330104607fca2d44e64b285858337cf Mon Sep 17 00:00:00 2001 From: mikolaj Date: Fri, 30 Jan 2026 16:21:02 +0100 Subject: [PATCH 3/7] Added optional MockObject rules and fixtures to phpstan --- composer.json | 3 +- extension-mocks.neon | 3 + phpstan.neon | 2 + rules/RequireMockObjectInPropertyTypeRule.php | 92 +++++++++++ rules/RequireMockObjectInReturnTypeRule.php | 143 ++++++++++++++++++ ...equireMockObjectInPropertyTypeRuleTest.php | 37 +++++ .../RequireMockObjectInReturnTypeRuleTest.php | 37 +++++ .../data/require-mockobject-property.php | 25 +++ .../rules/data/require-mockobject-return.php | 34 +++++ 9 files changed, 375 insertions(+), 1 deletion(-) create mode 100644 extension-mocks.neon create mode 100644 rules/RequireMockObjectInPropertyTypeRule.php create mode 100644 rules/RequireMockObjectInReturnTypeRule.php create mode 100644 tests/rules/RequireMockObjectInPropertyTypeRuleTest.php create mode 100644 tests/rules/RequireMockObjectInReturnTypeRuleTest.php create mode 100644 tests/rules/data/require-mockobject-property.php create mode 100644 tests/rules/data/require-mockobject-return.php diff --git a/composer.json b/composer.json index 10ebf34..0ad6043 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,8 @@ }, "autoload-dev": { "psr-4": { - "Ibexa\\Tests\\PHPStan\\Rules\\": "tests/rules/" + "Ibexa\\Tests\\PHPStan\\Rules\\": "tests/rules/", + "Ibexa\\Tests\\PHPStan\\Rules\\Data\\": "tests/rules/data/" } }, "scripts": { diff --git a/extension-mocks.neon b/extension-mocks.neon new file mode 100644 index 0000000..cc76970 --- /dev/null +++ b/extension-mocks.neon @@ -0,0 +1,3 @@ +rules: + - Ibexa\PHPStan\Rules\RequireMockObjectInPropertyTypeRule + - Ibexa\PHPStan\Rules\RequireMockObjectInReturnTypeRule diff --git a/phpstan.neon b/phpstan.neon index ab3d432..cdd269d 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -3,4 +3,6 @@ parameters: paths: - rules - tests + excludePaths: + - tests/rules/data/* checkMissingCallableSignature: true diff --git a/rules/RequireMockObjectInPropertyTypeRule.php b/rules/RequireMockObjectInPropertyTypeRule.php new file mode 100644 index 0000000..1c0af61 --- /dev/null +++ b/rules/RequireMockObjectInPropertyTypeRule.php @@ -0,0 +1,92 @@ + + */ +final class RequireMockObjectInPropertyTypeRule implements Rule +{ + public function getNodeType(): string + { + return Property::class; + } + + /** + * @return list<\PHPStan\Rules\IdentifierRuleError> + */ + public function processNode(Node $node, Scope $scope): array + { + if ($node->type === null) { + return []; + } + + if (!$this->docCommentIncludesMockObject($node)) { + return []; + } + + if ($this->typeNodeIncludesMockObject($node->type)) { + return []; + } + + return [ + RuleErrorBuilder::message('Property typed as MockObject only in PHPDoc. Use intersection type with MockObject.') + ->identifier('tests.mockObject.propertyType') + ->build(), + ]; + } + + private function typeNodeIncludesMockObject(Node $type): bool + { + if ($type instanceof NullableType) { + return $this->typeNodeIncludesMockObject($type->type); + } + + if ($type instanceof UnionType || $type instanceof IntersectionType) { + foreach ($type->types as $innerType) { + if ($this->typeNodeIncludesMockObject($innerType)) { + return true; + } + } + + return false; + } + + if ($type instanceof Identifier) { + return $type->toString() === 'MockObject'; + } + + if ($type instanceof Name) { + return $type->getLast() === 'MockObject'; + } + + return false; + } + + private function docCommentIncludesMockObject(Property $property): bool + { + $docComment = $property->getDocComment(); + if ($docComment === null) { + return false; + } + + return preg_match('/@var\\s+[^\\n]*MockObject/', $docComment->getText()) === 1; + } +} diff --git a/rules/RequireMockObjectInReturnTypeRule.php b/rules/RequireMockObjectInReturnTypeRule.php new file mode 100644 index 0000000..128f0e2 --- /dev/null +++ b/rules/RequireMockObjectInReturnTypeRule.php @@ -0,0 +1,143 @@ + + */ +final class RequireMockObjectInReturnTypeRule implements Rule +{ + public function getNodeType(): string + { + return ClassMethod::class; + } + + /** + * @return list<\PHPStan\Rules\IdentifierRuleError> + */ + public function processNode(Node $node, Scope $scope): array + { + if ($node->returnType === null || $node->stmts === null) { + return []; + } + + if (!$this->returnsMock($node)) { + return []; + } + + if ($this->typeNodeIncludesMockObject($node->returnType)) { + return []; + } + + return [ + RuleErrorBuilder::message('Method returns a mock but return type is missing MockObject intersection.') + ->identifier('tests.mockObject.returnType') + ->build(), + ]; + } + + private function returnsMock(ClassMethod $node): bool + { + $mockVariables = []; + foreach ($node->getStmts() ?? [] as $stmt) { + if ($stmt instanceof Node\Stmt\Expression && $stmt->expr instanceof Node\Expr\Assign) { + $assign = $stmt->expr; + if ($assign->var instanceof Variable && is_string($assign->var->name)) { + if ($assign->expr instanceof MethodCall && $this->isCreateMockCall($assign->expr)) { + $mockVariables[$assign->var->name] = true; + } + + if ($assign->expr instanceof StaticCall && $this->isCreateMockCall($assign->expr)) { + $mockVariables[$assign->var->name] = true; + } + } + } + + if (!$stmt instanceof Node\Stmt\Return_ || $stmt->expr === null) { + continue; + } + + $expr = $stmt->expr; + if ($expr instanceof MethodCall && $this->isCreateMockCall($expr)) { + return true; + } + + if ($expr instanceof StaticCall && $this->isCreateMockCall($expr)) { + return true; + } + + if ($expr instanceof Variable && is_string($expr->name) && isset($mockVariables[$expr->name])) { + return true; + } + } + + return false; + } + + /** + * @param \PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Expr\StaticCall $call + */ + private function isCreateMockCall(Node $call): bool + { + if (!$call->name instanceof Node\Identifier) { + return false; + } + + if ($call->name->toString() !== 'createMock') { + return false; + } + + if ($call instanceof MethodCall) { + return $call->var instanceof Variable && $call->var->name === 'this'; + } + + return true; + } + + private function typeNodeIncludesMockObject(Node $type): bool + { + if ($type instanceof NullableType) { + return $this->typeNodeIncludesMockObject($type->type); + } + + if ($type instanceof UnionType || $type instanceof IntersectionType) { + foreach ($type->types as $innerType) { + if ($this->typeNodeIncludesMockObject($innerType)) { + return true; + } + } + + return false; + } + + if ($type instanceof Identifier) { + return $type->toString() === 'MockObject'; + } + + if ($type instanceof Name) { + return $type->getLast() === 'MockObject'; + } + + return false; + } +} diff --git a/tests/rules/RequireMockObjectInPropertyTypeRuleTest.php b/tests/rules/RequireMockObjectInPropertyTypeRuleTest.php new file mode 100644 index 0000000..3d540df --- /dev/null +++ b/tests/rules/RequireMockObjectInPropertyTypeRuleTest.php @@ -0,0 +1,37 @@ + + */ +final class RequireMockObjectInPropertyTypeRuleTest extends RuleTestCase +{ + protected function getRule(): Rule + { + return new RequireMockObjectInPropertyTypeRule(); + } + + public function testRule(): void + { + $this->analyse( + [__DIR__ . '/data/require-mockobject-property.php'], + [ + [ + 'Property typed as MockObject only in PHPDoc. Use intersection type with MockObject.', + 16, + ], + ] + ); + } +} diff --git a/tests/rules/RequireMockObjectInReturnTypeRuleTest.php b/tests/rules/RequireMockObjectInReturnTypeRuleTest.php new file mode 100644 index 0000000..c4b3bda --- /dev/null +++ b/tests/rules/RequireMockObjectInReturnTypeRuleTest.php @@ -0,0 +1,37 @@ + + */ +final class RequireMockObjectInReturnTypeRuleTest extends RuleTestCase +{ + protected function getRule(): Rule + { + return new RequireMockObjectInReturnTypeRule(); + } + + public function testRule(): void + { + $this->analyse( + [__DIR__ . '/data/require-mockobject-return.php'], + [ + [ + 'Method returns a mock but return type is missing MockObject intersection.', + 15, + ], + ] + ); + } +} diff --git a/tests/rules/data/require-mockobject-property.php b/tests/rules/data/require-mockobject-property.php new file mode 100644 index 0000000..01e056d --- /dev/null +++ b/tests/rules/data/require-mockobject-property.php @@ -0,0 +1,25 @@ +createMock(Foo::class); + + return $foo; + } + + private function createFooOk(): Foo&MockObject + { + return $this->createMock(Foo::class); + } +} + +final class Foo +{ +} + +interface MockObject +{ +} From 13062958f3c64f07d7a5e716c8d484bbd12e6d69 Mon Sep 17 00:00:00 2001 From: mikolaj Date: Tue, 10 Feb 2026 09:46:11 +0100 Subject: [PATCH 4/7] Added opt-in MockObject rules and fixtures --- composer.json | 3 +-- phpstan.neon | 2 +- rules/RequireMockObjectInPropertyTypeRule.php | 4 ++-- rules/RequireMockObjectInReturnTypeRule.php | 4 ++-- .../RequireMockObjectInPropertyTypeFixture.php} | 2 +- .../RequireMockObjectInReturnTypeFixture.php} | 2 +- tests/rules/RequireMockObjectInPropertyTypeRuleTest.php | 2 +- tests/rules/RequireMockObjectInReturnTypeRuleTest.php | 2 +- 8 files changed, 10 insertions(+), 11 deletions(-) rename tests/rules/{data/require-mockobject-property.php => Fixtures/RequireMockObjectInPropertyTypeFixture.php} (89%) rename tests/rules/{data/require-mockobject-return.php => Fixtures/RequireMockObjectInReturnTypeFixture.php} (92%) diff --git a/composer.json b/composer.json index 0ad6043..10ebf34 100644 --- a/composer.json +++ b/composer.json @@ -25,8 +25,7 @@ }, "autoload-dev": { "psr-4": { - "Ibexa\\Tests\\PHPStan\\Rules\\": "tests/rules/", - "Ibexa\\Tests\\PHPStan\\Rules\\Data\\": "tests/rules/data/" + "Ibexa\\Tests\\PHPStan\\Rules\\": "tests/rules/" } }, "scripts": { diff --git a/phpstan.neon b/phpstan.neon index cdd269d..c6a905c 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -4,5 +4,5 @@ parameters: - rules - tests excludePaths: - - tests/rules/data/* + - tests/rules/Fixtures/* checkMissingCallableSignature: true diff --git a/rules/RequireMockObjectInPropertyTypeRule.php b/rules/RequireMockObjectInPropertyTypeRule.php index 1c0af61..15d7fd9 100644 --- a/rules/RequireMockObjectInPropertyTypeRule.php +++ b/rules/RequireMockObjectInPropertyTypeRule.php @@ -22,7 +22,7 @@ /** * @implements Rule */ -final class RequireMockObjectInPropertyTypeRule implements Rule +final readonly class RequireMockObjectInPropertyTypeRule implements Rule { public function getNodeType(): string { @@ -48,7 +48,7 @@ public function processNode(Node $node, Scope $scope): array return [ RuleErrorBuilder::message('Property typed as MockObject only in PHPDoc. Use intersection type with MockObject.') - ->identifier('tests.mockObject.propertyType') + ->identifier('Ibexa.requireMockObjectPropertyType') ->build(), ]; } diff --git a/rules/RequireMockObjectInReturnTypeRule.php b/rules/RequireMockObjectInReturnTypeRule.php index 128f0e2..56d3289 100644 --- a/rules/RequireMockObjectInReturnTypeRule.php +++ b/rules/RequireMockObjectInReturnTypeRule.php @@ -25,7 +25,7 @@ /** * @implements Rule */ -final class RequireMockObjectInReturnTypeRule implements Rule +final readonly class RequireMockObjectInReturnTypeRule implements Rule { public function getNodeType(): string { @@ -51,7 +51,7 @@ public function processNode(Node $node, Scope $scope): array return [ RuleErrorBuilder::message('Method returns a mock but return type is missing MockObject intersection.') - ->identifier('tests.mockObject.returnType') + ->identifier('Ibexa.requireMockObjectReturnType') ->build(), ]; } diff --git a/tests/rules/data/require-mockobject-property.php b/tests/rules/Fixtures/RequireMockObjectInPropertyTypeFixture.php similarity index 89% rename from tests/rules/data/require-mockobject-property.php rename to tests/rules/Fixtures/RequireMockObjectInPropertyTypeFixture.php index 01e056d..3708f8b 100644 --- a/tests/rules/data/require-mockobject-property.php +++ b/tests/rules/Fixtures/RequireMockObjectInPropertyTypeFixture.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace Ibexa\Tests\PHPStan\Rules\Data; +namespace Ibexa\Tests\PHPStan\Rules\Fixtures; use PHPUnit\Framework\TestCase; diff --git a/tests/rules/data/require-mockobject-return.php b/tests/rules/Fixtures/RequireMockObjectInReturnTypeFixture.php similarity index 92% rename from tests/rules/data/require-mockobject-return.php rename to tests/rules/Fixtures/RequireMockObjectInReturnTypeFixture.php index 7e2a312..456ba35 100644 --- a/tests/rules/data/require-mockobject-return.php +++ b/tests/rules/Fixtures/RequireMockObjectInReturnTypeFixture.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace Ibexa\Tests\PHPStan\Rules\Data; +namespace Ibexa\Tests\PHPStan\Rules\Fixtures; use PHPUnit\Framework\TestCase; diff --git a/tests/rules/RequireMockObjectInPropertyTypeRuleTest.php b/tests/rules/RequireMockObjectInPropertyTypeRuleTest.php index 3d540df..55e97cb 100644 --- a/tests/rules/RequireMockObjectInPropertyTypeRuleTest.php +++ b/tests/rules/RequireMockObjectInPropertyTypeRuleTest.php @@ -25,7 +25,7 @@ protected function getRule(): Rule public function testRule(): void { $this->analyse( - [__DIR__ . '/data/require-mockobject-property.php'], + [__DIR__ . '/Fixtures/RequireMockObjectInPropertyTypeFixture.php'], [ [ 'Property typed as MockObject only in PHPDoc. Use intersection type with MockObject.', diff --git a/tests/rules/RequireMockObjectInReturnTypeRuleTest.php b/tests/rules/RequireMockObjectInReturnTypeRuleTest.php index c4b3bda..b416c70 100644 --- a/tests/rules/RequireMockObjectInReturnTypeRuleTest.php +++ b/tests/rules/RequireMockObjectInReturnTypeRuleTest.php @@ -25,7 +25,7 @@ protected function getRule(): Rule public function testRule(): void { $this->analyse( - [__DIR__ . '/data/require-mockobject-return.php'], + [__DIR__ . '/Fixtures/RequireMockObjectInReturnTypeFixture.php'], [ [ 'Method returns a mock but return type is missing MockObject intersection.', From 85258a84308dbb02325562da2f0ab7545e4ec72a Mon Sep 17 00:00:00 2001 From: mikolaj Date: Tue, 10 Feb 2026 09:49:35 +0100 Subject: [PATCH 5/7] Normalized PHPStan rule identifiers --- rules/NoConfigResolverParametersInConstructorRule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/NoConfigResolverParametersInConstructorRule.php b/rules/NoConfigResolverParametersInConstructorRule.php index 6bd3213..94fa6d8 100644 --- a/rules/NoConfigResolverParametersInConstructorRule.php +++ b/rules/NoConfigResolverParametersInConstructorRule.php @@ -60,7 +60,7 @@ public function processNode(Node $node, Scope $scope): array return [ RuleErrorBuilder ::message('Referring to ConfigResolver parameters in constructor is not allowed due to potential scope change.') - ->identifier('Ibexa.NoConfigResolverParametersInConstructor') + ->identifier('Ibexa.noConfigResolverParametersInConstructor') ->nonIgnorable() ->build(), ]; From 42c5f020f32e56182076d7e832fe51e96e790e78 Mon Sep 17 00:00:00 2001 From: mikolaj Date: Tue, 10 Feb 2026 11:53:56 +0100 Subject: [PATCH 6/7] Adjusted mock-return rule to require concrete type --- extension-mocks.neon | 2 +- ... RequireConcreteTypeForMockReturnRule.php} | 47 +++++++++++++++---- ...quireConcreteTypeForMockReturnFixture.php} | 7 ++- ...uireConcreteTypeForMockReturnRuleTest.php} | 14 +++--- 4 files changed, 51 insertions(+), 19 deletions(-) rename rules/{RequireMockObjectInReturnTypeRule.php => RequireConcreteTypeForMockReturnRule.php} (71%) rename tests/rules/Fixtures/{RequireMockObjectInReturnTypeFixture.php => RequireConcreteTypeForMockReturnFixture.php} (76%) rename tests/rules/{RequireMockObjectInReturnTypeRuleTest.php => RequireConcreteTypeForMockReturnRuleTest.php} (51%) diff --git a/extension-mocks.neon b/extension-mocks.neon index cc76970..a172105 100644 --- a/extension-mocks.neon +++ b/extension-mocks.neon @@ -1,3 +1,3 @@ rules: - Ibexa\PHPStan\Rules\RequireMockObjectInPropertyTypeRule - - Ibexa\PHPStan\Rules\RequireMockObjectInReturnTypeRule + - Ibexa\PHPStan\Rules\RequireConcreteTypeForMockReturnRule diff --git a/rules/RequireMockObjectInReturnTypeRule.php b/rules/RequireConcreteTypeForMockReturnRule.php similarity index 71% rename from rules/RequireMockObjectInReturnTypeRule.php rename to rules/RequireConcreteTypeForMockReturnRule.php index 56d3289..30332a0 100644 --- a/rules/RequireMockObjectInReturnTypeRule.php +++ b/rules/RequireConcreteTypeForMockReturnRule.php @@ -25,7 +25,7 @@ /** * @implements Rule */ -final readonly class RequireMockObjectInReturnTypeRule implements Rule +final readonly class RequireConcreteTypeForMockReturnRule implements Rule { public function getNodeType(): string { @@ -45,13 +45,13 @@ public function processNode(Node $node, Scope $scope): array return []; } - if ($this->typeNodeIncludesMockObject($node->returnType)) { + if (!$this->typeNodeIsMockObjectOnly($node->returnType)) { return []; } return [ - RuleErrorBuilder::message('Method returns a mock but return type is missing MockObject intersection.') - ->identifier('Ibexa.requireMockObjectReturnType') + RuleErrorBuilder::message('Method returns a mock and declares only MockObject as return type. Use an intersection with a concrete type.') + ->identifier('Ibexa.requireConcreteTypeForMockReturn') ->build(), ]; } @@ -114,22 +114,49 @@ private function isCreateMockCall(Node $call): bool return true; } - private function typeNodeIncludesMockObject(Node $type): bool + private function typeNodeIsMockObjectOnly(Node $type): bool { if ($type instanceof NullableType) { - return $this->typeNodeIncludesMockObject($type->type); + return $this->typeNodeIsMockObjectOnly($type->type); } - if ($type instanceof UnionType || $type instanceof IntersectionType) { + if ($type instanceof IntersectionType) { + $hasMockObject = false; foreach ($type->types as $innerType) { - if ($this->typeNodeIncludesMockObject($innerType)) { - return true; + if ($this->isMockObjectType($innerType)) { + $hasMockObject = true; + continue; } + + return false; } - return false; + return $hasMockObject; + } + + if ($type instanceof UnionType) { + $hasMockObject = false; + foreach ($type->types as $innerType) { + if ($innerType instanceof Name && $innerType->getLast() === 'null') { + continue; + } + + if ($this->isMockObjectType($innerType)) { + $hasMockObject = true; + continue; + } + + return false; + } + + return $hasMockObject; } + return $this->isMockObjectType($type); + } + + private function isMockObjectType(Node $type): bool + { if ($type instanceof Identifier) { return $type->toString() === 'MockObject'; } diff --git a/tests/rules/Fixtures/RequireMockObjectInReturnTypeFixture.php b/tests/rules/Fixtures/RequireConcreteTypeForMockReturnFixture.php similarity index 76% rename from tests/rules/Fixtures/RequireMockObjectInReturnTypeFixture.php rename to tests/rules/Fixtures/RequireConcreteTypeForMockReturnFixture.php index 456ba35..784402b 100644 --- a/tests/rules/Fixtures/RequireMockObjectInReturnTypeFixture.php +++ b/tests/rules/Fixtures/RequireConcreteTypeForMockReturnFixture.php @@ -10,7 +10,7 @@ use PHPUnit\Framework\TestCase; -final class ReturnMockTypeTest extends TestCase +final class ConcreteMockReturnTypeFixture extends TestCase { private function createFoo(): Foo { @@ -23,6 +23,11 @@ private function createFooOk(): Foo&MockObject { return $this->createMock(Foo::class); } + + private function createMockObjectOnly(): MockObject + { + return $this->createMock(Foo::class); + } } final class Foo diff --git a/tests/rules/RequireMockObjectInReturnTypeRuleTest.php b/tests/rules/RequireConcreteTypeForMockReturnRuleTest.php similarity index 51% rename from tests/rules/RequireMockObjectInReturnTypeRuleTest.php rename to tests/rules/RequireConcreteTypeForMockReturnRuleTest.php index b416c70..02855e9 100644 --- a/tests/rules/RequireMockObjectInReturnTypeRuleTest.php +++ b/tests/rules/RequireConcreteTypeForMockReturnRuleTest.php @@ -8,28 +8,28 @@ namespace Ibexa\Tests\PHPStan\Rules; -use Ibexa\PHPStan\Rules\RequireMockObjectInReturnTypeRule; +use Ibexa\PHPStan\Rules\RequireConcreteTypeForMockReturnRule; use PHPStan\Rules\Rule; use PHPStan\Testing\RuleTestCase; /** - * @extends RuleTestCase + * @extends RuleTestCase */ -final class RequireMockObjectInReturnTypeRuleTest extends RuleTestCase +final class RequireConcreteTypeForMockReturnRuleTest extends RuleTestCase { protected function getRule(): Rule { - return new RequireMockObjectInReturnTypeRule(); + return new RequireConcreteTypeForMockReturnRule(); } public function testRule(): void { $this->analyse( - [__DIR__ . '/Fixtures/RequireMockObjectInReturnTypeFixture.php'], + [__DIR__ . '/Fixtures/RequireConcreteTypeForMockReturnFixture.php'], [ [ - 'Method returns a mock but return type is missing MockObject intersection.', - 15, + 'Method returns a mock and declares only MockObject as return type. Use an intersection with a concrete type.', + 27, ], ] ); From c5c2920f464d50d4b5b330c4b21d0448977c9d38 Mon Sep 17 00:00:00 2001 From: mikolaj Date: Tue, 10 Feb 2026 12:11:46 +0100 Subject: [PATCH 7/7] Added .gitignore to keep local caches and vendor out of version control --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e57a11e --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/vendor/ +/.php-cs-fixer.cache +/.phpunit.result.cache +/composer.lock