Skip to content

Commit e69bb2c

Browse files
committed
Extract ignore-new-errors logic into BaselineIgnoredErrorHelper
1 parent 3464d8a commit e69bb2c

File tree

3 files changed

+123
-85
lines changed

3 files changed

+123
-85
lines changed

Diff for: conf/config.neon

+3
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,9 @@ services:
470470
ignoreErrors: %ignoreErrors%
471471
reportUnmatchedIgnoredErrors: %reportUnmatchedIgnoredErrors%
472472

473+
-
474+
class: PHPStan\Analyser\Ignore\BaselineIgnoredErrorHelper
475+
473476
-
474477
class: PHPStan\Analyser\Ignore\IgnoreLexer
475478

Diff for: src/Analyser/Ignore/BaselineIgnoredErrorHelper.php

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser\Ignore;
4+
5+
use PHPStan\Analyser\Error;
6+
use PHPStan\File\FileHelper;
7+
use PHPStan\File\ParentDirectoryRelativePathHelper;
8+
use PHPStan\File\RelativePathHelper;
9+
10+
final class BaselineIgnoredErrorHelper
11+
{
12+
13+
public function __construct(
14+
private FileHelper $fileHelper,
15+
)
16+
{
17+
}
18+
19+
/**
20+
* @param mixed[][] $baselinedErrors errors currently present in the baseline
21+
* @param list<Error> $currentAnalysisErrors errors from the current analysis
22+
* @return list<Error> errors from the current analysis which already exit in the baseline
23+
*/
24+
public function removeUnusedIgnoredErrors(array $baselinedErrors, array $currentAnalysisErrors, ParentDirectoryRelativePathHelper $baselinePathHelper): array
25+
{
26+
$ignoreErrorsByFile = $this->mapIgnoredErrorsByFile($baselinedErrors);
27+
28+
$ignoreUseCount = [];
29+
$nextBaselinedErrors = [];
30+
foreach ($currentAnalysisErrors as $error) {
31+
$hasMatchingIgnore = $this->checkIgnoreErrorByPath($error->getFilePath(), $ignoreErrorsByFile, $error, $ignoreUseCount, $baselinePathHelper);
32+
if ($hasMatchingIgnore) {
33+
$nextBaselinedErrors[] = $error;
34+
continue;
35+
}
36+
37+
$traitFilePath = $error->getTraitFilePath();
38+
if ($traitFilePath === null) {
39+
continue;
40+
}
41+
42+
$hasMatchingIgnore = $this->checkIgnoreErrorByPath($traitFilePath, $ignoreErrorsByFile, $error, $ignoreUseCount, $baselinePathHelper);
43+
if (!$hasMatchingIgnore) {
44+
continue;
45+
}
46+
47+
$nextBaselinedErrors[] = $error;
48+
}
49+
50+
return $nextBaselinedErrors;
51+
}
52+
53+
/**
54+
* @param mixed[][] $ignoreErrorsByFile
55+
* @param int[] $ignoreUseCount map of indexes of ignores and how often they have been "used" to ignore an error
56+
*/
57+
private function checkIgnoreErrorByPath(string $filePath, array $ignoreErrorsByFile, Error $error, array &$ignoreUseCount, RelativePathHelper $baselinePathHelper): bool
58+
{
59+
$relativePath = $baselinePathHelper->getRelativePath($filePath);
60+
if (!isset($ignoreErrorsByFile[$relativePath])) {
61+
return false;
62+
}
63+
64+
foreach ($ignoreErrorsByFile[$relativePath] as $ignoreError) {
65+
$ignore = $ignoreError['ignoreError'];
66+
$shouldIgnore = IgnoredError::shouldIgnore($this->fileHelper, $error, $ignore['message'] ?? null, $ignore['identifier'] ?? null, null);
67+
if (!$shouldIgnore) {
68+
continue;
69+
}
70+
71+
$realCount = $ignoreUseCount[$ignoreError['index']] ?? 0;
72+
$realCount++;
73+
$ignoreUseCount[$ignoreError['index']] = $realCount;
74+
75+
if ($realCount <= $ignore['count']) {
76+
return true;
77+
}
78+
}
79+
80+
return false;
81+
}
82+
83+
/**
84+
* @param mixed[][] $baselineIgnoreErrors
85+
* @return mixed[][] ignored errors from baseline mapped and grouped by files
86+
*/
87+
private function mapIgnoredErrorsByFile(array $baselineIgnoreErrors): array
88+
{
89+
$ignoreErrorsByFile = [];
90+
91+
foreach ($baselineIgnoreErrors as $i => $ignoreError) {
92+
$ignoreErrorEntry = [
93+
'index' => $i,
94+
'ignoreError' => $ignoreError,
95+
];
96+
97+
$normalizedPath = $this->fileHelper->normalizePath($ignoreError['path']);
98+
$ignoreErrorsByFile[$normalizedPath][] = $ignoreErrorEntry;
99+
}
100+
101+
return $ignoreErrorsByFile;
102+
}
103+
104+
}

