Skip to content

Commit 653ec23

Browse files
authored
fix(dead-code): preserve generic @return tags in RemoveReturnTagIncompatibleWithNativeTypeRector (#8183)
The rule incorrectly removed `@return self<T>` and `@return ClassName<T>` annotations when the native return type was `self`. These annotations are not incompatible, because they carry template parameters that the native type cannot express. PHPStan and Psalm rely on them for generic type inference. Add an early bail-out when the `@return` type node is a `GenericTypeNode`, which covers all parameterized return types (`self<T>`, `static<T>`, `Foo<T>`).
1 parent 3c481b6 commit 653ec23

3 files changed

Lines changed: 63 additions & 0 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace Rector\Tests\DeadCode\Rector\ClassMethod\RemoveReturnTagIncompatibleWithNativeTypeRector\Fixture;
4+
5+
/**
6+
* @template TPayload
7+
* @template TDomainId
8+
*/
9+
final class SkipGenericFluentBuilder
10+
{
11+
/**
12+
* @return SkipGenericFluentBuilder<TPayload, TDomainId>
13+
*/
14+
public function withFoo(): self
15+
{
16+
return $this;
17+
}
18+
19+
/**
20+
* @return SkipGenericFluentBuilder<TPayload, TDomainId>
21+
*/
22+
public function withBar(): self
23+
{
24+
return $this;
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace Rector\Tests\DeadCode\Rector\ClassMethod\RemoveReturnTagIncompatibleWithNativeTypeRector\Fixture;
4+
5+
/**
6+
* @template TAdded
7+
* @template TRemoved
8+
*/
9+
final readonly class SkipGenericSelfReturn
10+
{
11+
/**
12+
* @param list<TAdded> $added
13+
* @param list<TRemoved> $removed
14+
*/
15+
private function __construct(
16+
public array $added,
17+
public array $removed,
18+
) {
19+
}
20+
21+
/**
22+
* @param list<TAdded> $added
23+
* @param list<TRemoved> $removed
24+
*
25+
* @return self<TAdded, TRemoved>
26+
*/
27+
public static function create(array $added, array $removed): self
28+
{
29+
return new self($added, $removed);
30+
}
31+
}

rules/DeadCode/Rector/ClassMethod/RemoveReturnTagIncompatibleWithNativeTypeRector.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use PHPStan\Analyser\Scope;
1111
use PHPStan\PhpDoc\ResolvedPhpDocBlock;
1212
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
13+
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
1314
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
1415
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
1516
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
@@ -103,6 +104,11 @@ public function refactor(Node $node): ?Node
103104
return null;
104105
}
105106

107+
// keep generic return types, because they carry template info the native type cannot express
108+
if ($returnTagValueNode->type instanceof GenericTypeNode) {
109+
return null;
110+
}
111+
106112
if ($this->isClassTypeAlias($node, $returnTagValueNode)) {
107113
return null;
108114
}

0 commit comments

Comments
 (0)