Skip to content

Commit 5f1db78

Browse files
committed
Per-file batch fixing for --fix
1 parent 7604335 commit 5f1db78

10 files changed

Lines changed: 767 additions & 59 deletions

src/Analyser/Error.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,27 @@ public function getFixedErrorDiff(): ?FixedErrorDiff
261261
return $this->fixedErrorDiff;
262262
}
263263

264+
/**
265+
* @internal Experimental
266+
*/
267+
public function withFixedErrorDiff(FixedErrorDiff $fixedErrorDiff): self
268+
{
269+
return new self(
270+
$this->message,
271+
$this->file,
272+
$this->line,
273+
$this->canBeIgnored,
274+
$this->filePath,
275+
$this->traitFilePath,
276+
$this->tip,
277+
$this->nodeLine,
278+
$this->nodeType,
279+
$this->identifier,
280+
$this->metadata,
281+
$fixedErrorDiff,
282+
);
283+
}
284+
264285
/**
265286
* @return mixed
266287
*/

src/Analyser/FileAnalyserCallback.php

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use PHPStan\Rules\Registry as RuleRegistry;
1818
use function array_keys;
1919
use function get_class;
20+
use function spl_object_id;
2021
use function sprintf;
2122

2223
/**
@@ -45,6 +46,9 @@ final class FileAnalyserCallback
4546
/** @var list<Error> */
4647
private array $temporaryFileErrors = [];
4748

49+
/** @var array<string, list<PendingFix>> */
50+
private array $pendingFixesByFile = [];
51+
4852
/** @var LinesToIgnore */
4953
private array $linesToIgnore;
5054

@@ -77,8 +81,6 @@ public function __construct(
7781

7882
public function __invoke(Node $node, Scope $scope): void
7983
{
80-
$parserNodes = $this->parserNodes;
81-
8284
/** @var Scope&NodeCallbackInvoker $scope */
8385
if ($node instanceof Node\Stmt\Trait_) {
8486
foreach (array_keys($this->linesToIgnore[$this->file] ?? []) as $lineToIgnore) {
@@ -101,14 +103,6 @@ public function __invoke(Node $node, Scope $scope): void
101103
}
102104
}
103105

104-
if ($scope->isInTrait()) {
105-
$traitReflection = $scope->getTraitReflection();
106-
if ($traitReflection->getFileName() !== null) {
107-
$traitFilePath = $traitReflection->getFileName();
108-
$parserNodes = $this->parser->parseFile($traitFilePath);
109-
}
110-
}
111-
112106
if ($this->outerNodeCallback !== null) {
113107
($this->outerNodeCallback)($node, $scope);
114108
}
@@ -149,7 +143,7 @@ public function __invoke(Node $node, Scope $scope): void
149143
}
150144

151145
foreach ($ruleErrors as $ruleError) {
152-
$error = $this->ruleErrorTransformer->transform($ruleError, $scope, $parserNodes, $node);
146+
[$error, $pendingFix] = $this->ruleErrorTransformer->transformPreserveFixable($ruleError, $scope, $node);
153147

154148
if ($error->canBeIgnored()) {
155149
foreach ($this->ignoreErrorExtensions as $ignoreErrorExtension) {
@@ -160,6 +154,11 @@ public function __invoke(Node $node, Scope $scope): void
160154
}
161155

162156
$this->temporaryFileErrors[] = $error;
157+
if ($pendingFix === null) {
158+
continue;
159+
}
160+
161+
$this->pendingFixesByFile[$pendingFix->fixingFilePath][] = $pendingFix;
163162
}
164163
}
165164

@@ -305,7 +304,34 @@ public function getUnmatchedLineIgnores(): array
305304
*/
306305
public function getTemporaryFileErrors(): array
307306
{
308-
return $this->temporaryFileErrors;
307+
if ($this->pendingFixesByFile === []) {
308+
return $this->temporaryFileErrors;
309+
}
310+
311+
$parserNodesByFile = [$this->file => $this->parserNodes];
312+
foreach (array_keys($this->pendingFixesByFile) as $fixingFilePath) {
313+
if (isset($parserNodesByFile[$fixingFilePath])) {
314+
continue;
315+
}
316+
$parserNodesByFile[$fixingFilePath] = $this->parser->parseFile($fixingFilePath);
317+
}
318+
319+
$diffsByErrorId = $this->ruleErrorTransformer->finalizePendingFixes(
320+
$this->pendingFixesByFile,
321+
$parserNodesByFile,
322+
);
323+
324+
if ($diffsByErrorId === []) {
325+
return $this->temporaryFileErrors;
326+
}
327+
328+
$finalErrors = [];
329+
foreach ($this->temporaryFileErrors as $error) {
330+
$diff = $diffsByErrorId[spl_object_id($error)] ?? null;
331+
$finalErrors[] = $diff !== null ? $error->withFixedErrorDiff($diff) : $error;
332+
}
333+
334+
return $finalErrors;
309335
}
310336

311337
/**

src/Analyser/PendingFix.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser;
4+
5+
use Closure;
6+
use PhpParser\Node;
7+
8+
/**
9+
* @internal
10+
*/
11+
final class PendingFix
12+
{
13+
14+
/**
15+
* @param Closure(Node): Node $newNodeCallable
16+
*/
17+
public function __construct(
18+
public readonly Error $error,
19+
public readonly Node $originalNode,
20+
public readonly Closure $newNodeCallable,
21+
public readonly string $fixingFilePath,
22+
)
23+
{
24+
}
25+
26+
}

0 commit comments

Comments
 (0)