Skip to content

Commit 5c22b9d

Browse files
committed
PromoteParameterRule - deduplicate already reported errors
1 parent 2443663 commit 5c22b9d

File tree

4 files changed

+146
-0
lines changed

4 files changed

+146
-0
lines changed

src/Rules/Playground/PromoteParameterRule.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,15 @@
44

55
use PhpParser\Node;
66
use PHPStan\Analyser\Scope;
7+
use PHPStan\DependencyInjection\Container;
8+
use PHPStan\DependencyInjection\MissingServiceException;
79
use PHPStan\Rules\FixableNodeRuleError;
10+
use PHPStan\Rules\LazyRegistry;
811
use PHPStan\Rules\LineRuleError;
912
use PHPStan\Rules\Rule;
1013
use PHPStan\Rules\RuleErrorBuilder;
14+
use function count;
15+
use function get_class;
1116
use function sprintf;
1217

1318
/**
@@ -17,12 +22,16 @@
1722
final class PromoteParameterRule implements Rule
1823
{
1924

25+
/** @var Rule<TNodeType>|false|null */
26+
private Rule|false|null $originalRule = null;
27+
2028
/**
2129
* @param Rule<TNodeType> $rule
2230
* @param class-string<TNodeType> $nodeType
2331
*/
2432
public function __construct(
2533
private Rule $rule,
34+
private Container $container,
2635
private string $nodeType,
2736
private bool $parameterValue,
2837
private string $parameterName,
@@ -35,6 +44,49 @@ public function getNodeType(): string
3544
return $this->nodeType;
3645
}
3746

47+
/**
48+
* @return Rule<TNodeType>|null
49+
*/
50+
private function getOriginalRule(): ?Rule
51+
{
52+
if ($this->originalRule === false) {
53+
return null;
54+
}
55+
56+
if ($this->originalRule !== null) {
57+
return $this->originalRule;
58+
}
59+
60+
$originalRule = null;
61+
try {
62+
/** @var Rule<TNodeType> $originalRule */
63+
$originalRule = $this->container->getByType(get_class($this->rule));
64+
$taggedRules = $this->container->getServicesByTag(LazyRegistry::RULE_TAG);
65+
$found = false;
66+
foreach ($taggedRules as $rule) {
67+
if ($originalRule !== $rule) {
68+
continue;
69+
}
70+
71+
$found = true;
72+
break;
73+
}
74+
75+
if (!$found) {
76+
$originalRule = null;
77+
}
78+
} catch (MissingServiceException) {
79+
// pass
80+
}
81+
82+
if ($originalRule === null) {
83+
$this->originalRule = false;
84+
return null;
85+
}
86+
87+
return $this->originalRule = $originalRule;
88+
}
89+
3890
public function processNode(Node $node, Scope $scope): array
3991
{
4092
if ($this->parameterValue) {
@@ -45,6 +97,14 @@ public function processNode(Node $node, Scope $scope): array
4597
return [];
4698
}
4799

100+
$originalRule = $this->getOriginalRule();
101+
if ($originalRule !== null) {
102+
$originalRuleErrors = $originalRule->processNode($node, $scope);
103+
if (count($originalRuleErrors) > 0) {
104+
return [];
105+
}
106+
}
107+
48108
$errors = [];
49109
foreach ($this->rule->processNode($node, $scope) as $error) {
50110
$builder = RuleErrorBuilder::message($error->getMessage())

tests/PHPStan/Rules/Playground/PromoteParameterRuleTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ protected function getRule(): Rule
2121
self::getContainer(),
2222
[],
2323
)),
24+
self::getContainer(),
2425
ClassPropertiesNode::class,
2526
false,
2627
'checkUninitializedProperties',
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Playground;
4+
5+
use PHPStan\Node\InClassMethodNode;
6+
use PHPStan\Php\PhpVersion;
7+
use PHPStan\Reflection\Php\PhpClassReflectionExtension;
8+
use PHPStan\Rules\Methods\MethodParameterComparisonHelper;
9+
use PHPStan\Rules\Methods\MethodSignatureRule;
10+
use PHPStan\Rules\Methods\MethodVisibilityComparisonHelper;
11+
use PHPStan\Rules\Methods\OverridingMethodRule;
12+
use PHPStan\Rules\Rule;
13+
use PHPStan\Testing\RuleTestCase;
14+
15+
/**
16+
* @extends RuleTestCase<PromoteParameterRule<InClassMethodNode>>
17+
*/
18+
class PromoteParameterRuleWithOriginalRuleTest extends RuleTestCase
19+
{
20+
21+
protected function getRule(): Rule
22+
{
23+
return new PromoteParameterRule(
24+
new OverridingMethodRule(
25+
self::getContainer()->getByType(PhpVersion::class),
26+
self::getContainer()->getByType(MethodSignatureRule::class),
27+
true,
28+
self::getContainer()->getByType(MethodParameterComparisonHelper::class),
29+
self::getContainer()->getByType(MethodVisibilityComparisonHelper::class),
30+
self::getContainer()->getByType(PhpClassReflectionExtension::class),
31+
true,
32+
),
33+
self::getContainer(),
34+
InClassMethodNode::class,
35+
false,
36+
'checkMissingOverrideMethodAttribute',
37+
);
38+
}
39+
40+
public function testRule(): void
41+
{
42+
$this->analyse([__DIR__ . '/data/promote-missing-override.php'], [
43+
[
44+
'Method PromoteMissingOverride\Bar::doFoo() overrides method PromoteMissingOverride\Foo::doFoo() but is missing the #[\Override] attribute.',
45+
18,
46+
'This error would be reported if the <fg=cyan>checkMissingOverrideMethodAttribute: true</> parameter was enabled in your <fg=cyan>%configurationFile%</>.',
47+
],
48+
]);
49+
}
50+
51+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace PromoteMissingOverride;
4+
5+
class Foo
6+
{
7+
8+
public function doFoo(): void
9+
{
10+
11+
}
12+
13+
}
14+
15+
class Bar extends Foo
16+
{
17+
18+
public function doFoo(): void
19+
{
20+
21+
}
22+
23+
24+
public function doBar(): void
25+
{
26+
27+
}
28+
29+
#[\Override]
30+
public function doBaz(): void
31+
{
32+
}
33+
34+
}

0 commit comments

Comments
 (0)