Skip to content

Commit

Permalink
Make array_map understand union of callables.
Browse files Browse the repository at this point in the history
  • Loading branch information
mad-briller committed Feb 2, 2024
1 parent f6cab89 commit eac0a6c
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 4 deletions.
5 changes: 3 additions & 2 deletions src/Type/Php/ArrayMapFunctionReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
$callableIsNull = $callableType->isNull()->yes();

if ($callableType->isCallable()->yes()) {
$valueType = new NeverType();
$valueTypes = [new NeverType()];
foreach ($callableType->getCallableParametersAcceptors($scope) as $parametersAcceptor) {
$valueType = TypeCombinator::union($valueType, $parametersAcceptor->getReturnType());
$valueTypes[] = $parametersAcceptor->getReturnType();
}
$valueType = TypeCombinator::union(...$valueTypes);
} elseif ($callableIsNull) {
$arrayBuilder = ConstantArrayTypeBuilder::createEmpty();
foreach (array_slice($functionCall->getArgs(), 1) as $index => $arg) {
Expand Down
11 changes: 9 additions & 2 deletions src/Type/UnionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
use PHPStan\Type\Generic\TemplateUnionType;
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
use function array_map;
use function array_merge;
use function array_unique;
use function array_values;
use function count;
Expand Down Expand Up @@ -703,15 +704,21 @@ public function isCallable(): TrinaryLogic
*/
public function getCallableParametersAcceptors(ClassMemberAccessAnswerer $scope): array
{
$acceptors = [];

foreach ($this->types as $type) {
if ($type->isCallable()->no()) {
continue;
}

return $type->getCallableParametersAcceptors($scope);
$acceptors = array_merge($acceptors, $type->getCallableParametersAcceptors($scope));
}

if (count($acceptors) === 0) {
throw new ShouldNotHappenException();
}

throw new ShouldNotHappenException();
return $acceptors;
}

public function isCloneable(): TrinaryLogic
Expand Down
1 change: 1 addition & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1427,6 +1427,7 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10302-interface-extends.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10302-trait-extends.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10302-trait-implements.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10442.php');
}

/**
Expand Down
15 changes: 15 additions & 0 deletions tests/PHPStan/Analyser/data/bug-10442.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace Bug10442;

use function PHPStan\Testing\assertType;

/**
* @param callable(mixed): string|callable(mixed): int $callable
*/
function test(callable $callable): void
{
$val = array_map($callable, ['val', 'val2']);

assertType('array{int|string, int|string}', $val);
}

0 comments on commit eac0a6c

Please sign in to comment.