Skip to content

Commit be3c27a

Browse files
committed
Fix NewStaticRule when there's @phpstan-consistent-constructor but constructor implementation is in a parent
1 parent 4274cfb commit be3c27a

File tree

3 files changed

+67
-8
lines changed

3 files changed

+67
-8
lines changed

src/Rules/Classes/NewStaticRule.php

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
use PHPStan\Analyser\Scope;
77
use PHPStan\DependencyInjection\RegisteredRule;
88
use PHPStan\Php\PhpVersion;
9+
use PHPStan\Reflection\ClassReflection;
10+
use PHPStan\Reflection\Dummy\DummyConstructorReflection;
11+
use PHPStan\Reflection\ExtendedMethodReflection;
912
use PHPStan\Reflection\Php\PhpMethodReflection;
1013
use PHPStan\Rules\Rule;
1114
use PHPStan\Rules\RuleErrorBuilder;
@@ -55,11 +58,10 @@ public function processNode(Node $node, Scope $scope): array
5558
->tip('See: https://phpstan.org/blog/solving-phpstan-error-unsafe-usage-of-new-static')
5659
->build(),
5760
];
61+
if ($classReflection->hasConsistentConstructor()) {
62+
return [];
63+
}
5864
if (!$classReflection->hasConstructor()) {
59-
if ($classReflection->hasConsistentConstructor()) {
60-
return [];
61-
}
62-
6365
return $messages;
6466
}
6567

@@ -68,10 +70,6 @@ public function processNode(Node $node, Scope $scope): array
6870
return [];
6971
}
7072

71-
if ($constructor->getDeclaringClass()->hasConsistentConstructor()) {
72-
return [];
73-
}
74-
7573
foreach ($classReflection->getImmediateInterfaces() as $interface) {
7674
if ($interface->hasConstructor()) {
7775
return [];
@@ -82,6 +80,14 @@ public function processNode(Node $node, Scope $scope): array
8280
return [];
8381
}
8482

83+
$parent = $classReflection->getParentClass();
84+
if ($parent !== null) {
85+
$parentConstructor = $this->findConsistentParentConstructor($parent);
86+
if ($parentConstructor !== null) {
87+
return [];
88+
}
89+
}
90+
8591
if ($constructor instanceof PhpMethodReflection) {
8692
$prototype = $constructor->getPrototype();
8793
if ($prototype->isAbstract()) {
@@ -105,4 +111,22 @@ public function processNode(Node $node, Scope $scope): array
105111
return $messages;
106112
}
107113

114+
private function findConsistentParentConstructor(ClassReflection $classReflection): ?ExtendedMethodReflection
115+
{
116+
if ($classReflection->hasConsistentConstructor()) {
117+
if ($classReflection->hasConstructor()) {
118+
return $classReflection->getConstructor();
119+
}
120+
121+
return new DummyConstructorReflection($classReflection);
122+
}
123+
124+
$parent = $classReflection->getParentClass();
125+
if ($parent === null) {
126+
return null;
127+
}
128+
129+
return $this->findConsistentParentConstructor($parent);
130+
}
131+
108132
}

tests/PHPStan/Rules/Classes/NewStaticRuleTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,9 @@ public function testBug11316(): void
6767
$this->analyse([__DIR__ . '/data/bug-11316.php'], []);
6868
}
6969

70+
public function testBug10722(): void
71+
{
72+
$this->analyse([__DIR__ . '/data/bug-10722.php'], []);
73+
}
74+
7075
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace Bug10722;
4+
5+
class BaseClass {
6+
public function __construct(protected string $value) {
7+
}
8+
}
9+
10+
/**
11+
* @phpstan-consistent-constructor
12+
*/
13+
class ChildClass extends BaseClass {
14+
public function fromString(string $value): static {
15+
return new static($value);
16+
}
17+
}
18+
19+
/**
20+
* @phpstan-consistent-constructor
21+
*/
22+
class ChildClass2 extends BaseClass {
23+
24+
}
25+
26+
class ChildClass3 extends ChildClass2 {
27+
public function fromString(string $value): static {
28+
return new static($value);
29+
}
30+
}

0 commit comments

Comments
 (0)