Skip to content

Commit

Permalink
Apply same fix in MethodSignatureRule from be2b415
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Nov 20, 2023
1 parent 1bf37b9 commit 85fcd5f
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 21 deletions.
3 changes: 2 additions & 1 deletion src/PhpDoc/StubValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ private function getRuleRegistry(Container $container): RuleRegistry
$crossCheckInterfacesHelper = $container->getByType(CrossCheckInterfacesHelper::class);
$phpVersion = $container->getByType(PhpVersion::class);
$localTypeAliasesCheck = $container->getByType(LocalTypeAliasesCheck::class);
$phpClassReflectionExtension = $container->getByType(PhpClassReflectionExtension::class);

$rules = [
// level 0
Expand All @@ -165,7 +166,7 @@ private function getRuleRegistry(Container $container): RuleRegistry
new ExistingClassesInTypehintsRule($functionDefinitionCheck),
new \PHPStan\Rules\Functions\ExistingClassesInTypehintsRule($functionDefinitionCheck),
new ExistingClassesInPropertiesRule($reflectionProvider, $classCaseSensitivityCheck, $unresolvableTypeHelper, $phpVersion, true, false),
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')),
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')),
new DuplicateDeclarationRule(),
new LocalTypeAliasesRule($localTypeAliasesCheck),
new LocalTypeTraitAliasesRule($localTypeAliasesCheck, $reflectionProvider),
Expand Down
41 changes: 25 additions & 16 deletions src/Rules/Methods/MethodSignatureRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
use PHPStan\Reflection\ParameterReflectionWithPhpDocs;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Reflection\ParametersAcceptorWithPhpDocs;
use PHPStan\Reflection\Php\NativeBuiltinMethodReflection;
use PHPStan\Reflection\Php\PhpClassReflectionExtension;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\TrinaryLogic;
Expand All @@ -22,7 +24,6 @@
use PHPStan\Type\TypeTraverser;
use PHPStan\Type\VerbosityLevel;
use function count;
use function is_bool;
use function min;
use function sprintf;

Expand All @@ -33,6 +34,7 @@ class MethodSignatureRule implements Rule
{

public function __construct(
private PhpClassReflectionExtension $phpClassReflectionExtension,
private bool $reportMaybes,
private bool $reportStatic,
private bool $abstractTraitMethod,
Expand Down Expand Up @@ -62,7 +64,7 @@ public function processNode(Node $node, Scope $scope): array

$errors = [];
$declaringClass = $method->getDeclaringClass();
foreach ($this->collectParentMethods($methodName, $method->getDeclaringClass()) as $parentMethod) {
foreach ($this->collectParentMethods($methodName, $method->getDeclaringClass()) as [$parentMethod, $parentMethodDeclaringClass]) {
$parentVariants = $parentMethod->getVariants();
if (count($parentVariants) !== 1) {
continue;
Expand All @@ -77,7 +79,7 @@ public function processNode(Node $node, Scope $scope): array
$method->getName(),
$returnTypeCompatibility->no() ? 'compatible' : 'covariant',
$parentReturnType->describe(VerbosityLevel::value()),
$parentMethod->getDeclaringClass()->getDisplayName(),
$parentMethodDeclaringClass->getDisplayName(),
$parentMethod->getName(),
))->build();
}
Expand All @@ -102,7 +104,7 @@ public function processNode(Node $node, Scope $scope): array
$parameterResult->no() ? 'compatible' : 'contravariant',
$parentParameter->getName(),
$parentParameterType->describe(VerbosityLevel::value()),
$parentMethod->getDeclaringClass()->getDisplayName(),
$parentMethodDeclaringClass->getDisplayName(),
$parentMethod->getName(),
))->build();
}
Expand All @@ -112,7 +114,7 @@ public function processNode(Node $node, Scope $scope): array
}

