[Strict] Add support string|null|false on BooleanInBooleanNotRuleFixerRector (#2035)

* [Strict] Add support string|null|false on BooleanInBooleanNotRuleFixerRector

* fixed 🎉

* test in trat as non empty true as well

* add test for string and null only, tested with both TREAT_AS_NON_EMPTY true and false

* multi union types on DisallowedEmptyRuleFixerRector
This commit is contained in:
Abdul Malik Ikhsan 2022-04-10 13:01:19 +07:00 committed by GitHub
parent 76667332cf
commit 9b46906e39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 168 additions and 31 deletions

View File

@ -9811,8 +9811,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
```diff
class SomeClass
{
- public function run(string|null $name)
+ public function run(string $name)
public function run(string|null $name)
{
- if (! $name) {
+ if ($name === null) {

View File

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\Strict\Rector\BooleanNot\BooleanInBooleanNotRuleFixerRector\Fixture;
class StringNullAndFalse
{
public function run(string|null|false $name)
{
if (! $name) {
return 'no name';
}
return 'name';
}
}
?>
-----
<?php
declare(strict_types=1);
namespace Rector\Tests\Strict\Rector\BooleanNot\BooleanInBooleanNotRuleFixerRector\Fixture;
class StringNullAndFalse
{
public function run(string|null|false $name)
{
if ($name === '' || $name === false || $name === null) {
return 'no name';
}
return 'name';
}
}
?>

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Rector\Tests\Strict\Rector\BooleanNot\BooleanInBooleanNotRuleFixerRector\Fixture;
final class UnionWithStringNull
final class UnionWithNullFromParam
{
public function run(string|null $value)
{
@ -24,7 +24,7 @@ declare(strict_types=1);
namespace Rector\Tests\Strict\Rector\BooleanNot\BooleanInBooleanNotRuleFixerRector\Fixture;
final class UnionWithStringNull
final class UnionWithNullFromParam
{
public function run(string|null $value)
{

View File

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\Strict\Rector\BooleanNot\BooleanInBooleanNotRuleFixerRector\FixtureTreatAsNonEmpty;
class StringNullAndFalse
{
public function run(string|null|false $name)
{
if (! $name) {
return 'no name';
}
return 'name';
}
}
?>
-----
<?php
declare(strict_types=1);
namespace Rector\Tests\Strict\Rector\BooleanNot\BooleanInBooleanNotRuleFixerRector\FixtureTreatAsNonEmpty;
class StringNullAndFalse
{
public function run(string|null|false $name)
{
if ($name === '' || $name === false || $name === null) {
return 'no name';
}
return 'name';
}
}
?>

View File

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\Strict\Rector\BooleanNot\BooleanInBooleanNotRuleFixerRector\FixtureTreatAsNonEmpty;
class UnionWithNullFromParam
{
public function run(string|null $name)
{
if (! $name) {
return 'no name';
}
return 'name';
}
}
?>
-----
<?php
declare(strict_types=1);
namespace Rector\Tests\Strict\Rector\BooleanNot\BooleanInBooleanNotRuleFixerRector\FixtureTreatAsNonEmpty;
class UnionWithNullFromParam
{
public function run(string|null $name)
{
if ($name === null) {
return 'no name';
}
return 'name';
}
}
?>

View File

@ -26,7 +26,7 @@ final class MultiUnionTypes
*/
public function get($id)
{
return $id === [] || $id === 0 || $id === '';
return $id === [] || $id === 0 || $id === '' || $id === null;
}
}

View File

@ -18,6 +18,7 @@ use PhpParser\Node\Scalar\String_;
use PHPStan\Type\ArrayType;
use PHPStan\Type\BooleanType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\NullType;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
@ -50,6 +51,10 @@ final class ExactCompareFactory
return new Identical($expr, new Array_([]));
}
if ($exprType instanceof NullType) {
return new Identical($expr, $this->nodeFactory->createNull());
}
if (! $exprType instanceof UnionType) {
return null;
}
@ -113,8 +118,36 @@ final class ExactCompareFactory
$compareExprs[] = $this->createNotIdenticalFalsyCompare($unionedType, $expr, $treatAsNotEmpty);
}
/** @var ?Expr $truthyExpr */
return $this->resolveTruthyExpr($compareExprs);
}
/**
* @return array<Expr|null>
*/
private function collectCompareExprs(UnionType $unionType, Expr $expr, bool $treatAsNonEmpty): array
{
$compareExprs = [];
foreach ($unionType->getTypes() as $unionedType) {
$compareExprs[] = $this->createIdenticalFalsyCompare($unionedType, $expr, $treatAsNonEmpty);
}
return $compareExprs;
}
private function cleanUpPossibleNullableUnionType(UnionType $unionType): Type
{
return count($unionType->getTypes()) === 2
? TypeCombinator::removeNull($unionType)
: $unionType;
}
/**
* @param array<Expr|null> $compareExprs
*/
private function resolveTruthyExpr(array $compareExprs): ?Expr
{
$truthyExpr = array_shift($compareExprs);
foreach ($compareExprs as $compareExpr) {
if (! $compareExpr instanceof Expr) {
return null;
@ -124,7 +157,6 @@ final class ExactCompareFactory
return null;
}
/** @var Expr $compareExpr */
$truthyExpr = new BooleanOr($truthyExpr, $compareExpr);
}
@ -133,22 +165,11 @@ final class ExactCompareFactory
private function createTruthyFromUnionType(UnionType $unionType, Expr $expr, bool $treatAsNonEmpty): Expr|null
{
$unionType = TypeCombinator::removeNull($unionType);
$unionType = $this->cleanUpPossibleNullableUnionType($unionType);
if ($unionType instanceof UnionType) {
$compareExprs = [];
foreach ($unionType->getTypes() as $unionedType) {
$compareExprs[] = $this->createIdenticalFalsyCompare($unionedType, $expr, $treatAsNonEmpty);
}
/** @var Expr $truthyExpr */
$truthyExpr = array_shift($compareExprs);
foreach ($compareExprs as $compareExpr) {
/** @var Expr $compareExpr */
$truthyExpr = new BooleanOr($truthyExpr, $compareExpr);
}
return $truthyExpr;
$compareExprs = $this->collectCompareExprs($unionType, $expr, $treatAsNonEmpty);
return $this->resolveTruthyExpr($compareExprs);
}
if ($unionType instanceof BooleanType) {
@ -162,16 +183,16 @@ final class ExactCompareFactory
$toNullIdentical = new Identical($expr, $this->nodeFactory->createNull());
// assume we have to check empty string, integer and bools
if (! $treatAsNonEmpty) {
$scalarFalsyIdentical = $this->createIdenticalFalsyCompare($unionType, $expr, $treatAsNonEmpty);
if (! $scalarFalsyIdentical instanceof Expr) {
return null;
}
return new BooleanOr($toNullIdentical, $scalarFalsyIdentical);
if ($treatAsNonEmpty) {
return $toNullIdentical;
}
return $toNullIdentical;
// assume we have to check empty string, integer and bools
$scalarFalsyIdentical = $this->createIdenticalFalsyCompare($unionType, $expr, $treatAsNonEmpty);
if (! $scalarFalsyIdentical instanceof Expr) {
return null;
}
return new BooleanOr($toNullIdentical, $scalarFalsyIdentical);
}
}

View File

@ -52,7 +52,7 @@ CODE_SAMPLE
<<<'CODE_SAMPLE'
class SomeClass
{
public function run(string $name)
public function run(string|null $name)
{
if ($name === null) {
return 'no name';