Skip to content

Commit

Permalink
Keep iterable key type and value type when subtracting from iterable
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Feb 22, 2021
1 parent 3b109a7 commit c1f7aaf
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 5 deletions.
11 changes: 8 additions & 3 deletions src/Type/TypeCombinator.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use PHPStan\Type\Constant\ConstantFloatType;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\Generic\GenericObjectType;
use PHPStan\Type\Generic\TemplateType;

class TypeCombinator
Expand Down Expand Up @@ -61,13 +62,17 @@ public static function remove(Type $fromType, Type $typeToRemove): Type
return new ConstantBooleanType(!$typeToRemove->getValue());
}
} elseif ($fromType instanceof IterableType) {
$traversableType = new ObjectType(\Traversable::class);
$arrayType = new ArrayType(new MixedType(), new MixedType());
if ($typeToRemove->isSuperTypeOf($arrayType)->yes()) {
return $traversableType;
return new GenericObjectType(\Traversable::class, [
$fromType->getIterableKeyType(),
$fromType->getIterableValueType(),
]);
}

$traversableType = new ObjectType(\Traversable::class);
if ($typeToRemove->isSuperTypeOf($traversableType)->yes()) {
return $arrayType;
return new ArrayType($fromType->getIterableKeyType(), $fromType->getIterableValueType());
}
} elseif ($fromType instanceof IntegerRangeType) {
$type = $fromType->tryRemove($typeToRemove);
Expand Down
6 changes: 6 additions & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10918,6 +10918,11 @@ public function dataBugInstanceOfClassString(): array
return $this->gatherAssertTypes(__DIR__ . '/data/instanceof-class-string.php');
}

public function dataBug4498(): array
{
return $this->gatherAssertTypes(__DIR__ . '/data/bug-4498.php');
}

/**
* @param string $file
* @return array<string, mixed[]>
Expand Down Expand Up @@ -11163,6 +11168,7 @@ private function gatherAssertTypes(string $file): array
* @dataProvider dataBug3321
* @dataProvider dataBug3769
* @dataProvider dataBugInstanceOfClassString
* @dataProvider dataBug4498
* @param string $assertType
* @param string $file
* @param mixed ...$args
Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Analyser/data/bug-1233.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public function toArray($value): array
assertType('mixed~array', $value);

if (is_iterable($value)) {
assertType('Traversable', $value);
assertType('Traversable<mixed, mixed>', $value);
return iterator_to_array($value);
}

Expand Down
50 changes: 50 additions & 0 deletions tests/PHPStan/Analyser/data/bug-4498.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace Bug4498;

use function PHPStan\Analyser\assertType;

class Foo
{

/**
* @param iterable<TKey, TValue> $iterable
*
* @return iterable<TKey, TValue>
*
* @template TKey
* @template TValue
*/
public function fcn(iterable $iterable): iterable
{
if ($iterable instanceof \Traversable) {
assertType('iterable<TKey (method Bug4498\Foo::fcn(), argument), TValue (method Bug4498\Foo::fcn(), argument)>&Traversable', $iterable);
return $iterable;
}

assertType('array<TKey (method Bug4498\Foo::fcn(), argument), TValue (method Bug4498\Foo::fcn(), argument)>', $iterable);

return $iterable;
}

/**
* @param iterable<TKey, TValue> $iterable
*
* @return iterable<TKey, TValue>
*
* @template TKey
* @template TValue
*/
public function bar(iterable $iterable): iterable
{
if (is_array($iterable)) {
assertType('array<TKey (method Bug4498\Foo::bar(), argument), TValue (method Bug4498\Foo::bar(), argument)>', $iterable);
return $iterable;
}

assertType('Traversable<TKey (method Bug4498\Foo::bar(), argument), TValue (method Bug4498\Foo::bar(), argument)>', $iterable);

return $iterable;
}

}
8 changes: 8 additions & 0 deletions tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1862,4 +1862,12 @@ public function testBug3321(): void
$this->analyse([__DIR__ . '/../../Analyser/data/bug-3321.php'], []);
}

public function testBug4498(): void
{
$this->checkThisOnly = false;
$this->checkNullables = true;
$this->checkUnionTypes = true;
$this->analyse([__DIR__ . '/../../Analyser/data/bug-4498.php'], []);
}

}
2 changes: 1 addition & 1 deletion tests/PHPStan/Type/TypeCombinatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3176,7 +3176,7 @@ public function dataRemove(): array
new IterableType(new MixedType(), new MixedType()),
new ArrayType(new MixedType(), new MixedType()),
ObjectType::class,
'Traversable',
'Traversable<mixed, mixed>',
],
[
new IterableType(new MixedType(), new MixedType()),
Expand Down

0 comments on commit c1f7aaf

Please sign in to comment.