Skip to content

Commit ca2c66c

Browse files
committed
Bleeding edge - OverridingMethodRule - include template types in prototype declaring class
1 parent 5ff0a2e commit ca2c66c

8 files changed

+42
-33
lines changed

conf/bleedingEdge.neon

+1
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@ parameters:
3535
allInvalidPhpDocs: true
3636
strictStaticMethodTemplateTypeVariance: true
3737
propertyVariance: true
38+
genericPrototypeMessage: true

conf/config.level0.neon

+1
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ services:
145145
class: PHPStan\Rules\Methods\OverridingMethodRule
146146
arguments:
147147
checkPhpDocMethodSignatures: %checkPhpDocMethodSignatures%
148+
genericPrototypeMessage: %featureToggles.genericPrototypeMessage%
148149
tags:
149150
- phpstan.rules.rule
150151

conf/config.neon

+4
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ parameters:
6565
allInvalidPhpDocs: false
6666
strictStaticMethodTemplateTypeVariance: false
6767
propertyVariance: false
68+
genericPrototypeMessage: false
6869
fileExtensions:
6970
- php
7071
checkAdvancedIsset: false
@@ -300,6 +301,7 @@ parametersSchema:
300301
allInvalidPhpDocs: bool()
301302
strictStaticMethodTemplateTypeVariance: bool()
302303
propertyVariance: bool()
304+
genericPrototypeMessage: bool()
303305
])
304306
fileExtensions: listOf(string())
305307
checkAdvancedIsset: bool()
@@ -1085,6 +1087,8 @@ services:
10851087

10861088
-
10871089
class: PHPStan\Rules\Methods\MethodParameterComparisonHelper
1090+
arguments:
1091+
genericPrototypeMessage: %featureToggles.genericPrototypeMessage%
10881092

10891093
-
10901094
class: PHPStan\Rules\MissingTypehintCheck

src/PhpDoc/StubValidator.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ private function getRuleRegistry(Container $container): RuleRegistry
157157
new ExistingClassesInTypehintsRule($functionDefinitionCheck),
158158
new \PHPStan\Rules\Functions\ExistingClassesInTypehintsRule($functionDefinitionCheck),
159159
new ExistingClassesInPropertiesRule($reflectionProvider, $classCaseSensitivityCheck, $unresolvableTypeHelper, $phpVersion, true, false),
160-
new OverridingMethodRule($phpVersion, new MethodSignatureRule(true, true), true, new MethodParameterComparisonHelper($phpVersion)),
160+
new OverridingMethodRule($phpVersion, new MethodSignatureRule(true, true), true, new MethodParameterComparisonHelper($phpVersion, $container->getParameter('featureToggles')['genericPrototypeMessage']), $container->getParameter('featureToggles')['genericPrototypeMessage']),
161161
new DuplicateDeclarationRule(),
162162

163163
// level 2

src/Rules/Methods/MethodParameterComparisonHelper.php

+11-11
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
class MethodParameterComparisonHelper
2525
{
2626

27-
public function __construct(private PhpVersion $phpVersion)
27+
public function __construct(private PhpVersion $phpVersion, private bool $genericPrototypeMessage)
2828
{
2929
}
3030

@@ -47,7 +47,7 @@ public function compare(MethodPrototypeReflection $prototype, PhpMethodFromParse
4747
'Method %s::%s() overrides method %s::%s() but misses parameter #%d $%s.',
4848
$method->getDeclaringClass()->getDisplayName(),
4949
$method->getName(),
50-
$prototype->getDeclaringClass()->getDisplayName(false),
50+
$prototype->getDeclaringClass()->getDisplayName($this->genericPrototypeMessage),
5151
$prototype->getName(),
5252
$i + 1,
5353
$prototypeParameter->getName(),
@@ -73,7 +73,7 @@ public function compare(MethodPrototypeReflection $prototype, PhpMethodFromParse
7373
$method->getName(),
7474
$i + 1,
7575
$prototypeParameter->getName(),
76-
$prototype->getDeclaringClass()->getDisplayName(false),
76+
$prototype->getDeclaringClass()->getDisplayName($this->genericPrototypeMessage),
7777
$prototype->getName(),
7878
));
7979

@@ -92,7 +92,7 @@ public function compare(MethodPrototypeReflection $prototype, PhpMethodFromParse
9292
$method->getName(),
9393
$i + 1,
9494
$prototypeParameter->getName(),
95-
$prototype->getDeclaringClass()->getDisplayName(false),
95+
$prototype->getDeclaringClass()->getDisplayName($this->genericPrototypeMessage),
9696
$prototype->getName(),
9797
));
9898

