diff --git a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php index bf4378d9158..964b6495148 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php @@ -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; @@ -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; @@ -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; @@ -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 diff --git a/tests/AttributeTest.php b/tests/AttributeTest.php index ddb5b1f5fd9..4a631c5b3dd 100644 --- a/tests/AttributeTest.php +++ b/tests/AttributeTest.php @@ -295,14 +295,28 @@ class Foo ], 'override' => [ 'code' => ' [], + 'ignored_issues' => [], + 'php_version' => '8.3', + ], + 'overrideInterface' => [ + 'code' => ' [], @@ -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' => ' 'InvalidOverride - src' . DIRECTORY_SEPARATOR . 'somefile.php:3:25', + 'error_levels' => [], + 'php_version' => '8.3', + ], + 'overrideConstructor' => [ + 'code' => ' 'InvalidOverride - src' . DIRECTORY_SEPARATOR . 'somefile.php:10:25', + 'error_levels' => [], + 'php_version' => '8.3', + ], + 'overridePrivate' => [ + 'code' => ' 'InvalidOverride - src' . DIRECTORY_SEPARATOR . 'somefile.php:7:25', + 'error_levels' => [], + 'php_version' => '8.3', + ], + 'overrideInterfaceWithNoParent' => [ + 'code' => ' 'InvalidOverride - src' . DIRECTORY_SEPARATOR . 'somefile.php:3:25', + 'error_levels' => [], + 'php_version' => '8.3', + ], 'tooFewArgumentsToAttributeConstructor' => [ 'code' => '