Skip to content

Commit c612c94

Browse files
committed
Fix analysis on array_map with named arguments
1 parent 5878035 commit c612c94

File tree

3 files changed

+71
-12
lines changed

3 files changed

+71
-12
lines changed

src/Parser/ArrayMapArgVisitor.php

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use PhpParser\Node;
77
use PhpParser\NodeVisitorAbstract;
88
use PHPStan\DependencyInjection\AutowiredService;
9-
use function array_slice;
9+
use function array_splice;
1010
use function count;
1111

1212
#[AutowiredService]
@@ -18,19 +18,41 @@ final class ArrayMapArgVisitor extends NodeVisitorAbstract
1818
#[Override]
1919
public function enterNode(Node $node): ?Node
2020
{
21-
if ($node instanceof Node\Expr\FuncCall && $node->name instanceof Node\Name && !$node->isFirstClassCallable()) {
22-
$functionName = $node->name->toLowerString();
23-
if ($functionName === 'array_map') {
24-
$args = $node->getArgs();
25-
if (isset($args[0])) {
26-
$slicedArgs = array_slice($args, 1);
27-
if (count($slicedArgs) > 0) {
28-
$args[0]->value->setAttribute(self::ATTRIBUTE_NAME, $slicedArgs);
29-
}
30-
}
31-
}
21+
if (!$this->isArrayMapCall($node)) {
22+
return null;
3223
}
24+
25+
$args = $node->getArgs();
26+
if (count($args) < 2) {
27+
return null;
28+
}
29+
30+
$callbackPos = 0;
31+
if ($args[1]->name !== null && $args[1]->name->name === 'callback') {
32+
$callbackPos = 1;
33+
}
34+
[$callback] = array_splice($args, $callbackPos, 1);
35+
$callback->value->setAttribute(self::ATTRIBUTE_NAME, $args);
36+
3337
return null;
3438
}
3539

40+
/**
41+
* @phpstan-assert-if-true Node\Expr\FuncCall $node
42+
*/
43+
private function isArrayMapCall(Node $node): bool
44+
{
45+
if (!$node instanceof Node\Expr\FuncCall) {
46+
return false;
47+
}
48+
if (!$node->name instanceof Node\Name) {
49+
return false;
50+
}
51+
if ($node->isFirstClassCallable()) {
52+
return false;
53+
}
54+
55+
return $node->name->toLowerString() === 'array_map';
56+
}
57+
3658
}

tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2182,4 +2182,15 @@ public function testBug13065(): void
21822182
$this->analyse([__DIR__ . '/data/bug-13065.php'], $errors);
21832183
}
21842184

2185+
public function testBug12317(): void
2186+
{
2187+
if (PHP_VERSION_ID < 80000) {
2188+
$this->markTestSkipped('Test requires PHP 8.0.');
2189+
}
2190+
2191+
$this->checkExplicitMixed = true;
2192+
$this->checkImplicitMixed = true;
2193+
$this->analyse([__DIR__ . '/data/bug-12317.php'], []);
2194+
}
2195+
21852196
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug12317;
4+
5+
class Uuid {
6+
public function __construct(public string $uuid) {}
7+
public function __toString() { return $this->uuid; }
8+
}
9+
10+
class HelloWorld
11+
{
12+
/**
13+
* @param list<Uuid> $a
14+
*
15+
* @return list<string>
16+
*/
17+
public function sayHello(array $a): array
18+
{
19+
$b = array_map(
20+
array: $a,
21+
callback: static fn(Uuid $c): string => (string) $c,
22+
);
23+
24+
return $b;
25+
}
26+
}

0 commit comments

Comments
 (0)