diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php index 438d524b6..a655d843e 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php @@ -216,7 +216,6 @@ class ArrayAssignmentAnalyzer $current_type, $root_type, $offset_already_existed, - $child_stmt, $parent_var_id ); } else { @@ -471,7 +470,6 @@ class ArrayAssignmentAnalyzer Union $value_type, Union $root_type, bool $offset_already_existed, - ?PhpParser\Node\Expr $child_stmt, ?string $parent_var_id ): Union { $templated_assignment = false; @@ -525,7 +523,6 @@ class ArrayAssignmentAnalyzer } if ($offset_already_existed - && $child_stmt && $parent_var_id && ($parent_type = $context->vars_in_scope[$parent_var_id] ?? null) ) { @@ -669,7 +666,8 @@ class ArrayAssignmentAnalyzer } /** - * @param array $child_stmts + * @param non-empty-list $child_stmts + * @param-out PhpParser\Node\Expr $child_stmt */ private static function analyzeNestedArrayAssignment( StatementsAnalyzer $statements_analyzer, @@ -688,17 +686,14 @@ class ArrayAssignmentAnalyzer ): void { $reversed_child_stmts = []; $var_id_additions = []; - $full_var_id = true; $child_stmt = null; - if (!empty($child_stmts)) { - $root_var = reset($child_stmts)->var; - } + $root_var = reset($child_stmts)->var; // First go from the root element up, and go as far as we can to figure out what // array types there are - while ($child_stmts) { + do { $child_stmt = array_shift($child_stmts); if (count($child_stmts)) { @@ -820,12 +815,12 @@ class ArrayAssignmentAnalyzer $current_dim = $child_stmt->dim; $parent_var_id = $extended_var_id; - } + } while ($child_stmts); if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph && $root_var_id !== null && isset($context->references_to_external_scope[$root_var_id]) - && isset($root_var) && $root_var instanceof Variable && is_string($root_var->name) + && $root_var instanceof Variable && is_string($root_var->name) && $root_var_id === '$' . $root_var->name ) { // Array is a reference to an external scope, mark it as used @@ -841,7 +836,6 @@ class ArrayAssignmentAnalyzer if ($root_var_id && $full_var_id - && $child_stmt && ($child_stmt_var_type = $statements_analyzer->node_data->getType($child_stmt->var)) && !$child_stmt_var_type->hasObjectType() ) { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php index 7c0b4672a..cb312b06a 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php @@ -1101,44 +1101,45 @@ class ArrayFetchAnalyzer ): void { $has_array_access = true; - if ($in_assignment && $type instanceof TArray) { - $from_empty_array = $type->isEmptyArray(); + if ($in_assignment) { + if ($type instanceof TArray) { + $from_empty_array = $type->isEmptyArray(); - if (count($key_values) === 1) { - $single_atomic = $key_values[0]; - $from_mixed_array = $type->type_params[1]->isMixed(); + if (count($key_values) === 1) { + $single_atomic = $key_values[0]; + $from_mixed_array = $type->type_params[1]->isMixed(); - [$previous_key_type, $previous_value_type] = $type->type_params; + [$previous_key_type, $previous_value_type] = $type->type_params; - // ok, type becomes an TKeyedArray - $array_type->removeType($type_string); - $type = new TKeyedArray([ - $single_atomic->value => $from_mixed_array ? Type::getMixed() : Type::getNever() - ]); - if ($single_atomic instanceof TLiteralClassString) { - $type->class_strings[$single_atomic->value] = true; + // ok, type becomes an TKeyedArray + $array_type->removeType($type_string); + $type = new TKeyedArray([ + $single_atomic->value => $from_mixed_array ? Type::getMixed() : Type::getNever() + ]); + if ($single_atomic instanceof TLiteralClassString) { + $type->class_strings[$single_atomic->value] = true; + } + + $type->sealed = $from_empty_array; + + if (!$from_empty_array) { + $type->previous_value_type = clone $previous_value_type; + $type->previous_key_type = clone $previous_key_type; + } + + $array_type->addType($type); + } elseif (!$stmt->dim && $from_empty_array && $replacement_type) { + $array_type->removeType($type_string); + $array_type->addType(new TNonEmptyList($replacement_type)); + return; } - - $type->sealed = $from_empty_array; - - if (!$from_empty_array) { - $type->previous_value_type = clone $previous_value_type; - $type->previous_key_type = clone $previous_key_type; - } - - $array_type->addType($type); - } elseif (!$stmt->dim && $from_empty_array && $replacement_type) { - $array_type->removeType($type_string); - $array_type->addType(new TNonEmptyList($replacement_type)); - return; + } elseif ($type instanceof TKeyedArray + && $type->previous_value_type + && $type->previous_value_type->isMixed() + && count($key_values) === 1 + ) { + $type->properties[$key_values[0]->value] = Type::getMixed(); } - } elseif ($in_assignment - && $type instanceof TKeyedArray - && $type->previous_value_type - && $type->previous_value_type->isMixed() - && count($key_values) === 1 - ) { - $type->properties[$key_values[0]->value] = Type::getMixed(); } $offset_type = self::replaceOffsetTypeWithInts($offset_type); diff --git a/src/Psalm/Internal/Analyzer/Statements/ReturnAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/ReturnAnalyzer.php index dc3909caf..6ce6cdb15 100644 --- a/src/Psalm/Internal/Analyzer/Statements/ReturnAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/ReturnAnalyzer.php @@ -155,6 +155,8 @@ class ReturnAnalyzer if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) { $context->inside_return = false; + $context->has_returned = true; + return; } @@ -213,6 +215,8 @@ class ReturnAnalyzer } } + $context->has_returned = true; + if ($source instanceof FunctionLikeAnalyzer && !($source->getSource() instanceof TraitAnalyzer) ) { diff --git a/src/Psalm/Internal/Analyzer/Statements/ThrowAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/ThrowAnalyzer.php index fbe259dfe..27adedc1e 100644 --- a/src/Psalm/Internal/Analyzer/Statements/ThrowAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/ThrowAnalyzer.php @@ -28,9 +28,11 @@ class ThrowAnalyzer ): bool { $context->inside_throw = true; if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) { + $context->has_returned = true; return false; } $context->inside_throw = false; + $context->has_returned = true; if ($context->finally_scope) { foreach ($context->vars_in_scope as $var_id => $type) { diff --git a/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php b/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php index 5e2f91d0e..ea6b490bf 100644 --- a/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php @@ -560,10 +560,8 @@ class StatementsAnalyzer extends SourceAnalyzer UnsetAnalyzer::analyze($statements_analyzer, $stmt, $context); } elseif ($stmt instanceof PhpParser\Node\Stmt\Return_) { ReturnAnalyzer::analyze($statements_analyzer, $stmt, $context); - $context->has_returned = true; } elseif ($stmt instanceof PhpParser\Node\Stmt\Throw_) { ThrowAnalyzer::analyze($statements_analyzer, $stmt, $context); - $context->has_returned = true; } elseif ($stmt instanceof PhpParser\Node\Stmt\Switch_) { SwitchAnalyzer::analyze($statements_analyzer, $stmt, $context); } elseif ($stmt instanceof PhpParser\Node\Stmt\Break_) {