Skip to content
Permalink
Browse files

Improve handling of external-mutation-free objects created in pure fu…

…nctions
  • Loading branch information...
muglug committed Aug 31, 2019
1 parent f096c3d commit 900cfc0f0591fc637ee2d0f6867c2eee9ae56433
@@ -1206,7 +1206,11 @@ function (PhpParser\Node\Arg $arg) {
if ($method_storage) {
if (!$context->collect_mutations && !$context->collect_initializations) {
if ($context->pure && !$method_storage->pure) {
$method_pure_compatible = $method_storage->external_mutation_free
&& (!empty($stmt->var->inferredType->external_mutation_free)
|| isset($stmt->var->pure));
if ($context->pure && !$method_storage->pure && !$method_pure_compatible) {
if (IssueBuffer::accepts(
new ImpureMethodCall(
'Cannot call an impure method ' . $method_id . ' from a pure context',
@@ -1216,7 +1220,10 @@ function (PhpParser\Node\Arg $arg) {
)) {
// fall through
}
} elseif ($context->mutation_free && !$method_storage->mutation_free) {
} elseif ($context->mutation_free
&& !$method_storage->mutation_free
&& !$method_pure_compatible
) {
if (IssueBuffer::accepts(
new ImpureMethodCall(
'Cannot call an possibly-mutating method '
@@ -1230,6 +1237,7 @@ function (PhpParser\Node\Arg $arg) {
} elseif ($context->external_mutation_free
&& !$method_storage->mutation_free
&& $fq_class_name !== $context->self
&& !$method_pure_compatible
) {
if (IssueBuffer::accepts(
new ImpureMethodCall(
@@ -1242,8 +1250,7 @@ function (PhpParser\Node\Arg $arg) {
// fall through
}
} elseif (($method_storage->mutation_free
|| ($method_storage->external_mutation_free
&& (isset($stmt->var->external_mutation_free) || isset($stmt->var->pure))))
|| $method_pure_compatible)
&& $codebase->find_unused_variables
&& !$context->inside_conditional
&& !$context->inside_unset
@@ -306,11 +306,6 @@ public static function analyze(
) {
$storage = $codebase->classlike_storage_provider->get($fq_class_name);
if ($storage->external_mutation_free) {
/** @psalm-suppress UndefinedPropertyAssignment */
$stmt->external_mutation_free = true;
}
// if we're not calling this constructor via new static()
if ($storage->abstract && !$can_extend) {
if (IssueBuffer::accepts(
@@ -478,6 +473,10 @@ public static function analyze(
// fall through
}
}
if ($storage->external_mutation_free) {
$stmt->inferredType->external_mutation_free = true;
}
}
}
@@ -46,7 +46,7 @@ private function checkNonTrivialExpr(PhpParser\Node\Expr $node)
return false;
}
if ($node instanceof PhpParser\Node\Expr\New_ && isset($node->external_mutation_free)) {
if ($node instanceof PhpParser\Node\Expr\New_ && !empty($node->inferredType->external_mutation_free)) {
return false;
}
@@ -1463,6 +1463,10 @@ public static function combineUnionTypes(
$combined_type->had_template = true;
}
if ($type_1->external_mutation_free && $type_2->external_mutation_free) {
$combined_type->external_mutation_free = true;
}
if ($both_failed_reconciliation) {
$combined_type->failed_reconciliation = true;
}
@@ -154,6 +154,11 @@ class Union
*/
public $by_ref = false;
/**
* @var bool
*/
public $external_mutation_free = false;
/** @var null|string */
private $id;
@@ -77,6 +77,28 @@ function setDefaultOptions(array $defaultOptions): void {
}
}',
],
'canCreateObjectWithNoExternalMutations' => [
'<?php
/** @psalm-external-mutation-free */
class Counter {
private int $count = 0;
public function __construct(int $count) {
$this->count = $count;
}
public function increment() : void {
$this->count++;
}
}
/** @psalm-pure */
function makesACounter(int $i) : Counter {
$c = new Counter($i);
$c->increment();
return $c;
}',
],
];
}
@@ -175,6 +197,28 @@ function filterOdd(int $i, A $a) : ?int {
}',
'error_message' => 'ImpureMethodCall',
],
'canCreateObjectWithNoExternalMutations' => [
'<?php
class Counter {
private int $count = 0;
public function __construct(int $count) {
$this->count = $count;
}
public function increment() : void {
$this->count += rand(0, 5);
}
}
/** @psalm-pure */
function makesACounter(int $i) : Counter {
$c = new Counter($i);
$c->increment();
return $c;
}',
'error_message' => 'ImpureMethodCall',
],
];
}
}

0 comments on commit 900cfc0

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