Skip to content

Commit 4fe59c0

Browse files
authored
Fix nested arrays in DocblockReturnArrayFromDirectArrayInstanceRector (#7284)
* add nested fixture * misc
1 parent 4e279f6 commit 4fe59c0

File tree

4 files changed

+58
-60
lines changed

4 files changed

+58
-60
lines changed

rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/complex_array.php.inc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\DocblockRetur
3131
class ComplexArray
3232
{
3333
/**
34-
* @return array<string, object|mixed[]>
34+
* @return array<string, \stdClass|array<string, int|string|array<string, string>>>
3535
*/
3636
public function run(): array
3737
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\DocblockReturnArrayFromDirectArrayInstanceRector\Fixture;
4+
5+
final class CoverImplicitKey
6+
{
7+
private static function getExpectedAllOwners(): array
8+
{
9+
return [
10+
[
11+
'key1' => 100,
12+
'key2' => '-25.5%',
13+
],
14+
[
15+
'key3' => 'Yes',
16+
'key4' => 200,
17+
],
18+
];
19+
}
20+
}
21+
22+
?>
23+
-----
24+
<?php
25+
26+
namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\DocblockReturnArrayFromDirectArrayInstanceRector\Fixture;
27+
28+
final class CoverImplicitKey
29+
{
30+
/**
31+
* @return array<int, array<string, string|int>>
32+
*/
33+
private static function getExpectedAllOwners(): array
34+
{
35+
return [
36+
[
37+
'key1' => 100,
38+
'key2' => '-25.5%',
39+
],
40+
[
41+
'key3' => 'Yes',
42+
'key4' => 200,
43+
],
44+
];
45+
}
46+
}
47+
48+
?>

rules/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ public function refactor(Node $node): ?Node
100100

101101
// resolve simple type
102102
$returnedType = $this->getType($soleReturn->expr);
103-
104103
if (! $returnedType instanceof ConstantArrayType) {
105104
return null;
106105
}

rules/TypeDeclarationDocblocks/TypeResolver/ConstantArrayTypeGeneralizer.php

Lines changed: 9 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,12 @@
66

77
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
88
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
9-
use PHPStan\Type\ArrayType;
10-
use PHPStan\Type\BooleanType;
119
use PHPStan\Type\Constant\ConstantArrayType;
12-
use PHPStan\Type\FloatType;
1310
use PHPStan\Type\IntegerType;
14-
use PHPStan\Type\IntersectionType;
1511
use PHPStan\Type\MixedType;
1612
use PHPStan\Type\NeverType;
17-
use PHPStan\Type\ObjectWithoutClassType;
18-
use PHPStan\Type\StringType;
1913
use PHPStan\Type\Type;
20-
use PHPStan\Type\UnionType;
21-
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
14+
use Rector\Privatization\TypeManipulator\TypeNormalizer;
2215
use Rector\StaticTypeMapper\StaticTypeMapper;
2316

2417
final class ConstantArrayTypeGeneralizer
@@ -32,8 +25,8 @@ final class ConstantArrayTypeGeneralizer
3225
private int $currentNesting = 0;
3326

3427
public function __construct(
35-
private readonly TypeFactory $typeFactory,
3628
private readonly StaticTypeMapper $staticTypeMapper,
29+
private readonly TypeNormalizer $typeNormalizer
3730
) {
3831
}
3932

@@ -45,7 +38,7 @@ public function generalize(ConstantArrayType $constantArrayType, bool $isFresh =
4538
++$this->currentNesting;
4639
}
4740

48-
$genericKeyType = $this->constantToGenericType($constantArrayType->getKeyType());
41+
$genericKeyType = $this->typeNormalizer->generalizeConstantTypes($constantArrayType->getKeyType());
4942

5043
if ($constantArrayType->getItemType() instanceof NeverType) {
5144
$genericKeyType = new IntegerType();
@@ -60,7 +53,12 @@ public function generalize(ConstantArrayType $constantArrayType, bool $isFresh =
6053
$genericItemType = $this->generalize($itemType, false);
6154
}
6255
} else {
63-
$genericItemType = $this->constantToGenericType($itemType);
56+
$genericItemType = $this->typeNormalizer->generalizeConstantTypes($itemType);
57+
}
58+
59+
// correct
60+
if ($genericItemType instanceof NeverType) {
61+
$genericItemType = new MixedType();
6462
}
6563

6664
return $this->createArrayGenericTypeNode($genericKeyType, $genericItemType);
@@ -78,51 +76,4 @@ private function createArrayGenericTypeNode(Type $keyType, Type|GenericTypeNode
7876

7977
return new GenericTypeNode(new IdentifierTypeNode('array'), [$keyDocTypeNode, $itemDocTypeNode]);
8078
}
81-
82-
/**
83-
* Covers constant types too and makes them more generic
84-
*/
85-
private function constantToGenericType(Type $type): Type
86-
{
87-
if ($type->isString()->yes()) {
88-
return new StringType();
89-
}
90-
91-
if ($type->isInteger()->yes()) {
92-
return new IntegerType();
93-
}
94-
95-
if ($type->isBoolean()->yes()) {
96-
return new BooleanType();
97-
}
98-
99-
if ($type->isFloat()->yes()) {
100-
return new FloatType();
101-
}
102-
103-
if ($type->isObject()->yes()) {
104-
return new ObjectWithoutClassType();
105-
}
106-
107-
if ($type instanceof UnionType || $type instanceof IntersectionType) {
108-
$genericComplexTypes = [];
109-
foreach ($type->getTypes() as $splitType) {
110-
$genericComplexTypes[] = $this->constantToGenericType($splitType);
111-
}
112-
113-
$genericComplexTypes = $this->typeFactory->uniquateTypes($genericComplexTypes);
114-
if (count($genericComplexTypes) > 1) {
115-
return new UnionType($genericComplexTypes);
116-
}
117-
118-
return $genericComplexTypes[0];
119-
}
120-
121-
if ($type instanceof ConstantArrayType) {
122-
return new ArrayType(new MixedType(), new MixedType());
123-
}
124-
125-
// unclear
126-
return new MixedType();
127-
}
12879
}

0 commit comments

Comments
 (0)