Skip to content

Commit f3ad801

Browse files
committed
Merge branch 2.1.x into 2.2.x
2 parents a883ebf + 95e0784 commit f3ad801

3 files changed

Lines changed: 93 additions & 1 deletion

File tree

src/Type/IntersectionType.php

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -950,7 +950,17 @@ public function hasOffsetValueType(Type $offsetType): TrinaryLogic
950950
}
951951
}
952952

953-
return $this->intersectResults(static fn (Type $type): TrinaryLogic => $type->hasOffsetValueType($offsetType));
953+
$result = $this->intersectResults(static fn (Type $type): TrinaryLogic => $type->hasOffsetValueType($offsetType));
954+
955+
if (!$result->yes() && $this->isCallable()->yes() && $this->isArray()->yes()) {
956+
$arrayKeyOffsetType = $offsetType->toArrayKey();
957+
$callableArrayOffsetType = new UnionType([new ConstantIntegerType(0), new ConstantIntegerType(1)]);
958+
if ($callableArrayOffsetType->isSuperTypeOf($arrayKeyOffsetType)->yes()) {
959+
return TrinaryLogic::createYes();
960+
}
961+
}
962+
963+
return $result;
954964
}
955965

956966
public function getOffsetValueType(Type $offsetType): Type
@@ -960,6 +970,21 @@ public function getOffsetValueType(Type $offsetType): Type
960970
return TypeUtils::toBenevolentUnion($result);
961971
}
962972

973+
if ($this->isCallable()->yes() && $this->isArray()->yes()) {
974+
$arrayKeyOffsetType = $offsetType->toArrayKey();
975+
$callableArrayOffsetType = new UnionType([new ConstantIntegerType(0), new ConstantIntegerType(1)]);
976+
if ($callableArrayOffsetType->isSuperTypeOf($arrayKeyOffsetType)->yes()) {
977+
if ((new ConstantIntegerType(0))->isSuperTypeOf($arrayKeyOffsetType)->yes()) {
978+
$narrowedType = new UnionType([new ClassStringType(), new ObjectWithoutClassType()]);
979+
} elseif ((new ConstantIntegerType(1))->isSuperTypeOf($arrayKeyOffsetType)->yes()) {
980+
$narrowedType = new StringType();
981+
} else {
982+
$narrowedType = new UnionType([new StringType(), new ObjectWithoutClassType()]);
983+
}
984+
$result = TypeCombinator::intersect($result, $narrowedType);
985+
}
986+
}
987+
963988
return $result;
964989
}
965990

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug3842;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class ClassA
8+
{
9+
public static function callback(): void
10+
{
11+
}
12+
}
13+
14+
class ClassB
15+
{
16+
public function callback(): void
17+
{
18+
}
19+
}
20+
21+
function testIsArrayOnCallable(callable $value): void {
22+
if (is_array($value)) {
23+
assertType('array<mixed, mixed>&callable(): mixed', $value);
24+
assertType('class-string|object', $value[0]);
25+
assertType('string', $value[1]);
26+
}
27+
}
28+
29+
/** @param callable-array $value */
30+
function testCallableArrayPhpDoc(array $value): void {
31+
assertType('array&callable(): mixed', $value);
32+
assertType('class-string|object', $value[0]);
33+
assertType('string', $value[1]);
34+
}
35+
36+
function testIsStringOnCallable(callable $value): void {
37+
if (is_string($value)) {
38+
assertType('callable-string', $value);
39+
}
40+
}
41+
42+
/** @param array{string|object, string} $values */
43+
function check(array $values): void {
44+
}
45+
46+
/** @param array{class-string|object, string} $values */
47+
function checkClassString(array $values): void {
48+
}
49+
50+
/** @param 0|1 $offset */
51+
function testCallableArrayUnionOffset(callable $value, int $offset): void {
52+
if (is_array($value)) {
53+
assertType('object|string', $value[$offset]);
54+
}
55+
}
56+
57+
function testPassCallableArray(callable $value): void {
58+
if (is_array($value)) {
59+
check($value);
60+
checkClassString($value);
61+
}
62+
}

tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2951,4 +2951,9 @@ public function testBug13643(): void
29512951
$this->analyse([__DIR__ . '/data/bug-13643.php'], []);
29522952
}
29532953

2954+
public function testBug3842(): void
2955+
{
2956+
$this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-3842.php'], []);
2957+
}
2958+
29542959
}

0 commit comments

Comments
 (0)