Skip to content

Commit 3cd0ed3

Browse files
MartinMystikJonasondrejmirtes
authored andcommitted
ignoreErrors: multiple messages in and explixit reportUnmatched
1 parent bd57fc5 commit 3cd0ed3

File tree

7 files changed

+139
-29
lines changed

7 files changed

+139
-29
lines changed

conf/config.neon

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,15 +314,28 @@ parametersSchema:
314314
structure([
315315
message: string()
316316
path: string()
317+
?reportUnmatched: bool()
318+
]),
319+
structure([
320+
messages: listOf(string())
321+
path: string()
322+
?reportUnmatched: bool()
317323
]),
318324
structure([
319325
message: string()
320326
count: int()
321327
path: string()
328+
?reportUnmatched: bool()
322329
]),
323330
structure([
324331
message: string()
325332
paths: listOf(string())
333+
?reportUnmatched: bool()
334+
]),
335+
structure([
336+
messages: listOf(string())
337+
paths: listOf(string())
338+
?reportUnmatched: bool()
326339
])
327340
)
328341
)

src/Analyser/IgnoredErrorHelper.php

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,37 @@ public function initialize(): IgnoredErrorHelperResult
2828
$otherIgnoreErrors = [];
2929
$ignoreErrorsByFile = [];
3030
$errors = [];
31-
foreach ($this->ignoreErrors as $i => $ignoreError) {
31+
32+
$expandedIgnoreErrors = [];
33+
foreach ($this->ignoreErrors as $ignoreError) {
34+
if (is_array($ignoreError)) {
35+
if (!isset($ignoreError['message']) && !isset($ignoreError['messages'])) {
36+
$errors[] = sprintf(
37+
'Ignored error %s is missing a message.',
38+
Json::encode($ignoreError),
39+
);
40+
continue;
41+
}
42+
if (isset($ignoreError['messages'])) {
43+
foreach ($ignoreError['messages'] as $message) {
44+
$expandedIgnoreError = $ignoreError;
45+
unset($expandedIgnoreError['messages']);
46+
$expandedIgnoreError['message'] = $message;
47+
$expandedIgnoreErrors[] = $expandedIgnoreError;
48+
}
49+
} else {
50+
$expandedIgnoreErrors[] = $ignoreError;
51+
}
52+
} else {
53+
$expandedIgnoreErrors[] = $ignoreError;
54+
}
55+
}
56+
57+
foreach ($expandedIgnoreErrors as $i => $ignoreError) {
58+
$ignoreErrorEntry = [
59+
'index' => $i,
60+
'ignoreError' => $ignoreError,
61+
];
3262
try {
3363
if (is_array($ignoreError)) {
3464
if (!isset($ignoreError['message'])) {
@@ -39,44 +69,32 @@ public function initialize(): IgnoredErrorHelperResult
3969
continue;
4070
}
4171
if (!isset($ignoreError['path'])) {
42-
if (!isset($ignoreError['paths'])) {
72+
if (!isset($ignoreError['paths']) && !isset($ignoreError['reportUnmatched'])) {
4373
$errors[] = sprintf(
44-
'Ignored error %s is missing a path.',
74+
'Ignored error %s is missing a path, paths or reportUnmatched.',
4575
Json::encode($ignoreError),
4676
);
4777
}
4878

49-
$otherIgnoreErrors[] = [
50-
'index' => $i,
51-
'ignoreError' => $ignoreError,
52-
];
79+
$otherIgnoreErrors[] = $ignoreErrorEntry;
5380
} elseif (@is_file($ignoreError['path'])) {
5481
$normalizedPath = $this->fileHelper->normalizePath($ignoreError['path']);
5582
$ignoreError['path'] = $normalizedPath;
56-
$ignoreErrorsByFile[$normalizedPath][] = [
57-
'index' => $i,
58-
'ignoreError' => $ignoreError,
59-
];
83+
$ignoreErrorsByFile[$normalizedPath][] = $ignoreErrorEntry;
6084
$ignoreError['realPath'] = $normalizedPath;
61-
$this->ignoreErrors[$i] = $ignoreError;
85+
$expandedIgnoreErrors[$i] = $ignoreError;
6286
} else {
63-
$otherIgnoreErrors[] = [
64-
'index' => $i,
65-
'ignoreError' => $ignoreError,
66-
];
87+
$otherIgnoreErrors[] = $ignoreErrorEntry;
6788
}
6889
} else {
69-
$otherIgnoreErrors[] = [
70-
'index' => $i,
71-
'ignoreError' => $ignoreError,
72-
];
90+
$otherIgnoreErrors[] = $ignoreErrorEntry;
7391
}
7492
} catch (JsonException $e) {
7593
$errors[] = $e->getMessage();
7694
}
7795
}
7896

79-
return new IgnoredErrorHelperResult($this->fileHelper, $errors, $otherIgnoreErrors, $ignoreErrorsByFile, $this->ignoreErrors, $this->reportUnmatchedIgnoredErrors);
97+
return new IgnoredErrorHelperResult($this->fileHelper, $errors, $otherIgnoreErrors, $ignoreErrorsByFile, $expandedIgnoreErrors, $this->reportUnmatchedIgnoredErrors);
8098
}
8199

82100
}

