Skip to content

Commit 01b4d4e

Browse files
committed
GetTemplateTypeRule
1 parent b73a8aa commit 01b4d4e

File tree

4 files changed

+139
-0
lines changed

4 files changed

+139
-0
lines changed

Diff for: conf/config.level0.neon

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ rules:
3131
- PHPStan\Rules\Api\ApiMethodCallRule
3232
- PHPStan\Rules\Api\ApiStaticCallRule
3333
- PHPStan\Rules\Api\ApiTraitUseRule
34+
- PHPStan\Rules\Api\GetTemplateTypeRule
3435
- PHPStan\Rules\Api\PhpStanNamespaceIn3rdPartyPackageRule
3536
- PHPStan\Rules\Arrays\DuplicateKeysInLiteralArraysRule
3637
- PHPStan\Rules\Arrays\EmptyArrayItemRule

Diff for: src/Rules/Api/GetTemplateTypeRule.php

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Api;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Expr\MethodCall;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\Reflection\ReflectionProvider;
9+
use PHPStan\Rules\Rule;
10+
use PHPStan\Rules\RuleErrorBuilder;
11+
use PHPStan\Type\Type;
12+
use function count;
13+
use function sprintf;
14+
15+
/**
16+
* @implements Rule<MethodCall>
17+
*/
18+
class GetTemplateTypeRule implements Rule
19+
{
20+
21+
public function __construct(private ReflectionProvider $reflectionProvider)
22+
{
23+
}
24+
25+
public function getNodeType(): string
26+
{
27+
return MethodCall::class;
28+
}
29+
30+
public function processNode(Node $node, Scope $scope): array
31+
{
32+
$args = $node->getArgs();
33+
if (count($args) < 2) {
34+
return [];
35+
}
36+
if (!$node->name instanceof Node\Identifier) {
37+
return [];
38+
}
39+
40+
if ($node->name->toLowerString() !== 'gettemplatetype') {
41+
return [];
42+
}
43+
44+
$calledOnType = $scope->getType($node->var);
45+
$methodReflection = $scope->getMethodReflection($calledOnType, $node->name->toString());
46+
if ($methodReflection === null) {
47+
return [];
48+
}
49+
50+
if (!$methodReflection->getDeclaringClass()->is(Type::class)) {
51+
return [];
52+
}
53+
54+
$classType = $scope->getType($args[0]->value);
55+
$templateType = $scope->getType($args[1]->value);
56+
$errors = [];
57+
foreach ($classType->getConstantStrings() as $classNameType) {
58+
if (!$this->reflectionProvider->hasClass($classNameType->getValue())) {
59+
continue;
60+
}
61+
$classReflection = $this->reflectionProvider->getClass($classNameType->getValue());
62+
$templateTypeMap = $classReflection->getTemplateTypeMap();
63+
foreach ($templateType->getConstantStrings() as $templateTypeName) {
64+
if ($templateTypeMap->hasType($templateTypeName->getValue())) {
65+
continue;
66+
}
67+
68+
$errors[] = RuleErrorBuilder::message(sprintf(
69+
'Call to %s::%s() references unknown template type %s on class %s.',
70+
$methodReflection->getDeclaringClass()->getDisplayName(),
71+
$methodReflection->getName(),
72+
$templateTypeName->getValue(),
73+
$classReflection->getDisplayName(),
74+
))->build();
75+
}
76+
}
77+
78+
return $errors;
79+
}
80+
81+
}

Diff for: tests/PHPStan/Rules/Api/GetTemplateTypeRuleTest.php

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Api;
4+
5+
use PHPStan\Rules\Rule;
6+
use PHPStan\Testing\RuleTestCase;
7+
8+
/**
9+
* @extends RuleTestCase<GetTemplateTypeRule>
10+
*/
11+
class GetTemplateTypeRuleTest extends RuleTestCase
12+
{
13+
14+
protected function getRule(): Rule
15+
{
16+
return new GetTemplateTypeRule($this->createReflectionProvider());
17+
}
18+
19+
public function testRule(): void
20+
{
21+
$this->analyse([__DIR__ . '/data/get-template-type.php'], [
22+
[
23+
'Call to PHPStan\Type\Type::getTemplateType() references unknown template type TSendd on class Generator.',
24+
15,
25+
],
26+
[
27+
'Call to PHPStan\Type\ObjectType::getTemplateType() references unknown template type TSendd on class Generator.',
28+
21,
29+
],
30+
]);
31+
}
32+
33+
}

Diff for: tests/PHPStan/Rules/Api/data/get-template-type.php

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace GetTemplateType;
4+
5+
use Generator;
6+
use PHPStan\Type\ObjectType;
7+
use PHPStan\Type\Type;
8+
9+
class Foo
10+
{
11+
12+
public function doFoo(Type $type): void
13+
{
14+
$type->getTemplateType(Generator::class, 'TSend');
15+
$type->getTemplateType(Generator::class, 'TSendd');
16+
}
17+
18+
public function doBar(ObjectType $type): void
19+
{
20+
$type->getTemplateType(Generator::class, 'TSend');
21+
$type->getTemplateType(Generator::class, 'TSendd');
22+
}
23+
24+
}

0 commit comments

Comments
 (0)