Skip to content

Commit 13fb5b1

Browse files
committed
Fixed wrong template types resolution when inheriting phpDocs
1 parent 3f20c2d commit 13fb5b1

File tree

4 files changed

+40
-16
lines changed

4 files changed

+40
-16
lines changed

src/Reflection/Php/PhpClassReflectionExtension.php

+18-15
Original file line numberDiff line numberDiff line change
@@ -432,12 +432,13 @@ private function createMethod(
432432
$stubPhpDocParameterTypes = [];
433433
$stubPhpDocParameterVariadicity = [];
434434
if (count($variantNames) === 1) {
435-
$stubPhpDoc = $this->findMethodPhpDocIncludingAncestors($declaringClassName, $methodReflection->getName(), array_map(static function (ParameterSignature $parameterSignature): string {
435+
$stubPhpDocPair = $this->findMethodPhpDocIncludingAncestors($declaringClass, $methodReflection->getName(), array_map(static function (ParameterSignature $parameterSignature): string {
436436
return $parameterSignature->getName();
437437
}, $methodSignature->getParameters()));
438-
if ($stubPhpDoc !== null) {
438+
if ($stubPhpDocPair !== null) {
439+
[$stubPhpDoc, $stubDeclaringClass] = $stubPhpDocPair;
439440
$stubPhpDocString = $stubPhpDoc->getPhpDocString();
440-
$templateTypeMap = $declaringClass->getActiveTemplateTypeMap();
441+
$templateTypeMap = $stubDeclaringClass->getActiveTemplateTypeMap();
441442
$returnTag = $stubPhpDoc->getReturnTag();
442443
if ($returnTag !== null) {
443444
$stubPhpDocReturnType = $returnTag->getType();
@@ -491,9 +492,14 @@ private function createMethod(
491492
}
492493

493494
$declaringTraitName = $this->findMethodTrait($methodReflection);
494-
$resolvedPhpDoc = $this->findMethodPhpDocIncludingAncestors($declaringClassName, $methodReflection->getName(), array_map(static function (\ReflectionParameter $parameter): string {
495+
$resolvedPhpDoc = null;
496+
$stubPhpDocPair = $this->findMethodPhpDocIncludingAncestors($declaringClass, $methodReflection->getName(), array_map(static function (\ReflectionParameter $parameter): string {
495497
return $parameter->getName();
496498
}, $methodReflection->getParameters()));
499+
$phpDocBlockClassReflection = $declaringClass;
500+
if ($stubPhpDocPair !== null) {
501+
[$resolvedPhpDoc, $phpDocBlockClassReflection] = $stubPhpDocPair;
502+
}
497503
$stubPhpDocString = null;
498504
$phpDocBlock = null;
499505
if ($resolvedPhpDoc === null) {
@@ -526,7 +532,6 @@ private function createMethod(
526532
}
527533
}
528534
} else {
529-
$phpDocBlockClassReflection = $declaringClass;
530535
$isPhpDocBlockExplicit = true;
531536
$stubPhpDocString = $resolvedPhpDoc->getPhpDocString();
532537
}
@@ -547,7 +552,7 @@ private function createMethod(
547552
$isInternal = false;
548553
$isFinal = false;
549554
if ($resolvedPhpDoc !== null) {
550-
if (!isset($phpDocBlockClassReflection) || !isset($isPhpDocBlockExplicit)) {
555+
if (!isset($isPhpDocBlockExplicit)) {
551556
throw new \PHPStan\ShouldNotHappenException();
552557
}
553558
$templateTypeMap = $resolvedPhpDoc->getTemplateTypeMap();
@@ -883,25 +888,23 @@ private function getPhpDocReturnType(ClassReflection $phpDocBlockClassReflection
883888
}
884889

885890
/**
886-
* @param string $declaringClassName
891+
* @param ClassReflection $declaringClass
887892
* @param string $methodName
888893
* @param array<int, string> $positionalParameterNames
889-
* @return \PHPStan\PhpDoc\ResolvedPhpDocBlock|null
894+
* @return array{\PHPStan\PhpDoc\ResolvedPhpDocBlock, ClassReflection}|null
890895
*/
891-
private function findMethodPhpDocIncludingAncestors(string $declaringClassName, string $methodName, array $positionalParameterNames): ?ResolvedPhpDocBlock
896+
private function findMethodPhpDocIncludingAncestors(ClassReflection $declaringClass, string $methodName, array $positionalParameterNames): ?array
892897
{
898+
$declaringClassName = $declaringClass->getName();
893899
$resolved = $this->stubPhpDocProvider->findMethodPhpDoc($declaringClassName, $methodName, $positionalParameterNames);
894900
if ($resolved !== null) {
895-
return $resolved;
901+
return [$resolved, $declaringClass];
896902
}
897903
if (!$this->stubPhpDocProvider->isKnownClass($declaringClassName)) {
898904
return null;
899905
}
900-
if (!$this->reflectionProvider->hasClass($declaringClassName)) {
901-
return null;
902-
}
903906

904-
$ancestors = $this->reflectionProvider->getClass($declaringClassName)->getAncestors();
907+
$ancestors = $declaringClass->getAncestors();
905908
foreach ($ancestors as $ancestor) {
906909
if ($ancestor->getName() === $declaringClassName) {
907910
continue;
@@ -915,7 +918,7 @@ private function findMethodPhpDocIncludingAncestors(string $declaringClassName,
915918
continue;
916919
}
917920

918-
return $resolved;
921+
return [$resolved, $ancestor];
919922
}
920923

921924
return null;

stubs/iterable.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,9 @@ public function send($value) {}
7171

7272
/**
7373
* @implements Traversable<mixed, mixed>
74+
* @implements ArrayAccess<mixed, mixed>
7475
*/
75-
class SimpleXMLElement implements Traversable
76+
class SimpleXMLElement implements Traversable, ArrayAccess
7677
{
7778

7879
}

tests/PHPStan/Rules/Arrays/OffsetAccessAssignmentRuleTest.php

+6
Original file line numberDiff line numberDiff line change
@@ -167,4 +167,10 @@ public function testOffsetAccessAssignmentToScalarWithoutMaybes(): void
167167
);
168168
}
169169

170+
public function testInheritDocTemplateTypeResolution(): void
171+
{
172+
$this->checkUnionTypes = true;
173+
$this->analyse([__DIR__ . '/data/inherit-doc-template-type-resolution.php'], []);
174+
}
175+
170176
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace InheritDocTemplateTypeResolution;
4+
5+
class Foo extends \SimpleXMLElement
6+
{
7+
8+
public function removeThis(): void
9+
{
10+
unset($this[0]);
11+
12+
}
13+
14+
}

0 commit comments

Comments
 (0)