Skip to content

Commit

Permalink
[Downgrade PHP 8.0] Fix downgrade type copying to property (#1592)
Browse files Browse the repository at this point in the history
Co-authored-by: Ondrej Mirtes <ondrej@mirtes.cz>
Co-authored-by: GitHub Action <action@github.com>
  • Loading branch information
3 people authored Dec 30, 2021
1 parent 58db667 commit b25e351
Show file tree
Hide file tree
Showing 16 changed files with 207 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ public function test(Type $firstType, Type $secondType, bool $areExpectedEqual):
$this->assertSame($areExpectedEqual, $areEqual);
}

/**
* @return Iterator<bool[]|BooleanType[]|StringType[]>
*/
public function provideData(): Iterator
{
yield [new StringType(), new BooleanType(), false];
Expand Down
1 change: 0 additions & 1 deletion packages/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ public function getTagsByName(string $name): array
public function getParamType(string $name): Type
{
$paramTagValueNodes = $this->getParamTagValueByName($name);

return $this->getTypeOrMixed($paramTagValueNodes);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\MixedType;
use PHPStan\Type\NeverType;
Expand Down Expand Up @@ -62,6 +63,7 @@ public function changeVarType(PhpDocInfo $phpDocInfo, Type $newType): void
} else {
// add completely new one
$varTagValueNode = new VarTagValueNode($newPHPStanPhpDocType, '', '');

$phpDocInfo->addTagValueNode($varTagValueNode);
}
}
Expand Down Expand Up @@ -171,4 +173,11 @@ public function copyPropertyDocToParam(Property $property, Param $param): void

$this->changeParamType($phpDocInfo, $paramType, $param, $paramVarName);
}

public function changeVarTypeNode(PhpDocInfo $phpDocInfo, TypeNode $typeNode): void
{
// add completely new one
$varTagValueNode = new VarTagValueNode($typeNode, '', '');
$phpDocInfo->addTagValueNode($varTagValueNode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): Type
return new IdentifierTypeNode('false');
}

if ($type instanceof ConstantBooleanType) {
// cannot be parent of union
return new IdentifierTypeNode('true');
}

return new IdentifierTypeNode('bool');
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use Rector\StaticTypeMapper\ValueObject\Type\AliasedObjectType;
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedGenericObjectType;
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
use Rector\StaticTypeMapper\ValueObject\Type\NonExistingObjectType;
use Rector\StaticTypeMapper\ValueObject\Type\SelfObjectType;
use Rector\StaticTypeMapper\ValueObject\Type\ShortenedObjectType;
use Symfony\Contracts\Service\Attribute\Required;
Expand Down Expand Up @@ -56,6 +57,11 @@ public function mapToPHPStanPhpDocTypeNode(Type $type, TypeKind $typeKind): Type
return $this->mapGenericObjectType($type, $typeKind);
}

if ($type instanceof NonExistingObjectType) {
// possibly generic type
return new IdentifierTypeNode($type->getClassName());
}

return new IdentifierTypeNode('\\' . $type->getClassName());
}

Expand Down
3 changes: 3 additions & 0 deletions packages/StaticTypeMapper/PhpDocParser/NullableTypeMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ public function getNodeType(): string
return NullableTypeNode::class;
}

