|
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