Skip to content

Commit

Permalink
[TypeDeclaration] Add If else assign support on TypedPropertyFromAssi…
Browse files Browse the repository at this point in the history
…gnsRector (#5314)

Co-authored-by: GitHub Action <actions@github.com>
  • Loading branch information
samsonasik and actions-user committed Dec 3, 2023
1 parent 368075b commit be924be
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 6 deletions.
8 changes: 8 additions & 0 deletions packages/NodeTypeResolver/PHPStan/Type/TypeFactory.php
Expand Up @@ -4,6 +4,8 @@

namespace Rector\NodeTypeResolver\PHPStan\Type;

use Rector\NodeTypeResolver\PHPStan\ObjectWithoutClassTypeWithParentTypes;
use PHPStan\Type\ObjectType;
use PHPStan\Type\ArrayType;
use PHPStan\Type\BooleanType;
use PHPStan\Type\Constant\ConstantArrayType;
Expand Down Expand Up @@ -58,8 +60,14 @@ public function uniquateTypes(array $types, bool $keepConstant = false): array
{
$constantTypeHashes = [];
$uniqueTypes = [];
$totalTypes = count($types);

foreach ($types as $type) {
if ($totalTypes > 1 && $type instanceof ObjectWithoutClassTypeWithParentTypes) {
$parents = $type->getParentTypes();
$type = new ObjectType($parents[0]->getClassName());
}

$removedConstantType = $this->removeValueFromConstantType($type);
$removedConstantTypeHash = $this->typeHasher->createTypeHash($removedConstantType);

Expand Down
Expand Up @@ -24,7 +24,7 @@ namespace Rector\Tests\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsR

final class AnonymousExtendsExistingClassInUnion
{
private ?\DateTime $x = null;
private \DateTime $x;

public function __construct()
{
Expand Down
@@ -0,0 +1,39 @@
<?php

namespace Rector\Tests\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsRector\Fixture;

final class IfElseAssign
{
private $apiUrl;

public function __construct()
{
if (rand(0,1)) {
$this->apiUrl = 'https://example.com/';
} else {
$this->apiUrl = 'https://another.example.com/';
}
}
}

?>
-----
<?php

namespace Rector\Tests\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsRector\Fixture;

final class IfElseAssign
{
private string $apiUrl;

public function __construct()
{
if (rand(0,1)) {
$this->apiUrl = 'https://example.com/';
} else {
$this->apiUrl = 'https://another.example.com/';
}
}
}

?>
@@ -0,0 +1,43 @@
<?php

namespace Rector\Tests\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsRector\Fixture;

final class IfElseAssignComplex
{
private $apiUrl;

public function __construct()
{
if (rand(0,1)) {
if (rand(0, 1)) {
$this->apiUrl = 'https://example.com/';
}
} else {
$this->apiUrl = 'https://another.example.com/';
}
}
}

?>
-----
<?php

namespace Rector\Tests\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsRector\Fixture;

final class IfElseAssignComplex
{
private ?string $apiUrl = null;

public function __construct()
{
if (rand(0,1)) {
if (rand(0, 1)) {
$this->apiUrl = 'https://example.com/';
}
} else {
$this->apiUrl = 'https://another.example.com/';
}
}
}

?>
Expand Up @@ -2,7 +2,7 @@

namespace Rector\Tests\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsRector\FixtureComplexTypes;

final class AnonymousExtendsExistingClassInUnion
final class AnonymousExtendsExistingClassInUnion2
{
private $x;

Expand All @@ -22,9 +22,9 @@ final class AnonymousExtendsExistingClassInUnion

namespace Rector\Tests\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsRector\FixtureComplexTypes;

final class AnonymousExtendsExistingClassInUnion
final class AnonymousExtendsExistingClassInUnion2
{
private ?\DateTime $x = null;
private \DateTime $x;

public function __construct()
{
Expand Down
Expand Up @@ -28,7 +28,7 @@ namespace Rector\Tests\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsR

final class AnonymousExtendsExistingClassDocblockExists
{
private ?\DateTime $x = null;
private \DateTime $x;

public function __construct()
{
Expand Down
10 changes: 9 additions & 1 deletion rules/DeadCode/PhpDoc/DeadVarTagValueNodeAnalyzer.php
Expand Up @@ -7,6 +7,7 @@
use PhpParser\Node\Stmt\Property;
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\UnionType;
use Rector\NodeTypeResolver\TypeComparator\TypeComparator;
use Rector\StaticTypeMapper\StaticTypeMapper;
Expand Down Expand Up @@ -37,10 +38,17 @@ public function isDead(VarTagValueNode $varTagValueNode, Property $property): bo
return ! $docType instanceof IntersectionType;
}

return $this->typeComparator->arePhpParserAndPhpStanPhpDocTypesEqual(
if ($this->typeComparator->arePhpParserAndPhpStanPhpDocTypesEqual(
$property->type,
$varTagValueNode->type,
$property
)) {
return true;
}

return $docType instanceof UnionType && $this->typeComparator->areTypesEqual(
TypeCombinator::removeNull($docType),
$propertyType
);
}
}
Expand Up @@ -7,9 +7,12 @@
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Else_;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\If_;
use PhpParser\NodeTraverser;
use PHPStan\Type\ObjectType;
use Rector\Core\NodeAnalyzer\PropertyFetchAnalyzer;
Expand Down Expand Up @@ -50,6 +53,11 @@ public function isPropertyAssigned(ClassLike $classLike, string $propertyName):
$this->simpleCallableNodeTraverser->traverseNodesWithCallable((array) $initializeClassMethod->stmts, function (
Node $node
) use ($propertyName, &$isAssignedInConstructor): ?int {
if ($this->isIfElseAssign($node, $propertyName)) {
$isAssignedInConstructor = true;
return NodeTraverser::STOP_TRAVERSAL;
}

$expr = $this->matchAssignExprToPropertyName($node, $propertyName);
if (! $expr instanceof Expr) {
return null;
Expand Down Expand Up @@ -77,6 +85,39 @@ public function isPropertyAssigned(ClassLike $classLike, string $propertyName):
return $isAssignedInConstructor;
}

/**
* @param Stmt[] $stmts
*/
private function isAssignedInStmts(array $stmts, string $propertyName): bool
{
$isAssigned = false;
foreach ($stmts as $stmt) {
// non Expression can be on next stmt
if (! $stmt instanceof Expression) {
$isAssigned = false;
break;
}

if ($this->matchAssignExprToPropertyName($stmt->expr, $propertyName) instanceof Expr) {
$isAssigned = true;
}
}

return $isAssigned;
}

private function isIfElseAssign(Node $node, string $propertyName): bool
{
if (! $node instanceof If_ || $node->elseifs !== [] || ! $node->else instanceof Else_) {
return false;
}

return $this->isAssignedInStmts($node->stmts, $propertyName) && $this->isAssignedInStmts(
$node->else->stmts,
$propertyName
);
}

private function matchAssignExprToPropertyName(Node $node, string $propertyName): ?Expr
{
if (! $node instanceof Assign) {
Expand Down

0 comments on commit be924be

Please sign in to comment.