Skip to content

Commit 304d67e

Browse files
VincentLangletondrejmirtes
authored andcommitted
Solve issue 9766
1 parent edb696e commit 304d67e

4 files changed

Lines changed: 58 additions & 2 deletions

File tree

src/Rules/RuleLevelHelper.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use PHPStan\Type\Type;
2020
use PHPStan\Type\TypeCombinator;
2121
use PHPStan\Type\TypeTraverser;
22+
use PHPStan\Type\TypeUtils;
2223
use PHPStan\Type\UnionType;
2324
use PHPStan\Type\VerbosityLevel;
2425
use function array_merge;
@@ -119,7 +120,7 @@ private function transformAcceptedType(Type $acceptingType, Type $acceptedType):
119120
if ($this->checkBenevolentUnionTypes) {
120121
if ($acceptedType instanceof BenevolentUnionType) {
121122
$checkForUnion = true;
122-
return $traverse(new UnionType($acceptedType->getTypes()));
123+
return $traverse(TypeUtils::toStrictUnion($acceptedType));
123124
}
124125
}
125126

@@ -149,7 +150,7 @@ public function acceptsWithReason(Type $acceptingType, Type $acceptedType, bool
149150
$traverse = static function (Type $type, callable $traverse) use (&$checkForUnion): Type {
150151
if ($type instanceof BenevolentUnionType) {
151152
$checkForUnion = true;
152-
return new UnionType($type->getTypes());
153+
return TypeUtils::toStrictUnion($type);
153154
}
154155

155156
return $traverse($type);

src/Type/TypeUtils.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
use PHPStan\Type\Constant\ConstantIntegerType;
99
use PHPStan\Type\Constant\ConstantStringType;
1010
use PHPStan\Type\Enum\EnumCaseObjectType;
11+
use PHPStan\Type\Generic\TemplateBenevolentUnionType;
1112
use PHPStan\Type\Generic\TemplateType;
13+
use PHPStan\Type\Generic\TemplateUnionType;
1214
use function array_merge;
1315
use function array_unique;
1416
use function array_values;
@@ -275,6 +277,28 @@ public static function toBenevolentUnion(Type $type): Type
275277
return $type;
276278
}
277279

280+
/**
281+
* @return ($type is UnionType ? UnionType : Type)
282+
*/
283+
public static function toStrictUnion(Type $type): Type
284+
{
285+
if ($type instanceof TemplateBenevolentUnionType) {
286+
return new TemplateUnionType(
287+
$type->getScope(),
288+
$type->getStrategy(),
289+
$type->getVariance(),
290+
$type->getName(),
291+
static::toStrictUnion($type->getBound()),
292+
);
293+
}
294+
295+
if ($type instanceof BenevolentUnionType) {
296+
return new UnionType($type->getTypes());
297+
}
298+
299+
return $type;
300+
}
301+
278302
/**
279303
* @return Type[]
280304
*/

tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -949,4 +949,10 @@ public function testBug6175(): void
949949
$this->analyse([__DIR__ . '/data/bug-6175.php'], []);
950950
}
951951

952+
public function testBug9766(): void
953+
{
954+
$this->checkBenevolentUnionTypes = true;
955+
$this->analyse([__DIR__ . '/data/bug-9766.php'], []);
956+
}
957+
952958
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace Bug9766;
4+
5+
/**
6+
* @template TKey of array-key
7+
* @template TItem
8+
*/
9+
abstract class PHPStanBug {
10+
/**
11+
* @param iterable<TKey, TItem> $items
12+
*/
13+
public function __construct(
14+
private iterable $items,
15+
) {
16+
// empty
17+
}
18+
19+
/**
20+
* @return iterable<TKey, TItem>
21+
*/
22+
protected function getItems(): iterable {
23+
return $this->items;
24+
}
25+
}

0 commit comments

Comments
 (0)