Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Reflection/Annotations/AnnotationMethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,9 @@ public function getSelfOutType(): ?Type
return null;
}

public function returnsByReference(): TrinaryLogic
{
return TrinaryLogic::createMaybe();
}

}
5 changes: 5 additions & 0 deletions src/Reflection/Dummy/ChangedTypeMethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,9 @@ public function getSelfOutType(): ?Type
return $this->reflection->getSelfOutType();
}

public function returnsByReference(): TrinaryLogic
{
return $this->reflection->returnsByReference();
}

}
5 changes: 5 additions & 0 deletions src/Reflection/Dummy/DummyMethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,9 @@ public function getSelfOutType(): ?Type
return null;
}

public function returnsByReference(): TrinaryLogic
{
return TrinaryLogic::createMaybe();
}

}
3 changes: 3 additions & 0 deletions src/Reflection/ExtendedMethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace PHPStan\Reflection;

use PHPStan\TrinaryLogic;
use PHPStan\Type\Type;

/**
Expand All @@ -25,4 +26,6 @@ public function getAsserts(): Assertions;

public function getSelfOutType(): ?Type;

public function returnsByReference(): TrinaryLogic;

}
2 changes: 2 additions & 0 deletions src/Reflection/FunctionReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,6 @@ public function getAsserts(): Assertions;

public function getDocComment(): ?string;

public function returnsByReference(): TrinaryLogic;

}
9 changes: 9 additions & 0 deletions src/Reflection/Native/NativeFunctionReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ class NativeFunctionReflection implements FunctionReflection

private Assertions $assertions;

private TrinaryLogic $returnsByReference;

/**
* @param ParametersAcceptorWithPhpDocs[] $variants
*/
Expand All @@ -25,9 +27,11 @@ public function __construct(
private bool $isDeprecated,
?Assertions $assertions = null,
private ?string $phpDocComment = null,
?TrinaryLogic $returnsByReference = null,
)
{
$this->assertions = $assertions ?? Assertions::createEmpty();
$this->returnsByReference = $returnsByReference ?? TrinaryLogic::createMaybe();
}

public function getName(): string
Expand Down Expand Up @@ -108,4 +112,9 @@ public function getDocComment(): ?string
return $this->phpDocComment;
}

public function returnsByReference(): TrinaryLogic
{
return $this->returnsByReference;
}

}
5 changes: 5 additions & 0 deletions src/Reflection/Native/NativeMethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -167,4 +167,9 @@ public function getSelfOutType(): ?Type
return $this->selfOutType;
}

public function returnsByReference(): TrinaryLogic
{
return $this->reflection->returnsByReference();
}

}
2 changes: 2 additions & 0 deletions src/Reflection/Php/BuiltinMethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,6 @@ public function isInternal(): bool;

public function isAbstract(): bool;

public function returnsByReference(): TrinaryLogic;

}
5 changes: 5 additions & 0 deletions src/Reflection/Php/ClosureCallMethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,9 @@ public function getSelfOutType(): ?Type
return $this->nativeMethodReflection->getSelfOutType();
}

public function returnsByReference(): TrinaryLogic
{
return $this->nativeMethodReflection->returnsByReference();
}

}
5 changes: 5 additions & 0 deletions src/Reflection/Php/EnumCasesMethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,9 @@ public function getSelfOutType(): ?Type
return null;
}

public function returnsByReference(): TrinaryLogic
{
return TrinaryLogic::createNo();
}

}
5 changes: 5 additions & 0 deletions src/Reflection/Php/FakeBuiltinMethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,9 @@ public function getParameters(): array
return [];
}

public function returnsByReference(): TrinaryLogic
{
return TrinaryLogic::createMaybe();
}

}
5 changes: 5 additions & 0 deletions src/Reflection/Php/NativeBuiltinMethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,9 @@ public function getParameters(): array
return $this->reflection->getParameters();
}