/**
* @return ExtendedMethodReflection[]
* @return list<array{ExtendedMethodReflection, ClassReflection}>
*/
private function collectParentMethods(string $methodName, ClassReflection $class): array
{
Expand All @@ -122,7 +124,7 @@ private function collectParentMethods(string $methodName, ClassReflection $class
if ($parentClass !== null && $parentClass->hasNativeMethod($methodName)) {
$parentMethod = $parentClass->getNativeMethod($methodName);
if (!$parentMethod->isPrivate()) {
$parentMethods[] = $parentMethod;
$parentMethods[] = [$parentMethod, $parentMethod->getDeclaringClass()];
}
}

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

$parentMethods[] = $interface->getNativeMethod($methodName);
$method = $interface->getNativeMethod($methodName);
$parentMethods[] = [$method, $method->getDeclaringClass()];
}

if ($this->abstractTraitMethod) {
foreach ($class->getTraits(true) as $trait) {
if (!$trait->hasNativeMethod($methodName)) {
$nativeTraitReflection = $trait->getNativeReflection();
if (!$nativeTraitReflection->hasMethod($methodName)) {
continue;
}

$method = $trait->getNativeMethod($methodName);
$isAbstract = $method->isAbstract();
if (is_bool($isAbstract)) {
if ($isAbstract) {
$parentMethods[] = $method;
}
} elseif ($isAbstract->yes()) {
$parentMethods[] = $method;
$methodReflection = $nativeTraitReflection->getMethod($methodName);
$isAbstract = $methodReflection->isAbstract();
if (!$isAbstract) {
continue;
}

$parentMethods[] = [
$this->phpClassReflectionExtension->createUserlandMethodReflection(
$trait,
$class,
new NativeBuiltinMethodReflection($methodReflection),
),
$trait->getNativeMethod($methodName)->getDeclaringClass(),
];
}
}

Expand Down
23 changes: 21 additions & 2 deletions tests/PHPStan/Rules/Methods/MethodSignatureRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ protected function getRule(): Rule
{
$phpVersion = new PhpVersion(PHP_VERSION_ID);

$phpClassReflectionExtension = self::getContainer()->getByType(PhpClassReflectionExtension::class);

return new OverridingMethodRule(
$phpVersion,
new MethodSignatureRule($this->reportMaybes, $this->reportStatic, true),
new MethodSignatureRule($phpClassReflectionExtension, $this->reportMaybes, $this->reportStatic, true),
true,
new MethodParameterComparisonHelper($phpVersion, true),
self::getContainer()->getByType(PhpClassReflectionExtension::class),
$phpClassReflectionExtension,
true,
true,
false,
Expand Down Expand Up @@ -443,4 +445,21 @@ public function testTraits(): void
]);
}

public function testBug10166(): void
{
if (PHP_VERSION_ID < 80000) {
$this->markTestSkipped('Test requires PHP 8.0.');
}

$this->reportMaybes = true;
$this->reportStatic = true;

$this->analyse([__DIR__ . '/data/bug-10166.php'], [
[
'Return type Bug10166\ReturnTypeClass2|null of method Bug10166\ReturnTypeClass2::createSelf() is not covariant with return type Bug10166\ReturnTypeClass2 of method Bug10166\ReturnTypeTrait::createSelf().',
23,
],
]);
}

}
6 changes: 4 additions & 2 deletions tests/PHPStan/Rules/Methods/OverridingMethodRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ protected function getRule(): Rule
{
$phpVersion = new PhpVersion($this->phpVersionId);

$phpClassReflectionExtension = self::getContainer()->getByType(PhpClassReflectionExtension::class);

return new OverridingMethodRule(
$phpVersion,
new MethodSignatureRule(true, true, true),
new MethodSignatureRule($phpClassReflectionExtension, true, true, true),
false,
new MethodParameterComparisonHelper($phpVersion, true),
self::getContainer()->getByType(PhpClassReflectionExtension::class),
$phpClassReflectionExtension,
true,
true,
$this->checkMissingOverrideMethodAttribute,
Expand Down
27 changes: 27 additions & 0 deletions tests/PHPStan/Rules/Methods/data/bug-10166.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Bug10166;

trait ReturnTypeTrait {
abstract public static function createSelf(): self;
}

final class ReturnTypeClass
{
use ReturnTypeTrait;

public static function createSelf(): self
{
return new self();
}
}

final class ReturnTypeClass2
{
use ReturnTypeTrait;

public static function createSelf(): ?self
{
return new self();
}
}

0 comments on commit 85fcd5f

Please sign in to comment.