Skip to content

Commit 8bd7601

Browse files
committed
[skip] fix match class + path matching in reporting of unused skips
1 parent aea1570 commit 8bd7601

5 files changed

Lines changed: 35 additions & 9 deletions

File tree

‎src/Reporting/UnusedSkipResolver.php‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,9 @@ private function resolveUnusedRuleScopedSkips(array $usedSkips): array
117117
foreach ($this->resolveRelativePathsByClass() as $rectorClass => $relativePaths) {
118118
$unusedRelativePaths = [];
119119
foreach ($relativePaths as $path => $relativePath) {
120-
if (! in_array($path, $usedSkips, true)) {
120+
// used skips are tracked scoped to their rule as "class|path", so the same path
121+
// skipped under another rule does not mark this one used (see SkipSkipper)
122+
if (! in_array($rectorClass . '|' . $path, $usedSkips, true)) {
121123
$unusedRelativePaths[] = $relativePath;
122124
}
123125
}

‎src/Skipper/Skipper/SkipSkipper.php‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,11 @@ public function doesMatchSkip(object | string $checker, string $filePath, array
3030
return true;
3131
}
3232

33-
// mark the specific matched path used, so unused paths under the same rule are reported
33+
// mark the specific matched path used, scoped to its rule via "class|path" - the same
34+
// path can be skipped under multiple rules, so a path-only key would mark them all used
3435
$matchedPath = $this->fileInfoMatcher->matchPattern($filePath, $skippedFiles);
3536
if ($matchedPath !== null) {
36-
$this->usedSkipCollector->markUsed($matchedPath);
37+
$this->usedSkipCollector->markUsed($skippedClass . '|' . $matchedPath);
3738
return true;
3839
}
3940
}

‎tests/Reporting/MissConfigurationReporterTest.php‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ public function testReportsOnlyTrackableUnusedSkips(): void
6565
{
6666
SimpleParameterProvider::setParameter(Option::REPORT_UNUSED_SKIPS, true);
6767

68-
// matched rule-scoped path is marked used by its path, not by the rule class
69-
$processResult = new ProcessResult([], [], 0, [self::USED_RULE_MASK]);
68+
// matched rule-scoped path is marked used scoped to its rule as "class|path"
69+
$processResult = new ProcessResult([], [], 0, [AnotherClassToSkip::class . '|' . self::USED_RULE_MASK]);
7070
$this->missConfigurationReporter->reportUnusedSkips($processResult);
7171

7272
$output = $this->bufferedOutput->fetch();

‎tests/Reporting/UnusedSkipResolverTest.php‎

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ public function testResolvesUnusedSkipsAsRuleAndPath(): void
5353
{
5454
SimpleParameterProvider::setParameter(Option::REPORT_UNUSED_SKIPS, true);
5555

56-
$processResult = new ProcessResult([], [], 0, [self::USED_RULE_MASK]);
56+
// used skips are tracked scoped to their rule as "class|path"
57+
$processResult = new ProcessResult([], [], 0, [AnotherClassToSkip::class . '|' . self::USED_RULE_MASK]);
5758
$unusedSkips = $this->unusedSkipResolver->resolve($processResult);
5859

5960
// rule-scoped unused skip is reported as "rule:" with the path nested below
@@ -65,6 +66,28 @@ public function testResolvesUnusedSkipsAsRuleAndPath(): void
6566
$this->assertNotContains(ThreeMan::class, $unusedSkips);
6667
}
6768

69+
public function testSamePathUnderAnotherRuleDoesNotMarkSkipUsed(): void
70+
{
71+
SimpleParameterProvider::setParameter(Option::REPORT_UNUSED_SKIPS, true);
72+
73+
$sharedMask = '*/shared-mask/*';
74+
SimpleParameterProvider::setParameter(Option::SKIP, [
75+
// same path skipped under two rules - only the first one actually matched a file
76+
AnotherClassToSkip::class => [$sharedMask],
77+
FifthElement::class => [$sharedMask],
78+
]);
79+
80+
// used skip is scoped to its rule, so the shared path stays unused under the other rule
81+
$processResult = new ProcessResult([], [], 0, [AnotherClassToSkip::class . '|' . $sharedMask]);
82+
$unusedSkips = $this->unusedSkipResolver->resolve($processResult);
83+
84+
// the never-matched rule still reports its shared path as unused
85+
$this->assertContains(FifthElement::class . ':' . "\n * " . $sharedMask, $unusedSkips);
86+
87+
// the matched rule is excluded
88+
$this->assertNotContains(AnotherClassToSkip::class . ':' . "\n * " . $sharedMask, $unusedSkips);
89+
}
90+
6891
public function testReportsUnusedSkipAsRelativePath(): void
6992
{
7093
SimpleParameterProvider::setParameter(Option::REPORT_UNUSED_SKIPS, true);

‎tests/Skipper/Skipper/UsedSkipCollectorTest.php‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,11 @@ public function testCollectsMatchedPathNotRuleClassForRuleScopedSkip(): void
8383

8484
$usedSkips = $this->usedSkipCollector->provide();
8585

86-
// the specific matched path is collected, not the rule class
87-
$this->assertContains('*/someDirectory/*', $usedSkips);
86+
// the specific matched path is collected scoped to its rule as "class|path"
87+
$this->assertContains(AnotherClassToSkip::class . '|' . '*/someDirectory/*', $usedSkips);
8888
$this->assertNotContains(AnotherClassToSkip::class, $usedSkips);
8989

9090
// the unmatched sibling path under the same rule is never collected
91-
$this->assertNotContains(self::UNUSED_SKIP_MARKER, $usedSkips);
91+
$this->assertNotContains(AnotherClassToSkip::class . '|' . self::UNUSED_SKIP_MARKER, $usedSkips);
9292
}
9393
}

0 commit comments

Comments
 (0)