13
13
use PHPStan \TrinaryLogic ;
14
14
use PHPStan \Type \Generic \TemplateTypeHelper ;
15
15
use PHPStan \Type \MixedType ;
16
+ use PHPStan \Type \StaticType ;
16
17
use PHPStan \Type \Type ;
17
18
use PHPStan \Type \TypehintHelper ;
19
+ use PHPStan \Type \TypeTraverser ;
18
20
use PHPStan \Type \VerbosityLevel ;
19
21
use PHPStan \Type \VoidType ;
20
22
@@ -62,6 +64,7 @@ public function processNode(Node $node, Scope $scope): array
62
64
$ parameters = ParametersAcceptorSelector::selectSingle ($ method ->getVariants ());
63
65
64
66
$ errors = [];
67
+ $ declaringClass = $ method ->getDeclaringClass ();
65
68
foreach ($ this ->collectParentMethods ($ methodName , $ method ->getDeclaringClass ()) as $ parentMethod ) {
66
69
$ parentVariants = $ parentMethod ->getVariants ();
67
70
if (count ($ parentVariants ) !== 1 ) {
@@ -72,7 +75,7 @@ public function processNode(Node $node, Scope $scope): array
72
75
continue ;
73
76
}
74
77
75
- [$ returnTypeCompatibility , $ returnType , $ parentReturnType ] = $ this ->checkReturnTypeCompatibility ($ parameters , $ parentParameters );
78
+ [$ returnTypeCompatibility , $ returnType , $ parentReturnType ] = $ this ->checkReturnTypeCompatibility ($ declaringClass , $ parameters , $ parentParameters );
76
79
if ($ returnTypeCompatibility ->no () || (!$ returnTypeCompatibility ->yes () && $ this ->reportMaybes )) {
77
80
$ errors [] = RuleErrorBuilder::message (sprintf (
78
81
'Return type (%s) of method %s::%s() should be %s with return type (%s) of method %s::%s() ' ,
@@ -86,7 +89,7 @@ public function processNode(Node $node, Scope $scope): array
86
89
))->build ();
87
90
}
88
91
89
- $ parameterResults = $ this ->checkParameterTypeCompatibility ($ parameters ->getParameters (), $ parentParameters ->getParameters ());
92
+ $ parameterResults = $ this ->checkParameterTypeCompatibility ($ declaringClass , $ parameters ->getParameters (), $ parentParameters ->getParameters ());
90
93
foreach ($ parameterResults as $ parameterIndex => [$ parameterResult , $ parameterType , $ parentParameterType ]) {
91
94
if ($ parameterResult ->yes ()) {
92
95
continue ;
@@ -149,6 +152,7 @@ private function collectParentMethods(string $methodName, ClassReflection $class
149
152
* @return array{TrinaryLogic, Type, Type}
150
153
*/
151
154
private function checkReturnTypeCompatibility (
155
+ ClassReflection $ declaringClass ,
152
156
ParametersAcceptorWithPhpDocs $ currentVariant ,
153
157
ParametersAcceptorWithPhpDocs $ parentVariant
154
158
): array
@@ -157,10 +161,11 @@ private function checkReturnTypeCompatibility(
157
161
$ currentVariant ->getNativeReturnType (),
158
162
TemplateTypeHelper::resolveToBounds ($ currentVariant ->getPhpDocReturnType ())
159
163
);
160
- $ parentReturnType = TypehintHelper::decideType (
164
+ $ originalParentReturnType = TypehintHelper::decideType (
161
165
$ parentVariant ->getNativeReturnType (),
162
166
TemplateTypeHelper::resolveToBounds ($ parentVariant ->getPhpDocReturnType ())
163
167
);
168
+ $ parentReturnType = $ this ->transformStaticType ($ declaringClass , $ originalParentReturnType );
164
169
// Allow adding `void` return type hints when the parent defines no return type
165
170
if ($ returnType instanceof VoidType && $ parentReturnType instanceof MixedType) {
166
171
return [TrinaryLogic::createYes (), $ returnType , $ parentReturnType ];
@@ -174,7 +179,7 @@ private function checkReturnTypeCompatibility(
174
179
return [$ parentReturnType ->isSuperTypeOf ($ returnType ), TypehintHelper::decideType (
175
180
$ currentVariant ->getNativeReturnType (),
176
181
$ currentVariant ->getPhpDocReturnType ()
177
- ), $ parentReturnType ];
182
+ ), $ originalParentReturnType ];
178
183
}
179
184
180
185
/**
@@ -183,6 +188,7 @@ private function checkReturnTypeCompatibility(
183
188
* @return array<int, array{TrinaryLogic, Type, Type}>
184
189
*/
185
190
private function checkParameterTypeCompatibility (
191
+ ClassReflection $ declaringClass ,
186
192
array $ parameters ,
187
193
array $ parentParameters
188
194
): array
@@ -198,18 +204,34 @@ private function checkParameterTypeCompatibility(
198
204
$ parameter ->getNativeType (),
199
205
TemplateTypeHelper::resolveToBounds ($ parameter ->getPhpDocType ())
200
206
);
201
- $ parentParameterType = TypehintHelper::decideType (
207
+ $ originalParameterType = TypehintHelper::decideType (
202
208
$ parentParameter ->getNativeType (),
203
209
TemplateTypeHelper::resolveToBounds ($ parentParameter ->getPhpDocType ())
204
210
);
211
+ $ parentParameterType = $ this ->transformStaticType ($ declaringClass , $ originalParameterType );
205
212
206
213
$ parameterResults [] = [$ parameterType ->isSuperTypeOf ($ parentParameterType ), TypehintHelper::decideType (
207
214
$ parameter ->getNativeType (),
208
215
$ parameter ->getPhpDocType ()
209
- ), $ parentParameterType ];
216
+ ), $ originalParameterType ];
210
217
}
211
218
212
219
return $ parameterResults ;
213
220
}
214
221
222
+ private function transformStaticType (ClassReflection $ declaringClass , Type $ type ): Type
223
+ {
224
+ return TypeTraverser::map ($ type , static function (Type $ type , callable $ traverse ) use ($ declaringClass ): Type {
225
+ if ($ type instanceof StaticType) {
226
+ $ changedType = $ type ->changeBaseClass ($ declaringClass );
227
+ if ($ declaringClass ->isFinal ()) {
228
+ $ changedType = $ changedType ->getStaticObjectType ();
229
+ }
230
+ return $ traverse ($ changedType );
231
+ }
232
+
233
+ return $ traverse ($ type );
234
+ });
235
+ }
236
+
215
237
}
0 commit comments