@@ -133,7 +133,7 @@ public function compare(MethodPrototypeReflection $prototype, PhpMethodFromParse
133133
$method->getName(),
134134
$i + 1,
135135
$prototypeParameter->getName(),
136-
$prototype->getDeclaringClass()->getDisplayName(false),
136+
$prototype->getDeclaringClass()->getDisplayName($this->genericPrototypeMessage),
137137
$prototype->getName(),
138138
));
139139

@@ -181,7 +181,7 @@ public function compare(MethodPrototypeReflection $prototype, PhpMethodFromParse
181181
$i + $j + 1,
182182
$remainingPrototypeParameter->getName(),
183183
$remainingPrototypeParameter->getNativeType()->describe(VerbosityLevel::typeOnly()),
184-
$prototype->getDeclaringClass()->getDisplayName(false),
184+
$prototype->getDeclaringClass()->getDisplayName($this->genericPrototypeMessage),
185185
$prototype->getName(),
186186
));
187187

@@ -201,7 +201,7 @@ public function compare(MethodPrototypeReflection $prototype, PhpMethodFromParse
201201
$method->getName(),
202202
$i + 1,
203203
$prototypeParameter->getName(),
204-
$prototype->getDeclaringClass()->getDisplayName(false),
204+
$prototype->getDeclaringClass()->getDisplayName($this->genericPrototypeMessage),
205205
$prototype->getName(),
206206
));
207207

@@ -223,7 +223,7 @@ public function compare(MethodPrototypeReflection $prototype, PhpMethodFromParse
223223
$method->getName(),
224224
$i + 1,
225225
$prototypeParameter->getName(),
226-
$prototype->getDeclaringClass()->getDisplayName(false),
226+
$prototype->getDeclaringClass()->getDisplayName($this->genericPrototypeMessage),
227227
$prototype->getName(),
228228
));
229229

@@ -253,7 +253,7 @@ public function compare(MethodPrototypeReflection $prototype, PhpMethodFromParse
253253
$i + 1,
254254
$prototypeParameter->getName(),
255255
$prototypeParameterType->describe(VerbosityLevel::typeOnly()),
256-
$prototype->getDeclaringClass()->getDisplayName(false),
256+
$prototype->getDeclaringClass()->getDisplayName($this->genericPrototypeMessage),
257257
$prototype->getName(),
258258
));
259259

@@ -281,7 +281,7 @@ public function compare(MethodPrototypeReflection $prototype, PhpMethodFromParse
281281
$i + 1,
282282
$prototypeParameter->getName(),
283283
$prototypeParameterType->describe(VerbosityLevel::typeOnly()),
284-
$prototype->getDeclaringClass()->getDisplayName(false),
284+
$prototype->getDeclaringClass()->getDisplayName($this->genericPrototypeMessage),
285285
$prototype->getName(),
286286
));
287287

@@ -301,7 +301,7 @@ public function compare(MethodPrototypeReflection $prototype, PhpMethodFromParse
301301
$i + 1,
302302
$prototypeParameter->getName(),
303303
$prototypeParameterType->describe(VerbosityLevel::typeOnly()),
304-
$prototype->getDeclaringClass()->getDisplayName(false),
304+
$prototype->getDeclaringClass()->getDisplayName($this->genericPrototypeMessage),
305305
$prototype->getName(),
306306
));
307307

src/Rules/Methods/OverridingMethodRule.php

