Skip to content

Commit 93d85c4

Browse files
authored
Make assigning property via reference impure
1 parent 43d3ac4 commit 93d85c4

File tree

4 files changed

+91
-2
lines changed

4 files changed

+91
-2
lines changed

src/Analyser/ImpurePoint.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use PHPStan\Node\VirtualNode;
77

88
/**
9-
* @phpstan-type ImpurePointIdentifier = 'echo'|'die'|'exit'|'propertyAssign'|'propertyUnset'|'methodCall'|'new'|'functionCall'|'include'|'require'|'print'|'eval'|'superglobal'|'yield'|'yieldFrom'|'static'|'global'|'betweenPhpTags'
9+
* @phpstan-type ImpurePointIdentifier = 'echo'|'die'|'exit'|'propertyAssign'|'propertyAssignByRef'|'propertyUnset'|'methodCall'|'new'|'functionCall'|'include'|'require'|'print'|'eval'|'superglobal'|'yield'|'yieldFrom'|'static'|'global'|'betweenPhpTags'
1010
* @api
1111
*/
1212
class ImpurePoint

src/Analyser/NodeScopeResolver.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2138,7 +2138,23 @@ public function processExprNode(Node\Stmt $stmt, Expr $expr, MutatingScope $scop
21382138
$nodeCallback,
21392139
$context,
21402140
function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context): ExpressionResult {
2141+
$impurePoints = [];
21412142
if ($expr instanceof AssignRef) {
2143+
$referencedExpr = $expr->expr;
2144+
while ($referencedExpr instanceof ArrayDimFetch) {
2145+
$referencedExpr = $referencedExpr->var;
2146+
}
2147+
2148+
if ($referencedExpr instanceof PropertyFetch || $referencedExpr instanceof StaticPropertyFetch) {
2149+
$impurePoints[] = new ImpurePoint(
2150+
$scope,
2151+
$expr,
2152+
'propertyAssignByRef',
2153+
'property assignment by reference',
2154+
false,
2155+
);
2156+
}
2157+
21422158
$scope = $scope->enterExpressionAssign($expr->expr);
21432159
}
21442160

@@ -2153,7 +2169,7 @@ function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context): Exp
21532169
$result = $this->processExprNode($stmt, $expr->expr, $scope, $nodeCallback, $context->enterDeep());
21542170
$hasYield = $result->hasYield();
21552171
$throwPoints = $result->getThrowPoints();
2156-
$impurePoints = $result->getImpurePoints();
2172+
$impurePoints = array_merge($impurePoints, $result->getImpurePoints());
21572173
$scope = $result->getScope();
21582174

21592175
if ($expr instanceof AssignRef) {

tests/PHPStan/Rules/Pure/PureMethodRuleTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,4 +157,14 @@ public function testPureConstructor(): void
157157
]);
158158
}
159159

160+
public function testImpureAssignRef(): void
161+
{
162+
$this->analyse([__DIR__ . '/data/impure-assign-ref.php'], [
163+
[
164+
'Possibly impure property assignment by reference in pure method ImpureAssignRef\HelloWorld::bar6().',
165+
49,
166+
],
167+
]);
168+
}
169+
160170
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php declare(strict_types = 1); // lint >= 7.4
2+
3+
namespace ImpureAssignRef;
4+
5+
class HelloWorld
6+
{
7+
public int $value = 0;
8+
/** @var array<int, int> */
9+
public array $arr = [];
10+
/** @var array<int, \stdClass> */
11+
public array $objectArr = [];
12+
public static int $staticValue = 0;
13+
/** @var array<int, int> */
14+
public static array $staticArr = [];
15+
16+
private function bar1(): void
17+
{
18+
$value = &$this->value;
19+
$value = 1;
20+
}
21+
22+
private function bar2(): void
23+
{
24+
$value = &$this->arr[0];
25+
$value = 1;
26+
}
27+
28+
private function bar3(): void
29+
{
30+
$value = &self::$staticValue;
31+
$value = 1;
32+
}
33+
34+
private function bar4(): void
35+
{
36+
$value = &self::$staticArr[0];
37+
$value = 1;
38+
}
39+
40+
private function bar5(self $other): void
41+
{
42+
$value = &$other->value;
43+
$value = 1;
44+
}
45+
46+
/** @phpstan-pure */
47+
private function bar6(): int
48+
{
49+
$value = &$this->objectArr[0]->foo;
50+
51+
return 1;
52+
}
53+
54+
public function foo(): void
55+
{
56+
$this->bar1();
57+
$this->bar2();
58+
$this->bar3();
59+
$this->bar4();
60+
$this->bar5(new self());
61+
$this->bar6();
62+
}
63+
}

0 commit comments

Comments
 (0)