diff --git a/src/Reflection/Php/PhpMethodReflection.php b/src/Reflection/Php/PhpMethodReflection.php index b141bcec7e..432fd69350 100644 --- a/src/Reflection/Php/PhpMethodReflection.php +++ b/src/Reflection/Php/PhpMethodReflection.php @@ -302,9 +302,30 @@ public function isPublic(): bool private function getReturnType(): Type { if ($this->returnType === null) { - $this->returnType = TypehintHelper::decideType( - $this->getNativeReturnType(), + $name = strtolower($this->getName()); + $returnType = $this->reflection->getReturnType(); + if ($returnType === null) { + if (in_array($name, ['__construct', '__destruct', '__unset', '__wakeup', '__clone'], true)) { + return $this->returnType = TypehintHelper::decideType(new VoidType(), $this->phpDocReturnType); + } + if ($name === '__tostring') { + return $this->returnType = TypehintHelper::decideType(new StringType(), $this->phpDocReturnType); + } + if ($name === '__isset') { + return $this->returnType = TypehintHelper::decideType(new BooleanType(), $this->phpDocReturnType); + } + if ($name === '__sleep') { + return $this->returnType = TypehintHelper::decideType(new ArrayType(new IntegerType(), new StringType()), $this->phpDocReturnType); + } + if ($name === '__set_state') { + return $this->returnType = TypehintHelper::decideType(new ObjectWithoutClassType(), $this->phpDocReturnType); + } + } + + $this->returnType = TypehintHelper::decideTypeFromReflection( + $returnType, $this->phpDocReturnType, + $this->declaringClass, ); } @@ -323,28 +344,8 @@ private function getPhpDocReturnType(): Type private function getNativeReturnType(): Type { if ($this->nativeReturnType === null) { - $returnType = $this->reflection->getReturnType(); - if ($returnType === null) { - $name = strtolower($this->getName()); - if (in_array($this->getName(), ['__construct', '__destruct', '__unset', '__wakeup', '__clone'], true)) { - return $this->nativeReturnType = new VoidType(); - } - if ($name === '__tostring') { - return $this->nativeReturnType = new StringType(); - } - if ($name === '__isset') { - return $this->nativeReturnType = new BooleanType(); - } - if ($name === '__sleep') { - return $this->nativeReturnType = new ArrayType(new IntegerType(), new StringType()); - } - if ($name === '__set_state') { - return $this->nativeReturnType = new ObjectWithoutClassType(); - } - } - $this->nativeReturnType = TypehintHelper::decideTypeFromReflection( - $returnType, + $this->reflection->getReturnType(), null, $this->declaringClass, ); diff --git a/tests/PHPStan/Analyser/nsrt/bug-to-string-type.php b/tests/PHPStan/Analyser/nsrt/bug-to-string-type.php new file mode 100644 index 0000000000..31e1b97281 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-to-string-type.php @@ -0,0 +1,36 @@ +__toString()); + assertNativeType('mixed', $test->__toString()); +}