Skip to content

Commit a483c11

Browse files
authored
Detect closure parameter types when passing closure in a union
1 parent 7b104ae commit a483c11

File tree

3 files changed

+36
-0
lines changed

3 files changed

+36
-0
lines changed

src/Analyser/NodeScopeResolver.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@
139139
use PHPStan\Type\VoidType;
140140
use Throwable;
141141
use function array_fill_keys;
142+
use function array_filter;
142143
use function array_key_exists;
143144
use function array_map;
144145
use function array_merge;
@@ -2858,6 +2859,13 @@ private function processClosureNode(
28582859
$byRefUses = [];
28592860

28602861
if ($passedToType !== null && !$passedToType->isCallable()->no()) {
2862+
if ($passedToType instanceof UnionType) {
2863+
$passedToType = TypeCombinator::union(...array_filter(
2864+
$passedToType->getTypes(),
2865+
static fn (Type $type) => $type->isCallable()->yes(),
2866+
));
2867+
}
2868+
28612869
$callableParameters = null;
28622870
$acceptors = $passedToType->getCallableParametersAcceptors($scope);
28632871
if (count($acceptors) === 1) {
@@ -2987,6 +2995,13 @@ private function processArrowFunctionNode(
29872995
}
29882996

29892997
if ($passedToType !== null && !$passedToType->isCallable()->no()) {
2998+
if ($passedToType instanceof UnionType) {
2999+
$passedToType = TypeCombinator::union(...array_filter(
3000+
$passedToType->getTypes(),
3001+
static fn (Type $type) => $type->isCallable()->yes(),
3002+
));
3003+
}
3004+
29903005
$callableParameters = null;
29913006
$acceptors = $passedToType->getCallableParametersAcceptors($scope);
29923007
if (count($acceptors) === 1) {

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,10 @@ public function dataFileAsserts(): iterable
714714
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6672.php');
715715
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6687.php');
716716

717+
if (self::$useStaticReflectionProvider) {
718+
yield from $this->gatherAssertTypes(__DIR__ . '/data/callable-in-union.php');
719+
}
720+
717721
require_once __DIR__ . '/data/countable.php';
718722
yield from $this->gatherAssertTypes(__DIR__ . '/data/countable.php');
719723
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace CallableInUnion;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/** @param array<string, mixed>|(callable(array<string, mixed>): array<string, mixed>) $_ */
8+
function acceptArrayOrCallable($_)
9+
{
10+
}
11+
12+
acceptArrayOrCallable(fn ($parameter) => assertType('array<string, mixed>', $parameter));
13+
14+
acceptArrayOrCallable(function ($parameter) {
15+
assertType('array<string, mixed>', $parameter);
16+
return $parameter;
17+
});

0 commit comments

Comments
 (0)