|
13 | 13 | use PhpParser\Node\Expr\Variable; |
14 | 14 | use PhpParser\Node\Scalar\String_; |
15 | 15 | use PhpParser\Node\Stmt\Class_; |
| 16 | +use PhpParser\Node\Stmt\Return_; |
| 17 | +use PhpParser\NodeFinder; |
16 | 18 | use PHPStan\Reflection\ClassReflection; |
17 | 19 | use PHPStan\Type\IntersectionType; |
18 | 20 | use PHPStan\Type\MixedType; |
@@ -241,6 +243,10 @@ public function refactor(Node $node): ?Class_ |
241 | 243 | return null; |
242 | 244 | } |
243 | 245 |
|
| 246 | + if ($this->shouldSkipReturnForConflictWithReturnedNodeType($innerClosure, $returnType)) { |
| 247 | + return null; |
| 248 | + } |
| 249 | + |
244 | 250 | $returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( |
245 | 251 | $returnType, |
246 | 252 | TypeKind::RETURN |
@@ -326,4 +332,28 @@ private function fallbackMockedObjectInSetUp( |
326 | 332 |
|
327 | 333 | return $callerType; |
328 | 334 | } |
| 335 | + |
| 336 | + private function shouldSkipReturnForConflictWithReturnedNodeType( |
| 337 | + Closure|ArrowFunction $functionLike, |
| 338 | + Type $returnType |
| 339 | + ): bool { |
| 340 | + // find return functionLike, to check current type |
| 341 | + $nodeFinder = new NodeFinder(); |
| 342 | + $returns = $nodeFinder->findInstanceOf($functionLike, Return_::class); |
| 343 | + $returnTypes = []; |
| 344 | + foreach ($returns as $return) { |
| 345 | + if ($return->expr instanceof Node) { |
| 346 | + $returnTypes[] = $this->getType($return->expr); |
| 347 | + } |
| 348 | + } |
| 349 | + |
| 350 | + if (count($returnTypes) === 1) { |
| 351 | + $closureReturnedNodeType = $returnTypes[0]; |
| 352 | + if (! $closureReturnedNodeType->isSuperTypeOf($returnType)->yes()) { |
| 353 | + return true; |
| 354 | + } |
| 355 | + } |
| 356 | + |
| 357 | + return false; |
| 358 | + } |
329 | 359 | } |
0 commit comments