Skip to content

Commit 82f7963

Browse files
Add VersionCompareFunctionDynamicThrowTypeExtension
1 parent a5aa2ce commit 82f7963

File tree

4 files changed

+86
-1
lines changed

4 files changed

+86
-1
lines changed

src/Type/Php/VersionCompareFunctionDynamicReturnTypeExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
final class VersionCompareFunctionDynamicReturnTypeExtension implements DynamicFunctionReturnTypeExtension
3030
{
3131

32-
private const VALID_OPERATORS = [
32+
public const VALID_OPERATORS = [
3333
'<',
3434
'lt',
3535
'<=',
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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\DependencyInjection\AutowiredService;
8+
use PHPStan\Php\PhpVersion;
9+
use PHPStan\Reflection\FunctionReflection;
10+
use PHPStan\Type\DynamicFunctionThrowTypeExtension;
11+
use PHPStan\Type\Type;
12+
use function count;
13+
use function in_array;
14+
15+
#[AutowiredService]
16+
final class VersionCompareFunctionDynamicThrowTypeExtension implements DynamicFunctionThrowTypeExtension
17+
{
18+
19+
public function __construct(
20+
private PhpVersion $phpVersion,
21+
)
22+
{
23+
}
24+
25+
public function isFunctionSupported(FunctionReflection $functionReflection): bool
26+
{
27+
return $functionReflection->getName() === 'version_compare';
28+
}
29+
30+
public function getThrowTypeFromFunctionCall(
31+
FunctionReflection $functionReflection,
32+
FuncCall $funcCall,
33+
Scope $scope,
34+
): ?Type
35+
{
36+
if (!$this->phpVersion->throwsValueErrorForInternalFunctions()) {
37+
return null;
38+
}
39+
40+
$args = $funcCall->getArgs();
41+
if (!isset($args[2])) {
42+
return null;
43+
}
44+
45+
$operatorStrings = $scope->getType($args[2]->value)->getConstantStrings();
46+
if (count($operatorStrings) === 0) {
47+
return $functionReflection->getThrowType();
48+
}
49+
50+
foreach ($operatorStrings as $operatorString) {
51+
$operatorValue = $operatorString->getValue();
52+
if (!in_array($operatorValue, VersionCompareFunctionDynamicReturnTypeExtension::VALID_OPERATORS, true)) {
53+
return $functionReflection->getThrowType();
54+
}
55+
}
56+
57+
return null;
58+
}
59+
60+
}

tests/PHPStan/Rules/Exceptions/ThrowsVoidFunctionWithExplicitThrowPointRuleTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,11 @@ public function testRule(bool $missingCheckedExceptionInThrows, array $checkedEx
9797
$this->analyse([__DIR__ . '/data/throws-void-function.php'], $errors);
9898
}
9999

100+
public function testBug13515(): void
101+
{
102+
$this->missingCheckedExceptionInThrows = false;
103+
$this->checkedExceptionClasses = [];
104+
$this->analyse([__DIR__ . '/data/bug-13515.php'], []);
105+
}
106+
100107
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Bug13515;
4+
5+
/** @throws void */
6+
function fromArray(string $name, string $versionWithoutMicro) : void
7+
{
8+
if ($name === 'Mac OS X'
9+
&& \version_compare(
10+
$versionWithoutMicro,
11+
'10.12',
12+
'>='
13+
)
14+
) {
15+
$name = 'macOS';
16+
$marketingName = 'macOS';
17+
}
18+
}

0 commit comments

Comments
 (0)