From 57275efac450b9b0466e9dc934d3c07e2e9a1e43 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Tue, 30 Apr 2024 15:22:44 +0200 Subject: [PATCH] [TypeDeclarations] Add IncreaseDeclareStrictTypesRector to raise level of declare() coverage (#5849) * [TypeDeclarations] Add IncreaseDeclareStrictTypesRector to raise level of declare() coverage * [ci-review] Rector Rectify --------- Co-authored-by: GitHub Action --- .../skip_already_with_strict_types.php.inc | 9 ++ .../Fixture/some_class.php.inc | 19 +++ .../IncreaseDeclareStrictTypesRectorTest.php | 28 ++++ .../config/configured_rule.php | 9 ++ .../NodeAnalyzer/DeclareStrictTypeFinder.php | 28 ++++ .../DeclareStrictTypesRector.php | 29 ++-- .../IncreaseDeclareStrictTypesRector.php | 135 ++++++++++++++++++ utils/Command/MissingInSetCommand.php | 2 + 8 files changed, 239 insertions(+), 20 deletions(-) create mode 100644 rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/IncreaseDeclareStrictTypesRector/Fixture/skip_already_with_strict_types.php.inc create mode 100644 rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/IncreaseDeclareStrictTypesRector/Fixture/some_class.php.inc create mode 100644 rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/IncreaseDeclareStrictTypesRector/IncreaseDeclareStrictTypesRectorTest.php create mode 100644 rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/IncreaseDeclareStrictTypesRector/config/configured_rule.php create mode 100644 rules/TypeDeclaration/NodeAnalyzer/DeclareStrictTypeFinder.php create mode 100644 rules/TypeDeclaration/Rector/StmtsAwareInterface/IncreaseDeclareStrictTypesRector.php diff --git a/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/IncreaseDeclareStrictTypesRector/Fixture/skip_already_with_strict_types.php.inc b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/IncreaseDeclareStrictTypesRector/Fixture/skip_already_with_strict_types.php.inc new file mode 100644 index 00000000000..7383a11e7d3 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/IncreaseDeclareStrictTypesRector/Fixture/skip_already_with_strict_types.php.inc @@ -0,0 +1,9 @@ + +----- +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/TypeDeclaration/Rector/StmtsAwareInterface/IncreaseDeclareStrictTypesRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/IncreaseDeclareStrictTypesRector/config/configured_rule.php new file mode 100644 index 00000000000..177749b095b --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/StmtsAwareInterface/IncreaseDeclareStrictTypesRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([IncreaseDeclareStrictTypesRector::class]); diff --git a/rules/TypeDeclaration/NodeAnalyzer/DeclareStrictTypeFinder.php b/rules/TypeDeclaration/NodeAnalyzer/DeclareStrictTypeFinder.php new file mode 100644 index 00000000000..775e3c5b8a3 --- /dev/null +++ b/rules/TypeDeclaration/NodeAnalyzer/DeclareStrictTypeFinder.php @@ -0,0 +1,28 @@ +declares as $declare) { + if ($declare->key->toString() === 'strict_types') { + return true; + } + } + + return false; + } +} diff --git a/rules/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector.php b/rules/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector.php index 2402114ea50..f19338031a7 100644 --- a/rules/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector.php +++ b/rules/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector.php @@ -15,6 +15,7 @@ use Rector\Contract\PhpParser\Node\StmtsAwareInterface; use Rector\PhpParser\Node\CustomNode\FileWithoutNamespace; use Rector\Rector\AbstractRector; +use Rector\TypeDeclaration\NodeAnalyzer\DeclareStrictTypeFinder; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -23,6 +24,11 @@ */ final class DeclareStrictTypesRector extends AbstractRector { + public function __construct( + private readonly DeclareStrictTypeFinder $declareStrictTypeFinder + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition('Add declare(strict_types=1) if missing', [ @@ -78,11 +84,9 @@ public function beforeTraverse(array $nodes): ?array $stmt = $currentStmt; } - if (! $stmt instanceof Stmt) { - return null; - } - - if ($this->shouldSkip($stmt)) { + // when first stmt is Declare_, verify if there is strict_types definition already, + // as multiple declare is allowed, with declare(strict_types=1) only allowed on very first stmt + if ($this->declareStrictTypeFinder->hasDeclareStrictTypes($stmt)) { return null; } @@ -117,19 +121,4 @@ public function refactor(Node $node): ?Node // workaroudn, as Rector now only hooks to specific nodes, not arrays return null; } - - private function shouldSkip(Stmt $stmt): bool - { - // when first stmt is Declare_, verify if there is strict_types definition already, - // as multiple declare is allowed, with declare(strict_types=1) only allowed on very first stmt - if ($stmt instanceof Declare_) { - foreach ($stmt->declares as $declare) { - if ($declare->key->toString() === 'strict_types') { - return true; - } - } - } - - return false; - } } diff --git a/rules/TypeDeclaration/Rector/StmtsAwareInterface/IncreaseDeclareStrictTypesRector.php b/rules/TypeDeclaration/Rector/StmtsAwareInterface/IncreaseDeclareStrictTypesRector.php new file mode 100644 index 00000000000..8711cd9f927 --- /dev/null +++ b/rules/TypeDeclaration/Rector/StmtsAwareInterface/IncreaseDeclareStrictTypesRector.php @@ -0,0 +1,135 @@ + 10, + ], + ), + ] + ); + } + + /** + * @param Node[] $nodes + * @return Node[]|null + */ + public function beforeTraverse(array $nodes): ?array + { + parent::beforeTraverse($nodes); + + $newStmts = $this->file->getNewStmts(); + if ($newStmts === []) { + return null; + } + + $rootStmt = \current($newStmts); + $stmt = $rootStmt; + + // skip classes without namespace for safety reasons + if ($rootStmt instanceof FileWithoutNamespace) { + return null; + } + + if ($this->declareStrictTypeFinder->hasDeclareStrictTypes($stmt)) { + return null; + } + + // keep change withing a limit + if ($this->changedItemCount >= $this->limit) { + return null; + } + + ++$this->changedItemCount; + + $strictTypesDeclare = $this->creteStrictTypesDeclare(); + + $rectorWithLineChange = new RectorWithLineChange(self::class, $stmt->getLine()); + $this->file->addRectorClassWithLine($rectorWithLineChange); + + return \array_merge([$strictTypesDeclare, new Nop()], $nodes); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [StmtsAwareInterface::class]; + } + + /** + * @param StmtsAwareInterface $node + */ + public function refactor(Node $node): ?Node + { + // workaround, as Rector now only hooks to specific nodes, not arrays + return null; + } + + public function configure(array $configuration): void + { + Assert::keyExists($configuration, self::LIMIT); + $this->limit = (int) $configuration[self::LIMIT]; + } + + private function creteStrictTypesDeclare(): Declare_ + { + $declareDeclare = new DeclareDeclare(new Identifier('strict_types'), new LNumber(1)); + return new Declare_([$declareDeclare]); + } +} diff --git a/utils/Command/MissingInSetCommand.php b/utils/Command/MissingInSetCommand.php index 0df03c8e373..ef587ee0cd7 100644 --- a/utils/Command/MissingInSetCommand.php +++ b/utils/Command/MissingInSetCommand.php @@ -21,6 +21,7 @@ use Rector\Privatization\Rector\Class_\FinalizeTestCaseClassRector; use Rector\TypeDeclaration\Rector\BooleanAnd\BinaryOpNullableToInstanceofRector; use Rector\TypeDeclaration\Rector\StmtsAwareInterface\DeclareStrictTypesRector; +use Rector\TypeDeclaration\Rector\StmtsAwareInterface\IncreaseDeclareStrictTypesRector; use Rector\TypeDeclaration\Rector\While_\WhileNullableToInstanceofRector; use Rector\Utils\Enum\RectorDirectoryToSetFileMap; use Rector\Utils\Finder\RectorClassFinder; @@ -45,6 +46,7 @@ final class MissingInSetCommand extends Command StrvalToTypeCastRector::class, BoolvalToTypeCastRector::class, FloatvalToTypeCastRector::class, + IncreaseDeclareStrictTypesRector::class, // changes behavior, should be applied on purpose regardless PHP 7.3 level JsonThrowOnErrorRector::class, // in confront with sub type safe belt detection on RemoveUseless*TagRector