Skip to content

Commit 297a9fe

Browse files
committed
TypehintHelper::decideTypeFromReflection should accept correct ancestor ClassReflection, not just class name
1 parent b439fed commit 297a9fe

File tree

6 files changed

+40
-22
lines changed

6 files changed

+40
-22
lines changed

Diff for: src/Reflection/Php/PhpClassReflectionExtension.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -749,7 +749,7 @@ private function createMethod(
749749
$nativeReturnType = TypehintHelper::decideTypeFromReflection(
750750
$methodReflection->getReturnType(),
751751
null,
752-
$declaringClass->getName(),
752+
$declaringClass,
753753
);
754754
$phpDocReturnType = $this->getPhpDocReturnType($phpDocBlockClassReflection, $resolvedPhpDoc, $nativeReturnType);
755755
$phpDocThrowType = $resolvedPhpDoc->getThrowsTag() !== null ? $resolvedPhpDoc->getThrowsTag()->getType() : null;

Diff for: src/Reflection/Php/PhpMethodReflection.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ private function getReturnType(): Type
345345
$this->returnType = TypehintHelper::decideTypeFromReflection(
346346
$returnType,
347347
$this->phpDocReturnType,
348-
$this->declaringClass->getName(),
348+
$this->declaringClass,
349349
);
350350
}
351351

@@ -367,7 +367,7 @@ private function getNativeReturnType(): Type
367367
$this->nativeReturnType = TypehintHelper::decideTypeFromReflection(
368368
$this->reflection->getReturnType(),
369369
null,
370-
$this->declaringClass->getName(),
370+
$this->declaringClass,
371371
);
372372
}
373373

Diff for: src/Reflection/Php/PhpPropertyReflection.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public function getReadableType(): Type
8787
$this->type = TypehintHelper::decideTypeFromReflection(
8888
$this->nativeType,
8989
$this->phpDocType,
90-
$this->declaringClass->getName(),
90+
$this->declaringClass,
9191
);
9292
}
9393

@@ -134,7 +134,7 @@ public function getNativeType(): Type
134134
$this->finalNativeType = TypehintHelper::decideTypeFromReflection(
135135
$this->nativeType,
136136
null,
137-
$this->declaringClass->getName(),
137+
$this->declaringClass,
138138
);
139139
}
140140

Diff for: src/Type/TypehintHelper.php

+26-8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PHPStan\Type;
44

5+
use PHPStan\Reflection\ClassReflection;
56
use PHPStan\Reflection\ReflectionProviderStaticAccessor;
67
use PHPStan\ShouldNotHappenException;
78
use PHPStan\Type\Constant\ConstantBooleanType;
@@ -13,14 +14,15 @@
1314
use function array_map;
1415
use function count;
1516
use function get_class;
17+
use function is_string;
1618
use function sprintf;
1719
use function str_ends_with;
1820
use function strtolower;
1921

