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

Consolidate similar functionality

This commit is contained in:
Matt Brown 2020-11-07 00:58:20 -05:00 committed by Daniil Gentili
parent a79b7eeca3
commit 36a89c49bf
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
9 changed files with 161 additions and 184 deletions

View File

@ -188,14 +188,14 @@ class IfConditionalAnalyzer
$assigned_var_ids
);
$cond_assigned_var_ids = array_merge(
$assigned_in_conditional_var_ids = array_merge(
$first_cond_assigned_var_ids,
$more_cond_assigned_var_ids
);
} else {
$cond_referenced_var_ids = $first_cond_referenced_var_ids;
$cond_assigned_var_ids = $first_cond_assigned_var_ids;
$assigned_in_conditional_var_ids = $first_cond_assigned_var_ids;
}
$newish_var_ids = array_map(
@ -211,7 +211,7 @@ class IfConditionalAnalyzer
$if_conditional_context->vars_in_scope,
$pre_condition_vars_in_scope,
$cond_referenced_var_ids,
$cond_assigned_var_ids
$assigned_in_conditional_var_ids
)
);
@ -270,7 +270,7 @@ class IfConditionalAnalyzer
}
// get all the var ids that were referened in the conditional, but not assigned in it
$cond_referenced_var_ids = array_diff_key($cond_referenced_var_ids, $cond_assigned_var_ids);
$cond_referenced_var_ids = array_diff_key($cond_referenced_var_ids, $assigned_in_conditional_var_ids);
$cond_referenced_var_ids = array_merge($newish_var_ids, $cond_referenced_var_ids);
@ -278,7 +278,7 @@ class IfConditionalAnalyzer
$if_context,
$original_context,
$cond_referenced_var_ids,
$cond_assigned_var_ids,
$assigned_in_conditional_var_ids,
$entry_clauses
);
}

View File

@ -157,60 +157,18 @@ class ElseAnalyzer
$if_scope->final_actions = array_merge($final_actions, $if_scope->final_actions);
$else_redefined_vars = $else_context->getRedefinedVars($original_context->vars_in_scope);
// if it doesn't end in a return
if (!$has_leaving_statements) {
if ($if_scope->new_vars === null && $else) {
$if_scope->new_vars = array_diff_key($else_context->vars_in_scope, $outer_context->vars_in_scope);
} elseif ($if_scope->new_vars !== null) {
foreach ($if_scope->new_vars as $new_var => $type) {
if (!$else_context->hasVariable($new_var)) {
unset($if_scope->new_vars[$new_var]);
} else {
$if_scope->new_vars[$new_var] = Type::combineUnionTypes(
$type,
$else_context->vars_in_scope[$new_var],
$codebase
);
}
}
}
if ($if_scope->assigned_var_ids === null) {
$if_scope->assigned_var_ids = $new_assigned_var_ids;
} else {
$if_scope->assigned_var_ids = array_intersect_key($new_assigned_var_ids, $if_scope->assigned_var_ids);
}
if ($if_scope->redefined_vars === null) {
$if_scope->redefined_vars = $else_redefined_vars;
$if_scope->possibly_redefined_vars = $if_scope->redefined_vars;
} else {
foreach ($if_scope->redefined_vars as $redefined_var => $type) {
if (!isset($else_redefined_vars[$redefined_var])) {
unset($if_scope->redefined_vars[$redefined_var]);
} else {
$if_scope->redefined_vars[$redefined_var] = Type::combineUnionTypes(
$else_redefined_vars[$redefined_var],
$type,
$codebase
);
}
}
foreach ($else_redefined_vars as $var => $type) {
if (isset($if_scope->possibly_redefined_vars[$var])) {
$if_scope->possibly_redefined_vars[$var] = Type::combineUnionTypes(
$type,
$if_scope->possibly_redefined_vars[$var],
$codebase
);
} else {
$if_scope->possibly_redefined_vars[$var] = $type;
}
}
}
IfAnalyzer::updateIfScope(
$codebase,
$if_scope,
$else_context,
$original_context,
$new_assigned_var_ids,
$new_possibly_assigned_var_ids,
[],
(bool) $else
);
$if_scope->reasonable_clauses = [];
}

View File

