[Feature] Add ParamAnnotationIncorrectNullableRector for fixing incorrect null type in @param (#2069)

Co-authored-by: Jiří Bok <jiri.bok@protonmail.com>
This commit is contained in:
dorrogeray 2022-04-14 10:03:45 +02:00 committed by GitHub
parent 1c1732f017
commit c509923516
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 959 additions and 2 deletions

View File

@ -1,4 +1,4 @@
# 510 Rules Overview
# 511 Rules Overview
<br>
@ -88,7 +88,7 @@
- [Transform](#transform) (36)
- [TypeDeclaration](#typedeclaration) (25)
- [TypeDeclaration](#typedeclaration) (26)
- [Visibility](#visibility) (3)
@ -11600,6 +11600,30 @@ Change null in argument, that is now not nullable anymore
<br>
### ParamAnnotationIncorrectNullableRector
Add or remove null type from `@param` phpdoc typehint based on php parameter type declaration
- class: [`Rector\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector`](../rules/TypeDeclaration/Rector/ClassMethod/ParamAnnotationIncorrectNullableRector.php)
```diff
final class SomeClass
{
/**
- * @param \DateTime[] $dateTimes
+ * @param \DateTime[]|null $dateTimes
*/
public function setDateTimes(?array $dateTimes): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}
```
<br>
### ParamTypeByMethodCallTypeRector
Change param type based on passed method call type

View File

@ -0,0 +1,47 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamAnnotationComplex
{
/**
* @Serializer\VirtualProperty
* @Serializer\Type("array<DateTime>")
* @Assert\All({
* @Assert\NotBlank,
* @AppAssert\Country,
* })
* @param \DateTime[] $dateTimes
*/
public function setDateTimes(?array $dateTimes): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}
?>
-----
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamAnnotationComplex
{
/**
* @Serializer\VirtualProperty
* @Serializer\Type("array<DateTime>")
* @Assert\All({
* @Assert\NotBlank,
* @AppAssert\Country,
* })
* @param \DateTime[]|null $dateTimes
*/
public function setDateTimes(?array $dateTimes): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}
?>

View File

@ -0,0 +1,27 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
/**
* @param \DateTime[]|null $dateTimes
*/
function reverseDateTimes(array $dateTimes): array
{
return array_reverse($dateTimes);
}
?>
-----
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
/**
* @param \DateTime[] $dateTimes
*/
function reverseDateTimes(array $dateTimes): array
{
return array_reverse($dateTimes);
}
?>

View File

@ -0,0 +1,35 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamAnnotationIncorrectlyIncludesNullOnScalar
{
/**
* @param string|null $text
*/
public function setDateTimes(string $text): self
{
$this->text = $text;
return $this;
}
}
?>
-----
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamAnnotationIncorrectlyIncludesNullOnScalar
{
/**
* @param string $text
*/
public function setDateTimes(string $text): self
{
$this->text = $text;
return $this;
}
}
?>

View File

@ -0,0 +1,35 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamAnnotationIncorrectlyIncludesNullWithNonNullDefaultValue
{
/**
* @param bool|null $flag
*/
public function setFlag(bool $flag = true): self
{
$this->flag = $flag;
return $this;
}
}
?>
-----
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamAnnotationIncorrectlyIncludesNullWithNonNullDefaultValue
{
/**
* @param bool $flag
*/
public function setFlag(bool $flag = true): self
{
$this->flag = $flag;
return $this;
}
}
?>

View File

@ -0,0 +1,39 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamAnnotationIncorrectlyIncludesNullWithNonNullDefaultValueFromConst
{
private const FLAG_DEFAULT_VALUE = true;
/**
* @param bool|null $flag
*/
public function setFlag(bool $flag = self::FLAG_DEFAULT_VALUE): self
{
$this->flag = $flag;
return $this;
}
}
?>
-----
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamAnnotationIncorrectlyIncludesNullWithNonNullDefaultValueFromConst
{
private const FLAG_DEFAULT_VALUE = true;
/**
* @param bool $flag
*/
public function setFlag(bool $flag = self::FLAG_DEFAULT_VALUE): self
{
$this->flag = $flag;
return $this;
}
}
?>

View File

@ -0,0 +1,35 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamAnnotationIncorrectlyIncludesNull
{
/**
* @param \DateTime[]|null $dateTimes
*/
public function setDateTimes(array $dateTimes): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}
?>
-----
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamAnnotationIncorrectlyIncludesNull
{
/**
* @param \DateTime[] $dateTimes
*/
public function setDateTimes(array $dateTimes): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}
?>

View File

@ -0,0 +1,27 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
/**
* @param \DateTime[] $dateTimes
*/
function reverseDateTimes(?array $dateTimes): array
{
return array_reverse($dateTimes);
}
?>
-----
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
/**
* @param \DateTime[]|null $dateTimes
*/
function reverseDateTimes(?array $dateTimes): array
{
return array_reverse($dateTimes);
}
?>

View File

@ -0,0 +1,43 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamAnnotationIsMissingNullMultiple
{
/**
* @param \DateTime[] $dateTimes
* @param array<\DateTimeImmutable> $immutableDateTimes
* @param array<\DateTimeInterface>|null $dateTimeInterfaces
*/
public function setDateTimes(?array $dateTimes, ?array $immutableDateTimes, ?array $dateTimeInterfaces): self
{
$this->dateTimes = $dateTimes;
$this->immutableDateTimes = $immutableDateTimes;
$this->dateTimeInterfaces = $dateTimeInterfaces;
return $this;
}
}
?>
-----
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamAnnotationIsMissingNullMultiple
{
/**
* @param \DateTime[]|null $dateTimes
* @param \DateTimeImmutable[]|null $immutableDateTimes
* @param array<\DateTimeInterface>|null $dateTimeInterfaces
*/
public function setDateTimes(?array $dateTimes, ?array $immutableDateTimes, ?array $dateTimeInterfaces): self
{
$this->dateTimes = $dateTimes;
$this->immutableDateTimes = $immutableDateTimes;
$this->dateTimeInterfaces = $dateTimeInterfaces;
return $this;
}
}
?>

View File

@ -0,0 +1,35 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamAnnotationIsMissingNullWithDefaultNull
{
/**
* @param \DateTime[] $dateTimes
*/
public function setDateTimes(array $dateTimes = null): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}
?>
-----
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamAnnotationIsMissingNullWithDefaultNull
{
/**
* @param \DateTime[]|null $dateTimes
*/
public function setDateTimes(array $dateTimes = null): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}
?>

View File

@ -0,0 +1,41 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamAnnotationIsMissingNullWithGenericSyntax
{
/**
* Rector automatically transforms generic int-keyed simple arrays to the [] notation
* @see \Rector\PHPStanStaticTypeMapper\TypeMapper\ArrayTypeMapper::isIntegerKeyAndNonNestedArray
*
* @param array<int,\DateTime> $dateTimes
*/
public function setDateTimes(?array $dateTimes): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}
?>
-----
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamAnnotationIsMissingNullWithGenericSyntax
{
/**
* Rector automatically transforms generic int-keyed simple arrays to the [] notation
* @see \Rector\PHPStanStaticTypeMapper\TypeMapper\ArrayTypeMapper::isIntegerKeyAndNonNestedArray
*
* @param \DateTime[]|null $dateTimes
*/
public function setDateTimes(?array $dateTimes): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}
?>

View File

@ -0,0 +1,35 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamAnnotationIsMissingNullWithNestedGenericSyntax
{
/**
* @param array<int, array<string, \DateTime>> $dateTimes
*/
public function setDateTimes(?array $dateTimes): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}
?>
-----
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamAnnotationIsMissingNullWithNestedGenericSyntax
{
/**
* @param array<int, array<string, \DateTime>>|null $dateTimes
*/
public function setDateTimes(?array $dateTimes): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}
?>

View File

@ -0,0 +1,35 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamAnnotationIsMissingNull
{
/**
* @param \DateTime[] $dateTimes
*/
public function setDateTimes(?array $dateTimes): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}
?>
-----
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamAnnotationIsMissingNull
{
/**
* @param \DateTime[]|null $dateTimes
*/
public function setDateTimes(?array $dateTimes): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}
?>

View File

@ -0,0 +1,39 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamAnnotationIncorrectlyIncludesNullWithNullDefaultValueFromConst
{
private const FLAG_DEFAULT_VALUE = null;
/**
* @param bool $flag
*/
public function setFlag(bool $flag = self::FLAG_DEFAULT_VALUE): self
{
$this->flag = $flag;
return $this;
}
}
?>
-----
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamAnnotationIncorrectlyIncludesNullWithNullDefaultValueFromConst
{
private const FLAG_DEFAULT_VALUE = null;
/**
* @param bool|null $flag
*/
public function setFlag(bool $flag = self::FLAG_DEFAULT_VALUE): self
{
$this->flag = $flag;
return $this;
}
}
?>

View File

@ -0,0 +1,35 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamUnionAnnotationIncorrectlyIncludesNull
{
/**
* @param \DateTime[]|\DateTimeImmutable[]|null $dateTimes
*/
public function setDateTimes(array $dateTimes): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}
?>
-----
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamUnionAnnotationIncorrectlyIncludesNull
{
/**
* @param \DateTime[]|\DateTimeImmutable[] $dateTimes
*/
public function setDateTimes(array $dateTimes): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}
?>

View File

@ -0,0 +1,35 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamUnionAnnotationMissingNull
{
/**
* @param \DateTime[]|\DateTimeImmutable[] $dateTimes
*/
public function setDateTimes(?array $dateTimes): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}
?>
-----
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class ParamUnionAnnotationMissingNull
{
/**
* @param \DateTime[]|\DateTimeImmutable[]|null $dateTimes
*/
public function setDateTimes(?array $dateTimes): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}
?>

View File

@ -0,0 +1,14 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class SkipBrokenParamAnnotation
{
/** @param 'week'|'month'|'year' $dateTimes */
public function setDateTimes(string $dateTimes): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class SkipParamAnnotationComplexOnPhpdocParserFailure
{
/**
* Parser fails to interpret annotations when there is no comma after AppAssert\Country in Assert\All, this is likely a bug in rector or one
* of its dependencies. But we will skip these cases safely, so no worries.
*
* @OA\Property(property="dateTimes[]", default="null")
* @Serializer\Groups({"export"})
* @Assert\All({
* @Assert\NotBlank,
* @AppAssert\Country
* })
* @Serializer\VirtualProperty
* @param \DateTime[] $dateTimes
*/
public function setDateTimes(?array $dateTimes): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class SkipParamAnnotationWhenItAlreadyIncludesNull
{
/**
* @param \DateTime[]|null $dateTimes
*/
public function setDateTimes(?array $dateTimes): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class SkipParamAnnotationWhenPropertyHasNoType
{
/**
* @param \DateTime[] $dateTimes
*/
public function setDateTimes($dateTimes): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class SkipParamAnnotationWithDefaultNull
{
/**
* @param \DateTime[]|null $dateTimes
*/
public function setDateTimes(array $dateTimes = null): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class SkipParamAnnotationWithGenericSyntaxWhenNullIsNotMissing
{
/**
* @param array<int,\DateTime>|null $dateTimes
*/
public function setDateTimes(?array $dateTimes): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class SkipParamAnnotationWithDefaultNull
{
/**
* @param bool|null $flag
*/
public function setDateTimes(?bool $flag = true): self
{
$this->flag = $flag;
return $this;
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\Fixture;
final class SkipWhenDefaultParamValueIsNullAsString
{
/**
* @param string $text
*/
public function setDateTimes(string $text = 'null'): self
{
$this->$text = $text;
return $this;
}
}

View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector;
use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;
final class ParamAnnotationIncorrectNullableRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
{
$this->doTestFileInfo($fileInfo);
}
/**
* @return Iterator<SmartFileInfo>
*/
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
public function provideConfigFilePath(): string
{
return __DIR__ . '/config/config.php';
}
}

View File

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(ParamAnnotationIncorrectNullableRector::class);
};

View File

@ -0,0 +1,208 @@
<?php
declare(strict_types=1);
namespace Rector\TypeDeclaration\Rector\ClassMethod;
use PhpParser\Node;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
use PHPStan\Type\Type;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\NodeTypeResolver\TypeComparator\TypeComparator;
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
use Rector\TypeDeclaration\Guard\PhpDocNestedAnnotationGuard;
use Rector\TypeDeclaration\Helper\PhpDocNullableTypeHelper;
use Rector\TypeDeclaration\PhpDocParser\ParamPhpDocNodeFactory;
use Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Tests\TypeDeclaration\Rector\ClassMethod\ParamAnnotationIncorrectNullableRector\ParamAnnotationIncorrectNullableRectorTest
*/
final class ParamAnnotationIncorrectNullableRector extends AbstractRector
{
public function __construct(
private readonly TypeComparator $typeComparator,
private readonly PhpDocNullableTypeHelper $phpDocNullableTypeHelper,
private readonly PhpDocNestedAnnotationGuard $phpDocNestedAnnotationGuard,
private readonly ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard,
private readonly ParamPhpDocNodeFactory $paramPhpDocNodeFactory
) {
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
'Add or remove null type from @param phpdoc typehint based on php parameter type declaration',
[
new CodeSample(
<<<'CODE_SAMPLE'
final class SomeClass
{
/**
* @param \DateTime[] $dateTimes
*/
public function setDateTimes(?array $dateTimes): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
final class SomeClass
{
/**
* @param \DateTime[]|null $dateTimes
*/
public function setDateTimes(?array $dateTimes): self
{
$this->dateTimes = $dateTimes;
return $this;
}
}
CODE_SAMPLE
),
]
);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [ClassMethod::class, Function_::class];
}
/**
* @param ClassMethod|Function_ $node
*/
public function refactor(Node $node): ?Node
{
if ($node->getParams() === []) {
return null;
}
if ($node instanceof ClassMethod && $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($node)) {
return null;
}
if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::TYPED_PROPERTIES)) {
return null;
}
if (! $this->phpDocNestedAnnotationGuard->isPhpDocCommentCorrectlyParsed($node)) {
return null;
}
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
$phpDocNode = $phpDocInfo->getPhpDocNode();
return $this->updateParamTagsIfRequired($phpDocNode, $node, $phpDocInfo);
}
private function matchParamByName(string $desiredParamName, ClassMethod|Function_ $node): ?Param
{
foreach ($node->getParams() as $param) {
$paramName = $this->nodeNameResolver->getName($param);
if ('$' . $paramName !== $desiredParamName) {
continue;
}
return $param;
}
return null;
}
private function wasUpdateOfParamTypeRequired(
PhpDocInfo $phpDocInfo,
Type $newType,
Param $param,
string $paramName
): bool
{
// better skip, could crash hard
if ($phpDocInfo->hasInvalidTag('@param')) {
return false;
}
$typeNode = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($newType, TypeKind::PARAM());
$paramTagValueNode = $phpDocInfo->getParamTagValueByName($paramName);
// override existing type
if ($paramTagValueNode !== null) {
// already set
$currentType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType(
$paramTagValueNode->type,
$param
);
if ($this->typeComparator->areTypesEqual($currentType, $newType)) {
return false;
}
$paramTagValueNode->type = $typeNode;
} else {
$paramTagValueNode = $this->paramPhpDocNodeFactory->create($typeNode, $param);
$phpDocInfo->addTagValueNode($paramTagValueNode);
}
return true;
}
/**
* @return ClassMethod|Function_|null
*/
private function updateParamTagsIfRequired(
PhpDocNode $phpDocNode,
ClassMethod|Function_ $node,
PhpDocInfo $phpDocInfo
): ?Node {
$paramTagValueNodes = $phpDocNode->getParamTagValues();
$paramTagWasUpdated = false;
foreach ($paramTagValueNodes as $paramTagValueNode) {
if ($paramTagValueNode->type === null) {
continue;
}
$param = $this->matchParamByName($paramTagValueNode->parameterName, $node);
if (! $param instanceof Param) {
continue;
}
$docType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType(
$paramTagValueNode->type,
$node
);
$updatedPhpDocType = $this->phpDocNullableTypeHelper->resolveUpdatedPhpDocTypeFromPhpDocTypeAndParamNode(
$docType,
$param
);
if (! $updatedPhpDocType instanceof Type) {
continue;
}
if ($this->wasUpdateOfParamTypeRequired(
$phpDocInfo,
$updatedPhpDocType,
$param,
$paramTagValueNode->parameterName
)) {
$paramTagWasUpdated = true;
}
}
return $paramTagWasUpdated ? $node : null;
}
}