Skip to content

Commit aa92113

Browse files
committed
When internal errors occur, do not show other reported errors
1 parent c90dcdd commit aa92113

File tree

8 files changed

+160
-59
lines changed

8 files changed

+160
-59
lines changed

src/Analyser/Analyser.php

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
use function array_merge;
1212
use function count;
1313
use function memory_get_peak_usage;
14-
use function sprintf;
1514

1615
class Analyser
1716
{
@@ -99,14 +98,7 @@ public function analyse(
9998
throw $t;
10099
}
101100
$internalErrorsCount++;
102-
$internalErrorMessage = sprintf('Internal error: %s', $t->getMessage());
103-
$internalErrorMessage .= sprintf(
104-
'%sRun PHPStan with --debug option and post the stack trace to:%s%s',
105-
"\n",
106-
"\n",
107-
'https://github.com/phpstan/phpstan/issues/new?template=Bug_report.yaml',
108-
);
109-
$errors[] = (new Error($internalErrorMessage, $file, null, $t))
101+
$errors[] = (new Error($t->getMessage(), $file, null, $t))
110102
->withIdentifier('phpstan.internal')
111103
->withMetadata([
112104
InternalError::STACK_TRACE_METADATA_KEY => InternalError::prepareTrace($t),

src/Analyser/InternalError.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ class InternalError implements JsonSerializable
2323
*/
2424
public function __construct(
2525
private string $message,
26+
private string $contextDescription,
2627
private array $trace,
28+
private ?string $traceAsString,
2729
)
2830
{
2931
}
@@ -51,6 +53,11 @@ public function getMessage(): string
5153
return $this->message;
5254
}
5355

56+
public function getContextDescription(): string
57+
{
58+
return $this->contextDescription;
59+
}
60+
5461
/**
5562
* @return Trace
5663
*/
@@ -59,12 +66,17 @@ public function getTrace(): array
5966
return $this->trace;
6067
}
6168

69+
public function getTraceAsString(): ?string
70+
{
71+
return $this->traceAsString;
72+
}
73+
6274
/**
6375
* @param mixed[] $json
6476
*/
6577
public static function decode(array $json): self
6678
{
67-
return new self($json['message'], $json['trace']);
79+
return new self($json['message'], $json['contextDescription'], $json['trace'], $json['traceAsString']);
6880
}
6981

7082
/**
@@ -75,7 +87,9 @@ public function jsonSerialize()
7587
{
7688
return [
7789
'message' => $this->message,
90+
'contextDescription' => $this->contextDescription,
7891
'trace' => $this->trace,
92+
'traceAsString' => $this->traceAsString,
7993
];
8094
}
8195

src/Command/AnalyseCommand.php

Lines changed: 96 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace PHPStan\Command;
44

55
use OndraM\CiDetector\CiDetector;
6+
use PHPStan\Analyser\InternalError;
67
use PHPStan\Command\ErrorFormatter\BaselineNeonErrorFormatter;
78
use PHPStan\Command\ErrorFormatter\BaselinePhpErrorFormatter;
89
use PHPStan\Command\ErrorFormatter\ErrorFormatter;
@@ -328,13 +329,108 @@ protected function execute(InputInterface $input, OutputInterface $output): int
328329
throw $t;
329330
}
330331

332+
$internalErrors = [];
333+
foreach ($analysisResult->getInternalErrorObjects() as $internalError) {
334+
$internalErrors[$internalError->getMessage()] = new InternalError(
335+
sprintf('Internal error: %s', $internalError->getMessage()),
336+
$internalError->getContextDescription(),
337+
$internalError->getTrace(),
338+
$internalError->getTraceAsString(),
339+
);
340+
}
341+
foreach ($analysisResult->getFileSpecificErrors() as $fileSpecificError) {
342+
if (!$fileSpecificError->hasNonIgnorableException()) {
343+
continue;
344+
}
345+
346+
$message = $fileSpecificError->getMessage();
347+
if ($fileSpecificError->getIdentifier() === 'phpstan.internal') {
348+
$message = sprintf('Internal error: %s', $message);
349+
}
350+
351+
$metadata = $fileSpecificError->getMetadata();
352+
$internalErrors[$fileSpecificError->getMessage()] = new InternalError(
353+
$message,
354+
sprintf('analysing file %s', $fileSpecificError->getTraitFilePath() ?? $fileSpecificError->getFilePath()),
355+
$metadata[InternalError::STACK_TRACE_METADATA_KEY] ?? [],
356+
$metadata[InternalError::STACK_TRACE_AS_STRING_METADATA_KEY] ?? null,
357+
);
358+
}
359+
360+
$internalErrors = array_values($internalErrors);
361+
$bugReportUrl = 'https://github.com/phpstan/phpstan/issues/new?template=Bug_report.yaml';
362+
foreach ($internalErrors as $i => $internalError) {
363+
$message = sprintf('%s while %s', $internalError->getMessage(), $internalError->getContextDescription());
364+
if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
365+
$firstTraceItem = $internalError->getTrace()[0] ?? null;
366+
$trace = '';
367+
if ($firstTraceItem !== null && $firstTraceItem['file'] !== null && $firstTraceItem['line'] !== null) {
368+
$trace = sprintf('## %s(%d)%s', $firstTraceItem['file'], $firstTraceItem['line'], "\n");
369+
}
370+
$trace .= $internalError->getTraceAsString();
371+
$message .= sprintf('%sPost the following stack trace to %s: %s%s', "\n\n", $bugReportUrl, "\n", $trace);
372+
} else {
373+
$message .= sprintf('%sRun PHPStan with -v option and post the stack trace to:%s%s', "\n", "\n", $bugReportUrl);
374+
}
375+
$internalErrors[$i] = new InternalError(
376+
$message,
377+
$internalError->getContextDescription(),
378+
$internalError->getTrace(),
379+
$internalError->getTraceAsString(),
380+
);
381+
}
382+
383+
$internalErrors = array_values($internalErrors);
384+
331385
if ($generateBaselineFile !== null) {
386+
if (count($internalErrors) > 0) {
387+
foreach ($internalErrors as $internalError) {
388+
$inceptionResult->getStdOutput()->writeLineFormatted($internalError->getMessage());
389+
$inceptionResult->getStdOutput()->writeLineFormatted('');
390+
}
391+
392+
$inceptionResult->getStdOutput()->getStyle()->error(sprintf(
393+
'%s occurred. Baseline could not be generated.',
394+
count($internalErrors) === 1 ? 'An internal error' : 'Internal errors',
395+
));
396+
397+
return $inceptionResult->handleReturn(1, $analysisResult->getPeakMemoryUsageBytes());
398+
}
399+
332400
return $this->generateBaseline($generateBaselineFile, $inceptionResult, $analysisResult, $output, $allowEmptyBaseline, $baselineExtension, $failWithoutResultCache);
333401
}
334402

335403
/** @var ErrorFormatter $errorFormatter */
336404
$errorFormatter = $container->getService($errorFormatterServiceName);
337405

406+
if (count($internalErrors) > 0) {
407+
$analysisResult = new AnalysisResult(
408+
[],
409+
array_map(static fn (InternalError $internalError) => $internalError->getMessage(), $internalErrors),
410+
[],
411+
[],
412+
[],
413+
$analysisResult->isDefaultLevelUsed(),
414+
$analysisResult->getProjectConfigFile(),
415+
$analysisResult->isResultCacheSaved(),
416+
$analysisResult->getPeakMemoryUsageBytes(),
417+
$analysisResult->isResultCacheUsed(),
418+
$analysisResult->getChangedProjectExtensionFilesOutsideOfAnalysedPaths(),
419+
);
420+
421+
$exitCode = $errorFormatter->formatErrors($analysisResult, $inceptionResult->getStdOutput());
422+
423+
$errorOutput->writeLineFormatted('⚠️ Result is incomplete because of internal errors. ⚠️');
424+
$errorOutput->writeLineFormatted(' Fix these errors first and then re-run PHPStan');
425+
$errorOutput->writeLineFormatted(' to get all reported errors.');
426+
$errorOutput->writeLineFormatted('');
427+
428+
return $inceptionResult->handleReturn(
429+
$exitCode,
430+
$analysisResult->getPeakMemoryUsageBytes(),
431+
);
432+
}
433+
338434
$exitCode = $errorFormatter->formatErrors($analysisResult, $inceptionResult->getStdOutput());
339435
if ($failWithoutResultCache && !$analysisResult->isResultCacheUsed()) {
340436
$exitCode = 2;
@@ -413,35 +509,6 @@ private function generateBaseline(string $generateBaselineFile, InceptionResult
413509

414510
return $inceptionResult->handleReturn(1, $analysisResult->getPeakMemoryUsageBytes());
415511
}
416-
if ($analysisResult->hasInternalErrors()) {
417-
$internalErrors = array_values(array_unique($analysisResult->getInternalErrors()));
418-
419-
foreach ($internalErrors as $internalError) {
420-
$inceptionResult->getStdOutput()->writeLineFormatted($internalError);
421-
$inceptionResult->getStdOutput()->writeLineFormatted('');
422-
}
423-
424-
$inceptionResult->getStdOutput()->getStyle()->error(sprintf(
425-
'%s occurred. Baseline could not be generated.',
426-
count($internalErrors) === 1 ? 'An internal error' : 'Internal errors',
427-
));
428-
429-
return $inceptionResult->handleReturn(1, $analysisResult->getPeakMemoryUsageBytes());
430-
}
431-
432-
foreach ($analysisResult->getFileSpecificErrors() as $fileSpecificError) {
433-
if (!$fileSpecificError->hasNonIgnorableException()) {
434-
continue;
435-
}
436-
437-
$inceptionResult->getStdOutput()->getStyle()->error('An internal error occurred. Baseline could not be generated.');
438-
439-
$inceptionResult->getStdOutput()->writeLineFormatted($fileSpecificError->getMessage());
440-
$inceptionResult->getStdOutput()->writeLineFormatted($fileSpecificError->getFile());
441-
$inceptionResult->getStdOutput()->writeLineFormatted('');
442-
443-
return $inceptionResult->handleReturn(1, $analysisResult->getPeakMemoryUsageBytes());
444-
}
445512

446513
$streamOutput = $this->createStreamOutput();
447514
$errorConsoleStyle = new ErrorsConsoleStyle(new StringInput(''), $streamOutput);

src/Command/AnalysisResult.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,22 @@ public function getNotFileSpecificErrors(): array
8181
}
8282

8383
/**
84+
* @deprecated Use getInternalErrorObjects
8485
* @return list<string>
8586
*/
8687
public function getInternalErrors(): array
8788
{
8889
return array_map(static fn (InternalError $internalError) => $internalError->getMessage(), $this->internalErrors);
8990
}
9091

92+
/**
93+
* @return list<InternalError>
94+
*/
95+
public function getInternalErrorObjects(): array
96+
{
97+
return $this->internalErrors;
98+
}
99+
91100
/**
92101
* @return list<string>
93102
*/

src/Command/FixerApplication.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,12 @@ private function analyse(
480480
$server->close();
481481
$output->writeln('<error>Worker process exited: ' . $e->getMessage() . '</error>');
482482
$phpstanFixerEncoder->write(['action' => 'analysisCrash', 'data' => [
483-
'internalErrors' => [new InternalError($e->getMessage(), InternalError::prepareTrace($e))],
483+
'internalErrors' => [new InternalError(
484+
$e->getMessage(),
485+
'running PHPStan Pro worker',
486+
InternalError::prepareTrace($e),
487+
$e->getTraceAsString(),
488+
)],
484489
]]);
485490
throw $e;
486491
});