public function returnsByReference(): TrinaryLogic
{
return TrinaryLogic::createFromBoolean($this->reflection->returnsReference());
}

}
5 changes: 5 additions & 0 deletions src/Reflection/Php/PhpFunctionFromParserNodeReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -260,4 +260,9 @@ public function getDocComment(): ?string
return $this->phpDocComment;
}

public function returnsByReference(): TrinaryLogic
{
return TrinaryLogic::createFromBoolean($this->functionLike->returnsByRef());
}

}
5 changes: 5 additions & 0 deletions src/Reflection/Php/PhpFunctionReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -265,4 +265,9 @@ public function getDocComment(): ?string
return $this->phpDocComment;
}

public function returnsByReference(): TrinaryLogic
{
return TrinaryLogic::createFromBoolean($this->reflection->returnsReference());
}

}
6 changes: 6 additions & 0 deletions src/Reflection/Php/PhpMethodFromParserNodeReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\ExtendedMethodReflection;
use PHPStan\Reflection\MissingMethodFromReflectionException;
use PHPStan\TrinaryLogic;
use PHPStan\Type\ArrayType;
use PHPStan\Type\BooleanType;
use PHPStan\Type\Generic\TemplateTypeMap;
Expand Down Expand Up @@ -142,4 +143,9 @@ public function getSelfOutType(): ?Type
return $this->selfOutType;
}

public function returnsByReference(): TrinaryLogic
{
return TrinaryLogic::createFromBoolean($this->getClassMethod()->returnsByRef());
}

}
5 changes: 5 additions & 0 deletions src/Reflection/Php/PhpMethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -436,4 +436,9 @@ public function getDocComment(): ?string
return $this->phpDocComment;
}

public function returnsByReference(): TrinaryLogic
{
return $this->reflection->returnsByReference();
}

}
5 changes: 5 additions & 0 deletions src/Reflection/ResolvedMethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,9 @@ public function getSelfOutType(): ?Type
return $this->selfOutType;
}

public function returnsByReference(): TrinaryLogic
{
return $this->reflection->returnsByReference();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,11 @@ public function findFunctionReflection(string $functionName): ?NativeFunctionRef
$phpDocReturnType = null;
$asserts = Assertions::createEmpty();
$docComment = null;
$returnsByReference = TrinaryLogic::createMaybe();
try {
$reflectionFunction = $this->reflector->reflectFunction($functionName);
$reflectionFunctionAdapter = new ReflectionFunction($reflectionFunction);
$returnsByReference = TrinaryLogic::createFromBoolean($reflectionFunctionAdapter->returnsReference());
if ($reflectionFunction->getFileName() !== null) {
$fileName = $reflectionFunction->getFileName();
$docComment = $reflectionFunction->getDocComment();
Expand Down Expand Up @@ -174,6 +176,7 @@ public function findFunctionReflection(string $functionName): ?NativeFunctionRef
$isDeprecated,
$asserts,
$docComment,
$returnsByReference,
);
$this->functionMap[$lowerCasedFunctionName] = $functionReflection;

Expand Down
5 changes: 5 additions & 0 deletions src/Reflection/Type/IntersectionTypeMethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,4 +170,9 @@ public function getSelfOutType(): ?Type
return null;
}

public function returnsByReference(): TrinaryLogic
{
return TrinaryLogic::lazyMaxMin($this->methods, static fn (ExtendedMethodReflection $method): TrinaryLogic => $method->returnsByReference());
}

}
5 changes: 5 additions & 0 deletions src/Reflection/Type/UnionTypeMethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,9 @@ public function getSelfOutType(): ?Type
return null;
}

public function returnsByReference(): TrinaryLogic
{
return TrinaryLogic::lazyExtremeIdentity($this->methods, static fn (ExtendedMethodReflection $method): TrinaryLogic => $method->returnsByReference());
}

}
5 changes: 5 additions & 0 deletions src/Reflection/WrappedExtendedMethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,9 @@ public function getSelfOutType(): ?Type
return null;
}

