Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various type-related fixes #341

Merged
merged 3 commits into from Oct 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/Type/Accessory/NonEmptyArrayType.php
Expand Up @@ -46,6 +46,10 @@ public function isSuperTypeOf(Type $type): TrinaryLogic
return TrinaryLogic::createYes();
}

if ($type instanceof CompoundType) {
return $type->isSubTypeOf($this);
}

return (new ArrayType(new MixedType(), new MixedType()))
->isSuperTypeOf($type)
->and($type->isIterableAtLeastOnce());
Expand Down
17 changes: 17 additions & 0 deletions src/Type/Generic/GenericClassStringType.php
Expand Up @@ -156,6 +156,23 @@ public function getReferencedTemplateTypes(TemplateTypeVariance $positionVarianc
return $this->type->getReferencedTemplateTypes($variance);
}

public function equals(Type $type): bool
{
if (!$type instanceof self) {
return false;
}

if (!parent::equals($type)) {
return false;
}

if (!$this->type->equals($type->type)) {
return false;
}

return true;
}

/**
* @param mixed[] $properties
* @return Type
Expand Down
5 changes: 5 additions & 0 deletions src/Type/IterableType.php
Expand Up @@ -34,6 +34,11 @@ public function __construct(
$this->itemType = $itemType;
}

public function getKeyType(): Type
{
return $this->keyType;
}

public function getItemType(): Type
{
return $this->itemType;
Expand Down
17 changes: 17 additions & 0 deletions src/Type/TypeCombinator.php
Expand Up @@ -738,11 +738,28 @@ public static function intersect(Type ...$types): Type
if ($types[$i] instanceof ConstantArrayType && $types[$j] instanceof HasOffsetType) {
$types[$i] = $types[$i]->makeOffsetRequired($types[$j]->getOffsetType());
array_splice($types, $j--, 1);
continue;
}

if ($types[$j] instanceof ConstantArrayType && $types[$i] instanceof HasOffsetType) {
$types[$j] = $types[$j]->makeOffsetRequired($types[$i]->getOffsetType());
array_splice($types, $i--, 1);
continue 2;
}

if (
($types[$i] instanceof ArrayType || $types[$i] instanceof IterableType) &&
($types[$j] instanceof ArrayType || $types[$j] instanceof IterableType)
) {
$keyType = self::intersect($types[$i]->getKeyType(), $types[$j]->getKeyType());
$itemType = self::intersect($types[$i]->getItemType(), $types[$j]->getItemType());
if ($types[$i] instanceof IterableType && $types[$j] instanceof IterableType) {
$types[$j] = new IterableType($keyType, $itemType);
} else {
$types[$j] = new ArrayType($keyType, $itemType);
}
array_splice($types, $i--, 1);
continue 2;
}

continue;
Expand Down
50 changes: 50 additions & 0 deletions tests/PHPStan/Type/Generic/GenericClassStringTypeTest.php
Expand Up @@ -273,4 +273,54 @@ public function testAccepts(
);
}

public function dataEquals(): array
{
return [
[
new GenericClassStringType(new ObjectType(\Exception::class)),
new GenericClassStringType(new ObjectType(\Exception::class)),
true,
],
[
new GenericClassStringType(new ObjectType(\Exception::class)),
new GenericClassStringType(new ObjectType(\stdClass::class)),
false,
],
[
new GenericClassStringType(new StaticType(\Exception::class)),
new GenericClassStringType(new StaticType(\Exception::class)),
true,
],
[
new GenericClassStringType(new StaticType(\Exception::class)),
new GenericClassStringType(new StaticType(\stdClass::class)),
false,
],
];
}

/**
* @dataProvider dataEquals
*/
public function testEquals(GenericClassStringType $type, Type $otherType, bool $expected): void
{
$verbosityLevel = VerbosityLevel::precise();
$typeDescription = $type->describe($verbosityLevel);
$otherTypeDescription = $otherType->describe($verbosityLevel);

$actual = $type->equals($otherType);
$this->assertSame(
$expected,
$actual,
sprintf('%s -> equals(%s)', $typeDescription, $otherTypeDescription)
);

$actual = $otherType->equals($type);
$this->assertSame(
$expected,
$actual,
sprintf('%s -> equals(%s)', $otherTypeDescription, $typeDescription)
);
}

}
55 changes: 53 additions & 2 deletions tests/PHPStan/Type/TypeCombinatorTest.php
Expand Up @@ -2002,8 +2002,48 @@ public function dataIntersect(): array
new ArrayType(new MixedType(), new MixedType()),
new IterableType(new MixedType(), new StringType()),
],
IntersectionType::class,
'array&iterable<string>', // this is correct but 'array<string>' would be better
ArrayType::class,
'array<string>',
],
[
[
new ArrayType(new IntegerType(), new MixedType()),
new IterableType(new MixedType(), new StringType()),
],
ArrayType::class,
'array<int, string>',
],
[
[
new ArrayType(new IntegerType(), new MixedType()),
new IterableType(new StringType(), new MixedType()),
],
NeverType::class,
'*NEVER*',
],
[
[
new ArrayType(new MixedType(), new IntegerType()),
new IterableType(new MixedType(), new StringType()),
],
NeverType::class,
'*NEVER*',
],
[
[
new ArrayType(new IntegerType(), new MixedType()),
new ArrayType(new MixedType(), new StringType()),
],
ArrayType::class,
'array<int, string>',
],
[
[
new IterableType(new IntegerType(), new MixedType()),
new IterableType(new MixedType(), new StringType()),
],
IterableType::class,
'iterable<int, string>',
],
[
[
Expand Down Expand Up @@ -2886,6 +2926,17 @@ public function dataIntersect(): array
NeverType::class,
'*NEVER*',
],
[
[
new IntersectionType([
new ArrayType(new StringType(), new IntegerType()),
new NonEmptyArrayType(),
]),
new NeverType(),
],
NeverType::class,
'*NEVER*',
],
];
}

Expand Down