Skip to content

Commit cf64761

Browse files
committed
Fix @phpstan-self-out with GenericStaticType when method is called on $this
1 parent dab99cb commit cf64761

File tree

2 files changed

+62
-26
lines changed

2 files changed

+62
-26
lines changed

src/Reflection/Type/CallbackUnresolvedMethodPrototypeReflection.php

+38-26
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
use PHPStan\Reflection\ExtendedParametersAcceptor;
1111
use PHPStan\Reflection\Php\ExtendedDummyParameter;
1212
use PHPStan\Reflection\ResolvedMethodReflection;
13+
use PHPStan\Type\ThisType;
1314
use PHPStan\Type\Type;
15+
use PHPStan\Type\TypeCombinator;
1416
use function array_map;
1517

1618
final class CallbackUnresolvedMethodPrototypeReflection implements UnresolvedMethodPrototypeReflection
@@ -82,32 +84,42 @@ public function withCalledOnType(Type $type): UnresolvedMethodPrototypeReflectio
8284

8385
private function transformMethodWithStaticType(ClassReflection $declaringClass, ExtendedMethodReflection $method): ExtendedMethodReflection
8486
{
85-
$variantFn = fn (ExtendedParametersAcceptor $acceptor): ExtendedParametersAcceptor => new ExtendedFunctionVariant(
86-
$acceptor->getTemplateTypeMap(),
87-
$acceptor->getResolvedTemplateTypeMap(),
88-
array_map(
89-
fn (ExtendedParameterReflection $parameter): ExtendedParameterReflection => new ExtendedDummyParameter(
90-
$parameter->getName(),
91-
$this->transformStaticType($parameter->getType()),
92-
$parameter->isOptional(),
93-
$parameter->passedByReference(),
94-
$parameter->isVariadic(),
95-
$parameter->getDefaultValue(),
96-
$parameter->getNativeType(),
97-
$this->transformStaticType($parameter->getPhpDocType()),
98-
$parameter->getOutType() !== null ? $this->transformStaticType($parameter->getOutType()) : null,
99-
$parameter->isImmediatelyInvokedCallable(),
100-
$parameter->getClosureThisType() !== null ? $this->transformStaticType($parameter->getClosureThisType()) : null,
101-
$parameter->getAttributes(),
87+
$selfOutType = $method->getSelfOutType() !== null ? $this->transformStaticType($method->getSelfOutType()) : null;
88+
$variantFn = function (ExtendedParametersAcceptor $acceptor) use (&$selfOutType): ExtendedParametersAcceptor {
89+
$originalReturnType = $acceptor->getReturnType();
90+
if ($originalReturnType instanceof ThisType && $selfOutType !== null) {
91+
$returnType = TypeCombinator::intersect($selfOutType, $this->transformStaticType($originalReturnType));
92+
$selfOutType = $returnType;
93+
} else {
94+
$returnType = $this->transformStaticType($originalReturnType);
95+
}
96+
return new ExtendedFunctionVariant(
97+
$acceptor->getTemplateTypeMap(),
98+
$acceptor->getResolvedTemplateTypeMap(),
99+
array_map(
100+
fn (ExtendedParameterReflection $parameter): ExtendedParameterReflection => new ExtendedDummyParameter(
101+
$parameter->getName(),
102+
$this->transformStaticType($parameter->getType()),
103+
$parameter->isOptional(),
104+
$parameter->passedByReference(),
105+
$parameter->isVariadic(),
106+
$parameter->getDefaultValue(),
107+
$parameter->getNativeType(),
108+
$this->transformStaticType($parameter->getPhpDocType()),
109+
$parameter->getOutType() !== null ? $this->transformStaticType($parameter->getOutType()) : null,
110+
$parameter->isImmediatelyInvokedCallable(),
111+
$parameter->getClosureThisType() !== null ? $this->transformStaticType($parameter->getClosureThisType()) : null,
112+
$parameter->getAttributes(),
113+
),
114+
$acceptor->getParameters(),
102115
),
103-
$acceptor->getParameters(),
104-
),
105-
$acceptor->isVariadic(),
106-
$this->transformStaticType($acceptor->getReturnType()),
107-
$this->transformStaticType($acceptor->getPhpDocReturnType()),
108-
$this->transformStaticType($acceptor->getNativeReturnType()),
109-
$acceptor->getCallSiteVarianceMap(),
110-
);
116+
$acceptor->isVariadic(),
117+
$returnType,
118+
$this->transformStaticType($acceptor->getPhpDocReturnType()),
119+
$this->transformStaticType($acceptor->getNativeReturnType()),
120+
$acceptor->getCallSiteVarianceMap(),
121+
);
122+
};
111123
$variants = array_map($variantFn, $method->getVariants());
112124
$namedArgumentVariants = $method->getNamedArgumentsVariants();
113125
$namedArgumentVariants = $namedArgumentVariants !== null
@@ -119,7 +131,7 @@ private function transformMethodWithStaticType(ClassReflection $declaringClass,
119131
$method,
120132
$variants,
121133
$namedArgumentVariants,
122-
$method->getSelfOutType() !== null ? $this->transformStaticType($method->getSelfOutType()) : null,
134+
$selfOutType,
123135
);
124136
}
125137

tests/PHPStan/Analyser/nsrt/bug-12575.php

+24
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,28 @@ public function add(string $class)
3030
class Bar extends Foo
3131
{
3232

33+
public function doFoo(): void
34+
{
35+
assertType('$this(Bug12575\Bar<T of object (class Bug12575\Bar, argument)>)&static(Bug12575\Bar<Bug12575\A&T of object (class Bug12575\Bar, argument)>)', $this->add(A::class));
36+
assertType('$this(Bug12575\Bar<T of object (class Bug12575\Bar, argument)>)&static(Bug12575\Bar<Bug12575\A&T of object (class Bug12575\Bar, argument)>)', $this);
37+
assertType('T of object (class Bug12575\Bar, argument)', $this->getT());
38+
}
39+
40+
public function doBar(): void
41+
{
42+
$this->add(B::class);
43+
assertType('$this(Bug12575\Bar<T of object (class Bug12575\Bar, argument)>)&static(Bug12575\Bar<Bug12575\B&T of object (class Bug12575\Bar, argument)>)', $this);
44+
assertType('T of object (class Bug12575\Bar, argument)', $this->getT());
45+
}
46+
47+
/**
48+
* @return T
49+
*/
50+
public function getT()
51+
{
52+
53+
}
54+
3355
}
3456

3557
interface A
@@ -49,6 +71,7 @@ interface B
4971
function doFoo(Bar $bar): void {
5072
assertType('Bug12575\\Bar<Bug12575\\A&Bug12575\\B>', $bar->add(B::class));
5173
assertType('Bug12575\\Bar<Bug12575\\A&Bug12575\\B>', $bar);
74+
assertType('Bug12575\A&Bug12575\B', $bar->getT());
5275
};
5376

5477
/**
@@ -58,4 +81,5 @@ function doFoo(Bar $bar): void {
5881
function doBar(Bar $bar): void {
5982
$bar->add(B::class);
6083
assertType('Bug12575\\Bar<Bug12575\\A&Bug12575\\B>', $bar);
84+
assertType('Bug12575\A&Bug12575\B', $bar->getT());
6185
};

0 commit comments

Comments
 (0)