2022
class TypehintHelper
2123
{
2224

23-
private static function getTypeObjectFromTypehint(string $typeString, ?string $selfClass): Type
25+
private static function getTypeObjectFromTypehint(string $typeString, ClassReflection|string|null $selfClass): Type
2426
{
2527
switch (strtolower($typeString)) {
2628
case 'int':
@@ -48,20 +50,36 @@ private static function getTypeObjectFromTypehint(string $typeString, ?string $s
4850
case 'mixed':
4951
return new MixedType(true);
5052
case 'self':
53+
if ($selfClass instanceof ClassReflection) {
54+
$selfClass = $selfClass->getName();
55+
}
5156
return $selfClass !== null ? new ObjectType($selfClass) : new ErrorType();
5257
case 'parent':
5358
$reflectionProvider = ReflectionProviderStaticAccessor::getInstance();
54-
if ($selfClass !== null && $reflectionProvider->hasClass($selfClass)) {
55-
$classReflection = $reflectionProvider->getClass($selfClass);
56-
if ($classReflection->getParentClass() !== null) {
57-
return new ObjectType($classReflection->getParentClass()->getName());
59+
if (is_string($selfClass)) {
60+
if ($reflectionProvider->hasClass($selfClass)) {
61+
$selfClass = $reflectionProvider->getClass($selfClass);
62+
} else {
63+
$selfClass = null;
64+
}
65+
}
66+
if ($selfClass !== null) {
67+
if ($selfClass->getParentClass() !== null) {
68+
return new ObjectType($selfClass->getParentClass()->getName());
5869
}
5970
}
6071
return new NonexistentParentClassType();
6172
case 'static':
6273
$reflectionProvider = ReflectionProviderStaticAccessor::getInstance();
63-
if ($selfClass !== null && $reflectionProvider->hasClass($selfClass)) {
64-
return new StaticType($reflectionProvider->getClass($selfClass));
74+
if (is_string($selfClass)) {
75+
if ($reflectionProvider->hasClass($selfClass)) {
76+
$selfClass = $reflectionProvider->getClass($selfClass);
77+
} else {
78+
$selfClass = null;
79+
}
80+
}
81+
if ($selfClass !== null) {
82+
return new StaticType($selfClass);
6583
}
6684

6785
return new ErrorType();
@@ -78,7 +96,7 @@ private static function getTypeObjectFromTypehint(string $typeString, ?string $s
7896
public static function decideTypeFromReflection(
7997
?ReflectionType $reflectionType,
8098
?Type $phpDocType = null,
81-
?string $selfClass = null,
99+
ClassReflection|string|null $selfClass = null,
82100
bool $isVariadic = false,
83101
): Type
84102
{

Diff for: tests/PHPStan/Rules/Methods/MethodSignatureRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ public function testBug7652(): void
384384
$this->reportStatic = true;
385385
$this->analyse([__DIR__ . '/data/bug-7652.php'], [
386386
[
387-
'Return type mixed of method Bug7652\Options::offsetGet() is not covariant with tentative return type mixed of method ArrayAccess::offsetGet().',
387+
'Return type mixed of method Bug7652\Options::offsetGet() is not covariant with tentative return type mixed of method ArrayAccess<key-of<TArray of array>,value-of<TArray of array>>::offsetGet().',
388388
23,
389389
'Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.',
390390
],

Diff for: tests/PHPStan/Rules/Methods/OverridingMethodRuleTest.php

+8-8
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public function testOverridingFinalMethod(int $phpVersion, string $contravariant
120120
280,
121121
],
122122
[
123-
'Parameter #1 $index (int) of method OverridingFinalMethod\FixedArrayOffsetExists::offsetExists() is not ' . $contravariantMessage . ' with parameter #1 $offset (mixed) of method ArrayAccess::offsetExists().',
123+
'Parameter #1 $index (int) of method OverridingFinalMethod\FixedArrayOffsetExists::offsetExists() is not ' . $contravariantMessage . ' with parameter #1 $offset (mixed) of method ArrayAccess<int,mixed>::offsetExists().',
124124
313,
125125
],
126126
];
@@ -472,37 +472,37 @@ public function dataTentativeReturnTypes(): array
472472
80100,
473473
[
474474
[
475-
'Return type mixed of method TentativeReturnTypes\Foo::getIterator() is not covariant with tentative return type Traversable of method IteratorAggregate::getIterator().',
475+
'Return type mixed of method TentativeReturnTypes\Foo::getIterator() is not covariant with tentative return type Traversable of method IteratorAggregate<mixed,mixed>::getIterator().',
476476
8,
477477
'Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.',
478478
],
479479
[
480-
'Return type string of method TentativeReturnTypes\Lorem::getIterator() is not covariant with tentative return type Traversable of method IteratorAggregate::getIterator().',
480+
'Return type string of method TentativeReturnTypes\Lorem::getIterator() is not covariant with tentative return type Traversable of method IteratorAggregate<mixed,mixed>::getIterator().',
481481
40,
482482
'Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.',
483483
],
484484
[
485-
'Return type mixed of method TentativeReturnTypes\UntypedIterator::current() is not covariant with tentative return type mixed of method Iterator::current().',
485+
'Return type mixed of method TentativeReturnTypes\UntypedIterator::current() is not covariant with tentative return type mixed of method Iterator<mixed,mixed>::current().',
486486
75,
487487
'Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.',
488488
],
489489
[
490-
'Return type mixed of method TentativeReturnTypes\UntypedIterator::next() is not covariant with tentative return type void of method Iterator::next().',
490+
'Return type mixed of method TentativeReturnTypes\UntypedIterator::next() is not covariant with tentative return type void of method Iterator<mixed,mixed>::next().',
491491
79,
492492
'Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.',
493493
],
494494
[
495-
'Return type mixed of method TentativeReturnTypes\UntypedIterator::key() is not covariant with tentative return type mixed of method Iterator::key().',
495+
'Return type mixed of method TentativeReturnTypes\UntypedIterator::key() is not covariant with tentative return type mixed of method Iterator<mixed,mixed>::key().',
496496
83,
497497
'Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.',
498498
],
499499
[
500-
'Return type mixed of method TentativeReturnTypes\UntypedIterator::valid() is not covariant with tentative return type bool of method Iterator::valid().',
500+
'Return type mixed of method TentativeReturnTypes\UntypedIterator::valid() is not covariant with tentative return type bool of method Iterator<mixed,mixed>::valid().',
501501
87,
502502
'Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.',
503503
],
504504
[
505-
'Return type mixed of method TentativeReturnTypes\UntypedIterator::rewind() is not covariant with tentative return type void of method Iterator::rewind().',
505+
'Return type mixed of method TentativeReturnTypes\UntypedIterator::rewind() is not covariant with tentative return type void of method Iterator<mixed,mixed>::rewind().',
506506
91,
507507
'Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.',
508508
],

0 commit comments

Comments
 (0)