Skip to content

Commit

Permalink
[Php74] Skip array var type filled default null in __construct on Typ…
Browse files Browse the repository at this point in the history
…edPropertyRector (#2029)

Co-authored-by: GitHub Action <action@github.com>
  • Loading branch information
samsonasik and actions-user committed Apr 8, 2022
1 parent 4fb405e commit 4b351a7
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 3 deletions.
5 changes: 5 additions & 0 deletions packages/NodeTypeResolver/NodeTypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
use Rector\NodeTypeResolver\NodeTypeCorrector\GenericClassStringTypeCorrector;
use Rector\NodeTypeResolver\NodeTypeCorrector\HasOffsetTypeCorrector;
use Rector\NodeTypeResolver\NodeTypeResolver\IdentifierTypeResolver;
use Rector\StaticTypeMapper\ValueObject\Type\AliasedObjectType;
use Rector\StaticTypeMapper\ValueObject\Type\ShortenedObjectType;
use Rector\TypeDeclaration\PHPStan\Type\ObjectTypeSpecifier;

Expand Down Expand Up @@ -256,6 +257,10 @@ public function getFullyQualifiedClassName(TypeWithClassName $typeWithClassName)
return $typeWithClassName->getFullyQualifiedName();
}

if ($typeWithClassName instanceof AliasedObjectType) {
return $typeWithClassName->getFullyQualifiedName();
}

return $typeWithClassName->getClassName();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Rector\Tests\Php74\Rector\Property\TypedPropertyRector\Fixture;

final class SkipArrayTypeFilledDefaultNull
{
/**
* @var array
*/
private $property;

public function __construct(array $property = null)
{
$this->property = $property;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

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

final class ArrayTypeFilledDefaultNull
{
/**
* @var array
*/
private $property;

public function __construct(array $property = null)
{
$this->property = $property;
}
}

?>
-----
<?php

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

final class ArrayTypeFilledDefaultNull
{
private ?array $property = null;

public function __construct(array $property = null)
{
$this->property = $property;
}
}

?>
5 changes: 4 additions & 1 deletion rules/Naming/Naming/PropertyNaming.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Rector\Naming\ValueObject\ExpectedName;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\PHPStanStaticTypeMapper\Utils\TypeUnwrapper;
use Rector\StaticTypeMapper\ValueObject\Type\AliasedObjectType;
use Rector\StaticTypeMapper\ValueObject\Type\SelfObjectType;

/**
Expand Down Expand Up @@ -83,7 +84,9 @@ public function getExpectedNameFromType(Type $type): ?ExpectedName
return null;
}

$className = $this->nodeTypeResolver->getFullyQualifiedClassName($type);
$className = $type instanceof AliasedObjectType
? $type->getClassName()
: $this->nodeTypeResolver->getFullyQualifiedClassName($type);

// generic types are usually mix of parent type and specific type - various way to handle it
if ($type instanceof GenericObjectType) {
Expand Down
60 changes: 58 additions & 2 deletions rules/TypeDeclaration/TypeInferer/VarDocPropertyTypeInferer.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use PHPStan\Type\NeverType;
use PHPStan\Type\NullType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeWithClassName;
use PHPStan\Type\UnionType;
use PHPStan\Type\VoidType;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
Expand All @@ -20,6 +21,7 @@
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Core\PhpParser\NodeFinder\PropertyFetchFinder;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
use Rector\PHPStanStaticTypeMapper\DoctrineTypeAnalyzer;
use Rector\TypeDeclaration\AlreadyAssignDetector\ConstructorAssignDetector;
Expand All @@ -38,7 +40,9 @@ public function __construct(
private readonly BetterNodeFinder $betterNodeFinder,
private readonly PropertyFetchFinder $propertyFetchFinder,
private readonly NodeNameResolver $nodeNameResolver,
private readonly PropertyManipulator $propertyManipulator
private readonly PropertyManipulator $propertyManipulator,
private readonly AssignToPropertyTypeInferer $assignToPropertyTypeInferer,
private readonly NodeTypeResolver $nodeTypeResolver
) {
}

Expand All @@ -51,6 +55,11 @@ public function inferProperty(Property $property): Type
return new MixedType();
}

$class = $this->betterNodeFinder->findParentType($property, Class_::class);
if (! $class instanceof Class_) {
return new MixedType();
}

// default value type must be added to each resolved type if set
$propertyDefaultValue = $property->props[0]->default;
if ($propertyDefaultValue instanceof Expr) {
Expand All @@ -59,7 +68,54 @@ public function inferProperty(Property $property): Type
$resolvedType = $this->makeNullableForAccessedBeforeInitialization($property, $resolvedType, $phpDocInfo);
}

return $this->genericClassStringTypeNormalizer->normalize($resolvedType);
$resolvedType = $this->genericClassStringTypeNormalizer->normalize($resolvedType);
$propertyName = $this->nodeNameResolver->getName($property);
$assignInferredPropertyType = $this->assignToPropertyTypeInferer->inferPropertyInClassLike(
$property,
$propertyName,
$class
);

if (! $assignInferredPropertyType instanceof Type) {
return $resolvedType;
}

if ($this->isAssignInferredUnionTypesMoreThanResolvedType($resolvedType, $assignInferredPropertyType)) {
return new MixedType();
}

if ($resolvedType::class === $assignInferredPropertyType::class) {
return $resolvedType;
}

if ($this->isBothTypeWithClassName($resolvedType, $assignInferredPropertyType)) {
/** @var TypeWithClassName $resolvedType */
$classNameResolvedType = $this->nodeTypeResolver->getFullyQualifiedClassName($resolvedType);
/** @var TypeWithClassName $assignInferredPropertyType */
$classNameInferredPropertyType = $this->nodeTypeResolver->getFullyQualifiedClassName(
$assignInferredPropertyType
);

if ($classNameResolvedType === $classNameInferredPropertyType) {
return $resolvedType;
}
}

return new MixedType();
}

private function isAssignInferredUnionTypesMoreThanResolvedType(
Type $resolvedType,
Type $assignInferredPropertyType
): bool {
return $resolvedType instanceof UnionType && $assignInferredPropertyType instanceof UnionType && count(
$assignInferredPropertyType->getTypes()
) > count($resolvedType->getTypes());
}

private function isBothTypeWithClassName(Type $resolvedType, Type $assignInferredPropertyType): bool
{
return $resolvedType instanceof TypeWithClassName && $assignInferredPropertyType instanceof TypeWithClassName;
}

private function makeNullableForAccessedBeforeInitialization(
Expand Down

0 comments on commit 4b351a7

Please sign in to comment.