Skip to content

Commit cfba309

Browse files
committed
Merge branch 2.1.x into 2.2.x
2 parents c853aa2 + f8d7b8a commit cfba309

7 files changed

Lines changed: 258 additions & 0 deletions

File tree

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ lint:
139139
--exclude tests/PHPStan/Rules/Variables/data/bug-14349.php \
140140
--exclude tests/PHPStan/Rules/Variables/data/bug-14352.php \
141141
--exclude tests/PHPStan/Rules/Variables/data/bug-14351.php \
142+
--exclude tests/PHPStan/Rules/Properties/data/bug-14457.php \
142143
src tests
143144

144145
install-paratest:

src/Rules/Methods/OverridingMethodRule.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,23 @@ public function processNode(Node $node, Scope&NodeCallbackInvoker&CollectedDataE
8787
->build(),
8888
], $node, $scope);
8989
}
90+
$parentAbstract = $parentConstructor->isAbstract();
91+
if (
92+
$method->isAbstract()->yes()
93+
&& !(is_bool($parentAbstract) ? $parentAbstract : $parentAbstract->yes())
94+
) {
95+
return $this->addErrors([
96+
RuleErrorBuilder::message(sprintf(
97+
'Cannot make non-abstract method %s::%s() abstract in class %s.',
98+
$parentConstructor->getDeclaringClass()->getDisplayName(true),
99+
$parentConstructor->getName(),
100+
$method->getDeclaringClass()->getDisplayName(),
101+
))
102+
->nonIgnorable()
103+
->identifier('method.abstractOverridingNonAbstract')
104+
->build(),
105+
], $node, $scope);
106+
}
90107
}
91108
}
92109

@@ -189,6 +206,22 @@ public function processNode(Node $node, Scope&NodeCallbackInvoker&CollectedDataE
189206
->build();
190207
}
191208

209+
$prototypeAbstract = $prototype->isAbstract();
210+
if (
211+
$method->isAbstract()->yes()
212+
&& !(is_bool($prototypeAbstract) ? $prototypeAbstract : $prototypeAbstract->yes())
213+
) {
214+
$messages[] = RuleErrorBuilder::message(sprintf(
215+
'Cannot make non-abstract method %s::%s() abstract in class %s.',
216+
$prototypeDeclaringClass->getDisplayName(true),
217+
$prototype->getName(),
218+
$method->getDeclaringClass()->getDisplayName(),
219+
))
220+
->nonIgnorable()
221+
->identifier('method.abstractOverridingNonAbstract')
222+
->build();
223+
}
224+
192225
if ($checkVisibility) {
193226
$messages = array_merge($messages, $this->methodVisibilityComparisonHelper->compare($prototype, $prototypeDeclaringClass, $method));
194227
}

src/Rules/Properties/OverridingPropertyRule.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,23 @@ public function processNode(Node $node, Scope $scope): array
178178
))->identifier('property.notWritable')->nonIgnorable()->build();
179179
}
180180
}
181+
if ($node->isAbstract() && $prototype->isAbstract()->no()) {
182+
foreach (['get', 'set'] as $hookType) {
183+
if (!$propertyReflection->hasHook($hookType) || !$prototype->hasHook($hookType)) {
184+
continue;
185+
}
186+
$errors[] = RuleErrorBuilder::message(sprintf(
187+
'Cannot make non-abstract method %s::$%s::%s() abstract in class %s.',
188+
$prototype->getDeclaringClass()->getDisplayName(true),
189+
$node->getName(),
190+
$hookType,
191+
$classReflection->getDisplayName(),
192+
))
193+
->nonIgnorable()
194+
->identifier('property.abstractOverridingNonAbstractHook')
195+
->build();
196+
}
197+
}
181198
}
182199

183200
if ($prototype->isPublic()) {

tests/PHPStan/Rules/Methods/OverridingMethodRuleTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,4 +853,31 @@ public function testFixWithTabs(): void
853853
$this->fix(__DIR__ . '/data/fix-with-tabs.php', __DIR__ . '/data/fix-with-tabs.php.fixed');
854854
}
855855