src/Analyser/IgnoredErrorHelperResult.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,12 @@ public function process(
186186

187187
$analysedFilesKeys = array_fill_keys($analysedFiles, true);
188188

189-
if ($this->reportUnmatchedIgnoredErrors && !$hasInternalErrors) {
189+
if (!$hasInternalErrors) {
190190
foreach ($unmatchedIgnoredErrors as $unmatchedIgnoredError) {
191+
$reportUnmatched = $unmatchedIgnoredError['reportUnmatched'] ?? $this->reportUnmatchedIgnoredErrors;
192+
if ($reportUnmatched === false) {
193+
continue;
194+
}
191195
if (
192196
isset($unmatchedIgnoredError['count'])
193197
&& isset($unmatchedIgnoredError['realCount'])

src/DependencyInjection/ParametersSchemaExtension.php

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use function array_map;
1717
use function count;
1818
use function is_array;
19+
use function substr;
1920

2021
class ParametersSchemaExtension extends CompilerExtension
2122
{
@@ -53,7 +54,7 @@ public function loadConfiguration(): void
5354
/**
5455
* @param Statement[] $statements
5556
*/
56-
private function processSchema(array $statements): Schema
57+
private function processSchema(array $statements, bool $required = true): Schema
5758
{
5859
if (count($statements) === 0) {
5960
throw new ShouldNotHappenException();
@@ -70,7 +71,9 @@ private function processSchema(array $statements): Schema
7071
}
7172
}
7273

73-
$parameterSchema->required();
74+
if ($required) {
75+
$parameterSchema->required();
76+
}
7477

7578
return $parameterSchema;
7679
}
@@ -79,7 +82,7 @@ private function processSchema(array $statements): Schema
7982
* @param mixed $argument
8083
* @return mixed
8184
*/
82-
private function processArgument($argument)
85+
private function processArgument($argument, bool $required = true)
8386
{
8487
if ($argument instanceof Statement) {
8588
if ($argument->entity === 'schema') {
@@ -96,14 +99,16 @@ private function processArgument($argument)
9699
throw new ShouldNotHappenException('schema() should have at least one argument.');
97100
}
98101

99-
return $this->processSchema($arguments);
102+
return $this->processSchema($arguments, $required);
100103
}
101104

102-
return $this->processSchema([$argument]);
105+
return $this->processSchema([$argument], $required);
103106
} elseif (is_array($argument)) {
104107
$processedArray = [];
105108
foreach ($argument as $key => $val) {
106-
$processedArray[$key] = $this->processArgument($val);
109+
$required = $key[0] !== '?';
110+
$key = $required ? $key : substr($key, 1);
111+
$processedArray[$key] = $this->processArgument($val, $required);
107112
}
108113

109114
return $processedArray;

tests/PHPStan/Analyser/AnalyserTest.php

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,21 @@ public function testIgnoreErrorByPath(): void
8383
$this->assertNoErrors($result);
8484
}
8585

86+
public function testIgnoreErrorMultiByPath(): void
87+
{
88+
$ignoreErrors = [
89+
[
90+
'messages' => [
91+
'#First fail#',
92+
'#Second fail#',
93+
],
94+
'path' => __DIR__ . '/data/two-different-fails.php',
95+
],
96+
];
97+
$result = $this->runAnalyser($ignoreErrors, true, __DIR__ . '/data/two-different-fails.php', false);
98+
$this->assertNoErrors($result);
99+
}
100+
86101
public function testIgnoreErrorByPathAndCount(): void
87102
{
88103
$ignoreErrors = [
@@ -200,6 +215,21 @@ public function testIgnoreErrorByPaths(): void
200215
$this->assertNoErrors($result);
201216
}
202217

218+
public function testIgnoreErrorMultiByPaths(): void
219+
{
220+
$ignoreErrors = [
221+
[
222+
'messages' => [
223+
'#First fail#',
224+
'#Second fail#',
225+
],
226+
'paths' => [__DIR__ . '/data/two-different-fails.php'],
227+
],
228+
];
229+
$result = $this->runAnalyser($ignoreErrors, true, __DIR__ . '/data/two-different-fails.php', false);
230+
$this->assertNoErrors($result);
231+
}
232+
203233
public function testIgnoreErrorByPathsMultipleUnmatched(): void
204234
{
205235
$ignoreErrors = [
@@ -301,7 +331,7 @@ public function testIgnoredErrorMissingPath(): void
301331
];
302332
$result = $this->runAnalyser($ignoreErrors, true, __DIR__ . '/data/empty/empty.php', false);
303333
$this->assertCount(1, $result);
304-
$this->assertSame('Ignored error {"message":"#Fail\\\\.#"} is missing a path.', $result[0]);
334+
$this->assertSame('Ignored error {"message":"#Fail\\\\.#"} is missing a path, paths or reportUnmatched.', $result[0]);
305335
}
306336

307337
public function testReportMultipleParserErrorsAtOnce(): void
@@ -416,6 +446,31 @@ public function testIgnoreLine(bool $reportUnmatchedIgnoredErrors): void
416446
$this->assertSame(26, $result[3]->getLine());
417447
}
418448

449+
public function testIgnoreErrorExplicitReportUnmatchedDisable(): void
450+
{
451+
$ignoreErrors = [
452+
[
453+
'message' => '#Fail#',
454+
'reportUnmatched' => false,
455+
],
456+
];
457+
$result = $this->runAnalyser($ignoreErrors, true, __DIR__ . '/data/bootstrap.php', false);
458+
$this->assertNoErrors($result);
459+
}
460+
461+
public function testIgnoreErrorExplicitReportUnmatchedEnable(): void
462+
{
463+
$ignoreErrors = [
464+
[
465+
'message' => '#Fail#',
466+
'reportUnmatched' => true,
467+
],
468+
];
469+
$result = $this->runAnalyser($ignoreErrors, false, __DIR__ . '/data/bootstrap.php', false);
470+
$this->assertCount(1, $result);
471+
$this->assertSame('Ignored error pattern #Fail# was not matched in reported errors.', $result[0]);
472+
}
473+
419474
/**
420475
* @param mixed[] $ignoreErrors
421476
* @param string|string[] $filePaths
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
3+
namespace TwoFails;
4+
5+
fail('First fail');
6+
fail('Second fail');

tests/PHPStan/Rules/AlwaysFailRule.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use PhpParser\Node;
66
use PHPStan\Analyser\Scope;
7+
use function count;
78

89
/**
910
* @implements Rule<Node\Expr\FuncCall>
@@ -16,6 +17,10 @@ public function getNodeType(): string
1617
return Node\Expr\FuncCall::class;
1718
}
1819

20+
/**
21+
* @param Node\Expr\FuncCall $node
22+
* @return string[]
23+
*/
1924
public function processNode(Node $node, Scope $scope): array
2025
{
2126
if (!$node->name instanceof Node\Name) {
@@ -26,6 +31,10 @@ public function processNode(Node $node, Scope $scope): array
2631
return [];
2732
}
2833

34+
if (count($node->getArgs()) === 1 && $node->getArgs()[0]->value instanceof Node\Scalar\String_) {
35+
return [$node->getArgs()[0]->value->value];
36+
}
37+
2938
return ['Fail.'];
3039
}
3140

0 commit comments

Comments
 (0)