From 9271588573b2566185e14e278b9c2f9a50b5ffb0 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Fri, 4 Oct 2019 23:46:23 +0200 Subject: [PATCH] [StrictCodeQuality] Add VarInlineAnnotationToAssertRector --- composer.json | 8 +- .../FuncCall/FunctionCallToConstantRector.php | 2 +- ...VersionCompareFuncCallToConstantRector.php | 24 ++-- .../FunctionCallToConstantRectorTest.php | 6 +- ...ionCompareFuncCallToConstantRectorTest.php | 3 - .../VarInlineAnnotationToAssertRector.php | 115 ++++++++++++++++++ .../Fixture/fixture.php.inc | 37 ++++++ .../Fixture/scalar.php.inc | 41 +++++++ .../VarInlineAnnotationToAssertRectorTest.php | 29 +++++ phpstan.neon | 2 + 10 files changed, 244 insertions(+), 23 deletions(-) create mode 100644 packages/StrictCodeQuality/src/Rector/Stmt/VarInlineAnnotationToAssertRector.php create mode 100644 packages/StrictCodeQuality/tests/Rector/Stmt/VarInlineAnnotationToAssertRector/Fixture/fixture.php.inc create mode 100644 packages/StrictCodeQuality/tests/Rector/Stmt/VarInlineAnnotationToAssertRector/Fixture/scalar.php.inc create mode 100644 packages/StrictCodeQuality/tests/Rector/Stmt/VarInlineAnnotationToAssertRector/VarInlineAnnotationToAssertRectorTest.php diff --git a/composer.json b/composer.json index b6d1e1227264..7b68c6dfde42 100644 --- a/composer.json +++ b/composer.json @@ -97,7 +97,8 @@ "Rector\\TypeDeclaration\\": "packages/TypeDeclaration/src", "Rector\\ZendToSymfony\\": "packages/ZendToSymfony/src", "Rector\\Utils\\DocumentationGenerator\\": "utils/DocumentationGenerator/src", - "Rector\\Utils\\RectorGenerator\\": "utils/RectorGenerator/src" + "Rector\\Utils\\RectorGenerator\\": "utils/RectorGenerator/src", + "Rector\\StrictCodeQuality\\": "packages/StrictCodeQuality/src" } }, "autoload-dev": { @@ -151,7 +152,8 @@ "Rector\\Symfony\\Tests\\": "packages/Symfony/tests", "Rector\\Twig\\Tests\\": "packages/Twig/tests", "Rector\\TypeDeclaration\\Tests\\": "packages/TypeDeclaration/tests", - "Rector\\ZendToSymfony\\Tests\\": "packages/ZendToSymfony/tests" + "Rector\\ZendToSymfony\\Tests\\": "packages/ZendToSymfony/tests", + "Rector\\StrictCodeQuality\\Tests\\": "packages/StrictCodeQuality/tests" }, "classmap": [ "packages/Symfony/tests/Rector/FrameworkBundle/AbstractToConstructorInjectionRectorSource", @@ -209,4 +211,4 @@ "dev-master": "0.6-dev" } } -} +} \ No newline at end of file diff --git a/packages/CodingStyle/src/Rector/FuncCall/FunctionCallToConstantRector.php b/packages/CodingStyle/src/Rector/FuncCall/FunctionCallToConstantRector.php index e1935c989c1c..07163ffe8acc 100644 --- a/packages/CodingStyle/src/Rector/FuncCall/FunctionCallToConstantRector.php +++ b/packages/CodingStyle/src/Rector/FuncCall/FunctionCallToConstantRector.php @@ -18,7 +18,7 @@ final class FunctionCallToConstantRector extends AbstractRector /** * @var string[]string */ - private $functionsToConstants; + private $functionsToConstants = []; /** * @param string[] $functionsToConstants diff --git a/packages/CodingStyle/src/Rector/FuncCall/VersionCompareFuncCallToConstantRector.php b/packages/CodingStyle/src/Rector/FuncCall/VersionCompareFuncCallToConstantRector.php index 564e30d66338..12f5bb6ced4c 100644 --- a/packages/CodingStyle/src/Rector/FuncCall/VersionCompareFuncCallToConstantRector.php +++ b/packages/CodingStyle/src/Rector/FuncCall/VersionCompareFuncCallToConstantRector.php @@ -85,7 +85,7 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - if (!$this->isName($node, 'version_compare')) { + if (! $this->isName($node, 'version_compare')) { return null; } @@ -93,7 +93,9 @@ public function refactor(Node $node): ?Node return null; } - if (!$this->isPhpVersionConstant($node->args[0]->value) && !$this->isPhpVersionConstant($node->args[1]->value)) { + if (! $this->isPhpVersionConstant($node->args[0]->value) && ! $this->isPhpVersionConstant( + $node->args[1]->value + )) { return null; } @@ -107,35 +109,35 @@ public function refactor(Node $node): ?Node return new $comparison($left, $right); } - private function isPhpVersionConstant(Expr $value): bool + private function isPhpVersionConstant(Expr $expr): bool { - if ($value instanceof ConstFetch && $value->name->toString() === 'PHP_VERSION') { + if ($expr instanceof ConstFetch && $expr->name->toString() === 'PHP_VERSION') { return true; } return false; } - private function getNewNodeForArg(Expr $value): Node + private function getNewNodeForArg(Expr $expr): Node { - if ($this->isPhpVersionConstant($value)) { + if ($this->isPhpVersionConstant($expr)) { return new ConstFetch(new Name('PHP_VERSION_ID')); } - return $this->getVersionNumberFormVersionString($value); + return $this->getVersionNumberFormVersionString($expr); } - private function getVersionNumberFormVersionString(Expr $value): LNumber + private function getVersionNumberFormVersionString(Expr $expr): LNumber { - if (! $value instanceof String_) { + if (! $expr instanceof String_) { throw new ShouldNotHappenException(); } - if (! preg_match('/^\d+\.\d+\.\d+$/', $value->value)) { + if (! preg_match('#^\d+\.\d+\.\d+$#', $expr->value)) { throw new ShouldNotHappenException(); } - $versionParts = explode('.', $value->value); + $versionParts = explode('.', $expr->value); return new LNumber((int) $versionParts[0] * 10000 + (int) $versionParts[1] * 100 + (int) $versionParts[2]); } diff --git a/packages/CodingStyle/tests/Rector/FuncCall/FunctionCallToConstantRector/FunctionCallToConstantRectorTest.php b/packages/CodingStyle/tests/Rector/FuncCall/FunctionCallToConstantRector/FunctionCallToConstantRectorTest.php index ce572941a1db..8500eaa34d7c 100644 --- a/packages/CodingStyle/tests/Rector/FuncCall/FunctionCallToConstantRector/FunctionCallToConstantRectorTest.php +++ b/packages/CodingStyle/tests/Rector/FuncCall/FunctionCallToConstantRector/FunctionCallToConstantRectorTest.php @@ -3,10 +3,7 @@ namespace Rector\CodingStyle\Tests\Rector\FuncCall\FunctionCallToConstantRector; use Iterator; -use Rector\Autodiscovery\Rector\FileSystem\MoveServicesBySuffixToDirectoryRector; -use Rector\CodingStyle\Rector\FuncCall\ConsistentImplodeRector; use Rector\CodingStyle\Rector\FuncCall\FunctionCallToConstantRector; -use Rector\CodingStyle\Rector\FuncCall\VersionCompareFuncCallToConstantRector; use Rector\Testing\PHPUnit\AbstractRectorTestCase; final class FunctionCallToConstantRectorTest extends AbstractRectorTestCase @@ -22,7 +19,6 @@ public function test(string $file): void public function provideDataForTest(): Iterator { yield [__DIR__ . '/Fixture/fixture.php.inc']; - } protected function getRectorsWithConfiguration(): array @@ -31,7 +27,7 @@ protected function getRectorsWithConfiguration(): array FunctionCallToConstantRector::class => [ '$functionsToConstants' => [ 'php_sapi_name' => 'PHP_SAPI', - 'pi' => 'M_PI' + 'pi' => 'M_PI', ], ], ]; diff --git a/packages/CodingStyle/tests/Rector/FuncCall/VersionCompareFuncCallToConstantRector/VersionCompareFuncCallToConstantRectorTest.php b/packages/CodingStyle/tests/Rector/FuncCall/VersionCompareFuncCallToConstantRector/VersionCompareFuncCallToConstantRectorTest.php index f22a8a84bdbb..90ebb4cf9f09 100644 --- a/packages/CodingStyle/tests/Rector/FuncCall/VersionCompareFuncCallToConstantRector/VersionCompareFuncCallToConstantRectorTest.php +++ b/packages/CodingStyle/tests/Rector/FuncCall/VersionCompareFuncCallToConstantRector/VersionCompareFuncCallToConstantRectorTest.php @@ -3,8 +3,6 @@ namespace Rector\CodingStyle\Tests\Rector\FuncCall\VersionCompareFuncCallToConstantRector; use Iterator; -use Rector\Autodiscovery\Rector\FileSystem\MoveServicesBySuffixToDirectoryRector; -use Rector\CodingStyle\Rector\FuncCall\ConsistentImplodeRector; use Rector\CodingStyle\Rector\FuncCall\VersionCompareFuncCallToConstantRector; use Rector\Testing\PHPUnit\AbstractRectorTestCase; @@ -22,7 +20,6 @@ public function provideDataForTest(): Iterator { yield [__DIR__ . '/Fixture/version-compare.php.inc']; yield [__DIR__ . '/Fixture/skip-version-compare.php.inc']; - } protected function getRectorClass(): string diff --git a/packages/StrictCodeQuality/src/Rector/Stmt/VarInlineAnnotationToAssertRector.php b/packages/StrictCodeQuality/src/Rector/Stmt/VarInlineAnnotationToAssertRector.php new file mode 100644 index 000000000000..12a66fb7c751 --- /dev/null +++ b/packages/StrictCodeQuality/src/Rector/Stmt/VarInlineAnnotationToAssertRector.php @@ -0,0 +1,115 @@ +call(); + } +} +PHP + , + <<<'PHP' +class SomeClass +{ + public function run() + { + /** @var SpecificClass $value */ + assert($value instanceof SpecificClass); + $value->call(); + } +} +PHP + ), + ]); + } + + /** + * @return string[] + */ + public function getNodeTypes(): array + { + return [Stmt::class]; + } + + /** + * @param Stmt $node + */ + public function refactor(Node $node): ?Node + { + $phpDocInfo = $this->getPhpDocInfo($node); + if ($phpDocInfo === null) { + return null; + } + + $variable = $this->betterNodeFinder->findFirstInstanceOf($node, Variable::class); + if (! $variable instanceof Variable) { + return null; + } + + $variable->setAttribute('comments', []); + $type = $phpDocInfo->getVarType(); + + $assertFuncCall = null; + if ($type instanceof ObjectType) { + $instanceOf = new Instanceof_($variable, new FullyQualified($type->getClassName())); + $assertFuncCall = $this->createFunction('assert', [$instanceOf]); + } + + if ($type instanceof IntegerType) { + $isInt = $this->createFunction('is_int', [$variable]); + $assertFuncCall = $this->createFunction('assert', [$isInt]); + } + + if ($type instanceof StringType) { + $isInt = $this->createFunction('is_string', [$variable]); + $assertFuncCall = $this->createFunction('assert', [$isInt]); + } + + if ($type instanceof BooleanType) { + $isInt = $this->createFunction('is_bool', [$variable]); + $assertFuncCall = $this->createFunction('assert', [$isInt]); + } + + if ($assertFuncCall === null) { + return null; + } + + $this->addNodeBeforeNode($assertFuncCall, $node); + + // cleanup @var annotation + $varTagValueNode = $phpDocInfo->getByType(VarTagValueNode::class); + $phpDocInfo->removeTagValueNodeFromNode($varTagValueNode); + + $this->docBlockManipulator->updateNodeWithPhpDocInfo($node, $phpDocInfo); + + return $node; + } +} diff --git a/packages/StrictCodeQuality/tests/Rector/Stmt/VarInlineAnnotationToAssertRector/Fixture/fixture.php.inc b/packages/StrictCodeQuality/tests/Rector/Stmt/VarInlineAnnotationToAssertRector/Fixture/fixture.php.inc new file mode 100644 index 000000000000..2faf1a796a94 --- /dev/null +++ b/packages/StrictCodeQuality/tests/Rector/Stmt/VarInlineAnnotationToAssertRector/Fixture/fixture.php.inc @@ -0,0 +1,37 @@ +call(); + } +} + +class SpecificClass +{ +} + +?> +----- +call(); + } +} + +class SpecificClass +{ +} + +?> diff --git a/packages/StrictCodeQuality/tests/Rector/Stmt/VarInlineAnnotationToAssertRector/Fixture/scalar.php.inc b/packages/StrictCodeQuality/tests/Rector/Stmt/VarInlineAnnotationToAssertRector/Fixture/scalar.php.inc new file mode 100644 index 000000000000..47050ed1241a --- /dev/null +++ b/packages/StrictCodeQuality/tests/Rector/Stmt/VarInlineAnnotationToAssertRector/Fixture/scalar.php.inc @@ -0,0 +1,41 @@ +call(); + + /** @var string $value */ + $value->call(); + + /** @var bool $value */ + $value->call(); + } +} + +?> +----- +call(); + assert(is_string($value)); + + $value->call(); + assert(is_bool($value)); + + $value->call(); + } +} + +?> diff --git a/packages/StrictCodeQuality/tests/Rector/Stmt/VarInlineAnnotationToAssertRector/VarInlineAnnotationToAssertRectorTest.php b/packages/StrictCodeQuality/tests/Rector/Stmt/VarInlineAnnotationToAssertRector/VarInlineAnnotationToAssertRectorTest.php new file mode 100644 index 000000000000..02c00ec32adf --- /dev/null +++ b/packages/StrictCodeQuality/tests/Rector/Stmt/VarInlineAnnotationToAssertRector/VarInlineAnnotationToAssertRectorTest.php @@ -0,0 +1,29 @@ +doTestFile($file); + } + + public function provideDataForTest(): Iterator + { + yield [__DIR__ . '/Fixture/fixture.php.inc']; + yield [__DIR__ . '/Fixture/scalar.php.inc']; + } + + protected function getRectorClass(): string + { + return VarInlineAnnotationToAssertRector::class; + } +} diff --git a/phpstan.neon b/phpstan.neon index 99595a8a79fc..3578f0513200 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -198,3 +198,5 @@ parameters: - '#Method Rector\\BetterPhpDocParser\\PhpDocNodeFactory\\JMS\\SerializerTypePhpDocNodeFactory\:\:resolveTypeAnnotation\(\) should return JMS\\Serializer\\Annotation\\Type\|null but returns object\|null#' - '#Method Rector\\BetterPhpDocParser\\PhpDocNodeFactory\\JMS\\SerializerTypePhpDocNodeFactory\:\:resolveTypeAnnotation\(\) should return JMS\\Serializer\\Annotation\\Type\|null but returns Doctrine\\ORM\\Mapping\\Annotation\|Symfony\\Component\\Validator\\Constraint\|null#' + + - '#Parameter \#1 \$phpDocTagValueNode of method Rector\\BetterPhpDocParser\\PhpDocInfo\\PhpDocInfo\:\:removeTagValueNodeFromNode\(\) expects PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTagValueNode, PHPStan\\PhpDocParser\\Ast\\PhpDoc\\VarTagValueNode\|null given#'