1313use PHPStan \TrinaryLogic ;
1414use PHPStan \Type \Generic \TemplateTypeHelper ;
1515use PHPStan \Type \MixedType ;
16+ use PHPStan \Type \StaticType ;
1617use PHPStan \Type \Type ;
1718use PHPStan \Type \TypehintHelper ;
19+ use PHPStan \Type \TypeTraverser ;
1820use PHPStan \Type \VerbosityLevel ;
1921use PHPStan \Type \VoidType ;
2022
@@ -62,6 +64,7 @@ public function processNode(Node $node, Scope $scope): array
6264 $ parameters = ParametersAcceptorSelector::selectSingle ($ method ->getVariants ());
6365
6466 $ errors = [];
67+ $ declaringClass = $ method ->getDeclaringClass ();
6568 foreach ($ this ->collectParentMethods ($ methodName , $ method ->getDeclaringClass ()) as $ parentMethod ) {
6669 $ parentVariants = $ parentMethod ->getVariants ();
6770 if (count ($ parentVariants ) !== 1 ) {
@@ -72,7 +75,7 @@ public function processNode(Node $node, Scope $scope): array
7275 continue ;
7376 }
7477
75- [$ returnTypeCompatibility , $ returnType , $ parentReturnType ] = $ this ->checkReturnTypeCompatibility ($ parameters , $ parentParameters );
78+ [$ returnTypeCompatibility , $ returnType , $ parentReturnType ] = $ this ->checkReturnTypeCompatibility ($ declaringClass , $ parameters , $ parentParameters );
7679 if ($ returnTypeCompatibility ->no () || (!$ returnTypeCompatibility ->yes () && $ this ->reportMaybes )) {
7780 $ errors [] = RuleErrorBuilder::message (sprintf (
7881 '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
8689 ))->build ();
8790 }
8891
89- $ parameterResults = $ this ->checkParameterTypeCompatibility ($ parameters ->getParameters (), $ parentParameters ->getParameters ());
92+ $ parameterResults = $ this ->checkParameterTypeCompatibility ($ declaringClass , $ parameters ->getParameters (), $ parentParameters ->getParameters ());
9093 foreach ($ parameterResults as $ parameterIndex => [$ parameterResult , $ parameterType , $ parentParameterType ]) {
9194 if ($ parameterResult ->yes ()) {
9295 continue ;
@@ -149,6 +152,7 @@ private function collectParentMethods(string $methodName, ClassReflection $class
149152 * @return array{TrinaryLogic, Type, Type}
150153 */
151154 private function checkReturnTypeCompatibility (
155+ ClassReflection $ declaringClass ,
152156 ParametersAcceptorWithPhpDocs $ currentVariant ,
153157 ParametersAcceptorWithPhpDocs $ parentVariant
154158 ): array
@@ -157,10 +161,11 @@ private function checkReturnTypeCompatibility(
157161 $ currentVariant ->getNativeReturnType (),
158162 TemplateTypeHelper::resolveToBounds ($ currentVariant ->getPhpDocReturnType ())
159163 );
160- $ parentReturnType = TypehintHelper::decideType (
164+ $ originalParentReturnType = TypehintHelper::decideType (
161165 $ parentVariant ->getNativeReturnType (),
162166 TemplateTypeHelper::resolveToBounds ($ parentVariant ->getPhpDocReturnType ())
163167 );
168+ $ parentReturnType = $ this ->transformStaticType ($ declaringClass , $ originalParentReturnType );
164169 // Allow adding `void` return type hints when the parent defines no return type
165170 if ($ returnType instanceof VoidType && $ parentReturnType instanceof MixedType) {
166171 return [TrinaryLogic::createYes (), $ returnType , $ parentReturnType ];
@@ -174,7 +179,7 @@ private function checkReturnTypeCompatibility(
174179 return [$ parentReturnType ->isSuperTypeOf ($ returnType ), TypehintHelper::decideType (
175180 $ currentVariant ->getNativeReturnType (),
176181 $ currentVariant ->getPhpDocReturnType ()
177- ), $ parentReturnType ];
182+ ), $ originalParentReturnType ];
178183 }
179184
180185 /**
@@ -183,6 +188,7 @@ private function checkReturnTypeCompatibility(
183188 * @return array<int, array{TrinaryLogic, Type, Type}>
184189 */
185190 private function checkParameterTypeCompatibility (
191+ ClassReflection $ declaringClass ,
186192 array $ parameters ,
187193 array $ parentParameters
188194 ): array
@@ -198,18 +204,34 @@ private function checkParameterTypeCompatibility(
198204 $ parameter ->getNativeType (),
199205 TemplateTypeHelper::resolveToBounds ($ parameter ->getPhpDocType ())
200206 );
201- $ parentParameterType = TypehintHelper::decideType (
207+ $ originalParameterType = TypehintHelper::decideType (
202208 $ parentParameter ->getNativeType (),
203209 TemplateTypeHelper::resolveToBounds ($ parentParameter ->getPhpDocType ())
204210 );
211+ $ parentParameterType = $ this ->transformStaticType ($ declaringClass , $ originalParameterType );
205212
206213 $ parameterResults [] = [$ parameterType ->isSuperTypeOf ($ parentParameterType ), TypehintHelper::decideType (
207214 $ parameter ->getNativeType (),
208215 $ parameter ->getPhpDocType ()
209- ), $ parentParameterType ];
216+ ), $ originalParameterType ];
210217 }
211218
212219 return $ parameterResults ;
213220 }
214221
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+
215237}
0 commit comments