Skip to content

Commit 0232ad5

Browse files
alekittonicolas-grekas
authored andcommitted
Generate code for never return type
1 parent 88dda72 commit 0232ad5

File tree

6 files changed

+135
-1
lines changed

6 files changed

+135
-1
lines changed

Diff for: 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
}

Diff for: tests/ProxyManagerTest/Functional/LazyLoadingGhostFunctionalTest.php

+42-1
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,12 @@
3232
use ProxyManagerTestAsset\ClassWithPublicStringTypedProperty;
3333
use ProxyManagerTestAsset\ClassWithSelfHint;
3434
use ProxyManagerTestAsset\EmptyClass;
35+
use ProxyManagerTestAsset\NeverCounter;
3536
use ProxyManagerTestAsset\OtherObjectAccessClass;
3637
use ProxyManagerTestAsset\VoidCounter;
3738
use ReflectionClass;
3839
use ReflectionProperty;
40+
use RuntimeException;
3941
use stdClass;
4042

4143
use function array_key_exists;
@@ -1547,7 +1549,46 @@ static function (
15471549
self::assertSame($initialCounter + $increment, $proxy->counter);
15481550
}
15491551

1550-
private static function isPropertyInitialized($object, ReflectionProperty $property): bool
1552+
/**
1553+
* @requires PHP 8.1
1554+
*
1555+
* @psalm-suppress UnusedVariable this method uses by-ref assignment of properties, and isn't recognized by static analysis
1556+
* @psalm-suppress UndefinedClass Class, interface or enum named never does not exist
1557+
*/
1558+
public function testWillExecuteLogicInANeverMethod(): void
1559+
{
1560+
$initialCounter = random_int(10, 1000);
1561+
1562+
$proxy = (new LazyLoadingGhostFactory())->createProxy(
1563+
NeverCounter::class,
1564+
static function (
1565+
LazyLoadingInterface $proxy,
1566+
string $method,
1567+
array $params,
1568+
?Closure & $initializer,
1569+
array $properties
1570+
) use ($initialCounter): bool {
1571+
$initializer = null;
1572+
1573+
$properties['counter'] = $initialCounter;
1574+
1575+
return true;
1576+
}
1577+
);
1578+
1579+
$increment = random_int(1001, 10000);
1580+
1581+
try {
1582+
$proxy->increment($increment);
1583+
$this->fail('RuntimeException expected');
1584+
} catch (RuntimeException $e) {
1585+
// Nothing to do
1586+
}
1587+
1588+
self::assertSame($initialCounter + $increment, $proxy->counter);
1589+
}
1590+
1591+
private static function isPropertyInitialized(object $object, ReflectionProperty $property): bool
15511592
{
15521593
return array_key_exists(
15531594
($property->isProtected() ? "\0*\0" : '')

Diff for: 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) {

Diff for: 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
*

Diff for: 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) {

Diff for: tests/ProxyManagerTestAsset/NeverCounter.php

+21
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)