Skip to content

Commit 8a6918f

Browse files
mad-brillerondrejmirtes
authored andcommitted
Wip: added resolution of generic @method tags.
1 parent 97408e6 commit 8a6918f

File tree

6 files changed

+62
-1
lines changed

6 files changed

+62
-1
lines changed

src/PhpDoc/PhpDocNodeResolver.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,12 +185,24 @@ public function resolveMethodTags(PhpDocNode $phpDocNode, NameScope $nameScope):
185185
);
186186
}
187187

188+
$templates = [];
189+
foreach ($tagValue->templateTypes as $templateType) {
190+
$templates[$templateType->name] = new TemplateTag(
191+
$templateType->name,
192+
$templateType->bound !== null
193+
? $this->typeNodeResolver->resolve($templateType->bound, $nameScope)
194+
: new MixedType(),
195+
TemplateTypeVariance::createInvariant()
196+
);
197+
}
198+
188199
$resolved[$tagValue->methodName] = new MethodTag(
189200
$tagValue->returnType !== null
190201
? $this->typeNodeResolver->resolve($tagValue->returnType, $nameScope)
191202
: new MixedType(),
192203
$tagValue->isStatic,
193204
$parameters,
205+
$templates
194206
);
195207
}
196208
}

src/PhpDoc/Tag/MethodTag.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ class MethodTag
1010

1111
/**
1212
* @param array<string, MethodTagParameter> $parameters
13+
* @param array<string, TemplateTag> $templates
1314
*/
1415
public function __construct(
1516
private Type $returnType,
1617
private bool $isStatic,
1718
private array $parameters,
19+
private array $templates = [],
1820
)
1921
{
2022
}
@@ -37,4 +39,12 @@ public function getParameters(): array
3739
return $this->parameters;
3840
}
3941

42+
/**
43+
* @return array<string, TemplateTag>
44+
*/
45+
public function getTemplates(): array
46+
{
47+
return $this->templates;
48+
}
49+
4050
}

src/Reflection/Annotations/AnnotationMethodReflection.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public function __construct(
3030
private bool $isStatic,
3131
private bool $isVariadic,
3232
private ?Type $throwType,
33+
private TemplateTypeMap $templateTypeMap,
3334
)
3435
{
3536
}
@@ -69,7 +70,7 @@ public function getVariants(): array
6970
if ($this->variants === null) {
7071
$this->variants = [
7172
new FunctionVariantWithPhpDocs(
72-
TemplateTypeMap::createEmpty(),
73+
$this->templateTypeMap,
7374
null,
7475
$this->parameters,
7576
$this->isVariadic,

src/Reflection/Annotations/AnnotationsMethodsClassReflectionExtension.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,18 @@
22

33
namespace PHPStan\Reflection\Annotations;
44

5+
use PHPStan\PhpDoc\Tag\TemplateTag;
56
use PHPStan\Reflection\ClassReflection;
67
use PHPStan\Reflection\ExtendedMethodReflection;
78
use PHPStan\Reflection\MethodReflection;
89
use PHPStan\Reflection\MethodsClassReflectionExtension;
10+
use PHPStan\Type\Generic\TemplateTypeFactory;
911
use PHPStan\Type\Generic\TemplateTypeHelper;
12+
use PHPStan\Type\Generic\TemplateTypeMap;
13+
use PHPStan\Type\Generic\TemplateTypeScope;
1014
use PHPStan\Type\Generic\TemplateTypeVariance;
15+
use PHPStan\Type\Type;
16+
1117
use function count;
1218

1319
class AnnotationsMethodsClassReflectionExtension implements MethodsClassReflectionExtension
@@ -57,6 +63,13 @@ private function findClassReflectionWithMethod(
5763
);
5864
}
5965

66+
$templateTypeScope = TemplateTypeScope::createWithClass($classReflection->getName());
67+
68+
$templateTypeMap = new TemplateTypeMap(array_map(
69+
static fn (TemplateTag $tag): Type => TemplateTypeFactory::fromTemplateTag($templateTypeScope, $tag),
70+
$methodTags[$methodName]->getTemplates()
71+
));
72+
6073
$isStatic = $methodTags[$methodName]->isStatic();
6174
$nativeCallMethodName = $isStatic ? '__callStatic' : '__call';
6275

@@ -75,6 +88,7 @@ private function findClassReflectionWithMethod(
7588
$classReflection->hasNativeMethod($nativeCallMethodName)
7689
? $classReflection->getNativeMethod($nativeCallMethodName)->getThrowType()
7790
: null,
91+
$templateTypeMap,
7892
);
7993
}
8094

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public function dataFileAsserts(): iterable
2222
yield from $this->gatherAssertTypes(__DIR__ . '/data/json-decode/narrow_type_with_force_array.php');
2323
yield from $this->gatherAssertTypes(__DIR__ . '/data/json-decode/invalid_type.php');
2424
yield from $this->gatherAssertTypes(__DIR__ . '/data/json-decode/json_object_as_array.php');
25+
yield from $this->gatherAssertTypes(__DIR__ . '/data/generic-method-tags.php');
2526

2627
require_once __DIR__ . '/data/bug2574.php';
2728

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace GenericMethodTags;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/**
8+
* @method TVal doThing<TVal of mixed>(TVal $param)
9+
*/
10+
class Test
11+
{
12+
public function __call(): mixed
13+
{
14+
}
15+
}
16+
17+
function test(int $int, string $string): void
18+
{
19+
$test = new Test();
20+
21+
assertType('int', $test->doThing($int));
22+
assertType('string', $test->doThing($string));
23+
}

0 commit comments

Comments
 (0)