Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

require-extends should not error on interfaces #2861

Merged
merged 6 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 8 additions & 4 deletions src/Rules/Classes/RequireExtendsRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,20 @@ public function processNode(Node $node, Scope $scope): array
{
$classReflection = $node->getClassReflection();

if ($classReflection->isInterface()) {
return [];
}

$errors = [];
foreach ($classReflection->getImmediateInterfaces() as $interface) {
foreach ($classReflection->getInterfaces() as $interface) {
$extendsTags = $interface->getRequireExtendsTags();
foreach ($extendsTags as $extendsTag) {
$type = $extendsTag->getType();
if (!$type instanceof ObjectType) {
continue;
}

if ($classReflection->isSubclassOf($type->getClassName())) {
if ($classReflection->is($type->getClassName())) {
continue;
}

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

foreach ($classReflection->getTraits() as $trait) {
foreach ($classReflection->getTraits(true) as $trait) {
$extendsTags = $trait->getRequireExtendsTags();
foreach ($extendsTags as $extendsTag) {
$type = $extendsTag->getType();
if (!$type instanceof ObjectType) {
continue;
}

if ($classReflection->isSubclassOf($type->getClassName())) {
if ($classReflection->is($type->getClassName())) {
continue;
}

Expand Down
2 changes: 1 addition & 1 deletion src/Rules/Classes/RequireImplementsRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function processNode(Node $node, Scope $scope): array
$classReflection = $node->getClassReflection();

$errors = [];
foreach ($classReflection->getTraits() as $trait) {
foreach ($classReflection->getTraits(true) as $trait) {
$implementsTags = $trait->getRequireImplementsTags();
foreach ($implementsTags as $implementsTag) {
$type = $implementsTag->getType();
Expand Down
20 changes: 20 additions & 0 deletions tests/PHPStan/Rules/Classes/RequireExtendsRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,24 @@ public function testRule(): void
$this->analyse([__DIR__ . '/../PhpDoc/data/incompatible-require-extends.php'], $expectedErrors);
}

public function testExtendedInterfaceBug(): void
{
$this->analyse([__DIR__ . '/data/bug-10302-extended-interface.php'], [
[
'Interface Bug10302ExtendedInterface\BatchAware requires implementing class to extend Bug10302ExtendedInterface\Model, but Bug10302ExtendedInterface\AnotherModel does not.',
34,
],
]);
}

public function testExtendedTraitBug(): void
{
$this->analyse([__DIR__ . '/data/bug-10302-extended-trait.php'], [
[
'Trait Bug10302ExtendedTrait\Foo requires using class to extend Bug10302ExtendedTrait\Father, but Bug10302ExtendedTrait\Baz does not.',
21,
],
]);
}

}
10 changes: 10 additions & 0 deletions tests/PHPStan/Rules/Classes/RequireImplementsRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,14 @@ public function testRule(): void
$this->analyse([__DIR__ . '/../PhpDoc/data/incompatible-require-implements.php'], $expectedErrors);
}

public function testExtendedTraitBug(): void
{
$this->analyse([__DIR__ . '/data/bug-10302-extended-implements-trait.php'], [
[
'Trait Bug10302ExtendedImplementsTrait\Foo requires using class to implement Bug10302ExtendedImplementsTrait\Interface1, but Bug10302ExtendedImplementsTrait\Baz does not.',
21,
],
]);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace Bug10302ExtendedImplementsTrait;

interface Interface1
{

}

/** @phpstan-require-implements Interface1 */
trait Foo
{

}

trait Bar
{
use Foo;
}

class Baz
{

use Bar;

}

class Baz2 implements Interface1
{

use Bar;

}
50 changes: 50 additions & 0 deletions tests/PHPStan/Rules/Classes/data/bug-10302-extended-interface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace Bug10302ExtendedInterface;

use function PHPStan\Testing\assertType;

/**
* @property-read bool $busy
* @phpstan-require-extends Model
*/
interface BatchAware
{
}

interface BatchAwareExtended extends BatchAware
{

}

/**
* @property-read bool $busy2
*/
class Model implements BatchAware
{
/**
* @return mixed
*/
public function __get(string $name)
{
return 1;
}
}

class AnotherModel implements BatchAwareExtended
{

}


/**
* @property-read bool $busy
* @phpstan-require-extends TraitModel
*/
trait BatchAwareTrait
{
}

class TraitModel {
use BatchAwareTrait;
}
34 changes: 34 additions & 0 deletions tests/PHPStan/Rules/Classes/data/bug-10302-extended-trait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace Bug10302ExtendedTrait;

class Father
{

}

/** @phpstan-require-extends Father */
trait Foo
{

}

trait Bar
{
use Foo;
}

class Baz // should report: Trait Foo requires using class to extend Father, but Baz does not.
{

use Bar;

}


class Baz2 extends Father
{

use Bar;

}