Skip to content

Commit

Permalink
Fixed generalizing arrays in scope, simplified offset unset in Consta…
Browse files Browse the repository at this point in the history
…ntArrayType
  • Loading branch information
ondrejmirtes committed Aug 25, 2022
1 parent 0b9f1c0 commit 6173110
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 19 deletions.
1 change: 1 addition & 0 deletions src/Analyser/MutatingScope.php
Expand Up @@ -4638,6 +4638,7 @@ private static function generalizeType(Type $a, Type $b): Type
$constantArraysA->getOffsetValueType($keyType),
$constantArraysB->getOffsetValueType($keyType),
),
!$constantArraysA->hasOffsetValueType($keyType)->and($constantArraysB->hasOffsetValueType($keyType))->negate()->no(),
);
}

Expand Down
47 changes: 29 additions & 18 deletions src/Type/Constant/ConstantArrayType.php
Expand Up @@ -575,20 +575,12 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni
public function unsetOffset(Type $offsetType): Type
{
$offsetType = ArrayType::castToArrayKeyType($offsetType);

$results = [];
foreach (TypeUtils::getConstantScalars($offsetType) as $constantScalar) {
if (!$constantScalar instanceof ConstantIntegerType && !$constantScalar instanceof ConstantStringType) {
continue;
}

$hasKey = false;
if ($offsetType instanceof ConstantIntegerType || $offsetType instanceof ConstantStringType) {
foreach ($this->keyTypes as $i => $keyType) {
if ($keyType->getValue() !== $constantScalar->getValue()) {
if ($keyType->getValue() !== $offsetType->getValue()) {
continue;
}

$hasKey = true;
$keyTypes = $this->keyTypes;
unset($keyTypes[$i]);
$valueTypes = $this->valueTypes;
Expand All @@ -608,17 +600,36 @@ public function unsetOffset(Type $offsetType): Type
$k++;
}

$results[] = new self($newKeyTypes, $newValueTypes, $this->nextAutoIndexes, $newOptionalKeys);
break;
}
if ($hasKey) {
continue;
return new self($newKeyTypes, $newValueTypes, $this->nextAutoIndexes, $newOptionalKeys);
}

$results[] = $this;
return $this;
}
if ($results !== []) {
return TypeCombinator::union(...$results);

$constantScalars = TypeUtils::getConstantScalars($offsetType);
if (count($constantScalars) > 0) {
$optionalKeys = $this->optionalKeys;

foreach ($constantScalars as $constantScalar) {
$constantScalar = ArrayType::castToArrayKeyType($constantScalar);
if (!$constantScalar instanceof ConstantIntegerType && !$constantScalar instanceof ConstantStringType) {
continue;
}

foreach ($this->keyTypes as $i => $keyType) {
if ($keyType->getValue() !== $constantScalar->getValue()) {
continue;
}

if (in_array($i, $optionalKeys, true)) {
continue 2;
}

$optionalKeys[] = $i;
}
}

return new self($this->keyTypes, $this->valueTypes, $this->nextAutoIndexes, $optionalKeys);
}

return new ArrayType($this->getKeyType(), $this->getItemType());
Expand Down
1 change: 1 addition & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Expand Up @@ -982,6 +982,7 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7809.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/composer-non-empty-array-after-unset.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Arrays/data/bug-6000.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/prestashop-breakdowns-empty-array.php');
}

/**
Expand Down
41 changes: 41 additions & 0 deletions tests/PHPStan/Analyser/data/prestashop-breakdowns-empty-array.php
@@ -0,0 +1,41 @@
<?php

namespace PrestashopBreakdownsEmptyArray;

use function PHPStan\Testing\assertType;

class Foo
{

/**
* @param mixed[] $arrayMixed
*/
public function getTaxBreakdown($mixed, $arrayMixed): void
{
$breakdowns = [
'product_tax' => $mixed,
'shipping_tax' => $arrayMixed,
'ecotax_tax' => $arrayMixed,
'wrapping_tax' => $arrayMixed,
];

foreach ($breakdowns as $type => $bd) {
if (empty($bd)) {
assertType('array{product_tax?: mixed, shipping_tax?: array, ecotax_tax?: array, wrapping_tax?: array}', $breakdowns);
unset($breakdowns[$type]);
assertType('array{product_tax?: mixed, shipping_tax?: array, ecotax_tax?: array, wrapping_tax?: array}', $breakdowns);
}
}

assertType('array{product_tax?: mixed, shipping_tax?: array, ecotax_tax?: array, wrapping_tax?: array}', $breakdowns);
}

public function doFoo(): void
{
$a = ['foo' => 1, 'bar' => 2];
assertType('array{foo: 1, bar: 2}', $a);
unset($a['foo']);
assertType('array{bar: 2}', $a);
}

}
Expand Up @@ -42,7 +42,7 @@ public function doBaz(): void
}
}

assertType('array{}|array{a?: bool, b?: numeric-string, c?: int<-1, 1>, d?: int<0, 1>}', $breakdowns);
assertType('array{a?: bool, b?: numeric-string, c?: int<-1, 1>, d?: int<0, 1>}', $breakdowns);
}

}

0 comments on commit 6173110

Please sign in to comment.