Skip to content

Commit 47a85bf

Browse files
committed
Refactoring: introduce MethodTagTemplateTypeCheck
1 parent 777a82a commit 47a85bf

File tree

4 files changed

+99
-58
lines changed

4 files changed

+99
-58
lines changed

conf/config.neon

+3
Original file line numberDiff line numberDiff line change
@@ -1005,6 +1005,9 @@ services:
10051005
-
10061006
class: PHPStan\Rules\Generics\GenericObjectTypeCheck
10071007

1008+
-
1009+
class: PHPStan\Rules\Generics\MethodTagTemplateTypeCheck
1010+
10081011
-
10091012
class: PHPStan\Rules\Generics\TemplateTypeCheck
10101013
arguments:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Generics;
4+
5+
use PhpParser\Node\Stmt\ClassLike;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Internal\SprintfHelper;
8+
use PHPStan\Reflection\ClassReflection;
9+
use PHPStan\Rules\IdentifierRuleError;
10+
use PHPStan\Rules\RuleErrorBuilder;
11+
use PHPStan\Type\FileTypeMapper;
12+
use PHPStan\Type\Generic\TemplateTypeScope;
13+
use PHPStan\Type\VerbosityLevel;
14+
use function array_keys;
15+
use function array_merge;
16+
use function sprintf;
17+
18+
final class MethodTagTemplateTypeCheck
19+
{
20+
21+
public function __construct(
22+
private FileTypeMapper $fileTypeMapper,
23+
private TemplateTypeCheck $templateTypeCheck,
24+
)
25+
{
26+
}
27+
28+
/**
29+
* @return list<IdentifierRuleError>
30+
*/
31+
public function check(
32+
ClassReflection $classReflection,
33+
Scope $scope,
34+
ClassLike $node,
35+
string $docComment,
36+
): array
37+
{
38+
$className = $classReflection->getDisplayName();
39+
$resolvedPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc(
40+
$scope->getFile(),
41+
$classReflection->getName(),
42+
$scope->isInTrait() ? $scope->getTraitReflection()->getName() : null,
43+
null,
44+
$docComment,
45+
);
46+
47+
$messages = [];
48+
$escapedClassName = SprintfHelper::escapeFormatString($className);
49+
$classTemplateTypes = $classReflection->getTemplateTypeMap()->getTypes();
50+
51+
foreach ($resolvedPhpDoc->getMethodTags() as $methodName => $methodTag) {
52+
$methodTemplateTags = $methodTag->getTemplateTags();
53+
$escapedMethodName = SprintfHelper::escapeFormatString($methodName);
54+
55+
$messages = array_merge($messages, $this->templateTypeCheck->check(
56+
$scope,
57+
$node,
58+
TemplateTypeScope::createWithMethod($className, $methodName),
59+
$methodTemplateTags,
60+
sprintf('PHPDoc tag @method template for method %s::%s() cannot have existing class %%s as its name.', $escapedClassName, $escapedMethodName),
61+
sprintf('PHPDoc tag @method template for method %s::%s() cannot have existing type alias %%s as its name.', $escapedClassName, $escapedMethodName),
62+
sprintf('PHPDoc tag @method template %%s for method %s::%s() has invalid bound type %%s.', $escapedClassName, $escapedMethodName),
63+
sprintf('PHPDoc tag @method template %%s for method %s::%s() with bound type %%s is not supported.', $escapedClassName, $escapedMethodName),
64+
));
65+
66+
foreach (array_keys($methodTemplateTags) as $name) {
67+
if (!isset($classTemplateTypes[$name])) {
68+
continue;
69+
}
70+
71+
$messages[] = RuleErrorBuilder::message(sprintf('PHPDoc tag @method template %s for method %s::%s() shadows @template %s for class %s.', $name, $className, $methodName, $classTemplateTypes[$name]->describe(VerbosityLevel::typeOnly()), $classReflection->getDisplayName(false)))
72+
->identifier('methodTag.shadowTemplate')
73+
->build();
74+
}
75+
}
76+
77+
return $messages;
78+
}
79+
80+
}

src/Rules/Generics/MethodTagTemplateTypeRule.php

+5-49
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,8 @@
44

55
use PhpParser\Node;
66
use PHPStan\Analyser\Scope;
7-
use PHPStan\Internal\SprintfHelper;
87
use PHPStan\Node\InClassNode;
98
use PHPStan\Rules\Rule;
10-
use PHPStan\Rules\RuleErrorBuilder;
11-
use PHPStan\Type\FileTypeMapper;
12-
use PHPStan\Type\Generic\TemplateTypeScope;
13-
use PHPStan\Type\VerbosityLevel;
14-
use function array_keys;
15-
use function array_merge;
16-
use function sprintf;
179

