Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit fea805d

Browse files
committed
Add ShouldCallParentMethodsRule
1 parent c6acfc9 commit fea805d

File tree

4 files changed

+200
-0
lines changed

4 files changed

+200
-0
lines changed

‎rules.neon‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ rules:
33
- PHPStan\Rules\PHPUnit\AssertSameNullExpectedRule
44
- PHPStan\Rules\PHPUnit\AssertSameWithCountRule
55
- PHPStan\Rules\PHPUnit\MockMethodCallRule
6+
- PHPStan\Rules\PHPUnit\ShouldCallParentMethodsRule
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\PHPUnit;
4+
5+
use PhpParser\Node;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Node\InClassMethodNode;
8+
use PHPStan\Rules\RuleErrorBuilder;
9+
use PHPUnit\Framework\TestCase;
10+
11+
/**
12+
* @implements \PHPStan\Rules\Rule<InClassMethodNode>
13+
*/
14+
class ShouldCallParentMethodsRule implements \PHPStan\Rules\Rule
15+
{
16+
17+
public function getNodeType(): string
18+
{
19+
return InClassMethodNode::class;
20+
}
21+
22+
public function processNode(Node $node, Scope $scope): array
23+
{
24+
/** @var InClassMethodNode $node */
25+
$node = $node;
26+
27+
if ($scope->getClassReflection() === null) {
28+
return [];
29+
}
30+
31+
$parentClass = $scope->getClassReflection()->getParentClass();
32+
33+
if ($parentClass === false) {
34+
return [];
35+
}
36+
37+
if ($parentClass->getName() === TestCase::class) {
38+
return [];
39+
}
40+
41+
if (!in_array(strtolower($node->getOriginalNode()->name->name), ['setup', 'teardown'], true)) {
42+
return [];
43+
}
44+
45+
$hasParentCall = $this->hasParentClassCall($node->getOriginalNode()->getStmts());
46+
47+
if (!$hasParentCall) {
48+
return [
49+
RuleErrorBuilder::message(
50+
sprintf('Missing call to parent::%s method.', $node->getOriginalNode()->name->name)
51+
)->build(),
52+
];
53+
}
54+
55+
return [];
56+
}
57+
58+
/**
59+
* @param Node\Stmt[]|null $stmts
60+
*
61+
* @return bool
62+
*/
63+
private function hasParentClassCall(?array $stmts): bool
64+
{
65+
if ($stmts === null) {
66+
return false;
67+
}
68+
69+
foreach ($stmts as $stmt) {
70+
if (! $stmt instanceof Node\Stmt\Expression) {
71+
continue;
72+
}
73+
74+
if (! $stmt->expr instanceof Node\Expr\StaticCall) {
75+
continue;
76+
}
77+
78+
if (! $stmt->expr->class instanceof Node\Name) {
79+
continue;
80+
}
81+
82+
$class = (string) $stmt->expr->class;
83+
84+
if (strtolower($class) !== 'parent') {
85+
continue;
86+
}
87+
88+
if (! $stmt->expr->name instanceof Node\Identifier) {
89+
continue;
90+
}
91+
92+
if (in_array(strtolower($stmt->expr->name->name), ['setup', 'teardown'], true)) {
93+
return true;
94+
}
95+
}
96+
97+
return false;
98+
}
99+
100+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\PHPUnit;
4+
5+
use PHPStan\Rules\Rule;
6+
7+
/**
8+
* @extends \PHPStan\Testing\RuleTestCase<ShouldCallParentMethodsRule>
9+
*/
10+
class ShouldCallParentMethodsRuleTest extends \PHPStan\Testing\RuleTestCase
11+
{
12+
13+
protected function getRule(): Rule
14+
{
15+
return new ShouldCallParentMethodsRule();
16+
}
17+
18+
public function testRule(): void
19+
{
20+
$this->analyse([__DIR__ . '/data/missing-parent-method-calls.php'], [
21+
[
22+
'Missing call to parent::setUp method.',
23+
32,
24+
],
25+
[
26+
'Missing call to parent::tearDown method.',
27+
63,
28+
],
29+
]);
30+
}
31+
32+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
namespace MissingParentMethodCalls;
4+
5+
use PHPUnit\Framework\TestCase;
6+
7+
class FooTest extends TestCase
8+
{
9+
public function setUp(): void
10+
{
11+
$this->foo = true;
12+
}
13+
}
14+
15+
class BaseTestCase extends TestCase
16+
{
17+
public function setUp(): void
18+
{
19+
$this->bar = true;
20+
}
21+
22+
public function tearDown(): void
23+
{
24+
$this->bar = null;
25+
}
26+
}
27+
28+
class BazTest extends BaseTestCase
29+
{
30+
private $baz;
31+
32+
public function setUp(): void
33+
{
34+
$this->baz = true;
35+
}
36+
37+
public function baz(): bool
38+
{
39+
return $this->baz;
40+
}
41+
}
42+
43+
class BarBazTest extends BaseTestCase
44+
{
45+
public function setUp(): void
46+
{
47+
parent::setUp();
48+
49+
$this->barBaz = true;
50+
}
51+
}
52+
53+
class FooBarBazTest extends BaseTestCase
54+
{
55+
public function setUp(): void
56+
{
57+
$result = 1 + 1;
58+
parent::setUp();
59+
60+
$this->fooBarBaz = $result;
61+
}
62+
63+
public function tearDown(): void
64+
{
65+
$this->fooBarBaz = null;
66+
}
67+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /