Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ecs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ parameters:
- 'src/Rector/AbstractRector.php'

Symplify\CodingStandard\Sniffs\CleanCode\CognitiveComplexitySniff:
- 'packages/NodeTypeResolver/src/PhpDoc/NodeAnalyzer/DocBlockManipulator.php'
- 'packages/MinimalScope/src/Rector/Class_/ChangeLocalPropertyToVariableRector.php'
- 'packages/CodingStyle/src/Rector/ClassMethod/NewlineBeforeNewAssignSetRector.php'
# solve later
Expand Down
28 changes: 28 additions & 0 deletions packages/BetterPhpDocParser/src/PhpDocInfo/PhpDocInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,11 @@ public function hasByType(string $type): bool
return (bool) $this->getByType($type);
}

public function hasByName(string $name): bool
{
return (bool) $this->getTagsByName($name);
}

public function getByType(string $type): ?PhpDocTagValueNode
{
$this->ensureTypeIsTagValueNode($type, __METHOD__);
Expand Down Expand Up @@ -256,6 +261,21 @@ public function removeByType(string $type): void
}
}

public function removeByName(string $nameToRemove): void
{
foreach ($this->phpDocNode->children as $key => $phpDocChildNode) {
if (! $phpDocChildNode instanceof PhpDocTagNode) {
continue;
}

if (! $this->areAnnotationNamesEqual($nameToRemove, $phpDocChildNode->name)) {
continue;
}

unset($this->phpDocNode->children[$key]);
}
}

private function getParamTagValueByName(string $name): ?AttributeAwareParamTagValueNode
{
$phpDocNode = $this->getPhpDocNode();
Expand Down Expand Up @@ -283,4 +303,12 @@ private function ensureTypeIsTagValueNode(string $type, string $location): void
PhpDocTagValueNode::class
));
}

