Skip to content

Commit 30c9b01

Browse files
committed
Fix ?parent in return type
1 parent 69c3b3b commit 30c9b01

File tree

6 files changed

+45
-10
lines changed

6 files changed

+45
-10
lines changed

src/Analyser/MutatingScope.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3322,7 +3322,7 @@ public function getFunctionType($type, bool $isNullable, bool $isVariadic): Type
33223322
}
33233323
}
33243324

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

33283328
public function enterForeach(Expr $iteratee, string $valueName, ?string $keyName): self

src/Rules/Properties/OverridingPropertyRule.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ public function processNode(Node $node, Scope $scope): array
123123
$prototype->getNativeType()->describe(VerbosityLevel::typeOnly())
124124
))->nonIgnorable()->build();
125125
} else {
126-
$nativeType = ParserNodeTypeToPHPStanType::resolve($node->getNativeType(), $scope->getClassReflection()->getName());
126+
$nativeType = ParserNodeTypeToPHPStanType::resolve($node->getNativeType(), $scope->getClassReflection());
127127
if (!$prototype->getNativeType()->equals($nativeType)) {
128128
$typeErrors[] = RuleErrorBuilder::message(sprintf(
129129
'Type %s of property %s::$%s is not the same as type %s of overridden property %s::$%s.',
@@ -141,7 +141,7 @@ public function processNode(Node $node, Scope $scope): array
141141
'Property %s::$%s (%s) overriding property %s::$%s should not have a native type.',
142142
$classReflection->getDisplayName(),
143143
$node->getName(),
144-
ParserNodeTypeToPHPStanType::resolve($node->getNativeType(), $scope->getClassReflection()->getName())->describe(VerbosityLevel::typeOnly()),
144+
ParserNodeTypeToPHPStanType::resolve($node->getNativeType(), $scope->getClassReflection())->describe(VerbosityLevel::typeOnly()),
145145
$prototype->getDeclaringClass()->getDisplayName(),
146146
$node->getName()
147147
))->nonIgnorable()->build();

src/Type/ParserNodeTypeToPHPStanType.php

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,32 @@
44

55
use PhpParser\Node\Name;
66
use PhpParser\Node\NullableType;
7+
use PHPStan\Reflection\ClassReflection;
78
use PHPStan\Type\Constant\ConstantBooleanType;
89

910
class ParserNodeTypeToPHPStanType
1011
{
1112

1213
/**
1314
* @param \PhpParser\Node\Name|\PhpParser\Node\Identifier|\PhpParser\Node\NullableType|\PhpParser\Node\UnionType|null $type
14-
* @param string|null $className
15+
* @param ClassReflection|null $classReflection
1516
* @return Type
1617
*/
17-
public static function resolve($type, ?string $className): Type
18+
public static function resolve($type, ?ClassReflection $classReflection): Type
1819
{
1920
if ($type === null) {
2021
return new MixedType();
2122
} elseif ($type instanceof Name) {
2223
$typeClassName = (string) $type;
2324
$lowercasedClassName = strtolower($typeClassName);
24-
if ($className !== null && in_array($lowercasedClassName, ['self', 'static'], true)) {
25-
$typeClassName = $className;
25+
if ($classReflection !== null && in_array($lowercasedClassName, ['self', 'static'], true)) {
26+
$typeClassName = $classReflection->getName();
2627
} elseif (
2728
$lowercasedClassName === 'parent'
29+
&& $classReflection !== null
30+
&& $classReflection->getParentClass() !== false
2831
) {
29-
throw new \PHPStan\ShouldNotHappenException('parent type is not supported here');
32+
$typeClassName = $classReflection->getParentClass()->getName();
3033
}
3134

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

3639
return new ObjectType($typeClassName);
3740
} elseif ($type instanceof NullableType) {
38-
return TypeCombinator::addNull(self::resolve($type->type, $className));
41+
return TypeCombinator::addNull(self::resolve($type->type, $classReflection));
3942
} elseif ($type instanceof \PhpParser\Node\UnionType) {
4043
$types = [];
4144
foreach ($type->types as $unionTypeType) {
42-
$types[] = self::resolve($unionTypeType, $className);
45+
$types[] = self::resolve($unionTypeType, $classReflection);
4346
}
4447

4548
return TypeCombinator::union(...$types);

tests/PHPStan/Analyser/AnalyserIntegrationTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,12 @@ public function testBug5231Two(): void
426426
$this->assertNotEmpty($errors);
427427
}
428428

429+
public function testBug5529(): void
430+
{
431+
$errors = $this->runAnalyse(__DIR__ . '/data/bug-5529.php');
432+
$this->assertCount(0, $errors);
433+
}
434+
429435
/**
430436
* @param string $file
431437
* @return \PHPStan\Analyser\Error[]

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,8 @@ public function dataFileAsserts(): iterable
477477
yield from $this->gatherAssertTypes(__DIR__ . '/data/literal-string.php');
478478

479479
yield from $this->gatherAssertTypes(__DIR__ . '/data/filter-var-returns-non-empty-string.php');
480+
481+
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-5529.php');
480482
}
481483

482484
/**
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Bug5529;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class ParentClass
8+
{}
9+
10+
class HelloWorld extends ParentClass
11+
{
12+
public function run(): ?parent
13+
{
14+
if (rand(0,1)) {
15+
return $this;
16+
}
17+
18+
return null;
19+
}
20+
}
21+
22+
function (HelloWorld $hw): void {
23+
assertType(ParentClass::class . '|null', $hw->run());
24+
};

0 commit comments

Comments
 (0)