diff --git a/src/Type/NeverType.php b/src/Type/NeverType.php index 2da6f5e9fa..cf1d90f7a1 100644 --- a/src/Type/NeverType.php +++ b/src/Type/NeverType.php @@ -14,6 +14,7 @@ use PHPStan\ShouldNotHappenException; use PHPStan\TrinaryLogic; use PHPStan\Type\Enum\EnumCaseObjectType; +use PHPStan\Type\Generic\TemplateType; use PHPStan\Type\Traits\NonGeneralizableTypeTrait; use PHPStan\Type\Traits\NonGenericTypeTrait; use PHPStan\Type\Traits\NonRemoveableTypeTrait; @@ -81,6 +82,10 @@ public function isSuperTypeOf(Type $type): IsSuperTypeOfResult return IsSuperTypeOfResult::createYes(); } + if ($type instanceof TemplateType) { + return IsSuperTypeOfResult::createMaybe(); + } + return IsSuperTypeOfResult::createNo(); } diff --git a/src/Type/NonAcceptingNeverType.php b/src/Type/NonAcceptingNeverType.php index dd14d3f9d2..38b274f3f8 100644 --- a/src/Type/NonAcceptingNeverType.php +++ b/src/Type/NonAcceptingNeverType.php @@ -2,6 +2,8 @@ namespace PHPStan\Type; +use PHPStan\Type\Generic\TemplateType; + /** @api */ class NonAcceptingNeverType extends NeverType { @@ -21,6 +23,10 @@ public function isSuperTypeOf(Type $type): IsSuperTypeOfResult return IsSuperTypeOfResult::createMaybe(); } + if ($type instanceof TemplateType) { + return IsSuperTypeOfResult::createMaybe(); + } + return IsSuperTypeOfResult::createNo(); } diff --git a/tests/PHPStan/Analyser/nsrt/bug-9634.php b/tests/PHPStan/Analyser/nsrt/bug-9634.php new file mode 100644 index 0000000000..ad7710b420 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-9634.php @@ -0,0 +1,29 @@ + */ + static function none(): self; + + /** @return T */ + function unwrap(): mixed; + + /** + * @return (T is never ? false : bool) + */ + function isSome(): bool; +} + +/** @param Option $o */ +function f(Option $o): void { + assertType('false', $o->isSome()); +} + +/** @param Option $o */ +function g(Option $o): void { + assertType('bool', $o->isSome()); +} diff --git a/tests/PHPStan/Rules/PhpDoc/MethodConditionalReturnTypeRuleTest.php b/tests/PHPStan/Rules/PhpDoc/MethodConditionalReturnTypeRuleTest.php index 35ab5c2e4e..be9c8d0097 100644 --- a/tests/PHPStan/Rules/PhpDoc/MethodConditionalReturnTypeRuleTest.php +++ b/tests/PHPStan/Rules/PhpDoc/MethodConditionalReturnTypeRuleTest.php @@ -102,4 +102,9 @@ public function testBug11939(): void $this->analyse([__DIR__ . '/data/bug-11939.php'], []); } + public function testBug9634(): void + { + $this->analyse([__DIR__ . '/data/bug-9634.php'], []); + } + } diff --git a/tests/PHPStan/Rules/PhpDoc/data/bug-9634.php b/tests/PHPStan/Rules/PhpDoc/data/bug-9634.php new file mode 100644 index 0000000000..25ed190521 --- /dev/null +++ b/tests/PHPStan/Rules/PhpDoc/data/bug-9634.php @@ -0,0 +1,22 @@ + */ + static function none(): self; + + /** @return T */ + function unwrap(): mixed; + + /** + * @return (T is never ? false : bool) + */ + function isSome(): bool; +} + +/** @param Option $o */ +function f(Option $o): true { + return $o->isSome(); +}