mirror of
https://github.com/rectorphp/rector.git
synced 2025-02-24 11:44:14 +01:00
Merge pull request #2949 from rectorphp/complex-property-fetch
Improve PropertyFetchManipulator
This commit is contained in:
commit
3650ad07c9
@ -13,6 +13,7 @@ use PhpParser\NodeTraverser;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
||||
use Rector\Core\PhpParser\Node\Manipulator\PropertyFetchAssignManipulator;
|
||||
use Rector\Core\PhpParser\Node\Manipulator\PropertyFetchManipulator;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\TypeDeclaration\Contract\TypeInferer\ParamTypeInfererInterface;
|
||||
@ -25,9 +26,17 @@ final class GetterNodeParamTypeInferer extends AbstractTypeInferer implements Pa
|
||||
*/
|
||||
private $propertyFetchManipulator;
|
||||
|
||||
public function __construct(PropertyFetchManipulator $propertyFetchManipulator)
|
||||
{
|
||||
/**
|
||||
* @var PropertyFetchAssignManipulator
|
||||
*/
|
||||
private $propertyFetchAssignManipulator;
|
||||
|
||||
public function __construct(
|
||||
PropertyFetchManipulator $propertyFetchManipulator,
|
||||
PropertyFetchAssignManipulator $propertyFetchAssignManipulator
|
||||
) {
|
||||
$this->propertyFetchManipulator = $propertyFetchManipulator;
|
||||
$this->propertyFetchAssignManipulator = $propertyFetchAssignManipulator;
|
||||
}
|
||||
|
||||
public function inferParam(Param $param): Type
|
||||
@ -44,7 +53,10 @@ final class GetterNodeParamTypeInferer extends AbstractTypeInferer implements Pa
|
||||
/** @var string $paramName */
|
||||
$paramName = $this->nodeNameResolver->getName($param);
|
||||
|
||||
$propertyNames = $this->propertyFetchManipulator->getPropertyNamesOfAssignOfVariable($classMethod, $paramName);
|
||||
$propertyNames = $this->propertyFetchAssignManipulator->getPropertyNamesOfAssignOfVariable(
|
||||
$classMethod,
|
||||
$paramName
|
||||
);
|
||||
if ($propertyNames === []) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\NullType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\PhpParser\Node\Manipulator\PropertyFetchManipulator;
|
||||
use Rector\Core\PhpParser\Node\Manipulator\ClassMethodPropertyFetchManipulator;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\PHPStan\Type\AliasedObjectType;
|
||||
use Rector\PHPStan\Type\FullyQualifiedObjectType;
|
||||
@ -30,13 +30,13 @@ use Rector\TypeDeclaration\TypeInferer\AbstractTypeInferer;
|
||||
final class ConstructorPropertyTypeInferer extends AbstractTypeInferer implements PropertyTypeInfererInterface
|
||||
{
|
||||
/**
|
||||
* @var PropertyFetchManipulator
|
||||
* @var ClassMethodPropertyFetchManipulator
|
||||
*/
|
||||
private $propertyFetchManipulator;
|
||||
private $classMethodPropertyFetchManipulator;
|
||||
|
||||
public function __construct(PropertyFetchManipulator $propertyFetchManipulator)
|
||||
public function __construct(ClassMethodPropertyFetchManipulator $classMethodPropertyFetchManipulator)
|
||||
{
|
||||
$this->propertyFetchManipulator = $propertyFetchManipulator;
|
||||
$this->classMethodPropertyFetchManipulator = $classMethodPropertyFetchManipulator;
|
||||
}
|
||||
|
||||
public function inferProperty(Property $property): Type
|
||||
@ -58,7 +58,7 @@ final class ConstructorPropertyTypeInferer extends AbstractTypeInferer implement
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
$param = $this->propertyFetchManipulator->resolveParamForPropertyFetch($classMethod, $propertyName);
|
||||
$param = $this->classMethodPropertyFetchManipulator->resolveParamForPropertyFetch($classMethod, $propertyName);
|
||||
if ($param === null) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Core\PhpParser\Node\Manipulator;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\NodeTraverser;
|
||||
use Rector\Core\PhpParser\NodeTraverser\CallableNodeTraverser;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
|
||||
final class ClassMethodPropertyFetchManipulator
|
||||
{
|
||||
/**
|
||||
* @var CallableNodeTraverser
|
||||
*/
|
||||
private $callableNodeTraverser;
|
||||
|
||||
/**
|
||||
* @var NodeNameResolver
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
|
||||
public function __construct(CallableNodeTraverser $callableNodeTraverser, NodeNameResolver $nodeNameResolver)
|
||||
{
|
||||
$this->callableNodeTraverser = $callableNodeTraverser;
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* In case the property name is different to param name:
|
||||
*
|
||||
* E.g.:
|
||||
* (SomeType $anotherValue)
|
||||
* $this->value = $anotherValue;
|
||||
* ↓
|
||||
* (SomeType $anotherValue)
|
||||
*/
|
||||
public function resolveParamForPropertyFetch(ClassMethod $classMethod, string $propertyName): ?Param
|
||||
{
|
||||
$assignedParamName = null;
|
||||
|
||||
$this->callableNodeTraverser->traverseNodesWithCallable((array) $classMethod->stmts, function (Node $node) use (
|
||||
$propertyName,
|
||||
&$assignedParamName
|
||||
): ?int {
|
||||
if (! $node instanceof Assign) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->nodeNameResolver->isName($node->var, $propertyName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$assignedParamName = $this->nodeNameResolver->getName($node->expr);
|
||||
|
||||
return NodeTraverser::STOP_TRAVERSAL;
|
||||
});
|
||||
|
||||
/** @var string|null $assignedParamName */
|
||||
if ($assignedParamName === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var Param $param */
|
||||
foreach ((array) $classMethod->params as $param) {
|
||||
if (! $this->nodeNameResolver->isName($param, $assignedParamName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $param;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Core\PhpParser\Node\Manipulator;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use Rector\Core\PhpParser\NodeTraverser\CallableNodeTraverser;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
|
||||
final class PropertyFetchAssignManipulator
|
||||
{
|
||||
/**
|
||||
* @var NodeNameResolver
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
|
||||
/**
|
||||
* @var CallableNodeTraverser
|
||||
*/
|
||||
private $callableNodeTraverser;
|
||||
|
||||
public function __construct(NodeNameResolver $nodeNameResolver, CallableNodeTraverser $callableNodeTraverser)
|
||||
{
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->callableNodeTraverser = $callableNodeTraverser;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getPropertyNamesOfAssignOfVariable(Node $node, string $paramName): array
|
||||
{
|
||||
$propertyNames = [];
|
||||
|
||||
$this->callableNodeTraverser->traverseNodesWithCallable($node, function (Node $node) use (
|
||||
$paramName,
|
||||
&$propertyNames
|
||||
) {
|
||||
if (! $this->isVariableAssignToThisPropertyFetch($node, $paramName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var Assign $node */
|
||||
$propertyName = $this->nodeNameResolver->getName($node->expr);
|
||||
if ($propertyName) {
|
||||
$propertyNames[] = $propertyName;
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
return $propertyNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches:
|
||||
* "$this->someValue = $<variableName>;"
|
||||
*/
|
||||
public function isVariableAssignToThisPropertyFetch(Node $node, string $variableName): bool
|
||||
{
|
||||
if (! $node instanceof Assign) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $node->expr instanceof Variable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->nodeNameResolver->isName($node->expr, $variableName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $node->var instanceof PropertyFetch) {
|
||||
return false;
|
||||
}
|
||||
// must be local property
|
||||
return $this->nodeNameResolver->isName($node->var->var, 'this');
|
||||
}
|
||||
}
|
@ -12,13 +12,11 @@ use PhpParser\Node\Expr\StaticPropertyFetch;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\NodeTraverser;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\ErrorType;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\TypeWithClassName;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\PhpParser\NodeTraverser\CallableNodeTraverser;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
@ -86,13 +84,9 @@ final class PropertyFetchManipulator
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isMagicOnType(Node $node, Type $type): bool
|
||||
public function isMagicOnType(PropertyFetch $propertyFetch, Type $type): bool
|
||||
{
|
||||
if (! $node instanceof PropertyFetch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$varNodeType = $this->nodeTypeResolver->resolve($node);
|
||||
$varNodeType = $this->nodeTypeResolver->resolve($propertyFetch);
|
||||
|
||||
if ($varNodeType instanceof ErrorType) {
|
||||
return true;
|
||||
@ -106,12 +100,12 @@ final class PropertyFetchManipulator
|
||||
return false;
|
||||
}
|
||||
|
||||
$nodeName = $this->nodeNameResolver->getName($node);
|
||||
$nodeName = $this->nodeNameResolver->getName($propertyFetch);
|
||||
if ($nodeName === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ! $this->hasPublicProperty($node, $nodeName);
|
||||
return ! $this->hasPublicProperty($propertyFetch, $nodeName);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -162,6 +156,7 @@ final class PropertyFetchManipulator
|
||||
if (! $node->var instanceof PropertyFetch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// must be local property
|
||||
return $this->nodeNameResolver->isName($node->var->var, 'this');
|
||||
}
|
||||
@ -188,53 +183,6 @@ final class PropertyFetchManipulator
|
||||
return $this->nodeNameResolver->isName($node->var, 'this');
|
||||
}
|
||||
|
||||
/**
|
||||
* In case the property name is different to param name:
|
||||
*
|
||||
* E.g.:
|
||||
* (SomeType $anotherValue)
|
||||
* $this->value = $anotherValue;
|
||||
* ↓
|
||||
* (SomeType $anotherValue)
|
||||
*/
|
||||
public function resolveParamForPropertyFetch(ClassMethod $classMethod, string $propertyName): ?Param
|
||||
{
|
||||
$assignedParamName = null;
|
||||
|
||||
$this->callableNodeTraverser->traverseNodesWithCallable((array) $classMethod->stmts, function (Node $node) use (
|
||||
$propertyName,
|
||||
&$assignedParamName
|
||||
): ?int {
|
||||
if (! $node instanceof Assign) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->nodeNameResolver->isName($node->var, $propertyName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$assignedParamName = $this->nodeNameResolver->getName($node->expr);
|
||||
|
||||
return NodeTraverser::STOP_TRAVERSAL;
|
||||
});
|
||||
|
||||
/** @var string|null $assignedParamName */
|
||||
if ($assignedParamName === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var Param $param */
|
||||
foreach ((array) $classMethod->params as $param) {
|
||||
if (! $this->nodeNameResolver->isName($param, $assignedParamName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $param;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PropertyFetch|StaticPropertyFetch|null
|
||||
*/
|
||||
@ -290,14 +238,11 @@ final class PropertyFetchManipulator
|
||||
}
|
||||
|
||||
$propertyFetchType = $nodeScope->getType($propertyFetch->var);
|
||||
if ($propertyFetchType instanceof ObjectType) {
|
||||
$propertyFetchType = $propertyFetchType->getClassName();
|
||||
}
|
||||
|
||||
if (! is_string($propertyFetchType)) {
|
||||
if (! $propertyFetchType instanceof TypeWithClassName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$propertyFetchType = $propertyFetchType->getClassName();
|
||||
if (! $this->reflectionProvider->hasClass($propertyFetchType)) {
|
||||
return false;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user