Skip to content

Commit

Permalink
Fix infinite loop when fully qualified callback is supplied
Browse files Browse the repository at this point in the history
This seemed to only cause a problem when running the code on PHP 7
and where the same file contained a native call to the same callback
function.

Example:

  <?php
  \array_filter([1], '\\is_int');
  \is_int(1);
  • Loading branch information
cs278 authored and ondrejmirtes committed Dec 1, 2022
1 parent 64c1ea2 commit 94ca7b2
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 3 deletions.
16 changes: 13 additions & 3 deletions src/Type/Php/ArrayFilterFunctionReturnTypeReturnTypeExtension.php
Expand Up @@ -33,6 +33,7 @@
use function count;
use function is_string;
use function strtolower;
use function substr;

class ArrayFilterFunctionReturnTypeReturnTypeExtension implements DynamicFunctionReturnTypeExtension
{
Expand Down Expand Up @@ -83,7 +84,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
return $this->filterByTruthyValue($scope, $callbackArg->params[0]->var, $arrayArgType, null, $callbackArg->expr);
} elseif ($callbackArg instanceof String_) {
$itemVar = new Variable('item');
$expr = new FuncCall(new Name($callbackArg->value), [new Arg($itemVar)]);
$expr = new FuncCall(self::createFunctionName($callbackArg->value), [new Arg($itemVar)]);
return $this->filterByTruthyValue($scope, $itemVar, $arrayArgType, null, $expr);
}
}
Expand All @@ -98,7 +99,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
return $this->filterByTruthyValue($scope, null, $arrayArgType, $callbackArg->params[0]->var, $callbackArg->expr);
} elseif ($callbackArg instanceof String_) {
$keyVar = new Variable('key');
$expr = new FuncCall(new Name($callbackArg->value), [new Arg($keyVar)]);
$expr = new FuncCall(self::createFunctionName($callbackArg->value), [new Arg($keyVar)]);
return $this->filterByTruthyValue($scope, null, $arrayArgType, $keyVar, $expr);
}
}
Expand All @@ -114,7 +115,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
} elseif ($callbackArg instanceof String_) {
$itemVar = new Variable('item');
$keyVar = new Variable('key');
$expr = new FuncCall(new Name($callbackArg->value), [new Arg($itemVar), new Arg($keyVar)]);
$expr = new FuncCall(self::createFunctionName($callbackArg->value), [new Arg($itemVar), new Arg($keyVar)]);
return $this->filterByTruthyValue($scope, $itemVar, $arrayArgType, $keyVar, $expr);
}
}
Expand Down Expand Up @@ -234,4 +235,13 @@ private function processKeyAndItemType(MutatingScope $scope, Type $keyType, Type
];
}

private static function createFunctionName(string $funcName): Name
{
if ($funcName[0] === '\\') {
return new Name\FullyQualified(substr($funcName, 1));
}

return new Name($funcName);
}

}
6 changes: 6 additions & 0 deletions tests/PHPStan/Analyser/AnalyserIntegrationTest.php
Expand Up @@ -1025,6 +1025,12 @@ public function testBug7110(): void
$this->assertSame(30, $errors[0]->getLine());
}

public function testBug8376(): void
{
$errors = $this->runAnalyse(__DIR__ . '/data/bug-8376.php');
$this->assertNoErrors($errors);
}

public function testAssertDocblock(): void
{
$errors = $this->runAnalyse(__DIR__ . '/data/assert-docblock.php');
Expand Down
11 changes: 11 additions & 0 deletions tests/PHPStan/Analyser/data/bug-8376.php
@@ -0,0 +1,11 @@
<?php declare(strict_types=1);

namespace Bug8376;

function (array $arr): array {
return array_filter($arr, '\\is_int');
};

function ($var): void {
\assert(\is_int($var));
};

0 comments on commit 94ca7b2

Please sign in to comment.