diff --git a/src/Type/Generic/TemplateTypeTrait.php b/src/Type/Generic/TemplateTypeTrait.php index d5d15a26abf..4bb15474666 100644 --- a/src/Type/Generic/TemplateTypeTrait.php +++ b/src/Type/Generic/TemplateTypeTrait.php @@ -409,6 +409,9 @@ public function tryRemove(Type $typeToRemove): ?Type if ($this->getBound() === $bound || $this->getBound()->equals($bound)) { return null; } + if ($bound instanceof NeverType) { + return $bound; + } return TemplateTypeFactory::create( $this->getScope(), diff --git a/src/Type/ObjectType.php b/src/Type/ObjectType.php index 139315d1c4c..15a60c8c223 100644 --- a/src/Type/ObjectType.php +++ b/src/Type/ObjectType.php @@ -2094,6 +2094,9 @@ public function tryRemove(Type $typeToRemove): ?Type // time, without rebuilding the subtracted union once per member. $membersToRemove = []; foreach ($typeToRemove->getTypes() as $member) { + if ($member->isSuperTypeOf($this)->yes()) { + return new NeverType(); + } $isSuperTypeOfMember = $this->isSuperTypeOf($member); if ($isSuperTypeOfMember->yes()) { $membersToRemove[] = $member; diff --git a/tests/PHPStan/Rules/Exceptions/ThrowsVoidFunctionWithExplicitThrowPointRuleTest.php b/tests/PHPStan/Rules/Exceptions/ThrowsVoidFunctionWithExplicitThrowPointRuleTest.php index 207b05bb76f..bc6191d89ad 100644 --- a/tests/PHPStan/Rules/Exceptions/ThrowsVoidFunctionWithExplicitThrowPointRuleTest.php +++ b/tests/PHPStan/Rules/Exceptions/ThrowsVoidFunctionWithExplicitThrowPointRuleTest.php @@ -104,4 +104,11 @@ public function testBug13515(): void $this->analyse([__DIR__ . '/data/bug-13515.php'], []); } + public function testBug15392(): void + { + $this->missingCheckedExceptionInThrows = false; + $this->checkedExceptionClasses = []; + $this->analyse([__DIR__ . '/data/bug-15392.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Exceptions/data/bug-15392.php b/tests/PHPStan/Rules/Exceptions/data/bug-15392.php new file mode 100644 index 00000000000..c649a2294a0 --- /dev/null +++ b/tests/PHPStan/Rules/Exceptions/data/bug-15392.php @@ -0,0 +1,33 @@ +isSuperTypeOf($this)->yes()) return new NeverType(); */ + zval selfZv; + ZVAL_OBJ(&selfZv, self); + zv::Val memberIsSuperType = pt_type_call(Z_OBJ_P(member), PT_LC("issupertypeof"), 1, &selfZv); + if (UNEXPECTED(memberIsSuperType.isUndef())) return zv::Val(); + zend_long memberIsSuperTypeValue = pt_type_result_trinary(memberIsSuperType.raw()); + if (UNEXPECTED(memberIsSuperTypeValue < 0)) return zv::Val(); + if (memberIsSuperTypeValue == PT_TRI_YES) return pt_type_new_never_type(); zv::Val isSuperTypeOfMember = thisCall(PT_LC("issupertypeof"), otIsSuperTypeOf, 1, member, [&]() { return isSuperTypeOf(member); }); if (UNEXPECTED(isSuperTypeOfMember.isUndef())) return zv::Val(); zend_long memberValue = pt_type_result_trinary(isSuperTypeOfMember.raw()); diff --git a/turbo-ext/src/TypeTraits.cpp b/turbo-ext/src/TypeTraits.cpp index 6272c721393..a3d8e6e77b3 100644 --- a/turbo-ext/src/TypeTraits.cpp +++ b/turbo-ext/src/TypeTraits.cpp @@ -4705,6 +4705,8 @@ class TemplateTypeTrait zv::Val equal = pt_type_op(Z_OBJ_P(boundOnceMore.raw()), PT_OP_EQUALS, 1, removed.raw()); if (UNEXPECTED(equal.isUndef())) return zv::Val(); if (zend_is_true(equal.raw())) return zv::Val::null(); + /* if ($bound instanceof NeverType) return $bound; */ + if (zv::Ref(removed.raw()).instanceOf(pt_ce_never_type)) return removed; return recreateKeepingDefault(removed.raw()); }