Skip to content

Commit 3a695a7

Browse files
committed
#4 detect immutable parent properties
1 parent 35cf80c commit 3a695a7

File tree

5 files changed

+46
-8
lines changed

5 files changed

+46
-8
lines changed

src/Helper/AnnotationParser.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public static function classHasAnnotation(array $annotations, array $nodes): boo
2222
return false;
2323
}
2424

25-
return self::isWhitelisted($classNode, $annotations);
25+
return self::hasNodeImmutableAnnotation($classNode, $annotations);
2626
}
2727

2828
/**
@@ -42,7 +42,7 @@ public static function propertiesWithWhitelistedAnnotations(array $annotations,
4242

4343
$classProperties = NodeParser::getClassProperties($classNode);
4444
foreach ($classProperties as $property) {
45-
$whitelisted = self::isWhitelisted($property, $annotations);
45+
$whitelisted = self::hasNodeImmutableAnnotation($property, $annotations);
4646
if ($whitelisted) {
4747
foreach ($property->props as $prop) {
4848
$whitelistedProperties[] = (string)$prop->name;
@@ -79,7 +79,7 @@ public static function getAnnotations(Node $node): array
7979
*
8080
* @return bool
8181
*/
82-
private static function isWhitelisted(Node $node, array $whitelistAnnotations): bool
82+
public static function hasNodeImmutableAnnotation(Node $node, array $whitelistAnnotations): bool
8383
{
8484
$nodeAnnotations = self::getAnnotations($node);
8585

src/Rules/ImmutableObjectRule.php

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ public function processNode(Node $node, Scope $scope): array
5959
return [];
6060
}
6161

62+
if ($scope->getFunctionName() === '__construct') {
63+
return [];
64+
}
65+
6266
$this->detectImmutableProperties($scope);
6367

6468
$isImmutable = $this->isImmutable;
@@ -89,10 +93,6 @@ public function processNode(Node $node, Scope $scope): array
8993
return [];
9094
}
9195

92-
if ($scope->getFunctionName() === '__construct') {
93-
return [];
94-
}
95-
9696
$propertyName = $node->var->name;
9797
if ($propertyName instanceof Node\Identifier) {
9898
$propertyName = (string)$propertyName;
@@ -154,9 +154,16 @@ private function getInheritedImmutableProperties(Scope $scope): array
154154
$hasImmutableParent = true;
155155

156156
$immutableParentProperties += Converter::propertyStringNames(NodeParser::getNonPrivateProperties($classNode));
157+
158+
continue;
157159
}
158160

159-
// @TODO: detect non private parent properties annotated as immutable (instead of whole class)
161+
$nonPrivateParentProperties = NodeParser::getNonPrivateProperties($classNode);
162+
foreach ($nonPrivateParentProperties as $property) {
163+
if (AnnotationParser::hasNodeImmutableAnnotation($property, self::WHITELISTED_ANNOTATIONS)) {
164+
$immutableParentProperties[] = Converter::propertyToString($property);
165+
}
166+
}
160167
}
161168

162169
return ['properties' => $immutableParentProperties, 'hasImmutableParent' => $hasImmutableParent];
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Svnldwg\PHPStan\Test\Fixture\ImmutableObjectRule\Failure\Inheritance\ImmutableParentProperty;
6+
7+
class ChildClass extends ParentClass
8+
{
9+
public function set(): void
10+
{
11+
$this->foo = 10; // declared immutable in parent class
12+
}
13+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Svnldwg\PHPStan\Test\Fixture\ImmutableObjectRule\Failure\Inheritance\ImmutableParentProperty;
6+
7+
class ParentClass
8+
{
9+
/** @psalm-immutable */
10+
protected $foo;
11+
}

test/Integration/Rules/ImmutableObjectRuleTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,13 @@ public function provideCasesWhereAnalysisShouldFail(): iterable
109109
9,
110110
],
111111
],
112+
'immutable-parent-property' => [
113+
__DIR__ . '/../../Fixture/ImmutableObjectRule/Failure/Inheritance/ImmutableParentProperty/ChildClass.php',
114+
[
115+
'Property is declared immutable, but class property "foo" is modified in method "set"',
116+
11,
117+
],
118+
],
112119
];
113120

114121
foreach ($paths as $description => [$path, $error]) {

0 commit comments

Comments
 (0)