Skip to content

Commit

Permalink
[TypeDeclaration] Add false and true in union support on ReturnUnionT…
Browse files Browse the repository at this point in the history
…ypeRector (#5355)

* [TypeDeclaration] Add false and true in union on ReturnUnionTypeRector

* Fix

* failing fixture

* fixing true and false exists in union

* [ci-review] Rector Rectify

* fix

* fixture fix

* fix namespace

* reduce complexity

* fix phpstan

---------

Co-authored-by: GitHub Action <actions@github.com>
  • Loading branch information
samsonasik and actions-user committed Jan 3, 2024
1 parent 5c03979 commit 09c077e
Show file tree
Hide file tree
Showing 11 changed files with 204 additions and 7 deletions.
1 change: 1 addition & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ parameters:
- src/PhpDocParser/PhpDocParser/PhpDocNodeTraverser.php
- rules/Php70/EregToPcreTransformer.php
- src/BetterPhpDocParser/PhpDocManipulator/PhpDocClassRenamer.php
- src/NodeTypeResolver/PHPStan/Type/TypeFactory.php

# known types
- '#Parameter (.*?) expects PhpParser\\Node, PhpParser\\Node\|null given#'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnUnionTypeRector\

final class MultiScalar
{
public function run($value): bool|string
public function run($value): false|string
{
if ($value) {
return false;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnUnionTypeRector\Fixture;

/**
* true|othertype cannot work on <=php 8.1, ref https://3v4l.org/UJqXT
*/
final class TrueInUnionBecomeBool
{
public function run($value)
{
if ($value) {
return true;
}

return substr('warning', 1);
}
}

?>
-----
<?php

namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnUnionTypeRector\Fixture;

/**
* true|othertype cannot work on <=php 8.1, ref https://3v4l.org/UJqXT
*/
final class TrueInUnionBecomeBool
{
public function run($value): bool|string
{
if ($value) {
return true;
}

return substr('warning', 1);
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnUnionTypeRector\FixtureTrueInUnion;

final class TrueFalseInUnion
{
public function run($value)
{
if ($value) {
return true;
}

if (rand(0, 1)) {
return false;
}

return substr('warning', 1);
}
}

?>
-----
<?php

namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnUnionTypeRector\FixtureTrueInUnion;

final class TrueFalseInUnion
{
public function run($value): bool|string
{
if ($value) {
return true;
}

if (rand(0, 1)) {
return false;
}

return substr('warning', 1);
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnUnionTypeRector\FixtureTrueInUnion;

/**
* true|othertype work on >= php 8.2, ref https://3v4l.org/UJqXT
*/
final class TrueInUnion
{
public function run($value)
{
if ($value) {
return true;
}

return substr('warning', 1);
}
}

?>
-----
<?php

namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnUnionTypeRector\FixtureTrueInUnion;

/**
* true|othertype work on >= php 8.2, ref https://3v4l.org/UJqXT
*/
final class TrueInUnion
{
public function run($value): true|string
{
if ($value) {
return true;
}

return substr('warning', 1);
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnUnionTypeRector;

use Iterator;
use PHPUnit\Framework\Attributes\DataProvider;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

final class TrueInUnionTest extends AbstractRectorTestCase
{
#[DataProvider('provideData')]
public function test(string $filePath): void
{
$this->doTestFile($filePath);
}

public static function provideData(): Iterator
{
return self::yieldFilesFromDirectory(__DIR__ . '/FixtureTrueInUnion');
}

public function provideConfigFilePath(): string
{
return __DIR__ . '/config/configured_rule_true_in_union.php';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\ValueObject\PhpVersionFeature;
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnUnionTypeRector;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rule(ReturnUnionTypeRector::class);
$rectorConfig->phpVersion(PhpVersionFeature::UNION_TYPES);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\ValueObject\PhpVersionFeature;
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnUnionTypeRector;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rule(ReturnUnionTypeRector::class);
$rectorConfig->phpVersion(PhpVersionFeature::NULL_FALSE_TRUE_STANDALONE_TYPE);
};
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public function inferFunctionLike(FunctionLike $functionLike): Type
$types[] = new VoidType();
}

return $this->typeFactory->createMixedPassedOrUnionType($types);
return $this->typeFactory->createMixedPassedOrUnionTypeAndKeepConstant($types);
}

/**
Expand Down
37 changes: 33 additions & 4 deletions src/NodeTypeResolver/PHPStan/Type/TypeFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@ public function uniquateTypes(array $types, bool $keepConstant = false): array
$uniqueTypes = [];
$totalTypes = count($types);

$hasFalse = false;
$hasTrue = false;
foreach ($types as $type) {
if ($totalTypes > 1 && $type instanceof ObjectWithoutClassTypeWithParentTypes) {
$parents = $type->getParentTypes();
$type = new ObjectType($parents[0]->getClassName());
}
$type = $this->normalizeObjectType($totalTypes, $type);
$type = $this->normalizeBooleanType($hasFalse, $hasTrue, $type);

$removedConstantType = $this->removeValueFromConstantType($type);
$removedConstantTypeHash = $this->typeHasher->createTypeHash($removedConstantType);
Expand All @@ -92,6 +92,35 @@ public function uniquateTypes(array $types, bool $keepConstant = false): array
return array_values($uniqueTypes);
}

private function normalizeObjectType(int $totalTypes, Type $type): Type
{
if ($totalTypes > 1 && $type instanceof ObjectWithoutClassTypeWithParentTypes) {
$parents = $type->getParentTypes();
return new ObjectType($parents[0]->getClassName());
}

return $type;
}

private function normalizeBooleanType(bool &$hasFalse, bool &$hasTrue, Type $type): Type
{
if ($type instanceof ConstantBooleanType) {
if ($type->getValue()) {
$hasTrue = true;
}

if ($type->getValue() === false) {
$hasFalse = true;
}
}

if ($hasFalse && $hasTrue && $type instanceof ConstantBooleanType) {
return new BooleanType();
}

return $type;
}

/**
* @param Type[] $types
* @return Type[]
Expand Down
2 changes: 1 addition & 1 deletion src/NodeTypeResolver/PHPStan/TypeHasher.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public function createTypeHash(Type $type): string
}

if ($type instanceof ConstantType) {
return $type::class . $type->getValue();
return $type::class;
}

if ($type instanceof UnionType) {
Expand Down

0 comments on commit 09c077e

Please sign in to comment.