Skip to content

Commit

Permalink
Fix ?parent in return type
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Aug 25, 2021
1 parent 69c3b3b commit 30c9b01
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 10 deletions.
2 changes: 1 addition & 1 deletion src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -3322,7 +3322,7 @@ public function getFunctionType($type, bool $isNullable, bool $isVariadic): Type
}
}

return ParserNodeTypeToPHPStanType::resolve($type, $this->isInClass() ? $this->getClassReflection()->getName() : null);
return ParserNodeTypeToPHPStanType::resolve($type, $this->isInClass() ? $this->getClassReflection() : null);
}

public function enterForeach(Expr $iteratee, string $valueName, ?string $keyName): self
Expand Down
4 changes: 2 additions & 2 deletions src/Rules/Properties/OverridingPropertyRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public function processNode(Node $node, Scope $scope): array
$prototype->getNativeType()->describe(VerbosityLevel::typeOnly())
))->nonIgnorable()->build();
} else {
$nativeType = ParserNodeTypeToPHPStanType::resolve($node->getNativeType(), $scope->getClassReflection()->getName());
$nativeType = ParserNodeTypeToPHPStanType::resolve($node->getNativeType(), $scope->getClassReflection());
if (!$prototype->getNativeType()->equals($nativeType)) {
$typeErrors[] = RuleErrorBuilder::message(sprintf(
'Type %s of property %s::$%s is not the same as type %s of overridden property %s::$%s.',
Expand All @@ -141,7 +141,7 @@ public function processNode(Node $node, Scope $scope): array
'Property %s::$%s (%s) overriding property %s::$%s should not have a native type.',
$classReflection->getDisplayName(),
$node->getName(),
ParserNodeTypeToPHPStanType::resolve($node->getNativeType(), $scope->getClassReflection()->getName())->describe(VerbosityLevel::typeOnly()),
ParserNodeTypeToPHPStanType::resolve($node->getNativeType(), $scope->getClassReflection())->describe(VerbosityLevel::typeOnly()),
$prototype->getDeclaringClass()->getDisplayName(),
$node->getName()
))->nonIgnorable()->build();
Expand Down
17 changes: 10 additions & 7 deletions src/Type/ParserNodeTypeToPHPStanType.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,32 @@

use PhpParser\Node\Name;
use PhpParser\Node\NullableType;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Type\Constant\ConstantBooleanType;

class ParserNodeTypeToPHPStanType
{

/**
* @param \PhpParser\Node\Name|\PhpParser\Node\Identifier|\PhpParser\Node\NullableType|\PhpParser\Node\UnionType|null $type
* @param string|null $className
* @param ClassReflection|null $classReflection
* @return Type
*/
public static function resolve($type, ?string $className): Type
public static function resolve($type, ?ClassReflection $classReflection): Type
{
if ($type === null) {
return new MixedType();
} elseif ($type instanceof Name) {
$typeClassName = (string) $type;
$lowercasedClassName = strtolower($typeClassName);
if ($className !== null && in_array($lowercasedClassName, ['self', 'static'], true)) {
$typeClassName = $className;
if ($classReflection !== null && in_array($lowercasedClassName, ['self', 'static'], true)) {
$typeClassName = $classReflection->getName();
} elseif (
$lowercasedClassName === 'parent'
&& $classReflection !== null
&& $classReflection->getParentClass() !== false
) {
throw new \PHPStan\ShouldNotHappenException('parent type is not supported here');
$typeClassName = $classReflection->getParentClass()->getName();
}

if ($lowercasedClassName === 'static') {
Expand All @@ -35,11 +38,11 @@ public static function resolve($type, ?string $className): Type

return new ObjectType($typeClassName);
} elseif ($type instanceof NullableType) {
return TypeCombinator::addNull(self::resolve($type->type, $className));
return TypeCombinator::addNull(self::resolve($type->type, $classReflection));
} elseif ($type instanceof \PhpParser\Node\UnionType) {
$types = [];
foreach ($type->types as $unionTypeType) {
$types[] = self::resolve($unionTypeType, $className);
$types[] = self::resolve($unionTypeType, $classReflection);
}

return TypeCombinator::union(...$types);
Expand Down
6 changes: 6 additions & 0 deletions tests/PHPStan/Analyser/AnalyserIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,12 @@ public function testBug5231Two(): void
$this->assertNotEmpty($errors);
}

public function testBug5529(): void
{
$errors = $this->runAnalyse(__DIR__ . '/data/bug-5529.php');
$this->assertCount(0, $errors);
}

/**
* @param string $file
* @return \PHPStan\Analyser\Error[]
Expand Down
2 changes: 2 additions & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,8 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/data/literal-string.php');

yield from $this->gatherAssertTypes(__DIR__ . '/data/filter-var-returns-non-empty-string.php');

yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-5529.php');
}

/**
Expand Down
24 changes: 24 additions & 0 deletions tests/PHPStan/Analyser/data/bug-5529.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Bug5529;

use function PHPStan\Testing\assertType;

class ParentClass
{}

class HelloWorld extends ParentClass
{
public function run(): ?parent
{
if (rand(0,1)) {
return $this;
}

return null;
}
}

function (HelloWorld $hw): void {
assertType(ParentClass::class . '|null', $hw->run());
};

0 comments on commit 30c9b01

Please sign in to comment.