add UnionTypesRector

This commit is contained in:
TomasVotruba 2019-11-17 14:06:32 +01:00
parent a9cd107fb9
commit b930046917
5 changed files with 199 additions and 2 deletions

View File

@ -100,7 +100,8 @@
"Rector\\Utils\\RectorGenerator\\": "utils/RectorGenerator/src",
"Rector\\StrictCodeQuality\\": "packages/StrictCodeQuality/src",
"Rector\\DynamicTypeAnalysis\\": "packages/DynamicTypeAnalysis/src",
"Rector\\PhpDeglobalize\\": "packages/PhpDeglobalize/src"
"Rector\\PhpDeglobalize\\": "packages/PhpDeglobalize/src",
"Rector\\Php80\\": "packages/Php80/src"
}
},
"autoload-dev": {
@ -157,7 +158,8 @@
"Rector\\ZendToSymfony\\Tests\\": "packages/ZendToSymfony/tests",
"Rector\\StrictCodeQuality\\Tests\\": "packages/StrictCodeQuality/tests",
"Rector\\DynamicTypeAnalysis\\Tests\\": "packages/DynamicTypeAnalysis/tests",
"Rector\\PhpDeglobalize\\Tests\\": "packages/PhpDeglobalize/tests"
"Rector\\PhpDeglobalize\\Tests\\": "packages/PhpDeglobalize/tests",
"Rector\\Php80\\Tests\\": "packages/Php80/tests"
},
"classmap": [
"packages/Symfony/tests/Rector/FrameworkBundle/AbstractToConstructorInjectionRectorSource",

View File

@ -0,0 +1,131 @@
<?php
declare(strict_types=1);
namespace Rector\Php80\Rector\FunctionLike;
use PhpParser\Node;
use PhpParser\Node\Expr\ArrowFunction;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\UnionType as PhpParserUnionType;
use PHPStan\Type\UnionType;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
/**
* @see \Rector\Php80\Tests\Rector\FunctionLike\UnionTypesRector\UnionTypesRectorTest
*/
final class UnionTypesRector extends AbstractRector
{
public function getDefinition(): RectorDefinition
{
return new RectorDefinition(
'Change docs types to union types, where possible (properties are covered by TypedPropertiesRector)',
[
new CodeSample(
<<<'PHP'
class SomeClass {
/**
* @param array|int $number
* @return bool|float
*/
public function go($number)
{
}
}
PHP
,
<<<'PHP'
class SomeClass {
/**
* @param array|int $number
* @return bool|float
*/
public function go(array|int $number): bool|float
{
}
}
PHP
),
]
);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [FunctionLike::class];
}
/**
* @param ClassMethod|Function_|Node\Expr\Closure|ArrowFunction $node
*/
public function refactor(Node $node): ?Node
{
$phpDocInfo = $this->getPhpDocInfo($node);
if ($phpDocInfo === null) {
return null;
}
$this->refactorParamTypes($node, $phpDocInfo);
$this->refactorReturnType($node, $phpDocInfo);
return $node;
}
/**
* @param ClassMethod|Function_|Node\Expr\Closure|ArrowFunction $functionLike
*/
private function refactorReturnType(FunctionLike $functionLike, PhpDocInfo $phpDocInfo): void
{
// do not override existing return type
if ($functionLike->getReturnType() !== null) {
return;
}
$returnType = $phpDocInfo->getReturnType();
if (! $returnType instanceof UnionType) {
return;
}
$phpParserUnionType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($returnType);
if (! $phpParserUnionType instanceof PhpParserUnionType) {
return;
}
$functionLike->returnType = $phpParserUnionType;
}
/**
* @param ClassMethod|Function_|Node\Expr\Closure|ArrowFunction $functionLike
*/
private function refactorParamTypes(FunctionLike $functionLike, PhpDocInfo $phpDocInfo): void
{
foreach ($functionLike->params as $param) {
if ($param->type !== null) {
continue;
}
/** @var string $paramName */
$paramName = $this->getName($param->var);
$paramType = $phpDocInfo->getParamType($paramName);
if (! $paramType instanceof UnionType) {
continue;
}
$phpParserUnionType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($paramType);
if (! $phpParserUnionType instanceof PhpParserUnionType) {
continue;
}
$param->type = $phpParserUnionType;
}
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace Rector\Php80\Tests\Rector\FunctionLike\UnionTypesRector\Fixture;
class SomeClass {
/**
* @param array|int $number
* @return bool|float
*/
public function go($number)
{
}
}
?>
-----
<?php
namespace Rector\Php80\Tests\Rector\FunctionLike\UnionTypesRector\Fixture;
class SomeClass {
/**
* @param array|int $number
* @return bool|float
*/
public function go(array|int $number): bool|float
{
}
}
?>

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Rector\Php80\Tests\Rector\FunctionLike\UnionTypesRector;
use Iterator;
use Rector\Php80\Rector\FunctionLike\UnionTypesRector;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
final class UnionTypesRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideDataForTest()
*/
public function test(string $file): void
{
$this->doTestFile($file);
}
public function provideDataForTest(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
protected function getRectorClass(): string
{
return UnionTypesRector::class;
}
}

View File

@ -7,6 +7,7 @@ namespace Rector\Utils\RectorGenerator\Configuration;
use Nette\Utils\Strings;
use PhpParser\Node;
use Rector\Exception\FileSystem\FileNotFoundException;
use Rector\Exception\ShouldNotHappenException;
use Rector\Set\Set;
use Rector\Utils\RectorGenerator\Exception\ConfigurationException;
use Rector\Utils\RectorGenerator\Node\NodeClassProvider;
@ -114,6 +115,8 @@ final class ConfigurationFactory
continue 2;
}
}
throw new ShouldNotHappenException(sprintf('Node endings with "%s" was not found', $nodeType));
}
return array_unique($fqnNodeTypes);