Skip to content

Commit

Permalink
Fix checking empty constant arrays in array function return type exte…
Browse files Browse the repository at this point in the history
…nsions
  • Loading branch information
Richard van Velzen authored and ondrejmirtes committed Sep 5, 2022
1 parent 4228b5a commit f129659
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 10 deletions.
5 changes: 4 additions & 1 deletion src/Type/Php/ArrayKeyFirstDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,11 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
if (count($constantArrays) > 0) {
$keyTypes = [];
foreach ($constantArrays as $constantArray) {
if ($constantArray->isEmpty()) {
$iterableAtLeastOnce = $constantArray->isIterableAtLeastOnce();
if (!$iterableAtLeastOnce->yes()) {
$keyTypes[] = new NullType();
}
if ($iterableAtLeastOnce->no()) {
continue;
}

Expand Down
5 changes: 4 additions & 1 deletion src/Type/Php/ArrayKeyLastDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,11 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
if (count($constantArrays) > 0) {
$keyTypes = [];
foreach ($constantArrays as $constantArray) {
if ($constantArray->isEmpty()) {
$iterableAtLeastOnce = $constantArray->isIterableAtLeastOnce();
if (!$iterableAtLeastOnce->yes()) {
$keyTypes[] = new NullType();
}
if ($iterableAtLeastOnce->no()) {
continue;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,11 @@ public function getTypeFromFunctionCall(
if (count($constantArrays) > 0) {
$keyTypes = [];
foreach ($constantArrays as $constantArray) {
if ($constantArray->isEmpty()) {
$iterableAtLeastOnce = $argType->isIterableAtLeastOnce();
if (!$iterableAtLeastOnce->yes()) {
$keyTypes[] = new ConstantBooleanType(false);
}
if ($iterableAtLeastOnce->no()) {
continue;
}

Expand Down
15 changes: 8 additions & 7 deletions src/Type/Php/ArrayReduceFunctionReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\TrinaryLogic;
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
use PHPStan\Type\NullType;
use PHPStan\Type\Type;
Expand Down Expand Up @@ -47,18 +48,18 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
$arraysType = $scope->getType($functionCall->getArgs()[0]->value);
$constantArrays = TypeUtils::getOldConstantArrays($arraysType);
if (count($constantArrays) > 0) {
$onlyEmpty = true;
$onlyNonEmpty = true;
$onlyEmpty = TrinaryLogic::createYes();
$onlyNonEmpty = TrinaryLogic::createYes();
foreach ($constantArrays as $constantArray) {
$isEmpty = $constantArray->isEmpty();
$onlyEmpty = $onlyEmpty && $isEmpty;
$onlyNonEmpty = $onlyNonEmpty && !$isEmpty;
$iterableAtLeastOnce = $constantArray->isIterableAtLeastOnce();
$onlyEmpty = $onlyEmpty->and($iterableAtLeastOnce->negate());
$onlyNonEmpty = $onlyNonEmpty->and($iterableAtLeastOnce);
}

if ($onlyEmpty) {
if ($onlyEmpty->yes()) {
return $initialType;
}
if ($onlyNonEmpty) {
if ($onlyNonEmpty->yes()) {
return $callbackReturnType;
}
}
Expand Down
1 change: 1 addition & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,7 @@ public function dataFileAsserts(): iterable
}

yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7921.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7928.php');
}

/**
Expand Down
29 changes: 29 additions & 0 deletions tests/PHPStan/Analyser/data/bug-7928.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php declare(strict_types = 1);

namespace Bug7928;

use function PHPStan\Testing\assertType;

function findEntry(): ?string
{
$array = [
randomEntry(),
randomEntry(),
];
$array = array_filter($array);
$first = reset($array);
assertType('non-falsy-string|false', $first);
if ($first === false) {
return null;
}
return $first;
};

function randomEntry(): ?string
{
$rand = mt_rand(0, 1);
if ($rand === 1) {
return 'foo';
}
return null;
}

0 comments on commit f129659

Please sign in to comment.