Skip to content

Commit da097ea

Browse files
committed
[Php80] Skip mixed and untyped calls in TernaryToNullsafeCoalesceRector
TypeCombinator::containsNull() is false for MixedType, so a call returning mixed (or declaring no return type) was rewritten to "?->... ?? $fallback", and a null result started falling through to the fallback. Assisted-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 61bc76b commit da097ea

3 files changed

Lines changed: 38 additions & 3 deletions

File tree

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Rector\Tests\Php80\Rector\Ternary\TernaryToNullsafeCoalesceRector\Fixture;
4+
5+
use Rector\Tests\Php80\Rector\Ternary\TernaryToNullsafeCoalesceRector\Source\SomeObject;
6+
7+
final class SkipMixedReturn
8+
{
9+
public function run(?SomeObject $someObject): mixed
10+
{
11+
return null !== $someObject ? $someObject->findMixed() : '';
12+
}
13+
14+
public function runUntyped(?SomeObject $someObject): mixed
15+
{
16+
return null !== $someObject ? $someObject->findUntyped() : '';
17+
}
18+
19+
public function runUntypedProperty(?SomeObject $someObject): mixed
20+
{
21+
return null !== $someObject ? $someObject->untypedName : '';
22+
}
23+
}

‎rules-tests/Php80/Rector/Ternary/TernaryToNullsafeCoalesceRector/Source/SomeObject.php‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ final class SomeObject
88
{
99
public string $name = 'name';
1010

11+
public $untypedName = 'name';
12+
1113
public function getName(): string
1214
{
1315
return $this->name;
@@ -17,4 +19,14 @@ public function findName(): ?string
1719
{
1820
return $this->name;
1921
}
22+
23+
public function findMixed(): mixed
24+
{
25+
return $this->name;
26+
}
27+
28+
public function findUntyped()
29+
{
30+
return $this->name;
31+
}
2032
}

‎rules/Php80/Rector/Ternary/TernaryToNullsafeCoalesceRector.php‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
use PhpParser\Node\Expr\StaticPropertyFetch;
1717
use PhpParser\Node\Expr\Ternary;
1818
use PhpParser\Node\Expr\Variable;
19-
use PHPStan\Type\TypeCombinator;
2019
use Rector\PhpParser\Node\Value\ValueResolver;
2120
use Rector\Rector\AbstractRector;
2221
use Rector\ValueObject\PhpVersionFeature;
@@ -185,13 +184,14 @@ private function isPureExpr(Expr $expr): bool
185184
/**
186185
* Guards the "?? $fallback" rewrite.
187186
*
188-
* The ternary and the coalesce only agree while the call itself cannot return null:
187+
* The ternary and the coalesce only agree while the call itself cannot return null,
188+
* so "mixed" and untyped calls are skipped too:
189189
*
190190
* null !== $a ? $a->find() : '' // $a->find() returning null yields null
191191
* $a?->find() ?? '' // $a->find() returning null yields ''
192192
*/
193193
private function shouldSkipCoalesceFallback(Expr $callExpr): bool
194194
{
195-
return TypeCombinator::containsNull($this->getType($callExpr));
195+
return ! $this->getType($callExpr)->isNull()->no();
196196
}
197197
}

0 commit comments

Comments
 (0)