Skip to content

Commit 22c6dae

Browse files
authored
[CodeQuality] Fix infinite loop on trait on AddInstanceofAssertForNullableInstanceRector (#818)
1 parent 7706a9e commit 22c6dae

2 files changed

Lines changed: 72 additions & 0 deletions

File tree

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\ClassMethod\AddInstanceofAssertForNullableInstanceRector\Fixture;
4+
5+
trait InTrait
6+
{
7+
public function test(\Throwable $e)
8+
{
9+
$p = $e->getPrevious();
10+
$p->getMessage();
11+
}
12+
}
13+
14+
?>
15+
-----
16+
<?php
17+
18+
namespace Rector\PHPUnit\Tests\CodeQuality\Rector\ClassMethod\AddInstanceofAssertForNullableInstanceRector\Fixture;
19+
20+
trait InTrait
21+
{
22+
public function test(\Throwable $e)
23+
{
24+
$p = $e->getPrevious();
25+
$this->assertInstanceOf(\Throwable::class, $p);
26+
$p->getMessage();
27+
}
28+
}
29+
30+
?>

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66

77
use PhpParser\Node;
88
use PhpParser\Node\Expr\MethodCall;
9+
use PhpParser\Node\Expr\StaticCall;
910
use PhpParser\Node\Expr\Variable;
1011
use PhpParser\Node\Stmt;
1112
use PhpParser\Node\Stmt\ClassMethod;
13+
use PhpParser\Node\Stmt\Expression;
1214
use PhpParser\Node\Stmt\Foreach_;
1315
use Rector\PHPUnit\CodeQuality\NodeAnalyser\NullableObjectAssignCollector;
1416
use Rector\PHPUnit\CodeQuality\NodeFactory\AssertMethodCallFactory;
@@ -116,6 +118,19 @@ public function refactor(Node $node): ?Node
116118

117119
$next = 0;
118120
foreach ($node->stmts as $key => $stmt) {
121+
// already asserted, e.g. in trait, where $this is not known as TestCase so the type is not narrowed
122+
$assertedVariableName = $this->matchAssertInstanceOfVariableName($stmt);
123+
if ($assertedVariableName !== null) {
124+
$assertedVariableNameToType = $variableNameToTypeCollection->matchByVariableName(
125+
$assertedVariableName
126+
);
127+
if ($assertedVariableNameToType instanceof VariableNameToType) {
128+
$variableNameToTypeCollection->remove($assertedVariableNameToType);
129+
}
130+
131+
continue;
132+
}
133+
119134
// has callable on nullable variables of already collected names?
120135
// a single statement can use multiple nullable variables at once
121136
$matchedNullableVariableNameToTypes = $this->matchNullableVariableNameToTypes(
@@ -146,6 +161,33 @@ public function refactor(Node $node): ?Node
146161
return $node;
147162
}
148163

164+
private function matchAssertInstanceOfVariableName(Stmt $stmt): ?string
165+
{
166+
if (! $stmt instanceof Expression) {
167+
return null;
168+
}
169+
170+
$call = $stmt->expr;
171+
if (! $call instanceof MethodCall && ! $call instanceof StaticCall) {
172+
return null;
173+
}
174+
175+
if (! $this->isName($call->name, 'assertInstanceOf')) {
176+
return null;
177+
}
178+
179+
if ($call->isFirstClassCallable()) {
180+
return null;
181+
}
182+
183+
$args = $call->getArgs();
184+
if (! isset($args[1]) || ! $args[1]->value instanceof Variable) {
185+
return null;
186+
}
187+
188+
return $this->getName($args[1]->value);
189+
}
190+
149191
/**
150192
* @return VariableNameToType[]
151193
*/

0 commit comments

Comments
 (0)