diff --git a/src/Type/Php/ArrayPadDynamicReturnTypeExtension.php b/src/Type/Php/ArrayPadDynamicReturnTypeExtension.php new file mode 100644 index 0000000000..896034f32f --- /dev/null +++ b/src/Type/Php/ArrayPadDynamicReturnTypeExtension.php @@ -0,0 +1,56 @@ +getName() === 'array_pad'; + } + + public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): ?Type + { + if (!isset($functionCall->getArgs()[2])) { + return null; + } + + $arrayType = $scope->getType($functionCall->getArgs()[0]->value); + $itemType = $scope->getType($functionCall->getArgs()[2]->value); + + $returnType = new ArrayType( + TypeCombinator::union($arrayType->getIterableKeyType(), new IntegerType()), + TypeCombinator::union($arrayType->getIterableValueType(), $itemType), + ); + + $lengthType = $scope->getType($functionCall->getArgs()[1]->value); + if ( + $arrayType->isIterableAtLeastOnce()->yes() + || $lengthType->isSuperTypeOf(new ConstantIntegerType(0))->no() + ) { + $returnType = TypeCombinator::intersect($returnType, new NonEmptyArrayType()); + } + + if ($arrayType->isList()->yes()) { + $returnType = TypeCombinator::intersect($returnType, new AccessoryArrayListType()); + } + + return $returnType; + } + +} diff --git a/tests/PHPStan/Analyser/nsrt/array_pad.php b/tests/PHPStan/Analyser/nsrt/array_pad.php new file mode 100644 index 0000000000..5b28a64458 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/array_pad.php @@ -0,0 +1,78 @@ + $arrayString + * @param array $arrayInt + * @param non-empty-array $nonEmptyArrayString + * @param non-empty-array $nonEmptyArrayInt + * @param list $listString + * @param list $listInt + * @param non-empty-list $nonEmptyListString + * @param non-empty-list $nonEmptyListInt + * @param int $int + * @param positive-int $positiveInt + * @param negative-int $negativeInt + * @param positive-int|negative-int $nonZero + */ + public function test( + $arrayString, + $arrayInt, + $nonEmptyArrayString, + $nonEmptyArrayInt, + $listString, + $listInt, + $nonEmptyListString, + $nonEmptyListInt, + $int, + $positiveInt, + $negativeInt, + $nonZero, + ): void + { + assertType('array', array_pad($arrayString, $int, 'foo')); + assertType('non-empty-array', array_pad($arrayString, $positiveInt, 'foo')); + assertType('non-empty-array', array_pad($arrayString, $negativeInt, 'foo')); + assertType('non-empty-array', array_pad($arrayString, $nonZero, 'foo')); + + assertType('array', array_pad($arrayInt, $int, 'foo')); + assertType('non-empty-array', array_pad($arrayInt, $positiveInt, 'foo')); + assertType('non-empty-array', array_pad($arrayInt, $negativeInt, 'foo')); + assertType('non-empty-array', array_pad($arrayInt, $nonZero, 'foo')); + + assertType('non-empty-array', array_pad($nonEmptyArrayString, $int, 'foo')); + assertType('non-empty-array', array_pad($nonEmptyArrayString, $positiveInt, 'foo')); + assertType('non-empty-array', array_pad($nonEmptyArrayString, $negativeInt, 'foo')); + assertType('non-empty-array', array_pad($nonEmptyArrayString, $nonZero, 'foo')); + + assertType('non-empty-array', array_pad($nonEmptyArrayInt, $int, 'foo')); + assertType('non-empty-array', array_pad($nonEmptyArrayInt, $positiveInt, 'foo')); + assertType('non-empty-array', array_pad($nonEmptyArrayInt, $negativeInt, 'foo')); + assertType('non-empty-array', array_pad($nonEmptyArrayInt, $nonZero, 'foo')); + + assertType('list', array_pad($listString, $int, 'foo')); + assertType('non-empty-list', array_pad($listString, $positiveInt, 'foo')); + assertType('non-empty-list', array_pad($listString, $negativeInt, 'foo')); + assertType('non-empty-list', array_pad($listString, $nonZero, 'foo')); + + assertType('list<\'foo\'|int>', array_pad($listInt, $int, 'foo')); + assertType('non-empty-list<\'foo\'|int>', array_pad($listInt, $positiveInt, 'foo')); + assertType('non-empty-list<\'foo\'|int>', array_pad($listInt, $negativeInt, 'foo')); + assertType('non-empty-list<\'foo\'|int>', array_pad($listInt, $nonZero, 'foo')); + + assertType('non-empty-list', array_pad($nonEmptyListString, $int, 'foo')); + assertType('non-empty-list', array_pad($nonEmptyListString, $positiveInt, 'foo')); + assertType('non-empty-list', array_pad($nonEmptyListString, $negativeInt, 'foo')); + assertType('non-empty-list', array_pad($nonEmptyListString, $nonZero, 'foo')); + + assertType('non-empty-list<\'foo\'|int>', array_pad($nonEmptyListInt, $int, 'foo')); + assertType('non-empty-list<\'foo\'|int>', array_pad($nonEmptyListInt, $positiveInt, 'foo')); + assertType('non-empty-list<\'foo\'|int>', array_pad($nonEmptyListInt, $negativeInt, 'foo')); + assertType('non-empty-list<\'foo\'|int>', array_pad($nonEmptyListInt, $nonZero, 'foo')); + } +}