|
38 | 38 | use PhpParser\Node\Expr\Variable;
|
39 | 39 | use PhpParser\Node\Identifier;
|
40 | 40 | use PhpParser\Node\Name;
|
| 41 | +use PhpParser\Node\Scalar\Int_; |
41 | 42 | use PhpParser\Node\Stmt\Break_;
|
42 | 43 | use PhpParser\Node\Stmt\Class_;
|
43 | 44 | use PhpParser\Node\Stmt\Continue_;
|
|
174 | 175 | use PHPStan\Type\Generic\TemplateTypeMap;
|
175 | 176 | use PHPStan\Type\Generic\TemplateTypeVariance;
|
176 | 177 | use PHPStan\Type\Generic\TemplateTypeVarianceMap;
|
| 178 | +use PHPStan\Type\IntegerRangeType; |
177 | 179 | use PHPStan\Type\IntegerType;
|
178 | 180 | use PHPStan\Type\IntersectionType;
|
179 | 181 | use PHPStan\Type\MixedType;
|
@@ -2628,29 +2630,72 @@ static function (): void {
|
2628 | 2630 | && in_array($functionReflection->getName(), ['array_pop', 'array_shift'], true)
|
2629 | 2631 | && count($expr->getArgs()) >= 1
|
2630 | 2632 | ) {
|
2631 |
| - $arrayArg = $expr->getArgs()[0]->value; |
| 2633 | + $args = $expr->getArgs(); |
| 2634 | + $arrayArg = $args[0]->value; |
2632 | 2635 |
|
2633 | 2636 | $arrayArgType = $scope->getType($arrayArg);
|
2634 | 2637 | $arrayArgNativeType = $scope->getNativeType($arrayArg);
|
2635 | 2638 |
|
| 2639 | + $countArrayExpr = new FuncCall(new Name('count'), [$args[0]]); |
| 2640 | + $hasCountExpr = $scope->hasExpressionType($countArrayExpr)->yes(); |
| 2641 | + if ($hasCountExpr) { |
| 2642 | + $countType = $scope->getType(new BinaryOp\Minus($countArrayExpr, new Int_(1))); |
| 2643 | + $countNativeType = $scope->getType(new BinaryOp\Minus($countArrayExpr, new Int_(1))); |
| 2644 | + } |
| 2645 | + |
2636 | 2646 | $isArrayPop = $functionReflection->getName() === 'array_pop';
|
2637 | 2647 | $scope = $scope->invalidateExpression($arrayArg)->assignExpression(
|
2638 | 2648 | $arrayArg,
|
2639 | 2649 | $isArrayPop ? $arrayArgType->popArray() : $arrayArgType->shiftArray(),
|
2640 | 2650 | $isArrayPop ? $arrayArgNativeType->popArray() : $arrayArgNativeType->shiftArray(),
|
2641 | 2651 | );
|
| 2652 | + |
| 2653 | + if ( |
| 2654 | + $hasCountExpr |
| 2655 | + && IntegerRangeType::fromInterval(0, null)->isSuperTypeOf($countType)->yes() |
| 2656 | + && IntegerRangeType::fromInterval(0, null)->isSuperTypeOf($countNativeType)->yes() |
| 2657 | + ) { |
| 2658 | + $scope = $scope->assignExpression($countArrayExpr, $countType, $countNativeType); |
| 2659 | + } |
2642 | 2660 | }
|
2643 | 2661 |
|
2644 | 2662 | if (
|
2645 | 2663 | $functionReflection !== null
|
2646 | 2664 | && in_array($functionReflection->getName(), ['array_push', 'array_unshift'], true)
|
2647 | 2665 | && count($expr->getArgs()) >= 2
|
2648 | 2666 | ) {
|
| 2667 | + $args = $expr->getArgs(); |
2649 | 2668 | $arrayType = $this->getArrayFunctionAppendingType($functionReflection, $scope, $expr);
|
2650 | 2669 | $arrayNativeType = $this->getArrayFunctionAppendingType($functionReflection, $scope->doNotTreatPhpDocTypesAsCertain(), $expr);
|
2651 | 2670 |
|
2652 |
| - $arrayArg = $expr->getArgs()[0]->value; |
| 2671 | + $arrayArg = $args[0]->value; |
| 2672 | + $addedElementsCount = count($args) - 1; |
| 2673 | + for ($i = 1; $i < count($args); $i++) { |
| 2674 | + if ($args[$i]->unpack) { |
| 2675 | + $addedElementsCount = null; |
| 2676 | + break; |
| 2677 | + } |
| 2678 | + } |
| 2679 | + |
| 2680 | + $countArrayExpr = new FuncCall(new Name('count'), [$args[0]]); |
| 2681 | + $hasCountExpr = $scope->hasExpressionType($countArrayExpr)->yes(); |
| 2682 | + if ($hasCountExpr && $addedElementsCount !== null) { |
| 2683 | + $countType = $scope->getType(new BinaryOp\Plus($countArrayExpr, new Int_($addedElementsCount))); |
| 2684 | + $countNativeType = $scope->getType(new BinaryOp\Plus($countArrayExpr, new Int_($addedElementsCount))); |
| 2685 | + } else { |
| 2686 | + $countType = IntegerRangeType::fromInterval($addedElementsCount, null); |
| 2687 | + $countNativeType = IntegerRangeType::fromInterval($addedElementsCount, null); |
| 2688 | + } |
| 2689 | + |
2653 | 2690 | $scope = $scope->invalidateExpression($arrayArg)->assignExpression($arrayArg, $arrayType, $arrayNativeType);
|
| 2691 | + |
| 2692 | + if ( |
| 2693 | + IntegerRangeType::fromInterval(0, null)->isSuperTypeOf($countType)->yes() |
| 2694 | + && IntegerRangeType::fromInterval(0, null)->isSuperTypeOf($countNativeType)->yes() |
| 2695 | + ) { |
| 2696 | + $scope = $scope->assignExpression($countArrayExpr, $countType, $countNativeType); |
| 2697 | + } |
| 2698 | + |
2654 | 2699 | }
|
2655 | 2700 |
|
2656 | 2701 | if (
|
|
0 commit comments