Skip to content

Commit 85fcd5f

Browse files
committed
Apply same fix in MethodSignatureRule from be2b415
1 parent 1bf37b9 commit 85fcd5f

File tree

5 files changed

+79
-21
lines changed

5 files changed

+79
-21
lines changed

src/PhpDoc/StubValidator.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ private function getRuleRegistry(Container $container): RuleRegistry
155155
$crossCheckInterfacesHelper = $container->getByType(CrossCheckInterfacesHelper::class);
156156
$phpVersion = $container->getByType(PhpVersion::class);
157157
$localTypeAliasesCheck = $container->getByType(LocalTypeAliasesCheck::class);
158+
$phpClassReflectionExtension = $container->getByType(PhpClassReflectionExtension::class);
158159

159160
$rules = [
160161
// level 0
@@ -165,7 +166,7 @@ private function getRuleRegistry(Container $container): RuleRegistry
165166
new ExistingClassesInTypehintsRule($functionDefinitionCheck),
166167
new \PHPStan\Rules\Functions\ExistingClassesInTypehintsRule($functionDefinitionCheck),
167168
new ExistingClassesInPropertiesRule($reflectionProvider, $classCaseSensitivityCheck, $unresolvableTypeHelper, $phpVersion, true, false),
168-
new OverridingMethodRule($phpVersion, new MethodSignatureRule(true, true, $container->getParameter('featureToggles')['abstractTraitMethod']), true, new MethodParameterComparisonHelper($phpVersion, $container->getParameter('featureToggles')['genericPrototypeMessage']), $container->getByType(PhpClassReflectionExtension::class), $container->getParameter('featureToggles')['genericPrototypeMessage'], $container->getParameter('featureToggles')['finalByPhpDoc'], $container->getParameter('checkMissingOverrideMethodAttribute')),
169+
new OverridingMethodRule($phpVersion, new MethodSignatureRule($phpClassReflectionExtension, true, true, $container->getParameter('featureToggles')['abstractTraitMethod']), true, new MethodParameterComparisonHelper($phpVersion, $container->getParameter('featureToggles')['genericPrototypeMessage']), $phpClassReflectionExtension, $container->getParameter('featureToggles')['genericPrototypeMessage'], $container->getParameter('featureToggles')['finalByPhpDoc'], $container->getParameter('checkMissingOverrideMethodAttribute')),
169170
new DuplicateDeclarationRule(),
170171
new LocalTypeAliasesRule($localTypeAliasesCheck),
171172
new LocalTypeTraitAliasesRule($localTypeAliasesCheck, $reflectionProvider),

src/Rules/Methods/MethodSignatureRule.php

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
use PHPStan\Reflection\ParameterReflectionWithPhpDocs;
1111
use PHPStan\Reflection\ParametersAcceptorSelector;
1212
use PHPStan\Reflection\ParametersAcceptorWithPhpDocs;
13+
use PHPStan\Reflection\Php\NativeBuiltinMethodReflection;
14+
use PHPStan\Reflection\Php\PhpClassReflectionExtension;
1315
use PHPStan\Rules\Rule;
1416
use PHPStan\Rules\RuleErrorBuilder;
1517
use PHPStan\TrinaryLogic;
@@ -22,7 +24,6 @@
2224
use PHPStan\Type\TypeTraverser;
2325
use PHPStan\Type\VerbosityLevel;
2426
use function count;
25-
use function is_bool;
2627
use function min;
2728
use function sprintf;
2829

@@ -33,6 +34,7 @@ class MethodSignatureRule implements Rule
3334
{
3435

3536
public function __construct(
37+
private PhpClassReflectionExtension $phpClassReflectionExtension,
3638
private bool $reportMaybes,
3739
private bool $reportStatic,
3840
private bool $abstractTraitMethod,
@@ -62,7 +64,7 @@ public function processNode(Node $node, Scope $scope): array
6264

6365
$errors = [];
6466
$declaringClass = $method->getDeclaringClass();
65-
foreach ($this->collectParentMethods($methodName, $method->getDeclaringClass()) as $parentMethod) {
67+
foreach ($this->collectParentMethods($methodName, $method->getDeclaringClass()) as [$parentMethod, $parentMethodDeclaringClass]) {
6668
$parentVariants = $parentMethod->getVariants();
6769
if (count($parentVariants) !== 1) {
6870
continue;
@@ -77,7 +79,7 @@ public function processNode(Node $node, Scope $scope): array
7779
$method->getName(),
7880
$returnTypeCompatibility->no() ? 'compatible' : 'covariant',
7981
$parentReturnType->describe(VerbosityLevel::value()),
80-
$parentMethod->getDeclaringClass()->getDisplayName(),
82+
$parentMethodDeclaringClass->getDisplayName(),
8183
$parentMethod->getName(),
8284
))->build();
8385
}
@@ -102,7 +104,7 @@ public function processNode(Node $node, Scope $scope): array
102104
$parameterResult->no() ? 'compatible' : 'contravariant',
103105
$parentParameter->getName(),
104106
$parentParameterType->describe(VerbosityLevel::value()),
105-
$parentMethod->getDeclaringClass()->getDisplayName(),
107+
$parentMethodDeclaringClass->getDisplayName(),
106108
$parentMethod->getName(),
107109
))->build();
108110
}
@@ -112,7 +114,7 @@ public function processNode(Node $node, Scope $scope): array
112114
}
113115

