Skip to content

Commit b6f11fe

Browse files
committed
Report wrongly typed generic arrays
1 parent aafaf8f commit b6f11fe

File tree

3 files changed

+58
-14
lines changed

3 files changed

+58
-14
lines changed

src/Rules/PHPUnit/DataProviderDataRule.php

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
use PHPStan\Analyser\Scope;
77
use PHPStan\Node\Expr\TypeExpr;
88
use PHPStan\Rules\Rule;
9-
use PHPStan\Type\Constant\ConstantArrayType;
109
use PHPStan\Type\ObjectType;
10+
use PHPStan\Type\Type;
1111
use PHPUnit\Framework\TestCase;
1212
use function array_slice;
1313
use function count;
@@ -47,7 +47,7 @@ public function processNode(Node $node, Scope $scope): array
4747
return [];
4848
}
4949

50-
$constantArrays = [];
50+
$arraysTypes = [];
5151
if ($node instanceof Node\Stmt\Return_ || $node instanceof Node\Expr\YieldFrom) {
5252
if ($node->expr === null) {
5353
return [];
@@ -58,24 +58,28 @@ public function processNode(Node $node, Scope $scope): array
5858
foreach ($exprConstArrays as $constArray) {
5959
foreach ($constArray->getValueTypes() as $valueType) {
6060
foreach ($valueType->getConstantArrays() as $constValueArray) {
61-
$constantArrays[] = $constValueArray;
61+
$arraysTypes[] = $constValueArray;
6262
}
6363
}
6464
}
6565

66-
if ($constantArrays === []) {
67-
$constantArrays = $exprType->getIterableValueType()->getConstantArrays();
66+
if ($arraysTypes === []) {
67+
$arraysTypes = $exprType->getIterableValueType()->getConstantArrays();
68+
}
69+
70+
if ($arraysTypes === []) {
71+
$arraysTypes = $exprType->getIterableValueType()->getArrays();
6872
}
6973
} elseif ($node instanceof Node\Expr\Yield_) {
7074
if ($node->value === null) {
7175
return [];
7276
}
7377

7478
$exprType = $scope->getType($node->value);
75-
$constantArrays = $exprType->getConstantArrays();
79+
$arraysTypes = $exprType->getConstantArrays();
7680
}
7781

78-
if ($constantArrays === []) {
82+
if ($arraysTypes === []) {
7983
return [];
8084
}
8185

@@ -111,14 +115,16 @@ public function processNode(Node $node, Scope $scope): array
111115
}
112116

113117
foreach ($testsWithProvider as $testMethod) {
114-
foreach ($constantArrays as $constantArray) {
115-
$args = $this->arrayItemsToArgs($constantArray);
118+
$numberOfParameters = $testMethod->getNumberOfParameters();
119+
120+
foreach ($arraysTypes as $arraysType) {
121+
$args = $this->arrayItemsToArgs($arraysType, $numberOfParameters);
116122
if ($args === null) {
117123
continue;
118124
}
119125

120-
if ($trimArgs && $maxNumberOfParameters !== $testMethod->getNumberOfParameters()) {
121-
$args = array_slice($args, 0, min($testMethod->getNumberOfParameters(), $maxNumberOfParameters));
126+
if ($trimArgs && $maxNumberOfParameters !== $numberOfParameters) {
127+
$args = array_slice($args, 0, min($numberOfParameters, $maxNumberOfParameters));
122128
}
123129

124130
$scope->invokeNodeCallback(new Node\Expr\MethodCall(
@@ -136,12 +142,26 @@ public function processNode(Node $node, Scope $scope): array
136142
/**
137143
* @return array<Node\Arg>
138144
*/
139-
private function arrayItemsToArgs(ConstantArrayType $array): ?array
145+
private function arrayItemsToArgs(Type $array, int $numberOfParameters): ?array
140146
{
141147
$args = [];
142148

143-
$keyTypes = $array->getKeyTypes();
144-
foreach ($array->getValueTypes() as $i => $valueType) {
149+
$constArrays = $array->getConstantArrays();
150+
if ($constArrays !== [] && count($constArrays) === 1) {
151+
$keyTypes = $constArrays[0]->getKeyTypes();
152+
$valueTypes = $constArrays[0]->getValueTypes();
153+
} elseif ($array->isArray()->yes()) {
154+
$keyTypes = [];
155+
$valueTypes = [];
156+
for ($i = 0; $i < $numberOfParameters; ++$i) {
157+
$keyTypes[$i] = $array->getIterableKeyType();
158+
$valueTypes[$i] = $array->getIterableValueType();
159+
}
160+
} else {
161+
return null;
162+
}
163+
164+
foreach ($valueTypes as $i => $valueType) {
145165
$key = $keyTypes[$i]->getConstantStrings();
146166
if (count($key) > 1) {
147167
return null;

tests/Rules/PHPUnit/DataProviderDataRuleTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ public function testRule(): void
158158
'Parameter #1 $s1 of method DataProviderDataTest\TestArrayIterator::testFooBar() expects string, int|string given.',
159159
421,
160160
],
161+
[
162+
'Parameter #1 $si of method DataProviderDataTest\TestWrongTypedIterable::testBar() expects int, string given.',
163+
439,
164+
],
161165
]);
162166
}
163167

tests/Rules/PHPUnit/data/data-provider-data.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,3 +426,23 @@ public function aProvider(): iterable
426426
]);
427427
}
428428
}
429+
430+
class TestWrongTypedIterable extends TestCase
431+
{
432+
/** @dataProvider aProvider */
433+
public function testBar(int $si): void
434+
{
435+
}
436+
437+
public function aProvider(): iterable
438+
{
439+
return $this->data();
440+
}
441+
442+
/**
443+
* @return iterable<array<string>>
444+
*/
445+
public function data(): iterable
446+
{
447+
}
448+
}

0 commit comments

Comments
 (0)