From b71c3265d5f90745d52b081a1201e3d97617e14a Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Sun, 7 Jan 2024 17:54:29 +0100 Subject: [PATCH] fix: RenameMethodRector should handle NullsafeMethodCall (#5444) --- .../rename_nullsafe_method_call.php.inc | 35 +++++++++++++++++++ .../Rector/MethodCall/RenameMethodRector.php | 11 +++--- src/NodeTypeResolver/NodeTypeResolver.php | 2 +- src/PhpParser/AstResolver.php | 5 +-- src/Reflection/ReflectionResolver.php | 3 +- .../PHPUnit/AbstractRectorTestCase.php | 2 +- 6 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_nullsafe_method_call.php.inc diff --git a/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_nullsafe_method_call.php.inc b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_nullsafe_method_call.php.inc new file mode 100644 index 00000000000..7e83b5dae99 --- /dev/null +++ b/rules-tests/Renaming/Rector/MethodCall/RenameMethodRector/Fixture/rename_nullsafe_method_call.php.inc @@ -0,0 +1,35 @@ +add('someContent'); + + $anotherHtml = $html; + $anotherHtml?->add('someContent'); + } +} + +?> +----- +addHtml('someContent'); + + $anotherHtml = $html; + $anotherHtml?->addHtml('someContent'); + } +} + +?> diff --git a/rules/Renaming/Rector/MethodCall/RenameMethodRector.php b/rules/Renaming/Rector/MethodCall/RenameMethodRector.php index 0f52d451e0c..82720905cc6 100644 --- a/rules/Renaming/Rector/MethodCall/RenameMethodRector.php +++ b/rules/Renaming/Rector/MethodCall/RenameMethodRector.php @@ -8,6 +8,7 @@ use PhpParser\Node; use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\NullsafeMethodCall; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Identifier; use PhpParser\Node\Stmt\Class_; @@ -68,11 +69,11 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [MethodCall::class, StaticCall::class, Class_::class, Interface_::class]; + return [MethodCall::class, NullsafeMethodCall::class, StaticCall::class, Class_::class, Interface_::class]; } /** - * @param MethodCall|StaticCall|Class_|Interface_ $node + * @param MethodCall|NullsafeMethodCall|StaticCall|Class_|Interface_ $node */ public function refactorWithScope(Node $node, Scope $scope): ?Node { @@ -94,7 +95,7 @@ public function configure(array $configuration): void } private function shouldSkipClassMethod( - MethodCall | StaticCall $call, + MethodCall|NullsafeMethodCall|StaticCall $call, MethodCallRenameInterface $methodCallRename ): bool { $classReflection = $this->reflectionResolver->resolveClassReflectionSourceObject($call); @@ -212,8 +213,8 @@ private function shouldSkipRename( } private function refactorMethodCallAndStaticCall( - StaticCall|MethodCall $call - ): ArrayDimFetch|null|MethodCall|StaticCall { + StaticCall|MethodCall|NullsafeMethodCall $call + ): ArrayDimFetch|null|MethodCall|StaticCall|NullsafeMethodCall { $callName = $this->getName($call->name); if ($callName === null) { return null; diff --git a/src/NodeTypeResolver/NodeTypeResolver.php b/src/NodeTypeResolver/NodeTypeResolver.php index 2f35e6abe3b..e93ae672298 100644 --- a/src/NodeTypeResolver/NodeTypeResolver.php +++ b/src/NodeTypeResolver/NodeTypeResolver.php @@ -301,7 +301,7 @@ public function getFullyQualifiedClassName(TypeWithClassName $typeWithClassName) public function isMethodStaticCallOrClassMethodObjectType(Node $node, ObjectType $objectType): bool { - if ($node instanceof MethodCall) { + if ($node instanceof MethodCall || $node instanceof Expr\NullsafeMethodCall) { // method call is variable return return $this->isObjectType($node->var, $objectType); } diff --git a/src/PhpParser/AstResolver.php b/src/PhpParser/AstResolver.php index 7b9e680a947..934c600f571 100644 --- a/src/PhpParser/AstResolver.php +++ b/src/PhpParser/AstResolver.php @@ -8,6 +8,7 @@ use PhpParser\Node\Expr; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\NullsafeMethodCall; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Param; use PhpParser\Node\Stmt; @@ -174,9 +175,9 @@ public function resolveClassMethod(string $className, string $methodName): ?Clas return $classMethod; } - public function resolveClassMethodFromCall(MethodCall | StaticCall $call): ?ClassMethod + public function resolveClassMethodFromCall(MethodCall | StaticCall | NullsafeMethodCall $call): ?ClassMethod { - $callerStaticType = $call instanceof MethodCall + $callerStaticType = ($call instanceof MethodCall || $call instanceof NullsafeMethodCall) ? $this->nodeTypeResolver->getType($call->var) : $this->nodeTypeResolver->getType($call->class); diff --git a/src/Reflection/ReflectionResolver.php b/src/Reflection/ReflectionResolver.php index 2c8c7d4758b..66b2b517172 100644 --- a/src/Reflection/ReflectionResolver.php +++ b/src/Reflection/ReflectionResolver.php @@ -8,6 +8,7 @@ use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\New_; +use PhpParser\Node\Expr\NullsafeMethodCall; use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\StaticPropertyFetch; @@ -76,7 +77,7 @@ public function resolveClassReflection(?Node $node): ?ClassReflection } public function resolveClassReflectionSourceObject( - MethodCall|StaticCall|PropertyFetch|StaticPropertyFetch $node + MethodCall|NullsafeMethodCall|StaticCall|PropertyFetch|StaticPropertyFetch $node ): ?ClassReflection { if ($node instanceof PropertyFetch || $node instanceof StaticPropertyFetch) { $objectType = $node instanceof PropertyFetch diff --git a/src/Testing/PHPUnit/AbstractRectorTestCase.php b/src/Testing/PHPUnit/AbstractRectorTestCase.php index 935229bd7ac..3b8e6d36cd5 100644 --- a/src/Testing/PHPUnit/AbstractRectorTestCase.php +++ b/src/Testing/PHPUnit/AbstractRectorTestCase.php @@ -204,7 +204,7 @@ private function includePreloadFilesAndScoperAutoload(): void if (file_exists(__DIR__ . '/../../../preload.php')) { if (file_exists(__DIR__ . '/../../../vendor')) { require_once __DIR__ . '/../../../preload.php'; - // test case in rector split package + // test case in rector split package } elseif (file_exists(__DIR__ . '/../../../../../../vendor')) { require_once __DIR__ . '/../../../preload-split-package.php'; }