Skip to content

Commit 07d6405

Browse files
committed
Fix array intersection between HasOffsetType and HasOffsetValueType
1 parent aa05a7d commit 07d6405

File tree

6 files changed

+79
-2
lines changed

6 files changed

+79
-2
lines changed

phpstan-baseline.neon

+11-1
Original file line numberDiff line numberDiff line change
@@ -1579,7 +1579,7 @@ parameters:
15791579

15801580
-
15811581
message: "#^Doing instanceof PHPStan\\\\Type\\\\Constant\\\\ConstantStringType is error\\-prone and deprecated\\. Use Type\\:\\:getConstantStrings\\(\\) instead\\.$#"
1582-
count: 4
1582+
count: 5
15831583
path: src/Type/TypeCombinator.php
15841584

15851585
-
@@ -1622,6 +1622,16 @@ parameters:
16221622
count: 1
16231623
path: src/Type/TypeCombinator.php
16241624

1625+
-
1626+
message: "#^Instanceof between PHPStan\\\\Type\\\\Constant\\\\ConstantIntegerType and PHPStan\\\\Type\\\\Constant\\\\ConstantIntegerType will always evaluate to true\\.$#"
1627+
count: 1
1628+
path: src/Type/TypeCombinator.php
1629+
1630+
-
1631+
message: "#^Result of \\|\\| is always true\\.$#"
1632+
count: 1
1633+
path: src/Type/TypeCombinator.php
1634+
16251635
-
16261636
message: "#^Doing instanceof PHPStan\\\\Type\\\\ArrayType is error\\-prone and deprecated\\. Use Type\\:\\:isArray\\(\\) or Type\\:\\:getArrays\\(\\) instead\\.$#"
16271637
count: 3

src/Type/TypeCombinator.php

+6
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,12 @@ private static function processArrayAccessoryTypes(array $arrayTypes): array
591591
if (!($innerType instanceof AccessoryType) && !($innerType instanceof CallableType)) {
592592
continue;
593593
}
594+
if ($innerType instanceof HasOffsetType) {
595+
$offset = $innerType->getOffsetType();
596+
if ($offset instanceof ConstantStringType || $offset instanceof ConstantIntegerType) {
597+
$innerType = new HasOffsetValueType($offset, $arrayType->getIterableValueType());
598+
}
599+
}
594600
if ($innerType instanceof HasOffsetValueType) {
595601
$accessoryTypes[sprintf('hasOffsetValue(%s)', $innerType->getOffsetType()->describe(VerbosityLevel::cache()))][$i] = $innerType;
596602
continue;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Bug11518Types;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/**
8+
* @param mixed[] $a
9+
* @return array{thing: mixed}
10+
* */
11+
function blah(array $a): array
12+
{
13+
if (!array_key_exists('thing', $a)) {
14+
$a['thing'] = 'bla';
15+
assertType('hasOffsetValue(\'thing\', \'bla\')&non-empty-array', $a);
16+
} else {
17+
assertType('array&hasOffset(\'thing\')', $a);
18+
}
19+
20+
assertType('array&hasOffsetValue(\'thing\', mixed)', $a);
21+
22+
return $a;
23+
}

tests/PHPStan/Rules/Functions/ReturnTypeRuleTest.php

+7
Original file line numberDiff line numberDiff line change
@@ -283,4 +283,11 @@ public function testBug10732(): void
283283
$this->analyse([__DIR__ . '/data/bug-10732.php'], []);
284284
}
285285

286+
public function testBug11518(): void
287+
{
288+
$this->checkExplicitMixed = true;
289+
$this->checkNullables = true;
290+
$this->analyse([__DIR__ . '/data/bug-11518.php'], []);
291+
}
292+
286293
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace Bug11518;
4+
5+
/**
6+
* @param mixed[] $a
7+
* @return array{thing: mixed}
8+
* */
9+
function blah(array $a): array
10+
{
11+
if (!array_key_exists('thing', $a)) {
12+
$a['thing'] = 'bla';
13+
}
14+
15+
return $a;
16+
}

tests/PHPStan/Type/TypeCombinatorTest.php

+16-1
Original file line numberDiff line numberDiff line change
@@ -978,7 +978,7 @@ public function dataUnion(): iterable
978978
]),
979979
],
980980
IntersectionType::class,
981-
'array&hasOffset(\'foo\')',
981+
'array&hasOffsetValue(\'foo\', mixed)',
982982
],
983983
[
984984
[
@@ -2568,6 +2568,21 @@ public function dataUnion(): iterable
25682568
ClosureType::class,
25692569
'Closure(): mixed',
25702570
];
2571+
yield [
2572+
[
2573+
new IntersectionType([
2574+
new ArrayType(new MixedType(), new MixedType()),
2575+
new NonEmptyArrayType(),
2576+
new HasOffsetValueType(new ConstantStringType('thing'), new ConstantStringType('bla')),
2577+
]),
2578+
new IntersectionType([
2579+
new ArrayType(new MixedType(), new MixedType()),
2580+
new HasOffsetType(new ConstantStringType('thing')),
2581+
]),
2582+
],
2583+
IntersectionType::class,
2584+
'array&hasOffsetValue(\'thing\', mixed)',
2585+
];
25712586
}
25722587

25732588
/**

0 commit comments

Comments
 (0)