Skip to content

Commit 56d179b

Browse files
committed
Updated Rector to commit dd21759b1194fe28cd266337124fd3035c62ead9
rectorphp/rector-src@dd21759 [skip] mark class/path skip used only when rule would change the file (#8076)
1 parent 228203d commit 56d179b

8 files changed

Lines changed: 98 additions & 26 deletions

File tree

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 = '2328ea6338d2496c409aaf2d8a001052e323feda';
22+
public const PACKAGE_VERSION = 'dd21759b1194fe28cd266337124fd3035c62ead9';
2323
/**
2424
* @api
2525
* @var string
2626
*/
27-
public const RELEASE_DATE = '2026-06-21 14:38:56';
27+
public const RELEASE_DATE = '2026-06-22 11:04:17';
2828
/**
2929
* @var int
3030
*/

src/Rector/AbstractRector.php

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
use PhpParser\Node\Stmt\Interface_;
1414
use PhpParser\Node\Stmt\Property;
1515
use PhpParser\Node\Stmt\Trait_;
16+
use PhpParser\NodeTraverser;
1617
use PhpParser\NodeVisitor;
18+
use PhpParser\NodeVisitor\CloningVisitor;
1719
use PhpParser\NodeVisitorAbstract;
1820
use PHPStan\Analyser\MutatingScope;
1921
use PHPStan\Type\ObjectType;
@@ -33,6 +35,7 @@
3335
use Rector\PhpParser\Comparing\NodeComparator;
3436
use Rector\PhpParser\Node\NodeFactory;
3537
use Rector\Skipper\Skipper\Skipper;
38+
use Rector\Skipper\ValueObject\SkipMatch;
3639
use Rector\ValueObject\Application\File;
3740
abstract class AbstractRector extends NodeVisitorAbstract implements RectorInterface
3841
{
@@ -106,7 +109,18 @@ final public function enterNode(Node $node)
106109
return null;
107110
}
108111
$filePath = $this->file->getFilePath();
109-
if ($this->skipper->shouldSkipCurrentNode($this, $filePath, static::class, $node)) {
112+
// node already changed by this rule in a previous pass → hard skip
113+
if ($this->skipper->shouldSkipCurrentNode(static::class, $node)) {
114+
return null;
115+
}
116+
// class/path skip is configured for this rule and file: run the rule on a deep clone to learn
117+
// whether it would actually have changed anything. Only a skip that prevents a real change
118+
// counts as used; the original node is left untouched, so the file stays skipped either way.
119+
$skipMatch = $this->skipper->matchSkip($this, $filePath);
120+
if ($skipMatch instanceof SkipMatch) {
121+
if ($this->refactor($this->cloneNode($node)) !== null) {
122+
$this->skipper->markSkipUsed($skipMatch);
123+
}
110124
return null;
111125
}
112126
// ensure origNode pulled before refactor to avoid changed during refactor, ref https://3v4l.org/YMEGN
@@ -210,6 +224,14 @@ protected function mirrorComments(Node $newNode, Node $oldNode): void
210224
{
211225
$this->commentsMerger->mirrorComments($newNode, $oldNode);
212226
}
227+
/**
228+
* Deep clone, so a skipped rule can be probed on the clone without mutating the real node.
229+
*/
230+
private function cloneNode(Node $node): Node
231+
{
232+
$nodeTraverser = new NodeTraverser(new CloningVisitor());
233+
return $nodeTraverser->traverse([$node])[0];
234+
}
213235
/**
214236
* @param Node|Node[] $refactoredNode
215237
* @return Node|Node[]

src/Skipper/SkipVoter/ClassSkipVoter.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use PHPStan\Reflection\ReflectionProvider;
77
use Rector\Skipper\SkipCriteriaResolver\SkippedClassResolver;
88
use Rector\Skipper\Skipper\SkipSkipper;
9+
use Rector\Skipper\ValueObject\SkipMatch;
910
final class ClassSkipVoter
1011
{
1112
/**
@@ -39,9 +40,9 @@ public function match($element): bool
3940
/**
4041
* @param string|object $element
4142
*/
42-
public function shouldSkip($element, string $filePath): bool
43+
public function matchSkip($element, string $filePath): ?SkipMatch
4344
{
4445
$skippedClasses = $this->skippedClassResolver->resolve();
45-
return $this->skipSkipper->doesMatchSkip($element, $filePath, $skippedClasses);
46+
return $this->skipSkipper->match($element, $filePath, $skippedClasses);
4647
}
4748
}

src/Skipper/Skipper/SkipSkipper.php

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,38 @@
44
namespace Rector\Skipper\Skipper;
55

66
use Rector\Skipper\Matcher\FileInfoMatcher;
7+
use Rector\Skipper\ValueObject\SkipMatch;
78
final class SkipSkipper
89
{
910
/**
1011
* @readonly
1112
*/
1213
private FileInfoMatcher $fileInfoMatcher;
13-
/**
14-
* @readonly
15-
*/
16-
private \Rector\Skipper\Skipper\UsedSkipCollector $usedSkipCollector;
17-
public function __construct(FileInfoMatcher $fileInfoMatcher, \Rector\Skipper\Skipper\UsedSkipCollector $usedSkipCollector)
14+
public function __construct(FileInfoMatcher $fileInfoMatcher)
1815
{
1916
$this->fileInfoMatcher = $fileInfoMatcher;
20-
$this->usedSkipCollector = $usedSkipCollector;
2117
}
2218
/**
2319
* @param array<string, string[]|null> $skippedClasses
2420
* @param object|string $checker
2521
*/
26-
public function doesMatchSkip($checker, string $filePath, array $skippedClasses): bool
22+
public function match($checker, string $filePath, array $skippedClasses): ?SkipMatch
2723
{
2824
foreach ($skippedClasses as $skippedClass => $skippedFiles) {
2925
if (!is_a($checker, $skippedClass, \true)) {
3026
continue;
3127
}
3228
// skip everywhere
3329
if (!is_array($skippedFiles)) {
34-
$this->usedSkipCollector->markUsed($skippedClass);
35-
return \true;
30+
return new SkipMatch($skippedClass, null);
3631
}
37-
// mark the specific matched path used, scoped to its rule - the same path can be skipped
38-
// under multiple rules, so the path is nested under its rule, not tracked on its own
32+
// the same path can be skipped under multiple rules, so the matched path is reported
33+
// scoped to its rule, not tracked on its own
3934
$matchedPath = $this->fileInfoMatcher->matchPattern($filePath, $skippedFiles);
4035
if ($matchedPath !== null) {
41-
$this->usedSkipCollector->markUsed($skippedClass, $matchedPath);
42-
return \true;
36+
return new SkipMatch($skippedClass, $matchedPath);
4337
}
4438
}
45-
return \false;
39+
return null;
4640
}
4741
}

src/Skipper/Skipper/Skipper.php

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Rector\Contract\Rector\RectorInterface;
88
use Rector\ProcessAnalyzer\RectifiedAnalyzer;
99
use Rector\Skipper\SkipVoter\ClassSkipVoter;
10+
use Rector\Skipper\ValueObject\SkipMatch;
1011
/**
1112
* @api
1213
* @see \Rector\Tests\Skipper\Skipper\SkipperTest
@@ -25,11 +26,16 @@ final class Skipper
2526
* @readonly
2627
*/
2728
private ClassSkipVoter $classSkipVoter;
28-
public function __construct(RectifiedAnalyzer $rectifiedAnalyzer, \Rector\Skipper\Skipper\PathSkipper $pathSkipper, ClassSkipVoter $classSkipVoter)
29+
/**
30+
* @readonly
31+
*/
32+
private \Rector\Skipper\Skipper\UsedSkipCollector $usedSkipCollector;
33+
public function __construct(RectifiedAnalyzer $rectifiedAnalyzer, \Rector\Skipper\Skipper\PathSkipper $pathSkipper, ClassSkipVoter $classSkipVoter, \Rector\Skipper\Skipper\UsedSkipCollector $usedSkipCollector)
2934
{
3035
$this->rectifiedAnalyzer = $rectifiedAnalyzer;
3136
$this->pathSkipper = $pathSkipper;
3237
$this->classSkipVoter = $classSkipVoter;
38+
$this->usedSkipCollector = $usedSkipCollector;
3339
}
3440
/**
3541
* @param string|object $element
@@ -47,20 +53,34 @@ public function shouldSkipFilePath(string $filePath): bool
4753
*/
4854
public function shouldSkipElementAndFilePath($element, string $filePath): bool
4955
{
50-
if (!$this->classSkipVoter->match($element)) {
56+
$skipMatch = $this->matchSkip($element, $filePath);
57+
if (!$skipMatch instanceof SkipMatch) {
5158
return \false;
5259
}
53-
return $this->classSkipVoter->shouldSkip($element, $filePath);
60+
$this->markSkipUsed($skipMatch);
61+
return \true;
5462
}
5563
/**
56-
* @param class-string<RectorInterface> $rectorClass
64+
* Match a class/path skip without marking it used. Callers that can only tell whether the skip
65+
* actually prevented a change later on must mark it used themselves via markSkipUsed().
5766
* @param string|object $element
5867
*/
59-
public function shouldSkipCurrentNode($element, string $filePath, string $rectorClass, Node $node): bool
68+
public function matchSkip($element, string $filePath): ?SkipMatch
6069
{
61-
if ($this->shouldSkipElementAndFilePath($element, $filePath)) {
62-
return \true;
70+
if (!$this->classSkipVoter->match($element)) {
71+
return null;
6372
}
73+
return $this->classSkipVoter->matchSkip($element, $filePath);
74+
}
75+
public function markSkipUsed(SkipMatch $skipMatch): void
76+
{
77+
$this->usedSkipCollector->markUsed($skipMatch->getSkippedClass(), $skipMatch->getMatchedPath());
78+
}
79+
/**
80+
* @param class-string<RectorInterface> $rectorClass
81+
*/
82+
public function shouldSkipCurrentNode(string $rectorClass, Node $node): bool
83+
{
6484
return $this->rectifiedAnalyzer->hasRectified($rectorClass, $node);
6585
}
6686
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
declare (strict_types=1);
4+
namespace Rector\Skipper\ValueObject;
5+
6+
/**
7+
* Result of a matched class/path skip: the skipped class and the concrete path pattern
8+
* that matched, if the skip is scoped to specific paths.
9+
*/
10+
final class SkipMatch
11+
{
12+
/**
13+
* @readonly
14+
*/
15+
private string $skippedClass;
16+
/**
17+
* @readonly
18+
*/
19+
private ?string $matchedPath;
20+
public function __construct(string $skippedClass, ?string $matchedPath)
21+
{
22+
$this->skippedClass = $skippedClass;
23+
$this->matchedPath = $matchedPath;
24+
}
25+
public function getSkippedClass(): string
26+
{
27+
return $this->skippedClass;
28+
}
29+
public function getMatchedPath(): ?string
30+
{
31+
return $this->matchedPath;
32+
}
33+
}

vendor/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2495,6 +2495,7 @@
24952495
'Rector\\Skipper\\Skipper\\SkipSkipper' => $baseDir . '/src/Skipper/Skipper/SkipSkipper.php',
24962496
'Rector\\Skipper\\Skipper\\Skipper' => $baseDir . '/src/Skipper/Skipper/Skipper.php',
24972497
'Rector\\Skipper\\Skipper\\UsedSkipCollector' => $baseDir . '/src/Skipper/Skipper/UsedSkipCollector.php',
2498+
'Rector\\Skipper\\ValueObject\\SkipMatch' => $baseDir . '/src/Skipper/ValueObject/SkipMatch.php',
24982499
'Rector\\StaticReflection\\DynamicSourceLocatorDecorator' => $baseDir . '/src/StaticReflection/DynamicSourceLocatorDecorator.php',
24992500
'Rector\\StaticTypeMapper\\Contract\\PhpDocParser\\PhpDocTypeMapperInterface' => $baseDir . '/src/StaticTypeMapper/Contract/PhpDocParser/PhpDocTypeMapperInterface.php',
25002501
'Rector\\StaticTypeMapper\\Contract\\PhpParser\\PhpParserNodeMapperInterface' => $baseDir . '/src/StaticTypeMapper/Contract/PhpParser/PhpParserNodeMapperInterface.php',

vendor/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2760,6 +2760,7 @@ class ComposerStaticInit311c1aff9403b33c90f3edae71d2b69d
27602760
'Rector\\Skipper\\Skipper\\SkipSkipper' => __DIR__ . '/../..' . '/src/Skipper/Skipper/SkipSkipper.php',
27612761
'Rector\\Skipper\\Skipper\\Skipper' => __DIR__ . '/../..' . '/src/Skipper/Skipper/Skipper.php',
27622762
'Rector\\Skipper\\Skipper\\UsedSkipCollector' => __DIR__ . '/../..' . '/src/Skipper/Skipper/UsedSkipCollector.php',
2763+
'Rector\\Skipper\\ValueObject\\SkipMatch' => __DIR__ . '/../..' . '/src/Skipper/ValueObject/SkipMatch.php',
27632764
'Rector\\StaticReflection\\DynamicSourceLocatorDecorator' => __DIR__ . '/../..' . '/src/StaticReflection/DynamicSourceLocatorDecorator.php',
27642765
'Rector\\StaticTypeMapper\\Contract\\PhpDocParser\\PhpDocTypeMapperInterface' => __DIR__ . '/../..' . '/src/StaticTypeMapper/Contract/PhpDocParser/PhpDocTypeMapperInterface.php',
27652766
'Rector\\StaticTypeMapper\\Contract\\PhpParser\\PhpParserNodeMapperInterface' => __DIR__ . '/../..' . '/src/StaticTypeMapper/Contract/PhpParser/PhpParserNodeMapperInterface.php',

0 commit comments

Comments
 (0)