1810
/**
1911
* @implements Rule<InClassNode>
@@ -22,8 +14,7 @@ final class MethodTagTemplateTypeRule implements Rule
2214
{
2315

2416
public function __construct(
25-
private FileTypeMapper $fileTypeMapper,
26-
private TemplateTypeCheck $templateTypeCheck,
17+
private MethodTagTemplateTypeCheck $check,
2718
)
2819
{
2920
}
@@ -40,47 +31,12 @@ public function processNode(Node $node, Scope $scope): array
4031
return [];
4132
}
4233

43-
$classReflection = $node->getClassReflection();
44-
$className = $classReflection->getDisplayName();
45-
$resolvedPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc(
46-
$scope->getFile(),
47-
$classReflection->getName(),
48-
$scope->isInTrait() ? $scope->getTraitReflection()->getName() : null,
49-
null,
34+
return $this->check->check(
35+
$node->getClassReflection(),
36+
$scope,
37+
$node->getOriginalNode(),
5038
$docComment->getText(),
5139
);
52-
53-
$messages = [];
54-
$escapedClassName = SprintfHelper::escapeFormatString($className);
55-
$classTemplateTypes = $classReflection->getTemplateTypeMap()->getTypes();
56-
57-
foreach ($resolvedPhpDoc->getMethodTags() as $methodName => $methodTag) {
58-
$methodTemplateTags = $methodTag->getTemplateTags();
59-
$escapedMethodName = SprintfHelper::escapeFormatString($methodName);
60-
61-
$messages = array_merge($messages, $this->templateTypeCheck->check(
62-
$scope,
63-
$node,
64-
TemplateTypeScope::createWithMethod($className, $methodName),
65-
$methodTemplateTags,
66-
sprintf('PHPDoc tag @method template for method %s::%s() cannot have existing class %%s as its name.', $escapedClassName, $escapedMethodName),
67-
sprintf('PHPDoc tag @method template for method %s::%s() cannot have existing type alias %%s as its name.', $escapedClassName, $escapedMethodName),
68-
sprintf('PHPDoc tag @method template %%s for method %s::%s() has invalid bound type %%s.', $escapedClassName, $escapedMethodName),
69-
sprintf('PHPDoc tag @method template %%s for method %s::%s() with bound type %%s is not supported.', $escapedClassName, $escapedMethodName),
70-
));
71-
72-
foreach (array_keys($methodTemplateTags) as $name) {
73-
if (!isset($classTemplateTypes[$name])) {
74-
continue;
75-
}
76-
77-
$messages[] = RuleErrorBuilder::message(sprintf('PHPDoc tag @method template %s for method %s::%s() shadows @template %s for class %s.', $name, $className, $methodName, $classTemplateTypes[$name]->describe(VerbosityLevel::typeOnly()), $classReflection->getDisplayName(false)))
78-
->identifier('methodTag.shadowTemplate')
79-
->build();
80-
}
81-
}
82-
83-
return $messages;
8440
}
8541

8642
}

tests/PHPStan/Rules/Generics/MethodTagTemplateTypeRuleTest.php

+11-9
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,18 @@ protected function getRule(): Rule
2121
$typeAliasResolver = $this->createTypeAliasResolver(['TypeAlias' => 'int'], $reflectionProvider);
2222

2323
return new MethodTagTemplateTypeRule(
24-
self::getContainer()->getByType(FileTypeMapper::class),
25-
new TemplateTypeCheck(
26-
$reflectionProvider,
27-
new ClassNameCheck(
28-
new ClassCaseSensitivityCheck($reflectionProvider, true),
29-
new ClassForbiddenNameCheck(self::getContainer()),
24+
new MethodTagTemplateTypeCheck(
25+
self::getContainer()->getByType(FileTypeMapper::class),
26+
new TemplateTypeCheck(
27+
$reflectionProvider,
28+
new ClassNameCheck(
29+
new ClassCaseSensitivityCheck($reflectionProvider, true),
30+
new ClassForbiddenNameCheck(self::getContainer()),
31+
),
32+
new GenericObjectTypeCheck(),
33+
$typeAliasResolver,
34+
true,
3035
),
31-
new GenericObjectTypeCheck(),
32-
$typeAliasResolver,
33-
true,
3436
),
3537
);
3638
}

0 commit comments

Comments
 (0)