From ba1f7a43c24eee23c5c38d9b582954f996fcefa0 Mon Sep 17 00:00:00 2001 From: AlexandruGG Date: Sat, 3 Jul 2021 19:08:19 +0100 Subject: [PATCH 1/3] Add SpreadRemoval Mutator --- resources/schema.json | 1 + src/Mutator/Operator/SpreadRemoval.php | 99 +++++++++++++ src/Mutator/ProfileList.php | 2 + .../Mutator/Operator/SpreadRemovalTest.php | 139 ++++++++++++++++++ 4 files changed, 241 insertions(+) create mode 100644 src/Mutator/Operator/SpreadRemoval.php create mode 100644 tests/phpunit/Mutator/Operator/SpreadRemovalTest.php diff --git a/resources/schema.json b/resources/schema.json index bc3067d1c..08f670140 100644 --- a/resources/schema.json +++ b/resources/schema.json @@ -324,6 +324,7 @@ "This": { "$ref": "#/definitions/default-mutator-config" }, "Spaceship": { "$ref": "#/definitions/default-mutator-config" }, "Spread": { "$ref": "#/definitions/default-mutator-config" }, + "SpreadRemoval": { "$ref": "#/definitions/default-mutator-config" }, "Foreach_": { "$ref": "#/definitions/default-mutator-config" }, "For_": { "$ref": "#/definitions/default-mutator-config" }, "DoWhile": { "$ref": "#/definitions/default-mutator-config" }, diff --git a/src/Mutator/Operator/SpreadRemoval.php b/src/Mutator/Operator/SpreadRemoval.php new file mode 100644 index 000000000..7a92a8347 --- /dev/null +++ b/src/Mutator/Operator/SpreadRemoval.php @@ -0,0 +1,99 @@ + + */ +final class SpreadRemoval implements Mutator +{ + use GetMutatorName; + + public static function getDefinition(): ?Definition + { + return new Definition( + <<<'TXT' +Removes a spread operator in an array expression. For example: + +```php +$x = [...$collection, 4, 5]; +``` + +Will be mutated to: + +```php +$x = [$collection, 4, 5]; +``` +TXT + , + MutatorCategory::SEMANTIC_REDUCTION, + null, + <<<'DIFF' +- $x = [...$collection, 4, 5]; ++ $x = [$collection, 4, 5]; +DIFF + ); + } + + /** + * @psalm-mutation-free + * + * @return iterable + */ + public function mutate(Node $node): iterable + { + yield new Node\Expr\ArrayItem( + $node->value, + null, + false, + $node->getAttributes(), + false + ); + } + + public function canMutate(Node $node): bool + { + return $node instanceof Node\Expr\ArrayItem && $node->unpack; + } +} diff --git a/src/Mutator/ProfileList.php b/src/Mutator/ProfileList.php index 553d9f278..5245019fc 100644 --- a/src/Mutator/ProfileList.php +++ b/src/Mutator/ProfileList.php @@ -159,6 +159,7 @@ final class ProfileList Mutator\Operator\NullSafeMethodCall::class, Mutator\Operator\NullSafePropertyCall::class, Mutator\Operator\Spread::class, + Mutator\Operator\SpreadRemoval::class, Mutator\Operator\Ternary::class, Mutator\Operator\Throw_::class, ]; @@ -362,6 +363,7 @@ final class ProfileList 'NullSafeMethodCall' => Mutator\Operator\NullSafeMethodCall::class, 'NullSafePropertyCall' => Mutator\Operator\NullSafePropertyCall::class, 'Spread' => Mutator\Operator\Spread::class, + 'SpreadRemoval' => Mutator\Operator\SpreadRemoval::class, 'Ternary' => Mutator\Operator\Ternary::class, 'Throw_' => Mutator\Operator\Throw_::class, diff --git a/tests/phpunit/Mutator/Operator/SpreadRemovalTest.php b/tests/phpunit/Mutator/Operator/SpreadRemovalTest.php new file mode 100644 index 000000000..4ea73f010 --- /dev/null +++ b/tests/phpunit/Mutator/Operator/SpreadRemovalTest.php @@ -0,0 +1,139 @@ += 7.4 + * @dataProvider mutationsProvider + * + * @param string|string[] $expected + */ + public function test_it_can_mutate(string $input, $expected = []): void + { + $this->doTest($input, $expected); + } + + public function mutationsProvider(): iterable + { + yield 'Spread removal for a raw array' => [ + <<<'PHP' + [ + <<<'PHP' + [ + <<<'PHP' + [ + <<<'PHP' +getCollection(), 4]; +PHP + , + <<<'PHP' +getCollection(), 4]; +PHP + , + ]; + + yield 'Spread removal for a new iterator object' => [ + <<<'PHP' + [ + <<<'PHP' + Date: Sat, 3 Jul 2021 20:18:45 +0100 Subject: [PATCH 2/3] Add SpreadAssignment Mutator --- resources/schema.json | 1 + src/Mutator/Operator/SpreadAssignment.php | 107 +++++++++++ src/Mutator/ProfileList.php | 2 + .../Mutator/Operator/SpreadAssignmentTest.php | 172 ++++++++++++++++++ 4 files changed, 282 insertions(+) create mode 100644 src/Mutator/Operator/SpreadAssignment.php create mode 100644 tests/phpunit/Mutator/Operator/SpreadAssignmentTest.php diff --git a/resources/schema.json b/resources/schema.json index 08f670140..a25e8a773 100644 --- a/resources/schema.json +++ b/resources/schema.json @@ -324,6 +324,7 @@ "This": { "$ref": "#/definitions/default-mutator-config" }, "Spaceship": { "$ref": "#/definitions/default-mutator-config" }, "Spread": { "$ref": "#/definitions/default-mutator-config" }, + "SpreadAssignment": { "$ref": "#/definitions/default-mutator-config" }, "SpreadRemoval": { "$ref": "#/definitions/default-mutator-config" }, "Foreach_": { "$ref": "#/definitions/default-mutator-config" }, "For_": { "$ref": "#/definitions/default-mutator-config" }, diff --git a/src/Mutator/Operator/SpreadAssignment.php b/src/Mutator/Operator/SpreadAssignment.php new file mode 100644 index 000000000..05c2b47a3 --- /dev/null +++ b/src/Mutator/Operator/SpreadAssignment.php @@ -0,0 +1,107 @@ + + */ +final class SpreadAssignment implements Mutator +{ + use GetMutatorName; + + public static function getDefinition(): ?Definition + { + return new Definition( + <<<'TXT' +Removes a spread operator in an array expression and turns it into an assignment. For example: + +```php +$x = [...$collection]; +``` + +Will be mutated to: + +```php +$x = $collection; +``` +TXT + , + MutatorCategory::SEMANTIC_REDUCTION, + null, + <<<'DIFF' +- $x = [...$collection]; ++ $x = $collection; +DIFF + ); + } + + /** + * @psalm-mutation-free + * + * @return iterable + */ + public function mutate(Node $node): iterable + { + Assert::allNotNull($node->items); + + yield $node->items[0]->value; + } + + public function canMutate(Node $node): bool + { + if (!$node instanceof Node\Expr\Array_) { + return false; + } + + if (count($node->items) !== 1) { + return false; + } + + Assert::allNotNull($node->items); + + return $node->items[0]->unpack; + } +} diff --git a/src/Mutator/ProfileList.php b/src/Mutator/ProfileList.php index 5245019fc..44b63acb8 100644 --- a/src/Mutator/ProfileList.php +++ b/src/Mutator/ProfileList.php @@ -159,6 +159,7 @@ final class ProfileList Mutator\Operator\NullSafeMethodCall::class, Mutator\Operator\NullSafePropertyCall::class, Mutator\Operator\Spread::class, + Mutator\Operator\SpreadAssignment::class, Mutator\Operator\SpreadRemoval::class, Mutator\Operator\Ternary::class, Mutator\Operator\Throw_::class, @@ -363,6 +364,7 @@ final class ProfileList 'NullSafeMethodCall' => Mutator\Operator\NullSafeMethodCall::class, 'NullSafePropertyCall' => Mutator\Operator\NullSafePropertyCall::class, 'Spread' => Mutator\Operator\Spread::class, + 'SpreadAssignment' => Mutator\Operator\SpreadAssignment::class, 'SpreadRemoval' => Mutator\Operator\SpreadRemoval::class, 'Ternary' => Mutator\Operator\Ternary::class, 'Throw_' => Mutator\Operator\Throw_::class, diff --git a/tests/phpunit/Mutator/Operator/SpreadAssignmentTest.php b/tests/phpunit/Mutator/Operator/SpreadAssignmentTest.php new file mode 100644 index 000000000..50762e768 --- /dev/null +++ b/tests/phpunit/Mutator/Operator/SpreadAssignmentTest.php @@ -0,0 +1,172 @@ += 7.4 + * @dataProvider mutationsProvider + * + * @param string|string[] $expected + */ + public function test_it_can_mutate(string $input, $expected = []): void + { + $this->doTest($input, $expected); + } + + public function mutationsProvider(): iterable + { + yield 'Spread assignment for a raw array' => [ + <<<'PHP' + [ + <<<'PHP' + [ + <<<'PHP' + [ + <<<'PHP' + [ + <<<'PHP' +getCollection()]; +PHP + , + <<<'PHP' +getCollection(); +PHP + , + ]; + + yield 'Spread assignment for a new iterator object' => [ + <<<'PHP' + [ + <<<'PHP' + [ + <<<'PHP' + [ + <<<'PHP' + Date: Sat, 3 Jul 2021 20:25:19 +0100 Subject: [PATCH 3/3] Rename Spread Mutator to SpreadOneItem --- resources/schema.json | 2 +- .../Operator/{Spread.php => SpreadOneItem.php} | 2 +- src/Mutator/ProfileList.php | 4 ++-- .../{SpreadTest.php => SpreadOneItemTest.php} | 12 ++++++------ 4 files changed, 10 insertions(+), 10 deletions(-) rename src/Mutator/Operator/{Spread.php => SpreadOneItem.php} (98%) rename tests/phpunit/Mutator/Operator/{SpreadTest.php => SpreadOneItemTest.php} (90%) diff --git a/resources/schema.json b/resources/schema.json index a25e8a773..85e65a5ba 100644 --- a/resources/schema.json +++ b/resources/schema.json @@ -323,7 +323,7 @@ "NewObject": { "$ref": "#/definitions/default-mutator-config" }, "This": { "$ref": "#/definitions/default-mutator-config" }, "Spaceship": { "$ref": "#/definitions/default-mutator-config" }, - "Spread": { "$ref": "#/definitions/default-mutator-config" }, + "SpreadOneItem": { "$ref": "#/definitions/default-mutator-config" }, "SpreadAssignment": { "$ref": "#/definitions/default-mutator-config" }, "SpreadRemoval": { "$ref": "#/definitions/default-mutator-config" }, "Foreach_": { "$ref": "#/definitions/default-mutator-config" }, diff --git a/src/Mutator/Operator/Spread.php b/src/Mutator/Operator/SpreadOneItem.php similarity index 98% rename from src/Mutator/Operator/Spread.php rename to src/Mutator/Operator/SpreadOneItem.php index 42f4e6a16..a0b752d70 100644 --- a/src/Mutator/Operator/Spread.php +++ b/src/Mutator/Operator/SpreadOneItem.php @@ -46,7 +46,7 @@ * * @implements Mutator */ -final class Spread implements Mutator +final class SpreadOneItem implements Mutator { use GetMutatorName; diff --git a/src/Mutator/ProfileList.php b/src/Mutator/ProfileList.php index 44b63acb8..cb224528c 100644 --- a/src/Mutator/ProfileList.php +++ b/src/Mutator/ProfileList.php @@ -158,8 +158,8 @@ final class ProfileList Mutator\Operator\Finally_::class, Mutator\Operator\NullSafeMethodCall::class, Mutator\Operator\NullSafePropertyCall::class, - Mutator\Operator\Spread::class, Mutator\Operator\SpreadAssignment::class, + Mutator\Operator\SpreadOneItem::class, Mutator\Operator\SpreadRemoval::class, Mutator\Operator\Ternary::class, Mutator\Operator\Throw_::class, @@ -363,8 +363,8 @@ final class ProfileList 'Finally_' => Mutator\Operator\Finally_::class, 'NullSafeMethodCall' => Mutator\Operator\NullSafeMethodCall::class, 'NullSafePropertyCall' => Mutator\Operator\NullSafePropertyCall::class, - 'Spread' => Mutator\Operator\Spread::class, 'SpreadAssignment' => Mutator\Operator\SpreadAssignment::class, + 'SpreadOneItem' => Mutator\Operator\SpreadOneItem::class, 'SpreadRemoval' => Mutator\Operator\SpreadRemoval::class, 'Ternary' => Mutator\Operator\Ternary::class, 'Throw_' => Mutator\Operator\Throw_::class, diff --git a/tests/phpunit/Mutator/Operator/SpreadTest.php b/tests/phpunit/Mutator/Operator/SpreadOneItemTest.php similarity index 90% rename from tests/phpunit/Mutator/Operator/SpreadTest.php rename to tests/phpunit/Mutator/Operator/SpreadOneItemTest.php index aa219ef74..98c5fe69b 100644 --- a/tests/phpunit/Mutator/Operator/SpreadTest.php +++ b/tests/phpunit/Mutator/Operator/SpreadOneItemTest.php @@ -37,7 +37,7 @@ use Infection\Tests\Mutator\BaseMutatorTestCase; -final class SpreadTest extends BaseMutatorTestCase +final class SpreadOneItemTest extends BaseMutatorTestCase { /** * @requires PHP >= 7.4 @@ -52,7 +52,7 @@ public function test_it_can_mutate(string $input, $expected = []): void public function mutationsProvider(): iterable { - yield 'Spread for a raw array' => [ + yield 'Spread one item for a raw array' => [ <<<'PHP' [ + yield 'Spread one item for a variable' => [ <<<'PHP' [ + yield 'Spread one item for a function call' => [ <<<'PHP' [ + yield 'Spread one item for a method call' => [ <<<'PHP' [ + yield 'Spread one item for a new iterator object' => [ <<<'PHP'