44
55use PhpParser \Node \Expr \FuncCall ;
66use PHPStan \Analyser \Scope ;
7+ use PHPStan \Php \PhpVersion ;
78use PHPStan \Reflection \FunctionReflection ;
89use PHPStan \Reflection \ParametersAcceptorSelector ;
910use PHPStan \Type \Accessory \NonEmptyArrayType ;
1011use PHPStan \Type \ArrayType ;
1112use PHPStan \Type \Constant \ConstantArrayTypeBuilder ;
13+ use PHPStan \Type \Constant \ConstantBooleanType ;
1214use PHPStan \Type \Constant \ConstantIntegerType ;
15+ use PHPStan \Type \IntegerRangeType ;
1316use PHPStan \Type \IntegerType ;
1417use PHPStan \Type \IntersectionType ;
18+ use PHPStan \Type \NeverType ;
1519use PHPStan \Type \Type ;
20+ use PHPStan \Type \TypeCombinator ;
1621
1722class ArrayFillFunctionReturnTypeExtension implements \PHPStan \Type \DynamicFunctionReturnTypeExtension
1823{
1924
2025 private const MAX_SIZE_USE_CONSTANT_ARRAY = 100 ;
2126
27+ private PhpVersion $ phpVersion ;
28+
29+ public function __construct (PhpVersion $ phpVersion )
30+ {
31+ $ this ->phpVersion = $ phpVersion ;
32+ }
33+
2234 public function isFunctionSupported (FunctionReflection $ functionReflection ): bool
2335 {
2436 return $ functionReflection ->getName () === 'array_fill ' ;
@@ -34,6 +46,23 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
3446 $ numberType = $ scope ->getType ($ functionCall ->args [1 ]->value );
3547 $ valueType = $ scope ->getType ($ functionCall ->args [2 ]->value );
3648
49+ if ($ numberType instanceof IntegerRangeType) {
50+ if ($ numberType ->getMin () < 0 ) {
51+ return TypeCombinator::union (
52+ new ArrayType (new IntegerType (), $ valueType ),
53+ new ConstantBooleanType (false )
54+ );
55+ }
56+ }
57+
58+ // check against negative-int, which is not allowed
59+ if (IntegerRangeType::fromInterval (null , -1 )->isSuperTypeOf ($ numberType )->yes ()) {
60+ if ($ this ->phpVersion ->throwsValueErrorForInternalFunctions ()) {
61+ return new NeverType ();
62+ }
63+ return new ConstantBooleanType (false );
64+ }
65+
3766 if (
3867 $ startIndexType instanceof ConstantIntegerType
3968 && $ numberType instanceof ConstantIntegerType
@@ -56,10 +85,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
5685 return $ arrayBuilder ->getArray ();
5786 }
5887
59- if (
60- $ numberType instanceof ConstantIntegerType
61- && $ numberType ->getValue () > 0
62- ) {
88+ if (IntegerRangeType::fromInterval (1 , null )->isSuperTypeOf ($ numberType )->yes ()) {
6389 return new IntersectionType ([
6490 new ArrayType (new IntegerType (), $ valueType ),
6591 new NonEmptyArrayType (),
0 commit comments