Skip to content

Commit

Permalink
Fix variadic parameters for generics
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Apr 20, 2022
1 parent 39e8286 commit f091c56
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 6 deletions.
39 changes: 33 additions & 6 deletions src/Reflection/GenericParametersAcceptorResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
use PHPStan\Type\ErrorType;
use PHPStan\Type\Generic\TemplateTypeMap;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use function array_key_exists;
use function array_merge;
use function count;
use function is_int;

class GenericParametersAcceptorResolver
{
Expand All @@ -18,15 +22,38 @@ public static function resolve(array $argTypes, ParametersAcceptor $parametersAc
{
$typeMap = TemplateTypeMap::createEmpty();

foreach ($parametersAcceptor->getParameters() as $i => $param) {
if (isset($argTypes[$i])) {
$argType = $argTypes[$i];
} elseif (isset($argTypes[$param->getName()])) {
$argType = $argTypes[$param->getName()];
$parameters = $parametersAcceptor->getParameters();
$namedArgTypes = [];
foreach ($argTypes as $i => $argType) {
if (is_int($i)) {
if (isset($parameters[$i])) {
$namedArgTypes[$parameters[$i]->getName()] = $argType;
continue;
}
if (count($parameters) > 0) {
$lastParameter = $parameters[count($parameters) - 1];
if ($lastParameter->isVariadic()) {
$parameterName = $lastParameter->getName();
if (array_key_exists($parameterName, $namedArgTypes)) {
$namedArgTypes[$parameterName] = TypeCombinator::union($namedArgTypes[$parameterName], $argType);
continue;
}
$namedArgTypes[$parameterName] = $argType;
}
}
continue;
}

$namedArgTypes[$i] = $argType;
}

foreach ($parametersAcceptor->getParameters() as $param) {
if (isset($namedArgTypes[$param->getName()])) {
$argType = $namedArgTypes[$param->getName()];
} elseif ($param->getDefaultValue() !== null) {
$argType = $param->getDefaultValue();
} else {
break;
continue;
}

$paramType = $param->getType();
Expand Down
6 changes: 6 additions & 0 deletions tests/PHPStan/Analyser/AnalyserIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,12 @@ public function testBug6979(): void
$this->assertNoErrors($errors);
}

public function testBug7068(): void
{
$errors = $this->runAnalyse(__DIR__ . '/data/bug-7068.php');
$this->assertNoErrors($errors);
}

/**
* @param string[]|null $allAnalysedFiles
* @return Error[]
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 @@ -867,6 +867,7 @@ public function dataFileAsserts(): iterable

yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6927.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/constant-array-optional-set.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7068.php');
}

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

namespace Bug7068;

use function PHPStan\Testing\assertType;

class Foo
{

/**
* @template T
* @param array<T> ...$arrays
* @return array<T>
*/
function merge(array ...$arrays): array {
return array_merge(...$arrays);
}

public function doFoo(): void
{
assertType('array<int>', $this->merge([1, 2], [3, 4], [5]));
assertType('array<int|string>', $this->merge([1, 2], ['foo', 'bar']));
}

}

0 comments on commit f091c56

Please sign in to comment.