Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 2b9e658

Browse files
Fix IntersectionType::isIterableOnce()
1 parent 46062ed commit 2b9e658

File tree

3 files changed

+62
-4
lines changed

3 files changed

+62
-4
lines changed

‎src/Type/IntersectionType.php‎

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
use PHPStan\Type\Generic\TemplateTypeVariance;
4040
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
4141
use PHPStan\Type\Traits\NonRemoveableTypeTrait;
42+
use function array_filter;
4243
use function array_intersect_key;
4344
use function array_map;
4445
use function array_shift;
@@ -599,7 +600,10 @@ public function isIterable(): TrinaryLogic
599600

600601
public function isIterableAtLeastOnce(): TrinaryLogic
601602
{
602-
return $this->intersectResults(static fn (Type $type): TrinaryLogic => $type->isIterableAtLeastOnce());
603+
return $this->intersectResults(
604+
static fn (Type $type): TrinaryLogic => $type->isIterableAtLeastOnce(),
605+
static fn (Type $type): bool => !$type->isIterable()->no(),
606+
);
603607
}
604608

605609
public function getArraySize(): Type
@@ -1243,10 +1247,22 @@ public function getFiniteTypes(): array
12431247

12441248
/**
12451249
* @param callable(Type $type): TrinaryLogic $getResult
1250+
* @param (callable(Type $type): bool)|null $filter
12461251
*/
1247-
private function intersectResults(callable $getResult): TrinaryLogic
1248-
{
1249-
return TrinaryLogic::lazyMaxMin($this->types, $getResult);
1252+
private function intersectResults(
1253+
callable $getResult,
1254+
?callable $filter = null,
1255+
): TrinaryLogic
1256+
{
1257+
$types = $this->types;
1258+
if ($filter !== null) {
1259+
$types = array_filter($types, $filter);
1260+
}
1261+
if (count($types) === 0) {
1262+
return TrinaryLogic::createNo();
1263+
}
1264+
1265+
return TrinaryLogic::lazyMaxMin($types, $getResult);
12501266
}
12511267

12521268
/**

‎tests/PHPStan/Rules/Arrays/DeadForeachRuleTest.php‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,9 @@ public function testBug8292(): void
4040
$this->analyse([__DIR__ . '/data/bug-8292.php'], []);
4141
}
4242

43+
public function testBug13248(): void
44+
{
45+
$this->analyse([__DIR__ . '/data/bug-13248.php'], []);
46+
}
47+
4348
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace Bug13248;
4+
5+
use ArrayIterator;
6+
use IteratorAggregate;
7+
use Traversable;
8+
9+
class X
10+
{
11+
}
12+
13+
/**
14+
* @implements IteratorAggregate<int, string>
15+
*/
16+
class Y extends X implements IteratorAggregate
17+
{
18+
/**
19+
* @return ArrayIterator<int<0, 2>, 'a'|'b'|'c'>
20+
*/
21+
public function getIterator(): Traversable
22+
{
23+
return new ArrayIterator(['a', 'b', 'c']);
24+
}
25+
}
26+
27+
/**
28+
* @return X&Traversable<int, string>
29+
*/
30+
function y(): X
31+
{
32+
return new Y();
33+
}
34+
35+
foreach (y() as $item) { // hm?
36+
echo $item . PHP_EOL;
37+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /