diff --git a/src/Reflection/GenericParametersAcceptorResolver.php b/src/Reflection/GenericParametersAcceptorResolver.php index 2e79b78c9b..c26b40cafe 100644 --- a/src/Reflection/GenericParametersAcceptorResolver.php +++ b/src/Reflection/GenericParametersAcceptorResolver.php @@ -12,7 +12,7 @@ class GenericParametersAcceptorResolver /** * @api - * @param Type[] $argTypes + * @param array $argTypes */ public static function resolve(array $argTypes, ParametersAcceptor $parametersAcceptor): ParametersAcceptor { @@ -21,6 +21,8 @@ public static function resolve(array $argTypes, ParametersAcceptor $parametersAc foreach ($parametersAcceptor->getParameters() as $i => $param) { if (isset($argTypes[$i])) { $argType = $argTypes[$i]; + } elseif (isset($argTypes[$param->getName()])) { + $argType = $argTypes[$param->getName()]; } elseif ($param->getDefaultValue() !== null) { $argType = $param->getDefaultValue(); } else { diff --git a/src/Reflection/ParametersAcceptorSelector.php b/src/Reflection/ParametersAcceptorSelector.php index 899a8a87d1..15d13d5f63 100644 --- a/src/Reflection/ParametersAcceptorSelector.php +++ b/src/Reflection/ParametersAcceptorSelector.php @@ -18,6 +18,7 @@ use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; use PHPStan\Type\UnionType; +use function array_key_last; use function array_slice; use function count; use function sprintf; @@ -161,13 +162,14 @@ public static function selectFromArgs( } } - foreach ($args as $arg) { + foreach ($args as $i => $arg) { $type = $scope->getType($arg->value); + $index = $arg->name !== null ? $arg->name->toString() : $i; if ($arg->unpack) { $unpack = true; - $types[] = $type->getIterableValueType(); + $types[$index] = $type->getIterableValueType(); } else { - $types[] = $type; + $types[$index] = $type; } } @@ -175,7 +177,7 @@ public static function selectFromArgs( } /** - * @param Type[] $types + * @param array $types * @param ParametersAcceptor[] $parametersAcceptors */ public static function selectFromTypes( @@ -246,7 +248,7 @@ public static function selectFromTypes( break; } - $type = $types[count($types) - 1]; + $type = $types[array_key_last($types)]; } else { $type = $types[$i]; } diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index 4d22b68c2b..ff72a3fd2c 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -846,6 +846,11 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6917.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6936-limit.php'); + + if (PHP_VERSION_ID >= 80000) { + yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-5262.php'); + } + yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6927.php'); } diff --git a/tests/PHPStan/Analyser/data/bug-5262.php b/tests/PHPStan/Analyser/data/bug-5262.php new file mode 100644 index 0000000000..229d9fbeb2 --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-5262.php @@ -0,0 +1,36 @@ + $testclass + * @return T + */ +function test(bool $optional = false, string $testclass = TestBase::class): TestBase +{ + return new $testclass(); +} + +class TestBase +{ +} + +class TestChild extends TestBase +{ + public function hello(): string + { + return 'world'; + } +} + +function runTest(): void +{ + assertType('Bug5262\TestChild', test(false, TestChild::class)); + assertType('Bug5262\TestChild', test(false, testclass: TestChild::class)); + assertType('Bug5262\TestChild', test(optional: false, testclass: TestChild::class)); + assertType('Bug5262\TestChild', test(testclass: TestChild::class, optional: false)); + assertType('Bug5262\TestChild', test(testclass: TestChild::class)); +}