From c4dd05e2e3ae6359ff4eb57ce7d7bd7619da139d Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 15 Feb 2024 19:49:53 +0700 Subject: [PATCH] [CodeQuality] Add StaticToSelfStaticMethodCallOnFinalClassRector (#5621) * [CodeQuality] Add StaticToSelfStaticMethodCallOnFinalClassRector * dynamic test * fix * fix * fix phpstan * ensure args included * fix * skip not a Name * fix --- config/set/code-quality.php | 2 + .../Fixture/call_statically.php.inc | 37 ++++++ .../Fixture/skip_already_self.php.inc | 15 +++ .../skip_call_non_existence_method.php.inc | 11 ++ .../Fixture/skip_dynamic_method.php.inc | 11 ++ .../Fixture/skip_from_variable.php.inc | 15 +++ .../Fixture/skip_non_final.php.inc | 15 +++ .../Fixture/skip_non_static_call.php.inc | 15 +++ ...StaticMethodCallOnFinalClassRectorTest.php | 28 +++++ .../config/configured_rule.php | 9 ++ ...SelfStaticMethodCallOnFinalClassRector.php | 118 ++++++++++++++++++ 11 files changed, 276 insertions(+) create mode 100644 rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/call_statically.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/skip_already_self.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/skip_call_non_existence_method.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/skip_dynamic_method.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/skip_from_variable.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/skip_non_final.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/skip_non_static_call.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/StaticToSelfStaticMethodCallOnFinalClassRectorTest.php create mode 100644 rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/config/configured_rule.php create mode 100644 rules/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector.php diff --git a/config/set/code-quality.php b/config/set/code-quality.php index 0d6aa51c27c..f07eabc2969 100644 --- a/config/set/code-quality.php +++ b/config/set/code-quality.php @@ -11,6 +11,7 @@ use Rector\CodeQuality\Rector\Catch_\ThrowWithPreviousExceptionRector; use Rector\CodeQuality\Rector\Class_\CompleteDynamicPropertiesRector; use Rector\CodeQuality\Rector\Class_\InlineConstructorDefaultToPropertyRector; +use Rector\CodeQuality\Rector\Class_\StaticToSelfStaticMethodCallOnFinalClassRector; use Rector\CodeQuality\Rector\ClassConstFetch\ConvertStaticPrivateConstantToSelfRector; use Rector\CodeQuality\Rector\ClassMethod\InlineArrayReturnAssignRector; use Rector\CodeQuality\Rector\ClassMethod\LocallyCalledStaticMethodToNonStaticRector; @@ -192,5 +193,6 @@ NumberCompareToMaxFuncCallRector::class, CompleteMissingIfElseBracketRector::class, RemoveUselessIsObjectCheckRector::class, + StaticToSelfStaticMethodCallOnFinalClassRector::class, ]); }; diff --git a/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/call_statically.php.inc b/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/call_statically.php.inc new file mode 100644 index 00000000000..24651fbcaf2 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/call_statically.php.inc @@ -0,0 +1,37 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/skip_already_self.php.inc b/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/skip_already_self.php.inc new file mode 100644 index 00000000000..594d1ef8d99 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/skip_already_self.php.inc @@ -0,0 +1,15 @@ +run(); + } + + private function run() + { + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/StaticToSelfStaticMethodCallOnFinalClassRectorTest.php b/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/StaticToSelfStaticMethodCallOnFinalClassRectorTest.php new file mode 100644 index 00000000000..1463be7d355 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/StaticToSelfStaticMethodCallOnFinalClassRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/config/configured_rule.php new file mode 100644 index 00000000000..14e1d98bcd7 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([StaticToSelfStaticMethodCallOnFinalClassRector::class]); diff --git a/rules/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector.php b/rules/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector.php new file mode 100644 index 00000000000..6faaf048f0d --- /dev/null +++ b/rules/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector.php @@ -0,0 +1,118 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Class_ + { + if (! $node->isFinal()) { + return null; + } + + $hasChanged = false; + + $this->traverseNodesWithCallable($node->stmts, function (Node $subNode) use (&$hasChanged, $node): ?StaticCall { + if (! $subNode instanceof StaticCall) { + return null; + } + + if (! $this->isName($subNode->class, ObjectReference::STATIC)) { + return null; + } + + // skip dynamic method + if (! $subNode->name instanceof Identifier) { + return null; + } + + $methodName = (string) $this->getName($subNode->name); + $targetClassMethod = $node->getMethod($methodName); + + // skip call non-existing method from current class to ensure transformation is safe + if (! $targetClassMethod instanceof ClassMethod) { + return null; + } + + // avoid overlapped change + if (! $targetClassMethod->isStatic()) { + return null; + } + + $hasChanged = true; + $subNode->class = new Name('self'); + return $subNode; + }); + + if ($hasChanged) { + return $node; + } + + return null; + } +}