diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index f8b7f59459..7300fbd87d 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -47,7 +47,7 @@ parameters: path: src/Analyser/MutatingScope.php - - message: "#^Only numeric types are allowed in pre\\-increment, bool\\|float\\|int\\|string\\|null given\\.$#" + message: "#^Only numeric types are allowed in pre\\-increment, float\\|int\\|string\\|null given\\.$#" count: 1 path: src/Analyser/MutatingScope.php diff --git a/src/Analyser/Analyser.php b/src/Analyser/Analyser.php index 5b41b25db6..fe3545bdd5 100644 --- a/src/Analyser/Analyser.php +++ b/src/Analyser/Analyser.php @@ -49,6 +49,10 @@ public function analyse( /** @var list $errors */ $errors = []; + /** @var list $filteredPhpErrors */ + $filteredPhpErrors = []; + /** @var list $allPhpErrors */ + $allPhpErrors = []; /** @var list $locallyIgnoredErrors */ $locallyIgnoredErrors = []; @@ -77,6 +81,9 @@ public function analyse( null, ); $errors = array_merge($errors, $fileAnalyserResult->getErrors()); + $filteredPhpErrors = array_merge($filteredPhpErrors, $fileAnalyserResult->getFilteredPhpErrors()); + $allPhpErrors = array_merge($allPhpErrors, $fileAnalyserResult->getAllPhpErrors()); + $locallyIgnoredErrors = array_merge($locallyIgnoredErrors, $fileAnalyserResult->getLocallyIgnoredErrors()); $linesToIgnore[$file] = $fileAnalyserResult->getLinesToIgnore(); $unmatchedLineIgnores[$file] = $fileAnalyserResult->getUnmatchedLineIgnores(); @@ -115,6 +122,8 @@ public function analyse( return new AnalyserResult( $errors, + $filteredPhpErrors, + $allPhpErrors, $locallyIgnoredErrors, $linesToIgnore, $unmatchedLineIgnores, diff --git a/src/Analyser/AnalyserResult.php b/src/Analyser/AnalyserResult.php index e9e0e562cb..56cafb327d 100644 --- a/src/Analyser/AnalyserResult.php +++ b/src/Analyser/AnalyserResult.php @@ -17,6 +17,8 @@ class AnalyserResult /** * @param list $unorderedErrors + * @param list $filteredPhpErrors + * @param list $allPhpErrors * @param list $locallyIgnoredErrors * @param array $linesToIgnore * @param array $unmatchedLineIgnores @@ -27,6 +29,8 @@ class AnalyserResult */ public function __construct( private array $unorderedErrors, + private array $filteredPhpErrors, + private array $allPhpErrors, private array $locallyIgnoredErrors, private array $linesToIgnore, private array $unmatchedLineIgnores, @@ -72,6 +76,22 @@ public function getErrors(): array return $this->errors; } + /** + * @return list + */ + public function getFilteredPhpErrors(): array + { + return $this->filteredPhpErrors; + } + + /** + * @return list + */ + public function getAllPhpErrors(): array + { + return $this->allPhpErrors; + } + /** * @return list */ diff --git a/src/Analyser/AnalyserResultFinalizer.php b/src/Analyser/AnalyserResultFinalizer.php index fc06852be7..4f09cfbfa6 100644 --- a/src/Analyser/AnalyserResultFinalizer.php +++ b/src/Analyser/AnalyserResultFinalizer.php @@ -8,6 +8,7 @@ use PHPStan\BetterReflection\Reflector\Exception\IdentifierNotFound; use PHPStan\Node\CollectedDataNode; use PHPStan\Rules\Registry as RuleRegistry; +use function array_merge; use function count; use function sprintf; @@ -27,12 +28,12 @@ public function __construct( public function finalize(AnalyserResult $analyserResult, bool $onlyFiles): FinalizerResult { if (count($analyserResult->getCollectedData()) === 0) { - return $this->addUnmatchedIgnoredErrors($analyserResult, [], []); + return $this->addUnmatchedIgnoredErrors($this->mergeFilteredPhpErrors($analyserResult), [], []); } $hasInternalErrors = count($analyserResult->getInternalErrors()) > 0 || $analyserResult->hasReachedInternalErrorsCountLimit(); if ($hasInternalErrors) { - return $this->addUnmatchedIgnoredErrors($analyserResult, [], []); + return $this->addUnmatchedIgnoredErrors($this->mergeFilteredPhpErrors($analyserResult), [], []); } $nodeType = CollectedDataNode::class; @@ -88,7 +89,9 @@ public function finalize(AnalyserResult $analyserResult, bool $onlyFiles): Final } return $this->addUnmatchedIgnoredErrors(new AnalyserResult( - $errors, + array_merge($errors, $analyserResult->getFilteredPhpErrors()), + [], + $analyserResult->getAllPhpErrors(), $locallyIgnoredErrors, $allLinesToIgnore, $allUnmatchedLineIgnores, @@ -101,6 +104,24 @@ public function finalize(AnalyserResult $analyserResult, bool $onlyFiles): Final ), $collectorErrors, $locallyIgnoredCollectorErrors); } + private function mergeFilteredPhpErrors(AnalyserResult $analyserResult): AnalyserResult + { + return new AnalyserResult( + array_merge($analyserResult->getUnorderedErrors(), $analyserResult->getFilteredPhpErrors()), + [], + $analyserResult->getAllPhpErrors(), + $analyserResult->getLocallyIgnoredErrors(), + $analyserResult->getLinesToIgnore(), + $analyserResult->getUnmatchedLineIgnores(), + $analyserResult->getInternalErrors(), + $analyserResult->getCollectedData(), + $analyserResult->getDependencies(), + $analyserResult->getExportedNodes(), + $analyserResult->hasReachedInternalErrorsCountLimit(), + $analyserResult->getPeakMemoryUsageBytes(), + ); + } + /** * @param list $collectorErrors * @param list $locallyIgnoredCollectorErrors @@ -150,6 +171,8 @@ private function addUnmatchedIgnoredErrors( return new FinalizerResult( new AnalyserResult( $errors, + $analyserResult->getFilteredPhpErrors(), + $analyserResult->getAllPhpErrors(), $analyserResult->getLocallyIgnoredErrors(), $analyserResult->getLinesToIgnore(), $analyserResult->getUnmatchedLineIgnores(), diff --git a/src/Analyser/FileAnalyser.php b/src/Analyser/FileAnalyser.php index 5794453370..0e6c861d94 100644 --- a/src/Analyser/FileAnalyser.php +++ b/src/Analyser/FileAnalyser.php @@ -16,7 +16,6 @@ use PHPStan\Parser\ParserErrorsException; use PHPStan\Rules\Registry as RuleRegistry; use function array_keys; -use function array_merge; use function array_unique; use function array_values; use function count; @@ -28,12 +27,23 @@ use function set_error_handler; use function sprintf; use const E_DEPRECATED; +use const E_ERROR; +use const E_NOTICE; +use const E_PARSE; +use const E_STRICT; +use const E_USER_ERROR; +use const E_USER_NOTICE; +use const E_USER_WARNING; +use const E_WARNING; class FileAnalyser { /** @var list */ - private array $collectedErrors = []; + private array $allPhpErrors = []; + + /** @var list */ + private array $filteredPhpErrors = []; public function __construct( private ScopeFactory $scopeFactory, @@ -209,8 +219,6 @@ public function analyseFile( $this->restoreCollectErrorsHandler(); - $fileErrors = array_merge($fileErrors, $this->collectedErrors); - foreach ($linesToIgnore as $fileKey => $lines) { if (count($lines) > 0) { continue; @@ -227,7 +235,17 @@ public function analyseFile( unset($unmatchedLineIgnores[$fileKey]); } - return new FileAnalyserResult($fileErrors, $locallyIgnoredErrors, $fileCollectedData, array_values(array_unique($fileDependencies)), $exportedNodes, $linesToIgnore, $unmatchedLineIgnores); + return new FileAnalyserResult( + $fileErrors, + $this->filteredPhpErrors, + $this->allPhpErrors, + $locallyIgnoredErrors, + $fileCollectedData, + array_values(array_unique($fileDependencies)), + $exportedNodes, + $linesToIgnore, + $unmatchedLineIgnores, + ); } /** @@ -249,13 +267,18 @@ private function getLinesToIgnoreFromTokens(array $nodes): array */ private function collectErrors(array $analysedFiles): void { - $this->collectedErrors = []; + $this->filteredPhpErrors = []; + $this->allPhpErrors = []; set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline) use ($analysedFiles): bool { if ((error_reporting() & $errno) === 0) { // silence @ operator return true; } + $errorMessage = sprintf('%s: %s', $this->getErrorLabel($errno), $errstr); + + $this->allPhpErrors[] = (new Error($errorMessage, $errfile, $errline, true))->withIdentifier('phpstan.php'); + if ($errno === E_DEPRECATED) { return true; } @@ -264,7 +287,7 @@ private function collectErrors(array $analysedFiles): void return true; } - $this->collectedErrors[] = (new Error($errstr, $errfile, $errline, true))->withIdentifier('phpstan.php'); + $this->filteredPhpErrors[] = (new Error($errorMessage, $errfile, $errline, true))->withIdentifier('phpstan.php'); return true; }); @@ -275,4 +298,28 @@ private function restoreCollectErrorsHandler(): void restore_error_handler(); } + private function getErrorLabel(int $errno): string + { + switch ($errno) { + case E_ERROR: + return 'Fatal error'; + case E_WARNING: + return 'Warning'; + case E_PARSE: + return 'Parse error'; + case E_NOTICE: + return 'Notice'; + case E_USER_ERROR: + return 'User error (E_USER_ERROR)'; + case E_USER_WARNING: + return 'User warning (E_USER_WARNING)'; + case E_USER_NOTICE: + return 'User notice (E_USER_NOTICE)'; + case E_STRICT: + return 'Strict error (E_STRICT)'; + } + + return 'Unknown PHP error'; + } + } diff --git a/src/Analyser/FileAnalyserResult.php b/src/Analyser/FileAnalyserResult.php index b464726f5f..23e34a9278 100644 --- a/src/Analyser/FileAnalyserResult.php +++ b/src/Analyser/FileAnalyserResult.php @@ -13,6 +13,8 @@ class FileAnalyserResult /** * @param list $errors + * @param list $filteredPhpErrors + * @param list $allPhpErrors * @param list $locallyIgnoredErrors * @param list $collectedData * @param list $dependencies @@ -22,6 +24,8 @@ class FileAnalyserResult */ public function __construct( private array $errors, + private array $filteredPhpErrors, + private array $allPhpErrors, private array $locallyIgnoredErrors, private array $collectedData, private array $dependencies, @@ -40,6 +44,22 @@ public function getErrors(): array return $this->errors; } + /** + * @return list + */ + public function getFilteredPhpErrors(): array + { + return $this->filteredPhpErrors; + } + + /** + * @return list + */ + public function getAllPhpErrors(): array + { + return $this->allPhpErrors; + } + /** * @return list */ diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 838f406def..93e5816a73 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -144,6 +144,7 @@ use function get_class; use function implode; use function in_array; +use function is_bool; use function is_numeric; use function is_string; use function ltrim; @@ -1540,7 +1541,9 @@ static function (): void { foreach ($varScalars as $varValue) { if ($node instanceof Expr\PreInc) { - ++$varValue; + if (!is_bool($varValue)) { + ++$varValue; + } } elseif (is_numeric($varValue)) { --$varValue; } diff --git a/src/Analyser/ResultCache/ResultCacheManager.php b/src/Analyser/ResultCache/ResultCacheManager.php index 12c9bbd828..0ebf90727c 100644 --- a/src/Analyser/ResultCache/ResultCacheManager.php +++ b/src/Analyser/ResultCache/ResultCacheManager.php @@ -507,6 +507,8 @@ public function process(AnalyserResult $analyserResult, ResultCache $resultCache return new ResultCacheProcessResult(new AnalyserResult( $flatErrors, + $analyserResult->getFilteredPhpErrors(), + $analyserResult->getAllPhpErrors(), $flatLocallyIgnoredErrors, $linesToIgnore, $unmatchedLineIgnores, diff --git a/src/Command/AnalyseApplication.php b/src/Command/AnalyseApplication.php index 9a83931b83..a71e9dd769 100644 --- a/src/Command/AnalyseApplication.php +++ b/src/Command/AnalyseApplication.php @@ -89,6 +89,8 @@ public function analyse( $stubErrors = $this->stubValidator->validate($projectStubFiles, $debug); $intermediateAnalyserResult = new AnalyserResult( array_merge($intermediateAnalyserResult->getUnorderedErrors(), $stubErrors), + $intermediateAnalyserResult->getFilteredPhpErrors(), + $intermediateAnalyserResult->getAllPhpErrors(), $intermediateAnalyserResult->getLocallyIgnoredErrors(), $intermediateAnalyserResult->getLinesToIgnore(), $intermediateAnalyserResult->getUnmatchedLineIgnores(), @@ -104,7 +106,10 @@ public function analyse( $resultCacheResult = $resultCacheManager->process($intermediateAnalyserResult, $resultCache, $errorOutput, $onlyFiles, true); $analyserResult = $this->analyserResultFinalizer->finalize($resultCacheResult->getAnalyserResult(), $onlyFiles)->getAnalyserResult(); $internalErrors = $analyserResult->getInternalErrors(); - $errors = $analyserResult->getErrors(); + $errors = array_merge( + $analyserResult->getErrors(), + $analyserResult->getFilteredPhpErrors(), + ); $hasInternalErrors = count($internalErrors) > 0 || $analyserResult->hasReachedInternalErrorsCountLimit(); $memoryUsageBytes = $analyserResult->getPeakMemoryUsageBytes(); $isResultCacheUsed = !$resultCache->isFullAnalysis(); @@ -181,7 +186,7 @@ private function runAnalyser( $errorOutput->getStyle()->progressStart($allAnalysedFilesCount); $errorOutput->getStyle()->progressAdvance($allAnalysedFilesCount); $errorOutput->getStyle()->progressFinish(); - return new AnalyserResult([], [], [], [], [], [], [], [], false, memory_get_peak_usage(true)); + return new AnalyserResult([], [], [], [], [], [], [], [], [], [], false, memory_get_peak_usage(true)); } if (!$debug) { diff --git a/src/Command/AnalyserRunner.php b/src/Command/AnalyserRunner.php index f820818484..da6eb42962 100644 --- a/src/Command/AnalyserRunner.php +++ b/src/Command/AnalyserRunner.php @@ -47,7 +47,7 @@ public function runAnalyser( { $filesCount = count($files); if ($filesCount === 0) { - return new AnalyserResult([], [], [], [], [], [], [], [], false, memory_get_peak_usage(true)); + return new AnalyserResult([], [], [], [], [], [], [], [], [], [], false, memory_get_peak_usage(true)); } $schedule = $this->scheduler->scheduleWork($this->cpuCoreCounter->getNumberOfCpuCores(), $files); diff --git a/src/Command/FixerWorkerCommand.php b/src/Command/FixerWorkerCommand.php index 0c40996e76..942c66e486 100644 --- a/src/Command/FixerWorkerCommand.php +++ b/src/Command/FixerWorkerCommand.php @@ -321,7 +321,7 @@ private function runAnalyser(LoopInterface $loop, Container $container, array $f $parallelAnalyser = $container->getByType(ParallelAnalyser::class); $filesCount = count($files); if ($filesCount === 0) { - return resolve(new AnalyserResult([], [], [], [], [], [], [], [], false, memory_get_peak_usage(true))); + return resolve(new AnalyserResult([], [], [], [], [], [], [], [], [], [], false, memory_get_peak_usage(true))); } /** @var Scheduler $scheduler */ diff --git a/src/Command/WorkerCommand.php b/src/Command/WorkerCommand.php index d21fccefeb..01be926b0f 100644 --- a/src/Command/WorkerCommand.php +++ b/src/Command/WorkerCommand.php @@ -23,6 +23,7 @@ use Symfony\Component\Console\Output\OutputInterface; use Throwable; use function array_fill_keys; +use function array_merge; use function defined; use function is_array; use function is_bool; @@ -182,6 +183,8 @@ private function runWorker( $internalErrorsCount = 0; $files = $json['files']; $errors = []; + $filteredPhpErrors = []; + $allPhpErrors = []; $locallyIgnoredErrors = []; $linesToIgnore = []; $unmatchedLineIgnores = []; @@ -192,6 +195,8 @@ private function runWorker( try { $fileAnalyserResult = $fileAnalyser->analyseFile($file, $analysedFiles, $ruleRegistry, $collectorRegistry, null); $fileErrors = $fileAnalyserResult->getErrors(); + $filteredPhpErrors = array_merge($filteredPhpErrors, $fileAnalyserResult->getFilteredPhpErrors()); + $allPhpErrors = array_merge($allPhpErrors, $fileAnalyserResult->getAllPhpErrors()); $linesToIgnore[$file] = $fileAnalyserResult->getLinesToIgnore(); $unmatchedLineIgnores[$file] = $fileAnalyserResult->getUnmatchedLineIgnores(); $dependencies[$file] = $fileAnalyserResult->getDependencies(); @@ -227,6 +232,8 @@ private function runWorker( 'action' => 'result', 'result' => [ 'errors' => $errors, + 'filteredPhpErrors' => $filteredPhpErrors, + 'allPhpErrors' => $allPhpErrors, 'locallyIgnoredErrors' => $locallyIgnoredErrors, 'linesToIgnore' => $linesToIgnore, 'unmatchedLineIgnores' => $unmatchedLineIgnores, diff --git a/src/Parallel/ParallelAnalyser.php b/src/Parallel/ParallelAnalyser.php index 85966907ce..8bc740dd2c 100644 --- a/src/Parallel/ParallelAnalyser.php +++ b/src/Parallel/ParallelAnalyser.php @@ -70,6 +70,8 @@ public function analyse( $numberOfProcesses = $schedule->getNumberOfProcesses(); $someChildEnded = false; $errors = []; + $filteredPhpErrors = []; + $allPhpErrors = []; $locallyIgnoredErrors = []; $linesToIgnore = []; $unmatchedLineIgnores = []; @@ -84,7 +86,7 @@ public function analyse( $deferred = new Deferred(); $server = new TcpServer('127.0.0.1:0', $loop); - $this->processPool = new ProcessPool($server, static function () use ($deferred, &$jobs, &$internalErrors, &$internalErrorsCount, &$reachedInternalErrorsCountLimit, &$errors, &$locallyIgnoredErrors, &$linesToIgnore, &$unmatchedLineIgnores, &$collectedData, &$dependencies, &$exportedNodes, &$peakMemoryUsages): void { + $this->processPool = new ProcessPool($server, static function () use ($deferred, &$jobs, &$internalErrors, &$internalErrorsCount, &$reachedInternalErrorsCountLimit, &$errors, &$filteredPhpErrors, &$allPhpErrors, &$locallyIgnoredErrors, &$linesToIgnore, &$unmatchedLineIgnores, &$collectedData, &$dependencies, &$exportedNodes, &$peakMemoryUsages): void { if (count($jobs) > 0 && $internalErrorsCount === 0) { $internalErrors[] = 'Some parallel worker jobs have not finished.'; $internalErrorsCount++; @@ -92,6 +94,8 @@ public function analyse( $deferred->resolve(new AnalyserResult( $errors, + $filteredPhpErrors, + $allPhpErrors, $locallyIgnoredErrors, $linesToIgnore, $unmatchedLineIgnores, @@ -159,7 +163,7 @@ public function analyse( $commandOptions, $input, ), $loop, $this->processTimeout); - $process->start(function (array $json) use ($process, &$internalErrors, &$errors, &$locallyIgnoredErrors, &$linesToIgnore, &$unmatchedLineIgnores, &$collectedData, &$dependencies, &$exportedNodes, &$peakMemoryUsages, &$jobs, $postFileCallback, &$internalErrorsCount, &$reachedInternalErrorsCountLimit, $processIdentifier, $onFileAnalysisHandler): void { + $process->start(function (array $json) use ($process, &$internalErrors, &$errors, &$filteredPhpErrors, &$allPhpErrors, &$locallyIgnoredErrors, &$linesToIgnore, &$unmatchedLineIgnores, &$collectedData, &$dependencies, &$exportedNodes, &$peakMemoryUsages, &$jobs, $postFileCallback, &$internalErrorsCount, &$reachedInternalErrorsCountLimit, $processIdentifier, $onFileAnalysisHandler): void { $fileErrors = []; foreach ($json['errors'] as $jsonError) { if (is_string($jsonError)) { @@ -170,6 +174,14 @@ public function analyse( $fileErrors[] = Error::decode($jsonError); } + foreach ($json['filteredPhpErrors'] as $filteredPhpError) { + $filteredPhpErrors[] = Error::decode($filteredPhpError); + } + + foreach ($json['allPhpErrors'] as $allPhpError) { + $allPhpErrors[] = Error::decode($allPhpError); + } + $locallyIgnoredFileErrors = []; foreach ($json['locallyIgnoredErrors'] as $locallyIgnoredJsonError) { $locallyIgnoredFileErrors[] = Error::decode($locallyIgnoredJsonError); diff --git a/src/Testing/RuleTestCase.php b/src/Testing/RuleTestCase.php index bbaa0789cd..67cae7e669 100644 --- a/src/Testing/RuleTestCase.php +++ b/src/Testing/RuleTestCase.php @@ -177,6 +177,13 @@ public function gatherAnalyserErrors(array $files): array $this->fail(implode("\n", $analyserResult->getInternalErrors())); } + if ($this->shouldFailOnPhpErrors() && count($analyserResult->getAllPhpErrors()) > 0) { + $this->fail(implode("\n", array_map( + static fn (Error $error): string => sprintf('%s on %s:%d', $error->getMessage(), $error->getFile(), $error->getLine()), + $analyserResult->getAllPhpErrors(), + ))); + } + $finalizer = new AnalyserResultFinalizer( $ruleRegistry, new RuleErrorTransformer(), @@ -198,6 +205,11 @@ protected function shouldPolluteScopeWithAlwaysIterableForeach(): bool return true; } + protected function shouldFailOnPhpErrors(): bool + { + return true; + } + public static function getAdditionalConfigFiles(): array { return [ diff --git a/src/Type/Php/SimpleXMLElementConstructorThrowTypeExtension.php b/src/Type/Php/SimpleXMLElementConstructorThrowTypeExtension.php index 41d903e5c5..ea5da83872 100644 --- a/src/Type/Php/SimpleXMLElementConstructorThrowTypeExtension.php +++ b/src/Type/Php/SimpleXMLElementConstructorThrowTypeExtension.php @@ -12,6 +12,7 @@ use SimpleXMLElement; use function count; use function extension_loaded; +use function libxml_use_internal_errors; class SimpleXMLElementConstructorThrowTypeExtension implements DynamicStaticMethodThrowTypeExtension { @@ -32,14 +33,20 @@ public function getThrowTypeFromStaticMethodCall(MethodReflection $methodReflect $valueType = $scope->getType($methodCall->getArgs()[0]->value); $constantStrings = $valueType->getConstantStrings(); - foreach ($constantStrings as $constantString) { - try { - new SimpleXMLElement($constantString->getValue()); - } catch (\Exception $e) { // phpcs:ignore - return $methodReflection->getThrowType(); - } + $internalErrorsOld = libxml_use_internal_errors(true); + + try { + foreach ($constantStrings as $constantString) { + try { + new SimpleXMLElement($constantString->getValue()); + } catch (\Exception $e) { // phpcs:ignore + return $methodReflection->getThrowType(); + } - $valueType = TypeCombinator::remove($valueType, $constantString); + $valueType = TypeCombinator::remove($valueType, $constantString); + } + } finally { + libxml_use_internal_errors($internalErrorsOld); } if (!$valueType instanceof NeverType) { diff --git a/tests/PHPStan/Rules/WarningEmittingRuleTest.php b/tests/PHPStan/Rules/WarningEmittingRuleTest.php new file mode 100644 index 0000000000..6b0ac62cb8 --- /dev/null +++ b/tests/PHPStan/Rules/WarningEmittingRuleTest.php @@ -0,0 +1,53 @@ + + */ +class WarningEmittingRuleTest extends RuleTestCase +{ + + /** + * @return Rule + */ + protected function getRule(): Rule + { + return new class implements Rule { + + public function getNodeType(): string + { + return Node::class; + } + + public function processNode(Node $node, Scope $scope): array + { + echo $undefined; // @phpstan-ignore variable.undefined + return []; + } + + }; + } + + public function testRule(): void + { + if (PHP_VERSION_ID < 70300) { + self::markTestSkipped('For some reason this test does not work on PHP 7.2 with old PHPUnit'); + } + + try { + $this->analyse([__DIR__ . '/data/empty-file.php'], []); + self::fail('Should throw an exception'); + + } catch (AssertionFailedError $e) { + self::assertStringContainsString('Undefined variable', $e->getMessage()); // exact message differs between PHPStan versions + } + } + +} diff --git a/tests/PHPStan/Rules/data/empty-file.php b/tests/PHPStan/Rules/data/empty-file.php new file mode 100644 index 0000000000..60ac8c38d2 --- /dev/null +++ b/tests/PHPStan/Rules/data/empty-file.php @@ -0,0 +1,3 @@ +