Skip to content

Commit d1eadf8

Browse files
alekittonicolas-grekas
authored andcommitted
Generate code for never return type
1 parent bf56e3a commit d1eadf8

File tree

6 files changed

+134
-0
lines changed

6 files changed

+134
-0
lines changed

src/ProxyManager/Generator/Util/ProxiedMethodReturnExpression.php

+4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ public static function generate(string $returnedValueExpression, ?ReflectionMeth
2424
return $returnedValueExpression . ";\nreturn;";
2525
}
2626

27+
if ($originalReturnType instanceof ReflectionNamedType && $originalReturnType->getName() === 'never') {
28+
return $returnedValueExpression . ';';
29+
}
30+
2731
return 'return ' . $returnedValueExpression . ';';
2832
}
2933
}

tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php

+41
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,12 @@
3333
use ProxyManagerTestAsset\ClassWithReadOnlyProperties;
3434
use ProxyManagerTestAsset\ClassWithSelfHint;
3535
use ProxyManagerTestAsset\EmptyClass;
36+
use ProxyManagerTestAsset\NeverCounter;
3637
use ProxyManagerTestAsset\OtherObjectAccessClass;
3738
use ProxyManagerTestAsset\VoidCounter;
3839
use ReflectionClass;
3940
use ReflectionProperty;
41+
use RuntimeException;
4042
use stdClass;
4143

4244
use function array_key_exists;
@@ -1586,6 +1588,45 @@ static function (
15861588
self::assertSame($initialCounter + $increment, $proxy->counter);
15871589
}
15881590