/**
* @param NullableTypeNode $typeNode
*/
public function mapToPHPStanType(TypeNode $typeNode, Node $node, NameScope $nameScope): Type
{
$type = $typeNode->type;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace Rector\Tests\DowngradePhp80\Rector\Class_\DowngradePropertyPromotionRector\Fixture;

class ArrayShapes
{
/** @param array<string, array{\stdClass, \Exception}> $value */
public function __construct(private array $value)
{
}
}

?>
-----
<?php

namespace Rector\Tests\DowngradePhp80\Rector\Class_\DowngradePropertyPromotionRector\Fixture;

class ArrayShapes
{
/**
* @var array<string, array{\stdClass, \Exception}>
*/
private array $value;
/** @param array<string, array{\stdClass, \Exception}> $value */
public function __construct(array $value)
{
$this->value = $value;
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace Rector\Tests\DowngradePhp80\Rector\Class_\DowngradePropertyPromotionRector\Fixture;

class MixedTypeInPhpDoc
{
/** @param mixed $value */
public function __construct(public $value)
{
}
}

?>
-----
<?php

namespace Rector\Tests\DowngradePhp80\Rector\Class_\DowngradePropertyPromotionRector\Fixture;

class MixedTypeInPhpDoc
{
/**
* @var mixed
*/
public $value;
/** @param mixed $value */
public function __construct($value)
{
$this->value = $value;
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace Rector\Tests\DowngradePhp80\Rector\Class_\DowngradePropertyPromotionRector\Fixture;

/** @template T of \stdClass */
class TemplateType
{
/** @param T $value */
public function __construct(private \stdClass $value)
{
}
}

?>
-----
<?php

namespace Rector\Tests\DowngradePhp80\Rector\Class_\DowngradePropertyPromotionRector\Fixture;

/** @template T of \stdClass */
class TemplateType
{
/**
* @var T
*/
private \stdClass $value;
/** @param T $value */
public function __construct(\stdClass $value)
{
$this->value = $value;
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace Rector\Tests\DowngradePhp80\Rector\Class_\DowngradePropertyPromotionRector\Fixture;

class TrueVsBool
{
/** @param array<string, true> $value */
public function __construct(private array $value)
{
}
}

?>
-----
<?php

namespace Rector\Tests\DowngradePhp80\Rector\Class_\DowngradePropertyPromotionRector\Fixture;

class TrueVsBool
{
/**
* @var array<string, true>
*/
private array $value;
/** @param array<string, true> $value */
public function __construct(array $value)
{
$this->value = $value;
}
}

?>
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddArrayReturnDocTypeRector\
final class FullyQualifiedName
{
/**
* @return \Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddArrayReturnDocTypeRector\Source\ValidationResult[]|bool[]
* @return bool[]|\Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddArrayReturnDocTypeRector\Source\ValidationResult[]
*/
public function isValidDataProvider(): array
{
Expand Down
7 changes: 6 additions & 1 deletion rules/CodeQuality/NodeFactory/PropertyTypeDecorator.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Rector\Core\Php\PhpVersionProvider;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
use Rector\Privatization\TypeManipulator\TypeNormalizer;
use Rector\StaticTypeMapper\StaticTypeMapper;

final class PropertyTypeDecorator
Expand All @@ -23,12 +24,16 @@ public function __construct(
private readonly PhpVersionProvider $phpVersionProvider,
private readonly StaticTypeMapper $staticTypeMapper,
private readonly PhpDocTypeChanger $phpDocTypeChanger,
private readonly PhpDocInfoFactory $phpDocInfoFactory
private readonly PhpDocInfoFactory $phpDocInfoFactory,
private readonly TypeNormalizer $typeNormalizer,
) {
}

public function decorateProperty(Property $property, Type $propertyType): void
{
// generalize false/true type to bool, as mostly default value but accepts both
$propertyType = $this->typeNormalizer->generalizeConstantBoolTypes($propertyType);

$this->decoratePropertyWithVarDoc($property, $propertyType);
$this->decoratePropertyWithType($property, $propertyType);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\TypeComparator\TypeComparator;
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
use Rector\Privatization\TypeManipulator\TypeNormalizer;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;

Expand All @@ -28,7 +29,8 @@ final class VarConstantCommentRector extends AbstractRector
{
public function __construct(
private readonly TypeComparator $typeComparator,
private readonly PhpDocTypeChanger $phpDocTypeChanger
private readonly PhpDocTypeChanger $phpDocTypeChanger,
private readonly TypeNormalizer $typeNormalizer,
) {
}

Expand Down Expand Up @@ -81,6 +83,9 @@ public function refactor(Node $node): ?Node
return null;
}

// generalize false/true type to bool, as mostly default value but accepts both
$constType = $this->typeNormalizer->generalizeConstantBoolTypes($constType);

$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);

if ($this->shouldSkipConstantArrayType($constType, $phpDocInfo)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Property;
use PHPStan\Type\MixedType;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
use Rector\Core\Exception\ShouldNotHappenException;
Expand Down Expand Up @@ -207,6 +207,7 @@ private function createPropertiesFromParams(array $params): array
$property = $this->nodeFactory->createProperty($name);
$property->flags = $param->flags;
$property->type = $param->type;

$this->decoratePropertyWithParamDocInfo($param, $property);

$hasNew = $param->default === null
Expand Down Expand Up @@ -240,14 +241,12 @@ private function decoratePropertyWithParamDocInfo(Param $param, Property $proper
return;
}

$type = $phpDocInfo->getParamType($name);

// MixedType likely means there was no param type defined
if ($type instanceof MixedType) {
$paramTagValueNode = $phpDocInfo->getParamTagValueByName($name);
if (! $paramTagValueNode instanceof ParamTagValueNode) {
return;
}

$propertyDocInfo = $this->phpDocInfoFactory->createEmpty($property);
$this->phpDocTypeChanger->changeVarType($propertyDocInfo, $type);
$this->phpDocTypeChanger->changeVarTypeNode($propertyDocInfo, $paramTagValueNode->type);
}
}
28 changes: 28 additions & 0 deletions rules/Privatization/TypeManipulator/TypeNormalizer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Rector\Privatization\TypeManipulator;

use PHPStan\Type\BooleanType;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeTraverser;

final class TypeNormalizer
{
/**
* Generalize false/true type to bool,
* as mostly default value but accepts both
*/
public function generalizeConstantBoolTypes(Type $type): Type
{
return TypeTraverser::map($type, function (Type $type, callable $traverseCallback): BooleanType|Type {
if ($type instanceof ConstantBooleanType) {
return new BooleanType();
}

return $traverseCallback($type, $traverseCallback);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use Rector\DeadCode\PhpDoc\TagRemover\ReturnTagRemover;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Privatization\TypeManipulator\NormalizeTypeToRespectArrayScalarType;
use Rector\Privatization\TypeManipulator\TypeNormalizer;
use Rector\TypeDeclaration\NodeTypeAnalyzer\DetailedTypeAnalyzer;
use Rector\TypeDeclaration\TypeAnalyzer\AdvancedArrayAnalyzer;
use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer;
Expand All @@ -43,6 +44,7 @@ public function __construct(
private readonly NormalizeTypeToRespectArrayScalarType $normalizeTypeToRespectArrayScalarType,
private readonly ReturnTagRemover $returnTagRemover,
private readonly DetailedTypeAnalyzer $detailedTypeAnalyzer,
private readonly TypeNormalizer $typeNormalizer,
) {
}

Expand Down Expand Up @@ -118,6 +120,9 @@ public function refactor(Node $node): ?Node
$node->returnType
);

// generalize false/true type to bool, as mostly default value but accepts both
$inferredReturnType = $this->typeNormalizer->generalizeConstantBoolTypes($inferredReturnType);

$currentReturnType = $phpDocInfo->getReturnType();

if ($this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethodOldTypeWithNewType(
Expand Down

0 comments on commit b25e351

Please sign in to comment.