Skip to content

Commit

Permalink
Fix DsMapDynamicReturnTypeExtension
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Mar 18, 2021
1 parent 10a2b24 commit 330eb22
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 7 deletions.
30 changes: 23 additions & 7 deletions src/Type/Php/DsMapDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Type\DynamicMethodReturnTypeExtension;
use PHPStan\Type\Generic\TemplateType;
use PHPStan\Type\Generic\TemplateTypeScope;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\UnionType;
Expand All @@ -27,22 +28,33 @@ public function isMethodSupported(MethodReflection $methodReflection): bool

public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
{
$returnType = $methodReflection->getVariants()[0]->getReturnType();
$returnType = ParametersAcceptorSelector::selectFromArgs(
$scope,
$methodCall->args,
$methodReflection->getVariants()
)->getReturnType();

if (count($methodCall->args) > 1) {
return ParametersAcceptorSelector::selectFromArgs(
$scope,
$methodCall->args,
$methodReflection->getVariants()
)->getReturnType();
return $returnType;
}

if ($returnType instanceof UnionType) {
$types = array_values(
array_filter(
$returnType->getTypes(),
static function (Type $type): bool {
return !$type instanceof TemplateType;
if (
$type instanceof TemplateType
&& $type->getName() === 'TDefault'
&& (
$type->getScope()->equals(TemplateTypeScope::createWithMethod('Ds\Map', 'get'))
|| $type->getScope()->equals(TemplateTypeScope::createWithMethod('Ds\Map', 'remove'))
)
) {
return false;
}

return true;
}
)
);
Expand All @@ -51,6 +63,10 @@ static function (Type $type): bool {
return $types[0];
}

if (count($types) === 0) {
return $returnType;
}

return TypeCombinator::union(...$types);
}

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 @@ -5695,6 +5695,11 @@ public function dataBug4707(): array
return $this->gatherAssertTypes(__DIR__ . '/data/bug-4707.php');
}

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

/**
* @dataProvider dataArrayFunctions
* @param string $description
Expand Down Expand Up @@ -11317,6 +11322,7 @@ private function gatherAssertTypes(string $file): array
* @dataProvider dataBug4213
* @dataProvider dataBug4657
* @dataProvider dataBug4707
* @dataProvider dataBug4545
* @param string $assertType
* @param string $file
* @param mixed ...$args
Expand Down
43 changes: 43 additions & 0 deletions tests/PHPStan/Analyser/data/bug-4545.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace Bug4545;

use Closure;
use Ds\Hashable;
use Ds\Map;
use Ds\Set;
use function PHPStan\Analyser\assertType;

class Foo
{

/**
* Returns keys which either exist only in one of the maps or exist in both but their associated values are not equal.
*
* @template TKey of Hashable
* @template TValue1
* @template TValue2
*
* @param Map<TKey, TValue1> $firstMap
* @param Map<TKey, TValue2> $secondMap
* @param Closure(TValue1, TValue2): bool $comparator
*
* @return Set<TKey>
*/
function compareMaps(Map $firstMap, Map $secondMap, Closure $comparator): Set
{
$firstMapKeys = $firstMap->keys();
$secondMapKeys = $secondMap->keys();
$keys = $firstMapKeys->xor($secondMapKeys);
$intersect = $firstMapKeys->intersect($secondMapKeys);
foreach ($intersect as $key) {
assertType('TValue1 (method Bug4545\Foo::compareMaps(), argument)', $firstMap->get($key));
assertType('TValue2 (method Bug4545\Foo::compareMaps(), argument)', $secondMap->get($key));
assertType('int|TValue2 (method Bug4545\Foo::compareMaps(), argument)', $secondMap->get($key, 1));
}

return $keys;
}

}

0 comments on commit 330eb22

Please sign in to comment.