Skip to content
15 changes: 15 additions & 0 deletions src/Rules/TooWideTypehints/TooWideTypeCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace PHPStan\Rules\TooWideTypehints;

use PhpParser\Node\Expr\ConstFetch;
use PHPStan\Analyser\Scope;
use PHPStan\DependencyInjection\AutowiredParameter;
use PHPStan\DependencyInjection\AutowiredService;
Expand All @@ -25,8 +26,10 @@
use PHPStan\Type\VerbosityLevel;
use PHPStan\Type\VoidType;
use function count;
use function in_array;
use function lcfirst;
use function sprintf;
use function strtolower;

#[AutowiredService]
final class TooWideTypeCheck
Expand Down Expand Up @@ -205,6 +208,18 @@ public function checkFunctionReturnType(

$returnType = TypeCombinator::union(...$returnTypes);

if (
count($returnStatements) === 1
&& $returnStatements[0]->getReturnNode()->expr instanceof ConstFetch
&& in_array(strtolower($returnStatements[0]->getReturnNode()->expr->name->toString()), ['true', 'false'], true)
&& (
$nativeFunctionReturnType->isBoolean()->yes()
|| $phpDocFunctionReturnType->isBoolean()->yes()
)
) {
return [];
}

$unionMessagePattern = sprintf('%s never returns %%s so it can be removed from the return type.', $functionDescription);
$boolMessagePattern = sprintf('%s never returns %%s so the return type can be changed to %%s.', $functionDescription);

Expand Down
26 changes: 9 additions & 17 deletions tests/PHPStan/Analyser/AnalyserIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1107,23 +1107,15 @@ public function testBug8376(): void
public function testAssertDocblock(): void
{
$errors = $this->runAnalyse(__DIR__ . '/nsrt/assert-docblock.php');
$this->assertCount(8, $errors);
$this->assertSame('Function AssertDocblock\validateStringArrayIfTrue() never returns false so the return type can be changed to true.', $errors[0]->getMessage());
$this->assertSame(17, $errors[0]->getLine());
$this->assertSame('Function AssertDocblock\validateStringArrayIfFalse() never returns true so the return type can be changed to false.', $errors[1]->getMessage());
$this->assertSame(25, $errors[1]->getLine());
$this->assertSame('Function AssertDocblock\validateStringOrIntArray() never returns true so the return type can be changed to false.', $errors[2]->getMessage());
$this->assertSame(34, $errors[2]->getLine());
$this->assertSame('Function AssertDocblock\validateStringOrNonEmptyIntArray() never returns true so the return type can be changed to false.', $errors[3]->getMessage());
$this->assertSame(44, $errors[3]->getLine());
$this->assertSame('Call to method AssertDocblock\A::testInt() with string will always evaluate to false.', $errors[4]->getMessage());
$this->assertSame(218, $errors[4]->getLine());
$this->assertSame('Call to method AssertDocblock\A::testNotInt() with string will always evaluate to true.', $errors[5]->getMessage());
$this->assertSame(224, $errors[5]->getLine());
$this->assertSame('Call to method AssertDocblock\A::testInt() with int will always evaluate to true.', $errors[6]->getMessage());
$this->assertSame(232, $errors[6]->getLine());
$this->assertSame('Call to method AssertDocblock\A::testNotInt() with int will always evaluate to false.', $errors[7]->getMessage());
$this->assertSame(238, $errors[7]->getLine());
$this->assertCount(4, $errors);
$this->assertSame('Call to method AssertDocblock\A::testInt() with string will always evaluate to false.', $errors[0]->getMessage());
$this->assertSame(218, $errors[0]->getLine());
$this->assertSame('Call to method AssertDocblock\A::testNotInt() with string will always evaluate to true.', $errors[1]->getMessage());
$this->assertSame(224, $errors[1]->getLine());
$this->assertSame('Call to method AssertDocblock\A::testInt() with int will always evaluate to true.', $errors[2]->getMessage());
$this->assertSame(232, $errors[2]->getLine());
$this->assertSame('Call to method AssertDocblock\A::testNotInt() with int will always evaluate to false.', $errors[3]->getMessage());
$this->assertSame(238, $errors[3]->getLine());
}

#[RequiresPhp('>= 8.0')]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ public function testBug13384cPhp82(): void
'Function Bug13384c\doFooPhpdoc2() never returns true so the return type can be changed to false.',
100,
],
[
'Function Bug13384c\returnsTrueUnionReturn() never returns int so it can be removed from the return type.',
130,
],
[
'Function Bug13384c\returnsTruePhpdocUnionReturn() never returns int so it can be removed from the return type.',
137,
],
]);
}

