From 12766be0e052a76120efc3d67a45877b768b6366 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Mon, 31 Jul 2023 19:58:46 +0700 Subject: [PATCH 1/5] [PHPStanStaticTypeMapper] Handle crash on DowngradeMixedTypeDeclarationRector for PHPStan\Type\ConditionalType --- .../Fixture/fixture.php.inc | 35 +++++++++++++++++++ .../IssueConditionalTypeTest.php | 28 +++++++++++++++ .../config/configured_rule.php | 10 ++++++ 3 files changed, 73 insertions(+) create mode 100644 tests/Issues/IssueConditionalType/Fixture/fixture.php.inc create mode 100644 tests/Issues/IssueConditionalType/IssueConditionalTypeTest.php create mode 100644 tests/Issues/IssueConditionalType/config/configured_rule.php diff --git a/tests/Issues/IssueConditionalType/Fixture/fixture.php.inc b/tests/Issues/IssueConditionalType/Fixture/fixture.php.inc new file mode 100644 index 00000000000..cbc1e595125 --- /dev/null +++ b/tests/Issues/IssueConditionalType/Fixture/fixture.php.inc @@ -0,0 +1,35 @@ +|scalar + * + * @return (TValue is scalar ? array|scalar : array) + */ + public function resolveValue(): mixed + { + } +} + +?> +----- +|scalar + * + * @return (TValue is scalar ? array|scalar : array) + */ + public function resolveValue() + { + } +} + +?> diff --git a/tests/Issues/IssueConditionalType/IssueConditionalTypeTest.php b/tests/Issues/IssueConditionalType/IssueConditionalTypeTest.php new file mode 100644 index 00000000000..1a1d09fe9f9 --- /dev/null +++ b/tests/Issues/IssueConditionalType/IssueConditionalTypeTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/IssueConditionalType/config/configured_rule.php b/tests/Issues/IssueConditionalType/config/configured_rule.php new file mode 100644 index 00000000000..dc84ae67c1c --- /dev/null +++ b/tests/Issues/IssueConditionalType/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(DowngradeMixedTypeDeclarationRector::class); +}; From 0fa84110d2248a3b294bf42bb701abd524bcafb9 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Mon, 31 Jul 2023 20:04:34 +0700 Subject: [PATCH 2/5] add ConditionalTypeMapper --- .../TypeMapper/ConditionalTypeMapper.php | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 packages/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeMapper.php diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeMapper.php new file mode 100644 index 00000000000..bef147c1900 --- /dev/null +++ b/packages/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeMapper.php @@ -0,0 +1,55 @@ + + */ +final class ConditionalTypeMapper implements TypeMapperInterface +{ + private PHPStanStaticTypeMapper $phpStanStaticTypeMapper; + + #[Required] + public function autowire(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void + { + $this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper; + } + + /** + * @return class-string + */ + public function getNodeClass(): string + { + return ConditionalType::class; + } + + /** + * @param ConditionalType $type + */ + public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode + { + 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); + } +} From 566ab5c6c34bde3e51115b3af63210b67a5fd44f Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Mon, 31 Jul 2023 20:23:07 +0700 Subject: [PATCH 3/5] update with TypeTraverser::map() --- .../TypeMapper/ConditionalTypeMapper.php | 11 +++++++++++ .../IssueConditionalType/Fixture/fixture.php.inc | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeMapper.php index bef147c1900..e972eabe40d 100644 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeMapper.php +++ b/packages/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeMapper.php @@ -4,6 +4,7 @@ namespace Rector\PHPStanStaticTypeMapper\TypeMapper; +use Nette\Utils\Strings; use PhpParser\Node; use PHPStan\PhpDocParser\Ast\Type\TypeNode; use PHPStan\Type\ConditionalType; @@ -13,6 +14,7 @@ use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper; use Symfony\Contracts\Service\Attribute\Required; +use PHPStan\Type\TypeTraverser; /** * @implements TypeMapperInterface @@ -40,6 +42,15 @@ public function getNodeClass(): string */ public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode { + $type = TypeTraverser::map($type, static function (Type $type, callable $traverse): Type { + if ($type instanceof \PHPStan\Type\ObjectType && $type->getClassReflection() === null) { + $newClassName = Strings::after($type->getClassName(), '\\', -1); + $type = new \PHPStan\Type\ObjectType($newClassName); + } + + return $traverse($type); + }); + return $type->toPhpDocNode(); } diff --git a/tests/Issues/IssueConditionalType/Fixture/fixture.php.inc b/tests/Issues/IssueConditionalType/Fixture/fixture.php.inc index cbc1e595125..664077f8aa2 100644 --- a/tests/Issues/IssueConditionalType/Fixture/fixture.php.inc +++ b/tests/Issues/IssueConditionalType/Fixture/fixture.php.inc @@ -25,7 +25,7 @@ class Fixture /** * @template TValue of array|scalar * - * @return (TValue is scalar ? array|scalar : array) + * @return (TValue is (bool | float | int | string) ? (array | bool | float | int | string) : array<(array | bool | float | int | string)>) */ public function resolveValue() { From 3ac61b5bfb5f65a9216c7ce12f6507695138e591 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 31 Jul 2023 13:24:49 +0000 Subject: [PATCH 4/5] [ci-review] Rector Rectify --- .../TypeMapper/ConditionalTypeMapper.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeMapper.php index e972eabe40d..c9be5dc79d6 100644 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeMapper.php +++ b/packages/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeMapper.php @@ -4,6 +4,8 @@ namespace Rector\PHPStanStaticTypeMapper\TypeMapper; +use PHPStan\Type\ObjectType; +use PHPStan\Reflection\ClassReflection; use Nette\Utils\Strings; use PhpParser\Node; use PHPStan\PhpDocParser\Ast\Type\TypeNode; @@ -43,9 +45,9 @@ public function getNodeClass(): string public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode { $type = TypeTraverser::map($type, static function (Type $type, callable $traverse): Type { - if ($type instanceof \PHPStan\Type\ObjectType && $type->getClassReflection() === null) { + if ($type instanceof ObjectType && !$type->getClassReflection() instanceof ClassReflection) { $newClassName = Strings::after($type->getClassName(), '\\', -1); - $type = new \PHPStan\Type\ObjectType($newClassName); + $type = new ObjectType($newClassName); } return $traverse($type); From 199fb802f4679a3c568db3ec7c631287d2360b4d Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Mon, 31 Jul 2023 20:27:54 +0700 Subject: [PATCH 5/5] fix phpstan --- .../TypeMapper/ConditionalTypeMapper.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeMapper.php b/packages/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeMapper.php index c9be5dc79d6..3a9c9d547ec 100644 --- a/packages/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeMapper.php +++ b/packages/PHPStanStaticTypeMapper/TypeMapper/ConditionalTypeMapper.php @@ -46,8 +46,8 @@ 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 = Strings::after($type->getClassName(), '\\', -1); - $type = new ObjectType($newClassName); + $newClassName = (string) Strings::after($type->getClassName(), '\\', -1); + return $traverse(new ObjectType($newClassName)); } return $traverse($type);