Skip to content

Commit

Permalink
Improve reconciliation of ||
Browse files Browse the repository at this point in the history
Ref #2426
  • Loading branch information
muglug committed Jan 6, 2020
1 parent 578b90c commit 5c45221
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 39 deletions.
Expand Up @@ -295,44 +295,69 @@ function ($c) {
return null;
}

$pre_referenced_var_ids = $context->referenced_var_ids;
$context->referenced_var_ids = [];
if (!$stmt->left instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr
&& !($stmt->left instanceof PhpParser\Node\Expr\BooleanNot
&& $stmt->left->expr instanceof PhpParser\Node\Expr\BinaryOp\BooleanAnd)
) {
$if_scope = new \Psalm\Internal\Scope\IfScope();

$pre_assigned_var_ids = $context->assigned_var_ids;
try {
$if_conditional_scope = IfAnalyzer::analyzeIfConditional(
$statements_analyzer,
$stmt->left,
$context,
$codebase,
$if_scope,
$context->branch_point ?: (int) $stmt->getAttribute('startFilePos')
);

$left_context = clone $context;
$left_context->parent_context = $context;
$left_context->if_context = null;
$left_context->assigned_var_ids = [];
$left_context = $if_conditional_scope->if_context;

if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->left, $left_context) === false) {
return false;
}
$left_referenced_var_ids = $if_conditional_scope->cond_referenced_var_ids;
$left_assigned_var_ids = $if_conditional_scope->cond_assigned_var_ids;
} catch (\Psalm\Exception\ScopeAnalysisException $e) {
return false;
}
} else {
$pre_referenced_var_ids = $context->referenced_var_ids;
$context->referenced_var_ids = [];

foreach ($left_context->vars_in_scope as $var_id => $type) {
if (!isset($context->vars_in_scope[$var_id])) {
if (isset($left_context->assigned_var_ids[$var_id])) {
$context->vars_in_scope[$var_id] = clone $type;
$pre_assigned_var_ids = $context->assigned_var_ids;

$left_context = clone $context;
$left_context->parent_context = $context;
$left_context->if_context = null;
$left_context->assigned_var_ids = [];

if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->left, $left_context) === false) {
return false;
}

foreach ($left_context->vars_in_scope as $var_id => $type) {
if (!isset($context->vars_in_scope[$var_id])) {
if (isset($left_context->assigned_var_ids[$var_id])) {
$context->vars_in_scope[$var_id] = clone $type;
}
} else {
$context->vars_in_scope[$var_id] = Type::combineUnionTypes(
$context->vars_in_scope[$var_id],
$type,
$codebase
);
}
} else {
$context->vars_in_scope[$var_id] = Type::combineUnionTypes(
$context->vars_in_scope[$var_id],
$type,
$codebase
);
}
}

if ($context->collect_references) {
$context->unreferenced_vars = $left_context->unreferenced_vars;
}
if ($context->collect_references) {
$context->unreferenced_vars = $left_context->unreferenced_vars;
}

$left_referenced_var_ids = $left_context->referenced_var_ids;
$left_context->referenced_var_ids = array_merge($pre_referenced_var_ids, $left_referenced_var_ids);
$left_referenced_var_ids = $left_context->referenced_var_ids;
$left_context->referenced_var_ids = array_merge($pre_referenced_var_ids, $left_referenced_var_ids);

$left_assigned_var_ids = array_diff_key($left_context->assigned_var_ids, $pre_assigned_var_ids);
$left_assigned_var_ids = array_diff_key($left_context->assigned_var_ids, $pre_assigned_var_ids);

$left_referenced_var_ids = array_diff_key($left_referenced_var_ids, $left_assigned_var_ids);
$left_referenced_var_ids = array_diff_key($left_referenced_var_ids, $left_assigned_var_ids);
}

$left_clauses = Algebra::getFormula(
\spl_object_id($stmt->left),
Expand Down
15 changes: 4 additions & 11 deletions tests/TypeReconciliation/ConditionalTest.php
Expand Up @@ -2498,7 +2498,7 @@ function foo(): int {
return 0;
}',
],
'SKIPPED-assertHardConditional' => [
'assertHardConditionalWithString' => [
'<?php
interface Convertor {
function maybeConvert(string $value): ?SomeObject;
Expand All @@ -2508,19 +2508,12 @@ interface SomeObject {
function isValid(): bool;
}
/**
* @param mixed $value
*/
function exampleWithOr(Convertor $convertor, $value): SomeObject
{
if (!\is_string($value)
|| ($value = $convertor->maybeConvert($value)) === null
|| !$value->isValid()
) {
function exampleWithOr(Convertor $convertor, string $value): SomeObject {
if (($value = $convertor->maybeConvert($value)) === null || !$value->isValid()) {
throw new Exception();
}
return $value;
return $value; // $value is SomeObject here and cannot be a string
}'
],
];
Expand Down

0 comments on commit 5c45221

Please sign in to comment.