|
5 | 5 | use PhpParser\Node;
|
6 | 6 | use PhpParser\Node\Expr\MethodCall;
|
7 | 7 | use PHPStan\Analyser\Scope;
|
| 8 | +use PHPStan\Rules\IdentifierRuleError; |
8 | 9 | use PHPStan\Rules\Rule;
|
9 | 10 | use PHPStan\Rules\RuleErrorBuilder;
|
10 |
| -use PHPUnit\Framework\MockObject\Builder\InvocationMocker; |
| 11 | +use PHPStan\Type\Type; |
11 | 12 | use PHPUnit\Framework\MockObject\MockObject;
|
12 | 13 | use PHPUnit\Framework\MockObject\Stub;
|
13 | 14 | use function array_filter;
|
@@ -47,44 +48,58 @@ public function processNode(Node $node, Scope $scope): array
|
47 | 48 | $method = $constantString->getValue();
|
48 | 49 | $type = $scope->getType($node->var);
|
49 | 50 |
|
50 |
| - if ( |
51 |
| - ( |
52 |
| - in_array(MockObject::class, $type->getObjectClassNames(), true) |
53 |
| - || in_array(Stub::class, $type->getObjectClassNames(), true) |
54 |
| - ) |
55 |
| - && !$type->hasMethod($method)->yes() |
56 |
| - ) { |
57 |
| - $mockClasses = array_filter($type->getObjectClassNames(), static fn (string $class): bool => $class !== MockObject::class && $class !== Stub::class); |
58 |
| - if (count($mockClasses) === 0) { |
59 |
| - continue; |
60 |
| - } |
61 |
| - |
62 |
| - $errors[] = RuleErrorBuilder::message(sprintf( |
63 |
| - 'Trying to mock an undefined method %s() on class %s.', |
64 |
| - $method, |
65 |
| - implode('&', $mockClasses), |
66 |
| - ))->identifier('phpunit.mockMethod')->build(); |
| 51 | + $error = $this->checkCallOnType($type, $method); |
| 52 | + if ($error !== null) { |
| 53 | + $errors[] = $error; |
67 | 54 | continue;
|
68 | 55 | }
|
69 | 56 |
|
70 |
| - $mockedClassObject = $type->getTemplateType(InvocationMocker::class, 'TMockedClass'); |
71 |
| - if ($mockedClassObject->hasMethod($method)->yes()) { |
| 57 | + if (!$node->var instanceof MethodCall) { |
72 | 58 | continue;
|
73 | 59 | }
|
74 | 60 |
|
75 |
| - $classNames = $mockedClassObject->getObjectClassNames(); |
76 |
| - if (count($classNames) === 0) { |
| 61 | + if (!$node->var->name instanceof Node\Identifier) { |
77 | 62 | continue;
|
78 | 63 | }
|
79 | 64 |
|
80 |
| - $errors[] = RuleErrorBuilder::message(sprintf( |
| 65 | + if ($node->var->name->toLowerString() !== 'expects') { |
| 66 | + continue; |
| 67 | + } |
| 68 | + |
| 69 | + $varType = $scope->getType($node->var->var); |
| 70 | + $error = $this->checkCallOnType($varType, $method); |
| 71 | + if ($error === null) { |
| 72 | + continue; |
| 73 | + } |
| 74 | + |
| 75 | + $errors[] = $error; |
| 76 | + } |
| 77 | + |
| 78 | + return $errors; |
| 79 | + } |
| 80 | + |
| 81 | + private function checkCallOnType(Type $type, string $method): ?IdentifierRuleError |
| 82 | + { |
| 83 | + if ( |
| 84 | + ( |
| 85 | + in_array(MockObject::class, $type->getObjectClassNames(), true) |
| 86 | + || in_array(Stub::class, $type->getObjectClassNames(), true) |
| 87 | + ) |
| 88 | + && !$type->hasMethod($method)->yes() |
| 89 | + ) { |
| 90 | + $mockClasses = array_filter($type->getObjectClassNames(), static fn (string $class): bool => $class !== MockObject::class && $class !== Stub::class); |
| 91 | + if (count($mockClasses) === 0) { |
| 92 | + return null; |
| 93 | + } |
| 94 | + |
| 95 | + return RuleErrorBuilder::message(sprintf( |
81 | 96 | 'Trying to mock an undefined method %s() on class %s.',
|
82 | 97 | $method,
|
83 |
| - implode('|', $classNames), |
| 98 | + implode('&', $mockClasses), |
84 | 99 | ))->identifier('phpunit.mockMethod')->build();
|
85 | 100 | }
|
86 | 101 |
|
87 |
| - return $errors; |
| 102 | + return null; |
88 | 103 | }
|
89 | 104 |
|
90 | 105 | }
|
0 commit comments