From d1eadf86d5b843c496614ef075a8f655a880f1d8 Mon Sep 17 00:00:00 2001 From: Alessandro Chitolina Date: Fri, 18 Feb 2022 22:29:01 +0100 Subject: [PATCH] Generate code for never return type --- .../Util/ProxiedMethodReturnExpression.php | 4 ++ .../LazyLoadingGhostFunctionalTest.php | 41 +++++++++++++++++++ .../LazyLoadingValueHolderFunctionalTest.php | 26 ++++++++++++ .../Functional/NullObjectFunctionalTest.php | 16 ++++++++ .../Functional/RemoteObjectFunctionalTest.php | 26 ++++++++++++ tests/ProxyManagerTestAsset/NeverCounter.php | 21 ++++++++++ 6 files changed, 134 insertions(+) create mode 100644 tests/ProxyManagerTestAsset/NeverCounter.php diff --git a/src/ProxyManager/Generator/Util/ProxiedMethodReturnExpression.php b/src/ProxyManager/Generator/Util/ProxiedMethodReturnExpression.php index f931e2dc..481a1ab2 100644 --- a/src/ProxyManager/Generator/Util/ProxiedMethodReturnExpression.php +++ b/src/ProxyManager/Generator/Util/ProxiedMethodReturnExpression.php @@ -24,6 +24,10 @@ public static function generate(string $returnedValueExpression, ?ReflectionMeth return $returnedValueExpression . ";\nreturn;"; } + if ($originalReturnType instanceof ReflectionNamedType && $originalReturnType->getName() === 'never') { + return $returnedValueExpression . ';'; + } + return 'return ' . $returnedValueExpression . ';'; } } diff --git a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php index 357c6ca0..7864ea46 100644 --- a/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php +++ b/tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php @@ -33,10 +33,12 @@ use ProxyManagerTestAsset\ClassWithReadOnlyProperties; use ProxyManagerTestAsset\ClassWithSelfHint; use ProxyManagerTestAsset\EmptyClass; +use ProxyManagerTestAsset\NeverCounter; use ProxyManagerTestAsset\OtherObjectAccessClass; use ProxyManagerTestAsset\VoidCounter; use ReflectionClass; use ReflectionProperty; +use RuntimeException; use stdClass; use function array_key_exists; @@ -1586,6 +1588,45 @@ static function ( self::assertSame($initialCounter + $increment, $proxy->counter); } + /** + * @requires PHP 8.1 + * + * @psalm-suppress UnusedVariable this method uses by-ref assignment of properties, and isn't recognized by static analysis + * @psalm-suppress UndefinedClass Class, interface or enum named never does not exist + */ + public function testWillExecuteLogicInANeverMethod(): void + { + $initialCounter = random_int(10, 1000); + + $proxy = (new LazyLoadingGhostFactory())->createProxy( + NeverCounter::class, + static function ( + LazyLoadingInterface $proxy, + string $method, + array $params, + ?Closure & $initializer, + array $properties + ) use ($initialCounter): bool { + $initializer = null; + + $properties['counter'] = $initialCounter; + + return true; + } + ); + + $increment = random_int(1001, 10000); + + try { + $proxy->increment($increment); + $this->fail('RuntimeException expected'); + } catch (RuntimeException $e) { + // Nothing to do + } + + self::assertSame($initialCounter + $increment, $proxy->counter); + } + private static function isPropertyInitialized($object, ReflectionProperty $property): bool { return array_key_exists( diff --git a/tests/ProxyManagerTest/Functional/LazyLoadingValueHolderFunctionalTest.php b/tests/ProxyManagerTest/Functional/LazyLoadingValueHolderFunctionalTest.php index 0bee6630..a840c205 100644 --- a/tests/ProxyManagerTest/Functional/LazyLoadingValueHolderFunctionalTest.php +++ b/tests/ProxyManagerTest/Functional/LazyLoadingValueHolderFunctionalTest.php @@ -27,10 +27,12 @@ use ProxyManagerTestAsset\ClassWithPublicStringTypedProperty; use ProxyManagerTestAsset\ClassWithSelfHint; use ProxyManagerTestAsset\EmptyClass; +use ProxyManagerTestAsset\NeverCounter; use ProxyManagerTestAsset\OtherObjectAccessClass; use ProxyManagerTestAsset\VoidCounter; use ReflectionClass; use ReflectionProperty; +use RuntimeException; use stdClass; use function array_values; @@ -695,6 +697,30 @@ public function testWillExecuteLogicInAVoidMethod(): void self::assertSame($increment, $proxy->counter); } + /** + * @requires PHP 8.1 + * + * @psalm-suppress UndefinedClass Class, interface or enum named never does not exist + */ + public function testWillExecuteLogicInANeverMethod(): void + { + $proxy = (new LazyLoadingValueHolderFactory())->createProxy( + NeverCounter::class, + $this->createInitializer(NeverCounter::class, new NeverCounter()) + ); + + $increment = random_int(100, 1000); + + try { + $proxy->increment($increment); + $this->fail('RuntimeException expected'); + } catch (RuntimeException $e) { + // Do nothing + } + + self::assertSame($increment, $proxy->counter); + } + public function getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope(): Generator { foreach ((new ReflectionClass(OtherObjectAccessClass::class))->getProperties() as $property) { diff --git a/tests/ProxyManagerTest/Functional/NullObjectFunctionalTest.php b/tests/ProxyManagerTest/Functional/NullObjectFunctionalTest.php index b1b860c7..bcf37bde 100644 --- a/tests/ProxyManagerTest/Functional/NullObjectFunctionalTest.php +++ b/tests/ProxyManagerTest/Functional/NullObjectFunctionalTest.php @@ -18,9 +18,11 @@ use ProxyManagerTestAsset\ClassWithPublicStringTypedProperty; use ProxyManagerTestAsset\ClassWithSelfHint; use ProxyManagerTestAsset\EmptyClass; +use ProxyManagerTestAsset\NeverCounter; use ProxyManagerTestAsset\VoidCounter; use ReflectionProperty; use stdClass; +use TypeError; use function array_values; use function assert; @@ -126,6 +128,20 @@ public function testPropertyUnset(NullObjectInterface $proxy, string $publicProp self::assertFalse(isset($proxy->$publicProperty)); } + /** + * @requires PHP 8.1 + */ + public function testNeverReturningMethodCalls(): void + { + $proxy = (new NullObjectFactory())->createProxy(NeverCounter::class); + $method = [$proxy, 'increment']; + + self::assertIsCallable($method); + + $this->expectException(TypeError::class); + $method(random_int(10, 1000)); + } + /** * Generates a list of object | invoked method | parameters | expected result * diff --git a/tests/ProxyManagerTest/Functional/RemoteObjectFunctionalTest.php b/tests/ProxyManagerTest/Functional/RemoteObjectFunctionalTest.php index f277784f..d3fb41dc 100644 --- a/tests/ProxyManagerTest/Functional/RemoteObjectFunctionalTest.php +++ b/tests/ProxyManagerTest/Functional/RemoteObjectFunctionalTest.php @@ -15,6 +15,7 @@ use ProxyManagerTestAsset\ClassWithPublicStringNullableTypedProperty; use ProxyManagerTestAsset\ClassWithPublicStringTypedProperty; use ProxyManagerTestAsset\ClassWithSelfHint; +use ProxyManagerTestAsset\NeverCounter; use ProxyManagerTestAsset\OtherObjectAccessClass; use ProxyManagerTestAsset\RemoteProxy\BazServiceInterface; use ProxyManagerTestAsset\RemoteProxy\Foo; @@ -26,6 +27,7 @@ use ProxyManagerTestAsset\RemoteProxy\VariadicArgumentsServiceInterface; use ProxyManagerTestAsset\VoidCounter; use ReflectionClass; +use RuntimeException; use stdClass; use function assert; @@ -403,6 +405,30 @@ public function testWillExecuteLogicInAVoidMethod(): void $proxy->increment($increment); } + /** + * @requires PHP 8.1 + */ + public function testWillExecuteLogicInANeverMethod(): void + { + $adapter = $this->createMock(AdapterInterface::class); + + $increment = random_int(10, 1000); + + $adapter + ->expects(self::once()) + ->method('call') + ->with(NeverCounter::class, 'increment', [$increment]) + ->willThrowException(new RuntimeException()); + + $proxy = clone (new RemoteObjectFactory($adapter)) + ->createProxy(NeverCounter::class); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage(''); + + $proxy->increment($increment); + } + public function getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope(): Generator { foreach ((new ReflectionClass(OtherObjectAccessClass::class))->getProperties() as $property) { diff --git a/tests/ProxyManagerTestAsset/NeverCounter.php b/tests/ProxyManagerTestAsset/NeverCounter.php new file mode 100644 index 00000000..ca94f66c --- /dev/null +++ b/tests/ProxyManagerTestAsset/NeverCounter.php @@ -0,0 +1,21 @@ +counter += $amount; + throw new RuntimeException(); + } +}