diff --git a/rules/DeadCode/PhpDoc/DeadParamTagValueNodeAnalyzer.php b/rules/DeadCode/PhpDoc/DeadParamTagValueNodeAnalyzer.php index ef4831f6dac..099cd4bde91 100644 --- a/rules/DeadCode/PhpDoc/DeadParamTagValueNodeAnalyzer.php +++ b/rules/DeadCode/PhpDoc/DeadParamTagValueNodeAnalyzer.php @@ -8,6 +8,7 @@ use PhpParser\Node\Param; use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; +use PHPStan\Type\Generic\TemplateType; use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger; use Rector\BetterPhpDocParser\ValueObject\Type\BracketsAwareUnionTypeNode; use Rector\DeadCode\PhpDoc\Guard\StandaloneTypeRemovalGuard; @@ -15,6 +16,7 @@ use Rector\DeadCode\TypeNodeAnalyzer\MixedArrayTypeNodeAnalyzer; use Rector\NodeNameResolver\NodeNameResolver; use Rector\NodeTypeResolver\TypeComparator\TypeComparator; +use Rector\StaticTypeMapper\StaticTypeMapper; use Rector\TypeDeclaration\NodeAnalyzer\ParamAnalyzer; final class DeadParamTagValueNodeAnalyzer { @@ -53,7 +55,12 @@ final class DeadParamTagValueNodeAnalyzer * @var \Rector\DeadCode\PhpDoc\Guard\StandaloneTypeRemovalGuard */ private $standaloneTypeRemovalGuard; - public function __construct(NodeNameResolver $nodeNameResolver, TypeComparator $typeComparator, GenericTypeNodeAnalyzer $genericTypeNodeAnalyzer, MixedArrayTypeNodeAnalyzer $mixedArrayTypeNodeAnalyzer, ParamAnalyzer $paramAnalyzer, PhpDocTypeChanger $phpDocTypeChanger, StandaloneTypeRemovalGuard $standaloneTypeRemovalGuard) + /** + * @readonly + * @var \Rector\StaticTypeMapper\StaticTypeMapper + */ + private $staticTypeMapper; + public function __construct(NodeNameResolver $nodeNameResolver, TypeComparator $typeComparator, GenericTypeNodeAnalyzer $genericTypeNodeAnalyzer, MixedArrayTypeNodeAnalyzer $mixedArrayTypeNodeAnalyzer, ParamAnalyzer $paramAnalyzer, PhpDocTypeChanger $phpDocTypeChanger, StandaloneTypeRemovalGuard $standaloneTypeRemovalGuard, StaticTypeMapper $staticTypeMapper) { $this->nodeNameResolver = $nodeNameResolver; $this->typeComparator = $typeComparator; @@ -62,6 +69,7 @@ public function __construct(NodeNameResolver $nodeNameResolver, TypeComparator $ $this->paramAnalyzer = $paramAnalyzer; $this->phpDocTypeChanger = $phpDocTypeChanger; $this->standaloneTypeRemovalGuard = $standaloneTypeRemovalGuard; + $this->staticTypeMapper = $staticTypeMapper; } public function isDead(ParamTagValueNode $paramTagValueNode, FunctionLike $functionLike) : bool { @@ -75,6 +83,10 @@ public function isDead(ParamTagValueNode $paramTagValueNode, FunctionLike $funct if ($paramTagValueNode->description !== '') { return \false; } + $docType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($paramTagValueNode->type, $functionLike); + if ($docType instanceof TemplateType) { + return \false; + } if ($param->type instanceof Name && $this->nodeNameResolver->isName($param->type, 'object')) { return $paramTagValueNode->type instanceof IdentifierTypeNode && (string) $paramTagValueNode->type === 'object'; } diff --git a/rules/DeadCode/PhpDoc/DeadReturnTagValueNodeAnalyzer.php b/rules/DeadCode/PhpDoc/DeadReturnTagValueNodeAnalyzer.php index b3044e6c6f5..13f7182bca9 100644 --- a/rules/DeadCode/PhpDoc/DeadReturnTagValueNodeAnalyzer.php +++ b/rules/DeadCode/PhpDoc/DeadReturnTagValueNodeAnalyzer.php @@ -11,6 +11,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode; +use PHPStan\Type\Generic\TemplateType; use PHPStan\Type\TypeCombinator; use PHPStan\Type\UnionType; use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger; @@ -74,6 +75,10 @@ public function isDead(ReturnTagValueNode $returnTagValueNode, $functionLike) : if ($returnTagValueNode->description !== '') { return \false; } + $docType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($returnTagValueNode->type, $functionLike); + if ($docType instanceof TemplateType) { + return \false; + } $scope = $functionLike->getAttribute(AttributeKey::SCOPE); if ($scope instanceof Scope && $scope->isInTrait() && $returnTagValueNode->type instanceof ThisTypeNode) { return \false; diff --git a/rules/DeadCode/PhpDoc/DeadVarTagValueNodeAnalyzer.php b/rules/DeadCode/PhpDoc/DeadVarTagValueNodeAnalyzer.php index 905617a855b..b0c24035074 100644 --- a/rules/DeadCode/PhpDoc/DeadVarTagValueNodeAnalyzer.php +++ b/rules/DeadCode/PhpDoc/DeadVarTagValueNodeAnalyzer.php @@ -5,6 +5,7 @@ use PhpParser\Node\Stmt\Property; use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; +use PHPStan\Type\Generic\TemplateType; use PHPStan\Type\IntersectionType; use PHPStan\Type\ObjectType; use PHPStan\Type\TypeCombinator; @@ -39,6 +40,9 @@ public function isDead(VarTagValueNode $varTagValueNode, Property $property) : b // is strict type superior to doc type? keep strict type only $propertyType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($property->type); $docType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($varTagValueNode->type, $property); + if ($docType instanceof TemplateType) { + return \false; + } if ($propertyType instanceof UnionType && !$docType instanceof UnionType) { return !$docType instanceof IntersectionType; } diff --git a/rules/Php80/Rector/FunctionLike/MixedTypeRector.php b/rules/Php80/Rector/FunctionLike/MixedTypeRector.php index 98c06638407..76d93eb8181 100644 --- a/rules/Php80/Rector/FunctionLike/MixedTypeRector.php +++ b/rules/Php80/Rector/FunctionLike/MixedTypeRector.php @@ -11,7 +11,6 @@ use PhpParser\Node\Stmt\Function_; use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; use PHPStan\Reflection\ClassReflection; -use PHPStan\Type\Generic\TemplateType; use PHPStan\Type\MixedType; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; @@ -141,9 +140,6 @@ private function refactorParamTypes($functionLike, PhpDocInfo $phpDocInfo) : voi if (!$paramType instanceof MixedType) { continue; } - if ($paramType instanceof TemplateType) { - continue; - } $this->hasChanged = \true; $param->type = new Identifier('mixed'); if ($param->flags !== 0) { diff --git a/src/Application/VersionResolver.php b/src/Application/VersionResolver.php index a730f383199..f6ce9391006 100644 --- a/src/Application/VersionResolver.php +++ b/src/Application/VersionResolver.php @@ -19,12 +19,12 @@ final class VersionResolver * @api * @var string */ - public const PACKAGE_VERSION = 'aa7c931b47d3983290d2bec9916862099752e834'; + public const PACKAGE_VERSION = '61a787e62541281bd5f3678fb2819d645c8f0055'; /** * @api * @var string */ - public const RELEASE_DATE = '2024-10-26 19:06:05'; + public const RELEASE_DATE = '2024-10-26 22:09:03'; /** * @var int */ diff --git a/src/NodeTypeResolver/TypeComparator/TypeComparator.php b/src/NodeTypeResolver/TypeComparator/TypeComparator.php index ad671b3b758..5a5a4a6e08c 100644 --- a/src/NodeTypeResolver/TypeComparator/TypeComparator.php +++ b/src/NodeTypeResolver/TypeComparator/TypeComparator.php @@ -65,6 +65,8 @@ public function __construct(TypeHasher $typeHasher, TypeNormalizer $typeNormaliz } public function areTypesEqual(Type $firstType, Type $secondType) : bool { + $firstType = $this->normalizeTemplateType($firstType); + $secondType = $this->normalizeTemplateType($secondType); $firstTypeHash = $this->typeHasher->createTypeHash($firstType); $secondTypeHash = $this->typeHasher->createTypeHash($secondType); if ($firstTypeHash === $secondTypeHash) { @@ -89,9 +91,6 @@ public function arePhpParserAndPhpStanPhpDocTypesEqual(Node $phpParserNode, Type { $phpParserNodeType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($phpParserNode); $phpStanDocType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($phpStanDocTypeNode, $node); - if ($phpStanDocType instanceof TemplateType) { - return \false; - } if (!$this->areTypesEqual($phpParserNodeType, $phpStanDocType) && $this->isSubtype($phpStanDocType, $phpParserNodeType)) { return \false; } @@ -110,6 +109,8 @@ public function arePhpParserAndPhpStanPhpDocTypesEqual(Node $phpParserNode, Type } public function isSubtype(Type $checkedType, Type $mainType) : bool { + $checkedType = $this->normalizeTemplateType($checkedType); + $mainType = $this->normalizeTemplateType($mainType); if ($mainType instanceof MixedType) { return \false; } @@ -121,6 +122,14 @@ public function isSubtype(Type $checkedType, Type $mainType) : bool } return $this->arrayTypeComparator->isSubtype($checkedType, $mainType); } + /** + * unless it by ref, object param has its own life vs redefined variable + * see https://3v4l.org/dI5Pe vs https://3v4l.org/S8i71 + */ + private function normalizeTemplateType(Type $type) : Type + { + return $type instanceof TemplateType ? $type->getBound() : $type; + } private function areAliasedObjectMatchingFqnObject(Type $firstType, Type $secondType) : bool { if ($firstType instanceof AliasedObjectType && $secondType instanceof ObjectType) { diff --git a/vendor/nette/utils/src/Utils/ArrayList.php b/vendor/nette/utils/src/Utils/ArrayList.php index df748b3b205..4bc255fe4a3 100644 --- a/vendor/nette/utils/src/Utils/ArrayList.php +++ b/vendor/nette/utils/src/Utils/ArrayList.php @@ -104,7 +104,7 @@ public function offsetUnset($index) : void } /** * Prepends a item. - * @param mixed $value + * @param T $value */ public function prepend($value) : void {