Skip to content

Commit ac4d3de

Browse files
committed
New parameter checkMissingOverrideMethodAttribute
1 parent 5a19a9c commit ac4d3de

8 files changed

+69
-1
lines changed

conf/config.level0.neon

+1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ services:
154154
checkPhpDocMethodSignatures: %checkPhpDocMethodSignatures%
155155
genericPrototypeMessage: %featureToggles.genericPrototypeMessage%
156156
finalByPhpDoc: %featureToggles.finalByPhpDoc%
157+
checkMissingOverrideMethodAttribute: %checkMissingOverrideMethodAttribute%
157158
tags:
158159
- phpstan.rules.rule
159160

conf/config.neon

+1
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ parameters:
116116
reportMaybesInPropertyPhpDocTypes: false
117117
reportStaticMethodSignatures: false
118118
reportWrongPhpDocTypeInVarTag: false
119+
checkMissingOverrideMethodAttribute: false
119120
mixinExcludeClasses: []
120121
scanFiles: []
121122
scanDirectories: []

conf/parametersSchema.neon

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ parametersSchema:
114114
reportMaybesInPropertyPhpDocTypes: bool()
115115
reportStaticMethodSignatures: bool()
116116
reportWrongPhpDocTypeInVarTag: bool()
117+
checkMissingOverrideMethodAttribute: bool()
117118
parallel: structure([
118119
jobSize: int(),
119120
processTimeout: float(),

src/PhpDoc/StubValidator.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ private function getRuleRegistry(Container $container): RuleRegistry
164164
new ExistingClassesInTypehintsRule($functionDefinitionCheck),
165165
new \PHPStan\Rules\Functions\ExistingClassesInTypehintsRule($functionDefinitionCheck),
166166
new ExistingClassesInPropertiesRule($reflectionProvider, $classCaseSensitivityCheck, $unresolvableTypeHelper, $phpVersion, true, false),
167-
new OverridingMethodRule($phpVersion, new MethodSignatureRule(true, true, $container->getParameter('featureToggles')['abstractTraitMethod']), true, new MethodParameterComparisonHelper($phpVersion, $container->getParameter('featureToggles')['genericPrototypeMessage']), $container->getParameter('featureToggles')['genericPrototypeMessage'], $container->getParameter('featureToggles')['finalByPhpDoc']),
167+
new OverridingMethodRule($phpVersion, new MethodSignatureRule(true, true, $container->getParameter('featureToggles')['abstractTraitMethod']), true, new MethodParameterComparisonHelper($phpVersion, $container->getParameter('featureToggles')['genericPrototypeMessage']), $container->getParameter('featureToggles')['genericPrototypeMessage'], $container->getParameter('featureToggles')['finalByPhpDoc'], $container->getParameter('checkMissingOverrideMethodAttribute')),
168168
new DuplicateDeclarationRule(),
169169
new LocalTypeAliasesRule($localTypeAliasesCheck),
170170
new LocalTypeTraitAliasesRule($localTypeAliasesCheck, $reflectionProvider),

src/Rules/Methods/OverridingMethodRule.php

+14
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public function __construct(
3737
private MethodParameterComparisonHelper $methodParameterComparisonHelper,
3838
private bool $genericPrototypeMessage,
3939
private bool $finalByPhpDoc,
40+
private bool $checkMissingOverrideMethodAttribute,
4041
)
4142
{
4243
}
@@ -96,6 +97,19 @@ public function processNode(Node $node, Scope $scope): array
9697
[$prototype, $checkVisibility] = $prototypeData;
9798

9899
$messages = [];
100+
if (
101+
$this->phpVersion->supportsOverrideAttribute()
102+
&& $this->checkMissingOverrideMethodAttribute
103+
&& !$this->hasOverrideAttribute($node->getOriginalNode())
104+
) {
105+
$messages[] = RuleErrorBuilder::message(sprintf(
106+
'Method %s::%s() overrides method %s::%s() but is missing the #[Override] attribute.',
107+
$method->getDeclaringClass()->getDisplayName(),
108+
$method->getName(),
109+
$prototype->getDeclaringClass()->getDisplayName($this->genericPrototypeMessage),
110+
$prototype->getName(),
111+
))->build();
112+
}
99113
if ($prototype->isFinalByKeyword()->yes()) {
100114
$messages[] = RuleErrorBuilder::message(sprintf(
101115
'Method %s::%s() overrides final method %s::%s().',

tests/PHPStan/Rules/Methods/MethodSignatureRuleTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ protected function getRule(): Rule
2828
new MethodParameterComparisonHelper($phpVersion, true),
2929
true,
3030
true,
31+
false,
3132
);
3233
}
3334

tests/PHPStan/Rules/Methods/OverridingMethodRuleTest.php

+27
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ class OverridingMethodRuleTest extends RuleTestCase
1717

1818
private int $phpVersionId;
1919

20+
private bool $checkMissingOverrideMethodAttribute = false;
21+
2022
protected function getRule(): Rule
2123
{
2224
$phpVersion = new PhpVersion($this->phpVersionId);
@@ -28,6 +30,7 @@ protected function getRule(): Rule
2830
new MethodParameterComparisonHelper($phpVersion, true),
2931
true,
3032
true,
33+
$this->checkMissingOverrideMethodAttribute,
3134
);
3235
}
3336

@@ -751,4 +754,28 @@ public function testOverrideAttribute(): void
751754
]);
752755
}
753756

757+
public function dataCheckMissingOverrideAttribute(): iterable
758+
{
759+
yield [false, 80000, []];
760+
yield [true, 80000, []];
761+
yield [false, 80300, []];
762+
yield [true, 80300, [
763+
[
764+
'Method CheckMissingOverrideAttr\Bar::doFoo() overrides method CheckMissingOverrideAttr\Foo::doFoo() but is missing the #[Override] attribute.',
765+
18,
766+
],
767+
]];
768+
}
769+
770+
/**
771+
* @dataProvider dataCheckMissingOverrideAttribute
772+
* @param list<array{0: string, 1: int, 2?: string}> $errors
773+
*/
774+
public function testCheckMissingOverrideAttribute(bool $checkMissingOverrideMethodAttribute, int $phpVersionId, array $errors): void
775+
{
776+
$this->checkMissingOverrideMethodAttribute = $checkMissingOverrideMethodAttribute;
777+
$this->phpVersionId = $phpVersionId;
778+
$this->analyse([__DIR__ . '/data/check-missing-override-attr.php'], $errors);
779+
}
780+
754781
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace CheckMissingOverrideAttr;
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+
}

0 commit comments

Comments
 (0)