+10-9
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public function __construct(
2929
private MethodSignatureRule $methodSignatureRule,
3030
private bool $checkPhpDocMethodSignatures,
3131
private MethodParameterComparisonHelper $methodParameterComparisonHelper,
32+
private bool $genericPrototypeMessage,
3233
)
3334
{
3435
}
@@ -53,7 +54,7 @@ public function processNode(Node $node, Scope $scope): array
5354
'Method %s::%s() overrides final method %s::%s().',
5455
$method->getDeclaringClass()->getDisplayName(),
5556
$method->getName(),
56-
$parent->getDisplayName(false),
57+
$parent->getDisplayName($this->genericPrototypeMessage),
5758
$parentConstructor->getName(),
5859
))->nonIgnorable()->build(),
5960
], $node, $scope);
@@ -74,7 +75,7 @@ public function processNode(Node $node, Scope $scope): array
7475
'Method %s::%s() overrides final method %s::%s().',
7576
$method->getDeclaringClass()->getDisplayName(),
7677
$method->getName(),
77-
$prototype->getDeclaringClass()->getDisplayName(false),
78+
$prototype->getDeclaringClass()->getDisplayName($this->genericPrototypeMessage),
7879
$prototype->getName(),
7980
))->nonIgnorable()->build();
8081
}
@@ -85,7 +86,7 @@ public function processNode(Node $node, Scope $scope): array
8586
'Non-static method %s::%s() overrides static method %s::%s().',
8687
$method->getDeclaringClass()->getDisplayName(),
8788
$method->getName(),
88-
$prototype->getDeclaringClass()->getDisplayName(false),
89+
$prototype->getDeclaringClass()->getDisplayName($this->genericPrototypeMessage),
8990
$prototype->getName(),
9091
))->nonIgnorable()->build();
9192
}
@@ -94,7 +95,7 @@ public function processNode(Node $node, Scope $scope): array
9495
'Static method %s::%s() overrides non-static method %s::%s().',
9596
$method->getDeclaringClass()->getDisplayName(),
9697
$method->getName(),
97-
$prototype->getDeclaringClass()->getDisplayName(false),
98+
$prototype->getDeclaringClass()->getDisplayName($this->genericPrototypeMessage),
9899
$prototype->getName(),
99100
))->nonIgnorable()->build();
100101
}
@@ -106,7 +107,7 @@ public function processNode(Node $node, Scope $scope): array
106107
$method->isPrivate() ? 'Private' : 'Protected',
107108
$method->getDeclaringClass()->getDisplayName(),
108109
$method->getName(),
109-
$prototype->getDeclaringClass()->getDisplayName(false),
110+
$prototype->getDeclaringClass()->getDisplayName($this->genericPrototypeMessage),
110111
$prototype->getName(),
111112
))->nonIgnorable()->build();
112113
}
@@ -115,7 +116,7 @@ public function processNode(Node $node, Scope $scope): array
115116
'Private method %s::%s() overriding protected method %s::%s() should be protected or public.',
116117
$method->getDeclaringClass()->getDisplayName(),
117118
$method->getName(),
118-
$prototype->getDeclaringClass()->getDisplayName(false),
119+
$prototype->getDeclaringClass()->getDisplayName($this->genericPrototypeMessage),
119120
$prototype->getName(),
120121
))->nonIgnorable()->build();
121122
}
@@ -143,7 +144,7 @@ public function processNode(Node $node, Scope $scope): array
143144
$method->getDeclaringClass()->getDisplayName(),
144145
$method->getName(),
145146
$prototype->getTentativeReturnType()->describe(VerbosityLevel::typeOnly()),
146-
$prototype->getDeclaringClass()->getDisplayName(false),
147+
$prototype->getDeclaringClass()->getDisplayName($this->genericPrototypeMessage),
147148
$prototype->getName(),
148149
))->tip('Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.')->nonIgnorable()->build();
149150
}
@@ -165,7 +166,7 @@ public function processNode(Node $node, Scope $scope): array
165166
$method->getDeclaringClass()->getDisplayName(),
166167
$method->getName(),
167168
$prototypeReturnType->describe(VerbosityLevel::typeOnly()),
168-
$prototype->getDeclaringClass()->getDisplayName(false),
169+
$prototype->getDeclaringClass()->getDisplayName($this->genericPrototypeMessage),
169170
$prototype->getName(),
170171
))->nonIgnorable()->build();
171172
} else {
@@ -175,7 +176,7 @@ public function processNode(Node $node, Scope $scope): array
175176
$method->getDeclaringClass()->getDisplayName(),
176177
$method->getName(),
177178
$prototypeReturnType->describe(VerbosityLevel::typeOnly()),
178-
$prototype->getDeclaringClass()->getDisplayName(false),
179+
$prototype->getDeclaringClass()->getDisplayName($this->genericPrototypeMessage),
179180
$prototype->getName(),
180181
))->nonIgnorable()->build();
181182
}

tests/PHPStan/Rules/Methods/MethodSignatureRuleTest.php

+3-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ protected function getRule(): Rule
2525
$phpVersion,
2626
new MethodSignatureRule($this->reportMaybes, $this->reportStatic),
2727
true,
28-
new MethodParameterComparisonHelper($phpVersion),
28+
new MethodParameterComparisonHelper($phpVersion, true),
29+
true,
2930
);
3031
}
3132

@@ -384,7 +385,7 @@ public function testBug7652(): void
384385
$this->reportStatic = true;
385386
$this->analyse([__DIR__ . '/data/bug-7652.php'], [
386387
[
387-
'Return type mixed of method Bug7652\Options::offsetGet() is not covariant with tentative return type mixed of method ArrayAccess::offsetGet().',
388+
'Return type mixed of method Bug7652\Options::offsetGet() is not covariant with tentative return type mixed of method ArrayAccess<key-of<TArray of array>,value-of<TArray of array>>::offsetGet().',
388389
23,
389390
'Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.',
390391
],

tests/PHPStan/Rules/Methods/OverridingMethodRuleTest.php

+11-10
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ protected function getRule(): Rule
2525
$phpVersion,
2626
new MethodSignatureRule(true, true),
2727
false,
28-
new MethodParameterComparisonHelper($phpVersion),
28+
new MethodParameterComparisonHelper($phpVersion, true),
29+
true,
2930
);
3031
}
3132

