Skip to content

Commit 992f3ff

Browse files
[CodeQuality][Php84] Handle crash on ForeachToArrayFindRector+OptionalParametersAfterRequiredRector (#7033)
* [CodeQuality][Php84] Handle crash on ForeachToArrayFindRector+OptionalParametersAfterRequiredRector * rec * rec * fix * [ci-review] Rector Rectify * Fix --------- Co-authored-by: GitHub Action <actions@github.com>
1 parent 4c3ec6c commit 992f3ff

6 files changed

Lines changed: 120 additions & 16 deletions

File tree

rules/CodeQuality/Rector/ClassMethod/OptionalParametersAfterRequiredRector.php

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -89,22 +89,19 @@ public function getNodeTypes(): array
8989
*/
9090
public function refactor(Node $node): ClassMethod|Function_|null|New_|MethodCall|StaticCall|FuncCall
9191
{
92-
$scope = ScopeFetcher::fetch($node);
93-
9492
if ($node instanceof ClassMethod || $node instanceof Function_) {
95-
return $this->refactorClassMethodOrFunction($node, $scope);
93+
return $this->refactorClassMethodOrFunction($node);
9694
}
9795

9896
if ($node instanceof New_) {
99-
return $this->refactorNew($node, $scope);
97+
return $this->refactorNew($node);
10098
}
10199

102-
return $this->refactorMethodCallOrFuncCall($node, $scope);
100+
return $this->refactorMethodCallOrFuncCall($node);
103101
}
104102

105103
private function refactorClassMethodOrFunction(
106-
ClassMethod|Function_ $node,
107-
Scope $scope
104+
ClassMethod|Function_ $node
108105
): ClassMethod|Function_|null {
109106
if ($node->params === []) {
110107
return null;
@@ -114,6 +111,7 @@ private function refactorClassMethodOrFunction(
114111
return null;
115112
}
116113

114+
$scope = ScopeFetcher::fetch($node);
117115
if ($node instanceof ClassMethod) {
118116
$reflection = $this->reflectionResolver->resolveMethodReflectionFromClassMethod($node, $scope);
119117
} else {
@@ -138,7 +136,7 @@ private function refactorClassMethodOrFunction(
138136
return $node;
139137
}
140138

141-
private function refactorNew(New_ $new, Scope $scope): ?New_
139+
private function refactorNew(New_ $new): ?New_
142140
{
143141
if ($new->args === []) {
144142
return null;
@@ -153,6 +151,7 @@ private function refactorNew(New_ $new, Scope $scope): ?New_
153151
return null;
154152
}
155153

154+
$scope = ScopeFetcher::fetch($new);
156155
$expectedArgOrParamOrder = $this->resolveExpectedArgParamOrderIfDifferent($methodReflection, $new, $scope);
157156
if ($expectedArgOrParamOrder === null) {
158157
return null;
@@ -164,8 +163,7 @@ private function refactorNew(New_ $new, Scope $scope): ?New_
164163
}
165164

166165
private function refactorMethodCallOrFuncCall(
167-
MethodCall|StaticCall|FuncCall $node,
168-
Scope $scope
166+
MethodCall|StaticCall|FuncCall $node
169167
): MethodCall|StaticCall|FuncCall|null {
170168
if ($node->isFirstClassCallable()) {
171169
return null;
@@ -176,6 +174,7 @@ private function refactorMethodCallOrFuncCall(
176174
return null;
177175
}
178176

177+
$scope = ScopeFetcher::fetch($node);
179178
$expectedArgOrParamOrder = $this->resolveExpectedArgParamOrderIfDifferent($reflection, $node, $scope);
180179
if ($expectedArgOrParamOrder === null) {
181180
return null;
@@ -215,6 +214,7 @@ private function resolveExpectedArgParamOrderIfDifferent(
215214
return null;
216215
}
217216

217+
$scope = ScopeFetcher::fetch($node);
218218
$parametersAcceptor = ParametersAcceptorSelectorVariantsWrapper::select($reflection, $node, $scope);
219219
$expectedParameterReflections = $this->requireOptionalParamResolver->resolveFromParametersAcceptor(
220220
$parametersAcceptor

rules/Php84/Rector/Class_/DeprecatedAnnotationToDeprecatedAttributeRector.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,9 @@ private function createAttributeGroup(string $annotationValue): AttributeGroup
157157
value: new String_($annotationValue, [
158158
AttributeKey::KIND => String_::KIND_NOWDOC,
159159
AttributeKey::DOC_LABEL => 'TXT',
160-
]))]
161-
)
160+
])
161+
)]
162+
),
162163
]);
163164
}
164165