114116
/**
115-
* @return ExtendedMethodReflection[]
117+
* @return list<array{ExtendedMethodReflection, ClassReflection}>
116118
*/
117119
private function collectParentMethods(string $methodName, ClassReflection $class): array
118120
{
@@ -122,7 +124,7 @@ private function collectParentMethods(string $methodName, ClassReflection $class
122124
if ($parentClass !== null && $parentClass->hasNativeMethod($methodName)) {
123125
$parentMethod = $parentClass->getNativeMethod($methodName);
124126
if (!$parentMethod->isPrivate()) {
125-
$parentMethods[] = $parentMethod;
127+
$parentMethods[] = [$parentMethod, $parentMethod->getDeclaringClass()];
126128
}
127129
}
128130

@@ -131,24 +133,31 @@ private function collectParentMethods(string $methodName, ClassReflection $class
131133
continue;
132134
}
133135

134-
$parentMethods[] = $interface->getNativeMethod($methodName);
136+
$method = $interface->getNativeMethod($methodName);
137+
$parentMethods[] = [$method, $method->getDeclaringClass()];
135138
}
136139

137140
if ($this->abstractTraitMethod) {
138141
foreach ($class->getTraits(true) as $trait) {
139-
if (!$trait->hasNativeMethod($methodName)) {
142+
$nativeTraitReflection = $trait->getNativeReflection();
143+
if (!$nativeTraitReflection->hasMethod($methodName)) {
140144
continue;
141145
}
142146

143-
$method = $trait->getNativeMethod($methodName);
144-
$isAbstract = $method->isAbstract();
145-
if (is_bool($isAbstract)) {
146-
if ($isAbstract) {
147-
$parentMethods[] = $method;
148-
}
149-
} elseif ($isAbstract->yes()) {
150-
$parentMethods[] = $method;
147+
$methodReflection = $nativeTraitReflection->getMethod($methodName);
148+
$isAbstract = $methodReflection->isAbstract();
149+
if (!$isAbstract) {
150+
continue;
151151
}
152+
153+
$parentMethods[] = [
154+
$this->phpClassReflectionExtension->createUserlandMethodReflection(
155+
$trait,
156+
$class,
157+
new NativeBuiltinMethodReflection($methodReflection),
158+
),
159+
$trait->getNativeMethod($methodName)->getDeclaringClass(),
160+
];
152161
}
153162
}
154163

tests/PHPStan/Rules/Methods/MethodSignatureRuleTest.php

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@ protected function getRule(): Rule
2222
{
2323
$phpVersion = new PhpVersion(PHP_VERSION_ID);
2424

25+
$phpClassReflectionExtension = self::getContainer()->getByType(PhpClassReflectionExtension::class);
26+
2527
return new OverridingMethodRule(
2628
$phpVersion,
27-
new MethodSignatureRule($this->reportMaybes, $this->reportStatic, true),
29+
new MethodSignatureRule($phpClassReflectionExtension, $this->reportMaybes, $this->reportStatic, true),
2830
true,
2931
new MethodParameterComparisonHelper($phpVersion, true),
30-
self::getContainer()->getByType(PhpClassReflectionExtension::class),
32+
$phpClassReflectionExtension,
3133
true,
3234
true,
3335
false,
@@ -443,4 +445,21 @@ public function testTraits(): void
443445
]);
444446
}
445447

448+
public function testBug10166(): void
449+
{
450+
if (PHP_VERSION_ID < 80000) {
451+
$this->markTestSkipped('Test requires PHP 8.0.');
452+
}
453+
454+
$this->reportMaybes = true;
455+
$this->reportStatic = true;
456+
457+
$this->analyse([__DIR__ . '/data/bug-10166.php'], [
458+
[
459+
'Return type Bug10166\ReturnTypeClass2|null of method Bug10166\ReturnTypeClass2::createSelf() is not covariant with return type Bug10166\ReturnTypeClass2 of method Bug10166\ReturnTypeTrait::createSelf().',
460+
23,
461+
],
462+
]);
463+
}
464+
446465
}

tests/PHPStan/Rules/Methods/OverridingMethodRuleTest.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@ protected function getRule(): Rule
2424
{
2525
$phpVersion = new PhpVersion($this->phpVersionId);
2626

27+
$phpClassReflectionExtension = self::getContainer()->getByType(PhpClassReflectionExtension::class);
28+
2729
return new OverridingMethodRule(
2830
$phpVersion,
29-
new MethodSignatureRule(true, true, true),
31+
new MethodSignatureRule($phpClassReflectionExtension, true, true, true),
3032
false,
3133
new MethodParameterComparisonHelper($phpVersion, true),
32-
self::getContainer()->getByType(PhpClassReflectionExtension::class),
34+
$phpClassReflectionExtension,
3335
true,
3436
true,
3537
$this->checkMissingOverrideMethodAttribute,
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace Bug10166;
4+
5+
trait ReturnTypeTrait {
6+
abstract public static function createSelf(): self;
7+
}
8+
9+
final class ReturnTypeClass
10+
{
11+
use ReturnTypeTrait;
12+
13+
public static function createSelf(): self
14+
{
15+
return new self();
16+
}
17+
}
18+
19+
final class ReturnTypeClass2
20+
{
21+
use ReturnTypeTrait;
22+
23+
public static function createSelf(): ?self
24+
{
25+
return new self();
26+
}
27+
}

0 commit comments

Comments
 (0)