Skip to content

Commit 696058c

Browse files
[DeadCode] Add RemoveDeadInstanceOfAssertRector (#8202)
Co-authored-by: GitHub Action <actions@github.com>
1 parent 286fdb5 commit 696058c

11 files changed

Lines changed: 319 additions & 1 deletion

File tree

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
namespace Rector\Tests\DeadCode\Rector\StmtsAwareInterface\RemoveDeadInstanceOfAssertRector\Fixture;
4+
5+
use Rector\Tests\DeadCode\Rector\StmtsAwareInterface\RemoveDeadInstanceOfAssertRector\Source\UserRepository;
6+
7+
final class PromotedProperty
8+
{
9+
public function __construct(
10+
private UserRepository $userRepository
11+
) {
12+
}
13+
14+
public function run(): void
15+
{
16+
assert($this->userRepository instanceof UserRepository);
17+
}
18+
}
19+
20+
?>
21+
-----
22+
<?php
23+
24+
namespace Rector\Tests\DeadCode\Rector\StmtsAwareInterface\RemoveDeadInstanceOfAssertRector\Fixture;
25+
26+
use Rector\Tests\DeadCode\Rector\StmtsAwareInterface\RemoveDeadInstanceOfAssertRector\Source\UserRepository;
27+
28+
final class PromotedProperty
29+
{
30+
public function __construct(
31+
private UserRepository $userRepository
32+
) {
33+
}
34+
35+
public function run(): void
36+
{
37+
}
38+
}
39+
40+
?>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Rector\Tests\DeadCode\Rector\StmtsAwareInterface\RemoveDeadInstanceOfAssertRector\Fixture;
4+
5+
use Rector\Tests\DeadCode\Rector\StmtsAwareInterface\RemoveDeadInstanceOfAssertRector\Source\UserRepository;
6+
7+
final class SkipNullableProperty
8+
{
9+
public function __construct(
10+
private ?UserRepository $userRepository = null
11+
) {
12+
}
13+
14+
public function run(): void
15+
{
16+
assert($this->userRepository instanceof UserRepository);
17+
}
18+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Rector\Tests\DeadCode\Rector\StmtsAwareInterface\RemoveDeadInstanceOfAssertRector\Fixture;
4+
5+
use Rector\Tests\DeadCode\Rector\StmtsAwareInterface\RemoveDeadInstanceOfAssertRector\Source\UserRepository;
6+
7+
final class SkipUntypedProperty
8+
{
9+
private $userRepository;
10+
11+
public function run(): void
12+
{
13+
assert($this->userRepository instanceof UserRepository);
14+
}
15+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Rector\Tests\DeadCode\Rector\StmtsAwareInterface\RemoveDeadInstanceOfAssertRector\Fixture;
4+
5+
use Rector\Tests\DeadCode\Rector\StmtsAwareInterface\RemoveDeadInstanceOfAssertRector\Source\UserRepository;
6+
7+
final class SkipWithMessage
8+
{
9+
public function __construct(
10+
private UserRepository $userRepository
11+
) {
12+
}
13+
14+
public function run(): void
15+
{
16+
assert($this->userRepository instanceof UserRepository, 'must be set');
17+
}
18+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Rector\Tests\DeadCode\Rector\StmtsAwareInterface\RemoveDeadInstanceOfAssertRector\Fixture;
4+
5+
use Rector\Tests\DeadCode\Rector\StmtsAwareInterface\RemoveDeadInstanceOfAssertRector\Source\UserRepository;
6+
7+
function typedVariable(UserRepository $userRepository): void
8+
{
9+
assert($userRepository instanceof UserRepository);
10+
}
11+
12+
?>
13+
-----
14+
<?php
15+
16+
namespace Rector\Tests\DeadCode\Rector\StmtsAwareInterface\RemoveDeadInstanceOfAssertRector\Fixture;
17+
18+
use Rector\Tests\DeadCode\Rector\StmtsAwareInterface\RemoveDeadInstanceOfAssertRector\Source\UserRepository;
19+
20+
function typedVariable(UserRepository $userRepository): void
21+
{
22+
}
23+
24+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Tests\DeadCode\Rector\StmtsAwareInterface\RemoveDeadInstanceOfAssertRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class RemoveDeadInstanceOfAssertRectorTest extends AbstractRectorTestCase
12+
{
13+
#[DataProvider('provideData')]
14+
public function test(string $filePath): void
15+
{
16+
$this->doTestFile($filePath);
17+
}
18+
19+
public static function provideData(): Iterator
20+
{
21+
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
22+
}
23+
24+
public function provideConfigFilePath(): string
25+
{
26+
return __DIR__ . '/config/configured_rule.php';
27+
}
28+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Tests\DeadCode\Rector\StmtsAwareInterface\RemoveDeadInstanceOfAssertRector\Source;
6+
7+
final class UserRepository
8+
{
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Rector\Config\RectorConfig;
6+
use Rector\DeadCode\Rector\StmtsAwareInterface\RemoveDeadInstanceOfAssertRector;
7+
8+
return RectorConfig::configure()
9+
->withRules([RemoveDeadInstanceOfAssertRector::class]);
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\DeadCode\Rector\StmtsAwareInterface;
6+
7+
use PhpParser\Comment\Doc;
8+
use PhpParser\Node;
9+
use PhpParser\Node\Expr;
10+
use PhpParser\Node\Expr\FuncCall;
11+
use PhpParser\Node\Expr\Instanceof_;
12+
use PhpParser\Node\Expr\PropertyFetch;
13+
use PhpParser\Node\Expr\Variable;
14+
use PhpParser\Node\Name;
15+
use PhpParser\Node\Stmt\Expression;
16+
use PhpParser\Node\Stmt\Nop;
17+
use Rector\PhpParser\Enum\NodeGroup;
18+
use Rector\Rector\AbstractRector;
19+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
20+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
21+
22+
/**
23+
* @see \Rector\Tests\DeadCode\Rector\StmtsAwareInterface\RemoveDeadInstanceOfAssertRector\RemoveDeadInstanceOfAssertRectorTest
24+
*/
25+
final class RemoveDeadInstanceOfAssertRector extends AbstractRector
26+
{
27+
public function getRuleDefinition(): RuleDefinition
28+
{
29+
return new RuleDefinition(
30+
'Remove assert() with instanceof check on a value whose type is already known',
31+
[
32+
new CodeSample(
33+
<<<'CODE_SAMPLE'
34+
final class SomeClass
35+
{
36+
public function __construct(
37+
private UserRepository $userRepository
38+
) {
39+
}
40+
41+
public function run(): void
42+
{
43+
assert($this->userRepository instanceof UserRepository);
44+
}
45+
}
46+
CODE_SAMPLE
47+
,
48+
<<<'CODE_SAMPLE'
49+
final class SomeClass
50+
{
51+
public function __construct(
52+
private UserRepository $userRepository
53+
) {
54+
}
55+
56+
public function run(): void
57+
{
58+
}
59+
}
60+
CODE_SAMPLE
61+
),
62+
]
63+
);
64+
}
65+
66+
public function getNodeTypes(): array
67+
{
68+
return NodeGroup::STMTS_AWARE;
69+
}
70+
71+
/**
72+
* @param StmtsAware $node
73+
*/
74+
public function refactor(Node $node): ?Node
75+
{
76+
if ($node->stmts === null) {
77+
return null;
78+
}
79+
80+
$hasChanged = false;
81+
82+
foreach ($node->stmts as $key => $stmt) {
83+
if (! $stmt instanceof Expression) {
84+
continue;
85+
}
86+
87+
if (! $this->isDeadInstanceOfAssert($stmt->expr)) {
88+
continue;
89+
}
90+
91+
$docComment = $stmt->getDocComment();
92+
if ($docComment instanceof Doc) {
93+
$nop = new Nop();
94+
$nop->setDocComment($docComment);
95+
$node->stmts[$key] = $nop;
96+
} else {
97+
unset($node->stmts[$key]);
98+
}
99+
100+
$hasChanged = true;
101+
}
102+
103+
if (! $hasChanged) {
104+
return null;
105+
}
106+
107+
$node->stmts = array_values($node->stmts);
108+
109+
return $node;
110+
}
111+
112+
private function isDeadInstanceOfAssert(Expr $expr): bool
113+
{
114+
if (! $expr instanceof FuncCall) {
115+
return false;
116+
}
117+
118+
if ($expr->isFirstClassCallable()) {
119+
return false;
120+
}
121+
122+
if (! $this->isName($expr, 'assert')) {
123+
return false;
124+
}
125+
126+
// single-arg assert only; keep asserts that carry a description message
127+
if (count($expr->getArgs()) !== 1) {
128+
return false;
129+
}
130+
131+
$instanceof = $expr->getArgs()[0]
132+
->value;
133+
if (! $instanceof instanceof Instanceof_) {
134+
return false;
135+
}
136+
137+
// only property fetch or variable value
138+
if (! $instanceof->expr instanceof PropertyFetch && ! $instanceof->expr instanceof Variable) {
139+
return false;
140+
}
141+
142+
if (! $instanceof->class instanceof Name) {
143+
return false;
144+
}
145+
146+
$classType = $this->nodeTypeResolver->getType($instanceof->class);
147+
$exprType = $this->nodeTypeResolver->getType($instanceof->expr);
148+
if ($classType->equals($exprType)) {
149+
return true;
150+
}
151+
152+
return $classType->isSuperTypeOf($exprType)
153+
->yes();
154+
}
155+
}

‎rules/Privatization/Guard/ParentPropertyLookupGuard.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
*
2929
* @var string[]
3030
*/
31-
private const SYMFONY_COMMAND_RESERVED_PROPERTY_NAMES = ['defaultName', 'defaultDescription'];
31+
private const array SYMFONY_COMMAND_RESERVED_PROPERTY_NAMES = ['defaultName', 'defaultDescription'];
3232

3333
public function __construct(
3434
private BetterNodeFinder $betterNodeFinder,

0 commit comments

Comments
 (0)