diff --git a/src/Reflection/Php/PhpClassReflectionExtension.php b/src/Reflection/Php/PhpClassReflectionExtension.php index a31e70433e3..c4ad8bfbfad 100644 --- a/src/Reflection/Php/PhpClassReflectionExtension.php +++ b/src/Reflection/Php/PhpClassReflectionExtension.php @@ -1317,7 +1317,8 @@ private function findMethodPhpDocIncludingAncestors( if ($resolved !== null) { return [$resolved, $declaringClass]; } - if (!$this->stubPhpDocProvider->isKnownClass($declaringClassName)) { + $isKnownClass = $this->stubPhpDocProvider->isKnownClass($declaringClassName); + if (!$isKnownClass && !$declaringClass->isBuiltin()) { return null; } @@ -1335,6 +1336,10 @@ private function findMethodPhpDocIncludingAncestors( continue; } + if (!$isKnownClass && $ancestor->isGeneric()) { + continue; + } + return [$resolved, $ancestor]; } diff --git a/stubs/date.stub b/stubs/date.stub index 8191057c642..3cb6da0d1de 100644 --- a/stubs/date.stub +++ b/stubs/date.stub @@ -41,12 +41,6 @@ interface DateTimeInterface { {} } -class DateTimeImmutable implements DateTimeInterface { -} - -class DateTime implements DateTimeInterface { -} - /** @return \DateInterval&object{days:int} */ function date_diff(\DateTimeInterface $baseObject, \DateTimeInterface $targetObject, bool $absolute = false): \DateInterval {} diff --git a/tests/PHPStan/Analyser/nsrt/bug-14632.php b/tests/PHPStan/Analyser/nsrt/bug-14632.php new file mode 100644 index 00000000000..b7e31ae7704 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-14632.php @@ -0,0 +1,26 @@ +diff($b); + assertType('DateInterval&object{days: int}', $interval); + assertType('int', $interval->days); +} + +function testDateTimeImmutable(DateTimeImmutable $a, DateTimeImmutable $b): void { + $interval = $a->diff($b); + assertType('DateInterval&object{days: int}', $interval); + assertType('int', $interval->days); +} + +function testDateTime(DateTime $a, DateTime $b): void { + $interval = $a->diff($b); + assertType('DateInterval&object{days: int}', $interval); + assertType('int', $interval->days); +} diff --git a/tests/PHPStan/Analyser/nsrt/builtin-generic-ancestor-stubs.php b/tests/PHPStan/Analyser/nsrt/builtin-generic-ancestor-stubs.php new file mode 100644 index 00000000000..0de66fc8316 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/builtin-generic-ancestor-stubs.php @@ -0,0 +1,28 @@ += 8.4 + +namespace BuiltinGenericAncestorStubs; + +use Dom\NodeList; +use MultipleIterator; +use SplFileObject; +use WeakMap; +use function PHPStan\Testing\assertType; + +function testSplFileObject(SplFileObject $file): void { + assertType('int', $file->key()); + assertType('array|string|false', $file->current()); +} + +function testMultipleIterator(MultipleIterator $it): void { + assertType('array', $it->key()); + assertType('array', $it->current()); +} + +/** @param WeakMap $map */ +function testWeakMap(WeakMap $map): void { + assertType('int<0, max>', $map->count()); +} + +function testDomNodeList(NodeList $list): void { + assertType('int<0, max>', $list->count()); +}