diff --git a/src/PhpDoc/TypeNodeResolver.php b/src/PhpDoc/TypeNodeResolver.php index 99d5cab27e..645f237ce1 100644 --- a/src/PhpDoc/TypeNodeResolver.php +++ b/src/PhpDoc/TypeNodeResolver.php @@ -696,22 +696,8 @@ static function (string $variance): TemplateTypeVariance { return new ErrorType(); } elseif ($mainTypeName === 'value-of') { if (count($genericTypes) === 1) { // value-of - $genericType = $genericTypes[0]; - if ($genericType->isEnum()->yes()) { - $valueTypes = []; - foreach ($genericType->getEnumCases() as $enumCase) { - $valueType = $enumCase->getBackingValueType(); - if ($valueType === null) { - continue; - } - - $valueTypes[] = $valueType; - } - - return TypeCombinator::union(...$valueTypes); - } + $type = new ValueOfType($genericTypes[0]); - $type = new ValueOfType($genericType); return $type->isResolvable() ? $type->resolve() : $type; } diff --git a/src/Type/ValueOfType.php b/src/Type/ValueOfType.php index ffaef499d5..9df5411c47 100644 --- a/src/Type/ValueOfType.php +++ b/src/Type/ValueOfType.php @@ -49,6 +49,20 @@ public function isResolvable(): bool protected function getResult(): Type { + if ($this->type->isEnum()->yes()) { + $valueTypes = []; + foreach ($this->type->getEnumCases() as $enumCase) { + $valueType = $enumCase->getBackingValueType(); + if ($valueType === null) { + continue; + } + + $valueTypes[] = $valueType; + } + + return TypeCombinator::union(...$valueTypes); + } + return $this->type->getIterableValueType(); } diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index 121efe3e7f..c670c60fa6 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -334,6 +334,11 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/array-sum.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/array-plus.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-4573.php'); + + if (PHP_VERSION_ID >= 80100) { + yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-9881.php'); + } + yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-4577.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-4579.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-3321.php'); diff --git a/tests/PHPStan/Analyser/data/bug-9881.php b/tests/PHPStan/Analyser/data/bug-9881.php new file mode 100644 index 0000000000..129ca9c87f --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-9881.php @@ -0,0 +1,27 @@ += 8.1 + +namespace Bug9881; + +use BackedEnum; +use function PHPStan\Testing\assertType; + +class HelloWorld +{ + /** + * @template B of BackedEnum + * @param B[] $enums + * @return value-of[] + */ + public static function arrayEnumToStrings(array $enums): array + { + return array_map(static fn (BackedEnum $code): string|int => $code->value, $enums); + } +} + +enum Test: string { + case DA = 'da'; +} + +function (Test ...$da): void { + assertType('array<\'da\'>', HelloWorld::arrayEnumToStrings($da)); +};