Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 57 additions & 13 deletions src/Reporting/UnusedSkipResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,26 +33,45 @@ public function __construct(
*/
public function resolve(ProcessResult $processResult): array
{
if (! SimpleParameterProvider::provideBoolParameter(Option::REPORT_UNUSED_SKIPS, false)) {
if ($this->shouldSkipReporting()) {
return [];
}

$usedSkips = $processResult->getUsedSkips();

return [
...$this->resolveUnusedRuleScopedSkips($usedSkips),
...$this->resolveUnusedGlobalSkips($usedSkips),
];
}

private function shouldSkipReporting(): bool
{
if (! SimpleParameterProvider::provideBoolParameter(Option::REPORT_UNUSED_SKIPS, false)) {
return true;
}

// a narrowed run (cli paths, "--only" or "--only-suffix") only touches part of the codebase,
// so skips outside that scope look falsely unused - reporting them would be noise
if (SimpleParameterProvider::provideBoolParameter(Option::IS_RUN_NARROWED, false)) {
return [];
return true;
}

// a cached run only re-processes changed files, so skips on cached files never get a chance
// to match and would all look falsely unused - reporting them would be noise
if (SimpleParameterProvider::provideBoolParameter(Option::IS_CACHED_RUN, false)) {
return [];
}
return SimpleParameterProvider::provideBoolParameter(Option::IS_CACHED_RUN, false);
}

// map of rule => (trackable skip path => relative display path); skips are tracked at
// runtime by their path, but rule-scoped ones are printed grouped under their rule so the
// user knows exactly what to remove. Skip-everywhere rule skips (null path) are forgotten
// from the container at boot, so they never reach the skipper and cannot be tracked.
/**
* Map of rule => (trackable skip path => relative display path); skips are tracked at
* runtime by their path, but rule-scoped ones are printed grouped under their rule so the
* user knows exactly what to remove. Skip-everywhere rule skips (null path) are forgotten
* from the container at boot, so they never reach the skipper and cannot be tracked.
*
* @return array<string, array<string, string>>
*/
private function resolveRelativePathsByClass(): array
{
$relativePathsByClass = [];
foreach ($this->skippedClassResolver->resolve() as $rectorClass => $paths) {
if ($paths === null) {
Expand All @@ -65,22 +84,37 @@ public function resolve(ProcessResult $processResult): array
}
}

// global mask paths like "*/some/*" are hard to spot and report false positives, skip them
return $relativePathsByClass;
}

/**
* @return array<string, string>
*/
private function resolveGlobalRelativePaths(): array
{
$globalRelativePaths = [];
foreach ($this->skippedPathsResolver->resolve() as $globalPath) {
// global mask paths like "*/some/*" are hard to spot and report false positives, skip them
if (str_contains($globalPath, '*')) {
continue;
}

$globalRelativePaths[$globalPath] = $this->filePathHelper->relativePath($globalPath);
}

$usedSkips = $processResult->getUsedSkips();
return $globalRelativePaths;
}

/**
* @param string[] $usedSkips
* @return string[]
*/
private function resolveUnusedRuleScopedSkips(array $usedSkips): array
{
$unusedSkips = [];

// group unused rule-scoped paths under their rule, matching the "->withSkip()" config shape
foreach ($relativePathsByClass as $rectorClass => $relativePaths) {
foreach ($this->resolveRelativePathsByClass() as $rectorClass => $relativePaths) {
$unusedRelativePaths = [];
foreach ($relativePaths as $path => $relativePath) {
if (! in_array($path, $usedSkips, true)) {
Expand All @@ -96,7 +130,17 @@ public function resolve(ProcessResult $processResult): array
$unusedSkips[] = $rectorClass . ':' . "\n * " . implode("\n * ", $unusedRelativePaths);
}

foreach ($globalRelativePaths as $path => $relativePath) {
return $unusedSkips;
}

/**
* @param string[] $usedSkips
* @return string[]
*/
private function resolveUnusedGlobalSkips(array $usedSkips): array
{
$unusedSkips = [];
foreach ($this->resolveGlobalRelativePaths() as $path => $relativePath) {
if (! in_array($path, $usedSkips, true)) {
$unusedSkips[] = $relativePath;
}
Expand Down
Loading