856+
public function testBug14457(): void
857+
{
858+
$this->phpVersionId = PHP_VERSION_ID;
859+
$this->analyse([__DIR__ . '/data/bug-14457.php'], [
860+
[
861+
'Cannot make non-abstract method Bug14457\ParentClass::foo() abstract in class Bug14457\ChildClass.',
862+
12,
863+
],
864+
[
865+
'Cannot make non-abstract method Bug14457\StaticParent::staticMethod() abstract in class Bug14457\StaticChild.',
866+
56,
867+
],
868+
[
869+
'Cannot make non-abstract method Bug14457\ProtectedParent::protectedMethod() abstract in class Bug14457\ProtectedChild.',
870+
67,
871+
],
872+
[
873+
'Cannot make non-abstract method Bug14457\GrandParent_::inherited() abstract in class Bug14457\Child_.',
874+
81,
875+
],
876+
[
877+
'Cannot make non-abstract method Bug14457\ConstructorParent::__construct() abstract in class Bug14457\ConstructorChild.',
878+
91,
879+
],
880+
]);
881+
}
882+
856883
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?php // lint >= 8.0
2+
3+
namespace Bug14457;
4+
5+
abstract class ParentClass {
6+
public function foo(): int {
7+
return 42;
8+
}
9+
}
10+
11+
abstract class ChildClass extends ParentClass {
12+
abstract public function foo(): int;
13+
}
14+
15+
// OK: abstract method overriding abstract method
16+
abstract class AbstractParent {
17+
abstract public function bar(): int;
18+
}
19+
20+
abstract class AbstractChild extends AbstractParent {
21+
abstract public function bar(): int;
22+
}
23+
24+
// OK: non-abstract method overriding non-abstract method
25+
abstract class ConcreteParent {
26+
public function baz(): int {
27+
return 1;
28+
}
29+
}
30+
31+
abstract class ConcreteChild extends ConcreteParent {
32+
public function baz(): int {
33+
return 2;
34+
}
35+
}
36+
37+
// OK: non-abstract method overriding abstract method (implementing it)
38+
abstract class AbstractParent2 {
39+
abstract public function qux(): int;
40+
}
41+
42+
abstract class ConcreteChild2 extends AbstractParent2 {
43+
public function qux(): int {
44+
return 1;
45+
}
46+
}
47+
48+
// abstract static method overriding non-abstract static method
49+
abstract class StaticParent {
50+
public static function staticMethod(): int {
51+
return 1;
52+
}
53+
}
54+
55+
abstract class StaticChild extends StaticParent {
56+
abstract public static function staticMethod(): int;
57+
}
58+
59+
// abstract protected method overriding non-abstract protected method
60+
abstract class ProtectedParent {
61+
protected function protectedMethod(): int {
62+
return 1;
63+
}
64+
}
65+
66+
abstract class ProtectedChild extends ProtectedParent {
67+
abstract protected function protectedMethod(): int;
68+
}
69+
70+
// multiple levels of inheritance
71+
abstract class GrandParent_ {
72+
public function inherited(): int {
73+
return 1;
74+
}
75+
}
76+
77+
abstract class Parent_ extends GrandParent_ {
78+
}
79+
80+
abstract class Child_ extends Parent_ {
81+
abstract public function inherited(): int;
82+
}
83+
84+
// abstract constructor overriding non-abstract constructor
85+
abstract class ConstructorParent {
86+
public function __construct() {
87+
}
88+
}
89+
90+
abstract class ConstructorChild extends ConstructorParent {
91+
abstract public function __construct();
92+
}
93+
94+
// OK: abstract constructor overriding abstract constructor
95+
abstract class AbstractConstructorParent {
96+
abstract public function __construct();
97+
}
98+
99+
abstract class AbstractConstructorChild extends AbstractConstructorParent {
100+
abstract public function __construct();
101+
}

tests/PHPStan/Rules/Properties/OverridingPropertyRuleTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,4 +345,28 @@ public function testFixMissingOverrideAttribute(): void
345345
$this->fix(__DIR__ . '/data/property-override-attr-missing.php', __DIR__ . '/data/property-override-attr-missing.php.fixed');
346346
}
347347

348+
#[RequiresPhp('>= 8.4.0')]
349+
public function testBug14457(): void
350+
{
351+
$this->reportMaybes = true;
352+
$this->analyse([__DIR__ . '/data/bug-14457.php'], [
353+
[
354+
'Cannot make non-abstract method Bug14457Property\ParentClass::$bar::get() abstract in class Bug14457Property\ChildClass.',
355+
10,
356+
],
357+
[
358+
'Cannot make non-abstract method Bug14457Property\SetParent::$setProp::set() abstract in class Bug14457Property\SetChild.',
359+
42,
360+
],
361+
[
362+
'Cannot make non-abstract method Bug14457Property\BothParent::$bothProp::get() abstract in class Bug14457Property\BothChild.',
363+
54,
364+
],
365+
[
366+
'Cannot make non-abstract method Bug14457Property\BothParent::$bothProp::set() abstract in class Bug14457Property\BothChild.',
367+
54,
368+
],
369+
]);
370+
}
371+
348372
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php // lint >= 8.4
2+
3+
namespace Bug14457Property;
4+
5+
abstract class ParentClass {
6+
public int $bar { get => 42; }
7+
}
8+
9+
abstract class ChildClass extends ParentClass {
10+
abstract public int $bar { get; }
11+
}
12+
13+
// OK: abstract hook overriding abstract hook
14+
abstract class AbstractParent {
15+
abstract public int $prop { get; }
16+
}
17+
18+
abstract class AbstractChild extends AbstractParent {
19+
abstract public int $prop { get; }
20+
}
21+
22+
// OK: concrete hook overriding abstract hook
23+
abstract class ConcreteChild extends AbstractParent {
24+
public int $prop { get => 1; }
25+
}
26+
27+
// OK: concrete hook overriding concrete hook
28+
abstract class ConcreteParent {
29+
public int $val { get => 1; }
30+
}
31+
32+
abstract class ConcreteChild2 extends ConcreteParent {
33+
public int $val { get => 2; }
34+
}
35+
36+
// abstract set hook overriding non-abstract set hook
37+
abstract class SetParent {
38+
public int $setProp { set => $value; }
39+
}
40+
41+
abstract class SetChild extends SetParent {
42+
abstract public int $setProp { set; }
43+
}
44+
45+
// both get and set hooks abstract overriding non-abstract
46+
abstract class BothParent {
47+
public int $bothProp {
48+
get => 1;
49+
set => $value;
50+
}
51+
}
52+
53+
abstract class BothChild extends BothParent {
54+
abstract public int $bothProp { get; set; }
55+
}

0 commit comments

Comments
 (0)