Diff for: src/Command/AnalyseCommand.php

+16-85
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
use Nette\FileNotFoundException;
77
use Nette\InvalidStateException;
88
use OndraM\CiDetector\CiDetector;
9-
use PHPStan\Analyser\Error;
10-
use PHPStan\Analyser\Ignore\IgnoredError;
9+
use PHPStan\Analyser\Ignore\BaselineIgnoredErrorHelper;
1110
use PHPStan\Analyser\InternalError;
1211
use PHPStan\Command\ErrorFormatter\BaselineNeonErrorFormatter;
1312
use PHPStan\Command\ErrorFormatter\BaselinePhpErrorFormatter;
@@ -602,10 +601,9 @@ private function getMessageFromInternalError(FileHelper $fileHelper, InternalErr
602601
private function generateBaseline(string $generateBaselineFile, InceptionResult $inceptionResult, AnalysisResult $analysisResult, OutputInterface $output, bool $allowEmptyBaseline, string $baselineExtension, bool $failWithoutResultCache, bool $onlyRemoveErrors, Container $container): int
603602
{
604603
$baselineFileDirectory = dirname($generateBaselineFile);
605-
$fileHelper = $container->getByType(FileHelper::class);
606604
$baselinePathHelper = new ParentDirectoryRelativePathHelper($baselineFileDirectory);
607605
if ($onlyRemoveErrors) {
608-
$analysisResult = $this->filterAnalysisResultForExistingErrors($analysisResult, $generateBaselineFile, $inceptionResult, $fileHelper, $baselinePathHelper);
606+
$analysisResult = $this->filterAnalysisResultForExistingErrors($analysisResult, $generateBaselineFile, $inceptionResult, $container, $baselinePathHelper);
609607
}
610608

611609
if (!$allowEmptyBaseline && !$analysisResult->hasErrors()) {
@@ -618,7 +616,6 @@ private function generateBaseline(string $generateBaselineFile, InceptionResult
618616
$streamOutput = $this->createStreamOutput();
619617
$errorConsoleStyle = new ErrorsConsoleStyle(new StringInput(''), $streamOutput);
620618
$baselineOutput = new SymfonyOutput($streamOutput, new SymfonyStyle($errorConsoleStyle));
621-
$baselinePathHelper = new ParentDirectoryRelativePathHelper($baselineFileDirectory);
622619

623620
if ($baselineExtension === 'php') {
624621
$baselineErrorFormatter = new BaselinePhpErrorFormatter($baselinePathHelper);
@@ -692,37 +689,19 @@ private function generateBaseline(string $generateBaselineFile, InceptionResult
692689
return $inceptionResult->handleReturn($exitCode, $analysisResult->getPeakMemoryUsageBytes(), $this->analysisStartTime);
693690
}
694691

695-
private function filterAnalysisResultForExistingErrors(AnalysisResult $analysisResult, string $generateBaselineFile, InceptionResult $inceptionResult, FileHelper $fileHelper, RelativePathHelper $baselinePathHelper): AnalysisResult
692+
private function filterAnalysisResultForExistingErrors(AnalysisResult $analysisResult, string $generateBaselineFile, InceptionResult $inceptionResult, Container $container, ParentDirectoryRelativePathHelper $baselinePathHelper): AnalysisResult
696693
{
697-
$fileSpecificErrors = $analysisResult->getFileSpecificErrors();
698-
699-
$baselineIgnoreErrors = $this->getCurrentBaselineIgnoreErrors($generateBaselineFile, $inceptionResult);
700-
$ignoreErrorsByFile = $this->mapIgnoredErrors($baselineIgnoreErrors, $fileHelper);
701-
702-
$ignoreUseCount = [];
703-
704-
foreach ($fileSpecificErrors as $errorIndex => $error) {
705-
$hasMatchingIgnore = $this->checkIgnoreErrorByPath($error->getFilePath(), $ignoreErrorsByFile, $fileHelper, $error, $ignoreUseCount, $baselinePathHelper);
706-
if ($hasMatchingIgnore) {
707-
continue;
708-
}
694+
$currentAnalysisErrors = $analysisResult->getFileSpecificErrors();
709695

710-
$traitFilePath = $error->getTraitFilePath();
711-
if ($traitFilePath !== null) {
712-
$hasMatchingIgnore = $this->checkIgnoreErrorByPath($traitFilePath, $ignoreErrorsByFile, $fileHelper, $error, $ignoreUseCount, $baselinePathHelper);
713-
if ($hasMatchingIgnore) {
714-
continue;
715-
}
716-
}
696+
$currentBaselinedErrors = $this->getCurrentBaselinedErrors($generateBaselineFile, $inceptionResult);
717697

718-
// the error was not matched in the baseline, making it a new error, new errors should be ignored here
719-
unset($fileSpecificErrors[$errorIndex]);
720-
}
698+
/** @var BaselineIgnoredErrorHelper $baselineIgnoredErrorsHelper */
699+
$baselineIgnoredErrorsHelper = $container->getByType(BaselineIgnoredErrorHelper::class);
721700

722-
$fileSpecificErrors = array_values($fileSpecificErrors);
701+
$nextBaselinedErrors = $baselineIgnoredErrorsHelper->removeUnusedIgnoredErrors($currentBaselinedErrors, $currentAnalysisErrors, $baselinePathHelper);
723702

724703
return new AnalysisResult(
725-
$fileSpecificErrors,
704+
$nextBaselinedErrors,
726705
$analysisResult->getNotFileSpecificErrors(),
727706
$analysisResult->getInternalErrorObjects(),
728707
$analysisResult->getWarnings(),
@@ -736,36 +715,6 @@ private function filterAnalysisResultForExistingErrors(AnalysisResult $analysisR
736715
);
737716
}
738717

739-
/**
740-
* @param mixed[][] $ignoreErrorsByFile
741-
* @param int[] $ignoreUseCount map of indexes of ignores and how often they have been "used" to ignore an error
742-
*/
743-
private function checkIgnoreErrorByPath(string $filePath, array $ignoreErrorsByFile, FileHelper $fileHelper, Error $error, array &$ignoreUseCount, RelativePathHelper $baselinePathHelper): bool
744-
{
745-
$relativePath = $baselinePathHelper->getRelativePath($filePath);
746-
if (!isset($ignoreErrorsByFile[$relativePath])) {
747-
return false;
748-
}
749-
750-
foreach ($ignoreErrorsByFile[$relativePath] as $ignoreError) {
751-
$ignore = $ignoreError['ignoreError'];
752-
$shouldIgnore = IgnoredError::shouldIgnore($fileHelper, $error, $ignore['message'] ?? null, $ignore['identifier'] ?? null, null);
753-
if (!$shouldIgnore) {
754-
continue;
755-
}
756-
757-
$realCount = $ignoreUseCount[$ignoreError['index']] ?? 0;
758-
$realCount++;
759-
$ignoreUseCount[$ignoreError['index']] = $realCount;
760-
761-
if ($realCount <= $ignore['count']) {
762-
return true;
763-
}
764-
}
765-
766-
return false;
767-
}
768-
769718
/**
770719
* @param string[] $files
771720
*/
@@ -808,41 +757,23 @@ private function runDiagnoseExtensions(Container $container, Output $errorOutput
808757
}
809758
}
810759

811-
private function getCurrentBaselineIgnoreErrors(string $generateBaselineFile, InceptionResult $inceptionResult): mixed
760+
/**
761+
* @return mixed[][]
762+
*/
763+
private function getCurrentBaselinedErrors(string $generateBaselineFile, InceptionResult $inceptionResult): array
812764
{
813765
$loader = new Loader();
814766
try {
815767
$currentBaselineConfig = $loader->load($generateBaselineFile);
816-
$baselineIgnoreErrors = $currentBaselineConfig['parameters']['ignoreErrors'] ?? [];
768+
$baselinedErrors = $currentBaselineConfig['parameters']['ignoreErrors'] ?? [];
817769
} catch (FileNotFoundException) {
818770
// currently no baseline file -> empty config
819-
$baselineIgnoreErrors = [];
771+
$baselinedErrors = [];
820772
} catch (InvalidStateException $invalidStateException) {
821773
$inceptionResult->getErrorOutput()->writeLineFormatted($invalidStateException->getMessage());
822774
throw $invalidStateException;
823775
}
824-
return $baselineIgnoreErrors;
825-
}
826-
827-
/**
828-
* @param mixed[][] $baselineIgnoreErrors
829-
* @return mixed[][]
830-
*/
831-
private function mapIgnoredErrors(array $baselineIgnoreErrors, FileHelper $fileHelper): array
832-
{
833-
$ignoreErrorsByFile = [];
834-
835-
foreach ($baselineIgnoreErrors as $i => $ignoreError) {
836-
$ignoreErrorEntry = [
837-
'index' => $i,
838-
'ignoreError' => $ignoreError,
839-
];
840-
841-
$normalizedPath = $fileHelper->normalizePath($ignoreError['path']);
842-
$ignoreErrorsByFile[$normalizedPath][] = $ignoreErrorEntry;
843-
}
844-
845-
return $ignoreErrorsByFile;
776+
return $baselinedErrors;
846777
}
847778

848779
}

0 commit comments

Comments
 (0)