diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index a085f8f07b..185c2c5530 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -2649,7 +2649,7 @@ static function (): void { $nodeCallback(new InvalidateExprNode($expr->var), $scope); $scope = $scope->invalidateExpression($expr->var, true); } - if ($parametersAcceptor !== null) { + if ($parametersAcceptor !== null && !$methodReflection->isStatic()) { $selfOutType = $methodReflection->getSelfOutType(); if ($selfOutType !== null) { $scope = $scope->assignExpression( diff --git a/src/Rules/PhpDoc/IncompatibleSelfOutTypeRule.php b/src/Rules/PhpDoc/IncompatibleSelfOutTypeRule.php index 46164fff02..b3092b7a4a 100644 --- a/src/Rules/PhpDoc/IncompatibleSelfOutTypeRule.php +++ b/src/Rules/PhpDoc/IncompatibleSelfOutTypeRule.php @@ -34,19 +34,24 @@ public function processNode(Node $node, Scope $scope): array $classReflection = $method->getDeclaringClass(); $classType = new ObjectType($classReflection->getName(), null, $classReflection); - if ($classType->isSuperTypeOf($selfOutType)->yes()) { - return []; - } - - return [ - RuleErrorBuilder::message(sprintf( + $errors = []; + if (!$classType->isSuperTypeOf($selfOutType)->yes()) { + $errors[] = RuleErrorBuilder::message(sprintf( 'Self-out type %s of method %s::%s is not subtype of %s.', $selfOutType->describe(VerbosityLevel::precise()), $classReflection->getName(), $method->getName(), $classType->describe(VerbosityLevel::precise()), - ))->identifier('selfOut.type')->build(), - ]; + ))->identifier('selfOut.type')->build(); + } + + if ($method->isStatic()) { + $errors[] = RuleErrorBuilder::message(sprintf('PHPDoc tag @phpstan-self-out is not supported above static method %s::%s().', $classReflection->getName(), $method->getName())) + ->identifier('selfOut.static') + ->build(); + } + + return $errors; } } diff --git a/tests/PHPStan/Analyser/nsrt/self-out.php b/tests/PHPStan/Analyser/nsrt/self-out.php index d4de8dbf84..fa623d2d5a 100644 --- a/tests/PHPStan/Analyser/nsrt/self-out.php +++ b/tests/PHPStan/Analyser/nsrt/self-out.php @@ -50,6 +50,14 @@ public function setData($data) { */ public function test(): void { } + + /** + * @phpstan-self-out self + */ + public static function selfOutWithStaticMethod(): void + { + + } } /** @@ -94,4 +102,7 @@ function () { $i->setData(true); assertType('SelfOut\\a', $i); + + $i->selfOutWithStaticMethod(); + assertType('SelfOut\\a', $i); }; diff --git a/tests/PHPStan/Rules/PhpDoc/IncompatibleSelfOutTypeRuleTest.php b/tests/PHPStan/Rules/PhpDoc/IncompatibleSelfOutTypeRuleTest.php index 96ea886257..dae30a1039 100644 --- a/tests/PHPStan/Rules/PhpDoc/IncompatibleSelfOutTypeRuleTest.php +++ b/tests/PHPStan/Rules/PhpDoc/IncompatibleSelfOutTypeRuleTest.php @@ -27,6 +27,10 @@ public function testRule(): void 'Self-out type IncompatibleSelfOutType\A|null of method IncompatibleSelfOutType\A::four is not subtype of IncompatibleSelfOutType\A.', 28, ], + [ + 'PHPDoc tag @phpstan-self-out is not supported above static method IncompatibleSelfOutType\Foo::selfOutStatic().', + 38, + ], ]); } diff --git a/tests/PHPStan/Rules/PhpDoc/data/incompatible-self-out-type.php b/tests/PHPStan/Rules/PhpDoc/data/incompatible-self-out-type.php index a0c4e977a0..018b6b1c98 100644 --- a/tests/PHPStan/Rules/PhpDoc/data/incompatible-self-out-type.php +++ b/tests/PHPStan/Rules/PhpDoc/data/incompatible-self-out-type.php @@ -27,3 +27,17 @@ public function three(); */ public function four(); } + +/** + * @template T + */ +class Foo +{ + + /** @phpstan-self-out self */ + public static function selfOutStatic(): void + { + + } + +}