Skip to content

Commit af1f20b

Browse files
committed
Updated Rector to commit d6fb9bf663e1b958328ef23c28b1bd0a3b9b7c69
rectorphp/rector-src@d6fb9bf [dead-code] Add RemoveParentDelegatingConstructorRector (#7769)
1 parent 7c34cfe commit af1f20b

File tree

10 files changed

+207
-11
lines changed

10 files changed

+207
-11
lines changed
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
<?php
2+
3+
declare (strict_types=1);
4+
namespace Rector\DeadCode\Rector\ClassMethod;
5+
6+
use PhpParser\Node;
7+
use PhpParser\Node\Arg;
8+
use PhpParser\Node\Expr\StaticCall;
9+
use PhpParser\Node\Expr\Variable;
10+
use PhpParser\Node\Stmt;
11+
use PhpParser\Node\Stmt\ClassMethod;
12+
use PhpParser\Node\Stmt\Expression;
13+
use PhpParser\NodeVisitor;
14+
use PHPStan\Reflection\ClassReflection;
15+
use PHPStan\Reflection\ExtendedMethodReflection;
16+
use Rector\Enum\ObjectReference;
17+
use Rector\PHPStan\ScopeFetcher;
18+
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
19+
use Rector\Rector\AbstractRector;
20+
use Rector\StaticTypeMapper\StaticTypeMapper;
21+
use Rector\ValueObject\MethodName;
22+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
23+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
24+
/**
25+
* @see \Rector\Tests\DeadCode\Rector\ClassMethod\RemoveParentDelegatingConstructorRector\RemoveParentDelegatingConstructorRectorTest
26+
*/
27+
final class RemoveParentDelegatingConstructorRector extends AbstractRector
28+
{
29+
/**
30+
* @readonly
31+
*/
32+
private StaticTypeMapper $staticTypeMapper;
33+
public function __construct(StaticTypeMapper $staticTypeMapper)
34+
{
35+
$this->staticTypeMapper = $staticTypeMapper;
36+
}
37+
public function getRuleDefinition(): RuleDefinition
38+
{
39+
return new RuleDefinition('Remove constructor that only delegates call to parent class with same values', [new CodeSample(<<<'CODE_SAMPLE'
40+
class Node
41+
{
42+
public function __construct(array $attributes)
43+
{
44+
}
45+
}
46+
47+
class SomeParent extends Node
48+
{
49+
public function __construct(array $attributes)
50+
{
51+
parent::__construct($attributes);
52+
}
53+
}
54+
CODE_SAMPLE
55+
, <<<'CODE_SAMPLE'
56+
class Node
57+
{
58+
public function __construct(array $attributes)
59+
{
60+
}
61+
}
62+
63+
class SomeParent extends Node
64+
{
65+
}
66+
CODE_SAMPLE
67+
)]);
68+
}
69+
/**
70+
* @return array<class-string<Node>>
71+
*/
72+
public function getNodeTypes(): array
73+
{
74+
return [ClassMethod::class];
75+
}
76+
/**
77+
* @param ClassMethod $node
78+
*/
79+
public function refactor(Node $node): ?int
80+
{
81+
if (!$this->isName($node, MethodName::CONSTRUCT)) {
82+
return null;
83+
}
84+
if ($node->stmts === null || count($node->stmts) !== 1) {
85+
return null;
86+
}
87+
$parentMethodReflection = $this->matchParentConstructorReflection($node);
88+
if (!$parentMethodReflection instanceof ExtendedMethodReflection) {
89+
return null;
90+
}
91+
$soleStmt = $node->stmts[0];
92+
$parentCallArgs = $this->matchParentConstructorCallArgs($soleStmt);
93+
if ($parentCallArgs === null) {
94+
return null;
95+
}
96+
// match count and order
97+
if (!$this->isParameterAndArgCountAndOrderIdentical($node)) {
98+
return null;
99+
}
100+
// match parameter types and parent constructor types
101+
if (!$this->areConstructorAndParentParameterTypesMatching($node, $parentMethodReflection)) {
102+
return null;
103+
}
104+
return NodeVisitor::REMOVE_NODE;
105+
}
106+
private function matchParentConstructorReflection(ClassMethod $classMethod): ?ExtendedMethodReflection
107+
{
108+
$scope = ScopeFetcher::fetch($classMethod);
109+
$classReflection = $scope->getClassReflection();
110+
if (!$classReflection instanceof ClassReflection) {
111+
return null;
112+
}
113+
$parentClassReflection = $classReflection->getParentClass();
114+
if (!$parentClassReflection instanceof ClassReflection) {
115+
return null;
116+
}
117+
if (!$parentClassReflection->hasConstructor()) {
118+
return null;
119+
}
120+
return $parentClassReflection->getConstructor();
121+
}
122+
/**
123+
* Looking for parent::__construct()
124+
*
125+
* @return Arg[]|null
126+
*/
127+
private function matchParentConstructorCallArgs(Stmt $stmt): ?array
128+
{
129+
if (!$stmt instanceof Expression) {
130+
return null;
131+
}
132+
if (!$stmt->expr instanceof StaticCall) {
133+
return null;
134+
}
135+
$staticCall = $stmt->expr;
136+
if ($staticCall->isFirstClassCallable()) {
137+
return null;
138+
}
139+
if (!$this->isName($staticCall->class, ObjectReference::PARENT)) {
140+
return null;
141+
}
142+
if (!$this->isName($staticCall->name, MethodName::CONSTRUCT)) {
143+
return null;
144+
}
145+
return $staticCall->getArgs();
146+
}
147+
private function isParameterAndArgCountAndOrderIdentical(ClassMethod $classMethod): bool
148+
{
149+
$soleStmt = $classMethod->stmts[0];
150+
$parentCallArgs = $this->matchParentConstructorCallArgs($soleStmt);
151+
if ($parentCallArgs === null) {
152+
return \false;
153+
}
154+
$constructorParams = $classMethod->getParams();
155+
if (count($constructorParams) !== count($parentCallArgs)) {
156+
return \false;
157+
}
158+
// match passed names in the same order
159+
$paramNames = [];
160+
foreach ($constructorParams as $constructorParam) {
161+
$paramNames[] = $this->getName($constructorParam->var);
162+
}
163+
$argNames = [];
164+
foreach ($parentCallArgs as $parentCallArg) {
165+
$argValue = $parentCallArg->value;
166+
if (!$argValue instanceof Variable) {
167+
return \false;
168+
}
169+
$argNames[] = $this->getName($argValue);
170+
}
171+
return $paramNames === $argNames;
172+
}
173+
private function areConstructorAndParentParameterTypesMatching(ClassMethod $classMethod, ExtendedMethodReflection $extendedMethodReflection): bool
174+
{
175+
foreach ($classMethod->getParams() as $position => $param) {
176+
$parameterType = $param->type;
177+
// no type override
178+
if ($parameterType === null) {
179+
continue;
180+
}
181+
$parametersSelector = $extendedMethodReflection->getOnlyVariant();
182+
foreach ($parametersSelector->getParameters() as $index => $parameterReflection) {
183+
if ($index !== $position) {
184+
continue;
185+
}
186+
$parentParameterType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($parameterReflection->getType(), TypeKind::PARAM);
187+
if (!$this->nodeComparator->areNodesEqual($parameterType, $parentParameterType)) {
188+
return \false;
189+
}
190+
}
191+
}
192+
return \true;
193+
}
194+
}

src/Application/VersionResolver.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ final class VersionResolver
1919
* @api
2020
* @var string
2121
*/
22-
public const PACKAGE_VERSION = '083c72809d8a31a3c618e4002d79b959c5888364';
22+
public const PACKAGE_VERSION = 'd6fb9bf663e1b958328ef23c28b1bd0a3b9b7c69';
2323
/**
2424
* @api
2525
* @var string
2626
*/
27-
public const RELEASE_DATE = '2025-12-22 14:27:18';
27+
public const RELEASE_DATE = '2025-12-22 16:17:50';
2828
/**
2929
* @var int
3030
*/

src/Config/Level/DeadCodeLevel.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Rector\DeadCode\Rector\ClassMethod\RemoveArgumentFromDefaultParentCallRector;
1717
use Rector\DeadCode\Rector\ClassMethod\RemoveEmptyClassMethodRector;
1818
use Rector\DeadCode\Rector\ClassMethod\RemoveNullTagValueNodeRector;
19+
use Rector\DeadCode\Rector\ClassMethod\RemoveParentDelegatingConstructorRector;
1920
use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedConstructorParamRector;
2021
use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodParameterRector;
2122
use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodRector;
@@ -125,6 +126,7 @@ final class DeadCodeLevel
125126
RemoveDeadStmtRector::class,
126127
UnwrapFutureCompatibleIfPhpVersionRector::class,
127128
RemoveParentCallWithoutParentRector::class,
129+
RemoveParentDelegatingConstructorRector::class,
128130
RemoveDeadConditionAboveReturnRector::class,
129131
RemoveDeadLoopRector::class,
130132
// removing methods could be risky if there is some magic loading them

src/Reporting/DeprecatedRulesReporter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
declare (strict_types=1);
44
namespace Rector\Reporting;
55

6-
use Rector\PhpParserNode\FileNode;
76
use Rector\Configuration\Deprecation\Contract\DeprecatedInterface;
87
use Rector\Configuration\Option;
98
use Rector\Configuration\Parameter\SimpleParameterProvider;
109
use Rector\Contract\PhpParser\Node\StmtsAwareInterface;
1110
use Rector\Contract\Rector\RectorInterface;
1211
use Rector\PhpParser\Enum\NodeGroup;
12+
use Rector\PhpParserNode\FileNode;
1313
use ReflectionMethod;
1414
use RectorPrefix202512\Symfony\Component\Console\Style\SymfonyStyle;
1515
final class DeprecatedRulesReporter

vendor/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1400,6 +1400,7 @@
14001400
'Rector\\DeadCode\\Rector\\ClassMethod\\RemoveArgumentFromDefaultParentCallRector' => $baseDir . '/rules/DeadCode/Rector/ClassMethod/RemoveArgumentFromDefaultParentCallRector.php',
14011401
'Rector\\DeadCode\\Rector\\ClassMethod\\RemoveEmptyClassMethodRector' => $baseDir . '/rules/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector.php',
14021402
'Rector\\DeadCode\\Rector\\ClassMethod\\RemoveNullTagValueNodeRector' => $baseDir . '/rules/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector.php',
1403+
'Rector\\DeadCode\\Rector\\ClassMethod\\RemoveParentDelegatingConstructorRector' => $baseDir . '/rules/DeadCode/Rector/ClassMethod/RemoveParentDelegatingConstructorRector.php',
14031404
'Rector\\DeadCode\\Rector\\ClassMethod\\RemoveUnusedConstructorParamRector' => $baseDir . '/rules/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector.php',
14041405
'Rector\\DeadCode\\Rector\\ClassMethod\\RemoveUnusedPrivateMethodParameterRector' => $baseDir . '/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector.php',
14051406
'Rector\\DeadCode\\Rector\\ClassMethod\\RemoveUnusedPrivateMethodRector' => $baseDir . '/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector.php',

vendor/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1660,6 +1660,7 @@ class ComposerStaticInit8feb715f5d726b0b23c2b018f39541bb
16601660
'Rector\\DeadCode\\Rector\\ClassMethod\\RemoveArgumentFromDefaultParentCallRector' => __DIR__ . '/../..' . '/rules/DeadCode/Rector/ClassMethod/RemoveArgumentFromDefaultParentCallRector.php',
16611661
'Rector\\DeadCode\\Rector\\ClassMethod\\RemoveEmptyClassMethodRector' => __DIR__ . '/../..' . '/rules/DeadCode/Rector/ClassMethod/RemoveEmptyClassMethodRector.php',
16621662
'Rector\\DeadCode\\Rector\\ClassMethod\\RemoveNullTagValueNodeRector' => __DIR__ . '/../..' . '/rules/DeadCode/Rector/ClassMethod/RemoveNullTagValueNodeRector.php',
1663+
'Rector\\DeadCode\\Rector\\ClassMethod\\RemoveParentDelegatingConstructorRector' => __DIR__ . '/../..' . '/rules/DeadCode/Rector/ClassMethod/RemoveParentDelegatingConstructorRector.php',
16631664
'Rector\\DeadCode\\Rector\\ClassMethod\\RemoveUnusedConstructorParamRector' => __DIR__ . '/../..' . '/rules/DeadCode/Rector/ClassMethod/RemoveUnusedConstructorParamRector.php',
16641665
'Rector\\DeadCode\\Rector\\ClassMethod\\RemoveUnusedPrivateMethodParameterRector' => __DIR__ . '/../..' . '/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodParameterRector.php',
16651666
'Rector\\DeadCode\\Rector\\ClassMethod\\RemoveUnusedPrivateMethodRector' => __DIR__ . '/../..' . '/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector.php',

vendor/composer/installed.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1694,12 +1694,12 @@
16941694
"source": {
16951695
"type": "git",
16961696
"url": "https:\/\/github.com\/rectorphp\/rector-doctrine.git",
1697-
"reference": "5366b9aec649d31275938b35bc6c6e3419508efe"
1697+
"reference": "5159e01e642e967f52d09783bc38ae2ff925fe44"
16981698
},
16991699
"dist": {
17001700
"type": "zip",
1701-
"url": "https:\/\/api.github.com\/repos\/rectorphp\/rector-doctrine\/zipball\/5366b9aec649d31275938b35bc6c6e3419508efe",
1702-
"reference": "5366b9aec649d31275938b35bc6c6e3419508efe",
1701+
"url": "https:\/\/api.github.com\/repos\/rectorphp\/rector-doctrine\/zipball\/5159e01e642e967f52d09783bc38ae2ff925fe44",
1702+
"reference": "5159e01e642e967f52d09783bc38ae2ff925fe44",
17031703
"shasum": ""
17041704
},
17051705
"require": {
@@ -1724,7 +1724,7 @@
17241724
"tomasvotruba\/class-leak": "^2.1",
17251725
"tracy\/tracy": "^2.11"
17261726
},
1727-
"time": "2025-12-03T11:25:07+00:00",
1727+
"time": "2025-12-22T13:38:13+00:00",
17281728
"default-branch": true,
17291729
"type": "rector-extension",
17301730
"extra": {

0 commit comments

Comments
 (0)