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

Fix false MissingOverrideAttribute issue #10989

Open
wants to merge 5 commits into
base: 5.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -1996,8 +1996,9 @@
&& $codebase->config->ensure_override_attribute
&& $overridden_method_ids
&& $storage->cased_name !== '__construct'
&& ($storage->cased_name !== '__toString'
|| isset($appearing_class_storage->direct_class_interfaces['stringable']))
&& (array_key_exists($storage->cased_name, $appearing_class_storage->methods)

Check failure on line 1999 in src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php

View workflow job for this annotation

GitHub Actions / build

PossiblyNullArgument

src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php:1999:42: PossiblyNullArgument: Argument 1 of array_key_exists cannot be null, possibly null value provided (see https://psalm.dev/078)
|| array_key_exists('stringable', $appearing_class_storage->direct_class_interfaces)
)
) {
IssueBuffer::maybeAdd(
new MissingOverrideAttribute(
Expand Down
265 changes: 265 additions & 0 deletions tests/OverrideTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,178 @@ public function __toString(): string {
'ignored_issues' => [],
'php_version' => '8.3',
],
'Test case #10982 - https://github.com/vimeo/psalm/issues/10982' => [
'code' => '
<?php
trait Foo
{
private function inTrait(): void { echo "foobar\n"; }
}

class A {
use Foo;

public function bar(): void {
$this->inTrait();
}
}

class B extends A {
use Foo;

function baz(): void
{
$this->inTrait();
}
}
',
'assertions' => [],
'ignored_issues' => [],
'php_version' => '8.3',
],
'Test case #10989 - https://github.com/vimeo/psalm/pull/10989#discussion_r1615149365' => [
'code' => '
<?php

trait Foo
{
protected function inTrait(): void { echo "foobar\n"; }
}

class A {
use Foo;

public function bar(): void {
$this->inTrait();
}
}

class B extends A {
use Foo;

function baz(): void
{
$this->inTrait();
}
}

$b = new B();
$b->baz();
',
'assertions' => [],
'ignored_issues' => [],
'php_version' => '8.3',
],
'Valid examples #1 - https://wiki.php.net/rfc/marking_overriden_methods' => [
'code' => '
<?php
class P {
protected function p(): void {}
}

class C extends P {
#[\Override]
public function p(): void {}
}
',
'assertions' => [],
'ignored_issues' => [],
'php_version' => '8.3',
],
'Valid examples #2 - https://wiki.php.net/rfc/marking_overriden_methods' => [
'code' => '<?php
/**
* @template T
* @implements \IteratorAggregate<T>
*/
class Foo implements IteratorAggregate
{
#[\Override]
public function getIterator(): Traversable
{
yield from [];
}
}
',
'assertions' => [],
'ignored_issues' => [],
'php_version' => '8.3',
],
'Valid examples #3 - https://wiki.php.net/rfc/marking_overriden_methods' => [
'code' => '<?php
trait T {
#[\Override]
public function t(): void {}
}
',
'assertions' => [],
'ignored_issues' => [],
'php_version' => '8.3',
],
'Valid examples #4 - https://wiki.php.net/rfc/marking_overriden_methods' => [
'code' => '<?php
trait T {
#[\Override]
public function i(): void {}
}

interface I {
public function i(): void;
}

class Foo implements I {
use T;
}
',
'assertions' => [],
'ignored_issues' => [],
'php_version' => '8.3',
],
'Valid examples #5 - https://wiki.php.net/rfc/marking_overriden_methods' => [
'code' => '<?php
interface I {
public function i();
}

interface II extends I {
#[\Override]
public function i();
}

class P {
public function p1() {}
public function p2() {}
public function p3() {}
public function p4() {}
}

class PP extends P {
#[\Override]
public function p1() {}
#[\Override]
public function p2() {}
#[\Override]
public function p3() {}
}

class C extends PP implements I {
#[\Override]
public function i() {}
#[\Override]
public function p1() {}
#[\Override]
public function p2() {}
#[\Override]
public function p3() {}
#[\Override]
public function p4() {}
public function c() {}
}
',
'assertions' => [],
'ignored_issues' => ['MissingReturnType'],
'php_version' => '8.3',
],
];
}

Expand Down Expand Up @@ -216,6 +388,99 @@ public function __toString(): string {
'error_levels' => [],
'php_version' => '8.3',
],
'Invalid examples #1 - https://wiki.php.net/rfc/marking_overriden_methods' => [
'code' => '<?php
class C
{
#[\Override]
public function c(): void {}
// Fatal error: C::c() has #[\Override] attribute, but no matching parent method exists
}
',
'error_message' => 'InvalidOverride',
'error_levels' => [],
'php_version' => '8.3',
],
'Invalid examples #2 - https://wiki.php.net/rfc/marking_overriden_methods' => [
'code' => '<?php
interface I {
public function i(): void;
}

class P {
#[\Override]
public function i(): void {}
// Fatal error: P::i() has #[\Override] attribute, but no matching parent method exists
}

class C extends P implements I {}
',
'error_message' => 'InvalidOverride',
'error_levels' => [],
'php_version' => '8.3',
],
'Invalid examples #3 - https://wiki.php.net/rfc/marking_overriden_methods' => [
'code' => '<?php
trait T {
#[\Override]
public function t(): void {}
}

class Foo {
use T;
// Fatal error: Foo::t() has #[\Override] attribute, but no matching parent method exists
}
',
'error_message' => 'InvalidOverride',
'error_levels' => [],
'php_version' => '8.3',
],
'Invalid examples #4 - https://wiki.php.net/rfc/marking_overriden_methods' => [
'code' => '<?php
class P {
private function p(): void {}
}

class C extends P {
#[\Override]
public function p(): void {}
// Fatal error: C::p() has #[\Override] attribute, but no matching parent method exists
}
',
'error_message' => 'InvalidOverride',
'error_levels' => [],
'php_version' => '8.3',
],
'Invalid examples #5 - https://wiki.php.net/rfc/marking_overriden_methods' => [
'code' => '<?php
trait T {
public function t(): void {}
}

class C {
use T;

#[\Override]
public function t(): void {}
// Fatal error: C::t() has #[\Override] attribute, but no matching parent method exists
}
',
'error_message' => 'InvalidOverride',
'error_levels' => [],
'php_version' => '8.3',
],
'Invalid examples #6 - https://wiki.php.net/rfc/marking_overriden_methods' => [
'code' => '<?php
interface I {
#[\Override]
public function i(): void;
// Fatal error: I::i() has #[\Override] attribute, but no matching parent method exists
}
',
'error_message' => 'InvalidOverride',
'error_levels' => [],
'php_version' => '8.3',
],
];
}
}
Loading