1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-27 04:45:20 +01:00

Merge pull request #9795 from robchett/powReturnTypeProvider

Improve return types of pow()
This commit is contained in:
orklah 2023-05-16 20:48:25 +02:00 committed by GitHub
commit 69589ebe83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 136 additions and 0 deletions

View File

@ -36,6 +36,7 @@ use Psalm\Internal\Provider\ReturnTypeProvider\MbInternalEncodingReturnTypeProvi
use Psalm\Internal\Provider\ReturnTypeProvider\MinMaxReturnTypeProvider;
use Psalm\Internal\Provider\ReturnTypeProvider\MktimeReturnTypeProvider;
use Psalm\Internal\Provider\ReturnTypeProvider\ParseUrlReturnTypeProvider;
use Psalm\Internal\Provider\ReturnTypeProvider\PowReturnTypeProvider;
use Psalm\Internal\Provider\ReturnTypeProvider\RandReturnTypeProvider;
use Psalm\Internal\Provider\ReturnTypeProvider\RoundReturnTypeProvider;
use Psalm\Internal\Provider\ReturnTypeProvider\StrReplaceReturnTypeProvider;
@ -103,6 +104,7 @@ class FunctionReturnTypeProvider
$this->registerClass(RoundReturnTypeProvider::class);
$this->registerClass(MbInternalEncodingReturnTypeProvider::class);
$this->registerClass(DateReturnTypeProvider::class);
$this->registerClass(PowReturnTypeProvider::class);
}
/**

View File

@ -0,0 +1,92 @@
<?php
namespace Psalm\Internal\Provider\ReturnTypeProvider;
use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
use Psalm\Type;
use Psalm\Type\Atomic\TFloat;
use Psalm\Type\Atomic\TInt;
use Psalm\Type\Atomic\TLiteralFloat;
use Psalm\Type\Atomic\TLiteralInt;
use Psalm\Type\Union;
use function count;
/**
* @internal
*/
class PowReturnTypeProvider implements FunctionReturnTypeProviderInterface
{
/**
* @return array<lowercase-string>
*/
public static function getFunctionIds(): array
{
return ['pow'];
}
public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): ?Union
{
$call_args = $event->getCallArgs();
if (count($call_args) !== 2) {
return null;
}
$first_arg = $event->getStatementsSource()->getNodeTypeProvider()->getType($call_args[0]->value);
$second_arg = $event->getStatementsSource()->getNodeTypeProvider()->getType($call_args[1]->value);
$first_arg_literal = null;
$first_arg_is_int = false;
$first_arg_is_float = false;
if ($first_arg !== null && $first_arg->isSingle()) {
$first_atomic_type = $first_arg->getSingleAtomic();
if ($first_atomic_type instanceof TInt) {
$first_arg_is_int = true;
} elseif ($first_atomic_type instanceof TFloat) {
$first_arg_is_float = true;
}
if ($first_atomic_type instanceof TLiteralInt
|| $first_atomic_type instanceof TLiteralFloat
) {
$first_arg_literal = $first_atomic_type->value;
}
}
$second_arg_literal = null;
$second_arg_is_int = false;
$second_arg_is_float = false;
if ($second_arg !== null && $second_arg->isSingle()) {
$second_atomic_type = $second_arg->getSingleAtomic();
if ($second_atomic_type instanceof TInt) {
$second_arg_is_int = true;
} elseif ($second_atomic_type instanceof TFloat) {
$second_arg_is_float = true;
}
if ($second_atomic_type instanceof TLiteralInt
|| $second_atomic_type instanceof TLiteralFloat
) {
$second_arg_literal = $second_atomic_type->value;
}
}
if ($first_arg_literal === 0) {
return Type::getInt(true, 0);
}
if ($second_arg_literal === 0) {
return Type::getInt(true, 1);
}
if ($first_arg_literal !== null && $second_arg_literal !== null) {
return Type::getFloat($first_arg_literal ** $second_arg_literal);
}
if ($first_arg_is_int && $second_arg_is_int) {
return Type::getInt();
}
if ($first_arg_is_float || $second_arg_is_float) {
return Type::getFloat();
}
return new Union([new TInt(), new TFloat()]);
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace Psalm\Tests\ReturnTypeProvider;
use Psalm\Tests\TestCase;
use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait;
class PowReturnTypeProviderTest extends TestCase
{
use ValidCodeAnalysisTestTrait;
public function providerValidCodeParse(): iterable
{
yield 'test' => [
'code' => '<?php
function getInt(): int {
return 1;
}
function getFloat(): float {
return 1.0;
}
$int = getInt();
$float = getFloat();
$a = pow($int, $int);
$b = pow($int, $float);
$c = pow($float, $int);
$d = pow(1000, 1000);
$e = pow(0, 1000);
$f = pow(1000, 0);
',
'assertions' => [
'$a===' => 'int',
'$b===' => 'float',
'$c===' => 'float',
'$d===' => 'float(INF)',
'$e===' => '0',
'$f===' => '1',
],
];
}
}