diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/IfAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/IfAnalyzer.php index 596c192d7..a882d6b3c 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/IfAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/IfAnalyzer.php @@ -146,92 +146,22 @@ class IfAnalyzer } else { if (!$has_break_statement) { $if_scope->reasonable_clauses = []; - } - } - if ($has_leaving_statements && !$has_break_statement && !$stmt->else && !$stmt->elseifs) { - // If we're assigning inside - if ($if_conditional_scope->cond_assigned_var_ids - && $if_scope->mic_drop_context - ) { - self::addConditionallyAssignedVarsToContext( - $statements_analyzer, - $stmt->cond, - $if_scope->mic_drop_context, - $outer_context, - $if_conditional_scope->cond_assigned_var_ids - ); - } + if (!$stmt->else && !$stmt->elseifs) { + $mic_drop = self::handleMicDrop( + $statements_analyzer, + $stmt->cond, + $if_scope, + $if_conditional_scope, + $outer_context, + $new_assigned_var_ids + ); - if ($if_scope->negated_types) { - $changed_var_ids = []; - - $outer_context_vars_reconciled = Reconciler::reconcileKeyedTypes( - $if_scope->negated_types, - [], - $outer_context->vars_in_scope, - $changed_var_ids, - [], - $statements_analyzer, - $statements_analyzer->getTemplateTypeMap() ?: [], - $outer_context->inside_loop, - new CodeLocation( - $statements_analyzer->getSource(), - $stmt->cond instanceof PhpParser\Node\Expr\BooleanNot - ? $stmt->cond->expr - : $stmt->cond, - $outer_context->include_location, - false - ) - ); - - foreach ($changed_var_ids as $changed_var_id => $_) { - $outer_context->removeVarFromConflictingClauses($changed_var_id); - } - - $changed_var_ids += $new_assigned_var_ids; - - foreach ($changed_var_ids as $var_id => $_) { - $if_scope->negated_clauses = Context::filterClauses( - $var_id, - $if_scope->negated_clauses + $outer_context->clauses = Algebra::simplifyCNF( + array_merge($outer_context->clauses, $if_scope->negated_clauses) ); } - - foreach ($changed_var_ids as $var_id => $_) { - $first_appearance = $statements_analyzer->getFirstAppearance($var_id); - - if ($first_appearance - && isset($outer_context->vars_in_scope[$var_id]) - && isset($outer_context_vars_reconciled[$var_id]) - && $outer_context->vars_in_scope[$var_id]->hasMixed() - && !$outer_context_vars_reconciled[$var_id]->hasMixed() - ) { - if (!$outer_context->collect_initializations - && !$outer_context->collect_mutations - && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath() - && (!(($parent_source = $statements_analyzer->getSource()) - instanceof \Psalm\Internal\Analyzer\FunctionLikeAnalyzer) - || !$parent_source->getSource() instanceof \Psalm\Internal\Analyzer\TraitAnalyzer) - ) { - $codebase->analyzer->decrementMixedCount($statements_analyzer->getFilePath()); - } - - IssueBuffer::remove( - $statements_analyzer->getFilePath(), - 'MixedAssignment', - $first_appearance->raw_file_start - ); - } - } - - $outer_context->vars_in_scope = $outer_context_vars_reconciled; - $mic_drop = true; } - - $outer_context->clauses = Algebra::simplifyCNF( - array_merge($outer_context->clauses, $if_scope->negated_clauses) - ); } // update the parent context as necessary, but only if we can safely reason about type negation. @@ -306,6 +236,104 @@ class IfAnalyzer return null; } + /** + * This handles the situation when returning inside an + * if block with no else or elseifs + * + * @param array $new_assigned_var_ids + */ + private static function handleMicDrop( + StatementsAnalyzer $statements_analyzer, + PhpParser\Node\Expr $cond, + IfScope $if_scope, + IfConditionalScope $if_conditional_scope, + Context $outer_context, + array $new_assigned_var_ids + ) : bool { + // If we're assigning inside + if ($if_conditional_scope->cond_assigned_var_ids + && $if_scope->mic_drop_context + ) { + self::addConditionallyAssignedVarsToContext( + $statements_analyzer, + $cond, + $if_scope->mic_drop_context, + $outer_context, + $if_conditional_scope->cond_assigned_var_ids + ); + } + + if (!$if_scope->negated_types) { + return false; + } + + $changed_var_ids = []; + + $outer_context_vars_reconciled = Reconciler::reconcileKeyedTypes( + $if_scope->negated_types, + [], + $outer_context->vars_in_scope, + $changed_var_ids, + [], + $statements_analyzer, + $statements_analyzer->getTemplateTypeMap() ?: [], + $outer_context->inside_loop, + new CodeLocation( + $statements_analyzer->getSource(), + $cond instanceof PhpParser\Node\Expr\BooleanNot + ? $cond->expr + : $cond, + $outer_context->include_location, + false + ) + ); + + foreach ($changed_var_ids as $changed_var_id => $_) { + $outer_context->removeVarFromConflictingClauses($changed_var_id); + } + + $changed_var_ids += $new_assigned_var_ids; + + foreach ($changed_var_ids as $var_id => $_) { + $if_scope->negated_clauses = Context::filterClauses( + $var_id, + $if_scope->negated_clauses + ); + } + + foreach ($changed_var_ids as $var_id => $_) { + $first_appearance = $statements_analyzer->getFirstAppearance($var_id); + + if ($first_appearance + && isset($outer_context->vars_in_scope[$var_id]) + && isset($outer_context_vars_reconciled[$var_id]) + && $outer_context->vars_in_scope[$var_id]->hasMixed() + && !$outer_context_vars_reconciled[$var_id]->hasMixed() + ) { + if (!$outer_context->collect_initializations + && !$outer_context->collect_mutations + && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath() + && (!(($parent_source = $statements_analyzer->getSource()) + instanceof \Psalm\Internal\Analyzer\FunctionLikeAnalyzer) + || !$parent_source->getSource() instanceof \Psalm\Internal\Analyzer\TraitAnalyzer) + ) { + $codebase = $statements_analyzer->getCodebase(); + $codebase->analyzer->decrementMixedCount($statements_analyzer->getFilePath()); + } + + IssueBuffer::remove( + $statements_analyzer->getFilePath(), + 'MixedAssignment', + $first_appearance->raw_file_start + ); + } + } + + $outer_context->vars_in_scope = $outer_context_vars_reconciled; + + return true; + } + /** * @param array $cond_assigned_var_ids */