[Strict] Add parameter to require non-empty values opt-in (#956)

This commit is contained in:
Tomas Votruba 2021-10-05 17:37:14 +02:00 committed by GitHub
parent d4cb2395dc
commit ed80bec474
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 604 additions and 119 deletions

View File

@ -59,7 +59,7 @@ jobs:
## First run Rector - here can't be --dry-run !!! it would stop the job with it and not commit anything in the future
- run: bin/rector process ${{ matrix.paths }} --ansi --no-progress-bar
- run: vendor/bin/ecs check --match-git-diff --fix --ansi
- run: vendor/bin/ecs check --fix --ansi
# see https://github.com/EndBug/add-and-commit
-

View File

@ -9782,15 +9782,34 @@ Rename file to respect class name
Fixer for PHPStan reports by strict type rule - "PHPStan\Rules\BooleansInConditions\BooleanInBooleanNotRule"
:wrench: **configure it!**
- class: [`Rector\Strict\Rector\BooleanNot\BooleanInBooleanNotRuleFixerRector`](../rules/Strict/Rector/BooleanNot/BooleanInBooleanNotRuleFixerRector.php)
```php
use Rector\Strict\Rector\BooleanNot\BooleanInBooleanNotRuleFixerRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(BooleanInBooleanNotRuleFixerRector::class)
->call('configure', [[
BooleanInBooleanNotRuleFixerRector::TREAT_AS_NON_EMPTY => true,
]]);
};
```
```diff
class SomeClass
{
public function run(string $name)
- public function run(string|null $name)
+ public function run(string $name)
{
- if (! $name) {
+ if ($name === '') {
+ if ($name === null) {
return 'no name';
}
@ -9805,8 +9824,26 @@ Fixer for PHPStan reports by strict type rule - "PHPStan\Rules\BooleansInConditi
Fixer for PHPStan reports by strict type rule - "PHPStan\Rules\BooleansInConditions\BooleanInIfConditionRule"
:wrench: **configure it!**
- class: [`Rector\Strict\Rector\If_\BooleanInIfConditionRuleFixerRector`](../rules/Strict/Rector/If_/BooleanInIfConditionRuleFixerRector.php)
```php
use Rector\Strict\Rector\If_\BooleanInIfConditionRuleFixerRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(BooleanInIfConditionRuleFixerRector::class)
->call('configure', [[
BooleanInIfConditionRuleFixerRector::TREAT_AS_NON_EMPTY => false,
]]);
};
```
```diff
final class NegatedString
{
@ -9828,8 +9865,26 @@ Fixer for PHPStan reports by strict type rule - "PHPStan\Rules\BooleansInConditi
Fixer for PHPStan reports by strict type rule - "PHPStan\Rules\BooleansInConditions\BooleanInTernaryOperatorRule"
:wrench: **configure it!**
- class: [`Rector\Strict\Rector\Ternary\BooleanInTernaryOperatorRuleFixerRector`](../rules/Strict/Rector/Ternary/BooleanInTernaryOperatorRuleFixerRector.php)
```php
use Rector\Strict\Rector\Ternary\BooleanInTernaryOperatorRuleFixerRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(BooleanInTernaryOperatorRuleFixerRector::class)
->call('configure', [[
BooleanInTernaryOperatorRuleFixerRector::TREAT_AS_NON_EMPTY => false,
]]);
};
```
```diff
final class ArrayCompare
{
@ -9847,8 +9902,26 @@ Fixer for PHPStan reports by strict type rule - "PHPStan\Rules\BooleansInConditi
Fixer for PHPStan reports by strict type rule - "PHPStan\Rules\DisallowedConstructs\DisallowedEmptyRule"
:wrench: **configure it!**
- class: [`Rector\Strict\Rector\Empty_\DisallowedEmptyRuleFixerRector`](../rules/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector.php)
```php
use Rector\Strict\Rector\Empty_\DisallowedEmptyRuleFixerRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(DisallowedEmptyRuleFixerRector::class)
->call('configure', [[
DisallowedEmptyRuleFixerRector::TREAT_AS_NON_EMPTY => false,
]]);
};
```
```diff
final class SomeEmptyArray
{
@ -9866,8 +9939,26 @@ Fixer for PHPStan reports by strict type rule - "PHPStan\Rules\DisallowedConstru
Fixer for PHPStan reports by strict type rule - "PHPStan\Rules\DisallowedConstructs\DisallowedShortTernaryRule"
:wrench: **configure it!**
- class: [`Rector\Strict\Rector\Ternary\DisallowedShortTernaryRuleFixerRector`](../rules/Strict/Rector/Ternary/DisallowedShortTernaryRuleFixerRector.php)
```php
use Rector\Strict\Rector\Ternary\DisallowedShortTernaryRuleFixerRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(DisallowedShortTernaryRuleFixerRector::class)
->call('configure', [[
DisallowedShortTernaryRuleFixerRector::TREAT_AS_NON_EMPTY => false,
]]);
};
```
```diff
final class ShortTernaryArray
{

View File

@ -501,3 +501,13 @@ parameters:
# scope & mutating scope mish-mash
- '#Parameter \#4 \$nodeCallback of method PHPStan\\Analyser\\NodeScopeResolver\:\:processStmtNodes\(\) expects callable\(PhpParser\\Node, PHPStan\\Analyser\\Scope\)\: void, Closure\(PhpParser\\Node, PHPStan\\Analyser\\MutatingScope\)\: void given#'
# share configuration to avoid duplication in 5 rules
-
message: '#Instead of abstract class, use specific service with composition#'
path: rules/Strict/Rector/AbstractFalsyScalarRuleFixerRector.php
# false positive - configured parmaeter
-
message: '#Property with protected modifier is not allowed\. Use interface contract method instead#'
path: rules/Strict/Rector/AbstractFalsyScalarRuleFixerRector.php

View File

@ -2,7 +2,7 @@
namespace Rector\Tests\CodeQuality\Rector\Expression\InlineIfToExplicitIfRector\Fixture;
class BooleanOr
class SomeBooleanOr
{
public function run()
{
@ -17,7 +17,7 @@ class BooleanOr
namespace Rector\Tests\CodeQuality\Rector\Expression\InlineIfToExplicitIfRector\Fixture;
class BooleanOr
class SomeBooleanOr
{
public function run()
{

View File

@ -4,7 +4,7 @@ namespace Rector\Tests\EarlyReturn\Rector\If_\ChangeNestedIfsToEarlyReturnRector
use Rector\NodeTypeResolver\Node\AttributeKey;
class BooleanAnd
class AnotherBooleanAnd
{
private function isPreviousExpressionVisuallySimilar(Expression $previousExpression, Node $previousNode): bool
{
@ -27,7 +27,7 @@ namespace Rector\Tests\EarlyReturn\Rector\If_\ChangeNestedIfsToEarlyReturnRector
use Rector\NodeTypeResolver\Node\AttributeKey;
class BooleanAnd
class AnotherBooleanAnd
{
private function isPreviousExpressionVisuallySimilar(Expression $previousExpression, Node $previousNode): bool
{

View File

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

View File

@ -38,7 +38,7 @@ final class UnionWithNull
public function run()
{
if ($this->value === null) {
if ($this->value === null || $this->value === '') {
return 'empty';
}

View File

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

View File

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\Strict\Rector\BooleanNot\BooleanInBooleanNotRuleFixerRector\FixtureTreatAsNonEmpty;
final class UnionWithNull
{
/**
* @var string|null
*/
private $value;
public function run()
{
if (! $this->value) {
return 'empty';
}
return 'set';
}
}
?>
-----
<?php
declare(strict_types=1);
namespace Rector\Tests\Strict\Rector\BooleanNot\BooleanInBooleanNotRuleFixerRector\FixtureTreatAsNonEmpty;
final class UnionWithNull
{
/**
* @var string|null
*/
private $value;
public function run()
{
if ($this->value === null) {
return 'empty';
}
return 'set';
}
}
?>

View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\Strict\Rector\BooleanNot\BooleanInBooleanNotRuleFixerRector;
use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;
final class TreatAsNonEmptyRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
{
$this->doTestFileInfo($fileInfo);
}
/**
* @return Iterator<SmartFileInfo>
*/
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureTreatAsNonEmpty');
}
public function provideConfigFilePath(): string
{
return __DIR__ . '/config/treat_as_non_empty.php';
}
}

View File

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
use Rector\Strict\Rector\BooleanNot\BooleanInBooleanNotRuleFixerRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(BooleanInBooleanNotRuleFixerRector::class)
->call('configure', [[
BooleanInBooleanNotRuleFixerRector::TREAT_AS_NON_EMPTY => true,
]]);
};

View File

@ -0,0 +1,27 @@
<?php
namespace Rector\Tests\Strict\Rector\Empty_\DisallowedEmptyRuleFixerRector\Fixture;
final class EmptyStringNullable
{
public function run(string|null $value)
{
return empty($value);
}
}
?>
-----
<?php
namespace Rector\Tests\Strict\Rector\Empty_\DisallowedEmptyRuleFixerRector\Fixture;
final class EmptyStringNullable
{
public function run(string|null $value)
{
return $value === null || $value === '';
}
}
?>

View File

@ -2,7 +2,7 @@
namespace Rector\Tests\Strict\Rector\Empty_\DisallowedEmptyRuleFixerRector\Fixture;
final class SkipMultiUnionTypes
final class MultiUnionTypes
{
/**
* @param int|int[]|null|string $id
@ -12,3 +12,22 @@ final class SkipMultiUnionTypes
return empty($id);
}
}
?>
-----
<?php
namespace Rector\Tests\Strict\Rector\Empty_\DisallowedEmptyRuleFixerRector\Fixture;
final class MultiUnionTypes
{
/**
* @param int|int[]|null|string $id
*/
public function get($id)
{
return $id === [] || $id === 0 || $id === '';
}
}
?>

View File

@ -20,7 +20,7 @@ final class UnionObjectNullable
{
public function run(null|\DateTime $dateTime)
{
return $dateTime === null;
return !$dateTime instanceof \DateTime;
}
}

View File

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\Strict\Rector\If_\BooleanInIfConditionRuleFixerRector\Fixture;
final class IntegerUnionString
{
public function run(int|string $value)
{
if ($value) {
return 'name';
}
return 'no name';
}
}
?>
-----
<?php
declare(strict_types=1);
namespace Rector\Tests\Strict\Rector\If_\BooleanInIfConditionRuleFixerRector\Fixture;
final class IntegerUnionString
{
public function run(int|string $value)
{
if ($value !== 0 || $value !== '') {
return 'name';
}
return 'no name';
}
}
?>

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\Strict\Rector\Ternary\BooleanInTernaryOperatorRuleFixerRector\Fixture;
final class NullableString
{
public function run(string|null $value)
{
return $value ? 1 : 2;
}
}
?>
-----
<?php
declare(strict_types=1);
namespace Rector\Tests\Strict\Rector\Ternary\BooleanInTernaryOperatorRuleFixerRector\Fixture;
final class NullableString
{
public function run(string|null $value)
{
return $value !== null && $value !== '' ? 1 : 2;
}
}
?>

View File

@ -6,27 +6,33 @@ namespace Rector\Strict\NodeFactory;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\BinaryOp\BooleanAnd;
use PhpParser\Node\Expr\BinaryOp\BooleanOr;
use PhpParser\Node\Expr\BinaryOp\Identical;
use PhpParser\Node\Expr\BinaryOp\NotIdentical;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\BooleanNot;
use PhpParser\Node\Expr\Instanceof_;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Scalar\String_;
use PHPStan\Type\ArrayType;
use PHPStan\Type\BooleanType;
use PHPStan\Type\FloatType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\TypeWithClassName;
use PHPStan\Type\UnionType;
use Rector\Core\PhpParser\Node\NodeFactory;
final class ExactCompareFactory
{
public function createIdenticalFalsyCompare(Type $exprType, Expr $expr): ?Identical
public function __construct(
private NodeFactory $nodeFactory
) {
}
public function createIdenticalFalsyCompare(Type $exprType, Expr $expr, bool $treatAsNonEmpty): Expr|null
{
if ($exprType instanceof StringType) {
return new Identical($expr, new String_(''));
@ -36,6 +42,10 @@ final class ExactCompareFactory
return new Identical($expr, new LNumber(0));
}
if ($exprType instanceof BooleanType) {
return new Identical($expr, $this->nodeFactory->createFalse());
}
if ($exprType instanceof ArrayType) {
return new Identical($expr, new Array_([]));
}
@ -44,14 +54,10 @@ final class ExactCompareFactory
return null;
}
if (! TypeCombinator::containsNull($exprType)) {
return null;
}
return $this->createTruthyFromUnionType($exprType, $expr);
return $this->createTruthyFromUnionType($exprType, $expr, $treatAsNonEmpty);
}
public function createNotIdenticalFalsyCompare(Type $exprType, Expr $expr): NotIdentical|Identical|Instanceof_|null
public function createNotIdenticalFalsyCompare(Type $exprType, Expr $expr, bool $treatAsNotEmpty): Expr|null
{
if ($exprType instanceof StringType) {
return new NotIdentical($expr, new String_(''));
@ -69,74 +75,90 @@ final class ExactCompareFactory
return null;
}
if (! TypeCombinator::containsNull($exprType)) {
return null;
}
return $this->createFromUnionType($exprType, $expr);
return $this->createFromUnionType($exprType, $expr, $treatAsNotEmpty);
}
private function createFromUnionType(Type|UnionType $exprType, Expr $expr): Identical|Instanceof_|NotIdentical
{
$exprType = TypeCombinator::removeNull($exprType);
if ($exprType instanceof BooleanType) {
$trueConstFetch = new ConstFetch(new Name('true'));
return new Identical($expr, $trueConstFetch);
}
if ($exprType instanceof TypeWithClassName) {
return new Instanceof_($expr, new FullyQualified($exprType->getClassName()));
}
$nullConstFetch = new ConstFetch(new Name('null'));
return new NotIdentical($expr, $nullConstFetch);
}
private function resolveFalsyTypesCount(UnionType $unionType): int
{
$falsyTypesCount = 0;
foreach ($unionType->getTypes() as $unionedType) {
if ($unionedType instanceof StringType) {
++$falsyTypesCount;
}
if ($unionedType instanceof IntegerType) {
++$falsyTypesCount;
}
if ($unionedType instanceof FloatType) {
++$falsyTypesCount;
}
if ($unionedType instanceof ArrayType) {
++$falsyTypesCount;
}
}
return $falsyTypesCount;
}
private function createTruthyFromUnionType(UnionType $unionType, Expr $expr): ?Identical
private function createFromUnionType(UnionType $unionType, Expr $expr, bool $treatAsNotEmpty): Expr|null
{
$unionType = TypeCombinator::removeNull($unionType);
if ($unionType instanceof BooleanType) {
$trueConstFetch = new ConstFetch(new Name('true'));
return new Identical($expr, $trueConstFetch);
return new Identical($expr, $this->nodeFactory->createTrue());
}
if ($unionType instanceof TypeWithClassName) {
return new Instanceof_($expr, new FullyQualified($unionType->getClassName()));
}
$nullConstFetch = $this->nodeFactory->createNull();
$toNullNotIdentical = new NotIdentical($expr, $nullConstFetch);
if ($unionType instanceof UnionType) {
$falsyTypesCount = $this->resolveFalsyTypesCount($unionType);
$compareExprs = [];
// impossible to refactor to string value compare, as many falsy values can be provided
if ($falsyTypesCount > 1) {
return null;
foreach ($unionType->getTypes() as $unionedType) {
$compareExprs[] = $this->createNotIdenticalFalsyCompare($unionedType, $expr, $treatAsNotEmpty);
}
/** @var Expr $truthyExpr */
$truthyExpr = array_shift($compareExprs);
foreach ($compareExprs as $compareExpr) {
/** @var Expr $compareExpr */
$truthyExpr = new BooleanOr($truthyExpr, $compareExpr);
}
return $truthyExpr;
}
$nullConstFetch = new ConstFetch(new Name('null'));
return new Identical($expr, $nullConstFetch);
$compareExpr = $this->createNotIdenticalFalsyCompare($unionType, $expr, $treatAsNotEmpty);
if (! $compareExpr instanceof Expr) {
return null;
}
return new BooleanAnd($toNullNotIdentical, $compareExpr);
}
private function createTruthyFromUnionType(UnionType $unionType, Expr $expr, bool $treatAsNonEmpty): Expr|null
{
$unionType = TypeCombinator::removeNull($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;
}
if ($unionType instanceof BooleanType) {
return new Identical($expr, $this->nodeFactory->createTrue());
}
if ($unionType instanceof TypeWithClassName) {
$instanceOf = new Instanceof_($expr, new FullyQualified($unionType->getClassName()));
return new BooleanNot($instanceOf);
}
$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);
}
return $toNullIdentical;
}
}

View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Rector\Strict\Rector;
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
use Rector\Core\Rector\AbstractRector;
use Webmozart\Assert\Assert;
/**
* @see \Rector\Tests\Strict\Rector\BooleanNot\BooleanInBooleanNotRuleFixerRector\BooleanInBooleanNotRuleFixerRectorTest
*/
abstract class AbstractFalsyScalarRuleFixerRector extends AbstractRector implements ConfigurableRectorInterface
{
/**
* @var string
*/
public const TREAT_AS_NON_EMPTY = 'treat_as_non_empty';
protected bool $treatAsNonEmpty = false;
/**
* @param array<string, mixed> $configuration
*/
public function configure(array $configuration): void
{
$treatAsNonEmpty = $configuration[self::TREAT_AS_NON_EMPTY] ?? false;
Assert::boolean($treatAsNonEmpty);
$this->treatAsNonEmpty = $treatAsNonEmpty;
}
}

View File

@ -5,13 +5,13 @@ declare(strict_types=1);
namespace Rector\Strict\Rector\BooleanNot;
use PhpParser\Node;
use PhpParser\Node\Expr\BinaryOp\Identical;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\BooleanNot;
use PHPStan\Analyser\Scope;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Strict\NodeFactory\ExactCompareFactory;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Rector\Strict\Rector\AbstractFalsyScalarRuleFixerRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
@ -20,7 +20,7 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
*
* @see \Rector\Tests\Strict\Rector\BooleanNot\BooleanInBooleanNotRuleFixerRector\BooleanInBooleanNotRuleFixerRectorTest
*/
final class BooleanInBooleanNotRuleFixerRector extends AbstractRector
final class BooleanInBooleanNotRuleFixerRector extends AbstractFalsyScalarRuleFixerRector
{
public function __construct(
private ExactCompareFactory $exactCompareFactory
@ -34,11 +34,11 @@ final class BooleanInBooleanNotRuleFixerRector extends AbstractRector
'PHPStan\Rules\BooleansInConditions\BooleanInBooleanNotRule'
);
return new RuleDefinition($errorMessage, [
new CodeSample(
new ConfiguredCodeSample(
<<<'CODE_SAMPLE'
class SomeClass
{
public function run(string $name)
public function run(string|null $name)
{
if (! $name) {
return 'no name';
@ -54,7 +54,7 @@ class SomeClass
{
public function run(string $name)
{
if ($name === '') {
if ($name === null) {
return 'no name';
}
@ -62,6 +62,10 @@ class SomeClass
}
}
CODE_SAMPLE
,
[
self::TREAT_AS_NON_EMPTY => true,
]
),
]);
}
@ -77,7 +81,7 @@ CODE_SAMPLE
/**
* @param BooleanNot $node
*/
public function refactor(Node $node): ?Identical
public function refactor(Node $node): ?Expr
{
$scope = $node->getAttribute(AttributeKey::SCOPE);
if (! $scope instanceof Scope) {
@ -86,6 +90,6 @@ CODE_SAMPLE
$exprType = $scope->getType($node->expr);
return $this->exactCompareFactory->createIdenticalFalsyCompare($exprType, $node->expr);
return $this->exactCompareFactory->createIdenticalFalsyCompare($exprType, $node->expr, $this->treatAsNonEmpty);
}
}

View File

@ -5,22 +5,20 @@ declare(strict_types=1);
namespace Rector\Strict\Rector\Empty_;
use PhpParser\Node;
use PhpParser\Node\Expr\BinaryOp\Identical;
use PhpParser\Node\Expr\BinaryOp\NotIdentical;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\BooleanNot;
use PhpParser\Node\Expr\Empty_;
use PhpParser\Node\Expr\Instanceof_;
use PHPStan\Analyser\Scope;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Strict\NodeFactory\ExactCompareFactory;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Rector\Strict\Rector\AbstractFalsyScalarRuleFixerRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Tests\Strict\Rector\Empty_\DisallowedEmptyRuleFixerRector\DisallowedEmptyRuleFixerRectorTest
*/
final class DisallowedEmptyRuleFixerRector extends AbstractRector
final class DisallowedEmptyRuleFixerRector extends AbstractFalsyScalarRuleFixerRector
{
public function __construct(
private ExactCompareFactory $exactCompareFactory,
@ -34,7 +32,7 @@ final class DisallowedEmptyRuleFixerRector extends AbstractRector
'PHPStan\Rules\DisallowedConstructs\DisallowedEmptyRule'
);
return new RuleDefinition($errorMessage, [
new CodeSample(
new ConfiguredCodeSample(
<<<'CODE_SAMPLE'
final class SomeEmptyArray
{
@ -54,6 +52,10 @@ final class SomeEmptyArray
}
}
CODE_SAMPLE
,
[
self::TREAT_AS_NON_EMPTY => false,
]
),
]);
}
@ -69,7 +71,7 @@ CODE_SAMPLE
/**
* @param Empty_|BooleanNot $node
*/
public function refactor(Node $node)
public function refactor(Node $node): Expr|null
{
$scope = $node->getAttribute(AttributeKey::SCOPE);
if (! $scope instanceof Scope) {
@ -80,10 +82,10 @@ CODE_SAMPLE
return $this->refactorBooleanNot($node, $scope);
}
return $this->refactorEmpty($node, $scope);
return $this->refactorEmpty($node, $scope, $this->treatAsNonEmpty);
}
private function refactorBooleanNot(BooleanNot $booleanNot, Scope $scope): NotIdentical|Identical|Instanceof_|null
private function refactorBooleanNot(BooleanNot $booleanNot, Scope $scope): Expr|null
{
if (! $booleanNot->expr instanceof Empty_) {
return null;
@ -92,12 +94,16 @@ CODE_SAMPLE
$empty = $booleanNot->expr;
$emptyExprType = $scope->getType($empty->expr);
return $this->exactCompareFactory->createNotIdenticalFalsyCompare($emptyExprType, $empty->expr);
return $this->exactCompareFactory->createNotIdenticalFalsyCompare(
$emptyExprType,
$empty->expr,
$this->treatAsNonEmpty
);
}
private function refactorEmpty(Empty_ $empty, Scope $scope): ?Identical
private function refactorEmpty(Empty_ $empty, Scope $scope, bool $treatAsNonEmpty): Expr|null
{
$exprType = $scope->getType($empty->expr);
return $this->exactCompareFactory->createIdenticalFalsyCompare($exprType, $empty->expr);
return $this->exactCompareFactory->createIdenticalFalsyCompare($exprType, $empty->expr, $treatAsNonEmpty);
}
}

View File

@ -5,12 +5,13 @@ declare(strict_types=1);
namespace Rector\Strict\Rector\If_;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Stmt\If_;
use PHPStan\Analyser\Scope;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Strict\NodeFactory\ExactCompareFactory;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Rector\Strict\Rector\AbstractFalsyScalarRuleFixerRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
@ -20,7 +21,7 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
*
* @see \Rector\Tests\Strict\Rector\If_\BooleanInIfConditionRuleFixerRector\BooleanInIfConditionRuleFixerRectorTest
*/
final class BooleanInIfConditionRuleFixerRector extends AbstractRector
final class BooleanInIfConditionRuleFixerRector extends AbstractFalsyScalarRuleFixerRector
{
public function __construct(
private ExactCompareFactory $exactCompareFactory
@ -34,7 +35,7 @@ final class BooleanInIfConditionRuleFixerRector extends AbstractRector
'PHPStan\Rules\BooleansInConditions\BooleanInIfConditionRule'
);
return new RuleDefinition($errorMessage, [
new CodeSample(
new ConfiguredCodeSample(
<<<'CODE_SAMPLE'
final class NegatedString
{
@ -62,6 +63,10 @@ final class NegatedString
}
}
CODE_SAMPLE
,
[
self::TREAT_AS_NON_EMPTY => false,
]
),
]);
}
@ -86,7 +91,11 @@ CODE_SAMPLE
// 1. if
$ifCondExprType = $scope->getType($node->cond);
$notIdentical = $this->exactCompareFactory->createNotIdenticalFalsyCompare($ifCondExprType, $node->cond);
$notIdentical = $this->exactCompareFactory->createNotIdenticalFalsyCompare(
$ifCondExprType,
$node->cond,
$this->treatAsNonEmpty
);
if ($notIdentical !== null) {
$node->cond = $notIdentical;
}
@ -96,9 +105,11 @@ CODE_SAMPLE
$elseifCondExprType = $scope->getType($elseif->cond);
$notIdentical = $this->exactCompareFactory->createNotIdenticalFalsyCompare(
$elseifCondExprType,
$elseif->cond
$elseif->cond,
$this->treatAsNonEmpty
);
if ($notIdentical === null) {
if (! $notIdentical instanceof Expr) {
continue;
}

View File

@ -8,10 +8,10 @@ use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Ternary;
use PHPStan\Analyser\Scope;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Strict\NodeFactory\ExactCompareFactory;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Rector\Strict\Rector\AbstractFalsyScalarRuleFixerRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
@ -20,7 +20,7 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
*
* @see \Rector\Tests\Strict\Rector\Ternary\BooleanInTernaryOperatorRuleFixerRector\BooleanInTernaryOperatorRuleFixerRectorTest
*/
final class BooleanInTernaryOperatorRuleFixerRector extends AbstractRector
final class BooleanInTernaryOperatorRuleFixerRector extends AbstractFalsyScalarRuleFixerRector
{
public function __construct(
private ExactCompareFactory $exactCompareFactory
@ -34,7 +34,7 @@ final class BooleanInTernaryOperatorRuleFixerRector extends AbstractRector
'PHPStan\Rules\BooleansInConditions\BooleanInTernaryOperatorRule'
);
return new RuleDefinition($errorMessage, [
new CodeSample(
new ConfiguredCodeSample(
<<<'CODE_SAMPLE'
final class ArrayCompare
{
@ -54,6 +54,10 @@ final class ArrayCompare
}
}
CODE_SAMPLE
,
[
self::TREAT_AS_NON_EMPTY => false,
]
),
]);
}
@ -83,7 +87,11 @@ CODE_SAMPLE
$exprType = $scope->getType($node->cond);
$falsyIdentical = $this->exactCompareFactory->createNotIdenticalFalsyCompare($exprType, $node->cond);
$falsyIdentical = $this->exactCompareFactory->createNotIdenticalFalsyCompare(
$exprType,
$node->cond,
$this->treatAsNonEmpty
);
if (! $falsyIdentical instanceof Expr) {
return null;
}

View File

@ -5,13 +5,14 @@ declare(strict_types=1);
namespace Rector\Strict\Rector\Ternary;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\Ternary;
use PHPStan\Analyser\Scope;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Strict\NodeFactory\ExactCompareFactory;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Rector\Strict\Rector\AbstractFalsyScalarRuleFixerRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
@ -20,7 +21,7 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
*
* @see \Rector\Tests\Strict\Rector\Ternary\DisallowedShortTernaryRuleFixerRector\DisallowedShortTernaryRuleFixerRectorTest
*/
final class DisallowedShortTernaryRuleFixerRector extends AbstractRector
final class DisallowedShortTernaryRuleFixerRector extends AbstractFalsyScalarRuleFixerRector
{
public function __construct(
private ExactCompareFactory $exactCompareFactory,
@ -34,7 +35,7 @@ final class DisallowedShortTernaryRuleFixerRector extends AbstractRector
'PHPStan\Rules\DisallowedConstructs\DisallowedShortTernaryRule'
);
return new RuleDefinition($errorMessage, [
new CodeSample(
new ConfiguredCodeSample(
<<<'CODE_SAMPLE'
final class ShortTernaryArray
{
@ -54,6 +55,10 @@ final class ShortTernaryArray
}
}
CODE_SAMPLE
,
[
self::TREAT_AS_NON_EMPTY => false,
]
),
]);
}
@ -88,13 +93,17 @@ CODE_SAMPLE
}
$exprType = $scope->getType($node->cond);
$falsyIdentical = $this->exactCompareFactory->createNotIdenticalFalsyCompare($exprType, $node->cond);
if ($falsyIdentical === null) {
$compareExpr = $this->exactCompareFactory->createNotIdenticalFalsyCompare(
$exprType,
$node->cond,
$this->treatAsNonEmpty
);
if (! $compareExpr instanceof Expr) {
return null;
}
$node->if = $node->cond;
$node->cond = $falsyIdentical;
$node->cond = $compareExpr;
return $node;
}
@ -108,10 +117,11 @@ CODE_SAMPLE
$falsyCompareExpr = $this->exactCompareFactory->createNotIdenticalFalsyCompare(
$firstArgType,
$firstArgValue
$firstArgValue,
$this->treatAsNonEmpty
);
if ($falsyCompareExpr === null) {
if (! $falsyCompareExpr instanceof Expr) {
return;
}