Skip to content

Commit

Permalink
Fixed inferring return type coming from array_map
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Feb 25, 2021
1 parent d97ddee commit ea7e0ac
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 3 deletions.
21 changes: 20 additions & 1 deletion src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
use PHPStan\Reflection\ParametersAcceptor;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Reflection\PassedByReference;
use PHPStan\Reflection\Php\DummyParameter;
use PHPStan\Reflection\Php\PhpFunctionFromParserNodeReflection;
use PHPStan\Reflection\Php\PhpMethodFromParserNodeReflection;
use PHPStan\Reflection\PropertyReflection;
Expand Down Expand Up @@ -1281,7 +1282,25 @@ private function resolveType(Expr $node): Type
$returnType = TypehintHelper::decideType($this->getFunctionType($node->returnType, false, false), $returnType);
}
} else {
$closureScope = $this->enterAnonymousFunctionWithoutReflection($node);
$callableParameters = null;
$arg = $node->getAttribute('parent');
if ($arg instanceof Arg) {
$funcCall = $arg->getAttribute('parent');
$argOrder = $arg->getAttribute('expressionOrder');
if ($funcCall instanceof FuncCall && $funcCall->name instanceof Name) {
$functionName = $this->reflectionProvider->resolveFunctionName($funcCall->name, $this);
if (
$functionName === 'array_map'
&& $argOrder === 0
&& isset($funcCall->args[1])
) {
$callableParameters = [
new DummyParameter('item', $this->getType($funcCall->args[1]->value)->getIterableValueType(), false, PassedByReference::createNo(), false, null),
];
}
}
}
$closureScope = $this->enterAnonymousFunctionWithoutReflection($node, $callableParameters);
$closureReturnStatements = [];
$closureYieldStatements = [];
$closureExecutionEnds = [];
Expand Down
5 changes: 4 additions & 1 deletion src/NodeVisitor/StatementOrderVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ public function enterNode(Node $node)
$node->setAttribute('statementOrder', $order);
$node->setAttribute('statementDepth', $this->depth);

if ($node instanceof Node\Expr && count($this->expressionOrderStack) > 0) {
if (
($node instanceof Node\Expr || $node instanceof Node\Arg)
&& count($this->expressionOrderStack) > 0
) {
$expressionOrder = $this->expressionOrderStack[count($this->expressionOrderStack) - 1];
$node->setAttribute('expressionOrder', $expressionOrder);
$node->setAttribute('expressionDepth', $this->expressionDepth);
Expand Down
6 changes: 6 additions & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10923,6 +10923,11 @@ public function dataBug4498(): array
return $this->gatherAssertTypes(__DIR__ . '/data/bug-4498.php');
}

public function dataBug4587(): array
{
return $this->gatherAssertTypes(__DIR__ . '/data/bug-4587.php');
}

/**
* @param string $file
* @return array<string, mixed[]>
Expand Down Expand Up @@ -11169,6 +11174,7 @@ private function gatherAssertTypes(string $file): array
* @dataProvider dataBug3769
* @dataProvider dataBugInstanceOfClassString
* @dataProvider dataBug4498
* @dataProvider dataBug4587
* @param string $assertType
* @param string $file
* @param mixed ...$args
Expand Down
37 changes: 37 additions & 0 deletions tests/PHPStan/Analyser/data/bug-4587.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace Bug4587;

use function PHPStan\Analyser\assertType;

class HelloWorld
{
public function a(): void
{
/** @var list<array{a: int}> $results */
$results = [];

$type = array_map(static function (array $result): array {
assertType('array(\'a\' => int)', $result);
return $result;
}, $results);

assertType('array<int, array(\'a\' => int)>', $type);
}

public function b(): void
{
/** @var list<array{a: int}> $results */
$results = [];

$type = array_map(static function (array $result): array {
assertType('array(\'a\' => int)', $result);
$result['a'] = (string) $result['a'];
assertType('array(\'a\' => string&numeric)', $result);

return $result;
}, $results);

assertType('array<int, array(\'a\' => string&numeric)>', $type);
}
}
2 changes: 1 addition & 1 deletion tests/PHPStan/Analyser/data/generics.php
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ function testArrayMap(array $listOfIntegers)

return (string) $int;
}, $listOfIntegers);
assertType('array<string>', $strings);
assertType('array<string&numeric>', $strings);
}

/**
Expand Down

0 comments on commit ea7e0ac

Please sign in to comment.