diff --git a/src/Reflection/InitializerExprTypeResolver.php b/src/Reflection/InitializerExprTypeResolver.php index 7ab5be8d8f..d6404efacb 100644 --- a/src/Reflection/InitializerExprTypeResolver.php +++ b/src/Reflection/InitializerExprTypeResolver.php @@ -66,6 +66,7 @@ use PHPStan\Type\TypeUtils; use PHPStan\Type\TypeWithClassName; use PHPStan\Type\UnionType; +use function array_key_exists; use function array_keys; use function array_merge; use function assert; @@ -79,6 +80,7 @@ use function is_int; use function max; use function min; +use function sprintf; use function strtolower; use const INF; @@ -87,6 +89,9 @@ class InitializerExprTypeResolver public const CALCULATE_SCALARS_LIMIT = 128; + /** @var array */ + private array $currentlyResolvingClassConstant = []; + public function __construct( private ConstantResolver $constantResolver, private ReflectionProviderProvider $reflectionProviderProvider, @@ -1876,9 +1881,18 @@ function (Type $type, callable $traverse): Type { continue; } + $resolvingName = sprintf('%s::%s', $constantClassReflection->getName(), $constantName); + if (array_key_exists($resolvingName, $this->currentlyResolvingClassConstant)) { + $types[] = new MixedType(); + continue; + } + + $this->currentlyResolvingClassConstant[$resolvingName] = true; + if (!$isObject) { $reflectionConstant = $constantClassReflection->getNativeReflection()->getReflectionConstant($constantName); if ($reflectionConstant === false) { + unset($this->currentlyResolvingClassConstant[$resolvingName]); continue; } $reflectionConstantDeclaringClass = $reflectionConstant->getDeclaringClass(); @@ -1893,6 +1907,7 @@ function (Type $type, callable $traverse): Type { $constantType, $nativeType, ); + unset($this->currentlyResolvingClassConstant[$resolvingName]); continue; } @@ -1903,6 +1918,7 @@ function (Type $type, callable $traverse): Type { && !$constantReflection->hasPhpDocType() && !$constantReflection->hasNativeType() ) { + unset($this->currentlyResolvingClassConstant[$resolvingName]); return new MixedType(); } @@ -1925,6 +1941,7 @@ function (Type $type, callable $traverse): Type { $constantType, $nativeType, ); + unset($this->currentlyResolvingClassConstant[$resolvingName]); $types[] = $constantType; } diff --git a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php index 5717d7013a..bc04dbaa59 100644 --- a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php +++ b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php @@ -1264,6 +1264,12 @@ public function testBug10086(): void $this->assertNoErrors($errors); } + public function testBug10147(): void + { + $errors = $this->runAnalyse(__DIR__ . '/data/bug-10147.php'); + $this->assertNoErrors($errors); + } + /** * @param string[]|null $allAnalysedFiles * @return Error[] diff --git a/tests/PHPStan/Analyser/data/bug-10147.php b/tests/PHPStan/Analyser/data/bug-10147.php new file mode 100644 index 0000000000..8798de28e7 --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-10147.php @@ -0,0 +1,13 @@ +