Skip to content

Commit d5dc103

Browse files
committed
[rector] add rule to encourage node passing over assignig in rector rules
1 parent 103574d commit d5dc103

File tree

10 files changed

+164
-45
lines changed

10 files changed

+164
-45
lines changed

config/rector-rules.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ rules:
44
- Symplify\PHPStanRules\Rules\Rector\NoInstanceOfStaticReflectionRule
55
- Symplify\PHPStanRules\Rules\Rector\NoLeadingBackslashInNameRule
66
- Symplify\PHPStanRules\Rules\Rector\NoClassReflectionStaticReflectionRule
7+
- Symplify\PHPStanRules\Rules\Rector\NoPropertyNodeAssignRule
78

89
services:
910
# $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
@@ -13,4 +13,6 @@ final class RectorRuleIdentifier
1313
public const PHP_RULE_IMPLEMENTS_MIN_VERSION = 'rector.phpRuleImplementsMinVersion';
1414

1515
public const NO_CLASS_REFLECTION_STATIC_REFLECTION = 'rector.noClassReflectionStaticReflection';
16+
17+
public const NO_PROPERTY_NODE_ASSIGN = 'rector.noPropertyNodeAssign';
1618
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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\Assign;
9+
use PHPStan\Analyser\Scope;
10+
use PHPStan\Rules\Rule;
11+
use PHPStan\Rules\RuleErrorBuilder;
12+
use PHPStan\Type\ObjectType;
13+
use Rector\Contract\Rector\RectorInterface;
14+
use Symplify\PHPStanRules\Enum\RuleIdentifier\RectorRuleIdentifier;
15+
16+
/**
17+
* @see \Symplify\PHPStanRules\Tests\Rules\Rector\NoPropertyNodeAssignRule\NoPropertyNodeAssignRuleTest
18+
*
19+
* @implements Rule<Assign>
20+
*/
21+
final class NoPropertyNodeAssignRule implements Rule
22+
{
23+
/**
24+
* @var string
25+
*/
26+
public const ERROR_MESSAGE = 'Avoid assigning a node to property to avoid object juggling, pass it as argument instead';
27+
28+
public function getNodeType(): string
29+
{
30+
return Assign::class;
31+
}
32+
33+
/**
34+
* @param Assign $node
35+
*/
36+
public function processNode(Node $node, Scope $scope): array
37+
{
38+
if (! $scope->isInClass()) {
39+
return [];
40+
}
41+
42+
$classReflection = $scope->getClassReflection();
43+
if (! $classReflection->isSubclassOf(RectorInterface::class)) {
44+
return [];
45+
}
46+
47+
if (! $node->var instanceof Node\Expr\PropertyFetch) {
48+
return [];
49+
}
50+
51+
$propertyFetch = $node->var;
52+
if (! $propertyFetch->var instanceof Node\Expr\Variable) {
53+
return [];
54+
}
55+
56+
if (! is_string($propertyFetch->var->name)) {
57+
return [];
58+
}
59+
60+
if ($propertyFetch->var->name !== 'this') {
61+
return [];
62+
}
63+
64+
$assignedExprType = $scope->getType($node->expr);
65+
if (! $assignedExprType instanceof ObjectType) {
66+
return [];
67+
}
68+
69+
if (! $assignedExprType->isInstanceOf(\PhpParser\Node::class)->yes()) {
70+
return [];
71+
}
72+
73+
$ruleError = RuleErrorBuilder::message(self::ERROR_MESSAGE)
74+
->identifier(RectorRuleIdentifier::NO_PROPERTY_NODE_ASSIGN)
75+
->build();
76+
77+
return [$ruleError];
78+
}
79+
}

tests/Rules/Rector/NoLeadingBackslashInNameRule/Fixture/LeadingBackslashInName.php

Lines changed: 0 additions & 15 deletions
This file was deleted.
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\NoLeadingBackslashInNameRule\Fixture;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Name;
9+
use Rector\Rector\AbstractRector;
10+
11+
final class RectorWithPropertyNode extends AbstractRector
12+
{
13+
private ?Node\Stmt\Class_ $class = null;
14+
15+
public function getNodeTypes(): array
16+
{
17+
return [];
18+
}
19+
20+
public function refactor(Node $node)
21+
{
22+
$this->class = $node;
23+
24+
return null;
25+
}
26+
}

tests/Rules/Rector/NoLeadingBackslashInNameRule/Fixture/SkipNoBackslash.php

Lines changed: 0 additions & 15 deletions
This file was deleted.

tests/Rules/Rector/NoLeadingBackslashInNameRule/Fixture/SkipUseFullyQualified.php

Lines changed: 0 additions & 15 deletions
This file was deleted.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symplify\PHPStanRules\Tests\Rules\Rector\NoPropertyNodeAssignRule\Fixture;
6+
7+
use PhpParser\Node;
8+
use Rector\Rector\AbstractRector;
9+
10+
final class SomeNodePropertyAssign extends AbstractRector
11+
{
12+
private ?\PhpParser\Node $localNode = null;
13+
14+
public function getNodeTypes(): array
15+
{
16+
}
17+
18+
public function refactor(Node $node)
19+
{
20+
$this->localNode = $node;
21+
}
22+
}
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\NoPropertyNodeAssignRule;
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\NoPropertyNodeAssignRule;
12+
13+
final class NoPropertyNodeAssignRuleTest 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/SomeNodePropertyAssign.php', [
24+
[NoPropertyNodeAssignRule::ERROR_MESSAGE, 20],
25+
]];
26+
}
27+
28+
protected function getRule(): Rule
29+
{
30+
return new NoPropertyNodeAssignRule();
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\NoPropertyNodeAssignRule

0 commit comments

Comments
 (0)