Skip to content

Commit 4b52a58

Browse files
b1rdexondrejmirtes
authored andcommitted
array_filter w/ arrow functions return type extension
1 parent 3d0ac6f commit 4b52a58

File tree

3 files changed

+68
-6
lines changed

3 files changed

+68
-6
lines changed

src/Type/Php/ArrayFilterFunctionReturnTypeReturnTypeExtension.php

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PHPStan\Type\Php;
44

5+
use PhpParser\Node\Expr\ArrowFunction;
56
use PhpParser\Node\Expr\Closure;
67
use PhpParser\Node\Expr\FuncCall;
78
use PhpParser\Node\Expr\Variable;
@@ -53,18 +54,29 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
5354
);
5455
}
5556

56-
if ($flagArg === null && $callbackArg instanceof Closure && count($callbackArg->stmts) === 1) {
57-
$statement = $callbackArg->stmts[0];
58-
if ($statement instanceof Return_ && $statement->expr !== null && count($callbackArg->params) > 0) {
59-
if (!$callbackArg->params[0]->var instanceof Variable || !is_string($callbackArg->params[0]->var->name)) {
57+
if ($flagArg === null) {
58+
$var = null;
59+
$expr = null;
60+
if ($callbackArg instanceof Closure && count($callbackArg->stmts) === 1 && count($callbackArg->params) > 0) {
61+
$statement = $callbackArg->stmts[0];
62+
if ($statement instanceof Return_ && $statement->expr !== null) {
63+
$var = $callbackArg->params[0]->var;
64+
$expr = $statement->expr;
65+
}
66+
} elseif ($callbackArg instanceof ArrowFunction && count($callbackArg->params) > 0) {
67+
$var = $callbackArg->params[0]->var;
68+
$expr = $callbackArg->expr;
69+
}
70+
if ($var !== null && $expr !== null) {
71+
if (!$var instanceof Variable || !is_string($var->name)) {
6072
throw new \PHPStan\ShouldNotHappenException();
6173
}
62-
$itemVariableName = $callbackArg->params[0]->var->name;
74+
$itemVariableName = $var->name;
6375
if (!$scope instanceof MutatingScope) {
6476
throw new \PHPStan\ShouldNotHappenException();
6577
}
6678
$scope = $scope->assignVariable($itemVariableName, $itemType);
67-
$scope = $scope->filterByTruthyValue($statement->expr);
79+
$scope = $scope->filterByTruthyValue($expr);
6880
$itemType = $scope->getVariableType($itemVariableName);
6981
}
7082
}

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10585,6 +10585,15 @@ public function dataBug3986(): array
1058510585
return $this->gatherAssertTypes(__DIR__ . '/data/bug-3986.php');
1058610586
}
1058710587

10588+
public function dataBug4188(): array
10589+
{
10590+
if (PHP_VERSION_ID < 70400) {
10591+
return [];
10592+
}
10593+
10594+
return $this->gatherAssertTypes(__DIR__ . '/data/bug-4188.php');
10595+
}
10596+
1058810597
/**
1058910598
* @param string $file
1059010599
* @return array<string, mixed[]>
@@ -10787,6 +10796,7 @@ private function gatherAssertTypes(string $file): array
1078710796
* @dataProvider dataBug1209
1078810797
* @dataProvider dataBug2980
1078910798
* @dataProvider dataBug3986
10799+
* @dataProvider dataBug4188
1079010800
* @param string $assertType
1079110801
* @param string $file
1079210802
* @param mixed ...$args
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php // lint >= 7.4
2+
3+
namespace Bug4188;
4+
5+
interface A {}
6+
interface B {}
7+
8+
use function PHPStan\Analyser\assertType;
9+
10+
class Test
11+
{
12+
/** @param array<A|B> $data */
13+
public function set(array $data): void
14+
{
15+
$filtered = array_filter(
16+
$data,
17+
function ($param): bool {
18+
return $param instanceof B;
19+
},
20+
);
21+
assertType('array<Bug4188\B>', $filtered);
22+
23+
$this->onlyB($filtered);
24+
}
25+
26+
/** @param array<A|B> $data */
27+
public function setShort(array $data): void
28+
{
29+
$filtered = array_filter(
30+
$data,
31+
fn($param): bool => $param instanceof B,
32+
);
33+
assertType('array<Bug4188\B>', $filtered);
34+
35+
$this->onlyB($filtered);
36+
}
37+
38+
/** @param B[] $data */
39+
public function onlyB(array $data): void {}
40+
}

0 commit comments

Comments
 (0)