Skip to content

Commit e2d2cfb

Browse files
authored
[CodeQuality] Fix AddInstanceofAssertForNullableArgumentRector duplicate assertInstanceOf inside traits (#773)
1 parent 0f24ff4 commit e2d2cfb

4 files changed

Lines changed: 122 additions & 0 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\ClassMethod\AddInstanceofAssertForNullableArgumentRector\Fixture;
6+
7+
use Rector\PHPUnit\Tests\CodeQuality\Rector\ClassMethod\AddInstanceofAssertForNullableArgumentRector\Source\SomeFactory;
8+
9+
trait NullableArgInTrait
10+
{
11+
public function assertSomething(SomeFactory $someFactory): void
12+
{
13+
$someObject = $someFactory->create();
14+
15+
$someFactory->process($someObject);
16+
}
17+
}
18+
19+
?>
20+
-----
21+
<?php
22+
23+
declare(strict_types=1);
24+
25+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\ClassMethod\AddInstanceofAssertForNullableArgumentRector\Fixture;
26+
27+
use Rector\PHPUnit\Tests\CodeQuality\Rector\ClassMethod\AddInstanceofAssertForNullableArgumentRector\Source\SomeFactory;
28+
29+
trait NullableArgInTrait
30+
{
31+
public function assertSomething(SomeFactory $someFactory): void
32+
{
33+
$someObject = $someFactory->create();
34+
$this->assertInstanceOf(\Rector\PHPUnit\Tests\CodeQuality\Rector\ClassMethod\AddInstanceofAssertForNullableArgumentRector\Source\SomeClassUsedInTests::class, $someObject);
35+
36+
$someFactory->process($someObject);
37+
}
38+
}
39+
40+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\ClassMethod\AddInstanceofAssertForNullableArgumentRector\Fixture;
6+
7+
use Rector\PHPUnit\Tests\CodeQuality\Rector\ClassMethod\AddInstanceofAssertForNullableArgumentRector\Source\SomeFactory;
8+
9+
trait SkipAlreadyAssertedInTrait
10+
{
11+
public function assertSomething(SomeFactory $someFactory): void
12+
{
13+
$someObject = $someFactory->create();
14+
$this->assertInstanceOf(\Rector\PHPUnit\Tests\CodeQuality\Rector\ClassMethod\AddInstanceofAssertForNullableArgumentRector\Source\SomeClassUsedInTests::class, $someObject);
15+
16+
$someFactory->process($someObject);
17+
}
18+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\ClassMethod\AddInstanceofAssertForNullableArgumentRector\Source;
6+
7+
final class SomeFactory
8+
{
9+
public function create(): ?SomeClassUsedInTests
10+
{
11+
return null;
12+
}
13+
14+
public function process(SomeClassUsedInTests $someClass): void
15+
{
16+
}
17+
}

‎rules/CodeQuality/Rector/ClassMethod/AddInstanceofAssertForNullableArgumentRector.php‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use PhpParser\Node\Expr\Variable;
1010
use PhpParser\Node\Stmt;
1111
use PhpParser\Node\Stmt\ClassMethod;
12+
use PhpParser\Node\Stmt\Expression;
1213
use PhpParser\Node\Stmt\Foreach_;
1314
use PhpParser\NodeVisitor;
1415
use PHPStan\Type\ObjectType;
@@ -132,6 +133,20 @@ public function refactor(Node $node): ?Node
132133

133134
$next = 0;
134135
foreach ($node->stmts as $key => $stmt) {
136+
// already asserted in a previous run? drop the variable to keep the rule idempotent
137+
$assertedVariableName = $this->matchAssertInstanceOfVariableName($stmt);
138+
if (is_string($assertedVariableName)) {
139+
$alreadyAssertedVariableNameToType = $variableNameToTypeCollection->matchByVariableName(
140+
$assertedVariableName
141+
);
142+
143+
if ($alreadyAssertedVariableNameToType instanceof VariableNameToType) {
144+
$variableNameToTypeCollection->remove($alreadyAssertedVariableNameToType);
145+
}
146+
147+
continue;
148+
}
149+
135150
// has callable on nullable variable of already collected name?
136151
$matchedNullableVariableNameToType = $this->matchedNullableArgumentNameToType(
137152
$stmt,
@@ -224,4 +239,36 @@ private function matchedNullableArgumentNameToType(
224239

225240
return $matchedNullableVariableNameToType;
226241
}
242+
243+
private function matchAssertInstanceOfVariableName(Stmt $stmt): ?string
244+
{
245+
if (! $stmt instanceof Expression) {
246+
return null;
247+
}
248+
249+
if (! $stmt->expr instanceof MethodCall) {
250+
return null;
251+
}
252+
253+
$methodCall = $stmt->expr;
254+
if (! $this->isName($methodCall->name, 'assertInstanceOf')) {
255+
return null;
256+
}
257+
258+
$args = $methodCall->getArgs();
259+
if (! isset($args[1])) {
260+
return null;
261+
}
262+
263+
if (! $args[1]->value instanceof Variable) {
264+
return null;
265+
}
266+
267+
$variableName = $this->getName($args[1]->value);
268+
if (! is_string($variableName)) {
269+
return null;
270+
}
271+
272+
return $variableName;
273+
}
227274
}

0 commit comments

Comments
 (0)