Skip to content

Commit 3054f44

Browse files
committed
Fix phpstan/phpstan#14352: "Cannot re-assign $this." false-positive
- Added `isDimFetchAssign` property to VariableAssignNode to distinguish direct variable assignments from array dimension fetch assignments - Set the flag to true in AssignHandler when processing ArrayDimFetch chains - Updated InvalidVariableAssignRule to skip $this check for dim-fetch assigns - New regression test in tests/PHPStan/Rules/Variables/data/bug-14352.php
1 parent bebb16f commit 3054f44

5 files changed

Lines changed: 26 additions & 4 deletions

File tree

src/Analyser/ExprHandler/AssignHandler.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ public function processAssignVar(
463463

464464
if ($varType->isArray()->yes() || !(new ObjectType(ArrayAccess::class))->isSuperTypeOf($varType)->yes()) {
465465
if ($var instanceof Variable && is_string($var->name)) {
466-
$nodeScopeResolver->callNodeCallback($nodeCallback, new VariableAssignNode($var, new TypeExpr($valueToWrite)), $scopeBeforeAssignEval, $storage);
466+
$nodeScopeResolver->callNodeCallback($nodeCallback, new VariableAssignNode($var, new TypeExpr($valueToWrite), true), $scopeBeforeAssignEval, $storage);
467467
$scope = $scope->assignVariable($var->name, $valueToWrite, $nativeValueToWrite, TrinaryLogic::createYes());
468468
} else {
469469
if ($var instanceof PropertyFetch || $var instanceof StaticPropertyFetch) {
@@ -480,7 +480,7 @@ public function processAssignVar(
480480
}
481481
} else {
482482
if ($var instanceof Variable) {
483-
$nodeScopeResolver->callNodeCallback($nodeCallback, new VariableAssignNode($var, $assignedPropertyExpr), $scopeBeforeAssignEval, $storage);
483+
$nodeScopeResolver->callNodeCallback($nodeCallback, new VariableAssignNode($var, $assignedPropertyExpr, true), $scopeBeforeAssignEval, $storage);
484484
} elseif ($var instanceof PropertyFetch || $var instanceof StaticPropertyFetch) {
485485
$nodeScopeResolver->callNodeCallback($nodeCallback, new PropertyAssignNode($var, $assignedPropertyExpr, $isAssignOp), $scopeBeforeAssignEval, $storage);
486486
if ($var instanceof PropertyFetch && $var->name instanceof Node\Identifier && !$isAssignOp) {
@@ -799,7 +799,7 @@ public function processAssignVar(
799799
}
800800

801801
if ($var instanceof Variable && is_string($var->name)) {
802-
$nodeScopeResolver->callNodeCallback($nodeCallback, new VariableAssignNode($var, $assignedPropertyExpr), $scope, $storage);
802+
$nodeScopeResolver->callNodeCallback($nodeCallback, new VariableAssignNode($var, $assignedPropertyExpr, true), $scope, $storage);
803803
$scope = $scope->assignVariable($var->name, $valueToWrite, $nativeValueToWrite, TrinaryLogic::createYes());
804804
} else {
805805
if ($var instanceof PropertyFetch || $var instanceof StaticPropertyFetch) {

src/Node/VariableAssignNode.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ final class VariableAssignNode extends NodeAbstract implements VirtualNode
1212
public function __construct(
1313
private Expr\Variable $variable,
1414
private Expr $assignedExpr,
15+
private bool $isDimFetchAssign = false,
1516
)
1617
{
1718
parent::__construct($variable->getAttributes());
@@ -27,6 +28,11 @@ public function getAssignedExpr(): Expr
2728
return $this->assignedExpr;
2829
}
2930

31+
public function isDimFetchAssign(): bool
32+
{
33+
return $this->isDimFetchAssign;
34+
}
35+
3036
#[Override]
3137
public function getType(): string
3238
{

src/Rules/Variables/InvalidVariableAssignRule.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public function processNode(Node $node, Scope $scope): array
2929
return [];
3030
}
3131

32-
if ($variable->name === 'this') {
32+
if ($variable->name === 'this' && !$node->isDimFetchAssign()) {
3333
return [
3434
RuleErrorBuilder::message('Cannot re-assign $this.')
3535
->identifier('assign.this')

tests/PHPStan/Rules/Variables/InvalidVariableAssignRuleTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ public function testBug3585(): void
5050
]);
5151
}
5252

53+
public function testBug14352(): void
54+
{
55+
$this->analyse([__DIR__ . '/data/bug-14352.php'], []);
56+
}
57+
5358
public function testBug14349(): void
5459
{
5560
$this->analyse([__DIR__ . '/data/bug-14349.php'], [
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug14352;
4+
5+
class Test
6+
{
7+
public function doFoo(string $key, string $value): void
8+
{
9+
$this[$key] = $value;
10+
}
11+
}

0 commit comments

Comments
 (0)