diff --git a/src/Testing/RuleTestCase.php b/src/Testing/RuleTestCase.php index 47c8a997e9..cecb642af2 100644 --- a/src/Testing/RuleTestCase.php +++ b/src/Testing/RuleTestCase.php @@ -2,6 +2,7 @@ namespace PHPStan\Testing; +use LogicException; use PhpParser\Node; use PHPStan\Analyser\Analyser; use PHPStan\Analyser\AnalyserResultFinalizer; @@ -40,7 +41,9 @@ use function array_map; use function array_merge; use function count; +use function file; use function implode; +use function preg_match; use function sprintf; use function str_replace; @@ -142,10 +145,26 @@ private function getAnalyser(DirectRuleRegistry $ruleRegistry): Analyser /** * @param string[] $files - * @param list $expectedErrors + * @param ?list $expectedErrors */ - public function analyse(array $files, array $expectedErrors): void + public function analyse(array $files, ?array $expectedErrors): void { + if ($expectedErrors === null) { + $expectedErrors = []; + foreach ($files as $file) { + $lines = file($file); + if ($lines === false) { + throw new LogicException('Error while reading data from ' . $file); + } + foreach ($lines as $n => $line) { + if (preg_match('~// error: (.+?)(?:, tip: (.+))?$~m', $line, $match) !== 1) { + continue; + } + $expectedErrors[] = [$match[1], $n + 1, $match[2] ?? null]; + } + } + } + [$actualErrors, $delayedErrors] = $this->gatherAnalyserErrorsWithDelayedErrors($files); $strictlyTypedSprintf = static function (int $line, string $message, ?string $tip): string { $message = sprintf('%02d: %s', $line, $message); diff --git a/tests/PHPStan/Rules/Comparison/BooleanAndConstantConditionRuleTest.php b/tests/PHPStan/Rules/Comparison/BooleanAndConstantConditionRuleTest.php index aa55dbe441..a3e58e376e 100644 --- a/tests/PHPStan/Rules/Comparison/BooleanAndConstantConditionRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/BooleanAndConstantConditionRuleTest.php @@ -346,20 +346,14 @@ public function testBug5743(): void public static function dataBug4969(): iterable { yield [false, []]; - yield [true, [ - [ - 'Result of && is always false.', - 15, - 'Because the type is coming from a PHPDoc, you can turn off this check by setting treatPhpDocTypesAsCertain: false in your %configurationFile%.', - ], - ]]; + yield [true, null]; } /** - * @param list $expectedErrors + * @param ?list $expectedErrors */ #[DataProvider('dataBug4969')] - public function testBug4969(bool $treatPhpDocTypesAsCertain, array $expectedErrors): void + public function testBug4969(bool $treatPhpDocTypesAsCertain, ?array $expectedErrors): void { $this->treatPhpDocTypesAsCertain = $treatPhpDocTypesAsCertain; $this->analyse([__DIR__ . '/data/bug-4969.php'], $expectedErrors); diff --git a/tests/PHPStan/Rules/Comparison/MatchExpressionRuleTest.php b/tests/PHPStan/Rules/Comparison/MatchExpressionRuleTest.php index 014b57899c..6b986ffd53 100644 --- a/tests/PHPStan/Rules/Comparison/MatchExpressionRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/MatchExpressionRuleTest.php @@ -37,57 +37,7 @@ protected function shouldTreatPhpDocTypesAsCertain(): bool public function testRule(): void { - $tipText = 'Remove remaining cases below this one and this error will disappear too.'; - $this->analyse([__DIR__ . '/data/match-expr.php'], [ - [ - 'Match arm comparison between 1|2|3 and \'foo\' is always false.', - 14, - ], - [ - 'Match arm comparison between 1|2|3 and 0 is always false.', - 19, - ], - [ - 'Match arm comparison between 3 and 3 is always true.', - 28, - $tipText, - ], - [ - 'Match arm comparison between 3 and 3 is always true.', - 35, - $tipText, - ], - [ - 'Match arm comparison between 1 and 1 is always true.', - 40, - $tipText, - ], - [ - 'Match arm comparison between 1 and 1 is always true.', - 46, - $tipText, - ], - [ - 'Match expression does not handle remaining value: 3', - 50, - ], - [ - 'Match arm comparison between 1|2 and 3 is always false.', - 61, - ], - [ - 'Match expression does not handle remaining values: 1|2|3', - 78, - ], - [ - 'Match expression does not handle remaining value: true', - 90, - ], - [ - 'Match expression does not handle remaining values: int|int<2, max>', - 168, - ], - ]); + $this->analyse([__DIR__ . '/data/match-expr.php'], null); } public function testBug5161(): void diff --git a/tests/PHPStan/Rules/Comparison/data/bug-4969.php b/tests/PHPStan/Rules/Comparison/data/bug-4969.php index 658c311cf0..f7c787fa53 100644 --- a/tests/PHPStan/Rules/Comparison/data/bug-4969.php +++ b/tests/PHPStan/Rules/Comparison/data/bug-4969.php @@ -12,7 +12,7 @@ public function set(array $config): void if (!is_string($config['host'])) { throw new \InvalidArgumentException('error'); } - if (isset($config['port']) && !is_int($config['port'])) { + if (isset($config['port']) && !is_int($config['port'])) { // error: Result of && is always false., tip: Because the type is coming from a PHPDoc, you can turn off this check by setting treatPhpDocTypesAsCertain: false in your %configurationFile%. throw new \InvalidArgumentException('error'); } } diff --git a/tests/PHPStan/Rules/Comparison/data/match-expr.php b/tests/PHPStan/Rules/Comparison/data/match-expr.php index baeec9922d..088aedc222 100644 --- a/tests/PHPStan/Rules/Comparison/data/match-expr.php +++ b/tests/PHPStan/Rules/Comparison/data/match-expr.php @@ -11,12 +11,12 @@ class Foo public function doFoo(int $i): void { match ($i) { - 'foo' => null, // always false + 'foo' => null, // always false // error: Match arm comparison between 1|2|3 and 'foo' is always false. default => null, }; match ($i) { - 0 => null, + 0 => null, // error: Match arm comparison between 1|2|3 and 0 is always false. 1 => null, 2 => null, 3 => null, // always true, but do not report (it's the last one) @@ -25,29 +25,29 @@ public function doFoo(int $i): void match ($i) { 1 => null, 2 => null, - 3 => null, // always true - report with strict-rules + 3 => null, // always true - report with strict-rules // error: Match arm comparison between 3 and 3 is always true., tip: Remove remaining cases below this one and this error will disappear too. 4 => null, // unreachable }; match ($i) { 1 => null, 2 => null, - 3 => null, // always true - report with strict-rules + 3 => null, // always true - report with strict-rules // error: Match arm comparison between 3 and 3 is always true., tip: Remove remaining cases below this one and this error will disappear too. default => null, // unreachable }; match (1) { - 1 => null, // always true - report with strict-rules + 1 => null, // always true - report with strict-rules // error: Match arm comparison between 1 and 1 is always true., tip: Remove remaining cases below this one and this error will disappear too. 2 => null, // unreachable 3 => null, // unreachable }; match (1) { - 1 => null, // always true - report with strict-rules + 1 => null, // always true - report with strict-rules // error: Match arm comparison between 1 and 1 is always true., tip: Remove remaining cases below this one and this error will disappear too. default => null, // unreachable }; - match ($i) { + match ($i) { // error: Match expression does not handle remaining value: 3 1, 2 => null, // unhandled }; @@ -58,7 +58,7 @@ public function doFoo(int $i): void }; match ($i) { - 3, 3 => null, // second 3 is always false + 3, 3 => null, // second 3 is always false // error: Match arm comparison between 1|2 and 3 is always false. default => null, }; @@ -75,7 +75,7 @@ public function doFoo(int $i): void 1 => 2, }; - match ($i) { + match ($i) { // error: Match expression does not handle remaining values: 1|2|3 // unhandled }; } @@ -87,7 +87,7 @@ public function doBar(\Exception $e): void default => null, }; - match (true) { + match (true) { // error: Match expression does not handle remaining value: true $e instanceof \InvalidArgumentException => true, $e instanceof \InvalidArgumentException => true, // reported by ImpossibleInstanceOfRule }; @@ -165,7 +165,7 @@ public function bar(int $bar): void */ public function baz(int $bar): void { - $str = match($bar) { + $str = match($bar) { // error: Match expression does not handle remaining values: int|int<2, max> 1 => 'test' }; }