Skip to content

Commit ccaf2c8

Browse files
authored
[DowngradePhp82] Handle nullable true on DowngradeStandaloneNullTrueFalseReturnTypeRector (#374)
* [DowngradePhp82] Handle nullable true on DowngradeStandaloneNullTrueFalseReturnTypeRector * fix * fix * eol
1 parent 364fbdb commit ccaf2c8

3 files changed

Lines changed: 118 additions & 1 deletion

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace Rector\Tests\DowngradePhp82\Rector\Class_\DowngradeStandaloneNullTrueFalseReturnTypeRector\Fixture;
4+
5+
final class NullableTrue
6+
{
7+
public function run(): ?true
8+
{
9+
return rand(0, 1) ? true : null;
10+
}
11+
}
12+
13+
?>
14+
-----
15+
<?php
16+
17+
namespace Rector\Tests\DowngradePhp82\Rector\Class_\DowngradeStandaloneNullTrueFalseReturnTypeRector\Fixture;
18+
19+
final class NullableTrue
20+
{
21+
/**
22+
* @return true|null
23+
*/
24+
public function run(): ?bool
25+
{
26+
return rand(0, 1) ? true : null;
27+
}
28+
}
29+
30+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace Rector\Tests\DowngradePhp82\Rector\Class_\DowngradeStandaloneNullTrueFalseReturnTypeRector\Fixture;
4+
5+
final class UnionTrue
6+
{
7+
public function run(): null|true
8+
{
9+
return rand(0, 1) ? true : null;
10+
}
11+
}
12+
13+
?>
14+
-----
15+
<?php
16+
17+
namespace Rector\Tests\DowngradePhp82\Rector\Class_\DowngradeStandaloneNullTrueFalseReturnTypeRector\Fixture;
18+
19+
final class UnionTrue
20+
{
21+
/**
22+
* @return null|true
23+
*/
24+
public function run(): null|bool
25+
{
26+
return rand(0, 1) ? true : null;
27+
}
28+
}
29+
30+
?>

‎rules/DowngradePhp82/Rector/FunctionLike/DowngradeStandaloneNullTrueFalseReturnTypeRector.php‎

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@
1717
use PHPStan\Type\Constant\ConstantBooleanType;
1818
use PHPStan\Type\NullType;
1919
use PHPStan\Type\Type;
20+
use PHPStan\Type\UnionType as PHPStanUnionType;
2021
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
2122
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
2223
use Rector\NodeTypeResolver\Node\AttributeKey;
2324
use Rector\PhpParser\AstResolver;
2425
use Rector\Rector\AbstractRector;
26+
use Rector\StaticTypeMapper\StaticTypeMapper;
2527
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
2628
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
2729

@@ -35,7 +37,8 @@ final class DowngradeStandaloneNullTrueFalseReturnTypeRector extends AbstractRec
3537
public function __construct(
3638
private readonly PhpDocInfoFactory $phpDocInfoFactory,
3739
private readonly PhpDocTypeChanger $phpDocTypeChanger,
38-
private readonly AstResolver $astResolver
40+
private readonly AstResolver $astResolver,
41+
private readonly StaticTypeMapper $staticTypeMapper
3942
) {
4043
}
4144

@@ -83,6 +86,25 @@ public function run(): mixed
8386
public function refactor(Node $node): ?Node
8487
{
8588
$returnType = $node->returnType;
89+
90+
if ($returnType instanceof NullableType && $returnType->type instanceof Identifier) {
91+
$innerName = $this->getName($returnType->type);
92+
if (in_array($innerName, ['false', 'true'], true)) {
93+
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
94+
$unionType = new PHPStanUnionType([new ConstantBooleanType($innerName === 'true'), new NullType()]);
95+
$this->phpDocTypeChanger->changeReturnType($node, $phpDocInfo, $unionType);
96+
$node->returnType = new NullableType(new Identifier('bool'));
97+
98+
return $node;
99+
}
100+
101+
return null;
102+
}
103+
104+
if ($returnType instanceof UnionType) {
105+
return $this->refactorUnionType($node, $returnType);
106+
}
107+
86108
if (! $returnType instanceof Identifier) {
87109
return null;
88110
}
@@ -102,6 +124,41 @@ public function refactor(Node $node): ?Node
102124
return $node;
103125
}
104126

127+
/**
128+
* @param ClassMethod|Function_|Closure|ArrowFunction $node
129+
*/
130+
private function refactorUnionType(Node $node, UnionType $unionType): ?Node
131+
{
132+
$hasChanged = false;
133+
134+
$originalUnionType = clone $unionType;
135+
foreach ($unionType->types as $key => $type) {
136+
if ($type instanceof Identifier) {
137+
$name = $this->getName($type);
138+
if (! in_array($name, ['true', 'false'], true)) {
139+
continue;
140+
}
141+
142+
$unionType->types[$key] = new Identifier('bool');
143+
$hasChanged = true;
144+
}
145+
}
146+
147+
if (! $hasChanged) {
148+
return null;
149+
}
150+
151+
$unionType->types = array_values(array_unique($unionType->types, SORT_REGULAR));
152+
$phpStanType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($originalUnionType);
153+
154+
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
155+
$this->phpDocTypeChanger->changeReturnType($node, $phpDocInfo, $phpStanType);
156+
157+
$node->returnType = $unionType;
158+
159+
return $node;
160+
}
161+
105162
private function resolveType(Identifier $identifier): Type
106163
{
107164
$nodeName = $this->getName($identifier);

0 commit comments

Comments
 (0)