mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-10 13:12:23 +00:00
cover nullable type
This commit is contained in:
parent
e9fea42472
commit
14474809d9
|
@ -4,15 +4,8 @@ declare(strict_types=1);
|
|||
|
||||
namespace Rector\FamilyTree\Reflection;
|
||||
|
||||
use PhpParser\Node\Expr;
|
||||
|
||||
final class FamilyRelationsAnalyzer
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private const KNOWN_PARENT_CLASSES = [\PhpParser\Node::class, Expr::class];
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
|
@ -36,10 +29,6 @@ final class FamilyRelationsAnalyzer
|
|||
|
||||
public function isParentClass(string $class): bool
|
||||
{
|
||||
if (in_array($class, self::KNOWN_PARENT_CLASSES, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (get_declared_classes() as $declaredClass) {
|
||||
if ($declaredClass === $class) {
|
||||
continue;
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\PHPStanStaticTypeMapper\Utils;
|
||||
|
||||
use PHPStan\Type\NullType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\UnionType;
|
||||
|
||||
final class TypeUnwrapper
|
||||
{
|
||||
/**
|
||||
* E.g. null|ClassType → ClassType
|
||||
*/
|
||||
public function unwrapNullableType(UnionType $unionType): ?Type
|
||||
{
|
||||
if (count($unionType->getTypes()) !== 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $unionType->isSuperTypeOf(new NullType())->yes()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($unionType->getTypes() as $unionedType) {
|
||||
if ($unionedType instanceof NullType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $unionedType;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -5,7 +5,6 @@ declare(strict_types=1);
|
|||
namespace Rector\Naming\Naming;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PHPStan\Type\NullType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\StaticType;
|
||||
use PHPStan\Type\Type;
|
||||
|
@ -13,6 +12,7 @@ use PHPStan\Type\TypeWithClassName;
|
|||
use PHPStan\Type\UnionType;
|
||||
use Rector\PHPStan\Type\SelfObjectType;
|
||||
use Rector\PHPStan\Type\ShortenedObjectType;
|
||||
use Rector\PHPStanStaticTypeMapper\Utils\TypeUnwrapper;
|
||||
|
||||
final class PropertyNaming
|
||||
{
|
||||
|
@ -26,10 +26,20 @@ final class PropertyNaming
|
|||
*/
|
||||
private const INTERFACE = 'Interface';
|
||||
|
||||
/**
|
||||
* @var TypeUnwrapper
|
||||
*/
|
||||
private $typeUnwrapper;
|
||||
|
||||
public function __construct(TypeUnwrapper $typeUnwrapper)
|
||||
{
|
||||
$this->typeUnwrapper = $typeUnwrapper;
|
||||
}
|
||||
|
||||
public function getExpectedNameFromType(Type $type): ?string
|
||||
{
|
||||
if ($type instanceof UnionType) {
|
||||
$type = $this->unwrapNullableType($type);
|
||||
$type = $this->typeUnwrapper->unwrapNullableType($type);
|
||||
}
|
||||
|
||||
if (! $type instanceof TypeWithClassName) {
|
||||
|
@ -155,30 +165,6 @@ final class PropertyNaming
|
|||
return $shortClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* E.g. null|ClassType → ClassType
|
||||
*/
|
||||
private function unwrapNullableType(UnionType $unionType): ?Type
|
||||
{
|
||||
if (count($unionType->getTypes()) !== 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $unionType->isSuperTypeOf(new NullType())->yes()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($unionType->getTypes() as $unionedType) {
|
||||
if ($unionedType instanceof NullType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $unionedType;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function isNumberOrUpper(string $char): bool
|
||||
{
|
||||
if (ctype_upper($char)) {
|
||||
|
|
|
@ -38,9 +38,9 @@ final class VarTagValueNodeRenamer
|
|||
{
|
||||
$phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO);
|
||||
|
||||
$parent = $node->getAttribute(AttributeKey::PARENT_NODE);
|
||||
if ($parent instanceof Expression) {
|
||||
$expressionPhpDocInfo = $parent->getAttribute(AttributeKey::PHP_DOC_INFO);
|
||||
$expression = $node->getAttribute(AttributeKey::CURRENT_STATEMENT);
|
||||
if ($expression instanceof Node) {
|
||||
$expressionPhpDocInfo = $expression->getAttribute(AttributeKey::PHP_DOC_INFO);
|
||||
}
|
||||
|
||||
return $expressionPhpDocInfo ?? $phpDocInfo;
|
||||
|
|
|
@ -10,7 +10,9 @@ use PhpParser\Node\Expr\Assign;
|
|||
use PhpParser\Node\Expr\FuncCall;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PHPStan\Type\TypeWithClassName;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\RectorDefinition\CodeSample;
|
||||
use Rector\Core\RectorDefinition\RectorDefinition;
|
||||
|
@ -22,12 +24,18 @@ use Rector\Naming\NamingConvention\NamingConventionAnalyzer;
|
|||
use Rector\Naming\PhpDoc\VarTagValueNodeRenamer;
|
||||
use Rector\Naming\ValueObject\VariableAndCallAssign;
|
||||
use Rector\Naming\VariableRenamer;
|
||||
use Rector\PHPStanStaticTypeMapper\Utils\TypeUnwrapper;
|
||||
|
||||
/**
|
||||
* @see \Rector\Naming\Tests\Rector\Assign\RenameVariableToMatchGetMethodNameRector\RenameVariableToMatchGetMethodNameRectorTest
|
||||
*/
|
||||
final class RenameVariableToMatchGetMethodNameRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private const ALLOWED_PARENT_TYPES = [ClassLike::class];
|
||||
|
||||
/**
|
||||
* @var ExpectedNameResolver
|
||||
*/
|
||||
|
@ -63,6 +71,11 @@ final class RenameVariableToMatchGetMethodNameRector extends AbstractRector
|
|||
*/
|
||||
private $varTagValueNodeRenamer;
|
||||
|
||||
/**
|
||||
* @var TypeUnwrapper
|
||||
*/
|
||||
private $typeUtils;
|
||||
|
||||
public function __construct(
|
||||
ExpectedNameResolver $expectedNameResolver,
|
||||
VariableRenamer $variableRenamer,
|
||||
|
@ -70,7 +83,8 @@ final class RenameVariableToMatchGetMethodNameRector extends AbstractRector
|
|||
FamilyRelationsAnalyzer $familyRelationsAnalyzer,
|
||||
VariableAndCallAssignMatcher $variableAndCallAssignMatcher,
|
||||
NamingConventionAnalyzer $namingConventionAnalyzer,
|
||||
VarTagValueNodeRenamer $varTagValueNodeRenamer
|
||||
VarTagValueNodeRenamer $varTagValueNodeRenamer,
|
||||
TypeUnwrapper $typeUtils
|
||||
) {
|
||||
$this->expectedNameResolver = $expectedNameResolver;
|
||||
$this->variableRenamer = $variableRenamer;
|
||||
|
@ -79,6 +93,7 @@ final class RenameVariableToMatchGetMethodNameRector extends AbstractRector
|
|||
$this->variableAndCallAssignMatcher = $variableAndCallAssignMatcher;
|
||||
$this->namingConventionAnalyzer = $namingConventionAnalyzer;
|
||||
$this->varTagValueNodeRenamer = $varTagValueNodeRenamer;
|
||||
$this->typeUtils = $typeUtils;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
|
@ -135,25 +150,25 @@ PHP
|
|||
return null;
|
||||
}
|
||||
|
||||
return $this->renameVariable($variableAndCallAssign, $expectedName);
|
||||
$this->renameVariable($variableAndCallAssign, $expectedName);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function renameVariable(VariableAndCallAssign $variableAndCallAssign, string $newName): Assign
|
||||
private function renameVariable(VariableAndCallAssign $variableAndCallAssign, string $expectedName): void
|
||||
{
|
||||
$this->varTagValueNodeRenamer->renameAssignVarTagVariableName(
|
||||
$variableAndCallAssign->getAssign(),
|
||||
$variableAndCallAssign->getVariableName(),
|
||||
$newName
|
||||
$expectedName
|
||||
);
|
||||
|
||||
$this->variableRenamer->renameVariableInFunctionLike(
|
||||
$variableAndCallAssign->getFunctionLike(),
|
||||
$variableAndCallAssign->getAssign(),
|
||||
$variableAndCallAssign->getVariableName(),
|
||||
$newName
|
||||
$expectedName
|
||||
);
|
||||
|
||||
return $variableAndCallAssign->getAssign();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -162,10 +177,19 @@ PHP
|
|||
private function isClassTypeWithChildren(Expr $expr): bool
|
||||
{
|
||||
$callStaticType = $this->getStaticType($expr);
|
||||
|
||||
if ($callStaticType instanceof UnionType) {
|
||||
$callStaticType = $this->typeUtils->unwrapNullableType($callStaticType);
|
||||
}
|
||||
|
||||
if (! $callStaticType instanceof TypeWithClassName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (in_array($callStaticType->getClassName(), self::ALLOWED_PARENT_TYPES, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->familyRelationsAnalyzer->isParentClass($callStaticType->getClassName());
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ use PhpParser\Node\FunctionLike;
|
|||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use Rector\Core\PhpParser\NodeTraverser\CallableNodeTraverser;
|
||||
use Rector\Naming\PhpDoc\VarTagValueNodeRenamer;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
|
||||
final class VariableRenamer
|
||||
|
@ -26,10 +27,19 @@ final class VariableRenamer
|
|||
*/
|
||||
private $nodeNameResolver;
|
||||
|
||||
public function __construct(CallableNodeTraverser $callableNodeTraverser, NodeNameResolver $nodeNameResolver)
|
||||
{
|
||||
/**
|
||||
* @var VarTagValueNodeRenamer
|
||||
*/
|
||||
private $varTagValueNodeRenamer;
|
||||
|
||||
public function __construct(
|
||||
CallableNodeTraverser $callableNodeTraverser,
|
||||
NodeNameResolver $nodeNameResolver,
|
||||
VarTagValueNodeRenamer $varTagValueNodeRenamer
|
||||
) {
|
||||
$this->callableNodeTraverser = $callableNodeTraverser;
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->varTagValueNodeRenamer = $varTagValueNodeRenamer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -64,6 +74,7 @@ final class VariableRenamer
|
|||
}
|
||||
|
||||
$node->name = $expectedName;
|
||||
$this->varTagValueNodeRenamer->renameAssignVarTagVariableName($node, $oldName, $expectedName);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Naming\Tests\Rector\Assign\RenameVariableToMatchGetMethodNameRector\Fixture;
|
||||
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Interface_;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
class RenameVariableExpression
|
||||
{
|
||||
private function shouldSkip(ClassMethod $classMethod): bool
|
||||
{
|
||||
/** @var ClassLike|null $class */
|
||||
$class = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if ($class === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @var Interface_ $class */
|
||||
return ! (bool) $class->extends;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Naming\Tests\Rector\Assign\RenameVariableToMatchGetMethodNameRector\Fixture;
|
||||
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Interface_;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
class RenameVariableExpression
|
||||
{
|
||||
private function shouldSkip(ClassMethod $classMethod): bool
|
||||
{
|
||||
/** @var ClassLike|null $classLike */
|
||||
$classLike = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if ($classLike === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @var Interface_ $classLike */
|
||||
return ! (bool) $classLike->extends;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Naming\Tests\Rector\Assign\RenameVariableToMatchGetMethodNameRector\Fixture;
|
||||
|
||||
use PhpParser\Node\Expr\Array_;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
class SkipParentNode
|
||||
{
|
||||
public function run(Array_ $array)
|
||||
{
|
||||
$parent = $array->getAttribute(AttributeKey::PARENT_NODE);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user