Skip to content

Commit

Permalink
Merge pull request #10644 from edsrzf/invalid-override
Browse files Browse the repository at this point in the history
  • Loading branch information
weirdan authored Feb 3, 2024
2 parents 080c8f6 + ea9cb44 commit 3b8d81b
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 5 deletions.
1 change: 1 addition & 0 deletions config.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@
<xs:element name="InvalidNamedArgument" type="ArgumentIssueHandlerType" minOccurs="0" />
<xs:element name="InvalidNullableReturnType" type="IssueHandlerType" minOccurs="0" />
<xs:element name="InvalidOperand" type="IssueHandlerType" minOccurs="0" />
<xs:element name="InvalidOverride" type="IssueHandlerType" minOccurs="0" />
<xs:element name="InvalidParamDefault" type="IssueHandlerType" minOccurs="0" />
<xs:element name="InvalidParent" type="IssueHandlerType" minOccurs="0" />
<xs:element name="InvalidPassByReference" type="IssueHandlerType" minOccurs="0" />
Expand Down
1 change: 1 addition & 0 deletions docs/running_psalm/error_levels.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ Level 5 and above allows a more non-verifiable code, and higher levels are even
- [CircularReference](issues/CircularReference.md)
- [ConflictingReferenceConstraint](issues/ConflictingReferenceConstraint.md)
- [ContinueOutsideLoop](issues/ContinueOutsideLoop.md)
- [InvalidOverride](issues/InvalidOverride.md)
- [InvalidTypeImport](issues/InvalidTypeImport.md)
- [MethodSignatureMismatch](issues/MethodSignatureMismatch.md)
- [OverriddenMethodAccess](issues/OverriddenMethodAccess.md)
Expand Down
1 change: 1 addition & 0 deletions docs/running_psalm/issues.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
- [InvalidNamedArgument](issues/InvalidNamedArgument.md)
- [InvalidNullableReturnType](issues/InvalidNullableReturnType.md)
- [InvalidOperand](issues/InvalidOperand.md)
- [InvalidOverride](issues/InvalidOverride.md)
- [InvalidParamDefault](issues/InvalidParamDefault.md)
- [InvalidParent](issues/InvalidParent.md)
- [InvalidPassByReference](issues/InvalidPassByReference.md)
Expand Down
24 changes: 24 additions & 0 deletions docs/running_psalm/issues/InvalidOverride.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# InvalidOverride

Emitted when an `Override` attribute was added to a method that does not override a method from a parent class or implemented interface.

```php
<?php

class A {
function receive(): void
{
}
}

class B extends A {
#[Override]
function recieve(): void
{
}
}
```

## Why this is bad

A fatal error will be thrown.
19 changes: 19 additions & 0 deletions src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
use Psalm\Internal\Type\TemplateStandinTypeReplacer;
use Psalm\Internal\Type\TypeExpander;
use Psalm\Issue\InvalidDocblockParamName;
use Psalm\Issue\InvalidOverride;
use Psalm\Issue\InvalidParamDefault;
use Psalm\Issue\InvalidThrow;
use Psalm\Issue\MethodSignatureMismatch;
Expand All @@ -48,6 +49,7 @@
use Psalm\Node\Expr\VirtualVariable;
use Psalm\Node\Stmt\VirtualWhile;
use Psalm\Plugin\EventHandler\Event\AfterFunctionLikeAnalysisEvent;
use Psalm\Storage\AttributeStorage;
use Psalm\Storage\ClassLikeStorage;
use Psalm\Storage\FunctionLikeParameter;
use Psalm\Storage\FunctionLikeStorage;
Expand All @@ -65,6 +67,7 @@

use function array_combine;
use function array_diff_key;
use function array_filter;
use function array_key_exists;
use function array_keys;
use function array_merge;
Expand Down Expand Up @@ -1970,6 +1973,22 @@ private function getFunctionInformation(
true,
);

if ($codebase->analysis_php_version_id >= 8_03_00
&& (!$overridden_method_ids || $storage->cased_name === '__construct')
&& array_filter(
$storage->attributes,
static fn(AttributeStorage $s): bool => $s->fq_class_name === 'Override',
)
) {
IssueBuffer::maybeAdd(
new InvalidOverride(
'Method ' . $storage->cased_name . ' does not match any parent method',
$codeLocation,
),
$this->getSuppressedIssues(),
);
}

if ($overridden_method_ids
&& !$context->collect_initializations
&& !$context->collect_mutations
Expand Down
9 changes: 9 additions & 0 deletions src/Psalm/Issue/InvalidOverride.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Psalm\Issue;

final class InvalidOverride extends CodeIssue
{
public const ERROR_LEVEL = 7;
public const SHORTCODE = 357;
}
79 changes: 74 additions & 5 deletions tests/AttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -295,14 +295,28 @@ class Foo
],
'override' => [
'code' => '<?php
class C {
public function f(): void {}
}
namespace OverrideAttribute;
use Override;
class C2 extends C {
#[Override]
public function f(): void {}
}
',
'assertions' => [],
'ignored_issues' => [],
'php_version' => '8.3',
],
'overrideInterface' => [
'code' => '<?php
interface I {
public function f(): void;
}
class HelloWorld {
interface I2 extends I {
#[Override]
public function __invoke() {}
public function f(): void;
}
',
'assertions' => [],
Expand Down Expand Up @@ -527,6 +541,61 @@ function foo() : void {}',
function foo(#[Pure] string $str) : void {}',
'error_message' => 'UndefinedAttributeClass - src' . DIRECTORY_SEPARATOR . 'somefile.php:4:36',
],
'overrideWithNoParent' => [
'code' => '<?php
class C {
#[Override]
public function f(): void {}
}
',
'error_message' => 'InvalidOverride - src' . DIRECTORY_SEPARATOR . 'somefile.php:3:25',
'error_levels' => [],
'php_version' => '8.3',
],
'overrideConstructor' => [
'code' => '<?php
/**
* @psalm-consistent-constructor
*/
class C {
public function __construct() {}
}
class C2 extends C {
#[Override]
public function __construct() {}
}
',
'error_message' => 'InvalidOverride - src' . DIRECTORY_SEPARATOR . 'somefile.php:10:25',
'error_levels' => [],
'php_version' => '8.3',
],
'overridePrivate' => [
'code' => '<?php
class C {
private function f(): void {}
}
class C2 extends C {
#[Override]
private function f(): void {}
}
',
'error_message' => 'InvalidOverride - src' . DIRECTORY_SEPARATOR . 'somefile.php:7:25',
'error_levels' => [],
'php_version' => '8.3',
],
'overrideInterfaceWithNoParent' => [
'code' => '<?php
interface I {
#[Override]
public function f(): void;
}
',
'error_message' => 'InvalidOverride - src' . DIRECTORY_SEPARATOR . 'somefile.php:3:25',
'error_levels' => [],
'php_version' => '8.3',
],
'tooFewArgumentsToAttributeConstructor' => [
'code' => '<?php
namespace Foo;
Expand Down
4 changes: 4 additions & 0 deletions tests/DocumentationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,10 @@ public function providerInvalidCodeParse(): array
case 'InvalidInterfaceImplementation':
$php_version = '8.1';
break;

case 'InvalidOverride':
$php_version = '8.3';
break;
}

$invalid_code_data[$issue_name] = [
Expand Down

0 comments on commit 3b8d81b

Please sign in to comment.