1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-30 04:39:00 +01:00
psalm/tests/AlgebraTest.php

264 lines
8.3 KiB
PHP
Raw Normal View History

2019-07-15 22:08:38 +02:00
<?php
2019-07-15 22:08:38 +02:00
namespace Psalm\Tests;
use PhpParser;
use Psalm\Context;
2021-06-08 04:55:21 +02:00
use Psalm\Internal\Algebra;
2020-11-03 22:15:44 +01:00
use Psalm\Internal\Algebra\FormulaGenerator;
2019-07-15 22:08:38 +02:00
use Psalm\Internal\Analyzer\FileAnalyzer;
use Psalm\Internal\Analyzer\StatementsAnalyzer;
use Psalm\Internal\Clause;
2021-12-03 20:11:20 +01:00
use Psalm\Internal\Provider\NodeDataProvider;
2019-07-15 22:08:38 +02:00
use Psalm\Internal\Provider\StatementsProvider;
use Psalm\Storage\Assertion\Falsy;
use Psalm\Storage\Assertion\IsIdentical;
use Psalm\Storage\Assertion\IsIsset;
use Psalm\Storage\Assertion\IsType;
use Psalm\Storage\Assertion\Truthy;
use Psalm\Type;
use Psalm\Type\Atomic\TArray;
use Psalm\Type\Atomic\TInt;
use Psalm\Type\Atomic\TString;
2019-07-15 22:08:38 +02:00
2021-12-03 21:07:25 +01:00
use function spl_object_id;
2019-07-15 22:08:38 +02:00
class AlgebraTest extends TestCase
{
public function testNegateFormula(): void
2019-07-15 22:08:38 +02:00
{
$formula = [
new Clause(['$a' => ['truthy' => new Truthy()]], 1, 1),
2019-07-15 22:08:38 +02:00
];
$negated_formula = Algebra::negateFormula($formula);
$this->assertCount(1, $negated_formula);
$this->assertSame('!$a', (string)$negated_formula[0]);
2019-07-15 22:08:38 +02:00
$formula = [
new Clause(['$a' => ['truthy' => new Truthy()], '$b' => ['truthy' => new Truthy()]], 1, 1),
2019-07-15 22:08:38 +02:00
];
$negated_formula = Algebra::negateFormula($formula);
$this->assertCount(2, $negated_formula);
$this->assertSame('!$a', (string)$negated_formula[0]);
$this->assertSame('!$b', (string)$negated_formula[1]);
2019-07-15 22:08:38 +02:00
$formula = [
new Clause(['$a' => ['truthy' => new Truthy()]], 1, 1),
new Clause(['$b' => ['truthy' => new Truthy()]], 1, 2),
2019-07-15 22:08:38 +02:00
];
$negated_formula = Algebra::negateFormula($formula);
$this->assertCount(1, $negated_formula);
$this->assertSame('(!$a) || (!$b)', (string)$negated_formula[0]);
2019-07-15 22:08:38 +02:00
$a1 = new IsType(new TInt());
$a2 = new IsType(new TString());
2019-07-15 22:08:38 +02:00
$formula = [
new Clause(
[
'$a' => [(string)$a1 => $a1, (string)$a2 => $a2],
'$b' => ['truthy' => new Truthy()]
],
1,
1
),
2019-07-15 22:08:38 +02:00
];
$negated_formula = Algebra::negateFormula($formula);
$this->assertCount(3, $negated_formula);
2022-03-14 18:02:55 +01:00
$this->assertSame('$a is not int', (string)$negated_formula[0]);
$this->assertSame('$a is not string', (string)$negated_formula[1]);
$this->assertSame('!$b', (string)$negated_formula[2]);
2019-07-15 22:08:38 +02:00
}
2020-11-03 22:44:24 +01:00
public function testNegateFormulaWithUnreconcilableTerm(): void
{
$a1 = new IsType(new TInt());
2020-11-03 22:44:24 +01:00
$formula = [
new Clause(['$a' => [(string)$a1 => $a1]], 1, 1),
2022-10-03 11:32:15 +02:00
new Clause(['$b' => [(string)$a1 => $a1]], 1, 2, false, false),
2020-11-03 22:44:24 +01:00
];
$negated_formula = Algebra::negateFormula($formula);
$this->assertCount(1, $negated_formula);
$this->assertSame('$a is not int', (string)$negated_formula[0]);
2020-11-03 22:44:24 +01:00
}
public function testCombinatorialExpansion(): void
2019-07-15 22:08:38 +02:00
{
$dnf = '<?php ($b0 === true && $b4 === true && $b8 === true)
|| ($b0 === true && $b1 === true && $b2 === true)
|| ($b0 === true && $b3 === true && $b6 === true)
|| ($b1 === true && $b4 === true && $b7 === true)
|| ($b2 === true && $b5 === true && $b8 === true)
|| ($b2 === true && $b4 === true && $b6 === true)
|| ($b3 === true && $b4 === true && $b5 === true)
|| ($b6 === true && $b7 === true && $b8 === true);';
$has_errors = false;
$dnf_stmt = StatementsProvider::parseStatements($dnf, 7_04_00, $has_errors)[0];
2019-07-15 22:08:38 +02:00
$this->assertInstanceOf(PhpParser\Node\Stmt\Expression::class, $dnf_stmt);
$file_analyzer = new FileAnalyzer($this->project_analyzer, 'somefile.php', 'somefile.php');
$file_analyzer->context = new Context();
2021-12-03 20:11:20 +01:00
$statements_analyzer = new StatementsAnalyzer($file_analyzer, new NodeDataProvider());
2019-07-15 22:08:38 +02:00
2020-11-03 22:15:44 +01:00
$dnf_clauses = FormulaGenerator::getFormula(
2021-12-03 21:07:25 +01:00
spl_object_id($dnf_stmt->expr),
spl_object_id($dnf_stmt->expr),
2019-07-15 22:08:38 +02:00
$dnf_stmt->expr,
null,
$statements_analyzer
);
$this->assertCount(6_561, $dnf_clauses);
2019-07-15 22:08:38 +02:00
$simplified_dnf_clauses = Algebra::simplifyCNF($dnf_clauses);
$this->assertCount(23, $simplified_dnf_clauses);
}
public function testContainsClause(): void
2019-07-15 22:08:38 +02:00
{
$this->assertTrue(
(new Clause(
[
'$a' => ['truthy' => new Truthy()],
'$b' => ['truthy' => new Truthy()],
2020-08-26 21:35:29 +02:00
],
1,
1
2019-07-15 22:08:38 +02:00
))->contains(
new Clause(
[
'$a' => ['truthy' => new Truthy()],
2020-08-26 21:35:29 +02:00
],
1,
1
2019-07-15 22:08:38 +02:00
)
)
);
$this->assertFalse(
(new Clause(
[
'$a' => ['truthy' => new Truthy()],
2020-08-26 21:35:29 +02:00
],
1,
1
2019-07-15 22:08:38 +02:00
))->contains(
new Clause(
[
'$a' => ['truthy' => new Truthy()],
'$b' => ['truthy' => new Truthy()],
2020-08-26 21:35:29 +02:00
],
1,
1
2019-07-15 22:08:38 +02:00
)
)
);
}
public function testSimplifySimpleCNF(): void
2019-07-15 22:08:38 +02:00
{
$formula = [
new Clause(['$a' => ['truthy' => new Truthy()]], 1, 1),
new Clause(['$a' => ['falsy' => new Falsy()], '$b' => ['falsy' => new Falsy()]], 1, 2),
2019-07-15 22:08:38 +02:00
];
$simplified_formula = Algebra::simplifyCNF($formula);
$this->assertCount(2, $simplified_formula);
$this->assertSame('$a', (string)$simplified_formula[0]);
$this->assertSame('!$b', (string)$simplified_formula[1]);
2019-07-15 22:08:38 +02:00
}
2022-01-16 21:34:25 +01:00
public function testSimplifyCNFWithOneUselessTerm(): void
{
/** @psalm-suppress ArgumentTypeCoercion due to Psalm bug */
$formula = [
new Clause(['$a' => ['truthy' => new Truthy()], '$b' => ['truthy' => new Truthy()]], 1, 1),
new Clause(['$a' => ['falsy' => new Falsy()], '$b' => ['truthy' => new Truthy()]], 1, 2),
];
$simplified_formula = Algebra::simplifyCNF($formula);
$this->assertCount(1, $simplified_formula);
$this->assertSame('$b', (string)$simplified_formula[0]);
}
public function testSimplifyCNFWithNonUselessTerm(): void
{
$formula = [
new Clause(['$a' => ['truthy' => new Truthy()], '$b' => ['truthy' => new Truthy()]], 1, 1),
new Clause(['$a' => ['falsy' => new Falsy()], '$b' => ['falsy' => new Falsy()]], 1, 2),
];
$simplified_formula = Algebra::simplifyCNF($formula);
$this->assertCount(2, $simplified_formula);
$this->assertSame('($a) || ($b)', (string)$simplified_formula[0]);
$this->assertSame('(!$a) || (!$b)', (string)$simplified_formula[1]);
}
public function testSimplifyCNFWithUselessTermAndOneInMiddle(): void
{
/** @psalm-suppress ArgumentTypeCoercion due to Psalm bug */
$formula = [
new Clause(['$a' => ['truthy' => new Truthy()], '$b' => ['truthy' => new Truthy()]], 1, 1),
new Clause(['$b' => ['truthy' => new Truthy()]], 1, 2),
new Clause(['$a' => ['falsy' => new Falsy()], '$b' => ['truthy' => new Truthy()]], 1, 3),
];
$simplified_formula = Algebra::simplifyCNF($formula);
$this->assertCount(1, $simplified_formula);
$this->assertSame('$b', (string)$simplified_formula[0]);
}
public function testGroupImpossibilities(): void
{
$a1 = new IsIdentical(new TArray([Type::getArrayKey(), Type::getMixed()]));
2021-12-03 20:11:20 +01:00
$clause1 = (new Clause(
[
'$a' => [(string)$a1 => $a1]
],
2020-08-26 21:35:29 +02:00
1,
2,
false,
true,
true,
2020-08-26 21:35:29 +02:00
[]
))->calculateNegation();
$a2 = new IsIsset();
2021-12-03 20:11:20 +01:00
$clause2 = (new Clause(
[
'$b' => [(string)$a2 => $a2]
],
2020-08-26 21:35:29 +02:00
1,
2,
false,
true,
true,
2020-08-26 21:35:29 +02:00
[]
))->calculateNegation();
$result_clauses = Algebra::groupImpossibilities([$clause1, $clause2]);
$this->assertCount(0, $result_clauses);
}
2019-07-15 22:08:38 +02:00
}