Skip to content

Commit c54e495

Browse files
committed
Dynamic return type extension for pow()
1 parent f0b80bc commit c54e495

File tree

5 files changed

+84
-4
lines changed

5 files changed

+84
-4
lines changed

conf/config.neon

+5
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,11 @@ services:
976976
tags:
977977
- phpstan.broker.dynamicFunctionReturnTypeExtension
978978

979+
-
980+
class: PHPStan\Type\Php\PowFunctionReturnTypeExtension
981+
tags:
982+
- phpstan.broker.dynamicFunctionReturnTypeExtension
983+
979984
-
980985
class: PHPStan\Type\Php\StrtotimeFunctionReturnTypeExtension
981986
tags:

src/Reflection/SignatureMap/NativeFunctionReflectionProvider.php

-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
use PHPStan\Type\IntegerType;
1313
use PHPStan\Type\NullType;
1414
use PHPStan\Type\StringAlwaysAcceptingObjectWithToStringType;
15-
use PHPStan\Type\TypeUtils;
1615
use PHPStan\Type\UnionType;
1716

1817
class NativeFunctionReflectionProvider
@@ -44,9 +43,6 @@ public function findFunctionReflection(string $functionName): ?NativeFunctionRef
4443
while ($this->signatureMapProvider->hasFunctionSignature($lowerCasedFunctionName, $i)) {
4544
$functionSignature = $this->signatureMapProvider->getFunctionSignature($lowerCasedFunctionName, null, $i);
4645
$returnType = $functionSignature->getReturnType();
47-
if ($lowerCasedFunctionName === 'pow') {
48-
$returnType = TypeUtils::toBenevolentUnion($returnType);
49-
}
5046
$variants[] = new FunctionVariant(
5147
TemplateTypeMap::createEmpty(),
5248
null,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Php;
4+
5+
use PhpParser\Node\Expr\FuncCall;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Reflection\FunctionReflection;
8+
use PHPStan\Type\BenevolentUnionType;
9+
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
10+
use PHPStan\Type\FloatType;
11+
use PHPStan\Type\IntegerType;
12+
use PHPStan\Type\MixedType;
13+
use PHPStan\Type\ObjectWithoutClassType;
14+
use PHPStan\Type\Type;
15+
use PHPStan\Type\TypeCombinator;
16+
17+
class PowFunctionReturnTypeExtension implements DynamicFunctionReturnTypeExtension
18+
{
19+
20+
public function isFunctionSupported(FunctionReflection $functionReflection): bool
21+
{
22+
return $functionReflection->getName() === 'pow';
23+
}
24+
25+
public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): Type
26+
{
27+
$defaultReturnType = new BenevolentUnionType([
28+
new FloatType(),
29+
new IntegerType(),
30+
]);
31+
if (count($functionCall->args) < 2) {
32+
return $defaultReturnType;
33+
}
34+
35+
$firstArgType = $scope->getType($functionCall->args[0]->value);
36+
$secondArgType = $scope->getType($functionCall->args[1]->value);
37+
if ($firstArgType instanceof MixedType || $secondArgType instanceof MixedType) {
38+
return $defaultReturnType;
39+
}
40+
41+
$object = new ObjectWithoutClassType();
42+
if (
43+
!$object->isSuperTypeOf($firstArgType)->no()
44+
|| !$object->isSuperTypeOf($secondArgType)->no()
45+
) {
46+
return TypeCombinator::union($firstArgType, $secondArgType);
47+
}
48+
49+
return $defaultReturnType;
50+
}
51+
52+
}

tests/PHPStan/Analyser/NodeScopeResolverTest.php

+6
Original file line numberDiff line numberDiff line change
@@ -10141,6 +10141,11 @@ public function dataBugFromPr339(): array
1014110141
return $this->gatherAssertTypes(__DIR__ . '/data/bug-pr-339.php');
1014210142
}
1014310143

10144+
public function dataPow(): array
10145+
{
10146+
return $this->gatherAssertTypes(__DIR__ . '/data/pow.php');
10147+
}
10148+
1014410149
/**
1014510150
* @dataProvider dataBug2574
1014610151
* @dataProvider dataBug2577
@@ -10217,6 +10222,7 @@ public function dataBugFromPr339(): array
1021710222
* @dataProvider dataBug3866
1021810223
* @dataProvider dataBug1014
1021910224
* @dataProvider dataBugFromPr339
10225+
* @dataProvider dataPow
1022010226
* @param string $assertType
1022110227
* @param string $file
1022210228
* @param mixed ...$args

tests/PHPStan/Analyser/data/pow.php

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace PowFunction;
4+
5+
use function PHPStan\Analyser\assertType;
6+
7+
function ($a, $b): void {
8+
assertType('(float|int)', pow($a, $b));
9+
};
10+
11+
function (int $a, int $b): void {
12+
assertType('(float|int)', pow($a, $b));
13+
};
14+
15+
function (\GMP $a, \GMP $b): void {
16+
assertType('GMP', pow($a, $b));
17+
};
18+
19+
function (\stdClass $a, \GMP $b): void {
20+
assertType('GMP|stdClass', pow($a, $b));
21+
};

0 commit comments

Comments
 (0)