Skip to content

Commit

Permalink
Support bool as template type bound
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Aug 22, 2021
1 parent 5a44c29 commit 9d191f9
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/Rules/Generics/TemplateTypeCheck.php
Expand Up @@ -7,6 +7,7 @@
use PHPStan\Rules\ClassCaseSensitivityCheck;
use PHPStan\Rules\ClassNameNodePair;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\BooleanType;
use PHPStan\Type\Generic\GenericObjectType;
use PHPStan\Type\Generic\TemplateType;
use PHPStan\Type\Generic\TemplateTypeScope;
Expand Down Expand Up @@ -110,6 +111,7 @@ public function check(
$boundClass === MixedType::class
|| $boundClass === StringType::class
|| $boundClass === IntegerType::class
|| $boundClass === BooleanType::class
|| $boundClass === ObjectWithoutClassType::class
|| $boundClass === ObjectType::class
|| $boundClass === GenericObjectType::class
Expand Down
54 changes: 54 additions & 0 deletions src/Type/Generic/TemplateBooleanType.php
@@ -0,0 +1,54 @@
<?php declare(strict_types = 1);

namespace PHPStan\Type\Generic;

use PHPStan\Type\BooleanType;
use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
use PHPStan\Type\Type;

/** @api */
final class TemplateBooleanType extends BooleanType implements TemplateType
{

/** @use TemplateTypeTrait<BooleanType> */
use TemplateTypeTrait;
use UndecidedComparisonCompoundTypeTrait;

public function __construct(
TemplateTypeScope $scope,
TemplateTypeStrategy $templateTypeStrategy,
TemplateTypeVariance $templateTypeVariance,
string $name,
BooleanType $bound
)
{
parent::__construct();
$this->scope = $scope;
$this->strategy = $templateTypeStrategy;
$this->variance = $templateTypeVariance;
$this->name = $name;
$this->bound = $bound;
}

public function traverse(callable $cb): Type
{
$newBound = $cb($this->getBound());
if ($this->getBound() !== $newBound && $newBound instanceof BooleanType) {
return new self(
$this->scope,
$this->strategy,
$this->variance,
$this->name,
$newBound
);
}

return $this;
}

protected function shouldGeneralizeInferredType(): bool
{
return false;
}

}
5 changes: 5 additions & 0 deletions src/Type/Generic/TemplateTypeFactory.php
Expand Up @@ -4,6 +4,7 @@

use PHPStan\PhpDoc\Tag\TemplateTag;
use PHPStan\Type\BenevolentUnionType;
use PHPStan\Type\BooleanType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
Expand Down Expand Up @@ -44,6 +45,10 @@ public static function create(TemplateTypeScope $scope, string $name, ?Type $bou
return new TemplateIntegerType($scope, $strategy, $variance, $name, $bound);
}

if ($bound instanceof BooleanType && ($boundClass === BooleanType::class || $bound instanceof TemplateType)) {
return new TemplateBooleanType($scope, $strategy, $variance, $name, $bound);
}

if ($bound instanceof MixedType && ($boundClass === MixedType::class || $bound instanceof TemplateType)) {
return new TemplateMixedType($scope, $strategy, $variance, $name, $bound);
}
Expand Down
17 changes: 17 additions & 0 deletions tests/PHPStan/Analyser/data/generics.php
Expand Up @@ -1405,3 +1405,20 @@ function (): void {
$array = ['a' => 1, 'b' => 2];
assertType('array(\'a\' => int, \'b\' => int)', a($array));
};


/**
* @template T of bool
* @param T $b
* @return T
*/
function boolBound(bool $b): bool
{
return $b;
}

function (bool $b): void {
assertType('true', boolBound(true));
assertType('false', boolBound(false));
assertType('bool', boolBound($b));
};
6 changes: 6 additions & 0 deletions tests/PHPStan/Rules/Generics/data/function-template.php
Expand Up @@ -33,3 +33,9 @@ function lorem()
{

}

/** @template T of bool */
function ipsum()
{

}

0 comments on commit 9d191f9

Please sign in to comment.