Skip to content

Commit

Permalink
Fix analysis on array_map with named arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
takaram committed Jan 1, 2025
1 parent 068be33 commit 6a2cc72
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 12 deletions.
43 changes: 31 additions & 12 deletions src/Parser/ArrayMapArgVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
use function array_slice;
use function array_splice;
use function count;

final class ArrayMapArgVisitor extends NodeVisitorAbstract
Expand All @@ -14,19 +14,38 @@ final class ArrayMapArgVisitor extends NodeVisitorAbstract

public function enterNode(Node $node): ?Node
{
if ($node instanceof Node\Expr\FuncCall && $node->name instanceof Node\Name && !$node->isFirstClassCallable()) {
$functionName = $node->name->toLowerString();
if ($functionName === 'array_map') {
$args = $node->getArgs();
if (isset($args[0])) {
$slicedArgs = array_slice($args, 1);
if (count($slicedArgs) > 0) {
$args[0]->value->setAttribute(self::ATTRIBUTE_NAME, $slicedArgs);
}
}
}
if (!$this->isArrayMapCall($node)) {
return null;
}

$args = $node->getArgs();
if (count($args) < 2) {
return null;
}

$callbackPos = $args[1]->name?->name === 'callback' ? 1 : 0;
[$callback] = array_splice($args, $callbackPos, 1);
$callback->value->setAttribute(self::ATTRIBUTE_NAME, $args);

return null;
}

/**
* @phpstan-assert-if-true Node\Expr\FuncCall $node
*/
private function isArrayMapCall(Node $node): bool
{
if (!$node instanceof Node\Expr\FuncCall) {
return false;
}
if (!$node->name instanceof Node\Name) {
return false;
}
if ($node->isFirstClassCallable()) {
return false;
}

return $node->name->toLowerString() === 'array_map';
}

}
11 changes: 11 additions & 0 deletions tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1936,4 +1936,15 @@ public function testBug12051(): void
$this->analyse([__DIR__ . '/data/bug-12051.php'], []);
}

public function testBug12317(): void
{
if (PHP_VERSION_ID < 80000) {
$this->markTestSkipped('Test requires PHP 8.0.');
}

$this->checkExplicitMixed = true;
$this->checkImplicitMixed = true;
$this->analyse([__DIR__ . '/data/bug-12317.php'], []);
}

}
26 changes: 26 additions & 0 deletions tests/PHPStan/Rules/Functions/data/bug-12317.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php declare(strict_types = 1);

namespace Bug12317;

class Uuid {
public function __construct(public string $uuid) {}
public function __toString() { return $this->uuid; }
}

class HelloWorld
{
/**
* @param list<Uuid> $a
*
* @return list<string>
*/
public function sayHello(array $a): array
{
$b = array_map(
array: $a,
callback: static fn(Uuid $c): string => (string) $c,
);

return $b;
}
}

0 comments on commit 6a2cc72

Please sign in to comment.