Updated Rector to commit d6fc9012918faf26cfae30aaad329b8a3c65be75

d6fc901291 [TypeDeclaration] Add TypedPropertyFromJMSSerializerAttributeTypeRector (#5909)
This commit is contained in:
Tomas Votruba 2024-05-24 13:06:38 +00:00
parent 81725974a0
commit 5d71d0b09c
14 changed files with 246 additions and 23 deletions

View File

@ -1,4 +1,4 @@
# 377 Rules Overview
# 378 Rules Overview
<br>
@ -60,7 +60,7 @@
- [Transform](#transform) (25)
- [TypeDeclaration](#typedeclaration) (48)
- [TypeDeclaration](#typedeclaration) (49)
- [Visibility](#visibility) (3)
@ -7446,6 +7446,23 @@ Add typed property from assigned types
<br>
### TypedPropertyFromJMSSerializerAttributeTypeRector
Add typed property from JMS Serializer Type attribute
- class: [`Rector\TypeDeclaration\Rector\Class_\TypedPropertyFromJMSSerializerAttributeTypeRector`](../rules/TypeDeclaration/Rector/Class_/TypedPropertyFromJMSSerializerAttributeTypeRector.php)
```diff
final class SomeClass
{
#[\JMS\Serializer\Annotation\Type('string')]
- private $name;
+ private ?string $name = null;
}
```
<br>
### TypedPropertyFromStrictConstructorRector
Add typed properties based only on strict constructor types

View File

@ -0,0 +1,191 @@
<?php
declare (strict_types=1);
namespace Rector\TypeDeclaration\Rector\Class_;
use RectorPrefix202405\Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\NullableType;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\Php74\Guard\MakePropertyTypedGuard;
use Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer;
use Rector\PhpParser\Node\Value\ValueResolver;
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
use Rector\Rector\AbstractRector;
use Rector\Reflection\ReflectionResolver;
use Rector\StaticTypeMapper\Mapper\ScalarStringToTypeMapper;
use Rector\StaticTypeMapper\StaticTypeMapper;
use Rector\TypeDeclaration\AlreadyAssignDetector\ConstructorAssignDetector;
use Rector\TypeDeclaration\TypeInferer\PropertyTypeInferer\AllAssignNodePropertyTypeInferer;
use Rector\ValueObject\PhpVersionFeature;
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromJMSSerializerAttributeTypeRector\TypedPropertyFromJMSSerializerAttributeTypeRectorTest
*/
final class TypedPropertyFromJMSSerializerAttributeTypeRector extends AbstractRector implements MinPhpVersionInterface
{
/**
* @readonly
* @var \Rector\TypeDeclaration\TypeInferer\PropertyTypeInferer\AllAssignNodePropertyTypeInferer
*/
private $allAssignNodePropertyTypeInferer;
/**
* @readonly
* @var \Rector\Php74\Guard\MakePropertyTypedGuard
*/
private $makePropertyTypedGuard;
/**
* @readonly
* @var \Rector\Reflection\ReflectionResolver
*/
private $reflectionResolver;
/**
* @readonly
* @var \Rector\PhpParser\Node\Value\ValueResolver
*/
private $valueResolver;
/**
* @readonly
* @var \Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer
*/
private $phpAttributeAnalyzer;
/**
* @readonly
* @var \Rector\StaticTypeMapper\Mapper\ScalarStringToTypeMapper
*/
private $scalarStringToTypeMapper;
/**
* @readonly
* @var \Rector\StaticTypeMapper\StaticTypeMapper
*/
private $staticTypeMapper;
/**
* @readonly
* @var \Rector\TypeDeclaration\AlreadyAssignDetector\ConstructorAssignDetector
*/
private $constructorAssignDetector;
/**
* @var string
*/
private const JMS_TYPE = 'JMS\\Serializer\\Annotation\\Type';
public function __construct(AllAssignNodePropertyTypeInferer $allAssignNodePropertyTypeInferer, MakePropertyTypedGuard $makePropertyTypedGuard, ReflectionResolver $reflectionResolver, ValueResolver $valueResolver, PhpAttributeAnalyzer $phpAttributeAnalyzer, ScalarStringToTypeMapper $scalarStringToTypeMapper, StaticTypeMapper $staticTypeMapper, ConstructorAssignDetector $constructorAssignDetector)
{
$this->allAssignNodePropertyTypeInferer = $allAssignNodePropertyTypeInferer;
$this->makePropertyTypedGuard = $makePropertyTypedGuard;
$this->reflectionResolver = $reflectionResolver;
$this->valueResolver = $valueResolver;
$this->phpAttributeAnalyzer = $phpAttributeAnalyzer;
$this->scalarStringToTypeMapper = $scalarStringToTypeMapper;
$this->staticTypeMapper = $staticTypeMapper;
$this->constructorAssignDetector = $constructorAssignDetector;
}
public function getRuleDefinition() : RuleDefinition
{
return new RuleDefinition('Add typed property from JMS Serializer Type attribute', [new CodeSample(<<<'CODE_SAMPLE'
final class SomeClass
{
#[\JMS\Serializer\Annotation\Type('string')]
private $name;
}
CODE_SAMPLE
, <<<'CODE_SAMPLE'
final class SomeClass
{
#[\JMS\Serializer\Annotation\Type('string')]
private ?string $name = null;
}
CODE_SAMPLE
)]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes() : array
{
return [Class_::class];
}
public function provideMinPhpVersion() : int
{
return PhpVersionFeature::ATTRIBUTES;
}
/**
* @param Class_ $node
*/
public function refactor(Node $node) : ?Node
{
$hasChanged = \false;
$classReflection = null;
foreach ($node->getProperties() as $property) {
if (!$property->isPrivate()) {
continue;
}
if ($property->type instanceof Node) {
continue;
}
if (!$this->phpAttributeAnalyzer->hasPhpAttribute($property, self::JMS_TYPE)) {
continue;
}
if (!$classReflection instanceof ClassReflection) {
$classReflection = $this->reflectionResolver->resolveClassReflection($node);
}
if (!$classReflection instanceof ClassReflection) {
return null;
}
if (!$this->makePropertyTypedGuard->isLegal($property, $classReflection, \false)) {
continue;
}
$inferredType = $this->allAssignNodePropertyTypeInferer->inferProperty($property, $classReflection, $this->file);
// has assigned with type
if ($inferredType instanceof Type) {
continue;
}
if ($property->props[0]->default instanceof Node) {
continue;
}
$typeValue = null;
foreach ($property->attrGroups as $attrGroup) {
foreach ($attrGroup->attrs as $attr) {
if ($attr->name->toString() === self::JMS_TYPE) {
$typeValue = $this->valueResolver->getValue($attr->args[0]->value);
break;
}
}
}
if (!\is_string($typeValue)) {
continue;
}
$typeValue = Strings::match($typeValue, '#\\w+#');
if (isset($typeValue[0]) && \is_string($typeValue[0])) {
$type = $this->scalarStringToTypeMapper->mapScalarStringToType($typeValue[0]);
if ($type instanceof MixedType) {
$type = new ObjectType($typeValue[0]);
}
$propertyType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($type, TypeKind::PROPERTY);
if (!$propertyType instanceof Identifier && !$propertyType instanceof FullyQualified) {
return null;
}
$isInConstructorAssigned = $this->constructorAssignDetector->isPropertyAssigned($node, $this->getName($property));
$type = $isInConstructorAssigned ? $propertyType : new NullableType($propertyType);
$property->type = $type;
if (!$isInConstructorAssigned) {
$property->props[0]->default = new ConstFetch(new Name('null'));
}
$hasChanged = \true;
}
}
if ($hasChanged) {
return $node;
}
return null;
}
}

View File

@ -19,12 +19,12 @@ final class VersionResolver
* @api
* @var string
*/
public const PACKAGE_VERSION = '22d6fc2789f575fff16e76e7c7f963793e11f53b';
public const PACKAGE_VERSION = 'd6fc9012918faf26cfae30aaad329b8a3c65be75';
/**
* @api
* @var string
*/
public const RELEASE_DATE = '2024-05-23 15:32:34';
public const RELEASE_DATE = '2024-05-24 15:03:11';
/**
* @var int
*/

View File

@ -10,6 +10,7 @@ use Rector\TypeDeclaration\Rector\Class_\ChildDoctrineRepositoryClassTypeRector;
use Rector\TypeDeclaration\Rector\Class_\MergeDateTimePropertyTypeDeclarationRector;
use Rector\TypeDeclaration\Rector\Class_\PropertyTypeFromStrictSetterGetterRector;
use Rector\TypeDeclaration\Rector\Class_\ReturnTypeFromStrictTernaryRector;
use Rector\TypeDeclaration\Rector\Class_\TypedPropertyFromJMSSerializerAttributeTypeRector;
use Rector\TypeDeclaration\Rector\ClassMethod\AddMethodCallBasedStrictParamTypeRector;
use Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeBasedOnPHPUnitDataProviderRector;
use Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeFromPropertyTypeRector;
@ -100,5 +101,6 @@ final class TypeDeclarationLevel
ReturnNeverTypeRector::class,
StrictArrayParamDimFetchRector::class,
StrictStringParamConcatRector::class,
TypedPropertyFromJMSSerializerAttributeTypeRector::class,
];
}

View File

@ -2417,6 +2417,7 @@ return array(
'Rector\\TypeDeclaration\\Rector\\Class_\\MergeDateTimePropertyTypeDeclarationRector' => $baseDir . '/rules/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector.php',
'Rector\\TypeDeclaration\\Rector\\Class_\\PropertyTypeFromStrictSetterGetterRector' => $baseDir . '/rules/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector.php',
'Rector\\TypeDeclaration\\Rector\\Class_\\ReturnTypeFromStrictTernaryRector' => $baseDir . '/rules/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector.php',
'Rector\\TypeDeclaration\\Rector\\Class_\\TypedPropertyFromJMSSerializerAttributeTypeRector' => $baseDir . '/rules/TypeDeclaration/Rector/Class_/TypedPropertyFromJMSSerializerAttributeTypeRector.php',
'Rector\\TypeDeclaration\\Rector\\Closure\\AddClosureVoidReturnTypeWhereNoReturnRector' => $baseDir . '/rules/TypeDeclaration/Rector/Closure/AddClosureVoidReturnTypeWhereNoReturnRector.php',
'Rector\\TypeDeclaration\\Rector\\Empty_\\EmptyOnNullableObjectToInstanceOfRector' => $baseDir . '/rules/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector.php',
'Rector\\TypeDeclaration\\Rector\\FunctionLike\\AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRector' => $baseDir . '/rules/TypeDeclaration/Rector/FunctionLike/AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRector.php',

View File

@ -2636,6 +2636,7 @@ class ComposerStaticInit8f3085135f9c0dec79e149b0c0400440
'Rector\\TypeDeclaration\\Rector\\Class_\\MergeDateTimePropertyTypeDeclarationRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector.php',
'Rector\\TypeDeclaration\\Rector\\Class_\\PropertyTypeFromStrictSetterGetterRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector.php',
'Rector\\TypeDeclaration\\Rector\\Class_\\ReturnTypeFromStrictTernaryRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector.php',
'Rector\\TypeDeclaration\\Rector\\Class_\\TypedPropertyFromJMSSerializerAttributeTypeRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/Class_/TypedPropertyFromJMSSerializerAttributeTypeRector.php',
'Rector\\TypeDeclaration\\Rector\\Closure\\AddClosureVoidReturnTypeWhereNoReturnRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/Closure/AddClosureVoidReturnTypeWhereNoReturnRector.php',
'Rector\\TypeDeclaration\\Rector\\Empty_\\EmptyOnNullableObjectToInstanceOfRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/Empty_/EmptyOnNullableObjectToInstanceOfRector.php',
'Rector\\TypeDeclaration\\Rector\\FunctionLike\\AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/FunctionLike/AddParamTypeForFunctionLikeWithinCallLikeArgDeclarationRector.php',

View File

@ -1382,17 +1382,17 @@
},
{
"name": "react\/promise",
"version": "v3.1.0",
"version_normalized": "3.1.0.0",
"version": "v3.2.0",
"version_normalized": "3.2.0.0",
"source": {
"type": "git",
"url": "https:\/\/github.com\/reactphp\/promise.git",
"reference": "e563d55d1641de1dea9f5e84f3cccc66d2bfe02c"
"reference": "8a164643313c71354582dc850b42b33fa12a4b63"
},
"dist": {
"type": "zip",
"url": "https:\/\/api.github.com\/repos\/reactphp\/promise\/zipball\/e563d55d1641de1dea9f5e84f3cccc66d2bfe02c",
"reference": "e563d55d1641de1dea9f5e84f3cccc66d2bfe02c",
"url": "https:\/\/api.github.com\/repos\/reactphp\/promise\/zipball\/8a164643313c71354582dc850b42b33fa12a4b63",
"reference": "8a164643313c71354582dc850b42b33fa12a4b63",
"shasum": ""
},
"require": {
@ -1402,7 +1402,7 @@
"phpstan\/phpstan": "1.10.39 || 1.4.10",
"phpunit\/phpunit": "^9.6 || ^7.5"
},
"time": "2023-11-16T16:21:57+00:00",
"time": "2024-05-24T10:39:05+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -1446,7 +1446,7 @@
],
"support": {
"issues": "https:\/\/github.com\/reactphp\/promise\/issues",
"source": "https:\/\/github.com\/reactphp\/promise\/tree\/v3.1.0"
"source": "https:\/\/github.com\/reactphp\/promise\/tree\/v3.2.0"
},
"funding": [
{

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,16 @@
# Changelog
## 3.2.0 (2024-05-24)
* Feature: Improve PHP 8.4+ support by avoiding implicitly nullable type declarations.
(#260 by @Ayesh)
* Feature: Include previous exceptions when reporting unhandled promise rejections.
(#262 by @clue)
* Update test suite to improve PHP 8.4+ support.
(#261 by @SimonFrings)
## 3.1.0 (2023-11-16)
* Feature: Full PHP 8.3 compatibility.

View File

@ -664,7 +664,7 @@ This project follows [SemVer](https://semver.org/).
This will install the latest supported version from this branch:
```bash
composer require react/promise:^3.1
composer require react/promise:^3.2
```
See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.

View File

@ -18,7 +18,7 @@ final class Deferred
/**
* @param (callable(callable(T):void,callable(\Throwable):void):void)|null $canceller
*/
public function __construct(callable $canceller = null)
public function __construct(?callable $canceller = null)
{
$this->promise = new Promise(function ($resolve, $reject) : void {
$this->resolveCallback = $resolve;

View File

@ -30,7 +30,7 @@ final class FulfilledPromise implements PromiseInterface
* @param ?(callable((T is void ? null : T)): (PromiseInterface<TFulfilled>|TFulfilled)) $onFulfilled
* @return PromiseInterface<($onFulfilled is null ? T : TFulfilled)>
*/
public function then(callable $onFulfilled = null, callable $onRejected = null) : PromiseInterface
public function then(?callable $onFulfilled = null, ?callable $onRejected = null) : PromiseInterface
{
if (null === $onFulfilled) {
return $this;

View File

@ -32,16 +32,16 @@ final class RejectedPromise implements PromiseInterface
}
$handler = set_rejection_handler(null);
if ($handler === null) {
$message = 'Unhandled promise rejection with ' . \get_class($this->reason) . ': ' . $this->reason->getMessage() . ' in ' . $this->reason->getFile() . ':' . $this->reason->getLine() . \PHP_EOL;
$message .= 'Stack trace:' . \PHP_EOL . $this->reason->getTraceAsString();
$message = 'Unhandled promise rejection with ' . $this->reason;
\error_log($message);
return;
}
try {
$handler($this->reason);
} catch (\Throwable $e) {
$message = 'Fatal error: Uncaught ' . \get_class($e) . ' from unhandled promise rejection handler: ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine() . \PHP_EOL;
$message .= 'Stack trace:' . \PHP_EOL . $e->getTraceAsString();
\preg_match('/^([^:\\s]++)(.*+)$/sm', (string) $e, $match);
\assert(isset($match[1], $match[2]));
$message = 'Fatal error: Uncaught ' . $match[1] . ' from unhandled promise rejection handler' . $match[2];
\error_log($message);
exit(255);
}
@ -52,7 +52,7 @@ final class RejectedPromise implements PromiseInterface
* @param ?(callable(\Throwable): (PromiseInterface<TRejected>|TRejected)) $onRejected
* @return PromiseInterface<($onRejected is null ? never : TRejected)>
*/
public function then(callable $onFulfilled = null, callable $onRejected = null) : PromiseInterface
public function then(?callable $onFulfilled = null, ?callable $onRejected = null) : PromiseInterface
{
if (null === $onRejected) {
return $this;

View File

@ -23,7 +23,7 @@ final class Promise implements PromiseInterface
* @param callable(callable(T):void,callable(\Throwable):void):void $resolver
* @param (callable(callable(T):void,callable(\Throwable):void):void)|null $canceller
*/
public function __construct(callable $resolver, callable $canceller = null)
public function __construct(callable $resolver, ?callable $canceller = null)
{
$this->canceller = $canceller;
// Explicitly overwrite arguments with null values before invoking
@ -33,7 +33,7 @@ final class Promise implements PromiseInterface
$resolver = $canceller = null;
$this->call($cb);
}
public function then(callable $onFulfilled = null, callable $onRejected = null) : PromiseInterface
public function then(?callable $onFulfilled = null, ?callable $onRejected = null) : PromiseInterface
{
if (null !== $this->result) {
return $this->result->then($onFulfilled, $onRejected);
@ -135,7 +135,7 @@ final class Promise implements PromiseInterface
{
return $this->finally($onFulfilledOrRejected);
}
private function resolver(callable $onFulfilled = null, callable $onRejected = null) : callable
private function resolver(?callable $onFulfilled = null, ?callable $onRejected = null) : callable
{
return function (callable $resolve, callable $reject) use($onFulfilled, $onRejected) : void {
$this->handlers[] = static function (PromiseInterface $promise) use($onFulfilled, $onRejected, $resolve, $reject) : void {