mirror of
https://github.com/rectorphp/rector.git
synced 2025-02-24 11:44:14 +01:00
decopule PhpDoTypeMapper, PhpParserNodeMapper
This commit is contained in:
parent
3f15e1b558
commit
94bf9d1522
@ -6,9 +6,9 @@ services:
|
||||
Rector\NodeTypeResolver\:
|
||||
resource: '../src'
|
||||
exclude:
|
||||
- '../src/Contract'
|
||||
- '../src/Contract/*'
|
||||
# "Type" is because the file is needed for PHPStan container only
|
||||
- '../src/Type'
|
||||
- '../src/PHPStan/TypeExtension/*'
|
||||
|
||||
Rector\Php\TypeAnalyzer: null
|
||||
Rector\FileSystem\FilesFinder: null
|
||||
|
@ -1,8 +1,8 @@
|
||||
services:
|
||||
-
|
||||
class: Rector\NodeTypeResolver\Type\TypeExtension\StaticContainerGetDynamicMethodReturnTypeExtension
|
||||
class: Rector\NodeTypeResolver\PHPStan\TypeExtension\StaticContainerGetDynamicMethodReturnTypeExtension
|
||||
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
|
||||
|
||||
-
|
||||
class: Rector\NodeTypeResolver\Type\TypeExtension\KernelGetContainerAfterBootReturnTypeExtension
|
||||
class: Rector\NodeTypeResolver\PHPStan\TypeExtension\KernelGetContainerAfterBootReturnTypeExtension
|
||||
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
|
||||
|
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\Contract\PhpDocParser;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PHPStan\Analyser\NameScope;
|
||||
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
|
||||
use PHPStan\Type\Type;
|
||||
|
||||
interface PhpDocTypeMapperInterface
|
||||
{
|
||||
public function getNodeType(): string;
|
||||
|
||||
public function mapToPHPStanType(TypeNode $typeNode, Node $node, NameScope $nameScope): Type;
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\Contract\PhpParser;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PHPStan\Type\Type;
|
||||
|
||||
interface PhpParserNodeMapperInterface
|
||||
{
|
||||
public function getNodeType(): string;
|
||||
|
||||
public function mapToPHPStan(Node $node): Type;
|
||||
}
|
@ -196,6 +196,10 @@ final class NodeTypeResolver
|
||||
return $nodeType->isSuperTypeOf(new NullType())->yes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Use @see NodeTypeResolver::resolve() instead
|
||||
*/
|
||||
public function getStaticType(Node $node): Type
|
||||
{
|
||||
if ($this->isArrayExpr($node)) {
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\Type\TypeExtension;
|
||||
namespace Rector\NodeTypeResolver\PHPStan\TypeExtension;
|
||||
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PHPStan\Analyser\Scope;
|
@ -2,7 +2,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\Type\TypeExtension;
|
||||
namespace Rector\NodeTypeResolver\PHPStan\TypeExtension;
|
||||
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PHPStan\Analyser\Scope;
|
64
packages/NodeTypeResolver/src/PHPStan/TypeHasher.php
Normal file
64
packages/NodeTypeResolver/src/PHPStan/TypeHasher.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\PHPStan;
|
||||
|
||||
use PHPStan\ShouldNotHappenException;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\ConstantType;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\TypeWithClassName;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\PHPStan\Type\ShortenedObjectType;
|
||||
use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper;
|
||||
|
||||
final class TypeHasher
|
||||
{
|
||||
/**
|
||||
* @var PHPStanStaticTypeMapper
|
||||
*/
|
||||
private $phpStanStaticTypeMapper;
|
||||
|
||||
public function __construct(PHPStanStaticTypeMapper $phpStanStaticTypeMapper)
|
||||
{
|
||||
$this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper;
|
||||
}
|
||||
|
||||
public function createTypeHash(Type $type): string
|
||||
{
|
||||
if ($type instanceof MixedType) {
|
||||
return serialize($type);
|
||||
}
|
||||
|
||||
if ($type instanceof ArrayType) {
|
||||
// @todo sort to make different order identical
|
||||
return $this->createTypeHash($type->getItemType()) . '[]';
|
||||
}
|
||||
|
||||
if ($type instanceof ShortenedObjectType) {
|
||||
return $type->getFullyQualifiedName();
|
||||
}
|
||||
|
||||
if ($type instanceof TypeWithClassName) {
|
||||
return $type->getClassName();
|
||||
}
|
||||
|
||||
if ($type instanceof ConstantType) {
|
||||
if (method_exists($type, 'getValue')) {
|
||||
return get_class($type) . $type->getValue();
|
||||
}
|
||||
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
if ($type instanceof UnionType) {
|
||||
$types = $type->getTypes();
|
||||
sort($types);
|
||||
$type = new UnionType($types);
|
||||
}
|
||||
|
||||
return $this->phpStanStaticTypeMapper->mapToDocString($type);
|
||||
}
|
||||
}
|
@ -45,6 +45,7 @@ use Rector\BetterPhpDocParser\Printer\PhpDocInfoPrinter;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
use Rector\NodeTypeResolver\Exception\MissingTagException;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\PHPStan\TypeHasher;
|
||||
use Rector\NodeTypeResolver\StaticTypeMapper;
|
||||
use Rector\PHPStan\Type\AliasedObjectType;
|
||||
use Rector\PHPStan\Type\ShortenedObjectType;
|
||||
@ -94,6 +95,11 @@ final class DocBlockManipulator
|
||||
*/
|
||||
private $docBlockNameImporter;
|
||||
|
||||
/**
|
||||
* @var TypeHasher
|
||||
*/
|
||||
private $typeHasher;
|
||||
|
||||
public function __construct(
|
||||
PhpDocInfoFactory $phpDocInfoFactory,
|
||||
PhpDocInfoPrinter $phpDocInfoPrinter,
|
||||
@ -101,7 +107,8 @@ final class DocBlockManipulator
|
||||
PhpDocNodeTraverser $phpDocNodeTraverser,
|
||||
StaticTypeMapper $staticTypeMapper,
|
||||
DocBlockClassRenamer $docBlockClassRenamer,
|
||||
DocBlockNameImporter $docBlockNameImporter
|
||||
DocBlockNameImporter $docBlockNameImporter,
|
||||
TypeHasher $typeHasher
|
||||
) {
|
||||
$this->phpDocInfoFactory = $phpDocInfoFactory;
|
||||
$this->phpDocInfoPrinter = $phpDocInfoPrinter;
|
||||
@ -110,6 +117,7 @@ final class DocBlockManipulator
|
||||
$this->staticTypeMapper = $staticTypeMapper;
|
||||
$this->docBlockClassRenamer = $docBlockClassRenamer;
|
||||
$this->docBlockNameImporter = $docBlockNameImporter;
|
||||
$this->typeHasher = $typeHasher;
|
||||
}
|
||||
|
||||
public function hasTag(Node $node, string $name): bool
|
||||
@ -566,8 +574,8 @@ final class DocBlockManipulator
|
||||
return true;
|
||||
}
|
||||
|
||||
$firstTypeHash = $this->staticTypeMapper->createTypeHash($firstType);
|
||||
$secondTypeHash = $this->staticTypeMapper->createTypeHash($secondType);
|
||||
$firstTypeHash = $this->typeHasher->createTypeHash($firstType);
|
||||
$secondTypeHash = $this->typeHasher->createTypeHash($secondType);
|
||||
|
||||
if ($firstTypeHash === $secondTypeHash) {
|
||||
return true;
|
||||
|
41
packages/NodeTypeResolver/src/PhpDoc/PhpDocTypeMapper.php
Normal file
41
packages/NodeTypeResolver/src/PhpDoc/PhpDocTypeMapper.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\PhpDoc;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PHPStan\Analyser\NameScope;
|
||||
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\Exception\NotImplementedException;
|
||||
use Rector\NodeTypeResolver\Contract\PhpDocParser\PhpDocTypeMapperInterface;
|
||||
|
||||
final class PhpDocTypeMapper
|
||||
{
|
||||
/**
|
||||
* @var PhpDocTypeMapperInterface[]
|
||||
*/
|
||||
private $phpDocTypeMappers = [];
|
||||
|
||||
/**
|
||||
* @param PhpDocTypeMapperInterface[] $phpDocTypeMappers
|
||||
*/
|
||||
public function __construct(array $phpDocTypeMappers)
|
||||
{
|
||||
$this->phpDocTypeMappers = $phpDocTypeMappers;
|
||||
}
|
||||
|
||||
public function mapToPHPStanType(TypeNode $typeNode, Node $node, NameScope $nameScope): Type
|
||||
{
|
||||
foreach ($this->phpDocTypeMappers as $phpDocTypeMapper) {
|
||||
if (! is_a($typeNode, $phpDocTypeMapper->getNodeType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $phpDocTypeMapper->mapToPHPStanType($typeNode, $node, $nameScope);
|
||||
}
|
||||
|
||||
throw new NotImplementedException(__METHOD__ . ' for ' . get_class($typeNode));
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\PhpDocParser;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PHPStan\Analyser\NameScope;
|
||||
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\NodeTypeResolver\Contract\PhpDocParser\PhpDocTypeMapperInterface;
|
||||
use Rector\NodeTypeResolver\PhpDoc\PhpDocTypeMapper;
|
||||
|
||||
final class ArrayTypeMapper implements PhpDocTypeMapperInterface
|
||||
{
|
||||
/**
|
||||
* @var PhpDocTypeMapper
|
||||
*/
|
||||
private $phpDocTypeMapper;
|
||||
|
||||
public function getNodeType(): string
|
||||
{
|
||||
return ArrayTypeNode::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function autowireGenericPhpDocTypeMapper(PhpDocTypeMapper $phpDocTypeMapper): void
|
||||
{
|
||||
$this->phpDocTypeMapper = $phpDocTypeMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ArrayTypeNode $typeNode
|
||||
*/
|
||||
public function mapToPHPStanType(TypeNode $typeNode, Node $node, NameScope $nameScope): Type
|
||||
{
|
||||
$nestedType = $this->phpDocTypeMapper->mapToPHPStanType($typeNode->type, $node, $nameScope);
|
||||
|
||||
// @todo improve for key!
|
||||
return new ArrayType(new MixedType(), $nestedType);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\PhpDocParser;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PHPStan\Analyser\NameScope;
|
||||
use PHPStan\PhpDoc\TypeNodeResolver;
|
||||
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\NodeTypeResolver\Contract\PhpDocParser\PhpDocTypeMapperInterface;
|
||||
|
||||
final class GenericTypeMapper implements PhpDocTypeMapperInterface
|
||||
{
|
||||
/**
|
||||
* @var TypeNodeResolver
|
||||
*/
|
||||
private $typeNodeResolver;
|
||||
|
||||
public function __construct(TypeNodeResolver $typeNodeResolver)
|
||||
{
|
||||
$this->typeNodeResolver = $typeNodeResolver;
|
||||
}
|
||||
|
||||
public function getNodeType(): string
|
||||
{
|
||||
return GenericTypeNode::class;
|
||||
}
|
||||
|
||||
public function mapToPHPStanType(TypeNode $typeNode, Node $node, NameScope $nameScope): Type
|
||||
{
|
||||
return $this->typeNodeResolver->resolve($typeNode, $nameScope);
|
||||
}
|
||||
}
|
@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\PhpDocParser;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PHPStan\Analyser\NameScope;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
|
||||
use PHPStan\Type\ClassStringType;
|
||||
use PHPStan\Type\IterableType;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\StaticType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\AttributeAwarePhpDoc\Ast\Type\AttributeAwareIdentifierTypeNode;
|
||||
use Rector\BetterPhpDocParser\Type\PreSlashStringType;
|
||||
use Rector\NodeTypeResolver\Contract\PhpDocParser\PhpDocTypeMapperInterface;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\TypeMapper\ScalarStringToTypeMapper;
|
||||
use Rector\PHPStan\Type\ParentStaticType;
|
||||
use Rector\PHPStan\Type\SelfObjectType;
|
||||
use Rector\TypeDeclaration\PHPStan\Type\ObjectTypeSpecifier;
|
||||
|
||||
final class IdentifierTypeMapper implements PhpDocTypeMapperInterface
|
||||
{
|
||||
/**
|
||||
* @var ScalarStringToTypeMapper
|
||||
*/
|
||||
private $scalarStringToTypeMapper;
|
||||
|
||||
/**
|
||||
* @var ObjectTypeSpecifier
|
||||
*/
|
||||
private $objectTypeSpecifier;
|
||||
|
||||
public function __construct(
|
||||
ScalarStringToTypeMapper $scalarStringToTypeMapper,
|
||||
ObjectTypeSpecifier $objectTypeSpecifier
|
||||
) {
|
||||
$this->scalarStringToTypeMapper = $scalarStringToTypeMapper;
|
||||
$this->objectTypeSpecifier = $objectTypeSpecifier;
|
||||
}
|
||||
|
||||
public function getNodeType(): string
|
||||
{
|
||||
return IdentifierTypeNode::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AttributeAwareIdentifierTypeNode&IdentifierTypeNode $typeNode
|
||||
*/
|
||||
public function mapToPHPStanType(TypeNode $typeNode, Node $node, NameScope $nameScope): Type
|
||||
{
|
||||
$type = $this->scalarStringToTypeMapper->mapScalarStringToType($typeNode->name);
|
||||
if (! $type instanceof MixedType) {
|
||||
return $type;
|
||||
}
|
||||
|
||||
$loweredName = strtolower($typeNode->name);
|
||||
|
||||
// @todo for all scalars
|
||||
if ($loweredName === '\string') {
|
||||
return new PreSlashStringType();
|
||||
}
|
||||
|
||||
if ($loweredName === 'class-string') {
|
||||
return new ClassStringType();
|
||||
}
|
||||
|
||||
if ($loweredName === 'self') {
|
||||
return $this->mapSelf($node);
|
||||
}
|
||||
|
||||
if ($loweredName === 'parent') {
|
||||
return $this->mapParent($node);
|
||||
}
|
||||
|
||||
if ($loweredName === 'static') {
|
||||
return $this->mapStatic($node);
|
||||
}
|
||||
|
||||
if ($loweredName === 'iterable') {
|
||||
return new IterableType(new MixedType(), new MixedType());
|
||||
}
|
||||
|
||||
// @todo improve - making many false positives now
|
||||
$objectType = new ObjectType($typeNode->name);
|
||||
|
||||
return $this->objectTypeSpecifier->narrowToFullyQualifiedOrAlaisedObjectType($node, $objectType);
|
||||
}
|
||||
|
||||
private function mapStatic(Node $node): Type
|
||||
{
|
||||
/** @var string|null $className */
|
||||
$className = $node->getAttribute(AttributeKey::CLASS_NAME);
|
||||
if ($className === null) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
return new StaticType($className);
|
||||
}
|
||||
|
||||
private function mapParent(Node $node): Type
|
||||
{
|
||||
/** @var string|null $parentClassName */
|
||||
$parentClassName = $node->getAttribute(AttributeKey::PARENT_CLASS_NAME);
|
||||
if ($parentClassName === null) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
return new ParentStaticType($parentClassName);
|
||||
}
|
||||
|
||||
private function mapSelf(Node $node): Type
|
||||
{
|
||||
/** @var string|null $className */
|
||||
$className = $node->getAttribute(AttributeKey::CLASS_NAME);
|
||||
if ($className === null) {
|
||||
// self outside the class, e.g. in a function
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
return new SelfObjectType($className);
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\PhpDocParser;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PHPStan\Analyser\NameScope;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\NodeTypeResolver\Contract\PhpDocParser\PhpDocTypeMapperInterface;
|
||||
use Rector\NodeTypeResolver\PhpDoc\PhpDocTypeMapper;
|
||||
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
|
||||
|
||||
final class IntersectionTypeMapper implements PhpDocTypeMapperInterface
|
||||
{
|
||||
/**
|
||||
* @var PhpDocTypeMapper
|
||||
*/
|
||||
private $phpDocTypeMapper;
|
||||
|
||||
/**
|
||||
* @var TypeFactory
|
||||
*/
|
||||
private $typeFactory;
|
||||
|
||||
public function __construct(TypeFactory $typeFactory)
|
||||
{
|
||||
$this->typeFactory = $typeFactory;
|
||||
}
|
||||
|
||||
public function getNodeType(): string
|
||||
{
|
||||
return IntersectionTypeNode::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function autowireIntersectionTypeMapper(PhpDocTypeMapper $phpDocTypeMapper): void
|
||||
{
|
||||
$this->phpDocTypeMapper = $phpDocTypeMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IntersectionTypeNode $typeNode
|
||||
*/
|
||||
public function mapToPHPStanType(TypeNode $typeNode, Node $node, NameScope $nameScope): Type
|
||||
{
|
||||
$unionedTypes = [];
|
||||
foreach ($typeNode->types as $unionedTypeNode) {
|
||||
$unionedTypes[] = $this->phpDocTypeMapper->mapToPHPStanType($unionedTypeNode, $node, $nameScope);
|
||||
}
|
||||
|
||||
// to prevent missing class error, e.g. in tests
|
||||
return $this->typeFactory->createMixedPassedOrUnionType($unionedTypes);
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\PhpDocParser;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PHPStan\Analyser\NameScope;
|
||||
use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
|
||||
use PHPStan\Type\ThisType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
use Rector\NodeTypeResolver\Contract\PhpDocParser\PhpDocTypeMapperInterface;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
final class ThisTypeMapper implements PhpDocTypeMapperInterface
|
||||
{
|
||||
public function getNodeType(): string
|
||||
{
|
||||
return ThisTypeNode::class;
|
||||
}
|
||||
|
||||
public function mapToPHPStanType(TypeNode $typeNode, Node $node, NameScope $nameScope): Type
|
||||
{
|
||||
if ($node === null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
/** @var string $className */
|
||||
$className = $node->getAttribute(AttributeKey::CLASS_NAME);
|
||||
|
||||
return new ThisType($className);
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\PhpDocParser;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PHPStan\Analyser\NameScope;
|
||||
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\NodeTypeResolver\Contract\PhpDocParser\PhpDocTypeMapperInterface;
|
||||
use Rector\NodeTypeResolver\PhpDoc\PhpDocTypeMapper;
|
||||
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
|
||||
|
||||
final class UnionTypeMapper implements PhpDocTypeMapperInterface
|
||||
{
|
||||
/**
|
||||
* @var PhpDocTypeMapper
|
||||
*/
|
||||
private $phpDocTypeMapper;
|
||||
|
||||
/**
|
||||
* @var TypeFactory
|
||||
*/
|
||||
private $typeFactory;
|
||||
|
||||
public function __construct(TypeFactory $typeFactory)
|
||||
{
|
||||
$this->typeFactory = $typeFactory;
|
||||
}
|
||||
|
||||
public function getNodeType(): string
|
||||
{
|
||||
return UnionTypeNode::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function autowireUnionTypeMapper(PhpDocTypeMapper $phpDocTypeMapper): void
|
||||
{
|
||||
$this->phpDocTypeMapper = $phpDocTypeMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UnionTypeNode $typeNode
|
||||
*/
|
||||
public function mapToPHPStanType(TypeNode $typeNode, Node $node, NameScope $nameScope): Type
|
||||
{
|
||||
$unionedTypes = [];
|
||||
foreach ($typeNode->types as $unionedTypeNode) {
|
||||
$unionedTypes[] = $this->phpDocTypeMapper->mapToPHPStanType($unionedTypeNode, $node, $nameScope);
|
||||
}
|
||||
|
||||
// to prevent missing class error, e.g. in tests
|
||||
return $this->typeFactory->createMixedPassedOrUnionType($unionedTypes);
|
||||
}
|
||||
}
|
35
packages/NodeTypeResolver/src/PhpParser/ExprNodeMapper.php
Normal file
35
packages/NodeTypeResolver/src/PhpParser/ExprNodeMapper.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\PhpParser;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\NodeTypeResolver\Contract\PhpParser\PhpParserNodeMapperInterface;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
final class ExprNodeMapper implements PhpParserNodeMapperInterface
|
||||
{
|
||||
public function getNodeType(): string
|
||||
{
|
||||
return Expr::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Expr $node
|
||||
*/
|
||||
public function mapToPHPStan(Node $node): Type
|
||||
{
|
||||
/** @var Scope|null $scope */
|
||||
$scope = $node->getAttribute(AttributeKey::SCOPE);
|
||||
if ($scope === null) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
return $scope->getType($node);
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\PhpParser;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\NodeTypeResolver\Contract\PhpParser\PhpParserNodeMapperInterface;
|
||||
use Rector\PHPStan\Type\FullyQualifiedObjectType;
|
||||
|
||||
final class FullyQualifiedNodeMapper implements PhpParserNodeMapperInterface
|
||||
{
|
||||
public function getNodeType(): string
|
||||
{
|
||||
return FullyQualified::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FullyQualified $node
|
||||
*/
|
||||
public function mapToPHPStan(Node $node): Type
|
||||
{
|
||||
return new FullyQualifiedObjectType($node->toString());
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\PhpParser;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\StringType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\NodeTypeResolver\Contract\PhpParser\PhpParserNodeMapperInterface;
|
||||
use Rector\NodeTypeResolver\TypeMapper\ScalarStringToTypeMapper;
|
||||
|
||||
final class IdentifierNodeMapper implements PhpParserNodeMapperInterface
|
||||
{
|
||||
/**
|
||||
* @var ScalarStringToTypeMapper
|
||||
*/
|
||||
private $scalarStringToTypeMapper;
|
||||
|
||||
public function __construct(ScalarStringToTypeMapper $scalarStringToTypeMapper)
|
||||
{
|
||||
$this->scalarStringToTypeMapper = $scalarStringToTypeMapper;
|
||||
}
|
||||
|
||||
public function getNodeType(): string
|
||||
{
|
||||
return Identifier::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Identifier $node
|
||||
*/
|
||||
public function mapToPHPStan(Node $node): Type
|
||||
{
|
||||
if ($node->name === 'string') {
|
||||
return new StringType();
|
||||
}
|
||||
|
||||
$type = $this->scalarStringToTypeMapper->mapScalarStringToType($node->name);
|
||||
if ($type !== null) {
|
||||
return $type;
|
||||
}
|
||||
|
||||
return new MixedType();
|
||||
}
|
||||
}
|
35
packages/NodeTypeResolver/src/PhpParser/NameNodeMapper.php
Normal file
35
packages/NodeTypeResolver/src/PhpParser/NameNodeMapper.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\PhpParser;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Name;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\NodeTypeResolver\ClassExistenceStaticHelper;
|
||||
use Rector\NodeTypeResolver\Contract\PhpParser\PhpParserNodeMapperInterface;
|
||||
use Rector\PHPStan\Type\FullyQualifiedObjectType;
|
||||
|
||||
final class NameNodeMapper implements PhpParserNodeMapperInterface
|
||||
{
|
||||
public function getNodeType(): string
|
||||
{
|
||||
return Name::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Name $node
|
||||
*/
|
||||
public function mapToPHPStan(Node $node): Type
|
||||
{
|
||||
$name = $node->toString();
|
||||
|
||||
if (ClassExistenceStaticHelper::doesClassLikeExist($name)) {
|
||||
return new FullyQualifiedObjectType($node->toString());
|
||||
}
|
||||
|
||||
return new MixedType();
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\PhpParser;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\NullableType;
|
||||
use PHPStan\Type\NullType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\NodeTypeResolver\Contract\PhpParser\PhpParserNodeMapperInterface;
|
||||
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
|
||||
use Rector\NodeTypeResolver\TypeMapper\PhpParserNodeMapper;
|
||||
|
||||
final class NullableTypeNodeMapper implements PhpParserNodeMapperInterface
|
||||
{
|
||||
/**
|
||||
* @var TypeFactory
|
||||
*/
|
||||
private $typeFactory;
|
||||
|
||||
/**
|
||||
* @var PhpParserNodeMapper
|
||||
*/
|
||||
private $phpParserNodeMapper;
|
||||
|
||||
public function __construct(TypeFactory $typeFactory)
|
||||
{
|
||||
$this->typeFactory = $typeFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function autowireNullableTypeNodeMapper(PhpParserNodeMapper $phpParserNodeMapper): void
|
||||
{
|
||||
$this->phpParserNodeMapper = $phpParserNodeMapper;
|
||||
}
|
||||
|
||||
public function getNodeType(): string
|
||||
{
|
||||
return NullableType::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param NullableType $node
|
||||
*/
|
||||
public function mapToPHPStan(Node $node): Type
|
||||
{
|
||||
$types = [];
|
||||
$types[] = $this->phpParserNodeMapper->mapToPHPStanType($node->type);
|
||||
$types[] = new NullType();
|
||||
|
||||
return $this->typeFactory->createMixedPassedOrUnionType($types);
|
||||
}
|
||||
}
|
@ -6,7 +6,6 @@ namespace Rector\NodeTypeResolver;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
@ -14,93 +13,54 @@ use PhpParser\Node\NullableType;
|
||||
use PhpParser\Node\Stmt\UseUse;
|
||||
use PhpParser\Node\UnionType as PhpParserUnionType;
|
||||
use PHPStan\Analyser\NameScope;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\PhpDoc\TypeNodeResolver;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\BooleanType;
|
||||
use PHPStan\Type\CallableType;
|
||||
use PHPStan\Type\ClassStringType;
|
||||
use PHPStan\Type\ConstantType;
|
||||
use PHPStan\Type\FloatType;
|
||||
use PHPStan\Type\IntegerType;
|
||||
use PHPStan\Type\IterableType;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\NullType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\ObjectWithoutClassType;
|
||||
use PHPStan\Type\ResourceType;
|
||||
use PHPStan\Type\StaticType;
|
||||
use PHPStan\Type\StringType;
|
||||
use PHPStan\Type\ThisType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\UnionType;
|
||||
use PHPStan\Type\VoidType;
|
||||
use Rector\BetterPhpDocParser\Type\PreSlashStringType;
|
||||
use Rector\Exception\NotImplementedException;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
|
||||
use Rector\NodeTypeResolver\PhpDoc\PhpDocTypeMapper;
|
||||
use Rector\NodeTypeResolver\TypeMapper\PhpParserNodeMapper;
|
||||
use Rector\PhpParser\Node\Resolver\NameResolver;
|
||||
use Rector\PHPStan\Type\FullyQualifiedObjectType;
|
||||
use Rector\PHPStan\Type\ParentStaticType;
|
||||
use Rector\PHPStan\Type\SelfObjectType;
|
||||
use Rector\PHPStan\Type\ShortenedObjectType;
|
||||
use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper;
|
||||
use Rector\TypeDeclaration\PHPStan\Type\ObjectTypeSpecifier;
|
||||
|
||||
/**
|
||||
* Maps PhpParser <=> PHPStan <=> PHPStan doc <=> string type nodes between all possible formats
|
||||
*/
|
||||
final class StaticTypeMapper
|
||||
{
|
||||
/**
|
||||
* @var TypeFactory
|
||||
*/
|
||||
private $typeFactory;
|
||||
|
||||
/**
|
||||
* @var ObjectTypeSpecifier
|
||||
*/
|
||||
private $objectTypeSpecifier;
|
||||
|
||||
/**
|
||||
* @var PHPStanStaticTypeMapper
|
||||
*/
|
||||
private $phpStanStaticTypeMapper;
|
||||
|
||||
/**
|
||||
* @var TypeNodeResolver
|
||||
*/
|
||||
private $typeNodeResolver;
|
||||
|
||||
/**
|
||||
* @var NameResolver
|
||||
*/
|
||||
private $nameResolver;
|
||||
|
||||
/**
|
||||
* @var PhpParserNodeMapper
|
||||
*/
|
||||
private $phpParserNodeMapper;
|
||||
|
||||
/**
|
||||
* @var PhpDocTypeMapper
|
||||
*/
|
||||
private $phpDocTypeMapper;
|
||||
|
||||
public function __construct(
|
||||
TypeFactory $typeFactory,
|
||||
ObjectTypeSpecifier $objectTypeSpecifier,
|
||||
PHPStanStaticTypeMapper $phpStanStaticTypeMapper,
|
||||
TypeNodeResolver $typeNodeResolver,
|
||||
NameResolver $nameResolver
|
||||
NameResolver $nameResolver,
|
||||
PhpParserNodeMapper $phpParserNodeMapper,
|
||||
PhpDocTypeMapper $phpDocTypeMapper
|
||||
) {
|
||||
$this->typeFactory = $typeFactory;
|
||||
$this->objectTypeSpecifier = $objectTypeSpecifier;
|
||||
$this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper;
|
||||
$this->typeNodeResolver = $typeNodeResolver;
|
||||
$this->nameResolver = $nameResolver;
|
||||
$this->phpParserNodeMapper = $phpParserNodeMapper;
|
||||
$this->phpDocTypeMapper = $phpDocTypeMapper;
|
||||
}
|
||||
|
||||
public function mapPHPStanTypeToPHPStanPhpDocTypeNode(Type $phpStanType): TypeNode
|
||||
@ -123,47 +83,7 @@ final class StaticTypeMapper
|
||||
|
||||
public function mapPhpParserNodePHPStanType(Node $node): Type
|
||||
{
|
||||
if ($node instanceof Expr) {
|
||||
/** @var Scope $scope */
|
||||
$scope = $node->getAttribute(AttributeKey::SCOPE);
|
||||
|
||||
return $scope->getType($node);
|
||||
}
|
||||
|
||||
if ($node instanceof NullableType) {
|
||||
$types = [];
|
||||
$types[] = $this->mapPhpParserNodePHPStanType($node->type);
|
||||
$types[] = new NullType();
|
||||
|
||||
return $this->typeFactory->createMixedPassedOrUnionType($types);
|
||||
}
|
||||
|
||||
if ($node instanceof Identifier) {
|
||||
if ($node->name === 'string') {
|
||||
return new StringType();
|
||||
}
|
||||
|
||||
$type = $this->mapScalarStringToType($node->name);
|
||||
if ($type !== null) {
|
||||
return $type;
|
||||
}
|
||||
}
|
||||
|
||||
if ($node instanceof FullyQualified) {
|
||||
return new FullyQualifiedObjectType($node->toString());
|
||||
}
|
||||
|
||||
if ($node instanceof Name) {
|
||||
$name = $node->toString();
|
||||
|
||||
if (ClassExistenceStaticHelper::doesClassLikeExist($name)) {
|
||||
return new FullyQualifiedObjectType($node->toString());
|
||||
}
|
||||
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
throw new NotImplementedException(__METHOD__ . 'for type ' . get_class($node));
|
||||
return $this->phpParserNodeMapper->mapToPHPStanType($node);
|
||||
}
|
||||
|
||||
public function mapPHPStanPhpDocTypeToPHPStanType(PhpDocTagValueNode $phpDocTagValueNode, Node $node): Type
|
||||
@ -178,42 +98,6 @@ final class StaticTypeMapper
|
||||
throw new NotImplementedException(__METHOD__ . ' for ' . get_class($phpDocTagValueNode));
|
||||
}
|
||||
|
||||
public function createTypeHash(Type $type): string
|
||||
{
|
||||
if ($type instanceof MixedType) {
|
||||
return serialize($type);
|
||||
}
|
||||
|
||||
if ($type instanceof ArrayType) {
|
||||
// @todo sort to make different order identical
|
||||
return $this->createTypeHash($type->getItemType()) . '[]';
|
||||
}
|
||||
|
||||
if ($type instanceof ShortenedObjectType) {
|
||||
return $type->getFullyQualifiedName();
|
||||
}
|
||||
|
||||
if ($type instanceof FullyQualifiedObjectType || $type instanceof ObjectType) {
|
||||
return $type->getClassName();
|
||||
}
|
||||
|
||||
if ($type instanceof ConstantType) {
|
||||
if (method_exists($type, 'getValue')) {
|
||||
return get_class($type) . $type->getValue();
|
||||
}
|
||||
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
if ($type instanceof UnionType) {
|
||||
$types = $type->getTypes();
|
||||
sort($types);
|
||||
$type = new UnionType($types);
|
||||
}
|
||||
|
||||
return $this->mapPHPStanTypeToDocString($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Identifier|Name|NullableType|null
|
||||
*/
|
||||
@ -265,154 +149,8 @@ final class StaticTypeMapper
|
||||
public function mapPHPStanPhpDocTypeNodeToPHPStanType(TypeNode $typeNode, Node $node): Type
|
||||
{
|
||||
$nameScope = $this->createNameScopeFromNode($node);
|
||||
// @todo use in the future
|
||||
|
||||
$type = $this->typeNodeResolver->resolve($typeNode, $nameScope);
|
||||
if ($typeNode instanceof GenericTypeNode) {
|
||||
return $type;
|
||||
}
|
||||
|
||||
return $this->customMapPHPStanPhpDocTypeNodeToPHPStanType($typeNode, $node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Move gradualy to @see \PHPStan\PhpDoc\TypeNodeResolver from PHPStan
|
||||
*/
|
||||
private function customMapPHPStanPhpDocTypeNodeToPHPStanType(TypeNode $typeNode, Node $node): Type
|
||||
{
|
||||
if ($typeNode instanceof IdentifierTypeNode) {
|
||||
$type = $this->mapScalarStringToType($typeNode->name);
|
||||
if ($type !== null) {
|
||||
return $type;
|
||||
}
|
||||
|
||||
$loweredName = strtolower($typeNode->name);
|
||||
if ($loweredName === '\string') {
|
||||
return new PreSlashStringType();
|
||||
}
|
||||
|
||||
if ($loweredName === 'class-string') {
|
||||
return new ClassStringType();
|
||||
}
|
||||
|
||||
if ($loweredName === 'self') {
|
||||
/** @var string|null $className */
|
||||
$className = $node->getAttribute(AttributeKey::CLASS_NAME);
|
||||
if ($className === null) {
|
||||
// self outside the class, e.g. in a function
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
return new SelfObjectType($className);
|
||||
}
|
||||
|
||||
if ($loweredName === 'parent') {
|
||||
/** @var string|null $parentClassName */
|
||||
$parentClassName = $node->getAttribute(AttributeKey::PARENT_CLASS_NAME);
|
||||
if ($parentClassName === null) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
return new ParentStaticType($parentClassName);
|
||||
}
|
||||
|
||||
if ($loweredName === 'static') {
|
||||
/** @var string|null $className */
|
||||
$className = $node->getAttribute(AttributeKey::CLASS_NAME);
|
||||
if ($className === null) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
return new StaticType($className);
|
||||
}
|
||||
|
||||
if ($loweredName === 'iterable') {
|
||||
return new IterableType(new MixedType(), new MixedType());
|
||||
}
|
||||
|
||||
// @todo improve - making many false positives now
|
||||
$objectType = new ObjectType($typeNode->name);
|
||||
|
||||
return $this->objectTypeSpecifier->narrowToFullyQualifiedOrAlaisedObjectType($node, $objectType);
|
||||
}
|
||||
|
||||
if ($typeNode instanceof ArrayTypeNode) {
|
||||
$nestedType = $this->mapPHPStanPhpDocTypeNodeToPHPStanType($typeNode->type, $node);
|
||||
|
||||
return new ArrayType(new MixedType(), $nestedType);
|
||||
}
|
||||
|
||||
if ($typeNode instanceof UnionTypeNode || $typeNode instanceof IntersectionTypeNode) {
|
||||
$unionedTypes = [];
|
||||
foreach ($typeNode->types as $unionedTypeNode) {
|
||||
$unionedTypes[] = $this->mapPHPStanPhpDocTypeNodeToPHPStanType($unionedTypeNode, $node);
|
||||
}
|
||||
|
||||
// to prevent missing class error, e.g. in tests
|
||||
return $this->typeFactory->createMixedPassedOrUnionType($unionedTypes);
|
||||
}
|
||||
|
||||
if ($typeNode instanceof ThisTypeNode) {
|
||||
if ($node === null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
/** @var string $className */
|
||||
$className = $node->getAttribute(AttributeKey::CLASS_NAME);
|
||||
|
||||
return new ThisType($className);
|
||||
}
|
||||
|
||||
throw new NotImplementedException(__METHOD__ . ' for ' . get_class($typeNode));
|
||||
}
|
||||
|
||||
private function mapScalarStringToType(string $scalarName): ?Type
|
||||
{
|
||||
$loweredScalarName = Strings::lower($scalarName);
|
||||
if ($loweredScalarName === 'string') {
|
||||
return new StringType();
|
||||
}
|
||||
|
||||
if (in_array($loweredScalarName, ['float', 'real', 'double'], true)) {
|
||||
return new FloatType();
|
||||
}
|
||||
|
||||
if (in_array($loweredScalarName, ['int', 'integer'], true)) {
|
||||
return new IntegerType();
|
||||
}
|
||||
|
||||
if (in_array($loweredScalarName, ['false', 'true', 'bool', 'boolean'], true)) {
|
||||
return new BooleanType();
|
||||
}
|
||||
|
||||
if ($loweredScalarName === 'array') {
|
||||
return new ArrayType(new MixedType(), new MixedType());
|
||||
}
|
||||
|
||||
if ($loweredScalarName === 'null') {
|
||||
return new NullType();
|
||||
}
|
||||
|
||||
if ($loweredScalarName === 'void') {
|
||||
return new VoidType();
|
||||
}
|
||||
|
||||
if ($loweredScalarName === 'object') {
|
||||
return new ObjectWithoutClassType();
|
||||
}
|
||||
|
||||
if ($loweredScalarName === 'resource') {
|
||||
return new ResourceType();
|
||||
}
|
||||
|
||||
if (in_array($loweredScalarName, ['callback', 'callable'], true)) {
|
||||
return new CallableType();
|
||||
}
|
||||
|
||||
if ($loweredScalarName === 'mixed') {
|
||||
return new MixedType(true);
|
||||
}
|
||||
|
||||
return null;
|
||||
return $this->phpDocTypeMapper->mapToPHPStanType($typeNode, $node, $nameScope);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\TypeMapper;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\Exception\NotImplementedException;
|
||||
use Rector\NodeTypeResolver\Contract\PhpParser\PhpParserNodeMapperInterface;
|
||||
|
||||
final class PhpParserNodeMapper
|
||||
{
|
||||
/**
|
||||
* @var PhpParserNodeMapperInterface[]
|
||||
*/
|
||||
private $phpParserNodeMappers = [];
|
||||
|
||||
/**
|
||||
* @param PhpParserNodeMapperInterface[] $phpParserNodeMappers
|
||||
*/
|
||||
public function __construct(array $phpParserNodeMappers)
|
||||
{
|
||||
$this->phpParserNodeMappers = $phpParserNodeMappers;
|
||||
}
|
||||
|
||||
public function mapToPHPStanType(Node $node): Type
|
||||
{
|
||||
foreach ($this->phpParserNodeMappers as $phpParserNodeMapper) {
|
||||
if (! is_a($node, $phpParserNodeMapper->getNodeType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $phpParserNodeMapper->mapToPHPStan($node);
|
||||
}
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\TypeMapper;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\BooleanType;
|
||||
use PHPStan\Type\CallableType;
|
||||
use PHPStan\Type\FloatType;
|
||||
use PHPStan\Type\IntegerType;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\NullType;
|
||||
use PHPStan\Type\ObjectWithoutClassType;
|
||||
use PHPStan\Type\ResourceType;
|
||||
use PHPStan\Type\StringType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\VoidType;
|
||||
|
||||
final class ScalarStringToTypeMapper
|
||||
{
|
||||
public function mapScalarStringToType(string $scalarName): Type
|
||||
{
|
||||
$loweredScalarName = Strings::lower($scalarName);
|
||||
|
||||
$scalarNameByType = [
|
||||
StringType::class => ['string'],
|
||||
FloatType::class => ['float', 'real', 'double'],
|
||||
IntegerType::class => ['int', 'integer'],
|
||||
BooleanType::class => ['false', 'true', 'bool', 'boolean'],
|
||||
NullType::class => ['null'],
|
||||
VoidType::class => ['void'],
|
||||
ResourceType::class => ['resource'],
|
||||
CallableType::class => ['callback', 'callable'],
|
||||
ObjectWithoutClassType::class => ['object'],
|
||||
];
|
||||
|
||||
foreach ($scalarNameByType as $objectType => $scalarNames) {
|
||||
if (! in_array($loweredScalarName, $scalarNames, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return new $objectType();
|
||||
}
|
||||
|
||||
if ($loweredScalarName === 'array') {
|
||||
return new ArrayType(new MixedType(), new MixedType());
|
||||
}
|
||||
|
||||
if ($loweredScalarName === 'mixed') {
|
||||
return new MixedType(true);
|
||||
}
|
||||
|
||||
return new MixedType();
|
||||
}
|
||||
}
|
@ -23,6 +23,14 @@ final class ArrayTypeMapper implements TypeMapperInterface
|
||||
*/
|
||||
private $phpStanStaticTypeMapper;
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function autowireArrayTypeMapper(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void
|
||||
{
|
||||
$this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper;
|
||||
}
|
||||
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return ArrayType::class;
|
||||
@ -74,14 +82,6 @@ final class ArrayTypeMapper implements TypeMapperInterface
|
||||
return implode('|', $docStringTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function autowireArrayTypeMapper(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void
|
||||
{
|
||||
$this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper;
|
||||
}
|
||||
|
||||
private function mapArrayUnionTypeToDocString(ArrayType $arrayType, UnionType $unionType): string
|
||||
{
|
||||
$unionedTypesAsString = [];
|
||||
|
@ -8,8 +8,8 @@ use PhpParser\Node;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
|
||||
use PHPStan\Type\StringType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\TypeWithClassName;
|
||||
use PHPStan\Type\VerbosityLevel;
|
||||
use Rector\Php\PhpVersionProvider;
|
||||
use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;
|
||||
@ -29,17 +29,20 @@ final class TypeWithClassNameTypeMapper implements TypeMapperInterface
|
||||
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return StringType::class;
|
||||
return TypeWithClassName::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param StringType $type
|
||||
* @param TypeWithClassName $type
|
||||
*/
|
||||
public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode
|
||||
{
|
||||
return new IdentifierTypeNode('string');
|
||||
return new IdentifierTypeNode('string-class');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TypeWithClassName $type
|
||||
*/
|
||||
public function mapToPhpParserNode(Type $type, ?string $kind = null): ?Node
|
||||
{
|
||||
if (! $this->phpVersionProvider->isAtLeast(PhpVersionFeature::SCALAR_TYPES)) {
|
||||
@ -49,6 +52,9 @@ final class TypeWithClassNameTypeMapper implements TypeMapperInterface
|
||||
return new Identifier('string');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TypeWithClassName $type
|
||||
*/
|
||||
public function mapToDocString(Type $type, ?Type $parentType = null): string
|
||||
{
|
||||
return $type->describe(VerbosityLevel::typeOnly());
|
||||
|
@ -11,17 +11,12 @@ use PHPStan\Type\NeverType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
|
||||
use Rector\NodeTypeResolver\StaticTypeMapper;
|
||||
use Rector\NodeTypeResolver\PHPStan\TypeHasher;
|
||||
use Rector\PHPStan\TypeFactoryStaticHelper;
|
||||
use Rector\TypeDeclaration\ValueObject\NestedArrayTypeValueObject;
|
||||
|
||||
final class TypeNormalizer
|
||||
{
|
||||
/**
|
||||
* @var StaticTypeMapper
|
||||
*/
|
||||
private $staticTypeMapper;
|
||||
|
||||
/**
|
||||
* @var TypeFactory
|
||||
*/
|
||||
@ -32,10 +27,15 @@ final class TypeNormalizer
|
||||
*/
|
||||
private $collectedNestedArrayTypes = [];
|
||||
|
||||
public function __construct(StaticTypeMapper $staticTypeMapper, TypeFactory $typeFactory)
|
||||
/**
|
||||
* @var TypeHasher
|
||||
*/
|
||||
private $typeHasher;
|
||||
|
||||
public function __construct(TypeFactory $typeFactory, TypeHasher $typeHasher)
|
||||
{
|
||||
$this->staticTypeMapper = $staticTypeMapper;
|
||||
$this->typeFactory = $typeFactory;
|
||||
$this->typeHasher = $typeHasher;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,7 +84,7 @@ final class TypeNormalizer
|
||||
$uniqueTypes = [];
|
||||
$removedKeys = [];
|
||||
foreach ($type->getValueTypes() as $key => $valueType) {
|
||||
$typeHash = $this->staticTypeMapper->createTypeHash($valueType);
|
||||
$typeHash = $this->typeHasher->createTypeHash($valueType);
|
||||
|
||||
$valueType = $this->uniqueateConstantArrayType($valueType);
|
||||
$valueType = $this->normalizeArrayOfUnionToUnionArray($valueType);
|
||||
|
@ -140,7 +140,6 @@ PHP
|
||||
}
|
||||
|
||||
$type = $this->resolveType($node, $injectTagValueNode);
|
||||
|
||||
return $this->refactorPropertyWithAnnotation($node, $type, $tagClass);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user