mirror of
https://github.com/rectorphp/rector.git
synced 2024-05-28 23:10:51 +00:00
[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:
parent
1c1732f017
commit
c509923516
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -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);
|
||||
}
|
||||
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -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);
|
||||
}
|
||||
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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';
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user