Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion resources/infection.json5
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"mutators": {
"@default": false,
"PHPStan\\Infection\\LooseBooleanMutator": true,
"PHPStan\\Infection\\TrinaryLogicMutator": true
"PHPStan\\Infection\\TrinaryLogicMutator": true,
"PHPStan\\Infection\\IsSuperTypeOfCalleeAndArgumentMutator": true
},
"bootstrap": "build-infection/vendor/autoload.php"
}
96 changes: 96 additions & 0 deletions src/Infection/IsSuperTypeOfCalleeAndArgumentMutator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php declare(strict_types = 1);

namespace PHPStan\Infection;

use Infection\Mutator\Definition;
use Infection\Mutator\Mutator;
use Infection\Mutator\MutatorCategory;
use PhpParser\Node;
use RuntimeException;
use function count;
use function in_array;

/**
* @implements Mutator<Node\Expr\MethodCall>
*/
final class IsSuperTypeOfCalleeAndArgumentMutator implements Mutator
{

public static function getDefinition(): Definition
{
return new Definition(
<<<'TXT'
Replaces the callee and the argument of a isSuperTypeOf() method call.
TXT
,
MutatorCategory::ORTHOGONAL_REPLACEMENT,
null,
<<<'DIFF'
- $a->isSuperTypeOf($b);
+ $b->isSuperTypeOf($a);
DIFF,
);
}

public function getName(): string
{
return self::class;
}

public function canMutate(Node $node): bool
{
if (!$node instanceof Node\Expr\MethodCall) {
return false;
}

if (
!$node->var instanceof Node\Expr\Variable
&& !$node->var instanceof Node\Expr\PropertyFetch
&& !$node->var instanceof Node\Expr\MethodCall
&& !$node->var instanceof Node\Expr\StaticCall
&& !$node->var instanceof Node\Expr\New_
) {
return false;
}

if (!$node->name instanceof Node\Identifier) {
return false;
}

if (!in_array($node->name->name, ['isSuperTypeOf'], true)) {
return false;
}

$args = $node->getArgs();
if (count($args) !== 1) {
return false;
}

if (
!$args[0]->value instanceof Node\Expr\Variable
&& !$args[0]->value instanceof Node\Expr\PropertyFetch
&& !$args[0]->value instanceof Node\Expr\MethodCall
&& !$args[0]->value instanceof Node\Expr\StaticCall
&& !$args[0]->value instanceof Node\Expr\New_
) {
return false;
}

return true;
}

public function mutate(Node $node): iterable
{
$args = $node->getArgs();
if (count($args) !== 1) {
throw new RuntimeException();
}

yield new Node\Expr\MethodCall(
$args[0]->value,
$node->name,
[new Node\Arg($node->var)],
);
}

}
186 changes: 186 additions & 0 deletions tests/Infection/IsSuperTypeOfCalleeAndArgumentMutatorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
<?php declare(strict_types = 1);

namespace PHPStan\Infection;

use Infection\Testing\BaseMutatorTestCase;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;

#[CoversClass(IsSuperTypeOfCalleeAndArgumentMutator::class)]
final class IsSuperTypeOfCalleeAndArgumentMutatorTest extends BaseMutatorTestCase
{

/**
* @param string|string[] $expected
*/
#[DataProvider('mutationsProvider')]
public function testMutator(string $input, $expected = []): void
{
$this->assertMutatesInput($input, $expected);
}

/**
* @return iterable<string, array{0: string, 1?: string}>
*/
public static function mutationsProvider(): iterable
{
yield 'It mutates isSuperTypeOf' => [
<<<'PHP'
<?php

$a->isSuperTypeOf($b);
PHP
,
<<<'PHP'
<?php

$b->isSuperTypeOf($a);
PHP
,
];

yield 'It mutates isSuperTypeOf with property fetch' => [
<<<'PHP'
<?php

$this->a->isSuperTypeOf($b);
PHP
,
<<<'PHP'
<?php

$b->isSuperTypeOf($this->a);
PHP
,
];

yield 'It mutates isSuperTypeOf with property fetch (reversed)' => [
<<<'PHP'
<?php

$a->isSuperTypeOf($this->b);
PHP
,
<<<'PHP'
<?php

$this->b->isSuperTypeOf($a);
PHP
,
];

yield 'It mutates isSuperTypeOf with method-call' => [
<<<'PHP'
<?php

$a->isSuperTypeOf($this->call());
PHP
,
<<<'PHP'
<?php

$this->call()->isSuperTypeOf($a);
PHP
,
];

yield 'It mutates isSuperTypeOf with method-call (reversed)' => [
<<<'PHP'
<?php

$this->call()->isSuperTypeOf($a);
PHP
,
<<<'PHP'
<?php

$a->isSuperTypeOf($this->call());
PHP
,
];

yield 'It mutates isSuperTypeOf with static method call' => [
<<<'PHP'
<?php

IntegerRangeType::fromInterval(0, null)->isSuperTypeOf($a);
PHP
,
<<<'PHP'
<?php

$a->isSuperTypeOf(IntegerRangeType::fromInterval(0, null));
PHP
,
];

yield 'It mutates isSuperTypeOf with static method call (reversed)' => [
<<<'PHP'
<?php

$a->isSuperTypeOf(IntegerRangeType::fromInterval(0, null));
PHP
,
<<<'PHP'
<?php

IntegerRangeType::fromInterval(0, null)->isSuperTypeOf($a);
PHP
,
];

yield 'It mutates isSuperTypeOf with new' => [
<<<'PHP'
<?php

(new ConstantStringType(''))->isSuperTypeOf($delimiterType);
PHP
,
<<<'PHP'
<?php

$delimiterType->isSuperTypeOf(new ConstantStringType(''));
PHP
,
];

yield 'It mutates isSuperTypeOf with new (reversed)' => [
<<<'PHP'
<?php

$delimiterType->isSuperTypeOf(new ConstantStringType(''));
PHP
,
<<<'PHP'
<?php

(new ConstantStringType(''))->isSuperTypeOf($delimiterType);
PHP
,
];

yield 'skip isSuperTypeOf with more arguments' => [
<<<'PHP'
<?php

$a->isSuperTypeOf($b, $c);
PHP
,
];

yield 'skip other method calls' => [
<<<'PHP'
<?php

$a->isConstantValue($b);
PHP
,
];
}

protected function getTestedMutatorClassName(): string
{
return IsSuperTypeOfCalleeAndArgumentMutator::class;
}

}
3 changes: 2 additions & 1 deletion tests/phpt/infection-config-default.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ echo shell_exec($bin);
"mutators": {
"@default": false,
"PHPStan\\Infection\\LooseBooleanMutator": true,
"PHPStan\\Infection\\TrinaryLogicMutator": true
"PHPStan\\Infection\\TrinaryLogicMutator": true,
"PHPStan\\Infection\\IsSuperTypeOfCalleeAndArgumentMutator": true
},
"bootstrap": "build-infection\/vendor\/autoload.php"
}
1 change: 1 addition & 0 deletions tests/phpt/infection-config.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ echo shell_exec($bin." --source-directory='more/files/' --timeout=180 --mutator-
"@default": false,
"PHPStan\\Infection\\LooseBooleanMutator": true,
"PHPStan\\Infection\\TrinaryLogicMutator": true,
"PHPStan\\Infection\\IsSuperTypeOfCalleeAndArgumentMutator": true,
"My\\Class": true
},
"bootstrap": "build-infection\/vendor\/autoload.php"
Expand Down
Loading