Merge pull request #2829 from rectorphp/node-type-property

decouple PropertyFetchTypeResolver
This commit is contained in:
Tomas Votruba 2020-02-10 02:09:54 +01:00 committed by GitHub
commit 65f4cdd63a
4 changed files with 147 additions and 86 deletions

View File

@ -145,7 +145,6 @@ PHP
}
$objectType = $this->getObjectType($objectExpr);
if ($objectType instanceof ObjectType) {
$class = $this->classLikeParsedNodesFinder->findClass($objectType->getClassName());

View File

@ -10,13 +10,10 @@ use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Param;
use PhpParser\Node\Scalar;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Nop;
use PHPStan\Analyser\Scope;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\ArrayType;
use PHPStan\Type\FloatType;
@ -29,9 +26,7 @@ use PHPStan\Type\Type;
use PHPStan\Type\TypeUtils;
use PHPStan\Type\TypeWithClassName;
use PHPStan\Type\UnionType;
use Rector\BetterPhpDocParser\PhpDocParser\BetterPhpDocParser;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\NodeContainer\NodeCollector\ParsedNodeCollector;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Contract\PerNodeTypeResolver\PerNodeTypeResolverInterface;
use Rector\NodeTypeResolver\Node\AttributeKey;
@ -39,7 +34,6 @@ use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
use Rector\NodeTypeResolver\Reflection\ClassReflectionTypesResolver;
use Rector\NodeTypeResolver\TypeAnalyzer\ArrayTypeAnalyzer;
use Rector\TypeDeclaration\PHPStan\Type\ObjectTypeSpecifier;
use ReflectionProperty;
final class NodeTypeResolver
{
@ -73,21 +67,6 @@ final class NodeTypeResolver
*/
private $objectTypeSpecifier;
/**
* @var ParsedNodeCollector
*/
private $parsedNodeCollector;
/**
* @var BetterPhpDocParser
*/
private $betterPhpDocParser;
/**
* @var StaticTypeMapper
*/
private $staticTypeMapper;
/**
* @var ArrayTypeAnalyzer
*/
@ -97,13 +76,10 @@ final class NodeTypeResolver
* @param PerNodeTypeResolverInterface[] $perNodeTypeResolvers
*/
public function __construct(
BetterPhpDocParser $betterPhpDocParser,
NodeNameResolver $nodeNameResolver,
ParsedNodeCollector $parsedNodeCollector,
ClassReflectionTypesResolver $classReflectionTypesResolver,
ReflectionProvider $reflectionProvider,
TypeFactory $typeFactory,
StaticTypeMapper $staticTypeMapper,
ObjectTypeSpecifier $objectTypeSpecifier,
array $perNodeTypeResolvers
) {
@ -117,9 +93,6 @@ final class NodeTypeResolver
$this->reflectionProvider = $reflectionProvider;
$this->typeFactory = $typeFactory;
$this->objectTypeSpecifier = $objectTypeSpecifier;
$this->parsedNodeCollector = $parsedNodeCollector;
$this->betterPhpDocParser = $betterPhpDocParser;
$this->staticTypeMapper = $staticTypeMapper;
}
/**
@ -218,10 +191,6 @@ final class NodeTypeResolver
return $staticType;
}
if ($node instanceof PropertyFetch) {
return $this->resolvePropertyFetchType($node);
}
return $this->objectTypeSpecifier->narrowToFullyQualifiedOrAlaisedObjectType($node, $staticType);
}
@ -401,48 +370,6 @@ final class NodeTypeResolver
return $className === null || Strings::contains($className, 'AnonymousClass');
}
private function getVendorPropertyFetchType(PropertyFetch $propertyFetch): ?Type
{
$varObjectType = $this->resolve($propertyFetch->var);
if (! $varObjectType instanceof TypeWithClassName) {
return null;
}
if ($this->parsedNodeCollector->findClass($varObjectType->getClassName()) !== null) {
return null;
}
// 3rd party code
$propertyName = $this->nodeNameResolver->getName($propertyFetch->name);
if ($propertyName === null) {
return null;
}
if (! property_exists($varObjectType->getClassName(), $propertyName)) {
return null;
}
// property is used
$propertyReflection = new ReflectionProperty($varObjectType->getClassName(), $propertyName);
if (! $propertyReflection->getDocComment()) {
return null;
}
$phpDocNode = $this->betterPhpDocParser->parseString((string) $propertyReflection->getDocComment());
$varTagValues = $phpDocNode->getVarTagValues();
if (! isset($varTagValues[0])) {
return null;
}
$typeNode = $varTagValues[0]->type;
if (! $typeNode instanceof TypeNode) {
return null;
}
return $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($typeNode, new Nop());
}
/**
* @return string[]
*/
@ -470,15 +397,4 @@ final class NodeTypeResolver
return false;
}
private function resolvePropertyFetchType(PropertyFetch $propertyFetch)
{
// compensate 3rd party non-analysed property reflection
$vendorPropertyType = $this->getVendorPropertyFetchType($propertyFetch);
if ($vendorPropertyType !== null) {
return $vendorPropertyType;
}
return new MixedType();
}
}