@@ -84,7 +85,7 @@ public function testOverridingFinalMethod(int $phpVersion, string $contravariant
8485
115,
8586
],
8687
[
87-
'Parameter #1 $size (int) of method OverridingFinalMethod\FixedArray::setSize() is not ' . $contravariantMessage . ' with parameter #1 $size (mixed) of method SplFixedArray::setSize().',
88+
'Parameter #1 $size (int) of method OverridingFinalMethod\FixedArray::setSize() is not ' . $contravariantMessage . ' with parameter #1 $size (mixed) of method SplFixedArray<mixed>::setSize().',
8889
125,
8990
],
9091
[
@@ -120,7 +121,7 @@ public function testOverridingFinalMethod(int $phpVersion, string $contravariant
120121
280,
121122
],
122123
[
123-
'Parameter #1 $index (int) of method OverridingFinalMethod\FixedArrayOffsetExists::offsetExists() is not ' . $contravariantMessage . ' with parameter #1 $offset (mixed) of method ArrayAccess::offsetExists().',
124+
'Parameter #1 $index (int) of method OverridingFinalMethod\FixedArrayOffsetExists::offsetExists() is not ' . $contravariantMessage . ' with parameter #1 $offset (mixed) of method ArrayAccess<int,mixed>::offsetExists().',
124125
313,
125126
],
126127
];
@@ -472,37 +473,37 @@ public function dataTentativeReturnTypes(): array
472473
80100,
473474
[
474475
[
475-
'Return type mixed of method TentativeReturnTypes\Foo::getIterator() is not covariant with tentative return type Traversable of method IteratorAggregate::getIterator().',
476+
'Return type mixed of method TentativeReturnTypes\Foo::getIterator() is not covariant with tentative return type Traversable of method IteratorAggregate<mixed,mixed>::getIterator().',
476477
8,
477478
'Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.',
478479
],
479480
[
480-
'Return type string of method TentativeReturnTypes\Lorem::getIterator() is not covariant with tentative return type Traversable of method IteratorAggregate::getIterator().',
481+
'Return type string of method TentativeReturnTypes\Lorem::getIterator() is not covariant with tentative return type Traversable of method IteratorAggregate<mixed,mixed>::getIterator().',
481482
40,
482483
'Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.',
483484
],
484485
[
485-
'Return type mixed of method TentativeReturnTypes\UntypedIterator::current() is not covariant with tentative return type mixed of method Iterator::current().',
486+
'Return type mixed of method TentativeReturnTypes\UntypedIterator::current() is not covariant with tentative return type mixed of method Iterator<mixed,mixed>::current().',
486487
75,
487488
'Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.',
488489
],
489490
[
490-
'Return type mixed of method TentativeReturnTypes\UntypedIterator::next() is not covariant with tentative return type void of method Iterator::next().',
491+
'Return type mixed of method TentativeReturnTypes\UntypedIterator::next() is not covariant with tentative return type void of method Iterator<mixed,mixed>::next().',
491492
79,
492493
'Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.',
493494
],
494495
[
495-
'Return type mixed of method TentativeReturnTypes\UntypedIterator::key() is not covariant with tentative return type mixed of method Iterator::key().',
496+
'Return type mixed of method TentativeReturnTypes\UntypedIterator::key() is not covariant with tentative return type mixed of method Iterator<mixed,mixed>::key().',
496497
83,
497498
'Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.',
498499
],
499500
[
500-
'Return type mixed of method TentativeReturnTypes\UntypedIterator::valid() is not covariant with tentative return type bool of method Iterator::valid().',
501+
'Return type mixed of method TentativeReturnTypes\UntypedIterator::valid() is not covariant with tentative return type bool of method Iterator<mixed,mixed>::valid().',
501502
87,
502503
'Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.',
503504
],
504505
[
505-
'Return type mixed of method TentativeReturnTypes\UntypedIterator::rewind() is not covariant with tentative return type void of method Iterator::rewind().',
506+
'Return type mixed of method TentativeReturnTypes\UntypedIterator::rewind() is not covariant with tentative return type void of method Iterator<mixed,mixed>::rewind().',
506507
91,
507508
'Make it covariant, or use the #[\ReturnTypeWillChange] attribute to temporarily suppress the error.',
508509
],

0 commit comments

Comments
 (0)