Skip to content

Commit 0cef5d0

Browse files
committed
add grouped error formatter
1 parent 795cc66 commit 0cef5d0

File tree

1 file changed

+112
-0
lines changed

1 file changed

+112
-0
lines changed
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Command\ErrorFormatter;
4+
5+
use PHPStan\Analyser\Error;
6+
use PHPStan\Command\AnalysisResult;
7+
use PHPStan\Command\Output;
8+
use PHPStan\File\RelativePathHelper;
9+
use Symfony\Component\Console\Formatter\OutputFormatter;
10+
use function count;
11+
use function is_string;
12+
use function sprintf;
13+
use function str_replace;
14+
use function uasort;
15+
16+
final class GroupedErrorFormatter implements ErrorFormatter
17+
{
18+
19+
private const NO_IDENTIFIER = 'without identifier';
20+
21+
public function __construct(private RelativePathHelper $relativePathHelper, private ?string $editorUrl, private ?string $editorUrlTitle)
22+
{
23+
}
24+
25+
public function formatErrors(AnalysisResult $analysisResult, Output $output): int
26+
{
27+
$style = $output->getStyle();
28+
29+
if (!$analysisResult->hasErrors() && !$analysisResult->hasWarnings()) {
30+
$style->success('No errors');
31+
32+
return 0;
33+
}
34+
35+
/** @var array<string, Error[]> $groupedErrors */
36+
$groupedErrors = [];
37+
38+
foreach ($analysisResult->getFileSpecificErrors() as $fileSpecificError) {
39+
$identifier = $fileSpecificError->getIdentifier() ?? self::NO_IDENTIFIER;
40+
41+
$groupedErrors[$identifier][] = $fileSpecificError;
42+
}
43+
44+
uasort($groupedErrors, static fn ($errorsA, $errorsB) => count($errorsB) <=> count($errorsA));
45+
46+
foreach ($groupedErrors as $identifier => $errors) {
47+
$count = count($errors);
48+
$output->writeRaw(sprintf('[%s] (%dx):', $identifier, $count));
49+
$output->writeLineFormatted('');
50+
51+
foreach ($errors as $error) {
52+
$file = $error->getTraitFilePath() ?? $error->getFilePath();
53+
$relFile = $this->relativePathHelper->getRelativePath($file);
54+
$line = (string) $error->getLine();
55+
$message = $error->getMessage();
56+
57+
if (is_string($this->editorUrl)) {
58+
$url = str_replace(
59+
['%file%', '%relFile%', '%line%'],
60+
[$file, $relFile, $line],
61+
$this->editorUrl,
62+
);
63+
64+
if (is_string($this->editorUrlTitle)) {
65+
$title = str_replace(
66+
['%file%', '%relFile%', '%line%'],
67+
[$file, $relFile, $line],
68+
$this->editorUrlTitle,
69+
);
70+
} else {
71+
$title = sprintf('%s:%s', $file, $line);
72+
}
73+
74+
$fileStr = '<href=' . OutputFormatter::escape($url) . '>' . $title . '</>';
75+
} else {
76+
$fileStr = sprintf('%s:%s', $file, $line);
77+
}
78+
79+
$output->writeLineFormatted(sprintf("\t- %s: %s", $fileStr, $message));
80+
}
81+
$output->writeLineFormatted('');
82+
}
83+
84+
foreach ($analysisResult->getNotFileSpecificErrors() as $notFileSpecificError) {
85+
$output->writeRaw(sprintf('?:?:%s', $notFileSpecificError));
86+
$output->writeLineFormatted('');
87+
}
88+
89+
foreach ($analysisResult->getWarnings() as $warning) {
90+
$output->writeRaw(sprintf('?:?:%s', $warning));
91+
$output->writeLineFormatted('');
92+
}
93+
94+
$totalErrorsCount = $analysisResult->getTotalErrorsCount();
95+
$warningsCount = count($analysisResult->getWarnings());
96+
97+
$finalMessage = sprintf($totalErrorsCount === 1 ? 'Found %d error' : 'Found %d errors', $totalErrorsCount);
98+
99+
if ($analysisResult->hasWarnings()) {
100+
$finalMessage .= sprintf($warningsCount === 1 ? ' and %d warning' : ' and %d warnings', $warningsCount);
101+
}
102+
103+
if ($analysisResult->hasErrors()) {
104+
$style->error($finalMessage);
105+
} else {
106+
$style->warning($finalMessage);
107+
}
108+
109+
return $analysisResult->hasErrors() ? 1 : 0;
110+
}
111+
112+
}

0 commit comments

Comments
 (0)