5
5
use PhpParser \Node ;
6
6
use PHPStan \Analyser \Scope ;
7
7
use PHPStan \Node \InClassNode ;
8
- use PHPStan \Reflection \ReflectionProvider ;
9
- use PHPStan \Rules \ClassNameCheck ;
10
- use PHPStan \Rules \ClassNameNodePair ;
11
- use PHPStan \Rules \Generics \GenericObjectTypeCheck ;
12
- use PHPStan \Rules \MissingTypehintCheck ;
13
- use PHPStan \Rules \PhpDoc \UnresolvableTypeHelper ;
14
8
use PHPStan \Rules \Rule ;
15
- use PHPStan \Rules \RuleErrorBuilder ;
16
- use PHPStan \Type \VerbosityLevel ;
17
- use function array_merge ;
18
- use function implode ;
19
- use function sprintf ;
20
9
21
10
/**
22
11
* @implements Rule<InClassNode>
23
12
*/
24
13
final class MixinRule implements Rule
25
14
{
26
15
27
- public function __construct (
28
- private ReflectionProvider $ reflectionProvider ,
29
- private ClassNameCheck $ classCheck ,
30
- private GenericObjectTypeCheck $ genericObjectTypeCheck ,
31
- private MissingTypehintCheck $ missingTypehintCheck ,
32
- private UnresolvableTypeHelper $ unresolvableTypeHelper ,
33
- private bool $ checkClassCaseSensitivity ,
34
- private bool $ absentTypeChecks ,
35
- )
16
+ public function __construct (private MixinCheck $ check )
36
17
{
37
18
}
38
19
@@ -43,93 +24,7 @@ public function getNodeType(): string
43
24
44
25
public function processNode (Node $ node , Scope $ scope ): array
45
26
{
46
- $ classReflection = $ node ->getClassReflection ();
47
- $ mixinTags = $ classReflection ->getMixinTags ();
48
- $ errors = [];
49
- foreach ($ mixinTags as $ mixinTag ) {
50
- $ type = $ mixinTag ->getType ();
51
- if (!$ type ->canCallMethods ()->yes () || !$ type ->canAccessProperties ()->yes ()) {
52
- $ errors [] = RuleErrorBuilder::message (sprintf ('PHPDoc tag @mixin contains non-object type %s. ' , $ type ->describe (VerbosityLevel::typeOnly ())))
53
- ->identifier ('mixin.nonObject ' )
54
- ->build ();
55
- continue ;
56
- }
57
-
58
- if (
59
- $ this ->unresolvableTypeHelper ->containsUnresolvableType ($ type )
60
- ) {
61
- $ errors [] = RuleErrorBuilder::message ('PHPDoc tag @mixin contains unresolvable type. ' )
62
- ->identifier ('mixin.unresolvableType ' )
63
- ->build ();
64
- continue ;
65
- }
66
-
67
- $ errors = array_merge ($ errors , $ this ->genericObjectTypeCheck ->check (
68
- $ type ,
69
- 'PHPDoc tag @mixin contains generic type %s but %s %s is not generic. ' ,
70
- 'Generic type %s in PHPDoc tag @mixin does not specify all template types of %s %s: %s ' ,
71
- 'Generic type %s in PHPDoc tag @mixin specifies %d template types, but %s %s supports only %d: %s ' ,
72
- 'Type %s in generic type %s in PHPDoc tag @mixin is not subtype of template type %s of %s %s. ' ,
73
- 'Call-site variance of %s in generic type %s in PHPDoc tag @mixin is in conflict with %s template type %s of %s %s. ' ,
74
- 'Call-site variance of %s in generic type %s in PHPDoc tag @mixin is redundant, template type %s of %s %s has the same variance. ' ,
75
- ));
76
-
77
- foreach ($ this ->missingTypehintCheck ->getNonGenericObjectTypesWithGenericClass ($ type ) as [$ innerName , $ genericTypeNames ]) {
78
- $ errors [] = RuleErrorBuilder::message (sprintf (
79
- 'PHPDoc tag @mixin contains generic %s but does not specify its types: %s ' ,
80
- $ innerName ,
81
- implode (', ' , $ genericTypeNames ),
82
- ))
83
- ->identifier ('missingType.generics ' )
84
- ->build ();
85
- }
86
-
87
- if ($ this ->absentTypeChecks ) {
88
- foreach ($ this ->missingTypehintCheck ->getIterableTypesWithMissingValueTypehint ($ type ) as $ iterableType ) {
89
- $ iterableTypeDescription = $ iterableType ->describe (VerbosityLevel::typeOnly ());
90
- $ errors [] = RuleErrorBuilder::message (sprintf (
91
- '%s %s has PHPDoc tag @mixin with no value type specified in iterable type %s. ' ,
92
- $ classReflection ->getClassTypeDescription (),
93
- $ classReflection ->getDisplayName (),
94
- $ iterableTypeDescription ,
95
- ))
96
- ->tip (MissingTypehintCheck::MISSING_ITERABLE_VALUE_TYPE_TIP )
97
- ->identifier ('missingType.iterableValue ' )
98
- ->build ();
99
- }
100
-
101
- foreach ($ this ->missingTypehintCheck ->getCallablesWithMissingSignature ($ type ) as $ callableType ) {
102
- $ errors [] = RuleErrorBuilder::message (sprintf (
103
- '%s %s has PHPDoc tag @mixin with no signature specified for %s. ' ,
104
- $ classReflection ->getClassTypeDescription (),
105
- $ classReflection ->getDisplayName (),
106
- $ callableType ->describe (VerbosityLevel::typeOnly ()),
107
- ))->identifier ('missingType.callable ' )->build ();
108
- }
109
- }
110
-
111
- foreach ($ type ->getReferencedClasses () as $ class ) {
112
- if (!$ this ->reflectionProvider ->hasClass ($ class )) {
113
- $ errors [] = RuleErrorBuilder::message (sprintf ('PHPDoc tag @mixin contains unknown class %s. ' , $ class ))
114
- ->identifier ('class.notFound ' )
115
- ->discoveringSymbolsTip ()
116
- ->build ();
117
- } elseif ($ this ->reflectionProvider ->getClass ($ class )->isTrait ()) {
118
- $ errors [] = RuleErrorBuilder::message (sprintf ('PHPDoc tag @mixin contains invalid type %s. ' , $ class ))
119
- ->identifier ('mixin.trait ' )
120
- ->build ();
121
- } else {
122
- $ errors = array_merge (
123
- $ errors ,
124
- $ this ->classCheck ->checkClassNames ([
125
- new ClassNameNodePair ($ class , $ node ),
126
- ], $ this ->checkClassCaseSensitivity ),
127
- );
128
- }
129
- }
130
- }
131
-
132
- return $ errors ;
27
+ return $ this ->check ->check ($ node ->getClassReflection (), $ node ->getOriginalNode ());
133
28
}
134
29
135
30
}
0 commit comments