diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/apply_attribute_to_override_method_from_interface.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/apply_attribute_to_override_method_from_interface.php.inc deleted file mode 100644 index 78beb9c4939..00000000000 --- a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/apply_attribute_to_override_method_from_interface.php.inc +++ /dev/null @@ -1,30 +0,0 @@ - ------ - diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/apply_attribute_to_override_method_from_trait.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/apply_attribute_to_override_method_from_trait.php.inc index 9414b841d06..0444a87ae8d 100644 --- a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/apply_attribute_to_override_method_from_trait.php.inc +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/apply_attribute_to_override_method_from_trait.php.inc @@ -8,10 +8,6 @@ class ApplyAttributeToOverrideMethodFromTrait { use ExampleFromTrait; - public function foo() - { - } - public function bar() { } @@ -30,10 +26,6 @@ class ApplyAttributeToOverrideMethodFromTrait use ExampleFromTrait; #[\Override] - public function foo() - { - } - public function bar() { } diff --git a/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/skip_abstract_trait_method.php.inc b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/skip_abstract_trait_method.php.inc new file mode 100644 index 00000000000..af974d94595 --- /dev/null +++ b/rules-tests/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector/Fixture/skip_abstract_trait_method.php.inc @@ -0,0 +1,14 @@ +reflectionProvider->getClass($className); - $parentClassReflections = [ - ...$classReflection->getParents(), - ...$classReflection->getInterfaces(), - ...$classReflection->getTraits(), - ]; - $this->processAddOverrideAttribute($node, $parentClassReflections); + $parentClassReflections = array_merge($classReflection->getParents(), $classReflection->getTraits()); + if ($parentClassReflections === []) { + return null; + } + + foreach ($node->getMethods() as $classMethod) { + $this->processAddOverrideAttribute($classMethod, $parentClassReflections); + } if (! $this->hasChanged) { return null; @@ -125,47 +144,92 @@ public function provideMinPhpVersion(): int /** * @param ClassReflection[] $parentClassReflections */ - private function processAddOverrideAttribute(Class_ $class, array $parentClassReflections): void + private function processAddOverrideAttribute(ClassMethod $classMethod, array $parentClassReflections): void { - if ($parentClassReflections === []) { + if ($this->shouldSkipClassMethod($classMethod)) { return; } - foreach ($class->getMethods() as $classMethod) { - if ($classMethod->name->toString() === '__construct') { + /** @var string $classMethodName */ + $classMethodName = $this->getName($classMethod->name); + + // Private methods should be ignored + foreach ($parentClassReflections as $parentClassReflection) { + if (! $parentClassReflection->hasNativeMethod($classMethod->name->toString())) { + continue; + } + + // ignore if it is a private method on the parent + if (! $parentClassReflection->hasNativeMethod($classMethodName)) { continue; } - if ($classMethod->isPrivate()) { + $parentMethod = $parentClassReflection->getNativeMethod($classMethodName); + if ($parentMethod->isPrivate()) { continue; } - // ignore if it already uses the attribute - if ($this->phpAttributeAnalyzer->hasPhpAttribute($classMethod, 'Override')) { + if ($this->shouldSkipParentClassMethod($parentClassReflection, $classMethod)) { continue; } - // Private methods should be ignored - foreach ($parentClassReflections as $parentClassReflection) { - if (! $parentClassReflection->hasNativeMethod($classMethod->name->toString())) { - continue; - } + $classMethod->attrGroups[] = new AttributeGroup([new Attribute(new FullyQualified(self::OVERRIDE_CLASS))]); + $this->hasChanged = true; + return; + } + } + + private function shouldSkipClassMethod(ClassMethod $classMethod): bool + { + if ($this->isName($classMethod->name, MethodName::CONSTRUCT)) { + return true; + } + + if ($classMethod->isPrivate()) { + return true; + } + + // ignore if it already uses the attribute + return $this->phpAttributeAnalyzer->hasPhpAttribute($classMethod, self::OVERRIDE_CLASS); + } + + private function shouldSkipParentClassMethod(ClassReflection $parentClassReflection, ClassMethod $classMethod): bool + { + // parse parent method, if it has some contents or not + $parentClass = $this->astResolver->resolveClassFromClassReflection($parentClassReflection); + if (! $parentClass instanceof ClassLike) { + return true; + } + + $parentClassMethod = $parentClass->getMethod($classMethod->name->toString()); + if (! $parentClassMethod instanceof ClassMethod) { + return true; + } - // ignore if it is a private method on the parent - $parentMethod = $parentClassReflection->getNativeMethod($classMethod->name->toString()); - if ($parentMethod->isPrivate()) { - continue; - } + if ($parentClassMethod->isAbstract()) { + return true; + } - if ($parentClassReflection->isTrait() && ! $parentMethod->isAbstract()) { - continue; - } + // has any stmts? + if ($parentClassMethod->stmts === null || $parentClassMethod->stmts === []) { + return true; + } - $classMethod->attrGroups[] = new AttributeGroup([new Attribute(new FullyQualified('Override'))]); - $this->hasChanged = true; + if (count($parentClassMethod->stmts) === 1) { + /** @var Stmt $soleStmt */ + $soleStmt = $parentClassMethod->stmts[0]; + // most likely, return null; is interface to be designed to override + if ($soleStmt instanceof Return_ && $soleStmt->expr instanceof Expr && $this->valueResolver->isNull( + $soleStmt->expr + )) { + return true; + } - continue 2; + if ($soleStmt instanceof Throw_) { + return true; } } + + return false; } }