Skip to content

Commit 0d0de94

Browse files
committed
Bleeding edge - check @mixin PHPDoc tag above traits
1 parent ba59142 commit 0d0de94

9 files changed

+245
-0
lines changed

conf/config.level2.neon

+10
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ conditionalTags:
5555
phpstan.rules.rule: %featureToggles.absentTypeChecks%
5656
PHPStan\Rules\Classes\MethodTagTraitUseRule:
5757
phpstan.rules.rule: %featureToggles.absentTypeChecks%
58+
PHPStan\Rules\Classes\MixinTraitRule:
59+
phpstan.rules.rule: %featureToggles.absentTypeChecks%
60+
PHPStan\Rules\Classes\MixinTraitUseRule:
61+
phpstan.rules.rule: %featureToggles.absentTypeChecks%
5862
PHPStan\Rules\Classes\PropertyTagRule:
5963
phpstan.rules.rule: %featureToggles.absentTypeChecks%
6064
PHPStan\Rules\Classes\PropertyTagTraitRule:
@@ -86,6 +90,12 @@ services:
8690
tags:
8791
- phpstan.rules.rule
8892

93+
-
94+
class: PHPStan\Rules\Classes\MixinTraitRule
95+
96+
-
97+
class: PHPStan\Rules\Classes\MixinTraitUseRule
98+
8999
-
90100
class: PHPStan\Rules\Classes\MethodTagRule
91101

src/PhpDoc/StubValidator.php

+4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
use PHPStan\Rules\Classes\MethodTagTraitUseRule;
3434
use PHPStan\Rules\Classes\MixinCheck;
3535
use PHPStan\Rules\Classes\MixinRule;
36+
use PHPStan\Rules\Classes\MixinTraitRule;
37+
use PHPStan\Rules\Classes\MixinTraitUseRule;
3638
use PHPStan\Rules\Classes\PropertyTagCheck;
3739
use PHPStan\Rules\Classes\PropertyTagRule;
3840
use PHPStan\Rules\Classes\PropertyTagTraitRule;
@@ -259,6 +261,8 @@ private function getRuleRegistry(Container $container): RuleRegistry
259261
$rules[] = new PropertyTagTraitRule($propertyTagCheck, $reflectionProvider);
260262
$rules[] = new PropertyTagTraitUseRule($propertyTagCheck);
261263
$rules[] = new MixinRule($mixinCheck);
264+
$rules[] = new MixinTraitRule($mixinCheck, $reflectionProvider);
265+
$rules[] = new MixinTraitUseRule($mixinCheck);
262266
$rules[] = new LocalTypeTraitUseAliasesRule($localTypeAliasesCheck);
263267
$rules[] = new MethodTagTemplateTypeTraitRule($methodTagTemplateTypeCheck, $reflectionProvider);
264268
}

src/Rules/Classes/MixinTraitRule.php

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Classes;
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 MixinTraitRule implements Rule
14+
{
15+
16+
public function __construct(private MixinCheck $check, private ReflectionProvider $reflectionProvider)
17+
{
18+
}
19+
20+
public function getNodeType(): string
21+
{
22+
return Node\Stmt\Trait_::class;
23+
}
24+
25+
public function processNode(Node $node, Scope $scope): array
26+
{
27+
$traitName = $node->namespacedName;
28+
if ($traitName === null) {
29+
return [];
30+
}
31+
32+
if (!$this->reflectionProvider->hasClass($traitName->toString())) {
33+
return [];
34+
}
35+
36+
return $this->check->checkInTraitDefinitionContext(
37+
$this->reflectionProvider->getClass($traitName->toString()),
38+
);
39+
}
40+
41+
}
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Classes;
4+
5+
use PhpParser\Node;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Node\InTraitNode;
8+
use PHPStan\Rules\Rule;
9+
10+
/**
11+
* @implements Rule<InTraitNode>
12+
*/
13+
final class MixinTraitUseRule implements Rule
14+
{
15+
16+
public function __construct(private MixinCheck $check)
17+
{
18+
}
19+
20+
public function getNodeType(): string
21+
{
22+
return InTraitNode::class;
23+
}
24+
25+
public function processNode(Node $node, Scope $scope): array
26+
{
27+
return $this->check->checkInTraitUseContext(
28+
$node->getTraitReflection(),
29+
$node->getImplementingClassReflection(),
30+
$node->getOriginalNode(),
31+
);
32+
}
33+
34+
}

