1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-26 20:34:47 +01:00

Make clause fully immutable

This commit is contained in:
Brown 2020-08-26 15:35:29 -04:00 committed by Daniil Gentili
parent b09309aa92
commit 6d43a6696c
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
18 changed files with 185 additions and 104 deletions

View File

@ -158,7 +158,7 @@ class Context
/**
* A list of hashed clauses that have already been factored in
*
* @var list<string>
* @var list<string|int>
*/
public $reconciled_expression_clauses = [];

View File

@ -58,6 +58,8 @@ class AlgebraAnalyzer
$hash = $formula2_clause->hash;
if (!$formula2_clause->generated
&& !$formula2_clause->wedge
&& $formula2_clause->reconcilable
&& (isset($formula1_hashes[$hash]) || isset($formula2_hashes[$hash]))
&& !array_intersect_key($new_assigned_var_ids, $formula2_clause->possibilities)
) {

View File

@ -52,8 +52,11 @@ class DoAnalyzer
}
}
$cond_id = \spl_object_id($stmt->cond);
$while_clauses = Algebra::getFormula(
\spl_object_id($stmt->cond),
$cond_id,
$cond_id,
$stmt->cond,
$context->self,
$statements_analyzer,
@ -83,7 +86,7 @@ class DoAnalyzer
);
if (!$while_clauses) {
$while_clauses = [new Clause([], true)];
$while_clauses = [new Clause([], $cond_id, $cond_id, true)];
}
LoopAnalyzer::analyze(

View File

@ -107,8 +107,11 @@ class IfAnalyzer
}
}
$cond_object_id = \spl_object_id($stmt->cond);
$if_clauses = Algebra::getFormula(
\spl_object_id($stmt->cond),
$cond_object_id,
$cond_object_id,
$stmt->cond,
$context->self,
$statements_analyzer,
@ -124,7 +127,7 @@ class IfAnalyzer
/**
* @return Clause
*/
function (Clause $c) use ($mixed_var_ids) {
function (Clause $c) use ($mixed_var_ids, $cond_object_id) {
$keys = array_keys($c->possibilities);
$mixed_var_ids = \array_diff($mixed_var_ids, $keys);
@ -132,7 +135,7 @@ class IfAnalyzer
foreach ($keys as $key) {
foreach ($mixed_var_ids as $mixed_var_id) {
if (preg_match('/^' . preg_quote($mixed_var_id, '/') . '(\[|-)/', $key)) {
return new Clause([], true);
return new Clause([], $cond_object_id, $cond_object_id, true);
}
}
}
@ -1086,8 +1089,11 @@ class IfAnalyzer
}
}
$elseif_cond_id = \spl_object_id($elseif->cond);
$elseif_clauses = Algebra::getFormula(
\spl_object_id($elseif->cond),
$elseif_cond_id,
$elseif_cond_id,
$elseif->cond,
$else_context->self,
$statements_analyzer,
@ -1098,7 +1104,7 @@ class IfAnalyzer
/**
* @return Clause
*/
function (Clause $c) use ($mixed_var_ids) {
function (Clause $c) use ($mixed_var_ids, $elseif_cond_id) {
$keys = array_keys($c->possibilities);
$mixed_var_ids = \array_diff($mixed_var_ids, $keys);
@ -1106,7 +1112,7 @@ class IfAnalyzer
foreach ($keys as $key) {
foreach ($mixed_var_ids as $mixed_var_id) {
if (preg_match('/^' . preg_quote($mixed_var_id, '/') . '(\[|-)/', $key)) {
return new Clause([], true);
return new Clause([], $elseif_cond_id, $elseif_cond_id, true);
}
}
}
@ -1120,13 +1126,13 @@ class IfAnalyzer
/**
* @return Clause
*/
function (Clause $c) use ($cond_assigned_var_ids) {
function (Clause $c) use ($cond_assigned_var_ids, $elseif_cond_id) {
$keys = array_keys($c->possibilities);
foreach ($keys as $key) {
foreach ($cond_assigned_var_ids as $conditional_assigned_var_id => $_) {
if (preg_match('/^' . preg_quote($conditional_assigned_var_id, '/') . '(\[|-|$)/', $key)) {
return new Clause([], true);
return new Clause([], $elseif_cond_id, $elseif_cond_id, true);
}
}
}
@ -1403,7 +1409,8 @@ class IfAnalyzer
if ($reasonable_clause_count && $reasonable_clause_count < 20000 && $elseif_clauses) {
$if_scope->reasonable_clauses = Algebra::combineOredClauses(
$if_scope->reasonable_clauses,
$elseif_clauses
$elseif_clauses,
\spl_object_id($elseif->cond)
);
} else {
$if_scope->reasonable_clauses = [];

View File

@ -70,8 +70,11 @@ class LoopAnalyzer
if ($pre_conditions) {
foreach ($pre_conditions as $i => $pre_condition) {
$pre_condition_id = \spl_object_id($pre_condition);
$pre_condition_clauses[$i] = Algebra::getFormula(
\spl_object_id($pre_condition),
$pre_condition_id,
$pre_condition_id,
$pre_condition,
$loop_scope->loop_context->self,
$statements_analyzer,

View File

@ -283,8 +283,10 @@ class SwitchCaseAnalyzer
$case_clauses = [];
if ($case_equality_expr) {
$case_equality_expr_id = \spl_object_id($case_equality_expr);
$case_clauses = Algebra::getFormula(
\spl_object_id($case_equality_expr),
$case_equality_expr_id,
$case_equality_expr_id,
$case_equality_expr,
$context->self,
$statements_analyzer,
@ -379,9 +381,12 @@ class SwitchCaseAnalyzer
try {
$negated_case_clauses = Algebra::negateFormula($case_clauses);
} catch (\Psalm\Exception\ComplicatedExpressionException $e) {
$case_equality_expr_id = \spl_object_id($case_equality_expr);
try {
$negated_case_clauses = Algebra::getFormula(
\spl_object_id($case_equality_expr),
$case_equality_expr_id,
$case_equality_expr_id,
new PhpParser\Node\Expr\BooleanNot($case_equality_expr),
$context->self,
$statements_analyzer,

View File

@ -60,8 +60,11 @@ class AndAnalyzer
$codebase = $statements_analyzer->getCodebase();
$left_cond_id = \spl_object_id($stmt->left);
$left_clauses = Algebra::getFormula(
\spl_object_id($stmt->left),
$left_cond_id,
$left_cond_id,
$stmt->left,
$context->self,
$statements_analyzer,
@ -110,7 +113,7 @@ class AndAnalyzer
$left_type_assertions = Algebra::getTruthsFromFormula(
$simplified_clauses,
\spl_object_id($stmt->left),
$left_cond_id,
$left_referenced_var_ids,
$active_left_assertions
);

View File

@ -34,8 +34,11 @@ class CoalesceAnalyzer
$codebase = $statements_analyzer->getCodebase();
$stmt_id = \spl_object_id($stmt);
$if_clauses = Algebra::getFormula(
\spl_object_id($stmt),
$stmt_id,
$stmt_id,
$stmt,
$context->self,
$statements_analyzer,
@ -61,7 +64,7 @@ class CoalesceAnalyzer
/**
* @return \Psalm\Internal\Clause
*/
function (\Psalm\Internal\Clause $c) use ($mixed_var_ids) {
function (\Psalm\Internal\Clause $c) use ($mixed_var_ids, $stmt_id) {
$keys = array_keys($c->possibilities);
$mixed_var_ids = \array_diff($mixed_var_ids, $keys);
@ -69,7 +72,7 @@ class CoalesceAnalyzer
foreach ($keys as $key) {
foreach ($mixed_var_ids as $mixed_var_id) {
if (preg_match('/^' . preg_quote($mixed_var_id, '/') . '(\[|-)/', $key)) {
return new \Psalm\Internal\Clause([], true);
return new \Psalm\Internal\Clause([], $stmt_id, $stmt_id, true);
}
}
}

View File

@ -111,8 +111,11 @@ class OrAnalyzer
$left_referenced_var_ids = array_diff_key($left_referenced_var_ids, $left_assigned_var_ids);
}
$left_cond_id = \spl_object_id($stmt->left);
$left_clauses = Algebra::getFormula(
\spl_object_id($stmt->left),
$left_cond_id,
$left_cond_id,
$stmt->left,
$context->self,
$statements_analyzer,
@ -124,7 +127,8 @@ class OrAnalyzer
} catch (\Psalm\Exception\ComplicatedExpressionException $e) {
try {
$negated_left_clauses = Algebra::getFormula(
\spl_object_id($stmt->left),
$left_cond_id,
$left_cond_id,
new PhpParser\Node\Expr\BooleanNot($stmt->left),
$context->self,
$statements_analyzer,
@ -167,7 +171,7 @@ class OrAnalyzer
$negated_type_assertions = Algebra::getTruthsFromFormula(
$clauses_for_right_analysis,
\spl_object_id($stmt->left),
$left_cond_id,
$left_referenced_var_ids,
$active_negated_type_assertions
);

View File

@ -805,8 +805,11 @@ class FunctionCallAnalyzer extends CallAnalyzer
PhpParser\Node\Arg $first_arg,
Context $context
) : void {
$first_arg_value_id = \spl_object_id($first_arg->value);
$assert_clauses = \Psalm\Type\Algebra::getFormula(
\spl_object_id($first_arg->value),
$first_arg_value_id,
$first_arg_value_id,
$first_arg->value,
$context->self,
$statements_analyzer,

View File

@ -703,7 +703,8 @@ class CallAnalyzer
);
$assert_clauses = \Psalm\Type\Algebra::getFormula(
\spl_object_id($conditional),
\mt_rand(0, 1000000),
\mt_rand(0, 1000000),
$conditional,
$context->self,
$statements_analyzer,
@ -711,6 +712,7 @@ class CallAnalyzer
);
} else {
$assert_clauses = \Psalm\Type\Algebra::getFormula(
\spl_object_id($arg_value),
\spl_object_id($arg_value),
$arg_value,
$context->self,
@ -731,6 +733,7 @@ class CallAnalyzer
} elseif ($arg_value && $assertion->rule === [['falsy']]) {
$assert_clauses = \Psalm\Type\Algebra::negateFormula(
\Psalm\Type\Algebra::getFormula(
\spl_object_id($arg_value),
\spl_object_id($arg_value),
$arg_value,
$context->self,

View File

@ -53,8 +53,11 @@ class TernaryAnalyzer
$codebase = $statements_analyzer->getCodebase();
$cond_id = \spl_object_id($stmt->cond);
$if_clauses = \Psalm\Type\Algebra::getFormula(
\spl_object_id($stmt->cond),
$cond_id,
$cond_id,
$stmt->cond,
$context->self,
$statements_analyzer,
@ -80,7 +83,7 @@ class TernaryAnalyzer
/**
* @return \Psalm\Internal\Clause
*/
function (\Psalm\Internal\Clause $c) use ($mixed_var_ids) {
function (\Psalm\Internal\Clause $c) use ($mixed_var_ids, $cond_id) {
$keys = array_keys($c->possibilities);
$mixed_var_ids = \array_diff($mixed_var_ids, $keys);
@ -88,7 +91,7 @@ class TernaryAnalyzer
foreach ($keys as $key) {
foreach ($mixed_var_ids as $mixed_var_id) {
if (preg_match('/^' . preg_quote($mixed_var_id, '/') . '(\[|-)/', $key)) {
return new \Psalm\Internal\Clause([], true);
return new \Psalm\Internal\Clause([], $cond_id, $cond_id, true);
}
}
}
@ -128,7 +131,7 @@ class TernaryAnalyzer
$reconcilable_if_types = Algebra::getTruthsFromFormula(
$ternary_clauses,
\spl_object_id($stmt->cond),
$cond_id,
$cond_referenced_var_ids,
$active_if_types
);

View File

@ -11,7 +11,6 @@ use function json_encode;
use function ksort;
use function md5;
use function sort;
use function mt_rand;
use function array_unique;
use function strpos;
@ -22,7 +21,10 @@ use function strpos;
*/
class Clause
{
/** @var ?int */
/** @var int */
public $creating_conditional_id;
/** @var int */
public $creating_object_id;
/**
@ -70,7 +72,7 @@ class Clause
/** @var array<string, bool> */
public $redefined_vars = [];
/** @var string */
/** @var string|int */
public $hash;
/**
@ -82,27 +84,28 @@ class Clause
*/
public function __construct(
array $possibilities,
int $creating_conditional_id,
int $creating_object_id,
$wedge = false,
$reconcilable = true,
$generated = false,
array $redefined_vars = [],
?int $creating_object_id = null
array $redefined_vars = []
) {
$this->possibilities = $possibilities;
$this->wedge = $wedge;
$this->reconcilable = $reconcilable;
$this->generated = $generated;
$this->redefined_vars = $redefined_vars;
$this->creating_conditional_id = $creating_conditional_id;
$this->creating_object_id = $creating_object_id;
if ($wedge || !$reconcilable) {
/** @psalm-suppress ImpureFunctionCall as this has to be globally unique */
$this->hash = (string) mt_rand(0, 1000000);
$this->hash = ($wedge ? 'w' : '') . $creating_object_id;
} else {
ksort($possibilities);
foreach ($possibilities as &$possible_types) {
sort($possible_types);
foreach ($possibilities as $i => $_) {
sort($possibilities[$i]);
}
$this->hash = md5((string) json_encode($possibilities));
@ -180,11 +183,12 @@ class Clause
return new self(
$possibilities,
$this->creating_conditional_id,
$this->creating_object_id,
$this->wedge,
$this->reconcilable,
$this->generated,
$this->redefined_vars,
$this->creating_object_id
$this->redefined_vars
);
}
@ -195,11 +199,12 @@ class Clause
return new self(
$possibilities,
$this->creating_conditional_id,
$this->creating_object_id,
$this->wedge,
$this->reconcilable,
$this->generated,
$this->redefined_vars,
$this->creating_object_id
$this->redefined_vars
);
}
@ -213,11 +218,12 @@ class Clause
return new self(
$possibilities,
$this->creating_conditional_id,
$this->creating_object_id,
$this->wedge,
$this->reconcilable,
$this->generated,
$this->redefined_vars,
$this->creating_object_id
$this->redefined_vars
);
}

View File

@ -2164,8 +2164,11 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements PhpParse
break;
}
$cond_id = \spl_object_id($function_stmt->cond);
$if_clauses = \Psalm\Type\Algebra::getFormula(
\spl_object_id($function_stmt->cond),
$cond_id,
$cond_id,
$function_stmt->cond,
$this->fq_classlike_names
? $this->fq_classlike_names[count($this->fq_classlike_names) - 1]

View File

@ -91,7 +91,8 @@ class Algebra
* @return array<int, Clause>
*/
public static function getFormula(
int $object_id,
int $conditional_object_id,
int $creating_object_id,
PhpParser\Node\Expr $conditional,
$this_class_name,
FileSource $source,
@ -103,7 +104,8 @@ class Algebra
$conditional instanceof PhpParser\Node\Expr\BinaryOp\LogicalAnd
) {
$left_assertions = self::getFormula(
$object_id,
$conditional_object_id,
\spl_object_id($conditional->left),
$conditional->left,
$this_class_name,
$source,
@ -113,7 +115,8 @@ class Algebra
);
$right_assertions = self::getFormula(
$object_id,
$conditional_object_id,
\spl_object_id($conditional->right),
$conditional->right,
$this_class_name,
$source,
@ -131,10 +134,9 @@ class Algebra
if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr ||
$conditional instanceof PhpParser\Node\Expr\BinaryOp\LogicalOr
) {
// at the moment we only support formulae in CNF
$left_clauses = self::getFormula(
$object_id,
$conditional_object_id,
\spl_object_id($conditional->left),
$conditional->left,
$this_class_name,
$source,
@ -144,7 +146,8 @@ class Algebra
);
$right_clauses = self::getFormula(
$object_id,
$conditional_object_id,
\spl_object_id($conditional->right),
$conditional->right,
$this_class_name,
$source,
@ -153,7 +156,7 @@ class Algebra
$cache
);
return self::combineOredClauses($left_clauses, $right_clauses);
return self::combineOredClauses($left_clauses, $right_clauses, $conditional_object_id);
}
if ($conditional instanceof PhpParser\Node\Expr\BooleanNot) {
@ -171,7 +174,8 @@ class Algebra
);
return self::getFormula(
$object_id,
$conditional_object_id,
$conditional_object_id,
$and_expr,
$this_class_name,
$source,
@ -220,6 +224,8 @@ class Algebra
foreach ($anded_types as $orred_types) {
$clauses[] = new Clause(
[$var => $orred_types],
$conditional_object_id,
\spl_object_id($conditional->expr),
false,
true,
$orred_types[0][0] === '='
@ -227,8 +233,7 @@ class Algebra
|| (strlen($orred_types[0]) > 1
&& ($orred_types[0][1] === '='
|| $orred_types[0][1] === '~')),
$redefined ? [$var => true] : [],
$object_id
$redefined ? [$var => true] : []
);
}
}
@ -251,7 +256,8 @@ class Algebra
);
return self::getFormula(
$object_id,
$conditional_object_id,
\spl_object_id($conditional->expr),
$and_expr,
$this_class_name,
$source,
@ -263,7 +269,8 @@ class Algebra
return self::negateFormula(
self::getFormula(
$object_id,
$conditional_object_id,
\spl_object_id($conditional->expr),
$conditional->expr,
$this_class_name,
$source,
@ -286,7 +293,8 @@ class Algebra
$inside_negation = !$inside_negation;
return self::getFormula(
$object_id,
$conditional_object_id,
\spl_object_id($conditional->left),
$conditional->left,
$this_class_name,
$source,
@ -303,7 +311,8 @@ class Algebra
$inside_negation = !$inside_negation;
return self::getFormula(
$object_id,
$conditional_object_id,
\spl_object_id($conditional->right),
$conditional->right,
$this_class_name,
$source,
@ -318,7 +327,8 @@ class Algebra
|| $conditional->left instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr)
) {
return self::getFormula(
$object_id,
$conditional_object_id,
\spl_object_id($conditional->left),
$conditional->left,
$this_class_name,
$source,
@ -333,7 +343,8 @@ class Algebra
|| $conditional->right instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr)
) {
return self::getFormula(
$object_id,
$conditional_object_id,
\spl_object_id($conditional->right),
$conditional->right,
$this_class_name,
$source,
@ -380,6 +391,8 @@ class Algebra
foreach ($anded_types as $orred_types) {
$clauses[] = new Clause(
[$var => $orred_types],
$conditional_object_id,
$creating_object_id,
false,
true,
$orred_types[0][0] === '='
@ -387,8 +400,7 @@ class Algebra
|| (strlen($orred_types[0]) > 1
&& ($orred_types[0][1] === '='
|| $orred_types[0][1] === '~')),
$redefined ? [$var => true] : [],
$object_id
$redefined ? [$var => true] : []
);
}
}
@ -396,7 +408,7 @@ class Algebra
return $clauses;
}
return [new Clause([], true)];
return [new Clause([], $conditional_object_id, $creating_object_id, true)];
}
/**
@ -527,7 +539,7 @@ class Algebra
*/
public static function getTruthsFromFormula(
array $clauses,
?int $creating_object_id = null,
?int $creating_conditional_id = null,
array &$cond_referenced_var_ids = [],
array &$active_truths = []
) {
@ -554,7 +566,7 @@ class Algebra
$truths[$var] = [[$possible_type]];
}
if ($creating_object_id && $creating_object_id === $clause->creating_object_id) {
if ($creating_conditional_id && $creating_conditional_id === $clause->creating_conditional_id) {
if (!isset($active_truths[$var])) {
$active_truths[$var] = [];
}
@ -585,7 +597,7 @@ class Algebra
/** @var array<int, string> $things_that_can_be_said */
$truths[$var] = [$things_that_can_be_said];
if ($creating_object_id && $creating_object_id === $clause->creating_object_id) {
if ($creating_conditional_id && $creating_conditional_id === $clause->creating_conditional_id) {
$active_truths[$var] = [$things_that_can_be_said];
}
}
@ -620,10 +632,7 @@ class Algebra
foreach ($impossible_types as $impossible_type) {
$seed_clause = new Clause(
[$var => [$impossible_type]],
false,
true,
false,
[],
$clause->creating_conditional_id,
$clause->creating_object_id
);
@ -660,13 +669,14 @@ class Algebra
$new_clause = new Clause(
$new_clause_possibilities,
$clause->creating_conditional_id === $grouped_clause->creating_conditional_id
? $clause->creating_conditional_id
: $grouped_clause->creating_conditional_id,
$clause->creating_object_id,
false,
true,
true,
[],
$clause->creating_object_id === $grouped_clause->creating_object_id
? $clause->creating_object_id
: null
[]
);
$new_clauses[] = $new_clause;
@ -694,7 +704,7 @@ class Algebra
*
* @psalm-pure
*/
public static function combineOredClauses(array $left_clauses, array $right_clauses)
public static function combineOredClauses(array $left_clauses, array $right_clauses, int $conditional_object_id)
{
$clauses = [];
@ -709,7 +719,7 @@ class Algebra
}
if ($all_wedges) {
return [new Clause([], true)];
return [new Clause([], $conditional_object_id, $conditional_object_id, true)];
}
foreach ($left_clauses as $left_clause) {
@ -768,26 +778,27 @@ class Algebra
}
}
$creating_object_id = $right_clause->creating_object_id === $left_clause->creating_object_id
? $right_clause->creating_object_id
: null;
$creating_conditional_id = $right_clause->creating_conditional_id === $left_clause->creating_conditional_id
? $right_clause->creating_conditional_id
: $conditional_object_id;
$clauses[] = new Clause(
$possibilities,
$creating_conditional_id,
$creating_conditional_id,
false,
$can_reconcile,
$right_clause->generated
|| $left_clause->generated
|| count($left_clauses) > 1
|| count($right_clauses) > 1,
[],
$creating_object_id
[]
);
}
}
if ($has_wedge) {
$clauses[] = new Clause([], true);
$clauses[] = new Clause([], $conditional_object_id, $conditional_object_id, true);
}
return $clauses;
@ -811,13 +822,12 @@ class Algebra
* @param array<int, Clause> $clauses
*
* @return non-empty-list<Clause>
*
* @psalm-pure
*/
public static function negateFormula(array $clauses)
{
if (!$clauses) {
return [new Clause([], true)];
$cond_id = \mt_rand(0, 100000000);
return [new Clause([], $cond_id, $cond_id, true)];
}
$clauses_with_impossibilities = [];
@ -831,13 +841,15 @@ class Algebra
$impossible_clauses = self::groupImpossibilities($clauses_with_impossibilities);
if (!$impossible_clauses) {
return [new Clause([], true)];
$cond_id = \mt_rand(0, 100000000);
return [new Clause([], $cond_id, $cond_id, true)];
}
$negated = self::simplifyCNF($impossible_clauses);
if (!$negated) {
return [new Clause([], true)];
$cond_id = \mt_rand(0, 100000000);
return [new Clause([], $cond_id, $cond_id, true)];
}
return $negated;

View File

@ -21,7 +21,7 @@ class AlgebraTest extends TestCase
public function testNegateFormula()
{
$formula = [
new Clause(['$a' => ['!falsy']]),
new Clause(['$a' => ['!falsy']], 1, 1),
];
$negated_formula = Algebra::negateFormula($formula);
@ -30,7 +30,7 @@ class AlgebraTest extends TestCase
$this->assertSame(['$a' => ['falsy']], $negated_formula[0]->possibilities);
$formula = [
new Clause(['$a' => ['!falsy'], '$b' => ['!falsy']]),
new Clause(['$a' => ['!falsy'], '$b' => ['!falsy']], 1, 1),
];
$negated_formula = Algebra::negateFormula($formula);
@ -40,8 +40,8 @@ class AlgebraTest extends TestCase
$this->assertSame(['$b' => ['falsy']], $negated_formula[1]->possibilities);
$formula = [
new Clause(['$a' => ['!falsy']]),
new Clause(['$b' => ['!falsy']]),
new Clause(['$a' => ['!falsy']], 1, 1),
new Clause(['$b' => ['!falsy']], 1, 2),
];
$negated_formula = Algebra::negateFormula($formula);
@ -50,7 +50,7 @@ class AlgebraTest extends TestCase
$this->assertSame(['$b' => ['falsy'], '$a' => ['falsy']], $negated_formula[0]->possibilities);
$formula = [
new Clause(['$a' => ['int', 'string'], '$b' => ['!falsy']]),
new Clause(['$a' => ['int', 'string'], '$b' => ['!falsy']], 1, 1),
];
$negated_formula = Algebra::negateFormula($formula);
@ -84,6 +84,7 @@ class AlgebraTest extends TestCase
$statements_analyzer = new StatementsAnalyzer($file_analyzer, new \Psalm\Internal\Provider\NodeDataProvider());
$dnf_clauses = Algebra::getFormula(
\spl_object_id($dnf_stmt->expr),
\spl_object_id($dnf_stmt->expr),
$dnf_stmt->expr,
null,
@ -107,12 +108,16 @@ class AlgebraTest extends TestCase
[
'$a' => ['!falsy'],
'$b' => ['!falsy'],
]
],
1,
1
))->contains(
new Clause(
[
'$a' => ['!falsy'],
]
],
1,
1
)
)
);
@ -121,13 +126,17 @@ class AlgebraTest extends TestCase
(new Clause(
[
'$a' => ['!falsy'],
]
],
1,
1
))->contains(
new Clause(
[
'$a' => ['!falsy'],
'$b' => ['!falsy'],
]
],
1,
1
)
)
);
@ -139,8 +148,8 @@ class AlgebraTest extends TestCase
public function testSimplifyCNF()
{
$formula = [
new Clause(['$a' => ['!falsy']]),
new Clause(['$a' => ['falsy'], '$b' => ['falsy']]),
new Clause(['$a' => ['!falsy']], 1, 1),
new Clause(['$a' => ['falsy'], '$b' => ['falsy']], 1, 2),
];
$simplified_formula = Algebra::simplifyCNF($formula);
@ -156,22 +165,24 @@ class AlgebraTest extends TestCase
[
'$a' => ['=array']
],
1,
2,
false,
true,
true,
[],
5377
[]
))->calculateNegation();
$clause2 = (new \Psalm\Internal\Clause(
[
'$b' => ['isset']
],
1,
2,
false,
true,
true,
[],
5377
[]
))->calculateNegation();
$result_clauses = Algebra::groupImpossibilities([$clause1, $clause2]);

View File

@ -2171,6 +2171,10 @@ class ConditionalTest extends \Psalm\Tests\TestCase
}
}'
],
'manyNestedWedgeAssertions' => [
'<?php
if (rand(0, 1) && rand(0, 1)) {}'
],
'assertionAfterAssertionInsideBooleanNot' => [
'<?php
class A {}

View File

@ -1027,6 +1027,12 @@ class TypeAlgebraTest extends \Psalm\Tests\TestCase
|| ($i && $j);
}'
],
'fineCheck' => [
'<?php
function foo(bool $b, bool $c) : void {
if ((!$b || rand(0, 1)) && (!$c || rand(0, 1))) {}
}'
],
];
}