mirror of
https://github.com/rectorphp/rector.git
synced 2025-01-19 14:27:14 +01:00
improve VariableTypeResolver
This commit is contained in:
parent
a9533b7ecb
commit
ac40adfd9f
@ -5,16 +5,22 @@ namespace Rector\NodeTypeResolver;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Broker\Broker;
|
||||
use PHPStan\TrinaryLogic;
|
||||
use PHPStan\Type\IntersectionType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\ThisType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\BetterPhpDocParser\NodeAnalyzer\DocBlockAnalyzer;
|
||||
use Rector\Node\Attribute;
|
||||
use Rector\NodeTypeResolver\Contract\PerNodeTypeResolver\PerNodeTypeResolverInterface;
|
||||
use Rector\NodeTypeResolver\Reflection\ClassReflectionTypesResolver;
|
||||
use Rector\Php\TypeAnalyzer;
|
||||
use Symplify\PackageBuilder\Reflection\PrivatesAccessor;
|
||||
|
||||
final class NodeTypeResolver
|
||||
{
|
||||
@ -28,9 +34,24 @@ final class NodeTypeResolver
|
||||
*/
|
||||
private $classReflectionTypesResolver;
|
||||
|
||||
public function __construct(ClassReflectionTypesResolver $classReflectionTypesResolver)
|
||||
{
|
||||
/**
|
||||
* @var DocBlockAnalyzer
|
||||
*/
|
||||
private $docBlockAnalyzer;
|
||||
|
||||
/**
|
||||
* @var TypeAnalyzer
|
||||
*/
|
||||
private $typeAnalyzer;
|
||||
|
||||
public function __construct(
|
||||
ClassReflectionTypesResolver $classReflectionTypesResolver,
|
||||
DocBlockAnalyzer $docBlockAnalyzer,
|
||||
TypeAnalyzer $typeAnalyzer
|
||||
) {
|
||||
$this->classReflectionTypesResolver = $classReflectionTypesResolver;
|
||||
$this->docBlockAnalyzer = $docBlockAnalyzer;
|
||||
$this->typeAnalyzer = $typeAnalyzer;
|
||||
}
|
||||
|
||||
public function addPerNodeTypeResolver(PerNodeTypeResolverInterface $perNodeTypeResolver): void
|
||||
@ -52,6 +73,11 @@ final class NodeTypeResolver
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($node instanceof Param) {
|
||||
// @todo resolve parents etc.
|
||||
return [$node->type->toString()];
|
||||
}
|
||||
|
||||
if ($node instanceof Variable) {
|
||||
return $this->resolveVariableNode($node, $nodeScope);
|
||||
}
|
||||
@ -60,6 +86,25 @@ final class NodeTypeResolver
|
||||
return $this->resolveExprNode($node);
|
||||
}
|
||||
|
||||
if ($node instanceof Property) {
|
||||
// doc
|
||||
$propertyTypes = $this->docBlockAnalyzer->getVarTypes($node);
|
||||
if ($propertyTypes === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$propertyTypes = $this->filterOutScalarTypes($propertyTypes);
|
||||
|
||||
/** @var Broker $broker */
|
||||
$broker = (new PrivatesAccessor())->getPrivateProperty($nodeScope, 'broker');
|
||||
foreach ($propertyTypes as $propertyType) {
|
||||
$propertyClassReflection = $broker->getClass($propertyType);
|
||||
$propertyTypes += $this->classReflectionTypesResolver->resolve($propertyClassReflection);
|
||||
}
|
||||
|
||||
return $propertyTypes;
|
||||
}
|
||||
|
||||
$nodeClass = get_class($node);
|
||||
if (isset($this->perNodeTypeResolvers[$nodeClass])) {
|
||||
return $this->perNodeTypeResolvers[$nodeClass]->resolve($node);
|
||||
@ -82,6 +127,7 @@ final class NodeTypeResolver
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo make use of recursion
|
||||
* @return string[]
|
||||
*/
|
||||
private function resolveObjectTypesToStrings(Type $type): array
|
||||
@ -94,7 +140,6 @@ final class NodeTypeResolver
|
||||
|
||||
if ($type instanceof UnionType) {
|
||||
foreach ($type->getTypes() as $type) {
|
||||
// @todo recursion
|
||||
if ($type instanceof ObjectType) {
|
||||
$types[] = $type->getClassName();
|
||||
}
|
||||
@ -103,7 +148,6 @@ final class NodeTypeResolver
|
||||
|
||||
if ($type instanceof IntersectionType) {
|
||||
foreach ($type->getTypes() as $type) {
|
||||
// @todo recursion
|
||||
if ($type instanceof ObjectType) {
|
||||
$types[] = $type->getClassName();
|
||||
}
|
||||
@ -128,9 +172,52 @@ final class NodeTypeResolver
|
||||
return $this->classReflectionTypesResolver->resolve($nodeScope->getClassReflection());
|
||||
}
|
||||
|
||||
return $this->resolveObjectTypesToStrings($type);
|
||||
$types = $this->resolveObjectTypesToStrings($type);
|
||||
|
||||
// complete parents
|
||||
$broker = (new PrivatesAccessor())->getPrivateProperty($nodeScope, 'broker');
|
||||
foreach ($types as $type) {
|
||||
$propertyClassReflection = $broker->getClass($type);
|
||||
$types = array_merge($types, $this->classReflectionTypesResolver->resolve($propertyClassReflection));
|
||||
}
|
||||
|
||||
return array_unique($types);
|
||||
}
|
||||
|
||||
return [];
|
||||
// get from annotation
|
||||
$variableTypes = $this->docBlockAnalyzer->getVarTypes($variableNode);
|
||||
|
||||
$broker = (new PrivatesAccessor())->getPrivateProperty($nodeScope, 'broker');
|
||||
foreach ($variableTypes as $i => $type) {
|
||||
if (! class_exists($type)) {
|
||||
unset($variableTypes[$i]);
|
||||
continue;
|
||||
}
|
||||
$propertyClassReflection = $broker->getClass($type);
|
||||
$variableTypes = array_merge(
|
||||
$variableTypes,
|
||||
$this->classReflectionTypesResolver->resolve($propertyClassReflection)
|
||||
);
|
||||
}
|
||||
|
||||
return array_unique($variableTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $propertyTypes
|
||||
* @return string[]
|
||||
*/
|
||||
private function filterOutScalarTypes(array $propertyTypes): array
|
||||
{
|
||||
foreach ($propertyTypes as $key => $type) {
|
||||
if (! $this->typeAnalyzer->isPhpReservedType($type)) {
|
||||
continue;
|
||||
}
|
||||
unset($propertyTypes[$key]);
|
||||
}
|
||||
if ($propertyTypes === ['null']) {
|
||||
return [];
|
||||
}
|
||||
return $propertyTypes;
|
||||
}
|
||||
}
|
||||
|
@ -37,8 +37,15 @@ final class FormIsValidRector extends AbstractRector
|
||||
'Adds `$form->isSubmitted()` validatoin to all `$form->isValid()` calls in Form in Symfony',
|
||||
[
|
||||
new CodeSample(
|
||||
'if ($form->isValid()) { ... };',
|
||||
'if ($form->isSubmitted() && $form->isValid()) { ... };'
|
||||
<<<'CODE_SAMPLE'
|
||||
if ($form->isValid()) {
|
||||
}
|
||||
CODE_SAMPLE
|
||||
,
|
||||
<<<'CODE_SAMPLE'
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
}
|
||||
CODE_SAMPLE
|
||||
),
|
||||
]
|
||||
);
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php declare (strict_types=1);
|
||||
|
||||
namespace Rector\Tests\FormIsValidRector;
|
||||
|
||||
class SomeController
|
||||
{
|
||||
public function action()
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php declare (strict_types=1);
|
||||
|
||||
namespace Rector\Tests\FormIsValidRector;
|
||||
|
||||
class SomeController
|
||||
{
|
||||
public function action()
|
||||
|
@ -125,6 +125,7 @@ CODE_SAMPLE
|
||||
private function refactorVariableNode(Variable $variableNode): void
|
||||
{
|
||||
$match = $this->matchOriginAndNewType($variableNode);
|
||||
|
||||
if (! $match) {
|
||||
return;
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace SomeNamespace;
|
||||
|
||||
class ActionClass
|
||||
class SecondActionClass
|
||||
{
|
||||
public function someFunction(string $name)
|
||||
{
|
||||
|
@ -4,7 +4,7 @@ namespace SomeNamespace;
|
||||
|
||||
use Rector\Tests\Rector\DomainDrivenDesign\ValueObjectRemoverRector\Source\SomeValueObject;
|
||||
|
||||
class ActionClass
|
||||
class ThirdActionClass
|
||||
{
|
||||
/**
|
||||
* @param null|string $name
|
||||
|
@ -4,7 +4,7 @@ namespace SomeNamespace;
|
||||
|
||||
use Rector\Tests\Rector\DomainDrivenDesign\ValueObjectRemoverRector\Source\SomeValueObject;
|
||||
|
||||
class ActionClass
|
||||
class FourthActionClass
|
||||
{
|
||||
public function someFunction(?string $name): ?string
|
||||
{
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace SomeNamespace;
|
||||
|
||||
class ActionClass
|
||||
class SecondActionClass
|
||||
{
|
||||
public function someFunction(\Rector\Tests\Rector\DomainDrivenDesign\ValueObjectRemoverRector\Source\SomeChildOfValueObject $name)
|
||||
{
|
||||
|
@ -4,7 +4,7 @@ namespace SomeNamespace;
|
||||
|
||||
use Rector\Tests\Rector\DomainDrivenDesign\ValueObjectRemoverRector\Source\SomeValueObject;
|
||||
|
||||
class ActionClass
|
||||
class ThirdActionClass
|
||||
{
|
||||
/**
|
||||
* @param null|SomeValueObject $name
|
||||
|
@ -4,7 +4,7 @@ namespace SomeNamespace;
|
||||
|
||||
use Rector\Tests\Rector\DomainDrivenDesign\ValueObjectRemoverRector\Source\SomeValueObject;
|
||||
|
||||
class ActionClass
|
||||
class FourthActionClass
|
||||
{
|
||||
public function someFunction(?SomeValueObject $name): ?SomeValueObject
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user