Expand All @@ -111,12 +119,29 @@ public function testBug13384cPrePhp82(): void
'Function Bug13384c\doFooPhpdoc2() never returns true so the return type can be changed to false.',
100,
],
[
'Function Bug13384c\returnsTrueUnionReturn() never returns int so it can be removed from the return type.',
130,
],
[
'Function Bug13384c\returnsTruePhpdocUnionReturn() never returns int so it can be removed from the return type.',
137,
],
]);
}

public function testBug13384cOff(): void
{
$this->analyse([__DIR__ . '/data/bug-13384c.php'], []);
$this->analyse([__DIR__ . '/data/bug-13384c.php'], [
[
'Function Bug13384c\returnsTrueUnionReturn() never returns int so it can be removed from the return type.',
130,
],
[
'Function Bug13384c\returnsTruePhpdocUnionReturn() never returns int so it can be removed from the return type.',
137,
],
]);
}

public function testNestedTooWideType(): void
Expand Down
63 changes: 48 additions & 15 deletions tests/PHPStan/Rules/TooWideTypehints/data/bug-13384c.php
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<?php
<?php // lint >= 8.0

namespace Bug13384c;

function doFoo(): bool {
return false;
return returnsFalse();
}

function doFoo2(): bool {
return true;
return returnsTrue();
}

function doFoo3(): bool {
Expand All @@ -20,22 +20,22 @@ function doFoo3(): bool {

class Bug13384c {
public function doBarPublic(): bool {
return false;
return returnsFalse();
}

/**
* @return false
*/
private function doBarPhpdocReturn(): bool {
return false;
return returnsFalse();
}

private function doBar(): bool {
return false;
return returnsFalse();
}

private function doBar2(): bool {
return true;
return returnsTrue();
}

private function doBar3(): bool {
Expand All @@ -46,25 +46,25 @@ private function doBar3(): bool {
}

private function doBarMixed() {
return true;
return returnsTrue();
}

/**
* @return bool
*/
private function doBarPhpdoc() {
return true;
return returnsTrue();
}

}

class Bug13384Static {
private static function doBar(): bool {
return false;
return returnsFalse();
}

private static function doBar2(): bool {
return true;
return returnsTrue();
}

private static function doBar3(): bool {
Expand All @@ -75,14 +75,14 @@ private static function doBar3(): bool {
}

private static function doBarMixed() {
return true;
return returnsTrue();
}

/**
* @return bool
*/
private static function doBarPhpdoc() {
return true;
return returnsTrue();
}

}
Expand All @@ -91,16 +91,49 @@ private static function doBarPhpdoc() {
* @return bool
*/
function doFooPhpdoc() {
return true;
return returnsTrue();
}

/**
* @return bool
*/
function doFooPhpdoc2() {
return false;
return returnsFalse();
}

function doFooMixed() {
return returnsTrue();
}

/**
* @return true
*/
function returnsTrue(): bool {
return true;
}

/**
* @return false
*/
function returnsFalse(): bool {
return false;
}

function returnsTrueNoPhpdoc(): bool {
return true;
}

function returnsFalseNoPhpdoc(): bool {
return false;
}

function returnsTrueUnionReturn(): int|bool {
return true;
}

/**
* @return int|bool
*/
function returnsTruePhpdocUnionReturn() {
return true;
}
Loading