View File

@ -0,0 +1,146 @@
<?php
declare(strict_types=1);
namespace Rector\NodeTypeResolver\PerNodeTypeResolver;
use PhpParser\Builder\Property;
use PhpParser\Node;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Stmt\Nop;
use PHPStan\Analyser\Scope;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeWithClassName;
use Rector\BetterPhpDocParser\PhpDocParser\BetterPhpDocParser;
use Rector\Core\NodeContainer\NodeCollector\ParsedNodeCollector;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Contract\PerNodeTypeResolver\PerNodeTypeResolverInterface;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\NodeTypeResolver\StaticTypeMapper;
use ReflectionProperty;
/**
* @see \Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\NameTypeResolver\NameTypeResolverTest
*/
final class PropertyFetchTypeResolver implements PerNodeTypeResolverInterface
{
/**
* @var ParsedNodeCollector
*/
private $parsedNodeCollector;
/**
* @var NodeTypeResolver
*/
private $nodeTypeResolver;
/**
* @var NodeNameResolver
*/
private $nodeNameResolver;
/**
* @var BetterPhpDocParser
*/
private $betterPhpDocParser;
/**
* @var StaticTypeMapper
*/
private $staticTypeMapper;
public function __construct(
ParsedNodeCollector $parsedNodeCollector,
NodeNameResolver $nodeNameResolver,
BetterPhpDocParser $betterPhpDocParser,
StaticTypeMapper $staticTypeMapper
) {
$this->parsedNodeCollector = $parsedNodeCollector;
$this->nodeNameResolver = $nodeNameResolver;
$this->betterPhpDocParser = $betterPhpDocParser;
$this->staticTypeMapper = $staticTypeMapper;
}
/**
* @required
*/
public function autowirePropertyTypeResolver(NodeTypeResolver $nodeTypeResolver): void
{
$this->nodeTypeResolver = $nodeTypeResolver;
}
/**
* @return string[]
*/
public function getNodeClasses(): array
{
return [PropertyFetch::class];
}
/**
* @param PropertyFetch $node
*/
public function resolve(Node $node): Type
{
// compensate 3rd party non-analysed property reflection
$vendorPropertyType = $this->getVendorPropertyFetchType($node);
if ($vendorPropertyType !== null) {
return $vendorPropertyType;
}
/** @var Scope|null $scope */
$scope = $node->getAttribute(AttributeKey::SCOPE);
if ($scope === null) {
return new MixedType();
}
return $scope->getType($node);
}
private function getVendorPropertyFetchType(PropertyFetch $propertyFetch): ?Type
{
$varObjectType = $this->nodeTypeResolver->resolve($propertyFetch->var);
if (! $varObjectType instanceof TypeWithClassName) {
return null;
}
$class = $this->parsedNodeCollector->findClass($varObjectType->getClassName());
if ($class !== null) {
return null;
}
// 3rd party code
$propertyName = $this->nodeNameResolver->getName($propertyFetch->name);
if ($propertyName === null) {
return null;
}
if (! property_exists($varObjectType->getClassName(), $propertyName)) {
return null;
}
// property is used
$propertyReflection = new ReflectionProperty($varObjectType->getClassName(), $propertyName);
if (! $propertyReflection->getDocComment()) {
return null;
}
$phpDocNode = $this->betterPhpDocParser->parseString((string) $propertyReflection->getDocComment());
$varTagValues = $phpDocNode->getVarTagValues();
if (! isset($varTagValues[0])) {
return null;
}
$typeNode = $varTagValues[0]->type;
if (! $typeNode instanceof TypeNode) {
return null;
}
return $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($typeNode, new Nop());
}
}

View File

@ -595,7 +595,7 @@ class Command
*/
private $processTitle;
/**
* @var InputDefinition
* @var \Symfony\Component\Console\Input\InputDefinition
*/
private $definition;
/**