|
2 | 2 |
|
3 | 3 | namespace PHPStan\Command;
|
4 | 4 |
|
5 |
| -use Nette\Utils\Strings; |
6 | 5 | use OndraM\CiDetector\CiDetector;
|
7 |
| -use PhpMerge\internal\Hunk; |
8 |
| -use PhpMerge\internal\Line; |
9 |
| -use PhpMerge\MergeConflict; |
10 |
| -use PhpMerge\PhpMerge; |
11 | 6 | use PHPStan\Analyser\InternalError;
|
12 | 7 | use PHPStan\Command\ErrorFormatter\BaselineNeonErrorFormatter;
|
13 | 8 | use PHPStan\Command\ErrorFormatter\BaselinePhpErrorFormatter;
|
|
24 | 19 | use PHPStan\File\ParentDirectoryRelativePathHelper;
|
25 | 20 | use PHPStan\File\PathNotFoundException;
|
26 | 21 | use PHPStan\File\RelativePathHelper;
|
| 22 | +use PHPStan\Fixable\FileChangedException; |
| 23 | +use PHPStan\Fixable\MergeConflictException; |
| 24 | +use PHPStan\Fixable\Patcher; |
27 | 25 | use PHPStan\Internal\BytesHelper;
|
28 | 26 | use PHPStan\Internal\DirectoryCreator;
|
29 | 27 | use PHPStan\Internal\DirectoryCreatorException;
|
30 | 28 | use PHPStan\ShouldNotHappenException;
|
31 |
| -use ReflectionClass; |
32 |
| -use SebastianBergmann\Diff\Differ; |
33 | 29 | use Symfony\Component\Console\Command\Command;
|
34 | 30 | use Symfony\Component\Console\Input\InputArgument;
|
35 | 31 | use Symfony\Component\Console\Input\InputInterface;
|
|
58 | 54 | use function is_string;
|
59 | 55 | use function pathinfo;
|
60 | 56 | use function rewind;
|
61 |
| -use function sha1; |
62 | 57 | use function sprintf;
|
63 | 58 | use function str_contains;
|
64 | 59 | use function stream_get_contents;
|
65 | 60 | use function strlen;
|
66 | 61 | use function substr;
|
67 | 62 | use const PATHINFO_BASENAME;
|
68 | 63 | use const PATHINFO_EXTENSION;
|
69 |
| -use const PREG_SPLIT_DELIM_CAPTURE; |
70 |
| -use const PREG_SPLIT_NO_EMPTY; |
71 | 64 |
|
72 | 65 | /**
|
73 | 66 | * @phpstan-import-type Trace from InternalError as InternalErrorTrace
|
@@ -524,79 +517,29 @@ protected function execute(InputInterface $input, OutputInterface $output): int
|
524 | 517 | $exitCode = 1;
|
525 | 518 | } else {
|
526 | 519 | $skippedCount = 0;
|
527 |
| - $fixableErrorsByFile = []; |
| 520 | + $diffsByFile = []; |
528 | 521 | foreach ($fixableErrors as $fixableError) {
|
529 | 522 | $fixFile = $fixableError->getFilePath();
|
530 | 523 | if ($fixableError->getTraitFilePath() !== null) {
|
531 | 524 | $fixFile = $fixableError->getTraitFilePath();
|
532 | 525 | }
|
533 | 526 |
|
534 |
| - $fixableErrorsByFile[$fixFile][] = $fixableError; |
535 |
| - } |
536 |
| - |
537 |
| - $differ = $container->getByType(Differ::class); |
538 |
| - |
539 |
| - foreach ($fixableErrorsByFile as $file => $fileFixableErrors) { |
540 |
| - $fileContents = FileReader::read($file); |
541 |
| - $fileHash = sha1($fileContents); |
542 |
| - $diffHunks = []; |
543 |
| - foreach ($fileFixableErrors as $fileFixableError) { |
544 |
| - $diff = $fileFixableError->getFixedErrorDiff(); |
545 |
| - if ($diff === null) { |
546 |
| - throw new ShouldNotHappenException(); |
547 |
| - } |
548 |
| - if ($diff->originalHash !== $fileHash) { |
549 |
| - $skippedCount++; |
550 |
| - continue; |
551 |
| - } |
552 |
| - |
553 |
| - $diffHunks[] = Hunk::createArray(Line::createArray($diff->diff)); |
554 |
| - } |
555 |
| - |
556 |
| - if (count($diffHunks) === 0) { |
557 |
| - continue; |
| 527 | + if ($fixableError->getFixedErrorDiff() === null) { |
| 528 | + throw new ShouldNotHappenException(); |
558 | 529 | }
|
559 | 530 |
|
560 |
| - $baseLines = Line::createArray(array_map( |
561 |
| - static fn ($l) => [$l, Differ::OLD], |
562 |
| - self::splitStringByLines($fileContents), |
563 |
| - )); |
564 |
| - |
565 |
| - $refMerge = new ReflectionClass(PhpMerge::class); |
566 |
| - $refMergeMethod = $refMerge->getMethod('mergeHunks'); |
567 |
| - $refMergeMethod->setAccessible(true); |
568 |
| - |
569 |
| - $result = Line::createArray(array_map( |
570 |
| - static fn ($l) => [$l, Differ::OLD], |
571 |
| - $refMergeMethod->invokeArgs(null, [ |
572 |
| - $baseLines, |
573 |
| - $diffHunks[0], |
574 |
| - [], |
575 |
| - ]), |
576 |
| - )); |
577 |
| - |
578 |
| - for ($i = 0; $i < count($diffHunks); $i++) { |
579 |
| - /** @var MergeConflict[] $conflicts */ |
580 |
| - $conflicts = []; |
581 |
| - $merged = $refMergeMethod->invokeArgs(null, [ |
582 |
| - $baseLines, |
583 |
| - Hunk::createArray(Line::createArray($differ->diffToArray($fileContents, implode('', array_map(static fn ($l) => $l->getContent(), $result))))), |
584 |
| - $diffHunks[$i], |
585 |
| - &$conflicts, |
586 |
| - ]); |
587 |
| - if (count($conflicts) > 0) { |
588 |
| - $skippedCount += count($diffHunks); |
589 |
| - continue 2; |
590 |
| - } |
591 |
| - |
592 |
| - $result = Line::createArray(array_map( |
593 |
| - static fn ($l) => [$l, Differ::OLD], |
594 |
| - $merged, |
595 |
| - )); |
| 531 | + $diffsByFile[$fixFile][] = $fixableError->getFixedErrorDiff(); |
| 532 | + } |
596 | 533 |
|
| 534 | + $patcher = $container->getByType(Patcher::class); |
| 535 | + foreach ($diffsByFile as $file => $diffs) { |
| 536 | + try { |
| 537 | + $finalFileContents = $patcher->applyDiffs($file, $diffs); |
| 538 | + } catch (FileChangedException | MergeConflictException) { |
| 539 | + $skippedCount += count($diffs); |
| 540 | + continue; |
597 | 541 | }
|
598 | 542 |
|
599 |
| - $finalFileContents = implode('', array_map(static fn ($l) => $l->getContent(), $result)); |
600 | 543 | FileWriter::write($file, $finalFileContents);
|
601 | 544 | }
|
602 | 545 |
|
@@ -679,14 +622,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
|
679 | 622 | );
|
680 | 623 | }
|
681 | 624 |
|
682 |
| - /** |
683 |
| - * @return string[] |
684 |
| - */ |
685 |
| - private static function splitStringByLines(string $input): array |
686 |
| - { |
687 |
| - return Strings::split($input, '/(.*\R)/', PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); |
688 |
| - } |
689 |
| - |
690 | 625 | private function createStreamOutput(): StreamOutput
|
691 | 626 | {
|
692 | 627 | $resource = fopen('php://memory', 'w', false);
|
|
0 commit comments