@ -63,7 +63,7 @@ class ElseIfAnalyzer
$elseif_context = $if_conditional_scope->if_context;
$cond_referenced_var_ids = $if_conditional_scope->cond_referenced_var_ids;
$cond_assigned_var_ids = $if_conditional_scope->cond_assigned_var_ids;
$assigned_in_conditional_var_ids = $if_conditional_scope->assigned_in_conditional_var_ids;
$entry_clauses = $if_conditional_scope->entry_clauses;
} catch (\Psalm\Exception\ScopeAnalysisException $e) {
return false;
@ -114,11 +114,11 @@ class ElseIfAnalyzer
/**
* @return Clause
*/
function (Clause $c) use ($cond_assigned_var_ids, $elseif_cond_id): Clause {
function (Clause $c) use ($assigned_in_conditional_var_ids, $elseif_cond_id): Clause {
$keys = array_keys($c->possibilities);
foreach ($keys as $key) {
foreach ($cond_assigned_var_ids as $conditional_assigned_var_id => $_) {
foreach ($assigned_in_conditional_var_ids as $conditional_assigned_var_id => $_) {
if (preg_match('/^' . preg_quote($conditional_assigned_var_id, '/') . '(\[|-|$)/', $key)) {
return new Clause([], $elseif_cond_id, $elseif_cond_id, true);
}
@ -136,7 +136,7 @@ class ElseIfAnalyzer
$elseif_clauses,
$statements_analyzer,
$elseif->cond,
$cond_assigned_var_ids
$assigned_in_conditional_var_ids
);
$elseif_context_clauses = array_merge($entry_clauses, $elseif_clauses);
@ -219,7 +219,7 @@ class ElseIfAnalyzer
}
}
$changed_var_ids = [];
$newly_reconciled_var_ids = [];
// if the elseif has an || in the conditional, we cannot easily reason about it
if ($reconcilable_elseif_types) {
@ -227,7 +227,7 @@ class ElseIfAnalyzer
$reconcilable_elseif_types,
$active_elseif_types,
$elseif_context->vars_in_scope,
$changed_var_ids,
$newly_reconciled_var_ids,
$cond_referenced_var_ids,
$statements_analyzer,
$statements_analyzer->getTemplateTypeMap() ?: [],
@ -243,10 +243,10 @@ class ElseIfAnalyzer
$elseif_context->vars_in_scope = $elseif_vars_reconciled;
if ($changed_var_ids) {
if ($newly_reconciled_var_ids) {
$elseif_context->clauses = Context::removeReconciledClauses(
$elseif_context->clauses,
$changed_var_ids
$newly_reconciled_var_ids
)[0];
}
}
@ -314,79 +314,16 @@ class ElseIfAnalyzer
$if_scope->final_actions = array_merge($final_actions, $if_scope->final_actions);
// update the parent context as necessary
$elseif_redefined_vars = $elseif_context->getRedefinedVars($outer_context->vars_in_scope);
if (!$has_leaving_statements) {
if ($if_scope->new_vars === null) {
$if_scope->new_vars = array_diff_key($elseif_context->vars_in_scope, $outer_context->vars_in_scope);
} else {
foreach ($if_scope->new_vars as $new_var => $type) {
if (!$elseif_context->hasVariable($new_var)) {
unset($if_scope->new_vars[$new_var]);
} else {
$if_scope->new_vars[$new_var] = Type::combineUnionTypes(
$type,
$elseif_context->vars_in_scope[$new_var],
$codebase
);
}
}
}
$possibly_redefined_vars = $elseif_redefined_vars;
foreach ($possibly_redefined_vars as $var_id => $_) {
if (!isset($new_stmts_assigned_var_ids[$var_id])
&& isset($changed_var_ids[$var_id])
) {
unset($possibly_redefined_vars[$var_id]);
}
}
$assigned_var_ids = array_merge($new_stmts_assigned_var_ids, $cond_assigned_var_ids);
if ($if_scope->assigned_var_ids === null) {
$if_scope->assigned_var_ids = $assigned_var_ids;
} else {
$if_scope->assigned_var_ids = array_intersect_key($assigned_var_ids, $if_scope->assigned_var_ids);
}
if ($if_scope->redefined_vars === null) {
$if_scope->redefined_vars = $elseif_redefined_vars;
$if_scope->possibly_redefined_vars = $possibly_redefined_vars;
} else {
foreach ($if_scope->redefined_vars as $redefined_var => $type) {
if (!isset($elseif_redefined_vars[$redefined_var])) {
unset($if_scope->redefined_vars[$redefined_var]);
} else {
$if_scope->redefined_vars[$redefined_var] = Type::combineUnionTypes(
$elseif_redefined_vars[$redefined_var],
$type,
$codebase
);
if (isset($outer_context->vars_in_scope[$redefined_var])
&& $if_scope->redefined_vars[$redefined_var]->equals(
$outer_context->vars_in_scope[$redefined_var]
)
) {
unset($if_scope->redefined_vars[$redefined_var]);
}
}
}
foreach ($possibly_redefined_vars as $var => $type) {
if (isset($if_scope->possibly_redefined_vars[$var])) {
$if_scope->possibly_redefined_vars[$var] = Type::combineUnionTypes(
$type,
$if_scope->possibly_redefined_vars[$var],
$codebase
);
} else {
$if_scope->possibly_redefined_vars[$var] = $type;
}
}
}
IfAnalyzer::updateIfScope(
$codebase,
$if_scope,
$elseif_context,
$outer_context,
array_merge($new_stmts_assigned_var_ids, $assigned_in_conditional_var_ids),
$new_stmts_possibly_assigned_var_ids,
$newly_reconciled_var_ids
);
$reasonable_clause_count = count($if_scope->reasonable_clauses);
@ -394,7 +331,7 @@ class ElseIfAnalyzer
$if_scope->reasonable_clauses = Algebra::combineOredClauses(
$if_scope->reasonable_clauses,
$elseif_clauses,
\spl_object_id($elseif->cond)
$elseif_cond_id
);
} else {
$if_scope->reasonable_clauses = [];
@ -405,13 +342,13 @@ class ElseIfAnalyzer
if ($negated_elseif_types) {
if ($has_leaving_statements) {
$changed_var_ids = [];
$newly_reconciled_var_ids = [];
$leaving_vars_reconciled = Reconciler::reconcileKeyedTypes(
$negated_elseif_types,
[],
$pre_conditional_context->vars_in_scope,
$changed_var_ids,
$newly_reconciled_var_ids,
[],
$statements_analyzer,
$statements_analyzer->getTemplateTypeMap() ?: [],

View File

@ -6,6 +6,7 @@ use Psalm\Internal\Analyzer\ScopeAnalyzer;
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
use Psalm\Internal\Analyzer\StatementsAnalyzer;
use Psalm\Internal\Type\Comparator\UnionTypeComparator;
use Psalm\Codebase;
use Psalm\CodeLocation;
use Psalm\Context;
use Psalm\Issue\ConflictingReferenceConstraint;
@ -114,27 +115,19 @@ class IfAnalyzer
$mic_drop = false;
if (!$has_leaving_statements) {
$if_scope->new_vars = array_diff_key($if_context->vars_in_scope, $outer_context->vars_in_scope);
$if_scope->redefined_vars = $if_context->getRedefinedVars($outer_context->vars_in_scope);
$if_scope->possibly_redefined_vars = $if_scope->redefined_vars;
$if_scope->assigned_var_ids = $new_assigned_var_ids;
$if_scope->possibly_assigned_var_ids = $new_possibly_assigned_var_ids;
$changed_var_ids = $new_assigned_var_ids;
// if the variable was only set in the conditional, it's not possibly redefined
foreach ($if_scope->possibly_redefined_vars as $var_id => $_) {
if (!isset($new_possibly_assigned_var_ids[$var_id])
&& isset($if_scope->if_cond_changed_var_ids[$var_id])
) {
unset($if_scope->possibly_redefined_vars[$var_id]);
}
}
self::updateIfScope(
$codebase,
$if_scope,
$if_context,
$outer_context,
$new_assigned_var_ids,
$new_possibly_assigned_var_ids,
$if_scope->if_cond_changed_var_ids
);
if ($if_scope->reasonable_clauses) {
// remove all reasonable clauses that would be negated by the if stmts
foreach ($changed_var_ids as $var_id => $_) {
foreach ($new_assigned_var_ids as $var_id => $_) {
$if_scope->reasonable_clauses = Context::filterClauses(
$var_id,
$if_scope->reasonable_clauses,
@ -251,7 +244,7 @@ class IfAnalyzer
array $new_assigned_var_ids
) : bool {
// If we're assigning inside
if ($if_conditional_scope->cond_assigned_var_ids
if ($if_conditional_scope->assigned_in_conditional_var_ids
&& $if_scope->mic_drop_context
) {
self::addConditionallyAssignedVarsToContext(
@ -259,7 +252,7 @@ class IfAnalyzer
$cond,
$if_scope->mic_drop_context,
$outer_context,
$if_conditional_scope->cond_assigned_var_ids
$if_conditional_scope->assigned_in_conditional_var_ids
);
}
@ -267,13 +260,13 @@ class IfAnalyzer
return false;
}
$changed_var_ids = [];
$newly_reconciled_var_ids = [];
$outer_context_vars_reconciled = Reconciler::reconcileKeyedTypes(
$if_scope->negated_types,
[],
$outer_context->vars_in_scope,
$changed_var_ids,
$newly_reconciled_var_ids,
[],
$statements_analyzer,
$statements_analyzer->getTemplateTypeMap() ?: [],
@ -288,20 +281,20 @@ class IfAnalyzer
)
);
foreach ($changed_var_ids as $changed_var_id => $_) {
foreach ($newly_reconciled_var_ids as $changed_var_id => $_) {
$outer_context->removeVarFromConflictingClauses($changed_var_id);
}
$changed_var_ids += $new_assigned_var_ids;
$newly_reconciled_var_ids += $new_assigned_var_ids;
foreach ($changed_var_ids as $var_id => $_) {
foreach ($newly_reconciled_var_ids as $var_id => $_) {
$if_scope->negated_clauses = Context::filterClauses(
$var_id,
$if_scope->negated_clauses
);
}
foreach ($changed_var_ids as $var_id => $_) {
foreach ($newly_reconciled_var_ids as $var_id => $_) {
$first_appearance = $statements_analyzer->getFirstAppearance($var_id);
if ($first_appearance
@ -335,19 +328,19 @@ class IfAnalyzer
}
/**
* @param array<string, int> $cond_assigned_var_ids
* @param array<string, int> $assigned_in_conditional_var_ids
*/
public static function addConditionallyAssignedVarsToContext(
StatementsAnalyzer $statements_analyzer,
PhpParser\Node\Expr $cond,
Context $mic_drop_context,
Context $outer_context,
array $cond_assigned_var_ids
array $assigned_in_conditional_var_ids
) : void {
// this filters out coercions to expeccted types in ArgumentAnalyzer
$cond_assigned_var_ids = \array_filter($cond_assigned_var_ids);
$assigned_in_conditional_var_ids = \array_filter($assigned_in_conditional_var_ids);
if (!$cond_assigned_var_ids) {
if (!$assigned_in_conditional_var_ids) {
return;
}
@ -397,7 +390,7 @@ class IfAnalyzer
$statements_analyzer->node_data = $old_node_data;
foreach ($cond_assigned_var_ids as $var_id => $_) {
foreach ($assigned_in_conditional_var_ids as $var_id => $_) {
if (isset($mic_drop_context->vars_in_scope[$var_id])) {
$outer_context->vars_in_scope[$var_id] = clone $mic_drop_context->vars_in_scope[$var_id];
}
@ -431,4 +424,95 @@ class IfAnalyzer
return new PhpParser\Node\Expr\BooleanNot($expr, $expr->getAttributes());
}
/**
* @param array<string, int> $assigned_var_ids
* @param array<string, bool> $possibly_assigned_var_ids
* @param array<string, bool> $newly_reconciled_var_ids
*/
public static function updateIfScope(
Codebase $codebase,
IfScope $if_scope,
Context $if_context,
Context $outer_context,
array $assigned_var_ids,
array $possibly_assigned_var_ids,
array $newly_reconciled_var_ids,
bool $update_new_vars = true
) : void {
$redefined_vars = $if_context->getRedefinedVars($outer_context->vars_in_scope);
if ($if_scope->new_vars === null) {
if ($update_new_vars) {
$if_scope->new_vars = array_diff_key($if_context->vars_in_scope, $outer_context->vars_in_scope);
}
} else {
foreach ($if_scope->new_vars as $new_var => $type) {
if (!$if_context->hasVariable($new_var)) {
unset($if_scope->new_vars[$new_var]);
} else {
$if_scope->new_vars[$new_var] = Type::combineUnionTypes(
$type,
$if_context->vars_in_scope[$new_var],
$codebase
);
}
}
}
$possibly_redefined_vars = $redefined_vars;
foreach ($possibly_redefined_vars as $var_id => $_) {
if (!isset($possibly_assigned_var_ids[$var_id])
&& isset($newly_reconciled_var_ids[$var_id])
) {
unset($possibly_redefined_vars[$var_id]);
}
}
if ($if_scope->assigned_var_ids === null) {
$if_scope->assigned_var_ids = $assigned_var_ids;
} else {
$if_scope->assigned_var_ids = \array_intersect_key($assigned_var_ids, $if_scope->assigned_var_ids);
}
$if_scope->possibly_assigned_var_ids += $possibly_assigned_var_ids;
if ($if_scope->redefined_vars === null) {
$if_scope->redefined_vars = $redefined_vars;
$if_scope->possibly_redefined_vars = $possibly_redefined_vars;
} else {
foreach ($if_scope->redefined_vars as $redefined_var => $type) {
if (!isset($redefined_vars[$redefined_var])) {
unset($if_scope->redefined_vars[$redefined_var]);
} else {
$if_scope->redefined_vars[$redefined_var] = Type::combineUnionTypes(
$redefined_vars[$redefined_var],
$type,
$codebase
);
if (isset($outer_context->vars_in_scope[$redefined_var])
&& $if_scope->redefined_vars[$redefined_var]->equals(
$outer_context->vars_in_scope[$redefined_var]
)
) {
unset($if_scope->redefined_vars[$redefined_var]);
}
}
}
foreach ($possibly_redefined_vars as $var => $type) {
if (isset($if_scope->possibly_redefined_vars[$var])) {
$if_scope->possibly_redefined_vars[$var] = Type::combineUnionTypes(
$type,
$if_scope->possibly_redefined_vars[$var],
$codebase
);
} else {
$if_scope->possibly_redefined_vars[$var] = $type;
}
}
}
}
}

View File

@ -101,7 +101,7 @@ class IfElseAnalyzer
$original_context = $if_conditional_scope->original_context;
$cond_referenced_var_ids = $if_conditional_scope->cond_referenced_var_ids;
$cond_assigned_var_ids = $if_conditional_scope->cond_assigned_var_ids;
$assigned_in_conditional_var_ids = $if_conditional_scope->assigned_in_conditional_var_ids;
} catch (\Psalm\Exception\ScopeAnalysisException $e) {
return false;
}
@ -161,11 +161,11 @@ class IfElseAnalyzer
$if_clauses,
$statements_analyzer,
$stmt->cond,
$cond_assigned_var_ids
$assigned_in_conditional_var_ids
);
// if we have assignments in the if, we may have duplicate clauses
if ($cond_assigned_var_ids) {
if ($assigned_in_conditional_var_ids) {
$if_clauses = Algebra::simplifyCNF($if_clauses);
}

View File

@ -71,7 +71,7 @@ class OrAnalyzer
$left_context = $if_conditional_scope->if_context;
$left_referenced_var_ids = $if_conditional_scope->cond_referenced_var_ids;
$left_assigned_var_ids = $if_conditional_scope->cond_assigned_var_ids;
$left_assigned_var_ids = $if_conditional_scope->assigned_in_conditional_var_ids;
if ($stmt->left instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr) {
$mic_drop_context = clone $context;

View File

@ -845,14 +845,12 @@ class FunctionCallAnalyzer extends CallAnalyzer
$codebase
);
$cond_assigned_var_ids = [];
\Psalm\Internal\Analyzer\AlgebraAnalyzer::checkForParadox(
$context->clauses,
$assert_clauses,
$statements_analyzer,
$stmt,
$cond_assigned_var_ids
[]
);
$simplified_clauses = Algebra::simplifyCNF(array_merge($context->clauses, $assert_clauses));

View File

@ -48,7 +48,7 @@ class TernaryAnalyzer
$if_context = $if_conditional_scope->if_context;
$cond_referenced_var_ids = $if_conditional_scope->cond_referenced_var_ids;
$cond_assigned_var_ids = $if_conditional_scope->cond_assigned_var_ids;
$assigned_in_conditional_var_ids = $if_conditional_scope->assigned_in_conditional_var_ids;
} catch (\Psalm\Exception\ScopeAnalysisException $e) {
return false;
}
@ -110,7 +110,7 @@ class TernaryAnalyzer
$if_clauses,
$statements_analyzer,
$stmt->cond,
$cond_assigned_var_ids
$assigned_in_conditional_var_ids
);
$ternary_clauses = array_merge($context->clauses, $if_clauses);

View File

@ -20,27 +20,27 @@ class IfConditionalScope
/**
* @var array<string, int>
*/
public $cond_assigned_var_ids;
public $assigned_in_conditional_var_ids;
/** @var list<\Psalm\Internal\Clause> */
public $entry_clauses;
/**
* @param array<string, bool> $cond_referenced_var_ids
* @param array<string, int> $cond_assigned_var_ids
* @param array<string, int> $assigned_in_conditional_var_ids
* @param list<\Psalm\Internal\Clause> $entry_clauses
*/
public function __construct(
Context $if_context,
Context $original_context,
array $cond_referenced_var_ids,
array $cond_assigned_var_ids,
array $assigned_in_conditional_var_ids,
array $entry_clauses
) {
$this->if_context = $if_context;
$this->original_context = $original_context;
$this->cond_referenced_var_ids = $cond_referenced_var_ids;
$this->cond_assigned_var_ids = $cond_assigned_var_ids;
$this->assigned_in_conditional_var_ids = $assigned_in_conditional_var_ids;
$this->entry_clauses = $entry_clauses;
}
}