Skip to content

Commit 42c3d05

Browse files
github-actions[bot]staabm
authored andcommitted
Fix "T is never" in conditional return type reported as always false
- Added TemplateType handling in NonAcceptingNeverType::isSuperTypeOf() and NeverType::isSuperTypeOf() to return Maybe instead of No, since a template type can be instantiated to never - New regression tests in tests/PHPStan/Rules/PhpDoc/data/bug-9634.php and tests/PHPStan/Analyser/nsrt/bug-9634.php Closes phpstan/phpstan#9634
1 parent 47f36d8 commit 42c3d05

5 files changed

Lines changed: 67 additions & 0 deletions

File tree

src/Type/NeverType.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PHPStan\ShouldNotHappenException;
1515
use PHPStan\TrinaryLogic;
1616
use PHPStan\Type\Enum\EnumCaseObjectType;
17+
use PHPStan\Type\Generic\TemplateType;
1718
use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
1819
use PHPStan\Type\Traits\NonGenericTypeTrait;
1920
use PHPStan\Type\Traits\NonRemoveableTypeTrait;
@@ -81,6 +82,10 @@ public function isSuperTypeOf(Type $type): IsSuperTypeOfResult
8182
return IsSuperTypeOfResult::createYes();
8283
}
8384

85+
if ($type instanceof TemplateType) {
86+
return IsSuperTypeOfResult::createMaybe();
87+
}
88+
8489
return IsSuperTypeOfResult::createNo();
8590
}
8691

src/Type/NonAcceptingNeverType.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace PHPStan\Type;
44

5+
use PHPStan\Type\Generic\TemplateType;
6+
57
/** @api */
68
class NonAcceptingNeverType extends NeverType
79
{
@@ -21,6 +23,10 @@ public function isSuperTypeOf(Type $type): IsSuperTypeOfResult
2123
return IsSuperTypeOfResult::createMaybe();
2224
}
2325

26+
if ($type instanceof TemplateType) {
27+
return IsSuperTypeOfResult::createMaybe();
28+
}
29+
2430
return IsSuperTypeOfResult::createNo();
2531
}
2632

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug9634;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/** @template T */
8+
interface Option {
9+
/** @return self<never> */
10+
static function none(): self;
11+
12+
/** @return T */
13+
function unwrap(): mixed;
14+
15+
/**
16+
* @return (T is never ? false : bool)
17+
*/
18+
function isSome(): bool;
19+
}
20+
21+
/** @param Option<never> $o */
22+
function f(Option $o): void {
23+
assertType('false', $o->isSome());
24+
}
25+
26+
/** @param Option<int> $o */
27+
function g(Option $o): void {
28+
assertType('bool', $o->isSome());
29+
}

tests/PHPStan/Rules/PhpDoc/MethodConditionalReturnTypeRuleTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,9 @@ public function testBug11939(): void
102102
$this->analyse([__DIR__ . '/data/bug-11939.php'], []);
103103
}
104104

105+
public function testBug9634(): void
106+
{
107+
$this->analyse([__DIR__ . '/data/bug-9634.php'], []);
108+
}
109+
105110
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug9634;
4+
5+
/** @template T */
6+
interface Option {
7+
/** @return self<never> */
8+
static function none(): self;
9+
10+
/** @return T */
11+
function unwrap(): mixed;
12+
13+
/**
14+
* @return (T is never ? false : bool)
15+
*/
16+
function isSome(): bool;
17+
}
18+
19+
/** @param Option<never> $o */
20+
function f(Option $o): true {
21+
return $o->isSome();
22+
}

0 commit comments

Comments
 (0)