Skip to content

Commit 7d55272

Browse files
authored
Prefer exact type over mixed[] in case of empty array in ClassMethodArrayDocblockParamFromLocalCallsRector (#7334)
1 parent 6d40f8a commit 7d55272

File tree

5 files changed

+116
-9
lines changed

5 files changed

+116
-9
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclarationDocblocks\Rector\Class_\ClassMethodArrayDocblockParamFromLocalCallsRector\Fixture;
4+
5+
use Rector\Tests\TypeDeclarationDocblocks\Rector\Class_\ClassMethodArrayDocblockParamFromLocalCallsRector\Source\SomeReturnedObject;
6+
7+
final class ObjectsOverruleEmptyArray
8+
{
9+
public function go(array $emptyArray)
10+
{
11+
$this->run($this->getItemsWithArray());
12+
$this->run($this->getItems());
13+
14+
$this->run($emptyArray);
15+
}
16+
17+
public function run(array $result)
18+
{
19+
}
20+
21+
/**
22+
* @return SomeReturnedObject[]|array
23+
*/
24+
private function getItems()
25+
{
26+
return [];
27+
}
28+
29+
/**
30+
* @return SomeReturnedObject[]
31+
*/
32+
private function getItemsWithArray()
33+
{
34+
return [];
35+
}
36+
}
37+
38+
?>
39+
-----
40+
<?php
41+
42+
namespace Rector\Tests\TypeDeclarationDocblocks\Rector\Class_\ClassMethodArrayDocblockParamFromLocalCallsRector\Fixture;
43+
44+
use Rector\Tests\TypeDeclarationDocblocks\Rector\Class_\ClassMethodArrayDocblockParamFromLocalCallsRector\Source\SomeReturnedObject;
45+
46+
final class ObjectsOverruleEmptyArray
47+
{
48+
public function go(array $emptyArray)
49+
{
50+
$this->run($this->getItemsWithArray());
51+
$this->run($this->getItems());
52+
53+
$this->run($emptyArray);
54+
}
55+
56+
/**
57+
* @param \Rector\Tests\TypeDeclarationDocblocks\Rector\Class_\ClassMethodArrayDocblockParamFromLocalCallsRector\Source\SomeReturnedObject[] $result
58+
*/
59+
public function run(array $result)
60+
{
61+
}
62+
63+
/**
64+
* @return SomeReturnedObject[]|array
65+
*/
66+
private function getItems()
67+
{
68+
return [];
69+
}
70+
71+
/**
72+
* @return SomeReturnedObject[]
73+
*/
74+
private function getItemsWithArray()
75+
{
76+
return [];
77+
}
78+
}
79+
80+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclarationDocblocks\Rector\Class_\ClassMethodArrayDocblockParamFromLocalCallsRector\Source;
4+
5+
final class SomeReturnedObject
6+
{
7+
}

rules/TypeDeclaration/NodeAnalyzer/CallTypesResolver.php

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use PhpParser\Node\Identifier;
1313
use PhpParser\Node\VariadicPlaceholder;
1414
use PHPStan\Reflection\ReflectionProvider;
15+
use PHPStan\Type\ArrayType;
1516
use PHPStan\Type\MixedType;
1617
use PHPStan\Type\ObjectType;
1718
use PHPStan\Type\ThisType;
@@ -83,7 +84,7 @@ public function resolveTypesFromCalls(array $calls): array
8384
}
8485

8586
// unite to single type
86-
return $this->unionToSingleType($staticTypesByArgumentPosition);
87+
return $this->unionToSingleType($staticTypesByArgumentPosition, true);
8788
}
8889

8990
private function resolveStrictArgValueType(Arg $arg): Type
@@ -113,13 +114,19 @@ private function correctSelfType(Type $argValueType): Type
113114
* @param array<int, Type[]> $staticTypesByArgumentPosition
114115
* @return array<int, Type>
115116
*/
116-
private function unionToSingleType(array $staticTypesByArgumentPosition): array
117+
private function unionToSingleType(array $staticTypesByArgumentPosition, bool $removeMixedArray = false): array
117118
{
118119
$staticTypeByArgumentPosition = [];
120+
119121
foreach ($staticTypesByArgumentPosition as $position => $staticTypes) {
120-
$unionedType = $this->typeFactory->createMixedPassedOrUnionType($staticTypes);
122+
if ($removeMixedArray) {
123+
$staticTypes = array_filter(
124+
$staticTypes,
125+
fn (Type $type): bool => ! $this->isArrayMixedMixedType($type)
126+
);
127+
}
121128

122-
// narrow parents to most child type
129+
$unionedType = $this->typeFactory->createMixedPassedOrUnionType($staticTypes);
123130

124131
$staticTypeByArgumentPosition[$position] = $this->narrowParentObjectTreeToSingleObjectChildType(
125132
$unionedType
@@ -212,4 +219,17 @@ private function isEmptyArray(Expr $expr): bool
212219

213220
return $expr->items === [];
214221
}
222+
223+
private function isArrayMixedMixedType(Type $type): bool
224+
{
225+
if (! $type instanceof ArrayType) {
226+
return false;
227+
}
228+
229+
if (! $type->getItemType() instanceof MixedType) {
230+
return false;
231+
}
232+
233+
return $type->getKeyType() instanceof MixedType;
234+
}
215235
}

src/NodeTypeResolver/NodeTypeCorrector/AccessoryNonEmptyArrayTypeCorrector.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ public function correct(Type $mainType): Type
3030

3131
if ($type instanceof ArrayType
3232
&& $type->getIterableValueType() instanceof IntersectionType
33-
&& $type->getIterableValueType()->isString()->yes()) {
33+
&& $type->getIterableValueType()
34+
->isString()
35+
->yes()) {
3436
return new ArrayType(new MixedType(), new StringType());
3537
}
3638
}

src/PostRector/Collector/UseNodesToAddCollector.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,8 @@ public function getUseImportTypesByNode(File $file): array
8383
return $objectTypes;
8484
}
8585

86-
public function hasImport(
87-
File $file,
88-
FullyQualifiedObjectType $fullyQualifiedObjectType
89-
): bool {
86+
public function hasImport(File $file, FullyQualifiedObjectType $fullyQualifiedObjectType): bool
87+
{
9088
$useImports = $this->getUseImportTypesByNode($file);
9189

9290
foreach ($useImports as $useImport) {

0 commit comments

Comments
 (0)