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

Fix overeager inference

This commit is contained in:
Matt Brown 2020-11-03 16:44:24 -05:00 committed by Daniil Gentili
parent d4846b14e6
commit 8b44459c7c
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
7 changed files with 49 additions and 6 deletions

View File

@ -248,6 +248,10 @@ class Algebra
} }
foreach ($clause->possibilities as $var => $possible_types) { foreach ($clause->possibilities as $var => $possible_types) {
if ($var[0] === '*') {
continue;
}
// if there's only one possible type, return it // if there's only one possible type, return it
if (count($clause->possibilities) === 1 && count($possible_types) === 1) { if (count($clause->possibilities) === 1 && count($possible_types) === 1) {
$possible_type = array_pop($possible_types); $possible_type = array_pop($possible_types);
@ -551,6 +555,13 @@ class Algebra
*/ */
public static function negateFormula(array $clauses): array public static function negateFormula(array $clauses): array
{ {
$clauses = array_filter(
$clauses,
function ($clause) {
return $clause->reconcilable;
}
);
if (!$clauses) { if (!$clauses) {
$cond_id = \mt_rand(0, 100000000); $cond_id = \mt_rand(0, 100000000);
return [new Clause([], $cond_id, $cond_id, true)]; return [new Clause([], $cond_id, $cond_id, true)];

View File

@ -350,6 +350,10 @@ class FormulaGenerator
return $clauses; return $clauses;
} }
return [new Clause([], $conditional_object_id, $creating_object_id, true)]; /** @psalm-suppress MixedOperand */
$conditional_ref = '*' . $conditional->getAttribute('startFilePos')
. ':' . $conditional->getAttribute('endFilePos');
return [new Clause([$conditional_ref => ['!falsy']], $conditional_object_id, $creating_object_id)];
} }
} }

View File

@ -27,7 +27,7 @@ class AlgebraAnalyzer
* *
* @param list<Clause> $formula_1 * @param list<Clause> $formula_1
* @param list<Clause> $formula_2 * @param list<Clause> $formula_2
* @param array<string, mixed> $new_assigned_var_ids * @param array<string, int> $new_assigned_var_ids
*/ */
public static function checkForParadox( public static function checkForParadox(
array $formula_1, array $formula_1,

View File

@ -307,6 +307,8 @@ class IfAnalyzer
$if_scope->if_cond_changed_var_ids = $changed_var_ids; $if_scope->if_cond_changed_var_ids = $changed_var_ids;
} }
$if_context->reconciled_expression_clauses = [];
$old_if_context = clone $if_context; $old_if_context = clone $if_context;
$context->vars_possibly_in_scope = array_merge( $context->vars_possibly_in_scope = array_merge(
$if_context->vars_possibly_in_scope, $if_context->vars_possibly_in_scope,

View File

@ -120,10 +120,7 @@ class MethodCallPurityAnalyzer
&& !$method_storage->mutation_free && !$method_storage->mutation_free
&& !$method_pure_compatible && !$method_pure_compatible
) { ) {
if (!$method_storage->mutation_free) {
$statements_analyzer->getSource()->inferred_has_mutation = true; $statements_analyzer->getSource()->inferred_has_mutation = true;
}
$statements_analyzer->getSource()->inferred_impure = true; $statements_analyzer->getSource()->inferred_impure = true;
} }

View File

@ -55,6 +55,19 @@ class AlgebraTest extends TestCase
$this->assertSame(['$b' => ['falsy']], $negated_formula[2]->possibilities); $this->assertSame(['$b' => ['falsy']], $negated_formula[2]->possibilities);
} }
public function testNegateFormulaWithUnreconcilableTerm(): void
{
$formula = [
new Clause(['$a' => ['int']], 1, 1),
new Clause(['$b' => ['int']], 1, 2, false, false),
];
$negated_formula = Algebra::negateFormula($formula);
$this->assertCount(1, $negated_formula);
$this->assertSame(['$a' => ['!int']], $negated_formula[0]->possibilities);
}
public function testCombinatorialExpansion(): void public function testCombinatorialExpansion(): void
{ {
$dnf = '<?php ($b0 === true && $b4 === true && $b8 === true) $dnf = '<?php ($b0 === true && $b4 === true && $b8 === true)

View File

@ -2958,6 +2958,22 @@ class ConditionalTest extends \Psalm\Tests\TestCase
return $pos; return $pos;
}' }'
], ],
'usedAssertedVarButNotWithStrongerTypeGuarantee' => [
'<?php
function broken(bool $b, ?User $u) : void {
if ($b || (rand(0, 1) && (!$u || takesUser($u)))) {
return;
}
if ($u) {}
}
class User {}
function takesUser(User $a) : bool {
return true;
}'
],
]; ];
} }