Skip to content

Commit

Permalink
[TypeDeclaration] Skip intersection with iterable on ReturnTypeDeclar…
Browse files Browse the repository at this point in the history
…ationRector on php 8.1 feature enabled (#3022)

* [TypeDeclaration] Skip intersection with iterable on ReturnTypeDeclarationRector on php 8.1 feature enabled

* update fixture

* Fixed 🎉

* Fix

* final touch: eol

* Final touch: clean up manual check Name, use ReflectionProvider instead

* Final touch: phpstan

* Really Really Final touch: Clean up

* rename fixture

* Revert rename fixture

This reverts commit d775f38.

* Add GenericClassStringTypeMapper

* phpstan

* Final touch: clean up

* Final touch: clean up

* Final touch: remove unnecessary string type check

* Fix CI not load bcadd function

* final touch: phpstan load

* final touch: map AccessoryLiteralStringTypeMapper to literal-string for PHPStan TypeNode

* Final touch: map TypeNode for various String accessory type

* Really Final touch: autowire PHPStanStaticTypeMapper on GenericClassStringTypeMapper

* Final touch: move GenericTypeNode type from ClassStringTypeMapper to dedicated GenericClassStringTypeMapper

* Final touch: move GenericTypeNode type from ClassStringTypeMapper to dedicated GenericClassStringTypeMapper

* Really Really Final touch: ensure check support feature Scalar Types when return string name

* Really Really Final touch: ensure check support feature Scalar Types when return string name

* Really Really Final touch: inject to __construct for GenericClassStringTypeMapper dep

* Final touch: avoid manually define list of excluded type by check aginst ObjectType after verify it is not ObjectWithoutClassType

* Really Really Really Final touch: Remove unnecessary move logic to private method, as can be checked directly now

* Really Final touch: typo fix
  • Loading branch information
samsonasik committed Oct 29, 2022
1 parent af4cf28 commit 997e5f4
Show file tree
Hide file tree
Showing 12 changed files with 304 additions and 65 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

declare(strict_types=1);

namespace Rector\PHPStanStaticTypeMapper\TypeMapper;

use PhpParser\Node;
use PhpParser\Node\Name;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Type\Accessory\AccessoryLiteralStringType;
use PHPStan\Type\Type;
use Rector\Core\Php\PhpVersionProvider;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;

/**
* @implements TypeMapperInterface<AccessoryLiteralStringType>
*/
final class AccessoryLiteralStringTypeMapper implements TypeMapperInterface
{
public function __construct(
private readonly PhpVersionProvider $phpVersionProvider
) {
}

/**
* @return class-string<Type>
*/
public function getNodeClass(): string
{
return AccessoryLiteralStringType::class;
}

/**
* @param AccessoryLiteralStringType $type
*/
public function mapToPHPStanPhpDocTypeNode(Type $type, string $typeKind): TypeNode
{
return new IdentifierTypeNode('literal-string');
}

/**
* @param AccessoryLiteralStringType $type
*/
public function mapToPhpParserNode(Type $type, string $typeKind): ?Node
{
if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) {
return null;
}

return new Name('string');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,20 @@
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
use PHPStan\Type\Type;
use Rector\Core\Php\PhpVersionProvider;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;

/**
* @implements TypeMapperInterface<AccessoryNonEmptyStringType>
*/
final class AccessoryNonEmptyStringTypeMapper implements TypeMapperInterface
{
public function __construct(
private readonly PhpVersionProvider $phpVersionProvider
) {
}

/**
* @return class-string<Type>
*/
Expand All @@ -30,14 +37,18 @@ public function getNodeClass(): string
*/
public function mapToPHPStanPhpDocTypeNode(Type $type, string $typeKind): TypeNode
{
return new IdentifierTypeNode('string');
return new IdentifierTypeNode('non-empty-string');
}

/**
* @param AccessoryNonEmptyStringType $type
*/
public function mapToPhpParserNode(Type $type, string $typeKind): ?Node
{
if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) {
return null;
}

return new Name('string');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,20 @@
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Type\Accessory\AccessoryNonFalsyStringType;
use PHPStan\Type\Type;
use Rector\Core\Php\PhpVersionProvider;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;

/**
* @implements TypeMapperInterface<AccessoryNonFalsyStringType>
*/
final class AccessoryNonFalsyStringTypeMapper implements TypeMapperInterface
{
public function __construct(
private readonly PhpVersionProvider $phpVersionProvider
) {
}

/**
* @return class-string<Type>
*/
Expand All @@ -30,14 +37,18 @@ public function getNodeClass(): string
*/
public function mapToPHPStanPhpDocTypeNode(Type $type, string $typeKind): TypeNode
{
return new IdentifierTypeNode('string');
return new IdentifierTypeNode('non-falsy-string');
}

/**
* @param AccessoryNonFalsyStringType $type
*/
public function mapToPhpParserNode(Type $type, string $typeKind): ?Node
{
if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) {
return null;
}

return new Name('string');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,20 @@
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Type\Accessory\AccessoryNumericStringType;
use PHPStan\Type\Type;
use Rector\Core\Php\PhpVersionProvider;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;

/**
* @implements TypeMapperInterface<AccessoryNumericStringType>
*/
final class AccessoryNumericStringTypeMapper implements TypeMapperInterface
{
public function __construct(
private readonly PhpVersionProvider $phpVersionProvider
) {
}

/**
* @return class-string<Type>
*/
Expand All @@ -30,14 +37,18 @@ public function getNodeClass(): string
*/
public function mapToPHPStanPhpDocTypeNode(Type $type, string $typeKind): TypeNode
{
return new IdentifierTypeNode('string');
return new IdentifierTypeNode('numeric-string');
}

/**
* @param AccessoryNumericStringType $type
*/
public function mapToPhpParserNode(Type $type, string $typeKind): ?Node
{
if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) {
return null;
}

return new Name('string');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,26 @@
namespace Rector\PHPStanStaticTypeMapper\TypeMapper;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Type\ClassStringType;
use PHPStan\Type\Generic\GenericClassStringType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\Core\Php\PhpVersionProvider;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;
use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper;
use Symfony\Contracts\Service\Attribute\Required;

/**
* @implements TypeMapperInterface<ClassStringType>
*/
final class ClassStringTypeMapper implements TypeMapperInterface
{
private PHPStanStaticTypeMapper $phpStanStaticTypeMapper;
public function __construct(
private readonly GenericClassStringTypeMapper $genericClassStringTypeMapper,
private readonly PhpVersionProvider $phpVersionProvider
) {
}

/**
* @return class-string<Type>
Expand All @@ -38,55 +39,22 @@ public function getNodeClass(): string
*/
public function mapToPHPStanPhpDocTypeNode(Type $type, string $typeKind): TypeNode
{
$attributeAwareIdentifierTypeNode = new IdentifierTypeNode('class-string');

if ($type instanceof GenericClassStringType) {
$genericType = $this->resolveGenericObjectType($type);

$genericTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($genericType, $typeKind);
return new GenericTypeNode($attributeAwareIdentifierTypeNode, [$genericTypeNode]);
return $this->genericClassStringTypeMapper->mapToPHPStanPhpDocTypeNode($type, $typeKind);
}

return $attributeAwareIdentifierTypeNode;
return new IdentifierTypeNode('class-string');
}

/**
* @param ClassStringType $type
*/
public function mapToPhpParserNode(Type $type, string $typeKind): ?Node
{
return new Name('string');
}

#[Required]
public function autowire(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void
{
$this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper;
}

private function normalizeType(string $classType): string
{
if (is_a($classType, Expr::class, true)) {
return Expr::class;
if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) {
return null;
}

if (is_a($classType, Node::class, true)) {
return Node::class;
}

return $classType;
}

private function resolveGenericObjectType(GenericClassStringType $genericClassStringType): ObjectType|Type
{
$genericType = $genericClassStringType->getGenericType();

if (! $genericType instanceof ObjectType) {
return $genericType;
}

$className = $genericType->getClassName();
$className = $this->normalizeType($className);
return new ObjectType($className);
return new Name('string');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

declare(strict_types=1);

namespace Rector\PHPStanStaticTypeMapper\TypeMapper;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Type\Generic\GenericClassStringType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\Core\Php\PhpVersionProvider;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;
use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper;
use Symfony\Contracts\Service\Attribute\Required;

/**
* @implements TypeMapperInterface<GenericClassStringType>
*/
final class GenericClassStringTypeMapper implements TypeMapperInterface
{
private PHPStanStaticTypeMapper $phpStanStaticTypeMapper;

public function __construct(
private readonly PhpVersionProvider $phpVersionProvider
) {
}

#[Required]
public function autowire(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void
{
$this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper;
}

/**
* @return class-string<Type>
*/
public function getNodeClass(): string
{
return GenericClassStringType::class;
}

/**
* @param GenericClassStringType $type
*/
public function mapToPHPStanPhpDocTypeNode(Type $type, string $typeKind): TypeNode
{
$attributeAwareIdentifierTypeNode = new IdentifierTypeNode('class-string');
$genericType = $this->resolveGenericObjectType($type);

$genericTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($genericType, $typeKind);
return new GenericTypeNode($attributeAwareIdentifierTypeNode, [$genericTypeNode]);
}

/**
* @param GenericClassStringType $type
*/
public function mapToPhpParserNode(Type $type, string $typeKind): ?Node
{
if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::SCALAR_TYPES)) {
return null;
}

return new Name('string');
}

private function resolveGenericObjectType(GenericClassStringType $genericClassStringType): ObjectType|Type
{
$genericType = $genericClassStringType->getGenericType();

if (! $genericType instanceof ObjectType) {
return $genericType;
}

$className = $genericType->getClassName();
$className = $this->normalizeType($className);
return new ObjectType($className);
}

private function normalizeType(string $classType): string
{
if (is_a($classType, Expr::class, true)) {
return Expr::class;
}

if (is_a($classType, Node::class, true)) {
return Node::class;
}

return $classType;
}
}
Loading

0 comments on commit 997e5f4

Please sign in to comment.