tests/PHPStan/Analyser/NodeScopeResolverTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ public function dataFileAsserts(): iterable
184184
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Classes/data/bug-11591.php');
185185
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Classes/data/bug-11591-method-tag.php');
186186
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Classes/data/bug-11591-property-tag.php');
187+
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Classes/data/mixin-trait-use.php');
187188
}
188189

189190
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Classes;
4+
5+
use PHPStan\Rules\ClassCaseSensitivityCheck;
6+
use PHPStan\Rules\ClassForbiddenNameCheck;
7+
use PHPStan\Rules\ClassNameCheck;
8+
use PHPStan\Rules\Generics\GenericObjectTypeCheck;
9+
use PHPStan\Rules\MissingTypehintCheck;
10+
use PHPStan\Rules\PhpDoc\UnresolvableTypeHelper;
11+
use PHPStan\Rules\Rule;
12+
use PHPStan\Testing\RuleTestCase;
13+
14+
/**
15+
* @extends RuleTestCase<MixinTraitRule>
16+
*/
17+
class MixinTraitRuleTest extends RuleTestCase
18+
{
19+
20+
protected function getRule(): Rule
21+
{
22+
$reflectionProvider = $this->createReflectionProvider();
23+
24+
return new MixinTraitRule(
25+
new MixinCheck(
26+
$reflectionProvider,
27+
new ClassNameCheck(
28+
new ClassCaseSensitivityCheck($reflectionProvider, true),
29+
new ClassForbiddenNameCheck(self::getContainer()),
30+
),
31+
new GenericObjectTypeCheck(),
32+
new MissingTypehintCheck(true, true, true, true, []),
33+
new UnresolvableTypeHelper(),
34+
true,
35+
true,
36+
),
37+
$reflectionProvider,
38+
);
39+
}
40+
41+
public function testRule(): void
42+
{
43+
$this->analyse([__DIR__ . '/data/mixin-trait.php'], [
44+
[
45+
'Trait MixinTrait\FooTrait has PHPDoc tag @mixin with no value type specified in iterable type array.',
46+
14,
47+
MissingTypehintCheck::MISSING_ITERABLE_VALUE_TYPE_TIP,
48+
],
49+
]);
50+
}
51+
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Classes;
4+
5+
use PHPStan\Rules\ClassCaseSensitivityCheck;
6+
use PHPStan\Rules\ClassForbiddenNameCheck;
7+
use PHPStan\Rules\ClassNameCheck;
8+
use PHPStan\Rules\Generics\GenericObjectTypeCheck;
9+
use PHPStan\Rules\MissingTypehintCheck;
10+
use PHPStan\Rules\PhpDoc\UnresolvableTypeHelper;
11+
use PHPStan\Rules\Rule;
12+
use PHPStan\Testing\RuleTestCase;
13+
14+
/**
15+
* @extends RuleTestCase<MixinTraitUseRule>
16+
*/
17+
class MixinTraitUseRuleTest extends RuleTestCase
18+
{
19+
20+
protected function getRule(): Rule
21+
{
22+
$reflectionProvider = $this->createReflectionProvider();
23+
24+
return new MixinTraitUseRule(
25+
new MixinCheck(
26+
$reflectionProvider,
27+
new ClassNameCheck(
28+
new ClassCaseSensitivityCheck($reflectionProvider, true),
29+
new ClassForbiddenNameCheck(self::getContainer()),
30+
),
31+
new GenericObjectTypeCheck(),
32+
new MissingTypehintCheck(true, true, true, true, []),
33+
new UnresolvableTypeHelper(),
34+
true,
35+
true,
36+
),
37+
);
38+
}
39+
40+
public function testRule(): void
41+
{
42+
$this->analyse([__DIR__ . '/data/mixin-trait-use.php'], [
43+
[
44+
'PHPDoc tag @mixin contains unresolvable type.',
45+
22,
46+
],
47+
]);
48+
}
49+
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace MixinTraitUse;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/**
8+
* @template T
9+
*/
10+
interface Foo
11+
{
12+
13+
/** @return T */
14+
public function get();
15+
16+
}
17+
18+
/**
19+
* @mixin Foo<static>
20+
* @mixin string&int
21+
*/
22+
trait FooTrait
23+
{
24+
25+
}
26+
27+
class Usages
28+
{
29+
30+
use FooTrait;
31+
32+
}
33+
34+
function (Usages $u): void {
35+
assertType(Usages::class, $u->get());
36+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace MixinTrait;
4+
5+
/** @template T */
6+
class Foo
7+
{
8+
9+
}
10+
11+
/**
12+
* @mixin Foo<array>
13+
*/
14+
trait FooTrait
15+
{
16+
17+
}

0 commit comments

Comments
 (0)