Skip to content

Commit be1a850

Browse files
committed
Apply thousands of fixes in time linear in their number
Patcher copied the accepted runs of a file for each group of diffs: the runs looked up for the group still referenced them when array_splice() inserted the group's runs, and array_splice() builds a new array even to append one at the end, which is where the runs of diffs coming in the order of their lines go. Removing all 50,000 entries of a baseline with --fix spent 15.3 s in Patcher::apply() and now 0.3 s, the whole --fix of the project took 41.9 s and now 17.5 s. Amp-Thread-ID: https://ampcode.com/threads/T-01a1a198-fa57-7583-9394-63372a7e2fa8
1 parent 48f825b commit be1a850

2 files changed

Lines changed: 28 additions & 0 deletions

File tree

‎src/Fixable/Patcher.php‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,20 @@ public function apply(array $contentsByFile, array $diffGroups): array
7171

7272
$insertionsByFile[$diff->file] = $insertions;
7373
}
74+
// a reference to the runs would make the insertions below copy them all, for each group
75+
unset($fileRuns);
7476

7577
foreach ($insertionsByFile as $file => $insertions) {
7678
$acceptedRuns[$file] ??= [];
7779

7880
// runs of a single diff are ordered and non-overlapping,
7981
// inserting from the last one keeps the earlier indexes valid
8082
for ($i = count($insertions) - 1; $i >= 0; $i--) {
83+
if ($insertions[$i][0] === count($acceptedRuns[$file])) {
84+
// the diffs usually come in the order of their lines, array_splice() would copy the runs to append one
85+
$acceptedRuns[$file][] = $insertions[$i][1];
86+
continue;
87+
}
8188
array_splice($acceptedRuns[$file], $insertions[$i][0], 0, [$insertions[$i][1]]);
8289
}
8390
}

‎tests/PHPStan/Fixable/PatcherTest.php‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,25 @@ public function testGroupIsSkippedInAllFilesWhenItConflictsInOne(): void
3636
$this->assertSame(2, $result['skippedCount']);
3737
}
3838

39+
public function testRunsAreSplicedInTheOrderOfTheirLines(): void
40+
{
41+
$code = "1\n2\n3\n4\n5\n6\n";
42+
$group = static fn (string $diff): array => [new FixedErrorDiff('a.php', hash('sha256', $code), $diff)];
43+
44+
$result = (new Patcher())->apply(['a.php' => $code], [
45+
$group("@@ -4,1 +4,1 @@\n-4\n+D\n"),
46+
// before the run of an earlier error
47+
$group("@@ -2,1 +2,1 @@\n-2\n+B\n"),
48+
// one run before the accepted ones and one after them
49+
$group("@@ -1,1 +1,1 @@\n-1\n+A\n@@ -6,1 +6,1 @@\n-6\n+F\n"),
50+
// between them
51+
$group("@@ -5,1 +5,1 @@\n-5\n+E\n"),
52+
// the same change as another error
53+
$group("@@ -4,1 +4,1 @@\n-4\n+D\n"),
54+
]);
55+
56+
$this->assertSame(['a.php' => "A\nB\n3\nD\nE\nF\n"], $result['contents']);
57+
$this->assertSame(5, $result['appliedCount']);
58+
}
59+
3960
}

0 commit comments

Comments
 (0)