Skip to content

Commit 51521f4

Browse files
Improve
1 parent fe51050 commit 51521f4

File tree

5 files changed

+95
-12
lines changed

5 files changed

+95
-12
lines changed

extension.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ services:
5151
class: PHPStan\Type\PHPUnit\MockForIntersectionDynamicReturnTypeExtension
5252
tags:
5353
- phpstan.broker.dynamicMethodReturnTypeExtension
54+
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
5455
-
5556
class: PHPStan\Rules\PHPUnit\CoversHelper
5657
-

src/Type/PHPUnit/MockForIntersectionDynamicReturnTypeExtension.php

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,23 @@
22

33
namespace PHPStan\Type\PHPUnit;
44

5+
use PhpParser\Node\Arg;
56
use PhpParser\Node\Expr\MethodCall;
7+
use PhpParser\Node\Expr\StaticCall;
68
use PHPStan\Analyser\Scope;
79
use PHPStan\Reflection\MethodReflection;
810
use PHPStan\Type\DynamicMethodReturnTypeExtension;
11+
use PHPStan\Type\DynamicStaticMethodReturnTypeExtension;
912
use PHPStan\Type\ObjectType;
1013
use PHPStan\Type\Type;
1114
use PHPStan\Type\TypeCombinator;
1215
use PHPUnit\Framework\MockObject\MockObject;
1316
use PHPUnit\Framework\MockObject\Stub;
1417
use PHPUnit\Framework\TestCase;
1518
use function count;
16-
use function in_array;
19+
use function strtolower;
1720

18-
class MockForIntersectionDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
21+
class MockForIntersectionDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension, DynamicStaticMethodReturnTypeExtension
1922
{
2023

2124
public function getClass(): string
@@ -25,19 +28,29 @@ public function getClass(): string
2528

2629
public function isMethodSupported(MethodReflection $methodReflection): bool
2730
{
28-
return in_array(
29-
$methodReflection->getName(),
30-
[
31-
'createMockForIntersectionOfInterfaces',
32-
'createStubForIntersectionOfInterfaces',
33-
],
34-
true,
35-
);
31+
return strtolower($methodReflection->getName()) === 'createmockforintersectionofinterfaces';
32+
}
33+
34+
public function isStaticMethodSupported(MethodReflection $methodReflection): bool
35+
{
36+
return strtolower($methodReflection->getName()) === 'createstubforintersectionofinterfaces';
37+
}
38+
39+
public function getTypeFromStaticMethodCall(MethodReflection $methodReflection, StaticCall $methodCall, Scope $scope): ?Type
40+
{
41+
return $this->getTypeFromCall($methodReflection, $methodCall->getArgs(), $scope);
3642
}
3743

3844
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type
3945
{
40-
$args = $methodCall->getArgs();
46+
return $this->getTypeFromCall($methodReflection, $methodCall->getArgs(), $scope);
47+
}
48+
49+
/**
50+
* @param array<Arg> $args
51+
*/
52+
private function getTypeFromCall(MethodReflection $methodReflection, array $args, Scope $scope): ?Type
53+
{
4154
if (!isset($args[0])) {
4255
return null;
4356
}

tests/Rules/PHPUnit/data/mock-method-call.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public function testMockForIntersection()
5151

5252
public function testStubForIntersection()
5353
{
54-
$stub = $this->createStubForIntersectionOfInterfaces([FooInterface::class, BarInterface::class]);
54+
$stub = static::createStubForIntersectionOfInterfaces([FooInterface::class, BarInterface::class]);
5555
$stub->method('fooMethod');
5656
$stub->method('barMethod');
5757
$stub->method('bazMethod');
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\PHPUnit;
4+
5+
use PHPStan\Testing\TypeInferenceTestCase;
6+
use PHPUnit\Framework\Attributes\DataProvider;
7+
use PHPUnit\Framework\TestCase;
8+
use function method_exists;
9+
10+
class MockForIntersectionDynamicReturnTypeExtensionTest extends TypeInferenceTestCase
11+
{
12+
13+
/** @return mixed[] */
14+
public static function dataFileAsserts(): iterable
15+
{
16+
if (method_exists(TestCase::class, 'createMockForIntersectionOfInterfaces')) { // @phpstan-ignore-line
17+
yield from self::gatherAssertTypes(__DIR__ . '/data/mock-for-intersection.php');
18+
}
19+
20+
return [];
21+
}
22+
23+
/**
24+
* @dataProvider dataFileAsserts
25+
* @param mixed ...$args
26+
*/
27+
#[DataProvider('dataFileAsserts')]
28+
public function testFileAsserts(
29+
string $assertType,
30+
string $file,
31+
...$args
32+
): void
33+
{
34+
$this->assertFileAsserts($assertType, $file, ...$args);
35+
}
36+
37+
public static function getAdditionalConfigFiles(): array
38+
{
39+
return [__DIR__ . '/../../../extension.neon'];
40+
}
41+
42+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace MockForIntersection;
4+
5+
use PHPUnit\Framework\TestCase;
6+
7+
use function PHPStan\Testing\assertType;
8+
9+
class Foo extends TestCase
10+
{
11+
12+
public function inheritedAssertMethodsNarrowType(?string $s): void
13+
{
14+
assertType(
15+
'MockForIntersection\BarInterface&MockForIntersection\FooInterface&PHPUnit\Framework\MockObject\MockObject',
16+
$this->createMockForIntersectionOfInterfaces([FooInterface::class, BarInterface::class]),
17+
);
18+
assertType(
19+
'MockForIntersection\BarInterface&MockForIntersection\FooInterface&PHPUnit\Framework\MockObject\Stub',
20+
self::createStubForIntersectionOfInterfaces([FooInterface::class, BarInterface::class]),
21+
);
22+
}
23+
24+
}
25+
26+
interface FooInterface {}
27+
interface BarInterface {}

0 commit comments

Comments
 (0)