Skip to content

Commit cc855b2

Browse files
committed
Playground - ArrayDimCastRule
1 parent cf0db03 commit cc855b2

File tree

4 files changed

+145
-0
lines changed

4 files changed

+145
-0
lines changed

issue-bot/playground.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
rules:
2+
- PHPStan\Rules\Playground\ArrayDimCastRule
23
- PHPStan\Rules\Playground\FunctionNeverRule
34
- PHPStan\Rules\Playground\LiteralArrayKeyCastRule
45
- PHPStan\Rules\Playground\MethodNeverRule
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Playground;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Expr\ArrayDimFetch;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\Rules\Rule;
9+
use PHPStan\Rules\RuleErrorBuilder;
10+
use PHPStan\Type\GeneralizePrecision;
11+
use PHPStan\Type\VerbosityLevel;
12+
use function sprintf;
13+
14+
/**
15+
* @implements Rule<ArrayDimFetch>
16+
*/
17+
final class ArrayDimCastRule implements Rule
18+
{
19+
20+
public function getNodeType(): string
21+
{
22+
return ArrayDimFetch::class;
23+
}
24+
25+
public function processNode(Node $node, Scope $scope): array
26+
{
27+
if ($node->dim === null) {
28+
return [];
29+
}
30+
31+
$varType = $scope->getType($node->var);
32+
if ($varType->isArray()->no()) {
33+
return [];
34+
}
35+
36+
$dimType = $scope->getType($node->dim);
37+
if (!$dimType->isConstantScalarValue()->yes()) {
38+
return [];
39+
}
40+
41+
$constantScalars = $dimType->getConstantScalarTypes();
42+
$errors = [];
43+
foreach ($constantScalars as $constantScalar) {
44+
$arrayKeyType = $constantScalar->toArrayKey();
45+
if ($arrayKeyType->equals($constantScalar)) {
46+
continue;
47+
}
48+
49+
$errors[] = RuleErrorBuilder::message(sprintf(
50+
'Key %s (%s) will be cast to %s (%s) in the array access.',
51+
$constantScalar->describe(VerbosityLevel::value()),
52+
$constantScalar->generalize(GeneralizePrecision::lessSpecific())->describe(VerbosityLevel::typeOnly()),
53+
$arrayKeyType->describe(VerbosityLevel::value()),
54+
$arrayKeyType->describe(VerbosityLevel::typeOnly()),
55+
))->identifier('phpstanPlayground.arrayDimFetchCast')
56+
->build();
57+
}
58+
59+
return $errors;
60+
}
61+
62+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Playground;
4+
5+
use PHPStan\Rules\Rule;
6+
use PHPStan\Testing\RuleTestCase;
7+
8+
/**
9+
* @extends RuleTestCase<ArrayDimCastRule>
10+
*/
11+
final class ArrayDimCastRuleTest extends RuleTestCase
12+
{
13+
14+
protected function getRule(): Rule
15+
{
16+
return new ArrayDimCastRule();
17+
}
18+
19+
public function testRule(): void
20+
{
21+
$this->analyse([__DIR__ . '/data/array-dim-fetch-cast.php'], [
22+
[
23+
"Key '1' (string) will be cast to 1 (int) in the array access.",
24+
13,
25+
],
26+
[
27+
"Key null (null) will be cast to '' (string) in the array access.",
28+
14,
29+
],
30+
[
31+
'Key 2.5 (float) will be cast to 2 (int) in the array access.',
32+
15,
33+
],
34+
[
35+
'Key true (bool) will be cast to 1 (int) in the array access.',
36+
17,
37+
],
38+
[
39+
'Key false (bool) will be cast to 0 (int) in the array access.',
40+
18,
41+
],
42+
[
43+
"Key '10' (string) will be cast to 10 (int) in the array access.",
44+
20,
45+
],
46+
[
47+
"Key '1' (string) will be cast to 1 (int) in the array access.",
48+
26,
49+
],
50+
]);
51+
}
52+
53+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace ArrayDimFetchCast;
4+
5+
class Foo
6+
{
7+
8+
public function doFoo(array $a): void
9+
{
10+
$partiallyCast = rand(0,1) ? '10' : 10;
11+
echo $a['a'];
12+
echo $a['+1'];
13+
echo $a['1']; // cast to 1
14+
echo $a[null]; // cast to ''
15+
echo $a[2.5]; // cast to 2
16+
echo $a['1.2'];
17+
echo $a[true]; // cast to 1
18+
echo $a[false]; // cast to 0
19+
echo $a['08'];
20+
echo $a[$partiallyCast]; // one part of the union is cast to 10
21+
}
22+
23+
public function doBar($mixed): void
24+
{
25+
echo $mixed['a'];
26+
echo $mixed['1'];
27+
}
28+
29+
}

0 commit comments

Comments
 (0)