Skip to content

Commit

Permalink
Implemented ReflectionMethod/ReflectionFunction::hasTentativeReturnTy…
Browse files Browse the repository at this point in the history
…pe() and getTentativeReturnType()
  • Loading branch information
kukulich committed Oct 13, 2021
1 parent 77c876f commit b15b243
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 10 deletions.
4 changes: 2 additions & 2 deletions src/Reflection/Adapter/ReflectionFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -216,12 +216,12 @@ public function getAttributes(?string $name = null, int $flags = 0): array

public function hasTentativeReturnType(): bool
{
throw new Exception\NotImplemented('Not implemented');
return $this->betterReflectionFunction->hasTentativeReturnType();
}

public function getTentativeReturnType(): ?CoreReflectionType
{
throw new Exception\NotImplemented('Not implemented');
return ReflectionType::fromTypeOrNull($this->betterReflectionFunction->getTentativeReturnType());
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/Reflection/Adapter/ReflectionMethod.php
Original file line number Diff line number Diff line change
Expand Up @@ -302,12 +302,12 @@ public function getAttributes(?string $name = null, int $flags = 0): array

public function hasTentativeReturnType(): bool
{
throw new Exception\NotImplemented('Not implemented');
return $this->betterReflectionMethod->hasTentativeReturnType();
}

public function getTentativeReturnType(): ?CoreReflectionType
{
throw new Exception\NotImplemented('Not implemented');
return ReflectionType::fromTypeOrNull($this->betterReflectionMethod->getTentativeReturnType());
}

/**
Expand Down
18 changes: 18 additions & 0 deletions src/Reflection/ReflectionFunctionAbstract.php
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,24 @@ public function hasReturnType(): bool
return $this->getReturnType() !== null;
}

public function hasTentativeReturnType(): bool
{
if ($this->isUserDefined()) {
return false;
}

return $this->hasReturnType();
}

public function getTentativeReturnType(): ReflectionNamedType|ReflectionUnionType|null
{
if ($this->isUserDefined()) {
return null;
}

return $this->getReturnType();
}

/**
* Set the return type declaration.
*/
Expand Down
9 changes: 9 additions & 0 deletions src/SourceLocator/SourceStubber/ReflectionSourceStubber.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
use function get_defined_constants;
use function in_array;
use function interface_exists;
use function method_exists;
use function trait_exists;

/**
Expand Down Expand Up @@ -116,6 +117,10 @@ public function generateFunctionStub(string $functionName): ?StubData
$this->addParameters($functionNode, $functionReflection);

$returnType = $functionReflection->getReturnType();
if ($returnType === null && method_exists($functionReflection, 'getTentativeReturnType')) {
$returnType = $functionReflection->getTentativeReturnType();
}

assert($returnType instanceof CoreReflectionNamedType || $returnType instanceof CoreReflectionUnionType || $returnType === null);

if ($returnType !== null) {
Expand Down Expand Up @@ -379,6 +384,10 @@ private function addMethods(Class_|Interface_|Trait_ $classNode, CoreReflectionC
$this->addParameters($methodNode, $methodReflection);

$returnType = $methodReflection->getReturnType();
if ($returnType === null && method_exists($methodReflection, 'getTentativeReturnType')) {
$returnType = $methodReflection->getTentativeReturnType();
}

assert($returnType instanceof CoreReflectionNamedType || $returnType instanceof CoreReflectionUnionType || $returnType === null);

if ($returnType !== null) {
Expand Down
4 changes: 2 additions & 2 deletions test/unit/Reflection/Adapter/ReflectionFunctionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ public function methodExpectationProvider(): array
['isGenerator', null, true, []],
['isVariadic', null, true, []],
['getAttributes', NotImplemented::class, null, []],
['hasTentativeReturnType', NotImplemented::class, null, []],
['getTentativeReturnType', NotImplemented::class, null, []],
['hasTentativeReturnType', null, false, []],
['getTentativeReturnType', null, null, []],
['getClosureUsedVariables', NotImplemented::class, null, []],

// ReflectionFunction
Expand Down
4 changes: 2 additions & 2 deletions test/unit/Reflection/Adapter/ReflectionMethodTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ public function methodExpectationProvider(): array
['isGenerator', null, true, []],
['isVariadic', null, true, []],
['getAttributes', NotImplemented::class, null, []],
['hasTentativeReturnType', NotImplemented::class, null, []],
['getTentativeReturnType', NotImplemented::class, null, []],
['hasTentativeReturnType', null, false, []],
['getTentativeReturnType', null, null, []],
['getClosureUsedVariables', NotImplemented::class, null, []],

// ReflectionMethod
Expand Down
46 changes: 46 additions & 0 deletions test/unit/Reflection/ReflectionFunctionAbstractTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use PhpParser\Parser;
use PhpParser\PrettyPrinter\Standard as StandardPrettyPrinter;
use PHPUnit\Framework\TestCase;
use ReflectionClass as CoreReflectionClass;
use Roave\BetterReflection\Reflection\Exception\InvalidArrowFunctionBodyNode;
use Roave\BetterReflection\Reflection\Exception\Uncloneable;
use Roave\BetterReflection\Reflection\ReflectionFunction;
Expand All @@ -24,7 +25,9 @@
use Roave\BetterReflection\Reflector\DefaultReflector;
use Roave\BetterReflection\SourceLocator\Ast\Locator;
use Roave\BetterReflection\SourceLocator\Located\LocatedSource;
use Roave\BetterReflection\SourceLocator\SourceStubber\ReflectionSourceStubber;
use Roave\BetterReflection\SourceLocator\Type\ClosureSourceLocator;
use Roave\BetterReflection\SourceLocator\Type\PhpInternalSourceLocator;
use Roave\BetterReflection\SourceLocator\Type\SingleFileSourceLocator;
use Roave\BetterReflection\SourceLocator\Type\StringSourceLocator;
use Roave\BetterReflectionTest\BetterReflectionSingleton;
Expand Down Expand Up @@ -501,6 +504,49 @@ public function testRemoveReturnType(): void
self::assertStringNotContainsString(': string', (new StandardPrettyPrinter())->prettyPrint([$functionInfo->getAst()]));
}

/**
* @requires PHP >= 8.1
*/
public function testHasTentativeReturnType(): void
{
$classInfo = (new DefaultReflector(new PhpInternalSourceLocator($this->astLocator, new ReflectionSourceStubber())))->reflectClass(CoreReflectionClass::class);
$methodInfo = $classInfo->getMethod('getName');

self::assertTrue($methodInfo->hasTentativeReturnType());
}

public function testHasNotTentativeReturnType(): void
{
$functionInfo = (new DefaultReflector(
new SingleFileSourceLocator(__DIR__ . '/../Fixture/Php7ReturnTypeDeclarations.php', $this->astLocator),
))->reflectFunction('returnsString');

self::assertFalse($functionInfo->hasTentativeReturnType());
}

/**
* @requires PHP >= 8.1
*/
public function testGetTentativeReturnType(): void
{
$classInfo = (new DefaultReflector(new PhpInternalSourceLocator($this->astLocator, new ReflectionSourceStubber())))->reflectClass(CoreReflectionClass::class);
$methodInfo = $classInfo->getMethod('getName');

$returnType = $methodInfo->getTentativeReturnType();

self::assertNotNull($returnType);
self::assertSame('string', $returnType->__toString());
}

public function testNoTentativeReturnType(): void
{
$functionInfo = (new DefaultReflector(
new SingleFileSourceLocator(__DIR__ . '/../Fixture/Php7ReturnTypeDeclarations.php', $this->astLocator),
))->reflectFunction('returnsString');

self::assertNull($functionInfo->getTentativeReturnType());
}

public function testCannotClone(): void
{
$php = '<?php function foo() {}';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
use function get_declared_traits;
use function get_defined_functions;
use function in_array;
use function method_exists;
use function sort;

/**
Expand Down Expand Up @@ -307,7 +308,13 @@ private function assertSameMethodAttributes(CoreReflectionMethod $original, Refl
self::assertSame($original->isAbstract(), $stubbed->isAbstract());
self::assertSame($original->isDeprecated(), $stubbed->isDeprecated());

self::assertSame((string) $original->getReturnType(), (string) $stubbed->getReturnType());
if (method_exists($original, 'hasTentativeReturnType') && $original->hasTentativeReturnType()) {
self::assertSame($original->hasTentativeReturnType(), $stubbed->hasTentativeReturnType());
self::assertSame((string) $original->getTentativeReturnType(), (string) $stubbed->getTentativeReturnType());
} else {
self::assertSame($original->hasReturnType(), $stubbed->hasReturnType());
self::assertSame((string) $original->getReturnType(), (string) $stubbed->getReturnType());
}
}

private function assertSameParameterAttributes(
Expand Down Expand Up @@ -377,7 +384,13 @@ public function testInternalFunctionsReturnType(string $functionName): void
$stubbedReflection = $this->reflector->reflectFunction($functionName);
$originalReflection = new CoreReflectionFunction($functionName);

self::assertSame((string) $originalReflection->getReturnType(), (string) $stubbedReflection->getReturnType());
if (method_exists($originalReflection, 'hasTentativeReturnType') && $originalReflection->hasTentativeReturnType()) {
self::assertSame($originalReflection->hasTentativeReturnType(), $stubbedReflection->hasTentativeReturnType());
self::assertSame((string) $originalReflection->getTentativeReturnType(), (string) $stubbedReflection->getTentativeReturnType());
} else {
self::assertSame($originalReflection->hasReturnType(), $stubbedReflection->hasReturnType());
self::assertSame((string) $originalReflection->getReturnType(), (string) $stubbedReflection->getReturnType());
}
}

public function testFunctionWithParameterPassedByReference(): void
Expand Down

0 comments on commit b15b243

Please sign in to comment.