diff --git a/src/Reflection/InitializerExprTypeResolver.php b/src/Reflection/InitializerExprTypeResolver.php index f990d2b8c49..81acd57775f 100644 --- a/src/Reflection/InitializerExprTypeResolver.php +++ b/src/Reflection/InitializerExprTypeResolver.php @@ -2748,6 +2748,11 @@ public function getClassConstFetchTypeByReflection(Name|Expr $class, string $con unset($this->currentlyResolvingClassConstant[$resolvingName]); continue; } + if ($reflectionConstant->isEnumCase()) { + $types[] = new EnumCaseObjectType($constantClassReflection->getName(), $constantName); + unset($this->currentlyResolvingClassConstant[$resolvingName]); + continue; + } $reflectionConstantDeclaringClass = $reflectionConstant->getDeclaringClass(); $constantType = $this->getType($reflectionConstant->getValueExpression(), InitializerExprContext::fromClass($reflectionConstantDeclaringClass->getName(), $reflectionConstantDeclaringClass->getFileName() ?: null)); $nativeType = null; diff --git a/tests/PHPStan/Reflection/InitializerExprTypeResolverTest.php b/tests/PHPStan/Reflection/InitializerExprTypeResolverTest.php index 6c3ba79cfb0..51f3fca45e5 100644 --- a/tests/PHPStan/Reflection/InitializerExprTypeResolverTest.php +++ b/tests/PHPStan/Reflection/InitializerExprTypeResolverTest.php @@ -3,18 +3,70 @@ namespace PHPStan\Reflection; use PhpParser\Node\Expr; +use PhpParser\Node\Expr\ClassConstFetch; +use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Scalar\LNumber; use PhpParser\Node\Scalar\String_; +use PhpParser\ParserFactory; +use PHPStan\Analyser\ConstantResolver; +use PHPStan\BetterReflection\Reflection\Adapter\ReflectionClass; +use PHPStan\BetterReflection\Reflector\DefaultReflector; +use PHPStan\BetterReflection\SourceLocator\Ast\Locator; +use PHPStan\BetterReflection\SourceLocator\Type\StringSourceLocator; +use PHPStan\Php\PhpVersion; +use PHPStan\Reflection\ReflectionProvider\DirectReflectionProviderProvider; use PHPStan\ShouldNotHappenException; use PHPStan\Testing\PHPStanTestCase; use PHPStan\Type\Constant\ConstantIntegerType; +use PHPStan\Type\Constant\OversizedArrayBuilder; use PHPStan\Type\NeverType; +use PHPStan\Type\OperatorTypeSpecifyingExtensionRegistry; use PHPStan\Type\Type; +use PHPStan\Type\UnaryOperatorTypeSpecifyingExtensionRegistry; +use PHPStan\Type\VerbosityLevel; use PHPUnit\Framework\Attributes\DataProvider; class InitializerExprTypeResolverTest extends PHPStanTestCase { + public function testUnitEnumDefaultWithClassAdapter(): void + { + $container = self::getContainer(); + // Do not resolve the runtime's RoundingMode polyfill on older PHP versions. + $reflector = new DefaultReflector(new StringSourceLocator( + 'createForNewestSupportedVersion()), + )); + $classReflection = $container->getByType(ClassReflectionFactory::class)->create( + 'RoundingMode', + new ReflectionClass($reflector->reflectClass('RoundingMode')), + null, + null, + null, + ); + + // PHP runtimes without native enum reflection use the generic class adapter. + $reflectionProvider = $this->createMock(ReflectionProvider::class); + $reflectionProvider->method('hasClass')->with('RoundingMode')->willReturn(true); + $reflectionProvider->method('getClass')->with('RoundingMode')->willReturn($classReflection); + $resolver = new InitializerExprTypeResolver( + $container->getByType(ConstantResolver::class), + new DirectReflectionProviderProvider($reflectionProvider), + new PhpVersion(80500), + $container->getByType(OperatorTypeSpecifyingExtensionRegistry::class), + $container->getByType(UnaryOperatorTypeSpecifyingExtensionRegistry::class), + new OversizedArrayBuilder(), + $container->getParameter('usePathConstantsAsConstantString'), + ); + + $type = $resolver->getType( + new ClassConstFetch(new FullyQualified('RoundingMode'), 'HalfAwayFromZero'), + InitializerExprContext::createEmpty(), + ); + + $this->assertSame('RoundingMode::HalfAwayFromZero', $type->describe(VerbosityLevel::precise())); + } + public static function dataExplicitNever(): iterable { yield [