Skip to content

Commit e10458b

Browse files
committed
FinalClassRule now fixable
1 parent 8f4c135 commit e10458b

File tree

4 files changed

+116
-7
lines changed

4 files changed

+116
-7
lines changed

build/PHPStan/Build/FinalClassRule.php

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PHPStan\Build;
44

5+
use PhpParser\Modifiers;
56
use PhpParser\Node;
67
use PHPStan\Analyser\Scope;
78
use PHPStan\File\FileHelper;
@@ -23,7 +24,7 @@
2324
final class FinalClassRule implements Rule
2425
{
2526

26-
public function __construct(private FileHelper $fileHelper)
27+
public function __construct(private FileHelper $fileHelper, private bool $skipTests = true)
2728
{
2829
}
2930

@@ -58,16 +59,25 @@ public function processNode(Node $node, Scope $scope): array
5859
return [];
5960
}
6061

61-
if (str_starts_with($this->fileHelper->normalizePath($scope->getFile()), $this->fileHelper->normalizePath(dirname(__DIR__, 3) . '/tests'))) {
62+
if ($this->skipTests && str_starts_with($this->fileHelper->normalizePath($scope->getFile()), $this->fileHelper->normalizePath(dirname(__DIR__, 3) . '/tests'))) {
6263
return [];
6364
}
6465

66+
$errorBuilder = RuleErrorBuilder::message(
67+
sprintf('Class %s must be abstract or final.', $classReflection->getDisplayName()),
68+
)->identifier('phpstan.finalClass');
69+
70+
$originalNode = $node->getOriginalNode();
71+
if ($originalNode instanceof Node\Stmt\Class_ && $originalNode->name !== null) {
72+
$errorBuilder->fixNode($originalNode, static function ($classNode) {
73+
$classNode->flags |= Modifiers::FINAL;
74+
75+
return $classNode;
76+
});
77+
}
78+
6579
return [
66-
RuleErrorBuilder::message(
67-
sprintf('Class %s must be abstract or final.', $classReflection->getDisplayName()),
68-
)
69-
->identifier('phpstan.finalClass')
70-
->build(),
80+
$errorBuilder->build(),
7181
];
7282
}
7383

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Build;
4+
5+
use PHPStan\File\FileHelper;
6+
use PHPStan\Rules\Rule;
7+
use PHPStan\Testing\RuleTestCase;
8+
9+
/**
10+
* @extends RuleTestCase<FinalClassRule>
11+
*/
12+
class FinalClassRuleTest extends RuleTestCase
13+
{
14+
15+
protected function getRule(): Rule
16+
{
17+
return new FinalClassRule(self::getContainer()->getByType(FileHelper::class), skipTests: false);
18+
}
19+
20+
public function testRule(): void
21+
{
22+
$this->analyse([__DIR__ . '/data/final-class-rule.php'], [
23+
[
24+
'Class FinalClassRule\Baz must be abstract or final.',
25+
29,
26+
],
27+
]);
28+
}
29+
30+
public function testFix(): void
31+
{
32+
$this->fix(__DIR__ . '/data/final-class-rule.php', __DIR__ . '/data/final-class-rule.php.fixed');
33+
}
34+
35+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace FinalClassRule;
4+
5+
interface Foo
6+
{
7+
8+
}
9+
10+
trait Bar
11+
{
12+
13+
}
14+
15+
$c = new class () {
16+
17+
};
18+
19+
final class FinalFoo
20+
{
21+
22+
}
23+
24+
abstract class Lorem
25+
{
26+
27+
}
28+
29+
class Baz
30+
{
31+
32+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace FinalClassRule;
4+
5+
interface Foo
6+
{
7+
8+
}
9+
10+
trait Bar
11+
{
12+
13+
}
14+
15+
$c = new class () {
16+
17+
};
18+
19+
final class FinalFoo
20+
{
21+
22+
}
23+
24+
abstract class Lorem
25+
{
26+
27+
}
28+
29+
final class Baz
30+
{
31+
32+
}

0 commit comments

Comments
 (0)