diff --git a/UPGRADING.md b/UPGRADING.md index f2089bac57d..de4d26099fa 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -1,3 +1,7 @@ +# Upgrading to the next Rector release + +* `TypeMapperInterface::mapToPhpParserNode()` is removed; custom type mappers only map to PHPDoc nodes now. A custom `Type` controls its native declaration by overriding `toNativePhpTypeNode()`. + # Upgrading from Rector 2.2.14 to 2.3 * `FileWithoutNamespace` is deprecated, and replaced by `FileNode` that represents both namespaced and non-namespaced files and allow changes inside diff --git a/phpstan.neon b/phpstan.neon index 4ca5ab2b499..42728a62310 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -284,11 +284,6 @@ parameters: paths: - src/NodeTypeResolver/PHPStan/Type/TypeFactory.php - # from mapper interface - - - identifier: return.unusedType - message: '#mapToPhpParserNode\(\) never returns PhpParser\\.* so it can be removed from the return#' - # false positive - identifier: return.unusedType diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/has_property_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/has_property_type.php.inc new file mode 100644 index 00000000000..fa3353b29c6 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/has_property_type.php.inc @@ -0,0 +1,53 @@ +type)) { + return; + } + + if ($data->type === 'value') { + $this->runData($data); + } + } + + private function runData($data) + { + } +} +----- +type)) { + return; + } + + if ($data->type === 'value') { + $this->runData($data); + } + } + + private function runData(object $data) + { + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_has_property_type.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_has_property_type.php.inc deleted file mode 100644 index a3ceb5af64c..00000000000 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector/Fixture/skip_has_property_type.php.inc +++ /dev/null @@ -1,26 +0,0 @@ -type)) { - return; - } - - if ($data->type === 'value') { - $this->runData($data); - } - } - - private function runData($data) - { - } -} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/different_usage.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/different_usage.php.inc index 4e77ccb4457..b71a4c09674 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/different_usage.php.inc +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/different_usage.php.inc @@ -33,7 +33,7 @@ use stdClass; final class DifferentUsage { /** - * @param \stdClass[]|\DateTime[] $items + * @param \DateTime[]|\stdClass[] $items */ public function process(array $items): void { diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/with_array_filter_and_array_walk.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/with_array_filter_and_array_walk.php.inc index cdd7a0e2d49..0e4f6379507 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/with_array_filter_and_array_walk.php.inc +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/AddParamArrayDocblockBasedOnCallableNativeFuncCallRector/Fixture/with_array_filter_and_array_walk.php.inc @@ -33,7 +33,7 @@ use stdClass; final class WithArrayFilterAndArrayWalk { /** - * @param string[]|\stdClass[] $items + * @param \stdClass[]|string[] $items */ public function process(array $items): void { diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/return_anonymous_class_and_other.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/return_anonymous_class_and_other.php.inc new file mode 100644 index 00000000000..c8b2ff08a54 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromReturnNewRector/Fixture/return_anonymous_class_and_other.php.inc @@ -0,0 +1,35 @@ +>|array|bool> + * @return array|int|string>|bool> */ public function run(): array { diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/Nesting/not_too_complex.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/Nesting/not_too_complex.php.inc index 20f86cb5131..bf28bd4a243 100644 --- a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/Nesting/not_too_complex.php.inc +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/Nesting/not_too_complex.php.inc @@ -26,7 +26,7 @@ namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\DocblockRetur final class AvoidExtremeNesting { /** - * @return array + * @return array */ public function run(): array { diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/covert-implicit-key.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/covert-implicit-key.php.inc index 6efd75479a4..d8194433e41 100644 --- a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/covert-implicit-key.php.inc +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/covert-implicit-key.php.inc @@ -28,7 +28,7 @@ namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\DocblockRetur final class CoverImplicitKey { /** - * @return array> + * @return array> */ private static function getExpectedAllOwners(): array { diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/covert-object-return.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/covert-object-return.php.inc index 63ca0a557a4..671c48ba721 100644 --- a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/covert-object-return.php.inc +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/covert-object-return.php.inc @@ -26,7 +26,7 @@ final class CoverObjectReturn private ?\Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\DocblockReturnArrayFromDirectArrayInstanceRector\Source\SomeObjectInDirectArray $item = null; /** - * @return array + * @return array */ public function toArray(): array { diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/repeated_item_type.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/repeated_item_type.php.inc index 6dc7d5caf9b..9ac3c15f517 100644 --- a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/repeated_item_type.php.inc +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/repeated_item_type.php.inc @@ -44,7 +44,7 @@ final class RepeatedItemType ) {} /** - * @return array> + * @return array|string> */ public function toRequestPayload(): array { diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/with_duplicated_nested_array.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/with_duplicated_nested_array.php.inc index aa21e75ba02..63242d692e1 100644 --- a/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/with_duplicated_nested_array.php.inc +++ b/rules-tests/TypeDeclarationDocblocks/Rector/ClassMethod/DocblockReturnArrayFromDirectArrayInstanceRector/Fixture/with_duplicated_nested_array.php.inc @@ -34,7 +34,7 @@ namespace Rector\Tests\TypeDeclarationDocblocks\Rector\ClassMethod\DocblockRetur final class WithDuplicatedNestedArray { /** - * @return array + * @return array */ public static function run(): iterable { diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/cover_mixed_and_known.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/cover_mixed_and_known.php.inc index 01833c54588..e926b0c003c 100644 --- a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/cover_mixed_and_known.php.inc +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/cover_mixed_and_known.php.inc @@ -32,7 +32,7 @@ final class CoverMixedAndKnown } /** - * @param string[]|mixed[] $items + * @param mixed[] $items */ private function run(array $items) { diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/multiple_calls_with_various_types.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/multiple_calls_with_various_types.php.inc index 7515deb7f05..215392d4343 100644 --- a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/multiple_calls_with_various_types.php.inc +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/multiple_calls_with_various_types.php.inc @@ -30,7 +30,7 @@ final class MultipleCallsWithVariousTypes } /** - * @param string[]|int[] $items + * @param int[]|string[] $items */ private function run(array $items) { diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/union_objects.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/union_objects.php.inc index 100062d9b76..b2f8dd0b917 100644 --- a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/union_objects.php.inc +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/union_objects.php.inc @@ -42,7 +42,7 @@ final class UnionObjects } /** - * @param \Rector\Tests\TypeDeclarationDocblocks\Rector\Class_\ClassMethodArrayDocblockParamFromLocalCallsRector\Source\SomeReturnedObject[]|\Rector\Tests\TypeDeclarationDocblocks\Rector\Class_\ClassMethodArrayDocblockParamFromLocalCallsRector\Source\AnotherReturnedObject[] $items + * @param \Rector\Tests\TypeDeclarationDocblocks\Rector\Class_\ClassMethodArrayDocblockParamFromLocalCallsRector\Source\AnotherReturnedObject[]|\Rector\Tests\TypeDeclarationDocblocks\Rector\Class_\ClassMethodArrayDocblockParamFromLocalCallsRector\Source\SomeReturnedObject[] $items */ private function run(array $items) { diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/unique_types.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/unique_types.php.inc index 8db2d62a7db..1bb09f15c69 100644 --- a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/unique_types.php.inc +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/unique_types.php.inc @@ -32,7 +32,7 @@ class UniqueTypes } /** - * @param string[]|int[] $items + * @param int[]|string[] $items */ private function run(array $items) { diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/array_union_generic_same_type.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/array_union_generic_same_type.php.inc index 5167555baf3..18e27679cd1 100644 --- a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/array_union_generic_same_type.php.inc +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/array_union_generic_same_type.php.inc @@ -25,7 +25,7 @@ use stdClass; class ArrayUnionGenericSameType { /** - * @var array|class-string<\DateTime>> + * @var array|class-string<\stdClass>> */ private array $dependencies = [ stdClass::class, diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/int_key_inside.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/int_key_inside.php.inc index 5c16a52841d..b548d0e2851 100644 --- a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/int_key_inside.php.inc +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/DocblockVarArrayFromPropertyDefaultsRector/Fixture/int_key_inside.php.inc @@ -23,7 +23,7 @@ namespace Rector\Tests\TypeDeclarationDocblocks\Rector\Class_\DocblockVarArrayFr final class IntKeyInside { /** - * @var array>>> + * @var array>>> */ private array $array = [ 'a' => [ @@ -35,4 +35,4 @@ final class IntKeyInside ]; } -?> \ No newline at end of file +?> diff --git a/rules/TypeDeclaration/NodeManipulator/AddUnionReturnType.php b/rules/TypeDeclaration/NodeManipulator/AddUnionReturnType.php index 1a0014e94d3..b4253c7c0f1 100644 --- a/rules/TypeDeclaration/NodeManipulator/AddUnionReturnType.php +++ b/rules/TypeDeclaration/NodeManipulator/AddUnionReturnType.php @@ -11,7 +11,7 @@ use PHPStan\Analyser\Scope; use PHPStan\Type\UnionType; use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; -use Rector\PHPStanStaticTypeMapper\TypeMapper\UnionTypeMapper; +use Rector\StaticTypeMapper\StaticTypeMapper; use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer; use Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard; @@ -19,7 +19,7 @@ { public function __construct( private ReturnTypeInferer $returnTypeInferer, - private UnionTypeMapper $unionTypeMapper, + private StaticTypeMapper $staticTypeMapper, private ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard, ) { } @@ -53,7 +53,7 @@ public function add(ClassMethod|Function_ $node, Scope $scope): ClassMethod|Func return null; } - $returnType = $this->unionTypeMapper->mapToPhpParserNode($inferReturnType, TypeKind::RETURN); + $returnType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($inferReturnType, TypeKind::RETURN); if (! $returnType instanceof Node) { return null; } diff --git a/rules/TypeDeclaration/NodeTypeAnalyzer/DetailedTypeAnalyzer.php b/rules/TypeDeclaration/NodeTypeAnalyzer/DetailedTypeAnalyzer.php deleted file mode 100644 index d44fa1ce1bc..00000000000 --- a/rules/TypeDeclaration/NodeTypeAnalyzer/DetailedTypeAnalyzer.php +++ /dev/null @@ -1,45 +0,0 @@ -getTypes()) > self::MAX_NUMBER_OF_TYPES; - } - - if ($type instanceof ConstantArrayType) { - return count($type->getValueTypes()) > self::MAX_NUMBER_OF_TYPES; - } - - if ($type instanceof GenericObjectType) { - return $this->isTooDetailedGenericObjectType($type); - } - - return false; - } - - private function isTooDetailedGenericObjectType(GenericObjectType $genericObjectType): bool - { - if (count($genericObjectType->getTypes()) !== 1) { - return false; - } - - $genericType = $genericObjectType->getTypes()[0]; - return $this->isTooDetailed($genericType); - } -} diff --git a/rules/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector.php b/rules/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector.php index c2a734be845..a48c1c634c4 100644 --- a/rules/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector.php +++ b/rules/TypeDeclaration/Rector/ClassMethod/ReturnNullableTypeRector.php @@ -11,10 +11,10 @@ use PHPStan\Type\UnionType; use Rector\PHPStan\ScopeFetcher; use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; -use Rector\PHPStanStaticTypeMapper\TypeMapper\UnionTypeMapper; use Rector\Rector\AbstractRector; use Rector\RuleDoc\CodeSample\CodeSample; use Rector\RuleDoc\RuleDefinition; +use Rector\StaticTypeMapper\StaticTypeMapper; use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer; use Rector\ValueObject\PhpVersion; use Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard; @@ -26,7 +26,7 @@ final class ReturnNullableTypeRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private readonly UnionTypeMapper $unionTypeMapper, + private readonly StaticTypeMapper $staticTypeMapper, private readonly ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard, private readonly ReturnTypeInferer $returnTypeInferer, ) { @@ -108,7 +108,7 @@ public function refactor(Node $node): ?Node return null; } - $returnType = $this->unionTypeMapper->mapToPhpParserNode($inferReturnType, TypeKind::RETURN); + $returnType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($inferReturnType, TypeKind::RETURN); if (! $returnType instanceof Node) { return null; } diff --git a/rules/TypeDeclaration/TypeAnalyzer/GenericClassStringTypeNormalizer.php b/rules/TypeDeclaration/TypeAnalyzer/GenericClassStringTypeNormalizer.php deleted file mode 100644 index 72d8f123bda..00000000000 --- a/rules/TypeDeclaration/TypeAnalyzer/GenericClassStringTypeNormalizer.php +++ /dev/null @@ -1,17 +0,0 @@ -getTypes(), fn (Type $type): bool => $type instanceof GenericClassStringType); - } -} diff --git a/src/DependencyInjection/LazyContainerFactory.php b/src/DependencyInjection/LazyContainerFactory.php index 1cb851e9149..e6e5a1590e0 100644 --- a/src/DependencyInjection/LazyContainerFactory.php +++ b/src/DependencyInjection/LazyContainerFactory.php @@ -55,11 +55,6 @@ use Rector\PhpParser\NodeVisitor\LocalVariableScopeNodeVisitor; use Rector\PhpParser\NodeVisitor\NameAndArgNodeVisitor; use Rector\PhpParser\NodeVisitor\PhpVersionConditionNodeVisitor; -use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper; -use Rector\PHPStanStaticTypeMapper\TypeMapper\ArrayTypeMapper; -use Rector\PHPStanStaticTypeMapper\TypeMapper\ConditionalTypeForParameterMapper; -use Rector\PHPStanStaticTypeMapper\TypeMapper\ConditionalTypeMapper; -use Rector\PHPStanStaticTypeMapper\TypeMapper\UnionTypeMapper; use Rector\PostRector\Application\PostFileProcessor; use Rector\Rector\AbstractRector; use Symfony\Component\Console\Application; @@ -104,7 +99,6 @@ public function create(): RectorConfig $this->registerConsole($rectorConfig); $this->registerFileProcessing($rectorConfig); $this->registerCachingAndResettables($rectorConfig); - $this->registerTypeMappers($rectorConfig); $this->registerNodeNameResolvers($rectorConfig); $this->registerRectorAutowiring($rectorConfig); $this->registerTaggedServices($rectorConfig); @@ -167,44 +161,6 @@ private function registerCachingAndResettables(RectorConfig $rectorConfig): void }); } - private function registerTypeMappers(RectorConfig $rectorConfig): void - { - // tagged services - $rectorConfig->afterResolving( - ArrayTypeMapper::class, - static function (ArrayTypeMapper $arrayTypeMapper) use ($rectorConfig): void { - $arrayTypeMapper->autowire($rectorConfig->make(PHPStanStaticTypeMapper::class)); - } - ); - - $rectorConfig->afterResolving( - ConditionalTypeForParameterMapper::class, - static function ( - ConditionalTypeForParameterMapper $conditionalTypeForParameterMapper - ) use ($rectorConfig): void { - $phpStanStaticTypeMapper = $rectorConfig->make(PHPStanStaticTypeMapper::class); - $conditionalTypeForParameterMapper->autowire($phpStanStaticTypeMapper); - } - ); - - $rectorConfig->afterResolving( - ConditionalTypeMapper::class, - static function (ConditionalTypeMapper $conditionalTypeMapper) use ($rectorConfig): void { - $phpStanStaticTypeMapper = $rectorConfig->make(PHPStanStaticTypeMapper::class); - $conditionalTypeMapper->autowire($phpStanStaticTypeMapper); - } - ); - - $rectorConfig->afterResolving( - UnionTypeMapper::class, - static function (UnionTypeMapper $unionTypeMapper) use ($rectorConfig): void { - $phpStanStaticTypeMapper = $rectorConfig->make(PHPStanStaticTypeMapper::class); - $unionTypeMapper->autowire($phpStanStaticTypeMapper); - } - ); - - } - private function registerNodeNameResolvers(RectorConfig $rectorConfig): void { // node name resolvers @@ -237,7 +193,6 @@ private function registerTaggedServices(RectorConfig $rectorConfig): void // PHP 8.0 attributes $rectorConfig->autodiscover(__DIR__ . '/../PhpAttribute/AnnotationToAttributeMapper'); - $rectorConfig->autodiscover(__DIR__ . '/../PHPStanStaticTypeMapper/TypeMapper'); $rectorConfig->autodiscover(__DIR__ . '/../StaticTypeMapper/PhpDocParser'); $rectorConfig->autodiscover(__DIR__ . '/../NodeNameResolver/NodeNameResolver'); $rectorConfig->autodiscover(__DIR__ . '/../NodeTypeResolver/NodeTypeResolver'); diff --git a/src/NodeTypeResolver/PHPStan/ObjectWithoutClassTypeWithParentTypes.php b/src/NodeTypeResolver/PHPStan/ObjectWithoutClassTypeWithParentTypes.php index 4b89d93898d..a22bc3c295a 100644 --- a/src/NodeTypeResolver/PHPStan/ObjectWithoutClassTypeWithParentTypes.php +++ b/src/NodeTypeResolver/PHPStan/ObjectWithoutClassTypeWithParentTypes.php @@ -4,6 +4,10 @@ namespace Rector\NodeTypeResolver\PHPStan; +use PhpParser\Node\ComplexType; +use PhpParser\Node\Identifier; +use PhpParser\Node\Name; +use PHPStan\Type\NativeTypePosition; use PHPStan\Type\ObjectWithoutClassType; use PHPStan\Type\Type; use PHPStan\Type\TypeWithClassName; @@ -27,4 +31,14 @@ public function getParentTypes(): array { return $this->parentTypes; } + + public function toNativePhpTypeNode(NativeTypePosition $position): Identifier|Name|ComplexType|null + { + // anonymous class implementing a single other type + if (count($this->parentTypes) === 1) { + return $this->parentTypes[0]->toNativePhpTypeNode($position); + } + + return parent::toNativePhpTypeNode($position); + } } diff --git a/src/PHPStanStaticTypeMapper/Contract/TypeMapperInterface.php b/src/PHPStanStaticTypeMapper/Contract/TypeMapperInterface.php deleted file mode 100644 index 36e49ef8019..00000000000 --- a/src/PHPStanStaticTypeMapper/Contract/TypeMapperInterface.php +++ /dev/null @@ -1,39 +0,0 @@ -> - */ - public function getNodeClasses(): array; - - /** - * @param TType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode; - - /** - * @param TType $type - * @param TypeKind::* $typeKind - * @return Name|ComplexType|Identifier|null - */ - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node; -} diff --git a/src/PHPStanStaticTypeMapper/Enum/TypeKind.php b/src/PHPStanStaticTypeMapper/Enum/TypeKind.php index 49d6de14834..c7fb7623001 100644 --- a/src/PHPStanStaticTypeMapper/Enum/TypeKind.php +++ b/src/PHPStanStaticTypeMapper/Enum/TypeKind.php @@ -11,6 +11,4 @@ final class TypeKind public const string RETURN = 'return'; public const string PARAM = 'param'; - - public const string UNION = 'union'; } diff --git a/src/PHPStanStaticTypeMapper/PHPStanStaticTypeMapper.php b/src/PHPStanStaticTypeMapper/PHPStanStaticTypeMapper.php deleted file mode 100644 index 5dbdbb96081..00000000000 --- a/src/PHPStanStaticTypeMapper/PHPStanStaticTypeMapper.php +++ /dev/null @@ -1,77 +0,0 @@ -matchTypeMapper($type); - if (! $typeMapper instanceof TypeMapperInterface) { - throw new NotImplementedYetException(__METHOD__ . ' for ' . $type::class); - } - - return $typeMapper->mapToPHPStanPhpDocTypeNode($type); - } - - /** - * @param TypeKind::* $typeKind - */ - public function mapToPhpParserNode(Type $type, string $typeKind): Name|ComplexType|Identifier|null - { - $typeMapper = $this->matchTypeMapper($type); - if (! $typeMapper instanceof TypeMapperInterface) { - throw new NotImplementedYetException(__METHOD__ . ' for ' . $type::class); - } - - return $typeMapper->mapToPhpParserNode($type, $typeKind); - } - - /** - * Match the most specific mapper: when a type is handled by both a mapper for a parent - * class and one for its subclass, the subclass mapper wins, regardless of registration order. - * - * @return TypeMapperInterface|null - */ - private function matchTypeMapper(Type $type): ?TypeMapperInterface - { - $matchedTypeMapper = null; - $matchedNodeClass = null; - - foreach ($this->typeMappers as $typeMapper) { - foreach ($typeMapper->getNodeClasses() as $nodeClass) { - if (! $type instanceof $nodeClass) { - continue; - } - - if ($matchedNodeClass === null || is_a($nodeClass, $matchedNodeClass, true)) { - $matchedNodeClass = $nodeClass; - $matchedTypeMapper = $typeMapper; - } - } - } - - return $matchedTypeMapper; - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/AccessoryArrayTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/AccessoryArrayTypeMapper.php deleted file mode 100644 index 14b073970d3..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/AccessoryArrayTypeMapper.php +++ /dev/null @@ -1,45 +0,0 @@ - - */ -final class AccessoryArrayTypeMapper implements TypeMapperInterface -{ - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [ - HasOffsetType::class, - HasOffsetValueType::class, - NonEmptyArrayType::class, - OversizedArrayType::class, - ]; - } - - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - return $type->toPhpDocNode(); - } - - public function mapToPhpParserNode(Type $type, string $typeKind): Identifier - { - return new Identifier('array'); - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/AccessoryStringTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/AccessoryStringTypeMapper.php deleted file mode 100644 index 6fa39774cd5..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/AccessoryStringTypeMapper.php +++ /dev/null @@ -1,57 +0,0 @@ - - */ -final readonly class AccessoryStringTypeMapper implements TypeMapperInterface -{ - public function __construct( - private PhpVersionProvider $phpVersionProvider - ) { - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [ - AccessoryLiteralStringType::class, - AccessoryNonEmptyStringType::class, - AccessoryNonFalsyStringType::class, - AccessoryNumericStringType::class, - ]; - } - - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - return $type->toPhpDocNode(); - } - - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node - { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersion::PHP_70)) { - return null; - } - - return new Identifier('string'); - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/ArrayTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/ArrayTypeMapper.php deleted file mode 100644 index e3766509add..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/ArrayTypeMapper.php +++ /dev/null @@ -1,297 +0,0 @@ - - */ -final class ArrayTypeMapper implements TypeMapperInterface -{ - private PHPStanStaticTypeMapper $phpStanStaticTypeMapper; - - public function __construct( - private readonly GenericClassStringTypeNormalizer $genericClassStringTypeNormalizer, - private readonly DetailedTypeAnalyzer $detailedTypeAnalyzer - ) { - } - - // To avoid circular dependency - - public function autowire(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void - { - $this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper; - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [ArrayType::class]; - } - - /** - * @param ArrayType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - return $this->splitUnionItemType($this->createArrayTypeNode($type)); - } - - /** - * @param ArrayType $type - */ - public function mapToPhpParserNode(Type $type, string $typeKind): Identifier - { - return new Identifier('array'); - } - - /** - * Returns "(A|B)[]" as is, the union item type is split later - */ - private function createArrayTypeNode(ArrayType $type): TypeNode - { - // this cannot be handled by PHPStan $type->toPhpDocNode() as requires space removal around "|" in union type - // then e.g. "int" instead of explicit number, and nice arrays - $itemType = $type->getIterableValueType(); - - $isGenericArray = $this->isGenericArrayCandidate($type); - - if ($itemType instanceof UnionType && ! $isGenericArray) { - return $this->createArrayTypeNodeFromUnionType($itemType); - } - - if ($itemType instanceof ArrayType && $this->isGenericArrayCandidate($itemType)) { - return $this->createGenericArrayType($type, true); - } - - if ($isGenericArray) { - return $this->createGenericArrayType($type, true); - } - - // keep "int" key in arary - if ($type->getKeyType() instanceof IntegerType) { - $keyTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($type->getKeyType()); - - if (! $type->isList()->maybe()) { - $nestedTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($type->getItemType()); - - return new GenericTypeNode(new IdentifierTypeNode('array'), [$keyTypeNode, $nestedTypeNode]); - } - } - - $itemTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($itemType); - return new ArrayTypeNode($itemTypeNode); - } - - /** - * "(A|B)[]" becomes "A[]|B[]", or "array|A>" when all items are generics of the same type - */ - private function splitUnionItemType(TypeNode $typeNode): TypeNode - { - if (! $typeNode instanceof ArrayTypeNode || ! $typeNode->type instanceof UnionTypeNode) { - return $typeNode; - } - - $unionTypeNode = $typeNode->type; - if ($this->hasOnlySameGenericTypes($unionTypeNode)) { - return new GenericTypeNode(new IdentifierTypeNode('array'), [ - new IdentifierTypeNode('int'), - $unionTypeNode, - ]); - } - - $arrayTypeNodes = []; - foreach ($unionTypeNode->types as $unionedTypeNode) { - $arrayTypeNodes[] = new ArrayTypeNode($unionedTypeNode); - } - - return new UnionTypeNode($arrayTypeNodes); - } - - private function hasOnlySameGenericTypes(UnionTypeNode $unionTypeNode): bool - { - $genericTypeName = null; - - foreach ($unionTypeNode->types as $unionedTypeNode) { - if (! $unionedTypeNode instanceof GenericTypeNode) { - return false; - } - - if (count($unionedTypeNode->genericTypes) !== 1) { - return false; - } - - if (! $unionedTypeNode->type instanceof IdentifierTypeNode) { - return false; - } - - $genericTypeName ??= $unionedTypeNode->type->name; - if ($genericTypeName !== $unionedTypeNode->type->name) { - return false; - } - } - - return true; - } - - /** - * Value type in "array" uses "array" instead of "A[]|B[]" - */ - private function createGenericArrayValueTypeNode(Type $itemType): TypeNode - { - if (! $itemType instanceof ArrayType) { - return $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($itemType); - } - - $arrayTypeNode = $this->createArrayTypeNode($itemType); - if ($arrayTypeNode instanceof ArrayTypeNode - && ($arrayTypeNode->type instanceof UnionTypeNode || $arrayTypeNode->type instanceof ArrayTypeNode)) { - return new GenericTypeNode(new IdentifierTypeNode('array'), [$arrayTypeNode->type]); - } - - return $this->splitUnionItemType($arrayTypeNode); - } - - private function createArrayTypeNodeFromUnionType(UnionType $unionType): ArrayTypeNode - { - $unionedArrayType = []; - foreach ($unionType->getTypes() as $unionedType) { - $typeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($unionedType); - $unionedArrayType[(string) $typeNode] = $typeNode; - } - - if (count($unionedArrayType) > 1) { - return new ArrayTypeNode(new UnionTypeNode($unionedArrayType)); - } - - /** @var TypeNode $arrayType */ - $arrayType = array_shift($unionedArrayType); - return new ArrayTypeNode($arrayType); - } - - private function isGenericArrayCandidate(ArrayType $arrayType): bool - { - if ($arrayType->getKeyType() instanceof MixedType) { - return false; - } - - if ($this->isClassStringArrayType($arrayType)) { - return true; - } - - // skip simple arrays, like "string[]", from converting to obvious "array" - if ($this->isIntegerKeyAndNonNestedArray($arrayType)) { - return false; - } - - if ($arrayType->getKeyType() instanceof NeverType) { - return false; - } - - // make sure the integer key type is not natural/implicit array int keys - $keysArrayType = $arrayType->getKeysArray(); - if (! $keysArrayType instanceof ConstantArrayType) { - return true; - } - - foreach ($keysArrayType->getValueTypes() as $key => $keyType) { - if (! $keyType instanceof ConstantIntegerType) { - return true; - } - - if ($key !== $keyType->getValue()) { - return true; - } - } - - return false; - } - - private function createGenericArrayType(ArrayType $arrayType, bool $withKey = false): GenericTypeNode - { - $itemType = $arrayType->getIterableValueType(); - $identifierTypeNode = new IdentifierTypeNode('array'); - - // is class-string[] list only - if ($this->isClassStringArrayType($arrayType)) { - $withKey = false; - } - - if (! $withKey) { - $itemTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($itemType); - return new GenericTypeNode($identifierTypeNode, [$itemTypeNode]); - } - - $keyTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($arrayType->getKeyType()); - $itemTypeNode = $this->createGenericArrayValueTypeNode($itemType); - - if ($itemTypeNode instanceof UnionTypeNode && $this->isPairClassTooDetailed($itemType)) { - $itemTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode(new ClassStringType()); - } - - return new GenericTypeNode($identifierTypeNode, [$keyTypeNode, $itemTypeNode]); - } - - private function isPairClassTooDetailed(Type $itemType): bool - { - if (! $itemType instanceof UnionType) { - return false; - } - - if (! $this->genericClassStringTypeNormalizer->isAllGenericClassStringType($itemType)) { - return false; - } - - return $this->detailedTypeAnalyzer->isTooDetailed($itemType); - } - - private function isIntegerKeyAndNonNestedArray(ArrayType $arrayType): bool - { - if (! $arrayType->getKeyType()->isInteger()->yes()) { - return false; - } - - return ! $arrayType->getIterableValueType() - ->isArray() - ->yes(); - } - - private function isClassStringArrayType(ArrayType $arrayType): bool - { - if ($arrayType->getKeyType() instanceof MixedType) { - return $arrayType->getIterableValueType() instanceof GenericClassStringType; - } - - if ($arrayType->getKeyType() instanceof ConstantIntegerType) { - return $arrayType->getIterableValueType() instanceof GenericClassStringType; - } - - return false; - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/BooleanTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/BooleanTypeMapper.php deleted file mode 100644 index cf72c0c41b6..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/BooleanTypeMapper.php +++ /dev/null @@ -1,74 +0,0 @@ - - */ -final readonly class BooleanTypeMapper implements TypeMapperInterface -{ - public function __construct( - private PhpVersionProvider $phpVersionProvider - ) { - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [BooleanType::class]; - } - - /** - * @param BooleanType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - return $type->toPhpDocNode(); - } - - /** - * @param BooleanType $type - */ - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node - { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersion::PHP_70)) { - return null; - } - - if ($typeKind === TypeKind::PROPERTY) { - return new Identifier('bool'); - } - - if ($typeKind === TypeKind::UNION && $type->isFalse()->yes()) { - return new Identifier('false'); - } - - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersion::PHP_82)) { - return new Identifier('bool'); - } - - if ($type->isTrue()->yes()) { - return new Identifier('true'); - } - - if ($type->isFalse()->yes()) { - return new Identifier('false'); - } - - return new Identifier('bool'); - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/CallableTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/CallableTypeMapper.php deleted file mode 100644 index e953961ea88..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/CallableTypeMapper.php +++ /dev/null @@ -1,49 +0,0 @@ - - */ -final class CallableTypeMapper implements TypeMapperInterface -{ - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [CallableType::class]; - } - - /** - * @param CallableType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - return $type->toPhpDocNode(); - } - - /** - * @param TypeKind::* $typeKind - * @param CallableType|ClosureType $type - */ - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node - { - if ($typeKind === TypeKind::PROPERTY) { - return null; - } - - return new Identifier('callable'); - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/ClassStringTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/ClassStringTypeMapper.php deleted file mode 100644 index 9dd3e76cf21..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/ClassStringTypeMapper.php +++ /dev/null @@ -1,69 +0,0 @@ - - */ -final readonly class ClassStringTypeMapper implements TypeMapperInterface -{ - public function __construct( - private PhpVersionProvider $phpVersionProvider - ) { - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [ClassStringType::class]; - } - - /** - * @param ClassStringType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - $type = TypeTraverser::map($type, static function (Type $type, callable $traverse): Type { - if (! $type instanceof ObjectType) { - return $traverse($type); - } - - $typeClass = $type::class; - - if ($typeClass === 'PHPStan\Type\ObjectType') { - return new ObjectType('\\' . $type->getClassName()); - } - - return $traverse($type); - }); - - return $type->toPhpDocNode(); - } - - /** - * @param ClassStringType $type - */ - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node - { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersion::PHP_70)) { - return null; - } - - return new Identifier('string'); - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/ClosureTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/ClosureTypeMapper.php deleted file mode 100644 index 7853101336c..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/ClosureTypeMapper.php +++ /dev/null @@ -1,101 +0,0 @@ - - */ -final readonly class ClosureTypeMapper implements TypeMapperInterface -{ - public function __construct( - private PhpVersionProvider $phpVersionProvider - ) { - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [ClosureType::class]; - } - - /** - * @param ClosureType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - $typeNode = $type->toPhpDocNode(); - - $phpDocNodeTraverser = new NodeTraverser([new CallablePhpDocNodeVisitor( - static function (AstNode $astNode): ?FullyQualifiedIdentifierTypeNode { - if (! $astNode instanceof IdentifierTypeNode) { - return null; - } - - if ($astNode->name !== 'Closure') { - return null; - } - - return new FullyQualifiedIdentifierTypeNode('Closure'); - } - )]); - $phpDocNodeTraverser->traverse([$typeNode]); - - return $typeNode; - } - - /** - * @param TypeKind::* $typeKind - * @param ClosureType $type - */ - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node - { - // ref https://3v4l.org/iKMK6#v5.3.29 - if ($typeKind === TypeKind::PARAM && $this->phpVersionProvider->isAtLeastPhpVersion( - PhpVersion::PHP_53 - )) { - return new FullyQualified('Closure'); - } - - // ref https://3v4l.org/g8WvW#v7.4.0 - if ($typeKind === TypeKind::PROPERTY && $this->phpVersionProvider->isAtLeastPhpVersion( - PhpVersion::PHP_74 - )) { - return new FullyQualified('Closure'); - } - - // ref https://3v4l.org/nUreN#v7.0.0 - if ($typeKind === TypeKind::RETURN && $this->phpVersionProvider->isAtLeastPhpVersion( - PhpVersion::PHP_70 - )) { - return new FullyQualified('Closure'); - } - - // ref https://3v4l.org/ruh5g#v8.0.0 - if ($typeKind === TypeKind::UNION && $this->phpVersionProvider->isAtLeastPhpVersion( - PhpVersion::PHP_80 - )) { - return new FullyQualified('Closure'); - } - - return null; - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeForParameterMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeForParameterMapper.php deleted file mode 100644 index ce6698893cb..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeForParameterMapper.php +++ /dev/null @@ -1,53 +0,0 @@ - - */ -final class ConditionalTypeForParameterMapper implements TypeMapperInterface -{ - private PHPStanStaticTypeMapper $phpStanStaticTypeMapper; - - public function autowire(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void - { - $this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper; - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [ConditionalTypeForParameter::class]; - } - - /** - * @param ConditionalTypeForParameter $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - return $type->toPhpDocNode(); - } - - /** - * @param ConditionalTypeForParameter $type - * @param TypeKind::* $typeKind - */ - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node - { - $type = TypeCombinator::union($type->getIf(), $type->getElse()); - return $this->phpStanStaticTypeMapper->mapToPhpParserNode($type, $typeKind); - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeMapper.php deleted file mode 100644 index 72c567911b0..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeMapper.php +++ /dev/null @@ -1,66 +0,0 @@ - - */ -final class ConditionalTypeMapper implements TypeMapperInterface -{ - private PHPStanStaticTypeMapper $phpStanStaticTypeMapper; - - public function autowire(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void - { - $this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper; - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [ConditionalType::class]; - } - - /** - * @param ConditionalType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - $type = TypeTraverser::map($type, static function (Type $type, callable $traverse): Type { - if ($type instanceof ObjectType && ! $type->getClassReflection() instanceof ClassReflection) { - $newClassName = (string) Strings::after($type->getClassName(), '\\', -1); - return $traverse(new ObjectType($newClassName)); - } - - return $traverse($type); - }); - - return $type->toPhpDocNode(); - } - - /** - * @param ConditionalType $type - * @param TypeKind::* $typeKind - */ - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node - { - $type = TypeCombinator::union($type->getIf(), $type->getElse()); - return $this->phpStanStaticTypeMapper->mapToPhpParserNode($type, $typeKind); - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/ConstantArrayTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/ConstantArrayTypeMapper.php deleted file mode 100644 index 4fb46d028a2..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/ConstantArrayTypeMapper.php +++ /dev/null @@ -1,50 +0,0 @@ - - */ -final readonly class ConstantArrayTypeMapper implements TypeMapperInterface -{ - public function __construct( - private PhpVersionProvider $phpVersionProvider - ) { - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [ConstantArrayType::class]; - } - - /** - * @param ConstantArrayType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - return $type->toPhpDocNode(); - } - - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node - { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersion::PHP_70)) { - return null; - } - - return new Identifier('array'); - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/FloatTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/FloatTypeMapper.php deleted file mode 100644 index 51f86b5a830..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/FloatTypeMapper.php +++ /dev/null @@ -1,53 +0,0 @@ - - */ -final readonly class FloatTypeMapper implements TypeMapperInterface -{ - public function __construct( - private PhpVersionProvider $phpVersionProvider - ) { - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [FloatType::class]; - } - - /** - * @param FloatType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - return $type->toPhpDocNode(); - } - - /** - * @param FloatType $type - */ - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node - { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersion::PHP_70)) { - return null; - } - - return new Identifier('float'); - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/HasMethodTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/HasMethodTypeMapper.php deleted file mode 100644 index 7d390862d95..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/HasMethodTypeMapper.php +++ /dev/null @@ -1,46 +0,0 @@ - - */ -final readonly class HasMethodTypeMapper implements TypeMapperInterface -{ - public function __construct( - private ObjectWithoutClassTypeMapper $objectWithoutClassTypeMapper - ) { - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [HasMethodType::class]; - } - - /** - * @param HasMethodType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - return $type->toPhpDocNode(); - } - - /** - * @param HasMethodType $type - */ - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node - { - return $this->objectWithoutClassTypeMapper->mapToPhpParserNode($type, $typeKind); - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/HasPropertyTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/HasPropertyTypeMapper.php deleted file mode 100644 index 01ffe7fa2ed..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/HasPropertyTypeMapper.php +++ /dev/null @@ -1,46 +0,0 @@ - - */ -final readonly class HasPropertyTypeMapper implements TypeMapperInterface -{ - public function __construct( - private ObjectWithoutClassTypeMapper $objectWithoutClassTypeMapper - ) { - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [HasPropertyType::class]; - } - - /** - * @param HasPropertyType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - return $type->toPhpDocNode(); - } - - /** - * @param HasPropertyType $type - */ - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node - { - return $this->objectWithoutClassTypeMapper->mapToPhpParserNode($type, $typeKind); - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/IntegerTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/IntegerTypeMapper.php deleted file mode 100644 index dbccc5e30a9..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/IntegerTypeMapper.php +++ /dev/null @@ -1,54 +0,0 @@ - - */ -final readonly class IntegerTypeMapper implements TypeMapperInterface -{ - public function __construct( - private PhpVersionProvider $phpVersionProvider - ) { - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [IntegerType::class]; - } - - /** - * @param IntegerType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): IdentifierTypeNode - { - // note: cannot be handled by PHPStan as uses explicit values - return new IdentifierTypeNode('int'); - } - - /** - * @param IntegerType $type - */ - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node - { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersion::PHP_70)) { - return null; - } - - return new Identifier('int'); - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/IntersectionTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/IntersectionTypeMapper.php deleted file mode 100644 index dafaa306b84..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/IntersectionTypeMapper.php +++ /dev/null @@ -1,145 +0,0 @@ - - */ -final readonly class IntersectionTypeMapper implements TypeMapperInterface -{ - public function __construct( - private PhpVersionProvider $phpVersionProvider, - private ObjectWithoutClassTypeMapper $objectWithoutClassTypeMapper, - private ObjectTypeMapper $objectTypeMapper, - private ScalarStringToTypeMapper $scalarStringToTypeMapper, - ) { - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [IntersectionType::class]; - } - - /** - * @param IntersectionType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - $typeNode = $type->toPhpDocNode(); - - $phpDocNodeTraverser = new NodeTraverser([new CallablePhpDocNodeVisitor( - function (AstNode $astNode) use ($typeNode): int|IdentifierTypeNode|null { - if ($astNode === $typeNode) { - return null; - } - - if ($astNode instanceof UnionTypeNode) { - return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; - } - - if ($astNode instanceof ArrayShapeItemNode) { - return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; - } - - if (! $astNode instanceof IdentifierTypeNode) { - return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; - } - - $type = $this->scalarStringToTypeMapper->mapScalarStringToType($astNode->name); - if ($type->isScalar()->yes()) { - return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; - } - - if ($type->isArray()->yes()) { - return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; - } - - if ($type instanceof MixedType && $type->isExplicitMixed()) { - return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; - } - - $astNode->name = '\\' . ltrim($astNode->name, '\\'); - return $astNode; - } - )]); - $phpDocNodeTraverser->traverse([$typeNode]); - - return $typeNode; - } - - /** - * @param IntersectionType $type - */ - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node - { - // accessory string types, e.g. "numeric-string&non-falsy-string", are just "string" - if ($type->isString()->yes()) { - return new Identifier('string'); - } - - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersion::PHP_81)) { - return null; - } - - $intersectionedTypeNodes = []; - foreach ($type->getTypes() as $type) { - if ($type instanceof ObjectWithoutClassType) { - return $this->objectWithoutClassTypeMapper->mapToPhpParserNode($type, $typeKind); - } - - if (! $type instanceof ObjectType) { - return null; - } - - $resolvedType = $this->objectTypeMapper->mapToPhpParserNode($type, $typeKind); - - if (! $resolvedType instanceof FullyQualified) { - return null; - } - - $intersectionedTypeNodes[] = $resolvedType; - } - - if ($intersectionedTypeNodes === []) { - return null; - } - - if (count($intersectionedTypeNodes) === 1) { - return current($intersectionedTypeNodes); - } - - if ($typeKind === TypeKind::UNION && ! $this->phpVersionProvider->isAtLeastPhpVersion( - PhpVersion::PHP_82 - )) { - return null; - } - - return new Node\IntersectionType($intersectionedTypeNodes); - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/IterableTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/IterableTypeMapper.php deleted file mode 100644 index 27db268db96..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/IterableTypeMapper.php +++ /dev/null @@ -1,41 +0,0 @@ - - */ -final class IterableTypeMapper implements TypeMapperInterface -{ - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [IterableType::class]; - } - - /** - * @param IterableType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - return $type->toPhpDocNode(); - } - - /** - * @param IterableType $type - */ - public function mapToPhpParserNode(Type $type, string $typeKind): Identifier - { - return new Identifier('iterable'); - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/MixedTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/MixedTypeMapper.php deleted file mode 100644 index f87cdccab57..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/MixedTypeMapper.php +++ /dev/null @@ -1,62 +0,0 @@ - - */ -final readonly class MixedTypeMapper implements TypeMapperInterface -{ - public function __construct( - private PhpVersionProvider $phpVersionProvider - ) { - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [MixedType::class]; - } - - /** - * @param MixedType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - return $type->toPhpDocNode(); - } - - /** - * @param MixedType $type - */ - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node - { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersion::PHP_80)) { - return null; - } - - if (! $type->isExplicitMixed()) { - return null; - } - - if ($typeKind === TypeKind::UNION) { - return null; - } - - return new Identifier('mixed'); - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/NeverTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/NeverTypeMapper.php deleted file mode 100644 index ea94feb2759..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/NeverTypeMapper.php +++ /dev/null @@ -1,41 +0,0 @@ - - */ -final class NeverTypeMapper implements TypeMapperInterface -{ - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [NeverType::class]; - } - - /** - * @param NeverType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - return $type->toPhpDocNode(); - } - - /** - * @param NeverType $type - */ - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node - { - return null; - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/NonexistentParentClassTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/NonexistentParentClassTypeMapper.php deleted file mode 100644 index f2d88a35dd3..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/NonexistentParentClassTypeMapper.php +++ /dev/null @@ -1,47 +0,0 @@ - - * @see \Rector\Tests\PHPStanStaticTypeMapper\TypeMapper\NonexistentParentClassTypeMapperTest - */ -final class NonexistentParentClassTypeMapper implements TypeMapperInterface -{ - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [NonexistentParentClassType::class]; - } - - /** - * @param NonexistentParentClassType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - return $type->toPhpDocNode(); - } - - /** - * @param NonexistentParentClassType $type - */ - public function mapToPhpParserNode(Type $type, string $typeKind): Name - { - return new Name(ObjectReference::PARENT); - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/NullTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/NullTypeMapper.php deleted file mode 100644 index 41a8bf637ee..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/NullTypeMapper.php +++ /dev/null @@ -1,65 +0,0 @@ - - */ -final readonly class NullTypeMapper implements TypeMapperInterface -{ - public function __construct( - private PhpVersionProvider $phpVersionProvider - ) { - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [NullType::class]; - } - - /** - * @param NullType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - return $type->toPhpDocNode(); - } - - /** - * @param TypeKind::* $typeKind - * @param NullType $type - */ - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node - { - // can be a standalone type, only case where null makes sense - if ($this->phpVersionProvider->isAtLeastPhpVersion( - PhpVersion::PHP_82 - ) && $typeKind === TypeKind::RETURN) { - return new Identifier('null'); - } - - // if part of union, can be added even in PHP 8.0 - if ($typeKind === TypeKind::UNION && $this->phpVersionProvider->isAtLeastPhpVersion( - PhpVersion::PHP_71 - )) { - return new Identifier('null'); - } - - return null; - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/ObjectTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/ObjectTypeMapper.php deleted file mode 100644 index 5df7718ffd5..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/ObjectTypeMapper.php +++ /dev/null @@ -1,103 +0,0 @@ - - */ -final class ObjectTypeMapper implements TypeMapperInterface -{ - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [ObjectType::class]; - } - - /** - * @param ObjectType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - $type = TypeTraverser::map($type, static function (Type $type, callable $traverse): Type { - if ($type instanceof ArrayType && ($type->getItemType() instanceof MixedType && $type->getKeyType() instanceof MixedType)) { - return new ArrayType(new MixedType(), new MixedType(true)); - } - - if (! $type instanceof ObjectType) { - return $traverse($type); - } - - $typeClass = $type::class; - - // early native ObjectType check - if ($typeClass === 'PHPStan\Type\ObjectType') { - return new ObjectType('\\' . $type->getClassName()); - } - - if ($type instanceof FullyQualifiedObjectType) { - return new ObjectType('\\' . $type->getClassName()); - } - - if ($type instanceof GenericObjectType) { - return $traverse(new GenericObjectType('\\' . $type->getClassName(), $type->getTypes())); - } - - return $traverse($type); - }); - - return $type->toPhpDocNode(); - } - - /** - * @param ObjectType $type - */ - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node - { - if ($type instanceof SelfObjectType) { - return new Name('self'); - } - - if ($type instanceof ShortenedObjectType || $type instanceof AliasedObjectType) { - return new FullyQualified($type->getFullyQualifiedName()); - } - - if ($type instanceof FullyQualifiedObjectType) { - $className = $type->getClassName(); - - if (str_starts_with($className, '\\')) { - // skip leading \ - return new FullyQualified(Strings::substring($className, 1)); - } - - return new FullyQualified($className); - } - - if ($type instanceof NonExistingObjectType) { - return null; - } - - return new FullyQualified($type->getClassName()); - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/ObjectWithoutClassTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/ObjectWithoutClassTypeMapper.php deleted file mode 100644 index 7b579cbb1fc..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/ObjectWithoutClassTypeMapper.php +++ /dev/null @@ -1,66 +0,0 @@ - - */ -final readonly class ObjectWithoutClassTypeMapper implements TypeMapperInterface -{ - public function __construct( - private PhpVersionProvider $phpVersionProvider - ) { - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [ObjectWithoutClassType::class]; - } - - /** - * @param ObjectWithoutClassType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - return $type->toPhpDocNode(); - } - - /** - * @param ObjectWithoutClassType|HasMethodType|HasPropertyType $type - */ - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node - { - // special case for anonymous classes that implement another type - if ($type instanceof ObjectWithoutClassTypeWithParentTypes) { - $parentTypes = $type->getParentTypes(); - if (count($parentTypes) === 1) { - $parentType = $parentTypes[0]; - return new FullyQualified($parentType->getClassName()); - } - } - - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersion::PHP_72)) { - return null; - } - - return new Identifier('object'); - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/ParentStaticTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/ParentStaticTypeMapper.php deleted file mode 100644 index db27b9dd2b0..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/ParentStaticTypeMapper.php +++ /dev/null @@ -1,42 +0,0 @@ - - */ -final class ParentStaticTypeMapper implements TypeMapperInterface -{ - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [ParentStaticType::class]; - } - - /** - * @param ParentStaticType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - return $type->toPhpDocNode(); - } - - /** - * @param ParentStaticType $type - */ - public function mapToPhpParserNode(Type $type, string $typeKind): Name - { - return new Name(ObjectReference::PARENT); - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/ResourceTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/ResourceTypeMapper.php deleted file mode 100644 index f2b2ebb6e95..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/ResourceTypeMapper.php +++ /dev/null @@ -1,41 +0,0 @@ - - */ -final class ResourceTypeMapper implements TypeMapperInterface -{ - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [ResourceType::class]; - } - - /** - * @param ResourceType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - return $type->toPhpDocNode(); - } - - /** - * @param ResourceType $type - */ - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node - { - return null; - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/StaticTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/StaticTypeMapper.php deleted file mode 100644 index bcd234e5124..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/StaticTypeMapper.php +++ /dev/null @@ -1,66 +0,0 @@ - - */ -final readonly class StaticTypeMapper implements TypeMapperInterface -{ - public function __construct( - private PhpVersionProvider $phpVersionProvider - ) { - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [StaticType::class]; - } - - /** - * @param StaticType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - return $type->toPhpDocNode(); - } - - /** - * @param SimpleStaticType|StaticType $type - */ - public function mapToPhpParserNode(Type $type, string $typeKind): Name - { - if ($type instanceof SelfStaticType) { - return new Name(ObjectReference::SELF); - } - - if ($typeKind !== TypeKind::RETURN) { - return new Name(ObjectReference::SELF); - } - - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersion::PHP_80)) { - return new Name(ObjectReference::SELF); - } - - return new Name(ObjectReference::STATIC); - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/StrictMixedTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/StrictMixedTypeMapper.php deleted file mode 100644 index 90c58411d43..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/StrictMixedTypeMapper.php +++ /dev/null @@ -1,60 +0,0 @@ - - */ -final readonly class StrictMixedTypeMapper implements TypeMapperInterface -{ - private const string MIXED = 'mixed'; - - public function __construct( - private PhpVersionProvider $phpVersionProvider - ) { - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [StrictMixedType::class]; - } - - /** - * @param StrictMixedType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - return $type->toPhpDocNode(); - } - - /** - * @param StrictMixedType $type - */ - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node - { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersion::PHP_80)) { - return null; - } - - if ($typeKind === TypeKind::UNION) { - return null; - } - - return new Identifier(self::MIXED); - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/StringTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/StringTypeMapper.php deleted file mode 100644 index b4149f93d3d..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/StringTypeMapper.php +++ /dev/null @@ -1,50 +0,0 @@ - - */ -final readonly class StringTypeMapper implements TypeMapperInterface -{ - public function __construct( - private PhpVersionProvider $phpVersionProvider - ) { - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [StringType::class]; - } - - /** - * @param StringType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - return $type->toPhpDocNode(); - } - - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node - { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersion::PHP_70)) { - return null; - } - - return new Identifier('string'); - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/TypeWithClassNameTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/TypeWithClassNameTypeMapper.php deleted file mode 100644 index 3a6f46a2930..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/TypeWithClassNameTypeMapper.php +++ /dev/null @@ -1,53 +0,0 @@ - - */ -final readonly class TypeWithClassNameTypeMapper implements TypeMapperInterface -{ - public function __construct( - private PhpVersionProvider $phpVersionProvider - ) { - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [TypeWithClassName::class]; - } - - /** - * @param TypeWithClassName $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - return $type->toPhpDocNode(); - } - - /** - * @param TypeWithClassName $type - */ - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node - { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersion::PHP_70)) { - return null; - } - - return new Identifier('string'); - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/UnionTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/UnionTypeMapper.php deleted file mode 100644 index 0d68d41dcd5..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/UnionTypeMapper.php +++ /dev/null @@ -1,302 +0,0 @@ - - */ -final class UnionTypeMapper implements TypeMapperInterface -{ - private PHPStanStaticTypeMapper $phpStanStaticTypeMapper; - - public function __construct( - private readonly PhpVersionProvider $phpVersionProvider, - private readonly PropertyAnalyzer $propertyAnalyzer - ) { - } - - public function autowire(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void - { - $this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper; - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [UnionType::class]; - } - - /** - * @param UnionType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): UnionTypeNode - { - $unionTypesNodes = []; - - foreach ($type->getTypes() as $unionedType) { - if ($unionedType instanceof ArrayType && $unionedType->getItemType() instanceof NeverType) { - $unionedType = new ArrayType($unionedType->getKeyType(), new MixedType()); - } - - $unionedTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($unionedType); - - // an array of union item type is mapped to "A[]|B[]", merge it into this union - $typeNodes = $unionedTypeNode instanceof UnionTypeNode ? $unionedTypeNode->types : [$unionedTypeNode]; - - // remove duplicates, e.g. "int[]|int[]" - foreach ($typeNodes as $typeNode) { - $unionTypesNodes[(string) $typeNode] = $typeNode; - } - } - - return new UnionTypeNode(array_values($unionTypesNodes)); - } - - /** - * @param UnionType $type - */ - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node - { - $phpParserUnionType = $this->matchPhpParserUnionType($type, $typeKind); - if ($phpParserUnionType instanceof PhpParserUnionType) { - return $this->resolveUnionTypeNode($phpParserUnionType); - } - - return $phpParserUnionType; - } - - /** - * If type is nullable, and has only one other value, - * this creates at least "?Type" in case of PHP 7.1-7.4 - */ - private function resolveTypeWithNullablePHPParserUnionType( - PhpParserUnionType $phpParserUnionType - ): PhpParserUnionType|NullableType|null { - $totalTypes = count($phpParserUnionType->types); - if ($totalTypes === 2) { - $firstType = $phpParserUnionType->types[0]; - $secondType = $phpParserUnionType->types[1]; - - try { - Assert::isAnyOf($firstType, [Name::class, Identifier::class]); - Assert::isAnyOf($secondType, [Name::class, Identifier::class]); - } catch (InvalidArgumentException) { - return $this->resolveUnionTypes($phpParserUnionType); - } - - $firstTypeValue = $firstType->toString(); - $secondTypeValue = $secondType->toString(); - - if ($firstTypeValue === $secondTypeValue) { - return $this->resolveUnionTypes($phpParserUnionType); - } - - if ($firstTypeValue === 'null') { - return $this->resolveNullableType(new NullableType($secondType)); - } - - if ($secondTypeValue === 'null') { - return $this->resolveNullableType(new NullableType($firstType)); - } - } - - return $this->resolveUnionTypes($phpParserUnionType); - } - - private function resolveNullableType(NullableType $nullableType): null|NullableType|PhpParserUnionType - { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersion::PHP_71)) { - return null; - } - - /** @var PHPParserNodeIntersectionType|Identifier|Name $type */ - $type = $nullableType->type; - if (! $type instanceof PHPParserNodeIntersectionType) { - // ?false is allowed only since PHP 8.2+, lets fallback to bool instead - if ($type->toString() === 'false' && ! $this->phpVersionProvider->isAtLeastPhpVersion( - PhpVersion::PHP_82 - )) { - return new NullableType(new Identifier('bool')); - } - - return $nullableType; - } - - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersion::PHP_80)) { - return null; - } - - $types = [$type]; - $types[] = new Identifier('null'); - - return new PhpParserUnionType($types); - } - - private function resolveUnionTypes(PhpParserUnionType $phpParserUnionType): ?PhpParserUnionType - { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersion::PHP_80)) { - return null; - } - - return $phpParserUnionType; - } - - private function hasObjectAndStaticType(PhpParserUnionType $phpParserUnionType): bool - { - $hasAnonymousObjectType = false; - $hasObjectType = false; - foreach ($phpParserUnionType->types as $type) { - if ($type instanceof Identifier && $type->toString() === 'object') { - $hasAnonymousObjectType = true; - continue; - } - - if ($type instanceof FullyQualified || ($type instanceof Name && $type->isSpecialClassName())) { - $hasObjectType = true; - continue; - } - } - - return $hasObjectType && $hasAnonymousObjectType; - } - - /** - * @return Name|FullyQualified|ComplexType|Identifier|null - */ - private function matchPhpParserUnionType(UnionType $unionType, string $typeKind): ?Node - { - $phpParserUnionedTypes = []; - - foreach ($unionType->getTypes() as $unionedType) { - // NullType or ConstantBooleanType with false value inside UnionType is allowed - // void type and mixed type are not allowed in union - $phpParserNode = $this->phpStanStaticTypeMapper->mapToPhpParserNode($unionedType, TypeKind::UNION); - if ($phpParserNode === null) { - return null; - } - - // special callable type only not allowed on property - if ($typeKind === TypeKind::PROPERTY && $this->propertyAnalyzer->isForbiddenType($unionedType)) { - return null; - } - - $phpParserUnionedTypes[] = $phpParserNode; - } - - /** @var Identifier[]|Name[] $phpParserUnionedTypes */ - $phpParserUnionedTypes = array_unique($phpParserUnionedTypes, SORT_REGULAR); - $phpParserUnionedTypes = $this->removeRedundantIntersectionTypes($phpParserUnionedTypes); - - $countPhpParserUnionedTypes = count($phpParserUnionedTypes); - if ($countPhpParserUnionedTypes === 1) { - return $phpParserUnionedTypes[0]; - } - - return $this->resolveTypeWithNullablePHPParserUnionType(new PhpParserUnionType($phpParserUnionedTypes)); - } - - /** - * PHP rejects e.g. "A|(A&B)" with "Type A&B is redundant as it is more restrictive than type A", - * so drop intersections that contain all parts of another member of the union - * - * @param array $phpParserUnionedTypes - * @return list - */ - private function removeRedundantIntersectionTypes(array $phpParserUnionedTypes): array - { - $typeNames = []; - foreach ($phpParserUnionedTypes as $key => $phpParserUnionedType) { - $typeNames[$key] = $this->resolveIntersectionPartNames($phpParserUnionedType); - } - - foreach ($phpParserUnionedTypes as $key => $phpParserUnionedType) { - if (! $phpParserUnionedType instanceof PHPParserNodeIntersectionType) { - continue; - } - - foreach ($typeNames as $otherKey => $otherTypeNames) { - if ($otherKey === $key || $otherTypeNames === []) { - continue; - } - - // already removed - if (! isset($phpParserUnionedTypes[$otherKey])) { - continue; - } - - if (array_diff($otherTypeNames, $typeNames[$key]) === []) { - unset($phpParserUnionedTypes[$key]); - continue 2; - } - } - } - - return array_values($phpParserUnionedTypes); - } - - /** - * @return string[] - */ - private function resolveIntersectionPartNames(Node $node): array - { - if ($node instanceof Name) { - return [$node->toString()]; - } - - if (! $node instanceof PHPParserNodeIntersectionType) { - return []; - } - - $names = []; - foreach ($node->types as $type) { - if (! $type instanceof Name) { - return []; - } - - $names[] = $type->toString(); - } - - return $names; - } - - private function resolveUnionTypeNode(PhpParserUnionType $phpParserUnionType): ?PhpParserUnionType - { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersion::PHP_80)) { - return null; - } - - // special case that would crash, when stdClass and object is used, - if ($this->hasObjectAndStaticType($phpParserUnionType)) { - return null; - } - - return $phpParserUnionType; - } -} diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/VoidTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/VoidTypeMapper.php deleted file mode 100644 index 7b771ba1807..00000000000 --- a/src/PHPStanStaticTypeMapper/TypeMapper/VoidTypeMapper.php +++ /dev/null @@ -1,61 +0,0 @@ - - */ -final readonly class VoidTypeMapper implements TypeMapperInterface -{ - private const string VOID = 'void'; - - public function __construct( - private PhpVersionProvider $phpVersionProvider - ) { - } - - /** - * @return array> - */ - public function getNodeClasses(): array - { - return [VoidType::class]; - } - - /** - * @param VoidType $type - */ - public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode - { - return $type->toPhpDocNode(); - } - - /** - * @param TypeKind::* $typeKind - * @param VoidType $type - */ - public function mapToPhpParserNode(Type $type, string $typeKind): ?Node - { - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersion::PHP_71)) { - return null; - } - - if (in_array($typeKind, [TypeKind::PARAM, TypeKind::PROPERTY, TypeKind::UNION], true)) { - return null; - } - - return new Identifier(self::VOID); - } -} diff --git a/src/StaticTypeMapper/StaticTypeMapper.php b/src/StaticTypeMapper/StaticTypeMapper.php index 98e9705766f..ebfb4b7dd35 100644 --- a/src/StaticTypeMapper/StaticTypeMapper.php +++ b/src/StaticTypeMapper/StaticTypeMapper.php @@ -8,18 +8,30 @@ use PhpParser\Node\ComplexType; use PhpParser\Node\Identifier; use PhpParser\Node\Name; +use PHPStan\Php\PhpVersion; use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\ThrowsTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; +use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode; +use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode; +use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; +use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode; use PHPStan\PhpDocParser\Ast\Type\TypeNode; +use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode; +use PHPStan\Type\ArrayType; +use PHPStan\Type\BooleanType; +use PHPStan\Type\IntegerType; use PHPStan\Type\MixedType; +use PHPStan\Type\NativeTypePosition; use PHPStan\Type\Type; +use PHPStan\Type\TypeTraverser; +use PHPStan\Type\UnionType; use Rector\Exception\NotImplementedYetException; +use Rector\Php\PhpVersionProvider; use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; -use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper; use Rector\StaticTypeMapper\Mapper\PhpParserNodeMapper; use Rector\StaticTypeMapper\Naming\NameScopeFactory; use Rector\StaticTypeMapper\PhpDoc\PhpDocTypeMapper; @@ -32,24 +44,57 @@ { public function __construct( private NameScopeFactory $nameScopeFactory, - private PHPStanStaticTypeMapper $phpStanStaticTypeMapper, private PhpDocTypeMapper $phpDocTypeMapper, - private PhpParserNodeMapper $phpParserNodeMapper + private PhpParserNodeMapper $phpParserNodeMapper, + private PhpVersionProvider $phpVersionProvider ) { } public function mapPHPStanTypeToPHPStanPhpDocTypeNode(Type $phpStanType): TypeNode { - return $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($phpStanType); + // explicit int values and ranges are too detailed for a docblock + $phpStanType = TypeTraverser::map($phpStanType, static function (Type $type, callable $traverse): Type { + if ($type instanceof IntegerType) { + return new IntegerType(); + } + + if ($type instanceof UnionType || $type instanceof ArrayType) { + return $traverse($type); + } + + return $type; + }); + + return $this->styleTypeNode($phpStanType->toPhpDocNode()); } /** * @param TypeKind::* $typeKind - * @return Name|ComplexType|Identifier|null */ - public function mapPHPStanTypeToPhpParserNode(Type $phpStanType, string $typeKind): ?Node + public function mapPHPStanTypeToPhpParserNode(Type $phpStanType, string $typeKind): Name|ComplexType|Identifier|null { - return $this->phpStanStaticTypeMapper->mapToPhpParserNode($phpStanType, $typeKind); + if ($typeKind === TypeKind::PARAM) { + return $phpStanType->toNativePhpTypeNode(NativeTypePosition::parameter($this->getPhpVersion())); + } + + if ($typeKind === TypeKind::RETURN) { + return $phpStanType->toNativePhpTypeNode(NativeTypePosition::returnType($this->getPhpVersion())); + } + + // Rector policy: a property or constant holding only true, false or null is too narrow to be useful + if ($phpStanType->isNull()->yes()) { + return null; + } + + $phpStanType = TypeTraverser::map($phpStanType, static function (Type $type, callable $traverse): Type { + if ($type->isTrue()->yes() || $type->isFalse()->yes()) { + return new BooleanType(); + } + + return $traverse($type); + }); + + return $phpStanType->toNativePhpTypeNode(NativeTypePosition::property($this->getPhpVersion())); } public function mapPhpParserNodePHPStanType(Node $node): Type @@ -81,4 +126,85 @@ public function mapPHPStanPhpDocTypeNodeToPHPStanType(TypeNode $typeNode, Node $ $nameScope = $this->nameScopeFactory->createNameScopeFromNodeWithoutTemplateTypes($node); return $this->phpDocTypeMapper->mapToPHPStanType($typeNode, $node, $nameScope); } + + /** + * Rector prints "A[]" instead of "array" or "array" + */ + private function styleTypeNode(TypeNode $typeNode): TypeNode + { + if ($typeNode instanceof UnionTypeNode) { + $unionedTypeNodes = []; + foreach ($typeNode->types as $unionedTypeNode) { + $unionedTypeNode = $this->styleTypeNode($unionedTypeNode); + + // "A[]|B[]" from an array of a union is merged into this union + foreach ($unionedTypeNode instanceof UnionTypeNode ? $unionedTypeNode->types : [$unionedTypeNode] as $singleTypeNode) { + $unionedTypeNodes[(string) $singleTypeNode] = $singleTypeNode; + } + } + + return new UnionTypeNode(array_values($unionedTypeNodes)); + } + + if ($typeNode instanceof IntersectionTypeNode) { + return new IntersectionTypeNode(array_map($this->styleTypeNode(...), $typeNode->types)); + } + + if ($typeNode instanceof IdentifierTypeNode && $typeNode->name === 'array') { + return new ArrayTypeNode(new IdentifierTypeNode('mixed')); + } + + if (! $typeNode instanceof GenericTypeNode) { + return $typeNode; + } + + $genericTypeNodes = array_map($this->styleTypeNode(...), $typeNode->genericTypes); + if ($typeNode->type instanceof IdentifierTypeNode && $typeNode->type->name === 'array' && $this->isArrayOfItemsOnly($genericTypeNodes)) { + $itemTypeNode = $genericTypeNodes[array_key_last($genericTypeNodes)]; + if (! $itemTypeNode instanceof UnionTypeNode) { + return new ArrayTypeNode($itemTypeNode); + } + + return new UnionTypeNode(array_map( + static fn (TypeNode $unionedTypeNode): ArrayTypeNode => new ArrayTypeNode($unionedTypeNode), + $itemTypeNode->types + )); + } + + $typeNode->genericTypes = $genericTypeNodes; + return $typeNode; + } + + /** + * "array" or "array" can be printed as "A[]", unless that would lose the int key of nested arrays, + * or turn "array|A>" into "A[]|A[]" + * + * @param TypeNode[] $genericTypeNodes + */ + private function isArrayOfItemsOnly(array $genericTypeNodes): bool + { + if (count($genericTypeNodes) === 2 && (string) $genericTypeNodes[0] !== 'int') { + return false; + } + + $itemTypeNode = $genericTypeNodes[array_key_last($genericTypeNodes)]; + $itemTypeNodes = $itemTypeNode instanceof UnionTypeNode ? $itemTypeNode->types : [$itemTypeNode]; + + foreach ($itemTypeNodes as $singleItemTypeNode) { + if ($singleItemTypeNode instanceof GenericTypeNode && count($itemTypeNodes) > 1) { + return false; + } + + if (count($genericTypeNodes) === 2 && ($singleItemTypeNode instanceof ArrayTypeNode || $singleItemTypeNode instanceof GenericTypeNode)) { + return false; + } + } + + return true; + } + + private function getPhpVersion(): PhpVersion + { + return new PhpVersion($this->phpVersionProvider->provide()); + } } diff --git a/src/StaticTypeMapper/ValueObject/Type/AliasedObjectType.php b/src/StaticTypeMapper/ValueObject/Type/AliasedObjectType.php index 30dddc4966a..6f3898b563a 100644 --- a/src/StaticTypeMapper/ValueObject/Type/AliasedObjectType.php +++ b/src/StaticTypeMapper/ValueObject/Type/AliasedObjectType.php @@ -5,10 +5,12 @@ namespace Rector\StaticTypeMapper\ValueObject\Type; use PhpParser\Node\Name; +use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Stmt\Use_; use PhpParser\Node\UseItem; use PHPStan\PhpDocParser\Ast\Type\RelativeNameNode; use PHPStan\PhpDocParser\Ast\Type\TypeNode; +use PHPStan\Type\NativeTypePosition; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; use Rector\StaticTypeMapper\Resolver\ClassNameFromObjectTypeResolver; @@ -70,6 +72,11 @@ public function equals(Type $type): bool return parent::equals($type); } + public function toNativePhpTypeNode(NativeTypePosition $position): ?Name + { + return $position->isClassNameAllowed() ? new FullyQualified($this->fullyQualifiedClass) : null; + } + public function toPhpDocNode(): TypeNode { return new RelativeNameNode($this->getShortName()); diff --git a/src/StaticTypeMapper/ValueObject/Type/FullyQualifiedObjectType.php b/src/StaticTypeMapper/ValueObject/Type/FullyQualifiedObjectType.php index 88aaa413f1d..bd745af33e3 100644 --- a/src/StaticTypeMapper/ValueObject/Type/FullyQualifiedObjectType.php +++ b/src/StaticTypeMapper/ValueObject/Type/FullyQualifiedObjectType.php @@ -6,8 +6,10 @@ use Nette\Utils\Strings; use PhpParser\Node\Name; +use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Stmt\Use_; use PhpParser\Node\UseItem; +use PHPStan\Type\NativeTypePosition; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; use Rector\NodeTypeResolver\Node\AttributeKey; @@ -77,4 +79,9 @@ public function equals(Type $type): bool return false; } + + public function toNativePhpTypeNode(NativeTypePosition $position): ?Name + { + return $position->isClassNameAllowed() ? new FullyQualified(ltrim($this->getClassName(), '\\')) : null; + } } diff --git a/src/StaticTypeMapper/ValueObject/Type/NonExistingObjectType.php b/src/StaticTypeMapper/ValueObject/Type/NonExistingObjectType.php index 8ea12223b31..18c1de9e7d9 100644 --- a/src/StaticTypeMapper/ValueObject/Type/NonExistingObjectType.php +++ b/src/StaticTypeMapper/ValueObject/Type/NonExistingObjectType.php @@ -4,8 +4,12 @@ namespace Rector\StaticTypeMapper\ValueObject\Type; +use PhpParser\Node\ComplexType; +use PhpParser\Node\Identifier; +use PhpParser\Node\Name; use PHPStan\PhpDocParser\Ast\Type\RelativeNameNode; use PHPStan\PhpDocParser\Ast\Type\TypeNode; +use PHPStan\Type\NativeTypePosition; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; @@ -26,6 +30,11 @@ public function equals(Type $type): bool return false; } + public function toNativePhpTypeNode(NativeTypePosition $position): Identifier|Name|ComplexType|null + { + return null; + } + /** * printed as resolved by Rector, which keeps names written with a namespace as they are */ diff --git a/src/StaticTypeMapper/ValueObject/Type/ParentStaticType.php b/src/StaticTypeMapper/ValueObject/Type/ParentStaticType.php index ab95077abc7..bbc6a2d5fc2 100644 --- a/src/StaticTypeMapper/ValueObject/Type/ParentStaticType.php +++ b/src/StaticTypeMapper/ValueObject/Type/ParentStaticType.php @@ -4,8 +4,14 @@ namespace Rector\StaticTypeMapper\ValueObject\Type; +use PhpParser\Node\Name; +use PHPStan\Type\NativeTypePosition; use PHPStan\Type\StaticType; final class ParentStaticType extends StaticType { + public function toNativePhpTypeNode(NativeTypePosition $position): ?Name + { + return $position->isClassNameAllowed() ? new Name('parent') : null; + } } diff --git a/src/StaticTypeMapper/ValueObject/Type/SelfObjectType.php b/src/StaticTypeMapper/ValueObject/Type/SelfObjectType.php index 9a1c02cce22..1f61241f630 100644 --- a/src/StaticTypeMapper/ValueObject/Type/SelfObjectType.php +++ b/src/StaticTypeMapper/ValueObject/Type/SelfObjectType.php @@ -4,8 +4,14 @@ namespace Rector\StaticTypeMapper\ValueObject\Type; +use PhpParser\Node\Name; +use PHPStan\Type\NativeTypePosition; use PHPStan\Type\ObjectType; final class SelfObjectType extends ObjectType { + public function toNativePhpTypeNode(NativeTypePosition $position): ?Name + { + return $position->isClassNameAllowed() ? new Name('self') : null; + } } diff --git a/src/StaticTypeMapper/ValueObject/Type/SelfStaticType.php b/src/StaticTypeMapper/ValueObject/Type/SelfStaticType.php index d415523eb8f..f67ca5f13b0 100644 --- a/src/StaticTypeMapper/ValueObject/Type/SelfStaticType.php +++ b/src/StaticTypeMapper/ValueObject/Type/SelfStaticType.php @@ -4,8 +4,14 @@ namespace Rector\StaticTypeMapper\ValueObject\Type; +use PhpParser\Node\Name; +use PHPStan\Type\NativeTypePosition; use PHPStan\Type\ThisType; final class SelfStaticType extends ThisType { + public function toNativePhpTypeNode(NativeTypePosition $position): ?Name + { + return $position->isClassNameAllowed() ? new Name('self') : null; + } } diff --git a/src/StaticTypeMapper/ValueObject/Type/ShortenedObjectType.php b/src/StaticTypeMapper/ValueObject/Type/ShortenedObjectType.php index 820a62d6132..f40533edb48 100644 --- a/src/StaticTypeMapper/ValueObject/Type/ShortenedObjectType.php +++ b/src/StaticTypeMapper/ValueObject/Type/ShortenedObjectType.php @@ -4,9 +4,12 @@ namespace Rector\StaticTypeMapper\ValueObject\Type; +use PhpParser\Node\Name; +use PhpParser\Node\Name\FullyQualified; use PHPStan\PhpDocParser\Ast\Type\RelativeNameNode; use PHPStan\PhpDocParser\Ast\Type\TypeNode; use PHPStan\Type\IsSuperTypeOfResult; +use PHPStan\Type\NativeTypePosition; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; @@ -59,6 +62,11 @@ public function equals(Type $type): bool return false; } + public function toNativePhpTypeNode(NativeTypePosition $position): ?Name + { + return $position->isClassNameAllowed() ? new FullyQualified($this->fullyQualifiedName) : null; + } + public function toPhpDocNode(): TypeNode { return new RelativeNameNode($this->getShortName()); diff --git a/tests/Issues/ImportFullyQualifiedIdentifierDocblock/Source/AddFullyQualifiedIdentifierDocblockRector.php b/tests/Issues/ImportFullyQualifiedIdentifierDocblock/Source/AddFullyQualifiedIdentifierDocblockRector.php index 0f040cb9e73..a91ca996e74 100644 --- a/tests/Issues/ImportFullyQualifiedIdentifierDocblock/Source/AddFullyQualifiedIdentifierDocblockRector.php +++ b/tests/Issues/ImportFullyQualifiedIdentifierDocblock/Source/AddFullyQualifiedIdentifierDocblockRector.php @@ -8,7 +8,7 @@ use PhpParser\Node\Stmt\Property; use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; -use Rector\BetterPhpDocParser\ValueObject\Type\FullyQualifiedIdentifierTypeNode; +use PHPStan\PhpDocParser\Ast\Type\FullyQualifiedNameNode; use Rector\Comments\NodeDocBlock\DocBlockUpdater; use Rector\Rector\AbstractRector; use Rector\RuleDoc\RuleDefinition; @@ -42,7 +42,7 @@ public function refactor(Node $node) return null; } - $varTagValueNode = new VarTagValueNode(new FullyQualifiedIdentifierTypeNode('DateTime'), '', ''); + $varTagValueNode = new VarTagValueNode(new FullyQualifiedNameNode('DateTime'), '', ''); $phpDocInfo->addTagValueNode($varTagValueNode); $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); diff --git a/tests/PHPStanStaticTypeMapper/TypeMapper/ArrayTypeMapperTest.php b/tests/PHPStanStaticTypeMapper/TypeMapper/ArrayTypeMapperTest.php deleted file mode 100644 index 47d3e5ff592..00000000000 --- a/tests/PHPStanStaticTypeMapper/TypeMapper/ArrayTypeMapperTest.php +++ /dev/null @@ -1,94 +0,0 @@ -arrayTypeMapper = $this->make(ArrayTypeMapper::class); - } - - #[DataProvider('provideDataWithoutKeys')] - #[DataProvider('provideDataUnionedWithoutKeys')] - public function testWithoutKeys(ArrayType $arrayType, string $expectedResult): void - { - $actualTypeNode = $this->arrayTypeMapper->mapToPHPStanPhpDocTypeNode($arrayType); - $this->assertSame($expectedResult, new Printer()->print($actualTypeNode)); - } - - #[DataProvider('provideDataWithKeys')] - public function testWithKeys(ArrayType $arrayType, string $expectedResult): void - { - $actualTypeNode = $this->arrayTypeMapper->mapToPHPStanPhpDocTypeNode($arrayType); - $this->assertSame($expectedResult, new Printer()->print($actualTypeNode)); - } - - /** - * @return Iterator - */ - public static function provideDataWithoutKeys(): Iterator - { - $arrayType = new ArrayType(new MixedType(), new StringType()); - yield [$arrayType, 'string[]']; - - $stringStringUnionType = new UnionType([new StringType(), new StringType()]); - $arrayType = new ArrayType(new MixedType(), $stringStringUnionType); - yield [$arrayType, 'string[]']; - } - - /** - * @return Iterator> - */ - public static function provideDataUnionedWithoutKeys(): Iterator - { - $stringAndIntegerUnionType = new UnionType([new StringType(), new IntegerType()]); - $unionArrayType = new ArrayType(new MixedType(), $stringAndIntegerUnionType); - yield [$unionArrayType, 'string[]|int[]']; - - $moreNestedUnionArrayType = new ArrayType(new MixedType(), $unionArrayType); - yield [$moreNestedUnionArrayType, 'string[][]|int[][]']; - - $evenMoreNestedUnionArrayType = new ArrayType(new MixedType(), $moreNestedUnionArrayType); - yield [$evenMoreNestedUnionArrayType, 'string[][][]|int[][][]']; - } - - /** - * @return Iterator> - */ - public static function provideDataWithKeys(): Iterator - { - $arrayMixedToStringType = new ArrayType(new MixedType(), new StringType()); - $arrayType = new ArrayType(new StringType(), $arrayMixedToStringType); - yield [$arrayType, 'array']; - - $stringAndIntegerUnionType = new UnionType([new StringType(), new IntegerType()]); - - $stringAndIntegerUnionArrayType = new ArrayType(new MixedType(), $stringAndIntegerUnionType); - $arrayType = new ArrayType(new StringType(), $stringAndIntegerUnionArrayType); - yield [$arrayType, 'array>']; - - $arrayType = new ArrayType(new StringType(), new IntegerType()); - yield [$arrayType, 'array']; - - $arrayType = new ArrayType(new StringType(), $stringAndIntegerUnionType); - yield [$arrayType, 'array']; - } -} diff --git a/tests/PHPStanStaticTypeMapper/TypeMapper/NonexistentParentClassTypeMapperTest.php b/tests/PHPStanStaticTypeMapper/TypeMapper/NonexistentParentClassTypeMapperTest.php deleted file mode 100644 index cd5c4100c1e..00000000000 --- a/tests/PHPStanStaticTypeMapper/TypeMapper/NonexistentParentClassTypeMapperTest.php +++ /dev/null @@ -1,40 +0,0 @@ -phpStanStaticTypeMapper = $this->make(PHPStanStaticTypeMapper::class); - } - - public function testMapToPhpParserNode(): void - { - $node = $this->phpStanStaticTypeMapper->mapToPhpParserNode( - new NonexistentParentClassType(), - TypeKind::RETURN - ); - - $this->assertInstanceOf(Name::class, $node); - $this->assertSame('parent', $node->toString()); - } - - public function testMapToPHPStanPhpDocTypeNode(): void - { - $typeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode(new NonexistentParentClassType()); - $this->assertSame('parent', (string) $typeNode); - } -} diff --git a/tests/PHPStanStaticTypeMapper/TypeMapperOrderTest.php b/tests/PHPStanStaticTypeMapper/TypeMapperOrderTest.php deleted file mode 100644 index 3a357eec35a..00000000000 --- a/tests/PHPStanStaticTypeMapper/TypeMapperOrderTest.php +++ /dev/null @@ -1,38 +0,0 @@ -phpStanStaticTypeMapper = $this->make(PHPStanStaticTypeMapper::class); - } - - /** - * The most specific mapper wins regardless of registration order: ClassStringType extends - * StringType, so the ClassStringType mapper must match it over the StringType one. - */ - public function testMostSpecificMapperWinsOverParent(): void - { - $typeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode(new ClassStringType()); - $this->assertSame('class-string', (string) $typeNode); - } - - public function testParentMapperStillMatchesParentType(): void - { - $typeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode(new StringType()); - $this->assertSame('string', (string) $typeNode); - } -}