src/ProcessAnalyzer/RectifiedAnalyzer.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PhpParser\Node;
88
use PhpParser\Node\Stmt;
99
use Rector\Contract\Rector\RectorInterface;
10+
use Rector\NodeAnalyzer\ScopeAnalyzer;
1011
use Rector\NodeTypeResolver\Node\AttributeKey;
1112

1213
/**
@@ -17,8 +18,13 @@
1718
* - just re-printed but token start still >= 0
1819
* - has above node skipped traverse children on current rule
1920
*/
20-
final class RectifiedAnalyzer
21+
final readonly class RectifiedAnalyzer
2122
{
23+
public function __construct(
24+
private ScopeAnalyzer $scopeAnalyzer
25+
) {
26+
}
27+
2228
/**
2329
* @param class-string<RectorInterface> $rectorClass
2430
*/
@@ -81,10 +87,10 @@ private function isJustReprintedOverlappedTokenStart(Node $node, ?Node $original
8187
return true;
8288
}
8389

84-
if ($node instanceof Stmt) {
85-
return ! in_array(AttributeKey::SCOPE, array_keys($node->getAttributes()), true);
90+
if (! $this->scopeAnalyzer->isRefreshable($node)) {
91+
return false;
8692
}
8793

88-
return $node->getAttributes() === [];
94+
return ! in_array(AttributeKey::SCOPE, array_keys($node->getAttributes()), true);
8995
}
9096
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
namespace Rector\Tests\Issues\ScopeNotAvailable\FixtureForeachToArrayParam;
4+
5+
use App\Entity\Url;
6+
7+
class Fixture
8+
{
9+
/**
10+
* @param array<Url> $urls
11+
*/
12+
public function create(array $urls, string $hash): ?Url
13+
{
14+
if (0 === \count($urls)) {
15+
$url = new Url();
16+
} else {
17+
$url = null;
18+
foreach ($urls as $urlToCheck) {
19+
if ($urlToCheck->getPathHash() === $hash) {
20+
$url = $urlToCheck;
21+
break;
22+
}
23+
}
24+
}
25+
26+
return $url;
27+
}
28+
}
29+
30+
?>
31+
-----
32+
<?php
33+
34+
namespace Rector\Tests\Issues\ScopeNotAvailable\FixtureForeachToArrayParam;
35+
36+
use App\Entity\Url;
37+
38+
class Fixture
39+
{
40+
/**
41+
* @param array<Url> $urls
42+
*/
43+
public function create(array $urls, string $hash): ?Url
44+
{
45+
if (0 === \count($urls)) {
46+
$url = new Url();
47+
} else {
48+
$url = array_find($urls, fn($urlToCheck) => $urlToCheck->getPathHash() === $hash);
49+
}
50+
51+
return $url;
52+
}
53+
}
54+
55+
?>
Lines changed: 28 additions & 0 deletions
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\Issues\ScopeNotAvailable;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class ForeachToArrayParamTest 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__ . '/FixtureForeachToArrayParam');
22+
}
23+
24+
public function provideConfigFilePath(): string
25+
{
26+
return __DIR__ . '/config/foreach_to_array_param.php';
27+
}
28+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Rector\CodeQuality\Rector\ClassMethod\OptionalParametersAfterRequiredRector;
6+
use Rector\Config\RectorConfig;
7+
use Rector\Php84\Rector\Foreach_\ForeachToArrayFindRector;
8+
use Rector\ValueObject\PhpVersion;
9+
10+
return static function (RectorConfig $rectorConfig): void {
11+
$rectorConfig->rules([ForeachToArrayFindRector::class, OptionalParametersAfterRequiredRector::class]);
12+
13+
$rectorConfig->phpVersion(PhpVersion::PHP_84);
14+
};

0 commit comments

Comments
 (0)