Skip to content
Permalink
Browse files

Clear out property assignments when necessary

  • Loading branch information...
muglug committed Sep 7, 2019
1 parent e7a69f7 commit 85ae8f93d213ebd220d2a29306ccdfbb395a1c04
@@ -1302,6 +1302,10 @@ function (PhpParser\Node\Arg $arg) {
&& !$method_pure_compatible
) {
$context->removeAllObjectVars();
} elseif ($method_storage->this_property_mutations) {
foreach ($method_storage->this_property_mutations as $name => $_) {
$context->remove($lhs_var_id . '->' . $name);
}
}
}
@@ -465,6 +465,23 @@ public function enterNode(PhpParser\Node $node)
$var_type->queueClassLikesForScanning($this->codebase, $this->file_storage);
}
}
if ($node instanceof PhpParser\Node\Expr\Assign
|| $node instanceof PhpParser\Node\Expr\AssignOp
|| $node instanceof PhpParser\Node\Expr\AssignRef
) {
if ($node->var instanceof PhpParser\Node\Expr\PropertyFetch
&& $node->var->var instanceof PhpParser\Node\Expr\Variable
&& $node->var->var->name === 'this'
&& $node->var->name instanceof PhpParser\Node\Identifier
) {
$functionlike_storage = end($this->functionlike_storages);
if ($functionlike_storage instanceof MethodStorage) {
$functionlike_storage->this_property_mutations[$node->var->name->name] = true;
}
}
}
} elseif ($node instanceof PhpParser\Node\Stmt\Const_) {
foreach ($node->consts as $const) {
$const_type = StatementsAnalyzer::getSimpleType($this->codebase, $const->value, $this->aliases)
@@ -57,4 +57,9 @@ class MethodStorage extends FunctionLikeStorage
* @var bool
*/
public $external_mutation_free = false;
/**
* @var ?array<string, bool>
*/
public $this_property_mutations = null;
}
@@ -22,21 +22,28 @@ public function testForgetPropertyAssignments()
$this->addFile(
'somefile.php',
'<?php
class XCollector {
/** @var X[] */
private static array $xs = [];
public static function modify() : void {
foreach (self::$xs as $x) {
$x->x = null;
}
}
}
class X {
/** @var ?int **/
private $x;
public $x;
public function getX(): int {
$this->x = 5;
$this->modifyX();
XCollector::modify();
return $this->x;
}
private function modifyX(): void {
$this->x = null;
}
}'
);
@@ -46,31 +53,33 @@ private function modifyX(): void {
/**
* @return void
*/
public function testForgetPropertyAssignmentsInBranchWithThrow()
public function testForgetPropertyAssignmentsPassesNormally()
{
Config::getInstance()->remember_property_assignments_after_call = false;
$this->addFile(
'somefile.php',
'<?php
class XCollector {
/** @var X[] */
private static array $xs = [];
public static function modify() : void {
foreach (self::$xs as $x) {
$x->x = null;
}
}
}
class X {
/** @var ?int **/
private $x;
public $x;
public function getX(bool $b): int {
public function getX(): int {
$this->x = 5;
if ($b) {
$this->modifyX();
throw new \Exception("bad");
}
XCollector::modify();
return $this->x;
}
private function modifyX(): void {
$this->x = null;
}
}'
);
@@ -80,70 +89,81 @@ private function modifyX(): void {
/**
* @return void
*/
public function testRemovePropertyAfterReassignment()
public function testForgetPropertyAssignmentsInBranchWithThrow()
{
Config::getInstance()->remember_property_assignments_after_call = false;
$this->addFile(
'somefile.php',
'<?php
class A {
/** @var A|null */
public $parent;
class XCollector {
/** @var X[] */
private static array $xs = [];
public function __construct() {
$this->parent = rand(0, 1) ? new A : null;
public static function modify() : void {
foreach (self::$xs as $x) {
$x->x = null;
}
}
}
$a = new A();
if ($a->parent === null) {
throw new \Exception("bad");
}
class X {
/** @var ?int **/
public $x;
$a = $a->parent;'
);
public function getX(bool $b): int {
$this->x = 5;
$context = new Context();
if ($b) {
XCollector::modify();
throw new \Exception("bad");
}
$this->analyzeFile('somefile.php', $context);
return $this->x;
}
}'
);
$this->assertSame('A', (string) $context->vars_in_scope['$a']);
$this->assertFalse(isset($context->vars_in_scope['$a->parent']));
$this->analyzeFile('somefile.php', new Context());
}
/**
* @return void
*/
public function testRemoveClauseAfterReassignment()
public function testForgetPropertyAssignmentsInBranchWithThrowNormally()
{
Config::getInstance()->remember_property_assignments_after_call = false;
$this->addFile(
'somefile.php',
'<?php
class Test {
/** @var ?bool */
private $foo;
class XCollector {
/** @var X[] */
private static array $xs = [];
public function run(): void {
$this->foo = false;
$this->bar();
if ($this->foo === true) {}
public static function modify() : void {
foreach (self::$xs as $x) {
$x->x = null;
}
}
}
class X {
/** @var ?int **/
public $x;
private function bar(): void {
if (mt_rand(0, 1)) {
$this->foo = true;
public function getX(bool $b): int {
$this->x = 5;
if ($b) {
XCollector::modify();
throw new \Exception("bad");
}
return $this->x;
}
}'
);
$context = new Context();
$this->analyzeFile('somefile.php', $context);
$this->analyzeFile('somefile.php', new Context());
}
/**
@@ -1740,6 +1760,44 @@ public function bar(): void {
}
}',
],
'rememberThisPropertyAsssignmentsInMethod' => [
'<?php
class A {
public bool $foo = false;
public function bar() : void {
$this->foo = false;
$this->maybeChange();
if ($this->foo) {}
}
public function maybeChange() : void {
if (rand(0, 1)) {
$this->foo = true;
}
}
}'
],
'testRemoveClauseAfterReassignment' => [
'<?php
class Test {
/** @var ?bool */
private $foo;
public function run(): void {
$this->foo = false;
$this->bar();
if ($this->foo === true) {}
}
private function bar(): void {
if (mt_rand(0, 1)) {
$this->foo = true;
}
}
}',
],
];
}

0 comments on commit 85ae8f9

Please sign in to comment.
You can’t perform that action at this time.