Skip to content
Merged
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions conf/bleedingEdge.neon
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ parameters:
newStaticInAbstractClassStaticMethod: true
checkExtensionsForComparisonOperators: true
reportTooWideBool: true
rawMessageInBaseline: true
1 change: 1 addition & 0 deletions conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ parameters:
newStaticInAbstractClassStaticMethod: false
checkExtensionsForComparisonOperators: false
reportTooWideBool: false
rawMessageInBaseline: false
fileExtensions:
- php
checkAdvancedIsset: false
Expand Down
1 change: 1 addition & 0 deletions conf/parametersSchema.neon
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ parametersSchema:
newStaticInAbstractClassStaticMethod: bool()
checkExtensionsForComparisonOperators: bool()
reportTooWideBool: bool()
rawMessageInBaseline: bool()
])
fileExtensions: listOf(string())
checkAdvancedIsset: bool()
Expand Down
5 changes: 3 additions & 2 deletions src/Command/AnalyseCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -733,12 +733,13 @@ private function generateBaseline(string $generateBaselineFile, InceptionResult
$baselineOutput = new SymfonyOutput($streamOutput, new SymfonyStyle($errorConsoleStyle));
$baselineFileDirectory = dirname($generateBaselineFile);
$baselinePathHelper = new ParentDirectoryRelativePathHelper($baselineFileDirectory);
$rawMessageInBaseline = $inceptionResult->getContainer()->getParameter('featureToggles')['rawMessageInBaseline'];

if ($baselineExtension === 'php') {
$baselineErrorFormatter = new BaselinePhpErrorFormatter($baselinePathHelper);
$baselineErrorFormatter = new BaselinePhpErrorFormatter($baselinePathHelper, $rawMessageInBaseline);
$baselineErrorFormatter->formatErrors($analysisResult, $baselineOutput);
} else {
$baselineErrorFormatter = new BaselineNeonErrorFormatter($baselinePathHelper);
$baselineErrorFormatter = new BaselineNeonErrorFormatter($baselinePathHelper, $rawMessageInBaseline);
$existingBaselineContent = is_file($generateBaselineFile) ? FileReader::read($generateBaselineFile) : '';
$baselineErrorFormatter->formatErrors($analysisResult, $baselineOutput, $existingBaselineContent);
}
Expand Down
13 changes: 9 additions & 4 deletions src/Command/ErrorFormatter/BaselineNeonErrorFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
final class BaselineNeonErrorFormatter
{

public function __construct(private RelativePathHelper $relativePathHelper)
public function __construct(private RelativePathHelper $relativePathHelper, private bool $useRawMessage)
{
}

Expand All @@ -42,6 +42,7 @@ public function formatErrors(
}
ksort($fileErrors, SORT_STRING);

$messageKey = $this->useRawMessage ? 'rawMessage' : 'message';
$errorsToOutput = [];
foreach ($fileErrors as $file => $errors) {
$fileErrorsByMessage = [];
Expand Down Expand Up @@ -72,19 +73,23 @@ public function formatErrors(
ksort($fileErrorsByMessage, SORT_STRING);

foreach ($fileErrorsByMessage as $message => [$totalCount, $identifiers]) {
if (!$this->useRawMessage) {
$message = '#^' . preg_quote($message, '#') . '$#';
}

ksort($identifiers, SORT_STRING);
if (count($identifiers) > 0) {
foreach ($identifiers as $identifier => $identifierCount) {
$errorsToOutput[] = [
'message' => Helpers::escape('#^' . preg_quote($message, '#') . '$#'),
$messageKey => Helpers::escape($message),
'identifier' => $identifier,
'count' => $identifierCount,
'path' => Helpers::escape($file),
];
}
} else {
$errorsToOutput[] = [
'message' => Helpers::escape('#^' . preg_quote($message, '#') . '$#'),
$messageKey => Helpers::escape($message),
'count' => $totalCount,
'path' => Helpers::escape($file),
];
Expand All @@ -98,7 +103,7 @@ public function formatErrors(
}

/**
* @param array<int, array{message: string, count: int, path: string}> $ignoreErrors
* @param array<int, array<string, string|int>> $ignoreErrors
*/
private function getNeon(array $ignoreErrors, string $existingBaselineContent): string
{
Expand Down
19 changes: 14 additions & 5 deletions src/Command/ErrorFormatter/BaselinePhpErrorFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
final class BaselinePhpErrorFormatter
{

public function __construct(private RelativePathHelper $relativePathHelper)
public function __construct(private RelativePathHelper $relativePathHelper, private bool $useRawMessage)
{
}

Expand Down Expand Up @@ -76,21 +76,30 @@ public function formatErrors(
ksort($fileErrorsByMessage, SORT_STRING);

foreach ($fileErrorsByMessage as $message => [$totalCount, $identifiers]) {
if ($this->useRawMessage) {
$messageKey = 'rawMessage';
} else {
$messageKey = 'message';
$message = '#^' . preg_quote($message, '#') . '$#';
}

ksort($identifiers, SORT_STRING);
if (count($identifiers) > 0) {
foreach ($identifiers as $identifier => $identifierCount) {
$php .= sprintf(
"\$ignoreErrors[] = [\n\t'message' => %s,\n\t'identifier' => %s,\n\t'count' => %d,\n\t'path' => __DIR__ . %s,\n];\n",
var_export(Helpers::escape('#^' . preg_quote($message, '#') . '$#'), true),
"\$ignoreErrors[] = [\n\t%s => %s,\n\t'identifier' => %s,\n\t'count' => %d,\n\t'path' => __DIR__ . %s,\n];\n",
var_export($messageKey, true),
var_export(Helpers::escape($message), true),
var_export(Helpers::escape($identifier), true),
var_export($identifierCount, true),
var_export(Helpers::escape($file), true),
);
}
} else {
$php .= sprintf(
"\$ignoreErrors[] = [\n\t'message' => %s,\n\t'count' => %d,\n\t'path' => __DIR__ . %s,\n];\n",
var_export(Helpers::escape('#^' . preg_quote($message, '#') . '$#'), true),
"\$ignoreErrors[] = [\n\t%s => %s,\n\t'count' => %d,\n\t'path' => __DIR__ . %s,\n];\n",
var_export($messageKey, true),
var_export(Helpers::escape($message), true),
var_export($totalCount, true),
var_export(Helpers::escape($file), true),
);
Expand Down
144 changes: 130 additions & 14 deletions tests/PHPStan/Command/ErrorFormatter/BaselineNeonErrorFormatterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public static function dataFormatterOutputProvider(): iterable
0,
0,
0,
false,
[],
];

Expand All @@ -44,6 +45,7 @@ public static function dataFormatterOutputProvider(): iterable
1,
1,
0,
false,
[
[
'message' => '#^Foo$#',
Expand All @@ -58,6 +60,7 @@ public static function dataFormatterOutputProvider(): iterable
1,
4,
0,
false,
[
[
'message' => "#^Bar\nBar2$#",
Expand Down Expand Up @@ -87,6 +90,7 @@ public static function dataFormatterOutputProvider(): iterable
1,
4,
2,
false,
[
[
'message' => "#^Bar\nBar2$#",
Expand All @@ -110,6 +114,36 @@ public static function dataFormatterOutputProvider(): iterable
],
],
];

yield [
'Multiple file, multiple generic errors (raw messages)',
1,
4,
2,
true,
[
[
'rawMessage' => "Bar\nBar2",
'count' => 1,
'path' => 'folder with unicode 😃/file name with "spaces" and unicode 😃.php',
],
[
'rawMessage' => 'Foo',
'count' => 1,
'path' => 'folder with unicode 😃/file name with "spaces" and unicode 😃.php',
],
[
'rawMessage' => "Bar\nBar2",
'count' => 1,
'path' => 'foo.php',
],
[
'rawMessage' => 'Foo<Bar>',
'count' => 1,
'path' => 'foo.php',
],
],
];
}

/**
Expand All @@ -121,10 +155,11 @@ public function testFormatErrors(
int $exitCode,
int $numFileErrors,
int $numGenericErrors,
bool $useRawMessage,
array $expected,
): void
{
$formatter = new BaselineNeonErrorFormatter(new SimpleRelativePathHelper(self::DIRECTORY_PATH));
$formatter = new BaselineNeonErrorFormatter(new SimpleRelativePathHelper(self::DIRECTORY_PATH), $useRawMessage);

$this->assertSame($exitCode, $formatter->formatErrors(
$this->getAnalysisResult($numFileErrors, $numGenericErrors),
Expand All @@ -137,7 +172,7 @@ public function testFormatErrors(

public function testFormatErrorMessagesRegexEscape(): void
{
$formatter = new BaselineNeonErrorFormatter(new SimpleRelativePathHelper(self::DIRECTORY_PATH));
$formatter = new BaselineNeonErrorFormatter(new SimpleRelativePathHelper(self::DIRECTORY_PATH), false);

$result = new AnalysisResult(
[new Error('Escape Regex with file # ~ \' ()', 'Testfile')],
Expand Down Expand Up @@ -176,11 +211,51 @@ public function testFormatErrorMessagesRegexEscape(): void
);
}

public function testEscapeDiNeon(): void
/**
* @return iterable<int, array{Error, bool, array<string, string|int>}>
*/
public static function dataEscapeDiNeon(): iterable
{
yield [
new Error('Test %value%', 'Testfile'),
false,
[
'message' => '#^Test %%value%%$#',
'count' => 1,
'path' => 'Testfile',
],
];

yield [
new Error('Test %value%', 'Testfile'),
true,
[
'rawMessage' => 'Test %%value%%',
'count' => 1,
'path' => 'Testfile',
],
];

yield [
new Error('@Foo', 'Testfile'),
true,
[
'rawMessage' => '@@Foo',
'count' => 1,
'path' => 'Testfile',
],
];
}

/**
* @param array<string, string|int> $expected
*/
#[DataProvider('dataEscapeDiNeon')]
public function testEscapeDiNeon(Error $error, bool $useRawMessage, array $expected): void
{
$formatter = new BaselineNeonErrorFormatter(new SimpleRelativePathHelper(self::DIRECTORY_PATH));
$formatter = new BaselineNeonErrorFormatter(new SimpleRelativePathHelper(self::DIRECTORY_PATH), $useRawMessage);
$result = new AnalysisResult(
[new Error('Test %value%', 'Testfile')],
[$error],
[],
[],
[],
Expand All @@ -203,11 +278,7 @@ public function testEscapeDiNeon(): void
Neon::encode([
'parameters' => [
'ignoreErrors' => [
[
'message' => '#^Test %%value%%$#',
'count' => 1,
'path' => 'Testfile',
],
$expected,
],
],
], Neon::BLOCK),
Expand Down Expand Up @@ -245,7 +316,7 @@ public static function outputOrderingProvider(): Generator
#[DataProvider('outputOrderingProvider')]
public function testOutputOrdering(array $errors): void
{
$formatter = new BaselineNeonErrorFormatter(new SimpleRelativePathHelper(self::DIRECTORY_PATH));
$formatter = new BaselineNeonErrorFormatter(new SimpleRelativePathHelper(self::DIRECTORY_PATH), false);
$result = new AnalysisResult(
$errors,
[],
Expand Down Expand Up @@ -404,7 +475,7 @@ public function testEndOfFileNewlines(
int $expectedNewlinesCount,
): void
{
$formatter = new BaselineNeonErrorFormatter(new SimpleRelativePathHelper(self::DIRECTORY_PATH));
$formatter = new BaselineNeonErrorFormatter(new SimpleRelativePathHelper(self::DIRECTORY_PATH), false);
$result = new AnalysisResult(
$errors,
[],
Expand Down Expand Up @@ -468,6 +539,7 @@ public static function dataFormatErrorsWithIdentifiers(): iterable
6,
))->withIdentifier('argument.type'),
],
false,
[
'parameters' => [
'ignoreErrors' => [
Expand Down Expand Up @@ -515,6 +587,7 @@ public static function dataFormatErrorsWithIdentifiers(): iterable
5,
))->withIdentifier('argument.type'),
],
false,
[
'parameters' => [
'ignoreErrors' => [
Expand Down Expand Up @@ -545,16 +618,59 @@ public static function dataFormatErrorsWithIdentifiers(): iterable
],
],
];

yield [
[
new Error(
'Foo',
__DIR__ . '/Foo.php',
5,
),
new Error(
'Foo',
__DIR__ . '/Foo.php',
5,
),
(new Error(
'Foo with identifier',
__DIR__ . '/Foo.php',
5,
))->withIdentifier('argument.type'),
(new Error(
'Foo with identifier',
__DIR__ . '/Foo.php',
6,
))->withIdentifier('argument.type'),
],
true,
[
'parameters' => [
'ignoreErrors' => [
[
'rawMessage' => 'Foo',
'count' => 2,
'path' => 'Foo.php',
],
[
'rawMessage' => 'Foo with identifier',
'identifier' => 'argument.type',
'count' => 2,
'path' => 'Foo.php',
],
],
],
],
];
}

/**
* @param list<Error> $errors
* @param mixed[] $expectedOutput
*/
#[DataProvider('dataFormatErrorsWithIdentifiers')]
public function testFormatErrorsWithIdentifiers(array $errors, array $expectedOutput): void
public function testFormatErrorsWithIdentifiers(array $errors, bool $useRawMessage, array $expectedOutput): void
{
$formatter = new BaselineNeonErrorFormatter(new SimpleRelativePathHelper(__DIR__));
$formatter = new BaselineNeonErrorFormatter(new SimpleRelativePathHelper(__DIR__), $useRawMessage);
$formatter->formatErrors(
new AnalysisResult(
$errors,
Expand Down
Loading
Loading