rector/packages/StaticTypeMapper/StaticTypeMapper.php

133 lines
5.2 KiB
PHP

<?php
declare (strict_types=1);
namespace Rector\StaticTypeMapper;
use PhpParser\Node;
use PhpParser\Node\ComplexType;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\UnionType;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ThrowsTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use Rector\Core\Exception\NotImplementedYetException;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper;
use Rector\StaticTypeMapper\Mapper\PhpParserNodeMapper;
use Rector\StaticTypeMapper\Naming\NameScopeFactory;
use Rector\StaticTypeMapper\PhpDoc\PhpDocTypeMapper;
/**
* Maps PhpParser <=> PHPStan <=> PHPStan doc <=> string type nodes between all possible formats
* @see \Rector\Tests\NodeTypeResolver\StaticTypeMapper\StaticTypeMapperTest
*/
final class StaticTypeMapper
{
/**
* @readonly
* @var \Rector\StaticTypeMapper\Naming\NameScopeFactory
*/
private $nameScopeFactory;
/**
* @readonly
* @var \Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper
*/
private $phpStanStaticTypeMapper;
/**
* @readonly
* @var \Rector\StaticTypeMapper\PhpDoc\PhpDocTypeMapper
*/
private $phpDocTypeMapper;
/**
* @readonly
* @var \Rector\StaticTypeMapper\Mapper\PhpParserNodeMapper
*/
private $phpParserNodeMapper;
/**
* @readonly
* @var \Rector\NodeNameResolver\NodeNameResolver
*/
private $nodeNameResolver;
/**
* @var array<string, string>
*/
private const STANDALONE_MAPS = ['false' => 'bool'];
public function __construct(NameScopeFactory $nameScopeFactory, PHPStanStaticTypeMapper $phpStanStaticTypeMapper, PhpDocTypeMapper $phpDocTypeMapper, PhpParserNodeMapper $phpParserNodeMapper, NodeNameResolver $nodeNameResolver)
{
$this->nameScopeFactory = $nameScopeFactory;
$this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper;
$this->phpDocTypeMapper = $phpDocTypeMapper;
$this->phpParserNodeMapper = $phpParserNodeMapper;
$this->nodeNameResolver = $nodeNameResolver;
}
public function mapPHPStanTypeToPHPStanPhpDocTypeNode(Type $phpStanType) : TypeNode
{
return $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($phpStanType);
}
/**
* @param TypeKind::* $typeKind
* @return Name|ComplexType|Identifier|null
*/
public function mapPHPStanTypeToPhpParserNode(Type $phpStanType, string $typeKind) : ?Node
{
$node = $this->phpStanStaticTypeMapper->mapToPhpParserNode($phpStanType, $typeKind);
if (!$node instanceof Node) {
return null;
}
if ($node instanceof UnionType) {
return $node;
}
if (!$node instanceof Name) {
return $node;
}
$nodeName = $this->nodeNameResolver->getName($node);
foreach (self::STANDALONE_MAPS as $key => $type) {
if ($nodeName === $key) {
return new Name($type);
}
}
return $node;
}
public function mapPhpParserNodePHPStanType(Node $node) : Type
{
return $this->phpParserNodeMapper->mapToPHPStanType($node);
}
public function mapPHPStanPhpDocTypeToPHPStanType(PhpDocTagValueNode $phpDocTagValueNode, Node $node) : Type
{
if ($phpDocTagValueNode instanceof TemplateTagValueNode) {
// special case
$nameScope = $this->nameScopeFactory->createNameScopeFromNodeWithoutTemplateTypes($node);
if (!$phpDocTagValueNode->bound instanceof TypeNode) {
return new MixedType();
}
return $this->phpDocTypeMapper->mapToPHPStanType($phpDocTagValueNode->bound, $node, $nameScope);
}
if ($phpDocTagValueNode instanceof ReturnTagValueNode || $phpDocTagValueNode instanceof ParamTagValueNode || $phpDocTagValueNode instanceof VarTagValueNode || $phpDocTagValueNode instanceof ThrowsTagValueNode) {
return $this->mapPHPStanPhpDocTypeNodeToPHPStanType($phpDocTagValueNode->type, $node);
}
throw new NotImplementedYetException(__METHOD__ . ' for ' . \get_class($phpDocTagValueNode));
}
public function mapPHPStanPhpDocTypeNodeToPHPStanType(TypeNode $typeNode, Node $node) : Type
{
if ($node instanceof Param) {
$classMethod = $node->getAttribute(AttributeKey::PARENT_NODE);
if ($classMethod instanceof ClassMethod) {
// param does not hany any clue about template map, but class method has
$node = $classMethod;
}
}
$nameScope = $this->nameScopeFactory->createNameScopeFromNode($node);
return $this->phpDocTypeMapper->mapToPHPStanType($typeNode, $node, $nameScope);
}
}