Skip to content

Commit

Permalink
TypehintHelper::decideTypeFromReflection should accept correct ancest…
Browse files Browse the repository at this point in the history
…or ClassReflection, not just class name
  • Loading branch information
ondrejmirtes committed Apr 4, 2023
1 parent b439fed commit 297a9fe
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 22 deletions.
2 changes: 1 addition & 1 deletion src/Reflection/Php/PhpClassReflectionExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -749,7 +749,7 @@ private function createMethod(
$nativeReturnType = TypehintHelper::decideTypeFromReflection(
$methodReflection->getReturnType(),
null,
$declaringClass->getName(),
$declaringClass,
);
$phpDocReturnType = $this->getPhpDocReturnType($phpDocBlockClassReflection, $resolvedPhpDoc, $nativeReturnType);
$phpDocThrowType = $resolvedPhpDoc->getThrowsTag() !== null ? $resolvedPhpDoc->getThrowsTag()->getType() : null;
Expand Down
4 changes: 2 additions & 2 deletions src/Reflection/Php/PhpMethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ private function getReturnType(): Type
$this->returnType = TypehintHelper::decideTypeFromReflection(
$returnType,
$this->phpDocReturnType,
$this->declaringClass->getName(),
$this->declaringClass,
);
}

Expand All @@ -367,7 +367,7 @@ private function getNativeReturnType(): Type
$this->nativeReturnType = TypehintHelper::decideTypeFromReflection(
$this->reflection->getReturnType(),
null,
$this->declaringClass->getName(),
$this->declaringClass,
);
}

Expand Down
4 changes: 2 additions & 2 deletions src/Reflection/Php/PhpPropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public function getReadableType(): Type
$this->type = TypehintHelper::decideTypeFromReflection(
$this->nativeType,
$this->phpDocType,
$this->declaringClass->getName(),
$this->declaringClass,
);
}

Expand Down Expand Up @@ -134,7 +134,7 @@ public function getNativeType(): Type
$this->finalNativeType = TypehintHelper::decideTypeFromReflection(
$this->nativeType,
null,
$this->declaringClass->getName(),
$this->declaringClass,
);
}

Expand Down
34 changes: 26 additions & 8 deletions src/Type/TypehintHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace PHPStan\Type;

use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\ReflectionProviderStaticAccessor;
use PHPStan\ShouldNotHappenException;
use PHPStan\Type\Constant\ConstantBooleanType;
Expand All @@ -13,14 +14,15 @@
use function array_map;
use function count;
use function get_class;
use function is_string;
use function sprintf;
use function str_ends_with;
use function strtolower;

