Skip to content

Commit aadbf62

Browse files
committed
Bleeding edge - MethodTagTemplateTypeTraitRule
1 parent 085fcf4 commit aadbf62

File tree

5 files changed

+135
-0
lines changed

5 files changed

+135
-0
lines changed

conf/config.level2.neon

+5
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ conditionalTags:
6565
phpstan.rules.rule: %featureToggles.closureDefaultParameterTypeRule%
6666
PHPStan\Rules\Functions\IncompatibleClosureDefaultParameterTypeRule:
6767
phpstan.rules.rule: %featureToggles.closureDefaultParameterTypeRule%
68+
PHPStan\Rules\Generics\MethodTagTemplateTypeTraitRule:
69+
phpstan.rules.rule: %featureToggles.absentTypeChecks%
6870
PHPStan\Rules\Methods\IllegalConstructorMethodCallRule:
6971
phpstan.rules.rule: %featureToggles.illegalConstructorMethodCall%
7072
PHPStan\Rules\Methods\IllegalConstructorStaticCallRule:
@@ -127,6 +129,9 @@ services:
127129
reportMaybes: %reportMaybes%
128130
tags:
129131
- phpstan.rules.rule
132+
133+
-
134+
class: PHPStan\Rules\Generics\MethodTagTemplateTypeTraitRule
130135
-
131136
class: PHPStan\Rules\Methods\IllegalConstructorMethodCallRule
132137
-

src/PhpDoc/StubValidator.php

+2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
use PHPStan\Rules\Generics\MethodSignatureVarianceRule;
5555
use PHPStan\Rules\Generics\MethodTagTemplateTypeCheck;
5656
use PHPStan\Rules\Generics\MethodTagTemplateTypeRule;
57+
use PHPStan\Rules\Generics\MethodTagTemplateTypeTraitRule;
5758
use PHPStan\Rules\Generics\MethodTemplateTypeRule;
5859
use PHPStan\Rules\Generics\TemplateTypeCheck;
5960
use PHPStan\Rules\Generics\TraitTemplateTypeRule;
@@ -257,6 +258,7 @@ private function getRuleRegistry(Container $container): RuleRegistry
257258
$rules[] = new PropertyTagTraitUseRule($propertyTagCheck);
258259
$rules[] = new MixinRule($reflectionProvider, $classNameCheck, $genericObjectTypeCheck, $missingTypehintCheck, $unresolvableTypeHelper, true, true);
259260
$rules[] = new LocalTypeTraitUseAliasesRule($localTypeAliasesCheck);
261+
$rules[] = new MethodTagTemplateTypeTraitRule($methodTagTemplateTypeCheck, $reflectionProvider);
260262
}
261263

262264
return new DirectRuleRegistry($rules);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Generics;
4+
5+
use PhpParser\Node;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Reflection\ReflectionProvider;
8+
use PHPStan\Rules\Rule;
9+
10+
/**
11+
* @implements Rule<Node\Stmt\Trait_>
12+
*/
13+
final class MethodTagTemplateTypeTraitRule implements Rule
14+
{
15+
16+
public function __construct(
17+
private MethodTagTemplateTypeCheck $check,
18+
private ReflectionProvider $reflectionProvider,
19+
)
20+
{
21+
}
22+
23+
public function getNodeType(): string
24+
{
25+
return Node\Stmt\Trait_::class;
26+
}
27+
28+
public function processNode(Node $node, Scope $scope): array
29+
{
30+
$docComment = $node->getDocComment();
31+
if ($docComment === null) {
32+
return [];
33+
}
34+
35+
$traitName = $node->namespacedName;
36+
if ($traitName === null) {
37+
return [];
38+
}
39+
40+
if (!$this->reflectionProvider->hasClass($traitName->toString())) {
41+
return [];
42+
}
43+
44+
return $this->check->check(
45+
$this->reflectionProvider->getClass($traitName->toString()),
46+
$scope,
47+
$node,
48+
$docComment->getText(),
49+
);
50+
}
51+
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Generics;
4+
5+
use PHPStan\Rules\ClassCaseSensitivityCheck;
6+
use PHPStan\Rules\ClassForbiddenNameCheck;
7+
use PHPStan\Rules\ClassNameCheck;
8+
use PHPStan\Rules\Rule;
9+
use PHPStan\Testing\RuleTestCase;
10+
use PHPStan\Type\FileTypeMapper;
11+
12+
/**
13+
* @extends RuleTestCase<MethodTagTemplateTypeTraitRule>
14+
*/
15+
class MethodTagTemplateTypeTraitRuleTest extends RuleTestCase
16+
{
17+
18+
protected function getRule(): Rule
19+
{
20+
$reflectionProvider = $this->createReflectionProvider();
21+
$typeAliasResolver = $this->createTypeAliasResolver(['TypeAlias' => 'int'], $reflectionProvider);
22+
23+
return new MethodTagTemplateTypeTraitRule(
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,
35+
),
36+
),
37+
$reflectionProvider,
38+
);
39+
}
40+
41+
public function testRule(): void
42+
{
43+
$this->analyse([__DIR__ . '/data/method-tag-trait-template.php'], [
44+
[
45+
'PHPDoc tag @method template U for method MethodTagTraitTemplate\HelloWorld::sayHello() has invalid bound type MethodTagTraitTemplate\Nonexisting.',
46+
11,
47+
],
48+
[
49+
'PHPDoc tag @method template for method MethodTagTraitTemplate\HelloWorld::sayHello() cannot have existing class stdClass as its name.',
50+
11,
51+
],
52+
[
53+
'PHPDoc tag @method template T for method MethodTagTraitTemplate\HelloWorld::sayHello() shadows @template T for class MethodTagTraitTemplate\HelloWorld.',
54+
11,
55+
],
56+
[
57+
'PHPDoc tag @method template for method MethodTagTraitTemplate\HelloWorld::typeAlias() cannot have existing type alias TypeAlias as its name.',
58+
11,
59+
],
60+
]);
61+
}
62+
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace MethodTagTraitTemplate;
4+
5+
/**
6+
* @template T
7+
*
8+
* @method void sayHello<T, U of Nonexisting, stdClass>(T $a, U $b, stdClass $c)
9+
* @method void typeAlias<TypeAlias of mixed>(TypeAlias $a)
10+
*/
11+
trait HelloWorld
12+
{
13+
}

0 commit comments

Comments
 (0)