public function returnsByReference(): TrinaryLogic
{
return TrinaryLogic::createMaybe();
}

}
62 changes: 62 additions & 0 deletions tests/PHPStan/Reflection/FunctionReflectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Testing\PHPStanTestCase;
use PHPStan\TrinaryLogic;
use const PHP_VERSION_ID;

class FunctionReflectionTest extends PHPStanTestCase
{
Expand Down Expand Up @@ -127,6 +129,66 @@ public function testMethodHasPhpdoc(string $className, string $methodName, ?stri
$this->assertSame($expectedDocComment, $methodReflection->getDocComment());
}

public function dataFunctionReturnsByReference(): iterable
{
yield ['\\implode', TrinaryLogic::createNo()];

yield ['ReturnsByReference\\foo', TrinaryLogic::createNo()];
yield ['ReturnsByReference\\refFoo', TrinaryLogic::createYes()];
}

/**
* @dataProvider dataFunctionReturnsByReference
*/
public function testFunctionReturnsByReference(string $functionName, TrinaryLogic $expectedReturnsByRef): void
{
require_once __DIR__ . '/data/returns-by-reference.php';

$reflectionProvider = $this->createReflectionProvider();

$functionReflection = $reflectionProvider->getFunction(new Node\Name($functionName), null);
$this->assertSame($expectedReturnsByRef, $functionReflection->returnsByReference());
}

public function dataMethodReturnsByReference(): iterable
{
yield ['ReturnsByReference\\X', 'foo', TrinaryLogic::createNo()];
yield ['ReturnsByReference\\X', 'refFoo', TrinaryLogic::createYes()];

yield ['ReturnsByReference\\SubX', 'foo', TrinaryLogic::createNo()];
yield ['ReturnsByReference\\SubX', 'refFoo', TrinaryLogic::createYes()];
yield ['ReturnsByReference\\SubX', 'subRefFoo', TrinaryLogic::createYes()];

yield ['ReturnsByReference\\TraitX', 'traitFoo', TrinaryLogic::createNo()];
yield ['ReturnsByReference\\TraitX', 'refTraitFoo', TrinaryLogic::createYes()];

if (PHP_VERSION_ID < 80100) {
return;
}

yield ['ReturnsByReference\\E', 'enumFoo', TrinaryLogic::createNo()];
yield ['ReturnsByReference\\E', 'refEnumFoo', TrinaryLogic::createYes()];
// cases() method cannot be overridden; https://3v4l.org/ebm83
yield ['ReturnsByReference\\E', 'cases', TrinaryLogic::createNo()];
}

/**
* @dataProvider dataMethodReturnsByReference
*/
public function testMethodReturnsByReference(string $className, string $methodName, TrinaryLogic $expectedReturnsByRef): void
{
$reflectionProvider = $this->createReflectionProvider();
$class = $reflectionProvider->getClass($className);
$scope = $this->createMock(Scope::class);
$scope->method('isInClass')->willReturn(true);
$scope->method('getClassReflection')->willReturn($class);
$scope->method('canAccessProperty')->willReturn(true);
$classReflection = $reflectionProvider->getClass($className);

$methodReflection = $classReflection->getMethod($methodName, $scope);
$this->assertSame($expectedReturnsByRef, $methodReflection->returnsByReference());
}

/**
* @return string[]
*/
Expand Down
11 changes: 11 additions & 0 deletions tests/PHPStan/Reflection/data/returns-by-reference-enum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php // lint >= 8.1

namespace ReturnsByReference;

enum E {
case E1;

function enumFoo() {}

function &refEnumFoo() {}
}
28 changes: 28 additions & 0 deletions tests/PHPStan/Reflection/data/returns-by-reference.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace ReturnsByReference;

function foo() {}

function &refFoo() {}

class X {
function foo() {}

function &refFoo() {}
}

class SubX extends X {
function &subRefFoo() {}

}

class TraitX {
use T;
}

trait T {
function traitFoo() {}

function &refTraitFoo() {}
}