From a4c489ee0914233600f93379edee4932ce2d4ee4 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Wed, 5 Feb 2025 11:37:04 +0700 Subject: [PATCH] [TypeDeclaration] Allow union with closure type on property on TypedPropertyFromAssignsRector (#6717) --- .../skip_union_callable.php.inc | 17 ++++++++ .../FixtureComplexTypes/union_closure.php.inc | 41 +++++++++++++++++++ src/NodeAnalyzer/PropertyAnalyzer.php | 2 +- .../TypeMapper/ClosureTypeMapper.php | 17 +++++--- .../TypeMapper/UnionTypeMapper.php | 4 +- 5 files changed, 73 insertions(+), 8 deletions(-) create mode 100644 rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/skip_union_callable.php.inc create mode 100644 rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/union_closure.php.inc diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/skip_union_callable.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/skip_union_callable.php.inc new file mode 100644 index 00000000000..71dd84c27fe --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/skip_union_callable.php.inc @@ -0,0 +1,17 @@ +property = $prop; + } else { + $this->property = false; + } + } +} diff --git a/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/union_closure.php.inc b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/union_closure.php.inc new file mode 100644 index 00000000000..1a7f41156bb --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector/FixtureComplexTypes/union_closure.php.inc @@ -0,0 +1,41 @@ +property = function (): void { + }; + } else { + $this->property = false; + } + } +} + +?> +----- +property = function (): void { + }; + } else { + $this->property = false; + } + } +} + +?> diff --git a/src/NodeAnalyzer/PropertyAnalyzer.php b/src/NodeAnalyzer/PropertyAnalyzer.php index b57791347e0..ecbf9dfec96 100644 --- a/src/NodeAnalyzer/PropertyAnalyzer.php +++ b/src/NodeAnalyzer/PropertyAnalyzer.php @@ -43,7 +43,7 @@ public function hasForbiddenType(Property $property): bool return false; } - private function isForbiddenType(Type $type): bool + public function isForbiddenType(Type $type): bool { if ($type instanceof NonExistingObjectType) { return true; diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/ClosureTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/ClosureTypeMapper.php index 3ec4f97d626..a5f08c244ec 100644 --- a/src/PHPStanStaticTypeMapper/TypeMapper/ClosureTypeMapper.php +++ b/src/PHPStanStaticTypeMapper/TypeMapper/ClosureTypeMapper.php @@ -80,15 +80,20 @@ public function mapToPhpParserNode(Type $type, string $typeKind): ?Node return new FullyQualified('Closure'); } - if ($typeKind !== TypeKind::RETURN) { - return null; + // ref https://3v4l.org/nUreN#v7.0.0 + if ($typeKind === TypeKind::RETURN && $this->phpVersionProvider->isAtLeastPhpVersion( + PhpVersionFeature::ANONYMOUS_FUNCTION_RETURN_TYPE + )) { + return new FullyQualified('Closure'); } - // ref https://3v4l.org/nUreN#v7.0.0 - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::ANONYMOUS_FUNCTION_RETURN_TYPE)) { - return null; + // ref https://3v4l.org/ruh5g#v8.0.0 + if ($typeKind === TypeKind::UNION && $this->phpVersionProvider->isAtLeastPhpVersion( + PhpVersionFeature::UNION_TYPES + )) { + return new FullyQualified('Closure'); } - return new FullyQualified('Closure'); + return null; } } diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/UnionTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/UnionTypeMapper.php index 3248ca7b828..339745d66ef 100644 --- a/src/PHPStanStaticTypeMapper/TypeMapper/UnionTypeMapper.php +++ b/src/PHPStanStaticTypeMapper/TypeMapper/UnionTypeMapper.php @@ -16,6 +16,7 @@ use PHPStan\Type\Type; use PHPStan\Type\UnionType; use Rector\BetterPhpDocParser\ValueObject\Type\BracketsAwareUnionTypeNode; +use Rector\NodeAnalyzer\PropertyAnalyzer; use Rector\Php\PhpVersionProvider; use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface; use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; @@ -33,6 +34,7 @@ final class UnionTypeMapper implements TypeMapperInterface public function __construct( private readonly PhpVersionProvider $phpVersionProvider, + private readonly PropertyAnalyzer $propertyAnalyzer ) { } @@ -184,7 +186,7 @@ private function matchPhpParserUnionType(UnionType $unionType, string $typeKind) } // special callable type only not allowed on property - if ($typeKind === TypeKind::PROPERTY && $unionedType->isCallable()->yes()) { + if ($typeKind === TypeKind::PROPERTY && $this->propertyAnalyzer->isForbiddenType($unionedType)) { return null; }