Skip to content

Commit c062fdd

Browse files
pawell67claude
andcommitted
Resolve template types of method calls on a concrete generic static return type
A return type like `static<int, string>` (no template type in its arguments) was turned into a GenericObjectType that kept the ClassReflection of the declaring class, so methods called on the result saw unresolved TKey/TValue (`array<TKey, TValue>`). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
1 parent 48d4823 commit c062fdd

2 files changed

Lines changed: 63 additions & 1 deletion

File tree

‎src/Reflection/Type/CalledOnTypeUnresolvedMethodPrototypeReflection.php‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use PHPStan\Reflection\Php\ExtendedDummyParameter;
1212
use PHPStan\Reflection\ResolvedMethodReflection;
1313
use PHPStan\Turbo\ShadowedByTurboExtension;
14+
use PHPStan\Type\Generic\GenericObjectType;
1415
use PHPStan\Type\Generic\GenericStaticType;
1516
use PHPStan\Type\StaticType;
1617
use PHPStan\Type\ThisType;
@@ -149,7 +150,19 @@ private function transformStaticType(Type $type): Type
149150
if (count($calledOnTypeReflections) === 1) {
150151
$calledOnTypeReflection = $calledOnTypeReflections[0];
151152

152-
return $traverse($type->changeBaseClass($calledOnTypeReflection)->getStaticObjectType());
153+
$staticObjectType = $type->changeBaseClass($calledOnTypeReflection)->getStaticObjectType();
154+
if ($staticObjectType instanceof GenericObjectType) {
155+
// do not carry over the class reflection of the declaring class,
156+
// it would not know the type arguments of the resolved type
157+
$staticObjectType = new GenericObjectType(
158+
$staticObjectType->getClassName(),
159+
$staticObjectType->getTypes(),
160+
$staticObjectType->getSubtractedType(),
161+
variances: $staticObjectType->getVariances(),
162+
);
163+
}
164+
165+
return $traverse($staticObjectType);
153166
}
154167

155168
return $this->calledOnType;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace GenericStaticConcreteArguments;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/**
8+
* @template TKey of array-key
9+
* @template TValue
10+
*/
11+
class Box
12+
{
13+
14+
/** @return static<int, string> */
15+
public function concrete()
16+
{
17+
return new static();
18+
}
19+
20+
/** @return static<int, TValue> */
21+
public function withTemplate()
22+
{
23+
return new static();
24+
}
25+
26+
/** @return array<TKey, TValue> */
27+
public function all(): array
28+
{
29+
return [];
30+
}
31+
32+
/** @return TValue */
33+
public function first()
34+
{
35+
return 1;
36+
}
37+
38+
}
39+
40+
/**
41+
* @param Box<string, bool> $box
42+
*/
43+
function test(Box $box): void
44+
{
45+
assertType('GenericStaticConcreteArguments\Box<int, string>', $box->concrete());
46+
assertType('array<int, string>', $box->concrete()->all());
47+
assertType('string', $box->concrete()->first());
48+
assertType('array<int, bool>', $box->withTemplate()->all());
49+
}

0 commit comments

Comments
 (0)