Skip to content

Commit d5ef3f8

Browse files
authored
[rector] add NoOnlyNullReturnInRefactorRule (#247)
1 parent 0abcb28 commit d5ef3f8

File tree

7 files changed

+166
-0
lines changed

7 files changed

+166
-0
lines changed

config/rector-rules.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ rules:
66
- Symplify\PHPStanRules\Rules\Rector\NoClassReflectionStaticReflectionRule
77
- Symplify\PHPStanRules\Rules\Rector\NoPropertyNodeAssignRule
88
- Symplify\PHPStanRules\Rules\Rector\PreferDirectIsNameRule
9+
- Symplify\PHPStanRules\Rules\Rector\NoOnlyNullReturnInRefactorRule
910

1011
services:
1112
# $node->getAttribute($1) => Type|null by $1

src/Enum/RuleIdentifier/RectorRuleIdentifier.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,6 @@ final class RectorRuleIdentifier
1717
public const NO_PROPERTY_NODE_ASSIGN = 'rector.noPropertyNodeAssign';
1818

1919
public const PREFER_DIRECT_IS_NAME = 'rector.preferDirectIsName';
20+
21+
public const NO_ONLY_NULL_RETURN_IN_REFACTOR = 'rector.noOnlyNullReturnInRefactor';
2022
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symplify\PHPStanRules\Rules\Rector;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Expr\ConstFetch;
9+
use PhpParser\Node\Stmt\ClassMethod;
10+
use PhpParser\Node\Stmt\Return_;
11+
use PhpParser\NodeFinder;
12+
use PHPStan\Analyser\Scope;
13+
use PHPStan\Reflection\ClassReflection;
14+
use PHPStan\Rules\Rule;
15+
use PHPStan\Rules\RuleErrorBuilder;
16+
use Rector\Rector\AbstractRector;
17+
use Symplify\PHPStanRules\Enum\RuleIdentifier\RectorRuleIdentifier;
18+
use Symplify\PHPStanRules\Helper\NamingHelper;
19+
20+
/**
21+
* @see \Symplify\PHPStanRules\Tests\Rules\Rector\NoOnlyNullReturnInRefactorRule\NoOnlyNullReturnInRefactorRuleTest
22+
*
23+
* @implements Rule<ClassMethod>
24+
*/
25+
final class NoOnlyNullReturnInRefactorRule implements Rule
26+
{
27+
/**
28+
* @var string
29+
*/
30+
public const ERROR_MESSAGE = 'The refactor() method returns always null, but it should return at least one modified node';
31+
32+
public function getNodeType(): string
33+
{
34+
return ClassMethod::class;
35+
}
36+
37+
/**
38+
* @param ClassMethod $node
39+
*/
40+
public function processNode(Node $node, Scope $scope): array
41+
{
42+
if (! NamingHelper::isName($node->name, 'refactor')) {
43+
return [];
44+
}
45+
46+
$classReflection = $scope->getClassReflection();
47+
if (! $classReflection instanceof ClassReflection) {
48+
return [];
49+
}
50+
51+
if (! $classReflection->is(AbstractRector::class)) {
52+
return [];
53+
}
54+
55+
$nodeFinder = new NodeFinder();
56+
$returns = $nodeFinder->findInstanceOf((array) $node->stmts, Return_::class);
57+
58+
// should not happen, but out of scope of this PR
59+
if ($returns === []) {
60+
return [];
61+
}
62+
63+
foreach ($returns as $return) {
64+
if (! $return->expr instanceof ConstFetch) {
65+
return [];
66+
}
67+
68+
if ($return->expr->name->toLowerString() !== 'null') {
69+
return [];
70+
}
71+
}
72+
73+
return [RuleErrorBuilder::message(self::ERROR_MESSAGE)
74+
->identifier(RectorRuleIdentifier::NO_ONLY_NULL_RETURN_IN_REFACTOR)
75+
->build()];
76+
}
77+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symplify\PHPStanRules\Tests\Rules\Rector\NoOnlyNullReturnInRefactorRule\Fixture;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Stmt\If_;
9+
use Rector\Rector\AbstractRector;
10+
11+
final class RefactorOnlyReturnNull extends AbstractRector
12+
{
13+
public function getNodeTypes(): array
14+
{
15+
return [];
16+
}
17+
18+
public function refactor(Node $node)
19+
{
20+
if ($node instanceof If_) {
21+
return null;
22+
}
23+
24+
return null;
25+
}
26+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symplify\PHPStanRules\Tests\Rules\Rector\NoOnlyNullReturnInRefactorRule\Fixture;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Stmt\If_;
9+
use Rector\Rector\AbstractRector;
10+
11+
final class SkipOtherReturnThanNull extends AbstractRector
12+
{
13+
public function getNodeTypes(): array
14+
{
15+
return [];
16+
}
17+
18+
public function refactor(Node $node)
19+
{
20+
if ($node instanceof If_) {
21+
return null;
22+
}
23+
24+
return $node;
25+
}
26+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symplify\PHPStanRules\Tests\Rules\Rector\NoOnlyNullReturnInRefactorRule;
6+
7+
use Iterator;
8+
use PHPStan\Rules\Rule;
9+
use PHPStan\Testing\RuleTestCase;
10+
use PHPUnit\Framework\Attributes\DataProvider;
11+
use Symplify\PHPStanRules\Rules\Rector\NoOnlyNullReturnInRefactorRule;
12+
13+
final class NoOnlyNullReturnInRefactorRuleTest extends RuleTestCase
14+
{
15+
#[DataProvider('provideData')]
16+
public function testRule(string $filePath, array $expectedErrorsWithLines): void
17+
{
18+
$this->analyse([$filePath], $expectedErrorsWithLines);
19+
}
20+
21+
public static function provideData(): Iterator
22+
{
23+
yield [__DIR__ . '/Fixture/RefactorOnlyReturnNull.php', [[NoOnlyNullReturnInRefactorRule::ERROR_MESSAGE, 18]]];
24+
25+
yield [__DIR__ . '/Fixture/SkipOtherReturnThanNull.php', []];
26+
}
27+
28+
protected function getRule(): Rule
29+
{
30+
return new NoOnlyNullReturnInRefactorRule();
31+
}
32+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
rules:
2+
- Symplify\PHPStanRules\Rules\Rector\NoOnlyNullReturnInRefactorRule

0 commit comments

Comments
 (0)