Skip to content

Commit 43cf87f

Browse files
committed
#6840 Report useless array filter
1 parent 492dede commit 43cf87f

File tree

4 files changed

+132
-0
lines changed

4 files changed

+132
-0
lines changed

conf/config.level5.neon

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ parameters:
88
rules:
99
- PHPStan\Rules\DateTimeInstantiationRule
1010
- PHPStan\Rules\Functions\ImplodeFunctionRule
11+
- PHPStan\Rules\Functions\ArrayFilterEmptyRule
1112

1213
services:
1314
-
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Functions;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Expr\FuncCall;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\Reflection\ReflectionProvider;
9+
use PHPStan\Rules\Rule;
10+
use PHPStan\Rules\RuleErrorBuilder;
11+
use PHPStan\Type\StaticTypeFactory;
12+
use PHPStan\Type\VerbosityLevel;
13+
use function count;
14+
use function sprintf;
15+
use function strtolower;
16+
17+
/**
18+
* @implements Rule<Node\Expr\FuncCall>
19+
*/
20+
class ArrayFilterEmptyRule implements Rule
21+
{
22+
23+
public function __construct(private ReflectionProvider $reflectionProvider)
24+
{
25+
}
26+
27+
public function getNodeType(): string
28+
{
29+
return FuncCall::class;
30+
}
31+
32+
public function processNode(Node $node, Scope $scope): array
33+
{
34+
if (!($node->name instanceof Node\Name)) {
35+
return [];
36+
}
37+
38+
if (strtolower($this->reflectionProvider->resolveFunctionName($node->name, $scope)) !== 'array_filter') {
39+
return [];
40+
}
41+
42+
$args = $node->getArgs();
43+
if (count($args) !== 1) {
44+
return [];
45+
}
46+
47+
$arrayType = $scope->getType($args[0]->value);
48+
$falsyType = StaticTypeFactory::falsey();
49+
50+
if ($falsyType->isSuperTypeOf($arrayType->getIterableValueType())->no()) {
51+
$message = 'Parameter #1 $array (%s) to function array_filter cannot contain empty values, call has no effect.';
52+
return [
53+
RuleErrorBuilder::message(sprintf(
54+
$message,
55+
$arrayType->describe(VerbosityLevel::value()),
56+
))->build(),
57+
];
58+
}
59+
60+
return [];
61+
}
62+
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Functions;
4+
5+
use PHPStan\Rules\Rule;
6+
use PHPStan\Testing\RuleTestCase;
7+
8+
/**
9+
* @extends RuleTestCase<ArrayFilterEmptyRule>
10+
*/
11+
class ArrayFilterEmptyRuleTest extends RuleTestCase
12+
{
13+
14+
protected function getRule(): Rule
15+
{
16+
return new ArrayFilterEmptyRule($this->createReflectionProvider());
17+
}
18+
19+
public function testFile(): void
20+
{
21+
$expectedErrors = [
22+
[
23+
'Parameter #1 $array (array{1, 3}) to function array_filter cannot contain empty values, call has no effect.',
24+
10,
25+
],
26+
[
27+
'Parameter #1 $array (array{\'test\'}) to function array_filter cannot contain empty values, call has no effect.',
28+
11,
29+
],
30+
[
31+
'Parameter #1 $array (array{true, true}) to function array_filter cannot contain empty values, call has no effect.',
32+
17,
33+
],
34+
[
35+
'Parameter #1 $array (array{stdClass}) to function array_filter cannot contain empty values, call has no effect.',
36+
18,
37+
],
38+
[
39+
'Parameter #1 $array (array<stdClass>) to function array_filter cannot contain empty values, call has no effect.',
40+
20,
41+
],
42+
];
43+
44+
$this->analyse([__DIR__ . '/data/array_filter_empty.php'], $expectedErrors);
45+
}
46+
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
/** @var \stdClass[] $objects */
4+
$objects = [];
5+
/** @var array<\stdClass|null> $objectsOrNull */
6+
$objectsOrNull = [];
7+
8+
array_filter([0]);
9+
array_filter([0,1,3]);
10+
array_filter([1,3]);
11+
array_filter(['test']);
12+
array_filter(['', 'test']);
13+
array_filter([null]);
14+
array_filter([null, 'test']);
15+
array_filter([false, 'test']);
16+
array_filter([true, false]);
17+
array_filter([true, true]);
18+
array_filter([new \stdClass()]);
19+
array_filter([new \stdClass(), null]);
20+
array_filter($objects);
21+
array_filter($objectsOrNull);

0 commit comments

Comments
 (0)