Skip to content

Commit bb32d03

Browse files
Respect phpstan level in ParameterCastableToNumberRule
1 parent 124af5f commit bb32d03

File tree

4 files changed

+67
-9
lines changed

4 files changed

+67
-9
lines changed

src/Rules/Functions/ParameterCastableToNumberRule.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use PHPStan\Rules\ParameterCastableToStringCheck;
1212
use PHPStan\Rules\Rule;
1313
use PHPStan\Type\ErrorType;
14+
use PHPStan\Type\MixedType;
1415
use PHPStan\Type\Type;
1516
use function count;
1617
use function in_array;
@@ -69,7 +70,7 @@ public function processNode(Node $node, Scope $scope): array
6970

7071
$castFn = $this->phpVersion->supportsObjectsInArraySumProduct()
7172
? static fn (Type $t) => $t->toNumber()
72-
: static fn (Type $t) => !$t->isObject()->no() ? new ErrorType() : $t->toNumber();
73+
: static fn (Type $t) => ($t instanceof MixedType || $t->isObject()->no()) ? $t->toNumber() : new ErrorType();
7374

7475
$error = $this->parameterCastableToStringCheck->checkParameter(
7576
$origArgs[0],

src/Rules/ParameterCastableToStringCheck.php

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use PHPStan\Analyser\ArgumentsNormalizer;
77
use PHPStan\Analyser\Scope;
88
use PHPStan\DependencyInjection\AutowiredService;
9+
use PHPStan\Node\Expr\TypeExpr;
910
use PHPStan\Reflection\ParameterReflection;
1011
use PHPStan\Type\ErrorType;
1112
use PHPStan\Type\Type;
@@ -34,22 +35,31 @@ public function checkParameter(
3435
return null;
3536
}
3637

37-
$typeResult = $this->ruleLevelHelper->findTypeToCheck(
38+
$arrayTypeResult = $this->ruleLevelHelper->findTypeToCheck(
3839
$scope,
3940
$parameter->value,
4041
'',
41-
static fn (Type $type): bool => $type->isArray()->yes() && !$castFn($type->getIterableValueType()) instanceof ErrorType,
42+
static fn (Type $type): bool => $type->isArray()->yes(),
43+
);
44+
45+
$arrayType = $arrayTypeResult->getType();
46+
if (!$arrayType->isArray()->yes()) {
47+
return null;
48+
}
49+
50+
$typeResult = $this->ruleLevelHelper->findTypeToCheck(
51+
$scope,
52+
new TypeExpr($arrayType->getIterableValueType()),
53+
'',
54+
static fn (Type $type): bool => !$castFn($type) instanceof ErrorType,
4255
);
4356

44-
if (
45-
! $typeResult->getType()->isArray()->yes()
46-
|| !$castFn($typeResult->getType()->getIterableValueType()) instanceof ErrorType
47-
) {
57+
if (!$castFn($typeResult->getType()) instanceof ErrorType) {
4858
return null;
4959
}
5060

5161
return RuleErrorBuilder::message(
52-
sprintf($errorMessageTemplate, $parameterName, $functionName, $typeResult->getType()->describe(VerbosityLevel::typeOnly())),
62+
sprintf($errorMessageTemplate, $parameterName, $functionName, $arrayTypeResult->getType()->describe(VerbosityLevel::typeOnly())),
5363
)->identifier('argument.type')->build();
5464
}
5565

tests/PHPStan/Rules/Functions/ParameterCastableToNumberRuleTest.php

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,16 @@
1818
class ParameterCastableToNumberRuleTest extends RuleTestCase
1919
{
2020

21+
private bool $checkUnion = true;
22+
private bool $checkExplicitMixed = true;
23+
private bool $checkImplicitMixed = true;
24+
2125
protected function getRule(): Rule
2226
{
2327
$broker = self::createReflectionProvider();
2428
return new ParameterCastableToNumberRule(
2529
$broker,
26-
new ParameterCastableToStringCheck(new RuleLevelHelper($broker, true, false, true, true, true, false, true)),
30+
new ParameterCastableToStringCheck(new RuleLevelHelper($broker, true, false, $this->checkUnion, $this->checkExplicitMixed, $this->checkImplicitMixed, false, true)),
2731
self::getContainer()->getByType(PhpVersion::class),
2832
);
2933
}
@@ -213,6 +217,39 @@ public function testBug13775Bis(): void
213217
$this->analyse([__DIR__ . '/data/bug-13775-bis.php'], $this->hackPhp74ErrorMessages($errors));
214218
}
215219

220+
public function testBug13775TerWithoutMixed(): void
221+
{
222+
$this->checkExplicitMixed = false;
223+
$this->checkImplicitMixed = false;
224+
225+
$this->analyse([__DIR__ . '/data/bug-13775-ter.php'], $this->hackPhp74ErrorMessages([
226+
[
227+
'Parameter #1 $array of function array_sum expects an array of values castable to number, array<int, int|string> given.',
228+
9,
229+
],
230+
[
231+
'Parameter #1 $array of function array_product expects an array of values castable to number, array<int, int|string> given.',
232+
10,
233+
],
234+
]));
235+
}
236+
237+
public function testBug13775TerWithoutUnion(): void
238+
{
239+
$this->checkUnion = false;
240+
241+
$this->analyse([__DIR__ . '/data/bug-13775-ter.php'], $this->hackPhp74ErrorMessages([
242+
[
243+
'Parameter #1 $array of function array_sum expects an array of values castable to number, array<int, mixed> given.',
244+
5,
245+
],
246+
[
247+
'Parameter #1 $array of function array_product expects an array of values castable to number, array<int, mixed> given.',
248+
6,
249+
],
250+
]));
251+
}
252+
216253
public function testBug12146(): void
217254
{
218255
$this->analyse([__DIR__ . '/data/bug-12146.php'], $this->hackPhp74ErrorMessages([
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php declare(strict_types = 1);
2+
3+
/** @var mixed $a */
4+
$a = doFoo();
5+
echo array_sum([$a]) . "\n\n";
6+
echo array_product([$a]) . "\n\n";
7+
8+
$b = rand(0, 1) ? 42 : '';
9+
echo array_sum([$b]) . "\n\n";
10+
echo array_product([$b]) . "\n\n";

0 commit comments

Comments
 (0)