1591+
/**
1592+
* @requires PHP 8.1
1593+
*
1594+
* @psalm-suppress UnusedVariable this method uses by-ref assignment of properties, and isn't recognized by static analysis
1595+
* @psalm-suppress UndefinedClass Class, interface or enum named never does not exist
1596+
*/
1597+
public function testWillExecuteLogicInANeverMethod(): void
1598+
{
1599+
$initialCounter = random_int(10, 1000);
1600+
1601+
$proxy = (new LazyLoadingGhostFactory())->createProxy(
1602+
NeverCounter::class,
1603+
static function (
1604+
LazyLoadingInterface $proxy,
1605+
string $method,
1606+
array $params,
1607+
?Closure & $initializer,
1608+
array $properties
1609+
) use ($initialCounter): bool {
1610+
$initializer = null;
1611+
1612+
$properties['counter'] = $initialCounter;
1613+
1614+
return true;
1615+
}
1616+
);
1617+
1618+
$increment = random_int(1001, 10000);
1619+
1620+
try {
1621+
$proxy->increment($increment);
1622+
$this->fail('RuntimeException expected');
1623+
} catch (RuntimeException $e) {
1624+
// Nothing to do
1625+
}
1626+
1627+
self::assertSame($initialCounter + $increment, $proxy->counter);
1628+
}
1629+
15891630
private static function isPropertyInitialized($object, ReflectionProperty $property): bool
15901631
{
15911632
return array_key_exists(

tests/ProxyManagerTest/Functional/LazyLoadingValueHolderFunctionalTest.php

+26
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@
2727
use ProxyManagerTestAsset\ClassWithPublicStringTypedProperty;
2828
use ProxyManagerTestAsset\ClassWithSelfHint;
2929
use ProxyManagerTestAsset\EmptyClass;
30+
use ProxyManagerTestAsset\NeverCounter;
3031
use ProxyManagerTestAsset\OtherObjectAccessClass;
3132
use ProxyManagerTestAsset\VoidCounter;
3233
use ReflectionClass;
3334
use ReflectionProperty;
35+
use RuntimeException;
3436
use stdClass;
3537

3638
use function array_values;
@@ -695,6 +697,30 @@ public function testWillExecuteLogicInAVoidMethod(): void
695697
self::assertSame($increment, $proxy->counter);
696698
}
697699

700+
/**
701+
* @requires PHP 8.1
702+
*
703+
* @psalm-suppress UndefinedClass Class, interface or enum named never does not exist
704+
*/
705+
public function testWillExecuteLogicInANeverMethod(): void
706+
{
707+
$proxy = (new LazyLoadingValueHolderFactory())->createProxy(
708+
NeverCounter::class,
709+
$this->createInitializer(NeverCounter::class, new NeverCounter())
710+
);
711+
712+
$increment = random_int(100, 1000);
713+
714+
try {
715+
$proxy->increment($increment);
716+
$this->fail('RuntimeException expected');
717+
} catch (RuntimeException $e) {
718+
// Do nothing
719+
}
720+
721+
self::assertSame($increment, $proxy->counter);
722+
}
723+
698724
public function getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope(): Generator
699725
{
700726
foreach ((new ReflectionClass(OtherObjectAccessClass::class))->getProperties() as $property) {

tests/ProxyManagerTest/Functional/NullObjectFunctionalTest.php

+16
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
use ProxyManagerTestAsset\ClassWithPublicStringTypedProperty;
1919
use ProxyManagerTestAsset\ClassWithSelfHint;
2020
use ProxyManagerTestAsset\EmptyClass;
21+
use ProxyManagerTestAsset\NeverCounter;
2122
use ProxyManagerTestAsset\VoidCounter;
2223
use ReflectionProperty;
2324
use stdClass;
25+
use TypeError;
2426

2527
use function array_values;
2628
use function assert;
@@ -126,6 +128,20 @@ public function testPropertyUnset(NullObjectInterface $proxy, string $publicProp
126128
self::assertFalse(isset($proxy->$publicProperty));
127129
}
128130

131+
/**
132+
* @requires PHP 8.1
133+
*/
134+
public function testNeverReturningMethodCalls(): void
135+
{
136+
$proxy = (new NullObjectFactory())->createProxy(NeverCounter::class);
137+
$method = [$proxy, 'increment'];
138+
139+
self::assertIsCallable($method);
140+
141+
$this->expectException(TypeError::class);
142+
$method(random_int(10, 1000));
143+
}
144+
129145
/**
130146
* Generates a list of object | invoked method | parameters | expected result
131147
*

tests/ProxyManagerTest/Functional/RemoteObjectFunctionalTest.php

+26
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use ProxyManagerTestAsset\ClassWithPublicStringNullableTypedProperty;
1616
use ProxyManagerTestAsset\ClassWithPublicStringTypedProperty;
1717
use ProxyManagerTestAsset\ClassWithSelfHint;
18+
use ProxyManagerTestAsset\NeverCounter;
1819
use ProxyManagerTestAsset\OtherObjectAccessClass;
1920
use ProxyManagerTestAsset\RemoteProxy\BazServiceInterface;
2021
use ProxyManagerTestAsset\RemoteProxy\Foo;
@@ -26,6 +27,7 @@
2627
use ProxyManagerTestAsset\RemoteProxy\VariadicArgumentsServiceInterface;
2728
use ProxyManagerTestAsset\VoidCounter;
2829
use ReflectionClass;
30+
use RuntimeException;
2931
use stdClass;
3032

3133
use function assert;
@@ -403,6 +405,30 @@ public function testWillExecuteLogicInAVoidMethod(): void
403405
$proxy->increment($increment);
404406
}
405407

408+
/**
409+
* @requires PHP 8.1
410+
*/
411+
public function testWillExecuteLogicInANeverMethod(): void
412+
{
413+
$adapter = $this->createMock(AdapterInterface::class);
414+
415+
$increment = random_int(10, 1000);
416+
417+
$adapter
418+
->expects(self::once())
419+
->method('call')
420+
->with(NeverCounter::class, 'increment', [$increment])
421+
->willThrowException(new RuntimeException());
422+
423+
$proxy = clone (new RemoteObjectFactory($adapter))
424+
->createProxy(NeverCounter::class);
425+
426+
$this->expectException(RuntimeException::class);
427+
$this->expectExceptionMessage('');
428+
429+
$proxy->increment($increment);
430+
}
431+
406432
public function getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope(): Generator
407433
{
408434
foreach ((new ReflectionClass(OtherObjectAccessClass::class))->getProperties() as $property) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ProxyManagerTestAsset;
6+
7+
use RuntimeException;
8+
9+
class NeverCounter
10+
{
11+
/**
12+
* @var int
13+
*/
14+
public $counter = 0;
15+
16+
public function increment(int $amount) : never
17+
{
18+
$this->counter += $amount;
19+
throw new RuntimeException();
20+
}
21+
}

0 commit comments

Comments
 (0)