private function areAnnotationNamesEqual(string $firstAnnotationName, string $secondAnnotationName): bool
{
$firstAnnotationName = trim($firstAnnotationName, '@');
$secondAnnotationName = trim($secondAnnotationName, '@');

return $firstAnnotationName === $secondAnnotationName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Return_;
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\Exception\ShouldNotHappenException;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PhpParser\NodeTransformer;
Expand Down Expand Up @@ -156,8 +158,11 @@ private function transformArrayToYieldsOnMethodNode(ClassMethod $classMethod, Ar

$this->removeNode($parentNode);

// remove doc block
$this->docBlockManipulator->removeTagFromNode($classMethod, 'return');
/** @var PhpDocInfo|null $phpDocInfo */
$phpDocInfo = $classMethod->getAttribute(AttributeKey::PHP_DOC_INFO);
if ($phpDocInfo instanceof PhpDocInfo) {
$phpDocInfo->removeByType(ReturnTagValueNode::class);
}

// change return typehint
$classMethod->returnType = new FullyQualified(Iterator::class);
Expand Down
107 changes: 107 additions & 0 deletions packages/NodeTypeResolver/src/PHPStan/TypeComparator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

declare(strict_types=1);

namespace Rector\NodeTypeResolver\PHPStan;

use PHPStan\Type\ArrayType;
use PHPStan\Type\BooleanType;
use PHPStan\Type\FloatType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use Rector\PHPStan\Type\AliasedObjectType;
use Rector\PHPStan\Type\ShortenedObjectType;

final class TypeComparator
{
/**
* @var TypeHasher
*/
private $typeHasher;

public function __construct(TypeHasher $typeHasher)
{
$this->typeHasher = $typeHasher;
}

public function areTypesEquals(Type $firstType, Type $secondType): bool
{
if ($this->areBothSameScalarType($firstType, $secondType)) {
return true;
}

// aliases and types
if ($this->areAliasedObjectMatchingFqnObject($firstType, $secondType)) {
return true;
}

if ($this->typeHasher->areTypesEqual($firstType, $secondType)) {
return true;
}

return $this->areArrayTypeWithSingleObjectChildToParent($firstType, $secondType);
}

private function areBothSameScalarType(Type $firstType, Type $secondType): bool
{
if ($firstType instanceof StringType && $secondType instanceof StringType) {
return true;
}

if ($firstType instanceof IntegerType && $secondType instanceof IntegerType) {
return true;
}

if ($firstType instanceof FloatType && $secondType instanceof FloatType) {
return true;
}
return $firstType instanceof BooleanType && $secondType instanceof BooleanType;
}

private function areAliasedObjectMatchingFqnObject(Type $firstType, Type $secondType): bool
{
if ($firstType instanceof AliasedObjectType && $secondType instanceof ObjectType && $firstType->getFullyQualifiedClass() === $secondType->getClassName()) {
return true;
}
return $secondType instanceof AliasedObjectType && $firstType instanceof ObjectType && $secondType->getFullyQualifiedClass() === $firstType->getClassName();
}

/**
* E.g. class A extends B, class B → A[] is subtype of B[] → keep A[]
*/
private function areArrayTypeWithSingleObjectChildToParent(Type $firstType, Type $secondType): bool
{
if (! $firstType instanceof ArrayType || ! $secondType instanceof ArrayType) {
return false;
}

$firstArrayItemType = $firstType->getItemType();
$secondArrayItemType = $secondType->getItemType();

if ($firstArrayItemType instanceof ObjectType && $secondArrayItemType instanceof ObjectType) {
$firstFqnClassName = $this->getFqnClassName($firstArrayItemType);
$secondFqnClassName = $this->getFqnClassName($secondArrayItemType);

if (is_a($firstFqnClassName, $secondFqnClassName, true)) {
return true;
}

if (is_a($secondFqnClassName, $firstFqnClassName, true)) {
return true;
}
}

return false;
}

private function getFqnClassName(ObjectType $objectType): string
{
if ($objectType instanceof ShortenedObjectType) {
return $objectType->getFullyQualifiedName();
}

return $objectType->getClassName();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\NodeTypeResolver\Contract\PerNodeTypeResolver\PerNodeTypeResolverInterface;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
use Rector\PhpParser\Node\Resolver\NameResolver;
use Rector\PhpParser\NodeTraverser\CallableNodeTraverser;

Expand All @@ -30,11 +30,6 @@ final class ParamTypeResolver implements PerNodeTypeResolverInterface
*/
private $nameResolver;

/**
* @var DocBlockManipulator
*/
private $docBlockManipulator;

/**
* @var CallableNodeTraverser
*/
Expand All @@ -45,13 +40,9 @@ final class ParamTypeResolver implements PerNodeTypeResolverInterface
*/
private $nodeTypeResolver;

public function __construct(
NameResolver $nameResolver,
DocBlockManipulator $docBlockManipulator,
CallableNodeTraverser $callableNodeTraverser
) {
public function __construct(NameResolver $nameResolver, CallableNodeTraverser $callableNodeTraverser)
{
$this->nameResolver = $nameResolver;
$this->docBlockManipulator = $docBlockManipulator;
$this->callableNodeTraverser = $callableNodeTraverser;
}

Expand Down Expand Up @@ -129,7 +120,13 @@ private function resolveFromFunctionDocBlock(Param $param): Type
/** @var string $paramName */
$paramName = $this->nameResolver->getName($param);

return $this->docBlockManipulator->getParamTypeByName($parentNode, '$' . $paramName);
/** @var PhpDocInfo|null $phpDocInfo */
$phpDocInfo = $parentNode->getAttribute(AttributeKey::PHP_DOC_INFO);
if ($phpDocInfo === null) {
return new MixedType();
}

return $phpDocInfo->getParamType($paramName);
}

private function resolveFromType(Node $node)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
use PHPStan\Analyser\Scope;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\NodeTypeResolver\Contract\PerNodeTypeResolver\PerNodeTypeResolverInterface;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
use Rector\NodeTypeResolver\PHPStan\Collector\TraitNodeScopeCollector;
use Rector\PhpParser\Node\Resolver\NameResolver;

Expand All @@ -23,11 +23,6 @@
*/
final class VariableTypeResolver implements PerNodeTypeResolverInterface
{
/**
* @var DocBlockManipulator
*/
private $docBlockManipulator;

/**
* @var NameResolver
*/
Expand All @@ -43,12 +38,8 @@ final class VariableTypeResolver implements PerNodeTypeResolverInterface
*/
private $traitNodeScopeCollector;

public function __construct(
DocBlockManipulator $docBlockManipulator,
NameResolver $nameResolver,
TraitNodeScopeCollector $traitNodeScopeCollector
) {
$this->docBlockManipulator = $docBlockManipulator;
public function __construct(NameResolver $nameResolver, TraitNodeScopeCollector $traitNodeScopeCollector)
{
$this->nameResolver = $nameResolver;
$this->traitNodeScopeCollector = $traitNodeScopeCollector;
}
Expand Down Expand Up @@ -82,7 +73,12 @@ public function resolve(Node $variableNode): Type
}

// get from annotation
return $this->docBlockManipulator->getVarType($variableNode);
$phpDocInfo = $variableNode->getAttribute(AttributeKey::PHP_DOC_INFO);
if ($phpDocInfo instanceof PhpDocInfo) {
$phpDocInfo->getVarType();
}

return new MixedType();
}

/**
Expand Down
Loading