mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Forbid implementing some interfaces
- `Throwable` can only be implemented when classes extend one of `Exception` or `Error` - `UnitEnum` and `BackedEnum` cannot be implemented by user-defined classes Refs vimeo/psalm#7722
This commit is contained in:
parent
0fb0714141
commit
085e8f6fb2
@ -272,6 +272,7 @@
|
|||||||
<xs:element name="InvalidFalsableReturnType" type="IssueHandlerType" minOccurs="0" />
|
<xs:element name="InvalidFalsableReturnType" type="IssueHandlerType" minOccurs="0" />
|
||||||
<xs:element name="InvalidFunctionCall" type="IssueHandlerType" minOccurs="0" />
|
<xs:element name="InvalidFunctionCall" type="IssueHandlerType" minOccurs="0" />
|
||||||
<xs:element name="InvalidGlobal" type="IssueHandlerType" minOccurs="0" />
|
<xs:element name="InvalidGlobal" type="IssueHandlerType" minOccurs="0" />
|
||||||
|
<xs:element name="InvalidInterfaceImplementation" type="ClassIssueHandlerType" minOccurs="0" />
|
||||||
<xs:element name="InvalidIterator" type="IssueHandlerType" minOccurs="0" />
|
<xs:element name="InvalidIterator" type="IssueHandlerType" minOccurs="0" />
|
||||||
<xs:element name="InvalidLiteralArgument" type="ArgumentIssueHandlerType" minOccurs="0" />
|
<xs:element name="InvalidLiteralArgument" type="ArgumentIssueHandlerType" minOccurs="0" />
|
||||||
<xs:element name="InvalidMethodCall" type="IssueHandlerType" minOccurs="0" />
|
<xs:element name="InvalidMethodCall" type="IssueHandlerType" minOccurs="0" />
|
||||||
|
@ -48,6 +48,7 @@ Level 5 and above allows a more non-verifiable code, and higher levels are even
|
|||||||
- [InvalidEnumMethod](issues/InvalidEnumMethod.md)
|
- [InvalidEnumMethod](issues/InvalidEnumMethod.md)
|
||||||
- [InvalidExtendClass](issues/InvalidExtendClass.md)
|
- [InvalidExtendClass](issues/InvalidExtendClass.md)
|
||||||
- [InvalidGlobal](issues/InvalidGlobal.md)
|
- [InvalidGlobal](issues/InvalidGlobal.md)
|
||||||
|
- [InvalidInterfaceImplementation](issues/InvalidInterfaceImplementation.md)
|
||||||
- [InvalidParamDefault](issues/InvalidParamDefault.md)
|
- [InvalidParamDefault](issues/InvalidParamDefault.md)
|
||||||
- [InvalidParent](issues/InvalidParent.md)
|
- [InvalidParent](issues/InvalidParent.md)
|
||||||
- [InvalidPassByReference](issues/InvalidPassByReference.md)
|
- [InvalidPassByReference](issues/InvalidPassByReference.md)
|
||||||
|
@ -75,6 +75,7 @@
|
|||||||
- [InvalidFalsableReturnType](issues/InvalidFalsableReturnType.md)
|
- [InvalidFalsableReturnType](issues/InvalidFalsableReturnType.md)
|
||||||
- [InvalidFunctionCall](issues/InvalidFunctionCall.md)
|
- [InvalidFunctionCall](issues/InvalidFunctionCall.md)
|
||||||
- [InvalidGlobal](issues/InvalidGlobal.md)
|
- [InvalidGlobal](issues/InvalidGlobal.md)
|
||||||
|
- [InvalidInterfaceImplementation](issues/InvalidInterfaceImplementation.md)
|
||||||
- [InvalidIterator](issues/InvalidIterator.md)
|
- [InvalidIterator](issues/InvalidIterator.md)
|
||||||
- [InvalidLiteralArgument](issues/InvalidLiteralArgument.md)
|
- [InvalidLiteralArgument](issues/InvalidLiteralArgument.md)
|
||||||
- [InvalidMethodCall](issues/InvalidMethodCall.md)
|
- [InvalidMethodCall](issues/InvalidMethodCall.md)
|
||||||
|
15
docs/running_psalm/issues/InvalidInterfaceImplementation.md
Normal file
15
docs/running_psalm/issues/InvalidInterfaceImplementation.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# InvalidInterfaceImplementation
|
||||||
|
|
||||||
|
Emitted when trying to implement interface that cannot be implemented (e.g. `Throwable`, `UnitEnum`, `BackedEnum`).
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class E implements UnitEnum
|
||||||
|
{
|
||||||
|
public static function cases(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
@ -39,6 +39,7 @@ use Psalm\Issue\InaccessibleMethod;
|
|||||||
use Psalm\Issue\InternalClass;
|
use Psalm\Issue\InternalClass;
|
||||||
use Psalm\Issue\InvalidEnumCaseValue;
|
use Psalm\Issue\InvalidEnumCaseValue;
|
||||||
use Psalm\Issue\InvalidExtendClass;
|
use Psalm\Issue\InvalidExtendClass;
|
||||||
|
use Psalm\Issue\InvalidInterfaceImplementation;
|
||||||
use Psalm\Issue\InvalidTraversableImplementation;
|
use Psalm\Issue\InvalidTraversableImplementation;
|
||||||
use Psalm\Issue\MethodSignatureMismatch;
|
use Psalm\Issue\MethodSignatureMismatch;
|
||||||
use Psalm\Issue\MismatchingDocblockPropertyType;
|
use Psalm\Issue\MismatchingDocblockPropertyType;
|
||||||
@ -2114,6 +2115,35 @@ class ClassAnalyzer extends ClassLikeAnalyzer
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($fq_interface_name_lc === 'throwable'
|
||||||
|
&& $codebase->analysis_php_version_id >= 7_00_00
|
||||||
|
&& !$storage->abstract
|
||||||
|
&& !isset($storage->parent_classes['exception'])
|
||||||
|
&& !isset($storage->parent_classes['error'])
|
||||||
|
) {
|
||||||
|
IssueBuffer::maybeAdd(
|
||||||
|
new InvalidInterfaceImplementation(
|
||||||
|
'Classes implementing Throwable should extend Exception or Error',
|
||||||
|
$code_location,
|
||||||
|
$fq_class_name,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($fq_interface_name_lc === 'unitenum'
|
||||||
|
|| $fq_interface_name_lc === 'backedenum')
|
||||||
|
&& !$storage->is_enum
|
||||||
|
&& $codebase->analysis_php_version_id >= 8_01_00
|
||||||
|
) {
|
||||||
|
IssueBuffer::maybeAdd(
|
||||||
|
new InvalidInterfaceImplementation(
|
||||||
|
$fq_interface_name . ' cannot be implemented by classes',
|
||||||
|
$code_location,
|
||||||
|
$fq_class_name,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if ($interface_storage->deprecated) {
|
if ($interface_storage->deprecated) {
|
||||||
IssueBuffer::maybeAdd(
|
IssueBuffer::maybeAdd(
|
||||||
new DeprecatedInterface(
|
new DeprecatedInterface(
|
||||||
|
9
src/Psalm/Issue/InvalidInterfaceImplementation.php
Normal file
9
src/Psalm/Issue/InvalidInterfaceImplementation.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psalm\Issue;
|
||||||
|
|
||||||
|
class InvalidInterfaceImplementation extends ClassIssue
|
||||||
|
{
|
||||||
|
const ERROR_LEVEL = -1;
|
||||||
|
const SHORTCODE = 317;
|
||||||
|
}
|
@ -1202,6 +1202,14 @@ class ClassTest extends TestCase
|
|||||||
',
|
',
|
||||||
'error_message' => 'MixedMethodCall',
|
'error_message' => 'MixedMethodCall',
|
||||||
],
|
],
|
||||||
|
'forbiddenThrowableImplementation' => [
|
||||||
|
'code' => '<?php
|
||||||
|
class C implements Throwable {}
|
||||||
|
',
|
||||||
|
'error_message' => 'InvalidInterfaceImplementation',
|
||||||
|
'ignored_issues' => [],
|
||||||
|
'php_version' => '7.0',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -308,6 +308,7 @@ class DocumentationTest extends TestCase
|
|||||||
case 'InvalidEnumMethod':
|
case 'InvalidEnumMethod':
|
||||||
case 'NoEnumProperties':
|
case 'NoEnumProperties':
|
||||||
case 'OverriddenFinalConstant':
|
case 'OverriddenFinalConstant':
|
||||||
|
case 'InvalidInterfaceImplementation':
|
||||||
$php_version = '8.1';
|
$php_version = '8.1';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -765,6 +765,46 @@ class EnumTest extends TestCase
|
|||||||
'ignored_issues' => [],
|
'ignored_issues' => [],
|
||||||
'php_version' => '8.1',
|
'php_version' => '8.1',
|
||||||
],
|
],
|
||||||
|
'forbiddenUnitEnumImplementation' => [
|
||||||
|
'code' => '<?php
|
||||||
|
class Foo implements UnitEnum {
|
||||||
|
/** @psalm-pure */
|
||||||
|
public static function cases(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
',
|
||||||
|
'error_message' => 'InvalidInterfaceImplementation',
|
||||||
|
'ignored_issues' => [],
|
||||||
|
'php_version' => '8.1',
|
||||||
|
],
|
||||||
|
'forbiddenBackedEnumImplementation' => [
|
||||||
|
'code' => '<?php
|
||||||
|
class Foo implements BackedEnum {
|
||||||
|
/** @psalm-pure */
|
||||||
|
public static function cases(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @psalm-pure */
|
||||||
|
public static function from(int|string $value): static
|
||||||
|
{
|
||||||
|
throw new Exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @psalm-pure */
|
||||||
|
public static function tryFrom(int|string $value): ?static
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
',
|
||||||
|
'error_message' => 'InvalidInterfaceImplementation',
|
||||||
|
'ignored_issues' => [],
|
||||||
|
'php_version' => '8.1',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user