Skip to content

Commit

Permalink
Fix array intersection between HasOffsetType and HasOffsetValueType
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Aug 19, 2024
1 parent aa05a7d commit 07d6405
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 2 deletions.
12 changes: 11 additions & 1 deletion phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -1579,7 +1579,7 @@ parameters:

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

-
Expand Down Expand Up @@ -1622,6 +1622,16 @@ parameters:
count: 1
path: src/Type/TypeCombinator.php

-
message: "#^Instanceof between PHPStan\\\\Type\\\\Constant\\\\ConstantIntegerType and PHPStan\\\\Type\\\\Constant\\\\ConstantIntegerType will always evaluate to true\\.$#"
count: 1
path: src/Type/TypeCombinator.php

-
message: "#^Result of \\|\\| is always true\\.$#"
count: 1
path: src/Type/TypeCombinator.php

-
message: "#^Doing instanceof PHPStan\\\\Type\\\\ArrayType is error\\-prone and deprecated\\. Use Type\\:\\:isArray\\(\\) or Type\\:\\:getArrays\\(\\) instead\\.$#"
count: 3
Expand Down
6 changes: 6 additions & 0 deletions src/Type/TypeCombinator.php
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,12 @@ private static function processArrayAccessoryTypes(array $arrayTypes): array
if (!($innerType instanceof AccessoryType) && !($innerType instanceof CallableType)) {
continue;
}
if ($innerType instanceof HasOffsetType) {
$offset = $innerType->getOffsetType();
if ($offset instanceof ConstantStringType || $offset instanceof ConstantIntegerType) {
$innerType = new HasOffsetValueType($offset, $arrayType->getIterableValueType());
}
}
if ($innerType instanceof HasOffsetValueType) {
$accessoryTypes[sprintf('hasOffsetValue(%s)', $innerType->getOffsetType()->describe(VerbosityLevel::cache()))][$i] = $innerType;
continue;
Expand Down
23 changes: 23 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-11518-types.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Bug11518Types;

use function PHPStan\Testing\assertType;

/**
* @param mixed[] $a
* @return array{thing: mixed}
* */
function blah(array $a): array
{
if (!array_key_exists('thing', $a)) {
$a['thing'] = 'bla';
assertType('hasOffsetValue(\'thing\', \'bla\')&non-empty-array', $a);
} else {
assertType('array&hasOffset(\'thing\')', $a);
}

assertType('array&hasOffsetValue(\'thing\', mixed)', $a);

return $a;
}
7 changes: 7 additions & 0 deletions tests/PHPStan/Rules/Functions/ReturnTypeRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -283,4 +283,11 @@ public function testBug10732(): void
$this->analyse([__DIR__ . '/data/bug-10732.php'], []);
}

public function testBug11518(): void
{
$this->checkExplicitMixed = true;
$this->checkNullables = true;
$this->analyse([__DIR__ . '/data/bug-11518.php'], []);
}

}
16 changes: 16 additions & 0 deletions tests/PHPStan/Rules/Functions/data/bug-11518.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Bug11518;

/**
* @param mixed[] $a
* @return array{thing: mixed}
* */
function blah(array $a): array
{
if (!array_key_exists('thing', $a)) {
$a['thing'] = 'bla';
}

return $a;
}
17 changes: 16 additions & 1 deletion tests/PHPStan/Type/TypeCombinatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -978,7 +978,7 @@ public function dataUnion(): iterable
]),
],
IntersectionType::class,
'array&hasOffset(\'foo\')',
'array&hasOffsetValue(\'foo\', mixed)',
],
[
[
Expand Down Expand Up @@ -2568,6 +2568,21 @@ public function dataUnion(): iterable
ClosureType::class,
'Closure(): mixed',
];
yield [
[
new IntersectionType([
new ArrayType(new MixedType(), new MixedType()),
new NonEmptyArrayType(),
new HasOffsetValueType(new ConstantStringType('thing'), new ConstantStringType('bla')),
]),
new IntersectionType([
new ArrayType(new MixedType(), new MixedType()),
new HasOffsetType(new ConstantStringType('thing')),
]),
],
IntersectionType::class,
'array&hasOffsetValue(\'thing\', mixed)',
];
}

/**
Expand Down

0 comments on commit 07d6405

Please sign in to comment.