diff --git a/build/target-repository/docs/rector_rules_overview.md b/build/target-repository/docs/rector_rules_overview.md index d31902d9a69..e29b393904e 100644 --- a/build/target-repository/docs/rector_rules_overview.md +++ b/build/target-repository/docs/rector_rules_overview.md @@ -6,9 +6,9 @@ - [Arguments](#arguments) (6) -- [CodeQuality](#codequality) (77) +- [CodeQuality](#codequality) (78) -- [CodingStyle](#codingstyle) (38) +- [CodingStyle](#codingstyle) (37) - [Compatibility](#compatibility) (1) @@ -528,6 +528,31 @@ Change `array_push()` to direct variable assign
+### CleanupUnneededNullsafeOperatorRector + +Cleanup unneeded nullsafe operator + +- class: [`Rector\CodeQuality\Rector\NullsafeMethodCall\CleanupUnneededNullsafeOperatorRector`](../rules/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector.php) + +```diff + class HelloWorld { + public function getString(): string + { + return 'hello world'; + } + } + + function get(): HelloWorld + { + return new HelloWorld(); + } + +-echo get()?->getString(); ++echo get()->getString(); +``` + +
+ ### CombineIfRector Merges nested if statements @@ -2357,26 +2382,6 @@ return static function (RectorConfig $rectorConfig): void {
-### RemoveDoubleUnderscoreInMethodNameRector - -Non-magic PHP object methods cannot start with "__" - -- class: [`Rector\CodingStyle\Rector\ClassMethod\RemoveDoubleUnderscoreInMethodNameRector`](../rules/CodingStyle/Rector/ClassMethod/RemoveDoubleUnderscoreInMethodNameRector.php) - -```diff - class SomeClass - { -- public function __getName($anotherObject) -+ public function getName($anotherObject) - { -- $anotherObject->__getSurname(); -+ $anotherObject->getSurname(); - } - } -``` - -
- ### RemoveFinalFromConstRector Remove final from constants in classes defined as final diff --git a/config/set/code-quality.php b/config/set/code-quality.php index 19a1bd1869f..80a38211a52 100644 --- a/config/set/code-quality.php +++ b/config/set/code-quality.php @@ -3,7 +3,6 @@ declare(strict_types=1); use Rector\CodeQuality\Rector\Array_\CallableThisArrayToAnonymousFunctionRector; - use Rector\CodeQuality\Rector\Assign\CombinedAssignRector; use Rector\CodeQuality\Rector\Assign\SplitListAssignToSeparateLineRector; use Rector\CodeQuality\Rector\BooleanAnd\SimplifyEmptyArrayCheckRector; @@ -82,6 +81,7 @@ use Rector\CodingStyle\Rector\ClassMethod\FuncGetArgsToVariadicParamRector; use Rector\CodingStyle\Rector\FuncCall\CallUserFuncToMethodCallRector; use Rector\CodingStyle\Rector\FuncCall\CountArrayToEmptyArrayComparisonRector; +use Rector\CodeQuality\Rector\NullsafeMethodCall\CleanupUnneededNullsafeOperatorRector; use Rector\Config\RectorConfig; use Rector\Php52\Rector\Property\VarToPublicPropertyRector; use Rector\Php71\Rector\FuncCall\RemoveExtraParametersRector; @@ -197,5 +197,6 @@ OptionalParametersAfterRequiredRector::class, SimplifyEmptyCheckOnEmptyArrayRector::class, SwitchTrueToIfRector::class, + CleanupUnneededNullsafeOperatorRector::class, ]); }; diff --git a/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/CleanupUnneededNullsafeOperatorRectorTest.php b/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/CleanupUnneededNullsafeOperatorRectorTest.php new file mode 100644 index 00000000000..ff96658dabd --- /dev/null +++ b/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/CleanupUnneededNullsafeOperatorRectorTest.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/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace.php.inc b/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace.php.inc new file mode 100644 index 00000000000..01a7155b5e6 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace.php.inc @@ -0,0 +1,41 @@ +getString(); + +?> +----- +getString(); + +?> diff --git a/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace_on_method_call.php.inc b/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace_on_method_call.php.inc new file mode 100644 index 00000000000..ed0e4088803 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace_on_method_call.php.inc @@ -0,0 +1,47 @@ +get2()?->getString2(); + +?> +----- +get2()->getString2(); + +?> diff --git a/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace_with_invalid_doc.php.inc b/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace_with_invalid_doc.php.inc new file mode 100644 index 00000000000..dc5aaef5838 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace_with_invalid_doc.php.inc @@ -0,0 +1,47 @@ +getString(); + +?> +----- +getString(); + +?> diff --git a/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/skip_nullable.php.inc b/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/skip_nullable.php.inc new file mode 100644 index 00000000000..3faf89a876c --- /dev/null +++ b/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/skip_nullable.php.inc @@ -0,0 +1,20 @@ +getString(); + +?> diff --git a/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/config/configured_rule.php new file mode 100644 index 00000000000..8df188a1f29 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(CleanupUnneededNullsafeOperatorRector::class); +}; diff --git a/rules/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector.php b/rules/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector.php new file mode 100644 index 00000000000..146a73a86e2 --- /dev/null +++ b/rules/CodeQuality/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector.php @@ -0,0 +1,114 @@ +getString(); +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +class HelloWorld { + public function getString(): string + { + return 'hello world'; + } +} + +function get(): HelloWorld +{ + return new HelloWorld(); +} + +echo get()->getString(); +CODE_SAMPLE + ), + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [NullsafeMethodCall::class]; + } + + /** + * @param NullsafeMethodCall $node + */ + public function refactor(Node $node): ?Node + { + if (! $node->name instanceof Identifier) { + return null; + } + + if (! $node->var instanceof FuncCall && ! $node->var instanceof MethodCall && ! $node->var instanceof StaticCall) { + return null; + } + + $returnNode = $this->returnStrictTypeAnalyzer->resolveMethodCallReturnNode($node->var); + + if (! $returnNode instanceof Node) { + return null; + } + + $type = $this->getType($returnNode); + if (! $type instanceof FullyQualifiedObjectType) { + return null; + } + + return new MethodCall($node->var, $node->name, $node->args); + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NULLSAFE_OPERATOR; + } +} diff --git a/rules/TypeDeclaration/TypeAnalyzer/ReturnStrictTypeAnalyzer.php b/rules/TypeDeclaration/TypeAnalyzer/ReturnStrictTypeAnalyzer.php index 34d51051d89..37a0ad85f30 100644 --- a/rules/TypeDeclaration/TypeAnalyzer/ReturnStrictTypeAnalyzer.php +++ b/rules/TypeDeclaration/TypeAnalyzer/ReturnStrictTypeAnalyzer.php @@ -60,7 +60,7 @@ public function collectStrictReturnTypes(array $returns): array return $this->typeNodeUnwrapper->uniquateNodes($returnedStrictTypeNodes); } - private function resolveMethodCallReturnNode(MethodCall | StaticCall | FuncCall $call): ?Node + public function resolveMethodCallReturnNode(MethodCall | StaticCall | FuncCall $call): ?Node { $methodReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($call); if ($methodReflection === null) { diff --git a/src/ValueObject/PhpVersionFeature.php b/src/ValueObject/PhpVersionFeature.php index 78853a62cd9..eedfca164f9 100644 --- a/src/ValueObject/PhpVersionFeature.php +++ b/src/ValueObject/PhpVersionFeature.php @@ -433,6 +433,11 @@ final class PhpVersionFeature */ public const STATIC_VISIBILITY_SET_STATE = PhpVersion::PHP_80; + /** + * @var int + */ + public const NULLSAFE_OPERATOR = PhpVersion::PHP_80; + /** * @var int */