class TypehintHelper
{

private static function getTypeObjectFromTypehint(string $typeString, ?string $selfClass): Type
private static function getTypeObjectFromTypehint(string $typeString, ClassReflection|string|null $selfClass): Type
{
switch (strtolower($typeString)) {
case 'int':
Expand Down Expand Up @@ -48,20 +50,36 @@ private static function getTypeObjectFromTypehint(string $typeString, ?string $s
case 'mixed':
return new MixedType(true);
case 'self':
if ($selfClass instanceof ClassReflection) {
$selfClass = $selfClass->getName();
}
return $selfClass !== null ? new ObjectType($selfClass) : new ErrorType();
case 'parent':
$reflectionProvider = ReflectionProviderStaticAccessor::getInstance();
if ($selfClass !== null && $reflectionProvider->hasClass($selfClass)) {
$classReflection = $reflectionProvider->getClass($selfClass);
if ($classReflection->getParentClass() !== null) {
return new ObjectType($classReflection->getParentClass()->getName());
if (is_string($selfClass)) {
if ($reflectionProvider->hasClass($selfClass)) {
$selfClass = $reflectionProvider->getClass($selfClass);
} else {
$selfClass = null;
}
}
if ($selfClass !== null) {
if ($selfClass->getParentClass() !== null) {
return new ObjectType($selfClass->getParentClass()->getName());
}
}
return new NonexistentParentClassType();
case 'static':
$reflectionProvider = ReflectionProviderStaticAccessor::getInstance();
if ($selfClass !== null && $reflectionProvider->hasClass($selfClass)) {
return new StaticType($reflectionProvider->getClass($selfClass));
if (is_string($selfClass)) {
if ($reflectionProvider->hasClass($selfClass)) {
$selfClass = $reflectionProvider->getClass($selfClass);
} else {
$selfClass = null;
}
}
if ($selfClass !== null) {
return new StaticType($selfClass);
}

return new ErrorType();
Expand All @@ -78,7 +96,7 @@ private static function getTypeObjectFromTypehint(string $typeString, ?string $s
public static function decideTypeFromReflection(
?ReflectionType $reflectionType,
?Type $phpDocType = null,
?string $selfClass = null,
ClassReflection|string|null $selfClass = null,
bool $isVariadic = false,
): Type
{
Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Rules/Methods/MethodSignatureRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ public function testBug7652(): void
$this->reportStatic = true;
$this->analyse([__DIR__ . '/data/bug-7652.php'], [
[
'Return type mixed of method Bug7652\Options::offsetGet() is not covariant with tentative return type mixed of method ArrayAccess::offsetGet().',
'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().',
23,
'Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.',
],
Expand Down
16 changes: 8 additions & 8 deletions tests/PHPStan/Rules/Methods/OverridingMethodRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public function testOverridingFinalMethod(int $phpVersion, string $contravariant
280,
],
[
'Parameter #1 $index (int) of method OverridingFinalMethod\FixedArrayOffsetExists::offsetExists() is not ' . $contravariantMessage . ' with parameter #1 $offset (mixed) of method ArrayAccess::offsetExists().',
'Parameter #1 $index (int) of method OverridingFinalMethod\FixedArrayOffsetExists::offsetExists() is not ' . $contravariantMessage . ' with parameter #1 $offset (mixed) of method ArrayAccess<int,mixed>::offsetExists().',
313,
],
];
Expand Down Expand Up @@ -472,37 +472,37 @@ public function dataTentativeReturnTypes(): array
80100,
[
[
'Return type mixed of method TentativeReturnTypes\Foo::getIterator() is not covariant with tentative return type Traversable of method IteratorAggregate::getIterator().',
'Return type mixed of method TentativeReturnTypes\Foo::getIterator() is not covariant with tentative return type Traversable of method IteratorAggregate<mixed,mixed>::getIterator().',
8,
'Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.',
],
[
'Return type string of method TentativeReturnTypes\Lorem::getIterator() is not covariant with tentative return type Traversable of method IteratorAggregate::getIterator().',
'Return type string of method TentativeReturnTypes\Lorem::getIterator() is not covariant with tentative return type Traversable of method IteratorAggregate<mixed,mixed>::getIterator().',
40,
'Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.',
],
[
'Return type mixed of method TentativeReturnTypes\UntypedIterator::current() is not covariant with tentative return type mixed of method Iterator::current().',
'Return type mixed of method TentativeReturnTypes\UntypedIterator::current() is not covariant with tentative return type mixed of method Iterator<mixed,mixed>::current().',
75,
'Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.',
],
[
'Return type mixed of method TentativeReturnTypes\UntypedIterator::next() is not covariant with tentative return type void of method Iterator::next().',
'Return type mixed of method TentativeReturnTypes\UntypedIterator::next() is not covariant with tentative return type void of method Iterator<mixed,mixed>::next().',
79,
'Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.',
],
[
'Return type mixed of method TentativeReturnTypes\UntypedIterator::key() is not covariant with tentative return type mixed of method Iterator::key().',
'Return type mixed of method TentativeReturnTypes\UntypedIterator::key() is not covariant with tentative return type mixed of method Iterator<mixed,mixed>::key().',
83,
'Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.',
],
[
'Return type mixed of method TentativeReturnTypes\UntypedIterator::valid() is not covariant with tentative return type bool of method Iterator::valid().',
'Return type mixed of method TentativeReturnTypes\UntypedIterator::valid() is not covariant with tentative return type bool of method Iterator<mixed,mixed>::valid().',
87,
'Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.',
],
[
'Return type mixed of method TentativeReturnTypes\UntypedIterator::rewind() is not covariant with tentative return type void of method Iterator::rewind().',
'Return type mixed of method TentativeReturnTypes\UntypedIterator::rewind() is not covariant with tentative return type void of method Iterator<mixed,mixed>::rewind().',
91,
'Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.',
],
Expand Down

0 comments on commit 297a9fe

Please sign in to comment.