@@ -37,9 +37,25 @@ public function __construct(
37
37
*/
38
38
public function check (ClassReflection $ classReflection , ClassLike $ node ): array
39
39
{
40
- $ mixinTags = $ classReflection ->getMixinTags ();
41
40
$ errors = [];
42
- foreach ($ mixinTags as $ mixinTag ) {
41
+ foreach ($ this ->checkInTraitDefinitionContext ($ classReflection ) as $ error ) {
42
+ $ errors [] = $ error ;
43
+ }
44
+
45
+ foreach ($ this ->checkInTraitUseContext ($ classReflection , $ classReflection , $ node ) as $ error ) {
46
+ $ errors [] = $ error ;
47
+ }
48
+
49
+ return $ errors ;
50
+ }
51
+
52
+ /**
53
+ * @return list<IdentifierRuleError>
54
+ */
55
+ public function checkInTraitDefinitionContext (ClassReflection $ classReflection ): array
56
+ {
57
+ $ errors = [];
58
+ foreach ($ classReflection ->getMixinTags () as $ mixinTag ) {
43
59
$ type = $ mixinTag ->getType ();
44
60
if (!$ type ->canCallMethods ()->yes () || !$ type ->canAccessProperties ()->yes ()) {
45
61
$ errors [] = RuleErrorBuilder::message (sprintf ('PHPDoc tag @mixin contains non-object type %s. ' , $ type ->describe (VerbosityLevel::typeOnly ())))
@@ -48,6 +64,67 @@ public function check(ClassReflection $classReflection, ClassLike $node): array
48
64
continue ;
49
65
}
50
66
67
+ if (!$ this ->absentTypeChecks ) {
68
+ continue ;
69
+ }
70
+
71
+ foreach ($ this ->missingTypehintCheck ->getIterableTypesWithMissingValueTypehint ($ type ) as $ iterableType ) {
72
+ $ iterableTypeDescription = $ iterableType ->describe (VerbosityLevel::typeOnly ());
73
+ $ errors [] = RuleErrorBuilder::message (sprintf (
74
+ '%s %s has PHPDoc tag @mixin with no value type specified in iterable type %s. ' ,
75
+ $ classReflection ->getClassTypeDescription (),
76
+ $ classReflection ->getDisplayName (),
77
+ $ iterableTypeDescription ,
78
+ ))
79
+ ->tip (MissingTypehintCheck::MISSING_ITERABLE_VALUE_TYPE_TIP )
80
+ ->identifier ('missingType.iterableValue ' )
81
+ ->build ();
82
+ }
83
+
84
+ foreach ($ this ->missingTypehintCheck ->getNonGenericObjectTypesWithGenericClass ($ type ) as [$ innerName , $ genericTypeNames ]) {
85
+ $ errors [] = RuleErrorBuilder::message (sprintf (
86
+ 'PHPDoc tag @mixin contains generic %s but does not specify its types: %s ' ,
87
+ $ innerName ,
88
+ implode (', ' , $ genericTypeNames ),
89
+ ))
90
+ ->identifier ('missingType.generics ' )
91
+ ->build ();
92
+ }
93
+
94
+ foreach ($ this ->missingTypehintCheck ->getCallablesWithMissingSignature ($ type ) as $ callableType ) {
95
+ $ errors [] = RuleErrorBuilder::message (sprintf (
96
+ '%s %s has PHPDoc tag @mixin with no signature specified for %s. ' ,
97
+ $ classReflection ->getClassTypeDescription (),
98
+ $ classReflection ->getDisplayName (),
99
+ $ callableType ->describe (VerbosityLevel::typeOnly ()),
100
+ ))->identifier ('missingType.callable ' )->build ();
101
+ }
102
+ }
103
+
104
+ return $ errors ;
105
+ }
106
+
107
+ /**
108
+ * @return list<IdentifierRuleError>
109
+ */
110
+ public function checkInTraitUseContext (
111
+ ClassReflection $ reflection ,
112
+ ClassReflection $ implementingClassReflection ,
113
+ ClassLike $ node ,
114
+ ): array
115
+ {
116
+ if ($ reflection ->getNativeReflection ()->getName () === $ implementingClassReflection ->getName ()) {
117
+ $ phpDoc = $ reflection ->getResolvedPhpDoc ();
118
+ } else {
119
+ $ phpDoc = $ reflection ->getTraitContextResolvedPhpDoc ($ implementingClassReflection );
120
+ }
121
+ if ($ phpDoc === null ) {
122
+ return [];
123
+ }
124
+
125
+ $ errors = [];
126
+ foreach ($ phpDoc ->getMixinTags () as $ mixinTag ) {
127
+ $ type = $ mixinTag ->getType ();
51
128
if (
52
129
$ this ->unresolvableTypeHelper ->containsUnresolvableType ($ type )
53
130
) {
@@ -67,40 +144,6 @@ public function check(ClassReflection $classReflection, ClassLike $node): array
67
144
'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. ' ,
68
145
));
69
146
70
- foreach ($ this ->missingTypehintCheck ->getNonGenericObjectTypesWithGenericClass ($ type ) as [$ innerName , $ genericTypeNames ]) {
71
- $ errors [] = RuleErrorBuilder::message (sprintf (
72
- 'PHPDoc tag @mixin contains generic %s but does not specify its types: %s ' ,
73
- $ innerName ,
74
- implode (', ' , $ genericTypeNames ),
75
- ))
76
- ->identifier ('missingType.generics ' )
77
- ->build ();
78
- }
79
-
80
- if ($ this ->absentTypeChecks ) {
81
- foreach ($ this ->missingTypehintCheck ->getIterableTypesWithMissingValueTypehint ($ type ) as $ iterableType ) {
82
- $ iterableTypeDescription = $ iterableType ->describe (VerbosityLevel::typeOnly ());
83
- $ errors [] = RuleErrorBuilder::message (sprintf (
84
- '%s %s has PHPDoc tag @mixin with no value type specified in iterable type %s. ' ,
85
- $ classReflection ->getClassTypeDescription (),
86
- $ classReflection ->getDisplayName (),
87
- $ iterableTypeDescription ,
88
- ))
89
- ->tip (MissingTypehintCheck::MISSING_ITERABLE_VALUE_TYPE_TIP )
90
- ->identifier ('missingType.iterableValue ' )
91
- ->build ();
92
- }
93
-
94
- foreach ($ this ->missingTypehintCheck ->getCallablesWithMissingSignature ($ type ) as $ callableType ) {
95
- $ errors [] = RuleErrorBuilder::message (sprintf (
96
- '%s %s has PHPDoc tag @mixin with no signature specified for %s. ' ,
97
- $ classReflection ->getClassTypeDescription (),
98
- $ classReflection ->getDisplayName (),
99
- $ callableType ->describe (VerbosityLevel::typeOnly ()),
100
- ))->identifier ('missingType.callable ' )->build ();
101
- }
102
- }
103
-
104
147
foreach ($ type ->getReferencedClasses () as $ class ) {
105
148
if (!$ this ->reflectionProvider ->hasClass ($ class )) {
106
149
$ errors [] = RuleErrorBuilder::message (sprintf ('PHPDoc tag @mixin contains unknown class %s. ' , $ class ))
0 commit comments