src/Command/FixerWorkerCommand.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,12 @@ function (array $errors, array $locallyIgnoredErrors, array $analysedFiles) use
237237
if ($hasInternalErrors) {
238238
$out->write(['action' => 'analysisCrash', 'data' => [
239239
'internalErrors' => count($finalizerResult->getAnalyserResult()->getInternalErrors()) > 0 ? $finalizerResult->getAnalyserResult()->getInternalErrors() : [
240-
new InternalError('Internal error occurred', []),
240+
new InternalError(
241+
'Internal error occurred',
242+
'running analyser in PHPStan Pro worker',
243+
[],
244+
null,
245+
),
241246
],
242247
]]);
243248
}

src/Command/WorkerCommand.php

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,12 @@ private function runWorker(
165165
'result' => [
166166
'errors' => [],
167167
'internalErrors' => [
168-
new InternalError($error->getMessage(), InternalError::prepareTrace($error)),
168+
new InternalError(
169+
$error->getMessage(),
170+
'communicating with main process in parallel worker',
171+
InternalError::prepareTrace($error),
172+
$error->getTraceAsString(),
173+
),
169174
],
170175
'filteredPhpErrors' => [],
171176
'allPhpErrors' => [],
@@ -186,7 +191,7 @@ private function runWorker(
186191
$fileAnalyser = $container->getByType(FileAnalyser::class);
187192
$ruleRegistry = $container->getByType(RuleRegistry::class);
188193
$collectorRegistry = $container->getByType(CollectorRegistry::class);
189-
$in->on('data', static function (array $json) use ($fileAnalyser, $ruleRegistry, $collectorRegistry, $out, $analysedFiles, $output): void {
194+
$in->on('data', static function (array $json) use ($fileAnalyser, $ruleRegistry, $collectorRegistry, $out, $analysedFiles): void {
190195
$action = $json['action'];
191196
if ($action !== 'analyse') {
192197
return;
@@ -225,18 +230,12 @@ private function runWorker(
225230
}
226231
} catch (Throwable $t) {
227232
$internalErrorsCount++;
228-
$internalErrorMessage = sprintf('Internal error: %s while analysing file %s', $t->getMessage(), $file);
229-
230-
$bugReportUrl = 'https://github.com/phpstan/phpstan/issues/new?template=Bug_report.yaml';
231-
if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
232-
$trace = sprintf('## %s(%d)%s', $t->getFile(), $t->getLine(), "\n");
233-
$trace .= $t->getTraceAsString();
234-
$internalErrorMessage .= sprintf('%sPost the following stack trace to %s: %s%s', "\n\n", $bugReportUrl, "\n", $trace);
235-
} else {
236-
$internalErrorMessage .= sprintf('%sRun PHPStan with -v option and post the stack trace to:%s%s', "\n", "\n", $bugReportUrl);
237-
}
238-
239-
$internalErrors[] = new InternalError($internalErrorMessage, InternalError::prepareTrace($t));
233+
$internalErrors[] = new InternalError(
234+
$t->getMessage(),
235+
sprintf('analysing file %s', $file),
236+
InternalError::prepareTrace($t),
237+
$t->getTraceAsString(),
238+
);
240239
}
241240
}
242241

src/Parallel/ParallelAnalyser.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,12 @@ public function analyse(
9090
$server = new TcpServer('127.0.0.1:0', $loop);
9191
$this->processPool = new ProcessPool($server, static function () use ($deferred, &$jobs, &$internalErrors, &$internalErrorsCount, &$reachedInternalErrorsCountLimit, &$errors, &$filteredPhpErrors, &$allPhpErrors, &$locallyIgnoredErrors, &$linesToIgnore, &$unmatchedLineIgnores, &$collectedData, &$dependencies, &$exportedNodes, &$peakMemoryUsages): void {
9292
if (count($jobs) > 0 && $internalErrorsCount === 0) {
93-
$internalErrors[] = new InternalError('Some parallel worker jobs have not finished.', []);
93+
$internalErrors[] = new InternalError(
94+
'Some parallel worker jobs have not finished.',
95+
'running parallel worker',
96+
[],
97+
null,
98+
);
9499
$internalErrorsCount++;
95100
}
96101

@@ -139,7 +144,12 @@ public function analyse(
139144
$serverPort = parse_url($serverAddress, PHP_URL_PORT);
140145

141146
$handleError = function (Throwable $error) use (&$internalErrors, &$internalErrorsCount, &$reachedInternalErrorsCountLimit): void {
142-
$internalErrors[] = new InternalError($error->getMessage(), InternalError::prepareTrace($error));
147+
$internalErrors[] = new InternalError(
148+
$error->getMessage(),
149+
'communicating with parallel worker',
150+
InternalError::prepareTrace($error),
151+
$error->getTraceAsString(),
152+
);
143153
$internalErrorsCount++;
144154
$reachedInternalErrorsCountLimit = true;
145155
$this->processPool->quitAll();
@@ -289,12 +299,12 @@ public function analyse(
289299
$memoryLimitMessage,
290300
ini_get('memory_limit'),
291301
'Increase your memory limit in php.ini or run PHPStan with --memory-limit CLI option.',
292-
), []);
302+
), 'running parallel worker', [], null);
293303
$internalErrorsCount++;
294304
return;
295305
}
296306

297-
$internalErrors[] = new InternalError(sprintf('<error>Child process error</error> (exit code %d): %s', $exitCode, $output), []);
307+
$internalErrors[] = new InternalError(sprintf('<error>Child process error</error> (exit code %d): %s', $exitCode, $output), 'running parallel worker', [], null);
298308
$internalErrorsCount++;
299309
});
300310
$this->processPool->attachProcess($processIdentifier, $process);

0 commit comments

Comments
 (0)