Skip to content

Commit

Permalink
Apply psalm-inheritors to interfaces too
Browse files Browse the repository at this point in the history
  • Loading branch information
robchett authored and weirdan committed Aug 31, 2023
1 parent e23971c commit d0c4d17
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 13 deletions.
21 changes: 21 additions & 0 deletions src/Psalm/Internal/Analyzer/InterfaceAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@
use Psalm\Internal\Analyzer\Statements\Expression\ClassConstAnalyzer;
use Psalm\Internal\FileManipulation\FileManipulationBuffer;
use Psalm\Internal\Provider\NodeDataProvider;
use Psalm\Internal\Type\Comparator\UnionTypeComparator;
use Psalm\Issue\InheritorViolation;
use Psalm\Issue\ParseError;
use Psalm\Issue\UndefinedInterface;
use Psalm\IssueBuffer;
use Psalm\Type\Atomic\TNamedObject;
use Psalm\Type\Union;
use UnexpectedValueException;

use function strtolower;
Expand Down Expand Up @@ -109,6 +113,23 @@ public function analyze(): void
}
}

$class_union = new Union([new TNamedObject($fq_interface_name)]);
foreach ($class_storage->direct_interface_parents as $parent_interface) {
$parent_storage = $codebase->classlikes->getStorageFor($parent_interface);
if ($parent_storage && $parent_storage->inheritors) {
if (!UnionTypeComparator::isContainedBy($codebase, $class_union, $parent_storage->inheritors)) {
IssueBuffer::maybeAdd(
new InheritorViolation(
'Interface ' . $fq_interface_name . '
is not an allowed inheritor of parent interface ' . $parent_interface,
new CodeLocation($this, $this->class),
),
$this->getSuppressedIssues(),
);
}
}
}

$fq_interface_name = $this->getFQCLN();

if (!$fq_interface_name) {
Expand Down
37 changes: 24 additions & 13 deletions tests/ClassTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -857,7 +857,6 @@ private final function __construct() {}
*/
class BaseClass {}
class FooClass extends BaseClass {}
$a = new FooClass();
PHP,
],
'unionInheritorIsAllowed' => [
Expand All @@ -868,9 +867,7 @@ class FooClass extends BaseClass {}
*/
class BaseClass {}
class FooClass extends BaseClass {}
$a = new FooClass();
class BarClass extends FooClass {}
$b = new BarClass();
PHP,
],
'multiInheritorIsAllowed' => [
Expand All @@ -881,9 +878,7 @@ class BarClass extends FooClass {}
*/
class BaseClass {}
class FooClass extends BaseClass {}
$a = new FooClass();
class BarClass extends FooClass {}
$b = new BarClass();
PHP,
],
'skippedInheritorIsAllowed' => [
Expand All @@ -894,9 +889,7 @@ class BarClass extends FooClass {}
*/
class BaseClass {}
class FooClass extends BaseClass {}
$a = new FooClass();
class BarClass extends FooClass {}
$b = new BarClass();
PHP,
],
'CompositeInheritorIsAllowed' => [
Expand All @@ -908,7 +901,6 @@ class BarClass extends FooClass {}
class BaseClass {}
interface FooInterface {}
class BarClass extends BaseClass implements FooInterface {}
$b = new BarClass();
PHP,
],
'InterfaceInheritorIsAllowed' => [
Expand All @@ -919,12 +911,10 @@ class BarClass extends BaseClass implements FooInterface {}
*/
interface BaseInterface {}
class FooClass implements BaseInterface {}
$a = new FooClass();
class BarClass implements BaseInterface {}
$b = new BarClass();
PHP,
],
'MultiInterfaceInheritorIsAllowed' => [
],
'MultiInterfaceInheritorIsAllowed' => [
'code' => <<<'PHP'
<?php
/**
Expand All @@ -936,7 +926,16 @@ interface InterfaceA {}
*/
interface InterfaceB {}
class FooClass implements InterfaceA, InterfaceB {}
$a = new FooClass();
PHP,
],
'InterfaceOfInterfaceInheritorIsAllowed' => [
'code' => <<<'PHP'
<?php
/**
* @psalm-inheritors InterfaceB
*/
interface InterfaceA {}
interface InterfaceB extends InterfaceA {}
PHP,
],
];
Expand Down Expand Up @@ -1409,6 +1408,18 @@ class BazClass implements BaseInterface {}
'error_message' => 'InheritorViolation',
'ignored_issues' => [],
],
'interfaceCannotImplementIfNotInInheritors' => [
'code' => <<<'PHP'
<?php
/**
* @psalm-inheritors FooClass|BarClass
*/
interface BaseInterface {}
interface BazInterface extends BaseInterface {}
PHP,
'error_message' => 'InheritorViolation',
'ignored_issues' => [],
],
'UnfulfilledInterfaceInheritors' => [
'code' => <<<'PHP'
<?php
Expand Down

0 comments on commit d0c4d17

Please sign in to comment.