diff --git a/packages/NodeTypeResolver/NodeTypeResolver.php b/packages/NodeTypeResolver/NodeTypeResolver.php index e6c8b2dc1f6..c47df838b94 100644 --- a/packages/NodeTypeResolver/NodeTypeResolver.php +++ b/packages/NodeTypeResolver/NodeTypeResolver.php @@ -15,8 +15,8 @@ use PhpParser\Node\Expr\Ternary; use PhpParser\Node\Name; use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\NullableType; -use PhpParser\Node\Param; use PhpParser\Node\Stmt\Property; +use PhpParser\Node\UnionType as NodeUnionType; use PHPStan\Analyser\Scope; use PHPStan\Broker\ClassAutoloadingException; use PHPStan\Reflection\ClassReflection; @@ -130,7 +130,7 @@ final class NodeTypeResolver } public function getType(Node $node) : Type { - if ($node instanceof Property && $node->type instanceof NullableType) { + if ($node instanceof Property && $node->type instanceof Node) { return $this->getType($node->type); } if ($node instanceof NullableType) { @@ -175,6 +175,13 @@ final class NodeTypeResolver } return new MixedType(); } + if ($node instanceof NodeUnionType) { + $types = []; + foreach ($node->types as $type) { + $types[] = $this->getType($type); + } + return new UnionType($types); + } if (!$node instanceof Expr) { return new MixedType(); } diff --git a/packages/NodeTypeResolver/NodeTypeResolver/IdentifierTypeResolver.php b/packages/NodeTypeResolver/NodeTypeResolver/IdentifierTypeResolver.php index 37a44db5cc6..8a0ad70cd04 100644 --- a/packages/NodeTypeResolver/NodeTypeResolver/IdentifierTypeResolver.php +++ b/packages/NodeTypeResolver/NodeTypeResolver/IdentifierTypeResolver.php @@ -5,10 +5,15 @@ namespace Rector\NodeTypeResolver\NodeTypeResolver; use PhpParser\Node; use PhpParser\Node\Identifier; +use PHPStan\Type\ArrayType; use PHPStan\Type\BooleanType; +use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\FloatType; use PHPStan\Type\IntegerType; +use PHPStan\Type\IterableType; use PHPStan\Type\MixedType; +use PHPStan\Type\NullType; +use PHPStan\Type\ObjectWithoutClassType; use PHPStan\Type\StringType; use PHPStan\Type\Type; use Rector\NodeTypeResolver\Contract\NodeTypeResolverInterface; @@ -26,20 +31,39 @@ final class IdentifierTypeResolver implements NodeTypeResolverInterface } /** * @param Identifier $node - * @return StringType|BooleanType|IntegerType|FloatType|MixedType + * @return StringType|BooleanType|ConstantBooleanType|NullType|ObjectWithoutClassType|ArrayType|IterableType|IntegerType|FloatType|MixedType */ public function resolve(Node $node) : Type { - if ($node->toLowerString() === 'string') { + $lowerString = $node->toLowerString(); + if ($lowerString === 'string') { return new StringType(); } - if ($node->toLowerString() === 'bool') { + if ($lowerString === 'bool') { return new BooleanType(); } - if ($node->toLowerString() === 'int') { + if ($lowerString === 'false') { + return new ConstantBooleanType(\false); + } + if ($lowerString === 'true') { + return new ConstantBooleanType(\true); + } + if ($lowerString === 'null') { + return new NullType(); + } + if ($lowerString === 'object') { + return new ObjectWithoutClassType(); + } + if ($lowerString === 'array') { + return new ArrayType(new MixedType(), new MixedType()); + } + if ($lowerString === 'int') { return new IntegerType(); } - if ($node->toLowerString() === 'float') { + if ($lowerString === 'iterable') { + return new IterableType(new MixedType(), new MixedType()); + } + if ($lowerString === 'float') { return new FloatType(); } return new MixedType(); diff --git a/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php b/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php index 8d78bf94ff7..737051f148e 100644 --- a/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php +++ b/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php @@ -15,6 +15,8 @@ use PhpParser\Node\Stmt\Property; use PhpParser\Node\UnionType; use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; +use PHPStan\Type\MixedType; +use PHPStan\Type\TypeCombinator; use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger; use Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey; use Rector\Core\Contract\Rector\AllowEmptyConfigurableRectorInterface; @@ -25,6 +27,7 @@ use Rector\Core\ValueObject\PhpVersionFeature; use Rector\DeadCode\PhpDoc\TagRemover\VarTagRemover; use Rector\Naming\VariableRenamer; use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\NodeTypeResolver\TypeComparator\TypeComparator; use Rector\Php80\Guard\MakePropertyPromotionGuard; use Rector\Php80\NodeAnalyzer\PromotedPropertyCandidateResolver; use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; @@ -83,7 +86,12 @@ final class ClassPropertyAssignToConstructorPromotionRector extends AbstractRect * @var \Rector\Php80\Guard\MakePropertyPromotionGuard */ private $makePropertyPromotionGuard; - public function __construct(PromotedPropertyCandidateResolver $promotedPropertyCandidateResolver, VariableRenamer $variableRenamer, VarTagRemover $varTagRemover, ParamAnalyzer $paramAnalyzer, PhpDocTypeChanger $phpDocTypeChanger, MakePropertyPromotionGuard $makePropertyPromotionGuard) + /** + * @readonly + * @var \Rector\NodeTypeResolver\TypeComparator\TypeComparator + */ + private $typeComparator; + public function __construct(PromotedPropertyCandidateResolver $promotedPropertyCandidateResolver, VariableRenamer $variableRenamer, VarTagRemover $varTagRemover, ParamAnalyzer $paramAnalyzer, PhpDocTypeChanger $phpDocTypeChanger, MakePropertyPromotionGuard $makePropertyPromotionGuard, TypeComparator $typeComparator) { $this->promotedPropertyCandidateResolver = $promotedPropertyCandidateResolver; $this->variableRenamer = $variableRenamer; @@ -91,6 +99,7 @@ final class ClassPropertyAssignToConstructorPromotionRector extends AbstractRect $this->paramAnalyzer = $paramAnalyzer; $this->phpDocTypeChanger = $phpDocTypeChanger; $this->makePropertyPromotionGuard = $makePropertyPromotionGuard; + $this->typeComparator = $typeComparator; } public function getRuleDefinition() : RuleDefinition { @@ -177,7 +186,7 @@ CODE_SAMPLE $param->flags = $property->flags; // Copy over attributes of the "old" property $param->attrGroups = \array_merge($param->attrGroups, $property->attrGroups); - $this->processNullableType($property, $param); + $this->processUnionType($property, $param); $this->phpDocTypeChanger->copyPropertyDocToParam($constructClassMethod, $property, $param); } return $node; @@ -186,16 +195,31 @@ CODE_SAMPLE { return PhpVersionFeature::PROPERTY_PROMOTION; } - private function processNullableType(Property $property, Param $param) : void + private function processUnionType(Property $property, Param $param) : void { - if ($this->nodeTypeResolver->isNullableType($property)) { - $objectType = $this->getType($property); - $param->type = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($objectType, TypeKind::PARAM); + if ($property->type instanceof Node) { + $param->type = $property->type; + return; } - if ($param->default instanceof Expr && $this->valueResolver->isNull($param->default)) { - $paramType = $this->getType($param); - $param->type = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($paramType, TypeKind::PARAM); + if (!$param->default instanceof Expr) { + return; } + if (!$param->type instanceof Node) { + return; + } + $defaultType = $this->getType($param->default); + $paramType = $this->getType($param->type); + if ($this->typeComparator->isSubtype($defaultType, $paramType)) { + return; + } + if ($this->typeComparator->areTypesEqual($defaultType, $paramType)) { + return; + } + if ($paramType instanceof MixedType) { + return; + } + $paramType = TypeCombinator::union($paramType, $defaultType); + $param->type = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($paramType, TypeKind::PARAM); } private function decorateParamWithPropertyPhpDocInfo(ClassMethod $classMethod, Property $property, Param $param, string $paramName) : void { diff --git a/src/Application/VersionResolver.php b/src/Application/VersionResolver.php index b65629265a7..979d84b04e7 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 = 'c567d5d44340e83f62e8599832af4019d7ef37b3'; + public const PACKAGE_VERSION = 'ff21394bc2448bae7cfcf7e6b4be462a4bae104f'; /** * @api * @var string */ - public const RELEASE_DATE = '2023-06-05 15:37:04'; + public const RELEASE_DATE = '2023-06-06 11:27:10'; /** * @var int */ diff --git a/src/Kernel/RectorKernel.php b/src/Kernel/RectorKernel.php index 4b7e6653b30..a959cd036e0 100644 --- a/src/Kernel/RectorKernel.php +++ b/src/Kernel/RectorKernel.php @@ -15,7 +15,7 @@ final class RectorKernel /** * @var string */ - private const CACHE_KEY = 'v57'; + private const CACHE_KEY = 'v58'; /** * @var \Symfony\Component\DependencyInjection\ContainerInterface|null */ diff --git a/vendor/autoload.php b/vendor/autoload.php index f2778512800..25fd15e8ca2 100644 --- a/vendor/autoload.php +++ b/vendor/autoload.php @@ -22,4 +22,4 @@ if (PHP_VERSION_ID < 50600) { require_once __DIR__ . '/composer/autoload_real.php'; -return ComposerAutoloaderInit394d9dd8e972072b638bd44515fa169a::getLoader(); +return ComposerAutoloaderInit661a48a1090125ae9f8625adda34402d::getLoader(); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php index 1eca7bd1cde..cd1676c68fc 100644 --- a/vendor/composer/autoload_real.php +++ b/vendor/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php @generated by Composer -class ComposerAutoloaderInit394d9dd8e972072b638bd44515fa169a +class ComposerAutoloaderInit661a48a1090125ae9f8625adda34402d { private static $loader; @@ -22,17 +22,17 @@ class ComposerAutoloaderInit394d9dd8e972072b638bd44515fa169a return self::$loader; } - spl_autoload_register(array('ComposerAutoloaderInit394d9dd8e972072b638bd44515fa169a', 'loadClassLoader'), true, true); + spl_autoload_register(array('ComposerAutoloaderInit661a48a1090125ae9f8625adda34402d', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__)); - spl_autoload_unregister(array('ComposerAutoloaderInit394d9dd8e972072b638bd44515fa169a', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInit661a48a1090125ae9f8625adda34402d', 'loadClassLoader')); require __DIR__ . '/autoload_static.php'; - call_user_func(\Composer\Autoload\ComposerStaticInit394d9dd8e972072b638bd44515fa169a::getInitializer($loader)); + call_user_func(\Composer\Autoload\ComposerStaticInit661a48a1090125ae9f8625adda34402d::getInitializer($loader)); $loader->setClassMapAuthoritative(true); $loader->register(true); - $filesToLoad = \Composer\Autoload\ComposerStaticInit394d9dd8e972072b638bd44515fa169a::$files; + $filesToLoad = \Composer\Autoload\ComposerStaticInit661a48a1090125ae9f8625adda34402d::$files; $requireFile = \Closure::bind(static function ($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 0f581ed5c64..ab7a4f8f5db 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -4,7 +4,7 @@ namespace Composer\Autoload; -class ComposerStaticInit394d9dd8e972072b638bd44515fa169a +class ComposerStaticInit661a48a1090125ae9f8625adda34402d { public static $files = array ( 'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php', @@ -3048,9 +3048,9 @@ class ComposerStaticInit394d9dd8e972072b638bd44515fa169a public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { - $loader->prefixLengthsPsr4 = ComposerStaticInit394d9dd8e972072b638bd44515fa169a::$prefixLengthsPsr4; - $loader->prefixDirsPsr4 = ComposerStaticInit394d9dd8e972072b638bd44515fa169a::$prefixDirsPsr4; - $loader->classMap = ComposerStaticInit394d9dd8e972072b638bd44515fa169a::$classMap; + $loader->prefixLengthsPsr4 = ComposerStaticInit661a48a1090125ae9f8625adda34402d::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit661a48a1090125ae9f8625adda34402d::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInit661a48a1090125ae9f8625adda34402d::$classMap; }, null, ClassLoader::class); }