diff --git a/src/Analyser/ConstantResolver.php b/src/Analyser/ConstantResolver.php index 52f3b515e3..d304ef5a64 100644 --- a/src/Analyser/ConstantResolver.php +++ b/src/Analyser/ConstantResolver.php @@ -13,6 +13,8 @@ use PHPStan\Reflection\ReflectionProvider\ReflectionProviderProvider; use PHPStan\ShouldNotHappenException; use PHPStan\Type\Accessory\AccessoryNonFalsyStringType; +use PHPStan\Type\ArrayType; +use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantFloatType; use PHPStan\Type\Constant\ConstantIntegerType; use PHPStan\Type\Constant\ConstantStringType; @@ -424,7 +426,7 @@ public function resolveConstantType(string $constantName, Type $constantType): T return $constantType; } if (in_array($constantName, $this->dynamicConstantNames, true)) { - return $constantType->generalize(GeneralizePrecision::lessSpecific()); + return $this->generalizeDynamicConstantType($constantType); } } @@ -459,13 +461,23 @@ public function resolveClassConstantType(string $className, string $constantName } if ($constantType->isConstantValue()->yes()) { - return $constantType->generalize(GeneralizePrecision::lessSpecific()); + return $this->generalizeDynamicConstantType($constantType); } } return $constantType; } + private function generalizeDynamicConstantType(Type $constantType): Type + { + $generalized = $constantType->generalize(GeneralizePrecision::lessSpecific()); + if ($generalized->equals(new ConstantArrayType([], []))) { + return new ArrayType(new MixedType(), new MixedType()); + } + + return $generalized; + } + private function createInteger(?int $min, ?int $max): Type { if ($min !== null && $min === $max) { diff --git a/tests/PHPStan/Analyser/Bug8526IntegrationTest.php b/tests/PHPStan/Analyser/Bug8526IntegrationTest.php new file mode 100644 index 0000000000..2600ccd28a --- /dev/null +++ b/tests/PHPStan/Analyser/Bug8526IntegrationTest.php @@ -0,0 +1,53 @@ +runAnalyse(__DIR__ . '/data/bug-8526.php'); + $this->assertNoErrors($errors); + } + + /** + * @return list + */ + private function runAnalyse(string $file): array + { + $file = $this->getFileHelper()->normalizePath($file); + + $analyser = self::getContainer()->getByType(Analyser::class); + $finalizer = self::getContainer()->getByType(AnalyserResultFinalizer::class); + $errors = $finalizer->finalize( + $analyser->analyse([$file], null, null, true), + false, + true, + )->getErrors(); + foreach ($errors as $error) { + $this->assertSame($file, $error->getFilePath()); + } + + return $errors; + } + + public static function getAdditionalConfigFiles(): array + { + return array_unique( + array_merge( + parent::getAdditionalConfigFiles(), + [ + __DIR__ . '/bug-8526.neon', + ], + ), + ); + } + +} diff --git a/tests/PHPStan/Analyser/bug-8526.neon b/tests/PHPStan/Analyser/bug-8526.neon new file mode 100644 index 0000000000..a3e89eab62 --- /dev/null +++ b/tests/PHPStan/Analyser/bug-8526.neon @@ -0,0 +1,7 @@ +includes: + - ../../../conf/bleedingEdge.neon + +parameters: + dynamicConstantNames: + - FOO + - DYNAMICARRAY diff --git a/tests/PHPStan/Analyser/data/bug-8526.php b/tests/PHPStan/Analyser/data/bug-8526.php new file mode 100644 index 0000000000..ba035357a0 --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-8526.php @@ -0,0 +1,16 @@ +', DynamicConstantClass::DYNAMIC_EMPTY_ARRAY_WITH_PHPDOC_CONSTANT); assertType('int', DynamicConstantClass::DYNAMIC_INCOMPATIBLE_PHPDOC_CONSTANT); + assertType('array', DynamicConstantClass::DYNAMIC_EMPTY_ARRAY_NO_PHPDOC); + assertType('array', GLOBAL_DYNAMIC_EMPTY_ARRAY); } } diff --git a/tests/PHPStan/Analyser/dynamic-constants.neon b/tests/PHPStan/Analyser/dynamic-constants.neon index 2dfb2167af..324a24b4a8 100644 --- a/tests/PHPStan/Analyser/dynamic-constants.neon +++ b/tests/PHPStan/Analyser/dynamic-constants.neon @@ -7,6 +7,8 @@ parameters: - DynamicConstants\DynamicConstantClass::DYNAMIC_NULL_WITH_PHPDOC_CONSTANT - DynamicConstants\DynamicConstantClass::DYNAMIC_EMPTY_ARRAY_WITH_PHPDOC_CONSTANT - DynamicConstants\DynamicConstantClass::DYNAMIC_INCOMPATIBLE_PHPDOC_CONSTANT + - DynamicConstants\DynamicConstantClass::DYNAMIC_EMPTY_ARRAY_NO_PHPDOC - GLOBAL_DYNAMIC_CONSTANT + - GLOBAL_DYNAMIC_EMPTY_ARRAY DynamicConstants\DynamicConstantClass::DYNAMIC_CONSTANT_WITH_EXPLICIT_TYPES_IN_CLASS: 'string|null' GLOBAL_DYNAMIC_CONSTANT_WITH_EXPLICIT_TYPES: 'string|null'