From 92e4acb0cdc63a5c2266e66623975ec1b43299c0 Mon Sep 17 00:00:00 2001 From: johan Date: Wed, 22 Feb 2023 16:06:58 +0100 Subject: [PATCH] [CleanupUnneededNullsafeOperatorRector] Wip adding new rule fix #7781 --- .../docs/rector_rules_overview.md | 29 ++++- config/set/code-quality.php | 3 +- config/set/php80.php | 1 - ...anupUnneededNullsafeOperatorRectorTest.php | 28 +++++ .../Fixture/replace.php.inc | 41 +++++++ .../Fixture/replace_with_invalid_doc.php.inc | 47 ++++++++ .../Fixture/skip.php.inc | 20 ++++ .../config/configured_rule.php | 10 ++ .../CleanupUnneededNullsafeOperatorRector.php | 105 ++++++++++++++++++ .../TypeAnalyzer/ReturnStrictTypeAnalyzer.php | 2 +- src/ValueObject/PhpVersionFeature.php | 5 + 11 files changed, 286 insertions(+), 5 deletions(-) create mode 100644 rules-tests/CodingStyle/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/CleanupUnneededNullsafeOperatorRectorTest.php create mode 100644 rules-tests/CodingStyle/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace.php.inc create mode 100644 rules-tests/CodingStyle/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace_with_invalid_doc.php.inc create mode 100644 rules-tests/CodingStyle/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/skip.php.inc create mode 100644 rules-tests/Php80/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/config/configured_rule.php create mode 100644 rules/CodingStyle/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector.php diff --git a/build/target-repository/docs/rector_rules_overview.md b/build/target-repository/docs/rector_rules_overview.md index 8915de87d35..52768d7dae9 100644 --- a/build/target-repository/docs/rector_rules_overview.md +++ b/build/target-repository/docs/rector_rules_overview.md @@ -1,4 +1,4 @@ -# 414 Rules Overview +# 415 Rules Overview
@@ -8,7 +8,7 @@ - [CodeQuality](#codequality) (78) -- [CodingStyle](#codingstyle) (39) +- [CodingStyle](#codingstyle) (40) - [Compatibility](#compatibility) (1) @@ -1946,6 +1946,31 @@ Type and name of catch exception should match
+### CleanupUnneededNullsafeOperatorRector + +Cleanup unneeded nullsafe operator + +- class: [`Rector\CodingStyle\Rector\NullsafeMethodCall\CleanupUnneededNullsafeOperatorRector`](../rules/CodingStyle/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector.php) + +```diff + class HelloWorld { + public function getString(): string + { + return 'hello world'; + } + } + + public function get(): HelloWorld + { + return new HelloWorld(); + } + +-echo $this->get()?->getHelloWorld(); ++echo $this->get()->getHelloWorld(); +``` + +
+ ### ConsistentImplodeRector Changes various implode forms to consistent one diff --git a/config/set/code-quality.php b/config/set/code-quality.php index 0dbeb96a60e..7d43cd8518f 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; @@ -83,6 +82,7 @@ use Rector\CodingStyle\Rector\ClassMethod\FuncGetArgsToVariadicParamRector; use Rector\CodingStyle\Rector\FuncCall\CallUserFuncToMethodCallRector; use Rector\CodingStyle\Rector\FuncCall\CountArrayToEmptyArrayComparisonRector; +use Rector\CodingStyle\Rector\NullsafeMethodCall\CleanupUnneededNullsafeOperatorRector; use Rector\Config\RectorConfig; use Rector\Php52\Rector\Property\VarToPublicPropertyRector; use Rector\Php71\Rector\FuncCall\RemoveExtraParametersRector; @@ -199,5 +199,6 @@ TernaryEmptyArrayArrayDimFetchToCoalesceRector::class, OptionalParametersAfterRequiredRector::class, SimplifyEmptyCheckOnEmptyArrayRector::class, + CleanupUnneededNullsafeOperatorRector::class, ]); }; diff --git a/config/set/php80.php b/config/set/php80.php index 7f8de75376f..a7fe67c20f8 100644 --- a/config/set/php80.php +++ b/config/set/php80.php @@ -3,7 +3,6 @@ declare(strict_types=1); use Rector\Arguments\Rector\ClassMethod\ArgumentAdderRector; - use Rector\Arguments\Rector\FuncCall\FunctionArgumentDefaultValueReplacerRector; use Rector\Arguments\ValueObject\ArgumentAdder; use Rector\Arguments\ValueObject\ReplaceFuncCallArgumentDefaultValue; diff --git a/rules-tests/CodingStyle/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/CleanupUnneededNullsafeOperatorRectorTest.php b/rules-tests/CodingStyle/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/CleanupUnneededNullsafeOperatorRectorTest.php new file mode 100644 index 00000000000..406af628540 --- /dev/null +++ b/rules-tests/CodingStyle/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/CodingStyle/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace.php.inc b/rules-tests/CodingStyle/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace.php.inc new file mode 100644 index 00000000000..b881aa514bb --- /dev/null +++ b/rules-tests/CodingStyle/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace.php.inc @@ -0,0 +1,41 @@ +get()?->getReplace(); + +?> +----- +get()->getReplace(); + +?> diff --git a/rules-tests/CodingStyle/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace_with_invalid_doc.php.inc b/rules-tests/CodingStyle/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace_with_invalid_doc.php.inc new file mode 100644 index 00000000000..1e49058b1c2 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/replace_with_invalid_doc.php.inc @@ -0,0 +1,47 @@ +get()?->getReplaceWithInvalidDoc(); + +?> +----- +get()->getReplaceWithInvalidDoc(); + +?> diff --git a/rules-tests/CodingStyle/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/skip.php.inc b/rules-tests/CodingStyle/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/skip.php.inc new file mode 100644 index 00000000000..2910e3a9699 --- /dev/null +++ b/rules-tests/CodingStyle/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/Fixture/skip.php.inc @@ -0,0 +1,20 @@ +get()?->getReplace(); + +?> diff --git a/rules-tests/Php80/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/config/configured_rule.php b/rules-tests/Php80/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/config/configured_rule.php new file mode 100644 index 00000000000..7b273257dd5 --- /dev/null +++ b/rules-tests/Php80/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(CleanupUnneededNullsafeOperatorRector::class); +}; diff --git a/rules/CodingStyle/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector.php b/rules/CodingStyle/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector.php new file mode 100644 index 00000000000..c1059dc7020 --- /dev/null +++ b/rules/CodingStyle/Rector/NullsafeMethodCall/CleanupUnneededNullsafeOperatorRector.php @@ -0,0 +1,105 @@ +get()?->getHelloWorld(); +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +class HelloWorld { + public function getString(): string + { + return 'hello world'; + } +} + +public function get(): HelloWorld +{ + return new HelloWorld(); +} + +echo $this->get()->getHelloWorld(); +CODE_SAMPLE + ), + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [NullsafeMethodCall::class]; + } + + /** + * @param NullsafeMethodCall $node + */ + public function refactor(Node $node): ?Node + { + if (!$node->var instanceof MethodCall) { + return null; + } + + $returnNode = $this->returnStrictTypeAnalyzer->resolveMethodCallReturnNode($node->var); + + if (null === $returnNode) { + return null; + } + + // Remove not needed Nullsafe for method call. + $node = $node->var; + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::NULLSAFE_OPERATOR; + } +} diff --git a/rules/TypeDeclaration/TypeAnalyzer/ReturnStrictTypeAnalyzer.php b/rules/TypeDeclaration/TypeAnalyzer/ReturnStrictTypeAnalyzer.php index 7346885c0f8..2bcb5e49579 100644 --- a/rules/TypeDeclaration/TypeAnalyzer/ReturnStrictTypeAnalyzer.php +++ b/rules/TypeDeclaration/TypeAnalyzer/ReturnStrictTypeAnalyzer.php @@ -59,7 +59,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 d0e71f75b5d..b8d4f356704 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 */