Skip to content

Commit 487f2fa

Browse files
committed
Check abstract method in non-abstract class
1 parent ada27eb commit 487f2fa

File tree

4 files changed

+118
-0
lines changed

4 files changed

+118
-0
lines changed

conf/config.level0.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ rules:
2828
- PHPStan\Rules\Functions\ExistingClassesInTypehintsRule
2929
- PHPStan\Rules\Functions\InnerFunctionRule
3030
- PHPStan\Rules\Functions\PrintfParametersRule
31+
- PHPStan\Rules\Methods\AbstractMethodInNonAbstractClassRule
3132
- PHPStan\Rules\Methods\ExistingClassesInTypehintsRule
3233
- PHPStan\Rules\Methods\OverridingMethodRule
3334
- PHPStan\Rules\Properties\AccessPropertiesInAssignRule
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Methods;
4+
5+
use PhpParser\Node;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Rules\Rule;
8+
use PHPStan\Rules\RuleErrorBuilder;
9+
10+
/**
11+
* @implements Rule<Node\Stmt\ClassMethod>
12+
*/
13+
class AbstractMethodInNonAbstractClassRule implements Rule
14+
{
15+
16+
public function getNodeType(): string
17+
{
18+
return Node\Stmt\ClassMethod::class;
19+
}
20+
21+
public function processNode(Node $node, Scope $scope): array
22+
{
23+
if (!$scope->isInClass()) {
24+
throw new \PHPStan\ShouldNotHappenException();
25+
}
26+
27+
$class = $scope->getClassReflection();
28+
if ($class->isAbstract()) {
29+
return [];
30+
}
31+
32+
if (!$node->isAbstract()) {
33+
return [];
34+
}
35+
36+
return [
37+
RuleErrorBuilder::message(sprintf('Non-abstract class %s contains abstract method %s().', $class->getName(), $node->name->toString()))->nonIgnorable()->build(),
38+
];
39+
}
40+
41+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Methods;
4+
5+
use PHPStan\Rules\Rule;
6+
use PHPStan\Testing\RuleTestCase;
7+
8+
/**
9+
* @extends RuleTestCase<AbstractMethodInNonAbstractClassRule>
10+
*/
11+
class AbstractMethodInNonAbstractClassRuleTest extends RuleTestCase
12+
{
13+
14+
protected function getRule(): Rule
15+
{
16+
return new AbstractMethodInNonAbstractClassRule();
17+
}
18+
19+
public function testRule(): void
20+
{
21+
if (!self::$useStaticReflectionProvider) {
22+
$this->markTestSkipped('Test requires static reflection.');
23+
}
24+
$this->analyse([__DIR__ . '/data/abstract-method.php'], [
25+
[
26+
'Non-abstract class AbstractMethod\Bar contains abstract method doBar().',
27+
15,
28+
],
29+
[
30+
'Non-abstract class AbstractMethod\Baz contains abstract method doBar().',
31+
22,
32+
],
33+
]);
34+
}
35+
36+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
namespace AbstractMethod;
4+
5+
abstract class Foo
6+
{
7+
8+
abstract public function doFoo(): void;
9+
10+
}
11+
12+
class Bar
13+
{
14+
15+
abstract public function doBar(): void;
16+
17+
}
18+
19+
interface Baz
20+
{
21+
22+
abstract public function doBar(): void;
23+
24+
}
25+
26+
trait X
27+
{
28+
29+
abstract public static function a(self $b): void;
30+
31+
}
32+
33+
class Y
34+
{
35+
36+
use X;
37+
38+
public static function a(self $b): void {}
39+
40+
}

0 commit comments

Comments
 (0)