Skip to content

Commit 98a1037

Browse files
authored
require-extends should not error on interfaces
1 parent af1df58 commit 98a1037

File tree

7 files changed

+156
-5
lines changed

7 files changed

+156
-5
lines changed

src/Rules/Classes/RequireExtendsRule.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,20 @@ public function processNode(Node $node, Scope $scope): array
2626
{
2727
$classReflection = $node->getClassReflection();
2828

29+
if ($classReflection->isInterface()) {
30+
return [];
31+
}
32+
2933
$errors = [];
30-
foreach ($classReflection->getImmediateInterfaces() as $interface) {
34+
foreach ($classReflection->getInterfaces() as $interface) {
3135
$extendsTags = $interface->getRequireExtendsTags();
3236
foreach ($extendsTags as $extendsTag) {
3337
$type = $extendsTag->getType();
3438
if (!$type instanceof ObjectType) {
3539
continue;
3640
}
3741

38-
if ($classReflection->isSubclassOf($type->getClassName())) {
42+
if ($classReflection->is($type->getClassName())) {
3943
continue;
4044
}
4145

@@ -50,15 +54,15 @@ public function processNode(Node $node, Scope $scope): array
5054
}
5155
}
5256

53-
foreach ($classReflection->getTraits() as $trait) {
57+
foreach ($classReflection->getTraits(true) as $trait) {
5458
$extendsTags = $trait->getRequireExtendsTags();
5559
foreach ($extendsTags as $extendsTag) {
5660
$type = $extendsTag->getType();
5761
if (!$type instanceof ObjectType) {
5862
continue;
5963
}
6064

61-
if ($classReflection->isSubclassOf($type->getClassName())) {
65+
if ($classReflection->is($type->getClassName())) {
6266
continue;
6367
}
6468

src/Rules/Classes/RequireImplementsRule.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public function processNode(Node $node, Scope $scope): array
2727
$classReflection = $node->getClassReflection();
2828

2929
$errors = [];
30-
foreach ($classReflection->getTraits() as $trait) {
30+
foreach ($classReflection->getTraits(true) as $trait) {
3131
$implementsTags = $trait->getRequireImplementsTags();
3232
foreach ($implementsTags as $implementsTag) {
3333
$type = $implementsTag->getType();

tests/PHPStan/Rules/Classes/RequireExtendsRuleTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,24 @@ public function testRule(): void
5757
$this->analyse([__DIR__ . '/../PhpDoc/data/incompatible-require-extends.php'], $expectedErrors);
5858
}
5959

60+
public function testExtendedInterfaceBug(): void
61+
{
62+
$this->analyse([__DIR__ . '/data/bug-10302-extended-interface.php'], [
63+
[
64+
'Interface Bug10302ExtendedInterface\BatchAware requires implementing class to extend Bug10302ExtendedInterface\Model, but Bug10302ExtendedInterface\AnotherModel does not.',
65+
34,
66+
],
67+
]);
68+
}
69+
70+
public function testExtendedTraitBug(): void
71+
{
72+
$this->analyse([__DIR__ . '/data/bug-10302-extended-trait.php'], [
73+
[
74+
'Trait Bug10302ExtendedTrait\Foo requires using class to extend Bug10302ExtendedTrait\Father, but Bug10302ExtendedTrait\Baz does not.',
75+
21,
76+
],
77+
]);
78+
}
79+
6080
}

tests/PHPStan/Rules/Classes/RequireImplementsRuleTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,14 @@ public function testRule(): void
7373
$this->analyse([__DIR__ . '/../PhpDoc/data/incompatible-require-implements.php'], $expectedErrors);
7474
}
7575

76+
public function testExtendedTraitBug(): void
77+
{
78+
$this->analyse([__DIR__ . '/data/bug-10302-extended-implements-trait.php'], [
79+
[
80+
'Trait Bug10302ExtendedImplementsTrait\Foo requires using class to implement Bug10302ExtendedImplementsTrait\Interface1, but Bug10302ExtendedImplementsTrait\Baz does not.',
81+
21,
82+
],
83+
]);
84+
}
85+
7686
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace Bug10302ExtendedImplementsTrait;
4+
5+
interface Interface1
6+
{
7+
8+
}
9+
10+
/** @phpstan-require-implements Interface1 */
11+
trait Foo
12+
{
13+
14+
}
15+
16+
trait Bar
17+
{
18+
use Foo;
19+
}
20+
21+
class Baz
22+
{
23+
24+
use Bar;
25+
26+
}
27+
28+
class Baz2 implements Interface1
29+
{
30+
31+
use Bar;
32+
33+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
namespace Bug10302ExtendedInterface;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/**
8+
* @property-read bool $busy
9+
* @phpstan-require-extends Model
10+
*/
11+
interface BatchAware
12+
{
13+
}
14+
15+
interface BatchAwareExtended extends BatchAware
16+
{
17+
18+
}
19+
20+
/**
21+
* @property-read bool $busy2
22+
*/
23+
class Model implements BatchAware
24+
{
25+
/**
26+
* @return mixed
27+
*/
28+
public function __get(string $name)
29+
{
30+
return 1;
31+
}
32+
}
33+
34+
class AnotherModel implements BatchAwareExtended
35+
{
36+
37+
}
38+
39+
40+
/**
41+
* @property-read bool $busy
42+
* @phpstan-require-extends TraitModel
43+
*/
44+
trait BatchAwareTrait
45+
{
46+
}
47+
48+
class TraitModel {
49+
use BatchAwareTrait;
50+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace Bug10302ExtendedTrait;
4+
5+
class Father
6+
{
7+
8+
}
9+
10+
/** @phpstan-require-extends Father */
11+
trait Foo
12+
{
13+
14+
}
15+
16+
trait Bar
17+
{
18+
use Foo;
19+
}
20+
21+
class Baz // should report: Trait Foo requires using class to extend Father, but Baz does not.
22+
{
23+
24+
use Bar;
25+
26+
}
27+
28+
29+
class Baz2 extends Father
30+
{
31+
32+
use Bar;
33+
34+
}

0 commit comments

Comments
 (0)