Skip to content

Commit 34edb50

Browse files
committed
Fixed accepting ConstantArrayType
1 parent 61cb5aa commit 34edb50

File tree

7 files changed

+95
-71
lines changed

7 files changed

+95
-71
lines changed

src/Rules/RuleLevelHelper.php

+2-48
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
use PHPStan\Reflection\ReflectionProvider;
88
use PHPStan\Type\BenevolentUnionType;
99
use PHPStan\Type\CompoundType;
10-
use PHPStan\Type\Constant\ConstantArrayType;
1110
use PHPStan\Type\ErrorType;
1211
use PHPStan\Type\MixedType;
1312
use PHPStan\Type\NeverType;
@@ -63,52 +62,6 @@ public function accepts(Type $acceptingType, Type $acceptedType, bool $strictTyp
6362
$acceptedType = TypeCombinator::removeNull($acceptedType);
6463
}
6564

66-
if (
67-
$acceptedType->isArray()->yes()
68-
&& $acceptingType->isArray()->yes()
69-
&& !$acceptingType instanceof ConstantArrayType
70-
) {
71-
$acceptedConstantArrays = TypeUtils::getConstantArrays($acceptedType);
72-
if (count($acceptedConstantArrays) > 0) {
73-
foreach ($acceptedConstantArrays as $acceptedConstantArray) {
74-
foreach ($acceptedConstantArray->getKeyTypes() as $i => $keyType) {
75-
$valueType = $acceptedConstantArray->getValueTypes()[$i];
76-
if (
77-
!self::accepts(
78-
$acceptingType->getIterableKeyType(),
79-
$keyType,
80-
$strictTypes
81-
) || !self::accepts(
82-
$acceptingType->getIterableValueType(),
83-
$valueType,
84-
$strictTypes
85-
)
86-
) {
87-
return false;
88-
}
89-
}
90-
}
91-
92-
return true;
93-
}
94-
95-
if (
96-
!self::accepts(
97-
$acceptingType->getIterableKeyType(),
98-
$acceptedType->getIterableKeyType(),
99-
$strictTypes
100-
) || !self::accepts(
101-
$acceptingType->getIterableValueType(),
102-
$acceptedType->getIterableValueType(),
103-
$strictTypes
104-
)
105-
) {
106-
return false;
107-
}
108-
109-
return true;
110-
}
111-
11265
if ($acceptingType instanceof UnionType && !$acceptedType instanceof CompoundType) {
11366
foreach ($acceptingType->getTypes() as $innerType) {
11467
if (self::accepts($innerType, $acceptedType, $strictTypes)) {
@@ -122,7 +75,8 @@ public function accepts(Type $acceptingType, Type $acceptedType, bool $strictTyp
12275
if (
12376
$acceptedType->isArray()->yes()
12477
&& $acceptingType->isArray()->yes()
125-
&& !$acceptingType instanceof ConstantArrayType
78+
&& count(TypeUtils::getConstantArrays($acceptedType)) === 0
79+
&& count(TypeUtils::getConstantArrays($acceptingType)) === 0
12680
) {
12781
return self::accepts(
12882
$acceptingType->getIterableKeyType(),

src/Type/ArrayType.php

+13-8
Original file line numberDiff line numberDiff line change
@@ -61,20 +61,25 @@ public function getReferencedClasses(): array
6161

6262
public function accepts(Type $type, bool $strictTypes): TrinaryLogic
6363
{
64-
$arrays = TypeUtils::getArrays($type);
65-
if (count($arrays) > 0) {
64+
if ($type instanceof CompoundType) {
65+
return CompoundTypeHelper::accepts($type, $this, $strictTypes);
66+
}
67+
68+
if ($type instanceof ConstantArrayType) {
6669
$result = TrinaryLogic::createYes();
67-
foreach ($arrays as $array) {
68-
$result = $result
69-
->and($this->getItemType()->accepts($array->getItemType(), $strictTypes))
70-
->and($this->keyType->accepts($array->keyType, $strictTypes));
70+
$thisKeyType = $this->keyType;
71+
$itemType = $this->getItemType();
72+
foreach ($type->getKeyTypes() as $i => $keyType) {
73+
$valueType = $type->getValueTypes()[$i];
74+
$result = $result->and($thisKeyType->accepts($keyType, $strictTypes))->and($itemType->accepts($valueType, $strictTypes));
7175
}
7276

7377
return $result;
7478
}
7579

76-
if ($type instanceof CompoundType) {
77-
return CompoundTypeHelper::accepts($type, $this, $strictTypes);
80+
if ($type instanceof ArrayType) {
81+
return $this->getItemType()->accepts($type->getItemType(), $strictTypes)
82+
->and($this->keyType->accepts($type->keyType, $strictTypes));
7883
}
7984

8085
return TrinaryLogic::createNo();

tests/PHPStan/Levels/data/acceptTypes-5.json

-10
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,6 @@
4444
"line": 118,
4545
"ignorable": true
4646
},
47-
{
48-
"message": "Parameter #1 $i of method Levels\\AcceptTypes\\Foo::doBarArray() expects array<int>, array<int, int|null> given.",
49-
"line": 119,
50-
"ignorable": true
51-
},
52-
{
53-
"message": "Parameter #1 $i of method Levels\\AcceptTypes\\Foo::doBarArray() expects array<int>, array<int, float|int> given.",
54-
"line": 120,
55-
"ignorable": true
56-
},
5747
{
5848
"message": "Parameter #1 $callable of method Levels\\AcceptTypes\\Foo::expectCallable() expects callable(): mixed, 'nonexistentFunction' given.",
5949
"line": 144,

tests/PHPStan/Levels/data/acceptTypes-7.json

+15
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,21 @@
1919
"line": 92,
2020
"ignorable": true
2121
},
22+
{
23+
"message": "Parameter #1 $i of method Levels\\AcceptTypes\\Foo::doBarArray() expects array<int>, array<int, int|null> given.",
24+
"line": 119,
25+
"ignorable": true
26+
},
27+
{
28+
"message": "Parameter #1 $i of method Levels\\AcceptTypes\\Foo::doBarArray() expects array<int>, array<int, float|int> given.",
29+
"line": 120,
30+
"ignorable": true
31+
},
32+
{
33+
"message": "Parameter #1 $i of method Levels\\AcceptTypes\\Foo::doBarArray() expects array<int>, array<int, int|null> given.",
34+
"line": 131,
35+
"ignorable": true
36+
},
2237
{
2338
"message": "Parameter #1 $i of method Levels\\AcceptTypes\\Foo::doBarArray() expects array<int>, array<int, float|int> given.",
2439
"line": 132,

tests/PHPStan/Levels/data/acceptTypes-8.json

-5
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,6 @@
1414
"line": 91,
1515
"ignorable": true
1616
},
17-
{
18-
"message": "Parameter #1 $i of method Levels\\AcceptTypes\\Foo::doBarArray() expects array<int>, array<int, int|null> given.",
19-
"line": 131,
20-
"ignorable": true
21-
},
2217
{
2318
"message": "Parameter #1 $i of method Levels\\AcceptTypes\\Baz::doBar() expects int, int|null given.",
2419
"line": 405,

tests/PHPStan/Rules/Methods/data/call-methods.php

+42
Original file line numberDiff line numberDiff line change
@@ -1598,3 +1598,45 @@ public function doBar(array $members)
15981598
}
15991599

16001600
}
1601+
1602+
class ConstantArrayAccepts
1603+
{
1604+
1605+
/**
1606+
* @param array{
1607+
* name: string,
1608+
* color: string,
1609+
* year: int,
1610+
* } $param
1611+
*/
1612+
public function doFoo(array $param): void
1613+
{
1614+
$this->doBar($param);
1615+
}
1616+
1617+
/**
1618+
* @param array{
1619+
* name: string,
1620+
* color?: string,
1621+
* } $param
1622+
*/
1623+
public function doBar(array $param): void
1624+
{
1625+
1626+
}
1627+
1628+
}
1629+
1630+
class ConstantArrayAcceptsOptionalKey
1631+
{
1632+
1633+
/**
1634+
* @param array{wrapperClass?: class-string} $params
1635+
*/
1636+
public function doFoo(array $params)
1637+
{
1638+
$this->doFoo(['wrapperClass' => \stdClass::class, 'undocumented' => 42]);
1639+
$this->doFoo([]);
1640+
}
1641+
1642+
}

tests/PHPStan/Type/ArrayTypeTest.php

+23
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,29 @@ public function dataAccepts(): array
110110
]),
111111
TrinaryLogic::createYes(),
112112
],
113+
[
114+
new ArrayType(new MixedType(), new CallableType()),
115+
new ConstantArrayType([
116+
new ConstantIntegerType(0),
117+
new ConstantIntegerType(1),
118+
], [
119+
new ConstantArrayType([
120+
new ConstantIntegerType(0),
121+
new ConstantIntegerType(1),
122+
], [
123+
new ThisType(self::class),
124+
new ConstantStringType('dataAccepts'),
125+
]),
126+
new ConstantArrayType([
127+
new ConstantIntegerType(0),
128+
new ConstantIntegerType(1),
129+
], [
130+
new ThisType(self::class),
131+
new ConstantStringType('dataIsSuperTypeOf'),
132+
]),
133+
]),
134+
TrinaryLogic::createYes(),
135+
],
113136
];
114137
}
115138

0 commit comments

Comments
 (0)